1/*
2 * Copyright (c) 1997 - 2006 Kungliga Tekniska Högskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
4 * All rights reserved.
5 *
6 * Portions Copyright (c) 2009 Apple Inc. All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 *
12 * 1. Redistributions of source code must retain the above copyright
13 *    notice, this list of conditions and the following disclaimer.
14 *
15 * 2. Redistributions in binary form must reproduce the above copyright
16 *    notice, this list of conditions and the following disclaimer in the
17 *    documentation and/or other materials provided with the distribution.
18 *
19 * 3. Neither the name of the Institute nor the names of its contributors
20 *    may be used to endorse or promote products derived from this software
21 *    without specific prior written permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 * SUCH DAMAGE.
34 */
35
36#include "gsskrb5_locl.h"
37
38#include <heim-ipc.h>
39
40#define GKAS(name) static OM_uint32					\
41name(OM_uint32 *, gsskrb5_ctx, krb5_context, const gss_cred_id_t,	\
42      const gss_buffer_t, const gss_channel_bindings_t,			\
43      gss_name_t *, gss_OID *, gss_buffer_t,				\
44      OM_uint32 *, OM_uint32 *, gss_cred_id_t *)
45
46
47GKAS(acceptor_wait_for_dcestyle);
48GKAS(gsskrb5_acceptor_start);
49GKAS(step_acceptor_completed);
50
51HEIMDAL_MUTEX gssapi_keytab_mutex = HEIMDAL_MUTEX_INITIALIZER;
52krb5_keytab _gsskrb5_keytab;
53
54static krb5_error_code
55validate_keytab(krb5_context context, const char *name, krb5_keytab *id)
56{
57    krb5_error_code ret;
58
59    ret = krb5_kt_resolve(context, name, id);
60    if (ret)
61	return ret;
62
63    ret = krb5_kt_have_content(context, *id);
64    if (ret) {
65	krb5_kt_close(context, *id);
66	*id = NULL;
67    }
68
69    return ret;
70}
71
72OM_uint32
73_gsskrb5_register_acceptor_identity(OM_uint32 *min_stat, const char *identity)
74{
75    krb5_context context;
76    krb5_error_code ret;
77
78    *min_stat = 0;
79
80    ret = _gsskrb5_init(&context);
81    if(ret)
82	return GSS_S_FAILURE;
83
84    HEIMDAL_MUTEX_lock(&gssapi_keytab_mutex);
85
86    if(_gsskrb5_keytab != NULL) {
87	krb5_kt_close(context, _gsskrb5_keytab);
88	_gsskrb5_keytab = NULL;
89    }
90    if (identity == NULL) {
91	ret = krb5_kt_default(context, &_gsskrb5_keytab);
92    } else {
93	/*
94	 * First check if we can the keytab as is and if it has content...
95	 */
96	ret = validate_keytab(context, identity, &_gsskrb5_keytab);
97	/*
98	 * if it doesn't, lets prepend FILE: and try again
99	 */
100	if (ret) {
101	    char *p = NULL;
102	    ret = asprintf(&p, "FILE:%s", identity);
103	    if(ret < 0 || p == NULL) {
104		HEIMDAL_MUTEX_unlock(&gssapi_keytab_mutex);
105		return GSS_S_FAILURE;
106	    }
107	    ret = validate_keytab(context, p, &_gsskrb5_keytab);
108	    free(p);
109	}
110    }
111    HEIMDAL_MUTEX_unlock(&gssapi_keytab_mutex);
112    if(ret) {
113	*min_stat = ret;
114	return GSS_S_FAILURE;
115    }
116    return GSS_S_COMPLETE;
117}
118
119void
120_gsskrb5i_is_cfx(krb5_context context, gsskrb5_ctx ctx, int acceptor)
121{
122    krb5_keyblock *key;
123
124    krb5_auth_con_getlocalseqnumber(context, ctx->auth_context,
125				    (int32_t *)&ctx->gk5c.seqnumlo);
126    ctx->gk5c.seqnumhi = 0;
127
128    if (acceptor) {
129	if (ctx->auth_context->local_subkey)
130	    key = ctx->auth_context->local_subkey;
131	else
132	    key = ctx->auth_context->remote_subkey;
133    } else {
134	if (ctx->auth_context->remote_subkey)
135	    key = ctx->auth_context->remote_subkey;
136	else
137	    key = ctx->auth_context->local_subkey;
138    }
139    if (key == NULL)
140	key = ctx->auth_context->keyblock;
141
142    if (key == NULL)
143	return;
144
145    switch (key->keytype) {
146    case ETYPE_DES_CBC_CRC:
147    case ETYPE_DES_CBC_MD4:
148    case ETYPE_DES_CBC_MD5:
149    case ETYPE_DES3_CBC_MD5:
150    case ETYPE_OLD_DES3_CBC_SHA1:
151    case ETYPE_DES3_CBC_SHA1:
152    case ETYPE_ARCFOUR_HMAC_MD5:
153    case ETYPE_ARCFOUR_HMAC_MD5_56:
154	break;
155    default :
156        ctx->more_flags |= IS_CFX;
157
158	ctx->gk5c.flags &= ~(GK5C_ACCEPTOR_SUBKEY | GK5C_DCE_STYLE);
159
160	if (acceptor) {
161	    ctx->gk5c.flags |= GK5C_ACCEPTOR;
162	    if (ctx->auth_context->local_subkey)
163		ctx->gk5c.flags |= GK5C_ACCEPTOR_SUBKEY;
164	} else {
165	    if (ctx->auth_context->remote_subkey)
166		ctx->gk5c.flags |= GK5C_ACCEPTOR_SUBKEY;
167	}
168
169	if (ctx->flags & GSS_C_DCE_STYLE)
170	    ctx->gk5c.flags |= GK5C_DCE_STYLE;
171
172	break;
173    }
174    if (ctx->gk5c.crypto)
175        krb5_crypto_destroy(context, ctx->gk5c.crypto);
176    krb5_crypto_init(context, key, 0, &ctx->gk5c.crypto);
177}
178
179
180static OM_uint32
181gsskrb5_accept_delegated_token
182(OM_uint32 * minor_status,
183 gsskrb5_ctx ctx,
184 krb5_context context,
185 gss_cred_id_t * delegated_cred_handle
186    )
187{
188    krb5_ccache ccache = NULL;
189    krb5_error_code kret;
190    int32_t ac_flags, ret = GSS_S_COMPLETE;
191
192    *minor_status = 0;
193
194    /* XXX Create a new delegated_cred_handle? */
195    if (delegated_cred_handle == NULL) {
196	kret = krb5_cc_default (context, &ccache);
197    } else {
198	*delegated_cred_handle = NULL;
199	kret = krb5_cc_new_unique (context, krb5_cc_type_memory,
200				   NULL, &ccache);
201    }
202    if (kret) {
203	ctx->flags &= ~GSS_C_DELEG_FLAG;
204	goto out;
205    }
206
207    kret = krb5_cc_initialize(context, ccache, ctx->source);
208    if (kret) {
209	ctx->flags &= ~GSS_C_DELEG_FLAG;
210	goto out;
211    }
212
213    krb5_auth_con_removeflags(context,
214			      ctx->auth_context,
215			      KRB5_AUTH_CONTEXT_DO_TIME,
216			      &ac_flags);
217    kret = krb5_rd_cred2(context,
218			 ctx->auth_context,
219			 ccache,
220			 &ctx->fwd_data);
221    krb5_auth_con_setflags(context,
222			   ctx->auth_context,
223			   ac_flags);
224    if (kret) {
225	ctx->flags &= ~GSS_C_DELEG_FLAG;
226	ret = GSS_S_FAILURE;
227	*minor_status = kret;
228	goto out;
229    }
230
231    if (delegated_cred_handle) {
232	gsskrb5_cred handle;
233
234	ret = _gsskrb5_krb5_import_cred(minor_status,
235					ccache,
236					NULL,
237					NULL,
238					delegated_cred_handle);
239	if (ret != GSS_S_COMPLETE)
240	    goto out;
241
242	handle = (gsskrb5_cred) *delegated_cred_handle;
243
244	handle->cred_flags |= GSS_CF_DESTROY_CRED_ON_RELEASE;
245	krb5_cc_close(context, ccache);
246	ccache = NULL;
247    }
248
249out:
250    if (ccache) {
251	/* Don't destroy the default cred cache */
252	if (delegated_cred_handle == NULL)
253	    krb5_cc_close(context, ccache);
254	else
255	    krb5_cc_destroy(context, ccache);
256    }
257    return ret;
258}
259
260
261static OM_uint32
262step_acceptor_completed(OM_uint32 * minor_status,
263			gsskrb5_ctx ctx,
264			krb5_context context,
265			const gss_cred_id_t acceptor_cred_handle,
266			const gss_buffer_t input_token_buffer,
267			const gss_channel_bindings_t input_chan_bindings,
268			gss_name_t * src_name,
269			gss_OID * mech_type,
270			gss_buffer_t output_token,
271			OM_uint32 * ret_flags,
272			OM_uint32 * time_rec,
273			gss_cred_id_t * delegated_cred_handle)
274{
275    /*
276     * If we get there, the caller have called
277     * gss_accept_sec_context() one time too many.
278     */
279    return GSS_S_BAD_STATUS;
280}
281
282static OM_uint32
283gsskrb5_acceptor_ready(OM_uint32 * minor_status,
284		       gsskrb5_ctx ctx,
285		       krb5_context context,
286		       gss_cred_id_t *delegated_cred_handle)
287{
288    OM_uint32 ret;
289    int32_t seq_number;
290    int is_cfx = 0;
291
292    krb5_auth_con_getremoteseqnumber (context,
293				      ctx->auth_context,
294				      &seq_number);
295
296    _gsskrb5i_is_cfx(context, ctx, 1);
297    is_cfx = (ctx->more_flags & IS_CFX);
298
299    ret = _gssapi_msg_order_create(minor_status,
300				   &ctx->gk5c.order,
301				   _gssapi_msg_order_f(ctx->flags),
302				   seq_number, 0, is_cfx);
303    if (ret)
304	return ret;
305
306    /*
307     * If requested, set local sequence num to remote sequence if this
308     * isn't a mutual authentication context
309     */
310    if (!(ctx->flags & GSS_C_MUTUAL_FLAG) && _gssapi_msg_order_f(ctx->flags)) {
311	krb5_auth_con_setlocalseqnumber(context,
312					ctx->auth_context,
313					seq_number);
314    }
315
316    /*
317     * We should handle the delegation ticket, in case it's there
318     */
319    if (ctx->fwd_data.length > 0 && (ctx->flags & GSS_C_DELEG_FLAG)) {
320	ret = gsskrb5_accept_delegated_token(minor_status,
321					     ctx,
322					     context,
323					     delegated_cred_handle);
324	if (ret)
325	    return ret;
326    } else {
327	/* Well, looks like it wasn't there after all */
328	ctx->flags &= ~GSS_C_DELEG_FLAG;
329    }
330
331    ctx->acceptor_state = step_acceptor_completed;
332
333    ctx->more_flags |= OPEN;
334
335    return GSS_S_COMPLETE;
336}
337
338OM_uint32
339_gsskrb5_error_token(OM_uint32 *minor_status,
340		     gss_OID mech,
341		     krb5_context context,
342		     krb5_error_code error_code,
343		     krb5_data *e_data,
344		     krb5_principal server,
345		     gss_buffer_t output_token)
346{
347    krb5_error_code ret;
348    krb5_data outbuf;
349
350    ret = krb5_mk_error(context, error_code, NULL, e_data, NULL,
351			server, NULL, NULL, &outbuf);
352    if (ret) {
353	*minor_status = ret;
354	return GSS_S_FAILURE;
355    }
356
357    ret = _gsskrb5_encapsulate(minor_status,
358			       &outbuf,
359			       output_token,
360			       "\x03\x00",
361			       mech);
362    krb5_data_free (&outbuf);
363    if (ret)
364	return ret;
365
366    *minor_status = 0;
367    return GSS_S_COMPLETE;
368}
369
370static OM_uint32
371send_error_token(OM_uint32 *minor_status,
372		 krb5_context context,
373		 krb5_error_code kret,
374		 krb5_principal server,
375		 krb5_data *indata,
376		 gss_OID mech,
377		 gss_buffer_t output_token)
378{
379    krb5_principal ap_req_server = NULL;
380    OM_uint32 maj_stat;
381    /* this e_data value encodes KERB_AP_ERR_TYPE_SKEW_RECOVERY which
382       tells windows to try again with the corrected timestamp. See
383       [MS-KILE] 2.2.1 KERB-ERROR-DATA */
384    krb5_data e_data = { 7, rk_UNCONST("\x30\x05\xa1\x03\x02\x01\x02") };
385
386    /* build server from request if the acceptor had not selected one */
387    if (server == NULL && indata) {
388	krb5_error_code ret;
389	AP_REQ ap_req;
390
391	ret = krb5_decode_ap_req(context, indata, &ap_req);
392	if (ret) {
393	    *minor_status = ret;
394	    return GSS_S_FAILURE;
395	}
396	ret = _krb5_principalname2krb5_principal(context,
397						  &ap_req_server,
398						  ap_req.ticket.sname,
399						  ap_req.ticket.realm);
400	free_AP_REQ(&ap_req);
401	if (ret) {
402	    *minor_status = ret;
403	    return GSS_S_FAILURE;
404	}
405	server = ap_req_server;
406    }
407
408    maj_stat = _gsskrb5_error_token(minor_status, mech, context, kret,
409				    &e_data, server,  output_token);
410    if (ap_req_server)
411	krb5_free_principal(context, ap_req_server);
412    if (maj_stat)
413	return GSS_S_FAILURE;
414
415    *minor_status = 0;
416    return GSS_S_CONTINUE_NEEDED;
417}
418
419static OM_uint32
420iakerb_acceptor_start(OM_uint32 * minor_status,
421		      gsskrb5_ctx ctx,
422		      krb5_context context,
423		      const gss_cred_id_t acceptor_cred_handle,
424		      const gss_buffer_t input_token_buffer,
425		      const gss_channel_bindings_t input_chan_bindings,
426		      gss_name_t * src_name,
427		      gss_OID * mech_type,
428		      gss_buffer_t output_token,
429		      OM_uint32 * ret_flags,
430		      OM_uint32 * time_rec,
431		      gss_cred_id_t * delegated_cred_handle)
432{
433    krb5_data indata, outdata;
434    gss_buffer_desc idata;
435    krb5_error_code kret;
436    heim_ipc ictx;
437    OM_uint32 ret;
438
439    if (ctx->messages == NULL) {
440	ctx->messages = krb5_storage_emem();
441	if (ctx->messages == NULL) {
442	    *minor_status = ENOMEM;
443	    return GSS_S_FAILURE;
444	}
445    }
446
447    ret = _gsskrb5_iakerb_parse_header(minor_status, context, ctx, input_token_buffer, &indata);
448    if (ret == GSS_S_DEFECTIVE_TOKEN) {
449	ctx->acceptor_state = gsskrb5_acceptor_start;
450	return GSS_S_COMPLETE;
451    } else if (ret != GSS_S_COMPLETE)
452	return ret;
453
454    krb5_storage_write(ctx->messages,
455		       input_token_buffer->value,
456		       input_token_buffer->length);
457
458    idata.value = indata.data;
459    idata.length = indata.length;
460
461    heim_assert(ctx->iakerbrealm != NULL, "realm not set by decoder, non OPT value");
462
463    if (krb5_realm_is_lkdc(ctx->iakerbrealm)) {
464
465	kret = heim_ipc_init_context("ANY:org.h5l.kdc", &ictx);
466	if (kret) {
467	    *minor_status = kret;
468	    return GSS_S_FAILURE;
469	}
470
471	kret = heim_ipc_call(ictx, &indata, &outdata, NULL);
472	heim_ipc_free_context(ictx);
473	if (kret) {
474	    _gsskrb5_error_token(minor_status, ctx->mech, context, kret,
475				 NULL, NULL, output_token);
476	    *minor_status = kret;
477	    return GSS_S_FAILURE;
478	}
479
480	ret = _gsskrb5_iakerb_make_header(minor_status, context, ctx, ctx->iakerbrealm, &outdata, output_token);
481	heim_ipc_free_data(&outdata);
482	if (ret)
483	    return ret;
484
485    } else {
486	/* XXX dont support non local realms right now */
487	*minor_status = EINVAL;
488	return GSS_S_FAILURE;
489    }
490
491    krb5_storage_write(ctx->messages,
492		       output_token->value,
493		       output_token->length);
494
495    return GSS_S_CONTINUE_NEEDED;
496}
497
498
499static OM_uint32
500pku2u_acceptor_start(OM_uint32 * minor_status,
501		     gsskrb5_ctx ctx,
502		     krb5_context context,
503		     const gss_cred_id_t acceptor_cred_handle,
504		     const gss_buffer_t input_token_buffer,
505		     const gss_channel_bindings_t input_chan_bindings,
506		     gss_name_t * src_name,
507		     gss_OID * mech_type,
508		     gss_buffer_t output_token,
509		     OM_uint32 * ret_flags,
510		     OM_uint32 * time_rec,
511		     gss_cred_id_t * delegated_cred_handle)
512{
513    krb5_data indata, outdata;
514    krb5_error_code kret;
515    heim_ipc ictx;
516    OM_uint32 ret;
517
518    if (ctx->messages == NULL) {
519	ctx->messages = krb5_storage_emem();
520	if (ctx->messages == NULL) {
521	    *minor_status = ENOMEM;
522	    return GSS_S_FAILURE;
523	}
524    }
525
526    ret = _gsskrb5_decapsulate (minor_status,
527				input_token_buffer,
528				&indata,
529				"\x05\x01",
530				ctx->mech);
531    if (ret == GSS_S_DEFECTIVE_TOKEN) {
532	ctx->acceptor_state = gsskrb5_acceptor_start;
533	return GSS_S_COMPLETE;
534    } else if (ret != GSS_S_COMPLETE)
535	return ret;
536
537    krb5_storage_write(ctx->messages,
538		       input_token_buffer->value,
539		       input_token_buffer->length);
540
541    kret = heim_ipc_init_context("ANY:org.h5l.kdc", &ictx);
542    if (kret) {
543	*minor_status = kret;
544	return GSS_S_FAILURE;
545    }
546
547
548    ret = _gsskrb5_encapsulate(minor_status,
549			       &outdata,
550			       output_token,
551			       "\x06\x00",
552			       ctx->mech);
553    heim_ipc_free_data(&outdata);
554    if (ret != GSS_S_COMPLETE)
555	return ret;
556
557    krb5_storage_write(ctx->messages,
558		       output_token->value,
559		       output_token->length);
560
561
562    *minor_status = 0;
563    return GSS_S_FAILURE;
564}
565
566static OM_uint32
567gsskrb5_acceptor_start(OM_uint32 * minor_status,
568		       gsskrb5_ctx ctx,
569		       krb5_context context,
570		       const gss_cred_id_t acceptor_cred_handle,
571		       const gss_buffer_t input_token_buffer,
572		       const gss_channel_bindings_t input_chan_bindings,
573		       gss_name_t * src_name,
574		       gss_OID * mech_type,
575		       gss_buffer_t output_token,
576		       OM_uint32 * ret_flags,
577		       OM_uint32 * time_rec,
578		       gss_cred_id_t * delegated_cred_handle)
579{
580    krb5_error_code kret;
581    OM_uint32 ret = GSS_S_COMPLETE;
582    krb5_data indata;
583    krb5_flags ap_options;
584    krb5_keytab keytab = NULL;
585    int is_cfx = 0;
586    const gsskrb5_cred acceptor_cred = (gsskrb5_cred)acceptor_cred_handle;
587    krb5_boolean is_hostbased_service = FALSE;
588
589    /*
590     * We may, or may not, have an escapsulation.
591     */
592    ret = _gsskrb5_decapsulate (minor_status,
593				input_token_buffer,
594				&indata,
595				"\x01\x00",
596				ctx->mech);
597
598    if (ret) {
599	/* Assume that there is no OID wrapping. */
600	indata.length	= input_token_buffer->length;
601	indata.data	= input_token_buffer->value;
602    }
603
604    /*
605     * We need to get our keytab
606     */
607    if (acceptor_cred == NULL) {
608	if (_gsskrb5_keytab != NULL)
609	    keytab = _gsskrb5_keytab;
610    } else if (acceptor_cred->keytab != NULL) {
611	keytab = acceptor_cred->keytab;
612    }
613
614    is_hostbased_service =
615	(acceptor_cred &&
616	 acceptor_cred->principal &&
617	 krb5_principal_is_gss_hostbased_service(context, acceptor_cred->principal));
618
619    /*
620     * We need to check the ticket and create the AP-REP packet
621     */
622
623    {
624	krb5_rd_req_in_ctx in = NULL;
625	krb5_rd_req_out_ctx out = NULL;
626	krb5_principal server = NULL;
627
628	if (acceptor_cred && !is_hostbased_service)
629	    server = acceptor_cred->principal;
630
631	kret = krb5_rd_req_in_ctx_alloc(context, &in);
632	if (kret == 0)
633	    kret = krb5_rd_req_in_set_keytab(context, in, keytab);
634	if (kret) {
635	    if (in)
636		krb5_rd_req_in_ctx_free(context, in);
637	    *minor_status = kret;
638	    return GSS_S_FAILURE;
639	}
640
641	kret = krb5_rd_req_ctx(context,
642			       &ctx->auth_context,
643			       &indata,
644			       server,
645			       in, &out);
646	krb5_rd_req_in_ctx_free(context, in);
647	if (ret && _gss_mg_log_level(5)) {
648	    const char *e = krb5_get_error_message(context, ret);
649	    char *s = NULL;
650	    if (server)
651		(void)krb5_unparse_name(context, server, &s);
652	    _gss_mg_log(5, "gss-asc: rd_req (server: %s) failed with: %d: %s",
653			s ? s : "<not specified>",
654			ret, e);
655	    krb5_free_error_message(context, e);
656	    if (s)
657		krb5_xfree(s);
658	}
659
660
661	switch (kret) {
662	case 0:
663	    break;
664	case KRB5KRB_AP_ERR_SKEW:
665	case KRB5KRB_AP_ERR_TKT_NYV:
666	    /*
667	     * No reply in non-MUTUAL mode, but we don't know that its
668	     * non-MUTUAL mode yet, thats inside the 8003 checksum, so
669	     * lets only send the error token on clock skew, that
670	     * limit when send error token for non-MUTUAL.
671	     */
672	    return send_error_token(minor_status, context, kret,
673				    server, &indata, ctx->mech, output_token);
674	case KRB5KRB_AP_ERR_MODIFIED:
675	case KRB5_KT_NOTFOUND:
676	case KRB5_KT_END:
677	    /*
678	     * If the error is on the keytab entry missing or bad
679	     * decryption, lets assume that the keytab version was
680	     * wrong and tell the client that.
681	     */
682	    return send_error_token(minor_status, context, KRB5KRB_AP_ERR_MODIFIED,
683				    server, NULL, ctx->mech, output_token);
684	default:
685	    *minor_status = kret;
686	    return GSS_S_FAILURE;
687	}
688
689	/*
690	 * we need to remember some data on the context_handle.
691	 */
692	kret = krb5_rd_req_out_get_ap_req_options(context, out,
693						  &ap_options);
694	if (kret == 0)
695	    kret = krb5_rd_req_out_get_ticket(context, out,
696					      &ctx->ticket);
697	if (kret == 0)
698	    kret = krb5_rd_req_out_get_keyblock(context, out,
699						&ctx->service_keyblock);
700	if (kret == 0) {
701	    int flags;
702	    flags = krb5_rd_req_out_get_flags(context, out);
703	    if (flags & KRB5_RD_REQ_OUT_PAC_VALID)
704		ctx->more_flags |= PAC_VALID;
705	}
706	if (kret == 0 && is_hostbased_service) {
707	    krb5_principal sp = ctx->ticket->server;
708
709	    if (sp->name.name_string.len < 1 ||
710		strcmp(sp->name.name_string.val[0], acceptor_cred->principal->name.name_string.val[0]) != 0)
711	    {
712		kret = KRB5KRB_AP_WRONG_PRINC;
713		krb5_set_error_message(context, ret, "Expecting service %s but got %s",
714				       acceptor_cred->principal->name.name_string.val[0],
715				       sp->name.name_string.val[0]);
716	    }
717	}
718
719	ctx->endtime = ctx->ticket->ticket.endtime;
720
721	krb5_rd_req_out_ctx_free(context, out);
722	if (kret) {
723	    ret = GSS_S_FAILURE;
724	    *minor_status = kret;
725	    return ret;
726	}
727    }
728
729
730    /*
731     * We need to copy the principal names to the context and the
732     * calling layer.
733     */
734    kret = krb5_copy_principal(context,
735			       ctx->ticket->client,
736			       &ctx->source);
737    if (kret) {
738	*minor_status = kret;
739	return GSS_S_FAILURE;
740    }
741
742    kret = krb5_copy_principal(context,
743			       ctx->ticket->server,
744			       &ctx->target);
745    if (kret) {
746	ret = GSS_S_FAILURE;
747	*minor_status = kret;
748	return ret;
749    }
750
751    /*
752     * We need to setup some compat stuff, this assumes that
753     * context_handle->target is already set.
754     */
755    ret = _gss_DES3_get_mic_compat(minor_status, ctx, context);
756    if (ret)
757	return ret;
758
759    if (src_name != NULL) {
760	kret = krb5_copy_principal (context,
761				    ctx->ticket->client,
762				    (gsskrb5_name*)src_name);
763	if (kret) {
764	    ret = GSS_S_FAILURE;
765	    *minor_status = kret;
766	    return ret;
767	}
768    }
769
770    /*
771     * We need to get the flags out of the 8003 checksum.
772     */
773
774    {
775	krb5_authenticator authenticator;
776
777	kret = krb5_auth_con_getauthenticator(context,
778					      ctx->auth_context,
779					      &authenticator);
780	if(kret) {
781	    ret = GSS_S_FAILURE;
782	    *minor_status = kret;
783	    return ret;
784	}
785
786	if (authenticator->cksum == NULL) {
787	    krb5_free_authenticator(context, &authenticator);
788	    *minor_status = 0;
789	    return GSS_S_BAD_BINDINGS;
790	}
791
792        if (authenticator->cksum->cksumtype == CKSUMTYPE_GSSAPI) {
793	    krb5_data finished_data;
794	    krb5_crypto crypto = NULL;
795
796	    if (ctx->auth_context->remote_subkey) {
797		kret = krb5_crypto_init(context,
798					ctx->auth_context->remote_subkey,
799					0, &crypto);
800		if (kret) {
801		    *minor_status = kret;
802		    return GSS_S_FAILURE;
803		}
804	    }
805
806	    krb5_data_zero(&finished_data);
807
808            ret = _gsskrb5_verify_8003_checksum(minor_status,
809						context,
810						crypto,
811						input_chan_bindings,
812						authenticator->cksum,
813						&ctx->flags,
814						&ctx->fwd_data,
815						&finished_data);
816
817	    krb5_free_authenticator(context, &authenticator);
818	    if (ret) {
819		krb5_crypto_destroy(context, crypto);
820		return ret;
821	    }
822
823	    if (finished_data.length) {
824		GSS_KRB5_FINISHED finished;
825		krb5_data pkt;
826
827		memset(&finished, 0, sizeof(finished));
828
829		if (ctx->messages == NULL) {
830		    krb5_crypto_destroy(context, crypto);
831		    krb5_data_free(&finished_data);
832		    *minor_status = 0;
833		    return GSS_S_BAD_SIG;
834		}
835
836		kret = krb5_storage_to_data(ctx->messages, &pkt);
837		if (kret) {
838		    krb5_crypto_destroy(context, crypto);
839		    krb5_data_free(&finished_data);
840		    *minor_status = kret;
841		    return GSS_S_FAILURE;
842		}
843
844		if (ctx->auth_context->remote_subkey == NULL) {
845		    krb5_crypto_destroy(context, crypto);
846		    krb5_data_free(&finished_data);
847		    krb5_data_free(&pkt);
848		    *minor_status = 0;
849		    return GSS_S_BAD_SIG;
850		}
851
852		kret = decode_GSS_KRB5_FINISHED(finished_data.data,
853						finished_data.length,
854						&finished, NULL);
855		krb5_data_free(&finished_data);
856		if (kret) {
857		    krb5_crypto_destroy(context, crypto);
858		    krb5_data_free(&pkt);
859		    *minor_status = kret;
860		    return GSS_S_FAILURE;
861		}
862
863		kret = krb5_verify_checksum(context, crypto,
864					    KRB5_KU_FINISHED,
865					    pkt.data, pkt.length,
866					    &finished.gss_mic);
867		free_GSS_KRB5_FINISHED(&finished);
868		krb5_data_free(&pkt);
869		if (kret) {
870		    krb5_crypto_destroy(context, crypto);
871		    *minor_status = kret;
872		    return GSS_S_FAILURE;
873		}
874	    }
875	    krb5_crypto_destroy(context, crypto);
876
877        } else {
878	    krb5_crypto crypto;
879
880	    kret = krb5_crypto_init(context,
881				    ctx->auth_context->keyblock,
882				    0, &crypto);
883	    if(kret) {
884		krb5_free_authenticator(context, &authenticator);
885
886		ret = GSS_S_FAILURE;
887		*minor_status = kret;
888		return ret;
889	    }
890
891	    /*
892	     * Windows accepts Samba3's use of a kerberos, rather than
893	     * GSSAPI checksum here
894	     */
895
896	    kret = krb5_verify_checksum(context,
897					crypto, KRB5_KU_AP_REQ_AUTH_CKSUM, NULL, 0,
898					authenticator->cksum);
899	    krb5_free_authenticator(context, &authenticator);
900	    krb5_crypto_destroy(context, crypto);
901
902	    if(kret) {
903		ret = GSS_S_BAD_SIG;
904		*minor_status = kret;
905		return ret;
906	    }
907
908	    /*
909	     * Samba style get some flags (but not DCE-STYLE), use
910	     * ap_options to guess the mutual flag.
911	     */
912 	    ctx->flags = GSS_C_REPLAY_FLAG | GSS_C_SEQUENCE_FLAG;
913	    if (ap_options & AP_OPTS_MUTUAL_REQUIRED)
914		ctx->flags |= GSS_C_MUTUAL_FLAG;
915        }
916    }
917
918    if(ctx->flags & GSS_C_MUTUAL_FLAG) {
919	krb5_data outbuf;
920	int use_subkey = 0;
921
922	_gsskrb5i_is_cfx(context, ctx, 1);
923	is_cfx = (ctx->more_flags & IS_CFX);
924
925	if (is_cfx || (ap_options & AP_OPTS_USE_SUBKEY)) {
926	    use_subkey = 1;
927	} else {
928	    krb5_keyblock *rkey;
929
930	    /*
931	     * If there is a initiator subkey, copy that to acceptor
932	     * subkey to match Windows behavior
933	     */
934	    kret = krb5_auth_con_getremotesubkey(context,
935						 ctx->auth_context,
936						 &rkey);
937	    if (kret == 0) {
938		kret = krb5_auth_con_setlocalsubkey(context,
939						    ctx->auth_context,
940						    rkey);
941		if (kret == 0)
942		    use_subkey = 1;
943		krb5_free_keyblock(context, rkey);
944	    }
945	}
946	if (use_subkey) {
947	    ctx->gk5c.flags |= GK5C_ACCEPTOR_SUBKEY;
948	    krb5_auth_con_addflags(context, ctx->auth_context,
949				   KRB5_AUTH_CONTEXT_USE_SUBKEY,
950				   NULL);
951	}
952
953	kret = krb5_mk_rep(context,
954			   ctx->auth_context,
955			   &outbuf);
956	if (kret) {
957	    *minor_status = kret;
958	    return GSS_S_FAILURE;
959	}
960
961	if (IS_DCE_STYLE(ctx)) {
962	    output_token->length = outbuf.length;
963	    output_token->value = outbuf.data;
964	} else {
965	    ret = _gsskrb5_encapsulate(minor_status,
966				       &outbuf,
967				       output_token,
968				       "\x02\x00",
969				       ctx->mech);
970	    krb5_data_free (&outbuf);
971	    if (ret)
972		return ret;
973	}
974    }
975
976    ctx->flags |= GSS_C_TRANS_FLAG;
977
978    /* Remember the flags */
979
980    ctx->endtime = ctx->ticket->ticket.endtime;
981    ctx->more_flags |= OPEN;
982
983    if (mech_type)
984	*mech_type = ctx->mech;
985
986    if (time_rec) {
987	ret = _gsskrb5_lifetime_left(minor_status,
988				     context,
989				     ctx->endtime,
990				     time_rec);
991	if (ret) {
992	    return ret;
993	}
994    }
995
996    /*
997     * When GSS_C_DCE_STYLE is in use, we need ask for a AP-REP from
998     * the client.
999     */
1000    if (IS_DCE_STYLE(ctx)) {
1001	/*
1002	 * Return flags to caller, but we haven't processed
1003	 * delgations yet
1004	 */
1005	if (ret_flags)
1006	    *ret_flags = (ctx->flags & ~GSS_C_DELEG_FLAG);
1007
1008	ctx->acceptor_state = acceptor_wait_for_dcestyle;
1009	return GSS_S_CONTINUE_NEEDED;
1010    }
1011
1012    ret = gsskrb5_acceptor_ready(minor_status, ctx, context,
1013				 delegated_cred_handle);
1014
1015    if (ret_flags)
1016	*ret_flags = ctx->flags;
1017
1018    return ret;
1019}
1020
1021static OM_uint32
1022acceptor_wait_for_dcestyle(OM_uint32 * minor_status,
1023			   gsskrb5_ctx ctx,
1024			   krb5_context context,
1025			   const gss_cred_id_t acceptor_cred_handle,
1026			   const gss_buffer_t input_token_buffer,
1027			   const gss_channel_bindings_t input_chan_bindings,
1028			   gss_name_t * src_name,
1029			   gss_OID * mech_type,
1030			   gss_buffer_t output_token,
1031			   OM_uint32 * ret_flags,
1032			   OM_uint32 * time_rec,
1033			   gss_cred_id_t * delegated_cred_handle)
1034{
1035    OM_uint32 ret;
1036    krb5_error_code kret;
1037    krb5_data inbuf;
1038    int32_t r_seq_number, l_seq_number;
1039
1040    /*
1041     * We know it's GSS_C_DCE_STYLE so we don't need to decapsulate the AP_REP
1042     */
1043
1044    inbuf.length = input_token_buffer->length;
1045    inbuf.data = input_token_buffer->value;
1046
1047    /*
1048     * We need to remeber the old remote seq_number, then check if the
1049     * client has replied with our local seq_number, and then reset
1050     * the remote seq_number to the old value
1051     */
1052    {
1053	kret = krb5_auth_con_getlocalseqnumber(context,
1054					       ctx->auth_context,
1055					       &l_seq_number);
1056	if (kret) {
1057	    *minor_status = kret;
1058	    return GSS_S_FAILURE;
1059	}
1060
1061	kret = krb5_auth_con_getremoteseqnumber(context,
1062						ctx->auth_context,
1063						&r_seq_number);
1064	if (kret) {
1065	    *minor_status = kret;
1066	    return GSS_S_FAILURE;
1067	}
1068
1069	kret = krb5_auth_con_setremoteseqnumber(context,
1070						ctx->auth_context,
1071						l_seq_number);
1072	if (kret) {
1073	    *minor_status = kret;
1074	    return GSS_S_FAILURE;
1075	}
1076    }
1077
1078    /*
1079     * We need to verify the AP_REP, but we need to flag that this is
1080     * DCE_STYLE, so don't check the timestamps this time, but put the
1081     * flag DO_TIME back afterward.
1082    */
1083    {
1084	krb5_ap_rep_enc_part *repl;
1085	int32_t auth_flags;
1086
1087	krb5_auth_con_removeflags(context,
1088				  ctx->auth_context,
1089				  KRB5_AUTH_CONTEXT_DO_TIME,
1090				  &auth_flags);
1091
1092	kret = krb5_rd_rep(context, ctx->auth_context, &inbuf, &repl);
1093	if (kret) {
1094	    *minor_status = kret;
1095	    return GSS_S_FAILURE;
1096	}
1097	krb5_free_ap_rep_enc_part(context, repl);
1098	krb5_auth_con_setflags(context, ctx->auth_context, auth_flags);
1099    }
1100
1101    /* We need to check the liftime */
1102    {
1103	OM_uint32 lifetime_rec;
1104
1105	ret = _gsskrb5_lifetime_left(minor_status,
1106				     context,
1107				     ctx->endtime,
1108				     &lifetime_rec);
1109	if (ret) {
1110	    return ret;
1111	}
1112	if (lifetime_rec == 0) {
1113	    return GSS_S_CONTEXT_EXPIRED;
1114	}
1115
1116	if (time_rec) *time_rec = lifetime_rec;
1117    }
1118
1119    /* We need to give the caller the flags which are in use */
1120    if (ret_flags) *ret_flags = ctx->flags;
1121
1122    if (src_name) {
1123	kret = krb5_copy_principal(context,
1124				   ctx->source,
1125				   (gsskrb5_name*)src_name);
1126	if (kret) {
1127	    *minor_status = kret;
1128	    return GSS_S_FAILURE;
1129	}
1130    }
1131
1132    /*
1133     * After the krb5_rd_rep() the remote and local seq_number should
1134     * be the same, because the client just replies the seq_number
1135     * from our AP-REP in its AP-REP, but then the client uses the
1136     * seq_number from its AP-REQ for GSS_wrap()
1137     */
1138    {
1139	int32_t tmp_r_seq_number, tmp_l_seq_number;
1140
1141	kret = krb5_auth_con_getremoteseqnumber(context,
1142						ctx->auth_context,
1143						&tmp_r_seq_number);
1144	if (kret) {
1145	    *minor_status = kret;
1146	    return GSS_S_FAILURE;
1147	}
1148
1149	kret = krb5_auth_con_getlocalseqnumber(context,
1150					       ctx->auth_context,
1151					       &tmp_l_seq_number);
1152	if (kret) {
1153
1154	    *minor_status = kret;
1155	    return GSS_S_FAILURE;
1156	}
1157
1158	/*
1159	 * Here we check if the client has responsed with our local seq_number,
1160	 */
1161	if (tmp_r_seq_number != tmp_l_seq_number) {
1162	    return GSS_S_UNSEQ_TOKEN;
1163	}
1164    }
1165
1166    /*
1167     * We need to reset the remote seq_number, because the client will use,
1168     * the old one for the GSS_wrap() calls
1169     */
1170    {
1171	kret = krb5_auth_con_setremoteseqnumber(context,
1172						ctx->auth_context,
1173						r_seq_number);
1174	if (kret) {
1175	    *minor_status = kret;
1176	    return GSS_S_FAILURE;
1177	}
1178    }
1179
1180    return gsskrb5_acceptor_ready(minor_status, ctx, context,
1181				  delegated_cred_handle);
1182}
1183
1184
1185static OM_uint32
1186accept_sec_context(OM_uint32 * minor_status,
1187		   gss_ctx_id_t * context_handle,
1188		   const gss_cred_id_t acceptor_cred_handle,
1189		   const gss_buffer_t input_token_buffer,
1190		   const gss_channel_bindings_t input_chan_bindings,
1191		   gss_name_t * src_name,
1192		   gss_OID * mech_type,
1193		   gss_buffer_t output_token,
1194		   OM_uint32 * ret_flags,
1195		   OM_uint32 * time_rec,
1196		   gss_cred_id_t * delegated_cred_handle,
1197		   gss_OID mech,
1198		   gsskrb5_acceptor_state acceptor_state)
1199{
1200    krb5_context context;
1201    OM_uint32 ret;
1202    gsskrb5_ctx ctx;
1203
1204    GSSAPI_KRB5_INIT(&context);
1205
1206    output_token->length = 0;
1207    output_token->value = NULL;
1208
1209    if (src_name != NULL)
1210	*src_name = NULL;
1211    if (mech_type)
1212	*mech_type = mech;
1213
1214    if (*context_handle == GSS_C_NO_CONTEXT) {
1215	ret = _gsskrb5_create_ctx(minor_status,
1216				  context_handle,
1217				  context,
1218				  input_chan_bindings,
1219				  mech);
1220	if (ret)
1221	    return ret;
1222
1223	/* mark as acceptor */
1224	ctx = (gsskrb5_ctx)*context_handle;
1225	ctx->gk5c.flags |= GK5C_ACCEPTOR;
1226
1227	ctx->acceptor_state = acceptor_state;
1228    } else {
1229	ctx = (gsskrb5_ctx)*context_handle;
1230    }
1231
1232    HEIMDAL_MUTEX_lock(&ctx->ctx_id_mutex);
1233
1234    do {
1235	ret = ctx->acceptor_state(minor_status, ctx, context, acceptor_cred_handle,
1236				  input_token_buffer, input_chan_bindings,
1237				  src_name, mech_type, output_token, ret_flags,
1238				  time_rec, delegated_cred_handle);
1239    } while (output_token->length == 0
1240	     && ret == GSS_S_COMPLETE &&
1241	     ctx->acceptor_state != step_acceptor_completed);
1242
1243    HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex);
1244
1245    if (GSS_ERROR(ret)) {
1246	OM_uint32 min2;
1247	_gsskrb5_delete_sec_context(&min2, context_handle, GSS_C_NO_BUFFER);
1248    }
1249
1250    return ret;
1251}
1252
1253OM_uint32
1254_gsspku2u_accept_sec_context(OM_uint32 * minor_status,
1255			     gss_ctx_id_t * context_handle,
1256			     const gss_cred_id_t acceptor_cred_handle,
1257			     const gss_buffer_t input_token_buffer,
1258			     const gss_channel_bindings_t input_chan_bindings,
1259			     gss_name_t * src_name,
1260			     gss_OID * mech_type,
1261			     gss_buffer_t output_token,
1262			     OM_uint32 * ret_flags,
1263			     OM_uint32 * time_rec,
1264			     gss_cred_id_t * delegated_cred_handle)
1265{
1266    return accept_sec_context(minor_status,
1267			      context_handle,
1268			      acceptor_cred_handle,
1269			      input_token_buffer,
1270			      input_chan_bindings,
1271			      src_name,
1272			      mech_type,
1273			      output_token,
1274			      ret_flags,
1275			      time_rec,
1276			      delegated_cred_handle,
1277			      GSS_PKU2U_MECHANISM,
1278			      pku2u_acceptor_start);
1279}
1280
1281OM_uint32
1282_gsskrb5_accept_sec_context(OM_uint32 * minor_status,
1283			    gss_ctx_id_t * context_handle,
1284			    const gss_cred_id_t acceptor_cred_handle,
1285			    const gss_buffer_t input_token_buffer,
1286			    const gss_channel_bindings_t input_chan_bindings,
1287			    gss_name_t * src_name,
1288			    gss_OID * mech_type,
1289			    gss_buffer_t output_token,
1290			    OM_uint32 * ret_flags,
1291			    OM_uint32 * time_rec,
1292			    gss_cred_id_t * delegated_cred_handle)
1293{
1294    return accept_sec_context(minor_status,
1295			      context_handle,
1296			      acceptor_cred_handle,
1297			      input_token_buffer,
1298			      input_chan_bindings,
1299			      src_name,
1300			      mech_type,
1301			      output_token,
1302			      ret_flags,
1303			      time_rec,
1304			      delegated_cred_handle,
1305			      GSS_KRB5_MECHANISM,
1306			      gsskrb5_acceptor_start);
1307}
1308
1309OM_uint32
1310_gssiakerb_accept_sec_context(OM_uint32 * minor_status,
1311			      gss_ctx_id_t * context_handle,
1312			      const gss_cred_id_t acceptor_cred_handle,
1313			      const gss_buffer_t input_token_buffer,
1314			      const gss_channel_bindings_t input_chan_bindings,
1315			      gss_name_t * src_name,
1316			      gss_OID * mech_type,
1317			      gss_buffer_t output_token,
1318			      OM_uint32 * ret_flags,
1319			      OM_uint32 * time_rec,
1320			      gss_cred_id_t * delegated_cred_handle)
1321{
1322    return accept_sec_context(minor_status,
1323			      context_handle,
1324			      acceptor_cred_handle,
1325			      input_token_buffer,
1326			      input_chan_bindings,
1327			      src_name,
1328			      mech_type,
1329			      output_token,
1330			      ret_flags,
1331			      time_rec,
1332			      delegated_cred_handle,
1333			      GSS_IAKERB_MECHANISM,
1334			      iakerb_acceptor_start);
1335}
1336