18abd06a7SGlenn Strauss #include "first.h" 28abd06a7SGlenn Strauss 322e8b456SStefan Bühler #include "plugin.h" 422e8b456SStefan Bühler #include "http_auth.h" 522e8b456SStefan Bühler #include "log.h" 622e8b456SStefan Bühler #include "response.h" 722e8b456SStefan Bühler 881b2d1f0SGlenn Strauss #include "inet_ntop_cache.h" 981b2d1f0SGlenn Strauss #include "base64.h" 1081b2d1f0SGlenn Strauss #include "md5.h" 1181b2d1f0SGlenn Strauss 12bcdc6a3bSJan Kneschke #include <sys/types.h> 13bcdc6a3bSJan Kneschke #include <sys/stat.h> 14bcdc6a3bSJan Kneschke 15bcdc6a3bSJan Kneschke #include <stdlib.h> 16bcdc6a3bSJan Kneschke #include <string.h> 17bcdc6a3bSJan Kneschke #include <errno.h> 18bcdc6a3bSJan Kneschke #include <fcntl.h> 19bcdc6a3bSJan Kneschke #include <unistd.h> 20bcdc6a3bSJan Kneschke 21bcdc6a3bSJan Kneschke /** 22bcdc6a3bSJan Kneschke * the basic and digest auth framework 23bcdc6a3bSJan Kneschke */ 24bcdc6a3bSJan Kneschke 25*4b3a91e6SGlenn Strauss typedef struct { 26*4b3a91e6SGlenn Strauss /* auth */ 27*4b3a91e6SGlenn Strauss array *auth_require; 28*4b3a91e6SGlenn Strauss 29*4b3a91e6SGlenn Strauss buffer *auth_plain_groupfile; 30*4b3a91e6SGlenn Strauss buffer *auth_plain_userfile; 31*4b3a91e6SGlenn Strauss 32*4b3a91e6SGlenn Strauss buffer *auth_htdigest_userfile; 33*4b3a91e6SGlenn Strauss buffer *auth_htpasswd_userfile; 34*4b3a91e6SGlenn Strauss 35*4b3a91e6SGlenn Strauss buffer *auth_backend_conf; 36*4b3a91e6SGlenn Strauss 37*4b3a91e6SGlenn Strauss unsigned short auth_debug; 38*4b3a91e6SGlenn Strauss 39*4b3a91e6SGlenn Strauss /* generated */ 40*4b3a91e6SGlenn Strauss const http_auth_backend_t *auth_backend; 41*4b3a91e6SGlenn Strauss } mod_auth_plugin_config; 42*4b3a91e6SGlenn Strauss 43*4b3a91e6SGlenn Strauss typedef struct { 44*4b3a91e6SGlenn Strauss PLUGIN_DATA; 45*4b3a91e6SGlenn Strauss buffer *tmp_buf; 46*4b3a91e6SGlenn Strauss 47*4b3a91e6SGlenn Strauss mod_auth_plugin_config **config_storage; 48*4b3a91e6SGlenn Strauss 49*4b3a91e6SGlenn Strauss mod_auth_plugin_config conf; 50*4b3a91e6SGlenn Strauss } mod_auth_plugin_data; 51*4b3a91e6SGlenn Strauss 52bcdc6a3bSJan Kneschke INIT_FUNC(mod_auth_init) { 53bcdc6a3bSJan Kneschke mod_auth_plugin_data *p; 54bcdc6a3bSJan Kneschke 55bcdc6a3bSJan Kneschke p = calloc(1, sizeof(*p)); 56bcdc6a3bSJan Kneschke 57bcdc6a3bSJan Kneschke p->tmp_buf = buffer_init(); 58bcdc6a3bSJan Kneschke 59bcdc6a3bSJan Kneschke return p; 60bcdc6a3bSJan Kneschke } 61bcdc6a3bSJan Kneschke 62bcdc6a3bSJan Kneschke FREE_FUNC(mod_auth_free) { 63bcdc6a3bSJan Kneschke mod_auth_plugin_data *p = p_d; 64bcdc6a3bSJan Kneschke 65bcdc6a3bSJan Kneschke UNUSED(srv); 66bcdc6a3bSJan Kneschke 67bcdc6a3bSJan Kneschke if (!p) return HANDLER_GO_ON; 68bcdc6a3bSJan Kneschke 69bcdc6a3bSJan Kneschke buffer_free(p->tmp_buf); 70bcdc6a3bSJan Kneschke 71bcdc6a3bSJan Kneschke if (p->config_storage) { 72bcdc6a3bSJan Kneschke size_t i; 73bcdc6a3bSJan Kneschke for (i = 0; i < srv->config_context->used; i++) { 74bcdc6a3bSJan Kneschke mod_auth_plugin_config *s = p->config_storage[i]; 75bcdc6a3bSJan Kneschke 7633cebeb0SStefan Bühler if (NULL == s) continue; 774a6ce4aeSJan Kneschke 78bcdc6a3bSJan Kneschke array_free(s->auth_require); 79bcdc6a3bSJan Kneschke buffer_free(s->auth_backend_conf); 80bcdc6a3bSJan Kneschke 81bcdc6a3bSJan Kneschke free(s); 82bcdc6a3bSJan Kneschke } 83bcdc6a3bSJan Kneschke free(p->config_storage); 84bcdc6a3bSJan Kneschke } 85bcdc6a3bSJan Kneschke 86bcdc6a3bSJan Kneschke free(p); 87bcdc6a3bSJan Kneschke 88bcdc6a3bSJan Kneschke return HANDLER_GO_ON; 89bcdc6a3bSJan Kneschke } 90bcdc6a3bSJan Kneschke 91bcdc6a3bSJan Kneschke #define PATCH(x) \ 92bcdc6a3bSJan Kneschke p->conf.x = s->x; 934a6ce4aeSJan Kneschke static int mod_auth_patch_connection(server *srv, connection *con, mod_auth_plugin_data *p) { 94bcdc6a3bSJan Kneschke size_t i, j; 954a6ce4aeSJan Kneschke mod_auth_plugin_config *s = p->config_storage[0]; 964a6ce4aeSJan Kneschke 974a6ce4aeSJan Kneschke PATCH(auth_backend); 984a6ce4aeSJan Kneschke PATCH(auth_require); 994a6ce4aeSJan Kneschke PATCH(auth_debug); 100bcdc6a3bSJan Kneschke 101bcdc6a3bSJan Kneschke /* skip the first, the global context */ 102bcdc6a3bSJan Kneschke for (i = 1; i < srv->config_context->used; i++) { 103bcdc6a3bSJan Kneschke data_config *dc = (data_config *)srv->config_context->data[i]; 1044a6ce4aeSJan Kneschke s = p->config_storage[i]; 105bcdc6a3bSJan Kneschke 106bcdc6a3bSJan Kneschke /* condition didn't match */ 107bcdc6a3bSJan Kneschke if (!config_check_cond(srv, con, dc)) continue; 108bcdc6a3bSJan Kneschke 109bcdc6a3bSJan Kneschke /* merge config */ 110bcdc6a3bSJan Kneschke for (j = 0; j < dc->value->used; j++) { 111bcdc6a3bSJan Kneschke data_unset *du = dc->value->data[j]; 112bcdc6a3bSJan Kneschke 113bcdc6a3bSJan Kneschke if (buffer_is_equal_string(du->key, CONST_STR_LEN("auth.backend"))) { 114bcdc6a3bSJan Kneschke PATCH(auth_backend); 115bcdc6a3bSJan Kneschke } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("auth.require"))) { 116bcdc6a3bSJan Kneschke PATCH(auth_require); 117bcdc6a3bSJan Kneschke } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("auth.debug"))) { 118bcdc6a3bSJan Kneschke PATCH(auth_debug); 119bcdc6a3bSJan Kneschke } 120bcdc6a3bSJan Kneschke } 121bcdc6a3bSJan Kneschke } 122bcdc6a3bSJan Kneschke 123bcdc6a3bSJan Kneschke return 0; 124bcdc6a3bSJan Kneschke } 125bcdc6a3bSJan Kneschke #undef PATCH 126bcdc6a3bSJan Kneschke 12781b2d1f0SGlenn Strauss static int mod_auth_match_rules(server *srv, array *req, const char *username, const char *group, const char *host) { 12881b2d1f0SGlenn Strauss const char *r = NULL, *rules = NULL; 12981b2d1f0SGlenn Strauss int username_len; 13081b2d1f0SGlenn Strauss data_string *require; 13181b2d1f0SGlenn Strauss 13281b2d1f0SGlenn Strauss UNUSED(group); 13381b2d1f0SGlenn Strauss UNUSED(host); 13481b2d1f0SGlenn Strauss 13581b2d1f0SGlenn Strauss require = (data_string *)array_get_element(req, "require"); 13681b2d1f0SGlenn Strauss if (!require) return -1; /*(should not happen; config is validated at startup)*/ 13781b2d1f0SGlenn Strauss 13881b2d1f0SGlenn Strauss /* if we get here, the user we got a authed user */ 13981b2d1f0SGlenn Strauss if (0 == strcmp(require->value->ptr, "valid-user")) { 14081b2d1f0SGlenn Strauss return 0; 14181b2d1f0SGlenn Strauss } 14281b2d1f0SGlenn Strauss 14381b2d1f0SGlenn Strauss /* user=name1|group=name3|host=name4 */ 14481b2d1f0SGlenn Strauss 14581b2d1f0SGlenn Strauss /* seperate the string by | */ 14681b2d1f0SGlenn Strauss #if 0 14781b2d1f0SGlenn Strauss log_error_write(srv, __FILE__, __LINE__, "sb", "rules", require->value); 14881b2d1f0SGlenn Strauss #endif 14981b2d1f0SGlenn Strauss 15081b2d1f0SGlenn Strauss username_len = username ? strlen(username) : 0; 15181b2d1f0SGlenn Strauss 15281b2d1f0SGlenn Strauss r = rules = require->value->ptr; 15381b2d1f0SGlenn Strauss 15481b2d1f0SGlenn Strauss while (1) { 15581b2d1f0SGlenn Strauss const char *eq; 15681b2d1f0SGlenn Strauss const char *k, *v, *e; 15781b2d1f0SGlenn Strauss int k_len, v_len, r_len; 15881b2d1f0SGlenn Strauss 15981b2d1f0SGlenn Strauss e = strchr(r, '|'); 16081b2d1f0SGlenn Strauss 16181b2d1f0SGlenn Strauss if (e) { 16281b2d1f0SGlenn Strauss r_len = e - r; 16381b2d1f0SGlenn Strauss } else { 16481b2d1f0SGlenn Strauss r_len = strlen(rules) - (r - rules); 16581b2d1f0SGlenn Strauss } 16681b2d1f0SGlenn Strauss 16781b2d1f0SGlenn Strauss /* from r to r + r_len is a rule */ 16881b2d1f0SGlenn Strauss 16981b2d1f0SGlenn Strauss if (0 == strncmp(r, "valid-user", r_len)) { 17081b2d1f0SGlenn Strauss log_error_write(srv, __FILE__, __LINE__, "sb", 17181b2d1f0SGlenn Strauss "parsing the 'require' section in 'auth.require' failed: valid-user cannot be combined with other require rules", 17281b2d1f0SGlenn Strauss require->value); 17381b2d1f0SGlenn Strauss return -1; 17481b2d1f0SGlenn Strauss } 17581b2d1f0SGlenn Strauss 17681b2d1f0SGlenn Strauss /* search for = in the rules */ 17781b2d1f0SGlenn Strauss if (NULL == (eq = strchr(r, '='))) { 17881b2d1f0SGlenn Strauss log_error_write(srv, __FILE__, __LINE__, "sb", 17981b2d1f0SGlenn Strauss "parsing the 'require' section in 'auth.require' failed: a = is missing", 18081b2d1f0SGlenn Strauss require->value); 18181b2d1f0SGlenn Strauss return -1; 18281b2d1f0SGlenn Strauss } 18381b2d1f0SGlenn Strauss 18481b2d1f0SGlenn Strauss /* = out of range */ 18581b2d1f0SGlenn Strauss if (eq > r + r_len) { 18681b2d1f0SGlenn Strauss log_error_write(srv, __FILE__, __LINE__, "sb", 18781b2d1f0SGlenn Strauss "parsing the 'require' section in 'auth.require' failed: = out of range", 18881b2d1f0SGlenn Strauss require->value); 18981b2d1f0SGlenn Strauss 19081b2d1f0SGlenn Strauss return -1; 19181b2d1f0SGlenn Strauss } 19281b2d1f0SGlenn Strauss 19381b2d1f0SGlenn Strauss /* the part before the = is user|group|host */ 19481b2d1f0SGlenn Strauss 19581b2d1f0SGlenn Strauss k = r; 19681b2d1f0SGlenn Strauss k_len = eq - r; 19781b2d1f0SGlenn Strauss v = eq + 1; 19881b2d1f0SGlenn Strauss v_len = r_len - k_len - 1; 19981b2d1f0SGlenn Strauss 20081b2d1f0SGlenn Strauss if (k_len == 4) { 20181b2d1f0SGlenn Strauss if (0 == strncmp(k, "user", k_len)) { 20281b2d1f0SGlenn Strauss if (username && 20381b2d1f0SGlenn Strauss username_len == v_len && 20481b2d1f0SGlenn Strauss 0 == strncmp(username, v, v_len)) { 20581b2d1f0SGlenn Strauss return 0; 20681b2d1f0SGlenn Strauss } 20781b2d1f0SGlenn Strauss } else if (0 == strncmp(k, "host", k_len)) { 20881b2d1f0SGlenn Strauss log_error_write(srv, __FILE__, __LINE__, "s", "host ... (not implemented)"); 20981b2d1f0SGlenn Strauss } else { 21081b2d1f0SGlenn Strauss log_error_write(srv, __FILE__, __LINE__, "s", "unknown key"); 21181b2d1f0SGlenn Strauss return -1; 21281b2d1f0SGlenn Strauss } 21381b2d1f0SGlenn Strauss } else if (k_len == 5) { 21481b2d1f0SGlenn Strauss if (0 == strncmp(k, "group", k_len)) { 21581b2d1f0SGlenn Strauss log_error_write(srv, __FILE__, __LINE__, "s", "group ... (not implemented)"); 21681b2d1f0SGlenn Strauss } else { 21781b2d1f0SGlenn Strauss log_error_write(srv, __FILE__, __LINE__, "ss", "unknown key", k); 21881b2d1f0SGlenn Strauss return -1; 21981b2d1f0SGlenn Strauss } 22081b2d1f0SGlenn Strauss } else { 22181b2d1f0SGlenn Strauss log_error_write(srv, __FILE__, __LINE__, "s", "unknown key"); 22281b2d1f0SGlenn Strauss return -1; 22381b2d1f0SGlenn Strauss } 22481b2d1f0SGlenn Strauss 22581b2d1f0SGlenn Strauss if (!e) break; 22681b2d1f0SGlenn Strauss r = e + 1; 22781b2d1f0SGlenn Strauss } 22881b2d1f0SGlenn Strauss 22981b2d1f0SGlenn Strauss log_error_write(srv, __FILE__, __LINE__, "s", "nothing matched"); 23081b2d1f0SGlenn Strauss 23181b2d1f0SGlenn Strauss return -1; 23281b2d1f0SGlenn Strauss } 23381b2d1f0SGlenn Strauss 234*4b3a91e6SGlenn Strauss static handler_t mod_auth_send_400_bad_request(server *srv, connection *con) { 235*4b3a91e6SGlenn Strauss UNUSED(srv); 236*4b3a91e6SGlenn Strauss 237*4b3a91e6SGlenn Strauss /* a field was missing or invalid */ 238*4b3a91e6SGlenn Strauss con->http_status = 400; /* Bad Request */ 239*4b3a91e6SGlenn Strauss con->mode = DIRECT; 240*4b3a91e6SGlenn Strauss 241*4b3a91e6SGlenn Strauss return HANDLER_FINISHED; 242*4b3a91e6SGlenn Strauss } 243*4b3a91e6SGlenn Strauss 244*4b3a91e6SGlenn Strauss static int mod_auth_digest_generate_nonce(server *srv, mod_auth_plugin_data *p, buffer *fn, char (*out)[33]); 245*4b3a91e6SGlenn Strauss 246*4b3a91e6SGlenn Strauss static handler_t mod_auth_send_401_unauthorized_basic(server *srv, connection *con, mod_auth_plugin_data *p, array *req) { 247*4b3a91e6SGlenn Strauss data_string *realm = (data_string *)array_get_element(req, "realm"); 248*4b3a91e6SGlenn Strauss 249*4b3a91e6SGlenn Strauss con->http_status = 401; 250*4b3a91e6SGlenn Strauss con->mode = DIRECT; 251*4b3a91e6SGlenn Strauss 252*4b3a91e6SGlenn Strauss if (!realm) return HANDLER_FINISHED; /*(should not happen; config is validated at startup)*/ 253*4b3a91e6SGlenn Strauss 254*4b3a91e6SGlenn Strauss buffer_copy_string_len(p->tmp_buf, CONST_STR_LEN("Basic realm=\"")); 255*4b3a91e6SGlenn Strauss buffer_append_string_buffer(p->tmp_buf, realm->value); 256*4b3a91e6SGlenn Strauss buffer_append_string_len(p->tmp_buf, CONST_STR_LEN("\", charset=\"UTF-8\"")); 257*4b3a91e6SGlenn Strauss 258*4b3a91e6SGlenn Strauss response_header_insert(srv, con, CONST_STR_LEN("WWW-Authenticate"), CONST_BUF_LEN(p->tmp_buf)); 259*4b3a91e6SGlenn Strauss 260*4b3a91e6SGlenn Strauss return HANDLER_FINISHED; 261*4b3a91e6SGlenn Strauss } 262*4b3a91e6SGlenn Strauss 263*4b3a91e6SGlenn Strauss static handler_t mod_auth_send_401_unauthorized_digest(server *srv, connection *con, mod_auth_plugin_data *p, array *req, int nonce_stale) { 264*4b3a91e6SGlenn Strauss data_string *realm = (data_string *)array_get_element(req, "realm"); 265*4b3a91e6SGlenn Strauss char hh[33]; 266*4b3a91e6SGlenn Strauss 267*4b3a91e6SGlenn Strauss con->http_status = 401; 268*4b3a91e6SGlenn Strauss con->mode = DIRECT; 269*4b3a91e6SGlenn Strauss 270*4b3a91e6SGlenn Strauss if (!realm) return HANDLER_FINISHED; /*(should not happen; config is validated at startup)*/ 271*4b3a91e6SGlenn Strauss 272*4b3a91e6SGlenn Strauss /* using unknown contents of srv->tmp_buf (modified elsewhere) 273*4b3a91e6SGlenn Strauss * adds dubious amount of randomness. Remove use of srv->tmp_buf? */ 274*4b3a91e6SGlenn Strauss mod_auth_digest_generate_nonce(srv, p, srv->tmp_buf, &hh); 275*4b3a91e6SGlenn Strauss 276*4b3a91e6SGlenn Strauss buffer_copy_string_len(p->tmp_buf, CONST_STR_LEN("Digest realm=\"")); 277*4b3a91e6SGlenn Strauss buffer_append_string_buffer(p->tmp_buf, realm->value); 278*4b3a91e6SGlenn Strauss buffer_append_string_len(p->tmp_buf, CONST_STR_LEN("\", charset=\"UTF-8\", nonce=\"")); 279*4b3a91e6SGlenn Strauss buffer_append_uint_hex(p->tmp_buf, (uintmax_t)srv->cur_ts); 280*4b3a91e6SGlenn Strauss buffer_append_string_len(p->tmp_buf, CONST_STR_LEN(":")); 281*4b3a91e6SGlenn Strauss buffer_append_string(p->tmp_buf, hh); 282*4b3a91e6SGlenn Strauss buffer_append_string_len(p->tmp_buf, CONST_STR_LEN("\", qop=\"auth\"")); 283*4b3a91e6SGlenn Strauss if (nonce_stale) { 284*4b3a91e6SGlenn Strauss buffer_append_string_len(p->tmp_buf, CONST_STR_LEN(", stale=true")); 285*4b3a91e6SGlenn Strauss } 286*4b3a91e6SGlenn Strauss 287*4b3a91e6SGlenn Strauss response_header_insert(srv, con, CONST_STR_LEN("WWW-Authenticate"), CONST_BUF_LEN(p->tmp_buf)); 288*4b3a91e6SGlenn Strauss 289*4b3a91e6SGlenn Strauss return HANDLER_FINISHED; 290*4b3a91e6SGlenn Strauss } 291*4b3a91e6SGlenn Strauss 292*4b3a91e6SGlenn Strauss static void mod_auth_setenv(server *srv, connection *con, const char *username, size_t ulen, const char *auth_type, size_t alen) { 293*4b3a91e6SGlenn Strauss data_string *ds; 294*4b3a91e6SGlenn Strauss UNUSED(srv); 295*4b3a91e6SGlenn Strauss 296*4b3a91e6SGlenn Strauss /* the REMOTE_USER header */ 297*4b3a91e6SGlenn Strauss 298*4b3a91e6SGlenn Strauss if (NULL == (ds = (data_string *)array_get_element(con->environment, "REMOTE_USER"))) { 299*4b3a91e6SGlenn Strauss if (NULL == (ds = (data_string *)array_get_unused_element(con->environment, TYPE_STRING))) { 300*4b3a91e6SGlenn Strauss ds = data_string_init(); 301*4b3a91e6SGlenn Strauss } 302*4b3a91e6SGlenn Strauss buffer_copy_string_len(ds->key, CONST_STR_LEN("REMOTE_USER")); 303*4b3a91e6SGlenn Strauss array_insert_unique(con->environment, (data_unset *)ds); 304*4b3a91e6SGlenn Strauss } 305*4b3a91e6SGlenn Strauss buffer_copy_string_len(ds->value, username, ulen); 306*4b3a91e6SGlenn Strauss 307*4b3a91e6SGlenn Strauss /* AUTH_TYPE environment */ 308*4b3a91e6SGlenn Strauss 309*4b3a91e6SGlenn Strauss if (NULL == (ds = (data_string *)array_get_element(con->environment, "AUTH_TYPE"))) { 310*4b3a91e6SGlenn Strauss if (NULL == (ds = (data_string *)array_get_unused_element(con->environment, TYPE_STRING))) { 311*4b3a91e6SGlenn Strauss ds = data_string_init(); 312*4b3a91e6SGlenn Strauss } 313*4b3a91e6SGlenn Strauss buffer_copy_string_len(ds->key, CONST_STR_LEN("AUTH_TYPE")); 314*4b3a91e6SGlenn Strauss array_insert_unique(con->environment, (data_unset *)ds); 315*4b3a91e6SGlenn Strauss } 316*4b3a91e6SGlenn Strauss buffer_copy_string_len(ds->value, auth_type, alen); 317*4b3a91e6SGlenn Strauss } 318*4b3a91e6SGlenn Strauss 319*4b3a91e6SGlenn Strauss static handler_t mod_auth_basic_check(server *srv, connection *con, mod_auth_plugin_data *p, array *req, const char *realm_str) { 32081b2d1f0SGlenn Strauss buffer *username; 32181b2d1f0SGlenn Strauss char *pw; 32281b2d1f0SGlenn Strauss 32381b2d1f0SGlenn Strauss data_string *realm; 32481b2d1f0SGlenn Strauss 32581b2d1f0SGlenn Strauss realm = (data_string *)array_get_element(req, "realm"); 326*4b3a91e6SGlenn Strauss if (!realm) { /*(should not happen; config is validated at startup)*/ 327*4b3a91e6SGlenn Strauss return mod_auth_send_400_bad_request(srv, con); 328*4b3a91e6SGlenn Strauss } 32981b2d1f0SGlenn Strauss 33081b2d1f0SGlenn Strauss username = buffer_init(); 33181b2d1f0SGlenn Strauss 33281b2d1f0SGlenn Strauss if (!buffer_append_base64_decode(username, realm_str, strlen(realm_str), BASE64_STANDARD)) { 33381b2d1f0SGlenn Strauss log_error_write(srv, __FILE__, __LINE__, "sb", "decodeing base64-string failed", username); 33481b2d1f0SGlenn Strauss 33581b2d1f0SGlenn Strauss buffer_free(username); 336*4b3a91e6SGlenn Strauss return mod_auth_send_400_bad_request(srv, con); 33781b2d1f0SGlenn Strauss } 33881b2d1f0SGlenn Strauss 33981b2d1f0SGlenn Strauss /* r2 == user:password */ 34081b2d1f0SGlenn Strauss if (NULL == (pw = strchr(username->ptr, ':'))) { 34181b2d1f0SGlenn Strauss log_error_write(srv, __FILE__, __LINE__, "sb", ": is missing in", username); 34281b2d1f0SGlenn Strauss 34381b2d1f0SGlenn Strauss buffer_free(username); 344*4b3a91e6SGlenn Strauss return mod_auth_send_400_bad_request(srv, con); 34581b2d1f0SGlenn Strauss } 34681b2d1f0SGlenn Strauss 34781b2d1f0SGlenn Strauss buffer_string_set_length(username, pw - username->ptr); 34881b2d1f0SGlenn Strauss pw++; 34981b2d1f0SGlenn Strauss 350*4b3a91e6SGlenn Strauss switch (p->conf.auth_backend->basic(srv, con, p->conf.auth_backend->p_d, username, realm->value, pw)) { 351*4b3a91e6SGlenn Strauss case HANDLER_GO_ON: 352*4b3a91e6SGlenn Strauss break; 353*4b3a91e6SGlenn Strauss case HANDLER_WAIT_FOR_EVENT: 35481b2d1f0SGlenn Strauss buffer_free(username); 355*4b3a91e6SGlenn Strauss return HANDLER_WAIT_FOR_EVENT; 356*4b3a91e6SGlenn Strauss case HANDLER_FINISHED: 357*4b3a91e6SGlenn Strauss buffer_free(username); 358*4b3a91e6SGlenn Strauss return HANDLER_FINISHED; 359*4b3a91e6SGlenn Strauss case HANDLER_ERROR: 360*4b3a91e6SGlenn Strauss default: 361*4b3a91e6SGlenn Strauss log_error_write(srv, __FILE__, __LINE__, "sbsBss", "password doesn't match for", con->uri.path, "username:", username, ", IP:", inet_ntop_cache_get_ip(srv, &(con->dst_addr))); 362*4b3a91e6SGlenn Strauss buffer_free(username); 363*4b3a91e6SGlenn Strauss return mod_auth_send_401_unauthorized_basic(srv, con, p, req); 36481b2d1f0SGlenn Strauss } 36581b2d1f0SGlenn Strauss 36681b2d1f0SGlenn Strauss /* value is our allow-rules */ 36781b2d1f0SGlenn Strauss if (mod_auth_match_rules(srv, req, username->ptr, NULL, NULL)) { 36881b2d1f0SGlenn Strauss log_error_write(srv, __FILE__, __LINE__, "s", "rules didn't match"); 36981b2d1f0SGlenn Strauss 370*4b3a91e6SGlenn Strauss buffer_free(username); 371*4b3a91e6SGlenn Strauss return mod_auth_send_401_unauthorized_basic(srv, con, p, req); 37281b2d1f0SGlenn Strauss } 37381b2d1f0SGlenn Strauss 374*4b3a91e6SGlenn Strauss mod_auth_setenv(srv, con, CONST_BUF_LEN(username), CONST_STR_LEN("Basic")); 37581b2d1f0SGlenn Strauss 37681b2d1f0SGlenn Strauss buffer_free(username); 37781b2d1f0SGlenn Strauss 378*4b3a91e6SGlenn Strauss return HANDLER_GO_ON; 37981b2d1f0SGlenn Strauss } 38081b2d1f0SGlenn Strauss 38181b2d1f0SGlenn Strauss #define HASHLEN 16 38281b2d1f0SGlenn Strauss #define HASHHEXLEN 32 38381b2d1f0SGlenn Strauss typedef unsigned char HASH[HASHLEN]; 38481b2d1f0SGlenn Strauss typedef char HASHHEX[HASHHEXLEN+1]; 38581b2d1f0SGlenn Strauss 38681b2d1f0SGlenn Strauss static void CvtHex(const HASH Bin, char (*Hex)[33]) { 38781b2d1f0SGlenn Strauss li_tohex(*Hex, sizeof(*Hex), (const char*) Bin, 16); 38881b2d1f0SGlenn Strauss } 38981b2d1f0SGlenn Strauss 39081b2d1f0SGlenn Strauss typedef struct { 39181b2d1f0SGlenn Strauss const char *key; 39281b2d1f0SGlenn Strauss int key_len; 39381b2d1f0SGlenn Strauss char **ptr; 39481b2d1f0SGlenn Strauss } digest_kv; 39581b2d1f0SGlenn Strauss 396*4b3a91e6SGlenn Strauss static handler_t mod_auth_digest_check(server *srv, connection *con, mod_auth_plugin_data *p, array *req, const char *realm_str) { 39781b2d1f0SGlenn Strauss char a1[33]; 39881b2d1f0SGlenn Strauss char a2[33]; 39981b2d1f0SGlenn Strauss 40081b2d1f0SGlenn Strauss char *username = NULL; 40181b2d1f0SGlenn Strauss char *realm = NULL; 40281b2d1f0SGlenn Strauss char *nonce = NULL; 40381b2d1f0SGlenn Strauss char *uri = NULL; 40481b2d1f0SGlenn Strauss char *algorithm = NULL; 40581b2d1f0SGlenn Strauss char *qop = NULL; 40681b2d1f0SGlenn Strauss char *cnonce = NULL; 40781b2d1f0SGlenn Strauss char *nc = NULL; 40881b2d1f0SGlenn Strauss char *respons = NULL; 40981b2d1f0SGlenn Strauss 41081b2d1f0SGlenn Strauss char *e, *c; 41181b2d1f0SGlenn Strauss const char *m = NULL; 41281b2d1f0SGlenn Strauss int i; 41381b2d1f0SGlenn Strauss buffer *b; 41481b2d1f0SGlenn Strauss 41581b2d1f0SGlenn Strauss li_MD5_CTX Md5Ctx; 41681b2d1f0SGlenn Strauss HASH HA1; 41781b2d1f0SGlenn Strauss HASH HA2; 41881b2d1f0SGlenn Strauss HASH RespHash; 41981b2d1f0SGlenn Strauss HASHHEX HA2Hex; 42081b2d1f0SGlenn Strauss 42181b2d1f0SGlenn Strauss 42281b2d1f0SGlenn Strauss /* init pointers */ 42381b2d1f0SGlenn Strauss #define S(x) \ 42481b2d1f0SGlenn Strauss x, sizeof(x)-1, NULL 42581b2d1f0SGlenn Strauss digest_kv dkv[10] = { 42681b2d1f0SGlenn Strauss { S("username=") }, 42781b2d1f0SGlenn Strauss { S("realm=") }, 42881b2d1f0SGlenn Strauss { S("nonce=") }, 42981b2d1f0SGlenn Strauss { S("uri=") }, 43081b2d1f0SGlenn Strauss { S("algorithm=") }, 43181b2d1f0SGlenn Strauss { S("qop=") }, 43281b2d1f0SGlenn Strauss { S("cnonce=") }, 43381b2d1f0SGlenn Strauss { S("nc=") }, 43481b2d1f0SGlenn Strauss { S("response=") }, 43581b2d1f0SGlenn Strauss 43681b2d1f0SGlenn Strauss { NULL, 0, NULL } 43781b2d1f0SGlenn Strauss }; 43881b2d1f0SGlenn Strauss #undef S 43981b2d1f0SGlenn Strauss 44081b2d1f0SGlenn Strauss dkv[0].ptr = &username; 44181b2d1f0SGlenn Strauss dkv[1].ptr = &realm; 44281b2d1f0SGlenn Strauss dkv[2].ptr = &nonce; 44381b2d1f0SGlenn Strauss dkv[3].ptr = &uri; 44481b2d1f0SGlenn Strauss dkv[4].ptr = &algorithm; 44581b2d1f0SGlenn Strauss dkv[5].ptr = &qop; 44681b2d1f0SGlenn Strauss dkv[6].ptr = &cnonce; 44781b2d1f0SGlenn Strauss dkv[7].ptr = &nc; 44881b2d1f0SGlenn Strauss dkv[8].ptr = &respons; 44981b2d1f0SGlenn Strauss 45081b2d1f0SGlenn Strauss b = buffer_init_string(realm_str); 45181b2d1f0SGlenn Strauss 45281b2d1f0SGlenn Strauss /* parse credentials from client */ 45381b2d1f0SGlenn Strauss for (c = b->ptr; *c; c++) { 45481b2d1f0SGlenn Strauss /* skip whitespaces */ 45581b2d1f0SGlenn Strauss while (*c == ' ' || *c == '\t') c++; 45681b2d1f0SGlenn Strauss if (!*c) break; 45781b2d1f0SGlenn Strauss 45881b2d1f0SGlenn Strauss for (i = 0; dkv[i].key; i++) { 45981b2d1f0SGlenn Strauss if ((0 == strncmp(c, dkv[i].key, dkv[i].key_len))) { 46081b2d1f0SGlenn Strauss if ((c[dkv[i].key_len] == '"') && 46181b2d1f0SGlenn Strauss (NULL != (e = strchr(c + dkv[i].key_len + 1, '"')))) { 46281b2d1f0SGlenn Strauss /* value with "..." */ 46381b2d1f0SGlenn Strauss *(dkv[i].ptr) = c + dkv[i].key_len + 1; 46481b2d1f0SGlenn Strauss c = e; 46581b2d1f0SGlenn Strauss 46681b2d1f0SGlenn Strauss *e = '\0'; 46781b2d1f0SGlenn Strauss } else if (NULL != (e = strchr(c + dkv[i].key_len, ','))) { 46881b2d1f0SGlenn Strauss /* value without "...", terminated by ',' */ 46981b2d1f0SGlenn Strauss *(dkv[i].ptr) = c + dkv[i].key_len; 47081b2d1f0SGlenn Strauss c = e; 47181b2d1f0SGlenn Strauss 47281b2d1f0SGlenn Strauss *e = '\0'; 47381b2d1f0SGlenn Strauss } else { 47481b2d1f0SGlenn Strauss /* value without "...", terminated by EOL */ 47581b2d1f0SGlenn Strauss *(dkv[i].ptr) = c + dkv[i].key_len; 47681b2d1f0SGlenn Strauss c += strlen(c) - 1; 47781b2d1f0SGlenn Strauss } 47881b2d1f0SGlenn Strauss break; 47981b2d1f0SGlenn Strauss } 48081b2d1f0SGlenn Strauss } 48181b2d1f0SGlenn Strauss } 48281b2d1f0SGlenn Strauss 48381b2d1f0SGlenn Strauss if (p->conf.auth_debug > 1) { 48481b2d1f0SGlenn Strauss log_error_write(srv, __FILE__, __LINE__, "ss", "username", username); 48581b2d1f0SGlenn Strauss log_error_write(srv, __FILE__, __LINE__, "ss", "realm", realm); 48681b2d1f0SGlenn Strauss log_error_write(srv, __FILE__, __LINE__, "ss", "nonce", nonce); 48781b2d1f0SGlenn Strauss log_error_write(srv, __FILE__, __LINE__, "ss", "uri", uri); 48881b2d1f0SGlenn Strauss log_error_write(srv, __FILE__, __LINE__, "ss", "algorithm", algorithm); 48981b2d1f0SGlenn Strauss log_error_write(srv, __FILE__, __LINE__, "ss", "qop", qop); 49081b2d1f0SGlenn Strauss log_error_write(srv, __FILE__, __LINE__, "ss", "cnonce", cnonce); 49181b2d1f0SGlenn Strauss log_error_write(srv, __FILE__, __LINE__, "ss", "nc", nc); 49281b2d1f0SGlenn Strauss log_error_write(srv, __FILE__, __LINE__, "ss", "response", respons); 49381b2d1f0SGlenn Strauss } 49481b2d1f0SGlenn Strauss 49581b2d1f0SGlenn Strauss /* check if everything is transmitted */ 49681b2d1f0SGlenn Strauss if (!username || 49781b2d1f0SGlenn Strauss !realm || 49881b2d1f0SGlenn Strauss !nonce || 49981b2d1f0SGlenn Strauss !uri || 50081b2d1f0SGlenn Strauss (qop && (!nc || !cnonce)) || 50181b2d1f0SGlenn Strauss !respons ) { 50281b2d1f0SGlenn Strauss /* missing field */ 50381b2d1f0SGlenn Strauss 50481b2d1f0SGlenn Strauss log_error_write(srv, __FILE__, __LINE__, "s", 50581b2d1f0SGlenn Strauss "digest: missing field"); 50681b2d1f0SGlenn Strauss 50781b2d1f0SGlenn Strauss buffer_free(b); 508*4b3a91e6SGlenn Strauss return mod_auth_send_400_bad_request(srv, con); 50981b2d1f0SGlenn Strauss } 51081b2d1f0SGlenn Strauss 51181b2d1f0SGlenn Strauss /** 51281b2d1f0SGlenn Strauss * protect the md5-sess against missing cnonce and nonce 51381b2d1f0SGlenn Strauss */ 51481b2d1f0SGlenn Strauss if (algorithm && 51581b2d1f0SGlenn Strauss 0 == strcasecmp(algorithm, "md5-sess") && 51681b2d1f0SGlenn Strauss (!nonce || !cnonce)) { 51781b2d1f0SGlenn Strauss log_error_write(srv, __FILE__, __LINE__, "s", 51881b2d1f0SGlenn Strauss "digest: (md5-sess: missing field"); 51981b2d1f0SGlenn Strauss 52081b2d1f0SGlenn Strauss buffer_free(b); 521*4b3a91e6SGlenn Strauss return mod_auth_send_400_bad_request(srv, con); 52281b2d1f0SGlenn Strauss } 52381b2d1f0SGlenn Strauss 52481b2d1f0SGlenn Strauss if (qop && strcasecmp(qop, "auth-int") == 0) { 52581b2d1f0SGlenn Strauss log_error_write(srv, __FILE__, __LINE__, "s", 52681b2d1f0SGlenn Strauss "digest: qop=auth-int not supported"); 52781b2d1f0SGlenn Strauss 52881b2d1f0SGlenn Strauss buffer_free(b); 529*4b3a91e6SGlenn Strauss return mod_auth_send_400_bad_request(srv, con); 53081b2d1f0SGlenn Strauss } 53181b2d1f0SGlenn Strauss 53281b2d1f0SGlenn Strauss m = get_http_method_name(con->request.http_method); 53381b2d1f0SGlenn Strauss force_assert(m); 53481b2d1f0SGlenn Strauss 53581b2d1f0SGlenn Strauss /* detect if attacker is attempting to reuse valid digest for one uri 53681b2d1f0SGlenn Strauss * on a different request uri. Might also happen if intermediate proxy 53781b2d1f0SGlenn Strauss * altered client request line. (Altered request would not result in 53881b2d1f0SGlenn Strauss * the same digest as that calculated by the client.) 53981b2d1f0SGlenn Strauss * Internal redirects such as with mod_rewrite will modify request uri. 54081b2d1f0SGlenn Strauss * Reauthentication is done to detect crossing auth realms, but this 54181b2d1f0SGlenn Strauss * uri validation step is bypassed. con->request.orig_uri is original 54281b2d1f0SGlenn Strauss * uri sent in client request. */ 54381b2d1f0SGlenn Strauss { 54481b2d1f0SGlenn Strauss const size_t ulen = strlen(uri); 54581b2d1f0SGlenn Strauss const size_t rlen = buffer_string_length(con->request.orig_uri); 54681b2d1f0SGlenn Strauss if (!buffer_is_equal_string(con->request.orig_uri, uri, ulen) 54781b2d1f0SGlenn Strauss && !(rlen < ulen && 0 == memcmp(con->request.orig_uri->ptr, uri, rlen) && uri[rlen] == '?')) { 54881b2d1f0SGlenn Strauss log_error_write(srv, __FILE__, __LINE__, "sbssss", 54981b2d1f0SGlenn Strauss "digest: auth failed: uri mismatch (", con->request.orig_uri, "!=", uri, "), IP:", inet_ntop_cache_get_ip(srv, &(con->dst_addr))); 55081b2d1f0SGlenn Strauss buffer_free(b); 551*4b3a91e6SGlenn Strauss return mod_auth_send_400_bad_request(srv, con); 55281b2d1f0SGlenn Strauss } 55381b2d1f0SGlenn Strauss } 55481b2d1f0SGlenn Strauss 55581b2d1f0SGlenn Strauss /* password-string == HA1 */ 556*4b3a91e6SGlenn Strauss switch (p->conf.auth_backend->digest(srv, con, p->conf.auth_backend->p_d, username, realm, HA1)) { 557*4b3a91e6SGlenn Strauss case HANDLER_GO_ON: 558*4b3a91e6SGlenn Strauss break; 559*4b3a91e6SGlenn Strauss case HANDLER_WAIT_FOR_EVENT: 56081b2d1f0SGlenn Strauss buffer_free(b); 561*4b3a91e6SGlenn Strauss return HANDLER_WAIT_FOR_EVENT; 562*4b3a91e6SGlenn Strauss case HANDLER_FINISHED: 563*4b3a91e6SGlenn Strauss buffer_free(b); 564*4b3a91e6SGlenn Strauss return HANDLER_FINISHED; 565*4b3a91e6SGlenn Strauss case HANDLER_ERROR: 566*4b3a91e6SGlenn Strauss default: 567*4b3a91e6SGlenn Strauss buffer_free(b); 568*4b3a91e6SGlenn Strauss return mod_auth_send_401_unauthorized_digest(srv, con, p, req, 0); 56981b2d1f0SGlenn Strauss } 57081b2d1f0SGlenn Strauss 57181b2d1f0SGlenn Strauss if (algorithm && 57281b2d1f0SGlenn Strauss strcasecmp(algorithm, "md5-sess") == 0) { 57381b2d1f0SGlenn Strauss li_MD5_Init(&Md5Ctx); 57481b2d1f0SGlenn Strauss /* Errata ID 1649: http://www.rfc-editor.org/errata_search.php?rfc=2617 */ 57581b2d1f0SGlenn Strauss CvtHex(HA1, &a1); 57681b2d1f0SGlenn Strauss li_MD5_Update(&Md5Ctx, (unsigned char *)a1, HASHHEXLEN); 57781b2d1f0SGlenn Strauss li_MD5_Update(&Md5Ctx, CONST_STR_LEN(":")); 57881b2d1f0SGlenn Strauss li_MD5_Update(&Md5Ctx, (unsigned char *)nonce, strlen(nonce)); 57981b2d1f0SGlenn Strauss li_MD5_Update(&Md5Ctx, CONST_STR_LEN(":")); 58081b2d1f0SGlenn Strauss li_MD5_Update(&Md5Ctx, (unsigned char *)cnonce, strlen(cnonce)); 58181b2d1f0SGlenn Strauss li_MD5_Final(HA1, &Md5Ctx); 58281b2d1f0SGlenn Strauss } 58381b2d1f0SGlenn Strauss 58481b2d1f0SGlenn Strauss CvtHex(HA1, &a1); 58581b2d1f0SGlenn Strauss 58681b2d1f0SGlenn Strauss /* calculate H(A2) */ 58781b2d1f0SGlenn Strauss li_MD5_Init(&Md5Ctx); 58881b2d1f0SGlenn Strauss li_MD5_Update(&Md5Ctx, (unsigned char *)m, strlen(m)); 58981b2d1f0SGlenn Strauss li_MD5_Update(&Md5Ctx, CONST_STR_LEN(":")); 59081b2d1f0SGlenn Strauss li_MD5_Update(&Md5Ctx, (unsigned char *)uri, strlen(uri)); 59181b2d1f0SGlenn Strauss /* qop=auth-int not supported, already checked above */ 59281b2d1f0SGlenn Strauss /* 59381b2d1f0SGlenn Strauss if (qop && strcasecmp(qop, "auth-int") == 0) { 59481b2d1f0SGlenn Strauss li_MD5_Update(&Md5Ctx, CONST_STR_LEN(":")); 59581b2d1f0SGlenn Strauss li_MD5_Update(&Md5Ctx, (unsigned char *) [body checksum], HASHHEXLEN); 59681b2d1f0SGlenn Strauss } 59781b2d1f0SGlenn Strauss */ 59881b2d1f0SGlenn Strauss li_MD5_Final(HA2, &Md5Ctx); 59981b2d1f0SGlenn Strauss CvtHex(HA2, &HA2Hex); 60081b2d1f0SGlenn Strauss 60181b2d1f0SGlenn Strauss /* calculate response */ 60281b2d1f0SGlenn Strauss li_MD5_Init(&Md5Ctx); 60381b2d1f0SGlenn Strauss li_MD5_Update(&Md5Ctx, (unsigned char *)a1, HASHHEXLEN); 60481b2d1f0SGlenn Strauss li_MD5_Update(&Md5Ctx, CONST_STR_LEN(":")); 60581b2d1f0SGlenn Strauss li_MD5_Update(&Md5Ctx, (unsigned char *)nonce, strlen(nonce)); 60681b2d1f0SGlenn Strauss li_MD5_Update(&Md5Ctx, CONST_STR_LEN(":")); 60781b2d1f0SGlenn Strauss if (qop && *qop) { 60881b2d1f0SGlenn Strauss li_MD5_Update(&Md5Ctx, (unsigned char *)nc, strlen(nc)); 60981b2d1f0SGlenn Strauss li_MD5_Update(&Md5Ctx, CONST_STR_LEN(":")); 61081b2d1f0SGlenn Strauss li_MD5_Update(&Md5Ctx, (unsigned char *)cnonce, strlen(cnonce)); 61181b2d1f0SGlenn Strauss li_MD5_Update(&Md5Ctx, CONST_STR_LEN(":")); 61281b2d1f0SGlenn Strauss li_MD5_Update(&Md5Ctx, (unsigned char *)qop, strlen(qop)); 61381b2d1f0SGlenn Strauss li_MD5_Update(&Md5Ctx, CONST_STR_LEN(":")); 61481b2d1f0SGlenn Strauss }; 61581b2d1f0SGlenn Strauss li_MD5_Update(&Md5Ctx, (unsigned char *)HA2Hex, HASHHEXLEN); 61681b2d1f0SGlenn Strauss li_MD5_Final(RespHash, &Md5Ctx); 61781b2d1f0SGlenn Strauss CvtHex(RespHash, &a2); 61881b2d1f0SGlenn Strauss 61981b2d1f0SGlenn Strauss if (0 != strcmp(a2, respons)) { 62081b2d1f0SGlenn Strauss /* digest not ok */ 62181b2d1f0SGlenn Strauss 62281b2d1f0SGlenn Strauss if (p->conf.auth_debug) { 62381b2d1f0SGlenn Strauss log_error_write(srv, __FILE__, __LINE__, "sss", 62481b2d1f0SGlenn Strauss "digest: digest mismatch", a2, respons); 62581b2d1f0SGlenn Strauss } 62681b2d1f0SGlenn Strauss 62781b2d1f0SGlenn Strauss log_error_write(srv, __FILE__, __LINE__, "ssss", 62881b2d1f0SGlenn Strauss "digest: auth failed for ", username, ": wrong password, IP:", inet_ntop_cache_get_ip(srv, &(con->dst_addr))); 62981b2d1f0SGlenn Strauss 63081b2d1f0SGlenn Strauss buffer_free(b); 631*4b3a91e6SGlenn Strauss return mod_auth_send_401_unauthorized_digest(srv, con, p, req, 0); 63281b2d1f0SGlenn Strauss } 63381b2d1f0SGlenn Strauss 63481b2d1f0SGlenn Strauss /* value is our allow-rules */ 63581b2d1f0SGlenn Strauss if (mod_auth_match_rules(srv, req, username, NULL, NULL)) { 63681b2d1f0SGlenn Strauss 63781b2d1f0SGlenn Strauss if (p->conf.auth_debug) { 63881b2d1f0SGlenn Strauss log_error_write(srv, __FILE__, __LINE__, "s", 63981b2d1f0SGlenn Strauss "digest: rules did match"); 64081b2d1f0SGlenn Strauss } 64181b2d1f0SGlenn Strauss 642*4b3a91e6SGlenn Strauss buffer_free(b); 643*4b3a91e6SGlenn Strauss return mod_auth_send_401_unauthorized_digest(srv, con, p, req, 0); 64481b2d1f0SGlenn Strauss } 64581b2d1f0SGlenn Strauss 64681b2d1f0SGlenn Strauss /* check age of nonce. Note that rand() is used in nonce generation 64781b2d1f0SGlenn Strauss * in mod_auth_digest_generate_nonce(). If that were replaced 64881b2d1f0SGlenn Strauss * with nanosecond time, then nonce secret would remain unique enough 64981b2d1f0SGlenn Strauss * for the purposes of Digest auth, and would be reproducible (and 65081b2d1f0SGlenn Strauss * verifiable) if nanoseconds were inclued with seconds as part of the 65181b2d1f0SGlenn Strauss * nonce "timestamp:secret". Since that is not done, timestamp in 65281b2d1f0SGlenn Strauss * nonce could theoretically be modified and still produce same md5sum, 65381b2d1f0SGlenn Strauss * but that is highly unlikely within a 10 min (moving) window of valid 65481b2d1f0SGlenn Strauss * time relative to current time (now) */ 65581b2d1f0SGlenn Strauss { 65681b2d1f0SGlenn Strauss time_t ts = 0; 65781b2d1f0SGlenn Strauss const unsigned char * const nonce_uns = (unsigned char *)nonce; 65881b2d1f0SGlenn Strauss for (i = 0; i < 8 && light_isxdigit(nonce_uns[i]); ++i) { 65981b2d1f0SGlenn Strauss ts = (ts << 4) + hex2int(nonce_uns[i]); 66081b2d1f0SGlenn Strauss } 66181b2d1f0SGlenn Strauss if (i != 8 || nonce[8] != ':' 66281b2d1f0SGlenn Strauss || ts > srv->cur_ts || srv->cur_ts - ts > 600) { /*(10 mins)*/ 663*4b3a91e6SGlenn Strauss /* nonce is stale; have client regenerate digest */ 66481b2d1f0SGlenn Strauss buffer_free(b); 665*4b3a91e6SGlenn Strauss return mod_auth_send_401_unauthorized_digest(srv, con, p, req, 1); 66681b2d1f0SGlenn Strauss } /*(future: might send nextnonce when expiration is imminent)*/ 66781b2d1f0SGlenn Strauss } 66881b2d1f0SGlenn Strauss 669*4b3a91e6SGlenn Strauss mod_auth_setenv(srv, con, username, strlen(username), CONST_STR_LEN("Digest")); 67081b2d1f0SGlenn Strauss 67181b2d1f0SGlenn Strauss buffer_free(b); 67281b2d1f0SGlenn Strauss 67381b2d1f0SGlenn Strauss if (p->conf.auth_debug) { 67481b2d1f0SGlenn Strauss log_error_write(srv, __FILE__, __LINE__, "s", 67581b2d1f0SGlenn Strauss "digest: auth ok"); 67681b2d1f0SGlenn Strauss } 677*4b3a91e6SGlenn Strauss 678*4b3a91e6SGlenn Strauss return HANDLER_GO_ON; 67981b2d1f0SGlenn Strauss } 68081b2d1f0SGlenn Strauss 68181b2d1f0SGlenn Strauss static int mod_auth_digest_generate_nonce(server *srv, mod_auth_plugin_data *p, buffer *fn, char (*out)[33]) { 68281b2d1f0SGlenn Strauss HASH h; 68381b2d1f0SGlenn Strauss li_MD5_CTX Md5Ctx; 68481b2d1f0SGlenn Strauss char hh[LI_ITOSTRING_LENGTH]; 68581b2d1f0SGlenn Strauss 68681b2d1f0SGlenn Strauss UNUSED(p); 68781b2d1f0SGlenn Strauss 68881b2d1f0SGlenn Strauss /* generate shared-secret */ 68981b2d1f0SGlenn Strauss li_MD5_Init(&Md5Ctx); 69081b2d1f0SGlenn Strauss li_MD5_Update(&Md5Ctx, CONST_BUF_LEN(fn)); 69181b2d1f0SGlenn Strauss li_MD5_Update(&Md5Ctx, CONST_STR_LEN("+")); 69281b2d1f0SGlenn Strauss 69381b2d1f0SGlenn Strauss /* we assume sizeof(time_t) == 4 here, but if not it ain't a problem at all */ 69481b2d1f0SGlenn Strauss li_itostrn(hh, sizeof(hh), srv->cur_ts); 69581b2d1f0SGlenn Strauss li_MD5_Update(&Md5Ctx, (unsigned char *)hh, strlen(hh)); 69681b2d1f0SGlenn Strauss li_MD5_Update(&Md5Ctx, (unsigned char *)srv->entropy, sizeof(srv->entropy)); 69781b2d1f0SGlenn Strauss li_itostrn(hh, sizeof(hh), rand()); 69881b2d1f0SGlenn Strauss li_MD5_Update(&Md5Ctx, (unsigned char *)hh, strlen(hh)); 69981b2d1f0SGlenn Strauss 70081b2d1f0SGlenn Strauss li_MD5_Final(h, &Md5Ctx); 70181b2d1f0SGlenn Strauss 70281b2d1f0SGlenn Strauss CvtHex(h, out); 70381b2d1f0SGlenn Strauss 70481b2d1f0SGlenn Strauss return 0; 70581b2d1f0SGlenn Strauss } 70681b2d1f0SGlenn Strauss 707bcdc6a3bSJan Kneschke static handler_t mod_auth_uri_handler(server *srv, connection *con, void *p_d) { 708bcdc6a3bSJan Kneschke size_t k; 709*4b3a91e6SGlenn Strauss int auth_required; 710bcdc6a3bSJan Kneschke char *http_authorization = NULL; 711bcdc6a3bSJan Kneschke data_string *ds; 712bcdc6a3bSJan Kneschke mod_auth_plugin_data *p = p_d; 713bcdc6a3bSJan Kneschke array *req; 714f9d58670SStefan Bühler data_string *req_method; 715bcdc6a3bSJan Kneschke 716bcdc6a3bSJan Kneschke /* select the right config */ 7174a6ce4aeSJan Kneschke mod_auth_patch_connection(srv, con, p); 718bcdc6a3bSJan Kneschke 719bcdc6a3bSJan Kneschke if (p->conf.auth_require == NULL) return HANDLER_GO_ON; 720bcdc6a3bSJan Kneschke 721bcdc6a3bSJan Kneschke /* 722bcdc6a3bSJan Kneschke * AUTH 723bcdc6a3bSJan Kneschke * 724bcdc6a3bSJan Kneschke */ 725bcdc6a3bSJan Kneschke 726bcdc6a3bSJan Kneschke /* do we have to ask for auth ? */ 727bcdc6a3bSJan Kneschke 728bcdc6a3bSJan Kneschke auth_required = 0; 729bcdc6a3bSJan Kneschke 730bcdc6a3bSJan Kneschke /* search auth-directives for path */ 731bcdc6a3bSJan Kneschke for (k = 0; k < p->conf.auth_require->used; k++) { 73267079527SJan Kneschke buffer *require = p->conf.auth_require->data[k]->key; 733bcdc6a3bSJan Kneschke 734ad3e93eaSStefan Bühler if (buffer_is_empty(require)) continue; 735ad3e93eaSStefan Bühler if (buffer_string_length(con->uri.path) < buffer_string_length(require)) continue; 7364afd2ae2SJan Kneschke 7374afd2ae2SJan Kneschke /* if we have a case-insensitive FS we have to lower-case the URI here too */ 7384afd2ae2SJan Kneschke 7394afd2ae2SJan Kneschke if (con->conf.force_lowercase_filenames) { 740ad3e93eaSStefan Bühler if (0 == strncasecmp(con->uri.path->ptr, require->ptr, buffer_string_length(require))) { 741bcdc6a3bSJan Kneschke auth_required = 1; 742bcdc6a3bSJan Kneschke break; 743bcdc6a3bSJan Kneschke } 7444afd2ae2SJan Kneschke } else { 745ad3e93eaSStefan Bühler if (0 == strncmp(con->uri.path->ptr, require->ptr, buffer_string_length(require))) { 7464afd2ae2SJan Kneschke auth_required = 1; 7474afd2ae2SJan Kneschke break; 7484afd2ae2SJan Kneschke } 7494afd2ae2SJan Kneschke } 750bcdc6a3bSJan Kneschke } 751bcdc6a3bSJan Kneschke 752bcdc6a3bSJan Kneschke /* nothing to do for us */ 753bcdc6a3bSJan Kneschke if (auth_required == 0) return HANDLER_GO_ON; 754bcdc6a3bSJan Kneschke 755bcdc6a3bSJan Kneschke req = ((data_array *)(p->conf.auth_require->data[k]))->value; 756f9d58670SStefan Bühler req_method = (data_string *)array_get_element(req, "method"); 757f9d58670SStefan Bühler 758f9d58670SStefan Bühler if (0 == strcmp(req_method->value->ptr, "extern")) { 759f9d58670SStefan Bühler /* require REMOTE_USER to be already set */ 760f9d58670SStefan Bühler if (NULL == (ds = (data_string *)array_get_element(con->environment, "REMOTE_USER"))) { 761f9d58670SStefan Bühler con->http_status = 401; 762f9d58670SStefan Bühler con->mode = DIRECT; 763f9d58670SStefan Bühler return HANDLER_FINISHED; 76481b2d1f0SGlenn Strauss } else if (mod_auth_match_rules(srv, req, ds->value->ptr, NULL, NULL)) { 765f9d58670SStefan Bühler log_error_write(srv, __FILE__, __LINE__, "s", "rules didn't match"); 766f9d58670SStefan Bühler con->http_status = 401; 767f9d58670SStefan Bühler con->mode = DIRECT; 768f9d58670SStefan Bühler return HANDLER_FINISHED; 769f9d58670SStefan Bühler } else { 770f9d58670SStefan Bühler return HANDLER_GO_ON; 771f9d58670SStefan Bühler } 772f9d58670SStefan Bühler } 773bcdc6a3bSJan Kneschke 774*4b3a91e6SGlenn Strauss /* check for configured backend 775*4b3a91e6SGlenn Strauss * (XXX: should try to catch this at config time, if not "extern" method) */ 776*4b3a91e6SGlenn Strauss if (NULL == p->conf.auth_backend) { 777*4b3a91e6SGlenn Strauss log_error_write(srv, __FILE__, __LINE__, "sb", "auth.backend not configured for", con->uri.path); 778*4b3a91e6SGlenn Strauss con->http_status = 500; 779*4b3a91e6SGlenn Strauss con->mode = DIRECT; 780*4b3a91e6SGlenn Strauss return HANDLER_FINISHED; 781*4b3a91e6SGlenn Strauss } 782*4b3a91e6SGlenn Strauss 783bcdc6a3bSJan Kneschke /* try to get Authorization-header */ 784bcdc6a3bSJan Kneschke 785ad3e93eaSStefan Bühler if (NULL != (ds = (data_string *)array_get_element(con->request.headers, "Authorization")) && !buffer_is_empty(ds->value)) { 786bcdc6a3bSJan Kneschke char *auth_realm; 787bcdc6a3bSJan Kneschke 788f9d58670SStefan Bühler http_authorization = ds->value->ptr; 789bcdc6a3bSJan Kneschke 790bcdc6a3bSJan Kneschke /* parse auth-header */ 791bcdc6a3bSJan Kneschke if (NULL != (auth_realm = strchr(http_authorization, ' '))) { 792bcdc6a3bSJan Kneschke int auth_type_len = auth_realm - http_authorization; 793bcdc6a3bSJan Kneschke 794bcdc6a3bSJan Kneschke if ((auth_type_len == 5) && 795708f499dSJan Kneschke (0 == strncasecmp(http_authorization, "Basic", auth_type_len))) { 796f9d58670SStefan Bühler if (0 == strcmp(req_method->value->ptr, "basic")) { 797*4b3a91e6SGlenn Strauss return mod_auth_basic_check(srv, con, p, req, auth_realm+1); 798bcdc6a3bSJan Kneschke } 799bcdc6a3bSJan Kneschke } else if ((auth_type_len == 6) && 800708f499dSJan Kneschke (0 == strncasecmp(http_authorization, "Digest", auth_type_len))) { 801f9d58670SStefan Bühler if (0 == strcmp(req_method->value->ptr, "digest")) { 802*4b3a91e6SGlenn Strauss return mod_auth_digest_check(srv, con, p, req, auth_realm+1); 803bcdc6a3bSJan Kneschke } 804bcdc6a3bSJan Kneschke } else { 805bcdc6a3bSJan Kneschke log_error_write(srv, __FILE__, __LINE__, "ss", 806780e8e6eSStefan Bühler "unknown authentication type:", 807bcdc6a3bSJan Kneschke http_authorization); 808*4b3a91e6SGlenn Strauss return mod_auth_send_400_bad_request(srv, con); 809*4b3a91e6SGlenn Strauss } 810*4b3a91e6SGlenn Strauss } else { 811*4b3a91e6SGlenn Strauss return mod_auth_send_400_bad_request(srv, con); 812bcdc6a3bSJan Kneschke } 813bcdc6a3bSJan Kneschke } 814bcdc6a3bSJan Kneschke 815*4b3a91e6SGlenn Strauss if (0 == strcmp(req_method->value->ptr, "basic")) { 816*4b3a91e6SGlenn Strauss return mod_auth_send_401_unauthorized_basic(srv, con, p, req); 817*4b3a91e6SGlenn Strauss } else if (0 == strcmp(req_method->value->ptr, "digest")) { 818*4b3a91e6SGlenn Strauss return mod_auth_send_401_unauthorized_digest(srv, con, p, req, 0); 819bcdc6a3bSJan Kneschke } else { 820bcdc6a3bSJan Kneschke /* evil */ 821*4b3a91e6SGlenn Strauss con->http_status = 401; 822*4b3a91e6SGlenn Strauss con->mode = DIRECT; 823bcdc6a3bSJan Kneschke return HANDLER_FINISHED; 8247187271fSStefan Bühler } 825bcdc6a3bSJan Kneschke } 826bcdc6a3bSJan Kneschke 827bcdc6a3bSJan Kneschke SETDEFAULTS_FUNC(mod_auth_set_defaults) { 828bcdc6a3bSJan Kneschke mod_auth_plugin_data *p = p_d; 829bcdc6a3bSJan Kneschke size_t i; 830bcdc6a3bSJan Kneschke 831bcdc6a3bSJan Kneschke config_values_t cv[] = { 832bcdc6a3bSJan Kneschke { "auth.backend", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 0 */ 833*4b3a91e6SGlenn Strauss { "auth.require", NULL, T_CONFIG_LOCAL, T_CONFIG_SCOPE_CONNECTION }, /* 1 */ 834*4b3a91e6SGlenn Strauss { "auth.debug", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, /* 2 */ 835bcdc6a3bSJan Kneschke { NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET } 836bcdc6a3bSJan Kneschke }; 837bcdc6a3bSJan Kneschke 8386f208cfdSStefan Bühler p->config_storage = calloc(1, srv->config_context->used * sizeof(mod_auth_plugin_config *)); 839bcdc6a3bSJan Kneschke 840bcdc6a3bSJan Kneschke for (i = 0; i < srv->config_context->used; i++) { 841c512345fSStefan Bühler data_config const* config = (data_config const*)srv->config_context->data[i]; 842bcdc6a3bSJan Kneschke mod_auth_plugin_config *s; 843bcdc6a3bSJan Kneschke size_t n; 844bcdc6a3bSJan Kneschke data_array *da; 845bcdc6a3bSJan Kneschke 846bac9212cSJan Kneschke s = calloc(1, sizeof(mod_auth_plugin_config)); 847bcdc6a3bSJan Kneschke s->auth_backend_conf = buffer_init(); 848bcdc6a3bSJan Kneschke 849bcdc6a3bSJan Kneschke s->auth_debug = 0; 850bcdc6a3bSJan Kneschke 851bcdc6a3bSJan Kneschke s->auth_require = array_init(); 852bcdc6a3bSJan Kneschke 853bcdc6a3bSJan Kneschke cv[0].destination = s->auth_backend_conf; 854*4b3a91e6SGlenn Strauss cv[1].destination = s->auth_require; 855*4b3a91e6SGlenn Strauss cv[2].destination = &(s->auth_debug); 856bcdc6a3bSJan Kneschke 857bcdc6a3bSJan Kneschke p->config_storage[i] = s; 858bcdc6a3bSJan Kneschke 859c512345fSStefan Bühler if (0 != config_insert_values_global(srv, config->value, cv, i == 0 ? T_CONFIG_SCOPE_SERVER : T_CONFIG_SCOPE_CONNECTION)) { 860bcdc6a3bSJan Kneschke return HANDLER_ERROR; 861bcdc6a3bSJan Kneschke } 862bcdc6a3bSJan Kneschke 863ad3e93eaSStefan Bühler if (!buffer_string_is_empty(s->auth_backend_conf)) { 864*4b3a91e6SGlenn Strauss s->auth_backend = http_auth_backend_get(s->auth_backend_conf); 865*4b3a91e6SGlenn Strauss if (NULL == s->auth_backend) { 866bcdc6a3bSJan Kneschke log_error_write(srv, __FILE__, __LINE__, "sb", "auth.backend not supported:", s->auth_backend_conf); 867bcdc6a3bSJan Kneschke 868bcdc6a3bSJan Kneschke return HANDLER_ERROR; 869bcdc6a3bSJan Kneschke } 870bcdc6a3bSJan Kneschke } 871bcdc6a3bSJan Kneschke 872bcdc6a3bSJan Kneschke /* no auth.require for this section */ 873c512345fSStefan Bühler if (NULL == (da = (data_array *)array_get_element(config->value, "auth.require"))) continue; 874bcdc6a3bSJan Kneschke 875bcdc6a3bSJan Kneschke if (da->type != TYPE_ARRAY) continue; 876bcdc6a3bSJan Kneschke 877bcdc6a3bSJan Kneschke for (n = 0; n < da->value->used; n++) { 878bcdc6a3bSJan Kneschke size_t m; 879bcdc6a3bSJan Kneschke data_array *da_file = (data_array *)da->value->data[n]; 880bcdc6a3bSJan Kneschke const char *method, *realm, *require; 881bcdc6a3bSJan Kneschke 882bcdc6a3bSJan Kneschke if (da->value->data[n]->type != TYPE_ARRAY) { 883cc7a8ae0SJan Kneschke log_error_write(srv, __FILE__, __LINE__, "ss", 884cc7a8ae0SJan Kneschke "auth.require should contain an array as in:", 885cc7a8ae0SJan Kneschke "auth.require = ( \"...\" => ( ..., ...) )"); 886bcdc6a3bSJan Kneschke 887bcdc6a3bSJan Kneschke return HANDLER_ERROR; 888bcdc6a3bSJan Kneschke } 889bcdc6a3bSJan Kneschke 890bcdc6a3bSJan Kneschke method = realm = require = NULL; 891bcdc6a3bSJan Kneschke 892bcdc6a3bSJan Kneschke for (m = 0; m < da_file->value->used; m++) { 893bcdc6a3bSJan Kneschke if (da_file->value->data[m]->type == TYPE_STRING) { 894bcdc6a3bSJan Kneschke if (0 == strcmp(da_file->value->data[m]->key->ptr, "method")) { 895bcdc6a3bSJan Kneschke method = ((data_string *)(da_file->value->data[m]))->value->ptr; 896bcdc6a3bSJan Kneschke } else if (0 == strcmp(da_file->value->data[m]->key->ptr, "realm")) { 897bcdc6a3bSJan Kneschke realm = ((data_string *)(da_file->value->data[m]))->value->ptr; 898bcdc6a3bSJan Kneschke } else if (0 == strcmp(da_file->value->data[m]->key->ptr, "require")) { 899bcdc6a3bSJan Kneschke require = ((data_string *)(da_file->value->data[m]))->value->ptr; 900bcdc6a3bSJan Kneschke } else { 901cc7a8ae0SJan Kneschke log_error_write(srv, __FILE__, __LINE__, "ssbs", 902cc7a8ae0SJan Kneschke "the field is unknown in:", 903cc7a8ae0SJan Kneschke "auth.require = ( \"...\" => ( ..., -> \"", 904cc7a8ae0SJan Kneschke da_file->value->data[m]->key, 905cc7a8ae0SJan Kneschke "\" <- => \"...\" ) )"); 906cc7a8ae0SJan Kneschke 907bcdc6a3bSJan Kneschke return HANDLER_ERROR; 908bcdc6a3bSJan Kneschke } 909bcdc6a3bSJan Kneschke } else { 910cc7a8ae0SJan Kneschke log_error_write(srv, __FILE__, __LINE__, "ssbs", 911cc7a8ae0SJan Kneschke "a string was expected for:", 912cc7a8ae0SJan Kneschke "auth.require = ( \"...\" => ( ..., -> \"", 913cc7a8ae0SJan Kneschke da_file->value->data[m]->key, 914cc7a8ae0SJan Kneschke "\" <- => \"...\" ) )"); 915bcdc6a3bSJan Kneschke 916bcdc6a3bSJan Kneschke return HANDLER_ERROR; 917bcdc6a3bSJan Kneschke } 918bcdc6a3bSJan Kneschke } 919bcdc6a3bSJan Kneschke 920bcdc6a3bSJan Kneschke if (method == NULL) { 921cc7a8ae0SJan Kneschke log_error_write(srv, __FILE__, __LINE__, "ss", 9224df22f2aSStefan Bühler "the method field is missing in:", 923cc7a8ae0SJan Kneschke "auth.require = ( \"...\" => ( ..., \"method\" => \"...\" ) )"); 924bcdc6a3bSJan Kneschke return HANDLER_ERROR; 9254a6ce4aeSJan Kneschke } else { 9264a6ce4aeSJan Kneschke if (0 != strcmp(method, "basic") && 927f9d58670SStefan Bühler 0 != strcmp(method, "digest") && 928f9d58670SStefan Bühler 0 != strcmp(method, "extern")) { 929cc7a8ae0SJan Kneschke log_error_write(srv, __FILE__, __LINE__, "ss", 930f9d58670SStefan Bühler "method has to be either \"basic\", \"digest\" or \"extern\" in", 931cc7a8ae0SJan Kneschke "auth.require = ( \"...\" => ( ..., \"method\" => \"...\") )"); 9324a6ce4aeSJan Kneschke return HANDLER_ERROR; 9334a6ce4aeSJan Kneschke } 934bcdc6a3bSJan Kneschke } 935bcdc6a3bSJan Kneschke 936bcdc6a3bSJan Kneschke if (realm == NULL) { 937cc7a8ae0SJan Kneschke log_error_write(srv, __FILE__, __LINE__, "ss", 9384df22f2aSStefan Bühler "the realm field is missing in:", 939cc7a8ae0SJan Kneschke "auth.require = ( \"...\" => ( ..., \"realm\" => \"...\" ) )"); 940bcdc6a3bSJan Kneschke return HANDLER_ERROR; 941bcdc6a3bSJan Kneschke } 942bcdc6a3bSJan Kneschke 943bcdc6a3bSJan Kneschke if (require == NULL) { 944cc7a8ae0SJan Kneschke log_error_write(srv, __FILE__, __LINE__, "ss", 945cc7a8ae0SJan Kneschke "the require field is missing in:", 946cc7a8ae0SJan Kneschke "auth.require = ( \"...\" => ( ..., \"require\" => \"...\" ) )"); 947bcdc6a3bSJan Kneschke return HANDLER_ERROR; 948bcdc6a3bSJan Kneschke } 949bcdc6a3bSJan Kneschke 950bcdc6a3bSJan Kneschke if (method && realm && require) { 951bcdc6a3bSJan Kneschke data_string *ds; 952bcdc6a3bSJan Kneschke data_array *a; 953bcdc6a3bSJan Kneschke 954bcdc6a3bSJan Kneschke a = data_array_init(); 9556afad87dSStefan Bühler buffer_copy_buffer(a->key, da_file->key); 956bcdc6a3bSJan Kneschke 957bcdc6a3bSJan Kneschke ds = data_string_init(); 958bcdc6a3bSJan Kneschke 95952861d77SStefan Bühler buffer_copy_string_len(ds->key, CONST_STR_LEN("method")); 960bcdc6a3bSJan Kneschke buffer_copy_string(ds->value, method); 961bcdc6a3bSJan Kneschke 962bcdc6a3bSJan Kneschke array_insert_unique(a->value, (data_unset *)ds); 963bcdc6a3bSJan Kneschke 964bcdc6a3bSJan Kneschke ds = data_string_init(); 965bcdc6a3bSJan Kneschke 96652861d77SStefan Bühler buffer_copy_string_len(ds->key, CONST_STR_LEN("realm")); 967bcdc6a3bSJan Kneschke buffer_copy_string(ds->value, realm); 968bcdc6a3bSJan Kneschke 969bcdc6a3bSJan Kneschke array_insert_unique(a->value, (data_unset *)ds); 970bcdc6a3bSJan Kneschke 971bcdc6a3bSJan Kneschke ds = data_string_init(); 972bcdc6a3bSJan Kneschke 97352861d77SStefan Bühler buffer_copy_string_len(ds->key, CONST_STR_LEN("require")); 974bcdc6a3bSJan Kneschke buffer_copy_string(ds->value, require); 975bcdc6a3bSJan Kneschke 976bcdc6a3bSJan Kneschke array_insert_unique(a->value, (data_unset *)ds); 977bcdc6a3bSJan Kneschke 978bcdc6a3bSJan Kneschke array_insert_unique(s->auth_require, (data_unset *)a); 979bcdc6a3bSJan Kneschke } 980bcdc6a3bSJan Kneschke } 981ea7b9abaSJan Kneschke } 982ea7b9abaSJan Kneschke 983ea7b9abaSJan Kneschke return HANDLER_GO_ON; 984ea7b9abaSJan Kneschke } 985ea7b9abaSJan Kneschke 98663f785a2SStefan Bühler int mod_auth_plugin_init(plugin *p); 987bcdc6a3bSJan Kneschke int mod_auth_plugin_init(plugin *p) { 988bcdc6a3bSJan Kneschke p->version = LIGHTTPD_VERSION_ID; 989bcdc6a3bSJan Kneschke p->name = buffer_init_string("auth"); 990bcdc6a3bSJan Kneschke p->init = mod_auth_init; 991bcdc6a3bSJan Kneschke p->set_defaults = mod_auth_set_defaults; 992bcdc6a3bSJan Kneschke p->handle_uri_clean = mod_auth_uri_handler; 993bcdc6a3bSJan Kneschke p->cleanup = mod_auth_free; 994bcdc6a3bSJan Kneschke 995bcdc6a3bSJan Kneschke p->data = NULL; 996bcdc6a3bSJan Kneschke 997bcdc6a3bSJan Kneschke return 0; 998bcdc6a3bSJan Kneschke } 999