~zulcss/samba/server-dailies-3.4

« back to all changes in this revision

Viewing changes to source4/heimdal/kdc/digest.c

  • Committer: Chuck Short
  • Date: 2010-09-28 20:38:39 UTC
  • Revision ID: zulcss@ubuntu.com-20100928203839-pgjulytsi9ue63x1
Initial version

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * Copyright (c) 2006 - 2007 Kungliga Tekniska Högskolan
 
3
 * (Royal Institute of Technology, Stockholm, Sweden).
 
4
 * All rights reserved.
 
5
 *
 
6
 * Redistribution and use in source and binary forms, with or without
 
7
 * modification, are permitted provided that the following conditions
 
8
 * are met:
 
9
 *
 
10
 * 1. Redistributions of source code must retain the above copyright
 
11
 *    notice, this list of conditions and the following disclaimer.
 
12
 *
 
13
 * 2. Redistributions in binary form must reproduce the above copyright
 
14
 *    notice, this list of conditions and the following disclaimer in the
 
15
 *    documentation and/or other materials provided with the distribution.
 
16
 *
 
17
 * 3. Neither the name of the Institute nor the names of its contributors
 
18
 *    may be used to endorse or promote products derived from this software
 
19
 *    without specific prior written permission.
 
20
 *
 
21
 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
 
22
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 
23
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 
24
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
 
25
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 
26
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 
27
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 
28
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 
29
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 
30
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 
31
 * SUCH DAMAGE.
 
32
 */
 
33
 
 
34
#include "kdc_locl.h"
 
35
#include <hex.h>
 
36
 
 
37
RCSID("$Id$");
 
38
 
 
39
#define MS_CHAP_V2      0x20
 
40
#define CHAP_MD5        0x10
 
41
#define DIGEST_MD5      0x08
 
42
#define NTLM_V2         0x04
 
43
#define NTLM_V1_SESSION 0x02
 
44
#define NTLM_V1         0x01
 
45
 
 
46
const struct units _kdc_digestunits[] = {
 
47
    {"ms-chap-v2",              1U << 5},
 
48
    {"chap-md5",                1U << 4},
 
49
    {"digest-md5",              1U << 3},
 
50
    {"ntlm-v2",         1U << 2},
 
51
    {"ntlm-v1-session", 1U << 1},
 
52
    {"ntlm-v1",         1U << 0},
 
53
    {NULL,      0}
 
54
};
 
55
 
 
56
 
 
57
static krb5_error_code
 
58
get_digest_key(krb5_context context,
 
59
               krb5_kdc_configuration *config,
 
60
               hdb_entry_ex *server,
 
61
               krb5_crypto *crypto)
 
62
{
 
63
    krb5_error_code ret;
 
64
    krb5_enctype enctype;
 
65
    Key *key;
 
66
 
 
67
    ret = _kdc_get_preferred_key(context,
 
68
                                 config,
 
69
                                 server,
 
70
                                 "digest-service",
 
71
                                 &enctype,
 
72
                                 &key);
 
73
    if (ret)
 
74
        return ret;
 
75
    return krb5_crypto_init(context, &key->key, 0, crypto);
 
76
}
 
77
 
 
78
/*
 
79
 *
 
80
 */
 
81
 
 
82
static char *
 
83
get_ntlm_targetname(krb5_context context,
 
84
                    hdb_entry_ex *client)
 
85
{
 
86
    char *targetname, *p;
 
87
 
 
88
    targetname = strdup(krb5_principal_get_realm(context,
 
89
                                                 client->entry.principal));
 
90
    if (targetname == NULL)
 
91
        return NULL;
 
92
 
 
93
    p = strchr(targetname, '.');
 
94
    if (p)
 
95
        *p = '\0';
 
96
 
 
97
    strupr(targetname);
 
98
    return targetname;
 
99
}
 
100
 
 
101
static krb5_error_code
 
102
fill_targetinfo(krb5_context context,
 
103
                char *targetname,
 
104
                hdb_entry_ex *client,
 
105
                krb5_data *data)
 
106
{
 
107
    struct ntlm_targetinfo ti;
 
108
    krb5_error_code ret;
 
109
    struct ntlm_buf d;
 
110
    krb5_principal p;
 
111
    const char *str;
 
112
 
 
113
    memset(&ti, 0, sizeof(ti));
 
114
 
 
115
    ti.domainname = targetname;
 
116
    p = client->entry.principal;
 
117
    str = krb5_principal_get_comp_string(context, p, 0);
 
118
    if (str != NULL &&
 
119
        (strcmp("host", str) == 0 ||
 
120
         strcmp("ftp", str) == 0 ||
 
121
         strcmp("imap", str) == 0 ||
 
122
         strcmp("pop", str) == 0 ||
 
123
         strcmp("smtp", str)))
 
124
        {
 
125
            str = krb5_principal_get_comp_string(context, p, 1);
 
126
            ti.dnsservername = rk_UNCONST(str);
 
127
        }
 
128
 
 
129
    ret = heim_ntlm_encode_targetinfo(&ti, 1, &d);
 
130
    if (ret)
 
131
        return ret;
 
132
 
 
133
    data->data = d.data;
 
134
    data->length = d.length;
 
135
 
 
136
    return 0;
 
137
}
 
138
 
 
139
 
 
140
static const unsigned char ms_chap_v2_magic1[39] = {
 
141
    0x4D, 0x61, 0x67, 0x69, 0x63, 0x20, 0x73, 0x65, 0x72, 0x76,
 
142
    0x65, 0x72, 0x20, 0x74, 0x6F, 0x20, 0x63, 0x6C, 0x69, 0x65,
 
143
    0x6E, 0x74, 0x20, 0x73, 0x69, 0x67, 0x6E, 0x69, 0x6E, 0x67,
 
144
    0x20, 0x63, 0x6F, 0x6E, 0x73, 0x74, 0x61, 0x6E, 0x74
 
145
};
 
146
static const unsigned char ms_chap_v2_magic2[41] = {
 
147
    0x50, 0x61, 0x64, 0x20, 0x74, 0x6F, 0x20, 0x6D, 0x61, 0x6B,
 
148
    0x65, 0x20, 0x69, 0x74, 0x20, 0x64, 0x6F, 0x20, 0x6D, 0x6F,
 
149
    0x72, 0x65, 0x20, 0x74, 0x68, 0x61, 0x6E, 0x20, 0x6F, 0x6E,
 
150
    0x65, 0x20, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6F,
 
151
    0x6E
 
152
};
 
153
static const unsigned char ms_rfc3079_magic1[27] = {
 
154
    0x54, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74,
 
155
    0x68, 0x65, 0x20, 0x4d, 0x50, 0x50, 0x45, 0x20, 0x4d,
 
156
    0x61, 0x73, 0x74, 0x65, 0x72, 0x20, 0x4b, 0x65, 0x79
 
157
};
 
158
 
 
159
/*
 
160
 *
 
161
 */
 
162
 
 
163
static krb5_error_code
 
164
get_password_entry(krb5_context context,
 
165
                   krb5_kdc_configuration *config,
 
166
                   const char *username,
 
167
                   char **password)
 
168
{
 
169
    krb5_principal clientprincipal;
 
170
    krb5_error_code ret;
 
171
    hdb_entry_ex *user;
 
172
    HDB *db;
 
173
 
 
174
    /* get username */
 
175
    ret = krb5_parse_name(context, username, &clientprincipal);
 
176
    if (ret)
 
177
        return ret;
 
178
 
 
179
    ret = _kdc_db_fetch(context, config, clientprincipal,
 
180
                        HDB_F_GET_CLIENT, &db, &user);
 
181
    krb5_free_principal(context, clientprincipal);
 
182
    if (ret)
 
183
        return ret;
 
184
 
 
185
    ret = hdb_entry_get_password(context, db, &user->entry, password);
 
186
    if (ret || password == NULL) {
 
187
        if (ret == 0) {
 
188
            ret = EINVAL;
 
189
            krb5_set_error_message(context, ret, "password missing");
 
190
        }
 
191
        memset(user, 0, sizeof(*user));
 
192
    }
 
193
    _kdc_free_ent (context, user);
 
194
    return ret;
 
195
}
 
