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