| /* Copyright (c) 2010 The Chromium OS Authors. All rights reserved. |
| * Use of this source code is governed by a BSD-style license that can be |
| * found in the LICENSE file. |
| * |
| * Functions for generating and manipulating a verified boot kernel image. |
| * (Userland portion) |
| */ |
| #include "kernel_image.h" |
| |
| #include <fcntl.h> |
| #include <stddef.h> |
| #include <stdio.h> |
| #include <sys/types.h> |
| #include <sys/stat.h> |
| #include <unistd.h> |
| |
| #include "cryptolib.h" |
| #include "file_keys.h" |
| #include "kernel_blob.h" |
| #include "rollback_index.h" |
| #include "signature_digest.h" |
| #include "stateful_util.h" |
| |
| /* Macro to determine the size of a field structure in the KernelImage |
| * structure. */ |
| #define FIELD_LEN(field) (sizeof(((KernelImage*)0)->field)) |
| |
| KernelImage* KernelImageNew(void) { |
| KernelImage* image = (KernelImage*) Malloc(sizeof(KernelImage)); |
| if (image) { |
| image->kernel_sign_key = NULL; |
| image->kernel_key_signature = NULL; |
| image->preamble_signature = NULL; |
| image->kernel_signature = NULL; |
| image->kernel_data = NULL; |
| image->padded_header_size = 0x4000; |
| } |
| return image; |
| } |
| |
| void KernelImageFree(KernelImage* image) { |
| if (image) { |
| Free(image->kernel_sign_key); |
| Free(image->kernel_key_signature); |
| Free(image->preamble_signature); |
| Free(image->kernel_signature); |
| Free(image->kernel_data); |
| Free(image); |
| } |
| } |
| |
| uint64_t GetHeaderSizeOnDisk(const KernelImage* image) { |
| uint64_t kernel_signature_len = siglen_map[image->kernel_sign_algorithm]; |
| uint64_t kernel_key_signature_len = |
| siglen_map[image->firmware_sign_algorithm]; |
| |
| return FIELD_LEN(magic) + |
| GetKernelHeaderLen(image) + |
| kernel_key_signature_len + |
| GetKernelPreambleLen(image->kernel_sign_algorithm) + |
| kernel_signature_len; |
| } |
| |
| |
| KernelImage* ReadKernelImage(const char* input_file) { |
| uint64_t file_size; |
| uint64_t on_disk_header_size; |
| uint64_t on_disk_padding; |
| int header_len = 0; |
| int firmware_sign_key_len; |
| int kernel_key_signature_len; |
| int kernel_sign_key_len; |
| int kernel_signature_len; |
| uint8_t* kernel_buf; |
| uint8_t header_checksum[FIELD_LEN(header_checksum)]; |
| MemcpyState st; |
| KernelImage* image = KernelImageNew(); |
| |
| if (!image) |
| return NULL; |
| |
| kernel_buf = BufferFromFile(input_file, &file_size); |
| |
| st.remaining_len = file_size; |
| st.remaining_buf = kernel_buf; |
| st.overrun = 0; |
| |
| /* Read and compare magic bytes. */ |
| StatefulMemcpy(&st, &image->magic, KERNEL_MAGIC_SIZE); |
| |
| if (SafeMemcmp(image->magic, KERNEL_MAGIC, KERNEL_MAGIC_SIZE)) { |
| debug("Wrong Kernel Magic.\n"); |
| Free(kernel_buf); |
| return NULL; |
| } |
| StatefulMemcpy(&st, &image->header_version, FIELD_LEN(header_version)); |
| StatefulMemcpy(&st, &image->header_len, FIELD_LEN(header_len)); |
| StatefulMemcpy(&st, &image->firmware_sign_algorithm, |
| FIELD_LEN(firmware_sign_algorithm)); |
| StatefulMemcpy(&st, &image->kernel_sign_algorithm, |
| FIELD_LEN(kernel_sign_algorithm)); |
| |
| /* Valid Kernel Key signing algorithm. */ |
| if (image->firmware_sign_algorithm >= kNumAlgorithms) { |
| Free(kernel_buf); |
| return NULL; |
| } |
| |
| /* Valid Kernel Signing Algorithm? */ |
| if (image->kernel_sign_algorithm >= kNumAlgorithms) { |
| Free(kernel_buf); |
| return NULL; |
| } |
| |
| /* Compute size of pre-processed RSA public keys and signatures. */ |
| firmware_sign_key_len = RSAProcessedKeySize(image->firmware_sign_algorithm); |
| kernel_key_signature_len = siglen_map[image->firmware_sign_algorithm]; |
| kernel_sign_key_len = RSAProcessedKeySize(image->kernel_sign_algorithm); |
| kernel_signature_len = siglen_map[image->kernel_sign_algorithm]; |
| |
| /* Check whether key header length is correct. */ |
| header_len = GetKernelHeaderLen(image); |
| if (header_len != image->header_len) { |
| debug("Header length mismatch. Got: %d, Expected: %d\n", |
| image->header_len, header_len); |
| Free(kernel_buf); |
| return NULL; |
| } |
| |
| /* Read pre-processed public half of the kernel signing key. */ |
| StatefulMemcpy(&st, &image->kernel_key_version, |
| FIELD_LEN(kernel_key_version)); |
| image->kernel_sign_key = (uint8_t*) Malloc(kernel_sign_key_len); |
| StatefulMemcpy(&st, image->kernel_sign_key, kernel_sign_key_len); |
| StatefulMemcpy(&st, image->header_checksum, FIELD_LEN(header_checksum)); |
| |
| /* Check whether the header checksum matches. */ |
| CalculateKernelHeaderChecksum(image, header_checksum); |
| if (SafeMemcmp(header_checksum, image->header_checksum, |
| FIELD_LEN(header_checksum))) { |
| debug("Invalid kernel header checksum!\n"); |
| Free(kernel_buf); |
| return NULL; |
| } |
| |
| /* Read key signature. */ |
| image->kernel_key_signature = (uint8_t*) Malloc(kernel_key_signature_len); |
| StatefulMemcpy(&st, image->kernel_key_signature, |
| kernel_key_signature_len); |
| |
| /* Read the kernel preamble. */ |
| StatefulMemcpy(&st, &image->kernel_version, FIELD_LEN(kernel_version)); |
| StatefulMemcpy(&st, &image->kernel_len, FIELD_LEN(kernel_len)); |
| StatefulMemcpy(&st, &image->bootloader_offset, FIELD_LEN(bootloader_offset)); |
| StatefulMemcpy(&st, &image->bootloader_size, FIELD_LEN(bootloader_size)); |
| StatefulMemcpy(&st, &image->padded_header_size, |
| FIELD_LEN(padded_header_size)); |
| |
| /* Read preamble and kernel signatures. */ |
| image->kernel_signature = (uint8_t*) Malloc(kernel_signature_len); |
| StatefulMemcpy(&st, image->kernel_signature, kernel_signature_len); |
| image->preamble_signature = (uint8_t*) Malloc(kernel_signature_len); |
| StatefulMemcpy(&st, image->preamble_signature, kernel_signature_len); |
| |
| /* Skip over the rest of the padded header, unless we're already past it. */ |
| on_disk_header_size = file_size - st.remaining_len; |
| if (image->padded_header_size > on_disk_header_size) { |
| on_disk_padding = image->padded_header_size - on_disk_header_size; |
| if (st.remaining_len < on_disk_padding) |
| st.overrun = -1; |
| st.remaining_buf += on_disk_padding; |
| st.remaining_len -= on_disk_padding; |
| } |
| |
| /* Read kernel image data. */ |
| image->kernel_data = (uint8_t*) Malloc(image->kernel_len); |
| StatefulMemcpy(&st, image->kernel_data, image->kernel_len); |
| |
| if(st.overrun || st.remaining_len != 0) { /* Overrun or underrun. */ |
| Free(kernel_buf); |
| return NULL; |
| } |
| Free(kernel_buf); |
| return image; |
| } |
| |
| int GetKernelHeaderLen(const KernelImage* image) { |
| return (FIELD_LEN(header_version) + FIELD_LEN(header_len) + |
| FIELD_LEN(firmware_sign_algorithm) + |
| FIELD_LEN(kernel_sign_algorithm) + FIELD_LEN(kernel_key_version) + |
| RSAProcessedKeySize(image->kernel_sign_algorithm) + |
| FIELD_LEN(header_checksum)); |
| } |
| |
| void CalculateKernelHeaderChecksum(const KernelImage* image, |
| uint8_t* header_checksum) { |
| uint8_t* checksum; |
| DigestContext ctx; |
| DigestInit(&ctx, SHA512_DIGEST_ALGORITHM); |
| DigestUpdate(&ctx, (uint8_t*) &image->header_version, |
| sizeof(image->header_version)); |
| DigestUpdate(&ctx, (uint8_t*) &image->header_len, |
| sizeof(image->header_len)); |
| DigestUpdate(&ctx, (uint8_t*) &image->firmware_sign_algorithm, |
| sizeof(image->firmware_sign_algorithm)); |
| DigestUpdate(&ctx, (uint8_t*) &image->kernel_sign_algorithm, |
| sizeof(image->kernel_sign_algorithm)); |
| DigestUpdate(&ctx, (uint8_t*) &image->kernel_key_version, |
| sizeof(image->kernel_key_version)); |
| DigestUpdate(&ctx, image->kernel_sign_key, |
| RSAProcessedKeySize(image->kernel_sign_algorithm)); |
| checksum = DigestFinal(&ctx); |
| Memcpy(header_checksum, checksum, FIELD_LEN(header_checksum)); |
| Free(checksum); |
| return; |
| } |
| |
| uint8_t* GetKernelHeaderBlob(const KernelImage* image) { |
| uint8_t* header_blob = NULL; |
| MemcpyState st; |
| |
| header_blob = (uint8_t*) Malloc(GetKernelHeaderLen(image)); |
| st.remaining_len = GetKernelHeaderLen(image); |
| st.remaining_buf = header_blob; |
| st.overrun = 0; |
| |
| StatefulMemcpy_r(&st, &image->header_version, FIELD_LEN(header_version)); |
| StatefulMemcpy_r(&st, &image->header_len, FIELD_LEN(header_len)); |
| StatefulMemcpy_r(&st, &image->firmware_sign_algorithm, |
| FIELD_LEN(firmware_sign_algorithm)); |
| StatefulMemcpy_r(&st, &image->kernel_sign_algorithm, |
| FIELD_LEN(kernel_sign_algorithm)); |
| StatefulMemcpy_r(&st, &image->kernel_key_version, |
| FIELD_LEN(kernel_key_version)); |
| StatefulMemcpy_r(&st, image->kernel_sign_key, |
| RSAProcessedKeySize(image->kernel_sign_algorithm)); |
| StatefulMemcpy_r(&st, &image->header_checksum, FIELD_LEN(header_checksum)); |
| |
| if (st.overrun || st.remaining_len != 0) { /* Underrun or Overrun. */ |
| Free(header_blob); |
| return NULL; |
| } |
| return header_blob; |
| } |
| |
| uint8_t* GetKernelPreambleBlob(const KernelImage* image) { |
| uint8_t* preamble_blob = NULL; |
| MemcpyState st; |
| |
| preamble_blob = (uint8_t*) Malloc( |
| GetKernelPreambleLen(image->kernel_sign_algorithm)); |
| st.remaining_len = GetKernelPreambleLen(image->kernel_sign_algorithm); |
| st.remaining_buf = preamble_blob; |
| st.overrun = 0; |
| |
| StatefulMemcpy_r(&st, &image->kernel_version, FIELD_LEN(kernel_version)); |
| StatefulMemcpy_r(&st, &image->kernel_len, FIELD_LEN(kernel_len)); |
| StatefulMemcpy_r(&st, &image->bootloader_offset, FIELD_LEN(bootloader_offset)); |
| StatefulMemcpy_r(&st, &image->bootloader_size, FIELD_LEN(bootloader_size)); |
| StatefulMemcpy_r(&st, &image->padded_header_size, |
| FIELD_LEN(padded_header_size)); |
| StatefulMemcpy_r(&st, image->kernel_signature, |
| siglen_map[image->kernel_sign_algorithm]); |
| |
| if (st.overrun || st.remaining_len != 0) { /* Overrun or Underrun. */ |
| Free(preamble_blob); |
| return NULL; |
| } |
| return preamble_blob; |
| } |
| |
| uint8_t* GetKernelBlob(const KernelImage* image, uint64_t* blob_len) { |
| int kernel_key_signature_len; |
| int kernel_signature_len; |
| uint8_t* kernel_blob = NULL; |
| uint8_t* header_blob = NULL; |
| MemcpyState st; |
| uint64_t on_disk_header_size; |
| uint64_t on_disk_padding = 0; |
| |
| if (!image) |
| return NULL; |
| kernel_key_signature_len = siglen_map[image->firmware_sign_algorithm]; |
| kernel_signature_len = siglen_map[image->kernel_sign_algorithm]; |
| on_disk_header_size = GetHeaderSizeOnDisk(image); |
| if (image->padded_header_size > on_disk_header_size) |
| on_disk_padding = image->padded_header_size - on_disk_header_size; |
| *blob_len = on_disk_header_size + on_disk_padding + image->kernel_len; |
| kernel_blob = (uint8_t*) Malloc(*blob_len); |
| st.remaining_len = *blob_len; |
| st.remaining_buf = kernel_blob; |
| st.overrun = 0; |
| header_blob = GetKernelHeaderBlob(image); |
| |
| StatefulMemcpy_r(&st, image->magic, FIELD_LEN(magic)); |
| StatefulMemcpy_r(&st, header_blob, GetKernelHeaderLen(image)); |
| StatefulMemcpy_r(&st, image->kernel_key_signature, kernel_key_signature_len); |
| /* Copy over kernel preamble blob (including signatures.) */ |
| StatefulMemcpy_r(&st, &image->kernel_version, FIELD_LEN(kernel_version)); |
| StatefulMemcpy_r(&st, &image->kernel_len, FIELD_LEN(kernel_len)); |
| StatefulMemcpy_r(&st, &image->bootloader_offset, |
| FIELD_LEN(bootloader_offset)); |
| StatefulMemcpy_r(&st, &image->bootloader_size, FIELD_LEN(bootloader_size)); |
| StatefulMemcpy_r(&st, &image->padded_header_size, |
| FIELD_LEN(padded_header_size)); |
| StatefulMemcpy_r(&st, image->kernel_signature, kernel_signature_len); |
| StatefulMemcpy_r(&st, image->preamble_signature, kernel_signature_len); |
| /* Copy a bunch of zeros to pad out the header */ |
| if (on_disk_padding) |
| StatefulMemset_r(&st, 0, on_disk_padding); |
| StatefulMemcpy_r(&st, image->kernel_data, image->kernel_len); |
| |
| Free(header_blob); |
| |
| if (st.overrun || st.remaining_len != 0) { /* Underrun or Overrun. */ |
| debug("GetKernelBlob() failed.\n"); |
| Free(kernel_blob); |
| return NULL; |
| } |
| return kernel_blob; |
| } |
| |
| int WriteKernelImage(const char* output_file, |
| const KernelImage* image, |
| int is_only_vblock) { |
| int fd; |
| int success = 1; |
| uint8_t* kernel_blob; |
| uint64_t blob_len; |
| |
| if (!image) |
| return 0; |
| if (-1 == (fd = creat(output_file, S_IRWXU))) { |
| debug("Couldn't open file for writing kernel image: %s\n", |
| output_file); |
| return 0; |
| } |
| kernel_blob = GetKernelBlob(image, &blob_len); |
| if (!kernel_blob) { |
| debug("Couldn't create kernel blob from KernelImage.\n"); |
| return 0; |
| } |
| if (!is_only_vblock) { |
| if (blob_len != write(fd, kernel_blob, blob_len)) { |
| debug("Couldn't write Kernel Image to file: %s\n", |
| output_file); |
| success = 0; |
| } |
| } else { |
| /* Exclude kernel_data. */ |
| int vblock_len = blob_len - (image->kernel_len); |
| if (vblock_len != write(fd, kernel_blob, vblock_len)) { |
| debug("Couldn't write Kernel Image Verification block to file: %s\n", |
| output_file); |
| success = 0; |
| } |
| } |
| Free(kernel_blob); |
| close(fd); |
| return success; |
| } |
| |
| void PrintKernelImage(const KernelImage* image) { |
| uint64_t header_size; |
| |
| if (!image) |
| return; |
| |
| header_size = GetHeaderSizeOnDisk(image); |
| if (image->padded_header_size > header_size) |
| header_size = image->padded_header_size; |
| |
| |
| /* Print header. */ |
| printf("Header Version = %d\n" |
| "Header Length = %d\n" |
| "Kernel Key Signature Algorithm = %s\n" |
| "Kernel Signature Algorithm = %s\n" |
| "Kernel Key Version = %d\n\n", |
| image->header_version, |
| image->header_len, |
| algo_strings[image->firmware_sign_algorithm], |
| algo_strings[image->kernel_sign_algorithm], |
| image->kernel_key_version); |
| /* TODO(gauravsh): Output hash and key signature here? */ |
| /* Print preamble. */ |
| printf("Kernel Version = %d\n" |
| "kernel Length = %" PRId64 " (0x%" PRIx64 ")\n" |
| "Bootloader Offset = %" PRId64 " (0x%" PRIx64 ")\n" |
| "Bootloader Size = %" PRId64 " (0x%" PRIx64 ")\n" |
| "Padded Header Size = %" PRId64 " (0x%" PRIx64 ")\n\n" |
| "Actual Header Size on disk = %" PRIu64 " (0x%" PRIx64 ")\n", |
| image->kernel_version, |
| image->kernel_len, image->kernel_len, |
| image->bootloader_offset, image->bootloader_offset, |
| image->bootloader_size, image->bootloader_size, |
| image->padded_header_size, image->padded_header_size, |
| header_size, header_size); |
| /* TODO(gauravsh): Output kernel signature here? */ |
| } |
| |
| |
| int VerifyKernelImage(const RSAPublicKey* firmware_key, |
| const KernelImage* image, |
| const int dev_mode) { |
| RSAPublicKey* kernel_sign_key = NULL; |
| uint8_t* header_digest = NULL; |
| uint8_t* preamble_digest = NULL; |
| uint8_t* kernel_digest = NULL; |
| int kernel_sign_key_size; |
| int kernel_signature_size; |
| int error_code = 0; |
| DigestContext ctx; |
| if (!image) |
| return VERIFY_KERNEL_INVALID_IMAGE; |
| |
| /* Verify kernel key signature on the key header if we |
| * are not in dev mode. |
| * |
| * TODO(gauravsh): Add additional sanity checks here for: |
| * 1) verifying the header length is correct. |
| * 2) header_checksum is correct. |
| */ |
| |
| if (image->firmware_sign_algorithm >= kNumAlgorithms) |
| return VERIFY_KERNEL_INVALID_ALGORITHM; |
| if (image->kernel_sign_algorithm >= kNumAlgorithms) |
| return VERIFY_KERNEL_INVALID_ALGORITHM; |
| |
| if (!dev_mode) { |
| DigestInit(&ctx, image->firmware_sign_algorithm); |
| DigestUpdate(&ctx, (uint8_t*) &image->header_version, |
| FIELD_LEN(header_version)); |
| DigestUpdate(&ctx, (uint8_t*) &image->header_len, |
| FIELD_LEN(header_len)); |
| DigestUpdate(&ctx, (uint8_t*) &image->firmware_sign_algorithm, |
| FIELD_LEN(firmware_sign_algorithm)); |
| DigestUpdate(&ctx, (uint8_t*) &image->kernel_sign_algorithm, |
| FIELD_LEN(kernel_sign_algorithm)); |
| DigestUpdate(&ctx, (uint8_t*) &image->kernel_key_version, |
| FIELD_LEN(kernel_key_version)); |
| DigestUpdate(&ctx, image->kernel_sign_key, |
| RSAProcessedKeySize(image->kernel_sign_algorithm)); |
| DigestUpdate(&ctx, image->header_checksum, |
| FIELD_LEN(header_checksum)); |
| header_digest = DigestFinal(&ctx); |
| if (!RSAVerify(firmware_key, image->kernel_key_signature, |
| siglen_map[image->firmware_sign_algorithm], |
| image->firmware_sign_algorithm, |
| header_digest)) { |
| debug("VerifyKernelImage(): Key signature check failed.\n"); |
| error_code = VERIFY_KERNEL_KEY_SIGNATURE_FAILED; |
| goto verify_failure; |
| } |
| } |
| |
| /* Get kernel signing key to verify the rest of the kernel. */ |
| kernel_sign_key_size = RSAProcessedKeySize(image->kernel_sign_algorithm); |
| kernel_sign_key = RSAPublicKeyFromBuf(image->kernel_sign_key, |
| kernel_sign_key_size); |
| kernel_signature_size = siglen_map[image->kernel_sign_algorithm]; |
| |
| /* Verify kernel preamble signature. */ |
| DigestInit(&ctx, image->kernel_sign_algorithm); |
| DigestUpdate(&ctx, (uint8_t*) &image->kernel_version, |
| FIELD_LEN(kernel_version)); |
| DigestUpdate(&ctx, (uint8_t*) &image->kernel_len, |
| FIELD_LEN(kernel_len)); |
| DigestUpdate(&ctx, (uint8_t*) &image->bootloader_offset, |
| FIELD_LEN(bootloader_offset)); |
| DigestUpdate(&ctx, (uint8_t*) &image->bootloader_size, |
| FIELD_LEN(bootloader_size)); |
| DigestUpdate(&ctx, (uint8_t*) &image->padded_header_size, |
| FIELD_LEN(padded_header_size)); |
| DigestUpdate(&ctx, (uint8_t*) image->kernel_signature, |
| kernel_signature_size); |
| preamble_digest = DigestFinal(&ctx); |
| if (!RSAVerify(kernel_sign_key, image->preamble_signature, |
| kernel_signature_size, image->kernel_sign_algorithm, |
| preamble_digest)) { |
| error_code = VERIFY_KERNEL_PREAMBLE_SIGNATURE_FAILED; |
| goto verify_failure; |
| } |
| |
| /* Verify kernel signature - kernel signature is computed on the contents |
| * of kernel_data. |
| * Association between the kernel_data and preamble is maintained by making |
| * the kernel signature a part of the preamble and verifying it as part |
| * of preamble signature checking. */ |
| |
| kernel_digest = DigestBuf(image->kernel_data, |
| image->kernel_len, |
| image->kernel_sign_algorithm); |
| if (!RSAVerify(kernel_sign_key, image->kernel_signature, |
| kernel_signature_size, image->kernel_sign_algorithm, |
| kernel_digest)) { |
| error_code = VERIFY_KERNEL_SIGNATURE_FAILED; |
| goto verify_failure; |
| } |
| |
| verify_failure: |
| RSAPublicKeyFree(kernel_sign_key); |
| Free(kernel_digest); |
| Free(preamble_digest); |
| Free(header_digest); |
| return error_code; |
| } |
| |
| const char* VerifyKernelErrorString(int error) { |
| return kVerifyKernelErrors[error]; |
| } |
| |
| int AddKernelKeySignature(KernelImage* image, const char* firmware_key_file) { |
| uint8_t* header_blob = NULL; |
| uint8_t* signature = NULL; |
| int signature_len = siglen_map[image->firmware_sign_algorithm]; |
| if (!image || !firmware_key_file) |
| return 0; |
| header_blob = GetKernelHeaderBlob(image); |
| if (!header_blob) |
| return 0; |
| if (!(signature = SignatureBuf(header_blob, |
| GetKernelHeaderLen(image), |
| firmware_key_file, |
| image->firmware_sign_algorithm))) { |
| Free(header_blob); |
| return 0; |
| } |
| image->kernel_key_signature = Malloc(signature_len); |
| Memcpy(image->kernel_key_signature, signature, signature_len); |
| Free(signature); |
| Free(header_blob); |
| return 1; |
| } |
| |
| int AddKernelSignature(KernelImage* image, |
| const char* kernel_signing_key_file) { |
| uint8_t* preamble_blob = NULL; |
| uint8_t* preamble_signature = NULL; |
| uint8_t* kernel_signature = NULL; |
| uint8_t* kernel_buf; |
| int algorithm = image->kernel_sign_algorithm; |
| int signature_len = siglen_map[algorithm]; |
| |
| /* Kernel signature must be calculated first as its used for computing the |
| * preamble signature. */ |
| kernel_buf = (uint8_t*) Malloc(image->kernel_len); |
| Memcpy(kernel_buf, image->kernel_data, image->kernel_len); |
| if (!(kernel_signature = SignatureBuf(kernel_buf, |
| image->kernel_len, |
| kernel_signing_key_file, |
| algorithm))) { |
| Free(preamble_blob); |
| Free(kernel_buf); |
| debug("Could not compute signature on the kernel.\n"); |
| return 0; |
| } |
| image->kernel_signature = (uint8_t*) Malloc(signature_len); |
| Memcpy(image->kernel_signature, kernel_signature, signature_len); |
| |
| |
| preamble_blob = GetKernelPreambleBlob(image); |
| if (!(preamble_signature = SignatureBuf(preamble_blob, |
| GetKernelPreambleLen(algorithm), |
| kernel_signing_key_file, |
| algorithm))) { |
| debug("Could not compute signature on the kernel preamble.\n"); |
| Free(preamble_blob); |
| return 0; |
| } |
| image->preamble_signature = (uint8_t*) Malloc(signature_len); |
| Memcpy(image->preamble_signature, preamble_signature, signature_len); |
| |
| Free(preamble_signature); |
| Free(preamble_blob); |
| Free(kernel_signature); |
| Free(kernel_buf); |
| return 1; |
| } |
| |
| void PrintKernelEntry(kernel_entry* entry) { |
| debug("Boot Priority = %d\n", entry->boot_priority); |
| debug("Boot Tries Remaining = %d\n", entry->boot_tries_remaining); |
| debug("Boot Success Flag = %d\n", entry->boot_success_flag); |
| } |
| |
| // Return the smallest integral multiple of [alignment] that is equal to or |
| // greater than [val]. Used to determine the number of |
| // pages/sectors/blocks/whatever needed to contain [val] items/bytes/etc. |
| static uint64_t roundup(uint64_t val, uint64_t alignment) { |
| uint64_t rem = val % alignment; |
| if ( rem ) |
| return val + (alignment - rem); |
| return val; |
| } |
| |
| // Match regexp /\b--\b/ to delimit the start of the kernel commandline. If we |
| // don't find one, we'll use the whole thing. |
| static unsigned int find_cmdline_start(char *input, unsigned int max_len) { |
| int start = 0; |
| int i; |
| for(i = 0; i < max_len-1 && input[i]; i++) { |
| if (input[i] == '-' && input[i+1] == '-') { // found a "--" |
| if ((i == 0 || input[i-1] == ' ') && // nothing before it |
| (i+2 >= max_len || input[i+2] == ' ')) { // nothing after it |
| start = i+2; // note: hope there's a trailing '\0' |
| break; |
| } |
| } |
| } |
| while(input[start] == ' ') // skip leading spaces |
| start++; |
| |
| return start; |
| } |
| |
| uint8_t* GenerateKernelBlob(const char* kernel_file, |
| const char* config_file, |
| const char* bootloader_file, |
| uint64_t* ret_blob_len, |
| uint64_t* ret_bootloader_offset, |
| uint64_t* ret_bootloader_size) { |
| uint8_t* kernel_buf; |
| uint8_t* config_buf; |
| uint8_t* bootloader_buf; |
| uint8_t* blob = 0; |
| uint64_t kernel_size; |
| uint64_t config_size; |
| uint64_t bootloader_size; |
| uint64_t blob_size; |
| uint64_t kernel32_start = 0; |
| uint64_t kernel32_size = 0; |
| uint64_t bootloader_mem_start; |
| uint64_t bootloader_mem_size; |
| uint64_t now; |
| struct linux_kernel_header *lh = 0; |
| struct linux_kernel_params *params = 0; |
| uint32_t cmdline_addr; |
| uint64_t i; |
| |
| // Read the input files. |
| kernel_buf = BufferFromFile(kernel_file, &kernel_size); |
| if (!kernel_buf) |
| goto done0; |
| |
| config_buf = BufferFromFile(config_file, &config_size); |
| if (!config_buf) |
| goto done1; |
| if (config_size < CROS_CONFIG_SIZE) // need room for trailing '\0' |
| goto done1; |
| |
| // Replace any newlines with spaces in the config file. |
| for (i=0; i < config_size; i++) |
| if (config_buf[i] == '\n') |
| config_buf[i] = ' '; |
| |
| bootloader_buf = BufferFromFile(bootloader_file, &bootloader_size); |
| if (!bootloader_buf) |
| goto done2; |
| |
| // The first part of vmlinuz is a header, followed by a real-mode boot stub. |
| // We only want the 32-bit part. |
| if (kernel_size) { |
| lh = (struct linux_kernel_header *)kernel_buf; |
| kernel32_start = (lh->setup_sects+1) << 9; |
| kernel32_size = kernel_size - kernel32_start; |
| } |
| |
| // Allocate and zero the blob we need. |
| blob_size = roundup(kernel32_size, CROS_ALIGN) + |
| CROS_CONFIG_SIZE + |
| CROS_PARAMS_SIZE + |
| roundup(bootloader_size, CROS_ALIGN); |
| blob = (uint8_t *)Malloc(blob_size); |
| if (!blob) |
| goto done3; |
| Memset(blob, 0, blob_size); |
| now = 0; |
| |
| // Copy the 32-bit kernel. |
| if (kernel32_size) |
| Memcpy(blob + now, kernel_buf + kernel32_start, kernel32_size); |
| now += roundup(now + kernel32_size, CROS_ALIGN); |
| |
| // Find the load address of the commandline. We'll need it later. |
| cmdline_addr = CROS_32BIT_ENTRY_ADDR + now |
| + find_cmdline_start((char *)config_buf, config_size); |
| |
| // Copy the config. |
| if (config_size) |
| Memcpy(blob + now, config_buf, config_size); |
| now += CROS_CONFIG_SIZE; |
| |
| // The zeropage data is next. Overlay the linux_kernel_header onto it, and |
| // tweak a few fields. |
| params = (struct linux_kernel_params *)(blob + now); |
| |
| if (kernel_size) |
| Memcpy(&(params->setup_sects), &(lh->setup_sects), |
| sizeof(*lh) - offsetof(struct linux_kernel_header, setup_sects)); |
| params->boot_flag = 0; |
| params->ramdisk_image = 0; // we don't support initrd |
| params->ramdisk_size = 0; |
| params->type_of_loader = 0xff; |
| params->cmd_line_ptr = cmdline_addr; |
| now += CROS_PARAMS_SIZE; |
| |
| // Finally, append the bootloader. Remember where it will load in memory, too. |
| bootloader_mem_start = CROS_32BIT_ENTRY_ADDR + now; |
| bootloader_mem_size = roundup(bootloader_size, CROS_ALIGN); |
| if (bootloader_size) |
| Memcpy(blob + now, bootloader_buf, bootloader_size); |
| now += bootloader_mem_size; |
| |
| // Pass back some info. |
| if (ret_blob_len) |
| *ret_blob_len = blob_size; |
| if (ret_bootloader_offset) |
| *ret_bootloader_offset = bootloader_mem_start; |
| if (ret_bootloader_size) |
| *ret_bootloader_size = bootloader_mem_size; |
| |
| // Clean up and return the blob. |
| done3: |
| Free(bootloader_buf); |
| done2: |
| Free(config_buf); |
| done1: |
| Free(kernel_buf); |
| done0: |
| return blob; |
| } |