Commit 16a26b20 authored by Sascha Hauer's avatar Sascha Hauer Committed by Richard Weinberger

ubifs: authentication: Add hashes to index nodes

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: default avatarSascha Hauer <s.hauer@pengutronix.de>
Signed-off-by: default avatarRichard Weinberger <richard@nod.at>
parent 823838a4
...@@ -305,6 +305,8 @@ int ubifs_read_master(struct ubifs_info *c) ...@@ -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_dead = le64_to_cpu(c->mst_node->total_dead);
c->lst.total_dark = le64_to_cpu(c->mst_node->total_dark); 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; c->calc_idx_sz = c->bi.old_idx_sz;
if (c->mst_node->flags & cpu_to_le32(UBIFS_MST_NO_ORPHS)) if (c->mst_node->flags & cpu_to_le32(UBIFS_MST_NO_ORPHS))
...@@ -378,6 +380,7 @@ int ubifs_write_master(struct ubifs_info *c) ...@@ -378,6 +380,7 @@ int ubifs_write_master(struct ubifs_info *c)
c->mst_offs = offs; c->mst_offs = offs;
c->mst_node->highest_inum = cpu_to_le64(c->highest_inum); 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); err = ubifs_write_node(c, c->mst_node, len, lnum, offs);
if (err) if (err)
return err; return err;
......
...@@ -197,7 +197,8 @@ static inline int ubifs_return_leb(struct ubifs_info *c, int lnum) ...@@ -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) 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;
} }
/** /**
...@@ -212,7 +213,7 @@ struct ubifs_branch *ubifs_idx_branch(const struct ubifs_info *c, ...@@ -212,7 +213,7 @@ struct ubifs_branch *ubifs_idx_branch(const struct ubifs_info *c,
int bnum) int bnum)
{ {
return (struct ubifs_branch *)((void *)idx->branches + return (struct ubifs_branch *)((void *)idx->branches +
(UBIFS_BRANCH_SZ + c->key_len) * bnum); (UBIFS_BRANCH_SZ + c->key_len + c->hash_len) * bnum);
} }
/** /**
......
...@@ -56,6 +56,7 @@ struct replay_entry { ...@@ -56,6 +56,7 @@ struct replay_entry {
int lnum; int lnum;
int offs; int offs;
int len; int len;
u8 hash[UBIFS_HASH_ARR_SZ];
unsigned int deletion:1; unsigned int deletion:1;
unsigned long long sqnum; unsigned long long sqnum;
struct list_head list; struct list_head list;
...@@ -228,7 +229,7 @@ static int apply_replay_entry(struct ubifs_info *c, struct replay_entry *r) ...@@ -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); err = ubifs_tnc_remove_nm(c, &r->key, &r->nm);
else else
err = ubifs_tnc_add_nm(c, &r->key, r->lnum, r->offs, err = ubifs_tnc_add_nm(c, &r->key, r->lnum, r->offs,
r->len, NULL, &r->nm); r->len, r->hash, &r->nm);
} else { } else {
if (r->deletion) if (r->deletion)
switch (key_type(c, &r->key)) { switch (key_type(c, &r->key)) {
...@@ -248,7 +249,7 @@ static int apply_replay_entry(struct ubifs_info *c, struct replay_entry *r) ...@@ -248,7 +249,7 @@ static int apply_replay_entry(struct ubifs_info *c, struct replay_entry *r)
} }
else else
err = ubifs_tnc_add(c, &r->key, r->lnum, r->offs, err = ubifs_tnc_add(c, &r->key, r->lnum, r->offs,
r->len, NULL); r->len, r->hash);
if (err) if (err)
return err; return err;
...@@ -352,9 +353,9 @@ static void destroy_replay_list(struct ubifs_info *c) ...@@ -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. * 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, static int insert_node(struct ubifs_info *c, int lnum, int offs, int len,
union ubifs_key *key, unsigned long long sqnum, const u8 *hash, union ubifs_key *key,
int deletion, int *used, loff_t old_size, unsigned long long sqnum, int deletion, int *used,
loff_t new_size) loff_t old_size, loff_t new_size)
{ {
struct replay_entry *r; struct replay_entry *r;
...@@ -372,6 +373,7 @@ static int insert_node(struct ubifs_info *c, int lnum, int offs, int len, ...@@ -372,6 +373,7 @@ static int insert_node(struct ubifs_info *c, int lnum, int offs, int len,
r->lnum = lnum; r->lnum = lnum;
r->offs = offs; r->offs = offs;
r->len = len; r->len = len;
ubifs_copy_hash(c, hash, r->hash);
r->deletion = !!deletion; r->deletion = !!deletion;
r->sqnum = sqnum; r->sqnum = sqnum;
key_copy(c, key, &r->key); key_copy(c, key, &r->key);
...@@ -400,8 +402,9 @@ static int insert_node(struct ubifs_info *c, int lnum, int offs, int len, ...@@ -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. * negative error code in case of failure.
*/ */
static int insert_dent(struct ubifs_info *c, int lnum, int offs, int len, static int insert_dent(struct ubifs_info *c, int lnum, int offs, int len,
union ubifs_key *key, const char *name, int nlen, const u8 *hash, union ubifs_key *key,
unsigned long long sqnum, int deletion, int *used) const char *name, int nlen, unsigned long long sqnum,
int deletion, int *used)
{ {
struct replay_entry *r; struct replay_entry *r;
char *nbuf; char *nbuf;
...@@ -425,6 +428,7 @@ static int insert_dent(struct ubifs_info *c, int lnum, int offs, int len, ...@@ -425,6 +428,7 @@ static int insert_dent(struct ubifs_info *c, int lnum, int offs, int len,
r->lnum = lnum; r->lnum = lnum;
r->offs = offs; r->offs = offs;
r->len = len; r->len = len;
ubifs_copy_hash(c, hash, r->hash);
r->deletion = !!deletion; r->deletion = !!deletion;
r->sqnum = sqnum; r->sqnum = sqnum;
key_copy(c, key, &r->key); key_copy(c, key, &r->key);
...@@ -582,6 +586,7 @@ static int replay_bud(struct ubifs_info *c, struct bud_entry *b) ...@@ -582,6 +586,7 @@ static int replay_bud(struct ubifs_info *c, struct bud_entry *b)
*/ */
list_for_each_entry(snod, &sleb->nodes, list) { list_for_each_entry(snod, &sleb->nodes, list) {
u8 hash[UBIFS_HASH_ARR_SZ];
int deletion = 0; int deletion = 0;
cond_resched(); cond_resched();
...@@ -591,6 +596,8 @@ static int replay_bud(struct ubifs_info *c, struct bud_entry *b) ...@@ -591,6 +596,8 @@ static int replay_bud(struct ubifs_info *c, struct bud_entry *b)
goto out_dump; goto out_dump;
} }
ubifs_node_calc_hash(c, snod->node, hash);
if (snod->sqnum > c->max_sqnum) if (snod->sqnum > c->max_sqnum)
c->max_sqnum = snod->sqnum; c->max_sqnum = snod->sqnum;
...@@ -602,7 +609,7 @@ static int replay_bud(struct ubifs_info *c, struct bud_entry *b) ...@@ -602,7 +609,7 @@ static int replay_bud(struct ubifs_info *c, struct bud_entry *b)
if (le32_to_cpu(ino->nlink) == 0) if (le32_to_cpu(ino->nlink) == 0)
deletion = 1; 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, &snod->key, snod->sqnum, deletion,
&used, 0, new_size); &used, 0, new_size);
break; break;
...@@ -614,7 +621,7 @@ static int replay_bud(struct ubifs_info *c, struct bud_entry *b) ...@@ -614,7 +621,7 @@ static int replay_bud(struct ubifs_info *c, struct bud_entry *b)
key_block(c, &snod->key) * key_block(c, &snod->key) *
UBIFS_BLOCK_SIZE; 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, &snod->key, snod->sqnum, deletion,
&used, 0, new_size); &used, 0, new_size);
break; break;
...@@ -628,7 +635,7 @@ static int replay_bud(struct ubifs_info *c, struct bud_entry *b) ...@@ -628,7 +635,7 @@ static int replay_bud(struct ubifs_info *c, struct bud_entry *b)
if (err) if (err)
goto out_dump; 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, &snod->key, dent->name,
le16_to_cpu(dent->nlen), snod->sqnum, le16_to_cpu(dent->nlen), snod->sqnum,
!le64_to_cpu(dent->inum), &used); !le64_to_cpu(dent->inum), &used);
...@@ -654,7 +661,7 @@ static int replay_bud(struct ubifs_info *c, struct bud_entry *b) ...@@ -654,7 +661,7 @@ static int replay_bud(struct ubifs_info *c, struct bud_entry *b)
* functions which expect nodes to have keys. * functions which expect nodes to have keys.
*/ */
trun_key_init(c, &key, le32_to_cpu(trun->inum)); 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, &key, snod->sqnum, 1, &used,
old_size, new_size); old_size, new_size);
break; break;
......
...@@ -488,6 +488,12 @@ static int try_read_node(const struct ubifs_info *c, void *buf, int type, ...@@ -488,6 +488,12 @@ static int try_read_node(const struct ubifs_info *c, void *buf, int type,
if (crc != node_crc) if (crc != node_crc)
return 0; 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; return 1;
} }
...@@ -1713,6 +1719,12 @@ static int validate_data_node(struct ubifs_info *c, void *buf, ...@@ -1713,6 +1719,12 @@ static int validate_data_node(struct ubifs_info *c, void *buf,
goto out; 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); len = le32_to_cpu(ch->len);
if (len != zbr->len) { if (len != zbr->len) {
ubifs_err(c, "bad node length %d, expected %d", len, zbr->len); ubifs_err(c, "bad node length %d, expected %d", len, zbr->len);
......
...@@ -38,6 +38,7 @@ static int make_idx_node(struct ubifs_info *c, struct ubifs_idx_node *idx, ...@@ -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 *znode, int lnum, int offs, int len)
{ {
struct ubifs_znode *zp; struct ubifs_znode *zp;
u8 hash[UBIFS_HASH_ARR_SZ];
int i, err; int i, err;
/* Make index node */ /* Make index node */
...@@ -52,6 +53,7 @@ static int make_idx_node(struct ubifs_info *c, struct ubifs_idx_node *idx, ...@@ -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->lnum = cpu_to_le32(zbr->lnum);
br->offs = cpu_to_le32(zbr->offs); br->offs = cpu_to_le32(zbr->offs);
br->len = cpu_to_le32(zbr->len); br->len = cpu_to_le32(zbr->len);
ubifs_copy_hash(c, zbr->hash, ubifs_branch_hash(c, br));
if (!zbr->lnum || !zbr->len) { if (!zbr->lnum || !zbr->len) {
ubifs_err(c, "bad ref in znode"); ubifs_err(c, "bad ref in znode");
ubifs_dump_znode(c, znode); ubifs_dump_znode(c, znode);
...@@ -62,6 +64,7 @@ static int make_idx_node(struct ubifs_info *c, struct ubifs_idx_node *idx, ...@@ -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_prepare_node(c, idx, len, 0);
ubifs_node_calc_hash(c, idx, hash);
znode->lnum = lnum; znode->lnum = lnum;
znode->offs = offs; znode->offs = offs;
...@@ -78,10 +81,12 @@ static int make_idx_node(struct ubifs_info *c, struct ubifs_idx_node *idx, ...@@ -78,10 +81,12 @@ static int make_idx_node(struct ubifs_info *c, struct ubifs_idx_node *idx,
zbr->lnum = lnum; zbr->lnum = lnum;
zbr->offs = offs; zbr->offs = offs;
zbr->len = len; zbr->len = len;
ubifs_copy_hash(c, hash, zbr->hash);
} else { } else {
c->zroot.lnum = lnum; c->zroot.lnum = lnum;
c->zroot.offs = offs; c->zroot.offs = offs;
c->zroot.len = len; c->zroot.len = len;
ubifs_copy_hash(c, hash, c->zroot.hash);
} }
c->calc_idx_sz += ALIGN(len, 8); c->calc_idx_sz += ALIGN(len, 8);
...@@ -647,6 +652,8 @@ static int get_znodes_to_commit(struct ubifs_info *c) ...@@ -647,6 +652,8 @@ static int get_znodes_to_commit(struct ubifs_info *c)
znode->cnext = c->cnext; znode->cnext = c->cnext;
break; break;
} }
znode->cparent = znode->parent;
znode->ciip = znode->iip;
znode->cnext = cnext; znode->cnext = cnext;
znode = cnext; znode = cnext;
cnt += 1; cnt += 1;
...@@ -840,6 +847,8 @@ static int write_index(struct ubifs_info *c) ...@@ -840,6 +847,8 @@ static int write_index(struct ubifs_info *c)
} }
while (1) { while (1) {
u8 hash[UBIFS_HASH_ARR_SZ];
cond_resched(); cond_resched();
znode = cnext; znode = cnext;
...@@ -857,6 +866,7 @@ static int write_index(struct ubifs_info *c) ...@@ -857,6 +866,7 @@ static int write_index(struct ubifs_info *c)
br->lnum = cpu_to_le32(zbr->lnum); br->lnum = cpu_to_le32(zbr->lnum);
br->offs = cpu_to_le32(zbr->offs); br->offs = cpu_to_le32(zbr->offs);
br->len = cpu_to_le32(zbr->len); br->len = cpu_to_le32(zbr->len);
ubifs_copy_hash(c, zbr->hash, ubifs_branch_hash(c, br));
if (!zbr->lnum || !zbr->len) { if (!zbr->lnum || !zbr->len) {
ubifs_err(c, "bad ref in znode"); ubifs_err(c, "bad ref in znode");
ubifs_dump_znode(c, znode); ubifs_dump_znode(c, znode);
...@@ -868,6 +878,23 @@ static int write_index(struct ubifs_info *c) ...@@ -868,6 +878,23 @@ static int write_index(struct ubifs_info *c)
} }
len = ubifs_idx_node_sz(c, znode->child_cnt); len = ubifs_idx_node_sz(c, znode->child_cnt);
ubifs_prepare_node(c, idx, len, 0); 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 */ /* Determine the index node position */
if (lnum == -1) { if (lnum == -1) {
......
...@@ -293,6 +293,12 @@ static int read_znode(struct ubifs_info *c, struct ubifs_zbranch *zzbr, ...@@ -293,6 +293,12 @@ static int read_znode(struct ubifs_info *c, struct ubifs_zbranch *zzbr,
return err; 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->child_cnt = le16_to_cpu(idx->child_cnt);
znode->level = le16_to_cpu(idx->level); znode->level = le16_to_cpu(idx->level);
...@@ -309,13 +315,14 @@ static int read_znode(struct ubifs_info *c, struct ubifs_zbranch *zzbr, ...@@ -309,13 +315,14 @@ static int read_znode(struct ubifs_info *c, struct ubifs_zbranch *zzbr,
} }
for (i = 0; i < znode->child_cnt; i++) { 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]; struct ubifs_zbranch *zbr = &znode->zbranch[i];
key_read(c, &br->key, &zbr->key); key_read(c, &br->key, &zbr->key);
zbr->lnum = le32_to_cpu(br->lnum); zbr->lnum = le32_to_cpu(br->lnum);
zbr->offs = le32_to_cpu(br->offs); zbr->offs = le32_to_cpu(br->offs);
zbr->len = le32_to_cpu(br->len); zbr->len = le32_to_cpu(br->len);
ubifs_copy_hash(c, ubifs_branch_hash(c, br), zbr->hash);
zbr->znode = NULL; zbr->znode = NULL;
/* Validate branch */ /* Validate branch */
...@@ -497,5 +504,11 @@ int ubifs_tnc_read_node(struct ubifs_info *c, struct ubifs_zbranch *zbr, ...@@ -497,5 +504,11 @@ int ubifs_tnc_read_node(struct ubifs_info *c, struct ubifs_zbranch *zbr,
return -EINVAL; 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; return 0;
} }
...@@ -765,6 +765,8 @@ struct ubifs_zbranch { ...@@ -765,6 +765,8 @@ struct ubifs_zbranch {
* struct ubifs_znode - in-memory representation of an indexing node. * struct ubifs_znode - in-memory representation of an indexing node.
* @parent: parent znode or NULL if it is the root * @parent: parent znode or NULL if it is the root
* @cnext: next znode to commit * @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) * @flags: znode flags (%DIRTY_ZNODE, %COW_ZNODE or %OBSOLETE_ZNODE)
* @time: last access time (seconds) * @time: last access time (seconds)
* @level: level of the entry in the TNC tree * @level: level of the entry in the TNC tree
...@@ -782,6 +784,8 @@ struct ubifs_zbranch { ...@@ -782,6 +784,8 @@ struct ubifs_zbranch {
struct ubifs_znode { struct ubifs_znode {
struct ubifs_znode *parent; struct ubifs_znode *parent;
struct ubifs_znode *cnext; struct ubifs_znode *cnext;
struct ubifs_znode *cparent;
int ciip;
unsigned long flags; unsigned long flags;
time64_t time; time64_t time;
int level; int level;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment