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