| .. | .. |
|---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-only |
|---|
| 1 | 2 | /* |
|---|
| 2 | 3 | * Copyright (C) 2012 Red Hat, Inc. |
|---|
| 3 | 4 | * |
|---|
| 4 | 5 | * Author: Mikulas Patocka <mpatocka@redhat.com> |
|---|
| 5 | 6 | * |
|---|
| 6 | 7 | * Based on Chromium dm-verity driver (C) 2011 The Chromium OS Authors |
|---|
| 7 | | - * |
|---|
| 8 | | - * This file is released under the GPLv2. |
|---|
| 9 | 8 | * |
|---|
| 10 | 9 | * In the file "/sys/module/dm_verity/parameters/prefetch_cluster" you can set |
|---|
| 11 | 10 | * default prefetch value. Data are read in "prefetch_cluster" chunks from the |
|---|
| .. | .. |
|---|
| 16 | 15 | |
|---|
| 17 | 16 | #include "dm-verity.h" |
|---|
| 18 | 17 | #include "dm-verity-fec.h" |
|---|
| 19 | | - |
|---|
| 18 | +#include "dm-verity-verify-sig.h" |
|---|
| 20 | 19 | #include <linux/module.h> |
|---|
| 21 | 20 | #include <linux/reboot.h> |
|---|
| 22 | 21 | |
|---|
| .. | .. |
|---|
| 31 | 30 | |
|---|
| 32 | 31 | #define DM_VERITY_OPT_LOGGING "ignore_corruption" |
|---|
| 33 | 32 | #define DM_VERITY_OPT_RESTART "restart_on_corruption" |
|---|
| 33 | +#define DM_VERITY_OPT_PANIC "panic_on_corruption" |
|---|
| 34 | 34 | #define DM_VERITY_OPT_IGN_ZEROES "ignore_zero_blocks" |
|---|
| 35 | 35 | #define DM_VERITY_OPT_AT_MOST_ONCE "check_at_most_once" |
|---|
| 36 | 36 | |
|---|
| 37 | | -#define DM_VERITY_OPTS_MAX (3 + DM_VERITY_OPTS_FEC) |
|---|
| 37 | +#define DM_VERITY_OPTS_MAX (3 + DM_VERITY_OPTS_FEC + \ |
|---|
| 38 | + DM_VERITY_ROOT_HASH_VERIFICATION_OPTS) |
|---|
| 38 | 39 | |
|---|
| 39 | 40 | static unsigned dm_verity_prefetch_cluster = DM_VERITY_DEFAULT_PREFETCH_SIZE; |
|---|
| 40 | 41 | |
|---|
| .. | .. |
|---|
| 251 | 252 | if (v->mode == DM_VERITY_MODE_LOGGING) |
|---|
| 252 | 253 | return 0; |
|---|
| 253 | 254 | |
|---|
| 254 | | - if (v->mode == DM_VERITY_MODE_RESTART) { |
|---|
| 255 | | -#ifdef CONFIG_DM_VERITY_AVB |
|---|
| 256 | | - dm_verity_avb_error_handler(); |
|---|
| 257 | | -#endif |
|---|
| 255 | + if (v->mode == DM_VERITY_MODE_RESTART) |
|---|
| 258 | 256 | kernel_restart("dm-verity device corrupted"); |
|---|
| 259 | | - } |
|---|
| 257 | + |
|---|
| 258 | + if (v->mode == DM_VERITY_MODE_PANIC) |
|---|
| 259 | + panic("dm-verity device corrupted"); |
|---|
| 260 | 260 | |
|---|
| 261 | 261 | return 1; |
|---|
| 262 | 262 | } |
|---|
| .. | .. |
|---|
| 634 | 634 | |
|---|
| 635 | 635 | static void verity_submit_prefetch(struct dm_verity *v, struct dm_verity_io *io) |
|---|
| 636 | 636 | { |
|---|
| 637 | + sector_t block = io->block; |
|---|
| 638 | + unsigned int n_blocks = io->n_blocks; |
|---|
| 637 | 639 | struct dm_verity_prefetch_work *pw; |
|---|
| 640 | + |
|---|
| 641 | + if (v->validated_blocks) { |
|---|
| 642 | + while (n_blocks && test_bit(block, v->validated_blocks)) { |
|---|
| 643 | + block++; |
|---|
| 644 | + n_blocks--; |
|---|
| 645 | + } |
|---|
| 646 | + while (n_blocks && test_bit(block + n_blocks - 1, |
|---|
| 647 | + v->validated_blocks)) |
|---|
| 648 | + n_blocks--; |
|---|
| 649 | + if (!n_blocks) |
|---|
| 650 | + return; |
|---|
| 651 | + } |
|---|
| 638 | 652 | |
|---|
| 639 | 653 | pw = kmalloc(sizeof(struct dm_verity_prefetch_work), |
|---|
| 640 | 654 | GFP_NOIO | __GFP_NORETRY | __GFP_NOMEMALLOC | __GFP_NOWARN); |
|---|
| .. | .. |
|---|
| 644 | 658 | |
|---|
| 645 | 659 | INIT_WORK(&pw->work, verity_prefetch_io); |
|---|
| 646 | 660 | pw->v = v; |
|---|
| 647 | | - pw->block = io->block; |
|---|
| 648 | | - pw->n_blocks = io->n_blocks; |
|---|
| 661 | + pw->block = block; |
|---|
| 662 | + pw->n_blocks = n_blocks; |
|---|
| 649 | 663 | queue_work(v->verify_wq, &pw->work); |
|---|
| 650 | 664 | } |
|---|
| 651 | 665 | |
|---|
| .. | .. |
|---|
| 690 | 704 | |
|---|
| 691 | 705 | verity_submit_prefetch(v, io); |
|---|
| 692 | 706 | |
|---|
| 693 | | - generic_make_request(bio); |
|---|
| 707 | + submit_bio_noacct(bio); |
|---|
| 694 | 708 | |
|---|
| 695 | 709 | return DM_MAPIO_SUBMITTED; |
|---|
| 696 | 710 | } |
|---|
| .. | .. |
|---|
| 737 | 751 | args++; |
|---|
| 738 | 752 | if (v->validated_blocks) |
|---|
| 739 | 753 | args++; |
|---|
| 754 | + if (v->signature_key_desc) |
|---|
| 755 | + args += DM_VERITY_ROOT_HASH_VERIFICATION_OPTS; |
|---|
| 740 | 756 | if (!args) |
|---|
| 741 | 757 | return; |
|---|
| 742 | 758 | DMEMIT(" %u", args); |
|---|
| .. | .. |
|---|
| 749 | 765 | case DM_VERITY_MODE_RESTART: |
|---|
| 750 | 766 | DMEMIT(DM_VERITY_OPT_RESTART); |
|---|
| 751 | 767 | break; |
|---|
| 768 | + case DM_VERITY_MODE_PANIC: |
|---|
| 769 | + DMEMIT(DM_VERITY_OPT_PANIC); |
|---|
| 770 | + break; |
|---|
| 752 | 771 | default: |
|---|
| 753 | 772 | BUG(); |
|---|
| 754 | 773 | } |
|---|
| .. | .. |
|---|
| 758 | 777 | if (v->validated_blocks) |
|---|
| 759 | 778 | DMEMIT(" " DM_VERITY_OPT_AT_MOST_ONCE); |
|---|
| 760 | 779 | sz = verity_fec_status_table(v, sz, result, maxlen); |
|---|
| 780 | + if (v->signature_key_desc) |
|---|
| 781 | + DMEMIT(" " DM_VERITY_ROOT_HASH_VERIFICATION_OPT_SIG_KEY |
|---|
| 782 | + " %s", v->signature_key_desc); |
|---|
| 761 | 783 | break; |
|---|
| 762 | 784 | } |
|---|
| 763 | 785 | } |
|---|
| .. | .. |
|---|
| 823 | 845 | |
|---|
| 824 | 846 | verity_fec_dtr(v); |
|---|
| 825 | 847 | |
|---|
| 848 | + kfree(v->signature_key_desc); |
|---|
| 849 | + |
|---|
| 826 | 850 | kfree(v); |
|---|
| 827 | 851 | } |
|---|
| 828 | 852 | |
|---|
| .. | .. |
|---|
| 878 | 902 | return r; |
|---|
| 879 | 903 | } |
|---|
| 880 | 904 | |
|---|
| 881 | | -static int verity_parse_opt_args(struct dm_arg_set *as, struct dm_verity *v) |
|---|
| 905 | +static int verity_parse_opt_args(struct dm_arg_set *as, struct dm_verity *v, |
|---|
| 906 | + struct dm_verity_sig_opts *verify_args) |
|---|
| 882 | 907 | { |
|---|
| 883 | 908 | int r; |
|---|
| 884 | 909 | unsigned argc; |
|---|
| .. | .. |
|---|
| 908 | 933 | v->mode = DM_VERITY_MODE_RESTART; |
|---|
| 909 | 934 | continue; |
|---|
| 910 | 935 | |
|---|
| 936 | + } else if (!strcasecmp(arg_name, DM_VERITY_OPT_PANIC)) { |
|---|
| 937 | + v->mode = DM_VERITY_MODE_PANIC; |
|---|
| 938 | + continue; |
|---|
| 939 | + |
|---|
| 911 | 940 | } else if (!strcasecmp(arg_name, DM_VERITY_OPT_IGN_ZEROES)) { |
|---|
| 912 | 941 | r = verity_alloc_zero_digest(v); |
|---|
| 913 | 942 | if (r) { |
|---|
| .. | .. |
|---|
| 927 | 956 | if (r) |
|---|
| 928 | 957 | return r; |
|---|
| 929 | 958 | continue; |
|---|
| 959 | + } else if (verity_verify_is_sig_opt_arg(arg_name)) { |
|---|
| 960 | + r = verity_verify_sig_parse_opt_args(as, v, |
|---|
| 961 | + verify_args, |
|---|
| 962 | + &argc, arg_name); |
|---|
| 963 | + if (r) |
|---|
| 964 | + return r; |
|---|
| 965 | + continue; |
|---|
| 966 | + |
|---|
| 930 | 967 | } |
|---|
| 931 | 968 | |
|---|
| 932 | 969 | ti->error = "Unrecognized verity feature request"; |
|---|
| .. | .. |
|---|
| 953 | 990 | static int verity_ctr(struct dm_target *ti, unsigned argc, char **argv) |
|---|
| 954 | 991 | { |
|---|
| 955 | 992 | struct dm_verity *v; |
|---|
| 993 | + struct dm_verity_sig_opts verify_args = {0}; |
|---|
| 956 | 994 | struct dm_arg_set as; |
|---|
| 957 | 995 | unsigned int num; |
|---|
| 958 | 996 | unsigned long long num_ll; |
|---|
| .. | .. |
|---|
| 960 | 998 | int i; |
|---|
| 961 | 999 | sector_t hash_position; |
|---|
| 962 | 1000 | char dummy; |
|---|
| 1001 | + char *root_hash_digest_to_validate; |
|---|
| 963 | 1002 | |
|---|
| 964 | 1003 | v = kzalloc(sizeof(struct dm_verity), GFP_KERNEL); |
|---|
| 965 | 1004 | if (!v) { |
|---|
| .. | .. |
|---|
| 1093 | 1132 | r = -EINVAL; |
|---|
| 1094 | 1133 | goto bad; |
|---|
| 1095 | 1134 | } |
|---|
| 1135 | + root_hash_digest_to_validate = argv[8]; |
|---|
| 1096 | 1136 | |
|---|
| 1097 | 1137 | if (strcmp(argv[9], "-")) { |
|---|
| 1098 | 1138 | v->salt_size = strlen(argv[9]) / 2; |
|---|
| .. | .. |
|---|
| 1118 | 1158 | as.argc = argc; |
|---|
| 1119 | 1159 | as.argv = argv; |
|---|
| 1120 | 1160 | |
|---|
| 1121 | | - r = verity_parse_opt_args(&as, v); |
|---|
| 1161 | + r = verity_parse_opt_args(&as, v, &verify_args); |
|---|
| 1122 | 1162 | if (r < 0) |
|---|
| 1123 | 1163 | goto bad; |
|---|
| 1124 | 1164 | } |
|---|
| 1125 | 1165 | |
|---|
| 1126 | | -#ifdef CONFIG_DM_ANDROID_VERITY_AT_MOST_ONCE_DEFAULT_ENABLED |
|---|
| 1127 | | - if (!v->validated_blocks) { |
|---|
| 1128 | | - r = verity_alloc_most_once(v); |
|---|
| 1129 | | - if (r) |
|---|
| 1130 | | - goto bad; |
|---|
| 1166 | + /* Root hash signature is a optional parameter*/ |
|---|
| 1167 | + r = verity_verify_root_hash(root_hash_digest_to_validate, |
|---|
| 1168 | + strlen(root_hash_digest_to_validate), |
|---|
| 1169 | + verify_args.sig, |
|---|
| 1170 | + verify_args.sig_size); |
|---|
| 1171 | + if (r < 0) { |
|---|
| 1172 | + ti->error = "Root hash verification failed"; |
|---|
| 1173 | + goto bad; |
|---|
| 1131 | 1174 | } |
|---|
| 1132 | | -#endif |
|---|
| 1133 | | - |
|---|
| 1134 | 1175 | v->hash_per_block_bits = |
|---|
| 1135 | 1176 | __fls((1 << v->hash_dev_block_bits) / v->digest_size); |
|---|
| 1136 | 1177 | |
|---|
| .. | .. |
|---|
| 1196 | 1237 | ti->per_io_data_size = roundup(ti->per_io_data_size, |
|---|
| 1197 | 1238 | __alignof__(struct dm_verity_io)); |
|---|
| 1198 | 1239 | |
|---|
| 1240 | + verity_verify_sig_opts_cleanup(&verify_args); |
|---|
| 1241 | + |
|---|
| 1199 | 1242 | return 0; |
|---|
| 1200 | 1243 | |
|---|
| 1201 | 1244 | bad: |
|---|
| 1245 | + |
|---|
| 1246 | + verity_verify_sig_opts_cleanup(&verify_args); |
|---|
| 1202 | 1247 | verity_dtr(ti); |
|---|
| 1203 | 1248 | |
|---|
| 1204 | 1249 | return r; |
|---|
| .. | .. |
|---|
| 1206 | 1251 | |
|---|
| 1207 | 1252 | static struct target_type verity_target = { |
|---|
| 1208 | 1253 | .name = "verity", |
|---|
| 1209 | | - .version = {1, 4, 0}, |
|---|
| 1254 | + .features = DM_TARGET_IMMUTABLE, |
|---|
| 1255 | + .version = {1, 7, 0}, |
|---|
| 1256 | + .features = DM_TARGET_IMMUTABLE, |
|---|
| 1210 | 1257 | .module = THIS_MODULE, |
|---|
| 1211 | 1258 | .ctr = verity_ctr, |
|---|
| 1212 | 1259 | .dtr = verity_dtr, |
|---|