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