Export heimdal's rand-fortuna PRNG to the kernel
authorBen Kaduk <kaduk@mit.edu>
Wed, 27 Mar 2013 21:02:55 +0000 (17:02 -0400)
committerJeffrey Altman <jaltman@your-file-system.com>
Wed, 10 Sep 2014 19:05:07 +0000 (15:05 -0400)
Some systems (e.g., AIX, SGI, DFBSD, HPUX) do not supply a useful
implementation of osi_readRandom(), in some cases because the kernel
does not expose a random-number interface to kernel modules.  We want
real random numbers on all systems, because we want to use the for
setting the RX epoch and connection ID in the kernel.

Build hcrypto's rand-fortuna PRNG into the rand-kernel interface we expose,
and implement RAND_bytes using rand-fortuna when osi_ReadRandom()
is not useful.

Add stub routines to config.h as needed, and add a heim_threads.h
with the necessary locking for rand-fortuna.  The rand-fortuna algorithm
requires some measure of time's passage, so provide a stub gettimeofday()
with single-second resolution.  We use a single (global) mutex for the
hcrypto kernel code, so that we can statically declare an initializer to
be the address of that mutex.  Otherwise the locking is taken essentially
wholesale from rx_kmutex.

rand-fortuna requires the sha256 code for its hashing, and also
requires a stub rand-fortuna to satisfy linker symbol visibility.

Since the rand-fortuna code does not have any actual sources of entropy
available to it during its initialization routines, we must explicitly
seed the in-kernel rand-fortuna using entropy passed in from userland.
(Userland will always have at least /dev/random available, so the
userland hcrypto should always have usable entropy.)  Be sure to do so
early in the afsd startup sequence, before any daemons are started, so
that entropy is available to the core rx code for generating the epoch
and cid -- the rand-fortuna code will (erroneously) always claim that
it has startup entropy even though in this case it may not actually
have any entropy.  The rand-fortuna code does not consider itself
fully seeded until it has 128 bytes of entropy, so be sure to pass
more than that in from userspace.

It is preferrable to always build this code into the kernel, even on
systems when it is not going to be used, to help prevent bitrot.  This
also avoids the possibility of a new system being supported that would
attempt to use the rand-fortuna code but fail to supply any seed entropy,
which would not necessarily be readily apparent.

Change-Id: I614d2bd9ac52803ec3b9572cc694cd836c8427dd
Reviewed-on: http://gerrit.openafs.org/10840
Reviewed-by: D Brashear <shadow@your-file-system.com>
Tested-by: BuildBot <buildbot@rampaginggeek.com>
Reviewed-by: Jeffrey Altman <jaltman@your-file-system.com>

src/afs/afs_call.c
src/afsd/Makefile.in
src/afsd/afsd.c
src/config/afs_args.h
src/crypto/hcrypto/kernel/config.h
src/crypto/hcrypto/kernel/heim_threads.h [new file with mode: 0644]
src/crypto/hcrypto/kernel/rand-timer.c [new file with mode: 0644]
src/crypto/hcrypto/kernel/rand.c
src/crypto/hcrypto/kernel/strcasecmp.c [new file with mode: 0644]
src/libafs/Makefile.common.in
src/libafs/MakefileProto.LINUX.in

index 4e3c4c4..f3448ed 100644 (file)
 #include "h/ksynch.h"
 #include "h/sunddi.h"
 #endif
+#include <hcrypto/rand.h>
+
+/* No hckernel-specific header for this prototype. */
+#ifndef UKERNEL
+extern void init_hckernel_mutex(void);
+#endif
 
 #if defined(AFS_SUN5_ENV) || defined(AFS_AIX_ENV) || defined(AFS_SGI_ENV) || defined(AFS_HPUX_ENV)
 #define        AFS_MINBUFFERS  100
@@ -101,6 +107,11 @@ afs_InitSetup(int preallocs)
     if (afs_InitSetup_done)
        return EAGAIN;
 
+    /* Initialize a lock for the kernel hcrypto bits. */
+#ifndef UKERNEL
+    init_hckernel_mutex();
+#endif
+
 #ifdef AFS_SUN510_ENV
     /* Initialize a RW lock for the ifinfo global array */
     rw_init(&afsifinfo_lock, NULL, RW_DRIVER, NULL);