196
 
 
197
/*
 
198
 *
 
199
 */
 
200
 
 
201
krb5_error_code
 
202
_kdc_do_digest(krb5_context context,
 
203
               krb5_kdc_configuration *config,
 
204
               const DigestREQ *req, krb5_data *reply,
 
205
               const char *from, struct sockaddr *addr)
 
206
{
 
207
    krb5_error_code ret = 0;
 
208
    krb5_ticket *ticket = NULL;
 
209
    krb5_auth_context ac = NULL;
 
210
    krb5_keytab id = NULL;
 
211
    krb5_crypto crypto = NULL;
 
212
    DigestReqInner ireq;
 
213
    DigestRepInner r;
 
214
    DigestREP rep;
 
215
    krb5_flags ap_req_options;
 
216
    krb5_data buf;
 
217
    size_t size;
 
218
    krb5_storage *sp = NULL;
 
219
    Checksum res;
 
220
    hdb_entry_ex *server = NULL, *user = NULL;
 
221
    hdb_entry_ex *client = NULL;
 
222
    char *client_name = NULL, *password = NULL;
 
223
    krb5_data serverNonce;
 
224
 
 
225
    if(!config->enable_digest) {
 
226
        kdc_log(context, config, 0,
 
227
                "Rejected digest request (disabled) from %s", from);
 
228
        return KRB5KDC_ERR_POLICY;
 
229
    }
 
230
 
 
231
    krb5_data_zero(&buf);
 
232
    krb5_data_zero(reply);
 
233
    krb5_data_zero(&serverNonce);
 
234
    memset(&ireq, 0, sizeof(ireq));
 
235
    memset(&r, 0, sizeof(r));
 
236
    memset(&rep, 0, sizeof(rep));
 
237
 
 
238
    kdc_log(context, config, 0, "Digest request from %s", from);
 
239
 
 
240
    ret = krb5_kt_resolve(context, "HDB:", &id);
 
241
    if (ret) {
 
242
        kdc_log(context, config, 0, "Can't open database for digest");
 
243
        goto out;
 
244
    }
 
245
 
 
246
    ret = krb5_rd_req(context,
 
247
                      &ac,
 
248
                      &req->apReq,
 
249
                      NULL,
 
250
                      id,
 
251
                      &ap_req_options,
 
252
                      &ticket);
 
253
    if (ret)
 
254
        goto out;
 
255
 
 
256
    /* check the server principal in the ticket matches digest/R@R */
 
257
    {
 
258
        krb5_principal principal = NULL;
 
259
        const char *p, *r;
 
260
 
 
261
        ret = krb5_ticket_get_server(context, ticket, &principal);
 
262
        if (ret)
 
263
            goto out;
 
264
 
 
265
        ret = EINVAL;
 
266
        krb5_set_error_message(context, ret, "Wrong digest server principal used");
 
267
        p = krb5_principal_get_comp_string(context, principal, 0);
 
268
        if (p == NULL) {
 
269
            krb5_free_principal(context, principal);
 
270
            goto out;
 
271
        }
 
272
        if (strcmp(p, KRB5_DIGEST_NAME) != 0) {
 
273
            krb5_free_principal(context, principal);
 
274
            goto out;
 
275
        }
 
276
 
 
277
        p = krb5_principal_get_comp_string(context, principal, 1);
 
278
        if (p == NULL) {
 
279
            krb5_free_principal(context, principal);
 
280
            goto out;
 
281
        }
 
282
        r = krb5_principal_get_realm(context, principal);
 
283
        if (r == NULL) {
 
284
            krb5_free_principal(context, principal);
 
285
            goto out;
 
286
        }
 
287
        if (strcmp(p, r) != 0) {
 
288
            krb5_free_principal(context, principal);
 
289
            goto out;
 
290
        }
 
291
        krb5_clear_error_message(context);
 
292
 
 
293
        ret = _kdc_db_fetch(context, config, principal,
 
294
                            HDB_F_GET_SERVER, NULL, &server);
 
295
        if (ret)
 
296
            goto out;
 
297
 
 
298
        krb5_free_principal(context, principal);
 
299
    }
 
300
 
 
301
    /* check the client is allowed to do digest auth */
 
302
    {
 
303
        krb5_principal principal = NULL;
 
304
 
 
305
        ret = krb5_ticket_get_client(context, ticket, &principal);
 
306
        if (ret)
 
307
            goto out;
 
308
 
 
309
        ret = krb5_unparse_name(context, principal, &client_name);
 
310
        if (ret) {
 
311
            krb5_free_principal(context, principal);
 
312
            goto out;
 
313
        }
 
314
 
 
315
        ret = _kdc_db_fetch(context, config, principal,
 
316
                            HDB_F_GET_CLIENT, NULL, &client);
 
317
        krb5_free_principal(context, principal);
 
318
        if (ret)
 
319
            goto out;
 
320
 
 
321
        if (client->entry.flags.allow_digest == 0) {
 
322
            kdc_log(context, config, 0,
 
323
                    "Client %s tried to use digest "
 
324
                    "but is not allowed to",
 
325
                    client_name);
 
326
            ret = KRB5KDC_ERR_POLICY;
 
327
            krb5_set_error_message(context, ret,
 
328
                                   "Client is not permitted to use digest");
 
329
            goto out;
 
330
        }
 
331
    }
 
332
 
 
333
    /* unpack request */
 
334
    {
 
335
        krb5_keyblock *key;
 
336
 
 
337
        ret = krb5_auth_con_getremotesubkey(context, ac, &key);
 
338
        if (ret)
 
339
            goto out;
 
340
        if (key == NULL) {
 
341
            ret = EINVAL;
 
342
            krb5_set_error_message(context, ret, "digest: remote subkey not found");
 
343
            goto out;
 
344
        }
 
345
 
 
346
        ret = krb5_crypto_init(context, key, 0, &crypto);
 
347
        krb5_free_keyblock (context, key);
 
348
        if (ret)
 
349
            goto out;
 
350
    }
 
351
 
 
352
    ret = krb5_decrypt_EncryptedData(context, crypto, KRB5_KU_DIGEST_ENCRYPT,
 
353
                                     &req->innerReq, &buf);
 
354
    krb5_crypto_destroy(context, crypto);
 
355
    crypto = NULL;
 
356
    if (ret)
 
357
        goto out;
 
358
        
 
359
    ret = decode_DigestReqInner(buf.data, buf.length, &ireq, NULL);
 
360
    krb5_data_free(&buf);
 
361
    if (ret) {
 
362
        krb5_set_error_message(context, ret, "Failed to decode digest inner request");
 
363
        goto out;
 
364
    }
 
365
 
 
366
    kdc_log(context, config, 0, "Valid digest request from %s (%s)",
 
367
            client_name, from);
 
368
 
 
369
    /*
 
370
     * Process the inner request
 
371
     */
 
372
 
 
373
    switch (ireq.element) {
 
374
    case choice_DigestReqInner_init: {
 
375
        unsigned char server_nonce[16], identifier;
 
376
 
 
377
        RAND_pseudo_bytes(&identifier, sizeof(identifier));
 
378
        RAND_pseudo_bytes(server_nonce, sizeof(server_nonce));
 
379
 
 
380
        server_nonce[0] = kdc_time & 0xff;
 
381
        server_nonce[1] = (kdc_time >> 8) & 0xff;
 
382
        server_nonce[2] = (kdc_time >> 16) & 0xff;
 
383
        server_nonce[3] = (kdc_time >> 24) & 0xff;
 
384
 
 
385
        r.element = choice_DigestRepInner_initReply;
 
386
 
 
387
        hex_encode(server_nonce, sizeof(server_nonce), &r.u.initReply.nonce);
 
388
        if (r.u.initReply.nonce == NULL) {
 
389
            ret = ENOMEM;
 
390
            krb5_set_error_message(context, ret, "Failed to decode server nonce");
 
391
            goto out;
 
392
        }
 
393
 
 
394
        sp = krb5_storage_emem();
 
395
        if (sp == NULL) {
 
396
            ret = ENOMEM;
 
397
            krb5_set_error_message(context, ret, "malloc: out of memory");
 
398
            goto out;
 
399
        }
 
400
        ret = krb5_store_stringz(sp, ireq.u.init.type);
 
401
        if (ret) {
 
402
            krb5_clear_error_message(context);
 
403
            goto out;
 
404
        }
 
405
 
 
406
        if (ireq.u.init.channel) {
 
407
            char *s;
 
408
 
 
409
            asprintf(&s, "%s-%s:%s", r.u.initReply.nonce,
 
410
                     ireq.u.init.channel->cb_type,
 
411
                     ireq.u.init.channel->cb_binding);
 
412
            if (s == NULL) {
 
413
                ret = ENOMEM;
 
414
                krb5_set_error_message(context, ret,
 
415
                                       "Failed to allocate channel binding");
 
416
                goto out;
 
417
            }
 
418
            free(r.u.initReply.nonce);
 
419
            r.u.initReply.nonce = s;
 
420
        }
 
421
        
 
422
        ret = krb5_store_stringz(sp, r.u.initReply.nonce);
 
423
        if (ret) {
 
424
            krb5_clear_error_message(context);
 
425
            goto out;
 
426
        }
 
427
 
 
428
        if (strcasecmp(ireq.u.init.type, "CHAP") == 0) {
 
429
            r.u.initReply.identifier =
 
430
                malloc(sizeof(*r.u.initReply.identifier));
 
431
            if (r.u.initReply.identifier == NULL) {
 
432
                ret = ENOMEM;
 
433
                krb5_set_error_message(context, ret, "malloc: out of memory");
 
434
                goto out;
 
435
            }
 
436
 
 
437
            asprintf(r.u.initReply.identifier, "%02X", identifier & 0xff);
 
438
            if (*r.u.initReply.identifier == NULL) {
 
439
                ret = ENOMEM;
 
440
                krb5_set_error_message(context, ret, "malloc: out of memory");
 
441
                goto out;
 
442
            }
 
443
 
 
444
        } else
 
445
            r.u.initReply.identifier = NULL;
 
446
 
 
447
        if (ireq.u.init.hostname) {
 
448
            ret = krb5_store_stringz(sp, *ireq.u.init.hostname);
 
449
            if (ret) {
 
450
                krb5_clear_error_message(context);
 
451
                goto out;
 
452
            }
 
453
        }
 
454
 
 
455
        ret = krb5_storage_to_data(sp, &buf);
 
456
        if (ret) {
 
457
            krb5_clear_error_message(context);
 
458
            goto out;
 
459
        }
 
460
 
 
461
        ret = get_digest_key(context, config, server, &crypto);
 
462
        if (ret)
 
463
            goto out;
 
464
 
 
465
        ret = krb5_create_checksum(context,
 
466
                                   crypto,
 
467
                                   KRB5_KU_DIGEST_OPAQUE,
 
468
                                   0,
 
469
                                   buf.data,
 
470
                                   buf.length,
 
471
                                   &res);
 
472
        krb5_crypto_destroy(context, crypto);
 
473
        crypto = NULL;
 
474
        krb5_data_free(&buf);
 
475
        if (ret)
 
476
            goto out;
 
477
        
 
478
        ASN1_MALLOC_ENCODE(Checksum, buf.data, buf.length, &res, &size, ret);
 
479
        free_Checksum(&res);
 
480
        if (ret) {
 
481
            krb5_set_error_message(context, ret, "Failed to encode "
 
482
                                   "checksum in digest request");
 
483
            goto out;
 
484
        }
 
485
        if (size != buf.length)
 
486
            krb5_abortx(context, "ASN1 internal error");
 
487
 
 
488
        hex_encode(buf.data, buf.length, &r.u.initReply.opaque);
 
489
        free(buf.data);
 
490
        if (r.u.initReply.opaque == NULL) {
 
491
            krb5_clear_error_message(context);
 
492
            ret = ENOMEM;
 
493
            goto out;
 
494
        }
 
495
 
 
496
        kdc_log(context, config, 0, "Digest %s init request successful from %s",
 
497
                ireq.u.init.type, from);
 
498
 
 
499
        break;
 
500
    }
 
501
    case choice_DigestReqInner_digestRequest: {
 
502
        sp = krb5_storage_emem();
 
503
        if (sp == NULL) {
 
504
            ret = ENOMEM;
 
505
            krb5_set_error_message(context, ret, "malloc: out of memory");
 
506
            goto out;
 
507
        }
 
508
        ret = krb5_store_stringz(sp, ireq.u.digestRequest.type);
 
509
        if (ret) {
 
510
            krb5_clear_error_message(context);
 
511
            goto out;
 
512
        }
 
513
 
 
514
        krb5_store_stringz(sp, ireq.u.digestRequest.serverNonce);
 
515
 
 
516
        if (ireq.u.digestRequest.hostname) {
 
517
            ret = krb5_store_stringz(sp, *ireq.u.digestRequest.hostname);
 
518
            if (ret) {
 
519
                krb5_clear_error_message(context);
 
520
                goto out;
 
521
            }
 
522
        }
 
523
 
 
524
        buf.length = strlen(ireq.u.digestRequest.opaque);
 
525
        buf.data = malloc(buf.length);
 
526
        if (buf.data == NULL) {
 
527
            ret = ENOMEM;
 
528
            krb5_set_error_message(context, ret, "malloc: out of memory");
 
529
            goto out;
 
530
        }
 
531
 
 
532
        ret = hex_decode(ireq.u.digestRequest.opaque, buf.data, buf.length);
 
533
        if (ret <= 0) {
 
534
            ret = ENOMEM;
 
535
            krb5_set_error_message(context, ret, "Failed to decode opaque");
 
536
            goto out;
 
537
        }
 
538
        buf.length = ret;
 
539
 
 
540
        ret = decode_Checksum(buf.data, buf.length, &res, NULL);
 
541
        free(buf.data);
 
542
        if (ret) {
 
543
            krb5_set_error_message(context, ret, "Failed to decode digest Checksum");
 
544
            goto out;
 
545
        }
 
546
        
 
547
        ret = krb5_storage_to_data(sp, &buf);
 
548
        if (ret) {
 
549
            krb5_clear_error_message(context);
 
550
            goto out;
 
551
        }
 
552
 
 
553
        serverNonce.length = strlen(ireq.u.digestRequest.serverNonce);
 
554
        serverNonce.data = malloc(serverNonce.length);
 
555
        if (serverNonce.data == NULL) {
 
556
            ret = ENOMEM;
 
557
            krb5_set_error_message(context, ret, "malloc: out of memory");
 
558
            goto out;
 
559
        }
 
560
        
 
561
        /*
 
562
         * CHAP does the checksum of the raw nonce, but do it for all
 
563
         * types, since we need to check the timestamp.
 
564
         */
 
565
        {
 
566
            ssize_t ssize;
 
567
        
 
568
            ssize = hex_decode(ireq.u.digestRequest.serverNonce,
 
569
                               serverNonce.data, serverNonce.length);
 
570
            if (ssize <= 0) {
 
571
                ret = ENOMEM;
 
572
                krb5_set_error_message(context, ret, "Failed to decode serverNonce");
 
573
                goto out;
 
574
            }
 
575
            serverNonce.length = ssize;
 
576
        }
 
577
 
 
578
        ret = get_digest_key(context, config, server, &crypto);
 
579
        if (ret)
 
580
            goto out;
 
581
 
 
582
        ret = krb5_verify_checksum(context, crypto,
 
583
                                   KRB5_KU_DIGEST_OPAQUE,
 
584
                                   buf.data, buf.length, &res);
 
585
        krb5_crypto_destroy(context, crypto);
 
586
        crypto = NULL;
 
587
        if (ret)
 
588
            goto out;
 
589
 
 
590
        /* verify time */
 
591
        {
 
592
            unsigned char *p = serverNonce.data;
 
593
            uint32_t t;
 
594
        
 
595
            if (serverNonce.length < 4) {
 
596
                ret = EINVAL;
 
597
                krb5_set_error_message(context, ret, "server nonce too short");
 
598
                goto out;
 
599
            }
 
600
            t = p[0] | (p[1] << 8) | (p[2] << 16) | (p[3] << 24);
 
601
 
 
602
            if (abs((kdc_time & 0xffffffff) - t) > context->max_skew) {
 
603
                ret = EINVAL;
 
604
                krb5_set_error_message(context, ret, "time screw in server nonce ");
 
605
                goto out;
 
606
            }
 
607
        }
 
608
 
 
609
        if (strcasecmp(ireq.u.digestRequest.type, "CHAP") == 0) {
 
610
            MD5_CTX ctx;
 
611
            unsigned char md[MD5_DIGEST_LENGTH];
 
612
            char *mdx;
 
613
            char id;
 
614
 
 
615
            if ((config->digests_allowed & CHAP_MD5) == 0) {
 
616
                kdc_log(context, config, 0, "Digest CHAP MD5 not allowed");
 
617
                goto out;
 
618
            }
 
619
 
 
620
            if (ireq.u.digestRequest.identifier == NULL) {
 
621
                ret = EINVAL;
 
622
                krb5_set_error_message(context, ret, "Identifier missing "
 
623
                                       "from CHAP request");
 
624
                goto out;
 
625
            }
 
626
        
 
627
            if (hex_decode(*ireq.u.digestRequest.identifier, &id, 1) != 1) {
 
628
                ret = EINVAL;
 
629
                krb5_set_error_message(context, ret, "failed to decode identifier");
 
630
                goto out;
 
631
            }
 
632
        
 
633
            ret = get_password_entry(context, config,
 
634
                                     ireq.u.digestRequest.username,
 
635
                                     &password);
 
636
            if (ret)
 
637
                goto out;
 
638
 
 
639
            MD5_Init(&ctx);
 
640
            MD5_Update(&ctx, &id, 1);
 
641
            MD5_Update(&ctx, password, strlen(password));
 
642
            MD5_Update(&ctx, serverNonce.data, serverNonce.length);
 
643
            MD5_Final(md, &ctx);
 
644
 
 
645
            hex_encode(md, sizeof(md), &mdx);
 
646
            if (mdx == NULL) {
 
647
                krb5_clear_error_message(context);
 
648
                ret = ENOMEM;
 
649
                goto out;
 
650
            }
 
651
 
 
652
            r.element = choice_DigestRepInner_response;
 
653
 
 
654
            ret = strcasecmp(mdx, ireq.u.digestRequest.responseData);
 
655
            free(mdx);
 
656
            if (ret == 0) {
 
657
                r.u.response.success = TRUE;
 
658
            } else {
 
659
                kdc_log(context, config, 0,
 
660
                        "CHAP reply mismatch for %s",
 
661
                        ireq.u.digestRequest.username);
 
662
                r.u.response.success = FALSE;
 
663
            }
 
664
 
 
665
        } else if (strcasecmp(ireq.u.digestRequest.type, "SASL-DIGEST-MD5") == 0) {
 
666
            MD5_CTX ctx;
 
667
            unsigned char md[MD5_DIGEST_LENGTH];
 
668
            char *mdx;
 
669
            char *A1, *A2;
 
670
 
 
671
            if ((config->digests_allowed & DIGEST_MD5) == 0) {
 
672
                kdc_log(context, config, 0, "Digest SASL MD5 not allowed");
 
673
                goto out;
 
674
            }
 
675
 
 
676
            if (ireq.u.digestRequest.nonceCount == NULL)
 
677
                goto out;
 
678
            if (ireq.u.digestRequest.clientNonce == NULL)
 
679
                goto out;
 
680
            if (ireq.u.digestRequest.qop == NULL)
 
681
                goto out;
 
682
            if (ireq.u.digestRequest.realm == NULL)
 
683
                goto out;
 
684
        
 
685
            ret = get_password_entry(context, config,
 
686
                                     ireq.u.digestRequest.username,
 
687
                                     &password);
 
688
            if (ret)
 
689
                goto failed;
 
690
 
 
691
            MD5_Init(&ctx);
 
692
            MD5_Update(&ctx, ireq.u.digestRequest.username,
 
693
                       strlen(ireq.u.digestRequest.username));
 
694
            MD5_Update(&ctx, ":", 1);
 
695
            MD5_Update(&ctx, *ireq.u.digestRequest.realm,
 
696
                       strlen(*ireq.u.digestRequest.realm));
 
697
            MD5_Update(&ctx, ":", 1);
 
698
            MD5_Update(&ctx, password, strlen(password));
 
699
            MD5_Final(md, &ctx);
 
700
        
 
701
            MD5_Init(&ctx);
 
702
            MD5_Update(&ctx, md, sizeof(md));
 
703
            MD5_Update(&ctx, ":", 1);
 
704
            MD5_Update(&ctx, ireq.u.digestRequest.serverNonce,
 
705
                       strlen(ireq.u.digestRequest.serverNonce));
 
706
            MD5_Update(&ctx, ":", 1);
 
707
            MD5_Update(&ctx, *ireq.u.digestRequest.nonceCount,
 
708
                       strlen(*ireq.u.digestRequest.nonceCount));
 
709
            if (ireq.u.digestRequest.authid) {
 
710
                MD5_Update(&ctx, ":", 1);
 
711
                MD5_Update(&ctx, *ireq.u.digestRequest.authid,
 
712
                           strlen(*ireq.u.digestRequest.authid));
 
713
            }
 
714
            MD5_Final(md, &ctx);
 
715
            hex_encode(md, sizeof(md), &A1);
 
716
            if (A1 == NULL) {
 
717
                ret = ENOMEM;
 
718
                krb5_set_error_message(context, ret, "malloc: out of memory");
 
719
                goto failed;
 
720
            }
 
721
        
 
722
            MD5_Init(&ctx);
 
723
            MD5_Update(&ctx, "AUTHENTICATE:", sizeof("AUTHENTICATE:") - 1);
 
724
            MD5_Update(&ctx, *ireq.u.digestRequest.uri,
 
725
                       strlen(*ireq.u.digestRequest.uri));
 
726
        
 
727
            /* conf|int */
 
728
            if (strcmp(ireq.u.digestRequest.digest, "clear") != 0) {
 
729
                static char conf_zeros[] = ":00000000000000000000000000000000";
 
730
                MD5_Update(&ctx, conf_zeros, sizeof(conf_zeros) - 1);
 
731
            }
 
732
        
 
733
            MD5_Final(md, &ctx);
 
734
            hex_encode(md, sizeof(md), &A2);
 
735
            if (A2 == NULL) {
 
736
                ret = ENOMEM;
 
737
                krb5_set_error_message(context, ret, "malloc: out of memory");
 
738
                free(A1);
 
739
                goto failed;
 
740
            }
 
741
 
 
742
            MD5_Init(&ctx);
 
743
            MD5_Update(&ctx, A1, strlen(A2));
 
744
            MD5_Update(&ctx, ":", 1);
 
745
            MD5_Update(&ctx, ireq.u.digestRequest.serverNonce,
 
746
                       strlen(ireq.u.digestRequest.serverNonce));
 
747
            MD5_Update(&ctx, ":", 1);
 
748
            MD5_Update(&ctx, *ireq.u.digestRequest.nonceCount,
 
749
                       strlen(*ireq.u.digestRequest.nonceCount));
 
750
            MD5_Update(&ctx, ":", 1);
 
751
            MD5_Update(&ctx, *ireq.u.digestRequest.clientNonce,
 
752
                       strlen(*ireq.u.digestRequest.clientNonce));
 
753
            MD5_Update(&ctx, ":", 1);
 
754
            MD5_Update(&ctx, *ireq.u.digestRequest.qop,
 
755
                       strlen(*ireq.u.digestRequest.qop));
 
756
            MD5_Update(&ctx, ":", 1);
 
757
            MD5_Update(&ctx, A2, strlen(A2));
 
758
 
 
759
            MD5_Final(md, &ctx);
 
760
 
 
761
            free(A1);
 
762
            free(A2);
 
763
 
 
764
            hex_encode(md, sizeof(md), &mdx);
 
765
            if (mdx == NULL) {
 
766
                krb5_clear_error_message(context);
 
767
                ret = ENOMEM;
 
768
                goto out;
 
769
            }
 
770
 
 
771
            r.element = choice_DigestRepInner_response;
 
772
            ret = strcasecmp(mdx, ireq.u.digestRequest.responseData);
 
773
            free(mdx);
 
774
            if (ret == 0) {
 
775
                r.u.response.success = TRUE;
 
776
            } else {
 
777
                kdc_log(context, config, 0,
 
778
                        "DIGEST-MD5 reply mismatch for %s",
 
779
                        ireq.u.digestRequest.username);
 
780
                r.u.response.success = FALSE;
 
781
            }
 
782
 
 
783
        } else if (strcasecmp(ireq.u.digestRequest.type, "MS-CHAP-V2") == 0) {
 
784
            unsigned char md[SHA_DIGEST_LENGTH], challange[SHA_DIGEST_LENGTH];
 
785
            krb5_principal clientprincipal = NULL;
 
786
            char *mdx;
 
787
            const char *username;
 
788
            struct ntlm_buf answer;
 
789
            Key *key = NULL;
 
790
            SHA_CTX ctx;
 
791
 
 
792
            if ((config->digests_allowed & MS_CHAP_V2) == 0) {
 
793
                kdc_log(context, config, 0, "MS-CHAP-V2 not allowed");
 
794
                goto failed;
 
795
            }
 
796
 
 
797
            if (ireq.u.digestRequest.clientNonce == NULL)  {
 
798
                ret = EINVAL;
 
799
                krb5_set_error_message(context, ret,
 
800
                                       "MS-CHAP-V2 clientNonce missing");
 
801
                goto failed;
 
802
            }   
 
803
            if (serverNonce.length != 16) {
 
804
                ret = EINVAL;
 
805
                krb5_set_error_message(context, ret,
 
806
                                       "MS-CHAP-V2 serverNonce wrong length");
 
807
                goto failed;
 
808
            }
 
809
 
 
810
            /* strip of the domain component */
 
811
            username = strchr(ireq.u.digestRequest.username, '\\');
 
812
            if (username == NULL)
 
813
                username = ireq.u.digestRequest.username;
 
814
            else
 
815
                username++;
 
816
 
 
817
            /* ChallangeHash */
 
818
            SHA1_Init(&ctx);
 
819
            {
 
820
                ssize_t ssize;
 
821
                krb5_data clientNonce;
 
822
                
 
823
                clientNonce.length = strlen(*ireq.u.digestRequest.clientNonce);
 
824
                clientNonce.data = malloc(clientNonce.length);
 
825
                if (clientNonce.data == NULL) {
 
826
                    ret = ENOMEM;
 
827
                    krb5_set_error_message(context, ret, "malloc: out of memory");
 
828
                    goto out;
 
829
                }
 
830
 
 
831
                ssize = hex_decode(*ireq.u.digestRequest.clientNonce,
 
832
                                   clientNonce.data, clientNonce.length);
 
833
                if (ssize != 16) {
 
834
                    ret = ENOMEM;
 
835
                    krb5_set_error_message(context, ret,
 
836
                                           "Failed to decode clientNonce");
 
837
                    goto out;
 
838
                }
 
839
                SHA1_Update(&ctx, clientNonce.data, ssize);
 
840
                free(clientNonce.data);
 
841
            }
 
842
            SHA1_Update(&ctx, serverNonce.data, serverNonce.length);
 
843
            SHA1_Update(&ctx, username, strlen(username));
 
844
            SHA1_Final(challange, &ctx);
 
845
 
 
846
            /* NtPasswordHash */
 
847
            ret = krb5_parse_name(context, username, &clientprincipal);
 
848
            if (ret)
 
849
                goto failed;
 
850
        
 
851
            ret = _kdc_db_fetch(context, config, clientprincipal,
 
852
                                HDB_F_GET_CLIENT, NULL, &user);
 
853
            krb5_free_principal(context, clientprincipal);
 
854
            if (ret) {
 
855
                krb5_set_error_message(context, ret,
 
856
                                       "MS-CHAP-V2 user %s not in database",
 
857
                                       username);
 
858
                goto failed;
 
859
            }
 
860
 
 
861
            ret = hdb_enctype2key(context, &user->entry,
 
862
                                  ETYPE_ARCFOUR_HMAC_MD5, &key);
 
863
            if (ret) {
 
864
                krb5_set_error_message(context, ret,
 
865
                                       "MS-CHAP-V2 missing arcfour key %s",
 
866
                                       username);
 
867
                goto failed;
 
868
            }
 
869
 
 
870
            /* ChallengeResponse */
 
871
            ret = heim_ntlm_calculate_ntlm1(key->key.keyvalue.data,
 
872
                                            key->key.keyvalue.length,
 
873
                                            challange, &answer);
 
874
            if (ret) {
 
875
                krb5_set_error_message(context, ret, "NTLM missing arcfour key");
 
876
                goto failed;
 
877
            }
 
878
        
 
879
            hex_encode(answer.data, answer.length, &mdx);
 
880
            if (mdx == NULL) {
 
881
                free(answer.data);
 
882
                krb5_clear_error_message(context);
 
883
                ret = ENOMEM;
 
884
                goto out;
 
885
            }
 
886
 
 
887
            r.element = choice_DigestRepInner_response;
 
888
            ret = strcasecmp(mdx, ireq.u.digestRequest.responseData);
 
889
            if (ret == 0) {
 
890
                r.u.response.success = TRUE;
 
891
            } else {
 
892
                kdc_log(context, config, 0,
 
893
                        "MS-CHAP-V2 hash mismatch for %s",
 
894
                        ireq.u.digestRequest.username);
 
895
                r.u.response.success = FALSE;
 
896
            }
 
897
            free(mdx);
 
898
 
 
899
            if (r.u.response.success) {
 
900
                unsigned char hashhash[MD4_DIGEST_LENGTH];
 
901
 
 
902
                /* hashhash */
 
903
                {
 
904
                    MD4_CTX hctx;
 
905
 
 
906
                    MD4_Init(&hctx);
 
907
                    MD4_Update(&hctx, key->key.keyvalue.data,
 
908
                               key->key.keyvalue.length);
 
909
                    MD4_Final(hashhash, &hctx);
 
910
                }
 
911
 
 
912
                /* GenerateAuthenticatorResponse */
 
913
                SHA1_Init(&ctx);
 
914
                SHA1_Update(&ctx, hashhash, sizeof(hashhash));
 
915
                SHA1_Update(&ctx, answer.data, answer.length);
 
916
                SHA1_Update(&ctx, ms_chap_v2_magic1,sizeof(ms_chap_v2_magic1));
 
917
                SHA1_Final(md, &ctx);
 
918
 
 
919
                SHA1_Init(&ctx);
 
920
                SHA1_Update(&ctx, md, sizeof(md));
 
921
                SHA1_Update(&ctx, challange, 8);
 
922
                SHA1_Update(&ctx, ms_chap_v2_magic2, sizeof(ms_chap_v2_magic2));
 
923
                SHA1_Final(md, &ctx);
 
924
 
 
925
                r.u.response.rsp = calloc(1, sizeof(*r.u.response.rsp));
 
926
                if (r.u.response.rsp == NULL) {
 
927
                    free(answer.data);
 
928
                    krb5_clear_error_message(context);
 
929
                    ret = ENOMEM;
 
930
                    goto out;
 
931
                }
 
932
 
 
933
                hex_encode(md, sizeof(md), r.u.response.rsp);
 
934
                if (r.u.response.rsp == NULL) {
 
935
                    free(answer.data);
 
936
                    krb5_clear_error_message(context);
 
937
                    ret = ENOMEM;
 
938
                    goto out;
 
939
                }
 
940
 
 
941
                /* get_master, rfc 3079 3.4 */
 
942
                SHA1_Init(&ctx);
 
943
                SHA1_Update(&ctx, hashhash, 16); /* md4(hash) */
 
944
                SHA1_Update(&ctx, answer.data, answer.length);
 
945
                SHA1_Update(&ctx, ms_rfc3079_magic1, sizeof(ms_rfc3079_magic1));
 
946
                SHA1_Final(md, &ctx);
 
947
 
 
948
                free(answer.data);
 
949
 
 
950
                r.u.response.session_key =
 
951
                    calloc(1, sizeof(*r.u.response.session_key));
 
952
                if (r.u.response.session_key == NULL) {
 
953
                    krb5_clear_error_message(context);
 
954
                    ret = ENOMEM;
 
955
                    goto out;
 
956
                }
 
957
 
 
958
                ret = krb5_data_copy(r.u.response.session_key, md, 16);
 
959
                if (ret) {
 
960
                    krb5_clear_error_message(context);
 
961
                    goto out;
 
962
                }
 
963
            }
 
964
 
 
965
        } else {
 
966
            r.element = choice_DigestRepInner_error;
 
967
            asprintf(&r.u.error.reason, "Unsupported digest type %s",
 
968
                     ireq.u.digestRequest.type);
 
969
            if (r.u.error.reason == NULL) {
 
970
                ret = ENOMEM;
 
971
                krb5_set_error_message(context, ret, "malloc: out of memory");
 
972
                goto out;
 
973
            }
 
974
            r.u.error.code = EINVAL;
 
975
        }
 
976
 
 
977
        kdc_log(context, config, 0, "Digest %s request successful %s",
 
978
                ireq.u.digestRequest.type, ireq.u.digestRequest.username);
 
979
 
 
980
        break;
 
981
    }
 
982
    case choice_DigestReqInner_ntlmInit:
 
983
 
 
984
        if ((config->digests_allowed & (NTLM_V1|NTLM_V1_SESSION|NTLM_V2)) == 0) {
 
985
            kdc_log(context, config, 0, "NTLM not allowed");
 
986
            goto failed;
 
987
        }
 
988
 
 
989
        r.element = choice_DigestRepInner_ntlmInitReply;
 
990
 
 
991
        r.u.ntlmInitReply.flags = NTLM_NEG_UNICODE;
 
992
 
 
993
        if ((ireq.u.ntlmInit.flags & NTLM_NEG_UNICODE) == 0) {
 
994
            kdc_log(context, config, 0, "NTLM client have no unicode");
 
995
            goto failed;
 
996
        }
 
997
 
 
998
        if (ireq.u.ntlmInit.flags & NTLM_NEG_NTLM)
 
999
            r.u.ntlmInitReply.flags |= NTLM_NEG_NTLM;
 
1000
        else {
 
1001
            kdc_log(context, config, 0, "NTLM client doesn't support NTLM");
 
1002
            goto failed;
 
1003
        }
 
1004
 
 
1005
        r.u.ntlmInitReply.flags |=
 
1006
            NTLM_NEG_TARGET |
 
1007
            NTLM_TARGET_DOMAIN |
 
1008
            NTLM_ENC_128;
 
1009
 
 
1010
#define ALL                                     \
 
1011
        NTLM_NEG_SIGN|                          \
 
1012
            NTLM_NEG_SEAL|                      \
 
1013
            NTLM_NEG_ALWAYS_SIGN|               \
 
1014
            NTLM_NEG_NTLM2_SESSION|             \
 
1015
            NTLM_NEG_KEYEX
 
1016
 
 
1017
        r.u.ntlmInitReply.flags |= (ireq.u.ntlmInit.flags & (ALL));
 
1018
 
 
1019
#undef ALL
 
1020
 
 
1021
        r.u.ntlmInitReply.targetname =
 
1022
            get_ntlm_targetname(context, client);
 
1023
        if (r.u.ntlmInitReply.targetname == NULL) {
 
1024
            ret = ENOMEM;
 
1025
            krb5_set_error_message(context, ret, "malloc: out of memory");
 
1026
            goto out;
 
1027
        }
 
1028
        r.u.ntlmInitReply.challange.data = malloc(8);
 
1029
        if (r.u.ntlmInitReply.challange.data == NULL) {
 
1030
            ret = ENOMEM;
 
1031
            krb5_set_error_message(context, ret, "malloc: out of memory");
 
1032
            goto out;
 
1033
        }
 
1034
        r.u.ntlmInitReply.challange.length = 8;
 
1035
        if (RAND_bytes(r.u.ntlmInitReply.challange.data,
 
1036
                       r.u.ntlmInitReply.challange.length) != 1)
 
1037
            {
 
1038
                ret = ENOMEM;
 
1039
                krb5_set_error_message(context, ret, "out of random error");
 
1040
                goto out;
 
1041
            }
 
1042
        /* XXX fix targetinfo */
 
1043
        ALLOC(r.u.ntlmInitReply.targetinfo);
 
1044
        if (r.u.ntlmInitReply.targetinfo == NULL) {
 
1045
            ret = ENOMEM;
 
1046
            krb5_set_error_message(context, ret, "malloc: out of memory");
 
1047
            goto out;
 
1048
        }
 
1049
 
 
1050
        ret = fill_targetinfo(context,
 
1051
                              r.u.ntlmInitReply.targetname,
 
1052
                              client,
 
1053
                              r.u.ntlmInitReply.targetinfo);
 
1054
        if (ret) {
 
1055
            ret = ENOMEM;
 
1056
            krb5_set_error_message(context, ret, "malloc: out of memory");
 
1057
            goto out;
 
1058
        }
 
1059
 
 
1060
        /*
 
1061
         * Save data encryted in opaque for the second part of the
 
1062
         * ntlm authentication
 
1063
         */
 
1064
        sp = krb5_storage_emem();
 
1065
        if (sp == NULL) {
 
1066
            ret = ENOMEM;
 
1067
            krb5_set_error_message(context, ret, "malloc: out of memory");
 
1068
            goto out;
 
1069
        }
 
1070
        
 
1071
        ret = krb5_storage_write(sp, r.u.ntlmInitReply.challange.data, 8);
 
1072
        if (ret != 8) {
 
1073
            ret = ENOMEM;
 
1074
            krb5_set_error_message(context, ret, "storage write challange");
 
1075
            goto out;
 
1076
        }
 
1077
        ret = krb5_store_uint32(sp, r.u.ntlmInitReply.flags);
 
1078
        if (ret) {
 
1079
            krb5_clear_error_message(context);
 
1080
            goto out;
 
1081
        }
 
1082
 
 
1083
        ret = krb5_storage_to_data(sp, &buf);
 
1084
        if (ret) {
 
1085
            krb5_clear_error_message(context);
 
1086
            goto out;
 
1087
        }
 
1088
 
 
1089
        ret = get_digest_key(context, config, server, &crypto);
 
1090
        if (ret)
 
1091
            goto out;
 
1092
 
 
1093
        ret = krb5_encrypt(context, crypto, KRB5_KU_DIGEST_OPAQUE,
 
1094
                           buf.data, buf.length, &r.u.ntlmInitReply.opaque);
 
1095
        krb5_data_free(&buf);
 
1096
        krb5_crypto_destroy(context, crypto);
 
1097
        crypto = NULL;
 
1098
        if (ret)
 
1099
            goto out;
 
1100
 
 
1101
        kdc_log(context, config, 0, "NTLM init from %s", from);
 
1102
 
 
1103
        break;
 
1104
 
 
1105
    case choice_DigestReqInner_ntlmRequest: {
 
1106
        krb5_principal clientprincipal;
 
1107
        unsigned char sessionkey[16];
 
1108
        unsigned char challange[8];
 
1109
        uint32_t flags;
 
1110
        Key *key = NULL;
 
1111
        int version;
 
1112
        
 
1113
        r.element = choice_DigestRepInner_ntlmResponse;
 
1114
        r.u.ntlmResponse.success = 0;
 
1115
        r.u.ntlmResponse.flags = 0;
 
1116
        r.u.ntlmResponse.sessionkey = NULL;
 
1117
        r.u.ntlmResponse.tickets = NULL;
 
1118
 
 
1119
        /* get username */
 
1120
        ret = krb5_parse_name(context,
 
1121
                              ireq.u.ntlmRequest.username,
 
1122
                              &clientprincipal);
 
1123
        if (ret)
 
1124
            goto failed;
 
1125
 
 
1126
        ret = _kdc_db_fetch(context, config, clientprincipal,
 
1127
                            HDB_F_GET_CLIENT, NULL, &user);
 
1128
        krb5_free_principal(context, clientprincipal);
 
1129
        if (ret) {
 
1130
            krb5_set_error_message(context, ret, "NTLM user %s not in database",
 
1131
                                   ireq.u.ntlmRequest.username);
 
1132
            goto failed;
 
1133
        }
 
1134
 
 
1135
        ret = get_digest_key(context, config, server, &crypto);
 
1136
        if (ret)
 
1137
            goto failed;
 
1138
 
 
1139
        ret = krb5_decrypt(context, crypto, KRB5_KU_DIGEST_OPAQUE,
 
1140
                           ireq.u.ntlmRequest.opaque.data,
 
1141
                           ireq.u.ntlmRequest.opaque.length, &buf);
 
1142
        krb5_crypto_destroy(context, crypto);
 
1143
        crypto = NULL;
 
1144
        if (ret) {
 
1145
            kdc_log(context, config, 0,
 
1146
                    "Failed to decrypt nonce from %s", from);
 
1147
            goto failed;
 
1148
        }
 
1149
 
 
1150
        sp = krb5_storage_from_data(&buf);
 
1151
        if (sp == NULL) {
 
1152
            ret = ENOMEM;
 
1153
            krb5_set_error_message(context, ret, "malloc: out of memory");
 
1154
            goto out;
 
1155
        }
 
1156
        
 
1157
        ret = krb5_storage_read(sp, challange, sizeof(challange));
 
1158
        if (ret != sizeof(challange)) {
 
1159
            ret = ENOMEM;
 
1160
            krb5_set_error_message(context, ret, "NTLM storage read challange");
 
1161
            goto out;
 
1162
        }
 
1163
        ret = krb5_ret_uint32(sp, &flags);
 
1164
        if (ret) {
 
1165
            krb5_set_error_message(context, ret, "NTLM storage read flags");
 
1166
            goto out;
 
1167
        }
 
1168
        krb5_data_free(&buf);
 
1169
 
 
1170
        if ((flags & NTLM_NEG_NTLM) == 0) {
 
1171
            ret = EINVAL;
 
1172
            krb5_set_error_message(context, ret, "NTLM not negotiated");
 
1173
            goto out;
 
1174
        }
 
1175
 
 
1176
        ret = hdb_enctype2key(context, &user->entry,
 
1177
                              ETYPE_ARCFOUR_HMAC_MD5, &key);
 
1178
        if (ret) {
 
1179
            krb5_set_error_message(context, ret, "NTLM missing arcfour key");
 
1180
            goto out;
 
1181
        }
 
1182
 
 
1183
        /* check if this is NTLMv2 */
 
1184
        if (ireq.u.ntlmRequest.ntlm.length != 24) {
 
1185
            struct ntlm_buf infotarget, answer;
 
1186
            char *targetname;
 
1187
 
 
1188
            if ((config->digests_allowed & NTLM_V2) == 0) {
 
1189
                kdc_log(context, config, 0, "NTLM v2 not allowed");
 
1190
                goto out;
 
1191
            }
 
1192
 
 
1193
            version = 2;
 
1194
 
 
1195
            targetname = get_ntlm_targetname(context, client);
 
1196
            if (targetname == NULL) {
 
1197
                ret = ENOMEM;
 
1198
                krb5_set_error_message(context, ret, "malloc: out of memory");
 
1199
                goto out;
 
1200
            }
 
1201
 
 
1202
            answer.length = ireq.u.ntlmRequest.ntlm.length;
 
1203
            answer.data = ireq.u.ntlmRequest.ntlm.data;
 
1204
 
 
1205
            ret = heim_ntlm_verify_ntlm2(key->key.keyvalue.data,
 
1206
                                         key->key.keyvalue.length,
 
1207
                                         ireq.u.ntlmRequest.username,
 
1208
                                         targetname,
 
1209
                                         0,
 
1210
                                         challange,
 
1211
                                         &answer,
 
1212
                                         &infotarget,
 
1213
                                         sessionkey);
 
1214
            free(targetname);
 
1215
            if (ret) {
 
1216
                krb5_set_error_message(context, ret, "NTLM v2 verify failed");
 
1217
                goto failed;
 
1218
            }
 
1219
 
 
1220
            /* XXX verify infotarget matches client (checksum ?) */
 
1221
 
 
1222
            free(infotarget.data);
 
1223
            /* */
 
1224
 
 
1225
        } else {
 
1226
            struct ntlm_buf answer;
 
1227
 
 
1228
            version = 1;
 
1229
 
 
1230
            if (flags & NTLM_NEG_NTLM2_SESSION) {
 
1231
                unsigned char sessionhash[MD5_DIGEST_LENGTH];
 
1232
                MD5_CTX md5ctx;
 
1233
                
 
1234
                if ((config->digests_allowed & NTLM_V1_SESSION) == 0) {
 
1235
                    kdc_log(context, config, 0, "NTLM v1-session not allowed");
 
1236
                    ret = EINVAL;
 
1237
                    goto failed;
 
1238
                }
 
1239
 
 
1240
                if (ireq.u.ntlmRequest.lm.length != 24) {
 
1241
                    ret = EINVAL;
 
1242
                    krb5_set_error_message(context, ret, "LM hash have wrong length "
 
1243
                                           "for NTLM session key");
 
1244
                    goto failed;
 
1245
                }
 
1246
                
 
1247
                MD5_Init(&md5ctx);
 
1248
                MD5_Update(&md5ctx, challange, sizeof(challange));
 
1249
                MD5_Update(&md5ctx, ireq.u.ntlmRequest.lm.data, 8);
 
1250
                MD5_Final(sessionhash, &md5ctx);
 
1251
                memcpy(challange, sessionhash, sizeof(challange));
 
1252
            } else {
 
1253
                if ((config->digests_allowed & NTLM_V1) == 0) {
 
1254
                    kdc_log(context, config, 0, "NTLM v1 not allowed");
 
1255
                    goto failed;
 
1256
                }
 
1257
            }
 
1258
        
 
1259
            ret = heim_ntlm_calculate_ntlm1(key->key.keyvalue.data,
 
1260
                                            key->key.keyvalue.length,
 
1261
                                            challange, &answer);
 
1262
            if (ret) {
 
1263
                krb5_set_error_message(context, ret, "NTLM missing arcfour key");
 
1264
                goto failed;
 
1265
            }
 
1266
        
 
1267
            if (ireq.u.ntlmRequest.ntlm.length != answer.length ||
 
1268
                memcmp(ireq.u.ntlmRequest.ntlm.data, answer.data, answer.length) != 0)
 
1269
                {
 
1270
                    free(answer.data);
 
1271
                    ret = EINVAL;
 
1272
                    krb5_set_error_message(context, ret, "NTLM hash mismatch");
 
1273
                    goto failed;
 
1274
                }
 
1275
            free(answer.data);
 
1276
 
 
1277
            {
 
1278
                MD4_CTX ctx;
 
1279
 
 
1280
                MD4_Init(&ctx);
 
1281
                MD4_Update(&ctx,
 
1282
                           key->key.keyvalue.data, key->key.keyvalue.length);
 
1283
                MD4_Final(sessionkey, &ctx);
 
1284
            }
 
1285
        }
 
1286
 
 
1287
        if (ireq.u.ntlmRequest.sessionkey) {
 
1288
            unsigned char masterkey[MD4_DIGEST_LENGTH];
 
1289
            RC4_KEY rc4;
 
1290
            size_t len;
 
1291
        
 
1292
            if ((flags & NTLM_NEG_KEYEX) == 0) {
 
1293
                ret = EINVAL;
 
1294
                krb5_set_error_message(context, ret,
 
1295
                                       "NTLM client failed to neg key "
 
1296
                                       "exchange but still sent key");
 
1297
                goto failed;
 
1298
            }
 
1299
        
 
1300
            len = ireq.u.ntlmRequest.sessionkey->length;
 
1301
            if (len != sizeof(masterkey)){
 
1302
                ret = EINVAL;
 
1303
                krb5_set_error_message(context, ret,
 
1304
                                       "NTLM master key wrong length: %lu",
 
1305
                                       (unsigned long)len);
 
1306
                goto failed;
 
1307
            }
 
1308
        
 
1309
            RC4_set_key(&rc4, sizeof(sessionkey), sessionkey);
 
1310
        
 
1311
            RC4(&rc4, sizeof(masterkey),
 
1312
                ireq.u.ntlmRequest.sessionkey->data,
 
1313
                masterkey);
 
1314
            memset(&rc4, 0, sizeof(rc4));
 
1315
        
 
1316
            r.u.ntlmResponse.sessionkey =
 
1317
                malloc(sizeof(*r.u.ntlmResponse.sessionkey));
 
1318
            if (r.u.ntlmResponse.sessionkey == NULL) {
 
1319
                ret = EINVAL;
 
1320
                krb5_set_error_message(context, ret, "malloc: out of memory");
 
1321
                goto out;
 
1322
            }
 
1323
        
 
1324
            ret = krb5_data_copy(r.u.ntlmResponse.sessionkey,
 
1325
                                 masterkey, sizeof(masterkey));
 
1326
            if (ret) {
 
1327
                krb5_set_error_message(context, ret, "malloc: out of memory");
 
1328
                goto out;
 
1329
            }
 
1330
        }
 
1331
 
 
1332
        r.u.ntlmResponse.success = 1;
 
1333
        kdc_log(context, config, 0, "NTLM version %d successful for %s",
 
1334
                version, ireq.u.ntlmRequest.username);
 
1335
        break;
 
1336
    }
 
1337
    case choice_DigestReqInner_supportedMechs:
 
1338
 
 
1339
        kdc_log(context, config, 0, "digest supportedMechs from %s", from);
 
1340
 
 
1341
        r.element = choice_DigestRepInner_supportedMechs;
 
1342
        memset(&r.u.supportedMechs, 0, sizeof(r.u.supportedMechs));
 
1343
 
 
1344
        if (config->digests_allowed & NTLM_V1)
 
1345
            r.u.supportedMechs.ntlm_v1 = 1;
 
1346
        if (config->digests_allowed & NTLM_V1_SESSION)
 
1347
            r.u.supportedMechs.ntlm_v1_session = 1;
 
1348
        if (config->digests_allowed & NTLM_V2)
 
1349
            r.u.supportedMechs.ntlm_v2 = 1;
 
1350
        if (config->digests_allowed & DIGEST_MD5)
 
1351
            r.u.supportedMechs.digest_md5 = 1;
 
1352
        if (config->digests_allowed & CHAP_MD5)
 
1353
            r.u.supportedMechs.chap_md5 = 1;
 
1354
        if (config->digests_allowed & MS_CHAP_V2)
 
1355
            r.u.supportedMechs.ms_chap_v2 = 1;
 
1356
        break;
 
1357
 
 
1358
    default: {
 
1359
        const char *s;
 
1360
        ret = EINVAL;
 
1361
        krb5_set_error_message(context, ret, "unknown operation to digest");
 
1362
 
 
1363
        failed:
 
1364
 
 
1365
        s = krb5_get_error_message(context, ret);
 
1366
        if (s == NULL) {
 
1367
            krb5_clear_error_message(context);
 
1368
            goto out;
 
1369
        }
 
1370
        
 
1371
        kdc_log(context, config, 0, "Digest failed with: %s", s);
 
1372
 
 
1373
        r.element = choice_DigestRepInner_error;
 
1374
        r.u.error.reason = strdup("unknown error");
 
1375
        krb5_free_error_message(context, s);
 
1376
        if (r.u.error.reason == NULL) {
 
1377
            ret = ENOMEM;
 
1378
            krb5_set_error_message(context, ret, "malloc: out of memory");
 
1379
            goto out;
 
1380
        }
 
1381
        r.u.error.code = EINVAL;
 
1382
        break;
 
1383
    }
 
1384
    }
 
1385
 
 
1386
    ASN1_MALLOC_ENCODE(DigestRepInner, buf.data, buf.length, &r, &size, ret);
 
1387
    if (ret) {
 
1388
        krb5_set_error_message(context, ret, "Failed to encode inner digest reply");
 
1389
        goto out;
 
1390
    }
 
1391
    if (size != buf.length)
 
1392
        krb5_abortx(context, "ASN1 internal error");
 
1393
 
 
1394
    krb5_auth_con_addflags(context, ac, KRB5_AUTH_CONTEXT_USE_SUBKEY, NULL);
 
1395
 
 
1396
    ret = krb5_mk_rep (context, ac, &rep.apRep);
 
1397
    if (ret)
 
1398
        goto out;
 
1399
 
 
1400
    {
 
1401
        krb5_keyblock *key;
 
1402
 
 
1403
        ret = krb5_auth_con_getlocalsubkey(context, ac, &key);
 
1404
        if (ret)
 
1405
            goto out;
 
1406
 
 
1407
        ret = krb5_crypto_init(context, key, 0, &crypto);
 
1408
        krb5_free_keyblock (context, key);
 
1409
        if (ret)
 
1410
            goto out;
 
1411
    }
 
1412
 
 
1413
    ret = krb5_encrypt_EncryptedData(context, crypto, KRB5_KU_DIGEST_ENCRYPT,
 
1414
                                     buf.data, buf.length, 0,
 
1415
                                     &rep.innerRep);
 
1416
 
 
1417
    ASN1_MALLOC_ENCODE(DigestREP, reply->data, reply->length, &rep, &size, ret);
 
1418
    if (ret) {
 
1419
        krb5_set_error_message(context, ret, "Failed to encode digest reply");
 
1420
        goto out;
 
1421
    }
 
1422
    if (size != reply->length)
 
1423
        krb5_abortx(context, "ASN1 internal error");
 
1424
 
 
1425
 
 
1426
 out:
 
1427
    if (ac)
 
1428
        krb5_auth_con_free(context, ac);
 
1429
    if (ret)
 
1430
        krb5_warn(context, ret, "Digest request from %s failed", from);
 
1431
    if (ticket)
 
1432
        krb5_free_ticket(context, ticket);
 
1433
    if (id)
 
1434
        krb5_kt_close(context, id);
 
1435
    if (crypto)
 
1436
        krb5_crypto_destroy(context, crypto);
 
1437
    if (sp)
 
1438
        krb5_storage_free(sp);
 
1439
    if (user)
 
1440
        _kdc_free_ent (context, user);
 
1441
    if (server)
 
1442
        _kdc_free_ent (context, server);
 
1443
    if (client)
 
1444
        _kdc_free_ent (context, client);
 
1445
    if (password) {
 
1446
        memset(password, 0, strlen(password));
 
1447
        free (password);
 
1448
    }
 
1449
    if (client_name)
 
1450
        free (client_name);
 
1451
    krb5_data_free(&buf);
 
1452
    krb5_data_free(&serverNonce);
 
1453
    free_DigestREP(&rep);
 
1454
    free_DigestRepInner(&r);
 
1455
    free_DigestReqInner(&ireq);
 
1456
 
 
1457
    return ret;
 
1458
}