xref: /lighttpd1.4/src/mod_auth.c (revision 9570cc2f)
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