All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 00/25] UBIFS authentication support
@ 2018-07-04 12:41 Sascha Hauer
  2018-07-04 12:41 ` [PATCH 01/25] ubifs: refactor create_default_filesystem() Sascha Hauer
                   ` (24 more replies)
  0 siblings, 25 replies; 49+ messages in thread
From: Sascha Hauer @ 2018-07-04 12:41 UTC (permalink / raw)
  To: linux-mtd
  Cc: David Gstir, Richard Weinberger, kernel, linux-kernel, Sascha Hauer

This patchset introduces UBIFS authentication support. With authentication
enabled UBIFS is fully protected against offline changes. This is done by
hashing the different parts of UBIFS and protecting the toplevel hashes with
HMACs. The parts that are protected are:

* the index tree
* the journal
* the LPT
* the master nodes
* the superblock node

A detailed overview how the different parts are authenticated can be found
here:

https://github.com/sigma-star/ubifs-authentication/blob/master/ubifs-authentication-whitepaper.md

However, some details still had to be changed, so an updated version of that
document is part of this patchset.

Usage:
======

First add an authentication key to the kernel keyring. It must be of type
'logon'. The description can be freely chosen, it must be passed as mount
option later:

# keyctl add logon ubifs:foo 12345678901234567890123456789012 @s

Attach the UBI device and mount with auth_key=$description and
auth_hash_name=$algo:

# mount -t ubifs /dev/ubi0_0 -o auth_key=ubifs:foo,auth_hash_name=sha256 /mnt/

This mounts the UBIFS in authenticated mode. The hash algorithm can be freely
chosen from include/uapi/linux/hash_info.h as long as the digest is at maximum
64 bytes which is the space we reserved in the UBIFS structures. We always use
the same algorithms for creating HMACs, so using sha256 for hashing means that
we also use hmac(sha256) for creating authentication data.

When the authentication_key mount option is given, a UBIFS image which can be
authenticated with that key is mandatory, no unauthenticated image will be
accepted. Likewise, when the option is not given, no authenticated image can be
accepted since that couldn't be authenticated. We could skip authentication in
this case, but we couldn't create any valid HMACs when writing new data. We
could make it an option to mount in readonly mode for debugging purposes when
we do not have a key (or we already know that parts of the UBIFS image are
corrupted), but that is not implemented yet.

Offline signed images
=====================

Currently UBIFS authentication is only supported on the default filesystem the
kernel creates when an empty UBI volume is found. Support for offline signed
images is planned and this series already contains a patch which adds support
for it, but there's no mkfs.ubifs support for signed images yet.

Testing
=======

I've gone through various tests including powercut tests over the weekend and
running xfstests. It is tested on real hardware (i.MX6 based) on a 2k page NAND
and in nandsim on a simulated 512b page NAND in big LPT mode.  Currently I am
not aware of any issues, but this is v1 of the series, so please review
carefully and if possible try to break it yourself.

This patchset is based on v4.18-rc3 and can be obtained here:

git://git.pengutronix.de/sha/linux ubifs-authentication-v1

/Sascha

Sascha Hauer (25):
  ubifs: refactor create_default_filesystem()
  ubifs: pass ubifs_zbranch to try_read_node()
  ubifs: pass ubifs_zbranch to read_znode()
  ubifs: export pnode_lookup as ubifs_pnode_lookup
  ubifs: implement ubifs_lpt_lookup using ubifs_pnode_lookup
  ubifs: drop write_node
  ubifs: Store read superblock node
  ubifs: Format changes for authentication support
  ubifs: add separate functions to init/crc a node
  ubifs: add helper functions for authentication support
  ubifs: Create functions to embed a HMAC in a node
  ubifs: Add hashes to the tree node cache
  ubifs: authentication: Add hashes to index nodes
  ubifs: Add authentication nodes to journal
  ubifs: Add auth nodes to garbage collector journal head
  ubifs: authenticate replayed journal
  ubifs: authentication: authenticate LPT
  ubfis: authentication: authenticate master node
  ubifs: Create hash for default LPT
  ubifs: authentication: Authenticate super block node
  ubifs: Add hashes and HMACs to default filesystem
  ubifs: do not update inode size in-place in authenticated mode
  ubifs: Enable authentication support
  ubifs: support offline signed images
  Documentation: ubifs: Add authentication whitepaper

 .../filesystems/ubifs-authentication.md       | 426 +++++++++++++++
 Documentation/filesystems/ubifs.txt           |   7 +
 fs/ubifs/Kconfig                              |  12 +
 fs/ubifs/Makefile                             |   1 +
 fs/ubifs/auth.c                               | 489 ++++++++++++++++++
 fs/ubifs/debug.c                              |   6 +
 fs/ubifs/gc.c                                 |  40 +-
 fs/ubifs/io.c                                 |  96 +++-
 fs/ubifs/journal.c                            | 224 +++++---
 fs/ubifs/log.c                                |  17 +
 fs/ubifs/lpt.c                                | 174 ++++++-
 fs/ubifs/lpt_commit.c                         |  44 +-
 fs/ubifs/master.c                             |  69 ++-
 fs/ubifs/misc.h                               |   5 +-
 fs/ubifs/recovery.c                           | 118 +++--
 fs/ubifs/replay.c                             | 147 +++++-
 fs/ubifs/sb.c                                 | 208 +++++---
 fs/ubifs/super.c                              | 106 +++-
 fs/ubifs/tnc.c                                |  37 +-
 fs/ubifs/tnc_commit.c                         |  26 +
 fs/ubifs/tnc_misc.c                           |  27 +-
 fs/ubifs/ubifs-media.h                        |  65 ++-
 fs/ubifs/ubifs.h                              | 225 +++++++-
 23 files changed, 2280 insertions(+), 289 deletions(-)
 create mode 100644 Documentation/filesystems/ubifs-authentication.md
 create mode 100644 fs/ubifs/auth.c

-- 
2.18.0


^ permalink raw reply	[flat|nested] 49+ messages in thread

* [PATCH 01/25] ubifs: refactor create_default_filesystem()
  2018-07-04 12:41 [PATCH 00/25] UBIFS authentication support Sascha Hauer
@ 2018-07-04 12:41 ` Sascha Hauer
  2018-07-04 12:41 ` [PATCH 02/25] ubifs: pass ubifs_zbranch to try_read_node() Sascha Hauer
                   ` (23 subsequent siblings)
  24 siblings, 0 replies; 49+ messages in thread
From: Sascha Hauer @ 2018-07-04 12:41 UTC (permalink / raw)
  To: linux-mtd
  Cc: David Gstir, Richard Weinberger, kernel, linux-kernel, Sascha Hauer

create_default_filesystem() allocates memory for a node, writes that
node and frees the memory directly afterwards. With this patch we
allocate memory for all nodes at the beginning of the function and
free the memory at the end. This makes it easier to implement
authentication support since with authentication support we'll need
the contents of some nodes when creating other nodes.

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
 fs/ubifs/sb.c | 95 +++++++++++++++++++++++++--------------------------
 1 file changed, 47 insertions(+), 48 deletions(-)

diff --git a/fs/ubifs/sb.c b/fs/ubifs/sb.c
index 8c25081a5109..a32408f2f5c3 100644
--- a/fs/ubifs/sb.c
+++ b/fs/ubifs/sb.c
@@ -82,6 +82,7 @@ static int create_default_filesystem(struct ubifs_info *c)
 	int err, tmp, jnl_lebs, log_lebs, max_buds, main_lebs, main_first;
 	int lpt_lebs, lpt_first, orph_lebs, big_lpt, ino_waste, sup_flags = 0;
 	int min_leb_cnt = UBIFS_MIN_LEB_CNT;
+	int idx_node_size;
 	long long tmp64, main_bytes;
 	__le64 tmp_le64;
 	__le32 tmp_le32;
@@ -156,11 +157,19 @@ static int create_default_filesystem(struct ubifs_info *c)
 
 	main_first = c->leb_cnt - main_lebs;
 
+	sup = kzalloc(ALIGN(UBIFS_SB_NODE_SZ, c->min_io_size), GFP_KERNEL);
+	mst = kzalloc(c->mst_node_alsz, GFP_KERNEL);
+	idx_node_size = ubifs_idx_node_sz(c, 1);
+	idx = kzalloc(ALIGN(tmp, c->min_io_size), GFP_KERNEL);
+	ino = kzalloc(ALIGN(UBIFS_INO_NODE_SZ, c->min_io_size), GFP_KERNEL);
+	cs = kzalloc(ALIGN(UBIFS_CS_NODE_SZ, c->min_io_size), GFP_KERNEL);
+
+	if (!sup || !mst || !idx || !ino || !cs) {
+		err = -ENOMEM;
+		goto out;
+	}
+
 	/* Create default superblock */
-	tmp = ALIGN(UBIFS_SB_NODE_SZ, c->min_io_size);
-	sup = kzalloc(tmp, GFP_KERNEL);
-	if (!sup)
-		return -ENOMEM;
 
 	tmp64 = (long long)max_buds * c->leb_size;
 	if (big_lpt)
@@ -197,17 +206,9 @@ static int create_default_filesystem(struct ubifs_info *c)
 	sup->rp_size = cpu_to_le64(tmp64);
 	sup->ro_compat_version = cpu_to_le32(UBIFS_RO_COMPAT_VERSION);
 
-	err = ubifs_write_node(c, sup, UBIFS_SB_NODE_SZ, 0, 0);
-	kfree(sup);
-	if (err)
-		return err;
-
 	dbg_gen("default superblock created at LEB 0:0");
 
 	/* Create default master node */
-	mst = kzalloc(c->mst_node_alsz, GFP_KERNEL);
-	if (!mst)
-		return -ENOMEM;
 
 	mst->ch.node_type = UBIFS_MST_NODE;
 	mst->log_lnum     = cpu_to_le32(UBIFS_LOG_LNUM);
@@ -253,24 +254,9 @@ static int create_default_filesystem(struct ubifs_info *c)
 
 	mst->total_used = cpu_to_le64(UBIFS_INO_NODE_SZ);
 
-	err = ubifs_write_node(c, mst, UBIFS_MST_NODE_SZ, UBIFS_MST_LNUM, 0);
-	if (err) {
-		kfree(mst);
-		return err;
-	}
-	err = ubifs_write_node(c, mst, UBIFS_MST_NODE_SZ, UBIFS_MST_LNUM + 1,
-			       0);
-	kfree(mst);
-	if (err)
-		return err;
-
 	dbg_gen("default master node created at LEB %d:0", UBIFS_MST_LNUM);
 
 	/* Create the root indexing node */
-	tmp = ubifs_idx_node_sz(c, 1);
-	idx = kzalloc(ALIGN(tmp, c->min_io_size), GFP_KERNEL);
-	if (!idx)
-		return -ENOMEM;
 
 	c->key_fmt = UBIFS_SIMPLE_KEY_FMT;
 	c->key_hash = key_r5_hash;
@@ -282,19 +268,11 @@ static int create_default_filesystem(struct ubifs_info *c)
 	key_write_idx(c, &key, &br->key);
 	br->lnum = cpu_to_le32(main_first + DEFAULT_DATA_LEB);
 	br->len  = cpu_to_le32(UBIFS_INO_NODE_SZ);
-	err = ubifs_write_node(c, idx, tmp, main_first + DEFAULT_IDX_LEB, 0);
-	kfree(idx);
-	if (err)
-		return err;
 
 	dbg_gen("default root indexing node created LEB %d:0",
 		main_first + DEFAULT_IDX_LEB);
 
 	/* Create default root inode */
-	tmp = ALIGN(UBIFS_INO_NODE_SZ, c->min_io_size);
-	ino = kzalloc(tmp, GFP_KERNEL);
-	if (!ino)
-		return -ENOMEM;
 
 	ino_key_init_flash(c, &ino->key, UBIFS_ROOT_INO);
 	ino->ch.node_type = UBIFS_INO_NODE;
@@ -317,12 +295,6 @@ static int create_default_filesystem(struct ubifs_info *c)
 	/* Set compression enabled by default */
 	ino->flags = cpu_to_le32(UBIFS_COMPR_FL);
 
-	err = ubifs_write_node(c, ino, UBIFS_INO_NODE_SZ,
-			       main_first + DEFAULT_DATA_LEB, 0);
-	kfree(ino);
-	if (err)
-		return err;
-
 	dbg_gen("root inode created at LEB %d:0",
 		main_first + DEFAULT_DATA_LEB);
 
@@ -331,19 +303,46 @@ static int create_default_filesystem(struct ubifs_info *c)
 	 * always the case during normal file-system operation. Write a fake
 	 * commit start node to the log.
 	 */
-	tmp = ALIGN(UBIFS_CS_NODE_SZ, c->min_io_size);
-	cs = kzalloc(tmp, GFP_KERNEL);
-	if (!cs)
-		return -ENOMEM;
 
 	cs->ch.node_type = UBIFS_CS_NODE;
+
+	err = ubifs_write_node(c, sup, UBIFS_SB_NODE_SZ, 0, 0);
+	if (err)
+		goto out;
+
+	err = ubifs_write_node(c, mst, UBIFS_MST_NODE_SZ, UBIFS_MST_LNUM, 0);
+	if (err)
+		goto out;
+
+	err = ubifs_write_node(c, mst, UBIFS_MST_NODE_SZ, UBIFS_MST_LNUM + 1,
+			       0);
+	if (err)
+		goto out;
+
+	err = ubifs_write_node(c, idx, idx_node_size, main_first + DEFAULT_IDX_LEB, 0);
+	if (err)
+		goto out;
+
+	err = ubifs_write_node(c, ino, UBIFS_INO_NODE_SZ,
+			       main_first + DEFAULT_DATA_LEB, 0);
+	if (err)
+		goto out;
+
 	err = ubifs_write_node(c, cs, UBIFS_CS_NODE_SZ, UBIFS_LOG_LNUM, 0);
-	kfree(cs);
 	if (err)
-		return err;
+		goto out;
 
 	ubifs_msg(c, "default file-system created");
-	return 0;
+
+	err = 0;
+out:
+	kfree(sup);
+	kfree(mst);
+	kfree(idx);
+	kfree(ino);
+	kfree(cs);
+
+	return err;
 }
 
 /**
-- 
2.18.0


^ permalink raw reply related	[flat|nested] 49+ messages in thread

* [PATCH 02/25] ubifs: pass ubifs_zbranch to try_read_node()
  2018-07-04 12:41 [PATCH 00/25] UBIFS authentication support Sascha Hauer
  2018-07-04 12:41 ` [PATCH 01/25] ubifs: refactor create_default_filesystem() Sascha Hauer
@ 2018-07-04 12:41 ` Sascha Hauer
  2018-07-04 12:41 ` [PATCH 03/25] ubifs: pass ubifs_zbranch to read_znode() Sascha Hauer
                   ` (22 subsequent siblings)
  24 siblings, 0 replies; 49+ messages in thread
From: Sascha Hauer @ 2018-07-04 12:41 UTC (permalink / raw)
  To: linux-mtd
  Cc: David Gstir, Richard Weinberger, kernel, linux-kernel, Sascha Hauer

try_read_node() takes len, lnum and offs arguments which the caller all
extracts from the same struct ubifs_zbranch *. When adding authentication
support we would have to add a pointer to a hash to the arguments which
is also part of struct ubifs_zbranch. Pass the ubifs_zbranch * instead
so that we do not have to add another argument.

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
 fs/ubifs/tnc.c | 14 +++++++-------
 1 file changed, 7 insertions(+), 7 deletions(-)

diff --git a/fs/ubifs/tnc.c b/fs/ubifs/tnc.c
index 4a21e7f75e7a..2c42d1a1842a 100644
--- a/fs/ubifs/tnc.c
+++ b/fs/ubifs/tnc.c
@@ -35,7 +35,7 @@
 #include "ubifs.h"
 
 static int try_read_node(const struct ubifs_info *c, void *buf, int type,
-			 int len, int lnum, int offs);
+			 struct ubifs_zbranch *zbr);
 static int fallible_read_node(struct ubifs_info *c, const union ubifs_key *key,
 			      struct ubifs_zbranch *zbr, void *node);
 
@@ -433,9 +433,7 @@ static int tnc_read_hashed_node(struct ubifs_info *c, struct ubifs_zbranch *zbr,
  * @c: UBIFS file-system description object
  * @buf: buffer to read to
  * @type: node type
- * @len: node length (not aligned)
- * @lnum: LEB number of node to read
- * @offs: offset of node to read
+ * @zbr: the zbranch describing the node to read
  *
  * This function tries to read a node of known type and length, checks it and
  * stores it in @buf. This function returns %1 if a node is present and %0 if
@@ -453,8 +451,11 @@ static int tnc_read_hashed_node(struct ubifs_info *c, struct ubifs_zbranch *zbr,
  * journal nodes may potentially be corrupted, so checking is required.
  */
 static int try_read_node(const struct ubifs_info *c, void *buf, int type,
-			 int len, int lnum, int offs)
+			 struct ubifs_zbranch *zbr)
 {
+	int len = zbr->len;
+	int lnum = zbr->lnum;
+	int offs = zbr->offs;
 	int err, node_len;
 	struct ubifs_ch *ch = buf;
 	uint32_t crc, node_crc;
@@ -507,8 +508,7 @@ static int fallible_read_node(struct ubifs_info *c, const union ubifs_key *key,
 
 	dbg_tnck(key, "LEB %d:%d, key ", zbr->lnum, zbr->offs);
 
-	ret = try_read_node(c, node, key_type(c, key), zbr->len, zbr->lnum,
-			    zbr->offs);
+	ret = try_read_node(c, node, key_type(c, key), zbr);
 	if (ret == 1) {
 		union ubifs_key node_key;
 		struct ubifs_dent_node *dent = node;
-- 
2.18.0


^ permalink raw reply related	[flat|nested] 49+ messages in thread

* [PATCH 03/25] ubifs: pass ubifs_zbranch to read_znode()
  2018-07-04 12:41 [PATCH 00/25] UBIFS authentication support Sascha Hauer
  2018-07-04 12:41 ` [PATCH 01/25] ubifs: refactor create_default_filesystem() Sascha Hauer
  2018-07-04 12:41 ` [PATCH 02/25] ubifs: pass ubifs_zbranch to try_read_node() Sascha Hauer
@ 2018-07-04 12:41 ` Sascha Hauer
  2018-07-04 12:41 ` [PATCH 04/25] ubifs: export pnode_lookup as ubifs_pnode_lookup Sascha Hauer
                   ` (21 subsequent siblings)
  24 siblings, 0 replies; 49+ messages in thread
From: Sascha Hauer @ 2018-07-04 12:41 UTC (permalink / raw)
  To: linux-mtd
  Cc: David Gstir, Richard Weinberger, kernel, linux-kernel, Sascha Hauer

read_znode() takes len, lnum and offs arguments which the caller all
extracts from the same struct ubifs_zbranch *. When adding authentication
support we would have to add a pointer to a hash to the arguments which
is also part of struct ubifs_zbranch. Pass the ubifs_zbranch * instead
so that we do not have to add another argument.

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
 fs/ubifs/tnc_misc.c | 11 ++++++-----
 1 file changed, 6 insertions(+), 5 deletions(-)

diff --git a/fs/ubifs/tnc_misc.c b/fs/ubifs/tnc_misc.c
index 93f5b7859e6f..7e18d459906c 100644
--- a/fs/ubifs/tnc_misc.c
+++ b/fs/ubifs/tnc_misc.c
@@ -259,9 +259,7 @@ long ubifs_destroy_tnc_subtree(struct ubifs_znode *znode)
 /**
  * read_znode - read an indexing node from flash and fill znode.
  * @c: UBIFS file-system description object
- * @lnum: LEB of the indexing node to read
- * @offs: node offset
- * @len: node length
+ * @zbr: the zbranch describing the node to read
  * @znode: znode to read to
  *
  * This function reads an indexing node from the flash media and fills znode
@@ -270,9 +268,12 @@ long ubifs_destroy_tnc_subtree(struct ubifs_znode *znode)
  * is wrong with it, this function prints complaint messages and returns
  * %-EINVAL.
  */
-static int read_znode(struct ubifs_info *c, int lnum, int offs, int len,
+static int read_znode(struct ubifs_info *c, struct ubifs_zbranch *zzbr,
 		      struct ubifs_znode *znode)
 {
+	int lnum = zzbr->lnum;
+	int offs = zzbr->offs;
+	int len = zzbr->len;
 	int i, err, type, cmp;
 	struct ubifs_idx_node *idx;
 
@@ -419,7 +420,7 @@ struct ubifs_znode *ubifs_load_znode(struct ubifs_info *c,
 	if (!znode)
 		return ERR_PTR(-ENOMEM);
 
-	err = read_znode(c, zbr->lnum, zbr->offs, zbr->len, znode);
+	err = read_znode(c, zbr, znode);
 	if (err)
 		goto out;
 
-- 
2.18.0


^ permalink raw reply related	[flat|nested] 49+ messages in thread

* [PATCH 04/25] ubifs: export pnode_lookup as ubifs_pnode_lookup
  2018-07-04 12:41 [PATCH 00/25] UBIFS authentication support Sascha Hauer
                   ` (2 preceding siblings ...)
  2018-07-04 12:41 ` [PATCH 03/25] ubifs: pass ubifs_zbranch to read_znode() Sascha Hauer
@ 2018-07-04 12:41 ` Sascha Hauer
  2018-07-04 12:41 ` [PATCH 05/25] ubifs: implement ubifs_lpt_lookup using ubifs_pnode_lookup Sascha Hauer
                   ` (20 subsequent siblings)
  24 siblings, 0 replies; 49+ messages in thread
From: Sascha Hauer @ 2018-07-04 12:41 UTC (permalink / raw)
  To: linux-mtd
  Cc: David Gstir, Richard Weinberger, kernel, linux-kernel, Sascha Hauer

ubifs_lpt_lookup could be implemented using pnode_lookup. To make that
possible move pnode_lookup from lpt.c to lpt_commit.c. Rename it to
ubifs_pnode_lookup since it's now exported.

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
 fs/ubifs/lpt.c        | 32 ++++++++++++++++++++++++++++++++
 fs/ubifs/lpt_commit.c | 40 ++++------------------------------------
 fs/ubifs/ubifs.h      |  1 +
 3 files changed, 37 insertions(+), 36 deletions(-)

diff --git a/fs/ubifs/lpt.c b/fs/ubifs/lpt.c
index 8e99dad18880..6cd6f23d4512 100644
--- a/fs/ubifs/lpt.c
+++ b/fs/ubifs/lpt.c
@@ -1436,6 +1436,38 @@ struct ubifs_pnode *ubifs_get_pnode(struct ubifs_info *c,
 	return branch->pnode;
 }
 
+/**
+ * ubifs_pnode_lookup - lookup a pnode in the LPT.
+ * @c: UBIFS file-system description object
+ * @i: pnode number (0 to (main_lebs - 1) / UBIFS_LPT_FANOUT)
+ *
+ * This function returns a pointer to the pnode on success or a negative
+ * error code on failure.
+ */
+struct ubifs_pnode *ubifs_pnode_lookup(struct ubifs_info *c, int i)
+{
+	int err, h, iip, shft;
+	struct ubifs_nnode *nnode;
+
+	if (!c->nroot) {
+		err = ubifs_read_nnode(c, NULL, 0);
+		if (err)
+			return ERR_PTR(err);
+	}
+	i <<= UBIFS_LPT_FANOUT_SHIFT;
+	nnode = c->nroot;
+	shft = c->lpt_hght * UBIFS_LPT_FANOUT_SHIFT;
+	for (h = 1; h < c->lpt_hght; h++) {
+		iip = ((i >> shft) & (UBIFS_LPT_FANOUT - 1));
+		shft -= UBIFS_LPT_FANOUT_SHIFT;
+		nnode = ubifs_get_nnode(c, nnode, iip);
+		if (IS_ERR(nnode))
+			return ERR_CAST(nnode);
+	}
+	iip = ((i >> shft) & (UBIFS_LPT_FANOUT - 1));
+	return ubifs_get_pnode(c, nnode, iip);
+}
+
 /**
  * ubifs_lpt_lookup - lookup LEB properties in the LPT.
  * @c: UBIFS file-system description object
diff --git a/fs/ubifs/lpt_commit.c b/fs/ubifs/lpt_commit.c
index 78da65b2fb85..14e72d09b6b3 100644
--- a/fs/ubifs/lpt_commit.c
+++ b/fs/ubifs/lpt_commit.c
@@ -616,38 +616,6 @@ static struct ubifs_pnode *next_pnode_to_dirty(struct ubifs_info *c,
 	return ubifs_get_pnode(c, nnode, iip);
 }
 
-/**
- * pnode_lookup - lookup a pnode in the LPT.
- * @c: UBIFS file-system description object
- * @i: pnode number (0 to (main_lebs - 1) / UBIFS_LPT_FANOUT))
- *
- * This function returns a pointer to the pnode on success or a negative
- * error code on failure.
- */
-static struct ubifs_pnode *pnode_lookup(struct ubifs_info *c, int i)
-{
-	int err, h, iip, shft;
-	struct ubifs_nnode *nnode;
-
-	if (!c->nroot) {
-		err = ubifs_read_nnode(c, NULL, 0);
-		if (err)
-			return ERR_PTR(err);
-	}
-	i <<= UBIFS_LPT_FANOUT_SHIFT;
-	nnode = c->nroot;
-	shft = c->lpt_hght * UBIFS_LPT_FANOUT_SHIFT;
-	for (h = 1; h < c->lpt_hght; h++) {
-		iip = ((i >> shft) & (UBIFS_LPT_FANOUT - 1));
-		shft -= UBIFS_LPT_FANOUT_SHIFT;
-		nnode = ubifs_get_nnode(c, nnode, iip);
-		if (IS_ERR(nnode))
-			return ERR_CAST(nnode);
-	}
-	iip = ((i >> shft) & (UBIFS_LPT_FANOUT - 1));
-	return ubifs_get_pnode(c, nnode, iip);
-}
-
 /**
  * add_pnode_dirt - add dirty space to LPT LEB properties.
  * @c: UBIFS file-system description object
@@ -700,7 +668,7 @@ static int make_tree_dirty(struct ubifs_info *c)
 {
 	struct ubifs_pnode *pnode;
 
-	pnode = pnode_lookup(c, 0);
+	pnode = ubifs_pnode_lookup(c, 0);
 	if (IS_ERR(pnode))
 		return PTR_ERR(pnode);
 
@@ -954,7 +922,7 @@ static int make_pnode_dirty(struct ubifs_info *c, int node_num, int lnum,
 	struct ubifs_pnode *pnode;
 	struct ubifs_nbranch *branch;
 
-	pnode = pnode_lookup(c, node_num);
+	pnode = ubifs_pnode_lookup(c, node_num);
 	if (IS_ERR(pnode))
 		return PTR_ERR(pnode);
 	branch = &pnode->parent->nbranch[pnode->iip];
@@ -1556,7 +1524,7 @@ static int dbg_is_pnode_dirty(struct ubifs_info *c, int lnum, int offs)
 		struct ubifs_nbranch *branch;
 
 		cond_resched();
-		pnode = pnode_lookup(c, i);
+		pnode = ubifs_pnode_lookup(c, i);
 		if (IS_ERR(pnode))
 			return PTR_ERR(pnode);
 		branch = &pnode->parent->nbranch[pnode->iip];
@@ -1708,7 +1676,7 @@ int dbg_check_ltab(struct ubifs_info *c)
 	for (i = 0; i < cnt; i++) {
 		struct ubifs_pnode *pnode;
 
-		pnode = pnode_lookup(c, i);
+		pnode = ubifs_pnode_lookup(c, i);
 		if (IS_ERR(pnode))
 			return PTR_ERR(pnode);
 		cond_resched();
diff --git a/fs/ubifs/ubifs.h b/fs/ubifs/ubifs.h
index 04bf84d71e7b..83c5e75ed3b6 100644
--- a/fs/ubifs/ubifs.h
+++ b/fs/ubifs/ubifs.h
@@ -1695,6 +1695,7 @@ struct ubifs_pnode *ubifs_get_pnode(struct ubifs_info *c,
 				    struct ubifs_nnode *parent, int iip);
 struct ubifs_nnode *ubifs_get_nnode(struct ubifs_info *c,
 				    struct ubifs_nnode *parent, int iip);
+struct ubifs_pnode *ubifs_pnode_lookup(struct ubifs_info *c, int i);
 int ubifs_read_nnode(struct ubifs_info *c, struct ubifs_nnode *parent, int iip);
 void ubifs_add_lpt_dirt(struct ubifs_info *c, int lnum, int dirty);
 void ubifs_add_nnode_dirt(struct ubifs_info *c, struct ubifs_nnode *nnode);
-- 
2.18.0


^ permalink raw reply related	[flat|nested] 49+ messages in thread

* [PATCH 05/25] ubifs: implement ubifs_lpt_lookup using ubifs_pnode_lookup
  2018-07-04 12:41 [PATCH 00/25] UBIFS authentication support Sascha Hauer
                   ` (3 preceding siblings ...)
  2018-07-04 12:41 ` [PATCH 04/25] ubifs: export pnode_lookup as ubifs_pnode_lookup Sascha Hauer
@ 2018-07-04 12:41 ` Sascha Hauer
  2018-08-13  6:31   ` Sascha Hauer
  2018-07-04 12:41 ` [PATCH 06/25] ubifs: drop write_node Sascha Hauer
                   ` (19 subsequent siblings)
  24 siblings, 1 reply; 49+ messages in thread
From: Sascha Hauer @ 2018-07-04 12:41 UTC (permalink / raw)
  To: linux-mtd
  Cc: David Gstir, Richard Weinberger, kernel, linux-kernel, Sascha Hauer

ubifs_lpt_lookup() starts by looking up the nth pnode in the LPT. We
already have this functionality in ubifs_pnode_lookup(). Use this
function rather than open coding its functionality.

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
 fs/ubifs/lpt.c | 20 ++------------------
 1 file changed, 2 insertions(+), 18 deletions(-)

diff --git a/fs/ubifs/lpt.c b/fs/ubifs/lpt.c
index 6cd6f23d4512..cde7b9484157 100644
--- a/fs/ubifs/lpt.c
+++ b/fs/ubifs/lpt.c
@@ -1478,27 +1478,11 @@ struct ubifs_pnode *ubifs_pnode_lookup(struct ubifs_info *c, int i)
  */
 struct ubifs_lprops *ubifs_lpt_lookup(struct ubifs_info *c, int lnum)
 {
-	int err, i, h, iip, shft;
-	struct ubifs_nnode *nnode;
+	int i, iip;
 	struct ubifs_pnode *pnode;
 
-	if (!c->nroot) {
-		err = ubifs_read_nnode(c, NULL, 0);
-		if (err)
-			return ERR_PTR(err);
-	}
-	nnode = c->nroot;
 	i = lnum - c->main_first;
-	shft = c->lpt_hght * UBIFS_LPT_FANOUT_SHIFT;
-	for (h = 1; h < c->lpt_hght; h++) {
-		iip = ((i >> shft) & (UBIFS_LPT_FANOUT - 1));
-		shft -= UBIFS_LPT_FANOUT_SHIFT;
-		nnode = ubifs_get_nnode(c, nnode, iip);
-		if (IS_ERR(nnode))
-			return ERR_CAST(nnode);
-	}
-	iip = ((i >> shft) & (UBIFS_LPT_FANOUT - 1));
-	pnode = ubifs_get_pnode(c, nnode, iip);
+	pnode = ubifs_pnode_lookup(c, i);
 	if (IS_ERR(pnode))
 		return ERR_CAST(pnode);
 	iip = (i & (UBIFS_LPT_FANOUT - 1));
-- 
2.18.0


^ permalink raw reply related	[flat|nested] 49+ messages in thread

* [PATCH 06/25] ubifs: drop write_node
  2018-07-04 12:41 [PATCH 00/25] UBIFS authentication support Sascha Hauer
                   ` (4 preceding siblings ...)
  2018-07-04 12:41 ` [PATCH 05/25] ubifs: implement ubifs_lpt_lookup using ubifs_pnode_lookup Sascha Hauer
@ 2018-07-04 12:41 ` Sascha Hauer
  2018-07-04 12:41 ` [PATCH 07/25] ubifs: Store read superblock node Sascha Hauer
                   ` (18 subsequent siblings)
  24 siblings, 0 replies; 49+ messages in thread
From: Sascha Hauer @ 2018-07-04 12:41 UTC (permalink / raw)
  To: linux-mtd
  Cc: David Gstir, Richard Weinberger, kernel, linux-kernel, Sascha Hauer

write_node() is used only once and can easily be replaced with calls
to ubifs_prepare_node()/write_head() which makes the code a bit shorter.

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
 fs/ubifs/journal.c | 39 +++++----------------------------------
 1 file changed, 5 insertions(+), 34 deletions(-)

diff --git a/fs/ubifs/journal.c b/fs/ubifs/journal.c
index 07b4956e0425..754d969eb27e 100644
--- a/fs/ubifs/journal.c
+++ b/fs/ubifs/journal.c
@@ -228,36 +228,6 @@ static int reserve_space(struct ubifs_info *c, int jhead, int len)
 	return err;
 }
 
-/**
- * write_node - write node to a journal head.
- * @c: UBIFS file-system description object
- * @jhead: journal head
- * @node: node to write
- * @len: node length
- * @lnum: LEB number written is returned here
- * @offs: offset written is returned here
- *
- * This function writes a node to reserved space of journal head @jhead.
- * Returns zero in case of success and a negative error code in case of
- * failure.
- */
-static int write_node(struct ubifs_info *c, int jhead, void *node, int len,
-		      int *lnum, int *offs)
-{
-	struct ubifs_wbuf *wbuf = &c->jheads[jhead].wbuf;
-
-	ubifs_assert(jhead != GCHD);
-
-	*lnum = c->jheads[jhead].wbuf.lnum;
-	*offs = c->jheads[jhead].wbuf.offs + c->jheads[jhead].wbuf.used;
-
-	dbg_jnl("jhead %s, LEB %d:%d, len %d",
-		dbg_jhead(jhead), *lnum, *offs, len);
-	ubifs_prepare_node(c, node, len, 0);
-
-	return ubifs_wbuf_write_nolock(wbuf, node, len);
-}
-
 /**
  * write_head - write data to a journal head.
  * @c: UBIFS file-system description object
@@ -268,9 +238,9 @@ static int write_node(struct ubifs_info *c, int jhead, void *node, int len,
  * @offs: offset written is returned here
  * @sync: non-zero if the write-buffer has to by synchronized
  *
- * This function is the same as 'write_node()' but it does not assume the
- * buffer it is writing is a node, so it does not prepare it (which means
- * initializing common header and calculating CRC).
+ * This function writes data to the reserved space of journal head @jhead.
+ * Returns zero in case of success and a negative error code in case of
+ * failure.
  */
 static int write_head(struct ubifs_info *c, int jhead, void *buf, int len,
 		      int *lnum, int *offs, int sync)
@@ -758,7 +728,8 @@ int ubifs_jnl_write_data(struct ubifs_info *c, const struct inode *inode,
 	if (err)
 		goto out_free;
 
-	err = write_node(c, DATAHD, data, dlen, &lnum, &offs);
+	ubifs_prepare_node(c, data, dlen, 0);
+	err = write_head(c, DATAHD, data, dlen, &lnum, &offs, 0);
 	if (err)
 		goto out_release;
 	ubifs_wbuf_add_ino_nolock(&c->jheads[DATAHD].wbuf, key_inum(c, key));
-- 
2.18.0


^ permalink raw reply related	[flat|nested] 49+ messages in thread

* [PATCH 07/25] ubifs: Store read superblock node
  2018-07-04 12:41 [PATCH 00/25] UBIFS authentication support Sascha Hauer
                   ` (5 preceding siblings ...)
  2018-07-04 12:41 ` [PATCH 06/25] ubifs: drop write_node Sascha Hauer
@ 2018-07-04 12:41 ` Sascha Hauer
  2018-08-27 12:50   ` Richard Weinberger
  2018-07-04 12:41 ` [PATCH 08/25] ubifs: Format changes for authentication support Sascha Hauer
                   ` (17 subsequent siblings)
  24 siblings, 1 reply; 49+ messages in thread
From: Sascha Hauer @ 2018-07-04 12:41 UTC (permalink / raw)
  To: linux-mtd
  Cc: David Gstir, Richard Weinberger, kernel, linux-kernel, Sascha Hauer

The superblock node is read/modified/written several times throughout
the UBIFS code. Instead of reading it from the device each time just
keep a copy in memory and write back the modified copy when necessary.
This patch helps for authentication support, here we not only have to
read the superblock node, but also have to authenticate it, which
is easier if we do it once during initialization.

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
 fs/ubifs/sb.c    | 19 +++++--------------
 fs/ubifs/super.c |  8 +-------
 fs/ubifs/ubifs.h |  3 ++-
 3 files changed, 8 insertions(+), 22 deletions(-)

diff --git a/fs/ubifs/sb.c b/fs/ubifs/sb.c
index a32408f2f5c3..2fabd441d74a 100644
--- a/fs/ubifs/sb.c
+++ b/fs/ubifs/sb.c
@@ -497,7 +497,7 @@ static int validate_sb(struct ubifs_info *c, struct ubifs_sb_node *sup)
  * code. Note, the user of this function is responsible of kfree()'ing the
  * returned superblock buffer.
  */
-struct ubifs_sb_node *ubifs_read_sb_node(struct ubifs_info *c)
+static struct ubifs_sb_node *ubifs_read_sb_node(struct ubifs_info *c)
 {
 	struct ubifs_sb_node *sup;
 	int err;
@@ -554,6 +554,8 @@ int ubifs_read_superblock(struct ubifs_info *c)
 	if (IS_ERR(sup))
 		return PTR_ERR(sup);
 
+	c->superblock = sup;
+
 	c->fmt_version = le32_to_cpu(sup->fmt_version);
 	c->ro_compat_version = le32_to_cpu(sup->ro_compat_version);
 
@@ -685,7 +687,6 @@ int ubifs_read_superblock(struct ubifs_info *c)
 
 	err = validate_sb(c, sup);
 out:
-	kfree(sup);
 	return err;
 }
 
@@ -814,7 +815,7 @@ static int fixup_free_space(struct ubifs_info *c)
 int ubifs_fixup_free_space(struct ubifs_info *c)
 {
 	int err;
-	struct ubifs_sb_node *sup;
+	struct ubifs_sb_node *sup = c->superblock;
 
 	ubifs_assert(c->space_fixup);
 	ubifs_assert(!c->ro_mount);
@@ -825,16 +826,11 @@ int ubifs_fixup_free_space(struct ubifs_info *c)
 	if (err)
 		return err;
 
-	sup = ubifs_read_sb_node(c);
-	if (IS_ERR(sup))
-		return PTR_ERR(sup);
-
 	/* Free-space fixup is no longer required */
 	c->space_fixup = 0;
 	sup->flags &= cpu_to_le32(~UBIFS_FLG_SPACE_FIXUP);
 
 	err = ubifs_write_sb_node(c, sup);
-	kfree(sup);
 	if (err)
 		return err;
 
@@ -845,7 +841,7 @@ int ubifs_fixup_free_space(struct ubifs_info *c)
 int ubifs_enable_encryption(struct ubifs_info *c)
 {
 	int err;
-	struct ubifs_sb_node *sup;
+	struct ubifs_sb_node *sup = c->superblock;
 
 	if (c->encrypted)
 		return 0;
@@ -858,16 +854,11 @@ int ubifs_enable_encryption(struct ubifs_info *c)
 		return -EINVAL;
 	}
 
-	sup = ubifs_read_sb_node(c);
-	if (IS_ERR(sup))
-		return PTR_ERR(sup);
-
 	sup->flags |= cpu_to_le32(UBIFS_FLG_ENCRYPTION);
 
 	err = ubifs_write_sb_node(c, sup);
 	if (!err)
 		c->encrypted = 1;
-	kfree(sup);
 
 	return err;
 }
diff --git a/fs/ubifs/super.c b/fs/ubifs/super.c
index c5466c70d620..9e44baec30a3 100644
--- a/fs/ubifs/super.c
+++ b/fs/ubifs/super.c
@@ -1580,16 +1580,10 @@ static int ubifs_remount_rw(struct ubifs_info *c)
 		goto out;
 
 	if (c->old_leb_cnt != c->leb_cnt) {
-		struct ubifs_sb_node *sup;
+		struct ubifs_sb_node *sup = c->superblock;
 
-		sup = ubifs_read_sb_node(c);
-		if (IS_ERR(sup)) {
-			err = PTR_ERR(sup);
-			goto out;
-		}
 		sup->leb_cnt = cpu_to_le32(c->leb_cnt);
 		err = ubifs_write_sb_node(c, sup);
-		kfree(sup);
 		if (err)
 			goto out;
 	}
diff --git a/fs/ubifs/ubifs.h b/fs/ubifs/ubifs.h
index 83c5e75ed3b6..92574045e030 100644
--- a/fs/ubifs/ubifs.h
+++ b/fs/ubifs/ubifs.h
@@ -971,6 +971,7 @@ struct ubifs_debug_info;
  * struct ubifs_info - UBIFS file-system description data structure
  * (per-superblock).
  * @vfs_sb: VFS @struct super_block object
+ * @superblock: The super block node as read from the device
  *
  * @highest_inum: highest used inode number
  * @max_sqnum: current global sequence number
@@ -1217,6 +1218,7 @@ struct ubifs_debug_info;
  */
 struct ubifs_info {
 	struct super_block *vfs_sb;
+	struct ubifs_sb_node *superblock;
 
 	ino_t highest_inum;
 	unsigned long long max_sqnum;
@@ -1647,7 +1649,6 @@ int ubifs_write_master(struct ubifs_info *c);
 
 /* sb.c */
 int ubifs_read_superblock(struct ubifs_info *c);
-struct ubifs_sb_node *ubifs_read_sb_node(struct ubifs_info *c);
 int ubifs_write_sb_node(struct ubifs_info *c, struct ubifs_sb_node *sup);
 int ubifs_fixup_free_space(struct ubifs_info *c);
 int ubifs_enable_encryption(struct ubifs_info *c);
-- 
2.18.0


^ permalink raw reply related	[flat|nested] 49+ messages in thread

* [PATCH 08/25] ubifs: Format changes for authentication support
  2018-07-04 12:41 [PATCH 00/25] UBIFS authentication support Sascha Hauer
                   ` (6 preceding siblings ...)
  2018-07-04 12:41 ` [PATCH 07/25] ubifs: Store read superblock node Sascha Hauer
@ 2018-07-04 12:41 ` Sascha Hauer
  2018-07-04 12:41 ` [PATCH 09/25] ubifs: add separate functions to init/crc a node Sascha Hauer
                   ` (16 subsequent siblings)
  24 siblings, 0 replies; 49+ messages in thread
From: Sascha Hauer @ 2018-07-04 12:41 UTC (permalink / raw)
  To: linux-mtd
  Cc: David Gstir, Richard Weinberger, kernel, linux-kernel, Sascha Hauer

This patch adds the changes to the on disk format needed for
authentication support. We'll add:

* a HMAC covering super block node
* a HMAC covering the master node
* a hash over the root index node to the master node
* a hash over the LPT to the master node
* a flag to the filesystem flag indicating the filesystem is
  authenticated
* an authentication node necessary to authenticate the nodes written
  to the journal heads while they are written.
* a HMAC of a well known message to the super block node to be able
  to check if the correct key is provided

And finally, not visible in this patch, nevertheless explained here:

* hashes over the referenced child nodes in each branch of a index node

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
 fs/ubifs/debug.c       |  6 ++++++
 fs/ubifs/super.c       |  1 +
 fs/ubifs/ubifs-media.h | 46 +++++++++++++++++++++++++++++++++++++++---
 3 files changed, 50 insertions(+), 3 deletions(-)

diff --git a/fs/ubifs/debug.c b/fs/ubifs/debug.c
index 7cd8a7b95299..249c9e71252c 100644
--- a/fs/ubifs/debug.c
+++ b/fs/ubifs/debug.c
@@ -165,6 +165,8 @@ const char *dbg_ntype(int type)
 		return "commit start node";
 	case UBIFS_ORPH_NODE:
 		return "orphan node";
+	case UBIFS_AUTH_NODE:
+		return "auth node";
 	default:
 		return "unknown node";
 	}
@@ -542,6 +544,10 @@ void ubifs_dump_node(const struct ubifs_info *c, const void *node)
 			       (unsigned long long)le64_to_cpu(orph->inos[i]));
 		break;
 	}
+	case UBIFS_AUTH_NODE:
+	{
+		break;
+	}
 	default:
 		pr_err("node type %d was not recognized\n",
 		       (int)ch->node_type);
diff --git a/fs/ubifs/super.c b/fs/ubifs/super.c
index 9e44baec30a3..6b2d80391111 100644
--- a/fs/ubifs/super.c
+++ b/fs/ubifs/super.c
@@ -577,6 +577,7 @@ static int init_constants_early(struct ubifs_info *c)
 	c->ranges[UBIFS_REF_NODE].len  = UBIFS_REF_NODE_SZ;
 	c->ranges[UBIFS_TRUN_NODE].len = UBIFS_TRUN_NODE_SZ;
 	c->ranges[UBIFS_CS_NODE].len   = UBIFS_CS_NODE_SZ;
+	c->ranges[UBIFS_AUTH_NODE].len = UBIFS_AUTH_NODE_SZ;
 
 	c->ranges[UBIFS_INO_NODE].min_len  = UBIFS_INO_NODE_SZ;
 	c->ranges[UBIFS_INO_NODE].max_len  = UBIFS_MAX_INO_NODE_SZ;
diff --git a/fs/ubifs/ubifs-media.h b/fs/ubifs/ubifs-media.h
index e8c23c9d4f4a..8b7c1844014f 100644
--- a/fs/ubifs/ubifs-media.h
+++ b/fs/ubifs/ubifs-media.h
@@ -286,6 +286,7 @@ enum {
 #define UBIFS_IDX_NODE_SZ  sizeof(struct ubifs_idx_node)
 #define UBIFS_CS_NODE_SZ   sizeof(struct ubifs_cs_node)
 #define UBIFS_ORPH_NODE_SZ sizeof(struct ubifs_orph_node)
+#define UBIFS_AUTH_NODE_SZ sizeof(struct ubifs_auth_node)
 /* Extended attribute entry nodes are identical to directory entry nodes */
 #define UBIFS_XENT_NODE_SZ UBIFS_DENT_NODE_SZ
 /* Only this does not have to be multiple of 8 bytes */
@@ -300,6 +301,12 @@ enum {
 /* The largest UBIFS node */
 #define UBIFS_MAX_NODE_SZ UBIFS_MAX_INO_NODE_SZ
 
+/* The maxmimum size of a hash, enough for sha512 */
+#define UBIFS_MAX_HASH_LEN 64
+
+/* The maxmimum size of a hmac, enough for hmac(sha512) */
+#define UBIFS_MAX_HMAC_LEN 64
+
 /*
  * xattr name of UBIFS encryption context, we don't use a prefix
  * nor a long name to not waste space on the flash.
@@ -365,6 +372,7 @@ enum {
  * UBIFS_IDX_NODE: index node
  * UBIFS_CS_NODE: commit start node
  * UBIFS_ORPH_NODE: orphan node
+ * UBIFS_AUTH_NODE: authentication node
  * UBIFS_NODE_TYPES_CNT: count of supported node types
  *
  * Note, we index arrays by these numbers, so keep them low and contiguous.
@@ -384,6 +392,7 @@ enum {
 	UBIFS_IDX_NODE,
 	UBIFS_CS_NODE,
 	UBIFS_ORPH_NODE,
+	UBIFS_AUTH_NODE,
 	UBIFS_NODE_TYPES_CNT,
 };
 
@@ -421,15 +430,19 @@ enum {
  * UBIFS_FLG_DOUBLE_HASH: store a 32bit cookie in directory entry nodes to
  *			  support 64bit cookies for lookups by hash
  * UBIFS_FLG_ENCRYPTION: this filesystem contains encrypted files
+ * UBIFS_FLG_AUTHENTICATION: this filesystem contains hashes for authentication
  */
 enum {
 	UBIFS_FLG_BIGLPT = 0x02,
 	UBIFS_FLG_SPACE_FIXUP = 0x04,
 	UBIFS_FLG_DOUBLE_HASH = 0x08,
 	UBIFS_FLG_ENCRYPTION = 0x10,
+	UBIFS_FLG_AUTHENTICATION = 0x20,
 };
 
-#define UBIFS_FLG_MASK (UBIFS_FLG_BIGLPT|UBIFS_FLG_SPACE_FIXUP|UBIFS_FLG_DOUBLE_HASH|UBIFS_FLG_ENCRYPTION)
+#define UBIFS_FLG_MASK (UBIFS_FLG_BIGLPT | UBIFS_FLG_SPACE_FIXUP | \
+		UBIFS_FLG_DOUBLE_HASH | UBIFS_FLG_ENCRYPTION | \
+		UBIFS_FLG_AUTHENTICATION)
 
 /**
  * struct ubifs_ch - common header node.
@@ -633,6 +646,10 @@ struct ubifs_pad_node {
  * @time_gran: time granularity in nanoseconds
  * @uuid: UUID generated when the file system image was created
  * @ro_compat_version: UBIFS R/O compatibility version
+ * @hmac: HMAC to authenticate the superblock node
+ * @hmac_wkm: HMAC of a well known message (the string "UBIFS") as a convenience
+ *            to the user to check if the correct key is passed.
+ * @hash_algo: The hash algo used for this filesystem (one of enum hash_algo)
  */
 struct ubifs_sb_node {
 	struct ubifs_ch ch;
@@ -660,7 +677,10 @@ struct ubifs_sb_node {
 	__le32 time_gran;
 	__u8 uuid[16];
 	__le32 ro_compat_version;
-	__u8 padding2[3968];
+	__u8 hmac[UBIFS_MAX_HMAC_LEN];
+	__u8 hmac_wkm[UBIFS_MAX_HMAC_LEN];
+	__le16 hash_algo;
+	__u8 padding2[3838];
 } __packed;
 
 /**
@@ -695,6 +715,9 @@ struct ubifs_sb_node {
  * @empty_lebs: number of empty logical eraseblocks
  * @idx_lebs: number of indexing logical eraseblocks
  * @leb_cnt: count of LEBs used by file-system
+ * @hash_root_idx: the hash of the root index node
+ * @hash_lpt: the hash of the LPT
+ * @hmac: HMAC to authenticate the master node
  * @padding: reserved for future, zeroes
  */
 struct ubifs_mst_node {
@@ -727,7 +750,10 @@ struct ubifs_mst_node {
 	__le32 empty_lebs;
 	__le32 idx_lebs;
 	__le32 leb_cnt;
-	__u8 padding[344];
+	__u8 hash_root_idx[UBIFS_MAX_HASH_LEN];
+	__u8 hash_lpt[UBIFS_MAX_HASH_LEN];
+	__u8 hmac[UBIFS_MAX_HMAC_LEN];
+	__u8 padding[152];
 } __packed;
 
 /**
@@ -746,12 +772,26 @@ struct ubifs_ref_node {
 	__u8 padding[28];
 } __packed;
 
+/**
+ * struct ubifs_auth_node - node for authenticating other nodes
+ * @ch: common header
+ * @hmac: The HMAC
+ */
+struct ubifs_auth_node {
+	struct ubifs_ch ch;
+	__u8 hmac[];
+} __packed;
+
 /**
  * struct ubifs_branch - key/reference/length branch
  * @lnum: LEB number of the target node
  * @offs: offset within @lnum
  * @len: target node length
  * @key: key
+ *
+ * In an authenticated UBIFS we have the hash of the referenced node after @key.
+ * This can't be added to the struct type definition because @key is a
+ * dynamically sized element already.
  */
 struct ubifs_branch {
 	__le32 lnum;
-- 
2.18.0


^ permalink raw reply related	[flat|nested] 49+ messages in thread

* [PATCH 09/25] ubifs: add separate functions to init/crc a node
  2018-07-04 12:41 [PATCH 00/25] UBIFS authentication support Sascha Hauer
                   ` (7 preceding siblings ...)
  2018-07-04 12:41 ` [PATCH 08/25] ubifs: Format changes for authentication support Sascha Hauer
@ 2018-07-04 12:41 ` Sascha Hauer
  2018-07-04 12:41 ` [PATCH 10/25] ubifs: add helper functions for authentication support Sascha Hauer
                   ` (15 subsequent siblings)
  24 siblings, 0 replies; 49+ messages in thread
From: Sascha Hauer @ 2018-07-04 12:41 UTC (permalink / raw)
  To: linux-mtd
  Cc: David Gstir, Richard Weinberger, kernel, linux-kernel, Sascha Hauer

When adding authentication support we will embed a HMAC into some
nodes. To prepare these nodes we have to first initialize the nodes,
then add a HMAC and finally add a CRC. To accomplish this add separate
ubifs_init_node/ubifs_crc_node functions.

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
 fs/ubifs/io.c    | 42 +++++++++++++++++++++++++++---------------
 fs/ubifs/ubifs.h |  2 ++
 2 files changed, 29 insertions(+), 15 deletions(-)

diff --git a/fs/ubifs/io.c b/fs/ubifs/io.c
index fe77e9625e84..eeb8fb073d33 100644
--- a/fs/ubifs/io.c
+++ b/fs/ubifs/io.c
@@ -365,20 +365,8 @@ static unsigned long long next_sqnum(struct ubifs_info *c)
 	return sqnum;
 }
 
-/**
- * ubifs_prepare_node - prepare node to be written to flash.
- * @c: UBIFS file-system description object
- * @node: the node to pad
- * @len: node length
- * @pad: if the buffer has to be padded
- *
- * This function prepares node at @node to be written to the media - it
- * calculates node CRC, fills the common header, and adds proper padding up to
- * the next minimum I/O unit if @pad is not zero.
- */
-void ubifs_prepare_node(struct ubifs_info *c, void *node, int len, int pad)
+void ubifs_init_node(struct ubifs_info *c, void *node, int len, int pad)
 {
-	uint32_t crc;
 	struct ubifs_ch *ch = node;
 	unsigned long long sqnum = next_sqnum(c);
 
@@ -389,8 +377,6 @@ void ubifs_prepare_node(struct ubifs_info *c, void *node, int len, int pad)
 	ch->group_type = UBIFS_NO_NODE_GROUP;
 	ch->sqnum = cpu_to_le64(sqnum);
 	ch->padding[0] = ch->padding[1] = 0;
-	crc = crc32(UBIFS_CRC32_INIT, node + 8, len - 8);
-	ch->crc = cpu_to_le32(crc);
 
 	if (pad) {
 		len = ALIGN(len, 8);
@@ -399,6 +385,32 @@ void ubifs_prepare_node(struct ubifs_info *c, void *node, int len, int pad)
 	}
 }
 
+void ubifs_crc_node(struct ubifs_info *c, void *node, int len)
+{
+	struct ubifs_ch *ch = node;
+	uint32_t crc;
+
+	crc = crc32(UBIFS_CRC32_INIT, node + 8, len - 8);
+	ch->crc = cpu_to_le32(crc);
+}
+
+/**
+ * ubifs_prepare_node - prepare node to be written to flash.
+ * @c: UBIFS file-system description object
+ * @node: the node to pad
+ * @len: node length
+ * @pad: if the buffer has to be padded
+ *
+ * This function prepares node at @node to be written to the media - it
+ * calculates node CRC, fills the common header, and adds proper padding up to
+ * the next minimum I/O unit if @pad is not zero.
+ */
+void ubifs_prepare_node(struct ubifs_info *c, void *node, int len, int pad)
+{
+	ubifs_init_node(c, node, len, pad);
+	ubifs_crc_node(c, node, len);
+}
+
 /**
  * ubifs_prep_grp_node - prepare node of a group to be written to flash.
  * @c: UBIFS file-system description object
diff --git a/fs/ubifs/ubifs.h b/fs/ubifs/ubifs.h
index 92574045e030..485de4e27f96 100644
--- a/fs/ubifs/ubifs.h
+++ b/fs/ubifs/ubifs.h
@@ -1480,6 +1480,8 @@ int ubifs_write_node(struct ubifs_info *c, void *node, int len, int lnum,
 		     int offs);
 int ubifs_check_node(const struct ubifs_info *c, const void *buf, int lnum,
 		     int offs, int quiet, int must_chk_crc);
+void ubifs_init_node(struct ubifs_info *c, void *buf, int len, int pad);
+void ubifs_crc_node(struct ubifs_info *c, void *buf, int len);
 void ubifs_prepare_node(struct ubifs_info *c, void *buf, int len, int pad);
 void ubifs_prep_grp_node(struct ubifs_info *c, void *node, int len, int last);
 int ubifs_io_init(struct ubifs_info *c);
-- 
2.18.0


^ permalink raw reply related	[flat|nested] 49+ messages in thread

* [PATCH 10/25] ubifs: add helper functions for authentication support
  2018-07-04 12:41 [PATCH 00/25] UBIFS authentication support Sascha Hauer
                   ` (8 preceding siblings ...)
  2018-07-04 12:41 ` [PATCH 09/25] ubifs: add separate functions to init/crc a node Sascha Hauer
@ 2018-07-04 12:41 ` Sascha Hauer
  2018-08-27 12:50   ` Richard Weinberger
  2018-07-04 12:41 ` [PATCH 11/25] ubifs: Create functions to embed a HMAC in a node Sascha Hauer
                   ` (14 subsequent siblings)
  24 siblings, 1 reply; 49+ messages in thread
From: Sascha Hauer @ 2018-07-04 12:41 UTC (permalink / raw)
  To: linux-mtd
  Cc: David Gstir, Richard Weinberger, kernel, linux-kernel, Sascha Hauer

This patch adds the various helper functions needed for authentication
support. We need functions to hash nodes, to embed HMACs into a node and
to compare hashes and HMACs. Most functions first check if this
filesystem is authenticated and bail out early if not, which makes the
functions safe to be called with disabled authentication.

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
 fs/ubifs/Kconfig  |   1 +
 fs/ubifs/Makefile |   1 +
 fs/ubifs/auth.c   | 413 ++++++++++++++++++++++++++++++++++++++++++++++
 fs/ubifs/ubifs.h  | 187 +++++++++++++++++++++
 4 files changed, 602 insertions(+)
 create mode 100644 fs/ubifs/auth.c

diff --git a/fs/ubifs/Kconfig b/fs/ubifs/Kconfig
index 83a961bf7280..2a0b5f1134a4 100644
--- a/fs/ubifs/Kconfig
+++ b/fs/ubifs/Kconfig
@@ -7,6 +7,7 @@ config UBIFS_FS
 	select CRYPTO if UBIFS_FS_ZLIB
 	select CRYPTO_LZO if UBIFS_FS_LZO
 	select CRYPTO_DEFLATE if UBIFS_FS_ZLIB
+	select CRYPTO_HASH_INFO
 	depends on MTD_UBI
 	help
 	  UBIFS is a file system for flash devices which works on top of UBI.
diff --git a/fs/ubifs/Makefile b/fs/ubifs/Makefile
index 9758f709c736..2bdb8ae2f435 100644
--- a/fs/ubifs/Makefile
+++ b/fs/ubifs/Makefile
@@ -7,3 +7,4 @@ ubifs-y += budget.o find.o tnc_commit.o compress.o lpt.o lprops.o
 ubifs-y += recovery.o ioctl.o lpt_commit.o tnc_misc.o xattr.o debug.o
 ubifs-y += misc.o
 ubifs-$(CONFIG_UBIFS_FS_ENCRYPTION) += crypto.o
+ubifs-$(CONFIG_UBIFS_FS_AUTHENTICATION) += auth.o
diff --git a/fs/ubifs/auth.c b/fs/ubifs/auth.c
new file mode 100644
index 000000000000..fd21f2ec8734
--- /dev/null
+++ b/fs/ubifs/auth.c
@@ -0,0 +1,413 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * This file is part of UBIFS.
+ *
+ * Copyright (C) 2018 Pengutronix, Sascha Hauer <s.hauer@pengutronix.de>
+ */
+
+/*
+ * This file implements various helper functions for UBIFS authentication support
+ */
+
+#include <linux/crypto.h>
+#include <crypto/hash.h>
+#include <crypto/sha.h>
+#include <crypto/algapi.h>
+#include <keys/user-type.h>
+
+#include "ubifs.h"
+
+/**
+ * ubifs_node_calc_hash - calculate the hash of a UBIFS node
+ * @c: UBIFS file-system description object
+ * @node: the node to calculate a hash for
+ * @hash: the returned hash
+ */
+void __ubifs_node_calc_hash(const struct ubifs_info *c, const void *node,
+			    u8 *hash)
+{
+	const struct ubifs_ch *ch = node;
+	SHASH_DESC_ON_STACK(shash, c->hash_tfm);
+
+	shash->tfm = c->hash_tfm;
+	shash->flags = CRYPTO_TFM_REQ_MAY_SLEEP;
+
+	crypto_shash_digest(shash, node, le32_to_cpu(ch->len), hash);
+}
+
+/**
+ * ubifs_hash_calc_hmac - calculate a HMAC from a hash
+ * @c: UBIFS file-system description object
+ * @hash: the node to calculate a HMAC for
+ * @hmac: the returned HMAC
+ */
+static void ubifs_hash_calc_hmac(const struct ubifs_info *c, const u8 *hash,
+				 u8 *hmac)
+{
+	SHASH_DESC_ON_STACK(shash, c->hmac_tfm);
+
+	shash->tfm = c->hmac_tfm;
+	shash->flags = CRYPTO_TFM_REQ_MAY_SLEEP;
+
+	crypto_shash_digest(shash, hash, c->hash_len, hmac);
+}
+
+/**
+ * ubifs_prepare_auth_node - Prepare an authentication node
+ * @c: UBIFS file-system description object
+ * @node: the node to calculate a hash for
+ * @hash: input hash of previous nodes
+ *
+ * This function prepares an authentication node for writing onto flash.
+ * It creates a HMAC from the given input hash and writes it to the node.
+ */
+void ubifs_prepare_auth_node(struct ubifs_info *c, void *node,
+			     struct shash_desc *inhash)
+{
+	SHASH_DESC_ON_STACK(hash_desc, c->hash_tfm);
+	struct ubifs_auth_node *auth = node;
+	u8 hash[crypto_shash_descsize(c->hash_tfm)];
+
+	hash_desc->tfm = c->hash_tfm;
+	hash_desc->flags = CRYPTO_TFM_REQ_MAY_SLEEP;
+	ubifs_shash_copy_state(c, inhash, hash_desc);
+	crypto_shash_final(hash_desc, hash);
+
+	ubifs_hash_calc_hmac(c, hash, auth->hmac);
+
+	auth->ch.node_type = UBIFS_AUTH_NODE;
+	ubifs_prepare_node(c, auth, ubifs_auth_node_sz(c), 0);
+}
+
+static struct shash_desc *ubifs_get_desc(const struct ubifs_info *c,
+					 struct crypto_shash *tfm)
+{
+	struct shash_desc *desc;
+	int err;
+
+	if (!ubifs_authenticated(c))
+		return NULL;
+
+	desc = kmalloc(sizeof(*desc) + crypto_shash_descsize(tfm), GFP_KERNEL);
+	if (!desc)
+		return ERR_PTR(-ENOMEM);
+
+	desc->tfm = tfm;
+	desc->flags = CRYPTO_TFM_REQ_MAY_SLEEP;
+
+	err = crypto_shash_init(desc);
+	if (err) {
+		kfree(desc);
+		return ERR_PTR(err);
+	}
+
+	return desc;
+}
+
+/**
+ * __ubifs_hash_get_desc - get a descriptor suitable for hashing a node
+ * @c: UBIFS file-system description object
+ *
+ * This function returns a descriptor suitable for hashing a node. Free after use
+ * with kfree.
+ */
+struct shash_desc *__ubifs_hash_get_desc(const struct ubifs_info *c)
+{
+	return ubifs_get_desc(c, c->hash_tfm);
+}
+
+/**
+ * __ubifs_shash_final - finalize shash
+ * @c: UBIFS file-system description object
+ * @desc: the descriptor
+ * @out: the output hash
+ *
+ * Simple wrapper around crypto_shash_final(), safe to be called with
+ * disabled authentication.
+ */
+int __ubifs_shash_final(const struct ubifs_info *c, struct shash_desc *desc,
+			u8 *out)
+{
+	if (ubifs_authenticated(c))
+		return crypto_shash_final(desc, out);
+
+	return 0;
+}
+
+/**
+ * __ubifs_node_check_hash - check the hash of a node against given hash
+ * @c: UBIFS file-system description object
+ * @node: the node
+ * @expected: the expected hash
+ *
+ * This function calculates a hash over a node and compares it to the given hash.
+ * Returns 0 if both hashes are equal or authentication is disabled, otherwise a
+ * negative error code is returned.
+ */
+int __ubifs_node_check_hash(const struct ubifs_info *c, void *node,
+			    const u8 *expected)
+{
+	u8 calc[UBIFS_MAX_HASH_LEN];
+
+	__ubifs_node_calc_hash(c, node, calc);
+
+	if (ubifs_check_hash(c, expected, calc))
+		return -EPERM;
+
+	return 0;
+}
+
+/**
+ * ubifs_init_authentication - initialize UBIFS authentication support
+ * @c: UBIFS file-system description object
+ *
+ * This function returns 0 for success or a negative error code otherwise.
+ */
+int ubifs_init_authentication(struct ubifs_info *c)
+{
+	struct key *keyring_key;
+	const struct user_key_payload *ukp;
+	int err;
+	char hmac_name[CRYPTO_MAX_ALG_NAME];
+
+	if (!c->auth_hash_name) {
+		ubifs_err(c, "authentication hash name needed with authentication");
+		return -EINVAL;
+	}
+
+	c->auth_hash_algo = match_string(hash_algo_name, HASH_ALGO__LAST,
+					 c->auth_hash_name);
+	if ((int)c->auth_hash_algo < 0) {
+		ubifs_err(c, "Unknown hash algo %s specified",
+			  c->auth_hash_name);
+		return -EINVAL;
+	}
+
+	snprintf(hmac_name, CRYPTO_MAX_ALG_NAME, "hmac(%s)",
+		 c->auth_hash_name);
+
+	keyring_key = request_key(&key_type_logon, c->auth_key_name, NULL);
+
+	if (IS_ERR(keyring_key)) {
+		ubifs_err(c, "Failed to request key: %ld",
+			  PTR_ERR(keyring_key));
+		return PTR_ERR(keyring_key);
+	}
+
+	down_read(&keyring_key->sem);
+
+	if (keyring_key->type != &key_type_logon) {
+		ubifs_err(c, "key type must be logon");
+		err = -ENOKEY;
+		goto out;
+	}
+
+	ukp = user_key_payload_locked(keyring_key);
+	if (!ukp) {
+		/* key was revoked before we acquired its semaphore */
+		err = -EKEYREVOKED;
+		goto out;
+	}
+
+	c->hash_tfm = crypto_alloc_shash(c->auth_hash_name, 0,
+					 CRYPTO_ALG_ASYNC);
+	if (IS_ERR(c->hash_tfm)) {
+		err = PTR_ERR(c->hash_tfm);
+		ubifs_err(c, "Can not allocate %s: %d",
+			  c->auth_hash_name, err);
+		goto out;
+	}
+
+	c->hash_len = crypto_shash_digestsize(c->hash_tfm);
+	if (c->hash_len > UBIFS_MAX_HASH_LEN) {
+		ubifs_err(c, "hash %s is bigger than maximum allowed hash size (%d > %d)",
+			  c->auth_hash_name, c->hash_len, UBIFS_MAX_HASH_LEN);
+		err = -EINVAL;
+		goto out_free_hash;
+	}
+
+	c->hmac_tfm = crypto_alloc_shash(hmac_name, 0, CRYPTO_ALG_ASYNC);
+	if (IS_ERR(c->hmac_tfm)) {
+		err = PTR_ERR(c->hmac_tfm);
+		ubifs_err(c, "Can not allocate %s: %d", hmac_name, err);
+		goto out_free_hash;
+	}
+
+	c->hmac_desc_len = crypto_shash_digestsize(c->hmac_tfm);
+	if (c->hmac_desc_len > UBIFS_MAX_HMAC_LEN) {
+		ubifs_err(c, "hmac %s is bigger than maximum allowed hmac size (%d > %d)",
+			  hmac_name, c->hmac_desc_len, UBIFS_MAX_HMAC_LEN);
+		err = -EINVAL;
+		goto out_free_hash;
+	}
+
+	err = crypto_shash_setkey(c->hmac_tfm, ukp->data, ukp->datalen);
+	if (err)
+		goto out_free_hmac;
+
+	c->authenticated = true;
+
+	c->log_hash = ubifs_hash_get_desc(c);
+	if (IS_ERR(c->log_hash))
+		goto out_free_hmac;
+
+	err = 0;
+
+out_free_hmac:
+	if (err)
+		crypto_free_shash(c->hmac_tfm);
+out_free_hash:
+	if (err)
+		crypto_free_shash(c->hash_tfm);
+out:
+	up_read(&keyring_key->sem);
+	key_put(keyring_key);
+
+	return err;
+}
+
+/**
+ * __ubifs_exit_authentication - release resource
+ * @c: UBIFS file-system description object
+ *
+ * This function releases the authentication related resources.
+ */
+void __ubifs_exit_authentication(struct ubifs_info *c)
+{
+	if (!ubifs_authenticated(c))
+		return;
+
+	crypto_free_shash(c->hmac_tfm);
+	crypto_free_shash(c->hash_tfm);
+	kfree(c->log_hash);
+}
+
+/**
+ * ubifs_node_calc_hmac - calculate the HMAC of a UBIFS node
+ * @c: UBIFS file-system description object
+ * @node: the node to insert a HMAC into.
+ * @len: the length of the node
+ * @ofs_hmac: the offset in the node where the HMAC is inserted
+ * @hmac: returned HMAC
+ *
+ * This function calculates a HMAC of a UBIFS node. The HMAC is expected to be
+ * embedded into the node, so this area is not covered by the HMAC. Also not
+ * covered is the UBIFS_NODE_MAGIC and the CRC of the node.
+ */
+static void ubifs_node_calc_hmac(const struct ubifs_info *c, const void *node,
+				 int len, int ofs_hmac, void *hmac)
+{
+	SHASH_DESC_ON_STACK(shash, c->hmac_tfm);
+	int hmac_len = c->hmac_desc_len;
+
+	ubifs_assert(ofs_hmac > 8);
+	ubifs_assert(ofs_hmac + hmac_len < len);
+
+	shash->tfm = c->hmac_tfm;
+	shash->flags = CRYPTO_TFM_REQ_MAY_SLEEP;
+
+	crypto_shash_init(shash);
+
+	/* behind common node header CRC up to HMAC begin */
+	crypto_shash_update(shash, node + 8, ofs_hmac - 8);
+
+	/* behind HMAC, if any */
+	if (len - ofs_hmac - hmac_len > 0)
+		crypto_shash_update(shash, node + ofs_hmac + hmac_len,
+			    len - ofs_hmac - hmac_len);
+
+	crypto_shash_final(shash, hmac);
+}
+
+/**
+ * __ubifs_node_insert_hmac - insert a HMAC into a UBIFS node
+ * @c: UBIFS file-system description object
+ * @node: the node to insert a HMAC into.
+ * @len: the length of the node
+ * @ofs_hmac: the offset in the node where the HMAC is inserted
+ *
+ * This function inserts a HMAC at offset @ofs_hmac into the node given in
+ * @node.
+ */
+void __ubifs_node_insert_hmac(const struct ubifs_info *c, void *node, int len,
+			    int ofs_hmac)
+{
+	ubifs_node_calc_hmac(c, node, len, ofs_hmac, node + ofs_hmac);
+}
+
+/**
+ * __ubifs_node_verify_hmac - verify the HMAC of UBIFS node
+ * @c: UBIFS file-system description object
+ * @node: the node to insert a HMAC into.
+ * @len: the length of the node
+ * @ofs_hmac: the offset in the node where the HMAC is inserted
+ *
+ * This function verifies the HMAC at offset @ofs_hmac of the node given in
+ * @node. Returns 0 if successful or a negative error code otherwise.
+ */
+int __ubifs_node_verify_hmac(const struct ubifs_info *c, const void *node,
+			     int len, int ofs_hmac)
+{
+	int hmac_len = c->hmac_desc_len;
+	u8 *hmac;
+	int err;
+
+	hmac = kmalloc(hmac_len, GFP_NOFS);
+	if (!hmac)
+		return -ENOMEM;
+
+	ubifs_node_calc_hmac(c, node, len, ofs_hmac, hmac);
+
+	err = crypto_memneq(hmac, node + ofs_hmac, hmac_len);
+
+	kfree(hmac);
+
+	if (!err)
+		return 0;
+
+	return -EPERM;
+}
+
+int __ubifs_shash_copy_state(const struct ubifs_info *c, struct shash_desc *src,
+			     struct shash_desc *target)
+{
+	u8 state[crypto_shash_descsize(src->tfm)];
+	int err;
+
+	err = crypto_shash_export(src, state);
+	if (err)
+		return err;
+
+	return crypto_shash_import(target, state);
+}
+
+/**
+ * ubifs_hmac_wkm - Create a HMAC of the well known message
+ * @c: UBIFS file-system description object
+ * @hmac: The HMAC of the well known message
+ *
+ * This function creates a HMAC of a well known message. This is used
+ * to check if the provided key is suitable to authenticate a UBIFS
+ * image. This is only a convenience to the user to provide a better
+ * error message when the wrong key is provided.
+ */
+void ubifs_hmac_wkm(struct ubifs_info *c, u8 *hmac)
+{
+	SHASH_DESC_ON_STACK(shash, c->hmac_tfm);
+	int err;
+	const char well_known_message[] = "UBIFS";
+
+	if (!ubifs_authenticated(c))
+		return;
+
+	shash->tfm = c->hmac_tfm;
+	shash->flags = CRYPTO_TFM_REQ_MAY_SLEEP;
+
+	err = crypto_shash_init(shash);
+	BUG_ON(err);
+
+	crypto_shash_update(shash, well_known_message,
+			    sizeof(well_known_message) - 1);
+
+	crypto_shash_final(shash, hmac);
+}
diff --git a/fs/ubifs/ubifs.h b/fs/ubifs/ubifs.h
index 485de4e27f96..2086cb8f7115 100644
--- a/fs/ubifs/ubifs.h
+++ b/fs/ubifs/ubifs.h
@@ -39,6 +39,9 @@
 #include <linux/security.h>
 #include <linux/xattr.h>
 #include <linux/random.h>
+#include <crypto/hash_info.h>
+#include <crypto/hash.h>
+#include <crypto/algapi.h>
 
 #define __FS_HAS_ENCRYPTION IS_ENABLED(CONFIG_UBIFS_FS_ENCRYPTION)
 #include <linux/fscrypt.h>
@@ -1016,6 +1019,7 @@ struct ubifs_debug_info;
  * @bulk_read: enable bulk-reads
  * @default_compr: default compression algorithm (%UBIFS_COMPR_LZO, etc)
  * @rw_incompat: the media is not R/W compatible
+ * @authenticated: flag indigating the FS is mounted in authenticated mode
  *
  * @tnc_mutex: protects the Tree Node Cache (TNC), @zroot, @cnext, @enext, and
  *             @calc_idx_sz
@@ -1063,6 +1067,7 @@ struct ubifs_debug_info;
  * @key_hash: direntry key hash function
  * @key_fmt: key format
  * @key_len: key length
+ * @hash_len: The length of the index node hashes
  * @fanout: fanout of the index tree (number of links per indexing node)
  *
  * @min_io_size: minimal input/output unit size
@@ -1198,6 +1203,13 @@ struct ubifs_debug_info;
  * @rp_uid: reserved pool user ID
  * @rp_gid: reserved pool group ID
  *
+ * @hash_tfm: the hash transformation used for hashing nodes
+ * @hmac_tfm: the HMAC transformation for this filesystem
+ * @hmac_desc_len: length of the HMAC used for authentication
+ * @auth_key_name: the authentication key name
+ * @auth_hash_name: the name of the hash algorithm used for authentication
+ * @auth_hash_algo: the authentication hash used for this fs
+ *
  * @empty: %1 if the UBI device is empty
  * @need_recovery: %1 if the file-system needs recovery
  * @replaying: %1 during journal replay
@@ -1258,6 +1270,7 @@ struct ubifs_info {
 	unsigned int bulk_read:1;
 	unsigned int default_compr:2;
 	unsigned int rw_incompat:1;
+	unsigned int authenticated:1;
 
 	struct mutex tnc_mutex;
 	struct ubifs_zbranch zroot;
@@ -1302,6 +1315,7 @@ struct ubifs_info {
 	uint32_t (*key_hash)(const char *str, int len);
 	int key_fmt;
 	int key_len;
+	int hash_len;
 	int fanout;
 
 	int min_io_size;
@@ -1429,6 +1443,13 @@ struct ubifs_info {
 	kuid_t rp_uid;
 	kgid_t rp_gid;
 
+	struct crypto_shash *hash_tfm;
+	struct crypto_shash *hmac_tfm;
+	int hmac_desc_len;
+	char *auth_key_name;
+	char *auth_hash_name;
+	enum hash_algo auth_hash_algo;
+
 	/* The below fields are used only during mounting and re-mounting */
 	unsigned int empty:1;
 	unsigned int need_recovery:1;
@@ -1459,6 +1480,172 @@ extern const struct inode_operations ubifs_dir_inode_operations;
 extern const struct inode_operations ubifs_symlink_inode_operations;
 extern struct ubifs_compressor *ubifs_compressors[UBIFS_COMPR_TYPES_CNT];
 
+/* auth.c */
+static inline int ubifs_authenticated(const struct ubifs_info *c)
+{
+	return (IS_ENABLED(CONFIG_UBIFS_FS_AUTHENTICATION)) && c->authenticated;
+}
+
+struct shash_desc *__ubifs_hash_get_desc(const struct ubifs_info *c);
+static inline struct shash_desc *ubifs_hash_get_desc(const struct ubifs_info *c)
+{
+	return ubifs_authenticated(c) ? __ubifs_hash_get_desc(c) : NULL;
+}
+
+static inline void ubifs_shash_update(const struct ubifs_info *c,
+				      struct shash_desc *desc, const void *buf,
+				      unsigned int len)
+{
+	if (ubifs_authenticated(c))
+		crypto_shash_update(desc, buf, len);
+}
+
+static inline int ubifs_shash_final(const struct ubifs_info *c,
+				    struct shash_desc *desc, u8 *out)
+{
+	return ubifs_authenticated(c) ? crypto_shash_final(desc, out) : 0;
+}
+
+void __ubifs_node_calc_hash(const struct ubifs_info *c, const void *buf,
+			  u8 *hash);
+static inline void ubifs_node_calc_hash(const struct ubifs_info *c,
+					const void *buf, u8 *hash)
+{
+	if (ubifs_authenticated(c))
+		__ubifs_node_calc_hash(c, buf, hash);
+}
+
+void ubifs_prepare_auth_node(struct ubifs_info *c, void *node,
+			     struct shash_desc *inhash);
+
+/**
+ * ubifs_check_hash - compare two hashes
+ * @c: UBIFS file-system description object
+ * @expected: first hash
+ * @got: second hash
+ *
+ * Compare two hashes @expected and @got. Returns 0 when they are equal, a
+ * negative error code otherwise.
+ */
+static inline int ubifs_check_hash(const struct ubifs_info *c,
+				   const u8 *expected, const u8 *got)
+{
+	return crypto_memneq(expected, got, c->hash_len);
+}
+
+/**
+ * ubifs_check_hmac - compare two HMACs
+ * @c: UBIFS file-system description object
+ * @expected: first HMAC
+ * @got: second HMAC
+ *
+ * Compare two hashes @expected and @got. Returns 0 when they are equal, a
+ * negative error code otherwise.
+ */
+static inline int ubifs_check_hmac(const struct ubifs_info *c,
+				   const u8 *expected, const u8 *got)
+{
+	return crypto_memneq(expected, got, c->hmac_desc_len);
+}
+
+int __ubifs_node_check_hash(const struct ubifs_info *c, void *buf,
+			  const u8 *expected);
+static inline int ubifs_node_check_hash(const struct ubifs_info *c, void *buf,
+			  const u8 *expected)
+{
+	if (ubifs_authenticated(c))
+		return __ubifs_node_check_hash(c, buf, expected);
+	else
+		return 0;
+}
+
+int ubifs_init_authentication(struct ubifs_info *c);
+void __ubifs_exit_authentication(struct ubifs_info *c);
+static inline void ubifs_exit_authentication(struct ubifs_info *c)
+{
+	if (ubifs_authenticated(c))
+		__ubifs_exit_authentication(c);
+}
+
+/**
+ * ubifs_branch_hash - returns a pointer to the hash of a branch
+ * @c: UBIFS file-system description object
+ * @br: branch to get the hash from
+ *
+ * This returns a pointer to the hash of a branch. Since the key already is a
+ * dynamically sized object we cannot use a struct member here.
+ */
+static inline u8 *ubifs_branch_hash(struct ubifs_info *c,
+				    struct ubifs_branch *br)
+{
+	return (void *)br + sizeof(*br) + c->key_len;
+}
+
+/**
+ * ubifs_copy_hash - copy a hash
+ * @c: UBIFS file-system description object
+ * @from: source hash
+ * @to: destination hash
+ *
+ * With authentication this copies a hash, otherwise does nothing.
+ */
+static inline void ubifs_copy_hash(const struct ubifs_info *c, const u8 *from,
+				   u8 *to)
+{
+	if (ubifs_authenticated(c))
+		memcpy(to, from, c->hash_len);
+}
+
+void __ubifs_node_insert_hmac(const struct ubifs_info *c, void *buf,
+			      int len, int ofs_hmac);
+static inline void ubifs_node_insert_hmac(const struct ubifs_info *c, void *buf,
+					  int len, int ofs_hmac)
+{
+	if (ubifs_authenticated(c))
+		__ubifs_node_insert_hmac(c, buf, len, ofs_hmac);
+}
+
+int __ubifs_node_verify_hmac(const struct ubifs_info *c, const void *buf,
+			     int len, int ofs_hmac);
+static inline int ubifs_node_verify_hmac(const struct ubifs_info *c,
+					 const void *buf, int len, int ofs_hmac)
+{
+	if (ubifs_authenticated(c))
+		return __ubifs_node_verify_hmac(c, buf, len, ofs_hmac);
+	else
+		return 0;
+}
+
+/**
+ * ubifs_auth_node_sz - returns the size of an authentication node
+ * @c: UBIFS file-system description object
+ *
+ * This function returns the size of an authentication node which can
+ * be 0 for unauthenticated filesystems or the real size of an auth node
+ * authentication is enabled.
+ */
+static inline int ubifs_auth_node_sz(const struct ubifs_info *c)
+{
+	if (ubifs_authenticated(c))
+		return sizeof(struct ubifs_auth_node) + c->hmac_desc_len;
+	else
+		return 0;
+}
+
+void ubifs_hmac_wkm(struct ubifs_info *c, u8 *hmac);
+
+int __ubifs_shash_copy_state(const struct ubifs_info *c, struct shash_desc *src,
+			     struct shash_desc *target);
+static inline int ubifs_shash_copy_state(const struct ubifs_info *c,
+					   struct shash_desc *src,
+					   struct shash_desc *target)
+{
+	if (ubifs_authenticated(c))
+		return __ubifs_shash_copy_state(c, src, target);
+	else
+		return 0;
+}
+
 /* io.c */
 void ubifs_ro_mode(struct ubifs_info *c, int err);
 int ubifs_leb_read(const struct ubifs_info *c, int lnum, void *buf, int offs,
-- 
2.18.0


^ permalink raw reply related	[flat|nested] 49+ messages in thread

* [PATCH 11/25] ubifs: Create functions to embed a HMAC in a node
  2018-07-04 12:41 [PATCH 00/25] UBIFS authentication support Sascha Hauer
                   ` (9 preceding siblings ...)
  2018-07-04 12:41 ` [PATCH 10/25] ubifs: add helper functions for authentication support Sascha Hauer
@ 2018-07-04 12:41 ` Sascha Hauer
  2018-07-04 12:41 ` [PATCH 12/25] ubifs: Add hashes to the tree node cache Sascha Hauer
                   ` (13 subsequent siblings)
  24 siblings, 0 replies; 49+ messages in thread
From: Sascha Hauer @ 2018-07-04 12:41 UTC (permalink / raw)
  To: linux-mtd
  Cc: David Gstir, Richard Weinberger, kernel, linux-kernel, Sascha Hauer

With authentication support some nodes (master node, super block node)
get a HMAC embedded into them. This patch adds functions to prepare and
write such a node.
The difficulty is that besides the HMAC the nodes also have a CRC which
must stay valid. This means we first have to initialize all fields in
the node, then calculate the HMAC (not covering the CRC) and finally
calculate the CRC.

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
 fs/ubifs/io.c    | 58 +++++++++++++++++++++++++++++++++++++++++++-----
 fs/ubifs/ubifs.h |  4 ++++
 2 files changed, 56 insertions(+), 6 deletions(-)

diff --git a/fs/ubifs/io.c b/fs/ubifs/io.c
index eeb8fb073d33..6f83da07edb1 100644
--- a/fs/ubifs/io.c
+++ b/fs/ubifs/io.c
@@ -394,6 +394,31 @@ void ubifs_crc_node(struct ubifs_info *c, void *node, int len)
 	ch->crc = cpu_to_le32(crc);
 }
 
+/**
+ * ubifs_prepare_node_hmac - prepare node to be written to flash.
+ * @c: UBIFS file-system description object
+ * @node: the node to pad
+ * @len: node length
+ * @hmac_offs: offset of the HMAC in the node
+ * @pad: if the buffer has to be padded
+ *
+ * This function prepares node at @node to be written to the media - it
+ * calculates node CRC, fills the common header, and adds proper padding up to
+ * the next minimum I/O unit if @pad is not zero. if @hmac_offs is positive then
+ * a HMAC is inserted into the node at the given offset.
+ */
+void ubifs_prepare_node_hmac(struct ubifs_info *c, void *node, int len, int hmac_offs,
+			     int pad)
+{
+
+	ubifs_init_node(c, node, len, pad);
+
+	if (hmac_offs > 0)
+		ubifs_node_insert_hmac(c, node, len, hmac_offs);
+
+	ubifs_crc_node(c, node, len);
+}
+
 /**
  * ubifs_prepare_node - prepare node to be written to flash.
  * @c: UBIFS file-system description object
@@ -407,8 +432,7 @@ void ubifs_crc_node(struct ubifs_info *c, void *node, int len)
  */
 void ubifs_prepare_node(struct ubifs_info *c, void *node, int len, int pad)
 {
-	ubifs_init_node(c, node, len, pad);
-	ubifs_crc_node(c, node, len);
+	ubifs_prepare_node_hmac(c, node, len, 0, pad);
 }
 
 /**
@@ -860,12 +884,13 @@ int ubifs_wbuf_write_nolock(struct ubifs_wbuf *wbuf, void *buf, int len)
 }
 
 /**
- * ubifs_write_node - write node to the media.
+ * ubifs_write_node_hmac - write node to the media.
  * @c: UBIFS file-system description object
  * @buf: the node to write
  * @len: node length
  * @lnum: logical eraseblock number
  * @offs: offset within the logical eraseblock
+ * @hmac_offs: offset of the HMAC within the node
  *
  * This function automatically fills node magic number, assigns sequence
  * number, and calculates node CRC checksum. The length of the @buf buffer has
@@ -873,8 +898,8 @@ int ubifs_wbuf_write_nolock(struct ubifs_wbuf *wbuf, void *buf, int len)
  * appends padding node and padding bytes if needed. Returns zero in case of
  * success and a negative error code in case of failure.
  */
-int ubifs_write_node(struct ubifs_info *c, void *buf, int len, int lnum,
-		     int offs)
+int ubifs_write_node_hmac(struct ubifs_info *c, void *buf, int len, int lnum,
+			  int offs, int hmac_offs)
 {
 	int err, buf_len = ALIGN(len, c->min_io_size);
 
@@ -889,7 +914,8 @@ int ubifs_write_node(struct ubifs_info *c, void *buf, int len, int lnum,
 	if (c->ro_error)
 		return -EROFS;
 
-	ubifs_prepare_node(c, buf, len, 1);
+	ubifs_prepare_node_hmac(c, buf, len, hmac_offs, 1);
+
 	err = ubifs_leb_write(c, lnum, buf, offs, buf_len);
 	if (err)
 		ubifs_dump_node(c, buf);
@@ -897,6 +923,26 @@ int ubifs_write_node(struct ubifs_info *c, void *buf, int len, int lnum,
 	return err;
 }
 
+/**
+ * ubifs_write_node - write node to the media.
+ * @c: UBIFS file-system description object
+ * @buf: the node to write
+ * @len: node length
+ * @lnum: logical eraseblock number
+ * @offs: offset within the logical eraseblock
+ *
+ * This function automatically fills node magic number, assigns sequence
+ * number, and calculates node CRC checksum. The length of the @buf buffer has
+ * to be aligned to the minimal I/O unit size. This function automatically
+ * appends padding node and padding bytes if needed. Returns zero in case of
+ * success and a negative error code in case of failure.
+ */
+int ubifs_write_node(struct ubifs_info *c, void *buf, int len, int lnum,
+		     int offs)
+{
+	return ubifs_write_node_hmac(c, buf, len, lnum, offs, -1);
+}
+
 /**
  * ubifs_read_node_wbuf - read node from the media or write-buffer.
  * @wbuf: wbuf to check for un-written data
diff --git a/fs/ubifs/ubifs.h b/fs/ubifs/ubifs.h
index 2086cb8f7115..d8a7276e83df 100644
--- a/fs/ubifs/ubifs.h
+++ b/fs/ubifs/ubifs.h
@@ -1665,11 +1665,15 @@ int ubifs_read_node_wbuf(struct ubifs_wbuf *wbuf, void *buf, int type, int len,
 			 int lnum, int offs);
 int ubifs_write_node(struct ubifs_info *c, void *node, int len, int lnum,
 		     int offs);
+int ubifs_write_node_hmac(struct ubifs_info *c, void *buf, int len, int lnum,
+			  int offs, int hmac_offs);
 int ubifs_check_node(const struct ubifs_info *c, const void *buf, int lnum,
 		     int offs, int quiet, int must_chk_crc);
 void ubifs_init_node(struct ubifs_info *c, void *buf, int len, int pad);
 void ubifs_crc_node(struct ubifs_info *c, void *buf, int len);
 void ubifs_prepare_node(struct ubifs_info *c, void *buf, int len, int pad);
+void ubifs_prepare_node_hmac(struct ubifs_info *c, void *node, int len,
+			     int hmac_offs, int pad);
 void ubifs_prep_grp_node(struct ubifs_info *c, void *node, int len, int last);
 int ubifs_io_init(struct ubifs_info *c);
 void ubifs_pad(const struct ubifs_info *c, void *buf, int pad);
-- 
2.18.0


^ permalink raw reply related	[flat|nested] 49+ messages in thread

* [PATCH 12/25] ubifs: Add hashes to the tree node cache
  2018-07-04 12:41 [PATCH 00/25] UBIFS authentication support Sascha Hauer
                   ` (10 preceding siblings ...)
  2018-07-04 12:41 ` [PATCH 11/25] ubifs: Create functions to embed a HMAC in a node Sascha Hauer
@ 2018-07-04 12:41 ` Sascha Hauer
  2018-08-27 19:18   ` Richard Weinberger
  2018-07-04 12:41 ` [PATCH 13/25] ubifs: authentication: Add hashes to index nodes Sascha Hauer
                   ` (12 subsequent siblings)
  24 siblings, 1 reply; 49+ messages in thread
From: Sascha Hauer @ 2018-07-04 12:41 UTC (permalink / raw)
  To: linux-mtd
  Cc: David Gstir, Richard Weinberger, kernel, linux-kernel, Sascha Hauer

As part of the UBIFS authentication support every branch in the index
gets a hash covering the referenced node. To make that happen the tree
node cache needs hashes over the nodes. This patch adds a hash argument
to ubifs_tnc_add() and ubifs_tnc_add_nm(). The hashes are calculated
from the callers of these functions which actually prepare the nodes.
With this patch all the leaf nodes of the index tree get hashes, but
currently nothing is done with these hashes, this is left for a later
patch.

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
 fs/ubifs/journal.c | 93 ++++++++++++++++++++++++++++++++++------------
 fs/ubifs/replay.c  |  4 +-
 fs/ubifs/tnc.c     | 10 ++++-
 fs/ubifs/ubifs.h   |  7 +++-
 4 files changed, 84 insertions(+), 30 deletions(-)

diff --git a/fs/ubifs/journal.c b/fs/ubifs/journal.c
index 754d969eb27e..55b35bc33c31 100644
--- a/fs/ubifs/journal.c
+++ b/fs/ubifs/journal.c
@@ -517,6 +517,9 @@ int ubifs_jnl_update(struct ubifs_info *c, const struct inode *dir,
 	struct ubifs_dent_node *dent;
 	struct ubifs_ino_node *ino;
 	union ubifs_key dent_key, ino_key;
+	u8 hash_dent[UBIFS_MAX_HASH_LEN];
+	u8 hash_ino[UBIFS_MAX_HASH_LEN];
+	u8 hash_ino_host[UBIFS_MAX_HASH_LEN];
 
 	ubifs_assert(mutex_is_locked(&host_ui->ui_mutex));
 
@@ -571,11 +574,14 @@ int ubifs_jnl_update(struct ubifs_info *c, const struct inode *dir,
 
 	zero_dent_node_unused(dent);
 	ubifs_prep_grp_node(c, dent, dlen, 0);
+	ubifs_node_calc_hash(c, dent, hash_dent);
 
 	ino = (void *)dent + aligned_dlen;
 	pack_inode(c, ino, inode, 0);
+	ubifs_node_calc_hash(c, ino, hash_ino);
 	ino = (void *)ino + aligned_ilen;
 	pack_inode(c, ino, dir, 1);
+	ubifs_node_calc_hash(c, ino, hash_ino_host);
 
 	if (last_reference) {
 		err = ubifs_add_orphan(c, inode->i_ino);
@@ -607,7 +613,8 @@ int ubifs_jnl_update(struct ubifs_info *c, const struct inode *dir,
 			goto out_ro;
 		err = ubifs_add_dirt(c, lnum, dlen);
 	} else
-		err = ubifs_tnc_add_nm(c, &dent_key, lnum, dent_offs, dlen, nm);
+		err = ubifs_tnc_add_nm(c, &dent_key, lnum, dent_offs, dlen,
+				       hash_dent, nm);
 	if (err)
 		goto out_ro;
 
@@ -619,14 +626,14 @@ int ubifs_jnl_update(struct ubifs_info *c, const struct inode *dir,
 	 */
 	ino_key_init(c, &ino_key, inode->i_ino);
 	ino_offs = dent_offs + aligned_dlen;
-	err = ubifs_tnc_add(c, &ino_key, lnum, ino_offs, ilen);
+	err = ubifs_tnc_add(c, &ino_key, lnum, ino_offs, ilen, hash_ino);
 	if (err)
 		goto out_ro;
 
 	ino_key_init(c, &ino_key, dir->i_ino);
 	ino_offs += aligned_ilen;
 	err = ubifs_tnc_add(c, &ino_key, lnum, ino_offs,
-			    UBIFS_INO_NODE_SZ + host_ui->data_len);
+			    UBIFS_INO_NODE_SZ + host_ui->data_len, hash_ino_host);
 	if (err)
 		goto out_ro;
 
@@ -674,6 +681,7 @@ int ubifs_jnl_write_data(struct ubifs_info *c, const struct inode *inode,
 	int dlen = COMPRESSED_DATA_NODE_BUF_SZ, allocated = 1;
 	struct ubifs_inode *ui = ubifs_inode(inode);
 	bool encrypted = ubifs_crypt_is_encrypted(inode);
+	u8 hash[UBIFS_MAX_HASH_LEN];
 
 	dbg_jnlk(key, "ino %lu, blk %u, len %d, key ",
 		(unsigned long)key_inum(c, key), key_block(c, key), len);
@@ -732,10 +740,13 @@ int ubifs_jnl_write_data(struct ubifs_info *c, const struct inode *inode,
 	err = write_head(c, DATAHD, data, dlen, &lnum, &offs, 0);
 	if (err)
 		goto out_release;
+
+	ubifs_node_calc_hash(c, data, hash);
+
 	ubifs_wbuf_add_ino_nolock(&c->jheads[DATAHD].wbuf, key_inum(c, key));
 	release_head(c, DATAHD);
 
-	err = ubifs_tnc_add(c, key, lnum, offs, dlen);
+	err = ubifs_tnc_add(c, key, lnum, offs, dlen, hash);
 	if (err)
 		goto out_ro;
 
@@ -774,6 +785,7 @@ int ubifs_jnl_write_inode(struct ubifs_info *c, const struct inode *inode)
 	struct ubifs_ino_node *ino;
 	struct ubifs_inode *ui = ubifs_inode(inode);
 	int sync = 0, len = UBIFS_INO_NODE_SZ, last_reference = !inode->i_nlink;
+	u8 hash[UBIFS_MAX_HASH_LEN];
 
 	dbg_jnl("ino %lu, nlink %u", inode->i_ino, inode->i_nlink);
 
@@ -795,6 +807,7 @@ int ubifs_jnl_write_inode(struct ubifs_info *c, const struct inode *inode)
 		goto out_free;
 
 	pack_inode(c, ino, inode, 1);
+	ubifs_node_calc_hash(c, ino, hash);
 	err = write_head(c, BASEHD, ino, len, &lnum, &offs, sync);
 	if (err)
 		goto out_release;
@@ -813,7 +826,7 @@ int ubifs_jnl_write_inode(struct ubifs_info *c, const struct inode *inode)
 		union ubifs_key key;
 
 		ino_key_init(c, &key, inode->i_ino);
-		err = ubifs_tnc_add(c, &key, lnum, offs, len);
+		err = ubifs_tnc_add(c, &key, lnum, offs, len, hash);
 	}
 	if (err)
 		goto out_ro;
@@ -923,6 +936,10 @@ int ubifs_jnl_xrename(struct ubifs_info *c, const struct inode *fst_dir,
 	int aligned_dlen1, aligned_dlen2;
 	int twoparents = (fst_dir != snd_dir);
 	void *p;
+	u8 hash_dent1[UBIFS_MAX_HASH_LEN];
+	u8 hash_dent2[UBIFS_MAX_HASH_LEN];
+	u8 hash_p1[UBIFS_MAX_HASH_LEN];
+	u8 hash_p2[UBIFS_MAX_HASH_LEN];
 
 	ubifs_assert(ubifs_inode(fst_dir)->data_len == 0);
 	ubifs_assert(ubifs_inode(snd_dir)->data_len == 0);
@@ -958,6 +975,7 @@ int ubifs_jnl_xrename(struct ubifs_info *c, const struct inode *fst_dir,
 	set_dent_cookie(c, dent1);
 	zero_dent_node_unused(dent1);
 	ubifs_prep_grp_node(c, dent1, dlen1, 0);
+	ubifs_node_calc_hash(c, dent1, hash_dent1);
 
 	/* Make new dent for 2nd entry */
 	dent2 = (void *)dent1 + aligned_dlen1;
@@ -971,14 +989,18 @@ int ubifs_jnl_xrename(struct ubifs_info *c, const struct inode *fst_dir,
 	set_dent_cookie(c, dent2);
 	zero_dent_node_unused(dent2);
 	ubifs_prep_grp_node(c, dent2, dlen2, 0);
+	ubifs_node_calc_hash(c, dent2, hash_dent2);
 
 	p = (void *)dent2 + aligned_dlen2;
-	if (!twoparents)
+	if (!twoparents) {
 		pack_inode(c, p, fst_dir, 1);
-	else {
+		ubifs_node_calc_hash(c, p, hash_p1);
+	} else {
 		pack_inode(c, p, fst_dir, 0);
+		ubifs_node_calc_hash(c, p, hash_p1);
 		p += ALIGN(plen, 8);
 		pack_inode(c, p, snd_dir, 1);
+		ubifs_node_calc_hash(c, p, hash_p2);
 	}
 
 	err = write_head(c, BASEHD, dent1, len, &lnum, &offs, sync);
@@ -993,27 +1015,27 @@ int ubifs_jnl_xrename(struct ubifs_info *c, const struct inode *fst_dir,
 	release_head(c, BASEHD);
 
 	dent_key_init(c, &key, snd_dir->i_ino, snd_nm);
-	err = ubifs_tnc_add_nm(c, &key, lnum, offs, dlen1, snd_nm);
+	err = ubifs_tnc_add_nm(c, &key, lnum, offs, dlen1, hash_dent1, snd_nm);
 	if (err)
 		goto out_ro;
 
 	offs += aligned_dlen1;
 	dent_key_init(c, &key, fst_dir->i_ino, fst_nm);
-	err = ubifs_tnc_add_nm(c, &key, lnum, offs, dlen2, fst_nm);
+	err = ubifs_tnc_add_nm(c, &key, lnum, offs, dlen2, hash_dent2, fst_nm);
 	if (err)
 		goto out_ro;
 
 	offs += aligned_dlen2;
 
 	ino_key_init(c, &key, fst_dir->i_ino);
-	err = ubifs_tnc_add(c, &key, lnum, offs, plen);
+	err = ubifs_tnc_add(c, &key, lnum, offs, plen, hash_p1);
 	if (err)
 		goto out_ro;
 
 	if (twoparents) {
 		offs += ALIGN(plen, 8);
 		ino_key_init(c, &key, snd_dir->i_ino);
-		err = ubifs_tnc_add(c, &key, lnum, offs, plen);
+		err = ubifs_tnc_add(c, &key, lnum, offs, plen, hash_p2);
 		if (err)
 			goto out_ro;
 	}
@@ -1066,6 +1088,11 @@ int ubifs_jnl_rename(struct ubifs_info *c, const struct inode *old_dir,
 	int last_reference = !!(new_inode && new_inode->i_nlink == 0);
 	int move = (old_dir != new_dir);
 	struct ubifs_inode *uninitialized_var(new_ui);
+	u8 hash_old_dir[UBIFS_MAX_HASH_LEN];
+	u8 hash_new_dir[UBIFS_MAX_HASH_LEN];
+	u8 hash_new_inode[UBIFS_MAX_HASH_LEN];
+	u8 hash_dent1[UBIFS_MAX_HASH_LEN];
+	u8 hash_dent2[UBIFS_MAX_HASH_LEN];
 
 	ubifs_assert(ubifs_inode(old_dir)->data_len == 0);
 	ubifs_assert(ubifs_inode(new_dir)->data_len == 0);
@@ -1108,6 +1135,7 @@ int ubifs_jnl_rename(struct ubifs_info *c, const struct inode *old_dir,
 	set_dent_cookie(c, dent);
 	zero_dent_node_unused(dent);
 	ubifs_prep_grp_node(c, dent, dlen1, 0);
+	ubifs_node_calc_hash(c, dent, hash_dent1);
 
 	dent2 = (void *)dent + aligned_dlen1;
 	dent2->ch.node_type = UBIFS_DENT_NODE;
@@ -1127,19 +1155,24 @@ int ubifs_jnl_rename(struct ubifs_info *c, const struct inode *old_dir,
 	set_dent_cookie(c, dent2);
 	zero_dent_node_unused(dent2);
 	ubifs_prep_grp_node(c, dent2, dlen2, 0);
+	ubifs_node_calc_hash(c, dent2, hash_dent2);
 
 	p = (void *)dent2 + aligned_dlen2;
 	if (new_inode) {
 		pack_inode(c, p, new_inode, 0);
+		ubifs_node_calc_hash(c, p, hash_new_inode);
 		p += ALIGN(ilen, 8);
 	}
 
-	if (!move)
+	if (!move) {
 		pack_inode(c, p, old_dir, 1);
-	else {
+		ubifs_node_calc_hash(c, p, hash_old_dir);
+	} else {
 		pack_inode(c, p, old_dir, 0);
+		ubifs_node_calc_hash(c, p, hash_old_dir);
 		p += ALIGN(plen, 8);
 		pack_inode(c, p, new_dir, 1);
+		ubifs_node_calc_hash(c, p, hash_new_dir);
 	}
 
 	if (last_reference) {
@@ -1166,14 +1199,14 @@ int ubifs_jnl_rename(struct ubifs_info *c, const struct inode *old_dir,
 	release_head(c, BASEHD);
 
 	dent_key_init(c, &key, new_dir->i_ino, new_nm);
-	err = ubifs_tnc_add_nm(c, &key, lnum, offs, dlen1, new_nm);
+	err = ubifs_tnc_add_nm(c, &key, lnum, offs, dlen1, hash_dent1, new_nm);
 	if (err)
 		goto out_ro;
 
 	offs += aligned_dlen1;
 	if (whiteout) {
 		dent_key_init(c, &key, old_dir->i_ino, old_nm);
-		err = ubifs_tnc_add_nm(c, &key, lnum, offs, dlen2, old_nm);
+		err = ubifs_tnc_add_nm(c, &key, lnum, offs, dlen2, hash_dent2, old_nm);
 		if (err)
 			goto out_ro;
 
@@ -1192,21 +1225,21 @@ int ubifs_jnl_rename(struct ubifs_info *c, const struct inode *old_dir,
 	offs += aligned_dlen2;
 	if (new_inode) {
 		ino_key_init(c, &key, new_inode->i_ino);
-		err = ubifs_tnc_add(c, &key, lnum, offs, ilen);
+		err = ubifs_tnc_add(c, &key, lnum, offs, ilen, hash_new_inode);
 		if (err)
 			goto out_ro;
 		offs += ALIGN(ilen, 8);
 	}
 
 	ino_key_init(c, &key, old_dir->i_ino);
-	err = ubifs_tnc_add(c, &key, lnum, offs, plen);
+	err = ubifs_tnc_add(c, &key, lnum, offs, plen, hash_old_dir);
 	if (err)
 		goto out_ro;
 
 	if (move) {
 		offs += ALIGN(plen, 8);
 		ino_key_init(c, &key, new_dir->i_ino);
-		err = ubifs_tnc_add(c, &key, lnum, offs, plen);
+		err = ubifs_tnc_add(c, &key, lnum, offs, plen, hash_new_dir);
 		if (err)
 			goto out_ro;
 	}
@@ -1326,6 +1359,8 @@ int ubifs_jnl_truncate(struct ubifs_info *c, const struct inode *inode,
 	struct ubifs_inode *ui = ubifs_inode(inode);
 	ino_t inum = inode->i_ino;
 	unsigned int blk;
+	u8 hash_ino[UBIFS_MAX_HASH_LEN];
+	u8 hash_dn[UBIFS_MAX_HASH_LEN];
 
 	dbg_jnl("ino %lu, size %lld -> %lld",
 		(unsigned long)inum, old_size, new_size);
@@ -1378,9 +1413,13 @@ int ubifs_jnl_truncate(struct ubifs_info *c, const struct inode *inode,
 		goto out_free;
 
 	pack_inode(c, ino, inode, 0);
+	ubifs_node_calc_hash(c, ino, hash_ino);
+
 	ubifs_prep_grp_node(c, trun, UBIFS_TRUN_NODE_SZ, dlen ? 0 : 1);
-	if (dlen)
+	if (dlen) {
 		ubifs_prep_grp_node(c, dn, dlen, 1);
+		ubifs_node_calc_hash(c, dn, hash_dn);
+	}
 
 	err = write_head(c, BASEHD, ino, len, &lnum, &offs, sync);
 	if (err)
@@ -1391,13 +1430,13 @@ int ubifs_jnl_truncate(struct ubifs_info *c, const struct inode *inode,
 
 	if (dlen) {
 		sz = offs + UBIFS_INO_NODE_SZ + UBIFS_TRUN_NODE_SZ;
-		err = ubifs_tnc_add(c, &key, lnum, sz, dlen);
+		err = ubifs_tnc_add(c, &key, lnum, sz, dlen, hash_dn);
 		if (err)
 			goto out_ro;
 	}
 
 	ino_key_init(c, &key, inum);
-	err = ubifs_tnc_add(c, &key, lnum, offs, UBIFS_INO_NODE_SZ);
+	err = ubifs_tnc_add(c, &key, lnum, offs, UBIFS_INO_NODE_SZ, hash_ino);
 	if (err)
 		goto out_ro;
 
@@ -1458,6 +1497,7 @@ int ubifs_jnl_delete_xattr(struct ubifs_info *c, const struct inode *host,
 	union ubifs_key xent_key, key1, key2;
 	int sync = IS_DIRSYNC(host);
 	struct ubifs_inode *host_ui = ubifs_inode(host);
+	u8 hash[UBIFS_MAX_HASH_LEN];
 
 	ubifs_assert(inode->i_nlink == 0);
 	ubifs_assert(mutex_is_locked(&host_ui->ui_mutex));
@@ -1497,6 +1537,7 @@ int ubifs_jnl_delete_xattr(struct ubifs_info *c, const struct inode *host,
 	pack_inode(c, ino, inode, 0);
 	ino = (void *)ino + UBIFS_INO_NODE_SZ;
 	pack_inode(c, ino, host, 1);
+	ubifs_node_calc_hash(c, ino, hash);
 
 	err = write_head(c, BASEHD, xent, len, &lnum, &xent_offs, sync);
 	if (!sync && !err)
@@ -1529,7 +1570,7 @@ int ubifs_jnl_delete_xattr(struct ubifs_info *c, const struct inode *host,
 
 	/* And update TNC with the new host inode position */
 	ino_key_init(c, &key1, host->i_ino);
-	err = ubifs_tnc_add(c, &key1, lnum, xent_offs + len - hlen, hlen);
+	err = ubifs_tnc_add(c, &key1, lnum, xent_offs + len - hlen, hlen, hash);
 	if (err)
 		goto out_ro;
 
@@ -1567,6 +1608,8 @@ int ubifs_jnl_change_xattr(struct ubifs_info *c, const struct inode *inode,
 	struct ubifs_ino_node *ino;
 	union ubifs_key key;
 	int sync = IS_DIRSYNC(host);
+	u8 hash_host[UBIFS_MAX_HASH_LEN];
+	u8 hash[UBIFS_MAX_HASH_LEN];
 
 	dbg_jnl("ino %lu, ino %lu", host->i_ino, inode->i_ino);
 	ubifs_assert(host->i_nlink > 0);
@@ -1588,7 +1631,9 @@ int ubifs_jnl_change_xattr(struct ubifs_info *c, const struct inode *inode,
 		goto out_free;
 
 	pack_inode(c, ino, host, 0);
+	ubifs_node_calc_hash(c, ino, hash_host);
 	pack_inode(c, (void *)ino + aligned_len1, inode, 1);
+	ubifs_node_calc_hash(c, (void *)ino + aligned_len1, hash);
 
 	err = write_head(c, BASEHD, ino, aligned_len, &lnum, &offs, 0);
 	if (!sync && !err) {
@@ -1602,12 +1647,12 @@ int ubifs_jnl_change_xattr(struct ubifs_info *c, const struct inode *inode,
 		goto out_ro;
 
 	ino_key_init(c, &key, host->i_ino);
-	err = ubifs_tnc_add(c, &key, lnum, offs, len1);
+	err = ubifs_tnc_add(c, &key, lnum, offs, len1, hash_host);
 	if (err)
 		goto out_ro;
 
 	ino_key_init(c, &key, inode->i_ino);
-	err = ubifs_tnc_add(c, &key, lnum, offs + aligned_len1, len2);
+	err = ubifs_tnc_add(c, &key, lnum, offs + aligned_len1, len2, hash);
 	if (err)
 		goto out_ro;
 
diff --git a/fs/ubifs/replay.c b/fs/ubifs/replay.c
index 85c2a43082b7..7693556e5bd3 100644
--- a/fs/ubifs/replay.c
+++ b/fs/ubifs/replay.c
@@ -228,7 +228,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, &r->nm);
+					       r->len, NULL, &r->nm);
 	} else {
 		if (r->deletion)
 			switch (key_type(c, &r->key)) {
@@ -248,7 +248,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);
+					    r->len, NULL);
 		if (err)
 			return err;
 
diff --git a/fs/ubifs/tnc.c b/fs/ubifs/tnc.c
index 2c42d1a1842a..a47fced47823 100644
--- a/fs/ubifs/tnc.c
+++ b/fs/ubifs/tnc.c
@@ -2259,13 +2259,14 @@ static int tnc_insert(struct ubifs_info *c, struct ubifs_znode *znode,
  * @lnum: LEB number of node
  * @offs: node offset
  * @len: node length
+ * @hash: The hash over the node
  *
  * This function adds a node with key @key to TNC. The node may be new or it may
  * obsolete some existing one. Returns %0 on success or negative error code on
  * failure.
  */
 int ubifs_tnc_add(struct ubifs_info *c, const union ubifs_key *key, int lnum,
-		  int offs, int len)
+		  int offs, int len, const u8 *hash)
 {
 	int found, n, err = 0;
 	struct ubifs_znode *znode;
@@ -2280,6 +2281,7 @@ int ubifs_tnc_add(struct ubifs_info *c, const union ubifs_key *key, int lnum,
 		zbr.lnum = lnum;
 		zbr.offs = offs;
 		zbr.len = len;
+		ubifs_copy_hash(c, hash, zbr.hash);
 		key_copy(c, key, &zbr.key);
 		err = tnc_insert(c, znode, &zbr, n + 1);
 	} else if (found == 1) {
@@ -2290,6 +2292,7 @@ int ubifs_tnc_add(struct ubifs_info *c, const union ubifs_key *key, int lnum,
 		zbr->lnum = lnum;
 		zbr->offs = offs;
 		zbr->len = len;
+		ubifs_copy_hash(c, hash, zbr->hash);
 	} else
 		err = found;
 	if (!err)
@@ -2391,13 +2394,14 @@ int ubifs_tnc_replace(struct ubifs_info *c, const union ubifs_key *key,
  * @lnum: LEB number of node
  * @offs: node offset
  * @len: node length
+ * @hash: The hash over the node
  * @nm: node name
  *
  * This is the same as 'ubifs_tnc_add()' but it should be used with keys which
  * may have collisions, like directory entry keys.
  */
 int ubifs_tnc_add_nm(struct ubifs_info *c, const union ubifs_key *key,
-		     int lnum, int offs, int len,
+		     int lnum, int offs, int len, const u8 *hash,
 		     const struct fscrypt_name *nm)
 {
 	int found, n, err = 0;
@@ -2440,6 +2444,7 @@ int ubifs_tnc_add_nm(struct ubifs_info *c, const union ubifs_key *key,
 			zbr->lnum = lnum;
 			zbr->offs = offs;
 			zbr->len = len;
+			ubifs_copy_hash(c, hash, zbr->hash);
 			goto out_unlock;
 		}
 	}
@@ -2451,6 +2456,7 @@ int ubifs_tnc_add_nm(struct ubifs_info *c, const union ubifs_key *key,
 		zbr.lnum = lnum;
 		zbr.offs = offs;
 		zbr.len = len;
+		ubifs_copy_hash(c, hash, zbr.hash);
 		key_copy(c, key, &zbr.key);
 		err = tnc_insert(c, znode, &zbr, n + 1);
 		if (err)
diff --git a/fs/ubifs/ubifs.h b/fs/ubifs/ubifs.h
index d8a7276e83df..24ee376147e1 100644
--- a/fs/ubifs/ubifs.h
+++ b/fs/ubifs/ubifs.h
@@ -727,6 +727,7 @@ struct ubifs_jhead {
  * @lnum: LEB number of the target node (indexing node or data node)
  * @offs: target node offset within @lnum
  * @len: target node length
+ * @hash: the hash of the target node
  */
 struct ubifs_zbranch {
 	union ubifs_key key;
@@ -737,6 +738,7 @@ struct ubifs_zbranch {
 	int lnum;
 	int offs;
 	int len;
+	u8 hash[UBIFS_MAX_HASH_LEN];
 };
 
 /**
@@ -1773,11 +1775,12 @@ int ubifs_tnc_lookup_dh(struct ubifs_info *c, const union ubifs_key *key,
 int ubifs_tnc_locate(struct ubifs_info *c, const union ubifs_key *key,
 		     void *node, int *lnum, int *offs);
 int ubifs_tnc_add(struct ubifs_info *c, const union ubifs_key *key, int lnum,
-		  int offs, int len);
+		  int offs, int len, const u8 *hash);
 int ubifs_tnc_replace(struct ubifs_info *c, const union ubifs_key *key,
 		      int old_lnum, int old_offs, int lnum, int offs, int len);
 int ubifs_tnc_add_nm(struct ubifs_info *c, const union ubifs_key *key,
-		     int lnum, int offs, int len, const struct fscrypt_name *nm);
+		     int lnum, int offs, int len, const u8 *hash,
+		     const struct fscrypt_name *nm);
 int ubifs_tnc_remove(struct ubifs_info *c, const union ubifs_key *key);
 int ubifs_tnc_remove_nm(struct ubifs_info *c, const union ubifs_key *key,
 			const struct fscrypt_name *nm);
-- 
2.18.0


^ permalink raw reply related	[flat|nested] 49+ messages in thread

* [PATCH 13/25] ubifs: authentication: Add hashes to index nodes
  2018-07-04 12:41 [PATCH 00/25] UBIFS authentication support Sascha Hauer
                   ` (11 preceding siblings ...)
  2018-07-04 12:41 ` [PATCH 12/25] ubifs: Add hashes to the tree node cache Sascha Hauer
@ 2018-07-04 12:41 ` Sascha Hauer
  2018-08-27 19:36   ` Richard Weinberger
  2018-07-04 12:41 ` [PATCH 14/25] ubifs: Add authentication nodes to journal Sascha Hauer
                   ` (11 subsequent siblings)
  24 siblings, 1 reply; 49+ messages in thread
From: Sascha Hauer @ 2018-07-04 12:41 UTC (permalink / raw)
  To: linux-mtd
  Cc: David Gstir, Richard Weinberger, kernel, linux-kernel, Sascha Hauer

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 <s.hauer@pengutronix.de>
---
 fs/ubifs/master.c     |  3 +++
 fs/ubifs/misc.h       |  5 +++--
 fs/ubifs/replay.c     | 29 ++++++++++++++++++-----------
 fs/ubifs/tnc.c        | 13 +++++++++++++
 fs/ubifs/tnc_commit.c | 26 ++++++++++++++++++++++++++
 fs/ubifs/tnc_misc.c   | 16 +++++++++++++++-
 fs/ubifs/ubifs.h      |  4 ++++
 7 files changed, 82 insertions(+), 14 deletions(-)

diff --git a/fs/ubifs/master.c b/fs/ubifs/master.c
index c6a5e39e2ba5..f1a96b50ec68 100644
--- a/fs/ubifs/master.c
+++ b/fs/ubifs/master.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_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;
diff --git a/fs/ubifs/misc.h b/fs/ubifs/misc.h
index caf83d68fb38..8b00418f3197 100644
--- a/fs/ubifs/misc.h
+++ b/fs/ubifs/misc.h
@@ -195,7 +195,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;
 }
 
 /**
@@ -210,7 +211,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);
 }
 
 /**
diff --git a/fs/ubifs/replay.c b/fs/ubifs/replay.c
index 7693556e5bd3..9e9ff753515f 100644
--- a/fs/ubifs/replay.c
+++ b/fs/ubifs/replay.c
@@ -56,6 +56,7 @@ struct replay_entry {
 	int lnum;
 	int offs;
 	int len;
+	u8 hash[UBIFS_MAX_HASH_LEN];
 	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;
 
@@ -351,9 +352,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;
 
@@ -371,6 +372,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);
@@ -399,8 +401,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;
@@ -424,6 +427,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);
@@ -581,6 +585,7 @@ static int replay_bud(struct ubifs_info *c, struct bud_entry *b)
 	 */
 
 	list_for_each_entry(snod, &sleb->nodes, list) {
+		u8 hash[UBIFS_MAX_HASH_LEN];
 		int deletion = 0;
 
 		cond_resched();
@@ -590,6 +595,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;
 
@@ -601,7 +608,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;
@@ -613,7 +620,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;
@@ -627,7 +634,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);
@@ -653,7 +660,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;
diff --git a/fs/ubifs/tnc.c b/fs/ubifs/tnc.c
index a47fced47823..a00809d4fe6f 100644
--- a/fs/ubifs/tnc.c
+++ b/fs/ubifs/tnc.c
@@ -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_err(c, "hash mismatch on node at LEB %d:%d", lnum, offs);
+		return 0;
+	}
+
 	return 1;
 }
 
@@ -1713,6 +1719,13 @@ 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_err(c, "hash mismatch on node at LEB %d:%d",
+			  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);
diff --git a/fs/ubifs/tnc_commit.c b/fs/ubifs/tnc_commit.c
index a9df94ad46a3..3ad78d538885 100644
--- a/fs/ubifs/tnc_commit.c
+++ b/fs/ubifs/tnc_commit.c
@@ -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_MAX_HASH_LEN];
 	int i, err;
 
 	/* Make index node */
@@ -62,6 +63,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 +80,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 +651,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 +846,8 @@ static int write_index(struct ubifs_info *c)
 	}
 
 	while (1) {
+		u8 hash[UBIFS_MAX_HASH_LEN];
+
 		cond_resched();
 
 		znode = cnext;
@@ -857,6 +865,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 +877,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) {
diff --git a/fs/ubifs/tnc_misc.c b/fs/ubifs/tnc_misc.c
index 7e18d459906c..c3c3794c4e5a 100644
--- a/fs/ubifs/tnc_misc.c
+++ b/fs/ubifs/tnc_misc.c
@@ -287,6 +287,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_err(c, "hash mismatch on node at LEB %d:%d", lnum, offs);
+		return err;
+	}
+
 	znode->child_cnt = le16_to_cpu(idx->child_cnt);
 	znode->level = le16_to_cpu(idx->level);
 
@@ -303,13 +309,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 */
@@ -491,5 +498,12 @@ 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_err(c, "hash mismatch on node at LEB %d:%d",
+			  zbr->lnum, zbr->offs);
+		return err;
+	}
+
 	return 0;
 }
diff --git a/fs/ubifs/ubifs.h b/fs/ubifs/ubifs.h
index 24ee376147e1..bf4a99d799a1 100644
--- a/fs/ubifs/ubifs.h
+++ b/fs/ubifs/ubifs.h
@@ -745,6 +745,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
@@ -762,6 +764,8 @@ struct ubifs_zbranch {
 struct ubifs_znode {
 	struct ubifs_znode *parent;
 	struct ubifs_znode *cnext;
+	struct ubifs_znode *cparent;
+	int ciip;
 	unsigned long flags;
 	unsigned long time;
 	int level;
-- 
2.18.0


^ permalink raw reply related	[flat|nested] 49+ messages in thread

* [PATCH 14/25] ubifs: Add authentication nodes to journal
  2018-07-04 12:41 [PATCH 00/25] UBIFS authentication support Sascha Hauer
                   ` (12 preceding siblings ...)
  2018-07-04 12:41 ` [PATCH 13/25] ubifs: authentication: Add hashes to index nodes Sascha Hauer
@ 2018-07-04 12:41 ` Sascha Hauer
  2018-07-08  2:59   ` kbuild test robot
  2018-08-27 20:48   ` Richard Weinberger
  2018-07-04 12:41 ` [PATCH 15/25] ubifs: Add auth nodes to garbage collector journal head Sascha Hauer
                   ` (10 subsequent siblings)
  24 siblings, 2 replies; 49+ messages in thread
From: Sascha Hauer @ 2018-07-04 12:41 UTC (permalink / raw)
  To: linux-mtd
  Cc: David Gstir, Richard Weinberger, kernel, linux-kernel, Sascha Hauer

Nodes that are written to flash can only be authenticated through the
index after the next commit. When a journal replay is necessary the
nodes are not yet referenced by the index and thus can't be
authenticated.

This patch overcomes this situation by creating a hash over all nodes
beginning from the commit start node over the reference node(s) and
the buds themselves. From
time to time we insert authentication nodes. Authentication nodes
contain a HMAC from the current hash state, so that they can be
used to authenticate a journal replay up to the point where the
authentication node is. The hash is continued afterwards
so that theoretically we would only have to check the HMAC of
the last authentication node we find.

Overall we get this picture:

,,,,,,,,
,......,...........................................
,. CS  ,               hash1.----.           hash2.----.
,.  |  ,                    .    |hmac            .    |hmac
,.  v  ,                    .    v                .    v
,.REF#0,-> bud -> bud -> bud.-> auth -> bud -> bud.-> auth ...
,..|...,...........................................
,  |   ,
,  |   ,,,,,,,,,,,,,,,
.  |            hash3,----.
,  |                 ,    |hmac
,  v                 ,    v
, REF#1 -> bud -> bud,-> auth ...
,,,|,,,,,,,,,,,,,,,,,,
   v
  REF#2 -> ...
   |
   V
  ...

Note how hash3 covers CS, REF#0 and REF#1 so that it is not possible to
exchange or skip any reference nodes. Unlike the picture suggests the
auth nodes themselves are not hashed.

With this it is possible for an offline attacker to cut each journal
head or to drop the last reference node(s), but not to skip any journal
heads or to reorder any operations.

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
 fs/ubifs/gc.c      |   3 +-
 fs/ubifs/journal.c | 110 ++++++++++++++++++++++++++++++++++++++-------
 fs/ubifs/log.c     |  17 +++++++
 fs/ubifs/replay.c  |   2 +
 fs/ubifs/super.c   |  10 +++++
 fs/ubifs/ubifs.h   |   8 ++++
 6 files changed, 134 insertions(+), 16 deletions(-)

diff --git a/fs/ubifs/gc.c b/fs/ubifs/gc.c
index a03a47cf880d..ac3a3f7c6a6e 100644
--- a/fs/ubifs/gc.c
+++ b/fs/ubifs/gc.c
@@ -254,7 +254,8 @@ static int sort_nodes(struct ubifs_info *c, struct ubifs_scan_leb *sleb,
 			     snod->type == UBIFS_DATA_NODE ||
 			     snod->type == UBIFS_DENT_NODE ||
 			     snod->type == UBIFS_XENT_NODE ||
-			     snod->type == UBIFS_TRUN_NODE);
+			     snod->type == UBIFS_TRUN_NODE ||
+			     snod->type == UBIFS_AUTH_NODE);
 
 		if (snod->type != UBIFS_INO_NODE  &&
 		    snod->type != UBIFS_DATA_NODE &&
diff --git a/fs/ubifs/journal.c b/fs/ubifs/journal.c
index 55b35bc33c31..4fde75623a3f 100644
--- a/fs/ubifs/journal.c
+++ b/fs/ubifs/journal.c
@@ -228,6 +228,32 @@ static int reserve_space(struct ubifs_info *c, int jhead, int len)
 	return err;
 }
 
+static void ubifs_hash_nodes(struct ubifs_info *c, void *node,
+			     int len, struct shash_desc *hash)
+{
+	int auth_node_size = ubifs_auth_node_sz(c);
+
+	while (1) {
+		const struct ubifs_ch *ch = node;
+		int nodelen = le32_to_cpu(ch->len);
+
+		ubifs_assert(len >= auth_node_size);
+
+		if (len == auth_node_size)
+			break;
+
+		ubifs_assert(len > nodelen);
+		ubifs_assert(ch->magic == cpu_to_le32(UBIFS_NODE_MAGIC));
+
+		ubifs_shash_update(c, hash, (void *)node, nodelen);
+
+		node += ALIGN(nodelen, 8);
+		len -= ALIGN(nodelen, 8);
+	}
+
+	ubifs_prepare_auth_node(c, node, hash);
+}
+
 /**
  * write_head - write data to a journal head.
  * @c: UBIFS file-system description object
@@ -255,6 +281,9 @@ static int write_head(struct ubifs_info *c, int jhead, void *buf, int len,
 	dbg_jnl("jhead %s, LEB %d:%d, len %d",
 		dbg_jhead(jhead), *lnum, *offs, len);
 
+	if (ubifs_authenticated(c))
+		ubifs_hash_nodes(c, buf, len, c->jheads[jhead].log_hash);
+
 	err = ubifs_wbuf_write_nolock(wbuf, buf, len);
 	if (err)
 		return err;
@@ -542,7 +571,9 @@ int ubifs_jnl_update(struct ubifs_info *c, const struct inode *dir,
 
 	len = aligned_dlen + aligned_ilen + UBIFS_INO_NODE_SZ;
 	/* Make sure to also account for extended attributes */
-	len += host_ui->data_len;
+	len += ALIGN(host_ui->data_len, 8);
+
+	len += ubifs_auth_node_sz(c);
 
 	dent = kzalloc(len, GFP_NOFS);
 	if (!dent)
@@ -603,6 +634,7 @@ int ubifs_jnl_update(struct ubifs_info *c, const struct inode *dir,
 	}
 	release_head(c, BASEHD);
 	kfree(dent);
+	ubifs_add_dirt(c, lnum, ubifs_auth_node_sz(c));
 
 	if (deletion) {
 		if (nm->hash)
@@ -677,8 +709,9 @@ int ubifs_jnl_write_data(struct ubifs_info *c, const struct inode *inode,
 			 const union ubifs_key *key, const void *buf, int len)
 {
 	struct ubifs_data_node *data;
-	int err, lnum, offs, compr_type, out_len, compr_len;
+	int err, lnum, offs, compr_type, out_len, compr_len, auth_len;
 	int dlen = COMPRESSED_DATA_NODE_BUF_SZ, allocated = 1;
+	int aligned_dlen;
 	struct ubifs_inode *ui = ubifs_inode(inode);
 	bool encrypted = ubifs_crypt_is_encrypted(inode);
 	u8 hash[UBIFS_MAX_HASH_LEN];
@@ -690,7 +723,9 @@ int ubifs_jnl_write_data(struct ubifs_info *c, const struct inode *inode,
 	if (encrypted)
 		dlen += UBIFS_CIPHER_BLOCK_SIZE;
 
-	data = kmalloc(dlen, GFP_NOFS | __GFP_NOWARN);
+	auth_len = ubifs_auth_node_sz(c);
+
+	data = kmalloc(dlen + auth_len, GFP_NOFS | __GFP_NOWARN);
 	if (!data) {
 		/*
 		 * Fall-back to the write reserve buffer. Note, we might be
@@ -729,15 +764,16 @@ int ubifs_jnl_write_data(struct ubifs_info *c, const struct inode *inode,
 	}
 
 	dlen = UBIFS_DATA_NODE_SZ + out_len;
+	aligned_dlen = ALIGN(dlen, 8);
 	data->compr_type = cpu_to_le16(compr_type);
 
 	/* Make reservation before allocating sequence numbers */
-	err = make_reservation(c, DATAHD, dlen);
+	err = make_reservation(c, DATAHD, aligned_dlen + auth_len);
 	if (err)
 		goto out_free;
 
 	ubifs_prepare_node(c, data, dlen, 0);
-	err = write_head(c, DATAHD, data, dlen, &lnum, &offs, 0);
+	err = write_head(c, DATAHD, data, aligned_dlen + auth_len, &lnum, &offs, 0);
 	if (err)
 		goto out_release;
 
@@ -745,6 +781,7 @@ int ubifs_jnl_write_data(struct ubifs_info *c, const struct inode *inode,
 
 	ubifs_wbuf_add_ino_nolock(&c->jheads[DATAHD].wbuf, key_inum(c, key));
 	release_head(c, DATAHD);
+	ubifs_add_dirt(c, lnum, ubifs_auth_node_sz(c));
 
 	err = ubifs_tnc_add(c, key, lnum, offs, dlen, hash);
 	if (err)
@@ -784,7 +821,8 @@ int ubifs_jnl_write_inode(struct ubifs_info *c, const struct inode *inode)
 	int err, lnum, offs;
 	struct ubifs_ino_node *ino;
 	struct ubifs_inode *ui = ubifs_inode(inode);
-	int sync = 0, len = UBIFS_INO_NODE_SZ, last_reference = !inode->i_nlink;
+	int sync = 0, len, ilen = UBIFS_INO_NODE_SZ, last_reference = !inode->i_nlink;
+	int aligned_ilen;
 	u8 hash[UBIFS_MAX_HASH_LEN];
 
 	dbg_jnl("ino %lu, nlink %u", inode->i_ino, inode->i_nlink);
@@ -794,9 +832,14 @@ int ubifs_jnl_write_inode(struct ubifs_info *c, const struct inode *inode)
 	 * need to synchronize the write-buffer either.
 	 */
 	if (!last_reference) {
-		len += ui->data_len;
+		ilen += ui->data_len;
 		sync = IS_SYNC(inode);
 	}
+
+	aligned_ilen = ALIGN(ilen, 8);
+
+	len = aligned_ilen + ubifs_auth_node_sz(c);
+
 	ino = kmalloc(len, GFP_NOFS);
 	if (!ino)
 		return -ENOMEM;
@@ -816,17 +859,20 @@ int ubifs_jnl_write_inode(struct ubifs_info *c, const struct inode *inode)
 					  inode->i_ino);
 	release_head(c, BASEHD);
 
+	if (ubifs_authenticated(c))
+		ubifs_add_dirt(c, lnum, ubifs_auth_node_sz(c));
+
 	if (last_reference) {
 		err = ubifs_tnc_remove_ino(c, inode->i_ino);
 		if (err)
 			goto out_ro;
 		ubifs_delete_orphan(c, inode->i_ino);
-		err = ubifs_add_dirt(c, lnum, len);
+		err = ubifs_add_dirt(c, lnum, ilen);
 	} else {
 		union ubifs_key key;
 
 		ino_key_init(c, &key, inode->i_ino);
-		err = ubifs_tnc_add(c, &key, lnum, offs, len, hash);
+		err = ubifs_tnc_add(c, &key, lnum, offs, ilen, hash);
 	}
 	if (err)
 		goto out_ro;
@@ -955,6 +1001,8 @@ int ubifs_jnl_xrename(struct ubifs_info *c, const struct inode *fst_dir,
 	if (twoparents)
 		len += plen;
 
+	len += ubifs_auth_node_sz(c);
+
 	dent1 = kzalloc(len, GFP_NOFS);
 	if (!dent1)
 		return -ENOMEM;
@@ -1014,6 +1062,9 @@ int ubifs_jnl_xrename(struct ubifs_info *c, const struct inode *fst_dir,
 	}
 	release_head(c, BASEHD);
 
+	if (ubifs_authenticated(c))
+		ubifs_add_dirt(c, lnum, ubifs_auth_node_sz(c));
+
 	dent_key_init(c, &key, snd_dir->i_ino, snd_nm);
 	err = ubifs_tnc_add_nm(c, &key, lnum, offs, dlen1, hash_dent1, snd_nm);
 	if (err)
@@ -1115,6 +1166,9 @@ int ubifs_jnl_rename(struct ubifs_info *c, const struct inode *old_dir,
 	len = aligned_dlen1 + aligned_dlen2 + ALIGN(ilen, 8) + ALIGN(plen, 8);
 	if (move)
 		len += plen;
+
+	len += ubifs_auth_node_sz(c);
+
 	dent = kzalloc(len, GFP_NOFS);
 	if (!dent)
 		return -ENOMEM;
@@ -1198,6 +1252,9 @@ int ubifs_jnl_rename(struct ubifs_info *c, const struct inode *old_dir,
 	}
 	release_head(c, BASEHD);
 
+	if (ubifs_authenticated(c))
+		ubifs_add_dirt(c, lnum, ubifs_auth_node_sz(c));
+
 	dent_key_init(c, &key, new_dir->i_ino, new_nm);
 	err = ubifs_tnc_add_nm(c, &key, lnum, offs, dlen1, hash_dent1, new_nm);
 	if (err)
@@ -1356,6 +1413,7 @@ int ubifs_jnl_truncate(struct ubifs_info *c, const struct inode *inode,
 	struct ubifs_trun_node *trun;
 	struct ubifs_data_node *uninitialized_var(dn);
 	int err, dlen, len, lnum, offs, bit, sz, sync = IS_SYNC(inode);
+	int aligned_dlen;
 	struct ubifs_inode *ui = ubifs_inode(inode);
 	ino_t inum = inode->i_ino;
 	unsigned int blk;
@@ -1370,6 +1428,9 @@ int ubifs_jnl_truncate(struct ubifs_info *c, const struct inode *inode,
 
 	sz = UBIFS_TRUN_NODE_SZ + UBIFS_INO_NODE_SZ +
 	     UBIFS_MAX_DATA_NODE_SZ * WORST_COMPR_FACTOR;
+
+	sz += ubifs_auth_node_sz(c);
+
 	ino = kmalloc(sz, GFP_NOFS);
 	if (!ino)
 		return -ENOMEM;
@@ -1404,10 +1465,15 @@ int ubifs_jnl_truncate(struct ubifs_info *c, const struct inode *inode,
 		}
 	}
 
+	aligned_dlen = ALIGN(dlen, 8);
+
 	/* Must make reservation before allocating sequence numbers */
 	len = UBIFS_TRUN_NODE_SZ + UBIFS_INO_NODE_SZ;
 	if (dlen)
-		len += dlen;
+		len += aligned_dlen;
+
+	len += ubifs_auth_node_sz(c);
+
 	err = make_reservation(c, BASEHD, len);
 	if (err)
 		goto out_free;
@@ -1428,6 +1494,9 @@ int ubifs_jnl_truncate(struct ubifs_info *c, const struct inode *inode,
 		ubifs_wbuf_add_ino_nolock(&c->jheads[BASEHD].wbuf, inum);
 	release_head(c, BASEHD);
 
+	if (ubifs_authenticated(c))
+		ubifs_add_dirt(c, lnum, ubifs_auth_node_sz(c));
+
 	if (dlen) {
 		sz = offs + UBIFS_INO_NODE_SZ + UBIFS_TRUN_NODE_SZ;
 		err = ubifs_tnc_add(c, &key, lnum, sz, dlen, hash_dn);
@@ -1491,7 +1560,7 @@ int ubifs_jnl_delete_xattr(struct ubifs_info *c, const struct inode *host,
 			   const struct inode *inode,
 			   const struct fscrypt_name *nm)
 {
-	int err, xlen, hlen, len, lnum, xent_offs, aligned_xlen;
+	int err, xlen, hlen, len, lnum, xent_offs, aligned_xlen, tlen;
 	struct ubifs_dent_node *xent;
 	struct ubifs_ino_node *ino;
 	union ubifs_key xent_key, key1, key2;
@@ -1511,12 +1580,14 @@ int ubifs_jnl_delete_xattr(struct ubifs_info *c, const struct inode *host,
 	hlen = host_ui->data_len + UBIFS_INO_NODE_SZ;
 	len = aligned_xlen + UBIFS_INO_NODE_SZ + ALIGN(hlen, 8);
 
-	xent = kzalloc(len, GFP_NOFS);
+	tlen = len + ubifs_auth_node_sz(c);
+
+	xent = kzalloc(tlen, GFP_NOFS);
 	if (!xent)
 		return -ENOMEM;
 
 	/* Make reservation before allocating sequence numbers */
-	err = make_reservation(c, BASEHD, len);
+	err = make_reservation(c, BASEHD, tlen);
 	if (err) {
 		kfree(xent);
 		return err;
@@ -1539,10 +1610,13 @@ int ubifs_jnl_delete_xattr(struct ubifs_info *c, const struct inode *host,
 	pack_inode(c, ino, host, 1);
 	ubifs_node_calc_hash(c, ino, hash);
 
-	err = write_head(c, BASEHD, xent, len, &lnum, &xent_offs, sync);
+	err = write_head(c, BASEHD, xent, tlen, &lnum, &xent_offs, sync);
 	if (!sync && !err)
 		ubifs_wbuf_add_ino_nolock(&c->jheads[BASEHD].wbuf, host->i_ino);
 	release_head(c, BASEHD);
+
+	if (ubifs_authenticated(c))
+		ubifs_add_dirt(c, lnum, ubifs_auth_node_sz(c));
 	kfree(xent);
 	if (err)
 		goto out_ro;
@@ -1605,6 +1679,7 @@ int ubifs_jnl_change_xattr(struct ubifs_info *c, const struct inode *inode,
 {
 	int err, len1, len2, aligned_len, aligned_len1, lnum, offs;
 	struct ubifs_inode *host_ui = ubifs_inode(host);
+	struct ubifs_inode *ui = ubifs_inode(inode);
 	struct ubifs_ino_node *ino;
 	union ubifs_key key;
 	int sync = IS_DIRSYNC(host);
@@ -1617,10 +1692,12 @@ int ubifs_jnl_change_xattr(struct ubifs_info *c, const struct inode *inode,
 	ubifs_assert(mutex_is_locked(&host_ui->ui_mutex));
 
 	len1 = UBIFS_INO_NODE_SZ + host_ui->data_len;
-	len2 = UBIFS_INO_NODE_SZ + ubifs_inode(inode)->data_len;
+	len2 = UBIFS_INO_NODE_SZ + ui->data_len;
 	aligned_len1 = ALIGN(len1, 8);
 	aligned_len = aligned_len1 + ALIGN(len2, 8);
 
+	aligned_len += ubifs_auth_node_sz(c);
+
 	ino = kzalloc(aligned_len, GFP_NOFS);
 	if (!ino)
 		return -ENOMEM;
@@ -1646,6 +1723,9 @@ int ubifs_jnl_change_xattr(struct ubifs_info *c, const struct inode *inode,
 	if (err)
 		goto out_ro;
 
+	if (ubifs_authenticated(c))
+		ubifs_add_dirt(c, lnum, ubifs_auth_node_sz(c));
+
 	ino_key_init(c, &key, host->i_ino);
 	err = ubifs_tnc_add(c, &key, lnum, offs, len1, hash_host);
 	if (err)
diff --git a/fs/ubifs/log.c b/fs/ubifs/log.c
index 7cffa120a750..311757b2dc1a 100644
--- a/fs/ubifs/log.c
+++ b/fs/ubifs/log.c
@@ -236,6 +236,7 @@ int ubifs_add_bud_to_log(struct ubifs_info *c, int jhead, int lnum, int offs)
 	bud->lnum = lnum;
 	bud->start = offs;
 	bud->jhead = jhead;
+	bud->log_hash = NULL;
 
 	ref->ch.node_type = UBIFS_REF_NODE;
 	ref->lnum = cpu_to_le32(bud->lnum);
@@ -275,6 +276,12 @@ int ubifs_add_bud_to_log(struct ubifs_info *c, int jhead, int lnum, int offs)
 	if (err)
 		goto out_unlock;
 
+	ubifs_shash_update(c, c->log_hash, (void *)ref, UBIFS_REF_NODE_SZ);
+
+	err = ubifs_shash_copy_state(c, c->log_hash, c->jheads[jhead].log_hash);
+	if (err)
+		goto out_unlock;
+
 	c->lhead_offs += c->ref_node_alsz;
 
 	ubifs_add_bud(c, bud);
@@ -377,6 +384,11 @@ int ubifs_log_start_commit(struct ubifs_info *c, int *ltail_lnum)
 	cs->cmt_no = cpu_to_le64(c->cmt_no);
 	ubifs_prepare_node(c, cs, UBIFS_CS_NODE_SZ, 0);
 
+	if (c->authenticated) {
+		crypto_shash_init(c->log_hash);
+		crypto_shash_update(c->log_hash, (void *)cs, UBIFS_CS_NODE_SZ);
+	}
+
 	/*
 	 * Note, we do not lock 'c->log_mutex' because this is the commit start
 	 * phase and we are exclusively using the log. And we do not lock
@@ -402,6 +414,10 @@ int ubifs_log_start_commit(struct ubifs_info *c, int *ltail_lnum)
 
 		ubifs_prepare_node(c, ref, UBIFS_REF_NODE_SZ, 0);
 		len += UBIFS_REF_NODE_SZ;
+
+		ubifs_shash_update(c, c->log_hash, (void *)ref,
+				   UBIFS_REF_NODE_SZ);
+		ubifs_shash_copy_state(c, c->log_hash, c->jheads[i].log_hash);
 	}
 
 	ubifs_pad(c, buf + len, ALIGN(len, c->min_io_size) - len);
@@ -516,6 +532,7 @@ int ubifs_log_post_commit(struct ubifs_info *c, int old_ltail_lnum)
 		if (err)
 			return err;
 		list_del(&bud->list);
+		kfree(bud->log_hash);
 		kfree(bud);
 	}
 	mutex_lock(&c->log_mutex);
diff --git a/fs/ubifs/replay.c b/fs/ubifs/replay.c
index 9e9ff753515f..07a66ae90e89 100644
--- a/fs/ubifs/replay.c
+++ b/fs/ubifs/replay.c
@@ -665,6 +665,8 @@ static int replay_bud(struct ubifs_info *c, struct bud_entry *b)
 					  old_size, new_size);
 			break;
 		}
+		case UBIFS_AUTH_NODE:
+			break;
 		default:
 			ubifs_err(c, "unexpected node type %d in bud LEB %d:%d",
 				  snod->type, lnum, snod->offs);
diff --git a/fs/ubifs/super.c b/fs/ubifs/super.c
index 6b2d80391111..49de06921427 100644
--- a/fs/ubifs/super.c
+++ b/fs/ubifs/super.c
@@ -815,6 +815,9 @@ static int alloc_wbufs(struct ubifs_info *c)
 		c->jheads[i].wbuf.sync_callback = &bud_wbuf_callback;
 		c->jheads[i].wbuf.jhead = i;
 		c->jheads[i].grouped = 1;
+		c->jheads[i].log_hash = ubifs_hash_get_desc(c);
+		if (IS_ERR(c->jheads[i].log_hash))
+			goto out;
 	}
 
 	/*
@@ -825,6 +828,12 @@ static int alloc_wbufs(struct ubifs_info *c)
 	c->jheads[GCHD].grouped = 0;
 
 	return 0;
+
+out:
+	while (i--)
+		kfree(c->jheads[i].log_hash);
+
+	return err;
 }
 
 /**
@@ -839,6 +848,7 @@ static void free_wbufs(struct ubifs_info *c)
 		for (i = 0; i < c->jhead_cnt; i++) {
 			kfree(c->jheads[i].wbuf.buf);
 			kfree(c->jheads[i].wbuf.inodes);
+			kfree(c->jheads[i].log_hash);
 		}
 		kfree(c->jheads);
 		c->jheads = NULL;
diff --git a/fs/ubifs/ubifs.h b/fs/ubifs/ubifs.h
index bf4a99d799a1..5390d087da3a 100644
--- a/fs/ubifs/ubifs.h
+++ b/fs/ubifs/ubifs.h
@@ -697,6 +697,7 @@ struct ubifs_wbuf {
  * @jhead: journal head number this bud belongs to
  * @list: link in the list buds belonging to the same journal head
  * @rb: link in the tree of all buds
+ * @log_hash: the log hash from the commit start node up to this bud
  */
 struct ubifs_bud {
 	int lnum;
@@ -704,6 +705,7 @@ struct ubifs_bud {
 	int jhead;
 	struct list_head list;
 	struct rb_node rb;
+	struct shash_desc *log_hash;
 };
 
 /**
@@ -711,6 +713,7 @@ struct ubifs_bud {
  * @wbuf: head's write-buffer
  * @buds_list: list of bud LEBs belonging to this journal head
  * @grouped: non-zero if UBIFS groups nodes when writing to this journal head
+ * @log_hash: the log hash from the commit start node up to this journal head
  *
  * Note, the @buds list is protected by the @c->buds_lock.
  */
@@ -718,6 +721,7 @@ struct ubifs_jhead {
 	struct ubifs_wbuf wbuf;
 	struct list_head buds_list;
 	unsigned int grouped:1;
+	struct shash_desc *log_hash;
 };
 
 /**
@@ -1215,6 +1219,8 @@ struct ubifs_debug_info;
  * @auth_key_name: the authentication key name
  * @auth_hash_name: the name of the hash algorithm used for authentication
  * @auth_hash_algo: the authentication hash used for this fs
+ * @log_hash: the log hash from the commit start node up to the latest reference
+ *            node.
  *
  * @empty: %1 if the UBI device is empty
  * @need_recovery: %1 if the file-system needs recovery
@@ -1456,6 +1462,8 @@ struct ubifs_info {
 	char *auth_hash_name;
 	enum hash_algo auth_hash_algo;
 
+	struct shash_desc *log_hash;
+
 	/* The below fields are used only during mounting and re-mounting */
 	unsigned int empty:1;
 	unsigned int need_recovery:1;
-- 
2.18.0


^ permalink raw reply related	[flat|nested] 49+ messages in thread

* [PATCH 15/25] ubifs: Add auth nodes to garbage collector journal head
  2018-07-04 12:41 [PATCH 00/25] UBIFS authentication support Sascha Hauer
                   ` (13 preceding siblings ...)
  2018-07-04 12:41 ` [PATCH 14/25] ubifs: Add authentication nodes to journal Sascha Hauer
@ 2018-07-04 12:41 ` Sascha Hauer
  2018-08-27 20:51   ` Richard Weinberger
  2018-07-04 12:41 ` [PATCH 16/25] ubifs: authenticate replayed journal Sascha Hauer
                   ` (9 subsequent siblings)
  24 siblings, 1 reply; 49+ messages in thread
From: Sascha Hauer @ 2018-07-04 12:41 UTC (permalink / raw)
  To: linux-mtd
  Cc: David Gstir, Richard Weinberger, kernel, linux-kernel, Sascha Hauer

To be able to authenticate the garbage collector journal head add
authentication nodes to the buds the garbage collector creates.

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
 fs/ubifs/gc.c | 37 ++++++++++++++++++++++++++++++++++---
 1 file changed, 34 insertions(+), 3 deletions(-)

diff --git a/fs/ubifs/gc.c b/fs/ubifs/gc.c
index ac3a3f7c6a6e..8feeeb12b6ed 100644
--- a/fs/ubifs/gc.c
+++ b/fs/ubifs/gc.c
@@ -365,12 +365,13 @@ static int move_nodes(struct ubifs_info *c, struct ubifs_scan_leb *sleb)
 
 	/* Write nodes to their new location. Use the first-fit strategy */
 	while (1) {
-		int avail;
+		int avail, moved = 0;
 		struct ubifs_scan_node *snod, *tmp;
 
 		/* Move data nodes */
 		list_for_each_entry_safe(snod, tmp, &sleb->nodes, list) {
-			avail = c->leb_size - wbuf->offs - wbuf->used;
+			avail = c->leb_size - wbuf->offs - wbuf->used -
+					ubifs_auth_node_sz(c);
 			if  (snod->len > avail)
 				/*
 				 * Do not skip data nodes in order to optimize
@@ -378,14 +379,19 @@ static int move_nodes(struct ubifs_info *c, struct ubifs_scan_leb *sleb)
 				 */
 				break;
 
+			ubifs_shash_update(c, c->jheads[GCHD].log_hash,
+					   snod->node, snod->len);
+
 			err = move_node(c, sleb, snod, wbuf);
 			if (err)
 				goto out;
+			moved = 1;
 		}
 
 		/* Move non-data nodes */
 		list_for_each_entry_safe(snod, tmp, &nondata, list) {
-			avail = c->leb_size - wbuf->offs - wbuf->used;
+			avail = c->leb_size - wbuf->offs - wbuf->used -
+					ubifs_auth_node_sz(c);
 			if (avail < min)
 				break;
 
@@ -403,7 +409,32 @@ static int move_nodes(struct ubifs_info *c, struct ubifs_scan_leb *sleb)
 				continue;
 			}
 
+			ubifs_shash_update(c, c->jheads[GCHD].log_hash,
+					   snod->node, snod->len);
+
 			err = move_node(c, sleb, snod, wbuf);
+			if (err)
+				goto out;
+			moved = 1;
+		}
+
+		if (ubifs_authenticated(c) && moved) {
+			struct ubifs_auth_node *auth;
+
+			auth = kmalloc(ubifs_auth_node_sz(c), GFP_NOFS);
+			if (!auth) {
+				err = -ENOMEM;
+				goto out;
+			}
+
+			ubifs_prepare_auth_node(c, auth,
+						c->jheads[GCHD].log_hash);
+
+			err = ubifs_wbuf_write_nolock(wbuf, auth,
+						      ubifs_auth_node_sz(c));
+
+			kfree(auth);
+
 			if (err)
 				goto out;
 		}
-- 
2.18.0


^ permalink raw reply related	[flat|nested] 49+ messages in thread

* [PATCH 16/25] ubifs: authenticate replayed journal
  2018-07-04 12:41 [PATCH 00/25] UBIFS authentication support Sascha Hauer
                   ` (14 preceding siblings ...)
  2018-07-04 12:41 ` [PATCH 15/25] ubifs: Add auth nodes to garbage collector journal head Sascha Hauer
@ 2018-07-04 12:41 ` Sascha Hauer
  2018-07-08  6:08   ` kbuild test robot
  2018-08-27 21:16   ` Richard Weinberger
  2018-07-04 12:41 ` [PATCH 17/25] ubifs: authentication: authenticate LPT Sascha Hauer
                   ` (8 subsequent siblings)
  24 siblings, 2 replies; 49+ messages in thread
From: Sascha Hauer @ 2018-07-04 12:41 UTC (permalink / raw)
  To: linux-mtd
  Cc: David Gstir, Richard Weinberger, kernel, linux-kernel, Sascha Hauer

Make sure that during replay all buds can be authenticated. To do
this we calculate the hash chain until we find an authentication
node and check the HMAC in that node against the current status
of the hash chain.

After a power cut it can happen that some nodes have been written, but
not yet the authentication node for them. These nodes have to be
discarded during replay.

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
 fs/ubifs/replay.c | 116 +++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 114 insertions(+), 2 deletions(-)

diff --git a/fs/ubifs/replay.c b/fs/ubifs/replay.c
index 07a66ae90e89..45da17d1a74e 100644
--- a/fs/ubifs/replay.c
+++ b/fs/ubifs/replay.c
@@ -34,6 +34,8 @@
 
 #include "ubifs.h"
 #include <linux/list_sort.h>
+#include <crypto/hash.h>
+#include <crypto/algapi.h>
 
 /**
  * struct replay_entry - replay list entry.
@@ -530,6 +532,79 @@ static int is_last_bud(struct ubifs_info *c, struct ubifs_bud *bud)
 	return data == 0xFFFFFFFF;
 }
 
+/**
+ * authenticate_sleb - authenticate one scan LEB
+ * @c: UBIFS file-system description object
+ * @sleb: the scan LEB to authenticate
+ * @log_hash:
+ * @is_last: if true, this is is the last LEB
+ *
+ * This function iterates over the buds of a single LEB authenticating all buds
+ * with the authentication nodes on this LEB. Authentication nodes are written
+ * after some buds and contain a HMAC covering the authentication node itself
+ * and the buds between the last authentication node and the current
+ * authentication node. It can happen that the last buds cannot be authenticated
+ * because a powercut happened when some nodes were written but not the
+ * corresponding authentication node. This function returns the number of nodes
+ * that could be authenticated or a negative error code.
+ */
+static int authenticate_sleb(struct ubifs_info *c, struct ubifs_scan_leb *sleb,
+			     struct shash_desc *log_hash, int is_last)
+{
+	int n_not_auth = 0;
+	struct ubifs_scan_node *snod;
+	int n_nodes = 0;
+	int err;
+
+	if (!ubifs_authenticated(c))
+		return sleb->nodes_cnt;
+
+	list_for_each_entry(snod, &sleb->nodes, list) {
+
+		n_nodes++;
+
+		if (snod->type == UBIFS_AUTH_NODE) {
+			struct ubifs_auth_node *auth = snod->node;
+			SHASH_DESC_ON_STACK(hash_desc, c->hash_tfm);
+			u8 hash[crypto_shash_descsize(c->hash_tfm)];
+			SHASH_DESC_ON_STACK(hmac_desc, c->hmac_tfm);
+			u8 hmac[c->hmac_desc_len];
+
+			hash_desc->tfm = c->hash_tfm;
+			hash_desc->flags = CRYPTO_TFM_REQ_MAY_SLEEP;
+
+			ubifs_shash_copy_state(c, log_hash, hash_desc);
+			crypto_shash_final(hash_desc, hash);
+
+			hmac_desc->tfm = c->hmac_tfm;
+			hmac_desc->flags = CRYPTO_TFM_REQ_MAY_SLEEP;
+			crypto_shash_digest(hmac_desc, hash, c->hash_len, hmac);
+
+			err = ubifs_check_hmac(c, auth->hmac, hmac);
+			if (err) {
+				err = -EPERM;
+				goto out;
+			}
+			n_not_auth = 0;
+		} else {
+			crypto_shash_update(log_hash, snod->node, snod->len);
+			n_not_auth++;
+		}
+	}
+
+	/*
+	 * A powercut can happen when some nodes were written, but not yet
+	 * the corresponding authentication node. This may only happen on
+	 * the last bud though.
+	 */
+	if (n_not_auth && !is_last)
+		err = -EPERM;
+	else
+		err = 0;
+out:
+	return err ? err : n_nodes - n_not_auth;
+}
+
 /**
  * replay_bud - replay a bud logical eraseblock.
  * @c: UBIFS file-system description object
@@ -543,6 +618,7 @@ static int replay_bud(struct ubifs_info *c, struct bud_entry *b)
 {
 	int is_last = is_last_bud(c, b->bud);
 	int err = 0, used = 0, lnum = b->bud->lnum, offs = b->bud->start;
+	int n_nodes, n = 0;
 	struct ubifs_scan_leb *sleb;
 	struct ubifs_scan_node *snod;
 
@@ -562,6 +638,15 @@ static int replay_bud(struct ubifs_info *c, struct bud_entry *b)
 	if (IS_ERR(sleb))
 		return PTR_ERR(sleb);
 
+	n_nodes = authenticate_sleb(c, sleb, b->bud->log_hash, is_last);
+	if (n_nodes < 0) {
+		err = n_nodes;
+		goto out;
+	}
+
+	ubifs_shash_copy_state(c, b->bud->log_hash,
+			       c->jheads[b->bud->jhead].log_hash);
+
 	/*
 	 * The bud does not have to start from offset zero - the beginning of
 	 * the 'lnum' LEB may contain previously committed data. One of the
@@ -675,6 +760,10 @@ static int replay_bud(struct ubifs_info *c, struct bud_entry *b)
 		}
 		if (err)
 			goto out;
+
+		n++;
+		if (n == n_nodes)
+			break;
 	}
 
 	ubifs_assert(ubifs_search_bud(c, lnum));
@@ -753,6 +842,7 @@ static int add_replay_bud(struct ubifs_info *c, int lnum, int offs, int jhead,
 {
 	struct ubifs_bud *bud;
 	struct bud_entry *b;
+	int err;
 
 	dbg_mnt("add replay bud LEB %d:%d, head %d", lnum, offs, jhead);
 
@@ -762,13 +852,21 @@ static int add_replay_bud(struct ubifs_info *c, int lnum, int offs, int jhead,
 
 	b = kmalloc(sizeof(struct bud_entry), GFP_KERNEL);
 	if (!b) {
-		kfree(bud);
-		return -ENOMEM;
+		err = -ENOMEM;
+		goto out;
 	}
 
 	bud->lnum = lnum;
 	bud->start = offs;
 	bud->jhead = jhead;
+	bud->log_hash = ubifs_hash_get_desc(c);
+	if (IS_ERR(bud->log_hash)) {
+		err = PTR_ERR(bud->log_hash);
+		goto out;
+	}
+
+	ubifs_shash_copy_state(c, c->log_hash, bud->log_hash);
+
 	ubifs_add_bud(c, bud);
 
 	b->bud = bud;
@@ -776,6 +874,11 @@ static int add_replay_bud(struct ubifs_info *c, int lnum, int offs, int jhead,
 	list_add_tail(&b->list, &c->replay_buds);
 
 	return 0;
+out:
+	kfree(bud);
+	kfree(b);
+
+	return err;
 }
 
 /**
@@ -881,6 +984,12 @@ static int replay_log_leb(struct ubifs_info *c, int lnum, int offs, void *sbuf)
 
 		c->cs_sqnum = le64_to_cpu(node->ch.sqnum);
 		dbg_mnt("commit start sqnum %llu", c->cs_sqnum);
+
+		if (c->authenticated) {
+			crypto_shash_init(c->log_hash);
+			crypto_shash_update(c->log_hash, (void *)node,
+					    UBIFS_CS_NODE_SZ);
+		}
 	}
 
 	if (snod->sqnum < c->cs_sqnum) {
@@ -928,6 +1037,9 @@ static int replay_log_leb(struct ubifs_info *c, int lnum, int offs, void *sbuf)
 			if (err)
 				goto out_dump;
 
+			ubifs_shash_update(c, c->log_hash, (void *)ref,
+					   UBIFS_REF_NODE_SZ);
+
 			err = add_replay_bud(c, le32_to_cpu(ref->lnum),
 					     le32_to_cpu(ref->offs),
 					     le32_to_cpu(ref->jhead),
-- 
2.18.0


^ permalink raw reply related	[flat|nested] 49+ messages in thread

* [PATCH 17/25] ubifs: authentication: authenticate LPT
  2018-07-04 12:41 [PATCH 00/25] UBIFS authentication support Sascha Hauer
                   ` (15 preceding siblings ...)
  2018-07-04 12:41 ` [PATCH 16/25] ubifs: authenticate replayed journal Sascha Hauer
@ 2018-07-04 12:41 ` Sascha Hauer
  2018-07-04 12:41 ` [PATCH 18/25] ubfis: authentication: authenticate master node Sascha Hauer
                   ` (7 subsequent siblings)
  24 siblings, 0 replies; 49+ messages in thread
From: Sascha Hauer @ 2018-07-04 12:41 UTC (permalink / raw)
  To: linux-mtd
  Cc: David Gstir, Richard Weinberger, kernel, linux-kernel, Sascha Hauer

The LPT needs to be authenticated aswell. Since the LPT is only written
during commit it is enough to authenticate the whole LPT with a single
hash which is stored in the master node. Only the leaf nodes (pnodes)
are hashed which makes the implementation much simpler than it would be
to hash the complete LPT.

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
 fs/ubifs/lpt.c        | 127 ++++++++++++++++++++++++++++++++++++++++++
 fs/ubifs/lpt_commit.c |   4 ++
 fs/ubifs/ubifs.h      |   1 +
 3 files changed, 132 insertions(+)

diff --git a/fs/ubifs/lpt.c b/fs/ubifs/lpt.c
index cde7b9484157..2c440bb31d69 100644
--- a/fs/ubifs/lpt.c
+++ b/fs/ubifs/lpt.c
@@ -1633,6 +1633,129 @@ struct ubifs_lprops *ubifs_lpt_lookup_dirty(struct ubifs_info *c, int lnum)
 	return &pnode->lprops[iip];
 }
 
+/**
+ * ubifs_lpt_calc_hash - Calculate hash of the LPT pnodes
+ * @c: UBIFS file-system description object
+ * @hash: the returned hash of the LPT pnodes
+ *
+ * This function iterates over the LPT pnodes and creates a hash over them.
+ * Returns 0 for success or a negative error code otherwise.
+ */
+int ubifs_lpt_calc_hash(struct ubifs_info *c, u8 *hash)
+{
+	struct ubifs_nnode *nnode, *nn;
+	struct ubifs_cnode *cnode;
+	struct shash_desc *desc;
+	int iip = 0, i;
+	int bufsiz = max_t(int, c->nnode_sz, c->pnode_sz);
+	void *buf;
+	int err;
+
+	if (!ubifs_authenticated(c))
+		return 0;
+
+	desc = ubifs_hash_get_desc(c);
+	if (IS_ERR(desc))
+		return PTR_ERR(desc);
+
+	buf = kmalloc(bufsiz, GFP_NOFS);
+	if (!buf) {
+		err = -ENOMEM;
+		goto out;
+	}
+
+	if (!c->nroot) {
+		err = ubifs_read_nnode(c, NULL, 0);
+		if (err)
+			return err;
+	}
+
+	cnode = (struct ubifs_cnode *)c->nroot;
+
+	while (cnode) {
+		nnode = cnode->parent;
+		nn = (struct ubifs_nnode *)cnode;
+		if (cnode->level > 1) {
+			while (iip < UBIFS_LPT_FANOUT) {
+				if (nn->nbranch[iip].lnum == 0) {
+					/* Go right */
+					iip++;
+					continue;
+				}
+
+				nnode = ubifs_get_nnode(c, nn, iip);
+				if (IS_ERR(nnode)) {
+					err = PTR_ERR(nnode);
+					goto out;
+				}
+
+				/* Go down */
+				iip = 0;
+				cnode = (struct ubifs_cnode *)nnode;
+				break;
+			}
+			if (iip < UBIFS_LPT_FANOUT)
+				continue;
+		} else {
+			struct ubifs_pnode *pnode;
+
+			for (i = 0; i < UBIFS_LPT_FANOUT; i++) {
+				if (nn->nbranch[i].lnum == 0)
+					continue;
+				pnode = ubifs_get_pnode(c, nn, i);
+				if (IS_ERR(pnode)) {
+					err = PTR_ERR(pnode);
+					goto out;
+				}
+
+				ubifs_pack_pnode(c, buf, pnode);
+				ubifs_shash_update(c, desc, buf, c->pnode_sz);
+			}
+		}
+		/* Go up and to the right */
+		iip = cnode->iip + 1;
+		cnode = (struct ubifs_cnode *)nnode;
+	}
+
+	err = 0;
+	ubifs_shash_final(c, desc, hash);
+out:
+	kfree(desc);
+	kfree(buf);
+
+	return err;
+}
+
+/**
+ * lpt_check_hash - check the hash of the LPT.
+ * @c: UBIFS file-system description object
+ *
+ * This function calculates a hash over all pnodes in the LPT and compares it with
+ * the hash stored in the master node. Returns %0 on success and a negative error
+ * code on failure.
+ */
+static int lpt_check_hash(struct ubifs_info *c)
+{
+	int err;
+	u8 hash[UBIFS_MAX_HASH_LEN];
+
+	if (!ubifs_authenticated(c))
+		return 0;
+
+	err = ubifs_lpt_calc_hash(c, hash);
+	if (err)
+		return err;
+
+	if (ubifs_check_hash(c, c->mst_node->hash_lpt, hash)) {
+		err = -EPERM;
+		ubifs_err(c, "Failed to authenticate LPT");
+	} else {
+		err = 0;
+	}
+
+	return err;
+}
+
 /**
  * lpt_init_rd - initialize the LPT for reading.
  * @c: UBIFS file-system description object
@@ -1674,6 +1797,10 @@ static int lpt_init_rd(struct ubifs_info *c)
 	if (err)
 		return err;
 
+	err = lpt_check_hash(c);
+	if (err)
+		return err;
+
 	dbg_lp("space_bits %d", c->space_bits);
 	dbg_lp("lpt_lnum_bits %d", c->lpt_lnum_bits);
 	dbg_lp("lpt_offs_bits %d", c->lpt_offs_bits);
diff --git a/fs/ubifs/lpt_commit.c b/fs/ubifs/lpt_commit.c
index 14e72d09b6b3..817586f4962c 100644
--- a/fs/ubifs/lpt_commit.c
+++ b/fs/ubifs/lpt_commit.c
@@ -1245,6 +1245,10 @@ int ubifs_lpt_start_commit(struct ubifs_info *c)
 	if (err)
 		goto out;
 
+	err = ubifs_lpt_calc_hash(c, c->mst_node->hash_lpt);
+	if (err)
+		goto out;
+
 	/* Copy the LPT's own lprops for end commit to write */
 	memcpy(c->ltab_cmt, c->ltab,
 	       sizeof(struct ubifs_lpt_lprops) * c->lpt_lebs);
diff --git a/fs/ubifs/ubifs.h b/fs/ubifs/ubifs.h
index 5390d087da3a..5234a7c9380c 100644
--- a/fs/ubifs/ubifs.h
+++ b/fs/ubifs/ubifs.h
@@ -1913,6 +1913,7 @@ struct ubifs_nnode *ubifs_first_nnode(struct ubifs_info *c, int *hght);
 /* Needed only in debugging code in lpt_commit.c */
 int ubifs_unpack_nnode(const struct ubifs_info *c, void *buf,
 		       struct ubifs_nnode *nnode);
+int ubifs_lpt_calc_hash(struct ubifs_info *c, u8 *hash);
 
 /* lpt_commit.c */
 int ubifs_lpt_start_commit(struct ubifs_info *c);
-- 
2.18.0


^ permalink raw reply related	[flat|nested] 49+ messages in thread

* [PATCH 18/25] ubfis: authentication: authenticate master node
  2018-07-04 12:41 [PATCH 00/25] UBIFS authentication support Sascha Hauer
                   ` (16 preceding siblings ...)
  2018-07-04 12:41 ` [PATCH 17/25] ubifs: authentication: authenticate LPT Sascha Hauer
@ 2018-07-04 12:41 ` Sascha Hauer
  2018-07-04 12:41 ` [PATCH 19/25] ubifs: Create hash for default LPT Sascha Hauer
                   ` (6 subsequent siblings)
  24 siblings, 0 replies; 49+ messages in thread
From: Sascha Hauer @ 2018-07-04 12:41 UTC (permalink / raw)
  To: linux-mtd
  Cc: David Gstir, Richard Weinberger, kernel, linux-kernel, Sascha Hauer

The master node contains hashes over the root index node and the LPT.
This patch adds a HMAC to authenticate the master node itself.

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
 fs/ubifs/master.c   | 61 ++++++++++++++++++++++++++++++++++++++++-----
 fs/ubifs/recovery.c |  7 +++---
 fs/ubifs/ubifs.h    |  1 +
 3 files changed, 59 insertions(+), 10 deletions(-)

diff --git a/fs/ubifs/master.c b/fs/ubifs/master.c
index f1a96b50ec68..1f633601e95b 100644
--- a/fs/ubifs/master.c
+++ b/fs/ubifs/master.c
@@ -24,6 +24,42 @@
 
 #include "ubifs.h"
 
+/**
+ * ubifs_compare_master_node - compare two UBIFS master nodes
+ * @c: UBIFS file-system description object
+ * @m1: the first node
+ * @m2: the second node
+ *
+ * This function compares two UBIFS master nodes. Returns 0 if they are equal
+ * and nonzero if not.
+ */
+int ubifs_compare_master_node(struct ubifs_info *c, void *m1, void *m2)
+{
+	int ret;
+	int behind;
+	int hmac_offs = offsetof(struct ubifs_mst_node, hmac);
+
+	/*
+	 * Do not compare the common node header since the sequence number and
+	 * hence the CRC are different.
+	 */
+	ret = memcmp(m1 + UBIFS_CH_SZ, m2 + UBIFS_CH_SZ,
+		     hmac_offs - UBIFS_CH_SZ);
+	if (ret)
+		return ret;
+
+	/*
+	 * Do not compare the embedded HMAC aswell which also must be different
+	 * due to the different common node header.
+	 */
+	behind = hmac_offs + UBIFS_MAX_HMAC_LEN;
+
+	if (UBIFS_MST_NODE_SZ > behind)
+		return memcmp(m1 + behind, m2 + behind, UBIFS_MST_NODE_SZ - behind);
+
+	return 0;
+}
+
 /**
  * scan_for_master - search the valid master node.
  * @c: UBIFS file-system description object
@@ -37,7 +73,7 @@ static int scan_for_master(struct ubifs_info *c)
 {
 	struct ubifs_scan_leb *sleb;
 	struct ubifs_scan_node *snod;
-	int lnum, offs = 0, nodes_cnt;
+	int lnum, offs = 0, nodes_cnt, err;
 
 	lnum = UBIFS_MST_LNUM;
 
@@ -69,12 +105,23 @@ static int scan_for_master(struct ubifs_info *c)
 		goto out_dump;
 	if (snod->offs != offs)
 		goto out;
-	if (memcmp((void *)c->mst_node + UBIFS_CH_SZ,
-		   (void *)snod->node + UBIFS_CH_SZ,
-		   UBIFS_MST_NODE_SZ - UBIFS_CH_SZ))
+	if (ubifs_compare_master_node(c, c->mst_node, snod->node))
 		goto out;
+
 	c->mst_offs = offs;
 	ubifs_scan_destroy(sleb);
+
+	if (!ubifs_authenticated(c))
+		return 0;
+
+	err = ubifs_node_verify_hmac(c, c->mst_node,
+				     sizeof(struct ubifs_mst_node),
+				     offsetof(struct ubifs_mst_node, hmac));
+	if (err) {
+		ubifs_err(c, "Failed to verify master node HMAC");
+		return -EPERM;
+	}
+
 	return 0;
 
 out:
@@ -381,7 +428,8 @@ int ubifs_write_master(struct ubifs_info *c)
 	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_hmac(c, c->mst_node, len, lnum, offs,
+				    offsetof(struct ubifs_mst_node, hmac));
 	if (err)
 		return err;
 
@@ -392,7 +440,8 @@ int ubifs_write_master(struct ubifs_info *c)
 		if (err)
 			return err;
 	}
-	err = ubifs_write_node(c, c->mst_node, len, lnum, offs);
+	err = ubifs_write_node_hmac(c, c->mst_node, len, lnum, offs,
+				    offsetof(struct ubifs_mst_node, hmac));
 
 	return err;
 }
diff --git a/fs/ubifs/recovery.c b/fs/ubifs/recovery.c
index 3af4472061cc..3fa7c2cd96b9 100644
--- a/fs/ubifs/recovery.c
+++ b/fs/ubifs/recovery.c
@@ -212,7 +212,8 @@ static int write_rcvrd_mst_node(struct ubifs_info *c,
 	save_flags = mst->flags;
 	mst->flags |= cpu_to_le32(UBIFS_MST_RCVRY);
 
-	ubifs_prepare_node(c, mst, UBIFS_MST_NODE_SZ, 1);
+	ubifs_prepare_node_hmac(c, mst, UBIFS_MST_NODE_SZ,
+				offsetof(struct ubifs_mst_node, hmac), 1);
 	err = ubifs_leb_change(c, lnum, mst, sz);
 	if (err)
 		goto out;
@@ -264,9 +265,7 @@ int ubifs_recover_master_node(struct ubifs_info *c)
 			offs2 = (void *)mst2 - buf2;
 			if (offs1 == offs2) {
 				/* Same offset, so must be the same */
-				if (memcmp((void *)mst1 + UBIFS_CH_SZ,
-					   (void *)mst2 + UBIFS_CH_SZ,
-					   UBIFS_MST_NODE_SZ - UBIFS_CH_SZ))
+				if (ubifs_compare_master_node(c, mst1, mst2))
 					goto out_err;
 				mst = mst1;
 			} else if (offs2 + sz == offs1) {
diff --git a/fs/ubifs/ubifs.h b/fs/ubifs/ubifs.h
index 5234a7c9380c..a2805dec8aee 100644
--- a/fs/ubifs/ubifs.h
+++ b/fs/ubifs/ubifs.h
@@ -1852,6 +1852,7 @@ int ubifs_gc_should_commit(struct ubifs_info *c);
 void ubifs_wait_for_commit(struct ubifs_info *c);
 
 /* master.c */
+int ubifs_compare_master_node(struct ubifs_info *c, void *m1, void *m2);
 int ubifs_read_master(struct ubifs_info *c);
 int ubifs_write_master(struct ubifs_info *c);
 
-- 
2.18.0


^ permalink raw reply related	[flat|nested] 49+ messages in thread

* [PATCH 19/25] ubifs: Create hash for default LPT
  2018-07-04 12:41 [PATCH 00/25] UBIFS authentication support Sascha Hauer
                   ` (17 preceding siblings ...)
  2018-07-04 12:41 ` [PATCH 18/25] ubfis: authentication: authenticate master node Sascha Hauer
@ 2018-07-04 12:41 ` Sascha Hauer
  2018-07-04 12:41 ` [PATCH 20/25] ubifs: authentication: Authenticate super block node Sascha Hauer
                   ` (5 subsequent siblings)
  24 siblings, 0 replies; 49+ messages in thread
From: Sascha Hauer @ 2018-07-04 12:41 UTC (permalink / raw)
  To: linux-mtd
  Cc: David Gstir, Richard Weinberger, kernel, linux-kernel, Sascha Hauer

During creation of the default filesystem on an empty flash the default
LPT is created. With this patch a hash over the default LPT is
calculated which can be added to the default filesystems master node.

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
 fs/ubifs/lpt.c   | 13 ++++++++++++-
 fs/ubifs/sb.c    |  3 ++-
 fs/ubifs/ubifs.h |  2 +-
 3 files changed, 15 insertions(+), 3 deletions(-)

diff --git a/fs/ubifs/lpt.c b/fs/ubifs/lpt.c
index 2c440bb31d69..c979d604f69d 100644
--- a/fs/ubifs/lpt.c
+++ b/fs/ubifs/lpt.c
@@ -602,11 +602,12 @@ static int calc_pnode_num_from_parent(const struct ubifs_info *c,
  * @lpt_first: LEB number of first LPT LEB
  * @lpt_lebs: number of LEBs for LPT is passed and returned here
  * @big_lpt: use big LPT model is passed and returned here
+ * @hash: hash of the LPT is returned here
  *
  * This function returns %0 on success and a negative error code on failure.
  */
 int ubifs_create_dflt_lpt(struct ubifs_info *c, int *main_lebs, int lpt_first,
-			  int *lpt_lebs, int *big_lpt)
+			  int *lpt_lebs, int *big_lpt, u8 *hash)
 {
 	int lnum, err = 0, node_sz, iopos, i, j, cnt, len, alen, row;
 	int blnum, boffs, bsz, bcnt;
@@ -615,6 +616,7 @@ int ubifs_create_dflt_lpt(struct ubifs_info *c, int *main_lebs, int lpt_first,
 	void *buf = NULL, *p;
 	struct ubifs_lpt_lprops *ltab = NULL;
 	int *lsave = NULL;
+	struct shash_desc *desc;
 
 	err = calc_dflt_lpt_geom(c, main_lebs, big_lpt);
 	if (err)
@@ -628,6 +630,10 @@ int ubifs_create_dflt_lpt(struct ubifs_info *c, int *main_lebs, int lpt_first,
 	/* Needed by 'ubifs_pack_lsave()' */
 	c->main_first = c->leb_cnt - *main_lebs;
 
+	desc = ubifs_hash_get_desc(c);
+	if (IS_ERR(desc))
+		return PTR_ERR(desc);
+
 	lsave = kmalloc_array(c->lsave_cnt, sizeof(int), GFP_KERNEL);
 	pnode = kzalloc(sizeof(struct ubifs_pnode), GFP_KERNEL);
 	nnode = kzalloc(sizeof(struct ubifs_nnode), GFP_KERNEL);
@@ -675,6 +681,7 @@ int ubifs_create_dflt_lpt(struct ubifs_info *c, int *main_lebs, int lpt_first,
 
 	/* Add first pnode */
 	ubifs_pack_pnode(c, p, pnode);
+	ubifs_shash_update(c, desc, p, c->pnode_sz);
 	p += c->pnode_sz;
 	len = c->pnode_sz;
 	pnode->num += 1;
@@ -709,6 +716,7 @@ int ubifs_create_dflt_lpt(struct ubifs_info *c, int *main_lebs, int lpt_first,
 			len = 0;
 		}
 		ubifs_pack_pnode(c, p, pnode);
+		ubifs_shash_update(c, desc, p, c->pnode_sz);
 		p += c->pnode_sz;
 		len += c->pnode_sz;
 		/*
@@ -828,6 +836,8 @@ int ubifs_create_dflt_lpt(struct ubifs_info *c, int *main_lebs, int lpt_first,
 	if (err)
 		goto out;
 
+	ubifs_shash_final(c, desc, hash);
+
 	c->nhead_lnum = lnum;
 	c->nhead_offs = ALIGN(len, c->min_io_size);
 
@@ -851,6 +861,7 @@ int ubifs_create_dflt_lpt(struct ubifs_info *c, int *main_lebs, int lpt_first,
 		dbg_lp("LPT lsave is at %d:%d", c->lsave_lnum, c->lsave_offs);
 out:
 	c->ltab = NULL;
+	kfree(desc);
 	kfree(lsave);
 	vfree(ltab);
 	vfree(buf);
diff --git a/fs/ubifs/sb.c b/fs/ubifs/sb.c
index 2fabd441d74a..155480f62fac 100644
--- a/fs/ubifs/sb.c
+++ b/fs/ubifs/sb.c
@@ -87,6 +87,7 @@ static int create_default_filesystem(struct ubifs_info *c)
 	__le64 tmp_le64;
 	__le32 tmp_le32;
 	struct timespec ts;
+	u8 hash_lpt[UBIFS_MAX_HASH_LEN];
 
 	/* Some functions called from here depend on the @c->key_len filed */
 	c->key_len = UBIFS_SK_LEN;
@@ -148,7 +149,7 @@ static int create_default_filesystem(struct ubifs_info *c)
 	c->lsave_cnt = DEFAULT_LSAVE_CNT;
 	c->max_leb_cnt = c->leb_cnt;
 	err = ubifs_create_dflt_lpt(c, &main_lebs, lpt_first, &lpt_lebs,
-				    &big_lpt);
+				    &big_lpt, hash_lpt);
 	if (err)
 		return err;
 
diff --git a/fs/ubifs/ubifs.h b/fs/ubifs/ubifs.h
index a2805dec8aee..c5f15db915a9 100644
--- a/fs/ubifs/ubifs.h
+++ b/fs/ubifs/ubifs.h
@@ -1886,7 +1886,7 @@ int ubifs_clear_orphans(struct ubifs_info *c);
 /* lpt.c */
 int ubifs_calc_lpt_geom(struct ubifs_info *c);
 int ubifs_create_dflt_lpt(struct ubifs_info *c, int *main_lebs, int lpt_first,
-			  int *lpt_lebs, int *big_lpt);
+			  int *lpt_lebs, int *big_lpt, u8 *hash);
 int ubifs_lpt_init(struct ubifs_info *c, int rd, int wr);
 struct ubifs_lprops *ubifs_lpt_lookup(struct ubifs_info *c, int lnum);
 struct ubifs_lprops *ubifs_lpt_lookup_dirty(struct ubifs_info *c, int lnum);
-- 
2.18.0


^ permalink raw reply related	[flat|nested] 49+ messages in thread

* [PATCH 20/25] ubifs: authentication: Authenticate super block node
  2018-07-04 12:41 [PATCH 00/25] UBIFS authentication support Sascha Hauer
                   ` (18 preceding siblings ...)
  2018-07-04 12:41 ` [PATCH 19/25] ubifs: Create hash for default LPT Sascha Hauer
@ 2018-07-04 12:41 ` Sascha Hauer
  2018-07-04 12:41 ` [PATCH 21/25] ubifs: Add hashes and HMACs to default filesystem Sascha Hauer
                   ` (4 subsequent siblings)
  24 siblings, 0 replies; 49+ messages in thread
From: Sascha Hauer @ 2018-07-04 12:41 UTC (permalink / raw)
  To: linux-mtd
  Cc: David Gstir, Richard Weinberger, kernel, linux-kernel, Sascha Hauer

This adds a HMAC covering the super block node and adds the logic that
decides if a filesystem shall be mounted unauthenticated or
authenticated.

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
 fs/ubifs/sb.c | 63 ++++++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 62 insertions(+), 1 deletion(-)

diff --git a/fs/ubifs/sb.c b/fs/ubifs/sb.c
index 155480f62fac..2e814b5fb5d1 100644
--- a/fs/ubifs/sb.c
+++ b/fs/ubifs/sb.c
@@ -517,6 +517,62 @@ static struct ubifs_sb_node *ubifs_read_sb_node(struct ubifs_info *c)
 	return sup;
 }
 
+static int authenticate_sb_node(struct ubifs_info *c,
+				const struct ubifs_sb_node *sup)
+{
+	unsigned int sup_flags = le32_to_cpu(sup->flags);
+	u8 hmac_wkm[UBIFS_MAX_HMAC_LEN];
+	int authenticated = !!(sup_flags & UBIFS_FLG_AUTHENTICATION);
+	int hash_algo;
+	int err;
+
+	if (c->authenticated && !authenticated) {
+		ubifs_err(c, "authenticated FS forced, but found FS without authentication");
+		return -EINVAL;
+	}
+
+	if (!c->authenticated && authenticated) {
+		ubifs_err(c, "authenticated FS found, but no key given");
+		return -EINVAL;
+	}
+
+	ubifs_msg(c, "Mounting in %sauthenticated mode",
+		  c->authenticated ? "" : "un");
+
+	if (!c->authenticated)
+		return 0;
+
+	if (!IS_ENABLED(CONFIG_UBIFS_FS_AUTHENTICATION))
+		return -EOPNOTSUPP;
+
+	hash_algo = le16_to_cpu(sup->hash_algo);
+	if (hash_algo >= HASH_ALGO__LAST) {
+		ubifs_err(c, "superblock uses unknown hash algo %d",
+			  hash_algo);
+		return -EINVAL;
+	}
+
+	if (strcmp(hash_algo_name[hash_algo], c->auth_hash_name)) {
+		ubifs_err(c, "This filesystem uses %s for hashing,"
+			     " but %s is specified", hash_algo_name[hash_algo],
+			     c->auth_hash_name);
+		return -EINVAL;
+	}
+
+	ubifs_hmac_wkm(c, hmac_wkm);
+	if (ubifs_check_hmac(c, hmac_wkm, sup->hmac_wkm)) {
+		ubifs_err(c, "provided key does not fit");
+		return -ENOKEY;
+	}
+
+	err = ubifs_node_verify_hmac(c, sup, sizeof(*sup),
+				     offsetof(struct ubifs_sb_node, hmac));
+	if (err)
+		ubifs_err(c, "Failed to authenticate superblock: %d", err);
+
+	return err;
+}
+
 /**
  * ubifs_write_sb_node - write superblock node.
  * @c: UBIFS file-system description object
@@ -528,7 +584,8 @@ int ubifs_write_sb_node(struct ubifs_info *c, struct ubifs_sb_node *sup)
 {
 	int len = ALIGN(UBIFS_SB_NODE_SZ, c->min_io_size);
 
-	ubifs_prepare_node(c, sup, UBIFS_SB_NODE_SZ, 1);
+	ubifs_prepare_node_hmac(c, sup, UBIFS_SB_NODE_SZ,
+				offsetof(struct ubifs_sb_node, hmac), 1);
 	return ubifs_leb_change(c, UBIFS_SB_LNUM, sup, len);
 }
 
@@ -642,6 +699,10 @@ int ubifs_read_superblock(struct ubifs_info *c)
 	c->double_hash = !!(sup_flags & UBIFS_FLG_DOUBLE_HASH);
 	c->encrypted = !!(sup_flags & UBIFS_FLG_ENCRYPTION);
 
+	err = authenticate_sb_node(c, sup);
+	if (err)
+		goto out;
+
 	if ((sup_flags & ~UBIFS_FLG_MASK) != 0) {
 		ubifs_err(c, "Unknown feature flags found: %#x",
 			  sup_flags & ~UBIFS_FLG_MASK);
-- 
2.18.0


^ permalink raw reply related	[flat|nested] 49+ messages in thread

* [PATCH 21/25] ubifs: Add hashes and HMACs to default filesystem
  2018-07-04 12:41 [PATCH 00/25] UBIFS authentication support Sascha Hauer
                   ` (19 preceding siblings ...)
  2018-07-04 12:41 ` [PATCH 20/25] ubifs: authentication: Authenticate super block node Sascha Hauer
@ 2018-07-04 12:41 ` Sascha Hauer
  2018-07-04 12:41 ` [PATCH 22/25] ubifs: do not update inode size in-place in authenticated mode Sascha Hauer
                   ` (3 subsequent siblings)
  24 siblings, 0 replies; 49+ messages in thread
From: Sascha Hauer @ 2018-07-04 12:41 UTC (permalink / raw)
  To: linux-mtd
  Cc: David Gstir, Richard Weinberger, kernel, linux-kernel, Sascha Hauer

This patch calculates the necessary hashes and HMACs for the default
filesystem so that the dynamically created default fs can be
authenticated.

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
 fs/ubifs/sb.c | 32 +++++++++++++++++++++++++-------
 1 file changed, 25 insertions(+), 7 deletions(-)

diff --git a/fs/ubifs/sb.c b/fs/ubifs/sb.c
index 2e814b5fb5d1..99f857224255 100644
--- a/fs/ubifs/sb.c
+++ b/fs/ubifs/sb.c
@@ -87,6 +87,7 @@ static int create_default_filesystem(struct ubifs_info *c)
 	__le64 tmp_le64;
 	__le32 tmp_le32;
 	struct timespec ts;
+	u8 hash[UBIFS_MAX_HASH_LEN];
 	u8 hash_lpt[UBIFS_MAX_HASH_LEN];
 
 	/* Some functions called from here depend on the @c->key_len filed */
@@ -177,6 +178,14 @@ static int create_default_filesystem(struct ubifs_info *c)
 		sup_flags |= UBIFS_FLG_BIGLPT;
 	sup_flags |= UBIFS_FLG_DOUBLE_HASH;
 
+	if (ubifs_authenticated(c)) {
+		sup_flags |= UBIFS_FLG_AUTHENTICATION;
+		sup->hash_algo = cpu_to_le16(c->auth_hash_algo);
+		ubifs_hmac_wkm(c, sup->hmac_wkm);
+	} else {
+		sup->hash_algo = 0xffff;
+	}
+
 	sup->ch.node_type  = UBIFS_SB_NODE;
 	sup->key_hash      = UBIFS_KEY_HASH_R5;
 	sup->flags         = cpu_to_le32(sup_flags);
@@ -235,6 +244,7 @@ static int create_default_filesystem(struct ubifs_info *c)
 	mst->empty_lebs   = cpu_to_le32(main_lebs - 2);
 	mst->idx_lebs     = cpu_to_le32(1);
 	mst->leb_cnt      = cpu_to_le32(c->leb_cnt);
+	ubifs_copy_hash(c, hash_lpt, mst->hash_lpt);
 
 	/* Calculate lprops statistics */
 	tmp64 = main_bytes;
@@ -307,25 +317,33 @@ static int create_default_filesystem(struct ubifs_info *c)
 
 	cs->ch.node_type = UBIFS_CS_NODE;
 
-	err = ubifs_write_node(c, sup, UBIFS_SB_NODE_SZ, 0, 0);
+	err = ubifs_write_node_hmac(c, sup, UBIFS_SB_NODE_SZ, 0, 0,
+				    offsetof(struct ubifs_sb_node, hmac));
 	if (err)
 		goto out;
 
-	err = ubifs_write_node(c, mst, UBIFS_MST_NODE_SZ, UBIFS_MST_LNUM, 0);
+	err = ubifs_write_node(c, ino, UBIFS_INO_NODE_SZ,
+			       main_first + DEFAULT_DATA_LEB, 0);
 	if (err)
 		goto out;
 
-	err = ubifs_write_node(c, mst, UBIFS_MST_NODE_SZ, UBIFS_MST_LNUM + 1,
-			       0);
+	ubifs_node_calc_hash(c, ino, hash);
+	ubifs_copy_hash(c, hash, ubifs_branch_hash(c, br));
+
+	err = ubifs_write_node(c, idx, idx_node_size, main_first + DEFAULT_IDX_LEB, 0);
 	if (err)
 		goto out;
 
-	err = ubifs_write_node(c, idx, idx_node_size, main_first + DEFAULT_IDX_LEB, 0);
+	ubifs_node_calc_hash(c, idx, hash);
+	ubifs_copy_hash(c, hash, mst->hash_root_idx);
+
+	err = ubifs_write_node_hmac(c, mst, UBIFS_MST_NODE_SZ, UBIFS_MST_LNUM, 0,
+		offsetof(struct ubifs_mst_node, hmac));
 	if (err)
 		goto out;
 
-	err = ubifs_write_node(c, ino, UBIFS_INO_NODE_SZ,
-			       main_first + DEFAULT_DATA_LEB, 0);
+	err = ubifs_write_node_hmac(c, mst, UBIFS_MST_NODE_SZ, UBIFS_MST_LNUM + 1,
+			       0, offsetof(struct ubifs_mst_node, hmac));
 	if (err)
 		goto out;
 
-- 
2.18.0


^ permalink raw reply related	[flat|nested] 49+ messages in thread

* [PATCH 22/25] ubifs: do not update inode size in-place in authenticated mode
  2018-07-04 12:41 [PATCH 00/25] UBIFS authentication support Sascha Hauer
                   ` (20 preceding siblings ...)
  2018-07-04 12:41 ` [PATCH 21/25] ubifs: Add hashes and HMACs to default filesystem Sascha Hauer
@ 2018-07-04 12:41 ` Sascha Hauer
  2018-07-04 12:41 ` [PATCH 23/25] ubifs: Enable authentication support Sascha Hauer
                   ` (2 subsequent siblings)
  24 siblings, 0 replies; 49+ messages in thread
From: Sascha Hauer @ 2018-07-04 12:41 UTC (permalink / raw)
  To: linux-mtd
  Cc: David Gstir, Richard Weinberger, kernel, linux-kernel, Sascha Hauer

In authenticated mode we cannot fixup the inode sizes in-place
during recovery as this would invalidate the hashes and HMACs
we stored for this inode.

Instead, we just write the updated inodes to the journal. We can
only do this after ubifs_rcvry_gc_commit() is done though, so for
authenticated mode call ubifs_recover_size() after
ubifs_rcvry_gc_commit() and not vice versa as normally done.

Calling ubifs_recover_size() after ubifs_rcvry_gc_commit() has the
drawback that after a commit the size fixup information is gone, so
when a powercut happens while recovering from another powercut
we may lose some data written right before the first powercut.
This is why we only do this in authenticated mode and leave the
behaviour for unauthenticated mode untouched.

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
 fs/ubifs/recovery.c | 111 +++++++++++++++++++++++++++++++++-----------
 fs/ubifs/super.c    |  38 +++++++++++----
 fs/ubifs/ubifs.h    |   2 +-
 3 files changed, 113 insertions(+), 38 deletions(-)

diff --git a/fs/ubifs/recovery.c b/fs/ubifs/recovery.c
index 3fa7c2cd96b9..293f4ff10c13 100644
--- a/fs/ubifs/recovery.c
+++ b/fs/ubifs/recovery.c
@@ -1460,16 +1460,82 @@ static int fix_size_in_place(struct ubifs_info *c, struct size_entry *e)
 	return err;
 }
 
+/**
+ * inode_fix_size - fix inode size
+ * @c: UBIFS file-system description object
+ * @e: inode size information for recovery
+ */
+static int inode_fix_size(struct ubifs_info *c, struct size_entry *e)
+{
+	struct inode *inode;
+	struct ubifs_inode *ui;
+	int err;
+
+	if (c->ro_mount)
+		ubifs_assert(!e->inode);
+
+	if (e->inode) {
+		/* Remounting rw, pick up inode we stored earlier */
+		inode = e->inode;
+	} else {
+		inode = ubifs_iget(c->vfs_sb, e->inum);
+		if (IS_ERR(inode))
+			return PTR_ERR(inode);
+
+		if (inode->i_size >= e->d_size) {
+			/*
+			 * The original inode in the index already has a size
+			 * big enough, nothing to do
+			 */
+			iput(inode);
+			return 0;
+		}
+
+		dbg_rcvry("ino %lu size %lld -> %lld",
+			  (unsigned long)e->inum,
+			  inode->i_size, e->d_size);
+
+		ui = ubifs_inode(inode);
+
+		inode->i_size = e->d_size;
+		ui->ui_size = e->d_size;
+		ui->synced_i_size = e->d_size;
+
+		e->inode = inode;
+	}
+
+	/*
+	 * In readonly mode just keep the inode pinned in memory until we go
+	 * readwrite. In readwrite mode write the inode to the journal with the
+	 * fixed size.
+	 */
+	if (c->ro_mount)
+		return 0;
+
+	err = ubifs_jnl_write_inode(c, inode);
+
+	iput(inode);
+
+	if (err)
+		return err;
+
+	rb_erase(&e->rb, &c->size_tree);
+	kfree(e);
+
+	return 0;
+}
+
 /**
  * ubifs_recover_size - recover inode size.
  * @c: UBIFS file-system description object
+ * @in_place: If true, do a in-place size fixup
  *
  * This function attempts to fix inode size discrepancies identified by the
  * 'ubifs_recover_size_accum()' function.
  *
  * This functions returns %0 on success and a negative error code on failure.
  */
-int ubifs_recover_size(struct ubifs_info *c)
+int ubifs_recover_size(struct ubifs_info *c, bool in_place)
 {
 	struct rb_node *this = rb_first(&c->size_tree);
 
@@ -1478,6 +1544,9 @@ int ubifs_recover_size(struct ubifs_info *c)
 		int err;
 
 		e = rb_entry(this, struct size_entry, rb);
+
+		this = rb_next(this);
+
 		if (!e->exists) {
 			union ubifs_key key;
 
@@ -1501,40 +1570,26 @@ int ubifs_recover_size(struct ubifs_info *c)
 		}
 
 		if (e->exists && e->i_size < e->d_size) {
-			if (c->ro_mount) {
-				/* Fix the inode size and pin it in memory */
-				struct inode *inode;
-				struct ubifs_inode *ui;
-
-				ubifs_assert(!e->inode);
-
-				inode = ubifs_iget(c->vfs_sb, e->inum);
-				if (IS_ERR(inode))
-					return PTR_ERR(inode);
-
-				ui = ubifs_inode(inode);
-				if (inode->i_size < e->d_size) {
-					dbg_rcvry("ino %lu size %lld -> %lld",
-						  (unsigned long)e->inum,
-						  inode->i_size, e->d_size);
-					inode->i_size = e->d_size;
-					ui->ui_size = e->d_size;
-					ui->synced_i_size = e->d_size;
-					e->inode = inode;
-					this = rb_next(this);
-					continue;
-				}
-				iput(inode);
-			} else {
-				/* Fix the size in place */
+			ubifs_assert(!(c->ro_mount && in_place));
+
+			/*
+			 * We found data that is outside the found inode size,
+			 * fixup the inode size
+			 */
+
+			if (in_place) {
 				err = fix_size_in_place(c, e);
 				if (err)
 					return err;
 				iput(e->inode);
+			} else {
+				err = inode_fix_size(c, e);
+				if (err)
+					return err;
+				continue;
 			}
 		}
 
-		this = rb_next(this);
 		rb_erase(&e->rb, &c->size_tree);
 		kfree(e);
 	}
diff --git a/fs/ubifs/super.c b/fs/ubifs/super.c
index 49de06921427..de7bf613addc 100644
--- a/fs/ubifs/super.c
+++ b/fs/ubifs/super.c
@@ -1353,12 +1353,21 @@ static int mount_ubifs(struct ubifs_info *c)
 		}
 
 		if (c->need_recovery) {
-			err = ubifs_recover_size(c);
-			if (err)
-				goto out_orphans;
+			if (!ubifs_authenticated(c)) {
+				err = ubifs_recover_size(c, true);
+				if (err)
+					goto out_orphans;
+			}
+
 			err = ubifs_rcvry_gc_commit(c);
 			if (err)
 				goto out_orphans;
+
+			if (ubifs_authenticated(c)) {
+				err = ubifs_recover_size(c, false);
+				if (err)
+					goto out_orphans;
+			}
 		} else {
 			err = take_gc_lnum(c);
 			if (err)
@@ -1377,7 +1386,7 @@ static int mount_ubifs(struct ubifs_info *c)
 		if (err)
 			goto out_orphans;
 	} else if (c->need_recovery) {
-		err = ubifs_recover_size(c);
+		err = ubifs_recover_size(c, false);
 		if (err)
 			goto out_orphans;
 	} else {
@@ -1604,9 +1613,11 @@ static int ubifs_remount_rw(struct ubifs_info *c)
 		err = ubifs_write_rcvrd_mst_node(c);
 		if (err)
 			goto out;
-		err = ubifs_recover_size(c);
-		if (err)
-			goto out;
+		if (!ubifs_authenticated(c)) {
+			err = ubifs_recover_size(c, true);
+			if (err)
+				goto out;
+		}
 		err = ubifs_clean_lebs(c, c->sbuf);
 		if (err)
 			goto out;
@@ -1672,10 +1683,19 @@ static int ubifs_remount_rw(struct ubifs_info *c)
 			goto out;
 	}
 
-	if (c->need_recovery)
+	if (c->need_recovery) {
 		err = ubifs_rcvry_gc_commit(c);
-	else
+		if (err)
+			goto out;
+
+		if (ubifs_authenticated(c)) {
+			err = ubifs_recover_size(c, false);
+			if (err)
+				goto out;
+		}
+	} else {
 		err = ubifs_leb_unmap(c, c->gc_lnum);
+	}
 	if (err)
 		goto out;
 
diff --git a/fs/ubifs/ubifs.h b/fs/ubifs/ubifs.h
index c5f15db915a9..afe1b0904683 100644
--- a/fs/ubifs/ubifs.h
+++ b/fs/ubifs/ubifs.h
@@ -1996,7 +1996,7 @@ int ubifs_clean_lebs(struct ubifs_info *c, void *sbuf);
 int ubifs_rcvry_gc_commit(struct ubifs_info *c);
 int ubifs_recover_size_accum(struct ubifs_info *c, union ubifs_key *key,
 			     int deletion, loff_t new_size);
-int ubifs_recover_size(struct ubifs_info *c);
+int ubifs_recover_size(struct ubifs_info *c, bool in_place);
 void ubifs_destroy_size_tree(struct ubifs_info *c);
 
 /* ioctl.c */
-- 
2.18.0


^ permalink raw reply related	[flat|nested] 49+ messages in thread

* [PATCH 23/25] ubifs: Enable authentication support
  2018-07-04 12:41 [PATCH 00/25] UBIFS authentication support Sascha Hauer
                   ` (21 preceding siblings ...)
  2018-07-04 12:41 ` [PATCH 22/25] ubifs: do not update inode size in-place in authenticated mode Sascha Hauer
@ 2018-07-04 12:41 ` Sascha Hauer
  2018-07-04 12:41 ` [PATCH 24/25] ubifs: support offline signed images Sascha Hauer
  2018-07-04 12:41 ` [PATCH 25/25] Documentation: ubifs: Add authentication whitepaper Sascha Hauer
  24 siblings, 0 replies; 49+ messages in thread
From: Sascha Hauer @ 2018-07-04 12:41 UTC (permalink / raw)
  To: linux-mtd
  Cc: David Gstir, Richard Weinberger, kernel, linux-kernel, Sascha Hauer

With the preparations all being done this patch now enables authentication
support for UBIFS. Authentication is enabled when the newly introduced
auth_key and auth_hash_name mount options are passed. auth_key provides
the key which is used for authentication whereas auth_hash_name provides
the hashing algorithm used for this FS. Passing these options make
authentication mandatory and only UBIFS images that can be authenticated
with the given key are allowed.

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
 Documentation/filesystems/ubifs.txt |  7 ++++++
 fs/ubifs/Kconfig                    | 10 ++++++++
 fs/ubifs/super.c                    | 36 ++++++++++++++++++++++++++++-
 3 files changed, 52 insertions(+), 1 deletion(-)

diff --git a/Documentation/filesystems/ubifs.txt b/Documentation/filesystems/ubifs.txt
index a0a61d2f389f..acc80442a3bb 100644
--- a/Documentation/filesystems/ubifs.txt
+++ b/Documentation/filesystems/ubifs.txt
@@ -91,6 +91,13 @@ chk_data_crc		do not skip checking CRCs on data nodes
 compr=none              override default compressor and set it to "none"
 compr=lzo               override default compressor and set it to "lzo"
 compr=zlib              override default compressor and set it to "zlib"
+auth_key=		specify the key used for authenticating the filesystem.
+			Passing this option makes authentication mandatory.
+			The passed key must be present in the kernel keyring
+			and must be of type 'logon'
+auth_hash_name=		The hash algorithm used for authentication. Used for
+			both hashing and for creating HMACs. Typical values
+			include "sha256" or "sha512"
 
 
 Quick usage instructions
diff --git a/fs/ubifs/Kconfig b/fs/ubifs/Kconfig
index 2a0b5f1134a4..9da1e7c21b7f 100644
--- a/fs/ubifs/Kconfig
+++ b/fs/ubifs/Kconfig
@@ -75,3 +75,13 @@ config UBIFS_FS_SECURITY
 	  the extended attribute support in advance.
 
 	  If you are not using a security module, say N.
+
+config UBIFS_FS_AUTHENTICATION
+	bool "UBIFS authentication support"
+	select CRYPTO_HMAC
+	help
+	  Enable authentication support for UBIFS. This feature offers protection
+	  against offline changes for both data and metadata of the filesystem.
+	  If you say yes here you should also select a hashing algorithm such as
+	  sha256, these are not selected automatically since there are many
+	  different options.
diff --git a/fs/ubifs/super.c b/fs/ubifs/super.c
index de7bf613addc..b2ccd2d7ce4d 100644
--- a/fs/ubifs/super.c
+++ b/fs/ubifs/super.c
@@ -577,7 +577,9 @@ static int init_constants_early(struct ubifs_info *c)
 	c->ranges[UBIFS_REF_NODE].len  = UBIFS_REF_NODE_SZ;
 	c->ranges[UBIFS_TRUN_NODE].len = UBIFS_TRUN_NODE_SZ;
 	c->ranges[UBIFS_CS_NODE].len   = UBIFS_CS_NODE_SZ;
-	c->ranges[UBIFS_AUTH_NODE].len = UBIFS_AUTH_NODE_SZ;
+	c->ranges[UBIFS_AUTH_NODE].min_len = UBIFS_AUTH_NODE_SZ;
+	c->ranges[UBIFS_AUTH_NODE].max_len = UBIFS_AUTH_NODE_SZ +
+				UBIFS_MAX_HMAC_LEN;
 
 	c->ranges[UBIFS_INO_NODE].min_len  = UBIFS_INO_NODE_SZ;
 	c->ranges[UBIFS_INO_NODE].max_len  = UBIFS_MAX_INO_NODE_SZ;
@@ -932,6 +934,8 @@ static int check_volume_empty(struct ubifs_info *c)
  * Opt_chk_data_crc: check CRCs when reading data nodes
  * Opt_no_chk_data_crc: do not check CRCs when reading data nodes
  * Opt_override_compr: override default compressor
+ * Opt_auth_key: The key name used for authentication
+ * Opt_auth_hash_name: The hash type used for authentication
  * Opt_err: just end of array marker
  */
 enum {
@@ -942,6 +946,8 @@ enum {
 	Opt_chk_data_crc,
 	Opt_no_chk_data_crc,
 	Opt_override_compr,
+	Opt_auth_key,
+	Opt_auth_hash_name,
 	Opt_ignore,
 	Opt_err,
 };
@@ -954,6 +960,8 @@ static const match_table_t tokens = {
 	{Opt_chk_data_crc, "chk_data_crc"},
 	{Opt_no_chk_data_crc, "no_chk_data_crc"},
 	{Opt_override_compr, "compr=%s"},
+	{Opt_auth_key, "auth_key=%s"},
+	{Opt_auth_hash_name, "auth_hash_name=%s"},
 	{Opt_ignore, "ubi=%s"},
 	{Opt_ignore, "vol=%s"},
 	{Opt_err, NULL},
@@ -1056,6 +1064,16 @@ static int ubifs_parse_options(struct ubifs_info *c, char *options,
 			c->default_compr = c->mount_opts.compr_type;
 			break;
 		}
+		case Opt_auth_key:
+			c->auth_key_name = kstrdup(args[0].from, GFP_KERNEL);
+			if (!c->auth_key_name)
+				return -ENOMEM;
+			break;
+		case Opt_auth_hash_name:
+			c->auth_hash_name = kstrdup(args[0].from, GFP_KERNEL);
+			if (!c->auth_hash_name)
+				return -ENOMEM;
+			break;
 		case Opt_ignore:
 			break;
 		default:
@@ -1235,6 +1253,19 @@ static int mount_ubifs(struct ubifs_info *c)
 
 	c->mounting = 1;
 
+	if (c->auth_key_name) {
+		if (IS_ENABLED(CONFIG_UBIFS_FS_AUTHENTICATION)) {
+			err = ubifs_init_authentication(c);
+			if (err)
+				goto out_free;
+		} else {
+			ubifs_err(c, "auth_key_name, but UBIFS is built without"
+				  " authentication support");
+			err = -EINVAL;
+			goto out_free;
+		}
+	}
+
 	err = ubifs_read_superblock(c);
 	if (err)
 		goto out_free;
@@ -1552,7 +1583,10 @@ static void ubifs_umount(struct ubifs_info *c)
 	free_wbufs(c);
 	free_orphans(c);
 	ubifs_lpt_free(c, 0);
+	ubifs_exit_authentication(c);
 
+	kfree(c->auth_key_name);
+	kfree(c->auth_hash_name);
 	kfree(c->cbuf);
 	kfree(c->rcvrd_mst_node);
 	kfree(c->mst_node);
-- 
2.18.0


^ permalink raw reply related	[flat|nested] 49+ messages in thread

* [PATCH 24/25] ubifs: support offline signed images
  2018-07-04 12:41 [PATCH 00/25] UBIFS authentication support Sascha Hauer
                   ` (22 preceding siblings ...)
  2018-07-04 12:41 ` [PATCH 23/25] ubifs: Enable authentication support Sascha Hauer
@ 2018-07-04 12:41 ` Sascha Hauer
  2018-07-04 12:41 ` [PATCH 25/25] Documentation: ubifs: Add authentication whitepaper Sascha Hauer
  24 siblings, 0 replies; 49+ messages in thread
From: Sascha Hauer @ 2018-07-04 12:41 UTC (permalink / raw)
  To: linux-mtd
  Cc: David Gstir, Richard Weinberger, kernel, linux-kernel, Sascha Hauer

HMACs can only be generated on the system the UBIFS image is running on.
To support offline signed images we add a PKCS#7 signature to the UBIFS
image which can be created by mkfs.ubifs.

Both the master node and the superblock need to be authenticated, during
normal runtime both are protected with HMACs. For offline signature
support however only a single signature is desired. We add a signature
covering the superblock node directly behind it. To protect the master
node a hash of the master node is added to the superblock which is used
when the master node doesn't contain a HMAC.

Transition to a read/write filesystem is also supported. During
transition first the master node is rewritten with a HMAC (implicitly,
it is written anyway as the FS is marked dirty). Afterwards the
superblock is rewritten with a HMAC. Once after the image has been
mounted read/write it is HMAC only, the signature is no longer required
or even present on the filesystem.

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
 fs/ubifs/Kconfig       |  1 +
 fs/ubifs/auth.c        | 76 ++++++++++++++++++++++++++++++++++++++++++
 fs/ubifs/master.c      | 11 ++++--
 fs/ubifs/sb.c          | 22 ++++++++----
 fs/ubifs/super.c       | 15 +++++++++
 fs/ubifs/ubifs-media.h | 21 +++++++++++-
 fs/ubifs/ubifs.h       |  3 ++
 7 files changed, 139 insertions(+), 10 deletions(-)

diff --git a/fs/ubifs/Kconfig b/fs/ubifs/Kconfig
index 9da1e7c21b7f..73423317026b 100644
--- a/fs/ubifs/Kconfig
+++ b/fs/ubifs/Kconfig
@@ -79,6 +79,7 @@ config UBIFS_FS_SECURITY
 config UBIFS_FS_AUTHENTICATION
 	bool "UBIFS authentication support"
 	select CRYPTO_HMAC
+	select SYSTEM_DATA_VERIFICATION
 	help
 	  Enable authentication support for UBIFS. This feature offers protection
 	  against offline changes for both data and metadata of the filesystem.
diff --git a/fs/ubifs/auth.c b/fs/ubifs/auth.c
index fd21f2ec8734..070a02591cca 100644
--- a/fs/ubifs/auth.c
+++ b/fs/ubifs/auth.c
@@ -10,10 +10,12 @@
  */
 
 #include <linux/crypto.h>
+#include <linux/verification.h>
 #include <crypto/hash.h>
 #include <crypto/sha.h>
 #include <crypto/algapi.h>
 #include <keys/user-type.h>
+#include <keys/asymmetric-type.h>
 
 #include "ubifs.h"
 
@@ -157,6 +159,61 @@ int __ubifs_node_check_hash(const struct ubifs_info *c, void *node,
 	return 0;
 }
 
+/**
+ * ubifs_sb_verify_signature - verify the signature of a superblock
+ * @c: UBIFS file-system description object
+ * @sup: The superblock node
+ *
+ * To support offline signed images the superblock can be signed with a
+ * PKCS#7 signature. The signature is placed directly behind the superblock
+ * node in an ubifs_sig_node.
+ *
+ * Returns 0 when the signature can be successfully verified or a negative
+ * error code if not.
+ */
+int ubifs_sb_verify_signature(struct ubifs_info *c,
+			      const struct ubifs_sb_node *sup)
+{
+	int err;
+	struct ubifs_scan_leb *sleb;
+	struct ubifs_scan_node *snod;
+	const struct ubifs_sig_node *signode;
+
+	err = ubifs_leb_read(c, UBIFS_SB_LNUM, c->sbuf, 0, c->leb_size, 1);
+
+	sleb = ubifs_scan(c, UBIFS_SB_LNUM, UBIFS_SB_NODE_SZ, c->sbuf, 0);
+	if (IS_ERR(sleb)) {
+		err = PTR_ERR(sleb);
+		return err;
+	}
+
+	if (sleb->nodes_cnt == 0) {
+		ubifs_err(c, "Unable to find signature node");
+		err = -EINVAL;
+		goto out_destroy;
+	}
+
+	snod = list_entry(sleb->nodes.next, struct ubifs_scan_node, list);
+
+	if (snod->type != UBIFS_SIG_NODE) {
+		ubifs_err(c, "Signature node is of wrong type");
+		err = -EINVAL;
+		goto out_destroy;
+	}
+
+	signode = snod->node;
+
+	err = verify_pkcs7_signature(sup, sizeof(struct ubifs_sb_node),
+				     signode->sig, signode->len,
+				     NULL, VERIFYING_UNSPECIFIED_SIGNATURE,
+				     NULL, NULL);
+
+out_destroy:
+	ubifs_scan_destroy(sleb);
+
+	return err;
+}
+
 /**
  * ubifs_init_authentication - initialize UBIFS authentication support
  * @c: UBIFS file-system description object
@@ -411,3 +468,22 @@ void ubifs_hmac_wkm(struct ubifs_info *c, u8 *hmac)
 
 	crypto_shash_final(shash, hmac);
 }
+
+/*
+ * ubifs_hmac_zero - test if a HMAC is zero
+ * @c: UBIFS file-system description object
+ * @hmac: the HMAC to test
+ *
+ * This function tests if a HMAC is zero and returns true if it is
+ * and false otherwise.
+ */
+bool ubifs_hmac_zero(struct ubifs_info *c, const u8 *hmac)
+{
+	int i;
+
+	for (i = 0; i < c->hmac_desc_len; i++)
+		if (hmac[i] != 0)
+			return false;
+
+	return true;
+}
diff --git a/fs/ubifs/master.c b/fs/ubifs/master.c
index 1f633601e95b..8129bef973bc 100644
--- a/fs/ubifs/master.c
+++ b/fs/ubifs/master.c
@@ -114,9 +114,14 @@ static int scan_for_master(struct ubifs_info *c)
 	if (!ubifs_authenticated(c))
 		return 0;
 
-	err = ubifs_node_verify_hmac(c, c->mst_node,
-				     sizeof(struct ubifs_mst_node),
-				     offsetof(struct ubifs_mst_node, hmac));
+	if (ubifs_hmac_zero(c, c->mst_node->hmac))
+		err = ubifs_node_check_hash(c, c->mst_node,
+					    c->superblock->hash_mst);
+	else
+		err = ubifs_node_verify_hmac(c, c->mst_node,
+					sizeof(struct ubifs_mst_node),
+					offsetof(struct ubifs_mst_node, hmac));
+
 	if (err) {
 		ubifs_err(c, "Failed to verify master node HMAC");
 		return -EPERM;
diff --git a/fs/ubifs/sb.c b/fs/ubifs/sb.c
index 99f857224255..d38cd58b71bb 100644
--- a/fs/ubifs/sb.c
+++ b/fs/ubifs/sb.c
@@ -577,14 +577,24 @@ static int authenticate_sb_node(struct ubifs_info *c,
 		return -EINVAL;
 	}
 
-	ubifs_hmac_wkm(c, hmac_wkm);
-	if (ubifs_check_hmac(c, hmac_wkm, sup->hmac_wkm)) {
-		ubifs_err(c, "provided key does not fit");
-		return -ENOKEY;
+	/*
+	 * The super block node can either be authenticated by a HMAC or
+	 * by a signature in a ubifs_sig_node directly following the
+	 * super block node to support offline image creation.
+	 */
+	if (ubifs_hmac_zero(c, sup->hmac)) {
+		err = ubifs_sb_verify_signature(c, sup);
+	} else {
+		ubifs_hmac_wkm(c, hmac_wkm);
+		if (ubifs_check_hmac(c, hmac_wkm, sup->hmac_wkm)) {
+			ubifs_err(c, "provided key does not fit");
+			return -ENOKEY;
+		}
+		err = ubifs_node_verify_hmac(c, sup, sizeof(*sup),
+					     offsetof(struct ubifs_sb_node,
+						      hmac));
 	}
 
-	err = ubifs_node_verify_hmac(c, sup, sizeof(*sup),
-				     offsetof(struct ubifs_sb_node, hmac));
 	if (err)
 		ubifs_err(c, "Failed to authenticate superblock: %d", err);
 
diff --git a/fs/ubifs/super.c b/fs/ubifs/super.c
index b2ccd2d7ce4d..df4da55db998 100644
--- a/fs/ubifs/super.c
+++ b/fs/ubifs/super.c
@@ -580,6 +580,8 @@ static int init_constants_early(struct ubifs_info *c)
 	c->ranges[UBIFS_AUTH_NODE].min_len = UBIFS_AUTH_NODE_SZ;
 	c->ranges[UBIFS_AUTH_NODE].max_len = UBIFS_AUTH_NODE_SZ +
 				UBIFS_MAX_HMAC_LEN;
+	c->ranges[UBIFS_SIG_NODE].min_len = UBIFS_SIG_NODE_SZ;
+	c->ranges[UBIFS_SIG_NODE].max_len = c->leb_size - UBIFS_SB_NODE_SZ;
 
 	c->ranges[UBIFS_INO_NODE].min_len  = UBIFS_INO_NODE_SZ;
 	c->ranges[UBIFS_INO_NODE].max_len  = UBIFS_MAX_INO_NODE_SZ;
@@ -1349,6 +1351,19 @@ static int mount_ubifs(struct ubifs_info *c)
 		err = ubifs_write_master(c);
 		if (err)
 			goto out_lpt;
+
+		/*
+		 * Handle offline signed images: Now that the master node is
+		 * written and its validation no longer depends on the hash
+		 * in the superblock, we can update the offline signed
+		 * superblock with a HMAC version,
+		 */
+		if (ubifs_authenticated(c) &&
+		    ubifs_hmac_zero(c, c->superblock->hmac)) {
+			err = ubifs_write_sb_node(c, c->superblock);
+			if (err)
+				goto out_lpt;
+		}
 	}
 
 	err = dbg_check_idx_size(c, c->bi.old_idx_sz);
diff --git a/fs/ubifs/ubifs-media.h b/fs/ubifs/ubifs-media.h
index 8b7c1844014f..3a73eb59859f 100644
--- a/fs/ubifs/ubifs-media.h
+++ b/fs/ubifs/ubifs-media.h
@@ -287,6 +287,8 @@ enum {
 #define UBIFS_CS_NODE_SZ   sizeof(struct ubifs_cs_node)
 #define UBIFS_ORPH_NODE_SZ sizeof(struct ubifs_orph_node)
 #define UBIFS_AUTH_NODE_SZ sizeof(struct ubifs_auth_node)
+#define UBIFS_SIG_NODE_SZ  sizeof(struct ubifs_sig_node)
+
 /* Extended attribute entry nodes are identical to directory entry nodes */
 #define UBIFS_XENT_NODE_SZ UBIFS_DENT_NODE_SZ
 /* Only this does not have to be multiple of 8 bytes */
@@ -373,6 +375,7 @@ enum {
  * UBIFS_CS_NODE: commit start node
  * UBIFS_ORPH_NODE: orphan node
  * UBIFS_AUTH_NODE: authentication node
+ * UBIFS_SIG_NODE: signature node
  * UBIFS_NODE_TYPES_CNT: count of supported node types
  *
  * Note, we index arrays by these numbers, so keep them low and contiguous.
@@ -393,6 +396,7 @@ enum {
 	UBIFS_CS_NODE,
 	UBIFS_ORPH_NODE,
 	UBIFS_AUTH_NODE,
+	UBIFS_SIG_NODE,
 	UBIFS_NODE_TYPES_CNT,
 };
 
@@ -650,6 +654,8 @@ struct ubifs_pad_node {
  * @hmac_wkm: HMAC of a well known message (the string "UBIFS") as a convenience
  *            to the user to check if the correct key is passed.
  * @hash_algo: The hash algo used for this filesystem (one of enum hash_algo)
+ * @hash_mst: hash of the master node, only valid for signed images in which the
+ *            master node does not contain a hmac
  */
 struct ubifs_sb_node {
 	struct ubifs_ch ch;
@@ -680,7 +686,8 @@ struct ubifs_sb_node {
 	__u8 hmac[UBIFS_MAX_HMAC_LEN];
 	__u8 hmac_wkm[UBIFS_MAX_HMAC_LEN];
 	__le16 hash_algo;
-	__u8 padding2[3838];
+	__u8 hash_mst[UBIFS_MAX_HASH_LEN];
+	__u8 padding2[3774];
 } __packed;
 
 /**
@@ -782,6 +789,18 @@ struct ubifs_auth_node {
 	__u8 hmac[];
 } __packed;
 
+/**
+ * struct ubifs_sig_node - node for signing other nodes
+ * @ch: common header
+ * @len: The length of the signature data
+ * @sig: The signature data
+ */
+struct ubifs_sig_node {
+	struct ubifs_ch ch;
+	__le32 len;
+	__u8 sig[];
+} __packed;
+
 /**
  * struct ubifs_branch - key/reference/length branch
  * @lnum: LEB number of the target node
diff --git a/fs/ubifs/ubifs.h b/fs/ubifs/ubifs.h
index afe1b0904683..f511066214df 100644
--- a/fs/ubifs/ubifs.h
+++ b/fs/ubifs/ubifs.h
@@ -1645,6 +1645,9 @@ static inline int ubifs_auth_node_sz(const struct ubifs_info *c)
 	else
 		return 0;
 }
+int ubifs_sb_verify_signature(struct ubifs_info *c,
+			      const struct ubifs_sb_node *sup);
+bool ubifs_hmac_zero(struct ubifs_info *c, const u8 *hmac);
 
 void ubifs_hmac_wkm(struct ubifs_info *c, u8 *hmac);
 
-- 
2.18.0


^ permalink raw reply related	[flat|nested] 49+ messages in thread

* [PATCH 25/25] Documentation: ubifs: Add authentication whitepaper
  2018-07-04 12:41 [PATCH 00/25] UBIFS authentication support Sascha Hauer
                   ` (23 preceding siblings ...)
  2018-07-04 12:41 ` [PATCH 24/25] ubifs: support offline signed images Sascha Hauer
@ 2018-07-04 12:41 ` Sascha Hauer
  24 siblings, 0 replies; 49+ messages in thread
From: Sascha Hauer @ 2018-07-04 12:41 UTC (permalink / raw)
  To: linux-mtd
  Cc: David Gstir, Richard Weinberger, kernel, linux-kernel, Sascha Hauer

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
 .../filesystems/ubifs-authentication.md       | 426 ++++++++++++++++++
 1 file changed, 426 insertions(+)
 create mode 100644 Documentation/filesystems/ubifs-authentication.md

diff --git a/Documentation/filesystems/ubifs-authentication.md b/Documentation/filesystems/ubifs-authentication.md
new file mode 100644
index 000000000000..028b3e2e25f9
--- /dev/null
+++ b/Documentation/filesystems/ubifs-authentication.md
@@ -0,0 +1,426 @@
+% UBIFS Authentication
+% sigma star gmbh
+% 2018
+
+# Introduction
+
+UBIFS utilizes the fscrypt framework to provide confidentiality for file
+contents and file names. This prevents attacks where an attacker is able to
+read contents of the filesystem on a single point in time. A classic example
+is a lost smartphone where the attacker is unable to read personal data stored
+on the device without the filesystem decryption key.
+
+At the current state, UBIFS encryption however does not prevent attacks where
+the attacker is able to modify the filesystem contents and the user uses the
+device afterwards. In such a scenario an attacker can modify filesystem
+contents arbitrarily without the user noticing. One example is to modify a
+binary to perform a malicious action when executed [DMC-CBC-ATTACK]. Since
+most of the filesystem metadata of UBIFS is stored in plain, this makes it
+fairly easy to swap files and replace their contents.
+
+Other full disk encryption systems like dm-crypt cover all filesystem metadata,
+which makes such kinds of attacks more complicated, but not impossible.
+Especially, if the attacker is given access to the device multiple points in
+time. For dm-crypt and other filesystems that build upon the Linux block IO
+layer, the dm-integrity or dm-verity subsystems [DM-INTEGRITY, DM-VERITY]
+can be used to get full data authentication at the block layer.
+These can also be combined with dm-crypt [CRYPTSETUP2].
+
+This document describes an approach to get file contents _and_ full metadata
+authentication for UBIFS. Since UBIFS uses fscrypt for file contents and file
+name encryption, the authentication system could be tied into fscrypt such that
+existing features like key derivation can be utilized. It should however also
+be possible to use UBIFS authentication without using encryption.
+
+
+## MTD, UBI & UBIFS
+
+On Linux, the MTD (Memory Technology Devices) subsystem provides a uniform
+interface to access raw flash devices. One of the more prominent subsystems that
+work on top of MTD is UBI (Unsorted Block Images). It provides volume management
+for flash devices and is thus somewhat similar to LVM for block devices. In
+addition, it deals with flash-specific wear-leveling and transparent I/O error
+handling. UBI offers logical erase blocks (LEBs) to the layers on top of it
+and maps them transparently to physical erase blocks (PEBs) on the flash.
+
+UBIFS is a filesystem for raw flash which operates on top of UBI. Thus, wear
+leveling and some flash specifics are left to UBI, while UBIFS focuses on
+scalability, performance and recoverability.
+
+
+
+	+------------+ +*******+ +-----------+ +-----+
+	|            | * UBIFS * | UBI-BLOCK | | ... |
+	| JFFS/JFFS2 | +*******+ +-----------+ +-----+
+	|            | +-----------------------------+ +-----------+ +-----+
+	|            | |              UBI            | | MTD-BLOCK | | ... |
+	+------------+ +-----------------------------+ +-----------+ +-----+
+	+------------------------------------------------------------------+
+	|                  MEMORY TECHNOLOGY DEVICES (MTD)                 |
+	+------------------------------------------------------------------+
+	+-----------------------------+ +--------------------------+ +-----+
+	|         NAND DRIVERS        | |        NOR DRIVERS       | | ... |
+	+-----------------------------+ +--------------------------+ +-----+
+
+            Figure 1: Linux kernel subsystems for dealing with raw flash
+
+
+
+Internally, UBIFS maintains multiple data structures which are persisted on
+the flash:
+
+- *Index*: an on-flash B+ tree where the leaf nodes contain filesystem data
+- *Journal*: an additional data structure to collect FS changes before updating
+  the on-flash index and reduce flash wear.
+- *Tree Node Cache (TNC)*: an in-memory B+ tree that reflects the current FS
+  state to avoid frequent flash reads. It is basically the in-memory
+  representation of the index, but contains additional attributes.
+- *LEB property tree (LPT)*: an on-flash B+ tree for free space accounting per
+  UBI LEB.
+
+In the remainder of this section we will cover the on-flash UBIFS data
+structures in more detail. The TNC is of less importance here since it is never
+persisted onto the flash directly. More details on UBIFS can also be found in
+[UBIFS-WP].
+
+
+### UBIFS Index & Tree Node Cache
+
+Basic on-flash UBIFS entities are called *nodes*. UBIFS knows different types
+of nodes. Eg. data nodes (`struct ubifs_data_node`) which store chunks of file
+contents or inode nodes (`struct ubifs_ino_node`) which represent VFS inodes.
+Almost all types of nodes share a common header (`ubifs_ch`) containing basic
+information like node type, node length, a sequence number, etc. (see
+`fs/ubifs/ubifs-media.h`in kernel source). Exceptions are entries of the LPT
+and some less important node types like padding nodes which are used to pad
+unusable content at the end of LEBs.
+
+To avoid re-writing the whole B+ tree on every single change, it is implemented
+as *wandering tree*, where only the changed nodes are re-written and previous
+versions of them are obsoleted without erasing them right away. As a result,
+the index is not stored in a single place on the flash, but *wanders* around
+and there are obsolete parts on the flash as long as the LEB containing them is
+not reused by UBIFS. To find the most recent version of the index, UBIFS stores
+a special node called *master node* into UBI LEB 1 which always points to the
+most recent root node of the UBIFS index. For recoverability, the master node
+is additionally duplicated to LEB 2. Mounting UBIFS is thus a simple read of
+LEB 1 and 2 to get the current master node and from there get the location of
+the most recent on-flash index.
+
+The TNC is the in-memory representation of the on-flash index. It contains some
+additional runtime attributes per node which are not persisted. One of these is
+a dirty-flag which marks nodes that have to be persisted the next time the
+index is written onto the flash. The TNC acts as a write-back cache and all
+modifications of the on-flash index are done through the TNC. Like other caches,
+the TNC does not have to mirror the full index into memory, but reads parts of
+it from flash whenever needed. A *commit* is the UBIFS operation of updating the
+on-flash filesystem structures like the index. On every commit, the TNC nodes
+marked as dirty are written to the flash to update the persisted index.
+
+
+### Journal
+
+To avoid wearing out the flash, the index is only persisted (*commited*) when
+certain conditions are met (eg. `fsync(2)`). The journal is used to record
+any changes (in form of inode nodes, data nodes etc.) between commits
+of the index. During mount, the journal is read from the flash and replayed
+onto the TNC (which will be created on-demand from the on-flash index).
+
+UBIFS reserves a bunch of LEBs just for the journal called *log area*. The
+amount of log area LEBs is configured on filesystem creation (using
+`mkfs.ubifs`) and stored in the superblock node. The log area contains only
+two types of nodes: *reference nodes* and *commit start nodes*. A commit start
+node is written whenever an index commit is performed. Reference nodes are
+written on every journal update. Each reference node points to the position of
+other nodes (inode nodes, data nodes etc.) on the flash that are part of this
+journal entry. These nodes are called *buds* and describe the actual filesystem
+changes including their data.
+
+The log area is maintained as a ring. Whenever the journal is almost full,
+a commit is initiated. This also writes a commit start node so that during
+mount, UBIFS will seek for the most recent commit start node and just replay
+every reference node after that. Every reference node before the commit start
+node will be ignored as they are already part of the on-flash index.
+
+When writing a journal entry, UBIFS first ensures that enough space is
+available to write the reference node and buds part of this entry. Then, the
+reference node is written and afterwards the buds describing the file changes.
+On replay, UBIFS will record every reference node and inspect the location of
+the referenced LEBs to discover the buds. If these are corrupt or missing,
+UBIFS will attempt to recover them by re-reading the LEB. This is however only
+done for the last referenced LEB of the journal. Only this can become corrupt
+because of a power cut. If the recovery fails, UBIFS will not mount. An error
+for every other LEB will directly cause UBIFS to fail the mount operation.
+
+
+       | ----    LOG AREA     ---- | ----------    MAIN AREA    ------------ |
+
+        -----+------+-----+--------+----   ------+-----+-----+---------------
+        \    |      |     |        |   /  /      |     |     |               \
+        / CS |  REF | REF |        |   \  \ DENT | INO | INO |               /
+        \    |      |     |        |   /  /      |     |     |               \
+         ----+------+-----+--------+---   -------+-----+-----+----------------
+                 |     |                  ^            ^
+                 |     |                  |            |
+                 +------------------------+            |
+                       |                               |
+                       +-------------------------------+
+
+
+                Figure 2: UBIFS flash layout of log area with commit start nodes
+                          (CS) and reference nodes (REF) pointing to main area
+                          containing their buds
+
+
+### LEB Property Tree/Table
+
+The LEB property tree is used to store per-LEB information. This includes the
+LEB type and amount of free and *dirty* (old, obsolete content) space [1] on
+the LEB. The type is important, because UBIFS never mixes index nodes with data
+nodes on a single LEB and thus each LEB has a specific purpose. This again is
+useful for free space calculations. See [UBIFS-WP] for more details.
+
+The LEB property tree again is a B+ tree, but it is much smaller than the
+index. Due to its smaller size it is always written as one chunk on every
+commit. Thus, saving the LPT is an atomic operation.
+
+
+[1] Since LEBs can only be appended and never overwritten, there is a
+difference between free space ie. the remaining space left on the LEB to be
+written to without erasing it and previously written content that is obsolete
+but can't be overwritten without erasing the full LEB.
+
+
+# UBIFS Authentication
+
+This chapter introduces UBIFS authentication which enables UBIFS to verify
+the authenticity and integrity of metadata and file contents stored on flash.
+
+
+## Threat Model
+
+UBIFS authentication enables detection of offline data modification. While it
+does not prevent it, it enables (trusted) code to check the integrity and
+authenticity of on-flash file contents and filesystem metadata. This covers
+attacks where file contents are swapped.
+
+UBIFS authentication will not protect against rollback of full flash contents.
+Ie. an attacker can still dump the flash and restore it at a later time without
+detection. It will also not protect against partial rollback of individual
+index commits. That means that an attacker is able to partially undo changes.
+This is possible because UBIFS does not immediately overwrites obsolete
+versions of the index tree or the journal, but instead marks them as obsolete
+and garbage collection erases them at a later time. An attacker can use this by
+erasing parts of the current tree and restoring old versions that are still on
+the flash and have not yet been erased. This is possible, because every commit
+will always write a new version of the index root node and the master node
+without overwriting the previous version. This is further helped by the
+wear-leveling operations of UBI which copies contents from one physical
+eraseblock to another and does not atomically erase the first eraseblock.
+
+UBIFS authentication does not cover attacks where an attacker is able to
+execute code on the device after the authentication key was provided.
+Additional measures like secure boot and trusted boot have to be taken to
+ensure that only trusted code is executed on a device.
+
+
+## Authentication
+
+To be able to fully trust data read from flash, all UBIFS data structures
+stored on flash are authenticated. That is:
+
+- The index which includes file contents, file metadata like extended
+  attributes, file length etc.
+- The journal which also contains file contents and metadata by recording changes
+  to the filesystem
+- The LPT which stores UBI LEB metadata which UBIFS uses for free space accounting
+
+
+### Index Authentication
+
+Through UBIFS' concept of a wandering tree, it already takes care of only
+updating and persisting changed parts from leaf node up to the root node
+of the full B+ tree. This enables us to augment the index nodes of the tree
+with a hash over each node's child nodes. As a result, the index basically also
+a Merkle tree. Since the leaf nodes of the index contain the actual filesystem
+data, the hashes of their parent index nodes thus cover all the file contents
+and file metadata. When a file changes, the UBIFS index is updated accordingly
+from the leaf nodes up to the root node including the master node. This process
+can be hooked to recompute the hash only for each changed node at the same time.
+Whenever a file is read, UBIFS can verify the hashes from each leaf node up to
+the root node to ensure the node's integrity.
+
+To ensure the authenticity of the whole index, the UBIFS master node stores a
+keyed hash (HMAC) over its own contents and a hash of the root node of the index
+tree. As mentioned above, the master node is always written to the flash whenever
+the index is persisted (ie. on index commit).
+
+Using this approach only UBIFS index nodes and the master node are changed to
+include a hash. All other types of nodes will remain unchanged. This reduces
+the storage overhead which is precious for users of UBIFS (ie. embedded
+devices).
+
+
+                             +---------------+
+                             |  Master Node  |
+                             |    (hash)     |
+                             +---------------+
+                                     |
+                                     v
+                            +-------------------+
+                            |  Index Node #1    |
+                            |                   |
+                            | branch0   branchn |
+                            | (hash)    (hash)  |
+                            +-------------------+
+                               |    ...   |  (fanout: 8)
+                               |          |
+                       +-------+          +------+
+                       |                         |
+                       v                         v
+            +-------------------+       +-------------------+
+            |  Index Node #2    |       |  Index Node #3    |
+            |                   |       |                   |
+            | branch0   branchn |       | branch0   branchn |
+            | (hash)    (hash)  |       | (hash)    (hash)  |
+            +-------------------+       +-------------------+
+                 |   ...                     |   ...   |
+                 v                           v         v
+               +-----------+         +----------+  +-----------+
+               | Data Node |         | INO Node |  | DENT Node |
+               +-----------+         +----------+  +-----------+
+
+
+           Figure 3: Coverage areas of index node hash and master node HMAC
+
+
+
+The most important part for robustness and power-cut safety is to atomically
+persist the hash and file contents. Here the existing UBIFS logic for how
+changed nodes are persisted is already designed for this purpose such that
+UBIFS can safely recover if a power-cut occurs while persisting. Adding
+hashes to index nodes does not change this since each hash will be persisted
+atomically together with its respective node.
+
+
+### Journal Authentication
+
+The journal is authenticated too. Since the journal is continuously written
+it is necessary to also add authentication information frequently to the
+journal so that in case of a powercut not too much data can't be authenticated.
+This is done by creating a continuous hash beginning from the commit start node
+over the previous reference nodes, the current reference node, and the bud
+nodes. From time to time whenever it is suitable authentication nodes are added
+between the bud nodes. This new node type contains a HMAC over the current state
+of the hash chain. That way a journal can be authenticated up to the last
+authentication node. The tail of the journal which may not have a authentication
+node cannot be authenticated and is skipped during journal replay.
+
+We get this picture for journal authentication:
+
+    ,,,,,,,,
+    ,......,...........................................
+    ,. CS  ,               hash1.----.           hash2.----.
+    ,.  |  ,                    .    |hmac            .    |hmac
+    ,.  v  ,                    .    v                .    v
+    ,.REF#0,-> bud -> bud -> bud.-> auth -> bud -> bud.-> auth ...
+    ,..|...,...........................................
+    ,  |   ,
+    ,  |   ,,,,,,,,,,,,,,,
+    .  |            hash3,----.
+    ,  |                 ,    |hmac
+    ,  v                 ,    v
+    , REF#1 -> bud -> bud,-> auth ...
+    ,,,|,,,,,,,,,,,,,,,,,,
+       v
+      REF#2 -> ...
+       |
+       V
+      ...
+
+Since the hash also includes the reference nodes an attacker cannot reorder or
+skip any journal heads for replay. An attacker can only remove bud nodes or
+reference nodes from the end of the journal, effectively rewinding the
+filesystem at maximum back to the last commit.
+
+The location of the log area is stored in the master node. Since the master
+node is authenticated with a HMAC as described above, it is not possible to
+tamper with that without detection. The size of the log area is specified when
+the filesystem is created using `mkfs.ubifs` and stored in the superblock node.
+To avoid tampering with this and other values stored there, a HMAC is added to
+the superblock struct. The superblock node is stored in LEB 0 and is only
+modified on feature flag or similar changes, but never on file changes.
+
+
+### LPT Authentication
+
+The location of the LPT root node on the flash is stored in the UBIFS master
+node. Since the LPT is written and read atomically on every commit, there is
+no need to authenticate individual nodes of the tree. It suffices to
+protect the integrity of the full LPT by a simple hash stored in the master
+node. Since the master node itself is authenticated, the LPTs authenticity can
+be verified by verifying the authenticity of the master node and comparing the
+LTP hash stored there with the hash computed from the read on-flash LPT.
+
+
+## Key Management
+
+For simplicity, UBIFS authentication uses a single key to compute the HMACs
+of superblock, master, commit start and reference nodes. This key has to be
+available on creation of the filesystem (`mkfs.ubifs`) to authenticate the
+superblock node. Further, it has to be available on mount of the filesystem
+to verify authenticated nodes and generate new HMACs for changes.
+
+UBIFS authentication is intended to operate side-by-side with UBIFS encryption
+(fscrypt) to provide confidentiality and authenticity. Since UBIFS encryption
+has a different approach of encryption policies per directory, there can be
+multiple fscrypt master keys and there might be folders without encryption.
+UBIFS authentication on the other hand has an all-or-nothing approach in the
+sense that it either authenticates everything of the filesystem or nothing.
+Because of this and because UBIFS authentication should also be usable without
+encryption, it does not share the same master key with fscrypt, but manages
+a dedicated authentication key.
+
+The API for providing the authentication key has yet to be defined, but the
+key can eg. be provided by userspace through a keyring similar to the way it
+is currently done in fscrypt. It should however be noted that the current
+fscrypt approach has shown its flaws and the userspace API will eventually
+change [FSCRYPT-POLICY2].
+
+Nevertheless, it will be possible for a user to provide a single passphrase
+or key in userspace that covers UBIFS authentication and encryption. This can
+be solved by the corresponding userspace tools which derive a second key for
+authentication in addition to the derived fscrypt master key used for
+encryption.
+
+To be able to check if the proper key is available on mount, the UBIFS
+superblock node will additionally store a hash of the authentication key. This
+approach is similar to the approach proposed for fscrypt encryption policy v2
+[FSCRYPT-POLICY2].
+
+
+# Future Extensions
+
+In certain cases where a vendor wants to provide an authenticated filesystem
+image to customers, it should be possible to do so without sharing the secret
+UBIFS authentication key. Instead, in addition the each HMAC a digital
+signature could be stored where the vendor shares the public key alongside the
+filesystem image. In case this filesystem has to be modified afterwards,
+UBIFS can exchange all digital signatures with HMACs on first mount similar
+to the way the IMA/EVM subsystem deals with such situations. The HMAC key
+will then have to be provided beforehand in the normal way.
+
+
+# References
+
+[CRYPTSETUP2]        http://www.saout.de/pipermail/dm-crypt/2017-November/005745.html
+
+[DMC-CBC-ATTACK]     http://www.jakoblell.com/blog/2013/12/22/practical-malleability-attack-against-cbc-encrypted-luks-partitions/
+
+[DM-INTEGRITY]       https://www.kernel.org/doc/Documentation/device-mapper/dm-integrity.txt
+
+[DM-VERITY]          https://www.kernel.org/doc/Documentation/device-mapper/verity.txt
+
+[FSCRYPT-POLICY2]    https://www.spinics.net/lists/linux-ext4/msg58710.html
+
+[UBIFS-WP]           http://www.linux-mtd.infradead.org/doc/ubifs_whitepaper.pdf
-- 
2.18.0


^ permalink raw reply related	[flat|nested] 49+ messages in thread

* Re: [PATCH 14/25] ubifs: Add authentication nodes to journal
  2018-07-04 12:41 ` [PATCH 14/25] ubifs: Add authentication nodes to journal Sascha Hauer
@ 2018-07-08  2:59   ` kbuild test robot
  2018-08-27 20:48   ` Richard Weinberger
  1 sibling, 0 replies; 49+ messages in thread
From: kbuild test robot @ 2018-07-08  2:59 UTC (permalink / raw)
  To: Sascha Hauer
  Cc: kbuild-all, linux-mtd, David Gstir, Richard Weinberger, kernel,
	linux-kernel, Sascha Hauer

[-- Attachment #1: Type: text/plain, Size: 1047 bytes --]

Hi Sascha,

I love your patch! Yet something to improve:

[auto build test ERROR on linus/master]
[also build test ERROR on v4.18-rc3 next-20180706]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]

url:    https://github.com/0day-ci/linux/commits/Sascha-Hauer/UBIFS-authentication-support/20180705-043506
config: arm-aspeed_g4_defconfig (attached as .config)
compiler: arm-linux-gnueabi-gcc (Debian 7.2.0-11) 7.2.0
reproduce:
        wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # save the attached .config to linux build tree
        GCC_VERSION=7.2.0 make.cross ARCH=arm 

All errors (new ones prefixed by >>):

   fs/ubifs/log.o: In function `ubifs_log_start_commit':
>> log.c:(.text+0x9ac): undefined reference to `crypto_shash_update'

---
0-DAY kernel test infrastructure                Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all                   Intel Corporation

[-- Attachment #2: .config.gz --]
[-- Type: application/gzip, Size: 16309 bytes --]

^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH 16/25] ubifs: authenticate replayed journal
  2018-07-04 12:41 ` [PATCH 16/25] ubifs: authenticate replayed journal Sascha Hauer
@ 2018-07-08  6:08   ` kbuild test robot
  2018-08-27 21:16   ` Richard Weinberger
  1 sibling, 0 replies; 49+ messages in thread
From: kbuild test robot @ 2018-07-08  6:08 UTC (permalink / raw)
  To: Sascha Hauer
  Cc: kbuild-all, linux-mtd, David Gstir, Richard Weinberger, kernel,
	linux-kernel, Sascha Hauer

[-- Attachment #1: Type: text/plain, Size: 1177 bytes --]

Hi Sascha,

I love your patch! Yet something to improve:

[auto build test ERROR on linus/master]
[also build test ERROR on v4.18-rc3 next-20180706]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]

url:    https://github.com/0day-ci/linux/commits/Sascha-Hauer/UBIFS-authentication-support/20180705-043506
config: arm-aspeed_g4_defconfig (attached as .config)
compiler: arm-linux-gnueabi-gcc (Debian 7.2.0-11) 7.2.0
reproduce:
        wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # save the attached .config to linux build tree
        GCC_VERSION=7.2.0 make.cross ARCH=arm 

All errors (new ones prefixed by >>):

   fs/ubifs/replay.o: In function `ubifs_replay_journal':
>> replay.c:(.text+0x794): undefined reference to `crypto_shash_update'
   fs/ubifs/log.o: In function `ubifs_log_start_commit':
   log.c:(.text+0x9ac): undefined reference to `crypto_shash_update'

---
0-DAY kernel test infrastructure                Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all                   Intel Corporation

[-- Attachment #2: .config.gz --]
[-- Type: application/gzip, Size: 16309 bytes --]

^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH 05/25] ubifs: implement ubifs_lpt_lookup using ubifs_pnode_lookup
  2018-07-04 12:41 ` [PATCH 05/25] ubifs: implement ubifs_lpt_lookup using ubifs_pnode_lookup Sascha Hauer
@ 2018-08-13  6:31   ` Sascha Hauer
  2018-08-13  6:34     ` Richard Weinberger
  2018-08-26 20:59     ` Richard Weinberger
  0 siblings, 2 replies; 49+ messages in thread
From: Sascha Hauer @ 2018-08-13  6:31 UTC (permalink / raw)
  To: linux-mtd; +Cc: Richard Weinberger, David Gstir, linux-kernel, kernel

On Wed, Jul 04, 2018 at 02:41:17PM +0200, Sascha Hauer wrote:
> ubifs_lpt_lookup() starts by looking up the nth pnode in the LPT. We
> already have this functionality in ubifs_pnode_lookup(). Use this
> function rather than open coding its functionality.
> 
> Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
> ---
>  fs/ubifs/lpt.c | 20 ++------------------
>  1 file changed, 2 insertions(+), 18 deletions(-)
> 
> diff --git a/fs/ubifs/lpt.c b/fs/ubifs/lpt.c
> index 6cd6f23d4512..cde7b9484157 100644
> --- a/fs/ubifs/lpt.c
> +++ b/fs/ubifs/lpt.c
> @@ -1478,27 +1478,11 @@ struct ubifs_pnode *ubifs_pnode_lookup(struct ubifs_info *c, int i)
>   */
>  struct ubifs_lprops *ubifs_lpt_lookup(struct ubifs_info *c, int lnum)
>  {
> -	int err, i, h, iip, shft;
> -	struct ubifs_nnode *nnode;
> +	int i, iip;
>  	struct ubifs_pnode *pnode;
>  
> -	if (!c->nroot) {
> -		err = ubifs_read_nnode(c, NULL, 0);
> -		if (err)
> -			return ERR_PTR(err);
> -	}
> -	nnode = c->nroot;
>  	i = lnum - c->main_first;
> -	shft = c->lpt_hght * UBIFS_LPT_FANOUT_SHIFT;
> -	for (h = 1; h < c->lpt_hght; h++) {
> -		iip = ((i >> shft) & (UBIFS_LPT_FANOUT - 1));
> -		shft -= UBIFS_LPT_FANOUT_SHIFT;
> -		nnode = ubifs_get_nnode(c, nnode, iip);
> -		if (IS_ERR(nnode))
> -			return ERR_CAST(nnode);
> -	}
> -	iip = ((i >> shft) & (UBIFS_LPT_FANOUT - 1));
> -	pnode = ubifs_get_pnode(c, nnode, iip);
> +	pnode = ubifs_pnode_lookup(c, i);

This should be

	ubifs_pnode_lookup(c, i >> UBIFS_LPT_FANOUT_SHIFT);

Sascha

-- 
Pengutronix e.K.                           |                             |
Industrial Linux Solutions                 | http://www.pengutronix.de/  |
Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |

^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH 05/25] ubifs: implement ubifs_lpt_lookup using ubifs_pnode_lookup
  2018-08-13  6:31   ` Sascha Hauer
@ 2018-08-13  6:34     ` Richard Weinberger
  2018-08-13  8:12       ` Sascha Hauer
  2018-08-26 20:59     ` Richard Weinberger
  1 sibling, 1 reply; 49+ messages in thread
From: Richard Weinberger @ 2018-08-13  6:34 UTC (permalink / raw)
  To: Sascha Hauer; +Cc: linux-mtd, David Gstir, linux-kernel, kernel

Am Montag, 13. August 2018, 08:31:27 CEST schrieb Sascha Hauer:
> On Wed, Jul 04, 2018 at 02:41:17PM +0200, Sascha Hauer wrote:
> > ubifs_lpt_lookup() starts by looking up the nth pnode in the LPT. We
> > already have this functionality in ubifs_pnode_lookup(). Use this
> > function rather than open coding its functionality.
> > 
> > Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
> > ---
> >  fs/ubifs/lpt.c | 20 ++------------------
> >  1 file changed, 2 insertions(+), 18 deletions(-)
> > 
> > diff --git a/fs/ubifs/lpt.c b/fs/ubifs/lpt.c
> > index 6cd6f23d4512..cde7b9484157 100644
> > --- a/fs/ubifs/lpt.c
> > +++ b/fs/ubifs/lpt.c
> > @@ -1478,27 +1478,11 @@ struct ubifs_pnode *ubifs_pnode_lookup(struct ubifs_info *c, int i)
> >   */
> >  struct ubifs_lprops *ubifs_lpt_lookup(struct ubifs_info *c, int lnum)
> >  {
> > -	int err, i, h, iip, shft;
> > -	struct ubifs_nnode *nnode;
> > +	int i, iip;
> >  	struct ubifs_pnode *pnode;
> >  
> > -	if (!c->nroot) {
> > -		err = ubifs_read_nnode(c, NULL, 0);
> > -		if (err)
> > -			return ERR_PTR(err);
> > -	}
> > -	nnode = c->nroot;
> >  	i = lnum - c->main_first;
> > -	shft = c->lpt_hght * UBIFS_LPT_FANOUT_SHIFT;
> > -	for (h = 1; h < c->lpt_hght; h++) {
> > -		iip = ((i >> shft) & (UBIFS_LPT_FANOUT - 1));
> > -		shft -= UBIFS_LPT_FANOUT_SHIFT;
> > -		nnode = ubifs_get_nnode(c, nnode, iip);
> > -		if (IS_ERR(nnode))
> > -			return ERR_CAST(nnode);
> > -	}
> > -	iip = ((i >> shft) & (UBIFS_LPT_FANOUT - 1));
> > -	pnode = ubifs_get_pnode(c, nnode, iip);
> > +	pnode = ubifs_pnode_lookup(c, i);
> 
> This should be
> 
> 	ubifs_pnode_lookup(c, i >> UBIFS_LPT_FANOUT_SHIFT);

Can you please add a helper function for that? These shift games are
always confusing and not easy to spot.

Thanks,
//richard




^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH 05/25] ubifs: implement ubifs_lpt_lookup using ubifs_pnode_lookup
  2018-08-13  6:34     ` Richard Weinberger
@ 2018-08-13  8:12       ` Sascha Hauer
  2018-08-13 11:30         ` Richard Weinberger
  0 siblings, 1 reply; 49+ messages in thread
From: Sascha Hauer @ 2018-08-13  8:12 UTC (permalink / raw)
  To: Richard Weinberger; +Cc: linux-mtd, David Gstir, linux-kernel, kernel

On Mon, Aug 13, 2018 at 08:34:00AM +0200, Richard Weinberger wrote:
> Am Montag, 13. August 2018, 08:31:27 CEST schrieb Sascha Hauer:
> > On Wed, Jul 04, 2018 at 02:41:17PM +0200, Sascha Hauer wrote:
> > > ubifs_lpt_lookup() starts by looking up the nth pnode in the LPT. We
> > > already have this functionality in ubifs_pnode_lookup(). Use this
> > > function rather than open coding its functionality.
> > > 
> > > Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
> > > ---
> > >  fs/ubifs/lpt.c | 20 ++------------------
> > >  1 file changed, 2 insertions(+), 18 deletions(-)
> > > 
> > > diff --git a/fs/ubifs/lpt.c b/fs/ubifs/lpt.c
> > > index 6cd6f23d4512..cde7b9484157 100644
> > > --- a/fs/ubifs/lpt.c
> > > +++ b/fs/ubifs/lpt.c
> > > @@ -1478,27 +1478,11 @@ struct ubifs_pnode *ubifs_pnode_lookup(struct ubifs_info *c, int i)
> > >   */
> > >  struct ubifs_lprops *ubifs_lpt_lookup(struct ubifs_info *c, int lnum)
> > >  {
> > > -	int err, i, h, iip, shft;
> > > -	struct ubifs_nnode *nnode;
> > > +	int i, iip;
> > >  	struct ubifs_pnode *pnode;
> > >  
> > > -	if (!c->nroot) {
> > > -		err = ubifs_read_nnode(c, NULL, 0);
> > > -		if (err)
> > > -			return ERR_PTR(err);
> > > -	}
> > > -	nnode = c->nroot;
> > >  	i = lnum - c->main_first;
> > > -	shft = c->lpt_hght * UBIFS_LPT_FANOUT_SHIFT;
> > > -	for (h = 1; h < c->lpt_hght; h++) {
> > > -		iip = ((i >> shft) & (UBIFS_LPT_FANOUT - 1));
> > > -		shft -= UBIFS_LPT_FANOUT_SHIFT;
> > > -		nnode = ubifs_get_nnode(c, nnode, iip);
> > > -		if (IS_ERR(nnode))
> > > -			return ERR_CAST(nnode);
> > > -	}
> > > -	iip = ((i >> shft) & (UBIFS_LPT_FANOUT - 1));
> > > -	pnode = ubifs_get_pnode(c, nnode, iip);
> > > +	pnode = ubifs_pnode_lookup(c, i);
> > 
> > This should be
> > 
> > 	ubifs_pnode_lookup(c, i >> UBIFS_LPT_FANOUT_SHIFT);
> 
> Can you please add a helper function for that? These shift games are
> always confusing and not easy to spot.

Do you have a suggestion for a helper function? lpt.c is full of rather
non obvious arithmetic operations and I couldn't find a helper function
that could be reused at least once in the code.

I tried with:

static int lnum_to_pnode_num(struct ubifs_info *c, int lnum)
{
	return (lnum - c->main_first) >> UBIFS_LPT_FANOUT_SHIFT;
}

But I am not sure this really improves things

Sascha

-- 
Pengutronix e.K.                           |                             |
Industrial Linux Solutions                 | http://www.pengutronix.de/  |
Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |

^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH 05/25] ubifs: implement ubifs_lpt_lookup using ubifs_pnode_lookup
  2018-08-13  8:12       ` Sascha Hauer
@ 2018-08-13 11:30         ` Richard Weinberger
  0 siblings, 0 replies; 49+ messages in thread
From: Richard Weinberger @ 2018-08-13 11:30 UTC (permalink / raw)
  To: Sascha Hauer; +Cc: linux-mtd, David Gstir, linux-kernel, kernel

Am Montag, 13. August 2018, 10:12:38 CEST schrieb Sascha Hauer:
> > Can you please add a helper function for that? These shift games are
> > always confusing and not easy to spot.
> 
> Do you have a suggestion for a helper function? lpt.c is full of rather
> non obvious arithmetic operations and I couldn't find a helper function
> that could be reused at least once in the code.
> 
> I tried with:
> 
> static int lnum_to_pnode_num(struct ubifs_info *c, int lnum)
> {
> 	return (lnum - c->main_first) >> UBIFS_LPT_FANOUT_SHIFT;
> }
> 
> But I am not sure this really improves things

I had exactly such helpers in mind.
The lpt code translates often LEB numbers in both direction.
So, the helpers should have more than one user.

Long story short, I don't force you to add such helper and such.
Just thought while you are here you can try to make the code more
readable.

I think we both agree that lpt.c is not really review friendly. ;)

Thanks,
//richard





^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH 05/25] ubifs: implement ubifs_lpt_lookup using ubifs_pnode_lookup
  2018-08-13  6:31   ` Sascha Hauer
  2018-08-13  6:34     ` Richard Weinberger
@ 2018-08-26 20:59     ` Richard Weinberger
  1 sibling, 0 replies; 49+ messages in thread
From: Richard Weinberger @ 2018-08-26 20:59 UTC (permalink / raw)
  To: Sascha Hauer
  Cc: linux-mtd @ lists . infradead . org, Richard Weinberger,
	David Gstir, LKML, kernel

On Mon, Aug 13, 2018 at 8:31 AM Sascha Hauer <s.hauer@pengutronix.de> wrote:
>
> On Wed, Jul 04, 2018 at 02:41:17PM +0200, Sascha Hauer wrote:
> > ubifs_lpt_lookup() starts by looking up the nth pnode in the LPT. We
> > already have this functionality in ubifs_pnode_lookup(). Use this
> > function rather than open coding its functionality.
> >
> > Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
> > ---
> >  fs/ubifs/lpt.c | 20 ++------------------
> >  1 file changed, 2 insertions(+), 18 deletions(-)
> >
> > diff --git a/fs/ubifs/lpt.c b/fs/ubifs/lpt.c
> > index 6cd6f23d4512..cde7b9484157 100644
> > --- a/fs/ubifs/lpt.c
> > +++ b/fs/ubifs/lpt.c
> > @@ -1478,27 +1478,11 @@ struct ubifs_pnode *ubifs_pnode_lookup(struct ubifs_info *c, int i)
> >   */
> >  struct ubifs_lprops *ubifs_lpt_lookup(struct ubifs_info *c, int lnum)
> >  {
> > -     int err, i, h, iip, shft;
> > -     struct ubifs_nnode *nnode;
> > +     int i, iip;
> >       struct ubifs_pnode *pnode;
> >
> > -     if (!c->nroot) {
> > -             err = ubifs_read_nnode(c, NULL, 0);
> > -             if (err)
> > -                     return ERR_PTR(err);
> > -     }
> > -     nnode = c->nroot;
> >       i = lnum - c->main_first;
> > -     shft = c->lpt_hght * UBIFS_LPT_FANOUT_SHIFT;
> > -     for (h = 1; h < c->lpt_hght; h++) {
> > -             iip = ((i >> shft) & (UBIFS_LPT_FANOUT - 1));
> > -             shft -= UBIFS_LPT_FANOUT_SHIFT;
> > -             nnode = ubifs_get_nnode(c, nnode, iip);
> > -             if (IS_ERR(nnode))
> > -                     return ERR_CAST(nnode);
> > -     }
> > -     iip = ((i >> shft) & (UBIFS_LPT_FANOUT - 1));
> > -     pnode = ubifs_get_pnode(c, nnode, iip);
> > +     pnode = ubifs_pnode_lookup(c, i);
>
> This should be
>
>         ubifs_pnode_lookup(c, i >> UBIFS_LPT_FANOUT_SHIFT);

FWIW, I've rebased the whole series to Linus as of today, the merge
window introduced some minor conflicts.
http://git.infradead.org/users/rw/linux.git/shortlog/refs/heads/ubifs_auth_wip

The above fix is included.

Thanks,
//richard

^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH 10/25] ubifs: add helper functions for authentication support
  2018-07-04 12:41 ` [PATCH 10/25] ubifs: add helper functions for authentication support Sascha Hauer
@ 2018-08-27 12:50   ` Richard Weinberger
  2018-08-29  6:30     ` Sascha Hauer
  0 siblings, 1 reply; 49+ messages in thread
From: Richard Weinberger @ 2018-08-27 12:50 UTC (permalink / raw)
  To: Sascha Hauer; +Cc: linux-mtd, David Gstir, kernel, linux-kernel

Am Mittwoch, 4. Juli 2018, 14:41:22 CEST schrieb Sascha Hauer:
> This patch adds the various helper functions needed for authentication
> support. We need functions to hash nodes, to embed HMACs into a node and
> to compare hashes and HMACs. Most functions first check if this
> filesystem is authenticated and bail out early if not, which makes the
> functions safe to be called with disabled authentication.
> 
> Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
> ---
>  fs/ubifs/Kconfig  |   1 +
>  fs/ubifs/Makefile |   1 +
>  fs/ubifs/auth.c   | 413 ++++++++++++++++++++++++++++++++++++++++++++++
>  fs/ubifs/ubifs.h  | 187 +++++++++++++++++++++
>  4 files changed, 602 insertions(+)
>  create mode 100644 fs/ubifs/auth.c
> 
> diff --git a/fs/ubifs/Kconfig b/fs/ubifs/Kconfig
> index 83a961bf7280..2a0b5f1134a4 100644
> --- a/fs/ubifs/Kconfig
> +++ b/fs/ubifs/Kconfig
> @@ -7,6 +7,7 @@ config UBIFS_FS
>  	select CRYPTO if UBIFS_FS_ZLIB
>  	select CRYPTO_LZO if UBIFS_FS_LZO
>  	select CRYPTO_DEFLATE if UBIFS_FS_ZLIB
> +	select CRYPTO_HASH_INFO
>  	depends on MTD_UBI
>  	help
>  	  UBIFS is a file system for flash devices which works on top of UBI.
> diff --git a/fs/ubifs/Makefile b/fs/ubifs/Makefile
> index 9758f709c736..2bdb8ae2f435 100644
> --- a/fs/ubifs/Makefile
> +++ b/fs/ubifs/Makefile
> @@ -7,3 +7,4 @@ ubifs-y += budget.o find.o tnc_commit.o compress.o lpt.o lprops.o
>  ubifs-y += recovery.o ioctl.o lpt_commit.o tnc_misc.o xattr.o debug.o
>  ubifs-y += misc.o
>  ubifs-$(CONFIG_UBIFS_FS_ENCRYPTION) += crypto.o
> +ubifs-$(CONFIG_UBIFS_FS_AUTHENTICATION) += auth.o
> diff --git a/fs/ubifs/auth.c b/fs/ubifs/auth.c
> new file mode 100644
> index 000000000000..fd21f2ec8734
> --- /dev/null
> +++ b/fs/ubifs/auth.c
> @@ -0,0 +1,413 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * This file is part of UBIFS.
> + *
> + * Copyright (C) 2018 Pengutronix, Sascha Hauer <s.hauer@pengutronix.de>
> + */
> +
> +/*
> + * This file implements various helper functions for UBIFS authentication support
> + */
> +
> +#include <linux/crypto.h>
> +#include <crypto/hash.h>
> +#include <crypto/sha.h>
> +#include <crypto/algapi.h>
> +#include <keys/user-type.h>
> +
> +#include "ubifs.h"
> +
> +/**
> + * ubifs_node_calc_hash - calculate the hash of a UBIFS node
> + * @c: UBIFS file-system description object
> + * @node: the node to calculate a hash for
> + * @hash: the returned hash
> + */
> +void __ubifs_node_calc_hash(const struct ubifs_info *c, const void *node,
> +			    u8 *hash)
> +{
> +	const struct ubifs_ch *ch = node;
> +	SHASH_DESC_ON_STACK(shash, c->hash_tfm);
> +
> +	shash->tfm = c->hash_tfm;
> +	shash->flags = CRYPTO_TFM_REQ_MAY_SLEEP;
> +
> +	crypto_shash_digest(shash, node, le32_to_cpu(ch->len), hash);
> +}
> +
> +/**
> + * ubifs_hash_calc_hmac - calculate a HMAC from a hash
> + * @c: UBIFS file-system description object
> + * @hash: the node to calculate a HMAC for
> + * @hmac: the returned HMAC
> + */
> +static void ubifs_hash_calc_hmac(const struct ubifs_info *c, const u8 *hash,
> +				 u8 *hmac)
> +{
> +	SHASH_DESC_ON_STACK(shash, c->hmac_tfm);
> +
> +	shash->tfm = c->hmac_tfm;
> +	shash->flags = CRYPTO_TFM_REQ_MAY_SLEEP;
> +
> +	crypto_shash_digest(shash, hash, c->hash_len, hmac);

Crypto functions can fail, you need to check the return value.
(Please check also other call sites)

> +}
> +
> +/**
> + * ubifs_prepare_auth_node - Prepare an authentication node
> + * @c: UBIFS file-system description object
> + * @node: the node to calculate a hash for
> + * @hash: input hash of previous nodes
> + *
> + * This function prepares an authentication node for writing onto flash.
> + * It creates a HMAC from the given input hash and writes it to the node.
> + */
> +void ubifs_prepare_auth_node(struct ubifs_info *c, void *node,
> +			     struct shash_desc *inhash)
> +{
> +	SHASH_DESC_ON_STACK(hash_desc, c->hash_tfm);
> +	struct ubifs_auth_node *auth = node;
> +	u8 hash[crypto_shash_descsize(c->hash_tfm)];

Doesn't this introduce a new VLA?
Not that me make Kees unhappy. ;-)

> +	hash_desc->tfm = c->hash_tfm;
> +	hash_desc->flags = CRYPTO_TFM_REQ_MAY_SLEEP;
> +	ubifs_shash_copy_state(c, inhash, hash_desc);
> +	crypto_shash_final(hash_desc, hash);
> +
> +	ubifs_hash_calc_hmac(c, hash, auth->hmac);
> +
> +	auth->ch.node_type = UBIFS_AUTH_NODE;
> +	ubifs_prepare_node(c, auth, ubifs_auth_node_sz(c), 0);
> +}
> +
> +static struct shash_desc *ubifs_get_desc(const struct ubifs_info *c,
> +					 struct crypto_shash *tfm)
> +{
> +	struct shash_desc *desc;
> +	int err;
> +
> +	if (!ubifs_authenticated(c))
> +		return NULL;
> +
> +	desc = kmalloc(sizeof(*desc) + crypto_shash_descsize(tfm), GFP_KERNEL);
> +	if (!desc)
> +		return ERR_PTR(-ENOMEM);
> +
> +	desc->tfm = tfm;
> +	desc->flags = CRYPTO_TFM_REQ_MAY_SLEEP;
> +
> +	err = crypto_shash_init(desc);
> +	if (err) {
> +		kfree(desc);
> +		return ERR_PTR(err);
> +	}
> +
> +	return desc;
> +}
> +
> +/**
> + * __ubifs_hash_get_desc - get a descriptor suitable for hashing a node
> + * @c: UBIFS file-system description object
> + *
> + * This function returns a descriptor suitable for hashing a node. Free after use
> + * with kfree.
> + */
> +struct shash_desc *__ubifs_hash_get_desc(const struct ubifs_info *c)
> +{
> +	return ubifs_get_desc(c, c->hash_tfm);
> +}
> +
> +/**
> + * __ubifs_shash_final - finalize shash
> + * @c: UBIFS file-system description object
> + * @desc: the descriptor
> + * @out: the output hash
> + *
> + * Simple wrapper around crypto_shash_final(), safe to be called with
> + * disabled authentication.
> + */
> +int __ubifs_shash_final(const struct ubifs_info *c, struct shash_desc *desc,
> +			u8 *out)
> +{
> +	if (ubifs_authenticated(c))
> +		return crypto_shash_final(desc, out);
> +
> +	return 0;
> +}
> +
> +/**
> + * __ubifs_node_check_hash - check the hash of a node against given hash
> + * @c: UBIFS file-system description object
> + * @node: the node
> + * @expected: the expected hash
> + *
> + * This function calculates a hash over a node and compares it to the given hash.
> + * Returns 0 if both hashes are equal or authentication is disabled, otherwise a
> + * negative error code is returned.
> + */
> +int __ubifs_node_check_hash(const struct ubifs_info *c, void *node,
> +			    const u8 *expected)
> +{
> +	u8 calc[UBIFS_MAX_HASH_LEN];
> +
> +	__ubifs_node_calc_hash(c, node, calc);
> +
> +	if (ubifs_check_hash(c, expected, calc))
> +		return -EPERM;

Not sure if this action isn't a little too weak.
If a node's hash is not valid we are already in deep trouble and I'm not sure
whether it makes sense to trust the filesystem at all anymore.

But that's in general something I need to sort out with VFS/fscrypt folks.

> +	return 0;
> +}
> +
> +/**
> + * ubifs_init_authentication - initialize UBIFS authentication support
> + * @c: UBIFS file-system description object
> + *
> + * This function returns 0 for success or a negative error code otherwise.
> + */
> +int ubifs_init_authentication(struct ubifs_info *c)
> +{
> +	struct key *keyring_key;
> +	const struct user_key_payload *ukp;
> +	int err;
> +	char hmac_name[CRYPTO_MAX_ALG_NAME];
> +
> +	if (!c->auth_hash_name) {
> +		ubifs_err(c, "authentication hash name needed with authentication");
> +		return -EINVAL;
> +	}
> +
> +	c->auth_hash_algo = match_string(hash_algo_name, HASH_ALGO__LAST,
> +					 c->auth_hash_name);
> +	if ((int)c->auth_hash_algo < 0) {
> +		ubifs_err(c, "Unknown hash algo %s specified",
> +			  c->auth_hash_name);
> +		return -EINVAL;
> +	}
> +
> +	snprintf(hmac_name, CRYPTO_MAX_ALG_NAME, "hmac(%s)",
> +		 c->auth_hash_name);
> +
> +	keyring_key = request_key(&key_type_logon, c->auth_key_name, NULL);
> +
> +	if (IS_ERR(keyring_key)) {
> +		ubifs_err(c, "Failed to request key: %ld",
> +			  PTR_ERR(keyring_key));
> +		return PTR_ERR(keyring_key);
> +	}
> +
> +	down_read(&keyring_key->sem);
> +
> +	if (keyring_key->type != &key_type_logon) {
> +		ubifs_err(c, "key type must be logon");
> +		err = -ENOKEY;
> +		goto out;
> +	}
> +
> +	ukp = user_key_payload_locked(keyring_key);
> +	if (!ukp) {
> +		/* key was revoked before we acquired its semaphore */
> +		err = -EKEYREVOKED;
> +		goto out;
> +	}
> +
> +	c->hash_tfm = crypto_alloc_shash(c->auth_hash_name, 0,
> +					 CRYPTO_ALG_ASYNC);
> +	if (IS_ERR(c->hash_tfm)) {
> +		err = PTR_ERR(c->hash_tfm);
> +		ubifs_err(c, "Can not allocate %s: %d",
> +			  c->auth_hash_name, err);
> +		goto out;
> +	}
> +
> +	c->hash_len = crypto_shash_digestsize(c->hash_tfm);
> +	if (c->hash_len > UBIFS_MAX_HASH_LEN) {
> +		ubifs_err(c, "hash %s is bigger than maximum allowed hash size (%d > %d)",
> +			  c->auth_hash_name, c->hash_len, UBIFS_MAX_HASH_LEN);
> +		err = -EINVAL;
> +		goto out_free_hash;
> +	}
> +
> +	c->hmac_tfm = crypto_alloc_shash(hmac_name, 0, CRYPTO_ALG_ASYNC);
> +	if (IS_ERR(c->hmac_tfm)) {
> +		err = PTR_ERR(c->hmac_tfm);
> +		ubifs_err(c, "Can not allocate %s: %d", hmac_name, err);
> +		goto out_free_hash;
> +	}
> +
> +	c->hmac_desc_len = crypto_shash_digestsize(c->hmac_tfm);
> +	if (c->hmac_desc_len > UBIFS_MAX_HMAC_LEN) {
> +		ubifs_err(c, "hmac %s is bigger than maximum allowed hmac size (%d > %d)",
> +			  hmac_name, c->hmac_desc_len, UBIFS_MAX_HMAC_LEN);
> +		err = -EINVAL;
> +		goto out_free_hash;
> +	}
> +
> +	err = crypto_shash_setkey(c->hmac_tfm, ukp->data, ukp->datalen);
> +	if (err)
> +		goto out_free_hmac;
> +
> +	c->authenticated = true;
> +
> +	c->log_hash = ubifs_hash_get_desc(c);
> +	if (IS_ERR(c->log_hash))
> +		goto out_free_hmac;
> +
> +	err = 0;
> +
> +out_free_hmac:
> +	if (err)
> +		crypto_free_shash(c->hmac_tfm);
> +out_free_hash:
> +	if (err)
> +		crypto_free_shash(c->hash_tfm);
> +out:
> +	up_read(&keyring_key->sem);
> +	key_put(keyring_key);
> +
> +	return err;
> +}
> +
> +/**
> + * __ubifs_exit_authentication - release resource
> + * @c: UBIFS file-system description object
> + *
> + * This function releases the authentication related resources.
> + */
> +void __ubifs_exit_authentication(struct ubifs_info *c)
> +{
> +	if (!ubifs_authenticated(c))
> +		return;
> +
> +	crypto_free_shash(c->hmac_tfm);
> +	crypto_free_shash(c->hash_tfm);
> +	kfree(c->log_hash);
> +}
> +
> +/**
> + * ubifs_node_calc_hmac - calculate the HMAC of a UBIFS node
> + * @c: UBIFS file-system description object
> + * @node: the node to insert a HMAC into.
> + * @len: the length of the node
> + * @ofs_hmac: the offset in the node where the HMAC is inserted
> + * @hmac: returned HMAC
> + *
> + * This function calculates a HMAC of a UBIFS node. The HMAC is expected to be
> + * embedded into the node, so this area is not covered by the HMAC. Also not
> + * covered is the UBIFS_NODE_MAGIC and the CRC of the node.
> + */
> +static void ubifs_node_calc_hmac(const struct ubifs_info *c, const void *node,
> +				 int len, int ofs_hmac, void *hmac)
> +{
> +	SHASH_DESC_ON_STACK(shash, c->hmac_tfm);
> +	int hmac_len = c->hmac_desc_len;
> +
> +	ubifs_assert(ofs_hmac > 8);
> +	ubifs_assert(ofs_hmac + hmac_len < len);
> +
> +	shash->tfm = c->hmac_tfm;
> +	shash->flags = CRYPTO_TFM_REQ_MAY_SLEEP;
> +
> +	crypto_shash_init(shash);
> +
> +	/* behind common node header CRC up to HMAC begin */
> +	crypto_shash_update(shash, node + 8, ofs_hmac - 8);
> +
> +	/* behind HMAC, if any */
> +	if (len - ofs_hmac - hmac_len > 0)
> +		crypto_shash_update(shash, node + ofs_hmac + hmac_len,
> +			    len - ofs_hmac - hmac_len);
> +
> +	crypto_shash_final(shash, hmac);
> +}
> +
> +/**
> + * __ubifs_node_insert_hmac - insert a HMAC into a UBIFS node
> + * @c: UBIFS file-system description object
> + * @node: the node to insert a HMAC into.
> + * @len: the length of the node
> + * @ofs_hmac: the offset in the node where the HMAC is inserted
> + *
> + * This function inserts a HMAC at offset @ofs_hmac into the node given in
> + * @node.
> + */
> +void __ubifs_node_insert_hmac(const struct ubifs_info *c, void *node, int len,
> +			    int ofs_hmac)
> +{
> +	ubifs_node_calc_hmac(c, node, len, ofs_hmac, node + ofs_hmac);
> +}
> +
> +/**
> + * __ubifs_node_verify_hmac - verify the HMAC of UBIFS node
> + * @c: UBIFS file-system description object
> + * @node: the node to insert a HMAC into.
> + * @len: the length of the node
> + * @ofs_hmac: the offset in the node where the HMAC is inserted
> + *
> + * This function verifies the HMAC at offset @ofs_hmac of the node given in
> + * @node. Returns 0 if successful or a negative error code otherwise.
> + */
> +int __ubifs_node_verify_hmac(const struct ubifs_info *c, const void *node,
> +			     int len, int ofs_hmac)
> +{
> +	int hmac_len = c->hmac_desc_len;
> +	u8 *hmac;
> +	int err;
> +
> +	hmac = kmalloc(hmac_len, GFP_NOFS);
> +	if (!hmac)
> +		return -ENOMEM;
> +
> +	ubifs_node_calc_hmac(c, node, len, ofs_hmac, hmac);
> +
> +	err = crypto_memneq(hmac, node + ofs_hmac, hmac_len);
> +
> +	kfree(hmac);
> +
> +	if (!err)
> +		return 0;
> +
> +	return -EPERM;

Same.

> +}
> +
> +int __ubifs_shash_copy_state(const struct ubifs_info *c, struct shash_desc *src,
> +			     struct shash_desc *target)
> +{
> +	u8 state[crypto_shash_descsize(src->tfm)];
> +	int err;
> +
> +	err = crypto_shash_export(src, state);
> +	if (err)
> +		return err;
> +
> +	return crypto_shash_import(target, state);
> +}
> +
> +/**
> + * ubifs_hmac_wkm - Create a HMAC of the well known message
> + * @c: UBIFS file-system description object
> + * @hmac: The HMAC of the well known message
> + *
> + * This function creates a HMAC of a well known message. This is used
> + * to check if the provided key is suitable to authenticate a UBIFS
> + * image. This is only a convenience to the user to provide a better
> + * error message when the wrong key is provided.
> + */
> +void ubifs_hmac_wkm(struct ubifs_info *c, u8 *hmac)
> +{
> +	SHASH_DESC_ON_STACK(shash, c->hmac_tfm);
> +	int err;
> +	const char well_known_message[] = "UBIFS";
> +
> +	if (!ubifs_authenticated(c))
> +		return;
> +
> +	shash->tfm = c->hmac_tfm;
> +	shash->flags = CRYPTO_TFM_REQ_MAY_SLEEP;
> +
> +	err = crypto_shash_init(shash);
> +	BUG_ON(err);

Huh? Do we really have to shoot the kernel in the head here?

Thanks,
//richard



^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH 07/25] ubifs: Store read superblock node
  2018-07-04 12:41 ` [PATCH 07/25] ubifs: Store read superblock node Sascha Hauer
@ 2018-08-27 12:50   ` Richard Weinberger
  0 siblings, 0 replies; 49+ messages in thread
From: Richard Weinberger @ 2018-08-27 12:50 UTC (permalink / raw)
  To: Sascha Hauer; +Cc: linux-mtd, David Gstir, kernel, linux-kernel

Am Mittwoch, 4. Juli 2018, 14:41:19 CEST schrieb Sascha Hauer:
> The superblock node is read/modified/written several times throughout
> the UBIFS code. Instead of reading it from the device each time just
> keep a copy in memory and write back the modified copy when necessary.
> This patch helps for authentication support, here we not only have to
> read the superblock node, but also have to authenticate it, which
> is easier if we do it once during initialization.
> 
> Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
> ---
>  fs/ubifs/sb.c    | 19 +++++--------------
>  fs/ubifs/super.c |  8 +-------
>  fs/ubifs/ubifs.h |  3 ++-
>  3 files changed, 8 insertions(+), 22 deletions(-)
> 
> diff --git a/fs/ubifs/sb.c b/fs/ubifs/sb.c
> index a32408f2f5c3..2fabd441d74a 100644
> --- a/fs/ubifs/sb.c
> +++ b/fs/ubifs/sb.c
> @@ -497,7 +497,7 @@ static int validate_sb(struct ubifs_info *c, struct ubifs_sb_node *sup)
>   * code. Note, the user of this function is responsible of kfree()'ing the
>   * returned superblock buffer.
>   */
> -struct ubifs_sb_node *ubifs_read_sb_node(struct ubifs_info *c)
> +static struct ubifs_sb_node *ubifs_read_sb_node(struct ubifs_info *c)
>  {
>  	struct ubifs_sb_node *sup;
>  	int err;
> @@ -554,6 +554,8 @@ int ubifs_read_superblock(struct ubifs_info *c)
>  	if (IS_ERR(sup))
>  		return PTR_ERR(sup);
>  
> +	c->superblock = sup;
> +
>  	c->fmt_version = le32_to_cpu(sup->fmt_version);
>  	c->ro_compat_version = le32_to_cpu(sup->ro_compat_version);
>  
> @@ -685,7 +687,6 @@ int ubifs_read_superblock(struct ubifs_info *c)
>  
>  	err = validate_sb(c, sup);
>  out:
> -	kfree(sup);
>  	return err;
>  }
>  
> @@ -814,7 +815,7 @@ static int fixup_free_space(struct ubifs_info *c)
>  int ubifs_fixup_free_space(struct ubifs_info *c)
>  {
>  	int err;
> -	struct ubifs_sb_node *sup;
> +	struct ubifs_sb_node *sup = c->superblock;
>  
>  	ubifs_assert(c->space_fixup);
>  	ubifs_assert(!c->ro_mount);
> @@ -825,16 +826,11 @@ int ubifs_fixup_free_space(struct ubifs_info *c)
>  	if (err)
>  		return err;
>  
> -	sup = ubifs_read_sb_node(c);
> -	if (IS_ERR(sup))
> -		return PTR_ERR(sup);
> -
>  	/* Free-space fixup is no longer required */
>  	c->space_fixup = 0;
>  	sup->flags &= cpu_to_le32(~UBIFS_FLG_SPACE_FIXUP);
>  
>  	err = ubifs_write_sb_node(c, sup);
> -	kfree(sup);
>  	if (err)
>  		return err;
>  
> @@ -845,7 +841,7 @@ int ubifs_fixup_free_space(struct ubifs_info *c)
>  int ubifs_enable_encryption(struct ubifs_info *c)
>  {
>  	int err;
> -	struct ubifs_sb_node *sup;
> +	struct ubifs_sb_node *sup = c->superblock;
>  
>  	if (c->encrypted)
>  		return 0;
> @@ -858,16 +854,11 @@ int ubifs_enable_encryption(struct ubifs_info *c)
>  		return -EINVAL;
>  	}
>  
> -	sup = ubifs_read_sb_node(c);
> -	if (IS_ERR(sup))
> -		return PTR_ERR(sup);
> -
>  	sup->flags |= cpu_to_le32(UBIFS_FLG_ENCRYPTION);
>  
>  	err = ubifs_write_sb_node(c, sup);
>  	if (!err)
>  		c->encrypted = 1;
> -	kfree(sup);
>  
>  	return err;
>  }
> diff --git a/fs/ubifs/super.c b/fs/ubifs/super.c
> index c5466c70d620..9e44baec30a3 100644
> --- a/fs/ubifs/super.c
> +++ b/fs/ubifs/super.c
> @@ -1580,16 +1580,10 @@ static int ubifs_remount_rw(struct ubifs_info *c)
>  		goto out;
>  
>  	if (c->old_leb_cnt != c->leb_cnt) {
> -		struct ubifs_sb_node *sup;
> +		struct ubifs_sb_node *sup = c->superblock;
>  
> -		sup = ubifs_read_sb_node(c);
> -		if (IS_ERR(sup)) {
> -			err = PTR_ERR(sup);
> -			goto out;
> -		}
>  		sup->leb_cnt = cpu_to_le32(c->leb_cnt);
>  		err = ubifs_write_sb_node(c, sup);
> -		kfree(sup);
>  		if (err)
>  			goto out;
>  	}
> diff --git a/fs/ubifs/ubifs.h b/fs/ubifs/ubifs.h
> index 83c5e75ed3b6..92574045e030 100644
> --- a/fs/ubifs/ubifs.h
> +++ b/fs/ubifs/ubifs.h
> @@ -971,6 +971,7 @@ struct ubifs_debug_info;
>   * struct ubifs_info - UBIFS file-system description data structure
>   * (per-superblock).
>   * @vfs_sb: VFS @struct super_block object
> + * @superblock: The super block node as read from the device

Please name it sup_node to have consistent naming scheme.
We already store the master node, @mst_node.

Thanks,
//richard



^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH 12/25] ubifs: Add hashes to the tree node cache
  2018-07-04 12:41 ` [PATCH 12/25] ubifs: Add hashes to the tree node cache Sascha Hauer
@ 2018-08-27 19:18   ` Richard Weinberger
  2018-08-29 11:16     ` Sascha Hauer
  0 siblings, 1 reply; 49+ messages in thread
From: Richard Weinberger @ 2018-08-27 19:18 UTC (permalink / raw)
  To: Sascha Hauer; +Cc: linux-mtd, David Gstir, kernel, linux-kernel

Am Mittwoch, 4. Juli 2018, 14:41:24 CEST schrieb Sascha Hauer:
> As part of the UBIFS authentication support every branch in the index
> gets a hash covering the referenced node. To make that happen the tree
> node cache needs hashes over the nodes. This patch adds a hash argument
> to ubifs_tnc_add() and ubifs_tnc_add_nm(). The hashes are calculated
> from the callers of these functions which actually prepare the nodes.
> With this patch all the leaf nodes of the index tree get hashes, but
> currently nothing is done with these hashes, this is left for a later
> patch.
> 
> Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
> ---
>  fs/ubifs/journal.c | 93 ++++++++++++++++++++++++++++++++++------------
>  fs/ubifs/replay.c  |  4 +-
>  fs/ubifs/tnc.c     | 10 ++++-
>  fs/ubifs/ubifs.h   |  7 +++-
>  4 files changed, 84 insertions(+), 30 deletions(-)
> 
> diff --git a/fs/ubifs/journal.c b/fs/ubifs/journal.c
> index 754d969eb27e..55b35bc33c31 100644
> --- a/fs/ubifs/journal.c
> +++ b/fs/ubifs/journal.c
> @@ -517,6 +517,9 @@ int ubifs_jnl_update(struct ubifs_info *c, const struct inode *dir,
>  	struct ubifs_dent_node *dent;
>  	struct ubifs_ino_node *ino;
>  	union ubifs_key dent_key, ino_key;
> +	u8 hash_dent[UBIFS_MAX_HASH_LEN];
> +	u8 hash_ino[UBIFS_MAX_HASH_LEN];
> +	u8 hash_ino_host[UBIFS_MAX_HASH_LEN];

With authentication enabled, this makes perfectly sense.
But if someone builds UBIFS without and has a memory constraint system,
we could save by setting UBIFS_MAX_HASH_LEN to 0. What do you think?
Although, we need another define to not influence ubifs-media.h.

Thanks,
//richard



^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH 13/25] ubifs: authentication: Add hashes to index nodes
  2018-07-04 12:41 ` [PATCH 13/25] ubifs: authentication: Add hashes to index nodes Sascha Hauer
@ 2018-08-27 19:36   ` Richard Weinberger
  2018-09-07 10:25     ` Sascha Hauer
  0 siblings, 1 reply; 49+ messages in thread
From: Richard Weinberger @ 2018-08-27 19:36 UTC (permalink / raw)
  To: Sascha Hauer; +Cc: linux-mtd, David Gstir, kernel, linux-kernel

Am Mittwoch, 4. Juli 2018, 14:41:25 CEST schrieb Sascha Hauer:
> 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 <s.hauer@pengutronix.de>
> ---
>  fs/ubifs/master.c     |  3 +++
>  fs/ubifs/misc.h       |  5 +++--
>  fs/ubifs/replay.c     | 29 ++++++++++++++++++-----------
>  fs/ubifs/tnc.c        | 13 +++++++++++++
>  fs/ubifs/tnc_commit.c | 26 ++++++++++++++++++++++++++
>  fs/ubifs/tnc_misc.c   | 16 +++++++++++++++-
>  fs/ubifs/ubifs.h      |  4 ++++
>  7 files changed, 82 insertions(+), 14 deletions(-)
> 

[...]

> diff --git a/fs/ubifs/tnc.c b/fs/ubifs/tnc.c
> index a47fced47823..a00809d4fe6f 100644
> --- a/fs/ubifs/tnc.c
> +++ b/fs/ubifs/tnc.c
> @@ -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_err(c, "hash mismatch on node at LEB %d:%d", lnum, offs);
> +		return 0;
> +	}

Hmm, I think a global "hash is bad" handler would be nice to have.
That way we always report in the same way.

Maybe also a new file system specific ioctl to query whether a hash
failure was noticed.

>  	return 1;
>  }
>  
> @@ -1713,6 +1719,13 @@ 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_err(c, "hash mismatch on node at LEB %d:%d",
> +			  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);
> diff --git a/fs/ubifs/tnc_commit.c b/fs/ubifs/tnc_commit.c
> index a9df94ad46a3..3ad78d538885 100644
> --- a/fs/ubifs/tnc_commit.c
> +++ b/fs/ubifs/tnc_commit.c
> @@ -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_MAX_HASH_LEN];
>  	int i, err;
>  
>  	/* Make index node */
> @@ -62,6 +63,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 +80,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 +651,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 +846,8 @@ static int write_index(struct ubifs_info *c)
>  	}
>  
>  	while (1) {
> +		u8 hash[UBIFS_MAX_HASH_LEN];
> +
>  		cond_resched();
>  
>  		znode = cnext;
> @@ -857,6 +865,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 +877,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);

This lock looks correct too me.
Just in case, you did test with lockdep enabled? :-)




^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH 14/25] ubifs: Add authentication nodes to journal
  2018-07-04 12:41 ` [PATCH 14/25] ubifs: Add authentication nodes to journal Sascha Hauer
  2018-07-08  2:59   ` kbuild test robot
@ 2018-08-27 20:48   ` Richard Weinberger
  2018-08-29 14:38     ` Sascha Hauer
  1 sibling, 1 reply; 49+ messages in thread
From: Richard Weinberger @ 2018-08-27 20:48 UTC (permalink / raw)
  To: Sascha Hauer; +Cc: linux-mtd, David Gstir, kernel, linux-kernel

Am Mittwoch, 4. Juli 2018, 14:41:26 CEST schrieb Sascha Hauer:
> Nodes that are written to flash can only be authenticated through the
> index after the next commit. When a journal replay is necessary the
> nodes are not yet referenced by the index and thus can't be
> authenticated.
> 
> This patch overcomes this situation by creating a hash over all nodes
> beginning from the commit start node over the reference node(s) and
> the buds themselves. From
> time to time we insert authentication nodes. Authentication nodes
> contain a HMAC from the current hash state, so that they can be
> used to authenticate a journal replay up to the point where the
> authentication node is. The hash is continued afterwards
> so that theoretically we would only have to check the HMAC of
> the last authentication node we find.
> 
> Overall we get this picture:
> 
> ,,,,,,,,
> ,......,...........................................
> ,. CS  ,               hash1.----.           hash2.----.
> ,.  |  ,                    .    |hmac            .    |hmac
> ,.  v  ,                    .    v                .    v
> ,.REF#0,-> bud -> bud -> bud.-> auth -> bud -> bud.-> auth ...
> ,..|...,...........................................
> ,  |   ,
> ,  |   ,,,,,,,,,,,,,,,
> .  |            hash3,----.
> ,  |                 ,    |hmac
> ,  v                 ,    v
> , REF#1 -> bud -> bud,-> auth ...
> ,,,|,,,,,,,,,,,,,,,,,,
>    v
>   REF#2 -> ...
>    |
>    V
>   ...
> 
> Note how hash3 covers CS, REF#0 and REF#1 so that it is not possible to
> exchange or skip any reference nodes. Unlike the picture suggests the
> auth nodes themselves are not hashed.
> 
> With this it is possible for an offline attacker to cut each journal
> head or to drop the last reference node(s), but not to skip any journal
> heads or to reorder any operations.
> 
> Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
> ---
>  fs/ubifs/gc.c      |   3 +-
>  fs/ubifs/journal.c | 110 ++++++++++++++++++++++++++++++++++++++-------
>  fs/ubifs/log.c     |  17 +++++++
>  fs/ubifs/replay.c  |   2 +
>  fs/ubifs/super.c   |  10 +++++
>  fs/ubifs/ubifs.h   |   8 ++++
>  6 files changed, 134 insertions(+), 16 deletions(-)
> 
> diff --git a/fs/ubifs/gc.c b/fs/ubifs/gc.c
> index a03a47cf880d..ac3a3f7c6a6e 100644
> --- a/fs/ubifs/gc.c
> +++ b/fs/ubifs/gc.c
> @@ -254,7 +254,8 @@ static int sort_nodes(struct ubifs_info *c, struct ubifs_scan_leb *sleb,
>  			     snod->type == UBIFS_DATA_NODE ||
>  			     snod->type == UBIFS_DENT_NODE ||
>  			     snod->type == UBIFS_XENT_NODE ||
> -			     snod->type == UBIFS_TRUN_NODE);
> +			     snod->type == UBIFS_TRUN_NODE ||
> +			     snod->type == UBIFS_AUTH_NODE);
>  
>  		if (snod->type != UBIFS_INO_NODE  &&
>  		    snod->type != UBIFS_DATA_NODE &&
> diff --git a/fs/ubifs/journal.c b/fs/ubifs/journal.c
> index 55b35bc33c31..4fde75623a3f 100644
> --- a/fs/ubifs/journal.c
> +++ b/fs/ubifs/journal.c
> @@ -228,6 +228,32 @@ static int reserve_space(struct ubifs_info *c, int jhead, int len)
>  	return err;
>  }
>  
> +static void ubifs_hash_nodes(struct ubifs_info *c, void *node,
> +			     int len, struct shash_desc *hash)
> +{
> +	int auth_node_size = ubifs_auth_node_sz(c);
> +
> +	while (1) {
> +		const struct ubifs_ch *ch = node;
> +		int nodelen = le32_to_cpu(ch->len);
> +
> +		ubifs_assert(len >= auth_node_size);
> +
> +		if (len == auth_node_size)
> +			break;
> +
> +		ubifs_assert(len > nodelen);
> +		ubifs_assert(ch->magic == cpu_to_le32(UBIFS_NODE_MAGIC));

A malformed UBIFS image can trigger that assert, right?
Please handle this without ubifs_assert() and abort with an error.
ubifs_assert() does not stop execution by default.

> +		ubifs_shash_update(c, hash, (void *)node, nodelen);
> +
> +		node += ALIGN(nodelen, 8);
> +		len -= ALIGN(nodelen, 8);
> +	}
> +
> +	ubifs_prepare_auth_node(c, node, hash);
> +}
> +
>  /**
>   * write_head - write data to a journal head.
>   * @c: UBIFS file-system description object
> @@ -255,6 +281,9 @@ static int write_head(struct ubifs_info *c, int jhead, void *buf, int len,
>  	dbg_jnl("jhead %s, LEB %d:%d, len %d",
>  		dbg_jhead(jhead), *lnum, *offs, len);
>  
> +	if (ubifs_authenticated(c))
> +		ubifs_hash_nodes(c, buf, len, c->jheads[jhead].log_hash);
> +
>  	err = ubifs_wbuf_write_nolock(wbuf, buf, len);
>  	if (err)
>  		return err;
> @@ -542,7 +571,9 @@ int ubifs_jnl_update(struct ubifs_info *c, const struct inode *dir,
>  
>  	len = aligned_dlen + aligned_ilen + UBIFS_INO_NODE_SZ;
>  	/* Make sure to also account for extended attributes */
> -	len += host_ui->data_len;
> +	len += ALIGN(host_ui->data_len, 8);

Hmm, this change seems unrelated.
Why do you need an ALIGN now?

> +	len += ubifs_auth_node_sz(c);
>  
>  	dent = kzalloc(len, GFP_NOFS);
>  	if (!dent)
> @@ -603,6 +634,7 @@ int ubifs_jnl_update(struct ubifs_info *c, const struct inode *dir,
>  	}
>  	release_head(c, BASEHD);
>  	kfree(dent);
> +	ubifs_add_dirt(c, lnum, ubifs_auth_node_sz(c));

You have to account it immediately because while a commit you have no longer
a reference to them?
Upon replay you should have since you scan LEBs anyway.

An shouldn't this only get called when the file system is authenticated?
AFAICT ubifs_add_dirt(c, lnum, 0) is not a no-op.

>  	if (deletion) {
>  		if (nm->hash)
> @@ -677,8 +709,9 @@ int ubifs_jnl_write_data(struct ubifs_info *c, const struct inode *inode,
>  			 const union ubifs_key *key, const void *buf, int len)
>  {
>  	struct ubifs_data_node *data;
> -	int err, lnum, offs, compr_type, out_len, compr_len;
> +	int err, lnum, offs, compr_type, out_len, compr_len, auth_len;
>  	int dlen = COMPRESSED_DATA_NODE_BUF_SZ, allocated = 1;
> +	int aligned_dlen;
>  	struct ubifs_inode *ui = ubifs_inode(inode);
>  	bool encrypted = ubifs_crypt_is_encrypted(inode);
>  	u8 hash[UBIFS_MAX_HASH_LEN];
> @@ -690,7 +723,9 @@ int ubifs_jnl_write_data(struct ubifs_info *c, const struct inode *inode,
>  	if (encrypted)
>  		dlen += UBIFS_CIPHER_BLOCK_SIZE;
>  
> -	data = kmalloc(dlen, GFP_NOFS | __GFP_NOWARN);
> +	auth_len = ubifs_auth_node_sz(c);
> +
> +	data = kmalloc(dlen + auth_len, GFP_NOFS | __GFP_NOWARN);
>  	if (!data) {
>  		/*
>  		 * Fall-back to the write reserve buffer. Note, we might be
> @@ -729,15 +764,16 @@ int ubifs_jnl_write_data(struct ubifs_info *c, const struct inode *inode,
>  	}
>  
>  	dlen = UBIFS_DATA_NODE_SZ + out_len;
> +	aligned_dlen = ALIGN(dlen, 8);
>  	data->compr_type = cpu_to_le16(compr_type);
>  
>  	/* Make reservation before allocating sequence numbers */
> -	err = make_reservation(c, DATAHD, dlen);
> +	err = make_reservation(c, DATAHD, aligned_dlen + auth_len);

Okay, now I understand the ALIGN(), ubifs nodes need to be aligned
at an 8 border. Makes sense, _but_ you change this also for the non-authenticated
case.

>  	if (err)
>  		goto out_free;
>  
>  	ubifs_prepare_node(c, data, dlen, 0);
> -	err = write_head(c, DATAHD, data, dlen, &lnum, &offs, 0);
> +	err = write_head(c, DATAHD, data, aligned_dlen + auth_len, &lnum, &offs, 0);
>  	if (err)
>  		goto out_release;
>  
> @@ -745,6 +781,7 @@ int ubifs_jnl_write_data(struct ubifs_info *c, const struct inode *inode,
>  
>  	ubifs_wbuf_add_ino_nolock(&c->jheads[DATAHD].wbuf, key_inum(c, key));
>  	release_head(c, DATAHD);
> +	ubifs_add_dirt(c, lnum, ubifs_auth_node_sz(c));
>  
>  	err = ubifs_tnc_add(c, key, lnum, offs, dlen, hash);
>  	if (err)
> @@ -784,7 +821,8 @@ int ubifs_jnl_write_inode(struct ubifs_info *c, const struct inode *inode)
>  	int err, lnum, offs;
>  	struct ubifs_ino_node *ino;
>  	struct ubifs_inode *ui = ubifs_inode(inode);
> -	int sync = 0, len = UBIFS_INO_NODE_SZ, last_reference = !inode->i_nlink;
> +	int sync = 0, len, ilen = UBIFS_INO_NODE_SZ, last_reference = !inode->i_nlink;
> +	int aligned_ilen;
>  	u8 hash[UBIFS_MAX_HASH_LEN];
>  
>  	dbg_jnl("ino %lu, nlink %u", inode->i_ino, inode->i_nlink);
> @@ -794,9 +832,14 @@ int ubifs_jnl_write_inode(struct ubifs_info *c, const struct inode *inode)
>  	 * need to synchronize the write-buffer either.
>  	 */
>  	if (!last_reference) {
> -		len += ui->data_len;
> +		ilen += ui->data_len;
>  		sync = IS_SYNC(inode);
>  	}
> +
> +	aligned_ilen = ALIGN(ilen, 8);
> +
> +	len = aligned_ilen + ubifs_auth_node_sz(c);
> +
>  	ino = kmalloc(len, GFP_NOFS);
>  	if (!ino)
>  		return -ENOMEM;
> @@ -816,17 +859,20 @@ int ubifs_jnl_write_inode(struct ubifs_info *c, const struct inode *inode)
>  					  inode->i_ino);
>  	release_head(c, BASEHD);
>  
> +	if (ubifs_authenticated(c))
> +		ubifs_add_dirt(c, lnum, ubifs_auth_node_sz(c));
> +
>  	if (last_reference) {
>  		err = ubifs_tnc_remove_ino(c, inode->i_ino);
>  		if (err)
>  			goto out_ro;
>  		ubifs_delete_orphan(c, inode->i_ino);
> -		err = ubifs_add_dirt(c, lnum, len);
> +		err = ubifs_add_dirt(c, lnum, ilen);
>  	} else {
>  		union ubifs_key key;
>  
>  		ino_key_init(c, &key, inode->i_ino);
> -		err = ubifs_tnc_add(c, &key, lnum, offs, len, hash);
> +		err = ubifs_tnc_add(c, &key, lnum, offs, ilen, hash);
>  	}
>  	if (err)
>  		goto out_ro;
> @@ -955,6 +1001,8 @@ int ubifs_jnl_xrename(struct ubifs_info *c, const struct inode *fst_dir,
>  	if (twoparents)
>  		len += plen;
>  
> +	len += ubifs_auth_node_sz(c);
> +
>  	dent1 = kzalloc(len, GFP_NOFS);
>  	if (!dent1)
>  		return -ENOMEM;
> @@ -1014,6 +1062,9 @@ int ubifs_jnl_xrename(struct ubifs_info *c, const struct inode *fst_dir,
>  	}
>  	release_head(c, BASEHD);
>  
> +	if (ubifs_authenticated(c))
> +		ubifs_add_dirt(c, lnum, ubifs_auth_node_sz(c));
> +
>  	dent_key_init(c, &key, snd_dir->i_ino, snd_nm);
>  	err = ubifs_tnc_add_nm(c, &key, lnum, offs, dlen1, hash_dent1, snd_nm);
>  	if (err)
> @@ -1115,6 +1166,9 @@ int ubifs_jnl_rename(struct ubifs_info *c, const struct inode *old_dir,
>  	len = aligned_dlen1 + aligned_dlen2 + ALIGN(ilen, 8) + ALIGN(plen, 8);
>  	if (move)
>  		len += plen;
> +
> +	len += ubifs_auth_node_sz(c);
> +
>  	dent = kzalloc(len, GFP_NOFS);
>  	if (!dent)
>  		return -ENOMEM;
> @@ -1198,6 +1252,9 @@ int ubifs_jnl_rename(struct ubifs_info *c, const struct inode *old_dir,
>  	}
>  	release_head(c, BASEHD);
>  
> +	if (ubifs_authenticated(c))
> +		ubifs_add_dirt(c, lnum, ubifs_auth_node_sz(c));

This is always the same pattern. How about adding a helper function?
ubifs_add_auth_dirt()?

>  	dent_key_init(c, &key, new_dir->i_ino, new_nm);
>  	err = ubifs_tnc_add_nm(c, &key, lnum, offs, dlen1, hash_dent1, new_nm);
>  	if (err)
> @@ -1356,6 +1413,7 @@ int ubifs_jnl_truncate(struct ubifs_info *c, const struct inode *inode,
>  	struct ubifs_trun_node *trun;
>  	struct ubifs_data_node *uninitialized_var(dn);
>  	int err, dlen, len, lnum, offs, bit, sz, sync = IS_SYNC(inode);
> +	int aligned_dlen;
>  	struct ubifs_inode *ui = ubifs_inode(inode);
>  	ino_t inum = inode->i_ino;
>  	unsigned int blk;
> @@ -1370,6 +1428,9 @@ int ubifs_jnl_truncate(struct ubifs_info *c, const struct inode *inode,
>  
>  	sz = UBIFS_TRUN_NODE_SZ + UBIFS_INO_NODE_SZ +
>  	     UBIFS_MAX_DATA_NODE_SZ * WORST_COMPR_FACTOR;
> +
> +	sz += ubifs_auth_node_sz(c);
> +
>  	ino = kmalloc(sz, GFP_NOFS);
>  	if (!ino)
>  		return -ENOMEM;
> @@ -1404,10 +1465,15 @@ int ubifs_jnl_truncate(struct ubifs_info *c, const struct inode *inode,
>  		}
>  	}
>  
> +	aligned_dlen = ALIGN(dlen, 8);
> +
>  	/* Must make reservation before allocating sequence numbers */
>  	len = UBIFS_TRUN_NODE_SZ + UBIFS_INO_NODE_SZ;
>  	if (dlen)
> -		len += dlen;
> +		len += aligned_dlen;
> +
> +	len += ubifs_auth_node_sz(c);
> +
>  	err = make_reservation(c, BASEHD, len);
>  	if (err)
>  		goto out_free;
> @@ -1428,6 +1494,9 @@ int ubifs_jnl_truncate(struct ubifs_info *c, const struct inode *inode,
>  		ubifs_wbuf_add_ino_nolock(&c->jheads[BASEHD].wbuf, inum);
>  	release_head(c, BASEHD);
>  
> +	if (ubifs_authenticated(c))
> +		ubifs_add_dirt(c, lnum, ubifs_auth_node_sz(c));
> +
>  	if (dlen) {
>  		sz = offs + UBIFS_INO_NODE_SZ + UBIFS_TRUN_NODE_SZ;
>  		err = ubifs_tnc_add(c, &key, lnum, sz, dlen, hash_dn);
> @@ -1491,7 +1560,7 @@ int ubifs_jnl_delete_xattr(struct ubifs_info *c, const struct inode *host,
>  			   const struct inode *inode,
>  			   const struct fscrypt_name *nm)
>  {
> -	int err, xlen, hlen, len, lnum, xent_offs, aligned_xlen;
> +	int err, xlen, hlen, len, lnum, xent_offs, aligned_xlen, tlen;
>  	struct ubifs_dent_node *xent;
>  	struct ubifs_ino_node *ino;
>  	union ubifs_key xent_key, key1, key2;
> @@ -1511,12 +1580,14 @@ int ubifs_jnl_delete_xattr(struct ubifs_info *c, const struct inode *host,
>  	hlen = host_ui->data_len + UBIFS_INO_NODE_SZ;
>  	len = aligned_xlen + UBIFS_INO_NODE_SZ + ALIGN(hlen, 8);
>  
> -	xent = kzalloc(len, GFP_NOFS);
> +	tlen = len + ubifs_auth_node_sz(c);

xlen, hlen, len, tlen, oh my.. ;-)
What does the "t" stand for?
Sorry, I'm very bad at naming things.

> +	xent = kzalloc(tlen, GFP_NOFS);
>  	if (!xent)
>  		return -ENOMEM;
>  
>  	/* Make reservation before allocating sequence numbers */
> -	err = make_reservation(c, BASEHD, len);
> +	err = make_reservation(c, BASEHD, tlen);
>  	if (err) {
>  		kfree(xent);
>  		return err;
> @@ -1539,10 +1610,13 @@ int ubifs_jnl_delete_xattr(struct ubifs_info *c, const struct inode *host,
>  	pack_inode(c, ino, host, 1);
>  	ubifs_node_calc_hash(c, ino, hash);
>  
> -	err = write_head(c, BASEHD, xent, len, &lnum, &xent_offs, sync);
> +	err = write_head(c, BASEHD, xent, tlen, &lnum, &xent_offs, sync);
>  	if (!sync && !err)
>  		ubifs_wbuf_add_ino_nolock(&c->jheads[BASEHD].wbuf, host->i_ino);
>  	release_head(c, BASEHD);
> +
> +	if (ubifs_authenticated(c))
> +		ubifs_add_dirt(c, lnum, ubifs_auth_node_sz(c));
>  	kfree(xent);
>  	if (err)
>  		goto out_ro;
> @@ -1605,6 +1679,7 @@ int ubifs_jnl_change_xattr(struct ubifs_info *c, const struct inode *inode,
>  {
>  	int err, len1, len2, aligned_len, aligned_len1, lnum, offs;
>  	struct ubifs_inode *host_ui = ubifs_inode(host);
> +	struct ubifs_inode *ui = ubifs_inode(inode);
>  	struct ubifs_ino_node *ino;
>  	union ubifs_key key;
>  	int sync = IS_DIRSYNC(host);
> @@ -1617,10 +1692,12 @@ int ubifs_jnl_change_xattr(struct ubifs_info *c, const struct inode *inode,
>  	ubifs_assert(mutex_is_locked(&host_ui->ui_mutex));
>  
>  	len1 = UBIFS_INO_NODE_SZ + host_ui->data_len;
> -	len2 = UBIFS_INO_NODE_SZ + ubifs_inode(inode)->data_len;
> +	len2 = UBIFS_INO_NODE_SZ + ui->data_len;

Why do we need this change, seems unrelated?

>  	aligned_len1 = ALIGN(len1, 8);
>  	aligned_len = aligned_len1 + ALIGN(len2, 8);
>  
> +	aligned_len += ubifs_auth_node_sz(c);
> +
>  	ino = kzalloc(aligned_len, GFP_NOFS);
>  	if (!ino)
>  		return -ENOMEM;
> @@ -1646,6 +1723,9 @@ int ubifs_jnl_change_xattr(struct ubifs_info *c, const struct inode *inode,
>  	if (err)
>  		goto out_ro;
>  
> +	if (ubifs_authenticated(c))
> +		ubifs_add_dirt(c, lnum, ubifs_auth_node_sz(c));
> +
>  	ino_key_init(c, &key, host->i_ino);
>  	err = ubifs_tnc_add(c, &key, lnum, offs, len1, hash_host);
>  	if (err)
> diff --git a/fs/ubifs/log.c b/fs/ubifs/log.c
> index 7cffa120a750..311757b2dc1a 100644
> --- a/fs/ubifs/log.c
> +++ b/fs/ubifs/log.c
> @@ -236,6 +236,7 @@ int ubifs_add_bud_to_log(struct ubifs_info *c, int jhead, int lnum, int offs)
>  	bud->lnum = lnum;
>  	bud->start = offs;
>  	bud->jhead = jhead;
> +	bud->log_hash = NULL;
>  
>  	ref->ch.node_type = UBIFS_REF_NODE;
>  	ref->lnum = cpu_to_le32(bud->lnum);
> @@ -275,6 +276,12 @@ int ubifs_add_bud_to_log(struct ubifs_info *c, int jhead, int lnum, int offs)
>  	if (err)
>  		goto out_unlock;
>  
> +	ubifs_shash_update(c, c->log_hash, (void *)ref, UBIFS_REF_NODE_SZ);
> +
> +	err = ubifs_shash_copy_state(c, c->log_hash, c->jheads[jhead].log_hash);
> +	if (err)
> +		goto out_unlock;
> +
>  	c->lhead_offs += c->ref_node_alsz;
>  
>  	ubifs_add_bud(c, bud);
> @@ -377,6 +384,11 @@ int ubifs_log_start_commit(struct ubifs_info *c, int *ltail_lnum)
>  	cs->cmt_no = cpu_to_le64(c->cmt_no);
>  	ubifs_prepare_node(c, cs, UBIFS_CS_NODE_SZ, 0);
>  
> +	if (c->authenticated) {

ubifs_authenticated(c)?

> +		crypto_shash_init(c->log_hash);
> +		crypto_shash_update(c->log_hash, (void *)cs, UBIFS_CS_NODE_SZ);
> +	}
> +
>  	/*
>  	 * Note, we do not lock 'c->log_mutex' because this is the commit start
>  	 * phase and we are exclusively using the log. And we do not lock
> @@ -402,6 +414,10 @@ int ubifs_log_start_commit(struct ubifs_info *c, int *ltail_lnum)
>  
>  		ubifs_prepare_node(c, ref, UBIFS_REF_NODE_SZ, 0);
>  		len += UBIFS_REF_NODE_SZ;
> +
> +		ubifs_shash_update(c, c->log_hash, (void *)ref,

Why the void * cast?
(Applies to multiple calls to ubifs_shash_update)

> +				   UBIFS_REF_NODE_SZ);
> +		ubifs_shash_copy_state(c, c->log_hash, c->jheads[i].log_hash);
>  	}
>  
>  	ubifs_pad(c, buf + len, ALIGN(len, c->min_io_size) - len);
> @@ -516,6 +532,7 @@ int ubifs_log_post_commit(struct ubifs_info *c, int old_ltail_lnum)
>  		if (err)
>  			return err;
>  		list_del(&bud->list);
> +		kfree(bud->log_hash);
>  		kfree(bud);
>  	}
>  	mutex_lock(&c->log_mutex);
> diff --git a/fs/ubifs/replay.c b/fs/ubifs/replay.c
> index 9e9ff753515f..07a66ae90e89 100644
> --- a/fs/ubifs/replay.c
> +++ b/fs/ubifs/replay.c
> @@ -665,6 +665,8 @@ static int replay_bud(struct ubifs_info *c, struct bud_entry *b)
>  					  old_size, new_size);
>  			break;
>  		}
> +		case UBIFS_AUTH_NODE:
> +			break;
>  		default:
>  			ubifs_err(c, "unexpected node type %d in bud LEB %d:%d",
>  				  snod->type, lnum, snod->offs);
> diff --git a/fs/ubifs/super.c b/fs/ubifs/super.c
> index 6b2d80391111..49de06921427 100644
> --- a/fs/ubifs/super.c
> +++ b/fs/ubifs/super.c
> @@ -815,6 +815,9 @@ static int alloc_wbufs(struct ubifs_info *c)
>  		c->jheads[i].wbuf.sync_callback = &bud_wbuf_callback;
>  		c->jheads[i].wbuf.jhead = i;
>  		c->jheads[i].grouped = 1;
> +		c->jheads[i].log_hash = ubifs_hash_get_desc(c);
> +		if (IS_ERR(c->jheads[i].log_hash))
> +			goto out;
>  	}
>  
>  	/*
> @@ -825,6 +828,12 @@ static int alloc_wbufs(struct ubifs_info *c)
>  	c->jheads[GCHD].grouped = 0;
>  
>  	return 0;
> +
> +out:
> +	while (i--)
> +		kfree(c->jheads[i].log_hash);
> +
> +	return err;
>  }
>  
>  /**
> @@ -839,6 +848,7 @@ static void free_wbufs(struct ubifs_info *c)
>  		for (i = 0; i < c->jhead_cnt; i++) {
>  			kfree(c->jheads[i].wbuf.buf);
>  			kfree(c->jheads[i].wbuf.inodes);
> +			kfree(c->jheads[i].log_hash);
>  		}
>  		kfree(c->jheads);
>  		c->jheads = NULL;
> diff --git a/fs/ubifs/ubifs.h b/fs/ubifs/ubifs.h
> index bf4a99d799a1..5390d087da3a 100644
> --- a/fs/ubifs/ubifs.h
> +++ b/fs/ubifs/ubifs.h
> @@ -697,6 +697,7 @@ struct ubifs_wbuf {
>   * @jhead: journal head number this bud belongs to
>   * @list: link in the list buds belonging to the same journal head
>   * @rb: link in the tree of all buds
> + * @log_hash: the log hash from the commit start node up to this bud
>   */
>  struct ubifs_bud {
>  	int lnum;
> @@ -704,6 +705,7 @@ struct ubifs_bud {
>  	int jhead;
>  	struct list_head list;
>  	struct rb_node rb;
> +	struct shash_desc *log_hash;
>  };
>  
>  /**
> @@ -711,6 +713,7 @@ struct ubifs_bud {
>   * @wbuf: head's write-buffer
>   * @buds_list: list of bud LEBs belonging to this journal head
>   * @grouped: non-zero if UBIFS groups nodes when writing to this journal head
> + * @log_hash: the log hash from the commit start node up to this journal head
>   *
>   * Note, the @buds list is protected by the @c->buds_lock.
>   */
> @@ -718,6 +721,7 @@ struct ubifs_jhead {
>  	struct ubifs_wbuf wbuf;
>  	struct list_head buds_list;
>  	unsigned int grouped:1;
> +	struct shash_desc *log_hash;
>  };
>  
>  /**
> @@ -1215,6 +1219,8 @@ struct ubifs_debug_info;
>   * @auth_key_name: the authentication key name
>   * @auth_hash_name: the name of the hash algorithm used for authentication
>   * @auth_hash_algo: the authentication hash used for this fs
> + * @log_hash: the log hash from the commit start node up to the latest reference
> + *            node.
>   *
>   * @empty: %1 if the UBI device is empty
>   * @need_recovery: %1 if the file-system needs recovery
> @@ -1456,6 +1462,8 @@ struct ubifs_info {
>  	char *auth_hash_name;
>  	enum hash_algo auth_hash_algo;
>  
> +	struct shash_desc *log_hash;
> +
>  	/* The below fields are used only during mounting and re-mounting */
>  	unsigned int empty:1;
>  	unsigned int need_recovery:1;
> 

Thanks,
//richard



^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH 15/25] ubifs: Add auth nodes to garbage collector journal head
  2018-07-04 12:41 ` [PATCH 15/25] ubifs: Add auth nodes to garbage collector journal head Sascha Hauer
@ 2018-08-27 20:51   ` Richard Weinberger
  2018-08-30 14:43       ` Sascha Hauer
  0 siblings, 1 reply; 49+ messages in thread
From: Richard Weinberger @ 2018-08-27 20:51 UTC (permalink / raw)
  To: Sascha Hauer; +Cc: linux-mtd, David Gstir, kernel, linux-kernel

Am Mittwoch, 4. Juli 2018, 14:41:27 CEST schrieb Sascha Hauer:
> To be able to authenticate the garbage collector journal head add
> authentication nodes to the buds the garbage collector creates.
> 
> Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
> ---
>  fs/ubifs/gc.c | 37 ++++++++++++++++++++++++++++++++++---
>  1 file changed, 34 insertions(+), 3 deletions(-)
> 
> diff --git a/fs/ubifs/gc.c b/fs/ubifs/gc.c
> index ac3a3f7c6a6e..8feeeb12b6ed 100644
> --- a/fs/ubifs/gc.c
> +++ b/fs/ubifs/gc.c
> @@ -365,12 +365,13 @@ static int move_nodes(struct ubifs_info *c, struct ubifs_scan_leb *sleb)
>  
>  	/* Write nodes to their new location. Use the first-fit strategy */
>  	while (1) {
> -		int avail;
> +		int avail, moved = 0;
>  		struct ubifs_scan_node *snod, *tmp;
>  
>  		/* Move data nodes */
>  		list_for_each_entry_safe(snod, tmp, &sleb->nodes, list) {
> -			avail = c->leb_size - wbuf->offs - wbuf->used;
> +			avail = c->leb_size - wbuf->offs - wbuf->used -
> +					ubifs_auth_node_sz(c);
>  			if  (snod->len > avail)
>  				/*
>  				 * Do not skip data nodes in order to optimize
> @@ -378,14 +379,19 @@ static int move_nodes(struct ubifs_info *c, struct ubifs_scan_leb *sleb)
>  				 */
>  				break;
>  
> +			ubifs_shash_update(c, c->jheads[GCHD].log_hash,
> +					   snod->node, snod->len);
> +
>  			err = move_node(c, sleb, snod, wbuf);
>  			if (err)
>  				goto out;
> +			moved = 1;
>  		}
>  
>  		/* Move non-data nodes */
>  		list_for_each_entry_safe(snod, tmp, &nondata, list) {
> -			avail = c->leb_size - wbuf->offs - wbuf->used;
> +			avail = c->leb_size - wbuf->offs - wbuf->used -
> +					ubifs_auth_node_sz(c);
>  			if (avail < min)
>  				break;
>  
> @@ -403,7 +409,32 @@ static int move_nodes(struct ubifs_info *c, struct ubifs_scan_leb *sleb)
>  				continue;
>  			}
>  
> +			ubifs_shash_update(c, c->jheads[GCHD].log_hash,
> +					   snod->node, snod->len);
> +
>  			err = move_node(c, sleb, snod, wbuf);
> +			if (err)
> +				goto out;
> +			moved = 1;
> +		}
> +
> +		if (ubifs_authenticated(c) && moved) {
> +			struct ubifs_auth_node *auth;
> +
> +			auth = kmalloc(ubifs_auth_node_sz(c), GFP_NOFS);
> +			if (!auth) {
> +				err = -ENOMEM;
> +				goto out;
> +			}
> +
> +			ubifs_prepare_auth_node(c, auth,
> +						c->jheads[GCHD].log_hash);

ubifs_prepare_auth_node() does a crypto_shash_final(), check.
But the overall "hash life cycle" is not 100% clear to me.
For example, does move_nodes() assume that the hash is initialized
or is it allowed that an crypto_shash_update() happened before?

Thanks,
//richard



^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH 16/25] ubifs: authenticate replayed journal
  2018-07-04 12:41 ` [PATCH 16/25] ubifs: authenticate replayed journal Sascha Hauer
  2018-07-08  6:08   ` kbuild test robot
@ 2018-08-27 21:16   ` Richard Weinberger
  1 sibling, 0 replies; 49+ messages in thread
From: Richard Weinberger @ 2018-08-27 21:16 UTC (permalink / raw)
  To: Sascha Hauer; +Cc: linux-mtd, David Gstir, kernel, linux-kernel

Am Mittwoch, 4. Juli 2018, 14:41:28 CEST schrieb Sascha Hauer:
> Make sure that during replay all buds can be authenticated. To do
> this we calculate the hash chain until we find an authentication
> node and check the HMAC in that node against the current status
> of the hash chain.
> 
> After a power cut it can happen that some nodes have been written, but
> not yet the authentication node for them. These nodes have to be
> discarded during replay.
> 
> Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
> ---
>  fs/ubifs/replay.c | 116 +++++++++++++++++++++++++++++++++++++++++++++-
>  1 file changed, 114 insertions(+), 2 deletions(-)
> 
> diff --git a/fs/ubifs/replay.c b/fs/ubifs/replay.c
> index 07a66ae90e89..45da17d1a74e 100644
> --- a/fs/ubifs/replay.c
> +++ b/fs/ubifs/replay.c
> @@ -34,6 +34,8 @@
>  
>  #include "ubifs.h"
>  #include <linux/list_sort.h>
> +#include <crypto/hash.h>
> +#include <crypto/algapi.h>
>  
>  /**
>   * struct replay_entry - replay list entry.
> @@ -530,6 +532,79 @@ static int is_last_bud(struct ubifs_info *c, struct ubifs_bud *bud)
>  	return data == 0xFFFFFFFF;
>  }
>  
> +/**
> + * authenticate_sleb - authenticate one scan LEB
> + * @c: UBIFS file-system description object
> + * @sleb: the scan LEB to authenticate
> + * @log_hash:
> + * @is_last: if true, this is is the last LEB
> + *
> + * This function iterates over the buds of a single LEB authenticating all buds
> + * with the authentication nodes on this LEB. Authentication nodes are written
> + * after some buds and contain a HMAC covering the authentication node itself
> + * and the buds between the last authentication node and the current
> + * authentication node. It can happen that the last buds cannot be authenticated
> + * because a powercut happened when some nodes were written but not the
> + * corresponding authentication node. This function returns the number of nodes
> + * that could be authenticated or a negative error code.
> + */
> +static int authenticate_sleb(struct ubifs_info *c, struct ubifs_scan_leb *sleb,
> +			     struct shash_desc *log_hash, int is_last)
> +{
> +	int n_not_auth = 0;
> +	struct ubifs_scan_node *snod;
> +	int n_nodes = 0;
> +	int err;
> +
> +	if (!ubifs_authenticated(c))
> +		return sleb->nodes_cnt;
> +
> +	list_for_each_entry(snod, &sleb->nodes, list) {
> +
> +		n_nodes++;
> +
> +		if (snod->type == UBIFS_AUTH_NODE) {
> +			struct ubifs_auth_node *auth = snod->node;
> +			SHASH_DESC_ON_STACK(hash_desc, c->hash_tfm);
> +			u8 hash[crypto_shash_descsize(c->hash_tfm)];
> +			SHASH_DESC_ON_STACK(hmac_desc, c->hmac_tfm);
> +			u8 hmac[c->hmac_desc_len];
> +
> +			hash_desc->tfm = c->hash_tfm;
> +			hash_desc->flags = CRYPTO_TFM_REQ_MAY_SLEEP;
> +
> +			ubifs_shash_copy_state(c, log_hash, hash_desc);
> +			crypto_shash_final(hash_desc, hash);
> +
> +			hmac_desc->tfm = c->hmac_tfm;
> +			hmac_desc->flags = CRYPTO_TFM_REQ_MAY_SLEEP;
> +			crypto_shash_digest(hmac_desc, hash, c->hash_len, hmac);
> +
> +			err = ubifs_check_hmac(c, auth->hmac, hmac);
> +			if (err) {
> +				err = -EPERM;
> +				goto out;
> +			}
> +			n_not_auth = 0;
> +		} else {
> +			crypto_shash_update(log_hash, snod->node, snod->len);
> +			n_not_auth++;
> +		}
> +	}
> +
> +	/*
> +	 * A powercut can happen when some nodes were written, but not yet
> +	 * the corresponding authentication node. This may only happen on
> +	 * the last bud though.
> +	 */

Can you please add a little debug prints around that?

Thanks,
//richard



^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH 10/25] ubifs: add helper functions for authentication support
  2018-08-27 12:50   ` Richard Weinberger
@ 2018-08-29  6:30     ` Sascha Hauer
  0 siblings, 0 replies; 49+ messages in thread
From: Sascha Hauer @ 2018-08-29  6:30 UTC (permalink / raw)
  To: Richard Weinberger; +Cc: linux-mtd, David Gstir, kernel, linux-kernel

Hi Richard,

On Mon, Aug 27, 2018 at 02:50:37PM +0200, Richard Weinberger wrote:
> Am Mittwoch, 4. Juli 2018, 14:41:22 CEST schrieb Sascha Hauer:
> > This patch adds the various helper functions needed for authentication
> > support. We need functions to hash nodes, to embed HMACs into a node and
> > to compare hashes and HMACs. Most functions first check if this
> > filesystem is authenticated and bail out early if not, which makes the
> > functions safe to be called with disabled authentication.
> > 
> > Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
> > ---
> > +void ubifs_prepare_auth_node(struct ubifs_info *c, void *node,
> > +			     struct shash_desc *inhash)
> > +{
> > +	SHASH_DESC_ON_STACK(hash_desc, c->hash_tfm);
> > +	struct ubifs_auth_node *auth = node;
> > +	u8 hash[crypto_shash_descsize(c->hash_tfm)];
> 
> Doesn't this introduce a new VLA?
> Not that me make Kees unhappy. ;-)

/me just read https://lwn.net/Articles/749064/ and now realizes why this
is considered harmful.

Thanks for the review. I'll look into this and all the other points. I
guess that'll keep me busy for some time.

Thanks
  Sascha

-- 
Pengutronix e.K.                           |                             |
Industrial Linux Solutions                 | http://www.pengutronix.de/  |
Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |

^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH 12/25] ubifs: Add hashes to the tree node cache
  2018-08-27 19:18   ` Richard Weinberger
@ 2018-08-29 11:16     ` Sascha Hauer
  0 siblings, 0 replies; 49+ messages in thread
From: Sascha Hauer @ 2018-08-29 11:16 UTC (permalink / raw)
  To: Richard Weinberger; +Cc: linux-mtd, David Gstir, kernel, linux-kernel

On Mon, Aug 27, 2018 at 09:18:25PM +0200, Richard Weinberger wrote:
> Am Mittwoch, 4. Juli 2018, 14:41:24 CEST schrieb Sascha Hauer:
> > As part of the UBIFS authentication support every branch in the index
> > gets a hash covering the referenced node. To make that happen the tree
> > node cache needs hashes over the nodes. This patch adds a hash argument
> > to ubifs_tnc_add() and ubifs_tnc_add_nm(). The hashes are calculated
> > from the callers of these functions which actually prepare the nodes.
> > With this patch all the leaf nodes of the index tree get hashes, but
> > currently nothing is done with these hashes, this is left for a later
> > patch.
> > 
> > Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
> > ---
> >  fs/ubifs/journal.c | 93 ++++++++++++++++++++++++++++++++++------------
> >  fs/ubifs/replay.c  |  4 +-
> >  fs/ubifs/tnc.c     | 10 ++++-
> >  fs/ubifs/ubifs.h   |  7 +++-
> >  4 files changed, 84 insertions(+), 30 deletions(-)
> > 
> > diff --git a/fs/ubifs/journal.c b/fs/ubifs/journal.c
> > index 754d969eb27e..55b35bc33c31 100644
> > --- a/fs/ubifs/journal.c
> > +++ b/fs/ubifs/journal.c
> > @@ -517,6 +517,9 @@ int ubifs_jnl_update(struct ubifs_info *c, const struct inode *dir,
> >  	struct ubifs_dent_node *dent;
> >  	struct ubifs_ino_node *ino;
> >  	union ubifs_key dent_key, ino_key;
> > +	u8 hash_dent[UBIFS_MAX_HASH_LEN];
> > +	u8 hash_ino[UBIFS_MAX_HASH_LEN];
> > +	u8 hash_ino_host[UBIFS_MAX_HASH_LEN];
> 
> With authentication enabled, this makes perfectly sense.
> But if someone builds UBIFS without and has a memory constraint system,
> we could save by setting UBIFS_MAX_HASH_LEN to 0. What do you think?
> Although, we need another define to not influence ubifs-media.h.

I gave it a try and it looks good. Will do it that way.

Sascha

-- 
Pengutronix e.K.                           |                             |
Industrial Linux Solutions                 | http://www.pengutronix.de/  |
Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |

^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH 14/25] ubifs: Add authentication nodes to journal
  2018-08-27 20:48   ` Richard Weinberger
@ 2018-08-29 14:38     ` Sascha Hauer
  2018-08-29 14:54       ` Richard Weinberger
  2018-09-02 19:45       ` Richard Weinberger
  0 siblings, 2 replies; 49+ messages in thread
From: Sascha Hauer @ 2018-08-29 14:38 UTC (permalink / raw)
  To: Richard Weinberger; +Cc: linux-mtd, David Gstir, kernel, linux-kernel

On Mon, Aug 27, 2018 at 10:48:26PM +0200, Richard Weinberger wrote:
> Am Mittwoch, 4. Juli 2018, 14:41:26 CEST schrieb Sascha Hauer:
> > Nodes that are written to flash can only be authenticated through the
> > index after the next commit. When a journal replay is necessary the
> > nodes are not yet referenced by the index and thus can't be
> > authenticated.
> > 
> > This patch overcomes this situation by creating a hash over all nodes
> > beginning from the commit start node over the reference node(s) and
> > the buds themselves. From
> > time to time we insert authentication nodes. Authentication nodes
> > contain a HMAC from the current hash state, so that they can be
> > used to authenticate a journal replay up to the point where the
> > authentication node is. The hash is continued afterwards
> > so that theoretically we would only have to check the HMAC of
> > the last authentication node we find.
> > 
> > Overall we get this picture:
> > 
> > ,,,,,,,,
> > ,......,...........................................
> > ,. CS  ,               hash1.----.           hash2.----.
> > ,.  |  ,                    .    |hmac            .    |hmac
> > ,.  v  ,                    .    v                .    v
> > ,.REF#0,-> bud -> bud -> bud.-> auth -> bud -> bud.-> auth ...
> > ,..|...,...........................................
> > ,  |   ,
> > ,  |   ,,,,,,,,,,,,,,,
> > .  |            hash3,----.
> > ,  |                 ,    |hmac
> > ,  v                 ,    v
> > , REF#1 -> bud -> bud,-> auth ...
> > ,,,|,,,,,,,,,,,,,,,,,,
> >    v
> >   REF#2 -> ...
> >    |
> >    V
> >   ...
> > 
> > Note how hash3 covers CS, REF#0 and REF#1 so that it is not possible to
> > exchange or skip any reference nodes. Unlike the picture suggests the
> > auth nodes themselves are not hashed.
> > 
> > With this it is possible for an offline attacker to cut each journal
> > head or to drop the last reference node(s), but not to skip any journal
> > heads or to reorder any operations.
> > 
> > Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
> > ---
> >  fs/ubifs/gc.c      |   3 +-
> >  fs/ubifs/journal.c | 110 ++++++++++++++++++++++++++++++++++++++-------
> >  fs/ubifs/log.c     |  17 +++++++
> >  fs/ubifs/replay.c  |   2 +
> >  fs/ubifs/super.c   |  10 +++++
> >  fs/ubifs/ubifs.h   |   8 ++++
> >  6 files changed, 134 insertions(+), 16 deletions(-)
> > 
> > diff --git a/fs/ubifs/journal.c b/fs/ubifs/journal.c
> > index 55b35bc33c31..4fde75623a3f 100644
> > --- a/fs/ubifs/journal.c
> > +++ b/fs/ubifs/journal.c
> > @@ -228,6 +228,32 @@ static int reserve_space(struct ubifs_info *c, int jhead, int len)
> >  	return err;
> >  }
> >  
> > +static void ubifs_hash_nodes(struct ubifs_info *c, void *node,
> > +			     int len, struct shash_desc *hash)
> > +{
> > +	int auth_node_size = ubifs_auth_node_sz(c);
> > +
> > +	while (1) {
> > +		const struct ubifs_ch *ch = node;
> > +		int nodelen = le32_to_cpu(ch->len);
> > +
> > +		ubifs_assert(len >= auth_node_size);
> > +
> > +		if (len == auth_node_size)
> > +			break;
> > +
> > +		ubifs_assert(len > nodelen);
> > +		ubifs_assert(ch->magic == cpu_to_le32(UBIFS_NODE_MAGIC));
> 
> A malformed UBIFS image can trigger that assert, right?
> Please handle this without ubifs_assert() and abort with an error.
> ubifs_assert() does not stop execution by default.

In this function we iterate over the nodes we previously created in
memory. It is called with the same buffer write_head() is called with.

If this assertion triggers then we either failed creating a good buffer
containing all nodes or we failed iterating over it for some reason.
Either way it is an UBIFS internal error, not a malformed image.

> 
> > +		ubifs_shash_update(c, hash, (void *)node, nodelen);
> > +
> > +		node += ALIGN(nodelen, 8);
> > +		len -= ALIGN(nodelen, 8);
> > +	}
> > +
> > +	ubifs_prepare_auth_node(c, node, hash);
> > +}
> > +
> > @@ -603,6 +634,7 @@ int ubifs_jnl_update(struct ubifs_info *c, const struct inode *dir,
> >  	}
> >  	release_head(c, BASEHD);
> >  	kfree(dent);
> > +	ubifs_add_dirt(c, lnum, ubifs_auth_node_sz(c));
> 
> You have to account it immediately because while a commit you have no longer
> a reference to them?
> Upon replay you should have since you scan LEBs anyway.

What do you mean here? Is that a suggestion to change something?

> 
> An shouldn't this only get called when the file system is authenticated?
> AFAICT ubifs_add_dirt(c, lnum, 0) is not a no-op.

Right. I changed it to use the ubifs_add_auth_dirt() helper that you
suggested below.

> 
> >  	if (deletion) {
> >  		if (nm->hash)
> > @@ -677,8 +709,9 @@ int ubifs_jnl_write_data(struct ubifs_info *c, const struct inode *inode,
> >  			 const union ubifs_key *key, const void *buf, int len)
> >  {
> >  	struct ubifs_data_node *data;
> > -	int err, lnum, offs, compr_type, out_len, compr_len;
> > +	int err, lnum, offs, compr_type, out_len, compr_len, auth_len;
> >  	int dlen = COMPRESSED_DATA_NODE_BUF_SZ, allocated = 1;
> > +	int aligned_dlen;
> >  	struct ubifs_inode *ui = ubifs_inode(inode);
> >  	bool encrypted = ubifs_crypt_is_encrypted(inode);
> >  	u8 hash[UBIFS_MAX_HASH_LEN];
> > @@ -690,7 +723,9 @@ int ubifs_jnl_write_data(struct ubifs_info *c, const struct inode *inode,
> >  	if (encrypted)
> >  		dlen += UBIFS_CIPHER_BLOCK_SIZE;
> >  
> > -	data = kmalloc(dlen, GFP_NOFS | __GFP_NOWARN);
> > +	auth_len = ubifs_auth_node_sz(c);
> > +
> > +	data = kmalloc(dlen + auth_len, GFP_NOFS | __GFP_NOWARN);
> >  	if (!data) {
> >  		/*
> >  		 * Fall-back to the write reserve buffer. Note, we might be
> > @@ -729,15 +764,16 @@ int ubifs_jnl_write_data(struct ubifs_info *c, const struct inode *inode,
> >  	}
> >  
> >  	dlen = UBIFS_DATA_NODE_SZ + out_len;
> > +	aligned_dlen = ALIGN(dlen, 8);
> >  	data->compr_type = cpu_to_le16(compr_type);
> >  
> >  	/* Make reservation before allocating sequence numbers */
> > -	err = make_reservation(c, DATAHD, dlen);
> > +	err = make_reservation(c, DATAHD, aligned_dlen + auth_len);
> 
> Okay, now I understand the ALIGN(), ubifs nodes need to be aligned
> at an 8 border. Makes sense, _but_ you change this also for the non-authenticated
> case.

I assumed that make_reservation would align len anyway. I can't find the
place that led me to that assumption anymore and even if this is true
it's probably safer to just stick to the original len for the
non-authenticated case, so I'll change this and other places to use
the non aligned len.

BTW could you have a look at ubifs_jnl_change_xattr()? Unlike other
function in this file it explicitly calls make_reservation() with the
length of the last node aligned. Do you have an idea why?

> 
> >  	if (err)
> >  		goto out_free;
> >  
> >  	ubifs_prepare_node(c, data, dlen, 0);
> > -	err = write_head(c, DATAHD, data, dlen, &lnum, &offs, 0);
> > +	err = write_head(c, DATAHD, data, aligned_dlen + auth_len, &lnum, &offs, 0);
> >  	if (err)
> >  		goto out_release;
> >  
> > @@ -1198,6 +1252,9 @@ int ubifs_jnl_rename(struct ubifs_info *c, const struct inode *old_dir,
> >  	}
> >  	release_head(c, BASEHD);
> >  
> > +	if (ubifs_authenticated(c))
> > +		ubifs_add_dirt(c, lnum, ubifs_auth_node_sz(c));
> 
> This is always the same pattern. How about adding a helper function?
> ubifs_add_auth_dirt()?

Yes, sounds good.

> > @@ -1511,12 +1580,14 @@ int ubifs_jnl_delete_xattr(struct ubifs_info *c, const struct inode *host,
> >  	hlen = host_ui->data_len + UBIFS_INO_NODE_SZ;
> >  	len = aligned_xlen + UBIFS_INO_NODE_SZ + ALIGN(hlen, 8);
> >  
> > -	xent = kzalloc(len, GFP_NOFS);
> > +	tlen = len + ubifs_auth_node_sz(c);
> 
> xlen, hlen, len, tlen, oh my.. ;-)
> What does the "t" stand for?
> Sorry, I'm very bad at naming things.

I must have thought of something like total_len. I could change it to
write_len if that sounds better to you.

> > @@ -1617,10 +1692,12 @@ int ubifs_jnl_change_xattr(struct ubifs_info *c, const struct inode *inode,
> >  	ubifs_assert(mutex_is_locked(&host_ui->ui_mutex));
> >  
> >  	len1 = UBIFS_INO_NODE_SZ + host_ui->data_len;
> > -	len2 = UBIFS_INO_NODE_SZ + ubifs_inode(inode)->data_len;
> > +	len2 = UBIFS_INO_NODE_SZ + ui->data_len;
> 
> Why do we need this change, seems unrelated?

Some leftover from earlier versions. Will drop.

> > @@ -377,6 +384,11 @@ int ubifs_log_start_commit(struct ubifs_info *c, int *ltail_lnum)
> >  	cs->cmt_no = cpu_to_le64(c->cmt_no);
> >  	ubifs_prepare_node(c, cs, UBIFS_CS_NODE_SZ, 0);
> >  
> > +	if (c->authenticated) {
> 
> ubifs_authenticated(c)?

Yes.

> 
> > +		crypto_shash_init(c->log_hash);
> > +		crypto_shash_update(c->log_hash, (void *)cs, UBIFS_CS_NODE_SZ);
> > +	}
> > +
> >  	/*
> >  	 * Note, we do not lock 'c->log_mutex' because this is the commit start
> >  	 * phase and we are exclusively using the log. And we do not lock
> > @@ -402,6 +414,10 @@ int ubifs_log_start_commit(struct ubifs_info *c, int *ltail_lnum)
> >  
> >  		ubifs_prepare_node(c, ref, UBIFS_REF_NODE_SZ, 0);
> >  		len += UBIFS_REF_NODE_SZ;
> > +
> > +		ubifs_shash_update(c, c->log_hash, (void *)ref,
> 
> Why the void * cast?
> (Applies to multiple calls to ubifs_shash_update)

I think I used crypto_shash_update directly in ealier versions which
takes a u8 * and thus needs a cast. Now it can be removed.

Sascha

-- 
Pengutronix e.K.                           |                             |
Industrial Linux Solutions                 | http://www.pengutronix.de/  |
Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |

^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH 14/25] ubifs: Add authentication nodes to journal
  2018-08-29 14:38     ` Sascha Hauer
@ 2018-08-29 14:54       ` Richard Weinberger
  2018-08-30 13:41         ` Sascha Hauer
  2018-09-02 19:45       ` Richard Weinberger
  1 sibling, 1 reply; 49+ messages in thread
From: Richard Weinberger @ 2018-08-29 14:54 UTC (permalink / raw)
  To: Sascha Hauer; +Cc: linux-mtd, David Gstir, kernel, linux-kernel

Am Mittwoch, 29. August 2018, 16:38:34 CEST schrieb Sascha Hauer:
> On Mon, Aug 27, 2018 at 10:48:26PM +0200, Richard Weinberger wrote:
> > >  	release_head(c, BASEHD);
> > >  	kfree(dent);
> > > +	ubifs_add_dirt(c, lnum, ubifs_auth_node_sz(c));
> > 
> > You have to account it immediately because while a commit you have no longer
> > a reference to them?
> > Upon replay you should have since you scan LEBs anyway.
> 
> What do you mean here? Is that a suggestion to change something?

I don't fully understand how you keep the lprops dirty counter correct for
auth nodes. Hence the question.

Auth nodes are not referenced by the index, so you have to keep track of
them manually.
Is your current approach "mark them dirty immediately and rely on LPT commit"
to not get lost of an auth node?

I expected auth nodes getting dirtied also during journal reply.

> > 
> > An shouldn't this only get called when the file system is authenticated?
> > AFAICT ubifs_add_dirt(c, lnum, 0) is not a no-op.
> 
> Right. I changed it to use the ubifs_add_auth_dirt() helper that you
> suggested below.
> 
> > 
> > >  	if (deletion) {
> > >  		if (nm->hash)
> > > @@ -677,8 +709,9 @@ int ubifs_jnl_write_data(struct ubifs_info *c, const struct inode *inode,
> > >  			 const union ubifs_key *key, const void *buf, int len)
> > >  {
> > >  	struct ubifs_data_node *data;
> > > -	int err, lnum, offs, compr_type, out_len, compr_len;
> > > +	int err, lnum, offs, compr_type, out_len, compr_len, auth_len;
> > >  	int dlen = COMPRESSED_DATA_NODE_BUF_SZ, allocated = 1;
> > > +	int aligned_dlen;
> > >  	struct ubifs_inode *ui = ubifs_inode(inode);
> > >  	bool encrypted = ubifs_crypt_is_encrypted(inode);
> > >  	u8 hash[UBIFS_MAX_HASH_LEN];
> > > @@ -690,7 +723,9 @@ int ubifs_jnl_write_data(struct ubifs_info *c, const struct inode *inode,
> > >  	if (encrypted)
> > >  		dlen += UBIFS_CIPHER_BLOCK_SIZE;
> > >  
> > > -	data = kmalloc(dlen, GFP_NOFS | __GFP_NOWARN);
> > > +	auth_len = ubifs_auth_node_sz(c);
> > > +
> > > +	data = kmalloc(dlen + auth_len, GFP_NOFS | __GFP_NOWARN);
> > >  	if (!data) {
> > >  		/*
> > >  		 * Fall-back to the write reserve buffer. Note, we might be
> > > @@ -729,15 +764,16 @@ int ubifs_jnl_write_data(struct ubifs_info *c, const struct inode *inode,
> > >  	}
> > >  
> > >  	dlen = UBIFS_DATA_NODE_SZ + out_len;
> > > +	aligned_dlen = ALIGN(dlen, 8);
> > >  	data->compr_type = cpu_to_le16(compr_type);
> > >  
> > >  	/* Make reservation before allocating sequence numbers */
> > > -	err = make_reservation(c, DATAHD, dlen);
> > > +	err = make_reservation(c, DATAHD, aligned_dlen + auth_len);
> > 
> > Okay, now I understand the ALIGN(), ubifs nodes need to be aligned
> > at an 8 border. Makes sense, _but_ you change this also for the non-authenticated
> > case.
> 
> I assumed that make_reservation would align len anyway. I can't find the
> place that led me to that assumption anymore and even if this is true
> it's probably safer to just stick to the original len for the
> non-authenticated case, so I'll change this and other places to use
> the non aligned len.
> 
> BTW could you have a look at ubifs_jnl_change_xattr()? Unlike other
> function in this file it explicitly calls make_reservation() with the
> length of the last node aligned. Do you have an idea why?

Uhh. Let me check this.
More corner cases, I fear.

> > > @@ -1511,12 +1580,14 @@ int ubifs_jnl_delete_xattr(struct ubifs_info *c, const struct inode *host,
> > >  	hlen = host_ui->data_len + UBIFS_INO_NODE_SZ;
> > >  	len = aligned_xlen + UBIFS_INO_NODE_SZ + ALIGN(hlen, 8);
> > >  
> > > -	xent = kzalloc(len, GFP_NOFS);
> > > +	tlen = len + ubifs_auth_node_sz(c);
> > 
> > xlen, hlen, len, tlen, oh my.. ;-)
> > What does the "t" stand for?
> > Sorry, I'm very bad at naming things.
> 
> I must have thought of something like total_len. I could change it to
> write_len if that sounds better to you.

write_len is very good!

Thanks,
//richard




^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH 14/25] ubifs: Add authentication nodes to journal
  2018-08-29 14:54       ` Richard Weinberger
@ 2018-08-30 13:41         ` Sascha Hauer
  0 siblings, 0 replies; 49+ messages in thread
From: Sascha Hauer @ 2018-08-30 13:41 UTC (permalink / raw)
  To: Richard Weinberger; +Cc: linux-mtd, David Gstir, kernel, linux-kernel

On Wed, Aug 29, 2018 at 04:54:30PM +0200, Richard Weinberger wrote:
> Am Mittwoch, 29. August 2018, 16:38:34 CEST schrieb Sascha Hauer:
> > On Mon, Aug 27, 2018 at 10:48:26PM +0200, Richard Weinberger wrote:
> > > >  	release_head(c, BASEHD);
> > > >  	kfree(dent);
> > > > +	ubifs_add_dirt(c, lnum, ubifs_auth_node_sz(c));
> > > 
> > > You have to account it immediately because while a commit you have no longer
> > > a reference to them?
> > > Upon replay you should have since you scan LEBs anyway.
> > 
> > What do you mean here? Is that a suggestion to change something?
> 
> I don't fully understand how you keep the lprops dirty counter correct for
> auth nodes. Hence the question.
> 
> Auth nodes are not referenced by the index, so you have to keep track of
> them manually.

Yes. That's why I call ubifs_add_dirt(c, lnum, ubifs_auth_node_sz(c))
each time I create an auth node.

> Is your current approach "mark them dirty immediately and rely on LPT commit"
> to not get lost of an auth node?

Yes, I mark the auth nodes dirty in the assumption that the correct
values are written on a LPT commit.

> 
> I expected auth nodes getting dirtied also during journal reply.

Yes, this happens in replay.c replay_bud(). "used" gets increased with
every node found except when it's an auth node. Here "used" is not
increased, the result is that the auth node adds to the dirty space.

Sascha

-- 
Pengutronix e.K.                           |                             |
Industrial Linux Solutions                 | http://www.pengutronix.de/  |
Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |

^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH 15/25] ubifs: Add auth nodes to garbage collector journal head
  2018-08-27 20:51   ` Richard Weinberger
@ 2018-08-30 14:43       ` Sascha Hauer
  0 siblings, 0 replies; 49+ messages in thread
From: Sascha Hauer @ 2018-08-30 14:43 UTC (permalink / raw)
  To: Richard Weinberger; +Cc: linux-mtd, David Gstir, kernel, linux-kernel

On Mon, Aug 27, 2018 at 10:51:56PM +0200, Richard Weinberger wrote:
> Am Mittwoch, 4. Juli 2018, 14:41:27 CEST schrieb Sascha Hauer:
> > To be able to authenticate the garbage collector journal head add
> > authentication nodes to the buds the garbage collector creates.
> > 
> > Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
> > ---
> >  fs/ubifs/gc.c | 37 ++++++++++++++++++++++++++++++++++---
> >  1 file changed, 34 insertions(+), 3 deletions(-)
> > 
> > diff --git a/fs/ubifs/gc.c b/fs/ubifs/gc.c
> > index ac3a3f7c6a6e..8feeeb12b6ed 100644
> > --- a/fs/ubifs/gc.c
> > +++ b/fs/ubifs/gc.c
> > @@ -365,12 +365,13 @@ static int move_nodes(struct ubifs_info *c, struct ubifs_scan_leb *sleb)
> >  
> >  	/* Write nodes to their new location. Use the first-fit strategy */
> >  	while (1) {
> > -		int avail;
> > +		int avail, moved = 0;
> >  		struct ubifs_scan_node *snod, *tmp;
> >  
> >  		/* Move data nodes */
> >  		list_for_each_entry_safe(snod, tmp, &sleb->nodes, list) {
> > -			avail = c->leb_size - wbuf->offs - wbuf->used;
> > +			avail = c->leb_size - wbuf->offs - wbuf->used -
> > +					ubifs_auth_node_sz(c);
> >  			if  (snod->len > avail)
> >  				/*
> >  				 * Do not skip data nodes in order to optimize
> > @@ -378,14 +379,19 @@ static int move_nodes(struct ubifs_info *c, struct ubifs_scan_leb *sleb)
> >  				 */
> >  				break;
> >  
> > +			ubifs_shash_update(c, c->jheads[GCHD].log_hash,
> > +					   snod->node, snod->len);
> > +
> >  			err = move_node(c, sleb, snod, wbuf);
> >  			if (err)
> >  				goto out;
> > +			moved = 1;
> >  		}
> >  
> >  		/* Move non-data nodes */
> >  		list_for_each_entry_safe(snod, tmp, &nondata, list) {
> > -			avail = c->leb_size - wbuf->offs - wbuf->used;
> > +			avail = c->leb_size - wbuf->offs - wbuf->used -
> > +					ubifs_auth_node_sz(c);
> >  			if (avail < min)
> >  				break;
> >  
> > @@ -403,7 +409,32 @@ static int move_nodes(struct ubifs_info *c, struct ubifs_scan_leb *sleb)
> >  				continue;
> >  			}
> >  
> > +			ubifs_shash_update(c, c->jheads[GCHD].log_hash,
> > +					   snod->node, snod->len);
> > +
> >  			err = move_node(c, sleb, snod, wbuf);
> > +			if (err)
> > +				goto out;
> > +			moved = 1;
> > +		}
> > +
> > +		if (ubifs_authenticated(c) && moved) {
> > +			struct ubifs_auth_node *auth;
> > +
> > +			auth = kmalloc(ubifs_auth_node_sz(c), GFP_NOFS);
> > +			if (!auth) {
> > +				err = -ENOMEM;
> > +				goto out;
> > +			}
> > +
> > +			ubifs_prepare_auth_node(c, auth,
> > +						c->jheads[GCHD].log_hash);
> 
> ubifs_prepare_auth_node() does a crypto_shash_final(), check.
> But the overall "hash life cycle" is not 100% clear to me.
> For example, does move_nodes() assume that the hash is initialized
> or is it allowed that an crypto_shash_update() happened before?

move_nodes() assumes that the hash is
- initialized
- updated with the commit start node
- updated with all reference nodes before the one that point into
  the current LEB
- updated with the reference node pointing to the current LEB


To make that more clear here is the overall life cycle of the auth hashes:

Everything starts in ubifs_log_start_commit(). We initialize the global
log hash and update it with the commit start node:

>	ubifs_shash_init(c->log_hash);
>	ubifs_shash_update(c, c->log_hash, cs, UBIFS_CS_NODE_SZ);

Afterwards still in ubifs_log_start_commit() ref nodes are created for
each journal head. We update the global log hash with the reference
nodes and copy the current state into each journal heads log hash:

>	for (i = 0; i < c->jhead_cnt; i++) {
>		ubifs_prepare_node(c, ref, UBIFS_REF_NODE_SZ, 0);
>		ubifs_shash_update(c, c->log_hash, ref, UBIFS_REF_NODE_SZ);
>		ubifs_shash_copy_state(c, c->log_hash, c->jheads[i].log_hash);
>	}

From here on each journal head has its own log hash derived from the
global log hash. Whenever something is written to a journal head we
update the hash of that journal head. For the garbage collector this
happens in gc.c move_nodes():

>	for_each_node_in_gc_leb()
>		ubifs_shash_update(c, c->jheads[GCHD].log_hash, snod->node, snod->len);

For the base head and data head this happens in journal.c write_head():

>	ubifs_hash_nodes(c, buf, len, c->jheads[jhead].log_hash);

Whenever we want to write an auth node we can now call
ubifs_prepare_auth_node() with a journal heads current log hash state.
This creates us a suitable auth node with the correct hash. The trick
here is that not the hash state is finalized, but a copy thereof, so the
hash state can be continued to use.

The final interesting thing happens when a journal head is switched to a
new LEB in ubifs_add_bud_to_log(). We update the global log hash with the
newly created reference node and again the state is copied to the journal
heads log hash:

>	ubifs_shash_update(c, c->log_hash, ref, UBIFS_REF_NODE_SZ);
>	ubifs_shash_copy_state(c, c->log_hash, c->jheads[jhead].log_hash);

I hope that makes it more clear.

Sascha

-- 
Pengutronix e.K.                           |                             |
Industrial Linux Solutions                 | http://www.pengutronix.de/  |
Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |

^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH 15/25] ubifs: Add auth nodes to garbage collector journal head
@ 2018-08-30 14:43       ` Sascha Hauer
  0 siblings, 0 replies; 49+ messages in thread
From: Sascha Hauer @ 2018-08-30 14:43 UTC (permalink / raw)
  To: Richard Weinberger; +Cc: linux-mtd, David Gstir, kernel, linux-kernel

On Mon, Aug 27, 2018 at 10:51:56PM +0200, Richard Weinberger wrote:
> Am Mittwoch, 4. Juli 2018, 14:41:27 CEST schrieb Sascha Hauer:
> > To be able to authenticate the garbage collector journal head add
> > authentication nodes to the buds the garbage collector creates.
> > 
> > Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
> > ---
> >  fs/ubifs/gc.c | 37 ++++++++++++++++++++++++++++++++++---
> >  1 file changed, 34 insertions(+), 3 deletions(-)
> > 
> > diff --git a/fs/ubifs/gc.c b/fs/ubifs/gc.c
> > index ac3a3f7c6a6e..8feeeb12b6ed 100644
> > --- a/fs/ubifs/gc.c
> > +++ b/fs/ubifs/gc.c
> > @@ -365,12 +365,13 @@ static int move_nodes(struct ubifs_info *c, struct ubifs_scan_leb *sleb)
> >  
> >  	/* Write nodes to their new location. Use the first-fit strategy */
> >  	while (1) {
> > -		int avail;
> > +		int avail, moved = 0;
> >  		struct ubifs_scan_node *snod, *tmp;
> >  
> >  		/* Move data nodes */
> >  		list_for_each_entry_safe(snod, tmp, &sleb->nodes, list) {
> > -			avail = c->leb_size - wbuf->offs - wbuf->used;
> > +			avail = c->leb_size - wbuf->offs - wbuf->used -
> > +					ubifs_auth_node_sz(c);
> >  			if  (snod->len > avail)
> >  				/*
> >  				 * Do not skip data nodes in order to optimize
> > @@ -378,14 +379,19 @@ static int move_nodes(struct ubifs_info *c, struct ubifs_scan_leb *sleb)
> >  				 */
> >  				break;
> >  
> > +			ubifs_shash_update(c, c->jheads[GCHD].log_hash,
> > +					   snod->node, snod->len);
> > +
> >  			err = move_node(c, sleb, snod, wbuf);
> >  			if (err)
> >  				goto out;
> > +			moved = 1;
> >  		}
> >  
> >  		/* Move non-data nodes */
> >  		list_for_each_entry_safe(snod, tmp, &nondata, list) {
> > -			avail = c->leb_size - wbuf->offs - wbuf->used;
> > +			avail = c->leb_size - wbuf->offs - wbuf->used -
> > +					ubifs_auth_node_sz(c);
> >  			if (avail < min)
> >  				break;
> >  
> > @@ -403,7 +409,32 @@ static int move_nodes(struct ubifs_info *c, struct ubifs_scan_leb *sleb)
> >  				continue;
> >  			}
> >  
> > +			ubifs_shash_update(c, c->jheads[GCHD].log_hash,
> > +					   snod->node, snod->len);
> > +
> >  			err = move_node(c, sleb, snod, wbuf);
> > +			if (err)
> > +				goto out;
> > +			moved = 1;
> > +		}
> > +
> > +		if (ubifs_authenticated(c) && moved) {
> > +			struct ubifs_auth_node *auth;
> > +
> > +			auth = kmalloc(ubifs_auth_node_sz(c), GFP_NOFS);
> > +			if (!auth) {
> > +				err = -ENOMEM;
> > +				goto out;
> > +			}
> > +
> > +			ubifs_prepare_auth_node(c, auth,
> > +						c->jheads[GCHD].log_hash);
> 
> ubifs_prepare_auth_node() does a crypto_shash_final(), check.
> But the overall "hash life cycle" is not 100% clear to me.
> For example, does move_nodes() assume that the hash is initialized
> or is it allowed that an crypto_shash_update() happened before?

move_nodes() assumes that the hash is
- initialized
- updated with the commit start node
- updated with all reference nodes before the one that point into
  the current LEB
- updated with the reference node pointing to the current LEB


To make that more clear here is the overall life cycle of the auth hashes:

Everything starts in ubifs_log_start_commit(). We initialize the global
log hash and update it with the commit start node:

>	ubifs_shash_init(c->log_hash);
>	ubifs_shash_update(c, c->log_hash, cs, UBIFS_CS_NODE_SZ);

Afterwards still in ubifs_log_start_commit() ref nodes are created for
each journal head. We update the global log hash with the reference
nodes and copy the current state into each journal heads log hash:

>	for (i = 0; i < c->jhead_cnt; i++) {
>		ubifs_prepare_node(c, ref, UBIFS_REF_NODE_SZ, 0);
>		ubifs_shash_update(c, c->log_hash, ref, UBIFS_REF_NODE_SZ);
>		ubifs_shash_copy_state(c, c->log_hash, c->jheads[i].log_hash);
>	}

>From here on each journal head has its own log hash derived from the
global log hash. Whenever something is written to a journal head we
update the hash of that journal head. For the garbage collector this
happens in gc.c move_nodes():

>	for_each_node_in_gc_leb()
>		ubifs_shash_update(c, c->jheads[GCHD].log_hash, snod->node, snod->len);

For the base head and data head this happens in journal.c write_head():

>	ubifs_hash_nodes(c, buf, len, c->jheads[jhead].log_hash);

Whenever we want to write an auth node we can now call
ubifs_prepare_auth_node() with a journal heads current log hash state.
This creates us a suitable auth node with the correct hash. The trick
here is that not the hash state is finalized, but a copy thereof, so the
hash state can be continued to use.

The final interesting thing happens when a journal head is switched to a
new LEB in ubifs_add_bud_to_log(). We update the global log hash with the
newly created reference node and again the state is copied to the journal
heads log hash:

>	ubifs_shash_update(c, c->log_hash, ref, UBIFS_REF_NODE_SZ);
>	ubifs_shash_copy_state(c, c->log_hash, c->jheads[jhead].log_hash);

I hope that makes it more clear.

Sascha

-- 
Pengutronix e.K.                           |                             |
Industrial Linux Solutions                 | http://www.pengutronix.de/  |
Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |

^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH 14/25] ubifs: Add authentication nodes to journal
  2018-08-29 14:38     ` Sascha Hauer
  2018-08-29 14:54       ` Richard Weinberger
@ 2018-09-02 19:45       ` Richard Weinberger
  1 sibling, 0 replies; 49+ messages in thread
From: Richard Weinberger @ 2018-09-02 19:45 UTC (permalink / raw)
  To: Sascha Hauer; +Cc: linux-mtd, David Gstir, kernel, linux-kernel

Am Mittwoch, 29. August 2018, 16:38:34 CEST schrieb Sascha Hauer:
> I assumed that make_reservation would align len anyway. I can't find the
> place that led me to that assumption anymore and even if this is true
> it's probably safer to just stick to the original len for the
> non-authenticated case, so I'll change this and other places to use
> the non aligned len.
> 
> BTW could you have a look at ubifs_jnl_change_xattr()? Unlike other
> function in this file it explicitly calls make_reservation() with the
> length of the last node aligned. Do you have an idea why?

Well, this is not the only call site. Other call sites do so implicitly
since most UBIFS nodes sizes are aligned to 8.
But yes, there are some inconsistencies which need a cleanup. I've scheduled
that for the upcoming week.

Thanks,
//richard 



^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH 13/25] ubifs: authentication: Add hashes to index nodes
  2018-08-27 19:36   ` Richard Weinberger
@ 2018-09-07 10:25     ` Sascha Hauer
  0 siblings, 0 replies; 49+ messages in thread
From: Sascha Hauer @ 2018-09-07 10:25 UTC (permalink / raw)
  To: Richard Weinberger; +Cc: linux-mtd, David Gstir, kernel, linux-kernel

On Mon, Aug 27, 2018 at 09:36:56PM +0200, Richard Weinberger wrote:
> > diff --git a/fs/ubifs/tnc.c b/fs/ubifs/tnc.c
> > index a47fced47823..a00809d4fe6f 100644
> > --- a/fs/ubifs/tnc.c
> > +++ b/fs/ubifs/tnc.c
> > @@ -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_err(c, "hash mismatch on node at LEB %d:%d", lnum, offs);
> > +		return 0;
> > +	}
> 
> Hmm, I think a global "hash is bad" handler would be nice to have.
> That way we always report in the same way.

I created a function reporting a bad hash, so every failure goes through
the same code...

> 
> Maybe also a new file system specific ioctl to query whether a hash
> failure was noticed.

but I'll leave that for a later excercise if that's ok. I am unsure how
useful such an ioctl() is. It's too easy to interpret such a hash
mismatch as some kind of security violation when it's more likely just a
bug somewhere.

> > @@ -868,6 +877,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);
> 
> This lock looks correct too me.
> Just in case, you did test with lockdep enabled? :-)

Yes, I had lockdep enabled in all my tests.

Sascha

-- 
Pengutronix e.K.                           |                             |
Industrial Linux Solutions                 | http://www.pengutronix.de/  |
Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |

^ permalink raw reply	[flat|nested] 49+ messages in thread

end of thread, other threads:[~2018-09-07 10:25 UTC | newest]

Thread overview: 49+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2018-07-04 12:41 [PATCH 00/25] UBIFS authentication support Sascha Hauer
2018-07-04 12:41 ` [PATCH 01/25] ubifs: refactor create_default_filesystem() Sascha Hauer
2018-07-04 12:41 ` [PATCH 02/25] ubifs: pass ubifs_zbranch to try_read_node() Sascha Hauer
2018-07-04 12:41 ` [PATCH 03/25] ubifs: pass ubifs_zbranch to read_znode() Sascha Hauer
2018-07-04 12:41 ` [PATCH 04/25] ubifs: export pnode_lookup as ubifs_pnode_lookup Sascha Hauer
2018-07-04 12:41 ` [PATCH 05/25] ubifs: implement ubifs_lpt_lookup using ubifs_pnode_lookup Sascha Hauer
2018-08-13  6:31   ` Sascha Hauer
2018-08-13  6:34     ` Richard Weinberger
2018-08-13  8:12       ` Sascha Hauer
2018-08-13 11:30         ` Richard Weinberger
2018-08-26 20:59     ` Richard Weinberger
2018-07-04 12:41 ` [PATCH 06/25] ubifs: drop write_node Sascha Hauer
2018-07-04 12:41 ` [PATCH 07/25] ubifs: Store read superblock node Sascha Hauer
2018-08-27 12:50   ` Richard Weinberger
2018-07-04 12:41 ` [PATCH 08/25] ubifs: Format changes for authentication support Sascha Hauer
2018-07-04 12:41 ` [PATCH 09/25] ubifs: add separate functions to init/crc a node Sascha Hauer
2018-07-04 12:41 ` [PATCH 10/25] ubifs: add helper functions for authentication support Sascha Hauer
2018-08-27 12:50   ` Richard Weinberger
2018-08-29  6:30     ` Sascha Hauer
2018-07-04 12:41 ` [PATCH 11/25] ubifs: Create functions to embed a HMAC in a node Sascha Hauer
2018-07-04 12:41 ` [PATCH 12/25] ubifs: Add hashes to the tree node cache Sascha Hauer
2018-08-27 19:18   ` Richard Weinberger
2018-08-29 11:16     ` Sascha Hauer
2018-07-04 12:41 ` [PATCH 13/25] ubifs: authentication: Add hashes to index nodes Sascha Hauer
2018-08-27 19:36   ` Richard Weinberger
2018-09-07 10:25     ` Sascha Hauer
2018-07-04 12:41 ` [PATCH 14/25] ubifs: Add authentication nodes to journal Sascha Hauer
2018-07-08  2:59   ` kbuild test robot
2018-08-27 20:48   ` Richard Weinberger
2018-08-29 14:38     ` Sascha Hauer
2018-08-29 14:54       ` Richard Weinberger
2018-08-30 13:41         ` Sascha Hauer
2018-09-02 19:45       ` Richard Weinberger
2018-07-04 12:41 ` [PATCH 15/25] ubifs: Add auth nodes to garbage collector journal head Sascha Hauer
2018-08-27 20:51   ` Richard Weinberger
2018-08-30 14:43     ` Sascha Hauer
2018-08-30 14:43       ` Sascha Hauer
2018-07-04 12:41 ` [PATCH 16/25] ubifs: authenticate replayed journal Sascha Hauer
2018-07-08  6:08   ` kbuild test robot
2018-08-27 21:16   ` Richard Weinberger
2018-07-04 12:41 ` [PATCH 17/25] ubifs: authentication: authenticate LPT Sascha Hauer
2018-07-04 12:41 ` [PATCH 18/25] ubfis: authentication: authenticate master node Sascha Hauer
2018-07-04 12:41 ` [PATCH 19/25] ubifs: Create hash for default LPT Sascha Hauer
2018-07-04 12:41 ` [PATCH 20/25] ubifs: authentication: Authenticate super block node Sascha Hauer
2018-07-04 12:41 ` [PATCH 21/25] ubifs: Add hashes and HMACs to default filesystem Sascha Hauer
2018-07-04 12:41 ` [PATCH 22/25] ubifs: do not update inode size in-place in authenticated mode Sascha Hauer
2018-07-04 12:41 ` [PATCH 23/25] ubifs: Enable authentication support Sascha Hauer
2018-07-04 12:41 ` [PATCH 24/25] ubifs: support offline signed images Sascha Hauer
2018-07-04 12:41 ` [PATCH 25/25] Documentation: ubifs: Add authentication whitepaper Sascha Hauer

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.