diff options
Diffstat (limited to 'src/pluto/rnd.c')
-rw-r--r-- | src/pluto/rnd.c | 250 |
1 files changed, 250 insertions, 0 deletions
diff --git a/src/pluto/rnd.c b/src/pluto/rnd.c new file mode 100644 index 000000000..812882c6b --- /dev/null +++ b/src/pluto/rnd.c @@ -0,0 +1,250 @@ +/* randomness machinery + * Copyright (C) 1997 Angelos D. Keromytis. + * Copyright (C) 1998-2001 D. Hugh Redelmeier. + * + * This program is free software; you can redistribute it and/or modify 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. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * 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 GNU General Public License + * for more details. + * + * RCSID $Id: rnd.c,v 1.3 2005/09/08 16:26:30 as Exp $ + */ + +/* A true random number generator (we hope) + * + * Under LINUX ("linux" predefined), use /dev/urandom. + * Under OpenBSD ("__OpenBSD__" predefined), use arc4random(). + * Otherwise use our own random number generator based on clock skew. + * I (ADK) first heard of the idea from John Ioannidis, who heard it + * from Matt Blaze and/or Jack Lacy. + * ??? Why is mixing need for linux but not OpenBSD? + */ + +/* Pluto's uses of randomness: + * + * - Setting up the "secret_of_the_day". This changes every hour! 20 + * bytes a shot. It is used in building responder cookies. + * + * - generating initiator cookies (8 bytes, once per Phase 1 initiation). + * + * - 32 bytes per DH local secret. Once per Main Mode exchange and once + * per Quick Mode Exchange with PFS. (Size is our choice, with + * tradeoffs.) + * + * - 16 bytes per nonce we generate. Once per Main Mode exchange and + * once per Quick Mode exchange. (Again, we choose the size.) + * + * - 4 bytes per SPI number that we generate. We choose the SPIs for all + * inbound SPIs, one to three per IPSEC SA (one for AH (rare, probably) + * one for ESP (almost always), and one for tunnel (very common)). + * I don't actually know how the kernel would generate these numbers -- + * currently Pluto generates them; this isn't the way things will be + * done in the future. + * + * - 4 bytes per Message ID we need to generate. One per Quick Mode + * exchange. Eventually, one per informational exchange. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <signal.h> +#include <unistd.h> +#include <errno.h> +#include <sys/time.h> +#include <fcntl.h> + +#include <freeswan.h> + +#include "sha1.h" +#include "constants.h" +#include "defs.h" +#include "rnd.h" +#include "log.h" +#include "timer.h" + +#ifdef linux +# define USE_DEV_RANDOM 1 +# define RANDOM_PATH DEV_URANDOM +#else +# ifdef __OpenBSD__ +# define USE_ARC4RANDOM +# else +# define USE_CLOCK_SLEW +# endif +#endif + +#ifdef USE_ARC4RANDOM + +#define get_rnd_byte() (arc4random() % 256) + +#else /**** start of large #else ****/ + +#ifdef USE_DEV_RANDOM +static int random_fd = NULL_FD; +#endif + +#define RANDOM_POOL_SIZE SHA1_DIGEST_SIZE +static u_char random_pool[RANDOM_POOL_SIZE]; + +#ifdef USE_DEV_RANDOM + +/* Generate (what we hope is) a true random byte using /dev/urandom */ +static u_char +generate_rnd_byte(void) +{ + u_char c; + + if (read(random_fd, &c, sizeof(c)) == -1) + exit_log_errno((e, "read() failed in get_rnd_byte()")); + + return c; +} + +#else /* !USE_DEV_RANDOM */ + +/* Generate (what we hope is) a true random byte using the clock skew trick. + * Note: this code is not maintained! In particular, LINUX signal(2) + * semantics changed with glibc2 (and not for the better). It isn't clear + * that this code will work. We keep the code because someday it might + * come in handy. + */ +# error "This code is not maintained. Please define USE_DEV_RANDOM." + +static volatile sig_atomic_t i, j, k; + +/* timer signal handler */ +static void +rnd_handler(int ignore_me UNUSED) +{ + k <<= 1; /* Shift left by 1 */ + j++; + k |= (i & 0x1); /* Get lsbit of counter */ + + if (j != 8) + signal(SIGVTALRM, rnd_handler); +} + +static u_char +generate_rnd_byte(void) +{ + struct itimerval tmval, ntmval; + +# ifdef NEVER /* ??? */ +# ifdef linux + int mask = siggetmask(); + + mask |= SIGVTALRM; + sigsetmask(mask); +# endif +# endif + + i = 0; + j = 0; + + ntmval.it_interval.tv_sec = 0; + ntmval.it_interval.tv_usec = 1; + ntmval.it_value.tv_sec = 0; + ntmval.it_value.tv_usec = 1; + signal(SIGVTALRM, rnd_handler); + setitimer(ITIMER_VIRTUAL, &ntmval, &tmval); + + while (j != 8) + i++; + + setitimer(ITIMER_VIRTUAL, &tmval, &ntmval); + signal(SIGVTALRM, SIG_IGN); + +# ifdef NEVER /* ??? */ +# ifdef linux + mask ^= SIGVTALRM; + sigsetmask(mask); +# endif +# endif + + return k; +} + +#endif /* !USE_DEV_RANDOM */ + +static void +mix_pool(void) +{ + SHA1_CTX ctx; + + SHA1Init(&ctx); + SHA1Update(&ctx, random_pool, RANDOM_POOL_SIZE); + SHA1Final(random_pool, &ctx); +} + +/* + * Get a single random byte. + */ +static u_char +get_rnd_byte(void) +{ + random_pool[RANDOM_POOL_SIZE - 1] = generate_rnd_byte(); + random_pool[0] = generate_rnd_byte(); + mix_pool(); + return random_pool[0]; +} + +#endif /* !USE_ARC4RANDOM */ /**** end of large #else ****/ + +void +get_rnd_bytes(u_char *buffer, int length) +{ + int i; + + for (i = 0; i < length; i++) + buffer[i] = get_rnd_byte(); +} + +/* + * Initialize the random pool. + */ +void +init_rnd_pool(void) +{ +#ifndef USE_ARC4RANDOM +# ifdef USE_DEV_RANDOM + DBG(DBG_KLIPS, DBG_log("opening %s", RANDOM_PATH)); + random_fd = open(RANDOM_PATH, O_RDONLY); + if (random_fd == -1) + exit_log_errno((e, "open of %s failed in init_rnd_pool()", RANDOM_PATH)); + fcntl(random_fd, F_SETFD, FD_CLOEXEC); +# endif + + get_rnd_bytes(random_pool, RANDOM_POOL_SIZE); + mix_pool(); +#endif /* !USE_ARC4RANDOM */ + + /* start of rand(3) on the right foot */ + { + unsigned int seed; + + get_rnd_bytes((void *)&seed, sizeof(seed)); + srand(seed); + } +} + +u_char secret_of_the_day[SHA1_DIGEST_SIZE]; + +#ifndef NO_PLUTO + +void +init_secret(void) +{ + /* + * Generate the secret value for responder cookies, and + * schedule an event for refresh. + */ + get_rnd_bytes(secret_of_the_day, sizeof(secret_of_the_day)); + event_schedule(EVENT_REINIT_SECRET, EVENT_REINIT_SECRET_DELAY, NULL); +} + +#endif /* NO_PLUTO */ |