1178825Sdfr/*
2233294Sstas * Copyright (c) 1997 - 2006 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
36178825SdfrHEIMDAL_MUTEX gssapi_keytab_mutex = HEIMDAL_MUTEX_INITIALIZER;
37178825Sdfrkrb5_keytab _gsskrb5_keytab;
38178825Sdfr
39233294Sstasstatic krb5_error_code
40233294Sstasvalidate_keytab(krb5_context context, const char *name, krb5_keytab *id)
41233294Sstas{
42233294Sstas    krb5_error_code ret;
43233294Sstas
44233294Sstas    ret = krb5_kt_resolve(context, name, id);
45233294Sstas    if (ret)
46233294Sstas	return ret;
47233294Sstas
48233294Sstas    ret = krb5_kt_have_content(context, *id);
49233294Sstas    if (ret) {
50233294Sstas	krb5_kt_close(context, *id);
51233294Sstas	*id = NULL;
52233294Sstas    }
53233294Sstas
54233294Sstas    return ret;
55233294Sstas}
56233294Sstas
57178825SdfrOM_uint32
58233294Sstas_gsskrb5_register_acceptor_identity(OM_uint32 *min_stat, const char *identity)
59178825Sdfr{
60178825Sdfr    krb5_context context;
61178825Sdfr    krb5_error_code ret;
62178825Sdfr
63233294Sstas    *min_stat = 0;
64233294Sstas
65178825Sdfr    ret = _gsskrb5_init(&context);
66178825Sdfr    if(ret)
67178825Sdfr	return GSS_S_FAILURE;
68233294Sstas
69178825Sdfr    HEIMDAL_MUTEX_lock(&gssapi_keytab_mutex);
70178825Sdfr
71178825Sdfr    if(_gsskrb5_keytab != NULL) {
72178825Sdfr	krb5_kt_close(context, _gsskrb5_keytab);
73178825Sdfr	_gsskrb5_keytab = NULL;
74178825Sdfr    }
75178825Sdfr    if (identity == NULL) {
76178825Sdfr	ret = krb5_kt_default(context, &_gsskrb5_keytab);
77178825Sdfr    } else {
78233294Sstas	/*
79233294Sstas	 * First check if we can the keytab as is and if it has content...
80233294Sstas	 */
81233294Sstas	ret = validate_keytab(context, identity, &_gsskrb5_keytab);
82233294Sstas	/*
83233294Sstas	 * if it doesn't, lets prepend FILE: and try again
84233294Sstas	 */
85233294Sstas	if (ret) {
86233294Sstas	    char *p = NULL;
87233294Sstas	    ret = asprintf(&p, "FILE:%s", identity);
88233294Sstas	    if(ret < 0 || p == NULL) {
89233294Sstas		HEIMDAL_MUTEX_unlock(&gssapi_keytab_mutex);
90233294Sstas		return GSS_S_FAILURE;
91233294Sstas	    }
92233294Sstas	    ret = validate_keytab(context, p, &_gsskrb5_keytab);
93233294Sstas	    free(p);
94178825Sdfr	}
95178825Sdfr    }
96178825Sdfr    HEIMDAL_MUTEX_unlock(&gssapi_keytab_mutex);
97233294Sstas    if(ret) {
98233294Sstas	*min_stat = ret;
99178825Sdfr	return GSS_S_FAILURE;
100233294Sstas    }
101178825Sdfr    return GSS_S_COMPLETE;
102178825Sdfr}
103178825Sdfr
104178825Sdfrvoid
105233294Sstas_gsskrb5i_is_cfx(krb5_context context, gsskrb5_ctx ctx, int acceptor)
106178825Sdfr{
107233294Sstas    krb5_error_code ret;
108178825Sdfr    krb5_keyblock *key;
109178825Sdfr
110178825Sdfr    if (acceptor) {
111178825Sdfr	if (ctx->auth_context->local_subkey)
112178825Sdfr	    key = ctx->auth_context->local_subkey;
113178825Sdfr	else
114178825Sdfr	    key = ctx->auth_context->remote_subkey;
115178825Sdfr    } else {
116178825Sdfr	if (ctx->auth_context->remote_subkey)
117178825Sdfr	    key = ctx->auth_context->remote_subkey;
118178825Sdfr	else
119178825Sdfr	    key = ctx->auth_context->local_subkey;
120178825Sdfr    }
121178825Sdfr    if (key == NULL)
122178825Sdfr	key = ctx->auth_context->keyblock;
123178825Sdfr
124178825Sdfr    if (key == NULL)
125178825Sdfr	return;
126233294Sstas
127178825Sdfr    switch (key->keytype) {
128178825Sdfr    case ETYPE_DES_CBC_CRC:
129178825Sdfr    case ETYPE_DES_CBC_MD4:
130178825Sdfr    case ETYPE_DES_CBC_MD5:
131178825Sdfr    case ETYPE_DES3_CBC_MD5:
132233294Sstas    case ETYPE_OLD_DES3_CBC_SHA1:
133178825Sdfr    case ETYPE_DES3_CBC_SHA1:
134178825Sdfr    case ETYPE_ARCFOUR_HMAC_MD5:
135178825Sdfr    case ETYPE_ARCFOUR_HMAC_MD5_56:
136178825Sdfr	break;
137178825Sdfr    default :
138233294Sstas        ctx->more_flags |= IS_CFX;
139233294Sstas
140178825Sdfr	if ((acceptor && ctx->auth_context->local_subkey) ||
141178825Sdfr	    (!acceptor && ctx->auth_context->remote_subkey))
142178825Sdfr	    ctx->more_flags |= ACCEPTOR_SUBKEY;
143178825Sdfr	break;
144178825Sdfr    }
145233294Sstas    if (ctx->crypto)
146233294Sstas        krb5_crypto_destroy(context, ctx->crypto);
147233294Sstas    ret = krb5_crypto_init(context, key, 0, &ctx->crypto);
148178825Sdfr}
149178825Sdfr
150178825Sdfr
151178825Sdfrstatic OM_uint32
152178825Sdfrgsskrb5_accept_delegated_token
153178825Sdfr(OM_uint32 * minor_status,
154178825Sdfr gsskrb5_ctx ctx,
155178825Sdfr krb5_context context,
156178825Sdfr gss_cred_id_t * delegated_cred_handle
157178825Sdfr    )
158178825Sdfr{
159178825Sdfr    krb5_ccache ccache = NULL;
160178825Sdfr    krb5_error_code kret;
161178825Sdfr    int32_t ac_flags, ret = GSS_S_COMPLETE;
162233294Sstas
163178825Sdfr    *minor_status = 0;
164178825Sdfr
165178825Sdfr    /* XXX Create a new delegated_cred_handle? */
166178825Sdfr    if (delegated_cred_handle == NULL) {
167178825Sdfr	kret = krb5_cc_default (context, &ccache);
168178825Sdfr    } else {
169178825Sdfr	*delegated_cred_handle = NULL;
170233294Sstas	kret = krb5_cc_new_unique (context, krb5_cc_type_memory,
171233294Sstas				   NULL, &ccache);
172178825Sdfr    }
173178825Sdfr    if (kret) {
174178825Sdfr	ctx->flags &= ~GSS_C_DELEG_FLAG;
175178825Sdfr	goto out;
176178825Sdfr    }
177178825Sdfr
178178825Sdfr    kret = krb5_cc_initialize(context, ccache, ctx->source);
179178825Sdfr    if (kret) {
180178825Sdfr	ctx->flags &= ~GSS_C_DELEG_FLAG;
181178825Sdfr	goto out;
182178825Sdfr    }
183233294Sstas
184178825Sdfr    krb5_auth_con_removeflags(context,
185178825Sdfr			      ctx->auth_context,
186178825Sdfr			      KRB5_AUTH_CONTEXT_DO_TIME,
187178825Sdfr			      &ac_flags);
188178825Sdfr    kret = krb5_rd_cred2(context,
189178825Sdfr			 ctx->auth_context,
190178825Sdfr			 ccache,
191178825Sdfr			 &ctx->fwd_data);
192178825Sdfr    krb5_auth_con_setflags(context,
193178825Sdfr			   ctx->auth_context,
194178825Sdfr			   ac_flags);
195178825Sdfr    if (kret) {
196178825Sdfr	ctx->flags &= ~GSS_C_DELEG_FLAG;
197178825Sdfr	ret = GSS_S_FAILURE;
198178825Sdfr	*minor_status = kret;
199178825Sdfr	goto out;
200178825Sdfr    }
201178825Sdfr
202178825Sdfr    if (delegated_cred_handle) {
203178825Sdfr	gsskrb5_cred handle;
204178825Sdfr
205233294Sstas	ret = _gsskrb5_krb5_import_cred(minor_status,
206233294Sstas					ccache,
207233294Sstas					NULL,
208233294Sstas					NULL,
209233294Sstas					delegated_cred_handle);
210178825Sdfr	if (ret != GSS_S_COMPLETE)
211178825Sdfr	    goto out;
212178825Sdfr
213178825Sdfr	handle = (gsskrb5_cred) *delegated_cred_handle;
214233294Sstas
215178825Sdfr	handle->cred_flags |= GSS_CF_DESTROY_CRED_ON_RELEASE;
216178825Sdfr	krb5_cc_close(context, ccache);
217178825Sdfr	ccache = NULL;
218178825Sdfr    }
219178825Sdfr
220178825Sdfrout:
221178825Sdfr    if (ccache) {
222178825Sdfr	/* Don't destroy the default cred cache */
223178825Sdfr	if (delegated_cred_handle == NULL)
224178825Sdfr	    krb5_cc_close(context, ccache);
225178825Sdfr	else
226178825Sdfr	    krb5_cc_destroy(context, ccache);
227178825Sdfr    }
228178825Sdfr    return ret;
229178825Sdfr}
230178825Sdfr
231178825Sdfrstatic OM_uint32
232178825Sdfrgsskrb5_acceptor_ready(OM_uint32 * minor_status,
233178825Sdfr		       gsskrb5_ctx ctx,
234178825Sdfr		       krb5_context context,
235178825Sdfr		       gss_cred_id_t *delegated_cred_handle)
236178825Sdfr{
237178825Sdfr    OM_uint32 ret;
238178825Sdfr    int32_t seq_number;
239178825Sdfr    int is_cfx = 0;
240178825Sdfr
241233294Sstas    krb5_auth_con_getremoteseqnumber (context,
242233294Sstas				      ctx->auth_context,
243233294Sstas				      &seq_number);
244178825Sdfr
245233294Sstas    _gsskrb5i_is_cfx(context, ctx, 1);
246233294Sstas    is_cfx = (ctx->more_flags & IS_CFX);
247178825Sdfr
248178825Sdfr    ret = _gssapi_msg_order_create(minor_status,
249178825Sdfr				   &ctx->order,
250178825Sdfr				   _gssapi_msg_order_f(ctx->flags),
251178825Sdfr				   seq_number, 0, is_cfx);
252178825Sdfr    if (ret)
253178825Sdfr	return ret;
254178825Sdfr
255233294Sstas    /*
256178825Sdfr     * If requested, set local sequence num to remote sequence if this
257178825Sdfr     * isn't a mutual authentication context
258178825Sdfr     */
259178825Sdfr    if (!(ctx->flags & GSS_C_MUTUAL_FLAG) && _gssapi_msg_order_f(ctx->flags)) {
260178825Sdfr	krb5_auth_con_setlocalseqnumber(context,
261178825Sdfr					ctx->auth_context,
262178825Sdfr					seq_number);
263178825Sdfr    }
264178825Sdfr
265178825Sdfr    /*
266178825Sdfr     * We should handle the delegation ticket, in case it's there
267178825Sdfr     */
268178825Sdfr    if (ctx->fwd_data.length > 0 && (ctx->flags & GSS_C_DELEG_FLAG)) {
269178825Sdfr	ret = gsskrb5_accept_delegated_token(minor_status,
270178825Sdfr					     ctx,
271178825Sdfr					     context,
272178825Sdfr					     delegated_cred_handle);
273178825Sdfr	if (ret)
274178825Sdfr	    return ret;
275178825Sdfr    } else {
276178825Sdfr	/* Well, looks like it wasn't there after all */
277178825Sdfr	ctx->flags &= ~GSS_C_DELEG_FLAG;
278178825Sdfr    }
279178825Sdfr
280178825Sdfr    ctx->state = ACCEPTOR_READY;
281178825Sdfr    ctx->more_flags |= OPEN;
282178825Sdfr
283178825Sdfr    return GSS_S_COMPLETE;
284178825Sdfr}
285178825Sdfr
286178825Sdfrstatic OM_uint32
287233294Sstassend_error_token(OM_uint32 *minor_status,
288233294Sstas		 krb5_context context,
289233294Sstas		 krb5_error_code kret,
290233294Sstas		 krb5_principal server,
291233294Sstas		 krb5_data *indata,
292233294Sstas		 gss_buffer_t output_token)
293233294Sstas{
294233294Sstas    krb5_principal ap_req_server = NULL;
295233294Sstas    krb5_error_code ret;
296233294Sstas    krb5_data outbuf;
297233294Sstas    /* this e_data value encodes KERB_AP_ERR_TYPE_SKEW_RECOVERY which
298233294Sstas       tells windows to try again with the corrected timestamp. See
299233294Sstas       [MS-KILE] 2.2.1 KERB-ERROR-DATA */
300233294Sstas    krb5_data e_data = { 7, rk_UNCONST("\x30\x05\xa1\x03\x02\x01\x02") };
301233294Sstas
302233294Sstas    /* build server from request if the acceptor had not selected one */
303233294Sstas    if (server == NULL) {
304233294Sstas	AP_REQ ap_req;
305233294Sstas
306233294Sstas	ret = krb5_decode_ap_req(context, indata, &ap_req);
307233294Sstas	if (ret) {
308233294Sstas	    *minor_status = ret;
309233294Sstas	    return GSS_S_FAILURE;
310233294Sstas	}
311233294Sstas	ret = _krb5_principalname2krb5_principal(context,
312233294Sstas						  &ap_req_server,
313233294Sstas						  ap_req.ticket.sname,
314233294Sstas						  ap_req.ticket.realm);
315233294Sstas	free_AP_REQ(&ap_req);
316233294Sstas	if (ret) {
317233294Sstas	    *minor_status = ret;
318233294Sstas	    return GSS_S_FAILURE;
319233294Sstas	}
320233294Sstas	server = ap_req_server;
321233294Sstas    }
322233294Sstas
323233294Sstas    ret = krb5_mk_error(context, kret, NULL, &e_data, NULL,
324233294Sstas			server, NULL, NULL, &outbuf);
325233294Sstas    if (ap_req_server)
326233294Sstas	krb5_free_principal(context, ap_req_server);
327233294Sstas    if (ret) {
328233294Sstas	*minor_status = ret;
329233294Sstas	return GSS_S_FAILURE;
330233294Sstas    }
331233294Sstas
332233294Sstas    ret = _gsskrb5_encapsulate(minor_status,
333233294Sstas			       &outbuf,
334233294Sstas			       output_token,
335233294Sstas			       "\x03\x00",
336233294Sstas			       GSS_KRB5_MECHANISM);
337233294Sstas    krb5_data_free (&outbuf);
338233294Sstas    if (ret)
339233294Sstas	return ret;
340233294Sstas
341233294Sstas    *minor_status = 0;
342233294Sstas    return GSS_S_CONTINUE_NEEDED;
343233294Sstas}
344233294Sstas
345233294Sstas
346233294Sstasstatic OM_uint32
347178825Sdfrgsskrb5_acceptor_start(OM_uint32 * minor_status,
348178825Sdfr		       gsskrb5_ctx ctx,
349178825Sdfr		       krb5_context context,
350178825Sdfr		       const gss_cred_id_t acceptor_cred_handle,
351178825Sdfr		       const gss_buffer_t input_token_buffer,
352178825Sdfr		       const gss_channel_bindings_t input_chan_bindings,
353178825Sdfr		       gss_name_t * src_name,
354178825Sdfr		       gss_OID * mech_type,
355178825Sdfr		       gss_buffer_t output_token,
356178825Sdfr		       OM_uint32 * ret_flags,
357178825Sdfr		       OM_uint32 * time_rec,
358178825Sdfr		       gss_cred_id_t * delegated_cred_handle)
359178825Sdfr{
360178825Sdfr    krb5_error_code kret;
361178825Sdfr    OM_uint32 ret = GSS_S_COMPLETE;
362178825Sdfr    krb5_data indata;
363178825Sdfr    krb5_flags ap_options;
364178825Sdfr    krb5_keytab keytab = NULL;
365178825Sdfr    int is_cfx = 0;
366178825Sdfr    const gsskrb5_cred acceptor_cred = (gsskrb5_cred)acceptor_cred_handle;
367178825Sdfr
368178825Sdfr    /*
369178825Sdfr     * We may, or may not, have an escapsulation.
370178825Sdfr     */
371178825Sdfr    ret = _gsskrb5_decapsulate (minor_status,
372178825Sdfr				input_token_buffer,
373178825Sdfr				&indata,
374178825Sdfr				"\x01\x00",
375178825Sdfr				GSS_KRB5_MECHANISM);
376178825Sdfr
377178825Sdfr    if (ret) {
378178825Sdfr	/* Assume that there is no OID wrapping. */
379178825Sdfr	indata.length	= input_token_buffer->length;
380178825Sdfr	indata.data	= input_token_buffer->value;
381178825Sdfr    }
382178825Sdfr
383178825Sdfr    /*
384178825Sdfr     * We need to get our keytab
385178825Sdfr     */
386178825Sdfr    if (acceptor_cred == NULL) {
387178825Sdfr	if (_gsskrb5_keytab != NULL)
388178825Sdfr	    keytab = _gsskrb5_keytab;
389178825Sdfr    } else if (acceptor_cred->keytab != NULL) {
390178825Sdfr	keytab = acceptor_cred->keytab;
391178825Sdfr    }
392233294Sstas
393178825Sdfr    /*
394178825Sdfr     * We need to check the ticket and create the AP-REP packet
395178825Sdfr     */
396178825Sdfr
397178825Sdfr    {
398178825Sdfr	krb5_rd_req_in_ctx in = NULL;
399178825Sdfr	krb5_rd_req_out_ctx out = NULL;
400233294Sstas	krb5_principal server = NULL;
401178825Sdfr
402233294Sstas	if (acceptor_cred)
403233294Sstas	    server = acceptor_cred->principal;
404233294Sstas
405178825Sdfr	kret = krb5_rd_req_in_ctx_alloc(context, &in);
406178825Sdfr	if (kret == 0)
407178825Sdfr	    kret = krb5_rd_req_in_set_keytab(context, in, keytab);
408178825Sdfr	if (kret) {
409178825Sdfr	    if (in)
410178825Sdfr		krb5_rd_req_in_ctx_free(context, in);
411178825Sdfr	    *minor_status = kret;
412233294Sstas	    return GSS_S_FAILURE;
413178825Sdfr	}
414178825Sdfr
415178825Sdfr	kret = krb5_rd_req_ctx(context,
416178825Sdfr			       &ctx->auth_context,
417178825Sdfr			       &indata,
418233294Sstas			       server,
419178825Sdfr			       in, &out);
420178825Sdfr	krb5_rd_req_in_ctx_free(context, in);
421233294Sstas	if (kret == KRB5KRB_AP_ERR_SKEW || kret == KRB5KRB_AP_ERR_TKT_NYV) {
422233294Sstas	    /*
423233294Sstas	     * No reply in non-MUTUAL mode, but we don't know that its
424233294Sstas	     * non-MUTUAL mode yet, thats inside the 8003 checksum, so
425233294Sstas	     * lets only send the error token on clock skew, that
426233294Sstas	     * limit when send error token for non-MUTUAL.
427233294Sstas	     */
428233294Sstas	    return send_error_token(minor_status, context, kret,
429233294Sstas				    server, &indata, output_token);
430233294Sstas	} else if (kret) {
431178825Sdfr	    *minor_status = kret;
432233294Sstas	    return GSS_S_FAILURE;
433178825Sdfr	}
434178825Sdfr
435178825Sdfr	/*
436233294Sstas	 * we need to remember some data on the context_handle.
437178825Sdfr	 */
438178825Sdfr	kret = krb5_rd_req_out_get_ap_req_options(context, out,
439178825Sdfr						  &ap_options);
440178825Sdfr	if (kret == 0)
441233294Sstas	    kret = krb5_rd_req_out_get_ticket(context, out,
442178825Sdfr					      &ctx->ticket);
443178825Sdfr	if (kret == 0)
444178825Sdfr	    kret = krb5_rd_req_out_get_keyblock(context, out,
445178825Sdfr						&ctx->service_keyblock);
446178825Sdfr	ctx->lifetime = ctx->ticket->ticket.endtime;
447178825Sdfr
448178825Sdfr	krb5_rd_req_out_ctx_free(context, out);
449178825Sdfr	if (kret) {
450178825Sdfr	    ret = GSS_S_FAILURE;
451178825Sdfr	    *minor_status = kret;
452178825Sdfr	    return ret;
453178825Sdfr	}
454178825Sdfr    }
455233294Sstas
456233294Sstas
457178825Sdfr    /*
458178825Sdfr     * We need to copy the principal names to the context and the
459178825Sdfr     * calling layer.
460178825Sdfr     */
461178825Sdfr    kret = krb5_copy_principal(context,
462178825Sdfr			       ctx->ticket->client,
463178825Sdfr			       &ctx->source);
464178825Sdfr    if (kret) {
465178825Sdfr	ret = GSS_S_FAILURE;
466178825Sdfr	*minor_status = kret;
467178825Sdfr    }
468178825Sdfr
469233294Sstas    kret = krb5_copy_principal(context,
470178825Sdfr			       ctx->ticket->server,
471178825Sdfr			       &ctx->target);
472178825Sdfr    if (kret) {
473178825Sdfr	ret = GSS_S_FAILURE;
474178825Sdfr	*minor_status = kret;
475178825Sdfr	return ret;
476178825Sdfr    }
477233294Sstas
478178825Sdfr    /*
479178825Sdfr     * We need to setup some compat stuff, this assumes that
480178825Sdfr     * context_handle->target is already set.
481178825Sdfr     */
482178825Sdfr    ret = _gss_DES3_get_mic_compat(minor_status, ctx, context);
483178825Sdfr    if (ret)
484178825Sdfr	return ret;
485178825Sdfr
486178825Sdfr    if (src_name != NULL) {
487178825Sdfr	kret = krb5_copy_principal (context,
488178825Sdfr				    ctx->ticket->client,
489178825Sdfr				    (gsskrb5_name*)src_name);
490178825Sdfr	if (kret) {
491178825Sdfr	    ret = GSS_S_FAILURE;
492178825Sdfr	    *minor_status = kret;
493178825Sdfr	    return ret;
494178825Sdfr	}
495178825Sdfr    }
496178825Sdfr
497178825Sdfr    /*
498178825Sdfr     * We need to get the flags out of the 8003 checksum.
499178825Sdfr     */
500233294Sstas
501178825Sdfr    {
502178825Sdfr	krb5_authenticator authenticator;
503233294Sstas
504178825Sdfr	kret = krb5_auth_con_getauthenticator(context,
505178825Sdfr					      ctx->auth_context,
506178825Sdfr					      &authenticator);
507178825Sdfr	if(kret) {
508178825Sdfr	    ret = GSS_S_FAILURE;
509178825Sdfr	    *minor_status = kret;
510178825Sdfr	    return ret;
511178825Sdfr	}
512178825Sdfr
513233294Sstas	if (authenticator->cksum == NULL) {
514233294Sstas	    krb5_free_authenticator(context, &authenticator);
515233294Sstas	    *minor_status = 0;
516233294Sstas	    return GSS_S_BAD_BINDINGS;
517233294Sstas	}
518233294Sstas
519178825Sdfr        if (authenticator->cksum->cksumtype == CKSUMTYPE_GSSAPI) {
520178825Sdfr            ret = _gsskrb5_verify_8003_checksum(minor_status,
521178825Sdfr						input_chan_bindings,
522178825Sdfr						authenticator->cksum,
523178825Sdfr						&ctx->flags,
524178825Sdfr						&ctx->fwd_data);
525178825Sdfr
526178825Sdfr	    krb5_free_authenticator(context, &authenticator);
527178825Sdfr	    if (ret) {
528178825Sdfr		return ret;
529178825Sdfr	    }
530178825Sdfr        } else {
531178825Sdfr	    krb5_crypto crypto;
532178825Sdfr
533233294Sstas	    kret = krb5_crypto_init(context,
534233294Sstas				    ctx->auth_context->keyblock,
535178825Sdfr				    0, &crypto);
536178825Sdfr	    if(kret) {
537178825Sdfr		krb5_free_authenticator(context, &authenticator);
538178825Sdfr
539178825Sdfr		ret = GSS_S_FAILURE;
540178825Sdfr		*minor_status = kret;
541178825Sdfr		return ret;
542178825Sdfr	    }
543178825Sdfr
544233294Sstas	    /*
545178825Sdfr	     * Windows accepts Samba3's use of a kerberos, rather than
546233294Sstas	     * GSSAPI checksum here
547178825Sdfr	     */
548178825Sdfr
549178825Sdfr	    kret = krb5_verify_checksum(context,
550178825Sdfr					crypto, KRB5_KU_AP_REQ_AUTH_CKSUM, NULL, 0,
551178825Sdfr					authenticator->cksum);
552178825Sdfr	    krb5_free_authenticator(context, &authenticator);
553178825Sdfr	    krb5_crypto_destroy(context, crypto);
554178825Sdfr
555178825Sdfr	    if(kret) {
556178825Sdfr		ret = GSS_S_BAD_SIG;
557178825Sdfr		*minor_status = kret;
558178825Sdfr		return ret;
559178825Sdfr	    }
560178825Sdfr
561233294Sstas	    /*
562233294Sstas	     * Samba style get some flags (but not DCE-STYLE), use
563233294Sstas	     * ap_options to guess the mutual flag.
564178825Sdfr	     */
565233294Sstas 	    ctx->flags = GSS_C_REPLAY_FLAG | GSS_C_SEQUENCE_FLAG;
566233294Sstas	    if (ap_options & AP_OPTS_MUTUAL_REQUIRED)
567233294Sstas		ctx->flags |= GSS_C_MUTUAL_FLAG;
568178825Sdfr        }
569178825Sdfr    }
570233294Sstas
571178825Sdfr    if(ctx->flags & GSS_C_MUTUAL_FLAG) {
572178825Sdfr	krb5_data outbuf;
573233294Sstas	int use_subkey = 0;
574233294Sstas
575233294Sstas	_gsskrb5i_is_cfx(context, ctx, 1);
576233294Sstas	is_cfx = (ctx->more_flags & IS_CFX);
577233294Sstas
578233294Sstas	if (is_cfx || (ap_options & AP_OPTS_USE_SUBKEY)) {
579233294Sstas	    use_subkey = 1;
580233294Sstas	} else {
581233294Sstas	    krb5_keyblock *rkey;
582233294Sstas
583233294Sstas	    /*
584233294Sstas	     * If there is a initiator subkey, copy that to acceptor
585233294Sstas	     * subkey to match Windows behavior
586233294Sstas	     */
587233294Sstas	    kret = krb5_auth_con_getremotesubkey(context,
588233294Sstas						 ctx->auth_context,
589233294Sstas						 &rkey);
590233294Sstas	    if (kret == 0) {
591233294Sstas		kret = krb5_auth_con_setlocalsubkey(context,
592233294Sstas						    ctx->auth_context,
593233294Sstas						    rkey);
594233294Sstas		if (kret == 0)
595233294Sstas		    use_subkey = 1;
596233294Sstas		krb5_free_keyblock(context, rkey);
597233294Sstas	    }
598233294Sstas	}
599233294Sstas	if (use_subkey) {
600178825Sdfr	    ctx->more_flags |= ACCEPTOR_SUBKEY;
601233294Sstas	    krb5_auth_con_addflags(context, ctx->auth_context,
602233294Sstas				   KRB5_AUTH_CONTEXT_USE_SUBKEY,
603233294Sstas				   NULL);
604178825Sdfr	}
605233294Sstas
606178825Sdfr	kret = krb5_mk_rep(context,
607178825Sdfr			   ctx->auth_context,
608178825Sdfr			   &outbuf);
609178825Sdfr	if (kret) {
610178825Sdfr	    *minor_status = kret;
611178825Sdfr	    return GSS_S_FAILURE;
612178825Sdfr	}
613233294Sstas
614178825Sdfr	if (IS_DCE_STYLE(ctx)) {
615178825Sdfr	    output_token->length = outbuf.length;
616178825Sdfr	    output_token->value = outbuf.data;
617178825Sdfr	} else {
618178825Sdfr	    ret = _gsskrb5_encapsulate(minor_status,
619178825Sdfr				       &outbuf,
620178825Sdfr				       output_token,
621178825Sdfr				       "\x02\x00",
622178825Sdfr				       GSS_KRB5_MECHANISM);
623178825Sdfr	    krb5_data_free (&outbuf);
624178825Sdfr	    if (ret)
625178825Sdfr		return ret;
626178825Sdfr	}
627178825Sdfr    }
628233294Sstas
629178825Sdfr    ctx->flags |= GSS_C_TRANS_FLAG;
630178825Sdfr
631178825Sdfr    /* Remember the flags */
632233294Sstas
633178825Sdfr    ctx->lifetime = ctx->ticket->ticket.endtime;
634178825Sdfr    ctx->more_flags |= OPEN;
635233294Sstas
636178825Sdfr    if (mech_type)
637178825Sdfr	*mech_type = GSS_KRB5_MECHANISM;
638233294Sstas
639178825Sdfr    if (time_rec) {
640178825Sdfr	ret = _gsskrb5_lifetime_left(minor_status,
641178825Sdfr				     context,
642178825Sdfr				     ctx->lifetime,
643178825Sdfr				     time_rec);
644178825Sdfr	if (ret) {
645178825Sdfr	    return ret;
646178825Sdfr	}
647178825Sdfr    }
648178825Sdfr
649178825Sdfr    /*
650178825Sdfr     * When GSS_C_DCE_STYLE is in use, we need ask for a AP-REP from
651178825Sdfr     * the client.
652178825Sdfr     */
653178825Sdfr    if (IS_DCE_STYLE(ctx)) {
654178825Sdfr	/*
655178825Sdfr	 * Return flags to caller, but we haven't processed
656178825Sdfr	 * delgations yet
657178825Sdfr	 */
658178825Sdfr	if (ret_flags)
659178825Sdfr	    *ret_flags = (ctx->flags & ~GSS_C_DELEG_FLAG);
660178825Sdfr
661178825Sdfr	ctx->state = ACCEPTOR_WAIT_FOR_DCESTYLE;
662178825Sdfr	return GSS_S_CONTINUE_NEEDED;
663178825Sdfr    }
664178825Sdfr
665233294Sstas    ret = gsskrb5_acceptor_ready(minor_status, ctx, context,
666178825Sdfr				 delegated_cred_handle);
667178825Sdfr
668178825Sdfr    if (ret_flags)
669178825Sdfr	*ret_flags = ctx->flags;
670178825Sdfr
671178825Sdfr    return ret;
672178825Sdfr}
673178825Sdfr
674178825Sdfrstatic OM_uint32
675178825Sdfracceptor_wait_for_dcestyle(OM_uint32 * minor_status,
676178825Sdfr			   gsskrb5_ctx ctx,
677178825Sdfr			   krb5_context context,
678178825Sdfr			   const gss_cred_id_t acceptor_cred_handle,
679178825Sdfr			   const gss_buffer_t input_token_buffer,
680178825Sdfr			   const gss_channel_bindings_t input_chan_bindings,
681178825Sdfr			   gss_name_t * src_name,
682178825Sdfr			   gss_OID * mech_type,
683178825Sdfr			   gss_buffer_t output_token,
684178825Sdfr			   OM_uint32 * ret_flags,
685178825Sdfr			   OM_uint32 * time_rec,
686178825Sdfr			   gss_cred_id_t * delegated_cred_handle)
687178825Sdfr{
688178825Sdfr    OM_uint32 ret;
689178825Sdfr    krb5_error_code kret;
690178825Sdfr    krb5_data inbuf;
691178825Sdfr    int32_t r_seq_number, l_seq_number;
692233294Sstas
693233294Sstas    /*
694178825Sdfr     * We know it's GSS_C_DCE_STYLE so we don't need to decapsulate the AP_REP
695178825Sdfr     */
696178825Sdfr
697178825Sdfr    inbuf.length = input_token_buffer->length;
698178825Sdfr    inbuf.data = input_token_buffer->value;
699178825Sdfr
700233294Sstas    /*
701178825Sdfr     * We need to remeber the old remote seq_number, then check if the
702178825Sdfr     * client has replied with our local seq_number, and then reset
703178825Sdfr     * the remote seq_number to the old value
704178825Sdfr     */
705178825Sdfr    {
706178825Sdfr	kret = krb5_auth_con_getlocalseqnumber(context,
707178825Sdfr					       ctx->auth_context,
708178825Sdfr					       &l_seq_number);
709178825Sdfr	if (kret) {
710178825Sdfr	    *minor_status = kret;
711178825Sdfr	    return GSS_S_FAILURE;
712178825Sdfr	}
713178825Sdfr
714233294Sstas	kret = krb5_auth_con_getremoteseqnumber(context,
715233294Sstas						ctx->auth_context,
716233294Sstas						&r_seq_number);
717178825Sdfr	if (kret) {
718178825Sdfr	    *minor_status = kret;
719178825Sdfr	    return GSS_S_FAILURE;
720178825Sdfr	}
721178825Sdfr
722178825Sdfr	kret = krb5_auth_con_setremoteseqnumber(context,
723178825Sdfr						ctx->auth_context,
724178825Sdfr						l_seq_number);
725178825Sdfr	if (kret) {
726178825Sdfr	    *minor_status = kret;
727178825Sdfr	    return GSS_S_FAILURE;
728178825Sdfr	}
729178825Sdfr    }
730178825Sdfr
731233294Sstas    /*
732178825Sdfr     * We need to verify the AP_REP, but we need to flag that this is
733178825Sdfr     * DCE_STYLE, so don't check the timestamps this time, but put the
734178825Sdfr     * flag DO_TIME back afterward.
735233294Sstas    */
736178825Sdfr    {
737178825Sdfr	krb5_ap_rep_enc_part *repl;
738178825Sdfr	int32_t auth_flags;
739233294Sstas
740178825Sdfr	krb5_auth_con_removeflags(context,
741178825Sdfr				  ctx->auth_context,
742178825Sdfr				  KRB5_AUTH_CONTEXT_DO_TIME,
743178825Sdfr				  &auth_flags);
744178825Sdfr
745178825Sdfr	kret = krb5_rd_rep(context, ctx->auth_context, &inbuf, &repl);
746178825Sdfr	if (kret) {
747178825Sdfr	    *minor_status = kret;
748178825Sdfr	    return GSS_S_FAILURE;
749178825Sdfr	}
750178825Sdfr	krb5_free_ap_rep_enc_part(context, repl);
751178825Sdfr	krb5_auth_con_setflags(context, ctx->auth_context, auth_flags);
752178825Sdfr    }
753178825Sdfr
754178825Sdfr    /* We need to check the liftime */
755178825Sdfr    {
756178825Sdfr	OM_uint32 lifetime_rec;
757178825Sdfr
758178825Sdfr	ret = _gsskrb5_lifetime_left(minor_status,
759178825Sdfr				     context,
760178825Sdfr				     ctx->lifetime,
761178825Sdfr				     &lifetime_rec);
762178825Sdfr	if (ret) {
763178825Sdfr	    return ret;
764178825Sdfr	}
765178825Sdfr	if (lifetime_rec == 0) {
766178825Sdfr	    return GSS_S_CONTEXT_EXPIRED;
767178825Sdfr	}
768233294Sstas
769178825Sdfr	if (time_rec) *time_rec = lifetime_rec;
770178825Sdfr    }
771178825Sdfr
772178825Sdfr    /* We need to give the caller the flags which are in use */
773178825Sdfr    if (ret_flags) *ret_flags = ctx->flags;
774178825Sdfr
775178825Sdfr    if (src_name) {
776178825Sdfr	kret = krb5_copy_principal(context,
777178825Sdfr				   ctx->source,
778178825Sdfr				   (gsskrb5_name*)src_name);
779178825Sdfr	if (kret) {
780178825Sdfr	    *minor_status = kret;
781178825Sdfr	    return GSS_S_FAILURE;
782178825Sdfr	}
783178825Sdfr    }
784178825Sdfr
785178825Sdfr    /*
786178825Sdfr     * After the krb5_rd_rep() the remote and local seq_number should
787178825Sdfr     * be the same, because the client just replies the seq_number
788178825Sdfr     * from our AP-REP in its AP-REP, but then the client uses the
789178825Sdfr     * seq_number from its AP-REQ for GSS_wrap()
790178825Sdfr     */
791178825Sdfr    {
792178825Sdfr	int32_t tmp_r_seq_number, tmp_l_seq_number;
793178825Sdfr
794233294Sstas	kret = krb5_auth_con_getremoteseqnumber(context,
795233294Sstas						ctx->auth_context,
796233294Sstas						&tmp_r_seq_number);
797178825Sdfr	if (kret) {
798178825Sdfr	    *minor_status = kret;
799178825Sdfr	    return GSS_S_FAILURE;
800178825Sdfr	}
801178825Sdfr
802178825Sdfr	kret = krb5_auth_con_getlocalseqnumber(context,
803178825Sdfr					       ctx->auth_context,
804178825Sdfr					       &tmp_l_seq_number);
805178825Sdfr	if (kret) {
806178825Sdfr
807178825Sdfr	    *minor_status = kret;
808178825Sdfr	    return GSS_S_FAILURE;
809178825Sdfr	}
810178825Sdfr
811178825Sdfr	/*
812178825Sdfr	 * Here we check if the client has responsed with our local seq_number,
813178825Sdfr	 */
814178825Sdfr	if (tmp_r_seq_number != tmp_l_seq_number) {
815178825Sdfr	    return GSS_S_UNSEQ_TOKEN;
816178825Sdfr	}
817178825Sdfr    }
818178825Sdfr
819178825Sdfr    /*
820178825Sdfr     * We need to reset the remote seq_number, because the client will use,
821178825Sdfr     * the old one for the GSS_wrap() calls
822178825Sdfr     */
823178825Sdfr    {
824178825Sdfr	kret = krb5_auth_con_setremoteseqnumber(context,
825178825Sdfr						ctx->auth_context,
826233294Sstas						r_seq_number);
827178825Sdfr	if (kret) {
828178825Sdfr	    *minor_status = kret;
829178825Sdfr	    return GSS_S_FAILURE;
830178825Sdfr	}
831178825Sdfr    }
832178825Sdfr
833233294Sstas    return gsskrb5_acceptor_ready(minor_status, ctx, context,
834178825Sdfr				  delegated_cred_handle);
835178825Sdfr}
836178825Sdfr
837178825Sdfr
838233294SstasOM_uint32 GSSAPI_CALLCONV
839178825Sdfr_gsskrb5_accept_sec_context(OM_uint32 * minor_status,
840178825Sdfr			    gss_ctx_id_t * context_handle,
841178825Sdfr			    const gss_cred_id_t acceptor_cred_handle,
842178825Sdfr			    const gss_buffer_t input_token_buffer,
843178825Sdfr			    const gss_channel_bindings_t input_chan_bindings,
844178825Sdfr			    gss_name_t * src_name,
845178825Sdfr			    gss_OID * mech_type,
846178825Sdfr			    gss_buffer_t output_token,
847178825Sdfr			    OM_uint32 * ret_flags,
848178825Sdfr			    OM_uint32 * time_rec,
849178825Sdfr			    gss_cred_id_t * delegated_cred_handle)
850178825Sdfr{
851178825Sdfr    krb5_context context;
852178825Sdfr    OM_uint32 ret;
853178825Sdfr    gsskrb5_ctx ctx;
854178825Sdfr
855178825Sdfr    GSSAPI_KRB5_INIT(&context);
856178825Sdfr
857178825Sdfr    output_token->length = 0;
858178825Sdfr    output_token->value = NULL;
859178825Sdfr
860178825Sdfr    if (src_name != NULL)
861178825Sdfr	*src_name = NULL;
862178825Sdfr    if (mech_type)
863178825Sdfr	*mech_type = GSS_KRB5_MECHANISM;
864178825Sdfr
865178825Sdfr    if (*context_handle == GSS_C_NO_CONTEXT) {
866178825Sdfr	ret = _gsskrb5_create_ctx(minor_status,
867178825Sdfr				  context_handle,
868178825Sdfr				  context,
869178825Sdfr				  input_chan_bindings,
870178825Sdfr				  ACCEPTOR_START);
871178825Sdfr	if (ret)
872178825Sdfr	    return ret;
873178825Sdfr    }
874233294Sstas
875178825Sdfr    ctx = (gsskrb5_ctx)*context_handle;
876178825Sdfr
877233294Sstas
878178825Sdfr    /*
879233294Sstas     * TODO: check the channel_bindings
880178825Sdfr     * (above just sets them to krb5 layer)
881178825Sdfr     */
882178825Sdfr
883178825Sdfr    HEIMDAL_MUTEX_lock(&ctx->ctx_id_mutex);
884233294Sstas
885178825Sdfr    switch (ctx->state) {
886178825Sdfr    case ACCEPTOR_START:
887178825Sdfr	ret = gsskrb5_acceptor_start(minor_status,
888178825Sdfr				     ctx,
889178825Sdfr				     context,
890178825Sdfr				     acceptor_cred_handle,
891178825Sdfr				     input_token_buffer,
892178825Sdfr				     input_chan_bindings,
893178825Sdfr				     src_name,
894178825Sdfr				     mech_type,
895178825Sdfr				     output_token,
896178825Sdfr				     ret_flags,
897178825Sdfr				     time_rec,
898178825Sdfr				     delegated_cred_handle);
899178825Sdfr	break;
900178825Sdfr    case ACCEPTOR_WAIT_FOR_DCESTYLE:
901178825Sdfr	ret = acceptor_wait_for_dcestyle(minor_status,
902178825Sdfr					 ctx,
903178825Sdfr					 context,
904178825Sdfr					 acceptor_cred_handle,
905178825Sdfr					 input_token_buffer,
906178825Sdfr					 input_chan_bindings,
907178825Sdfr					 src_name,
908178825Sdfr					 mech_type,
909178825Sdfr					 output_token,
910178825Sdfr					 ret_flags,
911178825Sdfr					 time_rec,
912178825Sdfr					 delegated_cred_handle);
913178825Sdfr	break;
914178825Sdfr    case ACCEPTOR_READY:
915233294Sstas	/*
916178825Sdfr	 * If we get there, the caller have called
917178825Sdfr	 * gss_accept_sec_context() one time too many.
918178825Sdfr	 */
919178825Sdfr	ret =  GSS_S_BAD_STATUS;
920178825Sdfr	break;
921178825Sdfr    default:
922178825Sdfr	/* TODO: is this correct here? --metze */
923178825Sdfr	ret =  GSS_S_BAD_STATUS;
924178825Sdfr	break;
925178825Sdfr    }
926233294Sstas
927178825Sdfr    HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex);
928233294Sstas
929178825Sdfr    if (GSS_ERROR(ret)) {
930178825Sdfr	OM_uint32 min2;
931178825Sdfr	_gsskrb5_delete_sec_context(&min2, context_handle, GSS_C_NO_BUFFER);
932178825Sdfr    }
933178825Sdfr
934178825Sdfr    return ret;
935178825Sdfr}
936