diff --git a/src/network/ssl/ssl.c b/src/network/ssl/ssl.c index c14ab6762..4d598b444 100644 --- a/src/network/ssl/ssl.c +++ b/src/network/ssl/ssl.c @@ -8,6 +8,7 @@ #include #include #elif defined(CONFIG_GNUTLS) +#include #include #else #error "Huh?! You have SSL enabled, but not OPENSSL nor GNUTLS!! And then you want exactly *what* from me?" @@ -27,6 +28,7 @@ #include "util/conv.h" #include "util/error.h" #include "util/string.h" +#include "util/random.h" /* FIXME: As you can see, SSL is currently implemented in very, erm, @@ -282,3 +284,17 @@ get_ssl_connection_cipher(struct socket *socket) return str.source; } + +/* When CONFIG_SSL is defined, this implementation replaces the one in + * src/util/random.c. */ +void +random_nonce(unsigned char buf[], size_t size) +{ +#ifdef CONFIG_OPENSSL + RAND_pseudo_bytes(buf, size); +#elif defined(CONFIG_GNUTLS) + gcry_create_nonce(buf, size); +#else +# error unsupported SSL library +#endif +} diff --git a/src/protocol/auth/digest.c b/src/protocol/auth/digest.c index d8a2abdfb..c93bf50f3 100644 --- a/src/protocol/auth/digest.c +++ b/src/protocol/auth/digest.c @@ -15,6 +15,7 @@ #include "util/conv.h" #include "util/md5.h" #include "util/memory.h" +#include "util/random.h" /* Hexes a binary md5 digest. Taken from RFC 2617 */ @@ -31,18 +32,14 @@ convert_to_md5_digest_hex_T(md5_digest_bin_T bin, md5_digest_hex_T hex) } } -/* Initializes a random cnonce that is also a hexed md5 digest. */ +/* Initializes a random cnonce that has the same format as a hexed md5 + * digest. */ static void init_cnonce_digest(md5_digest_hex_T cnonce) { md5_digest_bin_T md5; - int random; - - srand(time(NULL)); - - random = rand(); - MD5((const unsigned char *) &random, sizeof(random), md5); + random_nonce(md5, MD5_DIGEST_LENGTH); convert_to_md5_digest_hex_T(md5, cnonce); } diff --git a/src/protocol/bittorrent/common.c b/src/protocol/bittorrent/common.c index cc932c1bf..c4591f066 100644 --- a/src/protocol/bittorrent/common.c +++ b/src/protocol/bittorrent/common.c @@ -17,6 +17,7 @@ #include "util/error.h" #include "util/lists.h" #include "util/memory.h" +#include "util/random.h" #include "util/sha1.h" #include "util/string.h" #include "util/snprintf.h" @@ -167,13 +168,10 @@ init_bittorrent_peer_id(bittorrent_id_T peer_id) } /* Hmm, sizeof(peer_id) don't work here. */ + random_nonce(peer_id + i, sizeof(bittorrent_id_T) - i); while (i < sizeof(bittorrent_id_T)) { - int random = rand(); - - while (i < sizeof(bittorrent_id_T) && (random & 0xF)) { - peer_id[i++] = hx(random & 0xF); - random >>= 4; - } + peer_id[i] = hx(peer_id[i] & 0xF); + i++; } } diff --git a/src/protocol/bittorrent/piececache.c b/src/protocol/bittorrent/piececache.c index d1644568a..306ec7ca3 100644 --- a/src/protocol/bittorrent/piececache.c +++ b/src/protocol/bittorrent/piececache.c @@ -35,6 +35,7 @@ #include "util/file.h" #include "util/lists.h" #include "util/memory.h" +#include "util/random.h" #include "util/string.h" @@ -165,7 +166,7 @@ find_random_in_bittorrent_piece_cache(struct bittorrent_piece_cache *cache, assert(peer->bitfield->bitsize == peer->bittorrent->meta.pieces); - srand(time(NULL)); + seed_rand_once(); foreachback_bitfield_set (piece, peer->bitfield) { assertm(cache->entries[piece].rarity, @@ -238,7 +239,7 @@ find_rarest_in_bittorrent_piece_cache(struct bittorrent_piece_cache *cache, assert(peer->bitfield->bitsize == peer->bittorrent->meta.pieces); - srand(time(NULL)); + seed_rand_once(); /* Try to randomize the piece picking using the strategy from the random * piece selection. */ diff --git a/src/util/Makefile b/src/util/Makefile index 1a453c4d4..c0fa9dfb7 100644 --- a/src/util/Makefile +++ b/src/util/Makefile @@ -32,6 +32,7 @@ OBJS = \ hash.o \ memlist.o \ memory.o \ + random.o \ secsave.o \ snprintf.o \ string.o \ diff --git a/src/util/random.c b/src/util/random.c new file mode 100644 index 000000000..42c6611c2 --- /dev/null +++ b/src/util/random.c @@ -0,0 +1,114 @@ +/** Random numbers. + * @file */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include + +#include "elinks.h" + +#include "util/random.h" + +void +seed_rand_once(void) +{ + static int seeded = 0; + + if (!seeded) { + srand(time(NULL)); + seeded = 1; + } +} + +#ifndef CONFIG_SSL + +static void +pseudorandom_nonce(unsigned char buf[], size_t size) +{ + static int initialized = 0; + static int accept_bits; + static int accept_mask; + unsigned int got_mask; + unsigned int got_random; + size_t index; + + if (!initialized) { + unsigned int shift; + + seed_rand_once(); + + /* 32767 <= RAND_MAX <= INT_MAX. Find the largest + * accept_mask such that accept_mask <= RAND_MAX and + * accept_mask + 1 is a power of two. */ + shift = RAND_MAX; + accept_bits = 0U; + accept_mask = 0U; + while (shift != 0U) { + shift >>= 1; + accept_bits++; + accept_mask = (accept_mask << 1) + 1U; + } + if (accept_mask > (unsigned int) RAND_MAX) { + accept_bits--; + accept_mask >>= 1; + } + + initialized = 1; + } + + got_mask = got_random = 0U; + for (index = 0; index < size; ) { + if (got_mask >= UCHAR_MAX) { + buf[index++] = (unsigned char) got_random; + got_mask >>= CHAR_BIT; + got_random >>= CHAR_BIT; + } else { + unsigned int candidate; + + do { + candidate = rand(); + } while (candidate > accept_mask); + + /* These shifts can discard some bits. */ + got_mask = (got_mask << accept_bits) | accept_mask; + got_random = (got_random << accept_bits) | candidate; + } + } +} + +/** Fill a buffer with random bytes. The bytes are not + * cryptographically random enough to be used in a key, but they + * should be good enough for a nonce or boundary string that may + * be sent in cleartext. + * + * If CONFIG_SSL is defined, then this function is instead defined in + * src/network/ssl/ssl.c, and it gets random numbers directly from the + * selected SSL library. */ +void +random_nonce(unsigned char buf[], size_t size) +{ + size_t i = 0; + FILE *f = fopen("/dev/urandom", "rb"); + + if (!f) f = fopen("/dev/prandom", "rb"); /* OpenBSD */ + if (f) { + i = fread(data, 1, length, f); + fclose(f); + } + + /* If the random device did not exist or could not provide + * enough data, then fill the buffer with rand(). The + * resulting numbers may be predictable but they provide + * ELinks with at least some way to generate boundary strings + * for multipart uploads. A more secure algorithm and entropy + * collection could be implemented, but there doesn't seem to + * be much point as SSL libraries already provide this + * facility. */ + if (i < size) + pseudorandom_nonce(buf + i, size - i); +} + +#endif /* ndef CONFIG_SSL */ diff --git a/src/util/random.h b/src/util/random.h new file mode 100644 index 000000000..bd4bb49ed --- /dev/null +++ b/src/util/random.h @@ -0,0 +1,11 @@ +/** Random numbers. + * @file */ + +#ifndef EL__UTIL_RANDOM_H +#define EL__UTIL_RANDOM_H + +void seed_rand_once(void); + +void random_nonce(unsigned char buf[], size_t size); + +#endif diff --git a/src/viewer/text/form.c b/src/viewer/text/form.c index b8adf1043..68c7276a6 100644 --- a/src/viewer/text/form.c +++ b/src/viewer/text/form.c @@ -45,6 +45,7 @@ #include "util/error.h" #include "util/file.h" #include "util/memory.h" +#include "util/random.h" #include "util/string.h" #include "viewer/action.h" #include "viewer/text/draw.h" @@ -834,14 +835,8 @@ static void randomize_boundary(unsigned char *data, int length) { int i; - FILE *f = fopen("/dev/urandom", "rb"); - if (!f) f = fopen("/dev/prandom", "rb"); /* OpenBSD */ - if (f) { - fread(data, 1, length, f); - fclose(f); - } - /* FIXME. What if both fails */ + random_nonce(data, length); for (i = 0; i < length; i++) { /* Only [0-9A-Za-z]. */ data[i] = data[i] & 63;