Merge tag 'trace-v5.12-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/rostedt...
[linux-2.6-microblaze.git] / drivers / md / dm-integrity.c
index b64fede..46b5d54 100644 (file)
@@ -40,6 +40,7 @@
 #define BITMAP_BLOCK_SIZE              4096    /* don't change it */
 #define BITMAP_FLUSH_INTERVAL          (10 * HZ)
 #define DISCARD_FILLER                 0xf6
+#define SALT_SIZE                      16
 
 /*
  * Warning - DEBUG_PRINT prints security-sensitive data to the log,
@@ -57,6 +58,7 @@
 #define SB_VERSION_2                   2
 #define SB_VERSION_3                   3
 #define SB_VERSION_4                   4
+#define SB_VERSION_5                   5
 #define SB_SECTORS                     8
 #define MAX_SECTORS_PER_BLOCK          8
 
@@ -72,12 +74,15 @@ struct superblock {
        __u8 log2_blocks_per_bitmap_bit;
        __u8 pad[2];
        __u64 recalc_sector;
+       __u8 pad2[8];
+       __u8 salt[SALT_SIZE];
 };
 
 #define SB_FLAG_HAVE_JOURNAL_MAC       0x1
 #define SB_FLAG_RECALCULATING          0x2
 #define SB_FLAG_DIRTY_BITMAP           0x4
 #define SB_FLAG_FIXED_PADDING          0x8
+#define SB_FLAG_FIXED_HMAC             0x10
 
 #define        JOURNAL_ENTRY_ROUNDUP           8
 
@@ -259,6 +264,7 @@ struct dm_integrity_c {
        bool recalculate_flag;
        bool discard;
        bool fix_padding;
+       bool fix_hmac;
        bool legacy_recalculate;
 
        struct alg_spec internal_hash_alg;
@@ -389,8 +395,11 @@ static int dm_integrity_failed(struct dm_integrity_c *ic)
 
 static bool dm_integrity_disable_recalculate(struct dm_integrity_c *ic)
 {
-       if ((ic->internal_hash_alg.key || ic->journal_mac_alg.key) &&
-           !ic->legacy_recalculate)
+       if (ic->legacy_recalculate)
+               return false;
+       if (!(ic->sb->flags & cpu_to_le32(SB_FLAG_FIXED_HMAC)) ?
+           ic->internal_hash_alg.key || ic->journal_mac_alg.key :
+           ic->internal_hash_alg.key && !ic->journal_mac_alg.key)
                return true;
        return false;
 }
@@ -477,7 +486,9 @@ static void wraparound_section(struct dm_integrity_c *ic, unsigned *sec_ptr)
 
 static void sb_set_version(struct dm_integrity_c *ic)
 {
-       if (ic->sb->flags & cpu_to_le32(SB_FLAG_FIXED_PADDING))
+       if (ic->sb->flags & cpu_to_le32(SB_FLAG_FIXED_HMAC))
+               ic->sb->version = SB_VERSION_5;
+       else if (ic->sb->flags & cpu_to_le32(SB_FLAG_FIXED_PADDING))
                ic->sb->version = SB_VERSION_4;
        else if (ic->mode == 'B' || ic->sb->flags & cpu_to_le32(SB_FLAG_DIRTY_BITMAP))
                ic->sb->version = SB_VERSION_3;
@@ -487,10 +498,58 @@ static void sb_set_version(struct dm_integrity_c *ic)
                ic->sb->version = SB_VERSION_1;
 }
 
+static int sb_mac(struct dm_integrity_c *ic, bool wr)
+{
+       SHASH_DESC_ON_STACK(desc, ic->journal_mac);
+       int r;
+       unsigned size = crypto_shash_digestsize(ic->journal_mac);
+
+       if (sizeof(struct superblock) + size > 1 << SECTOR_SHIFT) {
+               dm_integrity_io_error(ic, "digest is too long", -EINVAL);
+               return -EINVAL;
+       }
+
+       desc->tfm = ic->journal_mac;
+
+       r = crypto_shash_init(desc);
+       if (unlikely(r < 0)) {
+               dm_integrity_io_error(ic, "crypto_shash_init", r);
+               return r;
+       }
+
+       r = crypto_shash_update(desc, (__u8 *)ic->sb, (1 << SECTOR_SHIFT) - size);
+       if (unlikely(r < 0)) {
+               dm_integrity_io_error(ic, "crypto_shash_update", r);
+               return r;
+       }
+
+       if (likely(wr)) {
+               r = crypto_shash_final(desc, (__u8 *)ic->sb + (1 << SECTOR_SHIFT) - size);
+               if (unlikely(r < 0)) {
+                       dm_integrity_io_error(ic, "crypto_shash_final", r);
+                       return r;
+               }
+       } else {
+               __u8 result[HASH_MAX_DIGESTSIZE];
+               r = crypto_shash_final(desc, result);
+               if (unlikely(r < 0)) {
+                       dm_integrity_io_error(ic, "crypto_shash_final", r);
+                       return r;
+               }
+               if (memcmp((__u8 *)ic->sb + (1 << SECTOR_SHIFT) - size, result, size)) {
+                       dm_integrity_io_error(ic, "superblock mac", -EILSEQ);
+                       return -EILSEQ;
+               }
+       }
+
+       return 0;
+}
+
 static int sync_rw_sb(struct dm_integrity_c *ic, int op, int op_flags)
 {
        struct dm_io_request io_req;
        struct dm_io_region io_loc;
+       int r;
 
        io_req.bi_op = op;
        io_req.bi_op_flags = op_flags;
@@ -502,10 +561,28 @@ static int sync_rw_sb(struct dm_integrity_c *ic, int op, int op_flags)
        io_loc.sector = ic->start;
        io_loc.count = SB_SECTORS;
 
-       if (op == REQ_OP_WRITE)
+       if (op == REQ_OP_WRITE) {
                sb_set_version(ic);
+               if (ic->journal_mac && ic->sb->flags & cpu_to_le32(SB_FLAG_FIXED_HMAC)) {
+                       r = sb_mac(ic, true);
+                       if (unlikely(r))
+                               return r;
+               }
+       }
 
-       return dm_io(&io_req, 1, &io_loc, NULL);
+       r = dm_io(&io_req, 1, &io_loc, NULL);
+       if (unlikely(r))
+               return r;
+
+       if (op == REQ_OP_READ) {
+               if (ic->mode != 'R' && ic->journal_mac && ic->sb->flags & cpu_to_le32(SB_FLAG_FIXED_HMAC)) {
+                       r = sb_mac(ic, false);
+                       if (unlikely(r))
+                               return r;
+               }
+       }
+
+       return 0;
 }
 
 #define BITMAP_OP_TEST_ALL_SET         0
@@ -722,15 +799,32 @@ static void section_mac(struct dm_integrity_c *ic, unsigned section, __u8 result
        desc->tfm = ic->journal_mac;
 
        r = crypto_shash_init(desc);
-       if (unlikely(r)) {
+       if (unlikely(r < 0)) {
                dm_integrity_io_error(ic, "crypto_shash_init", r);
                goto err;
        }
 
+       if (ic->sb->flags & cpu_to_le32(SB_FLAG_FIXED_HMAC)) {
+               uint64_t section_le;
+
+               r = crypto_shash_update(desc, (__u8 *)&ic->sb->salt, SALT_SIZE);
+               if (unlikely(r < 0)) {
+                       dm_integrity_io_error(ic, "crypto_shash_update", r);
+                       goto err;
+               }
+
+               section_le = cpu_to_le64(section);
+               r = crypto_shash_update(desc, (__u8 *)&section_le, sizeof section_le);
+               if (unlikely(r < 0)) {
+                       dm_integrity_io_error(ic, "crypto_shash_update", r);
+                       goto err;
+               }
+       }
+
        for (j = 0; j < ic->journal_section_entries; j++) {
                struct journal_entry *je = access_journal_entry(ic, section, j);
                r = crypto_shash_update(desc, (__u8 *)&je->u.sector, sizeof je->u.sector);
-               if (unlikely(r)) {
+               if (unlikely(r < 0)) {
                        dm_integrity_io_error(ic, "crypto_shash_update", r);
                        goto err;
                }
@@ -740,7 +834,7 @@ static void section_mac(struct dm_integrity_c *ic, unsigned section, __u8 result
 
        if (likely(size <= JOURNAL_MAC_SIZE)) {
                r = crypto_shash_final(desc, result);
-               if (unlikely(r)) {
+               if (unlikely(r < 0)) {
                        dm_integrity_io_error(ic, "crypto_shash_final", r);
                        goto err;
                }
@@ -753,7 +847,7 @@ static void section_mac(struct dm_integrity_c *ic, unsigned section, __u8 result
                        goto err;
                }
                r = crypto_shash_final(desc, digest);
-               if (unlikely(r)) {
+               if (unlikely(r < 0)) {
                        dm_integrity_io_error(ic, "crypto_shash_final", r);
                        goto err;
                }
@@ -1399,7 +1493,7 @@ static void flush_notify(unsigned long error, void *fr_)
 {
        struct flush_request *fr = fr_;
        if (unlikely(error != 0))
-               dm_integrity_io_error(fr->ic, "flusing disk cache", -EIO);
+               dm_integrity_io_error(fr->ic, "flushing disk cache", -EIO);
        complete(&fr->comp);
 }
 
@@ -1556,6 +1650,14 @@ static void integrity_sector_checksum(struct dm_integrity_c *ic, sector_t sector
                goto failed;
        }
 
+       if (ic->sb->flags & cpu_to_le32(SB_FLAG_FIXED_HMAC)) {
+               r = crypto_shash_update(req, (__u8 *)&ic->sb->salt, SALT_SIZE);
+               if (unlikely(r < 0)) {
+                       dm_integrity_io_error(ic, "crypto_shash_update", r);
+                       goto failed;
+               }
+       }
+
        r = crypto_shash_update(req, (const __u8 *)&sector_le, sizeof sector_le);
        if (unlikely(r < 0)) {
                dm_integrity_io_error(ic, "crypto_shash_update", r);
@@ -3149,6 +3251,7 @@ static void dm_integrity_status(struct dm_target *ti, status_type_t type,
                arg_count += !!ic->journal_crypt_alg.alg_string;
                arg_count += !!ic->journal_mac_alg.alg_string;
                arg_count += (ic->sb->flags & cpu_to_le32(SB_FLAG_FIXED_PADDING)) != 0;
+               arg_count += (ic->sb->flags & cpu_to_le32(SB_FLAG_FIXED_HMAC)) != 0;
                arg_count += ic->legacy_recalculate;
                DMEMIT("%s %llu %u %c %u", ic->dev->name, ic->start,
                       ic->tag_size, ic->mode, arg_count);
@@ -3173,6 +3276,8 @@ static void dm_integrity_status(struct dm_target *ti, status_type_t type,
                }
                if ((ic->sb->flags & cpu_to_le32(SB_FLAG_FIXED_PADDING)) != 0)
                        DMEMIT(" fix_padding");
+               if ((ic->sb->flags & cpu_to_le32(SB_FLAG_FIXED_HMAC)) != 0)
+                       DMEMIT(" fix_hmac");
                if (ic->legacy_recalculate)
                        DMEMIT(" legacy_recalculate");
 
@@ -3310,6 +3415,11 @@ static int initialize_superblock(struct dm_integrity_c *ic, unsigned journal_sec
        if (!journal_sections)
                journal_sections = 1;
 
+       if (ic->fix_hmac && (ic->internal_hash_alg.alg_string || ic->journal_mac_alg.alg_string)) {
+               ic->sb->flags |= cpu_to_le32(SB_FLAG_FIXED_HMAC);
+               get_random_bytes(ic->sb->salt, SALT_SIZE);
+       }
+
        if (!ic->meta_dev) {
                if (ic->fix_padding)
                        ic->sb->flags |= cpu_to_le32(SB_FLAG_FIXED_PADDING);
@@ -3804,7 +3914,7 @@ static int dm_integrity_ctr(struct dm_target *ti, unsigned argc, char **argv)
        unsigned extra_args;
        struct dm_arg_set as;
        static const struct dm_arg _args[] = {
-               {0, 16, "Invalid number of feature args"},
+               {0, 17, "Invalid number of feature args"},
        };
        unsigned journal_sectors, interleave_sectors, buffer_sectors, journal_watermark, sync_msec;
        bool should_write_sb;
@@ -3942,7 +4052,7 @@ static int dm_integrity_ctr(struct dm_target *ti, unsigned argc, char **argv)
                        if (r)
                                goto bad;
                } else if (!strncmp(opt_string, "journal_mac:", strlen("journal_mac:"))) {
-                       r = get_alg_and_key(opt_string, &ic->journal_mac_alg,  &ti->error,
+                       r = get_alg_and_key(opt_string, &ic->journal_mac_alg, &ti->error,
                                            "Invalid journal_mac argument");
                        if (r)
                                goto bad;
@@ -3952,6 +4062,8 @@ static int dm_integrity_ctr(struct dm_target *ti, unsigned argc, char **argv)
                        ic->discard = true;
                } else if (!strcmp(opt_string, "fix_padding")) {
                        ic->fix_padding = true;
+               } else if (!strcmp(opt_string, "fix_hmac")) {
+                       ic->fix_hmac = true;
                } else if (!strcmp(opt_string, "legacy_recalculate")) {
                        ic->legacy_recalculate = true;
                } else {
@@ -4110,7 +4222,7 @@ static int dm_integrity_ctr(struct dm_target *ti, unsigned argc, char **argv)
                        should_write_sb = true;
        }
 
-       if (!ic->sb->version || ic->sb->version > SB_VERSION_4) {
+       if (!ic->sb->version || ic->sb->version > SB_VERSION_5) {
                r = -EINVAL;
                ti->error = "Unknown version";
                goto bad;
@@ -4442,7 +4554,7 @@ static void dm_integrity_dtr(struct dm_target *ti)
 
 static struct target_type integrity_target = {
        .name                   = "integrity",
-       .version                = {1, 6, 0},
+       .version                = {1, 7, 0},
        .module                 = THIS_MODULE,
        .features               = DM_TARGET_SINGLETON | DM_TARGET_INTEGRITY,
        .ctr                    = dm_integrity_ctr,