Permalink
Browse files

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>
  • Loading branch information...
1 parent 48ac0ed commit e65d07ee00fa1bfa9e59aee139779af01e5bb432 @wesolows wesolows committed with richlowe Dec 18, 2012
View
9 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)
View
1 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))
View
6 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 $@ $<
@@ -456,6 +459,9 @@ $(DBOOT_LINTS_DIR)/%.ln: $(UTSBASE)/i86pc/dboot/%.c
$(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))
View
14 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 */
View
132 usr/src/uts/i86pc/dboot/dboot_startkern.c
@@ -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 @@ extern int have_cpuid(void);
#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 @@ 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.
*/
@@ -813,7 +941,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);
}
@@ -831,6 +959,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.

0 comments on commit e65d07e

Please sign in to comment.