Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

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

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
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -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))
@@ -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;
+3 −2
Original line number Diff line number Diff line
@@ -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;
}

/**
@@ -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);
}

/**
+18 −11
Original line number Diff line number Diff line
@@ -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;
@@ -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)) {
@@ -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;

@@ -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;

@@ -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);
@@ -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;
@@ -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);
@@ -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();
@@ -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;

@@ -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;
@@ -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;
@@ -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);
@@ -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;
+12 −0
Original line number Diff line number Diff line
@@ -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;
}

@@ -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);
+27 −0
Original line number Diff line number Diff line
@@ -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 */
@@ -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);
@@ -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;
@@ -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);

@@ -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;
@@ -840,6 +847,8 @@ static int write_index(struct ubifs_info *c)
	}

	while (1) {
		u8 hash[UBIFS_HASH_ARR_SZ];

		cond_resched();

		znode = cnext;
@@ -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);
@@ -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) {
Loading