Skip to content

Commit 09d85f8

Browse files
Mikulas Patockasnitm
authored andcommitted
dm integrity: introduce the "fix_hmac" argument
The "fix_hmac" argument improves security of internal_hash and journal_mac: - the section number is mixed to the mac, so that an attacker can't copy sectors from one journal section to another journal section - the superblock is protected by journal_mac - a 16-byte salt stored in the superblock is mixed to the mac, so that the attacker can't detect that two disks have the same hmac key and also to disallow the attacker to move sectors from one disk to another Signed-off-by: Mikulas Patocka <mpatocka@redhat.com> Reported-by: Daniel Glockner <dg@emlix.com> Signed-off-by: Lukas Bulwahn <lukas.bulwahn@gmail.com> # ReST fix Tested-by: Milan Broz <gmazyland@gmail.com> Signed-off-by: Mike Snitzer <snitzer@redhat.com>
1 parent 4c9e988 commit 09d85f8

2 files changed

Lines changed: 136 additions & 13 deletions

File tree

Documentation/admin-guide/device-mapper/dm-integrity.rst

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,17 @@ fix_padding
186186
space-efficient. If this option is not present, large padding is
187187
used - that is for compatibility with older kernels.
188188

189+
fix_hmac
190+
Improve security of internal_hash and journal_mac:
191+
192+
- the section number is mixed to the mac, so that an attacker can't
193+
copy sectors from one journal section to another journal section
194+
- the superblock is protected by journal_mac
195+
- a 16-byte salt stored in the superblock is mixed to the mac, so
196+
that the attacker can't detect that two disks have the same hmac
197+
key and also to disallow the attacker to move sectors from one
198+
disk to another
199+
189200
legacy_recalculate
190201
Allow recalculating of volumes with HMAC keys. This is disabled by
191202
default for security reasons - an attacker could modify the volume,

drivers/md/dm-integrity.c

Lines changed: 125 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
#define BITMAP_BLOCK_SIZE 4096 /* don't change it */
4141
#define BITMAP_FLUSH_INTERVAL (10 * HZ)
4242
#define DISCARD_FILLER 0xf6
43+
#define SALT_SIZE 16
4344

