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

« back to all changes in this revision

Viewing changes to 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        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 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 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
 
}