From 9e3331ea11585533603f7c1b1de5f28fb851d13b Mon Sep 17 00:00:00 2001 From: Tom Kistner Date: Fri, 16 Oct 2009 09:10:40 +0000 Subject: [PATCH] Bugzilla #722 --- src/src/expand.c | 88 ++++++++++++++++++++++++++++++++++++++++++- src/src/functions.h | 3 +- src/src/host.c | 7 +++- src/src/tls-openssl.c | 76 +++++++++++++++++++++++++++++++++++-- 4 files changed, 167 insertions(+), 7 deletions(-) diff --git a/src/src/expand.c b/src/src/expand.c index 47453dc6d..0e7d31d97 100644 --- a/src/src/expand.c +++ b/src/src/expand.c @@ -1,4 +1,4 @@ -/* $Cambridge: exim/src/src/expand.c,v 1.103 2009/10/15 08:27:37 tom Exp $ */ +/* $Cambridge: exim/src/src/expand.c,v 1.104 2009/10/16 09:10:40 tom Exp $ */ /************************************************* * Exim - an Internet mail transport agent * @@ -187,6 +187,7 @@ static uschar *op_table_main[] = { US"nh", US"nhash", US"quote", + US"randint", US"rfc2047", US"rfc2047d", US"rxquote", @@ -219,6 +220,7 @@ enum { EOP_NH, EOP_NHASH, EOP_QUOTE, + EOP_RANDINT, EOP_RFC2047, EOP_RFC2047D, EOP_RXQUOTE, @@ -759,6 +761,75 @@ return rc; +/************************************************* +* Pseudo-random number generation * +*************************************************/ + +/* Pseudo-random number generation. The result is not "expected" to be +cryptographically strong but not so weak that someone will shoot themselves +in the foot using it as a nonce in some email header scheme or whatever +weirdness they'll twist this into. The result should ideally handle fork(). + +However, if we're stuck unable to provide this, then we'll fall back to +appallingly bad randomness. + +If SUPPORT_TLS is defined and OpenSSL is used, then this will not be used. +The GNUTLS randomness functions found do not seem amenable to extracting +random numbers outside of a TLS context. Any volunteers? + +Arguments: + max range maximum +Returns a random number in range [0, max-1] +*/ + +#if !defined(SUPPORT_TLS) || defined(USE_GNUTLS) +int +pseudo_random_number(int max) +{ + static pid_t pid = 0; + pid_t p2; +#if defined(HAVE_SRANDOM) && !defined(HAVE_SRANDOMDEV) + struct timeval tv; +#endif + + p2 = getpid(); + if (p2 != pid) + { + if (pid != 0) + { + +#ifdef HAVE_ARC4RANDOM + /* cryptographically strong randomness, common on *BSD platforms, not + so much elsewhere. Alas. */ + arc4random_stir(); +#elif defined(HAVE_SRANDOM) || defined(HAVE_SRANDOMDEV) +#ifdef HAVE_SRANDOMDEV + /* uses random(4) for seeding */ + srandomdev(); +#else + gettimeofday(&tv, NULL); + srandom(tv.tv_sec | tv.tv_usec | getpid()); +#endif +#else + /* Poor randomness and no seeding here */ +#endif + + } + pid = p2; + } + +#ifdef HAVE_ARC4RANDOM + return arc4random() % max; +#elif defined(HAVE_SRANDOM) || defined(HAVE_SRANDOMDEV) + return random() % max; +#else + /* This one returns a 16-bit number, definitely not crypto-strong */ + return random_number(max); +#endif +} + +#endif + /************************************************* * Pick out a name from a string * *************************************************/ @@ -5704,6 +5775,21 @@ while (*s != 0) continue; } + /* pseudo-random number less than N */ + + case EOP_RANDINT: + { + int max; + uschar *s; + + max = expand_string_integer(sub, TRUE); + if (expand_string_message != NULL) + goto EXPAND_FAILED; + s = string_sprintf("%d", pseudo_random_number(max)); + yield = string_cat(yield, &size, &ptr, s, Ustrlen(s)); + continue; + } + /* Unknown operator */ default: diff --git a/src/src/functions.h b/src/src/functions.h index 52f6f6b73..696d0a5db 100644 --- a/src/src/functions.h +++ b/src/src/functions.h @@ -1,4 +1,4 @@ -/* $Cambridge: exim/src/src/functions.h,v 1.45 2009/10/14 13:52:48 nm4 Exp $ */ +/* $Cambridge: exim/src/src/functions.h,v 1.46 2009/10/16 09:10:40 tom Exp $ */ /************************************************* * Exim - an Internet mail transport agent * @@ -190,6 +190,7 @@ extern uschar *parse_fix_phrase(uschar *, int, uschar *, int); extern uschar *parse_message_id(uschar *, uschar **, uschar **); extern uschar *parse_quote_2047(uschar *, int, uschar *, uschar *, int, BOOL); extern uschar *parse_date_time(uschar *str, time_t *t); +extern int pseudo_random_number(int); extern BOOL queue_action(uschar *, int, uschar **, int, int); extern void queue_check_only(void); diff --git a/src/src/host.c b/src/src/host.c index 18821035c..28daf2201 100644 --- a/src/src/host.c +++ b/src/src/host.c @@ -1,4 +1,4 @@ -/* $Cambridge: exim/src/src/host.c,v 1.29 2007/10/18 12:01:00 nm4 Exp $ */ +/* $Cambridge: exim/src/src/host.c,v 1.30 2009/10/16 09:10:40 tom Exp $ */ /************************************************* * Exim - an Internet mail transport agent * @@ -70,6 +70,9 @@ sprintf(addr, "%d.%d.%d.%d", very good for the uses to which it is put. When running the regression tests, start with a fixed seed. +If you need better, see pseudo_random_number() which is potentially stronger, +if a crypto library is available, but might end up just calling this instead. + Arguments: limit: one more than the largest number required @@ -79,6 +82,8 @@ Returns: a pseudo-random number in the range 0 to limit-1 int random_number(int limit) { +if (limit < 1) + return 0; if (random_seed == 0) { if (running_in_test_harness) random_seed = 42; else diff --git a/src/src/tls-openssl.c b/src/src/tls-openssl.c index edaed35ae..8bce3c4ca 100644 --- a/src/src/tls-openssl.c +++ b/src/src/tls-openssl.c @@ -1,4 +1,4 @@ -/* $Cambridge: exim/src/src/tls-openssl.c,v 1.16 2009/10/16 08:34:50 tom Exp $ */ +/* $Cambridge: exim/src/src/tls-openssl.c,v 1.17 2009/10/16 09:10:40 tom Exp $ */ /************************************************* * Exim - an Internet mail transport agent * @@ -26,8 +26,8 @@ functions from the OpenSSL library. */ /* Structure for collecting random data for seeding. */ typedef struct randstuff { - time_t t; - pid_t p; + struct timeval tv; + pid_t p; } randstuff; /* Local static variables */ @@ -327,7 +327,7 @@ afterwards. */ if (!RAND_status()) { randstuff r; - r.t = time(NULL); + gettimeofday(&r.tv, NULL); r.p = getpid(); RAND_seed((uschar *)(&r), sizeof(r)); @@ -1053,4 +1053,72 @@ fprintf(f, "OpenSSL compile-time version: %s\n", OPENSSL_VERSION_TEXT); fprintf(f, "OpenSSL runtime version: %s\n", SSLeay_version(SSLEAY_VERSION)); } + + + +/************************************************* +* Pseudo-random number generation * +*************************************************/ + +/* Pseudo-random number generation. The result is not expected to be +cryptographically strong but not so weak that someone will shoot themselves +in the foot using it as a nonce in input in some email header scheme or +whatever weirdness they'll twist this into. The result should handle fork() +and avoid repeating sequences. OpenSSL handles that for us. + +Arguments: + max range maximum +Returns a random number in range [0, max-1] +*/ + +int +pseudo_random_number(int max) +{ +unsigned int r; +int i, needed_len; +uschar *p; +uschar smallbuf[sizeof(r)]; + +if (max <= 1) + return 0; + +/* OpenSSL auto-seeds from /dev/random, etc, but this a double-check. */ +if (!RAND_status()) + { + randstuff r; + gettimeofday(&r.tv, NULL); + r.p = getpid(); + + RAND_seed((uschar *)(&r), sizeof(r)); + } +/* We're after pseudo-random, not random; if we still don't have enough data +in the internal PRNG then our options are limited. We could sleep and hope +for entropy to come along (prayer technique) but if the system is so depleted +in the first place then something is likely to just keep taking it. Instead, +we'll just take whatever little bit of pseudo-random we can still manage to +get. */ + +needed_len = sizeof(r); +/* Don't take 8 times more entropy than needed if int is 8 octets and we were +asked for a number less than 10. */ +for (r = max, i = 0; r; ++i) + r >>= 1; +i = (i + 7) / 8; +if (i < needed_len) + needed_len = i; + +/* We do not care if crypto-strong */ +(void) RAND_pseudo_bytes(smallbuf, needed_len); +r = 0; +for (p = smallbuf; needed_len; --needed_len, ++p) + { + r *= 256; + r += *p; + } + +/* We don't particularly care about weighted results; if someone wants +smooth distribution and cares enough then they should submit a patch then. */ +return r % max; +} + /* End of tls-openssl.c */ -- 2.25.1