Add support for passing saved entropy (random seed file) to the kernel

from the bootloader.  This can fix the problem of poor quality keys
for other kernel modules which call arc4random() early in kernel startup
(NFS startup, in particular, causes this).

We continue to rely on the etc/rc.d/random_seed script to save entropy
to the seed file at shutdown and erase the seed file at startup.

Boot loader support implemented only for i386 and amd64 ports for now but
it should be easy for other ports to do the same or similar.
This commit is contained in:
tls 2011-11-28 07:56:53 +00:00
parent 1dafd61846
commit a031ce7187
8 changed files with 146 additions and 35 deletions

View File

@ -1,4 +1,4 @@
.\" $NetBSD: boot.cfg.5,v 1.22 2011/05/26 04:25:26 uebayasi Exp $
.\" $NetBSD: boot.cfg.5,v 1.23 2011/11/28 07:56:53 tls Exp $
.\"
.\" Copyright (c) 2007 Stephen Borrill
.\" All rights reserved.
@ -147,6 +147,15 @@ time limit for the user to choose an option.
Passes a
.Xr userconf 4
command to the kernel at boot time .
.It Sy rndseed
Takes the path to a random-seed file as written by the -S flag to
.Xr rndctl 8
as an argument. This file is used to seed the
kernel entropy pool
.Xr rnd 9
very early in kernel startup, so that high quality randomness is
available to all kernel modules. This argument should be supplied
before any "load" commands that may load executable modules.
.El
.Sh EXAMPLES
Here is an example

View File

@ -1,4 +1,4 @@
/* $NetBSD: boot2.c,v 1.55 2011/06/23 12:07:00 mrg Exp $ */
/* $NetBSD: boot2.c,v 1.56 2011/11/28 07:56:54 tls Exp $ */
/*-
* Copyright (c) 2008, 2009 The NetBSD Foundation, Inc.
@ -136,6 +136,7 @@ const struct bootblk_command commands[] = {
{ "multiboot", command_multiboot },
{ "vesa", command_vesa },
{ "splash", splash_add },
{ "rndseed", rnd_add },
{ "userconf", userconf_add },
{ NULL, NULL },
};
@ -397,6 +398,7 @@ command_help(char *arg)
"load {path_to_module}\n"
"multiboot [xdNx:][filename] [<args>]\n"
"userconf {command}\n"
"rndseed {path_to_rndseed_file}\n"
"help|?\n"
"quit\n");
}

View File

@ -1,4 +1,4 @@
/* $NetBSD: bootmod.h,v 1.4 2011/02/06 23:16:05 jmcneill Exp $ */
/* $NetBSD: bootmod.h,v 1.5 2011/11/28 07:56:54 tls Exp $ */
/*-
* Copyright (c) 2008 Jared D. McNeill <jmcneill@invisible.ca>
@ -35,6 +35,7 @@ typedef struct boot_module {
uint8_t bm_type;
#define BM_TYPE_KMOD 0x00
#define BM_TYPE_IMAGE 0x01
#define BM_TYPE_RND 0x02
struct boot_module *bm_next;
} boot_module_t;

View File

@ -1,4 +1,4 @@
/* $NetBSD: exec.c,v 1.48 2011/07/17 20:54:41 joerg Exp $ */
/* $NetBSD: exec.c,v 1.49 2011/11/28 07:56:54 tls Exp $ */
/*-
* Copyright (c) 2008, 2009 The NetBSD Foundation, Inc.
@ -171,6 +171,12 @@ splash_add(char *name)
return module_add_common(name, BM_TYPE_IMAGE);
}
void
rnd_add(char *name)
{
return module_add_common(name, BM_TYPE_RND);
}
static void
module_add_common(char *name, uint8_t type)
{
@ -579,8 +585,19 @@ module_init(const char *kernel_path)
strncpy(bi->path, bm->bm_path, sizeof(bi->path) - 1);
bi->base = image_end;
bi->len = len;
bi->type = bm->bm_type == BM_TYPE_KMOD ?
BI_MODULE_ELF : BI_MODULE_IMAGE;
switch (bm->bm_type) {
case BM_TYPE_KMOD:
bi->type = BI_MODULE_ELF;
break;
case BM_TYPE_IMAGE:
bi->type = BI_MODULE_IMAGE;
break;
case BM_TYPE_RND:
default:
/* safest -- rnd checks the sha1 */
bi->type = BI_MODULE_RND;
break;
}
if ((howto & AB_SILENT) == 0)
printf(" \n");
}

