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 8*81b2d1f0SGlenn Strauss #include "inet_ntop_cache.h" 9*81b2d1f0SGlenn Strauss #include "base64.h" 10*81b2d1f0SGlenn Strauss #include "md5.h" 11*81b2d1f0SGlenn 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 21ea7b9abaSJan Kneschke handler_t auth_ldap_init(server *srv, mod_auth_plugin_config *s); 22ea7b9abaSJan Kneschke 23bcdc6a3bSJan Kneschke 24bcdc6a3bSJan Kneschke /** 25bcdc6a3bSJan Kneschke * the basic and digest auth framework 26bcdc6a3bSJan Kneschke * 27bcdc6a3bSJan Kneschke * - config handling 28bcdc6a3bSJan Kneschke * - protocol handling 29bcdc6a3bSJan Kneschke * 30bcdc6a3bSJan Kneschke * http_auth.c 31bcdc6a3bSJan Kneschke * http_auth_digest.c 32bcdc6a3bSJan Kneschke * 33bcdc6a3bSJan Kneschke * do the real work 34bcdc6a3bSJan Kneschke */ 35bcdc6a3bSJan Kneschke 36bcdc6a3bSJan Kneschke INIT_FUNC(mod_auth_init) { 37bcdc6a3bSJan Kneschke mod_auth_plugin_data *p; 38bcdc6a3bSJan Kneschke 39bcdc6a3bSJan Kneschke p = calloc(1, sizeof(*p)); 40bcdc6a3bSJan Kneschke 41bcdc6a3bSJan Kneschke p->tmp_buf = buffer_init(); 42bcdc6a3bSJan Kneschke 43bcdc6a3bSJan Kneschke p->auth_user = buffer_init(); 44bcdc6a3bSJan Kneschke #ifdef USE_LDAP 45bcdc6a3bSJan Kneschke p->ldap_filter = buffer_init(); 46bcdc6a3bSJan Kneschke #endif 47bcdc6a3bSJan Kneschke 48bcdc6a3bSJan Kneschke return p; 49bcdc6a3bSJan Kneschke } 50bcdc6a3bSJan Kneschke 51bcdc6a3bSJan Kneschke FREE_FUNC(mod_auth_free) { 52bcdc6a3bSJan Kneschke mod_auth_plugin_data *p = p_d; 53bcdc6a3bSJan Kneschke 54bcdc6a3bSJan Kneschke UNUSED(srv); 55bcdc6a3bSJan Kneschke 56bcdc6a3bSJan Kneschke if (!p) return HANDLER_GO_ON; 57bcdc6a3bSJan Kneschke 58bcdc6a3bSJan Kneschke buffer_free(p->tmp_buf); 59bcdc6a3bSJan Kneschke buffer_free(p->auth_user); 60bcdc6a3bSJan Kneschke #ifdef USE_LDAP 61bcdc6a3bSJan Kneschke buffer_free(p->ldap_filter); 62bcdc6a3bSJan Kneschke #endif 63bcdc6a3bSJan Kneschke 64bcdc6a3bSJan Kneschke if (p->config_storage) { 65bcdc6a3bSJan Kneschke size_t i; 66bcdc6a3bSJan Kneschke for (i = 0; i < srv->config_context->used; i++) { 67bcdc6a3bSJan Kneschke mod_auth_plugin_config *s = p->config_storage[i]; 68bcdc6a3bSJan Kneschke 6933cebeb0SStefan Bühler if (NULL == s) continue; 704a6ce4aeSJan Kneschke 71bcdc6a3bSJan Kneschke array_free(s->auth_require); 72bcdc6a3bSJan Kneschke buffer_free(s->auth_plain_groupfile); 73bcdc6a3bSJan Kneschke buffer_free(s->auth_plain_userfile); 74bcdc6a3bSJan Kneschke buffer_free(s->auth_htdigest_userfile); 75bcdc6a3bSJan Kneschke buffer_free(s->auth_htpasswd_userfile); 76bcdc6a3bSJan Kneschke buffer_free(s->auth_backend_conf); 77bcdc6a3bSJan Kneschke 78bcdc6a3bSJan Kneschke buffer_free(s->auth_ldap_hostname); 79bcdc6a3bSJan Kneschke buffer_free(s->auth_ldap_basedn); 809238cad6SJan Kneschke buffer_free(s->auth_ldap_binddn); 819238cad6SJan Kneschke buffer_free(s->auth_ldap_bindpw); 82bcdc6a3bSJan Kneschke buffer_free(s->auth_ldap_filter); 834a6ce4aeSJan Kneschke buffer_free(s->auth_ldap_cafile); 84bcdc6a3bSJan Kneschke 85bcdc6a3bSJan Kneschke #ifdef USE_LDAP 86bcdc6a3bSJan Kneschke buffer_free(s->ldap_filter_pre); 87bcdc6a3bSJan Kneschke buffer_free(s->ldap_filter_post); 88bcdc6a3bSJan Kneschke 89bcdc6a3bSJan Kneschke if (s->ldap) ldap_unbind_s(s->ldap); 90bcdc6a3bSJan Kneschke #endif 91bcdc6a3bSJan Kneschke 92bcdc6a3bSJan Kneschke free(s); 93bcdc6a3bSJan Kneschke } 94bcdc6a3bSJan Kneschke free(p->config_storage); 95bcdc6a3bSJan Kneschke } 96bcdc6a3bSJan Kneschke 97bcdc6a3bSJan Kneschke free(p); 98bcdc6a3bSJan Kneschke 99bcdc6a3bSJan Kneschke return HANDLER_GO_ON; 100bcdc6a3bSJan Kneschke } 101bcdc6a3bSJan Kneschke 102bcdc6a3bSJan Kneschke #define PATCH(x) \ 103bcdc6a3bSJan Kneschke p->conf.x = s->x; 1044a6ce4aeSJan Kneschke static int mod_auth_patch_connection(server *srv, connection *con, mod_auth_plugin_data *p) { 105bcdc6a3bSJan Kneschke size_t i, j; 1064a6ce4aeSJan Kneschke mod_auth_plugin_config *s = p->config_storage[0]; 1074a6ce4aeSJan Kneschke 1084a6ce4aeSJan Kneschke PATCH(auth_backend); 1094a6ce4aeSJan Kneschke PATCH(auth_plain_groupfile); 1104a6ce4aeSJan Kneschke PATCH(auth_plain_userfile); 1114a6ce4aeSJan Kneschke PATCH(auth_htdigest_userfile); 1124a6ce4aeSJan Kneschke PATCH(auth_htpasswd_userfile); 1134a6ce4aeSJan Kneschke PATCH(auth_require); 1144a6ce4aeSJan Kneschke PATCH(auth_debug); 1154a6ce4aeSJan Kneschke PATCH(auth_ldap_hostname); 1164a6ce4aeSJan Kneschke PATCH(auth_ldap_basedn); 1179238cad6SJan Kneschke PATCH(auth_ldap_binddn); 1189238cad6SJan Kneschke PATCH(auth_ldap_bindpw); 1194a6ce4aeSJan Kneschke PATCH(auth_ldap_filter); 1204a6ce4aeSJan Kneschke PATCH(auth_ldap_cafile); 1214a6ce4aeSJan Kneschke PATCH(auth_ldap_starttls); 122d471bcc9SMarcus Rückert PATCH(auth_ldap_allow_empty_pw); 1234a6ce4aeSJan Kneschke #ifdef USE_LDAP 124cfba07cbSStefan Bühler p->anon_conf = s; 1254a6ce4aeSJan Kneschke PATCH(ldap_filter_pre); 1264a6ce4aeSJan Kneschke PATCH(ldap_filter_post); 1274a6ce4aeSJan Kneschke #endif 128bcdc6a3bSJan Kneschke 129bcdc6a3bSJan Kneschke /* skip the first, the global context */ 130bcdc6a3bSJan Kneschke for (i = 1; i < srv->config_context->used; i++) { 131bcdc6a3bSJan Kneschke data_config *dc = (data_config *)srv->config_context->data[i]; 1324a6ce4aeSJan Kneschke s = p->config_storage[i]; 133bcdc6a3bSJan Kneschke 134bcdc6a3bSJan Kneschke /* condition didn't match */ 135bcdc6a3bSJan Kneschke if (!config_check_cond(srv, con, dc)) continue; 136bcdc6a3bSJan Kneschke 137bcdc6a3bSJan Kneschke /* merge config */ 138bcdc6a3bSJan Kneschke for (j = 0; j < dc->value->used; j++) { 139bcdc6a3bSJan Kneschke data_unset *du = dc->value->data[j]; 140bcdc6a3bSJan Kneschke 141bcdc6a3bSJan Kneschke if (buffer_is_equal_string(du->key, CONST_STR_LEN("auth.backend"))) { 142bcdc6a3bSJan Kneschke PATCH(auth_backend); 143bcdc6a3bSJan Kneschke } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("auth.backend.plain.groupfile"))) { 144bcdc6a3bSJan Kneschke PATCH(auth_plain_groupfile); 145bcdc6a3bSJan Kneschke } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("auth.backend.plain.userfile"))) { 146bcdc6a3bSJan Kneschke PATCH(auth_plain_userfile); 147bcdc6a3bSJan Kneschke } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("auth.backend.htdigest.userfile"))) { 148bcdc6a3bSJan Kneschke PATCH(auth_htdigest_userfile); 149bcdc6a3bSJan Kneschke } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("auth.backend.htpasswd.userfile"))) { 150bcdc6a3bSJan Kneschke PATCH(auth_htpasswd_userfile); 151bcdc6a3bSJan Kneschke } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("auth.require"))) { 152bcdc6a3bSJan Kneschke PATCH(auth_require); 153bcdc6a3bSJan Kneschke } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("auth.debug"))) { 154bcdc6a3bSJan Kneschke PATCH(auth_debug); 155bcdc6a3bSJan Kneschke } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("auth.backend.ldap.hostname"))) { 156bcdc6a3bSJan Kneschke PATCH(auth_ldap_hostname); 157bcdc6a3bSJan Kneschke #ifdef USE_LDAP 158cfba07cbSStefan Bühler p->anon_conf = s; 159bcdc6a3bSJan Kneschke #endif 160bcdc6a3bSJan Kneschke } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("auth.backend.ldap.base-dn"))) { 161bcdc6a3bSJan Kneschke PATCH(auth_ldap_basedn); 162bcdc6a3bSJan Kneschke } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("auth.backend.ldap.filter"))) { 163bcdc6a3bSJan Kneschke PATCH(auth_ldap_filter); 164ba715e1cSStefan Bühler #ifdef USE_LDAP 165ba715e1cSStefan Bühler PATCH(ldap_filter_pre); 166ba715e1cSStefan Bühler PATCH(ldap_filter_post); 167ba715e1cSStefan Bühler #endif 1684a6ce4aeSJan Kneschke } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("auth.backend.ldap.ca-file"))) { 1694a6ce4aeSJan Kneschke PATCH(auth_ldap_cafile); 1704a6ce4aeSJan Kneschke } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("auth.backend.ldap.starttls"))) { 1714a6ce4aeSJan Kneschke PATCH(auth_ldap_starttls); 17286766c10SStefan Bühler } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("auth.backend.ldap.bind-dn"))) { 17386766c10SStefan Bühler PATCH(auth_ldap_binddn); 17486766c10SStefan Bühler } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("auth.backend.ldap.bind-pw"))) { 17586766c10SStefan Bühler PATCH(auth_ldap_bindpw); 176d471bcc9SMarcus Rückert } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("auth.backend.ldap.allow-empty-pw"))) { 177d471bcc9SMarcus Rückert PATCH(auth_ldap_allow_empty_pw); 178bcdc6a3bSJan Kneschke } 179bcdc6a3bSJan Kneschke } 180bcdc6a3bSJan Kneschke } 181bcdc6a3bSJan Kneschke 182bcdc6a3bSJan Kneschke return 0; 183bcdc6a3bSJan Kneschke } 184bcdc6a3bSJan Kneschke #undef PATCH 185bcdc6a3bSJan Kneschke 186*81b2d1f0SGlenn Strauss static int mod_auth_match_rules(server *srv, array *req, const char *username, const char *group, const char *host) { 187*81b2d1f0SGlenn Strauss const char *r = NULL, *rules = NULL; 188*81b2d1f0SGlenn Strauss int username_len; 189*81b2d1f0SGlenn Strauss data_string *require; 190*81b2d1f0SGlenn Strauss 191*81b2d1f0SGlenn Strauss UNUSED(group); 192*81b2d1f0SGlenn Strauss UNUSED(host); 193*81b2d1f0SGlenn Strauss 194*81b2d1f0SGlenn Strauss require = (data_string *)array_get_element(req, "require"); 195*81b2d1f0SGlenn Strauss if (!require) return -1; /*(should not happen; config is validated at startup)*/ 196*81b2d1f0SGlenn Strauss 197*81b2d1f0SGlenn Strauss /* if we get here, the user we got a authed user */ 198*81b2d1f0SGlenn Strauss if (0 == strcmp(require->value->ptr, "valid-user")) { 199*81b2d1f0SGlenn Strauss return 0; 200*81b2d1f0SGlenn Strauss } 201*81b2d1f0SGlenn Strauss 202*81b2d1f0SGlenn Strauss /* user=name1|group=name3|host=name4 */ 203*81b2d1f0SGlenn Strauss 204*81b2d1f0SGlenn Strauss /* seperate the string by | */ 205*81b2d1f0SGlenn Strauss #if 0 206*81b2d1f0SGlenn Strauss log_error_write(srv, __FILE__, __LINE__, "sb", "rules", require->value); 207*81b2d1f0SGlenn Strauss #endif 208*81b2d1f0SGlenn Strauss 209*81b2d1f0SGlenn Strauss username_len = username ? strlen(username) : 0; 210*81b2d1f0SGlenn Strauss 211*81b2d1f0SGlenn Strauss r = rules = require->value->ptr; 212*81b2d1f0SGlenn Strauss 213*81b2d1f0SGlenn Strauss while (1) { 214*81b2d1f0SGlenn Strauss const char *eq; 215*81b2d1f0SGlenn Strauss const char *k, *v, *e; 216*81b2d1f0SGlenn Strauss int k_len, v_len, r_len; 217*81b2d1f0SGlenn Strauss 218*81b2d1f0SGlenn Strauss e = strchr(r, '|'); 219*81b2d1f0SGlenn Strauss 220*81b2d1f0SGlenn Strauss if (e) { 221*81b2d1f0SGlenn Strauss r_len = e - r; 222*81b2d1f0SGlenn Strauss } else { 223*81b2d1f0SGlenn Strauss r_len = strlen(rules) - (r - rules); 224*81b2d1f0SGlenn Strauss } 225*81b2d1f0SGlenn Strauss 226*81b2d1f0SGlenn Strauss /* from r to r + r_len is a rule */ 227*81b2d1f0SGlenn Strauss 228*81b2d1f0SGlenn Strauss if (0 == strncmp(r, "valid-user", r_len)) { 229*81b2d1f0SGlenn Strauss log_error_write(srv, __FILE__, __LINE__, "sb", 230*81b2d1f0SGlenn Strauss "parsing the 'require' section in 'auth.require' failed: valid-user cannot be combined with other require rules", 231*81b2d1f0SGlenn Strauss require->value); 232*81b2d1f0SGlenn Strauss return -1; 233*81b2d1f0SGlenn Strauss } 234*81b2d1f0SGlenn Strauss 235*81b2d1f0SGlenn Strauss /* search for = in the rules */ 236*81b2d1f0SGlenn Strauss if (NULL == (eq = strchr(r, '='))) { 237*81b2d1f0SGlenn Strauss log_error_write(srv, __FILE__, __LINE__, "sb", 238*81b2d1f0SGlenn Strauss "parsing the 'require' section in 'auth.require' failed: a = is missing", 239*81b2d1f0SGlenn Strauss require->value); 240*81b2d1f0SGlenn Strauss return -1; 241*81b2d1f0SGlenn Strauss } 242*81b2d1f0SGlenn Strauss 243*81b2d1f0SGlenn Strauss /* = out of range */ 244*81b2d1f0SGlenn Strauss if (eq > r + r_len) { 245*81b2d1f0SGlenn Strauss log_error_write(srv, __FILE__, __LINE__, "sb", 246*81b2d1f0SGlenn Strauss "parsing the 'require' section in 'auth.require' failed: = out of range", 247*81b2d1f0SGlenn Strauss require->value); 248*81b2d1f0SGlenn Strauss 249*81b2d1f0SGlenn Strauss return -1; 250*81b2d1f0SGlenn Strauss } 251*81b2d1f0SGlenn Strauss 252*81b2d1f0SGlenn Strauss /* the part before the = is user|group|host */ 253*81b2d1f0SGlenn Strauss 254*81b2d1f0SGlenn Strauss k = r; 255*81b2d1f0SGlenn Strauss k_len = eq - r; 256*81b2d1f0SGlenn Strauss v = eq + 1; 257*81b2d1f0SGlenn Strauss v_len = r_len - k_len - 1; 258*81b2d1f0SGlenn Strauss 259*81b2d1f0SGlenn Strauss if (k_len == 4) { 260*81b2d1f0SGlenn Strauss if (0 == strncmp(k, "user", k_len)) { 261*81b2d1f0SGlenn Strauss if (username && 262*81b2d1f0SGlenn Strauss username_len == v_len && 263*81b2d1f0SGlenn Strauss 0 == strncmp(username, v, v_len)) { 264*81b2d1f0SGlenn Strauss return 0; 265*81b2d1f0SGlenn Strauss } 266*81b2d1f0SGlenn Strauss } else if (0 == strncmp(k, "host", k_len)) { 267*81b2d1f0SGlenn Strauss log_error_write(srv, __FILE__, __LINE__, "s", "host ... (not implemented)"); 268*81b2d1f0SGlenn Strauss } else { 269*81b2d1f0SGlenn Strauss log_error_write(srv, __FILE__, __LINE__, "s", "unknown key"); 270*81b2d1f0SGlenn Strauss return -1; 271*81b2d1f0SGlenn Strauss } 272*81b2d1f0SGlenn Strauss } else if (k_len == 5) { 273*81b2d1f0SGlenn Strauss if (0 == strncmp(k, "group", k_len)) { 274*81b2d1f0SGlenn Strauss log_error_write(srv, __FILE__, __LINE__, "s", "group ... (not implemented)"); 275*81b2d1f0SGlenn Strauss } else { 276*81b2d1f0SGlenn Strauss log_error_write(srv, __FILE__, __LINE__, "ss", "unknown key", k); 277*81b2d1f0SGlenn Strauss return -1; 278*81b2d1f0SGlenn Strauss } 279*81b2d1f0SGlenn Strauss } else { 280*81b2d1f0SGlenn Strauss log_error_write(srv, __FILE__, __LINE__, "s", "unknown key"); 281*81b2d1f0SGlenn Strauss return -1; 282*81b2d1f0SGlenn Strauss } 283*81b2d1f0SGlenn Strauss 284*81b2d1f0SGlenn Strauss if (!e) break; 285*81b2d1f0SGlenn Strauss r = e + 1; 286*81b2d1f0SGlenn Strauss } 287*81b2d1f0SGlenn Strauss 288*81b2d1f0SGlenn Strauss log_error_write(srv, __FILE__, __LINE__, "s", "nothing matched"); 289*81b2d1f0SGlenn Strauss 290*81b2d1f0SGlenn Strauss return -1; 291*81b2d1f0SGlenn Strauss } 292*81b2d1f0SGlenn Strauss 293*81b2d1f0SGlenn Strauss static int mod_auth_basic_check(server *srv, connection *con, mod_auth_plugin_data *p, array *req, const char *realm_str) { 294*81b2d1f0SGlenn Strauss buffer *username; 295*81b2d1f0SGlenn Strauss char *pw; 296*81b2d1f0SGlenn Strauss 297*81b2d1f0SGlenn Strauss data_string *realm; 298*81b2d1f0SGlenn Strauss 299*81b2d1f0SGlenn Strauss realm = (data_string *)array_get_element(req, "realm"); 300*81b2d1f0SGlenn Strauss if (!realm) return 0; /*(should not happen; config is validated at startup)*/ 301*81b2d1f0SGlenn Strauss 302*81b2d1f0SGlenn Strauss username = buffer_init(); 303*81b2d1f0SGlenn Strauss 304*81b2d1f0SGlenn Strauss if (!buffer_append_base64_decode(username, realm_str, strlen(realm_str), BASE64_STANDARD)) { 305*81b2d1f0SGlenn Strauss log_error_write(srv, __FILE__, __LINE__, "sb", "decodeing base64-string failed", username); 306*81b2d1f0SGlenn Strauss 307*81b2d1f0SGlenn Strauss buffer_free(username); 308*81b2d1f0SGlenn Strauss return 0; 309*81b2d1f0SGlenn Strauss } 310*81b2d1f0SGlenn Strauss 311*81b2d1f0SGlenn Strauss /* r2 == user:password */ 312*81b2d1f0SGlenn Strauss if (NULL == (pw = strchr(username->ptr, ':'))) { 313*81b2d1f0SGlenn Strauss log_error_write(srv, __FILE__, __LINE__, "sb", ": is missing in", username); 314*81b2d1f0SGlenn Strauss 315*81b2d1f0SGlenn Strauss buffer_free(username); 316*81b2d1f0SGlenn Strauss return 0; 317*81b2d1f0SGlenn Strauss } 318*81b2d1f0SGlenn Strauss 319*81b2d1f0SGlenn Strauss buffer_string_set_length(username, pw - username->ptr); 320*81b2d1f0SGlenn Strauss pw++; 321*81b2d1f0SGlenn Strauss 322*81b2d1f0SGlenn Strauss /* password doesn't match */ 323*81b2d1f0SGlenn Strauss if (http_auth_basic_password_compare(srv, p, username, realm->value, pw)) { 324*81b2d1f0SGlenn 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))); 325*81b2d1f0SGlenn Strauss 326*81b2d1f0SGlenn Strauss buffer_free(username); 327*81b2d1f0SGlenn Strauss 328*81b2d1f0SGlenn Strauss return 0; 329*81b2d1f0SGlenn Strauss } 330*81b2d1f0SGlenn Strauss 331*81b2d1f0SGlenn Strauss /* value is our allow-rules */ 332*81b2d1f0SGlenn Strauss if (mod_auth_match_rules(srv, req, username->ptr, NULL, NULL)) { 333*81b2d1f0SGlenn Strauss buffer_free(username); 334*81b2d1f0SGlenn Strauss 335*81b2d1f0SGlenn Strauss log_error_write(srv, __FILE__, __LINE__, "s", "rules didn't match"); 336*81b2d1f0SGlenn Strauss 337*81b2d1f0SGlenn Strauss return 0; 338*81b2d1f0SGlenn Strauss } 339*81b2d1f0SGlenn Strauss 340*81b2d1f0SGlenn Strauss /* remember the username */ 341*81b2d1f0SGlenn Strauss buffer_copy_buffer(p->auth_user, username); 342*81b2d1f0SGlenn Strauss 343*81b2d1f0SGlenn Strauss buffer_free(username); 344*81b2d1f0SGlenn Strauss 345*81b2d1f0SGlenn Strauss return 1; 346*81b2d1f0SGlenn Strauss } 347*81b2d1f0SGlenn Strauss 348*81b2d1f0SGlenn Strauss #define HASHLEN 16 349*81b2d1f0SGlenn Strauss #define HASHHEXLEN 32 350*81b2d1f0SGlenn Strauss typedef unsigned char HASH[HASHLEN]; 351*81b2d1f0SGlenn Strauss typedef char HASHHEX[HASHHEXLEN+1]; 352*81b2d1f0SGlenn Strauss 353*81b2d1f0SGlenn Strauss static void CvtHex(const HASH Bin, char (*Hex)[33]) { 354*81b2d1f0SGlenn Strauss li_tohex(*Hex, sizeof(*Hex), (const char*) Bin, 16); 355*81b2d1f0SGlenn Strauss } 356*81b2d1f0SGlenn Strauss 357*81b2d1f0SGlenn Strauss typedef struct { 358*81b2d1f0SGlenn Strauss const char *key; 359*81b2d1f0SGlenn Strauss int key_len; 360*81b2d1f0SGlenn Strauss char **ptr; 361*81b2d1f0SGlenn Strauss } digest_kv; 362*81b2d1f0SGlenn Strauss 363*81b2d1f0SGlenn Strauss /* return values: -1: error/bad request, 0: failed, 1: success */ 364*81b2d1f0SGlenn Strauss static int mod_auth_digest_check(server *srv, connection *con, mod_auth_plugin_data *p, array *req, const char *realm_str) { 365*81b2d1f0SGlenn Strauss char a1[33]; 366*81b2d1f0SGlenn Strauss char a2[33]; 367*81b2d1f0SGlenn Strauss 368*81b2d1f0SGlenn Strauss char *username = NULL; 369*81b2d1f0SGlenn Strauss char *realm = NULL; 370*81b2d1f0SGlenn Strauss char *nonce = NULL; 371*81b2d1f0SGlenn Strauss char *uri = NULL; 372*81b2d1f0SGlenn Strauss char *algorithm = NULL; 373*81b2d1f0SGlenn Strauss char *qop = NULL; 374*81b2d1f0SGlenn Strauss char *cnonce = NULL; 375*81b2d1f0SGlenn Strauss char *nc = NULL; 376*81b2d1f0SGlenn Strauss char *respons = NULL; 377*81b2d1f0SGlenn Strauss 378*81b2d1f0SGlenn Strauss char *e, *c; 379*81b2d1f0SGlenn Strauss const char *m = NULL; 380*81b2d1f0SGlenn Strauss int i; 381*81b2d1f0SGlenn Strauss buffer *b; 382*81b2d1f0SGlenn Strauss 383*81b2d1f0SGlenn Strauss li_MD5_CTX Md5Ctx; 384*81b2d1f0SGlenn Strauss HASH HA1; 385*81b2d1f0SGlenn Strauss HASH HA2; 386*81b2d1f0SGlenn Strauss HASH RespHash; 387*81b2d1f0SGlenn Strauss HASHHEX HA2Hex; 388*81b2d1f0SGlenn Strauss 389*81b2d1f0SGlenn Strauss 390*81b2d1f0SGlenn Strauss /* init pointers */ 391*81b2d1f0SGlenn Strauss #define S(x) \ 392*81b2d1f0SGlenn Strauss x, sizeof(x)-1, NULL 393*81b2d1f0SGlenn Strauss digest_kv dkv[10] = { 394*81b2d1f0SGlenn Strauss { S("username=") }, 395*81b2d1f0SGlenn Strauss { S("realm=") }, 396*81b2d1f0SGlenn Strauss { S("nonce=") }, 397*81b2d1f0SGlenn Strauss { S("uri=") }, 398*81b2d1f0SGlenn Strauss { S("algorithm=") }, 399*81b2d1f0SGlenn Strauss { S("qop=") }, 400*81b2d1f0SGlenn Strauss { S("cnonce=") }, 401*81b2d1f0SGlenn Strauss { S("nc=") }, 402*81b2d1f0SGlenn Strauss { S("response=") }, 403*81b2d1f0SGlenn Strauss 404*81b2d1f0SGlenn Strauss { NULL, 0, NULL } 405*81b2d1f0SGlenn Strauss }; 406*81b2d1f0SGlenn Strauss #undef S 407*81b2d1f0SGlenn Strauss 408*81b2d1f0SGlenn Strauss dkv[0].ptr = &username; 409*81b2d1f0SGlenn Strauss dkv[1].ptr = &realm; 410*81b2d1f0SGlenn Strauss dkv[2].ptr = &nonce; 411*81b2d1f0SGlenn Strauss dkv[3].ptr = &uri; 412*81b2d1f0SGlenn Strauss dkv[4].ptr = &algorithm; 413*81b2d1f0SGlenn Strauss dkv[5].ptr = &qop; 414*81b2d1f0SGlenn Strauss dkv[6].ptr = &cnonce; 415*81b2d1f0SGlenn Strauss dkv[7].ptr = &nc; 416*81b2d1f0SGlenn Strauss dkv[8].ptr = &respons; 417*81b2d1f0SGlenn Strauss 418*81b2d1f0SGlenn Strauss b = buffer_init_string(realm_str); 419*81b2d1f0SGlenn Strauss 420*81b2d1f0SGlenn Strauss /* parse credentials from client */ 421*81b2d1f0SGlenn Strauss for (c = b->ptr; *c; c++) { 422*81b2d1f0SGlenn Strauss /* skip whitespaces */ 423*81b2d1f0SGlenn Strauss while (*c == ' ' || *c == '\t') c++; 424*81b2d1f0SGlenn Strauss if (!*c) break; 425*81b2d1f0SGlenn Strauss 426*81b2d1f0SGlenn Strauss for (i = 0; dkv[i].key; i++) { 427*81b2d1f0SGlenn Strauss if ((0 == strncmp(c, dkv[i].key, dkv[i].key_len))) { 428*81b2d1f0SGlenn Strauss if ((c[dkv[i].key_len] == '"') && 429*81b2d1f0SGlenn Strauss (NULL != (e = strchr(c + dkv[i].key_len + 1, '"')))) { 430*81b2d1f0SGlenn Strauss /* value with "..." */ 431*81b2d1f0SGlenn Strauss *(dkv[i].ptr) = c + dkv[i].key_len + 1; 432*81b2d1f0SGlenn Strauss c = e; 433*81b2d1f0SGlenn Strauss 434*81b2d1f0SGlenn Strauss *e = '\0'; 435*81b2d1f0SGlenn Strauss } else if (NULL != (e = strchr(c + dkv[i].key_len, ','))) { 436*81b2d1f0SGlenn Strauss /* value without "...", terminated by ',' */ 437*81b2d1f0SGlenn Strauss *(dkv[i].ptr) = c + dkv[i].key_len; 438*81b2d1f0SGlenn Strauss c = e; 439*81b2d1f0SGlenn Strauss 440*81b2d1f0SGlenn Strauss *e = '\0'; 441*81b2d1f0SGlenn Strauss } else { 442*81b2d1f0SGlenn Strauss /* value without "...", terminated by EOL */ 443*81b2d1f0SGlenn Strauss *(dkv[i].ptr) = c + dkv[i].key_len; 444*81b2d1f0SGlenn Strauss c += strlen(c) - 1; 445*81b2d1f0SGlenn Strauss } 446*81b2d1f0SGlenn Strauss break; 447*81b2d1f0SGlenn Strauss } 448*81b2d1f0SGlenn Strauss } 449*81b2d1f0SGlenn Strauss } 450*81b2d1f0SGlenn Strauss 451*81b2d1f0SGlenn Strauss if (p->conf.auth_debug > 1) { 452*81b2d1f0SGlenn Strauss log_error_write(srv, __FILE__, __LINE__, "ss", "username", username); 453*81b2d1f0SGlenn Strauss log_error_write(srv, __FILE__, __LINE__, "ss", "realm", realm); 454*81b2d1f0SGlenn Strauss log_error_write(srv, __FILE__, __LINE__, "ss", "nonce", nonce); 455*81b2d1f0SGlenn Strauss log_error_write(srv, __FILE__, __LINE__, "ss", "uri", uri); 456*81b2d1f0SGlenn Strauss log_error_write(srv, __FILE__, __LINE__, "ss", "algorithm", algorithm); 457*81b2d1f0SGlenn Strauss log_error_write(srv, __FILE__, __LINE__, "ss", "qop", qop); 458*81b2d1f0SGlenn Strauss log_error_write(srv, __FILE__, __LINE__, "ss", "cnonce", cnonce); 459*81b2d1f0SGlenn Strauss log_error_write(srv, __FILE__, __LINE__, "ss", "nc", nc); 460*81b2d1f0SGlenn Strauss log_error_write(srv, __FILE__, __LINE__, "ss", "response", respons); 461*81b2d1f0SGlenn Strauss } 462*81b2d1f0SGlenn Strauss 463*81b2d1f0SGlenn Strauss /* check if everything is transmitted */ 464*81b2d1f0SGlenn Strauss if (!username || 465*81b2d1f0SGlenn Strauss !realm || 466*81b2d1f0SGlenn Strauss !nonce || 467*81b2d1f0SGlenn Strauss !uri || 468*81b2d1f0SGlenn Strauss (qop && (!nc || !cnonce)) || 469*81b2d1f0SGlenn Strauss !respons ) { 470*81b2d1f0SGlenn Strauss /* missing field */ 471*81b2d1f0SGlenn Strauss 472*81b2d1f0SGlenn Strauss log_error_write(srv, __FILE__, __LINE__, "s", 473*81b2d1f0SGlenn Strauss "digest: missing field"); 474*81b2d1f0SGlenn Strauss 475*81b2d1f0SGlenn Strauss buffer_free(b); 476*81b2d1f0SGlenn Strauss return -1; 477*81b2d1f0SGlenn Strauss } 478*81b2d1f0SGlenn Strauss 479*81b2d1f0SGlenn Strauss /** 480*81b2d1f0SGlenn Strauss * protect the md5-sess against missing cnonce and nonce 481*81b2d1f0SGlenn Strauss */ 482*81b2d1f0SGlenn Strauss if (algorithm && 483*81b2d1f0SGlenn Strauss 0 == strcasecmp(algorithm, "md5-sess") && 484*81b2d1f0SGlenn Strauss (!nonce || !cnonce)) { 485*81b2d1f0SGlenn Strauss log_error_write(srv, __FILE__, __LINE__, "s", 486*81b2d1f0SGlenn Strauss "digest: (md5-sess: missing field"); 487*81b2d1f0SGlenn Strauss 488*81b2d1f0SGlenn Strauss buffer_free(b); 489*81b2d1f0SGlenn Strauss return -1; 490*81b2d1f0SGlenn Strauss } 491*81b2d1f0SGlenn Strauss 492*81b2d1f0SGlenn Strauss if (qop && strcasecmp(qop, "auth-int") == 0) { 493*81b2d1f0SGlenn Strauss log_error_write(srv, __FILE__, __LINE__, "s", 494*81b2d1f0SGlenn Strauss "digest: qop=auth-int not supported"); 495*81b2d1f0SGlenn Strauss 496*81b2d1f0SGlenn Strauss buffer_free(b); 497*81b2d1f0SGlenn Strauss return -1; 498*81b2d1f0SGlenn Strauss } 499*81b2d1f0SGlenn Strauss 500*81b2d1f0SGlenn Strauss m = get_http_method_name(con->request.http_method); 501*81b2d1f0SGlenn Strauss force_assert(m); 502*81b2d1f0SGlenn Strauss 503*81b2d1f0SGlenn Strauss /* detect if attacker is attempting to reuse valid digest for one uri 504*81b2d1f0SGlenn Strauss * on a different request uri. Might also happen if intermediate proxy 505*81b2d1f0SGlenn Strauss * altered client request line. (Altered request would not result in 506*81b2d1f0SGlenn Strauss * the same digest as that calculated by the client.) 507*81b2d1f0SGlenn Strauss * Internal redirects such as with mod_rewrite will modify request uri. 508*81b2d1f0SGlenn Strauss * Reauthentication is done to detect crossing auth realms, but this 509*81b2d1f0SGlenn Strauss * uri validation step is bypassed. con->request.orig_uri is original 510*81b2d1f0SGlenn Strauss * uri sent in client request. */ 511*81b2d1f0SGlenn Strauss { 512*81b2d1f0SGlenn Strauss const size_t ulen = strlen(uri); 513*81b2d1f0SGlenn Strauss const size_t rlen = buffer_string_length(con->request.orig_uri); 514*81b2d1f0SGlenn Strauss if (!buffer_is_equal_string(con->request.orig_uri, uri, ulen) 515*81b2d1f0SGlenn Strauss && !(rlen < ulen && 0 == memcmp(con->request.orig_uri->ptr, uri, rlen) && uri[rlen] == '?')) { 516*81b2d1f0SGlenn Strauss log_error_write(srv, __FILE__, __LINE__, "sbssss", 517*81b2d1f0SGlenn Strauss "digest: auth failed: uri mismatch (", con->request.orig_uri, "!=", uri, "), IP:", inet_ntop_cache_get_ip(srv, &(con->dst_addr))); 518*81b2d1f0SGlenn Strauss buffer_free(b); 519*81b2d1f0SGlenn Strauss return -1; 520*81b2d1f0SGlenn Strauss } 521*81b2d1f0SGlenn Strauss } 522*81b2d1f0SGlenn Strauss 523*81b2d1f0SGlenn Strauss /* password-string == HA1 */ 524*81b2d1f0SGlenn Strauss i = http_auth_get_password_digest(srv, p, username, realm, HA1); 525*81b2d1f0SGlenn Strauss if (1 != i) { 526*81b2d1f0SGlenn Strauss buffer_free(b); 527*81b2d1f0SGlenn Strauss return i; 528*81b2d1f0SGlenn Strauss } 529*81b2d1f0SGlenn Strauss 530*81b2d1f0SGlenn Strauss if (algorithm && 531*81b2d1f0SGlenn Strauss strcasecmp(algorithm, "md5-sess") == 0) { 532*81b2d1f0SGlenn Strauss li_MD5_Init(&Md5Ctx); 533*81b2d1f0SGlenn Strauss /* Errata ID 1649: http://www.rfc-editor.org/errata_search.php?rfc=2617 */ 534*81b2d1f0SGlenn Strauss CvtHex(HA1, &a1); 535*81b2d1f0SGlenn Strauss li_MD5_Update(&Md5Ctx, (unsigned char *)a1, HASHHEXLEN); 536*81b2d1f0SGlenn Strauss li_MD5_Update(&Md5Ctx, CONST_STR_LEN(":")); 537*81b2d1f0SGlenn Strauss li_MD5_Update(&Md5Ctx, (unsigned char *)nonce, strlen(nonce)); 538*81b2d1f0SGlenn Strauss li_MD5_Update(&Md5Ctx, CONST_STR_LEN(":")); 539*81b2d1f0SGlenn Strauss li_MD5_Update(&Md5Ctx, (unsigned char *)cnonce, strlen(cnonce)); 540*81b2d1f0SGlenn Strauss li_MD5_Final(HA1, &Md5Ctx); 541*81b2d1f0SGlenn Strauss } 542*81b2d1f0SGlenn Strauss 543*81b2d1f0SGlenn Strauss CvtHex(HA1, &a1); 544*81b2d1f0SGlenn Strauss 545*81b2d1f0SGlenn Strauss /* calculate H(A2) */ 546*81b2d1f0SGlenn Strauss li_MD5_Init(&Md5Ctx); 547*81b2d1f0SGlenn Strauss li_MD5_Update(&Md5Ctx, (unsigned char *)m, strlen(m)); 548*81b2d1f0SGlenn Strauss li_MD5_Update(&Md5Ctx, CONST_STR_LEN(":")); 549*81b2d1f0SGlenn Strauss li_MD5_Update(&Md5Ctx, (unsigned char *)uri, strlen(uri)); 550*81b2d1f0SGlenn Strauss /* qop=auth-int not supported, already checked above */ 551*81b2d1f0SGlenn Strauss /* 552*81b2d1f0SGlenn Strauss if (qop && strcasecmp(qop, "auth-int") == 0) { 553*81b2d1f0SGlenn Strauss li_MD5_Update(&Md5Ctx, CONST_STR_LEN(":")); 554*81b2d1f0SGlenn Strauss li_MD5_Update(&Md5Ctx, (unsigned char *) [body checksum], HASHHEXLEN); 555*81b2d1f0SGlenn Strauss } 556*81b2d1f0SGlenn Strauss */ 557*81b2d1f0SGlenn Strauss li_MD5_Final(HA2, &Md5Ctx); 558*81b2d1f0SGlenn Strauss CvtHex(HA2, &HA2Hex); 559*81b2d1f0SGlenn Strauss 560*81b2d1f0SGlenn Strauss /* calculate response */ 561*81b2d1f0SGlenn Strauss li_MD5_Init(&Md5Ctx); 562*81b2d1f0SGlenn Strauss li_MD5_Update(&Md5Ctx, (unsigned char *)a1, HASHHEXLEN); 563*81b2d1f0SGlenn Strauss li_MD5_Update(&Md5Ctx, CONST_STR_LEN(":")); 564*81b2d1f0SGlenn Strauss li_MD5_Update(&Md5Ctx, (unsigned char *)nonce, strlen(nonce)); 565*81b2d1f0SGlenn Strauss li_MD5_Update(&Md5Ctx, CONST_STR_LEN(":")); 566*81b2d1f0SGlenn Strauss if (qop && *qop) { 567*81b2d1f0SGlenn Strauss li_MD5_Update(&Md5Ctx, (unsigned char *)nc, strlen(nc)); 568*81b2d1f0SGlenn Strauss li_MD5_Update(&Md5Ctx, CONST_STR_LEN(":")); 569*81b2d1f0SGlenn Strauss li_MD5_Update(&Md5Ctx, (unsigned char *)cnonce, strlen(cnonce)); 570*81b2d1f0SGlenn Strauss li_MD5_Update(&Md5Ctx, CONST_STR_LEN(":")); 571*81b2d1f0SGlenn Strauss li_MD5_Update(&Md5Ctx, (unsigned char *)qop, strlen(qop)); 572*81b2d1f0SGlenn Strauss li_MD5_Update(&Md5Ctx, CONST_STR_LEN(":")); 573*81b2d1f0SGlenn Strauss }; 574*81b2d1f0SGlenn Strauss li_MD5_Update(&Md5Ctx, (unsigned char *)HA2Hex, HASHHEXLEN); 575*81b2d1f0SGlenn Strauss li_MD5_Final(RespHash, &Md5Ctx); 576*81b2d1f0SGlenn Strauss CvtHex(RespHash, &a2); 577*81b2d1f0SGlenn Strauss 578*81b2d1f0SGlenn Strauss if (0 != strcmp(a2, respons)) { 579*81b2d1f0SGlenn Strauss /* digest not ok */ 580*81b2d1f0SGlenn Strauss 581*81b2d1f0SGlenn Strauss if (p->conf.auth_debug) { 582*81b2d1f0SGlenn Strauss log_error_write(srv, __FILE__, __LINE__, "sss", 583*81b2d1f0SGlenn Strauss "digest: digest mismatch", a2, respons); 584*81b2d1f0SGlenn Strauss } 585*81b2d1f0SGlenn Strauss 586*81b2d1f0SGlenn Strauss log_error_write(srv, __FILE__, __LINE__, "ssss", 587*81b2d1f0SGlenn Strauss "digest: auth failed for ", username, ": wrong password, IP:", inet_ntop_cache_get_ip(srv, &(con->dst_addr))); 588*81b2d1f0SGlenn Strauss 589*81b2d1f0SGlenn Strauss buffer_free(b); 590*81b2d1f0SGlenn Strauss return 0; 591*81b2d1f0SGlenn Strauss } 592*81b2d1f0SGlenn Strauss 593*81b2d1f0SGlenn Strauss /* value is our allow-rules */ 594*81b2d1f0SGlenn Strauss if (mod_auth_match_rules(srv, req, username, NULL, NULL)) { 595*81b2d1f0SGlenn Strauss buffer_free(b); 596*81b2d1f0SGlenn Strauss 597*81b2d1f0SGlenn Strauss if (p->conf.auth_debug) { 598*81b2d1f0SGlenn Strauss log_error_write(srv, __FILE__, __LINE__, "s", 599*81b2d1f0SGlenn Strauss "digest: rules did match"); 600*81b2d1f0SGlenn Strauss } 601*81b2d1f0SGlenn Strauss 602*81b2d1f0SGlenn Strauss return 0; 603*81b2d1f0SGlenn Strauss } 604*81b2d1f0SGlenn Strauss 605*81b2d1f0SGlenn Strauss /* check age of nonce. Note that rand() is used in nonce generation 606*81b2d1f0SGlenn Strauss * in mod_auth_digest_generate_nonce(). If that were replaced 607*81b2d1f0SGlenn Strauss * with nanosecond time, then nonce secret would remain unique enough 608*81b2d1f0SGlenn Strauss * for the purposes of Digest auth, and would be reproducible (and 609*81b2d1f0SGlenn Strauss * verifiable) if nanoseconds were inclued with seconds as part of the 610*81b2d1f0SGlenn Strauss * nonce "timestamp:secret". Since that is not done, timestamp in 611*81b2d1f0SGlenn Strauss * nonce could theoretically be modified and still produce same md5sum, 612*81b2d1f0SGlenn Strauss * but that is highly unlikely within a 10 min (moving) window of valid 613*81b2d1f0SGlenn Strauss * time relative to current time (now) */ 614*81b2d1f0SGlenn Strauss { 615*81b2d1f0SGlenn Strauss time_t ts = 0; 616*81b2d1f0SGlenn Strauss const unsigned char * const nonce_uns = (unsigned char *)nonce; 617*81b2d1f0SGlenn Strauss for (i = 0; i < 8 && light_isxdigit(nonce_uns[i]); ++i) { 618*81b2d1f0SGlenn Strauss ts = (ts << 4) + hex2int(nonce_uns[i]); 619*81b2d1f0SGlenn Strauss } 620*81b2d1f0SGlenn Strauss if (i != 8 || nonce[8] != ':' 621*81b2d1f0SGlenn Strauss || ts > srv->cur_ts || srv->cur_ts - ts > 600) { /*(10 mins)*/ 622*81b2d1f0SGlenn Strauss buffer_free(b); 623*81b2d1f0SGlenn Strauss return -2; /* nonce is stale; have client regenerate digest */ 624*81b2d1f0SGlenn Strauss } /*(future: might send nextnonce when expiration is imminent)*/ 625*81b2d1f0SGlenn Strauss } 626*81b2d1f0SGlenn Strauss 627*81b2d1f0SGlenn Strauss /* remember the username */ 628*81b2d1f0SGlenn Strauss buffer_copy_string(p->auth_user, username); 629*81b2d1f0SGlenn Strauss 630*81b2d1f0SGlenn Strauss buffer_free(b); 631*81b2d1f0SGlenn Strauss 632*81b2d1f0SGlenn Strauss if (p->conf.auth_debug) { 633*81b2d1f0SGlenn Strauss log_error_write(srv, __FILE__, __LINE__, "s", 634*81b2d1f0SGlenn Strauss "digest: auth ok"); 635*81b2d1f0SGlenn Strauss } 636*81b2d1f0SGlenn Strauss return 1; 637*81b2d1f0SGlenn Strauss } 638*81b2d1f0SGlenn Strauss 639*81b2d1f0SGlenn Strauss static int mod_auth_digest_generate_nonce(server *srv, mod_auth_plugin_data *p, buffer *fn, char (*out)[33]) { 640*81b2d1f0SGlenn Strauss HASH h; 641*81b2d1f0SGlenn Strauss li_MD5_CTX Md5Ctx; 642*81b2d1f0SGlenn Strauss char hh[LI_ITOSTRING_LENGTH]; 643*81b2d1f0SGlenn Strauss 644*81b2d1f0SGlenn Strauss UNUSED(p); 645*81b2d1f0SGlenn Strauss 646*81b2d1f0SGlenn Strauss /* generate shared-secret */ 647*81b2d1f0SGlenn Strauss li_MD5_Init(&Md5Ctx); 648*81b2d1f0SGlenn Strauss li_MD5_Update(&Md5Ctx, CONST_BUF_LEN(fn)); 649*81b2d1f0SGlenn Strauss li_MD5_Update(&Md5Ctx, CONST_STR_LEN("+")); 650*81b2d1f0SGlenn Strauss 651*81b2d1f0SGlenn Strauss /* we assume sizeof(time_t) == 4 here, but if not it ain't a problem at all */ 652*81b2d1f0SGlenn Strauss li_itostrn(hh, sizeof(hh), srv->cur_ts); 653*81b2d1f0SGlenn Strauss li_MD5_Update(&Md5Ctx, (unsigned char *)hh, strlen(hh)); 654*81b2d1f0SGlenn Strauss li_MD5_Update(&Md5Ctx, (unsigned char *)srv->entropy, sizeof(srv->entropy)); 655*81b2d1f0SGlenn Strauss li_itostrn(hh, sizeof(hh), rand()); 656*81b2d1f0SGlenn Strauss li_MD5_Update(&Md5Ctx, (unsigned char *)hh, strlen(hh)); 657*81b2d1f0SGlenn Strauss 658*81b2d1f0SGlenn Strauss li_MD5_Final(h, &Md5Ctx); 659*81b2d1f0SGlenn Strauss 660*81b2d1f0SGlenn Strauss CvtHex(h, out); 661*81b2d1f0SGlenn Strauss 662*81b2d1f0SGlenn Strauss return 0; 663*81b2d1f0SGlenn Strauss } 664*81b2d1f0SGlenn Strauss 665bcdc6a3bSJan Kneschke static handler_t mod_auth_uri_handler(server *srv, connection *con, void *p_d) { 666bcdc6a3bSJan Kneschke size_t k; 667bcdc6a3bSJan Kneschke int auth_required = 0, auth_satisfied = 0; 668bcdc6a3bSJan Kneschke char *http_authorization = NULL; 6697187271fSStefan Bühler const char *auth_type = NULL; 670bcdc6a3bSJan Kneschke data_string *ds; 671bcdc6a3bSJan Kneschke mod_auth_plugin_data *p = p_d; 672bcdc6a3bSJan Kneschke array *req; 673f9d58670SStefan Bühler data_string *req_method; 674bcdc6a3bSJan Kneschke 675bcdc6a3bSJan Kneschke /* select the right config */ 6764a6ce4aeSJan Kneschke mod_auth_patch_connection(srv, con, p); 677bcdc6a3bSJan Kneschke 678bcdc6a3bSJan Kneschke if (p->conf.auth_require == NULL) return HANDLER_GO_ON; 679bcdc6a3bSJan Kneschke 680bcdc6a3bSJan Kneschke /* 681bcdc6a3bSJan Kneschke * AUTH 682bcdc6a3bSJan Kneschke * 683bcdc6a3bSJan Kneschke */ 684bcdc6a3bSJan Kneschke 685bcdc6a3bSJan Kneschke /* do we have to ask for auth ? */ 686bcdc6a3bSJan Kneschke 687bcdc6a3bSJan Kneschke auth_required = 0; 688bcdc6a3bSJan Kneschke auth_satisfied = 0; 689bcdc6a3bSJan Kneschke 690bcdc6a3bSJan Kneschke /* search auth-directives for path */ 691bcdc6a3bSJan Kneschke for (k = 0; k < p->conf.auth_require->used; k++) { 69267079527SJan Kneschke buffer *require = p->conf.auth_require->data[k]->key; 693bcdc6a3bSJan Kneschke 694ad3e93eaSStefan Bühler if (buffer_is_empty(require)) continue; 695ad3e93eaSStefan Bühler if (buffer_string_length(con->uri.path) < buffer_string_length(require)) continue; 6964afd2ae2SJan Kneschke 6974afd2ae2SJan Kneschke /* if we have a case-insensitive FS we have to lower-case the URI here too */ 6984afd2ae2SJan Kneschke 6994afd2ae2SJan Kneschke if (con->conf.force_lowercase_filenames) { 700ad3e93eaSStefan Bühler if (0 == strncasecmp(con->uri.path->ptr, require->ptr, buffer_string_length(require))) { 701bcdc6a3bSJan Kneschke auth_required = 1; 702bcdc6a3bSJan Kneschke break; 703bcdc6a3bSJan Kneschke } 7044afd2ae2SJan Kneschke } else { 705ad3e93eaSStefan Bühler if (0 == strncmp(con->uri.path->ptr, require->ptr, buffer_string_length(require))) { 7064afd2ae2SJan Kneschke auth_required = 1; 7074afd2ae2SJan Kneschke break; 7084afd2ae2SJan Kneschke } 7094afd2ae2SJan Kneschke } 710bcdc6a3bSJan Kneschke } 711bcdc6a3bSJan Kneschke 712bcdc6a3bSJan Kneschke /* nothing to do for us */ 713bcdc6a3bSJan Kneschke if (auth_required == 0) return HANDLER_GO_ON; 714bcdc6a3bSJan Kneschke 715bcdc6a3bSJan Kneschke req = ((data_array *)(p->conf.auth_require->data[k]))->value; 716f9d58670SStefan Bühler req_method = (data_string *)array_get_element(req, "method"); 717f9d58670SStefan Bühler 718f9d58670SStefan Bühler if (0 == strcmp(req_method->value->ptr, "extern")) { 719f9d58670SStefan Bühler /* require REMOTE_USER to be already set */ 720f9d58670SStefan Bühler if (NULL == (ds = (data_string *)array_get_element(con->environment, "REMOTE_USER"))) { 721f9d58670SStefan Bühler con->http_status = 401; 722f9d58670SStefan Bühler con->mode = DIRECT; 723f9d58670SStefan Bühler return HANDLER_FINISHED; 724*81b2d1f0SGlenn Strauss } else if (mod_auth_match_rules(srv, req, ds->value->ptr, NULL, NULL)) { 725f9d58670SStefan Bühler log_error_write(srv, __FILE__, __LINE__, "s", "rules didn't match"); 726f9d58670SStefan Bühler con->http_status = 401; 727f9d58670SStefan Bühler con->mode = DIRECT; 728f9d58670SStefan Bühler return HANDLER_FINISHED; 729f9d58670SStefan Bühler } else { 730f9d58670SStefan Bühler return HANDLER_GO_ON; 731f9d58670SStefan Bühler } 732f9d58670SStefan Bühler } 733bcdc6a3bSJan Kneschke 734bcdc6a3bSJan Kneschke /* try to get Authorization-header */ 735bcdc6a3bSJan Kneschke 736ad3e93eaSStefan Bühler if (NULL != (ds = (data_string *)array_get_element(con->request.headers, "Authorization")) && !buffer_is_empty(ds->value)) { 737bcdc6a3bSJan Kneschke char *auth_realm; 738bcdc6a3bSJan Kneschke 739f9d58670SStefan Bühler http_authorization = ds->value->ptr; 740bcdc6a3bSJan Kneschke 741bcdc6a3bSJan Kneschke /* parse auth-header */ 742bcdc6a3bSJan Kneschke if (NULL != (auth_realm = strchr(http_authorization, ' '))) { 743bcdc6a3bSJan Kneschke int auth_type_len = auth_realm - http_authorization; 744bcdc6a3bSJan Kneschke 745bcdc6a3bSJan Kneschke if ((auth_type_len == 5) && 746708f499dSJan Kneschke (0 == strncasecmp(http_authorization, "Basic", auth_type_len))) { 7477187271fSStefan Bühler auth_type = "Basic"; 748bcdc6a3bSJan Kneschke 749f9d58670SStefan Bühler if (0 == strcmp(req_method->value->ptr, "basic")) { 750*81b2d1f0SGlenn Strauss auth_satisfied = mod_auth_basic_check(srv, con, p, req, auth_realm+1); 751bcdc6a3bSJan Kneschke } 752bcdc6a3bSJan Kneschke } else if ((auth_type_len == 6) && 753708f499dSJan Kneschke (0 == strncasecmp(http_authorization, "Digest", auth_type_len))) { 7547187271fSStefan Bühler auth_type = "Digest"; 755f9d58670SStefan Bühler if (0 == strcmp(req_method->value->ptr, "digest")) { 756*81b2d1f0SGlenn Strauss if (-1 == (auth_satisfied = mod_auth_digest_check(srv, con, p, req, auth_realm+1))) { 757bcdc6a3bSJan Kneschke con->http_status = 400; 758ad12e4c5SStefan Bühler con->mode = DIRECT; 759bcdc6a3bSJan Kneschke 760bcdc6a3bSJan Kneschke /* a field was missing */ 761bcdc6a3bSJan Kneschke 762bcdc6a3bSJan Kneschke return HANDLER_FINISHED; 763bcdc6a3bSJan Kneschke } 764bcdc6a3bSJan Kneschke } 765bcdc6a3bSJan Kneschke } else { 766bcdc6a3bSJan Kneschke log_error_write(srv, __FILE__, __LINE__, "ss", 767780e8e6eSStefan Bühler "unknown authentication type:", 768bcdc6a3bSJan Kneschke http_authorization); 769bcdc6a3bSJan Kneschke } 770bcdc6a3bSJan Kneschke } 771bcdc6a3bSJan Kneschke } 772bcdc6a3bSJan Kneschke 77300cc4d7cSGlenn Strauss if (1 != auth_satisfied) { /*(0 or -2)*/ 774bcdc6a3bSJan Kneschke data_string *method, *realm; 775bcdc6a3bSJan Kneschke method = (data_string *)array_get_element(req, "method"); 776bcdc6a3bSJan Kneschke realm = (data_string *)array_get_element(req, "realm"); 777bcdc6a3bSJan Kneschke 778bcdc6a3bSJan Kneschke con->http_status = 401; 779ad12e4c5SStefan Bühler con->mode = DIRECT; 780bcdc6a3bSJan Kneschke 78172b133f5SGlenn Strauss if (!method) return HANDLER_FINISHED;/*(should not happen; config is validated at startup)*/ 78272b133f5SGlenn Strauss if (!realm) return HANDLER_FINISHED; /*(should not happen; config is validated at startup)*/ 78372b133f5SGlenn Strauss 784bcdc6a3bSJan Kneschke if (0 == strcmp(method->value->ptr, "basic")) { 78552861d77SStefan Bühler buffer_copy_string_len(p->tmp_buf, CONST_STR_LEN("Basic realm=\"")); 786bcdc6a3bSJan Kneschke buffer_append_string_buffer(p->tmp_buf, realm->value); 7874b412797SGlenn Strauss buffer_append_string_len(p->tmp_buf, CONST_STR_LEN("\", charset=\"UTF-8\"")); 788bcdc6a3bSJan Kneschke 789bcdc6a3bSJan Kneschke response_header_insert(srv, con, CONST_STR_LEN("WWW-Authenticate"), CONST_BUF_LEN(p->tmp_buf)); 790bcdc6a3bSJan Kneschke } else if (0 == strcmp(method->value->ptr, "digest")) { 791bcdc6a3bSJan Kneschke char hh[33]; 792*81b2d1f0SGlenn Strauss mod_auth_digest_generate_nonce(srv, p, srv->tmp_buf, &hh); 793bcdc6a3bSJan Kneschke 79452861d77SStefan Bühler buffer_copy_string_len(p->tmp_buf, CONST_STR_LEN("Digest realm=\"")); 795bcdc6a3bSJan Kneschke buffer_append_string_buffer(p->tmp_buf, realm->value); 7964b412797SGlenn Strauss buffer_append_string_len(p->tmp_buf, CONST_STR_LEN("\", charset=\"UTF-8\", nonce=\"")); 79700cc4d7cSGlenn Strauss buffer_append_uint_hex(p->tmp_buf, (uintmax_t)srv->cur_ts); 79800cc4d7cSGlenn Strauss buffer_append_string_len(p->tmp_buf, CONST_STR_LEN(":")); 799bcdc6a3bSJan Kneschke buffer_append_string(p->tmp_buf, hh); 80052861d77SStefan Bühler buffer_append_string_len(p->tmp_buf, CONST_STR_LEN("\", qop=\"auth\"")); 80100cc4d7cSGlenn Strauss if (-2 == auth_satisfied) { 80200cc4d7cSGlenn Strauss buffer_append_string_len(p->tmp_buf, CONST_STR_LEN(", stale=true")); 80300cc4d7cSGlenn Strauss } 804bcdc6a3bSJan Kneschke 805bcdc6a3bSJan Kneschke response_header_insert(srv, con, CONST_STR_LEN("WWW-Authenticate"), CONST_BUF_LEN(p->tmp_buf)); 806bcdc6a3bSJan Kneschke } else { 807bcdc6a3bSJan Kneschke /* evil */ 808bcdc6a3bSJan Kneschke } 809bcdc6a3bSJan Kneschke return HANDLER_FINISHED; 810bcdc6a3bSJan Kneschke } else { 811bcdc6a3bSJan Kneschke /* the REMOTE_USER header */ 812bcdc6a3bSJan Kneschke 813559b198fSStefan Bühler if (NULL == (ds = (data_string *)array_get_element(con->environment, "REMOTE_USER"))) { 8147187271fSStefan Bühler if (NULL == (ds = (data_string *)array_get_unused_element(con->environment, TYPE_STRING))) { 8157187271fSStefan Bühler ds = data_string_init(); 8167187271fSStefan Bühler } 817559b198fSStefan Bühler buffer_copy_string(ds->key, "REMOTE_USER"); 8187187271fSStefan Bühler array_insert_unique(con->environment, (data_unset *)ds); 819bcdc6a3bSJan Kneschke } 8206afad87dSStefan Bühler buffer_copy_buffer(ds->value, p->auth_user); 821559b198fSStefan Bühler 822559b198fSStefan Bühler /* AUTH_TYPE environment */ 823559b198fSStefan Bühler 824559b198fSStefan Bühler if (NULL == (ds = (data_string *)array_get_element(con->environment, "AUTH_TYPE"))) { 825559b198fSStefan Bühler if (NULL == (ds = (data_string *)array_get_unused_element(con->environment, TYPE_STRING))) { 826559b198fSStefan Bühler ds = data_string_init(); 827559b198fSStefan Bühler } 828559b198fSStefan Bühler buffer_copy_string(ds->key, "AUTH_TYPE"); 829559b198fSStefan Bühler array_insert_unique(con->environment, (data_unset *)ds); 830559b198fSStefan Bühler } 831559b198fSStefan Bühler buffer_copy_string(ds->value, auth_type); 832559b198fSStefan Bühler } 833bcdc6a3bSJan Kneschke 834bcdc6a3bSJan Kneschke return HANDLER_GO_ON; 835bcdc6a3bSJan Kneschke } 836bcdc6a3bSJan Kneschke 837bcdc6a3bSJan Kneschke SETDEFAULTS_FUNC(mod_auth_set_defaults) { 838bcdc6a3bSJan Kneschke mod_auth_plugin_data *p = p_d; 839bcdc6a3bSJan Kneschke size_t i; 840bcdc6a3bSJan Kneschke 841bcdc6a3bSJan Kneschke config_values_t cv[] = { 842bcdc6a3bSJan Kneschke { "auth.backend", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 0 */ 84375d22698SStefan Bühler { "auth.backend.plain.groupfile", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 1 */ 84475d22698SStefan Bühler { "auth.backend.plain.userfile", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 2 */ 84575d22698SStefan Bühler { "auth.require", NULL, T_CONFIG_LOCAL, T_CONFIG_SCOPE_CONNECTION }, /* 3 */ 84675d22698SStefan Bühler { "auth.backend.ldap.hostname", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 4 */ 84775d22698SStefan Bühler { "auth.backend.ldap.base-dn", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 5 */ 84875d22698SStefan Bühler { "auth.backend.ldap.filter", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 6 */ 84975d22698SStefan Bühler { "auth.backend.ldap.ca-file", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 7 */ 85075d22698SStefan Bühler { "auth.backend.ldap.starttls", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 8 */ 85175d22698SStefan Bühler { "auth.backend.ldap.bind-dn", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 9 */ 8529238cad6SJan Kneschke { "auth.backend.ldap.bind-pw", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 10 */ 85375d22698SStefan Bühler { "auth.backend.ldap.allow-empty-pw", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 11 */ 85475d22698SStefan Bühler { "auth.backend.htdigest.userfile", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 12 */ 85575d22698SStefan Bühler { "auth.backend.htpasswd.userfile", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 13 */ 85675d22698SStefan Bühler { "auth.debug", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, /* 14 */ 857bcdc6a3bSJan Kneschke { NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET } 858bcdc6a3bSJan Kneschke }; 859bcdc6a3bSJan Kneschke 8606f208cfdSStefan Bühler p->config_storage = calloc(1, srv->config_context->used * sizeof(mod_auth_plugin_config *)); 861bcdc6a3bSJan Kneschke 862bcdc6a3bSJan Kneschke for (i = 0; i < srv->config_context->used; i++) { 863c512345fSStefan Bühler data_config const* config = (data_config const*)srv->config_context->data[i]; 864bcdc6a3bSJan Kneschke mod_auth_plugin_config *s; 865bcdc6a3bSJan Kneschke size_t n; 866bcdc6a3bSJan Kneschke data_array *da; 867bcdc6a3bSJan Kneschke 868bac9212cSJan Kneschke s = calloc(1, sizeof(mod_auth_plugin_config)); 869bcdc6a3bSJan Kneschke s->auth_plain_groupfile = buffer_init(); 870bcdc6a3bSJan Kneschke s->auth_plain_userfile = buffer_init(); 871bcdc6a3bSJan Kneschke s->auth_htdigest_userfile = buffer_init(); 872bcdc6a3bSJan Kneschke s->auth_htpasswd_userfile = buffer_init(); 873bcdc6a3bSJan Kneschke s->auth_backend_conf = buffer_init(); 874bcdc6a3bSJan Kneschke 875bcdc6a3bSJan Kneschke s->auth_ldap_hostname = buffer_init(); 876bcdc6a3bSJan Kneschke s->auth_ldap_basedn = buffer_init(); 8779238cad6SJan Kneschke s->auth_ldap_binddn = buffer_init(); 8789238cad6SJan Kneschke s->auth_ldap_bindpw = buffer_init(); 879bcdc6a3bSJan Kneschke s->auth_ldap_filter = buffer_init(); 8804a6ce4aeSJan Kneschke s->auth_ldap_cafile = buffer_init(); 8814a6ce4aeSJan Kneschke s->auth_ldap_starttls = 0; 882bcdc6a3bSJan Kneschke s->auth_debug = 0; 883bcdc6a3bSJan Kneschke 884bcdc6a3bSJan Kneschke s->auth_require = array_init(); 885bcdc6a3bSJan Kneschke 886bcdc6a3bSJan Kneschke #ifdef USE_LDAP 887bcdc6a3bSJan Kneschke s->ldap_filter_pre = buffer_init(); 888bcdc6a3bSJan Kneschke s->ldap_filter_post = buffer_init(); 889bcdc6a3bSJan Kneschke s->ldap = NULL; 890bcdc6a3bSJan Kneschke #endif 891bcdc6a3bSJan Kneschke 892bcdc6a3bSJan Kneschke cv[0].destination = s->auth_backend_conf; 893bcdc6a3bSJan Kneschke cv[1].destination = s->auth_plain_groupfile; 894bcdc6a3bSJan Kneschke cv[2].destination = s->auth_plain_userfile; 895bcdc6a3bSJan Kneschke cv[3].destination = s->auth_require; 896bcdc6a3bSJan Kneschke cv[4].destination = s->auth_ldap_hostname; 897bcdc6a3bSJan Kneschke cv[5].destination = s->auth_ldap_basedn; 898bcdc6a3bSJan Kneschke cv[6].destination = s->auth_ldap_filter; 8994a6ce4aeSJan Kneschke cv[7].destination = s->auth_ldap_cafile; 9004a6ce4aeSJan Kneschke cv[8].destination = &(s->auth_ldap_starttls); 9019238cad6SJan Kneschke cv[9].destination = s->auth_ldap_binddn; 9029238cad6SJan Kneschke cv[10].destination = s->auth_ldap_bindpw; 903d471bcc9SMarcus Rückert cv[11].destination = &(s->auth_ldap_allow_empty_pw); 904d471bcc9SMarcus Rückert cv[12].destination = s->auth_htdigest_userfile; 905d471bcc9SMarcus Rückert cv[13].destination = s->auth_htpasswd_userfile; 906d471bcc9SMarcus Rückert cv[14].destination = &(s->auth_debug); 907bcdc6a3bSJan Kneschke 908bcdc6a3bSJan Kneschke p->config_storage[i] = s; 909bcdc6a3bSJan Kneschke 910c512345fSStefan Bühler if (0 != config_insert_values_global(srv, config->value, cv, i == 0 ? T_CONFIG_SCOPE_SERVER : T_CONFIG_SCOPE_CONNECTION)) { 911bcdc6a3bSJan Kneschke return HANDLER_ERROR; 912bcdc6a3bSJan Kneschke } 913bcdc6a3bSJan Kneschke 914ad3e93eaSStefan Bühler if (!buffer_string_is_empty(s->auth_backend_conf)) { 915bcdc6a3bSJan Kneschke if (0 == strcmp(s->auth_backend_conf->ptr, "htpasswd")) { 916bcdc6a3bSJan Kneschke s->auth_backend = AUTH_BACKEND_HTPASSWD; 917bcdc6a3bSJan Kneschke } else if (0 == strcmp(s->auth_backend_conf->ptr, "htdigest")) { 918bcdc6a3bSJan Kneschke s->auth_backend = AUTH_BACKEND_HTDIGEST; 919bcdc6a3bSJan Kneschke } else if (0 == strcmp(s->auth_backend_conf->ptr, "plain")) { 920bcdc6a3bSJan Kneschke s->auth_backend = AUTH_BACKEND_PLAIN; 921bcdc6a3bSJan Kneschke } else if (0 == strcmp(s->auth_backend_conf->ptr, "ldap")) { 922bcdc6a3bSJan Kneschke s->auth_backend = AUTH_BACKEND_LDAP; 923bcdc6a3bSJan Kneschke } else { 924bcdc6a3bSJan Kneschke log_error_write(srv, __FILE__, __LINE__, "sb", "auth.backend not supported:", s->auth_backend_conf); 925bcdc6a3bSJan Kneschke 926bcdc6a3bSJan Kneschke return HANDLER_ERROR; 927bcdc6a3bSJan Kneschke } 928bcdc6a3bSJan Kneschke } 929bcdc6a3bSJan Kneschke 930e7ff9483SStefan Bühler #ifdef USE_LDAP 931ad3e93eaSStefan Bühler if (!buffer_string_is_empty(s->auth_ldap_filter)) { 932e7ff9483SStefan Bühler char *dollar; 933e7ff9483SStefan Bühler 934e7ff9483SStefan Bühler /* parse filter */ 935e7ff9483SStefan Bühler 936e7ff9483SStefan Bühler if (NULL == (dollar = strchr(s->auth_ldap_filter->ptr, '$'))) { 937e7ff9483SStefan Bühler log_error_write(srv, __FILE__, __LINE__, "s", "ldap: auth.backend.ldap.filter is missing a replace-operator '$'"); 938e7ff9483SStefan Bühler 939e7ff9483SStefan Bühler return HANDLER_ERROR; 940e7ff9483SStefan Bühler } 941e7ff9483SStefan Bühler 942e7ff9483SStefan Bühler buffer_copy_string_len(s->ldap_filter_pre, s->auth_ldap_filter->ptr, dollar - s->auth_ldap_filter->ptr); 943e7ff9483SStefan Bühler buffer_copy_string(s->ldap_filter_post, dollar+1); 944e7ff9483SStefan Bühler } 945e7ff9483SStefan Bühler #endif 946e7ff9483SStefan Bühler 947bcdc6a3bSJan Kneschke /* no auth.require for this section */ 948c512345fSStefan Bühler if (NULL == (da = (data_array *)array_get_element(config->value, "auth.require"))) continue; 949bcdc6a3bSJan Kneschke 950bcdc6a3bSJan Kneschke if (da->type != TYPE_ARRAY) continue; 951bcdc6a3bSJan Kneschke 952bcdc6a3bSJan Kneschke for (n = 0; n < da->value->used; n++) { 953bcdc6a3bSJan Kneschke size_t m; 954bcdc6a3bSJan Kneschke data_array *da_file = (data_array *)da->value->data[n]; 955bcdc6a3bSJan Kneschke const char *method, *realm, *require; 956bcdc6a3bSJan Kneschke 957bcdc6a3bSJan Kneschke if (da->value->data[n]->type != TYPE_ARRAY) { 958cc7a8ae0SJan Kneschke log_error_write(srv, __FILE__, __LINE__, "ss", 959cc7a8ae0SJan Kneschke "auth.require should contain an array as in:", 960cc7a8ae0SJan Kneschke "auth.require = ( \"...\" => ( ..., ...) )"); 961bcdc6a3bSJan Kneschke 962bcdc6a3bSJan Kneschke return HANDLER_ERROR; 963bcdc6a3bSJan Kneschke } 964bcdc6a3bSJan Kneschke 965bcdc6a3bSJan Kneschke method = realm = require = NULL; 966bcdc6a3bSJan Kneschke 967bcdc6a3bSJan Kneschke for (m = 0; m < da_file->value->used; m++) { 968bcdc6a3bSJan Kneschke if (da_file->value->data[m]->type == TYPE_STRING) { 969bcdc6a3bSJan Kneschke if (0 == strcmp(da_file->value->data[m]->key->ptr, "method")) { 970bcdc6a3bSJan Kneschke method = ((data_string *)(da_file->value->data[m]))->value->ptr; 971bcdc6a3bSJan Kneschke } else if (0 == strcmp(da_file->value->data[m]->key->ptr, "realm")) { 972bcdc6a3bSJan Kneschke realm = ((data_string *)(da_file->value->data[m]))->value->ptr; 973bcdc6a3bSJan Kneschke } else if (0 == strcmp(da_file->value->data[m]->key->ptr, "require")) { 974bcdc6a3bSJan Kneschke require = ((data_string *)(da_file->value->data[m]))->value->ptr; 975bcdc6a3bSJan Kneschke } else { 976cc7a8ae0SJan Kneschke log_error_write(srv, __FILE__, __LINE__, "ssbs", 977cc7a8ae0SJan Kneschke "the field is unknown in:", 978cc7a8ae0SJan Kneschke "auth.require = ( \"...\" => ( ..., -> \"", 979cc7a8ae0SJan Kneschke da_file->value->data[m]->key, 980cc7a8ae0SJan Kneschke "\" <- => \"...\" ) )"); 981cc7a8ae0SJan Kneschke 982bcdc6a3bSJan Kneschke return HANDLER_ERROR; 983bcdc6a3bSJan Kneschke } 984bcdc6a3bSJan Kneschke } else { 985cc7a8ae0SJan Kneschke log_error_write(srv, __FILE__, __LINE__, "ssbs", 986cc7a8ae0SJan Kneschke "a string was expected for:", 987cc7a8ae0SJan Kneschke "auth.require = ( \"...\" => ( ..., -> \"", 988cc7a8ae0SJan Kneschke da_file->value->data[m]->key, 989cc7a8ae0SJan Kneschke "\" <- => \"...\" ) )"); 990bcdc6a3bSJan Kneschke 991bcdc6a3bSJan Kneschke return HANDLER_ERROR; 992bcdc6a3bSJan Kneschke } 993bcdc6a3bSJan Kneschke } 994bcdc6a3bSJan Kneschke 995bcdc6a3bSJan Kneschke if (method == NULL) { 996cc7a8ae0SJan Kneschke log_error_write(srv, __FILE__, __LINE__, "ss", 9974df22f2aSStefan Bühler "the method field is missing in:", 998cc7a8ae0SJan Kneschke "auth.require = ( \"...\" => ( ..., \"method\" => \"...\" ) )"); 999bcdc6a3bSJan Kneschke return HANDLER_ERROR; 10004a6ce4aeSJan Kneschke } else { 10014a6ce4aeSJan Kneschke if (0 != strcmp(method, "basic") && 1002f9d58670SStefan Bühler 0 != strcmp(method, "digest") && 1003f9d58670SStefan Bühler 0 != strcmp(method, "extern")) { 1004cc7a8ae0SJan Kneschke log_error_write(srv, __FILE__, __LINE__, "ss", 1005f9d58670SStefan Bühler "method has to be either \"basic\", \"digest\" or \"extern\" in", 1006cc7a8ae0SJan Kneschke "auth.require = ( \"...\" => ( ..., \"method\" => \"...\") )"); 10074a6ce4aeSJan Kneschke return HANDLER_ERROR; 10084a6ce4aeSJan Kneschke } 1009bcdc6a3bSJan Kneschke } 1010bcdc6a3bSJan Kneschke 1011bcdc6a3bSJan Kneschke if (realm == NULL) { 1012cc7a8ae0SJan Kneschke log_error_write(srv, __FILE__, __LINE__, "ss", 10134df22f2aSStefan Bühler "the realm field is missing in:", 1014cc7a8ae0SJan Kneschke "auth.require = ( \"...\" => ( ..., \"realm\" => \"...\" ) )"); 1015bcdc6a3bSJan Kneschke return HANDLER_ERROR; 1016bcdc6a3bSJan Kneschke } 1017bcdc6a3bSJan Kneschke 1018bcdc6a3bSJan Kneschke if (require == NULL) { 1019cc7a8ae0SJan Kneschke log_error_write(srv, __FILE__, __LINE__, "ss", 1020cc7a8ae0SJan Kneschke "the require field is missing in:", 1021cc7a8ae0SJan Kneschke "auth.require = ( \"...\" => ( ..., \"require\" => \"...\" ) )"); 1022bcdc6a3bSJan Kneschke return HANDLER_ERROR; 1023bcdc6a3bSJan Kneschke } 1024bcdc6a3bSJan Kneschke 1025bcdc6a3bSJan Kneschke if (method && realm && require) { 1026bcdc6a3bSJan Kneschke data_string *ds; 1027bcdc6a3bSJan Kneschke data_array *a; 1028bcdc6a3bSJan Kneschke 1029bcdc6a3bSJan Kneschke a = data_array_init(); 10306afad87dSStefan Bühler buffer_copy_buffer(a->key, da_file->key); 1031bcdc6a3bSJan Kneschke 1032bcdc6a3bSJan Kneschke ds = data_string_init(); 1033bcdc6a3bSJan Kneschke 103452861d77SStefan Bühler buffer_copy_string_len(ds->key, CONST_STR_LEN("method")); 1035bcdc6a3bSJan Kneschke buffer_copy_string(ds->value, method); 1036bcdc6a3bSJan Kneschke 1037bcdc6a3bSJan Kneschke array_insert_unique(a->value, (data_unset *)ds); 1038bcdc6a3bSJan Kneschke 1039bcdc6a3bSJan Kneschke ds = data_string_init(); 1040bcdc6a3bSJan Kneschke 104152861d77SStefan Bühler buffer_copy_string_len(ds->key, CONST_STR_LEN("realm")); 1042bcdc6a3bSJan Kneschke buffer_copy_string(ds->value, realm); 1043bcdc6a3bSJan Kneschke 1044bcdc6a3bSJan Kneschke array_insert_unique(a->value, (data_unset *)ds); 1045bcdc6a3bSJan Kneschke 1046bcdc6a3bSJan Kneschke ds = data_string_init(); 1047bcdc6a3bSJan Kneschke 104852861d77SStefan Bühler buffer_copy_string_len(ds->key, CONST_STR_LEN("require")); 1049bcdc6a3bSJan Kneschke buffer_copy_string(ds->value, require); 1050bcdc6a3bSJan Kneschke 1051bcdc6a3bSJan Kneschke array_insert_unique(a->value, (data_unset *)ds); 1052bcdc6a3bSJan Kneschke 1053bcdc6a3bSJan Kneschke array_insert_unique(s->auth_require, (data_unset *)a); 1054bcdc6a3bSJan Kneschke } 1055bcdc6a3bSJan Kneschke } 1056bcdc6a3bSJan Kneschke 1057ad3e93eaSStefan Bühler switch(s->auth_backend) { 1058ea7b9abaSJan Kneschke case AUTH_BACKEND_LDAP: { 1059ea7b9abaSJan Kneschke handler_t ret = auth_ldap_init(srv, s); 1060ea7b9abaSJan Kneschke if (ret == HANDLER_ERROR) 1061ea7b9abaSJan Kneschke return (ret); 1062ea7b9abaSJan Kneschke break; 1063ea7b9abaSJan Kneschke } 1064ea7b9abaSJan Kneschke default: 1065ea7b9abaSJan Kneschke break; 1066ea7b9abaSJan Kneschke } 1067ea7b9abaSJan Kneschke } 1068ea7b9abaSJan Kneschke 1069ea7b9abaSJan Kneschke return HANDLER_GO_ON; 1070ea7b9abaSJan Kneschke } 1071ea7b9abaSJan Kneschke 10726ca932dfSJan Kneschke handler_t auth_ldap_init(server *srv, mod_auth_plugin_config *s) { 1073bcdc6a3bSJan Kneschke #ifdef USE_LDAP 1074ea7b9abaSJan Kneschke int ret; 1075bcdc6a3bSJan Kneschke #if 0 1076bcdc6a3bSJan Kneschke if (s->auth_ldap_basedn->used == 0) { 1077bcdc6a3bSJan Kneschke log_error_write(srv, __FILE__, __LINE__, "s", "ldap: auth.backend.ldap.base-dn has to be set"); 1078bcdc6a3bSJan Kneschke 1079bcdc6a3bSJan Kneschke return HANDLER_ERROR; 1080bcdc6a3bSJan Kneschke } 1081bcdc6a3bSJan Kneschke #endif 1082bcdc6a3bSJan Kneschke 1083ad3e93eaSStefan Bühler if (!buffer_string_is_empty(s->auth_ldap_hostname)) { 1084cfba07cbSStefan Bühler /* free old context */ 1085cfba07cbSStefan Bühler if (NULL != s->ldap) ldap_unbind_s(s->ldap); 1086cfba07cbSStefan Bühler 1087bcdc6a3bSJan Kneschke if (NULL == (s->ldap = ldap_init(s->auth_ldap_hostname->ptr, LDAP_PORT))) { 1088bcdc6a3bSJan Kneschke log_error_write(srv, __FILE__, __LINE__, "ss", "ldap ...", strerror(errno)); 1089bcdc6a3bSJan Kneschke 1090bcdc6a3bSJan Kneschke return HANDLER_ERROR; 1091bcdc6a3bSJan Kneschke } 1092bcdc6a3bSJan Kneschke 1093bcdc6a3bSJan Kneschke ret = LDAP_VERSION3; 1094bcdc6a3bSJan Kneschke if (LDAP_OPT_SUCCESS != (ret = ldap_set_option(s->ldap, LDAP_OPT_PROTOCOL_VERSION, &ret))) { 1095bcdc6a3bSJan Kneschke log_error_write(srv, __FILE__, __LINE__, "ss", "ldap:", ldap_err2string(ret)); 1096bcdc6a3bSJan Kneschke 1097bcdc6a3bSJan Kneschke return HANDLER_ERROR; 1098bcdc6a3bSJan Kneschke } 1099bcdc6a3bSJan Kneschke 11004a6ce4aeSJan Kneschke if (s->auth_ldap_starttls) { 11016ca932dfSJan Kneschke /* if no CA file is given, it is ok, as we will use encryption 11026ca932dfSJan Kneschke * if the server requires a CAfile it will tell us */ 11036afad87dSStefan Bühler if (!buffer_string_is_empty(s->auth_ldap_cafile)) { 11046ca932dfSJan Kneschke if (LDAP_OPT_SUCCESS != (ret = ldap_set_option(NULL, LDAP_OPT_X_TLS_CACERTFILE, 11056ca932dfSJan Kneschke s->auth_ldap_cafile->ptr))) { 11066ca932dfSJan Kneschke log_error_write(srv, __FILE__, __LINE__, "ss", 11076ca932dfSJan Kneschke "Loading CA certificate failed:", ldap_err2string(ret)); 11084a6ce4aeSJan Kneschke 11094a6ce4aeSJan Kneschke return HANDLER_ERROR; 11104a6ce4aeSJan Kneschke } 11114a6ce4aeSJan Kneschke } 11124a6ce4aeSJan Kneschke 11134a6ce4aeSJan Kneschke if (LDAP_OPT_SUCCESS != (ret = ldap_start_tls_s(s->ldap, NULL, NULL))) { 11144a6ce4aeSJan Kneschke log_error_write(srv, __FILE__, __LINE__, "ss", "ldap startTLS failed:", ldap_err2string(ret)); 11154a6ce4aeSJan Kneschke 11164a6ce4aeSJan Kneschke return HANDLER_ERROR; 11174a6ce4aeSJan Kneschke } 11184a6ce4aeSJan Kneschke } 11194a6ce4aeSJan Kneschke 1120bcdc6a3bSJan Kneschke 1121bcdc6a3bSJan Kneschke /* 1. */ 1122ad3e93eaSStefan Bühler if (!buffer_string_is_empty(s->auth_ldap_binddn)) { 11239238cad6SJan Kneschke if (LDAP_SUCCESS != (ret = ldap_simple_bind_s(s->ldap, s->auth_ldap_binddn->ptr, s->auth_ldap_bindpw->ptr))) { 11249238cad6SJan Kneschke log_error_write(srv, __FILE__, __LINE__, "ss", "ldap:", ldap_err2string(ret)); 11259238cad6SJan Kneschke 11269238cad6SJan Kneschke return HANDLER_ERROR; 11279238cad6SJan Kneschke } 11289238cad6SJan Kneschke } else { 1129bcdc6a3bSJan Kneschke if (LDAP_SUCCESS != (ret = ldap_simple_bind_s(s->ldap, NULL, NULL))) { 1130bcdc6a3bSJan Kneschke log_error_write(srv, __FILE__, __LINE__, "ss", "ldap:", ldap_err2string(ret)); 1131bcdc6a3bSJan Kneschke 1132bcdc6a3bSJan Kneschke return HANDLER_ERROR; 1133bcdc6a3bSJan Kneschke } 1134bcdc6a3bSJan Kneschke } 11359238cad6SJan Kneschke } 11365a9992b1SStefan Bühler return HANDLER_GO_ON; 1137bcdc6a3bSJan Kneschke #else 11385a9992b1SStefan Bühler UNUSED(s); 1139bcdc6a3bSJan Kneschke log_error_write(srv, __FILE__, __LINE__, "s", "no ldap support available"); 1140bcdc6a3bSJan Kneschke return HANDLER_ERROR; 1141bcdc6a3bSJan Kneschke #endif 1142bcdc6a3bSJan Kneschke } 1143bcdc6a3bSJan Kneschke 114463f785a2SStefan Bühler int mod_auth_plugin_init(plugin *p); 1145bcdc6a3bSJan Kneschke int mod_auth_plugin_init(plugin *p) { 1146bcdc6a3bSJan Kneschke p->version = LIGHTTPD_VERSION_ID; 1147bcdc6a3bSJan Kneschke p->name = buffer_init_string("auth"); 1148bcdc6a3bSJan Kneschke p->init = mod_auth_init; 1149bcdc6a3bSJan Kneschke p->set_defaults = mod_auth_set_defaults; 1150bcdc6a3bSJan Kneschke p->handle_uri_clean = mod_auth_uri_handler; 1151bcdc6a3bSJan Kneschke p->cleanup = mod_auth_free; 1152bcdc6a3bSJan Kneschke 1153bcdc6a3bSJan Kneschke p->data = NULL; 1154bcdc6a3bSJan Kneschke 1155bcdc6a3bSJan Kneschke return 0; 1156bcdc6a3bSJan Kneschke } 1157