diff options
| author | Keith M Wesolowski <wesolows@foobazco.org> | 2012-11-21 01:19:52 +0000 |
|---|---|---|
| committer | Keith M Wesolowski <wesolows@foobazco.org> | 2012-11-22 01:04:26 +0000 |
| commit | 07de33ff39abfed09baa8bd74995810e038e675b (patch) | |
| tree | ebefaad9c9bae322a278dbd9d7540a975c8a5564 /usr/src | |
| parent | c9b5e96c15bb6567575976b216ad5ab119519ec6 (diff) | |
| download | illumos-joyent-07de33ff39abfed09baa8bd74995810e038e675b.tar.gz | |
OS-1685 dboot should check boot archive integrity
Diffstat (limited to 'usr/src')
| -rw-r--r-- | usr/src/common/crypto/sha1/sha1.c | 9 | ||||
| -rw-r--r-- | usr/src/uts/i86pc/Makefile.files | 1 | ||||
| -rw-r--r-- | usr/src/uts/i86pc/Makefile.rules | 3 | ||||
| -rw-r--r-- | usr/src/uts/i86pc/dboot/dboot_asm.s | 14 | ||||
| -rw-r--r-- | usr/src/uts/i86pc/dboot/dboot_startkern.c | 132 |
5 files changed, 153 insertions, 6 deletions
diff --git a/usr/src/common/crypto/sha1/sha1.c b/usr/src/common/crypto/sha1/sha1.c index 67dbd9979b..4a64eb2b8c 100644 --- a/usr/src/common/crypto/sha1/sha1.c +++ b/usr/src/common/crypto/sha1/sha1.c @@ -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) diff --git a/usr/src/uts/i86pc/Makefile.files b/usr/src/uts/i86pc/Makefile.files index dfe6ef4d8d..0a917008ca 100644 --- a/usr/src/uts/i86pc/Makefile.files +++ b/usr/src/uts/i86pc/Makefile.files @@ -173,6 +173,7 @@ DBOOT_OBJS += \ memcpy.o \ memset.o \ muldiv.o \ + sha1.o \ string.o \ $(BOOT_DRIVER_OBJS) \ $(DBOOT_OBJS_$(CLASS)) diff --git a/usr/src/uts/i86pc/Makefile.rules b/usr/src/uts/i86pc/Makefile.rules index 1942322231..91121e6777 100644 --- a/usr/src/uts/i86pc/Makefile.rules +++ b/usr/src/uts/i86pc/Makefile.rules @@ -242,6 +242,9 @@ $(DBOOT_OBJS_DIR)/%.o: $(UTSBASE)/i86pc/dboot/%.c $(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 $@ $< diff --git a/usr/src/uts/i86pc/dboot/dboot_asm.s b/usr/src/uts/i86pc/dboot/dboot_asm.s index 7be6eaadf0..47e525708f 100644 --- a/usr/src/uts/i86pc/dboot/dboot_asm.s +++ b/usr/src/uts/i86pc/dboot/dboot_asm.s @@ -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 @@ inb(int port) ret SET_SIZE(inb) + ENTRY(htonl) + movl %edi, %eax + bswap %eax + ret + SET_SIZE(htonl) + #elif defined(__i386) .code32 @@ -128,6 +132,12 @@ inb(int port) ret SET_SIZE(inb) + ENTRY(htonl) + movl 4(%esp), %eax + bswap %eax + ret + SET_SIZE(htonl) + #endif /* __i386 */ #endif /* __lint */ diff --git a/usr/src/uts/i86pc/dboot/dboot_startkern.c b/usr/src/uts/i86pc/dboot/dboot_startkern.c index 56c3857f55..a51f12e9cc 100644 --- a/usr/src/uts/i86pc/dboot/dboot_startkern.c +++ b/usr/src/uts/i86pc/dboot/dboot_startkern.c @@ -33,6 +33,7 @@ #include <sys/systm.h> #include <sys/mach_mmu.h> #include <sys/multiboot.h> +#include <sys/sha1.h> #if defined(__xpv) @@ -57,6 +58,8 @@ extern int have_cpuid(void); #include "dboot_xboot.h" #include "dboot_elfload.h" +#define SHA1_ASCII_LENGTH (SHA1_DIGEST_LENGTH * 2) + /* * Region of memory that may be corrupted by external actors. This can go away * once the firmware bug RICHMOND-16 is fixed and all systems with the bug are @@ -778,6 +781,129 @@ init_mem_alloc(void) #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. */ @@ -824,7 +950,7 @@ init_mem_alloc(void) 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); } @@ -842,6 +968,8 @@ init_mem_alloc(void) 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. @@ -903,7 +1031,7 @@ init_mem_alloc(void) end > CORRUPT_REGION_START) { memlists[memlists_used].addr = start; memlists[memlists_used].size = - CORRUPT_REGION_START - start; + CORRUPT_REGION_START - start; ++memlists_used; if (end > CORRUPT_REGION_END) start = CORRUPT_REGION_END; |
