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