From a41acf4983ee28d00b69ff88e6fd530ad3b62514 Mon Sep 17 00:00:00 2001 From: simon987 Date: Sun, 1 Mar 2020 21:05:36 -0500 Subject: [PATCH] PoC (wip...) --- CMakeLists.txt | 2 +- build.sh | 8 +- library.c | 270 +++++++++++++++++++++++++++++++++++++------------ 3 files changed, 216 insertions(+), 64 deletions(-) mode change 100644 => 100755 build.sh diff --git a/CMakeLists.txt b/CMakeLists.txt index 832940d..71663a1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,7 +4,7 @@ project(nginx_js C) set(CMAKE_C_STANDARD 11) -add_library(nginx_js SHARED library.c library.h) +add_library(nginx_js SHARED library.c) target_include_directories( nginx_js diff --git a/build.sh b/build.sh old mode 100644 new mode 100755 index 0597360..90283d2 --- a/build.sh +++ b/build.sh @@ -9,6 +9,12 @@ echo $CONFIG_ARGS ( cd ${NGINX_PATH} -bash -c "./configure ${CONFIG_ARGS}" +#bash -c "./configure ${CONFIG_ARGS}" make modules +#cp objs/ "${WD}" ) + +#rm /test/*.so +mv /home/simon/Downloads/nginx-1.16.1/objs/ngx_http_hello_world_module.so /test/module.so +chown -R www-data /test/ +systemctl restart nginx diff --git a/library.c b/library.c index 246ac00..b6468f4 100644 --- a/library.c +++ b/library.c @@ -1,35 +1,37 @@ #include #include "ngx_http.c" -#define HELLO_WORLD "hello world\r\n" +static ngx_int_t ngx_http_hello_world(ngx_conf_t *cf); + +static char *setup1(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); -static char *ngx_http_hello_world(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); static ngx_int_t ngx_http_hello_world_handler(ngx_http_request_t *r); -/** - * This module provided directive: hello world. - * - */ + static ngx_command_t ngx_http_hello_world_commands[] = { - { ngx_string("hello_world"), /* directive */ - NGX_HTTP_LOC_CONF|NGX_CONF_NOARGS, /* location context and takes - no arguments*/ - ngx_http_hello_world, /* configuration setup function */ - 0, /* No offset. Only one context is supported. */ - 0, /* No offset when storing the module configuration on struct. */ - NULL}, + {ngx_string("hello_world"), + + // NGX_CONF_TAKE1, for 1 arg etc + // NGX_CONF_FLAG for boolean + NGX_HTTP_LOC_CONF | NGX_HTTP_SRV_CONF | NGX_CONF_NOARGS, + + + setup1, /* configuration setup function */ + 0, /* No offset. Only one context is supported. */ + 0, /* No offset when storing the module configuration on struct. */ + NULL}, ngx_null_command /* command termination */ }; /* The hello world string. */ -static u_char ngx_hello_world[] = HELLO_WORLD; +//static u_char ngx_hello_world[] = HELLO_WORLD; /* The module context. */ static ngx_http_module_t ngx_http_hello_world_module_ctx = { NULL, /* preconfiguration */ - NULL, /* postconfiguration */ + ngx_http_hello_world, /* postconfiguration */ NULL, /* create main configuration */ NULL, /* init main configuration */ @@ -57,65 +59,209 @@ ngx_module_t ngx_http_hello_world_module = { NGX_MODULE_V1_PADDING }; -/** - * Content handler. - * - * @param r - * Pointer to the request structure. See http_request.h. - * @return - * The status of the response generation. - */ -static ngx_int_t ngx_http_hello_world_handler(ngx_http_request_t *r) -{ - ngx_buf_t *b; +__always_inline +void buf2hex(const unsigned char *buf, size_t buflen, char *hex_string) { + static const char hexdig[] = "0123456789ABCDEF"; + + const unsigned char *p; + size_t i; + + char *s = hex_string; + for (i = 0, p = buf; i < buflen; i++, p++) { + *s++ = hexdig[(*p >> 4) & 0x0f]; + *s++ = hexdig[*p & 0x0f]; + } +} + +#define SHA1_MD_LEN 20 +#define SHA1_STR_LEN 40 + +const int JS_SOLVER_CHALLENGE_OFFSET = 84 + 8 + 13; +//static const u_char JS_SOLVER[] = "Hello, workd"; +static const u_char JS_SOLVER[] = + "" + "Hello"; + +int serve_challenge(ngx_http_request_t *r, const char *challenge) { + + ngx_buf_t *b = ngx_pcalloc(r->pool, sizeof(ngx_buf_t)); ngx_chain_t out; - /* Set the Content-Type header. */ - r->headers_out.content_type.len = sizeof("text/plain") - 1; - r->headers_out.content_type.data = (u_char *) "text/plain"; + unsigned char buf[9000]; + memcpy(buf, JS_SOLVER, sizeof(JS_SOLVER)); + memcpy(buf + JS_SOLVER_CHALLENGE_OFFSET, challenge, SHA1_STR_LEN); - /* Allocate a new buffer for sending out the reply. */ - b = ngx_pcalloc(r->pool, sizeof(ngx_buf_t)); - - /* Insertion in the buffer chain. */ out.buf = b; - out.next = NULL; /* just one buffer */ + out.next = NULL; - b->pos = ngx_hello_world; /* first position in memory of the data */ - b->last = ngx_hello_world + sizeof(ngx_hello_world) - 1; /* last position in memory of the data */ - b->memory = 1; /* content is in read-only memory */ - b->last_buf = 1; /* there will be no more buffers in the request */ + // TODO: is that stack buffer gonna cause problems? + b->pos = buf; + b->last = buf + sizeof(JS_SOLVER) - 1; + b->memory = 1; + b->last_buf = 1; - /* Sending the headers for the reply. */ - r->headers_out.status = NGX_HTTP_OK; /* 200 status code */ - /* Get the content length of the body. */ - r->headers_out.content_length_n = sizeof(ngx_hello_world) - 1; - ngx_http_send_header(r); /* Send the headers */ + r->headers_out.status = NGX_HTTP_OK; + r->headers_out.content_length_n = sizeof(JS_SOLVER) - 1; + ngx_http_send_header(r); - /* Send the body, and return the status code of the output filter chain. */ return ngx_http_output_filter(r, &out); -} /* ngx_http_hello_world_handler */ +} /** - * Configuration setup function that installs the content handler. - * - * @param cf - * Module configuration structure pointer. - * @param cmd - * Module directives structure pointer. - * @param conf - * Module configuration structure pointer. - * @return string - * Status of the configuration setup. + * @param bucket + * @param addr + * @param secret + * @param out 40 bytes long string! */ -static char *ngx_http_hello_world(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) -{ - ngx_http_core_loc_conf_t *clcf; /* pointer to core location configuration */ +void get_challenge_string(int32_t bucket, ngx_str_t addr, const char *secret, char *out) { + char buf[4096]; + unsigned char md[SHA1_MD_LEN]; - /* Install the hello world handler. */ - clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module); - clcf->handler = ngx_http_hello_world_handler; + /* + * Challenge= hex( SHA1( concat(bucket, addr, secret) ) ) + */ + *((int32_t *) buf) = bucket; + memcpy((buf + sizeof(int32_t)), addr.data, addr.len); + memcpy((buf + sizeof(int32_t) + addr.len), secret, strlen(secret)); + SHA1((unsigned char *) buf, (size_t) (sizeof(int32_t) + addr.len + strlen(secret)), md); + buf2hex(md, SHA1_MD_LEN, out); +} + +int verify_response(int32_t bucket, ngx_str_t addr, const char *secret, ngx_str_t response, char *challenge) { + + /* + * Response is valid if it starts by the challenge, and + * its SHA1 hash contains the digits 0xB00B5 at the offset + * of the first digit + * + * e.g. + * challenge = "CC003677C91D53E29F7095FF90C670C69C7C46E7" + * response = "CC003677C91D53E29F7095FF90C670C69C7C46E7635919" + * SHA1(response) = "CCAE6E414FA62F9C2DFC2742B00B5C94A549BAE6" + * ^ offset 24 + */ + + if (response.len <= SHA1_STR_LEN) { + return -1; + } + + if (strncmp(challenge, (char *) response.data, SHA1_STR_LEN) != 0) { + return -1; + } + + unsigned char md[SHA1_MD_LEN]; + SHA1((unsigned char *) response.data, response.len, md); + + unsigned int nibble1 = challenge[0] & 0xF0; + + return md[nibble1] == 0 && md[nibble1 + 1] == 0; +} + +int get_cookie(ngx_http_request_t *r, ngx_str_t *name, ngx_str_t *value) { + ngx_table_elt_t **h; + + h = r->headers_in.cookies.elts; + + for (ngx_uint_t i = 0; i < r->headers_in.cookies.nelts; i++) { + u_char *start = h[i]->value.data; + u_char *end = h[i]->value.data + h[i]->value.len; + + while (start < end) { + while (start < end && *start == ' ') { start++; } + + if (ngx_strncmp(start, name->data, name->len) == 0) { + u_char *last; + for (last = start; last < end && *last != ';'; last++) {} + while (*start++ != '=' && start < last) {} + + value->data = start; + value->len = (last - start); + return 0; + } + while (*start++ != ';' && start < end) {} + } + } + + return -1; +} + +static ngx_int_t ngx_http_hello_world_handler(ngx_http_request_t *r) { + + //TODO: If the bucket is less than 5sec away from the next one, accept both current and latest bucket + + //TODO: argument + const char *secret = "my secret"; + + //TODO: argument + const long bucketSize = 30; + + long bucket = r->start_sec - (r->start_sec % bucketSize); + ngx_str_t addr = r->connection->addr_text; + + // Use real-ip ? + char challenge[SHA1_STR_LEN]; + get_challenge_string(bucket, addr, secret, challenge); + + ngx_str_t response; + int ret = get_cookie(r, &((ngx_str_t) ngx_string("res")), &response); + + //TODO: remove debug msg + char msg[4096]; + + sprintf(msg, "TS=%lu BUCKET=%lu SECRET=%s CHALLENGE=%s RET=%d COOKIE=%s", + r->start_sec, bucket, secret, challenge, ret, response.data); + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, msg); + + get_challenge_string(bucket, addr, secret, challenge); + + if (ret == NGX_DECLINED || verify_response(bucket, addr, secret, response, challenge) != 0) { + //Serve challenge + return serve_challenge(r, challenge); + } + + return NGX_DECLINED; +} + +//ngx_conf_set_flag_slot: translates "on" or "off" to 1 or 0 +//ngx_conf_set_str_slot: saves a string as an ngx_str_t +//ngx_conf_set_num_slot: parses a number and saves it to an int +//ngx_conf_set_size_slot: parses a data size ("8k", "1m", etc.) and saves it to a size_t + + +static char *setup1(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { +// ngx_http_core_loc_conf_t *clcf; /* pointer to core location configuration */ +// clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module); +// clcf->handler = ngx_http_hello_world_handler; return NGX_CONF_OK; -} /* ngx_http_hello_world */ +} + +static ngx_int_t ngx_http_hello_world(ngx_conf_t *cf) { + + ngx_http_handler_pt *h; + ngx_http_core_main_conf_t *cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module); + + h = ngx_array_push(&cmcf->phases[NGX_HTTP_CONTENT_PHASE].handlers); + if (h == NULL) { + ngx_log_error(NGX_LOG_ERR, cf->log, 0, "null"); + return NGX_ERROR; + } + + *h = ngx_http_hello_world_handler; + + return NGX_OK; +}