Skip to content

Commit

Permalink
ubifs: authentication: Add hashes to index nodes
Browse files Browse the repository at this point in the history
With this patch the hashes over the index nodes stored in the tree node
cache are written to flash and are checked when read back from flash.
The hash of the root index node is stored in the master node.

During journal replay the hashes are regenerated from the read nodes
and stored in the tree node cache. This means the nodes must previously
be authenticated by other means. This is done in a later patch.

Signed-off-by: Sascha Hauer <[email protected]>
Signed-off-by: Richard Weinberger <[email protected]>
  • Loading branch information
saschahauer authored and richardweinberger committed Oct 23, 2018
1 parent 823838a commit 16a26b2
Show file tree
Hide file tree
Showing 7 changed files with 81 additions and 14 deletions.
3 changes: 3 additions & 0 deletions fs/ubifs/master.c
Original file line number Diff line number Diff line change
Expand Up @@ -305,6 +305,8 @@ int ubifs_read_master(struct ubifs_info *c)
c->lst.total_dead = le64_to_cpu(c->mst_node->total_dead);
c->lst.total_dark = le64_to_cpu(c->mst_node->total_dark);

ubifs_copy_hash(c, c->mst_node->hash_root_idx, c->zroot.hash);

c->calc_idx_sz = c->bi.old_idx_sz;

if (c->mst_node->flags & cpu_to_le32(UBIFS_MST_NO_ORPHS))
Expand Down Expand Up @@ -378,6 +380,7 @@ int ubifs_write_master(struct ubifs_info *c)
c->mst_offs = offs;
c->mst_node->highest_inum = cpu_to_le64(c->highest_inum);

ubifs_copy_hash(c, c->zroot.hash, c->mst_node->hash_root_idx);
err = ubifs_write_node(c, c->mst_node, len, lnum, offs);
if (err)
return err;
Expand Down
5 changes: 3 additions & 2 deletions fs/ubifs/misc.h
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,8 @@ static inline int ubifs_return_leb(struct ubifs_info *c, int lnum)
*/
static inline int ubifs_idx_node_sz(const struct ubifs_info *c, int child_cnt)
{
return UBIFS_IDX_NODE_SZ + (UBIFS_BRANCH_SZ + c->key_len) * child_cnt;
return UBIFS_IDX_NODE_SZ + (UBIFS_BRANCH_SZ + c->key_len + c->hash_len)
* child_cnt;
}

