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