xref: /freebsd/crypto/heimdal/kdc/digest.c (revision f374ba41)
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 #ifdef DIGEST
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, NULL, &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 struct 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     memset(&res, 0, sizeof(res));
238 
239     kdc_log(context, config, 0, "Digest request from %s", from);
240 
241     ret = krb5_kt_resolve(context, "HDB:", &id);
242     if (ret) {
243 	kdc_log(context, config, 0, "Can't open database for digest");
244 	goto out;
245     }
246 
247     ret = krb5_rd_req(context,
248 		      &ac,
249 		      &req->apReq,
250 		      NULL,
251 		      id,
252 		      &ap_req_options,
253 		      &ticket);
254     if (ret)
255 	goto out;
256 
257     /* check the server principal in the ticket matches digest/R@R */
258     {
259 	krb5_principal principal = NULL;
260 	const char *p, *rr;
261 
262 	ret = krb5_ticket_get_server(context, ticket, &principal);
263 	if (ret)
264 	    goto out;
265 
266 	ret = EINVAL;
267 	krb5_set_error_message(context, ret, "Wrong digest server principal used");
268 	p = krb5_principal_get_comp_string(context, principal, 0);
269 	if (p == NULL) {
270 	    krb5_free_principal(context, principal);
271 	    goto out;
272 	}
273 	if (strcmp(p, KRB5_DIGEST_NAME) != 0) {
274 	    krb5_free_principal(context, principal);
275 	    goto out;
276 	}
277 
278 	p = krb5_principal_get_comp_string(context, principal, 1);
279 	if (p == NULL) {
280 	    krb5_free_principal(context, principal);
281 	    goto out;
282 	}
283 	rr = krb5_principal_get_realm(context, principal);
284 	if (rr == NULL) {
285 	    krb5_free_principal(context, principal);
286 	    goto out;
287 	}
288 	if (strcmp(p, rr) != 0) {
289 	    krb5_free_principal(context, principal);
290 	    goto out;
291 	}
292 	krb5_clear_error_message(context);
293 
294 	ret = _kdc_db_fetch(context, config, principal,
295 			    HDB_F_GET_SERVER, NULL, NULL, &server);
296 	if (ret)
297 	    goto out;
298 
299 	krb5_free_principal(context, principal);
300     }
301 
302     /* check the client is allowed to do digest auth */
303     {
304 	krb5_principal principal = NULL;
305 
306 	ret = krb5_ticket_get_client(context, ticket, &principal);
307 	if (ret)
308 	    goto out;
309 
310 	ret = krb5_unparse_name(context, principal, &client_name);
311 	if (ret) {
312 	    krb5_free_principal(context, principal);
313 	    goto out;
314 	}
315 
316 	ret = _kdc_db_fetch(context, config, principal,
317 			    HDB_F_GET_CLIENT, NULL, NULL, &client);
318 	krb5_free_principal(context, principal);
319 	if (ret)
320 	    goto out;
321 
322 	if (client->entry.flags.allow_digest == 0) {
323 	    kdc_log(context, config, 0,
324 		    "Client %s tried to use digest "
325 		    "but is not allowed to",
326 		    client_name);
327 	    ret = KRB5KDC_ERR_POLICY;
328 	    krb5_set_error_message(context, ret,
329 				   "Client is not permitted to use digest");
330 	    goto out;
331 	}
332     }
333 
334     /* unpack request */
335     {
336 	krb5_keyblock *key;
337 
338 	ret = krb5_auth_con_getremotesubkey(context, ac, &key);
339 	if (ret)
340 	    goto out;
341 	if (key == NULL) {
342 	    ret = EINVAL;
343 	    krb5_set_error_message(context, ret, "digest: remote subkey not found");
344 	    goto out;
345 	}
346 
347 	ret = krb5_crypto_init(context, key, 0, &crypto);
348 	krb5_free_keyblock (context, key);
349 	if (ret)
350 	    goto out;
351     }
352 
353     ret = krb5_decrypt_EncryptedData(context, crypto, KRB5_KU_DIGEST_ENCRYPT,
354 				     &req->innerReq, &buf);
355     krb5_crypto_destroy(context, crypto);
356     crypto = NULL;
357     if (ret)
358 	goto out;
359 
360     ret = decode_DigestReqInner(buf.data, buf.length, &ireq, NULL);
361     krb5_data_free(&buf);
362     if (ret) {
363 	krb5_set_error_message(context, ret, "Failed to decode digest inner request");
364 	goto out;
365     }
366 
367     kdc_log(context, config, 0, "Valid digest request from %s (%s)",
368 	    client_name, from);
369 
370     /*
371      * Process the inner request
372      */
373 
374     switch (ireq.element) {
375     case choice_DigestReqInner_init: {
376 	unsigned char server_nonce[16], identifier;
377 
378 	RAND_bytes(&identifier, sizeof(identifier));
379 	RAND_bytes(server_nonce, sizeof(server_nonce));
380 
381 	server_nonce[0] = kdc_time & 0xff;
382 	server_nonce[1] = (kdc_time >> 8) & 0xff;
383 	server_nonce[2] = (kdc_time >> 16) & 0xff;
384 	server_nonce[3] = (kdc_time >> 24) & 0xff;
385 
386 	r.element = choice_DigestRepInner_initReply;
387 
388 	hex_encode(server_nonce, sizeof(server_nonce), &r.u.initReply.nonce);
389 	if (r.u.initReply.nonce == NULL) {
390 	    ret = ENOMEM;
391 	    krb5_set_error_message(context, ret, "Failed to decode server nonce");
392 	    goto out;
393 	}
394 
395 	sp = krb5_storage_emem();
396 	if (sp == NULL) {
397 	    ret = ENOMEM;
398 	    krb5_set_error_message(context, ret, "malloc: out of memory");
399 	    goto out;
400 	}
401 	ret = krb5_store_stringz(sp, ireq.u.init.type);
402 	if (ret) {
403 	    krb5_clear_error_message(context);
404 	    goto out;
405 	}
406 
407 	if (ireq.u.init.channel) {
408 	    char *s;
409 
410 	    asprintf(&s, "%s-%s:%s", r.u.initReply.nonce,
411 		     ireq.u.init.channel->cb_type,
412 		     ireq.u.init.channel->cb_binding);
413 	    if (s == NULL) {
414 		ret = ENOMEM;
415 		krb5_set_error_message(context, ret,
416 				       "Failed to allocate channel binding");
417 		goto out;
418 	    }
419 	    free(r.u.initReply.nonce);
420 	    r.u.initReply.nonce = s;
421 	}
422 
423 	ret = krb5_store_stringz(sp, r.u.initReply.nonce);
424 	if (ret) {
425 	    krb5_clear_error_message(context);
426 	    goto out;
427 	}
428 
429 	if (strcasecmp(ireq.u.init.type, "CHAP") == 0) {
430 	    r.u.initReply.identifier =
431 		malloc(sizeof(*r.u.initReply.identifier));
432 	    if (r.u.initReply.identifier == NULL) {
433 		ret = ENOMEM;
434 		krb5_set_error_message(context, ret, "malloc: out of memory");
435 		goto out;
436 	    }
437 
438 	    asprintf(r.u.initReply.identifier, "%02X", identifier & 0xff);
439 	    if (*r.u.initReply.identifier == NULL) {
440 		ret = ENOMEM;
441 		krb5_set_error_message(context, ret, "malloc: out of memory");
442 		goto out;
443 	    }
444 
445 	} else
446 	    r.u.initReply.identifier = NULL;
447 
448 	if (ireq.u.init.hostname) {
449 	    ret = krb5_store_stringz(sp, *ireq.u.init.hostname);
450 	    if (ret) {
451 		krb5_clear_error_message(context);
452 		goto out;
453 	    }
454 	}
455 
456 	ret = krb5_storage_to_data(sp, &buf);
457 	if (ret) {
458 	    krb5_clear_error_message(context);
459 	    goto out;
460 	}
461 
462 	ret = get_digest_key(context, config, server, &crypto);
463 	if (ret)
464 	    goto out;
465 
466 	ret = krb5_create_checksum(context,
467 				   crypto,
468 				   KRB5_KU_DIGEST_OPAQUE,
469 				   0,
470 				   buf.data,
471 				   buf.length,
472 				   &res);
473 	krb5_crypto_destroy(context, crypto);
474 	crypto = NULL;
475 	krb5_data_free(&buf);
476 	if (ret)
477 	    goto out;
478 
479 	ASN1_MALLOC_ENCODE(Checksum, buf.data, buf.length, &res, &size, ret);
480 	free_Checksum(&res);
481 	if (ret) {
482 	    krb5_set_error_message(context, ret, "Failed to encode "
483 				   "checksum in digest request");
484 	    goto out;
485 	}
486 	if (size != buf.length)
487 	    krb5_abortx(context, "ASN1 internal error");
488 
489 	hex_encode(buf.data, buf.length, &r.u.initReply.opaque);
490 	free(buf.data);
491 	krb5_data_zero(&buf);
492 	if (r.u.initReply.opaque == NULL) {
493 	    krb5_clear_error_message(context);
494 	    ret = ENOMEM;
495 	    goto out;
496 	}
497 
498 	kdc_log(context, config, 0, "Digest %s init request successful from %s",
499 		ireq.u.init.type, from);
500 
501 	break;
502     }
503     case choice_DigestReqInner_digestRequest: {
504 	sp = krb5_storage_emem();
505 	if (sp == NULL) {
506 	    ret = ENOMEM;
507 	    krb5_set_error_message(context, ret, "malloc: out of memory");
508 	    goto out;
509 	}
510 	ret = krb5_store_stringz(sp, ireq.u.digestRequest.type);
511 	if (ret) {
512 	    krb5_clear_error_message(context);
513 	    goto out;
514 	}
515 
516 	krb5_store_stringz(sp, ireq.u.digestRequest.serverNonce);
517 
518 	if (ireq.u.digestRequest.hostname) {
519 	    ret = krb5_store_stringz(sp, *ireq.u.digestRequest.hostname);
520 	    if (ret) {
521 		krb5_clear_error_message(context);
522 		goto out;
523 	    }
524 	}
525 
526 	buf.length = strlen(ireq.u.digestRequest.opaque);
527 	buf.data = malloc(buf.length);
528 	if (buf.data == NULL) {
529 	    ret = ENOMEM;
530 	    krb5_set_error_message(context, ret, "malloc: out of memory");
531 	    goto out;
532 	}
533 
534 	ret = hex_decode(ireq.u.digestRequest.opaque, buf.data, buf.length);
535 	if (ret <= 0) {
536 	    ret = ENOMEM;
537 	    krb5_set_error_message(context, ret, "Failed to decode opaque");
538 	    goto out;
539 	}
540 	buf.length = ret;
541 
542 	ret = decode_Checksum(buf.data, buf.length, &res, NULL);
543 	free(buf.data);
544 	krb5_data_zero(&buf);
545 	if (ret) {
546 	    krb5_set_error_message(context, ret,
547 				   "Failed to decode digest Checksum");
548 	    goto out;
549 	}
550 
551 	ret = krb5_storage_to_data(sp, &buf);
552 	if (ret) {
553 	    krb5_clear_error_message(context);
554 	    goto out;
555 	}
556 
557 	serverNonce.length = strlen(ireq.u.digestRequest.serverNonce);
558 	serverNonce.data = malloc(serverNonce.length);
559 	if (serverNonce.data == NULL) {
560 	    ret = ENOMEM;
561 	    krb5_set_error_message(context, ret, "malloc: out of memory");
562 	    goto out;
563 	}
564 
565 	/*
566 	 * CHAP does the checksum of the raw nonce, but do it for all
567 	 * types, since we need to check the timestamp.
568 	 */
569 	{
570 	    ssize_t ssize;
571 
572 	    ssize = hex_decode(ireq.u.digestRequest.serverNonce,
573 			       serverNonce.data, serverNonce.length);
574 	    if (ssize <= 0) {
575 		ret = ENOMEM;
576 		krb5_set_error_message(context, ret, "Failed to decode serverNonce");
577 		goto out;
578 	    }
579 	    serverNonce.length = ssize;
580 	}
581 
582 	ret = get_digest_key(context, config, server, &crypto);
583 	if (ret)
584 	    goto out;
585 
586 	ret = krb5_verify_checksum(context, crypto,
587 				   KRB5_KU_DIGEST_OPAQUE,
588 				   buf.data, buf.length, &res);
589 	free_Checksum(&res);
590 	krb5_data_free(&buf);
591 	krb5_crypto_destroy(context, crypto);
592 	crypto = NULL;
593 	if (ret)
594 	    goto out;
595 
596 	/* verify time */
597 	{
598 	    unsigned char *p = serverNonce.data;
599 	    uint32_t t;
600 
601 	    if (serverNonce.length < 4) {
602 		ret = EINVAL;
603 		krb5_set_error_message(context, ret, "server nonce too short");
604 		goto out;
605 	    }
606 	    t = p[0] | (p[1] << 8) | (p[2] << 16) | (p[3] << 24);
607 
608 	    if (abs((kdc_time & 0xffffffff) - t) > context->max_skew) {
609 		ret = EINVAL;
610 		krb5_set_error_message(context, ret, "time screw in server nonce ");
611 		goto out;
612 	    }
613 	}
614 
615 	if (strcasecmp(ireq.u.digestRequest.type, "CHAP") == 0) {
616 	    EVP_MD_CTX *ctx;
617 	    unsigned char md[MD5_DIGEST_LENGTH];
618 	    char *mdx;
619 	    char idx;
620 
621 	    if ((config->digests_allowed & CHAP_MD5) == 0) {
622 		kdc_log(context, config, 0, "Digest CHAP MD5 not allowed");
623 		goto out;
624 	    }
625 
626 	    if (ireq.u.digestRequest.identifier == NULL) {
627 		ret = EINVAL;
628 		krb5_set_error_message(context, ret, "Identifier missing "
629 				       "from CHAP request");
630 		goto out;
631 	    }
632 
633 	    if (hex_decode(*ireq.u.digestRequest.identifier, &idx, 1) != 1) {
634 		ret = EINVAL;
635 		krb5_set_error_message(context, ret, "failed to decode identifier");
636 		goto out;
637 	    }
638 
639 	    ret = get_password_entry(context, config,
640 				     ireq.u.digestRequest.username,
641 				     &password);
642 	    if (ret)
643 		goto out;
644 
645 	    ctx = EVP_MD_CTX_create();
646 
647 	    EVP_DigestInit_ex(ctx, EVP_md5(), NULL);
648 	    EVP_DigestUpdate(ctx, &idx, 1);
649 	    EVP_DigestUpdate(ctx, password, strlen(password));
650 	    EVP_DigestUpdate(ctx, serverNonce.data, serverNonce.length);
651 	    EVP_DigestFinal_ex(ctx, md, NULL);
652 
653 	    EVP_MD_CTX_destroy(ctx);
654 
655 	    hex_encode(md, sizeof(md), &mdx);
656 	    if (mdx == NULL) {
657 		krb5_clear_error_message(context);
658 		ret = ENOMEM;
659 		goto out;
660 	    }
661 
662 	    r.element = choice_DigestRepInner_response;
663 
664 	    ret = strcasecmp(mdx, ireq.u.digestRequest.responseData);
665 	    free(mdx);
666 	    if (ret == 0) {
667 		r.u.response.success = TRUE;
668 	    } else {
669 		kdc_log(context, config, 0,
670 			"CHAP reply mismatch for %s",
671 			ireq.u.digestRequest.username);
672 		r.u.response.success = FALSE;
673 	    }
674 
675 	} else if (strcasecmp(ireq.u.digestRequest.type, "SASL-DIGEST-MD5") == 0) {
676 	    EVP_MD_CTX *ctx;
677 	    unsigned char md[MD5_DIGEST_LENGTH];
678 	    char *mdx;
679 	    char *A1, *A2;
680 
681 	    if ((config->digests_allowed & DIGEST_MD5) == 0) {
682 		kdc_log(context, config, 0, "Digest SASL MD5 not allowed");
683 		goto out;
684 	    }
685 
686 	    if (ireq.u.digestRequest.nonceCount == NULL)
687 		goto out;
688 	    if (ireq.u.digestRequest.clientNonce == NULL)
689 		goto out;
690 	    if (ireq.u.digestRequest.qop == NULL)
691 		goto out;
692 	    if (ireq.u.digestRequest.realm == NULL)
693 		goto out;
694 
695 	    ret = get_password_entry(context, config,
696 				     ireq.u.digestRequest.username,
697 				     &password);
698 	    if (ret)
699 		goto failed;
700 
701 	    ctx = EVP_MD_CTX_create();
702 
703 	    EVP_DigestInit_ex(ctx, EVP_md5(), NULL);
704 	    EVP_DigestUpdate(ctx, ireq.u.digestRequest.username,
705 		       strlen(ireq.u.digestRequest.username));
706 	    EVP_DigestUpdate(ctx, ":", 1);
707 	    EVP_DigestUpdate(ctx, *ireq.u.digestRequest.realm,
708 		       strlen(*ireq.u.digestRequest.realm));
709 	    EVP_DigestUpdate(ctx, ":", 1);
710 	    EVP_DigestUpdate(ctx, password, strlen(password));
711 	    EVP_DigestFinal_ex(ctx, md, NULL);
712 
713 	    EVP_DigestInit_ex(ctx, EVP_md5(), NULL);
714 	    EVP_DigestUpdate(ctx, md, sizeof(md));
715 	    EVP_DigestUpdate(ctx, ":", 1);
716 	    EVP_DigestUpdate(ctx, ireq.u.digestRequest.serverNonce,
717 		       strlen(ireq.u.digestRequest.serverNonce));
718 	    EVP_DigestUpdate(ctx, ":", 1);
719 	    EVP_DigestUpdate(ctx, *ireq.u.digestRequest.nonceCount,
720 		       strlen(*ireq.u.digestRequest.nonceCount));
721 	    if (ireq.u.digestRequest.authid) {
722 		EVP_DigestUpdate(ctx, ":", 1);
723 		EVP_DigestUpdate(ctx, *ireq.u.digestRequest.authid,
724 			   strlen(*ireq.u.digestRequest.authid));
725 	    }
726 	    EVP_DigestFinal_ex(ctx, md, NULL);
727 	    hex_encode(md, sizeof(md), &A1);
728 	    if (A1 == NULL) {
729 		ret = ENOMEM;
730 		krb5_set_error_message(context, ret, "malloc: out of memory");
731 		EVP_MD_CTX_destroy(ctx);
732 		goto failed;
733 	    }
734 
735 	    EVP_DigestInit_ex(ctx, EVP_md5(), NULL);
736 	    EVP_DigestUpdate(ctx,
737 			     "AUTHENTICATE:", sizeof("AUTHENTICATE:") - 1);
738 	    EVP_DigestUpdate(ctx, *ireq.u.digestRequest.uri,
739 		       strlen(*ireq.u.digestRequest.uri));
740 
741 	    /* conf|int */
742 	    if (strcmp(ireq.u.digestRequest.digest, "clear") != 0) {
743 		static char conf_zeros[] = ":00000000000000000000000000000000";
744 		EVP_DigestUpdate(ctx, conf_zeros, sizeof(conf_zeros) - 1);
745 	    }
746 
747 	    EVP_DigestFinal_ex(ctx, md, NULL);
748 
749 	    hex_encode(md, sizeof(md), &A2);
750 	    if (A2 == NULL) {
751 		ret = ENOMEM;
752 		krb5_set_error_message(context, ret, "malloc: out of memory");
753 		free(A1);
754 		goto failed;
755 	    }
756 
757 	    EVP_DigestInit_ex(ctx, EVP_md5(), NULL);
758 	    EVP_DigestUpdate(ctx, A1, strlen(A2));
759 	    EVP_DigestUpdate(ctx, ":", 1);
760 	    EVP_DigestUpdate(ctx, ireq.u.digestRequest.serverNonce,
761 		       strlen(ireq.u.digestRequest.serverNonce));
762 	    EVP_DigestUpdate(ctx, ":", 1);
763 	    EVP_DigestUpdate(ctx, *ireq.u.digestRequest.nonceCount,
764 		       strlen(*ireq.u.digestRequest.nonceCount));
765 	    EVP_DigestUpdate(ctx, ":", 1);
766 	    EVP_DigestUpdate(ctx, *ireq.u.digestRequest.clientNonce,
767 		       strlen(*ireq.u.digestRequest.clientNonce));
768 	    EVP_DigestUpdate(ctx, ":", 1);
769 	    EVP_DigestUpdate(ctx, *ireq.u.digestRequest.qop,
770 		       strlen(*ireq.u.digestRequest.qop));
771 	    EVP_DigestUpdate(ctx, ":", 1);
772 	    EVP_DigestUpdate(ctx, A2, strlen(A2));
773 
774 	    EVP_DigestFinal_ex(ctx, md, NULL);
775 
776 	    EVP_MD_CTX_destroy(ctx);
777 
778 	    free(A1);
779 	    free(A2);
780 
781 	    hex_encode(md, sizeof(md), &mdx);
782 	    if (mdx == NULL) {
783 		krb5_clear_error_message(context);
784 		ret = ENOMEM;
785 		goto out;
786 	    }
787 
788 	    r.element = choice_DigestRepInner_response;
789 	    ret = strcasecmp(mdx, ireq.u.digestRequest.responseData);
790 	    free(mdx);
791 	    if (ret == 0) {
792 		r.u.response.success = TRUE;
793 	    } else {
794 		kdc_log(context, config, 0,
795 			"DIGEST-MD5 reply mismatch for %s",
796 			ireq.u.digestRequest.username);
797 		r.u.response.success = FALSE;
798 	    }
799 
800 	} else if (strcasecmp(ireq.u.digestRequest.type, "MS-CHAP-V2") == 0) {
801 	    unsigned char md[SHA_DIGEST_LENGTH], challange[SHA_DIGEST_LENGTH];
802 	    krb5_principal clientprincipal = NULL;
803 	    char *mdx;
804 	    const char *username;
805 	    struct ntlm_buf answer;
806 	    Key *key = NULL;
807 	    EVP_MD_CTX *ctp;
808 
809 	    if ((config->digests_allowed & MS_CHAP_V2) == 0) {
810 		kdc_log(context, config, 0, "MS-CHAP-V2 not allowed");
811 		goto failed;
812 	    }
813 
814 	    if (ireq.u.digestRequest.clientNonce == NULL)  {
815 		ret = EINVAL;
816 		krb5_set_error_message(context, ret,
817 				       "MS-CHAP-V2 clientNonce missing");
818 		goto failed;
819 	    }
820 	    if (serverNonce.length != 16) {
821 		ret = EINVAL;
822 		krb5_set_error_message(context, ret,
823 				       "MS-CHAP-V2 serverNonce wrong length");
824 		goto failed;
825 	    }
826 
827 	    /* strip of the domain component */
828 	    username = strchr(ireq.u.digestRequest.username, '\\');
829 	    if (username == NULL)
830 		username = ireq.u.digestRequest.username;
831 	    else
832 		username++;
833 
834 	    ctp = EVP_MD_CTX_create();
835 
836 	    /* ChallangeHash */
837 	    EVP_DigestInit_ex(ctp, EVP_sha1(), NULL);
838 	    {
839 		ssize_t ssize;
840 		krb5_data clientNonce;
841 
842 		clientNonce.length = strlen(*ireq.u.digestRequest.clientNonce);
843 		clientNonce.data = malloc(clientNonce.length);
844 		if (clientNonce.data == NULL) {
845 		    ret = ENOMEM;
846 		    krb5_set_error_message(context, ret,
847 					   "malloc: out of memory");
848 		    EVP_MD_CTX_destroy(ctp);
849 		    goto out;
850 		}
851 
852 		ssize = hex_decode(*ireq.u.digestRequest.clientNonce,
853 				   clientNonce.data, clientNonce.length);
854 		if (ssize != 16) {
855 		    ret = ENOMEM;
856 		    krb5_set_error_message(context, ret,
857 					   "Failed to decode clientNonce");
858 		    EVP_MD_CTX_destroy(ctp);
859 		    goto out;
860 		}
861 		EVP_DigestUpdate(ctp, clientNonce.data, ssize);
862 		free(clientNonce.data);
863 	    }
864 	    EVP_DigestUpdate(ctp, serverNonce.data, serverNonce.length);
865 	    EVP_DigestUpdate(ctp, username, strlen(username));
866 
867 	    EVP_DigestFinal_ex(ctp, challange, NULL);
868 
869 	    EVP_MD_CTX_destroy(ctp);
870 
871 	    /* NtPasswordHash */
872 	    ret = krb5_parse_name(context, username, &clientprincipal);
873 	    if (ret)
874 		goto failed;
875 
876 	    ret = _kdc_db_fetch(context, config, clientprincipal,
877 				HDB_F_GET_CLIENT, NULL, NULL, &user);
878 	    krb5_free_principal(context, clientprincipal);
879 	    if (ret) {
880 		krb5_set_error_message(context, ret,
881 				       "MS-CHAP-V2 user %s not in database",
882 				       username);
883 		goto failed;
884 	    }
885 
886 	    ret = hdb_enctype2key(context, &user->entry,
887 				  ETYPE_ARCFOUR_HMAC_MD5, &key);
888 	    if (ret) {
889 		krb5_set_error_message(context, ret,
890 				       "MS-CHAP-V2 missing arcfour key %s",
891 				       username);
892 		goto failed;
893 	    }
894 
895 	    /* ChallengeResponse */
896 	    ret = heim_ntlm_calculate_ntlm1(key->key.keyvalue.data,
897 					    key->key.keyvalue.length,
898 					    challange, &answer);
899 	    if (ret) {
900 		krb5_set_error_message(context, ret, "NTLM missing arcfour key");
901 		goto failed;
902 	    }
903 
904 	    hex_encode(answer.data, answer.length, &mdx);
905 	    if (mdx == NULL) {
906 		free(answer.data);
907 		krb5_clear_error_message(context);
908 		ret = ENOMEM;
909 		goto out;
910 	    }
911 
912 	    r.element = choice_DigestRepInner_response;
913 	    ret = strcasecmp(mdx, ireq.u.digestRequest.responseData);
914 	    if (ret == 0) {
915 		r.u.response.success = TRUE;
916 	    } else {
917 		kdc_log(context, config, 0,
918 			"MS-CHAP-V2 hash mismatch for %s",
919 			ireq.u.digestRequest.username);
920 		r.u.response.success = FALSE;
921 	    }
922 	    free(mdx);
923 
924 	    if (r.u.response.success) {
925 		unsigned char hashhash[MD4_DIGEST_LENGTH];
926 		EVP_MD_CTX *ctxp;
927 
928 		ctxp = EVP_MD_CTX_create();
929 
930 		/* hashhash */
931 		{
932 		    EVP_DigestInit_ex(ctxp, EVP_md4(), NULL);
933 		    EVP_DigestUpdate(ctxp,
934 				     key->key.keyvalue.data,
935 				     key->key.keyvalue.length);
936 		    EVP_DigestFinal_ex(ctxp, hashhash, NULL);
937 		}
938 
939 		/* GenerateAuthenticatorResponse */
940 		EVP_DigestInit_ex(ctxp, EVP_sha1(), NULL);
941 		EVP_DigestUpdate(ctxp, hashhash, sizeof(hashhash));
942 		EVP_DigestUpdate(ctxp, answer.data, answer.length);
943 		EVP_DigestUpdate(ctxp, ms_chap_v2_magic1,
944 				 sizeof(ms_chap_v2_magic1));
945 		EVP_DigestFinal_ex(ctxp, md, NULL);
946 
947 		EVP_DigestInit_ex(ctxp, EVP_sha1(), NULL);
948 		EVP_DigestUpdate(ctxp, md, sizeof(md));
949 		EVP_DigestUpdate(ctxp, challange, 8);
950 		EVP_DigestUpdate(ctxp, ms_chap_v2_magic2,
951 				 sizeof(ms_chap_v2_magic2));
952 		EVP_DigestFinal_ex(ctxp, md, NULL);
953 
954 		r.u.response.rsp = calloc(1, sizeof(*r.u.response.rsp));
955 		if (r.u.response.rsp == NULL) {
956 		    free(answer.data);
957 		    krb5_clear_error_message(context);
958 		    EVP_MD_CTX_destroy(ctxp);
959 		    ret = ENOMEM;
960 		    goto out;
961 		}
962 
963 		hex_encode(md, sizeof(md), r.u.response.rsp);
964 		if (r.u.response.rsp == NULL) {
965 		    free(answer.data);
966 		    krb5_clear_error_message(context);
967 		    EVP_MD_CTX_destroy(ctxp);
968 		    ret = ENOMEM;
969 		    goto out;
970 		}
971 
972 		/* get_master, rfc 3079 3.4 */
973 		EVP_DigestInit_ex(ctxp, EVP_sha1(), NULL);
974 		EVP_DigestUpdate(ctxp, hashhash, 16);
975 		EVP_DigestUpdate(ctxp, answer.data, answer.length);
976 		EVP_DigestUpdate(ctxp, ms_rfc3079_magic1,
977 				 sizeof(ms_rfc3079_magic1));
978 		EVP_DigestFinal_ex(ctxp, md, NULL);
979 
980 		free(answer.data);
981 
982 		EVP_MD_CTX_destroy(ctxp);
983 
984 		r.u.response.session_key =
985 		    calloc(1, sizeof(*r.u.response.session_key));
986 		if (r.u.response.session_key == NULL) {
987 		    krb5_clear_error_message(context);
988 		    ret = ENOMEM;
989 		    goto out;
990 		}
991 
992 		ret = krb5_data_copy(r.u.response.session_key, md, 16);
993 		if (ret) {
994 		    krb5_clear_error_message(context);
995 		    goto out;
996 		}
997 	    }
998 
999 	} else {
1000 	    r.element = choice_DigestRepInner_error;
1001 	    asprintf(&r.u.error.reason, "Unsupported digest type %s",
1002 		     ireq.u.digestRequest.type);
1003 	    if (r.u.error.reason == NULL) {
1004 		ret = ENOMEM;
1005 		krb5_set_error_message(context, ret, "malloc: out of memory");
1006 		goto out;
1007 	    }
1008 	    r.u.error.code = EINVAL;
1009 	}
1010 
1011 	kdc_log(context, config, 0, "Digest %s request successful %s",
1012 		ireq.u.digestRequest.type, ireq.u.digestRequest.username);
1013 
1014 	break;
1015     }
1016     case choice_DigestReqInner_ntlmInit:
1017 
1018 	if ((config->digests_allowed & (NTLM_V1|NTLM_V1_SESSION|NTLM_V2)) == 0) {
1019 	    kdc_log(context, config, 0, "NTLM not allowed");
1020 	    goto failed;
1021 	}
1022 
1023 	r.element = choice_DigestRepInner_ntlmInitReply;
1024 
1025 	r.u.ntlmInitReply.flags = NTLM_NEG_UNICODE;
1026 
1027 	if ((ireq.u.ntlmInit.flags & NTLM_NEG_UNICODE) == 0) {
1028 	    kdc_log(context, config, 0, "NTLM client have no unicode");
1029 	    goto failed;
1030 	}
1031 
1032 	if (ireq.u.ntlmInit.flags & NTLM_NEG_NTLM)
1033 	    r.u.ntlmInitReply.flags |= NTLM_NEG_NTLM;
1034 	else {
1035 	    kdc_log(context, config, 0, "NTLM client doesn't support NTLM");
1036 	    goto failed;
1037 	}
1038 
1039 	r.u.ntlmInitReply.flags |=
1040 	    NTLM_NEG_TARGET |
1041 	    NTLM_TARGET_DOMAIN |
1042 	    NTLM_ENC_128;
1043 
1044 #define ALL					\
1045 	NTLM_NEG_SIGN|				\
1046 	    NTLM_NEG_SEAL|			\
1047 	    NTLM_NEG_ALWAYS_SIGN|		\
1048 	    NTLM_NEG_NTLM2_SESSION|		\
1049 	    NTLM_NEG_KEYEX
1050 
1051 	r.u.ntlmInitReply.flags |= (ireq.u.ntlmInit.flags & (ALL));
1052 
1053 #undef ALL
1054 
1055 	r.u.ntlmInitReply.targetname =
1056 	    get_ntlm_targetname(context, client);
1057 	if (r.u.ntlmInitReply.targetname == NULL) {
1058 	    ret = ENOMEM;
1059 	    krb5_set_error_message(context, ret, "malloc: out of memory");
1060 	    goto out;
1061 	}
1062 	r.u.ntlmInitReply.challange.data = malloc(8);
1063 	if (r.u.ntlmInitReply.challange.data == NULL) {
1064 	    ret = ENOMEM;
1065 	    krb5_set_error_message(context, ret, "malloc: out of memory");
1066 	    goto out;
1067 	}
1068 	r.u.ntlmInitReply.challange.length = 8;
1069 	if (RAND_bytes(r.u.ntlmInitReply.challange.data,
1070 		       r.u.ntlmInitReply.challange.length) != 1)
1071 	    {
1072 		ret = ENOMEM;
1073 		krb5_set_error_message(context, ret, "out of random error");
1074 		goto out;
1075 	    }
1076 	/* XXX fix targetinfo */
1077 	ALLOC(r.u.ntlmInitReply.targetinfo);
1078 	if (r.u.ntlmInitReply.targetinfo == NULL) {
1079 	    ret = ENOMEM;
1080 	    krb5_set_error_message(context, ret, "malloc: out of memory");
1081 	    goto out;
1082 	}
1083 
1084 	ret = fill_targetinfo(context,
1085 			      r.u.ntlmInitReply.targetname,
1086 			      client,
1087 			      r.u.ntlmInitReply.targetinfo);
1088 	if (ret) {
1089 	    ret = ENOMEM;
1090 	    krb5_set_error_message(context, ret, "malloc: out of memory");
1091 	    goto out;
1092 	}
1093 
1094 	/*
1095 	 * Save data encryted in opaque for the second part of the
1096 	 * ntlm authentication
1097 	 */
1098 	sp = krb5_storage_emem();
1099 	if (sp == NULL) {
1100 	    ret = ENOMEM;
1101 	    krb5_set_error_message(context, ret, "malloc: out of memory");
1102 	    goto out;
1103 	}
1104 
1105 	ret = krb5_storage_write(sp, r.u.ntlmInitReply.challange.data, 8);
1106 	if (ret != 8) {
1107 	    ret = ENOMEM;
1108 	    krb5_set_error_message(context, ret, "storage write challange");
1109 	    goto out;
1110 	}
1111 	ret = krb5_store_uint32(sp, r.u.ntlmInitReply.flags);
1112 	if (ret) {
1113 	    krb5_clear_error_message(context);
1114 	    goto out;
1115 	}
1116 
1117 	ret = krb5_storage_to_data(sp, &buf);
1118 	if (ret) {
1119 	    krb5_clear_error_message(context);
1120 	    goto out;
1121 	}
1122 
1123 	ret = get_digest_key(context, config, server, &crypto);
1124 	if (ret)
1125 	    goto out;
1126 
1127 	ret = krb5_encrypt(context, crypto, KRB5_KU_DIGEST_OPAQUE,
1128 			   buf.data, buf.length, &r.u.ntlmInitReply.opaque);
1129 	krb5_data_free(&buf);
1130 	krb5_crypto_destroy(context, crypto);
1131 	crypto = NULL;
1132 	if (ret)
1133 	    goto out;
1134 
1135 	kdc_log(context, config, 0, "NTLM init from %s", from);
1136 
1137 	break;
1138 
1139     case choice_DigestReqInner_ntlmRequest: {
1140 	krb5_principal clientprincipal;
1141 	unsigned char sessionkey[16];
1142 	unsigned char challange[8];
1143 	uint32_t flags;
1144 	Key *key = NULL;
1145 	int version;
1146 
1147 	r.element = choice_DigestRepInner_ntlmResponse;
1148 	r.u.ntlmResponse.success = 0;
1149 	r.u.ntlmResponse.flags = 0;
1150 	r.u.ntlmResponse.sessionkey = NULL;
1151 	r.u.ntlmResponse.tickets = NULL;
1152 
1153 	/* get username */
1154 	ret = krb5_parse_name(context,
1155 			      ireq.u.ntlmRequest.username,
1156 			      &clientprincipal);
1157 	if (ret)
1158 	    goto failed;
1159 
1160 	ret = _kdc_db_fetch(context, config, clientprincipal,
1161 			    HDB_F_GET_CLIENT, NULL, NULL, &user);
1162 	krb5_free_principal(context, clientprincipal);
1163 	if (ret) {
1164 	    krb5_set_error_message(context, ret, "NTLM user %s not in database",
1165 				   ireq.u.ntlmRequest.username);
1166 	    goto failed;
1167 	}
1168 
1169 	ret = get_digest_key(context, config, server, &crypto);
1170 	if (ret)
1171 	    goto failed;
1172 
1173 	ret = krb5_decrypt(context, crypto, KRB5_KU_DIGEST_OPAQUE,
1174 			   ireq.u.ntlmRequest.opaque.data,
1175 			   ireq.u.ntlmRequest.opaque.length, &buf);
1176 	krb5_crypto_destroy(context, crypto);
1177 	crypto = NULL;
1178 	if (ret) {
1179 	    kdc_log(context, config, 0,
1180 		    "Failed to decrypt nonce from %s", from);
1181 	    goto failed;
1182 	}
1183 
1184 	sp = krb5_storage_from_data(&buf);
1185 	if (sp == NULL) {
1186 	    ret = ENOMEM;
1187 	    krb5_set_error_message(context, ret, "malloc: out of memory");
1188 	    goto out;
1189 	}
1190 
1191 	ret = krb5_storage_read(sp, challange, sizeof(challange));
1192 	if (ret != sizeof(challange)) {
1193 	    ret = ENOMEM;
1194 	    krb5_set_error_message(context, ret, "NTLM storage read challange");
1195 	    goto out;
1196 	}
1197 	ret = krb5_ret_uint32(sp, &flags);
1198 	if (ret) {
1199 	    krb5_set_error_message(context, ret, "NTLM storage read flags");
1200 	    goto out;
1201 	}
1202 	krb5_storage_free(sp);
1203 	sp = NULL;
1204 	krb5_data_free(&buf);
1205 
1206 	if ((flags & NTLM_NEG_NTLM) == 0) {
1207 	    ret = EINVAL;
1208 	    krb5_set_error_message(context, ret, "NTLM not negotiated");
1209 	    goto out;
1210 	}
1211 
1212 	ret = hdb_enctype2key(context, &user->entry,
1213 			      ETYPE_ARCFOUR_HMAC_MD5, &key);
1214 	if (ret) {
1215 	    krb5_set_error_message(context, ret, "NTLM missing arcfour key");
1216 	    goto out;
1217 	}
1218 
1219 	/* check if this is NTLMv2 */
1220 	if (ireq.u.ntlmRequest.ntlm.length != 24) {
1221 	    struct ntlm_buf infotarget, answer;
1222 	    char *targetname;
1223 
1224 	    if ((config->digests_allowed & NTLM_V2) == 0) {
1225 		kdc_log(context, config, 0, "NTLM v2 not allowed");
1226 		goto out;
1227 	    }
1228 
1229 	    version = 2;
1230 
1231 	    targetname = get_ntlm_targetname(context, client);
1232 	    if (targetname == NULL) {
1233 		ret = ENOMEM;
1234 		krb5_set_error_message(context, ret, "malloc: out of memory");
1235 		goto out;
1236 	    }
1237 
1238 	    answer.length = ireq.u.ntlmRequest.ntlm.length;
1239 	    answer.data = ireq.u.ntlmRequest.ntlm.data;
1240 
1241 	    ret = heim_ntlm_verify_ntlm2(key->key.keyvalue.data,
1242 					 key->key.keyvalue.length,
1243 					 ireq.u.ntlmRequest.username,
1244 					 targetname,
1245 					 0,
1246 					 challange,
1247 					 &answer,
1248 					 &infotarget,
1249 					 sessionkey);
1250 	    free(targetname);
1251 	    if (ret) {
1252 		krb5_set_error_message(context, ret, "NTLM v2 verify failed");
1253 		goto failed;
1254 	    }
1255 
1256 	    /* XXX verify infotarget matches client (checksum ?) */
1257 
1258 	    free(infotarget.data);
1259 	    /* */
1260 
1261 	} else {
1262 	    struct ntlm_buf answer;
1263 
1264 	    version = 1;
1265 
1266 	    if (flags & NTLM_NEG_NTLM2_SESSION) {
1267 		unsigned char sessionhash[MD5_DIGEST_LENGTH];
1268 		EVP_MD_CTX *ctx;
1269 
1270 		if ((config->digests_allowed & NTLM_V1_SESSION) == 0) {
1271 		    kdc_log(context, config, 0, "NTLM v1-session not allowed");
1272 		    ret = EINVAL;
1273 		    goto failed;
1274 		}
1275 
1276 		if (ireq.u.ntlmRequest.lm.length != 24) {
1277 		    ret = EINVAL;
1278 		    krb5_set_error_message(context, ret, "LM hash have wrong length "
1279 					   "for NTLM session key");
1280 		    goto failed;
1281 		}
1282 
1283 		ctx = EVP_MD_CTX_create();
1284 
1285 		EVP_DigestInit_ex(ctx, EVP_md5(), NULL);
1286 
1287 		EVP_DigestUpdate(ctx, challange, sizeof(challange));
1288 		EVP_DigestUpdate(ctx, ireq.u.ntlmRequest.lm.data, 8);
1289 		EVP_DigestFinal_ex(ctx, sessionhash, NULL);
1290 		memcpy(challange, sessionhash, sizeof(challange));
1291 
1292 		EVP_MD_CTX_destroy(ctx);
1293 
1294 	    } else {
1295 		if ((config->digests_allowed & NTLM_V1) == 0) {
1296 		    kdc_log(context, config, 0, "NTLM v1 not allowed");
1297 		    goto failed;
1298 		}
1299 	    }
1300 
1301 	    ret = heim_ntlm_calculate_ntlm1(key->key.keyvalue.data,
1302 					    key->key.keyvalue.length,
1303 					    challange, &answer);
1304 	    if (ret) {
1305 		krb5_set_error_message(context, ret, "NTLM missing arcfour key");
1306 		goto failed;
1307 	    }
1308 
1309 	    if (ireq.u.ntlmRequest.ntlm.length != answer.length ||
1310 		memcmp(ireq.u.ntlmRequest.ntlm.data, answer.data, answer.length) != 0)
1311 		{
1312 		    free(answer.data);
1313 		    ret = EINVAL;
1314 		    krb5_set_error_message(context, ret, "NTLM hash mismatch");
1315 		    goto failed;
1316 		}
1317 	    free(answer.data);
1318 
1319 	    {
1320 		EVP_MD_CTX *ctx;
1321 
1322 		ctx = EVP_MD_CTX_create();
1323 
1324 		EVP_DigestInit_ex(ctx, EVP_md4(), NULL);
1325 		EVP_DigestUpdate(ctx,
1326 				 key->key.keyvalue.data,
1327 				 key->key.keyvalue.length);
1328 		EVP_DigestFinal_ex(ctx, sessionkey, NULL);
1329 
1330 		EVP_MD_CTX_destroy(ctx);
1331 	    }
1332 	}
1333 
1334 	if (ireq.u.ntlmRequest.sessionkey) {
1335 	    unsigned char masterkey[MD4_DIGEST_LENGTH];
1336 	    EVP_CIPHER_CTX *rc4;
1337 	    size_t len;
1338 
1339 	    if ((flags & NTLM_NEG_KEYEX) == 0) {
1340 		ret = EINVAL;
1341 		krb5_set_error_message(context, ret,
1342 				       "NTLM client failed to neg key "
1343 				       "exchange but still sent key");
1344 		goto failed;
1345 	    }
1346 
1347 	    len = ireq.u.ntlmRequest.sessionkey->length;
1348 	    if (len != sizeof(masterkey)){
1349 		ret = EINVAL;
1350 		krb5_set_error_message(context, ret,
1351 				       "NTLM master key wrong length: %lu",
1352 				       (unsigned long)len);
1353 		goto failed;
1354 	    }
1355 
1356 
1357 	    rc4 = EVP_CIPHER_CTX_new();
1358 	    if (rc4 == NULL) {
1359 		ret = ENOMEM;
1360 		krb5_set_error_message(context, ret,
1361 				       "NTLM failed to malloc cipher context");
1362 		goto failed;
1363 	    }
1364 	    EVP_CipherInit_ex(rc4, EVP_rc4(), NULL, sessionkey, NULL, 1);
1365 	    EVP_Cipher(rc4,
1366 		       masterkey, ireq.u.ntlmRequest.sessionkey->data,
1367 		       sizeof(masterkey));
1368 	    EVP_CIPHER_CTX_free(rc4);
1369 
1370 	    r.u.ntlmResponse.sessionkey =
1371 		malloc(sizeof(*r.u.ntlmResponse.sessionkey));
1372 	    if (r.u.ntlmResponse.sessionkey == NULL) {
1373 		ret = EINVAL;
1374 		krb5_set_error_message(context, ret, "malloc: out of memory");
1375 		goto out;
1376 	    }
1377 
1378 	    ret = krb5_data_copy(r.u.ntlmResponse.sessionkey,
1379 				 masterkey, sizeof(masterkey));
1380 	    if (ret) {
1381 		krb5_set_error_message(context, ret, "malloc: out of memory");
1382 		goto out;
1383 	    }
1384 	}
1385 
1386 	r.u.ntlmResponse.success = 1;
1387 	kdc_log(context, config, 0, "NTLM version %d successful for %s",
1388 		version, ireq.u.ntlmRequest.username);
1389 	break;
1390     }
1391     case choice_DigestReqInner_supportedMechs:
1392 
1393 	kdc_log(context, config, 0, "digest supportedMechs from %s", from);
1394 
1395 	r.element = choice_DigestRepInner_supportedMechs;
1396 	memset(&r.u.supportedMechs, 0, sizeof(r.u.supportedMechs));
1397 
1398 	if (config->digests_allowed & NTLM_V1)
1399 	    r.u.supportedMechs.ntlm_v1 = 1;
1400 	if (config->digests_allowed & NTLM_V1_SESSION)
1401 	    r.u.supportedMechs.ntlm_v1_session = 1;
1402 	if (config->digests_allowed & NTLM_V2)
1403 	    r.u.supportedMechs.ntlm_v2 = 1;
1404 	if (config->digests_allowed & DIGEST_MD5)
1405 	    r.u.supportedMechs.digest_md5 = 1;
1406 	if (config->digests_allowed & CHAP_MD5)
1407 	    r.u.supportedMechs.chap_md5 = 1;
1408 	if (config->digests_allowed & MS_CHAP_V2)
1409 	    r.u.supportedMechs.ms_chap_v2 = 1;
1410 	break;
1411 
1412     default: {
1413 	const char *s;
1414 	ret = EINVAL;
1415 	krb5_set_error_message(context, ret, "unknown operation to digest");
1416 
1417 	failed:
1418 
1419 	s = krb5_get_error_message(context, ret);
1420 	if (s == NULL) {
1421 	    krb5_clear_error_message(context);
1422 	    goto out;
1423 	}
1424 
1425 	kdc_log(context, config, 0, "Digest failed with: %s", s);
1426 
1427 	r.element = choice_DigestRepInner_error;
1428 	r.u.error.reason = strdup("unknown error");
1429 	krb5_free_error_message(context, s);
1430 	if (r.u.error.reason == NULL) {
1431 	    ret = ENOMEM;
1432 	    krb5_set_error_message(context, ret, "malloc: out of memory");
1433 	    goto out;
1434 	}
1435 	r.u.error.code = EINVAL;
1436 	break;
1437     }
1438     }
1439 
1440     ASN1_MALLOC_ENCODE(DigestRepInner, buf.data, buf.length, &r, &size, ret);
1441     if (ret) {
1442 	krb5_set_error_message(context, ret, "Failed to encode inner digest reply");
1443 	goto out;
1444     }
1445     if (size != buf.length)
1446 	krb5_abortx(context, "ASN1 internal error");
1447 
1448     krb5_auth_con_addflags(context, ac, KRB5_AUTH_CONTEXT_USE_SUBKEY, NULL);
1449 
1450     ret = krb5_mk_rep (context, ac, &rep.apRep);
1451     if (ret)
1452 	goto out;
1453 
1454     {
1455 	krb5_keyblock *key;
1456 
1457 	ret = krb5_auth_con_getlocalsubkey(context, ac, &key);
1458 	if (ret)
1459 	    goto out;
1460 
1461 	ret = krb5_crypto_init(context, key, 0, &crypto);
1462 	krb5_free_keyblock (context, key);
1463 	if (ret)
1464 	    goto out;
1465     }
1466 
1467     ret = krb5_encrypt_EncryptedData(context, crypto, KRB5_KU_DIGEST_ENCRYPT,
1468 				     buf.data, buf.length, 0,
1469 				     &rep.innerRep);
1470     if (ret) {
1471         krb5_prepend_error_message(context, ret, "Failed to encrypt digest: ");
1472         goto out;
1473     }
1474 
1475     ASN1_MALLOC_ENCODE(DigestREP, reply->data, reply->length, &rep, &size, ret);
1476     if (ret) {
1477 	krb5_set_error_message(context, ret, "Failed to encode digest reply");
1478 	goto out;
1479     }
1480     if (size != reply->length)
1481 	krb5_abortx(context, "ASN1 internal error");
1482 
1483 
1484  out:
1485     if (ac)
1486 	krb5_auth_con_free(context, ac);
1487     if (ret)
1488 	krb5_warn(context, ret, "Digest request from %s failed", from);
1489     if (ticket)
1490 	krb5_free_ticket(context, ticket);
1491     if (id)
1492 	krb5_kt_close(context, id);
1493     if (crypto)
1494 	krb5_crypto_destroy(context, crypto);
1495     if (sp)
1496 	krb5_storage_free(sp);
1497     if (user)
1498 	_kdc_free_ent (context, user);
1499     if (server)
1500 	_kdc_free_ent (context, server);
1501     if (client)
1502 	_kdc_free_ent (context, client);
1503     if (password) {
1504 	memset(password, 0, strlen(password));
1505 	free (password);
1506     }
1507     if (client_name)
1508 	free (client_name);
1509     krb5_data_free(&buf);
1510     krb5_data_free(&serverNonce);
1511     free_Checksum(&res);
1512     free_DigestREP(&rep);
1513     free_DigestRepInner(&r);
1514     free_DigestReqInner(&ireq);
1515 
1516     return ret;
1517 }
1518 
1519 #endif /* DIGEST */
1520