]> git.ipfire.org Git - thirdparty/openssh-portable.git/commitdiff
Resync arc4random with OpenBSD.
authorDarren Tucker <dtucker@dtucker.net>
Fri, 2 Sep 2022 04:28:14 +0000 (14:28 +1000)
committerDarren Tucker <dtucker@dtucker.net>
Fri, 2 Sep 2022 04:30:38 +0000 (14:30 +1000)
This brings us up to current, including djm's random-reseeding change,
as prompted by logan at cyberstorm.mu in bz#3467.  It brings the
platform-specific hooks from LibreSSL Portable, simplified to match our
use case.  ok djm@.

openbsd-compat/arc4random.c
openbsd-compat/arc4random.h [new file with mode: 0644]
openbsd-compat/openbsd-compat.h

index 2751fb839c868537b78302ed18b01b95b6891e42..ae48cce9ee6ac15af0e1fa5e4887f997437fb1ad 100644 (file)
@@ -1,11 +1,10 @@
-/* OPENBSD ORIGINAL: lib/libc/crypto/arc4random.c */
-
-/*     $OpenBSD: arc4random.c,v 1.25 2013/10/01 18:34:57 markus Exp $  */
+/*     $OpenBSD: arc4random.c,v 1.58 2022/07/31 13:41:45 tb Exp $      */
 
 /*
  * Copyright (c) 1996, David Mazieres <dm@uun.org>
  * Copyright (c) 2008, Damien Miller <djm@openbsd.org>
  * Copyright (c) 2013, Markus Friedl <markus@openbsd.org>
+ * Copyright (c) 2014, Theo de Raadt <deraadt@openbsd.org>
  *
  * Permission to use, copy, modify, and distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
  * ChaCha based random number generator for OpenBSD.
  */
 
+/* OPENBSD ORIGINAL: lib/libc/crypt/arc4random.c */
+
 #include "includes.h"
 
 #include <sys/types.h>
 
 #include <fcntl.h>
+#include <limits.h>
+#include <signal.h>
+#ifdef HAVE_STDINT_H
+#include <stdint.h>
+#endif
 #include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
+#include <sys/types.h>
+#include <sys/time.h>
 
 #ifndef HAVE_ARC4RANDOM
 
 # define getentropy(x, y) (_ssh_compat_getentropy((x), (y)))
 #endif
 
-#define MINIMUM(a, b)    (((a) < (b)) ? (a) : (b))
+#define DEF_WEAK(x)
 
 #include "log.h"
 
 #define KEYSTREAM_ONLY
 #include "chacha_private.h"
 
-#ifdef __GNUC__
+#define minimum(a, b) ((a) < (b) ? (a) : (b))
+
+#if defined(__GNUC__) || defined(_MSC_VER)
 #define inline __inline
-#else                          /* !__GNUC__ */
+#else                          /* __GNUC__ || _MSC_VER */
 #define inline
-#endif                         /* !__GNUC__ */
-
-/* OpenSSH isn't multithreaded */
-#define _ARC4_LOCK()
-#define _ARC4_UNLOCK()
+#endif                         /* !__GNUC__ && !_MSC_VER */
 
 #define KEYSZ  32
 #define IVSZ   8
 #define BLOCKSZ        64
 #define RSBUFSZ        (16*BLOCKSZ)
-static int rs_initialized;
-static pid_t rs_stir_pid;
-static chacha_ctx rs;          /* chacha context for random keystream */
-static u_char rs_buf[RSBUFSZ]; /* keystream blocks */
-static size_t rs_have;         /* valid bytes at end of rs_buf */
-static size_t rs_count;                /* bytes till reseed */
+
+#define REKEY_BASE     (1024*1024) /* NB. should be a power of 2 */
+
+/* Marked MAP_INHERIT_ZERO, so zero'd out in fork children. */
+static struct _rs {
+       size_t          rs_have;        /* valid bytes at end of rs_buf */
+       size_t          rs_count;       /* bytes till reseed */
+} *rs;
+
+/* Maybe be preserved in fork children, if _rs_allocate() decides. */
+static struct _rsx {
+       chacha_ctx      rs_chacha;      /* chacha context for random keystream */
+       u_char          rs_buf[RSBUFSZ];        /* keystream blocks */
+} *rsx;
+
+static inline int _rs_allocate(struct _rs **, struct _rsx **);
+static inline void _rs_forkdetect(void);
+#include "arc4random.h"
 
 static inline void _rs_rekey(u_char *dat, size_t datlen);
 
