*
* rngd reads data from a hardware random number generator, verifies it
* looks like random data, and adds it to /dev/random's entropy store.
- *
+ *
* In theory, this should allow you to read very quickly from
* /dev/random; rngd also adds bytes to the entropy store periodically
* when it's full, which makes predicting the entropy store's contents
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
- *
+ *
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
#include <string.h>
#include <argp.h>
#include <syslog.h>
+#include <signal.h>
#include "rngd.h"
#include "fips.h"
*/
/* Background/daemon mode */
-int am_daemon; /* Nonzero if we went daemon */
+bool am_daemon; /* True if we went daemon */
+
+bool server_running = true; /* set to false, to stop daemon */
/* Command line arguments and processing */
-const char *argp_program_version =
+const char *argp_program_version =
"rngd " VERSION "\n"
"Copyright 2001-2004 Jeff Garzik\n"
"Copyright (c) 2001 by Philipp Rumpf\n"
"Kernel device used for random number output (default: /dev/random)" },
{ "rng-device", 'r', "file", 0,
- "Kernel device used for random number input (default: /dev/hwrandom)" },
+ "Kernel device used for random number input (default: /dev/hwrng)" },
+
+ { "pid-file", 'p', "file", 0,
+ "File used for recording daemon PID, and multiple exclusion (default: /var/run/rngd.pid)" },
{ "random-step", 's', "nnn", 0,
"Number of bytes written to random-device at a time (default: 64)" },
{ "fill-watermark", 'W', "n", 0,
"Do not stop feeding entropy to random-device until at least n bits of entropy are available in the pool (default: 2048), 0 <= n <= 4096" },
- { "timeout", 't', "nnn", 0,
- "Interval written to random-device when the entropy pool is full, in seconds (default: 60)" },
+ { "quiet", 'q', 0, 0, "Suppress error messages" },
+
+ { "verbose" ,'v', 0, 0, "Report available entropy sources" },
+
+ { "no-drng", 'd', "1|0", 0,
+ "Do not use drng as a source of random number input (default: 0)" },
+
+ { "no-tpm", 'n', "1|0", 0,
+ "Do not use tpm as a source of random number input (default: 0)" },
{ 0 },
};
static struct arguments default_arguments = {
- .rng_name = "/dev/hwrandom",
.random_name = "/dev/random",
- .poll_timeout = 60,
+ .pid_file = "/var/run/rngd.pid",
.random_step = 64,
- .fill_watermark = 2048,
- .daemon = 1,
+ .daemon = true,
+ .enable_drng = true,
+ .enable_tpm = true,
+ .quiet = false,
+ .verbose = false,
};
struct arguments *arguments = &default_arguments;
+static struct rng rng_default = {
+ .rng_name = "/dev/hwrng",
+ .rng_fd = -1,
+ .xread = xread,
+};
+
+static struct rng rng_drng = {
+ .rng_name = "drng",
+ .rng_fd = -1,
+ .xread = xread_drng,
+};
+
+static struct rng rng_tpm = {
+ .rng_name = "/dev/tpm0",
+ .rng_fd = -1,
+ .xread = xread_tpm,
+};
+
+struct rng *rng_list;
/*
* command line processing
case 'o':
arguments->random_name = arg;
break;
- case 'r':
- arguments->rng_name = arg;
+ case 'p':
+ arguments->pid_file = arg;
break;
- case 't': {
- float f;
- if (sscanf(arg, "%f", &f) == 0)
- argp_usage(state);
- else
- arguments->poll_timeout = f;
+ case 'r':
+ rng_default.rng_name = arg;
break;
- }
-
case 'f':
- arguments->daemon = 0;
+ arguments->daemon = false;
break;
case 'b':
- arguments->daemon = 1;
+ arguments->daemon = true;
break;
case 's':
if (sscanf(arg, "%i", &arguments->random_step) == 0)
arguments->fill_watermark = n;
break;
}
+ case 'q':
+ arguments->quiet = true;
+ break;
+ case 'v':
+ arguments->verbose = true;
+ break;
+ case 'd': {
+ int n;
+ if ((sscanf(arg,"%i", &n) == 0) || ((n | 1)!=1))
+ argp_usage(state);
+ else
+ arguments->enable_drng = false;
+ break;
+ }
+ case 'n': {
+ int n;
+ if ((sscanf(arg,"%i", &n) == 0) || ((n | 1)!=1))
+ argp_usage(state);
+ else
+ arguments->enable_tpm = false;
+ break;
+ }
default:
return ARGP_ERR_UNKNOWN;
static struct argp argp = { options, parse_opt, NULL, doc };
-static void do_loop(int random_step,
- double poll_timeout)
+static int update_kernel_random(int random_step,
+ unsigned char *buf, fips_ctx_t *fipsctx_in)
{
- unsigned char buf[FIPS_RNG_BUFFER_SIZE];
unsigned char *p;
int fips;
- for (;;) {
- xread(buf, sizeof buf);
+ fips = fips_run_rng_test(fipsctx_in, buf);
+ if (fips)
+ return 1;
- fips = fips_run_rng_test(&fipsctx, buf);
+ for (p = buf; p + random_step <= &buf[FIPS_RNG_BUFFER_SIZE];
+ p += random_step) {
+ random_add_entropy(p, random_step);
+ random_sleep();
+ }
+ return 0;
+}
- if (fips) {
- message(LOG_DAEMON|LOG_ERR, "failed fips test\n");
- sleep(1);
- continue;
+static void do_loop(int random_step)
+{
+ unsigned char buf[FIPS_RNG_BUFFER_SIZE];
+ int retval = 0;
+ int no_work = 0;
+
+ while (no_work < 100) {
+ struct rng *iter;
+ bool work_done;
+
+ work_done = false;
+ for (iter = rng_list; iter; iter = iter->next)
+ {
+ int rc;
+
+ if (!server_running)
+ return;
+
+ if (iter->disabled)
+ continue; /* failed, no work */
+
+ retval = iter->xread(buf, sizeof buf, iter);
+ if (retval)
+ continue; /* failed, no work */
+
+ work_done = true;
+
+ rc = update_kernel_random(random_step,
+ buf, iter->fipsctx);
+ if (rc == 0) {
+ iter->success++;
+ if (iter->success >= RNG_OK_CREDIT) {
+ if (iter->failures)
+ iter->failures--;
+ iter->success = 0;
+ }
+ break; /* succeeded, work done */
+ }
+
+ iter->failures++;
+ if (iter->failures == MAX_RNG_FAILURES) {
+ if (!arguments->quiet)
+ message(LOG_DAEMON|LOG_ERR,
+ "too many FIPS failures, disabling entropy source\n");
+ iter->disabled = true;
+ }
}
- for (p = buf; p + random_step <= &buf[sizeof buf];
- p += random_step) {
- random_add_entropy(p, random_step);
- random_sleep(poll_timeout);
- }
+ if (!work_done)
+ no_work++;
}
+
+ if (!arguments->quiet)
+ message(LOG_DAEMON|LOG_ERR,
+ "No entropy sources working, exiting rngd\n");
}
+static void term_signal(int signo)
+{
+ server_running = false;
+}
int main(int argc, char **argv)
{
+ int rc_rng = 1;
+ int rc_drng = 1;
+ int rc_tpm = 1;
+ int pid_fd = -1;
+
+ openlog("rngd", 0, LOG_DAEMON);
+
+ /* Get the default watermark level for this platform */
+ arguments->fill_watermark = default_watermark();
+
+ /* Parsing of commandline parameters */
argp_parse(&argp, argc, argv, 0, 0, arguments);
- /* Init entropy source, and open TRNG device */
- init_entropy_source(arguments->rng_name);
+ /* Init entropy sources, and open TRNG device */
+ rc_rng = init_entropy_source(&rng_default);
+ if (arguments->enable_drng)
+ rc_drng = init_drng_entropy_source(&rng_drng);
+ if (arguments->enable_tpm && rc_rng)
+ rc_tpm = init_tpm_entropy_source(&rng_tpm);
+
+ if (rc_rng && rc_drng && rc_tpm) {
+ if (!arguments->quiet) {
+ message(LOG_DAEMON|LOG_ERR,
+ "can't open any entropy source");
+ message(LOG_DAEMON|LOG_ERR,
+ "Maybe RNG device modules are not loaded\n");
+ }
+ return 1;
+ }
+
+ if (arguments->verbose) {
+ printf("Available entropy sources:\n");
+ if (!rc_rng)
+ printf("\tIntel/AMD hardware rng\n");
+ if (!rc_drng)
+ printf("\tDRNG\n");
+ if (!rc_tpm)
+ printf("\tTPM\n");
+ }
+
+ if (rc_rng
+ && (rc_drng || !arguments->enable_drng)
+ && (rc_tpm || !arguments->enable_tpm)) {
+ if (!arguments->quiet)
+ message(LOG_DAEMON|LOG_ERR,
+ "No entropy source available, shutting down\n");
+ return 1;
+ }
/* Init entropy sink and open random device */
init_kernel_rng(arguments->random_name);
if (arguments->daemon) {
- am_daemon = 1;
+ am_daemon = true;
if (daemon(0, 0) < 0) {
- fprintf(stderr, "can't daemonize: %s\n",
+ if(!arguments->quiet)
+ fprintf(stderr, "can't daemonize: %s\n",
strerror(errno));
return 1;
}
- openlog("rngd", 0, LOG_DAEMON);
+ /* require valid, locked PID file to proceed */
+ pid_fd = write_pid_file(arguments->pid_file);
+ if (pid_fd < 0)
+ return 1;
+
+ signal(SIGHUP, SIG_IGN);
+ signal(SIGPIPE, SIG_IGN);
+ signal(SIGINT, term_signal);
+ signal(SIGTERM, term_signal);
}
- do_loop(arguments->random_step,
- arguments->poll_timeout ? : -1.0);
+ do_loop(arguments->random_step);
+
+ if (pid_fd >= 0)
+ unlink(arguments->pid_file);
return 0;
}