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