1 /* 2 * Copyright (C) 2016 The Android Open Source Project 3 * 4 * Permission is hereby granted, free of charge, to any person 5 * obtaining a copy of this software and associated documentation 6 * files (the "Software"), to deal in the Software without 7 * restriction, including without limitation the rights to use, copy, 8 * modify, merge, publish, distribute, sublicense, and/or sell copies 9 * of the Software, and to permit persons to whom the Software is 10 * furnished to do so, subject to the following conditions: 11 * 12 * The above copyright notice and this permission notice shall be 13 * included in all copies or substantial portions of the Software. 14 * 15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 18 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 19 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 20 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 21 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 * SOFTWARE. 23 */ 24 25 #include <android_avb/avb_cmdline.h> 26 #include <android_avb/avb_sha.h> 27 #include <android_avb/avb_util.h> 28 #include <android_avb/avb_version.h> 29 30 #define NUM_GUIDS 3 31 32 /* Substitutes all variables (e.g. $(ANDROID_SYSTEM_PARTUUID)) with 33 * values. Returns NULL on OOM, otherwise the cmdline with values 34 * replaced. 35 */ 36 char* avb_sub_cmdline(AvbOps* ops, 37 const char* cmdline, 38 const char* ab_suffix, 39 bool using_boot_for_vbmeta, 40 const AvbCmdlineSubstList* additional_substitutions) { 41 const char* part_name_str[NUM_GUIDS] = {"system", "boot", "vbmeta"}; 42 const char* replace_str[NUM_GUIDS] = {"$(ANDROID_SYSTEM_PARTUUID)", 43 "$(ANDROID_BOOT_PARTUUID)", 44 "$(ANDROID_VBMETA_PARTUUID)"}; 45 char* ret = NULL; 46 AvbIOResult io_ret; 47 size_t n; 48 49 /* Special-case for when the top-level vbmeta struct is in the boot 50 * partition. 51 */ 52 if (using_boot_for_vbmeta) { 53 part_name_str[2] = "boot"; 54 } 55 56 /* Replace unique partition GUIDs */ 57 for (n = 0; n < NUM_GUIDS; n++) { 58 char part_name[AVB_PART_NAME_MAX_SIZE]; 59 char guid_buf[37]; 60 61 /* Don't attempt to query the partition guid unless its search string is 62 * present in the command line. Note: the original cmdline is used here, 63 * not the replaced one. See b/116010959. 64 */ 65 if (avb_strstr(cmdline, replace_str[n]) == NULL) { 66 continue; 67 } 68 69 if (!avb_str_concat(part_name, 70 sizeof part_name, 71 part_name_str[n], 72 avb_strlen(part_name_str[n]), 73 ab_suffix, 74 avb_strlen(ab_suffix))) { 75 avb_error("Partition name and suffix does not fit.\n"); 76 goto fail; 77 } 78 79 io_ret = ops->get_unique_guid_for_partition( 80 ops, part_name, guid_buf, sizeof guid_buf); 81 if (io_ret == AVB_IO_RESULT_ERROR_OOM) { 82 goto fail; 83 } else if (io_ret != AVB_IO_RESULT_OK) { 84 avb_error("Error getting unique GUID for partition.\n"); 85 goto fail; 86 } 87 88 if (ret == NULL) { 89 ret = avb_replace(cmdline, replace_str[n], guid_buf); 90 } else { 91 char* new_ret = avb_replace(ret, replace_str[n], guid_buf); 92 avb_free(ret); 93 ret = new_ret; 94 } 95 if (ret == NULL) { 96 goto fail; 97 } 98 } 99 100 /* It's possible there is no _PARTUUID for replacement above. 101 * Duplicate cmdline to ret for additional substitutions below. 102 */ 103 if (ret == NULL) { 104 ret = avb_strdup(cmdline); 105 if (ret == NULL) { 106 goto fail; 107 } 108 } 109 110 /* Replace any additional substitutions. */ 111 if (additional_substitutions != NULL) { 112 for (n = 0; n < additional_substitutions->size; ++n) { 113 char* new_ret = avb_replace(ret, 114 additional_substitutions->tokens[n], 115 additional_substitutions->values[n]); 116 avb_free(ret); 117 ret = new_ret; 118 if (ret == NULL) { 119 goto fail; 120 } 121 } 122 } 123 124 return ret; 125 126 fail: 127 if (ret != NULL) { 128 avb_free(ret); 129 } 130 return NULL; 131 } 132 133 static int cmdline_append_option(AvbSlotVerifyData* slot_data, 134 const char* key, 135 const char* value) { 136 size_t offset, key_len, value_len; 137 char* new_cmdline; 138 139 key_len = avb_strlen(key); 140 value_len = avb_strlen(value); 141 142 offset = 0; 143 if (slot_data->cmdline != NULL) { 144 offset = avb_strlen(slot_data->cmdline); 145 if (offset > 0) { 146 offset += 1; 147 } 148 } 149 150 new_cmdline = avb_calloc(offset + key_len + value_len + 2); 151 if (new_cmdline == NULL) { 152 return 0; 153 } 154 if (offset > 0) { 155 avb_memcpy(new_cmdline, slot_data->cmdline, offset - 1); 156 new_cmdline[offset - 1] = ' '; 157 } 158 avb_memcpy(new_cmdline + offset, key, key_len); 159 new_cmdline[offset + key_len] = '='; 160 avb_memcpy(new_cmdline + offset + key_len + 1, value, value_len); 161 if (slot_data->cmdline != NULL) { 162 avb_free(slot_data->cmdline); 163 } 164 slot_data->cmdline = new_cmdline; 165 166 return 1; 167 } 168 169 #define AVB_MAX_DIGITS_UINT64 32 170 171 /* Writes |value| to |digits| in base 10 followed by a NUL byte. 172 * Returns number of characters written excluding the NUL byte. 173 */ 174 static size_t uint64_to_base10(uint64_t value, 175 char digits[AVB_MAX_DIGITS_UINT64]) { 176 char rev_digits[AVB_MAX_DIGITS_UINT64]; 177 size_t n, num_digits; 178 179 for (num_digits = 0; num_digits < AVB_MAX_DIGITS_UINT64 - 1;) { 180 rev_digits[num_digits++] = avb_div_by_10(&value) + '0'; 181 if (value == 0) { 182 break; 183 } 184 } 185 186 for (n = 0; n < num_digits; n++) { 187 digits[n] = rev_digits[num_digits - 1 - n]; 188 } 189 digits[n] = '\0'; 190 return n; 191 } 192 193 static int cmdline_append_version(AvbSlotVerifyData* slot_data, 194 const char* key, 195 uint64_t major_version, 196 uint64_t minor_version) { 197 char major_digits[AVB_MAX_DIGITS_UINT64]; 198 char minor_digits[AVB_MAX_DIGITS_UINT64]; 199 char combined[AVB_MAX_DIGITS_UINT64 * 2 + 1]; 200 size_t num_major_digits, num_minor_digits; 201 202 num_major_digits = uint64_to_base10(major_version, major_digits); 203 num_minor_digits = uint64_to_base10(minor_version, minor_digits); 204 avb_memcpy(combined, major_digits, num_major_digits); 205 combined[num_major_digits] = '.'; 206 avb_memcpy(combined + num_major_digits + 1, minor_digits, num_minor_digits); 207 combined[num_major_digits + 1 + num_minor_digits] = '\0'; 208 209 return cmdline_append_option(slot_data, key, combined); 210 } 211 212 static int cmdline_append_uint64_base10(AvbSlotVerifyData* slot_data, 213 const char* key, 214 uint64_t value) { 215 char digits[AVB_MAX_DIGITS_UINT64]; 216 uint64_to_base10(value, digits); 217 return cmdline_append_option(slot_data, key, digits); 218 } 219 220 static int cmdline_append_hex(AvbSlotVerifyData* slot_data, 221 const char* key, 222 const uint8_t* data, 223 size_t data_len) { 224 int ret; 225 char* hex_data = avb_bin2hex(data, data_len); 226 if (hex_data == NULL) { 227 return 0; 228 } 229 ret = cmdline_append_option(slot_data, key, hex_data); 230 avb_free(hex_data); 231 return ret; 232 } 233 234 AvbSlotVerifyResult avb_append_options( 235 AvbOps* ops, 236 AvbSlotVerifyFlags flags, 237 AvbSlotVerifyData* slot_data, 238 AvbVBMetaImageHeader* toplevel_vbmeta, 239 AvbAlgorithmType algorithm_type, 240 AvbHashtreeErrorMode hashtree_error_mode, 241 AvbHashtreeErrorMode resolved_hashtree_error_mode) { 242 AvbSlotVerifyResult ret; 243 const char* verity_mode = NULL; 244 bool is_device_unlocked; 245 AvbIOResult io_ret; 246 247 /* Add androidboot.vbmeta.device option... except if not using a vbmeta 248 * partition since it doesn't make sense in that case. 249 */ 250 if (!(flags & AVB_SLOT_VERIFY_FLAGS_NO_VBMETA_PARTITION)) { 251 if (!cmdline_append_option(slot_data, 252 "androidboot.vbmeta.device", 253 "PARTUUID=$(ANDROID_VBMETA_PARTUUID)")) { 254 ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM; 255 goto out; 256 } 257 } 258 259 /* Add androidboot.vbmeta.avb_version option. */ 260 if (!cmdline_append_version(slot_data, 261 "androidboot.vbmeta.avb_version", 262 AVB_VERSION_MAJOR, 263 AVB_VERSION_MINOR)) { 264 ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM; 265 goto out; 266 } 267 268 /* Set androidboot.avb.device_state to "locked" or "unlocked". */ 269 io_ret = ops->read_is_device_unlocked(ops, &is_device_unlocked); 270 if (io_ret == AVB_IO_RESULT_ERROR_OOM) { 271 ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM; 272 goto out; 273 } else if (io_ret != AVB_IO_RESULT_OK) { 274 avb_error("Error getting device state.\n"); 275 ret = AVB_SLOT_VERIFY_RESULT_ERROR_IO; 276 goto out; 277 } 278 if (!cmdline_append_option(slot_data, 279 "androidboot.vbmeta.device_state", 280 is_device_unlocked ? "unlocked" : "locked")) { 281 ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM; 282 goto out; 283 } 284 285 /* Set androidboot.vbmeta.{hash_alg, size, digest} - use same hash 286 * function as is used to sign vbmeta. 287 */ 288 switch (algorithm_type) { 289 /* Explicit fallthrough. */ 290 case AVB_ALGORITHM_TYPE_NONE: 291 case AVB_ALGORITHM_TYPE_SHA256_RSA2048: 292 case AVB_ALGORITHM_TYPE_SHA256_RSA4096: 293 case AVB_ALGORITHM_TYPE_SHA256_RSA8192: { 294 size_t n, total_size = 0; 295 uint8_t vbmeta_digest[AVB_SHA256_DIGEST_SIZE]; 296 avb_slot_verify_data_calculate_vbmeta_digest( 297 slot_data, AVB_DIGEST_TYPE_SHA256, vbmeta_digest); 298 for (n = 0; n < slot_data->num_vbmeta_images; n++) { 299 total_size += slot_data->vbmeta_images[n].vbmeta_size; 300 } 301 if (!cmdline_append_option( 302 slot_data, "androidboot.vbmeta.hash_alg", "sha256") || 303 !cmdline_append_uint64_base10( 304 slot_data, "androidboot.vbmeta.size", total_size) || 305 !cmdline_append_hex(slot_data, 306 "androidboot.vbmeta.digest", 307 vbmeta_digest, 308 AVB_SHA256_DIGEST_SIZE)) { 309 ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM; 310 goto out; 311 } 312 } break; 313 /* Explicit fallthrough. */ 314 case AVB_ALGORITHM_TYPE_SHA512_RSA2048: 315 case AVB_ALGORITHM_TYPE_SHA512_RSA4096: 316 case AVB_ALGORITHM_TYPE_SHA512_RSA8192: { 317 size_t n, total_size = 0; 318 uint8_t vbmeta_digest[AVB_SHA512_DIGEST_SIZE]; 319 avb_slot_verify_data_calculate_vbmeta_digest( 320 slot_data, AVB_DIGEST_TYPE_SHA512, vbmeta_digest); 321 for (n = 0; n < slot_data->num_vbmeta_images; n++) { 322 total_size += slot_data->vbmeta_images[n].vbmeta_size; 323 } 324 if (!cmdline_append_option( 325 slot_data, "androidboot.vbmeta.hash_alg", "sha512") || 326 !cmdline_append_uint64_base10( 327 slot_data, "androidboot.vbmeta.size", total_size) || 328 !cmdline_append_hex(slot_data, 329 "androidboot.vbmeta.digest", 330 vbmeta_digest, 331 AVB_SHA512_DIGEST_SIZE)) { 332 ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM; 333 goto out; 334 } 335 } break; 336 case _AVB_ALGORITHM_NUM_TYPES: 337 avb_assert_not_reached(); 338 break; 339 } 340 341 /* Set androidboot.veritymode and androidboot.vbmeta.invalidate_on_error */ 342 if (toplevel_vbmeta->flags & AVB_VBMETA_IMAGE_FLAGS_HASHTREE_DISABLED) { 343 verity_mode = "disabled"; 344 } else { 345 const char* dm_verity_mode = NULL; 346 char* new_ret; 347 348 switch (resolved_hashtree_error_mode) { 349 case AVB_HASHTREE_ERROR_MODE_RESTART_AND_INVALIDATE: 350 if (!cmdline_append_option( 351 slot_data, "androidboot.vbmeta.invalidate_on_error", "yes")) { 352 ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM; 353 goto out; 354 } 355 verity_mode = "enforcing"; 356 dm_verity_mode = "restart_on_corruption"; 357 break; 358 case AVB_HASHTREE_ERROR_MODE_RESTART: 359 verity_mode = "enforcing"; 360 dm_verity_mode = "restart_on_corruption"; 361 break; 362 case AVB_HASHTREE_ERROR_MODE_EIO: 363 verity_mode = "eio"; 364 /* For now there's no option to specify the EIO mode. So 365 * just use 'ignore_zero_blocks' since that's already set 366 * and dm-verity-target.c supports specifying this multiple 367 * times. 368 */ 369 dm_verity_mode = "ignore_zero_blocks"; 370 break; 371 case AVB_HASHTREE_ERROR_MODE_LOGGING: 372 verity_mode = "logging"; 373 dm_verity_mode = "ignore_corruption"; 374 break; 375 case AVB_HASHTREE_ERROR_MODE_MANAGED_RESTART_AND_EIO: 376 // Should never get here because MANAGED_RESTART_AND_EIO is 377 // remapped by avb_manage_hashtree_error_mode(). 378 avb_assert_not_reached(); 379 break; 380 } 381 new_ret = avb_replace( 382 slot_data->cmdline, "$(ANDROID_VERITY_MODE)", dm_verity_mode); 383 avb_free(slot_data->cmdline); 384 slot_data->cmdline = new_ret; 385 if (slot_data->cmdline == NULL) { 386 ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM; 387 goto out; 388 } 389 } 390 if (!cmdline_append_option( 391 slot_data, "androidboot.veritymode", verity_mode)) { 392 ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM; 393 goto out; 394 } 395 if (hashtree_error_mode == AVB_HASHTREE_ERROR_MODE_MANAGED_RESTART_AND_EIO) { 396 if (!cmdline_append_option( 397 slot_data, "androidboot.veritymode.managed", "yes")) { 398 ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM; 399 goto out; 400 } 401 } 402 403 ret = AVB_SLOT_VERIFY_RESULT_OK; 404 405 out: 406 407 return ret; 408 } 409 410 AvbCmdlineSubstList* avb_new_cmdline_subst_list() { 411 return (AvbCmdlineSubstList*)avb_calloc(sizeof(AvbCmdlineSubstList)); 412 } 413 414 void avb_free_cmdline_subst_list(AvbCmdlineSubstList* cmdline_subst) { 415 size_t i; 416 for (i = 0; i < cmdline_subst->size; ++i) { 417 avb_free(cmdline_subst->tokens[i]); 418 avb_free(cmdline_subst->values[i]); 419 } 420 cmdline_subst->size = 0; 421 avb_free(cmdline_subst); 422 } 423 424 AvbSlotVerifyResult avb_add_root_digest_substitution( 425 const char* part_name, 426 const uint8_t* digest, 427 size_t digest_size, 428 AvbCmdlineSubstList* out_cmdline_subst) { 429 const char* kDigestSubPrefix = "$(AVB_"; 430 const char* kDigestSubSuffix = "_ROOT_DIGEST)"; 431 size_t part_name_len = avb_strlen(part_name); 432 size_t list_index = out_cmdline_subst->size; 433 434 avb_assert(part_name_len < AVB_PART_NAME_MAX_SIZE); 435 avb_assert(digest_size <= AVB_SHA512_DIGEST_SIZE); 436 if (part_name_len >= AVB_PART_NAME_MAX_SIZE || 437 digest_size > AVB_SHA512_DIGEST_SIZE) { 438 return AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA; 439 } 440 441 if (out_cmdline_subst->size >= AVB_MAX_NUM_CMDLINE_SUBST) { 442 /* The list is full. Currently dynamic growth of this list is not supported. 443 */ 444 return AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA; 445 } 446 447 /* Construct the token to replace in the command line based on the partition 448 * name. For partition 'foo', this will be '$(AVB_FOO_ROOT_DIGEST)'. 449 */ 450 out_cmdline_subst->tokens[list_index] = 451 avb_strdupv(kDigestSubPrefix, part_name, kDigestSubSuffix, NULL); 452 if (out_cmdline_subst->tokens[list_index] == NULL) { 453 goto fail; 454 } 455 avb_uppercase(out_cmdline_subst->tokens[list_index]); 456 457 /* The digest value is hex encoded when inserted in the command line. */ 458 out_cmdline_subst->values[list_index] = avb_bin2hex(digest, digest_size); 459 if (out_cmdline_subst->values[list_index] == NULL) { 460 goto fail; 461 } 462 463 out_cmdline_subst->size++; 464 return AVB_SLOT_VERIFY_RESULT_OK; 465 466 fail: 467 if (out_cmdline_subst->tokens[list_index]) { 468 avb_free(out_cmdline_subst->tokens[list_index]); 469 } 470 if (out_cmdline_subst->values[list_index]) { 471 avb_free(out_cmdline_subst->values[list_index]); 472 } 473 return AVB_SLOT_VERIFY_RESULT_ERROR_OOM; 474 } 475