• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /asuswrt-rt-n18u-9.0.0.4.380.2695/release/src-rt-6.x.4708/router/lighttpd-1.4.39/src/
1#include "server.h"
2#include "log.h"
3#include "http_auth.h"
4#include "inet_ntop_cache.h"
5#include "stream.h"
6#include "base64.h"
7
8#ifdef HAVE_CRYPT_H
9# include <crypt.h>
10#elif defined(__linux__)
11/* linux needs _XOPEN_SOURCE */
12# define _XOPEN_SOURCE
13#endif
14
15#if defined(HAVE_LIBCRYPT) && !defined(HAVE_CRYPT)
16/* always assume crypt() is present if we have -lcrypt */
17# define HAVE_CRYPT
18#endif
19
20#include <sys/types.h>
21#include <sys/stat.h>
22
23#include <fcntl.h>
24#include <stdlib.h>
25#include <stdio.h>
26#include <string.h>
27#include <time.h>
28#include <errno.h>
29#include <unistd.h>
30#include <ctype.h>
31
32#include "md5.h"
33
34#ifdef USE_OPENSSL
35#include <openssl/sha.h>
36#endif
37
38#include "safe_memclear.h"
39
40#define HASHLEN 16
41#define HASHHEXLEN 32
42typedef unsigned char HASH[HASHLEN];
43typedef char HASHHEX[HASHHEXLEN+1];
44
45static void CvtHex(const HASH Bin, char Hex[33]) {
46	li_tohex(Hex, (const char*) Bin, 16);
47}
48
49/**
50 * the $apr1$ handling is taken from apache 1.3.x
51 */
52
53/*
54 * The apr_md5_encode() routine uses much code obtained from the FreeBSD 3.0
55 * MD5 crypt() function, which is licenced as follows:
56 * ----------------------------------------------------------------------------
57 * "THE BEER-WARE LICENSE" (Revision 42):
58 * <phk@login.dknet.dk> wrote this file.  As long as you retain this notice you
59 * can do whatever you want with this stuff. If we meet some day, and you think
60 * this stuff is worth it, you can buy me a beer in return.   Poul-Henning Kamp
61 * ----------------------------------------------------------------------------
62 */
63
64handler_t auth_ldap_init(server *srv, mod_auth_plugin_config *s);
65
66static int http_auth_get_password(server *srv, mod_auth_plugin_data *p, buffer *username, buffer *realm, buffer *password) {
67	int ret = -1;
68
69	if (buffer_is_empty(username) || buffer_is_empty(realm)) return -1;
70
71	if (p->conf.auth_backend == AUTH_BACKEND_HTDIGEST) {
72		stream f;
73		char * f_line;
74
75		if (buffer_string_is_empty(p->conf.auth_htdigest_userfile)) return -1;
76
77		if (0 != stream_open(&f, p->conf.auth_htdigest_userfile)) {
78			log_error_write(srv, __FILE__, __LINE__, "sbss", "opening digest-userfile", p->conf.auth_htdigest_userfile, "failed:", strerror(errno));
79
80			return -1;
81		}
82
83		f_line = f.start;
84
85		while (f_line - f.start != f.size) {
86			char *f_user, *f_pwd, *e, *f_realm;
87			size_t u_len, pwd_len, r_len;
88
89			f_user = f_line;
90
91			/*
92			 * htdigest format
93			 *
94			 * user:realm:md5(user:realm:password)
95			 */
96
97			if (NULL == (f_realm = memchr(f_user, ':', f.size - (f_user - f.start) ))) {
98				log_error_write(srv, __FILE__, __LINE__, "sbs",
99						"parsed error in", p->conf.auth_htdigest_userfile,
100						"expected 'username:realm:hashed password'");
101
102				stream_close(&f);
103
104				return -1;
105			}
106
107			if (NULL == (f_pwd = memchr(f_realm + 1, ':', f.size - (f_realm + 1 - f.start)))) {
108				log_error_write(srv, __FILE__, __LINE__, "sbs",
109						"parsed error in", p->conf.auth_plain_userfile,
110						"expected 'username:realm:hashed password'");
111
112				stream_close(&f);
113
114				return -1;
115			}
116
117			/* get pointers to the fields */
118			u_len = f_realm - f_user;
119			f_realm++;
120			r_len = f_pwd - f_realm;
121			f_pwd++;
122
123			if (NULL != (e = memchr(f_pwd, '\n', f.size - (f_pwd - f.start)))) {
124				pwd_len = e - f_pwd;
125			} else {
126				pwd_len = f.size - (f_pwd - f.start);
127			}
128
129			if (buffer_string_length(username) == u_len &&
130			    (buffer_string_length(realm) == r_len) &&
131			    (0 == strncmp(username->ptr, f_user, u_len)) &&
132			    (0 == strncmp(realm->ptr, f_realm, r_len))) {
133				/* found */
134
135				buffer_copy_string_len(password, f_pwd, pwd_len);
136
137				ret = 0;
138				break;
139			}
140
141			/* EOL */
142			if (!e) break;
143
144			f_line = e + 1;
145		}
146
147		stream_close(&f);
148	} else if (p->conf.auth_backend == AUTH_BACKEND_HTPASSWD ||
149		   p->conf.auth_backend == AUTH_BACKEND_PLAIN) {
150		stream f;
151		char * f_line;
152		buffer *auth_fn;
153
154		auth_fn = (p->conf.auth_backend == AUTH_BACKEND_HTPASSWD) ? p->conf.auth_htpasswd_userfile : p->conf.auth_plain_userfile;
155
156		if (buffer_string_is_empty(auth_fn)) return -1;
157
158		if (0 != stream_open(&f, auth_fn)) {
159			log_error_write(srv, __FILE__, __LINE__, "sbss",
160					"opening plain-userfile", auth_fn, "failed:", strerror(errno));
161
162			return -1;
163		}
164
165		f_line = f.start;
166
167		while (f_line - f.start != f.size) {
168			char *f_user, *f_pwd, *e;
169			size_t u_len, pwd_len;
170
171			f_user = f_line;
172
173			/*
174			 * htpasswd format
175			 *
176			 * user:crypted passwd
177			 */
178
179			if (NULL == (f_pwd = memchr(f_user, ':', f.size - (f_user - f.start) ))) {
180				log_error_write(srv, __FILE__, __LINE__, "sbs",
181						"parsed error in", auth_fn,
182						"expected 'username:hashed password'");
183
184				stream_close(&f);
185
186				return -1;
187			}
188
189			/* get pointers to the fields */
190			u_len = f_pwd - f_user;
191			f_pwd++;
192
193			if (NULL != (e = memchr(f_pwd, '\n', f.size - (f_pwd - f.start)))) {
194				pwd_len = e - f_pwd;
195			} else {
196				pwd_len = f.size - (f_pwd - f.start);
197			}
198
199			if (buffer_string_length(username) == u_len &&
200			    (0 == strncmp(username->ptr, f_user, u_len))) {
201				/* found */
202
203				buffer_copy_string_len(password, f_pwd, pwd_len);
204
205				ret = 0;
206				break;
207			}
208
209			/* EOL */
210			if (!e) break;
211
212			f_line = e + 1;
213		}
214
215		stream_close(&f);
216	} else if (p->conf.auth_backend == AUTH_BACKEND_LDAP) {
217		ret = 0;
218	} else {
219		return -1;
220	}
221
222	return ret;
223}
224
225int http_auth_match_rules(server *srv, array *req, const char *username, const char *group, const char *host) {
226	const char *r = NULL, *rules = NULL;
227	int username_len;
228	data_string *require;
229
230	UNUSED(group);
231	UNUSED(host);
232
233	require = (data_string *)array_get_element(req, "require");
234
235	/* if we get here, the user we got a authed user */
236	if (0 == strcmp(require->value->ptr, "valid-user")) {
237		return 0;
238	}
239
240	/* user=name1|group=name3|host=name4 */
241
242	/* seperate the string by | */
243#if 0
244	log_error_write(srv, __FILE__, __LINE__, "sb", "rules", require->value);
245#endif
246
247	username_len = username ? strlen(username) : 0;
248
249	r = rules = require->value->ptr;
250
251	while (1) {
252		const char *eq;
253		const char *k, *v, *e;
254		int k_len, v_len, r_len;
255
256		e = strchr(r, '|');
257
258		if (e) {
259			r_len = e - r;
260		} else {
261			r_len = strlen(rules) - (r - rules);
262		}
263
264		/* from r to r + r_len is a rule */
265
266		if (0 == strncmp(r, "valid-user", r_len)) {
267			log_error_write(srv, __FILE__, __LINE__, "sb",
268					"parsing the 'require' section in 'auth.require' failed: valid-user cannot be combined with other require rules",
269					require->value);
270			return -1;
271		}
272
273		/* search for = in the rules */
274		if (NULL == (eq = strchr(r, '='))) {
275			log_error_write(srv, __FILE__, __LINE__, "sb",
276					"parsing the 'require' section in 'auth.require' failed: a = is missing",
277					require->value);
278			return -1;
279		}
280
281		/* = out of range */
282		if (eq > r + r_len) {
283			log_error_write(srv, __FILE__, __LINE__, "sb",
284					"parsing the 'require' section in 'auth.require' failed: = out of range",
285					require->value);
286
287			return -1;
288		}
289
290		/* the part before the = is user|group|host */
291
292		k = r;
293		k_len = eq - r;
294		v = eq + 1;
295		v_len = r_len - k_len - 1;
296
297		if (k_len == 4) {
298			if (0 == strncmp(k, "user", k_len)) {
299				if (username &&
300				    username_len == v_len &&
301				    0 == strncmp(username, v, v_len)) {
302					return 0;
303				}
304			} else if (0 == strncmp(k, "host", k_len)) {
305				log_error_write(srv, __FILE__, __LINE__, "s", "host ... (not implemented)");
306			} else {
307				log_error_write(srv, __FILE__, __LINE__, "s", "unknown key");
308				return -1;
309			}
310		} else if (k_len == 5) {
311			if (0 == strncmp(k, "group", k_len)) {
312				log_error_write(srv, __FILE__, __LINE__, "s", "group ... (not implemented)");
313			} else {
314				log_error_write(srv, __FILE__, __LINE__, "ss", "unknown key", k);
315				return -1;
316			}
317		} else {
318			log_error_write(srv, __FILE__, __LINE__, "s", "unknown  key");
319			return -1;
320		}
321
322		if (!e) break;
323		r = e + 1;
324	}
325
326	log_error_write(srv, __FILE__, __LINE__, "s", "nothing matched");
327
328	return -1;
329}
330
331#define APR_MD5_DIGESTSIZE 16
332#define APR1_ID "$apr1$"
333
334/*
335 * The following MD5 password encryption code was largely borrowed from
336 * the FreeBSD 3.0 /usr/src/lib/libcrypt/crypt.c file, which is
337 * licenced as stated at the top of this file.
338 */
339
340static void to64(char *s, unsigned long v, int n)
341{
342	static const unsigned char itoa64[] =         /* 0 ... 63 => ASCII - 64 */
343		"./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
344
345	while (--n >= 0) {
346		*s++ = itoa64[v&0x3f];
347		v >>= 6;
348	}
349}
350
351static void apr_md5_encode(const char *pw, const char *salt, char *result, size_t nbytes) {
352	/*
353	 * Minimum size is 8 bytes for salt, plus 1 for the trailing NUL,
354	 * plus 4 for the '$' separators, plus the password hash itself.
355	 * Let's leave a goodly amount of leeway.
356	 */
357
358	char passwd[120], *p;
359	const char *sp, *ep;
360	unsigned char final[APR_MD5_DIGESTSIZE];
361	ssize_t sl, pl, i;
362	li_MD5_CTX ctx, ctx1;
363	unsigned long l;
364
365	/*
366	 * Refine the salt first.  It's possible we were given an already-hashed
367	 * string as the salt argument, so extract the actual salt value from it
368	 * if so.  Otherwise just use the string up to the first '$' as the salt.
369	 */
370	sp = salt;
371
372	/*
373	 * If it starts with the magic string, then skip that.
374	 */
375	if (!strncmp(sp, APR1_ID, strlen(APR1_ID))) {
376		sp += strlen(APR1_ID);
377	}
378
379	/*
380	 * It stops at the first '$' or 8 chars, whichever comes first
381	 */
382	for (ep = sp; (*ep != '\0') && (*ep != '$') && (ep < (sp + 8)); ep++) {
383		continue;
384	}
385
386	/*
387	 * Get the length of the true salt
388	 */
389	sl = ep - sp;
390
391	/*
392	 * 'Time to make the doughnuts..'
393	 */
394	li_MD5_Init(&ctx);
395
396	/*
397	 * The password first, since that is what is most unknown
398	 */
399	li_MD5_Update(&ctx, pw, strlen(pw));
400
401	/*
402	 * Then our magic string
403	 */
404	li_MD5_Update(&ctx, APR1_ID, strlen(APR1_ID));
405
406	/*
407	 * Then the raw salt
408	 */
409	li_MD5_Update(&ctx, sp, sl);
410
411	/*
412	 * Then just as many characters of the MD5(pw, salt, pw)
413	 */
414	li_MD5_Init(&ctx1);
415	li_MD5_Update(&ctx1, pw, strlen(pw));
416	li_MD5_Update(&ctx1, sp, sl);
417	li_MD5_Update(&ctx1, pw, strlen(pw));
418	li_MD5_Final(final, &ctx1);
419	for (pl = strlen(pw); pl > 0; pl -= APR_MD5_DIGESTSIZE) {
420		li_MD5_Update(
421			&ctx, final,
422			(pl > APR_MD5_DIGESTSIZE) ? APR_MD5_DIGESTSIZE : pl);
423	}
424
425	/*
426	 * Don't leave anything around in vm they could use.
427	 */
428	memset(final, 0, sizeof(final));
429
430	/*
431	 * Then something really weird...
432	 */
433	for (i = strlen(pw); i != 0; i >>= 1) {
434		if (i & 1) {
435			li_MD5_Update(&ctx, final, 1);
436		}
437		else {
438			li_MD5_Update(&ctx, pw, 1);
439		}
440	}
441
442	/*
443	 * Now make the output string.  We know our limitations, so we
444	 * can use the string routines without bounds checking.
445	 */
446	strcpy(passwd, APR1_ID);
447	strncat(passwd, sp, sl);
448	strcat(passwd, "$");
449
450	li_MD5_Final(final, &ctx);
451
452	/*
453	 * And now, just to make sure things don't run too fast..
454	 * On a 60 Mhz Pentium this takes 34 msec, so you would
455	 * need 30 seconds to build a 1000 entry dictionary...
456	 */
457	for (i = 0; i < 1000; i++) {
458		li_MD5_Init(&ctx1);
459		if (i & 1) {
460			li_MD5_Update(&ctx1, pw, strlen(pw));
461		}
462		else {
463			li_MD5_Update(&ctx1, final, APR_MD5_DIGESTSIZE);
464		}
465		if (i % 3) {
466			li_MD5_Update(&ctx1, sp, sl);
467		}
468
469		if (i % 7) {
470			li_MD5_Update(&ctx1, pw, strlen(pw));
471		}
472
473		if (i & 1) {
474			li_MD5_Update(&ctx1, final, APR_MD5_DIGESTSIZE);
475		}
476		else {
477			li_MD5_Update(&ctx1, pw, strlen(pw));
478		}
479		li_MD5_Final(final,&ctx1);
480	}
481
482	p = passwd + strlen(passwd);
483
484	l = (final[ 0]<<16) | (final[ 6]<<8) | final[12]; to64(p, l, 4); p += 4;
485	l = (final[ 1]<<16) | (final[ 7]<<8) | final[13]; to64(p, l, 4); p += 4;
486	l = (final[ 2]<<16) | (final[ 8]<<8) | final[14]; to64(p, l, 4); p += 4;
487	l = (final[ 3]<<16) | (final[ 9]<<8) | final[15]; to64(p, l, 4); p += 4;
488	l = (final[ 4]<<16) | (final[10]<<8) | final[ 5]; to64(p, l, 4); p += 4;
489	l =                    final[11]                ; to64(p, l, 2); p += 2;
490	*p = '\0';
491
492	/*
493	 * Don't leave anything around in vm they could use.
494	 */
495	safe_memclear(final, sizeof(final));
496
497	/* FIXME
498	 */
499#define apr_cpystrn strncpy
500	apr_cpystrn(result, passwd, nbytes - 1);
501}
502
503#ifdef USE_OPENSSL
504static void apr_sha_encode(const char *pw, char *result, size_t nbytes) {
505	unsigned char digest[20];
506	size_t base64_written;
507
508	SHA1((const unsigned char*) pw, strlen(pw), digest);
509
510	memset(result, 0, nbytes);
511
512	/* need 5 bytes for "{SHA}", 28 for base64 (3 bytes -> 4 bytes) of SHA1 (20 bytes), 1 terminating */
513	if (nbytes < 5 + 28 + 1) return;
514
515	memcpy(result, "{SHA}", 5);
516	base64_written = li_to_base64(result + 5, nbytes - 5, digest, 20, BASE64_STANDARD);
517	force_assert(base64_written == 28);
518	result[5 + base64_written] = '\0'; /* terminate string */
519}
520#endif
521
522/**
523 *
524 *
525 * @param password password-string from the auth-backend
526 * @param pw       password-string from the client
527 */
528
529static int http_auth_basic_password_compare(server *srv, mod_auth_plugin_data *p, array *req, buffer *username, buffer *realm, buffer *password, const char *pw) {
530	UNUSED(srv);
531	UNUSED(req);
532
533	if (p->conf.auth_backend == AUTH_BACKEND_HTDIGEST) {
534		/*
535		 * htdigest format
536		 *
537		 * user:realm:md5(user:realm:password)
538		 */
539
540		li_MD5_CTX Md5Ctx;
541		HASH HA1;
542		char a1[256];
543
544		li_MD5_Init(&Md5Ctx);
545		li_MD5_Update(&Md5Ctx, CONST_BUF_LEN(username));
546		li_MD5_Update(&Md5Ctx, CONST_STR_LEN(":"));
547		li_MD5_Update(&Md5Ctx, CONST_BUF_LEN(realm));
548		li_MD5_Update(&Md5Ctx, CONST_STR_LEN(":"));
549		li_MD5_Update(&Md5Ctx, (unsigned char *)pw, strlen(pw));
550		li_MD5_Final(HA1, &Md5Ctx);
551
552		CvtHex(HA1, a1);
553
554		if (0 == strcmp(password->ptr, a1)) {
555			return 0;
556		}
557	} else if (p->conf.auth_backend == AUTH_BACKEND_HTPASSWD) {
558		char sample[120];
559		if (!strncmp(password->ptr, APR1_ID, strlen(APR1_ID))) {
560			/*
561			 * The hash was created using $apr1$ custom algorithm.
562			 */
563			apr_md5_encode(pw, password->ptr, sample, sizeof(sample));
564			return (strcmp(sample, password->ptr) == 0) ? 0 : 1;
565#ifdef USE_OPENSSL
566		} else if (0 == strncmp(password->ptr, "{SHA}", 5)) {
567			apr_sha_encode(pw, sample, sizeof(sample));
568			return (strcmp(sample, password->ptr) == 0) ? 0 : 1;
569#endif
570		} else {
571#if defined(HAVE_CRYPT_R) || defined(HAVE_CRYPT)
572			char *crypted;
573#if defined(HAVE_CRYPT_R)
574			struct crypt_data crypt_tmp_data;
575			crypt_tmp_data.initialized = 0;
576#endif
577
578			/* a simple DES password is 2 + 11 characters. everything else should be longer. */
579			if (buffer_string_length(password) < 13) {
580				return -1;
581			}
582
583#if defined(HAVE_CRYPT_R)
584			if (0 == (crypted = crypt_r(pw, password->ptr, &crypt_tmp_data))) {
585#else
586			if (0 == (crypted = crypt(pw, password->ptr))) {
587#endif
588				/* crypt failed. */
589				return -1;
590			}
591
592			if (0 == strcmp(password->ptr, crypted)) {
593				return 0;
594			}
595#endif
596		}
597	} else if (p->conf.auth_backend == AUTH_BACKEND_PLAIN) {
598		if (0 == strcmp(password->ptr, pw)) {
599			return 0;
600		}
601	} else if (p->conf.auth_backend == AUTH_BACKEND_LDAP) {
602#ifdef USE_LDAP
603		LDAP *ldap;
604		LDAPMessage *lm, *first;
605		char *dn;
606		int ret;
607		char *attrs[] = { LDAP_NO_ATTRS, NULL };
608		size_t i, len;
609
610		/* for now we stay synchronous */
611
612		/*
613		 * 1. connect anonymously (done in plugin init)
614		 * 2. get DN for uid = username
615		 * 3. auth against ldap server
616		 * 4. (optional) check a field
617		 * 5. disconnect
618		 *
619		 */
620
621		/* check username
622		 *
623		 * we have to protect us againt username which modifies out filter in
624		 * a unpleasant way
625		 */
626
627		len = buffer_string_length(username);
628		for (i = 0; i < len; i++) {
629			char c = username->ptr[i];
630
631			if (!isalpha(c) &&
632			    !isdigit(c) &&
633			    (c != ' ') &&
634			    (c != '@') &&
635			    (c != '-') &&
636			    (c != '_') &&
637			    (c != '.') ) {
638
639				log_error_write(srv, __FILE__, __LINE__, "sbd",
640					"ldap: invalid character (- _.@a-zA-Z0-9 allowed) in username:", username, i);
641
642				return -1;
643			}
644		}
645
646		if (p->conf.auth_ldap_allow_empty_pw != 1 && pw[0] == '\0')
647			return -1;
648
649		/* build filter */
650		buffer_copy_buffer(p->ldap_filter, p->conf.ldap_filter_pre);
651		buffer_append_string_buffer(p->ldap_filter, username);
652		buffer_append_string_buffer(p->ldap_filter, p->conf.ldap_filter_post);
653
654
655		/* 2. */
656		if (p->anon_conf->ldap == NULL ||
657		    LDAP_SUCCESS != (ret = ldap_search_s(p->anon_conf->ldap, p->conf.auth_ldap_basedn->ptr, LDAP_SCOPE_SUBTREE, p->ldap_filter->ptr, attrs, 0, &lm))) {
658
659			/* try again; the ldap library sometimes fails for the first call but reconnects */
660			if (p->anon_conf->ldap == NULL || ret != LDAP_SERVER_DOWN ||
661			    LDAP_SUCCESS != (ret = ldap_search_s(p->anon_conf->ldap, p->conf.auth_ldap_basedn->ptr, LDAP_SCOPE_SUBTREE, p->ldap_filter->ptr, attrs, 0, &lm))) {
662
663				if (auth_ldap_init(srv, p->anon_conf) != HANDLER_GO_ON)
664					return -1;
665
666				if (NULL == p->anon_conf->ldap) return -1;
667
668				if (LDAP_SUCCESS != (ret = ldap_search_s(p->anon_conf->ldap, p->conf.auth_ldap_basedn->ptr, LDAP_SCOPE_SUBTREE, p->ldap_filter->ptr, attrs, 0, &lm))) {
669					log_error_write(srv, __FILE__, __LINE__, "sssb",
670							"ldap:", ldap_err2string(ret), "filter:", p->ldap_filter);
671					return -1;
672				}
673			}
674		}
675
676		if (NULL == (first = ldap_first_entry(p->anon_conf->ldap, lm))) {
677			log_error_write(srv, __FILE__, __LINE__, "s", "ldap ...");
678
679			ldap_msgfree(lm);
680
681			return -1;
682		}
683
684		if (NULL == (dn = ldap_get_dn(p->anon_conf->ldap, first))) {
685			log_error_write(srv, __FILE__, __LINE__, "s", "ldap ...");
686
687			ldap_msgfree(lm);
688
689			return -1;
690		}
691
692		ldap_msgfree(lm);
693
694
695		/* 3. */
696		if (NULL == (ldap = ldap_init(p->conf.auth_ldap_hostname->ptr, LDAP_PORT))) {
697			log_error_write(srv, __FILE__, __LINE__, "ss", "ldap ...", strerror(errno));
698			return -1;
699		}
700
701		ret = LDAP_VERSION3;
702		if (LDAP_OPT_SUCCESS != (ret = ldap_set_option(ldap, LDAP_OPT_PROTOCOL_VERSION, &ret))) {
703			log_error_write(srv, __FILE__, __LINE__, "ss", "ldap:", ldap_err2string(ret));
704
705			ldap_unbind_s(ldap);
706
707			return -1;
708		}
709
710		if (p->conf.auth_ldap_starttls == 1) {
711	 		if (LDAP_OPT_SUCCESS != (ret = ldap_start_tls_s(ldap, NULL,  NULL))) {
712	 			log_error_write(srv, __FILE__, __LINE__, "ss", "ldap startTLS failed:", ldap_err2string(ret));
713
714				ldap_unbind_s(ldap);
715
716				return -1;
717	 		}
718 		}
719
720
721		if (LDAP_SUCCESS != (ret = ldap_simple_bind_s(ldap, dn, pw))) {
722			log_error_write(srv, __FILE__, __LINE__, "ss", "ldap:", ldap_err2string(ret));
723
724			ldap_unbind_s(ldap);
725
726			return -1;
727		}
728
729		/* 5. */
730		ldap_unbind_s(ldap);
731
732		/* everything worked, good, access granted */
733
734		return 0;
735#endif
736	}
737	return -1;
738}
739
740int http_auth_basic_check(server *srv, connection *con, mod_auth_plugin_data *p, array *req, const char *realm_str) {
741	buffer *username, *password;
742	char *pw;
743
744	data_string *realm;
745
746	realm = (data_string *)array_get_element(req, "realm");
747
748	username = buffer_init();
749
750	if (!buffer_append_base64_decode(username, realm_str, strlen(realm_str), BASE64_STANDARD)) {
751		log_error_write(srv, __FILE__, __LINE__, "sb", "decodeing base64-string failed", username);
752
753		buffer_free(username);
754		return 0;
755	}
756
757	/* r2 == user:password */
758	if (NULL == (pw = strchr(username->ptr, ':'))) {
759		log_error_write(srv, __FILE__, __LINE__, "sb", ": is missing in", username);
760
761		buffer_free(username);
762		return 0;
763	}
764
765	buffer_string_set_length(username, pw - username->ptr);
766	pw++;
767
768	password = buffer_init();
769	/* copy password to r1 */
770	if (http_auth_get_password(srv, p, username, realm->value, password)) {
771		buffer_free(username);
772		buffer_free(password);
773
774		if (AUTH_BACKEND_UNSET == p->conf.auth_backend) {
775			log_error_write(srv, __FILE__, __LINE__, "s", "auth.backend is not set");
776		} else {
777			log_error_write(srv, __FILE__, __LINE__, "ss", "get_password failed, IP:", inet_ntop_cache_get_ip(srv, &(con->dst_addr)));
778		}
779
780		return 0;
781	}
782
783	/* password doesn't match */
784	if (http_auth_basic_password_compare(srv, p, req, username, realm->value, password, pw)) {
785		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)));
786
787		buffer_free(username);
788		buffer_free(password);
789
790		return 0;
791	}
792
793	/* value is our allow-rules */
794	if (http_auth_match_rules(srv, req, username->ptr, NULL, NULL)) {
795		buffer_free(username);
796		buffer_free(password);
797
798		log_error_write(srv, __FILE__, __LINE__, "s", "rules didn't match");
799
800		return 0;
801	}
802
803	/* remember the username */
804	buffer_copy_buffer(p->auth_user, username);
805
806	buffer_free(username);
807	buffer_free(password);
808
809	return 1;
810}
811
812typedef struct {
813	const char *key;
814	int key_len;
815	char **ptr;
816} digest_kv;
817
818/* return values: -1: error/bad request, 0: failed, 1: success */
819int http_auth_digest_check(server *srv, connection *con, mod_auth_plugin_data *p, array *req, const char *realm_str) {
820	char a1[256];
821	char a2[256];
822
823	char *username = NULL;
824	char *realm = NULL;
825	char *nonce = NULL;
826	char *uri = NULL;
827	char *algorithm = NULL;
828	char *qop = NULL;
829	char *cnonce = NULL;
830	char *nc = NULL;
831	char *respons = NULL;
832
833	char *e, *c;
834	const char *m = NULL;
835	int i;
836	buffer *password, *b, *username_buf, *realm_buf;
837
838	li_MD5_CTX Md5Ctx;
839	HASH HA1;
840	HASH HA2;
841	HASH RespHash;
842	HASHHEX HA2Hex;
843
844
845	/* init pointers */
846#define S(x) \
847	x, sizeof(x)-1, NULL
848	digest_kv dkv[10] = {
849		{ S("username=") },
850		{ S("realm=") },
851		{ S("nonce=") },
852		{ S("uri=") },
853		{ S("algorithm=") },
854		{ S("qop=") },
855		{ S("cnonce=") },
856		{ S("nc=") },
857		{ S("response=") },
858
859		{ NULL, 0, NULL }
860	};
861#undef S
862
863	dkv[0].ptr = &username;
864	dkv[1].ptr = &realm;
865	dkv[2].ptr = &nonce;
866	dkv[3].ptr = &uri;
867	dkv[4].ptr = &algorithm;
868	dkv[5].ptr = &qop;
869	dkv[6].ptr = &cnonce;
870	dkv[7].ptr = &nc;
871	dkv[8].ptr = &respons;
872
873	UNUSED(req);
874
875	if (p->conf.auth_backend != AUTH_BACKEND_HTDIGEST &&
876	    p->conf.auth_backend != AUTH_BACKEND_PLAIN) {
877		log_error_write(srv, __FILE__, __LINE__, "s",
878				"digest: unsupported backend (only htdigest or plain)");
879
880		return -1;
881	}
882
883	b = buffer_init_string(realm_str);
884
885	/* parse credentials from client */
886	for (c = b->ptr; *c; c++) {
887		/* skip whitespaces */
888		while (*c == ' ' || *c == '\t') c++;
889		if (!*c) break;
890
891		for (i = 0; dkv[i].key; i++) {
892			if ((0 == strncmp(c, dkv[i].key, dkv[i].key_len))) {
893				if ((c[dkv[i].key_len] == '"') &&
894				    (NULL != (e = strchr(c + dkv[i].key_len + 1, '"')))) {
895					/* value with "..." */
896					*(dkv[i].ptr) = c + dkv[i].key_len + 1;
897					c = e;
898
899					*e = '\0';
900				} else if (NULL != (e = strchr(c + dkv[i].key_len, ','))) {
901					/* value without "...", terminated by ',' */
902					*(dkv[i].ptr) = c + dkv[i].key_len;
903					c = e;
904
905					*e = '\0';
906				} else {
907					/* value without "...", terminated by EOL */
908					*(dkv[i].ptr) = c + dkv[i].key_len;
909					c += strlen(c) - 1;
910				}
911			}
912		}
913	}
914
915	if (p->conf.auth_debug > 1) {
916		log_error_write(srv, __FILE__, __LINE__, "ss", "username", username);
917		log_error_write(srv, __FILE__, __LINE__, "ss", "realm", realm);
918		log_error_write(srv, __FILE__, __LINE__, "ss", "nonce", nonce);
919		log_error_write(srv, __FILE__, __LINE__, "ss", "uri", uri);
920		log_error_write(srv, __FILE__, __LINE__, "ss", "algorithm", algorithm);
921		log_error_write(srv, __FILE__, __LINE__, "ss", "qop", qop);
922		log_error_write(srv, __FILE__, __LINE__, "ss", "cnonce", cnonce);
923		log_error_write(srv, __FILE__, __LINE__, "ss", "nc", nc);
924		log_error_write(srv, __FILE__, __LINE__, "ss", "response", respons);
925	}
926
927	/* check if everything is transmitted */
928	if (!username ||
929	    !realm ||
930	    !nonce ||
931	    !uri ||
932	    (qop && (!nc || !cnonce)) ||
933	    !respons ) {
934		/* missing field */
935
936		log_error_write(srv, __FILE__, __LINE__, "s",
937				"digest: missing field");
938
939		buffer_free(b);
940		return -1;
941	}
942
943	/**
944	 * protect the md5-sess against missing cnonce and nonce
945	 */
946	if (algorithm &&
947	    0 == strcasecmp(algorithm, "md5-sess") &&
948	    (!nonce || !cnonce)) {
949		log_error_write(srv, __FILE__, __LINE__, "s",
950				"digest: (md5-sess: missing field");
951
952		buffer_free(b);
953		return -1;
954	}
955
956	if (qop && strcasecmp(qop, "auth-int") == 0) {
957		log_error_write(srv, __FILE__, __LINE__, "s",
958				"digest: qop=auth-int not supported");
959
960		buffer_free(b);
961		return -1;
962	}
963
964	m = get_http_method_name(con->request.http_method);
965
966	/* password-string == HA1 */
967	password = buffer_init();
968	username_buf = buffer_init_string(username);
969	realm_buf = buffer_init_string(realm);
970	if (http_auth_get_password(srv, p, username_buf, realm_buf, password)) {
971		buffer_free(password);
972		buffer_free(b);
973		buffer_free(username_buf);
974		buffer_free(realm_buf);
975		return 0;
976	}
977
978	buffer_free(username_buf);
979	buffer_free(realm_buf);
980
981	if (p->conf.auth_backend == AUTH_BACKEND_PLAIN) {
982		/* generate password from plain-text */
983		li_MD5_Init(&Md5Ctx);
984		li_MD5_Update(&Md5Ctx, (unsigned char *)username, strlen(username));
985		li_MD5_Update(&Md5Ctx, CONST_STR_LEN(":"));
986		li_MD5_Update(&Md5Ctx, (unsigned char *)realm, strlen(realm));
987		li_MD5_Update(&Md5Ctx, CONST_STR_LEN(":"));
988		li_MD5_Update(&Md5Ctx, CONST_BUF_LEN(password));
989		li_MD5_Final(HA1, &Md5Ctx);
990	} else if (p->conf.auth_backend == AUTH_BACKEND_HTDIGEST) {
991		/* HA1 */
992		/* transform the 32-byte-hex-md5 to a 16-byte-md5 */
993		for (i = 0; i < HASHLEN; i++) {
994			HA1[i] = hex2int(password->ptr[i*2]) << 4;
995			HA1[i] |= hex2int(password->ptr[i*2+1]);
996		}
997	} else {
998		/* we already check that above */
999		SEGFAULT();
1000	}
1001
1002	buffer_free(password);
1003
1004	if (algorithm &&
1005	    strcasecmp(algorithm, "md5-sess") == 0) {
1006		li_MD5_Init(&Md5Ctx);
1007		/* Errata ID 1649: http://www.rfc-editor.org/errata_search.php?rfc=2617 */
1008		CvtHex(HA1, a1);
1009		li_MD5_Update(&Md5Ctx, (unsigned char *)a1, 32);
1010		li_MD5_Update(&Md5Ctx, CONST_STR_LEN(":"));
1011		li_MD5_Update(&Md5Ctx, (unsigned char *)nonce, strlen(nonce));
1012		li_MD5_Update(&Md5Ctx, CONST_STR_LEN(":"));
1013		li_MD5_Update(&Md5Ctx, (unsigned char *)cnonce, strlen(cnonce));
1014		li_MD5_Final(HA1, &Md5Ctx);
1015	}
1016
1017	CvtHex(HA1, a1);
1018
1019	/* calculate H(A2) */
1020	li_MD5_Init(&Md5Ctx);
1021	li_MD5_Update(&Md5Ctx, (unsigned char *)m, strlen(m));
1022	li_MD5_Update(&Md5Ctx, CONST_STR_LEN(":"));
1023	li_MD5_Update(&Md5Ctx, (unsigned char *)uri, strlen(uri));
1024	/* qop=auth-int not supported, already checked above */
1025/*
1026	if (qop && strcasecmp(qop, "auth-int") == 0) {
1027		li_MD5_Update(&Md5Ctx, CONST_STR_LEN(":"));
1028		li_MD5_Update(&Md5Ctx, (unsigned char *) [body checksum], HASHHEXLEN);
1029	}
1030*/
1031	li_MD5_Final(HA2, &Md5Ctx);
1032	CvtHex(HA2, HA2Hex);
1033
1034	/* calculate response */
1035	li_MD5_Init(&Md5Ctx);
1036	li_MD5_Update(&Md5Ctx, (unsigned char *)a1, HASHHEXLEN);
1037	li_MD5_Update(&Md5Ctx, CONST_STR_LEN(":"));
1038	li_MD5_Update(&Md5Ctx, (unsigned char *)nonce, strlen(nonce));
1039	li_MD5_Update(&Md5Ctx, CONST_STR_LEN(":"));
1040	if (qop && *qop) {
1041		li_MD5_Update(&Md5Ctx, (unsigned char *)nc, strlen(nc));
1042		li_MD5_Update(&Md5Ctx, CONST_STR_LEN(":"));
1043		li_MD5_Update(&Md5Ctx, (unsigned char *)cnonce, strlen(cnonce));
1044		li_MD5_Update(&Md5Ctx, CONST_STR_LEN(":"));
1045		li_MD5_Update(&Md5Ctx, (unsigned char *)qop, strlen(qop));
1046		li_MD5_Update(&Md5Ctx, CONST_STR_LEN(":"));
1047	};
1048	li_MD5_Update(&Md5Ctx, (unsigned char *)HA2Hex, HASHHEXLEN);
1049	li_MD5_Final(RespHash, &Md5Ctx);
1050	CvtHex(RespHash, a2);
1051
1052	if (0 != strcmp(a2, respons)) {
1053		/* digest not ok */
1054
1055		if (p->conf.auth_debug) {
1056			log_error_write(srv, __FILE__, __LINE__, "sss",
1057				"digest: digest mismatch", a2, respons);
1058		}
1059
1060		log_error_write(srv, __FILE__, __LINE__, "ssss",
1061				"digest: auth failed for ", username, ": wrong password, IP:", inet_ntop_cache_get_ip(srv, &(con->dst_addr)));
1062
1063		buffer_free(b);
1064		return 0;
1065	}
1066
1067	/* value is our allow-rules */
1068	if (http_auth_match_rules(srv, req, username, NULL, NULL)) {
1069		buffer_free(b);
1070
1071		log_error_write(srv, __FILE__, __LINE__, "s",
1072				"digest: rules did match");
1073
1074		return 0;
1075	}
1076
1077	/* remember the username */
1078	buffer_copy_string(p->auth_user, username);
1079
1080	buffer_free(b);
1081
1082	if (p->conf.auth_debug) {
1083		log_error_write(srv, __FILE__, __LINE__, "s",
1084				"digest: auth ok");
1085	}
1086	return 1;
1087}
1088
1089
1090int http_auth_digest_generate_nonce(server *srv, mod_auth_plugin_data *p, buffer *fn, char out[33]) {
1091	HASH h;
1092	li_MD5_CTX Md5Ctx;
1093	char hh[LI_ITOSTRING_LENGTH];
1094
1095	UNUSED(p);
1096
1097	/* generate shared-secret */
1098	li_MD5_Init(&Md5Ctx);
1099	li_MD5_Update(&Md5Ctx, CONST_BUF_LEN(fn));
1100	li_MD5_Update(&Md5Ctx, CONST_STR_LEN("+"));
1101
1102	/* we assume sizeof(time_t) == 4 here, but if not it ain't a problem at all */
1103	li_itostr(hh, srv->cur_ts);
1104	li_MD5_Update(&Md5Ctx, (unsigned char *)hh, strlen(hh));
1105	li_MD5_Update(&Md5Ctx, (unsigned char *)srv->entropy, sizeof(srv->entropy));
1106	li_itostr(hh, rand());
1107	li_MD5_Update(&Md5Ctx, (unsigned char *)hh, strlen(hh));
1108
1109	li_MD5_Final(h, &Md5Ctx);
1110
1111	CvtHex(h, out);
1112
1113	return 0;
1114}
1115