static void hmac_sha256_for_data(char *hash, 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; unsigned int i; if (data == NULL) { hash_set_zeros(hash); return; } if (HMAC(EVP_sha256(), (const void *)key, key_len, data, data_len, digest, &digest_len) == NULL) { hash_set_zeros(hash); return; } digest_str = calloc((digest_len * 2) + 1, sizeof(char)); if (digest_str == NULL) { abort(); } for (i = 0; i < digest_len; i++) { sprintf(&digest_str[i * 2], "%02x", (unsigned int)digest[i]); } hash_assign(digest_str, hash); free(digest_str); }
/* provide a wrapper for compute_hash() because we want a cheap-out option in * case we are looking for missing files only: * zeros hash: file missing * ones hash: file present */ int compute_hash_lazy(struct file *file, char *filename) { struct stat sb; if (lstat(filename, &sb) == 0) { hash_set_ones(file->hash); } else { hash_set_zeros(file->hash); } return 0; }
static void hmac_sha256_for_string(char *hash, const unsigned char *key, size_t key_len, const char *str) { if (str == NULL) { hash_set_zeros(hash); return; } hmac_sha256_for_data(hash, key, key_len, (const unsigned char *)str, strlen(str)); }
/* TODO: how should we properly handle compute_hash() failures? */ int compute_hash(struct file *file, char *filename) { int ret; char key[SWUPD_HASH_LEN]; size_t key_len; unsigned char *blob; FILE *fl; if (file->is_deleted) { hash_set_zeros(file->hash); return 0; } hash_set_zeros(key); if (file->is_link) { char link[PATH_MAXLEN]; memset(link, 0, PATH_MAXLEN); ret = readlink(filename, link, PATH_MAXLEN - 1); if (ret >= 0) { hmac_compute_key(filename, &file->stat, key, &key_len, file->use_xattrs); hmac_sha256_for_string(file->hash, (const unsigned char *)key, key_len, link); return 0; } else { return -1; } } if (file->is_dir) { hmac_compute_key(filename, &file->stat, key, &key_len, file->use_xattrs); hmac_sha256_for_string(file->hash, (const unsigned char *)key, key_len, SWUPD_HASH_DIRNAME); //Make independent of dirname return 0; } /* if we get here, this is a regular file */ fl = fopen(filename, "r"); if (!fl) { return -1; } blob = mmap(NULL, file->stat.st_size, PROT_READ, MAP_PRIVATE, fileno(fl), 0); if (blob == MAP_FAILED && file->stat.st_size != 0) { abort(); } hmac_compute_key(filename, &file->stat, key, &key_len, file->use_xattrs); hmac_sha256_for_data(file->hash, (const unsigned char *)key, key_len, blob, file->stat.st_size); munmap(blob, file->stat.st_size); fclose(fl); return 0; }
int match_manifests(struct manifest *m1, struct manifest *m2) { GList *list1, *list2; struct file *file1, *file2; int must_sort = 0; int count = 0; int first = 1; if (!m1) { printf("Matching manifests up failed: No old manifest!\n"); return -1; } if (!m2) { printf("Matching manifests up failed: No new manifest!\n"); return -1; } m1->files = g_list_sort(m1->files, file_sort_filename); m2->files = g_list_sort(m2->files, file_sort_filename); list1 = g_list_first(m1->files); list2 = g_list_first(m2->files); while (list1 && list2) { int ret; file1 = list1->data; file2 = list2->data; file1->peer = NULL; file2->peer = NULL; ret = strcmp(file1->filename, file2->filename); if (ret == 0) { if (file1->is_deleted && file2->is_deleted && file1->is_rename) { file2->is_rename = file1->is_rename; hash_assign(file1->hash, file2->hash); } if (hash_compare(file1->hash, file2->hash) && file1->is_dir == file2->is_dir && file1->is_link == file2->is_link && file1->is_deleted == file2->is_deleted && file1->is_file == file2->is_file && file1->is_config == file2->is_config && file1->is_state == file2->is_state && file1->is_boot == file2->is_boot && file1->last_change >= minversion) { file2->last_change = file1->last_change; file2->is_rename = file1->is_rename; } else { account_changed_file(); if (first) { LOG(file1, "file changed", ""); first = 0; } count++; } if (!file1->is_deleted || file2->is_deleted) { file1->peer = file2; file2->peer = file1; } list1 = g_list_next(list1); list2 = g_list_next(list2); continue; } if (first) { LOG(file1, "file added? ", "(file2 is %s)", file2->filename); first = 0; } if (ret < 0) { struct file *file3; /* * if we get here, file1 got deleted... what we must do * is add a file entry for it in the target list. * However, since we're currently walking the list we * HAVE to prepend the entry.. and mark for sort at the * end. */ file3 = calloc(1, sizeof(struct file)); if (file3 == NULL) { assert(0); } file3->filename = strdup(file1->filename); hash_set_zeros(file3->hash); file3->is_deleted = 1; file3->is_config = file1->is_config; file3->is_state = file1->is_state; file3->is_boot = file1->is_boot; if (!file1->is_deleted) { file3->last_change = m2->version; } else { file3->last_change = file1->last_change; file3->is_rename = file1->is_rename; hash_assign(file1->hash, file3->hash); } file3->peer = file1; file1->peer = file3; list1 = g_list_next(list1); m2->files = g_list_prepend(m2->files, file3); m2->count++; if (!file1->is_deleted) { account_deleted_file(); count++; if (first) { LOG(file1, "file got deleted", ""); first = 0; } } must_sort = 1; continue; } /* if we get here, ret is > 0, which means this is a new file added */ /* all we do is advance the pointer */ account_new_file(); list2 = g_list_next(list2); count++; } /* now deal with the tail ends */ while (list1) { file1 = list1->data; struct file *file3; if (first) { LOG(file1, "file changed tail", ""); first = 0; } count++; /* * if we get here, file1 got deleted... what we must do is add * a file entry for it in the target list. However, since * we're currently walking the list we HAVE to prepend the * entry.. and mark for sort at the end. */ file3 = calloc(1, sizeof(struct file)); if (file3 == NULL) { assert(0); } file3->filename = strdup(file1->filename); hash_set_zeros(file3->hash); file3->is_deleted = 1; file3->is_config = file1->is_config; file3->is_state = file1->is_state; file3->is_boot = file1->is_boot; if (!file1->is_deleted) { file3->last_change = m2->version; } else { file3->last_change = file1->last_change; file3->is_rename = file1->is_rename; hash_assign(file1->hash, file3->hash); } file3->peer = file1; file1->peer = file3; list1 = g_list_next(list1); m2->files = g_list_prepend(m2->files, file3); m2->count++; if (!file1->is_deleted) { account_deleted_file(); } must_sort = 1; } while (list2) { account_new_file(); list2 = g_list_next(list2); if (first) { first = 0; } count++; } if (must_sort) { m2->files = g_list_sort(m2->files, file_sort_filename); } return count; }