summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTom Kistner <tom@duncanthrax.net>2009-10-16 09:10:40 +0000
committerTom Kistner <tom@duncanthrax.net>2009-10-16 09:10:40 +0000
commit9e3331ea11585533603f7c1b1de5f28fb851d13b (patch)
tree993c062cbf51ddb2b3ecf4e6ddb82aa4f5dde06e
parent6afc838341d15248134205300a73eceea777cee5 (diff)
Bugzilla #722
-rw-r--r--src/src/expand.c88
-rw-r--r--src/src/functions.h3
-rw-r--r--src/src/host.c7
-rw-r--r--src/src/tls-openssl.c76
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,
@@ -760,6 +762,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 */