1178825Sdfr/*
2233294Sstas * Copyright (c) 2006 - 2007 Kungliga Tekniska H��gskolan
3233294Sstas * (Royal Institute of Technology, Stockholm, Sweden).
4233294Sstas * All rights reserved.
5178825Sdfr *
6233294Sstas * Redistribution and use in source and binary forms, with or without
7233294Sstas * modification, are permitted provided that the following conditions
8233294Sstas * are met:
9178825Sdfr *
10233294Sstas * 1. Redistributions of source code must retain the above copyright
11233294Sstas *    notice, this list of conditions and the following disclaimer.
12178825Sdfr *
13233294Sstas * 2. Redistributions in binary form must reproduce the above copyright
14233294Sstas *    notice, this list of conditions and the following disclaimer in the
15233294Sstas *    documentation and/or other materials provided with the distribution.
16178825Sdfr *
17233294Sstas * 3. Neither the name of the Institute nor the names of its contributors
18233294Sstas *    may be used to endorse or promote products derived from this software
19233294Sstas *    without specific prior written permission.
20178825Sdfr *
21233294Sstas * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
22233294Sstas * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23233294Sstas * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24233294Sstas * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
25233294Sstas * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26233294Sstas * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27233294Sstas * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28233294Sstas * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29233294Sstas * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30233294Sstas * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31233294Sstas * SUCH DAMAGE.
32178825Sdfr */
33178825Sdfr
34178825Sdfr#include "kdc_locl.h"
35178825Sdfr#include <hex.h>
36178825Sdfr
37233294Sstas#ifdef DIGEST
38178825Sdfr
39178825Sdfr#define MS_CHAP_V2	0x20
40178825Sdfr#define CHAP_MD5	0x10
41178825Sdfr#define DIGEST_MD5	0x08
42178825Sdfr#define NTLM_V2		0x04
43178825Sdfr#define NTLM_V1_SESSION	0x02
44178825Sdfr#define NTLM_V1		0x01
45178825Sdfr
46178825Sdfrconst struct units _kdc_digestunits[] = {
47233294Sstas    {"ms-chap-v2",		1U << 5},
48233294Sstas    {"chap-md5",		1U << 4},
49233294Sstas    {"digest-md5",		1U << 3},
50233294Sstas    {"ntlm-v2",		1U << 2},
51233294Sstas    {"ntlm-v1-session",	1U << 1},
52233294Sstas    {"ntlm-v1",		1U << 0},
53233294Sstas    {NULL,	0}
54178825Sdfr};
55178825Sdfr
56178825Sdfr
57178825Sdfrstatic krb5_error_code
58178825Sdfrget_digest_key(krb5_context context,
59178825Sdfr	       krb5_kdc_configuration *config,
60178825Sdfr	       hdb_entry_ex *server,
61178825Sdfr	       krb5_crypto *crypto)
62178825Sdfr{
63178825Sdfr    krb5_error_code ret;
64178825Sdfr    krb5_enctype enctype;
65178825Sdfr    Key *key;
66233294Sstas
67178825Sdfr    ret = _kdc_get_preferred_key(context,
68178825Sdfr				 config,
69178825Sdfr				 server,
70178825Sdfr				 "digest-service",
71178825Sdfr				 &enctype,
72178825Sdfr				 &key);
73178825Sdfr    if (ret)
74178825Sdfr	return ret;
75178825Sdfr    return krb5_crypto_init(context, &key->key, 0, crypto);
76178825Sdfr}
77178825Sdfr
78178825Sdfr/*
79178825Sdfr *
80178825Sdfr */
81178825Sdfr
82178825Sdfrstatic char *
83178825Sdfrget_ntlm_targetname(krb5_context context,
84178825Sdfr		    hdb_entry_ex *client)
85178825Sdfr{
86178825Sdfr    char *targetname, *p;
87178825Sdfr
88178825Sdfr    targetname = strdup(krb5_principal_get_realm(context,
89178825Sdfr						 client->entry.principal));
90178825Sdfr    if (targetname == NULL)
91178825Sdfr	return NULL;
92178825Sdfr
93178825Sdfr    p = strchr(targetname, '.');
94178825Sdfr    if (p)
95178825Sdfr	*p = '\0';
96178825Sdfr
97178825Sdfr    strupr(targetname);
98178825Sdfr    return targetname;
99178825Sdfr}
100178825Sdfr
101178825Sdfrstatic krb5_error_code
102178825Sdfrfill_targetinfo(krb5_context context,
103178825Sdfr		char *targetname,
104178825Sdfr		hdb_entry_ex *client,
105178825Sdfr		krb5_data *data)
106178825Sdfr{
107178825Sdfr    struct ntlm_targetinfo ti;
108178825Sdfr    krb5_error_code ret;
109178825Sdfr    struct ntlm_buf d;
110178825Sdfr    krb5_principal p;
111178825Sdfr    const char *str;
112178825Sdfr
113178825Sdfr    memset(&ti, 0, sizeof(ti));
114178825Sdfr
115178825Sdfr    ti.domainname = targetname;
116178825Sdfr    p = client->entry.principal;
117178825Sdfr    str = krb5_principal_get_comp_string(context, p, 0);
118233294Sstas    if (str != NULL &&
119233294Sstas	(strcmp("host", str) == 0 ||
120178825Sdfr	 strcmp("ftp", str) == 0 ||
121178825Sdfr	 strcmp("imap", str) == 0 ||
122178825Sdfr	 strcmp("pop", str) == 0 ||
123178825Sdfr	 strcmp("smtp", str)))
124233294Sstas	{
125233294Sstas	    str = krb5_principal_get_comp_string(context, p, 1);
126233294Sstas	    ti.dnsservername = rk_UNCONST(str);
127233294Sstas	}
128233294Sstas
129178825Sdfr    ret = heim_ntlm_encode_targetinfo(&ti, 1, &d);
130178825Sdfr    if (ret)
131178825Sdfr	return ret;
132178825Sdfr
133178825Sdfr    data->data = d.data;
134178825Sdfr    data->length = d.length;
135178825Sdfr
136178825Sdfr    return 0;
137178825Sdfr}
138178825Sdfr
139178825Sdfr
140178825Sdfrstatic const unsigned char ms_chap_v2_magic1[39] = {
141178825Sdfr    0x4D, 0x61, 0x67, 0x69, 0x63, 0x20, 0x73, 0x65, 0x72, 0x76,
142178825Sdfr    0x65, 0x72, 0x20, 0x74, 0x6F, 0x20, 0x63, 0x6C, 0x69, 0x65,
143178825Sdfr    0x6E, 0x74, 0x20, 0x73, 0x69, 0x67, 0x6E, 0x69, 0x6E, 0x67,
144178825Sdfr    0x20, 0x63, 0x6F, 0x6E, 0x73, 0x74, 0x61, 0x6E, 0x74
145178825Sdfr};
146178825Sdfrstatic const unsigned char ms_chap_v2_magic2[41] = {
147178825Sdfr    0x50, 0x61, 0x64, 0x20, 0x74, 0x6F, 0x20, 0x6D, 0x61, 0x6B,
148178825Sdfr    0x65, 0x20, 0x69, 0x74, 0x20, 0x64, 0x6F, 0x20, 0x6D, 0x6F,
149178825Sdfr    0x72, 0x65, 0x20, 0x74, 0x68, 0x61, 0x6E, 0x20, 0x6F, 0x6E,
150178825Sdfr    0x65, 0x20, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6F,
151178825Sdfr    0x6E
152178825Sdfr};
153178825Sdfrstatic const unsigned char ms_rfc3079_magic1[27] = {
154178825Sdfr    0x54, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74,
155178825Sdfr    0x68, 0x65, 0x20, 0x4d, 0x50, 0x50, 0x45, 0x20, 0x4d,
156178825Sdfr    0x61, 0x73, 0x74, 0x65, 0x72, 0x20, 0x4b, 0x65, 0x79
157178825Sdfr};
158178825Sdfr
159178825Sdfr/*
160178825Sdfr *
161178825Sdfr */
162178825Sdfr
163178825Sdfrstatic krb5_error_code
164178825Sdfrget_password_entry(krb5_context context,
165178825Sdfr		   krb5_kdc_configuration *config,
166178825Sdfr		   const char *username,
167178825Sdfr		   char **password)
168178825Sdfr{
169178825Sdfr    krb5_principal clientprincipal;
170178825Sdfr    krb5_error_code ret;
171178825Sdfr    hdb_entry_ex *user;
172178825Sdfr    HDB *db;
173178825Sdfr
174178825Sdfr    /* get username */
175178825Sdfr    ret = krb5_parse_name(context, username, &clientprincipal);
176178825Sdfr    if (ret)
177178825Sdfr	return ret;
178178825Sdfr
179178825Sdfr    ret = _kdc_db_fetch(context, config, clientprincipal,
180233294Sstas			HDB_F_GET_CLIENT, NULL, &db, &user);
181178825Sdfr    krb5_free_principal(context, clientprincipal);
182178825Sdfr    if (ret)
183178825Sdfr	return ret;
184178825Sdfr
185178825Sdfr    ret = hdb_entry_get_password(context, db, &user->entry, password);
186178825Sdfr    if (ret || password == NULL) {
187178825Sdfr	if (ret == 0) {
188178825Sdfr	    ret = EINVAL;
189233294Sstas	    krb5_set_error_message(context, ret, "password missing");
190178825Sdfr	}
191178825Sdfr	memset(user, 0, sizeof(*user));
192178825Sdfr    }
193178825Sdfr    _kdc_free_ent (context, user);
194178825Sdfr    return ret;
195178825Sdfr}
196178825Sdfr
197178825Sdfr/*
198178825Sdfr *
199178825Sdfr */
200178825Sdfr
201178825Sdfrkrb5_error_code
202233294Sstas_kdc_do_digest(krb5_context context,
203178825Sdfr	       krb5_kdc_configuration *config,
204233294Sstas	       const struct DigestREQ *req, krb5_data *reply,
205178825Sdfr	       const char *from, struct sockaddr *addr)
206178825Sdfr{
207178825Sdfr    krb5_error_code ret = 0;
208178825Sdfr    krb5_ticket *ticket = NULL;
209178825Sdfr    krb5_auth_context ac = NULL;
210178825Sdfr    krb5_keytab id = NULL;
211178825Sdfr    krb5_crypto crypto = NULL;
212178825Sdfr    DigestReqInner ireq;
213178825Sdfr    DigestRepInner r;
214178825Sdfr    DigestREP rep;
215178825Sdfr    krb5_flags ap_req_options;
216178825Sdfr    krb5_data buf;
217178825Sdfr    size_t size;
218178825Sdfr    krb5_storage *sp = NULL;
219178825Sdfr    Checksum res;
220178825Sdfr    hdb_entry_ex *server = NULL, *user = NULL;
221178825Sdfr    hdb_entry_ex *client = NULL;
222178825Sdfr    char *client_name = NULL, *password = NULL;
223178825Sdfr    krb5_data serverNonce;
224178825Sdfr
225178825Sdfr    if(!config->enable_digest) {
226233294Sstas	kdc_log(context, config, 0,
227178825Sdfr		"Rejected digest request (disabled) from %s", from);
228178825Sdfr	return KRB5KDC_ERR_POLICY;
229178825Sdfr    }
230178825Sdfr
231178825Sdfr    krb5_data_zero(&buf);
232178825Sdfr    krb5_data_zero(reply);
233178825Sdfr    krb5_data_zero(&serverNonce);
234178825Sdfr    memset(&ireq, 0, sizeof(ireq));
235178825Sdfr    memset(&r, 0, sizeof(r));
236178825Sdfr    memset(&rep, 0, sizeof(rep));
237233294Sstas    memset(&res, 0, sizeof(res));
238178825Sdfr
239178825Sdfr    kdc_log(context, config, 0, "Digest request from %s", from);
240178825Sdfr
241178825Sdfr    ret = krb5_kt_resolve(context, "HDB:", &id);
242178825Sdfr    if (ret) {
243178825Sdfr	kdc_log(context, config, 0, "Can't open database for digest");
244178825Sdfr	goto out;
245178825Sdfr    }
246178825Sdfr
247233294Sstas    ret = krb5_rd_req(context,
248178825Sdfr		      &ac,
249178825Sdfr		      &req->apReq,
250178825Sdfr		      NULL,
251178825Sdfr		      id,
252178825Sdfr		      &ap_req_options,
253178825Sdfr		      &ticket);
254178825Sdfr    if (ret)
255178825Sdfr	goto out;
256178825Sdfr
257178825Sdfr    /* check the server principal in the ticket matches digest/R@R */
258178825Sdfr    {
259178825Sdfr	krb5_principal principal = NULL;
260233294Sstas	const char *p, *rr;
261178825Sdfr
262178825Sdfr	ret = krb5_ticket_get_server(context, ticket, &principal);
263178825Sdfr	if (ret)
264178825Sdfr	    goto out;
265178825Sdfr
266178825Sdfr	ret = EINVAL;
267233294Sstas	krb5_set_error_message(context, ret, "Wrong digest server principal used");
268178825Sdfr	p = krb5_principal_get_comp_string(context, principal, 0);
269178825Sdfr	if (p == NULL) {
270178825Sdfr	    krb5_free_principal(context, principal);
271178825Sdfr	    goto out;
272178825Sdfr	}
273178825Sdfr	if (strcmp(p, KRB5_DIGEST_NAME) != 0) {
274178825Sdfr	    krb5_free_principal(context, principal);
275178825Sdfr	    goto out;
276178825Sdfr	}
277178825Sdfr
278178825Sdfr	p = krb5_principal_get_comp_string(context, principal, 1);
279178825Sdfr	if (p == NULL) {
280178825Sdfr	    krb5_free_principal(context, principal);
281178825Sdfr	    goto out;
282178825Sdfr	}
283233294Sstas	rr = krb5_principal_get_realm(context, principal);
284233294Sstas	if (rr == NULL) {
285178825Sdfr	    krb5_free_principal(context, principal);
286178825Sdfr	    goto out;
287178825Sdfr	}
288233294Sstas	if (strcmp(p, rr) != 0) {
289178825Sdfr	    krb5_free_principal(context, principal);
290178825Sdfr	    goto out;
291178825Sdfr	}
292233294Sstas	krb5_clear_error_message(context);
293178825Sdfr
294178825Sdfr	ret = _kdc_db_fetch(context, config, principal,
295233294Sstas			    HDB_F_GET_SERVER, NULL, NULL, &server);
296178825Sdfr	if (ret)
297178825Sdfr	    goto out;
298178825Sdfr
299178825Sdfr	krb5_free_principal(context, principal);
300178825Sdfr    }
301178825Sdfr
302178825Sdfr    /* check the client is allowed to do digest auth */
303178825Sdfr    {
304178825Sdfr	krb5_principal principal = NULL;
305178825Sdfr
306178825Sdfr	ret = krb5_ticket_get_client(context, ticket, &principal);
307178825Sdfr	if (ret)
308178825Sdfr	    goto out;
309178825Sdfr
310178825Sdfr	ret = krb5_unparse_name(context, principal, &client_name);
311178825Sdfr	if (ret) {
312178825Sdfr	    krb5_free_principal(context, principal);
313178825Sdfr	    goto out;
314178825Sdfr	}
315178825Sdfr
316178825Sdfr	ret = _kdc_db_fetch(context, config, principal,
317233294Sstas			    HDB_F_GET_CLIENT, NULL, NULL, &client);
318178825Sdfr	krb5_free_principal(context, principal);
319178825Sdfr	if (ret)
320178825Sdfr	    goto out;
321178825Sdfr
322178825Sdfr	if (client->entry.flags.allow_digest == 0) {
323233294Sstas	    kdc_log(context, config, 0,
324178825Sdfr		    "Client %s tried to use digest "
325233294Sstas		    "but is not allowed to",
326178825Sdfr		    client_name);
327178825Sdfr	    ret = KRB5KDC_ERR_POLICY;
328233294Sstas	    krb5_set_error_message(context, ret,
329233294Sstas				   "Client is not permitted to use digest");
330178825Sdfr	    goto out;
331178825Sdfr	}
332178825Sdfr    }
333178825Sdfr
334178825Sdfr    /* unpack request */
335178825Sdfr    {
336178825Sdfr	krb5_keyblock *key;
337178825Sdfr
338178825Sdfr	ret = krb5_auth_con_getremotesubkey(context, ac, &key);
339178825Sdfr	if (ret)
340178825Sdfr	    goto out;
341178825Sdfr	if (key == NULL) {
342178825Sdfr	    ret = EINVAL;
343233294Sstas	    krb5_set_error_message(context, ret, "digest: remote subkey not found");
344178825Sdfr	    goto out;
345178825Sdfr	}
346178825Sdfr
347178825Sdfr	ret = krb5_crypto_init(context, key, 0, &crypto);
348178825Sdfr	krb5_free_keyblock (context, key);
349178825Sdfr	if (ret)
350178825Sdfr	    goto out;
351178825Sdfr    }
352178825Sdfr
353178825Sdfr    ret = krb5_decrypt_EncryptedData(context, crypto, KRB5_KU_DIGEST_ENCRYPT,
354178825Sdfr				     &req->innerReq, &buf);
355178825Sdfr    krb5_crypto_destroy(context, crypto);
356178825Sdfr    crypto = NULL;
357178825Sdfr    if (ret)
358178825Sdfr	goto out;
359233294Sstas
360178825Sdfr    ret = decode_DigestReqInner(buf.data, buf.length, &ireq, NULL);
361178825Sdfr    krb5_data_free(&buf);
362178825Sdfr    if (ret) {
363233294Sstas	krb5_set_error_message(context, ret, "Failed to decode digest inner request");
364178825Sdfr	goto out;
365178825Sdfr    }
366178825Sdfr
367233294Sstas    kdc_log(context, config, 0, "Valid digest request from %s (%s)",
368178825Sdfr	    client_name, from);
369178825Sdfr
370178825Sdfr    /*
371178825Sdfr     * Process the inner request
372178825Sdfr     */
373178825Sdfr
374178825Sdfr    switch (ireq.element) {
375178825Sdfr    case choice_DigestReqInner_init: {
376178825Sdfr	unsigned char server_nonce[16], identifier;
377178825Sdfr
378178825Sdfr	RAND_pseudo_bytes(&identifier, sizeof(identifier));
379178825Sdfr	RAND_pseudo_bytes(server_nonce, sizeof(server_nonce));
380178825Sdfr
381178825Sdfr	server_nonce[0] = kdc_time & 0xff;
382178825Sdfr	server_nonce[1] = (kdc_time >> 8) & 0xff;
383178825Sdfr	server_nonce[2] = (kdc_time >> 16) & 0xff;
384178825Sdfr	server_nonce[3] = (kdc_time >> 24) & 0xff;
385178825Sdfr
386178825Sdfr	r.element = choice_DigestRepInner_initReply;
387178825Sdfr
388178825Sdfr	hex_encode(server_nonce, sizeof(server_nonce), &r.u.initReply.nonce);
389178825Sdfr	if (r.u.initReply.nonce == NULL) {
390178825Sdfr	    ret = ENOMEM;
391233294Sstas	    krb5_set_error_message(context, ret, "Failed to decode server nonce");
392178825Sdfr	    goto out;
393178825Sdfr	}
394178825Sdfr
395178825Sdfr	sp = krb5_storage_emem();
396178825Sdfr	if (sp == NULL) {
397178825Sdfr	    ret = ENOMEM;
398233294Sstas	    krb5_set_error_message(context, ret, "malloc: out of memory");
399178825Sdfr	    goto out;
400178825Sdfr	}
401178825Sdfr	ret = krb5_store_stringz(sp, ireq.u.init.type);
402178825Sdfr	if (ret) {
403233294Sstas	    krb5_clear_error_message(context);
404178825Sdfr	    goto out;
405178825Sdfr	}
406178825Sdfr
407178825Sdfr	if (ireq.u.init.channel) {
408178825Sdfr	    char *s;
409178825Sdfr
410178825Sdfr	    asprintf(&s, "%s-%s:%s", r.u.initReply.nonce,
411178825Sdfr		     ireq.u.init.channel->cb_type,
412178825Sdfr		     ireq.u.init.channel->cb_binding);
413178825Sdfr	    if (s == NULL) {
414178825Sdfr		ret = ENOMEM;
415233294Sstas		krb5_set_error_message(context, ret,
416233294Sstas				       "Failed to allocate channel binding");
417178825Sdfr		goto out;
418178825Sdfr	    }
419178825Sdfr	    free(r.u.initReply.nonce);
420178825Sdfr	    r.u.initReply.nonce = s;
421178825Sdfr	}
422233294Sstas
423178825Sdfr	ret = krb5_store_stringz(sp, r.u.initReply.nonce);
424178825Sdfr	if (ret) {
425233294Sstas	    krb5_clear_error_message(context);
426178825Sdfr	    goto out;
427178825Sdfr	}
428178825Sdfr
429178825Sdfr	if (strcasecmp(ireq.u.init.type, "CHAP") == 0) {
430233294Sstas	    r.u.initReply.identifier =
431178825Sdfr		malloc(sizeof(*r.u.initReply.identifier));
432178825Sdfr	    if (r.u.initReply.identifier == NULL) {
433178825Sdfr		ret = ENOMEM;
434233294Sstas		krb5_set_error_message(context, ret, "malloc: out of memory");
435178825Sdfr		goto out;
436178825Sdfr	    }
437178825Sdfr
438178825Sdfr	    asprintf(r.u.initReply.identifier, "%02X", identifier & 0xff);
439178825Sdfr	    if (*r.u.initReply.identifier == NULL) {
440178825Sdfr		ret = ENOMEM;
441233294Sstas		krb5_set_error_message(context, ret, "malloc: out of memory");
442178825Sdfr		goto out;
443178825Sdfr	    }
444178825Sdfr
445178825Sdfr	} else
446178825Sdfr	    r.u.initReply.identifier = NULL;
447178825Sdfr
448178825Sdfr	if (ireq.u.init.hostname) {
449178825Sdfr	    ret = krb5_store_stringz(sp, *ireq.u.init.hostname);
450178825Sdfr	    if (ret) {
451233294Sstas		krb5_clear_error_message(context);
452178825Sdfr		goto out;
453178825Sdfr	    }
454178825Sdfr	}
455178825Sdfr
456178825Sdfr	ret = krb5_storage_to_data(sp, &buf);
457178825Sdfr	if (ret) {
458233294Sstas	    krb5_clear_error_message(context);
459178825Sdfr	    goto out;
460178825Sdfr	}
461178825Sdfr
462178825Sdfr	ret = get_digest_key(context, config, server, &crypto);
463178825Sdfr	if (ret)
464178825Sdfr	    goto out;
465178825Sdfr
466178825Sdfr	ret = krb5_create_checksum(context,
467178825Sdfr				   crypto,
468178825Sdfr				   KRB5_KU_DIGEST_OPAQUE,
469178825Sdfr				   0,
470178825Sdfr				   buf.data,
471178825Sdfr				   buf.length,
472178825Sdfr				   &res);
473178825Sdfr	krb5_crypto_destroy(context, crypto);
474178825Sdfr	crypto = NULL;
475178825Sdfr	krb5_data_free(&buf);
476178825Sdfr	if (ret)
477178825Sdfr	    goto out;
478233294Sstas
479178825Sdfr	ASN1_MALLOC_ENCODE(Checksum, buf.data, buf.length, &res, &size, ret);
480178825Sdfr	free_Checksum(&res);
481178825Sdfr	if (ret) {
482233294Sstas	    krb5_set_error_message(context, ret, "Failed to encode "
483233294Sstas				   "checksum in digest request");
484178825Sdfr	    goto out;
485178825Sdfr	}
486178825Sdfr	if (size != buf.length)
487178825Sdfr	    krb5_abortx(context, "ASN1 internal error");
488178825Sdfr
489178825Sdfr	hex_encode(buf.data, buf.length, &r.u.initReply.opaque);
490178825Sdfr	free(buf.data);
491233294Sstas	krb5_data_zero(&buf);
492178825Sdfr	if (r.u.initReply.opaque == NULL) {
493233294Sstas	    krb5_clear_error_message(context);
494178825Sdfr	    ret = ENOMEM;
495178825Sdfr	    goto out;
496178825Sdfr	}
497178825Sdfr
498178825Sdfr	kdc_log(context, config, 0, "Digest %s init request successful from %s",
499178825Sdfr		ireq.u.init.type, from);
500178825Sdfr
501178825Sdfr	break;
502178825Sdfr    }
503178825Sdfr    case choice_DigestReqInner_digestRequest: {
504178825Sdfr	sp = krb5_storage_emem();
505178825Sdfr	if (sp == NULL) {
506178825Sdfr	    ret = ENOMEM;
507233294Sstas	    krb5_set_error_message(context, ret, "malloc: out of memory");
508178825Sdfr	    goto out;
509178825Sdfr	}
510178825Sdfr	ret = krb5_store_stringz(sp, ireq.u.digestRequest.type);
511178825Sdfr	if (ret) {
512233294Sstas	    krb5_clear_error_message(context);
513178825Sdfr	    goto out;
514178825Sdfr	}
515178825Sdfr
516178825Sdfr	krb5_store_stringz(sp, ireq.u.digestRequest.serverNonce);
517178825Sdfr
518178825Sdfr	if (ireq.u.digestRequest.hostname) {
519178825Sdfr	    ret = krb5_store_stringz(sp, *ireq.u.digestRequest.hostname);
520178825Sdfr	    if (ret) {
521233294Sstas		krb5_clear_error_message(context);
522178825Sdfr		goto out;
523178825Sdfr	    }
524178825Sdfr	}
525178825Sdfr
526178825Sdfr	buf.length = strlen(ireq.u.digestRequest.opaque);
527178825Sdfr	buf.data = malloc(buf.length);
528178825Sdfr	if (buf.data == NULL) {
529178825Sdfr	    ret = ENOMEM;
530233294Sstas	    krb5_set_error_message(context, ret, "malloc: out of memory");
531178825Sdfr	    goto out;
532178825Sdfr	}
533178825Sdfr
534178825Sdfr	ret = hex_decode(ireq.u.digestRequest.opaque, buf.data, buf.length);
535178825Sdfr	if (ret <= 0) {
536178825Sdfr	    ret = ENOMEM;
537233294Sstas	    krb5_set_error_message(context, ret, "Failed to decode opaque");
538178825Sdfr	    goto out;
539178825Sdfr	}
540178825Sdfr	buf.length = ret;
541178825Sdfr
542178825Sdfr	ret = decode_Checksum(buf.data, buf.length, &res, NULL);
543178825Sdfr	free(buf.data);
544233294Sstas	krb5_data_zero(&buf);
545178825Sdfr	if (ret) {
546233294Sstas	    krb5_set_error_message(context, ret,
547233294Sstas				   "Failed to decode digest Checksum");
548178825Sdfr	    goto out;
549178825Sdfr	}
550233294Sstas
551178825Sdfr	ret = krb5_storage_to_data(sp, &buf);
552178825Sdfr	if (ret) {
553233294Sstas	    krb5_clear_error_message(context);
554178825Sdfr	    goto out;
555178825Sdfr	}
556178825Sdfr
557178825Sdfr	serverNonce.length = strlen(ireq.u.digestRequest.serverNonce);
558178825Sdfr	serverNonce.data = malloc(serverNonce.length);
559178825Sdfr	if (serverNonce.data == NULL) {
560178825Sdfr	    ret = ENOMEM;
561233294Sstas	    krb5_set_error_message(context, ret, "malloc: out of memory");
562178825Sdfr	    goto out;
563178825Sdfr	}
564233294Sstas
565178825Sdfr	/*
566178825Sdfr	 * CHAP does the checksum of the raw nonce, but do it for all
567178825Sdfr	 * types, since we need to check the timestamp.
568178825Sdfr	 */
569178825Sdfr	{
570178825Sdfr	    ssize_t ssize;
571233294Sstas
572233294Sstas	    ssize = hex_decode(ireq.u.digestRequest.serverNonce,
573178825Sdfr			       serverNonce.data, serverNonce.length);
574178825Sdfr	    if (ssize <= 0) {
575178825Sdfr		ret = ENOMEM;
576233294Sstas		krb5_set_error_message(context, ret, "Failed to decode serverNonce");
577178825Sdfr		goto out;
578178825Sdfr	    }
579178825Sdfr	    serverNonce.length = ssize;
580178825Sdfr	}
581178825Sdfr
582178825Sdfr	ret = get_digest_key(context, config, server, &crypto);
583178825Sdfr	if (ret)
584178825Sdfr	    goto out;
585178825Sdfr
586233294Sstas	ret = krb5_verify_checksum(context, crypto,
587178825Sdfr				   KRB5_KU_DIGEST_OPAQUE,
588178825Sdfr				   buf.data, buf.length, &res);
589233294Sstas	free_Checksum(&res);
590233294Sstas	krb5_data_free(&buf);
591178825Sdfr	krb5_crypto_destroy(context, crypto);
592178825Sdfr	crypto = NULL;
593178825Sdfr	if (ret)
594178825Sdfr	    goto out;
595178825Sdfr
596178825Sdfr	/* verify time */
597178825Sdfr	{
598178825Sdfr	    unsigned char *p = serverNonce.data;
599178825Sdfr	    uint32_t t;
600233294Sstas
601178825Sdfr	    if (serverNonce.length < 4) {
602178825Sdfr		ret = EINVAL;
603233294Sstas		krb5_set_error_message(context, ret, "server nonce too short");
604178825Sdfr		goto out;
605178825Sdfr	    }
606178825Sdfr	    t = p[0] | (p[1] << 8) | (p[2] << 16) | (p[3] << 24);
607178825Sdfr
608178825Sdfr	    if (abs((kdc_time & 0xffffffff) - t) > context->max_skew) {
609178825Sdfr		ret = EINVAL;
610233294Sstas		krb5_set_error_message(context, ret, "time screw in server nonce ");
611178825Sdfr		goto out;
612178825Sdfr	    }
613178825Sdfr	}
614178825Sdfr
615178825Sdfr	if (strcasecmp(ireq.u.digestRequest.type, "CHAP") == 0) {
616233294Sstas	    EVP_MD_CTX *ctx;
617178825Sdfr	    unsigned char md[MD5_DIGEST_LENGTH];
618178825Sdfr	    char *mdx;
619233294Sstas	    char idx;
620178825Sdfr
621178825Sdfr	    if ((config->digests_allowed & CHAP_MD5) == 0) {
622178825Sdfr		kdc_log(context, config, 0, "Digest CHAP MD5 not allowed");
623178825Sdfr		goto out;
624178825Sdfr	    }
625178825Sdfr
626178825Sdfr	    if (ireq.u.digestRequest.identifier == NULL) {
627178825Sdfr		ret = EINVAL;
628233294Sstas		krb5_set_error_message(context, ret, "Identifier missing "
629233294Sstas				       "from CHAP request");
630178825Sdfr		goto out;
631178825Sdfr	    }
632233294Sstas
633233294Sstas	    if (hex_decode(*ireq.u.digestRequest.identifier, &idx, 1) != 1) {
634178825Sdfr		ret = EINVAL;
635233294Sstas		krb5_set_error_message(context, ret, "failed to decode identifier");
636178825Sdfr		goto out;
637178825Sdfr	    }
638233294Sstas
639233294Sstas	    ret = get_password_entry(context, config,
640178825Sdfr				     ireq.u.digestRequest.username,
641178825Sdfr				     &password);
642178825Sdfr	    if (ret)
643178825Sdfr		goto out;
644178825Sdfr
645233294Sstas	    ctx = EVP_MD_CTX_create();
646178825Sdfr
647233294Sstas	    EVP_DigestInit_ex(ctx, EVP_md5(), NULL);
648233294Sstas	    EVP_DigestUpdate(ctx, &idx, 1);
649233294Sstas	    EVP_DigestUpdate(ctx, password, strlen(password));
650233294Sstas	    EVP_DigestUpdate(ctx, serverNonce.data, serverNonce.length);
651233294Sstas	    EVP_DigestFinal_ex(ctx, md, NULL);
652233294Sstas
653233294Sstas	    EVP_MD_CTX_destroy(ctx);
654233294Sstas
655178825Sdfr	    hex_encode(md, sizeof(md), &mdx);
656178825Sdfr	    if (mdx == NULL) {
657233294Sstas		krb5_clear_error_message(context);
658178825Sdfr		ret = ENOMEM;
659178825Sdfr		goto out;
660178825Sdfr	    }
661178825Sdfr
662178825Sdfr	    r.element = choice_DigestRepInner_response;
663178825Sdfr
664178825Sdfr	    ret = strcasecmp(mdx, ireq.u.digestRequest.responseData);
665178825Sdfr	    free(mdx);
666178825Sdfr	    if (ret == 0) {
667178825Sdfr		r.u.response.success = TRUE;
668178825Sdfr	    } else {
669233294Sstas		kdc_log(context, config, 0,
670178825Sdfr			"CHAP reply mismatch for %s",
671178825Sdfr			ireq.u.digestRequest.username);
672178825Sdfr		r.u.response.success = FALSE;
673178825Sdfr	    }
674178825Sdfr
675178825Sdfr	} else if (strcasecmp(ireq.u.digestRequest.type, "SASL-DIGEST-MD5") == 0) {
676233294Sstas	    EVP_MD_CTX *ctx;
677178825Sdfr	    unsigned char md[MD5_DIGEST_LENGTH];
678178825Sdfr	    char *mdx;
679178825Sdfr	    char *A1, *A2;
680178825Sdfr
681178825Sdfr	    if ((config->digests_allowed & DIGEST_MD5) == 0) {
682178825Sdfr		kdc_log(context, config, 0, "Digest SASL MD5 not allowed");
683178825Sdfr		goto out;
684178825Sdfr	    }
685178825Sdfr
686233294Sstas	    if (ireq.u.digestRequest.nonceCount == NULL)
687178825Sdfr		goto out;
688233294Sstas	    if (ireq.u.digestRequest.clientNonce == NULL)
689178825Sdfr		goto out;
690233294Sstas	    if (ireq.u.digestRequest.qop == NULL)
691178825Sdfr		goto out;
692233294Sstas	    if (ireq.u.digestRequest.realm == NULL)
693178825Sdfr		goto out;
694233294Sstas
695233294Sstas	    ret = get_password_entry(context, config,
696178825Sdfr				     ireq.u.digestRequest.username,
697178825Sdfr				     &password);
698178825Sdfr	    if (ret)
699178825Sdfr		goto failed;
700178825Sdfr
701233294Sstas	    ctx = EVP_MD_CTX_create();
702233294Sstas
703233294Sstas	    EVP_DigestInit_ex(ctx, EVP_md5(), NULL);
704233294Sstas	    EVP_DigestUpdate(ctx, ireq.u.digestRequest.username,
705178825Sdfr		       strlen(ireq.u.digestRequest.username));
706233294Sstas	    EVP_DigestUpdate(ctx, ":", 1);
707233294Sstas	    EVP_DigestUpdate(ctx, *ireq.u.digestRequest.realm,
708178825Sdfr		       strlen(*ireq.u.digestRequest.realm));
709233294Sstas	    EVP_DigestUpdate(ctx, ":", 1);
710233294Sstas	    EVP_DigestUpdate(ctx, password, strlen(password));
711233294Sstas	    EVP_DigestFinal_ex(ctx, md, NULL);
712233294Sstas
713233294Sstas	    EVP_DigestInit_ex(ctx, EVP_md5(), NULL);
714233294Sstas	    EVP_DigestUpdate(ctx, md, sizeof(md));
715233294Sstas	    EVP_DigestUpdate(ctx, ":", 1);
716233294Sstas	    EVP_DigestUpdate(ctx, ireq.u.digestRequest.serverNonce,
717178825Sdfr		       strlen(ireq.u.digestRequest.serverNonce));
718233294Sstas	    EVP_DigestUpdate(ctx, ":", 1);
719233294Sstas	    EVP_DigestUpdate(ctx, *ireq.u.digestRequest.nonceCount,
720178825Sdfr		       strlen(*ireq.u.digestRequest.nonceCount));
721178825Sdfr	    if (ireq.u.digestRequest.authid) {
722233294Sstas		EVP_DigestUpdate(ctx, ":", 1);
723233294Sstas		EVP_DigestUpdate(ctx, *ireq.u.digestRequest.authid,
724178825Sdfr			   strlen(*ireq.u.digestRequest.authid));
725178825Sdfr	    }
726233294Sstas	    EVP_DigestFinal_ex(ctx, md, NULL);
727178825Sdfr	    hex_encode(md, sizeof(md), &A1);
728178825Sdfr	    if (A1 == NULL) {
729178825Sdfr		ret = ENOMEM;
730233294Sstas		krb5_set_error_message(context, ret, "malloc: out of memory");
731233294Sstas		EVP_MD_CTX_destroy(ctx);
732178825Sdfr		goto failed;
733178825Sdfr	    }
734233294Sstas
735233294Sstas	    EVP_DigestInit_ex(ctx, EVP_md5(), NULL);
736233294Sstas	    EVP_DigestUpdate(ctx,
737233294Sstas			     "AUTHENTICATE:", sizeof("AUTHENTICATE:") - 1);
738233294Sstas	    EVP_DigestUpdate(ctx, *ireq.u.digestRequest.uri,
739178825Sdfr		       strlen(*ireq.u.digestRequest.uri));
740233294Sstas
741178825Sdfr	    /* conf|int */
742178825Sdfr	    if (strcmp(ireq.u.digestRequest.digest, "clear") != 0) {
743178825Sdfr		static char conf_zeros[] = ":00000000000000000000000000000000";
744233294Sstas		EVP_DigestUpdate(ctx, conf_zeros, sizeof(conf_zeros) - 1);
745178825Sdfr	    }
746233294Sstas
747233294Sstas	    EVP_DigestFinal_ex(ctx, md, NULL);
748233294Sstas
749178825Sdfr	    hex_encode(md, sizeof(md), &A2);
750178825Sdfr	    if (A2 == NULL) {
751178825Sdfr		ret = ENOMEM;
752233294Sstas		krb5_set_error_message(context, ret, "malloc: out of memory");
753178825Sdfr		free(A1);
754178825Sdfr		goto failed;
755178825Sdfr	    }
756178825Sdfr
757233294Sstas	    EVP_DigestInit_ex(ctx, EVP_md5(), NULL);
758233294Sstas	    EVP_DigestUpdate(ctx, A1, strlen(A2));
759233294Sstas	    EVP_DigestUpdate(ctx, ":", 1);
760233294Sstas	    EVP_DigestUpdate(ctx, ireq.u.digestRequest.serverNonce,
761178825Sdfr		       strlen(ireq.u.digestRequest.serverNonce));
762233294Sstas	    EVP_DigestUpdate(ctx, ":", 1);
763233294Sstas	    EVP_DigestUpdate(ctx, *ireq.u.digestRequest.nonceCount,
764178825Sdfr		       strlen(*ireq.u.digestRequest.nonceCount));
765233294Sstas	    EVP_DigestUpdate(ctx, ":", 1);
766233294Sstas	    EVP_DigestUpdate(ctx, *ireq.u.digestRequest.clientNonce,
767178825Sdfr		       strlen(*ireq.u.digestRequest.clientNonce));
768233294Sstas	    EVP_DigestUpdate(ctx, ":", 1);
769233294Sstas	    EVP_DigestUpdate(ctx, *ireq.u.digestRequest.qop,
770178825Sdfr		       strlen(*ireq.u.digestRequest.qop));
771233294Sstas	    EVP_DigestUpdate(ctx, ":", 1);
772233294Sstas	    EVP_DigestUpdate(ctx, A2, strlen(A2));
773178825Sdfr
774233294Sstas	    EVP_DigestFinal_ex(ctx, md, NULL);
775178825Sdfr
776233294Sstas	    EVP_MD_CTX_destroy(ctx);
777233294Sstas
778178825Sdfr	    free(A1);
779178825Sdfr	    free(A2);
780178825Sdfr
781178825Sdfr	    hex_encode(md, sizeof(md), &mdx);
782178825Sdfr	    if (mdx == NULL) {
783233294Sstas		krb5_clear_error_message(context);
784178825Sdfr		ret = ENOMEM;
785178825Sdfr		goto out;
786178825Sdfr	    }
787178825Sdfr
788178825Sdfr	    r.element = choice_DigestRepInner_response;
789178825Sdfr	    ret = strcasecmp(mdx, ireq.u.digestRequest.responseData);
790178825Sdfr	    free(mdx);
791178825Sdfr	    if (ret == 0) {
792178825Sdfr		r.u.response.success = TRUE;
793178825Sdfr	    } else {
794233294Sstas		kdc_log(context, config, 0,
795178825Sdfr			"DIGEST-MD5 reply mismatch for %s",
796178825Sdfr			ireq.u.digestRequest.username);
797178825Sdfr		r.u.response.success = FALSE;
798178825Sdfr	    }
799178825Sdfr
800178825Sdfr	} else if (strcasecmp(ireq.u.digestRequest.type, "MS-CHAP-V2") == 0) {
801178825Sdfr	    unsigned char md[SHA_DIGEST_LENGTH], challange[SHA_DIGEST_LENGTH];
802178825Sdfr	    krb5_principal clientprincipal = NULL;
803178825Sdfr	    char *mdx;
804178825Sdfr	    const char *username;
805178825Sdfr	    struct ntlm_buf answer;
806178825Sdfr	    Key *key = NULL;
807233294Sstas	    EVP_MD_CTX *ctp;
808178825Sdfr
809178825Sdfr	    if ((config->digests_allowed & MS_CHAP_V2) == 0) {
810178825Sdfr		kdc_log(context, config, 0, "MS-CHAP-V2 not allowed");
811178825Sdfr		goto failed;
812178825Sdfr	    }
813178825Sdfr
814178825Sdfr	    if (ireq.u.digestRequest.clientNonce == NULL)  {
815178825Sdfr		ret = EINVAL;
816233294Sstas		krb5_set_error_message(context, ret,
817233294Sstas				       "MS-CHAP-V2 clientNonce missing");
818178825Sdfr		goto failed;
819233294Sstas	    }
820178825Sdfr	    if (serverNonce.length != 16) {
821178825Sdfr		ret = EINVAL;
822233294Sstas		krb5_set_error_message(context, ret,
823233294Sstas				       "MS-CHAP-V2 serverNonce wrong length");
824178825Sdfr		goto failed;
825178825Sdfr	    }
826178825Sdfr
827178825Sdfr	    /* strip of the domain component */
828178825Sdfr	    username = strchr(ireq.u.digestRequest.username, '\\');
829178825Sdfr	    if (username == NULL)
830178825Sdfr		username = ireq.u.digestRequest.username;
831178825Sdfr	    else
832178825Sdfr		username++;
833178825Sdfr
834233294Sstas	    ctp = EVP_MD_CTX_create();
835233294Sstas
836178825Sdfr	    /* ChallangeHash */
837233294Sstas	    EVP_DigestInit_ex(ctp, EVP_sha1(), NULL);
838178825Sdfr	    {
839178825Sdfr		ssize_t ssize;
840178825Sdfr		krb5_data clientNonce;
841233294Sstas
842178825Sdfr		clientNonce.length = strlen(*ireq.u.digestRequest.clientNonce);
843178825Sdfr		clientNonce.data = malloc(clientNonce.length);
844178825Sdfr		if (clientNonce.data == NULL) {
845178825Sdfr		    ret = ENOMEM;
846233294Sstas		    krb5_set_error_message(context, ret,
847233294Sstas					   "malloc: out of memory");
848233294Sstas		    EVP_MD_CTX_destroy(ctp);
849178825Sdfr		    goto out;
850178825Sdfr		}
851178825Sdfr
852233294Sstas		ssize = hex_decode(*ireq.u.digestRequest.clientNonce,
853178825Sdfr				   clientNonce.data, clientNonce.length);
854178825Sdfr		if (ssize != 16) {
855178825Sdfr		    ret = ENOMEM;
856233294Sstas		    krb5_set_error_message(context, ret,
857233294Sstas					   "Failed to decode clientNonce");
858233294Sstas		    EVP_MD_CTX_destroy(ctp);
859178825Sdfr		    goto out;
860178825Sdfr		}
861233294Sstas		EVP_DigestUpdate(ctp, clientNonce.data, ssize);
862178825Sdfr		free(clientNonce.data);
863178825Sdfr	    }
864233294Sstas	    EVP_DigestUpdate(ctp, serverNonce.data, serverNonce.length);
865233294Sstas	    EVP_DigestUpdate(ctp, username, strlen(username));
866178825Sdfr
867233294Sstas	    EVP_DigestFinal_ex(ctp, challange, NULL);
868233294Sstas
869233294Sstas	    EVP_MD_CTX_destroy(ctp);
870233294Sstas
871178825Sdfr	    /* NtPasswordHash */
872178825Sdfr	    ret = krb5_parse_name(context, username, &clientprincipal);
873178825Sdfr	    if (ret)
874178825Sdfr		goto failed;
875233294Sstas
876178825Sdfr	    ret = _kdc_db_fetch(context, config, clientprincipal,
877233294Sstas				HDB_F_GET_CLIENT, NULL, NULL, &user);
878178825Sdfr	    krb5_free_principal(context, clientprincipal);
879178825Sdfr	    if (ret) {
880233294Sstas		krb5_set_error_message(context, ret,
881233294Sstas				       "MS-CHAP-V2 user %s not in database",
882233294Sstas				       username);
883178825Sdfr		goto failed;
884178825Sdfr	    }
885178825Sdfr
886233294Sstas	    ret = hdb_enctype2key(context, &user->entry,
887178825Sdfr				  ETYPE_ARCFOUR_HMAC_MD5, &key);
888178825Sdfr	    if (ret) {
889233294Sstas		krb5_set_error_message(context, ret,
890233294Sstas				       "MS-CHAP-V2 missing arcfour key %s",
891233294Sstas				       username);
892178825Sdfr		goto failed;
893178825Sdfr	    }
894178825Sdfr
895178825Sdfr	    /* ChallengeResponse */
896178825Sdfr	    ret = heim_ntlm_calculate_ntlm1(key->key.keyvalue.data,
897178825Sdfr					    key->key.keyvalue.length,
898178825Sdfr					    challange, &answer);
899178825Sdfr	    if (ret) {
900233294Sstas		krb5_set_error_message(context, ret, "NTLM missing arcfour key");
901178825Sdfr		goto failed;
902178825Sdfr	    }
903233294Sstas
904178825Sdfr	    hex_encode(answer.data, answer.length, &mdx);
905178825Sdfr	    if (mdx == NULL) {
906178825Sdfr		free(answer.data);
907233294Sstas		krb5_clear_error_message(context);
908178825Sdfr		ret = ENOMEM;
909178825Sdfr		goto out;
910178825Sdfr	    }
911178825Sdfr
912178825Sdfr	    r.element = choice_DigestRepInner_response;
913178825Sdfr	    ret = strcasecmp(mdx, ireq.u.digestRequest.responseData);
914178825Sdfr	    if (ret == 0) {
915178825Sdfr		r.u.response.success = TRUE;
916178825Sdfr	    } else {
917233294Sstas		kdc_log(context, config, 0,
918178825Sdfr			"MS-CHAP-V2 hash mismatch for %s",
919178825Sdfr			ireq.u.digestRequest.username);
920178825Sdfr		r.u.response.success = FALSE;
921178825Sdfr	    }
922178825Sdfr	    free(mdx);
923178825Sdfr
924178825Sdfr	    if (r.u.response.success) {
925178825Sdfr		unsigned char hashhash[MD4_DIGEST_LENGTH];
926233294Sstas		EVP_MD_CTX *ctxp;
927178825Sdfr
928233294Sstas		ctxp = EVP_MD_CTX_create();
929233294Sstas
930178825Sdfr		/* hashhash */
931178825Sdfr		{
932233294Sstas		    EVP_DigestInit_ex(ctxp, EVP_md4(), NULL);
933233294Sstas		    EVP_DigestUpdate(ctxp,
934233294Sstas				     key->key.keyvalue.data,
935233294Sstas				     key->key.keyvalue.length);
936233294Sstas		    EVP_DigestFinal_ex(ctxp, hashhash, NULL);
937178825Sdfr		}
938178825Sdfr
939178825Sdfr		/* GenerateAuthenticatorResponse */
940233294Sstas		EVP_DigestInit_ex(ctxp, EVP_sha1(), NULL);
941233294Sstas		EVP_DigestUpdate(ctxp, hashhash, sizeof(hashhash));
942233294Sstas		EVP_DigestUpdate(ctxp, answer.data, answer.length);
943233294Sstas		EVP_DigestUpdate(ctxp, ms_chap_v2_magic1,
944233294Sstas				 sizeof(ms_chap_v2_magic1));
945233294Sstas		EVP_DigestFinal_ex(ctxp, md, NULL);
946178825Sdfr
947233294Sstas		EVP_DigestInit_ex(ctxp, EVP_sha1(), NULL);
948233294Sstas		EVP_DigestUpdate(ctxp, md, sizeof(md));
949233294Sstas		EVP_DigestUpdate(ctxp, challange, 8);
950233294Sstas		EVP_DigestUpdate(ctxp, ms_chap_v2_magic2,
951233294Sstas				 sizeof(ms_chap_v2_magic2));
952233294Sstas		EVP_DigestFinal_ex(ctxp, md, NULL);
953178825Sdfr
954178825Sdfr		r.u.response.rsp = calloc(1, sizeof(*r.u.response.rsp));
955178825Sdfr		if (r.u.response.rsp == NULL) {
956178825Sdfr		    free(answer.data);
957233294Sstas		    krb5_clear_error_message(context);
958233294Sstas		    EVP_MD_CTX_destroy(ctxp);
959178825Sdfr		    ret = ENOMEM;
960178825Sdfr		    goto out;
961178825Sdfr		}
962178825Sdfr
963178825Sdfr		hex_encode(md, sizeof(md), r.u.response.rsp);
964178825Sdfr		if (r.u.response.rsp == NULL) {
965178825Sdfr		    free(answer.data);
966233294Sstas		    krb5_clear_error_message(context);
967233294Sstas		    EVP_MD_CTX_destroy(ctxp);
968178825Sdfr		    ret = ENOMEM;
969178825Sdfr		    goto out;
970178825Sdfr		}
971178825Sdfr
972178825Sdfr		/* get_master, rfc 3079 3.4 */
973233294Sstas		EVP_DigestInit_ex(ctxp, EVP_sha1(), NULL);
974233294Sstas		EVP_DigestUpdate(ctxp, hashhash, 16);
975233294Sstas		EVP_DigestUpdate(ctxp, answer.data, answer.length);
976233294Sstas		EVP_DigestUpdate(ctxp, ms_rfc3079_magic1,
977233294Sstas				 sizeof(ms_rfc3079_magic1));
978233294Sstas		EVP_DigestFinal_ex(ctxp, md, NULL);
979178825Sdfr
980178825Sdfr		free(answer.data);
981178825Sdfr
982233294Sstas		EVP_MD_CTX_destroy(ctxp);
983233294Sstas
984233294Sstas		r.u.response.session_key =
985178825Sdfr		    calloc(1, sizeof(*r.u.response.session_key));
986178825Sdfr		if (r.u.response.session_key == NULL) {
987233294Sstas		    krb5_clear_error_message(context);
988178825Sdfr		    ret = ENOMEM;
989178825Sdfr		    goto out;
990178825Sdfr		}
991178825Sdfr
992178825Sdfr		ret = krb5_data_copy(r.u.response.session_key, md, 16);
993178825Sdfr		if (ret) {
994233294Sstas		    krb5_clear_error_message(context);
995178825Sdfr		    goto out;
996178825Sdfr		}
997178825Sdfr	    }
998178825Sdfr
999178825Sdfr	} else {
1000178825Sdfr	    r.element = choice_DigestRepInner_error;
1001233294Sstas	    asprintf(&r.u.error.reason, "Unsupported digest type %s",
1002178825Sdfr		     ireq.u.digestRequest.type);
1003178825Sdfr	    if (r.u.error.reason == NULL) {
1004178825Sdfr		ret = ENOMEM;
1005233294Sstas		krb5_set_error_message(context, ret, "malloc: out of memory");
1006178825Sdfr		goto out;
1007178825Sdfr	    }
1008178825Sdfr	    r.u.error.code = EINVAL;
1009178825Sdfr	}
1010178825Sdfr
1011178825Sdfr	kdc_log(context, config, 0, "Digest %s request successful %s",
1012178825Sdfr		ireq.u.digestRequest.type, ireq.u.digestRequest.username);
1013178825Sdfr
1014178825Sdfr	break;
1015178825Sdfr    }
1016178825Sdfr    case choice_DigestReqInner_ntlmInit:
1017178825Sdfr
1018178825Sdfr	if ((config->digests_allowed & (NTLM_V1|NTLM_V1_SESSION|NTLM_V2)) == 0) {
1019178825Sdfr	    kdc_log(context, config, 0, "NTLM not allowed");
1020178825Sdfr	    goto failed;
1021178825Sdfr	}
1022178825Sdfr
1023178825Sdfr	r.element = choice_DigestRepInner_ntlmInitReply;
1024178825Sdfr
1025178825Sdfr	r.u.ntlmInitReply.flags = NTLM_NEG_UNICODE;
1026178825Sdfr
1027178825Sdfr	if ((ireq.u.ntlmInit.flags & NTLM_NEG_UNICODE) == 0) {
1028178825Sdfr	    kdc_log(context, config, 0, "NTLM client have no unicode");
1029178825Sdfr	    goto failed;
1030178825Sdfr	}
1031178825Sdfr
1032178825Sdfr	if (ireq.u.ntlmInit.flags & NTLM_NEG_NTLM)
1033178825Sdfr	    r.u.ntlmInitReply.flags |= NTLM_NEG_NTLM;
1034178825Sdfr	else {
1035178825Sdfr	    kdc_log(context, config, 0, "NTLM client doesn't support NTLM");
1036178825Sdfr	    goto failed;
1037178825Sdfr	}
1038178825Sdfr
1039233294Sstas	r.u.ntlmInitReply.flags |=
1040178825Sdfr	    NTLM_NEG_TARGET |
1041178825Sdfr	    NTLM_TARGET_DOMAIN |
1042178825Sdfr	    NTLM_ENC_128;
1043178825Sdfr
1044178825Sdfr#define ALL					\
1045178825Sdfr	NTLM_NEG_SIGN|				\
1046178825Sdfr	    NTLM_NEG_SEAL|			\
1047178825Sdfr	    NTLM_NEG_ALWAYS_SIGN|		\
1048178825Sdfr	    NTLM_NEG_NTLM2_SESSION|		\
1049178825Sdfr	    NTLM_NEG_KEYEX
1050178825Sdfr
1051178825Sdfr	r.u.ntlmInitReply.flags |= (ireq.u.ntlmInit.flags & (ALL));
1052178825Sdfr
1053178825Sdfr#undef ALL
1054178825Sdfr
1055233294Sstas	r.u.ntlmInitReply.targetname =
1056178825Sdfr	    get_ntlm_targetname(context, client);
1057178825Sdfr	if (r.u.ntlmInitReply.targetname == NULL) {
1058178825Sdfr	    ret = ENOMEM;
1059233294Sstas	    krb5_set_error_message(context, ret, "malloc: out of memory");
1060178825Sdfr	    goto out;
1061178825Sdfr	}
1062178825Sdfr	r.u.ntlmInitReply.challange.data = malloc(8);
1063178825Sdfr	if (r.u.ntlmInitReply.challange.data == NULL) {
1064178825Sdfr	    ret = ENOMEM;
1065233294Sstas	    krb5_set_error_message(context, ret, "malloc: out of memory");
1066178825Sdfr	    goto out;
1067178825Sdfr	}
1068178825Sdfr	r.u.ntlmInitReply.challange.length = 8;
1069178825Sdfr	if (RAND_bytes(r.u.ntlmInitReply.challange.data,
1070233294Sstas		       r.u.ntlmInitReply.challange.length) != 1)
1071233294Sstas	    {
1072233294Sstas		ret = ENOMEM;
1073233294Sstas		krb5_set_error_message(context, ret, "out of random error");
1074233294Sstas		goto out;
1075233294Sstas	    }
1076178825Sdfr	/* XXX fix targetinfo */
1077178825Sdfr	ALLOC(r.u.ntlmInitReply.targetinfo);
1078178825Sdfr	if (r.u.ntlmInitReply.targetinfo == NULL) {
1079178825Sdfr	    ret = ENOMEM;
1080233294Sstas	    krb5_set_error_message(context, ret, "malloc: out of memory");
1081178825Sdfr	    goto out;
1082178825Sdfr	}
1083178825Sdfr
1084178825Sdfr	ret = fill_targetinfo(context,
1085178825Sdfr			      r.u.ntlmInitReply.targetname,
1086178825Sdfr			      client,
1087178825Sdfr			      r.u.ntlmInitReply.targetinfo);
1088178825Sdfr	if (ret) {
1089178825Sdfr	    ret = ENOMEM;
1090233294Sstas	    krb5_set_error_message(context, ret, "malloc: out of memory");
1091178825Sdfr	    goto out;
1092178825Sdfr	}
1093178825Sdfr
1094233294Sstas	/*
1095178825Sdfr	 * Save data encryted in opaque for the second part of the
1096178825Sdfr	 * ntlm authentication
1097178825Sdfr	 */
1098178825Sdfr	sp = krb5_storage_emem();
1099178825Sdfr	if (sp == NULL) {
1100178825Sdfr	    ret = ENOMEM;
1101233294Sstas	    krb5_set_error_message(context, ret, "malloc: out of memory");
1102178825Sdfr	    goto out;
1103178825Sdfr	}
1104233294Sstas
1105178825Sdfr	ret = krb5_storage_write(sp, r.u.ntlmInitReply.challange.data, 8);
1106178825Sdfr	if (ret != 8) {
1107178825Sdfr	    ret = ENOMEM;
1108233294Sstas	    krb5_set_error_message(context, ret, "storage write challange");
1109178825Sdfr	    goto out;
1110178825Sdfr	}
1111178825Sdfr	ret = krb5_store_uint32(sp, r.u.ntlmInitReply.flags);
1112178825Sdfr	if (ret) {
1113233294Sstas	    krb5_clear_error_message(context);
1114178825Sdfr	    goto out;
1115178825Sdfr	}
1116178825Sdfr
1117178825Sdfr	ret = krb5_storage_to_data(sp, &buf);
1118178825Sdfr	if (ret) {
1119233294Sstas	    krb5_clear_error_message(context);
1120178825Sdfr	    goto out;
1121178825Sdfr	}
1122178825Sdfr
1123178825Sdfr	ret = get_digest_key(context, config, server, &crypto);
1124178825Sdfr	if (ret)
1125178825Sdfr	    goto out;
1126178825Sdfr
1127178825Sdfr	ret = krb5_encrypt(context, crypto, KRB5_KU_DIGEST_OPAQUE,
1128178825Sdfr			   buf.data, buf.length, &r.u.ntlmInitReply.opaque);
1129178825Sdfr	krb5_data_free(&buf);
1130178825Sdfr	krb5_crypto_destroy(context, crypto);
1131178825Sdfr	crypto = NULL;
1132178825Sdfr	if (ret)
1133178825Sdfr	    goto out;
1134178825Sdfr
1135178825Sdfr	kdc_log(context, config, 0, "NTLM init from %s", from);
1136178825Sdfr
1137178825Sdfr	break;
1138178825Sdfr
1139178825Sdfr    case choice_DigestReqInner_ntlmRequest: {
1140178825Sdfr	krb5_principal clientprincipal;
1141178825Sdfr	unsigned char sessionkey[16];
1142178825Sdfr	unsigned char challange[8];
1143178825Sdfr	uint32_t flags;
1144178825Sdfr	Key *key = NULL;
1145178825Sdfr	int version;
1146233294Sstas
1147178825Sdfr	r.element = choice_DigestRepInner_ntlmResponse;
1148178825Sdfr	r.u.ntlmResponse.success = 0;
1149178825Sdfr	r.u.ntlmResponse.flags = 0;
1150178825Sdfr	r.u.ntlmResponse.sessionkey = NULL;
1151178825Sdfr	r.u.ntlmResponse.tickets = NULL;
1152178825Sdfr
1153178825Sdfr	/* get username */
1154178825Sdfr	ret = krb5_parse_name(context,
1155178825Sdfr			      ireq.u.ntlmRequest.username,
1156178825Sdfr			      &clientprincipal);
1157178825Sdfr	if (ret)
1158178825Sdfr	    goto failed;
1159178825Sdfr
1160178825Sdfr	ret = _kdc_db_fetch(context, config, clientprincipal,
1161233294Sstas			    HDB_F_GET_CLIENT, NULL, NULL, &user);
1162178825Sdfr	krb5_free_principal(context, clientprincipal);
1163178825Sdfr	if (ret) {
1164233294Sstas	    krb5_set_error_message(context, ret, "NTLM user %s not in database",
1165233294Sstas				   ireq.u.ntlmRequest.username);
1166178825Sdfr	    goto failed;
1167178825Sdfr	}
1168178825Sdfr
1169178825Sdfr	ret = get_digest_key(context, config, server, &crypto);
1170178825Sdfr	if (ret)
1171178825Sdfr	    goto failed;
1172178825Sdfr
1173178825Sdfr	ret = krb5_decrypt(context, crypto, KRB5_KU_DIGEST_OPAQUE,
1174178825Sdfr			   ireq.u.ntlmRequest.opaque.data,
1175178825Sdfr			   ireq.u.ntlmRequest.opaque.length, &buf);
1176178825Sdfr	krb5_crypto_destroy(context, crypto);
1177178825Sdfr	crypto = NULL;
1178178825Sdfr	if (ret) {
1179233294Sstas	    kdc_log(context, config, 0,
1180178825Sdfr		    "Failed to decrypt nonce from %s", from);
1181178825Sdfr	    goto failed;
1182178825Sdfr	}
1183178825Sdfr
1184178825Sdfr	sp = krb5_storage_from_data(&buf);
1185178825Sdfr	if (sp == NULL) {
1186178825Sdfr	    ret = ENOMEM;
1187233294Sstas	    krb5_set_error_message(context, ret, "malloc: out of memory");
1188178825Sdfr	    goto out;
1189178825Sdfr	}
1190233294Sstas
1191178825Sdfr	ret = krb5_storage_read(sp, challange, sizeof(challange));
1192178825Sdfr	if (ret != sizeof(challange)) {
1193178825Sdfr	    ret = ENOMEM;
1194233294Sstas	    krb5_set_error_message(context, ret, "NTLM storage read challange");
1195178825Sdfr	    goto out;
1196178825Sdfr	}
1197178825Sdfr	ret = krb5_ret_uint32(sp, &flags);
1198178825Sdfr	if (ret) {
1199233294Sstas	    krb5_set_error_message(context, ret, "NTLM storage read flags");
1200178825Sdfr	    goto out;
1201178825Sdfr	}
1202233294Sstas	krb5_storage_free(sp);
1203233294Sstas	sp = NULL;
1204178825Sdfr	krb5_data_free(&buf);
1205178825Sdfr
1206178825Sdfr	if ((flags & NTLM_NEG_NTLM) == 0) {
1207178825Sdfr	    ret = EINVAL;
1208233294Sstas	    krb5_set_error_message(context, ret, "NTLM not negotiated");
1209178825Sdfr	    goto out;
1210178825Sdfr	}
1211178825Sdfr
1212233294Sstas	ret = hdb_enctype2key(context, &user->entry,
1213178825Sdfr			      ETYPE_ARCFOUR_HMAC_MD5, &key);
1214178825Sdfr	if (ret) {
1215233294Sstas	    krb5_set_error_message(context, ret, "NTLM missing arcfour key");
1216178825Sdfr	    goto out;
1217178825Sdfr	}
1218178825Sdfr
1219178825Sdfr	/* check if this is NTLMv2 */
1220178825Sdfr	if (ireq.u.ntlmRequest.ntlm.length != 24) {
1221178825Sdfr	    struct ntlm_buf infotarget, answer;
1222178825Sdfr	    char *targetname;
1223178825Sdfr
1224178825Sdfr	    if ((config->digests_allowed & NTLM_V2) == 0) {
1225178825Sdfr		kdc_log(context, config, 0, "NTLM v2 not allowed");
1226178825Sdfr		goto out;
1227178825Sdfr	    }
1228178825Sdfr
1229178825Sdfr	    version = 2;
1230178825Sdfr
1231178825Sdfr	    targetname = get_ntlm_targetname(context, client);
1232178825Sdfr	    if (targetname == NULL) {
1233178825Sdfr		ret = ENOMEM;
1234233294Sstas		krb5_set_error_message(context, ret, "malloc: out of memory");
1235178825Sdfr		goto out;
1236178825Sdfr	    }
1237178825Sdfr
1238178825Sdfr	    answer.length = ireq.u.ntlmRequest.ntlm.length;
1239178825Sdfr	    answer.data = ireq.u.ntlmRequest.ntlm.data;
1240178825Sdfr
1241178825Sdfr	    ret = heim_ntlm_verify_ntlm2(key->key.keyvalue.data,
1242178825Sdfr					 key->key.keyvalue.length,
1243178825Sdfr					 ireq.u.ntlmRequest.username,
1244178825Sdfr					 targetname,
1245178825Sdfr					 0,
1246178825Sdfr					 challange,
1247178825Sdfr					 &answer,
1248178825Sdfr					 &infotarget,
1249178825Sdfr					 sessionkey);
1250178825Sdfr	    free(targetname);
1251178825Sdfr	    if (ret) {
1252233294Sstas		krb5_set_error_message(context, ret, "NTLM v2 verify failed");
1253178825Sdfr		goto failed;
1254178825Sdfr	    }
1255178825Sdfr
1256178825Sdfr	    /* XXX verify infotarget matches client (checksum ?) */
1257178825Sdfr
1258178825Sdfr	    free(infotarget.data);
1259178825Sdfr	    /* */
1260178825Sdfr
1261178825Sdfr	} else {
1262178825Sdfr	    struct ntlm_buf answer;
1263178825Sdfr
1264178825Sdfr	    version = 1;
1265178825Sdfr
1266178825Sdfr	    if (flags & NTLM_NEG_NTLM2_SESSION) {
1267178825Sdfr		unsigned char sessionhash[MD5_DIGEST_LENGTH];
1268233294Sstas		EVP_MD_CTX *ctx;
1269233294Sstas
1270178825Sdfr		if ((config->digests_allowed & NTLM_V1_SESSION) == 0) {
1271178825Sdfr		    kdc_log(context, config, 0, "NTLM v1-session not allowed");
1272178825Sdfr		    ret = EINVAL;
1273178825Sdfr		    goto failed;
1274178825Sdfr		}
1275178825Sdfr
1276178825Sdfr		if (ireq.u.ntlmRequest.lm.length != 24) {
1277178825Sdfr		    ret = EINVAL;
1278233294Sstas		    krb5_set_error_message(context, ret, "LM hash have wrong length "
1279233294Sstas					   "for NTLM session key");
1280178825Sdfr		    goto failed;
1281178825Sdfr		}
1282233294Sstas
1283233294Sstas		ctx = EVP_MD_CTX_create();
1284233294Sstas
1285233294Sstas		EVP_DigestInit_ex(ctx, EVP_md5(), NULL);
1286233294Sstas
1287233294Sstas		EVP_DigestUpdate(ctx, challange, sizeof(challange));
1288233294Sstas		EVP_DigestUpdate(ctx, ireq.u.ntlmRequest.lm.data, 8);
1289233294Sstas		EVP_DigestFinal_ex(ctx, sessionhash, NULL);
1290178825Sdfr		memcpy(challange, sessionhash, sizeof(challange));
1291233294Sstas
1292233294Sstas		EVP_MD_CTX_destroy(ctx);
1293233294Sstas
1294178825Sdfr	    } else {
1295178825Sdfr		if ((config->digests_allowed & NTLM_V1) == 0) {
1296178825Sdfr		    kdc_log(context, config, 0, "NTLM v1 not allowed");
1297178825Sdfr		    goto failed;
1298178825Sdfr		}
1299178825Sdfr	    }
1300233294Sstas
1301178825Sdfr	    ret = heim_ntlm_calculate_ntlm1(key->key.keyvalue.data,
1302178825Sdfr					    key->key.keyvalue.length,
1303178825Sdfr					    challange, &answer);
1304178825Sdfr	    if (ret) {
1305233294Sstas		krb5_set_error_message(context, ret, "NTLM missing arcfour key");
1306178825Sdfr		goto failed;
1307178825Sdfr	    }
1308233294Sstas
1309178825Sdfr	    if (ireq.u.ntlmRequest.ntlm.length != answer.length ||
1310178825Sdfr		memcmp(ireq.u.ntlmRequest.ntlm.data, answer.data, answer.length) != 0)
1311233294Sstas		{
1312233294Sstas		    free(answer.data);
1313233294Sstas		    ret = EINVAL;
1314233294Sstas		    krb5_set_error_message(context, ret, "NTLM hash mismatch");
1315233294Sstas		    goto failed;
1316233294Sstas		}
1317178825Sdfr	    free(answer.data);
1318178825Sdfr
1319178825Sdfr	    {
1320233294Sstas		EVP_MD_CTX *ctx;
1321178825Sdfr
1322233294Sstas		ctx = EVP_MD_CTX_create();
1323233294Sstas
1324233294Sstas		EVP_DigestInit_ex(ctx, EVP_md4(), NULL);
1325233294Sstas		EVP_DigestUpdate(ctx,
1326233294Sstas				 key->key.keyvalue.data,
1327233294Sstas				 key->key.keyvalue.length);
1328233294Sstas		EVP_DigestFinal_ex(ctx, sessionkey, NULL);
1329233294Sstas
1330233294Sstas		EVP_MD_CTX_destroy(ctx);
1331178825Sdfr	    }
1332178825Sdfr	}
1333178825Sdfr
1334178825Sdfr	if (ireq.u.ntlmRequest.sessionkey) {
1335178825Sdfr	    unsigned char masterkey[MD4_DIGEST_LENGTH];
1336233294Sstas	    EVP_CIPHER_CTX rc4;
1337178825Sdfr	    size_t len;
1338233294Sstas
1339178825Sdfr	    if ((flags & NTLM_NEG_KEYEX) == 0) {
1340178825Sdfr		ret = EINVAL;
1341233294Sstas		krb5_set_error_message(context, ret,
1342233294Sstas				       "NTLM client failed to neg key "
1343233294Sstas				       "exchange but still sent key");
1344178825Sdfr		goto failed;
1345178825Sdfr	    }
1346233294Sstas
1347178825Sdfr	    len = ireq.u.ntlmRequest.sessionkey->length;
1348178825Sdfr	    if (len != sizeof(masterkey)){
1349233294Sstas		ret = EINVAL;
1350233294Sstas		krb5_set_error_message(context, ret,
1351233294Sstas				       "NTLM master key wrong length: %lu",
1352233294Sstas				       (unsigned long)len);
1353178825Sdfr		goto failed;
1354178825Sdfr	    }
1355233294Sstas
1356233294Sstas
1357233294Sstas	    EVP_CIPHER_CTX_init(&rc4);
1358233294Sstas	    EVP_CipherInit_ex(&rc4, EVP_rc4(), NULL, sessionkey, NULL, 1);
1359233294Sstas	    EVP_Cipher(&rc4,
1360233294Sstas		       masterkey, ireq.u.ntlmRequest.sessionkey->data,
1361233294Sstas		       sizeof(masterkey));
1362233294Sstas	    EVP_CIPHER_CTX_cleanup(&rc4);
1363233294Sstas
1364233294Sstas	    r.u.ntlmResponse.sessionkey =
1365178825Sdfr		malloc(sizeof(*r.u.ntlmResponse.sessionkey));
1366178825Sdfr	    if (r.u.ntlmResponse.sessionkey == NULL) {
1367233294Sstas		ret = EINVAL;
1368233294Sstas		krb5_set_error_message(context, ret, "malloc: out of memory");
1369178825Sdfr		goto out;
1370178825Sdfr	    }
1371233294Sstas
1372178825Sdfr	    ret = krb5_data_copy(r.u.ntlmResponse.sessionkey,
1373178825Sdfr				 masterkey, sizeof(masterkey));
1374178825Sdfr	    if (ret) {
1375233294Sstas		krb5_set_error_message(context, ret, "malloc: out of memory");
1376178825Sdfr		goto out;
1377178825Sdfr	    }
1378178825Sdfr	}
1379178825Sdfr
1380178825Sdfr	r.u.ntlmResponse.success = 1;
1381178825Sdfr	kdc_log(context, config, 0, "NTLM version %d successful for %s",
1382178825Sdfr		version, ireq.u.ntlmRequest.username);
1383178825Sdfr	break;
1384178825Sdfr    }
1385178825Sdfr    case choice_DigestReqInner_supportedMechs:
1386178825Sdfr
1387178825Sdfr	kdc_log(context, config, 0, "digest supportedMechs from %s", from);
1388178825Sdfr
1389178825Sdfr	r.element = choice_DigestRepInner_supportedMechs;
1390178825Sdfr	memset(&r.u.supportedMechs, 0, sizeof(r.u.supportedMechs));
1391178825Sdfr
1392178825Sdfr	if (config->digests_allowed & NTLM_V1)
1393178825Sdfr	    r.u.supportedMechs.ntlm_v1 = 1;
1394178825Sdfr	if (config->digests_allowed & NTLM_V1_SESSION)
1395178825Sdfr	    r.u.supportedMechs.ntlm_v1_session = 1;
1396178825Sdfr	if (config->digests_allowed & NTLM_V2)
1397178825Sdfr	    r.u.supportedMechs.ntlm_v2 = 1;
1398178825Sdfr	if (config->digests_allowed & DIGEST_MD5)
1399178825Sdfr	    r.u.supportedMechs.digest_md5 = 1;
1400178825Sdfr	if (config->digests_allowed & CHAP_MD5)
1401178825Sdfr	    r.u.supportedMechs.chap_md5 = 1;
1402178825Sdfr	if (config->digests_allowed & MS_CHAP_V2)
1403178825Sdfr	    r.u.supportedMechs.ms_chap_v2 = 1;
1404178825Sdfr	break;
1405178825Sdfr
1406178825Sdfr    default: {
1407233294Sstas	const char *s;
1408178825Sdfr	ret = EINVAL;
1409233294Sstas	krb5_set_error_message(context, ret, "unknown operation to digest");
1410178825Sdfr
1411233294Sstas	failed:
1412178825Sdfr
1413178825Sdfr	s = krb5_get_error_message(context, ret);
1414178825Sdfr	if (s == NULL) {
1415233294Sstas	    krb5_clear_error_message(context);
1416178825Sdfr	    goto out;
1417178825Sdfr	}
1418233294Sstas
1419178825Sdfr	kdc_log(context, config, 0, "Digest failed with: %s", s);
1420178825Sdfr
1421178825Sdfr	r.element = choice_DigestRepInner_error;
1422178825Sdfr	r.u.error.reason = strdup("unknown error");
1423233294Sstas	krb5_free_error_message(context, s);
1424178825Sdfr	if (r.u.error.reason == NULL) {
1425178825Sdfr	    ret = ENOMEM;
1426233294Sstas	    krb5_set_error_message(context, ret, "malloc: out of memory");
1427178825Sdfr	    goto out;
1428178825Sdfr	}
1429178825Sdfr	r.u.error.code = EINVAL;
1430178825Sdfr	break;
1431178825Sdfr    }
1432178825Sdfr    }
1433178825Sdfr
1434178825Sdfr    ASN1_MALLOC_ENCODE(DigestRepInner, buf.data, buf.length, &r, &size, ret);
1435178825Sdfr    if (ret) {
1436233294Sstas	krb5_set_error_message(context, ret, "Failed to encode inner digest reply");
1437178825Sdfr	goto out;
1438178825Sdfr    }
1439178825Sdfr    if (size != buf.length)
1440178825Sdfr	krb5_abortx(context, "ASN1 internal error");
1441178825Sdfr
1442178825Sdfr    krb5_auth_con_addflags(context, ac, KRB5_AUTH_CONTEXT_USE_SUBKEY, NULL);
1443178825Sdfr
1444178825Sdfr    ret = krb5_mk_rep (context, ac, &rep.apRep);
1445178825Sdfr    if (ret)
1446178825Sdfr	goto out;
1447178825Sdfr
1448178825Sdfr    {
1449178825Sdfr	krb5_keyblock *key;
1450178825Sdfr
1451178825Sdfr	ret = krb5_auth_con_getlocalsubkey(context, ac, &key);
1452178825Sdfr	if (ret)
1453178825Sdfr	    goto out;
1454178825Sdfr
1455178825Sdfr	ret = krb5_crypto_init(context, key, 0, &crypto);
1456178825Sdfr	krb5_free_keyblock (context, key);
1457178825Sdfr	if (ret)
1458178825Sdfr	    goto out;
1459178825Sdfr    }
1460178825Sdfr
1461233294Sstas    ret = krb5_encrypt_EncryptedData(context, crypto, KRB5_KU_DIGEST_ENCRYPT,
1462178825Sdfr				     buf.data, buf.length, 0,
1463178825Sdfr				     &rep.innerRep);
1464233294Sstas
1465178825Sdfr    ASN1_MALLOC_ENCODE(DigestREP, reply->data, reply->length, &rep, &size, ret);
1466178825Sdfr    if (ret) {
1467233294Sstas	krb5_set_error_message(context, ret, "Failed to encode digest reply");
1468178825Sdfr	goto out;
1469178825Sdfr    }
1470178825Sdfr    if (size != reply->length)
1471178825Sdfr	krb5_abortx(context, "ASN1 internal error");
1472178825Sdfr
1473233294Sstas
1474233294Sstas out:
1475178825Sdfr    if (ac)
1476178825Sdfr	krb5_auth_con_free(context, ac);
1477178825Sdfr    if (ret)
1478178825Sdfr	krb5_warn(context, ret, "Digest request from %s failed", from);
1479178825Sdfr    if (ticket)
1480178825Sdfr	krb5_free_ticket(context, ticket);
1481178825Sdfr    if (id)
1482178825Sdfr	krb5_kt_close(context, id);
1483178825Sdfr    if (crypto)
1484178825Sdfr	krb5_crypto_destroy(context, crypto);
1485178825Sdfr    if (sp)
1486178825Sdfr	krb5_storage_free(sp);
1487178825Sdfr    if (user)
1488178825Sdfr	_kdc_free_ent (context, user);
1489178825Sdfr    if (server)
1490178825Sdfr	_kdc_free_ent (context, server);
1491178825Sdfr    if (client)
1492178825Sdfr	_kdc_free_ent (context, client);
1493178825Sdfr    if (password) {
1494178825Sdfr	memset(password, 0, strlen(password));
1495178825Sdfr	free (password);
1496178825Sdfr    }
1497178825Sdfr    if (client_name)
1498178825Sdfr	free (client_name);
1499178825Sdfr    krb5_data_free(&buf);
1500178825Sdfr    krb5_data_free(&serverNonce);
1501233294Sstas    free_Checksum(&res);
1502178825Sdfr    free_DigestREP(&rep);
1503178825Sdfr    free_DigestRepInner(&r);
1504178825Sdfr    free_DigestReqInner(&ireq);
1505178825Sdfr
1506178825Sdfr    return ret;
1507178825Sdfr}
1508233294Sstas
1509233294Sstas#endif /* DIGEST */
1510