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