summaryrefslogtreecommitdiff
path: root/usr/src
diff options
context:
space:
mode:
authorKeith M Wesolowski <wesolows@foobazco.org>2012-11-21 01:19:52 +0000
committerKeith M Wesolowski <wesolows@foobazco.org>2012-11-22 01:04:26 +0000
commit07de33ff39abfed09baa8bd74995810e038e675b (patch)
treeebefaad9c9bae322a278dbd9d7540a975c8a5564 /usr/src
parentc9b5e96c15bb6567575976b216ad5ab119519ec6 (diff)
downloadillumos-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.c9
-rw-r--r--usr/src/uts/i86pc/Makefile.files1
-rw-r--r--usr/src/uts/i86pc/Makefile.rules3
-rw-r--r--usr/src/uts/i86pc/dboot/dboot_asm.s14
-rw-r--r--usr/src/uts/i86pc/dboot/dboot_startkern.c132
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;