1178825Sdfr/*
2178825Sdfr * Copyright (c) 2006 Kungliga Tekniska H�gskolan
3178825Sdfr * (Royal Institute of Technology, Stockholm, Sweden).
4178825Sdfr * All rights reserved.
5178825Sdfr *
6178825Sdfr * Redistribution and use in source and binary forms, with or without
7178825Sdfr * modification, are permitted provided that the following conditions
8178825Sdfr * are met:
9178825Sdfr *
10178825Sdfr * 1. Redistributions of source code must retain the above copyright
11178825Sdfr *    notice, this list of conditions and the following disclaimer.
12178825Sdfr *
13178825Sdfr * 2. Redistributions in binary form must reproduce the above copyright
14178825Sdfr *    notice, this list of conditions and the following disclaimer in the
15178825Sdfr *    documentation and/or other materials provided with the distribution.
16178825Sdfr *
17178825Sdfr * 3. Neither the name of the Institute nor the names of its contributors
18178825Sdfr *    may be used to endorse or promote products derived from this software
19178825Sdfr *    without specific prior written permission.
20178825Sdfr *
21178825Sdfr * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
22178825Sdfr * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23178825Sdfr * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24178825Sdfr * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
25178825Sdfr * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26178825Sdfr * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27178825Sdfr * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28178825Sdfr * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29178825Sdfr * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30178825Sdfr * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31178825Sdfr * SUCH DAMAGE.
32178825Sdfr */
33178825Sdfr
34178825Sdfr#include "ntlm/ntlm.h"
35178825Sdfr
36178825SdfrRCSID("$Id: init_sec_context.c 22382 2007-12-30 12:13:17Z lha $");
37178825Sdfr
38178825Sdfrstatic int
39178825Sdfrfrom_file(const char *fn, const char *target_domain,
40178825Sdfr	  char **username, struct ntlm_buf *key)
41178825Sdfr{
42178825Sdfr    char *str, buf[1024];
43178825Sdfr    FILE *f;
44178825Sdfr
45178825Sdfr    f = fopen(fn, "r");
46178825Sdfr    if (f == NULL)
47178825Sdfr	return ENOENT;
48178825Sdfr
49178825Sdfr    while (fgets(buf, sizeof(buf), f) != NULL) {
50178825Sdfr	char *d, *u, *p;
51178825Sdfr	buf[strcspn(buf, "\r\n")] = '\0';
52178825Sdfr	if (buf[0] == '#')
53178825Sdfr	    continue;
54178825Sdfr	str = NULL;
55178825Sdfr	d = strtok_r(buf, ":", &str);
56178825Sdfr	if (d && strcasecmp(target_domain, d) != 0)
57178825Sdfr	    continue;
58178825Sdfr	u = strtok_r(NULL, ":", &str);
59178825Sdfr	p = strtok_r(NULL, ":", &str);
60178825Sdfr	if (u == NULL || p == NULL)
61178825Sdfr	    continue;
62178825Sdfr
63178825Sdfr	*username = strdup(u);
64178825Sdfr
65178825Sdfr	heim_ntlm_nt_key(p, key);
66178825Sdfr
67178825Sdfr	memset(buf, 0, sizeof(buf));
68178825Sdfr	fclose(f);
69178825Sdfr	return 0;
70178825Sdfr    }
71178825Sdfr    memset(buf, 0, sizeof(buf));
72178825Sdfr    fclose(f);
73178825Sdfr    return ENOENT;
74178825Sdfr}
75178825Sdfr
76178825Sdfrstatic int
77178825Sdfrget_user_file(const ntlm_name target_name,
78178825Sdfr	      char **username, struct ntlm_buf *key)
79178825Sdfr{
80178825Sdfr    const char *fn;
81178825Sdfr
82178825Sdfr    if (issuid())
83178825Sdfr	return ENOENT;
84178825Sdfr
85178825Sdfr    fn = getenv("NTLM_USER_FILE");
86178825Sdfr    if (fn == NULL)
87178825Sdfr	return ENOENT;
88178825Sdfr    if (from_file(fn, target_name->domain, username, key) == 0)
89178825Sdfr	return 0;
90178825Sdfr
91178825Sdfr    return ENOENT;
92178825Sdfr}
93178825Sdfr
94178825Sdfr/*
95178825Sdfr * Pick up the ntlm cred from the default krb5 credential cache.
96178825Sdfr */
97178825Sdfr
98178825Sdfrstatic int
99178825Sdfrget_user_ccache(const ntlm_name name, char **username, struct ntlm_buf *key)
100178825Sdfr{
101178825Sdfr    krb5_principal client;
102178825Sdfr    krb5_context context = NULL;
103178825Sdfr    krb5_error_code ret;
104178825Sdfr    krb5_ccache id = NULL;
105178825Sdfr    krb5_creds mcreds, creds;
106178825Sdfr
107178825Sdfr    *username = NULL;
108178825Sdfr    key->length = 0;
109178825Sdfr    key->data = NULL;
110178825Sdfr
111178825Sdfr    memset(&creds, 0, sizeof(creds));
112178825Sdfr    memset(&mcreds, 0, sizeof(mcreds));
113178825Sdfr
114178825Sdfr    ret = krb5_init_context(&context);
115178825Sdfr    if (ret)
116178825Sdfr	return ret;
117178825Sdfr
118178825Sdfr    ret = krb5_cc_default(context, &id);
119178825Sdfr    if (ret)
120178825Sdfr	goto out;
121178825Sdfr
122178825Sdfr    ret = krb5_cc_get_principal(context, id, &client);
123178825Sdfr    if (ret)
124178825Sdfr	goto out;
125178825Sdfr
126178825Sdfr    ret = krb5_unparse_name_flags(context, client,
127178825Sdfr				  KRB5_PRINCIPAL_UNPARSE_NO_REALM,
128178825Sdfr				  username);
129178825Sdfr    if (ret)
130178825Sdfr	goto out;
131178825Sdfr
132178825Sdfr    ret = krb5_make_principal(context, &mcreds.server,
133178825Sdfr			      krb5_principal_get_realm(context, client),
134178825Sdfr			      "@ntlm-key", name->domain, NULL);
135178825Sdfr    krb5_free_principal(context, client);
136178825Sdfr    if (ret)
137178825Sdfr	goto out;
138178825Sdfr
139178825Sdfr    mcreds.session.keytype = ENCTYPE_ARCFOUR_HMAC_MD5;
140178825Sdfr    ret = krb5_cc_retrieve_cred(context, id, KRB5_TC_MATCH_KEYTYPE,
141178825Sdfr				&mcreds, &creds);
142178825Sdfr    if (ret) {
143178825Sdfr	char *s = krb5_get_error_message(context, ret);
144178825Sdfr	krb5_free_error_string(context, s);
145178825Sdfr	goto out;
146178825Sdfr    }
147178825Sdfr
148178825Sdfr    key->data = malloc(creds.session.keyvalue.length);
149178825Sdfr    if (key->data == NULL)
150178825Sdfr	goto out;
151178825Sdfr    key->length = creds.session.keyvalue.length;
152178825Sdfr    memcpy(key->data, creds.session.keyvalue.data, key->length);
153178825Sdfr
154178825Sdfr    krb5_free_cred_contents(context, &creds);
155178825Sdfr
156178825Sdfr    return 0;
157178825Sdfr
158178825Sdfrout:
159178825Sdfr    if (*username) {
160178825Sdfr	free(*username);
161178825Sdfr	*username = NULL;
162178825Sdfr    }
163178825Sdfr    krb5_free_cred_contents(context, &creds);
164178825Sdfr    if (mcreds.server)
165178825Sdfr	krb5_free_principal(context, mcreds.server);
166178825Sdfr    if (id)
167178825Sdfr	krb5_cc_close(context, id);
168178825Sdfr    if (context)
169178825Sdfr	krb5_free_context(context);
170178825Sdfr
171178825Sdfr    return ret;
172178825Sdfr}
173178825Sdfr
174178825Sdfrint
175178825Sdfr_gss_ntlm_get_user_cred(const ntlm_name target_name,
176178825Sdfr			ntlm_cred *rcred)
177178825Sdfr{
178178825Sdfr    ntlm_cred cred;
179178825Sdfr    int ret;
180178825Sdfr
181178825Sdfr    cred = calloc(1, sizeof(*cred));
182178825Sdfr    if (cred == NULL)
183178825Sdfr	return ENOMEM;
184178825Sdfr
185178825Sdfr    ret = get_user_file(target_name, &cred->username, &cred->key);
186178825Sdfr    if (ret)
187178825Sdfr	ret = get_user_ccache(target_name, &cred->username, &cred->key);
188178825Sdfr    if (ret) {
189178825Sdfr	free(cred);
190178825Sdfr	return ret;
191178825Sdfr    }
192178825Sdfr
193178825Sdfr    cred->domain = strdup(target_name->domain);
194178825Sdfr    *rcred = cred;
195178825Sdfr
196178825Sdfr    return ret;
197178825Sdfr}
198178825Sdfr
199178825Sdfrstatic int
200178825Sdfr_gss_copy_cred(ntlm_cred from, ntlm_cred *to)
201178825Sdfr{
202178825Sdfr    *to = calloc(1, sizeof(*to));
203178825Sdfr    if (*to == NULL)
204178825Sdfr	return ENOMEM;
205178825Sdfr    (*to)->username = strdup(from->username);
206178825Sdfr    if ((*to)->username == NULL) {
207178825Sdfr	free(*to);
208178825Sdfr	return ENOMEM;
209178825Sdfr    }
210178825Sdfr    (*to)->domain = strdup(from->domain);
211178825Sdfr    if ((*to)->domain == NULL) {
212178825Sdfr	free((*to)->username);
213178825Sdfr	free(*to);
214178825Sdfr	return ENOMEM;
215178825Sdfr    }
216178825Sdfr    (*to)->key.data = malloc(from->key.length);
217178825Sdfr    if ((*to)->key.data == NULL) {
218178825Sdfr	free((*to)->domain);
219178825Sdfr	free((*to)->username);
220178825Sdfr	free(*to);
221178825Sdfr	return ENOMEM;
222178825Sdfr    }
223178825Sdfr    memcpy((*to)->key.data, from->key.data, from->key.length);
224178825Sdfr    (*to)->key.length = from->key.length;
225178825Sdfr
226178825Sdfr    return 0;
227178825Sdfr}
228178825Sdfr
229178825SdfrOM_uint32
230178825Sdfr_gss_ntlm_init_sec_context
231178825Sdfr           (OM_uint32 * minor_status,
232178825Sdfr            const gss_cred_id_t initiator_cred_handle,
233178825Sdfr            gss_ctx_id_t * context_handle,
234178825Sdfr            const gss_name_t target_name,
235178825Sdfr            const gss_OID mech_type,
236178825Sdfr            OM_uint32 req_flags,
237178825Sdfr            OM_uint32 time_req,
238178825Sdfr            const gss_channel_bindings_t input_chan_bindings,
239178825Sdfr            const gss_buffer_t input_token,
240178825Sdfr            gss_OID * actual_mech_type,
241178825Sdfr            gss_buffer_t output_token,
242178825Sdfr            OM_uint32 * ret_flags,
243178825Sdfr            OM_uint32 * time_rec
244178825Sdfr	   )
245178825Sdfr{
246178825Sdfr    ntlm_ctx ctx;
247178825Sdfr    ntlm_name name = (ntlm_name)target_name;
248178825Sdfr
249178825Sdfr    *minor_status = 0;
250178825Sdfr
251178825Sdfr    if (ret_flags)
252178825Sdfr	*ret_flags = 0;
253178825Sdfr    if (time_rec)
254178825Sdfr	*time_rec = 0;
255178825Sdfr    if (actual_mech_type)
256178825Sdfr	*actual_mech_type = GSS_C_NO_OID;
257178825Sdfr
258178825Sdfr    if (*context_handle == GSS_C_NO_CONTEXT) {
259178825Sdfr	struct ntlm_type1 type1;
260178825Sdfr	struct ntlm_buf data;
261178825Sdfr	uint32_t flags = 0;
262178825Sdfr	int ret;
263178825Sdfr
264178825Sdfr	ctx = calloc(1, sizeof(*ctx));
265178825Sdfr	if (ctx == NULL) {
266178825Sdfr	    *minor_status = EINVAL;
267178825Sdfr	    return GSS_S_FAILURE;
268178825Sdfr	}
269178825Sdfr	*context_handle = (gss_ctx_id_t)ctx;
270178825Sdfr
271178825Sdfr	if (initiator_cred_handle != GSS_C_NO_CREDENTIAL) {
272178825Sdfr	    ntlm_cred cred = (ntlm_cred)initiator_cred_handle;
273178825Sdfr	    ret = _gss_copy_cred(cred, &ctx->client);
274178825Sdfr	} else
275178825Sdfr	    ret = _gss_ntlm_get_user_cred(name, &ctx->client);
276178825Sdfr
277178825Sdfr	if (ret) {
278178825Sdfr	    _gss_ntlm_delete_sec_context(minor_status, context_handle, NULL);
279178825Sdfr	    *minor_status = ret;
280178825Sdfr	    return GSS_S_FAILURE;
281178825Sdfr	}
282178825Sdfr
283178825Sdfr	if (req_flags & GSS_C_CONF_FLAG)
284178825Sdfr	    flags |= NTLM_NEG_SEAL;
285178825Sdfr	if (req_flags & GSS_C_INTEG_FLAG)
286178825Sdfr	    flags |= NTLM_NEG_SIGN;
287178825Sdfr	else
288178825Sdfr	    flags |= NTLM_NEG_ALWAYS_SIGN;
289178825Sdfr
290178825Sdfr	flags |= NTLM_NEG_UNICODE;
291178825Sdfr	flags |= NTLM_NEG_NTLM;
292178825Sdfr	flags |= NTLM_NEG_NTLM2_SESSION;
293178825Sdfr	flags |= NTLM_NEG_KEYEX;
294178825Sdfr
295178825Sdfr	memset(&type1, 0, sizeof(type1));
296178825Sdfr
297178825Sdfr	type1.flags = flags;
298178825Sdfr	type1.domain = name->domain;
299178825Sdfr	type1.hostname = NULL;
300178825Sdfr	type1.os[0] = 0;
301178825Sdfr	type1.os[1] = 0;
302178825Sdfr
303178825Sdfr	ret = heim_ntlm_encode_type1(&type1, &data);
304178825Sdfr	if (ret) {
305178825Sdfr	    _gss_ntlm_delete_sec_context(minor_status, context_handle, NULL);
306178825Sdfr	    *minor_status = ret;
307178825Sdfr	    return GSS_S_FAILURE;
308178825Sdfr	}
309178825Sdfr
310178825Sdfr	output_token->value = data.data;
311178825Sdfr	output_token->length = data.length;
312178825Sdfr
313178825Sdfr	return GSS_S_CONTINUE_NEEDED;
314178825Sdfr    } else {
315178825Sdfr	krb5_error_code ret;
316178825Sdfr	struct ntlm_type2 type2;
317178825Sdfr	struct ntlm_type3 type3;
318178825Sdfr	struct ntlm_buf data;
319178825Sdfr
320178825Sdfr	ctx = (ntlm_ctx)*context_handle;
321178825Sdfr
322178825Sdfr	data.data = input_token->value;
323178825Sdfr	data.length = input_token->length;
324178825Sdfr
325178825Sdfr	ret = heim_ntlm_decode_type2(&data, &type2);
326178825Sdfr	if (ret) {
327178825Sdfr	    _gss_ntlm_delete_sec_context(minor_status, context_handle, NULL);
328178825Sdfr	    *minor_status = ret;
329178825Sdfr	    return GSS_S_FAILURE;
330178825Sdfr	}
331178825Sdfr
332178825Sdfr	ctx->flags = type2.flags;
333178825Sdfr
334178825Sdfr	/* XXX check that type2.targetinfo matches `target_name� */
335178825Sdfr	/* XXX check verify targetinfo buffer */
336178825Sdfr
337178825Sdfr	memset(&type3, 0, sizeof(type3));
338178825Sdfr
339178825Sdfr	type3.username = ctx->client->username;
340178825Sdfr	type3.flags = type2.flags;
341178825Sdfr	type3.targetname = type2.targetname;
342178825Sdfr	type3.ws = rk_UNCONST("workstation");
343178825Sdfr
344178825Sdfr	/*
345178825Sdfr	 * NTLM Version 1 if no targetinfo buffer.
346178825Sdfr	 */
347178825Sdfr
348178825Sdfr	if (1 || type2.targetinfo.length == 0) {
349178825Sdfr	    struct ntlm_buf sessionkey;
350178825Sdfr
351178825Sdfr	    if (type2.flags & NTLM_NEG_NTLM2_SESSION) {
352178825Sdfr		unsigned char nonce[8];
353178825Sdfr
354178825Sdfr		if (RAND_bytes(nonce, sizeof(nonce)) != 1) {
355178825Sdfr		    _gss_ntlm_delete_sec_context(minor_status,
356178825Sdfr						 context_handle, NULL);
357178825Sdfr		    *minor_status = EINVAL;
358178825Sdfr		    return GSS_S_FAILURE;
359178825Sdfr		}
360178825Sdfr
361178825Sdfr		ret = heim_ntlm_calculate_ntlm2_sess(nonce,
362178825Sdfr						     type2.challange,
363178825Sdfr						     ctx->client->key.data,
364178825Sdfr						     &type3.lm,
365178825Sdfr						     &type3.ntlm);
366178825Sdfr	    } else {
367178825Sdfr		ret = heim_ntlm_calculate_ntlm1(ctx->client->key.data,
368178825Sdfr						ctx->client->key.length,
369178825Sdfr						type2.challange,
370178825Sdfr						&type3.ntlm);
371178825Sdfr
372178825Sdfr	    }
373178825Sdfr	    if (ret) {
374178825Sdfr		_gss_ntlm_delete_sec_context(minor_status,context_handle,NULL);
375178825Sdfr		*minor_status = ret;
376178825Sdfr		return GSS_S_FAILURE;
377178825Sdfr	    }
378178825Sdfr
379178825Sdfr	    ret = heim_ntlm_build_ntlm1_master(ctx->client->key.data,
380178825Sdfr					       ctx->client->key.length,
381178825Sdfr					       &sessionkey,
382178825Sdfr					       &type3.sessionkey);
383178825Sdfr	    if (ret) {
384178825Sdfr		if (type3.lm.data)
385178825Sdfr		    free(type3.lm.data);
386178825Sdfr		if (type3.ntlm.data)
387178825Sdfr		    free(type3.ntlm.data);
388178825Sdfr		_gss_ntlm_delete_sec_context(minor_status,context_handle,NULL);
389178825Sdfr		*minor_status = ret;
390178825Sdfr		return GSS_S_FAILURE;
391178825Sdfr	    }
392178825Sdfr
393178825Sdfr	    ret = krb5_data_copy(&ctx->sessionkey,
394178825Sdfr				 sessionkey.data, sessionkey.length);
395178825Sdfr	    free(sessionkey.data);
396178825Sdfr	    if (ret) {
397178825Sdfr		if (type3.lm.data)
398178825Sdfr		    free(type3.lm.data);
399178825Sdfr		if (type3.ntlm.data)
400178825Sdfr		    free(type3.ntlm.data);
401178825Sdfr		_gss_ntlm_delete_sec_context(minor_status,context_handle,NULL);
402178825Sdfr		*minor_status = ret;
403178825Sdfr		return GSS_S_FAILURE;
404178825Sdfr	    }
405178825Sdfr	    ctx->status |= STATUS_SESSIONKEY;
406178825Sdfr
407178825Sdfr	} else {
408178825Sdfr	    struct ntlm_buf sessionkey;
409178825Sdfr	    unsigned char ntlmv2[16];
410178825Sdfr	    struct ntlm_targetinfo ti;
411178825Sdfr
412178825Sdfr	    /* verify infotarget */
413178825Sdfr
414178825Sdfr	    ret = heim_ntlm_decode_targetinfo(&type2.targetinfo, 1, &ti);
415178825Sdfr	    if(ret) {
416178825Sdfr		_gss_ntlm_delete_sec_context(minor_status,
417178825Sdfr					     context_handle, NULL);
418178825Sdfr		*minor_status = ret;
419178825Sdfr		return GSS_S_FAILURE;
420178825Sdfr	    }
421178825Sdfr
422178825Sdfr	    if (ti.domainname && strcmp(ti.domainname, name->domain) != 0) {
423178825Sdfr		_gss_ntlm_delete_sec_context(minor_status,
424178825Sdfr					     context_handle, NULL);
425178825Sdfr		*minor_status = EINVAL;
426178825Sdfr		return GSS_S_FAILURE;
427178825Sdfr	    }
428178825Sdfr
429178825Sdfr	    ret = heim_ntlm_calculate_ntlm2(ctx->client->key.data,
430178825Sdfr					    ctx->client->key.length,
431178825Sdfr					    ctx->client->username,
432178825Sdfr					    name->domain,
433178825Sdfr					    type2.challange,
434178825Sdfr					    &type2.targetinfo,
435178825Sdfr					    ntlmv2,
436178825Sdfr					    &type3.ntlm);
437178825Sdfr	    if (ret) {
438178825Sdfr		_gss_ntlm_delete_sec_context(minor_status,
439178825Sdfr					     context_handle, NULL);
440178825Sdfr		*minor_status = ret;
441178825Sdfr		return GSS_S_FAILURE;
442178825Sdfr	    }
443178825Sdfr
444178825Sdfr	    ret = heim_ntlm_build_ntlm1_master(ntlmv2, sizeof(ntlmv2),
445178825Sdfr					       &sessionkey,
446178825Sdfr					       &type3.sessionkey);
447178825Sdfr	    memset(ntlmv2, 0, sizeof(ntlmv2));
448178825Sdfr	    if (ret) {
449178825Sdfr		_gss_ntlm_delete_sec_context(minor_status,
450178825Sdfr					     context_handle, NULL);
451178825Sdfr		*minor_status = ret;
452178825Sdfr		return GSS_S_FAILURE;
453178825Sdfr	    }
454178825Sdfr
455178825Sdfr	    ctx->flags |= NTLM_NEG_NTLM2_SESSION;
456178825Sdfr
457178825Sdfr	    ret = krb5_data_copy(&ctx->sessionkey,
458178825Sdfr				 sessionkey.data, sessionkey.length);
459178825Sdfr	    free(sessionkey.data);
460178825Sdfr	}
461178825Sdfr
462178825Sdfr	if (ctx->flags & NTLM_NEG_NTLM2_SESSION) {
463178825Sdfr	    ctx->status |= STATUS_SESSIONKEY;
464178825Sdfr	    _gss_ntlm_set_key(&ctx->u.v2.send, 0, (ctx->flags & NTLM_NEG_KEYEX),
465178825Sdfr			      ctx->sessionkey.data,
466178825Sdfr			      ctx->sessionkey.length);
467178825Sdfr	    _gss_ntlm_set_key(&ctx->u.v2.recv, 1, (ctx->flags & NTLM_NEG_KEYEX),
468178825Sdfr			      ctx->sessionkey.data,
469178825Sdfr			      ctx->sessionkey.length);
470178825Sdfr	} else {
471178825Sdfr	    ctx->status |= STATUS_SESSIONKEY;
472178825Sdfr	    RC4_set_key(&ctx->u.v1.crypto_recv.key,
473178825Sdfr			ctx->sessionkey.length,
474178825Sdfr			ctx->sessionkey.data);
475178825Sdfr	    RC4_set_key(&ctx->u.v1.crypto_send.key,
476178825Sdfr			ctx->sessionkey.length,
477178825Sdfr			ctx->sessionkey.data);
478178825Sdfr	}
479178825Sdfr
480178825Sdfr
481178825Sdfr
482178825Sdfr	ret = heim_ntlm_encode_type3(&type3, &data);
483178825Sdfr	free(type3.sessionkey.data);
484178825Sdfr	if (type3.lm.data)
485178825Sdfr	    free(type3.lm.data);
486178825Sdfr	if (type3.ntlm.data)
487178825Sdfr	    free(type3.ntlm.data);
488178825Sdfr	if (ret) {
489178825Sdfr	    _gss_ntlm_delete_sec_context(minor_status, context_handle, NULL);
490178825Sdfr	    *minor_status = ret;
491178825Sdfr	    return GSS_S_FAILURE;
492178825Sdfr	}
493178825Sdfr
494178825Sdfr	output_token->length = data.length;
495178825Sdfr	output_token->value = data.data;
496178825Sdfr
497178825Sdfr	if (actual_mech_type)
498178825Sdfr	    *actual_mech_type = GSS_NTLM_MECHANISM;
499178825Sdfr	if (ret_flags)
500178825Sdfr	    *ret_flags = 0;
501178825Sdfr	if (time_rec)
502178825Sdfr	    *time_rec = GSS_C_INDEFINITE;
503178825Sdfr
504178825Sdfr	ctx->status |= STATUS_OPEN;
505178825Sdfr
506178825Sdfr	return GSS_S_COMPLETE;
507178825Sdfr    }
508178825Sdfr}
509