@@ -79,137 +97,128 @@ _rs_init(u_char *buf, size_t n)
 {
        if (n < KEYSZ + IVSZ)
                return;
-       chacha_keysetup(&rs, buf, KEYSZ * 8);
-       chacha_ivsetup(&rs, buf + KEYSZ);
+
+       if (rs == NULL) {
+               if (_rs_allocate(&rs, &rsx) == -1)
+                       _exit(1);
+       }
+
+       chacha_keysetup(&rsx->rs_chacha, buf, KEYSZ * 8);
+       chacha_ivsetup(&rsx->rs_chacha, buf + KEYSZ);
 }
 
 static void
 _rs_stir(void)
 {
        u_char rnd[KEYSZ + IVSZ];
+       uint32_t rekey_fuzz = 0;
 
        if (getentropy(rnd, sizeof rnd) == -1)
-               fatal("getentropy failed");
+               _getentropy_fail();
 
-       if (!rs_initialized) {
-               rs_initialized = 1;
+       if (!rs)
                _rs_init(rnd, sizeof(rnd));
-       else
+       else
                _rs_rekey(rnd, sizeof(rnd));
-       explicit_bzero(rnd, sizeof(rnd));
+       explicit_bzero(rnd, sizeof(rnd));       /* discard source seed */
 
        /* invalidate rs_buf */
-       rs_have = 0;
-       memset(rs_buf, 0, RSBUFSZ);
+       rs->rs_have = 0;
+       memset(rsx->rs_buf, 0, sizeof(rsx->rs_buf));
 
-       rs_count = 1600000;
+       /* rekey interval should not be predictable */
+       chacha_encrypt_bytes(&rsx->rs_chacha, (uint8_t *)&rekey_fuzz,
+           (uint8_t *)&rekey_fuzz, sizeof(rekey_fuzz));
+       rs->rs_count = REKEY_BASE + (rekey_fuzz % REKEY_BASE);
 }
 
 static inline void
 _rs_stir_if_needed(size_t len)
 {
-       pid_t pid = getpid();
-
-       if (rs_count <= len || !rs_initialized || rs_stir_pid != pid) {
-               rs_stir_pid = pid;
+       _rs_forkdetect();
+       if (!rs || rs->rs_count <= len)
                _rs_stir();
-       } else
-               rs_count -= len;
+       if (rs->rs_count <= len)
+               rs->rs_count = 0;
+       else
+               rs->rs_count -= len;
 }
 
 static inline void
 _rs_rekey(u_char *dat, size_t datlen)
 {
 #ifndef KEYSTREAM_ONLY
-       memset(rs_buf, 0,RSBUFSZ);
+       memset(rsx->rs_buf, 0, sizeof(rsx->rs_buf));
 #endif
        /* fill rs_buf with the keystream */
-       chacha_encrypt_bytes(&rs, rs_buf, rs_buf, RSBUFSZ);
+       chacha_encrypt_bytes(&rsx->rs_chacha, rsx->rs_buf,
+           rsx->rs_buf, sizeof(rsx->rs_buf));
        /* mix in optional user provided data */
        if (dat) {
                size_t i, m;
 
-               m = MINIMUM(datlen, KEYSZ + IVSZ);
+               m = minimum(datlen, KEYSZ + IVSZ);
                for (i = 0; i < m; i++)
-                       rs_buf[i] ^= dat[i];
+                       rsx->rs_buf[i] ^= dat[i];
        }
        /* immediately reinit for backtracking resistance */
-       _rs_init(rs_buf, KEYSZ + IVSZ);
-       memset(rs_buf, 0, KEYSZ + IVSZ);
-       rs_have = RSBUFSZ - KEYSZ - IVSZ;
+       _rs_init(rsx->rs_buf, KEYSZ + IVSZ);
+       memset(rsx->rs_buf, 0, KEYSZ + IVSZ);
+       rs->rs_have = sizeof(rsx->rs_buf) - KEYSZ - IVSZ;
 }
 
 static inline void
 _rs_random_buf(void *_buf, size_t n)
 {
        u_char *buf = (u_char *)_buf;
+       u_char *keystream;
        size_t m;
 
        _rs_stir_if_needed(n);
        while (n > 0) {
-               if (rs_have > 0) {
-                       m = MINIMUM(n, rs_have);
-                       memcpy(buf, rs_buf + RSBUFSZ - rs_have, m);
-                       memset(rs_buf + RSBUFSZ - rs_have, 0, m);
+               if (rs->rs_have > 0) {
+                       m = minimum(n, rs->rs_have);
+                       keystream = rsx->rs_buf + sizeof(rsx->rs_buf)
+                           - rs->rs_have;
+                       memcpy(buf, keystream, m);
+                       memset(keystream, 0, m);
                        buf += m;
                        n -= m;
-                       rs_have -= m;
+                       rs->rs_have -= m;
                }
-               if (rs_have == 0)
+               if (rs->rs_have == 0)
                        _rs_rekey(NULL, 0);
        }
 }
 
 static inline void
-_rs_random_u32(u_int32_t *val)
+_rs_random_u32(uint32_t *val)
 {
+       u_char *keystream;
+
        _rs_stir_if_needed(sizeof(*val));
-       if (rs_have < sizeof(*val))
+       if (rs->rs_have < sizeof(*val))
                _rs_rekey(NULL, 0);
-       memcpy(val, rs_buf + RSBUFSZ - rs_have, sizeof(*val));
-       memset(rs_buf + RSBUFSZ - rs_have, 0, sizeof(*val));
-       rs_have -= sizeof(*val);
-       return;
+       keystream = rsx->rs_buf + sizeof(rsx->rs_buf) - rs->rs_have;
+       memcpy(val, keystream, sizeof(*val));
+       memset(keystream, 0, sizeof(*val));
+       rs->rs_have -= sizeof(*val);
 }
 
-void
-arc4random_stir(void)
-{
-       _ARC4_LOCK();
-       _rs_stir();
-       _ARC4_UNLOCK();
-}
-
-void
-arc4random_addrandom(u_char *dat, int datlen)
-{
-       int m;
-
-       _ARC4_LOCK();
-       if (!rs_initialized)
-               _rs_stir();
-       while (datlen > 0) {
-               m = MINIMUM(datlen, KEYSZ + IVSZ);
-               _rs_rekey(dat, m);
-               dat += m;
-               datlen -= m;
-       }
-       _ARC4_UNLOCK();
-}
-
-u_int32_t
+uint32_t
 arc4random(void)
 {
-       u_int32_t val;
+       uint32_t val;
 
        _ARC4_LOCK();
        _rs_random_u32(&val);
        _ARC4_UNLOCK();
        return val;
 }
+DEF_WEAK(arc4random);
 
 /*
- * If we are providing arc4random, then we can provide a more efficient 
+ * If we are providing arc4random, then we can provide a more efficient
  * arc4random_buf().
  */
 # ifndef HAVE_ARC4RANDOM_BUF
@@ -220,6 +229,7 @@ arc4random_buf(void *buf, size_t n)
        _rs_random_buf(buf, n);
        _ARC4_UNLOCK();
 }
+DEF_WEAK(arc4random_buf);
 # endif /* !HAVE_ARC4RANDOM_BUF */
 #endif /* !HAVE_ARC4RANDOM */
 
@@ -242,24 +252,3 @@ arc4random_buf(void *_buf, size_t n)
 }
 #endif /* !defined(HAVE_ARC4RANDOM_BUF) && defined(HAVE_ARC4RANDOM) */
 
