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