From 93a4e38a3433b40978bcdeb226bb96dc364a6fb7 Mon Sep 17 00:00:00 2001 From: "Steffen (Daode) Nurpmeso" Date: Tue, 22 Aug 2017 16:03:21 +0200 Subject: [PATCH] Rework (usage of) *SSL random handling (Rich Salz).. Add config.h:n_RANDOM_USE_XSSL and ssl_rand_bytes() and use the *SSL RAND_* machinery if possible for our random numbers. Do not skip loading of the *SSL entropy file if the PRNG claims to have successfully initialized itself. Do not use RAND_seed() but only RAND_add() to feed entropy in the PRNG, use an entropy value that is a little bit more realistic than what we have used before, though likely still too high. Alway feed in. Always try to write the updated entropy to the *SSL entropy file if the PRNG claims to have enough entropy. Introduce n_random_create_buf() and make n_random_create_cp() a simple wrapper of that. --- auxlily.c | 97 ++++++++++++++++++++++++++++++++++++++++++++++---------------- cc-test.sh | 2 +- config.h | 7 +++++ nail.h | 1 + nailfuns.h | 11 +++++-- xssl.c | 95 ++++++++++++++++++++++++++++++++++++++++-------------------- 6 files changed, 154 insertions(+), 59 deletions(-) diff --git a/auxlily.c b/auxlily.c index 76ab9e92..4986f52a 100644 --- a/auxlily.c +++ b/auxlily.c @@ -56,7 +56,7 @@ # include #endif -#ifdef HAVE_GETRANDOM +#if defined HAVE_GETRANDOM && !n_RANDOM_USE_XSSL # include HAVE_GETRANDOM_HEADER #endif @@ -70,7 +70,7 @@ # endif #endif -#ifndef HAVE_POSIX_RANDOM +#if !defined HAVE_POSIX_RANDOM && !n_RANDOM_USE_XSSL union rand_state{ struct rand_arc4{ ui8_t _dat[256]; @@ -147,7 +147,7 @@ n__ERR_NUMBER_TO_MAPOFF #undef a_X }; -#ifndef HAVE_POSIX_RANDOM +#if !defined HAVE_POSIX_RANDOM && !n_RANDOM_USE_XSSL static union rand_state *a_aux_rand; #endif @@ -160,7 +160,7 @@ static size_t a_aux_err_linelen; /* Our ARC4 random generator with its completely unacademical pseudo * initialization (shall /dev/urandom fail) */ -#ifndef HAVE_POSIX_RANDOM +#if !defined HAVE_POSIX_RANDOM && !n_RANDOM_USE_XSSL static void a_aux_rand_init(void); SINLINE ui8_t a_aux_rand_get8(void); # ifndef HAVE_GETRANDOM @@ -171,7 +171,7 @@ static ui32_t a_aux_rand_weak(ui32_t seed); /* Find the descriptive mapping of an error number, or _ERR_INVAL */ static struct a_aux_err_map const *a_aux_err_map_from_no(si32_t eno); -#ifndef HAVE_POSIX_RANDOM +#if !defined HAVE_POSIX_RANDOM && !n_RANDOM_USE_XSSL static void a_aux_rand_init(void){ # ifndef HAVE_GETRANDOM @@ -285,7 +285,7 @@ a_aux_rand_weak(ui32_t seed){ return seed; } # endif /* HAVE_GETRANDOM */ -#endif /* !HAVE_POSIX_RANDOM */ +#endif /* !HAVE_POSIX_RANDOM && !n_RANDOM_USE_XSSL */ static struct a_aux_err_map const * a_aux_err_map_from_no(si32_t eno){ @@ -992,27 +992,59 @@ jleave: #endif /* HAVE_IDNA */ FL char * -n_random_create_cp(size_t length, ui32_t *reprocnt_or_null){ +n_random_create_buf(char *dat, size_t len, ui32_t *reprocnt_or_null){ struct str b64; - char *data, *cp; - size_t i; + char *indat, *cp, *oudat; + size_t i, inlen, oulen; NYD_ENTER; -#ifndef HAVE_POSIX_RANDOM - if(a_aux_rand == NULL) + if(!(n_psonce & n_PSO_RANDOM_INIT)){ + n_psonce |= n_PSO_RANDOM_INIT; + + if(n_poption & n_PO_D_V){ + char const *prngn; + +#if n_RANDOM_USE_XSSL + prngn = "*SSL RAND_*"; +#elif defined HAVE_POSIX_RANDOM + prngn = "POSIX/arc4random"; +#else + prngn = "builtin ARC4"; +#endif + n_err(_("Setting up PseudoRandomNumberGenerator: %s\n"), prngn); + } + +#if !defined HAVE_POSIX_RANDOM && !n_RANDOM_USE_XSSL a_aux_rand_init(); #endif + } /* We use our base64 encoder with _NOPAD set, so ensure the encoded result - * with PAD stripped is still longer than what the user requests, easy way */ - data = n_lofi_alloc(i = length + 3); + * with PAD stripped is still longer than what the user requests, easy way. + * The relation of base64 is fixed 3 in = 4 out, and we do not want to + * include the base64 PAD characters in our random string: give some pad */ + i = len; + if((inlen = i % 3) != 0) + i += 3 - inlen; +jinc1: + inlen = i >> 2; + oulen = inlen << 2; + if(oulen < len){ + i += 3; + goto jinc1; + } + inlen = inlen + (inlen << 1); + + indat = n_lofi_alloc(inlen +1); if(!(n_psonce & n_PSO_REPRODUCIBLE) || reprocnt_or_null == NULL){ -#ifndef HAVE_POSIX_RANDOM - while(i-- > 0) - data[i] = (char)a_aux_rand_get8(); +#if n_RANDOM_USE_XSSL + ssl_rand_bytes(indat, inlen); +#elif !defined HAVE_POSIX_RANDOM + for(i = inlen; i-- > 0;) + indat[i] = (char)a_aux_rand_get8(); #else - for(cp = data; i > 0;){ + for(cp = indat, i = inlen; i > 0;){ union {ui32_t i4; char c[4];} r; size_t j; @@ -1028,7 +1060,7 @@ n_random_create_cp(size_t length, ui32_t *reprocnt_or_null){ } #endif }else{ - for(cp = data; i > 0;){ + for(cp = indat, i = inlen; i > 0;){ union {ui32_t i4; char c[4];} r; size_t j; @@ -1054,15 +1086,30 @@ n_random_create_cp(size_t length, ui32_t *reprocnt_or_null){ } } - assert(length + 3 < UIZ_MAX / 4); - b64_encode_buf(&b64, data, length + 3, - B64_SALLOC | B64_RFC4648URL | B64_NOPAD); - n_lofi_free(data); + oudat = (len >= oulen) ? dat : n_lofi_alloc(oulen +1); + b64.s = oudat; + b64_encode_buf(&b64, indat, inlen, B64_BUF | B64_RFC4648URL | B64_NOPAD); + assert(b64.l >= len); + memcpy(dat, b64.s, len); + dat[len] = '\0'; + if(oudat != dat) + n_lofi_free(oudat); + + n_lofi_free(indat); + + NYD_LEAVE; + return dat; +} + +FL char * +n_random_create_cp(size_t len, ui32_t *reprocnt_or_null){ + char *dat; + NYD_ENTER; - assert(b64.l >= length); - b64.s[length] = '\0'; + dat = n_autorec_alloc(len +1); + dat = n_random_create_buf(dat, len, reprocnt_or_null); NYD_LEAVE; - return b64.s; + return dat; } FL si8_t diff --git a/cc-test.sh b/cc-test.sh index d8430d11..25db66bc 100755 --- a/cc-test.sh +++ b/cc-test.sh @@ -3162,7 +3162,7 @@ __EOT__ -X'source ./.trc' -Smta=./.tsendmail.sh \ >./.tall 2>&1 ${cat} ./.tall >> "${MBOX}" - check behave:compose_hooks-1 0 "${MBOX}" '3479456964 10101' + check behave:compose_hooks-1 0 "${MBOX}" '522535560 10101' ${rm} -f "${MBOX}" printf 'm this-goes@nowhere\nbody\n!.\n' | diff --git a/config.h b/config.h index 8d288151..ea301410 100644 --- a/config.h +++ b/config.h @@ -90,6 +90,13 @@ # define CHARSET_8BIT_OKEY ttycharset #endif +/* Simply use RAND_bytes(3) for randoms */ +#ifdef HAVE_XSSL +# define n_RANDOM_USE_XSSL 1 +#else +# define n_RANDOM_USE_XSSL 0 +#endif + /* Some environment variables for pipe hooks etc. */ #define n_PIPEENV_FILENAME "MAILX_FILENAME" #define n_PIPEENV_FILENAME_GENERATED "MAILX_FILENAME_GENERATED" diff --git a/nail.h b/nail.h index 3a64b6d2..d94f0229 100644 --- a/nail.h +++ b/nail.h @@ -1470,6 +1470,7 @@ enum n_program_state_once{ n_PSO_TERMCAP_DISABLE = 1u<<18, n_PSO_TERMCAP_CA_MODE = 1u<<19, n_PSO_LINE_EDITOR_INIT = 1u<<20, + n_PSO_RANDOM_INIT = 1u<<21, /* A subtile hack which works in conjunction with n_OPT_t_FLAG so as to * allow to have multiple states regarding the related header setup */ diff --git a/nailfuns.h b/nailfuns.h index 4ba85cb6..0df46a10 100644 --- a/nailfuns.h +++ b/nailfuns.h @@ -381,10 +381,12 @@ FL bool_t n_idna_to_ascii(struct n_string *out, char const *ibuf, size_t ilen); size_t ilen);*/ #endif -/* Get a (pseudo) random string of *length* bytes; returns salloc()ed buffer. +/* Get a (pseudo) random string of *len* bytes, _not_ counting the NUL + * terminator, the second returns an n_autorec_alloc()ed buffer. * If n_PSO_REPRODUCIBLE and reprocnt_or_null not NULL then we produce * a reproducable string by using and managing that counter instead */ -FL char *n_random_create_cp(size_t length, ui32_t *reprocnt_or_null); +FL char *n_random_create_buf(char *dat, size_t len, ui32_t *reprocnt_or_null); +FL char *n_random_create_cp(size_t len, ui32_t *reprocnt_or_null); /* Check whether the argument string is a true (1) or false (0) boolean, or an * invalid string, in which case -1 is returned; if emptyrv is not -1 then it, @@ -2693,6 +2695,11 @@ FL void hmac_md5(unsigned char *text, int text_len, unsigned char *key, */ #ifdef HAVE_XSSL +/* Our wrapper for RAND_bytes(3) */ +# if n_RANDOM_USE_XSSL +FL void ssl_rand_bytes(void *buf, size_t blen); +# endif + /* */ FL enum okay ssl_open(struct url const *urlp, struct sock *sp); diff --git a/xssl.c b/xssl.c index ec634628..fe242696 100644 --- a/xssl.c +++ b/xssl.c @@ -330,11 +330,14 @@ static enum okay load_crls(X509_STORE *store, enum okeys fok, enum okeys dok); static bool_t a_xssl_rand_init(void){ +#define a_XSSL_RAND_ENTROPY 32 + char b64buf[a_XSSL_RAND_ENTROPY * 5 +1], *randfile; char const *cp, *x; - bool_t rv; - NYD_ENTER; + bool_t err; + NYD2_ENTER; - rv = FAL0; + err = TRU1; + randfile = NULL; /* Shall use some external daemon? */ /* TODO obsolete *ssl_rand_egd* */ if((cp = ok_vlook(ssl_rand_egd)) != NULL){ /* TODO no one supports it now! */ @@ -342,7 +345,7 @@ a_xssl_rand_init(void){ #ifdef HAVE_XSSL_RAND_EGD if((x = fexpand(cp, FEXP_LOCAL | FEXP_NOPROTO)) != NULL && RAND_egd(cp = x) != -1){ - rv = TRU1; + err = FAL0; goto jleave; } n_err(_("*ssl_rand_egd* daemon at %s not available\n"), @@ -359,46 +362,56 @@ a_xssl_rand_init(void){ x = NULL; if(*cp != '\0'){ if((x = fexpand(cp, FEXP_LOCAL | FEXP_NOPROTO)) == NULL) - n_err(_("*ssl-rand-file*: filename expansion of %s failed\n"), + n_err(_("*ssl-rand-file*: expansion of %s failed " + "(using OpenSSL default)\n"), n_shexp_quote_cp(cp, FAL0)); } cp = x; } - - /* If the SSL PRNG is initialized don't put any more effort in this if the - * user defined entropy isn't usable */ if(cp == NULL){ - if(RAND_status()){ - rv = TRU1; - goto jleave; - } - - if((cp = RAND_file_name(salloc(PATH_MAX), PATH_MAX)) == NULL){ + randfile = n_lofi_alloc(PATH_MAX); + if((cp = RAND_file_name(randfile, PATH_MAX)) == NULL){ n_err(_("*ssl-rand-file*: no SSL entropy file, can't seed PRNG\n")); goto jleave; } } - if(RAND_load_file(cp, a_XSSL_RAND_LOAD_FILE_MAXBYTES) != -1){ - for(x = (char*)-1;;){ - RAND_seed(n_random_create_cp(32, NULL), 32); - if((rv = (RAND_status() != 0))) - break; - if((x = (char*)((uintptr_t)x >> 1)) == NULL){ - n_err(_("*ssl-rand-file*: can't seed SSL PRNG with entropy\n")); - goto jleave; - } + (void)RAND_load_file(cp, a_XSSL_RAND_LOAD_FILE_MAXBYTES); + + /* And feed in some data, then write the updated file. + * While this rather feeds the PRNG with itself in the n_RANDOM_USE_XSSL + * case, let us stir the buffer a little bit. + * Estimate a low but likely still too high number of entropy bytes, use + * 20%: base64 uses 3 input = 4 output bytes relation, and the base64 + * alphabet is a 6 bit one */ + n_LCTAV(n_RANDOM_USE_XSSL == 0 || n_RANDOM_USE_XSSL == 1); + for(x = (char*)-1;;){ + RAND_add(n_random_create_buf(b64buf, sizeof(b64buf) -1, NULL), + sizeof(b64buf) -1, a_XSSL_RAND_ENTROPY); + if((x = (char*)((uintptr_t)x >> (1 + (n_RANDOM_USE_XSSL * 3)))) == NULL){ + err = (RAND_status() == 0); + break; } +#if !n_RANDOM_USE_XSSL + if(!(err = (RAND_status() == 0))) + break; +#endif + } + + if(!err) + err = (RAND_write_file(cp) == -1); - if(RAND_write_file(cp) == -1) - n_err(_("*ssl-rand-file*: writing entropy to %s failed\n"), - n_shexp_quote_cp(cp, FAL0)); - }else - n_err(_("*ssl-rand-file*: %s cannot be loaded\n"), - n_shexp_quote_cp(cp, FAL0)); jleave: - NYD_LEAVE; - return rv; + if(randfile != NULL) + n_lofi_free(randfile); + if(err) + n_panic(_("Cannot seed the *SSL PseudoRandomNumberGenerator, " + "RAND_status() is 0!\n" + " Please set *ssl-rand-file* to a file with sufficient entropy.\n" + " On a machine with entropy: " + "\"$ dd if=/dev/urandom of=FILE bs=1024 count=1\"\n")); + NYD2_LEAVE; + return err; } static void @@ -1399,6 +1412,26 @@ jleave: return rv; } +#if n_RANDOM_USE_XSSL +FL void +ssl_rand_bytes(void *buf, size_t blen){ + NYD_ENTER; + + if(!(a_xssl_state & a_XSSL_S_RAND_INIT) && a_xssl_rand_init()) + a_xssl_state |= a_XSSL_S_RAND_INIT; + + while(blen > 0){ + si32_t i; + + i = n_MIN(SI32_MAX, blen); + blen -= i; + RAND_bytes(buf, i); + buf = (ui8_t*)buf + i; + } + NYD_LEAVE; +} +#endif + FL enum okay ssl_open(struct url const *urlp, struct sock *sp) { -- 2.11.4.GIT