xref: /lighttpd1.4/src/mod_auth.c (revision 4b3a91e6)
18abd06a7SGlenn Strauss #include "first.h"
28abd06a7SGlenn Strauss 
322e8b456SStefan Bühler #include "plugin.h"
422e8b456SStefan Bühler #include "http_auth.h"
522e8b456SStefan Bühler #include "log.h"
622e8b456SStefan Bühler #include "response.h"
722e8b456SStefan Bühler 
881b2d1f0SGlenn Strauss #include "inet_ntop_cache.h"
981b2d1f0SGlenn Strauss #include "base64.h"
1081b2d1f0SGlenn Strauss #include "md5.h"
1181b2d1f0SGlenn Strauss 
12bcdc6a3bSJan Kneschke #include <sys/types.h>
13bcdc6a3bSJan Kneschke #include <sys/stat.h>
14bcdc6a3bSJan Kneschke 
15bcdc6a3bSJan Kneschke #include <stdlib.h>
16bcdc6a3bSJan Kneschke #include <string.h>
17bcdc6a3bSJan Kneschke #include <errno.h>
18bcdc6a3bSJan Kneschke #include <fcntl.h>
19bcdc6a3bSJan Kneschke #include <unistd.h>
20bcdc6a3bSJan Kneschke 
21bcdc6a3bSJan Kneschke /**
22bcdc6a3bSJan Kneschke  * the basic and digest auth framework
23bcdc6a3bSJan Kneschke  */
24bcdc6a3bSJan Kneschke 
25*4b3a91e6SGlenn Strauss typedef struct {
26*4b3a91e6SGlenn Strauss 	/* auth */
27*4b3a91e6SGlenn Strauss 	array  *auth_require;
28*4b3a91e6SGlenn Strauss 
29*4b3a91e6SGlenn Strauss 	buffer *auth_plain_groupfile;
30*4b3a91e6SGlenn Strauss 	buffer *auth_plain_userfile;
31*4b3a91e6SGlenn Strauss 
32*4b3a91e6SGlenn Strauss 	buffer *auth_htdigest_userfile;
33*4b3a91e6SGlenn Strauss 	buffer *auth_htpasswd_userfile;
34*4b3a91e6SGlenn Strauss 
35*4b3a91e6SGlenn Strauss 	buffer *auth_backend_conf;
36*4b3a91e6SGlenn Strauss 
37*4b3a91e6SGlenn Strauss 	unsigned short auth_debug;
38*4b3a91e6SGlenn Strauss 
39*4b3a91e6SGlenn Strauss 	/* generated */
40*4b3a91e6SGlenn Strauss 	const http_auth_backend_t *auth_backend;
41*4b3a91e6SGlenn Strauss } mod_auth_plugin_config;
42*4b3a91e6SGlenn Strauss 
43*4b3a91e6SGlenn Strauss typedef struct {
44*4b3a91e6SGlenn Strauss 	PLUGIN_DATA;
45*4b3a91e6SGlenn Strauss 	buffer *tmp_buf;
46*4b3a91e6SGlenn Strauss 
47*4b3a91e6SGlenn Strauss 	mod_auth_plugin_config **config_storage;
48*4b3a91e6SGlenn Strauss 
49*4b3a91e6SGlenn Strauss 	mod_auth_plugin_config conf;
50*4b3a91e6SGlenn Strauss } mod_auth_plugin_data;
51*4b3a91e6SGlenn Strauss 
52bcdc6a3bSJan Kneschke INIT_FUNC(mod_auth_init) {
53bcdc6a3bSJan Kneschke 	mod_auth_plugin_data *p;
54bcdc6a3bSJan Kneschke 
55bcdc6a3bSJan Kneschke 	p = calloc(1, sizeof(*p));
56bcdc6a3bSJan Kneschke 
57bcdc6a3bSJan Kneschke 	p->tmp_buf = buffer_init();
58bcdc6a3bSJan Kneschke 
59bcdc6a3bSJan Kneschke 	return p;
60bcdc6a3bSJan Kneschke }
61bcdc6a3bSJan Kneschke 
62bcdc6a3bSJan Kneschke FREE_FUNC(mod_auth_free) {
63bcdc6a3bSJan Kneschke 	mod_auth_plugin_data *p = p_d;
64bcdc6a3bSJan Kneschke 
65bcdc6a3bSJan Kneschke 	UNUSED(srv);
66bcdc6a3bSJan Kneschke 
67bcdc6a3bSJan Kneschke 	if (!p) return HANDLER_GO_ON;
68bcdc6a3bSJan Kneschke 
69bcdc6a3bSJan Kneschke 	buffer_free(p->tmp_buf);
70bcdc6a3bSJan Kneschke 
71bcdc6a3bSJan Kneschke 	if (p->config_storage) {
72bcdc6a3bSJan Kneschke 		size_t i;
73bcdc6a3bSJan Kneschke 		for (i = 0; i < srv->config_context->used; i++) {
74bcdc6a3bSJan Kneschke 			mod_auth_plugin_config *s = p->config_storage[i];
75bcdc6a3bSJan Kneschke 
7633cebeb0SStefan Bühler 			if (NULL == s) continue;
774a6ce4aeSJan Kneschke 
78bcdc6a3bSJan Kneschke 			array_free(s->auth_require);
79bcdc6a3bSJan Kneschke 			buffer_free(s->auth_backend_conf);
80bcdc6a3bSJan Kneschke 
81bcdc6a3bSJan Kneschke 			free(s);
82bcdc6a3bSJan Kneschke 		}
83bcdc6a3bSJan Kneschke 		free(p->config_storage);
84bcdc6a3bSJan Kneschke 	}
85bcdc6a3bSJan Kneschke 
86bcdc6a3bSJan Kneschke 	free(p);
87bcdc6a3bSJan Kneschke 
88bcdc6a3bSJan Kneschke 	return HANDLER_GO_ON;
89bcdc6a3bSJan Kneschke }
90bcdc6a3bSJan Kneschke 
91bcdc6a3bSJan Kneschke #define PATCH(x) \
92bcdc6a3bSJan Kneschke 	p->conf.x = s->x;
934a6ce4aeSJan Kneschke static int mod_auth_patch_connection(server *srv, connection *con, mod_auth_plugin_data *p) {
94bcdc6a3bSJan Kneschke 	size_t i, j;
954a6ce4aeSJan Kneschke 	mod_auth_plugin_config *s = p->config_storage[0];
964a6ce4aeSJan Kneschke 
974a6ce4aeSJan Kneschke 	PATCH(auth_backend);
984a6ce4aeSJan Kneschke 	PATCH(auth_require);
994a6ce4aeSJan Kneschke 	PATCH(auth_debug);
100bcdc6a3bSJan Kneschke 
101bcdc6a3bSJan Kneschke 	/* skip the first, the global context */
102bcdc6a3bSJan Kneschke 	for (i = 1; i < srv->config_context->used; i++) {
103bcdc6a3bSJan Kneschke 		data_config *dc = (data_config *)srv->config_context->data[i];
1044a6ce4aeSJan Kneschke 		s = p->config_storage[i];
105bcdc6a3bSJan Kneschke 
106bcdc6a3bSJan Kneschke 		/* condition didn't match */
107bcdc6a3bSJan Kneschke 		if (!config_check_cond(srv, con, dc)) continue;
108bcdc6a3bSJan Kneschke 
109bcdc6a3bSJan Kneschke 		/* merge config */
110bcdc6a3bSJan Kneschke 		for (j = 0; j < dc->value->used; j++) {
111bcdc6a3bSJan Kneschke 			data_unset *du = dc->value->data[j];
112bcdc6a3bSJan Kneschke 
113bcdc6a3bSJan Kneschke 			if (buffer_is_equal_string(du->key, CONST_STR_LEN("auth.backend"))) {
114bcdc6a3bSJan Kneschke 				PATCH(auth_backend);
115bcdc6a3bSJan Kneschke 			} else if (buffer_is_equal_string(du->key, CONST_STR_LEN("auth.require"))) {
116bcdc6a3bSJan Kneschke 				PATCH(auth_require);
117bcdc6a3bSJan Kneschke 			} else if (buffer_is_equal_string(du->key, CONST_STR_LEN("auth.debug"))) {
118bcdc6a3bSJan Kneschke 				PATCH(auth_debug);
119bcdc6a3bSJan Kneschke 			}
120bcdc6a3bSJan Kneschke 		}
121bcdc6a3bSJan Kneschke 	}
122bcdc6a3bSJan Kneschke 
123bcdc6a3bSJan Kneschke 	return 0;
124bcdc6a3bSJan Kneschke }
125bcdc6a3bSJan Kneschke #undef PATCH
126bcdc6a3bSJan Kneschke 
12781b2d1f0SGlenn Strauss static int mod_auth_match_rules(server *srv, array *req, const char *username, const char *group, const char *host) {
12881b2d1f0SGlenn Strauss 	const char *r = NULL, *rules = NULL;
12981b2d1f0SGlenn Strauss 	int username_len;
13081b2d1f0SGlenn Strauss 	data_string *require;
13181b2d1f0SGlenn Strauss 
13281b2d1f0SGlenn Strauss 	UNUSED(group);
13381b2d1f0SGlenn Strauss 	UNUSED(host);
13481b2d1f0SGlenn Strauss 
13581b2d1f0SGlenn Strauss 	require = (data_string *)array_get_element(req, "require");
13681b2d1f0SGlenn Strauss 	if (!require) return -1; /*(should not happen; config is validated at startup)*/
13781b2d1f0SGlenn Strauss 
13881b2d1f0SGlenn Strauss 	/* if we get here, the user we got a authed user */
13981b2d1f0SGlenn Strauss 	if (0 == strcmp(require->value->ptr, "valid-user")) {
14081b2d1f0SGlenn Strauss 		return 0;
14181b2d1f0SGlenn Strauss 	}
14281b2d1f0SGlenn Strauss 
14381b2d1f0SGlenn Strauss 	/* user=name1|group=name3|host=name4 */
14481b2d1f0SGlenn Strauss 
14581b2d1f0SGlenn Strauss 	/* seperate the string by | */
14681b2d1f0SGlenn Strauss #if 0
14781b2d1f0SGlenn Strauss 	log_error_write(srv, __FILE__, __LINE__, "sb", "rules", require->value);
14881b2d1f0SGlenn Strauss #endif
14981b2d1f0SGlenn Strauss 
15081b2d1f0SGlenn Strauss 	username_len = username ? strlen(username) : 0;
15181b2d1f0SGlenn Strauss 
15281b2d1f0SGlenn Strauss 	r = rules = require->value->ptr;
15381b2d1f0SGlenn Strauss 
15481b2d1f0SGlenn Strauss 	while (1) {
15581b2d1f0SGlenn Strauss 		const char *eq;
15681b2d1f0SGlenn Strauss 		const char *k, *v, *e;
15781b2d1f0SGlenn Strauss 		int k_len, v_len, r_len;
15881b2d1f0SGlenn Strauss 
15981b2d1f0SGlenn Strauss 		e = strchr(r, '|');
16081b2d1f0SGlenn Strauss 
16181b2d1f0SGlenn Strauss 		if (e) {
16281b2d1f0SGlenn Strauss 			r_len = e - r;
16381b2d1f0SGlenn Strauss 		} else {
16481b2d1f0SGlenn Strauss 			r_len = strlen(rules) - (r - rules);
16581b2d1f0SGlenn Strauss 		}
16681b2d1f0SGlenn Strauss 
16781b2d1f0SGlenn Strauss 		/* from r to r + r_len is a rule */
16881b2d1f0SGlenn Strauss 
16981b2d1f0SGlenn Strauss 		if (0 == strncmp(r, "valid-user", r_len)) {
17081b2d1f0SGlenn Strauss 			log_error_write(srv, __FILE__, __LINE__, "sb",
17181b2d1f0SGlenn Strauss 					"parsing the 'require' section in 'auth.require' failed: valid-user cannot be combined with other require rules",
17281b2d1f0SGlenn Strauss 					require->value);
17381b2d1f0SGlenn Strauss 			return -1;
17481b2d1f0SGlenn Strauss 		}
17581b2d1f0SGlenn Strauss 
17681b2d1f0SGlenn Strauss 		/* search for = in the rules */
17781b2d1f0SGlenn Strauss 		if (NULL == (eq = strchr(r, '='))) {
17881b2d1f0SGlenn Strauss 			log_error_write(srv, __FILE__, __LINE__, "sb",
17981b2d1f0SGlenn Strauss 					"parsing the 'require' section in 'auth.require' failed: a = is missing",
18081b2d1f0SGlenn Strauss 					require->value);
18181b2d1f0SGlenn Strauss 			return -1;
18281b2d1f0SGlenn Strauss 		}
18381b2d1f0SGlenn Strauss 
18481b2d1f0SGlenn Strauss 		/* = out of range */
18581b2d1f0SGlenn Strauss 		if (eq > r + r_len) {
18681b2d1f0SGlenn Strauss 			log_error_write(srv, __FILE__, __LINE__, "sb",
18781b2d1f0SGlenn Strauss 					"parsing the 'require' section in 'auth.require' failed: = out of range",
18881b2d1f0SGlenn Strauss 					require->value);
18981b2d1f0SGlenn Strauss 
19081b2d1f0SGlenn Strauss 			return -1;
19181b2d1f0SGlenn Strauss 		}
19281b2d1f0SGlenn Strauss 
19381b2d1f0SGlenn Strauss 		/* the part before the = is user|group|host */
19481b2d1f0SGlenn Strauss 
19581b2d1f0SGlenn Strauss 		k = r;
19681b2d1f0SGlenn Strauss 		k_len = eq - r;
19781b2d1f0SGlenn Strauss 		v = eq + 1;
19881b2d1f0SGlenn Strauss 		v_len = r_len - k_len - 1;
19981b2d1f0SGlenn Strauss 
20081b2d1f0SGlenn Strauss 		if (k_len == 4) {
20181b2d1f0SGlenn Strauss 			if (0 == strncmp(k, "user", k_len)) {
20281b2d1f0SGlenn Strauss 				if (username &&
20381b2d1f0SGlenn Strauss 				    username_len == v_len &&
20481b2d1f0SGlenn Strauss 				    0 == strncmp(username, v, v_len)) {
20581b2d1f0SGlenn Strauss 					return 0;
20681b2d1f0SGlenn Strauss 				}
20781b2d1f0SGlenn Strauss 			} else if (0 == strncmp(k, "host", k_len)) {
20881b2d1f0SGlenn Strauss 				log_error_write(srv, __FILE__, __LINE__, "s", "host ... (not implemented)");
20981b2d1f0SGlenn Strauss 			} else {
21081b2d1f0SGlenn Strauss 				log_error_write(srv, __FILE__, __LINE__, "s", "unknown key");
21181b2d1f0SGlenn Strauss 				return -1;
21281b2d1f0SGlenn Strauss 			}
21381b2d1f0SGlenn Strauss 		} else if (k_len == 5) {
21481b2d1f0SGlenn Strauss 			if (0 == strncmp(k, "group", k_len)) {
21581b2d1f0SGlenn Strauss 				log_error_write(srv, __FILE__, __LINE__, "s", "group ... (not implemented)");
21681b2d1f0SGlenn Strauss 			} else {
21781b2d1f0SGlenn Strauss 				log_error_write(srv, __FILE__, __LINE__, "ss", "unknown key", k);
21881b2d1f0SGlenn Strauss 				return -1;
21981b2d1f0SGlenn Strauss 			}
22081b2d1f0SGlenn Strauss 		} else {
22181b2d1f0SGlenn Strauss 			log_error_write(srv, __FILE__, __LINE__, "s", "unknown  key");
22281b2d1f0SGlenn Strauss 			return -1;
22381b2d1f0SGlenn Strauss 		}
22481b2d1f0SGlenn Strauss 
22581b2d1f0SGlenn Strauss 		if (!e) break;
22681b2d1f0SGlenn Strauss 		r = e + 1;
22781b2d1f0SGlenn Strauss 	}
22881b2d1f0SGlenn Strauss 
22981b2d1f0SGlenn Strauss 	log_error_write(srv, __FILE__, __LINE__, "s", "nothing matched");
23081b2d1f0SGlenn Strauss 
23181b2d1f0SGlenn Strauss 	return -1;
23281b2d1f0SGlenn Strauss }
23381b2d1f0SGlenn Strauss 
234*4b3a91e6SGlenn Strauss static handler_t mod_auth_send_400_bad_request(server *srv, connection *con) {
235*4b3a91e6SGlenn Strauss 	UNUSED(srv);
236*4b3a91e6SGlenn Strauss 
237*4b3a91e6SGlenn Strauss 	/* a field was missing or invalid */
238*4b3a91e6SGlenn Strauss 	con->http_status = 400; /* Bad Request */
239*4b3a91e6SGlenn Strauss 	con->mode = DIRECT;
240*4b3a91e6SGlenn Strauss 
241*4b3a91e6SGlenn Strauss 	return HANDLER_FINISHED;
242*4b3a91e6SGlenn Strauss }
243*4b3a91e6SGlenn Strauss 
244*4b3a91e6SGlenn Strauss static int mod_auth_digest_generate_nonce(server *srv, mod_auth_plugin_data *p, buffer *fn, char (*out)[33]);
245*4b3a91e6SGlenn Strauss 
246*4b3a91e6SGlenn Strauss static handler_t mod_auth_send_401_unauthorized_basic(server *srv, connection *con, mod_auth_plugin_data *p, array *req) {
247*4b3a91e6SGlenn Strauss 	data_string *realm = (data_string *)array_get_element(req, "realm");
248*4b3a91e6SGlenn Strauss 
249*4b3a91e6SGlenn Strauss 	con->http_status = 401;
250*4b3a91e6SGlenn Strauss 	con->mode = DIRECT;
251*4b3a91e6SGlenn Strauss 
252*4b3a91e6SGlenn Strauss 	if (!realm) return HANDLER_FINISHED; /*(should not happen; config is validated at startup)*/
253*4b3a91e6SGlenn Strauss 
254*4b3a91e6SGlenn Strauss 	buffer_copy_string_len(p->tmp_buf, CONST_STR_LEN("Basic realm=\""));
255*4b3a91e6SGlenn Strauss 	buffer_append_string_buffer(p->tmp_buf, realm->value);
256*4b3a91e6SGlenn Strauss 	buffer_append_string_len(p->tmp_buf, CONST_STR_LEN("\", charset=\"UTF-8\""));
257*4b3a91e6SGlenn Strauss 
258*4b3a91e6SGlenn Strauss 	response_header_insert(srv, con, CONST_STR_LEN("WWW-Authenticate"), CONST_BUF_LEN(p->tmp_buf));
259*4b3a91e6SGlenn Strauss 
260*4b3a91e6SGlenn Strauss 	return HANDLER_FINISHED;
261*4b3a91e6SGlenn Strauss }
262*4b3a91e6SGlenn Strauss 
263*4b3a91e6SGlenn Strauss static handler_t mod_auth_send_401_unauthorized_digest(server *srv, connection *con, mod_auth_plugin_data *p, array *req, int nonce_stale) {
264*4b3a91e6SGlenn Strauss 	data_string *realm = (data_string *)array_get_element(req, "realm");
265*4b3a91e6SGlenn Strauss 	char hh[33];
266*4b3a91e6SGlenn Strauss 
267*4b3a91e6SGlenn Strauss 	con->http_status = 401;
268*4b3a91e6SGlenn Strauss 	con->mode = DIRECT;
269*4b3a91e6SGlenn Strauss 
270*4b3a91e6SGlenn Strauss 	if (!realm) return HANDLER_FINISHED; /*(should not happen; config is validated at startup)*/
271*4b3a91e6SGlenn Strauss 
272*4b3a91e6SGlenn Strauss 	/* using unknown contents of srv->tmp_buf (modified elsewhere)
273*4b3a91e6SGlenn Strauss 	 * adds dubious amount of randomness.  Remove use of srv->tmp_buf? */
274*4b3a91e6SGlenn Strauss 	mod_auth_digest_generate_nonce(srv, p, srv->tmp_buf, &hh);
275*4b3a91e6SGlenn Strauss 
276*4b3a91e6SGlenn Strauss 	buffer_copy_string_len(p->tmp_buf, CONST_STR_LEN("Digest realm=\""));
277*4b3a91e6SGlenn Strauss 	buffer_append_string_buffer(p->tmp_buf, realm->value);
278*4b3a91e6SGlenn Strauss 	buffer_append_string_len(p->tmp_buf, CONST_STR_LEN("\", charset=\"UTF-8\", nonce=\""));
279*4b3a91e6SGlenn Strauss 	buffer_append_uint_hex(p->tmp_buf, (uintmax_t)srv->cur_ts);
280*4b3a91e6SGlenn Strauss 	buffer_append_string_len(p->tmp_buf, CONST_STR_LEN(":"));
281*4b3a91e6SGlenn Strauss 	buffer_append_string(p->tmp_buf, hh);
282*4b3a91e6SGlenn Strauss 	buffer_append_string_len(p->tmp_buf, CONST_STR_LEN("\", qop=\"auth\""));
283*4b3a91e6SGlenn Strauss 	if (nonce_stale) {
284*4b3a91e6SGlenn Strauss 		buffer_append_string_len(p->tmp_buf, CONST_STR_LEN(", stale=true"));
285*4b3a91e6SGlenn Strauss 	}
286*4b3a91e6SGlenn Strauss 
287*4b3a91e6SGlenn Strauss 	response_header_insert(srv, con, CONST_STR_LEN("WWW-Authenticate"), CONST_BUF_LEN(p->tmp_buf));
288*4b3a91e6SGlenn Strauss 
289*4b3a91e6SGlenn Strauss 	return HANDLER_FINISHED;
290*4b3a91e6SGlenn Strauss }
291*4b3a91e6SGlenn Strauss 
292*4b3a91e6SGlenn Strauss static void mod_auth_setenv(server *srv, connection *con, const char *username, size_t ulen, const char *auth_type, size_t alen) {
293*4b3a91e6SGlenn Strauss 	data_string *ds;
294*4b3a91e6SGlenn Strauss 	UNUSED(srv);
295*4b3a91e6SGlenn Strauss 
296*4b3a91e6SGlenn Strauss 	/* the REMOTE_USER header */
297*4b3a91e6SGlenn Strauss 
298*4b3a91e6SGlenn Strauss 	if (NULL == (ds = (data_string *)array_get_element(con->environment, "REMOTE_USER"))) {
299*4b3a91e6SGlenn Strauss 		if (NULL == (ds = (data_string *)array_get_unused_element(con->environment, TYPE_STRING))) {
300*4b3a91e6SGlenn Strauss 			ds = data_string_init();
301*4b3a91e6SGlenn Strauss 		}
302*4b3a91e6SGlenn Strauss 		buffer_copy_string_len(ds->key, CONST_STR_LEN("REMOTE_USER"));
303*4b3a91e6SGlenn Strauss 		array_insert_unique(con->environment, (data_unset *)ds);
304*4b3a91e6SGlenn Strauss 	}
305*4b3a91e6SGlenn Strauss 	buffer_copy_string_len(ds->value, username, ulen);
306*4b3a91e6SGlenn Strauss 
307*4b3a91e6SGlenn Strauss 	/* AUTH_TYPE environment */
308*4b3a91e6SGlenn Strauss 
309*4b3a91e6SGlenn Strauss 	if (NULL == (ds = (data_string *)array_get_element(con->environment, "AUTH_TYPE"))) {
310*4b3a91e6SGlenn Strauss 		if (NULL == (ds = (data_string *)array_get_unused_element(con->environment, TYPE_STRING))) {
311*4b3a91e6SGlenn Strauss 			ds = data_string_init();
312*4b3a91e6SGlenn Strauss 		}
313*4b3a91e6SGlenn Strauss 		buffer_copy_string_len(ds->key, CONST_STR_LEN("AUTH_TYPE"));
314*4b3a91e6SGlenn Strauss 		array_insert_unique(con->environment, (data_unset *)ds);
315*4b3a91e6SGlenn Strauss 	}
316*4b3a91e6SGlenn Strauss 	buffer_copy_string_len(ds->value, auth_type, alen);
317*4b3a91e6SGlenn Strauss }
318*4b3a91e6SGlenn Strauss 
319*4b3a91e6SGlenn Strauss static handler_t mod_auth_basic_check(server *srv, connection *con, mod_auth_plugin_data *p, array *req, const char *realm_str) {
32081b2d1f0SGlenn Strauss 	buffer *username;
32181b2d1f0SGlenn Strauss 	char *pw;
32281b2d1f0SGlenn Strauss 
32381b2d1f0SGlenn Strauss 	data_string *realm;
32481b2d1f0SGlenn Strauss 
32581b2d1f0SGlenn Strauss 	realm = (data_string *)array_get_element(req, "realm");
326*4b3a91e6SGlenn Strauss 	if (!realm) { /*(should not happen; config is validated at startup)*/
327*4b3a91e6SGlenn Strauss 		return mod_auth_send_400_bad_request(srv, con);
328*4b3a91e6SGlenn Strauss 	}
32981b2d1f0SGlenn Strauss 
33081b2d1f0SGlenn Strauss 	username = buffer_init();
33181b2d1f0SGlenn Strauss 
33281b2d1f0SGlenn Strauss 	if (!buffer_append_base64_decode(username, realm_str, strlen(realm_str), BASE64_STANDARD)) {
33381b2d1f0SGlenn Strauss 		log_error_write(srv, __FILE__, __LINE__, "sb", "decodeing base64-string failed", username);
33481b2d1f0SGlenn Strauss 
33581b2d1f0SGlenn Strauss 		buffer_free(username);
336*4b3a91e6SGlenn Strauss 		return mod_auth_send_400_bad_request(srv, con);
33781b2d1f0SGlenn Strauss 	}
33881b2d1f0SGlenn Strauss 
33981b2d1f0SGlenn Strauss 	/* r2 == user:password */
34081b2d1f0SGlenn Strauss 	if (NULL == (pw = strchr(username->ptr, ':'))) {
34181b2d1f0SGlenn Strauss 		log_error_write(srv, __FILE__, __LINE__, "sb", ": is missing in", username);
34281b2d1f0SGlenn Strauss 
34381b2d1f0SGlenn Strauss 		buffer_free(username);
344*4b3a91e6SGlenn Strauss 		return mod_auth_send_400_bad_request(srv, con);
34581b2d1f0SGlenn Strauss 	}
34681b2d1f0SGlenn Strauss 
34781b2d1f0SGlenn Strauss 	buffer_string_set_length(username, pw - username->ptr);
34881b2d1f0SGlenn Strauss 	pw++;
34981b2d1f0SGlenn Strauss 
350*4b3a91e6SGlenn Strauss 	switch (p->conf.auth_backend->basic(srv, con, p->conf.auth_backend->p_d, username, realm->value, pw)) {
351*4b3a91e6SGlenn Strauss 	case HANDLER_GO_ON:
352*4b3a91e6SGlenn Strauss 		break;
353*4b3a91e6SGlenn Strauss 	case HANDLER_WAIT_FOR_EVENT:
35481b2d1f0SGlenn Strauss 		buffer_free(username);
355*4b3a91e6SGlenn Strauss 		return HANDLER_WAIT_FOR_EVENT;
356*4b3a91e6SGlenn Strauss 	case HANDLER_FINISHED:
357*4b3a91e6SGlenn Strauss 		buffer_free(username);
358*4b3a91e6SGlenn Strauss 		return HANDLER_FINISHED;
359*4b3a91e6SGlenn Strauss 	case HANDLER_ERROR:
360*4b3a91e6SGlenn Strauss 	default:
361*4b3a91e6SGlenn Strauss 		log_error_write(srv, __FILE__, __LINE__, "sbsBss", "password doesn't match for", con->uri.path, "username:", username, ", IP:", inet_ntop_cache_get_ip(srv, &(con->dst_addr)));
362*4b3a91e6SGlenn Strauss 		buffer_free(username);
363*4b3a91e6SGlenn Strauss 		return mod_auth_send_401_unauthorized_basic(srv, con, p, req);
36481b2d1f0SGlenn Strauss 	}
36581b2d1f0SGlenn Strauss 
36681b2d1f0SGlenn Strauss 	/* value is our allow-rules */
36781b2d1f0SGlenn Strauss 	if (mod_auth_match_rules(srv, req, username->ptr, NULL, NULL)) {
36881b2d1f0SGlenn Strauss 		log_error_write(srv, __FILE__, __LINE__, "s", "rules didn't match");
36981b2d1f0SGlenn Strauss 
370*4b3a91e6SGlenn Strauss 		buffer_free(username);
371*4b3a91e6SGlenn Strauss 		return mod_auth_send_401_unauthorized_basic(srv, con, p, req);
37281b2d1f0SGlenn Strauss 	}
37381b2d1f0SGlenn Strauss 
374*4b3a91e6SGlenn Strauss 	mod_auth_setenv(srv, con, CONST_BUF_LEN(username), CONST_STR_LEN("Basic"));
37581b2d1f0SGlenn Strauss 
37681b2d1f0SGlenn Strauss 	buffer_free(username);
37781b2d1f0SGlenn Strauss 
378*4b3a91e6SGlenn Strauss 	return HANDLER_GO_ON;
37981b2d1f0SGlenn Strauss }
38081b2d1f0SGlenn Strauss 
38181b2d1f0SGlenn Strauss #define HASHLEN 16
38281b2d1f0SGlenn Strauss #define HASHHEXLEN 32
38381b2d1f0SGlenn Strauss typedef unsigned char HASH[HASHLEN];
38481b2d1f0SGlenn Strauss typedef char HASHHEX[HASHHEXLEN+1];
38581b2d1f0SGlenn Strauss 
38681b2d1f0SGlenn Strauss static void CvtHex(const HASH Bin, char (*Hex)[33]) {
38781b2d1f0SGlenn Strauss 	li_tohex(*Hex, sizeof(*Hex), (const char*) Bin, 16);
38881b2d1f0SGlenn Strauss }
38981b2d1f0SGlenn Strauss 
39081b2d1f0SGlenn Strauss typedef struct {
39181b2d1f0SGlenn Strauss 	const char *key;
39281b2d1f0SGlenn Strauss 	int key_len;
39381b2d1f0SGlenn Strauss 	char **ptr;
39481b2d1f0SGlenn Strauss } digest_kv;
39581b2d1f0SGlenn Strauss 
396*4b3a91e6SGlenn Strauss static handler_t mod_auth_digest_check(server *srv, connection *con, mod_auth_plugin_data *p, array *req, const char *realm_str) {
39781b2d1f0SGlenn Strauss 	char a1[33];
39881b2d1f0SGlenn Strauss 	char a2[33];
39981b2d1f0SGlenn Strauss 
40081b2d1f0SGlenn Strauss 	char *username = NULL;
40181b2d1f0SGlenn Strauss 	char *realm = NULL;
40281b2d1f0SGlenn Strauss 	char *nonce = NULL;
40381b2d1f0SGlenn Strauss 	char *uri = NULL;
40481b2d1f0SGlenn Strauss 	char *algorithm = NULL;
40581b2d1f0SGlenn Strauss 	char *qop = NULL;
40681b2d1f0SGlenn Strauss 	char *cnonce = NULL;
40781b2d1f0SGlenn Strauss 	char *nc = NULL;
40881b2d1f0SGlenn Strauss 	char *respons = NULL;
40981b2d1f0SGlenn Strauss 
41081b2d1f0SGlenn Strauss 	char *e, *c;
41181b2d1f0SGlenn Strauss 	const char *m = NULL;
41281b2d1f0SGlenn Strauss 	int i;
41381b2d1f0SGlenn Strauss 	buffer *b;
41481b2d1f0SGlenn Strauss 
41581b2d1f0SGlenn Strauss 	li_MD5_CTX Md5Ctx;
41681b2d1f0SGlenn Strauss 	HASH HA1;
41781b2d1f0SGlenn Strauss 	HASH HA2;
41881b2d1f0SGlenn Strauss 	HASH RespHash;
41981b2d1f0SGlenn Strauss 	HASHHEX HA2Hex;
42081b2d1f0SGlenn Strauss 
42181b2d1f0SGlenn Strauss 
42281b2d1f0SGlenn Strauss 	/* init pointers */
42381b2d1f0SGlenn Strauss #define S(x) \
42481b2d1f0SGlenn Strauss 	x, sizeof(x)-1, NULL
42581b2d1f0SGlenn Strauss 	digest_kv dkv[10] = {
42681b2d1f0SGlenn Strauss 		{ S("username=") },
42781b2d1f0SGlenn Strauss 		{ S("realm=") },
42881b2d1f0SGlenn Strauss 		{ S("nonce=") },
42981b2d1f0SGlenn Strauss 		{ S("uri=") },
43081b2d1f0SGlenn Strauss 		{ S("algorithm=") },
43181b2d1f0SGlenn Strauss 		{ S("qop=") },
43281b2d1f0SGlenn Strauss 		{ S("cnonce=") },
43381b2d1f0SGlenn Strauss 		{ S("nc=") },
43481b2d1f0SGlenn Strauss 		{ S("response=") },
43581b2d1f0SGlenn Strauss 
43681b2d1f0SGlenn Strauss 		{ NULL, 0, NULL }
43781b2d1f0SGlenn Strauss 	};
43881b2d1f0SGlenn Strauss #undef S
43981b2d1f0SGlenn Strauss 
44081b2d1f0SGlenn Strauss 	dkv[0].ptr = &username;
44181b2d1f0SGlenn Strauss 	dkv[1].ptr = &realm;
44281b2d1f0SGlenn Strauss 	dkv[2].ptr = &nonce;
44381b2d1f0SGlenn Strauss 	dkv[3].ptr = &uri;
44481b2d1f0SGlenn Strauss 	dkv[4].ptr = &algorithm;
44581b2d1f0SGlenn Strauss 	dkv[5].ptr = &qop;
44681b2d1f0SGlenn Strauss 	dkv[6].ptr = &cnonce;
44781b2d1f0SGlenn Strauss 	dkv[7].ptr = &nc;
44881b2d1f0SGlenn Strauss 	dkv[8].ptr = &respons;
44981b2d1f0SGlenn Strauss 
45081b2d1f0SGlenn Strauss 	b = buffer_init_string(realm_str);
45181b2d1f0SGlenn Strauss 
45281b2d1f0SGlenn Strauss 	/* parse credentials from client */
45381b2d1f0SGlenn Strauss 	for (c = b->ptr; *c; c++) {
45481b2d1f0SGlenn Strauss 		/* skip whitespaces */
45581b2d1f0SGlenn Strauss 		while (*c == ' ' || *c == '\t') c++;
45681b2d1f0SGlenn Strauss 		if (!*c) break;
45781b2d1f0SGlenn Strauss 
45881b2d1f0SGlenn Strauss 		for (i = 0; dkv[i].key; i++) {
45981b2d1f0SGlenn Strauss 			if ((0 == strncmp(c, dkv[i].key, dkv[i].key_len))) {
46081b2d1f0SGlenn Strauss 				if ((c[dkv[i].key_len] == '"') &&
46181b2d1f0SGlenn Strauss 				    (NULL != (e = strchr(c + dkv[i].key_len + 1, '"')))) {
46281b2d1f0SGlenn Strauss 					/* value with "..." */
46381b2d1f0SGlenn Strauss 					*(dkv[i].ptr) = c + dkv[i].key_len + 1;
46481b2d1f0SGlenn Strauss 					c = e;
46581b2d1f0SGlenn Strauss 
46681b2d1f0SGlenn Strauss 					*e = '\0';
46781b2d1f0SGlenn Strauss 				} else if (NULL != (e = strchr(c + dkv[i].key_len, ','))) {
46881b2d1f0SGlenn Strauss 					/* value without "...", terminated by ',' */
46981b2d1f0SGlenn Strauss 					*(dkv[i].ptr) = c + dkv[i].key_len;
47081b2d1f0SGlenn Strauss 					c = e;
47181b2d1f0SGlenn Strauss 
47281b2d1f0SGlenn Strauss 					*e = '\0';
47381b2d1f0SGlenn Strauss 				} else {
47481b2d1f0SGlenn Strauss 					/* value without "...", terminated by EOL */
47581b2d1f0SGlenn Strauss 					*(dkv[i].ptr) = c + dkv[i].key_len;
47681b2d1f0SGlenn Strauss 					c += strlen(c) - 1;
47781b2d1f0SGlenn Strauss 				}
47881b2d1f0SGlenn Strauss 				break;
47981b2d1f0SGlenn Strauss 			}
48081b2d1f0SGlenn Strauss 		}
48181b2d1f0SGlenn Strauss 	}
48281b2d1f0SGlenn Strauss 
48381b2d1f0SGlenn Strauss 	if (p->conf.auth_debug > 1) {
48481b2d1f0SGlenn Strauss 		log_error_write(srv, __FILE__, __LINE__, "ss", "username", username);
48581b2d1f0SGlenn Strauss 		log_error_write(srv, __FILE__, __LINE__, "ss", "realm", realm);
48681b2d1f0SGlenn Strauss 		log_error_write(srv, __FILE__, __LINE__, "ss", "nonce", nonce);
48781b2d1f0SGlenn Strauss 		log_error_write(srv, __FILE__, __LINE__, "ss", "uri", uri);
48881b2d1f0SGlenn Strauss 		log_error_write(srv, __FILE__, __LINE__, "ss", "algorithm", algorithm);
48981b2d1f0SGlenn Strauss 		log_error_write(srv, __FILE__, __LINE__, "ss", "qop", qop);
49081b2d1f0SGlenn Strauss 		log_error_write(srv, __FILE__, __LINE__, "ss", "cnonce", cnonce);
49181b2d1f0SGlenn Strauss 		log_error_write(srv, __FILE__, __LINE__, "ss", "nc", nc);
49281b2d1f0SGlenn Strauss 		log_error_write(srv, __FILE__, __LINE__, "ss", "response", respons);
49381b2d1f0SGlenn Strauss 	}
49481b2d1f0SGlenn Strauss 
49581b2d1f0SGlenn Strauss 	/* check if everything is transmitted */
49681b2d1f0SGlenn Strauss 	if (!username ||
49781b2d1f0SGlenn Strauss 	    !realm ||
49881b2d1f0SGlenn Strauss 	    !nonce ||
49981b2d1f0SGlenn Strauss 	    !uri ||
50081b2d1f0SGlenn Strauss 	    (qop && (!nc || !cnonce)) ||
50181b2d1f0SGlenn Strauss 	    !respons ) {
50281b2d1f0SGlenn Strauss 		/* missing field */
50381b2d1f0SGlenn Strauss 
50481b2d1f0SGlenn Strauss 		log_error_write(srv, __FILE__, __LINE__, "s",
50581b2d1f0SGlenn Strauss 				"digest: missing field");
50681b2d1f0SGlenn Strauss 
50781b2d1f0SGlenn Strauss 		buffer_free(b);
508*4b3a91e6SGlenn Strauss 		return mod_auth_send_400_bad_request(srv, con);
50981b2d1f0SGlenn Strauss 	}
51081b2d1f0SGlenn Strauss 
51181b2d1f0SGlenn Strauss 	/**
51281b2d1f0SGlenn Strauss 	 * protect the md5-sess against missing cnonce and nonce
51381b2d1f0SGlenn Strauss 	 */
51481b2d1f0SGlenn Strauss 	if (algorithm &&
51581b2d1f0SGlenn Strauss 	    0 == strcasecmp(algorithm, "md5-sess") &&
51681b2d1f0SGlenn Strauss 	    (!nonce || !cnonce)) {
51781b2d1f0SGlenn Strauss 		log_error_write(srv, __FILE__, __LINE__, "s",
51881b2d1f0SGlenn Strauss 				"digest: (md5-sess: missing field");
51981b2d1f0SGlenn Strauss 
52081b2d1f0SGlenn Strauss 		buffer_free(b);
521*4b3a91e6SGlenn Strauss 		return mod_auth_send_400_bad_request(srv, con);
52281b2d1f0SGlenn Strauss 	}
52381b2d1f0SGlenn Strauss 
52481b2d1f0SGlenn Strauss 	if (qop && strcasecmp(qop, "auth-int") == 0) {
52581b2d1f0SGlenn Strauss 		log_error_write(srv, __FILE__, __LINE__, "s",
52681b2d1f0SGlenn Strauss 				"digest: qop=auth-int not supported");
52781b2d1f0SGlenn Strauss 
52881b2d1f0SGlenn Strauss 		buffer_free(b);
529*4b3a91e6SGlenn Strauss 		return mod_auth_send_400_bad_request(srv, con);
53081b2d1f0SGlenn Strauss 	}
53181b2d1f0SGlenn Strauss 
53281b2d1f0SGlenn Strauss 	m = get_http_method_name(con->request.http_method);
53381b2d1f0SGlenn Strauss 	force_assert(m);
53481b2d1f0SGlenn Strauss 
53581b2d1f0SGlenn Strauss 	/* detect if attacker is attempting to reuse valid digest for one uri
53681b2d1f0SGlenn Strauss 	 * on a different request uri.  Might also happen if intermediate proxy
53781b2d1f0SGlenn Strauss 	 * altered client request line.  (Altered request would not result in
53881b2d1f0SGlenn Strauss 	 * the same digest as that calculated by the client.)
53981b2d1f0SGlenn Strauss 	 * Internal redirects such as with mod_rewrite will modify request uri.
54081b2d1f0SGlenn Strauss 	 * Reauthentication is done to detect crossing auth realms, but this
54181b2d1f0SGlenn Strauss 	 * uri validation step is bypassed.  con->request.orig_uri is original
54281b2d1f0SGlenn Strauss 	 * uri sent in client request. */
54381b2d1f0SGlenn Strauss 	{
54481b2d1f0SGlenn Strauss 		const size_t ulen = strlen(uri);
54581b2d1f0SGlenn Strauss 		const size_t rlen = buffer_string_length(con->request.orig_uri);
54681b2d1f0SGlenn Strauss 		if (!buffer_is_equal_string(con->request.orig_uri, uri, ulen)
54781b2d1f0SGlenn Strauss 		    && !(rlen < ulen && 0 == memcmp(con->request.orig_uri->ptr, uri, rlen) && uri[rlen] == '?')) {
54881b2d1f0SGlenn Strauss 			log_error_write(srv, __FILE__, __LINE__, "sbssss",
54981b2d1f0SGlenn Strauss 					"digest: auth failed: uri mismatch (", con->request.orig_uri, "!=", uri, "), IP:", inet_ntop_cache_get_ip(srv, &(con->dst_addr)));
55081b2d1f0SGlenn Strauss 			buffer_free(b);
551*4b3a91e6SGlenn Strauss 			return mod_auth_send_400_bad_request(srv, con);
55281b2d1f0SGlenn Strauss 		}
55381b2d1f0SGlenn Strauss 	}
55481b2d1f0SGlenn Strauss 
55581b2d1f0SGlenn Strauss 	/* password-string == HA1 */
556*4b3a91e6SGlenn Strauss 	switch (p->conf.auth_backend->digest(srv, con, p->conf.auth_backend->p_d, username, realm, HA1)) {
557*4b3a91e6SGlenn Strauss 	case HANDLER_GO_ON:
558*4b3a91e6SGlenn Strauss 		break;
559*4b3a91e6SGlenn Strauss 	case HANDLER_WAIT_FOR_EVENT:
56081b2d1f0SGlenn Strauss 		buffer_free(b);
561*4b3a91e6SGlenn Strauss 		return HANDLER_WAIT_FOR_EVENT;
562*4b3a91e6SGlenn Strauss 	case HANDLER_FINISHED:
563*4b3a91e6SGlenn Strauss 		buffer_free(b);
564*4b3a91e6SGlenn Strauss 		return HANDLER_FINISHED;
565*4b3a91e6SGlenn Strauss 	case HANDLER_ERROR:
566*4b3a91e6SGlenn Strauss 	default:
567*4b3a91e6SGlenn Strauss 		buffer_free(b);
568*4b3a91e6SGlenn Strauss 		return mod_auth_send_401_unauthorized_digest(srv, con, p, req, 0);
56981b2d1f0SGlenn Strauss 	}
57081b2d1f0SGlenn Strauss 
57181b2d1f0SGlenn Strauss 	if (algorithm &&
57281b2d1f0SGlenn Strauss 	    strcasecmp(algorithm, "md5-sess") == 0) {
57381b2d1f0SGlenn Strauss 		li_MD5_Init(&Md5Ctx);
57481b2d1f0SGlenn Strauss 		/* Errata ID 1649: http://www.rfc-editor.org/errata_search.php?rfc=2617 */
57581b2d1f0SGlenn Strauss 		CvtHex(HA1, &a1);
57681b2d1f0SGlenn Strauss 		li_MD5_Update(&Md5Ctx, (unsigned char *)a1, HASHHEXLEN);
57781b2d1f0SGlenn Strauss 		li_MD5_Update(&Md5Ctx, CONST_STR_LEN(":"));
57881b2d1f0SGlenn Strauss 		li_MD5_Update(&Md5Ctx, (unsigned char *)nonce, strlen(nonce));
57981b2d1f0SGlenn Strauss 		li_MD5_Update(&Md5Ctx, CONST_STR_LEN(":"));
58081b2d1f0SGlenn Strauss 		li_MD5_Update(&Md5Ctx, (unsigned char *)cnonce, strlen(cnonce));
58181b2d1f0SGlenn Strauss 		li_MD5_Final(HA1, &Md5Ctx);
58281b2d1f0SGlenn Strauss 	}
58381b2d1f0SGlenn Strauss 
58481b2d1f0SGlenn Strauss 	CvtHex(HA1, &a1);
58581b2d1f0SGlenn Strauss 
58681b2d1f0SGlenn Strauss 	/* calculate H(A2) */
58781b2d1f0SGlenn Strauss 	li_MD5_Init(&Md5Ctx);
58881b2d1f0SGlenn Strauss 	li_MD5_Update(&Md5Ctx, (unsigned char *)m, strlen(m));
58981b2d1f0SGlenn Strauss 	li_MD5_Update(&Md5Ctx, CONST_STR_LEN(":"));
59081b2d1f0SGlenn Strauss 	li_MD5_Update(&Md5Ctx, (unsigned char *)uri, strlen(uri));
59181b2d1f0SGlenn Strauss 	/* qop=auth-int not supported, already checked above */
59281b2d1f0SGlenn Strauss /*
59381b2d1f0SGlenn Strauss 	if (qop && strcasecmp(qop, "auth-int") == 0) {
59481b2d1f0SGlenn Strauss 		li_MD5_Update(&Md5Ctx, CONST_STR_LEN(":"));
59581b2d1f0SGlenn Strauss 		li_MD5_Update(&Md5Ctx, (unsigned char *) [body checksum], HASHHEXLEN);
59681b2d1f0SGlenn Strauss 	}
59781b2d1f0SGlenn Strauss */
59881b2d1f0SGlenn Strauss 	li_MD5_Final(HA2, &Md5Ctx);
59981b2d1f0SGlenn Strauss 	CvtHex(HA2, &HA2Hex);
60081b2d1f0SGlenn Strauss 
60181b2d1f0SGlenn Strauss 	/* calculate response */
60281b2d1f0SGlenn Strauss 	li_MD5_Init(&Md5Ctx);
60381b2d1f0SGlenn Strauss 	li_MD5_Update(&Md5Ctx, (unsigned char *)a1, HASHHEXLEN);
60481b2d1f0SGlenn Strauss 	li_MD5_Update(&Md5Ctx, CONST_STR_LEN(":"));
60581b2d1f0SGlenn Strauss 	li_MD5_Update(&Md5Ctx, (unsigned char *)nonce, strlen(nonce));
60681b2d1f0SGlenn Strauss 	li_MD5_Update(&Md5Ctx, CONST_STR_LEN(":"));
60781b2d1f0SGlenn Strauss 	if (qop && *qop) {
60881b2d1f0SGlenn Strauss 		li_MD5_Update(&Md5Ctx, (unsigned char *)nc, strlen(nc));
60981b2d1f0SGlenn Strauss 		li_MD5_Update(&Md5Ctx, CONST_STR_LEN(":"));
61081b2d1f0SGlenn Strauss 		li_MD5_Update(&Md5Ctx, (unsigned char *)cnonce, strlen(cnonce));
61181b2d1f0SGlenn Strauss 		li_MD5_Update(&Md5Ctx, CONST_STR_LEN(":"));
61281b2d1f0SGlenn Strauss 		li_MD5_Update(&Md5Ctx, (unsigned char *)qop, strlen(qop));
61381b2d1f0SGlenn Strauss 		li_MD5_Update(&Md5Ctx, CONST_STR_LEN(":"));
61481b2d1f0SGlenn Strauss 	};
61581b2d1f0SGlenn Strauss 	li_MD5_Update(&Md5Ctx, (unsigned char *)HA2Hex, HASHHEXLEN);
61681b2d1f0SGlenn Strauss 	li_MD5_Final(RespHash, &Md5Ctx);
61781b2d1f0SGlenn Strauss 	CvtHex(RespHash, &a2);
61881b2d1f0SGlenn Strauss 
61981b2d1f0SGlenn Strauss 	if (0 != strcmp(a2, respons)) {
62081b2d1f0SGlenn Strauss 		/* digest not ok */
62181b2d1f0SGlenn Strauss 
62281b2d1f0SGlenn Strauss 		if (p->conf.auth_debug) {
62381b2d1f0SGlenn Strauss 			log_error_write(srv, __FILE__, __LINE__, "sss",
62481b2d1f0SGlenn Strauss 				"digest: digest mismatch", a2, respons);
62581b2d1f0SGlenn Strauss 		}
62681b2d1f0SGlenn Strauss 
62781b2d1f0SGlenn Strauss 		log_error_write(srv, __FILE__, __LINE__, "ssss",
62881b2d1f0SGlenn Strauss 				"digest: auth failed for ", username, ": wrong password, IP:", inet_ntop_cache_get_ip(srv, &(con->dst_addr)));
62981b2d1f0SGlenn Strauss 
63081b2d1f0SGlenn Strauss 		buffer_free(b);
631*4b3a91e6SGlenn Strauss 		return mod_auth_send_401_unauthorized_digest(srv, con, p, req, 0);
63281b2d1f0SGlenn Strauss 	}
63381b2d1f0SGlenn Strauss 
63481b2d1f0SGlenn Strauss 	/* value is our allow-rules */
63581b2d1f0SGlenn Strauss 	if (mod_auth_match_rules(srv, req, username, NULL, NULL)) {
63681b2d1f0SGlenn Strauss 
63781b2d1f0SGlenn Strauss 		if (p->conf.auth_debug) {
63881b2d1f0SGlenn Strauss 			log_error_write(srv, __FILE__, __LINE__, "s",
63981b2d1f0SGlenn Strauss 					"digest: rules did match");
64081b2d1f0SGlenn Strauss 		}
64181b2d1f0SGlenn Strauss 
642*4b3a91e6SGlenn Strauss 		buffer_free(b);
643*4b3a91e6SGlenn Strauss 		return mod_auth_send_401_unauthorized_digest(srv, con, p, req, 0);
64481b2d1f0SGlenn Strauss 	}
64581b2d1f0SGlenn Strauss 
64681b2d1f0SGlenn Strauss 	/* check age of nonce.  Note that rand() is used in nonce generation
64781b2d1f0SGlenn Strauss 	 * in mod_auth_digest_generate_nonce().  If that were replaced
64881b2d1f0SGlenn Strauss 	 * with nanosecond time, then nonce secret would remain unique enough
64981b2d1f0SGlenn Strauss 	 * for the purposes of Digest auth, and would be reproducible (and
65081b2d1f0SGlenn Strauss 	 * verifiable) if nanoseconds were inclued with seconds as part of the
65181b2d1f0SGlenn Strauss 	 * nonce "timestamp:secret".  Since that is not done, timestamp in
65281b2d1f0SGlenn Strauss 	 * nonce could theoretically be modified and still produce same md5sum,
65381b2d1f0SGlenn Strauss 	 * but that is highly unlikely within a 10 min (moving) window of valid
65481b2d1f0SGlenn Strauss 	 * time relative to current time (now) */
65581b2d1f0SGlenn Strauss 	{
65681b2d1f0SGlenn Strauss 		time_t ts = 0;
65781b2d1f0SGlenn Strauss 		const unsigned char * const nonce_uns = (unsigned char *)nonce;
65881b2d1f0SGlenn Strauss 		for (i = 0; i < 8 && light_isxdigit(nonce_uns[i]); ++i) {
65981b2d1f0SGlenn Strauss 			ts = (ts << 4) + hex2int(nonce_uns[i]);
66081b2d1f0SGlenn Strauss 		}
66181b2d1f0SGlenn Strauss 		if (i != 8 || nonce[8] != ':'
66281b2d1f0SGlenn Strauss 		    || ts > srv->cur_ts || srv->cur_ts - ts > 600) { /*(10 mins)*/
663*4b3a91e6SGlenn Strauss 			/* nonce is stale; have client regenerate digest */
66481b2d1f0SGlenn Strauss 			buffer_free(b);
665*4b3a91e6SGlenn Strauss 			return mod_auth_send_401_unauthorized_digest(srv, con, p, req, 1);
66681b2d1f0SGlenn Strauss 		} /*(future: might send nextnonce when expiration is imminent)*/
66781b2d1f0SGlenn Strauss 	}
66881b2d1f0SGlenn Strauss 
669*4b3a91e6SGlenn Strauss 	mod_auth_setenv(srv, con, username, strlen(username), CONST_STR_LEN("Digest"));
67081b2d1f0SGlenn Strauss 
67181b2d1f0SGlenn Strauss 	buffer_free(b);
67281b2d1f0SGlenn Strauss 
67381b2d1f0SGlenn Strauss 	if (p->conf.auth_debug) {
67481b2d1f0SGlenn Strauss 		log_error_write(srv, __FILE__, __LINE__, "s",
67581b2d1f0SGlenn Strauss 				"digest: auth ok");
67681b2d1f0SGlenn Strauss 	}
677*4b3a91e6SGlenn Strauss 
678*4b3a91e6SGlenn Strauss 	return HANDLER_GO_ON;
67981b2d1f0SGlenn Strauss }
68081b2d1f0SGlenn Strauss 
68181b2d1f0SGlenn Strauss static int mod_auth_digest_generate_nonce(server *srv, mod_auth_plugin_data *p, buffer *fn, char (*out)[33]) {
68281b2d1f0SGlenn Strauss 	HASH h;
68381b2d1f0SGlenn Strauss 	li_MD5_CTX Md5Ctx;
68481b2d1f0SGlenn Strauss 	char hh[LI_ITOSTRING_LENGTH];
68581b2d1f0SGlenn Strauss 
68681b2d1f0SGlenn Strauss 	UNUSED(p);
68781b2d1f0SGlenn Strauss 
68881b2d1f0SGlenn Strauss 	/* generate shared-secret */
68981b2d1f0SGlenn Strauss 	li_MD5_Init(&Md5Ctx);
69081b2d1f0SGlenn Strauss 	li_MD5_Update(&Md5Ctx, CONST_BUF_LEN(fn));
69181b2d1f0SGlenn Strauss 	li_MD5_Update(&Md5Ctx, CONST_STR_LEN("+"));
69281b2d1f0SGlenn Strauss 
69381b2d1f0SGlenn Strauss 	/* we assume sizeof(time_t) == 4 here, but if not it ain't a problem at all */
69481b2d1f0SGlenn Strauss 	li_itostrn(hh, sizeof(hh), srv->cur_ts);
69581b2d1f0SGlenn Strauss 	li_MD5_Update(&Md5Ctx, (unsigned char *)hh, strlen(hh));
69681b2d1f0SGlenn Strauss 	li_MD5_Update(&Md5Ctx, (unsigned char *)srv->entropy, sizeof(srv->entropy));
69781b2d1f0SGlenn Strauss 	li_itostrn(hh, sizeof(hh), rand());
69881b2d1f0SGlenn Strauss 	li_MD5_Update(&Md5Ctx, (unsigned char *)hh, strlen(hh));
69981b2d1f0SGlenn Strauss 
70081b2d1f0SGlenn Strauss 	li_MD5_Final(h, &Md5Ctx);
70181b2d1f0SGlenn Strauss 
70281b2d1f0SGlenn Strauss 	CvtHex(h, out);
70381b2d1f0SGlenn Strauss 
70481b2d1f0SGlenn Strauss 	return 0;
70581b2d1f0SGlenn Strauss }
70681b2d1f0SGlenn Strauss 
707bcdc6a3bSJan Kneschke static handler_t mod_auth_uri_handler(server *srv, connection *con, void *p_d) {
708bcdc6a3bSJan Kneschke 	size_t k;
709*4b3a91e6SGlenn Strauss 	int auth_required;
710bcdc6a3bSJan Kneschke 	char *http_authorization = NULL;
711bcdc6a3bSJan Kneschke 	data_string *ds;
712bcdc6a3bSJan Kneschke 	mod_auth_plugin_data *p = p_d;
713bcdc6a3bSJan Kneschke 	array *req;
714f9d58670SStefan Bühler 	data_string *req_method;
715bcdc6a3bSJan Kneschke 
716bcdc6a3bSJan Kneschke 	/* select the right config */
7174a6ce4aeSJan Kneschke 	mod_auth_patch_connection(srv, con, p);
718bcdc6a3bSJan Kneschke 
719bcdc6a3bSJan Kneschke 	if (p->conf.auth_require == NULL) return HANDLER_GO_ON;
720bcdc6a3bSJan Kneschke 
721bcdc6a3bSJan Kneschke 	/*
722bcdc6a3bSJan Kneschke 	 * AUTH
723bcdc6a3bSJan Kneschke 	 *
724bcdc6a3bSJan Kneschke 	 */
725bcdc6a3bSJan Kneschke 
726bcdc6a3bSJan Kneschke 	/* do we have to ask for auth ? */
727bcdc6a3bSJan Kneschke 
728bcdc6a3bSJan Kneschke 	auth_required = 0;
729bcdc6a3bSJan Kneschke 
730bcdc6a3bSJan Kneschke 	/* search auth-directives for path */
731bcdc6a3bSJan Kneschke 	for (k = 0; k < p->conf.auth_require->used; k++) {
73267079527SJan Kneschke 		buffer *require = p->conf.auth_require->data[k]->key;
733bcdc6a3bSJan Kneschke 
734ad3e93eaSStefan Bühler 		if (buffer_is_empty(require)) continue;
735ad3e93eaSStefan Bühler 		if (buffer_string_length(con->uri.path) < buffer_string_length(require)) continue;
7364afd2ae2SJan Kneschke 
7374afd2ae2SJan Kneschke 		/* if we have a case-insensitive FS we have to lower-case the URI here too */
7384afd2ae2SJan Kneschke 
7394afd2ae2SJan Kneschke 		if (con->conf.force_lowercase_filenames) {
740ad3e93eaSStefan Bühler 			if (0 == strncasecmp(con->uri.path->ptr, require->ptr, buffer_string_length(require))) {
741bcdc6a3bSJan Kneschke 				auth_required = 1;
742bcdc6a3bSJan Kneschke 				break;
743bcdc6a3bSJan Kneschke 			}
7444afd2ae2SJan Kneschke 		} else {
745ad3e93eaSStefan Bühler 			if (0 == strncmp(con->uri.path->ptr, require->ptr, buffer_string_length(require))) {
7464afd2ae2SJan Kneschke 				auth_required = 1;
7474afd2ae2SJan Kneschke 				break;
7484afd2ae2SJan Kneschke 			}
7494afd2ae2SJan Kneschke 		}
750bcdc6a3bSJan Kneschke 	}
751bcdc6a3bSJan Kneschke 
752bcdc6a3bSJan Kneschke 	/* nothing to do for us */
753bcdc6a3bSJan Kneschke 	if (auth_required == 0) return HANDLER_GO_ON;
754bcdc6a3bSJan Kneschke 
755bcdc6a3bSJan Kneschke 	req = ((data_array *)(p->conf.auth_require->data[k]))->value;
756f9d58670SStefan Bühler 	req_method = (data_string *)array_get_element(req, "method");
757f9d58670SStefan Bühler 
758f9d58670SStefan Bühler 	if (0 == strcmp(req_method->value->ptr, "extern")) {
759f9d58670SStefan Bühler 		/* require REMOTE_USER to be already set */
760f9d58670SStefan Bühler 		if (NULL == (ds = (data_string *)array_get_element(con->environment, "REMOTE_USER"))) {
761f9d58670SStefan Bühler 			con->http_status = 401;
762f9d58670SStefan Bühler 			con->mode = DIRECT;
763f9d58670SStefan Bühler 			return HANDLER_FINISHED;
76481b2d1f0SGlenn Strauss 		} else if (mod_auth_match_rules(srv, req, ds->value->ptr, NULL, NULL)) {
765f9d58670SStefan Bühler 			log_error_write(srv, __FILE__, __LINE__, "s", "rules didn't match");
766f9d58670SStefan Bühler 			con->http_status = 401;
767f9d58670SStefan Bühler 			con->mode = DIRECT;
768f9d58670SStefan Bühler 			return HANDLER_FINISHED;
769f9d58670SStefan Bühler 		} else {
770f9d58670SStefan Bühler 			return HANDLER_GO_ON;
771f9d58670SStefan Bühler 		}
772f9d58670SStefan Bühler 	}
773bcdc6a3bSJan Kneschke 
774*4b3a91e6SGlenn Strauss 	/* check for configured backend
775*4b3a91e6SGlenn Strauss 	 * (XXX: should try to catch this at config time, if not "extern" method) */
776*4b3a91e6SGlenn Strauss 	if (NULL == p->conf.auth_backend) {
777*4b3a91e6SGlenn Strauss 		log_error_write(srv, __FILE__, __LINE__, "sb", "auth.backend not configured for", con->uri.path);
778*4b3a91e6SGlenn Strauss 		con->http_status = 500;
779*4b3a91e6SGlenn Strauss 		con->mode = DIRECT;
780*4b3a91e6SGlenn Strauss 		return HANDLER_FINISHED;
781*4b3a91e6SGlenn Strauss 	}
782*4b3a91e6SGlenn Strauss 
783bcdc6a3bSJan Kneschke 	/* try to get Authorization-header */
784bcdc6a3bSJan Kneschke 
785ad3e93eaSStefan Bühler 	if (NULL != (ds = (data_string *)array_get_element(con->request.headers, "Authorization")) && !buffer_is_empty(ds->value)) {
786bcdc6a3bSJan Kneschke 		char *auth_realm;
787bcdc6a3bSJan Kneschke 
788f9d58670SStefan Bühler 		http_authorization = ds->value->ptr;
789bcdc6a3bSJan Kneschke 
790bcdc6a3bSJan Kneschke 		/* parse auth-header */
791bcdc6a3bSJan Kneschke 		if (NULL != (auth_realm = strchr(http_authorization, ' '))) {
792bcdc6a3bSJan Kneschke 			int auth_type_len = auth_realm - http_authorization;
793bcdc6a3bSJan Kneschke 
794bcdc6a3bSJan Kneschke 			if ((auth_type_len == 5) &&
795708f499dSJan Kneschke 			    (0 == strncasecmp(http_authorization, "Basic", auth_type_len))) {
796f9d58670SStefan Bühler 				if (0 == strcmp(req_method->value->ptr, "basic")) {
797*4b3a91e6SGlenn Strauss 					return mod_auth_basic_check(srv, con, p, req, auth_realm+1);
798bcdc6a3bSJan Kneschke 				}
799bcdc6a3bSJan Kneschke 			} else if ((auth_type_len == 6) &&
800708f499dSJan Kneschke 				   (0 == strncasecmp(http_authorization, "Digest", auth_type_len))) {
801f9d58670SStefan Bühler 				if (0 == strcmp(req_method->value->ptr, "digest")) {
802*4b3a91e6SGlenn Strauss 					return mod_auth_digest_check(srv, con, p, req, auth_realm+1);
803bcdc6a3bSJan Kneschke 				}
804bcdc6a3bSJan Kneschke 			} else {
805bcdc6a3bSJan Kneschke 				log_error_write(srv, __FILE__, __LINE__, "ss",
806780e8e6eSStefan Bühler 						"unknown authentication type:",
807bcdc6a3bSJan Kneschke 						http_authorization);
808*4b3a91e6SGlenn Strauss 				return mod_auth_send_400_bad_request(srv, con);
809*4b3a91e6SGlenn Strauss 			}
810*4b3a91e6SGlenn Strauss 		} else {
811*4b3a91e6SGlenn Strauss 			return mod_auth_send_400_bad_request(srv, con);
812bcdc6a3bSJan Kneschke 		}
813bcdc6a3bSJan Kneschke 	}
814bcdc6a3bSJan Kneschke 
815*4b3a91e6SGlenn Strauss 	if (0 == strcmp(req_method->value->ptr, "basic")) {
816*4b3a91e6SGlenn Strauss 		return mod_auth_send_401_unauthorized_basic(srv, con, p, req);
817*4b3a91e6SGlenn Strauss 	} else if (0 == strcmp(req_method->value->ptr, "digest")) {
818*4b3a91e6SGlenn Strauss 		return mod_auth_send_401_unauthorized_digest(srv, con, p, req, 0);
819bcdc6a3bSJan Kneschke 	} else {
820bcdc6a3bSJan Kneschke 		/* evil */
821*4b3a91e6SGlenn Strauss 		con->http_status = 401;
822*4b3a91e6SGlenn Strauss 		con->mode = DIRECT;
823bcdc6a3bSJan Kneschke 		return HANDLER_FINISHED;
8247187271fSStefan Bühler 	}
825bcdc6a3bSJan Kneschke }
826bcdc6a3bSJan Kneschke 
827bcdc6a3bSJan Kneschke SETDEFAULTS_FUNC(mod_auth_set_defaults) {
828bcdc6a3bSJan Kneschke 	mod_auth_plugin_data *p = p_d;
829bcdc6a3bSJan Kneschke 	size_t i;
830bcdc6a3bSJan Kneschke 
831bcdc6a3bSJan Kneschke 	config_values_t cv[] = {
832bcdc6a3bSJan Kneschke 		{ "auth.backend",                   NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 0 */
833*4b3a91e6SGlenn Strauss 		{ "auth.require",                   NULL, T_CONFIG_LOCAL, T_CONFIG_SCOPE_CONNECTION },  /* 1 */
834*4b3a91e6SGlenn Strauss 		{ "auth.debug",                     NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION },  /* 2 */
835bcdc6a3bSJan Kneschke 		{ NULL,                             NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET }
836bcdc6a3bSJan Kneschke 	};
837bcdc6a3bSJan Kneschke 
8386f208cfdSStefan Bühler 	p->config_storage = calloc(1, srv->config_context->used * sizeof(mod_auth_plugin_config *));
839bcdc6a3bSJan Kneschke 
840bcdc6a3bSJan Kneschke 	for (i = 0; i < srv->config_context->used; i++) {
841c512345fSStefan Bühler 		data_config const* config = (data_config const*)srv->config_context->data[i];
842bcdc6a3bSJan Kneschke 		mod_auth_plugin_config *s;
843bcdc6a3bSJan Kneschke 		size_t n;
844bcdc6a3bSJan Kneschke 		data_array *da;
845bcdc6a3bSJan Kneschke 
846bac9212cSJan Kneschke 		s = calloc(1, sizeof(mod_auth_plugin_config));
847bcdc6a3bSJan Kneschke 		s->auth_backend_conf = buffer_init();
848bcdc6a3bSJan Kneschke 
849bcdc6a3bSJan Kneschke 		s->auth_debug = 0;
850bcdc6a3bSJan Kneschke 
851bcdc6a3bSJan Kneschke 		s->auth_require = array_init();
852bcdc6a3bSJan Kneschke 
853bcdc6a3bSJan Kneschke 		cv[0].destination = s->auth_backend_conf;
854*4b3a91e6SGlenn Strauss 		cv[1].destination = s->auth_require;
855*4b3a91e6SGlenn Strauss 		cv[2].destination = &(s->auth_debug);
856bcdc6a3bSJan Kneschke 
857bcdc6a3bSJan Kneschke 		p->config_storage[i] = s;
858bcdc6a3bSJan Kneschke 
859c512345fSStefan Bühler 		if (0 != config_insert_values_global(srv, config->value, cv, i == 0 ? T_CONFIG_SCOPE_SERVER : T_CONFIG_SCOPE_CONNECTION)) {
860bcdc6a3bSJan Kneschke 			return HANDLER_ERROR;
861bcdc6a3bSJan Kneschke 		}
862bcdc6a3bSJan Kneschke 
863ad3e93eaSStefan Bühler 		if (!buffer_string_is_empty(s->auth_backend_conf)) {
864*4b3a91e6SGlenn Strauss 			s->auth_backend = http_auth_backend_get(s->auth_backend_conf);
865*4b3a91e6SGlenn Strauss 			if (NULL == s->auth_backend) {
866bcdc6a3bSJan Kneschke 				log_error_write(srv, __FILE__, __LINE__, "sb", "auth.backend not supported:", s->auth_backend_conf);
867bcdc6a3bSJan Kneschke 
868bcdc6a3bSJan Kneschke 				return HANDLER_ERROR;
869bcdc6a3bSJan Kneschke 			}
870bcdc6a3bSJan Kneschke 		}
871bcdc6a3bSJan Kneschke 
872bcdc6a3bSJan Kneschke 		/* no auth.require for this section */
873c512345fSStefan Bühler 		if (NULL == (da = (data_array *)array_get_element(config->value, "auth.require"))) continue;
874bcdc6a3bSJan Kneschke 
875bcdc6a3bSJan Kneschke 		if (da->type != TYPE_ARRAY) continue;
876bcdc6a3bSJan Kneschke 
877bcdc6a3bSJan Kneschke 		for (n = 0; n < da->value->used; n++) {
878bcdc6a3bSJan Kneschke 			size_t m;
879bcdc6a3bSJan Kneschke 			data_array *da_file = (data_array *)da->value->data[n];
880bcdc6a3bSJan Kneschke 			const char *method, *realm, *require;
881bcdc6a3bSJan Kneschke 
882bcdc6a3bSJan Kneschke 			if (da->value->data[n]->type != TYPE_ARRAY) {
883cc7a8ae0SJan Kneschke 				log_error_write(srv, __FILE__, __LINE__, "ss",
884cc7a8ae0SJan Kneschke 						"auth.require should contain an array as in:",
885cc7a8ae0SJan Kneschke 						"auth.require = ( \"...\" => ( ..., ...) )");
886bcdc6a3bSJan Kneschke 
887bcdc6a3bSJan Kneschke 				return HANDLER_ERROR;
888bcdc6a3bSJan Kneschke 			}
889bcdc6a3bSJan Kneschke 
890bcdc6a3bSJan Kneschke 			method = realm = require = NULL;
891bcdc6a3bSJan Kneschke 
892bcdc6a3bSJan Kneschke 			for (m = 0; m < da_file->value->used; m++) {
893bcdc6a3bSJan Kneschke 				if (da_file->value->data[m]->type == TYPE_STRING) {
894bcdc6a3bSJan Kneschke 					if (0 == strcmp(da_file->value->data[m]->key->ptr, "method")) {
895bcdc6a3bSJan Kneschke 						method = ((data_string *)(da_file->value->data[m]))->value->ptr;
896bcdc6a3bSJan Kneschke 					} else if (0 == strcmp(da_file->value->data[m]->key->ptr, "realm")) {
897bcdc6a3bSJan Kneschke 						realm = ((data_string *)(da_file->value->data[m]))->value->ptr;
898bcdc6a3bSJan Kneschke 					} else if (0 == strcmp(da_file->value->data[m]->key->ptr, "require")) {
899bcdc6a3bSJan Kneschke 						require = ((data_string *)(da_file->value->data[m]))->value->ptr;
900bcdc6a3bSJan Kneschke 					} else {
901cc7a8ae0SJan Kneschke 						log_error_write(srv, __FILE__, __LINE__, "ssbs",
902cc7a8ae0SJan Kneschke 							"the field is unknown in:",
903cc7a8ae0SJan Kneschke 							"auth.require = ( \"...\" => ( ..., -> \"",
904cc7a8ae0SJan Kneschke 							da_file->value->data[m]->key,
905cc7a8ae0SJan Kneschke 							"\" <- => \"...\" ) )");
906cc7a8ae0SJan Kneschke 
907bcdc6a3bSJan Kneschke 						return HANDLER_ERROR;
908bcdc6a3bSJan Kneschke 					}
909bcdc6a3bSJan Kneschke 				} else {
910cc7a8ae0SJan Kneschke 					log_error_write(srv, __FILE__, __LINE__, "ssbs",
911cc7a8ae0SJan Kneschke 						"a string was expected for:",
912cc7a8ae0SJan Kneschke 						"auth.require = ( \"...\" => ( ..., -> \"",
913cc7a8ae0SJan Kneschke 						da_file->value->data[m]->key,
914cc7a8ae0SJan Kneschke 						"\" <- => \"...\" ) )");
915bcdc6a3bSJan Kneschke 
916bcdc6a3bSJan Kneschke 					return HANDLER_ERROR;
917bcdc6a3bSJan Kneschke 				}
918bcdc6a3bSJan Kneschke 			}
919bcdc6a3bSJan Kneschke 
920bcdc6a3bSJan Kneschke 			if (method == NULL) {
921cc7a8ae0SJan Kneschke 				log_error_write(srv, __FILE__, __LINE__, "ss",
9224df22f2aSStefan Bühler 						"the method field is missing in:",
923cc7a8ae0SJan Kneschke 						"auth.require = ( \"...\" => ( ..., \"method\" => \"...\" ) )");
924bcdc6a3bSJan Kneschke 				return HANDLER_ERROR;
9254a6ce4aeSJan Kneschke 			} else {
9264a6ce4aeSJan Kneschke 				if (0 != strcmp(method, "basic") &&
927f9d58670SStefan Bühler 				    0 != strcmp(method, "digest") &&
928f9d58670SStefan Bühler 				    0 != strcmp(method, "extern")) {
929cc7a8ae0SJan Kneschke 					log_error_write(srv, __FILE__, __LINE__, "ss",
930f9d58670SStefan Bühler 							"method has to be either \"basic\", \"digest\" or \"extern\" in",
931cc7a8ae0SJan Kneschke 							"auth.require = ( \"...\" => ( ..., \"method\" => \"...\") )");
9324a6ce4aeSJan Kneschke 					return HANDLER_ERROR;
9334a6ce4aeSJan Kneschke 				}
934bcdc6a3bSJan Kneschke 			}
935bcdc6a3bSJan Kneschke 
936bcdc6a3bSJan Kneschke 			if (realm == NULL) {
937cc7a8ae0SJan Kneschke 				log_error_write(srv, __FILE__, __LINE__, "ss",
9384df22f2aSStefan Bühler 						"the realm field is missing in:",
939cc7a8ae0SJan Kneschke 						"auth.require = ( \"...\" => ( ..., \"realm\" => \"...\" ) )");
940bcdc6a3bSJan Kneschke 				return HANDLER_ERROR;
941bcdc6a3bSJan Kneschke 			}
942bcdc6a3bSJan Kneschke 
943bcdc6a3bSJan Kneschke 			if (require == NULL) {
944cc7a8ae0SJan Kneschke 				log_error_write(srv, __FILE__, __LINE__, "ss",
945cc7a8ae0SJan Kneschke 						"the require field is missing in:",
946cc7a8ae0SJan Kneschke 						"auth.require = ( \"...\" => ( ..., \"require\" => \"...\" ) )");
947bcdc6a3bSJan Kneschke 				return HANDLER_ERROR;
948bcdc6a3bSJan Kneschke 			}
949bcdc6a3bSJan Kneschke 
950bcdc6a3bSJan Kneschke 			if (method && realm && require) {
951bcdc6a3bSJan Kneschke 				data_string *ds;
952bcdc6a3bSJan Kneschke 				data_array *a;
953bcdc6a3bSJan Kneschke 
954bcdc6a3bSJan Kneschke 				a = data_array_init();
9556afad87dSStefan Bühler 				buffer_copy_buffer(a->key, da_file->key);
956bcdc6a3bSJan Kneschke 
957bcdc6a3bSJan Kneschke 				ds = data_string_init();
958bcdc6a3bSJan Kneschke 
95952861d77SStefan Bühler 				buffer_copy_string_len(ds->key, CONST_STR_LEN("method"));
960bcdc6a3bSJan Kneschke 				buffer_copy_string(ds->value, method);
961bcdc6a3bSJan Kneschke 
962bcdc6a3bSJan Kneschke 				array_insert_unique(a->value, (data_unset *)ds);
963bcdc6a3bSJan Kneschke 
964bcdc6a3bSJan Kneschke 				ds = data_string_init();
965bcdc6a3bSJan Kneschke 
96652861d77SStefan Bühler 				buffer_copy_string_len(ds->key, CONST_STR_LEN("realm"));
967bcdc6a3bSJan Kneschke 				buffer_copy_string(ds->value, realm);
968bcdc6a3bSJan Kneschke 
969bcdc6a3bSJan Kneschke 				array_insert_unique(a->value, (data_unset *)ds);
970bcdc6a3bSJan Kneschke 
971bcdc6a3bSJan Kneschke 				ds = data_string_init();
972bcdc6a3bSJan Kneschke 
97352861d77SStefan Bühler 				buffer_copy_string_len(ds->key, CONST_STR_LEN("require"));
974bcdc6a3bSJan Kneschke 				buffer_copy_string(ds->value, require);
975bcdc6a3bSJan Kneschke 
976bcdc6a3bSJan Kneschke 				array_insert_unique(a->value, (data_unset *)ds);
977bcdc6a3bSJan Kneschke 
978bcdc6a3bSJan Kneschke 				array_insert_unique(s->auth_require, (data_unset *)a);
979bcdc6a3bSJan Kneschke 			}
980bcdc6a3bSJan Kneschke 		}
981ea7b9abaSJan Kneschke 	}
982ea7b9abaSJan Kneschke 
983ea7b9abaSJan Kneschke 	return HANDLER_GO_ON;
984ea7b9abaSJan Kneschke }
985ea7b9abaSJan Kneschke 
98663f785a2SStefan Bühler int mod_auth_plugin_init(plugin *p);
987bcdc6a3bSJan Kneschke int mod_auth_plugin_init(plugin *p) {
988bcdc6a3bSJan Kneschke 	p->version     = LIGHTTPD_VERSION_ID;
989bcdc6a3bSJan Kneschke 	p->name        = buffer_init_string("auth");
990bcdc6a3bSJan Kneschke 	p->init        = mod_auth_init;
991bcdc6a3bSJan Kneschke 	p->set_defaults = mod_auth_set_defaults;
992bcdc6a3bSJan Kneschke 	p->handle_uri_clean = mod_auth_uri_handler;
993bcdc6a3bSJan Kneschke 	p->cleanup     = mod_auth_free;
994bcdc6a3bSJan Kneschke 
995bcdc6a3bSJan Kneschke 	p->data        = NULL;
996bcdc6a3bSJan Kneschke 
997bcdc6a3bSJan Kneschke 	return 0;
998bcdc6a3bSJan Kneschke }
999