Mercurial > illumos > illumos-gate
changeset 13902:953a602a9b70
3364 dboot should check boot archive integrity
Reviewed by: Hans Rosenfeld <hans.rosenfeld@nexenta.com>
Reviewed by: Dan McDonald <danmcd@nexenta.com>
Reviewed by: Richard Lowe <richlowe@richlowe.net>
Reviewed by: Garrett D'Amore <garrett@damore.org>
Approved by: Richard Lowe <richlowe@richlowe.net>
author | Keith Wesolowski <keith.wesolowski@joyent.com> |
---|---|
date | Tue, 18 Dec 2012 13:51:01 -0500 |
parents | 8de47e31a5b1 |
children | 93cd9d7e48de |
files | usr/src/common/crypto/sha1/sha1.c usr/src/uts/i86pc/Makefile.files usr/src/uts/i86pc/Makefile.rules usr/src/uts/i86pc/dboot/dboot_asm.s usr/src/uts/i86pc/dboot/dboot_startkern.c |
diffstat | 5 files changed, 157 insertions(+), 5 deletions(-) [+] |
line wrap: on
line diff
--- a/usr/src/common/crypto/sha1/sha1.c Sun Dec 09 20:12:10 2012 -0500 +++ b/usr/src/common/crypto/sha1/sha1.c Tue Dec 18 13:51:01 2012 -0500 @@ -32,13 +32,13 @@ * and appreciated. */ -#ifndef _KERNEL +#if !defined(_KERNEL) && !defined(_BOOT) #include <stdint.h> #include <strings.h> #include <stdlib.h> #include <errno.h> #include <sys/systeminfo.h> -#endif /* !_KERNEL */ +#endif /* !_KERNEL && !_BOOT */ #include <sys/types.h> #include <sys/param.h> @@ -52,6 +52,11 @@ #define HAVE_HTONL #endif +#ifdef _BOOT +#define bcopy(_s, _d, _l) ((void) memcpy((_d), (_s), (_l))) +#define bzero(_m, _l) ((void) memset((_m), 0, (_l))) +#endif + static void Encode(uint8_t *, const uint32_t *, size_t); #if defined(__sparc)
--- a/usr/src/uts/i86pc/Makefile.files Sun Dec 09 20:12:10 2012 -0500 +++ b/usr/src/uts/i86pc/Makefile.files Tue Dec 18 13:51:01 2012 -0500 @@ -173,6 +173,7 @@ memcpy.o \ memset.o \ muldiv.o \ + sha1.o \ string.o \ $(BOOT_DRIVER_OBJS) \ $(DBOOT_OBJS_$(CLASS))
--- a/usr/src/uts/i86pc/Makefile.rules Sun Dec 09 20:12:10 2012 -0500 +++ b/usr/src/uts/i86pc/Makefile.rules Tue Dec 18 13:51:01 2012 -0500 @@ -242,6 +242,9 @@ $(DBOOT_OBJS_DIR)/%.o: $(UTSBASE)/intel/ia32/%.s $(DBOOT_AS) -P -D_ASM $(DBOOT_DEFS) $(DBOOT_AS_INCL) -o $@ $< +$(DBOOT_OBJS_DIR)/%.o: $(COMMONBASE)/crypto/sha1/%.c + $(i386_CC) $(CERRWARN) -O $(DBOOT_DEFS) $(DBOOT_CC_INCL) -c -o $@ $< + $(DBOOT_OBJS_DIR)/%.o: $(COMMONBASE)/util/%.c $(i386_CC) $(DBOOT_FLAGS) -O $(DBOOT_DEFS) $(DBOOT_CC_INCL) -c -o $@ $< @@ -456,6 +459,9 @@ $(DBOOT_LINTS_DIR)/%.ln: $(UTSBASE)/i86pc/dboot/%.s @($(LHEAD) $(DBOOT_LINT) $(DBOOT_LOCAL_LINTFLAGS) $< $(LTAIL)) +$(DBOOT_LINTS_DIR)/%.ln: $(COMMONBASE)/crypto/sha1/%.c + @($(LHEAD) $(DBOOT_LINT) $(DBOOT_LOCAL_LINTFLAGS) $< $(LTAIL)) + $(DBOOT_LINTS_DIR)/%.ln: $(UTSBASE)/i86pc/boot/%.c @($(LHEAD) $(DBOOT_LINT) $(DBOOT_LOCAL_LINTFLAGS) $< $(LTAIL))
--- a/usr/src/uts/i86pc/dboot/dboot_asm.s Sun Dec 09 20:12:10 2012 -0500 +++ b/usr/src/uts/i86pc/dboot/dboot_asm.s Tue Dec 18 13:51:01 2012 -0500 @@ -24,8 +24,6 @@ * Use is subject to license terms. */ -#pragma ident "%Z%%M% %I% %E% SMI" - #include <sys/asm_linkage.h> #include <sys/asm_misc.h> @@ -87,6 +85,12 @@ ret SET_SIZE(inb) + ENTRY(htonl) + movl %edi, %eax + bswap %eax + ret + SET_SIZE(htonl) + #elif defined(__i386) .code32 @@ -128,6 +132,12 @@ ret SET_SIZE(inb) + ENTRY(htonl) + movl 4(%esp), %eax + bswap %eax + ret + SET_SIZE(htonl) + #endif /* __i386 */ #endif /* __lint */
--- a/usr/src/uts/i86pc/dboot/dboot_startkern.c Sun Dec 09 20:12:10 2012 -0500 +++ b/usr/src/uts/i86pc/dboot/dboot_startkern.c Tue Dec 18 13:51:01 2012 -0500 @@ -22,6 +22,8 @@ /* * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. + * + * Copyright 2012 Joyent, Inc. All rights reserved. */ @@ -31,6 +33,7 @@ #include <sys/systm.h> #include <sys/mach_mmu.h> #include <sys/multiboot.h> +#include <sys/sha1.h> #if defined(__xpv) @@ -55,6 +58,8 @@ #include "dboot_xboot.h" #include "dboot_elfload.h" +#define SHA1_ASCII_LENGTH (SHA1_DIGEST_LENGTH * 2) + /* * This file contains code that runs to transition us from either a multiboot * compliant loader (32 bit non-paging) or a XPV domain loader to @@ -767,6 +772,129 @@ #else /* !__xpv */ +static uint8_t +dboot_a2h(char v) +{ + if (v >= 'a') + return (v - 'a' + 0xa); + else if (v >= 'A') + return (v - 'A' + 0xa); + else if (v >= '0') + return (v - '0'); + else + dboot_panic("bad ASCII hex character %c\n", v); + + return (0); +} + +static void +digest_a2h(const char *ascii, uint8_t *digest) +{ + unsigned int i; + + for (i = 0; i < SHA1_DIGEST_LENGTH; i++) { + digest[i] = dboot_a2h(ascii[i * 2]) << 4; + digest[i] |= dboot_a2h(ascii[i * 2 + 1]); + } +} + +/* + * Generate a SHA-1 hash of the first len bytes of image, and compare it with + * the ASCII-format hash found in the 40-byte buffer at ascii. If they + * match, return 0, otherwise -1. This works only for images smaller than + * 4 GB, which should not be a problem. + */ +static int +check_image_hash(const char *ascii, const void *image, size_t len) +{ + SHA1_CTX ctx; + uint8_t digest[SHA1_DIGEST_LENGTH]; + uint8_t baseline[SHA1_DIGEST_LENGTH]; + unsigned int i; + + digest_a2h(ascii, baseline); + + SHA1Init(&ctx); + SHA1Update(&ctx, image, len); + SHA1Final(digest, &ctx); + + for (i = 0; i < SHA1_DIGEST_LENGTH; i++) { + if (digest[i] != baseline[i]) + return (-1); + } + + return (0); +} + +static void +check_images(void) +{ + int i; + char *hashes; + mb_module_t *mod, *hashmod; + char *hash; + char displayhash[SHA1_ASCII_LENGTH + 1]; + size_t hashlen; + size_t len; + + /* + * A brief note on lengths and sizes: GRUB, for reasons unknown, passes + * the address of the last valid byte in a module plus 1 as mod_end. + * This is of course a bug; the multiboot specification simply states + * that mod_start and mod_end "contain the start and end addresses of + * the boot module itself" which is pretty obviously not what GRUB is + * doing. However, fixing it requires that not only this code be + * changed but also that other code consuming this value and values + * derived from it be fixed, and that the kernel and GRUB must either + * both have the bug or neither. While there are a lot of combinations + * that will work, there are also some that won't, so for simplicity + * we'll just cope with the bug. That means we won't actually hash the + * byte at mod_end, and we will expect that mod_end for the hash file + * itself is one greater than some multiple of 41 (40 bytes of ASCII + * hash plus a newline for each module). + */ + + if (mb_info->mods_count > 1) { + mod = (mb_module_t *)mb_info->mods_addr; + hashmod = mod + (mb_info->mods_count - 1); + hashes = (char *)hashmod->mod_start; + hashlen = (size_t)(hashmod->mod_end - hashmod->mod_start); + hash = hashes; + if (prom_debug) { + dboot_printf("Hash module found at %lx size %lx\n", + (ulong_t)hashes, (ulong_t)hashlen); + } + } else { + DBG_MSG("Skipping hash check; no hash module found.\n"); + return; + } + + for (mod = (mb_module_t *)(mb_info->mods_addr), i = 0; + i < mb_info->mods_count - 1; ++mod, ++i) { + if ((hash - hashes) + SHA1_ASCII_LENGTH + 1 > hashlen) { + dboot_printf("Short hash module of length 0x%lx bytes; " + "skipping hash checks\n", (ulong_t)hashlen); + break; + } + + (void) memcpy(displayhash, hash, SHA1_ASCII_LENGTH); + displayhash[SHA1_ASCII_LENGTH] = '\0'; + if (prom_debug) { + dboot_printf("Checking hash for module %d [%s]: ", + i, displayhash); + } + + len = mod->mod_end - mod->mod_start; /* see above */ + if (check_image_hash(hash, (void *)mod->mod_start, len) != 0) { + dboot_panic("SHA-1 hash mismatch on %s; expected %s\n", + (char *)mod->mod_name, displayhash); + } else { + DBG_MSG("OK\n"); + } + hash += SHA1_ASCII_LENGTH + 1; + } +} + /* * During memory allocation, find the highest address not used yet. */ @@ -813,7 +941,7 @@ i < mb_info->mods_count; ++mod, ++i) { if (prom_debug) { - dboot_printf("\tmodule #%d: %s at: 0x%lx, len 0x%lx\n", + dboot_printf("\tmodule #%d: %s at: 0x%lx, end 0x%lx\n", i, (char *)(mod->mod_name), (ulong_t)mod->mod_start, (ulong_t)mod->mod_end); } @@ -831,6 +959,8 @@ bi->bi_module_cnt = mb_info->mods_count; DBG(bi->bi_module_cnt); + check_images(); + /* * Walk through the memory map from multiboot and build our memlist * structures. Note these will have native format pointers.