xref: /minix/crypto/external/bsd/heimdal/dist/kdc/digest.c (revision ebfedea0)
1 /*	$NetBSD: digest.c,v 1.1.1.1 2011/04/13 18:14:36 elric 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
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 *
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
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
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
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, *r;
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 	r = krb5_principal_get_realm(context, principal);
286 	if (r == NULL) {
287 	    krb5_free_principal(context, principal);
288 	    goto out;
289 	}
290 	if (strcmp(p, r) != 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 id;
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, &id, 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, &id, 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 *ctx;
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 	    ctx = EVP_MD_CTX_create();
837 
838 	    /* ChallangeHash */
839 	    EVP_DigestInit_ex(ctx, 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(ctx);
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(ctx);
861 		    goto out;
862 		}
863 		EVP_DigestUpdate(ctx, clientNonce.data, ssize);
864 		free(clientNonce.data);
865 	    }
866 	    EVP_DigestUpdate(ctx, serverNonce.data, serverNonce.length);
867 	    EVP_DigestUpdate(ctx, username, strlen(username));
868 
869 	    EVP_DigestFinal_ex(ctx, challange, NULL);
870 
871 	    EVP_MD_CTX_destroy(ctx);
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 *ctx;
929 
930 		ctx = EVP_MD_CTX_create();
931 
932 		/* hashhash */
933 		{
934 		    EVP_DigestInit_ex(ctx, EVP_md4(), NULL);
935 		    EVP_DigestUpdate(ctx,
936 				     key->key.keyvalue.data,
937 				     key->key.keyvalue.length);
938 		    EVP_DigestFinal_ex(ctx, hashhash, NULL);
939 		}
940 
941 		/* GenerateAuthenticatorResponse */
942 		EVP_DigestInit_ex(ctx, EVP_sha1(), NULL);
943 		EVP_DigestUpdate(ctx, hashhash, sizeof(hashhash));
944 		EVP_DigestUpdate(ctx, answer.data, answer.length);
945 		EVP_DigestUpdate(ctx, ms_chap_v2_magic1,
946 				 sizeof(ms_chap_v2_magic1));
947 		EVP_DigestFinal_ex(ctx, md, NULL);
948 
949 		EVP_DigestInit_ex(ctx, EVP_sha1(), NULL);
950 		EVP_DigestUpdate(ctx, md, sizeof(md));
951 		EVP_DigestUpdate(ctx, challange, 8);
952 		EVP_DigestUpdate(ctx, ms_chap_v2_magic2,
953 				 sizeof(ms_chap_v2_magic2));
954 		EVP_DigestFinal_ex(ctx, 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(ctx);
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(ctx);
970 		    ret = ENOMEM;
971 		    goto out;
972 		}
973 
974 		/* get_master, rfc 3079 3.4 */
975 		EVP_DigestInit_ex(ctx, EVP_sha1(), NULL);
976 		EVP_DigestUpdate(ctx, hashhash, 16);
977 		EVP_DigestUpdate(ctx, answer.data, answer.length);
978 		EVP_DigestUpdate(ctx, ms_rfc3079_magic1,
979 				 sizeof(ms_rfc3079_magic1));
980 		EVP_DigestFinal_ex(ctx, md, NULL);
981 
982 		free(answer.data);
983 
984 		EVP_MD_CTX_destroy(ctx);
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