@@ -1314,6 +1325,20 @@ afs_syscall_call(long parm, long parm2, long parm3,
     } else if (parm == AFSOP_SET_RMTSYS_FLAG) {
        afs_rmtsys_enable = parm2;
        code = 0;
+#ifndef UKERNEL
+    } else if (parm == AFSOP_SEED_ENTROPY) {
+       unsigned char *seedbuf;
+
+       if (parm3 > 4096) {
+           code = EFAULT;
+       } else {
+           seedbuf = afs_osi_Alloc(parm3);
+           AFS_COPYIN(AFSKPTR(parm2), seedbuf, parm3, code);
+           RAND_seed(seedbuf, parm3);
+           memset(seedbuf, 0, parm3);
+           afs_osi_Free(seedbuf, parm3);
+       }
+#endif
     } else {
        code = EINVAL;
     }
index 954ad49..c1dd92b 100644 (file)
@@ -37,7 +37,7 @@ FUSE_LIBS=@FUSE_LIBS@
 LDFLAGS_afsd = $(AFSD_LDFLAGS)
 afsd: afsd.o afsd_kernel.o $(AFSLIBS) $(AFSD_LIBS)
        $(LT_LDRULE_static) afsd.o afsd_kernel.o $(NON_SHARED) \
-               $(AFSLIBS) ${AFSD_LIBS} $(LIB_roken) $(MT_LIBS) $(XLIBS)
+               $(AFSLIBS) ${AFSD_LIBS} $(LIB_hcrypto) $(LIB_roken) $(MT_LIBS) $(XLIBS)
 
 LDFLAGS_afsd.fuse = $(AFSD_LDFLAGS)
 afsd.fuse: afsd_fuse.o $(UAFSLIBS) $(AFSD_LIBS)
index 0fb524a..3097610 100644 (file)
@@ -79,6 +79,7 @@
 
 #include <sys/file.h>
 #include <sys/wait.h>
+#include <hcrypto/rand.h>
 
 /* darwin dirent.h doesn't give us the prototypes we want if KERNEL is
  * defined */