/**
Expand All @@ -212,7 +213,7 @@ struct ubifs_branch *ubifs_idx_branch(const struct ubifs_info *c,
int bnum)
{
return (struct ubifs_branch *)((void *)idx->branches +
(UBIFS_BRANCH_SZ + c->key_len) * bnum);
(UBIFS_BRANCH_SZ + c->key_len + c->hash_len) * bnum);
}

/**
Expand Down
29 changes: 18 additions & 11 deletions fs/ubifs/replay.c
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ struct replay_entry {
int lnum;
int offs;
int len;
u8 hash[UBIFS_HASH_ARR_SZ];
unsigned int deletion:1;
unsigned long long sqnum;
struct list_head list;
Expand Down Expand Up @@ -228,7 +229,7 @@ static int apply_replay_entry(struct ubifs_info *c, struct replay_entry *r)
err = ubifs_tnc_remove_nm(c, &r->key, &r->nm);
else
err = ubifs_tnc_add_nm(c, &r->key, r->lnum, r->offs,
r->len, NULL, &r->nm);
r->len, r->hash, &r->nm);
} else {
if (r->deletion)
switch (key_type(c, &r->key)) {
Expand All @@ -248,7 +249,7 @@ static int apply_replay_entry(struct ubifs_info *c, struct replay_entry *r)
}
else
err = ubifs_tnc_add(c, &r->key, r->lnum, r->offs,
r->len, NULL);
r->len, r->hash);
if (err)
return err;

Expand Down Expand Up @@ -352,9 +353,9 @@ static void destroy_replay_list(struct ubifs_info *c)
* in case of success and a negative error code in case of failure.
*/
static int insert_node(struct ubifs_info *c, int lnum, int offs, int len,
union ubifs_key *key, unsigned long long sqnum,
int deletion, int *used, loff_t old_size,
loff_t new_size)
const u8 *hash, union ubifs_key *key,
unsigned long long sqnum, int deletion, int *used,
loff_t old_size, loff_t new_size)
{
struct replay_entry *r;

Expand All @@ -372,6 +373,7 @@ static int insert_node(struct ubifs_info *c, int lnum, int offs, int len,
r->lnum = lnum;
r->offs = offs;
r->len = len;
ubifs_copy_hash(c, hash, r->hash);
r->deletion = !!deletion;
r->sqnum = sqnum;
key_copy(c, key, &r->key);
Expand Down Expand Up @@ -400,8 +402,9 @@ static int insert_node(struct ubifs_info *c, int lnum, int offs, int len,
* negative error code in case of failure.
*/
static int insert_dent(struct ubifs_info *c, int lnum, int offs, int len,
union ubifs_key *key, const char *name, int nlen,
unsigned long long sqnum, int deletion, int *used)
const u8 *hash, union ubifs_key *key,
const char *name, int nlen, unsigned long long sqnum,
int deletion, int *used)
{
struct replay_entry *r;
char *nbuf;
Expand All @@ -425,6 +428,7 @@ static int insert_dent(struct ubifs_info *c, int lnum, int offs, int len,
r->lnum = lnum;
r->offs = offs;
r->len = len;
ubifs_copy_hash(c, hash, r->hash);
r->deletion = !!deletion;
r->sqnum = sqnum;
key_copy(c, key, &r->key);
Expand Down Expand Up @@ -582,6 +586,7 @@ static int replay_bud(struct ubifs_info *c, struct bud_entry *b)
*/

list_for_each_entry(snod, &sleb->nodes, list) {
u8 hash[UBIFS_HASH_ARR_SZ];
int deletion = 0;

cond_resched();
Expand All @@ -591,6 +596,8 @@ static int replay_bud(struct ubifs_info *c, struct bud_entry *b)
goto out_dump;
}

ubifs_node_calc_hash(c, snod->node, hash);

if (snod->sqnum > c->max_sqnum)
c->max_sqnum = snod->sqnum;

Expand All @@ -602,7 +609,7 @@ static int replay_bud(struct ubifs_info *c, struct bud_entry *b)

if (le32_to_cpu(ino->nlink) == 0)
deletion = 1;
err = insert_node(c, lnum, snod->offs, snod->len,
err = insert_node(c, lnum, snod->offs, snod->len, hash,
&snod->key, snod->sqnum, deletion,
&used, 0, new_size);
break;
Expand All @@ -614,7 +621,7 @@ static int replay_bud(struct ubifs_info *c, struct bud_entry *b)
key_block(c, &snod->key) *
UBIFS_BLOCK_SIZE;

err = insert_node(c, lnum, snod->offs, snod->len,
err = insert_node(c, lnum, snod->offs, snod->len, hash,
&snod->key, snod->sqnum, deletion,
&used, 0, new_size);
break;
Expand All @@ -628,7 +635,7 @@ static int replay_bud(struct ubifs_info *c, struct bud_entry *b)
if (err)
goto out_dump;

err = insert_dent(c, lnum, snod->offs, snod->len,
err = insert_dent(c, lnum, snod->offs, snod->len, hash,
&snod->key, dent->name,
le16_to_cpu(dent->nlen), snod->sqnum,
!le64_to_cpu(dent->inum), &used);
Expand All @@ -654,7 +661,7 @@ static int replay_bud(struct ubifs_info *c, struct bud_entry *b)
* functions which expect nodes to have keys.
*/
trun_key_init(c, &key, le32_to_cpu(trun->inum));
err = insert_node(c, lnum, snod->offs, snod->len,
err = insert_node(c, lnum, snod->offs, snod->len, hash,
&key, snod->sqnum, 1, &used,
old_size, new_size);
break;
Expand Down
12 changes: 12 additions & 0 deletions fs/ubifs/tnc.c
Original file line number Diff line number Diff line change
Expand Up @@ -488,6 +488,12 @@ static int try_read_node(const struct ubifs_info *c, void *buf, int type,
if (crc != node_crc)
return 0;

err = ubifs_node_check_hash(c, buf, zbr->hash);
if (err) {
ubifs_bad_hash(c, buf, zbr->hash, lnum, offs);
return 0;
}

return 1;
}

Expand Down Expand Up @@ -1713,6 +1719,12 @@ static int validate_data_node(struct ubifs_info *c, void *buf,
goto out;
}

err = ubifs_node_check_hash(c, buf, zbr->hash);
if (err) {
ubifs_bad_hash(c, buf, zbr->hash, zbr->lnum, zbr->offs);
return err;
}

len = le32_to_cpu(ch->len);
if (len != zbr->len) {
ubifs_err(c, "bad node length %d, expected %d", len, zbr->len);
Expand Down
27 changes: 27 additions & 0 deletions fs/ubifs/tnc_commit.c
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ static int make_idx_node(struct ubifs_info *c, struct ubifs_idx_node *idx,
struct ubifs_znode *znode, int lnum, int offs, int len)
{
struct ubifs_znode *zp;
u8 hash[UBIFS_HASH_ARR_SZ];
int i, err;

/* Make index node */
Expand All @@ -52,6 +53,7 @@ static int make_idx_node(struct ubifs_info *c, struct ubifs_idx_node *idx,
br->lnum = cpu_to_le32(zbr->lnum);
br->offs = cpu_to_le32(zbr->offs);
br->len = cpu_to_le32(zbr->len);
ubifs_copy_hash(c, zbr->hash, ubifs_branch_hash(c, br));
if (!zbr->lnum || !zbr->len) {
ubifs_err(c, "bad ref in znode");
ubifs_dump_znode(c, znode);
Expand All @@ -62,6 +64,7 @@ static int make_idx_node(struct ubifs_info *c, struct ubifs_idx_node *idx,
}
}
ubifs_prepare_node(c, idx, len, 0);
ubifs_node_calc_hash(c, idx, hash);

znode->lnum = lnum;
znode->offs = offs;
Expand All @@ -78,10 +81,12 @@ static int make_idx_node(struct ubifs_info *c, struct ubifs_idx_node *idx,
zbr->lnum = lnum;
zbr->offs = offs;
zbr->len = len;
ubifs_copy_hash(c, hash, zbr->hash);
} else {
c->zroot.lnum = lnum;
c->zroot.offs = offs;
c->zroot.len = len;
ubifs_copy_hash(c, hash, c->zroot.hash);
}
c->calc_idx_sz += ALIGN(len, 8);

Expand Down Expand Up @@ -647,6 +652,8 @@ static int get_znodes_to_commit(struct ubifs_info *c)
znode->cnext = c->cnext;
break;
}
znode->cparent = znode->parent;
znode->ciip = znode->iip;
znode->cnext = cnext;
znode = cnext;
cnt += 1;
Expand Down Expand Up @@ -840,6 +847,8 @@ static int write_index(struct ubifs_info *c)
}

while (1) {
u8 hash[UBIFS_HASH_ARR_SZ];

cond_resched();

znode = cnext;
Expand All @@ -857,6 +866,7 @@ static int write_index(struct ubifs_info *c)
br->lnum = cpu_to_le32(zbr->lnum);
br->offs = cpu_to_le32(zbr->offs);
br->len = cpu_to_le32(zbr->len);
ubifs_copy_hash(c, zbr->hash, ubifs_branch_hash(c, br));
if (!zbr->lnum || !zbr->len) {
ubifs_err(c, "bad ref in znode");
ubifs_dump_znode(c, znode);
Expand All @@ -868,6 +878,23 @@ static int write_index(struct ubifs_info *c)
}
len = ubifs_idx_node_sz(c, znode->child_cnt);
ubifs_prepare_node(c, idx, len, 0);
ubifs_node_calc_hash(c, idx, hash);

mutex_lock(&c->tnc_mutex);

if (znode->cparent)
ubifs_copy_hash(c, hash,
znode->cparent->zbranch[znode->ciip].hash);

if (znode->parent) {
if (!ubifs_zn_obsolete(znode))
ubifs_copy_hash(c, hash,
znode->parent->zbranch[znode->iip].hash);
} else {
ubifs_copy_hash(c, hash, c->zroot.hash);
}

mutex_unlock(&c->tnc_mutex);

/* Determine the index node position */
if (lnum == -1) {
Expand Down
15 changes: 14 additions & 1 deletion fs/ubifs/tnc_misc.c
Original file line number Diff line number Diff line change
Expand Up @@ -293,6 +293,12 @@ static int read_znode(struct ubifs_info *c, struct ubifs_zbranch *zzbr,
return err;
}

err = ubifs_node_check_hash(c, idx, zzbr->hash);
if (err) {
ubifs_bad_hash(c, idx, zzbr->hash, lnum, offs);
return err;
}

znode->child_cnt = le16_to_cpu(idx->child_cnt);
znode->level = le16_to_cpu(idx->level);

Expand All @@ -309,13 +315,14 @@ static int read_znode(struct ubifs_info *c, struct ubifs_zbranch *zzbr,
}

for (i = 0; i < znode->child_cnt; i++) {
const struct ubifs_branch *br = ubifs_idx_branch(c, idx, i);
struct ubifs_branch *br = ubifs_idx_branch(c, idx, i);
struct ubifs_zbranch *zbr = &znode->zbranch[i];

key_read(c, &br->key, &zbr->key);
zbr->lnum = le32_to_cpu(br->lnum);
zbr->offs = le32_to_cpu(br->offs);
zbr->len = le32_to_cpu(br->len);
ubifs_copy_hash(c, ubifs_branch_hash(c, br), zbr->hash);
zbr->znode = NULL;

/* Validate branch */
Expand Down Expand Up @@ -497,5 +504,11 @@ int ubifs_tnc_read_node(struct ubifs_info *c, struct ubifs_zbranch *zbr,
return -EINVAL;
}

err = ubifs_node_check_hash(c, node, zbr->hash);
if (err) {
ubifs_bad_hash(c, node, zbr->hash, zbr->lnum, zbr->offs);
return err;
}

return 0;
}
4 changes: 4 additions & 0 deletions fs/ubifs/ubifs.h
Original file line number Diff line number Diff line change
Expand Up @@ -765,6 +765,8 @@ struct ubifs_zbranch {
* struct ubifs_znode - in-memory representation of an indexing node.
* @parent: parent znode or NULL if it is the root
* @cnext: next znode to commit
* @cparent: parent node for this commit
* @ciip: index in cparent's zbranch array
* @flags: znode flags (%DIRTY_ZNODE, %COW_ZNODE or %OBSOLETE_ZNODE)
* @time: last access time (seconds)
* @level: level of the entry in the TNC tree
Expand All @@ -782,6 +784,8 @@ struct ubifs_zbranch {
struct ubifs_znode {
struct ubifs_znode *parent;
struct ubifs_znode *cnext;
struct ubifs_znode *cparent;
int ciip;
unsigned long flags;
time64_t time;
int level;
Expand Down

0 comments on commit 16a26b2

Please sign in to comment.