Fri Dec 6 14:43:18 2019 UTC ()
Teach `rndctl -L' to update the seed file, not just delete it.

The seed file is updated by entering the old seed into the system and
then hashing the old seed together with data from /dev/urandom, and
writing it atomically with write-to-temporary/rename-to-permanent.

This way, interruption by crash or power loss does not obliterate
your persistent entropy (unless it causes file system corruption).


(riastradh)
diff -r1.3 -r1.4 src/sbin/rndctl/Makefile
diff -r0 -r1.1 src/sbin/rndctl/namespace.h
diff -r1.22 -r1.23 src/sbin/rndctl/rndctl.8
diff -r1.30 -r1.31 src/sbin/rndctl/rndctl.c

cvs diff -r1.3 -r1.4 src/sbin/rndctl/Makefile (switch to unified diff)

--- src/sbin/rndctl/Makefile 2019/10/13 07:28:13 1.3
+++ src/sbin/rndctl/Makefile 2019/12/06 14:43:18 1.4
@@ -1,8 +1,20 @@ @@ -1,8 +1,20 @@
1# $NetBSD: Makefile,v 1.3 2019/10/13 07:28:13 mrg Exp $ 1# $NetBSD: Makefile,v 1.4 2019/12/06 14:43:18 riastradh Exp $
2 2
3PROG= rndctl 3PROG= rndctl
4MAN= rndctl.8 4MAN= rndctl.8
5 5
6COPTS.rndctl.c+= ${GCC_NO_STRINGOP_TRUNCATION} 6COPTS.rndctl.c+= ${GCC_NO_STRINGOP_TRUNCATION}
7 7
 8SRCS+= rndctl.c
 9
 10# Hack: libc does not export public SHA-3 symbols, so we'll just copy
 11# them here statically.
 12.PATH: ${NETBSDSRCDIR}/common/lib/libc/hash/sha3
 13
 14# Hack for namespace.h in sha3.c.
 15CPPFLAGS+= -I${.CURDIR}
 16
 17SRCS+= sha3.c
 18SRCS+= keccak.c
 19
8.include <bsd.prog.mk> 20.include <bsd.prog.mk>

File Added: src/sbin/rndctl/namespace.h
/* dummy for sha3.c */

cvs diff -r1.22 -r1.23 src/sbin/rndctl/rndctl.8 (switch to unified diff)

--- src/sbin/rndctl/rndctl.8 2014/08/10 17:13:02 1.22
+++ src/sbin/rndctl/rndctl.8 2019/12/06 14:43:18 1.23
@@ -1,153 +1,157 @@ @@ -1,153 +1,157 @@
1.\" $NetBSD: rndctl.8,v 1.22 2014/08/10 17:13:02 wiz Exp $ 1.\" $NetBSD: rndctl.8,v 1.23 2019/12/06 14:43:18 riastradh Exp $
2.\" 2.\"
3.\" Copyright (c) 1997 Michael Graff 3.\" Copyright (c) 1997 Michael Graff
4.\" All rights reserved. 4.\" All rights reserved.
5.\" 5.\"
6.\" Redistribution and use in source and binary forms, with or without 6.\" Redistribution and use in source and binary forms, with or without
7.\" modification, are permitted provided that the following conditions 7.\" modification, are permitted provided that the following conditions
8.\" are met: 8.\" are met:
9.\" 1. Redistributions of source code must retain the above copyright 9.\" 1. Redistributions of source code must retain the above copyright
10.\" notice, this list of conditions and the following disclaimer. 10.\" notice, this list of conditions and the following disclaimer.
11.\" 2. Redistributions in binary form must reproduce the above copyright 11.\" 2. Redistributions in binary form must reproduce the above copyright
12.\" notice, this list of conditions and the following disclaimer in the 12.\" notice, this list of conditions and the following disclaimer in the
13.\" documentation and/or other materials provided with the distribution. 13.\" documentation and/or other materials provided with the distribution.
14.\" 3. The name of the author may not be used to endorse or promote products 14.\" 3. The name of the author may not be used to endorse or promote products
15.\" derived from this software without specific prior written permission. 15.\" derived from this software without specific prior written permission.
16.\" 16.\"
17.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 17.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 18.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 19.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20.\" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 20.\" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 21.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
22.\" BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22.\" BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23.\" LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 23.\" LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
24.\" AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 24.\" AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
25.\" OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25.\" OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27.\" SUCH DAMAGE. 27.\" SUCH DAMAGE.
28.\" 28.\"
29.Dd August 10, 2014 29.Dd August 10, 2014
30.Dt RNDCTL 8 30.Dt RNDCTL 8
31.Os 31.Os
32.Sh NAME 32.Sh NAME
33.Nm rndctl 33.Nm rndctl
34.Nd in-kernel random number generator management tool 34.Nd in-kernel random number generator management tool
35.Sh SYNOPSIS 35.Sh SYNOPSIS
36.Nm 36.Nm
37.Op Fl CcEe 37.Op Fl CcEe
38.Op Fl d Ar devname | Fl t Ar devtype 38.Op Fl d Ar devname | Fl t Ar devtype
39.Nm 39.Nm
40.Op Fl lsv 40.Op Fl lsv
41.Op Fl d Ar devname | Fl t Ar devtype 41.Op Fl d Ar devname | Fl t Ar devtype
42.Nm 42.Nm
43.Fl L Ar save-file 43.Fl L Ar save-file
44.Nm 44.Nm
45.Fl S Ar save-file 45.Fl S Ar save-file
46.Sh DESCRIPTION 46.Sh DESCRIPTION
47The 47The
48.Nm 48.Nm
49program displays statistics on the current state of the 49program displays statistics on the current state of the
50.Xr rnd 4 50.Xr rnd 4
51pseudo-driver, and allows the administrator to control which sources 51pseudo-driver, and allows the administrator to control which sources
52are allowed to contribute to the randomness pool maintained by 52are allowed to contribute to the randomness pool maintained by
53.Xr rnd 4 , 53.Xr rnd 4 ,
54as well as whether a given source counts as strongly random. 54as well as whether a given source counts as strongly random.
55.Pp 55.Pp
56The following options are available: 56The following options are available:
57.Bl -tag -width 123456 57.Bl -tag -width 123456
58.It Fl C 58.It Fl C
59Disable collection of timing information for the given 59Disable collection of timing information for the given
60device name or device type. 60device name or device type.
61.It Fl c 61.It Fl c
62Enable collection of timing information for the given 62Enable collection of timing information for the given
63device name or device type. 63device name or device type.
64.It Fl d 64.It Fl d
65Only the device named 65Only the device named
66.Ar devname 66.Ar devname
67is altered or displayed. 67is altered or displayed.
68This is mutually exclusive with 68This is mutually exclusive with
69.Fl t . 69.Fl t .
70.It Fl E 70.It Fl E
71Disable entropy estimation from the collected timing information for 71Disable entropy estimation from the collected timing information for
72the given device name or device type. 72the given device name or device type.
73If collection is still enabled, timing information is still 73If collection is still enabled, timing information is still
74collected and mixed into the internal entropy pool, 74collected and mixed into the internal entropy pool,
75but no entropy is assumed to be present. 75but no entropy is assumed to be present.
76.It Fl e 76.It Fl e
77Enable entropy estimation using the collected timing information 77Enable entropy estimation using the collected timing information
78for the given device name or device type. 78for the given device name or device type.
79.It Fl L 79.It Fl L
80Load saved entropy from file 80Load saved entropy from file
81.Ar save-file , 81.Ar save-file
82which will be overwritten and deleted before the entropy is loaded into 82and overwrite it with a seed derived by hashing it together with output
83the kernel. 83from
 84.Pa /dev/urandom
 85so that the new seed has at least as much entropy as either the old
 86seed had or the system already has.
 87If interrupted, either the old seed or the new seed will be in place.
84.It Fl l 88.It Fl l
85List all sources, or, if the 89List all sources, or, if the
86.Fl t 90.Fl t
87or 91or
88.Fl d 92.Fl d
89flags are specified, only those specified by the 93flags are specified, only those specified by the
90.Ar devtype 94.Ar devtype
91or 95or
92.Ar devname 96.Ar devname
93specified. 97specified.
94.It Fl S 98.It Fl S
95Save entropy pool to file 99Save entropy pool to file
96.Ar save-file . 100.Ar save-file .
97The file format is specific to 101The file format is specific to
98.Nm 102.Nm
99and includes an estimate of the amount of saved entropy and a checksum. 103and includes an estimate of the amount of saved entropy and a checksum.
100.It Fl s 104.It Fl s
101Display statistics on the current state of the random collection pool. 105Display statistics on the current state of the random collection pool.
102.It Fl t 106.It Fl t
103All devices of type 107All devices of type
104.Ar devtype 108.Ar devtype
105are altered or displayed. 109are altered or displayed.
106This is mutually exclusive with 110This is mutually exclusive with
107.Fl d . 111.Fl d .
108.Pp 112.Pp
109The available types are: 113The available types are:
110.Bl -tag -width "diskx" 114.Bl -tag -width "diskx"
111.It Ic disk 115.It Ic disk
112Physical hard drives. 116Physical hard drives.
113.It Ic net 117.It Ic net
114Network interfaces. 118Network interfaces.
115.It Ic tape 119.It Ic tape
116Tape devices. 120Tape devices.
117.It Ic tty 121.It Ic tty
118Terminal, mouse, or other user input devices. 122Terminal, mouse, or other user input devices.
119.It Ic rng 123.It Ic rng
120Random number generators. 124Random number generators.
121.El 125.El
122.It Fl v 126.It Fl v
123Verbose output: show entropy estimation statistics for each source. 127Verbose output: show entropy estimation statistics for each source.
124.El 128.El
125.Sh FILES 129.Sh FILES
126.Bl -tag -width /dev/urandomx -compact 130.Bl -tag -width /dev/urandomx -compact
127.It Pa /dev/random 131.It Pa /dev/random
128Returns 132Returns
129.Dq good 133.Dq good
130values only. 134values only.
131.It Pa /dev/urandom 135.It Pa /dev/urandom
132Always returns data, degenerates to a pseudo-random generator. 136Always returns data, degenerates to a pseudo-random generator.
133.El 137.El
134.Sh SEE ALSO 138.Sh SEE ALSO
135.Xr rnd 4 , 139.Xr rnd 4 ,
136.Xr rnd 9 140.Xr rnd 9
137.Sh HISTORY 141.Sh HISTORY
138The 142The
139.Nm 143.Nm
140program was first made available in 144program was first made available in
141.Nx 1.3 . 145.Nx 1.3 .
142.Sh AUTHORS 146.Sh AUTHORS
143The 147The
144.Nm 148.Nm
145program was written by 149program was written by
146.An Michael Graff 150.An Michael Graff
147.Aq explorer@flame.org . 151.Aq explorer@flame.org .
148.Sh BUGS 152.Sh BUGS
149Turning on entropy estimation from unsafe or predictable sources will 153Turning on entropy estimation from unsafe or predictable sources will
150weaken system security, while turning on entropy collection from such 154weaken system security, while turning on entropy collection from such
151sources may weaken system security. 155sources may weaken system security.
152.Pp 156.Pp
153Care should be taken when using this command. 157Care should be taken when using this command.

cvs diff -r1.30 -r1.31 src/sbin/rndctl/rndctl.c (switch to unified diff)

--- src/sbin/rndctl/rndctl.c 2015/04/13 22:18:50 1.30
+++ src/sbin/rndctl/rndctl.c 2019/12/06 14:43:18 1.31
@@ -1,536 +1,656 @@ @@ -1,536 +1,656 @@
1/* $NetBSD: rndctl.c,v 1.30 2015/04/13 22:18:50 riastradh Exp $ */ 1/* $NetBSD: rndctl.c,v 1.31 2019/12/06 14:43:18 riastradh Exp $ */
2 2
3/*- 3/*-
4 * Copyright (c) 1997 Michael Graff. 4 * Copyright (c) 1997 Michael Graff.
5 * All rights reserved. 5 * All rights reserved.
6 * 6 *
7 * Redistribution and use in source and binary forms, with or without 7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions 8 * modification, are permitted provided that the following conditions
9 * are met: 9 * are met:
10 * 1. Redistributions of source code must retain the above copyright 10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer. 11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright 12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the 13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution. 14 * documentation and/or other materials provided with the distribution.
15 * 3. Neither the name of the author nor the names of other contributors 15 * 3. Neither the name of the author nor the names of other contributors
16 * may be used to endorse or promote products derived from this software 16 * may be used to endorse or promote products derived from this software
17 * without specific prior written permission. 17 * without specific prior written permission.
18 * 18 *
19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
20 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 20 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
21 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 21 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
22 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 22 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
23 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 23 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
24 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 24 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 25 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
26 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 26 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE. 29 * SUCH DAMAGE.
30 */ 30 */
31#include <sys/cdefs.h> 31#include <sys/cdefs.h>
32#include <sys/types.h> 32#include <sys/types.h>
33#include <sha1.h> 33#include <sha1.h>
34 34
35#ifndef lint 35#ifndef lint
36__RCSID("$NetBSD: rndctl.c,v 1.30 2015/04/13 22:18:50 riastradh Exp $"); 36__RCSID("$NetBSD: rndctl.c,v 1.31 2019/12/06 14:43:18 riastradh Exp $");
37#endif 37#endif
38 38
39 39
40#include <sys/types.h> 40#include <sys/types.h>
41#include <sys/ioctl.h> 41#include <sys/ioctl.h>
42#include <sys/param.h> 42#include <sys/param.h>
43#include <sys/rndio.h> 43#include <sys/rndio.h>
 44#include <sys/sha3.h>
44 45
45#include <stdio.h> 46#include <stdio.h>
46#include <stdlib.h> 47#include <stdlib.h>
47#include <unistd.h> 48#include <unistd.h>
48#include <fcntl.h> 49#include <fcntl.h>
49#include <errno.h> 50#include <errno.h>
50#include <err.h> 51#include <err.h>
51#include <paths.h> 52#include <paths.h>
52#include <string.h> 53#include <string.h>
53 54
54typedef struct { 55typedef struct {
55 const char *a_name; 56 const char *a_name;
56 u_int32_t a_type; 57 u_int32_t a_type;
57} arg_t; 58} arg_t;
58 59
59static const arg_t source_types[] = { 60static const arg_t source_types[] = {
60 { "???", RND_TYPE_UNKNOWN }, 61 { "???", RND_TYPE_UNKNOWN },
61 { "disk", RND_TYPE_DISK }, 62 { "disk", RND_TYPE_DISK },
62 { "net", RND_TYPE_NET }, 63 { "net", RND_TYPE_NET },
63 { "tape", RND_TYPE_TAPE }, 64 { "tape", RND_TYPE_TAPE },
64 { "tty", RND_TYPE_TTY }, 65 { "tty", RND_TYPE_TTY },
65 { "rng", RND_TYPE_RNG }, 66 { "rng", RND_TYPE_RNG },
66 { "skew", RND_TYPE_SKEW }, 67 { "skew", RND_TYPE_SKEW },
67 { "env", RND_TYPE_ENV }, 68 { "env", RND_TYPE_ENV },
68 { "vm", RND_TYPE_VM }, 69 { "vm", RND_TYPE_VM },
69 { "power", RND_TYPE_POWER }, 70 { "power", RND_TYPE_POWER },
70 { NULL, 0 } 71 { NULL, 0 }
71}; 72};
72 73
73__dead static void usage(void); 74__dead static void usage(void);
74static u_int32_t find_type(const char *name); 75static u_int32_t find_type(const char *name);
75static const char *find_name(u_int32_t); 76static const char *find_name(u_int32_t);
76static void do_ioctl(rndctl_t *); 77static void do_ioctl(rndctl_t *);
77static char * strflags(u_int32_t); 78static char * strflags(u_int32_t);
78static void do_list(int, u_int32_t, char *); 79static void do_list(int, u_int32_t, char *);
79static void do_stats(void); 80static void do_stats(void);
80 81
81static int vflag; 82static int vflag;
82 83
83static void 84static void
84usage(void) 85usage(void)
85{ 86{
86 87
87 fprintf(stderr, "usage: %s [-CEce] [-d devname | -t devtype]\n", 88 fprintf(stderr, "usage: %s [-CEce] [-d devname | -t devtype]\n",
88 getprogname()); 89 getprogname());
89 fprintf(stderr, " %s [-lsv] [-d devname | -t devtype]\n", 90 fprintf(stderr, " %s [-lsv] [-d devname | -t devtype]\n",
90 getprogname()); 91 getprogname());
91 fprintf(stderr, " %s -[L|S] save-file\n", getprogname()); 92 fprintf(stderr, " %s -[L|S] save-file\n", getprogname());
92 exit(1); 93 exit(1);
93} 94}
94 95
95static u_int32_t 96static u_int32_t
96find_type(const char *name) 97find_type(const char *name)
97{ 98{
98 const arg_t *a; 99 const arg_t *a;
99 100
100 a = source_types; 101 a = source_types;
101 102
102 while (a->a_name != NULL) { 103 while (a->a_name != NULL) {
103 if (strcmp(a->a_name, name) == 0) 104 if (strcmp(a->a_name, name) == 0)
104 return (a->a_type); 105 return (a->a_type);
105 a++; 106 a++;
106 } 107 }
107 108
108 errx(1, "device name %s unknown", name); 109 errx(1, "device name %s unknown", name);
109 return (0); 110 return (0);
110} 111}
111 112
112static const char * 113static const char *
113find_name(u_int32_t type) 114find_name(u_int32_t type)
114{ 115{
115 const arg_t *a; 116 const arg_t *a;
116 117
117 a = source_types; 118 a = source_types;
118 119
119 while (a->a_name != NULL) { 120 while (a->a_name != NULL) {
120 if (type == a->a_type) 121 if (type == a->a_type)
121 return (a->a_name); 122 return (a->a_name);
122 a++; 123 a++;
123 } 124 }
124 125
125 warnx("device type %u unknown", type); 126 warnx("device type %u unknown", type);
126 return ("???"); 127 return ("???");
127} 128}
128 129
129static void 130static void
130do_save(const char *const filename) 131do_save(const char *filename, const void *extra, size_t nextra,
 132 uint32_t extraentropy)
131{ 133{
132 int est1, est2; 134 char tmp[PATH_MAX];
133 rndpoolstat_t rp; 135 uint32_t systementropy;
 136 uint8_t buf[32];
 137 SHAKE128_CTX shake128;
134 rndsave_t rs; 138 rndsave_t rs;
135 SHA1_CTX s; 139 SHA1_CTX s;
136 140 ssize_t nread, nwrit;
137 int fd; 141 int fd;
138 142
139 fd = open(_PATH_URANDOM, O_RDONLY, 0644); 143 /* Paranoia: Avoid stack memory disclosure. */
140 if (fd < 0) { 144 memset(&rs, 0, sizeof rs);
141 err(1, "device open"); 
142 } 
143 
144 if (ioctl(fd, RNDGETPOOLSTAT, &rp) < 0) { 
145 err(1, "ioctl(RNDGETPOOLSTAT)"); 
146 } 
147 145
148 est1 = rp.curentropy; 146 /* Format the temporary file name. */
 147 if (snprintf(tmp, sizeof tmp, "%s.tmp", filename) >= PATH_MAX)
 148 errx(1, "path too long");
149 149
150 if (read(fd, rs.data, sizeof(rs.data)) != sizeof(rs.data)) { 150 /* Open /dev/urandom. */
151 err(1, "entropy read"); 151 if ((fd = open(_PATH_URANDOM, O_RDONLY)) == -1)
152 } 152 err(1, "device open");
153 153
154 if (ioctl(fd, RNDGETPOOLSTAT, &rp) < 0) { 154 /* Find how much entropy is in the pool. */
155 err(1, "ioctl(RNDGETPOOLSTAT)"); 155 if (ioctl(fd, RNDGETENTCNT, &systementropy) == -1)
156 } 156 err(1, "ioctl(RNDGETENTCNT)");
 157
 158 /* Read some data from /dev/urandom. */
 159 if ((size_t)(nread = read(fd, buf, sizeof buf)) != sizeof buf) {
 160 if (nread == -1)
 161 err(1, "read");
 162 else
 163 errx(1, "truncated read");
 164 }
 165
 166 /* Close /dev/urandom; we're done with it. */
 167 if (close(fd) == -1)
 168 warn("close");
 169 fd = -1; /* paranoia */
157 170
158 est2 = rp.curentropy; 171 /*
 172 * Hash what we read together with the extra input to generate
 173 * the seed data.
 174 */
 175 SHAKE128_Init(&shake128);
 176 SHAKE128_Update(&shake128, buf, sizeof buf);
 177 SHAKE128_Update(&shake128, extra, nextra);
 178 SHAKE128_Final(rs.data, sizeof(rs.data), &shake128);
 179 explicit_memset(&shake128, 0, sizeof shake128); /* paranoia */
159 180
160 if (est1 - est2 < 0) { 181 /*
161 rs.entropy = 0; 182 * Report an upper bound on the min-entropy of the seed data.
162 } else { 183 * We take the larger of the system entropy and the extra
163 rs.entropy = est1 - est2; 184 * entropy -- the system state and the extra input may or may
164 } 185 * not be independent, so we can't add them -- and clamp to the
 186 * size of the data.
 187 */
 188 systementropy = MIN(systementropy,
 189 MIN(sizeof(buf), UINT32_MAX/NBBY)*NBBY);
 190 extraentropy = MIN(extraentropy, MIN(nextra, UINT32_MAX/NBBY)*NBBY);
 191 rs.entropy = MIN(MAX(systementropy, extraentropy),
 192 MIN(sizeof(rs.data), UINT32_MAX/NBBY)*NBBY);
165 193
 194 /*
 195 * Compute the checksum on the 32-bit entropy count, in host
 196 * byte order (XXX this means it is not portable across
 197 * different-endian platforms!), followed by the seed data.
 198 */
166 SHA1Init(&s); 199 SHA1Init(&s);
167 SHA1Update(&s, (uint8_t *)&rs.entropy, sizeof(rs.entropy)); 200 SHA1Update(&s, (const uint8_t *)&rs.entropy, sizeof(rs.entropy));
168 SHA1Update(&s, rs.data, sizeof(rs.data)); 201 SHA1Update(&s, rs.data, sizeof(rs.data));
169 SHA1Final(rs.digest, &s); 202 SHA1Final(rs.digest, &s);
 203 explicit_memset(&s, 0, sizeof s); /* paranoia */
170 204
171 close(fd); 205 /*
172 unlink(filename); 206 * Write it to a temporary file and sync it before we commit.
173 fd = open(filename, O_CREAT|O_EXCL|O_WRONLY, 0600); 207 * This way either the old seed or the new seed is completely
174 if (fd < 0) { 208 * written in the expected location on disk even if the system
175 err(1, "output open"); 209 * crashes as long as the file system doesn't get corrupted too
176 } 210 * badly.
177 211 *
178 if (write(fd, &rs, sizeof(rs)) != sizeof(rs)) { 212 * If interrupted after this point and the temporary file is
179 unlink(filename); 213 * disclosed, no big deal -- either the pool was predictable to
180 fsync_range(fd, FDATASYNC|FDISKSYNC, (off_t)0, (off_t)0); 214 * begin with in which case we're hosed either way, or we've
181 err(1, "write"); 215 * just revealed some output which is not a problem.
182 } 216 */
183 fsync_range(fd, FDATASYNC|FDISKSYNC, (off_t)0, (off_t)0); 217 if ((fd = open(tmp, O_CREAT|O_TRUNC|O_WRONLY, 0600)) == -1)
184 close(fd); 218 err(1, "open seed file to save");
 219 if ((size_t)(nwrit = write(fd, &rs, sizeof rs)) != sizeof rs) {
 220 int error = errno;
 221 if (unlink(tmp) == -1)
 222 warn("unlink");
 223 if (nwrit == -1)
 224 errc(1, error, "write");
 225 else
 226 errx(1, "truncated write");
 227 }
 228 explicit_memset(&rs, 0, sizeof rs); /* paranoia */
 229 if (fsync_range(fd, FDATASYNC|FDISKSYNC, 0, 0) == -1) {
 230 int error = errno;
 231 if (unlink(tmp) == -1)
 232 warn("unlink");
 233 errc(1, error, "fsync_range");
 234 }
 235 if (close(fd) == -1)
 236 warn("close");
 237
 238 /* Rename it over the original file to commit. */
 239 if (rename(tmp, filename) == -1)
 240 err(1, "rename");
185} 241}
186 242
187static void 243static void
188do_load(const char *const filename) 244do_load(const char *filename)
189{ 245{
190 int fd; 246 char tmp[PATH_MAX];
191 rndsave_t rs, rszero; 247 int fd_seed, fd_random;
 248 rndsave_t rs;
192 rnddata_t rd; 249 rnddata_t rd;
 250 ssize_t nread, nwrit;
193 SHA1_CTX s; 251 SHA1_CTX s;
194 uint8_t digest[SHA1_DIGEST_LENGTH]; 252 uint8_t digest[SHA1_DIGEST_LENGTH];
195 253
196 fd = open(filename, O_RDWR, 0600); 254 /*
197 if (fd < 0) { 255 * The order of operations is important here:
198 err(1, "input open"); 256 *
199 } 257 * 1. Load the old seed.
200 258 * 2. Feed the old seed into the kernel.
201 unlink(filename); 259 * 3. Generate and write a new seed.
 260 * 4. Erase the old seed.
 261 *
 262 * This follows the procedure in
 263 *
 264 * Niels Ferguson, Bruce Schneier, and Tadayoshi Kohno,
 265 * _Cryptography Engineering_, Wiley, 2010, Sec. 9.6.2
 266 * `Update Seed File'.
 267 *
 268 * There is a race condition: If another process generates a
 269 * key from /dev/urandom after step (2) but before step (3),
 270 * and if the machine crashes before step (3), an adversary who
 271 * can read the disk after the crash can probably guess the
 272 * complete state of the entropy pool and thereby predict the
 273 * key.
 274 *
 275 * There's not much we can do here without some kind of
 276 * systemwide lock on /dev/urandom and without introducing an
 277 * opportunity for a crash to wipe out the entropy altogether.
 278 * To avoid this race, you should ensure that any key
 279 * generation happens _after_ `rndctl -L' has completed.
 280 */
202 281
203 if (read(fd, &rs, sizeof(rs)) != sizeof(rs)) { 282 /* Format the temporary file name. */
204 err(1, "read"); 283 if (snprintf(tmp, sizeof tmp, "%s.tmp", filename) >= PATH_MAX)
 284 errx(1, "path too long");
 285
 286 /* 1. Load the old seed. */
 287 if ((fd_seed = open(filename, O_RDWR)) == -1)
 288 err(1, "open seed file to load");
 289 if ((size_t)(nread = read(fd_seed, &rs, sizeof rs)) != sizeof rs) {
 290 if (nread == -1)
 291 err(1, "read seed");
 292 else
 293 errx(1, "seed too short");
205 } 294 }
206 295
207 memset(&rszero, 0, sizeof(rszero)); 296 /* Verify its checksum. */
208 if (pwrite(fd, &rszero, sizeof(rszero), (off_t)0) != sizeof(rszero)) 
209 err(1, "overwrite"); 
210 fsync_range(fd, FDATASYNC|FDISKSYNC, (off_t)0, (off_t)0); 
211 close(fd); 
212 
213 SHA1Init(&s); 297 SHA1Init(&s);
214 SHA1Update(&s, (uint8_t *)&rs.entropy, sizeof(rs.entropy)); 298 SHA1Update(&s, (const uint8_t *)&rs.entropy, sizeof(rs.entropy));
215 SHA1Update(&s, rs.data, sizeof(rs.data)); 299 SHA1Update(&s, rs.data, sizeof(rs.data));
216 SHA1Final(digest, &s); 300 SHA1Final(digest, &s);
217 301 if (!consttime_memequal(digest, rs.digest, sizeof(digest))) {
218 if (memcmp(digest, rs.digest, sizeof(digest))) { 302 /*
219 errx(1, "bad digest"); 303 * If the checksum doesn't match, doesn't hurt to feed
 304 * the seed in anyway, but act as though it has zero
 305 * entropy in case it was corrupted with predictable
 306 * garbage.
 307 */
 308 warnx("bad checksum");
 309 rs.entropy = 0;
220 } 310 }
221 311
 312 /* Format the ioctl request. */
222 rd.len = MIN(sizeof(rd.data), sizeof(rs.data)); 313 rd.len = MIN(sizeof(rd.data), sizeof(rs.data));
223 rd.entropy = rs.entropy; 314 rd.entropy = rs.entropy;
224 memcpy(rd.data, rs.data, MIN(sizeof(rd.data), sizeof(rs.data))); 315 memcpy(rd.data, rs.data, rd.len);
 316 explicit_memset(&rs, 0, sizeof rs); /* paranoia */
225 317
226 fd = open(_PATH_URANDOM, O_RDWR, 0644); 318 /* 2. Feed the old seed into the kernel. */
227 if (fd < 0) { 319 if ((fd_random = open(_PATH_URANDOM, O_WRONLY)) == -1)
228 err(1, "device open"); 320 err(1, "open /dev/urandom");
229 } 321 if (ioctl(fd_random, RNDADDDATA, &rd) == -1)
 322 err(1, "RNDADDDATA");
 323 if (close(fd_random) == -1)
 324 warn("close /dev/urandom");
 325 fd_random = -1; /* paranoia */
230 326
231 if (ioctl(fd, RNDADDDATA, &rd) < 0) { 327 /*
232 err(1, "ioctl"); 328 * 3. Generate and write a new seed. Note that we hash the old
 329 * seed together with whatever /dev/urandom returns in do_save.
 330 * Why? After RNDADDDATA, the input may not be distributed
 331 * immediately to /dev/urandom.
 332 */
 333 do_save(filename, rd.data, rd.len, rd.entropy);
 334 explicit_memset(&rd, 0, sizeof rd); /* paranoia */
 335
 336 /*
 337 * 4. Erase the old seed. Only effective if we're on a
 338 * fixed-address file system like ffs -- doesn't help to erase
 339 * the data on lfs, but doesn't hurt either. No need to unlink
 340 * because do_save will have already overwritten it.
 341 */
 342 memset(&rs, 0, sizeof rs);
 343 if ((size_t)(nwrit = pwrite(fd_seed, &rs, sizeof rs, 0)) !=
 344 sizeof rs) {
 345 if (nwrit == -1)
 346 err(1, "overwrite old seed");
 347 else
 348 errx(1, "truncated overwrite");
233 } 349 }
234 close(fd); 350 if (fsync_range(fd_seed, FDATASYNC|FDISKSYNC, 0, 0) == -1)
 351 err(1, "fsync_range");
235} 352}
236 353
237static void 354static void
238do_ioctl(rndctl_t *rctl) 355do_ioctl(rndctl_t *rctl)
239{ 356{
240 int fd; 357 int fd;
241 int res; 358 int res;
242 359
243 fd = open(_PATH_URANDOM, O_RDONLY, 0644); 360 fd = open(_PATH_URANDOM, O_RDONLY, 0644);
244 if (fd < 0) 361 if (fd < 0)
245 err(1, "open"); 362 err(1, "open");
246 363
247 res = ioctl(fd, RNDCTL, rctl); 364 res = ioctl(fd, RNDCTL, rctl);
248 if (res < 0) 365 if (res < 0)
249 err(1, "ioctl(RNDCTL)"); 366 err(1, "ioctl(RNDCTL)");
250 367
251 close(fd); 368 close(fd);
252} 369}
253 370
254static char * 371static char *
255strflags(u_int32_t fl) 372strflags(u_int32_t fl)
256{ 373{
257 static char str[512]; 374 static char str[512];
258 375
259 str[0] = '\0'; 376 str[0] = '\0';
260 if (fl & RND_FLAG_NO_ESTIMATE) 377 if (fl & RND_FLAG_NO_ESTIMATE)
261 ; 378 ;
262 else 379 else
263 strlcat(str, "estimate, ", sizeof(str)); 380 strlcat(str, "estimate, ", sizeof(str));
264 381
265 if (fl & RND_FLAG_NO_COLLECT) 382 if (fl & RND_FLAG_NO_COLLECT)
266 ; 383 ;
267 else 384 else
268 strlcat(str, "collect, ", sizeof(str)); 385 strlcat(str, "collect, ", sizeof(str));
269 386
270 if (fl & RND_FLAG_COLLECT_VALUE) 387 if (fl & RND_FLAG_COLLECT_VALUE)
271 strlcat(str, "v, ", sizeof(str)); 388 strlcat(str, "v, ", sizeof(str));
272 if (fl & RND_FLAG_COLLECT_TIME) 389 if (fl & RND_FLAG_COLLECT_TIME)
273 strlcat(str, "t, ", sizeof(str)); 390 strlcat(str, "t, ", sizeof(str));
274 if (fl & RND_FLAG_ESTIMATE_VALUE) 391 if (fl & RND_FLAG_ESTIMATE_VALUE)
275 strlcat(str, "dv, ", sizeof(str)); 392 strlcat(str, "dv, ", sizeof(str));
276 if (fl & RND_FLAG_ESTIMATE_TIME) 393 if (fl & RND_FLAG_ESTIMATE_TIME)
277 strlcat(str, "dt, ", sizeof(str)); 394 strlcat(str, "dt, ", sizeof(str));
278 395
279 if (str[strlen(str) - 2] == ',') 396 if (str[strlen(str) - 2] == ',')
280 str[strlen(str) - 2] = '\0'; 397 str[strlen(str) - 2] = '\0';
281 398
282 return (str); 399 return (str);
283} 400}
284 401
285#define HEADER "Source Bits Type Flags\n" 402#define HEADER "Source Bits Type Flags\n"
286 403
287static void 404static void
288do_list(int all, u_int32_t type, char *name) 405do_list(int all, u_int32_t type, char *name)
289{ 406{
290 rndstat_est_t rstat; 407 rndstat_est_t rstat;
291 rndstat_est_name_t rstat_name; 408 rndstat_est_name_t rstat_name;
292 int fd; 409 int fd;
293 int res; 410 int res;
294 uint32_t i; 411 uint32_t i;
295 u_int32_t start; 412 u_int32_t start;
296 413
297 fd = open(_PATH_URANDOM, O_RDONLY, 0644); 414 fd = open(_PATH_URANDOM, O_RDONLY, 0644);
298 if (fd < 0) 415 if (fd < 0)
299 err(1, "open"); 416 err(1, "open");
300 417
301 if (all == 0 && type == 0xff) { 418 if (all == 0 && type == 0xff) {
302 strncpy(rstat_name.name, name, sizeof(rstat_name.name)); 419 strncpy(rstat_name.name, name, sizeof(rstat_name.name));
303 res = ioctl(fd, RNDGETESTNAME, &rstat_name); 420 res = ioctl(fd, RNDGETESTNAME, &rstat_name);
304 if (res < 0) 421 if (res < 0)
305 err(1, "ioctl(RNDGETESTNAME)"); 422 err(1, "ioctl(RNDGETESTNAME)");
306 printf(HEADER); 423 printf(HEADER);
307 printf("%-16s %10u %-4s %s\n", 424 printf("%-16s %10u %-4s %s\n",
308 rstat_name.source.rt.name, 425 rstat_name.source.rt.name,
309 rstat_name.source.rt.total, 426 rstat_name.source.rt.total,
310 find_name(rstat_name.source.rt.type), 427 find_name(rstat_name.source.rt.type),
311 strflags(rstat_name.source.rt.flags)); 428 strflags(rstat_name.source.rt.flags));
312 if (vflag) { 429 if (vflag) {
313 printf("\tDt samples = %d\n", 430 printf("\tDt samples = %d\n",
314 rstat_name.source.dt_samples); 431 rstat_name.source.dt_samples);
315 printf("\tDt bits = %d\n", 432 printf("\tDt bits = %d\n",
316 rstat_name.source.dt_total); 433 rstat_name.source.dt_total);
317 printf("\tDv samples = %d\n", 434 printf("\tDv samples = %d\n",
318 rstat_name.source.dv_samples); 435 rstat_name.source.dv_samples);
319 printf("\tDv bits = %d\n", 436 printf("\tDv bits = %d\n",
320 rstat_name.source.dv_total); 437 rstat_name.source.dv_total);
321 } 438 }
322 close(fd); 439 close(fd);
323 return; 440 return;
324 } 441 }
325 442
326 /* 443 /*
327 * Run through all the devices present in the system, and either 444 * Run through all the devices present in the system, and either
328 * print out ones that match, or print out all of them. 445 * print out ones that match, or print out all of them.
329 */ 446 */
330 printf(HEADER); 447 printf(HEADER);
331 start = 0; 448 start = 0;
332 for (;;) { 449 for (;;) {
333 rstat.count = RND_MAXSTATCOUNT; 450 rstat.count = RND_MAXSTATCOUNT;
334 rstat.start = start; 451 rstat.start = start;
335 res = ioctl(fd, RNDGETESTNUM, &rstat); 452 res = ioctl(fd, RNDGETESTNUM, &rstat);
336 if (res < 0) 453 if (res < 0)
337 err(1, "ioctl(RNDGETESTNUM)"); 454 err(1, "ioctl(RNDGETESTNUM)");
338 455
339 if (rstat.count == 0) 456 if (rstat.count == 0)
340 break; 457 break;
341 458
342 for (i = 0; i < rstat.count; i++) { 459 for (i = 0; i < rstat.count; i++) {
343 if (all != 0 || 460 if (all != 0 ||
344 type == rstat.source[i].rt.type) 461 type == rstat.source[i].rt.type)
345 printf("%-16s %10u %-4s %s\n", 462 printf("%-16s %10u %-4s %s\n",
346 rstat.source[i].rt.name, 463 rstat.source[i].rt.name,
347 rstat.source[i].rt.total, 464 rstat.source[i].rt.total,
348 find_name(rstat.source[i].rt.type), 465 find_name(rstat.source[i].rt.type),
349 strflags(rstat.source[i].rt.flags)); 466 strflags(rstat.source[i].rt.flags));
350 if (vflag) { 467 if (vflag) {
351 printf("\tDt samples = %d\n", 468 printf("\tDt samples = %d\n",
352 rstat.source[i].dt_samples); 469 rstat.source[i].dt_samples);
353 printf("\tDt bits = %d\n", 470 printf("\tDt bits = %d\n",
354 rstat.source[i].dt_total); 471 rstat.source[i].dt_total);
355 printf("\tDv samples = %d\n", 472 printf("\tDv samples = %d\n",
356 rstat.source[i].dv_samples); 473 rstat.source[i].dv_samples);
357 printf("\tDv bits = %d\n", 474 printf("\tDv bits = %d\n",
358 rstat.source[i].dv_total); 475 rstat.source[i].dv_total);
359 } 476 }
360 } 477 }
361 start += rstat.count; 478 start += rstat.count;
362 } 479 }
363 480
364 close(fd); 481 close(fd);
365} 482}
366 483
367static void 484static void
368do_stats(void) 485do_stats(void)
369{ 486{
370 rndpoolstat_t rs; 487 rndpoolstat_t rs;
371 int fd; 488 int fd;
372 489
373 fd = open(_PATH_URANDOM, O_RDONLY, 0644); 490 fd = open(_PATH_URANDOM, O_RDONLY, 0644);
374 if (fd < 0) 491 if (fd < 0)
375 err(1, "open"); 492 err(1, "open");
376 493
377 if (ioctl(fd, RNDGETPOOLSTAT, &rs) < 0) 494 if (ioctl(fd, RNDGETPOOLSTAT, &rs) < 0)
378 err(1, "ioctl(RNDGETPOOLSTAT)"); 495 err(1, "ioctl(RNDGETPOOLSTAT)");
379 496
380 printf("\t%9u bits mixed into pool\n", rs.added); 497 printf("\t%9u bits mixed into pool\n", rs.added);
381 printf("\t%9u bits currently stored in pool (max %u)\n", 498 printf("\t%9u bits currently stored in pool (max %u)\n",
382 rs.curentropy, rs.maxentropy); 499 rs.curentropy, rs.maxentropy);
383 printf("\t%9u bits of entropy discarded due to full pool\n", 500 printf("\t%9u bits of entropy discarded due to full pool\n",
384 rs.discarded); 501 rs.discarded);
385 printf("\t%9u hard-random bits generated\n", rs.removed); 502 printf("\t%9u hard-random bits generated\n", rs.removed);
386 printf("\t%9u pseudo-random bits generated\n", rs.generated); 503 printf("\t%9u pseudo-random bits generated\n", rs.generated);
387 504
388 close(fd); 505 close(fd);
389} 506}
390 507
391int 508int
392main(int argc, char **argv) 509main(int argc, char **argv)
393{ 510{
394 rndctl_t rctl; 511 rndctl_t rctl;
395 int ch, cmd, lflag, mflag, sflag; 512 int ch, cmd, lflag, mflag, sflag;
396 u_int32_t type; 513 u_int32_t type;
397 char name[16]; 514 char name[16];
398 const char *filename = NULL; 515 const char *filename = NULL;
399 516
 517 if (SHA3_Selftest() != 0)
 518 errx(1, "SHA-3 self-test failed");
 519
400 rctl.mask = 0; 520 rctl.mask = 0;
401 rctl.flags = 0; 521 rctl.flags = 0;
402 522
403 cmd = 0; 523 cmd = 0;
404 lflag = 0; 524 lflag = 0;
405 mflag = 0; 525 mflag = 0;
406 sflag = 0; 526 sflag = 0;
407 type = 0xff; 527 type = 0xff;
408 528
409 while ((ch = getopt(argc, argv, "CES:L:celt:d:sv")) != -1) { 529 while ((ch = getopt(argc, argv, "CES:L:celt:d:sv")) != -1) {
410 switch (ch) { 530 switch (ch) {
411 case 'C': 531 case 'C':
412 rctl.flags |= RND_FLAG_NO_COLLECT; 532 rctl.flags |= RND_FLAG_NO_COLLECT;
413 rctl.mask |= RND_FLAG_NO_COLLECT; 533 rctl.mask |= RND_FLAG_NO_COLLECT;
414 mflag++; 534 mflag++;
415 break; 535 break;
416 case 'E': 536 case 'E':
417 rctl.flags |= RND_FLAG_NO_ESTIMATE; 537 rctl.flags |= RND_FLAG_NO_ESTIMATE;
418 rctl.mask |= RND_FLAG_NO_ESTIMATE; 538 rctl.mask |= RND_FLAG_NO_ESTIMATE;
419 mflag++; 539 mflag++;
420 break; 540 break;
421 case 'L': 541 case 'L':
422 if (cmd != 0) 542 if (cmd != 0)
423 usage(); 543 usage();
424 cmd = 'L'; 544 cmd = 'L';
425 filename = optarg; 545 filename = optarg;
426 break; 546 break;
427 case 'S': 547 case 'S':
428 if (cmd != 0) 548 if (cmd != 0)
429 usage(); 549 usage();
430 cmd = 'S'; 550 cmd = 'S';
431 filename = optarg; 551 filename = optarg;
432 break; 552 break;
433 case 'c': 553 case 'c':
434 rctl.flags &= ~RND_FLAG_NO_COLLECT; 554 rctl.flags &= ~RND_FLAG_NO_COLLECT;
435 rctl.mask |= RND_FLAG_NO_COLLECT; 555 rctl.mask |= RND_FLAG_NO_COLLECT;
436 mflag++; 556 mflag++;
437 break; 557 break;
438 case 'e': 558 case 'e':
439 rctl.flags &= ~RND_FLAG_NO_ESTIMATE; 559 rctl.flags &= ~RND_FLAG_NO_ESTIMATE;
440 rctl.mask |= RND_FLAG_NO_ESTIMATE; 560 rctl.mask |= RND_FLAG_NO_ESTIMATE;
441 mflag++; 561 mflag++;
442 break; 562 break;
443 case 'l': 563 case 'l':
444 lflag++; 564 lflag++;
445 break; 565 break;
446 case 't': 566 case 't':
447 if (cmd != 0) 567 if (cmd != 0)
448 usage(); 568 usage();
449 cmd = 't'; 569 cmd = 't';
450 570
451 type = find_type(optarg); 571 type = find_type(optarg);
452 break; 572 break;
453 case 'd': 573 case 'd':
454 if (cmd != 0) 574 if (cmd != 0)
455 usage(); 575 usage();
456 cmd = 'd'; 576 cmd = 'd';
457 577
458 type = 0xff; 578 type = 0xff;
459 strlcpy(name, optarg, sizeof(name)); 579 strlcpy(name, optarg, sizeof(name));
460 break; 580 break;
461 case 's': 581 case 's':
462 sflag++; 582 sflag++;
463 break; 583 break;
464 case 'v': 584 case 'v':
465 vflag++; 585 vflag++;
466 break; 586 break;
467 case '?': 587 case '?':
468 default: 588 default:
469 usage(); 589 usage();
470 } 590 }
471 } 591 }
472 argc -= optind; 592 argc -= optind;
473 argv += optind; 593 argv += optind;
474 594
475 /* 595 /*
476 * No leftover non-option arguments. 596 * No leftover non-option arguments.
477 */ 597 */
478 if (argc > 0) 598 if (argc > 0)
479 usage(); 599 usage();
480 600
481 /* 601 /*
482 * Save. 602 * Save.
483 */ 603 */
484 if (cmd == 'S') { 604 if (cmd == 'S') {
485 do_save(filename); 605 do_save(filename, NULL, 0, 0);
486 exit(0); 606 exit(0);
487 } 607 }
488 608
489 /* 609 /*
490 * Load. 610 * Load.
491 */ 611 */
492 if (cmd == 'L') { 612 if (cmd == 'L') {
493 do_load(filename); 613 do_load(filename);
494 exit(0); 614 exit(0);
495 } 615 }
496 616
497 /* 617 /*
498 * Cannot list and modify at the same time. 618 * Cannot list and modify at the same time.
499 */ 619 */
500 if ((lflag != 0 || sflag != 0) && mflag != 0) 620 if ((lflag != 0 || sflag != 0) && mflag != 0)
501 usage(); 621 usage();
502 622
503 /* 623 /*
504 * Bomb out on no-ops. 624 * Bomb out on no-ops.
505 */ 625 */
506 if (lflag == 0 && mflag == 0 && sflag == 0) 626 if (lflag == 0 && mflag == 0 && sflag == 0)
507 usage(); 627 usage();
508 628
509 /* 629 /*
510 * If not listing, we need a device name or a type. 630 * If not listing, we need a device name or a type.
511 */ 631 */
512 if (lflag == 0 && cmd == 0 && sflag == 0) 632 if (lflag == 0 && cmd == 0 && sflag == 0)
513 usage(); 633 usage();
514 634
515 /* 635 /*
516 * Modify request. 636 * Modify request.
517 */ 637 */
518 if (mflag != 0) { 638 if (mflag != 0) {
519 rctl.type = type; 639 rctl.type = type;
520 strncpy(rctl.name, name, sizeof(rctl.name)); 640 strncpy(rctl.name, name, sizeof(rctl.name));
521 do_ioctl(&rctl); 641 do_ioctl(&rctl);
522 642
523 exit(0); 643 exit(0);
524 } 644 }
525 645
526 /* 646 /*
527 * List sources. 647 * List sources.
528 */ 648 */
529 if (lflag != 0) 649 if (lflag != 0)
530 do_list(cmd == 0, type, name); 650 do_list(cmd == 0, type, name);
531 651
532 if (sflag != 0) 652 if (sflag != 0)
533 do_stats(); 653 do_stats();
534 654
535 exit(0); 655 exit(0);
536} 656}