@@ -2128,6 +2129,13 @@ afsd_run(void)
 
     /* initialize the rx random number generator from user space */
     {
+       /* rand-fortuna wants at least 128 bytes of seed; be generous. */
+       unsigned char seedbuf[256];
+       if (RAND_bytes(seedbuf, sizeof(seedbuf)) != 1) {
+           printf("SEED_ENTROPY: Error retrieving seed entropy\n");
+       }
+       afsd_syscall(AFSOP_SEED_ENTROPY, seedbuf, sizeof(seedbuf));
+       memset(seedbuf, 0, sizeof(seedbuf));
        /* parse multihomed address files */
        afs_uint32 addrbuf[MAXIPADDRS], maskbuf[MAXIPADDRS],
            mtubuf[MAXIPADDRS];
@@ -2675,6 +2683,10 @@ afsd_syscall_populate(struct afsd_syscall_args *args, int syscall, va_list ap)
        params[0] = CAST_SYSCALL_PARAM((va_arg(ap, afs_uint32)));
 #endif
        break;
+    case AFSOP_SEED_ENTROPY:
+       params[0] = CAST_SYSCALL_PARAM((va_arg(ap, void *)));
+       params[1] = CAST_SYSCALL_PARAM((va_arg(ap, afs_uint32)));
+       break;
     default:
        printf("Unknown syscall enountered: %d\n", syscall);
        opr_Assert(0);
index 296cf96..01bdac5 100644 (file)
@@ -52,6 +52,7 @@
 #define AFSOP_BKG_HANDLER        41     /* userspace-capable Bkg daemon */
 #define AFSOP_SET_RXMAXFRAGS     43     /* set rxi_nSendFrags, rxi_nRecvFrags */
 #define AFSOP_SET_RMTSYS_FLAG    44     /* set flag if rmtsys is enabled */
+#define AFSOP_SEED_ENTROPY       45     /* Give the kernel hcrypto entropy */
 
 /* The range 20-30 is reserved for AFS system offsets in the afs_syscall */
 #define        AFSCALL_PIOCTL          20
index d9c58c5..c7b22d9 100644 (file)
 #endif
 #define assert osi_Assert
 
+/* Linux's current.h defines current to get_current(), conflicing with
+ * heimdal's rand-fortuna.c's local variable. */
+#if defined(current)
+#undef current
+#define current current
+#endif
+
+/* AIX and some Solaris (and others, presumably) still have a 'u' symbol for
+ * the user area.  rand-fortuna.c has a local variable of that name. */
+#if defined(u)
+#undef u
+#define u u
+#endif
+
 /* hcrypto uses "static inline", which isn't supported by some of our
  * compilers */
 #if !defined(inline) && !defined(__GNUC__)
@@ -64,3 +78,22 @@ void * _afscrypto_realloc(void *, size_t);
 /* osi_readRandom is also prototyped in afs_prototypes.h, but pulling that in
  * here creates loads of additional dependencies */
 extern int osi_readRandom(void *, afs_size_t);
+
+#if defined(getpid)
+/* On linux, getpid() is unfortunately declared in terms of current, which
+ * already gives us a namespace clash.  It was lousy entropy, anyway. */
+#undef getpid
+#define getpid()       1
+#else
+static_inline pid_t getpid(void) {return 1;};
+#endif
+static_inline int open(const char *path, int flags, ...) {return -1;}
+static_inline void abort(void) {osi_Panic("hckernel aborting\n");}
+static_inline void rk_cloexec(int fd) {}
+static_inline ssize_t read(int d, void *buf, size_t nbytes) {return -1;}
+static_inline int close(int d) {return -1;}
+#if defined(HAVE_GETUID)
+#undef HAVE_GETUID
+#endif
+static_inline int gettimeofday(struct timeval *tp, void *tzp)
+    {if (tp == NULL) return -1; tp->tv_sec = osi_Time(); tp->tv_usec = 0; return 0;}
diff --git a/src/crypto/hcrypto/kernel/heim_threads.h b/src/crypto/hcrypto/kernel/heim_threads.h
new file mode 100644 (file)
index 0000000..cc0940f
--- /dev/null
@@ -0,0 +1,12 @@
+#include <rx_kmutex.h>
+#define HEIMDAL_MUTEX afs_kmutex_t*
+extern afs_kmutex_t hckernel_mutex;
+#define HEIMDAL_MUTEX_INITIALIZER      &hckernel_mutex;
+#define HEIMDAL_MUTEX_init(m)          MUTEX_INIT(*m,0,0,0)
+#define HEIMDAL_MUTEX_lock(m)          MUTEX_ENTER(*m)
+#define HEIMDAL_MUTEX_unlock(m)                MUTEX_EXIT(*m)
+#define HEIMDAL_MUTEX_destroy(m)       MUTEX_DESTROY(*m)
+
+/* Tell rand-fortuna we don't have userspace things. */
+#define NO_RAND_UNIX_METHOD
+#define NO_RAND_EGD_METHOD
diff --git a/src/crypto/hcrypto/kernel/rand-timer.c b/src/crypto/hcrypto/kernel/rand-timer.c
new file mode 100644 (file)
index 0000000..3a70235
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+ * Stub rand-timer "implementation" so that rand-fortuna is linkable
+ * into the kernel.
+ *
+ * Contains no copyrightable content.
+ */
+#include <config.h>
+
+#include <rand.h>
+#include "randi.h"
+
+static void
+timer_seed(const void *indata, int size)
+{
+}
+
+static int
+timer_bytes(unsigned char *outdata, int size)
+{
+    return 1;
+}
+
+static void
+timer_cleanup(void)
+{
+}
+
+static void
+timer_add(const void *indata, int size, double entropi)
+{
+}
+
+static int
+timer_pseudorand(unsigned char *outdata, int size)
+{
+    return 1;
+}
+
+static int
+timer_status(void)
+{
+    return 0;
+}
+
+const RAND_METHOD hc_rand_timer_method = {
+    timer_seed,
+    timer_bytes,
+    timer_cleanup,
+    timer_add,
+    timer_pseudorand,
+    timer_status
+};
+
+const RAND_METHOD *
+RAND_timer_method(void)
+{
+    return &hc_rand_timer_method;
+}
index b0254f3..8106486 100644 (file)
@@ -6,13 +6,40 @@
 #include <evp-hcrypto.h>
 #include <aes.h>
 #include <sha.h>
+#include <heim_threads.h>
+
+/*
+ * This mutex is used to synchronize hcrypto operations in the kernel.
+ * We cheat and assume that all access into hcrypto comes through routines
+ * in this file, so that we can ensure it is initialized before it is used.
+ */
+afs_kmutex_t hckernel_mutex;
+
+/* Called from osi_Init(); will only run once. */
+void
+init_hckernel_mutex(void)
+{
+    MUTEX_INIT(&hckernel_mutex, "hckernel", MUTEX_DEFAULT, 0);
+}
+
+void
+RAND_seed(const void *indata, size_t size)
+{
+    const RAND_METHOD *m = RAND_fortuna_method();
+    m->seed(indata, size);
+}
 
 int
 RAND_bytes(void *outdata, size_t size)
 {
     if (size == 0)
        return 0;
+#if defined(AFS_AIX_ENV) || defined(AFS_DFBSD_ENV) || defined(AFS_HPUX_ENV) || defined(AFS_SGI_ENV)
+    const RAND_METHOD *m = RAND_fortuna_method();
+    return m->bytes(outdata, size);
+#else
     if (osi_readRandom(outdata, size))
        return 0;
+#endif
     return 1;
 }
diff --git a/src/crypto/hcrypto/kernel/strcasecmp.c b/src/crypto/hcrypto/kernel/strcasecmp.c
new file mode 100644 (file)
index 0000000..4a5acf2
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2000, International Business Machines Corporation and others.
+ * All Rights Reserved.
+ *
+ * This software has been released under the terms of the IBM Public
+ * License.  For details, see the LICENSE file in the top-level source
+ * directory or online at http://www.openafs.org/dl/license10.html
+ */
+
+const char hckernel_strcasecmp_placeholder[] =
+               "This is not an empty compilation unit.";
+
+#ifndef afs_strcasecmp
+int
+afs_strcasecmp(const char *s1, const char *s2)
+{
+    while (*s1 && *s2) {
+       char c1, c2;
+
+       c1 = *s1++;
+       c2 = *s2++;
+       if (c1 >= 'A' && c1 <= 'Z')
+           c1 += 0x20;
+       if (c2 >= 'A' && c2 <= 'Z')
+           c2 += 0x20;
+       if (c1 != c2)
+           return c1 - c2;
+    }
+
+    return *s1 - *s2;
+}
+#endif
index e70c7df..325863f 100644 (file)
@@ -71,6 +71,9 @@ single_destdir_libafs: dest_libafs
 depsrcs: 
 
 AFSAOBJS = \
+       sha256-kernel.o \
+       rand-fortuna-kernel.o \
+       rand-timer-kernel.o \
        afs_atomlist.o \
        afs_lhash.o \
        afs_analyze.o \
@@ -211,6 +214,17 @@ AFSNONFSOBJS = \
 
 # init daemons call pioctl
 AFSPAGOBJS = \
+       sha256-kernel.o \
+       rand-fortuna-kernel.o \
+       rand-timer-kernel.o \
+       md5.o           \
+       evp.o           \
+       evp-algs.o      \
+       rand-kernel.o   \
+       alloc-kernel.o  \
+       aes.o           \
+       rijndael-alg-fst.o \
+       sha.o           \
        afs_atomlist.o \
        afs_error.o \
        afs_icl.o \
@@ -250,6 +264,7 @@ AFSPAGOBJS = \
        rx_pag_packet.o \
        rx_multi.o      \
        rx_stats.o      \
+       strcasecmp_pag.o        \
        opr_rbtree.o    \
        xdr_rx.o        \
        xdr_mem.o       \
@@ -534,6 +549,8 @@ rx_pag_packet.o: $(TOP_SRC_RX)/rx_packet.c
        $(CRULE_NOOPT) $(TOP_SRC_RX)/rx_packet.c
 rx_pag_knet.o: $(TOP_SRC_RX)/${MKAFS_OSTYPE}/rx_knet.c
        $(CRULE_NOOPT) $(TOP_SRC_RX)/${MKAFS_OSTYPE}/rx_knet.c
+strcasecmp_pag.o: $(TOP_SRCDIR)/crypto/hcrypto/kernel/strcasecmp.c
+       $(CRULE_NOOPT) $(TOP_SRCDIR)/crypto/hcrypto/kernel/strcasecmp.c
 
 # Crypto
 md5.o: $(TOP_SRCDIR)/external/heimdal/hcrypto/md5.c
@@ -552,6 +569,18 @@ rand-kernel.o: $(TOP_SRCDIR)/crypto/hcrypto/kernel/rand.c
        $(CRULE_OPT) $(TOP_SRCDIR)/crypto/hcrypto/kernel/rand.c
 CFLAGS-rand-kernel.o = -I$(TOP_INCDIR)/hcrypto
 
+rand-fortuna-kernel.o: $(TOP_SRCDIR)/external/heimdal/hcrypto/rand-fortuna.c
+       $(CRULE_OPT) $(TOP_SRCDIR)/external/heimdal/hcrypto/rand-fortuna.c
+CFLAGS-rand-fortuna-kernel.o = -I$(TOP_INCDIR)/hcrypto
+
+rand-timer-kernel.o: $(TOP_SRCDIR)/crypto/hcrypto/kernel/rand-timer.c
+       $(CRULE_OPT) $(TOP_SRCDIR)/crypto/hcrypto/kernel/rand-timer.c
+CFLAGS-rand-timer-kernel.o = -I$(TOP_SRCDIR)/external/heimdal/hcrypto
+
+sha256-kernel.o: $(TOP_SRCDIR)/external/heimdal/hcrypto/sha256.c
+       $(CRULE_OPT) $(TOP_SRCDIR)/external/heimdal/hcrypto/sha256.c
+CFLAGS-sha256-kernel.o = -I$(TOP_INCDIR)/hcrypto
+
 alloc-kernel.o: $(TOP_SRCDIR)/crypto/hcrypto/kernel/alloc.c
        $(CRULE_OPT) $(TOP_SRCDIR)/crypto/hcrypto/kernel/alloc.c
 
index 1213e4e..b7801ae 100644 (file)
@@ -57,6 +57,7 @@ AFS_OS_OBJS = \
 AFS_OS_PAGOBJS = \
        osi_alloc.o \
        osi_cred.o \
+       osi_crypto.o \
        osi_gcpags.o \
        osi_groups.o \
        osi_inode.o \
@@ -92,11 +93,14 @@ CFLAGS_evp.o = -I$(TOP_SRCDIR)/external/heimdal/hcrypto \
               -DHAVE_CONFIG_H
 CFLAGS_evp-algs.o = -I$(TOP_SRCDIR)/external/heimdal/hcrypto
 CFLAGS_evp-kernel.o = -I$(TOP_SRCDIR)/external/heimdal/hcrypto
+CFLAGS_rand-fortuna-kernel.o = -I$(TOP_SRCDIR)/external/heimdal/hcrypto
+CFLAGS_rand-timer-kernel.o = -I$(TOP_SRCDIR)/external/heimdal/hcrypto
 CFLAGS_rand-kernel.o = -I$(TOP_SRCDIR)/external/heimdal/hcrypto
 CFLAGS_aes.o = -I$(TOP_SRCDIR)/external/heimdal/hcrypto
 CFLAGS_rijndael-alg-fst.o = -I$(TOP_SRCDIR)/external/heimdal/hcrypto \
                            -DNO_CONFIG_H
 CFLAGS_sha.o = -I$(TOP_SRCDIR)/external/heimdal/hcrypto
+CFLAGS_sha256-kernel.o = -I$(TOP_SRCDIR)/external/heimdal/hcrypto
 CFLAGS_md5.o = -I$(TOP_SRCDIR)/external/heimdal/hcrypto
 CFLAGS_random.o = -I$(TOP_SRCDIR)/external/heimdal/hcrypto