From b4a278fd6935e7ac5fff91ae4619fb44504145ef Mon Sep 17 00:00:00 2001 From: Nate Gates Date: Mon, 1 Mar 2021 13:08:55 -0600 Subject: [PATCH 1/4] tidy up code, attempt to add flag for localhost bypass --- ngx_http_auth_digest_module.c | 779 +++++++++++++++++++++++----------- ngx_http_auth_digest_module.h | 174 +++++--- 2 files changed, 638 insertions(+), 315 deletions(-) diff --git a/ngx_http_auth_digest_module.c b/ngx_http_auth_digest_module.c index 69a228e..234ac63 100644 --- a/ngx_http_auth_digest_module.c +++ b/ngx_http_auth_digest_module.c @@ -12,11 +12,13 @@ #include "ngx_http_auth_digest_module.h" -static void *ngx_http_auth_digest_create_loc_conf(ngx_conf_t *cf) { +static void *ngx_http_auth_digest_create_loc_conf(ngx_conf_t *cf) +{ ngx_http_auth_digest_loc_conf_t *conf; conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_auth_digest_loc_conf_t)); - if (conf == NULL) { + if (conf == NULL) + { return NULL; } @@ -29,8 +31,8 @@ static void *ngx_http_auth_digest_create_loc_conf(ngx_conf_t *cf) { return conf; } -static char *ngx_http_auth_digest_merge_loc_conf(ngx_conf_t *cf, void *parent, - void *child) { +static char *ngx_http_auth_digest_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child) +{ ngx_http_auth_digest_loc_conf_t *prev = parent; ngx_http_auth_digest_loc_conf_t *conf = child; @@ -41,18 +43,21 @@ static char *ngx_http_auth_digest_merge_loc_conf(ngx_conf_t *cf, void *parent, ngx_conf_merge_sec_value(conf->evasion_time, prev->evasion_time, 300); ngx_conf_merge_value(conf->maxtries, prev->maxtries, 5); - if (conf->user_file.value.len == 0) { + if (conf->user_file.value.len == 0) + { conf->user_file = prev->user_file; } - if (conf->realm.value.len == 0) { + if (conf->realm.value.len == 0) + { conf->realm = prev->realm; } return NGX_CONF_OK; } -static ngx_int_t ngx_http_auth_digest_init(ngx_conf_t *cf) { +static ngx_int_t ngx_http_auth_digest_init(ngx_conf_t *cf) +{ ngx_http_handler_pt *h; ngx_http_core_main_conf_t *cmcf; ngx_str_t *shm_name; @@ -60,7 +65,8 @@ static ngx_int_t ngx_http_auth_digest_init(ngx_conf_t *cf) { cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module); h = ngx_array_push(&cmcf->phases[NGX_HTTP_ACCESS_PHASE].handlers); - if (h == NULL) { + if (h == NULL) + { return NGX_ERROR; } @@ -68,7 +74,8 @@ static ngx_int_t ngx_http_auth_digest_init(ngx_conf_t *cf) { ngx_http_auth_digest_cleanup_timer = ngx_pcalloc(cf->pool, sizeof(ngx_event_t)); - if (ngx_http_auth_digest_cleanup_timer == NULL) { + if (ngx_http_auth_digest_cleanup_timer == NULL) + { return NGX_ERROR; } @@ -76,14 +83,16 @@ static ngx_int_t ngx_http_auth_digest_init(ngx_conf_t *cf) { shm_name->len = sizeof("auth_digest"); shm_name->data = (unsigned char *)"auth_digest"; - if (ngx_http_auth_digest_shm_size == 0) { + if (ngx_http_auth_digest_shm_size == 0) + { ngx_http_auth_digest_shm_size = 4 * 256 * ngx_pagesize; // default to 4mb } ngx_http_auth_digest_shm_zone = ngx_shared_memory_add(cf, shm_name, ngx_http_auth_digest_shm_size, &ngx_http_auth_digest_module); - if (ngx_http_auth_digest_shm_zone == NULL) { + if (ngx_http_auth_digest_shm_zone == NULL) + { return NGX_ERROR; } ngx_http_auth_digest_shm_zone->init = ngx_http_auth_digest_init_shm_zone; @@ -91,8 +100,10 @@ static ngx_int_t ngx_http_auth_digest_init(ngx_conf_t *cf) { return NGX_OK; } -static ngx_int_t ngx_http_auth_digest_worker_init(ngx_cycle_t *cycle) { - if (ngx_process != NGX_PROCESS_WORKER) { +static ngx_int_t ngx_http_auth_digest_worker_init(ngx_cycle_t *cycle) +{ + if (ngx_process != NGX_PROCESS_WORKER) + { return NGX_OK; } @@ -102,16 +113,18 @@ static ngx_int_t ngx_http_auth_digest_worker_init(ngx_cycle_t *cycle) { ngx_array_create(cycle->pool, NGX_HTTP_AUTH_DIGEST_CLEANUP_BATCH_SIZE, sizeof(ngx_rbtree_node_t *)); - if (ngx_http_auth_digest_cleanup_list == NULL) { + if (ngx_http_auth_digest_cleanup_list == NULL) + { ngx_log_error(NGX_LOG_EMERG, cycle->log, 0, "Could not allocate shared memory for auth_digest"); return NGX_ERROR; } - ngx_connection_t *dummy; - dummy = ngx_pcalloc(cycle->pool, sizeof(ngx_connection_t)); + ngx_connection_t *dummy = ngx_pcalloc(cycle->pool, sizeof(ngx_connection_t)); if (dummy == NULL) + { return NGX_ERROR; + } dummy->fd = (ngx_socket_t)-1; dummy->data = cycle; @@ -123,13 +136,14 @@ static ngx_int_t ngx_http_auth_digest_worker_init(ngx_cycle_t *cycle) { return NGX_OK; } -static ngx_int_t ngx_http_auth_digest_handler(ngx_http_request_t *r) { +static ngx_int_t ngx_http_auth_digest_handler(ngx_http_request_t *r) +{ off_t offset; ssize_t n; ngx_fd_t fd; ngx_int_t rc; ngx_err_t err; - ngx_str_t user_file, passwd_line, realm; + ngx_str_t user_file, passwd_line, realm, localhost_nocheck; ngx_file_t file; ngx_uint_t i, begin, tail, idle; ngx_http_auth_digest_loc_conf_t *alcf; @@ -138,56 +152,74 @@ static ngx_int_t ngx_http_auth_digest_handler(ngx_http_request_t *r) { u_char line[NGX_HTTP_AUTH_DIGEST_BUF_SIZE]; u_char *p; - if (r->internal) { + if (r->internal) + { return NGX_DECLINED; } // if digest auth is disabled for this location, bail out immediately alcf = ngx_http_get_module_loc_conf(r, ngx_http_auth_digest_module); - if (alcf->realm.value.len == 0) { + if (alcf->realm.value.len == 0) + { return NGX_DECLINED; } - if (ngx_http_complex_value(r, &alcf->realm, &realm) != NGX_OK) { + if (ngx_http_complex_value(r, &alcf->realm, &realm) != NGX_OK) + { return NGX_ERROR; } - if (realm.len == 0 || alcf->user_file.value.len == 0) { + if (realm.len == 0 || alcf->user_file.value.len == 0) + { return NGX_DECLINED; } - if (ngx_strcmp(realm.data, "off") == 0) { + if (ngx_strcmp(realm.data, "off") == 0) + { return NGX_DECLINED; } - if (ngx_http_auth_digest_evading(r, alcf)) { + if (ngx_strcmp(localhost_nocheck.data, "true") && ngx_http_auth_os_is_loopback(&(r->headers_in.server)) == 0)) + { + return NGX_DECLINED; + } + + if (ngx_http_auth_digest_evading(r, alcf)) + { return NGX_HTTP_UNAUTHORIZED; } // unpack the Authorization header (if any) and verify that it contains all // required fields. otherwise send a challenge auth_fields = ngx_pcalloc(r->pool, sizeof(ngx_http_auth_digest_cred_t)); rc = ngx_http_auth_digest_check_credentials(r, auth_fields); - if (rc == NGX_DECLINED) { + if (rc == NGX_DECLINED) + { return ngx_http_auth_digest_send_challenge(r, &realm, 0); - } else if (rc == NGX_ERROR) { + } + else if (rc == NGX_ERROR) + { return NGX_HTTP_INTERNAL_SERVER_ERROR; } // check for the existence of a passwd file and attempt to open it - if (ngx_http_complex_value(r, &alcf->user_file, &user_file) != NGX_OK) { + if (ngx_http_complex_value(r, &alcf->user_file, &user_file) != NGX_OK) + { return NGX_ERROR; } fd = ngx_open_file(user_file.data, NGX_FILE_RDONLY, NGX_FILE_OPEN, 0); - if (fd == NGX_INVALID_FILE) { + if (fd == NGX_INVALID_FILE) + { ngx_uint_t level; err = ngx_errno; - if (err == NGX_ENOENT) { + if (err == NGX_ENOENT) + { level = NGX_LOG_ERR; rc = NGX_HTTP_FORBIDDEN; - - } else { + } + else + { level = NGX_LOG_CRIT; rc = NGX_HTTP_INTERNAL_SERVER_ERROR; } @@ -209,30 +241,37 @@ static ngx_int_t ngx_http_auth_digest_handler(ngx_http_request_t *r) { idle = 1; ngx_memzero(buf, NGX_HTTP_AUTH_DIGEST_BUF_SIZE); ngx_memzero(passwd_line.data, NGX_HTTP_AUTH_DIGEST_BUF_SIZE); - while (1) { + while (1) + { n = ngx_read_file(&file, buf + tail, NGX_HTTP_AUTH_DIGEST_BUF_SIZE - tail, offset); - if (n == NGX_ERROR) { + if (n == NGX_ERROR) + { ngx_http_auth_digest_close(&file); return NGX_HTTP_INTERNAL_SERVER_ERROR; } begin = 0; - for (i = 0; i < n + tail; i++) { - if (buf[i] == '\n' || buf[i] == '\r') { + for (i = 0; i < n + tail; i++) + { + if (buf[i] == '\n' || buf[i] == '\r') + { if (!idle && i - begin > - 36) { // 36 is the min length with a single-char name and realm + 36) + { // 36 is the min length with a single-char name and realm p = ngx_cpymem(passwd_line.data, &buf[begin], i - begin); p[0] = '\0'; passwd_line.len = i - begin; rc = ngx_http_auth_digest_verify_user(r, auth_fields, &passwd_line); - if (rc == NGX_HTTP_AUTH_DIGEST_USERNOTFOUND) { + if (rc == NGX_HTTP_AUTH_DIGEST_USERNOTFOUND) + { rc = NGX_DECLINED; } - if (rc != NGX_DECLINED) { + if (rc != NGX_DECLINED) + { ngx_http_auth_digest_close(&file); ngx_http_auth_digest_evasion_tracking( r, alcf, NGX_HTTP_AUTH_DIGEST_STATUS_SUCCESS); @@ -241,34 +280,43 @@ static ngx_int_t ngx_http_auth_digest_handler(ngx_http_request_t *r) { } idle = 1; begin = i; - } else if (idle) { + } + else if (idle) + { idle = 0; begin = i; } } - if (!idle) { + if (!idle) + { tail = n + tail - begin; - if (n == 0 && tail > 36) { + if (n == 0 && tail > 36) + { p = ngx_cpymem(passwd_line.data, &buf[begin], tail); p[0] = '\0'; passwd_line.len = i - begin; rc = ngx_http_auth_digest_verify_user(r, auth_fields, &passwd_line); - if (rc == NGX_HTTP_AUTH_DIGEST_USERNOTFOUND) { + if (rc == NGX_HTTP_AUTH_DIGEST_USERNOTFOUND) + { rc = NGX_DECLINED; } - if (rc != NGX_DECLINED) { + if (rc != NGX_DECLINED) + { ngx_http_auth_digest_close(&file); ngx_http_auth_digest_evasion_tracking( r, alcf, NGX_HTTP_AUTH_DIGEST_STATUS_SUCCESS); return rc; } - } else { + } + else + { ngx_memmove(buf, &buf[begin], tail); } } - if (n == 0) { + if (n == 0) + { break; } @@ -280,7 +328,8 @@ static ngx_int_t ngx_http_auth_digest_handler(ngx_http_request_t *r) { // log only wrong username/password, // not expired hash int nc = ngx_hextoi(auth_fields->nc.data, auth_fields->nc.len); - if (nc == 1) { + if (nc == 1) + { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "invalid username or password for %*s", auth_fields->username.len, auth_fields->username.data); @@ -295,10 +344,11 @@ static ngx_int_t ngx_http_auth_digest_handler(ngx_http_request_t *r) { } ngx_int_t -ngx_http_auth_digest_check_credentials(ngx_http_request_t *r, - ngx_http_auth_digest_cred_t *ctx) { +ngx_http_auth_digest_check_credentials(ngx_http_request_t *r, ngx_http_auth_digest_cred_t *ctx) +{ - if (r->headers_in.authorization == NULL) { + if (r->headers_in.authorization == NULL) + { return NGX_DECLINED; } @@ -338,7 +388,8 @@ ngx_http_auth_digest_check_credentials(ngx_http_request_t *r, value.data = 0; value.len = 0; - enum { + enum + { sw_start = 0, sw_scheme, sw_scheme_end, @@ -361,47 +412,62 @@ ngx_http_auth_digest_check_credentials(ngx_http_request_t *r, ch = *p++; - while (p <= last) { - switch (state) { + while (p <= last) + { + switch (state) + { default: case sw_error: return NGX_DECLINED; /* first char */ case sw_start: - if (ch == CR || ch == LF || ch == ' ' || ch == '\t') { + if (ch == CR || ch == LF || ch == ' ' || ch == '\t') + { ch = *p++; - } else if (token_char[ch >> 5] & (1 << (ch & 0x1f))) { + } + else if (token_char[ch >> 5] & (1 << (ch & 0x1f))) + { start = p - 1; state = sw_scheme; - } else { + } + else + { state = sw_error; } break; case sw_scheme: - if (token_char[ch >> 5] & (1 << (ch & 0x1f))) { + if (token_char[ch >> 5] & (1 << (ch & 0x1f))) + { ch = *p++; - } else if (ch == ' ') { + } + else if (ch == ' ') + { end = p - 1; state = sw_scheme_end; ctx->auth_scheme.data = start; ctx->auth_scheme.len = end - start; - if (ngx_strncasecmp(ctx->auth_scheme.data, (u_char *)"Digest", - ctx->auth_scheme.len) != 0) { + if (ngx_strncasecmp(ctx->auth_scheme.data, (u_char *)"Digest", ctx->auth_scheme.len) != 0) + { state = sw_error; } - } else { + } + else + { state = sw_error; } break; case sw_scheme_end: - if (ch == ' ') { + if (ch == ' ') + { ch = *p++; - } else { + } + else + { state = sw_param_name_start; } break; @@ -412,32 +478,45 @@ ngx_http_auth_digest_check_credentials(ngx_http_request_t *r, /* fall through */ case sw_lws: - if (comma_count > 0 && (token_char[ch >> 5] & (1 << (ch & 0x1f)))) { + if (comma_count > 0 && (token_char[ch >> 5] & (1 << (ch & 0x1f)))) + { state = sw_param_name_start; - } else if (ch == ',') { + } + else if (ch == ',') + { comma_count++; ch = *p++; - } else if (ch == CR || ch == LF || ch == ' ' || ch == '\t') { + } + else if (ch == CR || ch == LF || ch == ' ' || ch == '\t') + { ch = *p++; - } else { + } + else + { state = sw_error; } break; case sw_param_name_start: - if (token_char[ch >> 5] & (1 << (ch & 0x1f))) { + if (token_char[ch >> 5] & (1 << (ch & 0x1f))) + { start = p - 1; state = sw_param_name; ch = *p++; - } else { + } + else + { state = sw_error; } break; case sw_param_name: - if (token_char[ch >> 5] & (1 << (ch & 0x1f))) { + if (token_char[ch >> 5] & (1 << (ch & 0x1f))) + { ch = *p++; - } else if (ch == '=') { + } + else if (ch == '=') + { end = p - 1; state = sw_param_value_start; @@ -445,33 +524,42 @@ ngx_http_auth_digest_check_credentials(ngx_http_request_t *r, name.len = end - start; ch = *p++; - } else { + } + else + { state = sw_error; } break; case sw_param_value_start: - if (token_char[ch >> 5] & (1 << (ch & 0x1f))) { + if (token_char[ch >> 5] & (1 << (ch & 0x1f))) + { start = p - 1; state = sw_param_value; ch = *p++; - } else if (ch == '\"') { + } + else if (ch == '\"') + { start = p; quoted_pair_count = 0; state = sw_param_quoted_value; ch = *p++; - } else { + } + else + { state = sw_error; } break; case sw_param_value: in_value = token_char[ch >> 5] & (1 << (ch & 0x1f)); - if (in_value) { + if (in_value) + { ch = *p++; } - if (!in_value || p > last) { + if (!in_value || p > last) + { end = p - 1; value.data = start; value.len = end - start; @@ -481,29 +569,38 @@ ngx_http_auth_digest_check_credentials(ngx_http_request_t *r, break; case sw_param_quoted_value: - if (ch < 0x20 || ch == 0x7f) { + if (ch < 0x20 || ch == 0x7f) + { state = sw_error; - } else if (ch == '\\' && *p <= 0x7f) { + } + else if (ch == '\\' && *p <= 0x7f) + { quoted_pair_count++; /* Skip the next char, even if it's a \ */ ch = *(p += 2); - } else if (ch == '\"') { + } + else if (ch == '\"') + { end = p - 1; ch = *p++; value.data = start; value.len = end - start - quoted_pair_count; - if (quoted_pair_count > 0) { + if (quoted_pair_count > 0) + { value.data = ngx_palloc(r->pool, value.len); u_char *d = value.data; u_char *s = start; - for (; s < end; s++) { + for (; s < end; s++) + { ch = *s; - if (ch == '\\') { + if (ch == '\\') + { /* Make sure to add the next character * even if it's a \ */ s++; - if (s < end) { + if (s < end) + { *d++ = ch; } continue; @@ -513,33 +610,52 @@ ngx_http_auth_digest_check_credentials(ngx_http_request_t *r, } state = sw_param_end; goto param_end; - } else { + } + else + { ch = *p++; } break; param_end: case sw_param_end: - if (ngx_strncasecmp(name.data, (u_char *)"username", name.len) == 0) { + if (ngx_strncasecmp(name.data, (u_char *)"username", name.len) == 0) + { ctx->username = value; - } else if (ngx_strncasecmp(name.data, (u_char *)"qop", name.len) == 0) { + } + else if (ngx_strncasecmp(name.data, (u_char *)"qop", name.len) == 0) + { ctx->qop = value; - } else if (ngx_strncasecmp(name.data, (u_char *)"realm", name.len) == 0) { + } + else if (ngx_strncasecmp(name.data, (u_char *)"realm", name.len) == 0) + { ctx->realm = value; - } else if (ngx_strncasecmp(name.data, (u_char *)"nonce", name.len) == 0) { + } + else if (ngx_strncasecmp(name.data, (u_char *)"nonce", name.len) == 0) + { ctx->nonce = value; - } else if (ngx_strncasecmp(name.data, (u_char *)"nc", name.len) == 0) { + } + else if (ngx_strncasecmp(name.data, (u_char *)"nc", name.len) == 0) + { ctx->nc = value; - } else if (ngx_strncasecmp(name.data, (u_char *)"uri", name.len) == 0) { + } + else if (ngx_strncasecmp(name.data, (u_char *)"uri", name.len) == 0) + { ctx->uri = value; - } else if (ngx_strncasecmp(name.data, (u_char *)"cnonce", name.len) == - 0) { + } + else if (ngx_strncasecmp(name.data, (u_char *)"cnonce", name.len) == + 0) + { ctx->cnonce = value; - } else if (ngx_strncasecmp(name.data, (u_char *)"response", name.len) == - 0) { + } + else if (ngx_strncasecmp(name.data, (u_char *)"response", name.len) == + 0) + { ctx->response = value; - } else if (ngx_strncasecmp(name.data, (u_char *)"opaque", name.len) == - 0) { + } + else if (ngx_strncasecmp(name.data, (u_char *)"opaque", name.len) == + 0) + { ctx->opaque = value; } @@ -548,7 +664,8 @@ ngx_http_auth_digest_check_credentials(ngx_http_request_t *r, } } - if (state != sw_lws_start && state != sw_lws) { + if (state != sw_lws_start && state != sw_lws) + { return NGX_DECLINED; } @@ -557,7 +674,8 @@ ngx_http_auth_digest_check_credentials(ngx_http_request_t *r, if (!(ctx->username.len > 0 && ctx->qop.len > 0 && ctx->realm.len > 0 && ctx->nonce.len > 0 && ctx->nc.len > 0 && ctx->uri.len > 0 && ctx->cnonce.len > 0 && ctx->response.len > 0) || - ctx->nonce.len != 16) { + ctx->nonce.len != 16) + { return NGX_DECLINED; } @@ -567,9 +685,15 @@ ngx_http_auth_digest_check_credentials(ngx_http_request_t *r, static ngx_int_t ngx_http_auth_digest_verify_user(ngx_http_request_t *r, ngx_http_auth_digest_cred_t *fields, - ngx_str_t *line) { + ngx_str_t *line) +{ ngx_uint_t i, from, nomatch; - enum { sw_login, sw_ha1, sw_realm } state; + enum + { + sw_login, + sw_ha1, + sw_realm + } state; state = sw_login; from = 0; @@ -579,19 +703,24 @@ ngx_http_auth_digest_verify_user(ngx_http_request_t *r, // realm // character-by-character against the request's Authorization header fields u_char *buf = line->data; - for (i = 0; i <= line->len; i++) { + for (i = 0; i <= line->len; i++) + { u_char ch = buf[i]; - switch (state) { + switch (state) + { case sw_login: if (ch == '#') nomatch = 1; - if (ch == ':') { + if (ch == ':') + { if (fields->username.len != i) nomatch = 1; state = sw_realm; from = i + 1; - } else if (i > fields->username.len || ch != fields->username.data[i]) { + } + else if (i > fields->username.len || ch != fields->username.data[i]) + { nomatch = 1; } break; @@ -599,18 +728,22 @@ ngx_http_auth_digest_verify_user(ngx_http_request_t *r, case sw_realm: if (ch == '#') nomatch = 1; - if (ch == ':') { + if (ch == ':') + { if (fields->realm.len != i - from) nomatch = 1; state = sw_ha1; from = i + 1; - } else if (ch != fields->realm.data[i - from]) { + } + else if (ch != fields->realm.data[i - from]) + { nomatch = 1; } break; case sw_ha1: - if (ch == '\0' || ch == ':' || ch == '#' || ch == CR || ch == LF) { + if (ch == '\0' || ch == ':' || ch == '#' || ch == CR || ch == LF) + { if (i - from != 32) nomatch = 1; } @@ -618,7 +751,8 @@ ngx_http_auth_digest_verify_user(ngx_http_request_t *r, } } - if (nomatch) { + if (nomatch) + { return NGX_HTTP_AUTH_DIGEST_USERNOTFOUND; } @@ -628,7 +762,8 @@ ngx_http_auth_digest_verify_user(ngx_http_request_t *r, static ngx_int_t ngx_http_auth_digest_verify_hash(ngx_http_request_t *r, ngx_http_auth_digest_cred_t *fields, - u_char *hashed_pw) { + u_char *hashed_pw) +{ u_char *p; ngx_str_t http_method; ngx_str_t HA1, HA2, ha2_key; @@ -648,15 +783,15 @@ ngx_http_auth_digest_verify_hash(ngx_http_request_t *r, // - Otherwise the check is not executed and authorization is declined if (!((r->unparsed_uri.len == fields->uri.len) && (ngx_strncmp(r->unparsed_uri.data, fields->uri.data, fields->uri.len) == 0))) - { + { if (!((r->unparsed_uri.len > fields->uri.len) && (ngx_strncmp(r->unparsed_uri.data, fields->uri.data, fields->uri.len) == 0) && (r->unparsed_uri.data[fields->uri.len] == '?'))) { - return NGX_DECLINED; + return NGX_DECLINED; } } - + // the hashing scheme: // digest: // MD5(MD5(username:realm:password):nonce:nc:cnonce:qop:MD5(method:uri)) @@ -695,9 +830,13 @@ ngx_http_auth_digest_verify_hash(ngx_http_request_t *r, // calculate digest: md5(ha1:nonce:nc:cnonce:qop:ha2) digest_key.len = HA1.len - 1 + fields->nonce.len + fields->nc.len + fields->cnonce.len + fields->qop.len + HA2.len - 1 + 5 + 1; + digest_key.data = ngx_pcalloc(r->pool, digest_key.len); + if (digest_key.data == NULL) + { return NGX_HTTP_INTERNAL_SERVER_ERROR; + } p = ngx_cpymem(digest_key.data, HA1.data, HA1.len - 1); *p++ = ':'; @@ -713,8 +852,12 @@ ngx_http_auth_digest_verify_hash(ngx_http_request_t *r, digest.len = 33; digest.data = ngx_pcalloc(r->pool, 33); + if (digest.data == NULL) + { return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + ngx_md5_init(&md5); ngx_md5_update(&md5, digest_key.data, digest_key.len - 1); ngx_md5_final(hash, &md5); @@ -725,7 +868,9 @@ ngx_http_auth_digest_verify_hash(ngx_http_request_t *r, // and bail out if they don't match if (fields->response.len != digest.len - 1 || ngx_memcmp(digest.data, fields->response.data, fields->response.len) != 0) + { return NGX_DECLINED; + } ngx_http_auth_digest_nonce_t nonce; ngx_uint_t key; @@ -743,7 +888,8 @@ ngx_http_auth_digest_verify_hash(ngx_http_request_t *r, ngx_crc32_short((u_char *)&nonce.t, sizeof(nonce.t)); int nc = ngx_hextoi(fields->nc.data, fields->nc.len); - if (nc < 0 || nc >= alcf->replays) { + if (nc < 0 || nc >= alcf->replays) + { fields->stale = 1; return NGX_DECLINED; } @@ -753,15 +899,19 @@ ngx_http_auth_digest_verify_hash(ngx_http_request_t *r, found = (ngx_http_auth_digest_node_t *)ngx_http_auth_digest_rbtree_find( key, ngx_http_auth_digest_rbtree->root, ngx_http_auth_digest_rbtree->sentinel); - if (found != NULL) { - if (found->expires <= ngx_time()) { + if (found != NULL) + { + if (found->expires <= ngx_time()) + { fields->stale = 1; goto invalid; } - if (!ngx_bitvector_test(found->nc, nc)) { + if (!ngx_bitvector_test(found->nc, nc)) + { goto invalid; } - if (ngx_bitvector_test(found->nc, 0)) { + if (ngx_bitvector_test(found->nc, 0)) + { // if this is the first use of this nonce, switch the expiration time from // the timeout // param to now+expires. using the 0th element of the nc vector to flag @@ -831,7 +981,9 @@ ngx_http_auth_digest_verify_hash(ngx_http_request_t *r, info_header->value = hval; info_header->hash = 1; return NGX_OK; - } else { + } + else + { invalid: // nonce is invalid/expired or client reused an nc value. suspicious... ngx_shmtx_unlock(&shpool->mutex); @@ -841,13 +993,15 @@ ngx_http_auth_digest_verify_hash(ngx_http_request_t *r, static ngx_int_t ngx_http_auth_digest_send_challenge(ngx_http_request_t *r, ngx_str_t *realm, - ngx_uint_t is_stale) { + ngx_uint_t is_stale) +{ ngx_str_t challenge; u_char *p; size_t realm_len = strnlen((const char *)realm->data, realm->len); r->headers_out.www_authenticate = ngx_list_push(&r->headers_out.headers); - if (r->headers_out.www_authenticate == NULL) { + if (r->headers_out.www_authenticate == NULL) + { return NGX_HTTP_INTERNAL_SERVER_ERROR; } @@ -857,17 +1011,20 @@ static ngx_int_t ngx_http_auth_digest_send_challenge(ngx_http_request_t *r, challenge.len = sizeof("Digest algorithm=\"MD5\", qop=\"auth\", realm=\"\", nonce=\"\"") - 1 + realm_len + 16; - if (is_stale) { + if (is_stale) + { challenge.len += sizeof(", stale=\"true\"") - 1; } challenge.data = ngx_pnalloc(r->pool, challenge.len); - if (challenge.data == NULL) { + if (challenge.data == NULL) + { return NGX_HTTP_INTERNAL_SERVER_ERROR; } ngx_http_auth_digest_nonce_t nonce; nonce = ngx_http_auth_digest_next_nonce(r); - if (nonce.t == 0 && nonce.rnd == 0) { + if (nonce.t == 0 && nonce.rnd == 0) + { // oom error when allocating nonce session in rbtree return NGX_HTTP_SERVICE_UNAVAILABLE; } @@ -879,9 +1036,12 @@ static ngx_int_t ngx_http_auth_digest_send_challenge(ngx_http_request_t *r, p = ngx_cpymem(p, "\", nonce=\"", sizeof("\", nonce=\"") - 1); p = ngx_sprintf(p, "%08xl%08xl", nonce.rnd, nonce.t); - if (is_stale) { + if (is_stale) + { p = ngx_cpymem(p, "\", stale=\"true\"", sizeof("\", stale=\"true\"")); - } else { + } + else + { p = ngx_cpymem(p, "\"", sizeof("\"")); } r->headers_out.www_authenticate->value = challenge; @@ -889,8 +1049,10 @@ static ngx_int_t ngx_http_auth_digest_send_challenge(ngx_http_request_t *r, return NGX_HTTP_UNAUTHORIZED; } -static void ngx_http_auth_digest_close(ngx_file_t *file) { - if (ngx_close_file(file->fd) == NGX_FILE_ERROR) { +static void ngx_http_auth_digest_close(ngx_file_t *file) +{ + if (ngx_close_file(file->fd) == NGX_FILE_ERROR) + { ngx_log_error(NGX_LOG_ALERT, file->log, ngx_errno, ngx_close_file_n " \"%s\" failed", file->name.data); } @@ -898,13 +1060,15 @@ static void ngx_http_auth_digest_close(ngx_file_t *file) { static char *ngx_http_auth_digest_set_user_file(ngx_conf_t *cf, ngx_command_t *cmd, - void *conf) { + void *conf) +{ ngx_http_auth_digest_loc_conf_t *alcf = conf; ngx_str_t *value; ngx_http_compile_complex_value_t ccv; - if (alcf->user_file.value.len) { + if (alcf->user_file.value.len) + { return "is duplicate"; } @@ -918,7 +1082,8 @@ static char *ngx_http_auth_digest_set_user_file(ngx_conf_t *cf, ccv.zero = 1; ccv.conf_prefix = 1; - if (ngx_http_compile_complex_value(&ccv) != NGX_OK) { + if (ngx_http_compile_complex_value(&ccv) != NGX_OK) + { return NGX_CONF_ERROR; } @@ -926,13 +1091,15 @@ static char *ngx_http_auth_digest_set_user_file(ngx_conf_t *cf, } static char *ngx_http_auth_digest_set_realm(ngx_conf_t *cf, ngx_command_t *cmd, - void *conf) { + void *conf) +{ ngx_http_auth_digest_loc_conf_t *alcf = conf; ngx_str_t *value; ngx_http_compile_complex_value_t ccv; - if (alcf->realm.value.len) { + if (alcf->realm.value.len) + { return "is duplicate"; } @@ -947,7 +1114,8 @@ static char *ngx_http_auth_digest_set_realm(ngx_conf_t *cf, ngx_command_t *cmd, ccv.conf_prefix = 0; ccv.root_prefix = 0; - if (ngx_http_compile_complex_value(&ccv) != NGX_OK) { + if (ngx_http_compile_complex_value(&ccv) != NGX_OK) + { return NGX_CONF_ERROR; } @@ -955,14 +1123,16 @@ static char *ngx_http_auth_digest_set_realm(ngx_conf_t *cf, ngx_command_t *cmd, } static char *ngx_http_auth_digest_set_shm_size(ngx_conf_t *cf, - ngx_command_t *cmd, void *conf) { + ngx_command_t *cmd, void *conf) +{ ssize_t new_shm_size; ngx_str_t *value; value = cf->args->elts; new_shm_size = ngx_parse_size(&value[1]); - if (new_shm_size == NGX_ERROR) { + if (new_shm_size == NGX_ERROR) + { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "Invalid memory area size `%V'", &value[1]); return NGX_CONF_ERROR; @@ -970,7 +1140,8 @@ static char *ngx_http_auth_digest_set_shm_size(ngx_conf_t *cf, new_shm_size = ngx_align(new_shm_size, ngx_pagesize); - if (new_shm_size < 8 * (ssize_t)ngx_pagesize) { + if (new_shm_size < 8 * (ssize_t)ngx_pagesize) + { ngx_conf_log_error(NGX_LOG_WARN, cf, 0, "The auth_digest_shm_size value must be at least %udKiB", (8 * ngx_pagesize) >> 10); @@ -978,38 +1149,40 @@ static char *ngx_http_auth_digest_set_shm_size(ngx_conf_t *cf, } if (ngx_http_auth_digest_shm_size && - ngx_http_auth_digest_shm_size != (ngx_uint_t)new_shm_size) { - ngx_conf_log_error( - NGX_LOG_WARN, cf, 0, - "Cannot change memory area size without restart, ignoring change"); - } else { + ngx_http_auth_digest_shm_size != (ngx_uint_t)new_shm_size) + { + ngx_conf_log_error(NGX_LOG_WARN, cf, 0, "Cannot change memory area size without restart, ignoring change"); + } + else + { ngx_http_auth_digest_shm_size = new_shm_size; } - ngx_conf_log_error(NGX_LOG_DEBUG, cf, 0, - "Using %udKiB of shared memory for auth_digest", - new_shm_size >> 10); + ngx_conf_log_error(NGX_LOG_DEBUG, cf, 0, "Using %udKiB of shared memory for auth_digest", new_shm_size >> 10); return NGX_CONF_OK; } -static ngx_int_t ngx_http_auth_digest_init_shm_zone(ngx_shm_zone_t *shm_zone, - void *data) { +static ngx_int_t ngx_http_auth_digest_init_shm_zone(ngx_shm_zone_t *shm_zone, void *data) +{ ngx_slab_pool_t *shpool; ngx_rbtree_t *tree; ngx_rbtree_node_t *sentinel; ngx_atomic_t *lock; - if (data) { + if (data) + { shm_zone->data = data; return NGX_OK; } shpool = (ngx_slab_pool_t *)shm_zone->shm.addr; tree = ngx_slab_alloc(shpool, sizeof *tree); - if (tree == NULL) { + if (tree == NULL) + { return NGX_ERROR; } sentinel = ngx_slab_alloc(shpool, sizeof *sentinel); - if (sentinel == NULL) { + if (sentinel == NULL) + { return NGX_ERROR; } @@ -1018,12 +1191,14 @@ static ngx_int_t ngx_http_auth_digest_init_shm_zone(ngx_shm_zone_t *shm_zone, ngx_http_auth_digest_rbtree = tree; tree = ngx_slab_alloc(shpool, sizeof *tree); - if (tree == NULL) { + if (tree == NULL) + { return NGX_ERROR; } sentinel = ngx_slab_alloc(shpool, sizeof *sentinel); - if (sentinel == NULL) { + if (sentinel == NULL) + { return NGX_ERROR; } @@ -1031,7 +1206,8 @@ static ngx_int_t ngx_http_auth_digest_init_shm_zone(ngx_shm_zone_t *shm_zone, ngx_http_auth_digest_ev_rbtree = tree; lock = ngx_slab_alloc(shpool, sizeof(ngx_atomic_t)); - if (lock == NULL) { + if (lock == NULL) + { return NGX_ERROR; } ngx_http_auth_digest_cleanup_lock = lock; @@ -1040,7 +1216,8 @@ static ngx_int_t ngx_http_auth_digest_init_shm_zone(ngx_shm_zone_t *shm_zone, } static int ngx_http_auth_digest_rbtree_cmp(const ngx_rbtree_node_t *v_left, - const ngx_rbtree_node_t *v_right) { + const ngx_rbtree_node_t *v_right) +{ if (v_left->key == v_right->key) return 0; else @@ -1049,8 +1226,10 @@ static int ngx_http_auth_digest_rbtree_cmp(const ngx_rbtree_node_t *v_left, static int ngx_http_auth_digest_ev_rbtree_cmp(const ngx_rbtree_node_t *v_left, - const ngx_rbtree_node_t *v_right) { - if (v_left->key == v_right->key) { + const ngx_rbtree_node_t *v_right) +{ + if (v_left->key == v_right->key) + { ngx_http_auth_digest_ev_node_t *evleft = (ngx_http_auth_digest_ev_node_t *)v_left; ngx_http_auth_digest_ev_node_t *evright = @@ -1062,43 +1241,53 @@ ngx_http_auth_digest_ev_rbtree_cmp(const ngx_rbtree_node_t *v_left, return (v_left->key < v_right->key) ? -1 : 1; } -static void -ngx_rbtree_generic_insert(ngx_rbtree_node_t *temp, ngx_rbtree_node_t *node, - ngx_rbtree_node_t *sentinel, - int (*compare)(const ngx_rbtree_node_t *left, - const ngx_rbtree_node_t *right)) { - for (;;) { - if (node->key < temp->key) { +static void ngx_rbtree_generic_insert(ngx_rbtree_node_t *temp, + ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel, + int (*compare)(const ngx_rbtree_node_t *left, + const ngx_rbtree_node_t *right)) +{ + for (;;) + { + if (node->key < temp->key) + { - if (temp->left == sentinel) { + if (temp->left == sentinel) + { temp->left = node; break; } temp = temp->left; + } + else if (node->key > temp->key) + { - } else if (node->key > temp->key) { - - if (temp->right == sentinel) { + if (temp->right == sentinel) + { temp->right = node; break; } temp = temp->right; + } + else + { /* node->key == temp->key */ + if (compare(node, temp) < 0) + { - } else { /* node->key == temp->key */ - if (compare(node, temp) < 0) { - - if (temp->left == sentinel) { + if (temp->left == sentinel) + { temp->left = node; break; } temp = temp->left; + } + else + { - } else { - - if (temp->right == sentinel) { + if (temp->right == sentinel) + { temp->right = node; break; } @@ -1116,7 +1305,8 @@ ngx_rbtree_generic_insert(ngx_rbtree_node_t *temp, ngx_rbtree_node_t *node, static void ngx_http_auth_digest_rbtree_insert(ngx_rbtree_node_t *temp, ngx_rbtree_node_t *node, - ngx_rbtree_node_t *sentinel) { + ngx_rbtree_node_t *sentinel) +{ ngx_rbtree_generic_insert(temp, node, sentinel, ngx_http_auth_digest_rbtree_cmp); @@ -1124,7 +1314,8 @@ static void ngx_http_auth_digest_rbtree_insert(ngx_rbtree_node_t *temp, static void ngx_http_auth_digest_ev_rbtree_insert(ngx_rbtree_node_t *temp, ngx_rbtree_node_t *node, - ngx_rbtree_node_t *sentinel) { + ngx_rbtree_node_t *sentinel) +{ ngx_rbtree_generic_insert(temp, node, sentinel, ngx_http_auth_digest_ev_rbtree_cmp); @@ -1132,16 +1323,19 @@ static void ngx_http_auth_digest_ev_rbtree_insert(ngx_rbtree_node_t *temp, static ngx_rbtree_node_t * ngx_http_auth_digest_rbtree_find(ngx_rbtree_key_t key, ngx_rbtree_node_t *node, - ngx_rbtree_node_t *sentinel) { + ngx_rbtree_node_t *sentinel) +{ if (node == sentinel) return NULL; ngx_rbtree_node_t *found = (node->key == key) ? node : NULL; - if (found == NULL && node->left != sentinel) { + if (found == NULL && node->left != sentinel) + { found = ngx_http_auth_digest_rbtree_find(key, node->left, sentinel); } - if (found == NULL && node->right != sentinel) { + if (found == NULL && node->right != sentinel) + { found = ngx_http_auth_digest_rbtree_find(key, node->right, sentinel); } @@ -1151,35 +1345,41 @@ ngx_http_auth_digest_rbtree_find(ngx_rbtree_key_t key, ngx_rbtree_node_t *node, static ngx_http_auth_digest_ev_node_t * ngx_http_auth_digest_ev_rbtree_find(ngx_http_auth_digest_ev_node_t *this, ngx_rbtree_node_t *node, - ngx_rbtree_node_t *sentinel) { + ngx_rbtree_node_t *sentinel) +{ int cmpval; if (node == sentinel) return NULL; cmpval = ngx_http_auth_digest_ev_rbtree_cmp((ngx_rbtree_node_t *)this, node); - if (cmpval == 0) { + if (cmpval == 0) + { return (ngx_http_auth_digest_ev_node_t *)node; } return ngx_http_auth_digest_ev_rbtree_find( this, (cmpval < 0) ? node->left : node->right, sentinel); } -void ngx_http_auth_digest_cleanup(ngx_event_t *ev) { +void ngx_http_auth_digest_cleanup(ngx_event_t *ev) +{ if (ev->timer_set) ngx_del_timer(ev); - if (!(ngx_quit || ngx_terminate || ngx_exiting)) { + if (!(ngx_quit || ngx_terminate || ngx_exiting)) + { ngx_add_timer(ev, NGX_HTTP_AUTH_DIGEST_CLEANUP_INTERVAL); } - if (ngx_trylock(ngx_http_auth_digest_cleanup_lock)) { + if (ngx_trylock(ngx_http_auth_digest_cleanup_lock)) + { ngx_http_auth_digest_rbtree_prune(ev->log); ngx_http_auth_digest_ev_rbtree_prune(ev->log); ngx_unlock(ngx_http_auth_digest_cleanup_lock); } } -static void ngx_http_auth_digest_rbtree_prune(ngx_log_t *log) { +static void ngx_http_auth_digest_rbtree_prune(ngx_log_t *log) +{ ngx_uint_t i; time_t now = ngx_time(); ngx_slab_pool_t *shpool = @@ -1193,7 +1393,8 @@ static void ngx_http_auth_digest_rbtree_prune(ngx_log_t *log) { ngx_rbtree_node_t **elts = (ngx_rbtree_node_t **)ngx_http_auth_digest_cleanup_list->elts; - for (i = 0; i < ngx_http_auth_digest_cleanup_list->nelts; i++) { + for (i = 0; i < ngx_http_auth_digest_cleanup_list->nelts; i++) + { ngx_rbtree_delete(ngx_http_auth_digest_rbtree, elts[i]); ngx_slab_free_locked(shpool, elts[i]); } @@ -1201,15 +1402,19 @@ static void ngx_http_auth_digest_rbtree_prune(ngx_log_t *log) { // if the cleanup array grew during the run, shrink it back down if (ngx_http_auth_digest_cleanup_list->nalloc > - NGX_HTTP_AUTH_DIGEST_CLEANUP_BATCH_SIZE) { + NGX_HTTP_AUTH_DIGEST_CLEANUP_BATCH_SIZE) + { ngx_array_t *old_list = ngx_http_auth_digest_cleanup_list; ngx_array_t *new_list = ngx_array_create( old_list->pool, NGX_HTTP_AUTH_DIGEST_CLEANUP_BATCH_SIZE, sizeof(ngx_rbtree_node_t *)); - if (new_list != NULL) { + if (new_list != NULL) + { ngx_array_destroy(old_list); ngx_http_auth_digest_cleanup_list = new_list; - } else { + } + else + { ngx_log_error(NGX_LOG_ERR, log, 0, "auth_digest ran out of cleanup space"); } @@ -1218,27 +1423,32 @@ static void ngx_http_auth_digest_rbtree_prune(ngx_log_t *log) { static void ngx_http_auth_digest_rbtree_prune_walk(ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel, - time_t now, ngx_log_t *log) { + time_t now, ngx_log_t *log) +{ if (node == sentinel) return; - if (node->left != sentinel) { + if (node->left != sentinel) + { ngx_http_auth_digest_rbtree_prune_walk(node->left, sentinel, now, log); } - if (node->right != sentinel) { + if (node->right != sentinel) + { ngx_http_auth_digest_rbtree_prune_walk(node->right, sentinel, now, log); } ngx_http_auth_digest_node_t *dnode = (ngx_http_auth_digest_node_t *)node; - if (dnode->drop_time <= ngx_time()) { + if (dnode->drop_time <= ngx_time()) + { ngx_rbtree_node_t **dropnode = ngx_array_push(ngx_http_auth_digest_cleanup_list); dropnode[0] = node; } } -static void ngx_http_auth_digest_ev_rbtree_prune(ngx_log_t *log) { +static void ngx_http_auth_digest_ev_rbtree_prune(ngx_log_t *log) +{ ngx_uint_t i; time_t now = ngx_time(); ngx_slab_pool_t *shpool = @@ -1252,7 +1462,8 @@ static void ngx_http_auth_digest_ev_rbtree_prune(ngx_log_t *log) { ngx_rbtree_node_t **elts = (ngx_rbtree_node_t **)ngx_http_auth_digest_cleanup_list->elts; - for (i = 0; i < ngx_http_auth_digest_cleanup_list->nelts; i++) { + for (i = 0; i < ngx_http_auth_digest_cleanup_list->nelts; i++) + { ngx_rbtree_delete(ngx_http_auth_digest_ev_rbtree, elts[i]); ngx_slab_free_locked(shpool, elts[i]); } @@ -1260,47 +1471,54 @@ static void ngx_http_auth_digest_ev_rbtree_prune(ngx_log_t *log) { // if the cleanup array grew during the run, shrink it back down if (ngx_http_auth_digest_cleanup_list->nalloc > - NGX_HTTP_AUTH_DIGEST_CLEANUP_BATCH_SIZE) { + NGX_HTTP_AUTH_DIGEST_CLEANUP_BATCH_SIZE) + { ngx_array_t *old_list = ngx_http_auth_digest_cleanup_list; ngx_array_t *new_list = ngx_array_create( old_list->pool, NGX_HTTP_AUTH_DIGEST_CLEANUP_BATCH_SIZE, sizeof(ngx_rbtree_node_t *)); - if (new_list != NULL) { + if (new_list != NULL) + { ngx_array_destroy(old_list); ngx_http_auth_digest_cleanup_list = new_list; - } else { + } + else + { ngx_log_error(NGX_LOG_ERR, log, 0, "auth_digest ran out of cleanup space"); } } } -static void -ngx_http_auth_digest_ev_rbtree_prune_walk(ngx_rbtree_node_t *node, - ngx_rbtree_node_t *sentinel, - time_t now, ngx_log_t *log) { +static void ngx_http_auth_digest_ev_rbtree_prune_walk(ngx_rbtree_node_t *node, + ngx_rbtree_node_t *sentinel, + time_t now, ngx_log_t *log) +{ if (node == sentinel) return; - if (node->left != sentinel) { + if (node->left != sentinel) + { ngx_http_auth_digest_ev_rbtree_prune_walk(node->left, sentinel, now, log); } - if (node->right != sentinel) { + if (node->right != sentinel) + { ngx_http_auth_digest_ev_rbtree_prune_walk(node->right, sentinel, now, log); } ngx_http_auth_digest_ev_node_t *dnode = (ngx_http_auth_digest_ev_node_t *)node; - if (dnode->drop_time <= ngx_time()) { + if (dnode->drop_time <= ngx_time()) + { ngx_rbtree_node_t **dropnode = ngx_array_push(ngx_http_auth_digest_cleanup_list); dropnode[0] = node; } } -static ngx_http_auth_digest_nonce_t -ngx_http_auth_digest_next_nonce(ngx_http_request_t *r) { +static ngx_http_auth_digest_nonce_t ngx_http_auth_digest_next_nonce(ngx_http_request_t *r) +{ ngx_http_auth_digest_loc_conf_t *alcf; ngx_slab_pool_t *shpool; ngx_http_auth_digest_nonce_t nonce; @@ -1311,7 +1529,8 @@ ngx_http_auth_digest_next_nonce(ngx_http_request_t *r) { alcf = ngx_http_get_module_loc_conf(r, ngx_http_auth_digest_module); // create a nonce value that's not in the active set - while (1) { + while (1) + { nonce.t = ngx_time(); nonce.rnd = ngx_random(); key = ngx_crc32_short((u_char *)&nonce.rnd, sizeof nonce.rnd) ^ @@ -1322,7 +1541,8 @@ ngx_http_auth_digest_next_nonce(ngx_http_request_t *r) { ngx_http_auth_digest_rbtree_find(key, ngx_http_auth_digest_rbtree->root, ngx_http_auth_digest_rbtree->sentinel); - if (found != NULL) { + if (found != NULL) + { ngx_shmtx_unlock(&shpool->mutex); continue; } @@ -1330,7 +1550,8 @@ ngx_http_auth_digest_next_nonce(ngx_http_request_t *r) { node = ngx_slab_alloc_locked(shpool, sizeof(ngx_http_auth_digest_node_t) + ngx_bitvector_size(1 + alcf->replays)); - if (node == NULL) { + if (node == NULL) + { ngx_shmtx_unlock(&shpool->mutex); ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "auth_digest ran out of shm space. Increase the " @@ -1351,13 +1572,15 @@ ngx_http_auth_digest_next_nonce(ngx_http_request_t *r) { } static int ngx_http_auth_digest_srcaddr_key(struct sockaddr *sa, socklen_t len, - ngx_uint_t *key) { + ngx_uint_t *key) +{ struct sockaddr_in *sin; #if (NGX_HAVE_INET6) struct sockaddr_in6 *s6; #endif - switch (sa->sa_family) { + switch (sa->sa_family) + { case AF_INET: sin = (struct sockaddr_in *)sa; *key = ngx_crc32_short((u_char *)&sin->sin_addr, sizeof(sin->sin_addr)); @@ -1377,19 +1600,23 @@ static int ngx_http_auth_digest_srcaddr_key(struct sockaddr *sa, socklen_t len, static int ngx_http_auth_digest_srcaddr_cmp(struct sockaddr *sa1, socklen_t len1, struct sockaddr *sa2, - socklen_t len2) { + socklen_t len2) +{ struct sockaddr_in *sin1, *sin2; #if (NGX_HAVE_INET6) struct sockaddr_in6 *s61, *s62; #endif - if (len1 != len2) { + if (len1 != len2) + { return (len1 < len2) ? -1 : 1; } - if (sa1->sa_family != sa2->sa_family) { + if (sa1->sa_family != sa2->sa_family) + { return (sa1->sa_family < sa2->sa_family) ? -1 : 1; } - switch (sa1->sa_family) { + switch (sa1->sa_family) + { case AF_INET: sin1 = (struct sockaddr_in *)sa1; sin2 = (struct sockaddr_in *)sa2; @@ -1406,18 +1633,17 @@ static int ngx_http_auth_digest_srcaddr_cmp(struct sockaddr *sa1, return -999; } -static void -ngx_http_auth_digest_evasion_tracking(ngx_http_request_t *r, - ngx_http_auth_digest_loc_conf_t *alcf, - ngx_int_t status) { +static void ngx_http_auth_digest_evasion_tracking(ngx_http_request_t *r, + ngx_http_auth_digest_loc_conf_t *alcf, + ngx_int_t status) +{ ngx_slab_pool_t *shpool; ngx_uint_t key; ngx_http_auth_digest_ev_node_t testnode, *node; - if (!ngx_http_auth_digest_srcaddr_key(r->connection->sockaddr, - r->connection->socklen, &key)) { - ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, - "skipping evasive tactics for this source address"); + if (!ngx_http_auth_digest_srcaddr_key(r->connection->sockaddr, r->connection->socklen, &key)) + { + ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, "skipping evasive tactics for this source address"); return; } shpool = (ngx_slab_pool_t *)ngx_http_auth_digest_shm_zone->shm.addr; @@ -1425,24 +1651,25 @@ ngx_http_auth_digest_evasion_tracking(ngx_http_request_t *r, ngx_shmtx_lock(&shpool->mutex); ngx_memzero(&testnode, sizeof(testnode)); testnode.node.key = key; - ngx_memcpy(&testnode.src_addr, r->connection->sockaddr, - r->connection->socklen); + ngx_memcpy(&testnode.src_addr, r->connection->sockaddr, r->connection->socklen); testnode.src_addrlen = r->connection->socklen; - node = ngx_http_auth_digest_ev_rbtree_find( - &testnode, ngx_http_auth_digest_ev_rbtree->root, - ngx_http_auth_digest_ev_rbtree->sentinel); - if (node == NULL) { + node = ngx_http_auth_digest_ev_rbtree_find(&testnode, ngx_http_auth_digest_ev_rbtree->root, ngx_http_auth_digest_ev_rbtree->sentinel); + + if (node == NULL) + { // Don't bother creating a node if this was a successful auth - if (status == NGX_HTTP_AUTH_DIGEST_STATUS_SUCCESS) { - ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, - "sucessful auth, not tracking"); + if (status == NGX_HTTP_AUTH_DIGEST_STATUS_SUCCESS) + { + ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, "sucessful auth, not tracking"); ngx_shmtx_unlock(&shpool->mutex); return; } ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, "adding tracking node"); node = ngx_slab_alloc_locked(shpool, sizeof(ngx_http_auth_digest_ev_node_t)); - if (node == NULL) { + + if (node == NULL) + { ngx_shmtx_unlock(&shpool->mutex); ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "auth_digest ran out of shm space. Increase the " @@ -1455,16 +1682,22 @@ ngx_http_auth_digest_evasion_tracking(ngx_http_request_t *r, ((ngx_rbtree_node_t *)node)->key = key; ngx_rbtree_insert(ngx_http_auth_digest_ev_rbtree, &node->node); } - if (status == NGX_HTTP_AUTH_DIGEST_STATUS_SUCCESS) { + if (status == NGX_HTTP_AUTH_DIGEST_STATUS_SUCCESS) + { ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, "successful auth, clearing evasion counters"); node->failcount = 0; node->drop_time = ngx_time(); - } else { + } + else + { // Reset the failure count to 1 if we're outside the evasion window - if (ngx_time() > node->drop_time) { + if (ngx_time() > node->drop_time) + { node->failcount = 1; - } else { + } + else + { node->failcount += 1; } node->drop_time = ngx_time() + alcf->evasion_time; @@ -1476,14 +1709,15 @@ ngx_http_auth_digest_evasion_tracking(ngx_http_request_t *r, } static int ngx_http_auth_digest_evading(ngx_http_request_t *r, - ngx_http_auth_digest_loc_conf_t *alcf) { + ngx_http_auth_digest_loc_conf_t *alcf) +{ ngx_slab_pool_t *shpool; ngx_uint_t key; ngx_http_auth_digest_ev_node_t testnode, *node; int evading = 0; - if (!ngx_http_auth_digest_srcaddr_key(r->connection->sockaddr, - r->connection->socklen, &key)) { + if (!ngx_http_auth_digest_srcaddr_key(r->connection->sockaddr, r->connection->socklen, &key)) + { return 0; } @@ -1496,15 +1730,66 @@ static int ngx_http_auth_digest_evading(ngx_http_request_t *r, shpool = (ngx_slab_pool_t *)ngx_http_auth_digest_shm_zone->shm.addr; ngx_shmtx_lock(&shpool->mutex); - node = ngx_http_auth_digest_ev_rbtree_find( - &testnode, ngx_http_auth_digest_ev_rbtree->root, - ngx_http_auth_digest_ev_rbtree->sentinel); - if (node != NULL && node->failcount >= alcf->maxtries && - ngx_time() < node->drop_time) { - ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, - "ignoring authentication request - in evasion period"); + node = ngx_http_auth_digest_ev_rbtree_find(&testnode, ngx_http_auth_digest_ev_rbtree->root, ngx_http_auth_digest_ev_rbtree->sentinel); + + if (node != NULL && node->failcount >= alcf->maxtries && ngx_time() < node->drop_time) + { + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "ignoring authentication request - in evasion period"); evading = 1; } ngx_shmtx_unlock(&shpool->mutex); return evading; } + +// pulled in from nginx_auth_o_module +int ngx_http_auth_os_is_loopback(ngx_str_t *server) +{ + char *localhost = "localhost"; + int localhostLen = strlen(localhost); + char *loopback = "127.0.0.1"; + int loopbackLen = strlen(loopback); + + int pos = 0, portCharPos = 0; + char portChar[4] = ""; + int port = 0; + + if (server->len <= 0 || server->data == NULL) + return -1; + + if (((int)strlen((const char *)server->data) >= localhostLen) && (strncmp((const char *)server->data, localhost, localhostLen) == 0)) + { + pos = localhostLen; + } + else if (((int)strlen((const char *)server->data) >= loopbackLen) && (strncmp((const char *)server->data, loopback, loopbackLen) == 0)) + { + pos = loopbackLen; + } + else + { + return -1; + } + + if (server->data[pos] == '\0') + { + return 0; + } + else if (server->data[pos] == ':') + { + ++pos; + while (portCharPos <= 3 && server->data[pos] >= '0' && server->data[pos] <= '9') + { + portChar[portCharPos] = server->data[pos]; + ++portCharPos; + ++pos; + } + + if (server->data[pos] != '\0') + return -1; + + port = atoi(portChar); + if (port >= 1 && port <= 65535) + return 0; + } + + return -1; +} \ No newline at end of file diff --git a/ngx_http_auth_digest_module.h b/ngx_http_auth_digest_module.h index 6694354..3c4fa2c 100644 --- a/ngx_http_auth_digest_module.h +++ b/ngx_http_auth_digest_module.h @@ -4,7 +4,8 @@ #define NGX_HTTP_AUTH_DIGEST_USERNOTFOUND 1000 // the module conf -typedef struct { +typedef struct +{ ngx_http_complex_value_t realm; time_t timeout; time_t expires; @@ -14,10 +15,12 @@ typedef struct { ngx_int_t maxtries; ngx_http_complex_value_t user_file; ngx_str_t cache_dir; + ngx_str_t localhost_nocheck; } ngx_http_auth_digest_loc_conf_t; // contents of the request's authorization header -typedef struct { +typedef struct +{ ngx_str_t auth_scheme; ngx_str_t username; ngx_str_t realm; @@ -32,13 +35,15 @@ typedef struct { } ngx_http_auth_digest_cred_t; // the nonce as an issue-time/random-num pair -typedef struct { +typedef struct +{ ngx_uint_t rnd; time_t t; } ngx_http_auth_digest_nonce_t; // nonce entries in the rbtree -typedef struct { +typedef struct +{ ngx_rbtree_node_t node; // the node's .key is derived from the nonce val time_t expires; // time at which the node should be evicted time_t drop_time; @@ -46,7 +51,8 @@ typedef struct { } ngx_http_auth_digest_node_t; // evasion entries in the rbtree -typedef struct { +typedef struct +{ ngx_rbtree_node_t node; // the node's .key is derived from the source address time_t drop_time; ngx_int_t failcount; @@ -68,32 +74,25 @@ static char *ngx_http_auth_digest_set_user_file(ngx_conf_t *cf, #define NGX_HTTP_AUTH_DIGEST_BUF_SIZE 4096 // digest challenge generation -static ngx_int_t ngx_http_auth_digest_send_challenge(ngx_http_request_t *r, - ngx_str_t *realm, +static ngx_int_t ngx_http_auth_digest_send_challenge(ngx_http_request_t *r, + ngx_str_t *realm, ngx_uint_t is_stale); // digest response validators static ngx_int_t -ngx_http_auth_digest_check_credentials(ngx_http_request_t *r, - ngx_http_auth_digest_cred_t *ctx); +ngx_http_auth_digest_check_credentials(ngx_http_request_t *r, ngx_http_auth_digest_cred_t *ctx); static ngx_int_t -ngx_http_auth_digest_verify_user(ngx_http_request_t *r, - ngx_http_auth_digest_cred_t *fields, - ngx_str_t *line); +ngx_http_auth_digest_verify_user(ngx_http_request_t *r, ngx_http_auth_digest_cred_t *fields, ngx_str_t *line); static ngx_int_t -ngx_http_auth_digest_verify_hash(ngx_http_request_t *r, - ngx_http_auth_digest_cred_t *fields, - u_char *hashed_pw); +ngx_http_auth_digest_verify_hash(ngx_http_request_t *r, ngx_http_auth_digest_cred_t *fields, u_char *hashed_pw); // the shm segment that houses the used-nonces tree and evasion rbtree static ngx_uint_t ngx_http_auth_digest_shm_size; static ngx_shm_zone_t *ngx_http_auth_digest_shm_zone; static ngx_rbtree_t *ngx_http_auth_digest_rbtree; static ngx_rbtree_t *ngx_http_auth_digest_ev_rbtree; -static char *ngx_http_auth_digest_set_shm_size(ngx_conf_t *cf, - ngx_command_t *cmd, void *conf); -static ngx_int_t ngx_http_auth_digest_init_shm_zone(ngx_shm_zone_t *shm_zone, - void *data); +static char *ngx_http_auth_digest_set_shm_size(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); +static ngx_int_t ngx_http_auth_digest_init_shm_zone(ngx_shm_zone_t *shm_zone, void *data); // nonce bookkeeping static ngx_http_auth_digest_nonce_t @@ -122,6 +121,7 @@ ngx_http_auth_digest_ev_rbtree_prune_walk(ngx_rbtree_node_t *node, // evasive tactics functions static int ngx_http_auth_digest_srcaddr_key(struct sockaddr *sa, socklen_t len, ngx_uint_t *key); + static int ngx_http_auth_digest_srcaddr_cmp(struct sockaddr *sa1, socklen_t len1, struct sockaddr *sa2, @@ -130,12 +130,14 @@ static ngx_http_auth_digest_ev_node_t * ngx_http_auth_digest_ev_rbtree_find(ngx_http_auth_digest_ev_node_t *this, ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel); + #define NGX_HTTP_AUTH_DIGEST_STATUS_SUCCESS 1 #define NGX_HTTP_AUTH_DIGEST_STATUS_FAILURE 0 -static void -ngx_http_auth_digest_evasion_tracking(ngx_http_request_t *r, - ngx_http_auth_digest_loc_conf_t *alcf, - ngx_int_t status); + +static void ngx_http_auth_digest_evasion_tracking(ngx_http_request_t *r, + ngx_http_auth_digest_loc_conf_t *alcf, + ngx_int_t status); + static int ngx_http_auth_digest_evading(ngx_http_request_t *r, ngx_http_auth_digest_loc_conf_t *alcf); @@ -143,6 +145,7 @@ static int ngx_http_auth_digest_evading(ngx_http_request_t *r, static void ngx_http_auth_digest_rbtree_insert(ngx_rbtree_node_t *temp, ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel); + static void ngx_http_auth_digest_ev_rbtree_insert(ngx_rbtree_node_t *temp, ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel); @@ -151,17 +154,21 @@ ngx_rbtree_generic_insert(ngx_rbtree_node_t *temp, ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel, int (*compare)(const ngx_rbtree_node_t *left, const ngx_rbtree_node_t *right)); + static int ngx_http_auth_digest_rbtree_cmp(const ngx_rbtree_node_t *v_left, const ngx_rbtree_node_t *v_right); // quick & dirty bitvectors (for marking used nc values) -static ngx_inline ngx_uint_t ngx_bitvector_size(ngx_uint_t nbits) { +static ngx_inline ngx_uint_t ngx_bitvector_size(ngx_uint_t nbits) +{ return ((nbits + CHAR_BIT - 1) / CHAR_BIT); } -static ngx_inline ngx_uint_t ngx_bitvector_test(char *bv, ngx_uint_t bit) { +static ngx_inline ngx_uint_t ngx_bitvector_test(char *bv, ngx_uint_t bit) +{ return ((bv)[((bit) / CHAR_BIT)] & (1 << ((bit) % CHAR_BIT))); } -static ngx_inline void ngx_bitvector_set(char *bv, ngx_uint_t bit) { +static ngx_inline void ngx_bitvector_set(char *bv, ngx_uint_t bit) +{ ((bv)[((bit) / CHAR_BIT)] &= ~(1 << ((bit) % CHAR_BIT))); } @@ -173,48 +180,79 @@ static ngx_int_t ngx_http_auth_digest_init(ngx_conf_t *cf); static ngx_int_t ngx_http_auth_digest_worker_init(ngx_cycle_t *cycle); // module datastructures -static ngx_command_t ngx_http_auth_digest_commands[] = { - {ngx_string("auth_digest"), - NGX_HTTP_MAIN_CONF | NGX_HTTP_SRV_CONF | NGX_HTTP_LOC_CONF | - NGX_HTTP_LMT_CONF | NGX_CONF_TAKE1, - ngx_http_auth_digest_set_realm, NGX_HTTP_LOC_CONF_OFFSET, - offsetof(ngx_http_auth_digest_loc_conf_t, realm), NULL}, - {ngx_string("auth_digest_user_file"), - NGX_HTTP_MAIN_CONF | NGX_HTTP_SRV_CONF | NGX_HTTP_LOC_CONF | - NGX_HTTP_LMT_CONF | NGX_CONF_TAKE1, - ngx_http_auth_digest_set_user_file, NGX_HTTP_LOC_CONF_OFFSET, - offsetof(ngx_http_auth_digest_loc_conf_t, user_file), NULL}, - {ngx_string("auth_digest_timeout"), NGX_HTTP_MAIN_CONF | NGX_HTTP_SRV_CONF | - NGX_HTTP_LOC_CONF | NGX_CONF_TAKE1, - ngx_conf_set_sec_slot, NGX_HTTP_LOC_CONF_OFFSET, - offsetof(ngx_http_auth_digest_loc_conf_t, timeout), NULL}, - {ngx_string("auth_digest_expires"), NGX_HTTP_MAIN_CONF | NGX_HTTP_SRV_CONF | - NGX_HTTP_LOC_CONF | NGX_CONF_TAKE1, - ngx_conf_set_sec_slot, NGX_HTTP_LOC_CONF_OFFSET, - offsetof(ngx_http_auth_digest_loc_conf_t, expires), NULL}, - {ngx_string("auth_digest_drop_time"), - NGX_HTTP_MAIN_CONF | NGX_HTTP_SRV_CONF | NGX_HTTP_LOC_CONF | - NGX_CONF_TAKE1, - ngx_conf_set_sec_slot, NGX_HTTP_LOC_CONF_OFFSET, - offsetof(ngx_http_auth_digest_loc_conf_t, drop_time), NULL}, - {ngx_string("auth_digest_evasion_time"), - NGX_HTTP_MAIN_CONF | NGX_HTTP_SRV_CONF | NGX_HTTP_LOC_CONF | - NGX_CONF_TAKE1, - ngx_conf_set_sec_slot, NGX_HTTP_LOC_CONF_OFFSET, - offsetof(ngx_http_auth_digest_loc_conf_t, evasion_time), NULL}, - {ngx_string("auth_digest_replays"), NGX_HTTP_MAIN_CONF | NGX_HTTP_SRV_CONF | - NGX_HTTP_LOC_CONF | NGX_CONF_TAKE1, - ngx_conf_set_num_slot, NGX_HTTP_LOC_CONF_OFFSET, - offsetof(ngx_http_auth_digest_loc_conf_t, replays), NULL}, - {ngx_string("auth_digest_maxtries"), - NGX_HTTP_MAIN_CONF | NGX_HTTP_SRV_CONF | NGX_HTTP_LOC_CONF | - NGX_CONF_TAKE1, - ngx_conf_set_num_slot, NGX_HTTP_LOC_CONF_OFFSET, - offsetof(ngx_http_auth_digest_loc_conf_t, maxtries), NULL}, - {ngx_string("auth_digest_shm_size"), - NGX_HTTP_MAIN_CONF | NGX_HTTP_SRV_CONF | NGX_CONF_TAKE1, - ngx_http_auth_digest_set_shm_size, 0, 0, NULL}, - ngx_null_command}; +static ngx_command_t ngx_http_auth_digest_commands[] = + { + {ngx_string("auth_digest"), + NGX_HTTP_MAIN_CONF | NGX_HTTP_SRV_CONF | NGX_HTTP_LOC_CONF | + NGX_HTTP_LMT_CONF | NGX_CONF_TAKE1, + ngx_http_auth_digest_set_realm, + NGX_HTTP_LOC_CONF_OFFSET, + offsetof(ngx_http_auth_digest_loc_conf_t, realm), + NULL}, + + {ngx_string("auth_digest_user_file"), + NGX_HTTP_MAIN_CONF | NGX_HTTP_SRV_CONF | NGX_HTTP_LOC_CONF | + NGX_HTTP_LMT_CONF | NGX_CONF_TAKE1, + ngx_http_auth_digest_set_user_file, + NGX_HTTP_LOC_CONF_OFFSET, + offsetof(ngx_http_auth_digest_loc_conf_t, user_file), + NULL}, + + {ngx_string("auth_digest_localhost_nocheck"), + NGX_HTTP_LOC_CONF | NGX_CONF_TAKE1 | NGX_HTTP_LMT_CONF, + ngx_conf_set_str_slot, + NGX_HTTP_LOC_CONF_OFFSET, + offsetof(ngx_http_auth_digest_loc_conf_t, localhost_nocheck), + NULL}, + + {ngx_string("auth_digest_timeout"), + NGX_HTTP_MAIN_CONF | NGX_HTTP_SRV_CONF | NGX_HTTP_LOC_CONF | NGX_CONF_TAKE1, + ngx_conf_set_sec_slot, + NGX_HTTP_LOC_CONF_OFFSET, + offsetof(ngx_http_auth_digest_loc_conf_t, timeout), + NULL}, + + {ngx_string("auth_digest_expires"), + NGX_HTTP_MAIN_CONF | NGX_HTTP_SRV_CONF | NGX_HTTP_LOC_CONF | NGX_CONF_TAKE1, + ngx_conf_set_sec_slot, + NGX_HTTP_LOC_CONF_OFFSET, + offsetof(ngx_http_auth_digest_loc_conf_t, expires), + NULL}, + + {ngx_string("auth_digest_drop_time"), + NGX_HTTP_MAIN_CONF | NGX_HTTP_SRV_CONF | NGX_HTTP_LOC_CONF | + NGX_CONF_TAKE1, + ngx_conf_set_sec_slot, + NGX_HTTP_LOC_CONF_OFFSET, + offsetof(ngx_http_auth_digest_loc_conf_t, drop_time), + NULL}, + + {ngx_string("auth_digest_evasion_time"), + NGX_HTTP_MAIN_CONF | NGX_HTTP_SRV_CONF | NGX_HTTP_LOC_CONF | NGX_CONF_TAKE1, + ngx_conf_set_sec_slot, + NGX_HTTP_LOC_CONF_OFFSET, + offsetof(ngx_http_auth_digest_loc_conf_t, evasion_time), + NULL}, + + {ngx_string("auth_digest_replays"), + NGX_HTTP_MAIN_CONF | NGX_HTTP_SRV_CONF | NGX_HTTP_LOC_CONF | NGX_CONF_TAKE1, + ngx_conf_set_num_slot, + NGX_HTTP_LOC_CONF_OFFSET, + offsetof(ngx_http_auth_digest_loc_conf_t, replays), + NULL}, + + {ngx_string("auth_digest_maxtries"), + NGX_HTTP_MAIN_CONF | NGX_HTTP_SRV_CONF | NGX_HTTP_LOC_CONF | + NGX_CONF_TAKE1, + ngx_conf_set_num_slot, + NGX_HTTP_LOC_CONF_OFFSET, + offsetof(ngx_http_auth_digest_loc_conf_t, maxtries), + NULL}, + + {ngx_string("auth_digest_shm_size"), + NGX_HTTP_MAIN_CONF | NGX_HTTP_SRV_CONF | NGX_CONF_TAKE1, + ngx_http_auth_digest_set_shm_size, 0, 0, NULL}, + ngx_null_command}; static ngx_http_module_t ngx_http_auth_digest_module_ctx = { NULL, /* preconfiguration */ From a2f909f1a93132ff84da2bcdba289fc498cf1806 Mon Sep 17 00:00:00 2001 From: Nate Gates Date: Wed, 3 Mar 2021 15:42:18 -0600 Subject: [PATCH 2/4] got setting working, format clean up. --- ngx_http_auth_digest_module.c | 3299 ++++++++++++++++----------------- ngx_http_auth_digest_module.h | 20 +- 2 files changed, 1655 insertions(+), 1664 deletions(-) diff --git a/ngx_http_auth_digest_module.c b/ngx_http_auth_digest_module.c index 234ac63..55239c2 100644 --- a/ngx_http_auth_digest_module.c +++ b/ngx_http_auth_digest_module.c @@ -12,1784 +12,1779 @@ #include "ngx_http_auth_digest_module.h" -static void *ngx_http_auth_digest_create_loc_conf(ngx_conf_t *cf) +static void* ngx_http_auth_digest_create_loc_conf(ngx_conf_t* cf) { - ngx_http_auth_digest_loc_conf_t *conf; - - conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_auth_digest_loc_conf_t)); - if (conf == NULL) - { - return NULL; - } - - conf->timeout = NGX_CONF_UNSET_UINT; - conf->expires = NGX_CONF_UNSET_UINT; - conf->drop_time = NGX_CONF_UNSET_UINT; - conf->replays = NGX_CONF_UNSET_UINT; - conf->evasion_time = NGX_CONF_UNSET_UINT; - conf->maxtries = NGX_CONF_UNSET_UINT; - return conf; + ngx_http_auth_digest_loc_conf_t* conf; + + conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_auth_digest_loc_conf_t)); + if (conf == NULL) + { + return NULL; + } + + conf->timeout = NGX_CONF_UNSET_UINT; + conf->expires = NGX_CONF_UNSET_UINT; + conf->drop_time = NGX_CONF_UNSET_UINT; + conf->replays = NGX_CONF_UNSET_UINT; + conf->evasion_time = NGX_CONF_UNSET_UINT; + conf->maxtries = NGX_CONF_UNSET_UINT; + return conf; } -static char *ngx_http_auth_digest_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child) +static char* ngx_http_auth_digest_merge_loc_conf(ngx_conf_t* cf, void* parent, void* child) { - ngx_http_auth_digest_loc_conf_t *prev = parent; - ngx_http_auth_digest_loc_conf_t *conf = child; - - ngx_conf_merge_sec_value(conf->timeout, prev->timeout, 60); - ngx_conf_merge_sec_value(conf->expires, prev->expires, 10); - ngx_conf_merge_sec_value(conf->drop_time, prev->drop_time, 300); - ngx_conf_merge_value(conf->replays, prev->replays, 20); - ngx_conf_merge_sec_value(conf->evasion_time, prev->evasion_time, 300); - ngx_conf_merge_value(conf->maxtries, prev->maxtries, 5); - - if (conf->user_file.value.len == 0) - { - conf->user_file = prev->user_file; - } - - if (conf->realm.value.len == 0) - { - conf->realm = prev->realm; - } - - return NGX_CONF_OK; + ngx_http_auth_digest_loc_conf_t* prev = parent; + ngx_http_auth_digest_loc_conf_t* conf = child; + + ngx_conf_merge_sec_value(conf->timeout, prev->timeout, 60); + ngx_conf_merge_sec_value(conf->expires, prev->expires, 10); + ngx_conf_merge_sec_value(conf->drop_time, prev->drop_time, 300); + ngx_conf_merge_value(conf->replays, prev->replays, 20); + ngx_conf_merge_sec_value(conf->evasion_time, prev->evasion_time, 300); + ngx_conf_merge_value(conf->maxtries, prev->maxtries, 5); + ngx_conf_merge_str_value(conf->localhost_nocheck, prev->localhost_nocheck, "false"); + + if (conf->user_file.value.len == 0) + { + conf->user_file = prev->user_file; + } + + if (conf->realm.value.len == 0) + { + conf->realm = prev->realm; + } + + return NGX_CONF_OK; } -static ngx_int_t ngx_http_auth_digest_init(ngx_conf_t *cf) +static ngx_int_t ngx_http_auth_digest_init(ngx_conf_t* cf) { - ngx_http_handler_pt *h; - ngx_http_core_main_conf_t *cmcf; - ngx_str_t *shm_name; - - cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module); - - h = ngx_array_push(&cmcf->phases[NGX_HTTP_ACCESS_PHASE].handlers); - if (h == NULL) - { - return NGX_ERROR; - } - - *h = ngx_http_auth_digest_handler; - - ngx_http_auth_digest_cleanup_timer = - ngx_pcalloc(cf->pool, sizeof(ngx_event_t)); - if (ngx_http_auth_digest_cleanup_timer == NULL) - { - return NGX_ERROR; - } - - shm_name = ngx_palloc(cf->pool, sizeof *shm_name); - shm_name->len = sizeof("auth_digest"); - shm_name->data = (unsigned char *)"auth_digest"; - - if (ngx_http_auth_digest_shm_size == 0) - { - ngx_http_auth_digest_shm_size = 4 * 256 * ngx_pagesize; // default to 4mb - } - - ngx_http_auth_digest_shm_zone = - ngx_shared_memory_add(cf, shm_name, ngx_http_auth_digest_shm_size, - &ngx_http_auth_digest_module); - if (ngx_http_auth_digest_shm_zone == NULL) - { - return NGX_ERROR; - } - ngx_http_auth_digest_shm_zone->init = ngx_http_auth_digest_init_shm_zone; - - return NGX_OK; + ngx_http_handler_pt* h; + ngx_http_core_main_conf_t* cmcf; + ngx_str_t* shm_name; + + cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module); + + h = ngx_array_push(&cmcf->phases[NGX_HTTP_ACCESS_PHASE].handlers); + if (h == NULL) + { + return NGX_ERROR; + } + + *h = ngx_http_auth_digest_handler; + + ngx_http_auth_digest_cleanup_timer = + ngx_pcalloc(cf->pool, sizeof(ngx_event_t)); + if (ngx_http_auth_digest_cleanup_timer == NULL) + { + return NGX_ERROR; + } + + shm_name = ngx_palloc(cf->pool, sizeof * shm_name); + shm_name->len = sizeof("auth_digest"); + shm_name->data = (unsigned char*)"auth_digest"; + + if (ngx_http_auth_digest_shm_size == 0) + { + ngx_http_auth_digest_shm_size = 4 * 256 * ngx_pagesize; // default to 4mb + } + + ngx_http_auth_digest_shm_zone = + ngx_shared_memory_add(cf, shm_name, ngx_http_auth_digest_shm_size, + &ngx_http_auth_digest_module); + if (ngx_http_auth_digest_shm_zone == NULL) + { + return NGX_ERROR; + } + ngx_http_auth_digest_shm_zone->init = ngx_http_auth_digest_init_shm_zone; + + return NGX_OK; } -static ngx_int_t ngx_http_auth_digest_worker_init(ngx_cycle_t *cycle) +static ngx_int_t ngx_http_auth_digest_worker_init(ngx_cycle_t* cycle) { - if (ngx_process != NGX_PROCESS_WORKER) - { - return NGX_OK; - } - - // create a cleanup queue big enough for the max number of tree nodes in the - // shm - ngx_http_auth_digest_cleanup_list = - ngx_array_create(cycle->pool, NGX_HTTP_AUTH_DIGEST_CLEANUP_BATCH_SIZE, - sizeof(ngx_rbtree_node_t *)); - - if (ngx_http_auth_digest_cleanup_list == NULL) - { - ngx_log_error(NGX_LOG_EMERG, cycle->log, 0, - "Could not allocate shared memory for auth_digest"); - return NGX_ERROR; - } - - ngx_connection_t *dummy = ngx_pcalloc(cycle->pool, sizeof(ngx_connection_t)); - if (dummy == NULL) - { - return NGX_ERROR; - } - dummy->fd = (ngx_socket_t)-1; - dummy->data = cycle; - - ngx_http_auth_digest_cleanup_timer->log = ngx_cycle->log; - ngx_http_auth_digest_cleanup_timer->data = dummy; - ngx_http_auth_digest_cleanup_timer->handler = ngx_http_auth_digest_cleanup; - ngx_add_timer(ngx_http_auth_digest_cleanup_timer, - NGX_HTTP_AUTH_DIGEST_CLEANUP_INTERVAL); - return NGX_OK; + if (ngx_process != NGX_PROCESS_WORKER) + { + return NGX_OK; + } + + // create a cleanup queue big enough for the max number of tree nodes in the + // shm + ngx_http_auth_digest_cleanup_list = ngx_array_create(cycle->pool, NGX_HTTP_AUTH_DIGEST_CLEANUP_BATCH_SIZE, + sizeof(ngx_rbtree_node_t*)); + + if (ngx_http_auth_digest_cleanup_list == NULL) + { + ngx_log_error(NGX_LOG_EMERG, cycle->log, 0, "Could not allocate shared memory for auth_digest"); + return NGX_ERROR; + } + + ngx_connection_t* dummy = ngx_pcalloc(cycle->pool, sizeof(ngx_connection_t)); + if (dummy == NULL) + { + return NGX_ERROR; + } + dummy->fd = (ngx_socket_t)-1; + dummy->data = cycle; + + ngx_http_auth_digest_cleanup_timer->log = ngx_cycle->log; + ngx_http_auth_digest_cleanup_timer->data = dummy; + ngx_http_auth_digest_cleanup_timer->handler = ngx_http_auth_digest_cleanup; + ngx_add_timer(ngx_http_auth_digest_cleanup_timer, + NGX_HTTP_AUTH_DIGEST_CLEANUP_INTERVAL); + return NGX_OK; } -static ngx_int_t ngx_http_auth_digest_handler(ngx_http_request_t *r) +static ngx_int_t ngx_http_auth_digest_handler(ngx_http_request_t* r) { - off_t offset; - ssize_t n; - ngx_fd_t fd; - ngx_int_t rc; - ngx_err_t err; - ngx_str_t user_file, passwd_line, realm, localhost_nocheck; - ngx_file_t file; - ngx_uint_t i, begin, tail, idle; - ngx_http_auth_digest_loc_conf_t *alcf; - ngx_http_auth_digest_cred_t *auth_fields; - u_char buf[NGX_HTTP_AUTH_DIGEST_BUF_SIZE]; - u_char line[NGX_HTTP_AUTH_DIGEST_BUF_SIZE]; - u_char *p; - - if (r->internal) - { - return NGX_DECLINED; - } - - // if digest auth is disabled for this location, bail out immediately - alcf = ngx_http_get_module_loc_conf(r, ngx_http_auth_digest_module); - - if (alcf->realm.value.len == 0) - { - return NGX_DECLINED; - } - - if (ngx_http_complex_value(r, &alcf->realm, &realm) != NGX_OK) - { - return NGX_ERROR; - } - - if (realm.len == 0 || alcf->user_file.value.len == 0) - { - return NGX_DECLINED; - } - - if (ngx_strcmp(realm.data, "off") == 0) - { - return NGX_DECLINED; - } - - if (ngx_strcmp(localhost_nocheck.data, "true") && ngx_http_auth_os_is_loopback(&(r->headers_in.server)) == 0)) - { - return NGX_DECLINED; - } - - if (ngx_http_auth_digest_evading(r, alcf)) - { - return NGX_HTTP_UNAUTHORIZED; - } - // unpack the Authorization header (if any) and verify that it contains all - // required fields. otherwise send a challenge - auth_fields = ngx_pcalloc(r->pool, sizeof(ngx_http_auth_digest_cred_t)); - rc = ngx_http_auth_digest_check_credentials(r, auth_fields); - if (rc == NGX_DECLINED) - { - return ngx_http_auth_digest_send_challenge(r, &realm, 0); - } - else if (rc == NGX_ERROR) - { - return NGX_HTTP_INTERNAL_SERVER_ERROR; - } - - // check for the existence of a passwd file and attempt to open it - if (ngx_http_complex_value(r, &alcf->user_file, &user_file) != NGX_OK) - { - return NGX_ERROR; - } - fd = ngx_open_file(user_file.data, NGX_FILE_RDONLY, NGX_FILE_OPEN, 0); - if (fd == NGX_INVALID_FILE) - { - ngx_uint_t level; - err = ngx_errno; - - if (err == NGX_ENOENT) - { - level = NGX_LOG_ERR; - rc = NGX_HTTP_FORBIDDEN; - } - else - { - level = NGX_LOG_CRIT; - rc = NGX_HTTP_INTERNAL_SERVER_ERROR; - } - - ngx_log_error(level, r->connection->log, err, - ngx_open_file_n " \"%s\" failed", user_file.data); - return rc; - } - ngx_memzero(&file, sizeof(ngx_file_t)); - file.fd = fd; - file.name = user_file; - file.log = r->connection->log; - - // step through the passwd file and find the individual lines, then pass them - // off - // to be compared against the values in the authorization header - passwd_line.data = line; - offset = begin = tail = 0; - idle = 1; - ngx_memzero(buf, NGX_HTTP_AUTH_DIGEST_BUF_SIZE); - ngx_memzero(passwd_line.data, NGX_HTTP_AUTH_DIGEST_BUF_SIZE); - while (1) - { - n = ngx_read_file(&file, buf + tail, NGX_HTTP_AUTH_DIGEST_BUF_SIZE - tail, - offset); - if (n == NGX_ERROR) - { - ngx_http_auth_digest_close(&file); - return NGX_HTTP_INTERNAL_SERVER_ERROR; - } - - begin = 0; - for (i = 0; i < n + tail; i++) - { - if (buf[i] == '\n' || buf[i] == '\r') - { - if (!idle && - i - begin > - 36) - { // 36 is the min length with a single-char name and realm - p = ngx_cpymem(passwd_line.data, &buf[begin], i - begin); - p[0] = '\0'; - passwd_line.len = i - begin; - rc = ngx_http_auth_digest_verify_user(r, auth_fields, &passwd_line); - - if (rc == NGX_HTTP_AUTH_DIGEST_USERNOTFOUND) - { - rc = NGX_DECLINED; - } - - if (rc != NGX_DECLINED) - { - ngx_http_auth_digest_close(&file); - ngx_http_auth_digest_evasion_tracking( - r, alcf, NGX_HTTP_AUTH_DIGEST_STATUS_SUCCESS); - return rc; - } - } - idle = 1; - begin = i; - } - else if (idle) - { - idle = 0; - begin = i; - } - } - - if (!idle) - { - tail = n + tail - begin; - if (n == 0 && tail > 36) - { - p = ngx_cpymem(passwd_line.data, &buf[begin], tail); - p[0] = '\0'; - passwd_line.len = i - begin; - rc = ngx_http_auth_digest_verify_user(r, auth_fields, &passwd_line); - if (rc == NGX_HTTP_AUTH_DIGEST_USERNOTFOUND) - { - rc = NGX_DECLINED; - } - if (rc != NGX_DECLINED) - { - ngx_http_auth_digest_close(&file); - ngx_http_auth_digest_evasion_tracking( - r, alcf, NGX_HTTP_AUTH_DIGEST_STATUS_SUCCESS); - return rc; - } - } - else - { - ngx_memmove(buf, &buf[begin], tail); - } - } - - if (n == 0) - { - break; - } - - offset += n; - } - - ngx_http_auth_digest_close(&file); - - // log only wrong username/password, - // not expired hash - int nc = ngx_hextoi(auth_fields->nc.data, auth_fields->nc.len); - if (nc == 1) - { - ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, - "invalid username or password for %*s", - auth_fields->username.len, auth_fields->username.data); - } - - ngx_http_auth_digest_evasion_tracking(r, alcf, - NGX_HTTP_AUTH_DIGEST_STATUS_FAILURE); - - // since no match was found based on the fields in the authorization header, - // send a new challenge and let the client retry - return ngx_http_auth_digest_send_challenge(r, &realm, auth_fields->stale); + off_t offset; + ssize_t n; + ngx_fd_t fd; + ngx_int_t rc; + ngx_err_t err; + ngx_str_t user_file, passwd_line, realm; + ngx_file_t file; + ngx_uint_t i, begin, tail, idle; + ngx_http_auth_digest_loc_conf_t* alcf; + ngx_http_auth_digest_cred_t* auth_fields; + u_char buf[NGX_HTTP_AUTH_DIGEST_BUF_SIZE]; + u_char line[NGX_HTTP_AUTH_DIGEST_BUF_SIZE]; + u_char* p; + + if (r->internal) + { + return NGX_DECLINED; + } + + // if digest auth is disabled for this location, bail out immediately + alcf = ngx_http_get_module_loc_conf(r, ngx_http_auth_digest_module); + + if (alcf->realm.value.len == 0) + { + return NGX_DECLINED; + } + + if (ngx_http_complex_value(r, &alcf->realm, &realm) != NGX_OK) + { + return NGX_ERROR; + } + + if (realm.len == 0 || alcf->user_file.value.len == 0) + { + return NGX_DECLINED; + } + + if (ngx_strcmp(realm.data, "off") == 0) + { + return NGX_DECLINED; + } + + const int isLocalHost = ngx_http_auth_digest_is_loopback(&(r->headers_in.server)) == 0; + const int isLocalNoCheckEnabled = ngx_strcmp(alcf->localhost_nocheck.data, "true") == 0; + + if (isLocalNoCheckEnabled && isLocalHost) { + + return NGX_DECLINED; + } + + if (ngx_http_auth_digest_evading(r, alcf)) + { + return NGX_HTTP_UNAUTHORIZED; + } + // unpack the Authorization header (if any) and verify that it contains all + // required fields. otherwise send a challenge + auth_fields = ngx_pcalloc(r->pool, sizeof(ngx_http_auth_digest_cred_t)); + rc = ngx_http_auth_digest_check_credentials(r, auth_fields); + if (rc == NGX_DECLINED) + { + return ngx_http_auth_digest_send_challenge(r, &realm, 0); + } + else if (rc == NGX_ERROR) + { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + // check for the existence of a passwd file and attempt to open it + if (ngx_http_complex_value(r, &alcf->user_file, &user_file) != NGX_OK) { + return NGX_ERROR; + } + fd = ngx_open_file(user_file.data, NGX_FILE_RDONLY, NGX_FILE_OPEN, 0); + if (fd == NGX_INVALID_FILE) + { + ngx_uint_t level; + err = ngx_errno; + + if (err == NGX_ENOENT) { + level = NGX_LOG_ERR; + rc = NGX_HTTP_FORBIDDEN; + } + else { + level = NGX_LOG_CRIT; + rc = NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + ngx_log_error(level, r->connection->log, err, + ngx_open_file_n " \"%s\" failed", user_file.data); + return rc; + } + ngx_memzero(&file, sizeof(ngx_file_t)); + file.fd = fd; + file.name = user_file; + file.log = r->connection->log; + + // step through the passwd file and find the individual lines, then pass them + // off + // to be compared against the values in the authorization header + passwd_line.data = line; + offset = begin = tail = 0; + idle = 1; + ngx_memzero(buf, NGX_HTTP_AUTH_DIGEST_BUF_SIZE); + ngx_memzero(passwd_line.data, NGX_HTTP_AUTH_DIGEST_BUF_SIZE); + while (1) + { + n = ngx_read_file(&file, buf + tail, NGX_HTTP_AUTH_DIGEST_BUF_SIZE - tail, + offset); + if (n == NGX_ERROR) + { + ngx_http_auth_digest_close(&file); + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + begin = 0; + for (i = 0; i < n + tail; i++) + { + if (buf[i] == '\n' || buf[i] == '\r') + { + if (!idle && i - begin > 36) + { // 36 is the min length with a single-char name and realm + p = ngx_cpymem(passwd_line.data, &buf[begin], i - begin); + p[0] = '\0'; + passwd_line.len = i - begin; + rc = ngx_http_auth_digest_verify_user(r, auth_fields, &passwd_line); + + if (rc == NGX_HTTP_AUTH_DIGEST_USERNOTFOUND) + { + rc = NGX_DECLINED; + } + + if (rc != NGX_DECLINED) + { + ngx_http_auth_digest_close(&file); + ngx_http_auth_digest_evasion_tracking(r, alcf, NGX_HTTP_AUTH_DIGEST_STATUS_SUCCESS); + return rc; + } + } + idle = 1; + begin = i; + } + else if (idle) + { + idle = 0; + begin = i; + } + } + + if (!idle) + { + tail = n + tail - begin; + if (n == 0 && tail > 36) + { + p = ngx_cpymem(passwd_line.data, &buf[begin], tail); + p[0] = '\0'; + passwd_line.len = i - begin; + rc = ngx_http_auth_digest_verify_user(r, auth_fields, &passwd_line); + if (rc == NGX_HTTP_AUTH_DIGEST_USERNOTFOUND) + { + rc = NGX_DECLINED; + } + if (rc != NGX_DECLINED) + { + ngx_http_auth_digest_close(&file); + ngx_http_auth_digest_evasion_tracking( + r, alcf, NGX_HTTP_AUTH_DIGEST_STATUS_SUCCESS); + return rc; + } + } + else + { + ngx_memmove(buf, &buf[begin], tail); + } + } + + if (n == 0) + { + break; + } + + offset += n; + } + + ngx_http_auth_digest_close(&file); + + // log only wrong username/password, + // not expired hash + int nc = ngx_hextoi(auth_fields->nc.data, auth_fields->nc.len); + if (nc == 1) + { + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "invalid username or password for %*s", + auth_fields->username.len, auth_fields->username.data); + } + + ngx_http_auth_digest_evasion_tracking(r, alcf, + NGX_HTTP_AUTH_DIGEST_STATUS_FAILURE); + + // since no match was found based on the fields in the authorization header, + // send a new challenge and let the client retry + return ngx_http_auth_digest_send_challenge(r, &realm, auth_fields->stale); } ngx_int_t -ngx_http_auth_digest_check_credentials(ngx_http_request_t *r, ngx_http_auth_digest_cred_t *ctx) +ngx_http_auth_digest_check_credentials(ngx_http_request_t* r, ngx_http_auth_digest_cred_t* ctx) { - if (r->headers_in.authorization == NULL) - { - return NGX_DECLINED; - } - - /* - token = 1* - separators = "(" | ")" | "<" | ">" | "@" - | "," | ";" | ":" | "\" | <"> - | "/" | "[" | "]" | "?" | "=" - | "{" | "}" | SP | HT - */ - - static uint32_t token_char[] = { - 0x00000000, /* 0000 0000 0000 0000 0000 0000 0000 0000 */ - - /* ?>=< ;:98 7654 3210 /.-, +*)( '&%$ #"! */ - 0x03ff6cf8, /* 0000 0011 1111 1111 0110 1100 1111 1000 */ - - /* _^]\ [ZYX WVUT SRQP ONML KJIH GFED CBA@ */ - 0xc7fffffe, /* 1100 0111 1111 1111 1111 1111 1111 1110 */ - - /* ~}| {zyx wvut srqp onml kjih gfed cba` */ - 0x57ffffff, /* 0101 0111 1111 1111 1111 1111 1111 1111 */ - - 0x00000000, /* 0000 0000 0000 0000 0000 0000 0000 0000 */ - 0x00000000, /* 0000 0000 0000 0000 0000 0000 0000 0000 */ - 0x00000000, /* 0000 0000 0000 0000 0000 0000 0000 0000 */ - 0x00000000, /* 0000 0000 0000 0000 0000 0000 0000 0000 */ - }; - - uint32_t in_value; - u_char ch, *p, *last, *start = 0, *end; - ngx_str_t name, value; - ngx_int_t comma_count = 0, quoted_pair_count = 0; - - name.data = 0; - name.len = 0; - value.data = 0; - value.len = 0; - - enum - { - sw_start = 0, - sw_scheme, - sw_scheme_end, - sw_lws_start, - sw_lws, - sw_param_name_start, - sw_param_name, - sw_param_value_start, - sw_param_value, - sw_param_quoted_value, - sw_param_end, - sw_error, - } state; - - ngx_str_t encoded = r->headers_in.authorization->value; - - state = sw_start; - p = encoded.data; - last = encoded.data + encoded.len; - - ch = *p++; - - while (p <= last) - { - switch (state) - { - default: - case sw_error: - return NGX_DECLINED; - - /* first char */ - case sw_start: - if (ch == CR || ch == LF || ch == ' ' || ch == '\t') - { - ch = *p++; - } - else if (token_char[ch >> 5] & (1 << (ch & 0x1f))) - { - start = p - 1; - state = sw_scheme; - } - else - { - state = sw_error; - } - break; - - case sw_scheme: - if (token_char[ch >> 5] & (1 << (ch & 0x1f))) - { - ch = *p++; - } - else if (ch == ' ') - { - end = p - 1; - state = sw_scheme_end; - - ctx->auth_scheme.data = start; - ctx->auth_scheme.len = end - start; - - if (ngx_strncasecmp(ctx->auth_scheme.data, (u_char *)"Digest", ctx->auth_scheme.len) != 0) - { - state = sw_error; - } - } - else - { - state = sw_error; - } - break; - - case sw_scheme_end: - if (ch == ' ') - { - ch = *p++; - } - else - { - state = sw_param_name_start; - } - break; - - case sw_lws_start: - comma_count = 0; - state = sw_lws; - - /* fall through */ - case sw_lws: - if (comma_count > 0 && (token_char[ch >> 5] & (1 << (ch & 0x1f)))) - { - state = sw_param_name_start; - } - else if (ch == ',') - { - comma_count++; - ch = *p++; - } - else if (ch == CR || ch == LF || ch == ' ' || ch == '\t') - { - ch = *p++; - } - else - { - state = sw_error; - } - break; - - case sw_param_name_start: - if (token_char[ch >> 5] & (1 << (ch & 0x1f))) - { - start = p - 1; - state = sw_param_name; - ch = *p++; - } - else - { - state = sw_error; - } - break; - - case sw_param_name: - if (token_char[ch >> 5] & (1 << (ch & 0x1f))) - { - ch = *p++; - } - else if (ch == '=') - { - end = p - 1; - state = sw_param_value_start; - - name.data = start; - name.len = end - start; - - ch = *p++; - } - else - { - state = sw_error; - } - break; - - case sw_param_value_start: - if (token_char[ch >> 5] & (1 << (ch & 0x1f))) - { - start = p - 1; - state = sw_param_value; - ch = *p++; - } - else if (ch == '\"') - { - start = p; - quoted_pair_count = 0; - state = sw_param_quoted_value; - ch = *p++; - } - else - { - state = sw_error; - } - break; - - case sw_param_value: - in_value = token_char[ch >> 5] & (1 << (ch & 0x1f)); - if (in_value) - { - ch = *p++; - } - - if (!in_value || p > last) - { - end = p - 1; - value.data = start; - value.len = end - start; - state = sw_param_end; - goto param_end; - } - break; - - case sw_param_quoted_value: - if (ch < 0x20 || ch == 0x7f) - { - state = sw_error; - } - else if (ch == '\\' && *p <= 0x7f) - { - quoted_pair_count++; - /* Skip the next char, even if it's a \ */ - ch = *(p += 2); - } - else if (ch == '\"') - { - end = p - 1; - ch = *p++; - value.data = start; - value.len = end - start - quoted_pair_count; - if (quoted_pair_count > 0) - { - value.data = ngx_palloc(r->pool, value.len); - u_char *d = value.data; - u_char *s = start; - for (; s < end; s++) - { - ch = *s; - if (ch == '\\') - { - /* Make sure to add the next character - * even if it's a \ - */ - s++; - if (s < end) - { - *d++ = ch; - } - continue; - } - *d++ = ch; - } - } - state = sw_param_end; - goto param_end; - } - else - { - ch = *p++; - } - break; - - param_end: - case sw_param_end: - if (ngx_strncasecmp(name.data, (u_char *)"username", name.len) == 0) - { - ctx->username = value; - } - else if (ngx_strncasecmp(name.data, (u_char *)"qop", name.len) == 0) - { - ctx->qop = value; - } - else if (ngx_strncasecmp(name.data, (u_char *)"realm", name.len) == 0) - { - ctx->realm = value; - } - else if (ngx_strncasecmp(name.data, (u_char *)"nonce", name.len) == 0) - { - ctx->nonce = value; - } - else if (ngx_strncasecmp(name.data, (u_char *)"nc", name.len) == 0) - { - ctx->nc = value; - } - else if (ngx_strncasecmp(name.data, (u_char *)"uri", name.len) == 0) - { - ctx->uri = value; - } - else if (ngx_strncasecmp(name.data, (u_char *)"cnonce", name.len) == - 0) - { - ctx->cnonce = value; - } - else if (ngx_strncasecmp(name.data, (u_char *)"response", name.len) == - 0) - { - ctx->response = value; - } - else if (ngx_strncasecmp(name.data, (u_char *)"opaque", name.len) == - 0) - { - ctx->opaque = value; - } - - state = sw_lws_start; - break; - } - } - - if (state != sw_lws_start && state != sw_lws) - { - return NGX_DECLINED; - } - - // bail out if anything but the opaque field is missing from the request - // header - if (!(ctx->username.len > 0 && ctx->qop.len > 0 && ctx->realm.len > 0 && - ctx->nonce.len > 0 && ctx->nc.len > 0 && ctx->uri.len > 0 && - ctx->cnonce.len > 0 && ctx->response.len > 0) || - ctx->nonce.len != 16) - { - return NGX_DECLINED; - } - - return NGX_OK; + if (r->headers_in.authorization == NULL) + { + return NGX_DECLINED; + } + + /* + token = 1* + separators = "(" | ")" | "<" | ">" | "@" + | "," | ";" | ":" | "\" | <"> + | "/" | "[" | "]" | "?" | "=" + | "{" | "}" | SP | HT + */ + + static uint32_t token_char[] = { + 0x00000000, /* 0000 0000 0000 0000 0000 0000 0000 0000 */ + + /* ?>=< ;:98 7654 3210 /.-, +*)( '&%$ #"! */ + 0x03ff6cf8, /* 0000 0011 1111 1111 0110 1100 1111 1000 */ + + /* _^]\ [ZYX WVUT SRQP ONML KJIH GFED CBA@ */ + 0xc7fffffe, /* 1100 0111 1111 1111 1111 1111 1111 1110 */ + + /* ~}| {zyx wvut srqp onml kjih gfed cba` */ + 0x57ffffff, /* 0101 0111 1111 1111 1111 1111 1111 1111 */ + + 0x00000000, /* 0000 0000 0000 0000 0000 0000 0000 0000 */ + 0x00000000, /* 0000 0000 0000 0000 0000 0000 0000 0000 */ + 0x00000000, /* 0000 0000 0000 0000 0000 0000 0000 0000 */ + 0x00000000, /* 0000 0000 0000 0000 0000 0000 0000 0000 */ + }; + + uint32_t in_value; + u_char ch, * p, * last, * start = 0, * end; + ngx_str_t name, value; + ngx_int_t comma_count = 0, quoted_pair_count = 0; + + name.data = 0; + name.len = 0; + value.data = 0; + value.len = 0; + + enum + { + sw_start = 0, + sw_scheme, + sw_scheme_end, + sw_lws_start, + sw_lws, + sw_param_name_start, + sw_param_name, + sw_param_value_start, + sw_param_value, + sw_param_quoted_value, + sw_param_end, + sw_error, + } state; + + ngx_str_t encoded = r->headers_in.authorization->value; + + state = sw_start; + p = encoded.data; + last = encoded.data + encoded.len; + + ch = *p++; + + while (p <= last) + { + switch (state) + { + default: + case sw_error: + return NGX_DECLINED; + + /* first char */ + case sw_start: + if (ch == CR || ch == LF || ch == ' ' || ch == '\t') + { + ch = *p++; + } + else if (token_char[ch >> 5] & (1 << (ch & 0x1f))) + { + start = p - 1; + state = sw_scheme; + } + else + { + state = sw_error; + } + break; + + case sw_scheme: + if (token_char[ch >> 5] & (1 << (ch & 0x1f))) + { + ch = *p++; + } + else if (ch == ' ') + { + end = p - 1; + state = sw_scheme_end; + + ctx->auth_scheme.data = start; + ctx->auth_scheme.len = end - start; + + if (ngx_strncasecmp(ctx->auth_scheme.data, (u_char*)"Digest", ctx->auth_scheme.len) != 0) + { + state = sw_error; + } + } + else + { + state = sw_error; + } + break; + + case sw_scheme_end: + if (ch == ' ') + { + ch = *p++; + } + else + { + state = sw_param_name_start; + } + break; + + case sw_lws_start: + comma_count = 0; + state = sw_lws; + + /* fall through */ + case sw_lws: + if (comma_count > 0 && (token_char[ch >> 5] & (1 << (ch & 0x1f)))) + { + state = sw_param_name_start; + } + else if (ch == ',') + { + comma_count++; + ch = *p++; + } + else if (ch == CR || ch == LF || ch == ' ' || ch == '\t') + { + ch = *p++; + } + else + { + state = sw_error; + } + break; + + case sw_param_name_start: + if (token_char[ch >> 5] & (1 << (ch & 0x1f))) + { + start = p - 1; + state = sw_param_name; + ch = *p++; + } + else + { + state = sw_error; + } + break; + + case sw_param_name: + if (token_char[ch >> 5] & (1 << (ch & 0x1f))) + { + ch = *p++; + } + else if (ch == '=') + { + end = p - 1; + state = sw_param_value_start; + + name.data = start; + name.len = end - start; + + ch = *p++; + } + else + { + state = sw_error; + } + break; + + case sw_param_value_start: + if (token_char[ch >> 5] & (1 << (ch & 0x1f))) + { + start = p - 1; + state = sw_param_value; + ch = *p++; + } + else if (ch == '\"') + { + start = p; + quoted_pair_count = 0; + state = sw_param_quoted_value; + ch = *p++; + } + else + { + state = sw_error; + } + break; + + case sw_param_value: + in_value = token_char[ch >> 5] & (1 << (ch & 0x1f)); + if (in_value) + { + ch = *p++; + } + + if (!in_value || p > last) + { + end = p - 1; + value.data = start; + value.len = end - start; + state = sw_param_end; + goto param_end; + } + break; + + case sw_param_quoted_value: + if (ch < 0x20 || ch == 0x7f) + { + state = sw_error; + } + else if (ch == '\\' && *p <= 0x7f) + { + quoted_pair_count++; + /* Skip the next char, even if it's a \ */ + ch = *(p += 2); + } + else if (ch == '\"') + { + end = p - 1; + ch = *p++; + value.data = start; + value.len = end - start - quoted_pair_count; + if (quoted_pair_count > 0) + { + value.data = ngx_palloc(r->pool, value.len); + u_char* d = value.data; + u_char* s = start; + for (; s < end; s++) + { + ch = *s; + if (ch == '\\') + { + /* Make sure to add the next character + * even if it's a \ + */ + s++; + if (s < end) + { + *d++ = ch; + } + continue; + } + *d++ = ch; + } + } + state = sw_param_end; + goto param_end; + } + else + { + ch = *p++; + } + break; + + param_end: + case sw_param_end: + if (ngx_strncasecmp(name.data, (u_char*)"username", name.len) == 0) + { + ctx->username = value; + } + else if (ngx_strncasecmp(name.data, (u_char*)"qop", name.len) == 0) + { + ctx->qop = value; + } + else if (ngx_strncasecmp(name.data, (u_char*)"realm", name.len) == 0) + { + ctx->realm = value; + } + else if (ngx_strncasecmp(name.data, (u_char*)"nonce", name.len) == 0) + { + ctx->nonce = value; + } + else if (ngx_strncasecmp(name.data, (u_char*)"nc", name.len) == 0) + { + ctx->nc = value; + } + else if (ngx_strncasecmp(name.data, (u_char*)"uri", name.len) == 0) + { + ctx->uri = value; + } + else if (ngx_strncasecmp(name.data, (u_char*)"cnonce", name.len) == + 0) + { + ctx->cnonce = value; + } + else if (ngx_strncasecmp(name.data, (u_char*)"response", name.len) == + 0) + { + ctx->response = value; + } + else if (ngx_strncasecmp(name.data, (u_char*)"opaque", name.len) == + 0) + { + ctx->opaque = value; + } + + state = sw_lws_start; + break; + } + } + + if (state != sw_lws_start && state != sw_lws) + { + return NGX_DECLINED; + } + + // bail out if anything but the opaque field is missing from the request + // header + if (!(ctx->username.len > 0 && ctx->qop.len > 0 && ctx->realm.len > 0 && + ctx->nonce.len > 0 && ctx->nc.len > 0 && ctx->uri.len > 0 && + ctx->cnonce.len > 0 && ctx->response.len > 0) || + ctx->nonce.len != 16) + { + return NGX_DECLINED; + } + + return NGX_OK; } static ngx_int_t -ngx_http_auth_digest_verify_user(ngx_http_request_t *r, - ngx_http_auth_digest_cred_t *fields, - ngx_str_t *line) +ngx_http_auth_digest_verify_user(ngx_http_request_t* r, + ngx_http_auth_digest_cred_t* fields, + ngx_str_t* line) { - ngx_uint_t i, from, nomatch; - enum - { - sw_login, - sw_ha1, - sw_realm - } state; - - state = sw_login; - from = 0; - nomatch = 0; - - // step through a single line (of the passwd file), matching the username and - // realm - // character-by-character against the request's Authorization header fields - u_char *buf = line->data; - for (i = 0; i <= line->len; i++) - { - u_char ch = buf[i]; - - switch (state) - { - case sw_login: - if (ch == '#') - nomatch = 1; - if (ch == ':') - { - if (fields->username.len != i) - nomatch = 1; - state = sw_realm; - from = i + 1; - } - else if (i > fields->username.len || ch != fields->username.data[i]) - { - nomatch = 1; - } - break; - - case sw_realm: - if (ch == '#') - nomatch = 1; - if (ch == ':') - { - if (fields->realm.len != i - from) - nomatch = 1; - state = sw_ha1; - from = i + 1; - } - else if (ch != fields->realm.data[i - from]) - { - nomatch = 1; - } - break; - - case sw_ha1: - if (ch == '\0' || ch == ':' || ch == '#' || ch == CR || ch == LF) - { - if (i - from != 32) - nomatch = 1; - } - break; - } - } - - if (nomatch) - { - return NGX_HTTP_AUTH_DIGEST_USERNOTFOUND; - } - - return ngx_http_auth_digest_verify_hash(r, fields, &buf[from]); + ngx_uint_t i, from, nomatch; + enum + { + sw_login, + sw_ha1, + sw_realm + } state; + + state = sw_login; + from = 0; + nomatch = 0; + + // step through a single line (of the passwd file), matching the username and + // realm + // character-by-character against the request's Authorization header fields + u_char* buf = line->data; + for (i = 0; i <= line->len; i++) + { + u_char ch = buf[i]; + + switch (state) + { + case sw_login: + if (ch == '#') + nomatch = 1; + if (ch == ':') + { + if (fields->username.len != i) + nomatch = 1; + state = sw_realm; + from = i + 1; + } + else if (i > fields->username.len || ch != fields->username.data[i]) + { + nomatch = 1; + } + break; + + case sw_realm: + if (ch == '#') + nomatch = 1; + if (ch == ':') + { + if (fields->realm.len != i - from) + nomatch = 1; + state = sw_ha1; + from = i + 1; + } + else if (ch != fields->realm.data[i - from]) + { + nomatch = 1; + } + break; + + case sw_ha1: + if (ch == '\0' || ch == ':' || ch == '#' || ch == CR || ch == LF) + { + if (i - from != 32) + nomatch = 1; + } + break; + } + } + + if (nomatch) + { + return NGX_HTTP_AUTH_DIGEST_USERNOTFOUND; + } + + return ngx_http_auth_digest_verify_hash(r, fields, &buf[from]); } static ngx_int_t -ngx_http_auth_digest_verify_hash(ngx_http_request_t *r, - ngx_http_auth_digest_cred_t *fields, - u_char *hashed_pw) +ngx_http_auth_digest_verify_hash(ngx_http_request_t* r, + ngx_http_auth_digest_cred_t* fields, + u_char* hashed_pw) { - u_char *p; - ngx_str_t http_method; - ngx_str_t HA1, HA2, ha2_key; - ngx_str_t digest, digest_key; - ngx_md5_t md5; - u_char hash[16]; - - // The .net Http library sends the incorrect URI as part of the Authorization - // response. Instead of the complete URI including the query parameters it - // sends only the basic URI without the query parameters. It also uses this - // value in the calculations. - // To be compatible with the .net library the following change is made to this - // module: - // - Compare the URI in the Authorization (A-URI) with the request URI (R-URI). - // - If A-URI and R-URI are identical verify is executed. - // - If A-URI and R-URI are identical up to the '?' verify is executed - // - Otherwise the check is not executed and authorization is declined - if (!((r->unparsed_uri.len == fields->uri.len) && - (ngx_strncmp(r->unparsed_uri.data, fields->uri.data, fields->uri.len) == 0))) - { - if (!((r->unparsed_uri.len > fields->uri.len) && - (ngx_strncmp(r->unparsed_uri.data, fields->uri.data, fields->uri.len) == 0) && - (r->unparsed_uri.data[fields->uri.len] == '?'))) - { - return NGX_DECLINED; - } - } - - // the hashing scheme: - // digest: - // MD5(MD5(username:realm:password):nonce:nc:cnonce:qop:MD5(method:uri)) - // ^- HA1 ^- HA2 - // verify: fields->response == - // MD5($hashed_pw:nonce:nc:cnonce:qop:MD5(method:uri)) - - // ha1 was precalculated and saved to the passwd file: - // md5(username:realm:password) - HA1.len = 33; - HA1.data = ngx_pcalloc(r->pool, HA1.len); - p = ngx_cpymem(HA1.data, hashed_pw, 32); - - // calculate ha2: md5(method:uri) - http_method.len = r->method_name.len + 1; - http_method.data = ngx_pcalloc(r->pool, http_method.len); - if (http_method.data == NULL) - return NGX_HTTP_INTERNAL_SERVER_ERROR; - p = ngx_cpymem(http_method.data, r->method_name.data, r->method_name.len); - - ha2_key.len = http_method.len + fields->uri.len + 1; - ha2_key.data = ngx_pcalloc(r->pool, ha2_key.len); - if (ha2_key.data == NULL) - return NGX_HTTP_INTERNAL_SERVER_ERROR; - p = ngx_cpymem(ha2_key.data, http_method.data, http_method.len - 1); - *p++ = ':'; - p = ngx_cpymem(p, fields->uri.data, fields->uri.len); - - HA2.len = 33; - HA2.data = ngx_pcalloc(r->pool, HA2.len); - ngx_md5_init(&md5); - ngx_md5_update(&md5, ha2_key.data, ha2_key.len - 1); - ngx_md5_final(hash, &md5); - ngx_hex_dump(HA2.data, hash, 16); - - // calculate digest: md5(ha1:nonce:nc:cnonce:qop:ha2) - digest_key.len = HA1.len - 1 + fields->nonce.len + fields->nc.len + - fields->cnonce.len + fields->qop.len + HA2.len - 1 + 5 + 1; - - digest_key.data = ngx_pcalloc(r->pool, digest_key.len); - - if (digest_key.data == NULL) - { - return NGX_HTTP_INTERNAL_SERVER_ERROR; - } - - p = ngx_cpymem(digest_key.data, HA1.data, HA1.len - 1); - *p++ = ':'; - p = ngx_cpymem(p, fields->nonce.data, fields->nonce.len); - *p++ = ':'; - p = ngx_cpymem(p, fields->nc.data, fields->nc.len); - *p++ = ':'; - p = ngx_cpymem(p, fields->cnonce.data, fields->cnonce.len); - *p++ = ':'; - p = ngx_cpymem(p, fields->qop.data, fields->qop.len); - *p++ = ':'; - p = ngx_cpymem(p, HA2.data, HA2.len - 1); - - digest.len = 33; - digest.data = ngx_pcalloc(r->pool, 33); - - if (digest.data == NULL) - { - return NGX_HTTP_INTERNAL_SERVER_ERROR; - } - - ngx_md5_init(&md5); - ngx_md5_update(&md5, digest_key.data, digest_key.len - 1); - ngx_md5_final(hash, &md5); - ngx_hex_dump(digest.data, hash, 16); - - // compare the hash of the full digest string to the response field of the - // auth header - // and bail out if they don't match - if (fields->response.len != digest.len - 1 || - ngx_memcmp(digest.data, fields->response.data, fields->response.len) != 0) - { - return NGX_DECLINED; - } - - ngx_http_auth_digest_nonce_t nonce; - ngx_uint_t key; - ngx_http_auth_digest_node_t *found; - ngx_slab_pool_t *shpool; - ngx_http_auth_digest_loc_conf_t *alcf; - ngx_table_elt_t *info_header; - ngx_str_t hkey, hval; - - shpool = (ngx_slab_pool_t *)ngx_http_auth_digest_shm_zone->shm.addr; - alcf = ngx_http_get_module_loc_conf(r, ngx_http_auth_digest_module); - nonce.rnd = ngx_hextoi(fields->nonce.data, 8); - nonce.t = ngx_hextoi(&fields->nonce.data[8], 8); - key = ngx_crc32_short((u_char *)&nonce.rnd, sizeof nonce.rnd) ^ - ngx_crc32_short((u_char *)&nonce.t, sizeof(nonce.t)); - - int nc = ngx_hextoi(fields->nc.data, fields->nc.len); - if (nc < 0 || nc >= alcf->replays) - { - fields->stale = 1; - return NGX_DECLINED; - } - - // make sure nonce and nc are both valid - ngx_shmtx_lock(&shpool->mutex); - found = (ngx_http_auth_digest_node_t *)ngx_http_auth_digest_rbtree_find( - key, ngx_http_auth_digest_rbtree->root, - ngx_http_auth_digest_rbtree->sentinel); - if (found != NULL) - { - if (found->expires <= ngx_time()) - { - fields->stale = 1; - goto invalid; - } - if (!ngx_bitvector_test(found->nc, nc)) - { - goto invalid; - } - if (ngx_bitvector_test(found->nc, 0)) - { - // if this is the first use of this nonce, switch the expiration time from - // the timeout - // param to now+expires. using the 0th element of the nc vector to flag - // this... - ngx_bitvector_set(found->nc, 0); - found->expires = ngx_time() + alcf->expires; - found->drop_time = ngx_time() + alcf->drop_time; - } - - // mark this nc as ‘used’ to prevent replays - ngx_bitvector_set(found->nc, nc); - - // todo: if the bitvector is now ‘full’, could preemptively expire the node - // from the rbtree - // ngx_rbtree_delete(ngx_http_auth_digest_rbtree, found); - // ngx_slab_free_locked(shpool, found); - - ngx_shmtx_unlock(&shpool->mutex); - - // recalculate the digest with a modified HA2 value (for rspauth) and emit - // the - // Authentication-Info header - ngx_memset(ha2_key.data, 0, ha2_key.len); - p = ngx_snprintf(ha2_key.data, 1 + fields->uri.len, ":%s", - fields->uri.data); - - ngx_memset(HA2.data, 0, HA2.len); - ngx_md5_init(&md5); - ngx_md5_update(&md5, ha2_key.data, 1 + fields->uri.len); - ngx_md5_final(hash, &md5); - ngx_hex_dump(HA2.data, hash, 16); - - ngx_memset(digest_key.data, 0, digest_key.len); - p = ngx_cpymem(digest_key.data, HA1.data, HA1.len - 1); - *p++ = ':'; - p = ngx_cpymem(p, fields->nonce.data, fields->nonce.len); - *p++ = ':'; - p = ngx_cpymem(p, fields->nc.data, fields->nc.len); - *p++ = ':'; - p = ngx_cpymem(p, fields->cnonce.data, fields->cnonce.len); - *p++ = ':'; - p = ngx_cpymem(p, fields->qop.data, fields->qop.len); - *p++ = ':'; - p = ngx_cpymem(p, HA2.data, HA2.len - 1); - - ngx_md5_init(&md5); - ngx_md5_update(&md5, digest_key.data, digest_key.len - 1); - ngx_md5_final(hash, &md5); - ngx_hex_dump(digest.data, hash, 16); - - ngx_str_set(&hkey, "Authentication-Info"); - // sizeof() includes the null terminator, and digest.len also counts its - // null terminator - hval.len = sizeof("qop=\"auth\", rspauth=\"\", cnonce=\"\", nc=") + - fields->cnonce.len + fields->nc.len + digest.len - 2; - hval.data = ngx_pcalloc(r->pool, hval.len + 1); - if (hval.data == NULL) - return NGX_HTTP_INTERNAL_SERVER_ERROR; - p = ngx_snprintf(hval.data, hval.len, - "qop=\"auth\", rspauth=\"%*s\", cnonce=\"%*s\", nc=%*s", - digest.len - 1, digest.data, fields->cnonce.len, - fields->cnonce.data, fields->nc.len, fields->nc.data); - info_header = ngx_list_push(&r->headers_out.headers); - if (info_header == NULL) - return NGX_HTTP_INTERNAL_SERVER_ERROR; - info_header->key = hkey; - info_header->value = hval; - info_header->hash = 1; - return NGX_OK; - } - else - { - invalid: - // nonce is invalid/expired or client reused an nc value. suspicious... - ngx_shmtx_unlock(&shpool->mutex); - return NGX_DECLINED; - } + u_char* p; + ngx_str_t http_method; + ngx_str_t HA1, HA2, ha2_key; + ngx_str_t digest, digest_key; + ngx_md5_t md5; + u_char hash[16]; + + // The .net Http library sends the incorrect URI as part of the Authorization + // response. Instead of the complete URI including the query parameters it + // sends only the basic URI without the query parameters. It also uses this + // value in the calculations. + // To be compatible with the .net library the following change is made to this + // module: + // - Compare the URI in the Authorization (A-URI) with the request URI (R-URI). + // - If A-URI and R-URI are identical verify is executed. + // - If A-URI and R-URI are identical up to the '?' verify is executed + // - Otherwise the check is not executed and authorization is declined + if (!((r->unparsed_uri.len == fields->uri.len) && + (ngx_strncmp(r->unparsed_uri.data, fields->uri.data, fields->uri.len) == 0))) + { + if (!((r->unparsed_uri.len > fields->uri.len) && + (ngx_strncmp(r->unparsed_uri.data, fields->uri.data, fields->uri.len) == 0) && + (r->unparsed_uri.data[fields->uri.len] == '?'))) + { + return NGX_DECLINED; + } + } + + // the hashing scheme: + // digest: + // MD5(MD5(username:realm:password):nonce:nc:cnonce:qop:MD5(method:uri)) + // ^- HA1 ^- HA2 + // verify: fields->response == + // MD5($hashed_pw:nonce:nc:cnonce:qop:MD5(method:uri)) + + // ha1 was precalculated and saved to the passwd file: + // md5(username:realm:password) + HA1.len = 33; + HA1.data = ngx_pcalloc(r->pool, HA1.len); + p = ngx_cpymem(HA1.data, hashed_pw, 32); + + // calculate ha2: md5(method:uri) + http_method.len = r->method_name.len + 1; + http_method.data = ngx_pcalloc(r->pool, http_method.len); + if (http_method.data == NULL) + return NGX_HTTP_INTERNAL_SERVER_ERROR; + p = ngx_cpymem(http_method.data, r->method_name.data, r->method_name.len); + + ha2_key.len = http_method.len + fields->uri.len + 1; + ha2_key.data = ngx_pcalloc(r->pool, ha2_key.len); + if (ha2_key.data == NULL) + return NGX_HTTP_INTERNAL_SERVER_ERROR; + p = ngx_cpymem(ha2_key.data, http_method.data, http_method.len - 1); + *p++ = ':'; + p = ngx_cpymem(p, fields->uri.data, fields->uri.len); + + HA2.len = 33; + HA2.data = ngx_pcalloc(r->pool, HA2.len); + ngx_md5_init(&md5); + ngx_md5_update(&md5, ha2_key.data, ha2_key.len - 1); + ngx_md5_final(hash, &md5); + ngx_hex_dump(HA2.data, hash, 16); + + // calculate digest: md5(ha1:nonce:nc:cnonce:qop:ha2) + digest_key.len = HA1.len - 1 + fields->nonce.len + fields->nc.len + + fields->cnonce.len + fields->qop.len + HA2.len - 1 + 5 + 1; + + digest_key.data = ngx_pcalloc(r->pool, digest_key.len); + + if (digest_key.data == NULL) + { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + p = ngx_cpymem(digest_key.data, HA1.data, HA1.len - 1); + *p++ = ':'; + p = ngx_cpymem(p, fields->nonce.data, fields->nonce.len); + *p++ = ':'; + p = ngx_cpymem(p, fields->nc.data, fields->nc.len); + *p++ = ':'; + p = ngx_cpymem(p, fields->cnonce.data, fields->cnonce.len); + *p++ = ':'; + p = ngx_cpymem(p, fields->qop.data, fields->qop.len); + *p++ = ':'; + p = ngx_cpymem(p, HA2.data, HA2.len - 1); + + digest.len = 33; + digest.data = ngx_pcalloc(r->pool, 33); + + if (digest.data == NULL) + { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + ngx_md5_init(&md5); + ngx_md5_update(&md5, digest_key.data, digest_key.len - 1); + ngx_md5_final(hash, &md5); + ngx_hex_dump(digest.data, hash, 16); + + // compare the hash of the full digest string to the response field of the + // auth header + // and bail out if they don't match + if (fields->response.len != digest.len - 1 || + ngx_memcmp(digest.data, fields->response.data, fields->response.len) != 0) + { + return NGX_DECLINED; + } + + ngx_http_auth_digest_nonce_t nonce; + ngx_uint_t key; + ngx_http_auth_digest_node_t* found; + ngx_slab_pool_t* shpool; + ngx_http_auth_digest_loc_conf_t* alcf; + ngx_table_elt_t* info_header; + ngx_str_t hkey, hval; + + shpool = (ngx_slab_pool_t*)ngx_http_auth_digest_shm_zone->shm.addr; + alcf = ngx_http_get_module_loc_conf(r, ngx_http_auth_digest_module); + nonce.rnd = ngx_hextoi(fields->nonce.data, 8); + nonce.t = ngx_hextoi(&fields->nonce.data[8], 8); + key = ngx_crc32_short((u_char*)&nonce.rnd, sizeof nonce.rnd) ^ + ngx_crc32_short((u_char*)&nonce.t, sizeof(nonce.t)); + + int nc = ngx_hextoi(fields->nc.data, fields->nc.len); + if (nc < 0 || nc >= alcf->replays) + { + fields->stale = 1; + return NGX_DECLINED; + } + + // make sure nonce and nc are both valid + ngx_shmtx_lock(&shpool->mutex); + found = (ngx_http_auth_digest_node_t*)ngx_http_auth_digest_rbtree_find( + key, ngx_http_auth_digest_rbtree->root, + ngx_http_auth_digest_rbtree->sentinel); + if (found != NULL) + { + if (found->expires <= ngx_time()) + { + fields->stale = 1; + goto invalid; + } + if (!ngx_bitvector_test(found->nc, nc)) + { + goto invalid; + } + if (ngx_bitvector_test(found->nc, 0)) + { + // if this is the first use of this nonce, switch the expiration time from + // the timeout + // param to now+expires. using the 0th element of the nc vector to flag + // this... + ngx_bitvector_set(found->nc, 0); + found->expires = ngx_time() + alcf->expires; + found->drop_time = ngx_time() + alcf->drop_time; + } + + // mark this nc as ‘used’ to prevent replays + ngx_bitvector_set(found->nc, nc); + + // todo: if the bitvector is now ‘full’, could preemptively expire the node + // from the rbtree + // ngx_rbtree_delete(ngx_http_auth_digest_rbtree, found); + // ngx_slab_free_locked(shpool, found); + + ngx_shmtx_unlock(&shpool->mutex); + + // recalculate the digest with a modified HA2 value (for rspauth) and emit + // the + // Authentication-Info header + ngx_memset(ha2_key.data, 0, ha2_key.len); + p = ngx_snprintf(ha2_key.data, 1 + fields->uri.len, ":%s", + fields->uri.data); + + ngx_memset(HA2.data, 0, HA2.len); + ngx_md5_init(&md5); + ngx_md5_update(&md5, ha2_key.data, 1 + fields->uri.len); + ngx_md5_final(hash, &md5); + ngx_hex_dump(HA2.data, hash, 16); + + ngx_memset(digest_key.data, 0, digest_key.len); + p = ngx_cpymem(digest_key.data, HA1.data, HA1.len - 1); + *p++ = ':'; + p = ngx_cpymem(p, fields->nonce.data, fields->nonce.len); + *p++ = ':'; + p = ngx_cpymem(p, fields->nc.data, fields->nc.len); + *p++ = ':'; + p = ngx_cpymem(p, fields->cnonce.data, fields->cnonce.len); + *p++ = ':'; + p = ngx_cpymem(p, fields->qop.data, fields->qop.len); + *p++ = ':'; + p = ngx_cpymem(p, HA2.data, HA2.len - 1); + + ngx_md5_init(&md5); + ngx_md5_update(&md5, digest_key.data, digest_key.len - 1); + ngx_md5_final(hash, &md5); + ngx_hex_dump(digest.data, hash, 16); + + ngx_str_set(&hkey, "Authentication-Info"); + // sizeof() includes the null terminator, and digest.len also counts its + // null terminator + hval.len = sizeof("qop=\"auth\", rspauth=\"\", cnonce=\"\", nc=") + + fields->cnonce.len + fields->nc.len + digest.len - 2; + hval.data = ngx_pcalloc(r->pool, hval.len + 1); + if (hval.data == NULL) + return NGX_HTTP_INTERNAL_SERVER_ERROR; + p = ngx_snprintf(hval.data, hval.len, + "qop=\"auth\", rspauth=\"%*s\", cnonce=\"%*s\", nc=%*s", + digest.len - 1, digest.data, fields->cnonce.len, + fields->cnonce.data, fields->nc.len, fields->nc.data); + info_header = ngx_list_push(&r->headers_out.headers); + if (info_header == NULL) + return NGX_HTTP_INTERNAL_SERVER_ERROR; + info_header->key = hkey; + info_header->value = hval; + info_header->hash = 1; + return NGX_OK; + } + else + { + invalid: + // nonce is invalid/expired or client reused an nc value. suspicious... + ngx_shmtx_unlock(&shpool->mutex); + return NGX_DECLINED; + } } -static ngx_int_t ngx_http_auth_digest_send_challenge(ngx_http_request_t *r, - ngx_str_t *realm, - ngx_uint_t is_stale) +static ngx_int_t ngx_http_auth_digest_send_challenge(ngx_http_request_t* r, + ngx_str_t* realm, + ngx_uint_t is_stale) { - ngx_str_t challenge; - u_char *p; - size_t realm_len = strnlen((const char *)realm->data, realm->len); - - r->headers_out.www_authenticate = ngx_list_push(&r->headers_out.headers); - if (r->headers_out.www_authenticate == NULL) - { - return NGX_HTTP_INTERNAL_SERVER_ERROR; - } - - r->headers_out.www_authenticate->hash = 1; - ngx_str_set(&r->headers_out.www_authenticate->key, "WWW-Authenticate"); - - challenge.len = - sizeof("Digest algorithm=\"MD5\", qop=\"auth\", realm=\"\", nonce=\"\"") - - 1 + realm_len + 16; - if (is_stale) - { - challenge.len += sizeof(", stale=\"true\"") - 1; - } - challenge.data = ngx_pnalloc(r->pool, challenge.len); - if (challenge.data == NULL) - { - return NGX_HTTP_INTERNAL_SERVER_ERROR; - } - - ngx_http_auth_digest_nonce_t nonce; - nonce = ngx_http_auth_digest_next_nonce(r); - if (nonce.t == 0 && nonce.rnd == 0) - { - // oom error when allocating nonce session in rbtree - return NGX_HTTP_SERVICE_UNAVAILABLE; - } - - p = ngx_cpymem( - challenge.data, "Digest algorithm=\"MD5\", qop=\"auth\", realm=\"", - sizeof("Digest algorithm=\"MD5\", qop=\"auth\", realm=\"") - 1); - p = ngx_cpymem(p, realm->data, realm_len); - p = ngx_cpymem(p, "\", nonce=\"", sizeof("\", nonce=\"") - 1); - p = ngx_sprintf(p, "%08xl%08xl", nonce.rnd, nonce.t); - - if (is_stale) - { - p = ngx_cpymem(p, "\", stale=\"true\"", sizeof("\", stale=\"true\"")); - } - else - { - p = ngx_cpymem(p, "\"", sizeof("\"")); - } - r->headers_out.www_authenticate->value = challenge; - - return NGX_HTTP_UNAUTHORIZED; + ngx_str_t challenge; + u_char* p; + size_t realm_len = strnlen((const char*)realm->data, realm->len); + + r->headers_out.www_authenticate = ngx_list_push(&r->headers_out.headers); + if (r->headers_out.www_authenticate == NULL) + { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + r->headers_out.www_authenticate->hash = 1; + ngx_str_set(&r->headers_out.www_authenticate->key, "WWW-Authenticate"); + + challenge.len = + sizeof("Digest algorithm=\"MD5\", qop=\"auth\", realm=\"\", nonce=\"\"") - + 1 + realm_len + 16; + if (is_stale) + { + challenge.len += sizeof(", stale=\"true\"") - 1; + } + challenge.data = ngx_pnalloc(r->pool, challenge.len); + if (challenge.data == NULL) + { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + ngx_http_auth_digest_nonce_t nonce = ngx_http_auth_digest_next_nonce(r); + if (nonce.t == 0 && nonce.rnd == 0) + { + // oom error when allocating nonce session in rbtree + return NGX_HTTP_SERVICE_UNAVAILABLE; + } + + p = ngx_cpymem( + challenge.data, "Digest algorithm=\"MD5\", qop=\"auth\", realm=\"", + sizeof("Digest algorithm=\"MD5\", qop=\"auth\", realm=\"") - 1); + + p = ngx_cpymem(p, realm->data, realm_len); + p = ngx_cpymem(p, "\", nonce=\"", sizeof("\", nonce=\"") - 1); + p = ngx_sprintf(p, "%08xl%08xl", nonce.rnd, nonce.t); + + if (is_stale) + { + p = ngx_cpymem(p, "\", stale=\"true\"", sizeof("\", stale=\"true\"")); + } + else + { + p = ngx_cpymem(p, "\"", sizeof("\"")); + } + r->headers_out.www_authenticate->value = challenge; + + return NGX_HTTP_UNAUTHORIZED; } -static void ngx_http_auth_digest_close(ngx_file_t *file) +static void ngx_http_auth_digest_close(ngx_file_t* file) { - if (ngx_close_file(file->fd) == NGX_FILE_ERROR) - { - ngx_log_error(NGX_LOG_ALERT, file->log, ngx_errno, - ngx_close_file_n " \"%s\" failed", file->name.data); - } + if (ngx_close_file(file->fd) == NGX_FILE_ERROR) + { + ngx_log_error(NGX_LOG_ALERT, file->log, ngx_errno, + ngx_close_file_n " \"%s\" failed", file->name.data); + } } -static char *ngx_http_auth_digest_set_user_file(ngx_conf_t *cf, - ngx_command_t *cmd, - void *conf) +static char* ngx_http_auth_digest_set_user_file(ngx_conf_t* cf, + ngx_command_t* cmd, + void* conf) { - ngx_http_auth_digest_loc_conf_t *alcf = conf; + ngx_http_auth_digest_loc_conf_t* alcf = conf; - ngx_str_t *value; - ngx_http_compile_complex_value_t ccv; + ngx_str_t* value; + ngx_http_compile_complex_value_t ccv; - if (alcf->user_file.value.len) - { - return "is duplicate"; - } + if (alcf->user_file.value.len) + { + return "is duplicate"; + } - value = cf->args->elts; + value = cf->args->elts; - ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t)); + ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t)); - ccv.cf = cf; - ccv.value = &value[1]; - ccv.complex_value = &alcf->user_file; - ccv.zero = 1; - ccv.conf_prefix = 1; + ccv.cf = cf; + ccv.value = &value[1]; + ccv.complex_value = &alcf->user_file; + ccv.zero = 1; + ccv.conf_prefix = 1; - if (ngx_http_compile_complex_value(&ccv) != NGX_OK) - { - return NGX_CONF_ERROR; - } + if (ngx_http_compile_complex_value(&ccv) != NGX_OK) + { + return NGX_CONF_ERROR; + } - return NGX_CONF_OK; + return NGX_CONF_OK; } -static char *ngx_http_auth_digest_set_realm(ngx_conf_t *cf, ngx_command_t *cmd, - void *conf) +static char* ngx_http_auth_digest_set_realm(ngx_conf_t* cf, ngx_command_t* cmd, + void* conf) { - ngx_http_auth_digest_loc_conf_t *alcf = conf; + ngx_http_auth_digest_loc_conf_t* alcf = conf; - ngx_str_t *value; - ngx_http_compile_complex_value_t ccv; + ngx_str_t* value; + ngx_http_compile_complex_value_t ccv; - if (alcf->realm.value.len) - { - return "is duplicate"; - } + if (alcf->realm.value.len) + { + return "is duplicate"; + } - value = cf->args->elts; + value = cf->args->elts; - ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t)); + ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t)); - ccv.cf = cf; - ccv.value = &value[1]; - ccv.complex_value = &alcf->realm; - ccv.zero = 1; - ccv.conf_prefix = 0; - ccv.root_prefix = 0; + ccv.cf = cf; + ccv.value = &value[1]; + ccv.complex_value = &alcf->realm; + ccv.zero = 1; + ccv.conf_prefix = 0; + ccv.root_prefix = 0; - if (ngx_http_compile_complex_value(&ccv) != NGX_OK) - { - return NGX_CONF_ERROR; - } + if (ngx_http_compile_complex_value(&ccv) != NGX_OK) + { + return NGX_CONF_ERROR; + } - return NGX_CONF_OK; + return NGX_CONF_OK; } -static char *ngx_http_auth_digest_set_shm_size(ngx_conf_t *cf, - ngx_command_t *cmd, void *conf) +static char* ngx_http_auth_digest_set_shm_size(ngx_conf_t* cf, + ngx_command_t* cmd, void* conf) { - ssize_t new_shm_size; - ngx_str_t *value; - - value = cf->args->elts; - - new_shm_size = ngx_parse_size(&value[1]); - if (new_shm_size == NGX_ERROR) - { - ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "Invalid memory area size `%V'", - &value[1]); - return NGX_CONF_ERROR; - } - - new_shm_size = ngx_align(new_shm_size, ngx_pagesize); - - if (new_shm_size < 8 * (ssize_t)ngx_pagesize) - { - ngx_conf_log_error(NGX_LOG_WARN, cf, 0, - "The auth_digest_shm_size value must be at least %udKiB", - (8 * ngx_pagesize) >> 10); - new_shm_size = 8 * ngx_pagesize; - } - - if (ngx_http_auth_digest_shm_size && - ngx_http_auth_digest_shm_size != (ngx_uint_t)new_shm_size) - { - ngx_conf_log_error(NGX_LOG_WARN, cf, 0, "Cannot change memory area size without restart, ignoring change"); - } - else - { - ngx_http_auth_digest_shm_size = new_shm_size; - } - ngx_conf_log_error(NGX_LOG_DEBUG, cf, 0, "Using %udKiB of shared memory for auth_digest", new_shm_size >> 10); - return NGX_CONF_OK; + ssize_t new_shm_size; + ngx_str_t* value; + + value = cf->args->elts; + + new_shm_size = ngx_parse_size(&value[1]); + if (new_shm_size == NGX_ERROR) + { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "Invalid memory area size `%V'", + &value[1]); + return NGX_CONF_ERROR; + } + + new_shm_size = ngx_align(new_shm_size, ngx_pagesize); + + if (new_shm_size < 8 * (ssize_t)ngx_pagesize) + { + ngx_conf_log_error(NGX_LOG_WARN, cf, 0, + "The auth_digest_shm_size value must be at least %udKiB", + (8 * ngx_pagesize) >> 10); + new_shm_size = 8 * ngx_pagesize; + } + + if (ngx_http_auth_digest_shm_size && + ngx_http_auth_digest_shm_size != (ngx_uint_t)new_shm_size) + { + ngx_conf_log_error(NGX_LOG_WARN, cf, 0, "Cannot change memory area size without restart, ignoring change"); + } + else + { + ngx_http_auth_digest_shm_size = new_shm_size; + } + ngx_conf_log_error(NGX_LOG_DEBUG, cf, 0, "Using %udKiB of shared memory for auth_digest", new_shm_size >> 10); + return NGX_CONF_OK; } -static ngx_int_t ngx_http_auth_digest_init_shm_zone(ngx_shm_zone_t *shm_zone, void *data) +static ngx_int_t ngx_http_auth_digest_init_shm_zone(ngx_shm_zone_t* shm_zone, void* data) { - ngx_slab_pool_t *shpool; - ngx_rbtree_t *tree; - ngx_rbtree_node_t *sentinel; - ngx_atomic_t *lock; - if (data) - { - shm_zone->data = data; - return NGX_OK; - } - - shpool = (ngx_slab_pool_t *)shm_zone->shm.addr; - tree = ngx_slab_alloc(shpool, sizeof *tree); - if (tree == NULL) - { - return NGX_ERROR; - } - - sentinel = ngx_slab_alloc(shpool, sizeof *sentinel); - if (sentinel == NULL) - { - return NGX_ERROR; - } - - ngx_rbtree_init(tree, sentinel, ngx_http_auth_digest_rbtree_insert); - shm_zone->data = tree; - ngx_http_auth_digest_rbtree = tree; - - tree = ngx_slab_alloc(shpool, sizeof *tree); - if (tree == NULL) - { - return NGX_ERROR; - } - - sentinel = ngx_slab_alloc(shpool, sizeof *sentinel); - if (sentinel == NULL) - { - return NGX_ERROR; - } - - ngx_rbtree_init(tree, sentinel, ngx_http_auth_digest_ev_rbtree_insert); - ngx_http_auth_digest_ev_rbtree = tree; - - lock = ngx_slab_alloc(shpool, sizeof(ngx_atomic_t)); - if (lock == NULL) - { - return NGX_ERROR; - } - ngx_http_auth_digest_cleanup_lock = lock; - - return NGX_OK; + ngx_slab_pool_t* shpool; + ngx_rbtree_t* tree; + ngx_rbtree_node_t* sentinel; + ngx_atomic_t* lock; + if (data) + { + shm_zone->data = data; + return NGX_OK; + } + + shpool = (ngx_slab_pool_t*)shm_zone->shm.addr; + tree = ngx_slab_alloc(shpool, sizeof * tree); + if (tree == NULL) + { + return NGX_ERROR; + } + + sentinel = ngx_slab_alloc(shpool, sizeof * sentinel); + if (sentinel == NULL) + { + return NGX_ERROR; + } + + ngx_rbtree_init(tree, sentinel, ngx_http_auth_digest_rbtree_insert); + shm_zone->data = tree; + ngx_http_auth_digest_rbtree = tree; + + tree = ngx_slab_alloc(shpool, sizeof * tree); + if (tree == NULL) + { + return NGX_ERROR; + } + + sentinel = ngx_slab_alloc(shpool, sizeof * sentinel); + if (sentinel == NULL) + { + return NGX_ERROR; + } + + ngx_rbtree_init(tree, sentinel, ngx_http_auth_digest_ev_rbtree_insert); + ngx_http_auth_digest_ev_rbtree = tree; + + lock = ngx_slab_alloc(shpool, sizeof(ngx_atomic_t)); + if (lock == NULL) + { + return NGX_ERROR; + } + ngx_http_auth_digest_cleanup_lock = lock; + + return NGX_OK; } -static int ngx_http_auth_digest_rbtree_cmp(const ngx_rbtree_node_t *v_left, - const ngx_rbtree_node_t *v_right) +static int ngx_http_auth_digest_rbtree_cmp(const ngx_rbtree_node_t* v_left, + const ngx_rbtree_node_t* v_right) { - if (v_left->key == v_right->key) - return 0; - else - return (v_left->key < v_right->key) ? -1 : 1; + if (v_left->key == v_right->key) + return 0; + else + return (v_left->key < v_right->key) ? -1 : 1; } static int -ngx_http_auth_digest_ev_rbtree_cmp(const ngx_rbtree_node_t *v_left, - const ngx_rbtree_node_t *v_right) +ngx_http_auth_digest_ev_rbtree_cmp(const ngx_rbtree_node_t* v_left, + const ngx_rbtree_node_t* v_right) { - if (v_left->key == v_right->key) - { - ngx_http_auth_digest_ev_node_t *evleft = - (ngx_http_auth_digest_ev_node_t *)v_left; - ngx_http_auth_digest_ev_node_t *evright = - (ngx_http_auth_digest_ev_node_t *)v_right; - return ngx_http_auth_digest_srcaddr_cmp( - &evleft->src_addr, evleft->src_addrlen, &evright->src_addr, - evright->src_addrlen); - } - return (v_left->key < v_right->key) ? -1 : 1; + if (v_left->key == v_right->key) + { + ngx_http_auth_digest_ev_node_t* evleft = + (ngx_http_auth_digest_ev_node_t*)v_left; + ngx_http_auth_digest_ev_node_t* evright = + (ngx_http_auth_digest_ev_node_t*)v_right; + return ngx_http_auth_digest_srcaddr_cmp( + &evleft->src_addr, evleft->src_addrlen, &evright->src_addr, + evright->src_addrlen); + } + return (v_left->key < v_right->key) ? -1 : 1; } -static void ngx_rbtree_generic_insert(ngx_rbtree_node_t *temp, - ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel, - int (*compare)(const ngx_rbtree_node_t *left, - const ngx_rbtree_node_t *right)) +static void ngx_rbtree_generic_insert(ngx_rbtree_node_t* temp, + ngx_rbtree_node_t* node, ngx_rbtree_node_t* sentinel, + int (*compare)(const ngx_rbtree_node_t* left, + const ngx_rbtree_node_t* right)) { - for (;;) - { - if (node->key < temp->key) - { - - if (temp->left == sentinel) - { - temp->left = node; - break; - } - - temp = temp->left; - } - else if (node->key > temp->key) - { - - if (temp->right == sentinel) - { - temp->right = node; - break; - } - - temp = temp->right; - } - else - { /* node->key == temp->key */ - if (compare(node, temp) < 0) - { - - if (temp->left == sentinel) - { - temp->left = node; - break; - } - - temp = temp->left; - } - else - { - - if (temp->right == sentinel) - { - temp->right = node; - break; - } - - temp = temp->right; - } - } - } - - node->parent = temp; - node->left = sentinel; - node->right = sentinel; - ngx_rbt_red(node); + for (;;) + { + if (node->key < temp->key) + { + + if (temp->left == sentinel) + { + temp->left = node; + break; + } + + temp = temp->left; + } + else if (node->key > temp->key) + { + + if (temp->right == sentinel) + { + temp->right = node; + break; + } + + temp = temp->right; + } + else + { /* node->key == temp->key */ + if (compare(node, temp) < 0) + { + + if (temp->left == sentinel) + { + temp->left = node; + break; + } + + temp = temp->left; + } + else + { + + if (temp->right == sentinel) + { + temp->right = node; + break; + } + + temp = temp->right; + } + } + } + + node->parent = temp; + node->left = sentinel; + node->right = sentinel; + ngx_rbt_red(node); } -static void ngx_http_auth_digest_rbtree_insert(ngx_rbtree_node_t *temp, - ngx_rbtree_node_t *node, - ngx_rbtree_node_t *sentinel) +static void ngx_http_auth_digest_rbtree_insert(ngx_rbtree_node_t* temp, + ngx_rbtree_node_t* node, + ngx_rbtree_node_t* sentinel) { - ngx_rbtree_generic_insert(temp, node, sentinel, - ngx_http_auth_digest_rbtree_cmp); + ngx_rbtree_generic_insert(temp, node, sentinel, + ngx_http_auth_digest_rbtree_cmp); } -static void ngx_http_auth_digest_ev_rbtree_insert(ngx_rbtree_node_t *temp, - ngx_rbtree_node_t *node, - ngx_rbtree_node_t *sentinel) +static void ngx_http_auth_digest_ev_rbtree_insert(ngx_rbtree_node_t* temp, + ngx_rbtree_node_t* node, + ngx_rbtree_node_t* sentinel) { - ngx_rbtree_generic_insert(temp, node, sentinel, - ngx_http_auth_digest_ev_rbtree_cmp); + ngx_rbtree_generic_insert(temp, node, sentinel, + ngx_http_auth_digest_ev_rbtree_cmp); } -static ngx_rbtree_node_t * -ngx_http_auth_digest_rbtree_find(ngx_rbtree_key_t key, ngx_rbtree_node_t *node, - ngx_rbtree_node_t *sentinel) +static ngx_rbtree_node_t* +ngx_http_auth_digest_rbtree_find(ngx_rbtree_key_t key, ngx_rbtree_node_t* node, + ngx_rbtree_node_t* sentinel) { - if (node == sentinel) - return NULL; + if (node == sentinel) + return NULL; - ngx_rbtree_node_t *found = (node->key == key) ? node : NULL; - if (found == NULL && node->left != sentinel) - { - found = ngx_http_auth_digest_rbtree_find(key, node->left, sentinel); - } - if (found == NULL && node->right != sentinel) - { - found = ngx_http_auth_digest_rbtree_find(key, node->right, sentinel); - } + ngx_rbtree_node_t* found = (node->key == key) ? node : NULL; + if (found == NULL && node->left != sentinel) + { + found = ngx_http_auth_digest_rbtree_find(key, node->left, sentinel); + } + if (found == NULL && node->right != sentinel) + { + found = ngx_http_auth_digest_rbtree_find(key, node->right, sentinel); + } - return found; + return found; } -static ngx_http_auth_digest_ev_node_t * -ngx_http_auth_digest_ev_rbtree_find(ngx_http_auth_digest_ev_node_t *this, - ngx_rbtree_node_t *node, - ngx_rbtree_node_t *sentinel) +static ngx_http_auth_digest_ev_node_t* +ngx_http_auth_digest_ev_rbtree_find(ngx_http_auth_digest_ev_node_t* this, + ngx_rbtree_node_t* node, + ngx_rbtree_node_t* sentinel) { - int cmpval; - if (node == sentinel) - return NULL; - - cmpval = ngx_http_auth_digest_ev_rbtree_cmp((ngx_rbtree_node_t *)this, node); - if (cmpval == 0) - { - return (ngx_http_auth_digest_ev_node_t *)node; - } - return ngx_http_auth_digest_ev_rbtree_find( - this, (cmpval < 0) ? node->left : node->right, sentinel); + int cmpval; + if (node == sentinel) + return NULL; + + cmpval = ngx_http_auth_digest_ev_rbtree_cmp((ngx_rbtree_node_t*)this, node); + if (cmpval == 0) + { + return (ngx_http_auth_digest_ev_node_t*)node; + } + return ngx_http_auth_digest_ev_rbtree_find( + this, (cmpval < 0) ? node->left : node->right, sentinel); } -void ngx_http_auth_digest_cleanup(ngx_event_t *ev) +void ngx_http_auth_digest_cleanup(ngx_event_t* ev) { - if (ev->timer_set) - ngx_del_timer(ev); - - if (!(ngx_quit || ngx_terminate || ngx_exiting)) - { - ngx_add_timer(ev, NGX_HTTP_AUTH_DIGEST_CLEANUP_INTERVAL); - } - - if (ngx_trylock(ngx_http_auth_digest_cleanup_lock)) - { - ngx_http_auth_digest_rbtree_prune(ev->log); - ngx_http_auth_digest_ev_rbtree_prune(ev->log); - ngx_unlock(ngx_http_auth_digest_cleanup_lock); - } + if (ev->timer_set) + ngx_del_timer(ev); + + if (!(ngx_quit || ngx_terminate || ngx_exiting)) + { + ngx_add_timer(ev, NGX_HTTP_AUTH_DIGEST_CLEANUP_INTERVAL); + } + + if (ngx_trylock(ngx_http_auth_digest_cleanup_lock)) + { + ngx_http_auth_digest_rbtree_prune(ev->log); + ngx_http_auth_digest_ev_rbtree_prune(ev->log); + ngx_unlock(ngx_http_auth_digest_cleanup_lock); + } } -static void ngx_http_auth_digest_rbtree_prune(ngx_log_t *log) +static void ngx_http_auth_digest_rbtree_prune(ngx_log_t* log) { - ngx_uint_t i; - time_t now = ngx_time(); - ngx_slab_pool_t *shpool = - (ngx_slab_pool_t *)ngx_http_auth_digest_shm_zone->shm.addr; - - ngx_shmtx_lock(&shpool->mutex); - ngx_http_auth_digest_cleanup_list->nelts = 0; - ngx_http_auth_digest_rbtree_prune_walk(ngx_http_auth_digest_rbtree->root, - ngx_http_auth_digest_rbtree->sentinel, - now, log); - - ngx_rbtree_node_t **elts = - (ngx_rbtree_node_t **)ngx_http_auth_digest_cleanup_list->elts; - for (i = 0; i < ngx_http_auth_digest_cleanup_list->nelts; i++) - { - ngx_rbtree_delete(ngx_http_auth_digest_rbtree, elts[i]); - ngx_slab_free_locked(shpool, elts[i]); - } - ngx_shmtx_unlock(&shpool->mutex); - - // if the cleanup array grew during the run, shrink it back down - if (ngx_http_auth_digest_cleanup_list->nalloc > - NGX_HTTP_AUTH_DIGEST_CLEANUP_BATCH_SIZE) - { - ngx_array_t *old_list = ngx_http_auth_digest_cleanup_list; - ngx_array_t *new_list = ngx_array_create( - old_list->pool, NGX_HTTP_AUTH_DIGEST_CLEANUP_BATCH_SIZE, - sizeof(ngx_rbtree_node_t *)); - if (new_list != NULL) - { - ngx_array_destroy(old_list); - ngx_http_auth_digest_cleanup_list = new_list; - } - else - { - ngx_log_error(NGX_LOG_ERR, log, 0, - "auth_digest ran out of cleanup space"); - } - } + ngx_uint_t i; + time_t now = ngx_time(); + ngx_slab_pool_t* shpool = + (ngx_slab_pool_t*)ngx_http_auth_digest_shm_zone->shm.addr; + + ngx_shmtx_lock(&shpool->mutex); + ngx_http_auth_digest_cleanup_list->nelts = 0; + ngx_http_auth_digest_rbtree_prune_walk(ngx_http_auth_digest_rbtree->root, + ngx_http_auth_digest_rbtree->sentinel, + now, log); + + ngx_rbtree_node_t** elts = + (ngx_rbtree_node_t**)ngx_http_auth_digest_cleanup_list->elts; + for (i = 0; i < ngx_http_auth_digest_cleanup_list->nelts; i++) + { + ngx_rbtree_delete(ngx_http_auth_digest_rbtree, elts[i]); + ngx_slab_free_locked(shpool, elts[i]); + } + ngx_shmtx_unlock(&shpool->mutex); + + // if the cleanup array grew during the run, shrink it back down + if (ngx_http_auth_digest_cleanup_list->nalloc > + NGX_HTTP_AUTH_DIGEST_CLEANUP_BATCH_SIZE) + { + ngx_array_t* old_list = ngx_http_auth_digest_cleanup_list; + ngx_array_t* new_list = ngx_array_create( + old_list->pool, NGX_HTTP_AUTH_DIGEST_CLEANUP_BATCH_SIZE, + sizeof(ngx_rbtree_node_t*)); + if (new_list != NULL) + { + ngx_array_destroy(old_list); + ngx_http_auth_digest_cleanup_list = new_list; + } + else + { + ngx_log_error(NGX_LOG_ERR, log, 0, "auth_digest ran out of cleanup space"); + } + } } -static void ngx_http_auth_digest_rbtree_prune_walk(ngx_rbtree_node_t *node, - ngx_rbtree_node_t *sentinel, - time_t now, ngx_log_t *log) +static void ngx_http_auth_digest_rbtree_prune_walk(ngx_rbtree_node_t* node, + ngx_rbtree_node_t* sentinel, + time_t now, ngx_log_t* log) { - if (node == sentinel) - return; - - if (node->left != sentinel) - { - ngx_http_auth_digest_rbtree_prune_walk(node->left, sentinel, now, log); - } - - if (node->right != sentinel) - { - ngx_http_auth_digest_rbtree_prune_walk(node->right, sentinel, now, log); - } - - ngx_http_auth_digest_node_t *dnode = (ngx_http_auth_digest_node_t *)node; - if (dnode->drop_time <= ngx_time()) - { - ngx_rbtree_node_t **dropnode = - ngx_array_push(ngx_http_auth_digest_cleanup_list); - dropnode[0] = node; - } + if (node == sentinel) + return; + + if (node->left != sentinel) + { + ngx_http_auth_digest_rbtree_prune_walk(node->left, sentinel, now, log); + } + + if (node->right != sentinel) + { + ngx_http_auth_digest_rbtree_prune_walk(node->right, sentinel, now, log); + } + + ngx_http_auth_digest_node_t* dnode = (ngx_http_auth_digest_node_t*)node; + if (dnode->drop_time <= ngx_time()) + { + ngx_rbtree_node_t** dropnode = + ngx_array_push(ngx_http_auth_digest_cleanup_list); + dropnode[0] = node; + } } -static void ngx_http_auth_digest_ev_rbtree_prune(ngx_log_t *log) +static void ngx_http_auth_digest_ev_rbtree_prune(ngx_log_t* log) { - ngx_uint_t i; - time_t now = ngx_time(); - ngx_slab_pool_t *shpool = - (ngx_slab_pool_t *)ngx_http_auth_digest_shm_zone->shm.addr; - - ngx_shmtx_lock(&shpool->mutex); - ngx_http_auth_digest_cleanup_list->nelts = 0; - ngx_http_auth_digest_ev_rbtree_prune_walk( - ngx_http_auth_digest_ev_rbtree->root, - ngx_http_auth_digest_ev_rbtree->sentinel, now, log); - - ngx_rbtree_node_t **elts = - (ngx_rbtree_node_t **)ngx_http_auth_digest_cleanup_list->elts; - for (i = 0; i < ngx_http_auth_digest_cleanup_list->nelts; i++) - { - ngx_rbtree_delete(ngx_http_auth_digest_ev_rbtree, elts[i]); - ngx_slab_free_locked(shpool, elts[i]); - } - ngx_shmtx_unlock(&shpool->mutex); - - // if the cleanup array grew during the run, shrink it back down - if (ngx_http_auth_digest_cleanup_list->nalloc > - NGX_HTTP_AUTH_DIGEST_CLEANUP_BATCH_SIZE) - { - ngx_array_t *old_list = ngx_http_auth_digest_cleanup_list; - ngx_array_t *new_list = ngx_array_create( - old_list->pool, NGX_HTTP_AUTH_DIGEST_CLEANUP_BATCH_SIZE, - sizeof(ngx_rbtree_node_t *)); - if (new_list != NULL) - { - ngx_array_destroy(old_list); - ngx_http_auth_digest_cleanup_list = new_list; - } - else - { - ngx_log_error(NGX_LOG_ERR, log, 0, - "auth_digest ran out of cleanup space"); - } - } + ngx_uint_t i; + time_t now = ngx_time(); + ngx_slab_pool_t* shpool = + (ngx_slab_pool_t*)ngx_http_auth_digest_shm_zone->shm.addr; + + ngx_shmtx_lock(&shpool->mutex); + ngx_http_auth_digest_cleanup_list->nelts = 0; + ngx_http_auth_digest_ev_rbtree_prune_walk( + ngx_http_auth_digest_ev_rbtree->root, + ngx_http_auth_digest_ev_rbtree->sentinel, now, log); + + ngx_rbtree_node_t** elts = + (ngx_rbtree_node_t**)ngx_http_auth_digest_cleanup_list->elts; + for (i = 0; i < ngx_http_auth_digest_cleanup_list->nelts; i++) + { + ngx_rbtree_delete(ngx_http_auth_digest_ev_rbtree, elts[i]); + ngx_slab_free_locked(shpool, elts[i]); + } + ngx_shmtx_unlock(&shpool->mutex); + + // if the cleanup array grew during the run, shrink it back down + if (ngx_http_auth_digest_cleanup_list->nalloc > + NGX_HTTP_AUTH_DIGEST_CLEANUP_BATCH_SIZE) + { + ngx_array_t* old_list = ngx_http_auth_digest_cleanup_list; + ngx_array_t* new_list = ngx_array_create( + old_list->pool, NGX_HTTP_AUTH_DIGEST_CLEANUP_BATCH_SIZE, + sizeof(ngx_rbtree_node_t*)); + if (new_list != NULL) + { + ngx_array_destroy(old_list); + ngx_http_auth_digest_cleanup_list = new_list; + } + else + { + ngx_log_error(NGX_LOG_ERR, log, 0, + "auth_digest ran out of cleanup space"); + } + } } -static void ngx_http_auth_digest_ev_rbtree_prune_walk(ngx_rbtree_node_t *node, - ngx_rbtree_node_t *sentinel, - time_t now, ngx_log_t *log) +static void ngx_http_auth_digest_ev_rbtree_prune_walk(ngx_rbtree_node_t* node, + ngx_rbtree_node_t* sentinel, + time_t now, ngx_log_t* log) { - if (node == sentinel) - return; - - if (node->left != sentinel) - { - ngx_http_auth_digest_ev_rbtree_prune_walk(node->left, sentinel, now, log); - } - - if (node->right != sentinel) - { - ngx_http_auth_digest_ev_rbtree_prune_walk(node->right, sentinel, now, log); - } - - ngx_http_auth_digest_ev_node_t *dnode = - (ngx_http_auth_digest_ev_node_t *)node; - if (dnode->drop_time <= ngx_time()) - { - ngx_rbtree_node_t **dropnode = - ngx_array_push(ngx_http_auth_digest_cleanup_list); - dropnode[0] = node; - } + if (node == sentinel) + return; + + if (node->left != sentinel) + { + ngx_http_auth_digest_ev_rbtree_prune_walk(node->left, sentinel, now, log); + } + + if (node->right != sentinel) + { + ngx_http_auth_digest_ev_rbtree_prune_walk(node->right, sentinel, now, log); + } + + ngx_http_auth_digest_ev_node_t* dnode = + (ngx_http_auth_digest_ev_node_t*)node; + if (dnode->drop_time <= ngx_time()) + { + ngx_rbtree_node_t** dropnode = + ngx_array_push(ngx_http_auth_digest_cleanup_list); + dropnode[0] = node; + } } -static ngx_http_auth_digest_nonce_t ngx_http_auth_digest_next_nonce(ngx_http_request_t *r) +static ngx_http_auth_digest_nonce_t ngx_http_auth_digest_next_nonce(ngx_http_request_t* r) { - ngx_http_auth_digest_loc_conf_t *alcf; - ngx_slab_pool_t *shpool; - ngx_http_auth_digest_nonce_t nonce; - ngx_uint_t key; - ngx_http_auth_digest_node_t *node; - - shpool = (ngx_slab_pool_t *)ngx_http_auth_digest_shm_zone->shm.addr; - alcf = ngx_http_get_module_loc_conf(r, ngx_http_auth_digest_module); - - // create a nonce value that's not in the active set - while (1) - { - nonce.t = ngx_time(); - nonce.rnd = ngx_random(); - key = ngx_crc32_short((u_char *)&nonce.rnd, sizeof nonce.rnd) ^ - ngx_crc32_short((u_char *)&nonce.t, sizeof(nonce.t)); - - ngx_shmtx_lock(&shpool->mutex); - ngx_rbtree_node_t *found = - ngx_http_auth_digest_rbtree_find(key, ngx_http_auth_digest_rbtree->root, - ngx_http_auth_digest_rbtree->sentinel); - - if (found != NULL) - { - ngx_shmtx_unlock(&shpool->mutex); - continue; - } - - node = ngx_slab_alloc_locked(shpool, - sizeof(ngx_http_auth_digest_node_t) + - ngx_bitvector_size(1 + alcf->replays)); - if (node == NULL) - { - ngx_shmtx_unlock(&shpool->mutex); - ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, - "auth_digest ran out of shm space. Increase the " - "auth_digest_shm_size limit."); - nonce.t = 0; - nonce.rnd = 0; - return nonce; - } - node->expires = nonce.t + alcf->timeout; - node->drop_time = nonce.t + alcf->timeout; - ngx_memset(node->nc, 0xff, ngx_bitvector_size(1 + alcf->replays)); - ((ngx_rbtree_node_t *)node)->key = key; - ngx_rbtree_insert(ngx_http_auth_digest_rbtree, &node->node); - - ngx_shmtx_unlock(&shpool->mutex); - return nonce; - } + ngx_http_auth_digest_loc_conf_t* alcf; + ngx_slab_pool_t* shpool; + ngx_http_auth_digest_nonce_t nonce; + ngx_uint_t key; + ngx_http_auth_digest_node_t* node; + + shpool = (ngx_slab_pool_t*)ngx_http_auth_digest_shm_zone->shm.addr; + alcf = ngx_http_get_module_loc_conf(r, ngx_http_auth_digest_module); + + // create a nonce value that's not in the active set + while (1) + { + nonce.t = ngx_time(); + nonce.rnd = ngx_random(); + key = ngx_crc32_short((u_char*)&nonce.rnd, sizeof nonce.rnd) ^ + ngx_crc32_short((u_char*)&nonce.t, sizeof(nonce.t)); + + ngx_shmtx_lock(&shpool->mutex); + ngx_rbtree_node_t* found = + ngx_http_auth_digest_rbtree_find(key, ngx_http_auth_digest_rbtree->root, + ngx_http_auth_digest_rbtree->sentinel); + + if (found != NULL) + { + ngx_shmtx_unlock(&shpool->mutex); + continue; + } + + node = ngx_slab_alloc_locked(shpool, + sizeof(ngx_http_auth_digest_node_t) + + ngx_bitvector_size(1 + alcf->replays)); + if (node == NULL) + { + ngx_shmtx_unlock(&shpool->mutex); + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "auth_digest ran out of shm space. Increase the " + "auth_digest_shm_size limit."); + nonce.t = 0; + nonce.rnd = 0; + return nonce; + } + node->expires = nonce.t + alcf->timeout; + node->drop_time = nonce.t + alcf->timeout; + ngx_memset(node->nc, 0xff, ngx_bitvector_size(1 + alcf->replays)); + ((ngx_rbtree_node_t*)node)->key = key; + ngx_rbtree_insert(ngx_http_auth_digest_rbtree, &node->node); + + ngx_shmtx_unlock(&shpool->mutex); + return nonce; + } } -static int ngx_http_auth_digest_srcaddr_key(struct sockaddr *sa, socklen_t len, - ngx_uint_t *key) +static int ngx_http_auth_digest_srcaddr_key(struct sockaddr* sa, socklen_t len, + ngx_uint_t* key) { - struct sockaddr_in *sin; + struct sockaddr_in* sin; #if (NGX_HAVE_INET6) - struct sockaddr_in6 *s6; + struct sockaddr_in6* s6; #endif - switch (sa->sa_family) - { - case AF_INET: - sin = (struct sockaddr_in *)sa; - *key = ngx_crc32_short((u_char *)&sin->sin_addr, sizeof(sin->sin_addr)); - return 1; + switch (sa->sa_family) + { + case AF_INET: + sin = (struct sockaddr_in*)sa; + *key = ngx_crc32_short((u_char*)&sin->sin_addr, sizeof(sin->sin_addr)); + return 1; #if (NGX_HAVE_INET6) - case AF_INET6: - s6 = (struct sockaddr_in6 *)sa; - *key = ngx_crc32_short((u_char *)&s6->sin6_addr, sizeof(s6->sin6_addr)); - return 1; + case AF_INET6: + s6 = (struct sockaddr_in6*)sa; + *key = ngx_crc32_short((u_char*)&s6->sin6_addr, sizeof(s6->sin6_addr)); + return 1; #endif - default: - break; - } - return 0; + default: + break; + } + return 0; } -static int ngx_http_auth_digest_srcaddr_cmp(struct sockaddr *sa1, - socklen_t len1, - struct sockaddr *sa2, - socklen_t len2) +static int ngx_http_auth_digest_srcaddr_cmp(struct sockaddr* sa1, + socklen_t len1, + struct sockaddr* sa2, + socklen_t len2) { - struct sockaddr_in *sin1, *sin2; + struct sockaddr_in* sin1, * sin2; #if (NGX_HAVE_INET6) - struct sockaddr_in6 *s61, *s62; + struct sockaddr_in6* s61, * s62; #endif - if (len1 != len2) - { - return (len1 < len2) ? -1 : 1; - } - if (sa1->sa_family != sa2->sa_family) - { - return (sa1->sa_family < sa2->sa_family) ? -1 : 1; - } - - switch (sa1->sa_family) - { - case AF_INET: - sin1 = (struct sockaddr_in *)sa1; - sin2 = (struct sockaddr_in *)sa2; - return ngx_memcmp(&sin1->sin_addr, &sin2->sin_addr, sizeof(sin1->sin_addr)); + if (len1 != len2) + { + return (len1 < len2) ? -1 : 1; + } + if (sa1->sa_family != sa2->sa_family) + { + return (sa1->sa_family < sa2->sa_family) ? -1 : 1; + } + + switch (sa1->sa_family) + { + case AF_INET: + sin1 = (struct sockaddr_in*)sa1; + sin2 = (struct sockaddr_in*)sa2; + return ngx_memcmp(&sin1->sin_addr, &sin2->sin_addr, sizeof(sin1->sin_addr)); #if (NGX_HAVE_INET6) - case AF_INET6: - s61 = (struct sockaddr_in6 *)sa1; - s62 = (struct sockaddr_in6 *)sa2; - return ngx_memcmp(&s61->sin6_addr, &s62->sin6_addr, sizeof(s61->sin6_addr)); + case AF_INET6: + s61 = (struct sockaddr_in6*)sa1; + s62 = (struct sockaddr_in6*)sa2; + return ngx_memcmp(&s61->sin6_addr, &s62->sin6_addr, sizeof(s61->sin6_addr)); #endif - default: - break; - } - return -999; + default: + break; + } + return -999; } -static void ngx_http_auth_digest_evasion_tracking(ngx_http_request_t *r, - ngx_http_auth_digest_loc_conf_t *alcf, - ngx_int_t status) +static void ngx_http_auth_digest_evasion_tracking(ngx_http_request_t* r, + ngx_http_auth_digest_loc_conf_t* alcf, + ngx_int_t status) { - ngx_slab_pool_t *shpool; - ngx_uint_t key; - ngx_http_auth_digest_ev_node_t testnode, *node; - - if (!ngx_http_auth_digest_srcaddr_key(r->connection->sockaddr, r->connection->socklen, &key)) - { - ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, "skipping evasive tactics for this source address"); - return; - } - shpool = (ngx_slab_pool_t *)ngx_http_auth_digest_shm_zone->shm.addr; - - ngx_shmtx_lock(&shpool->mutex); - ngx_memzero(&testnode, sizeof(testnode)); - testnode.node.key = key; - ngx_memcpy(&testnode.src_addr, r->connection->sockaddr, r->connection->socklen); - testnode.src_addrlen = r->connection->socklen; - node = ngx_http_auth_digest_ev_rbtree_find(&testnode, ngx_http_auth_digest_ev_rbtree->root, ngx_http_auth_digest_ev_rbtree->sentinel); - - if (node == NULL) - { - // Don't bother creating a node if this was a successful auth - if (status == NGX_HTTP_AUTH_DIGEST_STATUS_SUCCESS) - { - ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, "sucessful auth, not tracking"); - ngx_shmtx_unlock(&shpool->mutex); - return; - } - ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, "adding tracking node"); - node = - ngx_slab_alloc_locked(shpool, sizeof(ngx_http_auth_digest_ev_node_t)); - - if (node == NULL) - { - ngx_shmtx_unlock(&shpool->mutex); - ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, - "auth_digest ran out of shm space. Increase the " - "auth_digest_shm_size limit."); - return; - } - ngx_memcpy(&node->src_addr, r->connection->sockaddr, - r->connection->socklen); - node->src_addrlen = r->connection->socklen; - ((ngx_rbtree_node_t *)node)->key = key; - ngx_rbtree_insert(ngx_http_auth_digest_ev_rbtree, &node->node); - } - if (status == NGX_HTTP_AUTH_DIGEST_STATUS_SUCCESS) - { - ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, - "successful auth, clearing evasion counters"); - node->failcount = 0; - node->drop_time = ngx_time(); - } - else - { - // Reset the failure count to 1 if we're outside the evasion window - if (ngx_time() > node->drop_time) - { - node->failcount = 1; - } - else - { - node->failcount += 1; - } - node->drop_time = ngx_time() + alcf->evasion_time; - ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, - "failed auth, updating failcount=%d, drop_time=%d", - node->failcount, node->drop_time); - } - ngx_shmtx_unlock(&shpool->mutex); + ngx_slab_pool_t* shpool; + ngx_uint_t key; + ngx_http_auth_digest_ev_node_t testnode, * node; + + if (!ngx_http_auth_digest_srcaddr_key(r->connection->sockaddr, r->connection->socklen, &key)) + { + ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, "skipping evasive tactics for this source address"); + return; + } + shpool = (ngx_slab_pool_t*)ngx_http_auth_digest_shm_zone->shm.addr; + + ngx_shmtx_lock(&shpool->mutex); + ngx_memzero(&testnode, sizeof(testnode)); + testnode.node.key = key; + ngx_memcpy(&testnode.src_addr, r->connection->sockaddr, r->connection->socklen); + testnode.src_addrlen = r->connection->socklen; + node = ngx_http_auth_digest_ev_rbtree_find(&testnode, ngx_http_auth_digest_ev_rbtree->root, ngx_http_auth_digest_ev_rbtree->sentinel); + + if (node == NULL) + { + // Don't bother creating a node if this was a successful auth + if (status == NGX_HTTP_AUTH_DIGEST_STATUS_SUCCESS) + { + ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, "sucessful auth, not tracking"); + ngx_shmtx_unlock(&shpool->mutex); + return; + } + ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, "adding tracking node"); + node = + ngx_slab_alloc_locked(shpool, sizeof(ngx_http_auth_digest_ev_node_t)); + + if (node == NULL) + { + ngx_shmtx_unlock(&shpool->mutex); + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "auth_digest ran out of shm space. Increase the " + "auth_digest_shm_size limit."); + return; + } + ngx_memcpy(&node->src_addr, r->connection->sockaddr, + r->connection->socklen); + node->src_addrlen = r->connection->socklen; + ((ngx_rbtree_node_t*)node)->key = key; + ngx_rbtree_insert(ngx_http_auth_digest_ev_rbtree, &node->node); + } + if (status == NGX_HTTP_AUTH_DIGEST_STATUS_SUCCESS) + { + ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, + "successful auth, clearing evasion counters"); + node->failcount = 0; + node->drop_time = ngx_time(); + } + else + { + // Reset the failure count to 1 if we're outside the evasion window + if (ngx_time() > node->drop_time) + { + node->failcount = 1; + } + else + { + node->failcount += 1; + } + node->drop_time = ngx_time() + alcf->evasion_time; + ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, + "failed auth, updating failcount=%d, drop_time=%d", + node->failcount, node->drop_time); + } + ngx_shmtx_unlock(&shpool->mutex); } -static int ngx_http_auth_digest_evading(ngx_http_request_t *r, - ngx_http_auth_digest_loc_conf_t *alcf) +static int ngx_http_auth_digest_evading(ngx_http_request_t* r, + ngx_http_auth_digest_loc_conf_t* alcf) { - ngx_slab_pool_t *shpool; - ngx_uint_t key; - ngx_http_auth_digest_ev_node_t testnode, *node; - int evading = 0; - - if (!ngx_http_auth_digest_srcaddr_key(r->connection->sockaddr, r->connection->socklen, &key)) - { - return 0; - } - - ngx_memzero(&testnode, sizeof(testnode)); - testnode.node.key = key; - ngx_memcpy(&testnode.src_addr, r->connection->sockaddr, - r->connection->socklen); - testnode.src_addrlen = r->connection->socklen; - - shpool = (ngx_slab_pool_t *)ngx_http_auth_digest_shm_zone->shm.addr; - - ngx_shmtx_lock(&shpool->mutex); - node = ngx_http_auth_digest_ev_rbtree_find(&testnode, ngx_http_auth_digest_ev_rbtree->root, ngx_http_auth_digest_ev_rbtree->sentinel); - - if (node != NULL && node->failcount >= alcf->maxtries && ngx_time() < node->drop_time) - { - ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "ignoring authentication request - in evasion period"); - evading = 1; - } - ngx_shmtx_unlock(&shpool->mutex); - return evading; + ngx_slab_pool_t* shpool; + ngx_uint_t key; + ngx_http_auth_digest_ev_node_t testnode, * node; + int evading = 0; + + if (!ngx_http_auth_digest_srcaddr_key(r->connection->sockaddr, r->connection->socklen, &key)) + { + return 0; + } + + ngx_memzero(&testnode, sizeof(testnode)); + testnode.node.key = key; + ngx_memcpy(&testnode.src_addr, r->connection->sockaddr, + r->connection->socklen); + testnode.src_addrlen = r->connection->socklen; + + shpool = (ngx_slab_pool_t*)ngx_http_auth_digest_shm_zone->shm.addr; + + ngx_shmtx_lock(&shpool->mutex); + node = ngx_http_auth_digest_ev_rbtree_find(&testnode, ngx_http_auth_digest_ev_rbtree->root, ngx_http_auth_digest_ev_rbtree->sentinel); + + if (node != NULL && node->failcount >= alcf->maxtries && ngx_time() < node->drop_time) + { + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "ignoring authentication request - in evasion period"); + evading = 1; + } + ngx_shmtx_unlock(&shpool->mutex); + return evading; } // pulled in from nginx_auth_o_module -int ngx_http_auth_os_is_loopback(ngx_str_t *server) +int ngx_http_auth_digest_is_loopback(ngx_str_t* server) { - char *localhost = "localhost"; - int localhostLen = strlen(localhost); - char *loopback = "127.0.0.1"; - int loopbackLen = strlen(loopback); - - int pos = 0, portCharPos = 0; - char portChar[4] = ""; - int port = 0; - - if (server->len <= 0 || server->data == NULL) - return -1; - - if (((int)strlen((const char *)server->data) >= localhostLen) && (strncmp((const char *)server->data, localhost, localhostLen) == 0)) - { - pos = localhostLen; - } - else if (((int)strlen((const char *)server->data) >= loopbackLen) && (strncmp((const char *)server->data, loopback, loopbackLen) == 0)) - { - pos = loopbackLen; - } - else - { - return -1; - } - - if (server->data[pos] == '\0') - { - return 0; - } - else if (server->data[pos] == ':') - { - ++pos; - while (portCharPos <= 3 && server->data[pos] >= '0' && server->data[pos] <= '9') - { - portChar[portCharPos] = server->data[pos]; - ++portCharPos; - ++pos; - } - - if (server->data[pos] != '\0') - return -1; - - port = atoi(portChar); - if (port >= 1 && port <= 65535) - return 0; - } - - return -1; + char* localhost = "localhost"; + int localhostLen = strlen(localhost); + char* loopback = "127.0.0.1"; + int loopbackLen = strlen(loopback); + + int pos = 0, portCharPos = 0; + char portChar[4] = ""; + int port = 0; + + if (server->len <= 0 || server->data == NULL) + return -1; + + if (((int)strlen((const char*)server->data) >= localhostLen) && (strncmp((const char*)server->data, localhost, localhostLen) == 0)) + { + pos = localhostLen; + } + else if (((int)strlen((const char*)server->data) >= loopbackLen) && (strncmp((const char*)server->data, loopback, loopbackLen) == 0)) + { + pos = loopbackLen; + } + else + { + return -1; + } + + if (server->data[pos] == '\0') + { + return 0; + } + else if (server->data[pos] == ':') + { + ++pos; + while (portCharPos <= 3 && server->data[pos] >= '0' && server->data[pos] <= '9') + { + portChar[portCharPos] = server->data[pos]; + ++portCharPos; + ++pos; + } + + if (server->data[pos] != '\0') + return -1; + + port = atoi(portChar); + if (port >= 1 && port <= 65535) + return 0; + } + + return -1; } \ No newline at end of file diff --git a/ngx_http_auth_digest_module.h b/ngx_http_auth_digest_module.h index 3c4fa2c..f058409 100644 --- a/ngx_http_auth_digest_module.h +++ b/ngx_http_auth_digest_module.h @@ -74,9 +74,7 @@ static char *ngx_http_auth_digest_set_user_file(ngx_conf_t *cf, #define NGX_HTTP_AUTH_DIGEST_BUF_SIZE 4096 // digest challenge generation -static ngx_int_t ngx_http_auth_digest_send_challenge(ngx_http_request_t *r, - ngx_str_t *realm, - ngx_uint_t is_stale); +static ngx_int_t ngx_http_auth_digest_send_challenge(ngx_http_request_t *r, ngx_str_t *realm, ngx_uint_t is_stale); // digest response validators static ngx_int_t @@ -86,6 +84,8 @@ ngx_http_auth_digest_verify_user(ngx_http_request_t *r, ngx_http_auth_digest_cre static ngx_int_t ngx_http_auth_digest_verify_hash(ngx_http_request_t *r, ngx_http_auth_digest_cred_t *fields, u_char *hashed_pw); +static int ngx_http_auth_digest_is_loopback(ngx_str_t *server); + // the shm segment that houses the used-nonces tree and evasion rbtree static ngx_uint_t ngx_http_auth_digest_shm_size; static ngx_shm_zone_t *ngx_http_auth_digest_shm_zone; @@ -183,23 +183,21 @@ static ngx_int_t ngx_http_auth_digest_worker_init(ngx_cycle_t *cycle); static ngx_command_t ngx_http_auth_digest_commands[] = { {ngx_string("auth_digest"), - NGX_HTTP_MAIN_CONF | NGX_HTTP_SRV_CONF | NGX_HTTP_LOC_CONF | - NGX_HTTP_LMT_CONF | NGX_CONF_TAKE1, + NGX_HTTP_MAIN_CONF | NGX_HTTP_SRV_CONF | NGX_HTTP_LOC_CONF | NGX_HTTP_LMT_CONF | NGX_CONF_TAKE1, ngx_http_auth_digest_set_realm, NGX_HTTP_LOC_CONF_OFFSET, offsetof(ngx_http_auth_digest_loc_conf_t, realm), NULL}, {ngx_string("auth_digest_user_file"), - NGX_HTTP_MAIN_CONF | NGX_HTTP_SRV_CONF | NGX_HTTP_LOC_CONF | - NGX_HTTP_LMT_CONF | NGX_CONF_TAKE1, + NGX_HTTP_MAIN_CONF | NGX_HTTP_SRV_CONF | NGX_HTTP_LOC_CONF | NGX_HTTP_LMT_CONF | NGX_CONF_TAKE1, ngx_http_auth_digest_set_user_file, NGX_HTTP_LOC_CONF_OFFSET, offsetof(ngx_http_auth_digest_loc_conf_t, user_file), NULL}, {ngx_string("auth_digest_localhost_nocheck"), - NGX_HTTP_LOC_CONF | NGX_CONF_TAKE1 | NGX_HTTP_LMT_CONF, + NGX_HTTP_MAIN_CONF | NGX_HTTP_SRV_CONF | NGX_HTTP_LOC_CONF | NGX_HTTP_LMT_CONF | NGX_CONF_TAKE1, ngx_conf_set_str_slot, NGX_HTTP_LOC_CONF_OFFSET, offsetof(ngx_http_auth_digest_loc_conf_t, localhost_nocheck), @@ -220,8 +218,7 @@ static ngx_command_t ngx_http_auth_digest_commands[] = NULL}, {ngx_string("auth_digest_drop_time"), - NGX_HTTP_MAIN_CONF | NGX_HTTP_SRV_CONF | NGX_HTTP_LOC_CONF | - NGX_CONF_TAKE1, + NGX_HTTP_MAIN_CONF | NGX_HTTP_SRV_CONF | NGX_HTTP_LOC_CONF | NGX_CONF_TAKE1, ngx_conf_set_sec_slot, NGX_HTTP_LOC_CONF_OFFSET, offsetof(ngx_http_auth_digest_loc_conf_t, drop_time), @@ -242,8 +239,7 @@ static ngx_command_t ngx_http_auth_digest_commands[] = NULL}, {ngx_string("auth_digest_maxtries"), - NGX_HTTP_MAIN_CONF | NGX_HTTP_SRV_CONF | NGX_HTTP_LOC_CONF | - NGX_CONF_TAKE1, + NGX_HTTP_MAIN_CONF | NGX_HTTP_SRV_CONF | NGX_HTTP_LOC_CONF | NGX_CONF_TAKE1, ngx_conf_set_num_slot, NGX_HTTP_LOC_CONF_OFFSET, offsetof(ngx_http_auth_digest_loc_conf_t, maxtries), From 0bb37cd66deb2451ef5a917b460fd1bdb60e5d64 Mon Sep 17 00:00:00 2001 From: Nate Gates Date: Thu, 4 Mar 2021 09:21:20 -0600 Subject: [PATCH 3/4] - var rename - fromatting --- ngx_http_auth_digest_module.c | 643 ++++++++++++---------------------- ngx_http_auth_digest_module.h | 385 ++++++++++---------- 2 files changed, 411 insertions(+), 617 deletions(-) diff --git a/ngx_http_auth_digest_module.c b/ngx_http_auth_digest_module.c index 55239c2..0a9e500 100644 --- a/ngx_http_auth_digest_module.c +++ b/ngx_http_auth_digest_module.c @@ -12,13 +12,11 @@ #include "ngx_http_auth_digest_module.h" -static void* ngx_http_auth_digest_create_loc_conf(ngx_conf_t* cf) -{ +static void* ngx_http_auth_digest_create_loc_conf(ngx_conf_t* cf) { ngx_http_auth_digest_loc_conf_t* conf; conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_auth_digest_loc_conf_t)); - if (conf == NULL) - { + if (conf == NULL) { return NULL; } @@ -31,8 +29,7 @@ static void* ngx_http_auth_digest_create_loc_conf(ngx_conf_t* cf) return conf; } -static char* ngx_http_auth_digest_merge_loc_conf(ngx_conf_t* cf, void* parent, void* child) -{ +static char* ngx_http_auth_digest_merge_loc_conf(ngx_conf_t* cf, void* parent, void* child) { ngx_http_auth_digest_loc_conf_t* prev = parent; ngx_http_auth_digest_loc_conf_t* conf = child; @@ -42,23 +39,20 @@ static char* ngx_http_auth_digest_merge_loc_conf(ngx_conf_t* cf, void* parent, v ngx_conf_merge_value(conf->replays, prev->replays, 20); ngx_conf_merge_sec_value(conf->evasion_time, prev->evasion_time, 300); ngx_conf_merge_value(conf->maxtries, prev->maxtries, 5); - ngx_conf_merge_str_value(conf->localhost_nocheck, prev->localhost_nocheck, "false"); + ngx_conf_merge_str_value(conf->allow_localhost, prev->allow_localhost, "off"); - if (conf->user_file.value.len == 0) - { + if (conf->user_file.value.len == 0) { conf->user_file = prev->user_file; } - if (conf->realm.value.len == 0) - { + if (conf->realm.value.len == 0) { conf->realm = prev->realm; } return NGX_CONF_OK; } -static ngx_int_t ngx_http_auth_digest_init(ngx_conf_t* cf) -{ +static ngx_int_t ngx_http_auth_digest_init(ngx_conf_t* cf) { ngx_http_handler_pt* h; ngx_http_core_main_conf_t* cmcf; ngx_str_t* shm_name; @@ -66,8 +60,7 @@ static ngx_int_t ngx_http_auth_digest_init(ngx_conf_t* cf) cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module); h = ngx_array_push(&cmcf->phases[NGX_HTTP_ACCESS_PHASE].handlers); - if (h == NULL) - { + if (h == NULL) { return NGX_ERROR; } @@ -75,8 +68,7 @@ static ngx_int_t ngx_http_auth_digest_init(ngx_conf_t* cf) ngx_http_auth_digest_cleanup_timer = ngx_pcalloc(cf->pool, sizeof(ngx_event_t)); - if (ngx_http_auth_digest_cleanup_timer == NULL) - { + if (ngx_http_auth_digest_cleanup_timer == NULL) { return NGX_ERROR; } @@ -84,16 +76,14 @@ static ngx_int_t ngx_http_auth_digest_init(ngx_conf_t* cf) shm_name->len = sizeof("auth_digest"); shm_name->data = (unsigned char*)"auth_digest"; - if (ngx_http_auth_digest_shm_size == 0) - { + if (ngx_http_auth_digest_shm_size == 0) { ngx_http_auth_digest_shm_size = 4 * 256 * ngx_pagesize; // default to 4mb } ngx_http_auth_digest_shm_zone = ngx_shared_memory_add(cf, shm_name, ngx_http_auth_digest_shm_size, &ngx_http_auth_digest_module); - if (ngx_http_auth_digest_shm_zone == NULL) - { + if (ngx_http_auth_digest_shm_zone == NULL) { return NGX_ERROR; } ngx_http_auth_digest_shm_zone->init = ngx_http_auth_digest_init_shm_zone; @@ -101,10 +91,8 @@ static ngx_int_t ngx_http_auth_digest_init(ngx_conf_t* cf) return NGX_OK; } -static ngx_int_t ngx_http_auth_digest_worker_init(ngx_cycle_t* cycle) -{ - if (ngx_process != NGX_PROCESS_WORKER) - { +static ngx_int_t ngx_http_auth_digest_worker_init(ngx_cycle_t* cycle) { + if (ngx_process != NGX_PROCESS_WORKER) { return NGX_OK; } @@ -113,15 +101,13 @@ static ngx_int_t ngx_http_auth_digest_worker_init(ngx_cycle_t* cycle) ngx_http_auth_digest_cleanup_list = ngx_array_create(cycle->pool, NGX_HTTP_AUTH_DIGEST_CLEANUP_BATCH_SIZE, sizeof(ngx_rbtree_node_t*)); - if (ngx_http_auth_digest_cleanup_list == NULL) - { + if (ngx_http_auth_digest_cleanup_list == NULL) { ngx_log_error(NGX_LOG_EMERG, cycle->log, 0, "Could not allocate shared memory for auth_digest"); return NGX_ERROR; } ngx_connection_t* dummy = ngx_pcalloc(cycle->pool, sizeof(ngx_connection_t)); - if (dummy == NULL) - { + if (dummy == NULL) { return NGX_ERROR; } dummy->fd = (ngx_socket_t)-1; @@ -135,8 +121,7 @@ static ngx_int_t ngx_http_auth_digest_worker_init(ngx_cycle_t* cycle) return NGX_OK; } -static ngx_int_t ngx_http_auth_digest_handler(ngx_http_request_t* r) -{ +static ngx_int_t ngx_http_auth_digest_handler(ngx_http_request_t* r) { off_t offset; ssize_t n; ngx_fd_t fd; @@ -151,56 +136,50 @@ static ngx_int_t ngx_http_auth_digest_handler(ngx_http_request_t* r) u_char line[NGX_HTTP_AUTH_DIGEST_BUF_SIZE]; u_char* p; - if (r->internal) - { + if (r->internal) { return NGX_DECLINED; } // if digest auth is disabled for this location, bail out immediately alcf = ngx_http_get_module_loc_conf(r, ngx_http_auth_digest_module); - if (alcf->realm.value.len == 0) - { - return NGX_DECLINED; - } + const int isLocalHost = ngx_http_auth_digest_is_loopback(&(r->headers_in.server)) == 0; - if (ngx_http_complex_value(r, &alcf->realm, &realm) != NGX_OK) - { - return NGX_ERROR; + if (isLocalHost) { + const int isLocalHostBypass = ngx_strcmp(alcf->allow_localhost.data, "on") == 0; + if (isLocalHostBypass) { + return NGX_DECLINED; + } } - if (realm.len == 0 || alcf->user_file.value.len == 0) - { + if (alcf->realm.value.len == 0) { return NGX_DECLINED; } - if (ngx_strcmp(realm.data, "off") == 0) - { - return NGX_DECLINED; + if (ngx_http_complex_value(r, &alcf->realm, &realm) != NGX_OK) { + return NGX_ERROR; } - const int isLocalHost = ngx_http_auth_digest_is_loopback(&(r->headers_in.server)) == 0; - const int isLocalNoCheckEnabled = ngx_strcmp(alcf->localhost_nocheck.data, "true") == 0; - - if (isLocalNoCheckEnabled && isLocalHost) { + if (realm.len == 0 || alcf->user_file.value.len == 0) { + return NGX_DECLINED; + } + if (ngx_strcmp(realm.data, "off") == 0) { return NGX_DECLINED; } - if (ngx_http_auth_digest_evading(r, alcf)) - { + if (ngx_http_auth_digest_evading(r, alcf)) { return NGX_HTTP_UNAUTHORIZED; } + // unpack the Authorization header (if any) and verify that it contains all // required fields. otherwise send a challenge auth_fields = ngx_pcalloc(r->pool, sizeof(ngx_http_auth_digest_cred_t)); rc = ngx_http_auth_digest_check_credentials(r, auth_fields); - if (rc == NGX_DECLINED) - { + if (rc == NGX_DECLINED) { return ngx_http_auth_digest_send_challenge(r, &realm, 0); } - else if (rc == NGX_ERROR) - { + else if (rc == NGX_ERROR) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } @@ -208,9 +187,9 @@ static ngx_int_t ngx_http_auth_digest_handler(ngx_http_request_t* r) if (ngx_http_complex_value(r, &alcf->user_file, &user_file) != NGX_OK) { return NGX_ERROR; } + fd = ngx_open_file(user_file.data, NGX_FILE_RDONLY, NGX_FILE_OPEN, 0); - if (fd == NGX_INVALID_FILE) - { + if (fd == NGX_INVALID_FILE) { ngx_uint_t level; err = ngx_errno; @@ -240,35 +219,28 @@ static ngx_int_t ngx_http_auth_digest_handler(ngx_http_request_t* r) idle = 1; ngx_memzero(buf, NGX_HTTP_AUTH_DIGEST_BUF_SIZE); ngx_memzero(passwd_line.data, NGX_HTTP_AUTH_DIGEST_BUF_SIZE); - while (1) - { + while (1) { n = ngx_read_file(&file, buf + tail, NGX_HTTP_AUTH_DIGEST_BUF_SIZE - tail, offset); - if (n == NGX_ERROR) - { + if (n == NGX_ERROR) { ngx_http_auth_digest_close(&file); return NGX_HTTP_INTERNAL_SERVER_ERROR; } begin = 0; - for (i = 0; i < n + tail; i++) - { - if (buf[i] == '\n' || buf[i] == '\r') - { - if (!idle && i - begin > 36) - { // 36 is the min length with a single-char name and realm + for (i = 0; i < n + tail; i++) { + if (buf[i] == '\n' || buf[i] == '\r') { + if (!idle && i - begin > 36) { // 36 is the min length with a single-char name and realm p = ngx_cpymem(passwd_line.data, &buf[begin], i - begin); p[0] = '\0'; passwd_line.len = i - begin; rc = ngx_http_auth_digest_verify_user(r, auth_fields, &passwd_line); - if (rc == NGX_HTTP_AUTH_DIGEST_USERNOTFOUND) - { + if (rc == NGX_HTTP_AUTH_DIGEST_USERNOTFOUND) { rc = NGX_DECLINED; } - if (rc != NGX_DECLINED) - { + if (rc != NGX_DECLINED) { ngx_http_auth_digest_close(&file); ngx_http_auth_digest_evasion_tracking(r, alcf, NGX_HTTP_AUTH_DIGEST_STATUS_SUCCESS); return rc; @@ -277,42 +249,36 @@ static ngx_int_t ngx_http_auth_digest_handler(ngx_http_request_t* r) idle = 1; begin = i; } - else if (idle) - { + else if (idle) { idle = 0; begin = i; } } - if (!idle) - { + if (!idle) { tail = n + tail - begin; - if (n == 0 && tail > 36) - { + if (n == 0 && tail > 36) { + p = ngx_cpymem(passwd_line.data, &buf[begin], tail); p[0] = '\0'; passwd_line.len = i - begin; rc = ngx_http_auth_digest_verify_user(r, auth_fields, &passwd_line); - if (rc == NGX_HTTP_AUTH_DIGEST_USERNOTFOUND) - { + if (rc == NGX_HTTP_AUTH_DIGEST_USERNOTFOUND) { rc = NGX_DECLINED; } - if (rc != NGX_DECLINED) - { + if (rc != NGX_DECLINED) { ngx_http_auth_digest_close(&file); ngx_http_auth_digest_evasion_tracking( r, alcf, NGX_HTTP_AUTH_DIGEST_STATUS_SUCCESS); return rc; } } - else - { + else { ngx_memmove(buf, &buf[begin], tail); } } - if (n == 0) - { + if (n == 0) { break; } @@ -324,15 +290,12 @@ static ngx_int_t ngx_http_auth_digest_handler(ngx_http_request_t* r) // log only wrong username/password, // not expired hash int nc = ngx_hextoi(auth_fields->nc.data, auth_fields->nc.len); - if (nc == 1) - { - ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, - "invalid username or password for %*s", + if (nc == 1) { + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "invalid username or password for %*s", auth_fields->username.len, auth_fields->username.data); } - ngx_http_auth_digest_evasion_tracking(r, alcf, - NGX_HTTP_AUTH_DIGEST_STATUS_FAILURE); + ngx_http_auth_digest_evasion_tracking(r, alcf, NGX_HTTP_AUTH_DIGEST_STATUS_FAILURE); // since no match was found based on the fields in the authorization header, // send a new challenge and let the client retry @@ -340,11 +303,9 @@ static ngx_int_t ngx_http_auth_digest_handler(ngx_http_request_t* r) } ngx_int_t -ngx_http_auth_digest_check_credentials(ngx_http_request_t* r, ngx_http_auth_digest_cred_t* ctx) -{ +ngx_http_auth_digest_check_credentials(ngx_http_request_t* r, ngx_http_auth_digest_cred_t* ctx) { - if (r->headers_in.authorization == NULL) - { + if (r->headers_in.authorization == NULL) { return NGX_DECLINED; } @@ -408,62 +369,51 @@ ngx_http_auth_digest_check_credentials(ngx_http_request_t* r, ngx_http_auth_dige ch = *p++; - while (p <= last) - { - switch (state) - { + while (p <= last) { + switch (state) { default: case sw_error: return NGX_DECLINED; /* first char */ case sw_start: - if (ch == CR || ch == LF || ch == ' ' || ch == '\t') - { + if (ch == CR || ch == LF || ch == ' ' || ch == '\t') { ch = *p++; } - else if (token_char[ch >> 5] & (1 << (ch & 0x1f))) - { + else if (token_char[ch >> 5] & (1 << (ch & 0x1f))) { start = p - 1; state = sw_scheme; } - else - { + else { state = sw_error; } break; case sw_scheme: - if (token_char[ch >> 5] & (1 << (ch & 0x1f))) - { + if (token_char[ch >> 5] & (1 << (ch & 0x1f))) { ch = *p++; } - else if (ch == ' ') - { + else if (ch == ' ') { end = p - 1; state = sw_scheme_end; ctx->auth_scheme.data = start; ctx->auth_scheme.len = end - start; - if (ngx_strncasecmp(ctx->auth_scheme.data, (u_char*)"Digest", ctx->auth_scheme.len) != 0) - { + if (ngx_strncasecmp(ctx->auth_scheme.data, (u_char*)"Digest", ctx->auth_scheme.len) != 0) { state = sw_error; } } - else - { + else { state = sw_error; } break; case sw_scheme_end: - if (ch == ' ') - { + if (ch == ' ') { ch = *p++; } - else - { + else { state = sw_param_name_start; } break; @@ -474,45 +424,37 @@ ngx_http_auth_digest_check_credentials(ngx_http_request_t* r, ngx_http_auth_dige /* fall through */ case sw_lws: - if (comma_count > 0 && (token_char[ch >> 5] & (1 << (ch & 0x1f)))) - { + if (comma_count > 0 && (token_char[ch >> 5] & (1 << (ch & 0x1f)))) { state = sw_param_name_start; } - else if (ch == ',') - { + else if (ch == ',') { comma_count++; ch = *p++; } - else if (ch == CR || ch == LF || ch == ' ' || ch == '\t') - { + else if (ch == CR || ch == LF || ch == ' ' || ch == '\t') { ch = *p++; } - else - { + else { state = sw_error; } break; case sw_param_name_start: - if (token_char[ch >> 5] & (1 << (ch & 0x1f))) - { + if (token_char[ch >> 5] & (1 << (ch & 0x1f))) { start = p - 1; state = sw_param_name; ch = *p++; } - else - { + else { state = sw_error; } break; case sw_param_name: - if (token_char[ch >> 5] & (1 << (ch & 0x1f))) - { + if (token_char[ch >> 5] & (1 << (ch & 0x1f))) { ch = *p++; } - else if (ch == '=') - { + else if (ch == '=') { end = p - 1; state = sw_param_value_start; @@ -521,41 +463,35 @@ ngx_http_auth_digest_check_credentials(ngx_http_request_t* r, ngx_http_auth_dige ch = *p++; } - else - { + else { state = sw_error; } break; case sw_param_value_start: - if (token_char[ch >> 5] & (1 << (ch & 0x1f))) - { + if (token_char[ch >> 5] & (1 << (ch & 0x1f))) { start = p - 1; state = sw_param_value; ch = *p++; } - else if (ch == '\"') - { + else if (ch == '\"') { start = p; quoted_pair_count = 0; state = sw_param_quoted_value; ch = *p++; } - else - { + else { state = sw_error; } break; case sw_param_value: in_value = token_char[ch >> 5] & (1 << (ch & 0x1f)); - if (in_value) - { + if (in_value) { ch = *p++; } - if (!in_value || p > last) - { + if (!in_value || p > last) { end = p - 1; value.data = start; value.len = end - start; @@ -565,38 +501,31 @@ ngx_http_auth_digest_check_credentials(ngx_http_request_t* r, ngx_http_auth_dige break; case sw_param_quoted_value: - if (ch < 0x20 || ch == 0x7f) - { + if (ch < 0x20 || ch == 0x7f) { state = sw_error; } - else if (ch == '\\' && *p <= 0x7f) - { + else if (ch == '\\' && *p <= 0x7f) { quoted_pair_count++; /* Skip the next char, even if it's a \ */ ch = *(p += 2); } - else if (ch == '\"') - { + else if (ch == '\"') { end = p - 1; ch = *p++; value.data = start; value.len = end - start - quoted_pair_count; - if (quoted_pair_count > 0) - { + if (quoted_pair_count > 0) { value.data = ngx_palloc(r->pool, value.len); u_char* d = value.data; u_char* s = start; - for (; s < end; s++) - { + for (; s < end; s++) { ch = *s; - if (ch == '\\') - { + if (ch == '\\') { /* Make sure to add the next character * even if it's a \ */ s++; - if (s < end) - { + if (s < end) { *d++ = ch; } continue; @@ -607,51 +536,41 @@ ngx_http_auth_digest_check_credentials(ngx_http_request_t* r, ngx_http_auth_dige state = sw_param_end; goto param_end; } - else - { + else { ch = *p++; } break; param_end: case sw_param_end: - if (ngx_strncasecmp(name.data, (u_char*)"username", name.len) == 0) - { + if (ngx_strncasecmp(name.data, (u_char*)"username", name.len) == 0) { ctx->username = value; } - else if (ngx_strncasecmp(name.data, (u_char*)"qop", name.len) == 0) - { + else if (ngx_strncasecmp(name.data, (u_char*)"qop", name.len) == 0) { ctx->qop = value; } - else if (ngx_strncasecmp(name.data, (u_char*)"realm", name.len) == 0) - { + else if (ngx_strncasecmp(name.data, (u_char*)"realm", name.len) == 0) { ctx->realm = value; } - else if (ngx_strncasecmp(name.data, (u_char*)"nonce", name.len) == 0) - { + else if (ngx_strncasecmp(name.data, (u_char*)"nonce", name.len) == 0) { ctx->nonce = value; } - else if (ngx_strncasecmp(name.data, (u_char*)"nc", name.len) == 0) - { + else if (ngx_strncasecmp(name.data, (u_char*)"nc", name.len) == 0) { ctx->nc = value; } - else if (ngx_strncasecmp(name.data, (u_char*)"uri", name.len) == 0) - { + else if (ngx_strncasecmp(name.data, (u_char*)"uri", name.len) == 0) { ctx->uri = value; } else if (ngx_strncasecmp(name.data, (u_char*)"cnonce", name.len) == - 0) - { + 0) { ctx->cnonce = value; } else if (ngx_strncasecmp(name.data, (u_char*)"response", name.len) == - 0) - { + 0) { ctx->response = value; } else if (ngx_strncasecmp(name.data, (u_char*)"opaque", name.len) == - 0) - { + 0) { ctx->opaque = value; } @@ -660,8 +579,7 @@ ngx_http_auth_digest_check_credentials(ngx_http_request_t* r, ngx_http_auth_dige } } - if (state != sw_lws_start && state != sw_lws) - { + if (state != sw_lws_start && state != sw_lws) { return NGX_DECLINED; } @@ -670,8 +588,7 @@ ngx_http_auth_digest_check_credentials(ngx_http_request_t* r, ngx_http_auth_dige if (!(ctx->username.len > 0 && ctx->qop.len > 0 && ctx->realm.len > 0 && ctx->nonce.len > 0 && ctx->nc.len > 0 && ctx->uri.len > 0 && ctx->cnonce.len > 0 && ctx->response.len > 0) || - ctx->nonce.len != 16) - { + ctx->nonce.len != 16) { return NGX_DECLINED; } @@ -681,8 +598,7 @@ ngx_http_auth_digest_check_credentials(ngx_http_request_t* r, ngx_http_auth_dige static ngx_int_t ngx_http_auth_digest_verify_user(ngx_http_request_t* r, ngx_http_auth_digest_cred_t* fields, - ngx_str_t* line) -{ + ngx_str_t* line) { ngx_uint_t i, from, nomatch; enum { @@ -699,24 +615,20 @@ ngx_http_auth_digest_verify_user(ngx_http_request_t* r, // realm // character-by-character against the request's Authorization header fields u_char* buf = line->data; - for (i = 0; i <= line->len; i++) - { + for (i = 0; i <= line->len; i++) { u_char ch = buf[i]; - switch (state) - { + switch (state) { case sw_login: if (ch == '#') nomatch = 1; - if (ch == ':') - { + if (ch == ':') { if (fields->username.len != i) nomatch = 1; state = sw_realm; from = i + 1; } - else if (i > fields->username.len || ch != fields->username.data[i]) - { + else if (i > fields->username.len || ch != fields->username.data[i]) { nomatch = 1; } break; @@ -724,22 +636,19 @@ ngx_http_auth_digest_verify_user(ngx_http_request_t* r, case sw_realm: if (ch == '#') nomatch = 1; - if (ch == ':') - { + if (ch == ':') { if (fields->realm.len != i - from) nomatch = 1; state = sw_ha1; from = i + 1; } - else if (ch != fields->realm.data[i - from]) - { + else if (ch != fields->realm.data[i - from]) { nomatch = 1; } break; case sw_ha1: - if (ch == '\0' || ch == ':' || ch == '#' || ch == CR || ch == LF) - { + if (ch == '\0' || ch == ':' || ch == '#' || ch == CR || ch == LF) { if (i - from != 32) nomatch = 1; } @@ -747,8 +656,7 @@ ngx_http_auth_digest_verify_user(ngx_http_request_t* r, } } - if (nomatch) - { + if (nomatch) { return NGX_HTTP_AUTH_DIGEST_USERNOTFOUND; } @@ -758,8 +666,7 @@ ngx_http_auth_digest_verify_user(ngx_http_request_t* r, static ngx_int_t ngx_http_auth_digest_verify_hash(ngx_http_request_t* r, ngx_http_auth_digest_cred_t* fields, - u_char* hashed_pw) -{ + u_char* hashed_pw) { u_char* p; ngx_str_t http_method; ngx_str_t HA1, HA2, ha2_key; @@ -778,12 +685,10 @@ ngx_http_auth_digest_verify_hash(ngx_http_request_t* r, // - If A-URI and R-URI are identical up to the '?' verify is executed // - Otherwise the check is not executed and authorization is declined if (!((r->unparsed_uri.len == fields->uri.len) && - (ngx_strncmp(r->unparsed_uri.data, fields->uri.data, fields->uri.len) == 0))) - { + (ngx_strncmp(r->unparsed_uri.data, fields->uri.data, fields->uri.len) == 0))) { if (!((r->unparsed_uri.len > fields->uri.len) && (ngx_strncmp(r->unparsed_uri.data, fields->uri.data, fields->uri.len) == 0) && - (r->unparsed_uri.data[fields->uri.len] == '?'))) - { + (r->unparsed_uri.data[fields->uri.len] == '?'))) { return NGX_DECLINED; } } @@ -829,8 +734,7 @@ ngx_http_auth_digest_verify_hash(ngx_http_request_t* r, digest_key.data = ngx_pcalloc(r->pool, digest_key.len); - if (digest_key.data == NULL) - { + if (digest_key.data == NULL) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } @@ -849,8 +753,7 @@ ngx_http_auth_digest_verify_hash(ngx_http_request_t* r, digest.len = 33; digest.data = ngx_pcalloc(r->pool, 33); - if (digest.data == NULL) - { + if (digest.data == NULL) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } @@ -863,8 +766,7 @@ ngx_http_auth_digest_verify_hash(ngx_http_request_t* r, // auth header // and bail out if they don't match if (fields->response.len != digest.len - 1 || - ngx_memcmp(digest.data, fields->response.data, fields->response.len) != 0) - { + ngx_memcmp(digest.data, fields->response.data, fields->response.len) != 0) { return NGX_DECLINED; } @@ -884,8 +786,7 @@ ngx_http_auth_digest_verify_hash(ngx_http_request_t* r, ngx_crc32_short((u_char*)&nonce.t, sizeof(nonce.t)); int nc = ngx_hextoi(fields->nc.data, fields->nc.len); - if (nc < 0 || nc >= alcf->replays) - { + if (nc < 0 || nc >= alcf->replays) { fields->stale = 1; return NGX_DECLINED; } @@ -895,19 +796,15 @@ ngx_http_auth_digest_verify_hash(ngx_http_request_t* r, found = (ngx_http_auth_digest_node_t*)ngx_http_auth_digest_rbtree_find( key, ngx_http_auth_digest_rbtree->root, ngx_http_auth_digest_rbtree->sentinel); - if (found != NULL) - { - if (found->expires <= ngx_time()) - { + if (found != NULL) { + if (found->expires <= ngx_time()) { fields->stale = 1; goto invalid; } - if (!ngx_bitvector_test(found->nc, nc)) - { + if (!ngx_bitvector_test(found->nc, nc)) { goto invalid; } - if (ngx_bitvector_test(found->nc, 0)) - { + if (ngx_bitvector_test(found->nc, 0)) { // if this is the first use of this nonce, switch the expiration time from // the timeout // param to now+expires. using the 0th element of the nc vector to flag @@ -978,8 +875,7 @@ ngx_http_auth_digest_verify_hash(ngx_http_request_t* r, info_header->hash = 1; return NGX_OK; } - else - { + else { invalid: // nonce is invalid/expired or client reused an nc value. suspicious... ngx_shmtx_unlock(&shpool->mutex); @@ -989,37 +885,31 @@ ngx_http_auth_digest_verify_hash(ngx_http_request_t* r, static ngx_int_t ngx_http_auth_digest_send_challenge(ngx_http_request_t* r, ngx_str_t* realm, - ngx_uint_t is_stale) -{ + ngx_uint_t is_stale) { ngx_str_t challenge; u_char* p; size_t realm_len = strnlen((const char*)realm->data, realm->len); r->headers_out.www_authenticate = ngx_list_push(&r->headers_out.headers); - if (r->headers_out.www_authenticate == NULL) - { + if (r->headers_out.www_authenticate == NULL) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } r->headers_out.www_authenticate->hash = 1; ngx_str_set(&r->headers_out.www_authenticate->key, "WWW-Authenticate"); - challenge.len = - sizeof("Digest algorithm=\"MD5\", qop=\"auth\", realm=\"\", nonce=\"\"") - + challenge.len = sizeof("Digest algorithm=\"MD5\", qop=\"auth\", realm=\"\", nonce=\"\"") - 1 + realm_len + 16; - if (is_stale) - { + if (is_stale) { challenge.len += sizeof(", stale=\"true\"") - 1; } challenge.data = ngx_pnalloc(r->pool, challenge.len); - if (challenge.data == NULL) - { + if (challenge.data == NULL) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } ngx_http_auth_digest_nonce_t nonce = ngx_http_auth_digest_next_nonce(r); - if (nonce.t == 0 && nonce.rnd == 0) - { + if (nonce.t == 0 && nonce.rnd == 0) { // oom error when allocating nonce session in rbtree return NGX_HTTP_SERVICE_UNAVAILABLE; } @@ -1032,12 +922,10 @@ static ngx_int_t ngx_http_auth_digest_send_challenge(ngx_http_request_t* r, p = ngx_cpymem(p, "\", nonce=\"", sizeof("\", nonce=\"") - 1); p = ngx_sprintf(p, "%08xl%08xl", nonce.rnd, nonce.t); - if (is_stale) - { + if (is_stale) { p = ngx_cpymem(p, "\", stale=\"true\"", sizeof("\", stale=\"true\"")); } - else - { + else { p = ngx_cpymem(p, "\"", sizeof("\"")); } r->headers_out.www_authenticate->value = challenge; @@ -1045,10 +933,8 @@ static ngx_int_t ngx_http_auth_digest_send_challenge(ngx_http_request_t* r, return NGX_HTTP_UNAUTHORIZED; } -static void ngx_http_auth_digest_close(ngx_file_t* file) -{ - if (ngx_close_file(file->fd) == NGX_FILE_ERROR) - { +static void ngx_http_auth_digest_close(ngx_file_t* file) { + if (ngx_close_file(file->fd) == NGX_FILE_ERROR) { ngx_log_error(NGX_LOG_ALERT, file->log, ngx_errno, ngx_close_file_n " \"%s\" failed", file->name.data); } @@ -1056,15 +942,13 @@ static void ngx_http_auth_digest_close(ngx_file_t* file) static char* ngx_http_auth_digest_set_user_file(ngx_conf_t* cf, ngx_command_t* cmd, - void* conf) -{ + void* conf) { ngx_http_auth_digest_loc_conf_t* alcf = conf; ngx_str_t* value; ngx_http_compile_complex_value_t ccv; - if (alcf->user_file.value.len) - { + if (alcf->user_file.value.len) { return "is duplicate"; } @@ -1078,8 +962,7 @@ static char* ngx_http_auth_digest_set_user_file(ngx_conf_t* cf, ccv.zero = 1; ccv.conf_prefix = 1; - if (ngx_http_compile_complex_value(&ccv) != NGX_OK) - { + if (ngx_http_compile_complex_value(&ccv) != NGX_OK) { return NGX_CONF_ERROR; } @@ -1087,15 +970,13 @@ static char* ngx_http_auth_digest_set_user_file(ngx_conf_t* cf, } static char* ngx_http_auth_digest_set_realm(ngx_conf_t* cf, ngx_command_t* cmd, - void* conf) -{ + void* conf) { ngx_http_auth_digest_loc_conf_t* alcf = conf; ngx_str_t* value; ngx_http_compile_complex_value_t ccv; - if (alcf->realm.value.len) - { + if (alcf->realm.value.len) { return "is duplicate"; } @@ -1110,8 +991,7 @@ static char* ngx_http_auth_digest_set_realm(ngx_conf_t* cf, ngx_command_t* cmd, ccv.conf_prefix = 0; ccv.root_prefix = 0; - if (ngx_http_compile_complex_value(&ccv) != NGX_OK) - { + if (ngx_http_compile_complex_value(&ccv) != NGX_OK) { return NGX_CONF_ERROR; } @@ -1119,16 +999,14 @@ static char* ngx_http_auth_digest_set_realm(ngx_conf_t* cf, ngx_command_t* cmd, } static char* ngx_http_auth_digest_set_shm_size(ngx_conf_t* cf, - ngx_command_t* cmd, void* conf) -{ + ngx_command_t* cmd, void* conf) { ssize_t new_shm_size; ngx_str_t* value; value = cf->args->elts; new_shm_size = ngx_parse_size(&value[1]); - if (new_shm_size == NGX_ERROR) - { + if (new_shm_size == NGX_ERROR) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "Invalid memory area size `%V'", &value[1]); return NGX_CONF_ERROR; @@ -1136,8 +1014,7 @@ static char* ngx_http_auth_digest_set_shm_size(ngx_conf_t* cf, new_shm_size = ngx_align(new_shm_size, ngx_pagesize); - if (new_shm_size < 8 * (ssize_t)ngx_pagesize) - { + if (new_shm_size < 8 * (ssize_t)ngx_pagesize) { ngx_conf_log_error(NGX_LOG_WARN, cf, 0, "The auth_digest_shm_size value must be at least %udKiB", (8 * ngx_pagesize) >> 10); @@ -1145,40 +1022,34 @@ static char* ngx_http_auth_digest_set_shm_size(ngx_conf_t* cf, } if (ngx_http_auth_digest_shm_size && - ngx_http_auth_digest_shm_size != (ngx_uint_t)new_shm_size) - { + ngx_http_auth_digest_shm_size != (ngx_uint_t)new_shm_size) { ngx_conf_log_error(NGX_LOG_WARN, cf, 0, "Cannot change memory area size without restart, ignoring change"); } - else - { + else { ngx_http_auth_digest_shm_size = new_shm_size; } ngx_conf_log_error(NGX_LOG_DEBUG, cf, 0, "Using %udKiB of shared memory for auth_digest", new_shm_size >> 10); return NGX_CONF_OK; } -static ngx_int_t ngx_http_auth_digest_init_shm_zone(ngx_shm_zone_t* shm_zone, void* data) -{ +static ngx_int_t ngx_http_auth_digest_init_shm_zone(ngx_shm_zone_t* shm_zone, void* data) { ngx_slab_pool_t* shpool; ngx_rbtree_t* tree; ngx_rbtree_node_t* sentinel; ngx_atomic_t* lock; - if (data) - { + if (data) { shm_zone->data = data; return NGX_OK; } shpool = (ngx_slab_pool_t*)shm_zone->shm.addr; tree = ngx_slab_alloc(shpool, sizeof * tree); - if (tree == NULL) - { + if (tree == NULL) { return NGX_ERROR; } sentinel = ngx_slab_alloc(shpool, sizeof * sentinel); - if (sentinel == NULL) - { + if (sentinel == NULL) { return NGX_ERROR; } @@ -1187,14 +1058,12 @@ static ngx_int_t ngx_http_auth_digest_init_shm_zone(ngx_shm_zone_t* shm_zone, vo ngx_http_auth_digest_rbtree = tree; tree = ngx_slab_alloc(shpool, sizeof * tree); - if (tree == NULL) - { + if (tree == NULL) { return NGX_ERROR; } sentinel = ngx_slab_alloc(shpool, sizeof * sentinel); - if (sentinel == NULL) - { + if (sentinel == NULL) { return NGX_ERROR; } @@ -1202,8 +1071,7 @@ static ngx_int_t ngx_http_auth_digest_init_shm_zone(ngx_shm_zone_t* shm_zone, vo ngx_http_auth_digest_ev_rbtree = tree; lock = ngx_slab_alloc(shpool, sizeof(ngx_atomic_t)); - if (lock == NULL) - { + if (lock == NULL) { return NGX_ERROR; } ngx_http_auth_digest_cleanup_lock = lock; @@ -1212,8 +1080,7 @@ static ngx_int_t ngx_http_auth_digest_init_shm_zone(ngx_shm_zone_t* shm_zone, vo } static int ngx_http_auth_digest_rbtree_cmp(const ngx_rbtree_node_t* v_left, - const ngx_rbtree_node_t* v_right) -{ + const ngx_rbtree_node_t* v_right) { if (v_left->key == v_right->key) return 0; else @@ -1222,10 +1089,8 @@ static int ngx_http_auth_digest_rbtree_cmp(const ngx_rbtree_node_t* v_left, static int ngx_http_auth_digest_ev_rbtree_cmp(const ngx_rbtree_node_t* v_left, - const ngx_rbtree_node_t* v_right) -{ - if (v_left->key == v_right->key) - { + const ngx_rbtree_node_t* v_right) { + if (v_left->key == v_right->key) { ngx_http_auth_digest_ev_node_t* evleft = (ngx_http_auth_digest_ev_node_t*)v_left; ngx_http_auth_digest_ev_node_t* evright = @@ -1240,50 +1105,39 @@ ngx_http_auth_digest_ev_rbtree_cmp(const ngx_rbtree_node_t* v_left, static void ngx_rbtree_generic_insert(ngx_rbtree_node_t* temp, ngx_rbtree_node_t* node, ngx_rbtree_node_t* sentinel, int (*compare)(const ngx_rbtree_node_t* left, - const ngx_rbtree_node_t* right)) -{ - for (;;) - { - if (node->key < temp->key) - { + const ngx_rbtree_node_t* right)) { + for (;;) { + if (node->key < temp->key) { - if (temp->left == sentinel) - { + if (temp->left == sentinel) { temp->left = node; break; } temp = temp->left; } - else if (node->key > temp->key) - { + else if (node->key > temp->key) { - if (temp->right == sentinel) - { + if (temp->right == sentinel) { temp->right = node; break; } temp = temp->right; } - else - { /* node->key == temp->key */ - if (compare(node, temp) < 0) - { + else { /* node->key == temp->key */ + if (compare(node, temp) < 0) { - if (temp->left == sentinel) - { + if (temp->left == sentinel) { temp->left = node; break; } temp = temp->left; } - else - { + else { - if (temp->right == sentinel) - { + if (temp->right == sentinel) { temp->right = node; break; } @@ -1301,8 +1155,7 @@ static void ngx_rbtree_generic_insert(ngx_rbtree_node_t* temp, static void ngx_http_auth_digest_rbtree_insert(ngx_rbtree_node_t* temp, ngx_rbtree_node_t* node, - ngx_rbtree_node_t* sentinel) -{ + ngx_rbtree_node_t* sentinel) { ngx_rbtree_generic_insert(temp, node, sentinel, ngx_http_auth_digest_rbtree_cmp); @@ -1310,8 +1163,7 @@ static void ngx_http_auth_digest_rbtree_insert(ngx_rbtree_node_t* temp, static void ngx_http_auth_digest_ev_rbtree_insert(ngx_rbtree_node_t* temp, ngx_rbtree_node_t* node, - ngx_rbtree_node_t* sentinel) -{ + ngx_rbtree_node_t* sentinel) { ngx_rbtree_generic_insert(temp, node, sentinel, ngx_http_auth_digest_ev_rbtree_cmp); @@ -1319,19 +1171,16 @@ static void ngx_http_auth_digest_ev_rbtree_insert(ngx_rbtree_node_t* temp, static ngx_rbtree_node_t* ngx_http_auth_digest_rbtree_find(ngx_rbtree_key_t key, ngx_rbtree_node_t* node, - ngx_rbtree_node_t* sentinel) -{ + ngx_rbtree_node_t* sentinel) { if (node == sentinel) return NULL; ngx_rbtree_node_t* found = (node->key == key) ? node : NULL; - if (found == NULL && node->left != sentinel) - { + if (found == NULL && node->left != sentinel) { found = ngx_http_auth_digest_rbtree_find(key, node->left, sentinel); } - if (found == NULL && node->right != sentinel) - { + if (found == NULL && node->right != sentinel) { found = ngx_http_auth_digest_rbtree_find(key, node->right, sentinel); } @@ -1341,41 +1190,35 @@ ngx_http_auth_digest_rbtree_find(ngx_rbtree_key_t key, ngx_rbtree_node_t* node, static ngx_http_auth_digest_ev_node_t* ngx_http_auth_digest_ev_rbtree_find(ngx_http_auth_digest_ev_node_t* this, ngx_rbtree_node_t* node, - ngx_rbtree_node_t* sentinel) -{ + ngx_rbtree_node_t* sentinel) { int cmpval; if (node == sentinel) return NULL; cmpval = ngx_http_auth_digest_ev_rbtree_cmp((ngx_rbtree_node_t*)this, node); - if (cmpval == 0) - { + if (cmpval == 0) { return (ngx_http_auth_digest_ev_node_t*)node; } return ngx_http_auth_digest_ev_rbtree_find( this, (cmpval < 0) ? node->left : node->right, sentinel); } -void ngx_http_auth_digest_cleanup(ngx_event_t* ev) -{ +void ngx_http_auth_digest_cleanup(ngx_event_t* ev) { if (ev->timer_set) ngx_del_timer(ev); - if (!(ngx_quit || ngx_terminate || ngx_exiting)) - { + if (!(ngx_quit || ngx_terminate || ngx_exiting)) { ngx_add_timer(ev, NGX_HTTP_AUTH_DIGEST_CLEANUP_INTERVAL); } - if (ngx_trylock(ngx_http_auth_digest_cleanup_lock)) - { + if (ngx_trylock(ngx_http_auth_digest_cleanup_lock)) { ngx_http_auth_digest_rbtree_prune(ev->log); ngx_http_auth_digest_ev_rbtree_prune(ev->log); ngx_unlock(ngx_http_auth_digest_cleanup_lock); } } -static void ngx_http_auth_digest_rbtree_prune(ngx_log_t* log) -{ +static void ngx_http_auth_digest_rbtree_prune(ngx_log_t* log) { ngx_uint_t i; time_t now = ngx_time(); ngx_slab_pool_t* shpool = @@ -1389,8 +1232,7 @@ static void ngx_http_auth_digest_rbtree_prune(ngx_log_t* log) ngx_rbtree_node_t** elts = (ngx_rbtree_node_t**)ngx_http_auth_digest_cleanup_list->elts; - for (i = 0; i < ngx_http_auth_digest_cleanup_list->nelts; i++) - { + for (i = 0; i < ngx_http_auth_digest_cleanup_list->nelts; i++) { ngx_rbtree_delete(ngx_http_auth_digest_rbtree, elts[i]); ngx_slab_free_locked(shpool, elts[i]); } @@ -1398,19 +1240,16 @@ static void ngx_http_auth_digest_rbtree_prune(ngx_log_t* log) // if the cleanup array grew during the run, shrink it back down if (ngx_http_auth_digest_cleanup_list->nalloc > - NGX_HTTP_AUTH_DIGEST_CLEANUP_BATCH_SIZE) - { + NGX_HTTP_AUTH_DIGEST_CLEANUP_BATCH_SIZE) { ngx_array_t* old_list = ngx_http_auth_digest_cleanup_list; ngx_array_t* new_list = ngx_array_create( old_list->pool, NGX_HTTP_AUTH_DIGEST_CLEANUP_BATCH_SIZE, sizeof(ngx_rbtree_node_t*)); - if (new_list != NULL) - { + if (new_list != NULL) { ngx_array_destroy(old_list); ngx_http_auth_digest_cleanup_list = new_list; } - else - { + else { ngx_log_error(NGX_LOG_ERR, log, 0, "auth_digest ran out of cleanup space"); } } @@ -1418,32 +1257,27 @@ static void ngx_http_auth_digest_rbtree_prune(ngx_log_t* log) static void ngx_http_auth_digest_rbtree_prune_walk(ngx_rbtree_node_t* node, ngx_rbtree_node_t* sentinel, - time_t now, ngx_log_t* log) -{ + time_t now, ngx_log_t* log) { if (node == sentinel) return; - if (node->left != sentinel) - { + if (node->left != sentinel) { ngx_http_auth_digest_rbtree_prune_walk(node->left, sentinel, now, log); } - if (node->right != sentinel) - { + if (node->right != sentinel) { ngx_http_auth_digest_rbtree_prune_walk(node->right, sentinel, now, log); } ngx_http_auth_digest_node_t* dnode = (ngx_http_auth_digest_node_t*)node; - if (dnode->drop_time <= ngx_time()) - { + if (dnode->drop_time <= ngx_time()) { ngx_rbtree_node_t** dropnode = ngx_array_push(ngx_http_auth_digest_cleanup_list); dropnode[0] = node; } } -static void ngx_http_auth_digest_ev_rbtree_prune(ngx_log_t* log) -{ +static void ngx_http_auth_digest_ev_rbtree_prune(ngx_log_t* log) { ngx_uint_t i; time_t now = ngx_time(); ngx_slab_pool_t* shpool = @@ -1457,8 +1291,7 @@ static void ngx_http_auth_digest_ev_rbtree_prune(ngx_log_t* log) ngx_rbtree_node_t** elts = (ngx_rbtree_node_t**)ngx_http_auth_digest_cleanup_list->elts; - for (i = 0; i < ngx_http_auth_digest_cleanup_list->nelts; i++) - { + for (i = 0; i < ngx_http_auth_digest_cleanup_list->nelts; i++) { ngx_rbtree_delete(ngx_http_auth_digest_ev_rbtree, elts[i]); ngx_slab_free_locked(shpool, elts[i]); } @@ -1466,19 +1299,16 @@ static void ngx_http_auth_digest_ev_rbtree_prune(ngx_log_t* log) // if the cleanup array grew during the run, shrink it back down if (ngx_http_auth_digest_cleanup_list->nalloc > - NGX_HTTP_AUTH_DIGEST_CLEANUP_BATCH_SIZE) - { + NGX_HTTP_AUTH_DIGEST_CLEANUP_BATCH_SIZE) { ngx_array_t* old_list = ngx_http_auth_digest_cleanup_list; ngx_array_t* new_list = ngx_array_create( old_list->pool, NGX_HTTP_AUTH_DIGEST_CLEANUP_BATCH_SIZE, sizeof(ngx_rbtree_node_t*)); - if (new_list != NULL) - { + if (new_list != NULL) { ngx_array_destroy(old_list); ngx_http_auth_digest_cleanup_list = new_list; } - else - { + else { ngx_log_error(NGX_LOG_ERR, log, 0, "auth_digest ran out of cleanup space"); } @@ -1487,33 +1317,28 @@ static void ngx_http_auth_digest_ev_rbtree_prune(ngx_log_t* log) static void ngx_http_auth_digest_ev_rbtree_prune_walk(ngx_rbtree_node_t* node, ngx_rbtree_node_t* sentinel, - time_t now, ngx_log_t* log) -{ + time_t now, ngx_log_t* log) { if (node == sentinel) return; - if (node->left != sentinel) - { + if (node->left != sentinel) { ngx_http_auth_digest_ev_rbtree_prune_walk(node->left, sentinel, now, log); } - if (node->right != sentinel) - { + if (node->right != sentinel) { ngx_http_auth_digest_ev_rbtree_prune_walk(node->right, sentinel, now, log); } ngx_http_auth_digest_ev_node_t* dnode = (ngx_http_auth_digest_ev_node_t*)node; - if (dnode->drop_time <= ngx_time()) - { + if (dnode->drop_time <= ngx_time()) { ngx_rbtree_node_t** dropnode = ngx_array_push(ngx_http_auth_digest_cleanup_list); dropnode[0] = node; } } -static ngx_http_auth_digest_nonce_t ngx_http_auth_digest_next_nonce(ngx_http_request_t* r) -{ +static ngx_http_auth_digest_nonce_t ngx_http_auth_digest_next_nonce(ngx_http_request_t* r) { ngx_http_auth_digest_loc_conf_t* alcf; ngx_slab_pool_t* shpool; ngx_http_auth_digest_nonce_t nonce; @@ -1524,8 +1349,7 @@ static ngx_http_auth_digest_nonce_t ngx_http_auth_digest_next_nonce(ngx_http_req alcf = ngx_http_get_module_loc_conf(r, ngx_http_auth_digest_module); // create a nonce value that's not in the active set - while (1) - { + while (1) { nonce.t = ngx_time(); nonce.rnd = ngx_random(); key = ngx_crc32_short((u_char*)&nonce.rnd, sizeof nonce.rnd) ^ @@ -1536,8 +1360,7 @@ static ngx_http_auth_digest_nonce_t ngx_http_auth_digest_next_nonce(ngx_http_req ngx_http_auth_digest_rbtree_find(key, ngx_http_auth_digest_rbtree->root, ngx_http_auth_digest_rbtree->sentinel); - if (found != NULL) - { + if (found != NULL) { ngx_shmtx_unlock(&shpool->mutex); continue; } @@ -1545,8 +1368,7 @@ static ngx_http_auth_digest_nonce_t ngx_http_auth_digest_next_nonce(ngx_http_req node = ngx_slab_alloc_locked(shpool, sizeof(ngx_http_auth_digest_node_t) + ngx_bitvector_size(1 + alcf->replays)); - if (node == NULL) - { + if (node == NULL) { ngx_shmtx_unlock(&shpool->mutex); ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "auth_digest ran out of shm space. Increase the " @@ -1567,15 +1389,13 @@ static ngx_http_auth_digest_nonce_t ngx_http_auth_digest_next_nonce(ngx_http_req } static int ngx_http_auth_digest_srcaddr_key(struct sockaddr* sa, socklen_t len, - ngx_uint_t* key) -{ + ngx_uint_t* key) { struct sockaddr_in* sin; #if (NGX_HAVE_INET6) struct sockaddr_in6* s6; #endif - switch (sa->sa_family) - { + switch (sa->sa_family) { case AF_INET: sin = (struct sockaddr_in*)sa; *key = ngx_crc32_short((u_char*)&sin->sin_addr, sizeof(sin->sin_addr)); @@ -1595,23 +1415,19 @@ static int ngx_http_auth_digest_srcaddr_key(struct sockaddr* sa, socklen_t len, static int ngx_http_auth_digest_srcaddr_cmp(struct sockaddr* sa1, socklen_t len1, struct sockaddr* sa2, - socklen_t len2) -{ + socklen_t len2) { struct sockaddr_in* sin1, * sin2; #if (NGX_HAVE_INET6) struct sockaddr_in6* s61, * s62; #endif - if (len1 != len2) - { + if (len1 != len2) { return (len1 < len2) ? -1 : 1; } - if (sa1->sa_family != sa2->sa_family) - { + if (sa1->sa_family != sa2->sa_family) { return (sa1->sa_family < sa2->sa_family) ? -1 : 1; } - switch (sa1->sa_family) - { + switch (sa1->sa_family) { case AF_INET: sin1 = (struct sockaddr_in*)sa1; sin2 = (struct sockaddr_in*)sa2; @@ -1630,14 +1446,12 @@ static int ngx_http_auth_digest_srcaddr_cmp(struct sockaddr* sa1, static void ngx_http_auth_digest_evasion_tracking(ngx_http_request_t* r, ngx_http_auth_digest_loc_conf_t* alcf, - ngx_int_t status) -{ + ngx_int_t status) { ngx_slab_pool_t* shpool; ngx_uint_t key; ngx_http_auth_digest_ev_node_t testnode, * node; - if (!ngx_http_auth_digest_srcaddr_key(r->connection->sockaddr, r->connection->socklen, &key)) - { + if (!ngx_http_auth_digest_srcaddr_key(r->connection->sockaddr, r->connection->socklen, &key)) { ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, "skipping evasive tactics for this source address"); return; } @@ -1650,11 +1464,9 @@ static void ngx_http_auth_digest_evasion_tracking(ngx_http_request_t* r, testnode.src_addrlen = r->connection->socklen; node = ngx_http_auth_digest_ev_rbtree_find(&testnode, ngx_http_auth_digest_ev_rbtree->root, ngx_http_auth_digest_ev_rbtree->sentinel); - if (node == NULL) - { + if (node == NULL) { // Don't bother creating a node if this was a successful auth - if (status == NGX_HTTP_AUTH_DIGEST_STATUS_SUCCESS) - { + if (status == NGX_HTTP_AUTH_DIGEST_STATUS_SUCCESS) { ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, "sucessful auth, not tracking"); ngx_shmtx_unlock(&shpool->mutex); return; @@ -1663,8 +1475,7 @@ static void ngx_http_auth_digest_evasion_tracking(ngx_http_request_t* r, node = ngx_slab_alloc_locked(shpool, sizeof(ngx_http_auth_digest_ev_node_t)); - if (node == NULL) - { + if (node == NULL) { ngx_shmtx_unlock(&shpool->mutex); ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "auth_digest ran out of shm space. Increase the " @@ -1677,22 +1488,18 @@ static void ngx_http_auth_digest_evasion_tracking(ngx_http_request_t* r, ((ngx_rbtree_node_t*)node)->key = key; ngx_rbtree_insert(ngx_http_auth_digest_ev_rbtree, &node->node); } - if (status == NGX_HTTP_AUTH_DIGEST_STATUS_SUCCESS) - { + if (status == NGX_HTTP_AUTH_DIGEST_STATUS_SUCCESS) { ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, "successful auth, clearing evasion counters"); node->failcount = 0; node->drop_time = ngx_time(); } - else - { + else { // Reset the failure count to 1 if we're outside the evasion window - if (ngx_time() > node->drop_time) - { + if (ngx_time() > node->drop_time) { node->failcount = 1; } - else - { + else { node->failcount += 1; } node->drop_time = ngx_time() + alcf->evasion_time; @@ -1704,15 +1511,13 @@ static void ngx_http_auth_digest_evasion_tracking(ngx_http_request_t* r, } static int ngx_http_auth_digest_evading(ngx_http_request_t* r, - ngx_http_auth_digest_loc_conf_t* alcf) -{ + ngx_http_auth_digest_loc_conf_t* alcf) { ngx_slab_pool_t* shpool; ngx_uint_t key; ngx_http_auth_digest_ev_node_t testnode, * node; int evading = 0; - if (!ngx_http_auth_digest_srcaddr_key(r->connection->sockaddr, r->connection->socklen, &key)) - { + if (!ngx_http_auth_digest_srcaddr_key(r->connection->sockaddr, r->connection->socklen, &key)) { return 0; } @@ -1727,8 +1532,7 @@ static int ngx_http_auth_digest_evading(ngx_http_request_t* r, ngx_shmtx_lock(&shpool->mutex); node = ngx_http_auth_digest_ev_rbtree_find(&testnode, ngx_http_auth_digest_ev_rbtree->root, ngx_http_auth_digest_ev_rbtree->sentinel); - if (node != NULL && node->failcount >= alcf->maxtries && ngx_time() < node->drop_time) - { + if (node != NULL && node->failcount >= alcf->maxtries && ngx_time() < node->drop_time) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "ignoring authentication request - in evasion period"); evading = 1; } @@ -1737,8 +1541,7 @@ static int ngx_http_auth_digest_evading(ngx_http_request_t* r, } // pulled in from nginx_auth_o_module -int ngx_http_auth_digest_is_loopback(ngx_str_t* server) -{ +int ngx_http_auth_digest_is_loopback(ngx_str_t* server) { char* localhost = "localhost"; int localhostLen = strlen(localhost); char* loopback = "127.0.0.1"; @@ -1751,28 +1554,22 @@ int ngx_http_auth_digest_is_loopback(ngx_str_t* server) if (server->len <= 0 || server->data == NULL) return -1; - if (((int)strlen((const char*)server->data) >= localhostLen) && (strncmp((const char*)server->data, localhost, localhostLen) == 0)) - { + if (((int)strlen((const char*)server->data) >= localhostLen) && (strncmp((const char*)server->data, localhost, localhostLen) == 0)) { pos = localhostLen; } - else if (((int)strlen((const char*)server->data) >= loopbackLen) && (strncmp((const char*)server->data, loopback, loopbackLen) == 0)) - { + else if (((int)strlen((const char*)server->data) >= loopbackLen) && (strncmp((const char*)server->data, loopback, loopbackLen) == 0)) { pos = loopbackLen; } - else - { + else { return -1; } - if (server->data[pos] == '\0') - { + if (server->data[pos] == '\0') { return 0; } - else if (server->data[pos] == ':') - { + else if (server->data[pos] == ':') { ++pos; - while (portCharPos <= 3 && server->data[pos] >= '0' && server->data[pos] <= '9') - { + while (portCharPos <= 3 && server->data[pos] >= '0' && server->data[pos] <= '9') { portChar[portCharPos] = server->data[pos]; ++portCharPos; ++pos; diff --git a/ngx_http_auth_digest_module.h b/ngx_http_auth_digest_module.h index f058409..9e20e6c 100644 --- a/ngx_http_auth_digest_module.h +++ b/ngx_http_auth_digest_module.h @@ -6,276 +6,273 @@ // the module conf typedef struct { - ngx_http_complex_value_t realm; - time_t timeout; - time_t expires; - time_t drop_time; - time_t evasion_time; - ngx_int_t replays; - ngx_int_t maxtries; - ngx_http_complex_value_t user_file; - ngx_str_t cache_dir; - ngx_str_t localhost_nocheck; + ngx_http_complex_value_t realm; + time_t timeout; + time_t expires; + time_t drop_time; + time_t evasion_time; + ngx_int_t replays; + ngx_int_t maxtries; + ngx_http_complex_value_t user_file; + ngx_str_t cache_dir; + ngx_str_t allow_localhost; } ngx_http_auth_digest_loc_conf_t; // contents of the request's authorization header typedef struct { - ngx_str_t auth_scheme; - ngx_str_t username; - ngx_str_t realm; - ngx_str_t nonce; - ngx_str_t nc; - ngx_str_t uri; - ngx_str_t qop; - ngx_str_t cnonce; - ngx_str_t response; - ngx_str_t opaque; - ngx_int_t stale; + ngx_str_t auth_scheme; + ngx_str_t username; + ngx_str_t realm; + ngx_str_t nonce; + ngx_str_t nc; + ngx_str_t uri; + ngx_str_t qop; + ngx_str_t cnonce; + ngx_str_t response; + ngx_str_t opaque; + ngx_int_t stale; } ngx_http_auth_digest_cred_t; // the nonce as an issue-time/random-num pair typedef struct { - ngx_uint_t rnd; - time_t t; + ngx_uint_t rnd; + time_t t; } ngx_http_auth_digest_nonce_t; // nonce entries in the rbtree typedef struct { - ngx_rbtree_node_t node; // the node's .key is derived from the nonce val - time_t expires; // time at which the node should be evicted - time_t drop_time; - char nc[0]; // bitvector of used nc values to prevent replays + ngx_rbtree_node_t node; // the node's .key is derived from the nonce val + time_t expires; // time at which the node should be evicted + time_t drop_time; + char nc[0]; // bitvector of used nc values to prevent replays } ngx_http_auth_digest_node_t; // evasion entries in the rbtree typedef struct { - ngx_rbtree_node_t node; // the node's .key is derived from the source address - time_t drop_time; - ngx_int_t failcount; - struct sockaddr src_addr; - socklen_t src_addrlen; + ngx_rbtree_node_t node; // the node's .key is derived from the source address + time_t drop_time; + ngx_int_t failcount; + struct sockaddr src_addr; + socklen_t src_addrlen; } ngx_http_auth_digest_ev_node_t; // the main event -static ngx_int_t ngx_http_auth_digest_handler(ngx_http_request_t *r); +static ngx_int_t ngx_http_auth_digest_handler(ngx_http_request_t* r); // realm handling -static char *ngx_http_auth_digest_set_realm(ngx_conf_t *cf, ngx_command_t *cmd, - void *conf); +static char* ngx_http_auth_digest_set_realm(ngx_conf_t* cf, ngx_command_t* cmd, + void* conf); // passwd file handling -static void ngx_http_auth_digest_close(ngx_file_t *file); -static char *ngx_http_auth_digest_set_user_file(ngx_conf_t *cf, - ngx_command_t *cmd, void *conf); +static void ngx_http_auth_digest_close(ngx_file_t* file); +static char* ngx_http_auth_digest_set_user_file(ngx_conf_t* cf, + ngx_command_t* cmd, void* conf); #define NGX_HTTP_AUTH_DIGEST_BUF_SIZE 4096 // digest challenge generation -static ngx_int_t ngx_http_auth_digest_send_challenge(ngx_http_request_t *r, ngx_str_t *realm, ngx_uint_t is_stale); +static ngx_int_t ngx_http_auth_digest_send_challenge(ngx_http_request_t* r, ngx_str_t* realm, ngx_uint_t is_stale); // digest response validators static ngx_int_t -ngx_http_auth_digest_check_credentials(ngx_http_request_t *r, ngx_http_auth_digest_cred_t *ctx); +ngx_http_auth_digest_check_credentials(ngx_http_request_t* r, ngx_http_auth_digest_cred_t* ctx); static ngx_int_t -ngx_http_auth_digest_verify_user(ngx_http_request_t *r, ngx_http_auth_digest_cred_t *fields, ngx_str_t *line); +ngx_http_auth_digest_verify_user(ngx_http_request_t* r, ngx_http_auth_digest_cred_t* fields, ngx_str_t* line); static ngx_int_t -ngx_http_auth_digest_verify_hash(ngx_http_request_t *r, ngx_http_auth_digest_cred_t *fields, u_char *hashed_pw); +ngx_http_auth_digest_verify_hash(ngx_http_request_t* r, ngx_http_auth_digest_cred_t* fields, u_char* hashed_pw); -static int ngx_http_auth_digest_is_loopback(ngx_str_t *server); +static int ngx_http_auth_digest_is_loopback(ngx_str_t* server); // the shm segment that houses the used-nonces tree and evasion rbtree static ngx_uint_t ngx_http_auth_digest_shm_size; -static ngx_shm_zone_t *ngx_http_auth_digest_shm_zone; -static ngx_rbtree_t *ngx_http_auth_digest_rbtree; -static ngx_rbtree_t *ngx_http_auth_digest_ev_rbtree; -static char *ngx_http_auth_digest_set_shm_size(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); -static ngx_int_t ngx_http_auth_digest_init_shm_zone(ngx_shm_zone_t *shm_zone, void *data); +static ngx_shm_zone_t* ngx_http_auth_digest_shm_zone; +static ngx_rbtree_t* ngx_http_auth_digest_rbtree; +static ngx_rbtree_t* ngx_http_auth_digest_ev_rbtree; +static char* ngx_http_auth_digest_set_shm_size(ngx_conf_t* cf, ngx_command_t* cmd, void* conf); +static ngx_int_t ngx_http_auth_digest_init_shm_zone(ngx_shm_zone_t* shm_zone, void* data); // nonce bookkeeping static ngx_http_auth_digest_nonce_t -ngx_http_auth_digest_next_nonce(ngx_http_request_t *r); -static ngx_rbtree_node_t * -ngx_http_auth_digest_rbtree_find(ngx_rbtree_key_t key, ngx_rbtree_node_t *node, - ngx_rbtree_node_t *sentinel); +ngx_http_auth_digest_next_nonce(ngx_http_request_t* r); +static ngx_rbtree_node_t* +ngx_http_auth_digest_rbtree_find(ngx_rbtree_key_t key, ngx_rbtree_node_t* node, + ngx_rbtree_node_t* sentinel); // nonce cleanup #define NGX_HTTP_AUTH_DIGEST_CLEANUP_INTERVAL 3000 #define NGX_HTTP_AUTH_DIGEST_CLEANUP_BATCH_SIZE 2048 -ngx_event_t *ngx_http_auth_digest_cleanup_timer; -static ngx_array_t *ngx_http_auth_digest_cleanup_list; -static ngx_atomic_t *ngx_http_auth_digest_cleanup_lock; -void ngx_http_auth_digest_cleanup(ngx_event_t *e); -static void ngx_http_auth_digest_rbtree_prune(ngx_log_t *log); -static void ngx_http_auth_digest_rbtree_prune_walk(ngx_rbtree_node_t *node, - ngx_rbtree_node_t *sentinel, - time_t now, ngx_log_t *log); -static void ngx_http_auth_digest_ev_rbtree_prune(ngx_log_t *log); +ngx_event_t* ngx_http_auth_digest_cleanup_timer; +static ngx_array_t* ngx_http_auth_digest_cleanup_list; +static ngx_atomic_t* ngx_http_auth_digest_cleanup_lock; +void ngx_http_auth_digest_cleanup(ngx_event_t* e); +static void ngx_http_auth_digest_rbtree_prune(ngx_log_t* log); +static void ngx_http_auth_digest_rbtree_prune_walk(ngx_rbtree_node_t* node, + ngx_rbtree_node_t* sentinel, + time_t now, ngx_log_t* log); +static void ngx_http_auth_digest_ev_rbtree_prune(ngx_log_t* log); static void -ngx_http_auth_digest_ev_rbtree_prune_walk(ngx_rbtree_node_t *node, - ngx_rbtree_node_t *sentinel, - time_t now, ngx_log_t *log); +ngx_http_auth_digest_ev_rbtree_prune_walk(ngx_rbtree_node_t* node, + ngx_rbtree_node_t* sentinel, + time_t now, ngx_log_t* log); // evasive tactics functions -static int ngx_http_auth_digest_srcaddr_key(struct sockaddr *sa, socklen_t len, - ngx_uint_t *key); - -static int ngx_http_auth_digest_srcaddr_cmp(struct sockaddr *sa1, - socklen_t len1, - struct sockaddr *sa2, - socklen_t len2); -static ngx_http_auth_digest_ev_node_t * -ngx_http_auth_digest_ev_rbtree_find(ngx_http_auth_digest_ev_node_t *this, - ngx_rbtree_node_t *node, - ngx_rbtree_node_t *sentinel); +static int ngx_http_auth_digest_srcaddr_key(struct sockaddr* sa, socklen_t len, + ngx_uint_t* key); + +static int ngx_http_auth_digest_srcaddr_cmp(struct sockaddr* sa1, + socklen_t len1, + struct sockaddr* sa2, + socklen_t len2); +static ngx_http_auth_digest_ev_node_t* +ngx_http_auth_digest_ev_rbtree_find(ngx_http_auth_digest_ev_node_t* this, + ngx_rbtree_node_t* node, + ngx_rbtree_node_t* sentinel); #define NGX_HTTP_AUTH_DIGEST_STATUS_SUCCESS 1 #define NGX_HTTP_AUTH_DIGEST_STATUS_FAILURE 0 -static void ngx_http_auth_digest_evasion_tracking(ngx_http_request_t *r, - ngx_http_auth_digest_loc_conf_t *alcf, - ngx_int_t status); +static void ngx_http_auth_digest_evasion_tracking(ngx_http_request_t* r, + ngx_http_auth_digest_loc_conf_t* alcf, + ngx_int_t status); -static int ngx_http_auth_digest_evading(ngx_http_request_t *r, - ngx_http_auth_digest_loc_conf_t *alcf); +static int ngx_http_auth_digest_evading(ngx_http_request_t* r, + ngx_http_auth_digest_loc_conf_t* alcf); // rbtree primitives -static void ngx_http_auth_digest_rbtree_insert(ngx_rbtree_node_t *temp, - ngx_rbtree_node_t *node, - ngx_rbtree_node_t *sentinel); +static void ngx_http_auth_digest_rbtree_insert(ngx_rbtree_node_t* temp, + ngx_rbtree_node_t* node, + ngx_rbtree_node_t* sentinel); -static void ngx_http_auth_digest_ev_rbtree_insert(ngx_rbtree_node_t *temp, - ngx_rbtree_node_t *node, - ngx_rbtree_node_t *sentinel); +static void ngx_http_auth_digest_ev_rbtree_insert(ngx_rbtree_node_t* temp, + ngx_rbtree_node_t* node, + ngx_rbtree_node_t* sentinel); static void -ngx_rbtree_generic_insert(ngx_rbtree_node_t *temp, ngx_rbtree_node_t *node, - ngx_rbtree_node_t *sentinel, - int (*compare)(const ngx_rbtree_node_t *left, - const ngx_rbtree_node_t *right)); +ngx_rbtree_generic_insert(ngx_rbtree_node_t* temp, ngx_rbtree_node_t* node, + ngx_rbtree_node_t* sentinel, + int (*compare)(const ngx_rbtree_node_t* left, + const ngx_rbtree_node_t* right)); -static int ngx_http_auth_digest_rbtree_cmp(const ngx_rbtree_node_t *v_left, - const ngx_rbtree_node_t *v_right); +static int ngx_http_auth_digest_rbtree_cmp(const ngx_rbtree_node_t* v_left, + const ngx_rbtree_node_t* v_right); // quick & dirty bitvectors (for marking used nc values) -static ngx_inline ngx_uint_t ngx_bitvector_size(ngx_uint_t nbits) -{ - return ((nbits + CHAR_BIT - 1) / CHAR_BIT); +static ngx_inline ngx_uint_t ngx_bitvector_size(ngx_uint_t nbits) { + return ((nbits + CHAR_BIT - 1) / CHAR_BIT); } -static ngx_inline ngx_uint_t ngx_bitvector_test(char *bv, ngx_uint_t bit) -{ - return ((bv)[((bit) / CHAR_BIT)] & (1 << ((bit) % CHAR_BIT))); +static ngx_inline ngx_uint_t ngx_bitvector_test(char* bv, ngx_uint_t bit) { + return ((bv)[((bit) / CHAR_BIT)] & (1 << ((bit) % CHAR_BIT))); } -static ngx_inline void ngx_bitvector_set(char *bv, ngx_uint_t bit) -{ - ((bv)[((bit) / CHAR_BIT)] &= ~(1 << ((bit) % CHAR_BIT))); +static ngx_inline void ngx_bitvector_set(char* bv, ngx_uint_t bit) { + ((bv)[((bit) / CHAR_BIT)] &= ~(1 << ((bit) % CHAR_BIT))); } // module plumbing -static void *ngx_http_auth_digest_create_loc_conf(ngx_conf_t *cf); -static char *ngx_http_auth_digest_merge_loc_conf(ngx_conf_t *cf, void *parent, - void *child); -static ngx_int_t ngx_http_auth_digest_init(ngx_conf_t *cf); -static ngx_int_t ngx_http_auth_digest_worker_init(ngx_cycle_t *cycle); +static void* ngx_http_auth_digest_create_loc_conf(ngx_conf_t* cf); +static char* ngx_http_auth_digest_merge_loc_conf(ngx_conf_t* cf, void* parent, + void* child); +static ngx_int_t ngx_http_auth_digest_init(ngx_conf_t* cf); +static ngx_int_t ngx_http_auth_digest_worker_init(ngx_cycle_t* cycle); // module datastructures static ngx_command_t ngx_http_auth_digest_commands[] = - { - {ngx_string("auth_digest"), - NGX_HTTP_MAIN_CONF | NGX_HTTP_SRV_CONF | NGX_HTTP_LOC_CONF | NGX_HTTP_LMT_CONF | NGX_CONF_TAKE1, - ngx_http_auth_digest_set_realm, - NGX_HTTP_LOC_CONF_OFFSET, - offsetof(ngx_http_auth_digest_loc_conf_t, realm), - NULL}, - - {ngx_string("auth_digest_user_file"), - NGX_HTTP_MAIN_CONF | NGX_HTTP_SRV_CONF | NGX_HTTP_LOC_CONF | NGX_HTTP_LMT_CONF | NGX_CONF_TAKE1, - ngx_http_auth_digest_set_user_file, - NGX_HTTP_LOC_CONF_OFFSET, - offsetof(ngx_http_auth_digest_loc_conf_t, user_file), - NULL}, - - {ngx_string("auth_digest_localhost_nocheck"), - NGX_HTTP_MAIN_CONF | NGX_HTTP_SRV_CONF | NGX_HTTP_LOC_CONF | NGX_HTTP_LMT_CONF | NGX_CONF_TAKE1, - ngx_conf_set_str_slot, - NGX_HTTP_LOC_CONF_OFFSET, - offsetof(ngx_http_auth_digest_loc_conf_t, localhost_nocheck), - NULL}, - - {ngx_string("auth_digest_timeout"), - NGX_HTTP_MAIN_CONF | NGX_HTTP_SRV_CONF | NGX_HTTP_LOC_CONF | NGX_CONF_TAKE1, - ngx_conf_set_sec_slot, - NGX_HTTP_LOC_CONF_OFFSET, - offsetof(ngx_http_auth_digest_loc_conf_t, timeout), - NULL}, - - {ngx_string("auth_digest_expires"), - NGX_HTTP_MAIN_CONF | NGX_HTTP_SRV_CONF | NGX_HTTP_LOC_CONF | NGX_CONF_TAKE1, - ngx_conf_set_sec_slot, - NGX_HTTP_LOC_CONF_OFFSET, - offsetof(ngx_http_auth_digest_loc_conf_t, expires), - NULL}, - - {ngx_string("auth_digest_drop_time"), - NGX_HTTP_MAIN_CONF | NGX_HTTP_SRV_CONF | NGX_HTTP_LOC_CONF | NGX_CONF_TAKE1, - ngx_conf_set_sec_slot, - NGX_HTTP_LOC_CONF_OFFSET, - offsetof(ngx_http_auth_digest_loc_conf_t, drop_time), - NULL}, - - {ngx_string("auth_digest_evasion_time"), - NGX_HTTP_MAIN_CONF | NGX_HTTP_SRV_CONF | NGX_HTTP_LOC_CONF | NGX_CONF_TAKE1, - ngx_conf_set_sec_slot, - NGX_HTTP_LOC_CONF_OFFSET, - offsetof(ngx_http_auth_digest_loc_conf_t, evasion_time), - NULL}, - - {ngx_string("auth_digest_replays"), - NGX_HTTP_MAIN_CONF | NGX_HTTP_SRV_CONF | NGX_HTTP_LOC_CONF | NGX_CONF_TAKE1, - ngx_conf_set_num_slot, - NGX_HTTP_LOC_CONF_OFFSET, - offsetof(ngx_http_auth_digest_loc_conf_t, replays), - NULL}, - - {ngx_string("auth_digest_maxtries"), - NGX_HTTP_MAIN_CONF | NGX_HTTP_SRV_CONF | NGX_HTTP_LOC_CONF | NGX_CONF_TAKE1, - ngx_conf_set_num_slot, - NGX_HTTP_LOC_CONF_OFFSET, - offsetof(ngx_http_auth_digest_loc_conf_t, maxtries), - NULL}, - - {ngx_string("auth_digest_shm_size"), - NGX_HTTP_MAIN_CONF | NGX_HTTP_SRV_CONF | NGX_CONF_TAKE1, - ngx_http_auth_digest_set_shm_size, 0, 0, NULL}, - ngx_null_command}; +{ + {ngx_string("auth_digest"), + NGX_HTTP_MAIN_CONF | NGX_HTTP_SRV_CONF | NGX_HTTP_LOC_CONF | NGX_HTTP_LMT_CONF | NGX_CONF_TAKE1, + ngx_http_auth_digest_set_realm, + NGX_HTTP_LOC_CONF_OFFSET, + offsetof(ngx_http_auth_digest_loc_conf_t, realm), + NULL}, + + {ngx_string("auth_digest_user_file"), + NGX_HTTP_MAIN_CONF | NGX_HTTP_SRV_CONF | NGX_HTTP_LOC_CONF | NGX_HTTP_LMT_CONF | NGX_CONF_TAKE1, + ngx_http_auth_digest_set_user_file, + NGX_HTTP_LOC_CONF_OFFSET, + offsetof(ngx_http_auth_digest_loc_conf_t, user_file), + NULL}, + + {ngx_string("auth_digest_allow_localhost"), + NGX_HTTP_MAIN_CONF | NGX_HTTP_SRV_CONF | NGX_HTTP_LOC_CONF | NGX_HTTP_LMT_CONF | NGX_CONF_TAKE1, + ngx_conf_set_str_slot, + NGX_HTTP_LOC_CONF_OFFSET, + offsetof(ngx_http_auth_digest_loc_conf_t, allow_localhost), + NULL}, + + {ngx_string("auth_digest_timeout"), + NGX_HTTP_MAIN_CONF | NGX_HTTP_SRV_CONF | NGX_HTTP_LOC_CONF | NGX_CONF_TAKE1, + ngx_conf_set_sec_slot, + NGX_HTTP_LOC_CONF_OFFSET, + offsetof(ngx_http_auth_digest_loc_conf_t, timeout), + NULL}, + + {ngx_string("auth_digest_expires"), + NGX_HTTP_MAIN_CONF | NGX_HTTP_SRV_CONF | NGX_HTTP_LOC_CONF | NGX_CONF_TAKE1, + ngx_conf_set_sec_slot, + NGX_HTTP_LOC_CONF_OFFSET, + offsetof(ngx_http_auth_digest_loc_conf_t, expires), + NULL}, + + {ngx_string("auth_digest_drop_time"), + NGX_HTTP_MAIN_CONF | NGX_HTTP_SRV_CONF | NGX_HTTP_LOC_CONF | NGX_CONF_TAKE1, + ngx_conf_set_sec_slot, + NGX_HTTP_LOC_CONF_OFFSET, + offsetof(ngx_http_auth_digest_loc_conf_t, drop_time), + NULL}, + + {ngx_string("auth_digest_evasion_time"), + NGX_HTTP_MAIN_CONF | NGX_HTTP_SRV_CONF | NGX_HTTP_LOC_CONF | NGX_CONF_TAKE1, + ngx_conf_set_sec_slot, + NGX_HTTP_LOC_CONF_OFFSET, + offsetof(ngx_http_auth_digest_loc_conf_t, evasion_time), + NULL}, + + {ngx_string("auth_digest_replays"), + NGX_HTTP_MAIN_CONF | NGX_HTTP_SRV_CONF | NGX_HTTP_LOC_CONF | NGX_CONF_TAKE1, + ngx_conf_set_num_slot, + NGX_HTTP_LOC_CONF_OFFSET, + offsetof(ngx_http_auth_digest_loc_conf_t, replays), + NULL}, + + {ngx_string("auth_digest_maxtries"), + NGX_HTTP_MAIN_CONF | NGX_HTTP_SRV_CONF | NGX_HTTP_LOC_CONF | NGX_CONF_TAKE1, + ngx_conf_set_num_slot, + NGX_HTTP_LOC_CONF_OFFSET, + offsetof(ngx_http_auth_digest_loc_conf_t, maxtries), + NULL}, + + {ngx_string("auth_digest_shm_size"), + NGX_HTTP_MAIN_CONF | NGX_HTTP_SRV_CONF | NGX_CONF_TAKE1, + ngx_http_auth_digest_set_shm_size, 0, 0, NULL}, + ngx_null_command }; static ngx_http_module_t ngx_http_auth_digest_module_ctx = { - NULL, /* preconfiguration */ - ngx_http_auth_digest_init, /* postconfiguration */ + NULL, /* preconfiguration */ + ngx_http_auth_digest_init, /* postconfiguration */ - NULL, /* create main configuration */ - NULL, /* init main configuration */ + NULL, /* create main configuration */ + NULL, /* init main configuration */ - NULL, /* create server configuration */ - NULL, /* merge server configuration */ + NULL, /* create server configuration */ + NULL, /* merge server configuration */ - ngx_http_auth_digest_create_loc_conf, /* create location configuration */ - ngx_http_auth_digest_merge_loc_conf /* merge location configuration */ + ngx_http_auth_digest_create_loc_conf, /* create location configuration */ + ngx_http_auth_digest_merge_loc_conf /* merge location configuration */ }; ngx_module_t ngx_http_auth_digest_module = { - NGX_MODULE_V1, - &ngx_http_auth_digest_module_ctx, /* module context */ - ngx_http_auth_digest_commands, /* module directives */ - NGX_HTTP_MODULE, /* module type */ - NULL, /* init master */ - NULL, /* init module */ - ngx_http_auth_digest_worker_init, /* init process */ - NULL, /* init thread */ - NULL, /* exit thread */ - NULL, /* exit process */ - NULL, /* exit master */ - NGX_MODULE_V1_PADDING}; + NGX_MODULE_V1, + &ngx_http_auth_digest_module_ctx, /* module context */ + ngx_http_auth_digest_commands, /* module directives */ + NGX_HTTP_MODULE, /* module type */ + NULL, /* init master */ + NULL, /* init module */ + ngx_http_auth_digest_worker_init, /* init process */ + NULL, /* init thread */ + NULL, /* exit thread */ + NULL, /* exit process */ + NULL, /* exit master */ + NGX_MODULE_V1_PADDING }; #endif From 0f54f3514344b0a5df1ba4cc96fb3c318c0a4a45 Mon Sep 17 00:00:00 2001 From: Nate Gates Date: Thu, 4 Mar 2021 14:38:35 -0600 Subject: [PATCH 4/4] updated documentation. --- .gitignore | 1 + readme.rst | 10 +++++++++- 2 files changed, 10 insertions(+), 1 deletion(-) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d4cca4c --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.vscode/*.* diff --git a/readme.rst b/readme.rst index 9f55260..62925a7 100644 --- a/readme.rst +++ b/readme.rst @@ -115,7 +115,15 @@ auth_digest_user_file of a username, realm, and md5 hash combining name, realm, and password. For example: ``joi:enfield:ef25e85b34208c246cfd09ab76b01db7`` This file needs to be readable by your nginx user! - + +auth_digest_allow_localhost +~~~~~~~~~~~~~~~~~~~~~~~~~~~ +:Syntax: ``auth_digest_allow_localhost`` *on | off* +:Default: *off* +:Context: server, location +:Description: + when enabled allows localhost traffic through unchallenged. + auth_digest_timeout ~~~~~~~~~~~~~~~~~~~ :Syntax: ``auth_digest_timeout`` *delay-time*