mirror of
https://github.com/clearlinux/swupd-client.git
synced 2026-09-08 06:31:50 +00:00
562 lines
13 KiB
C
562 lines
13 KiB
C
/*
|
|
* Software Updater - client side
|
|
*
|
|
* Copyright © 2013-2016 Intel Corporation.
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, version 2 or later of the License.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*
|
|
* Authors:
|
|
* Christophe Guiraud <christophe.guiraud@intel.com>
|
|
* Eric Lapuyade <eric.lapuyade@intel.com>
|
|
* Timothy C. Pepper <timothy.c.pepper@linux.intel.com>
|
|
*
|
|
*/
|
|
|
|
#define _GNU_SOURCE
|
|
|
|
#include <assert.h>
|
|
#include <dirent.h>
|
|
#include <errno.h>
|
|
#include <errno.h>
|
|
#include <openssl/hmac.h>
|
|
#include <openssl/sha.h>
|
|
#include <pthread.h>
|
|
#include <stdint.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <sys/mman.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/time.h>
|
|
#include <sys/types.h>
|
|
#include <sys/xattr.h>
|
|
#include <unistd.h>
|
|
|
|
#include "swupd.h"
|
|
#include "xattrs.h"
|
|
|
|
static void print_usage(void);
|
|
|
|
#define HMAC_SHA1 1
|
|
#define HMAC_SHA256 4
|
|
#define HMAC_SHA512 8
|
|
|
|
struct hmac_sha_stat {
|
|
uint64_t st_mode;
|
|
uint64_t st_uid;
|
|
uint64_t st_gid;
|
|
uint64_t st_rdev;
|
|
uint64_t st_size;
|
|
};
|
|
|
|
static char *bin2hex(const unsigned char *buf, size_t len)
|
|
{
|
|
static const char x_digit[] = "0123456789ABCDEF";
|
|
size_t i;
|
|
char *hex;
|
|
char *tmp;
|
|
|
|
hex = calloc(1, (len * 2) + 1);
|
|
if (hex == NULL) {
|
|
printf("Error: Memory Allocation\n");
|
|
return NULL;
|
|
}
|
|
|
|
tmp = hex;
|
|
for (i = 0; i < len; i++, buf++) {
|
|
*tmp++ = x_digit[(*buf >> 4)];
|
|
*tmp++ = x_digit[(*buf & 0x0F)];
|
|
}
|
|
|
|
return hex;
|
|
}
|
|
|
|
static char *hmac_sha_for_data(int method, const unsigned char *key, size_t key_len, const unsigned char *data, size_t data_len)
|
|
{
|
|
unsigned char digest[EVP_MAX_MD_SIZE];
|
|
unsigned int digest_len = 0;
|
|
char *digest_str;
|
|
const EVP_MD *evp_method;
|
|
|
|
switch (method) {
|
|
case HMAC_SHA1:
|
|
evp_method = EVP_sha1();
|
|
break;
|
|
case HMAC_SHA256:
|
|
evp_method = EVP_sha256();
|
|
break;
|
|
case HMAC_SHA512:
|
|
evp_method = EVP_sha512();
|
|
break;
|
|
default:
|
|
return NULL;
|
|
}
|
|
|
|
if ((data == NULL) || (data_len == 0)) {
|
|
return NULL;
|
|
}
|
|
|
|
if (HMAC(evp_method, (const void *)key, key_len, data, data_len, digest, &digest_len) == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
digest_str = bin2hex(digest, digest_len);
|
|
if (digest_str == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
return digest_str;
|
|
}
|
|
|
|
static char *hmac_sha_for_string(int method, const unsigned char *key, size_t key_len, const char *str)
|
|
{
|
|
if (str == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
return hmac_sha_for_data(method, key, key_len, (const unsigned char *)str, strlen(str));
|
|
}
|
|
|
|
static void print_xattr_list(const unsigned char *list, size_t len)
|
|
{
|
|
char *hexstr;
|
|
|
|
hexstr = bin2hex(list, len);
|
|
if (!hexstr) {
|
|
assert(0);
|
|
}
|
|
|
|
printf("- X-Attribute-Blob: Length=[%lu], Value=[0x%s]\n", (long unsigned int)len, hexstr);
|
|
free(hexstr);
|
|
}
|
|
|
|
static void hmac_compute_key(int method, const char *file,
|
|
const struct hmac_sha_stat *updt_stat,
|
|
char **key, size_t *key_len, int verbose)
|
|
{
|
|
char *xattrs_blob = NULL;
|
|
size_t len = 0;
|
|
char *key_str;
|
|
|
|
*key_len = 0;
|
|
|
|
xattrs_get_blob(file, &xattrs_blob, &len);
|
|
|
|
if ((xattrs_blob != NULL) && (len != 0)) {
|
|
*key = hmac_sha_for_data(method, (const unsigned char *)updt_stat,
|
|
sizeof(*updt_stat),
|
|
(const unsigned char *)xattrs_blob,
|
|
len);
|
|
if (*key != NULL) {
|
|
*key_len = strlen((const char *)*key);
|
|
if (verbose) {
|
|
print_xattr_list((const unsigned char *)xattrs_blob, len);
|
|
printf("- Hash Key: Length=[%lu], Value=[0x%s]\n", (long unsigned int)(*key_len), *key);
|
|
}
|
|
} else {
|
|
*key_len = 0;
|
|
printf("- Hash Key: Length=[%lu], Value=(null)\n", (long unsigned int)(*key_len));
|
|
fprintf(stderr, "computing hash key: error while computing key for file %s: %s\n",
|
|
file, strerror(errno));
|
|
}
|
|
|
|
if (len != 0) {
|
|
free(xattrs_blob);
|
|
}
|
|
} else {
|
|
*key = calloc(1, sizeof(*updt_stat));
|
|
if (*key != NULL) {
|
|
*key_len = sizeof(*updt_stat);
|
|
memcpy(*key, updt_stat, *key_len);
|
|
|
|
if (verbose) {
|
|
key_str = bin2hex((const unsigned char *)key, *key_len);
|
|
if (!key_str) {
|
|
assert(0);
|
|
}
|
|
printf("- Stat Key: Length=[%lu], Value=[0x%s]\n", (long unsigned int)(*key_len), (const char *)key_str);
|
|
free(key_str);
|
|
}
|
|
} else {
|
|
fprintf(stderr, "computing hash key: error while computing key for file %s: %s\n",
|
|
file, strerror(errno));
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void compute_hmac_sha(int method, char *dirpath, int verbose, unsigned int *hashcount)
|
|
{
|
|
DIR *dir;
|
|
struct dirent *entry;
|
|
struct stat stat;
|
|
char *statfile = NULL;
|
|
int ret;
|
|
unsigned char *blob;
|
|
FILE *fl;
|
|
struct hmac_sha_stat tfstat;
|
|
char link[PATH_MAXLEN];
|
|
char *hash = NULL;
|
|
char *key = NULL;
|
|
size_t key_len;
|
|
|
|
dir = opendir(dirpath);
|
|
if (dir == NULL) {
|
|
return;
|
|
}
|
|
|
|
while ((entry = readdir(dir)) != NULL) {
|
|
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) {
|
|
continue;
|
|
}
|
|
|
|
if (asprintf(&statfile, "%s/%s", dirpath, entry->d_name) <= 0) {
|
|
assert(0);
|
|
}
|
|
|
|
if (entry->d_type == DT_DIR) {
|
|
compute_hmac_sha(method, statfile, verbose, hashcount);
|
|
}
|
|
|
|
if (verbose) {
|
|
printf("File: %s\n", statfile);
|
|
}
|
|
|
|
memset(&stat, 0, sizeof(stat));
|
|
ret = lstat(statfile, &stat);
|
|
if (ret < 0) {
|
|
fprintf(stderr, "computing hash: stat error for file %s: %s\n", statfile, strerror(errno));
|
|
goto on_exit;
|
|
}
|
|
|
|
tfstat.st_mode = stat.st_mode;
|
|
tfstat.st_uid = stat.st_uid;
|
|
tfstat.st_gid = stat.st_gid;
|
|
tfstat.st_rdev = stat.st_rdev;
|
|
tfstat.st_size = stat.st_size;
|
|
|
|
if (S_ISLNK(stat.st_mode)) {
|
|
memset(link, 0, PATH_MAXLEN);
|
|
if ((ret = readlink(statfile, link, PATH_MAXLEN - 1)) < 0) {
|
|
printf("readlink error %i - %i / %s\n", ret, errno, strerror(errno));
|
|
goto on_exit;
|
|
}
|
|
hmac_compute_key(method, statfile, &tfstat, &key, &key_len, verbose);
|
|
if (key != NULL) {
|
|
hash = hmac_sha_for_string(method, (const unsigned char *)key, key_len, link);
|
|
(*hashcount)++;
|
|
}
|
|
} else if (S_ISDIR(stat.st_mode)) {
|
|
tfstat.st_size = 0;
|
|
hmac_compute_key(method, statfile, &tfstat, &key, &key_len, verbose);
|
|
if (key != NULL) {
|
|
hash = hmac_sha_for_string(method, (const unsigned char *)key, key_len, statfile);
|
|
(*hashcount)++;
|
|
}
|
|
} else { /* this is a regular file */
|
|
if ((fl = fopen(statfile, "r")) == NULL) {
|
|
fprintf(stderr, "computing hash: file open error for file %s: %s\n", statfile, strerror(errno));
|
|
goto on_exit;
|
|
}
|
|
|
|
blob = mmap(NULL, stat.st_size, PROT_READ, MAP_PRIVATE, fileno(fl), 0);
|
|
hmac_compute_key(method, statfile, &tfstat, &key, &key_len, verbose);
|
|
if (key != NULL) {
|
|
hash = hmac_sha_for_data(method, (const unsigned char *)key, key_len, blob, stat.st_size);
|
|
(*hashcount)++;
|
|
}
|
|
munmap(blob, stat.st_size);
|
|
fclose(fl);
|
|
}
|
|
|
|
if (!hash) {
|
|
assert(0);
|
|
}
|
|
if (verbose) {
|
|
printf("- Hash: 0x%s\n", hash);
|
|
}
|
|
|
|
free(key);
|
|
key = NULL;
|
|
free(hash);
|
|
hash = NULL;
|
|
free(statfile);
|
|
statfile = NULL;
|
|
}
|
|
|
|
on_exit:
|
|
free(key);
|
|
free(hash);
|
|
free(statfile);
|
|
closedir(dir);
|
|
}
|
|
|
|
static void bench_compute_hmac_sha(int method, char *dirpath, int verbose)
|
|
{
|
|
struct timeval start;
|
|
struct timeval end;
|
|
double diff_us;
|
|
|
|
unsigned int hashcount = 0;
|
|
|
|
gettimeofday(&start, NULL);
|
|
|
|
compute_hmac_sha(method, dirpath, verbose, &hashcount);
|
|
|
|
gettimeofday(&end, NULL);
|
|
|
|
diff_us = (end.tv_sec * 1000000 + end.tv_usec) - (start.tv_sec * 1000000 + start.tv_usec);
|
|
printf("...Processing time = %f seconds - Computed hash count = %u\n", diff_us / 1000000., hashcount);
|
|
}
|
|
|
|
static void bench_hmac_sha(int argc, char *argv[])
|
|
{
|
|
int verbose = 0;
|
|
|
|
if (argc > 3) {
|
|
if ((argc > 4) && (strcmp(argv[4], "-v") == 0)) {
|
|
verbose = 1;
|
|
}
|
|
|
|
if (strcmp(argv[2], "hmac-sha1") == 0) {
|
|
printf("[bench hmac sha1]\n");
|
|
return bench_compute_hmac_sha(HMAC_SHA1, argv[3], verbose);
|
|
} else if (strcmp(argv[2], "hmac-sha256") == 0) {
|
|
printf("[bench hmac sha256]\n");
|
|
return bench_compute_hmac_sha(HMAC_SHA256, argv[3], verbose);
|
|
} else if (strcmp(argv[2], "hmac-sha512") == 0) {
|
|
printf("[bench hmac sha512]\n");
|
|
return bench_compute_hmac_sha(HMAC_SHA512, argv[3], verbose);
|
|
}
|
|
}
|
|
|
|
print_usage();
|
|
}
|
|
|
|
static void *multithreads_do_hash(void *threadid)
|
|
{
|
|
long tid;
|
|
char *hash;
|
|
|
|
tid = (long)threadid;
|
|
hash = hmac_sha_for_string(HMAC_SHA256, (const unsigned char *)&threadid, sizeof(threadid), "The quick brown fox jumps over the lazy dog");
|
|
if (!hash) {
|
|
assert(0);
|
|
}
|
|
|
|
printf("multithreads: do_hash - Thread[#%ld] - hash = %s\n", tid, hash);
|
|
|
|
free(hash);
|
|
|
|
pthread_exit(NULL);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
#define NUM_THREADS 4
|
|
static void multithreads(void)
|
|
{
|
|
pthread_t threads[NUM_THREADS];
|
|
int ret;
|
|
long t;
|
|
|
|
for (t = 1; t < NUM_THREADS + 1; t++) {
|
|
printf("multithreads: creating thread %ld/%d\n", t, NUM_THREADS);
|
|
ret = pthread_create(&threads[t], NULL, multithreads_do_hash, (void *)t);
|
|
if (ret) {
|
|
printf("ERROR; return code from pthread_create() is %d\n", ret);
|
|
exit(-1);
|
|
}
|
|
}
|
|
|
|
pthread_exit(NULL);
|
|
}
|
|
|
|
static void xattrs_set_cmd(char *file, char *attr_name, char *attr_value)
|
|
{
|
|
int ret;
|
|
|
|
ret = setxattr(file, attr_name, (void *)&attr_value,
|
|
strlen(attr_value) + 1, 0);
|
|
if (ret < 0) {
|
|
printf("Set Extended attribute for file %s FAILED: %s\n", file,
|
|
strerror(errno));
|
|
} else {
|
|
printf("Set Extended attribute for file %s SUCCESS\n", file);
|
|
}
|
|
}
|
|
|
|
static void xattrs_get_blob_cmd(char *file)
|
|
{
|
|
char *xattrs_blob = NULL;
|
|
size_t len = 0;
|
|
|
|
xattrs_get_blob(file, &xattrs_blob, &len);
|
|
|
|
if ((xattrs_blob != NULL) && (len != 0)) {
|
|
printf("Extended attributes data blob for file %s:\n", file);
|
|
print_xattr_list((const unsigned char *)xattrs_blob, len);
|
|
} else {
|
|
printf("Extended attributes data blob for file %s is EMPTY\n",
|
|
file);
|
|
}
|
|
|
|
if (len != 0) {
|
|
free(xattrs_blob);
|
|
}
|
|
}
|
|
|
|
static void xattrs_copy_cmd(char *src, char *dst)
|
|
{
|
|
printf("Attempt to copy extended attributes from %s to %s\n", src, dst);
|
|
xattrs_copy(src, dst);
|
|
}
|
|
|
|
static void xattrs_test(char *dirpath)
|
|
{
|
|
char *src = NULL;
|
|
char *dst = NULL;
|
|
FILE *file = NULL;
|
|
int ret;
|
|
|
|
/* Create 2 files in the given working directory,
|
|
Set an extended attribute to the first file.
|
|
Copy the first file extended attributes to the other one.
|
|
Get the extended attributes data blob for both file and compare.
|
|
The test is sucessful if extended attributes data blobs are identical.
|
|
*/
|
|
|
|
printf("Extended attribute test working dir %s:\n", dirpath);
|
|
|
|
/* step 4: run valgrind on the patch program with the "corrupt" file */
|
|
if (asprintf(&src, "%s/testfile1.txt", dirpath) <= 0) {
|
|
assert(0);
|
|
}
|
|
|
|
if (asprintf(&dst, "%s/testfile2.txt", dirpath) <= 0) {
|
|
assert(0);
|
|
}
|
|
|
|
file = fopen(src, "w+");
|
|
if (file != NULL) {
|
|
fwrite("The quick brown fox",
|
|
sizeof(char), sizeof("The quick brown fox"), file);
|
|
fclose(file);
|
|
}
|
|
|
|
file = fopen(dst, "w+");
|
|
if (file != NULL) {
|
|
fwrite("The quick brown fox",
|
|
sizeof(char), sizeof("The quick brown fox"), file);
|
|
fclose(file);
|
|
}
|
|
|
|
ret = setxattr(src, "user.xattr_test", (void *)"xattr_test_val",
|
|
strlen("xattr_test_val") + 1, 0);
|
|
if (ret < 0) {
|
|
printf("Set Extended attribute for file %s FAILED: %s\n", src,
|
|
strerror(errno));
|
|
} else {
|
|
printf("Set Extended attribute for file %s SUCCESS\n", src);
|
|
}
|
|
|
|
xattrs_copy(src, dst);
|
|
|
|
if (xattrs_compare(src, dst) != 0) {
|
|
printf("TEST FAILURE\n");
|
|
} else {
|
|
printf("TEST SUCCESS\n");
|
|
}
|
|
|
|
if (src != NULL) {
|
|
unlink(src);
|
|
}
|
|
|
|
if (dst != NULL) {
|
|
unlink(dst);
|
|
}
|
|
|
|
free(src);
|
|
free(dst);
|
|
}
|
|
|
|
static void xattrs(int argc, char *argv[])
|
|
{
|
|
printf("[xattrs argc %d]\n", argc);
|
|
|
|
if (argc > 3) {
|
|
if (strcmp(argv[2], "set") == 0) {
|
|
if (argc != 6) {
|
|
return print_usage();
|
|
}
|
|
printf("[set]\n");
|
|
return xattrs_set_cmd(argv[3], argv[4], argv[5]);
|
|
} else if (strcmp(argv[2], "get_blob") == 0) {
|
|
if (argc != 4) {
|
|
return print_usage();
|
|
}
|
|
printf("[get_blob]\n");
|
|
return xattrs_get_blob_cmd(argv[3]);
|
|
} else if (strcmp(argv[2], "copy") == 0) {
|
|
if (argc != 5) {
|
|
return print_usage();
|
|
}
|
|
printf("[copy]\n");
|
|
return xattrs_copy_cmd(argv[3], argv[4]);
|
|
} else if (strcmp(argv[2], "test") == 0) {
|
|
if (argc != 4) {
|
|
return print_usage();
|
|
}
|
|
printf("[test]\n");
|
|
return xattrs_test(argv[3]);
|
|
}
|
|
}
|
|
|
|
print_usage();
|
|
}
|
|
|
|
static void print_usage(void)
|
|
{
|
|
printf("Usage:\n");
|
|
printf(" hash_test multithreads\n");
|
|
printf(" hash_test bench hmac-sha1 <path> [-v]\n");
|
|
printf(" hash_test bench hmac-sha256 <path> [-v]\n");
|
|
printf(" hash_test bench hmac-sha512 <path> [-v]\n");
|
|
printf(" hash_test xattrs set <filepath> <user.attr_name> <attr_value>\n");
|
|
printf(" hash_test xattrs get_blob <filepath>\n");
|
|
printf(" hash_test xattrs copy <src_filepath> <dst_filepath>\n");
|
|
printf(" hash_test xattrs test <R/W working path>\n");
|
|
printf(" hash_test help\n");
|
|
}
|
|
|
|
int main(int argc, char *argv[])
|
|
{
|
|
if (argc > 1) {
|
|
if (strcmp(argv[1], "multithreads") == 0) {
|
|
printf("[multithreads]\n");
|
|
multithreads();
|
|
return 0;
|
|
} else if (strcmp(argv[1], "bench") == 0) {
|
|
bench_hmac_sha(argc, argv);
|
|
return 0;
|
|
} else if (strcmp(argv[1], "xattrs") == 0) {
|
|
xattrs(argc, argv);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
print_usage();
|
|
|
|
return 0;
|
|
}
|