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 |