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