| OLD | NEW |
| 1 /* Copyright (c) 2010 The Chromium OS Authors. All rights reserved. | 1 /* Copyright (c) 2010 The Chromium OS Authors. All rights reserved. |
| 2 * Use of this source code is governed by a BSD-style license that can be | 2 * Use of this source code is governed by a BSD-style license that can be |
| 3 * found in the LICENSE file. | 3 * found in the LICENSE file. |
| 4 * | 4 * |
| 5 * Functions for loading a kernel from disk. | 5 * Functions for loading a kernel from disk. |
| 6 * (Firmware portion) | 6 * (Firmware portion) |
| 7 */ | 7 */ |
| 8 | 8 |
| 9 #include "vboot_kernel.h" | 9 #include "vboot_kernel.h" |
| 10 | 10 |
| (...skipping 102 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 113 | 113 |
| 114 int LoadKernel(LoadKernelParams* params) { | 114 int LoadKernel(LoadKernelParams* params) { |
| 115 VbPublicKey* kernel_subkey; | 115 VbPublicKey* kernel_subkey; |
| 116 GptData gpt; | 116 GptData gpt; |
| 117 uint64_t part_start, part_size; | 117 uint64_t part_start, part_size; |
| 118 uint64_t blba; | 118 uint64_t blba; |
| 119 uint64_t kbuf_sectors; | 119 uint64_t kbuf_sectors; |
| 120 uint8_t* kbuf = NULL; | 120 uint8_t* kbuf = NULL; |
| 121 int found_partitions = 0; | 121 int found_partitions = 0; |
| 122 int good_partition = -1; | 122 int good_partition = -1; |
| 123 uint16_t tpm_key_version = 0; | 123 uint32_t tpm_version = 0; |
| 124 uint16_t tpm_kernel_version = 0; | 124 uint64_t lowest_version = 0xFFFFFFFF; |
| 125 uint64_t lowest_key_version = 0xFFFF; | |
| 126 uint64_t lowest_kernel_version = 0xFFFF; | |
| 127 int is_dev; | 125 int is_dev; |
| 128 int is_rec; | 126 int is_rec; |
| 129 int is_normal; | 127 int is_normal; |
| 130 uint32_t status; | 128 uint32_t status; |
| 131 | 129 |
| 132 /* Sanity Checks */ | 130 /* Sanity Checks */ |
| 133 if (!params || | 131 if (!params || |
| 134 !params->bytes_per_lba || | 132 !params->bytes_per_lba || |
| 135 !params->ending_lba || | 133 !params->ending_lba || |
| 136 !params->kernel_buffer || | 134 !params->kernel_buffer || |
| (...skipping 20 matching lines...) Expand all Loading... |
| 157 if (0 != RollbackKernelRecovery(is_dev)) { | 155 if (0 != RollbackKernelRecovery(is_dev)) { |
| 158 VBDEBUG(("Error setting up TPM for recovery kernel\n")); | 156 VBDEBUG(("Error setting up TPM for recovery kernel\n")); |
| 159 /* Ignore return code, since we need to boot recovery mode to | 157 /* Ignore return code, since we need to boot recovery mode to |
| 160 * fix the TPM. */ | 158 * fix the TPM. */ |
| 161 } | 159 } |
| 162 } | 160 } |
| 163 | 161 |
| 164 if (is_normal) { | 162 if (is_normal) { |
| 165 /* Read current kernel key index from TPM. Assumes TPM is already | 163 /* Read current kernel key index from TPM. Assumes TPM is already |
| 166 * initialized. */ | 164 * initialized. */ |
| 167 status = RollbackKernelRead(&tpm_key_version, &tpm_kernel_version); | 165 status = RollbackKernelRead(&tpm_version); |
| 168 if (0 != status) { | 166 if (0 != status) { |
| 169 VBDEBUG(("Unable to get kernel versions from TPM\n")); | 167 VBDEBUG(("Unable to get kernel versions from TPM\n")); |
| 170 return (status == TPM_E_MUST_REBOOT ? | 168 return (status == TPM_E_MUST_REBOOT ? |
| 171 LOAD_KERNEL_REBOOT : LOAD_KERNEL_RECOVERY); | 169 LOAD_KERNEL_REBOOT : LOAD_KERNEL_RECOVERY); |
| 172 } | 170 } |
| 173 } else if (is_dev && !is_rec) { | 171 } else if (is_dev && !is_rec) { |
| 174 /* In developer mode, we ignore the kernel subkey, and just use | 172 /* In developer mode, we ignore the kernel subkey, and just use |
| 175 * the SHA-512 hash to verify the key block. */ | 173 * the SHA-512 hash to verify the key block. */ |
| 176 kernel_subkey = NULL; | 174 kernel_subkey = NULL; |
| 177 } | 175 } |
| (...skipping 17 matching lines...) Expand all Loading... |
| 195 kbuf = (uint8_t*)Malloc(KBUF_SIZE); | 193 kbuf = (uint8_t*)Malloc(KBUF_SIZE); |
| 196 if (!kbuf) | 194 if (!kbuf) |
| 197 break; | 195 break; |
| 198 | 196 |
| 199 /* Loop over candidate kernel partitions */ | 197 /* Loop over candidate kernel partitions */ |
| 200 while (GPT_SUCCESS == GptNextKernelEntry(&gpt, &part_start, &part_size)) { | 198 while (GPT_SUCCESS == GptNextKernelEntry(&gpt, &part_start, &part_size)) { |
| 201 VbKeyBlockHeader* key_block; | 199 VbKeyBlockHeader* key_block; |
| 202 VbKernelPreambleHeader* preamble; | 200 VbKernelPreambleHeader* preamble; |
| 203 RSAPublicKey* data_key; | 201 RSAPublicKey* data_key; |
| 204 uint64_t key_version; | 202 uint64_t key_version; |
| 203 uint64_t combined_version; |
| 205 uint64_t body_offset; | 204 uint64_t body_offset; |
| 206 | 205 |
| 207 VBDEBUG(("Found kernel entry at %" PRIu64 " size %" PRIu64 "\n", | 206 VBDEBUG(("Found kernel entry at %" PRIu64 " size %" PRIu64 "\n", |
| 208 part_start, part_size)); | 207 part_start, part_size)); |
| 209 | 208 |
| 210 /* Found at least one kernel partition. */ | 209 /* Found at least one kernel partition. */ |
| 211 found_partitions++; | 210 found_partitions++; |
| 212 | 211 |
| 213 /* Read the first part of the kernel partition */ | 212 /* Read the first part of the kernel partition */ |
| 214 if (part_size < kbuf_sectors) | 213 if (part_size < kbuf_sectors) |
| (...skipping 22 matching lines...) Expand all Loading... |
| 237 KEY_BLOCK_FLAG_RECOVERY_0))) { | 236 KEY_BLOCK_FLAG_RECOVERY_0))) { |
| 238 VBDEBUG(("Recovery flag mismatch.\n")); | 237 VBDEBUG(("Recovery flag mismatch.\n")); |
| 239 continue; | 238 continue; |
| 240 } | 239 } |
| 241 } | 240 } |
| 242 | 241 |
| 243 /* Check for rollback of key version. Note this is implicitly | 242 /* Check for rollback of key version. Note this is implicitly |
| 244 * skipped in recovery and developer modes because those set | 243 * skipped in recovery and developer modes because those set |
| 245 * key_version=0 above. */ | 244 * key_version=0 above. */ |
| 246 key_version = key_block->data_key.key_version; | 245 key_version = key_block->data_key.key_version; |
| 247 if (key_version < tpm_key_version) { | 246 if (key_version < (tpm_version >> 16)) { |
| 248 VBDEBUG(("Key version too old.\n")); | 247 VBDEBUG(("Key version too old.\n")); |
| 249 continue; | 248 continue; |
| 250 } | 249 } |
| 251 | 250 |
| 252 /* Get the key for preamble/data verification from the key block */ | 251 /* Get the key for preamble/data verification from the key block */ |
| 253 data_key = PublicKeyToRSA(&key_block->data_key); | 252 data_key = PublicKeyToRSA(&key_block->data_key); |
| 254 if (!data_key) | 253 if (!data_key) |
| 255 continue; | 254 continue; |
| 256 | 255 |
| 257 /* Verify the preamble, which follows the key block */ | 256 /* Verify the preamble, which follows the key block */ |
| 258 preamble = (VbKernelPreambleHeader*)(kbuf + key_block->key_block_size); | 257 preamble = (VbKernelPreambleHeader*)(kbuf + key_block->key_block_size); |
| 259 if ((0 != VerifyKernelPreamble(preamble, | 258 if ((0 != VerifyKernelPreamble(preamble, |
| 260 KBUF_SIZE - key_block->key_block_size, | 259 KBUF_SIZE - key_block->key_block_size, |
| 261 data_key))) { | 260 data_key))) { |
| 262 VBDEBUG(("Preamble verification failed.\n")); | 261 VBDEBUG(("Preamble verification failed.\n")); |
| 263 RSAPublicKeyFree(data_key); | 262 RSAPublicKeyFree(data_key); |
| 264 continue; | 263 continue; |
| 265 } | 264 } |
| 266 | 265 |
| 267 /* Check for rollback of kernel version. Note this is implicitly | 266 /* Check for rollback of kernel version. Note this is implicitly |
| 268 * skipped in recovery and developer modes because those set | 267 * skipped in recovery and developer modes because rollback_index |
| 269 * key_version=0 and kernel_version=0 above. */ | 268 * sets those to 0 in those modes. */ |
| 270 if (key_version == tpm_key_version && | 269 combined_version = ((key_version << 16) | |
| 271 preamble->kernel_version < tpm_kernel_version) { | 270 (preamble->kernel_version & 0xFFFF)); |
| 271 if (combined_version < tpm_version) { |
| 272 VBDEBUG(("Kernel version too low.\n")); | 272 VBDEBUG(("Kernel version too low.\n")); |
| 273 RSAPublicKeyFree(data_key); | 273 RSAPublicKeyFree(data_key); |
| 274 continue; | 274 continue; |
| 275 } | 275 } |
| 276 | 276 |
| 277 VBDEBUG(("Kernel preamble is good.\n")); | 277 VBDEBUG(("Kernel preamble is good.\n")); |
| 278 | 278 |
| 279 /* Check for lowest key version from a valid header. */ | 279 /* Check for lowest version from a valid header. */ |
| 280 if (lowest_key_version > key_version) { | 280 if (lowest_version > combined_version) |
| 281 lowest_key_version = key_version; | 281 lowest_version = combined_version; |
| 282 lowest_kernel_version = preamble->kernel_version; | |
| 283 } | |
| 284 else if (lowest_key_version == key_version && | |
| 285 lowest_kernel_version > preamble->kernel_version) { | |
| 286 lowest_kernel_version = preamble->kernel_version; | |
| 287 } | |
| 288 | 282 |
| 289 /* If we already have a good kernel, no need to read another | 283 /* If we already have a good kernel, no need to read another |
| 290 * one; we only needed to look at the versions to check for | 284 * one; we only needed to look at the versions to check for |
| 291 * rollback. */ | 285 * rollback. */ |
| 292 if (-1 != good_partition) | 286 if (-1 != good_partition) |
| 293 continue; | 287 continue; |
| 294 | 288 |
| 295 /* Verify body load address matches what we expect */ | 289 /* Verify body load address matches what we expect */ |
| 296 if ((preamble->body_load_address != (size_t)params->kernel_buffer) && | 290 if ((preamble->body_load_address != (size_t)params->kernel_buffer) && |
| 297 !(params->boot_flags & BOOT_FLAG_SKIP_ADDR_CHECK)) { | 291 !(params->boot_flags & BOOT_FLAG_SKIP_ADDR_CHECK)) { |
| (...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 355 if (!is_normal) { | 349 if (!is_normal) { |
| 356 VBDEBUG(("Boot_flags = !is_normal\n")); | 350 VBDEBUG(("Boot_flags = !is_normal\n")); |
| 357 break; | 351 break; |
| 358 } | 352 } |
| 359 | 353 |
| 360 /* Otherwise, we're in normal boot mode, so we do care about the | 354 /* Otherwise, we're in normal boot mode, so we do care about the |
| 361 * key index in the TPM. If the good partition's key version is | 355 * key index in the TPM. If the good partition's key version is |
| 362 * the same as the tpm, then the TPM doesn't need updating; we | 356 * the same as the tpm, then the TPM doesn't need updating; we |
| 363 * can stop now. Otherwise, we'll check all the other headers | 357 * can stop now. Otherwise, we'll check all the other headers |
| 364 * to see if they contain a newer key. */ | 358 * to see if they contain a newer key. */ |
| 365 if (key_version == tpm_key_version && | 359 if (combined_version == tpm_version) { |
| 366 preamble->kernel_version == tpm_kernel_version) { | 360 VBDEBUG(("Same kernel version\n")); |
| 367 VBDEBUG(("Same key version\n")); | |
| 368 break; | 361 break; |
| 369 } | 362 } |
| 370 } /* while(GptNextKernelEntry) */ | 363 } /* while(GptNextKernelEntry) */ |
| 371 } while(0); | 364 } while(0); |
| 372 | 365 |
| 373 /* Free kernel buffer */ | 366 /* Free kernel buffer */ |
| 374 if (kbuf) | 367 if (kbuf) |
| 375 Free(kbuf); | 368 Free(kbuf); |
| 376 | 369 |
| 377 /* Write and free GPT data */ | 370 /* Write and free GPT data */ |
| 378 WriteAndFreeGptData(&gpt); | 371 WriteAndFreeGptData(&gpt); |
| 379 | 372 |
| 380 /* Handle finding a good partition */ | 373 /* Handle finding a good partition */ |
| 381 if (good_partition >= 0) { | 374 if (good_partition >= 0) { |
| 382 VBDEBUG(("Good_partition >= 0\n")); | 375 VBDEBUG(("Good_partition >= 0\n")); |
| 383 | 376 |
| 384 /* See if we need to update the TPM */ | 377 /* See if we need to update the TPM */ |
| 385 if (is_normal) { | 378 if (is_normal) { |
| 386 /* We only update the TPM in normal boot mode. In developer | 379 /* We only update the TPM in normal boot mode. In developer |
| 387 * mode, the kernel is self-signed by the developer, so we can't | 380 * mode, the kernel is self-signed by the developer, so we can't |
| 388 * trust the key version and wouldn't want to roll the TPM | 381 * trust the key version and wouldn't want to roll the TPM |
| 389 * forward. In recovery mode, the TPM stays PP-unlocked, so | 382 * forward. In recovery mode, the TPM stays PP-unlocked, so |
| 390 * anything we write gets blown away by the firmware when we go | 383 * anything we write gets blown away by the firmware when we go |
| 391 * back to normal mode. */ | 384 * back to normal mode. */ |
| 392 VBDEBUG(("Boot_flags = is_normal\n")); | 385 VBDEBUG(("Boot_flags = is_normal\n")); |
| 393 if ((lowest_key_version > tpm_key_version) || | 386 if (lowest_version > tpm_version) { |
| 394 (lowest_key_version == tpm_key_version && | 387 status = RollbackKernelWrite((uint32_t)lowest_version); |
| 395 lowest_kernel_version > tpm_kernel_version)) { | |
| 396 | |
| 397 status = RollbackKernelWrite((uint16_t)lowest_key_version, | |
| 398 (uint16_t)lowest_kernel_version); | |
| 399 if (0 != status) { | 388 if (0 != status) { |
| 400 VBDEBUG(("Error writing kernel versions to TPM.\n")); | 389 VBDEBUG(("Error writing kernel versions to TPM.\n")); |
| 401 return (status == TPM_E_MUST_REBOOT ? | 390 return (status == TPM_E_MUST_REBOOT ? |
| 402 LOAD_KERNEL_REBOOT : LOAD_KERNEL_RECOVERY); | 391 LOAD_KERNEL_REBOOT : LOAD_KERNEL_RECOVERY); |
| 403 } | 392 } |
| 404 } | 393 } |
| 405 } | 394 } |
| 406 | 395 |
| 407 /* Lock the kernel versions */ | 396 /* Lock the kernel versions */ |
| 408 status = RollbackKernelLock(); | 397 status = RollbackKernelLock(); |
| 409 if (0 != status) { | 398 if (0 != status) { |
| 410 VBDEBUG(("Error locking kernel versions.\n")); | 399 VBDEBUG(("Error locking kernel versions.\n")); |
| 411 /* Don't reboot to recovery mode if we're already there */ | 400 /* Don't reboot to recovery mode if we're already there */ |
| 412 if (!is_rec) | 401 if (!is_rec) |
| 413 return (status == TPM_E_MUST_REBOOT ? | 402 return (status == TPM_E_MUST_REBOOT ? |
| 414 LOAD_KERNEL_REBOOT : LOAD_KERNEL_RECOVERY); | 403 LOAD_KERNEL_REBOOT : LOAD_KERNEL_RECOVERY); |
| 415 } | 404 } |
| 416 | 405 |
| 417 /* Success! */ | 406 /* Success! */ |
| 418 return LOAD_KERNEL_SUCCESS; | 407 return LOAD_KERNEL_SUCCESS; |
| 419 } | 408 } |
| 420 | 409 |
| 421 // Handle error cases | 410 // Handle error cases |
| 422 if (found_partitions) | 411 if (found_partitions) |
| 423 return LOAD_KERNEL_INVALID; | 412 return LOAD_KERNEL_INVALID; |
| 424 else | 413 else |
| 425 return LOAD_KERNEL_NOT_FOUND; | 414 return LOAD_KERNEL_NOT_FOUND; |
| 426 } | 415 } |
| OLD | NEW |