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_slot_verify.h> 26 #include <android_avb/avb_chain_partition_descriptor.h> 27 #include <android_avb/avb_cmdline.h> 28 #include <android_avb/avb_footer.h> 29 #include <android_avb/avb_hash_descriptor.h> 30 #include <android_avb/avb_kernel_cmdline_descriptor.h> 31 #include <android_avb/avb_sha.h> 32 #include <android_avb/avb_util.h> 33 #include <android_avb/avb_vbmeta_image.h> 34 #include <android_avb/avb_version.h> 35 36 /* Maximum number of partitions that can be loaded with avb_slot_verify(). */ 37 #define MAX_NUMBER_OF_LOADED_PARTITIONS 32 38 39 /* Maximum number of vbmeta images that can be loaded with avb_slot_verify(). */ 40 #define MAX_NUMBER_OF_VBMETA_IMAGES 32 41 42 /* Maximum size of a vbmeta image - 64 KiB. */ 43 #define VBMETA_MAX_SIZE (64 * 1024) 44 45 /* Helper function to see if we should continue with verification in 46 * allow_verification_error=true mode if something goes wrong. See the 47 * comments for the avb_slot_verify() function for more information. 48 */ 49 static inline bool result_should_continue(AvbSlotVerifyResult result) { 50 switch (result) { 51 case AVB_SLOT_VERIFY_RESULT_ERROR_OOM: 52 case AVB_SLOT_VERIFY_RESULT_ERROR_IO: 53 case AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA: 54 case AVB_SLOT_VERIFY_RESULT_ERROR_UNSUPPORTED_VERSION: 55 case AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_ARGUMENT: 56 return false; 57 58 case AVB_SLOT_VERIFY_RESULT_OK: 59 case AVB_SLOT_VERIFY_RESULT_ERROR_VERIFICATION: 60 case AVB_SLOT_VERIFY_RESULT_ERROR_ROLLBACK_INDEX: 61 case AVB_SLOT_VERIFY_RESULT_ERROR_PUBLIC_KEY_REJECTED: 62 return true; 63 } 64 65 return false; 66 } 67 68 static AvbSlotVerifyResult load_full_partition( 69 AvbOps* ops, const char* part_name, 70 uint64_t image_size, uint8_t** out_image_buf, 71 bool* out_image_preloaded) { 72 size_t part_num_read; 73 AvbIOResult io_ret; 74 75 /* Make sure that we do not overwrite existing data. */ 76 avb_assert(*out_image_buf == NULL); 77 avb_assert(!*out_image_preloaded); 78 79 /* We are going to implicitly cast image_size from uint64_t to size_t in the 80 * following code, so we need to make sure that the cast is safe. */ 81 if (image_size != (size_t)(image_size)) { 82 avb_errorv(part_name, ": Partition size too large to load.\n", NULL); 83 return AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA; 84 } 85 86 /* Try use a preloaded one. */ 87 if (ops->get_preloaded_partition != NULL) { 88 io_ret = ops->get_preloaded_partition( 89 ops, part_name, image_size, out_image_buf, &part_num_read); 90 if (io_ret == AVB_IO_RESULT_ERROR_OOM) { 91 return AVB_SLOT_VERIFY_RESULT_ERROR_OOM; 92 } else if (io_ret != AVB_IO_RESULT_OK) { 93 avb_errorv(part_name, ": Error loading data from partition.\n", NULL); 94 return AVB_SLOT_VERIFY_RESULT_ERROR_IO; 95 } 96 97 if (*out_image_buf != NULL) { 98 if (part_num_read != image_size) { 99 avb_errorv(part_name, ": Read incorrect number of bytes.\n", NULL); 100 return AVB_SLOT_VERIFY_RESULT_ERROR_IO; 101 } 102 *out_image_preloaded = true; 103 } 104 } 105 106 /* Allocate and copy the partition. */ 107 if (!*out_image_preloaded) { 108 *out_image_buf = avb_malloc(image_size); 109 if (*out_image_buf == NULL) { 110 return AVB_SLOT_VERIFY_RESULT_ERROR_OOM; 111 } 112 113 io_ret = ops->read_from_partition( 114 ops, part_name, 0 /* offset */, image_size, *out_image_buf, 115 &part_num_read); 116 if (io_ret == AVB_IO_RESULT_ERROR_OOM) { 117 return AVB_SLOT_VERIFY_RESULT_ERROR_OOM; 118 } else if (io_ret != AVB_IO_RESULT_OK) { 119 avb_errorv(part_name, ": Error loading data from partition.\n", NULL); 120 return AVB_SLOT_VERIFY_RESULT_ERROR_IO; 121 } 122 if (part_num_read != image_size) { 123 avb_errorv(part_name, ": Read incorrect number of bytes.\n", NULL); 124 return AVB_SLOT_VERIFY_RESULT_ERROR_IO; 125 } 126 } 127 128 return AVB_SLOT_VERIFY_RESULT_OK; 129 } 130 131 static AvbSlotVerifyResult load_and_verify_hash_partition( 132 AvbOps* ops, 133 const char* const* requested_partitions, 134 const char* ab_suffix, 135 bool allow_verification_error, 136 const AvbDescriptor* descriptor, 137 AvbSlotVerifyData* slot_data) { 138 AvbHashDescriptor hash_desc; 139 const uint8_t* desc_partition_name = NULL; 140 const uint8_t* desc_salt; 141 const uint8_t* desc_digest; 142 char part_name[AVB_PART_NAME_MAX_SIZE]; 143 AvbSlotVerifyResult ret; 144 AvbIOResult io_ret; 145 uint8_t* image_buf = NULL; 146 bool image_preloaded = false; 147 uint8_t* digest; 148 size_t digest_len; 149 const char* found; 150 uint64_t image_size = 0; 151 152 if (!avb_hash_descriptor_validate_and_byteswap( 153 (const AvbHashDescriptor*)descriptor, &hash_desc)) { 154 ret = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA; 155 goto out; 156 } 157 158 desc_partition_name = 159 ((const uint8_t*)descriptor) + sizeof(AvbHashDescriptor); 160 desc_salt = desc_partition_name + hash_desc.partition_name_len; 161 desc_digest = desc_salt + hash_desc.salt_len; 162 163 if (!avb_validate_utf8(desc_partition_name, hash_desc.partition_name_len)) { 164 avb_error("Partition name is not valid UTF-8.\n"); 165 ret = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA; 166 goto out; 167 } 168 169 /* Don't bother loading or validating unless the partition was 170 * requested in the first place. 171 */ 172 found = avb_strv_find_str(requested_partitions, 173 (const char*)desc_partition_name, 174 hash_desc.partition_name_len); 175 if (found == NULL) { 176 ret = AVB_SLOT_VERIFY_RESULT_OK; 177 goto out; 178 } 179 180 if (!avb_str_concat(part_name, 181 sizeof part_name, 182 (const char*)desc_partition_name, 183 hash_desc.partition_name_len, 184 ab_suffix, 185 avb_strlen(ab_suffix))) { 186 avb_error("Partition name and suffix does not fit.\n"); 187 ret = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA; 188 goto out; 189 } 190 191 /* If we're allowing verification errors then hash_desc.image_size 192 * may no longer match what's in the partition... so in this case 193 * just load the entire partition. 194 * 195 * For example, this can happen if a developer does 'fastboot flash 196 * boot /path/to/new/and/bigger/boot.img'. We want this to work 197 * since it's such a common workflow. 198 */ 199 image_size = hash_desc.image_size; 200 if (0) { 201 if (ops->get_size_of_partition == NULL) { 202 avb_errorv(part_name, 203 ": The get_size_of_partition() operation is " 204 "not implemented so we may not load the entire partition. " 205 "Please implement.", 206 NULL); 207 } else { 208 io_ret = ops->get_size_of_partition(ops, part_name, &image_size); 209 if (io_ret == AVB_IO_RESULT_ERROR_OOM) { 210 ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM; 211 goto out; 212 } else if (io_ret != AVB_IO_RESULT_OK) { 213 avb_errorv(part_name, ": Error determining partition size.\n", NULL); 214 ret = AVB_SLOT_VERIFY_RESULT_ERROR_IO; 215 goto out; 216 } 217 avb_debugv(part_name, ": Loading entire partition.\n", NULL); 218 } 219 } 220 221 ret = load_full_partition( 222 ops, part_name, image_size, &image_buf, &image_preloaded); 223 if (ret != AVB_SLOT_VERIFY_RESULT_OK) { 224 goto out; 225 } 226 227 if (avb_strcmp((const char*)hash_desc.hash_algorithm, "sha256") == 0) { 228 AvbSHA256Ctx sha256_ctx; 229 avb_sha256_init(&sha256_ctx); 230 avb_sha256_update(&sha256_ctx, desc_salt, hash_desc.salt_len); 231 avb_sha256_update(&sha256_ctx, image_buf, hash_desc.image_size); 232 digest = avb_sha256_final(&sha256_ctx); 233 digest_len = AVB_SHA256_DIGEST_SIZE; 234 } else if (avb_strcmp((const char*)hash_desc.hash_algorithm, "sha512") == 0) { 235 AvbSHA512Ctx sha512_ctx; 236 avb_sha512_init(&sha512_ctx); 237 avb_sha512_update(&sha512_ctx, desc_salt, hash_desc.salt_len); 238 avb_sha512_update(&sha512_ctx, image_buf, hash_desc.image_size); 239 digest = avb_sha512_final(&sha512_ctx); 240 digest_len = AVB_SHA512_DIGEST_SIZE; 241 } else { 242 avb_errorv(part_name, ": Unsupported hash algorithm.\n", NULL); 243 ret = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA; 244 goto out; 245 } 246 247 if (digest_len != hash_desc.digest_len) { 248 avb_errorv( 249 part_name, ": Digest in descriptor not of expected size.\n", NULL); 250 ret = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA; 251 goto out; 252 } 253 254 if (avb_safe_memcmp(digest, desc_digest, digest_len) != 0) { 255 avb_errorv(part_name, 256 ": Hash of data does not match digest in descriptor.\n", 257 NULL); 258 ret = AVB_SLOT_VERIFY_RESULT_ERROR_VERIFICATION; 259 goto out; 260 } 261 262 ret = AVB_SLOT_VERIFY_RESULT_OK; 263 264 out: 265 266 /* If it worked and something was loaded, copy to slot_data. */ 267 if ((ret == AVB_SLOT_VERIFY_RESULT_OK || result_should_continue(ret)) && 268 image_buf != NULL) { 269 AvbPartitionData* loaded_partition; 270 if (slot_data->num_loaded_partitions == MAX_NUMBER_OF_LOADED_PARTITIONS) { 271 avb_errorv(part_name, ": Too many loaded partitions.\n", NULL); 272 ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM; 273 goto fail; 274 } 275 loaded_partition = 276 &slot_data->loaded_partitions[slot_data->num_loaded_partitions++]; 277 loaded_partition->partition_name = avb_strdup(found); 278 loaded_partition->data_size = image_size; 279 loaded_partition->data = image_buf; 280 loaded_partition->preloaded = image_preloaded; 281 image_buf = NULL; 282 } 283 284 fail: 285 if (image_buf != NULL && !image_preloaded) { 286 avb_free(image_buf); 287 } 288 return ret; 289 } 290 291 static AvbSlotVerifyResult load_requested_partitions( 292 AvbOps* ops, 293 const char* const* requested_partitions, 294 const char* ab_suffix, 295 AvbSlotVerifyData* slot_data) { 296 AvbSlotVerifyResult ret; 297 uint8_t* image_buf = NULL; 298 bool image_preloaded = false; 299 size_t n; 300 301 if (ops->get_size_of_partition == NULL) { 302 avb_error("get_size_of_partition() not implemented.\n"); 303 ret = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_ARGUMENT; 304 goto out; 305 } 306 307 for (n = 0; requested_partitions[n] != NULL; n++) { 308 char part_name[AVB_PART_NAME_MAX_SIZE]; 309 AvbIOResult io_ret; 310 uint64_t image_size; 311 AvbPartitionData* loaded_partition; 312 313 if (!avb_str_concat(part_name, 314 sizeof part_name, 315 requested_partitions[n], 316 avb_strlen(requested_partitions[n]), 317 ab_suffix, 318 avb_strlen(ab_suffix))) { 319 avb_error("Partition name and suffix does not fit.\n"); 320 ret = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA; 321 goto out; 322 } 323 324 io_ret = ops->get_size_of_partition(ops, part_name, &image_size); 325 if (io_ret == AVB_IO_RESULT_ERROR_OOM) { 326 ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM; 327 goto out; 328 } else if (io_ret != AVB_IO_RESULT_OK) { 329 avb_errorv(part_name, ": Error determining partition size.\n", NULL); 330 ret = AVB_SLOT_VERIFY_RESULT_ERROR_IO; 331 goto out; 332 } 333 avb_debugv(part_name, ": Loading entire partition.\n", NULL); 334 335 ret = load_full_partition( 336 ops, part_name, image_size, &image_buf, &image_preloaded); 337 if (ret != AVB_SLOT_VERIFY_RESULT_OK) { 338 goto out; 339 } 340 341 /* Move to slot_data. */ 342 if (slot_data->num_loaded_partitions == MAX_NUMBER_OF_LOADED_PARTITIONS) { 343 avb_errorv(part_name, ": Too many loaded partitions.\n", NULL); 344 ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM; 345 goto out; 346 } 347 loaded_partition = 348 &slot_data->loaded_partitions[slot_data->num_loaded_partitions++]; 349 loaded_partition->partition_name = avb_strdup(requested_partitions[n]); 350 if (loaded_partition->partition_name == NULL) { 351 ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM; 352 goto out; 353 } 354 loaded_partition->data_size = image_size; 355 loaded_partition->data = image_buf; /* Transferring the owner. */ 356 loaded_partition->preloaded = image_preloaded; 357 image_buf = NULL; 358 image_preloaded = false; 359 } 360 361 ret = AVB_SLOT_VERIFY_RESULT_OK; 362 363 out: 364 /* Free the current buffer if any. */ 365 if (image_buf != NULL && !image_preloaded) { 366 avb_free(image_buf); 367 } 368 /* Buffers that are already saved in slot_data will be handled by the caller 369 * even on failure. */ 370 return ret; 371 } 372 373 static AvbSlotVerifyResult load_and_verify_vbmeta( 374 AvbOps* ops, 375 const char* const* requested_partitions, 376 const char* ab_suffix, 377 bool allow_verification_error, 378 AvbVBMetaImageFlags toplevel_vbmeta_flags, 379 int rollback_index_location, 380 const char* partition_name, 381 size_t partition_name_len, 382 const uint8_t* expected_public_key, 383 size_t expected_public_key_length, 384 AvbSlotVerifyData* slot_data, 385 AvbAlgorithmType* out_algorithm_type) { 386 char full_partition_name[AVB_PART_NAME_MAX_SIZE]; 387 AvbSlotVerifyResult ret; 388 AvbIOResult io_ret; 389 size_t vbmeta_offset; 390 size_t vbmeta_size; 391 uint8_t* vbmeta_buf = NULL; 392 size_t vbmeta_num_read; 393 AvbVBMetaVerifyResult vbmeta_ret; 394 const uint8_t* pk_data; 395 size_t pk_len; 396 AvbVBMetaImageHeader vbmeta_header; 397 uint64_t stored_rollback_index; 398 const AvbDescriptor** descriptors = NULL; 399 size_t num_descriptors; 400 size_t n; 401 bool is_main_vbmeta; 402 bool is_vbmeta_partition; 403 AvbVBMetaData* vbmeta_image_data = NULL; 404 405 ret = AVB_SLOT_VERIFY_RESULT_OK; 406 407 avb_assert(slot_data != NULL); 408 409 /* Since we allow top-level vbmeta in 'boot', use 410 * rollback_index_location to determine whether we're the main 411 * vbmeta struct. 412 */ 413 is_main_vbmeta = (rollback_index_location == 0); 414 is_vbmeta_partition = (avb_strcmp(partition_name, "vbmeta") == 0); 415 416 if (!avb_validate_utf8((const uint8_t*)partition_name, partition_name_len)) { 417 avb_error("Partition name is not valid UTF-8.\n"); 418 ret = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA; 419 goto out; 420 } 421 422 /* Construct full partition name. */ 423 if (!avb_str_concat(full_partition_name, 424 sizeof full_partition_name, 425 partition_name, 426 partition_name_len, 427 ab_suffix, 428 avb_strlen(ab_suffix))) { 429 avb_error("Partition name and suffix does not fit.\n"); 430 ret = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA; 431 goto out; 432 } 433 434 avb_debugv("Loading vbmeta struct from partition '", 435 full_partition_name, 436 "'.\n", 437 NULL); 438 439 /* If we're loading from the main vbmeta partition, the vbmeta 440 * struct is in the beginning. Otherwise we have to locate it via a 441 * footer. 442 */ 443 if (is_vbmeta_partition) { 444 vbmeta_offset = 0; 445 vbmeta_size = VBMETA_MAX_SIZE; 446 } else { 447 uint8_t footer_buf[AVB_FOOTER_SIZE]; 448 size_t footer_num_read; 449 AvbFooter footer; 450 451 io_ret = ops->read_from_partition(ops, 452 full_partition_name, 453 -AVB_FOOTER_SIZE, 454 AVB_FOOTER_SIZE, 455 footer_buf, 456 &footer_num_read); 457 if (io_ret == AVB_IO_RESULT_ERROR_OOM) { 458 ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM; 459 goto out; 460 } else if (io_ret != AVB_IO_RESULT_OK) { 461 avb_errorv(full_partition_name, ": Error loading footer.\n", NULL); 462 ret = AVB_SLOT_VERIFY_RESULT_ERROR_IO; 463 goto out; 464 } 465 avb_assert(footer_num_read == AVB_FOOTER_SIZE); 466 467 if (!avb_footer_validate_and_byteswap((const AvbFooter*)footer_buf, 468 &footer)) { 469 avb_errorv(full_partition_name, ": Error validating footer.\n", NULL); 470 ret = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA; 471 goto out; 472 } 473 474 /* Basic footer sanity check since the data is untrusted. */ 475 if (footer.vbmeta_size > VBMETA_MAX_SIZE) { 476 avb_errorv( 477 full_partition_name, ": Invalid vbmeta size in footer.\n", NULL); 478 ret = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA; 479 goto out; 480 } 481 482 vbmeta_offset = footer.vbmeta_offset; 483 vbmeta_size = footer.vbmeta_size; 484 } 485 486 vbmeta_buf = avb_malloc(vbmeta_size); 487 if (vbmeta_buf == NULL) { 488 ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM; 489 goto out; 490 } 491 492 io_ret = ops->read_from_partition(ops, 493 full_partition_name, 494 vbmeta_offset, 495 vbmeta_size, 496 vbmeta_buf, 497 &vbmeta_num_read); 498 if (io_ret == AVB_IO_RESULT_ERROR_OOM) { 499 ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM; 500 goto out; 501 } else if (io_ret != AVB_IO_RESULT_OK) { 502 /* If we're looking for 'vbmeta' but there is no such partition, 503 * go try to get it from the boot partition instead. 504 */ 505 if (is_main_vbmeta && io_ret == AVB_IO_RESULT_ERROR_NO_SUCH_PARTITION && 506 is_vbmeta_partition) { 507 avb_debugv(full_partition_name, 508 ": No such partition. Trying 'boot' instead.\n", 509 NULL); 510 ret = load_and_verify_vbmeta(ops, 511 requested_partitions, 512 ab_suffix, 513 allow_verification_error, 514 0 /* toplevel_vbmeta_flags */, 515 0 /* rollback_index_location */, 516 "boot", 517 avb_strlen("boot"), 518 NULL /* expected_public_key */, 519 0 /* expected_public_key_length */, 520 slot_data, 521 out_algorithm_type); 522 goto out; 523 } else { 524 avb_errorv(full_partition_name, ": Error loading vbmeta data.\n", NULL); 525 ret = AVB_SLOT_VERIFY_RESULT_ERROR_IO; 526 goto out; 527 } 528 } 529 avb_assert(vbmeta_num_read <= vbmeta_size); 530 531 /* Check if the image is properly signed and get the public key used 532 * to sign the image. 533 */ 534 vbmeta_ret = 535 avb_vbmeta_image_verify(vbmeta_buf, vbmeta_num_read, &pk_data, &pk_len); 536 switch (vbmeta_ret) { 537 case AVB_VBMETA_VERIFY_RESULT_OK: 538 avb_assert(pk_data != NULL && pk_len > 0); 539 break; 540 541 case AVB_VBMETA_VERIFY_RESULT_OK_NOT_SIGNED: 542 case AVB_VBMETA_VERIFY_RESULT_HASH_MISMATCH: 543 case AVB_VBMETA_VERIFY_RESULT_SIGNATURE_MISMATCH: 544 ret = AVB_SLOT_VERIFY_RESULT_ERROR_VERIFICATION; 545 avb_errorv(full_partition_name, 546 ": Error verifying vbmeta image: ", 547 avb_vbmeta_verify_result_to_string(vbmeta_ret), 548 "\n", 549 NULL); 550 if (!allow_verification_error) { 551 goto out; 552 } 553 break; 554 555 case AVB_VBMETA_VERIFY_RESULT_INVALID_VBMETA_HEADER: 556 /* No way to continue this case. */ 557 ret = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA; 558 avb_errorv(full_partition_name, 559 ": Error verifying vbmeta image: invalid vbmeta header\n", 560 NULL); 561 goto out; 562 563 case AVB_VBMETA_VERIFY_RESULT_UNSUPPORTED_VERSION: 564 /* No way to continue this case. */ 565 ret = AVB_SLOT_VERIFY_RESULT_ERROR_UNSUPPORTED_VERSION; 566 avb_errorv(full_partition_name, 567 ": Error verifying vbmeta image: unsupported AVB version\n", 568 NULL); 569 goto out; 570 } 571 572 /* Byteswap the header. */ 573 avb_vbmeta_image_header_to_host_byte_order((AvbVBMetaImageHeader*)vbmeta_buf, 574 &vbmeta_header); 575 576 /* If we're the toplevel, assign flags so they'll be passed down. */ 577 if (is_main_vbmeta) { 578 toplevel_vbmeta_flags = (AvbVBMetaImageFlags)vbmeta_header.flags; 579 } else { 580 if (vbmeta_header.flags != 0) { 581 ret = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA; 582 avb_errorv(full_partition_name, 583 ": chained vbmeta image has non-zero flags\n", 584 NULL); 585 goto out; 586 } 587 } 588 589 /* Check if key used to make signature matches what is expected. */ 590 if (pk_data != NULL) { 591 if (expected_public_key != NULL) { 592 avb_assert(!is_main_vbmeta); 593 if (expected_public_key_length != pk_len || 594 avb_safe_memcmp(expected_public_key, pk_data, pk_len) != 0) { 595 avb_errorv(full_partition_name, 596 ": Public key used to sign data does not match key in chain " 597 "partition descriptor.\n", 598 NULL); 599 ret = AVB_SLOT_VERIFY_RESULT_ERROR_PUBLIC_KEY_REJECTED; 600 if (!allow_verification_error) { 601 goto out; 602 } 603 } 604 } else { 605 bool key_is_trusted = false; 606 const uint8_t* pk_metadata = NULL; 607 size_t pk_metadata_len = 0; 608 609 if (vbmeta_header.public_key_metadata_size > 0) { 610 pk_metadata = vbmeta_buf + sizeof(AvbVBMetaImageHeader) + 611 vbmeta_header.authentication_data_block_size + 612 vbmeta_header.public_key_metadata_offset; 613 pk_metadata_len = vbmeta_header.public_key_metadata_size; 614 } 615 616 avb_assert(is_main_vbmeta); 617 io_ret = ops->validate_vbmeta_public_key( 618 ops, pk_data, pk_len, pk_metadata, pk_metadata_len, &key_is_trusted); 619 if (io_ret == AVB_IO_RESULT_ERROR_OOM) { 620 ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM; 621 goto out; 622 } else if (io_ret != AVB_IO_RESULT_OK) { 623 avb_errorv(full_partition_name, 624 ": Error while checking public key used to sign data.\n", 625 NULL); 626 ret = AVB_SLOT_VERIFY_RESULT_ERROR_IO; 627 goto out; 628 } 629 if (!key_is_trusted) { 630 avb_errorv(full_partition_name, 631 ": Public key used to sign data rejected.\n", 632 NULL); 633 ret = AVB_SLOT_VERIFY_RESULT_ERROR_PUBLIC_KEY_REJECTED; 634 if (!allow_verification_error) { 635 goto out; 636 } 637 } 638 } 639 } 640 641 /* Check rollback index. */ 642 io_ret = ops->read_rollback_index( 643 ops, rollback_index_location, &stored_rollback_index); 644 if (io_ret == AVB_IO_RESULT_ERROR_OOM) { 645 ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM; 646 goto out; 647 } else if (io_ret != AVB_IO_RESULT_OK) { 648 avb_errorv(full_partition_name, 649 ": Error getting rollback index for location.\n", 650 NULL); 651 ret = AVB_SLOT_VERIFY_RESULT_ERROR_IO; 652 goto out; 653 } 654 if (vbmeta_header.rollback_index < stored_rollback_index) { 655 avb_errorv( 656 full_partition_name, 657 ": Image rollback index is less than the stored rollback index.\n", 658 NULL); 659 ret = AVB_SLOT_VERIFY_RESULT_ERROR_ROLLBACK_INDEX; 660 if (!allow_verification_error) { 661 goto out; 662 } 663 } 664 665 /* Copy vbmeta to vbmeta_images before recursing. */ 666 if (is_main_vbmeta) { 667 avb_assert(slot_data->num_vbmeta_images == 0); 668 } else { 669 avb_assert(slot_data->num_vbmeta_images > 0); 670 } 671 if (slot_data->num_vbmeta_images == MAX_NUMBER_OF_VBMETA_IMAGES) { 672 avb_errorv(full_partition_name, ": Too many vbmeta images.\n", NULL); 673 ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM; 674 goto out; 675 } 676 vbmeta_image_data = &slot_data->vbmeta_images[slot_data->num_vbmeta_images++]; 677 vbmeta_image_data->partition_name = avb_strdup(partition_name); 678 vbmeta_image_data->vbmeta_data = vbmeta_buf; 679 /* Note that |vbmeta_buf| is actually |vbmeta_num_read| bytes long 680 * and this includes data past the end of the image. Pass the 681 * actual size of the vbmeta image. Also, no need to use 682 * avb_safe_add() since the header has already been verified. 683 */ 684 vbmeta_image_data->vbmeta_size = 685 sizeof(AvbVBMetaImageHeader) + 686 vbmeta_header.authentication_data_block_size + 687 vbmeta_header.auxiliary_data_block_size; 688 vbmeta_image_data->verify_result = vbmeta_ret; 689 690 /* If verification has been disabled by setting a bit in the image, 691 * we're done... except that we need to load the entirety of the 692 * requested partitions. 693 */ 694 if (vbmeta_header.flags & AVB_VBMETA_IMAGE_FLAGS_VERIFICATION_DISABLED) { 695 AvbSlotVerifyResult sub_ret; 696 avb_debugv( 697 full_partition_name, ": VERIFICATION_DISABLED bit is set.\n", NULL); 698 /* If load_requested_partitions() fail it is always a fatal 699 * failure (e.g. ERROR_INVALID_ARGUMENT, ERROR_OOM, etc.) rather 700 * than recoverable (e.g. one where result_should_continue() 701 * returns true) and we want to convey that error. 702 */ 703 sub_ret = load_requested_partitions( 704 ops, requested_partitions, ab_suffix, slot_data); 705 if (sub_ret != AVB_SLOT_VERIFY_RESULT_OK) { 706 ret = sub_ret; 707 } 708 goto out; 709 } 710 711 /* Now go through all descriptors and take the appropriate action: 712 * 713 * - hash descriptor: Load data from partition, calculate hash, and 714 * checks that it matches what's in the hash descriptor. 715 * 716 * - hashtree descriptor: Do nothing since verification happens 717 * on-the-fly from within the OS. 718 * 719 * - chained partition descriptor: Load the footer, load the vbmeta 720 * image, verify vbmeta image (includes rollback checks, hash 721 * checks, bail on chained partitions). 722 */ 723 descriptors = 724 avb_descriptor_get_all(vbmeta_buf, vbmeta_num_read, &num_descriptors); 725 for (n = 0; n < num_descriptors; n++) { 726 AvbDescriptor desc; 727 728 if (!avb_descriptor_validate_and_byteswap(descriptors[n], &desc)) { 729 avb_errorv(full_partition_name, ": Descriptor is invalid.\n", NULL); 730 ret = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA; 731 goto out; 732 } 733 734 switch (desc.tag) { 735 case AVB_DESCRIPTOR_TAG_HASH: { 736 AvbSlotVerifyResult sub_ret; 737 sub_ret = load_and_verify_hash_partition(ops, 738 requested_partitions, 739 ab_suffix, 740 allow_verification_error, 741 descriptors[n], 742 slot_data); 743 if (sub_ret != AVB_SLOT_VERIFY_RESULT_OK) { 744 ret = sub_ret; 745 if (!allow_verification_error || !result_should_continue(ret)) { 746 goto out; 747 } 748 } 749 } break; 750 751 case AVB_DESCRIPTOR_TAG_CHAIN_PARTITION: { 752 AvbSlotVerifyResult sub_ret; 753 AvbChainPartitionDescriptor chain_desc; 754 const uint8_t* chain_partition_name; 755 const uint8_t* chain_public_key; 756 757 /* Only allow CHAIN_PARTITION descriptors in the main vbmeta image. */ 758 if (!is_main_vbmeta) { 759 avb_errorv(full_partition_name, 760 ": Encountered chain descriptor not in main image.\n", 761 NULL); 762 ret = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA; 763 goto out; 764 } 765 766 if (!avb_chain_partition_descriptor_validate_and_byteswap( 767 (AvbChainPartitionDescriptor*)descriptors[n], &chain_desc)) { 768 avb_errorv(full_partition_name, 769 ": Chain partition descriptor is invalid.\n", 770 NULL); 771 ret = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA; 772 goto out; 773 } 774 775 if (chain_desc.rollback_index_location == 0) { 776 avb_errorv(full_partition_name, 777 ": Chain partition has invalid " 778 "rollback_index_location field.\n", 779 NULL); 780 ret = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA; 781 goto out; 782 } 783 784 chain_partition_name = ((const uint8_t*)descriptors[n]) + 785 sizeof(AvbChainPartitionDescriptor); 786 chain_public_key = chain_partition_name + chain_desc.partition_name_len; 787 788 sub_ret = load_and_verify_vbmeta(ops, 789 requested_partitions, 790 ab_suffix, 791 allow_verification_error, 792 toplevel_vbmeta_flags, 793 chain_desc.rollback_index_location, 794 (const char*)chain_partition_name, 795 chain_desc.partition_name_len, 796 chain_public_key, 797 chain_desc.public_key_len, 798 slot_data, 799 NULL /* out_algorithm_type */); 800 if (sub_ret != AVB_SLOT_VERIFY_RESULT_OK) { 801 ret = sub_ret; 802 if (!result_should_continue(ret)) { 803 goto out; 804 } 805 } 806 } break; 807 808 case AVB_DESCRIPTOR_TAG_KERNEL_CMDLINE: { 809 const uint8_t* kernel_cmdline; 810 AvbKernelCmdlineDescriptor kernel_cmdline_desc; 811 bool apply_cmdline; 812 813 if (!avb_kernel_cmdline_descriptor_validate_and_byteswap( 814 (AvbKernelCmdlineDescriptor*)descriptors[n], 815 &kernel_cmdline_desc)) { 816 avb_errorv(full_partition_name, 817 ": Kernel cmdline descriptor is invalid.\n", 818 NULL); 819 ret = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA; 820 goto out; 821 } 822 823 kernel_cmdline = ((const uint8_t*)descriptors[n]) + 824 sizeof(AvbKernelCmdlineDescriptor); 825 826 if (!avb_validate_utf8(kernel_cmdline, 827 kernel_cmdline_desc.kernel_cmdline_length)) { 828 avb_errorv(full_partition_name, 829 ": Kernel cmdline is not valid UTF-8.\n", 830 NULL); 831 ret = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA; 832 goto out; 833 } 834 835 /* Compare the flags for top-level VBMeta struct with flags in 836 * the command-line descriptor so command-line snippets only 837 * intended for a certain mode (dm-verity enabled/disabled) 838 * are skipped if applicable. 839 */ 840 apply_cmdline = true; 841 if (toplevel_vbmeta_flags & AVB_VBMETA_IMAGE_FLAGS_HASHTREE_DISABLED) { 842 if (kernel_cmdline_desc.flags & 843 AVB_KERNEL_CMDLINE_FLAGS_USE_ONLY_IF_HASHTREE_NOT_DISABLED) { 844 apply_cmdline = false; 845 } 846 } else { 847 if (kernel_cmdline_desc.flags & 848 AVB_KERNEL_CMDLINE_FLAGS_USE_ONLY_IF_HASHTREE_DISABLED) { 849 apply_cmdline = false; 850 } 851 } 852 853 if (apply_cmdline) { 854 if (slot_data->cmdline == NULL) { 855 slot_data->cmdline = 856 avb_calloc(kernel_cmdline_desc.kernel_cmdline_length + 1); 857 if (slot_data->cmdline == NULL) { 858 ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM; 859 goto out; 860 } 861 avb_memcpy(slot_data->cmdline, 862 kernel_cmdline, 863 kernel_cmdline_desc.kernel_cmdline_length); 864 } else { 865 /* new cmdline is: <existing_cmdline> + ' ' + <newcmdline> + '\0' */ 866 size_t orig_size = avb_strlen(slot_data->cmdline); 867 size_t new_size = 868 orig_size + 1 + kernel_cmdline_desc.kernel_cmdline_length + 1; 869 char* new_cmdline = avb_calloc(new_size); 870 if (new_cmdline == NULL) { 871 ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM; 872 goto out; 873 } 874 avb_memcpy(new_cmdline, slot_data->cmdline, orig_size); 875 new_cmdline[orig_size] = ' '; 876 avb_memcpy(new_cmdline + orig_size + 1, 877 kernel_cmdline, 878 kernel_cmdline_desc.kernel_cmdline_length); 879 avb_free(slot_data->cmdline); 880 slot_data->cmdline = new_cmdline; 881 } 882 } 883 } break; 884 885 /* Explicit fall-through */ 886 case AVB_DESCRIPTOR_TAG_PROPERTY: 887 case AVB_DESCRIPTOR_TAG_HASHTREE: 888 /* Do nothing. */ 889 break; 890 } 891 } 892 893 if (rollback_index_location >= AVB_MAX_NUMBER_OF_ROLLBACK_INDEX_LOCATIONS) { 894 avb_errorv( 895 full_partition_name, ": Invalid rollback_index_location.\n", NULL); 896 ret = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA; 897 goto out; 898 } 899 900 slot_data->rollback_indexes[rollback_index_location] = 901 vbmeta_header.rollback_index; 902 903 if (out_algorithm_type != NULL) { 904 *out_algorithm_type = (AvbAlgorithmType)vbmeta_header.algorithm_type; 905 } 906 907 out: 908 /* If |vbmeta_image_data| isn't NULL it means that it adopted 909 * |vbmeta_buf| so in that case don't free it here. 910 */ 911 if (vbmeta_image_data == NULL) { 912 if (vbmeta_buf != NULL) { 913 avb_free(vbmeta_buf); 914 } 915 } 916 if (descriptors != NULL) { 917 avb_free(descriptors); 918 } 919 return ret; 920 } 921 922 AvbSlotVerifyResult avb_slot_verify(AvbOps* ops, 923 const char* const* requested_partitions, 924 const char* ab_suffix, 925 AvbSlotVerifyFlags flags, 926 AvbHashtreeErrorMode hashtree_error_mode, 927 AvbSlotVerifyData** out_data) { 928 AvbSlotVerifyResult ret; 929 AvbSlotVerifyData* slot_data = NULL; 930 AvbAlgorithmType algorithm_type = AVB_ALGORITHM_TYPE_NONE; 931 bool using_boot_for_vbmeta = false; 932 AvbVBMetaImageHeader toplevel_vbmeta; 933 bool allow_verification_error = 934 (flags & AVB_SLOT_VERIFY_FLAGS_ALLOW_VERIFICATION_ERROR); 935 936 /* Fail early if we're missing the AvbOps needed for slot verification. 937 * 938 * For now, handle get_size_of_partition() not being implemented. In 939 * a later release we may change that. 940 */ 941 avb_assert(ops->read_is_device_unlocked != NULL); 942 avb_assert(ops->read_from_partition != NULL); 943 avb_assert(ops->validate_vbmeta_public_key != NULL); 944 avb_assert(ops->read_rollback_index != NULL); 945 avb_assert(ops->get_unique_guid_for_partition != NULL); 946 947 if (out_data != NULL) { 948 *out_data = NULL; 949 } 950 951 /* Allowing dm-verity errors defeats the purpose of verified boot so 952 * only allow this if set up to allow verification errors 953 * (e.g. typically only UNLOCKED mode). 954 */ 955 if (hashtree_error_mode == AVB_HASHTREE_ERROR_MODE_LOGGING && 956 !allow_verification_error) { 957 ret = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_ARGUMENT; 958 goto fail; 959 } 960 961 slot_data = avb_calloc(sizeof(AvbSlotVerifyData)); 962 if (slot_data == NULL) { 963 ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM; 964 goto fail; 965 } 966 slot_data->vbmeta_images = 967 avb_calloc(sizeof(AvbVBMetaData) * MAX_NUMBER_OF_VBMETA_IMAGES); 968 if (slot_data->vbmeta_images == NULL) { 969 ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM; 970 goto fail; 971 } 972 slot_data->loaded_partitions = 973 avb_calloc(sizeof(AvbPartitionData) * MAX_NUMBER_OF_LOADED_PARTITIONS); 974 if (slot_data->loaded_partitions == NULL) { 975 ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM; 976 goto fail; 977 } 978 979 ret = load_and_verify_vbmeta(ops, 980 requested_partitions, 981 ab_suffix, 982 allow_verification_error, 983 0 /* toplevel_vbmeta_flags */, 984 0 /* rollback_index_location */, 985 "vbmeta", 986 avb_strlen("vbmeta"), 987 NULL /* expected_public_key */, 988 0 /* expected_public_key_length */, 989 slot_data, 990 &algorithm_type); 991 if (!allow_verification_error && ret != AVB_SLOT_VERIFY_RESULT_OK) { 992 goto fail; 993 } 994 995 /* If things check out, mangle the kernel command-line as needed. */ 996 if (result_should_continue(ret)) { 997 if (avb_strcmp(slot_data->vbmeta_images[0].partition_name, "vbmeta") != 0) { 998 avb_assert( 999 avb_strcmp(slot_data->vbmeta_images[0].partition_name, "boot") == 0); 1000 using_boot_for_vbmeta = true; 1001 } 1002 1003 /* Byteswap top-level vbmeta header since we'll need it below. */ 1004 avb_vbmeta_image_header_to_host_byte_order( 1005 (const AvbVBMetaImageHeader*)slot_data->vbmeta_images[0].vbmeta_data, 1006 &toplevel_vbmeta); 1007 1008 /* Fill in |ab_suffix| field. */ 1009 slot_data->ab_suffix = avb_strdup(ab_suffix); 1010 if (slot_data->ab_suffix == NULL) { 1011 ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM; 1012 goto fail; 1013 } 1014 1015 /* If verification is disabled, we are done ... we specifically 1016 * don't want to add any androidboot.* options since verification 1017 * is disabled. 1018 */ 1019 if (toplevel_vbmeta.flags & AVB_VBMETA_IMAGE_FLAGS_VERIFICATION_DISABLED) { 1020 /* Since verification is disabled we didn't process any 1021 * descriptors and thus there's no cmdline... so set root= such 1022 * that the system partition is mounted. 1023 */ 1024 avb_assert(slot_data->cmdline == NULL); 1025 slot_data->cmdline = 1026 avb_strdup("root=PARTUUID=$(ANDROID_SYSTEM_PARTUUID)"); 1027 if (slot_data->cmdline == NULL) { 1028 ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM; 1029 goto fail; 1030 } 1031 } else { 1032 /* Add options - any failure in avb_append_options() is either an 1033 * I/O or OOM error. 1034 */ 1035 AvbSlotVerifyResult sub_ret = avb_append_options( 1036 ops, slot_data, &toplevel_vbmeta, algorithm_type, 1037 hashtree_error_mode); 1038 if (sub_ret != AVB_SLOT_VERIFY_RESULT_OK) { 1039 ret = sub_ret; 1040 goto fail; 1041 } 1042 } 1043 1044 /* Substitute $(ANDROID_SYSTEM_PARTUUID) and friends. */ 1045 if (slot_data->cmdline != NULL) { 1046 char* new_cmdline; 1047 new_cmdline = avb_sub_cmdline( 1048 ops, slot_data->cmdline, ab_suffix, using_boot_for_vbmeta); 1049 if (new_cmdline != slot_data->cmdline) { 1050 if (new_cmdline == NULL) { 1051 ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM; 1052 goto fail; 1053 } 1054 avb_free(slot_data->cmdline); 1055 slot_data->cmdline = new_cmdline; 1056 } 1057 } 1058 1059 if (out_data != NULL) { 1060 *out_data = slot_data; 1061 } else { 1062 avb_slot_verify_data_free(slot_data); 1063 } 1064 } 1065 1066 if (!allow_verification_error) { 1067 avb_assert(ret == AVB_SLOT_VERIFY_RESULT_OK); 1068 } 1069 1070 return ret; 1071 1072 fail: 1073 if (slot_data != NULL) { 1074 avb_slot_verify_data_free(slot_data); 1075 } 1076 return ret; 1077 } 1078 1079 void avb_slot_verify_data_free(AvbSlotVerifyData* data) { 1080 if (data->ab_suffix != NULL) { 1081 avb_free(data->ab_suffix); 1082 } 1083 if (data->cmdline != NULL) { 1084 avb_free(data->cmdline); 1085 } 1086 if (data->vbmeta_images != NULL) { 1087 size_t n; 1088 for (n = 0; n < data->num_vbmeta_images; n++) { 1089 AvbVBMetaData* vbmeta_image = &data->vbmeta_images[n]; 1090 if (vbmeta_image->partition_name != NULL) { 1091 avb_free(vbmeta_image->partition_name); 1092 } 1093 if (vbmeta_image->vbmeta_data != NULL) { 1094 avb_free(vbmeta_image->vbmeta_data); 1095 } 1096 } 1097 avb_free(data->vbmeta_images); 1098 } 1099 if (data->loaded_partitions != NULL) { 1100 size_t n; 1101 for (n = 0; n < data->num_loaded_partitions; n++) { 1102 AvbPartitionData* loaded_partition = &data->loaded_partitions[n]; 1103 if (loaded_partition->partition_name != NULL) { 1104 avb_free(loaded_partition->partition_name); 1105 } 1106 if (loaded_partition->data != NULL && !loaded_partition->preloaded) { 1107 avb_free(loaded_partition->data); 1108 } 1109 } 1110 avb_free(data->loaded_partitions); 1111 } 1112 avb_free(data); 1113 } 1114 1115 const char* avb_slot_verify_result_to_string(AvbSlotVerifyResult result) { 1116 const char* ret = NULL; 1117 1118 switch (result) { 1119 case AVB_SLOT_VERIFY_RESULT_OK: 1120 ret = "OK"; 1121 break; 1122 case AVB_SLOT_VERIFY_RESULT_ERROR_OOM: 1123 ret = "ERROR_OOM"; 1124 break; 1125 case AVB_SLOT_VERIFY_RESULT_ERROR_IO: 1126 ret = "ERROR_IO"; 1127 break; 1128 case AVB_SLOT_VERIFY_RESULT_ERROR_VERIFICATION: 1129 ret = "ERROR_VERIFICATION"; 1130 break; 1131 case AVB_SLOT_VERIFY_RESULT_ERROR_ROLLBACK_INDEX: 1132 ret = "ERROR_ROLLBACK_INDEX"; 1133 break; 1134 case AVB_SLOT_VERIFY_RESULT_ERROR_PUBLIC_KEY_REJECTED: 1135 ret = "ERROR_PUBLIC_KEY_REJECTED"; 1136 break; 1137 case AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA: 1138 ret = "ERROR_INVALID_METADATA"; 1139 break; 1140 case AVB_SLOT_VERIFY_RESULT_ERROR_UNSUPPORTED_VERSION: 1141 ret = "ERROR_UNSUPPORTED_VERSION"; 1142 break; 1143 case AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_ARGUMENT: 1144 ret = "ERROR_INVALID_ARGUMENT"; 1145 break; 1146 /* Do not add a 'default:' case here because of -Wswitch. */ 1147 } 1148 1149 if (ret == NULL) { 1150 avb_error("Unknown AvbSlotVerifyResult value.\n"); 1151 ret = "(unknown)"; 1152 } 1153 1154 return ret; 1155 } 1156