~ubuntu-branches/ubuntu/oneiric/jabberd2/oneiric-security

« back to all changes in this revision

Viewing changes to sx/scod/mech_digest_md5.c

  • Committer: Bazaar Package Importer
  • Author(s): Nicolai Spohrer
  • Date: 2008-08-12 16:13:43 UTC
  • mfrom: (1.1.3 upstream) (0.1.2 sid)
  • Revision ID: james.westby@ubuntu.com-20080812161343-6trz3r97dtevxd17
Tags: 2.2.1-1ubuntu1
* Merge with Debian unstable (LP: #257130), remaining changes:
  - debian/control:
    + Modify Maintainer field as per spec
    + Depend on libdb4.6-dev instead of libdb4.4-dev
    + Added Conflicts and Replaces: ..., jabber for jabberd2
  - debian/rules: Added libtoolize call (jabberd2 ships with
     an older ltmain.sh version that conflicts with the
     current libtool version)
  - debian/init: create /var/run/jabber directory with correct
     permissions
* Dropped changes:
  - Debian already depends on libpq-dev

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * scod - a minimal sasl implementation for jabberd2
 
3
 * Copyright (c) 2003 Robert Norris
 
4
 *
 
5
 * This program is free software; you can redistribute it and/or modify
 
6
 * it under the terms of the GNU General Public License as published by
 
7
 * the Free Software Foundation; either version 2 of the License, or
 
8
 * (at your option) any later version.
 
9
 *
 
10
 * This program is distributed in the hope that it will be useful,
 
11
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
12
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
 
13
 * GNU General Public License for more details.
 
14
 *
 
15
 * You should have received a copy of the GNU General Public License
 
16
 * along with this program; if not, write to the Free Software
 
17
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA02111-1307USA
 
18
 */
 
19
 
 
20
/* DIGEST-MD5 mechanism */
 
21
 
 
22
#include "scod.h"
 
23
 
 
24
#include <ctype.h>
 
25
 
 
26
#define HT  (9)
 
27
#define CR  (13)
 
28
#define LF  (10)
 
29
#define SP  (32)
 
30
#define DEL (127)
 
31
 
 
32
/* unions to comply with strict-alias rules for gcc3 */
 
33
union xhashv
 
34
{
 
35
  void **val;
 
36
  xht *xht_val;
 
37
};
 
38
 
 
39
union scod_u
 
40
{
 
41
  void **val;
 
42
  char **char_val;
 
43
};
 
44
 
 
45
static char *_opt_quote(char *in) {
 
46
    int nesc;
 
47
    char *r, *out, *w;
 
48
 
 
49
    r = in;
 
50
    nesc = 0;
 
51
    while(*r != '\0') {
 
52
        if(*r == '"' || *r == '\\')
 
53
            nesc++;
 
54
        r++;
 
55
    }
 
56
 
 
57
    out = (char *) malloc(sizeof(char) * (strlen(in) + nesc + 3));
 
58
 
 
59
    r = in;
 
60
    w = out;
 
61
 
 
62
    *w = '"';
 
63
    w++;
 
64
    while(*r != '\0') {
 
65
        if(*r == '"' || *r == '\\') {
 
66
            *w = '\\';
 
67
            w++;
 
68
        }
 
69
        *w = *r;
 
70
        w++;
 
71
        r++;
 
72
    }
 
73
 
 
74
    *w = '"';
 
75
    w++;
 
76
    *w = '\0';
 
77
 
 
78
    log_debug(ZONE, "escaped '%s' into '%s'", in, out);
 
79
 
 
80
    return out;
 
81
}
 
82
 
 
83
/** the list parser is based on code from cyrus-sasl. I love open source ;) */
 
84
static char *_opt_skip_lws(char *c) {
 
85
    if(c == NULL)
 
86
        return NULL;
 
87
 
 
88
    while(*c == ' ' || *c == HT || *c == CR || *c == LF) {
 
89
        if(*c == '\0')
 
90
            break;
 
91
        c++;
 
92
    }  
 
93
    
 
94
    return c;
 
95
}
 
96
 
 
97
static char *_opt_skip_token(char *c, int ci) {
 
98
    if(c == NULL)
 
99
        return NULL;
 
100
    
 
101
    while(*c > SP) {
 
102
        if(*c == DEL || *c == '(' || *c == ')' || *c == '<' || *c == '>' ||
 
103
           *c == '@' || *c == ',' || *c == ';' || *c == ':' || *c == '\\' ||
 
104
           *c == '\'' || *c == '/' || *c == '[' || *c == ']' || *c == '?' ||
 
105
           *c == '=' || *c == '{' || *c == '}') {
 
106
            if(ci) {
 
107
                if(!isupper((unsigned char) *c))
 
108
                break;
 
109
            } else
 
110
                break;
 
111
        }
 
112
        c++;
 
113
    }  
 
114
 
 
115
    return c;
 
116
}
 
117
 
 
118
static char *_opt_unquote(char *in) {
 
119
    char *out, *end;
 
120
    int esc = 0;
 
121
 
 
122
    /* if its not quoted, there's nothing to do */
 
123
    if(*in != '"')
 
124
        return _opt_skip_token(in, 0);
 
125
    
 
126
    in++;
 
127
    out = in;
 
128
    
 
129
    for(end = in; *end != '\0'; end++, out++) {
 
130
        if(esc) {
 
131
            *out = *end;
 
132
            esc = 0;
 
133
        }
 
134
        else if(*end == '\\') {
 
135
            esc = 1;
 
136
            out--;
 
137
        }
 
138
        else if(*end == '"')
 
139
            break;
 
140
        else
 
141
            *out = *end;      
 
142
    }
 
143
    
 
144
    if(*end != '"')
 
145
        return NULL;
 
146
    
 
147
    while(out <= end) {
 
148
        *out = '\0';
 
149
        out++;
 
150
    }
 
151
 
 
152
    end++;
 
153
    
 
154
    return end;  
 
155
 
156
 
 
157
static void _opt_get_pair(char **in, char **key, char **val) {
 
158
    char *end, *cur = *in;
 
159
 
 
160
    *key = NULL;
 
161
    *val = NULL;
 
162
    
 
163
    if(*cur == '\0')
 
164
        return;
 
165
    
 
166
    cur = _opt_skip_lws(cur);
 
167
    
 
168
    *key = cur;
 
169
    
 
170
    cur = _opt_skip_token(cur, 1);
 
171
    
 
172
    if(*cur != '=' && *cur != '\0') {
 
173
        *cur = '\0';
 
174
        cur++;
 
175
    }
 
176
    
 
177
    cur = _opt_skip_lws(cur);
 
178
    
 
179
    if(*cur != '=') {
 
180
        *key = NULL;
 
181
        return;
 
182
    }
 
183
    
 
184
    *cur = '\0';
 
185
    cur++;
 
186
    
 
187
    cur = _opt_skip_lws(cur);  
 
188
    
 
189
    *val = (*cur == '"') ? cur + 1 : cur;
 
190
    
 
191
    end = _opt_unquote(cur);
 
192
    if(end == NULL) {
 
193
        *key = NULL;
 
194
        return;
 
195
    }
 
196
    
 
197
    if(*end != ',' && *end != '\0') {
 
198
        *end = '\0';
 
199
        end++;
 
200
    }
 
201
    
 
202
    end = _opt_skip_lws(end);
 
203
    
 
204
    if(*end == ',') {
 
205
        *end = '\0';
 
206
        end++;
 
207
    }
 
208
    else if(*end != '\0') { 
 
209
        *key = NULL;
 
210
        return;
 
211
    }
 
212
    
 
213
    *in = end;
 
214
}
 
215
 
 
216
static xht _digest_md5_parse_options(const char *buf, int buflen) {
 
217
    xht hash, sub;
 
218
    char *nbuf, *in, *key, *val;
 
219
 
 
220
    nbuf = (char *) malloc(sizeof(char) * (buflen + 1));
 
221
    strncpy(nbuf, buf, buflen);
 
222
    nbuf[buflen] = '\0';
 
223
 
 
224
    hash = xhash_new(101);
 
225
 
 
226
    in = nbuf;
 
227
    while(1) {
 
228
        _opt_get_pair(&in, &key, &val);
 
229
        if(key == NULL)
 
230
            break;
 
231
 
 
232
        sub = xhash_get(hash, key);
 
233
        if(sub == NULL) {
 
234
            sub = xhash_new(11);
 
235
            xhash_put(hash, pstrdup(xhash_pool(hash), key), sub);
 
236
            pool_cleanup(xhash_pool(hash), (void (*)(void *)) xhash_free, sub);
 
237
        }
 
238
 
 
239
        xhash_put(sub, pstrdup(xhash_pool(hash), val), (void *) 1);
 
240
 
 
241
        log_debug(ZONE, "got key '%s' val '%s'", key, val);
 
242
    }
 
243
 
 
244
    free(nbuf);
 
245
 
 
246
    return hash;
 
247
}
 
248
 
 
249
static char *_digest_md5_gen_nonce(void) {
 
250
    int i, r;
 
251
    char nonce[65], hnonce[41];
 
252
 
 
253
    for(i = 0; i < 64; i++) {
 
254
        r = (int) (36.0 * rand() / RAND_MAX);
 
255
        nonce[i] = (r >= 0 && r <= 9) ? (r + 48) : (r + 87);
 
256
    }
 
257
    nonce[64] = '\0';
 
258
 
 
259
    shahash_r(nonce, hnonce);
 
260
 
 
261
    log_debug(ZONE, "generated nonce: %s", hnonce);
 
262
 
 
263
    return strdup(hnonce);
 
264
}
 
265
 
 
266
typedef struct _digest_md5_st {
 
267
    pool_t      p;
 
268
 
 
269
    char        *nonce;
 
270
    char        *cnonce;
 
271
    char        *nc;
 
272
 
 
273
    int         step;
 
274
} *digest_md5_t;
 
275
 
 
276
static int _digest_md5_client_start(scod_mech_t mech, scod_t sd, char **resp, int *resplen) {
 
277
    log_debug(ZONE, "DIGEST-MD5 client start");
 
278
 
 
279
    return sd_CONTINUE;
 
280
}
 
281
 
 
282
static int _digest_md5_client_step(scod_mech_t mech, scod_t sd, const char *chal, int challen, char **resp, int *resplen) {
 
283
    xht attrs, sub;
 
284
    char *key, *realm, *nonce, *qop, *charset, *algorithm, *cnonce, *c;
 
285
    md5_state_t md5;
 
286
    md5_byte_t hash[16];
 
287
    char ha1[33], ha2[33], hrsp[33];
 
288
    pool_t p;
 
289
    spool s;
 
290
    union xhashv xhv;
 
291
    union scod_u su;
 
292
 
 
293
    log_debug(ZONE, "DIGEST-MD5 client step; challenge: %.*s", challen, chal);
 
294
 
 
295
    if(sd->mech_data != NULL) {
 
296
        /* !!! check rspauth */
 
297
        sd->mech_data = NULL;
 
298
        return sd_SUCCESS;
 
299
    }
 
300
 
 
301
    realm = nonce = qop = charset = algorithm = NULL;
 
302
 
 
303
    attrs = _digest_md5_parse_options(chal, challen);
 
304
    if(xhash_iter_first(attrs))
 
305
        do {
 
306
            xhv.xht_val = &sub;
 
307
            xhash_iter_get(attrs, (const char **) &key, xhv.val);
 
308
            log_debug(ZONE, "extracting '%s'", key);
 
309
 
 
310
            if(xhash_iter_first(sub)) {
 
311
                if(strcmp(key, "realm") == 0) {
 
312
                    su.char_val = &realm;
 
313
                    (mech->ctx->cb)(sd, sd_cb_DIGEST_MD5_CHOOSE_REALM, (void *) sub, su.val, mech->ctx->cbarg);
 
314
                }
 
315
                else if(strcmp(key, "nonce") == 0)
 
316
                    xhash_iter_get(sub, (const char **) &nonce, NULL);
 
317
                else if(strcmp(key, "qop") == 0)
 
318
                    xhash_iter_get(sub, (const char **) &qop, NULL);
 
319
                else if(strcmp(key, "charset") == 0)
 
320
                    xhash_iter_get(sub, (const char **) &charset, NULL);
 
321
                else if(strcmp(key, "algorithm") == 0)
 
322
                    xhash_iter_get(sub, (const char **) &algorithm, NULL);
 
323
            }
 
324
        } while(xhash_iter_next(attrs));
 
325
 
 
326
    if(nonce == NULL || qop == NULL || charset == NULL || algorithm == NULL) {
 
327
        log_debug(ZONE, "missing attribute");
 
328
        xhash_free(attrs);
 
329
        return sd_auth_MALFORMED_DATA;
 
330
    }
 
331
 
 
332
    cnonce = _digest_md5_gen_nonce();
 
333
 
 
334
    md5_init(&md5);
 
335
    md5_append(&md5, sd->authnid, strlen(sd->authnid));
 
336
    md5_append(&md5, ":", 1);
 
337
    if(realm != NULL) md5_append(&md5, realm, strlen(realm));
 
338
    md5_append(&md5, ":", 1);
 
339
    md5_append(&md5, sd->pass, strlen(sd->pass));
 
340
    md5_finish(&md5, hash);
 
341
 
 
342
    md5_init(&md5);
 
343
    md5_append(&md5, hash, 16);
 
344
    md5_append(&md5, ":", 1);
 
345
    md5_append(&md5, nonce, strlen(nonce));
 
346
    md5_append(&md5, ":", 1);
 
347
    md5_append(&md5, cnonce, 40);
 
348
    if(sd->authzid != NULL) {
 
349
        md5_append(&md5, ":", 1);
 
350
        md5_append(&md5, sd->authzid, strlen(sd->authzid));
 
351
    }
 
352
    md5_finish(&md5, hash);                         /* A1 */
 
353
 
 
354
    hex_from_raw(hash, 16, ha1);
 
355
 
 
356
    log_debug(ZONE, "HEX(H(A1)) = %s", ha1);
 
357
 
 
358
    md5_init(&md5);
 
359
    md5_append(&md5, "AUTHENTICATE:", 13);
 
360
    md5_append(&md5, "xmpp/", 5);                   /* !!! make this configurable */
 
361
    md5_finish(&md5, hash);                         /* A2 */
 
362
 
 
363
    hex_from_raw(hash, 16, ha2);
 
364
 
 
365
    log_debug(ZONE, "HEX(H(A2)) = %s", ha2);
 
366
 
 
367
    md5_init(&md5);
 
368
    md5_append(&md5, ha1, 32);
 
369
    md5_append(&md5, ":", 1);
 
370
    md5_append(&md5, nonce, strlen(nonce));
 
371
    md5_append(&md5, ":", 1);
 
372
    md5_append(&md5, "00000001", 8);
 
373
    md5_append(&md5, ":", 1);
 
374
    md5_append(&md5, cnonce, 40);
 
375
    md5_append(&md5, ":auth:", 6);
 
376
    md5_append(&md5, ha2, 32);
 
377
    md5_finish(&md5, hash);                         /* KD(HA1, foo, HA2) */
 
378
 
 
379
    hex_from_raw(hash, 16, hrsp);
 
380
 
 
381
    log_debug(ZONE, "response is %s", hrsp);
 
382
 
 
383
    /* !!! generate rspauth and save it for later so we can validate */
 
384
 
 
385
    p = pool_new();
 
386
    s = spool_new(p);
 
387
 
 
388
    c = _opt_quote(sd->authnid);
 
389
    spooler(s, "username=", c, ",", s);
 
390
    free(c);
 
391
 
 
392
    c = _opt_quote(nonce);
 
393
    spooler(s, "nonce=", c, ",", s);
 
394
    free(c);
 
395
 
 
396
    c = _opt_quote(cnonce);
 
397
    spooler(s, "cnonce=", c, ",", s);
 
398
    free(c);
 
399
 
 
400
    if(sd->authzid != NULL) {
 
401
        c = _opt_quote(sd->authzid);
 
402
        spooler(s, "authzid=", c, ",", s);
 
403
        free(c);
 
404
    }
 
405
 
 
406
    if(realm != NULL) {
 
407
        c = _opt_quote(realm);
 
408
        spooler(s, "realm=", c, ",", s);
 
409
        free(c);
 
410
    }
 
411
 
 
412
    spooler(s, "nc=00000001,qop=auth,digest-uri=\"xmpp/\",charset=utf-8,response=", hrsp, s);
 
413
 
 
414
    *resp = strdup(spool_print(s));
 
415
    *resplen = strlen(*resp);
 
416
 
 
417
    pool_free(p);
 
418
    xhash_free(attrs);
 
419
 
 
420
    free(cnonce);
 
421
 
 
422
    log_debug(ZONE, "generated initial response: %.*s", *resplen, *resp);
 
423
 
 
424
    sd->mech_data = (void *) 1;
 
425
    
 
426
    return sd_CONTINUE;
 
427
}
 
428
 
 
429
static int _digest_md5_server_start(scod_mech_t mech, scod_t sd, const char *resp, int resplen, char **chal, int *challen) {
 
430
    digest_md5_t md;
 
431
    pool_t p;
 
432
    spool s;
 
433
    char *c, *nonce;
 
434
 
 
435
    log_debug(ZONE, "DIGEST-MD5 server start");
 
436
 
 
437
    p = pool_new();
 
438
    md = (digest_md5_t) pmalloco(p, sizeof(struct _digest_md5_st));
 
439
    md->p = p;
 
440
    sd->mech_data = md;
 
441
 
 
442
    p = pool_new();
 
443
    s = spool_new(p);
 
444
 
 
445
    if(sd->realm != NULL) {
 
446
        c = _opt_quote(sd->realm);
 
447
        spooler(s, "realm=", c, ",", s);
 
448
        free(c);
 
449
    }
 
450
 
 
451
    nonce = _digest_md5_gen_nonce();
 
452
    md->nonce = pstrdup(md->p, nonce);
 
453
    free(nonce);
 
454
 
 
455
    c = _opt_quote(md->nonce);
 
456
    spooler(s, "nonce=", c, ",qop=\"auth\",charset=utf-8,algorithm=md5-sess", s);
 
457
    free(c);
 
458
 
 
459
    *chal = strdup(spool_print(s));
 
460
    *challen = strlen(*chal);
 
461
 
 
462
    pool_free(p);
 
463
 
 
464
    log_debug(ZONE, "generated initial challenge: %.*s", *challen, *chal);
 
465
 
 
466
    return sd_CONTINUE;
 
467
}
 
468
 
 
469
static int _digest_md5_server_step(scod_mech_t mech, scod_t sd, const char *resp, int resplen, char **chal, int *challen) {
 
470
    digest_md5_t md = (digest_md5_t) sd->mech_data;
 
471
    xht attrs, sub;
 
472
    char *key, *username, *realm, *nonce, *cnonce, *nc, *qop, *digest_uri, *response, *charset, *pass, buf[257], *c, authzid[3072];
 
473
    int err;
 
474
    md5_state_t md5;
 
475
    md5_byte_t hash[16];
 
476
    char ha1[33], ha2[33], hrsp[33];
 
477
    struct _scod_cb_creds_st creds;
 
478
    union xhashv xhv;
 
479
    union scod_u su;
 
480
 
 
481
    log_debug(ZONE, "DIGEST-MD5 server step; response: %.*s", resplen, resp);
 
482
 
 
483
    if(md->step == 1) {
 
484
        /* we're done */
 
485
        pool_free(md->p);
 
486
        sd->mech_data = NULL;
 
487
        return sd_SUCCESS;
 
488
    }
 
489
 
 
490
    username = realm = nonce = cnonce = nc = qop = digest_uri = response = charset = NULL;
 
491
    authzid[0] = '\0';
 
492
 
 
493
    attrs = _digest_md5_parse_options(resp, resplen);
 
494
    if(xhash_iter_first(attrs))
 
495
        do {
 
496
            xhv.xht_val = &sub;
 
497
            xhash_iter_get(attrs, (const char **) &key, xhv.val);
 
498
            log_debug(ZONE, "extracting '%s'", key);
 
499
 
 
500
            if(xhash_iter_first(sub)) {
 
501
                if(strcmp(key, "username") == 0)
 
502
                    xhash_iter_get(sub, (const char **) &username, NULL);
 
503
                else if(strcmp(key, "realm") == 0)
 
504
                    xhash_iter_get(sub, (const char **) &realm, NULL);
 
505
                else if(strcmp(key, "nonce") == 0)
 
506
                    xhash_iter_get(sub, (const char **) &nonce, NULL);
 
507
                else if(strcmp(key, "cnonce") == 0)
 
508
                    xhash_iter_get(sub, (const char **) &cnonce, NULL);
 
509
                else if(strcmp(key, "nc") == 0)
 
510
                    xhash_iter_get(sub, (const char **) &nc, NULL);
 
511
                else if(strcmp(key, "qop") == 0)
 
512
                    xhash_iter_get(sub, (const char **) &qop, NULL);
 
513
                else if(strcmp(key, "digest-uri") == 0)
 
514
                    xhash_iter_get(sub, (const char **) &digest_uri, NULL);
 
515
                else if(strcmp(key, "response") == 0)
 
516
                    xhash_iter_get(sub, (const char **) &response, NULL);
 
517
                else if(strcmp(key, "charset") == 0)
 
518
                    xhash_iter_get(sub, (const char **) &charset, NULL);
 
519
                else if(strcmp(key, "authzid") == 0) {
 
520
                    xhash_iter_get(sub, (const char **) &c, NULL);
 
521
                    strncpy(authzid, c, sizeof(authzid));
 
522
                }
 
523
            }
 
524
        } while(xhash_iter_next(attrs));
 
525
 
 
526
    err = sd_SUCCESS;
 
527
    if(username == NULL || nonce == NULL || cnonce == NULL || nc == NULL || qop == NULL || digest_uri == NULL || response == NULL)
 
528
        err = sd_auth_MALFORMED_DATA;
 
529
    else if(strcmp(nonce, md->nonce) != 0)
 
530
        err = sd_auth_MISMATCH;
 
531
    else if(strcmp(qop, "auth") != 0)
 
532
        err = sd_auth_NOT_OFFERED;
 
533
 
 
534
    if(err != sd_SUCCESS) {
 
535
        log_debug(ZONE, "returning error %d", err);
 
536
 
 
537
        xhash_free(attrs);
 
538
        pool_free(md->p);
 
539
        sd->mech_data = NULL;
 
540
 
 
541
        return err;
 
542
    }
 
543
 
 
544
    /* !!! verify realm? */
 
545
 
 
546
    creds.authnid = username;
 
547
    creds.realm = realm;
 
548
    creds.pass = NULL;
 
549
    pass = buf;
 
550
    su.char_val = &pass;
 
551
    if((mech->ctx->cb)(sd, sd_cb_GET_PASS, &creds, su.val, mech->ctx->cbarg) != 0) {
 
552
        log_debug(ZONE, "user not found (or some other error getting password), failing");
 
553
 
 
554
        xhash_free(attrs);
 
555
        pool_free(md->p);
 
556
        sd->mech_data = NULL;
 
557
 
 
558
        return sd_auth_USER_UNKNOWN;
 
559
    }
 
560
 
 
561
    md->cnonce = pstrdup(md->p, cnonce);
 
562
    md->nc = pstrdup(md->p, nc);
 
563
 
 
564
    md5_init(&md5);
 
565
    md5_append(&md5, username, strlen(username));
 
566
    md5_append(&md5, ":", 1);
 
567
    if(realm != NULL) md5_append(&md5, realm, strlen(realm));
 
568
    md5_append(&md5, ":", 1);
 
569
    if(pass != NULL) md5_append(&md5, pass, strlen(pass));
 
570
    md5_finish(&md5, hash);
 
571
 
 
572
    md5_init(&md5);
 
573
    md5_append(&md5, hash, 16);
 
574
    md5_append(&md5, ":", 1);
 
575
    md5_append(&md5, md->nonce, strlen(md->nonce));
 
576
    md5_append(&md5, ":", 1);
 
577
    md5_append(&md5, md->cnonce, strlen(md->cnonce));
 
578
    if(authzid[0] != '\0') {
 
579
        md5_append(&md5, ":", 1);
 
580
        md5_append(&md5, authzid, strlen(authzid));
 
581
    }
 
582
    md5_finish(&md5, hash);                         /* A1 */
 
583
 
 
584
    hex_from_raw(hash, 16, ha1);
 
585
 
 
586
    log_debug(ZONE, "HEX(H(A1)) = %s", ha1);
 
587
 
 
588
    md5_init(&md5);
 
589
    md5_append(&md5, "AUTHENTICATE:", 13);
 
590
    md5_append(&md5, digest_uri, strlen(digest_uri));
 
591
    md5_finish(&md5, hash);                         /* A2 */
 
592
 
 
593
    hex_from_raw(hash, 16, ha2);
 
594
 
 
595
    log_debug(ZONE, "HEX(H(A2)) = %s", ha2);
 
596
 
 
597
    md5_init(&md5);
 
598
    md5_append(&md5, ha1, 32);
 
599
    md5_append(&md5, ":", 1);
 
600
    md5_append(&md5, nonce, strlen(nonce));
 
601
    md5_append(&md5, ":", 1);
 
602
    md5_append(&md5, nc, strlen(nc));
 
603
    md5_append(&md5, ":", 1);
 
604
    md5_append(&md5, cnonce, strlen(cnonce));
 
605
    md5_append(&md5, ":auth:", 6);
 
606
    md5_append(&md5, ha2, 32);
 
607
    md5_finish(&md5, hash);                         /* KD(HA1, foo, HA2) */
 
608
 
 
609
    hex_from_raw(hash, 16, hrsp);
 
610
 
 
611
    log_debug(ZONE, "our response is %s, theirs is %s", hrsp, response);
 
612
 
 
613
    if(strcmp(hrsp, response) != 0) {
 
614
        log_debug(ZONE, "not matched, denied");
 
615
 
 
616
        xhash_free(attrs);
 
617
        pool_free(md->p);
 
618
        sd->mech_data = NULL;
 
619
 
 
620
        return sd_auth_AUTH_FAILED;
 
621
    }
 
622
 
 
623
    log_debug(ZONE, "matched, they're authenticated");
 
624
 
 
625
    creds.authnid = username;
 
626
    creds.realm = realm;
 
627
    creds.pass = NULL;
 
628
 
 
629
    creds.authzid = authzid;
 
630
 
 
631
    if((mech->ctx->cb)(sd, sd_cb_CHECK_AUTHZID, &creds, NULL, mech->ctx->cbarg) != 0) {
 
632
        log_debug(ZONE, "authzid is invalid (app policy said so)");
 
633
 
 
634
        xhash_free(attrs);
 
635
        pool_free(md->p);
 
636
        sd->mech_data = NULL;
 
637
 
 
638
        return sd_auth_AUTHZID_POLICY;
 
639
    }
 
640
 
 
641
    sd->authzid = strdup(creds.authzid);
 
642
 
 
643
    md5_init(&md5);
 
644
    md5_append(&md5, ":", 1);
 
645
    md5_append(&md5, digest_uri, strlen(digest_uri));
 
646
    md5_finish(&md5, hash);                         /* rspauth A2 */
 
647
 
 
648
    hex_from_raw(hash, 16, ha2);
 
649
 
 
650
    log_debug(ZONE, "HEX(H(rspauth A2)) = %s", ha2);
 
651
 
 
652
    md5_init(&md5);
 
653
    md5_append(&md5, ha1, 32);
 
654
    md5_append(&md5, ":", 1);
 
655
    md5_append(&md5, nonce, strlen(nonce));
 
656
    md5_append(&md5, ":", 1);
 
657
    md5_append(&md5, nc, strlen(nc));
 
658
    md5_append(&md5, ":", 1);
 
659
    md5_append(&md5, cnonce, strlen(cnonce));
 
660
    md5_append(&md5, ":auth:", 6);
 
661
    md5_append(&md5, ha2, 32);
 
662
    md5_finish(&md5, hash);                         /* KD(HA1, foo, HA2) */
 
663
 
 
664
    hex_from_raw(hash, 16, hrsp);
 
665
 
 
666
    log_debug(ZONE, "rspauth: %s", hrsp);
 
667
 
 
668
    *chal = (char *) malloc(sizeof(char) * 41);
 
669
    snprintf(*chal, 41, "rspauth=%s", hrsp);
 
670
    *challen = 40;
 
671
 
 
672
    log_debug(ZONE, "generated final challenge: %.*s", *challen, *chal);
 
673
 
 
674
    md->step = 1;
 
675
    
 
676
    xhash_free(attrs);
 
677
 
 
678
    return sd_CONTINUE;
 
679
}
 
680
 
 
681
static void _digest_md5_free(scod_mech_t mech) {
 
682
    xhash_free((xht) mech->private);
 
683
}
 
684
 
 
685
int scod_mech_digest_md5_init(scod_mech_t mech) {
 
686
    log_debug(ZONE, "initialising DIGEST-MD5 mechanism");
 
687
 
 
688
    mech->name = "DIGEST-MD5";
 
689
 
 
690
    mech->flags = sd_flag_GET_PASS;
 
691
 
 
692
    mech->client_start = _digest_md5_client_start;
 
693
    mech->client_step = _digest_md5_client_step;
 
694
    mech->server_start = _digest_md5_server_start;
 
695
    mech->server_step = _digest_md5_server_step;
 
696
    mech->free = _digest_md5_free;
 
697
 
 
698
    return 0;
 
699
}