ANDROID: Incremental fs: Remove signature checks from kernel

Test: selftests pass
Bug: 133435829
Signed-off-by: Paul Lawrence <paullawrence@google.com>
Change-Id: Ia7e69b1b0176202da4b418ea815b370cbdacd5c2
This commit is contained in:
Paul Lawrence 2020-03-13 12:38:35 -07:00
parent 538096344f
commit 73e7d65693
11 changed files with 306 additions and 870 deletions

View file

@ -250,7 +250,7 @@ static int validate_hash_tree(struct file *bf, struct data_file *df,
{
u8 digest[INCFS_MAX_HASH_SIZE] = {};
struct mtree *tree = NULL;
struct ondisk_signature *sig = NULL;
struct incfs_df_signature *sig = NULL;
struct mem_range calc_digest_rng;
struct mem_range saved_digest_rng;
struct mem_range root_hash_rng;
@ -273,8 +273,8 @@ static int validate_hash_tree(struct file *bf, struct data_file *df,
return res;
for (lvl = 0; lvl < tree->depth; lvl++) {
loff_t lvl_off = tree->hash_level_suboffset[lvl] +
sig->mtree_offset;
loff_t lvl_off =
tree->hash_level_suboffset[lvl] + sig->hash_offset;
loff_t hash_block_off = lvl_off +
round_down(hash_block_index * digest_size,
INCFS_DATA_FILE_BLOCK_SIZE);
@ -322,72 +322,6 @@ static int validate_hash_tree(struct file *bf, struct data_file *df,
return 0;
}
static int revalidate_signature(struct file *bf, struct data_file *df)
{
struct ondisk_signature *sig = df->df_signature;
struct mem_range root_hash = {};
int result = 0;
u8 *sig_buf = NULL;
u8 *add_data_buf = NULL;
ssize_t read_res;
/* File has no signature. */
if (!sig || !df->df_hash_tree || sig->sig_size == 0)
return 0;
/* Signature has already been validated. */
if (df->df_signature_validated)
return 0;
add_data_buf = kzalloc(sig->add_data_size, GFP_NOFS);
if (!add_data_buf) {
result = -ENOMEM;
goto out;
}
read_res = incfs_kread(bf, add_data_buf, sig->add_data_size,
sig->add_data_offset);
if (read_res < 0) {
result = read_res;
goto out;
}
if (read_res != sig->add_data_size) {
result = -EIO;
goto out;
}
sig_buf = kzalloc(sig->sig_size, GFP_NOFS);
if (!sig_buf) {
result = -ENOMEM;
goto out;
}
read_res = incfs_kread(bf, sig_buf, sig->sig_size, sig->sig_offset);
if (read_res < 0) {
result = read_res;
goto out;
}
if (read_res != sig->sig_size) {
result = -EIO;
goto out;
}
root_hash = range(df->df_hash_tree->root_hash,
df->df_hash_tree->alg->digest_size);
result = incfs_validate_pkcs7_signature(
range(sig_buf, sig->sig_size),
root_hash,
range(add_data_buf, sig->add_data_size));
if (result == 0)
df->df_signature_validated = true;
out:
kfree(sig_buf);
kfree(add_data_buf);
return result;
}
static struct data_file_segment *get_file_segment(struct data_file *df,
int block_index)
{
@ -683,13 +617,6 @@ ssize_t incfs_read_data_file_block(struct mem_range dst, struct data_file *df,
result = err;
}
if (result > 0) {
int err = revalidate_signature(bf, df);
if (err < 0)
result = err;
}
if (result >= 0)
log_block_read(mi, &df->df_id, index, false /*timed out*/);
@ -755,7 +682,7 @@ int incfs_process_new_data_block(struct data_file *df,
int incfs_read_file_signature(struct data_file *df, struct mem_range dst)
{
struct file *bf = df->df_backing_file_context->bc_file;
struct ondisk_signature *sig;
struct incfs_df_signature *sig;
int read_res = 0;
if (!dst.data)
@ -785,7 +712,7 @@ int incfs_process_new_hash_block(struct data_file *df,
struct backing_file_context *bfc = NULL;
struct mount_info *mi = NULL;
struct mtree *hash_tree = NULL;
struct ondisk_signature *sig = NULL;
struct incfs_df_signature *sig = NULL;
loff_t hash_area_base = 0;
loff_t hash_area_size = 0;
int error = 0;
@ -804,11 +731,11 @@ int incfs_process_new_hash_block(struct data_file *df,
hash_tree = df->df_hash_tree;
sig = df->df_signature;
if (!hash_tree || !sig || sig->mtree_offset == 0)
if (!hash_tree || !sig || sig->hash_offset == 0)
return -ENOTSUPP;
hash_area_base = sig->mtree_offset;
hash_area_size = sig->mtree_size;
hash_area_base = sig->hash_offset;
hash_area_size = sig->hash_size;
if (hash_area_size < block->block_index * INCFS_DATA_FILE_BLOCK_SIZE
+ block->data_len) {
/* Hash block goes beyond dedicated hash area of this file. */
@ -866,58 +793,69 @@ static int process_file_signature_md(struct incfs_file_signature *sg,
{
struct data_file *df = handler->context;
struct mtree *hash_tree = NULL;
struct ondisk_signature *signature = NULL;
int error = 0;
loff_t base_tree_off = le64_to_cpu(sg->sg_hash_tree_offset);
u32 tree_size = le32_to_cpu(sg->sg_hash_tree_size);
loff_t sig_off = le64_to_cpu(sg->sg_sig_offset);
u32 sig_size = le32_to_cpu(sg->sg_sig_size);
loff_t add_data_off = le64_to_cpu(sg->sg_add_data_offset);
u32 add_data_size = le32_to_cpu(sg->sg_add_data_size);
struct incfs_df_signature *signature =
kzalloc(sizeof(*signature), GFP_NOFS);
void *buf = 0;
ssize_t read;
if (!df)
return -ENOENT;
if (!df || !df->df_backing_file_context ||
!df->df_backing_file_context->bc_file) {
error = -ENOENT;
goto out;
}
signature = kzalloc(sizeof(*signature), GFP_NOFS);
if (!signature) {
signature->hash_offset = le64_to_cpu(sg->sg_hash_tree_offset);
signature->hash_size = le32_to_cpu(sg->sg_hash_tree_size);
signature->sig_offset = le64_to_cpu(sg->sg_sig_offset);
signature->sig_size = le32_to_cpu(sg->sg_sig_size);
buf = kzalloc(signature->sig_size, GFP_NOFS);
if (!buf) {
error = -ENOMEM;
goto out;
}
signature->add_data_offset = add_data_off;
signature->add_data_size = add_data_size;
signature->sig_offset = sig_off;
signature->sig_size = sig_size;
signature->mtree_offset = base_tree_off;
signature->mtree_size = tree_size;
read = incfs_kread(df->df_backing_file_context->bc_file, buf,
signature->sig_size, signature->sig_offset);
if (read < 0) {
error = read;
goto out;
}
hash_tree = incfs_alloc_mtree(sg->sg_hash_alg, df->df_block_count,
range(sg->sg_root_hash, sizeof(sg->sg_root_hash)));
if (read != signature->sig_size) {
error = -EINVAL;
goto out;
}
hash_tree = incfs_alloc_mtree(range(buf, signature->sig_size),
df->df_block_count);
if (IS_ERR(hash_tree)) {
error = PTR_ERR(hash_tree);
hash_tree = NULL;
goto out;
}
if (hash_tree->hash_tree_area_size != tree_size) {
if (hash_tree->hash_tree_area_size != signature->hash_size) {
error = -EINVAL;
goto out;
}
if (tree_size > 0 && handler->md_record_offset <= base_tree_off) {
if (signature->hash_size > 0 &&
handler->md_record_offset <= signature->hash_offset) {
error = -EINVAL;
goto out;
}
if (handler->md_record_offset <= signature->add_data_offset ||
handler->md_record_offset <= signature->sig_offset) {
if (handler->md_record_offset <= signature->sig_offset) {
error = -EINVAL;
goto out;
}
df->df_hash_tree = hash_tree;
hash_tree = NULL;
df->df_signature = signature;
signature = NULL;
out:
if (error) {
incfs_free_mtree(hash_tree);
kfree(signature);
}
kfree(buf);
return error;
}

View file

@ -189,10 +189,7 @@ struct data_file {
struct mtree *df_hash_tree;
struct ondisk_signature *df_signature;
/* True, if file signature has already been validated. */
bool df_signature_validated;
struct incfs_df_signature *df_signature;
};
struct dir_file {

View file

@ -299,9 +299,7 @@ int incfs_write_file_attr_to_backing_file(struct backing_file_context *bfc,
}
int incfs_write_signature_to_backing_file(struct backing_file_context *bfc,
u8 hash_alg, u32 tree_size,
struct mem_range root_hash, struct mem_range add_data,
struct mem_range sig)
struct mem_range sig, u32 tree_size)
{
struct incfs_file_signature sg = {};
int result = 0;
@ -311,8 +309,6 @@ int incfs_write_signature_to_backing_file(struct backing_file_context *bfc,
if (!bfc)
return -EFAULT;
if (root_hash.len > sizeof(sg.sg_root_hash))
return -E2BIG;
LOCK_REQUIRED(bfc->bc_mutex);
@ -321,7 +317,6 @@ int incfs_write_signature_to_backing_file(struct backing_file_context *bfc,
sg.sg_header.h_md_entry_type = INCFS_MD_SIGNATURE;
sg.sg_header.h_record_size = cpu_to_le16(sizeof(sg));
sg.sg_header.h_next_md_offset = cpu_to_le64(0);
sg.sg_hash_alg = hash_alg;
if (sig.data != NULL && sig.len > 0) {
loff_t pos = incfs_get_end_offset(bfc->bc_file);
@ -333,20 +328,8 @@ int incfs_write_signature_to_backing_file(struct backing_file_context *bfc,
goto err;
}
if (add_data.len > 0) {
loff_t pos = incfs_get_end_offset(bfc->bc_file);
sg.sg_add_data_size = cpu_to_le32(add_data.len);
sg.sg_add_data_offset = cpu_to_le64(pos);
result = write_to_bf(bfc, add_data.data,
add_data.len, pos, false);
if (result)
goto err;
}
tree_area_pos = incfs_get_end_offset(bfc->bc_file);
if (hash_alg && tree_size > 0) {
if (tree_size > 0) {
if (tree_size > 5 * INCFS_DATA_FILE_BLOCK_SIZE) {
/*
* If hash tree is big enough, it makes sense to
@ -369,7 +352,6 @@ int incfs_write_signature_to_backing_file(struct backing_file_context *bfc,
sg.sg_hash_tree_size = cpu_to_le32(tree_size);
sg.sg_hash_tree_offset = cpu_to_le64(tree_area_pos);
}
memcpy(sg.sg_root_hash, root_hash.data, root_hash.len);
/* Write a hash tree metadata record pointing to the hash tree above. */
result = append_md_to_backing_file(bfc, &sg.sg_header);

View file

@ -217,27 +217,27 @@ struct incfs_file_attr {
__le32 fa_crc;
} __packed;
/* Metadata record for file attribute. Type = INCFS_MD_SIGNATURE */
/* Metadata record for file signature. Type = INCFS_MD_SIGNATURE */
struct incfs_file_signature {
struct incfs_md_header sg_header;
__u8 sg_hash_alg; /* Value from incfs_hash_tree_algorithm */
__le32 sg_sig_size; /* The size of the signature. */
__le64 sg_sig_offset; /* Signature's offset in the backing file */
__le32 sg_hash_tree_size; /* The size of the hash tree. */
__le64 sg_hash_tree_offset; /* Hash tree offset in the backing file */
__u8 sg_root_hash[INCFS_MAX_HASH_SIZE];
__le32 sg_sig_size; /* The size of the pkcs7 signature. */
__le64 sg_sig_offset; /* pkcs7 signature's offset in the backing file */
__le32 sg_add_data_size; /* The size of the additional data. */
__le64 sg_add_data_offset; /* Additional data's offset */
} __packed;
/* In memory version of above */
struct incfs_df_signature {
u32 sig_size;
u64 sig_offset;
u32 hash_size;
u64 hash_offset;
};
/* State of the backing file. */
struct backing_file_context {
/* Protects writes to bc_file */
@ -253,23 +253,6 @@ struct backing_file_context {
loff_t bc_last_md_record_offset;
};
/* Backing file locations of things required for signature validation. */
struct ondisk_signature {
loff_t add_data_offset; /* Additional data's offset */
loff_t sig_offset; /* pkcs7 signature's offset in the backing file */
loff_t mtree_offset; /* Backing file offset of the hash tree. */
u32 add_data_size; /* The size of the additional data. */
u32 sig_size; /* The size of the pkcs7 signature. */
u32 mtree_size; /* The size of the hash tree. */
};
struct metadata_handler {
loff_t md_record_offset;
loff_t md_prev_record_offset;
@ -319,9 +302,7 @@ int incfs_write_file_attr_to_backing_file(struct backing_file_context *bfc,
struct mem_range value, struct incfs_file_attr *attr);
int incfs_write_signature_to_backing_file(struct backing_file_context *bfc,
u8 hash_alg, u32 tree_size,
struct mem_range root_hash, struct mem_range add_data,
struct mem_range sig);
struct mem_range sig, u32 tree_size);
int incfs_make_empty_backing_file(struct backing_file_context *bfc,
incfs_uuid_t *uuid, u64 file_size);

View file

@ -10,70 +10,6 @@
#include "integrity.h"
int incfs_validate_pkcs7_signature(struct mem_range pkcs7_blob,
struct mem_range root_hash, struct mem_range add_data)
{
struct pkcs7_message *pkcs7 = NULL;
const void *data = NULL;
size_t data_len = 0;
const char *p;
int err;
pkcs7 = pkcs7_parse_message(pkcs7_blob.data, pkcs7_blob.len);
if (IS_ERR(pkcs7)) {
pr_debug("PKCS#7 parsing error. ptr=%p size=%ld err=%ld\n",
pkcs7_blob.data, pkcs7_blob.len, -PTR_ERR(pkcs7));
return PTR_ERR(pkcs7);
}
err = pkcs7_get_content_data(pkcs7, &data, &data_len, NULL);
if (err || data_len == 0 || data == NULL) {
pr_debug("PKCS#7 message does not contain data\n");
err = -EBADMSG;
goto out;
}
if (root_hash.len == 0) {
pr_debug("Root hash is empty.\n");
err = -EBADMSG;
goto out;
}
if (data_len != root_hash.len + add_data.len) {
pr_debug("PKCS#7 data size doesn't match arguments.\n");
err = -EKEYREJECTED;
goto out;
}
p = data;
if (memcmp(p, root_hash.data, root_hash.len) != 0) {
pr_debug("Root hash mismatch.\n");
err = -EKEYREJECTED;
goto out;
}
p += root_hash.len;
if (memcmp(p, add_data.data, add_data.len) != 0) {
pr_debug("Additional data mismatch.\n");
err = -EKEYREJECTED;
goto out;
}
err = pkcs7_verify(pkcs7, VERIFYING_UNSPECIFIED_SIGNATURE);
if (err)
pr_debug("PKCS#7 signature verification error: %d\n", -err);
/*
* RSA signature verification sometimes returns unexpected error codes
* when signature doesn't match.
*/
if (err == -ERANGE || err == -EINVAL)
err = -EBADMSG;
out:
pkcs7_free_message(pkcs7);
return err;
}
struct incfs_hash_alg *incfs_get_hash_alg(enum incfs_hash_tree_algorithm id)
{
static struct incfs_hash_alg sha256 = {
@ -113,11 +49,90 @@ struct incfs_hash_alg *incfs_get_hash_alg(enum incfs_hash_tree_algorithm id)
return result;
}
struct signature_info {
u32 version;
enum incfs_hash_tree_algorithm hash_algorithm;
u8 log2_blocksize;
struct mem_range salt;
struct mem_range root_hash;
};
struct mtree *incfs_alloc_mtree(enum incfs_hash_tree_algorithm id,
int data_block_count,
struct mem_range root_hash)
static u32 read_u32(u8 **p, u8 *top, u32 *result)
{
if (*p + sizeof(u32) > top)
return false;
*result = le32_to_cpu(*(u32 *)*p);
*p += sizeof(u32);
return true;
}
static bool read_u8(u8 **p, u8 *top, u8 *result)
{
if (*p + sizeof(u8) > top)
return false;
*result = *(u8 *)*p;
*p += sizeof(u8);
return true;
}
static bool read_mem_range(u8 **p, u8 *top, struct mem_range *range)
{
u32 len;
if (!read_u32(p, top, &len) || *p + len > top)
return false;
range->len = len;
range->data = *p;
*p += len;
return true;
}
static int incfs_parse_signature(struct mem_range signature,
struct signature_info *si)
{
u8 *p = signature.data;
u8 *top = signature.data + signature.len;
u32 hash_section_size;
if (signature.len > INCFS_MAX_SIGNATURE_SIZE)
return -EINVAL;
if (!read_u32(&p, top, &si->version) ||
si->version != INCFS_SIGNATURE_VERSION)
return -EINVAL;
if (!read_u32(&p, top, &hash_section_size) ||
p + hash_section_size > top)
return -EINVAL;
top = p + hash_section_size;
if (!read_u32(&p, top, &si->hash_algorithm) ||
si->hash_algorithm != INCFS_HASH_TREE_SHA256)
return -EINVAL;
if (!read_u8(&p, top, &si->log2_blocksize) || si->log2_blocksize != 12)
return -EINVAL;
if (!read_mem_range(&p, top, &si->salt))
return -EINVAL;
if (!read_mem_range(&p, top, &si->root_hash))
return -EINVAL;
if (p != top)
return -EINVAL;
return 0;
}
struct mtree *incfs_alloc_mtree(struct mem_range signature,
int data_block_count)
{
int error;
struct signature_info si;
struct mtree *result = NULL;
struct incfs_hash_alg *hash_alg = NULL;
int hash_per_block;
@ -129,11 +144,15 @@ struct mtree *incfs_alloc_mtree(enum incfs_hash_tree_algorithm id,
if (data_block_count <= 0)
return ERR_PTR(-EINVAL);
hash_alg = incfs_get_hash_alg(id);
error = incfs_parse_signature(signature, &si);
if (error)
return ERR_PTR(error);
hash_alg = incfs_get_hash_alg(si.hash_algorithm);
if (IS_ERR(hash_alg))
return ERR_PTR(PTR_ERR(hash_alg));
if (root_hash.len < hash_alg->digest_size)
if (si.root_hash.len < hash_alg->digest_size)
return ERR_PTR(-EINVAL);
result = kzalloc(sizeof(*result), GFP_NOFS);
@ -173,7 +192,7 @@ struct mtree *incfs_alloc_mtree(enum incfs_hash_tree_algorithm id,
}
/* Root hash is stored separately from the rest of the tree. */
memcpy(result->root_hash, root_hash.data, hash_alg->digest_size);
memcpy(result->root_hash, si.root_hash.data, hash_alg->digest_size);
return result;
err:
@ -215,13 +234,3 @@ int incfs_calc_digest(struct incfs_hash_alg *alg, struct mem_range data,
return crypto_shash_digest(desc, data.data, data.len, digest.data);
}
void incfs_free_signature_info(struct signature_info *si)
{
if (!si)
return;
kfree(si->root_hash.data);
kfree(si->additional_data.data);
kfree(si->signature.data);
kfree(si);
}

View file

@ -38,21 +38,10 @@ struct mtree {
int depth;
};
struct signature_info {
struct mem_range root_hash;
struct mem_range additional_data;
struct mem_range signature;
enum incfs_hash_tree_algorithm hash_alg;
};
struct incfs_hash_alg *incfs_get_hash_alg(enum incfs_hash_tree_algorithm id);
struct mtree *incfs_alloc_mtree(enum incfs_hash_tree_algorithm id,
int data_block_count,
struct mem_range root_hash);
struct mtree *incfs_alloc_mtree(struct mem_range signature,
int data_block_count);
void incfs_free_mtree(struct mtree *tree);
@ -64,9 +53,4 @@ size_t incfs_get_mtree_hash_count(enum incfs_hash_tree_algorithm alg,
int incfs_calc_digest(struct incfs_hash_alg *alg, struct mem_range data,
struct mem_range digest);
int incfs_validate_pkcs7_signature(struct mem_range pkcs7_blob,
struct mem_range root_hash, struct mem_range add_data);
void incfs_free_signature_info(struct signature_info *si);
#endif /* _INCFS_INTEGRITY_H */

View file

@ -837,104 +837,39 @@ static char *file_id_to_str(incfs_uuid_t id)
return result;
}
static struct signature_info *incfs_copy_signature_info_from_user(
struct incfs_file_signature_info __user *original)
static struct mem_range incfs_copy_signature_info_from_user(u8 __user *original,
u64 size)
{
struct incfs_file_signature_info usr_si;
struct signature_info *result;
int error;
u8 *result;
if (!original)
return NULL;
return range(NULL, 0);
if (copy_from_user(&usr_si, original, sizeof(usr_si)) > 0)
return ERR_PTR(-EFAULT);
if (size > INCFS_MAX_SIGNATURE_SIZE)
return range(ERR_PTR(-EFAULT), 0);
result = kzalloc(sizeof(*result), GFP_NOFS);
result = kzalloc(size, GFP_NOFS);
if (!result)
return ERR_PTR(-ENOMEM);
return range(ERR_PTR(-ENOMEM), 0);
result->hash_alg = usr_si.hash_tree_alg;
if (result->hash_alg) {
void *p = kzalloc(INCFS_MAX_HASH_SIZE, GFP_NOFS);
if (!p) {
error = -ENOMEM;
goto err;
if (copy_from_user(result, original, size)) {
kfree(result);
return range(ERR_PTR(-EFAULT), 0);
}
/* TODO this sets the root_hash length to MAX_HASH_SIZE not
* the actual size. Fix, then set INCFS_MAX_HASH_SIZE back
* to 64
*/
result->root_hash = range(p, INCFS_MAX_HASH_SIZE);
if (copy_from_user(p, u64_to_user_ptr(usr_si.root_hash),
result->root_hash.len) > 0) {
error = -EFAULT;
goto err;
}
}
if (usr_si.additional_data_size > INCFS_MAX_FILE_ATTR_SIZE) {
error = -E2BIG;
goto err;
}
if (usr_si.additional_data && usr_si.additional_data_size) {
void *p = kzalloc(usr_si.additional_data_size, GFP_NOFS);
if (!p) {
error = -ENOMEM;
goto err;
}
result->additional_data = range(p,
usr_si.additional_data_size);
if (copy_from_user(p, u64_to_user_ptr(usr_si.additional_data),
result->additional_data.len) > 0) {
error = -EFAULT;
goto err;
}
}
if (usr_si.signature_size > INCFS_MAX_SIGNATURE_SIZE) {
error = -E2BIG;
goto err;
}
if (usr_si.signature && usr_si.signature_size) {
void *p = kzalloc(usr_si.signature_size, GFP_NOFS);
if (!p) {
error = -ENOMEM;
goto err;
}
result->signature = range(p, usr_si.signature_size);
if (copy_from_user(p, u64_to_user_ptr(usr_si.signature),
result->signature.len) > 0) {
error = -EFAULT;
goto err;
}
}
return result;
err:
incfs_free_signature_info(result);
return ERR_PTR(-error);
return range(result, size);
}
static int init_new_file(struct mount_info *mi, struct dentry *dentry,
incfs_uuid_t *uuid, u64 size, struct mem_range attr,
struct incfs_file_signature_info __user *fsi)
u8 __user *user_signature_info, u64 signature_size)
{
struct path path = {};
struct file *new_file;
int error = 0;
struct backing_file_context *bfc = NULL;
u32 block_count;
struct mem_range mem_range = {NULL};
struct signature_info *si = NULL;
struct mem_range raw_signature = { NULL };
struct mtree *hash_tree = NULL;
if (!mi || !dentry || !uuid)
@ -984,44 +919,27 @@ static int init_new_file(struct mount_info *mi, struct dentry *dentry,
goto out;
}
if (fsi) {
si = incfs_copy_signature_info_from_user(fsi);
if (user_signature_info) {
raw_signature = incfs_copy_signature_info_from_user(
user_signature_info, signature_size);
if (IS_ERR(si)) {
error = PTR_ERR(si);
si = NULL;
if (IS_ERR(raw_signature.data)) {
error = PTR_ERR(raw_signature.data);
raw_signature.data = NULL;
goto out;
}
if (si->hash_alg) {
hash_tree = incfs_alloc_mtree(si->hash_alg, block_count,
si->root_hash);
hash_tree = incfs_alloc_mtree(raw_signature, block_count);
if (IS_ERR(hash_tree)) {
error = PTR_ERR(hash_tree);
hash_tree = NULL;
goto out;
}
/* TODO This code seems wrong when len is zero - we
* should error out??
*/
if (si->signature.len > 0)
error = incfs_validate_pkcs7_signature(
si->signature,
si->root_hash,
si->additional_data);
error = incfs_write_signature_to_backing_file(
bfc, raw_signature, hash_tree->hash_tree_area_size);
if (error)
goto out;
error = incfs_write_signature_to_backing_file(bfc,
si->hash_alg,
hash_tree->hash_tree_area_size,
si->root_hash, si->additional_data,
si->signature);
if (error)
goto out;
}
}
out:
@ -1030,8 +948,7 @@ static int init_new_file(struct mount_info *mi, struct dentry *dentry,
incfs_free_bfc(bfc);
}
incfs_free_mtree(hash_tree);
incfs_free_signature_info(si);
kfree(mem_range.data);
kfree(raw_signature.data);
if (error)
pr_debug("incfs: %s error: %d\n", __func__, error);
@ -1289,7 +1206,7 @@ static long ioctl_create_file(struct mount_info *mi,
goto delete_index_file;
}
/* Save the file's attrubute as an xattr */
/* Save the file's attribute as an xattr */
if (args.file_attr_len && args.file_attr) {
if (args.file_attr_len > INCFS_MAX_FILE_ATTR_SIZE) {
error = -E2BIG;
@ -1321,8 +1238,8 @@ static long ioctl_create_file(struct mount_info *mi,
/* Initializing a newly created file. */
error = init_new_file(mi, index_file_dentry, &args.file_id, args.size,
range(attr_value, args.file_attr_len),
(struct incfs_file_signature_info __user *)
args.signature_info);
(u8 __user *)args.signature_info,
args.signature_size);
if (error)
goto delete_index_file;

View file

@ -35,6 +35,8 @@
#define INCFS_XATTR_METADATA_NAME (XATTR_USER_PREFIX "incfs.metadata")
#define INCFS_MAX_SIGNATURE_SIZE 8096
#define INCFS_SIGNATURE_VERSION 2
#define INCFS_SIGNATURE_SECTIONS 2
#define INCFS_IOCTL_BASE_CODE 'g'
@ -139,46 +141,6 @@ enum incfs_hash_tree_algorithm {
INCFS_HASH_TREE_SHA256 = 1
};
struct incfs_file_signature_info {
/*
* A pointer to file's root hash (if determined != 0)
* Actual hash size determined by hash_tree_alg.
* Size of the buffer should be at least INCFS_MAX_HASH_SIZE
*
* Equivalent to: u8 *root_hash;
*/
__aligned_u64 root_hash;
/*
* A pointer to additional data that was attached to the root hash
* before signing.
*
* Equivalent to: u8 *additional_data;
*/
__aligned_u64 additional_data;
/* Size of additional data. */
__u32 additional_data_size;
__u32 reserved1;
/*
* A pointer to pkcs7 signature DER blob.
*
* Equivalent to: u8 *signature;
*/
__aligned_u64 signature;
/* Size of pkcs7 signature DER blob */
__u32 signature_size;
__u32 reserved2;
/* Value from incfs_hash_tree_algorithm */
__u8 hash_tree_alg;
};
/*
* Create a new file or directory.
*/
@ -232,10 +194,30 @@ struct incfs_new_file_args {
__u32 reserved4;
/* struct incfs_file_signature_info *signature_info; */
/*
* Points to an APK V4 Signature data blob
* Signature must have two sections
* Format is:
* u32 version
* u32 size_of_hash_info_section
* u8 hash_info_section[]
* u32 size_of_signing_info_section
* u8 signing_info_section[]
*
* Note that incfs does not care about what is in signing_info_section
*
* hash_info_section has following format:
* u32 hash_algorithm; // Must be SHA256 == 1
* u8 log2_blocksize; // Must be 12 for 4096 byte blocks
* u32 salt_size;
* u8 salt[];
* u32 hash_size;
* u8 root_hash[];
*/
__aligned_u64 signature_info;
__aligned_u64 reserved5;
/* Size of signature_info */
__aligned_u64 signature_size;
__aligned_u64 reserved6;
};

View file

@ -25,8 +25,6 @@
#include "lz4.h"
#include "utils.h"
#define __packed __attribute__((__packed__))
#define TEST_FAILURE 1
#define TEST_SUCCESS 0
#define INCFS_MAX_MTREE_LEVELS 8
@ -69,101 +67,6 @@ struct linux_dirent64 {
char d_name[0];
} __packed;
/*
* The certificate below and the private key were created by calling:
* openssl req -x509 -newkey rsa:4096 -keyout private.key -out cert.crt
* -days 1000 -sha256 -nodes -outform PEM -subj
* "/C=US/ST=WA/L=Kirkland/O=Example/OU=Org/CN=www.example.com"
*/
char x509_cert[] =
"-----BEGIN CERTIFICATE-----\n"
"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\n"
"-----END CERTIFICATE-----";
char private_key[] =
"-----BEGIN PRIVATE KEY-----\n"
"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\n"
"GyPbW1yZa5vE302a1Cr0Cd7RC4AFAAw=\n"
"-----END PRIVATE KEY-----";
struct test_files_set get_test_files_set(void)
{
static struct test_file files[] = {
@ -290,7 +193,7 @@ char *bin2hex(char *dst, const void *src, size_t count)
return dst;
}
static char *get_index_filename(char *mnt_dir, incfs_uuid_t id)
static char *get_index_filename(const char *mnt_dir, incfs_uuid_t id)
{
char path[FILENAME_MAX];
char str_id[1 + 2 * sizeof(id)];
@ -722,8 +625,6 @@ static int build_mtree(struct test_file *file)
int tree_lvl_index[INCFS_MAX_MTREE_LEVELS] = {};
int tree_lvl_count[INCFS_MAX_MTREE_LEVELS] = {};
int levels_count = 0;
char data_to_sign[256] = {};
int sig_data_size;
int i, level;
if (file->size == 0)
@ -797,19 +698,6 @@ static int build_mtree(struct test_file *file)
sha256(file->mtree[0].data,
INCFS_DATA_FILE_BLOCK_SIZE, file->root_hash);
/* Calculating digital signature */
snprintf(file->sig.add_data, sizeof(file->sig.add_data), "%ld",
file->size);
memcpy(data_to_sign, file->root_hash, SHA256_DIGEST_SIZE);
memcpy(data_to_sign + SHA256_DIGEST_SIZE, file->sig.add_data,
strlen(file->sig.add_data));
sig_data_size = SHA256_DIGEST_SIZE + strlen(file->sig.add_data);
if (!sign_pkcs7(data_to_sign, sig_data_size, private_key, x509_cert,
&file->sig.data, &file->sig.size)) {
ksft_print_msg("Signing failed.\n");
return -EINVAL;
}
return 0;
}
@ -1873,162 +1761,6 @@ static int multiple_providers_test(char *mount_dir)
return TEST_FAILURE;
}
static int signature_test(char *mount_dir)
{
struct test_files_set test = get_test_files_set();
const int file_num = test.files_count;
int i = 0;
unsigned char sig_buf[INCFS_MAX_SIGNATURE_SIZE];
char *backing_dir;
int cmd_fd = -1;
backing_dir = create_backing_dir(mount_dir);
if (!backing_dir)
goto failure;
/* Mount FS and release the backing file. (10s wait time) */
if (mount_fs(mount_dir, backing_dir, 10000) != 0)
goto failure;
cmd_fd = open_commands_file(mount_dir);
if (cmd_fd < 0)
goto failure;
/* Write hashes and data. */
for (i = 0; i < file_num; i++) {
struct test_file *file = &test.files[i];
int res;
build_mtree(file);
res = crypto_emit_file(cmd_fd, NULL, file->name, &file->id,
file->size, file->root_hash,
file->sig.data, file->sig.size, file->sig.add_data);
if (res) {
ksft_print_msg("Emit failed for %s. error: %s\n",
file->name, strerror(-res));
goto failure;
}
if (emit_test_file_data(mount_dir, file))
goto failure;
res = load_hash_tree(mount_dir, file);
if (res) {
ksft_print_msg("Can't load hashes for %s. error: %s\n",
file->name, strerror(-res));
goto failure;
}
}
/* Validate data */
for (i = 0; i < file_num; i++) {
struct test_file *file = &test.files[i];
int sig_len;
char *path;
int fd;
if (validate_test_file_content(mount_dir, file) < 0)
goto failure;
path = concat_file_name(mount_dir, file->name);
fd = open(path, O_RDWR);
free(path);
if (fd < 0) {
print_error("Can't open file");
goto failure;
}
sig_len = get_file_signature(fd, sig_buf, ARRAY_SIZE(sig_buf));
if (close(fd)) {
print_error("Can't close file");
goto failure;
}
if (sig_len < 0) {
ksft_print_msg("Can't load signature %s. error: %s\n",
file->name, strerror(-sig_len));
goto failure;
}
if (sig_len != file->sig.size ||
memcmp(sig_buf, file->sig.data, sig_len)) {
ksft_print_msg("Signature mismatch %s.\n",
file->name);
goto failure;
}
}
/* Unmount and mount again, to make sure the signature is persistent. */
close(cmd_fd);
cmd_fd = -1;
if (umount(mount_dir) != 0) {
print_error("Can't unmout FS");
goto failure;
}
if (mount_fs(mount_dir, backing_dir, 50) != 0)
goto failure;
cmd_fd = open_commands_file(mount_dir);
if (cmd_fd < 0)
goto failure;
/* Validate data again */
for (i = 0; i < file_num; i++) {
struct test_file *file = &test.files[i];
int sig_len;
char *path;
int fd;
if (validate_test_file_content(mount_dir, file) < 0)
goto failure;
path = concat_file_name(mount_dir, file->name);
fd = open(path, O_RDWR);
free(path);
if (fd < 0) {
print_error("Can't open file");
goto failure;
}
sig_len = get_file_signature(fd, sig_buf, ARRAY_SIZE(sig_buf));
if (close(fd)) {
print_error("Can't close file");
goto failure;
}
if (sig_len < 0) {
ksft_print_msg("Can't load signature %s. error: %s\n",
file->name, strerror(-sig_len));
goto failure;
}
if (sig_len != file->sig.size ||
memcmp(sig_buf, file->sig.data, sig_len)) {
ksft_print_msg("Signature mismatch %s.\n",
file->name);
goto failure;
}
}
/* Final unmount */
close(cmd_fd);
cmd_fd = -1;
if (umount(mount_dir) != 0) {
print_error("Can't unmout FS");
goto failure;
}
return TEST_SUCCESS;
failure:
close(cmd_fd);
free(backing_dir);
umount(mount_dir);
return TEST_FAILURE;
}
static int hash_tree_test(char *mount_dir)
{
char *backing_dir;
@ -2058,7 +1790,7 @@ static int hash_tree_test(char *mount_dir)
build_mtree(file);
res = crypto_emit_file(cmd_fd, NULL, file->name, &file->id,
file->size, file->root_hash,
file->sig.data, file->sig.size, file->sig.add_data);
file->sig.add_data);
if (i == corrupted_file_idx) {
/* Corrupt third blocks hash */
@ -2383,7 +2115,6 @@ int main(int argc, char *argv[])
MAKE_TEST(work_after_remount_test),
MAKE_TEST(child_procs_waiting_for_data_test),
MAKE_TEST(multiple_providers_test),
MAKE_TEST(signature_test),
MAKE_TEST(hash_tree_test),
MAKE_TEST(read_log_test),
};

View file

@ -23,7 +23,8 @@
#include "utils.h"
int mount_fs(char *mount_dir, char *backing_dir, int read_timeout_ms)
int mount_fs(const char *mount_dir, const char *backing_dir,
int read_timeout_ms)
{
static const char fs_name[] = INCFS_NAME;
char mount_options[512];
@ -39,7 +40,8 @@ int mount_fs(char *mount_dir, char *backing_dir, int read_timeout_ms)
return result;
}
int mount_fs_opt(char *mount_dir, char *backing_dir, char *opt)
int mount_fs_opt(const char *mount_dir, const char *backing_dir,
const char *opt)
{
static const char fs_name[] = INCFS_NAME;
int result;
@ -50,179 +52,94 @@ int mount_fs_opt(char *mount_dir, char *backing_dir, char *opt)
return result;
}
int unlink_node(int fd, int parent_ino, char *filename)
struct hash_section {
uint32_t algorithm;
uint8_t log2_blocksize;
uint32_t salt_size;
/* no salt */
uint32_t hash_size;
uint8_t hash[SHA256_DIGEST_SIZE];
} __packed;
struct signature_blob {
uint32_t version;
uint32_t hash_section_size;
struct hash_section hash_section;
uint32_t signing_section_size;
uint8_t signing_section[];
} __packed;
size_t format_signature(void **buf, const char *root_hash, const char *add_data)
{
return 0;
size_t size = sizeof(struct signature_blob) + strlen(add_data) + 1;
struct signature_blob *sb = malloc(size);
*sb = (struct signature_blob){
.version = INCFS_SIGNATURE_VERSION,
.hash_section_size = sizeof(struct hash_section),
.hash_section =
(struct hash_section){
.algorithm = INCFS_HASH_TREE_SHA256,
.log2_blocksize = 12,
.salt_size = 0,
.hash_size = SHA256_DIGEST_SIZE,
},
.signing_section_size = sizeof(uint32_t) + strlen(add_data) + 1,
};
memcpy(sb->hash_section.hash, root_hash, SHA256_DIGEST_SIZE);
memcpy((char *)sb->signing_section, add_data, strlen(add_data) + 1);
*buf = sb;
return size;
}
static EVP_PKEY *deserialize_private_key(const char *pem_key)
{
BIO *bio = NULL;
EVP_PKEY *pkey = NULL;
int len = strlen(pem_key);
bio = BIO_new_mem_buf(pem_key, len);
if (!bio)
return NULL;
pkey = PEM_read_bio_PrivateKey(bio, NULL, NULL, NULL);
BIO_free(bio);
return pkey;
}
static X509 *deserialize_cert(const char *pem_cert)
{
BIO *bio = NULL;
X509 *cert = NULL;
int len = strlen(pem_cert);
bio = BIO_new_mem_buf(pem_cert, len);
if (!bio)
return NULL;
cert = PEM_read_bio_X509(bio, NULL, NULL, NULL);
BIO_free(bio);
return cert;
}
bool sign_pkcs7(const void *data_to_sign, size_t data_size,
char *pkey_pem, char *cert_pem,
void **sig_ret, size_t *sig_size_ret)
{
/*
* PKCS#7 signing flags:
*
* - PKCS7_BINARY signing binary data, so skip MIME translation
*
* - PKCS7_NOATTR omit extra authenticated attributes, such as
* SMIMECapabilities
*
* - PKCS7_PARTIAL PKCS7_sign() creates a handle only, then
* PKCS7_sign_add_signer() can add a signer later.
* This is necessary to change the message digest
* algorithm from the default of SHA-1. Requires
* OpenSSL 1.0.0 or later.
*/
int pkcs7_flags = PKCS7_BINARY | PKCS7_NOATTR | PKCS7_PARTIAL;
void *sig;
size_t sig_size;
BIO *bio = NULL;
PKCS7 *p7 = NULL;
EVP_PKEY *pkey = NULL;
X509 *cert = NULL;
bool ok = false;
const EVP_MD *md = EVP_sha256();
pkey = deserialize_private_key(pkey_pem);
if (!pkey) {
printf("deserialize_private_key failed\n");
goto out;
}
cert = deserialize_cert(cert_pem);
if (!cert) {
printf("deserialize_cert failed\n");
goto out;
}
bio = BIO_new_mem_buf(data_to_sign, data_size);
if (!bio)
goto out;
p7 = PKCS7_sign(NULL, NULL, NULL, bio, pkcs7_flags);
if (!p7) {
printf("failed to initialize PKCS#7 signature object\n");
goto out;
}
if (!PKCS7_sign_add_signer(p7, cert, pkey, md, pkcs7_flags)) {
printf("failed to add signer to PKCS#7 signature object\n");
goto out;
}
if (PKCS7_final(p7, bio, pkcs7_flags) != 1) {
printf("failed to finalize PKCS#7 signature\n");
goto out;
}
BIO_free(bio);
bio = BIO_new(BIO_s_mem());
if (!bio) {
printf("out of memory\n");
goto out;
}
if (i2d_PKCS7_bio(bio, p7) != 1) {
printf("failed to DER-encode PKCS#7 signature object\n");
goto out;
}
sig_size = BIO_get_mem_data(bio, &sig);
*sig_ret = malloc(sig_size);
memcpy(*sig_ret, sig, sig_size);
*sig_size_ret = sig_size;
ok = true;
out:
PKCS7_free(p7);
BIO_free(bio);
return ok;
}
int crypto_emit_file(int fd, char *dir, char *filename, incfs_uuid_t *id_out,
size_t size, const char *root_hash, char *sig, size_t sig_size,
char *add_data)
int crypto_emit_file(int fd, const char *dir, const char *filename,
incfs_uuid_t *id_out, size_t size, const char *root_hash,
const char *add_data)
{
int mode = __S_IFREG | 0555;
struct incfs_file_signature_info sig_info = {
.hash_tree_alg = root_hash
? INCFS_HASH_TREE_SHA256
: 0,
.root_hash = ptr_to_u64(root_hash),
.additional_data = ptr_to_u64(add_data),
.additional_data_size = strlen(add_data),
.signature = ptr_to_u64(sig),
.signature_size = sig_size,
};
void *signature;
int error = 0;
struct incfs_new_file_args args = {
.size = size,
.mode = mode,
.file_name = ptr_to_u64(filename),
.directory_path = ptr_to_u64(dir),
.signature_info = ptr_to_u64(&sig_info),
.file_attr = 0,
.file_attr_len = 0
};
args.signature_size = format_signature(&signature, root_hash, add_data);
args.signature_info = ptr_to_u64(signature);
md5(filename, strlen(filename), (char *)args.file_id.bytes);
if (ioctl(fd, INCFS_IOC_CREATE_FILE, &args) != 0)
return -errno;
*id_out = args.file_id;
return 0;
if (ioctl(fd, INCFS_IOC_CREATE_FILE, &args) != 0) {
error = -errno;
goto out;
}
*id_out = args.file_id;
int emit_file(int fd, char *dir, char *filename, incfs_uuid_t *id_out,
size_t size, char *attr)
out:
free(signature);
return error;
}
int emit_file(int fd, const char *dir, const char *filename,
incfs_uuid_t *id_out, size_t size, const char *attr)
{
int mode = __S_IFREG | 0555;
struct incfs_file_signature_info sig_info = {
.hash_tree_alg = 0,
.root_hash = ptr_to_u64(NULL)
};
struct incfs_new_file_args args = {
.size = size,
struct incfs_new_file_args args = { .size = size,
.mode = mode,
.file_name = ptr_to_u64(filename),
.directory_path = ptr_to_u64(dir),
.signature_info = ptr_to_u64(&sig_info),
.signature_info = ptr_to_u64(NULL),
.signature_size = 0,
.file_attr = ptr_to_u64(attr),
.file_attr_len = attr ? strlen(attr) : 0
};
.file_attr_len =
attr ? strlen(attr) : 0 };
md5(filename, strlen(filename), (char *)args.file_id.bytes);
@ -250,7 +167,7 @@ int get_file_signature(int fd, unsigned char *buf, int buf_size)
return -errno;
}
loff_t get_file_size(char *name)
loff_t get_file_size(const char *name)
{
struct stat st;
@ -259,7 +176,7 @@ loff_t get_file_size(char *name)
return -ENOENT;
}
int open_commands_file(char *mount_dir)
int open_commands_file(const char *mount_dir)
{
char cmd_file[255];
int cmd_fd;
@ -273,7 +190,7 @@ int open_commands_file(char *mount_dir)
return cmd_fd;
}
int open_log_file(char *mount_dir)
int open_log_file(const char *mount_dir)
{
char cmd_file[255];
int cmd_fd;
@ -358,7 +275,7 @@ int delete_dir_tree(const char *dir_path)
return result;
}
void sha256(char *data, size_t dsize, char *hash)
void sha256(const char *data, size_t dsize, char *hash)
{
SHA256_CTX ctx;
@ -367,7 +284,7 @@ void sha256(char *data, size_t dsize, char *hash)
SHA256_Final((unsigned char *)hash, &ctx);
}
void md5(char *data, size_t dsize, char *hash)
void md5(const char *data, size_t dsize, char *hash)
{
MD5_CTX ctx;

View file

@ -9,6 +9,8 @@
#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof(arr[0]))
#define __packed __attribute__((__packed__))
#ifdef __LP64__
#define ptr_to_u64(p) ((__u64)p)
#else
@ -17,9 +19,11 @@
#define SHA256_DIGEST_SIZE 32
int mount_fs(char *mount_dir, char *backing_dir, int read_timeout_ms);
int mount_fs(const char *mount_dir, const char *backing_dir,
int read_timeout_ms);
int mount_fs_opt(char *mount_dir, char *backing_dir, char *opt);
int mount_fs_opt(const char *mount_dir, const char *backing_dir,
const char *opt);
int get_file_bmap(int cmd_fd, int ino, unsigned char *buf, int buf_size);
@ -28,32 +32,26 @@ int get_file_signature(int fd, unsigned char *buf, int buf_size);
int emit_node(int fd, char *filename, int *ino_out, int parent_ino,
size_t size, mode_t mode, char *attr);
int emit_file(int fd, char *dir, char *filename, incfs_uuid_t *id_out,
size_t size, char *attr);
int emit_file(int fd, const char *dir, const char *filename,
incfs_uuid_t *id_out, size_t size, const char *attr);
int crypto_emit_file(int fd, char *dir, char *filename, incfs_uuid_t *id_out,
size_t size, const char *root_hash, char *sig, size_t sig_size,
char *add_data);
int crypto_emit_file(int fd, const char *dir, const char *filename,
incfs_uuid_t *id_out, size_t size, const char *root_hash,
const char *add_data);
int unlink_node(int fd, int parent_ino, char *filename);
loff_t get_file_size(const char *name);
loff_t get_file_size(char *name);
int open_commands_file(const char *mount_dir);
int open_commands_file(char *mount_dir);
int open_log_file(char *mount_dir);
int open_log_file(const char *mount_dir);
int wait_for_pending_reads(int fd, int timeout_ms,
struct incfs_pending_read_info *prs, int prs_count);
char *concat_file_name(const char *dir, char *file);
void sha256(char *data, size_t dsize, char *hash);
void sha256(const char *data, size_t dsize, char *hash);
void md5(char *data, size_t dsize, char *hash);
bool sign_pkcs7(const void *data_to_sign, size_t data_size,
char *pkey_pem, char *cert_pem,
void **sig_ret, size_t *sig_size_ret);
void md5(const char *data, size_t dsize, char *hash);
int delete_dir_tree(const char *path);