1 #include "first.h" 2 3 #include <stdlib.h> 4 #include <string.h> 5 6 #include "sys-crypto-md.h" /* USE_LIB_CRYPTO */ 7 8 #include "base.h" 9 #include "plugin.h" 10 #include "http_auth.h" 11 #include "http_header.h" 12 #include "log.h" 13 #include "safe_memclear.h" 14 #include "algo_splaytree.h" 15 16 /** 17 * auth framework 18 */ 19 20 typedef struct { 21 splay_tree *sptree; /* data in nodes of tree are (http_auth_cache_entry *)*/ 22 time_t max_age; 23 } http_auth_cache; 24 25 typedef struct { 26 const http_auth_backend_t *auth_backend; 27 const array *auth_require; 28 http_auth_cache *auth_cache; 29 unsigned int auth_extern_authn; 30 } plugin_config; 31 32 typedef struct { 33 PLUGIN_DATA; 34 plugin_config defaults; 35 plugin_config conf; 36 } plugin_data; 37 38 typedef struct { 39 const struct http_auth_require_t *require; 40 time_t ctime; 41 int dalgo; 42 uint32_t dlen; 43 uint32_t ulen; 44 char *username; 45 char *pwdigest; 46 } http_auth_cache_entry; 47 48 static http_auth_cache_entry * 49 http_auth_cache_entry_init (const struct http_auth_require_t * const require, const int dalgo, const char *username, const uint32_t ulen, const char *pw, const uint32_t pwlen) 50 { 51 /*(similar to buffer_copy_string_len() for each element, 52 * but allocate exact lengths in single chunk of memory 53 * for cache to avoid wasting space and for memory locality)*/ 54 /* http_auth_require_t is stored instead of copying realm 55 *(store pointer to http_auth_require_t, which is persistent 56 * and will be different for each realm + permissions combo)*/ 57 http_auth_cache_entry * const ae = 58 malloc(sizeof(http_auth_cache_entry) + ulen + pwlen); 59 force_assert(ae); 60 ae->require = require; 61 ae->ctime = log_epoch_secs; 62 ae->dalgo = dalgo; 63 ae->ulen = ulen; 64 ae->dlen = pwlen; 65 ae->username = (char *)(ae + 1); 66 ae->pwdigest = ae->username + ulen; 67 memcpy(ae->username, username, ulen); 68 memcpy(ae->pwdigest, pw, pwlen); 69 return ae; 70 } 71 72 static void 73 http_auth_cache_entry_free (void *data) 74 { 75 http_auth_cache_entry * const ae = data; 76 safe_memclear(ae->pwdigest, ae->dlen); 77 free(ae); 78 } 79 80 static void 81 http_auth_cache_free (http_auth_cache *ac) 82 { 83 splay_tree *sptree = ac->sptree; 84 while (sptree) { 85 http_auth_cache_entry_free(sptree->data); 86 sptree = splaytree_delete(sptree, sptree->key); 87 } 88 free(ac); 89 } 90 91 static http_auth_cache * 92 http_auth_cache_init (const array *opts) 93 { 94 http_auth_cache *ac = malloc(sizeof(http_auth_cache)); 95 force_assert(ac); 96 ac->sptree = NULL; 97 ac->max_age = 600; /* 10 mins */ 98 for (uint32_t i = 0, used = opts->used; i < used; ++i) { 99 data_string *ds = (data_string *)opts->data[i]; 100 if (buffer_is_equal_string(&ds->key, CONST_STR_LEN("max-age"))) { 101 if (ds->type == TYPE_STRING) 102 ac->max_age = (time_t)strtol(ds->value.ptr, NULL, 10); 103 else if (ds->type == TYPE_INTEGER) 104 ac->max_age = (time_t)((data_integer *)ds)->value; 105 } 106 } 107 return ac; 108 } 109 110 static int 111 http_auth_cache_hash (const struct http_auth_require_t * const require, const char *username, const uint32_t ulen) 112 { 113 uint32_t h = /*(hash pointer value, which includes realm and permissions)*/ 114 djbhash((char *)(intptr_t)require, sizeof(intptr_t), DJBHASH_INIT); 115 h = djbhash(username, ulen, h); 116 /* strip highest bit of hash value for splaytree (see splaytree_djbhash())*/ 117 return (int32_t)(h & ~(((uint32_t)1) << 31)); 118 } 119 120 static http_auth_cache_entry * 121 http_auth_cache_query (splay_tree ** const sptree, const int ndx) 122 { 123 *sptree = splaytree_splay(*sptree, ndx); 124 return (*sptree && (*sptree)->key == ndx) ? (*sptree)->data : NULL; 125 } 126 127 static void 128 http_auth_cache_insert (splay_tree ** const sptree, const int ndx, void * const data, void(data_free_fn)(void *)) 129 { 130 /*(not necessary to re-splay (with current usage) since single-threaded 131 * and splaytree has not been modified since http_auth_cache_query())*/ 132 /* *sptree = splaytree_splay(*sptree, ndx); */ 133 if (NULL == *sptree || (*sptree)->key != ndx) 134 *sptree = splaytree_insert(*sptree, ndx, data); 135 else { /* collision; replace old entry */ 136 data_free_fn((*sptree)->data); 137 (*sptree)->data = data; 138 } 139 } 140 141 /* walk though cache, collect expired ids, and remove them in a second loop */ 142 static void 143 mod_auth_tag_old_entries (splay_tree * const t, int * const keys, int * const ndx, const time_t max_age, const time_t cur_ts) 144 { 145 if (*ndx == 8192) return; /*(must match num array entries in keys[])*/ 146 if (t->left) 147 mod_auth_tag_old_entries(t->left, keys, ndx, max_age, cur_ts); 148 if (t->right) 149 mod_auth_tag_old_entries(t->right, keys, ndx, max_age, cur_ts); 150 if (*ndx == 8192) return; /*(must match num array entries in keys[])*/ 151 152 const http_auth_cache_entry * const ae = t->data; 153 if (cur_ts - ae->ctime > max_age) 154 keys[(*ndx)++] = t->key; 155 } 156 157 __attribute_noinline__ 158 static void 159 mod_auth_periodic_cleanup(splay_tree **sptree_ptr, const time_t max_age, const time_t cur_ts) 160 { 161 splay_tree *sptree = *sptree_ptr; 162 int max_ndx, i; 163 int keys[8192]; /* 32k size on stack */ 164 do { 165 if (!sptree) break; 166 max_ndx = 0; 167 mod_auth_tag_old_entries(sptree, keys, &max_ndx, max_age, cur_ts); 168 for (i = 0; i < max_ndx; ++i) { 169 int ndx = keys[i]; 170 sptree = splaytree_splay(sptree, ndx); 171 if (sptree && sptree->key == ndx) { 172 http_auth_cache_entry_free(sptree->data); 173 sptree = splaytree_delete(sptree, ndx); 174 } 175 } 176 } while (max_ndx == sizeof(keys)/sizeof(int)); 177 *sptree_ptr = sptree; 178 } 179 180 TRIGGER_FUNC(mod_auth_periodic) 181 { 182 const plugin_data * const p = p_d; 183 const time_t cur_ts = log_epoch_secs; 184 if (cur_ts & 0x7) return HANDLER_GO_ON; /*(continue once each 8 sec)*/ 185 UNUSED(srv); 186 187 /* future: might construct array of (http_auth_cache *) at startup 188 * to avoid the need to search for them here */ 189 for (int i = 0, used = p->nconfig; i < used; ++i) { 190 const config_plugin_value_t *cpv = p->cvlist + p->cvlist[i].v.u2[0]; 191 for (; cpv->k_id != -1; ++cpv) { 192 if (cpv->k_id != 3) continue; /* k_id == 3 for auth.cache */ 193 if (cpv->vtype != T_CONFIG_LOCAL) continue; 194 http_auth_cache *ac = cpv->v.v; 195 mod_auth_periodic_cleanup(&ac->sptree, ac->max_age, cur_ts); 196 } 197 } 198 199 return HANDLER_GO_ON; 200 } 201 202 203 204 205 static handler_t mod_auth_check_basic(request_st *r, void *p_d, const struct http_auth_require_t *require, const struct http_auth_backend_t *backend); 206 static handler_t mod_auth_check_digest(request_st *r, void *p_d, const struct http_auth_require_t *require, const struct http_auth_backend_t *backend); 207 static handler_t mod_auth_check_extern(request_st *r, void *p_d, const struct http_auth_require_t *require, const struct http_auth_backend_t *backend); 208 209 INIT_FUNC(mod_auth_init) { 210 static http_auth_scheme_t http_auth_scheme_basic = { "basic", mod_auth_check_basic, NULL }; 211 static http_auth_scheme_t http_auth_scheme_digest = { "digest", mod_auth_check_digest, NULL }; 212 static const http_auth_scheme_t http_auth_scheme_extern = { "extern", mod_auth_check_extern, NULL }; 213 plugin_data *p = calloc(1, sizeof(*p)); 214 force_assert(p); 215 216 /* register http_auth_scheme_* */ 217 http_auth_scheme_basic.p_d = p; 218 http_auth_scheme_set(&http_auth_scheme_basic); 219 http_auth_scheme_digest.p_d = p; 220 http_auth_scheme_set(&http_auth_scheme_digest); 221 http_auth_scheme_set(&http_auth_scheme_extern); 222 223 return p; 224 } 225 226 FREE_FUNC(mod_auth_free) { 227 plugin_data * const p = p_d; 228 if (NULL == p->cvlist) return; 229 /* (init i to 0 if global context; to 1 to skip empty global context) */ 230 for (int i = !p->cvlist[0].v.u2[1], used = p->nconfig; i < used; ++i) { 231 config_plugin_value_t *cpv = p->cvlist + p->cvlist[i].v.u2[0]; 232 for (; -1 != cpv->k_id; ++cpv) { 233 if (cpv->vtype != T_CONFIG_LOCAL || NULL == cpv->v.v) continue; 234 switch (cpv->k_id) { 235 case 1: /* auth.require */ 236 array_free(cpv->v.v); 237 break; 238 case 3: /* auth.cache */ 239 http_auth_cache_free(cpv->v.v); 240 break; 241 default: 242 break; 243 } 244 } 245 } 246 } 247 248 /* data type for mod_auth structured data 249 * (parsed from auth.require array of strings) */ 250 typedef struct { 251 DATA_UNSET; 252 http_auth_require_t *require; 253 } data_auth; 254 255 static void data_auth_free(data_unset *d) 256 { 257 data_auth * const dauth = (data_auth *)d; 258 free(dauth->key.ptr); 259 http_auth_require_free(dauth->require); 260 free(dauth); 261 } 262 263 static data_auth *data_auth_init(void) 264 { 265 static const struct data_methods fn = { 266 NULL, /* copy must not be called on this data */ 267 data_auth_free, 268 NULL, /* insert_dup must not be called on this data */ 269 NULL /* print must not be called on this data */ 270 }; 271 data_auth * const dauth = calloc(1, sizeof(*dauth)); 272 force_assert(NULL != dauth); 273 dauth->type = TYPE_OTHER; 274 dauth->fn = &fn; 275 276 dauth->require = http_auth_require_init(); 277 278 return dauth; 279 } 280 281 static int mod_auth_algorithm_parse(http_auth_info_t *ai, const char *s) { 282 size_t len; 283 if (NULL == s) { 284 ai->dalgo = HTTP_AUTH_DIGEST_MD5; 285 ai->dlen = HTTP_AUTH_DIGEST_MD5_BINLEN; 286 return 1; 287 } 288 289 len = strlen(s); 290 if (len > 5 291 && (s[len-5] ) == '-' 292 && (s[len-4] | 0x20) == 's' 293 && (s[len-3] | 0x20) == 'e' 294 && (s[len-2] | 0x20) == 's' 295 && (s[len-1] | 0x20) == 's') { 296 ai->dalgo = HTTP_AUTH_DIGEST_SESS; 297 len -= 5; 298 } 299 else { 300 ai->dalgo = HTTP_AUTH_DIGEST_NONE; 301 } 302 303 if (3 == len 304 && 'm' == (s[0] | 0x20) 305 && 'd' == (s[1] | 0x20) 306 && '5' == (s[2] )) { 307 ai->dalgo |= HTTP_AUTH_DIGEST_MD5; 308 ai->dlen = HTTP_AUTH_DIGEST_MD5_BINLEN; 309 return 1; 310 } 311 #ifdef USE_LIB_CRYPTO 312 else if (len >= 7 313 && 's' == (s[0] | 0x20) 314 && 'h' == (s[1] | 0x20) 315 && 'a' == (s[2] | 0x20) 316 && '-' == (s[3] )) { 317 if (len == 7 && s[4] == '2' && s[5] == '5' && s[6] == '6') { 318 ai->dalgo |= HTTP_AUTH_DIGEST_SHA256; 319 ai->dlen = HTTP_AUTH_DIGEST_SHA256_BINLEN; 320 return 1; 321 } 322 #ifdef USE_LIB_CRYPTO_SHA512_256 323 if (len == 11 && 0 == memcmp(s+4, "512-256", 7)) { 324 ai->dalgo |= HTTP_AUTH_DIGEST_SHA512_256; 325 ai->dlen = HTTP_AUTH_DIGEST_SHA512_256_BINLEN; 326 return 1; 327 } 328 #endif 329 } 330 #endif 331 return 0; /*(error)*/ 332 } 333 334 static int mod_auth_algorithms_parse(int *algorithm, buffer *algos) { 335 for (char *s = algos->ptr, *p; s; s = p ? p+1 : NULL) { 336 http_auth_info_t ai; 337 int rc; 338 p = strchr(s, '|'); 339 if (p) *p = '\0'; 340 rc = mod_auth_algorithm_parse(&ai, s); 341 if (p) *p = '|'; 342 if (!rc) return 0; 343 *algorithm |= ai.dalgo; 344 } 345 return 1; 346 } 347 348 static int mod_auth_require_parse (http_auth_require_t * const require, const buffer *b, log_error_st *errh) 349 { 350 /* user=name1|user=name2|group=name3|host=name4 */ 351 352 const char *str = b->ptr; 353 const char *p; 354 355 if (buffer_is_equal_string(b, CONST_STR_LEN("valid-user"))) { 356 require->valid_user = 1; 357 return 1; /* success */ 358 } 359 360 do { 361 const char *eq; 362 size_t len; 363 p = strchr(str, '|'); 364 len = NULL != p ? (size_t)(p - str) : strlen(str); 365 eq = memchr(str, '=', len); 366 if (NULL == eq) { 367 log_error(errh, __FILE__, __LINE__, 368 "error parsing auth.require 'require' field: missing '=' " 369 "(expecting \"valid-user\" or \"user=a|user=b|group=g|host=h\"). " 370 "error value: %s error near: %s", b->ptr, str); 371 return 0; 372 } 373 if (eq[1] == '|' || eq[1] == '\0') { 374 log_error(errh, __FILE__, __LINE__, 375 "error parsing auth.require 'require' field: " 376 "missing token after '=' " 377 "(expecting \"valid-user\" or \"user=a|user=b|group=g|host=h\"). " 378 "error value: %s error near: %s", b->ptr, str); 379 return 0; 380 } 381 382 switch ((int)(eq - str)) { 383 case 4: 384 if (0 == memcmp(str, CONST_STR_LEN("user"))) { 385 /*("user=" is 5)*/ 386 array_set_key_value(&require->user, str+5, len-5, CONST_STR_LEN("")); 387 continue; 388 } 389 else if (0 == memcmp(str, CONST_STR_LEN("host"))) { 390 /*("host=" is 5)*/ 391 array_set_key_value(&require->host, str+5, len-5, CONST_STR_LEN("")); 392 log_error(errh, __FILE__, __LINE__, 393 "warning parsing auth.require 'require' field: " 394 "'host' not implemented; field value: %s", b->ptr); 395 continue; 396 } 397 break; /* to error */ 398 case 5: 399 if (0 == memcmp(str, CONST_STR_LEN("group"))) { 400 /*("group=" is 6)*/ 401 array_set_key_value(&require->group, str+6, len-6, CONST_STR_LEN("")); 402 #if 0/*(supported by mod_authn_ldap, but not all other backends)*/ 403 log_error(errh, __FILE__, __LINE__, 404 "warning parsing auth.require 'require' field: " 405 "'group' not implemented; field value: %s", b->ptr); 406 #endif 407 continue; 408 } 409 break; /* to error */ 410 case 10: 411 if (0 == memcmp(str, CONST_STR_LEN("valid-user"))) { 412 log_error(errh, __FILE__, __LINE__, 413 "error parsing auth.require 'require' field: " 414 "valid user can not be combined with other require rules " 415 "(expecting \"valid-user\" or " 416 "\"user=a|user=b|group=g|host=h\"). error value: %s", b->ptr); 417 return 0; 418 } 419 break; /* to error */ 420 default: 421 break; /* to error */ 422 } 423 424 log_error(errh, __FILE__, __LINE__, 425 "error parsing auth.require 'require' field: " 426 "invalid/unsupported token " 427 "(expecting \"valid-user\" or \"user=a|user=b|group=g|host=h\"). " 428 "error value: %s error near: %s", b->ptr, str); 429 return 0; 430 431 } while (p && *((str = p+1))); 432 433 return 1; /* success */ 434 } 435 436 static handler_t mod_auth_require_parse_array(const array *value, array * const auth_require, log_error_st *errh) 437 { 438 for (uint32_t n = 0; n < value->used; ++n) { 439 size_t m; 440 data_array *da_file = (data_array *)value->data[n]; 441 const buffer *method = NULL, *realm = NULL, *require = NULL; 442 const buffer *nonce_secret = NULL; 443 const http_auth_scheme_t *auth_scheme; 444 buffer *algos = NULL; 445 int algorithm = HTTP_AUTH_DIGEST_SESS; 446 447 if (!array_is_kvstring(&da_file->value)) { 448 log_error(errh, __FILE__, __LINE__, 449 "unexpected value for auth.require; expected " 450 "auth.require = ( \"urlpath\" => ( \"option\" => \"value\" ) )"); 451 452 return HANDLER_ERROR; 453 } 454 455 for (m = 0; m < da_file->value.used; m++) { 456 if (da_file->value.data[m]->type == TYPE_STRING) { 457 data_string *ds = (data_string *)da_file->value.data[m]; 458 if (buffer_is_equal_string(&ds->key, CONST_STR_LEN("method"))) { 459 method = &ds->value; 460 } else if (buffer_is_equal_string(&ds->key, CONST_STR_LEN("realm"))) { 461 realm = &ds->value; 462 } else if (buffer_is_equal_string(&ds->key, CONST_STR_LEN("require"))) { 463 require = &ds->value; 464 } else if (buffer_is_equal_string(&ds->key, CONST_STR_LEN("algorithm"))) { 465 algos = &ds->value; 466 } else if (buffer_is_equal_string(&ds->key, CONST_STR_LEN("nonce_secret")) 467 || buffer_is_equal_string(&ds->key, CONST_STR_LEN("nonce-secret"))) { 468 nonce_secret = &ds->value; 469 } else { 470 log_error(errh, __FILE__, __LINE__, 471 "the field is unknown in: " 472 "auth.require = ( \"...\" => ( ..., -> \"%s\" <- => \"...\" ) )", 473 da_file->value.data[m]->key.ptr); 474 475 return HANDLER_ERROR; 476 } 477 } else { 478 log_error(errh, __FILE__, __LINE__, 479 "a string was expected for: " 480 "auth.require = ( \"...\" => ( ..., -> \"%s\" <- => \"...\" ) )", 481 da_file->value.data[m]->key.ptr); 482 483 return HANDLER_ERROR; 484 } 485 } 486 487 if (buffer_string_is_empty(method)) { 488 log_error(errh, __FILE__, __LINE__, 489 "the method field is missing or blank in: " 490 "auth.require = ( \"...\" => ( ..., \"method\" => \"...\" ) )"); 491 return HANDLER_ERROR; 492 } else { 493 auth_scheme = http_auth_scheme_get(method); 494 if (NULL == auth_scheme) { 495 log_error(errh, __FILE__, __LINE__, 496 "unknown method %s (e.g. \"basic\", \"digest\" or \"extern\") in " 497 "auth.require = ( \"...\" => ( ..., \"method\" => \"...\") )", method->ptr); 498 return HANDLER_ERROR; 499 } 500 } 501 502 if (buffer_is_empty(realm)) { 503 log_error(errh, __FILE__, __LINE__, 504 "the realm field is missing in: " 505 "auth.require = ( \"...\" => ( ..., \"realm\" => \"...\" ) )"); 506 return HANDLER_ERROR; 507 } 508 509 if (buffer_string_is_empty(require)) { 510 log_error(errh, __FILE__, __LINE__, 511 "the require field is missing or blank in: " 512 "auth.require = ( \"...\" => ( ..., \"require\" => \"...\" ) )"); 513 return HANDLER_ERROR; 514 } 515 516 if (buffer_string_is_empty(algos)) { 517 algorithm |= HTTP_AUTH_DIGEST_MD5; 518 } else if (!mod_auth_algorithms_parse(&algorithm, algos)) { 519 log_error(errh, __FILE__, __LINE__, 520 "invalid algorithm in: " 521 "auth.require = ( \"...\" => ( ..., \"algorithm\" => \"...\" ) )"); 522 return HANDLER_ERROR; 523 } 524 525 if (require) { /*(always true at this point)*/ 526 data_auth * const dauth = data_auth_init(); 527 buffer_copy_buffer(&dauth->key, &da_file->key); 528 dauth->require->scheme = auth_scheme; 529 dauth->require->algorithm = algorithm; 530 dauth->require->realm = realm; 531 dauth->require->nonce_secret = nonce_secret; /*(NULL is ok)*/ 532 if (!mod_auth_require_parse(dauth->require, require, errh)) { 533 dauth->fn->free((data_unset *)dauth); 534 return HANDLER_ERROR; 535 } 536 array_insert_unique(auth_require, (data_unset *)dauth); 537 } 538 } 539 540 return HANDLER_GO_ON; 541 } 542 543 static void mod_auth_merge_config_cpv(plugin_config * const pconf, const config_plugin_value_t * const cpv) { 544 switch (cpv->k_id) { /* index into static config_plugin_keys_t cpk[] */ 545 case 0: /* auth.backend */ 546 if (cpv->vtype == T_CONFIG_LOCAL) 547 pconf->auth_backend = cpv->v.v; 548 break; 549 case 1: /* auth.require */ 550 if (cpv->vtype == T_CONFIG_LOCAL) 551 pconf->auth_require = cpv->v.v; 552 break; 553 case 2: /* auth.extern-authn */ 554 pconf->auth_extern_authn = cpv->v.u; 555 break; 556 case 3: /* auth.cache */ 557 if (cpv->vtype == T_CONFIG_LOCAL) 558 pconf->auth_cache = cpv->v.v; 559 break; 560 default:/* should not happen */ 561 return; 562 } 563 } 564 565 static void mod_auth_merge_config(plugin_config * const pconf, const config_plugin_value_t *cpv) { 566 do { 567 mod_auth_merge_config_cpv(pconf, cpv); 568 } while ((++cpv)->k_id != -1); 569 } 570 571 static void mod_auth_patch_config(request_st * const r, plugin_data * const p) { 572 p->conf = p->defaults; /* copy small struct instead of memcpy() */ 573 /*memcpy(&p->conf, &p->defaults, sizeof(plugin_config));*/ 574 for (int i = 1, used = p->nconfig; i < used; ++i) { 575 if (config_check_cond(r, (uint32_t)p->cvlist[i].k_id)) 576 mod_auth_merge_config(&p->conf, p->cvlist + p->cvlist[i].v.u2[0]); 577 } 578 } 579 580 SETDEFAULTS_FUNC(mod_auth_set_defaults) { 581 static const config_plugin_keys_t cpk[] = { 582 { CONST_STR_LEN("auth.backend"), 583 T_CONFIG_STRING, 584 T_CONFIG_SCOPE_CONNECTION } 585 ,{ CONST_STR_LEN("auth.require"), 586 T_CONFIG_ARRAY_KVARRAY, 587 T_CONFIG_SCOPE_CONNECTION } 588 ,{ CONST_STR_LEN("auth.extern-authn"), 589 T_CONFIG_BOOL, 590 T_CONFIG_SCOPE_CONNECTION } 591 ,{ CONST_STR_LEN("auth.cache"), 592 T_CONFIG_ARRAY, 593 T_CONFIG_SCOPE_CONNECTION } 594 ,{ NULL, 0, 595 T_CONFIG_UNSET, 596 T_CONFIG_SCOPE_UNSET } 597 }; 598 599 plugin_data * const p = p_d; 600 if (!config_plugin_values_init(srv, p, cpk, "mod_auth")) 601 return HANDLER_ERROR; 602 603 /* process and validate config directives 604 * (init i to 0 if global context; to 1 to skip empty global context) */ 605 for (int i = !p->cvlist[0].v.u2[1]; i < p->nconfig; ++i) { 606 config_plugin_value_t *cpv = p->cvlist + p->cvlist[i].v.u2[0]; 607 for (; -1 != cpv->k_id; ++cpv) { 608 switch (cpv->k_id) { 609 case 0: /* auth.backend */ 610 if (!buffer_string_is_empty(cpv->v.b)) { 611 const http_auth_backend_t * const auth_backend = 612 http_auth_backend_get(cpv->v.b); 613 if (NULL == auth_backend) { 614 log_error(srv->errh, __FILE__, __LINE__, 615 "auth.backend not supported: %s", cpv->v.b->ptr); 616 return HANDLER_ERROR; 617 } 618 *(const http_auth_backend_t **)&cpv->v.v = auth_backend; 619 cpv->vtype = T_CONFIG_LOCAL; 620 } 621 break; 622 case 1: /* auth.require */ 623 { 624 array * const a = array_init(4); 625 if (HANDLER_GO_ON != 626 mod_auth_require_parse_array(cpv->v.a, a, srv->errh)) { 627 array_free(a); 628 return HANDLER_ERROR; 629 } 630 cpv->v.a = a; 631 cpv->vtype = T_CONFIG_LOCAL; 632 } 633 break; 634 case 2: /* auth.extern-authn */ 635 break; 636 case 3: /* auth.cache */ 637 cpv->v.v = http_auth_cache_init(cpv->v.a); 638 cpv->vtype = T_CONFIG_LOCAL; 639 break; 640 default:/* should not happen */ 641 break; 642 } 643 } 644 } 645 646 /* initialize p->defaults from global config context */ 647 if (p->nconfig > 0 && p->cvlist->v.u2[1]) { 648 const config_plugin_value_t *cpv = p->cvlist + p->cvlist->v.u2[0]; 649 if (-1 != cpv->k_id) 650 mod_auth_merge_config(&p->defaults, cpv); 651 } 652 653 return HANDLER_GO_ON; 654 } 655 656 static handler_t mod_auth_uri_handler(request_st * const r, void *p_d) { 657 plugin_data *p = p_d; 658 data_auth *dauth; 659 660 mod_auth_patch_config(r, p); 661 662 if (p->conf.auth_require == NULL) return HANDLER_GO_ON; 663 664 /* search auth directives for first prefix match against URL path */ 665 /* if we have a case-insensitive FS we have to lower-case the URI here too */ 666 dauth = (!r->conf.force_lowercase_filenames) 667 ? (data_auth *)array_match_key_prefix(p->conf.auth_require, &r->uri.path) 668 : (data_auth *)array_match_key_prefix_nc(p->conf.auth_require, &r->uri.path); 669 if (NULL == dauth) return HANDLER_GO_ON; 670 671 { 672 const http_auth_scheme_t * const scheme = dauth->require->scheme; 673 if (p->conf.auth_extern_authn) { 674 const buffer *vb = http_header_env_get(r, CONST_STR_LEN("REMOTE_USER")); 675 if (NULL != vb && http_auth_match_rules(dauth->require, vb->ptr, NULL, NULL)) { 676 return HANDLER_GO_ON; 677 } 678 } 679 return scheme->checkfn(r, scheme->p_d, dauth->require, p->conf.auth_backend); 680 } 681 } 682 683 684 int mod_auth_plugin_init(plugin *p); 685 int mod_auth_plugin_init(plugin *p) { 686 p->version = LIGHTTPD_VERSION_ID; 687 p->name = "auth"; 688 p->init = mod_auth_init; 689 p->set_defaults = mod_auth_set_defaults; 690 p->handle_trigger = mod_auth_periodic; 691 p->handle_uri_clean = mod_auth_uri_handler; 692 p->cleanup = mod_auth_free; 693 694 return 0; 695 } 696 697 698 699 700 /* 701 * auth schemes (basic, digest, extern) 702 * 703 * (could be in separate file from mod_auth.c as long as registration occurs) 704 */ 705 706 #include "sys-crypto-md.h" 707 #include "base64.h" 708 #include "rand.h" 709 #include "http_header.h" 710 711 __attribute_cold__ 712 static handler_t mod_auth_send_400_bad_request(request_st * const r) { 713 714 /* a field was missing or invalid */ 715 r->http_status = 400; /* Bad Request */ 716 r->handler_module = NULL; 717 718 return HANDLER_FINISHED; 719 } 720 721 static handler_t mod_auth_send_401_unauthorized_basic(request_st * const r, const buffer * const realm) { 722 r->http_status = 401; 723 r->handler_module = NULL; 724 725 buffer * const tb = r->tmp_buf; 726 buffer_copy_string_len(tb, CONST_STR_LEN("Basic realm=\"")); 727 buffer_append_string_buffer(tb, realm); 728 buffer_append_string_len(tb, CONST_STR_LEN("\", charset=\"UTF-8\"")); 729 730 http_header_response_set(r, HTTP_HEADER_WWW_AUTHENTICATE, 731 CONST_STR_LEN("WWW-Authenticate"), 732 CONST_BUF_LEN(tb)); 733 734 return HANDLER_FINISHED; 735 } 736 737 static handler_t mod_auth_check_basic(request_st * const r, void *p_d, const struct http_auth_require_t * const require, const struct http_auth_backend_t * const backend) { 738 const buffer *b = http_header_request_get(r, HTTP_HEADER_AUTHORIZATION, CONST_STR_LEN("Authorization")); 739 buffer *username; 740 char *pw; 741 handler_t rc = HANDLER_UNSET; 742 743 if (NULL == backend || NULL == backend->basic) { 744 if (NULL == backend) 745 log_error(r->conf.errh, __FILE__, __LINE__, 746 "auth.backend not configured for %s", r->uri.path.ptr); 747 else 748 log_error(r->conf.errh, __FILE__, __LINE__, 749 "auth.require \"method\" => \"basic\" invalid " 750 "(try \"digest\"?) for %s", 751 r->uri.path.ptr); 752 r->http_status = 500; 753 r->handler_module = NULL; 754 return HANDLER_FINISHED; 755 } 756 757 if (NULL == b) { 758 return mod_auth_send_401_unauthorized_basic(r, require->realm); 759 } 760 761 if (!buffer_eq_icase_ssn(b->ptr, CONST_STR_LEN("Basic "))) { 762 return mod_auth_send_401_unauthorized_basic(r, require->realm); 763 } 764 #ifdef __COVERITY__ 765 if (buffer_string_length(b) < sizeof("Basic ")-1) { 766 return mod_auth_send_400_bad_request(r); 767 } 768 #endif 769 770 username = buffer_init(); 771 772 /* coverity[overflow_sink : FALSE] */ 773 if (!buffer_append_base64_decode(username, b->ptr+sizeof("Basic ")-1, buffer_string_length(b)-(sizeof("Basic ")-1), BASE64_STANDARD)) { 774 log_error(r->conf.errh, __FILE__, __LINE__, "decoding base64-string failed %s", b->ptr+sizeof("Basic ")-1); 775 776 buffer_free(username); 777 return mod_auth_send_400_bad_request(r); 778 } 779 780 /* r2 == user:password */ 781 if (NULL == (pw = strchr(username->ptr, ':'))) { 782 log_error(r->conf.errh, __FILE__, __LINE__, "missing ':' in %s", username->ptr); 783 784 buffer_free(username); 785 return mod_auth_send_400_bad_request(r); 786 } 787 788 uint32_t pwlen = buffer_string_length(username); 789 buffer_string_set_length(username, pw - username->ptr); 790 pw++; 791 pwlen -= (pw - username->ptr); 792 793 plugin_data * const p = p_d; 794 splay_tree ** sptree = p->conf.auth_cache 795 ? &p->conf.auth_cache->sptree 796 : NULL; 797 http_auth_cache_entry *ae = NULL; 798 int ndx = -1; 799 if (sptree) { 800 ndx = http_auth_cache_hash(require, CONST_BUF_LEN(username)); 801 ae = http_auth_cache_query(sptree, ndx); 802 if (ae && ae->require == require 803 && buffer_is_equal_string(username, ae->username, ae->ulen)) 804 rc = http_auth_const_time_memeq_pad(ae->pwdigest, ae->dlen, 805 pw, pwlen) 806 ? HANDLER_GO_ON 807 : HANDLER_ERROR; 808 else /*(not found or hash collision)*/ 809 ae = NULL; 810 } 811 812 if (NULL == ae) /* (HANDLER_UNSET == rc) */ 813 rc = backend->basic(r, backend->p_d, require, username, pw); 814 815 switch (rc) { 816 case HANDLER_GO_ON: 817 http_auth_setenv(r, CONST_BUF_LEN(username), CONST_STR_LEN("Basic")); 818 if (sptree && NULL == ae) { /*(cache (new) successful result)*/ 819 ae = http_auth_cache_entry_init(require, 0, CONST_BUF_LEN(username), 820 pw, pwlen); 821 http_auth_cache_insert(sptree, ndx, ae, http_auth_cache_entry_free); 822 } 823 break; 824 case HANDLER_WAIT_FOR_EVENT: 825 case HANDLER_FINISHED: 826 break; 827 case HANDLER_ERROR: 828 default: 829 log_error(r->conf.errh, __FILE__, __LINE__, 830 "password doesn't match for %s username: %s IP: %s", 831 r->uri.path.ptr, username->ptr, r->con->dst_addr_buf->ptr); 832 r->keep_alive = 0; /*(disable keep-alive if bad password)*/ 833 rc = HANDLER_UNSET; 834 break; 835 } 836 837 buffer_free(username); 838 return (HANDLER_UNSET != rc) ? rc : mod_auth_send_401_unauthorized_basic(r, require->realm); 839 } 840 841 842 #ifdef USE_LIB_CRYPTO 843 844 static void mod_auth_digest_mutate_sha256(http_auth_info_t *ai, const char *m, const char *uri, const char *nonce, const char *cnonce, const char *nc, const char *qop) { 845 SHA256_CTX ctx; 846 char a1[HTTP_AUTH_DIGEST_SHA256_BINLEN*2+1]; 847 char a2[HTTP_AUTH_DIGEST_SHA256_BINLEN*2+1]; 848 849 if (ai->dalgo & HTTP_AUTH_DIGEST_SESS) { 850 SHA256_Init(&ctx); 851 li_tohex(a1, sizeof(a1), (const char *)ai->digest, ai->dlen); 852 SHA256_Update(&ctx, (unsigned char *)a1, sizeof(a1)-1); 853 SHA256_Update(&ctx, CONST_STR_LEN(":")); 854 SHA256_Update(&ctx, (unsigned char *)nonce, strlen(nonce)); 855 SHA256_Update(&ctx, CONST_STR_LEN(":")); 856 SHA256_Update(&ctx, (unsigned char *)cnonce, strlen(cnonce)); 857 SHA256_Final(ai->digest, &ctx); 858 } 859 860 li_tohex(a1, sizeof(a1), (const char *)ai->digest, ai->dlen); 861 862 /* calculate H(A2) */ 863 SHA256_Init(&ctx); 864 SHA256_Update(&ctx, (unsigned char *)m, strlen(m)); 865 SHA256_Update(&ctx, CONST_STR_LEN(":")); 866 SHA256_Update(&ctx, (unsigned char *)uri, strlen(uri)); 867 #if 0 868 /* qop=auth-int not supported, already checked in caller */ 869 if (qop && buffer_eq_icase_ss(qop, strlen(qop), CONST_STR_LEN("auth-int"))){ 870 SHA256_Update(&ctx, CONST_STR_LEN(":")); 871 SHA256_Update(&ctx, (unsigned char *) [body checksum], ai->dlen*2); 872 } 873 #endif 874 SHA256_Final(ai->digest, &ctx); 875 li_tohex(a2, sizeof(a2), (const char *)ai->digest, ai->dlen); 876 877 /* calculate response */ 878 SHA256_Init(&ctx); 879 SHA256_Update(&ctx, (unsigned char *)a1, sizeof(a1)-1); 880 SHA256_Update(&ctx, CONST_STR_LEN(":")); 881 SHA256_Update(&ctx, (unsigned char *)nonce, strlen(nonce)); 882 SHA256_Update(&ctx, CONST_STR_LEN(":")); 883 if (qop && *qop) { 884 SHA256_Update(&ctx, (unsigned char *)nc, strlen(nc)); 885 SHA256_Update(&ctx, CONST_STR_LEN(":")); 886 SHA256_Update(&ctx, (unsigned char *)cnonce, strlen(cnonce)); 887 SHA256_Update(&ctx, CONST_STR_LEN(":")); 888 SHA256_Update(&ctx, (unsigned char *)qop, strlen(qop)); 889 SHA256_Update(&ctx, CONST_STR_LEN(":")); 890 } 891 SHA256_Update(&ctx, (unsigned char *)a2, sizeof(a2)-1); 892 SHA256_Final(ai->digest, &ctx); 893 } 894 895 static void mod_auth_digest_nonce_sha256(buffer *b, time_t cur_ts, int rnd, const buffer *secret) { 896 SHA256_CTX ctx; 897 size_t len; 898 unsigned char h[HTTP_AUTH_DIGEST_SHA256_BINLEN]; 899 char hh[HTTP_AUTH_DIGEST_SHA256_BINLEN*2+1]; 900 force_assert(sizeof(hh) >= LI_ITOSTRING_LENGTH); 901 SHA256_Init(&ctx); 902 len = li_itostrn(hh, sizeof(hh), cur_ts); 903 SHA256_Update(&ctx, (unsigned char *)hh, len); 904 len = li_itostrn(hh, sizeof(hh), rnd); 905 SHA256_Update(&ctx, (unsigned char *)hh, len); 906 if (secret) { 907 len = buffer_string_length(secret); 908 SHA256_Update(&ctx, (unsigned char *)secret->ptr, len); 909 } 910 SHA256_Final(h, &ctx); 911 li_tohex(hh, sizeof(hh), (const char *)h, sizeof(h)); 912 buffer_append_string_len(b, hh, sizeof(hh)-1); 913 } 914 915 #ifdef USE_LIB_CRYPTO_SHA512_256 916 917 static void mod_auth_digest_mutate_sha512_256(http_auth_info_t *ai, const char *m, const char *uri, const char *nonce, const char *cnonce, const char *nc, const char *qop) { 918 SHA512_CTX ctx; 919 char a1[HTTP_AUTH_DIGEST_SHA512_256_BINLEN*2+1]; 920 char a2[HTTP_AUTH_DIGEST_SHA512_256_BINLEN*2+1]; 921 922 if (ai->dalgo & HTTP_AUTH_DIGEST_SESS) { 923 SHA512_256_Init(&ctx); 924 li_tohex(a1, sizeof(a1), (const char *)ai->digest, ai->dlen); 925 SHA512_256_Update(&ctx, (unsigned char *)a1, sizeof(a1)-1); 926 SHA512_256_Update(&ctx, CONST_STR_LEN(":")); 927 SHA512_256_Update(&ctx, (unsigned char *)nonce, strlen(nonce)); 928 SHA512_256_Update(&ctx, CONST_STR_LEN(":")); 929 SHA512_256_Update(&ctx, (unsigned char *)cnonce, strlen(cnonce)); 930 SHA512_256_Final(ai->digest, &ctx); 931 } 932 933 li_tohex(a1, sizeof(a1), (const char *)ai->digest, ai->dlen); 934 935 /* calculate H(A2) */ 936 SHA512_256_Init(&ctx); 937 SHA512_256_Update(&ctx, (unsigned char *)m, strlen(m)); 938 SHA512_256_Update(&ctx, CONST_STR_LEN(":")); 939 SHA512_256_Update(&ctx, (unsigned char *)uri, strlen(uri)); 940 #if 0 941 /* qop=auth-int not supported, already checked in caller */ 942 if (qop && buffer_eq_icase_ss(qop, strlen(qop), CONST_STR_LEN("auth-int"))){ 943 SHA512_256_Update(&ctx, CONST_STR_LEN(":")); 944 SHA512_256_Update(&ctx, (unsigned char *)[body checksum], ai->dlen*2); 945 } 946 #endif 947 SHA512_256_Final(ai->digest, &ctx); 948 li_tohex(a2, sizeof(a2), (const char *)ai->digest, ai->dlen); 949 950 /* calculate response */ 951 SHA512_256_Init(&ctx); 952 SHA512_256_Update(&ctx, (unsigned char *)a1, sizeof(a1)-1); 953 SHA512_256_Update(&ctx, CONST_STR_LEN(":")); 954 SHA512_256_Update(&ctx, (unsigned char *)nonce, strlen(nonce)); 955 SHA512_256_Update(&ctx, CONST_STR_LEN(":")); 956 if (qop && *qop) { 957 SHA512_256_Update(&ctx, (unsigned char *)nc, strlen(nc)); 958 SHA512_256_Update(&ctx, CONST_STR_LEN(":")); 959 SHA512_256_Update(&ctx, (unsigned char *)cnonce, strlen(cnonce)); 960 SHA512_256_Update(&ctx, CONST_STR_LEN(":")); 961 SHA512_256_Update(&ctx, (unsigned char *)qop, strlen(qop)); 962 SHA512_256_Update(&ctx, CONST_STR_LEN(":")); 963 } 964 SHA512_256_Update(&ctx, (unsigned char *)a2, sizeof(a2)-1); 965 SHA512_256_Final(ai->digest, &ctx); 966 } 967 968 static void mod_auth_digest_nonce_sha512_256(buffer *b, time_t cur_ts, int rnd, const buffer *secret) { 969 SHA512_CTX ctx; 970 size_t len; 971 unsigned char h[HTTP_AUTH_DIGEST_SHA512_256_BINLEN]; 972 char hh[HTTP_AUTH_DIGEST_SHA512_256_BINLEN*2+1]; 973 force_assert(sizeof(hh) >= LI_ITOSTRING_LENGTH); 974 SHA512_256_Init(&ctx); 975 len = li_itostrn(hh, sizeof(hh), cur_ts); 976 SHA512_256_Update(&ctx, (unsigned char *)hh, len); 977 len = li_itostrn(hh, sizeof(hh), rnd); 978 SHA512_256_Update(&ctx, (unsigned char *)hh, len); 979 if (secret) { 980 len = buffer_string_length(secret); 981 SHA512_256_Update(&ctx, (unsigned char *)secret->ptr, len); 982 } 983 SHA512_256_Final(h, &ctx); 984 li_tohex(hh, sizeof(hh), (const char *)h, sizeof(h)); 985 buffer_append_string_len(b, hh, sizeof(hh)-1); 986 } 987 988 #endif /* USE_LIB_CRYPTO_SHA512_256 */ 989 990 #endif /* USE_LIB_CRYPTO */ 991 992 static void mod_auth_digest_mutate_md5(http_auth_info_t *ai, const char *m, const char *uri, const char *nonce, const char *cnonce, const char *nc, const char *qop) { 993 li_MD5_CTX ctx; 994 char a1[HTTP_AUTH_DIGEST_MD5_BINLEN*2+1]; 995 char a2[HTTP_AUTH_DIGEST_MD5_BINLEN*2+1]; 996 997 if (ai->dalgo & HTTP_AUTH_DIGEST_SESS) { 998 li_MD5_Init(&ctx); 999 /* http://www.rfc-editor.org/errata_search.php?rfc=2617 1000 * Errata ID: 1649 */ 1001 li_tohex(a1, sizeof(a1), (const char *)ai->digest, ai->dlen); 1002 li_MD5_Update(&ctx, (unsigned char *)a1, sizeof(a1)-1); 1003 li_MD5_Update(&ctx, CONST_STR_LEN(":")); 1004 li_MD5_Update(&ctx, (unsigned char *)nonce, strlen(nonce)); 1005 li_MD5_Update(&ctx, CONST_STR_LEN(":")); 1006 li_MD5_Update(&ctx, (unsigned char *)cnonce, strlen(cnonce)); 1007 li_MD5_Final(ai->digest, &ctx); 1008 } 1009 1010 li_tohex(a1, sizeof(a1), (const char *)ai->digest, ai->dlen); 1011 1012 /* calculate H(A2) */ 1013 li_MD5_Init(&ctx); 1014 li_MD5_Update(&ctx, (unsigned char *)m, strlen(m)); 1015 li_MD5_Update(&ctx, CONST_STR_LEN(":")); 1016 li_MD5_Update(&ctx, (unsigned char *)uri, strlen(uri)); 1017 #if 0 1018 /* qop=auth-int not supported, already checked in caller */ 1019 if (qop && buffer_eq_icase_ss(qop, strlen(qop), CONST_STR_LEN("auth-int"))){ 1020 li_MD5_Update(&ctx, CONST_STR_LEN(":")); 1021 li_MD5_Update(&ctx, (unsigned char *) [body checksum], ai->dlen*2); 1022 } 1023 #endif 1024 li_MD5_Final(ai->digest, &ctx); 1025 li_tohex(a2, sizeof(a2), (const char *)ai->digest, ai->dlen); 1026 1027 /* calculate response */ 1028 li_MD5_Init(&ctx); 1029 li_MD5_Update(&ctx, (unsigned char *)a1, sizeof(a1)-1); 1030 li_MD5_Update(&ctx, CONST_STR_LEN(":")); 1031 li_MD5_Update(&ctx, (unsigned char *)nonce, strlen(nonce)); 1032 li_MD5_Update(&ctx, CONST_STR_LEN(":")); 1033 if (qop && *qop) { 1034 li_MD5_Update(&ctx, (unsigned char *)nc, strlen(nc)); 1035 li_MD5_Update(&ctx, CONST_STR_LEN(":")); 1036 li_MD5_Update(&ctx, (unsigned char *)cnonce, strlen(cnonce)); 1037 li_MD5_Update(&ctx, CONST_STR_LEN(":")); 1038 li_MD5_Update(&ctx, (unsigned char *)qop, strlen(qop)); 1039 li_MD5_Update(&ctx, CONST_STR_LEN(":")); 1040 } 1041 li_MD5_Update(&ctx, (unsigned char *)a2, sizeof(a2)-1); 1042 li_MD5_Final(ai->digest, &ctx); 1043 } 1044 1045 static void mod_auth_digest_nonce_md5(buffer *b, time_t cur_ts, int rnd, const buffer *secret) { 1046 li_MD5_CTX ctx; 1047 size_t len; 1048 unsigned char h[HTTP_AUTH_DIGEST_MD5_BINLEN]; 1049 char hh[HTTP_AUTH_DIGEST_MD5_BINLEN*2+1]; 1050 force_assert(sizeof(hh) >= LI_ITOSTRING_LENGTH); 1051 li_MD5_Init(&ctx); 1052 len = li_itostrn(hh, sizeof(hh), cur_ts); 1053 li_MD5_Update(&ctx, (unsigned char *)hh, len); 1054 len = li_itostrn(hh, sizeof(hh), rnd); 1055 li_MD5_Update(&ctx, (unsigned char *)hh, len); 1056 if (secret) { 1057 len = buffer_string_length(secret); 1058 li_MD5_Update(&ctx, (unsigned char *)secret->ptr, len); 1059 } 1060 li_MD5_Final(h, &ctx); 1061 li_tohex(hh, sizeof(hh), (const char *)h, sizeof(h)); 1062 buffer_append_string_len(b, hh, sizeof(hh)-1); 1063 } 1064 1065 static void mod_auth_digest_mutate(http_auth_info_t *ai, const char *m, const char *uri, const char *nonce, const char *cnonce, const char *nc, const char *qop) { 1066 if (ai->dalgo & HTTP_AUTH_DIGEST_MD5) 1067 mod_auth_digest_mutate_md5(ai, m, uri, nonce, cnonce, nc, qop); 1068 #ifdef USE_LIB_CRYPTO 1069 else if (ai->dalgo & HTTP_AUTH_DIGEST_SHA256) 1070 mod_auth_digest_mutate_sha256(ai, m, uri, nonce, cnonce, nc, qop); 1071 #ifdef USE_LIB_CRYPTO_SHA512_256 1072 else if (ai->dalgo & HTTP_AUTH_DIGEST_SHA512_256) 1073 mod_auth_digest_mutate_sha512_256(ai, m, uri, nonce, cnonce, nc, qop); 1074 #endif 1075 #endif 1076 } 1077 1078 static void mod_auth_append_nonce(buffer *b, time_t cur_ts, const struct http_auth_require_t *require, int dalgo, int *rndptr) { 1079 buffer_append_uint_hex(b, (uintmax_t)cur_ts); 1080 buffer_append_string_len(b, CONST_STR_LEN(":")); 1081 const buffer * const nonce_secret = require->nonce_secret; 1082 int rnd; 1083 if (NULL == nonce_secret) 1084 rnd = rndptr ? *rndptr : li_rand_pseudo(); 1085 else { /*(do not directly expose random number generator single value)*/ 1086 rndptr 1087 ? (void)(rnd = *rndptr) 1088 : li_rand_pseudo_bytes((unsigned char *)&rnd, sizeof(rnd)); 1089 buffer_append_uint_hex(b, (uintmax_t)rnd); 1090 buffer_append_string_len(b, CONST_STR_LEN(":")); 1091 } 1092 switch (dalgo) { 1093 #ifdef USE_LIB_CRYPTO 1094 #ifdef USE_LIB_CRYPTO_SHA512_256 1095 case HTTP_AUTH_DIGEST_SHA512_256: 1096 mod_auth_digest_nonce_sha512_256(b, cur_ts, rnd, nonce_secret); 1097 break; 1098 #endif 1099 case HTTP_AUTH_DIGEST_SHA256: 1100 mod_auth_digest_nonce_sha256(b, cur_ts, rnd, nonce_secret); 1101 break; 1102 #endif 1103 /*case HTTP_AUTH_DIGEST_MD5:*/ 1104 default: 1105 mod_auth_digest_nonce_md5(b, cur_ts, rnd, nonce_secret); 1106 break; 1107 } 1108 } 1109 1110 static void mod_auth_digest_www_authenticate(buffer *b, time_t cur_ts, const struct http_auth_require_t *require, int nonce_stale) { 1111 int algos = nonce_stale ? nonce_stale : require->algorithm; 1112 int n = 0; 1113 int algoid[3]; 1114 unsigned int algolen[3]; 1115 const char *algoname[3]; 1116 #ifdef USE_LIB_CRYPTO 1117 #ifdef USE_LIB_CRYPTO_SHA512_256 1118 if (algos & HTTP_AUTH_DIGEST_SHA512_256) { 1119 algoid[n] = HTTP_AUTH_DIGEST_SHA512_256; 1120 algoname[n] = "SHA-512-256"; 1121 algolen[n] = sizeof("SHA-512-256")-1; 1122 ++n; 1123 } 1124 #endif 1125 if (algos & HTTP_AUTH_DIGEST_SHA256) { 1126 algoid[n] = HTTP_AUTH_DIGEST_SHA256; 1127 algoname[n] = "SHA-256"; 1128 algolen[n] = sizeof("SHA-256")-1; 1129 ++n; 1130 } 1131 #endif 1132 if (algos & HTTP_AUTH_DIGEST_MD5) { 1133 algoid[n] = HTTP_AUTH_DIGEST_MD5; 1134 algoname[n] = "MD5"; 1135 algolen[n] = sizeof("MD5")-1; 1136 ++n; 1137 } 1138 1139 buffer_clear(b); 1140 for (int i = 0; i < n; ++i) { 1141 if (i > 0) { 1142 buffer_append_string_len(b,CONST_STR_LEN("\r\nWWW-Authenticate: ")); 1143 } 1144 buffer_append_string_len(b, CONST_STR_LEN("Digest realm=\"")); 1145 buffer_append_string_buffer(b, require->realm); 1146 buffer_append_string_len(b, CONST_STR_LEN("\", charset=\"UTF-8\", algorithm=")); 1147 buffer_append_string_len(b, algoname[i], algolen[i]); 1148 buffer_append_string_len(b, CONST_STR_LEN(", nonce=\"")); 1149 mod_auth_append_nonce(b, cur_ts, require, algoid[i], NULL); 1150 buffer_append_string_len(b, CONST_STR_LEN("\", qop=\"auth\"")); 1151 if (nonce_stale) { 1152 buffer_append_string_len(b, CONST_STR_LEN(", stale=true")); 1153 } 1154 } 1155 } 1156 1157 static void mod_auth_digest_authentication_info(buffer *b, time_t cur_ts, const struct http_auth_require_t *require, int dalgo) { 1158 buffer_clear(b); 1159 buffer_append_string_len(b, CONST_STR_LEN("nextnonce=\"")); 1160 mod_auth_append_nonce(b, cur_ts, require, dalgo, NULL); 1161 buffer_append_string_len(b, CONST_STR_LEN("\"")); 1162 } 1163 1164 typedef struct { 1165 const char *key; 1166 int key_len; 1167 char **ptr; 1168 } digest_kv; 1169 1170 static handler_t mod_auth_send_401_unauthorized_digest(request_st *r, const struct http_auth_require_t *require, int nonce_stale); 1171 1172 static handler_t mod_auth_check_digest(request_st * const r, void *p_d, const struct http_auth_require_t * const require, const struct http_auth_backend_t * const backend) { 1173 const buffer *vb = http_header_request_get(r, HTTP_HEADER_AUTHORIZATION, CONST_STR_LEN("Authorization")); 1174 1175 char *username = NULL; 1176 char *realm = NULL; 1177 char *nonce = NULL; 1178 char *uri = NULL; 1179 char *algorithm = NULL; 1180 char *qop = NULL; 1181 char *cnonce = NULL; 1182 char *nc = NULL; 1183 char *respons = NULL; 1184 1185 char *e, *c; 1186 int i; 1187 buffer *b; 1188 http_auth_info_t ai; 1189 unsigned char rdigest[HTTP_AUTH_DIGEST_SHA256_BINLEN]; 1190 1191 1192 /* init pointers */ 1193 #define S(x) \ 1194 x, sizeof(x)-1, NULL 1195 digest_kv dkv[10] = { 1196 { S("username=") }, 1197 { S("realm=") }, 1198 { S("nonce=") }, 1199 { S("uri=") }, 1200 { S("algorithm=") }, 1201 { S("qop=") }, 1202 { S("cnonce=") }, 1203 { S("nc=") }, 1204 { S("response=") }, 1205 1206 { NULL, 0, NULL } 1207 }; 1208 #undef S 1209 1210 dkv[0].ptr = &username; 1211 dkv[1].ptr = &realm; 1212 dkv[2].ptr = &nonce; 1213 dkv[3].ptr = &uri; 1214 dkv[4].ptr = &algorithm; 1215 dkv[5].ptr = &qop; 1216 dkv[6].ptr = &cnonce; 1217 dkv[7].ptr = &nc; 1218 dkv[8].ptr = &respons; 1219 1220 if (NULL == backend || NULL == backend->digest) { 1221 if (NULL == backend) 1222 log_error(r->conf.errh, __FILE__, __LINE__, 1223 "auth.backend not configured for %s", r->uri.path.ptr); 1224 else 1225 log_error(r->conf.errh, __FILE__, __LINE__, 1226 "auth.require \"method\" => \"digest\" invalid " 1227 "(try \"basic\"?) for %s", 1228 r->uri.path.ptr); 1229 r->http_status = 500; 1230 r->handler_module = NULL; 1231 return HANDLER_FINISHED; 1232 } 1233 1234 if (NULL == vb) { 1235 return mod_auth_send_401_unauthorized_digest(r, require, 0); 1236 } 1237 1238 if (!buffer_eq_icase_ssn(vb->ptr, CONST_STR_LEN("Digest "))) { 1239 return mod_auth_send_401_unauthorized_digest(r, require, 0); 1240 } else { 1241 size_t n = buffer_string_length(vb); 1242 #ifdef __COVERITY__ 1243 if (n < sizeof("Digest ")-1) { 1244 return mod_auth_send_400_bad_request(r); 1245 } 1246 #endif 1247 n -= (sizeof("Digest ")-1); 1248 b = buffer_init(); 1249 buffer_copy_string_len(b,vb->ptr+sizeof("Digest ")-1,n); 1250 } 1251 1252 /* parse credentials from client */ 1253 for (c = b->ptr; *c; c++) { 1254 /* skip whitespaces */ 1255 while (*c == ' ' || *c == '\t') c++; 1256 if (!*c) break; 1257 1258 for (i = 0; dkv[i].key; i++) { 1259 if ((0 == strncmp(c, dkv[i].key, dkv[i].key_len))) { 1260 if ((c[dkv[i].key_len] == '"') && 1261 (NULL != (e = strchr(c + dkv[i].key_len + 1, '"')))) { 1262 /* value with "..." */ 1263 *(dkv[i].ptr) = c + dkv[i].key_len + 1; 1264 c = e; 1265 1266 *e = '\0'; 1267 } else if (NULL != (e = strchr(c + dkv[i].key_len, ','))) { 1268 /* value without "...", terminated by ',' */ 1269 *(dkv[i].ptr) = c + dkv[i].key_len; 1270 c = e; 1271 1272 *e = '\0'; 1273 } else { 1274 /* value without "...", terminated by EOL */ 1275 *(dkv[i].ptr) = c + dkv[i].key_len; 1276 c += strlen(c) - 1; 1277 } 1278 break; 1279 } 1280 } 1281 } 1282 1283 /* check if everything is transmitted */ 1284 if (!username || 1285 !realm || 1286 !nonce || 1287 !uri || 1288 (qop && (!nc || !cnonce)) || 1289 !respons ) { 1290 /* missing field */ 1291 1292 log_error(r->conf.errh, __FILE__, __LINE__, 1293 "digest: missing field"); 1294 1295 buffer_free(b); 1296 return mod_auth_send_400_bad_request(r); 1297 } 1298 1299 ai.username = username; 1300 ai.ulen = strlen(username); 1301 ai.realm = realm; 1302 ai.rlen = strlen(realm); 1303 1304 if (!buffer_is_equal_string(require->realm, ai.realm, ai.rlen)) { 1305 log_error(r->conf.errh, __FILE__, __LINE__, 1306 "digest: realm mismatch"); 1307 buffer_free(b); 1308 return mod_auth_send_401_unauthorized_digest(r, require, 0); 1309 } 1310 1311 if (!mod_auth_algorithm_parse(&ai, algorithm) 1312 || !(require->algorithm & ai.dalgo & ~HTTP_AUTH_DIGEST_SESS)) { 1313 log_error(r->conf.errh, __FILE__, __LINE__, 1314 "digest: (%s): invalid", algorithm); 1315 buffer_free(b); 1316 return mod_auth_send_400_bad_request(r); 1317 } 1318 1319 /** 1320 * protect the md5-sess against missing cnonce and nonce 1321 */ 1322 if ((ai.dalgo & HTTP_AUTH_DIGEST_SESS) && (!nonce || !cnonce)) { 1323 log_error(r->conf.errh, __FILE__, __LINE__, 1324 "digest: (%s): missing field", algorithm); 1325 1326 buffer_free(b); 1327 return mod_auth_send_400_bad_request(r); 1328 } 1329 1330 { 1331 size_t resplen = strlen(respons); 1332 if (0 != http_auth_digest_hex2bin(respons, resplen, 1333 rdigest, sizeof(rdigest)) 1334 || resplen != (ai.dlen << 1)) { 1335 log_error(r->conf.errh, __FILE__, __LINE__, 1336 "digest: (%s): invalid format", respons); 1337 buffer_free(b); 1338 return mod_auth_send_400_bad_request(r); 1339 } 1340 } 1341 1342 if (qop && buffer_eq_icase_ss(qop, strlen(qop), CONST_STR_LEN("auth-int"))){ 1343 log_error(r->conf.errh, __FILE__, __LINE__, 1344 "digest: qop=auth-int not supported"); 1345 1346 buffer_free(b); 1347 return mod_auth_send_400_bad_request(r); 1348 } 1349 1350 /* detect if attacker is attempting to reuse valid digest for one uri 1351 * on a different request uri. Might also happen if intermediate proxy 1352 * altered client request line. (Altered request would not result in 1353 * the same digest as that calculated by the client.) 1354 * Internal redirects such as with mod_rewrite will modify request uri. 1355 * Reauthentication is done to detect crossing auth realms, but this 1356 * uri validation step is bypassed. r->target_orig is 1357 * original request-target sent in client request. */ 1358 { 1359 const size_t ulen = strlen(uri); 1360 if (!buffer_is_equal_string(&r->target_orig, uri, ulen)) { 1361 log_error(r->conf.errh, __FILE__, __LINE__, 1362 "digest: auth failed: uri mismatch (%s != %s), IP: %s", 1363 r->target_orig.ptr, uri, r->con->dst_addr_buf->ptr); 1364 buffer_free(b); 1365 return mod_auth_send_400_bad_request(r); 1366 } 1367 } 1368 1369 /* check age of nonce. Note, random data is used in nonce generation 1370 * in mod_auth_send_401_unauthorized_digest(). If that were replaced 1371 * with nanosecond time, then nonce secret would remain unique enough 1372 * for the purposes of Digest auth, and would be reproducible (and 1373 * verifiable) if nanoseconds were included with seconds as part of the 1374 * nonce "timestamp:secret". However, doing so would expose a high 1375 * precision timestamp of the system to attackers. timestamp in nonce 1376 * could theoretically be modified and still produce same md5sum, but 1377 * that is highly unlikely within a 10 min (moving) window of valid 1378 * time relative to current time (now). 1379 * If it is desired to validate that nonces were generated by server, 1380 * then specify auth.require = ( ... => ( "secret" => "..." ) ) 1381 * When secret is specified, then instead of nanoseconds, the random 1382 * data value (included for unique nonces) will be exposed in the nonce 1383 * along with the timestamp, and the additional secret will be used to 1384 * validate that the server generated the nonce using that secret. */ 1385 int send_nextnonce; 1386 { 1387 time_t ts = 0; 1388 const unsigned char * const nonce_uns = (unsigned char *)nonce; 1389 for (i = 0; i < 8 && light_isxdigit(nonce_uns[i]); ++i) { 1390 ts = (ts << 4) + hex2int(nonce_uns[i]); 1391 } 1392 const time_t cur_ts = log_epoch_secs; 1393 if (nonce[i] != ':' 1394 || ts > cur_ts || cur_ts - ts > 600) { /*(10 mins)*/ 1395 /* nonce is stale; have client regenerate digest */ 1396 buffer_free(b); 1397 return mod_auth_send_401_unauthorized_digest(r, require, ai.dalgo); 1398 } 1399 1400 send_nextnonce = (cur_ts - ts > 540); /*(9 mins)*/ 1401 1402 if (require->nonce_secret) { 1403 unsigned int rnd = 0; 1404 for (int j = i+8; i < j && light_isxdigit(nonce_uns[i]); ++i) { 1405 rnd = (rnd << 4) + hex2int(nonce_uns[i]); 1406 } 1407 if (nonce[i] != ':') { 1408 /* nonce is invalid; 1409 * expect extra field w/ require->nonce_secret */ 1410 log_error(r->conf.errh, __FILE__, __LINE__, 1411 "digest: nonce invalid"); 1412 buffer_free(b); 1413 return mod_auth_send_400_bad_request(r); 1414 } 1415 buffer * const tb = r->tmp_buf; 1416 buffer_clear(tb); 1417 mod_auth_append_nonce(tb, cur_ts, require, ai.dalgo, (int *)&rnd); 1418 if (!buffer_eq_slen(tb, nonce, strlen(nonce))) { 1419 /* nonce not generated using current require->nonce_secret */ 1420 log_error(r->conf.errh, __FILE__, __LINE__, 1421 "digest: nonce mismatch"); 1422 buffer_free(b); 1423 return mod_auth_send_401_unauthorized_digest(r, require, 0); 1424 } 1425 } 1426 } 1427 1428 handler_t rc = HANDLER_UNSET; 1429 1430 plugin_data * const p = p_d; 1431 splay_tree ** sptree = p->conf.auth_cache 1432 ? &p->conf.auth_cache->sptree 1433 : NULL; 1434 http_auth_cache_entry *ae = NULL; 1435 int ndx = -1; 1436 if (sptree) { 1437 ndx = http_auth_cache_hash(require, ai.username, ai.ulen); 1438 ae = http_auth_cache_query(sptree, ndx); 1439 if (ae && ae->require == require 1440 && ae->dalgo == ai.dalgo 1441 && ae->dlen == ai.dlen 1442 && ae->ulen == ai.ulen 1443 && 0 == memcmp(ae->username, ai.username, ai.ulen)) { 1444 rc = HANDLER_GO_ON; 1445 memcpy(ai.digest, ae->pwdigest, ai.dlen); 1446 } 1447 else /*(not found or hash collision)*/ 1448 ae = NULL; 1449 } 1450 1451 if (HANDLER_UNSET == rc) 1452 rc = backend->digest(r, backend->p_d, &ai); 1453 1454 switch (rc) { 1455 case HANDLER_GO_ON: 1456 break; 1457 case HANDLER_WAIT_FOR_EVENT: 1458 buffer_free(b); 1459 return HANDLER_WAIT_FOR_EVENT; 1460 case HANDLER_FINISHED: 1461 buffer_free(b); 1462 return HANDLER_FINISHED; 1463 case HANDLER_ERROR: 1464 default: 1465 r->keep_alive = 0; /*(disable keep-alive if unknown user)*/ 1466 buffer_free(b); 1467 return mod_auth_send_401_unauthorized_digest(r, require, 0); 1468 } 1469 1470 if (sptree && NULL == ae) { /*(cache digest from backend)*/ 1471 ae = http_auth_cache_entry_init(require, ai.dalgo, ai.username, ai.ulen, 1472 (char *)ai.digest, ai.dlen); 1473 http_auth_cache_insert(sptree, ndx, ae, http_auth_cache_entry_free); 1474 } 1475 1476 const char *m = get_http_method_name(r->http_method); 1477 force_assert(m); 1478 1479 mod_auth_digest_mutate(&ai,m,uri,nonce,cnonce,nc,qop); 1480 1481 if (!http_auth_const_time_memeq(rdigest, ai.digest, ai.dlen)) { 1482 /* digest not ok */ 1483 log_error(r->conf.errh, __FILE__, __LINE__, 1484 "digest: auth failed for %s: wrong password, IP: %s", 1485 username, r->con->dst_addr_buf->ptr); 1486 r->keep_alive = 0; /*(disable keep-alive if bad password)*/ 1487 1488 buffer_free(b); 1489 return mod_auth_send_401_unauthorized_digest(r, require, 0); 1490 } 1491 1492 /* value is our allow-rules */ 1493 if (!http_auth_match_rules(require, username, NULL, NULL)) { 1494 buffer_free(b); 1495 return mod_auth_send_401_unauthorized_digest(r, require, 0); 1496 } 1497 1498 if (send_nextnonce) { 1499 /*(send nextnonce when expiration is approaching)*/ 1500 buffer * const tb = r->tmp_buf; 1501 const time_t cur_ts = log_epoch_secs; 1502 mod_auth_digest_authentication_info(tb, cur_ts, require, ai.dalgo); 1503 http_header_response_set(r, HTTP_HEADER_OTHER, 1504 CONST_STR_LEN("Authentication-Info"), 1505 CONST_BUF_LEN(tb)); 1506 } 1507 1508 http_auth_setenv(r, ai.username, ai.ulen, CONST_STR_LEN("Digest")); 1509 1510 buffer_free(b); 1511 1512 return HANDLER_GO_ON; 1513 } 1514 1515 static handler_t mod_auth_send_401_unauthorized_digest(request_st * const r, const struct http_auth_require_t * const require, int nonce_stale) { 1516 buffer * const tb = r->tmp_buf; 1517 mod_auth_digest_www_authenticate(tb, log_epoch_secs, require, nonce_stale); 1518 http_header_response_set(r, HTTP_HEADER_WWW_AUTHENTICATE, 1519 CONST_STR_LEN("WWW-Authenticate"), 1520 CONST_BUF_LEN(tb)); 1521 1522 r->http_status = 401; 1523 r->handler_module = NULL; 1524 return HANDLER_FINISHED; 1525 } 1526 1527 static handler_t mod_auth_check_extern(request_st * const r, void *p_d, const struct http_auth_require_t * const require, const struct http_auth_backend_t * const backend) { 1528 /* require REMOTE_USER already set */ 1529 const buffer *vb = http_header_env_get(r, CONST_STR_LEN("REMOTE_USER")); 1530 UNUSED(p_d); 1531 UNUSED(backend); 1532 if (NULL != vb && http_auth_match_rules(require, vb->ptr, NULL, NULL)) { 1533 return HANDLER_GO_ON; 1534 } else { 1535 r->http_status = 401; 1536 r->handler_module = NULL; 1537 return HANDLER_FINISHED; 1538 } 1539 } 1540