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