diff --git a/README b/README index 39da353..1e3525a 100644 --- a/README +++ b/README @@ -73,6 +73,24 @@ Synopsis # $b == 'abcde' } + location /base64 { + set $a 'abcde'; + set_encode_base64 $a; + set_decode_base64 $b $a; + + # now $a == 'YWJjZGU=' and + # $b == 'abcde' + } + + location /hex { + set $a 'abcde'; + set_encode_hex $a; + set_decode_hex $b $a; + + # now $a == '6162636465' and + # $b == 'abcde' + } + # GET /sha1 yields the output # aaf4c61ddcc5e8a2dabede0f3b482cd9aea9434d location /sha1 { @@ -93,6 +111,18 @@ Synopsis echo $today; } + # GET /signature yields the hmac-sha-1 signature + # given a secret and a string to sign + # this example yields the base64 encoded singature which is + # "HkADYytcoQQzqbjQX33k/ZBB/DQ=" + location /signature { + set $secret_key 'secret-key'; + set $string_to_sign "some-string-to-sign"; + set_hmac_sha1 $signature $secret_key $string_to_sign; + set_encode_base64 $signature $signature; + echo $signature; + } + Description Every directive provided by this module can be mixed freely with other nginx rewrite module's @@ -124,10 +154,21 @@ Directives RFC forces the [A-Z2-7] RFC-3548 compliant encoding, but we're using the "base32hex" encoding [0-9a-v]. + set_encode_base64 + + set_decode_base64 + + set_encode_hex + + set_decode_hex + set_sha1 (can be used with ngx_array module's array_map_op) set_local_today + set_hmac_sha1 (only enabled when nginx uses openssl, as with ssl support) + + Caveats Do not use $arg_XXX or $http_XXX or other special variables defined in the nginx core module as the target variable in this module's directives. diff --git a/config b/config index 9f19541..ee2eac4 100755 --- a/config +++ b/config @@ -1,7 +1,13 @@ ngx_addon_name=ngx_http_set_misc_module HTTP_AUX_FILTER_MODULES="$HTTP_AUX_FILTER_MODULES ngx_http_set_misc_module" -NGX_ADDON_SRCS="$NGX_ADDON_SRCS $ngx_addon_dir/src/ngx_http_set_base32.c $ngx_addon_dir/src/ngx_http_set_default_value.c $ngx_addon_dir/src/ngx_http_set_hashed_upstream.c $ngx_addon_dir/src/ngx_http_set_quote_sql.c $ngx_addon_dir/src/ngx_http_set_quote_json.c $ngx_addon_dir/src/ngx_http_set_unescape_uri.c $ngx_addon_dir/src/ngx_http_set_misc_module.c $ngx_addon_dir/src/ngx_http_set_escape_uri.c $ngx_addon_dir/src/ngx_http_set_hash.c $ngx_addon_dir/src/ngx_http_set_local_today.c" -NGX_ADDON_DEPS="$NGX_ADDON_DEPS $ngx_addon_dir/src/ddebug.h $ngx_addon_dir/src/ngx_http_set_default_value.h $ngx_addon_dir/src/ngx_http_set_hashed_upstream.h $ngx_addon_dir/src/ngx_http_set_quote_sql.h $ngx_addon_dir/src/ngx_http_set_quote_json.h $ngx_addon_dir/src/ngx_http_set_unescape_uri.h $ngx_addon_dir/src/ngx_http_set_escape_uri.h $ngx_addon_dir/src/ngx_http_set_hash.h $ngx_addon_dir/src/ngx_http_set_local_today.h" +NGX_ADDON_SRCS="$NGX_ADDON_SRCS $ngx_addon_dir/src/ngx_http_set_base32.c $ngx_addon_dir/src/ngx_http_set_default_value.c $ngx_addon_dir/src/ngx_http_set_hashed_upstream.c $ngx_addon_dir/src/ngx_http_set_quote_sql.c $ngx_addon_dir/src/ngx_http_set_quote_json.c $ngx_addon_dir/src/ngx_http_set_unescape_uri.c $ngx_addon_dir/src/ngx_http_set_misc_module.c $ngx_addon_dir/src/ngx_http_set_escape_uri.c $ngx_addon_dir/src/ngx_http_set_hash.c $ngx_addon_dir/src/ngx_http_set_local_today.c $ngx_addon_dir/src/ngx_http_set_hex.c $ngx_addon_dir/src/ngx_http_set_base64.c" +NGX_ADDON_DEPS="$NGX_ADDON_DEPS $ngx_addon_dir/src/ddebug.h $ngx_addon_dir/src/ngx_http_set_default_value.h $ngx_addon_dir/src/ngx_http_set_hashed_upstream.h $ngx_addon_dir/src/ngx_http_set_quote_sql.h $ngx_addon_dir/src/ngx_http_set_quote_json.h $ngx_addon_dir/src/ngx_http_set_unescape_uri.h $ngx_addon_dir/src/ngx_http_set_escape_uri.h $ngx_addon_dir/src/ngx_http_set_hash.h $ngx_addon_dir/src/ngx_http_set_local_today.h $ngx_addon_dir/src/ngx_http_set_hex.h $ngx_addon_dir/src/ngx_http_set_base64.h" + +if [ $USE_OPENSSL = YES ]; then + NGX_ADDON_DEPS="$NGX_ADDON_DEPS $ngx_addon_dir/src/ngx_http_set_hmac.h" + NGX_ADDON_SRCS="$NGX_ADDON_SRCS $ngx_addon_dir/src/ngx_http_set_hmac.c" +fi CFLAGS="$CFLAGS -DNDK_SET_VAR -DNDK_UPSTREAM_LIST" USE_SHA1=YES USE_MD5=YES + diff --git a/src/ngx_http_set_base64.c b/src/ngx_http_set_base64.c new file mode 100644 index 0000000..ac978e5 --- /dev/null +++ b/src/ngx_http_set_base64.c @@ -0,0 +1,44 @@ +#define DDEBUG 0 +#include "ddebug.h" +#include + +#include "ngx_http_set_base64.h" + +ngx_int_t +ngx_http_set_misc_set_decode_base64(ngx_http_request_t *r, + ngx_str_t *res, ngx_http_variable_value_t *v) +{ + + ngx_str_t src; + + src.len = v->len; + src.data = v->data; + + res->len = ngx_base64_decoded_length(v->len); + ndk_palloc_re(res->data, r->pool, res->len); + + if (ngx_decode_base64(res, &src) != NGX_OK) { + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "set_decode_base64: invalid value"); + return NGX_ERROR; + } + return NGX_OK; +} + + +ngx_int_t +ngx_http_set_misc_set_encode_base64(ngx_http_request_t *r, + ngx_str_t *res, ngx_http_variable_value_t *v) +{ + + ngx_str_t src; + + src.len = v->len; + src.data = v->data; + + res->len = ngx_base64_encoded_length(v->len); + ndk_palloc_re(res->data, r->pool, res->len); + ngx_encode_base64(res, &src); + return NGX_OK; +} + diff --git a/src/ngx_http_set_base64.h b/src/ngx_http_set_base64.h new file mode 100644 index 0000000..6594c77 --- /dev/null +++ b/src/ngx_http_set_base64.h @@ -0,0 +1,10 @@ +#include +#include +#include + +ngx_int_t ngx_http_set_misc_set_encode_base64(ngx_http_request_t *r, + ngx_str_t *res, ngx_http_variable_value_t *v); + +ngx_int_t ngx_http_set_misc_set_decode_base64(ngx_http_request_t *r, + ngx_str_t *res, ngx_http_variable_value_t *v); + diff --git a/src/ngx_http_set_hex.c b/src/ngx_http_set_hex.c new file mode 100644 index 0000000..d2be741 --- /dev/null +++ b/src/ngx_http_set_hex.c @@ -0,0 +1,46 @@ +#define DDEBUG 0 +#include "ddebug.h" +#include + +#include "ngx_http_set_hex.h" + +ngx_int_t +ngx_http_set_misc_set_decode_hex(ngx_http_request_t *r, + ngx_str_t *res, ngx_http_variable_value_t *v) +{ + + u_char *p; + ngx_int_t n; + ngx_int_t i; + + if (v->len % 2 != 0) { + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "set_decode_hex: invalid value"); + return NGX_ERROR; + } + p = v->data; + ndk_palloc_re(res->data, r->pool, v->len/2); + for (i = 0; i < v->len/2; i++) { + n = ngx_hextoi(p, 2); + if (n == NGX_ERROR || n > 255) { + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "set_decode_hex: invalid value"); + return NGX_ERROR; + } + p += 2; + res->data[i] = (u_char) n; + } + res->len = v->len/2; + return NGX_OK; +} + + +ngx_int_t +ngx_http_set_misc_set_encode_hex(ngx_http_request_t *r, + ngx_str_t *res, ngx_http_variable_value_t *v) +{ + res->len = (v->len)*2; + ndk_palloc_re(res->data, r->pool, res->len); + ngx_hex_dump(res->data, v->data, v->len); + return NGX_OK; +} diff --git a/src/ngx_http_set_hex.h b/src/ngx_http_set_hex.h new file mode 100644 index 0000000..e8b77e0 --- /dev/null +++ b/src/ngx_http_set_hex.h @@ -0,0 +1,9 @@ +#include +#include +#include + +ngx_int_t ngx_http_set_misc_set_decode_hex(ngx_http_request_t *r, + ngx_str_t *res, ngx_http_variable_value_t *v); + +ngx_int_t ngx_http_set_misc_set_encode_hex(ngx_http_request_t *r, + ngx_str_t *res, ngx_http_variable_value_t *v); diff --git a/src/ngx_http_set_hmac.c b/src/ngx_http_set_hmac.c new file mode 100644 index 0000000..708f768 --- /dev/null +++ b/src/ngx_http_set_hmac.c @@ -0,0 +1,36 @@ +#define DDEBUG 0 +#include "ddebug.h" +#include + +#include "ngx_http_set_hmac.h" +#include +#include + +/* this function's implementation is partly borrowed from + * https://github.com/anomalizer/ngx_aws_auth */ +ngx_int_t +ngx_http_set_misc_set_hmac_sha1(ngx_http_request_t *r, + ngx_str_t *res, ngx_http_variable_value_t *v) +{ + ngx_http_variable_value_t *secret, *string_to_sign; + unsigned int md_len; + unsigned char md[EVP_MAX_MD_SIZE]; + const EVP_MD *evp_md = EVP_sha1(); + + secret = v; + string_to_sign = v+1; + + dd("secret=%s, string_to_sign=%s", secret->data, string_to_sign->data); + + HMAC(evp_md, secret->data, secret->len, string_to_sign->data, + string_to_sign->len, md, &md_len); + + res->len = md_len; + ndk_palloc_re(res->data, r->pool, md_len); + ngx_memcpy(res->data, + &md, + md_len); + return NGX_OK; +} + + diff --git a/src/ngx_http_set_hmac.h b/src/ngx_http_set_hmac.h new file mode 100644 index 0000000..833d17c --- /dev/null +++ b/src/ngx_http_set_hmac.h @@ -0,0 +1,7 @@ +#include +#include +#include + +ngx_int_t ngx_http_set_misc_set_hmac_sha1(ngx_http_request_t *r, + ngx_str_t *res, ngx_http_variable_value_t *v); + diff --git a/src/ngx_http_set_misc_module.c b/src/ngx_http_set_misc_module.c index 75abf9d..164cd00 100755 --- a/src/ngx_http_set_misc_module.c +++ b/src/ngx_http_set_misc_module.c @@ -11,9 +11,52 @@ #include "ngx_http_set_escape_uri.h" #include "ngx_http_set_local_today.h" #include "ngx_http_set_hash.h" +#include "ngx_http_set_hex.h" +#include "ngx_http_set_base64.h" +#if NGX_OPENSSL +#include "ngx_http_set_hmac.h" +#endif #define NGX_UNESCAPE_URI_COMPONENT 0 +static ndk_set_var_t ngx_http_set_misc_set_encode_base64_filter = { + NDK_SET_VAR_VALUE, + ngx_http_set_misc_set_encode_base64, + 1, + NULL +}; + +static ndk_set_var_t ngx_http_set_misc_set_decode_base64_filter = { + NDK_SET_VAR_VALUE, + ngx_http_set_misc_set_decode_base64, + 1, + NULL +}; + + +static ndk_set_var_t ngx_http_set_misc_set_decode_hex_filter = { + NDK_SET_VAR_VALUE, + ngx_http_set_misc_set_decode_hex, + 1, + NULL +}; + +static ndk_set_var_t ngx_http_set_misc_set_encode_hex_filter = { + NDK_SET_VAR_VALUE, + ngx_http_set_misc_set_encode_hex, + 1, + NULL +}; + +#if NGX_OPENSSL +static ndk_set_var_t ngx_http_set_misc_set_hmac_sha1_filter = { + NDK_SET_VAR_MULTI_VALUE, + ngx_http_set_misc_set_hmac_sha1, + 2, + NULL +}; +#endif + #ifndef NGX_HTTP_SET_HASH static ndk_set_var_t ngx_http_set_misc_set_md5_filter = { NDK_SET_VAR_VALUE, @@ -87,6 +130,48 @@ static ndk_set_var_t ngx_http_set_misc_local_today_filter = { }; static ngx_command_t ngx_http_set_misc_commands[] = { + { ngx_string ("set_encode_base64"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_SIF_CONF + |NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_TAKE12, + ndk_set_var_value, + 0, + 0, + &ngx_http_set_misc_set_encode_base64_filter + }, + { ngx_string ("set_decode_base64"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_SIF_CONF + |NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_TAKE12, + ndk_set_var_value, + 0, + 0, + &ngx_http_set_misc_set_decode_base64_filter + }, + { ngx_string ("set_decode_hex"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_SIF_CONF + |NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_TAKE12, + ndk_set_var_value, + 0, + 0, + &ngx_http_set_misc_set_decode_hex_filter + }, + { ngx_string ("set_encode_hex"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_SIF_CONF + |NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_TAKE12, + ndk_set_var_value, + 0, + 0, + &ngx_http_set_misc_set_encode_hex_filter + }, +#if NGX_OPENSSL + { ngx_string ("set_hmac_sha1"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_SIF_CONF + |NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_TAKE3, + ndk_set_var_multi_value, + 0, + 0, + &ngx_http_set_misc_set_hmac_sha1_filter + }, +#endif #ifndef NGX_HTTP_SET_HASH { ngx_string ("set_md5"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_SIF_CONF diff --git a/t/base64.t b/t/base64.t new file mode 100644 index 0000000..10471ad --- /dev/null +++ b/t/base64.t @@ -0,0 +1,38 @@ +# vi:filetype=perl + +use lib 'lib'; +use Test::Nginx::Socket; + +#repeat_each(3); + +plan tests => repeat_each() * 2 * blocks(); + +no_long_string(); + +run_tests(); + +#no_diff(); + +__DATA__ + +=== TEST 1: base64 encode +--- config + location /bar { + set_encode_base64 $out "abcde"; + echo $out; + } +--- request + GET /bar +--- response_body +YWJjZGU= + +=== TEST 2: base64 decode +--- config + location /bar { + set_decode_base64 $out "YWJjZGU="; + echo $out; + } +--- request + GET /bar +--- response_body +abcde diff --git a/t/hex.t b/t/hex.t new file mode 100644 index 0000000..895a7bd --- /dev/null +++ b/t/hex.t @@ -0,0 +1,38 @@ +# vi:filetype=perl + +use lib 'lib'; +use Test::Nginx::Socket; + +#repeat_each(3); + +plan tests => repeat_each() * 2 * blocks(); + +no_long_string(); + +run_tests(); + +#no_diff(); + +__DATA__ + +=== TEST 1: hex encode +--- config + location /bar { + set_encode_hex $out "abcde"; + echo $out; + } +--- request + GET /bar +--- response_body +6162636465 + +=== TEST 2: hex decode +--- config + location /bar { + set_decode_hex $out "6162636465"; + echo $out; + } +--- request + GET /bar +--- response_body +abcde diff --git a/t/hmac.t b/t/hmac.t new file mode 100644 index 0000000..8267bbf --- /dev/null +++ b/t/hmac.t @@ -0,0 +1,46 @@ +# vi:filetype=perl + +use lib 'lib'; +use Test::Nginx::Socket; + +#repeat_each(3); + +plan tests => repeat_each() * 2 * blocks(); + +no_long_string(); + +run_tests(); + +#no_diff(); + +__DATA__ + +=== TEST 1: hmac +--- config + location /bar { + set $secret 'thisisverysecretstuff'; + set $string_to_sign 'some string we want to sign'; + set_hmac_sha1 $signature $secret $string_to_sign; + set_encode_base64 $signature $signature; + echo $signature; + } +--- request + GET /bar +--- response_body +R/pvxzHC4NLtj7S+kXFg/NePTmk= + +=== TEST 2: hmac empty vars +--- config + location /bar { + set $secret ''; + set $string_to_sign ''; + set_hmac_sha1 $signature $secret $string_to_sign; + set_encode_base64 $signature $signature; + echo $signature; + } +--- request + GET /bar +--- response_body ++9sdGxiqbAgyS31ktx+3Y3BpDh0= + +