From 4924aa205a64e505387dca5e5c074833607de7eb Mon Sep 17 00:00:00 2001 From: tls Date: Wed, 23 Nov 2011 10:47:48 +0000 Subject: [PATCH] Load entropy at system boot (only works at securelevel < 1); save at system shutdown. Disable with random_seed=NO in rc.conf if desired. Goes to some trouble to never load or save to network filesystems. Entropy should really be loaded by the boot loader but I am still sorting out how to pass it to the kernel. --- distrib/sets/lists/etc/mi | 3 +- etc/defaults/rc.conf | 5 +- etc/rc.d/Makefile | 7 +- etc/rc.d/random_seed | 91 +++++++++++ sbin/rndctl/rndctl.8 | 17 +- sbin/rndctl/rndctl.c | 153 +++++++++++++++++- sys/dev/rnd.c | 63 +++++++- .../securelevel/secmodel_securelevel.c | 9 +- sys/secmodel/suser/secmodel_suser.c | 5 +- sys/sys/kauth.h | 3 +- sys/sys/rnd.h | 4 +- 11 files changed, 336 insertions(+), 24 deletions(-) create mode 100755 etc/rc.d/random_seed diff --git a/distrib/sets/lists/etc/mi b/distrib/sets/lists/etc/mi index 00b63f71cebc..66958d8dcbd0 100644 --- a/distrib/sets/lists/etc/mi +++ b/distrib/sets/lists/etc/mi @@ -1,4 +1,4 @@ -# $NetBSD: mi,v 1.225 2011/09/06 21:32:30 riz Exp $ +# $NetBSD: mi,v 1.226 2011/11/23 10:47:49 tls Exp $ # # Note: end-user configuration files that are moved to another location # should not be marked "obsolete"; they should just be removed from @@ -254,6 +254,7 @@ ./etc/rc.d/racoon etc-net-rc ./etc/rc.d/raidframe etc-sys-rc ./etc/rc.d/raidframeparity etc-sys-rc +./etc/rc.d/random_seed etc-sys-rc ./etc/rc.d/rarpd etc-bootserver-rc ./etc/rc.d/rbootd etc-bootserver-rc ./etc/rc.d/rndctl etc-sys-rc diff --git a/etc/defaults/rc.conf b/etc/defaults/rc.conf index 15b88ac40e0b..44082fcf5bf4 100644 --- a/etc/defaults/rc.conf +++ b/etc/defaults/rc.conf @@ -1,4 +1,4 @@ -# $NetBSD: rc.conf,v 1.116 2011/11/21 20:56:21 darcy Exp $ +# $NetBSD: rc.conf,v 1.117 2011/11/23 10:47:48 tls Exp $ # # /etc/defaults/rc.conf -- # default configuration of /etc/rc.conf @@ -362,3 +362,6 @@ veriexec_strict=0 veriexec_verbose=0 veriexec_flags="-k" +# Entropy load/save to/from /dev/random at startup/shutdown +# +random_seed=YES diff --git a/etc/rc.d/Makefile b/etc/rc.d/Makefile index 198fcba139c2..e9a3eca8c86e 100755 --- a/etc/rc.d/Makefile +++ b/etc/rc.d/Makefile @@ -1,4 +1,4 @@ -# $NetBSD: Makefile,v 1.85 2011/09/06 21:32:29 riz Exp $ +# $NetBSD: Makefile,v 1.86 2011/11/23 10:47:48 tls Exp $ .include @@ -31,8 +31,9 @@ CONFIGFILES=\ named ndbootd network newsyslog nfsd nfslocking npf ntpd ntpdate \ perusertmp pf pf_boot pflogd postfix powerd ppp pwcheck \ quota \ - racoon rpcbind raidframe raidframeparity rarpd rbootd rndctl \ - root route6d routed rtadvd rtclocaltime rtsold rwho \ + racoon rpcbind raidframe raidframeparity random_seed rarpd \ + rbootd rndctl root route6d routed rtadvd rtclocaltime \ + rtsold rwho \ savecore screenblank securelevel sshd \ staticroute swap1 swap2 sysctl sysdb syslogd \ timed tpctl ttys \ diff --git a/etc/rc.d/random_seed b/etc/rc.d/random_seed new file mode 100755 index 000000000000..c980d8c9dbe0 --- /dev/null +++ b/etc/rc.d/random_seed @@ -0,0 +1,91 @@ +#!/bin/sh +# +# $NetBSD: random_seed,v 1.1 2011/11/23 10:47:48 tls Exp $ +# + +# PROVIDE: random_seed +# REQUIRE: mountcritlocal +# BEFORE: securelevel +# KEYWORD: shutdown + +$_rc_subr_loaded . /etc/rc.subr + +name="random_seed" +rcvar=$name +start_cmd="random_load" +stop_cmd="random_save" + +random_file=${random_file:-/var/db/entropy-file} + +fs_safe() +{ + # + # Enforce that the file's on a local filesystem. + # Include only the types we can actually write. + # + fstype=$(df -G $1 | awk '$2 == "fstype" {print $1}') + case $fstype in + ffs) + return 0 + ;; + lfs) + return 0 + ;; + ext2fs) + return 0; + ;; + msdosfs) + return 0; + ;; + v7fs) + return 0; + ;; + esac + return 1 +} + +random_load() +{ + if [ -f $random_file ]; then + + if ! fs_safe $(dirname ${random_file}); then + return 1 + fi + + eval $(stat -s ${random_file}) + + # The file must be owned by root, + if [ "$st_uid" != "0" ]; then + return 1 + fi + # and root read/write only. + if [ "$(echo $st_mode | tail -c4)" != "600" ]; then + return 1 + fi + + if rndctl -L ${random_file}; then + echo "Loaded entropy from disk." + fi + + fi +} + +random_save() +{ + oum=$(umask) + umask 077 + + rm -Pf ${random_file} + + if ! fs_safe $(dirname ${random_file}); then + return 1 + fi + + if rndctl -S ${random_file}; then + echo "Saved entropy to disk." + fi +} + + +load_rc_config $name +run_rc_command "$1" diff --git a/sbin/rndctl/rndctl.8 b/sbin/rndctl/rndctl.8 index e9cb94d71ec0..fc89a7537a13 100644 --- a/sbin/rndctl/rndctl.8 +++ b/sbin/rndctl/rndctl.8 @@ -1,4 +1,4 @@ -.\" $NetBSD: rndctl.8,v 1.18 2011/10/01 02:55:00 pgoyette Exp $ +.\" $NetBSD: rndctl.8,v 1.19 2011/11/23 10:47:49 tls Exp $ .\" .\" Copyright (c) 1997 Michael Graff .\" All rights reserved. @@ -39,6 +39,10 @@ .Nm .Fl ls .Op Fl d Ar devname | Fl t Ar devtype +.Nm +.Fl L Ar save-file +.Nm +.Fl S Ar save-file .Sh DESCRIPTION The .Nm @@ -104,6 +108,17 @@ Terminal, mouse, or other user input devices. .It Ic rng Random number generators. .El +.It Fl L +Load saved entropy from file +.Ar save-file , +which will be overwritten and deleted before the entropy is loaded into +the kernel. +.It Fl S +Save entropy pool to file +.Ar save-file . +The file format is specific to +.Nm +and includes an estimate of the amount of saved entropy and a checksum. .El .Sh FILES .Bl -tag -width /dev/urandomx -compact diff --git a/sbin/rndctl/rndctl.c b/sbin/rndctl/rndctl.c index 9ef6d213377f..3590835d86f8 100644 --- a/sbin/rndctl/rndctl.c +++ b/sbin/rndctl/rndctl.c @@ -1,4 +1,4 @@ -/* $NetBSD: rndctl.c,v 1.20 2011/08/27 18:48:59 joerg Exp $ */ +/* $NetBSD: rndctl.c,v 1.21 2011/11/23 10:47:49 tls Exp $ */ /*- * Copyright (c) 1997 Michael Graff. @@ -29,14 +29,17 @@ * SUCH DAMAGE. */ #include +#include +#include #ifndef lint -__RCSID("$NetBSD: rndctl.c,v 1.20 2011/08/27 18:48:59 joerg Exp $"); +__RCSID("$NetBSD: rndctl.c,v 1.21 2011/11/23 10:47:49 tls Exp $"); #endif #include #include +#include #include #include @@ -47,6 +50,12 @@ __RCSID("$NetBSD: rndctl.c,v 1.20 2011/08/27 18:48:59 joerg Exp $"); #include #include +typedef struct { + uint32_t entropy; + uint8_t data[RND_POOLWORDS * sizeof(uint32_t)]; + uint8_t digest[SHA1_DIGEST_LENGTH]; +} rndsave_t; + typedef struct { const char *a_name; u_int32_t a_type; @@ -78,6 +87,7 @@ usage(void) getprogname()); fprintf(stderr, " %s -ls [-d devname | -t devtype]\n", getprogname()); + fprintf(stderr, " %s -[L|S] save-file\n", getprogname()); exit(1); } @@ -115,6 +125,114 @@ find_name(u_int32_t type) return ("???"); } +static void +do_save(const char *const filename) +{ + int est1, est2; + rndpoolstat_t rp; + rndsave_t rs; + SHA1_CTX s; + + int fd; + + fd = open("/dev/urandom", O_RDONLY, 0644); + if (fd < 0) { + err(1, "device open"); + } + + if (ioctl(fd, RNDGETPOOLSTAT, &rp) < 0) { + err(1, "ioctl(RNDGETPOOLSTAT)"); + } + + est1 = rp.curentropy; + + if (read(fd, rs.data, sizeof(rs.data)) != sizeof(rs.data)) { + err(1, "entropy read"); + } + + if (ioctl(fd, RNDGETPOOLSTAT, &rp) < 0) { + err(1, "ioctl(RNDGETPOOLSTAT)"); + } + + est2 = rp.curentropy; + + if (est1 - est2 < 0) { + rs.entropy = 0; + } else { + rs.entropy = est1 - est2; + } + + SHA1Init(&s); + SHA1Update(&s, (uint8_t *)&rs.entropy, sizeof(rs.entropy)); + SHA1Update(&s, rs.data, sizeof(rs.data)); + SHA1Final(rs.digest, &s); + + close(fd); + unlink(filename); + fd = open(filename, O_CREAT|O_EXCL|O_WRONLY, 0600); + if (fd < 0) { + err(1, "output open"); + } + + if (write(fd, &rs, sizeof(rs)) != sizeof(rs)) { + unlink(filename); + fsync_range(fd, FDATASYNC|FDISKSYNC, (off_t)0, (off_t)0); + err(1, "write"); + } + fsync_range(fd, FDATASYNC|FDISKSYNC, (off_t)0, (off_t)0); + close(fd); +} + +static void +do_load(const char *const filename) +{ + int fd; + rndsave_t rs; + rnddata_t rd; + SHA1_CTX s; + uint8_t digest[SHA1_DIGEST_LENGTH]; + + fd = open(filename, O_RDWR, 0600); + if (fd < 0) { + err(1, "input open"); + } + + unlink(filename); + + if (read(fd, &rs, sizeof(rs)) != sizeof(rs)) { + err(1, "read"); + } + + if (write(fd, &rs, sizeof(rs) != sizeof(rs))) { + err(1, "overwrite"); + } + fsync_range(fd, FDATASYNC|FDISKSYNC, (off_t)0, (off_t)0); + close(fd); + + SHA1Init(&s); + SHA1Update(&s, (uint8_t *)&rs.entropy, sizeof(rs.entropy)); + SHA1Update(&s, rs.data, sizeof(rs.data)); + SHA1Final(digest, &s); + + if (memcmp(digest, rs.digest, sizeof(digest))) { + errx(1, "bad digest"); + } + + rd.len = MIN(sizeof(rd.data), sizeof(rs.data)); + rd.entropy = rs.entropy; + memcpy(rd.data, rs.data, MIN(sizeof(rd.data), sizeof(rs.data))); + + fd = open("/dev/urandom", O_RDWR, 0644); + if (fd < 0) { + err(1, "device open"); + } + + if (ioctl(fd, RNDADDDATA, &rd) < 0) { + err(1, "ioctl"); + } + close(fd); +} + static void do_ioctl(rndctl_t *rctl) { @@ -247,6 +365,7 @@ main(int argc, char **argv) int ch, cmd, lflag, mflag, sflag; u_int32_t type; char name[16]; + const char *filename = NULL; rctl.mask = 0; rctl.flags = 0; @@ -257,7 +376,7 @@ main(int argc, char **argv) sflag = 0; type = 0xff; - while ((ch = getopt(argc, argv, "CEcelt:d:s")) != -1) { + while ((ch = getopt(argc, argv, "CES:L:celt:d:s")) != -1) { switch (ch) { case 'C': rctl.flags |= RND_FLAG_NO_COLLECT; @@ -269,6 +388,18 @@ main(int argc, char **argv) rctl.mask |= RND_FLAG_NO_ESTIMATE; mflag++; break; + case 'L': + if (cmd != 0) + usage(); + cmd = 'L'; + filename = optarg; + break; + case 'S': + if (cmd != 0) + usage(); + cmd = 'S'; + filename = optarg; + break; case 'c': rctl.flags &= ~RND_FLAG_NO_COLLECT; rctl.mask |= RND_FLAG_NO_COLLECT; @@ -314,6 +445,22 @@ main(int argc, char **argv) if (argc > 0) usage(); + /* + * Save. + */ + if (cmd == 'S') { + do_save(filename); + exit(0); + } + + /* + * Load. + */ + if (cmd == 'L') { + do_load(filename); + exit(0); + } + /* * Cannot list and modify at the same time. */ diff --git a/sys/dev/rnd.c b/sys/dev/rnd.c index 2faef1366074..df9c11977bc3 100644 --- a/sys/dev/rnd.c +++ b/sys/dev/rnd.c @@ -1,4 +1,4 @@ -/* $NetBSD: rnd.c,v 1.85 2011/11/20 00:45:15 tls Exp $ */ +/* $NetBSD: rnd.c,v 1.86 2011/11/23 10:47:48 tls Exp $ */ /*- * Copyright (c) 1997-2011 The NetBSD Foundation, Inc. @@ -32,7 +32,7 @@ */ #include -__KERNEL_RCSID(0, "$NetBSD: rnd.c,v 1.85 2011/11/20 00:45:15 tls Exp $"); +__KERNEL_RCSID(0, "$NetBSD: rnd.c,v 1.86 2011/11/23 10:47:48 tls Exp $"); #include #include @@ -509,7 +509,15 @@ int rndwrite(dev_t dev, struct uio *uio, int ioflag) { u_int8_t *bf; - int n, ret; + int n, ret = 0, estimate_ok = 0, estimate = 0, added = 0; + + ret = kauth_authorize_device(curlwp->l_cred, + KAUTH_DEVICE_RND_ADDDATA, NULL, NULL, NULL, NULL); + if (ret) { + return (ret); + } + estimate_ok = !kauth_authorize_device(curlwp->l_cred, + KAUTH_DEVICE_RND_ADDDATA_ESTIMATE, NULL, NULL, NULL, NULL); DPRINTF(RND_DEBUG_WRITE, ("Random: Write of %zu requested\n", uio->uio_resid)); @@ -519,19 +527,43 @@ rndwrite(dev_t dev, struct uio *uio, int ioflag) ret = 0; bf = kmem_alloc(RND_TEMP_BUFFER_SIZE, KM_SLEEP); while (uio->uio_resid > 0) { + /* + * Don't flood the pool. + */ + if (added > RND_POOLWORDS * sizeof(int)) { + printf("rnd: added %d already, adding no more.\n", + added); + break; + } n = min(RND_TEMP_BUFFER_SIZE, uio->uio_resid); ret = uiomove((void *)bf, n, uio); if (ret != 0) break; + if (estimate_ok) { + /* + * Don't cause samples to be discarded by taking + * the pool's entropy estimate to the max. + */ + if (added > RND_POOLWORDS / 2) + estimate = 0; + else + estimate = n * NBBY / 2; + printf("rnd: adding on write, %d bytes, estimate %d\n", + n, estimate); + } else { + printf("rnd: kauth says no entropy.\n"); + } + /* * Mix in the bytes. */ mutex_spin_enter(&rndpool_mtx); - rndpool_add_data(&rnd_pool, bf, n, 0); + rndpool_add_data(&rnd_pool, bf, n, estimate); mutex_spin_exit(&rndpool_mtx); + added += n; DPRINTF(RND_DEBUG_WRITE, ("Random: Copied in %d bytes\n", n)); } kmem_free(bf, RND_TEMP_BUFFER_SIZE); @@ -558,9 +590,8 @@ rndioctl(dev_t dev, u_long cmd, void *addr, int flag, rndctl_t *rctl; rnddata_t *rnddata; u_int32_t count, start; - int ret; - - ret = 0; + int ret = 0; + int estimate_ok = 0, estimate = 0; switch (cmd) { case FIONBIO: @@ -589,6 +620,8 @@ rndioctl(dev_t dev, u_long cmd, void *addr, int flag, KAUTH_DEVICE_RND_ADDDATA, NULL, NULL, NULL, NULL); if (ret) return (ret); + estimate_ok = !kauth_authorize_device(l->l_cred, + KAUTH_DEVICE_RND_ADDDATA_ESTIMATE, NULL, NULL, NULL, NULL); break; default: @@ -720,9 +753,23 @@ rndioctl(dev_t dev, u_long cmd, void *addr, int flag, 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; + } + mutex_spin_enter(&rndpool_mtx); rndpool_add_data(&rnd_pool, rnddata->data, rnddata->len, - rnddata->entropy); + estimate); mutex_spin_exit(&rndpool_mtx); rnd_wakeup_readers(); diff --git a/sys/secmodel/securelevel/secmodel_securelevel.c b/sys/secmodel/securelevel/secmodel_securelevel.c index 47d38f51f4f0..9d372350112b 100644 --- a/sys/secmodel/securelevel/secmodel_securelevel.c +++ b/sys/secmodel/securelevel/secmodel_securelevel.c @@ -1,4 +1,4 @@ -/* $NetBSD: secmodel_securelevel.c,v 1.20 2009/10/07 01:06:57 elad Exp $ */ +/* $NetBSD: secmodel_securelevel.c,v 1.21 2011/11/23 10:47:48 tls Exp $ */ /*- * Copyright (c) 2006 Elad Efrat * All rights reserved. @@ -35,7 +35,7 @@ */ #include -__KERNEL_RCSID(0, "$NetBSD: secmodel_securelevel.c,v 1.20 2009/10/07 01:06:57 elad Exp $"); +__KERNEL_RCSID(0, "$NetBSD: secmodel_securelevel.c,v 1.21 2011/11/23 10:47:48 tls Exp $"); #ifdef _KERNEL_OPT #include "opt_insecure.h" @@ -553,6 +553,11 @@ secmodel_securelevel_device_cb(kauth_cred_t cred, kauth_action_t action, result = KAUTH_RESULT_DENY; break; + case KAUTH_DEVICE_RND_ADDDATA_ESTIMATE: + if (securelevel > 0) + result = KAUTH_RESULT_DENY; + break; + default: break; } diff --git a/sys/secmodel/suser/secmodel_suser.c b/sys/secmodel/suser/secmodel_suser.c index d55e3144099a..2b5d8fd828fd 100644 --- a/sys/secmodel/suser/secmodel_suser.c +++ b/sys/secmodel/suser/secmodel_suser.c @@ -1,4 +1,4 @@ -/* $NetBSD: secmodel_suser.c,v 1.34 2009/12/29 04:25:30 elad Exp $ */ +/* $NetBSD: secmodel_suser.c,v 1.35 2011/11/23 10:47:49 tls Exp $ */ /*- * Copyright (c) 2006 Elad Efrat * All rights reserved. @@ -38,7 +38,7 @@ */ #include -__KERNEL_RCSID(0, "$NetBSD: secmodel_suser.c,v 1.34 2009/12/29 04:25:30 elad Exp $"); +__KERNEL_RCSID(0, "$NetBSD: secmodel_suser.c,v 1.35 2011/11/23 10:47:49 tls Exp $"); #include #include @@ -838,6 +838,7 @@ secmodel_suser_device_cb(kauth_cred_t cred, kauth_action_t action, case KAUTH_DEVICE_TTY_PRIVSET: case KAUTH_DEVICE_TTY_STI: case KAUTH_DEVICE_RND_ADDDATA: + case KAUTH_DEVICE_RND_ADDDATA_ESTIMATE: case KAUTH_DEVICE_RND_GETPRIV: case KAUTH_DEVICE_RND_SETPRIV: if (isroot) diff --git a/sys/sys/kauth.h b/sys/sys/kauth.h index 7a31401259d6..64573254e79c 100644 --- a/sys/sys/kauth.h +++ b/sys/sys/kauth.h @@ -1,4 +1,4 @@ -/* $NetBSD: kauth.h,v 1.64 2009/12/24 19:02:07 elad Exp $ */ +/* $NetBSD: kauth.h,v 1.65 2011/11/23 10:47:49 tls Exp $ */ /*- * Copyright (c) 2005, 2006 Elad Efrat @@ -255,6 +255,7 @@ enum { KAUTH_DEVICE_RAWIO_PASSTHRU, KAUTH_DEVICE_BLUETOOTH_SETPRIV, KAUTH_DEVICE_RND_ADDDATA, + KAUTH_DEVICE_RND_ADDDATA_ESTIMATE, KAUTH_DEVICE_RND_GETPRIV, KAUTH_DEVICE_RND_SETPRIV, KAUTH_DEVICE_BLUETOOTH_BCSP, diff --git a/sys/sys/rnd.h b/sys/sys/rnd.h index c38d2153085a..f6d3a583eebd 100644 --- a/sys/sys/rnd.h +++ b/sys/sys/rnd.h @@ -1,4 +1,4 @@ -/* $NetBSD: rnd.h,v 1.22 2011/11/19 22:51:31 tls Exp $ */ +/* $NetBSD: rnd.h,v 1.23 2011/11/23 10:47:49 tls Exp $ */ /*- * Copyright (c) 1997 The NetBSD Foundation, Inc. @@ -209,7 +209,7 @@ typedef struct { typedef struct { uint32_t len; uint32_t entropy; - u_char data[RND_POOLWORDS * 4]; + u_char data[RND_POOLWORDS * sizeof(uint32_t)]; } rnddata_t; #define RNDGETENTCNT _IOR('R', 101, uint32_t) /* get entropy count */