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

index 5727565..7f09677 100644 (file)
@@ -517,6 +517,65 @@ 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_HMAC_ARR_SZ];
+       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;
+       }
+
+       err = ubifs_hmac_wkm(c, hmac_wkm);
+       if (err)
+               return err;
+
+       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
@@ -527,8 +586,13 @@ static 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 len = ALIGN(UBIFS_SB_NODE_SZ, c->min_io_size);
+       int err;
+
+       err = ubifs_prepare_node_hmac(c, sup, UBIFS_SB_NODE_SZ,
+                                     offsetof(struct ubifs_sb_node, hmac), 1);
+       if (err)
+               return err;
 
-       ubifs_prepare_node(c, sup, UBIFS_SB_NODE_SZ, 1);
        return ubifs_leb_change(c, UBIFS_SB_LNUM, sup, len);
 }
 
@@ -642,6 +706,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);