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