18abd06a7SGlenn Strauss #include "first.h" 28abd06a7SGlenn Strauss 3*04d76e7aSGlenn Strauss #include "base.h" 422e8b456SStefan Bühler #include "plugin.h" 522e8b456SStefan Bühler #include "http_auth.h" 622e8b456SStefan Bühler #include "log.h" 7bcdc6a3bSJan Kneschke 8bcdc6a3bSJan Kneschke #include <stdlib.h> 9bcdc6a3bSJan Kneschke #include <string.h> 10bcdc6a3bSJan Kneschke 11bcdc6a3bSJan Kneschke /** 127ba06c71SGlenn Strauss * auth framework 13bcdc6a3bSJan Kneschke */ 14bcdc6a3bSJan Kneschke 154b3a91e6SGlenn Strauss typedef struct { 164b3a91e6SGlenn Strauss /* auth */ 174b3a91e6SGlenn Strauss array *auth_require; 184b3a91e6SGlenn Strauss buffer *auth_backend_conf; 19f54d628cSGlenn Strauss unsigned short auth_extern_authn; 204b3a91e6SGlenn Strauss 214b3a91e6SGlenn Strauss /* generated */ 224b3a91e6SGlenn Strauss const http_auth_backend_t *auth_backend; 237ba06c71SGlenn Strauss } plugin_config; 244b3a91e6SGlenn Strauss 254b3a91e6SGlenn Strauss typedef struct { 264b3a91e6SGlenn Strauss PLUGIN_DATA; 274b3a91e6SGlenn Strauss 287ba06c71SGlenn Strauss plugin_config **config_storage; 294b3a91e6SGlenn Strauss 307ba06c71SGlenn Strauss plugin_config conf; 317ba06c71SGlenn Strauss } plugin_data; 327ba06c71SGlenn Strauss 337ba06c71SGlenn 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); 347ba06c71SGlenn 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); 357ba06c71SGlenn 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); 364b3a91e6SGlenn Strauss 37bcdc6a3bSJan Kneschke INIT_FUNC(mod_auth_init) { 387ba06c71SGlenn Strauss static const http_auth_scheme_t http_auth_scheme_basic = { "basic", mod_auth_check_basic, NULL }; 397ba06c71SGlenn Strauss static const http_auth_scheme_t http_auth_scheme_digest = { "digest", mod_auth_check_digest, NULL }; 407ba06c71SGlenn Strauss static const http_auth_scheme_t http_auth_scheme_extern = { "extern", mod_auth_check_extern, NULL }; 417ba06c71SGlenn Strauss plugin_data *p; 427ba06c71SGlenn Strauss 437ba06c71SGlenn Strauss /* register http_auth_scheme_* */ 447ba06c71SGlenn Strauss http_auth_scheme_set(&http_auth_scheme_basic); 457ba06c71SGlenn Strauss http_auth_scheme_set(&http_auth_scheme_digest); 467ba06c71SGlenn Strauss http_auth_scheme_set(&http_auth_scheme_extern); 47bcdc6a3bSJan Kneschke 48bcdc6a3bSJan Kneschke p = calloc(1, sizeof(*p)); 49bcdc6a3bSJan Kneschke 50bcdc6a3bSJan Kneschke return p; 51bcdc6a3bSJan Kneschke } 52bcdc6a3bSJan Kneschke 53bcdc6a3bSJan Kneschke FREE_FUNC(mod_auth_free) { 547ba06c71SGlenn Strauss plugin_data *p = p_d; 55bcdc6a3bSJan Kneschke 56bcdc6a3bSJan Kneschke UNUSED(srv); 57bcdc6a3bSJan Kneschke 58bcdc6a3bSJan Kneschke if (!p) return HANDLER_GO_ON; 59bcdc6a3bSJan Kneschke 60bcdc6a3bSJan Kneschke if (p->config_storage) { 61bcdc6a3bSJan Kneschke size_t i; 62bcdc6a3bSJan Kneschke for (i = 0; i < srv->config_context->used; i++) { 637ba06c71SGlenn Strauss plugin_config *s = p->config_storage[i]; 64bcdc6a3bSJan Kneschke 6533cebeb0SStefan Bühler if (NULL == s) continue; 664a6ce4aeSJan Kneschke 67bcdc6a3bSJan Kneschke array_free(s->auth_require); 68bcdc6a3bSJan Kneschke buffer_free(s->auth_backend_conf); 69bcdc6a3bSJan Kneschke 70bcdc6a3bSJan Kneschke free(s); 71bcdc6a3bSJan Kneschke } 72bcdc6a3bSJan Kneschke free(p->config_storage); 73bcdc6a3bSJan Kneschke } 74bcdc6a3bSJan Kneschke 75bcdc6a3bSJan Kneschke free(p); 76bcdc6a3bSJan Kneschke 77bcdc6a3bSJan Kneschke return HANDLER_GO_ON; 78bcdc6a3bSJan Kneschke } 79bcdc6a3bSJan Kneschke 807ba06c71SGlenn Strauss /* data type for mod_auth structured data 817ba06c71SGlenn Strauss * (parsed from auth.require array of strings) */ 827ba06c71SGlenn Strauss typedef struct { 837ba06c71SGlenn Strauss DATA_UNSET; 847ba06c71SGlenn Strauss http_auth_require_t *require; 857ba06c71SGlenn Strauss } data_auth; 867ba06c71SGlenn Strauss 877ba06c71SGlenn Strauss static void data_auth_free(data_unset *d) 887ba06c71SGlenn Strauss { 897ba06c71SGlenn Strauss data_auth * const dauth = (data_auth *)d; 907ba06c71SGlenn Strauss buffer_free(dauth->key); 917ba06c71SGlenn Strauss http_auth_require_free(dauth->require); 927ba06c71SGlenn Strauss free(dauth); 937ba06c71SGlenn Strauss } 947ba06c71SGlenn Strauss 957ba06c71SGlenn Strauss static data_auth *data_auth_init(void) 967ba06c71SGlenn Strauss { 977ba06c71SGlenn Strauss data_auth * const dauth = calloc(1, sizeof(*dauth)); 987ba06c71SGlenn Strauss force_assert(NULL != dauth); 997ba06c71SGlenn Strauss dauth->copy = NULL; /* must not be called on this data */ 1007ba06c71SGlenn Strauss dauth->free = data_auth_free; 1017ba06c71SGlenn Strauss dauth->reset = NULL; /* must not be called on this data */ 1027ba06c71SGlenn Strauss dauth->insert_dup = NULL; /* must not be called on this data */ 1037ba06c71SGlenn Strauss dauth->print = NULL; /* must not be called on this data */ 1047ba06c71SGlenn Strauss dauth->type = TYPE_OTHER; 1057ba06c71SGlenn Strauss 1067ba06c71SGlenn Strauss dauth->key = buffer_init(); 1077ba06c71SGlenn Strauss dauth->require = http_auth_require_init(); 1087ba06c71SGlenn Strauss 1097ba06c71SGlenn Strauss return dauth; 1107ba06c71SGlenn Strauss } 1117ba06c71SGlenn Strauss 1127ba06c71SGlenn Strauss static int mod_auth_require_parse (server *srv, http_auth_require_t * const require, const buffer *b) 1137ba06c71SGlenn Strauss { 1147ba06c71SGlenn Strauss /* user=name1|user=name2|group=name3|host=name4 */ 1157ba06c71SGlenn Strauss 1167ba06c71SGlenn Strauss const char *str = b->ptr; 1177ba06c71SGlenn Strauss const char *p; 1187ba06c71SGlenn Strauss 1197ba06c71SGlenn Strauss if (buffer_is_equal_string(b, CONST_STR_LEN("valid-user"))) { 1207ba06c71SGlenn Strauss require->valid_user = 1; 1217ba06c71SGlenn Strauss return 1; /* success */ 1227ba06c71SGlenn Strauss } 1237ba06c71SGlenn Strauss 1247ba06c71SGlenn Strauss do { 1257ba06c71SGlenn Strauss const char *eq; 1267ba06c71SGlenn Strauss size_t len; 1277ba06c71SGlenn Strauss p = strchr(str, '|'); 1287ba06c71SGlenn Strauss len = NULL != p ? (size_t)(p - str) : strlen(str); 1297ba06c71SGlenn Strauss eq = memchr(str, '=', len); 1307ba06c71SGlenn Strauss if (NULL == eq) { 1317ba06c71SGlenn Strauss log_error_write(srv, __FILE__, __LINE__, "sssbss", 1327ba06c71SGlenn Strauss "error parsing auth.require 'require' field: missing '='", 1337ba06c71SGlenn Strauss "(expecting \"valid-user\" or \"user=a|user=b|group=g|host=h\").", 1347ba06c71SGlenn Strauss "error value:", b, "error near:", str); 1357ba06c71SGlenn Strauss return 0; 1367ba06c71SGlenn Strauss } 1377ba06c71SGlenn Strauss if (p-1 == eq) { 1387ba06c71SGlenn Strauss log_error_write(srv, __FILE__, __LINE__, "sssbss", 1397ba06c71SGlenn Strauss "error parsing auth.require 'require' field: missing token after '='", 1407ba06c71SGlenn Strauss "(expecting \"valid-user\" or \"user=a|user=b|group=g|host=h\").", 1417ba06c71SGlenn Strauss "error value:", b, "error near:", str); 1427ba06c71SGlenn Strauss return 0; 1437ba06c71SGlenn Strauss } 1447ba06c71SGlenn Strauss 1457ba06c71SGlenn Strauss switch ((int)(eq - str)) { 1467ba06c71SGlenn Strauss case 4: 1477ba06c71SGlenn Strauss if (0 == memcmp(str, CONST_STR_LEN("user"))) { 1487ba06c71SGlenn Strauss data_string *ds = data_string_init(); 1497ba06c71SGlenn Strauss buffer_copy_string_len(ds->key,str+5,len-5); /*("user=" is 5)*/ 1507ba06c71SGlenn Strauss array_insert_unique(require->user, (data_unset *)ds); 1517ba06c71SGlenn Strauss continue; 1527ba06c71SGlenn Strauss } 1537ba06c71SGlenn Strauss else if (0 == memcmp(str, CONST_STR_LEN("host"))) { 1547ba06c71SGlenn Strauss data_string *ds = data_string_init(); 1557ba06c71SGlenn Strauss buffer_copy_string_len(ds->key,str+5,len-5); /*("host=" is 5)*/ 1567ba06c71SGlenn Strauss array_insert_unique(require->host, (data_unset *)ds); 1577ba06c71SGlenn Strauss log_error_write(srv, __FILE__, __LINE__, "ssb", 1587ba06c71SGlenn Strauss "warning parsing auth.require 'require' field: 'host' not implemented;", 1597ba06c71SGlenn Strauss "field value:", b); 1607ba06c71SGlenn Strauss continue; 1617ba06c71SGlenn Strauss } 1627ba06c71SGlenn Strauss break; /* to error */ 1637ba06c71SGlenn Strauss case 5: 1647ba06c71SGlenn Strauss if (0 == memcmp(str, CONST_STR_LEN("group"))) { 1657ba06c71SGlenn Strauss data_string *ds = data_string_init(); 1667ba06c71SGlenn Strauss buffer_copy_string_len(ds->key,str+6,len-6); /*("group=" is 6)*/ 1677ba06c71SGlenn Strauss array_insert_unique(require->group, (data_unset *)ds); 1689c91af0cSGlenn Strauss #if 0/*(supported by mod_authn_ldap, but not all other backends)*/ 1697ba06c71SGlenn Strauss log_error_write(srv, __FILE__, __LINE__, "ssb", 1707ba06c71SGlenn Strauss "warning parsing auth.require 'require' field: 'group' not implemented;", 1717ba06c71SGlenn Strauss "field value:", b); 1729c91af0cSGlenn Strauss #endif 1737ba06c71SGlenn Strauss continue; 1747ba06c71SGlenn Strauss } 1757ba06c71SGlenn Strauss break; /* to error */ 1767ba06c71SGlenn Strauss case 10: 1777ba06c71SGlenn Strauss if (0 == memcmp(str, CONST_STR_LEN("valid-user"))) { 1787ba06c71SGlenn Strauss log_error_write(srv, __FILE__, __LINE__, "sssb", 1797ba06c71SGlenn Strauss "error parsing auth.require 'require' field: valid user can not be combined with other require rules", 1807ba06c71SGlenn Strauss "(expecting \"valid-user\" or \"user=a|user=b|group=g|host=h\").", 1817ba06c71SGlenn Strauss "error value:", b); 1827ba06c71SGlenn Strauss return 0; 1837ba06c71SGlenn Strauss } 1847ba06c71SGlenn Strauss break; /* to error */ 1857ba06c71SGlenn Strauss default: 1867ba06c71SGlenn Strauss break; /* to error */ 1877ba06c71SGlenn Strauss } 1887ba06c71SGlenn Strauss 1897ba06c71SGlenn Strauss log_error_write(srv, __FILE__, __LINE__, "sssbss", 1907ba06c71SGlenn Strauss "error parsing auth.require 'require' field: invalid/unsupported token", 1917ba06c71SGlenn Strauss "(expecting \"valid-user\" or \"user=a|user=b|group=g|host=h\").", 1927ba06c71SGlenn Strauss "error value:", b, "error near:", str); 1937ba06c71SGlenn Strauss return 0; 1947ba06c71SGlenn Strauss 1957ba06c71SGlenn Strauss } while (p && *((str = p+1))); 1967ba06c71SGlenn Strauss 1977ba06c71SGlenn Strauss return 1; /* success */ 1987ba06c71SGlenn Strauss } 1997ba06c71SGlenn Strauss 2007ba06c71SGlenn Strauss SETDEFAULTS_FUNC(mod_auth_set_defaults) { 2017ba06c71SGlenn Strauss plugin_data *p = p_d; 2027ba06c71SGlenn Strauss size_t i; 2037ba06c71SGlenn Strauss 2047ba06c71SGlenn Strauss config_values_t cv[] = { 2057ba06c71SGlenn Strauss { "auth.backend", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 0 */ 2067ba06c71SGlenn Strauss { "auth.require", NULL, T_CONFIG_LOCAL, T_CONFIG_SCOPE_CONNECTION }, /* 1 */ 207f54d628cSGlenn Strauss { "auth.extern-authn", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION },/* 2 */ 2087ba06c71SGlenn Strauss { NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET } 2097ba06c71SGlenn Strauss }; 2107ba06c71SGlenn Strauss 2117ba06c71SGlenn Strauss p->config_storage = calloc(1, srv->config_context->used * sizeof(plugin_config *)); 2127ba06c71SGlenn Strauss 2137ba06c71SGlenn Strauss for (i = 0; i < srv->config_context->used; i++) { 2147ba06c71SGlenn Strauss data_config const* config = (data_config const*)srv->config_context->data[i]; 2157ba06c71SGlenn Strauss plugin_config *s; 2167ba06c71SGlenn Strauss size_t n; 2177ba06c71SGlenn Strauss data_array *da; 2187ba06c71SGlenn Strauss 2197ba06c71SGlenn Strauss s = calloc(1, sizeof(plugin_config)); 2207ba06c71SGlenn Strauss s->auth_backend_conf = buffer_init(); 2217ba06c71SGlenn Strauss 2227ba06c71SGlenn Strauss s->auth_require = array_init(); 2237ba06c71SGlenn Strauss 2247ba06c71SGlenn Strauss cv[0].destination = s->auth_backend_conf; 2257ba06c71SGlenn Strauss cv[1].destination = s->auth_require; /* T_CONFIG_LOCAL; not modified by config_insert_values_global() */ 226f54d628cSGlenn Strauss cv[2].destination = &s->auth_extern_authn; 2277ba06c71SGlenn Strauss 2287ba06c71SGlenn Strauss p->config_storage[i] = s; 2297ba06c71SGlenn Strauss 2307ba06c71SGlenn Strauss if (0 != config_insert_values_global(srv, config->value, cv, i == 0 ? T_CONFIG_SCOPE_SERVER : T_CONFIG_SCOPE_CONNECTION)) { 2317ba06c71SGlenn Strauss return HANDLER_ERROR; 2327ba06c71SGlenn Strauss } 2337ba06c71SGlenn Strauss 2347ba06c71SGlenn Strauss if (!buffer_string_is_empty(s->auth_backend_conf)) { 2357ba06c71SGlenn Strauss s->auth_backend = http_auth_backend_get(s->auth_backend_conf); 2367ba06c71SGlenn Strauss if (NULL == s->auth_backend) { 2377ba06c71SGlenn Strauss log_error_write(srv, __FILE__, __LINE__, "sb", "auth.backend not supported:", s->auth_backend_conf); 2387ba06c71SGlenn Strauss 2397ba06c71SGlenn Strauss return HANDLER_ERROR; 2407ba06c71SGlenn Strauss } 2417ba06c71SGlenn Strauss } 2427ba06c71SGlenn Strauss 2437ba06c71SGlenn Strauss /* no auth.require for this section */ 2447ba06c71SGlenn Strauss if (NULL == (da = (data_array *)array_get_element(config->value, "auth.require"))) continue; 2457ba06c71SGlenn Strauss 246bd77abe0SGlenn Strauss if (da->type != TYPE_ARRAY || !array_is_kvarray(da->value)) { 247bd77abe0SGlenn Strauss log_error_write(srv, __FILE__, __LINE__, "ss", 248bd77abe0SGlenn Strauss "unexpected value for auth.require; expected ", 249bd77abe0SGlenn Strauss "auth.require = ( \"urlpath\" => ( \"option\" => \"value\" ) )"); 250bd77abe0SGlenn Strauss return HANDLER_ERROR; 251bd77abe0SGlenn Strauss } 252bd77abe0SGlenn Strauss 2537ba06c71SGlenn Strauss 2547ba06c71SGlenn Strauss for (n = 0; n < da->value->used; n++) { 2557ba06c71SGlenn Strauss size_t m; 2567ba06c71SGlenn Strauss data_array *da_file = (data_array *)da->value->data[n]; 2577ba06c71SGlenn Strauss const buffer *method = NULL, *realm = NULL, *require = NULL; 2587ba06c71SGlenn Strauss const http_auth_scheme_t *auth_scheme; 2597ba06c71SGlenn Strauss 260bd77abe0SGlenn Strauss if (!array_is_kvstring(da_file->value)) { 2617ba06c71SGlenn Strauss log_error_write(srv, __FILE__, __LINE__, "ss", 262bd77abe0SGlenn Strauss "unexpected value for auth.require; expected ", 263bd77abe0SGlenn Strauss "auth.require = ( \"urlpath\" => ( \"option\" => \"value\" ) )"); 2647ba06c71SGlenn Strauss 2657ba06c71SGlenn Strauss return HANDLER_ERROR; 2667ba06c71SGlenn Strauss } 2677ba06c71SGlenn Strauss 2687ba06c71SGlenn Strauss for (m = 0; m < da_file->value->used; m++) { 2697ba06c71SGlenn Strauss if (da_file->value->data[m]->type == TYPE_STRING) { 2707ba06c71SGlenn Strauss data_string *ds = (data_string *)da_file->value->data[m]; 2717ba06c71SGlenn Strauss if (buffer_is_equal_string(ds->key, CONST_STR_LEN("method"))) { 2727ba06c71SGlenn Strauss method = ds->value; 2737ba06c71SGlenn Strauss } else if (buffer_is_equal_string(ds->key, CONST_STR_LEN("realm"))) { 2747ba06c71SGlenn Strauss realm = ds->value; 2757ba06c71SGlenn Strauss } else if (buffer_is_equal_string(ds->key, CONST_STR_LEN("require"))) { 2767ba06c71SGlenn Strauss require = ds->value; 2777ba06c71SGlenn Strauss } else { 2787ba06c71SGlenn Strauss log_error_write(srv, __FILE__, __LINE__, "ssbs", 2797ba06c71SGlenn Strauss "the field is unknown in:", 2807ba06c71SGlenn Strauss "auth.require = ( \"...\" => ( ..., -> \"", 2817ba06c71SGlenn Strauss da_file->value->data[m]->key, 2827ba06c71SGlenn Strauss "\" <- => \"...\" ) )"); 2837ba06c71SGlenn Strauss 2847ba06c71SGlenn Strauss return HANDLER_ERROR; 2857ba06c71SGlenn Strauss } 2867ba06c71SGlenn Strauss } else { 2877ba06c71SGlenn Strauss log_error_write(srv, __FILE__, __LINE__, "ssbs", 2887ba06c71SGlenn Strauss "a string was expected for:", 2897ba06c71SGlenn Strauss "auth.require = ( \"...\" => ( ..., -> \"", 2907ba06c71SGlenn Strauss da_file->value->data[m]->key, 2917ba06c71SGlenn Strauss "\" <- => \"...\" ) )"); 2927ba06c71SGlenn Strauss 2937ba06c71SGlenn Strauss return HANDLER_ERROR; 2947ba06c71SGlenn Strauss } 2957ba06c71SGlenn Strauss } 2967ba06c71SGlenn Strauss 2977ba06c71SGlenn Strauss if (buffer_string_is_empty(method)) { 2987ba06c71SGlenn Strauss log_error_write(srv, __FILE__, __LINE__, "ss", 2997ba06c71SGlenn Strauss "the method field is missing or blank in:", 3007ba06c71SGlenn Strauss "auth.require = ( \"...\" => ( ..., \"method\" => \"...\" ) )"); 3017ba06c71SGlenn Strauss return HANDLER_ERROR; 3027ba06c71SGlenn Strauss } else { 3037ba06c71SGlenn Strauss auth_scheme = http_auth_scheme_get(method); 3047ba06c71SGlenn Strauss if (NULL == auth_scheme) { 3057ba06c71SGlenn Strauss log_error_write(srv, __FILE__, __LINE__, "sbss", 3067ba06c71SGlenn Strauss "unknown method", method, "(e.g. \"basic\", \"digest\" or \"extern\") in", 3077ba06c71SGlenn Strauss "auth.require = ( \"...\" => ( ..., \"method\" => \"...\") )"); 3087ba06c71SGlenn Strauss return HANDLER_ERROR; 3097ba06c71SGlenn Strauss } 3107ba06c71SGlenn Strauss } 3117ba06c71SGlenn Strauss 3127ba06c71SGlenn Strauss if (buffer_is_empty(realm)) { 3137ba06c71SGlenn Strauss log_error_write(srv, __FILE__, __LINE__, "ss", 3147ba06c71SGlenn Strauss "the realm field is missing in:", 3157ba06c71SGlenn Strauss "auth.require = ( \"...\" => ( ..., \"realm\" => \"...\" ) )"); 3167ba06c71SGlenn Strauss return HANDLER_ERROR; 3177ba06c71SGlenn Strauss } 3187ba06c71SGlenn Strauss 3197ba06c71SGlenn Strauss if (buffer_string_is_empty(require)) { 3207ba06c71SGlenn Strauss log_error_write(srv, __FILE__, __LINE__, "ss", 3217ba06c71SGlenn Strauss "the require field is missing or blank in:", 3227ba06c71SGlenn Strauss "auth.require = ( \"...\" => ( ..., \"require\" => \"...\" ) )"); 3237ba06c71SGlenn Strauss return HANDLER_ERROR; 3247ba06c71SGlenn Strauss } 3257ba06c71SGlenn Strauss 326609e9a50SGlenn Strauss if (require) { /*(always true at this point)*/ 3277ba06c71SGlenn Strauss data_auth * const dauth = data_auth_init(); 3287ba06c71SGlenn Strauss buffer_copy_buffer(dauth->key, da_file->key); 3297ba06c71SGlenn Strauss dauth->require->scheme = auth_scheme; 3307ba06c71SGlenn Strauss buffer_copy_buffer(dauth->require->realm, realm); 3317ba06c71SGlenn Strauss if (!mod_auth_require_parse(srv, dauth->require, require)) { 3327ba06c71SGlenn Strauss dauth->free((data_unset *)dauth); 3337ba06c71SGlenn Strauss return HANDLER_ERROR; 3347ba06c71SGlenn Strauss } 3357ba06c71SGlenn Strauss array_insert_unique(s->auth_require, (data_unset *)dauth); 3367ba06c71SGlenn Strauss } 3377ba06c71SGlenn Strauss } 3387ba06c71SGlenn Strauss } 3397ba06c71SGlenn Strauss 3407ba06c71SGlenn Strauss return HANDLER_GO_ON; 3417ba06c71SGlenn Strauss } 3427ba06c71SGlenn Strauss 343bcdc6a3bSJan Kneschke #define PATCH(x) \ 344bcdc6a3bSJan Kneschke p->conf.x = s->x; 3457ba06c71SGlenn Strauss static int mod_auth_patch_connection(server *srv, connection *con, plugin_data *p) { 346bcdc6a3bSJan Kneschke size_t i, j; 3477ba06c71SGlenn Strauss plugin_config *s = p->config_storage[0]; 3484a6ce4aeSJan Kneschke 3494a6ce4aeSJan Kneschke PATCH(auth_backend); 3504a6ce4aeSJan Kneschke PATCH(auth_require); 351f54d628cSGlenn Strauss PATCH(auth_extern_authn); 352bcdc6a3bSJan Kneschke 353bcdc6a3bSJan Kneschke /* skip the first, the global context */ 354bcdc6a3bSJan Kneschke for (i = 1; i < srv->config_context->used; i++) { 355bcdc6a3bSJan Kneschke data_config *dc = (data_config *)srv->config_context->data[i]; 3564a6ce4aeSJan Kneschke s = p->config_storage[i]; 357bcdc6a3bSJan Kneschke 358bcdc6a3bSJan Kneschke /* condition didn't match */ 359bcdc6a3bSJan Kneschke if (!config_check_cond(srv, con, dc)) continue; 360bcdc6a3bSJan Kneschke 361bcdc6a3bSJan Kneschke /* merge config */ 362bcdc6a3bSJan Kneschke for (j = 0; j < dc->value->used; j++) { 363bcdc6a3bSJan Kneschke data_unset *du = dc->value->data[j]; 364bcdc6a3bSJan Kneschke 365bcdc6a3bSJan Kneschke if (buffer_is_equal_string(du->key, CONST_STR_LEN("auth.backend"))) { 366bcdc6a3bSJan Kneschke PATCH(auth_backend); 367bcdc6a3bSJan Kneschke } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("auth.require"))) { 368bcdc6a3bSJan Kneschke PATCH(auth_require); 369f54d628cSGlenn Strauss } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("auth.extern-authn"))) { 370f54d628cSGlenn Strauss PATCH(auth_extern_authn); 371bcdc6a3bSJan Kneschke } 372bcdc6a3bSJan Kneschke } 373bcdc6a3bSJan Kneschke } 374bcdc6a3bSJan Kneschke 375bcdc6a3bSJan Kneschke return 0; 376bcdc6a3bSJan Kneschke } 377bcdc6a3bSJan Kneschke #undef PATCH 378bcdc6a3bSJan Kneschke 3797ba06c71SGlenn Strauss static handler_t mod_auth_uri_handler(server *srv, connection *con, void *p_d) { 3807ba06c71SGlenn Strauss size_t k; 3817ba06c71SGlenn Strauss plugin_data *p = p_d; 38281b2d1f0SGlenn Strauss 3837ba06c71SGlenn Strauss mod_auth_patch_connection(srv, con, p); 38481b2d1f0SGlenn Strauss 3857ba06c71SGlenn Strauss if (p->conf.auth_require == NULL) return HANDLER_GO_ON; 38681b2d1f0SGlenn Strauss 3877ba06c71SGlenn Strauss /* search auth directives for first prefix match against URL path */ 3887ba06c71SGlenn Strauss for (k = 0; k < p->conf.auth_require->used; k++) { 3897ba06c71SGlenn Strauss const data_auth * const dauth = (data_auth *)p->conf.auth_require->data[k]; 3907ba06c71SGlenn Strauss const buffer *path = dauth->key; 3917ba06c71SGlenn Strauss 3927ba06c71SGlenn Strauss if (buffer_string_length(con->uri.path) < buffer_string_length(path)) continue; 3937ba06c71SGlenn Strauss 3947ba06c71SGlenn Strauss /* if we have a case-insensitive FS we have to lower-case the URI here too */ 3957ba06c71SGlenn Strauss 3967ba06c71SGlenn Strauss if (!con->conf.force_lowercase_filenames 3977ba06c71SGlenn Strauss ? 0 == strncmp(con->uri.path->ptr, path->ptr, buffer_string_length(path)) 3987ba06c71SGlenn Strauss : 0 == strncasecmp(con->uri.path->ptr, path->ptr, buffer_string_length(path))) { 3997ba06c71SGlenn Strauss const http_auth_scheme_t * const scheme = dauth->require->scheme; 400f54d628cSGlenn Strauss if (p->conf.auth_extern_authn) { 401f54d628cSGlenn Strauss data_string *ds = (data_string *)array_get_element(con->environment, "REMOTE_USER"); 402f54d628cSGlenn Strauss if (NULL != ds && http_auth_match_rules(dauth->require, ds->value->ptr, NULL, NULL)) { 403f54d628cSGlenn Strauss return HANDLER_GO_ON; 404f54d628cSGlenn Strauss } 405f54d628cSGlenn Strauss } 4067ba06c71SGlenn Strauss return scheme->checkfn(srv, con, scheme->p_d, dauth->require, p->conf.auth_backend); 4077ba06c71SGlenn Strauss } 4087ba06c71SGlenn Strauss } 4097ba06c71SGlenn Strauss 4107ba06c71SGlenn Strauss /* nothing to do for us */ 4117ba06c71SGlenn Strauss return HANDLER_GO_ON; 4127ba06c71SGlenn Strauss } 4137ba06c71SGlenn Strauss 4147ba06c71SGlenn Strauss int mod_auth_plugin_init(plugin *p); 4157ba06c71SGlenn Strauss int mod_auth_plugin_init(plugin *p) { 4167ba06c71SGlenn Strauss p->version = LIGHTTPD_VERSION_ID; 4177ba06c71SGlenn Strauss p->name = buffer_init_string("auth"); 4187ba06c71SGlenn Strauss p->init = mod_auth_init; 4197ba06c71SGlenn Strauss p->set_defaults = mod_auth_set_defaults; 4207ba06c71SGlenn Strauss p->handle_uri_clean = mod_auth_uri_handler; 4217ba06c71SGlenn Strauss p->cleanup = mod_auth_free; 4227ba06c71SGlenn Strauss 4237ba06c71SGlenn Strauss p->data = NULL; 4247ba06c71SGlenn Strauss 42581b2d1f0SGlenn Strauss return 0; 42681b2d1f0SGlenn Strauss } 42781b2d1f0SGlenn Strauss 42881b2d1f0SGlenn Strauss 42981b2d1f0SGlenn Strauss 43081b2d1f0SGlenn Strauss 4317ba06c71SGlenn Strauss /* 4327ba06c71SGlenn Strauss * auth schemes (basic, digest, extern) 4337ba06c71SGlenn Strauss * 4347ba06c71SGlenn Strauss * (could be in separate file from mod_auth.c as long as registration occurs) 4357ba06c71SGlenn Strauss */ 43681b2d1f0SGlenn Strauss 4377ba06c71SGlenn Strauss #include "response.h" 4387ba06c71SGlenn Strauss #include "base64.h" 4397ba06c71SGlenn Strauss #include "md5.h" 4407f4e156eSGlenn Strauss #include "rand.h" 44181b2d1f0SGlenn Strauss 4424b3a91e6SGlenn Strauss static handler_t mod_auth_send_400_bad_request(server *srv, connection *con) { 4434b3a91e6SGlenn Strauss UNUSED(srv); 4444b3a91e6SGlenn Strauss 4454b3a91e6SGlenn Strauss /* a field was missing or invalid */ 4464b3a91e6SGlenn Strauss con->http_status = 400; /* Bad Request */ 4474b3a91e6SGlenn Strauss con->mode = DIRECT; 4484b3a91e6SGlenn Strauss 4494b3a91e6SGlenn Strauss return HANDLER_FINISHED; 4504b3a91e6SGlenn Strauss } 4514b3a91e6SGlenn Strauss 4527ba06c71SGlenn Strauss static handler_t mod_auth_send_401_unauthorized_basic(server *srv, connection *con, buffer *realm) { 4534b3a91e6SGlenn Strauss con->http_status = 401; 4544b3a91e6SGlenn Strauss con->mode = DIRECT; 4554b3a91e6SGlenn Strauss 4567ba06c71SGlenn Strauss buffer_copy_string_len(srv->tmp_buf, CONST_STR_LEN("Basic realm=\"")); 4577ba06c71SGlenn Strauss buffer_append_string_buffer(srv->tmp_buf, realm); 4587ba06c71SGlenn Strauss buffer_append_string_len(srv->tmp_buf, CONST_STR_LEN("\", charset=\"UTF-8\"")); 4594b3a91e6SGlenn Strauss 4607ba06c71SGlenn Strauss response_header_insert(srv, con, CONST_STR_LEN("WWW-Authenticate"), CONST_BUF_LEN(srv->tmp_buf)); 4614b3a91e6SGlenn Strauss 4624b3a91e6SGlenn Strauss return HANDLER_FINISHED; 4634b3a91e6SGlenn Strauss } 4644b3a91e6SGlenn Strauss 4657ba06c71SGlenn 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) { 4667ba06c71SGlenn Strauss data_string *ds = (data_string *)array_get_element(con->request.headers, "Authorization"); 46781b2d1f0SGlenn Strauss buffer *username; 4687ba06c71SGlenn Strauss buffer *b; 46981b2d1f0SGlenn Strauss char *pw; 4707ba06c71SGlenn Strauss handler_t rc = HANDLER_UNSET; 47181b2d1f0SGlenn Strauss 4727ba06c71SGlenn Strauss UNUSED(p_d); 47381b2d1f0SGlenn Strauss 4747ba06c71SGlenn Strauss if (NULL == backend) { 4757ba06c71SGlenn Strauss log_error_write(srv, __FILE__, __LINE__, "sb", "auth.backend not configured for", con->uri.path); 4767ba06c71SGlenn Strauss con->http_status = 500; 4777ba06c71SGlenn Strauss con->mode = DIRECT; 4787ba06c71SGlenn Strauss return HANDLER_FINISHED; 4797ba06c71SGlenn Strauss } 4807ba06c71SGlenn Strauss 4817ba06c71SGlenn Strauss if (NULL == ds || buffer_is_empty(ds->value)) { 4827ba06c71SGlenn Strauss return mod_auth_send_401_unauthorized_basic(srv, con, require->realm); 4837ba06c71SGlenn Strauss } 4847ba06c71SGlenn Strauss 4857ba06c71SGlenn Strauss if (0 != strncasecmp(ds->value->ptr, "Basic ", sizeof("Basic ")-1)) { 4864b3a91e6SGlenn Strauss return mod_auth_send_400_bad_request(srv, con); 4874b3a91e6SGlenn Strauss } 4889bc61f16SGlenn Strauss #ifdef __COVERITY__ 4899bc61f16SGlenn Strauss if (buffer_string_length(ds->value) < sizeof("Basic ")-1) { 4909bc61f16SGlenn Strauss return mod_auth_send_400_bad_request(srv, con); 4919bc61f16SGlenn Strauss } 4929bc61f16SGlenn Strauss #endif 49381b2d1f0SGlenn Strauss 49481b2d1f0SGlenn Strauss username = buffer_init(); 49581b2d1f0SGlenn Strauss 4967ba06c71SGlenn Strauss b = ds->value; 4978047c2f4SGlenn Strauss /* coverity[overflow_sink : FALSE] */ 4987ba06c71SGlenn Strauss if (!buffer_append_base64_decode(username, b->ptr+sizeof("Basic ")-1, buffer_string_length(b)-(sizeof("Basic ")-1), BASE64_STANDARD)) { 4997ba06c71SGlenn Strauss log_error_write(srv, __FILE__, __LINE__, "sb", "decoding base64-string failed", username); 50081b2d1f0SGlenn Strauss 50181b2d1f0SGlenn Strauss buffer_free(username); 5024b3a91e6SGlenn Strauss return mod_auth_send_400_bad_request(srv, con); 50381b2d1f0SGlenn Strauss } 50481b2d1f0SGlenn Strauss 50581b2d1f0SGlenn Strauss /* r2 == user:password */ 50681b2d1f0SGlenn Strauss if (NULL == (pw = strchr(username->ptr, ':'))) { 5077ba06c71SGlenn Strauss log_error_write(srv, __FILE__, __LINE__, "sb", "missing ':' in", username); 50881b2d1f0SGlenn Strauss 50981b2d1f0SGlenn Strauss buffer_free(username); 5104b3a91e6SGlenn Strauss return mod_auth_send_400_bad_request(srv, con); 51181b2d1f0SGlenn Strauss } 51281b2d1f0SGlenn Strauss 51381b2d1f0SGlenn Strauss buffer_string_set_length(username, pw - username->ptr); 51481b2d1f0SGlenn Strauss pw++; 51581b2d1f0SGlenn Strauss 516a401c946SGlenn Strauss rc = backend->basic(srv, con, backend->p_d, require, username, pw); 5177ba06c71SGlenn Strauss switch (rc) { 5184b3a91e6SGlenn Strauss case HANDLER_GO_ON: 5197ba06c71SGlenn Strauss http_auth_setenv(con->environment, CONST_BUF_LEN(username), CONST_STR_LEN("Basic")); 5204b3a91e6SGlenn Strauss break; 5214b3a91e6SGlenn Strauss case HANDLER_WAIT_FOR_EVENT: 5224b3a91e6SGlenn Strauss case HANDLER_FINISHED: 5237ba06c71SGlenn Strauss break; 5244b3a91e6SGlenn Strauss case HANDLER_ERROR: 5254b3a91e6SGlenn Strauss default: 52628d12134SGlenn Strauss log_error_write(srv, __FILE__, __LINE__, "sbsBsB", "password doesn't match for", con->uri.path, "username:", username, ", IP:", con->dst_addr_buf); 5277ba06c71SGlenn Strauss rc = HANDLER_UNSET; 5287ba06c71SGlenn Strauss break; 52981b2d1f0SGlenn Strauss } 53081b2d1f0SGlenn Strauss 5314b3a91e6SGlenn Strauss buffer_free(username); 5327ba06c71SGlenn Strauss return (HANDLER_UNSET != rc) ? rc : mod_auth_send_401_unauthorized_basic(srv, con, require->realm); 53381b2d1f0SGlenn Strauss } 53481b2d1f0SGlenn Strauss 53581b2d1f0SGlenn Strauss #define HASHLEN 16 53681b2d1f0SGlenn Strauss #define HASHHEXLEN 32 53781b2d1f0SGlenn Strauss typedef unsigned char HASH[HASHLEN]; 53881b2d1f0SGlenn Strauss typedef char HASHHEX[HASHHEXLEN+1]; 53981b2d1f0SGlenn Strauss 54081b2d1f0SGlenn Strauss static void CvtHex(const HASH Bin, char (*Hex)[33]) { 54181b2d1f0SGlenn Strauss li_tohex(*Hex, sizeof(*Hex), (const char*) Bin, 16); 54281b2d1f0SGlenn Strauss } 54381b2d1f0SGlenn Strauss 54481b2d1f0SGlenn Strauss typedef struct { 54581b2d1f0SGlenn Strauss const char *key; 54681b2d1f0SGlenn Strauss int key_len; 54781b2d1f0SGlenn Strauss char **ptr; 54881b2d1f0SGlenn Strauss } digest_kv; 54981b2d1f0SGlenn Strauss 5507ba06c71SGlenn Strauss static handler_t mod_auth_send_401_unauthorized_digest(server *srv, connection *con, buffer *realm, int nonce_stale); 5517ba06c71SGlenn Strauss 5527ba06c71SGlenn 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) { 5537ba06c71SGlenn Strauss data_string *ds = (data_string *)array_get_element(con->request.headers, "Authorization"); 5547ba06c71SGlenn Strauss 55581b2d1f0SGlenn Strauss char a1[33]; 55681b2d1f0SGlenn Strauss char a2[33]; 55781b2d1f0SGlenn Strauss 55881b2d1f0SGlenn Strauss char *username = NULL; 55981b2d1f0SGlenn Strauss char *realm = NULL; 56081b2d1f0SGlenn Strauss char *nonce = NULL; 56181b2d1f0SGlenn Strauss char *uri = NULL; 56281b2d1f0SGlenn Strauss char *algorithm = NULL; 56381b2d1f0SGlenn Strauss char *qop = NULL; 56481b2d1f0SGlenn Strauss char *cnonce = NULL; 56581b2d1f0SGlenn Strauss char *nc = NULL; 56681b2d1f0SGlenn Strauss char *respons = NULL; 56781b2d1f0SGlenn Strauss 56881b2d1f0SGlenn Strauss char *e, *c; 56981b2d1f0SGlenn Strauss const char *m = NULL; 57081b2d1f0SGlenn Strauss int i; 57181b2d1f0SGlenn Strauss buffer *b; 57281b2d1f0SGlenn Strauss 57381b2d1f0SGlenn Strauss li_MD5_CTX Md5Ctx; 57481b2d1f0SGlenn Strauss HASH HA1; 57581b2d1f0SGlenn Strauss HASH HA2; 57681b2d1f0SGlenn Strauss HASH RespHash; 57781b2d1f0SGlenn Strauss HASHHEX HA2Hex; 57881b2d1f0SGlenn Strauss 57981b2d1f0SGlenn Strauss 58081b2d1f0SGlenn Strauss /* init pointers */ 58181b2d1f0SGlenn Strauss #define S(x) \ 58281b2d1f0SGlenn Strauss x, sizeof(x)-1, NULL 58381b2d1f0SGlenn Strauss digest_kv dkv[10] = { 58481b2d1f0SGlenn Strauss { S("username=") }, 58581b2d1f0SGlenn Strauss { S("realm=") }, 58681b2d1f0SGlenn Strauss { S("nonce=") }, 58781b2d1f0SGlenn Strauss { S("uri=") }, 58881b2d1f0SGlenn Strauss { S("algorithm=") }, 58981b2d1f0SGlenn Strauss { S("qop=") }, 59081b2d1f0SGlenn Strauss { S("cnonce=") }, 59181b2d1f0SGlenn Strauss { S("nc=") }, 59281b2d1f0SGlenn Strauss { S("response=") }, 59381b2d1f0SGlenn Strauss 59481b2d1f0SGlenn Strauss { NULL, 0, NULL } 59581b2d1f0SGlenn Strauss }; 59681b2d1f0SGlenn Strauss #undef S 59781b2d1f0SGlenn Strauss 59881b2d1f0SGlenn Strauss dkv[0].ptr = &username; 59981b2d1f0SGlenn Strauss dkv[1].ptr = &realm; 60081b2d1f0SGlenn Strauss dkv[2].ptr = &nonce; 60181b2d1f0SGlenn Strauss dkv[3].ptr = &uri; 60281b2d1f0SGlenn Strauss dkv[4].ptr = &algorithm; 60381b2d1f0SGlenn Strauss dkv[5].ptr = &qop; 60481b2d1f0SGlenn Strauss dkv[6].ptr = &cnonce; 60581b2d1f0SGlenn Strauss dkv[7].ptr = &nc; 60681b2d1f0SGlenn Strauss dkv[8].ptr = &respons; 60781b2d1f0SGlenn Strauss 6087ba06c71SGlenn Strauss UNUSED(p_d); 6097ba06c71SGlenn Strauss 6107ba06c71SGlenn Strauss if (NULL == backend) { 6117ba06c71SGlenn Strauss log_error_write(srv, __FILE__, __LINE__, "sb", "auth.backend not configured for", con->uri.path); 6127ba06c71SGlenn Strauss con->http_status = 500; 6137ba06c71SGlenn Strauss con->mode = DIRECT; 6147ba06c71SGlenn Strauss return HANDLER_FINISHED; 6157ba06c71SGlenn Strauss } 6167ba06c71SGlenn Strauss 6177ba06c71SGlenn Strauss if (NULL == ds || buffer_is_empty(ds->value)) { 6187ba06c71SGlenn Strauss return mod_auth_send_401_unauthorized_digest(srv, con, require->realm, 0); 6197ba06c71SGlenn Strauss } 6207ba06c71SGlenn Strauss 6217ba06c71SGlenn Strauss if (0 != strncasecmp(ds->value->ptr, "Digest ", sizeof("Digest ")-1)) { 6227ba06c71SGlenn Strauss return mod_auth_send_400_bad_request(srv, con); 623dadfb5fcSGlenn Strauss } else { 624dadfb5fcSGlenn Strauss size_t n = buffer_string_length(ds->value); 6259bc61f16SGlenn Strauss #ifdef __COVERITY__ 626dadfb5fcSGlenn Strauss if (n < sizeof("Digest ")-1) { 6279bc61f16SGlenn Strauss return mod_auth_send_400_bad_request(srv, con); 6289bc61f16SGlenn Strauss } 6299bc61f16SGlenn Strauss #endif 630dadfb5fcSGlenn Strauss n -= (sizeof("Digest ")-1); 6317ba06c71SGlenn Strauss b = buffer_init(); 632dadfb5fcSGlenn Strauss buffer_copy_string_len(b,ds->value->ptr+sizeof("Digest ")-1,n); 633dadfb5fcSGlenn Strauss } 63481b2d1f0SGlenn Strauss 63581b2d1f0SGlenn Strauss /* parse credentials from client */ 63681b2d1f0SGlenn Strauss for (c = b->ptr; *c; c++) { 63781b2d1f0SGlenn Strauss /* skip whitespaces */ 63881b2d1f0SGlenn Strauss while (*c == ' ' || *c == '\t') c++; 63981b2d1f0SGlenn Strauss if (!*c) break; 64081b2d1f0SGlenn Strauss 64181b2d1f0SGlenn Strauss for (i = 0; dkv[i].key; i++) { 64281b2d1f0SGlenn Strauss if ((0 == strncmp(c, dkv[i].key, dkv[i].key_len))) { 64381b2d1f0SGlenn Strauss if ((c[dkv[i].key_len] == '"') && 64481b2d1f0SGlenn Strauss (NULL != (e = strchr(c + dkv[i].key_len + 1, '"')))) { 64581b2d1f0SGlenn Strauss /* value with "..." */ 64681b2d1f0SGlenn Strauss *(dkv[i].ptr) = c + dkv[i].key_len + 1; 64781b2d1f0SGlenn Strauss c = e; 64881b2d1f0SGlenn Strauss 64981b2d1f0SGlenn Strauss *e = '\0'; 65081b2d1f0SGlenn Strauss } else if (NULL != (e = strchr(c + dkv[i].key_len, ','))) { 65181b2d1f0SGlenn Strauss /* value without "...", terminated by ',' */ 65281b2d1f0SGlenn Strauss *(dkv[i].ptr) = c + dkv[i].key_len; 65381b2d1f0SGlenn Strauss c = e; 65481b2d1f0SGlenn Strauss 65581b2d1f0SGlenn Strauss *e = '\0'; 65681b2d1f0SGlenn Strauss } else { 65781b2d1f0SGlenn Strauss /* value without "...", terminated by EOL */ 65881b2d1f0SGlenn Strauss *(dkv[i].ptr) = c + dkv[i].key_len; 65981b2d1f0SGlenn Strauss c += strlen(c) - 1; 66081b2d1f0SGlenn Strauss } 66181b2d1f0SGlenn Strauss break; 66281b2d1f0SGlenn Strauss } 66381b2d1f0SGlenn Strauss } 66481b2d1f0SGlenn Strauss } 66581b2d1f0SGlenn Strauss 66681b2d1f0SGlenn Strauss /* check if everything is transmitted */ 66781b2d1f0SGlenn Strauss if (!username || 66881b2d1f0SGlenn Strauss !realm || 66981b2d1f0SGlenn Strauss !nonce || 67081b2d1f0SGlenn Strauss !uri || 67181b2d1f0SGlenn Strauss (qop && (!nc || !cnonce)) || 67281b2d1f0SGlenn Strauss !respons ) { 67381b2d1f0SGlenn Strauss /* missing field */ 67481b2d1f0SGlenn Strauss 67581b2d1f0SGlenn Strauss log_error_write(srv, __FILE__, __LINE__, "s", 67681b2d1f0SGlenn Strauss "digest: missing field"); 67781b2d1f0SGlenn Strauss 67881b2d1f0SGlenn Strauss buffer_free(b); 6794b3a91e6SGlenn Strauss return mod_auth_send_400_bad_request(srv, con); 68081b2d1f0SGlenn Strauss } 68181b2d1f0SGlenn Strauss 6825a6e4df8SGlenn Strauss if (!buffer_is_equal_string(require->realm, realm, strlen(realm))) { 6835a6e4df8SGlenn Strauss log_error_write(srv, __FILE__, __LINE__, "s", 6845a6e4df8SGlenn Strauss "digest: realm mismatch"); 6855a6e4df8SGlenn Strauss buffer_free(b); 6865a6e4df8SGlenn Strauss return mod_auth_send_401_unauthorized_digest(srv, con, require->realm, 0); 6875a6e4df8SGlenn Strauss } 6885a6e4df8SGlenn Strauss 68981b2d1f0SGlenn Strauss /** 69081b2d1f0SGlenn Strauss * protect the md5-sess against missing cnonce and nonce 69181b2d1f0SGlenn Strauss */ 69281b2d1f0SGlenn Strauss if (algorithm && 69381b2d1f0SGlenn Strauss 0 == strcasecmp(algorithm, "md5-sess") && 69481b2d1f0SGlenn Strauss (!nonce || !cnonce)) { 69581b2d1f0SGlenn Strauss log_error_write(srv, __FILE__, __LINE__, "s", 69681b2d1f0SGlenn Strauss "digest: (md5-sess: missing field"); 69781b2d1f0SGlenn Strauss 69881b2d1f0SGlenn Strauss buffer_free(b); 6994b3a91e6SGlenn Strauss return mod_auth_send_400_bad_request(srv, con); 70081b2d1f0SGlenn Strauss } 70181b2d1f0SGlenn Strauss 70281b2d1f0SGlenn Strauss if (qop && strcasecmp(qop, "auth-int") == 0) { 70381b2d1f0SGlenn Strauss log_error_write(srv, __FILE__, __LINE__, "s", 70481b2d1f0SGlenn Strauss "digest: qop=auth-int not supported"); 70581b2d1f0SGlenn Strauss 70681b2d1f0SGlenn Strauss buffer_free(b); 7074b3a91e6SGlenn Strauss return mod_auth_send_400_bad_request(srv, con); 70881b2d1f0SGlenn Strauss } 70981b2d1f0SGlenn Strauss 71081b2d1f0SGlenn Strauss m = get_http_method_name(con->request.http_method); 71181b2d1f0SGlenn Strauss force_assert(m); 71281b2d1f0SGlenn Strauss 71381b2d1f0SGlenn Strauss /* detect if attacker is attempting to reuse valid digest for one uri 71481b2d1f0SGlenn Strauss * on a different request uri. Might also happen if intermediate proxy 71581b2d1f0SGlenn Strauss * altered client request line. (Altered request would not result in 71681b2d1f0SGlenn Strauss * the same digest as that calculated by the client.) 71781b2d1f0SGlenn Strauss * Internal redirects such as with mod_rewrite will modify request uri. 71881b2d1f0SGlenn Strauss * Reauthentication is done to detect crossing auth realms, but this 71981b2d1f0SGlenn Strauss * uri validation step is bypassed. con->request.orig_uri is original 72081b2d1f0SGlenn Strauss * uri sent in client request. */ 72181b2d1f0SGlenn Strauss { 72281b2d1f0SGlenn Strauss const size_t ulen = strlen(uri); 72381b2d1f0SGlenn Strauss const size_t rlen = buffer_string_length(con->request.orig_uri); 72481b2d1f0SGlenn Strauss if (!buffer_is_equal_string(con->request.orig_uri, uri, ulen) 72581b2d1f0SGlenn Strauss && !(rlen < ulen && 0 == memcmp(con->request.orig_uri->ptr, uri, rlen) && uri[rlen] == '?')) { 72628d12134SGlenn Strauss log_error_write(srv, __FILE__, __LINE__, "sbsssB", 7277ba06c71SGlenn Strauss "digest: auth failed: uri mismatch (", con->request.orig_uri, "!=", uri, "), IP:", con->dst_addr_buf); 72881b2d1f0SGlenn Strauss buffer_free(b); 7294b3a91e6SGlenn Strauss return mod_auth_send_400_bad_request(srv, con); 73081b2d1f0SGlenn Strauss } 73181b2d1f0SGlenn Strauss } 73281b2d1f0SGlenn Strauss 73381b2d1f0SGlenn Strauss /* password-string == HA1 */ 7347ba06c71SGlenn Strauss switch (backend->digest(srv, con, backend->p_d, username, realm, HA1)) { 7354b3a91e6SGlenn Strauss case HANDLER_GO_ON: 7364b3a91e6SGlenn Strauss break; 7374b3a91e6SGlenn Strauss case HANDLER_WAIT_FOR_EVENT: 73881b2d1f0SGlenn Strauss buffer_free(b); 7394b3a91e6SGlenn Strauss return HANDLER_WAIT_FOR_EVENT; 7404b3a91e6SGlenn Strauss case HANDLER_FINISHED: 7414b3a91e6SGlenn Strauss buffer_free(b); 7424b3a91e6SGlenn Strauss return HANDLER_FINISHED; 7434b3a91e6SGlenn Strauss case HANDLER_ERROR: 7444b3a91e6SGlenn Strauss default: 7454b3a91e6SGlenn Strauss buffer_free(b); 7467ba06c71SGlenn Strauss return mod_auth_send_401_unauthorized_digest(srv, con, require->realm, 0); 74781b2d1f0SGlenn Strauss } 74881b2d1f0SGlenn Strauss 74981b2d1f0SGlenn Strauss if (algorithm && 75081b2d1f0SGlenn Strauss strcasecmp(algorithm, "md5-sess") == 0) { 75181b2d1f0SGlenn Strauss li_MD5_Init(&Md5Ctx); 75281b2d1f0SGlenn Strauss /* Errata ID 1649: http://www.rfc-editor.org/errata_search.php?rfc=2617 */ 75381b2d1f0SGlenn Strauss CvtHex(HA1, &a1); 75481b2d1f0SGlenn Strauss li_MD5_Update(&Md5Ctx, (unsigned char *)a1, HASHHEXLEN); 75581b2d1f0SGlenn Strauss li_MD5_Update(&Md5Ctx, CONST_STR_LEN(":")); 75681b2d1f0SGlenn Strauss li_MD5_Update(&Md5Ctx, (unsigned char *)nonce, strlen(nonce)); 75781b2d1f0SGlenn Strauss li_MD5_Update(&Md5Ctx, CONST_STR_LEN(":")); 75881b2d1f0SGlenn Strauss li_MD5_Update(&Md5Ctx, (unsigned char *)cnonce, strlen(cnonce)); 75981b2d1f0SGlenn Strauss li_MD5_Final(HA1, &Md5Ctx); 76081b2d1f0SGlenn Strauss } 76181b2d1f0SGlenn Strauss 76281b2d1f0SGlenn Strauss CvtHex(HA1, &a1); 76381b2d1f0SGlenn Strauss 76481b2d1f0SGlenn Strauss /* calculate H(A2) */ 76581b2d1f0SGlenn Strauss li_MD5_Init(&Md5Ctx); 76681b2d1f0SGlenn Strauss li_MD5_Update(&Md5Ctx, (unsigned char *)m, strlen(m)); 76781b2d1f0SGlenn Strauss li_MD5_Update(&Md5Ctx, CONST_STR_LEN(":")); 76881b2d1f0SGlenn Strauss li_MD5_Update(&Md5Ctx, (unsigned char *)uri, strlen(uri)); 76981b2d1f0SGlenn Strauss /* qop=auth-int not supported, already checked above */ 77081b2d1f0SGlenn Strauss /* 77181b2d1f0SGlenn Strauss if (qop && strcasecmp(qop, "auth-int") == 0) { 77281b2d1f0SGlenn Strauss li_MD5_Update(&Md5Ctx, CONST_STR_LEN(":")); 77381b2d1f0SGlenn Strauss li_MD5_Update(&Md5Ctx, (unsigned char *) [body checksum], HASHHEXLEN); 77481b2d1f0SGlenn Strauss } 77581b2d1f0SGlenn Strauss */ 77681b2d1f0SGlenn Strauss li_MD5_Final(HA2, &Md5Ctx); 77781b2d1f0SGlenn Strauss CvtHex(HA2, &HA2Hex); 77881b2d1f0SGlenn Strauss 77981b2d1f0SGlenn Strauss /* calculate response */ 78081b2d1f0SGlenn Strauss li_MD5_Init(&Md5Ctx); 78181b2d1f0SGlenn Strauss li_MD5_Update(&Md5Ctx, (unsigned char *)a1, HASHHEXLEN); 78281b2d1f0SGlenn Strauss li_MD5_Update(&Md5Ctx, CONST_STR_LEN(":")); 78381b2d1f0SGlenn Strauss li_MD5_Update(&Md5Ctx, (unsigned char *)nonce, strlen(nonce)); 78481b2d1f0SGlenn Strauss li_MD5_Update(&Md5Ctx, CONST_STR_LEN(":")); 78581b2d1f0SGlenn Strauss if (qop && *qop) { 78681b2d1f0SGlenn Strauss li_MD5_Update(&Md5Ctx, (unsigned char *)nc, strlen(nc)); 78781b2d1f0SGlenn Strauss li_MD5_Update(&Md5Ctx, CONST_STR_LEN(":")); 78881b2d1f0SGlenn Strauss li_MD5_Update(&Md5Ctx, (unsigned char *)cnonce, strlen(cnonce)); 78981b2d1f0SGlenn Strauss li_MD5_Update(&Md5Ctx, CONST_STR_LEN(":")); 79081b2d1f0SGlenn Strauss li_MD5_Update(&Md5Ctx, (unsigned char *)qop, strlen(qop)); 79181b2d1f0SGlenn Strauss li_MD5_Update(&Md5Ctx, CONST_STR_LEN(":")); 79281b2d1f0SGlenn Strauss }; 79381b2d1f0SGlenn Strauss li_MD5_Update(&Md5Ctx, (unsigned char *)HA2Hex, HASHHEXLEN); 79481b2d1f0SGlenn Strauss li_MD5_Final(RespHash, &Md5Ctx); 79581b2d1f0SGlenn Strauss CvtHex(RespHash, &a2); 79681b2d1f0SGlenn Strauss 79781b2d1f0SGlenn Strauss if (0 != strcmp(a2, respons)) { 79881b2d1f0SGlenn Strauss /* digest not ok */ 79928d12134SGlenn Strauss log_error_write(srv, __FILE__, __LINE__, "sssB", 8007ba06c71SGlenn Strauss "digest: auth failed for ", username, ": wrong password, IP:", con->dst_addr_buf); 80181b2d1f0SGlenn Strauss 80281b2d1f0SGlenn Strauss buffer_free(b); 8037ba06c71SGlenn Strauss return mod_auth_send_401_unauthorized_digest(srv, con, require->realm, 0); 80481b2d1f0SGlenn Strauss } 80581b2d1f0SGlenn Strauss 80681b2d1f0SGlenn Strauss /* value is our allow-rules */ 8077ba06c71SGlenn Strauss if (!http_auth_match_rules(require, username, NULL, NULL)) { 8084b3a91e6SGlenn Strauss buffer_free(b); 8097ba06c71SGlenn Strauss return mod_auth_send_401_unauthorized_digest(srv, con, require->realm, 0); 81081b2d1f0SGlenn Strauss } 81181b2d1f0SGlenn Strauss 8127f4e156eSGlenn Strauss /* check age of nonce. Note, random data is used in nonce generation 8137ba06c71SGlenn Strauss * in mod_auth_send_401_unauthorized_digest(). If that were replaced 81481b2d1f0SGlenn Strauss * with nanosecond time, then nonce secret would remain unique enough 81581b2d1f0SGlenn Strauss * for the purposes of Digest auth, and would be reproducible (and 81681b2d1f0SGlenn Strauss * verifiable) if nanoseconds were inclued with seconds as part of the 81781b2d1f0SGlenn Strauss * nonce "timestamp:secret". Since that is not done, timestamp in 81881b2d1f0SGlenn Strauss * nonce could theoretically be modified and still produce same md5sum, 81981b2d1f0SGlenn Strauss * but that is highly unlikely within a 10 min (moving) window of valid 82081b2d1f0SGlenn Strauss * time relative to current time (now) */ 82181b2d1f0SGlenn Strauss { 82281b2d1f0SGlenn Strauss time_t ts = 0; 82381b2d1f0SGlenn Strauss const unsigned char * const nonce_uns = (unsigned char *)nonce; 82481b2d1f0SGlenn Strauss for (i = 0; i < 8 && light_isxdigit(nonce_uns[i]); ++i) { 82581b2d1f0SGlenn Strauss ts = (ts << 4) + hex2int(nonce_uns[i]); 82681b2d1f0SGlenn Strauss } 82783759aedSGlenn Strauss if (nonce[i] != ':' 82881b2d1f0SGlenn Strauss || ts > srv->cur_ts || srv->cur_ts - ts > 600) { /*(10 mins)*/ 8294b3a91e6SGlenn Strauss /* nonce is stale; have client regenerate digest */ 83081b2d1f0SGlenn Strauss buffer_free(b); 8317ba06c71SGlenn Strauss return mod_auth_send_401_unauthorized_digest(srv, con, require->realm, 1); 83281b2d1f0SGlenn Strauss } /*(future: might send nextnonce when expiration is imminent)*/ 83381b2d1f0SGlenn Strauss } 83481b2d1f0SGlenn Strauss 8357ba06c71SGlenn Strauss http_auth_setenv(con->environment, username, strlen(username), CONST_STR_LEN("Digest")); 83681b2d1f0SGlenn Strauss 83781b2d1f0SGlenn Strauss buffer_free(b); 83881b2d1f0SGlenn Strauss 8394b3a91e6SGlenn Strauss return HANDLER_GO_ON; 84081b2d1f0SGlenn Strauss } 84181b2d1f0SGlenn Strauss 8427ba06c71SGlenn Strauss static handler_t mod_auth_send_401_unauthorized_digest(server *srv, connection *con, buffer *realm, int nonce_stale) { 84381b2d1f0SGlenn Strauss li_MD5_CTX Md5Ctx; 8447ba06c71SGlenn Strauss HASH h; 8457ba06c71SGlenn Strauss char hh[33]; 84681b2d1f0SGlenn Strauss 8477ba06c71SGlenn Strauss force_assert(33 >= LI_ITOSTRING_LENGTH); /*(buffer used for both li_itostrn() and CvtHex())*/ 8487ba06c71SGlenn Strauss 8497ba06c71SGlenn Strauss /* generate nonce */ 8507ba06c71SGlenn Strauss 85181b2d1f0SGlenn Strauss /* generate shared-secret */ 85281b2d1f0SGlenn Strauss li_MD5_Init(&Md5Ctx); 85381b2d1f0SGlenn Strauss 85481b2d1f0SGlenn Strauss li_itostrn(hh, sizeof(hh), srv->cur_ts); 85581b2d1f0SGlenn Strauss li_MD5_Update(&Md5Ctx, (unsigned char *)hh, strlen(hh)); 8566e171bd4SGlenn Strauss li_itostrn(hh, sizeof(hh), li_rand_pseudo()); 85781b2d1f0SGlenn Strauss li_MD5_Update(&Md5Ctx, (unsigned char *)hh, strlen(hh)); 85881b2d1f0SGlenn Strauss 85981b2d1f0SGlenn Strauss li_MD5_Final(h, &Md5Ctx); 86081b2d1f0SGlenn Strauss 8617ba06c71SGlenn Strauss CvtHex(h, &hh); 86281b2d1f0SGlenn Strauss 8637ba06c71SGlenn Strauss /* generate WWW-Authenticate */ 86481b2d1f0SGlenn Strauss 865f9d58670SStefan Bühler con->http_status = 401; 866f9d58670SStefan Bühler con->mode = DIRECT; 8677ba06c71SGlenn Strauss 8687ba06c71SGlenn Strauss buffer_copy_string_len(srv->tmp_buf, CONST_STR_LEN("Digest realm=\"")); 8697ba06c71SGlenn Strauss buffer_append_string_buffer(srv->tmp_buf, realm); 8707ba06c71SGlenn Strauss buffer_append_string_len(srv->tmp_buf, CONST_STR_LEN("\", charset=\"UTF-8\", nonce=\"")); 8717ba06c71SGlenn Strauss buffer_append_uint_hex(srv->tmp_buf, (uintmax_t)srv->cur_ts); 8727ba06c71SGlenn Strauss buffer_append_string_len(srv->tmp_buf, CONST_STR_LEN(":")); 8737ba06c71SGlenn Strauss buffer_append_string(srv->tmp_buf, hh); 8747ba06c71SGlenn Strauss buffer_append_string_len(srv->tmp_buf, CONST_STR_LEN("\", qop=\"auth\"")); 8757ba06c71SGlenn Strauss if (nonce_stale) { 8767ba06c71SGlenn Strauss buffer_append_string_len(srv->tmp_buf, CONST_STR_LEN(", stale=true")); 8777ba06c71SGlenn Strauss } 8787ba06c71SGlenn Strauss 8797ba06c71SGlenn Strauss response_header_insert(srv, con, CONST_STR_LEN("WWW-Authenticate"), CONST_BUF_LEN(srv->tmp_buf)); 8807ba06c71SGlenn Strauss 881f9d58670SStefan Bühler return HANDLER_FINISHED; 8827ba06c71SGlenn Strauss } 8837ba06c71SGlenn Strauss 8847ba06c71SGlenn 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) { 8857ba06c71SGlenn Strauss /* require REMOTE_USER already set */ 8867ba06c71SGlenn Strauss data_string *ds = (data_string *)array_get_element(con->environment, "REMOTE_USER"); 8877ba06c71SGlenn Strauss UNUSED(srv); 8887ba06c71SGlenn Strauss UNUSED(p_d); 8897ba06c71SGlenn Strauss UNUSED(backend); 8907ba06c71SGlenn Strauss if (NULL != ds && http_auth_match_rules(require, ds->value->ptr, NULL, NULL)) { 891f9d58670SStefan Bühler return HANDLER_GO_ON; 892bcdc6a3bSJan Kneschke } else { 8934b3a91e6SGlenn Strauss con->http_status = 401; 8944b3a91e6SGlenn Strauss con->mode = DIRECT; 895bcdc6a3bSJan Kneschke return HANDLER_FINISHED; 8967187271fSStefan Bühler } 897bcdc6a3bSJan Kneschke } 898