diff --git a/Dockerfile b/Dockerfile index 2876c8d..e441598 100644 --- a/Dockerfile +++ b/Dockerfile @@ -2,7 +2,7 @@ FROM ubuntu:latest WORKDIR . -RUN apt-get update && apt-get install -y curl gcc make git libpcre3-dev zlib1g-dev +RUN apt-get update && apt-get install -y curl gcc make git libpcre3-dev zlib1g-dev libbsd-dev ADD ipscrub /ipscrub/ ADD Makefile / diff --git a/README.md b/README.md index b68bd4b..c01b5fe 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ ## Security Model -1. On initialization, and again every `PERIOD`, generate `salt` as `HASH(ngx_random() ++ timestamp)`. +1. On initialization, and again every `PERIOD`, generate `salt` using 128bits from `arc4random_buf()`. 2. On each request, generate masked IP address as `HASH(salt ++ IP address)`. 3. Log masked IP address. diff --git a/ipscrub/config b/ipscrub/config index d261e37..8d91d40 100644 --- a/ipscrub/config +++ b/ipscrub/config @@ -4,7 +4,13 @@ ngx_module_type=HTTP ngx_module_name=ngx_ipscrub_module ngx_module_deps="$ngx_addon_dir/src/ngx_ipscrub_support.h $ngx_addon_dir/src/ngx_ipscrub_debug.h" ngx_module_srcs="$ngx_addon_dir/src/ngx_ipscrub_module.c $ngx_addon_dir/src/ngx_ipscrub_support.c $ngx_addon_dir/src/ngx_ipscrub_debug.c" -ngx_module_libs=SHA1 + +# On Linux, link with libbsd, to get arc4random. +if [ `uname` = Linux ]; then + ngx_module_libs="SHA1 -lbsd" +else + ngx_module_libs="SHA1" +fi . auto/module diff --git a/ipscrub/src/ngx_ipscrub_module.c b/ipscrub/src/ngx_ipscrub_module.c index e01d8ef..8176403 100644 --- a/ipscrub/src/ngx_ipscrub_module.c +++ b/ipscrub/src/ngx_ipscrub_module.c @@ -42,7 +42,8 @@ static ngx_command_t ngx_ipscrub_commands[] = { // Globals. const int default_period_seconds = 10 * 60; // Period between salt changes. time_t period_start = -1; -long nonce = -1; // Input to salt generation. +#define num_nonce_bytes 16 +u_char nonce[num_nonce_bytes]; // Input to salt generation. static ngx_http_module_t ngx_ipscrub_module_ctx = { @@ -146,7 +147,7 @@ ngx_http_variable_remote_addr_ipscrub(ngx_http_request_t *r, ngx_http_variable_v // Regenerate salt if past end of period. time_t now = time(NULL); if (period_start == -1 || now - period_start > icf->period_seconds) { - rc = randlong(&nonce); + rc = randbytes((u_char *) &nonce, num_nonce_bytes); if (rc != NGX_OK) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } @@ -156,7 +157,7 @@ ngx_http_variable_remote_addr_ipscrub(ngx_http_request_t *r, ngx_http_variable_v } salt.data = (u_char *) &nonce; - salt.len = sizeof(nonce); + salt.len = num_nonce_bytes; // Although ngx_crypt provides a salted SHA function, specified by a salt beginning with {SSHA}, that function exposes the salt in its result. For our security model, this is inappropriate. Instead, we use the regular nginx SHA function specified by {SHA}, and manually combine the nonce and plaintext. rc = concat(r->pool, r->connection->addr_text, salt, &combined); diff --git a/ipscrub/src/ngx_ipscrub_support.c b/ipscrub/src/ngx_ipscrub_support.c index 2f68691..fb3300d 100644 --- a/ipscrub/src/ngx_ipscrub_support.c +++ b/ipscrub/src/ngx_ipscrub_support.c @@ -3,6 +3,15 @@ #include "ngx_ipscrub_support.h" +#if (NGX_FREEBSD || NGX_SOLARIS || NGX_DARWIN) +// arc4random is built-in on these platforms. +#elif (NGX_LINUX) +#include +#else +// TODO: test using libbsd on Windows. +#error ipscrub requires arc4random_buf. +#endif + // null_terminate allocates a new, null-terminated string based on input. ngx_int_t null_terminate(ngx_pool_t *pool, ngx_str_t input, u_char **out) { @@ -37,23 +46,20 @@ ngx_int_t concat(ngx_pool_t *pool, ngx_str_t prefix, ngx_str_t suffix, u_char ** return NGX_OK; } -// randlong fills out with secure random bytes and returns NGX_OK iff successful. -ngx_int_t randlong(long *out) { - #if !(NGX_DARWIN || NGX_SOLARIS || NGX_FREEBSD || NGX_LINUX) - // Windows not supported a.t.m. - // TODO: support Windows (https://msdn.microsoft.com/en-us/library/sxtz2fa8.aspx). - return -1; - #endif - - int rand = open("/dev/urandom", O_RDONLY); - if (rand < 0) { - return -1; +// randbytes fills out with secure random bytes. +// Return value of NGX_OK indicates success. +// Return value of NGX_ERROR indicates error. +ngx_int_t randbytes(u_char *out, int num_bytes) { + if (out == NULL) { + return NGX_ERROR; + } + if (num_bytes < 1 || num_bytes > 64) { + // Values outside these bounds may indicate parameter usage mistake. + return NGX_ERROR; } - ssize_t ret = read(rand, out, sizeof(long)); - if (ret != sizeof(long)) { - return -1; - } + + arc4random_buf(out, num_bytes); return NGX_OK; } diff --git a/ipscrub/src/ngx_ipscrub_support.h b/ipscrub/src/ngx_ipscrub_support.h index 7509911..38ea3f0 100644 --- a/ipscrub/src/ngx_ipscrub_support.h +++ b/ipscrub/src/ngx_ipscrub_support.h @@ -8,6 +8,6 @@ ngx_int_t null_terminate(ngx_pool_t *pool, ngx_str_t input, u_char **hashed); ngx_int_t concat(ngx_pool_t *pool, ngx_str_t prefix, ngx_str_t suffix, u_char **out); -ngx_int_t randlong(long *out); +ngx_int_t randbytes(u_char *out, int num_bytes); #endif /* _IPSCRUB_SUPPORT_H_INCLUDED_ */