Zsh Mailing List Archive
Messages sorted by: Reverse Date, Date, Thread, Author

preliminary patch for zsh/random module



It still needs testing on more platforms and additional error-checking as well as documentation, but I was hoping for some feedback on the general ideas.

diff --git a/Src/Modules/random.c b/Src/Modules/random.c
new file mode 100644
index 000000000..038a56c1f
--- /dev/null
+++ b/Src/Modules/random.c
@@ -0,0 +1,350 @@
+/*
+ * random.c - module to access kernel randome sources.
+ *
+ * This file is part of zsh, the Z shell.
+ *
+ * Copyright (c) 2022 Clinton Bunch
+ * All rights reserved.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and to distribute modified versions of this software for any
+ * purpose, provided that the above copyright notice and the following
+ * two paragraphs appear in all copies of this software.
+ *
+ * In no event shall Zoltán Hidvégi or the Zsh Development Group be liable
+ * to any party for direct, indirect, special, incidental, or consequential
+ * damages arising out of the use of this software and its documentation,
+ * even if Zoltán Hidvégi and the Zsh Development Group have been advised of
+ * the possibility of such damage.
+ *
+ * Clinton Bunch and the Zsh Development Group specifically disclaim any
+ * warranties, including, but not limited to, the implied warranties of
+ * merchantability and fitness for a particular purpose.  The software
+ * provided hereunder is on an "as is" basis, and Zoltán Hidvégi and the
+ * Zsh Development Group have no obligation to provide maintenance,
+ * support, updates, enhancements, or modifications.
+ *
+ */
+
+#include "random.mdh"
+#include "random.pro"
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdio.h>
+
+#ifdef HAVE_SYS_RANDOM_H
+#include <sys/random.h>
+#endif
+
+/* Simplify select URANDOM specific code */
+#if !defined(HAVE_ARC4RANDOM) && !defined(HAVE_GETRANDOM)
+#define USE_URANDOM
+#endif
+
+/* Define a buffer type for 32 bit integers */
+#ifdef UINT32_MAX
+typedef uint32_t zuint32;
+#else
+#define UINT32_MAX  UINT_MAX
+typedef unsigned int zuint32;
+#endif
+
+/* buffer to pre-load integers for SRANDOM to lessen the context switches */
+zuint32 rand_buff[8];
+static int buf_cnt = -1;
+
+#ifdef USE_URANDOM
+/* File descriptor for /dev/urandom */
+int randfd = -1;
+#endif /* USE_URANDOM */
+
+/*
+ *  Function takes an input buffer and converts the characters to hex and
+ *  places them in an outbut buffer twice the size. Returns -1 if it can't.
+ *  Returns the size of the output otherwise.
+ */
+
+/**/
+static int
+ztr_to_hex(char *out, size_t outlen, unsigned char *input, size_t inlen)
+{
+    int i = 0, j = 0;
+
+    if (outlen < (inlen * 2 + 1))
+    return -1;
+
+    for(i = 0, j = 0; i < inlen; i++, j+=2) {
+    if (j < outlen) {
+        sprintf((char *)(out + j),"%02X",input[i]);
+    } else {
+        return -1;
+    }
+    }
+    out[j]='\0';
+    return j;
+}
+
+/**/
+static int
+getrandom_buffer(void *buf, size_t len)
+{
+    int ret;
+
+#ifdef HAVE_ARC4RANDOM
+    arc4random_buf(buf,len);
+    ret = len;
+#elif defined(HAVE_GETRANDOM)
+    ret=getrandom(buf,len,0);
+#else
+    ret=read(randfd,buf,len);
+#endif
+    return ret;
+}
+
+/*
+ * Implements the getrandom builtin to return a string of random bytes of
+ * user-specified length.  It either prints them to stdout or returns them
+ * in a parameter passed on the command line.
+ *
+ */
+
+/**/
+static int
+bin_getrandom(char *name, char **argv, Options ops, int func)
+{
+
+    size_t len=8;
+    size_t byte_len=0, outlen;
+    int integer_out=0, i;
+    char int2str[11];  /*maximum string length of a 32-bit integer + null */
+
+    unsigned char *buf;
+    zuint32  *int_buf;
+    char *scalar = NULL, *arrname= NULL, *endptr, *len_arg, *outbuff;
+    char **array;
+
+    if (OPT_ISSET(ops, 's')) {
+    scalar = OPT_ARG(ops, 's');
+    if (!isident(scalar)) {
+        zwarnnam(name, "argument to -s not an identifier: %s", scalar);
+        return 1;
+    }
+    }
+
+    if (OPT_ISSET(ops,'a')) {
+    arrname = OPT_ARG(ops, 'a');
+    if (!isident(arrname)) {
+        zwarnnam(name,"argument to -a not an identifier: %s", arrname);
+        return 1;
+    }
+    }
+
+    if (OPT_ISSET(ops, 'i')) {
+    if (!OPT_ISSET(ops,'a')) {
+        zwarnnam(name,"-i requires -a");
+        return 1;
+    }
+    integer_out=1;
+    }
+
+    if (OPT_ISSET(ops, 'l')) {
+    errno = 0;
+
+    len_arg=OPT_ARG(ops, 'l');
+    len = strtoul(len_arg, &endptr, 10);
+    if (errno != 0) {
+        zwarnnam(name, "%s: %e", len_arg, errno);
+        return 1;
+    } else if (*len_arg == '\0' || *endptr != '\0') {
+        zwarnnam(name, "%s: invalid decimal number", len_arg);
+        return 1;
+    } else if (len > 64 || (integer_out && len > 16) || len <= 0) {
+        zwarnnam(name,"length must be between 1 and %d you specified: %d",
+            (integer_out?16:64), len);
+        return 1;
+    }
+
+    }
+
+    if (!byte_len)
+    byte_len = len;
+    if (integer_out)
+    byte_len = len*sizeof(zuint32);
+
+    buf = zalloc(byte_len);
+
+    if (getrandom_buffer(buf, byte_len) < 0) {
+    zwarnnam(name,"Couldn't get random data");
+    return 1;
+    }
+
+    if (OPT_ISSET(ops, 'r')) {
+    outbuff = (char *) buf;
+    outlen  = byte_len;
+    } else {
+    outlen=byte_len*2+1;
+    outbuff = (char*) zalloc(outlen);
+    ztr_to_hex(outbuff, outlen, buf, byte_len);
+    }
+
+    if (scalar) {
+    setsparam(scalar, metafy(outbuff, outlen, META_DUP));
+    }
+
+    if(arrname) {
+    array = (char **) zalloc((len+1)*sizeof(char *));
+    array[len] = NULL;
+
+    if(integer_out)
+        int_buf=(zuint32 *)buf;
+
+    for(i=0;i < len;i++) {
+        if(integer_out) {
+        sprintf(int2str,"%u",int_buf[i]);
+        } else {
+        sprintf(int2str,"%d",buf[i]);
+        }
+        array[i]=ztrdup(int2str);
+    }
+    setaparam(arrname,array);
+    }
+
+    if (!scalar && !arrname) {
+        fwrite(outbuff,1,outlen,stdout);
+        if (outbuff !=  (void *) buf) {
+        zfree(outbuff,outlen);
+    }
+    }
+
+
+    zfree(buf, len);
+    return 0;
+}
+
+
+
+/**/
+static zlong
+get_srandom(UNUSED(Param pm)) {
+
+    if(buf_cnt < 0) {
+    getrandom_buffer((void*) rand_buff,sizeof(rand_buff));
+    buf_cnt=7;
+    }
+    return rand_buff[buf_cnt--];
+}
+
+/**/
+static mnumber
+math_zrandom(UNUSED(char *name), UNUSED(int argc), UNUSED(mnumber *argv),
+         UNUSED(int id))
+{
+    mnumber ret;
+    uint32_t r;
+
+    r=get_srandom(NULL);
+    ret.type = MN_FLOAT;
+    ret.u.d = r/(UINT32_MAX+0.0);
+
+    return ret;
+}
+
+static struct builtin bintab[] = {
+    BUILTIN("getrandom", 0, bin_getrandom, 0, 5, 0, "ria:s:l:%", NULL),
+};
+
+static const struct gsu_integer srandom_gsu =
+{ get_srandom, nullintsetfn, stdunsetfn };
+
+static struct paramdef patab[] = {
+    SPECIALPMDEF("SRANDOM", PM_INTEGER|PM_READONLY, &srandom_gsu,NULL,NULL),
+};
+
+static struct mathfunc mftab[] = {
+    NUMMATHFUNC("zrandom", math_zrandom, 0, 0, 0),
+};
+
+static struct features module_features = {
+    bintab, sizeof(bintab)/sizeof(*bintab),
+    NULL, 0,
+    mftab, sizeof(mftab)/sizeof(*mftab),
+    patab, sizeof(patab)/sizeof(*patab),
+    0
+};
+
+/**/
+int
+setup_(UNUSED(Module m))
+{
+#ifdef USE_URANDOM
+    struct stat st;
+
+    if (lstat("/dev/urandom",&st) < 0) {
+    zwarn("No kernel random pool found.");
+    return 1;
+    }
+
+    if (!(S_ISCHR(st.st_mode)) ) {
+    zwarn("No kernel random pool found.");
+    return 1;
+    }
+#endif /* USE_URANDOM */
+    return 0;
+}
+
+/**/
+int
+features_(Module m, char ***features)
+{
+    *features = featuresarray(m, &module_features);
+    return 0;
+}
+
+/**/
+int
+enables_(Module m, int **enables)
+{
+    return handlefeatures(m, &module_features, enables);
+}
+
+/**/
+int
+boot_(Module m)
+{
+#ifdef USE_URANDOM
+    int tmpfd=-1;
+
+    if ((tmpfd = open("/dev/urandom", O_RDONLY)) < 0) {
+    zwarnnam("Could not access kernel random pool");
+    return 1;
+    }
+    randfd = movefd(tmpfd);
+    if (randfd < 0)
+    zwarnnam("Could not access kernel random pool");
+    return 1;
+    }
+#endif
+    return 0;
+}
+
+/**/
+int
+cleanup_(Module m)
+{
+    return setfeatureenables(m, &module_features, NULL);
+}
+
+/**/
+int
+finish_(UNUSED(Module m))
+{
+#ifdef USE_URANDOM
+    if (randfd >= 0)
+    zclose(randfd);
+#endif /* USE_URANDOM */
+    return 0;
+}
diff --git a/Src/Modules/random.mdd b/Src/Modules/random.mdd
new file mode 100644
index 000000000..3803a4533
--- /dev/null
+++ b/Src/Modules/random.mdd
@@ -0,0 +1,7 @@
+name=zsh/random
+link=either
+load=yes
+
+autofeatures="b:getrandom p:SRANDOM f:zrandom"
+
+objects="random.o"
diff --git a/configure.ac b/configure.ac
index 074141d38..f1fa01274 100644
--- a/configure.ac
+++ b/configure.ac
@@ -675,6 +675,7 @@ fi
 AC_CHECK_HEADERS(sys/time.h sys/times.h sys/select.h termcap.h termio.h \
          termios.h sys/param.h sys/filio.h string.h memory.h \
          limits.h fcntl.h libc.h sys/utsname.h sys/resource.h \
+                 sys/random.h \
          locale.h errno.h stdio.h stdarg.h varargs.h stdlib.h \
          unistd.h sys/capability.h \
          utmp.h utmpx.h sys/types.h pwd.h grp.h poll.h sys/mman.h \
@@ -1337,6 +1338,7 @@ AC_CHECK_FUNCS(strftime strptime mktime timelocal \
            cygwin_conv_path \
            nanosleep \
            srand_deterministic \
+               getrandom arc4random \
            setutxent getutxent endutxent getutent)
 AC_FUNC_STRCOLL






Messages sorted by: Reverse Date, Date, Thread, Author