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