~ubuntu-branches/ubuntu/quantal/lighttpd/quantal

« back to all changes in this revision

Viewing changes to .pc/CVE-2011-4362.patch/src/http_auth.c

  • Committer: Package Import Robot
  • Author(s): Mahyuddin Susanto
  • Date: 2011-12-20 17:32:22 UTC
  • Revision ID: package-import@ubuntu.com-20111220173222-a55k0846zbd6pxzo
Tags: 1.4.28-2ubuntu4
* debian/patches/CVE-2011-4362.patch: Fix DoS because of incorrect code in
  src/http_auth.c:67 (LP: #906792)
  - CVE-2011-4362

Show diffs side-by-side

added added

removed removed

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