1178825Sdfr/*
2233294Sstas * Copyright (c) 1997 - 2008 Kungliga Tekniska H��gskolan
3233294Sstas * (Royal Institute of Technology, Stockholm, Sweden).
4233294Sstas * All rights reserved.
5178825Sdfr *
6233294Sstas * Redistribution and use in source and binary forms, with or without
7233294Sstas * modification, are permitted provided that the following conditions
8233294Sstas * are met:
9178825Sdfr *
10233294Sstas * 1. Redistributions of source code must retain the above copyright
11233294Sstas *    notice, this list of conditions and the following disclaimer.
12178825Sdfr *
13233294Sstas * 2. Redistributions in binary form must reproduce the above copyright
14233294Sstas *    notice, this list of conditions and the following disclaimer in the
15233294Sstas *    documentation and/or other materials provided with the distribution.
16178825Sdfr *
17233294Sstas * 3. Neither the name of the Institute nor the names of its contributors
18233294Sstas *    may be used to endorse or promote products derived from this software
19233294Sstas *    without specific prior written permission.
20178825Sdfr *
21233294Sstas * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
22233294Sstas * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23233294Sstas * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24233294Sstas * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
25233294Sstas * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26233294Sstas * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27233294Sstas * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28233294Sstas * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29233294Sstas * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30233294Sstas * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31233294Sstas * SUCH DAMAGE.
32178825Sdfr */
33178825Sdfr
34233294Sstas#include "gsskrb5_locl.h"
35178825Sdfr
36178825Sdfr/*
37178825Sdfr * copy the addresses from `input_chan_bindings' (if any) to
38178825Sdfr * the auth context `ac'
39178825Sdfr */
40178825Sdfr
41178825Sdfrstatic OM_uint32
42178825Sdfrset_addresses (krb5_context context,
43178825Sdfr	       krb5_auth_context ac,
44233294Sstas	       const gss_channel_bindings_t input_chan_bindings)
45178825Sdfr{
46233294Sstas    /* Port numbers are expected to be in application_data.value,
47233294Sstas     * initator's port first */
48178825Sdfr
49178825Sdfr    krb5_address initiator_addr, acceptor_addr;
50178825Sdfr    krb5_error_code kret;
51233294Sstas
52178825Sdfr    if (input_chan_bindings == GSS_C_NO_CHANNEL_BINDINGS
53178825Sdfr	|| input_chan_bindings->application_data.length !=
54178825Sdfr	2 * sizeof(ac->local_port))
55178825Sdfr	return 0;
56178825Sdfr
57178825Sdfr    memset(&initiator_addr, 0, sizeof(initiator_addr));
58178825Sdfr    memset(&acceptor_addr, 0, sizeof(acceptor_addr));
59233294Sstas
60178825Sdfr    ac->local_port =
61178825Sdfr	*(int16_t *) input_chan_bindings->application_data.value;
62233294Sstas
63178825Sdfr    ac->remote_port =
64178825Sdfr	*((int16_t *) input_chan_bindings->application_data.value + 1);
65233294Sstas
66178825Sdfr    kret = _gsskrb5i_address_to_krb5addr(context,
67178825Sdfr					 input_chan_bindings->acceptor_addrtype,
68178825Sdfr					 &input_chan_bindings->acceptor_address,
69178825Sdfr					 ac->remote_port,
70178825Sdfr					 &acceptor_addr);
71178825Sdfr    if (kret)
72178825Sdfr	return kret;
73233294Sstas
74178825Sdfr    kret = _gsskrb5i_address_to_krb5addr(context,
75178825Sdfr					 input_chan_bindings->initiator_addrtype,
76178825Sdfr					 &input_chan_bindings->initiator_address,
77178825Sdfr					 ac->local_port,
78178825Sdfr					 &initiator_addr);
79178825Sdfr    if (kret) {
80178825Sdfr	krb5_free_address (context, &acceptor_addr);
81178825Sdfr	return kret;
82178825Sdfr    }
83233294Sstas
84178825Sdfr    kret = krb5_auth_con_setaddrs(context,
85178825Sdfr				  ac,
86178825Sdfr				  &initiator_addr,  /* local address */
87178825Sdfr				  &acceptor_addr);  /* remote address */
88233294Sstas
89178825Sdfr    krb5_free_address (context, &initiator_addr);
90178825Sdfr    krb5_free_address (context, &acceptor_addr);
91233294Sstas
92178825Sdfr#if 0
93178825Sdfr    free(input_chan_bindings->application_data.value);
94178825Sdfr    input_chan_bindings->application_data.value = NULL;
95178825Sdfr    input_chan_bindings->application_data.length = 0;
96178825Sdfr#endif
97178825Sdfr
98178825Sdfr    return kret;
99178825Sdfr}
100178825Sdfr
101178825SdfrOM_uint32
102178825Sdfr_gsskrb5_create_ctx(
103178825Sdfr        OM_uint32 * minor_status,
104178825Sdfr	gss_ctx_id_t * context_handle,
105178825Sdfr	krb5_context context,
106178825Sdfr 	const gss_channel_bindings_t input_chan_bindings,
107178825Sdfr 	enum gss_ctx_id_t_state state)
108178825Sdfr{
109178825Sdfr    krb5_error_code kret;
110178825Sdfr    gsskrb5_ctx ctx;
111178825Sdfr
112178825Sdfr    *context_handle = NULL;
113178825Sdfr
114178825Sdfr    ctx = malloc(sizeof(*ctx));
115178825Sdfr    if (ctx == NULL) {
116178825Sdfr	*minor_status = ENOMEM;
117178825Sdfr	return GSS_S_FAILURE;
118178825Sdfr    }
119178825Sdfr    ctx->auth_context		= NULL;
120233294Sstas    ctx->deleg_auth_context	= NULL;
121178825Sdfr    ctx->source			= NULL;
122178825Sdfr    ctx->target			= NULL;
123233294Sstas    ctx->kcred			= NULL;
124233294Sstas    ctx->ccache			= NULL;
125178825Sdfr    ctx->state			= state;
126178825Sdfr    ctx->flags			= 0;
127178825Sdfr    ctx->more_flags		= 0;
128178825Sdfr    ctx->service_keyblock	= NULL;
129178825Sdfr    ctx->ticket			= NULL;
130178825Sdfr    krb5_data_zero(&ctx->fwd_data);
131178825Sdfr    ctx->lifetime		= GSS_C_INDEFINITE;
132178825Sdfr    ctx->order			= NULL;
133233294Sstas    ctx->crypto			= NULL;
134178825Sdfr    HEIMDAL_MUTEX_init(&ctx->ctx_id_mutex);
135178825Sdfr
136178825Sdfr    kret = krb5_auth_con_init (context, &ctx->auth_context);
137178825Sdfr    if (kret) {
138178825Sdfr	*minor_status = kret;
139233294Sstas	HEIMDAL_MUTEX_destroy(&ctx->ctx_id_mutex);
140233294Sstas	return GSS_S_FAILURE;
141233294Sstas    }
142178825Sdfr
143233294Sstas    kret = krb5_auth_con_init (context, &ctx->deleg_auth_context);
144233294Sstas    if (kret) {
145233294Sstas	*minor_status = kret;
146233294Sstas	krb5_auth_con_free(context, ctx->auth_context);
147178825Sdfr	HEIMDAL_MUTEX_destroy(&ctx->ctx_id_mutex);
148178825Sdfr	return GSS_S_FAILURE;
149178825Sdfr    }
150178825Sdfr
151178825Sdfr    kret = set_addresses(context, ctx->auth_context, input_chan_bindings);
152178825Sdfr    if (kret) {
153178825Sdfr	*minor_status = kret;
154178825Sdfr
155233294Sstas	krb5_auth_con_free(context, ctx->auth_context);
156233294Sstas	krb5_auth_con_free(context, ctx->deleg_auth_context);
157233294Sstas
158178825Sdfr	HEIMDAL_MUTEX_destroy(&ctx->ctx_id_mutex);
159178825Sdfr
160233294Sstas	return GSS_S_BAD_BINDINGS;
161233294Sstas    }
162233294Sstas
163233294Sstas    kret = set_addresses(context, ctx->deleg_auth_context, input_chan_bindings);
164233294Sstas    if (kret) {
165233294Sstas	*minor_status = kret;
166233294Sstas
167178825Sdfr	krb5_auth_con_free(context, ctx->auth_context);
168233294Sstas	krb5_auth_con_free(context, ctx->deleg_auth_context);
169178825Sdfr
170233294Sstas	HEIMDAL_MUTEX_destroy(&ctx->ctx_id_mutex);
171233294Sstas
172178825Sdfr	return GSS_S_BAD_BINDINGS;
173178825Sdfr    }
174178825Sdfr
175178825Sdfr    /*
176178825Sdfr     * We need a sequence number
177178825Sdfr     */
178178825Sdfr
179178825Sdfr    krb5_auth_con_addflags(context,
180178825Sdfr			   ctx->auth_context,
181178825Sdfr			   KRB5_AUTH_CONTEXT_DO_SEQUENCE |
182178825Sdfr			   KRB5_AUTH_CONTEXT_CLEAR_FORWARDED_CRED,
183178825Sdfr			   NULL);
184178825Sdfr
185233294Sstas    /*
186233294Sstas     * We need a sequence number
187233294Sstas     */
188233294Sstas
189233294Sstas    krb5_auth_con_addflags(context,
190233294Sstas			   ctx->deleg_auth_context,
191233294Sstas			   KRB5_AUTH_CONTEXT_DO_SEQUENCE |
192233294Sstas			   KRB5_AUTH_CONTEXT_CLEAR_FORWARDED_CRED,
193233294Sstas			   NULL);
194233294Sstas
195178825Sdfr    *context_handle = (gss_ctx_id_t)ctx;
196178825Sdfr
197178825Sdfr    return GSS_S_COMPLETE;
198178825Sdfr}
199178825Sdfr
200178825Sdfr
201178825Sdfrstatic OM_uint32
202178825Sdfrgsskrb5_get_creds(
203178825Sdfr        OM_uint32 * minor_status,
204178825Sdfr	krb5_context context,
205178825Sdfr	krb5_ccache ccache,
206178825Sdfr	gsskrb5_ctx ctx,
207233294Sstas	const gss_name_t target_name,
208233294Sstas	int use_dns,
209178825Sdfr	OM_uint32 time_req,
210233294Sstas	OM_uint32 * time_rec)
211178825Sdfr{
212178825Sdfr    OM_uint32 ret;
213178825Sdfr    krb5_error_code kret;
214178825Sdfr    krb5_creds this_cred;
215178825Sdfr    OM_uint32 lifetime_rec;
216178825Sdfr
217233294Sstas    if (ctx->target) {
218233294Sstas	krb5_free_principal(context, ctx->target);
219233294Sstas	ctx->target = NULL;
220233294Sstas    }
221233294Sstas    if (ctx->kcred) {
222233294Sstas	krb5_free_creds(context, ctx->kcred);
223233294Sstas	ctx->kcred = NULL;
224233294Sstas    }
225178825Sdfr
226233294Sstas    ret = _gsskrb5_canon_name(minor_status, context, use_dns,
227233294Sstas			      ctx->source, target_name, &ctx->target);
228233294Sstas    if (ret)
229233294Sstas	return ret;
230233294Sstas
231178825Sdfr    memset(&this_cred, 0, sizeof(this_cred));
232178825Sdfr    this_cred.client = ctx->source;
233178825Sdfr    this_cred.server = ctx->target;
234178825Sdfr
235178825Sdfr    if (time_req && time_req != GSS_C_INDEFINITE) {
236178825Sdfr	krb5_timestamp ts;
237178825Sdfr
238178825Sdfr	krb5_timeofday (context, &ts);
239178825Sdfr	this_cred.times.endtime = ts + time_req;
240178825Sdfr    } else {
241178825Sdfr	this_cred.times.endtime   = 0;
242178825Sdfr    }
243178825Sdfr
244178825Sdfr    this_cred.session.keytype = KEYTYPE_NULL;
245178825Sdfr
246178825Sdfr    kret = krb5_get_credentials(context,
247178825Sdfr				0,
248178825Sdfr				ccache,
249178825Sdfr				&this_cred,
250233294Sstas				&ctx->kcred);
251178825Sdfr    if (kret) {
252178825Sdfr	*minor_status = kret;
253178825Sdfr	return GSS_S_FAILURE;
254178825Sdfr    }
255178825Sdfr
256233294Sstas    ctx->lifetime = ctx->kcred->times.endtime;
257178825Sdfr
258178825Sdfr    ret = _gsskrb5_lifetime_left(minor_status, context,
259178825Sdfr				 ctx->lifetime, &lifetime_rec);
260178825Sdfr    if (ret) return ret;
261178825Sdfr
262178825Sdfr    if (lifetime_rec == 0) {
263178825Sdfr	*minor_status = 0;
264178825Sdfr	return GSS_S_CONTEXT_EXPIRED;
265178825Sdfr    }
266178825Sdfr
267178825Sdfr    if (time_rec) *time_rec = lifetime_rec;
268178825Sdfr
269178825Sdfr    return GSS_S_COMPLETE;
270178825Sdfr}
271178825Sdfr
272178825Sdfrstatic OM_uint32
273178825Sdfrgsskrb5_initiator_ready(
274178825Sdfr	OM_uint32 * minor_status,
275178825Sdfr	gsskrb5_ctx ctx,
276178825Sdfr	krb5_context context)
277178825Sdfr{
278233294Sstas    OM_uint32 ret;
279233294Sstas    int32_t seq_number;
280233294Sstas    int is_cfx = 0;
281233294Sstas    OM_uint32 flags = ctx->flags;
282178825Sdfr
283233294Sstas    krb5_free_creds(context, ctx->kcred);
284233294Sstas    ctx->kcred = NULL;
285178825Sdfr
286233294Sstas    if (ctx->more_flags & CLOSE_CCACHE)
287233294Sstas	krb5_cc_close(context, ctx->ccache);
288233294Sstas    ctx->ccache = NULL;
289178825Sdfr
290233294Sstas    krb5_auth_con_getremoteseqnumber (context, ctx->auth_context, &seq_number);
291178825Sdfr
292233294Sstas    _gsskrb5i_is_cfx(context, ctx, 0);
293233294Sstas    is_cfx = (ctx->more_flags & IS_CFX);
294178825Sdfr
295233294Sstas    ret = _gssapi_msg_order_create(minor_status,
296233294Sstas				   &ctx->order,
297233294Sstas				   _gssapi_msg_order_f(flags),
298233294Sstas				   seq_number, 0, is_cfx);
299233294Sstas    if (ret) return ret;
300233294Sstas
301233294Sstas    ctx->state	= INITIATOR_READY;
302233294Sstas    ctx->more_flags	|= OPEN;
303233294Sstas
304233294Sstas    return GSS_S_COMPLETE;
305178825Sdfr}
306178825Sdfr
307178825Sdfr/*
308178825Sdfr * handle delegated creds in init-sec-context
309178825Sdfr */
310178825Sdfr
311178825Sdfrstatic void
312178825Sdfrdo_delegation (krb5_context context,
313178825Sdfr	       krb5_auth_context ac,
314178825Sdfr	       krb5_ccache ccache,
315178825Sdfr	       krb5_creds *cred,
316178825Sdfr	       krb5_const_principal name,
317178825Sdfr	       krb5_data *fwd_data,
318233294Sstas	       uint32_t flagmask,
319178825Sdfr	       uint32_t *flags)
320178825Sdfr{
321178825Sdfr    krb5_creds creds;
322178825Sdfr    KDCOptions fwd_flags;
323178825Sdfr    krb5_error_code kret;
324233294Sstas
325178825Sdfr    memset (&creds, 0, sizeof(creds));
326178825Sdfr    krb5_data_zero (fwd_data);
327233294Sstas
328178825Sdfr    kret = krb5_cc_get_principal(context, ccache, &creds.client);
329233294Sstas    if (kret)
330178825Sdfr	goto out;
331233294Sstas
332233294Sstas    kret = krb5_make_principal(context,
333233294Sstas			       &creds.server,
334233294Sstas			       creds.client->realm,
335233294Sstas			       KRB5_TGS_NAME,
336233294Sstas			       creds.client->realm,
337233294Sstas			       NULL);
338178825Sdfr    if (kret)
339233294Sstas	goto out;
340233294Sstas
341178825Sdfr    creds.times.endtime = 0;
342233294Sstas
343178825Sdfr    memset(&fwd_flags, 0, sizeof(fwd_flags));
344178825Sdfr    fwd_flags.forwarded = 1;
345178825Sdfr    fwd_flags.forwardable = 1;
346233294Sstas
347178825Sdfr    if ( /*target_name->name.name_type != KRB5_NT_SRV_HST ||*/
348233294Sstas	name->name.name_string.len < 2)
349178825Sdfr	goto out;
350233294Sstas
351178825Sdfr    kret = krb5_get_forwarded_creds(context,
352178825Sdfr				    ac,
353178825Sdfr				    ccache,
354178825Sdfr				    KDCOptions2int(fwd_flags),
355178825Sdfr				    name->name.name_string.val[1],
356178825Sdfr				    &creds,
357178825Sdfr				    fwd_data);
358233294Sstas
359178825Sdfr out:
360178825Sdfr    if (kret)
361233294Sstas	*flags &= ~flagmask;
362178825Sdfr    else
363233294Sstas	*flags |= flagmask;
364233294Sstas
365178825Sdfr    if (creds.client)
366178825Sdfr	krb5_free_principal(context, creds.client);
367178825Sdfr    if (creds.server)
368178825Sdfr	krb5_free_principal(context, creds.server);
369178825Sdfr}
370178825Sdfr
371178825Sdfr/*
372178825Sdfr * first stage of init-sec-context
373178825Sdfr */
374178825Sdfr
375178825Sdfrstatic OM_uint32
376178825Sdfrinit_auth
377178825Sdfr(OM_uint32 * minor_status,
378233294Sstas gsskrb5_cred cred,
379178825Sdfr gsskrb5_ctx ctx,
380178825Sdfr krb5_context context,
381233294Sstas gss_name_t name,
382178825Sdfr const gss_OID mech_type,
383178825Sdfr OM_uint32 req_flags,
384178825Sdfr OM_uint32 time_req,
385178825Sdfr const gss_buffer_t input_token,
386178825Sdfr gss_OID * actual_mech_type,
387178825Sdfr gss_buffer_t output_token,
388178825Sdfr OM_uint32 * ret_flags,
389178825Sdfr OM_uint32 * time_rec
390178825Sdfr    )
391178825Sdfr{
392178825Sdfr    OM_uint32 ret = GSS_S_FAILURE;
393178825Sdfr    krb5_error_code kret;
394178825Sdfr    krb5_data outbuf;
395178825Sdfr    krb5_data fwd_data;
396178825Sdfr    OM_uint32 lifetime_rec;
397233294Sstas    int allow_dns = 1;
398178825Sdfr
399178825Sdfr    krb5_data_zero(&outbuf);
400178825Sdfr    krb5_data_zero(&fwd_data);
401178825Sdfr
402178825Sdfr    *minor_status = 0;
403178825Sdfr
404178825Sdfr    if (actual_mech_type)
405178825Sdfr	*actual_mech_type = GSS_KRB5_MECHANISM;
406178825Sdfr
407233294Sstas    if (cred == NULL) {
408233294Sstas	kret = krb5_cc_default (context, &ctx->ccache);
409178825Sdfr	if (kret) {
410178825Sdfr	    *minor_status = kret;
411178825Sdfr	    ret = GSS_S_FAILURE;
412178825Sdfr	    goto failure;
413178825Sdfr	}
414233294Sstas	ctx->more_flags |= CLOSE_CCACHE;
415178825Sdfr    } else
416233294Sstas	ctx->ccache = cred->ccache;
417178825Sdfr
418233294Sstas    kret = krb5_cc_get_principal (context, ctx->ccache, &ctx->source);
419178825Sdfr    if (kret) {
420178825Sdfr	*minor_status = kret;
421178825Sdfr	ret = GSS_S_FAILURE;
422178825Sdfr	goto failure;
423178825Sdfr    }
424178825Sdfr
425178825Sdfr    /*
426178825Sdfr     * This is hideous glue for (NFS) clients that wants to limit the
427178825Sdfr     * available enctypes to what it can support (encryption in
428178825Sdfr     * kernel). If there is no enctypes selected for this credential,
429178825Sdfr     * reset it to the default set of enctypes.
430178825Sdfr     */
431178825Sdfr    {
432178825Sdfr	krb5_enctype *enctypes = NULL;
433178825Sdfr
434233294Sstas	if (cred && cred->enctypes)
435233294Sstas	    enctypes = cred->enctypes;
436178825Sdfr	krb5_set_default_in_tkt_etypes(context, enctypes);
437178825Sdfr    }
438178825Sdfr
439233294Sstas    /* canon name if needed for client + target realm */
440233294Sstas    kret = krb5_cc_get_config(context, ctx->ccache, NULL,
441233294Sstas			      "realm-config", &outbuf);
442233294Sstas    if (kret == 0) {
443233294Sstas	/* XXX 2 is no server canon */
444233294Sstas	if (outbuf.length < 1 || ((((unsigned char *)outbuf.data)[0]) & 2))
445233294Sstas	    allow_dns = 0;
446233294Sstas	krb5_data_free(&outbuf);
447233294Sstas    }
448233294Sstas
449233294Sstas    /*
450233294Sstas     * First we try w/o dns, hope that the KDC have register alias
451233294Sstas     * (and referrals if cross realm) for this principal. If that
452233294Sstas     * fails and if we are allowed to using this realm try again with
453233294Sstas     * DNS canonicalizion.
454233294Sstas     */
455233294Sstas    ret = gsskrb5_get_creds(minor_status, context, ctx->ccache,
456233294Sstas			    ctx, name, 0, time_req,
457233294Sstas			    time_rec);
458233294Sstas    if (ret && allow_dns)
459233294Sstas	ret = gsskrb5_get_creds(minor_status, context, ctx->ccache,
460233294Sstas				ctx, name, 1, time_req,
461233294Sstas				time_rec);
462178825Sdfr    if (ret)
463178825Sdfr	goto failure;
464178825Sdfr
465233294Sstas    ctx->lifetime = ctx->kcred->times.endtime;
466178825Sdfr
467233294Sstas    ret = _gss_DES3_get_mic_compat(minor_status, ctx, context);
468233294Sstas    if (ret)
469233294Sstas	goto failure;
470233294Sstas
471178825Sdfr    ret = _gsskrb5_lifetime_left(minor_status,
472178825Sdfr				 context,
473178825Sdfr				 ctx->lifetime,
474178825Sdfr				 &lifetime_rec);
475233294Sstas    if (ret)
476178825Sdfr	goto failure;
477178825Sdfr
478178825Sdfr    if (lifetime_rec == 0) {
479178825Sdfr	*minor_status = 0;
480178825Sdfr	ret = GSS_S_CONTEXT_EXPIRED;
481178825Sdfr	goto failure;
482178825Sdfr    }
483178825Sdfr
484233294Sstas    krb5_auth_con_setkey(context,
485233294Sstas			 ctx->auth_context,
486233294Sstas			 &ctx->kcred->session);
487178825Sdfr
488233294Sstas    kret = krb5_auth_con_generatelocalsubkey(context,
489178825Sdfr					     ctx->auth_context,
490233294Sstas					     &ctx->kcred->session);
491178825Sdfr    if(kret) {
492178825Sdfr	*minor_status = kret;
493178825Sdfr	ret = GSS_S_FAILURE;
494178825Sdfr	goto failure;
495178825Sdfr    }
496233294Sstas
497233294Sstas    return GSS_S_COMPLETE;
498233294Sstas
499233294Sstasfailure:
500233294Sstas    if (ctx->ccache && (ctx->more_flags & CLOSE_CCACHE))
501233294Sstas	krb5_cc_close(context, ctx->ccache);
502233294Sstas    ctx->ccache = NULL;
503233294Sstas
504233294Sstas    return ret;
505233294Sstas
506233294Sstas}
507233294Sstas
508233294Sstasstatic OM_uint32
509233294Sstasinit_auth_restart
510233294Sstas(OM_uint32 * minor_status,
511233294Sstas gsskrb5_cred cred,
512233294Sstas gsskrb5_ctx ctx,
513233294Sstas krb5_context context,
514233294Sstas OM_uint32 req_flags,
515233294Sstas const gss_channel_bindings_t input_chan_bindings,
516233294Sstas const gss_buffer_t input_token,
517233294Sstas gss_OID * actual_mech_type,
518233294Sstas gss_buffer_t output_token,
519233294Sstas OM_uint32 * ret_flags,
520233294Sstas OM_uint32 * time_rec
521233294Sstas    )
522233294Sstas{
523233294Sstas    OM_uint32 ret = GSS_S_FAILURE;
524233294Sstas    krb5_error_code kret;
525233294Sstas    krb5_flags ap_options;
526233294Sstas    krb5_data outbuf;
527233294Sstas    uint32_t flags;
528233294Sstas    krb5_data authenticator;
529233294Sstas    Checksum cksum;
530233294Sstas    krb5_enctype enctype;
531233294Sstas    krb5_data fwd_data, timedata;
532233294Sstas    int32_t offset = 0, oldoffset = 0;
533233294Sstas    uint32_t flagmask;
534233294Sstas
535233294Sstas    krb5_data_zero(&outbuf);
536233294Sstas    krb5_data_zero(&fwd_data);
537233294Sstas
538233294Sstas    *minor_status = 0;
539233294Sstas
540233294Sstas    /*
541233294Sstas     * If the credential doesn't have ok-as-delegate, check if there
542233294Sstas     * is a realm setting and use that.
543178825Sdfr     */
544233294Sstas    if (!ctx->kcred->flags.b.ok_as_delegate) {
545233294Sstas	krb5_data data;
546233294Sstas
547233294Sstas	ret = krb5_cc_get_config(context, ctx->ccache, NULL,
548233294Sstas				 "realm-config", &data);
549233294Sstas	if (ret == 0) {
550233294Sstas	    /* XXX 1 is use ok-as-delegate */
551233294Sstas	    if (data.length < 1 || ((((unsigned char *)data.data)[0]) & 1) == 0)
552233294Sstas		req_flags &= ~(GSS_C_DELEG_FLAG|GSS_C_DELEG_POLICY_FLAG);
553233294Sstas	    krb5_data_free(&data);
554233294Sstas	}
555178825Sdfr    }
556178825Sdfr
557233294Sstas    flagmask = 0;
558233294Sstas
559233294Sstas    /* if we used GSS_C_DELEG_POLICY_FLAG, trust KDC */
560233294Sstas    if ((req_flags & GSS_C_DELEG_POLICY_FLAG)
561233294Sstas	&& ctx->kcred->flags.b.ok_as_delegate)
562233294Sstas	flagmask |= GSS_C_DELEG_FLAG | GSS_C_DELEG_POLICY_FLAG;
563233294Sstas    /* if there still is a GSS_C_DELEG_FLAG, use that */
564233294Sstas    if (req_flags & GSS_C_DELEG_FLAG)
565233294Sstas	flagmask |= GSS_C_DELEG_FLAG;
566233294Sstas
567233294Sstas
568178825Sdfr    flags = 0;
569178825Sdfr    ap_options = 0;
570233294Sstas    if (flagmask & GSS_C_DELEG_FLAG) {
571178825Sdfr	do_delegation (context,
572233294Sstas		       ctx->deleg_auth_context,
573233294Sstas		       ctx->ccache, ctx->kcred, ctx->target,
574233294Sstas		       &fwd_data, flagmask, &flags);
575233294Sstas    }
576233294Sstas
577178825Sdfr    if (req_flags & GSS_C_MUTUAL_FLAG) {
578178825Sdfr	flags |= GSS_C_MUTUAL_FLAG;
579178825Sdfr	ap_options |= AP_OPTS_MUTUAL_REQUIRED;
580178825Sdfr    }
581233294Sstas
582178825Sdfr    if (req_flags & GSS_C_REPLAY_FLAG)
583178825Sdfr	flags |= GSS_C_REPLAY_FLAG;
584178825Sdfr    if (req_flags & GSS_C_SEQUENCE_FLAG)
585178825Sdfr	flags |= GSS_C_SEQUENCE_FLAG;
586233294Sstas#if 0
587178825Sdfr    if (req_flags & GSS_C_ANON_FLAG)
588178825Sdfr	;                               /* XXX */
589233294Sstas#endif
590178825Sdfr    if (req_flags & GSS_C_DCE_STYLE) {
591178825Sdfr	/* GSS_C_DCE_STYLE implies GSS_C_MUTUAL_FLAG */
592178825Sdfr	flags |= GSS_C_DCE_STYLE | GSS_C_MUTUAL_FLAG;
593178825Sdfr	ap_options |= AP_OPTS_MUTUAL_REQUIRED;
594178825Sdfr    }
595178825Sdfr    if (req_flags & GSS_C_IDENTIFY_FLAG)
596178825Sdfr	flags |= GSS_C_IDENTIFY_FLAG;
597178825Sdfr    if (req_flags & GSS_C_EXTENDED_ERROR_FLAG)
598178825Sdfr	flags |= GSS_C_EXTENDED_ERROR_FLAG;
599178825Sdfr
600233294Sstas    if (req_flags & GSS_C_CONF_FLAG) {
601233294Sstas	flags |= GSS_C_CONF_FLAG;
602233294Sstas    }
603233294Sstas    if (req_flags & GSS_C_INTEG_FLAG) {
604233294Sstas	flags |= GSS_C_INTEG_FLAG;
605233294Sstas    }
606233294Sstas    if (cred == NULL || !(cred->cred_flags & GSS_CF_NO_CI_FLAGS)) {
607233294Sstas	flags |= GSS_C_CONF_FLAG;
608233294Sstas	flags |= GSS_C_INTEG_FLAG;
609233294Sstas    }
610178825Sdfr    flags |= GSS_C_TRANS_FLAG;
611233294Sstas
612178825Sdfr    if (ret_flags)
613178825Sdfr	*ret_flags = flags;
614178825Sdfr    ctx->flags = flags;
615178825Sdfr    ctx->more_flags |= LOCAL;
616233294Sstas
617178825Sdfr    ret = _gsskrb5_create_8003_checksum (minor_status,
618178825Sdfr					 input_chan_bindings,
619178825Sdfr					 flags,
620178825Sdfr					 &fwd_data,
621178825Sdfr					 &cksum);
622178825Sdfr    krb5_data_free (&fwd_data);
623178825Sdfr    if (ret)
624178825Sdfr	goto failure;
625178825Sdfr
626178825Sdfr    enctype = ctx->auth_context->keyblock->keytype;
627178825Sdfr
628233294Sstas    ret = krb5_cc_get_config(context, ctx->ccache, ctx->target,
629233294Sstas			     "time-offset", &timedata);
630233294Sstas    if (ret == 0) {
631233294Sstas	if (timedata.length == 4) {
632233294Sstas	    const u_char *p = timedata.data;
633233294Sstas	    offset = (p[0] <<24) | (p[1] << 16) | (p[2] << 8) | (p[3] << 0);
634233294Sstas	}
635233294Sstas	krb5_data_free(&timedata);
636233294Sstas    }
637233294Sstas
638233294Sstas    if (offset) {
639233294Sstas	krb5_get_kdc_sec_offset (context, &oldoffset, NULL);
640233294Sstas	krb5_set_kdc_sec_offset (context, offset, -1);
641233294Sstas    }
642233294Sstas
643233294Sstas    kret = _krb5_build_authenticator(context,
644178825Sdfr				     ctx->auth_context,
645178825Sdfr				     enctype,
646233294Sstas				     ctx->kcred,
647178825Sdfr				     &cksum,
648178825Sdfr				     &authenticator,
649178825Sdfr				     KRB5_KU_AP_REQ_AUTH);
650178825Sdfr
651178825Sdfr    if (kret) {
652233294Sstas	if (offset)
653233294Sstas	    krb5_set_kdc_sec_offset (context, oldoffset, -1);
654178825Sdfr	*minor_status = kret;
655178825Sdfr	ret = GSS_S_FAILURE;
656178825Sdfr	goto failure;
657178825Sdfr    }
658178825Sdfr
659178825Sdfr    kret = krb5_build_ap_req (context,
660178825Sdfr			      enctype,
661233294Sstas			      ctx->kcred,
662178825Sdfr			      ap_options,
663178825Sdfr			      authenticator,
664178825Sdfr			      &outbuf);
665233294Sstas    if (offset)
666233294Sstas	krb5_set_kdc_sec_offset (context, oldoffset, -1);
667178825Sdfr    if (kret) {
668178825Sdfr	*minor_status = kret;
669178825Sdfr	ret = GSS_S_FAILURE;
670178825Sdfr	goto failure;
671178825Sdfr    }
672178825Sdfr
673233294Sstas    if (flags & GSS_C_DCE_STYLE) {
674233294Sstas	output_token->value = outbuf.data;
675233294Sstas	output_token->length = outbuf.length;
676233294Sstas    } else {
677233294Sstas        ret = _gsskrb5_encapsulate (minor_status, &outbuf, output_token,
678233294Sstas				    (u_char *)(intptr_t)"\x01\x00",
679233294Sstas				    GSS_KRB5_MECHANISM);
680233294Sstas	krb5_data_free (&outbuf);
681233294Sstas	if (ret)
682233294Sstas	    goto failure;
683233294Sstas    }
684178825Sdfr
685178825Sdfr    free_Checksum(&cksum);
686178825Sdfr
687178825Sdfr    if (flags & GSS_C_MUTUAL_FLAG) {
688178825Sdfr	ctx->state = INITIATOR_WAIT_FOR_MUTAL;
689178825Sdfr	return GSS_S_CONTINUE_NEEDED;
690178825Sdfr    }
691178825Sdfr
692178825Sdfr    return gsskrb5_initiator_ready(minor_status, ctx, context);
693178825Sdfrfailure:
694233294Sstas    if (ctx->ccache && (ctx->more_flags & CLOSE_CCACHE))
695233294Sstas	krb5_cc_close(context, ctx->ccache);
696233294Sstas    ctx->ccache = NULL;
697178825Sdfr
698178825Sdfr    return ret;
699233294Sstas}
700178825Sdfr
701233294Sstasstatic krb5_error_code
702233294Sstashandle_error_packet(krb5_context context,
703233294Sstas		    gsskrb5_ctx ctx,
704233294Sstas		    krb5_data indata)
705233294Sstas{
706233294Sstas    krb5_error_code kret;
707233294Sstas    KRB_ERROR error;
708233294Sstas
709233294Sstas    kret = krb5_rd_error(context, &indata, &error);
710233294Sstas    if (kret == 0) {
711233294Sstas	kret = krb5_error_from_rd_error(context, &error, NULL);
712233294Sstas
713233294Sstas	/* save the time skrew for this host */
714233294Sstas	if (kret == KRB5KRB_AP_ERR_SKEW) {
715233294Sstas	    krb5_data timedata;
716233294Sstas	    unsigned char p[4];
717233294Sstas	    int32_t t = error.stime - time(NULL);
718233294Sstas
719233294Sstas	    p[0] = (t >> 24) & 0xFF;
720233294Sstas	    p[1] = (t >> 16) & 0xFF;
721233294Sstas	    p[2] = (t >> 8)  & 0xFF;
722233294Sstas	    p[3] = (t >> 0)  & 0xFF;
723233294Sstas
724233294Sstas	    timedata.data = p;
725233294Sstas	    timedata.length = sizeof(p);
726233294Sstas
727233294Sstas	    krb5_cc_set_config(context, ctx->ccache, ctx->target,
728233294Sstas			       "time-offset", &timedata);
729233294Sstas
730233294Sstas	    if ((ctx->more_flags & RETRIED) == 0)
731233294Sstas		 ctx->state = INITIATOR_RESTART;
732233294Sstas	    ctx->more_flags |= RETRIED;
733233294Sstas	}
734233294Sstas	free_KRB_ERROR (&error);
735233294Sstas    }
736233294Sstas    return kret;
737178825Sdfr}
738178825Sdfr
739233294Sstas
740178825Sdfrstatic OM_uint32
741178825Sdfrrepl_mutual
742178825Sdfr(OM_uint32 * minor_status,
743178825Sdfr gsskrb5_ctx ctx,
744178825Sdfr krb5_context context,
745178825Sdfr const gss_OID mech_type,
746178825Sdfr OM_uint32 req_flags,
747178825Sdfr OM_uint32 time_req,
748178825Sdfr const gss_channel_bindings_t input_chan_bindings,
749178825Sdfr const gss_buffer_t input_token,
750178825Sdfr gss_OID * actual_mech_type,
751178825Sdfr gss_buffer_t output_token,
752178825Sdfr OM_uint32 * ret_flags,
753178825Sdfr OM_uint32 * time_rec
754178825Sdfr    )
755178825Sdfr{
756178825Sdfr    OM_uint32 ret;
757178825Sdfr    krb5_error_code kret;
758178825Sdfr    krb5_data indata;
759178825Sdfr    krb5_ap_rep_enc_part *repl;
760178825Sdfr
761178825Sdfr    output_token->length = 0;
762178825Sdfr    output_token->value = NULL;
763178825Sdfr
764178825Sdfr    if (actual_mech_type)
765178825Sdfr	*actual_mech_type = GSS_KRB5_MECHANISM;
766178825Sdfr
767233294Sstas    if (IS_DCE_STYLE(ctx)) {
768178825Sdfr	/* There is no OID wrapping. */
769178825Sdfr	indata.length	= input_token->length;
770178825Sdfr	indata.data	= input_token->value;
771233294Sstas	kret = krb5_rd_rep(context,
772233294Sstas			   ctx->auth_context,
773233294Sstas			   &indata,
774233294Sstas			   &repl);
775233294Sstas	if (kret) {
776233294Sstas	    ret = _gsskrb5_decapsulate(minor_status,
777233294Sstas				       input_token,
778233294Sstas				       &indata,
779233294Sstas				       "\x03\x00",
780233294Sstas				       GSS_KRB5_MECHANISM);
781233294Sstas	    if (ret == GSS_S_COMPLETE) {
782233294Sstas		*minor_status = handle_error_packet(context, ctx, indata);
783233294Sstas	    } else {
784233294Sstas		*minor_status = kret;
785233294Sstas	    }
786233294Sstas	    return GSS_S_FAILURE;
787233294Sstas	}
788178825Sdfr    } else {
789178825Sdfr	ret = _gsskrb5_decapsulate (minor_status,
790178825Sdfr				    input_token,
791178825Sdfr				    &indata,
792178825Sdfr				    "\x02\x00",
793178825Sdfr				    GSS_KRB5_MECHANISM);
794233294Sstas	if (ret == GSS_S_DEFECTIVE_TOKEN) {
795233294Sstas	    /* check if there is an error token sent instead */
796233294Sstas	    ret = _gsskrb5_decapsulate (minor_status,
797233294Sstas					input_token,
798233294Sstas					&indata,
799233294Sstas					"\x03\x00",
800233294Sstas					GSS_KRB5_MECHANISM);
801233294Sstas	    if (ret == GSS_S_COMPLETE) {
802233294Sstas		*minor_status = handle_error_packet(context, ctx, indata);
803233294Sstas		return GSS_S_FAILURE;
804233294Sstas	    }
805178825Sdfr	}
806233294Sstas	kret = krb5_rd_rep (context,
807233294Sstas			    ctx->auth_context,
808233294Sstas			    &indata,
809233294Sstas			    &repl);
810233294Sstas	if (kret) {
811233294Sstas	    *minor_status = kret;
812233294Sstas	    return GSS_S_FAILURE;
813233294Sstas	}
814178825Sdfr    }
815178825Sdfr
816178825Sdfr    krb5_free_ap_rep_enc_part (context,
817178825Sdfr			       repl);
818178825Sdfr
819178825Sdfr    *minor_status = 0;
820178825Sdfr    if (time_rec) {
821178825Sdfr	ret = _gsskrb5_lifetime_left(minor_status,
822178825Sdfr				     context,
823178825Sdfr				     ctx->lifetime,
824178825Sdfr				     time_rec);
825178825Sdfr    } else {
826178825Sdfr	ret = GSS_S_COMPLETE;
827178825Sdfr    }
828178825Sdfr    if (ret_flags)
829178825Sdfr	*ret_flags = ctx->flags;
830178825Sdfr
831178825Sdfr    if (req_flags & GSS_C_DCE_STYLE) {
832233294Sstas	int32_t local_seq, remote_seq;
833178825Sdfr	krb5_data outbuf;
834178825Sdfr
835233294Sstas	/*
836233294Sstas	 * So DCE_STYLE is strange. The client echos the seq number
837233294Sstas	 * that the server used in the server's mk_rep in its own
838233294Sstas	 * mk_rep(). After when done, it resets to it's own seq number
839233294Sstas	 * for the gss_wrap calls.
840233294Sstas	 */
841178825Sdfr
842233294Sstas	krb5_auth_con_getremoteseqnumber(context, ctx->auth_context, &remote_seq);
843233294Sstas	krb5_auth_con_getlocalseqnumber(context, ctx->auth_context, &local_seq);
844233294Sstas	krb5_auth_con_setlocalseqnumber(context, ctx->auth_context, remote_seq);
845233294Sstas
846233294Sstas	kret = krb5_mk_rep(context, ctx->auth_context, &outbuf);
847178825Sdfr	if (kret) {
848178825Sdfr	    *minor_status = kret;
849178825Sdfr	    return GSS_S_FAILURE;
850178825Sdfr	}
851233294Sstas
852233294Sstas	/* reset local seq number */
853233294Sstas	krb5_auth_con_setlocalseqnumber(context, ctx->auth_context, local_seq);
854233294Sstas
855178825Sdfr	output_token->length = outbuf.length;
856178825Sdfr	output_token->value  = outbuf.data;
857178825Sdfr    }
858178825Sdfr
859178825Sdfr    return gsskrb5_initiator_ready(minor_status, ctx, context);
860178825Sdfr}
861178825Sdfr
862178825Sdfr/*
863178825Sdfr * gss_init_sec_context
864178825Sdfr */
865178825Sdfr
866233294SstasOM_uint32 GSSAPI_CALLCONV _gsskrb5_init_sec_context
867178825Sdfr(OM_uint32 * minor_status,
868233294Sstas const gss_cred_id_t cred_handle,
869178825Sdfr gss_ctx_id_t * context_handle,
870178825Sdfr const gss_name_t target_name,
871178825Sdfr const gss_OID mech_type,
872178825Sdfr OM_uint32 req_flags,
873178825Sdfr OM_uint32 time_req,
874178825Sdfr const gss_channel_bindings_t input_chan_bindings,
875178825Sdfr const gss_buffer_t input_token,
876178825Sdfr gss_OID * actual_mech_type,
877178825Sdfr gss_buffer_t output_token,
878178825Sdfr OM_uint32 * ret_flags,
879178825Sdfr OM_uint32 * time_rec
880178825Sdfr    )
881178825Sdfr{
882178825Sdfr    krb5_context context;
883233294Sstas    gsskrb5_cred cred = (gsskrb5_cred)cred_handle;
884178825Sdfr    gsskrb5_ctx ctx;
885178825Sdfr    OM_uint32 ret;
886178825Sdfr
887178825Sdfr    GSSAPI_KRB5_INIT (&context);
888178825Sdfr
889178825Sdfr    output_token->length = 0;
890178825Sdfr    output_token->value  = NULL;
891178825Sdfr
892178825Sdfr    if (context_handle == NULL) {
893178825Sdfr	*minor_status = 0;
894178825Sdfr	return GSS_S_FAILURE | GSS_S_CALL_BAD_STRUCTURE;
895178825Sdfr    }
896178825Sdfr
897178825Sdfr    if (ret_flags)
898178825Sdfr	*ret_flags = 0;
899178825Sdfr    if (time_rec)
900178825Sdfr	*time_rec = 0;
901178825Sdfr
902178825Sdfr    if (target_name == GSS_C_NO_NAME) {
903178825Sdfr	if (actual_mech_type)
904178825Sdfr	    *actual_mech_type = GSS_C_NO_OID;
905178825Sdfr	*minor_status = 0;
906178825Sdfr	return GSS_S_BAD_NAME;
907178825Sdfr    }
908178825Sdfr
909233294Sstas    if (mech_type != GSS_C_NO_OID &&
910178825Sdfr	!gss_oid_equal(mech_type, GSS_KRB5_MECHANISM))
911178825Sdfr	return GSS_S_BAD_MECH;
912178825Sdfr
913178825Sdfr    if (input_token == GSS_C_NO_BUFFER || input_token->length == 0) {
914233294Sstas	OM_uint32 ret1;
915178825Sdfr
916178825Sdfr	if (*context_handle != GSS_C_NO_CONTEXT) {
917178825Sdfr	    *minor_status = 0;
918178825Sdfr	    return GSS_S_FAILURE | GSS_S_CALL_BAD_STRUCTURE;
919178825Sdfr	}
920233294Sstas
921233294Sstas	ret1 = _gsskrb5_create_ctx(minor_status,
922178825Sdfr				  context_handle,
923178825Sdfr				  context,
924178825Sdfr				  input_chan_bindings,
925178825Sdfr				  INITIATOR_START);
926233294Sstas	if (ret1)
927233294Sstas	    return ret1;
928178825Sdfr    }
929178825Sdfr
930178825Sdfr    if (*context_handle == GSS_C_NO_CONTEXT) {
931178825Sdfr	*minor_status = 0;
932178825Sdfr	return GSS_S_FAILURE | GSS_S_CALL_BAD_STRUCTURE;
933178825Sdfr    }
934178825Sdfr
935178825Sdfr    ctx = (gsskrb5_ctx) *context_handle;
936178825Sdfr
937178825Sdfr    HEIMDAL_MUTEX_lock(&ctx->ctx_id_mutex);
938178825Sdfr
939233294Sstas again:
940178825Sdfr    switch (ctx->state) {
941178825Sdfr    case INITIATOR_START:
942178825Sdfr	ret = init_auth(minor_status,
943178825Sdfr			cred,
944178825Sdfr			ctx,
945178825Sdfr			context,
946233294Sstas			target_name,
947178825Sdfr			mech_type,
948178825Sdfr			req_flags,
949178825Sdfr			time_req,
950178825Sdfr			input_token,
951178825Sdfr			actual_mech_type,
952178825Sdfr			output_token,
953178825Sdfr			ret_flags,
954178825Sdfr			time_rec);
955233294Sstas	if (ret != GSS_S_COMPLETE)
956233294Sstas	    break;
957233294Sstas	/* FALL THOUGH */
958233294Sstas    case INITIATOR_RESTART:
959233294Sstas	ret = init_auth_restart(minor_status,
960233294Sstas				cred,
961233294Sstas				ctx,
962233294Sstas				context,
963233294Sstas				req_flags,
964233294Sstas				input_chan_bindings,
965233294Sstas				input_token,
966233294Sstas				actual_mech_type,
967233294Sstas				output_token,
968233294Sstas				ret_flags,
969233294Sstas				time_rec);
970178825Sdfr	break;
971178825Sdfr    case INITIATOR_WAIT_FOR_MUTAL:
972178825Sdfr	ret = repl_mutual(minor_status,
973178825Sdfr			  ctx,
974178825Sdfr			  context,
975178825Sdfr			  mech_type,
976178825Sdfr			  req_flags,
977178825Sdfr			  time_req,
978178825Sdfr			  input_chan_bindings,
979178825Sdfr			  input_token,
980178825Sdfr			  actual_mech_type,
981178825Sdfr			  output_token,
982178825Sdfr			  ret_flags,
983178825Sdfr			  time_rec);
984233294Sstas	if (ctx->state == INITIATOR_RESTART)
985233294Sstas	    goto again;
986178825Sdfr	break;
987178825Sdfr    case INITIATOR_READY:
988233294Sstas	/*
989178825Sdfr	 * If we get there, the caller have called
990178825Sdfr	 * gss_init_sec_context() one time too many.
991178825Sdfr	 */
992233294Sstas	_gsskrb5_set_status(EINVAL, "init_sec_context "
993233294Sstas			    "called one time too many");
994233294Sstas	*minor_status = EINVAL;
995178825Sdfr	ret = GSS_S_BAD_STATUS;
996178825Sdfr	break;
997178825Sdfr    default:
998233294Sstas	_gsskrb5_set_status(EINVAL, "init_sec_context "
999233294Sstas			    "invalid state %d for client",
1000233294Sstas			    (int)ctx->state);
1001233294Sstas	*minor_status = EINVAL;
1002178825Sdfr	ret = GSS_S_BAD_STATUS;
1003178825Sdfr	break;
1004178825Sdfr    }
1005178825Sdfr    HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex);
1006178825Sdfr
1007178825Sdfr    /* destroy context in case of error */
1008178825Sdfr    if (GSS_ERROR(ret)) {
1009178825Sdfr	OM_uint32 min2;
1010178825Sdfr	_gsskrb5_delete_sec_context(&min2, context_handle, GSS_C_NO_BUFFER);
1011178825Sdfr    }
1012178825Sdfr
1013178825Sdfr    return ret;
1014178825Sdfr
1015178825Sdfr}
1016