]> git.ipfire.org Git - thirdparty/rng-tools.git/blobdiff - rngd.c
rngd: Allow up to a 1:1000 false error rate on FIPS tests
[thirdparty/rng-tools.git] / rngd.c
diff --git a/rngd.c b/rngd.c
index 8cb2ed186554d81a7140818727acbae1c0d1bca1..d7cd1b22706c274409b5f40dcde693c598e96ed2 100644 (file)
--- a/rngd.c
+++ b/rngd.c
@@ -3,7 +3,7 @@
  *
  * 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
@@ -15,7 +15,7 @@
  * 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
@@ -45,6 +45,7 @@
 #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"
@@ -81,7 +84,10 @@ static struct argp_option options[] = {
          "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)" },
@@ -89,22 +95,50 @@ static struct argp_option options[] = {
        { "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
@@ -115,23 +149,17 @@ static error_t parse_opt (int key, char *arg, struct argp_state *state)
        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)
@@ -145,6 +173,28 @@ static error_t parse_opt (int key, char *arg, struct argp_state *state)
                        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;
@@ -156,57 +206,165 @@ static error_t parse_opt (int key, char *arg, struct argp_state *state)
 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;
 }