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