1/*
2 * Copyright (c) 2006 - 2008 Kungliga Tekniska Högskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 *
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 *
17 * 3. Neither the name of the Institute nor the names of its contributors
18 *    may be used to endorse or promote products derived from this software
19 *    without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
32 */
33
34#include "ntlm.h"
35#include <fnmatch.h>
36
37static int
38validate_hostname(ntlm_name name)
39{
40    CFArrayRef array;
41    CFStringRef el;
42    CFIndex n, max;
43    char *str;
44    int res = 0;
45
46    if (name->domain == NULL)
47	return 0;
48
49    array = _gss_mg_copy_key(CFSTR("com.apple.GSS.NTLM"), CFSTR("AllowedHosts"));
50    if (array == NULL)
51	return 1;
52
53    if (CFGetTypeID(array) != CFArrayGetTypeID()) {
54	_gss_mg_log(1, "NTLM: invalid type of AllowedHosts");
55	CFRelease(array);
56	return 0;
57    }
58
59    max = CFArrayGetCount(array);
60
61    for (n = 0, res = 0; n < max && !res; n++) {
62	el = CFArrayGetValueAtIndex(array, n);
63	if (el == NULL || CFGetTypeID(el) != CFStringGetTypeID())
64	    continue;
65	str = rk_cfstring2cstring(el);
66	if (str == NULL)
67	    continue;
68	res = (fnmatch(str, name->domain, FNM_CASEFOLD) == 0);
69	free(str);
70    }
71
72    CFRelease(array);
73
74    return res;
75}
76
77
78static OM_uint32
79_gss_ntlm_copy_cred(OM_uint32 * minor, ntlm_cred from, ntlm_cred *to)
80{
81    return _gss_ntlm_duplicate_name(minor, (gss_name_t)from, (gss_name_t *)to);
82}
83
84OM_uint32
85_gss_ntlm_init_sec_context(OM_uint32 * minor_status,
86			   const gss_cred_id_t initiator_cred_handle,
87			   gss_ctx_id_t * context_handle,
88			   const gss_name_t target_name,
89			   const gss_OID mech_type,
90			   OM_uint32 req_flags,
91			   OM_uint32 time_req,
92			   const gss_channel_bindings_t input_chan_bindings,
93			   const gss_buffer_t input_token,
94			   gss_OID * actual_mech_type,
95			   gss_buffer_t output_token,
96			   OM_uint32 * ret_flags,
97			   OM_uint32 * time_rec)
98{
99    OM_uint32 major;
100    ntlm_ctx ctx;
101    ntlm_name name = (ntlm_name) target_name;
102
103    *minor_status = 0;
104
105    if (ret_flags)
106	*ret_flags = 0;
107    if (time_rec)
108	*time_rec = 0;
109    if (actual_mech_type)
110	*actual_mech_type = GSS_C_NO_OID;
111    if (name == NULL)
112	return GSS_S_BAD_NAME;
113
114    /*
115     * Check that NTLM is enabled before going forward
116     */
117
118    if (!gss_mo_get(GSS_NTLM_MECHANISM, GSS_C_NTLM_V1, NULL) &&
119	!gss_mo_get(GSS_NTLM_MECHANISM, GSS_C_NTLM_V2, NULL))
120    {
121	*minor_status = 0;
122	return GSS_S_UNAVAILABLE;
123    }
124
125    ctx = (ntlm_ctx) *context_handle;
126
127    if (actual_mech_type)
128	*actual_mech_type = GSS_NTLM_MECHANISM;
129    if (ret_flags)
130	*ret_flags = 0;
131
132    if (ctx == NULL) {
133	struct ntlm_type1 type1;
134	struct ntlm_buf data;
135	uint32_t flags = 0;
136	int ret;
137
138	if (!validate_hostname(name))
139	    return gss_mg_set_error_string(GSS_NTLM_MECHANISM, GSS_S_FAILURE,
140					   (*minor_status = EAUTH),
141					   "Not allowed to use NTLM to host %s",
142					   name->domain ? name->domain : "???");
143
144	ctx = calloc(1, sizeof(*ctx));
145	if (ctx == NULL) {
146	    *minor_status = ENOMEM;
147	    return gss_mg_set_error_string(GSS_NTLM_MECHANISM, GSS_S_FAILURE,
148					   ENOMEM, "out of memory");
149	}
150	*context_handle = (gss_ctx_id_t) ctx;
151
152	ctx->status |= STATUS_CLIENT;
153
154	if (initiator_cred_handle != GSS_C_NO_CREDENTIAL) {
155	    ntlm_cred cred = (ntlm_cred) initiator_cred_handle;
156	    major = _gss_ntlm_copy_cred(minor_status, cred, &ctx->client);
157	} else {
158	    ntlm_name_desc client_name;
159	    client_name.user = "";
160	    client_name.domain = "";
161	    client_name.flags = 0;
162
163	    major = _gss_ntlm_have_cred(minor_status, &client_name, &ctx->client);
164	}
165
166	if (major) {
167	    OM_uint32 junk;
168	    _gss_ntlm_delete_sec_context(&junk, context_handle, NULL);
169	    return major;
170	}
171
172	major = _gss_ntlm_duplicate_name(minor_status, (gss_name_t)name, &ctx->targetname);
173	if (major) {
174	    OM_uint32 junk;
175	    _gss_ntlm_delete_sec_context(&junk, context_handle, NULL);
176	    return major;
177	}
178
179	ret = asprintf(&ctx->clientsuppliedtargetname, "%s/%s", name->user, name->domain);
180	if (ret < 0 || ctx->clientsuppliedtargetname == NULL) {
181	    OM_uint32 junk;
182	    _gss_ntlm_delete_sec_context(&junk, context_handle, NULL);
183	    return major;
184	}
185
186	flags |= NTLM_NEG_UNICODE;
187	flags |= NTLM_NEG_NTLM;
188	flags |= NTLM_NEG_TARGET;
189	flags |= NTLM_NEG_VERSION;
190
191	if (req_flags & GSS_C_ANON_FLAG) {
192	    flags |= NTLM_NEG_ANONYMOUS;
193	} else {
194	    /* can't work with anon cred, just stop now */
195	    if ((ctx->client->flags & NTLM_ANON_NAME) != 0) {
196		OM_uint32 junk;
197		_gss_ntlm_delete_sec_context(&junk, context_handle, NULL);
198		gss_mg_set_error_string(GSS_NTLM_MECHANISM, GSS_S_NO_CRED, 0,
199					"Cant authenticate with anon name");
200		*minor_status = 0;
201		return GSS_S_NO_CRED;
202	    }
203
204	    if (req_flags & GSS_C_CONF_FLAG) {
205		flags |= NTLM_NEG_SEAL;
206		flags |= NTLM_NEG_SIGN;
207		flags |= NTLM_NEG_ALWAYS_SIGN;
208	    }
209	    if (req_flags & GSS_C_INTEG_FLAG) {
210		flags |= NTLM_NEG_SIGN;
211		flags |= NTLM_NEG_ALWAYS_SIGN;
212	    }
213
214
215	    flags |= NTLM_NEG_NTLM2_SESSION;
216	    flags |= NTLM_ENC_128;
217	    flags |= NTLM_NEG_KEYEX;
218	    flags |= NTLM_NEG_TARGET_INFO;
219	}
220
221	memset(&type1, 0, sizeof(type1));
222
223	type1.flags = flags;
224	type1.domain = NULL;
225	type1.hostname = NULL;
226	type1.os[0] = 0x0601b01d;
227	type1.os[1] = 0x0000000f;
228
229	ret = heim_ntlm_encode_type1(&type1, &data);
230	if (ret) {
231	    _gss_ntlm_delete_sec_context(minor_status, context_handle, NULL);
232	    *minor_status = ret;
233	    return GSS_S_FAILURE;
234	}
235	output_token->value = data.data;
236	output_token->length = data.length;
237
238	ret = krb5_data_copy(&ctx->type1, data.data, data.length);
239	if (ret) {
240	    _gss_ntlm_delete_sec_context(minor_status, context_handle, NULL);
241	    *minor_status = ret;
242	    return GSS_S_FAILURE;
243	}
244
245	ctx->flags = flags;
246
247	_gss_mg_log(1, "ntlm-isc-type1: %s\\%s",
248		    ctx->client->domain, ctx->client->user);
249
250	return GSS_S_CONTINUE_NEEDED;
251
252    } else if (ctx->flags & NTLM_NEG_ANONYMOUS) {
253	struct ntlm_type2 type2;
254	struct ntlm_type3 type3;
255	struct ntlm_buf ndata;
256	int ret;
257
258	memset(&type2, 0, sizeof(type2));
259	memset(&type3, 0, sizeof(type3));
260
261	if (input_token == GSS_C_NO_BUFFER)
262	    return GSS_S_FAILURE;
263
264	ndata.data = input_token->value;
265	ndata.length = input_token->length;
266
267	ret = heim_ntlm_decode_type2(&ndata, &type2);
268	if (ret) {
269	    _gss_ntlm_delete_sec_context(minor_status, context_handle, NULL);
270	    *minor_status = ret;
271	    return GSS_S_FAILURE;
272	}
273
274	type3.username = "";
275	type3.flags = ctx->flags;
276	type3.targetname = "";
277	type3.ws = "";
278
279	/* This is crazy, but apperently needed ? */
280	type3.lm.data = "\x00";
281	type3.lm.length = 1;
282
283	ret = heim_ntlm_encode_type3(&type3, &ndata, NULL);
284	heim_ntlm_free_type2(&type2);
285	if (ret) {
286	    _gss_ntlm_delete_sec_context(minor_status, context_handle, NULL);
287	    *minor_status = ret;
288	    return GSS_S_FAILURE;
289	}
290
291	output_token->value = ndata.data;
292	output_token->length = ndata.length;
293
294	ctx->gssflags |= GSS_C_ANON_FLAG;
295
296	ctx->srcname = _gss_ntlm_create_name(minor_status, "", "", 0);
297	if (ctx->srcname == NULL) {
298	    _gss_ntlm_delete_sec_context(minor_status, context_handle, NULL);
299	    return GSS_S_FAILURE;
300	}
301
302	_gss_mg_log(1, "ntlm-isc-type3: anonymous");
303
304    } else {
305	krb5_context context;
306	krb5_error_code ret;
307	krb5_storage *request, *response = NULL;
308	krb5_data response_data, data, cb;
309	char *ruser = NULL, *rdomain = NULL;
310	uint8_t channelbinding[16];
311
312	data.data = input_token->value;
313	data.length = input_token->length;
314
315	if (input_chan_bindings) {
316
317	    major = gss_mg_gen_cb(minor_status, input_chan_bindings,
318				  channelbinding, NULL);
319	    if (major) {
320		OM_uint32 junk;
321		_gss_ntlm_delete_sec_context(&junk, context_handle, NULL);
322		return major;
323	    }
324
325	    cb.data = channelbinding;
326	    cb.length = sizeof(channelbinding);
327	} else
328	    krb5_data_zero(&cb);
329
330	ret = krb5_init_context(&context);
331	if (ret) {
332	    _gss_ntlm_delete_sec_context(minor_status, context_handle, NULL);
333	    return ret;
334	}
335	ret = krb5_kcm_storage_request(context, KCM_OP_DO_NTLM_AUTH, &request);
336	if (ret) {
337	    krb5_free_context(context);
338	    _gss_ntlm_delete_sec_context(minor_status, context_handle, NULL);
339	    *minor_status = ret;
340	    return GSS_S_FAILURE;
341	}
342	ret = krb5_store_stringz(request, ctx->client->user);
343	if (ret == 0)
344	    ret = krb5_store_stringz(request, ctx->client->domain);
345	if (ret == 0)
346	    ret = krb5_store_data(request, data);
347	if (ret == 0)
348	    ret = krb5_store_data(request, cb);
349	if (ret == 0)
350	    ret = krb5_store_data(request, ctx->type1);
351	if (ret == 0)
352	    ret = krb5_store_stringz(request, ctx->clientsuppliedtargetname);
353	if (ret == 0)
354	    ret = krb5_store_uint32(request, ctx->flags);
355
356	if (ret == 0)
357	    ret = krb5_kcm_call(context, request, &response, &response_data);
358
359	krb5_free_context(context);
360	krb5_storage_free(request);
361	if (ret) {
362	    _gss_ntlm_delete_sec_context(minor_status, context_handle, NULL);
363	    *minor_status = ret;
364	    return gss_mg_set_error_string(GSS_NTLM_MECHANISM, GSS_S_FAILURE, ret,
365					   "failed to create ntlm response");
366	}
367	ret = krb5_ret_data(response, &data);
368	if (ret == 0)
369	    ret = krb5_ret_uint32(response, &ctx->kcmflags);
370	if (ret == 0)
371	    ret = krb5_ret_data(response, &ctx->sessionkey);
372	if (ret == 0)
373	    ret = krb5_ret_string(response, &ruser);
374	if (ret == 0)
375	    ret = krb5_ret_string(response, &rdomain);
376	if (ret == 0)
377	    ret = krb5_ret_uint32(response, &ctx->flags);
378	if (ret)
379	    ret = KRB5_CC_IO;
380
381	krb5_storage_free(response);
382	krb5_data_free(&response_data);
383
384	_gss_mg_log(1, "ntlm-isc-type3: kcm returned %d", ret);
385
386	if (ret) {
387	    _gss_ntlm_delete_sec_context(minor_status, context_handle, NULL);
388	    krb5_data_free(&data);
389	    free(ruser);
390	    free(rdomain);
391	    *minor_status = ret;
392	    return gss_mg_set_error_string(GSS_NTLM_MECHANISM, GSS_S_FAILURE, ret,
393					   "failed parse kcm reply");
394	}
395
396	_gss_mg_log(1, "ntlm-isc-type3: %s\\%s", rdomain, ruser);
397
398	ctx->srcname = _gss_ntlm_create_name(minor_status, ruser, rdomain, 0);
399	free(ruser);
400	free(rdomain);
401	if (ctx->srcname == NULL) {
402	    _gss_ntlm_delete_sec_context(minor_status, context_handle, NULL);
403	    krb5_data_free(&data);
404	    return GSS_S_FAILURE;
405	}
406
407	output_token->length = data.length;
408	output_token->value = data.data;
409
410    }
411
412    ctx->status |= STATUS_OPEN;
413
414    /*
415     * Now that we have a session key, let setup crypto layer
416     */
417
418    _gss_ntlm_set_keys(ctx);
419
420    if (ret_flags)
421	*ret_flags = ctx->gssflags;
422
423    if (time_rec)
424	*time_rec = GSS_C_INDEFINITE;
425
426
427
428    return GSS_S_COMPLETE;
429}
430