Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Mikulas Patocka and committed by
Mike Snitzer
09d85f8d 4c9e9883

+136 -13
+11
Documentation/admin-guide/device-mapper/dm-integrity.rst
··· 186 186 space-efficient. If this option is not present, large padding is 187 187 used - that is for compatibility with older kernels. 188 188 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 + 189 200 legacy_recalculate 190 201 Allow recalculating of volumes with HMAC keys. This is disabled by 191 202 default for security reasons - an attacker could modify the volume,
+125 -13
drivers/md/dm-integrity.c
··· 40 40 #define BITMAP_BLOCK_SIZE 4096 /* don't change it */ 41 41 #define BITMAP_FLUSH_INTERVAL (10 * HZ) 42 42 #define DISCARD_FILLER 0xf6 43 + #define SALT_SIZE 16 43 44 44 45 /* 45 46 * Warning - DEBUG_PRINT prints security-sensitive data to the log, ··· 58 57 #define SB_VERSION_2 2 59 58 #define SB_VERSION_3 3 60 59 #define SB_VERSION_4 4 60 + #define SB_VERSION_5 5 61 61 #define SB_SECTORS 8 62 62 #define MAX_SECTORS_PER_BLOCK 8 63 63 ··· 74 72 __u8 log2_blocks_per_bitmap_bit; 75 73 __u8 pad[2]; 76 74 __u64 recalc_sector; 75 + __u8 pad2[8]; 76 + __u8 salt[SALT_SIZE]; 77 77 }; 78 78 79 79 #define SB_FLAG_HAVE_JOURNAL_MAC 0x1 80 80 #define SB_FLAG_RECALCULATING 0x2 81 81 #define SB_FLAG_DIRTY_BITMAP 0x4 82 82 #define SB_FLAG_FIXED_PADDING 0x8 83 + #define SB_FLAG_FIXED_HMAC 0x10 83 84 84 85 #define JOURNAL_ENTRY_ROUNDUP 8 85 86 ··· 264 259 bool recalculate_flag; 265 260 bool discard; 266 261 bool fix_padding; 262 + bool fix_hmac; 267 263 bool legacy_recalculate; 268 264 269 265 struct alg_spec internal_hash_alg; ··· 395 389 396 390 static bool dm_integrity_disable_recalculate(struct dm_integrity_c *ic) 397 391 { 398 - if ((ic->internal_hash_alg.key || ic->journal_mac_alg.key) && 399 - !ic->legacy_recalculate) 392 + if (ic->legacy_recalculate) 393 + return false; 394 + if (!(ic->sb->flags & cpu_to_le32(SB_FLAG_FIXED_HMAC)) ? 395 + ic->internal_hash_alg.key || ic->journal_mac_alg.key : 396 + ic->internal_hash_alg.key && !ic->journal_mac_alg.key) 400 397 return true; 401 398 return false; 402 399 } ··· 486 477 487 478 static void sb_set_version(struct dm_integrity_c *ic) 488 479 { 489 - if (ic->sb->flags & cpu_to_le32(SB_FLAG_FIXED_PADDING)) 480 + if (ic->sb->flags & cpu_to_le32(SB_FLAG_FIXED_HMAC)) 481 + ic->sb->version = SB_VERSION_5; 482 + else if (ic->sb->flags & cpu_to_le32(SB_FLAG_FIXED_PADDING)) 490 483 ic->sb->version = SB_VERSION_4; 491 484 else if (ic->mode == 'B' || ic->sb->flags & cpu_to_le32(SB_FLAG_DIRTY_BITMAP)) 492 485 ic->sb->version = SB_VERSION_3; ··· 498 487 ic->sb->version = SB_VERSION_1; 499 488 } 500 489 490 + static int sb_mac(struct dm_integrity_c *ic, bool wr) 491 + { 492 + SHASH_DESC_ON_STACK(desc, ic->journal_mac); 493 + int r; 494 + unsigned size = crypto_shash_digestsize(ic->journal_mac); 495 + 496 + if (sizeof(struct superblock) + size > 1 << SECTOR_SHIFT) { 497 + dm_integrity_io_error(ic, "digest is too long", -EINVAL); 498 + return -EINVAL; 499 + } 500 + 501 + desc->tfm = ic->journal_mac; 502 + 503 + r = crypto_shash_init(desc); 504 + if (unlikely(r < 0)) { 505 + dm_integrity_io_error(ic, "crypto_shash_init", r); 506 + return r; 507 + } 508 + 509 + r = crypto_shash_update(desc, (__u8 *)ic->sb, (1 << SECTOR_SHIFT) - size); 510 + if (unlikely(r < 0)) { 511 + dm_integrity_io_error(ic, "crypto_shash_update", r); 512 + return r; 513 + } 514 + 515 + if (likely(wr)) { 516 + r = crypto_shash_final(desc, (__u8 *)ic->sb + (1 << SECTOR_SHIFT) - size); 517 + if (unlikely(r < 0)) { 518 + dm_integrity_io_error(ic, "crypto_shash_final", r); 519 + return r; 520 + } 521 + } else { 522 + __u8 result[HASH_MAX_DIGESTSIZE]; 523 + r = crypto_shash_final(desc, result); 524 + if (unlikely(r < 0)) { 525 + dm_integrity_io_error(ic, "crypto_shash_final", r); 526 + return r; 527 + } 528 + if (memcmp((__u8 *)ic->sb + (1 << SECTOR_SHIFT) - size, result, size)) { 529 + dm_integrity_io_error(ic, "superblock mac", -EILSEQ); 530 + return -EILSEQ; 531 + } 532 + } 533 + 534 + return 0; 535 + } 536 + 501 537 static int sync_rw_sb(struct dm_integrity_c *ic, int op, int op_flags) 502 538 { 503 539 struct dm_io_request io_req; 504 540 struct dm_io_region io_loc; 541 + int r; 505 542 506 543 io_req.bi_op = op; 507 544 io_req.bi_op_flags = op_flags; ··· 561 502 io_loc.sector = ic->start; 562 503 io_loc.count = SB_SECTORS; 563 504 564 - if (op == REQ_OP_WRITE) 505 + if (op == REQ_OP_WRITE) { 565 506 sb_set_version(ic); 507 + if (ic->journal_mac && ic->sb->flags & cpu_to_le32(SB_FLAG_FIXED_HMAC)) { 508 + r = sb_mac(ic, true); 509 + if (unlikely(r)) 510 + return r; 511 + } 512 + } 566 513 567 - return dm_io(&io_req, 1, &io_loc, NULL); 514 + r = dm_io(&io_req, 1, &io_loc, NULL); 515 + if (unlikely(r)) 516 + return r; 517 + 518 + if (op == REQ_OP_READ) { 519 + if (ic->mode != 'R' && ic->journal_mac && ic->sb->flags & cpu_to_le32(SB_FLAG_FIXED_HMAC)) { 520 + r = sb_mac(ic, false); 521 + if (unlikely(r)) 522 + return r; 523 + } 524 + } 525 + 526 + return 0; 568 527 } 569 528 570 529 #define BITMAP_OP_TEST_ALL_SET 0 ··· 799 722 desc->tfm = ic->journal_mac; 800 723 801 724 r = crypto_shash_init(desc); 802 - if (unlikely(r)) { 725 + if (unlikely(r < 0)) { 803 726 dm_integrity_io_error(ic, "crypto_shash_init", r); 804 727 goto err; 728 + } 729 + 730 + if (ic->sb->flags & cpu_to_le32(SB_FLAG_FIXED_HMAC)) { 731 + uint64_t section_le; 732 + 733 + r = crypto_shash_update(desc, (__u8 *)&ic->sb->salt, SALT_SIZE); 734 + if (unlikely(r < 0)) { 735 + dm_integrity_io_error(ic, "crypto_shash_update", r); 736 + goto err; 737 + } 738 + 739 + section_le = cpu_to_le64(section); 740 + r = crypto_shash_update(desc, (__u8 *)&section_le, sizeof section_le); 741 + if (unlikely(r < 0)) { 742 + dm_integrity_io_error(ic, "crypto_shash_update", r); 743 + goto err; 744 + } 805 745 } 806 746 807 747 for (j = 0; j < ic->journal_section_entries; j++) { 808 748 struct journal_entry *je = access_journal_entry(ic, section, j); 809 749 r = crypto_shash_update(desc, (__u8 *)&je->u.sector, sizeof je->u.sector); 810 - if (unlikely(r)) { 750 + if (unlikely(r < 0)) { 811 751 dm_integrity_io_error(ic, "crypto_shash_update", r); 812 752 goto err; 813 753 } ··· 834 740 835 741 if (likely(size <= JOURNAL_MAC_SIZE)) { 836 742 r = crypto_shash_final(desc, result); 837 - if (unlikely(r)) { 743 + if (unlikely(r < 0)) { 838 744 dm_integrity_io_error(ic, "crypto_shash_final", r); 839 745 goto err; 840 746 } ··· 847 753 goto err; 848 754 } 849 755 r = crypto_shash_final(desc, digest); 850 - if (unlikely(r)) { 756 + if (unlikely(r < 0)) { 851 757 dm_integrity_io_error(ic, "crypto_shash_final", r); 852 758 goto err; 853 759 } ··· 1648 1554 if (unlikely(r < 0)) { 1649 1555 dm_integrity_io_error(ic, "crypto_shash_init", r); 1650 1556 goto failed; 1557 + } 1558 + 1559 + if (ic->sb->flags & cpu_to_le32(SB_FLAG_FIXED_HMAC)) { 1560 + r = crypto_shash_update(req, (__u8 *)&ic->sb->salt, SALT_SIZE); 1561 + if (unlikely(r < 0)) { 1562 + dm_integrity_io_error(ic, "crypto_shash_update", r); 1563 + goto failed; 1564 + } 1651 1565 } 1652 1566 1653 1567 r = crypto_shash_update(req, (const __u8 *)&sector_le, sizeof sector_le); ··· 3251 3149 arg_count += !!ic->journal_crypt_alg.alg_string; 3252 3150 arg_count += !!ic->journal_mac_alg.alg_string; 3253 3151 arg_count += (ic->sb->flags & cpu_to_le32(SB_FLAG_FIXED_PADDING)) != 0; 3152 + arg_count += (ic->sb->flags & cpu_to_le32(SB_FLAG_FIXED_HMAC)) != 0; 3254 3153 arg_count += ic->legacy_recalculate; 3255 3154 DMEMIT("%s %llu %u %c %u", ic->dev->name, ic->start, 3256 3155 ic->tag_size, ic->mode, arg_count); ··· 3276 3173 } 3277 3174 if ((ic->sb->flags & cpu_to_le32(SB_FLAG_FIXED_PADDING)) != 0) 3278 3175 DMEMIT(" fix_padding"); 3176 + if ((ic->sb->flags & cpu_to_le32(SB_FLAG_FIXED_HMAC)) != 0) 3177 + DMEMIT(" fix_hmac"); 3279 3178 if (ic->legacy_recalculate) 3280 3179 DMEMIT(" legacy_recalculate"); 3281 3180 ··· 3414 3309 journal_sections = journal_sectors / ic->journal_section_sectors; 3415 3310 if (!journal_sections) 3416 3311 journal_sections = 1; 3312 + 3313 + if (ic->fix_hmac && (ic->internal_hash_alg.alg_string || ic->journal_mac_alg.alg_string)) { 3314 + ic->sb->flags |= cpu_to_le32(SB_FLAG_FIXED_HMAC); 3315 + get_random_bytes(ic->sb->salt, SALT_SIZE); 3316 + } 3417 3317 3418 3318 if (!ic->meta_dev) { 3419 3319 if (ic->fix_padding) ··· 3914 3804 unsigned extra_args; 3915 3805 struct dm_arg_set as; 3916 3806 static const struct dm_arg _args[] = { 3917 - {0, 16, "Invalid number of feature args"}, 3807 + {0, 17, "Invalid number of feature args"}, 3918 3808 }; 3919 3809 unsigned journal_sectors, interleave_sectors, buffer_sectors, journal_watermark, sync_msec; 3920 3810 bool should_write_sb; ··· 4052 3942 if (r) 4053 3943 goto bad; 4054 3944 } else if (!strncmp(opt_string, "journal_mac:", strlen("journal_mac:"))) { 4055 - r = get_alg_and_key(opt_string, &ic->journal_mac_alg, &ti->error, 3945 + r = get_alg_and_key(opt_string, &ic->journal_mac_alg, &ti->error, 4056 3946 "Invalid journal_mac argument"); 4057 3947 if (r) 4058 3948 goto bad; ··· 4062 3952 ic->discard = true; 4063 3953 } else if (!strcmp(opt_string, "fix_padding")) { 4064 3954 ic->fix_padding = true; 3955 + } else if (!strcmp(opt_string, "fix_hmac")) { 3956 + ic->fix_hmac = true; 4065 3957 } else if (!strcmp(opt_string, "legacy_recalculate")) { 4066 3958 ic->legacy_recalculate = true; 4067 3959 } else { ··· 4222 4110 should_write_sb = true; 4223 4111 } 4224 4112 4225 - if (!ic->sb->version || ic->sb->version > SB_VERSION_4) { 4113 + if (!ic->sb->version || ic->sb->version > SB_VERSION_5) { 4226 4114 r = -EINVAL; 4227 4115 ti->error = "Unknown version"; 4228 4116 goto bad; ··· 4554 4442 4555 4443 static struct target_type integrity_target = { 4556 4444 .name = "integrity", 4557 - .version = {1, 6, 0}, 4445 + .version = {1, 7, 0}, 4558 4446 .module = THIS_MODULE, 4559 4447 .features = DM_TARGET_SINGLETON | DM_TARGET_INTEGRITY, 4560 4448 .ctr = dm_integrity_ctr,