Bugzilla #722
authorTom Kistner <tom@duncanthrax.net>
Fri, 16 Oct 2009 09:10:40 +0000 (09:10 +0000)
committerTom Kistner <tom@duncanthrax.net>
Fri, 16 Oct 2009 09:10:40 +0000 (09:10 +0000)
src/src/expand.c
src/src/functions.h
src/src/host.c
src/src/tls-openssl.c

index 47453dc..0e7d31d 100644 (file)
@@ -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:
index 52f6f6b..696d0a5 100644 (file)
@@ -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);
index 1882103..28daf22 100644 (file)
@@ -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
index edaed35..8bce3c4 100644 (file)
@@ -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 */