4445
/*
4546
* Warning - DEBUG_PRINT prints security-sensitive data to the log,
@@ -57,6 +58,7 @@
5758
#define SB_VERSION_2 2
5859
#define SB_VERSION_3 3
5960
#define SB_VERSION_4 4
61+
#define SB_VERSION_5 5
6062
#define SB_SECTORS 8
6163
#define MAX_SECTORS_PER_BLOCK 8
6264

@@ -72,12 +74,15 @@ struct superblock {
7274
__u8 log2_blocks_per_bitmap_bit;
7375
__u8 pad[2];
7476
__u64 recalc_sector;
77+
__u8 pad2[8];
78+
__u8 salt[SALT_SIZE];
7579
};
7680

7781
#define SB_FLAG_HAVE_JOURNAL_MAC 0x1
7882
#define SB_FLAG_RECALCULATING 0x2
7983
#define SB_FLAG_DIRTY_BITMAP 0x4
8084
#define SB_FLAG_FIXED_PADDING 0x8
85+
#define SB_FLAG_FIXED_HMAC 0x10
8186

8287
#define JOURNAL_ENTRY_ROUNDUP 8
8388

@@ -259,6 +264,7 @@ struct dm_integrity_c {
259264
bool recalculate_flag;
260265
bool discard;
261266
bool fix_padding;
267+
bool fix_hmac;
262268
bool legacy_recalculate;
263269

264270
struct alg_spec internal_hash_alg;
@@ -389,8 +395,11 @@ static int dm_integrity_failed(struct dm_integrity_c *ic)
389395

390396
static bool dm_integrity_disable_recalculate(struct dm_integrity_c *ic)
391397
{
392-
if ((ic->internal_hash_alg.key || ic->journal_mac_alg.key) &&
393-
!ic->legacy_recalculate)
398+
if (ic->legacy_recalculate)
399+
return false;
400+
if (!(ic->sb->flags & cpu_to_le32(SB_FLAG_FIXED_HMAC)) ?
401+
ic->internal_hash_alg.key || ic->journal_mac_alg.key :
402+
ic->internal_hash_alg.key && !ic->journal_mac_alg.key)
394403
return true;
395404
return false;
396405
}
@@ -477,7 +486,9 @@ static void wraparound_section(struct dm_integrity_c *ic, unsigned *sec_ptr)
477486

478487
static void sb_set_version(struct dm_integrity_c *ic)
479488
{
480-
if (ic->sb->flags & cpu_to_le32(SB_FLAG_FIXED_PADDING))
489+
if (ic->sb->flags & cpu_to_le32(SB_FLAG_FIXED_HMAC))
490+
ic->sb->version = SB_VERSION_5;
491+
else if (ic->sb->flags & cpu_to_le32(SB_FLAG_FIXED_PADDING))
481492
ic->sb->version = SB_VERSION_4;
482493
else if (ic->mode == 'B' || ic->sb->flags & cpu_to_le32(SB_FLAG_DIRTY_BITMAP))
483494
ic->sb->version = SB_VERSION_3;
@@ -487,10 +498,58 @@ static void sb_set_version(struct dm_integrity_c *ic)
487498
ic->sb->version = SB_VERSION_1;
488499
}
489500

501+
static int sb_mac(struct dm_integrity_c *ic, bool wr)
502+
{
503+
SHASH_DESC_ON_STACK(desc, ic->journal_mac);
504+
int r;
505+
unsigned size = crypto_shash_digestsize(ic->journal_mac);
506+
507+
if (sizeof(struct superblock) + size > 1 << SECTOR_SHIFT) {
508+
dm_integrity_io_error(ic, "digest is too long", -EINVAL);
509+
return -EINVAL;
510+
}
511+
512+
desc->tfm = ic->journal_mac;
513+
514+
r = crypto_shash_init(desc);
515+
if (unlikely(r < 0)) {
516+
dm_integrity_io_error(ic, "crypto_shash_init", r);
517+
return r;
518+
}
519+
520+
r = crypto_shash_update(desc, (__u8 *)ic->sb, (1 << SECTOR_SHIFT) - size);
521+
if (unlikely(r < 0)) {
522+
dm_integrity_io_error(ic, "crypto_shash_update", r);
523+
return r;
524+
}
525+
526+
if (likely(wr)) {
527+
r = crypto_shash_final(desc, (__u8 *)ic->sb + (1 << SECTOR_SHIFT) - size);
528+
if (unlikely(r < 0)) {
529+
dm_integrity_io_error(ic, "crypto_shash_final", r);
530+
return r;
531+
}
532+
} else {
533+
__u8 result[HASH_MAX_DIGESTSIZE];
534+
r = crypto_shash_final(desc, result);
535+
if (unlikely(r < 0)) {
536+
dm_integrity_io_error(ic, "crypto_shash_final", r);
537+
return r;
538+
}
539+
if (memcmp((__u8 *)ic->sb + (1 << SECTOR_SHIFT) - size, result, size)) {
540+
dm_integrity_io_error(ic, "superblock mac", -EILSEQ);
541+
return -EILSEQ;
542+
}
543+
}
544+
545+
return 0;
546+
}
547+
490548
static int sync_rw_sb(struct dm_integrity_c *ic, int op, int op_flags)
491549
{
492550
struct dm_io_request io_req;
493551
struct dm_io_region io_loc;
552+
int r;
494553

495554
io_req.bi_op = op;
496555
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)
502561
io_loc.sector = ic->start;
503562
io_loc.count = SB_SECTORS;
504563

505-
if (op == REQ_OP_WRITE)
564+
if (op == REQ_OP_WRITE) {
506565
sb_set_version(ic);
566+
if (ic->journal_mac && ic->sb->flags & cpu_to_le32(SB_FLAG_FIXED_HMAC)) {
567+
r = sb_mac(ic, true);
568+
if (unlikely(r))
569+
return r;
570+
}
571+
}
507572

508-
return dm_io(&io_req, 1, &io_loc, NULL);
573+
r = dm_io(&io_req, 1, &io_loc, NULL);
574+
if (unlikely(r))
575+
return r;
576+
577+
if (op == REQ_OP_READ) {
578+
if (ic->mode != 'R' && ic->journal_mac && ic->sb->flags & cpu_to_le32(SB_FLAG_FIXED_HMAC)) {
579+
r = sb_mac(ic, false);
580+
if (unlikely(r))
581+
return r;
582+
}
583+
}
584+
585+
return 0;
509586
}
510587

511588
#define BITMAP_OP_TEST_ALL_SET 0
@@ -722,15 +799,32 @@ static void section_mac(struct dm_integrity_c *ic, unsigned section, __u8 result
722799
desc->tfm = ic->journal_mac;
723800

724801
r = crypto_shash_init(desc);
725-
if (unlikely(r)) {
802+
if (unlikely(r < 0)) {
726803
dm_integrity_io_error(ic, "crypto_shash_init", r);
727804
goto err;
728805
}
729806

807+
if (ic->sb->flags & cpu_to_le32(SB_FLAG_FIXED_HMAC)) {
808+
uint64_t section_le;
809+
810+
r = crypto_shash_update(desc, (__u8 *)&ic->sb->salt, SALT_SIZE);
811+
if (unlikely(r < 0)) {
812+
dm_integrity_io_error(ic, "crypto_shash_update", r);
813+
goto err;
814+
}
815+
816+
section_le = cpu_to_le64(section);
817+
r = crypto_shash_update(desc, (__u8 *)&section_le, sizeof section_le);
818+
if (unlikely(r < 0)) {
819+
dm_integrity_io_error(ic, "crypto_shash_update", r);
820+
goto err;
821+
}
822+
}
823+
730824
for (j = 0; j < ic->journal_section_entries; j++) {
731825
struct journal_entry *je = access_journal_entry(ic, section, j);
732826
r = crypto_shash_update(desc, (__u8 *)&je->u.sector, sizeof je->u.sector);
733-
if (unlikely(r)) {
827+
if (unlikely(r < 0)) {
734828
dm_integrity_io_error(ic, "crypto_shash_update", r);
735829
goto err;
736830
}
@@ -740,7 +834,7 @@ static void section_mac(struct dm_integrity_c *ic, unsigned section, __u8 result
740834

741835
if (likely(size <= JOURNAL_MAC_SIZE)) {
742836
r = crypto_shash_final(desc, result);
743-
if (unlikely(r)) {
837+
if (unlikely(r < 0)) {
744838
dm_integrity_io_error(ic, "crypto_shash_final", r);
745839
goto err;
746840
}
@@ -753,7 +847,7 @@ static void section_mac(struct dm_integrity_c *ic, unsigned section, __u8 result
753847
goto err;
754848
}
755849
r = crypto_shash_final(desc, digest);
756-
if (unlikely(r)) {
850+
if (unlikely(r < 0)) {
757851
dm_integrity_io_error(ic, "crypto_shash_final", r);
758852
goto err;
759853
}
@@ -1556,6 +1650,14 @@ static void integrity_sector_checksum(struct dm_integrity_c *ic, sector_t sector
15561650
goto failed;
15571651
}
15581652

1653+
if (ic->sb->flags & cpu_to_le32(SB_FLAG_FIXED_HMAC)) {
1654+
r = crypto_shash_update(req, (__u8 *)&ic->sb->salt, SALT_SIZE);
1655+
if (unlikely(r < 0)) {
1656+
dm_integrity_io_error(ic, "crypto_shash_update", r);
1657+
goto failed;
1658+
}
1659+
}
1660+
15591661
r = crypto_shash_update(req, (const __u8 *)&sector_le, sizeof sector_le);
15601662
if (unlikely(r < 0)) {
15611663
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,
31493251
arg_count += !!ic->journal_crypt_alg.alg_string;
31503252
arg_count += !!ic->journal_mac_alg.alg_string;
31513253
arg_count += (ic->sb->flags & cpu_to_le32(SB_FLAG_FIXED_PADDING)) != 0;
3254+
arg_count += (ic->sb->flags & cpu_to_le32(SB_FLAG_FIXED_HMAC)) != 0;
31523255
arg_count += ic->legacy_recalculate;
31533256
DMEMIT("%s %llu %u %c %u", ic->dev->name, ic->start,
31543257
ic->tag_size, ic->mode, arg_count);
@@ -3173,6 +3276,8 @@ static void dm_integrity_status(struct dm_target *ti, status_type_t type,
31733276
}
31743277
if ((ic->sb->flags & cpu_to_le32(SB_FLAG_FIXED_PADDING)) != 0)
31753278
DMEMIT(" fix_padding");
3279+
if ((ic->sb->flags & cpu_to_le32(SB_FLAG_FIXED_HMAC)) != 0)
3280+
DMEMIT(" fix_hmac");
31763281
if (ic->legacy_recalculate)
31773282
DMEMIT(" legacy_recalculate");
31783283

@@ -3310,6 +3415,11 @@ static int initialize_superblock(struct dm_integrity_c *ic, unsigned journal_sec
33103415
if (!journal_sections)
33113416
journal_sections = 1;
33123417

3418+
if (ic->fix_hmac && (ic->internal_hash_alg.alg_string || ic->journal_mac_alg.alg_string)) {
3419+
ic->sb->flags |= cpu_to_le32(SB_FLAG_FIXED_HMAC);
3420+
get_random_bytes(ic->sb->salt, SALT_SIZE);
3421+
}
3422+
33133423
if (!ic->meta_dev) {
33143424
if (ic->fix_padding)
33153425
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)
38043914
unsigned extra_args;
38053915
struct dm_arg_set as;
38063916
static const struct dm_arg _args[] = {
3807-
{0, 16, "Invalid number of feature args"},
3917+
{0, 17, "Invalid number of feature args"},
38083918
};
38093919
unsigned journal_sectors, interleave_sectors, buffer_sectors, journal_watermark, sync_msec;
38103920
bool should_write_sb;
@@ -3942,7 +4052,7 @@ static int dm_integrity_ctr(struct dm_target *ti, unsigned argc, char **argv)
39424052
if (r)
39434053
goto bad;
39444054
} else if (!strncmp(opt_string, "journal_mac:", strlen("journal_mac:"))) {
3945-
r = get_alg_and_key(opt_string, &ic->journal_mac_alg, &ti->error,
4055+
r = get_alg_and_key(opt_string, &ic->journal_mac_alg, &ti->error,
39464056
"Invalid journal_mac argument");
39474057
if (r)
39484058
goto bad;
@@ -3952,6 +4062,8 @@ static int dm_integrity_ctr(struct dm_target *ti, unsigned argc, char **argv)
39524062
ic->discard = true;
39534063
} else if (!strcmp(opt_string, "fix_padding")) {
39544064
ic->fix_padding = true;
4065+
} else if (!strcmp(opt_string, "fix_hmac")) {
4066+
ic->fix_hmac = true;
39554067
} else if (!strcmp(opt_string, "legacy_recalculate")) {
39564068
ic->legacy_recalculate = true;
39574069
} else {
@@ -4110,7 +4222,7 @@ static int dm_integrity_ctr(struct dm_target *ti, unsigned argc, char **argv)
41104222
should_write_sb = true;
41114223
}
41124224

4113-
if (!ic->sb->version || ic->sb->version > SB_VERSION_4) {
4225+
if (!ic->sb->version || ic->sb->version > SB_VERSION_5) {
41144226
r = -EINVAL;
41154227
ti->error = "Unknown version";
41164228
goto bad;
@@ -4442,7 +4554,7 @@ static void dm_integrity_dtr(struct dm_target *ti)
44424554

44434555
static struct target_type integrity_target = {
44444556
.name = "integrity",
4445-
.version = {1, 6, 0},
4557+
.version = {1, 7, 0},
44464558
.module = THIS_MODULE,
44474559
.features = DM_TARGET_SINGLETON | DM_TARGET_INTEGRITY,
44484560
.ctr = dm_integrity_ctr,

0 commit comments

Comments
 (0)