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