xref: /lighttpd1.4/src/mod_auth.c (revision 81b2d1f0)
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 
8*81b2d1f0SGlenn Strauss #include "inet_ntop_cache.h"
9*81b2d1f0SGlenn Strauss #include "base64.h"
10*81b2d1f0SGlenn Strauss #include "md5.h"
11*81b2d1f0SGlenn 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 
21ea7b9abaSJan Kneschke handler_t auth_ldap_init(server *srv, mod_auth_plugin_config *s);
22ea7b9abaSJan Kneschke 
23bcdc6a3bSJan Kneschke 
24bcdc6a3bSJan Kneschke /**
25bcdc6a3bSJan Kneschke  * the basic and digest auth framework
26bcdc6a3bSJan Kneschke  *
27bcdc6a3bSJan Kneschke  * - config handling
28bcdc6a3bSJan Kneschke  * - protocol handling
29bcdc6a3bSJan Kneschke  *
30bcdc6a3bSJan Kneschke  * http_auth.c
31bcdc6a3bSJan Kneschke  * http_auth_digest.c
32bcdc6a3bSJan Kneschke  *
33bcdc6a3bSJan Kneschke  * do the real work
34bcdc6a3bSJan Kneschke  */
35bcdc6a3bSJan Kneschke 
36bcdc6a3bSJan Kneschke INIT_FUNC(mod_auth_init) {
37bcdc6a3bSJan Kneschke 	mod_auth_plugin_data *p;
38bcdc6a3bSJan Kneschke 
39bcdc6a3bSJan Kneschke 	p = calloc(1, sizeof(*p));
40bcdc6a3bSJan Kneschke 
41bcdc6a3bSJan Kneschke 	p->tmp_buf = buffer_init();
42bcdc6a3bSJan Kneschke 
43bcdc6a3bSJan Kneschke 	p->auth_user = buffer_init();
44bcdc6a3bSJan Kneschke #ifdef USE_LDAP
45bcdc6a3bSJan Kneschke 	p->ldap_filter = buffer_init();
46bcdc6a3bSJan Kneschke #endif
47bcdc6a3bSJan Kneschke 
48bcdc6a3bSJan Kneschke 	return p;
49bcdc6a3bSJan Kneschke }
50bcdc6a3bSJan Kneschke 
51bcdc6a3bSJan Kneschke FREE_FUNC(mod_auth_free) {
52bcdc6a3bSJan Kneschke 	mod_auth_plugin_data *p = p_d;
53bcdc6a3bSJan Kneschke 
54bcdc6a3bSJan Kneschke 	UNUSED(srv);
55bcdc6a3bSJan Kneschke 
56bcdc6a3bSJan Kneschke 	if (!p) return HANDLER_GO_ON;
57bcdc6a3bSJan Kneschke 
58bcdc6a3bSJan Kneschke 	buffer_free(p->tmp_buf);
59bcdc6a3bSJan Kneschke 	buffer_free(p->auth_user);
60bcdc6a3bSJan Kneschke #ifdef USE_LDAP
61bcdc6a3bSJan Kneschke 	buffer_free(p->ldap_filter);
62bcdc6a3bSJan Kneschke #endif
63bcdc6a3bSJan Kneschke 
64bcdc6a3bSJan Kneschke 	if (p->config_storage) {
65bcdc6a3bSJan Kneschke 		size_t i;
66bcdc6a3bSJan Kneschke 		for (i = 0; i < srv->config_context->used; i++) {
67bcdc6a3bSJan Kneschke 			mod_auth_plugin_config *s = p->config_storage[i];
68bcdc6a3bSJan Kneschke 
6933cebeb0SStefan Bühler 			if (NULL == s) continue;
704a6ce4aeSJan Kneschke 
71bcdc6a3bSJan Kneschke 			array_free(s->auth_require);
72bcdc6a3bSJan Kneschke 			buffer_free(s->auth_plain_groupfile);
73bcdc6a3bSJan Kneschke 			buffer_free(s->auth_plain_userfile);
74bcdc6a3bSJan Kneschke 			buffer_free(s->auth_htdigest_userfile);
75bcdc6a3bSJan Kneschke 			buffer_free(s->auth_htpasswd_userfile);
76bcdc6a3bSJan Kneschke 			buffer_free(s->auth_backend_conf);
77bcdc6a3bSJan Kneschke 
78bcdc6a3bSJan Kneschke 			buffer_free(s->auth_ldap_hostname);
79bcdc6a3bSJan Kneschke 			buffer_free(s->auth_ldap_basedn);
809238cad6SJan Kneschke 			buffer_free(s->auth_ldap_binddn);
819238cad6SJan Kneschke 			buffer_free(s->auth_ldap_bindpw);
82bcdc6a3bSJan Kneschke 			buffer_free(s->auth_ldap_filter);
834a6ce4aeSJan Kneschke 			buffer_free(s->auth_ldap_cafile);
84bcdc6a3bSJan Kneschke 
85bcdc6a3bSJan Kneschke #ifdef USE_LDAP
86bcdc6a3bSJan Kneschke 			buffer_free(s->ldap_filter_pre);
87bcdc6a3bSJan Kneschke 			buffer_free(s->ldap_filter_post);
88bcdc6a3bSJan Kneschke 
89bcdc6a3bSJan Kneschke 			if (s->ldap) ldap_unbind_s(s->ldap);
90bcdc6a3bSJan Kneschke #endif
91bcdc6a3bSJan Kneschke 
92bcdc6a3bSJan Kneschke 			free(s);
93bcdc6a3bSJan Kneschke 		}
94bcdc6a3bSJan Kneschke 		free(p->config_storage);
95bcdc6a3bSJan Kneschke 	}
96bcdc6a3bSJan Kneschke 
97bcdc6a3bSJan Kneschke 	free(p);
98bcdc6a3bSJan Kneschke 
99bcdc6a3bSJan Kneschke 	return HANDLER_GO_ON;
100bcdc6a3bSJan Kneschke }
101bcdc6a3bSJan Kneschke 
102bcdc6a3bSJan Kneschke #define PATCH(x) \
103bcdc6a3bSJan Kneschke 	p->conf.x = s->x;
1044a6ce4aeSJan Kneschke static int mod_auth_patch_connection(server *srv, connection *con, mod_auth_plugin_data *p) {
105bcdc6a3bSJan Kneschke 	size_t i, j;
1064a6ce4aeSJan Kneschke 	mod_auth_plugin_config *s = p->config_storage[0];
1074a6ce4aeSJan Kneschke 
1084a6ce4aeSJan Kneschke 	PATCH(auth_backend);
1094a6ce4aeSJan Kneschke 	PATCH(auth_plain_groupfile);
1104a6ce4aeSJan Kneschke 	PATCH(auth_plain_userfile);
1114a6ce4aeSJan Kneschke 	PATCH(auth_htdigest_userfile);
1124a6ce4aeSJan Kneschke 	PATCH(auth_htpasswd_userfile);
1134a6ce4aeSJan Kneschke 	PATCH(auth_require);
1144a6ce4aeSJan Kneschke 	PATCH(auth_debug);
1154a6ce4aeSJan Kneschke 	PATCH(auth_ldap_hostname);
1164a6ce4aeSJan Kneschke 	PATCH(auth_ldap_basedn);
1179238cad6SJan Kneschke 	PATCH(auth_ldap_binddn);
1189238cad6SJan Kneschke 	PATCH(auth_ldap_bindpw);
1194a6ce4aeSJan Kneschke 	PATCH(auth_ldap_filter);
1204a6ce4aeSJan Kneschke 	PATCH(auth_ldap_cafile);
1214a6ce4aeSJan Kneschke 	PATCH(auth_ldap_starttls);
122d471bcc9SMarcus Rückert 	PATCH(auth_ldap_allow_empty_pw);
1234a6ce4aeSJan Kneschke #ifdef USE_LDAP
124cfba07cbSStefan Bühler 	p->anon_conf = s;
1254a6ce4aeSJan Kneschke 	PATCH(ldap_filter_pre);
1264a6ce4aeSJan Kneschke 	PATCH(ldap_filter_post);
1274a6ce4aeSJan Kneschke #endif
128bcdc6a3bSJan Kneschke 
129bcdc6a3bSJan Kneschke 	/* skip the first, the global context */
130bcdc6a3bSJan Kneschke 	for (i = 1; i < srv->config_context->used; i++) {
131bcdc6a3bSJan Kneschke 		data_config *dc = (data_config *)srv->config_context->data[i];
1324a6ce4aeSJan Kneschke 		s = p->config_storage[i];
133bcdc6a3bSJan Kneschke 
134bcdc6a3bSJan Kneschke 		/* condition didn't match */
135bcdc6a3bSJan Kneschke 		if (!config_check_cond(srv, con, dc)) continue;
136bcdc6a3bSJan Kneschke 
137bcdc6a3bSJan Kneschke 		/* merge config */
138bcdc6a3bSJan Kneschke 		for (j = 0; j < dc->value->used; j++) {
139bcdc6a3bSJan Kneschke 			data_unset *du = dc->value->data[j];
140bcdc6a3bSJan Kneschke 
141bcdc6a3bSJan Kneschke 			if (buffer_is_equal_string(du->key, CONST_STR_LEN("auth.backend"))) {
142bcdc6a3bSJan Kneschke 				PATCH(auth_backend);
143bcdc6a3bSJan Kneschke 			} else if (buffer_is_equal_string(du->key, CONST_STR_LEN("auth.backend.plain.groupfile"))) {
144bcdc6a3bSJan Kneschke 				PATCH(auth_plain_groupfile);
145bcdc6a3bSJan Kneschke 			} else if (buffer_is_equal_string(du->key, CONST_STR_LEN("auth.backend.plain.userfile"))) {
146bcdc6a3bSJan Kneschke 				PATCH(auth_plain_userfile);
147bcdc6a3bSJan Kneschke 			} else if (buffer_is_equal_string(du->key, CONST_STR_LEN("auth.backend.htdigest.userfile"))) {
148bcdc6a3bSJan Kneschke 				PATCH(auth_htdigest_userfile);
149bcdc6a3bSJan Kneschke 			} else if (buffer_is_equal_string(du->key, CONST_STR_LEN("auth.backend.htpasswd.userfile"))) {
150bcdc6a3bSJan Kneschke 				PATCH(auth_htpasswd_userfile);
151bcdc6a3bSJan Kneschke 			} else if (buffer_is_equal_string(du->key, CONST_STR_LEN("auth.require"))) {
152bcdc6a3bSJan Kneschke 				PATCH(auth_require);
153bcdc6a3bSJan Kneschke 			} else if (buffer_is_equal_string(du->key, CONST_STR_LEN("auth.debug"))) {
154bcdc6a3bSJan Kneschke 				PATCH(auth_debug);
155bcdc6a3bSJan Kneschke 			} else if (buffer_is_equal_string(du->key, CONST_STR_LEN("auth.backend.ldap.hostname"))) {
156bcdc6a3bSJan Kneschke 				PATCH(auth_ldap_hostname);
157bcdc6a3bSJan Kneschke #ifdef USE_LDAP
158cfba07cbSStefan Bühler 				p->anon_conf = s;
159bcdc6a3bSJan Kneschke #endif
160bcdc6a3bSJan Kneschke 			} else if (buffer_is_equal_string(du->key, CONST_STR_LEN("auth.backend.ldap.base-dn"))) {
161bcdc6a3bSJan Kneschke 				PATCH(auth_ldap_basedn);
162bcdc6a3bSJan Kneschke 			} else if (buffer_is_equal_string(du->key, CONST_STR_LEN("auth.backend.ldap.filter"))) {
163bcdc6a3bSJan Kneschke 				PATCH(auth_ldap_filter);
164ba715e1cSStefan Bühler #ifdef USE_LDAP
165ba715e1cSStefan Bühler 				PATCH(ldap_filter_pre);
166ba715e1cSStefan Bühler 				PATCH(ldap_filter_post);
167ba715e1cSStefan Bühler #endif
1684a6ce4aeSJan Kneschke 			} else if (buffer_is_equal_string(du->key, CONST_STR_LEN("auth.backend.ldap.ca-file"))) {
1694a6ce4aeSJan Kneschke 				PATCH(auth_ldap_cafile);
1704a6ce4aeSJan Kneschke 			} else if (buffer_is_equal_string(du->key, CONST_STR_LEN("auth.backend.ldap.starttls"))) {
1714a6ce4aeSJan Kneschke 				PATCH(auth_ldap_starttls);
17286766c10SStefan Bühler 			} else if (buffer_is_equal_string(du->key, CONST_STR_LEN("auth.backend.ldap.bind-dn"))) {
17386766c10SStefan Bühler 				PATCH(auth_ldap_binddn);
17486766c10SStefan Bühler 			} else if (buffer_is_equal_string(du->key, CONST_STR_LEN("auth.backend.ldap.bind-pw"))) {
17586766c10SStefan Bühler 				PATCH(auth_ldap_bindpw);
176d471bcc9SMarcus Rückert 			} else if (buffer_is_equal_string(du->key, CONST_STR_LEN("auth.backend.ldap.allow-empty-pw"))) {
177d471bcc9SMarcus Rückert 				PATCH(auth_ldap_allow_empty_pw);
178bcdc6a3bSJan Kneschke 			}
179bcdc6a3bSJan Kneschke 		}
180bcdc6a3bSJan Kneschke 	}
181bcdc6a3bSJan Kneschke 
182bcdc6a3bSJan Kneschke 	return 0;
183bcdc6a3bSJan Kneschke }
184bcdc6a3bSJan Kneschke #undef PATCH
185bcdc6a3bSJan Kneschke 
186*81b2d1f0SGlenn Strauss static int mod_auth_match_rules(server *srv, array *req, const char *username, const char *group, const char *host) {
187*81b2d1f0SGlenn Strauss 	const char *r = NULL, *rules = NULL;
188*81b2d1f0SGlenn Strauss 	int username_len;
189*81b2d1f0SGlenn Strauss 	data_string *require;
190*81b2d1f0SGlenn Strauss 
191*81b2d1f0SGlenn Strauss 	UNUSED(group);
192*81b2d1f0SGlenn Strauss 	UNUSED(host);
193*81b2d1f0SGlenn Strauss 
194*81b2d1f0SGlenn Strauss 	require = (data_string *)array_get_element(req, "require");
195*81b2d1f0SGlenn Strauss 	if (!require) return -1; /*(should not happen; config is validated at startup)*/
196*81b2d1f0SGlenn Strauss 
197*81b2d1f0SGlenn Strauss 	/* if we get here, the user we got a authed user */
198*81b2d1f0SGlenn Strauss 	if (0 == strcmp(require->value->ptr, "valid-user")) {
199*81b2d1f0SGlenn Strauss 		return 0;
200*81b2d1f0SGlenn Strauss 	}
201*81b2d1f0SGlenn Strauss 
202*81b2d1f0SGlenn Strauss 	/* user=name1|group=name3|host=name4 */
203*81b2d1f0SGlenn Strauss 
204*81b2d1f0SGlenn Strauss 	/* seperate the string by | */
205*81b2d1f0SGlenn Strauss #if 0
206*81b2d1f0SGlenn Strauss 	log_error_write(srv, __FILE__, __LINE__, "sb", "rules", require->value);
207*81b2d1f0SGlenn Strauss #endif
208*81b2d1f0SGlenn Strauss 
209*81b2d1f0SGlenn Strauss 	username_len = username ? strlen(username) : 0;
210*81b2d1f0SGlenn Strauss 
211*81b2d1f0SGlenn Strauss 	r = rules = require->value->ptr;
212*81b2d1f0SGlenn Strauss 
213*81b2d1f0SGlenn Strauss 	while (1) {
214*81b2d1f0SGlenn Strauss 		const char *eq;
215*81b2d1f0SGlenn Strauss 		const char *k, *v, *e;
216*81b2d1f0SGlenn Strauss 		int k_len, v_len, r_len;
217*81b2d1f0SGlenn Strauss 
218*81b2d1f0SGlenn Strauss 		e = strchr(r, '|');
219*81b2d1f0SGlenn Strauss 
220*81b2d1f0SGlenn Strauss 		if (e) {
221*81b2d1f0SGlenn Strauss 			r_len = e - r;
222*81b2d1f0SGlenn Strauss 		} else {
223*81b2d1f0SGlenn Strauss 			r_len = strlen(rules) - (r - rules);
224*81b2d1f0SGlenn Strauss 		}
225*81b2d1f0SGlenn Strauss 
226*81b2d1f0SGlenn Strauss 		/* from r to r + r_len is a rule */
227*81b2d1f0SGlenn Strauss 
228*81b2d1f0SGlenn Strauss 		if (0 == strncmp(r, "valid-user", r_len)) {
229*81b2d1f0SGlenn Strauss 			log_error_write(srv, __FILE__, __LINE__, "sb",
230*81b2d1f0SGlenn Strauss 					"parsing the 'require' section in 'auth.require' failed: valid-user cannot be combined with other require rules",
231*81b2d1f0SGlenn Strauss 					require->value);
232*81b2d1f0SGlenn Strauss 			return -1;
233*81b2d1f0SGlenn Strauss 		}
234*81b2d1f0SGlenn Strauss 
235*81b2d1f0SGlenn Strauss 		/* search for = in the rules */
236*81b2d1f0SGlenn Strauss 		if (NULL == (eq = strchr(r, '='))) {
237*81b2d1f0SGlenn Strauss 			log_error_write(srv, __FILE__, __LINE__, "sb",
238*81b2d1f0SGlenn Strauss 					"parsing the 'require' section in 'auth.require' failed: a = is missing",
239*81b2d1f0SGlenn Strauss 					require->value);
240*81b2d1f0SGlenn Strauss 			return -1;
241*81b2d1f0SGlenn Strauss 		}
242*81b2d1f0SGlenn Strauss 
243*81b2d1f0SGlenn Strauss 		/* = out of range */
244*81b2d1f0SGlenn Strauss 		if (eq > r + r_len) {
245*81b2d1f0SGlenn Strauss 			log_error_write(srv, __FILE__, __LINE__, "sb",
246*81b2d1f0SGlenn Strauss 					"parsing the 'require' section in 'auth.require' failed: = out of range",
247*81b2d1f0SGlenn Strauss 					require->value);
248*81b2d1f0SGlenn Strauss 
249*81b2d1f0SGlenn Strauss 			return -1;
250*81b2d1f0SGlenn Strauss 		}
251*81b2d1f0SGlenn Strauss 
252*81b2d1f0SGlenn Strauss 		/* the part before the = is user|group|host */
253*81b2d1f0SGlenn Strauss 
254*81b2d1f0SGlenn Strauss 		k = r;
255*81b2d1f0SGlenn Strauss 		k_len = eq - r;
256*81b2d1f0SGlenn Strauss 		v = eq + 1;
257*81b2d1f0SGlenn Strauss 		v_len = r_len - k_len - 1;
258*81b2d1f0SGlenn Strauss 
259*81b2d1f0SGlenn Strauss 		if (k_len == 4) {
260*81b2d1f0SGlenn Strauss 			if (0 == strncmp(k, "user", k_len)) {
261*81b2d1f0SGlenn Strauss 				if (username &&
262*81b2d1f0SGlenn Strauss 				    username_len == v_len &&
263*81b2d1f0SGlenn Strauss 				    0 == strncmp(username, v, v_len)) {
264*81b2d1f0SGlenn Strauss 					return 0;
265*81b2d1f0SGlenn Strauss 				}
266*81b2d1f0SGlenn Strauss 			} else if (0 == strncmp(k, "host", k_len)) {
267*81b2d1f0SGlenn Strauss 				log_error_write(srv, __FILE__, __LINE__, "s", "host ... (not implemented)");
268*81b2d1f0SGlenn Strauss 			} else {
269*81b2d1f0SGlenn Strauss 				log_error_write(srv, __FILE__, __LINE__, "s", "unknown key");
270*81b2d1f0SGlenn Strauss 				return -1;
271*81b2d1f0SGlenn Strauss 			}
272*81b2d1f0SGlenn Strauss 		} else if (k_len == 5) {
273*81b2d1f0SGlenn Strauss 			if (0 == strncmp(k, "group", k_len)) {
274*81b2d1f0SGlenn Strauss 				log_error_write(srv, __FILE__, __LINE__, "s", "group ... (not implemented)");
275*81b2d1f0SGlenn Strauss 			} else {
276*81b2d1f0SGlenn Strauss 				log_error_write(srv, __FILE__, __LINE__, "ss", "unknown key", k);
277*81b2d1f0SGlenn Strauss 				return -1;
278*81b2d1f0SGlenn Strauss 			}
279*81b2d1f0SGlenn Strauss 		} else {
280*81b2d1f0SGlenn Strauss 			log_error_write(srv, __FILE__, __LINE__, "s", "unknown  key");
281*81b2d1f0SGlenn Strauss 			return -1;
282*81b2d1f0SGlenn Strauss 		}
283*81b2d1f0SGlenn Strauss 
284*81b2d1f0SGlenn Strauss 		if (!e) break;
285*81b2d1f0SGlenn Strauss 		r = e + 1;
286*81b2d1f0SGlenn Strauss 	}
287*81b2d1f0SGlenn Strauss 
288*81b2d1f0SGlenn Strauss 	log_error_write(srv, __FILE__, __LINE__, "s", "nothing matched");
289*81b2d1f0SGlenn Strauss 
290*81b2d1f0SGlenn Strauss 	return -1;
291*81b2d1f0SGlenn Strauss }
292*81b2d1f0SGlenn Strauss 
293*81b2d1f0SGlenn Strauss static int mod_auth_basic_check(server *srv, connection *con, mod_auth_plugin_data *p, array *req, const char *realm_str) {
294*81b2d1f0SGlenn Strauss 	buffer *username;
295*81b2d1f0SGlenn Strauss 	char *pw;
296*81b2d1f0SGlenn Strauss 
297*81b2d1f0SGlenn Strauss 	data_string *realm;
298*81b2d1f0SGlenn Strauss 
299*81b2d1f0SGlenn Strauss 	realm = (data_string *)array_get_element(req, "realm");
300*81b2d1f0SGlenn Strauss 	if (!realm) return 0; /*(should not happen; config is validated at startup)*/
301*81b2d1f0SGlenn Strauss 
302*81b2d1f0SGlenn Strauss 	username = buffer_init();
303*81b2d1f0SGlenn Strauss 
304*81b2d1f0SGlenn Strauss 	if (!buffer_append_base64_decode(username, realm_str, strlen(realm_str), BASE64_STANDARD)) {
305*81b2d1f0SGlenn Strauss 		log_error_write(srv, __FILE__, __LINE__, "sb", "decodeing base64-string failed", username);
306*81b2d1f0SGlenn Strauss 
307*81b2d1f0SGlenn Strauss 		buffer_free(username);
308*81b2d1f0SGlenn Strauss 		return 0;
309*81b2d1f0SGlenn Strauss 	}
310*81b2d1f0SGlenn Strauss 
311*81b2d1f0SGlenn Strauss 	/* r2 == user:password */
312*81b2d1f0SGlenn Strauss 	if (NULL == (pw = strchr(username->ptr, ':'))) {
313*81b2d1f0SGlenn Strauss 		log_error_write(srv, __FILE__, __LINE__, "sb", ": is missing in", username);
314*81b2d1f0SGlenn Strauss 
315*81b2d1f0SGlenn Strauss 		buffer_free(username);
316*81b2d1f0SGlenn Strauss 		return 0;
317*81b2d1f0SGlenn Strauss 	}
318*81b2d1f0SGlenn Strauss 
319*81b2d1f0SGlenn Strauss 	buffer_string_set_length(username, pw - username->ptr);
320*81b2d1f0SGlenn Strauss 	pw++;
321*81b2d1f0SGlenn Strauss 
322*81b2d1f0SGlenn Strauss 	/* password doesn't match */
323*81b2d1f0SGlenn Strauss 	if (http_auth_basic_password_compare(srv, p, username, realm->value, pw)) {
324*81b2d1f0SGlenn 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)));
325*81b2d1f0SGlenn Strauss 
326*81b2d1f0SGlenn Strauss 		buffer_free(username);
327*81b2d1f0SGlenn Strauss 
328*81b2d1f0SGlenn Strauss 		return 0;
329*81b2d1f0SGlenn Strauss 	}
330*81b2d1f0SGlenn Strauss 
331*81b2d1f0SGlenn Strauss 	/* value is our allow-rules */
332*81b2d1f0SGlenn Strauss 	if (mod_auth_match_rules(srv, req, username->ptr, NULL, NULL)) {
333*81b2d1f0SGlenn Strauss 		buffer_free(username);
334*81b2d1f0SGlenn Strauss 
335*81b2d1f0SGlenn Strauss 		log_error_write(srv, __FILE__, __LINE__, "s", "rules didn't match");
336*81b2d1f0SGlenn Strauss 
337*81b2d1f0SGlenn Strauss 		return 0;
338*81b2d1f0SGlenn Strauss 	}
339*81b2d1f0SGlenn Strauss 
340*81b2d1f0SGlenn Strauss 	/* remember the username */
341*81b2d1f0SGlenn Strauss 	buffer_copy_buffer(p->auth_user, username);
342*81b2d1f0SGlenn Strauss 
343*81b2d1f0SGlenn Strauss 	buffer_free(username);
344*81b2d1f0SGlenn Strauss 
345*81b2d1f0SGlenn Strauss 	return 1;
346*81b2d1f0SGlenn Strauss }
347*81b2d1f0SGlenn Strauss 
348*81b2d1f0SGlenn Strauss #define HASHLEN 16
349*81b2d1f0SGlenn Strauss #define HASHHEXLEN 32
350*81b2d1f0SGlenn Strauss typedef unsigned char HASH[HASHLEN];
351*81b2d1f0SGlenn Strauss typedef char HASHHEX[HASHHEXLEN+1];
352*81b2d1f0SGlenn Strauss 
353*81b2d1f0SGlenn Strauss static void CvtHex(const HASH Bin, char (*Hex)[33]) {
354*81b2d1f0SGlenn Strauss 	li_tohex(*Hex, sizeof(*Hex), (const char*) Bin, 16);
355*81b2d1f0SGlenn Strauss }
356*81b2d1f0SGlenn Strauss 
357*81b2d1f0SGlenn Strauss typedef struct {
358*81b2d1f0SGlenn Strauss 	const char *key;
359*81b2d1f0SGlenn Strauss 	int key_len;
360*81b2d1f0SGlenn Strauss 	char **ptr;
361*81b2d1f0SGlenn Strauss } digest_kv;
362*81b2d1f0SGlenn Strauss 
363*81b2d1f0SGlenn Strauss /* return values: -1: error/bad request, 0: failed, 1: success */
364*81b2d1f0SGlenn Strauss static int mod_auth_digest_check(server *srv, connection *con, mod_auth_plugin_data *p, array *req, const char *realm_str) {
365*81b2d1f0SGlenn Strauss 	char a1[33];
366*81b2d1f0SGlenn Strauss 	char a2[33];
367*81b2d1f0SGlenn Strauss 
368*81b2d1f0SGlenn Strauss 	char *username = NULL;
369*81b2d1f0SGlenn Strauss 	char *realm = NULL;
370*81b2d1f0SGlenn Strauss 	char *nonce = NULL;
371*81b2d1f0SGlenn Strauss 	char *uri = NULL;
372*81b2d1f0SGlenn Strauss 	char *algorithm = NULL;
373*81b2d1f0SGlenn Strauss 	char *qop = NULL;
374*81b2d1f0SGlenn Strauss 	char *cnonce = NULL;
375*81b2d1f0SGlenn Strauss 	char *nc = NULL;
376*81b2d1f0SGlenn Strauss 	char *respons = NULL;
377*81b2d1f0SGlenn Strauss 
378*81b2d1f0SGlenn Strauss 	char *e, *c;
379*81b2d1f0SGlenn Strauss 	const char *m = NULL;
380*81b2d1f0SGlenn Strauss 	int i;
381*81b2d1f0SGlenn Strauss 	buffer *b;
382*81b2d1f0SGlenn Strauss 
383*81b2d1f0SGlenn Strauss 	li_MD5_CTX Md5Ctx;
384*81b2d1f0SGlenn Strauss 	HASH HA1;
385*81b2d1f0SGlenn Strauss 	HASH HA2;
386*81b2d1f0SGlenn Strauss 	HASH RespHash;
387*81b2d1f0SGlenn Strauss 	HASHHEX HA2Hex;
388*81b2d1f0SGlenn Strauss 
389*81b2d1f0SGlenn Strauss 
390*81b2d1f0SGlenn Strauss 	/* init pointers */
391*81b2d1f0SGlenn Strauss #define S(x) \
392*81b2d1f0SGlenn Strauss 	x, sizeof(x)-1, NULL
393*81b2d1f0SGlenn Strauss 	digest_kv dkv[10] = {
394*81b2d1f0SGlenn Strauss 		{ S("username=") },
395*81b2d1f0SGlenn Strauss 		{ S("realm=") },
396*81b2d1f0SGlenn Strauss 		{ S("nonce=") },
397*81b2d1f0SGlenn Strauss 		{ S("uri=") },
398*81b2d1f0SGlenn Strauss 		{ S("algorithm=") },
399*81b2d1f0SGlenn Strauss 		{ S("qop=") },
400*81b2d1f0SGlenn Strauss 		{ S("cnonce=") },
401*81b2d1f0SGlenn Strauss 		{ S("nc=") },
402*81b2d1f0SGlenn Strauss 		{ S("response=") },
403*81b2d1f0SGlenn Strauss 
404*81b2d1f0SGlenn Strauss 		{ NULL, 0, NULL }
405*81b2d1f0SGlenn Strauss 	};
406*81b2d1f0SGlenn Strauss #undef S
407*81b2d1f0SGlenn Strauss 
408*81b2d1f0SGlenn Strauss 	dkv[0].ptr = &username;
409*81b2d1f0SGlenn Strauss 	dkv[1].ptr = &realm;
410*81b2d1f0SGlenn Strauss 	dkv[2].ptr = &nonce;
411*81b2d1f0SGlenn Strauss 	dkv[3].ptr = &uri;
412*81b2d1f0SGlenn Strauss 	dkv[4].ptr = &algorithm;
413*81b2d1f0SGlenn Strauss 	dkv[5].ptr = &qop;
414*81b2d1f0SGlenn Strauss 	dkv[6].ptr = &cnonce;
415*81b2d1f0SGlenn Strauss 	dkv[7].ptr = &nc;
416*81b2d1f0SGlenn Strauss 	dkv[8].ptr = &respons;
417*81b2d1f0SGlenn Strauss 
418*81b2d1f0SGlenn Strauss 	b = buffer_init_string(realm_str);
419*81b2d1f0SGlenn Strauss 
420*81b2d1f0SGlenn Strauss 	/* parse credentials from client */
421*81b2d1f0SGlenn Strauss 	for (c = b->ptr; *c; c++) {
422*81b2d1f0SGlenn Strauss 		/* skip whitespaces */
423*81b2d1f0SGlenn Strauss 		while (*c == ' ' || *c == '\t') c++;
424*81b2d1f0SGlenn Strauss 		if (!*c) break;
425*81b2d1f0SGlenn Strauss 
426*81b2d1f0SGlenn Strauss 		for (i = 0; dkv[i].key; i++) {
427*81b2d1f0SGlenn Strauss 			if ((0 == strncmp(c, dkv[i].key, dkv[i].key_len))) {
428*81b2d1f0SGlenn Strauss 				if ((c[dkv[i].key_len] == '"') &&
429*81b2d1f0SGlenn Strauss 				    (NULL != (e = strchr(c + dkv[i].key_len + 1, '"')))) {
430*81b2d1f0SGlenn Strauss 					/* value with "..." */
431*81b2d1f0SGlenn Strauss 					*(dkv[i].ptr) = c + dkv[i].key_len + 1;
432*81b2d1f0SGlenn Strauss 					c = e;
433*81b2d1f0SGlenn Strauss 
434*81b2d1f0SGlenn Strauss 					*e = '\0';
435*81b2d1f0SGlenn Strauss 				} else if (NULL != (e = strchr(c + dkv[i].key_len, ','))) {
436*81b2d1f0SGlenn Strauss 					/* value without "...", terminated by ',' */
437*81b2d1f0SGlenn Strauss 					*(dkv[i].ptr) = c + dkv[i].key_len;
438*81b2d1f0SGlenn Strauss 					c = e;
439*81b2d1f0SGlenn Strauss 
440*81b2d1f0SGlenn Strauss 					*e = '\0';
441*81b2d1f0SGlenn Strauss 				} else {
442*81b2d1f0SGlenn Strauss 					/* value without "...", terminated by EOL */
443*81b2d1f0SGlenn Strauss 					*(dkv[i].ptr) = c + dkv[i].key_len;
444*81b2d1f0SGlenn Strauss 					c += strlen(c) - 1;
445*81b2d1f0SGlenn Strauss 				}
446*81b2d1f0SGlenn Strauss 				break;
447*81b2d1f0SGlenn Strauss 			}
448*81b2d1f0SGlenn Strauss 		}
449*81b2d1f0SGlenn Strauss 	}
450*81b2d1f0SGlenn Strauss 
451*81b2d1f0SGlenn Strauss 	if (p->conf.auth_debug > 1) {
452*81b2d1f0SGlenn Strauss 		log_error_write(srv, __FILE__, __LINE__, "ss", "username", username);
453*81b2d1f0SGlenn Strauss 		log_error_write(srv, __FILE__, __LINE__, "ss", "realm", realm);
454*81b2d1f0SGlenn Strauss 		log_error_write(srv, __FILE__, __LINE__, "ss", "nonce", nonce);
455*81b2d1f0SGlenn Strauss 		log_error_write(srv, __FILE__, __LINE__, "ss", "uri", uri);
456*81b2d1f0SGlenn Strauss 		log_error_write(srv, __FILE__, __LINE__, "ss", "algorithm", algorithm);
457*81b2d1f0SGlenn Strauss 		log_error_write(srv, __FILE__, __LINE__, "ss", "qop", qop);
458*81b2d1f0SGlenn Strauss 		log_error_write(srv, __FILE__, __LINE__, "ss", "cnonce", cnonce);
459*81b2d1f0SGlenn Strauss 		log_error_write(srv, __FILE__, __LINE__, "ss", "nc", nc);
460*81b2d1f0SGlenn Strauss 		log_error_write(srv, __FILE__, __LINE__, "ss", "response", respons);
461*81b2d1f0SGlenn Strauss 	}
462*81b2d1f0SGlenn Strauss 
463*81b2d1f0SGlenn Strauss 	/* check if everything is transmitted */
464*81b2d1f0SGlenn Strauss 	if (!username ||
465*81b2d1f0SGlenn Strauss 	    !realm ||
466*81b2d1f0SGlenn Strauss 	    !nonce ||
467*81b2d1f0SGlenn Strauss 	    !uri ||
468*81b2d1f0SGlenn Strauss 	    (qop && (!nc || !cnonce)) ||
469*81b2d1f0SGlenn Strauss 	    !respons ) {
470*81b2d1f0SGlenn Strauss 		/* missing field */
471*81b2d1f0SGlenn Strauss 
472*81b2d1f0SGlenn Strauss 		log_error_write(srv, __FILE__, __LINE__, "s",
473*81b2d1f0SGlenn Strauss 				"digest: missing field");
474*81b2d1f0SGlenn Strauss 
475*81b2d1f0SGlenn Strauss 		buffer_free(b);
476*81b2d1f0SGlenn Strauss 		return -1;
477*81b2d1f0SGlenn Strauss 	}
478*81b2d1f0SGlenn Strauss 
479*81b2d1f0SGlenn Strauss 	/**
480*81b2d1f0SGlenn Strauss 	 * protect the md5-sess against missing cnonce and nonce
481*81b2d1f0SGlenn Strauss 	 */
482*81b2d1f0SGlenn Strauss 	if (algorithm &&
483*81b2d1f0SGlenn Strauss 	    0 == strcasecmp(algorithm, "md5-sess") &&
484*81b2d1f0SGlenn Strauss 	    (!nonce || !cnonce)) {
485*81b2d1f0SGlenn Strauss 		log_error_write(srv, __FILE__, __LINE__, "s",
486*81b2d1f0SGlenn Strauss 				"digest: (md5-sess: missing field");
487*81b2d1f0SGlenn Strauss 
488*81b2d1f0SGlenn Strauss 		buffer_free(b);
489*81b2d1f0SGlenn Strauss 		return -1;
490*81b2d1f0SGlenn Strauss 	}
491*81b2d1f0SGlenn Strauss 
492*81b2d1f0SGlenn Strauss 	if (qop && strcasecmp(qop, "auth-int") == 0) {
493*81b2d1f0SGlenn Strauss 		log_error_write(srv, __FILE__, __LINE__, "s",
494*81b2d1f0SGlenn Strauss 				"digest: qop=auth-int not supported");
495*81b2d1f0SGlenn Strauss 
496*81b2d1f0SGlenn Strauss 		buffer_free(b);
497*81b2d1f0SGlenn Strauss 		return -1;
498*81b2d1f0SGlenn Strauss 	}
499*81b2d1f0SGlenn Strauss 
500*81b2d1f0SGlenn Strauss 	m = get_http_method_name(con->request.http_method);
501*81b2d1f0SGlenn Strauss 	force_assert(m);
502*81b2d1f0SGlenn Strauss 
503*81b2d1f0SGlenn Strauss 	/* detect if attacker is attempting to reuse valid digest for one uri
504*81b2d1f0SGlenn Strauss 	 * on a different request uri.  Might also happen if intermediate proxy
505*81b2d1f0SGlenn Strauss 	 * altered client request line.  (Altered request would not result in
506*81b2d1f0SGlenn Strauss 	 * the same digest as that calculated by the client.)
507*81b2d1f0SGlenn Strauss 	 * Internal redirects such as with mod_rewrite will modify request uri.
508*81b2d1f0SGlenn Strauss 	 * Reauthentication is done to detect crossing auth realms, but this
509*81b2d1f0SGlenn Strauss 	 * uri validation step is bypassed.  con->request.orig_uri is original
510*81b2d1f0SGlenn Strauss 	 * uri sent in client request. */
511*81b2d1f0SGlenn Strauss 	{
512*81b2d1f0SGlenn Strauss 		const size_t ulen = strlen(uri);
513*81b2d1f0SGlenn Strauss 		const size_t rlen = buffer_string_length(con->request.orig_uri);
514*81b2d1f0SGlenn Strauss 		if (!buffer_is_equal_string(con->request.orig_uri, uri, ulen)
515*81b2d1f0SGlenn Strauss 		    && !(rlen < ulen && 0 == memcmp(con->request.orig_uri->ptr, uri, rlen) && uri[rlen] == '?')) {
516*81b2d1f0SGlenn Strauss 			log_error_write(srv, __FILE__, __LINE__, "sbssss",
517*81b2d1f0SGlenn Strauss 					"digest: auth failed: uri mismatch (", con->request.orig_uri, "!=", uri, "), IP:", inet_ntop_cache_get_ip(srv, &(con->dst_addr)));
518*81b2d1f0SGlenn Strauss 			buffer_free(b);
519*81b2d1f0SGlenn Strauss 			return -1;
520*81b2d1f0SGlenn Strauss 		}
521*81b2d1f0SGlenn Strauss 	}
522*81b2d1f0SGlenn Strauss 
523*81b2d1f0SGlenn Strauss 	/* password-string == HA1 */
524*81b2d1f0SGlenn Strauss 	i = http_auth_get_password_digest(srv, p, username, realm, HA1);
525*81b2d1f0SGlenn Strauss 	if (1 != i) {
526*81b2d1f0SGlenn Strauss 		buffer_free(b);
527*81b2d1f0SGlenn Strauss 		return i;
528*81b2d1f0SGlenn Strauss 	}
529*81b2d1f0SGlenn Strauss 
530*81b2d1f0SGlenn Strauss 	if (algorithm &&
531*81b2d1f0SGlenn Strauss 	    strcasecmp(algorithm, "md5-sess") == 0) {
532*81b2d1f0SGlenn Strauss 		li_MD5_Init(&Md5Ctx);
533*81b2d1f0SGlenn Strauss 		/* Errata ID 1649: http://www.rfc-editor.org/errata_search.php?rfc=2617 */
534*81b2d1f0SGlenn Strauss 		CvtHex(HA1, &a1);
535*81b2d1f0SGlenn Strauss 		li_MD5_Update(&Md5Ctx, (unsigned char *)a1, HASHHEXLEN);
536*81b2d1f0SGlenn Strauss 		li_MD5_Update(&Md5Ctx, CONST_STR_LEN(":"));
537*81b2d1f0SGlenn Strauss 		li_MD5_Update(&Md5Ctx, (unsigned char *)nonce, strlen(nonce));
538*81b2d1f0SGlenn Strauss 		li_MD5_Update(&Md5Ctx, CONST_STR_LEN(":"));
539*81b2d1f0SGlenn Strauss 		li_MD5_Update(&Md5Ctx, (unsigned char *)cnonce, strlen(cnonce));
540*81b2d1f0SGlenn Strauss 		li_MD5_Final(HA1, &Md5Ctx);
541*81b2d1f0SGlenn Strauss 	}
542*81b2d1f0SGlenn Strauss 
543*81b2d1f0SGlenn Strauss 	CvtHex(HA1, &a1);
544*81b2d1f0SGlenn Strauss 
545*81b2d1f0SGlenn Strauss 	/* calculate H(A2) */
546*81b2d1f0SGlenn Strauss 	li_MD5_Init(&Md5Ctx);
547*81b2d1f0SGlenn Strauss 	li_MD5_Update(&Md5Ctx, (unsigned char *)m, strlen(m));
548*81b2d1f0SGlenn Strauss 	li_MD5_Update(&Md5Ctx, CONST_STR_LEN(":"));
549*81b2d1f0SGlenn Strauss 	li_MD5_Update(&Md5Ctx, (unsigned char *)uri, strlen(uri));
550*81b2d1f0SGlenn Strauss 	/* qop=auth-int not supported, already checked above */
551*81b2d1f0SGlenn Strauss /*
552*81b2d1f0SGlenn Strauss 	if (qop && strcasecmp(qop, "auth-int") == 0) {
553*81b2d1f0SGlenn Strauss 		li_MD5_Update(&Md5Ctx, CONST_STR_LEN(":"));
554*81b2d1f0SGlenn Strauss 		li_MD5_Update(&Md5Ctx, (unsigned char *) [body checksum], HASHHEXLEN);
555*81b2d1f0SGlenn Strauss 	}
556*81b2d1f0SGlenn Strauss */
557*81b2d1f0SGlenn Strauss 	li_MD5_Final(HA2, &Md5Ctx);
558*81b2d1f0SGlenn Strauss 	CvtHex(HA2, &HA2Hex);
559*81b2d1f0SGlenn Strauss 
560*81b2d1f0SGlenn Strauss 	/* calculate response */
561*81b2d1f0SGlenn Strauss 	li_MD5_Init(&Md5Ctx);
562*81b2d1f0SGlenn Strauss 	li_MD5_Update(&Md5Ctx, (unsigned char *)a1, HASHHEXLEN);
563*81b2d1f0SGlenn Strauss 	li_MD5_Update(&Md5Ctx, CONST_STR_LEN(":"));
564*81b2d1f0SGlenn Strauss 	li_MD5_Update(&Md5Ctx, (unsigned char *)nonce, strlen(nonce));
565*81b2d1f0SGlenn Strauss 	li_MD5_Update(&Md5Ctx, CONST_STR_LEN(":"));
566*81b2d1f0SGlenn Strauss 	if (qop && *qop) {
567*81b2d1f0SGlenn Strauss 		li_MD5_Update(&Md5Ctx, (unsigned char *)nc, strlen(nc));
568*81b2d1f0SGlenn Strauss 		li_MD5_Update(&Md5Ctx, CONST_STR_LEN(":"));
569*81b2d1f0SGlenn Strauss 		li_MD5_Update(&Md5Ctx, (unsigned char *)cnonce, strlen(cnonce));
570*81b2d1f0SGlenn Strauss 		li_MD5_Update(&Md5Ctx, CONST_STR_LEN(":"));
571*81b2d1f0SGlenn Strauss 		li_MD5_Update(&Md5Ctx, (unsigned char *)qop, strlen(qop));
572*81b2d1f0SGlenn Strauss 		li_MD5_Update(&Md5Ctx, CONST_STR_LEN(":"));
573*81b2d1f0SGlenn Strauss 	};
574*81b2d1f0SGlenn Strauss 	li_MD5_Update(&Md5Ctx, (unsigned char *)HA2Hex, HASHHEXLEN);
575*81b2d1f0SGlenn Strauss 	li_MD5_Final(RespHash, &Md5Ctx);
576*81b2d1f0SGlenn Strauss 	CvtHex(RespHash, &a2);
577*81b2d1f0SGlenn Strauss 
578*81b2d1f0SGlenn Strauss 	if (0 != strcmp(a2, respons)) {
579*81b2d1f0SGlenn Strauss 		/* digest not ok */
580*81b2d1f0SGlenn Strauss 
581*81b2d1f0SGlenn Strauss 		if (p->conf.auth_debug) {
582*81b2d1f0SGlenn Strauss 			log_error_write(srv, __FILE__, __LINE__, "sss",
583*81b2d1f0SGlenn Strauss 				"digest: digest mismatch", a2, respons);
584*81b2d1f0SGlenn Strauss 		}
585*81b2d1f0SGlenn Strauss 
586*81b2d1f0SGlenn Strauss 		log_error_write(srv, __FILE__, __LINE__, "ssss",
587*81b2d1f0SGlenn Strauss 				"digest: auth failed for ", username, ": wrong password, IP:", inet_ntop_cache_get_ip(srv, &(con->dst_addr)));
588*81b2d1f0SGlenn Strauss 
589*81b2d1f0SGlenn Strauss 		buffer_free(b);
590*81b2d1f0SGlenn Strauss 		return 0;
591*81b2d1f0SGlenn Strauss 	}
592*81b2d1f0SGlenn Strauss 
593*81b2d1f0SGlenn Strauss 	/* value is our allow-rules */
594*81b2d1f0SGlenn Strauss 	if (mod_auth_match_rules(srv, req, username, NULL, NULL)) {
595*81b2d1f0SGlenn Strauss 		buffer_free(b);
596*81b2d1f0SGlenn Strauss 
597*81b2d1f0SGlenn Strauss 		if (p->conf.auth_debug) {
598*81b2d1f0SGlenn Strauss 			log_error_write(srv, __FILE__, __LINE__, "s",
599*81b2d1f0SGlenn Strauss 					"digest: rules did match");
600*81b2d1f0SGlenn Strauss 		}
601*81b2d1f0SGlenn Strauss 
602*81b2d1f0SGlenn Strauss 		return 0;
603*81b2d1f0SGlenn Strauss 	}
604*81b2d1f0SGlenn Strauss 
605*81b2d1f0SGlenn Strauss 	/* check age of nonce.  Note that rand() is used in nonce generation
606*81b2d1f0SGlenn Strauss 	 * in mod_auth_digest_generate_nonce().  If that were replaced
607*81b2d1f0SGlenn Strauss 	 * with nanosecond time, then nonce secret would remain unique enough
608*81b2d1f0SGlenn Strauss 	 * for the purposes of Digest auth, and would be reproducible (and
609*81b2d1f0SGlenn Strauss 	 * verifiable) if nanoseconds were inclued with seconds as part of the
610*81b2d1f0SGlenn Strauss 	 * nonce "timestamp:secret".  Since that is not done, timestamp in
611*81b2d1f0SGlenn Strauss 	 * nonce could theoretically be modified and still produce same md5sum,
612*81b2d1f0SGlenn Strauss 	 * but that is highly unlikely within a 10 min (moving) window of valid
613*81b2d1f0SGlenn Strauss 	 * time relative to current time (now) */
614*81b2d1f0SGlenn Strauss 	{
615*81b2d1f0SGlenn Strauss 		time_t ts = 0;
616*81b2d1f0SGlenn Strauss 		const unsigned char * const nonce_uns = (unsigned char *)nonce;
617*81b2d1f0SGlenn Strauss 		for (i = 0; i < 8 && light_isxdigit(nonce_uns[i]); ++i) {
618*81b2d1f0SGlenn Strauss 			ts = (ts << 4) + hex2int(nonce_uns[i]);
619*81b2d1f0SGlenn Strauss 		}
620*81b2d1f0SGlenn Strauss 		if (i != 8 || nonce[8] != ':'
621*81b2d1f0SGlenn Strauss 		    || ts > srv->cur_ts || srv->cur_ts - ts > 600) { /*(10 mins)*/
622*81b2d1f0SGlenn Strauss 			buffer_free(b);
623*81b2d1f0SGlenn Strauss 			return -2; /* nonce is stale; have client regenerate digest */
624*81b2d1f0SGlenn Strauss 		} /*(future: might send nextnonce when expiration is imminent)*/
625*81b2d1f0SGlenn Strauss 	}
626*81b2d1f0SGlenn Strauss 
627*81b2d1f0SGlenn Strauss 	/* remember the username */
628*81b2d1f0SGlenn Strauss 	buffer_copy_string(p->auth_user, username);
629*81b2d1f0SGlenn Strauss 
630*81b2d1f0SGlenn Strauss 	buffer_free(b);
631*81b2d1f0SGlenn Strauss 
632*81b2d1f0SGlenn Strauss 	if (p->conf.auth_debug) {
633*81b2d1f0SGlenn Strauss 		log_error_write(srv, __FILE__, __LINE__, "s",
634*81b2d1f0SGlenn Strauss 				"digest: auth ok");
635*81b2d1f0SGlenn Strauss 	}
636*81b2d1f0SGlenn Strauss 	return 1;
637*81b2d1f0SGlenn Strauss }
638*81b2d1f0SGlenn Strauss 
639*81b2d1f0SGlenn Strauss static int mod_auth_digest_generate_nonce(server *srv, mod_auth_plugin_data *p, buffer *fn, char (*out)[33]) {
640*81b2d1f0SGlenn Strauss 	HASH h;
641*81b2d1f0SGlenn Strauss 	li_MD5_CTX Md5Ctx;
642*81b2d1f0SGlenn Strauss 	char hh[LI_ITOSTRING_LENGTH];
643*81b2d1f0SGlenn Strauss 
644*81b2d1f0SGlenn Strauss 	UNUSED(p);
645*81b2d1f0SGlenn Strauss 
646*81b2d1f0SGlenn Strauss 	/* generate shared-secret */
647*81b2d1f0SGlenn Strauss 	li_MD5_Init(&Md5Ctx);
648*81b2d1f0SGlenn Strauss 	li_MD5_Update(&Md5Ctx, CONST_BUF_LEN(fn));
649*81b2d1f0SGlenn Strauss 	li_MD5_Update(&Md5Ctx, CONST_STR_LEN("+"));
650*81b2d1f0SGlenn Strauss 
651*81b2d1f0SGlenn Strauss 	/* we assume sizeof(time_t) == 4 here, but if not it ain't a problem at all */
652*81b2d1f0SGlenn Strauss 	li_itostrn(hh, sizeof(hh), srv->cur_ts);
653*81b2d1f0SGlenn Strauss 	li_MD5_Update(&Md5Ctx, (unsigned char *)hh, strlen(hh));
654*81b2d1f0SGlenn Strauss 	li_MD5_Update(&Md5Ctx, (unsigned char *)srv->entropy, sizeof(srv->entropy));
655*81b2d1f0SGlenn Strauss 	li_itostrn(hh, sizeof(hh), rand());
656*81b2d1f0SGlenn Strauss 	li_MD5_Update(&Md5Ctx, (unsigned char *)hh, strlen(hh));
657*81b2d1f0SGlenn Strauss 
658*81b2d1f0SGlenn Strauss 	li_MD5_Final(h, &Md5Ctx);
659*81b2d1f0SGlenn Strauss 
660*81b2d1f0SGlenn Strauss 	CvtHex(h, out);
661*81b2d1f0SGlenn Strauss 
662*81b2d1f0SGlenn Strauss 	return 0;
663*81b2d1f0SGlenn Strauss }
664*81b2d1f0SGlenn Strauss 
665bcdc6a3bSJan Kneschke static handler_t mod_auth_uri_handler(server *srv, connection *con, void *p_d) {
666bcdc6a3bSJan Kneschke 	size_t k;
667bcdc6a3bSJan Kneschke 	int auth_required = 0, auth_satisfied = 0;
668bcdc6a3bSJan Kneschke 	char *http_authorization = NULL;
6697187271fSStefan Bühler 	const char *auth_type = NULL;
670bcdc6a3bSJan Kneschke 	data_string *ds;
671bcdc6a3bSJan Kneschke 	mod_auth_plugin_data *p = p_d;
672bcdc6a3bSJan Kneschke 	array *req;
673f9d58670SStefan Bühler 	data_string *req_method;
674bcdc6a3bSJan Kneschke 
675bcdc6a3bSJan Kneschke 	/* select the right config */
6764a6ce4aeSJan Kneschke 	mod_auth_patch_connection(srv, con, p);
677bcdc6a3bSJan Kneschke 
678bcdc6a3bSJan Kneschke 	if (p->conf.auth_require == NULL) return HANDLER_GO_ON;
679bcdc6a3bSJan Kneschke 
680bcdc6a3bSJan Kneschke 	/*
681bcdc6a3bSJan Kneschke 	 * AUTH
682bcdc6a3bSJan Kneschke 	 *
683bcdc6a3bSJan Kneschke 	 */
684bcdc6a3bSJan Kneschke 
685bcdc6a3bSJan Kneschke 	/* do we have to ask for auth ? */
686bcdc6a3bSJan Kneschke 
687bcdc6a3bSJan Kneschke 	auth_required = 0;
688bcdc6a3bSJan Kneschke 	auth_satisfied = 0;
689bcdc6a3bSJan Kneschke 
690bcdc6a3bSJan Kneschke 	/* search auth-directives for path */
691bcdc6a3bSJan Kneschke 	for (k = 0; k < p->conf.auth_require->used; k++) {
69267079527SJan Kneschke 		buffer *require = p->conf.auth_require->data[k]->key;
693bcdc6a3bSJan Kneschke 
694ad3e93eaSStefan Bühler 		if (buffer_is_empty(require)) continue;
695ad3e93eaSStefan Bühler 		if (buffer_string_length(con->uri.path) < buffer_string_length(require)) continue;
6964afd2ae2SJan Kneschke 
6974afd2ae2SJan Kneschke 		/* if we have a case-insensitive FS we have to lower-case the URI here too */
6984afd2ae2SJan Kneschke 
6994afd2ae2SJan Kneschke 		if (con->conf.force_lowercase_filenames) {
700ad3e93eaSStefan Bühler 			if (0 == strncasecmp(con->uri.path->ptr, require->ptr, buffer_string_length(require))) {
701bcdc6a3bSJan Kneschke 				auth_required = 1;
702bcdc6a3bSJan Kneschke 				break;
703bcdc6a3bSJan Kneschke 			}
7044afd2ae2SJan Kneschke 		} else {
705ad3e93eaSStefan Bühler 			if (0 == strncmp(con->uri.path->ptr, require->ptr, buffer_string_length(require))) {
7064afd2ae2SJan Kneschke 				auth_required = 1;
7074afd2ae2SJan Kneschke 				break;
7084afd2ae2SJan Kneschke 			}
7094afd2ae2SJan Kneschke 		}
710bcdc6a3bSJan Kneschke 	}
711bcdc6a3bSJan Kneschke 
712bcdc6a3bSJan Kneschke 	/* nothing to do for us */
713bcdc6a3bSJan Kneschke 	if (auth_required == 0) return HANDLER_GO_ON;
714bcdc6a3bSJan Kneschke 
715bcdc6a3bSJan Kneschke 	req = ((data_array *)(p->conf.auth_require->data[k]))->value;
716f9d58670SStefan Bühler 	req_method = (data_string *)array_get_element(req, "method");
717f9d58670SStefan Bühler 
718f9d58670SStefan Bühler 	if (0 == strcmp(req_method->value->ptr, "extern")) {
719f9d58670SStefan Bühler 		/* require REMOTE_USER to be already set */
720f9d58670SStefan Bühler 		if (NULL == (ds = (data_string *)array_get_element(con->environment, "REMOTE_USER"))) {
721f9d58670SStefan Bühler 			con->http_status = 401;
722f9d58670SStefan Bühler 			con->mode = DIRECT;
723f9d58670SStefan Bühler 			return HANDLER_FINISHED;
724*81b2d1f0SGlenn Strauss 		} else if (mod_auth_match_rules(srv, req, ds->value->ptr, NULL, NULL)) {
725f9d58670SStefan Bühler 			log_error_write(srv, __FILE__, __LINE__, "s", "rules didn't match");
726f9d58670SStefan Bühler 			con->http_status = 401;
727f9d58670SStefan Bühler 			con->mode = DIRECT;
728f9d58670SStefan Bühler 			return HANDLER_FINISHED;
729f9d58670SStefan Bühler 		} else {
730f9d58670SStefan Bühler 			return HANDLER_GO_ON;
731f9d58670SStefan Bühler 		}
732f9d58670SStefan Bühler 	}
733bcdc6a3bSJan Kneschke 
734bcdc6a3bSJan Kneschke 	/* try to get Authorization-header */
735bcdc6a3bSJan Kneschke 
736ad3e93eaSStefan Bühler 	if (NULL != (ds = (data_string *)array_get_element(con->request.headers, "Authorization")) && !buffer_is_empty(ds->value)) {
737bcdc6a3bSJan Kneschke 		char *auth_realm;
738bcdc6a3bSJan Kneschke 
739f9d58670SStefan Bühler 		http_authorization = ds->value->ptr;
740bcdc6a3bSJan Kneschke 
741bcdc6a3bSJan Kneschke 		/* parse auth-header */
742bcdc6a3bSJan Kneschke 		if (NULL != (auth_realm = strchr(http_authorization, ' '))) {
743bcdc6a3bSJan Kneschke 			int auth_type_len = auth_realm - http_authorization;
744bcdc6a3bSJan Kneschke 
745bcdc6a3bSJan Kneschke 			if ((auth_type_len == 5) &&
746708f499dSJan Kneschke 			    (0 == strncasecmp(http_authorization, "Basic", auth_type_len))) {
7477187271fSStefan Bühler 				auth_type = "Basic";
748bcdc6a3bSJan Kneschke 
749f9d58670SStefan Bühler 				if (0 == strcmp(req_method->value->ptr, "basic")) {
750*81b2d1f0SGlenn Strauss 					auth_satisfied = mod_auth_basic_check(srv, con, p, req, auth_realm+1);
751bcdc6a3bSJan Kneschke 				}
752bcdc6a3bSJan Kneschke 			} else if ((auth_type_len == 6) &&
753708f499dSJan Kneschke 				   (0 == strncasecmp(http_authorization, "Digest", auth_type_len))) {
7547187271fSStefan Bühler 				auth_type = "Digest";
755f9d58670SStefan Bühler 				if (0 == strcmp(req_method->value->ptr, "digest")) {
756*81b2d1f0SGlenn Strauss 					if (-1 == (auth_satisfied = mod_auth_digest_check(srv, con, p, req, auth_realm+1))) {
757bcdc6a3bSJan Kneschke 						con->http_status = 400;
758ad12e4c5SStefan Bühler 						con->mode = DIRECT;
759bcdc6a3bSJan Kneschke 
760bcdc6a3bSJan Kneschke 						/* a field was missing */
761bcdc6a3bSJan Kneschke 
762bcdc6a3bSJan Kneschke 						return HANDLER_FINISHED;
763bcdc6a3bSJan Kneschke 					}
764bcdc6a3bSJan Kneschke 				}
765bcdc6a3bSJan Kneschke 			} else {
766bcdc6a3bSJan Kneschke 				log_error_write(srv, __FILE__, __LINE__, "ss",
767780e8e6eSStefan Bühler 						"unknown authentication type:",
768bcdc6a3bSJan Kneschke 						http_authorization);
769bcdc6a3bSJan Kneschke 			}
770bcdc6a3bSJan Kneschke 		}
771bcdc6a3bSJan Kneschke 	}
772bcdc6a3bSJan Kneschke 
77300cc4d7cSGlenn Strauss 	if (1 != auth_satisfied) { /*(0 or -2)*/
774bcdc6a3bSJan Kneschke 		data_string *method, *realm;
775bcdc6a3bSJan Kneschke 		method = (data_string *)array_get_element(req, "method");
776bcdc6a3bSJan Kneschke 		realm = (data_string *)array_get_element(req, "realm");
777bcdc6a3bSJan Kneschke 
778bcdc6a3bSJan Kneschke 		con->http_status = 401;
779ad12e4c5SStefan Bühler 		con->mode = DIRECT;
780bcdc6a3bSJan Kneschke 
78172b133f5SGlenn Strauss 		if (!method) return HANDLER_FINISHED;/*(should not happen; config is validated at startup)*/
78272b133f5SGlenn Strauss 		if (!realm) return HANDLER_FINISHED; /*(should not happen; config is validated at startup)*/
78372b133f5SGlenn Strauss 
784bcdc6a3bSJan Kneschke 		if (0 == strcmp(method->value->ptr, "basic")) {
78552861d77SStefan Bühler 			buffer_copy_string_len(p->tmp_buf, CONST_STR_LEN("Basic realm=\""));
786bcdc6a3bSJan Kneschke 			buffer_append_string_buffer(p->tmp_buf, realm->value);
7874b412797SGlenn Strauss 			buffer_append_string_len(p->tmp_buf, CONST_STR_LEN("\", charset=\"UTF-8\""));
788bcdc6a3bSJan Kneschke 
789bcdc6a3bSJan Kneschke 			response_header_insert(srv, con, CONST_STR_LEN("WWW-Authenticate"), CONST_BUF_LEN(p->tmp_buf));
790bcdc6a3bSJan Kneschke 		} else if (0 == strcmp(method->value->ptr, "digest")) {
791bcdc6a3bSJan Kneschke 			char hh[33];
792*81b2d1f0SGlenn Strauss 			mod_auth_digest_generate_nonce(srv, p, srv->tmp_buf, &hh);
793bcdc6a3bSJan Kneschke 
79452861d77SStefan Bühler 			buffer_copy_string_len(p->tmp_buf, CONST_STR_LEN("Digest realm=\""));
795bcdc6a3bSJan Kneschke 			buffer_append_string_buffer(p->tmp_buf, realm->value);
7964b412797SGlenn Strauss 			buffer_append_string_len(p->tmp_buf, CONST_STR_LEN("\", charset=\"UTF-8\", nonce=\""));
79700cc4d7cSGlenn Strauss 			buffer_append_uint_hex(p->tmp_buf, (uintmax_t)srv->cur_ts);
79800cc4d7cSGlenn Strauss 			buffer_append_string_len(p->tmp_buf, CONST_STR_LEN(":"));
799bcdc6a3bSJan Kneschke 			buffer_append_string(p->tmp_buf, hh);
80052861d77SStefan Bühler 			buffer_append_string_len(p->tmp_buf, CONST_STR_LEN("\", qop=\"auth\""));
80100cc4d7cSGlenn Strauss 			if (-2 == auth_satisfied) {
80200cc4d7cSGlenn Strauss 				buffer_append_string_len(p->tmp_buf, CONST_STR_LEN(", stale=true"));
80300cc4d7cSGlenn Strauss 			}
804bcdc6a3bSJan Kneschke 
805bcdc6a3bSJan Kneschke 			response_header_insert(srv, con, CONST_STR_LEN("WWW-Authenticate"), CONST_BUF_LEN(p->tmp_buf));
806bcdc6a3bSJan Kneschke 		} else {
807bcdc6a3bSJan Kneschke 			/* evil */
808bcdc6a3bSJan Kneschke 		}
809bcdc6a3bSJan Kneschke 		return HANDLER_FINISHED;
810bcdc6a3bSJan Kneschke 	} else {
811bcdc6a3bSJan Kneschke 		/* the REMOTE_USER header */
812bcdc6a3bSJan Kneschke 
813559b198fSStefan Bühler 		if (NULL == (ds = (data_string *)array_get_element(con->environment, "REMOTE_USER"))) {
8147187271fSStefan Bühler 			if (NULL == (ds = (data_string *)array_get_unused_element(con->environment, TYPE_STRING))) {
8157187271fSStefan Bühler 				ds = data_string_init();
8167187271fSStefan Bühler 			}
817559b198fSStefan Bühler 			buffer_copy_string(ds->key, "REMOTE_USER");
8187187271fSStefan Bühler 			array_insert_unique(con->environment, (data_unset *)ds);
819bcdc6a3bSJan Kneschke 		}
8206afad87dSStefan Bühler 		buffer_copy_buffer(ds->value, p->auth_user);
821559b198fSStefan Bühler 
822559b198fSStefan Bühler 		/* AUTH_TYPE environment */
823559b198fSStefan Bühler 
824559b198fSStefan Bühler 		if (NULL == (ds = (data_string *)array_get_element(con->environment, "AUTH_TYPE"))) {
825559b198fSStefan Bühler 			if (NULL == (ds = (data_string *)array_get_unused_element(con->environment, TYPE_STRING))) {
826559b198fSStefan Bühler 				ds = data_string_init();
827559b198fSStefan Bühler 			}
828559b198fSStefan Bühler 			buffer_copy_string(ds->key, "AUTH_TYPE");
829559b198fSStefan Bühler 			array_insert_unique(con->environment, (data_unset *)ds);
830559b198fSStefan Bühler 		}
831559b198fSStefan Bühler 		buffer_copy_string(ds->value, auth_type);
832559b198fSStefan Bühler 	}
833bcdc6a3bSJan Kneschke 
834bcdc6a3bSJan Kneschke 	return HANDLER_GO_ON;
835bcdc6a3bSJan Kneschke }
836bcdc6a3bSJan Kneschke 
837bcdc6a3bSJan Kneschke SETDEFAULTS_FUNC(mod_auth_set_defaults) {
838bcdc6a3bSJan Kneschke 	mod_auth_plugin_data *p = p_d;
839bcdc6a3bSJan Kneschke 	size_t i;
840bcdc6a3bSJan Kneschke 
841bcdc6a3bSJan Kneschke 	config_values_t cv[] = {
842bcdc6a3bSJan Kneschke 		{ "auth.backend",                   NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 0 */
84375d22698SStefan Bühler 		{ "auth.backend.plain.groupfile",   NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 1 */
84475d22698SStefan Bühler 		{ "auth.backend.plain.userfile",    NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 2 */
84575d22698SStefan Bühler 		{ "auth.require",                   NULL, T_CONFIG_LOCAL, T_CONFIG_SCOPE_CONNECTION },  /* 3 */
84675d22698SStefan Bühler 		{ "auth.backend.ldap.hostname",     NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 4 */
84775d22698SStefan Bühler 		{ "auth.backend.ldap.base-dn",      NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 5 */
84875d22698SStefan Bühler 		{ "auth.backend.ldap.filter",       NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 6 */
84975d22698SStefan Bühler 		{ "auth.backend.ldap.ca-file",      NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 7 */
85075d22698SStefan Bühler 		{ "auth.backend.ldap.starttls",     NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 8 */
85175d22698SStefan Bühler  		{ "auth.backend.ldap.bind-dn",      NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 9 */
8529238cad6SJan Kneschke  		{ "auth.backend.ldap.bind-pw",      NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 10 */
85375d22698SStefan Bühler 		{ "auth.backend.ldap.allow-empty-pw",     NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 11 */
85475d22698SStefan Bühler 		{ "auth.backend.htdigest.userfile", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 12 */
85575d22698SStefan Bühler 		{ "auth.backend.htpasswd.userfile", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 13 */
85675d22698SStefan Bühler 		{ "auth.debug",                     NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION },  /* 14 */
857bcdc6a3bSJan Kneschke 		{ NULL,                             NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET }
858bcdc6a3bSJan Kneschke 	};
859bcdc6a3bSJan Kneschke 
8606f208cfdSStefan Bühler 	p->config_storage = calloc(1, srv->config_context->used * sizeof(mod_auth_plugin_config *));
861bcdc6a3bSJan Kneschke 
862bcdc6a3bSJan Kneschke 	for (i = 0; i < srv->config_context->used; i++) {
863c512345fSStefan Bühler 		data_config const* config = (data_config const*)srv->config_context->data[i];
864bcdc6a3bSJan Kneschke 		mod_auth_plugin_config *s;
865bcdc6a3bSJan Kneschke 		size_t n;
866bcdc6a3bSJan Kneschke 		data_array *da;
867bcdc6a3bSJan Kneschke 
868bac9212cSJan Kneschke 		s = calloc(1, sizeof(mod_auth_plugin_config));
869bcdc6a3bSJan Kneschke 		s->auth_plain_groupfile = buffer_init();
870bcdc6a3bSJan Kneschke 		s->auth_plain_userfile = buffer_init();
871bcdc6a3bSJan Kneschke 		s->auth_htdigest_userfile = buffer_init();
872bcdc6a3bSJan Kneschke 		s->auth_htpasswd_userfile = buffer_init();
873bcdc6a3bSJan Kneschke 		s->auth_backend_conf = buffer_init();
874bcdc6a3bSJan Kneschke 
875bcdc6a3bSJan Kneschke 		s->auth_ldap_hostname = buffer_init();
876bcdc6a3bSJan Kneschke 		s->auth_ldap_basedn = buffer_init();
8779238cad6SJan Kneschke 		s->auth_ldap_binddn = buffer_init();
8789238cad6SJan Kneschke 		s->auth_ldap_bindpw = buffer_init();
879bcdc6a3bSJan Kneschke 		s->auth_ldap_filter = buffer_init();
8804a6ce4aeSJan Kneschke 		s->auth_ldap_cafile = buffer_init();
8814a6ce4aeSJan Kneschke 		s->auth_ldap_starttls = 0;
882bcdc6a3bSJan Kneschke 		s->auth_debug = 0;
883bcdc6a3bSJan Kneschke 
884bcdc6a3bSJan Kneschke 		s->auth_require = array_init();
885bcdc6a3bSJan Kneschke 
886bcdc6a3bSJan Kneschke #ifdef USE_LDAP
887bcdc6a3bSJan Kneschke 		s->ldap_filter_pre = buffer_init();
888bcdc6a3bSJan Kneschke 		s->ldap_filter_post = buffer_init();
889bcdc6a3bSJan Kneschke 		s->ldap = NULL;
890bcdc6a3bSJan Kneschke #endif
891bcdc6a3bSJan Kneschke 
892bcdc6a3bSJan Kneschke 		cv[0].destination = s->auth_backend_conf;
893bcdc6a3bSJan Kneschke 		cv[1].destination = s->auth_plain_groupfile;
894bcdc6a3bSJan Kneschke 		cv[2].destination = s->auth_plain_userfile;
895bcdc6a3bSJan Kneschke 		cv[3].destination = s->auth_require;
896bcdc6a3bSJan Kneschke 		cv[4].destination = s->auth_ldap_hostname;
897bcdc6a3bSJan Kneschke 		cv[5].destination = s->auth_ldap_basedn;
898bcdc6a3bSJan Kneschke 		cv[6].destination = s->auth_ldap_filter;
8994a6ce4aeSJan Kneschke 		cv[7].destination = s->auth_ldap_cafile;
9004a6ce4aeSJan Kneschke 		cv[8].destination = &(s->auth_ldap_starttls);
9019238cad6SJan Kneschke 		cv[9].destination = s->auth_ldap_binddn;
9029238cad6SJan Kneschke 		cv[10].destination = s->auth_ldap_bindpw;
903d471bcc9SMarcus Rückert 		cv[11].destination = &(s->auth_ldap_allow_empty_pw);
904d471bcc9SMarcus Rückert 		cv[12].destination = s->auth_htdigest_userfile;
905d471bcc9SMarcus Rückert 		cv[13].destination = s->auth_htpasswd_userfile;
906d471bcc9SMarcus Rückert 		cv[14].destination = &(s->auth_debug);
907bcdc6a3bSJan Kneschke 
908bcdc6a3bSJan Kneschke 		p->config_storage[i] = s;
909bcdc6a3bSJan Kneschke 
910c512345fSStefan Bühler 		if (0 != config_insert_values_global(srv, config->value, cv, i == 0 ? T_CONFIG_SCOPE_SERVER : T_CONFIG_SCOPE_CONNECTION)) {
911bcdc6a3bSJan Kneschke 			return HANDLER_ERROR;
912bcdc6a3bSJan Kneschke 		}
913bcdc6a3bSJan Kneschke 
914ad3e93eaSStefan Bühler 		if (!buffer_string_is_empty(s->auth_backend_conf)) {
915bcdc6a3bSJan Kneschke 			if (0 == strcmp(s->auth_backend_conf->ptr, "htpasswd")) {
916bcdc6a3bSJan Kneschke 				s->auth_backend = AUTH_BACKEND_HTPASSWD;
917bcdc6a3bSJan Kneschke 			} else if (0 == strcmp(s->auth_backend_conf->ptr, "htdigest")) {
918bcdc6a3bSJan Kneschke 				s->auth_backend = AUTH_BACKEND_HTDIGEST;
919bcdc6a3bSJan Kneschke 			} else if (0 == strcmp(s->auth_backend_conf->ptr, "plain")) {
920bcdc6a3bSJan Kneschke 				s->auth_backend = AUTH_BACKEND_PLAIN;
921bcdc6a3bSJan Kneschke 			} else if (0 == strcmp(s->auth_backend_conf->ptr, "ldap")) {
922bcdc6a3bSJan Kneschke 				s->auth_backend = AUTH_BACKEND_LDAP;
923bcdc6a3bSJan Kneschke 			} else {
924bcdc6a3bSJan Kneschke 				log_error_write(srv, __FILE__, __LINE__, "sb", "auth.backend not supported:", s->auth_backend_conf);
925bcdc6a3bSJan Kneschke 
926bcdc6a3bSJan Kneschke 				return HANDLER_ERROR;
927bcdc6a3bSJan Kneschke 			}
928bcdc6a3bSJan Kneschke 		}
929bcdc6a3bSJan Kneschke 
930e7ff9483SStefan Bühler #ifdef USE_LDAP
931ad3e93eaSStefan Bühler 		if (!buffer_string_is_empty(s->auth_ldap_filter)) {
932e7ff9483SStefan Bühler 			char *dollar;
933e7ff9483SStefan Bühler 
934e7ff9483SStefan Bühler 			/* parse filter */
935e7ff9483SStefan Bühler 
936e7ff9483SStefan Bühler 			if (NULL == (dollar = strchr(s->auth_ldap_filter->ptr, '$'))) {
937e7ff9483SStefan Bühler 				log_error_write(srv, __FILE__, __LINE__, "s", "ldap: auth.backend.ldap.filter is missing a replace-operator '$'");
938e7ff9483SStefan Bühler 
939e7ff9483SStefan Bühler 				return HANDLER_ERROR;
940e7ff9483SStefan Bühler 			}
941e7ff9483SStefan Bühler 
942e7ff9483SStefan Bühler 			buffer_copy_string_len(s->ldap_filter_pre, s->auth_ldap_filter->ptr, dollar - s->auth_ldap_filter->ptr);
943e7ff9483SStefan Bühler 			buffer_copy_string(s->ldap_filter_post, dollar+1);
944e7ff9483SStefan Bühler 		}
945e7ff9483SStefan Bühler #endif
946e7ff9483SStefan Bühler 
947bcdc6a3bSJan Kneschke 		/* no auth.require for this section */
948c512345fSStefan Bühler 		if (NULL == (da = (data_array *)array_get_element(config->value, "auth.require"))) continue;
949bcdc6a3bSJan Kneschke 
950bcdc6a3bSJan Kneschke 		if (da->type != TYPE_ARRAY) continue;
951bcdc6a3bSJan Kneschke 
952bcdc6a3bSJan Kneschke 		for (n = 0; n < da->value->used; n++) {
953bcdc6a3bSJan Kneschke 			size_t m;
954bcdc6a3bSJan Kneschke 			data_array *da_file = (data_array *)da->value->data[n];
955bcdc6a3bSJan Kneschke 			const char *method, *realm, *require;
956bcdc6a3bSJan Kneschke 
957bcdc6a3bSJan Kneschke 			if (da->value->data[n]->type != TYPE_ARRAY) {
958cc7a8ae0SJan Kneschke 				log_error_write(srv, __FILE__, __LINE__, "ss",
959cc7a8ae0SJan Kneschke 						"auth.require should contain an array as in:",
960cc7a8ae0SJan Kneschke 						"auth.require = ( \"...\" => ( ..., ...) )");
961bcdc6a3bSJan Kneschke 
962bcdc6a3bSJan Kneschke 				return HANDLER_ERROR;
963bcdc6a3bSJan Kneschke 			}
964bcdc6a3bSJan Kneschke 
965bcdc6a3bSJan Kneschke 			method = realm = require = NULL;
966bcdc6a3bSJan Kneschke 
967bcdc6a3bSJan Kneschke 			for (m = 0; m < da_file->value->used; m++) {
968bcdc6a3bSJan Kneschke 				if (da_file->value->data[m]->type == TYPE_STRING) {
969bcdc6a3bSJan Kneschke 					if (0 == strcmp(da_file->value->data[m]->key->ptr, "method")) {
970bcdc6a3bSJan Kneschke 						method = ((data_string *)(da_file->value->data[m]))->value->ptr;
971bcdc6a3bSJan Kneschke 					} else if (0 == strcmp(da_file->value->data[m]->key->ptr, "realm")) {
972bcdc6a3bSJan Kneschke 						realm = ((data_string *)(da_file->value->data[m]))->value->ptr;
973bcdc6a3bSJan Kneschke 					} else if (0 == strcmp(da_file->value->data[m]->key->ptr, "require")) {
974bcdc6a3bSJan Kneschke 						require = ((data_string *)(da_file->value->data[m]))->value->ptr;
975bcdc6a3bSJan Kneschke 					} else {
976cc7a8ae0SJan Kneschke 						log_error_write(srv, __FILE__, __LINE__, "ssbs",
977cc7a8ae0SJan Kneschke 							"the field is unknown in:",
978cc7a8ae0SJan Kneschke 							"auth.require = ( \"...\" => ( ..., -> \"",
979cc7a8ae0SJan Kneschke 							da_file->value->data[m]->key,
980cc7a8ae0SJan Kneschke 							"\" <- => \"...\" ) )");
981cc7a8ae0SJan Kneschke 
982bcdc6a3bSJan Kneschke 						return HANDLER_ERROR;
983bcdc6a3bSJan Kneschke 					}
984bcdc6a3bSJan Kneschke 				} else {
985cc7a8ae0SJan Kneschke 					log_error_write(srv, __FILE__, __LINE__, "ssbs",
986cc7a8ae0SJan Kneschke 						"a string was expected for:",
987cc7a8ae0SJan Kneschke 						"auth.require = ( \"...\" => ( ..., -> \"",
988cc7a8ae0SJan Kneschke 						da_file->value->data[m]->key,
989cc7a8ae0SJan Kneschke 						"\" <- => \"...\" ) )");
990bcdc6a3bSJan Kneschke 
991bcdc6a3bSJan Kneschke 					return HANDLER_ERROR;
992bcdc6a3bSJan Kneschke 				}
993bcdc6a3bSJan Kneschke 			}
994bcdc6a3bSJan Kneschke 
995bcdc6a3bSJan Kneschke 			if (method == NULL) {
996cc7a8ae0SJan Kneschke 				log_error_write(srv, __FILE__, __LINE__, "ss",
9974df22f2aSStefan Bühler 						"the method field is missing in:",
998cc7a8ae0SJan Kneschke 						"auth.require = ( \"...\" => ( ..., \"method\" => \"...\" ) )");
999bcdc6a3bSJan Kneschke 				return HANDLER_ERROR;
10004a6ce4aeSJan Kneschke 			} else {
10014a6ce4aeSJan Kneschke 				if (0 != strcmp(method, "basic") &&
1002f9d58670SStefan Bühler 				    0 != strcmp(method, "digest") &&
1003f9d58670SStefan Bühler 				    0 != strcmp(method, "extern")) {
1004cc7a8ae0SJan Kneschke 					log_error_write(srv, __FILE__, __LINE__, "ss",
1005f9d58670SStefan Bühler 							"method has to be either \"basic\", \"digest\" or \"extern\" in",
1006cc7a8ae0SJan Kneschke 							"auth.require = ( \"...\" => ( ..., \"method\" => \"...\") )");
10074a6ce4aeSJan Kneschke 					return HANDLER_ERROR;
10084a6ce4aeSJan Kneschke 				}
1009bcdc6a3bSJan Kneschke 			}
1010bcdc6a3bSJan Kneschke 
1011bcdc6a3bSJan Kneschke 			if (realm == NULL) {
1012cc7a8ae0SJan Kneschke 				log_error_write(srv, __FILE__, __LINE__, "ss",
10134df22f2aSStefan Bühler 						"the realm field is missing in:",
1014cc7a8ae0SJan Kneschke 						"auth.require = ( \"...\" => ( ..., \"realm\" => \"...\" ) )");
1015bcdc6a3bSJan Kneschke 				return HANDLER_ERROR;
1016bcdc6a3bSJan Kneschke 			}
1017bcdc6a3bSJan Kneschke 
1018bcdc6a3bSJan Kneschke 			if (require == NULL) {
1019cc7a8ae0SJan Kneschke 				log_error_write(srv, __FILE__, __LINE__, "ss",
1020cc7a8ae0SJan Kneschke 						"the require field is missing in:",
1021cc7a8ae0SJan Kneschke 						"auth.require = ( \"...\" => ( ..., \"require\" => \"...\" ) )");
1022bcdc6a3bSJan Kneschke 				return HANDLER_ERROR;
1023bcdc6a3bSJan Kneschke 			}
1024bcdc6a3bSJan Kneschke 
1025bcdc6a3bSJan Kneschke 			if (method && realm && require) {
1026bcdc6a3bSJan Kneschke 				data_string *ds;
1027bcdc6a3bSJan Kneschke 				data_array *a;
1028bcdc6a3bSJan Kneschke 
1029bcdc6a3bSJan Kneschke 				a = data_array_init();
10306afad87dSStefan Bühler 				buffer_copy_buffer(a->key, da_file->key);
1031bcdc6a3bSJan Kneschke 
1032bcdc6a3bSJan Kneschke 				ds = data_string_init();
1033bcdc6a3bSJan Kneschke 
103452861d77SStefan Bühler 				buffer_copy_string_len(ds->key, CONST_STR_LEN("method"));
1035bcdc6a3bSJan Kneschke 				buffer_copy_string(ds->value, method);
1036bcdc6a3bSJan Kneschke 
1037bcdc6a3bSJan Kneschke 				array_insert_unique(a->value, (data_unset *)ds);
1038bcdc6a3bSJan Kneschke 
1039bcdc6a3bSJan Kneschke 				ds = data_string_init();
1040bcdc6a3bSJan Kneschke 
104152861d77SStefan Bühler 				buffer_copy_string_len(ds->key, CONST_STR_LEN("realm"));
1042bcdc6a3bSJan Kneschke 				buffer_copy_string(ds->value, realm);
1043bcdc6a3bSJan Kneschke 
1044bcdc6a3bSJan Kneschke 				array_insert_unique(a->value, (data_unset *)ds);
1045bcdc6a3bSJan Kneschke 
1046bcdc6a3bSJan Kneschke 				ds = data_string_init();
1047bcdc6a3bSJan Kneschke 
104852861d77SStefan Bühler 				buffer_copy_string_len(ds->key, CONST_STR_LEN("require"));
1049bcdc6a3bSJan Kneschke 				buffer_copy_string(ds->value, require);
1050bcdc6a3bSJan Kneschke 
1051bcdc6a3bSJan Kneschke 				array_insert_unique(a->value, (data_unset *)ds);
1052bcdc6a3bSJan Kneschke 
1053bcdc6a3bSJan Kneschke 				array_insert_unique(s->auth_require, (data_unset *)a);
1054bcdc6a3bSJan Kneschke 			}
1055bcdc6a3bSJan Kneschke 		}
1056bcdc6a3bSJan Kneschke 
1057ad3e93eaSStefan Bühler 		switch(s->auth_backend) {
1058ea7b9abaSJan Kneschke 		case AUTH_BACKEND_LDAP: {
1059ea7b9abaSJan Kneschke 			handler_t ret = auth_ldap_init(srv, s);
1060ea7b9abaSJan Kneschke 			if (ret == HANDLER_ERROR)
1061ea7b9abaSJan Kneschke 				return (ret);
1062ea7b9abaSJan Kneschke 			break;
1063ea7b9abaSJan Kneschke 		}
1064ea7b9abaSJan Kneschke 		default:
1065ea7b9abaSJan Kneschke 			break;
1066ea7b9abaSJan Kneschke 		}
1067ea7b9abaSJan Kneschke 	}
1068ea7b9abaSJan Kneschke 
1069ea7b9abaSJan Kneschke 	return HANDLER_GO_ON;
1070ea7b9abaSJan Kneschke }
1071ea7b9abaSJan Kneschke 
10726ca932dfSJan Kneschke handler_t auth_ldap_init(server *srv, mod_auth_plugin_config *s) {
1073bcdc6a3bSJan Kneschke #ifdef USE_LDAP
1074ea7b9abaSJan Kneschke 	int ret;
1075bcdc6a3bSJan Kneschke #if 0
1076bcdc6a3bSJan Kneschke 	if (s->auth_ldap_basedn->used == 0) {
1077bcdc6a3bSJan Kneschke 		log_error_write(srv, __FILE__, __LINE__, "s", "ldap: auth.backend.ldap.base-dn has to be set");
1078bcdc6a3bSJan Kneschke 
1079bcdc6a3bSJan Kneschke 		return HANDLER_ERROR;
1080bcdc6a3bSJan Kneschke 	}
1081bcdc6a3bSJan Kneschke #endif
1082bcdc6a3bSJan Kneschke 
1083ad3e93eaSStefan Bühler 	if (!buffer_string_is_empty(s->auth_ldap_hostname)) {
1084cfba07cbSStefan Bühler 		/* free old context */
1085cfba07cbSStefan Bühler 		if (NULL != s->ldap) ldap_unbind_s(s->ldap);
1086cfba07cbSStefan Bühler 
1087bcdc6a3bSJan Kneschke 		if (NULL == (s->ldap = ldap_init(s->auth_ldap_hostname->ptr, LDAP_PORT))) {
1088bcdc6a3bSJan Kneschke 			log_error_write(srv, __FILE__, __LINE__, "ss", "ldap ...", strerror(errno));
1089bcdc6a3bSJan Kneschke 
1090bcdc6a3bSJan Kneschke 			return HANDLER_ERROR;
1091bcdc6a3bSJan Kneschke 		}
1092bcdc6a3bSJan Kneschke 
1093bcdc6a3bSJan Kneschke 		ret = LDAP_VERSION3;
1094bcdc6a3bSJan Kneschke 		if (LDAP_OPT_SUCCESS != (ret = ldap_set_option(s->ldap, LDAP_OPT_PROTOCOL_VERSION, &ret))) {
1095bcdc6a3bSJan Kneschke 			log_error_write(srv, __FILE__, __LINE__, "ss", "ldap:", ldap_err2string(ret));
1096bcdc6a3bSJan Kneschke 
1097bcdc6a3bSJan Kneschke 			return HANDLER_ERROR;
1098bcdc6a3bSJan Kneschke 		}
1099bcdc6a3bSJan Kneschke 
11004a6ce4aeSJan Kneschke 		if (s->auth_ldap_starttls) {
11016ca932dfSJan Kneschke 			/* if no CA file is given, it is ok, as we will use encryption
11026ca932dfSJan Kneschke 				* if the server requires a CAfile it will tell us */
11036afad87dSStefan Bühler 			if (!buffer_string_is_empty(s->auth_ldap_cafile)) {
11046ca932dfSJan Kneschke 				if (LDAP_OPT_SUCCESS != (ret = ldap_set_option(NULL, LDAP_OPT_X_TLS_CACERTFILE,
11056ca932dfSJan Kneschke 								s->auth_ldap_cafile->ptr))) {
11066ca932dfSJan Kneschke 					log_error_write(srv, __FILE__, __LINE__, "ss",
11076ca932dfSJan Kneschke 							"Loading CA certificate failed:", ldap_err2string(ret));
11084a6ce4aeSJan Kneschke 
11094a6ce4aeSJan Kneschke 					return HANDLER_ERROR;
11104a6ce4aeSJan Kneschke 				}
11114a6ce4aeSJan Kneschke 			}
11124a6ce4aeSJan Kneschke 
11134a6ce4aeSJan Kneschke 			if (LDAP_OPT_SUCCESS != (ret = ldap_start_tls_s(s->ldap, NULL,  NULL))) {
11144a6ce4aeSJan Kneschke 				log_error_write(srv, __FILE__, __LINE__, "ss", "ldap startTLS failed:", ldap_err2string(ret));
11154a6ce4aeSJan Kneschke 
11164a6ce4aeSJan Kneschke 				return HANDLER_ERROR;
11174a6ce4aeSJan Kneschke 			}
11184a6ce4aeSJan Kneschke 		}
11194a6ce4aeSJan Kneschke 
1120bcdc6a3bSJan Kneschke 
1121bcdc6a3bSJan Kneschke 		/* 1. */
1122ad3e93eaSStefan Bühler 		if (!buffer_string_is_empty(s->auth_ldap_binddn)) {
11239238cad6SJan Kneschke 			if (LDAP_SUCCESS != (ret = ldap_simple_bind_s(s->ldap, s->auth_ldap_binddn->ptr, s->auth_ldap_bindpw->ptr))) {
11249238cad6SJan Kneschke 				log_error_write(srv, __FILE__, __LINE__, "ss", "ldap:", ldap_err2string(ret));
11259238cad6SJan Kneschke 
11269238cad6SJan Kneschke 				return HANDLER_ERROR;
11279238cad6SJan Kneschke 			}
11289238cad6SJan Kneschke 		} else {
1129bcdc6a3bSJan Kneschke 			if (LDAP_SUCCESS != (ret = ldap_simple_bind_s(s->ldap, NULL, NULL))) {
1130bcdc6a3bSJan Kneschke 				log_error_write(srv, __FILE__, __LINE__, "ss", "ldap:", ldap_err2string(ret));
1131bcdc6a3bSJan Kneschke 
1132bcdc6a3bSJan Kneschke 				return HANDLER_ERROR;
1133bcdc6a3bSJan Kneschke 			}
1134bcdc6a3bSJan Kneschke 		}
11359238cad6SJan Kneschke 	}
11365a9992b1SStefan Bühler 	return HANDLER_GO_ON;
1137bcdc6a3bSJan Kneschke #else
11385a9992b1SStefan Bühler 	UNUSED(s);
1139bcdc6a3bSJan Kneschke 	log_error_write(srv, __FILE__, __LINE__, "s", "no ldap support available");
1140bcdc6a3bSJan Kneschke 	return HANDLER_ERROR;
1141bcdc6a3bSJan Kneschke #endif
1142bcdc6a3bSJan Kneschke }
1143bcdc6a3bSJan Kneschke 
114463f785a2SStefan Bühler int mod_auth_plugin_init(plugin *p);
1145bcdc6a3bSJan Kneschke int mod_auth_plugin_init(plugin *p) {
1146bcdc6a3bSJan Kneschke 	p->version     = LIGHTTPD_VERSION_ID;
1147bcdc6a3bSJan Kneschke 	p->name        = buffer_init_string("auth");
1148bcdc6a3bSJan Kneschke 	p->init        = mod_auth_init;
1149bcdc6a3bSJan Kneschke 	p->set_defaults = mod_auth_set_defaults;
1150bcdc6a3bSJan Kneschke 	p->handle_uri_clean = mod_auth_uri_handler;
1151bcdc6a3bSJan Kneschke 	p->cleanup     = mod_auth_free;
1152bcdc6a3bSJan Kneschke 
1153bcdc6a3bSJan Kneschke 	p->data        = NULL;
1154bcdc6a3bSJan Kneschke 
1155bcdc6a3bSJan Kneschke 	return 0;
1156bcdc6a3bSJan Kneschke }
1157