We have an internal custom Apache module that generates UUIDs; it's using a legacy format that we need for our application. A few months ago, I was upgrading it to Apache 2.4, and I worked on modifying the UUID generator function, so that some of the bytes are random (including replacing the 4 byte IPv4 address element of the uuid that we used before with random data instead).
When I looked into the ap random functions, I didn't like the implementation, because I didn't see anywhere in the httpd codebase that entropy is periodically added to the entropy pool. After reading the details of how the Linux entropy pool works (https://lwn .net/Articles/525204/), I decided to use /dev/urandom instead, since Linux is periodically adding entropy to it. This code is not portable, but this was for a private Apache module that is only used on Linux. To preserve entropy on the web server machine, I also only generate a random number once per apache child, then increment an uint32 portion of it for each unique id call. I also have seconds and microseconds, so that's why I think it's OK to do increments from the random base, instead of generating a new random id on each request. Our app requires network order time_t in the second 32-bit word, so that's why the function is like below. If our app didn't require time_t, then I would not have used the high order bits of time_t, because it's fairly constant: to have a more unique id, it would be better to have more random data instead of that fairly constant data. /** * Make a new unique id as follows: * 1. Define the id. * 2. Base64-encode the id. * @param r Apache's request record. * @return The base64-encode cookie string, allocated in the request pool. */ static char *make_id( request_rec *r ) { #define RAW_COOKIE_SIZE (14) #define WANT_BASE64_COOKIE_SIZE (19) #define BASE64_ENCODE_LEN(len) (((len + 2) / 3 * 4) + 1) #define MASK_BITS(n) ((1<<n) - 1) struct { apr_uint32_t i1; /* b31: 0, b30-20: random, b19-0: usec; network o */ apr_uint32_t i2; /* seconds, network order */ apr_uint32_t i3; /* random, host order */ apr_uint16_t s1; /* random, host order */ } raw_cookie; apr_uint32_t i, rnd; char * const b64_cookie = apr_palloc( r->pool, BASE64_ENCODE_LEN(RAW_COOKIE_SIZE)); ap_assert(b64_cookie); if (unlikely(! rand_initialized)) { /* We delay initialization as late as possible in order * to collect more entropy in the random pool. */ init_random(r); } rnd = apr_atomic_inc32(&rand_uints.i1); i = rnd & MASK_BITS(11); i <<= 20; i += apr_time_usec(r->request_time); raw_cookie.i1 = htonl(i); raw_cookie.s1 = rnd >> 11; raw_cookie.i2 = htonl(apr_time_sec(r->request_time)); raw_cookie.i3 = rand_uints.i2; apr_base64_encode_binary(b64_cookie, (const void *)&raw_cookie, RAW_COOKIE_SIZE); if (BASE64_ENCODE_LEN(RAW_COOKIE_SIZE) > WANT_BASE64_COOKIE_SIZE) b64_cookie[WANT_BASE64_COOKIE_SIZE] = '\0'; return b64_cookie; } And supporting code: typedef struct { apr_uint32_t i1; apr_uint32_t i2; } rand_uints_t; static rand_uints_t rand_uints; static apr_thread_mutex_t *rand_mutex; static int rand_initialized; static void init_random(request_rec *r) { ap_assert(APR_SUCCESS == apr_thread_mutex_lock(rand_mutex)); if (rand_initialized == 0) { /* we won the race to initialize */ apr_file_t *f; apr_size_t bytes_read; ap_assert(APR_SUCCESS == apr_file_open(&f, "/dev/urandom", APR_READ|APR_BINARY, 0444, r->pool)); ap_assert(APR_SUCCESS == apr_file_read_full(f, &rand_uints, sizeof(rand_uints_t), &bytes_read)); ap_assert(sizeof(rand_uints) == bytes_read); ap_assert(APR_SUCCESS == apr_file_close(f)); rand_initialized = 1; } ap_assert(APR_SUCCESS == apr_thread_mutex_unlock(rand_mutex)); } /** * Initialization for each child process. */ static void init_child(apr_pool_t *p, server_rec *s) { ap_assert(APR_SUCCESS == apr_thread_mutex_create(&rand_mutex, APR_THREAD_MUTEX_DEFAULT, p)); } On Wed, Jun 26, 2013 at 8:49 AM, Jan Kaluža <jkal...@redhat.com> wrote: > Hi, > > currently mod_unique_id uses apr_gethostname(...) and PID pair as a base > to generate unique ID. The way how it's implemented brings some problems: > > 1. For IPv6-only hosts it uses low-order bits of IPv6 address as if they > were unique, which is wrong. > > 2. It relies on working DNS. It can happen that hostname does not have the > IP assigned during httpd start (for example during the boot) and I think it > is still valid use-case (without mod_unique_id module loaded, httpd works > well in this case). > > 3. It calls 1 second sleep to overcome possible usage of the same PID > after restart as the one used before the restart. > > If I'm right, we could fix the problems above by using > ap_random_insecure_bytes instead of "in_addr"/"pid" pair as a base for > unique ID generation. It would also make the code simpler. I think the > randomness generated by ap_random_insecure_bytes() is at least the same as > the one introduced by apr_gethostname() + pid pair. > > The attached patch implements it by removing in_addr/pid fields > unique_id_rec struct and introduces new "root" field which is initialized > using ap_random_insecure_bytes() function and is used as a base for unique > IDs. > > Regards, > Jan Kaluza >