1178825Sdfr/*
2178825Sdfr * Copyright (c) 1997 - 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 "krb5/gsskrb5_locl.h"
35178825Sdfr
36178825SdfrRCSID("$Id: accept_sec_context.c 20199 2007-02-07 22:36:39Z lha $");
37178825Sdfr
38178825SdfrHEIMDAL_MUTEX gssapi_keytab_mutex = HEIMDAL_MUTEX_INITIALIZER;
39178825Sdfrkrb5_keytab _gsskrb5_keytab;
40178825Sdfr
41178825SdfrOM_uint32
42178825Sdfr_gsskrb5_register_acceptor_identity (const char *identity)
43178825Sdfr{
44178825Sdfr    krb5_context context;
45178825Sdfr    krb5_error_code ret;
46178825Sdfr
47178825Sdfr    ret = _gsskrb5_init(&context);
48178825Sdfr    if(ret)
49178825Sdfr	return GSS_S_FAILURE;
50178825Sdfr
51178825Sdfr    HEIMDAL_MUTEX_lock(&gssapi_keytab_mutex);
52178825Sdfr
53178825Sdfr    if(_gsskrb5_keytab != NULL) {
54178825Sdfr	krb5_kt_close(context, _gsskrb5_keytab);
55178825Sdfr	_gsskrb5_keytab = NULL;
56178825Sdfr    }
57178825Sdfr    if (identity == NULL) {
58178825Sdfr	ret = krb5_kt_default(context, &_gsskrb5_keytab);
59178825Sdfr    } else {
60178825Sdfr	char *p;
61178825Sdfr
62178825Sdfr	asprintf(&p, "FILE:%s", identity);
63178825Sdfr	if(p == NULL) {
64178825Sdfr	    HEIMDAL_MUTEX_unlock(&gssapi_keytab_mutex);
65178825Sdfr	    return GSS_S_FAILURE;
66178825Sdfr	}
67178825Sdfr	ret = krb5_kt_resolve(context, p, &_gsskrb5_keytab);
68178825Sdfr	free(p);
69178825Sdfr    }
70178825Sdfr    HEIMDAL_MUTEX_unlock(&gssapi_keytab_mutex);
71178825Sdfr    if(ret)
72178825Sdfr	return GSS_S_FAILURE;
73178825Sdfr    return GSS_S_COMPLETE;
74178825Sdfr}
75178825Sdfr
76178825Sdfrvoid
77178825Sdfr_gsskrb5i_is_cfx(gsskrb5_ctx ctx, int *is_cfx)
78178825Sdfr{
79178825Sdfr    krb5_keyblock *key;
80178825Sdfr    int acceptor = (ctx->more_flags & LOCAL) == 0;
81178825Sdfr
82178825Sdfr    *is_cfx = 0;
83178825Sdfr
84178825Sdfr    if (acceptor) {
85178825Sdfr	if (ctx->auth_context->local_subkey)
86178825Sdfr	    key = ctx->auth_context->local_subkey;
87178825Sdfr	else
88178825Sdfr	    key = ctx->auth_context->remote_subkey;
89178825Sdfr    } else {
90178825Sdfr	if (ctx->auth_context->remote_subkey)
91178825Sdfr	    key = ctx->auth_context->remote_subkey;
92178825Sdfr	else
93178825Sdfr	    key = ctx->auth_context->local_subkey;
94178825Sdfr    }
95178825Sdfr    if (key == NULL)
96178825Sdfr	key = ctx->auth_context->keyblock;
97178825Sdfr
98178825Sdfr    if (key == NULL)
99178825Sdfr	return;
100178825Sdfr
101178825Sdfr    switch (key->keytype) {
102178825Sdfr    case ETYPE_DES_CBC_CRC:
103178825Sdfr    case ETYPE_DES_CBC_MD4:
104178825Sdfr    case ETYPE_DES_CBC_MD5:
105178825Sdfr    case ETYPE_DES3_CBC_MD5:
106178825Sdfr    case ETYPE_DES3_CBC_SHA1:
107178825Sdfr    case ETYPE_ARCFOUR_HMAC_MD5:
108178825Sdfr    case ETYPE_ARCFOUR_HMAC_MD5_56:
109178825Sdfr	break;
110178825Sdfr    default :
111178825Sdfr	*is_cfx = 1;
112178825Sdfr	if ((acceptor && ctx->auth_context->local_subkey) ||
113178825Sdfr	    (!acceptor && ctx->auth_context->remote_subkey))
114178825Sdfr	    ctx->more_flags |= ACCEPTOR_SUBKEY;
115178825Sdfr	break;
116178825Sdfr    }
117178825Sdfr}
118178825Sdfr
119178825Sdfr
120178825Sdfrstatic OM_uint32
121178825Sdfrgsskrb5_accept_delegated_token
122178825Sdfr(OM_uint32 * minor_status,
123178825Sdfr gsskrb5_ctx ctx,
124178825Sdfr krb5_context context,
125178825Sdfr gss_cred_id_t * delegated_cred_handle
126178825Sdfr    )
127178825Sdfr{
128178825Sdfr    krb5_ccache ccache = NULL;
129178825Sdfr    krb5_error_code kret;
130178825Sdfr    int32_t ac_flags, ret = GSS_S_COMPLETE;
131178825Sdfr
132178825Sdfr    *minor_status = 0;
133178825Sdfr
134178825Sdfr    /* XXX Create a new delegated_cred_handle? */
135178825Sdfr    if (delegated_cred_handle == NULL) {
136178825Sdfr	kret = krb5_cc_default (context, &ccache);
137178825Sdfr    } else {
138178825Sdfr	*delegated_cred_handle = NULL;
139178825Sdfr	kret = krb5_cc_gen_new (context, &krb5_mcc_ops, &ccache);
140178825Sdfr    }
141178825Sdfr    if (kret) {
142178825Sdfr	ctx->flags &= ~GSS_C_DELEG_FLAG;
143178825Sdfr	goto out;
144178825Sdfr    }
145178825Sdfr
146178825Sdfr    kret = krb5_cc_initialize(context, ccache, ctx->source);
147178825Sdfr    if (kret) {
148178825Sdfr	ctx->flags &= ~GSS_C_DELEG_FLAG;
149178825Sdfr	goto out;
150178825Sdfr    }
151178825Sdfr
152178825Sdfr    krb5_auth_con_removeflags(context,
153178825Sdfr			      ctx->auth_context,
154178825Sdfr			      KRB5_AUTH_CONTEXT_DO_TIME,
155178825Sdfr			      &ac_flags);
156178825Sdfr    kret = krb5_rd_cred2(context,
157178825Sdfr			 ctx->auth_context,
158178825Sdfr			 ccache,
159178825Sdfr			 &ctx->fwd_data);
160178825Sdfr    krb5_auth_con_setflags(context,
161178825Sdfr			   ctx->auth_context,
162178825Sdfr			   ac_flags);
163178825Sdfr    if (kret) {
164178825Sdfr	ctx->flags &= ~GSS_C_DELEG_FLAG;
165178825Sdfr	ret = GSS_S_FAILURE;
166178825Sdfr	*minor_status = kret;
167178825Sdfr	goto out;
168178825Sdfr    }
169178825Sdfr
170178825Sdfr    if (delegated_cred_handle) {
171178825Sdfr	gsskrb5_cred handle;
172178825Sdfr
173178825Sdfr	ret = _gsskrb5_import_cred(minor_status,
174178825Sdfr				   ccache,
175178825Sdfr				   NULL,
176178825Sdfr				   NULL,
177178825Sdfr				   delegated_cred_handle);
178178825Sdfr	if (ret != GSS_S_COMPLETE)
179178825Sdfr	    goto out;
180178825Sdfr
181178825Sdfr	handle = (gsskrb5_cred) *delegated_cred_handle;
182178825Sdfr
183178825Sdfr	handle->cred_flags |= GSS_CF_DESTROY_CRED_ON_RELEASE;
184178825Sdfr	krb5_cc_close(context, ccache);
185178825Sdfr	ccache = NULL;
186178825Sdfr    }
187178825Sdfr
188178825Sdfrout:
189178825Sdfr    if (ccache) {
190178825Sdfr	/* Don't destroy the default cred cache */
191178825Sdfr	if (delegated_cred_handle == NULL)
192178825Sdfr	    krb5_cc_close(context, ccache);
193178825Sdfr	else
194178825Sdfr	    krb5_cc_destroy(context, ccache);
195178825Sdfr    }
196178825Sdfr    return ret;
197178825Sdfr}
198178825Sdfr
199178825Sdfrstatic OM_uint32
200178825Sdfrgsskrb5_acceptor_ready(OM_uint32 * minor_status,
201178825Sdfr		       gsskrb5_ctx ctx,
202178825Sdfr		       krb5_context context,
203178825Sdfr		       gss_cred_id_t *delegated_cred_handle)
204178825Sdfr{
205178825Sdfr    OM_uint32 ret;
206178825Sdfr    int32_t seq_number;
207178825Sdfr    int is_cfx = 0;
208178825Sdfr
209178825Sdfr    krb5_auth_getremoteseqnumber (context,
210178825Sdfr				  ctx->auth_context,
211178825Sdfr				  &seq_number);
212178825Sdfr
213178825Sdfr    _gsskrb5i_is_cfx(ctx, &is_cfx);
214178825Sdfr
215178825Sdfr    ret = _gssapi_msg_order_create(minor_status,
216178825Sdfr				   &ctx->order,
217178825Sdfr				   _gssapi_msg_order_f(ctx->flags),
218178825Sdfr				   seq_number, 0, is_cfx);
219178825Sdfr    if (ret)
220178825Sdfr	return ret;
221178825Sdfr
222178825Sdfr    /*
223178825Sdfr     * If requested, set local sequence num to remote sequence if this
224178825Sdfr     * isn't a mutual authentication context
225178825Sdfr     */
226178825Sdfr    if (!(ctx->flags & GSS_C_MUTUAL_FLAG) && _gssapi_msg_order_f(ctx->flags)) {
227178825Sdfr	krb5_auth_con_setlocalseqnumber(context,
228178825Sdfr					ctx->auth_context,
229178825Sdfr					seq_number);
230178825Sdfr    }
231178825Sdfr
232178825Sdfr    /*
233178825Sdfr     * We should handle the delegation ticket, in case it's there
234178825Sdfr     */
235178825Sdfr    if (ctx->fwd_data.length > 0 && (ctx->flags & GSS_C_DELEG_FLAG)) {
236178825Sdfr	ret = gsskrb5_accept_delegated_token(minor_status,
237178825Sdfr					     ctx,
238178825Sdfr					     context,
239178825Sdfr					     delegated_cred_handle);
240178825Sdfr	if (ret)
241178825Sdfr	    return ret;
242178825Sdfr    } else {
243178825Sdfr	/* Well, looks like it wasn't there after all */
244178825Sdfr	ctx->flags &= ~GSS_C_DELEG_FLAG;
245178825Sdfr    }
246178825Sdfr
247178825Sdfr    ctx->state = ACCEPTOR_READY;
248178825Sdfr    ctx->more_flags |= OPEN;
249178825Sdfr
250178825Sdfr    return GSS_S_COMPLETE;
251178825Sdfr}
252178825Sdfr
253178825Sdfrstatic OM_uint32
254178825Sdfrgsskrb5_acceptor_start(OM_uint32 * minor_status,
255178825Sdfr		       gsskrb5_ctx ctx,
256178825Sdfr		       krb5_context context,
257178825Sdfr		       const gss_cred_id_t acceptor_cred_handle,
258178825Sdfr		       const gss_buffer_t input_token_buffer,
259178825Sdfr		       const gss_channel_bindings_t input_chan_bindings,
260178825Sdfr		       gss_name_t * src_name,
261178825Sdfr		       gss_OID * mech_type,
262178825Sdfr		       gss_buffer_t output_token,
263178825Sdfr		       OM_uint32 * ret_flags,
264178825Sdfr		       OM_uint32 * time_rec,
265178825Sdfr		       gss_cred_id_t * delegated_cred_handle)
266178825Sdfr{
267178825Sdfr    krb5_error_code kret;
268178825Sdfr    OM_uint32 ret = GSS_S_COMPLETE;
269178825Sdfr    krb5_data indata;
270178825Sdfr    krb5_flags ap_options;
271178825Sdfr    krb5_keytab keytab = NULL;
272178825Sdfr    int is_cfx = 0;
273178825Sdfr    const gsskrb5_cred acceptor_cred = (gsskrb5_cred)acceptor_cred_handle;
274178825Sdfr
275178825Sdfr    /*
276178825Sdfr     * We may, or may not, have an escapsulation.
277178825Sdfr     */
278178825Sdfr    ret = _gsskrb5_decapsulate (minor_status,
279178825Sdfr				input_token_buffer,
280178825Sdfr				&indata,
281178825Sdfr				"\x01\x00",
282178825Sdfr				GSS_KRB5_MECHANISM);
283178825Sdfr
284178825Sdfr    if (ret) {
285178825Sdfr	/* Assume that there is no OID wrapping. */
286178825Sdfr	indata.length	= input_token_buffer->length;
287178825Sdfr	indata.data	= input_token_buffer->value;
288178825Sdfr    }
289178825Sdfr
290178825Sdfr    /*
291178825Sdfr     * We need to get our keytab
292178825Sdfr     */
293178825Sdfr    if (acceptor_cred == NULL) {
294178825Sdfr	if (_gsskrb5_keytab != NULL)
295178825Sdfr	    keytab = _gsskrb5_keytab;
296178825Sdfr    } else if (acceptor_cred->keytab != NULL) {
297178825Sdfr	keytab = acceptor_cred->keytab;
298178825Sdfr    }
299178825Sdfr
300178825Sdfr    /*
301178825Sdfr     * We need to check the ticket and create the AP-REP packet
302178825Sdfr     */
303178825Sdfr
304178825Sdfr    {
305178825Sdfr	krb5_rd_req_in_ctx in = NULL;
306178825Sdfr	krb5_rd_req_out_ctx out = NULL;
307178825Sdfr
308178825Sdfr	kret = krb5_rd_req_in_ctx_alloc(context, &in);
309178825Sdfr	if (kret == 0)
310178825Sdfr	    kret = krb5_rd_req_in_set_keytab(context, in, keytab);
311178825Sdfr	if (kret) {
312178825Sdfr	    if (in)
313178825Sdfr		krb5_rd_req_in_ctx_free(context, in);
314178825Sdfr	    ret = GSS_S_FAILURE;
315178825Sdfr	    *minor_status = kret;
316178825Sdfr	    return ret;
317178825Sdfr	}
318178825Sdfr
319178825Sdfr	kret = krb5_rd_req_ctx(context,
320178825Sdfr			       &ctx->auth_context,
321178825Sdfr			       &indata,
322178825Sdfr			       (acceptor_cred_handle == GSS_C_NO_CREDENTIAL) ? NULL : acceptor_cred->principal,
323178825Sdfr			       in, &out);
324178825Sdfr	krb5_rd_req_in_ctx_free(context, in);
325178825Sdfr	if (kret) {
326178825Sdfr	    ret = GSS_S_FAILURE;
327178825Sdfr	    *minor_status = kret;
328178825Sdfr	    return ret;
329178825Sdfr	}
330178825Sdfr
331178825Sdfr	/*
332178825Sdfr	 * We need to remember some data on the context_handle.
333178825Sdfr	 */
334178825Sdfr	kret = krb5_rd_req_out_get_ap_req_options(context, out,
335178825Sdfr						  &ap_options);
336178825Sdfr	if (kret == 0)
337178825Sdfr	    kret = krb5_rd_req_out_get_ticket(context, out,
338178825Sdfr					      &ctx->ticket);
339178825Sdfr	if (kret == 0)
340178825Sdfr	    kret = krb5_rd_req_out_get_keyblock(context, out,
341178825Sdfr						&ctx->service_keyblock);
342178825Sdfr	ctx->lifetime = ctx->ticket->ticket.endtime;
343178825Sdfr
344178825Sdfr	krb5_rd_req_out_ctx_free(context, out);
345178825Sdfr	if (kret) {
346178825Sdfr	    ret = GSS_S_FAILURE;
347178825Sdfr	    *minor_status = kret;
348178825Sdfr	    return ret;
349178825Sdfr	}
350178825Sdfr    }
351178825Sdfr
352178825Sdfr
353178825Sdfr    /*
354178825Sdfr     * We need to copy the principal names to the context and the
355178825Sdfr     * calling layer.
356178825Sdfr     */
357178825Sdfr    kret = krb5_copy_principal(context,
358178825Sdfr			       ctx->ticket->client,
359178825Sdfr			       &ctx->source);
360178825Sdfr    if (kret) {
361178825Sdfr	ret = GSS_S_FAILURE;
362178825Sdfr	*minor_status = kret;
363178825Sdfr    }
364178825Sdfr
365178825Sdfr    kret = krb5_copy_principal(context,
366178825Sdfr			       ctx->ticket->server,
367178825Sdfr			       &ctx->target);
368178825Sdfr    if (kret) {
369178825Sdfr	ret = GSS_S_FAILURE;
370178825Sdfr	*minor_status = kret;
371178825Sdfr	return ret;
372178825Sdfr    }
373178825Sdfr
374178825Sdfr    /*
375178825Sdfr     * We need to setup some compat stuff, this assumes that
376178825Sdfr     * context_handle->target is already set.
377178825Sdfr     */
378178825Sdfr    ret = _gss_DES3_get_mic_compat(minor_status, ctx, context);
379178825Sdfr    if (ret)
380178825Sdfr	return ret;
381178825Sdfr
382178825Sdfr    if (src_name != NULL) {
383178825Sdfr	kret = krb5_copy_principal (context,
384178825Sdfr				    ctx->ticket->client,
385178825Sdfr				    (gsskrb5_name*)src_name);
386178825Sdfr	if (kret) {
387178825Sdfr	    ret = GSS_S_FAILURE;
388178825Sdfr	    *minor_status = kret;
389178825Sdfr	    return ret;
390178825Sdfr	}
391178825Sdfr    }
392178825Sdfr
393178825Sdfr    /*
394178825Sdfr     * We need to get the flags out of the 8003 checksum.
395178825Sdfr     */
396178825Sdfr    {
397178825Sdfr	krb5_authenticator authenticator;
398178825Sdfr
399178825Sdfr	kret = krb5_auth_con_getauthenticator(context,
400178825Sdfr					      ctx->auth_context,
401178825Sdfr					      &authenticator);
402178825Sdfr	if(kret) {
403178825Sdfr	    ret = GSS_S_FAILURE;
404178825Sdfr	    *minor_status = kret;
405178825Sdfr	    return ret;
406178825Sdfr	}
407178825Sdfr
408178825Sdfr        if (authenticator->cksum->cksumtype == CKSUMTYPE_GSSAPI) {
409178825Sdfr            ret = _gsskrb5_verify_8003_checksum(minor_status,
410178825Sdfr						input_chan_bindings,
411178825Sdfr						authenticator->cksum,
412178825Sdfr						&ctx->flags,
413178825Sdfr						&ctx->fwd_data);
414178825Sdfr
415178825Sdfr	    krb5_free_authenticator(context, &authenticator);
416178825Sdfr	    if (ret) {
417178825Sdfr		return ret;
418178825Sdfr	    }
419178825Sdfr        } else {
420178825Sdfr	    krb5_crypto crypto;
421178825Sdfr
422178825Sdfr	    kret = krb5_crypto_init(context,
423178825Sdfr				    ctx->auth_context->keyblock,
424178825Sdfr				    0, &crypto);
425178825Sdfr	    if(kret) {
426178825Sdfr		krb5_free_authenticator(context, &authenticator);
427178825Sdfr
428178825Sdfr		ret = GSS_S_FAILURE;
429178825Sdfr		*minor_status = kret;
430178825Sdfr		return ret;
431178825Sdfr	    }
432178825Sdfr
433178825Sdfr	    /*
434178825Sdfr	     * Windows accepts Samba3's use of a kerberos, rather than
435178825Sdfr	     * GSSAPI checksum here
436178825Sdfr	     */
437178825Sdfr
438178825Sdfr	    kret = krb5_verify_checksum(context,
439178825Sdfr					crypto, KRB5_KU_AP_REQ_AUTH_CKSUM, NULL, 0,
440178825Sdfr					authenticator->cksum);
441178825Sdfr	    krb5_free_authenticator(context, &authenticator);
442178825Sdfr	    krb5_crypto_destroy(context, crypto);
443178825Sdfr
444178825Sdfr	    if(kret) {
445178825Sdfr		ret = GSS_S_BAD_SIG;
446178825Sdfr		*minor_status = kret;
447178825Sdfr		return ret;
448178825Sdfr	    }
449178825Sdfr
450178825Sdfr	    /*
451178825Sdfr	     * Samba style get some flags (but not DCE-STYLE)
452178825Sdfr	     */
453178825Sdfr	    ctx->flags =
454178825Sdfr		GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG | GSS_C_SEQUENCE_FLAG;
455178825Sdfr        }
456178825Sdfr    }
457178825Sdfr
458178825Sdfr    if(ctx->flags & GSS_C_MUTUAL_FLAG) {
459178825Sdfr	krb5_data outbuf;
460178825Sdfr
461178825Sdfr	_gsskrb5i_is_cfx(ctx, &is_cfx);
462178825Sdfr
463178825Sdfr	if (is_cfx != 0
464178825Sdfr	    || (ap_options & AP_OPTS_USE_SUBKEY)) {
465178825Sdfr	    kret = krb5_auth_con_addflags(context,
466178825Sdfr					  ctx->auth_context,
467178825Sdfr					  KRB5_AUTH_CONTEXT_USE_SUBKEY,
468178825Sdfr					  NULL);
469178825Sdfr	    ctx->more_flags |= ACCEPTOR_SUBKEY;
470178825Sdfr	}
471178825Sdfr
472178825Sdfr	kret = krb5_mk_rep(context,
473178825Sdfr			   ctx->auth_context,
474178825Sdfr			   &outbuf);
475178825Sdfr	if (kret) {
476178825Sdfr	    *minor_status = kret;
477178825Sdfr	    return GSS_S_FAILURE;
478178825Sdfr	}
479178825Sdfr
480178825Sdfr	if (IS_DCE_STYLE(ctx)) {
481178825Sdfr	    output_token->length = outbuf.length;
482178825Sdfr	    output_token->value = outbuf.data;
483178825Sdfr	} else {
484178825Sdfr	    ret = _gsskrb5_encapsulate(minor_status,
485178825Sdfr				       &outbuf,
486178825Sdfr				       output_token,
487178825Sdfr				       "\x02\x00",
488178825Sdfr				       GSS_KRB5_MECHANISM);
489178825Sdfr	    krb5_data_free (&outbuf);
490178825Sdfr	    if (ret)
491178825Sdfr		return ret;
492178825Sdfr	}
493178825Sdfr    }
494178825Sdfr
495178825Sdfr    ctx->flags |= GSS_C_TRANS_FLAG;
496178825Sdfr
497178825Sdfr    /* Remember the flags */
498178825Sdfr
499178825Sdfr    ctx->lifetime = ctx->ticket->ticket.endtime;
500178825Sdfr    ctx->more_flags |= OPEN;
501178825Sdfr
502178825Sdfr    if (mech_type)
503178825Sdfr	*mech_type = GSS_KRB5_MECHANISM;
504178825Sdfr
505178825Sdfr    if (time_rec) {
506178825Sdfr	ret = _gsskrb5_lifetime_left(minor_status,
507178825Sdfr				     context,
508178825Sdfr				     ctx->lifetime,
509178825Sdfr				     time_rec);
510178825Sdfr	if (ret) {
511178825Sdfr	    return ret;
512178825Sdfr	}
513178825Sdfr    }
514178825Sdfr
515178825Sdfr    /*
516178825Sdfr     * When GSS_C_DCE_STYLE is in use, we need ask for a AP-REP from
517178825Sdfr     * the client.
518178825Sdfr     */
519178825Sdfr    if (IS_DCE_STYLE(ctx)) {
520178825Sdfr	/*
521178825Sdfr	 * Return flags to caller, but we haven't processed
522178825Sdfr	 * delgations yet
523178825Sdfr	 */
524178825Sdfr	if (ret_flags)
525178825Sdfr	    *ret_flags = (ctx->flags & ~GSS_C_DELEG_FLAG);
526178825Sdfr
527178825Sdfr	ctx->state = ACCEPTOR_WAIT_FOR_DCESTYLE;
528178825Sdfr	return GSS_S_CONTINUE_NEEDED;
529178825Sdfr    }
530178825Sdfr
531178825Sdfr    ret = gsskrb5_acceptor_ready(minor_status, ctx, context,
532178825Sdfr				 delegated_cred_handle);
533178825Sdfr
534178825Sdfr    if (ret_flags)
535178825Sdfr	*ret_flags = ctx->flags;
536178825Sdfr
537178825Sdfr    return ret;
538178825Sdfr}
539178825Sdfr
540178825Sdfrstatic OM_uint32
541178825Sdfracceptor_wait_for_dcestyle(OM_uint32 * minor_status,
542178825Sdfr			   gsskrb5_ctx ctx,
543178825Sdfr			   krb5_context context,
544178825Sdfr			   const gss_cred_id_t acceptor_cred_handle,
545178825Sdfr			   const gss_buffer_t input_token_buffer,
546178825Sdfr			   const gss_channel_bindings_t input_chan_bindings,
547178825Sdfr			   gss_name_t * src_name,
548178825Sdfr			   gss_OID * mech_type,
549178825Sdfr			   gss_buffer_t output_token,
550178825Sdfr			   OM_uint32 * ret_flags,
551178825Sdfr			   OM_uint32 * time_rec,
552178825Sdfr			   gss_cred_id_t * delegated_cred_handle)
553178825Sdfr{
554178825Sdfr    OM_uint32 ret;
555178825Sdfr    krb5_error_code kret;
556178825Sdfr    krb5_data inbuf;
557178825Sdfr    int32_t r_seq_number, l_seq_number;
558178825Sdfr
559178825Sdfr    /*
560178825Sdfr     * We know it's GSS_C_DCE_STYLE so we don't need to decapsulate the AP_REP
561178825Sdfr     */
562178825Sdfr
563178825Sdfr    inbuf.length = input_token_buffer->length;
564178825Sdfr    inbuf.data = input_token_buffer->value;
565178825Sdfr
566178825Sdfr    /*
567178825Sdfr     * We need to remeber the old remote seq_number, then check if the
568178825Sdfr     * client has replied with our local seq_number, and then reset
569178825Sdfr     * the remote seq_number to the old value
570178825Sdfr     */
571178825Sdfr    {
572178825Sdfr	kret = krb5_auth_con_getlocalseqnumber(context,
573178825Sdfr					       ctx->auth_context,
574178825Sdfr					       &l_seq_number);
575178825Sdfr	if (kret) {
576178825Sdfr	    *minor_status = kret;
577178825Sdfr	    return GSS_S_FAILURE;
578178825Sdfr	}
579178825Sdfr
580178825Sdfr	kret = krb5_auth_getremoteseqnumber(context,
581178825Sdfr					    ctx->auth_context,
582178825Sdfr					    &r_seq_number);
583178825Sdfr	if (kret) {
584178825Sdfr	    *minor_status = kret;
585178825Sdfr	    return GSS_S_FAILURE;
586178825Sdfr	}
587178825Sdfr
588178825Sdfr	kret = krb5_auth_con_setremoteseqnumber(context,
589178825Sdfr						ctx->auth_context,
590178825Sdfr						l_seq_number);
591178825Sdfr	if (kret) {
592178825Sdfr	    *minor_status = kret;
593178825Sdfr	    return GSS_S_FAILURE;
594178825Sdfr	}
595178825Sdfr    }
596178825Sdfr
597178825Sdfr    /*
598178825Sdfr     * We need to verify the AP_REP, but we need to flag that this is
599178825Sdfr     * DCE_STYLE, so don't check the timestamps this time, but put the
600178825Sdfr     * flag DO_TIME back afterward.
601178825Sdfr    */
602178825Sdfr    {
603178825Sdfr	krb5_ap_rep_enc_part *repl;
604178825Sdfr	int32_t auth_flags;
605178825Sdfr
606178825Sdfr	krb5_auth_con_removeflags(context,
607178825Sdfr				  ctx->auth_context,
608178825Sdfr				  KRB5_AUTH_CONTEXT_DO_TIME,
609178825Sdfr				  &auth_flags);
610178825Sdfr
611178825Sdfr	kret = krb5_rd_rep(context, ctx->auth_context, &inbuf, &repl);
612178825Sdfr	if (kret) {
613178825Sdfr	    *minor_status = kret;
614178825Sdfr	    return GSS_S_FAILURE;
615178825Sdfr	}
616178825Sdfr	krb5_free_ap_rep_enc_part(context, repl);
617178825Sdfr	krb5_auth_con_setflags(context, ctx->auth_context, auth_flags);
618178825Sdfr    }
619178825Sdfr
620178825Sdfr    /* We need to check the liftime */
621178825Sdfr    {
622178825Sdfr	OM_uint32 lifetime_rec;
623178825Sdfr
624178825Sdfr	ret = _gsskrb5_lifetime_left(minor_status,
625178825Sdfr				     context,
626178825Sdfr				     ctx->lifetime,
627178825Sdfr				     &lifetime_rec);
628178825Sdfr	if (ret) {
629178825Sdfr	    return ret;
630178825Sdfr	}
631178825Sdfr	if (lifetime_rec == 0) {
632178825Sdfr	    return GSS_S_CONTEXT_EXPIRED;
633178825Sdfr	}
634178825Sdfr
635178825Sdfr	if (time_rec) *time_rec = lifetime_rec;
636178825Sdfr    }
637178825Sdfr
638178825Sdfr    /* We need to give the caller the flags which are in use */
639178825Sdfr    if (ret_flags) *ret_flags = ctx->flags;
640178825Sdfr
641178825Sdfr    if (src_name) {
642178825Sdfr	kret = krb5_copy_principal(context,
643178825Sdfr				   ctx->source,
644178825Sdfr				   (gsskrb5_name*)src_name);
645178825Sdfr	if (kret) {
646178825Sdfr	    *minor_status = kret;
647178825Sdfr	    return GSS_S_FAILURE;
648178825Sdfr	}
649178825Sdfr    }
650178825Sdfr
651178825Sdfr    /*
652178825Sdfr     * After the krb5_rd_rep() the remote and local seq_number should
653178825Sdfr     * be the same, because the client just replies the seq_number
654178825Sdfr     * from our AP-REP in its AP-REP, but then the client uses the
655178825Sdfr     * seq_number from its AP-REQ for GSS_wrap()
656178825Sdfr     */
657178825Sdfr    {
658178825Sdfr	int32_t tmp_r_seq_number, tmp_l_seq_number;
659178825Sdfr
660178825Sdfr	kret = krb5_auth_getremoteseqnumber(context,
661178825Sdfr					    ctx->auth_context,
662178825Sdfr					    &tmp_r_seq_number);
663178825Sdfr	if (kret) {
664178825Sdfr	    *minor_status = kret;
665178825Sdfr	    return GSS_S_FAILURE;
666178825Sdfr	}
667178825Sdfr
668178825Sdfr	kret = krb5_auth_con_getlocalseqnumber(context,
669178825Sdfr					       ctx->auth_context,
670178825Sdfr					       &tmp_l_seq_number);
671178825Sdfr	if (kret) {
672178825Sdfr
673178825Sdfr	    *minor_status = kret;
674178825Sdfr	    return GSS_S_FAILURE;
675178825Sdfr	}
676178825Sdfr
677178825Sdfr	/*
678178825Sdfr	 * Here we check if the client has responsed with our local seq_number,
679178825Sdfr	 */
680178825Sdfr	if (tmp_r_seq_number != tmp_l_seq_number) {
681178825Sdfr	    return GSS_S_UNSEQ_TOKEN;
682178825Sdfr	}
683178825Sdfr    }
684178825Sdfr
685178825Sdfr    /*
686178825Sdfr     * We need to reset the remote seq_number, because the client will use,
687178825Sdfr     * the old one for the GSS_wrap() calls
688178825Sdfr     */
689178825Sdfr    {
690178825Sdfr	kret = krb5_auth_con_setremoteseqnumber(context,
691178825Sdfr						ctx->auth_context,
692178825Sdfr						r_seq_number);
693178825Sdfr	if (kret) {
694178825Sdfr	    *minor_status = kret;
695178825Sdfr	    return GSS_S_FAILURE;
696178825Sdfr	}
697178825Sdfr    }
698178825Sdfr
699178825Sdfr    return gsskrb5_acceptor_ready(minor_status, ctx, context,
700178825Sdfr				  delegated_cred_handle);
701178825Sdfr}
702178825Sdfr
703178825Sdfr
704178825SdfrOM_uint32
705178825Sdfr_gsskrb5_accept_sec_context(OM_uint32 * minor_status,
706178825Sdfr			    gss_ctx_id_t * context_handle,
707178825Sdfr			    const gss_cred_id_t acceptor_cred_handle,
708178825Sdfr			    const gss_buffer_t input_token_buffer,
709178825Sdfr			    const gss_channel_bindings_t input_chan_bindings,
710178825Sdfr			    gss_name_t * src_name,
711178825Sdfr			    gss_OID * mech_type,
712178825Sdfr			    gss_buffer_t output_token,
713178825Sdfr			    OM_uint32 * ret_flags,
714178825Sdfr			    OM_uint32 * time_rec,
715178825Sdfr			    gss_cred_id_t * delegated_cred_handle)
716178825Sdfr{
717178825Sdfr    krb5_context context;
718178825Sdfr    OM_uint32 ret;
719178825Sdfr    gsskrb5_ctx ctx;
720178825Sdfr
721178825Sdfr    GSSAPI_KRB5_INIT(&context);
722178825Sdfr
723178825Sdfr    output_token->length = 0;
724178825Sdfr    output_token->value = NULL;
725178825Sdfr
726178825Sdfr    if (src_name != NULL)
727178825Sdfr	*src_name = NULL;
728178825Sdfr    if (mech_type)
729178825Sdfr	*mech_type = GSS_KRB5_MECHANISM;
730178825Sdfr
731178825Sdfr    if (*context_handle == GSS_C_NO_CONTEXT) {
732178825Sdfr	ret = _gsskrb5_create_ctx(minor_status,
733178825Sdfr				  context_handle,
734178825Sdfr				  context,
735178825Sdfr				  input_chan_bindings,
736178825Sdfr				  ACCEPTOR_START);
737178825Sdfr	if (ret)
738178825Sdfr	    return ret;
739178825Sdfr    }
740178825Sdfr
741178825Sdfr    ctx = (gsskrb5_ctx)*context_handle;
742178825Sdfr
743178825Sdfr
744178825Sdfr    /*
745178825Sdfr     * TODO: check the channel_bindings
746178825Sdfr     * (above just sets them to krb5 layer)
747178825Sdfr     */
748178825Sdfr
749178825Sdfr    HEIMDAL_MUTEX_lock(&ctx->ctx_id_mutex);
750178825Sdfr
751178825Sdfr    switch (ctx->state) {
752178825Sdfr    case ACCEPTOR_START:
753178825Sdfr	ret = gsskrb5_acceptor_start(minor_status,
754178825Sdfr				     ctx,
755178825Sdfr				     context,
756178825Sdfr				     acceptor_cred_handle,
757178825Sdfr				     input_token_buffer,
758178825Sdfr				     input_chan_bindings,
759178825Sdfr				     src_name,
760178825Sdfr				     mech_type,
761178825Sdfr				     output_token,
762178825Sdfr				     ret_flags,
763178825Sdfr				     time_rec,
764178825Sdfr				     delegated_cred_handle);
765178825Sdfr	break;
766178825Sdfr    case ACCEPTOR_WAIT_FOR_DCESTYLE:
767178825Sdfr	ret = acceptor_wait_for_dcestyle(minor_status,
768178825Sdfr					 ctx,
769178825Sdfr					 context,
770178825Sdfr					 acceptor_cred_handle,
771178825Sdfr					 input_token_buffer,
772178825Sdfr					 input_chan_bindings,
773178825Sdfr					 src_name,
774178825Sdfr					 mech_type,
775178825Sdfr					 output_token,
776178825Sdfr					 ret_flags,
777178825Sdfr					 time_rec,
778178825Sdfr					 delegated_cred_handle);
779178825Sdfr	break;
780178825Sdfr    case ACCEPTOR_READY:
781178825Sdfr	/*
782178825Sdfr	 * If we get there, the caller have called
783178825Sdfr	 * gss_accept_sec_context() one time too many.
784178825Sdfr	 */
785178825Sdfr	ret =  GSS_S_BAD_STATUS;
786178825Sdfr	break;
787178825Sdfr    default:
788178825Sdfr	/* TODO: is this correct here? --metze */
789178825Sdfr	ret =  GSS_S_BAD_STATUS;
790178825Sdfr	break;
791178825Sdfr    }
792178825Sdfr
793178825Sdfr    HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex);
794178825Sdfr
795178825Sdfr    if (GSS_ERROR(ret)) {
796178825Sdfr	OM_uint32 min2;
797178825Sdfr	_gsskrb5_delete_sec_context(&min2, context_handle, GSS_C_NO_BUFFER);
798178825Sdfr    }
799178825Sdfr
800178825Sdfr    return ret;
801178825Sdfr}
802