-#if 0
-/*-------- Test code for i386 --------*/
-#include <stdio.h>
-#include <machine/pctr.h>
-int
-main(int argc, char **argv)
-{
-       const int iter = 1000000;
-       int     i;
-       pctrval v;
-
-       v = rdtsc();
-       for (i = 0; i < iter; i++)
-               arc4random();
-       v = rdtsc() - v;
-       v /= iter;
-
-       printf("%qd cycles\n", v);
-       exit(0);
-}
-#endif
diff --git a/openbsd-compat/arc4random.h b/openbsd-compat/arc4random.h
new file mode 100644 (file)
index 0000000..2b57611
--- /dev/null
@@ -0,0 +1,79 @@
+/*     $OpenBSD: arc4random_linux.h,v 1.12 2019/07/11 10:37:28 inoguchi Exp $  */
+
+/*
+ * Copyright (c) 1996, David Mazieres <dm@uun.org>
+ * Copyright (c) 2008, Damien Miller <djm@openbsd.org>
+ * Copyright (c) 2013, Markus Friedl <markus@openbsd.org>
+ * Copyright (c) 2014, Theo de Raadt <deraadt@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*
+ * Stub functions for portability.  From LibreSSL with some adaptations.
+ */
+
+#include <sys/mman.h>
+
+#include <signal.h>
+
+/* OpenSSH isn't multithreaded */
+#define _ARC4_LOCK()
+#define _ARC4_UNLOCK()
+#define _ARC4_ATFORK(f)
+
+static inline void
+_getentropy_fail(void)
+{
+       fatal("getentropy failed");
+}
+
+static volatile sig_atomic_t _rs_forked;
+
+static inline void
+_rs_forkhandler(void)
+{
+       _rs_forked = 1;
+}
+
+static inline void
+_rs_forkdetect(void)
+{
+       static pid_t _rs_pid = 0;
+       pid_t pid = getpid();
+
+       if (_rs_pid == 0 || _rs_pid == 1 || _rs_pid != pid || _rs_forked) {
+               _rs_pid = pid;
+               _rs_forked = 0;
+               if (rs)
+                       memset(rs, 0, sizeof(*rs));
+       }
+}
+
+static inline int
+_rs_allocate(struct _rs **rsp, struct _rsx **rsxp)
+{
+       if ((*rsp = mmap(NULL, sizeof(**rsp), PROT_READ|PROT_WRITE,
+           MAP_ANON|MAP_PRIVATE, -1, 0)) == MAP_FAILED)
+               return (-1);
+
+       if ((*rsxp = mmap(NULL, sizeof(**rsxp), PROT_READ|PROT_WRITE,
+           MAP_ANON|MAP_PRIVATE, -1, 0)) == MAP_FAILED) {
+               munmap(*rsp, sizeof(**rsp));
+               *rsp = NULL;
+               return (-1);
+       }
+
+       _ARC4_ATFORK(_rs_forkhandler);
+       return (0);
+}
index bbf89825b2f4bf64896ff6837b9ac37a04109019..4af207cdd40f63d244f84f85deb8af1280cb9687 100644 (file)
@@ -218,19 +218,18 @@ int writev(int, struct iovec *, int);
 int getpeereid(int , uid_t *, gid_t *);
 #endif
 
-#ifdef HAVE_ARC4RANDOM
-# ifndef HAVE_ARC4RANDOM_STIR
-#  define arc4random_stir()
-# endif
-#else
-unsigned int arc4random(void);
-void arc4random_stir(void);
+#ifndef HAVE_ARC4RANDOM
+uint32_t arc4random(void);
 #endif /* !HAVE_ARC4RANDOM */
 
 #ifndef HAVE_ARC4RANDOM_BUF
 void arc4random_buf(void *, size_t);
 #endif
 
+#ifndef HAVE_ARC4RANDOM_STIR
+# define arc4random_stir()
+#endif
+
 #ifndef HAVE_ARC4RANDOM_UNIFORM
 uint32_t arc4random_uniform(uint32_t);
 #endif