ubfis: authentication: Authenticate master node
authorSascha Hauer <s.hauer@pengutronix.de>
Fri, 7 Sep 2018 12:36:40 +0000 (14:36 +0200)
committerRichard Weinberger <richard@nod.at>
Tue, 23 Oct 2018 11:48:52 +0000 (13:48 +0200)
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>
Signed-off-by: Richard Weinberger <richard@nod.at>
fs/ubifs/master.c
fs/ubifs/recovery.c
fs/ubifs/ubifs.h

index 0ca9d35..5ea51bb 100644 (file)
 #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;
 }
index 984e30e..5c1334e 100644 (file)
@@ -212,7 +212,10 @@ 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);
+       err = ubifs_prepare_node_hmac(c, mst, UBIFS_MST_NODE_SZ,
+                                     offsetof(struct ubifs_mst_node, hmac), 1);
+       if (err)
+               goto out;
        err = ubifs_leb_change(c, lnum, mst, sz);
        if (err)
                goto out;
@@ -264,9 +267,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) {
index 7e519a4..c26d3c6 100644 (file)
@@ -1900,6 +1900,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);