18abd06a7SGlenn Strauss #include "first.h" 28abd06a7SGlenn Strauss 304d76e7aSGlenn Strauss #include "base.h" 422e8b456SStefan Bühler #include "plugin.h" 522e8b456SStefan Bühler #include "http_auth.h" 63dd3cde9SGlenn Strauss #include "http_header.h" 722e8b456SStefan Bühler #include "log.h" 8bcdc6a3bSJan Kneschke 9b9e2be50SGlenn Strauss #include "sys-crypto.h" /* USE_OPENSSL_CRYPTO */ 10b9e2be50SGlenn Strauss #ifdef USE_OPENSSL_CRYPTO 11b9e2be50SGlenn Strauss #include <openssl/sha.h> 12b9e2be50SGlenn Strauss #endif 13b9e2be50SGlenn Strauss 14bcdc6a3bSJan Kneschke #include <stdlib.h> 15bcdc6a3bSJan Kneschke #include <string.h> 16bcdc6a3bSJan Kneschke 17bcdc6a3bSJan Kneschke /** 187ba06c71SGlenn Strauss * auth framework 19bcdc6a3bSJan Kneschke */ 20bcdc6a3bSJan Kneschke 214b3a91e6SGlenn Strauss typedef struct { 224b3a91e6SGlenn Strauss /* auth */ 234b3a91e6SGlenn Strauss array *auth_require; 244b3a91e6SGlenn Strauss buffer *auth_backend_conf; 25f54d628cSGlenn Strauss unsigned short auth_extern_authn; 264b3a91e6SGlenn Strauss 274b3a91e6SGlenn Strauss /* generated */ 284b3a91e6SGlenn Strauss const http_auth_backend_t *auth_backend; 297ba06c71SGlenn Strauss } plugin_config; 304b3a91e6SGlenn Strauss 314b3a91e6SGlenn Strauss typedef struct { 324b3a91e6SGlenn Strauss PLUGIN_DATA; 334b3a91e6SGlenn Strauss 347ba06c71SGlenn Strauss plugin_config **config_storage; 354b3a91e6SGlenn Strauss 367ba06c71SGlenn Strauss plugin_config conf; 377ba06c71SGlenn Strauss } plugin_data; 387ba06c71SGlenn Strauss 397ba06c71SGlenn Strauss static handler_t mod_auth_check_basic(server *srv, connection *con, void *p_d, const struct http_auth_require_t *require, const struct http_auth_backend_t *backend); 407ba06c71SGlenn Strauss static handler_t mod_auth_check_digest(server *srv, connection *con, void *p_d, const struct http_auth_require_t *require, const struct http_auth_backend_t *backend); 417ba06c71SGlenn Strauss static handler_t mod_auth_check_extern(server *srv, connection *con, void *p_d, const struct http_auth_require_t *require, const struct http_auth_backend_t *backend); 424b3a91e6SGlenn Strauss 43bcdc6a3bSJan Kneschke INIT_FUNC(mod_auth_init) { 447ba06c71SGlenn Strauss static const http_auth_scheme_t http_auth_scheme_basic = { "basic", mod_auth_check_basic, NULL }; 457ba06c71SGlenn Strauss static const http_auth_scheme_t http_auth_scheme_digest = { "digest", mod_auth_check_digest, NULL }; 467ba06c71SGlenn Strauss static const http_auth_scheme_t http_auth_scheme_extern = { "extern", mod_auth_check_extern, NULL }; 477ba06c71SGlenn Strauss plugin_data *p; 487ba06c71SGlenn Strauss 497ba06c71SGlenn Strauss /* register http_auth_scheme_* */ 507ba06c71SGlenn Strauss http_auth_scheme_set(&http_auth_scheme_basic); 517ba06c71SGlenn Strauss http_auth_scheme_set(&http_auth_scheme_digest); 527ba06c71SGlenn Strauss http_auth_scheme_set(&http_auth_scheme_extern); 53bcdc6a3bSJan Kneschke 54bcdc6a3bSJan Kneschke p = calloc(1, sizeof(*p)); 55bcdc6a3bSJan Kneschke 56bcdc6a3bSJan Kneschke return p; 57bcdc6a3bSJan Kneschke } 58bcdc6a3bSJan Kneschke 59bcdc6a3bSJan Kneschke FREE_FUNC(mod_auth_free) { 607ba06c71SGlenn Strauss plugin_data *p = p_d; 61bcdc6a3bSJan Kneschke 62bcdc6a3bSJan Kneschke UNUSED(srv); 63bcdc6a3bSJan Kneschke 64bcdc6a3bSJan Kneschke if (!p) return HANDLER_GO_ON; 65bcdc6a3bSJan Kneschke 66bcdc6a3bSJan Kneschke if (p->config_storage) { 67bcdc6a3bSJan Kneschke size_t i; 68bcdc6a3bSJan Kneschke for (i = 0; i < srv->config_context->used; i++) { 697ba06c71SGlenn Strauss plugin_config *s = p->config_storage[i]; 70bcdc6a3bSJan Kneschke 7133cebeb0SStefan Bühler if (NULL == s) continue; 724a6ce4aeSJan Kneschke 73bcdc6a3bSJan Kneschke array_free(s->auth_require); 74bcdc6a3bSJan Kneschke buffer_free(s->auth_backend_conf); 75bcdc6a3bSJan Kneschke 76bcdc6a3bSJan Kneschke free(s); 77bcdc6a3bSJan Kneschke } 78bcdc6a3bSJan Kneschke free(p->config_storage); 79bcdc6a3bSJan Kneschke } 80bcdc6a3bSJan Kneschke 81bcdc6a3bSJan Kneschke free(p); 82bcdc6a3bSJan Kneschke 83bcdc6a3bSJan Kneschke return HANDLER_GO_ON; 84bcdc6a3bSJan Kneschke } 85bcdc6a3bSJan Kneschke 867ba06c71SGlenn Strauss /* data type for mod_auth structured data 877ba06c71SGlenn Strauss * (parsed from auth.require array of strings) */ 887ba06c71SGlenn Strauss typedef struct { 897ba06c71SGlenn Strauss DATA_UNSET; 907ba06c71SGlenn Strauss http_auth_require_t *require; 917ba06c71SGlenn Strauss } data_auth; 927ba06c71SGlenn Strauss 937ba06c71SGlenn Strauss static void data_auth_free(data_unset *d) 947ba06c71SGlenn Strauss { 957ba06c71SGlenn Strauss data_auth * const dauth = (data_auth *)d; 967ba06c71SGlenn Strauss buffer_free(dauth->key); 977ba06c71SGlenn Strauss http_auth_require_free(dauth->require); 987ba06c71SGlenn Strauss free(dauth); 997ba06c71SGlenn Strauss } 1007ba06c71SGlenn Strauss 1017ba06c71SGlenn Strauss static data_auth *data_auth_init(void) 1027ba06c71SGlenn Strauss { 1038c7f1dfbSGlenn Strauss static const struct data_methods fn = { 1048c7f1dfbSGlenn Strauss NULL, /* copy must not be called on this data */ 1058c7f1dfbSGlenn Strauss data_auth_free, 1068c7f1dfbSGlenn Strauss NULL, /* insert_dup must not be called on this data */ 1078c7f1dfbSGlenn Strauss NULL /* print must not be called on this data */ 1088c7f1dfbSGlenn Strauss }; 1097ba06c71SGlenn Strauss data_auth * const dauth = calloc(1, sizeof(*dauth)); 1107ba06c71SGlenn Strauss force_assert(NULL != dauth); 1117ba06c71SGlenn Strauss dauth->type = TYPE_OTHER; 1128c7f1dfbSGlenn Strauss dauth->fn = &fn; 1137ba06c71SGlenn Strauss 1147ba06c71SGlenn Strauss dauth->key = buffer_init(); 1157ba06c71SGlenn Strauss dauth->require = http_auth_require_init(); 1167ba06c71SGlenn Strauss 1177ba06c71SGlenn Strauss return dauth; 1187ba06c71SGlenn Strauss } 1197ba06c71SGlenn Strauss 120b9e2be50SGlenn Strauss static int mod_auth_algorithm_parse(http_auth_info_t *ai, const char *s) { 121b9e2be50SGlenn Strauss size_t len; 122b9e2be50SGlenn Strauss if (NULL == s) { 123b9e2be50SGlenn Strauss ai->dalgo = HTTP_AUTH_DIGEST_MD5; 124b9e2be50SGlenn Strauss ai->dlen = HTTP_AUTH_DIGEST_MD5_BINLEN; 125b9e2be50SGlenn Strauss return 1; 126b9e2be50SGlenn Strauss } 127b9e2be50SGlenn Strauss 128b9e2be50SGlenn Strauss len = strlen(s); 129b9e2be50SGlenn Strauss if (len > 5 130b9e2be50SGlenn Strauss && (s[len-5] ) == '-' 131b9e2be50SGlenn Strauss && (s[len-4] | 0x20) == 's' 132b9e2be50SGlenn Strauss && (s[len-3] | 0x20) == 'e' 133b9e2be50SGlenn Strauss && (s[len-2] | 0x20) == 's' 134b9e2be50SGlenn Strauss && (s[len-1] | 0x20) == 's') { 135b9e2be50SGlenn Strauss ai->dalgo = HTTP_AUTH_DIGEST_SESS; 136b9e2be50SGlenn Strauss len -= 5; 137b9e2be50SGlenn Strauss } 138b9e2be50SGlenn Strauss else { 139b9e2be50SGlenn Strauss ai->dalgo = HTTP_AUTH_DIGEST_NONE; 140b9e2be50SGlenn Strauss } 141b9e2be50SGlenn Strauss 142b9e2be50SGlenn Strauss if (3 == len 143b9e2be50SGlenn Strauss && 'm' == (s[0] | 0x20) 144b9e2be50SGlenn Strauss && 'd' == (s[1] | 0x20) 145b9e2be50SGlenn Strauss && '5' == (s[2] )) { 146b9e2be50SGlenn Strauss ai->dalgo |= HTTP_AUTH_DIGEST_MD5; 147b9e2be50SGlenn Strauss ai->dlen = HTTP_AUTH_DIGEST_MD5_BINLEN; 148b9e2be50SGlenn Strauss return 1; 149b9e2be50SGlenn Strauss } 150b9e2be50SGlenn Strauss #ifdef USE_OPENSSL_CRYPTO 151b9e2be50SGlenn Strauss else if (len >= 7 152b9e2be50SGlenn Strauss && 's' == (s[0] | 0x20) 153b9e2be50SGlenn Strauss && 'h' == (s[1] | 0x20) 154b9e2be50SGlenn Strauss && 'a' == (s[2] | 0x20) 155b9e2be50SGlenn Strauss && '-' == (s[3] )) { 156b9e2be50SGlenn Strauss if (len == 7 && s[4] == '2' && s[5] == '5' && s[6] == '6') { 157b9e2be50SGlenn Strauss ai->dalgo |= HTTP_AUTH_DIGEST_SHA256; 158b9e2be50SGlenn Strauss ai->dlen = HTTP_AUTH_DIGEST_SHA256_BINLEN; 159b9e2be50SGlenn Strauss return 1; 160b9e2be50SGlenn Strauss } 161b9e2be50SGlenn Strauss #ifdef SHA512_256_DIGEST_LENGTH 162b9e2be50SGlenn Strauss if (len == 11 && 0 == memcmp(s+4, "512-256", 7)) { 163b9e2be50SGlenn Strauss ai->dalgo |= HTTP_AUTH_DIGEST_SHA512_256; 164b9e2be50SGlenn Strauss ai->dlen = HTTP_AUTH_DIGEST_SHA512_256_BINLEN; 165b9e2be50SGlenn Strauss return 1; 166b9e2be50SGlenn Strauss } 167b9e2be50SGlenn Strauss #endif 168b9e2be50SGlenn Strauss } 169b9e2be50SGlenn Strauss #endif 170b9e2be50SGlenn Strauss return 0; /*(error)*/ 171b9e2be50SGlenn Strauss } 172b9e2be50SGlenn Strauss 173b9e2be50SGlenn Strauss static int mod_auth_algorithms_parse(int *algorithm, buffer *algos) { 174b9e2be50SGlenn Strauss for (char *s = algos->ptr, *p; s; s = p ? p+1 : NULL) { 175b9e2be50SGlenn Strauss http_auth_info_t ai; 176b9e2be50SGlenn Strauss int rc; 177b9e2be50SGlenn Strauss p = strchr(s, '|'); 178b9e2be50SGlenn Strauss if (p) *p = '\0'; 179b9e2be50SGlenn Strauss rc = mod_auth_algorithm_parse(&ai, s); 180b9e2be50SGlenn Strauss if (p) *p = '|'; 181b9e2be50SGlenn Strauss if (!rc) return 0; 182b9e2be50SGlenn Strauss *algorithm |= ai.dalgo; 183b9e2be50SGlenn Strauss } 184b9e2be50SGlenn Strauss return 1; 185b9e2be50SGlenn Strauss } 186b9e2be50SGlenn Strauss 1877ba06c71SGlenn Strauss static int mod_auth_require_parse (server *srv, http_auth_require_t * const require, const buffer *b) 1887ba06c71SGlenn Strauss { 1897ba06c71SGlenn Strauss /* user=name1|user=name2|group=name3|host=name4 */ 1907ba06c71SGlenn Strauss 1917ba06c71SGlenn Strauss const char *str = b->ptr; 1927ba06c71SGlenn Strauss const char *p; 1937ba06c71SGlenn Strauss 1947ba06c71SGlenn Strauss if (buffer_is_equal_string(b, CONST_STR_LEN("valid-user"))) { 1957ba06c71SGlenn Strauss require->valid_user = 1; 1967ba06c71SGlenn Strauss return 1; /* success */ 1977ba06c71SGlenn Strauss } 1987ba06c71SGlenn Strauss 1997ba06c71SGlenn Strauss do { 2007ba06c71SGlenn Strauss const char *eq; 2017ba06c71SGlenn Strauss size_t len; 2027ba06c71SGlenn Strauss p = strchr(str, '|'); 2037ba06c71SGlenn Strauss len = NULL != p ? (size_t)(p - str) : strlen(str); 2047ba06c71SGlenn Strauss eq = memchr(str, '=', len); 2057ba06c71SGlenn Strauss if (NULL == eq) { 2067ba06c71SGlenn Strauss log_error_write(srv, __FILE__, __LINE__, "sssbss", 2077ba06c71SGlenn Strauss "error parsing auth.require 'require' field: missing '='", 2087ba06c71SGlenn Strauss "(expecting \"valid-user\" or \"user=a|user=b|group=g|host=h\").", 2097ba06c71SGlenn Strauss "error value:", b, "error near:", str); 2107ba06c71SGlenn Strauss return 0; 2117ba06c71SGlenn Strauss } 212b2c18f48SGlenn Strauss if (eq[1] == '|' || eq[1] == '\0') { 2137ba06c71SGlenn Strauss log_error_write(srv, __FILE__, __LINE__, "sssbss", 2147ba06c71SGlenn Strauss "error parsing auth.require 'require' field: missing token after '='", 2157ba06c71SGlenn Strauss "(expecting \"valid-user\" or \"user=a|user=b|group=g|host=h\").", 2167ba06c71SGlenn Strauss "error value:", b, "error near:", str); 2177ba06c71SGlenn Strauss return 0; 2187ba06c71SGlenn Strauss } 2197ba06c71SGlenn Strauss 2207ba06c71SGlenn Strauss switch ((int)(eq - str)) { 2217ba06c71SGlenn Strauss case 4: 2227ba06c71SGlenn Strauss if (0 == memcmp(str, CONST_STR_LEN("user"))) { 223810109ccSGlenn Strauss /*("user=" is 5)*/ 224810109ccSGlenn Strauss array_set_key_value(require->user, str+5, len-5, CONST_STR_LEN("")); 2257ba06c71SGlenn Strauss continue; 2267ba06c71SGlenn Strauss } 2277ba06c71SGlenn Strauss else if (0 == memcmp(str, CONST_STR_LEN("host"))) { 228810109ccSGlenn Strauss /*("host=" is 5)*/ 229810109ccSGlenn Strauss array_set_key_value(require->host, str+5, len-5, CONST_STR_LEN("")); 2307ba06c71SGlenn Strauss log_error_write(srv, __FILE__, __LINE__, "ssb", 2317ba06c71SGlenn Strauss "warning parsing auth.require 'require' field: 'host' not implemented;", 2327ba06c71SGlenn Strauss "field value:", b); 2337ba06c71SGlenn Strauss continue; 2347ba06c71SGlenn Strauss } 2357ba06c71SGlenn Strauss break; /* to error */ 2367ba06c71SGlenn Strauss case 5: 2377ba06c71SGlenn Strauss if (0 == memcmp(str, CONST_STR_LEN("group"))) { 238810109ccSGlenn Strauss /*("group=" is 6)*/ 239810109ccSGlenn Strauss array_set_key_value(require->group, str+6, len-6, CONST_STR_LEN("")); 2409c91af0cSGlenn Strauss #if 0/*(supported by mod_authn_ldap, but not all other backends)*/ 2417ba06c71SGlenn Strauss log_error_write(srv, __FILE__, __LINE__, "ssb", 2427ba06c71SGlenn Strauss "warning parsing auth.require 'require' field: 'group' not implemented;", 2437ba06c71SGlenn Strauss "field value:", b); 2449c91af0cSGlenn Strauss #endif 2457ba06c71SGlenn Strauss continue; 2467ba06c71SGlenn Strauss } 2477ba06c71SGlenn Strauss break; /* to error */ 2487ba06c71SGlenn Strauss case 10: 2497ba06c71SGlenn Strauss if (0 == memcmp(str, CONST_STR_LEN("valid-user"))) { 2507ba06c71SGlenn Strauss log_error_write(srv, __FILE__, __LINE__, "sssb", 2517ba06c71SGlenn Strauss "error parsing auth.require 'require' field: valid user can not be combined with other require rules", 2527ba06c71SGlenn Strauss "(expecting \"valid-user\" or \"user=a|user=b|group=g|host=h\").", 2537ba06c71SGlenn Strauss "error value:", b); 2547ba06c71SGlenn Strauss return 0; 2557ba06c71SGlenn Strauss } 2567ba06c71SGlenn Strauss break; /* to error */ 2577ba06c71SGlenn Strauss default: 2587ba06c71SGlenn Strauss break; /* to error */ 2597ba06c71SGlenn Strauss } 2607ba06c71SGlenn Strauss 2617ba06c71SGlenn Strauss log_error_write(srv, __FILE__, __LINE__, "sssbss", 2627ba06c71SGlenn Strauss "error parsing auth.require 'require' field: invalid/unsupported token", 2637ba06c71SGlenn Strauss "(expecting \"valid-user\" or \"user=a|user=b|group=g|host=h\").", 2647ba06c71SGlenn Strauss "error value:", b, "error near:", str); 2657ba06c71SGlenn Strauss return 0; 2667ba06c71SGlenn Strauss 2677ba06c71SGlenn Strauss } while (p && *((str = p+1))); 2687ba06c71SGlenn Strauss 2697ba06c71SGlenn Strauss return 1; /* success */ 2707ba06c71SGlenn Strauss } 2717ba06c71SGlenn Strauss 2727ba06c71SGlenn Strauss SETDEFAULTS_FUNC(mod_auth_set_defaults) { 2737ba06c71SGlenn Strauss plugin_data *p = p_d; 2747ba06c71SGlenn Strauss size_t i; 2757ba06c71SGlenn Strauss 2767ba06c71SGlenn Strauss config_values_t cv[] = { 2777ba06c71SGlenn Strauss { "auth.backend", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 0 */ 2787ba06c71SGlenn Strauss { "auth.require", NULL, T_CONFIG_LOCAL, T_CONFIG_SCOPE_CONNECTION }, /* 1 */ 279f54d628cSGlenn Strauss { "auth.extern-authn", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION },/* 2 */ 2807ba06c71SGlenn Strauss { NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET } 2817ba06c71SGlenn Strauss }; 2827ba06c71SGlenn Strauss 2836a988bb0SMohammed Sadiq p->config_storage = calloc(srv->config_context->used, sizeof(plugin_config *)); 2847ba06c71SGlenn Strauss 2857ba06c71SGlenn Strauss for (i = 0; i < srv->config_context->used; i++) { 2867ba06c71SGlenn Strauss data_config const* config = (data_config const*)srv->config_context->data[i]; 2877ba06c71SGlenn Strauss plugin_config *s; 2887ba06c71SGlenn Strauss size_t n; 289*83535bbeSGlenn Strauss const data_array *da; 2907ba06c71SGlenn Strauss 2917ba06c71SGlenn Strauss s = calloc(1, sizeof(plugin_config)); 2927ba06c71SGlenn Strauss s->auth_backend_conf = buffer_init(); 2937ba06c71SGlenn Strauss 2947ba06c71SGlenn Strauss s->auth_require = array_init(); 2957ba06c71SGlenn Strauss 2967ba06c71SGlenn Strauss cv[0].destination = s->auth_backend_conf; 2977ba06c71SGlenn Strauss cv[1].destination = s->auth_require; /* T_CONFIG_LOCAL; not modified by config_insert_values_global() */ 298f54d628cSGlenn Strauss cv[2].destination = &s->auth_extern_authn; 2997ba06c71SGlenn Strauss 3007ba06c71SGlenn Strauss p->config_storage[i] = s; 3017ba06c71SGlenn Strauss 3027ba06c71SGlenn Strauss if (0 != config_insert_values_global(srv, config->value, cv, i == 0 ? T_CONFIG_SCOPE_SERVER : T_CONFIG_SCOPE_CONNECTION)) { 3037ba06c71SGlenn Strauss return HANDLER_ERROR; 3047ba06c71SGlenn Strauss } 3057ba06c71SGlenn Strauss 3067ba06c71SGlenn Strauss if (!buffer_string_is_empty(s->auth_backend_conf)) { 3077ba06c71SGlenn Strauss s->auth_backend = http_auth_backend_get(s->auth_backend_conf); 3087ba06c71SGlenn Strauss if (NULL == s->auth_backend) { 3097ba06c71SGlenn Strauss log_error_write(srv, __FILE__, __LINE__, "sb", "auth.backend not supported:", s->auth_backend_conf); 3107ba06c71SGlenn Strauss 3117ba06c71SGlenn Strauss return HANDLER_ERROR; 3127ba06c71SGlenn Strauss } 3137ba06c71SGlenn Strauss } 3147ba06c71SGlenn Strauss 3157ba06c71SGlenn Strauss /* no auth.require for this section */ 316*83535bbeSGlenn Strauss if (NULL == (da = (const data_array *)array_get_element_klen(config->value, CONST_STR_LEN("auth.require")))) continue; 3177ba06c71SGlenn Strauss 318bd77abe0SGlenn Strauss if (da->type != TYPE_ARRAY || !array_is_kvarray(da->value)) { 319bd77abe0SGlenn Strauss log_error_write(srv, __FILE__, __LINE__, "ss", 320bd77abe0SGlenn Strauss "unexpected value for auth.require; expected ", 321bd77abe0SGlenn Strauss "auth.require = ( \"urlpath\" => ( \"option\" => \"value\" ) )"); 322bd77abe0SGlenn Strauss return HANDLER_ERROR; 323bd77abe0SGlenn Strauss } 324bd77abe0SGlenn Strauss 3257ba06c71SGlenn Strauss 3267ba06c71SGlenn Strauss for (n = 0; n < da->value->used; n++) { 3277ba06c71SGlenn Strauss size_t m; 3287ba06c71SGlenn Strauss data_array *da_file = (data_array *)da->value->data[n]; 3297ba06c71SGlenn Strauss const buffer *method = NULL, *realm = NULL, *require = NULL; 3307ba06c71SGlenn Strauss const http_auth_scheme_t *auth_scheme; 331b9e2be50SGlenn Strauss buffer *algos = NULL; 332b9e2be50SGlenn Strauss int algorithm = HTTP_AUTH_DIGEST_SESS; 3337ba06c71SGlenn Strauss 334bd77abe0SGlenn Strauss if (!array_is_kvstring(da_file->value)) { 3357ba06c71SGlenn Strauss log_error_write(srv, __FILE__, __LINE__, "ss", 336bd77abe0SGlenn Strauss "unexpected value for auth.require; expected ", 337bd77abe0SGlenn Strauss "auth.require = ( \"urlpath\" => ( \"option\" => \"value\" ) )"); 3387ba06c71SGlenn Strauss 3397ba06c71SGlenn Strauss return HANDLER_ERROR; 3407ba06c71SGlenn Strauss } 3417ba06c71SGlenn Strauss 3427ba06c71SGlenn Strauss for (m = 0; m < da_file->value->used; m++) { 3437ba06c71SGlenn Strauss if (da_file->value->data[m]->type == TYPE_STRING) { 3447ba06c71SGlenn Strauss data_string *ds = (data_string *)da_file->value->data[m]; 3457ba06c71SGlenn Strauss if (buffer_is_equal_string(ds->key, CONST_STR_LEN("method"))) { 3467ba06c71SGlenn Strauss method = ds->value; 3477ba06c71SGlenn Strauss } else if (buffer_is_equal_string(ds->key, CONST_STR_LEN("realm"))) { 3487ba06c71SGlenn Strauss realm = ds->value; 3497ba06c71SGlenn Strauss } else if (buffer_is_equal_string(ds->key, CONST_STR_LEN("require"))) { 3507ba06c71SGlenn Strauss require = ds->value; 351b9e2be50SGlenn Strauss } else if (buffer_is_equal_string(ds->key, CONST_STR_LEN("algorithm"))) { 352b9e2be50SGlenn Strauss algos = ds->value; 3537ba06c71SGlenn Strauss } else { 3547ba06c71SGlenn Strauss log_error_write(srv, __FILE__, __LINE__, "ssbs", 3557ba06c71SGlenn Strauss "the field is unknown in:", 3567ba06c71SGlenn Strauss "auth.require = ( \"...\" => ( ..., -> \"", 3577ba06c71SGlenn Strauss da_file->value->data[m]->key, 3587ba06c71SGlenn Strauss "\" <- => \"...\" ) )"); 3597ba06c71SGlenn Strauss 3607ba06c71SGlenn Strauss return HANDLER_ERROR; 3617ba06c71SGlenn Strauss } 3627ba06c71SGlenn Strauss } else { 3637ba06c71SGlenn Strauss log_error_write(srv, __FILE__, __LINE__, "ssbs", 3647ba06c71SGlenn Strauss "a string was expected for:", 3657ba06c71SGlenn Strauss "auth.require = ( \"...\" => ( ..., -> \"", 3667ba06c71SGlenn Strauss da_file->value->data[m]->key, 3677ba06c71SGlenn Strauss "\" <- => \"...\" ) )"); 3687ba06c71SGlenn Strauss 3697ba06c71SGlenn Strauss return HANDLER_ERROR; 3707ba06c71SGlenn Strauss } 3717ba06c71SGlenn Strauss } 3727ba06c71SGlenn Strauss 3737ba06c71SGlenn Strauss if (buffer_string_is_empty(method)) { 3747ba06c71SGlenn Strauss log_error_write(srv, __FILE__, __LINE__, "ss", 3757ba06c71SGlenn Strauss "the method field is missing or blank in:", 3767ba06c71SGlenn Strauss "auth.require = ( \"...\" => ( ..., \"method\" => \"...\" ) )"); 3777ba06c71SGlenn Strauss return HANDLER_ERROR; 3787ba06c71SGlenn Strauss } else { 3797ba06c71SGlenn Strauss auth_scheme = http_auth_scheme_get(method); 3807ba06c71SGlenn Strauss if (NULL == auth_scheme) { 3817ba06c71SGlenn Strauss log_error_write(srv, __FILE__, __LINE__, "sbss", 3827ba06c71SGlenn Strauss "unknown method", method, "(e.g. \"basic\", \"digest\" or \"extern\") in", 3837ba06c71SGlenn Strauss "auth.require = ( \"...\" => ( ..., \"method\" => \"...\") )"); 3847ba06c71SGlenn Strauss return HANDLER_ERROR; 3857ba06c71SGlenn Strauss } 3867ba06c71SGlenn Strauss } 3877ba06c71SGlenn Strauss 3887ba06c71SGlenn Strauss if (buffer_is_empty(realm)) { 3897ba06c71SGlenn Strauss log_error_write(srv, __FILE__, __LINE__, "ss", 3907ba06c71SGlenn Strauss "the realm field is missing in:", 3917ba06c71SGlenn Strauss "auth.require = ( \"...\" => ( ..., \"realm\" => \"...\" ) )"); 3927ba06c71SGlenn Strauss return HANDLER_ERROR; 3937ba06c71SGlenn Strauss } 3947ba06c71SGlenn Strauss 3957ba06c71SGlenn Strauss if (buffer_string_is_empty(require)) { 3967ba06c71SGlenn Strauss log_error_write(srv, __FILE__, __LINE__, "ss", 3977ba06c71SGlenn Strauss "the require field is missing or blank in:", 3987ba06c71SGlenn Strauss "auth.require = ( \"...\" => ( ..., \"require\" => \"...\" ) )"); 3997ba06c71SGlenn Strauss return HANDLER_ERROR; 4007ba06c71SGlenn Strauss } 4017ba06c71SGlenn Strauss 402b9e2be50SGlenn Strauss if (buffer_string_is_empty(algos)) { 403b9e2be50SGlenn Strauss algorithm |= HTTP_AUTH_DIGEST_MD5; 404b9e2be50SGlenn Strauss } else if (!mod_auth_algorithms_parse(&algorithm, algos)) { 405b9e2be50SGlenn Strauss log_error_write(srv, __FILE__, __LINE__, "ss", 406b9e2be50SGlenn Strauss "invalid algorithm in:", 407b9e2be50SGlenn Strauss "auth.require = ( \"...\" => ( ..., \"algorithm\" => \"...\" ) )"); 408b9e2be50SGlenn Strauss return HANDLER_ERROR; 409b9e2be50SGlenn Strauss } 410b9e2be50SGlenn Strauss 411609e9a50SGlenn Strauss if (require) { /*(always true at this point)*/ 4127ba06c71SGlenn Strauss data_auth * const dauth = data_auth_init(); 4137ba06c71SGlenn Strauss buffer_copy_buffer(dauth->key, da_file->key); 4147ba06c71SGlenn Strauss dauth->require->scheme = auth_scheme; 415b9e2be50SGlenn Strauss dauth->require->algorithm = algorithm; 4167ba06c71SGlenn Strauss buffer_copy_buffer(dauth->require->realm, realm); 4177ba06c71SGlenn Strauss if (!mod_auth_require_parse(srv, dauth->require, require)) { 4188c7f1dfbSGlenn Strauss dauth->fn->free((data_unset *)dauth); 4197ba06c71SGlenn Strauss return HANDLER_ERROR; 4207ba06c71SGlenn Strauss } 4217ba06c71SGlenn Strauss array_insert_unique(s->auth_require, (data_unset *)dauth); 4227ba06c71SGlenn Strauss } 4237ba06c71SGlenn Strauss } 4247ba06c71SGlenn Strauss } 4257ba06c71SGlenn Strauss 4267ba06c71SGlenn Strauss return HANDLER_GO_ON; 4277ba06c71SGlenn Strauss } 4287ba06c71SGlenn Strauss 429bcdc6a3bSJan Kneschke #define PATCH(x) \ 430bcdc6a3bSJan Kneschke p->conf.x = s->x; 4317ba06c71SGlenn Strauss static int mod_auth_patch_connection(server *srv, connection *con, plugin_data *p) { 432bcdc6a3bSJan Kneschke size_t i, j; 4337ba06c71SGlenn Strauss plugin_config *s = p->config_storage[0]; 4344a6ce4aeSJan Kneschke 4354a6ce4aeSJan Kneschke PATCH(auth_backend); 4364a6ce4aeSJan Kneschke PATCH(auth_require); 437f54d628cSGlenn Strauss PATCH(auth_extern_authn); 438bcdc6a3bSJan Kneschke 439bcdc6a3bSJan Kneschke /* skip the first, the global context */ 440bcdc6a3bSJan Kneschke for (i = 1; i < srv->config_context->used; i++) { 441bcdc6a3bSJan Kneschke data_config *dc = (data_config *)srv->config_context->data[i]; 4424a6ce4aeSJan Kneschke s = p->config_storage[i]; 443bcdc6a3bSJan Kneschke 444bcdc6a3bSJan Kneschke /* condition didn't match */ 445bcdc6a3bSJan Kneschke if (!config_check_cond(srv, con, dc)) continue; 446bcdc6a3bSJan Kneschke 447bcdc6a3bSJan Kneschke /* merge config */ 448bcdc6a3bSJan Kneschke for (j = 0; j < dc->value->used; j++) { 449bcdc6a3bSJan Kneschke data_unset *du = dc->value->data[j]; 450bcdc6a3bSJan Kneschke 451bcdc6a3bSJan Kneschke if (buffer_is_equal_string(du->key, CONST_STR_LEN("auth.backend"))) { 452bcdc6a3bSJan Kneschke PATCH(auth_backend); 453bcdc6a3bSJan Kneschke } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("auth.require"))) { 454bcdc6a3bSJan Kneschke PATCH(auth_require); 455f54d628cSGlenn Strauss } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("auth.extern-authn"))) { 456f54d628cSGlenn Strauss PATCH(auth_extern_authn); 457bcdc6a3bSJan Kneschke } 458bcdc6a3bSJan Kneschke } 459bcdc6a3bSJan Kneschke } 460bcdc6a3bSJan Kneschke 461bcdc6a3bSJan Kneschke return 0; 462bcdc6a3bSJan Kneschke } 463bcdc6a3bSJan Kneschke #undef PATCH 464bcdc6a3bSJan Kneschke 4657ba06c71SGlenn Strauss static handler_t mod_auth_uri_handler(server *srv, connection *con, void *p_d) { 4667ba06c71SGlenn Strauss plugin_data *p = p_d; 467d61f3381SGlenn Strauss data_auth *dauth; 46881b2d1f0SGlenn Strauss 4697ba06c71SGlenn Strauss mod_auth_patch_connection(srv, con, p); 47081b2d1f0SGlenn Strauss 4717ba06c71SGlenn Strauss if (p->conf.auth_require == NULL) return HANDLER_GO_ON; 47281b2d1f0SGlenn Strauss 4737ba06c71SGlenn Strauss /* search auth directives for first prefix match against URL path */ 4747ba06c71SGlenn Strauss /* if we have a case-insensitive FS we have to lower-case the URI here too */ 475d61f3381SGlenn Strauss dauth = (!con->conf.force_lowercase_filenames) 476d61f3381SGlenn Strauss ? (data_auth *)array_match_key_prefix(p->conf.auth_require, con->uri.path) 477d61f3381SGlenn Strauss : (data_auth *)array_match_key_prefix_nc(p->conf.auth_require, con->uri.path); 478d61f3381SGlenn Strauss if (NULL == dauth) return HANDLER_GO_ON; 4797ba06c71SGlenn Strauss 480d61f3381SGlenn Strauss { 4817ba06c71SGlenn Strauss const http_auth_scheme_t * const scheme = dauth->require->scheme; 482f54d628cSGlenn Strauss if (p->conf.auth_extern_authn) { 4833dd3cde9SGlenn Strauss buffer *vb = http_header_env_get(con, CONST_STR_LEN("REMOTE_USER")); 4843dd3cde9SGlenn Strauss if (NULL != vb && http_auth_match_rules(dauth->require, vb->ptr, NULL, NULL)) { 485f54d628cSGlenn Strauss return HANDLER_GO_ON; 486f54d628cSGlenn Strauss } 487f54d628cSGlenn Strauss } 4887ba06c71SGlenn Strauss return scheme->checkfn(srv, con, scheme->p_d, dauth->require, p->conf.auth_backend); 4897ba06c71SGlenn Strauss } 4907ba06c71SGlenn Strauss } 4917ba06c71SGlenn Strauss 4927ba06c71SGlenn Strauss int mod_auth_plugin_init(plugin *p); 4937ba06c71SGlenn Strauss int mod_auth_plugin_init(plugin *p) { 4947ba06c71SGlenn Strauss p->version = LIGHTTPD_VERSION_ID; 4957ba06c71SGlenn Strauss p->name = buffer_init_string("auth"); 4967ba06c71SGlenn Strauss p->init = mod_auth_init; 4977ba06c71SGlenn Strauss p->set_defaults = mod_auth_set_defaults; 4987ba06c71SGlenn Strauss p->handle_uri_clean = mod_auth_uri_handler; 4997ba06c71SGlenn Strauss p->cleanup = mod_auth_free; 5007ba06c71SGlenn Strauss 5017ba06c71SGlenn Strauss p->data = NULL; 5027ba06c71SGlenn Strauss 50381b2d1f0SGlenn Strauss return 0; 50481b2d1f0SGlenn Strauss } 50581b2d1f0SGlenn Strauss 50681b2d1f0SGlenn Strauss 50781b2d1f0SGlenn Strauss 50881b2d1f0SGlenn Strauss 5097ba06c71SGlenn Strauss /* 5107ba06c71SGlenn Strauss * auth schemes (basic, digest, extern) 5117ba06c71SGlenn Strauss * 5127ba06c71SGlenn Strauss * (could be in separate file from mod_auth.c as long as registration occurs) 5137ba06c71SGlenn Strauss */ 51481b2d1f0SGlenn Strauss 5157ba06c71SGlenn Strauss #include "base64.h" 5167ba06c71SGlenn Strauss #include "md5.h" 5177f4e156eSGlenn Strauss #include "rand.h" 5183dd3cde9SGlenn Strauss #include "http_header.h" 51981b2d1f0SGlenn Strauss 5204b3a91e6SGlenn Strauss static handler_t mod_auth_send_400_bad_request(server *srv, connection *con) { 5214b3a91e6SGlenn Strauss UNUSED(srv); 5224b3a91e6SGlenn Strauss 5234b3a91e6SGlenn Strauss /* a field was missing or invalid */ 5244b3a91e6SGlenn Strauss con->http_status = 400; /* Bad Request */ 5254b3a91e6SGlenn Strauss con->mode = DIRECT; 5264b3a91e6SGlenn Strauss 5274b3a91e6SGlenn Strauss return HANDLER_FINISHED; 5284b3a91e6SGlenn Strauss } 5294b3a91e6SGlenn Strauss 5307ba06c71SGlenn Strauss static handler_t mod_auth_send_401_unauthorized_basic(server *srv, connection *con, buffer *realm) { 5314b3a91e6SGlenn Strauss con->http_status = 401; 5324b3a91e6SGlenn Strauss con->mode = DIRECT; 5334b3a91e6SGlenn Strauss 5347ba06c71SGlenn Strauss buffer_copy_string_len(srv->tmp_buf, CONST_STR_LEN("Basic realm=\"")); 5357ba06c71SGlenn Strauss buffer_append_string_buffer(srv->tmp_buf, realm); 5367ba06c71SGlenn Strauss buffer_append_string_len(srv->tmp_buf, CONST_STR_LEN("\", charset=\"UTF-8\"")); 5374b3a91e6SGlenn Strauss 5383dd3cde9SGlenn Strauss http_header_response_set(con, HTTP_HEADER_OTHER, CONST_STR_LEN("WWW-Authenticate"), CONST_BUF_LEN(srv->tmp_buf)); 5394b3a91e6SGlenn Strauss 5404b3a91e6SGlenn Strauss return HANDLER_FINISHED; 5414b3a91e6SGlenn Strauss } 5424b3a91e6SGlenn Strauss 5437ba06c71SGlenn Strauss static handler_t mod_auth_check_basic(server *srv, connection *con, void *p_d, const struct http_auth_require_t *require, const struct http_auth_backend_t *backend) { 5443dd3cde9SGlenn Strauss buffer *b = http_header_request_get(con, HTTP_HEADER_AUTHORIZATION, CONST_STR_LEN("Authorization")); 54581b2d1f0SGlenn Strauss buffer *username; 54681b2d1f0SGlenn Strauss char *pw; 5477ba06c71SGlenn Strauss handler_t rc = HANDLER_UNSET; 54881b2d1f0SGlenn Strauss 5497ba06c71SGlenn Strauss UNUSED(p_d); 55081b2d1f0SGlenn Strauss 5517ba06c71SGlenn Strauss if (NULL == backend) { 5527ba06c71SGlenn Strauss log_error_write(srv, __FILE__, __LINE__, "sb", "auth.backend not configured for", con->uri.path); 5537ba06c71SGlenn Strauss con->http_status = 500; 5547ba06c71SGlenn Strauss con->mode = DIRECT; 5557ba06c71SGlenn Strauss return HANDLER_FINISHED; 5567ba06c71SGlenn Strauss } 5577ba06c71SGlenn Strauss 5583dd3cde9SGlenn Strauss if (NULL == b) { 5597ba06c71SGlenn Strauss return mod_auth_send_401_unauthorized_basic(srv, con, require->realm); 5607ba06c71SGlenn Strauss } 5617ba06c71SGlenn Strauss 56213008156SGlenn Strauss if (!buffer_eq_icase_ssn(b->ptr, CONST_STR_LEN("Basic "))) { 5636b887f35SGlenn Strauss return mod_auth_send_401_unauthorized_basic(srv, con, require->realm); 5644b3a91e6SGlenn Strauss } 5659bc61f16SGlenn Strauss #ifdef __COVERITY__ 5663dd3cde9SGlenn Strauss if (buffer_string_length(b) < sizeof("Basic ")-1) { 5679bc61f16SGlenn Strauss return mod_auth_send_400_bad_request(srv, con); 5689bc61f16SGlenn Strauss } 5699bc61f16SGlenn Strauss #endif 57081b2d1f0SGlenn Strauss 57181b2d1f0SGlenn Strauss username = buffer_init(); 57281b2d1f0SGlenn Strauss 5738047c2f4SGlenn Strauss /* coverity[overflow_sink : FALSE] */ 5747ba06c71SGlenn Strauss if (!buffer_append_base64_decode(username, b->ptr+sizeof("Basic ")-1, buffer_string_length(b)-(sizeof("Basic ")-1), BASE64_STANDARD)) { 5757ba06c71SGlenn Strauss log_error_write(srv, __FILE__, __LINE__, "sb", "decoding base64-string failed", username); 57681b2d1f0SGlenn Strauss 57781b2d1f0SGlenn Strauss buffer_free(username); 5784b3a91e6SGlenn Strauss return mod_auth_send_400_bad_request(srv, con); 57981b2d1f0SGlenn Strauss } 58081b2d1f0SGlenn Strauss 58181b2d1f0SGlenn Strauss /* r2 == user:password */ 58281b2d1f0SGlenn Strauss if (NULL == (pw = strchr(username->ptr, ':'))) { 5837ba06c71SGlenn Strauss log_error_write(srv, __FILE__, __LINE__, "sb", "missing ':' in", username); 58481b2d1f0SGlenn Strauss 58581b2d1f0SGlenn Strauss buffer_free(username); 5864b3a91e6SGlenn Strauss return mod_auth_send_400_bad_request(srv, con); 58781b2d1f0SGlenn Strauss } 58881b2d1f0SGlenn Strauss 58981b2d1f0SGlenn Strauss buffer_string_set_length(username, pw - username->ptr); 59081b2d1f0SGlenn Strauss pw++; 59181b2d1f0SGlenn Strauss 592a401c946SGlenn Strauss rc = backend->basic(srv, con, backend->p_d, require, username, pw); 5937ba06c71SGlenn Strauss switch (rc) { 5944b3a91e6SGlenn Strauss case HANDLER_GO_ON: 5953dd3cde9SGlenn Strauss http_auth_setenv(con, CONST_BUF_LEN(username), CONST_STR_LEN("Basic")); 5964b3a91e6SGlenn Strauss break; 5974b3a91e6SGlenn Strauss case HANDLER_WAIT_FOR_EVENT: 5984b3a91e6SGlenn Strauss case HANDLER_FINISHED: 5997ba06c71SGlenn Strauss break; 6004b3a91e6SGlenn Strauss case HANDLER_ERROR: 6014b3a91e6SGlenn Strauss default: 60228d12134SGlenn Strauss log_error_write(srv, __FILE__, __LINE__, "sbsBsB", "password doesn't match for", con->uri.path, "username:", username, ", IP:", con->dst_addr_buf); 6038bddac92SGlenn Strauss con->keep_alive = 0; /*(disable keep-alive if bad password)*/ 6047ba06c71SGlenn Strauss rc = HANDLER_UNSET; 6057ba06c71SGlenn Strauss break; 60681b2d1f0SGlenn Strauss } 60781b2d1f0SGlenn Strauss 6084b3a91e6SGlenn Strauss buffer_free(username); 6097ba06c71SGlenn Strauss return (HANDLER_UNSET != rc) ? rc : mod_auth_send_401_unauthorized_basic(srv, con, require->realm); 61081b2d1f0SGlenn Strauss } 61181b2d1f0SGlenn Strauss 61281b2d1f0SGlenn Strauss 613b9e2be50SGlenn Strauss #ifdef USE_OPENSSL_CRYPTO 614b9e2be50SGlenn Strauss 615b9e2be50SGlenn Strauss static void mod_auth_digest_mutate_sha256(http_auth_info_t *ai, const char *m, const char *uri, const char *nonce, const char *cnonce, const char *nc, const char *qop) { 616b9e2be50SGlenn Strauss SHA256_CTX ctx; 617b9e2be50SGlenn Strauss char a1[HTTP_AUTH_DIGEST_SHA256_BINLEN*2+1]; 618b9e2be50SGlenn Strauss char a2[HTTP_AUTH_DIGEST_SHA256_BINLEN*2+1]; 619b9e2be50SGlenn Strauss 620b9e2be50SGlenn Strauss if (ai->dalgo & HTTP_AUTH_DIGEST_SESS) { 621b9e2be50SGlenn Strauss SHA256_Init(&ctx); 622b9e2be50SGlenn Strauss li_tohex(a1, sizeof(a1), (const char *)ai->digest, ai->dlen); 623b9e2be50SGlenn Strauss SHA256_Update(&ctx, (unsigned char *)a1, sizeof(a1)-1); 624b9e2be50SGlenn Strauss SHA256_Update(&ctx, CONST_STR_LEN(":")); 625b9e2be50SGlenn Strauss SHA256_Update(&ctx, (unsigned char *)nonce, strlen(nonce)); 626b9e2be50SGlenn Strauss SHA256_Update(&ctx, CONST_STR_LEN(":")); 627b9e2be50SGlenn Strauss SHA256_Update(&ctx, (unsigned char *)cnonce, strlen(cnonce)); 628b9e2be50SGlenn Strauss SHA256_Final(ai->digest, &ctx); 629b9e2be50SGlenn Strauss } 630b9e2be50SGlenn Strauss 631b9e2be50SGlenn Strauss li_tohex(a1, sizeof(a1), (const char *)ai->digest, ai->dlen); 632b9e2be50SGlenn Strauss 633b9e2be50SGlenn Strauss /* calculate H(A2) */ 634b9e2be50SGlenn Strauss SHA256_Init(&ctx); 635b9e2be50SGlenn Strauss SHA256_Update(&ctx, (unsigned char *)m, strlen(m)); 636b9e2be50SGlenn Strauss SHA256_Update(&ctx, CONST_STR_LEN(":")); 637b9e2be50SGlenn Strauss SHA256_Update(&ctx, (unsigned char *)uri, strlen(uri)); 638b9e2be50SGlenn Strauss #if 0 639b9e2be50SGlenn Strauss /* qop=auth-int not supported, already checked in caller */ 640fc42ec12SGlenn Strauss if (qop && buffer_eq_icase_ss(qop, strlen(qop), CONST_STR_LEN("auth-int"))){ 641b9e2be50SGlenn Strauss SHA256_Update(&ctx, CONST_STR_LEN(":")); 642b9e2be50SGlenn Strauss SHA256_Update(&ctx, (unsigned char *) [body checksum], ai->dlen*2); 643b9e2be50SGlenn Strauss } 644b9e2be50SGlenn Strauss #endif 645b9e2be50SGlenn Strauss SHA256_Final(ai->digest, &ctx); 646b9e2be50SGlenn Strauss li_tohex(a2, sizeof(a2), (const char *)ai->digest, ai->dlen); 647b9e2be50SGlenn Strauss 648b9e2be50SGlenn Strauss /* calculate response */ 649b9e2be50SGlenn Strauss SHA256_Init(&ctx); 650b9e2be50SGlenn Strauss SHA256_Update(&ctx, (unsigned char *)a1, sizeof(a1)-1); 651b9e2be50SGlenn Strauss SHA256_Update(&ctx, CONST_STR_LEN(":")); 652b9e2be50SGlenn Strauss SHA256_Update(&ctx, (unsigned char *)nonce, strlen(nonce)); 653b9e2be50SGlenn Strauss SHA256_Update(&ctx, CONST_STR_LEN(":")); 654b9e2be50SGlenn Strauss if (qop && *qop) { 655b9e2be50SGlenn Strauss SHA256_Update(&ctx, (unsigned char *)nc, strlen(nc)); 656b9e2be50SGlenn Strauss SHA256_Update(&ctx, CONST_STR_LEN(":")); 657b9e2be50SGlenn Strauss SHA256_Update(&ctx, (unsigned char *)cnonce, strlen(cnonce)); 658b9e2be50SGlenn Strauss SHA256_Update(&ctx, CONST_STR_LEN(":")); 659b9e2be50SGlenn Strauss SHA256_Update(&ctx, (unsigned char *)qop, strlen(qop)); 660b9e2be50SGlenn Strauss SHA256_Update(&ctx, CONST_STR_LEN(":")); 661b9e2be50SGlenn Strauss } 662b9e2be50SGlenn Strauss SHA256_Update(&ctx, (unsigned char *)a2, sizeof(a2)-1); 663b9e2be50SGlenn Strauss SHA256_Final(ai->digest, &ctx); 664b9e2be50SGlenn Strauss } 665b9e2be50SGlenn Strauss 666b9e2be50SGlenn Strauss static void mod_auth_digest_nonce_sha256(buffer *b, time_t cur_ts, int rnd) { 667b9e2be50SGlenn Strauss SHA256_CTX ctx; 668b9e2be50SGlenn Strauss unsigned char h[HTTP_AUTH_DIGEST_SHA256_BINLEN]; 669b9e2be50SGlenn Strauss char hh[HTTP_AUTH_DIGEST_SHA256_BINLEN*2+1]; 670b9e2be50SGlenn Strauss force_assert(sizeof(hh) >= LI_ITOSTRING_LENGTH); 671b9e2be50SGlenn Strauss SHA256_Init(&ctx); 672b9e2be50SGlenn Strauss li_itostrn(hh, sizeof(hh), cur_ts); 673b9e2be50SGlenn Strauss SHA256_Update(&ctx, (unsigned char *)hh, strlen(hh)); 674b9e2be50SGlenn Strauss li_itostrn(hh, sizeof(hh), rnd); 675b9e2be50SGlenn Strauss SHA256_Update(&ctx, (unsigned char *)hh, strlen(hh)); 676b9e2be50SGlenn Strauss SHA256_Final(h, &ctx); 677b9e2be50SGlenn Strauss li_tohex(hh, sizeof(hh), (const char *)h, sizeof(h)); 678b9e2be50SGlenn Strauss buffer_append_string_len(b, hh, sizeof(hh)-1); 679b9e2be50SGlenn Strauss } 680b9e2be50SGlenn Strauss 681b9e2be50SGlenn Strauss #ifdef SHA512_256_DIGEST_LENGTH 682b9e2be50SGlenn Strauss 683b9e2be50SGlenn Strauss static void mod_auth_digest_mutate_sha512_256(http_auth_info_t *ai, const char *m, const char *uri, const char *nonce, const char *cnonce, const char *nc, const char *qop) { 684b9e2be50SGlenn Strauss SHA512_CTX ctx; 685b9e2be50SGlenn Strauss char a1[HTTP_AUTH_DIGEST_SHA512_256_BINLEN*2+1]; 686b9e2be50SGlenn Strauss char a2[HTTP_AUTH_DIGEST_SHA512_256_BINLEN*2+1]; 687b9e2be50SGlenn Strauss 688b9e2be50SGlenn Strauss if (ai->dalgo & HTTP_AUTH_DIGEST_SESS) { 689b9e2be50SGlenn Strauss SHA512_256_Init(&ctx); 690b9e2be50SGlenn Strauss li_tohex(a1, sizeof(a1), (const char *)ai->digest, ai->dlen); 691b9e2be50SGlenn Strauss SHA512_256_Update(&ctx, (unsigned char *)a1, sizeof(a1)-1); 692b9e2be50SGlenn Strauss SHA512_256_Update(&ctx, CONST_STR_LEN(":")); 693b9e2be50SGlenn Strauss SHA512_256_Update(&ctx, (unsigned char *)nonce, strlen(nonce)); 694b9e2be50SGlenn Strauss SHA512_256_Update(&ctx, CONST_STR_LEN(":")); 695b9e2be50SGlenn Strauss SHA512_256_Update(&ctx, (unsigned char *)cnonce, strlen(cnonce)); 696b9e2be50SGlenn Strauss SHA512_256_Final(ai->digest, &ctx); 697b9e2be50SGlenn Strauss } 698b9e2be50SGlenn Strauss 699b9e2be50SGlenn Strauss li_tohex(a1, sizeof(a1), (const char *)ai->digest, ai->dlen); 700b9e2be50SGlenn Strauss 701b9e2be50SGlenn Strauss /* calculate H(A2) */ 702b9e2be50SGlenn Strauss SHA512_256_Init(&ctx); 703b9e2be50SGlenn Strauss SHA512_256_Update(&ctx, (unsigned char *)m, strlen(m)); 704b9e2be50SGlenn Strauss SHA512_256_Update(&ctx, CONST_STR_LEN(":")); 705b9e2be50SGlenn Strauss SHA512_256_Update(&ctx, (unsigned char *)uri, strlen(uri)); 706b9e2be50SGlenn Strauss #if 0 707b9e2be50SGlenn Strauss /* qop=auth-int not supported, already checked in caller */ 708fc42ec12SGlenn Strauss if (qop && buffer_eq_icase_ss(qop, strlen(qop), CONST_STR_LEN("auth-int"))){ 709b9e2be50SGlenn Strauss SHA512_256_Update(&ctx, CONST_STR_LEN(":")); 710b9e2be50SGlenn Strauss SHA512_256_Update(&ctx, (unsigned char *)[body checksum], ai->dlen*2); 711b9e2be50SGlenn Strauss } 712b9e2be50SGlenn Strauss #endif 713b9e2be50SGlenn Strauss SHA512_256_Final(ai->digest, &ctx); 714b9e2be50SGlenn Strauss li_tohex(a2, sizeof(a2), (const char *)ai->digest, ai->dlen); 715b9e2be50SGlenn Strauss 716b9e2be50SGlenn Strauss /* calculate response */ 717b9e2be50SGlenn Strauss SHA512_256_Init(&ctx); 718b9e2be50SGlenn Strauss SHA512_256_Update(&ctx, (unsigned char *)a1, sizeof(a1)-1); 719b9e2be50SGlenn Strauss SHA512_256_Update(&ctx, CONST_STR_LEN(":")); 720b9e2be50SGlenn Strauss SHA512_256_Update(&ctx, (unsigned char *)nonce, strlen(nonce)); 721b9e2be50SGlenn Strauss SHA512_256_Update(&ctx, CONST_STR_LEN(":")); 722b9e2be50SGlenn Strauss if (qop && *qop) { 723b9e2be50SGlenn Strauss SHA512_256_Update(&ctx, (unsigned char *)nc, strlen(nc)); 724b9e2be50SGlenn Strauss SHA512_256_Update(&ctx, CONST_STR_LEN(":")); 725b9e2be50SGlenn Strauss SHA512_256_Update(&ctx, (unsigned char *)cnonce, strlen(cnonce)); 726b9e2be50SGlenn Strauss SHA512_256_Update(&ctx, CONST_STR_LEN(":")); 727b9e2be50SGlenn Strauss SHA512_256_Update(&ctx, (unsigned char *)qop, strlen(qop)); 728b9e2be50SGlenn Strauss SHA512_256_Update(&ctx, CONST_STR_LEN(":")); 729b9e2be50SGlenn Strauss } 730b9e2be50SGlenn Strauss SHA512_256_Update(&ctx, (unsigned char *)a2, sizeof(a2)-1); 731b9e2be50SGlenn Strauss SHA512_256_Final(ai->digest, &ctx); 732b9e2be50SGlenn Strauss } 733b9e2be50SGlenn Strauss 734b9e2be50SGlenn Strauss static void mod_auth_digest_nonce_sha512_256(buffer *b, time_t cur_ts, int rnd) { 735b9e2be50SGlenn Strauss SHA512_CTX ctx; 736b9e2be50SGlenn Strauss unsigned char h[HTTP_AUTH_DIGEST_SHA512_256_BINLEN]; 737b9e2be50SGlenn Strauss char hh[HTTP_AUTH_DIGEST_SHA512_256_BINLEN*2+1]; 738b9e2be50SGlenn Strauss force_assert(sizeof(hh) >= LI_ITOSTRING_LENGTH); 739b9e2be50SGlenn Strauss SHA512_256_Init(&ctx); 740b9e2be50SGlenn Strauss li_itostrn(hh, sizeof(hh), cur_ts); 741b9e2be50SGlenn Strauss SHA512_256_Update(&ctx, (unsigned char *)hh, strlen(hh)); 742b9e2be50SGlenn Strauss li_itostrn(hh, sizeof(hh), rnd); 743b9e2be50SGlenn Strauss SHA512_256_Update(&ctx, (unsigned char *)hh, strlen(hh)); 744b9e2be50SGlenn Strauss SHA512_256_Final(h, &ctx); 745b9e2be50SGlenn Strauss li_tohex(hh, sizeof(hh), (const char *)h, sizeof(h)); 746b9e2be50SGlenn Strauss buffer_append_string_len(b, hh, sizeof(hh)-1); 747b9e2be50SGlenn Strauss } 748b9e2be50SGlenn Strauss 749b9e2be50SGlenn Strauss #endif /* SHA512_256_DIGEST_LENGTH */ 750b9e2be50SGlenn Strauss 751b9e2be50SGlenn Strauss #endif /* USE_OPENSSL_CRYPTO */ 752b9e2be50SGlenn Strauss 753b9e2be50SGlenn Strauss static void mod_auth_digest_mutate_md5(http_auth_info_t *ai, const char *m, const char *uri, const char *nonce, const char *cnonce, const char *nc, const char *qop) { 754b9e2be50SGlenn Strauss li_MD5_CTX ctx; 755b9e2be50SGlenn Strauss char a1[HTTP_AUTH_DIGEST_MD5_BINLEN*2+1]; 756b9e2be50SGlenn Strauss char a2[HTTP_AUTH_DIGEST_MD5_BINLEN*2+1]; 757b9e2be50SGlenn Strauss 758b9e2be50SGlenn Strauss if (ai->dalgo & HTTP_AUTH_DIGEST_SESS) { 759b9e2be50SGlenn Strauss li_MD5_Init(&ctx); 760b9e2be50SGlenn Strauss /* http://www.rfc-editor.org/errata_search.php?rfc=2617 761b9e2be50SGlenn Strauss * Errata ID: 1649 */ 762b9e2be50SGlenn Strauss li_tohex(a1, sizeof(a1), (const char *)ai->digest, ai->dlen); 763b9e2be50SGlenn Strauss li_MD5_Update(&ctx, (unsigned char *)a1, sizeof(a1)-1); 764b9e2be50SGlenn Strauss li_MD5_Update(&ctx, CONST_STR_LEN(":")); 765b9e2be50SGlenn Strauss li_MD5_Update(&ctx, (unsigned char *)nonce, strlen(nonce)); 766b9e2be50SGlenn Strauss li_MD5_Update(&ctx, CONST_STR_LEN(":")); 767b9e2be50SGlenn Strauss li_MD5_Update(&ctx, (unsigned char *)cnonce, strlen(cnonce)); 768b9e2be50SGlenn Strauss li_MD5_Final(ai->digest, &ctx); 769b9e2be50SGlenn Strauss } 770b9e2be50SGlenn Strauss 771b9e2be50SGlenn Strauss li_tohex(a1, sizeof(a1), (const char *)ai->digest, ai->dlen); 772b9e2be50SGlenn Strauss 773b9e2be50SGlenn Strauss /* calculate H(A2) */ 774b9e2be50SGlenn Strauss li_MD5_Init(&ctx); 775b9e2be50SGlenn Strauss li_MD5_Update(&ctx, (unsigned char *)m, strlen(m)); 776b9e2be50SGlenn Strauss li_MD5_Update(&ctx, CONST_STR_LEN(":")); 777b9e2be50SGlenn Strauss li_MD5_Update(&ctx, (unsigned char *)uri, strlen(uri)); 778b9e2be50SGlenn Strauss #if 0 779b9e2be50SGlenn Strauss /* qop=auth-int not supported, already checked in caller */ 780fc42ec12SGlenn Strauss if (qop && buffer_eq_icase_ss(qop, strlen(qop), CONST_STR_LEN("auth-int"))){ 781b9e2be50SGlenn Strauss li_MD5_Update(&ctx, CONST_STR_LEN(":")); 782b9e2be50SGlenn Strauss li_MD5_Update(&ctx, (unsigned char *) [body checksum], ai->dlen*2); 783b9e2be50SGlenn Strauss } 784b9e2be50SGlenn Strauss #endif 785b9e2be50SGlenn Strauss li_MD5_Final(ai->digest, &ctx); 786b9e2be50SGlenn Strauss li_tohex(a2, sizeof(a2), (const char *)ai->digest, ai->dlen); 787b9e2be50SGlenn Strauss 788b9e2be50SGlenn Strauss /* calculate response */ 789b9e2be50SGlenn Strauss li_MD5_Init(&ctx); 790b9e2be50SGlenn Strauss li_MD5_Update(&ctx, (unsigned char *)a1, sizeof(a1)-1); 791b9e2be50SGlenn Strauss li_MD5_Update(&ctx, CONST_STR_LEN(":")); 792b9e2be50SGlenn Strauss li_MD5_Update(&ctx, (unsigned char *)nonce, strlen(nonce)); 793b9e2be50SGlenn Strauss li_MD5_Update(&ctx, CONST_STR_LEN(":")); 794b9e2be50SGlenn Strauss if (qop && *qop) { 795b9e2be50SGlenn Strauss li_MD5_Update(&ctx, (unsigned char *)nc, strlen(nc)); 796b9e2be50SGlenn Strauss li_MD5_Update(&ctx, CONST_STR_LEN(":")); 797b9e2be50SGlenn Strauss li_MD5_Update(&ctx, (unsigned char *)cnonce, strlen(cnonce)); 798b9e2be50SGlenn Strauss li_MD5_Update(&ctx, CONST_STR_LEN(":")); 799b9e2be50SGlenn Strauss li_MD5_Update(&ctx, (unsigned char *)qop, strlen(qop)); 800b9e2be50SGlenn Strauss li_MD5_Update(&ctx, CONST_STR_LEN(":")); 801b9e2be50SGlenn Strauss } 802b9e2be50SGlenn Strauss li_MD5_Update(&ctx, (unsigned char *)a2, sizeof(a2)-1); 803b9e2be50SGlenn Strauss li_MD5_Final(ai->digest, &ctx); 804b9e2be50SGlenn Strauss } 805b9e2be50SGlenn Strauss 806b9e2be50SGlenn Strauss static void mod_auth_digest_nonce_md5(buffer *b, time_t cur_ts, int rnd) { 807b9e2be50SGlenn Strauss li_MD5_CTX ctx; 808b9e2be50SGlenn Strauss unsigned char h[HTTP_AUTH_DIGEST_MD5_BINLEN]; 809b9e2be50SGlenn Strauss char hh[HTTP_AUTH_DIGEST_MD5_BINLEN*2+1]; 810b9e2be50SGlenn Strauss force_assert(sizeof(hh) >= LI_ITOSTRING_LENGTH); 811b9e2be50SGlenn Strauss li_MD5_Init(&ctx); 812b9e2be50SGlenn Strauss li_itostrn(hh, sizeof(hh), cur_ts); 813b9e2be50SGlenn Strauss li_MD5_Update(&ctx, (unsigned char *)hh, strlen(hh)); 814b9e2be50SGlenn Strauss li_itostrn(hh, sizeof(hh), rnd); 815b9e2be50SGlenn Strauss li_MD5_Update(&ctx, (unsigned char *)hh, strlen(hh)); 816b9e2be50SGlenn Strauss li_MD5_Final(h, &ctx); 817b9e2be50SGlenn Strauss li_tohex(hh, sizeof(hh), (const char *)h, sizeof(h)); 818b9e2be50SGlenn Strauss buffer_append_string_len(b, hh, sizeof(hh)-1); 819b9e2be50SGlenn Strauss } 820b9e2be50SGlenn Strauss 821b9e2be50SGlenn Strauss static void mod_auth_digest_mutate(http_auth_info_t *ai, const char *m, const char *uri, const char *nonce, const char *cnonce, const char *nc, const char *qop) { 822b9e2be50SGlenn Strauss if (ai->dalgo & HTTP_AUTH_DIGEST_MD5) 823b9e2be50SGlenn Strauss mod_auth_digest_mutate_md5(ai, m, uri, nonce, cnonce, nc, qop); 824b9e2be50SGlenn Strauss #ifdef USE_OPENSSL_CRYPTO 825b9e2be50SGlenn Strauss else if (ai->dalgo & HTTP_AUTH_DIGEST_SHA256) 826b9e2be50SGlenn Strauss mod_auth_digest_mutate_sha256(ai, m, uri, nonce, cnonce, nc, qop); 827b9e2be50SGlenn Strauss #ifdef SHA512_256_DIGEST_LENGTH 828b9e2be50SGlenn Strauss else if (ai->dalgo & HTTP_AUTH_DIGEST_SHA512_256) 829b9e2be50SGlenn Strauss mod_auth_digest_mutate_sha512_256(ai, m, uri, nonce, cnonce, nc, qop); 830b9e2be50SGlenn Strauss #endif 831b9e2be50SGlenn Strauss #endif 832b9e2be50SGlenn Strauss } 833b9e2be50SGlenn Strauss 834b9e2be50SGlenn Strauss static void mod_auth_digest_www_authenticate(buffer *b, time_t cur_ts, const struct http_auth_require_t *require, int nonce_stale) { 835b9e2be50SGlenn Strauss const int rnd = li_rand_pseudo(); 836b9e2be50SGlenn Strauss int algos = nonce_stale ? nonce_stale : require->algorithm; 837b9e2be50SGlenn Strauss int n = 0; 838b9e2be50SGlenn Strauss void(*append_nonce[3])(buffer *, time_t, int); 839b9e2be50SGlenn Strauss unsigned int algolen[3]; 840b9e2be50SGlenn Strauss const char *algoname[3]; 841b9e2be50SGlenn Strauss #ifdef USE_OPENSSL_CRYPTO 842b9e2be50SGlenn Strauss #ifdef SHA512_256_DIGEST_LENGTH 843b9e2be50SGlenn Strauss if (algos & HTTP_AUTH_DIGEST_SHA512_256) { 844b9e2be50SGlenn Strauss append_nonce[n] = mod_auth_digest_nonce_sha512_256; 845b9e2be50SGlenn Strauss algoname[n] = "SHA-512-256"; 846b9e2be50SGlenn Strauss algolen[n] = sizeof("SHA-512-256")-1; 847b9e2be50SGlenn Strauss ++n; 848b9e2be50SGlenn Strauss } 849b9e2be50SGlenn Strauss #endif 850b9e2be50SGlenn Strauss if (algos & HTTP_AUTH_DIGEST_SHA256) { 851b9e2be50SGlenn Strauss append_nonce[n] = mod_auth_digest_nonce_sha256; 852b9e2be50SGlenn Strauss algoname[n] = "SHA-256"; 853b9e2be50SGlenn Strauss algolen[n] = sizeof("SHA-256")-1; 854b9e2be50SGlenn Strauss ++n; 855b9e2be50SGlenn Strauss } 856b9e2be50SGlenn Strauss #endif 857b9e2be50SGlenn Strauss if (algos & HTTP_AUTH_DIGEST_MD5) { 858b9e2be50SGlenn Strauss append_nonce[n] = mod_auth_digest_nonce_md5; 859b9e2be50SGlenn Strauss algoname[n] = "MD5"; 860b9e2be50SGlenn Strauss algolen[n] = sizeof("MD5")-1; 861b9e2be50SGlenn Strauss ++n; 862b9e2be50SGlenn Strauss } 863b9e2be50SGlenn Strauss 864b9e2be50SGlenn Strauss buffer_clear(b); 865b9e2be50SGlenn Strauss for (int i = 0; i < n; ++i) { 866b9e2be50SGlenn Strauss if (i > 0) { 867b9e2be50SGlenn Strauss buffer_append_string_len(b,CONST_STR_LEN("\r\nWWW-Authenticate: ")); 868b9e2be50SGlenn Strauss } 869b9e2be50SGlenn Strauss buffer_append_string_len(b, CONST_STR_LEN("Digest realm=\"")); 870b9e2be50SGlenn Strauss buffer_append_string_buffer(b, require->realm); 8711799e4c3SGlenn Strauss buffer_append_string_len(b, CONST_STR_LEN("\", charset=\"UTF-8\", algorithm=")); 872b9e2be50SGlenn Strauss buffer_append_string_len(b, algoname[i], algolen[i]); 8731799e4c3SGlenn Strauss buffer_append_string_len(b, CONST_STR_LEN(", nonce=\"")); 874b9e2be50SGlenn Strauss buffer_append_uint_hex(b, (uintmax_t)cur_ts); 875b9e2be50SGlenn Strauss buffer_append_string_len(b, CONST_STR_LEN(":")); 876b9e2be50SGlenn Strauss (append_nonce[i])(b, cur_ts, rnd); 877b9e2be50SGlenn Strauss buffer_append_string_len(b, CONST_STR_LEN("\", qop=\"auth\"")); 878b9e2be50SGlenn Strauss if (nonce_stale) { 879b9e2be50SGlenn Strauss buffer_append_string_len(b, CONST_STR_LEN(", stale=true")); 880b9e2be50SGlenn Strauss } 881b9e2be50SGlenn Strauss } 88281b2d1f0SGlenn Strauss } 88381b2d1f0SGlenn Strauss 8846ad325c6SGlenn Strauss static void mod_auth_digest_authentication_info(buffer *b, time_t cur_ts, int dalgo) { 8856ad325c6SGlenn Strauss const int rnd = li_rand_pseudo(); 8866ad325c6SGlenn Strauss void(*append_nonce)(buffer *, time_t, int); 8876ad325c6SGlenn Strauss switch (dalgo) { 8886ad325c6SGlenn Strauss #ifdef USE_OPENSSL_CRYPTO 8896ad325c6SGlenn Strauss #ifdef SHA512_256_DIGEST_LENGTH 8906ad325c6SGlenn Strauss case HTTP_AUTH_DIGEST_SHA512_256: 8916ad325c6SGlenn Strauss append_nonce = mod_auth_digest_nonce_sha512_256; 8926ad325c6SGlenn Strauss break; 8936ad325c6SGlenn Strauss #endif 8946ad325c6SGlenn Strauss case HTTP_AUTH_DIGEST_SHA256: 8956ad325c6SGlenn Strauss append_nonce = mod_auth_digest_nonce_sha256; 8966ad325c6SGlenn Strauss break; 8976ad325c6SGlenn Strauss #endif 8986ad325c6SGlenn Strauss /*case HTTP_AUTH_DIGEST_MD5:*/ 8996ad325c6SGlenn Strauss default: 9006ad325c6SGlenn Strauss append_nonce = mod_auth_digest_nonce_md5; 9016ad325c6SGlenn Strauss break; 9026ad325c6SGlenn Strauss } 9036ad325c6SGlenn Strauss buffer_clear(b); 9046ad325c6SGlenn Strauss buffer_append_string_len(b, CONST_STR_LEN("nextnonce=\"")); 9056ad325c6SGlenn Strauss buffer_append_uint_hex(b, (uintmax_t)cur_ts); 9066ad325c6SGlenn Strauss buffer_append_string_len(b, CONST_STR_LEN(":")); 9076ad325c6SGlenn Strauss (append_nonce)(b, cur_ts, rnd); 9086ad325c6SGlenn Strauss buffer_append_string_len(b, CONST_STR_LEN("\"")); 9096ad325c6SGlenn Strauss } 9106ad325c6SGlenn Strauss 91181b2d1f0SGlenn Strauss typedef struct { 91281b2d1f0SGlenn Strauss const char *key; 91381b2d1f0SGlenn Strauss int key_len; 91481b2d1f0SGlenn Strauss char **ptr; 91581b2d1f0SGlenn Strauss } digest_kv; 91681b2d1f0SGlenn Strauss 91728895ab2SGlenn Strauss static handler_t mod_auth_send_401_unauthorized_digest(server *srv, connection *con, const struct http_auth_require_t *require, int nonce_stale); 9187ba06c71SGlenn Strauss 9197ba06c71SGlenn Strauss static handler_t mod_auth_check_digest(server *srv, connection *con, void *p_d, const struct http_auth_require_t *require, const struct http_auth_backend_t *backend) { 9203dd3cde9SGlenn Strauss buffer *vb = http_header_request_get(con, HTTP_HEADER_AUTHORIZATION, CONST_STR_LEN("Authorization")); 9217ba06c71SGlenn Strauss 92281b2d1f0SGlenn Strauss char *username = NULL; 92381b2d1f0SGlenn Strauss char *realm = NULL; 92481b2d1f0SGlenn Strauss char *nonce = NULL; 92581b2d1f0SGlenn Strauss char *uri = NULL; 92681b2d1f0SGlenn Strauss char *algorithm = NULL; 92781b2d1f0SGlenn Strauss char *qop = NULL; 92881b2d1f0SGlenn Strauss char *cnonce = NULL; 92981b2d1f0SGlenn Strauss char *nc = NULL; 93081b2d1f0SGlenn Strauss char *respons = NULL; 93181b2d1f0SGlenn Strauss 93281b2d1f0SGlenn Strauss char *e, *c; 93381b2d1f0SGlenn Strauss const char *m = NULL; 93481b2d1f0SGlenn Strauss int i; 93581b2d1f0SGlenn Strauss buffer *b; 93660f4cf3aSGlenn Strauss http_auth_info_t ai; 937b9e2be50SGlenn Strauss unsigned char rdigest[HTTP_AUTH_DIGEST_SHA256_BINLEN]; 93881b2d1f0SGlenn Strauss 93981b2d1f0SGlenn Strauss 94081b2d1f0SGlenn Strauss /* init pointers */ 94181b2d1f0SGlenn Strauss #define S(x) \ 94281b2d1f0SGlenn Strauss x, sizeof(x)-1, NULL 94381b2d1f0SGlenn Strauss digest_kv dkv[10] = { 94481b2d1f0SGlenn Strauss { S("username=") }, 94581b2d1f0SGlenn Strauss { S("realm=") }, 94681b2d1f0SGlenn Strauss { S("nonce=") }, 94781b2d1f0SGlenn Strauss { S("uri=") }, 94881b2d1f0SGlenn Strauss { S("algorithm=") }, 94981b2d1f0SGlenn Strauss { S("qop=") }, 95081b2d1f0SGlenn Strauss { S("cnonce=") }, 95181b2d1f0SGlenn Strauss { S("nc=") }, 95281b2d1f0SGlenn Strauss { S("response=") }, 95381b2d1f0SGlenn Strauss 95481b2d1f0SGlenn Strauss { NULL, 0, NULL } 95581b2d1f0SGlenn Strauss }; 95681b2d1f0SGlenn Strauss #undef S 95781b2d1f0SGlenn Strauss 95881b2d1f0SGlenn Strauss dkv[0].ptr = &username; 95981b2d1f0SGlenn Strauss dkv[1].ptr = &realm; 96081b2d1f0SGlenn Strauss dkv[2].ptr = &nonce; 96181b2d1f0SGlenn Strauss dkv[3].ptr = &uri; 96281b2d1f0SGlenn Strauss dkv[4].ptr = &algorithm; 96381b2d1f0SGlenn Strauss dkv[5].ptr = &qop; 96481b2d1f0SGlenn Strauss dkv[6].ptr = &cnonce; 96581b2d1f0SGlenn Strauss dkv[7].ptr = &nc; 96681b2d1f0SGlenn Strauss dkv[8].ptr = &respons; 96781b2d1f0SGlenn Strauss 9687ba06c71SGlenn Strauss UNUSED(p_d); 9697ba06c71SGlenn Strauss 9707ba06c71SGlenn Strauss if (NULL == backend) { 9717ba06c71SGlenn Strauss log_error_write(srv, __FILE__, __LINE__, "sb", "auth.backend not configured for", con->uri.path); 9727ba06c71SGlenn Strauss con->http_status = 500; 9737ba06c71SGlenn Strauss con->mode = DIRECT; 9747ba06c71SGlenn Strauss return HANDLER_FINISHED; 9757ba06c71SGlenn Strauss } 9767ba06c71SGlenn Strauss 9773dd3cde9SGlenn Strauss if (NULL == vb) { 97828895ab2SGlenn Strauss return mod_auth_send_401_unauthorized_digest(srv, con, require, 0); 9797ba06c71SGlenn Strauss } 9807ba06c71SGlenn Strauss 98113008156SGlenn Strauss if (!buffer_eq_icase_ssn(vb->ptr, CONST_STR_LEN("Digest "))) { 98228895ab2SGlenn Strauss return mod_auth_send_401_unauthorized_digest(srv, con, require, 0); 983dadfb5fcSGlenn Strauss } else { 9843dd3cde9SGlenn Strauss size_t n = buffer_string_length(vb); 9859bc61f16SGlenn Strauss #ifdef __COVERITY__ 986dadfb5fcSGlenn Strauss if (n < sizeof("Digest ")-1) { 9879bc61f16SGlenn Strauss return mod_auth_send_400_bad_request(srv, con); 9889bc61f16SGlenn Strauss } 9899bc61f16SGlenn Strauss #endif 990dadfb5fcSGlenn Strauss n -= (sizeof("Digest ")-1); 9917ba06c71SGlenn Strauss b = buffer_init(); 9923dd3cde9SGlenn Strauss buffer_copy_string_len(b,vb->ptr+sizeof("Digest ")-1,n); 993dadfb5fcSGlenn Strauss } 99481b2d1f0SGlenn Strauss 99581b2d1f0SGlenn Strauss /* parse credentials from client */ 99681b2d1f0SGlenn Strauss for (c = b->ptr; *c; c++) { 99781b2d1f0SGlenn Strauss /* skip whitespaces */ 99881b2d1f0SGlenn Strauss while (*c == ' ' || *c == '\t') c++; 99981b2d1f0SGlenn Strauss if (!*c) break; 100081b2d1f0SGlenn Strauss 100181b2d1f0SGlenn Strauss for (i = 0; dkv[i].key; i++) { 100281b2d1f0SGlenn Strauss if ((0 == strncmp(c, dkv[i].key, dkv[i].key_len))) { 100381b2d1f0SGlenn Strauss if ((c[dkv[i].key_len] == '"') && 100481b2d1f0SGlenn Strauss (NULL != (e = strchr(c + dkv[i].key_len + 1, '"')))) { 100581b2d1f0SGlenn Strauss /* value with "..." */ 100681b2d1f0SGlenn Strauss *(dkv[i].ptr) = c + dkv[i].key_len + 1; 100781b2d1f0SGlenn Strauss c = e; 100881b2d1f0SGlenn Strauss 100981b2d1f0SGlenn Strauss *e = '\0'; 101081b2d1f0SGlenn Strauss } else if (NULL != (e = strchr(c + dkv[i].key_len, ','))) { 101181b2d1f0SGlenn Strauss /* value without "...", terminated by ',' */ 101281b2d1f0SGlenn Strauss *(dkv[i].ptr) = c + dkv[i].key_len; 101381b2d1f0SGlenn Strauss c = e; 101481b2d1f0SGlenn Strauss 101581b2d1f0SGlenn Strauss *e = '\0'; 101681b2d1f0SGlenn Strauss } else { 101781b2d1f0SGlenn Strauss /* value without "...", terminated by EOL */ 101881b2d1f0SGlenn Strauss *(dkv[i].ptr) = c + dkv[i].key_len; 101981b2d1f0SGlenn Strauss c += strlen(c) - 1; 102081b2d1f0SGlenn Strauss } 102181b2d1f0SGlenn Strauss break; 102281b2d1f0SGlenn Strauss } 102381b2d1f0SGlenn Strauss } 102481b2d1f0SGlenn Strauss } 102581b2d1f0SGlenn Strauss 102681b2d1f0SGlenn Strauss /* check if everything is transmitted */ 102781b2d1f0SGlenn Strauss if (!username || 102881b2d1f0SGlenn Strauss !realm || 102981b2d1f0SGlenn Strauss !nonce || 103081b2d1f0SGlenn Strauss !uri || 103181b2d1f0SGlenn Strauss (qop && (!nc || !cnonce)) || 103281b2d1f0SGlenn Strauss !respons ) { 103381b2d1f0SGlenn Strauss /* missing field */ 103481b2d1f0SGlenn Strauss 103581b2d1f0SGlenn Strauss log_error_write(srv, __FILE__, __LINE__, "s", 103681b2d1f0SGlenn Strauss "digest: missing field"); 103781b2d1f0SGlenn Strauss 103881b2d1f0SGlenn Strauss buffer_free(b); 10394b3a91e6SGlenn Strauss return mod_auth_send_400_bad_request(srv, con); 104081b2d1f0SGlenn Strauss } 104181b2d1f0SGlenn Strauss 104260f4cf3aSGlenn Strauss ai.username = username; 104360f4cf3aSGlenn Strauss ai.ulen = strlen(username); 104460f4cf3aSGlenn Strauss ai.realm = realm; 104560f4cf3aSGlenn Strauss ai.rlen = strlen(realm); 104660f4cf3aSGlenn Strauss 104760f4cf3aSGlenn Strauss if (!buffer_is_equal_string(require->realm, ai.realm, ai.rlen)) { 10485a6e4df8SGlenn Strauss log_error_write(srv, __FILE__, __LINE__, "s", 10495a6e4df8SGlenn Strauss "digest: realm mismatch"); 10505a6e4df8SGlenn Strauss buffer_free(b); 105128895ab2SGlenn Strauss return mod_auth_send_401_unauthorized_digest(srv, con, require, 0); 10525a6e4df8SGlenn Strauss } 10535a6e4df8SGlenn Strauss 1054b9e2be50SGlenn Strauss if (!mod_auth_algorithm_parse(&ai, algorithm) 1055b9e2be50SGlenn Strauss || !(require->algorithm & ai.dalgo & ~HTTP_AUTH_DIGEST_SESS)) { 1056b9e2be50SGlenn Strauss log_error_write(srv, __FILE__, __LINE__, "SSS", 1057b9e2be50SGlenn Strauss "digest: (", algorithm, "): invalid"); 1058b9e2be50SGlenn Strauss buffer_free(b); 1059b9e2be50SGlenn Strauss return mod_auth_send_400_bad_request(srv, con); 1060b9e2be50SGlenn Strauss } 1061b9e2be50SGlenn Strauss 106281b2d1f0SGlenn Strauss /** 106381b2d1f0SGlenn Strauss * protect the md5-sess against missing cnonce and nonce 106481b2d1f0SGlenn Strauss */ 1065b9e2be50SGlenn Strauss if ((ai.dalgo & HTTP_AUTH_DIGEST_SESS) && (!nonce || !cnonce)) { 1066b9e2be50SGlenn Strauss log_error_write(srv, __FILE__, __LINE__, "SSS", 1067b9e2be50SGlenn Strauss "digest: (", algorithm, "): missing field"); 106881b2d1f0SGlenn Strauss 106981b2d1f0SGlenn Strauss buffer_free(b); 10704b3a91e6SGlenn Strauss return mod_auth_send_400_bad_request(srv, con); 107181b2d1f0SGlenn Strauss } 107281b2d1f0SGlenn Strauss 1073b9e2be50SGlenn Strauss { 1074b9e2be50SGlenn Strauss size_t resplen = strlen(respons); 1075b9e2be50SGlenn Strauss if (0 != http_auth_digest_hex2bin(respons, resplen, 1076b9e2be50SGlenn Strauss rdigest, sizeof(rdigest)) 1077b9e2be50SGlenn Strauss || resplen != (ai.dlen << 1)) { 1078b9e2be50SGlenn Strauss log_error_write(srv, __FILE__, __LINE__, "SSS", 1079b9e2be50SGlenn Strauss "digest: (", respons, "): invalid format"); 1080b9e2be50SGlenn Strauss buffer_free(b); 1081b9e2be50SGlenn Strauss return mod_auth_send_400_bad_request(srv, con); 1082b9e2be50SGlenn Strauss } 1083b9e2be50SGlenn Strauss } 1084b9e2be50SGlenn Strauss 1085fc42ec12SGlenn Strauss if (qop && buffer_eq_icase_ss(qop, strlen(qop), CONST_STR_LEN("auth-int"))){ 108681b2d1f0SGlenn Strauss log_error_write(srv, __FILE__, __LINE__, "s", 108781b2d1f0SGlenn Strauss "digest: qop=auth-int not supported"); 108881b2d1f0SGlenn Strauss 108981b2d1f0SGlenn Strauss buffer_free(b); 10904b3a91e6SGlenn Strauss return mod_auth_send_400_bad_request(srv, con); 109181b2d1f0SGlenn Strauss } 109281b2d1f0SGlenn Strauss 109381b2d1f0SGlenn Strauss m = get_http_method_name(con->request.http_method); 109481b2d1f0SGlenn Strauss force_assert(m); 109581b2d1f0SGlenn Strauss 109681b2d1f0SGlenn Strauss /* detect if attacker is attempting to reuse valid digest for one uri 109781b2d1f0SGlenn Strauss * on a different request uri. Might also happen if intermediate proxy 109881b2d1f0SGlenn Strauss * altered client request line. (Altered request would not result in 109981b2d1f0SGlenn Strauss * the same digest as that calculated by the client.) 110081b2d1f0SGlenn Strauss * Internal redirects such as with mod_rewrite will modify request uri. 110181b2d1f0SGlenn Strauss * Reauthentication is done to detect crossing auth realms, but this 110281b2d1f0SGlenn Strauss * uri validation step is bypassed. con->request.orig_uri is original 110381b2d1f0SGlenn Strauss * uri sent in client request. */ 110481b2d1f0SGlenn Strauss { 110581b2d1f0SGlenn Strauss const size_t ulen = strlen(uri); 1106c81bd354SGlenn Strauss if (!buffer_is_equal_string(con->request.orig_uri, uri, ulen)) { 110728d12134SGlenn Strauss log_error_write(srv, __FILE__, __LINE__, "sbsssB", 11087ba06c71SGlenn Strauss "digest: auth failed: uri mismatch (", con->request.orig_uri, "!=", uri, "), IP:", con->dst_addr_buf); 110981b2d1f0SGlenn Strauss buffer_free(b); 11104b3a91e6SGlenn Strauss return mod_auth_send_400_bad_request(srv, con); 111181b2d1f0SGlenn Strauss } 111281b2d1f0SGlenn Strauss } 111381b2d1f0SGlenn Strauss 111460f4cf3aSGlenn Strauss switch (backend->digest(srv, con, backend->p_d, &ai)) { 11154b3a91e6SGlenn Strauss case HANDLER_GO_ON: 11164b3a91e6SGlenn Strauss break; 11174b3a91e6SGlenn Strauss case HANDLER_WAIT_FOR_EVENT: 111881b2d1f0SGlenn Strauss buffer_free(b); 11194b3a91e6SGlenn Strauss return HANDLER_WAIT_FOR_EVENT; 11204b3a91e6SGlenn Strauss case HANDLER_FINISHED: 11214b3a91e6SGlenn Strauss buffer_free(b); 11224b3a91e6SGlenn Strauss return HANDLER_FINISHED; 11234b3a91e6SGlenn Strauss case HANDLER_ERROR: 11244b3a91e6SGlenn Strauss default: 11258bddac92SGlenn Strauss con->keep_alive = 0; /*(disable keep-alive if unknown user)*/ 11264b3a91e6SGlenn Strauss buffer_free(b); 112728895ab2SGlenn Strauss return mod_auth_send_401_unauthorized_digest(srv, con, require, 0); 112881b2d1f0SGlenn Strauss } 112981b2d1f0SGlenn Strauss 1130b9e2be50SGlenn Strauss mod_auth_digest_mutate(&ai,m,uri,nonce,cnonce,nc,qop); 113181b2d1f0SGlenn Strauss 11320e749c1cSGlenn Strauss if (!http_auth_const_time_memeq(rdigest, ai.digest, ai.dlen)) { 113381b2d1f0SGlenn Strauss /* digest not ok */ 113428d12134SGlenn Strauss log_error_write(srv, __FILE__, __LINE__, "sssB", 11357ba06c71SGlenn Strauss "digest: auth failed for ", username, ": wrong password, IP:", con->dst_addr_buf); 11368bddac92SGlenn Strauss con->keep_alive = 0; /*(disable keep-alive if bad password)*/ 113781b2d1f0SGlenn Strauss 113881b2d1f0SGlenn Strauss buffer_free(b); 113928895ab2SGlenn Strauss return mod_auth_send_401_unauthorized_digest(srv, con, require, 0); 114081b2d1f0SGlenn Strauss } 114181b2d1f0SGlenn Strauss 114281b2d1f0SGlenn Strauss /* value is our allow-rules */ 11437ba06c71SGlenn Strauss if (!http_auth_match_rules(require, username, NULL, NULL)) { 11444b3a91e6SGlenn Strauss buffer_free(b); 114528895ab2SGlenn Strauss return mod_auth_send_401_unauthorized_digest(srv, con, require, 0); 114681b2d1f0SGlenn Strauss } 114781b2d1f0SGlenn Strauss 11487f4e156eSGlenn Strauss /* check age of nonce. Note, random data is used in nonce generation 11497ba06c71SGlenn Strauss * in mod_auth_send_401_unauthorized_digest(). If that were replaced 115081b2d1f0SGlenn Strauss * with nanosecond time, then nonce secret would remain unique enough 115181b2d1f0SGlenn Strauss * for the purposes of Digest auth, and would be reproducible (and 115281b2d1f0SGlenn Strauss * verifiable) if nanoseconds were inclued with seconds as part of the 115381b2d1f0SGlenn Strauss * nonce "timestamp:secret". Since that is not done, timestamp in 115481b2d1f0SGlenn Strauss * nonce could theoretically be modified and still produce same md5sum, 115581b2d1f0SGlenn Strauss * but that is highly unlikely within a 10 min (moving) window of valid 115681b2d1f0SGlenn Strauss * time relative to current time (now) */ 115781b2d1f0SGlenn Strauss { 115881b2d1f0SGlenn Strauss time_t ts = 0; 115981b2d1f0SGlenn Strauss const unsigned char * const nonce_uns = (unsigned char *)nonce; 116081b2d1f0SGlenn Strauss for (i = 0; i < 8 && light_isxdigit(nonce_uns[i]); ++i) { 116181b2d1f0SGlenn Strauss ts = (ts << 4) + hex2int(nonce_uns[i]); 116281b2d1f0SGlenn Strauss } 116383759aedSGlenn Strauss if (nonce[i] != ':' 116481b2d1f0SGlenn Strauss || ts > srv->cur_ts || srv->cur_ts - ts > 600) { /*(10 mins)*/ 11654b3a91e6SGlenn Strauss /* nonce is stale; have client regenerate digest */ 116681b2d1f0SGlenn Strauss buffer_free(b); 1167b9e2be50SGlenn Strauss return mod_auth_send_401_unauthorized_digest(srv, con, require, ai.dalgo); 11686ad325c6SGlenn Strauss } 11696ad325c6SGlenn Strauss else if (srv->cur_ts - ts > 540) { /*(9 mins)*/ 11706ad325c6SGlenn Strauss /*(send nextnonce when expiration is approaching)*/ 11716ad325c6SGlenn Strauss mod_auth_digest_authentication_info(srv->tmp_buf, srv->cur_ts, ai.dalgo); 11726ad325c6SGlenn Strauss http_header_response_set(con, HTTP_HEADER_OTHER, CONST_STR_LEN("Authentication-Info"), CONST_BUF_LEN(srv->tmp_buf)); 11736ad325c6SGlenn Strauss } 117481b2d1f0SGlenn Strauss } 117581b2d1f0SGlenn Strauss 117660f4cf3aSGlenn Strauss http_auth_setenv(con, ai.username, ai.ulen, CONST_STR_LEN("Digest")); 117781b2d1f0SGlenn Strauss 117881b2d1f0SGlenn Strauss buffer_free(b); 117981b2d1f0SGlenn Strauss 11804b3a91e6SGlenn Strauss return HANDLER_GO_ON; 118181b2d1f0SGlenn Strauss } 118281b2d1f0SGlenn Strauss 118328895ab2SGlenn Strauss static handler_t mod_auth_send_401_unauthorized_digest(server *srv, connection *con, const struct http_auth_require_t *require, int nonce_stale) { 1184b9e2be50SGlenn Strauss mod_auth_digest_www_authenticate(srv->tmp_buf, srv->cur_ts, require, nonce_stale); 1185b9e2be50SGlenn Strauss http_header_response_set(con, HTTP_HEADER_OTHER, CONST_STR_LEN("WWW-Authenticate"), CONST_BUF_LEN(srv->tmp_buf)); 118681b2d1f0SGlenn Strauss 1187f9d58670SStefan Bühler con->http_status = 401; 1188f9d58670SStefan Bühler con->mode = DIRECT; 1189f9d58670SStefan Bühler return HANDLER_FINISHED; 11907ba06c71SGlenn Strauss } 11917ba06c71SGlenn Strauss 11927ba06c71SGlenn Strauss static handler_t mod_auth_check_extern(server *srv, connection *con, void *p_d, const struct http_auth_require_t *require, const struct http_auth_backend_t *backend) { 11937ba06c71SGlenn Strauss /* require REMOTE_USER already set */ 11943dd3cde9SGlenn Strauss buffer *vb = http_header_env_get(con, CONST_STR_LEN("REMOTE_USER")); 11957ba06c71SGlenn Strauss UNUSED(srv); 11967ba06c71SGlenn Strauss UNUSED(p_d); 11977ba06c71SGlenn Strauss UNUSED(backend); 11983dd3cde9SGlenn Strauss if (NULL != vb && http_auth_match_rules(require, vb->ptr, NULL, NULL)) { 1199f9d58670SStefan Bühler return HANDLER_GO_ON; 1200bcdc6a3bSJan Kneschke } else { 12014b3a91e6SGlenn Strauss con->http_status = 401; 12024b3a91e6SGlenn Strauss con->mode = DIRECT; 1203bcdc6a3bSJan Kneschke return HANDLER_FINISHED; 12047187271fSStefan Bühler } 1205bcdc6a3bSJan Kneschke } 1206