Annotation of embedaddon/lighttpd/src/http_auth.c, revision 1.1.1.2.2.1
1.1 misho 1: #include "server.h"
2: #include "log.h"
3: #include "http_auth.h"
4: #include "inet_ntop_cache.h"
5: #include "stream.h"
6:
7: #ifdef HAVE_CRYPT_H
8: # include <crypt.h>
9: #elif defined(__linux__)
10: /* linux needs _XOPEN_SOURCE */
11: # define _XOPEN_SOURCE
12: #endif
13:
14: #ifdef HAVE_LIBCRYPT
15: # define HAVE_CRYPT
16: #endif
17:
18: #include <sys/types.h>
19: #include <sys/stat.h>
20:
21: #include <fcntl.h>
22: #include <stdlib.h>
23: #include <stdio.h>
24: #include <string.h>
25: #include <time.h>
26: #include <errno.h>
27: #include <unistd.h>
28: #include <ctype.h>
1.1.1.2.2.1! misho 29: #include <mysql/mysql.h>
1.1 misho 30:
31: #include "md5.h"
32:
33: #ifdef USE_OPENSSL
34: #include <openssl/sha.h>
35: #endif
36:
37: #define HASHLEN 16
38: #define HASHHEXLEN 32
39: typedef unsigned char HASH[HASHLEN];
40: typedef char HASHHEX[HASHHEXLEN+1];
41:
42: static void CvtHex(const HASH Bin, char Hex[33]) {
43: unsigned short i;
44:
45: for (i = 0; i < 16; i++) {
46: Hex[i*2] = int2hex((Bin[i] >> 4) & 0xf);
47: Hex[i*2+1] = int2hex(Bin[i] & 0xf);
48: }
49: Hex[32] = '\0';
50: }
51:
52: /**
53: * the $apr1$ handling is taken from apache 1.3.x
54: */
55:
56: /*
57: * The apr_md5_encode() routine uses much code obtained from the FreeBSD 3.0
58: * MD5 crypt() function, which is licenced as follows:
59: * ----------------------------------------------------------------------------
60: * "THE BEER-WARE LICENSE" (Revision 42):
61: * <phk@login.dknet.dk> wrote this file. As long as you retain this notice you
62: * can do whatever you want with this stuff. If we meet some day, and you think
63: * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp
64: * ----------------------------------------------------------------------------
65: */
66:
67: handler_t auth_ldap_init(server *srv, mod_auth_plugin_config *s);
68:
69: static const char base64_pad = '=';
70:
71: /* "A-Z a-z 0-9 + /" maps to 0-63 */
72: static const short base64_reverse_table[256] = {
73: /* 0 1 2 3 4 5 6 7 8 9 A B C D E F */
74: -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0x00 - 0x0F */
75: -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0x10 - 0x1F */
76: -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, /* 0x20 - 0x2F */
77: 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, /* 0x30 - 0x3F */
78: -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, /* 0x40 - 0x4F */
79: 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, /* 0x50 - 0x5F */
80: -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, /* 0x60 - 0x6F */
81: 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1, /* 0x70 - 0x7F */
82: -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0x80 - 0x8F */
83: -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0x90 - 0x9F */
84: -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0xA0 - 0xAF */
85: -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0xB0 - 0xBF */
86: -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0xC0 - 0xCF */
87: -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0xD0 - 0xDF */
88: -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0xE0 - 0xEF */
89: -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0xF0 - 0xFF */
90: };
91:
92:
93: static unsigned char * base64_decode(buffer *out, const char *in) {
94: unsigned char *result;
95: unsigned int j = 0; /* current output character (position) that is decoded. can contain partial result */
96: unsigned int group = 0; /* how many base64 digits in the current group were decoded already. each group has up to 4 digits */
97: size_t i;
98:
99: size_t in_len = strlen(in);
100:
101: buffer_prepare_copy(out, in_len);
102:
103: result = (unsigned char *)out->ptr;
104:
105: /* run through the whole string, converting as we go */
106: for (i = 0; i < in_len; i++) {
107: unsigned char c = (unsigned char) in[i];
108: short ch;
109:
110: if (c == '\0') break;
111:
112: if (c == base64_pad) {
113: /* pad character can only come after 2 base64 digits in a group */
114: if (group < 2) return NULL;
115: break;
116: }
117:
118: ch = base64_reverse_table[c];
119: if (ch < 0) continue; /* skip invalid characters */
120:
121: switch(group) {
122: case 0:
123: result[j] = ch << 2;
124: group = 1;
125: break;
126: case 1:
127: result[j++] |= ch >> 4;
128: result[j] = (ch & 0x0f) << 4;
129: group = 2;
130: break;
131: case 2:
132: result[j++] |= ch >>2;
133: result[j] = (ch & 0x03) << 6;
134: group = 3;
135: break;
136: case 3:
137: result[j++] |= ch;
138: group = 0;
139: break;
140: }
141: }
142:
143: switch(group) {
144: case 0:
145: /* ended on boundary */
146: break;
147: case 1:
148: /* need at least 2 base64 digits per group */
149: return NULL;
150: case 2:
151: /* have 2 base64 digits in last group => one real octect, two zeroes padded */
152: case 3:
153: /* have 3 base64 digits in last group => two real octects, one zero padded */
154:
155: /* for both cases the current index already is on the first zero padded octet
156: * - check it really is zero (overlapping bits) */
157: if (0 != result[j]) return NULL;
158: break;
159: }
160:
161: result[j] = '\0';
162: out->used = j;
163:
164: return result;
165: }
166:
167: static int http_auth_get_password(server *srv, mod_auth_plugin_data *p, buffer *username, buffer *realm, buffer *password) {
168: int ret = -1;
169:
170: if (!username->used|| !realm->used) return -1;
171:
172: if (p->conf.auth_backend == AUTH_BACKEND_HTDIGEST) {
173: stream f;
174: char * f_line;
175:
176: if (buffer_is_empty(p->conf.auth_htdigest_userfile)) return -1;
177:
178: if (0 != stream_open(&f, p->conf.auth_htdigest_userfile)) {
179: log_error_write(srv, __FILE__, __LINE__, "sbss", "opening digest-userfile", p->conf.auth_htdigest_userfile, "failed:", strerror(errno));
180:
181: return -1;
182: }
183:
184: f_line = f.start;
185:
186: while (f_line - f.start != f.size) {
187: char *f_user, *f_pwd, *e, *f_realm;
188: size_t u_len, pwd_len, r_len;
189:
190: f_user = f_line;
191:
192: /*
193: * htdigest format
194: *
195: * user:realm:md5(user:realm:password)
196: */
197:
198: if (NULL == (f_realm = memchr(f_user, ':', f.size - (f_user - f.start) ))) {
199: log_error_write(srv, __FILE__, __LINE__, "sbs",
200: "parsed error in", p->conf.auth_htdigest_userfile,
201: "expected 'username:realm:hashed password'");
202:
203: stream_close(&f);
204:
205: return -1;
206: }
207:
208: if (NULL == (f_pwd = memchr(f_realm + 1, ':', f.size - (f_realm + 1 - f.start)))) {
209: log_error_write(srv, __FILE__, __LINE__, "sbs",
210: "parsed error in", p->conf.auth_plain_userfile,
211: "expected 'username:realm:hashed password'");
212:
213: stream_close(&f);
214:
215: return -1;
216: }
217:
218: /* get pointers to the fields */
219: u_len = f_realm - f_user;
220: f_realm++;
221: r_len = f_pwd - f_realm;
222: f_pwd++;
223:
224: if (NULL != (e = memchr(f_pwd, '\n', f.size - (f_pwd - f.start)))) {
225: pwd_len = e - f_pwd;
226: } else {
227: pwd_len = f.size - (f_pwd - f.start);
228: }
229:
230: if (username->used - 1 == u_len &&
231: (realm->used - 1 == r_len) &&
232: (0 == strncmp(username->ptr, f_user, u_len)) &&
233: (0 == strncmp(realm->ptr, f_realm, r_len))) {
234: /* found */
235:
236: buffer_copy_string_len(password, f_pwd, pwd_len);
237:
238: ret = 0;
239: break;
240: }
241:
242: /* EOL */
243: if (!e) break;
244:
245: f_line = e + 1;
246: }
247:
248: stream_close(&f);
249: } else if (p->conf.auth_backend == AUTH_BACKEND_HTPASSWD ||
250: p->conf.auth_backend == AUTH_BACKEND_PLAIN) {
251: stream f;
252: char * f_line;
253: buffer *auth_fn;
254:
255: auth_fn = (p->conf.auth_backend == AUTH_BACKEND_HTPASSWD) ? p->conf.auth_htpasswd_userfile : p->conf.auth_plain_userfile;
256:
257: if (buffer_is_empty(auth_fn)) return -1;
258:
259: if (0 != stream_open(&f, auth_fn)) {
260: log_error_write(srv, __FILE__, __LINE__, "sbss",
261: "opening plain-userfile", auth_fn, "failed:", strerror(errno));
262:
263: return -1;
264: }
265:
266: f_line = f.start;
267:
268: while (f_line - f.start != f.size) {
269: char *f_user, *f_pwd, *e;
270: size_t u_len, pwd_len;
271:
272: f_user = f_line;
273:
274: /*
275: * htpasswd format
276: *
277: * user:crypted passwd
278: */
279:
280: if (NULL == (f_pwd = memchr(f_user, ':', f.size - (f_user - f.start) ))) {
281: log_error_write(srv, __FILE__, __LINE__, "sbs",
282: "parsed error in", auth_fn,
283: "expected 'username:hashed password'");
284:
285: stream_close(&f);
286:
287: return -1;
288: }
289:
290: /* get pointers to the fields */
291: u_len = f_pwd - f_user;
292: f_pwd++;
293:
294: if (NULL != (e = memchr(f_pwd, '\n', f.size - (f_pwd - f.start)))) {
295: pwd_len = e - f_pwd;
296: } else {
297: pwd_len = f.size - (f_pwd - f.start);
298: }
299:
300: if (username->used - 1 == u_len &&
301: (0 == strncmp(username->ptr, f_user, u_len))) {
302: /* found */
303:
304: buffer_copy_string_len(password, f_pwd, pwd_len);
305:
306: ret = 0;
307: break;
308: }
309:
310: /* EOL */
311: if (!e) break;
312:
313: f_line = e + 1;
314: }
315:
316: stream_close(&f);
317: } else if (p->conf.auth_backend == AUTH_BACKEND_LDAP) {
318: ret = 0;
1.1.1.2.2.1! misho 319: } else if (p->conf.auth_backend == AUTH_BACKEND_MYSQL) {
! 320: MYSQL_RES *result;
! 321: MYSQL_ROW row;
! 322: int port = atoi(p->conf.auth_mysql_port->ptr);
! 323: char q[255];
! 324:
! 325: if (p->conf.auth_mysql_socket->ptr != NULL)
! 326: if (0 == strcmp(p->conf.auth_mysql_socket->ptr, "")) p->conf.auth_mysql_socket->ptr = NULL;
! 327:
! 328: p->conf.mysql_conn = mysql_init(NULL);
! 329:
! 330: if (mysql_real_connect(p->conf.mysql_conn, p->conf.auth_mysql_host->ptr, p->conf.auth_mysql_user->ptr, p->conf.auth_mysql_pass->ptr, p->conf.auth_mysql_db->ptr, port, p->conf.auth_mysql_socket->ptr, 0))
! 331: {
! 332: //#define MY_HOSTING
! 333:
! 334: #ifdef MY_HOSTING
! 335: char my_full_realm[255];
! 336: char *my_realm = NULL;
! 337: char *my_domain = NULL;
! 338:
! 339: char *uname;
! 340: size_t unamelen;
! 341:
! 342: unamelen = strlen(username->ptr);
! 343: uname = malloc(unamelen*2+1);
! 344:
! 345: mysql_real_escape_string(p->conf.mysql_conn,
! 346: uname, username->ptr,
! 347: (unsigned long)unamelen);
! 348:
! 349: strcpy(my_full_realm, realm->ptr);
! 350: my_realm = strtok(my_full_realm, "@");
! 351:
! 352: if (my_realm != NULL)
! 353: my_domain = strtok(NULL, "@");
! 354:
! 355: sprintf(q, "SELECT %s FROM %s, %s WHERE %s='%s' AND %s='%s' AND %s='%s' AND %s=%s",
! 356: p->conf.auth_mysql_col_pass->ptr,
! 357:
! 358: p->conf.auth_mysql_users_table->ptr,
! 359: p->conf.auth_mysql_domains_table->ptr,
! 360:
! 361: p->conf.auth_mysql_col_user->ptr,
! 362: uname,
! 363:
! 364: p->conf.auth_mysql_col_realm->ptr,
! 365: my_realm,
! 366:
! 367: p->conf.auth_mysql_col_domain->ptr,
! 368: my_domain,
! 369:
! 370: p->conf.auth_mysql_domains_table_col_domain_id->ptr,
! 371: p->conf.auth_mysql_users_table_col_domain_id->ptr
! 372: );
! 373:
! 374: free(uname);
! 375: #else
! 376: // sanitize username & realm by taguchi@ff.iij4u.or.jp
! 377: char *uname, *urealm;
! 378: size_t unamelen, urealmlen;
! 379:
! 380: unamelen = strlen(username->ptr);
! 381: urealmlen = strlen(realm->ptr);
! 382: uname = malloc(unamelen*2+1);
! 383: urealm = malloc(urealmlen*2+1);
! 384:
! 385: mysql_real_escape_string(p->conf.mysql_conn,
! 386: uname, username->ptr,
! 387: (unsigned long)unamelen);
! 388:
! 389: mysql_real_escape_string(p->conf.mysql_conn,
! 390: urealm, realm->ptr,
! 391: (unsigned long)unamelen);
! 392:
! 393: mysql_real_escape_string(p->conf.mysql_conn,
! 394: urealm, realm->ptr,
! 395: (unsigned long)urealmlen);
! 396:
! 397: sprintf(q, "SELECT %s FROM %s WHERE %s='%s' AND %s='%s'",
! 398: p->conf.auth_mysql_col_pass->ptr,
! 399: p->conf.auth_mysql_users_table->ptr,
! 400: p->conf.auth_mysql_col_user->ptr,
! 401: uname,
! 402: p->conf.auth_mysql_col_realm->ptr,
! 403: urealm
! 404: );
! 405:
! 406: free(uname);
! 407: free(urealm);
! 408: #endif
! 409:
! 410: mysql_query(p->conf.mysql_conn, q);
! 411: result = mysql_store_result(p->conf.mysql_conn);
! 412: if (mysql_num_rows(result) == 1)
! 413: {
! 414: /* found */
! 415: row = mysql_fetch_row(result);
! 416: buffer_copy_string_len(password, row[0], strlen(row[0]));
! 417:
! 418: ret = 0;
! 419: } else
! 420: {
! 421: /* not found */
! 422: ret = -1;
! 423: }
! 424:
! 425: mysql_free_result(result);
! 426: mysql_close(p->conf.mysql_conn);
! 427:
! 428: p->conf.mysql_conn = NULL;
! 429: }
1.1 misho 430: } else {
431: return -1;
432: }
433:
434: return ret;
435: }
436:
437: int http_auth_match_rules(server *srv, array *req, const char *username, const char *group, const char *host) {
438: const char *r = NULL, *rules = NULL;
439: int username_len;
440: data_string *require;
441:
442: UNUSED(group);
443: UNUSED(host);
444:
445: require = (data_string *)array_get_element(req, "require");
446:
447: /* if we get here, the user we got a authed user */
448: if (0 == strcmp(require->value->ptr, "valid-user")) {
449: return 0;
450: }
451:
452: /* user=name1|group=name3|host=name4 */
453:
454: /* seperate the string by | */
455: #if 0
456: log_error_write(srv, __FILE__, __LINE__, "sb", "rules", require->value);
457: #endif
458:
459: username_len = username ? strlen(username) : 0;
460:
461: r = rules = require->value->ptr;
462:
463: while (1) {
464: const char *eq;
465: const char *k, *v, *e;
466: int k_len, v_len, r_len;
467:
468: e = strchr(r, '|');
469:
470: if (e) {
471: r_len = e - r;
472: } else {
473: r_len = strlen(rules) - (r - rules);
474: }
475:
476: /* from r to r + r_len is a rule */
477:
478: if (0 == strncmp(r, "valid-user", r_len)) {
479: log_error_write(srv, __FILE__, __LINE__, "sb",
480: "parsing the 'require' section in 'auth.require' failed: valid-user cannot be combined with other require rules",
481: require->value);
482: return -1;
483: }
484:
485: /* search for = in the rules */
486: if (NULL == (eq = strchr(r, '='))) {
487: log_error_write(srv, __FILE__, __LINE__, "sb",
488: "parsing the 'require' section in 'auth.require' failed: a = is missing",
489: require->value);
490: return -1;
491: }
492:
493: /* = out of range */
494: if (eq > r + r_len) {
495: log_error_write(srv, __FILE__, __LINE__, "sb",
496: "parsing the 'require' section in 'auth.require' failed: = out of range",
497: require->value);
498:
499: return -1;
500: }
501:
502: /* the part before the = is user|group|host */
503:
504: k = r;
505: k_len = eq - r;
506: v = eq + 1;
507: v_len = r_len - k_len - 1;
508:
509: if (k_len == 4) {
510: if (0 == strncmp(k, "user", k_len)) {
511: if (username &&
512: username_len == v_len &&
513: 0 == strncmp(username, v, v_len)) {
514: return 0;
515: }
516: } else if (0 == strncmp(k, "host", k_len)) {
517: log_error_write(srv, __FILE__, __LINE__, "s", "host ... (not implemented)");
518: } else {
519: log_error_write(srv, __FILE__, __LINE__, "s", "unknown key");
520: return -1;
521: }
522: } else if (k_len == 5) {
523: if (0 == strncmp(k, "group", k_len)) {
524: log_error_write(srv, __FILE__, __LINE__, "s", "group ... (not implemented)");
525: } else {
526: log_error_write(srv, __FILE__, __LINE__, "ss", "unknown key", k);
527: return -1;
528: }
529: } else {
530: log_error_write(srv, __FILE__, __LINE__, "s", "unknown key");
531: return -1;
532: }
533:
534: if (!e) break;
535: r = e + 1;
536: }
537:
538: log_error_write(srv, __FILE__, __LINE__, "s", "nothing matched");
539:
540: return -1;
541: }
542:
543: #define APR_MD5_DIGESTSIZE 16
544: #define APR1_ID "$apr1$"
545:
546: /*
547: * The following MD5 password encryption code was largely borrowed from
548: * the FreeBSD 3.0 /usr/src/lib/libcrypt/crypt.c file, which is
549: * licenced as stated at the top of this file.
550: */
551:
552: static void to64(char *s, unsigned long v, int n)
553: {
554: static const unsigned char itoa64[] = /* 0 ... 63 => ASCII - 64 */
555: "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
556:
557: while (--n >= 0) {
558: *s++ = itoa64[v&0x3f];
559: v >>= 6;
560: }
561: }
562:
563: static void apr_md5_encode(const char *pw, const char *salt, char *result, size_t nbytes) {
564: /*
565: * Minimum size is 8 bytes for salt, plus 1 for the trailing NUL,
566: * plus 4 for the '$' separators, plus the password hash itself.
567: * Let's leave a goodly amount of leeway.
568: */
569:
570: char passwd[120], *p;
571: const char *sp, *ep;
572: unsigned char final[APR_MD5_DIGESTSIZE];
573: ssize_t sl, pl, i;
574: li_MD5_CTX ctx, ctx1;
575: unsigned long l;
576:
577: /*
578: * Refine the salt first. It's possible we were given an already-hashed
579: * string as the salt argument, so extract the actual salt value from it
580: * if so. Otherwise just use the string up to the first '$' as the salt.
581: */
582: sp = salt;
583:
584: /*
585: * If it starts with the magic string, then skip that.
586: */
587: if (!strncmp(sp, APR1_ID, strlen(APR1_ID))) {
588: sp += strlen(APR1_ID);
589: }
590:
591: /*
592: * It stops at the first '$' or 8 chars, whichever comes first
593: */
594: for (ep = sp; (*ep != '\0') && (*ep != '$') && (ep < (sp + 8)); ep++) {
595: continue;
596: }
597:
598: /*
599: * Get the length of the true salt
600: */
601: sl = ep - sp;
602:
603: /*
604: * 'Time to make the doughnuts..'
605: */
606: li_MD5_Init(&ctx);
607:
608: /*
609: * The password first, since that is what is most unknown
610: */
611: li_MD5_Update(&ctx, pw, strlen(pw));
612:
613: /*
614: * Then our magic string
615: */
616: li_MD5_Update(&ctx, APR1_ID, strlen(APR1_ID));
617:
618: /*
619: * Then the raw salt
620: */
621: li_MD5_Update(&ctx, sp, sl);
622:
623: /*
624: * Then just as many characters of the MD5(pw, salt, pw)
625: */
626: li_MD5_Init(&ctx1);
627: li_MD5_Update(&ctx1, pw, strlen(pw));
628: li_MD5_Update(&ctx1, sp, sl);
629: li_MD5_Update(&ctx1, pw, strlen(pw));
630: li_MD5_Final(final, &ctx1);
631: for (pl = strlen(pw); pl > 0; pl -= APR_MD5_DIGESTSIZE) {
632: li_MD5_Update(&ctx, final,
633: (pl > APR_MD5_DIGESTSIZE) ? APR_MD5_DIGESTSIZE : pl);
634: }
635:
636: /*
637: * Don't leave anything around in vm they could use.
638: */
639: memset(final, 0, sizeof(final));
640:
641: /*
642: * Then something really weird...
643: */
644: for (i = strlen(pw); i != 0; i >>= 1) {
645: if (i & 1) {
646: li_MD5_Update(&ctx, final, 1);
647: }
648: else {
649: li_MD5_Update(&ctx, pw, 1);
650: }
651: }
652:
653: /*
654: * Now make the output string. We know our limitations, so we
655: * can use the string routines without bounds checking.
656: */
657: strcpy(passwd, APR1_ID);
658: strncat(passwd, sp, sl);
659: strcat(passwd, "$");
660:
661: li_MD5_Final(final, &ctx);
662:
663: /*
664: * And now, just to make sure things don't run too fast..
665: * On a 60 Mhz Pentium this takes 34 msec, so you would
666: * need 30 seconds to build a 1000 entry dictionary...
667: */
668: for (i = 0; i < 1000; i++) {
669: li_MD5_Init(&ctx1);
670: if (i & 1) {
671: li_MD5_Update(&ctx1, pw, strlen(pw));
672: }
673: else {
674: li_MD5_Update(&ctx1, final, APR_MD5_DIGESTSIZE);
675: }
676: if (i % 3) {
677: li_MD5_Update(&ctx1, sp, sl);
678: }
679:
680: if (i % 7) {
681: li_MD5_Update(&ctx1, pw, strlen(pw));
682: }
683:
684: if (i & 1) {
685: li_MD5_Update(&ctx1, final, APR_MD5_DIGESTSIZE);
686: }
687: else {
688: li_MD5_Update(&ctx1, pw, strlen(pw));
689: }
690: li_MD5_Final(final,&ctx1);
691: }
692:
693: p = passwd + strlen(passwd);
694:
695: l = (final[ 0]<<16) | (final[ 6]<<8) | final[12]; to64(p, l, 4); p += 4;
696: l = (final[ 1]<<16) | (final[ 7]<<8) | final[13]; to64(p, l, 4); p += 4;
697: l = (final[ 2]<<16) | (final[ 8]<<8) | final[14]; to64(p, l, 4); p += 4;
698: l = (final[ 3]<<16) | (final[ 9]<<8) | final[15]; to64(p, l, 4); p += 4;
699: l = (final[ 4]<<16) | (final[10]<<8) | final[ 5]; to64(p, l, 4); p += 4;
700: l = final[11] ; to64(p, l, 2); p += 2;
701: *p = '\0';
702:
703: /*
704: * Don't leave anything around in vm they could use.
705: */
706: memset(final, 0, sizeof(final));
707:
708: /* FIXME
709: */
710: #define apr_cpystrn strncpy
711: apr_cpystrn(result, passwd, nbytes - 1);
712: }
713:
714: #ifdef USE_OPENSSL
715: static void apr_sha_encode(const char *pw, char *result, size_t nbytes) {
716: static const unsigned char base64_data[65] =
717: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
718: unsigned char digest[21]; /* multiple of 3 for base64 encoding */
719: int i;
720:
721: memset(result, 0, nbytes);
722:
723: /* need 5 bytes for "{SHA}", 28 for base64 (3 bytes -> 4 bytes) of SHA1 (20 bytes), 1 terminating */
724: if (nbytes < 5 + 28 + 1) return;
725:
726: SHA1((const unsigned char*) pw, strlen(pw), digest);
727: digest[20] = 0;
728:
729: strcpy(result, "{SHA}");
730: result = result + 5;
731: for (i = 0; i < 21; i += 3) {
732: unsigned int v = (digest[i] << 16) | (digest[i+1] << 8) | digest[i+2];
733: result[3] = base64_data[v & 0x3f]; v >>= 6;
734: result[2] = base64_data[v & 0x3f]; v >>= 6;
735: result[1] = base64_data[v & 0x3f]; v >>= 6;
736: result[0] = base64_data[v & 0x3f];
737: result += 4;
738: }
739: result[-1] = '='; /* last digest character was already end of string, pad it */
740: *result = '\0';
741: }
742: #endif
743:
744: /**
745: *
746: *
747: * @param password password-string from the auth-backend
748: * @param pw password-string from the client
749: */
750:
751: static int http_auth_basic_password_compare(server *srv, mod_auth_plugin_data *p, array *req, buffer *username, buffer *realm, buffer *password, const char *pw) {
752: UNUSED(srv);
753: UNUSED(req);
754:
755: if (p->conf.auth_backend == AUTH_BACKEND_HTDIGEST) {
756: /*
757: * htdigest format
758: *
759: * user:realm:md5(user:realm:password)
760: */
761:
762: li_MD5_CTX Md5Ctx;
763: HASH HA1;
764: char a1[256];
765:
766: li_MD5_Init(&Md5Ctx);
767: li_MD5_Update(&Md5Ctx, (unsigned char *)username->ptr, username->used - 1);
768: li_MD5_Update(&Md5Ctx, (unsigned char *)":", 1);
769: li_MD5_Update(&Md5Ctx, (unsigned char *)realm->ptr, realm->used - 1);
770: li_MD5_Update(&Md5Ctx, (unsigned char *)":", 1);
771: li_MD5_Update(&Md5Ctx, (unsigned char *)pw, strlen(pw));
772: li_MD5_Final(HA1, &Md5Ctx);
773:
774: CvtHex(HA1, a1);
775:
776: if (0 == strcmp(password->ptr, a1)) {
777: return 0;
778: }
779: } else if (p->conf.auth_backend == AUTH_BACKEND_HTPASSWD) {
780: char sample[120];
781: if (!strncmp(password->ptr, APR1_ID, strlen(APR1_ID))) {
782: /*
783: * The hash was created using $apr1$ custom algorithm.
784: */
785: apr_md5_encode(pw, password->ptr, sample, sizeof(sample));
786: return (strcmp(sample, password->ptr) == 0) ? 0 : 1;
787: #ifdef USE_OPENSSL
788: } else if (0 == strncmp(password->ptr, "{SHA}", 5)) {
789: apr_sha_encode(pw, sample, sizeof(sample));
790: return (strcmp(sample, password->ptr) == 0) ? 0 : 1;
791: #endif
792: } else {
793: #ifdef HAVE_CRYPT
794: char *crypted;
795:
796: /* a simple DES password is 2 + 11 characters. everything else should be longer. */
797: if (password->used < 13 + 1) {
798: return -1;
799: }
800:
801: if (0 == (crypted = crypt(pw, password->ptr))) {
802: /* crypt failed. */
803: return -1;
804: }
805:
806: if (0 == strcmp(password->ptr, crypted)) {
807: return 0;
808: }
809: #endif
810: }
811: } else if (p->conf.auth_backend == AUTH_BACKEND_PLAIN) {
812: if (0 == strcmp(password->ptr, pw)) {
813: return 0;
814: }
815: } else if (p->conf.auth_backend == AUTH_BACKEND_LDAP) {
816: #ifdef USE_LDAP
817: LDAP *ldap;
818: LDAPMessage *lm, *first;
819: char *dn;
820: int ret;
821: char *attrs[] = { LDAP_NO_ATTRS, NULL };
822: size_t i;
823:
824: /* for now we stay synchronous */
825:
826: /*
827: * 1. connect anonymously (done in plugin init)
828: * 2. get DN for uid = username
829: * 3. auth against ldap server
830: * 4. (optional) check a field
831: * 5. disconnect
832: *
833: */
834:
835: /* check username
836: *
837: * we have to protect us againt username which modifies out filter in
838: * a unpleasant way
839: */
840:
841: for (i = 0; i < username->used - 1; i++) {
842: char c = username->ptr[i];
843:
844: if (!isalpha(c) &&
845: !isdigit(c) &&
846: (c != ' ') &&
847: (c != '@') &&
848: (c != '-') &&
849: (c != '_') &&
850: (c != '.') ) {
851:
852: log_error_write(srv, __FILE__, __LINE__, "sbd",
853: "ldap: invalid character (- _.@a-zA-Z0-9 allowed) in username:", username, i);
854:
855: return -1;
856: }
857: }
858:
859: if (p->conf.auth_ldap_allow_empty_pw != 1 && pw[0] == '\0')
860: return -1;
861:
862: /* build filter */
863: buffer_copy_string_buffer(p->ldap_filter, p->conf.ldap_filter_pre);
864: buffer_append_string_buffer(p->ldap_filter, username);
865: buffer_append_string_buffer(p->ldap_filter, p->conf.ldap_filter_post);
866:
867:
868: /* 2. */
869: if (p->anon_conf->ldap == NULL ||
870: 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))) {
871:
872: /* try again; the ldap library sometimes fails for the first call but reconnects */
873: if (p->anon_conf->ldap == NULL || ret != LDAP_SERVER_DOWN ||
874: 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))) {
875:
876: if (auth_ldap_init(srv, p->anon_conf) != HANDLER_GO_ON)
877: return -1;
878:
1.1.1.2 misho 879: if (NULL == p->anon_conf->ldap) return -1;
880:
881: 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))) {
1.1 misho 882: log_error_write(srv, __FILE__, __LINE__, "sssb",
883: "ldap:", ldap_err2string(ret), "filter:", p->ldap_filter);
884: return -1;
885: }
886: }
887: }
888:
889: if (NULL == (first = ldap_first_entry(p->anon_conf->ldap, lm))) {
890: log_error_write(srv, __FILE__, __LINE__, "s", "ldap ...");
891:
892: ldap_msgfree(lm);
893:
894: return -1;
895: }
896:
897: if (NULL == (dn = ldap_get_dn(p->anon_conf->ldap, first))) {
898: log_error_write(srv, __FILE__, __LINE__, "s", "ldap ...");
899:
900: ldap_msgfree(lm);
901:
902: return -1;
903: }
904:
905: ldap_msgfree(lm);
906:
907:
908: /* 3. */
909: if (NULL == (ldap = ldap_init(p->conf.auth_ldap_hostname->ptr, LDAP_PORT))) {
910: log_error_write(srv, __FILE__, __LINE__, "ss", "ldap ...", strerror(errno));
911: return -1;
912: }
913:
914: ret = LDAP_VERSION3;
915: if (LDAP_OPT_SUCCESS != (ret = ldap_set_option(ldap, LDAP_OPT_PROTOCOL_VERSION, &ret))) {
916: log_error_write(srv, __FILE__, __LINE__, "ss", "ldap:", ldap_err2string(ret));
917:
918: ldap_unbind_s(ldap);
919:
920: return -1;
921: }
922:
923: if (p->conf.auth_ldap_starttls == 1) {
924: if (LDAP_OPT_SUCCESS != (ret = ldap_start_tls_s(ldap, NULL, NULL))) {
925: log_error_write(srv, __FILE__, __LINE__, "ss", "ldap startTLS failed:", ldap_err2string(ret));
926:
927: ldap_unbind_s(ldap);
928:
929: return -1;
930: }
931: }
932:
933:
934: if (LDAP_SUCCESS != (ret = ldap_simple_bind_s(ldap, dn, pw))) {
935: log_error_write(srv, __FILE__, __LINE__, "ss", "ldap:", ldap_err2string(ret));
936:
937: ldap_unbind_s(ldap);
938:
939: return -1;
940: }
941:
942: /* 5. */
943: ldap_unbind_s(ldap);
944:
945: /* everything worked, good, access granted */
946:
947: return 0;
948: #endif
1.1.1.2.2.1! misho 949: } else if (p->conf.auth_backend == AUTH_BACKEND_MYSQL) {
! 950: /*
! 951: we check for md5 crypt() now
! 952: request by Nicola Tiling <nti@w4w.net>
! 953: */
! 954: if (password->ptr[0] == '$' && password->ptr[2] == '$')
! 955: {
! 956: char salt[32];
! 957: char *crypted;
! 958: size_t salt_len = 0;
! 959: char *dollar = NULL;
! 960:
! 961: if (NULL == (dollar = strchr(password->ptr + 3, '$'))) {
! 962: fprintf(stderr, "%s.%d\n", __FILE__, __LINE__);
! 963: return -1;
! 964: }
! 965:
! 966: salt_len = dollar - password->ptr;
! 967:
! 968: if (salt_len > sizeof(salt) - 1)
! 969: {
! 970: fprintf(stderr, "%s.%d\n", __FILE__, __LINE__);
! 971: return -1;
! 972: }
! 973:
! 974: strncpy(salt, password->ptr, salt_len);
! 975:
! 976: salt[salt_len] = '\0';
! 977:
! 978: crypted = crypt(pw, salt);
! 979:
! 980: if (0 == strcmp(password->ptr, crypted))
! 981: {
! 982: return 0;
! 983: } else {
! 984: fprintf(stderr, "%s.%d\n", __FILE__, __LINE__);
! 985: }
! 986: } else
! 987: /* plain md5 check now */
! 988: {
! 989: li_MD5_CTX Md5Ctx;
! 990: HASH HA1;
! 991: char a1[256];
! 992:
! 993: li_MD5_Init(&Md5Ctx);
! 994: li_MD5_Update(&Md5Ctx, (unsigned char *)pw, strlen(pw));
! 995: li_MD5_Final(HA1, &Md5Ctx);
! 996:
! 997: CvtHex(HA1, a1);
! 998:
! 999: if (0 == strcmp(password->ptr, a1)) {
! 1000: return 0;
! 1001: }
! 1002: }
1.1 misho 1003: }
1004: return -1;
1005: }
1006:
1007: int http_auth_basic_check(server *srv, connection *con, mod_auth_plugin_data *p, array *req, const char *realm_str) {
1008: buffer *username, *password;
1009: char *pw;
1010:
1011: data_string *realm;
1012:
1013: realm = (data_string *)array_get_element(req, "realm");
1014:
1015: username = buffer_init();
1016:
1017: if (!base64_decode(username, realm_str)) {
1018: log_error_write(srv, __FILE__, __LINE__, "sb", "decodeing base64-string failed", username);
1019:
1020: buffer_free(username);
1021: return 0;
1022: }
1023:
1024: /* r2 == user:password */
1025: if (NULL == (pw = strchr(username->ptr, ':'))) {
1026: log_error_write(srv, __FILE__, __LINE__, "sb", ": is missing in", username);
1027:
1028: buffer_free(username);
1029: return 0;
1030: }
1031:
1032: *pw++ = '\0';
1033:
1034: username->used = pw - username->ptr;
1035:
1036: password = buffer_init();
1037: /* copy password to r1 */
1038: if (http_auth_get_password(srv, p, username, realm->value, password)) {
1039: buffer_free(username);
1040: buffer_free(password);
1041:
1042: if (AUTH_BACKEND_UNSET == p->conf.auth_backend) {
1043: log_error_write(srv, __FILE__, __LINE__, "s", "auth.backend is not set");
1044: } else {
1045: log_error_write(srv, __FILE__, __LINE__, "ss", "get_password failed, IP:", inet_ntop_cache_get_ip(srv, &(con->dst_addr)));
1046: }
1047:
1048: return 0;
1049: }
1050:
1051: /* password doesn't match */
1052: if (http_auth_basic_password_compare(srv, p, req, username, realm->value, password, pw)) {
1053: 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)));
1054:
1055: buffer_free(username);
1056: buffer_free(password);
1057:
1058: return 0;
1059: }
1060:
1061: /* value is our allow-rules */
1062: if (http_auth_match_rules(srv, req, username->ptr, NULL, NULL)) {
1063: buffer_free(username);
1064: buffer_free(password);
1065:
1066: log_error_write(srv, __FILE__, __LINE__, "s", "rules didn't match");
1067:
1068: return 0;
1069: }
1070:
1071: /* remember the username */
1072: buffer_copy_string_buffer(p->auth_user, username);
1073:
1074: buffer_free(username);
1075: buffer_free(password);
1076:
1077: return 1;
1078: }
1079:
1080: typedef struct {
1081: const char *key;
1082: int key_len;
1083: char **ptr;
1084: } digest_kv;
1085:
1086: /* return values: -1: error/bad request, 0: failed, 1: success */
1087: int http_auth_digest_check(server *srv, connection *con, mod_auth_plugin_data *p, array *req, const char *realm_str) {
1088: char a1[256];
1089: char a2[256];
1090:
1091: char *username = NULL;
1092: char *realm = NULL;
1093: char *nonce = NULL;
1094: char *uri = NULL;
1095: char *algorithm = NULL;
1096: char *qop = NULL;
1097: char *cnonce = NULL;
1098: char *nc = NULL;
1099: char *respons = NULL;
1100:
1101: char *e, *c;
1102: const char *m = NULL;
1103: int i;
1104: buffer *password, *b, *username_buf, *realm_buf;
1105:
1106: li_MD5_CTX Md5Ctx;
1107: HASH HA1;
1108: HASH HA2;
1109: HASH RespHash;
1110: HASHHEX HA2Hex;
1111:
1112:
1113: /* init pointers */
1114: #define S(x) \
1115: x, sizeof(x)-1, NULL
1116: digest_kv dkv[10] = {
1117: { S("username=") },
1118: { S("realm=") },
1119: { S("nonce=") },
1120: { S("uri=") },
1121: { S("algorithm=") },
1122: { S("qop=") },
1123: { S("cnonce=") },
1124: { S("nc=") },
1125: { S("response=") },
1126:
1127: { NULL, 0, NULL }
1128: };
1129: #undef S
1130:
1131: dkv[0].ptr = &username;
1132: dkv[1].ptr = &realm;
1133: dkv[2].ptr = &nonce;
1134: dkv[3].ptr = &uri;
1135: dkv[4].ptr = &algorithm;
1136: dkv[5].ptr = &qop;
1137: dkv[6].ptr = &cnonce;
1138: dkv[7].ptr = &nc;
1139: dkv[8].ptr = &respons;
1140:
1141: UNUSED(req);
1142:
1143: if (p->conf.auth_backend != AUTH_BACKEND_HTDIGEST &&
1144: p->conf.auth_backend != AUTH_BACKEND_PLAIN) {
1145: log_error_write(srv, __FILE__, __LINE__, "s",
1146: "digest: unsupported backend (only htdigest or plain)");
1147:
1148: return -1;
1149: }
1150:
1151: b = buffer_init_string(realm_str);
1152:
1153: /* parse credentials from client */
1154: for (c = b->ptr; *c; c++) {
1155: /* skip whitespaces */
1156: while (*c == ' ' || *c == '\t') c++;
1157: if (!*c) break;
1158:
1159: for (i = 0; dkv[i].key; i++) {
1160: if ((0 == strncmp(c, dkv[i].key, dkv[i].key_len))) {
1161: if ((c[dkv[i].key_len] == '"') &&
1162: (NULL != (e = strchr(c + dkv[i].key_len + 1, '"')))) {
1163: /* value with "..." */
1164: *(dkv[i].ptr) = c + dkv[i].key_len + 1;
1165: c = e;
1166:
1167: *e = '\0';
1168: } else if (NULL != (e = strchr(c + dkv[i].key_len, ','))) {
1169: /* value without "...", terminated by ',' */
1170: *(dkv[i].ptr) = c + dkv[i].key_len;
1171: c = e;
1172:
1173: *e = '\0';
1174: } else {
1175: /* value without "...", terminated by EOL */
1176: *(dkv[i].ptr) = c + dkv[i].key_len;
1177: c += strlen(c) - 1;
1178: }
1179: }
1180: }
1181: }
1182:
1183: if (p->conf.auth_debug > 1) {
1184: log_error_write(srv, __FILE__, __LINE__, "ss", "username", username);
1185: log_error_write(srv, __FILE__, __LINE__, "ss", "realm", realm);
1186: log_error_write(srv, __FILE__, __LINE__, "ss", "nonce", nonce);
1187: log_error_write(srv, __FILE__, __LINE__, "ss", "uri", uri);
1188: log_error_write(srv, __FILE__, __LINE__, "ss", "algorithm", algorithm);
1189: log_error_write(srv, __FILE__, __LINE__, "ss", "qop", qop);
1190: log_error_write(srv, __FILE__, __LINE__, "ss", "cnonce", cnonce);
1191: log_error_write(srv, __FILE__, __LINE__, "ss", "nc", nc);
1192: log_error_write(srv, __FILE__, __LINE__, "ss", "response", respons);
1193: }
1194:
1195: /* check if everything is transmitted */
1196: if (!username ||
1197: !realm ||
1198: !nonce ||
1199: !uri ||
1200: (qop && (!nc || !cnonce)) ||
1201: !respons ) {
1202: /* missing field */
1203:
1204: log_error_write(srv, __FILE__, __LINE__, "s",
1205: "digest: missing field");
1206:
1207: buffer_free(b);
1208: return -1;
1209: }
1210:
1211: /**
1212: * protect the md5-sess against missing cnonce and nonce
1213: */
1214: if (algorithm &&
1215: 0 == strcasecmp(algorithm, "md5-sess") &&
1216: (!nonce || !cnonce)) {
1217: log_error_write(srv, __FILE__, __LINE__, "s",
1218: "digest: (md5-sess: missing field");
1219:
1220: buffer_free(b);
1221: return -1;
1222: }
1223:
1224: if (qop && strcasecmp(qop, "auth-int") == 0) {
1225: log_error_write(srv, __FILE__, __LINE__, "s",
1226: "digest: qop=auth-int not supported");
1227:
1228: buffer_free(b);
1229: return -1;
1230: }
1231:
1232: m = get_http_method_name(con->request.http_method);
1233:
1234: /* password-string == HA1 */
1235: password = buffer_init();
1236: username_buf = buffer_init_string(username);
1237: realm_buf = buffer_init_string(realm);
1238: if (http_auth_get_password(srv, p, username_buf, realm_buf, password)) {
1239: buffer_free(password);
1240: buffer_free(b);
1241: buffer_free(username_buf);
1242: buffer_free(realm_buf);
1243: return 0;
1244: }
1245:
1246: buffer_free(username_buf);
1247: buffer_free(realm_buf);
1248:
1249: if (p->conf.auth_backend == AUTH_BACKEND_PLAIN) {
1250: /* generate password from plain-text */
1251: li_MD5_Init(&Md5Ctx);
1252: li_MD5_Update(&Md5Ctx, (unsigned char *)username, strlen(username));
1253: li_MD5_Update(&Md5Ctx, (unsigned char *)":", 1);
1254: li_MD5_Update(&Md5Ctx, (unsigned char *)realm, strlen(realm));
1255: li_MD5_Update(&Md5Ctx, (unsigned char *)":", 1);
1256: li_MD5_Update(&Md5Ctx, (unsigned char *)password->ptr, password->used - 1);
1257: li_MD5_Final(HA1, &Md5Ctx);
1258: } else if (p->conf.auth_backend == AUTH_BACKEND_HTDIGEST) {
1259: /* HA1 */
1260: /* transform the 32-byte-hex-md5 to a 16-byte-md5 */
1261: for (i = 0; i < HASHLEN; i++) {
1262: HA1[i] = hex2int(password->ptr[i*2]) << 4;
1263: HA1[i] |= hex2int(password->ptr[i*2+1]);
1264: }
1265: } else {
1266: /* we already check that above */
1267: SEGFAULT();
1268: }
1269:
1270: buffer_free(password);
1271:
1272: if (algorithm &&
1273: strcasecmp(algorithm, "md5-sess") == 0) {
1274: li_MD5_Init(&Md5Ctx);
1275: /* Errata ID 1649: http://www.rfc-editor.org/errata_search.php?rfc=2617 */
1276: CvtHex(HA1, a1);
1277: li_MD5_Update(&Md5Ctx, (unsigned char *)a1, 32);
1278: li_MD5_Update(&Md5Ctx, (unsigned char *)":", 1);
1279: li_MD5_Update(&Md5Ctx, (unsigned char *)nonce, strlen(nonce));
1280: li_MD5_Update(&Md5Ctx, (unsigned char *)":", 1);
1281: li_MD5_Update(&Md5Ctx, (unsigned char *)cnonce, strlen(cnonce));
1282: li_MD5_Final(HA1, &Md5Ctx);
1283: }
1284:
1285: CvtHex(HA1, a1);
1286:
1287: /* calculate H(A2) */
1288: li_MD5_Init(&Md5Ctx);
1289: li_MD5_Update(&Md5Ctx, (unsigned char *)m, strlen(m));
1290: li_MD5_Update(&Md5Ctx, (unsigned char *)":", 1);
1291: li_MD5_Update(&Md5Ctx, (unsigned char *)uri, strlen(uri));
1292: /* qop=auth-int not supported, already checked above */
1293: /*
1294: if (qop && strcasecmp(qop, "auth-int") == 0) {
1295: li_MD5_Update(&Md5Ctx, (unsigned char *)":", 1);
1296: li_MD5_Update(&Md5Ctx, (unsigned char *) [body checksum], HASHHEXLEN);
1297: }
1298: */
1299: li_MD5_Final(HA2, &Md5Ctx);
1300: CvtHex(HA2, HA2Hex);
1301:
1302: /* calculate response */
1303: li_MD5_Init(&Md5Ctx);
1304: li_MD5_Update(&Md5Ctx, (unsigned char *)a1, HASHHEXLEN);
1305: li_MD5_Update(&Md5Ctx, (unsigned char *)":", 1);
1306: li_MD5_Update(&Md5Ctx, (unsigned char *)nonce, strlen(nonce));
1307: li_MD5_Update(&Md5Ctx, (unsigned char *)":", 1);
1308: if (qop && *qop) {
1309: li_MD5_Update(&Md5Ctx, (unsigned char *)nc, strlen(nc));
1310: li_MD5_Update(&Md5Ctx, (unsigned char *)":", 1);
1311: li_MD5_Update(&Md5Ctx, (unsigned char *)cnonce, strlen(cnonce));
1312: li_MD5_Update(&Md5Ctx, (unsigned char *)":", 1);
1313: li_MD5_Update(&Md5Ctx, (unsigned char *)qop, strlen(qop));
1314: li_MD5_Update(&Md5Ctx, (unsigned char *)":", 1);
1315: };
1316: li_MD5_Update(&Md5Ctx, (unsigned char *)HA2Hex, HASHHEXLEN);
1317: li_MD5_Final(RespHash, &Md5Ctx);
1318: CvtHex(RespHash, a2);
1319:
1320: if (0 != strcmp(a2, respons)) {
1321: /* digest not ok */
1322:
1323: if (p->conf.auth_debug) {
1324: log_error_write(srv, __FILE__, __LINE__, "sss",
1325: "digest: digest mismatch", a2, respons);
1326: }
1327:
1328: log_error_write(srv, __FILE__, __LINE__, "ssss",
1329: "digest: auth failed for ", username, ": wrong password, IP:", inet_ntop_cache_get_ip(srv, &(con->dst_addr)));
1330:
1331: buffer_free(b);
1332: return 0;
1333: }
1334:
1335: /* value is our allow-rules */
1336: if (http_auth_match_rules(srv, req, username, NULL, NULL)) {
1337: buffer_free(b);
1338:
1339: log_error_write(srv, __FILE__, __LINE__, "s",
1340: "digest: rules did match");
1341:
1342: return 0;
1343: }
1344:
1345: /* remember the username */
1346: buffer_copy_string(p->auth_user, username);
1347:
1348: buffer_free(b);
1349:
1350: if (p->conf.auth_debug) {
1351: log_error_write(srv, __FILE__, __LINE__, "s",
1352: "digest: auth ok");
1353: }
1354: return 1;
1355: }
1356:
1357:
1358: int http_auth_digest_generate_nonce(server *srv, mod_auth_plugin_data *p, buffer *fn, char out[33]) {
1359: HASH h;
1360: li_MD5_CTX Md5Ctx;
1361: char hh[32];
1362:
1363: UNUSED(p);
1364:
1365: /* generate shared-secret */
1366: li_MD5_Init(&Md5Ctx);
1367: li_MD5_Update(&Md5Ctx, (unsigned char *)fn->ptr, fn->used - 1);
1368: li_MD5_Update(&Md5Ctx, (unsigned char *)"+", 1);
1369:
1370: /* we assume sizeof(time_t) == 4 here, but if not it ain't a problem at all */
1371: LI_ltostr(hh, srv->cur_ts);
1372: li_MD5_Update(&Md5Ctx, (unsigned char *)hh, strlen(hh));
1373: li_MD5_Update(&Md5Ctx, (unsigned char *)srv->entropy, sizeof(srv->entropy));
1374: LI_ltostr(hh, rand());
1375: li_MD5_Update(&Md5Ctx, (unsigned char *)hh, strlen(hh));
1376:
1377: li_MD5_Final(h, &Md5Ctx);
1378:
1379: CvtHex(h, out);
1380:
1381: return 0;
1382: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>