View File

@ -1,4 +1,4 @@
/* $NetBSD: libi386.h,v 1.37 2011/06/16 13:27:59 joerg Exp $ */
/* $NetBSD: libi386.h,v 1.38 2011/11/28 07:56:54 tls Exp $ */
/*
* Copyright (c) 1996
@ -138,6 +138,7 @@ extern int doserrno; /* in dos_file.S */
void module_add(char *);
void splash_add(char *);
void rnd_add(char *);
void userconf_add(char *);
struct btinfo_framebuffer;

View File

@ -1,4 +1,4 @@
/* $NetBSD: bootinfo.h,v 1.18 2011/05/26 04:25:28 uebayasi Exp $ */
/* $NetBSD: bootinfo.h,v 1.19 2011/11/28 07:56:54 tls Exp $ */
/*
* Copyright (c) 1997
@ -175,6 +175,7 @@ struct bi_modulelist_entry {
#define BI_MODULE_NONE 0x00
#define BI_MODULE_ELF 0x01
#define BI_MODULE_IMAGE 0x02
#define BI_MODULE_RND 0x03
struct btinfo_modulelist {
struct btinfo_common common;

View File

@ -1,4 +1,4 @@
/* $NetBSD: x86_machdep.c,v 1.56 2011/08/13 21:04:05 christos Exp $ */
/* $NetBSD: x86_machdep.c,v 1.57 2011/11/28 07:56:54 tls Exp $ */
/*-
* Copyright (c) 2002, 2006, 2007 YAMAMOTO Takashi,
@ -31,7 +31,7 @@
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: x86_machdep.c,v 1.56 2011/08/13 21:04:05 christos Exp $");
__KERNEL_RCSID(0, "$NetBSD: x86_machdep.c,v 1.57 2011/11/28 07:56:54 tls Exp $");
#include "opt_modular.h"
#include "opt_physmem.h"
@ -50,6 +50,7 @@ __KERNEL_RCSID(0, "$NetBSD: x86_machdep.c,v 1.56 2011/08/13 21:04:05 christos Ex
#include <sys/module.h>
#include <sys/sysctl.h>
#include <sys/extent.h>
#include <sys/rnd.h>
#include <x86/cpuvar.h>
#include <x86/cputypes.h>
@ -173,6 +174,14 @@ module_init_md(void)
(void *)((uintptr_t)bi->base + KERNBASE), bi->len);
#endif
break;
case BI_MODULE_RND:
aprint_debug("Random seed data path=%s len=%d pa=%x\n",
bi->path, bi->len, bi->base);
KASSERT(trunc_page(bi->base) == bi->base);
rnd_seed(
(void *)((uintptr_t)bi->base + KERNBASE),
bi->len);
break;
default:
aprint_debug("Skipping non-ELF module\n");
break;

View File

@ -1,4 +1,4 @@
/* $NetBSD: rnd.c,v 1.86 2011/11/23 10:47:48 tls Exp $ */
/* $NetBSD: rnd.c,v 1.87 2011/11/28 07:56:54 tls Exp $ */
/*-
* Copyright (c) 1997-2011 The NetBSD Foundation, Inc.
@ -32,7 +32,7 @@
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: rnd.c,v 1.86 2011/11/23 10:47:48 tls Exp $");
__KERNEL_RCSID(0, "$NetBSD: rnd.c,v 1.87 2011/11/28 07:56:54 tls Exp $");
#include <sys/param.h>
#include <sys/ioctl.h>
@ -184,6 +184,7 @@ static int rnd_tested = 0;
LIST_HEAD(, krndsource) rnd_sources;
static rndsave_t *boot_rsp;
/*
* Generate a 32-bit counter. This should be more machine dependent,
* using cycle counters and the like when possible.
@ -406,6 +407,23 @@ rnd_init(void)
printf("rnd: initialised (%u)%s", RND_POOLBITS,
c ? " with counter\n" : "\n");
#endif
if (boot_rsp != NULL) {
mutex_spin_enter(&rndpool_mtx);
rndpool_add_data(&rnd_pool, boot_rsp->data,
sizeof(boot_rsp->data),
MIN(boot_rsp->entropy,
RND_POOLBITS / 2));
if (rndpool_get_entropy_count(&rnd_pool) >
RND_ENTROPY_THRESHOLD * 8) {
rnd_have_entropy = 1;
}
mutex_spin_exit(&rndpool_mtx);
#ifdef RND_VERBOSE
printf("rnd: seeded with %d bits\n",
MIN(boot_rsp->entropy, RND_POOLBITS / 2));
#endif
memset(boot_rsp, 0, sizeof(*boot_rsp));
}
}
int
@ -748,32 +766,41 @@ rndioctl(dev_t dev, u_long cmd, void *addr, int flag,
break;
case RNDADDDATA:
rnddata = (rnddata_t *)addr;
/*
* Don't seed twice if our bootloader has
* seed loading support.
*/
if (!boot_rsp) {
rnddata = (rnddata_t *)addr;
if (rnddata->len > sizeof(rnddata->data))
return EINVAL;
if (rnddata->len > sizeof(rnddata->data))
return EINVAL;
if (estimate_ok) {
/*
* Do not accept absurd entropy estimates, and
* do not flood the pool with entropy such that
* new samples are discarded henceforth.
*/
estimate = MIN((rnddata->len * NBBY) / 2,
MIN(rnddata->entropy,
RND_POOLWORDS * sizeof(int) *
NBBY / 2));
} else {
estimate = 0;
if (estimate_ok) {
/*
* Do not accept absurd entropy estimates, and
* do not flood the pool with entropy such that
* new samples are discarded henceforth.
*/
estimate = MIN((rnddata->len * NBBY) / 2,
MIN(rnddata->entropy,
RND_POOLBITS / 2));
} else {
estimate = 0;
}
mutex_spin_enter(&rndpool_mtx);
rndpool_add_data(&rnd_pool, rnddata->data,
rnddata->len, estimate);
mutex_spin_exit(&rndpool_mtx);
rnd_wakeup_readers();
}
mutex_spin_enter(&rndpool_mtx);
rndpool_add_data(&rnd_pool, rnddata->data, rnddata->len,
estimate);
mutex_spin_exit(&rndpool_mtx);
rnd_wakeup_readers();
#ifdef RND_VERBOSE
else {
printf("rnd: already seeded by boot loader\n");
}
#endif
break;
default:
@ -1441,3 +1468,47 @@ rndsink_detach(rndsink_t *rs)
}
mutex_spin_exit(&rndpool_mtx);
}
void
rnd_seed(void *base, size_t len)
{
SHA1_CTX s;
uint8_t digest[SHA1_DIGEST_LENGTH];
if (len != sizeof(*boot_rsp)) {
aprint_error("rnd: bad seed length %d\n", (int)len);
return;
}
boot_rsp = (rndsave_t *)base;
SHA1Init(&s);
SHA1Update(&s, (uint8_t *)&boot_rsp->entropy,
sizeof(boot_rsp->entropy));
SHA1Update(&s, boot_rsp->data, sizeof(boot_rsp->data));
SHA1Final(digest, &s);
if (memcmp(digest, boot_rsp->digest, sizeof(digest))) {
aprint_error("rnd: bad seed checksum\n");
return;
}
/*
* It's not really well-defined whether bootloader-supplied
* modules run before or after rnd_init(). Handle both cases.
*/
if (rnd_ready) {
#ifdef RND_VERBOSE
printf("rnd: ready, feeding in seed data directly.\n");
#endif
mutex_spin_enter(&rndpool_mtx);
rndpool_add_data(&rnd_pool, boot_rsp->data,
sizeof(boot_rsp->data),
MIN(boot_rsp->entropy, RND_POOLBITS / 2));
memset(boot_rsp, 0, sizeof(*boot_rsp));
mutex_spin_exit(&rndpool_mtx);
} else {
#ifdef RND_VERBOSE
printf("rnd: not ready, deferring seed feed.\n");
#endif
}
}