xref: /freebsd/crypto/krb5/src/lib/gssapi/spnego/spnego_mech.c (revision 7f2fe78b9dd5f51c821d771b63d2e096f6fd49e9)
1*7f2fe78bSCy Schubert /*
2*7f2fe78bSCy Schubert  * Copyright (C) 2006,2008 by the Massachusetts Institute of Technology.
3*7f2fe78bSCy Schubert  * All rights reserved.
4*7f2fe78bSCy Schubert  *
5*7f2fe78bSCy Schubert  * Export of this software from the United States of America may
6*7f2fe78bSCy Schubert  *   require a specific license from the United States Government.
7*7f2fe78bSCy Schubert  *   It is the responsibility of any person or organization contemplating
8*7f2fe78bSCy Schubert  *   export to obtain such a license before exporting.
9*7f2fe78bSCy Schubert  *
10*7f2fe78bSCy Schubert  * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
11*7f2fe78bSCy Schubert  * distribute this software and its documentation for any purpose and
12*7f2fe78bSCy Schubert  * without fee is hereby granted, provided that the above copyright
13*7f2fe78bSCy Schubert  * notice appear in all copies and that both that copyright notice and
14*7f2fe78bSCy Schubert  * this permission notice appear in supporting documentation, and that
15*7f2fe78bSCy Schubert  * the name of M.I.T. not be used in advertising or publicity pertaining
16*7f2fe78bSCy Schubert  * to distribution of the software without specific, written prior
17*7f2fe78bSCy Schubert  * permission.  Furthermore if you modify this software you must label
18*7f2fe78bSCy Schubert  * your software as modified software and not distribute it in such a
19*7f2fe78bSCy Schubert  * fashion that it might be confused with the original M.I.T. software.
20*7f2fe78bSCy Schubert  * M.I.T. makes no representations about the suitability of
21*7f2fe78bSCy Schubert  * this software for any purpose.  It is provided "as is" without express
22*7f2fe78bSCy Schubert  * or implied warranty.
23*7f2fe78bSCy Schubert  */
24*7f2fe78bSCy Schubert /*
25*7f2fe78bSCy Schubert  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
26*7f2fe78bSCy Schubert  * Use is subject to license terms.
27*7f2fe78bSCy Schubert  *
28*7f2fe78bSCy Schubert  * A module that implements the spnego security mechanism.
29*7f2fe78bSCy Schubert  * It is used to negotiate the security mechanism between
30*7f2fe78bSCy Schubert  * peers using the GSS-API.  SPNEGO is specified in RFC 4178.
31*7f2fe78bSCy Schubert  *
32*7f2fe78bSCy Schubert  */
33*7f2fe78bSCy Schubert /*
34*7f2fe78bSCy Schubert  * Copyright (c) 2006-2008, Novell, Inc.
35*7f2fe78bSCy Schubert  * All rights reserved.
36*7f2fe78bSCy Schubert  *
37*7f2fe78bSCy Schubert  * Redistribution and use in source and binary forms, with or without
38*7f2fe78bSCy Schubert  * modification, are permitted provided that the following conditions are met:
39*7f2fe78bSCy Schubert  *
40*7f2fe78bSCy Schubert  *   * Redistributions of source code must retain the above copyright notice,
41*7f2fe78bSCy Schubert  *       this list of conditions and the following disclaimer.
42*7f2fe78bSCy Schubert  *   * Redistributions in binary form must reproduce the above copyright
43*7f2fe78bSCy Schubert  *       notice, this list of conditions and the following disclaimer in the
44*7f2fe78bSCy Schubert  *       documentation and/or other materials provided with the distribution.
45*7f2fe78bSCy Schubert  *   * The copyright holder's name is not used to endorse or promote products
46*7f2fe78bSCy Schubert  *       derived from this software without specific prior written permission.
47*7f2fe78bSCy Schubert  *
48*7f2fe78bSCy Schubert  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
49*7f2fe78bSCy Schubert  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
50*7f2fe78bSCy Schubert  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
51*7f2fe78bSCy Schubert  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
52*7f2fe78bSCy Schubert  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
53*7f2fe78bSCy Schubert  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
54*7f2fe78bSCy Schubert  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
55*7f2fe78bSCy Schubert  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
56*7f2fe78bSCy Schubert  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
57*7f2fe78bSCy Schubert  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
58*7f2fe78bSCy Schubert  * POSSIBILITY OF SUCH DAMAGE.
59*7f2fe78bSCy Schubert  */
60*7f2fe78bSCy Schubert /* #pragma ident	"@(#)spnego_mech.c	1.7	04/09/28 SMI" */
61*7f2fe78bSCy Schubert 
62*7f2fe78bSCy Schubert #include	<k5-int.h>
63*7f2fe78bSCy Schubert #include	<k5-der.h>
64*7f2fe78bSCy Schubert #include	<krb5.h>
65*7f2fe78bSCy Schubert #include	<mglueP.h>
66*7f2fe78bSCy Schubert #include	"gssapiP_spnego.h"
67*7f2fe78bSCy Schubert #include	<gssapi_err_generic.h>
68*7f2fe78bSCy Schubert 
69*7f2fe78bSCy Schubert 
70*7f2fe78bSCy Schubert #define HARD_ERROR(v) ((v) != GSS_S_COMPLETE && (v) != GSS_S_CONTINUE_NEEDED)
71*7f2fe78bSCy Schubert typedef const gss_OID_desc *gss_OID_const;
72*7f2fe78bSCy Schubert 
73*7f2fe78bSCy Schubert /* private routines for spnego_mechanism */
74*7f2fe78bSCy Schubert static spnego_token_t make_spnego_token(const char *);
75*7f2fe78bSCy Schubert static gss_buffer_desc make_err_msg(const char *);
76*7f2fe78bSCy Schubert static int verify_token_header(struct k5input *, gss_OID_const);
77*7f2fe78bSCy Schubert static gss_OID get_mech_oid(OM_uint32 *minor_status, struct k5input *);
78*7f2fe78bSCy Schubert static gss_buffer_t get_octet_string(struct k5input *);
79*7f2fe78bSCy Schubert static gss_OID_set get_mech_set(OM_uint32 *, struct k5input *);
80*7f2fe78bSCy Schubert static OM_uint32 get_req_flags(struct k5input *, OM_uint32 *);
81*7f2fe78bSCy Schubert static OM_uint32 get_available_mechs(OM_uint32 *, gss_name_t, gss_cred_usage_t,
82*7f2fe78bSCy Schubert 				     gss_const_key_value_set_t,
83*7f2fe78bSCy Schubert 				     gss_cred_id_t *, gss_OID_set *,
84*7f2fe78bSCy Schubert 				     OM_uint32 *);
85*7f2fe78bSCy Schubert static OM_uint32 get_negotiable_mechs(OM_uint32 *, spnego_gss_ctx_id_t,
86*7f2fe78bSCy Schubert 				      spnego_gss_cred_id_t, gss_cred_usage_t);
87*7f2fe78bSCy Schubert static void release_spnego_ctx(spnego_gss_ctx_id_t *);
88*7f2fe78bSCy Schubert static spnego_gss_ctx_id_t create_spnego_ctx(int);
89*7f2fe78bSCy Schubert static int put_mech_set(gss_OID_set mechSet, gss_buffer_t buf);
90*7f2fe78bSCy Schubert 
91*7f2fe78bSCy Schubert static OM_uint32
92*7f2fe78bSCy Schubert process_mic(OM_uint32 *, gss_buffer_t, spnego_gss_ctx_id_t,
93*7f2fe78bSCy Schubert 	    gss_buffer_t *, OM_uint32 *, send_token_flag *);
94*7f2fe78bSCy Schubert static OM_uint32
95*7f2fe78bSCy Schubert handle_mic(OM_uint32 *, gss_buffer_t, int, spnego_gss_ctx_id_t,
96*7f2fe78bSCy Schubert 	   gss_buffer_t *, OM_uint32 *, send_token_flag *);
97*7f2fe78bSCy Schubert 
98*7f2fe78bSCy Schubert static OM_uint32
99*7f2fe78bSCy Schubert init_ctx_new(OM_uint32 *, spnego_gss_cred_id_t, send_token_flag *,
100*7f2fe78bSCy Schubert 	     spnego_gss_ctx_id_t *);
101*7f2fe78bSCy Schubert static OM_uint32
102*7f2fe78bSCy Schubert init_ctx_nego(OM_uint32 *, spnego_gss_ctx_id_t, OM_uint32, gss_OID,
103*7f2fe78bSCy Schubert 	      gss_buffer_t *, gss_buffer_t *, send_token_flag *);
104*7f2fe78bSCy Schubert static OM_uint32
105*7f2fe78bSCy Schubert init_ctx_cont(OM_uint32 *, spnego_gss_ctx_id_t, gss_buffer_t,
106*7f2fe78bSCy Schubert 	      gss_buffer_t *, gss_buffer_t *,
107*7f2fe78bSCy Schubert 	      OM_uint32 *, send_token_flag *);
108*7f2fe78bSCy Schubert static OM_uint32
109*7f2fe78bSCy Schubert init_ctx_reselect(OM_uint32 *, spnego_gss_ctx_id_t, OM_uint32,
110*7f2fe78bSCy Schubert 		  gss_OID, gss_buffer_t *, gss_buffer_t *, send_token_flag *);
111*7f2fe78bSCy Schubert static OM_uint32
112*7f2fe78bSCy Schubert init_ctx_call_init(OM_uint32 *, spnego_gss_ctx_id_t, spnego_gss_cred_id_t,
113*7f2fe78bSCy Schubert 		   OM_uint32, gss_name_t, OM_uint32, OM_uint32, gss_buffer_t,
114*7f2fe78bSCy Schubert 		   gss_channel_bindings_t,
115*7f2fe78bSCy Schubert 		   gss_buffer_t, OM_uint32 *, send_token_flag *);
116*7f2fe78bSCy Schubert 
117*7f2fe78bSCy Schubert static OM_uint32
118*7f2fe78bSCy Schubert acc_ctx_new(OM_uint32 *, gss_buffer_t, spnego_gss_cred_id_t, gss_buffer_t *,
119*7f2fe78bSCy Schubert 	    gss_buffer_t *, OM_uint32 *, send_token_flag *,
120*7f2fe78bSCy Schubert 	    spnego_gss_ctx_id_t *);
121*7f2fe78bSCy Schubert static OM_uint32
122*7f2fe78bSCy Schubert acc_ctx_cont(OM_uint32 *, gss_buffer_t, spnego_gss_ctx_id_t, gss_buffer_t *,
123*7f2fe78bSCy Schubert 	     gss_buffer_t *, OM_uint32 *, send_token_flag *);
124*7f2fe78bSCy Schubert static OM_uint32
125*7f2fe78bSCy Schubert acc_ctx_vfy_oid(OM_uint32 *, spnego_gss_ctx_id_t, gss_OID,
126*7f2fe78bSCy Schubert 		OM_uint32 *, send_token_flag *);
127*7f2fe78bSCy Schubert static OM_uint32
128*7f2fe78bSCy Schubert acc_ctx_call_acc(OM_uint32 *, spnego_gss_ctx_id_t, spnego_gss_cred_id_t,
129*7f2fe78bSCy Schubert 		 gss_buffer_t, gss_channel_bindings_t, gss_buffer_t,
130*7f2fe78bSCy Schubert 		 OM_uint32 *, OM_uint32 *, send_token_flag *);
131*7f2fe78bSCy Schubert 
132*7f2fe78bSCy Schubert static gss_OID
133*7f2fe78bSCy Schubert negotiate_mech(spnego_gss_ctx_id_t, gss_OID_set, OM_uint32 *);
134*7f2fe78bSCy Schubert 
135*7f2fe78bSCy Schubert static int
136*7f2fe78bSCy Schubert make_spnego_tokenInit_msg(spnego_gss_ctx_id_t,
137*7f2fe78bSCy Schubert 			int,
138*7f2fe78bSCy Schubert 			gss_buffer_t,
139*7f2fe78bSCy Schubert 			OM_uint32, gss_buffer_t, send_token_flag,
140*7f2fe78bSCy Schubert 			gss_buffer_t);
141*7f2fe78bSCy Schubert static OM_uint32
142*7f2fe78bSCy Schubert make_spnego_tokenTarg_msg(uint8_t, gss_OID, gss_buffer_t,
143*7f2fe78bSCy Schubert 			gss_buffer_t, send_token_flag,
144*7f2fe78bSCy Schubert 			gss_buffer_t);
145*7f2fe78bSCy Schubert 
146*7f2fe78bSCy Schubert static OM_uint32
147*7f2fe78bSCy Schubert get_negTokenInit(OM_uint32 *, gss_buffer_t, gss_buffer_t,
148*7f2fe78bSCy Schubert 		 gss_OID_set *, OM_uint32 *, gss_buffer_t *,
149*7f2fe78bSCy Schubert 		 gss_buffer_t *);
150*7f2fe78bSCy Schubert static OM_uint32
151*7f2fe78bSCy Schubert get_negTokenResp(OM_uint32 *, struct k5input *, OM_uint32 *, gss_OID *,
152*7f2fe78bSCy Schubert 		 gss_buffer_t *, gss_buffer_t *);
153*7f2fe78bSCy Schubert 
154*7f2fe78bSCy Schubert static int
155*7f2fe78bSCy Schubert is_kerb_mech(gss_OID oid);
156*7f2fe78bSCy Schubert 
157*7f2fe78bSCy Schubert /* SPNEGO oid structure */
158*7f2fe78bSCy Schubert static const gss_OID_desc spnego_oids[] = {
159*7f2fe78bSCy Schubert 	{SPNEGO_OID_LENGTH, SPNEGO_OID},
160*7f2fe78bSCy Schubert };
161*7f2fe78bSCy Schubert 
162*7f2fe78bSCy Schubert const gss_OID_desc * const gss_mech_spnego = spnego_oids+0;
163*7f2fe78bSCy Schubert static const gss_OID_set_desc spnego_oidsets[] = {
164*7f2fe78bSCy Schubert 	{1, (gss_OID) spnego_oids+0},
165*7f2fe78bSCy Schubert };
166*7f2fe78bSCy Schubert const gss_OID_set_desc * const gss_mech_set_spnego = spnego_oidsets+0;
167*7f2fe78bSCy Schubert 
168*7f2fe78bSCy Schubert static gss_OID_desc negoex_mech = { NEGOEX_OID_LENGTH, NEGOEX_OID };
169*7f2fe78bSCy Schubert 
170*7f2fe78bSCy Schubert static int make_NegHints(OM_uint32 *, gss_buffer_t *);
171*7f2fe78bSCy Schubert static OM_uint32
172*7f2fe78bSCy Schubert acc_ctx_hints(OM_uint32 *, spnego_gss_cred_id_t, gss_buffer_t *, OM_uint32 *,
173*7f2fe78bSCy Schubert 	      send_token_flag *, spnego_gss_ctx_id_t *);
174*7f2fe78bSCy Schubert 
175*7f2fe78bSCy Schubert /*
176*7f2fe78bSCy Schubert  * The Mech OID for SPNEGO:
177*7f2fe78bSCy Schubert  * { iso(1) org(3) dod(6) internet(1) security(5)
178*7f2fe78bSCy Schubert  *  mechanism(5) spnego(2) }
179*7f2fe78bSCy Schubert  */
180*7f2fe78bSCy Schubert static struct gss_config spnego_mechanism =
181*7f2fe78bSCy Schubert {
182*7f2fe78bSCy Schubert 	{SPNEGO_OID_LENGTH, SPNEGO_OID},
183*7f2fe78bSCy Schubert 	NULL,
184*7f2fe78bSCy Schubert 	spnego_gss_acquire_cred,
185*7f2fe78bSCy Schubert 	spnego_gss_release_cred,
186*7f2fe78bSCy Schubert 	spnego_gss_init_sec_context,
187*7f2fe78bSCy Schubert #ifndef LEAN_CLIENT
188*7f2fe78bSCy Schubert 	spnego_gss_accept_sec_context,
189*7f2fe78bSCy Schubert #else
190*7f2fe78bSCy Schubert 	NULL,
191*7f2fe78bSCy Schubert #endif  /* LEAN_CLIENT */
192*7f2fe78bSCy Schubert 	NULL,				/* gss_process_context_token */
193*7f2fe78bSCy Schubert 	spnego_gss_delete_sec_context,	/* gss_delete_sec_context */
194*7f2fe78bSCy Schubert 	spnego_gss_context_time,	/* gss_context_time */
195*7f2fe78bSCy Schubert 	spnego_gss_get_mic,		/* gss_get_mic */
196*7f2fe78bSCy Schubert 	spnego_gss_verify_mic,		/* gss_verify_mic */
197*7f2fe78bSCy Schubert 	spnego_gss_wrap,		/* gss_wrap */
198*7f2fe78bSCy Schubert 	spnego_gss_unwrap,		/* gss_unwrap */
199*7f2fe78bSCy Schubert 	spnego_gss_display_status,
200*7f2fe78bSCy Schubert 	NULL,				/* gss_indicate_mechs */
201*7f2fe78bSCy Schubert 	spnego_gss_compare_name,
202*7f2fe78bSCy Schubert 	spnego_gss_display_name,
203*7f2fe78bSCy Schubert 	spnego_gss_import_name,
204*7f2fe78bSCy Schubert 	spnego_gss_release_name,
205*7f2fe78bSCy Schubert 	spnego_gss_inquire_cred,	/* gss_inquire_cred */
206*7f2fe78bSCy Schubert 	NULL,				/* gss_add_cred */
207*7f2fe78bSCy Schubert #ifndef LEAN_CLIENT
208*7f2fe78bSCy Schubert 	spnego_gss_export_sec_context,		/* gss_export_sec_context */
209*7f2fe78bSCy Schubert 	spnego_gss_import_sec_context,		/* gss_import_sec_context */
210*7f2fe78bSCy Schubert #else
211*7f2fe78bSCy Schubert 	NULL,				/* gss_export_sec_context */
212*7f2fe78bSCy Schubert 	NULL,				/* gss_import_sec_context */
213*7f2fe78bSCy Schubert #endif /* LEAN_CLIENT */
214*7f2fe78bSCy Schubert 	NULL, 				/* gss_inquire_cred_by_mech */
215*7f2fe78bSCy Schubert 	spnego_gss_inquire_names_for_mech,
216*7f2fe78bSCy Schubert 	spnego_gss_inquire_context,	/* gss_inquire_context */
217*7f2fe78bSCy Schubert 	NULL,				/* gss_internal_release_oid */
218*7f2fe78bSCy Schubert 	spnego_gss_wrap_size_limit,	/* gss_wrap_size_limit */
219*7f2fe78bSCy Schubert 	spnego_gss_localname,
220*7f2fe78bSCy Schubert 	NULL,				/* gss_userok */
221*7f2fe78bSCy Schubert 	NULL,				/* gss_export_name */
222*7f2fe78bSCy Schubert 	spnego_gss_duplicate_name,	/* gss_duplicate_name */
223*7f2fe78bSCy Schubert 	NULL,				/* gss_store_cred */
224*7f2fe78bSCy Schubert  	spnego_gss_inquire_sec_context_by_oid, /* gss_inquire_sec_context_by_oid */
225*7f2fe78bSCy Schubert  	spnego_gss_inquire_cred_by_oid,	/* gss_inquire_cred_by_oid */
226*7f2fe78bSCy Schubert  	spnego_gss_set_sec_context_option, /* gss_set_sec_context_option */
227*7f2fe78bSCy Schubert 	spnego_gss_set_cred_option,	/* gssspi_set_cred_option */
228*7f2fe78bSCy Schubert  	NULL,				/* gssspi_mech_invoke */
229*7f2fe78bSCy Schubert 	spnego_gss_wrap_aead,
230*7f2fe78bSCy Schubert 	spnego_gss_unwrap_aead,
231*7f2fe78bSCy Schubert 	spnego_gss_wrap_iov,
232*7f2fe78bSCy Schubert 	spnego_gss_unwrap_iov,
233*7f2fe78bSCy Schubert 	spnego_gss_wrap_iov_length,
234*7f2fe78bSCy Schubert 	spnego_gss_complete_auth_token,
235*7f2fe78bSCy Schubert 	spnego_gss_acquire_cred_impersonate_name,
236*7f2fe78bSCy Schubert 	NULL,				/* gss_add_cred_impersonate_name */
237*7f2fe78bSCy Schubert 	spnego_gss_display_name_ext,
238*7f2fe78bSCy Schubert 	spnego_gss_inquire_name,
239*7f2fe78bSCy Schubert 	spnego_gss_get_name_attribute,
240*7f2fe78bSCy Schubert 	spnego_gss_set_name_attribute,
241*7f2fe78bSCy Schubert 	spnego_gss_delete_name_attribute,
242*7f2fe78bSCy Schubert 	spnego_gss_export_name_composite,
243*7f2fe78bSCy Schubert 	spnego_gss_map_name_to_any,
244*7f2fe78bSCy Schubert 	spnego_gss_release_any_name_mapping,
245*7f2fe78bSCy Schubert 	spnego_gss_pseudo_random,
246*7f2fe78bSCy Schubert 	spnego_gss_set_neg_mechs,
247*7f2fe78bSCy Schubert 	spnego_gss_inquire_saslname_for_mech,
248*7f2fe78bSCy Schubert 	spnego_gss_inquire_mech_for_saslname,
249*7f2fe78bSCy Schubert 	spnego_gss_inquire_attrs_for_mech,
250*7f2fe78bSCy Schubert 	spnego_gss_acquire_cred_from,
251*7f2fe78bSCy Schubert 	NULL,				/* gss_store_cred_into */
252*7f2fe78bSCy Schubert 	spnego_gss_acquire_cred_with_password,
253*7f2fe78bSCy Schubert 	spnego_gss_export_cred,
254*7f2fe78bSCy Schubert 	spnego_gss_import_cred,
255*7f2fe78bSCy Schubert 	NULL,				/* gssspi_import_sec_context_by_mech */
256*7f2fe78bSCy Schubert 	NULL,				/* gssspi_import_name_by_mech */
257*7f2fe78bSCy Schubert 	NULL,				/* gssspi_import_cred_by_mech */
258*7f2fe78bSCy Schubert 	spnego_gss_get_mic_iov,
259*7f2fe78bSCy Schubert 	spnego_gss_verify_mic_iov,
260*7f2fe78bSCy Schubert 	spnego_gss_get_mic_iov_length
261*7f2fe78bSCy Schubert };
262*7f2fe78bSCy Schubert 
263*7f2fe78bSCy Schubert #ifdef _GSS_STATIC_LINK
264*7f2fe78bSCy Schubert #include "mglueP.h"
265*7f2fe78bSCy Schubert 
gss_spnegomechglue_init(void)266*7f2fe78bSCy Schubert static int gss_spnegomechglue_init(void)
267*7f2fe78bSCy Schubert {
268*7f2fe78bSCy Schubert 	struct gss_mech_config mech_spnego;
269*7f2fe78bSCy Schubert 
270*7f2fe78bSCy Schubert 	memset(&mech_spnego, 0, sizeof(mech_spnego));
271*7f2fe78bSCy Schubert 	mech_spnego.mech = &spnego_mechanism;
272*7f2fe78bSCy Schubert 	mech_spnego.mechNameStr = "spnego";
273*7f2fe78bSCy Schubert 	mech_spnego.mech_type = GSS_C_NO_OID;
274*7f2fe78bSCy Schubert 
275*7f2fe78bSCy Schubert 	return gssint_register_mechinfo(&mech_spnego);
276*7f2fe78bSCy Schubert }
277*7f2fe78bSCy Schubert #else
278*7f2fe78bSCy Schubert gss_mechanism KRB5_CALLCONV
gss_mech_initialize(void)279*7f2fe78bSCy Schubert gss_mech_initialize(void)
280*7f2fe78bSCy Schubert {
281*7f2fe78bSCy Schubert 	return (&spnego_mechanism);
282*7f2fe78bSCy Schubert }
283*7f2fe78bSCy Schubert 
284*7f2fe78bSCy Schubert MAKE_INIT_FUNCTION(gss_krb5int_lib_init);
285*7f2fe78bSCy Schubert MAKE_FINI_FUNCTION(gss_krb5int_lib_fini);
286*7f2fe78bSCy Schubert int gss_krb5int_lib_init(void);
287*7f2fe78bSCy Schubert #endif /* _GSS_STATIC_LINK */
288*7f2fe78bSCy Schubert 
gss_spnegoint_lib_init(void)289*7f2fe78bSCy Schubert int gss_spnegoint_lib_init(void)
290*7f2fe78bSCy Schubert {
291*7f2fe78bSCy Schubert 	int err;
292*7f2fe78bSCy Schubert 
293*7f2fe78bSCy Schubert 	err = k5_key_register(K5_KEY_GSS_SPNEGO_STATUS, NULL);
294*7f2fe78bSCy Schubert 	if (err)
295*7f2fe78bSCy Schubert 		return err;
296*7f2fe78bSCy Schubert 
297*7f2fe78bSCy Schubert #ifdef _GSS_STATIC_LINK
298*7f2fe78bSCy Schubert 	return gss_spnegomechglue_init();
299*7f2fe78bSCy Schubert #else
300*7f2fe78bSCy Schubert 	return 0;
301*7f2fe78bSCy Schubert #endif
302*7f2fe78bSCy Schubert }
303*7f2fe78bSCy Schubert 
gss_spnegoint_lib_fini(void)304*7f2fe78bSCy Schubert void gss_spnegoint_lib_fini(void)
305*7f2fe78bSCy Schubert {
306*7f2fe78bSCy Schubert 	k5_key_delete(K5_KEY_GSS_SPNEGO_STATUS);
307*7f2fe78bSCy Schubert }
308*7f2fe78bSCy Schubert 
309*7f2fe78bSCy Schubert static OM_uint32
create_spnego_cred(OM_uint32 * minor_status,gss_cred_id_t mcred,spnego_gss_cred_id_t * cred_out)310*7f2fe78bSCy Schubert create_spnego_cred(OM_uint32 *minor_status, gss_cred_id_t mcred,
311*7f2fe78bSCy Schubert 		   spnego_gss_cred_id_t *cred_out)
312*7f2fe78bSCy Schubert {
313*7f2fe78bSCy Schubert 	spnego_gss_cred_id_t spcred;
314*7f2fe78bSCy Schubert 
315*7f2fe78bSCy Schubert 	*cred_out = NULL;
316*7f2fe78bSCy Schubert 	spcred = calloc(1, sizeof(*spcred));
317*7f2fe78bSCy Schubert 	if (spcred == NULL) {
318*7f2fe78bSCy Schubert 		*minor_status = ENOMEM;
319*7f2fe78bSCy Schubert 		return GSS_S_FAILURE;
320*7f2fe78bSCy Schubert 	}
321*7f2fe78bSCy Schubert 	spcred->mcred = mcred;
322*7f2fe78bSCy Schubert 	*cred_out = spcred;
323*7f2fe78bSCy Schubert 	return GSS_S_COMPLETE;
324*7f2fe78bSCy Schubert }
325*7f2fe78bSCy Schubert 
326*7f2fe78bSCy Schubert /*ARGSUSED*/
327*7f2fe78bSCy Schubert OM_uint32 KRB5_CALLCONV
spnego_gss_acquire_cred(OM_uint32 * minor_status,gss_name_t desired_name,OM_uint32 time_req,gss_OID_set desired_mechs,gss_cred_usage_t cred_usage,gss_cred_id_t * output_cred_handle,gss_OID_set * actual_mechs,OM_uint32 * time_rec)328*7f2fe78bSCy Schubert spnego_gss_acquire_cred(OM_uint32 *minor_status,
329*7f2fe78bSCy Schubert 			gss_name_t desired_name,
330*7f2fe78bSCy Schubert 			OM_uint32 time_req,
331*7f2fe78bSCy Schubert 			gss_OID_set desired_mechs,
332*7f2fe78bSCy Schubert 			gss_cred_usage_t cred_usage,
333*7f2fe78bSCy Schubert 			gss_cred_id_t *output_cred_handle,
334*7f2fe78bSCy Schubert 			gss_OID_set *actual_mechs,
335*7f2fe78bSCy Schubert 			OM_uint32 *time_rec)
336*7f2fe78bSCy Schubert {
337*7f2fe78bSCy Schubert     return spnego_gss_acquire_cred_from(minor_status, desired_name, time_req,
338*7f2fe78bSCy Schubert 					desired_mechs, cred_usage, NULL,
339*7f2fe78bSCy Schubert 					output_cred_handle, actual_mechs,
340*7f2fe78bSCy Schubert 					time_rec);
341*7f2fe78bSCy Schubert }
342*7f2fe78bSCy Schubert 
343*7f2fe78bSCy Schubert /*ARGSUSED*/
344*7f2fe78bSCy Schubert OM_uint32 KRB5_CALLCONV
spnego_gss_acquire_cred_from(OM_uint32 * minor_status,const gss_name_t desired_name,OM_uint32 time_req,const gss_OID_set desired_mechs,gss_cred_usage_t cred_usage,gss_const_key_value_set_t cred_store,gss_cred_id_t * output_cred_handle,gss_OID_set * actual_mechs,OM_uint32 * time_rec)345*7f2fe78bSCy Schubert spnego_gss_acquire_cred_from(OM_uint32 *minor_status,
346*7f2fe78bSCy Schubert 			     const gss_name_t desired_name,
347*7f2fe78bSCy Schubert 			     OM_uint32 time_req,
348*7f2fe78bSCy Schubert 			     const gss_OID_set desired_mechs,
349*7f2fe78bSCy Schubert 			     gss_cred_usage_t cred_usage,
350*7f2fe78bSCy Schubert 			     gss_const_key_value_set_t cred_store,
351*7f2fe78bSCy Schubert 			     gss_cred_id_t *output_cred_handle,
352*7f2fe78bSCy Schubert 			     gss_OID_set *actual_mechs,
353*7f2fe78bSCy Schubert 			     OM_uint32 *time_rec)
354*7f2fe78bSCy Schubert {
355*7f2fe78bSCy Schubert 	OM_uint32 status, tmpmin;
356*7f2fe78bSCy Schubert 	gss_OID_set amechs;
357*7f2fe78bSCy Schubert 	gss_cred_id_t mcred = NULL;
358*7f2fe78bSCy Schubert 	spnego_gss_cred_id_t spcred = NULL;
359*7f2fe78bSCy Schubert 	dsyslog("Entering spnego_gss_acquire_cred\n");
360*7f2fe78bSCy Schubert 
361*7f2fe78bSCy Schubert 	if (actual_mechs)
362*7f2fe78bSCy Schubert 		*actual_mechs = NULL;
363*7f2fe78bSCy Schubert 
364*7f2fe78bSCy Schubert 	if (time_rec)
365*7f2fe78bSCy Schubert 		*time_rec = 0;
366*7f2fe78bSCy Schubert 
367*7f2fe78bSCy Schubert 	/* We will obtain a mechglue credential and wrap it in a
368*7f2fe78bSCy Schubert 	 * spnego_gss_cred_id_rec structure.  Allocate the wrapper. */
369*7f2fe78bSCy Schubert 	status = create_spnego_cred(minor_status, mcred, &spcred);
370*7f2fe78bSCy Schubert 	if (status != GSS_S_COMPLETE)
371*7f2fe78bSCy Schubert 		return (status);
372*7f2fe78bSCy Schubert 
373*7f2fe78bSCy Schubert 	/*
374*7f2fe78bSCy Schubert 	 * Always use get_available_mechs to collect a list of
375*7f2fe78bSCy Schubert 	 * mechs for which creds are available.
376*7f2fe78bSCy Schubert 	 */
377*7f2fe78bSCy Schubert 	status = get_available_mechs(minor_status, desired_name,
378*7f2fe78bSCy Schubert 				     cred_usage, cred_store, &mcred,
379*7f2fe78bSCy Schubert 				     &amechs, time_rec);
380*7f2fe78bSCy Schubert 
381*7f2fe78bSCy Schubert 	if (actual_mechs && amechs != GSS_C_NULL_OID_SET) {
382*7f2fe78bSCy Schubert 		(void) gssint_copy_oid_set(&tmpmin, amechs, actual_mechs);
383*7f2fe78bSCy Schubert 	}
384*7f2fe78bSCy Schubert 	(void) gss_release_oid_set(&tmpmin, &amechs);
385*7f2fe78bSCy Schubert 
386*7f2fe78bSCy Schubert 	if (status == GSS_S_COMPLETE) {
387*7f2fe78bSCy Schubert 		spcred->mcred = mcred;
388*7f2fe78bSCy Schubert 		*output_cred_handle = (gss_cred_id_t)spcred;
389*7f2fe78bSCy Schubert 	} else {
390*7f2fe78bSCy Schubert 		free(spcred);
391*7f2fe78bSCy Schubert 		*output_cred_handle = GSS_C_NO_CREDENTIAL;
392*7f2fe78bSCy Schubert 	}
393*7f2fe78bSCy Schubert 
394*7f2fe78bSCy Schubert 	dsyslog("Leaving spnego_gss_acquire_cred\n");
395*7f2fe78bSCy Schubert 	return (status);
396*7f2fe78bSCy Schubert }
397*7f2fe78bSCy Schubert 
398*7f2fe78bSCy Schubert /*ARGSUSED*/
399*7f2fe78bSCy Schubert OM_uint32 KRB5_CALLCONV
spnego_gss_release_cred(OM_uint32 * minor_status,gss_cred_id_t * cred_handle)400*7f2fe78bSCy Schubert spnego_gss_release_cred(OM_uint32 *minor_status,
401*7f2fe78bSCy Schubert 			gss_cred_id_t *cred_handle)
402*7f2fe78bSCy Schubert {
403*7f2fe78bSCy Schubert 	spnego_gss_cred_id_t spcred = NULL;
404*7f2fe78bSCy Schubert 
405*7f2fe78bSCy Schubert 	dsyslog("Entering spnego_gss_release_cred\n");
406*7f2fe78bSCy Schubert 
407*7f2fe78bSCy Schubert 	if (minor_status == NULL || cred_handle == NULL)
408*7f2fe78bSCy Schubert 		return (GSS_S_CALL_INACCESSIBLE_WRITE);
409*7f2fe78bSCy Schubert 
410*7f2fe78bSCy Schubert 	*minor_status = 0;
411*7f2fe78bSCy Schubert 
412*7f2fe78bSCy Schubert 	if (*cred_handle == GSS_C_NO_CREDENTIAL)
413*7f2fe78bSCy Schubert 		return (GSS_S_COMPLETE);
414*7f2fe78bSCy Schubert 
415*7f2fe78bSCy Schubert 	spcred = (spnego_gss_cred_id_t)*cred_handle;
416*7f2fe78bSCy Schubert 	*cred_handle = GSS_C_NO_CREDENTIAL;
417*7f2fe78bSCy Schubert 	gss_release_oid_set(minor_status, &spcred->neg_mechs);
418*7f2fe78bSCy Schubert 	gss_release_cred(minor_status, &spcred->mcred);
419*7f2fe78bSCy Schubert 	free(spcred);
420*7f2fe78bSCy Schubert 
421*7f2fe78bSCy Schubert 	dsyslog("Leaving spnego_gss_release_cred\n");
422*7f2fe78bSCy Schubert 	return (GSS_S_COMPLETE);
423*7f2fe78bSCy Schubert }
424*7f2fe78bSCy Schubert 
425*7f2fe78bSCy Schubert static spnego_gss_ctx_id_t
create_spnego_ctx(int initiate)426*7f2fe78bSCy Schubert create_spnego_ctx(int initiate)
427*7f2fe78bSCy Schubert {
428*7f2fe78bSCy Schubert 	spnego_gss_ctx_id_t spnego_ctx = NULL;
429*7f2fe78bSCy Schubert 
430*7f2fe78bSCy Schubert 	spnego_ctx = malloc(sizeof(*spnego_ctx));
431*7f2fe78bSCy Schubert 	if (spnego_ctx == NULL) {
432*7f2fe78bSCy Schubert 		return (NULL);
433*7f2fe78bSCy Schubert 	}
434*7f2fe78bSCy Schubert 
435*7f2fe78bSCy Schubert 	spnego_ctx->magic_num = SPNEGO_MAGIC_ID;
436*7f2fe78bSCy Schubert 	spnego_ctx->ctx_handle = GSS_C_NO_CONTEXT;
437*7f2fe78bSCy Schubert 	spnego_ctx->mech_set = NULL;
438*7f2fe78bSCy Schubert 	spnego_ctx->internal_mech = NULL;
439*7f2fe78bSCy Schubert 	spnego_ctx->DER_mechTypes.length = 0;
440*7f2fe78bSCy Schubert 	spnego_ctx->DER_mechTypes.value = NULL;
441*7f2fe78bSCy Schubert 	spnego_ctx->mic_reqd = 0;
442*7f2fe78bSCy Schubert 	spnego_ctx->mic_sent = 0;
443*7f2fe78bSCy Schubert 	spnego_ctx->mic_rcvd = 0;
444*7f2fe78bSCy Schubert 	spnego_ctx->mech_complete = 0;
445*7f2fe78bSCy Schubert 	spnego_ctx->nego_done = 0;
446*7f2fe78bSCy Schubert 	spnego_ctx->opened = 0;
447*7f2fe78bSCy Schubert 	spnego_ctx->initiate = initiate;
448*7f2fe78bSCy Schubert 	spnego_ctx->internal_name = GSS_C_NO_NAME;
449*7f2fe78bSCy Schubert 	spnego_ctx->actual_mech = GSS_C_NO_OID;
450*7f2fe78bSCy Schubert 	spnego_ctx->deleg_cred = GSS_C_NO_CREDENTIAL;
451*7f2fe78bSCy Schubert 	spnego_ctx->negoex_step = 0;
452*7f2fe78bSCy Schubert 	memset(&spnego_ctx->negoex_transcript, 0, sizeof(struct k5buf));
453*7f2fe78bSCy Schubert 	spnego_ctx->negoex_seqnum = 0;
454*7f2fe78bSCy Schubert 	K5_TAILQ_INIT(&spnego_ctx->negoex_mechs);
455*7f2fe78bSCy Schubert 	spnego_ctx->kctx = NULL;
456*7f2fe78bSCy Schubert 	memset(spnego_ctx->negoex_conv_id, 0, GUID_LENGTH);
457*7f2fe78bSCy Schubert 
458*7f2fe78bSCy Schubert 	return (spnego_ctx);
459*7f2fe78bSCy Schubert }
460*7f2fe78bSCy Schubert 
461*7f2fe78bSCy Schubert /* iso(1) org(3) dod(6) internet(1) private(4) enterprises(1) samba(7165)
462*7f2fe78bSCy Schubert  * gssntlmssp(655) controls(1) spnego_req_mechlistMIC(2) */
463*7f2fe78bSCy Schubert static const gss_OID_desc spnego_req_mechlistMIC_oid =
464*7f2fe78bSCy Schubert 	{ 11, "\x2B\x06\x01\x04\x01\xB7\x7D\x85\x0F\x01\x02" };
465*7f2fe78bSCy Schubert 
466*7f2fe78bSCy Schubert /*
467*7f2fe78bSCy Schubert  * Return nonzero if the mechanism has reason to believe that a mechlistMIC
468*7f2fe78bSCy Schubert  * exchange will be required.  Microsoft servers erroneously require SPNEGO
469*7f2fe78bSCy Schubert  * mechlistMIC if they see an internal MIC within an NTLMSSP Authenticate
470*7f2fe78bSCy Schubert  * message, even if NTLMSSP was the preferred mechanism.
471*7f2fe78bSCy Schubert  */
472*7f2fe78bSCy Schubert static int
mech_requires_mechlistMIC(spnego_gss_ctx_id_t sc)473*7f2fe78bSCy Schubert mech_requires_mechlistMIC(spnego_gss_ctx_id_t sc)
474*7f2fe78bSCy Schubert {
475*7f2fe78bSCy Schubert 	OM_uint32 major, minor;
476*7f2fe78bSCy Schubert 	gss_ctx_id_t ctx = sc->ctx_handle;
477*7f2fe78bSCy Schubert 	gss_OID oid = (gss_OID)&spnego_req_mechlistMIC_oid;
478*7f2fe78bSCy Schubert 	gss_buffer_set_t bufs;
479*7f2fe78bSCy Schubert 	int result;
480*7f2fe78bSCy Schubert 
481*7f2fe78bSCy Schubert 	major = gss_inquire_sec_context_by_oid(&minor, ctx, oid, &bufs);
482*7f2fe78bSCy Schubert 	if (major != GSS_S_COMPLETE)
483*7f2fe78bSCy Schubert 		return 0;
484*7f2fe78bSCy Schubert 
485*7f2fe78bSCy Schubert 	/* Report true if the mech returns a single buffer containing a single
486*7f2fe78bSCy Schubert 	 * byte with value 1. */
487*7f2fe78bSCy Schubert 	result = (bufs != NULL && bufs->count == 1 &&
488*7f2fe78bSCy Schubert 		  bufs->elements[0].length == 1 &&
489*7f2fe78bSCy Schubert 		  memcmp(bufs->elements[0].value, "\1", 1) == 0);
490*7f2fe78bSCy Schubert 	(void) gss_release_buffer_set(&minor, &bufs);
491*7f2fe78bSCy Schubert 	return result;
492*7f2fe78bSCy Schubert }
493*7f2fe78bSCy Schubert 
494*7f2fe78bSCy Schubert /* iso(1) org(3) dod(6) internet(1) private(4) enterprises(1) Microsoft(311)
495*7f2fe78bSCy Schubert  * security(2) mechanisms(2) NTLM(10) */
496*7f2fe78bSCy Schubert static const gss_OID_desc gss_mech_ntlmssp_oid =
497*7f2fe78bSCy Schubert 	{ 10, "\x2b\x06\x01\x04\x01\x82\x37\x02\x02\x0a" };
498*7f2fe78bSCy Schubert 
499*7f2fe78bSCy Schubert /* iso(1) org(3) dod(6) internet(1) private(4) enterprises(1) samba(7165)
500*7f2fe78bSCy Schubert  * gssntlmssp(655) controls(1) ntlmssp_reset_crypto(3) */
501*7f2fe78bSCy Schubert static const gss_OID_desc ntlmssp_reset_crypto_oid =
502*7f2fe78bSCy Schubert 	{ 11, "\x2B\x06\x01\x04\x01\xB7\x7D\x85\x0F\x01\x03" };
503*7f2fe78bSCy Schubert 
504*7f2fe78bSCy Schubert /*
505*7f2fe78bSCy Schubert  * MS-SPNG section 3.3.5.1 warns that the NTLM mechanism requires special
506*7f2fe78bSCy Schubert  * handling of the crypto state to interop with Windows.  If the mechanism for
507*7f2fe78bSCy Schubert  * sc is SPNEGO, invoke a mechanism-specific operation on the context to reset
508*7f2fe78bSCy Schubert  * the RC4 state after producing or verifying a MIC.  Ignore a result of
509*7f2fe78bSCy Schubert  * GSS_S_UNAVAILABLE for compatibility with older versions of the mechanism
510*7f2fe78bSCy Schubert  * that do not support this functionality.
511*7f2fe78bSCy Schubert  */
512*7f2fe78bSCy Schubert static OM_uint32
ntlmssp_reset_crypto_state(OM_uint32 * minor_status,spnego_gss_ctx_id_t sc,OM_uint32 verify)513*7f2fe78bSCy Schubert ntlmssp_reset_crypto_state(OM_uint32 *minor_status, spnego_gss_ctx_id_t sc,
514*7f2fe78bSCy Schubert 			   OM_uint32 verify)
515*7f2fe78bSCy Schubert {
516*7f2fe78bSCy Schubert 	OM_uint32 major, minor;
517*7f2fe78bSCy Schubert 	gss_buffer_desc value;
518*7f2fe78bSCy Schubert 
519*7f2fe78bSCy Schubert 	if (!g_OID_equal(sc->internal_mech, &gss_mech_ntlmssp_oid))
520*7f2fe78bSCy Schubert 		return GSS_S_COMPLETE;
521*7f2fe78bSCy Schubert 
522*7f2fe78bSCy Schubert 	value.length = sizeof(verify);
523*7f2fe78bSCy Schubert 	value.value = &verify;
524*7f2fe78bSCy Schubert 	major = gss_set_sec_context_option(&minor, &sc->ctx_handle,
525*7f2fe78bSCy Schubert 					   (gss_OID)&ntlmssp_reset_crypto_oid,
526*7f2fe78bSCy Schubert 					   &value);
527*7f2fe78bSCy Schubert 	if (major == GSS_S_UNAVAILABLE)
528*7f2fe78bSCy Schubert 		return GSS_S_COMPLETE;
529*7f2fe78bSCy Schubert 	*minor_status = minor;
530*7f2fe78bSCy Schubert 	return major;
531*7f2fe78bSCy Schubert }
532*7f2fe78bSCy Schubert 
533*7f2fe78bSCy Schubert /*
534*7f2fe78bSCy Schubert  * Both initiator and acceptor call here to verify and/or create mechListMIC,
535*7f2fe78bSCy Schubert  * and to consistency-check the MIC state.  handle_mic is invoked only if the
536*7f2fe78bSCy Schubert  * negotiated mech has completed and supports MICs.
537*7f2fe78bSCy Schubert  */
538*7f2fe78bSCy Schubert static OM_uint32
handle_mic(OM_uint32 * minor_status,gss_buffer_t mic_in,int send_mechtok,spnego_gss_ctx_id_t sc,gss_buffer_t * mic_out,OM_uint32 * negState,send_token_flag * tokflag)539*7f2fe78bSCy Schubert handle_mic(OM_uint32 *minor_status, gss_buffer_t mic_in,
540*7f2fe78bSCy Schubert 	   int send_mechtok, spnego_gss_ctx_id_t sc,
541*7f2fe78bSCy Schubert 	   gss_buffer_t *mic_out,
542*7f2fe78bSCy Schubert 	   OM_uint32 *negState, send_token_flag *tokflag)
543*7f2fe78bSCy Schubert {
544*7f2fe78bSCy Schubert 	OM_uint32 ret;
545*7f2fe78bSCy Schubert 
546*7f2fe78bSCy Schubert 	ret = GSS_S_FAILURE;
547*7f2fe78bSCy Schubert 	*mic_out = GSS_C_NO_BUFFER;
548*7f2fe78bSCy Schubert 	if (mic_in != GSS_C_NO_BUFFER) {
549*7f2fe78bSCy Schubert 		if (sc->mic_rcvd) {
550*7f2fe78bSCy Schubert 			/* Reject MIC if we've already received a MIC. */
551*7f2fe78bSCy Schubert 			*negState = REJECT;
552*7f2fe78bSCy Schubert 			*tokflag = ERROR_TOKEN_SEND;
553*7f2fe78bSCy Schubert 			return GSS_S_DEFECTIVE_TOKEN;
554*7f2fe78bSCy Schubert 		}
555*7f2fe78bSCy Schubert 	} else if (sc->mic_reqd && !send_mechtok) {
556*7f2fe78bSCy Schubert 		/*
557*7f2fe78bSCy Schubert 		 * If the peer sends the final mechanism token, it
558*7f2fe78bSCy Schubert 		 * must send the MIC with that token if the
559*7f2fe78bSCy Schubert 		 * negotiation requires MICs.
560*7f2fe78bSCy Schubert 		 */
561*7f2fe78bSCy Schubert 		*negState = REJECT;
562*7f2fe78bSCy Schubert 		*tokflag = ERROR_TOKEN_SEND;
563*7f2fe78bSCy Schubert 		return GSS_S_DEFECTIVE_TOKEN;
564*7f2fe78bSCy Schubert 	}
565*7f2fe78bSCy Schubert 	ret = process_mic(minor_status, mic_in, sc, mic_out,
566*7f2fe78bSCy Schubert 			  negState, tokflag);
567*7f2fe78bSCy Schubert 	if (ret != GSS_S_COMPLETE) {
568*7f2fe78bSCy Schubert 		return ret;
569*7f2fe78bSCy Schubert 	}
570*7f2fe78bSCy Schubert 	if (sc->mic_reqd) {
571*7f2fe78bSCy Schubert 		assert(sc->mic_sent || sc->mic_rcvd);
572*7f2fe78bSCy Schubert 	}
573*7f2fe78bSCy Schubert 	if (sc->mic_sent && sc->mic_rcvd) {
574*7f2fe78bSCy Schubert 		ret = GSS_S_COMPLETE;
575*7f2fe78bSCy Schubert 		*negState = ACCEPT_COMPLETE;
576*7f2fe78bSCy Schubert 		if (*mic_out == GSS_C_NO_BUFFER) {
577*7f2fe78bSCy Schubert 			/*
578*7f2fe78bSCy Schubert 			 * We sent a MIC on the previous pass; we
579*7f2fe78bSCy Schubert 			 * shouldn't be sending a mechanism token.
580*7f2fe78bSCy Schubert 			 */
581*7f2fe78bSCy Schubert 			assert(!send_mechtok);
582*7f2fe78bSCy Schubert 			*tokflag = NO_TOKEN_SEND;
583*7f2fe78bSCy Schubert 		} else {
584*7f2fe78bSCy Schubert 			*tokflag = CONT_TOKEN_SEND;
585*7f2fe78bSCy Schubert 		}
586*7f2fe78bSCy Schubert 	} else if (sc->mic_reqd) {
587*7f2fe78bSCy Schubert 		*negState = ACCEPT_INCOMPLETE;
588*7f2fe78bSCy Schubert 		ret = GSS_S_CONTINUE_NEEDED;
589*7f2fe78bSCy Schubert 	} else if (*negState == ACCEPT_COMPLETE) {
590*7f2fe78bSCy Schubert 		ret = GSS_S_COMPLETE;
591*7f2fe78bSCy Schubert 	} else {
592*7f2fe78bSCy Schubert 		ret = GSS_S_CONTINUE_NEEDED;
593*7f2fe78bSCy Schubert 	}
594*7f2fe78bSCy Schubert 	return ret;
595*7f2fe78bSCy Schubert }
596*7f2fe78bSCy Schubert 
597*7f2fe78bSCy Schubert /*
598*7f2fe78bSCy Schubert  * Perform the actual verification and/or generation of mechListMIC.
599*7f2fe78bSCy Schubert  */
600*7f2fe78bSCy Schubert static OM_uint32
process_mic(OM_uint32 * minor_status,gss_buffer_t mic_in,spnego_gss_ctx_id_t sc,gss_buffer_t * mic_out,OM_uint32 * negState,send_token_flag * tokflag)601*7f2fe78bSCy Schubert process_mic(OM_uint32 *minor_status, gss_buffer_t mic_in,
602*7f2fe78bSCy Schubert 	    spnego_gss_ctx_id_t sc, gss_buffer_t *mic_out,
603*7f2fe78bSCy Schubert 	    OM_uint32 *negState, send_token_flag *tokflag)
604*7f2fe78bSCy Schubert {
605*7f2fe78bSCy Schubert 	OM_uint32 ret, tmpmin;
606*7f2fe78bSCy Schubert 	gss_qop_t qop_state;
607*7f2fe78bSCy Schubert 	gss_buffer_desc tmpmic = GSS_C_EMPTY_BUFFER;
608*7f2fe78bSCy Schubert 
609*7f2fe78bSCy Schubert 	ret = GSS_S_FAILURE;
610*7f2fe78bSCy Schubert 	if (mic_in != GSS_C_NO_BUFFER) {
611*7f2fe78bSCy Schubert 		ret = gss_verify_mic(minor_status, sc->ctx_handle,
612*7f2fe78bSCy Schubert 				     &sc->DER_mechTypes,
613*7f2fe78bSCy Schubert 				     mic_in, &qop_state);
614*7f2fe78bSCy Schubert 		if (ret == GSS_S_COMPLETE)
615*7f2fe78bSCy Schubert 			ret = ntlmssp_reset_crypto_state(minor_status, sc, 1);
616*7f2fe78bSCy Schubert 		if (ret != GSS_S_COMPLETE) {
617*7f2fe78bSCy Schubert 			*negState = REJECT;
618*7f2fe78bSCy Schubert 			*tokflag = ERROR_TOKEN_SEND;
619*7f2fe78bSCy Schubert 			return ret;
620*7f2fe78bSCy Schubert 		}
621*7f2fe78bSCy Schubert 		/* If we got a MIC, we must send a MIC. */
622*7f2fe78bSCy Schubert 		sc->mic_reqd = 1;
623*7f2fe78bSCy Schubert 		sc->mic_rcvd = 1;
624*7f2fe78bSCy Schubert 	}
625*7f2fe78bSCy Schubert 	if (sc->mic_reqd && !sc->mic_sent) {
626*7f2fe78bSCy Schubert 		ret = gss_get_mic(minor_status, sc->ctx_handle,
627*7f2fe78bSCy Schubert 				  GSS_C_QOP_DEFAULT,
628*7f2fe78bSCy Schubert 				  &sc->DER_mechTypes,
629*7f2fe78bSCy Schubert 				  &tmpmic);
630*7f2fe78bSCy Schubert 		if (ret == GSS_S_COMPLETE)
631*7f2fe78bSCy Schubert 			ret = ntlmssp_reset_crypto_state(minor_status, sc, 0);
632*7f2fe78bSCy Schubert 		if (ret != GSS_S_COMPLETE) {
633*7f2fe78bSCy Schubert 			gss_release_buffer(&tmpmin, &tmpmic);
634*7f2fe78bSCy Schubert 			*tokflag = NO_TOKEN_SEND;
635*7f2fe78bSCy Schubert 			return ret;
636*7f2fe78bSCy Schubert 		}
637*7f2fe78bSCy Schubert 		*mic_out = malloc(sizeof(gss_buffer_desc));
638*7f2fe78bSCy Schubert 		if (*mic_out == GSS_C_NO_BUFFER) {
639*7f2fe78bSCy Schubert 			gss_release_buffer(&tmpmin, &tmpmic);
640*7f2fe78bSCy Schubert 			*tokflag = NO_TOKEN_SEND;
641*7f2fe78bSCy Schubert 			return GSS_S_FAILURE;
642*7f2fe78bSCy Schubert 		}
643*7f2fe78bSCy Schubert 		**mic_out = tmpmic;
644*7f2fe78bSCy Schubert 		sc->mic_sent = 1;
645*7f2fe78bSCy Schubert 	}
646*7f2fe78bSCy Schubert 	return GSS_S_COMPLETE;
647*7f2fe78bSCy Schubert }
648*7f2fe78bSCy Schubert 
649*7f2fe78bSCy Schubert /* Create a new SPNEGO context handle for the initial call to
650*7f2fe78bSCy Schubert  * spnego_gss_init_sec_context().  */
651*7f2fe78bSCy Schubert static OM_uint32
init_ctx_new(OM_uint32 * minor_status,spnego_gss_cred_id_t spcred,send_token_flag * tokflag,spnego_gss_ctx_id_t * sc_out)652*7f2fe78bSCy Schubert init_ctx_new(OM_uint32 *minor_status,
653*7f2fe78bSCy Schubert 	     spnego_gss_cred_id_t spcred,
654*7f2fe78bSCy Schubert 	     send_token_flag *tokflag,
655*7f2fe78bSCy Schubert 	     spnego_gss_ctx_id_t *sc_out)
656*7f2fe78bSCy Schubert {
657*7f2fe78bSCy Schubert 	OM_uint32 ret;
658*7f2fe78bSCy Schubert 	spnego_gss_ctx_id_t sc = NULL;
659*7f2fe78bSCy Schubert 
660*7f2fe78bSCy Schubert 	*sc_out = NULL;
661*7f2fe78bSCy Schubert 
662*7f2fe78bSCy Schubert 	sc = create_spnego_ctx(1);
663*7f2fe78bSCy Schubert 	if (sc == NULL)
664*7f2fe78bSCy Schubert 		return GSS_S_FAILURE;
665*7f2fe78bSCy Schubert 
666*7f2fe78bSCy Schubert 	/* determine negotiation mech set */
667*7f2fe78bSCy Schubert 	ret = get_negotiable_mechs(minor_status, sc, spcred, GSS_C_INITIATE);
668*7f2fe78bSCy Schubert 	if (ret != GSS_S_COMPLETE)
669*7f2fe78bSCy Schubert 		goto cleanup;
670*7f2fe78bSCy Schubert 
671*7f2fe78bSCy Schubert 	/* Set an initial internal mech to make the first context token. */
672*7f2fe78bSCy Schubert 	sc->internal_mech = &sc->mech_set->elements[0];
673*7f2fe78bSCy Schubert 
674*7f2fe78bSCy Schubert 	if (put_mech_set(sc->mech_set, &sc->DER_mechTypes) < 0) {
675*7f2fe78bSCy Schubert 		ret = GSS_S_FAILURE;
676*7f2fe78bSCy Schubert 		goto cleanup;
677*7f2fe78bSCy Schubert 	}
678*7f2fe78bSCy Schubert 
679*7f2fe78bSCy Schubert 	sc->ctx_handle = GSS_C_NO_CONTEXT;
680*7f2fe78bSCy Schubert 	*sc_out = sc;
681*7f2fe78bSCy Schubert 	sc = NULL;
682*7f2fe78bSCy Schubert 	*tokflag = INIT_TOKEN_SEND;
683*7f2fe78bSCy Schubert 	ret = GSS_S_COMPLETE;
684*7f2fe78bSCy Schubert 
685*7f2fe78bSCy Schubert cleanup:
686*7f2fe78bSCy Schubert 	release_spnego_ctx(&sc);
687*7f2fe78bSCy Schubert 	return ret;
688*7f2fe78bSCy Schubert }
689*7f2fe78bSCy Schubert 
690*7f2fe78bSCy Schubert /*
691*7f2fe78bSCy Schubert  * Called by second and later calls to spnego_gss_init_sec_context()
692*7f2fe78bSCy Schubert  * to decode reply and update state.
693*7f2fe78bSCy Schubert  */
694*7f2fe78bSCy Schubert static OM_uint32
init_ctx_cont(OM_uint32 * minor_status,spnego_gss_ctx_id_t sc,gss_buffer_t buf,gss_buffer_t * responseToken,gss_buffer_t * mechListMIC,OM_uint32 * acc_negState,send_token_flag * tokflag)695*7f2fe78bSCy Schubert init_ctx_cont(OM_uint32 *minor_status, spnego_gss_ctx_id_t sc,
696*7f2fe78bSCy Schubert 	      gss_buffer_t buf, gss_buffer_t *responseToken,
697*7f2fe78bSCy Schubert 	      gss_buffer_t *mechListMIC, OM_uint32 *acc_negState,
698*7f2fe78bSCy Schubert 	      send_token_flag *tokflag)
699*7f2fe78bSCy Schubert {
700*7f2fe78bSCy Schubert 	OM_uint32 ret, tmpmin;
701*7f2fe78bSCy Schubert 	gss_OID supportedMech = GSS_C_NO_OID;
702*7f2fe78bSCy Schubert 	struct k5input in;
703*7f2fe78bSCy Schubert 
704*7f2fe78bSCy Schubert 	*acc_negState = UNSPECIFIED;
705*7f2fe78bSCy Schubert 	*tokflag = ERROR_TOKEN_SEND;
706*7f2fe78bSCy Schubert 
707*7f2fe78bSCy Schubert 	k5_input_init(&in, buf->value, buf->length);
708*7f2fe78bSCy Schubert 	ret = get_negTokenResp(minor_status, &in, acc_negState, &supportedMech,
709*7f2fe78bSCy Schubert 			       responseToken, mechListMIC);
710*7f2fe78bSCy Schubert 	if (ret != GSS_S_COMPLETE)
711*7f2fe78bSCy Schubert 		goto cleanup;
712*7f2fe78bSCy Schubert 
713*7f2fe78bSCy Schubert 	/* Bail out now on a reject with no error token.  If we have an error
714*7f2fe78bSCy Schubert 	 * token, keep going and get a better error status from the mech. */
715*7f2fe78bSCy Schubert 	if (*acc_negState == REJECT && *responseToken == GSS_C_NO_BUFFER) {
716*7f2fe78bSCy Schubert 		if (!sc->nego_done) {
717*7f2fe78bSCy Schubert 			/* RFC 4178 says to return GSS_S_BAD_MECH on a
718*7f2fe78bSCy Schubert 			 * mechanism negotiation failure. */
719*7f2fe78bSCy Schubert 			*minor_status = ERR_SPNEGO_NEGOTIATION_FAILED;
720*7f2fe78bSCy Schubert 			map_errcode(minor_status);
721*7f2fe78bSCy Schubert 			ret = GSS_S_BAD_MECH;
722*7f2fe78bSCy Schubert 		} else {
723*7f2fe78bSCy Schubert 			ret = GSS_S_FAILURE;
724*7f2fe78bSCy Schubert 		}
725*7f2fe78bSCy Schubert 		*tokflag = NO_TOKEN_SEND;
726*7f2fe78bSCy Schubert 		goto cleanup;
727*7f2fe78bSCy Schubert 	}
728*7f2fe78bSCy Schubert 	/*
729*7f2fe78bSCy Schubert 	 * nego_done is false for the first call to init_ctx_cont()
730*7f2fe78bSCy Schubert 	 */
731*7f2fe78bSCy Schubert 	if (!sc->nego_done) {
732*7f2fe78bSCy Schubert 		ret = init_ctx_nego(minor_status, sc, *acc_negState,
733*7f2fe78bSCy Schubert 				    supportedMech, responseToken, mechListMIC,
734*7f2fe78bSCy Schubert 				    tokflag);
735*7f2fe78bSCy Schubert 	} else if ((!sc->mech_complete && *responseToken == GSS_C_NO_BUFFER) ||
736*7f2fe78bSCy Schubert 		   (sc->mech_complete && *responseToken != GSS_C_NO_BUFFER)) {
737*7f2fe78bSCy Schubert 		/* Missing or spurious token from acceptor. */
738*7f2fe78bSCy Schubert 		ret = GSS_S_DEFECTIVE_TOKEN;
739*7f2fe78bSCy Schubert 	} else if (!sc->mech_complete ||
740*7f2fe78bSCy Schubert 		   (sc->mic_reqd &&
741*7f2fe78bSCy Schubert 		    (sc->ctx_flags & GSS_C_INTEG_FLAG))) {
742*7f2fe78bSCy Schubert 		/* Not obviously done; we may decide we're done later in
743*7f2fe78bSCy Schubert 		 * init_ctx_call_init or handle_mic. */
744*7f2fe78bSCy Schubert 		*tokflag = CONT_TOKEN_SEND;
745*7f2fe78bSCy Schubert 		ret = GSS_S_COMPLETE;
746*7f2fe78bSCy Schubert 	} else {
747*7f2fe78bSCy Schubert 		/* mech finished on last pass and no MIC required, so done. */
748*7f2fe78bSCy Schubert 		*tokflag = NO_TOKEN_SEND;
749*7f2fe78bSCy Schubert 		ret = GSS_S_COMPLETE;
750*7f2fe78bSCy Schubert 	}
751*7f2fe78bSCy Schubert cleanup:
752*7f2fe78bSCy Schubert 	if (supportedMech != GSS_C_NO_OID)
753*7f2fe78bSCy Schubert 		generic_gss_release_oid(&tmpmin, &supportedMech);
754*7f2fe78bSCy Schubert 	return ret;
755*7f2fe78bSCy Schubert }
756*7f2fe78bSCy Schubert 
757*7f2fe78bSCy Schubert /*
758*7f2fe78bSCy Schubert  * Consistency checking and mechanism negotiation handling for second
759*7f2fe78bSCy Schubert  * call of spnego_gss_init_sec_context().  Call init_ctx_reselect() to
760*7f2fe78bSCy Schubert  * update internal state if acceptor has counter-proposed.
761*7f2fe78bSCy Schubert  */
762*7f2fe78bSCy Schubert static OM_uint32
init_ctx_nego(OM_uint32 * minor_status,spnego_gss_ctx_id_t sc,OM_uint32 acc_negState,gss_OID supportedMech,gss_buffer_t * responseToken,gss_buffer_t * mechListMIC,send_token_flag * tokflag)763*7f2fe78bSCy Schubert init_ctx_nego(OM_uint32 *minor_status, spnego_gss_ctx_id_t sc,
764*7f2fe78bSCy Schubert 	      OM_uint32 acc_negState, gss_OID supportedMech,
765*7f2fe78bSCy Schubert 	      gss_buffer_t *responseToken, gss_buffer_t *mechListMIC,
766*7f2fe78bSCy Schubert 	      send_token_flag *tokflag)
767*7f2fe78bSCy Schubert {
768*7f2fe78bSCy Schubert 	OM_uint32 ret;
769*7f2fe78bSCy Schubert 
770*7f2fe78bSCy Schubert 	*tokflag = ERROR_TOKEN_SEND;
771*7f2fe78bSCy Schubert 	ret = GSS_S_DEFECTIVE_TOKEN;
772*7f2fe78bSCy Schubert 
773*7f2fe78bSCy Schubert 	/*
774*7f2fe78bSCy Schubert 	 * According to RFC 4178, both supportedMech and negState must be
775*7f2fe78bSCy Schubert 	 * present in the first acceptor token.  However, some Java
776*7f2fe78bSCy Schubert 	 * implementations include only a responseToken in the first
777*7f2fe78bSCy Schubert 	 * NegTokenResp.  In this case we can use sc->internal_mech as the
778*7f2fe78bSCy Schubert 	 * negotiated mechanism.  (We do not currently look at acc_negState
779*7f2fe78bSCy Schubert 	 * when continuing with the optimistic mechanism.)
780*7f2fe78bSCy Schubert 	 */
781*7f2fe78bSCy Schubert 	if (supportedMech == GSS_C_NO_OID)
782*7f2fe78bSCy Schubert 		supportedMech = sc->internal_mech;
783*7f2fe78bSCy Schubert 
784*7f2fe78bSCy Schubert 	/*
785*7f2fe78bSCy Schubert 	 * If the mechanism we sent is not the mechanism returned from
786*7f2fe78bSCy Schubert 	 * the server, we need to handle the server's counter
787*7f2fe78bSCy Schubert 	 * proposal.  There is a bug in SAMBA servers that always send
788*7f2fe78bSCy Schubert 	 * the old Kerberos mech OID, even though we sent the new one.
789*7f2fe78bSCy Schubert 	 * So we will treat all the Kerberos mech OIDS as the same.
790*7f2fe78bSCy Schubert          */
791*7f2fe78bSCy Schubert 	if (!(is_kerb_mech(supportedMech) &&
792*7f2fe78bSCy Schubert 	      is_kerb_mech(sc->internal_mech)) &&
793*7f2fe78bSCy Schubert 	    !g_OID_equal(supportedMech, sc->internal_mech)) {
794*7f2fe78bSCy Schubert 		ret = init_ctx_reselect(minor_status, sc,
795*7f2fe78bSCy Schubert 					acc_negState, supportedMech,
796*7f2fe78bSCy Schubert 					responseToken, mechListMIC, tokflag);
797*7f2fe78bSCy Schubert 
798*7f2fe78bSCy Schubert 	} else if (*responseToken == GSS_C_NO_BUFFER) {
799*7f2fe78bSCy Schubert 		if (sc->mech_complete) {
800*7f2fe78bSCy Schubert 			/*
801*7f2fe78bSCy Schubert 			 * Mech completed on first call to its
802*7f2fe78bSCy Schubert 			 * init_sec_context().  Acceptor sends no mech
803*7f2fe78bSCy Schubert 			 * token.
804*7f2fe78bSCy Schubert 			 */
805*7f2fe78bSCy Schubert 			*tokflag = NO_TOKEN_SEND;
806*7f2fe78bSCy Schubert 			ret = GSS_S_COMPLETE;
807*7f2fe78bSCy Schubert 		} else {
808*7f2fe78bSCy Schubert 			/*
809*7f2fe78bSCy Schubert 			 * Reject missing mech token when optimistic
810*7f2fe78bSCy Schubert 			 * mech selected.
811*7f2fe78bSCy Schubert 			 */
812*7f2fe78bSCy Schubert 			*minor_status = ERR_SPNEGO_NO_TOKEN_FROM_ACCEPTOR;
813*7f2fe78bSCy Schubert 			map_errcode(minor_status);
814*7f2fe78bSCy Schubert 			ret = GSS_S_DEFECTIVE_TOKEN;
815*7f2fe78bSCy Schubert 		}
816*7f2fe78bSCy Schubert 	} else if ((*responseToken)->length == 0 && sc->mech_complete) {
817*7f2fe78bSCy Schubert 		/* Handle old IIS servers returning empty token instead of
818*7f2fe78bSCy Schubert 		 * null tokens in the non-mutual auth case. */
819*7f2fe78bSCy Schubert 		*tokflag = NO_TOKEN_SEND;
820*7f2fe78bSCy Schubert 		ret = GSS_S_COMPLETE;
821*7f2fe78bSCy Schubert 	} else if (sc->mech_complete) {
822*7f2fe78bSCy Schubert 		/* Reject spurious mech token. */
823*7f2fe78bSCy Schubert 		ret = GSS_S_DEFECTIVE_TOKEN;
824*7f2fe78bSCy Schubert 	} else {
825*7f2fe78bSCy Schubert 		*tokflag = CONT_TOKEN_SEND;
826*7f2fe78bSCy Schubert 		ret = GSS_S_COMPLETE;
827*7f2fe78bSCy Schubert 	}
828*7f2fe78bSCy Schubert 	sc->nego_done = 1;
829*7f2fe78bSCy Schubert 	return ret;
830*7f2fe78bSCy Schubert }
831*7f2fe78bSCy Schubert 
832*7f2fe78bSCy Schubert /*
833*7f2fe78bSCy Schubert  * Handle acceptor's counter-proposal of an alternative mechanism.
834*7f2fe78bSCy Schubert  */
835*7f2fe78bSCy Schubert static OM_uint32
init_ctx_reselect(OM_uint32 * minor_status,spnego_gss_ctx_id_t sc,OM_uint32 acc_negState,gss_OID supportedMech,gss_buffer_t * responseToken,gss_buffer_t * mechListMIC,send_token_flag * tokflag)836*7f2fe78bSCy Schubert init_ctx_reselect(OM_uint32 *minor_status, spnego_gss_ctx_id_t sc,
837*7f2fe78bSCy Schubert 		  OM_uint32 acc_negState, gss_OID supportedMech,
838*7f2fe78bSCy Schubert 		  gss_buffer_t *responseToken, gss_buffer_t *mechListMIC,
839*7f2fe78bSCy Schubert 		  send_token_flag *tokflag)
840*7f2fe78bSCy Schubert {
841*7f2fe78bSCy Schubert 	OM_uint32 tmpmin;
842*7f2fe78bSCy Schubert 	size_t i;
843*7f2fe78bSCy Schubert 
844*7f2fe78bSCy Schubert 	gss_delete_sec_context(&tmpmin, &sc->ctx_handle,
845*7f2fe78bSCy Schubert 			       GSS_C_NO_BUFFER);
846*7f2fe78bSCy Schubert 
847*7f2fe78bSCy Schubert 	/* Find supportedMech in sc->mech_set. */
848*7f2fe78bSCy Schubert 	for (i = 0; i < sc->mech_set->count; i++) {
849*7f2fe78bSCy Schubert 		if (g_OID_equal(supportedMech, &sc->mech_set->elements[i]))
850*7f2fe78bSCy Schubert 			break;
851*7f2fe78bSCy Schubert 	}
852*7f2fe78bSCy Schubert 	if (i == sc->mech_set->count)
853*7f2fe78bSCy Schubert 		return GSS_S_DEFECTIVE_TOKEN;
854*7f2fe78bSCy Schubert 	sc->internal_mech = &sc->mech_set->elements[i];
855*7f2fe78bSCy Schubert 
856*7f2fe78bSCy Schubert 	/*
857*7f2fe78bSCy Schubert 	 * A server conforming to RFC4178 MUST set REQUEST_MIC here, but
858*7f2fe78bSCy Schubert 	 * Windows Server 2003 and earlier implement (roughly) RFC2478 instead,
859*7f2fe78bSCy Schubert 	 * and send ACCEPT_INCOMPLETE.  Tolerate that only if we are falling
860*7f2fe78bSCy Schubert 	 * back to NTLMSSP.
861*7f2fe78bSCy Schubert 	 */
862*7f2fe78bSCy Schubert 	if (acc_negState == ACCEPT_INCOMPLETE) {
863*7f2fe78bSCy Schubert 		if (!g_OID_equal(supportedMech, &gss_mech_ntlmssp_oid))
864*7f2fe78bSCy Schubert 			return GSS_S_DEFECTIVE_TOKEN;
865*7f2fe78bSCy Schubert 	} else if (acc_negState != REQUEST_MIC) {
866*7f2fe78bSCy Schubert 		return GSS_S_DEFECTIVE_TOKEN;
867*7f2fe78bSCy Schubert 	}
868*7f2fe78bSCy Schubert 
869*7f2fe78bSCy Schubert 	sc->mech_complete = 0;
870*7f2fe78bSCy Schubert 	sc->mic_reqd = (acc_negState == REQUEST_MIC);
871*7f2fe78bSCy Schubert 	*tokflag = CONT_TOKEN_SEND;
872*7f2fe78bSCy Schubert 	return GSS_S_COMPLETE;
873*7f2fe78bSCy Schubert }
874*7f2fe78bSCy Schubert 
875*7f2fe78bSCy Schubert /*
876*7f2fe78bSCy Schubert  * Wrap call to mechanism gss_init_sec_context() and update state
877*7f2fe78bSCy Schubert  * accordingly.
878*7f2fe78bSCy Schubert  */
879*7f2fe78bSCy Schubert static OM_uint32
init_ctx_call_init(OM_uint32 * minor_status,spnego_gss_ctx_id_t sc,spnego_gss_cred_id_t spcred,OM_uint32 acc_negState,gss_name_t target_name,OM_uint32 req_flags,OM_uint32 time_req,gss_buffer_t mechtok_in,gss_channel_bindings_t bindings,gss_buffer_t mechtok_out,OM_uint32 * time_rec,send_token_flag * send_token)880*7f2fe78bSCy Schubert init_ctx_call_init(OM_uint32 *minor_status,
881*7f2fe78bSCy Schubert 		   spnego_gss_ctx_id_t sc,
882*7f2fe78bSCy Schubert 		   spnego_gss_cred_id_t spcred,
883*7f2fe78bSCy Schubert 		   OM_uint32 acc_negState,
884*7f2fe78bSCy Schubert 		   gss_name_t target_name,
885*7f2fe78bSCy Schubert 		   OM_uint32 req_flags,
886*7f2fe78bSCy Schubert 		   OM_uint32 time_req,
887*7f2fe78bSCy Schubert 		   gss_buffer_t mechtok_in,
888*7f2fe78bSCy Schubert 		   gss_channel_bindings_t bindings,
889*7f2fe78bSCy Schubert 		   gss_buffer_t mechtok_out,
890*7f2fe78bSCy Schubert 		   OM_uint32 *time_rec,
891*7f2fe78bSCy Schubert 		   send_token_flag *send_token)
892*7f2fe78bSCy Schubert {
893*7f2fe78bSCy Schubert 	OM_uint32 ret, tmpret, tmpmin, mech_req_flags;
894*7f2fe78bSCy Schubert 	gss_cred_id_t mcred;
895*7f2fe78bSCy Schubert 
896*7f2fe78bSCy Schubert 	mcred = (spcred == NULL) ? GSS_C_NO_CREDENTIAL : spcred->mcred;
897*7f2fe78bSCy Schubert 
898*7f2fe78bSCy Schubert 	mech_req_flags = req_flags;
899*7f2fe78bSCy Schubert 	if (spcred == NULL || !spcred->no_ask_integ)
900*7f2fe78bSCy Schubert 		mech_req_flags |= GSS_C_INTEG_FLAG;
901*7f2fe78bSCy Schubert 
902*7f2fe78bSCy Schubert 	if (gss_oid_equal(sc->internal_mech, &negoex_mech)) {
903*7f2fe78bSCy Schubert 		ret = negoex_init(minor_status, sc, mcred, target_name,
904*7f2fe78bSCy Schubert 				  mech_req_flags, time_req, mechtok_in,
905*7f2fe78bSCy Schubert 				  bindings, mechtok_out, time_rec);
906*7f2fe78bSCy Schubert 	} else {
907*7f2fe78bSCy Schubert 		ret = gss_init_sec_context(minor_status, mcred,
908*7f2fe78bSCy Schubert 					   &sc->ctx_handle, target_name,
909*7f2fe78bSCy Schubert 					   sc->internal_mech, mech_req_flags,
910*7f2fe78bSCy Schubert 					   time_req, bindings, mechtok_in,
911*7f2fe78bSCy Schubert 					   &sc->actual_mech, mechtok_out,
912*7f2fe78bSCy Schubert 					   &sc->ctx_flags, time_rec);
913*7f2fe78bSCy Schubert 	}
914*7f2fe78bSCy Schubert 
915*7f2fe78bSCy Schubert 	/* Bail out if the acceptor gave us an error token but the mech didn't
916*7f2fe78bSCy Schubert 	 * see it as an error. */
917*7f2fe78bSCy Schubert 	if (acc_negState == REJECT && !GSS_ERROR(ret)) {
918*7f2fe78bSCy Schubert 		ret = GSS_S_DEFECTIVE_TOKEN;
919*7f2fe78bSCy Schubert 		goto fail;
920*7f2fe78bSCy Schubert 	}
921*7f2fe78bSCy Schubert 
922*7f2fe78bSCy Schubert 	if (ret == GSS_S_COMPLETE) {
923*7f2fe78bSCy Schubert 		sc->mech_complete = 1;
924*7f2fe78bSCy Schubert 		/*
925*7f2fe78bSCy Schubert 		 * Microsoft SPNEGO implementations expect an even number of
926*7f2fe78bSCy Schubert 		 * token exchanges.  So if we're sending a final token, ask for
927*7f2fe78bSCy Schubert 		 * a zero-length token back from the server.  Also ask for a
928*7f2fe78bSCy Schubert 		 * token back if this is the first token or if a MIC exchange
929*7f2fe78bSCy Schubert 		 * is required.
930*7f2fe78bSCy Schubert 		 */
931*7f2fe78bSCy Schubert 		if (*send_token == CONT_TOKEN_SEND &&
932*7f2fe78bSCy Schubert 		    mechtok_out->length == 0 &&
933*7f2fe78bSCy Schubert 		    (!sc->mic_reqd || !(sc->ctx_flags & GSS_C_INTEG_FLAG)))
934*7f2fe78bSCy Schubert 			*send_token = NO_TOKEN_SEND;
935*7f2fe78bSCy Schubert 
936*7f2fe78bSCy Schubert 		return GSS_S_COMPLETE;
937*7f2fe78bSCy Schubert 	}
938*7f2fe78bSCy Schubert 
939*7f2fe78bSCy Schubert 	if (ret == GSS_S_CONTINUE_NEEDED)
940*7f2fe78bSCy Schubert 		return GSS_S_COMPLETE;
941*7f2fe78bSCy Schubert 
942*7f2fe78bSCy Schubert 	if (*send_token != INIT_TOKEN_SEND) {
943*7f2fe78bSCy Schubert 		*send_token = ERROR_TOKEN_SEND;
944*7f2fe78bSCy Schubert 		return ret;
945*7f2fe78bSCy Schubert 	}
946*7f2fe78bSCy Schubert 
947*7f2fe78bSCy Schubert 	/*
948*7f2fe78bSCy Schubert 	 * Since this is the first token, we can fall back to later mechanisms
949*7f2fe78bSCy Schubert 	 * in the list.  Since the mechanism list is expected to be short, we
950*7f2fe78bSCy Schubert 	 * can do this with recursion.  If all mechanisms produce errors, the
951*7f2fe78bSCy Schubert 	 * caller should get the error from the first mech in the list.
952*7f2fe78bSCy Schubert 	 */
953*7f2fe78bSCy Schubert 	gssalloc_free(sc->mech_set->elements->elements);
954*7f2fe78bSCy Schubert 	memmove(sc->mech_set->elements, sc->mech_set->elements + 1,
955*7f2fe78bSCy Schubert 		--sc->mech_set->count * sizeof(*sc->mech_set->elements));
956*7f2fe78bSCy Schubert 	if (sc->mech_set->count == 0)
957*7f2fe78bSCy Schubert 		goto fail;
958*7f2fe78bSCy Schubert 	gss_release_buffer(&tmpmin, &sc->DER_mechTypes);
959*7f2fe78bSCy Schubert 	if (put_mech_set(sc->mech_set, &sc->DER_mechTypes) < 0)
960*7f2fe78bSCy Schubert 		goto fail;
961*7f2fe78bSCy Schubert 	gss_delete_sec_context(&tmpmin, &sc->ctx_handle, GSS_C_NO_BUFFER);
962*7f2fe78bSCy Schubert 	tmpret = init_ctx_call_init(&tmpmin, sc, spcred, acc_negState,
963*7f2fe78bSCy Schubert 				    target_name, req_flags, time_req,
964*7f2fe78bSCy Schubert 				    mechtok_in, bindings, mechtok_out,
965*7f2fe78bSCy Schubert 				    time_rec, send_token);
966*7f2fe78bSCy Schubert 	if (HARD_ERROR(tmpret))
967*7f2fe78bSCy Schubert 		goto fail;
968*7f2fe78bSCy Schubert 	*minor_status = tmpmin;
969*7f2fe78bSCy Schubert 	return tmpret;
970*7f2fe78bSCy Schubert 
971*7f2fe78bSCy Schubert fail:
972*7f2fe78bSCy Schubert 	/* Don't output token on error from first call. */
973*7f2fe78bSCy Schubert 	*send_token = NO_TOKEN_SEND;
974*7f2fe78bSCy Schubert 	return ret;
975*7f2fe78bSCy Schubert }
976*7f2fe78bSCy Schubert 
977*7f2fe78bSCy Schubert /*ARGSUSED*/
978*7f2fe78bSCy Schubert OM_uint32 KRB5_CALLCONV
spnego_gss_init_sec_context(OM_uint32 * minor_status,gss_cred_id_t claimant_cred_handle,gss_ctx_id_t * context_handle,gss_name_t target_name,gss_OID mech_type,OM_uint32 req_flags,OM_uint32 time_req,gss_channel_bindings_t bindings,gss_buffer_t input_token,gss_OID * actual_mech,gss_buffer_t output_token,OM_uint32 * ret_flags,OM_uint32 * time_rec)979*7f2fe78bSCy Schubert spnego_gss_init_sec_context(
980*7f2fe78bSCy Schubert 			OM_uint32 *minor_status,
981*7f2fe78bSCy Schubert 			gss_cred_id_t claimant_cred_handle,
982*7f2fe78bSCy Schubert 			gss_ctx_id_t *context_handle,
983*7f2fe78bSCy Schubert 			gss_name_t target_name,
984*7f2fe78bSCy Schubert 			gss_OID mech_type,
985*7f2fe78bSCy Schubert 			OM_uint32 req_flags,
986*7f2fe78bSCy Schubert 			OM_uint32 time_req,
987*7f2fe78bSCy Schubert 			gss_channel_bindings_t bindings,
988*7f2fe78bSCy Schubert 			gss_buffer_t input_token,
989*7f2fe78bSCy Schubert 			gss_OID *actual_mech,
990*7f2fe78bSCy Schubert 			gss_buffer_t output_token,
991*7f2fe78bSCy Schubert 			OM_uint32 *ret_flags,
992*7f2fe78bSCy Schubert 			OM_uint32 *time_rec)
993*7f2fe78bSCy Schubert {
994*7f2fe78bSCy Schubert 	send_token_flag send_token = NO_TOKEN_SEND;
995*7f2fe78bSCy Schubert 	OM_uint32 tmpmin, ret, negState = UNSPECIFIED, acc_negState;
996*7f2fe78bSCy Schubert 	gss_buffer_t mechtok_in, mechListMIC_in, mechListMIC_out;
997*7f2fe78bSCy Schubert 	gss_buffer_desc mechtok_out = GSS_C_EMPTY_BUFFER;
998*7f2fe78bSCy Schubert 	spnego_gss_cred_id_t spcred = NULL;
999*7f2fe78bSCy Schubert 	spnego_gss_ctx_id_t spnego_ctx = NULL;
1000*7f2fe78bSCy Schubert 
1001*7f2fe78bSCy Schubert 	dsyslog("Entering init_sec_context\n");
1002*7f2fe78bSCy Schubert 
1003*7f2fe78bSCy Schubert 	mechtok_in = mechListMIC_out = mechListMIC_in = GSS_C_NO_BUFFER;
1004*7f2fe78bSCy Schubert 
1005*7f2fe78bSCy Schubert 	/*
1006*7f2fe78bSCy Schubert 	 * This function works in three steps:
1007*7f2fe78bSCy Schubert 	 *
1008*7f2fe78bSCy Schubert 	 *   1. Perform mechanism negotiation.
1009*7f2fe78bSCy Schubert 	 *   2. Invoke the negotiated or optimistic mech's gss_init_sec_context
1010*7f2fe78bSCy Schubert 	 *      function and examine the results.
1011*7f2fe78bSCy Schubert 	 *   3. Process or generate MICs if necessary.
1012*7f2fe78bSCy Schubert 	 *
1013*7f2fe78bSCy Schubert 	 * The three steps share responsibility for determining when the
1014*7f2fe78bSCy Schubert 	 * exchange is complete.  If the selected mech completed in a previous
1015*7f2fe78bSCy Schubert 	 * call and no MIC exchange is expected, then step 1 will decide.  If
1016*7f2fe78bSCy Schubert 	 * the selected mech completes in this call and no MIC exchange is
1017*7f2fe78bSCy Schubert 	 * expected, then step 2 will decide.  If a MIC exchange is expected,
1018*7f2fe78bSCy Schubert 	 * then step 3 will decide.  If an error occurs in any step, the
1019*7f2fe78bSCy Schubert 	 * exchange will be aborted, possibly with an error token.
1020*7f2fe78bSCy Schubert 	 *
1021*7f2fe78bSCy Schubert 	 * negState determines the state of the negotiation, and is
1022*7f2fe78bSCy Schubert 	 * communicated to the acceptor if a continuing token is sent.
1023*7f2fe78bSCy Schubert 	 * send_token is used to indicate what type of token, if any, should be
1024*7f2fe78bSCy Schubert 	 * generated.
1025*7f2fe78bSCy Schubert 	 */
1026*7f2fe78bSCy Schubert 
1027*7f2fe78bSCy Schubert 	/* Validate arguments. */
1028*7f2fe78bSCy Schubert 	if (minor_status != NULL)
1029*7f2fe78bSCy Schubert 		*minor_status = 0;
1030*7f2fe78bSCy Schubert 	if (output_token != GSS_C_NO_BUFFER) {
1031*7f2fe78bSCy Schubert 		output_token->length = 0;
1032*7f2fe78bSCy Schubert 		output_token->value = NULL;
1033*7f2fe78bSCy Schubert 	}
1034*7f2fe78bSCy Schubert 	if (minor_status == NULL ||
1035*7f2fe78bSCy Schubert 	    output_token == GSS_C_NO_BUFFER ||
1036*7f2fe78bSCy Schubert 	    context_handle == NULL)
1037*7f2fe78bSCy Schubert 		return GSS_S_CALL_INACCESSIBLE_WRITE;
1038*7f2fe78bSCy Schubert 
1039*7f2fe78bSCy Schubert 	if (actual_mech != NULL)
1040*7f2fe78bSCy Schubert 		*actual_mech = GSS_C_NO_OID;
1041*7f2fe78bSCy Schubert 	if (time_rec != NULL)
1042*7f2fe78bSCy Schubert 		*time_rec = 0;
1043*7f2fe78bSCy Schubert 
1044*7f2fe78bSCy Schubert 	/* Step 1: perform mechanism negotiation. */
1045*7f2fe78bSCy Schubert 	spcred = (spnego_gss_cred_id_t)claimant_cred_handle;
1046*7f2fe78bSCy Schubert 	spnego_ctx = (spnego_gss_ctx_id_t)*context_handle;
1047*7f2fe78bSCy Schubert 	if (spnego_ctx == NULL) {
1048*7f2fe78bSCy Schubert 		ret = init_ctx_new(minor_status, spcred, &send_token,
1049*7f2fe78bSCy Schubert 				   &spnego_ctx);
1050*7f2fe78bSCy Schubert 		if (ret != GSS_S_COMPLETE)
1051*7f2fe78bSCy Schubert 			goto cleanup;
1052*7f2fe78bSCy Schubert 		*context_handle = (gss_ctx_id_t)spnego_ctx;
1053*7f2fe78bSCy Schubert 		acc_negState = UNSPECIFIED;
1054*7f2fe78bSCy Schubert 	} else {
1055*7f2fe78bSCy Schubert 		ret = init_ctx_cont(minor_status, spnego_ctx, input_token,
1056*7f2fe78bSCy Schubert 				    &mechtok_in, &mechListMIC_in,
1057*7f2fe78bSCy Schubert 				    &acc_negState, &send_token);
1058*7f2fe78bSCy Schubert 		if (ret != GSS_S_COMPLETE)
1059*7f2fe78bSCy Schubert 			goto cleanup;
1060*7f2fe78bSCy Schubert 	}
1061*7f2fe78bSCy Schubert 
1062*7f2fe78bSCy Schubert 	/* Step 2: invoke the selected or optimistic mechanism's
1063*7f2fe78bSCy Schubert 	 * gss_init_sec_context function, if it didn't complete previously. */
1064*7f2fe78bSCy Schubert 	if (!spnego_ctx->mech_complete) {
1065*7f2fe78bSCy Schubert 		ret = init_ctx_call_init(minor_status, spnego_ctx, spcred,
1066*7f2fe78bSCy Schubert 					 acc_negState, target_name, req_flags,
1067*7f2fe78bSCy Schubert 					 time_req, mechtok_in, bindings,
1068*7f2fe78bSCy Schubert 					 &mechtok_out, time_rec, &send_token);
1069*7f2fe78bSCy Schubert 		if (ret != GSS_S_COMPLETE)
1070*7f2fe78bSCy Schubert 			goto cleanup;
1071*7f2fe78bSCy Schubert 
1072*7f2fe78bSCy Schubert 		/* Give the mechanism a chance to force a mechlistMIC. */
1073*7f2fe78bSCy Schubert 		if (mech_requires_mechlistMIC(spnego_ctx))
1074*7f2fe78bSCy Schubert 			spnego_ctx->mic_reqd = 1;
1075*7f2fe78bSCy Schubert 	}
1076*7f2fe78bSCy Schubert 
1077*7f2fe78bSCy Schubert 	/* Step 3: process or generate the MIC, if the negotiated mech is
1078*7f2fe78bSCy Schubert 	 * complete and supports MICs.  Also decide the outgoing negState. */
1079*7f2fe78bSCy Schubert 	negState = ACCEPT_INCOMPLETE;
1080*7f2fe78bSCy Schubert 	if (spnego_ctx->mech_complete &&
1081*7f2fe78bSCy Schubert 	    (spnego_ctx->ctx_flags & GSS_C_INTEG_FLAG)) {
1082*7f2fe78bSCy Schubert 
1083*7f2fe78bSCy Schubert 		ret = handle_mic(minor_status,
1084*7f2fe78bSCy Schubert 				 mechListMIC_in,
1085*7f2fe78bSCy Schubert 				 (mechtok_out.length != 0),
1086*7f2fe78bSCy Schubert 				 spnego_ctx, &mechListMIC_out,
1087*7f2fe78bSCy Schubert 				 &negState, &send_token);
1088*7f2fe78bSCy Schubert 		if (HARD_ERROR(ret))
1089*7f2fe78bSCy Schubert 			goto cleanup;
1090*7f2fe78bSCy Schubert 	}
1091*7f2fe78bSCy Schubert 
1092*7f2fe78bSCy Schubert 	if (ret_flags != NULL)
1093*7f2fe78bSCy Schubert 		*ret_flags = spnego_ctx->ctx_flags & ~GSS_C_PROT_READY_FLAG;
1094*7f2fe78bSCy Schubert 
1095*7f2fe78bSCy Schubert 	ret = (send_token == NO_TOKEN_SEND || negState == ACCEPT_COMPLETE) ?
1096*7f2fe78bSCy Schubert 		GSS_S_COMPLETE : GSS_S_CONTINUE_NEEDED;
1097*7f2fe78bSCy Schubert 
1098*7f2fe78bSCy Schubert cleanup:
1099*7f2fe78bSCy Schubert 	if (send_token == INIT_TOKEN_SEND) {
1100*7f2fe78bSCy Schubert 		if (make_spnego_tokenInit_msg(spnego_ctx,
1101*7f2fe78bSCy Schubert 					      0,
1102*7f2fe78bSCy Schubert 					      mechListMIC_out,
1103*7f2fe78bSCy Schubert 					      req_flags,
1104*7f2fe78bSCy Schubert 					      &mechtok_out, send_token,
1105*7f2fe78bSCy Schubert 					      output_token) < 0) {
1106*7f2fe78bSCy Schubert 			ret = GSS_S_FAILURE;
1107*7f2fe78bSCy Schubert 		}
1108*7f2fe78bSCy Schubert 	} else if (send_token != NO_TOKEN_SEND) {
1109*7f2fe78bSCy Schubert 		if (send_token == ERROR_TOKEN_SEND)
1110*7f2fe78bSCy Schubert 			negState = REJECT;
1111*7f2fe78bSCy Schubert 		if (make_spnego_tokenTarg_msg(negState, GSS_C_NO_OID,
1112*7f2fe78bSCy Schubert 					      &mechtok_out, mechListMIC_out,
1113*7f2fe78bSCy Schubert 					      send_token,
1114*7f2fe78bSCy Schubert 					      output_token) < 0) {
1115*7f2fe78bSCy Schubert 			ret = GSS_S_FAILURE;
1116*7f2fe78bSCy Schubert 		}
1117*7f2fe78bSCy Schubert 	}
1118*7f2fe78bSCy Schubert 	gss_release_buffer(&tmpmin, &mechtok_out);
1119*7f2fe78bSCy Schubert 	if (ret == GSS_S_COMPLETE) {
1120*7f2fe78bSCy Schubert 		spnego_ctx->opened = 1;
1121*7f2fe78bSCy Schubert 		if (actual_mech != NULL)
1122*7f2fe78bSCy Schubert 			*actual_mech = spnego_ctx->actual_mech;
1123*7f2fe78bSCy Schubert 		/* Get an updated lifetime if we didn't call into the mech. */
1124*7f2fe78bSCy Schubert 		if (time_rec != NULL && *time_rec == 0) {
1125*7f2fe78bSCy Schubert 			(void) gss_context_time(&tmpmin,
1126*7f2fe78bSCy Schubert 						spnego_ctx->ctx_handle,
1127*7f2fe78bSCy Schubert 						time_rec);
1128*7f2fe78bSCy Schubert 		}
1129*7f2fe78bSCy Schubert 	} else if (ret != GSS_S_CONTINUE_NEEDED) {
1130*7f2fe78bSCy Schubert 		if (spnego_ctx != NULL) {
1131*7f2fe78bSCy Schubert 			gss_delete_sec_context(&tmpmin,
1132*7f2fe78bSCy Schubert 					       &spnego_ctx->ctx_handle,
1133*7f2fe78bSCy Schubert 					       GSS_C_NO_BUFFER);
1134*7f2fe78bSCy Schubert 			release_spnego_ctx(&spnego_ctx);
1135*7f2fe78bSCy Schubert 		}
1136*7f2fe78bSCy Schubert 		*context_handle = GSS_C_NO_CONTEXT;
1137*7f2fe78bSCy Schubert 	}
1138*7f2fe78bSCy Schubert 	if (mechtok_in != GSS_C_NO_BUFFER) {
1139*7f2fe78bSCy Schubert 		gss_release_buffer(&tmpmin, mechtok_in);
1140*7f2fe78bSCy Schubert 		free(mechtok_in);
1141*7f2fe78bSCy Schubert 	}
1142*7f2fe78bSCy Schubert 	if (mechListMIC_in != GSS_C_NO_BUFFER) {
1143*7f2fe78bSCy Schubert 		gss_release_buffer(&tmpmin, mechListMIC_in);
1144*7f2fe78bSCy Schubert 		free(mechListMIC_in);
1145*7f2fe78bSCy Schubert 	}
1146*7f2fe78bSCy Schubert 	if (mechListMIC_out != GSS_C_NO_BUFFER) {
1147*7f2fe78bSCy Schubert 		gss_release_buffer(&tmpmin, mechListMIC_out);
1148*7f2fe78bSCy Schubert 		free(mechListMIC_out);
1149*7f2fe78bSCy Schubert 	}
1150*7f2fe78bSCy Schubert 	return ret;
1151*7f2fe78bSCy Schubert } /* init_sec_context */
1152*7f2fe78bSCy Schubert 
1153*7f2fe78bSCy Schubert /* We don't want to import KRB5 headers here */
1154*7f2fe78bSCy Schubert static const gss_OID_desc gss_mech_krb5_oid =
1155*7f2fe78bSCy Schubert 	{ 9, "\052\206\110\206\367\022\001\002\002" };
1156*7f2fe78bSCy Schubert static const gss_OID_desc gss_mech_krb5_wrong_oid =
1157*7f2fe78bSCy Schubert 	{ 9, "\052\206\110\202\367\022\001\002\002" };
1158*7f2fe78bSCy Schubert 
1159*7f2fe78bSCy Schubert /*
1160*7f2fe78bSCy Schubert  * NegHints ::= SEQUENCE {
1161*7f2fe78bSCy Schubert  *    hintName       [0]  GeneralString      OPTIONAL,
1162*7f2fe78bSCy Schubert  *    hintAddress    [1]  OCTET STRING       OPTIONAL
1163*7f2fe78bSCy Schubert  * }
1164*7f2fe78bSCy Schubert  */
1165*7f2fe78bSCy Schubert 
1166*7f2fe78bSCy Schubert #define HOST_PREFIX	"host@"
1167*7f2fe78bSCy Schubert #define HOST_PREFIX_LEN	(sizeof(HOST_PREFIX) - 1)
1168*7f2fe78bSCy Schubert 
1169*7f2fe78bSCy Schubert /* Encode the dummy hintname string (as specified in [MS-SPNG]) into a
1170*7f2fe78bSCy Schubert  * DER-encoded [0] tagged GeneralString, and place the result in *outbuf. */
1171*7f2fe78bSCy Schubert static int
make_NegHints(OM_uint32 * minor_status,gss_buffer_t * outbuf)1172*7f2fe78bSCy Schubert make_NegHints(OM_uint32 *minor_status, gss_buffer_t *outbuf)
1173*7f2fe78bSCy Schubert {
1174*7f2fe78bSCy Schubert 	OM_uint32 major_status;
1175*7f2fe78bSCy Schubert 	size_t hint_len, tlen;
1176*7f2fe78bSCy Schubert 	uint8_t *t;
1177*7f2fe78bSCy Schubert 	const char *hintname = "not_defined_in_RFC4178@please_ignore";
1178*7f2fe78bSCy Schubert 	const size_t hintname_len = strlen(hintname);
1179*7f2fe78bSCy Schubert 	struct k5buf buf;
1180*7f2fe78bSCy Schubert 
1181*7f2fe78bSCy Schubert 	*outbuf = GSS_C_NO_BUFFER;
1182*7f2fe78bSCy Schubert 	major_status = GSS_S_FAILURE;
1183*7f2fe78bSCy Schubert 
1184*7f2fe78bSCy Schubert 	hint_len = k5_der_value_len(hintname_len);
1185*7f2fe78bSCy Schubert 	tlen = k5_der_value_len(hint_len);
1186*7f2fe78bSCy Schubert 
1187*7f2fe78bSCy Schubert 	t = gssalloc_malloc(tlen);
1188*7f2fe78bSCy Schubert 	if (t == NULL) {
1189*7f2fe78bSCy Schubert 		*minor_status = ENOMEM;
1190*7f2fe78bSCy Schubert 		goto errout;
1191*7f2fe78bSCy Schubert 	}
1192*7f2fe78bSCy Schubert 	k5_buf_init_fixed(&buf, t, tlen);
1193*7f2fe78bSCy Schubert 
1194*7f2fe78bSCy Schubert 	k5_der_add_taglen(&buf, CONTEXT | 0x00, hint_len);
1195*7f2fe78bSCy Schubert 	k5_der_add_value(&buf, GENERAL_STRING, hintname, hintname_len);
1196*7f2fe78bSCy Schubert 	assert(buf.len == tlen);
1197*7f2fe78bSCy Schubert 
1198*7f2fe78bSCy Schubert 	*outbuf = (gss_buffer_t)malloc(sizeof(gss_buffer_desc));
1199*7f2fe78bSCy Schubert 	if (*outbuf == NULL) {
1200*7f2fe78bSCy Schubert 		*minor_status = ENOMEM;
1201*7f2fe78bSCy Schubert 		goto errout;
1202*7f2fe78bSCy Schubert 	}
1203*7f2fe78bSCy Schubert 	(*outbuf)->value = (void *)t;
1204*7f2fe78bSCy Schubert 	(*outbuf)->length = tlen;
1205*7f2fe78bSCy Schubert 
1206*7f2fe78bSCy Schubert 	t = NULL; /* don't free */
1207*7f2fe78bSCy Schubert 
1208*7f2fe78bSCy Schubert 	*minor_status = 0;
1209*7f2fe78bSCy Schubert 	major_status = GSS_S_COMPLETE;
1210*7f2fe78bSCy Schubert 
1211*7f2fe78bSCy Schubert errout:
1212*7f2fe78bSCy Schubert 	if (t != NULL) {
1213*7f2fe78bSCy Schubert 		free(t);
1214*7f2fe78bSCy Schubert 	}
1215*7f2fe78bSCy Schubert 
1216*7f2fe78bSCy Schubert 	return (major_status);
1217*7f2fe78bSCy Schubert }
1218*7f2fe78bSCy Schubert 
1219*7f2fe78bSCy Schubert /*
1220*7f2fe78bSCy Schubert  * Create a new SPNEGO context handle for the initial call to
1221*7f2fe78bSCy Schubert  * spnego_gss_accept_sec_context() when the request is empty.  For empty
1222*7f2fe78bSCy Schubert  * requests, we implement the Microsoft NegHints extension to SPNEGO for
1223*7f2fe78bSCy Schubert  * compatibility with some versions of Samba.  See:
1224*7f2fe78bSCy Schubert  * https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-spng/8e71cf53-e867-4b79-b5b5-38c92be3d472
1225*7f2fe78bSCy Schubert  */
1226*7f2fe78bSCy Schubert static OM_uint32
acc_ctx_hints(OM_uint32 * minor_status,spnego_gss_cred_id_t spcred,gss_buffer_t * mechListMIC,OM_uint32 * negState,send_token_flag * return_token,spnego_gss_ctx_id_t * sc_out)1227*7f2fe78bSCy Schubert acc_ctx_hints(OM_uint32 *minor_status,
1228*7f2fe78bSCy Schubert 	      spnego_gss_cred_id_t spcred,
1229*7f2fe78bSCy Schubert 	      gss_buffer_t *mechListMIC,
1230*7f2fe78bSCy Schubert 	      OM_uint32 *negState,
1231*7f2fe78bSCy Schubert 	      send_token_flag *return_token,
1232*7f2fe78bSCy Schubert 	      spnego_gss_ctx_id_t *sc_out)
1233*7f2fe78bSCy Schubert {
1234*7f2fe78bSCy Schubert 	OM_uint32 ret;
1235*7f2fe78bSCy Schubert 	spnego_gss_ctx_id_t sc = NULL;
1236*7f2fe78bSCy Schubert 
1237*7f2fe78bSCy Schubert 	*mechListMIC = GSS_C_NO_BUFFER;
1238*7f2fe78bSCy Schubert 	*return_token = NO_TOKEN_SEND;
1239*7f2fe78bSCy Schubert 	*negState = REJECT;
1240*7f2fe78bSCy Schubert 	*minor_status = 0;
1241*7f2fe78bSCy Schubert 	*sc_out = NULL;
1242*7f2fe78bSCy Schubert 
1243*7f2fe78bSCy Schubert 	ret = make_NegHints(minor_status, mechListMIC);
1244*7f2fe78bSCy Schubert 	if (ret != GSS_S_COMPLETE)
1245*7f2fe78bSCy Schubert 		goto cleanup;
1246*7f2fe78bSCy Schubert 
1247*7f2fe78bSCy Schubert 	sc = create_spnego_ctx(0);
1248*7f2fe78bSCy Schubert 	if (sc == NULL) {
1249*7f2fe78bSCy Schubert 		ret = GSS_S_FAILURE;
1250*7f2fe78bSCy Schubert 		goto cleanup;
1251*7f2fe78bSCy Schubert 	}
1252*7f2fe78bSCy Schubert 
1253*7f2fe78bSCy Schubert 	ret = get_negotiable_mechs(minor_status, sc, spcred, GSS_C_ACCEPT);
1254*7f2fe78bSCy Schubert 	if (ret != GSS_S_COMPLETE)
1255*7f2fe78bSCy Schubert 		goto cleanup;
1256*7f2fe78bSCy Schubert 
1257*7f2fe78bSCy Schubert 	if (put_mech_set(sc->mech_set, &sc->DER_mechTypes) < 0) {
1258*7f2fe78bSCy Schubert 		ret = GSS_S_FAILURE;
1259*7f2fe78bSCy Schubert 		goto cleanup;
1260*7f2fe78bSCy Schubert 	}
1261*7f2fe78bSCy Schubert 	sc->internal_mech = GSS_C_NO_OID;
1262*7f2fe78bSCy Schubert 
1263*7f2fe78bSCy Schubert 	*negState = ACCEPT_INCOMPLETE;
1264*7f2fe78bSCy Schubert 	*return_token = INIT_TOKEN_SEND;
1265*7f2fe78bSCy Schubert 	sc->firstpass = 1;
1266*7f2fe78bSCy Schubert 	*sc_out = sc;
1267*7f2fe78bSCy Schubert 	sc = NULL;
1268*7f2fe78bSCy Schubert 	ret = GSS_S_COMPLETE;
1269*7f2fe78bSCy Schubert 
1270*7f2fe78bSCy Schubert cleanup:
1271*7f2fe78bSCy Schubert 	release_spnego_ctx(&sc);
1272*7f2fe78bSCy Schubert 
1273*7f2fe78bSCy Schubert 	return ret;
1274*7f2fe78bSCy Schubert }
1275*7f2fe78bSCy Schubert 
1276*7f2fe78bSCy Schubert /*
1277*7f2fe78bSCy Schubert  * Create a new SPNEGO context handle for the initial call to
1278*7f2fe78bSCy Schubert  * spnego_gss_accept_sec_context().  Set negState to REJECT if the token is
1279*7f2fe78bSCy Schubert  * defective, else ACCEPT_INCOMPLETE or REQUEST_MIC, depending on whether
1280*7f2fe78bSCy Schubert  * the initiator's preferred mechanism is supported.
1281*7f2fe78bSCy Schubert  */
1282*7f2fe78bSCy Schubert static OM_uint32
acc_ctx_new(OM_uint32 * minor_status,gss_buffer_t buf,spnego_gss_cred_id_t spcred,gss_buffer_t * mechToken,gss_buffer_t * mechListMIC,OM_uint32 * negState,send_token_flag * return_token,spnego_gss_ctx_id_t * sc_out)1283*7f2fe78bSCy Schubert acc_ctx_new(OM_uint32 *minor_status,
1284*7f2fe78bSCy Schubert 	    gss_buffer_t buf,
1285*7f2fe78bSCy Schubert 	    spnego_gss_cred_id_t spcred,
1286*7f2fe78bSCy Schubert 	    gss_buffer_t *mechToken,
1287*7f2fe78bSCy Schubert 	    gss_buffer_t *mechListMIC,
1288*7f2fe78bSCy Schubert 	    OM_uint32 *negState,
1289*7f2fe78bSCy Schubert 	    send_token_flag *return_token,
1290*7f2fe78bSCy Schubert 	    spnego_gss_ctx_id_t *sc_out)
1291*7f2fe78bSCy Schubert {
1292*7f2fe78bSCy Schubert 	OM_uint32 tmpmin, ret, req_flags;
1293*7f2fe78bSCy Schubert 	gss_OID_set mechTypes;
1294*7f2fe78bSCy Schubert 	gss_buffer_desc der_mechTypes;
1295*7f2fe78bSCy Schubert 	gss_OID mech_wanted;
1296*7f2fe78bSCy Schubert 	spnego_gss_ctx_id_t sc = NULL;
1297*7f2fe78bSCy Schubert 
1298*7f2fe78bSCy Schubert 	ret = GSS_S_DEFECTIVE_TOKEN;
1299*7f2fe78bSCy Schubert 	der_mechTypes.length = 0;
1300*7f2fe78bSCy Schubert 	der_mechTypes.value = NULL;
1301*7f2fe78bSCy Schubert 	*mechToken = *mechListMIC = GSS_C_NO_BUFFER;
1302*7f2fe78bSCy Schubert 	mechTypes = GSS_C_NO_OID_SET;
1303*7f2fe78bSCy Schubert 	*return_token = ERROR_TOKEN_SEND;
1304*7f2fe78bSCy Schubert 	*negState = REJECT;
1305*7f2fe78bSCy Schubert 	*minor_status = 0;
1306*7f2fe78bSCy Schubert 
1307*7f2fe78bSCy Schubert 	ret = get_negTokenInit(minor_status, buf, &der_mechTypes,
1308*7f2fe78bSCy Schubert 			       &mechTypes, &req_flags,
1309*7f2fe78bSCy Schubert 			       mechToken, mechListMIC);
1310*7f2fe78bSCy Schubert 	if (ret != GSS_S_COMPLETE) {
1311*7f2fe78bSCy Schubert 		goto cleanup;
1312*7f2fe78bSCy Schubert 	}
1313*7f2fe78bSCy Schubert 
1314*7f2fe78bSCy Schubert 	sc = create_spnego_ctx(0);
1315*7f2fe78bSCy Schubert 	if (sc == NULL) {
1316*7f2fe78bSCy Schubert 		ret = GSS_S_FAILURE;
1317*7f2fe78bSCy Schubert 		*return_token = NO_TOKEN_SEND;
1318*7f2fe78bSCy Schubert 		goto cleanup;
1319*7f2fe78bSCy Schubert 	}
1320*7f2fe78bSCy Schubert 
1321*7f2fe78bSCy Schubert 	ret = get_negotiable_mechs(minor_status, sc, spcred, GSS_C_ACCEPT);
1322*7f2fe78bSCy Schubert 	if (ret != GSS_S_COMPLETE) {
1323*7f2fe78bSCy Schubert 		*return_token = NO_TOKEN_SEND;
1324*7f2fe78bSCy Schubert 		goto cleanup;
1325*7f2fe78bSCy Schubert 	}
1326*7f2fe78bSCy Schubert 	/*
1327*7f2fe78bSCy Schubert 	 * Select the best match between the list of mechs
1328*7f2fe78bSCy Schubert 	 * that the initiator requested and the list that
1329*7f2fe78bSCy Schubert 	 * the acceptor will support.
1330*7f2fe78bSCy Schubert 	 */
1331*7f2fe78bSCy Schubert 	mech_wanted = negotiate_mech(sc, mechTypes, negState);
1332*7f2fe78bSCy Schubert 	if (*negState == REJECT) {
1333*7f2fe78bSCy Schubert 		ret = GSS_S_BAD_MECH;
1334*7f2fe78bSCy Schubert 		goto cleanup;
1335*7f2fe78bSCy Schubert 	}
1336*7f2fe78bSCy Schubert 
1337*7f2fe78bSCy Schubert 	sc->internal_mech = mech_wanted;
1338*7f2fe78bSCy Schubert 	sc->DER_mechTypes = der_mechTypes;
1339*7f2fe78bSCy Schubert 	der_mechTypes.length = 0;
1340*7f2fe78bSCy Schubert 	der_mechTypes.value = NULL;
1341*7f2fe78bSCy Schubert 
1342*7f2fe78bSCy Schubert 	if (*negState == REQUEST_MIC)
1343*7f2fe78bSCy Schubert 		sc->mic_reqd = 1;
1344*7f2fe78bSCy Schubert 
1345*7f2fe78bSCy Schubert 	*return_token = INIT_TOKEN_SEND;
1346*7f2fe78bSCy Schubert 	sc->firstpass = 1;
1347*7f2fe78bSCy Schubert 	*sc_out = sc;
1348*7f2fe78bSCy Schubert 	sc = NULL;
1349*7f2fe78bSCy Schubert 	ret = GSS_S_COMPLETE;
1350*7f2fe78bSCy Schubert 
1351*7f2fe78bSCy Schubert cleanup:
1352*7f2fe78bSCy Schubert 	release_spnego_ctx(&sc);
1353*7f2fe78bSCy Schubert 	gss_release_oid_set(&tmpmin, &mechTypes);
1354*7f2fe78bSCy Schubert 	if (der_mechTypes.length != 0)
1355*7f2fe78bSCy Schubert 		gss_release_buffer(&tmpmin, &der_mechTypes);
1356*7f2fe78bSCy Schubert 
1357*7f2fe78bSCy Schubert 	return ret;
1358*7f2fe78bSCy Schubert }
1359*7f2fe78bSCy Schubert 
1360*7f2fe78bSCy Schubert static OM_uint32
acc_ctx_cont(OM_uint32 * minstat,gss_buffer_t buf,spnego_gss_ctx_id_t sc,gss_buffer_t * responseToken,gss_buffer_t * mechListMIC,OM_uint32 * negState,send_token_flag * return_token)1361*7f2fe78bSCy Schubert acc_ctx_cont(OM_uint32 *minstat,
1362*7f2fe78bSCy Schubert 	     gss_buffer_t buf,
1363*7f2fe78bSCy Schubert 	     spnego_gss_ctx_id_t sc,
1364*7f2fe78bSCy Schubert 	     gss_buffer_t *responseToken,
1365*7f2fe78bSCy Schubert 	     gss_buffer_t *mechListMIC,
1366*7f2fe78bSCy Schubert 	     OM_uint32 *negState,
1367*7f2fe78bSCy Schubert 	     send_token_flag *return_token)
1368*7f2fe78bSCy Schubert {
1369*7f2fe78bSCy Schubert 	OM_uint32 ret, tmpmin;
1370*7f2fe78bSCy Schubert 	gss_OID supportedMech;
1371*7f2fe78bSCy Schubert 	struct k5input in;
1372*7f2fe78bSCy Schubert 
1373*7f2fe78bSCy Schubert 	ret = GSS_S_DEFECTIVE_TOKEN;
1374*7f2fe78bSCy Schubert 	*negState = REJECT;
1375*7f2fe78bSCy Schubert 	*minstat = 0;
1376*7f2fe78bSCy Schubert 	supportedMech = GSS_C_NO_OID;
1377*7f2fe78bSCy Schubert 	*return_token = ERROR_TOKEN_SEND;
1378*7f2fe78bSCy Schubert 	*responseToken = *mechListMIC = GSS_C_NO_BUFFER;
1379*7f2fe78bSCy Schubert 
1380*7f2fe78bSCy Schubert 	k5_input_init(&in, buf->value, buf->length);
1381*7f2fe78bSCy Schubert 
1382*7f2fe78bSCy Schubert 	/* Attempt to work with old Sun SPNEGO. */
1383*7f2fe78bSCy Schubert 	if (in.len > 0 && *in.ptr == HEADER_ID) {
1384*7f2fe78bSCy Schubert 		ret = verify_token_header(&in, gss_mech_spnego);
1385*7f2fe78bSCy Schubert 		if (ret) {
1386*7f2fe78bSCy Schubert 			*minstat = ret;
1387*7f2fe78bSCy Schubert 			return GSS_S_DEFECTIVE_TOKEN;
1388*7f2fe78bSCy Schubert 		}
1389*7f2fe78bSCy Schubert 	}
1390*7f2fe78bSCy Schubert 
1391*7f2fe78bSCy Schubert 	ret = get_negTokenResp(minstat, &in, negState, &supportedMech,
1392*7f2fe78bSCy Schubert 			       responseToken, mechListMIC);
1393*7f2fe78bSCy Schubert 	if (ret != GSS_S_COMPLETE)
1394*7f2fe78bSCy Schubert 		goto cleanup;
1395*7f2fe78bSCy Schubert 
1396*7f2fe78bSCy Schubert 	if (*responseToken == GSS_C_NO_BUFFER &&
1397*7f2fe78bSCy Schubert 	    *mechListMIC == GSS_C_NO_BUFFER) {
1398*7f2fe78bSCy Schubert 
1399*7f2fe78bSCy Schubert 		ret = GSS_S_DEFECTIVE_TOKEN;
1400*7f2fe78bSCy Schubert 		goto cleanup;
1401*7f2fe78bSCy Schubert 	}
1402*7f2fe78bSCy Schubert 	if (supportedMech != GSS_C_NO_OID) {
1403*7f2fe78bSCy Schubert 		ret = GSS_S_DEFECTIVE_TOKEN;
1404*7f2fe78bSCy Schubert 		goto cleanup;
1405*7f2fe78bSCy Schubert 	}
1406*7f2fe78bSCy Schubert 	sc->firstpass = 0;
1407*7f2fe78bSCy Schubert 	*negState = ACCEPT_INCOMPLETE;
1408*7f2fe78bSCy Schubert 	*return_token = CONT_TOKEN_SEND;
1409*7f2fe78bSCy Schubert cleanup:
1410*7f2fe78bSCy Schubert 	if (supportedMech != GSS_C_NO_OID) {
1411*7f2fe78bSCy Schubert 		generic_gss_release_oid(&tmpmin, &supportedMech);
1412*7f2fe78bSCy Schubert 	}
1413*7f2fe78bSCy Schubert 	return ret;
1414*7f2fe78bSCy Schubert }
1415*7f2fe78bSCy Schubert 
1416*7f2fe78bSCy Schubert /*
1417*7f2fe78bSCy Schubert  * Verify that mech OID is either exactly the same as the negotiated
1418*7f2fe78bSCy Schubert  * mech OID, or is a mech OID supported by the negotiated mech.  MS
1419*7f2fe78bSCy Schubert  * implementations can list a most preferred mech using an incorrect
1420*7f2fe78bSCy Schubert  * krb5 OID while emitting a krb5 initiator mech token having the
1421*7f2fe78bSCy Schubert  * correct krb5 mech OID.
1422*7f2fe78bSCy Schubert  */
1423*7f2fe78bSCy Schubert static OM_uint32
acc_ctx_vfy_oid(OM_uint32 * minor_status,spnego_gss_ctx_id_t sc,gss_OID mechoid,OM_uint32 * negState,send_token_flag * tokflag)1424*7f2fe78bSCy Schubert acc_ctx_vfy_oid(OM_uint32 *minor_status,
1425*7f2fe78bSCy Schubert 		spnego_gss_ctx_id_t sc, gss_OID mechoid,
1426*7f2fe78bSCy Schubert 		OM_uint32 *negState, send_token_flag *tokflag)
1427*7f2fe78bSCy Schubert {
1428*7f2fe78bSCy Schubert 	OM_uint32 ret, tmpmin;
1429*7f2fe78bSCy Schubert 	gss_mechanism mech = NULL;
1430*7f2fe78bSCy Schubert 	gss_OID_set mech_set = GSS_C_NO_OID_SET;
1431*7f2fe78bSCy Schubert 	int present = 0;
1432*7f2fe78bSCy Schubert 
1433*7f2fe78bSCy Schubert 	if (g_OID_equal(sc->internal_mech, mechoid))
1434*7f2fe78bSCy Schubert 		return GSS_S_COMPLETE;
1435*7f2fe78bSCy Schubert 
1436*7f2fe78bSCy Schubert 	mech = gssint_get_mechanism(sc->internal_mech);
1437*7f2fe78bSCy Schubert 	if (mech == NULL || mech->gss_indicate_mechs == NULL) {
1438*7f2fe78bSCy Schubert 		*minor_status = ERR_SPNEGO_NEGOTIATION_FAILED;
1439*7f2fe78bSCy Schubert 		map_errcode(minor_status);
1440*7f2fe78bSCy Schubert 		*negState = REJECT;
1441*7f2fe78bSCy Schubert 		*tokflag = ERROR_TOKEN_SEND;
1442*7f2fe78bSCy Schubert 		return GSS_S_BAD_MECH;
1443*7f2fe78bSCy Schubert 	}
1444*7f2fe78bSCy Schubert 	ret = mech->gss_indicate_mechs(minor_status, &mech_set);
1445*7f2fe78bSCy Schubert 	if (ret != GSS_S_COMPLETE) {
1446*7f2fe78bSCy Schubert 		*tokflag = NO_TOKEN_SEND;
1447*7f2fe78bSCy Schubert 		map_error(minor_status, mech);
1448*7f2fe78bSCy Schubert 		goto cleanup;
1449*7f2fe78bSCy Schubert 	}
1450*7f2fe78bSCy Schubert 	ret = gss_test_oid_set_member(minor_status, mechoid,
1451*7f2fe78bSCy Schubert 				      mech_set, &present);
1452*7f2fe78bSCy Schubert 	if (ret != GSS_S_COMPLETE)
1453*7f2fe78bSCy Schubert 		goto cleanup;
1454*7f2fe78bSCy Schubert 	if (!present) {
1455*7f2fe78bSCy Schubert 		*minor_status = ERR_SPNEGO_NEGOTIATION_FAILED;
1456*7f2fe78bSCy Schubert 		map_errcode(minor_status);
1457*7f2fe78bSCy Schubert 		*negState = REJECT;
1458*7f2fe78bSCy Schubert 		*tokflag = ERROR_TOKEN_SEND;
1459*7f2fe78bSCy Schubert 		ret = GSS_S_BAD_MECH;
1460*7f2fe78bSCy Schubert 	}
1461*7f2fe78bSCy Schubert cleanup:
1462*7f2fe78bSCy Schubert 	gss_release_oid_set(&tmpmin, &mech_set);
1463*7f2fe78bSCy Schubert 	return ret;
1464*7f2fe78bSCy Schubert }
1465*7f2fe78bSCy Schubert #ifndef LEAN_CLIENT
1466*7f2fe78bSCy Schubert /*
1467*7f2fe78bSCy Schubert  * Wrap call to gss_accept_sec_context() and update state
1468*7f2fe78bSCy Schubert  * accordingly.
1469*7f2fe78bSCy Schubert  */
1470*7f2fe78bSCy Schubert static OM_uint32
acc_ctx_call_acc(OM_uint32 * minor_status,spnego_gss_ctx_id_t sc,spnego_gss_cred_id_t spcred,gss_buffer_t mechtok_in,gss_channel_bindings_t bindings,gss_buffer_t mechtok_out,OM_uint32 * time_rec,OM_uint32 * negState,send_token_flag * tokflag)1471*7f2fe78bSCy Schubert acc_ctx_call_acc(OM_uint32 *minor_status, spnego_gss_ctx_id_t sc,
1472*7f2fe78bSCy Schubert 		 spnego_gss_cred_id_t spcred, gss_buffer_t mechtok_in,
1473*7f2fe78bSCy Schubert 		 gss_channel_bindings_t bindings, gss_buffer_t mechtok_out,
1474*7f2fe78bSCy Schubert 		 OM_uint32 *time_rec, OM_uint32 *negState,
1475*7f2fe78bSCy Schubert 		 send_token_flag *tokflag)
1476*7f2fe78bSCy Schubert {
1477*7f2fe78bSCy Schubert 	OM_uint32 ret, tmpmin;
1478*7f2fe78bSCy Schubert 	gss_OID_desc mechoid;
1479*7f2fe78bSCy Schubert 	gss_cred_id_t mcred;
1480*7f2fe78bSCy Schubert 	int negoex = gss_oid_equal(sc->internal_mech, &negoex_mech);
1481*7f2fe78bSCy Schubert 
1482*7f2fe78bSCy Schubert 	if (sc->ctx_handle == GSS_C_NO_CONTEXT && !negoex) {
1483*7f2fe78bSCy Schubert 		/*
1484*7f2fe78bSCy Schubert 		 * mechoid is an alias; don't free it.
1485*7f2fe78bSCy Schubert 		 */
1486*7f2fe78bSCy Schubert 		ret = gssint_get_mech_type(&mechoid, mechtok_in);
1487*7f2fe78bSCy Schubert 		if (ret != GSS_S_COMPLETE) {
1488*7f2fe78bSCy Schubert 			*tokflag = NO_TOKEN_SEND;
1489*7f2fe78bSCy Schubert 			return ret;
1490*7f2fe78bSCy Schubert 		}
1491*7f2fe78bSCy Schubert 		ret = acc_ctx_vfy_oid(minor_status, sc, &mechoid,
1492*7f2fe78bSCy Schubert 				      negState, tokflag);
1493*7f2fe78bSCy Schubert 		if (ret != GSS_S_COMPLETE)
1494*7f2fe78bSCy Schubert 			return ret;
1495*7f2fe78bSCy Schubert 	}
1496*7f2fe78bSCy Schubert 
1497*7f2fe78bSCy Schubert 	mcred = (spcred == NULL) ? GSS_C_NO_CREDENTIAL : spcred->mcred;
1498*7f2fe78bSCy Schubert 	if (negoex) {
1499*7f2fe78bSCy Schubert 		ret = negoex_accept(minor_status, sc, mcred, mechtok_in,
1500*7f2fe78bSCy Schubert 				    bindings, mechtok_out, time_rec);
1501*7f2fe78bSCy Schubert 	} else {
1502*7f2fe78bSCy Schubert 		(void) gss_release_name(&tmpmin, &sc->internal_name);
1503*7f2fe78bSCy Schubert 		(void) gss_release_cred(&tmpmin, &sc->deleg_cred);
1504*7f2fe78bSCy Schubert 		ret = gss_accept_sec_context(minor_status, &sc->ctx_handle,
1505*7f2fe78bSCy Schubert 					     mcred, mechtok_in, bindings,
1506*7f2fe78bSCy Schubert 					     &sc->internal_name,
1507*7f2fe78bSCy Schubert 					     &sc->actual_mech, mechtok_out,
1508*7f2fe78bSCy Schubert 					     &sc->ctx_flags, time_rec,
1509*7f2fe78bSCy Schubert 					     &sc->deleg_cred);
1510*7f2fe78bSCy Schubert 	}
1511*7f2fe78bSCy Schubert 	if (ret == GSS_S_COMPLETE) {
1512*7f2fe78bSCy Schubert #ifdef MS_BUG_TEST
1513*7f2fe78bSCy Schubert 		/*
1514*7f2fe78bSCy Schubert 		 * Force MIC to be not required even if we previously
1515*7f2fe78bSCy Schubert 		 * requested a MIC.
1516*7f2fe78bSCy Schubert 		 */
1517*7f2fe78bSCy Schubert 		char *envstr = getenv("MS_FORCE_NO_MIC");
1518*7f2fe78bSCy Schubert 
1519*7f2fe78bSCy Schubert 		if (envstr != NULL && strcmp(envstr, "1") == 0 &&
1520*7f2fe78bSCy Schubert 		    !(sc->ctx_flags & GSS_C_MUTUAL_FLAG) &&
1521*7f2fe78bSCy Schubert 		    sc->mic_reqd) {
1522*7f2fe78bSCy Schubert 
1523*7f2fe78bSCy Schubert 			sc->mic_reqd = 0;
1524*7f2fe78bSCy Schubert 		}
1525*7f2fe78bSCy Schubert #endif
1526*7f2fe78bSCy Schubert 		sc->mech_complete = 1;
1527*7f2fe78bSCy Schubert 
1528*7f2fe78bSCy Schubert 		if (!sc->mic_reqd ||
1529*7f2fe78bSCy Schubert 		    !(sc->ctx_flags & GSS_C_INTEG_FLAG)) {
1530*7f2fe78bSCy Schubert 			/* No MIC exchange required, so we're done. */
1531*7f2fe78bSCy Schubert 			*negState = ACCEPT_COMPLETE;
1532*7f2fe78bSCy Schubert 			ret = GSS_S_COMPLETE;
1533*7f2fe78bSCy Schubert 		} else {
1534*7f2fe78bSCy Schubert 			/* handle_mic will decide if we're done. */
1535*7f2fe78bSCy Schubert 			ret = GSS_S_CONTINUE_NEEDED;
1536*7f2fe78bSCy Schubert 		}
1537*7f2fe78bSCy Schubert 	} else if (ret != GSS_S_CONTINUE_NEEDED) {
1538*7f2fe78bSCy Schubert 		*negState = REJECT;
1539*7f2fe78bSCy Schubert 		*tokflag = ERROR_TOKEN_SEND;
1540*7f2fe78bSCy Schubert 	}
1541*7f2fe78bSCy Schubert 	return ret;
1542*7f2fe78bSCy Schubert }
1543*7f2fe78bSCy Schubert 
1544*7f2fe78bSCy Schubert /*ARGSUSED*/
1545*7f2fe78bSCy Schubert OM_uint32 KRB5_CALLCONV
spnego_gss_accept_sec_context(OM_uint32 * minor_status,gss_ctx_id_t * context_handle,gss_cred_id_t verifier_cred_handle,gss_buffer_t input_token,gss_channel_bindings_t bindings,gss_name_t * src_name,gss_OID * mech_type,gss_buffer_t output_token,OM_uint32 * ret_flags,OM_uint32 * time_rec,gss_cred_id_t * delegated_cred_handle)1546*7f2fe78bSCy Schubert spnego_gss_accept_sec_context(
1547*7f2fe78bSCy Schubert 			    OM_uint32 *minor_status,
1548*7f2fe78bSCy Schubert 			    gss_ctx_id_t *context_handle,
1549*7f2fe78bSCy Schubert 			    gss_cred_id_t verifier_cred_handle,
1550*7f2fe78bSCy Schubert 			    gss_buffer_t input_token,
1551*7f2fe78bSCy Schubert 			    gss_channel_bindings_t bindings,
1552*7f2fe78bSCy Schubert 			    gss_name_t *src_name,
1553*7f2fe78bSCy Schubert 			    gss_OID *mech_type,
1554*7f2fe78bSCy Schubert 			    gss_buffer_t output_token,
1555*7f2fe78bSCy Schubert 			    OM_uint32 *ret_flags,
1556*7f2fe78bSCy Schubert 			    OM_uint32 *time_rec,
1557*7f2fe78bSCy Schubert 			    gss_cred_id_t *delegated_cred_handle)
1558*7f2fe78bSCy Schubert {
1559*7f2fe78bSCy Schubert 	OM_uint32 ret, tmpmin, negState;
1560*7f2fe78bSCy Schubert 	send_token_flag return_token;
1561*7f2fe78bSCy Schubert 	gss_buffer_t mechtok_in, mic_in, mic_out;
1562*7f2fe78bSCy Schubert 	gss_buffer_desc mechtok_out = GSS_C_EMPTY_BUFFER;
1563*7f2fe78bSCy Schubert 	spnego_gss_ctx_id_t sc = NULL;
1564*7f2fe78bSCy Schubert 	spnego_gss_cred_id_t spcred = NULL;
1565*7f2fe78bSCy Schubert 	int sendTokenInit = 0, tmpret;
1566*7f2fe78bSCy Schubert 
1567*7f2fe78bSCy Schubert 	mechtok_in = mic_in = mic_out = GSS_C_NO_BUFFER;
1568*7f2fe78bSCy Schubert 
1569*7f2fe78bSCy Schubert 	/*
1570*7f2fe78bSCy Schubert 	 * This function works in three steps:
1571*7f2fe78bSCy Schubert 	 *
1572*7f2fe78bSCy Schubert 	 *   1. Perform mechanism negotiation.
1573*7f2fe78bSCy Schubert 	 *   2. Invoke the negotiated mech's gss_accept_sec_context function
1574*7f2fe78bSCy Schubert 	 *      and examine the results.
1575*7f2fe78bSCy Schubert 	 *   3. Process or generate MICs if necessary.
1576*7f2fe78bSCy Schubert 	 *
1577*7f2fe78bSCy Schubert 	 * Step one determines whether the negotiation requires a MIC exchange,
1578*7f2fe78bSCy Schubert 	 * while steps two and three share responsibility for determining when
1579*7f2fe78bSCy Schubert 	 * the exchange is complete.  If the selected mech completes in this
1580*7f2fe78bSCy Schubert 	 * call and no MIC exchange is expected, then step 2 will decide.  If a
1581*7f2fe78bSCy Schubert 	 * MIC exchange is expected, then step 3 will decide.  If an error
1582*7f2fe78bSCy Schubert 	 * occurs in any step, the exchange will be aborted, possibly with an
1583*7f2fe78bSCy Schubert 	 * error token.
1584*7f2fe78bSCy Schubert 	 *
1585*7f2fe78bSCy Schubert 	 * negState determines the state of the negotiation, and is
1586*7f2fe78bSCy Schubert 	 * communicated to the acceptor if a continuing token is sent.
1587*7f2fe78bSCy Schubert 	 * return_token is used to indicate what type of token, if any, should
1588*7f2fe78bSCy Schubert 	 * be generated.
1589*7f2fe78bSCy Schubert 	 */
1590*7f2fe78bSCy Schubert 
1591*7f2fe78bSCy Schubert 	/* Validate arguments. */
1592*7f2fe78bSCy Schubert 	if (minor_status != NULL)
1593*7f2fe78bSCy Schubert 		*minor_status = 0;
1594*7f2fe78bSCy Schubert 	if (output_token != GSS_C_NO_BUFFER) {
1595*7f2fe78bSCy Schubert 		output_token->length = 0;
1596*7f2fe78bSCy Schubert 		output_token->value = NULL;
1597*7f2fe78bSCy Schubert 	}
1598*7f2fe78bSCy Schubert 	if (src_name != NULL)
1599*7f2fe78bSCy Schubert 		*src_name = GSS_C_NO_NAME;
1600*7f2fe78bSCy Schubert 	if (mech_type != NULL)
1601*7f2fe78bSCy Schubert 		*mech_type = GSS_C_NO_OID;
1602*7f2fe78bSCy Schubert 	if (time_rec != NULL)
1603*7f2fe78bSCy Schubert 		*time_rec = 0;
1604*7f2fe78bSCy Schubert 	if (ret_flags != NULL)
1605*7f2fe78bSCy Schubert 		*ret_flags = 0;
1606*7f2fe78bSCy Schubert 	if (delegated_cred_handle != NULL)
1607*7f2fe78bSCy Schubert 		*delegated_cred_handle = GSS_C_NO_CREDENTIAL;
1608*7f2fe78bSCy Schubert 
1609*7f2fe78bSCy Schubert 	if (minor_status == NULL ||
1610*7f2fe78bSCy Schubert 	    output_token == GSS_C_NO_BUFFER ||
1611*7f2fe78bSCy Schubert 	    context_handle == NULL)
1612*7f2fe78bSCy Schubert 		return GSS_S_CALL_INACCESSIBLE_WRITE;
1613*7f2fe78bSCy Schubert 
1614*7f2fe78bSCy Schubert 	if (input_token == GSS_C_NO_BUFFER)
1615*7f2fe78bSCy Schubert 		return GSS_S_CALL_INACCESSIBLE_READ;
1616*7f2fe78bSCy Schubert 
1617*7f2fe78bSCy Schubert 	/* Step 1: Perform mechanism negotiation. */
1618*7f2fe78bSCy Schubert 	sc = (spnego_gss_ctx_id_t)*context_handle;
1619*7f2fe78bSCy Schubert 	spcred = (spnego_gss_cred_id_t)verifier_cred_handle;
1620*7f2fe78bSCy Schubert 	if (sc == NULL && input_token->length == 0) {
1621*7f2fe78bSCy Schubert 		/* Process a request for NegHints. */
1622*7f2fe78bSCy Schubert 		ret = acc_ctx_hints(minor_status, spcred, &mic_out, &negState,
1623*7f2fe78bSCy Schubert 				    &return_token, &sc);
1624*7f2fe78bSCy Schubert 		if (ret != GSS_S_COMPLETE)
1625*7f2fe78bSCy Schubert 			goto cleanup;
1626*7f2fe78bSCy Schubert 		*context_handle = (gss_ctx_id_t)sc;
1627*7f2fe78bSCy Schubert 		sendTokenInit = 1;
1628*7f2fe78bSCy Schubert 		ret = GSS_S_CONTINUE_NEEDED;
1629*7f2fe78bSCy Schubert 	} else if (sc == NULL || sc->internal_mech == GSS_C_NO_OID) {
1630*7f2fe78bSCy Schubert 		if (sc != NULL) {
1631*7f2fe78bSCy Schubert 			/* Discard the context from the NegHints request. */
1632*7f2fe78bSCy Schubert 			release_spnego_ctx(&sc);
1633*7f2fe78bSCy Schubert 			*context_handle = GSS_C_NO_CONTEXT;
1634*7f2fe78bSCy Schubert 		}
1635*7f2fe78bSCy Schubert 		/* Process an initial token; can set negState to
1636*7f2fe78bSCy Schubert 		 * REQUEST_MIC. */
1637*7f2fe78bSCy Schubert 		ret = acc_ctx_new(minor_status, input_token, spcred,
1638*7f2fe78bSCy Schubert 				  &mechtok_in, &mic_in, &negState,
1639*7f2fe78bSCy Schubert 				  &return_token, &sc);
1640*7f2fe78bSCy Schubert 		if (ret != GSS_S_COMPLETE)
1641*7f2fe78bSCy Schubert 			goto cleanup;
1642*7f2fe78bSCy Schubert 		*context_handle = (gss_ctx_id_t)sc;
1643*7f2fe78bSCy Schubert 		ret = GSS_S_CONTINUE_NEEDED;
1644*7f2fe78bSCy Schubert 	} else {
1645*7f2fe78bSCy Schubert 		/* Process a response token.  Can set negState to
1646*7f2fe78bSCy Schubert 		 * ACCEPT_INCOMPLETE. */
1647*7f2fe78bSCy Schubert 		ret = acc_ctx_cont(minor_status, input_token, sc, &mechtok_in,
1648*7f2fe78bSCy Schubert 				   &mic_in, &negState, &return_token);
1649*7f2fe78bSCy Schubert 		if (ret != GSS_S_COMPLETE)
1650*7f2fe78bSCy Schubert 			goto cleanup;
1651*7f2fe78bSCy Schubert 		ret = GSS_S_CONTINUE_NEEDED;
1652*7f2fe78bSCy Schubert 	}
1653*7f2fe78bSCy Schubert 
1654*7f2fe78bSCy Schubert 	/* Step 2: invoke the negotiated mechanism's gss_accept_sec_context
1655*7f2fe78bSCy Schubert 	 * function. */
1656*7f2fe78bSCy Schubert 	/*
1657*7f2fe78bSCy Schubert 	 * Handle mechtok_in and mic_in only if they are
1658*7f2fe78bSCy Schubert 	 * present in input_token.  If neither is present, whether
1659*7f2fe78bSCy Schubert 	 * this is an error depends on whether this is the first
1660*7f2fe78bSCy Schubert 	 * round-trip.  RET is set to a default value according to
1661*7f2fe78bSCy Schubert 	 * whether it is the first round-trip.
1662*7f2fe78bSCy Schubert 	 */
1663*7f2fe78bSCy Schubert 	if (negState != REQUEST_MIC && mechtok_in != GSS_C_NO_BUFFER) {
1664*7f2fe78bSCy Schubert 		ret = acc_ctx_call_acc(minor_status, sc, spcred, mechtok_in,
1665*7f2fe78bSCy Schubert 				       bindings, &mechtok_out, time_rec,
1666*7f2fe78bSCy Schubert 				       &negState, &return_token);
1667*7f2fe78bSCy Schubert 	}
1668*7f2fe78bSCy Schubert 
1669*7f2fe78bSCy Schubert 	/* Step 3: process or generate the MIC, if the negotiated mech is
1670*7f2fe78bSCy Schubert 	 * complete and supports MICs. */
1671*7f2fe78bSCy Schubert 	if (!HARD_ERROR(ret) && sc->mech_complete &&
1672*7f2fe78bSCy Schubert 	    (sc->ctx_flags & GSS_C_INTEG_FLAG)) {
1673*7f2fe78bSCy Schubert 
1674*7f2fe78bSCy Schubert 		ret = handle_mic(minor_status, mic_in,
1675*7f2fe78bSCy Schubert 				 (mechtok_out.length != 0),
1676*7f2fe78bSCy Schubert 				 sc, &mic_out,
1677*7f2fe78bSCy Schubert 				 &negState, &return_token);
1678*7f2fe78bSCy Schubert 	}
1679*7f2fe78bSCy Schubert 
1680*7f2fe78bSCy Schubert 	if (!HARD_ERROR(ret) && ret_flags != NULL)
1681*7f2fe78bSCy Schubert 		*ret_flags = sc->ctx_flags & ~GSS_C_PROT_READY_FLAG;
1682*7f2fe78bSCy Schubert 
1683*7f2fe78bSCy Schubert cleanup:
1684*7f2fe78bSCy Schubert 	if (return_token == INIT_TOKEN_SEND && sendTokenInit) {
1685*7f2fe78bSCy Schubert 		assert(sc != NULL);
1686*7f2fe78bSCy Schubert 		tmpret = make_spnego_tokenInit_msg(sc, 1, mic_out, 0,
1687*7f2fe78bSCy Schubert 						   GSS_C_NO_BUFFER,
1688*7f2fe78bSCy Schubert 						   return_token, output_token);
1689*7f2fe78bSCy Schubert 		if (tmpret < 0)
1690*7f2fe78bSCy Schubert 			ret = GSS_S_FAILURE;
1691*7f2fe78bSCy Schubert 	} else if (return_token != NO_TOKEN_SEND &&
1692*7f2fe78bSCy Schubert 		   return_token != CHECK_MIC) {
1693*7f2fe78bSCy Schubert 		tmpret = make_spnego_tokenTarg_msg(negState,
1694*7f2fe78bSCy Schubert 						   sc ? sc->internal_mech :
1695*7f2fe78bSCy Schubert 						   GSS_C_NO_OID,
1696*7f2fe78bSCy Schubert 						   &mechtok_out, mic_out,
1697*7f2fe78bSCy Schubert 						   return_token,
1698*7f2fe78bSCy Schubert 						   output_token);
1699*7f2fe78bSCy Schubert 		if (tmpret < 0)
1700*7f2fe78bSCy Schubert 			ret = GSS_S_FAILURE;
1701*7f2fe78bSCy Schubert 	}
1702*7f2fe78bSCy Schubert 	if (ret == GSS_S_COMPLETE) {
1703*7f2fe78bSCy Schubert 		sc->opened = 1;
1704*7f2fe78bSCy Schubert 		if (sc->internal_name != GSS_C_NO_NAME &&
1705*7f2fe78bSCy Schubert 		    src_name != NULL) {
1706*7f2fe78bSCy Schubert 			*src_name = sc->internal_name;
1707*7f2fe78bSCy Schubert 			sc->internal_name = GSS_C_NO_NAME;
1708*7f2fe78bSCy Schubert 		}
1709*7f2fe78bSCy Schubert 		if (mech_type != NULL)
1710*7f2fe78bSCy Schubert 			*mech_type = sc->actual_mech;
1711*7f2fe78bSCy Schubert 		/* Get an updated lifetime if we didn't call into the mech. */
1712*7f2fe78bSCy Schubert 		if (time_rec != NULL && *time_rec == 0) {
1713*7f2fe78bSCy Schubert 			(void) gss_context_time(&tmpmin, sc->ctx_handle,
1714*7f2fe78bSCy Schubert 						time_rec);
1715*7f2fe78bSCy Schubert 		}
1716*7f2fe78bSCy Schubert 		if (delegated_cred_handle != NULL) {
1717*7f2fe78bSCy Schubert 			*delegated_cred_handle = sc->deleg_cred;
1718*7f2fe78bSCy Schubert 			sc->deleg_cred = GSS_C_NO_CREDENTIAL;
1719*7f2fe78bSCy Schubert 		}
1720*7f2fe78bSCy Schubert 	} else if (ret != GSS_S_CONTINUE_NEEDED) {
1721*7f2fe78bSCy Schubert 		if (sc != NULL) {
1722*7f2fe78bSCy Schubert 			gss_delete_sec_context(&tmpmin, &sc->ctx_handle,
1723*7f2fe78bSCy Schubert 					       GSS_C_NO_BUFFER);
1724*7f2fe78bSCy Schubert 			release_spnego_ctx(&sc);
1725*7f2fe78bSCy Schubert 		}
1726*7f2fe78bSCy Schubert 		*context_handle = GSS_C_NO_CONTEXT;
1727*7f2fe78bSCy Schubert 	}
1728*7f2fe78bSCy Schubert 	gss_release_buffer(&tmpmin, &mechtok_out);
1729*7f2fe78bSCy Schubert 	if (mechtok_in != GSS_C_NO_BUFFER) {
1730*7f2fe78bSCy Schubert 		gss_release_buffer(&tmpmin, mechtok_in);
1731*7f2fe78bSCy Schubert 		free(mechtok_in);
1732*7f2fe78bSCy Schubert 	}
1733*7f2fe78bSCy Schubert 	if (mic_in != GSS_C_NO_BUFFER) {
1734*7f2fe78bSCy Schubert 		gss_release_buffer(&tmpmin, mic_in);
1735*7f2fe78bSCy Schubert 		free(mic_in);
1736*7f2fe78bSCy Schubert 	}
1737*7f2fe78bSCy Schubert 	if (mic_out != GSS_C_NO_BUFFER) {
1738*7f2fe78bSCy Schubert 		gss_release_buffer(&tmpmin, mic_out);
1739*7f2fe78bSCy Schubert 		free(mic_out);
1740*7f2fe78bSCy Schubert 	}
1741*7f2fe78bSCy Schubert 	return ret;
1742*7f2fe78bSCy Schubert }
1743*7f2fe78bSCy Schubert #endif /*  LEAN_CLIENT */
1744*7f2fe78bSCy Schubert 
1745*7f2fe78bSCy Schubert static struct {
1746*7f2fe78bSCy Schubert 	OM_uint32 status;
1747*7f2fe78bSCy Schubert 	const char *msg;
1748*7f2fe78bSCy Schubert } msg_table[] = {
1749*7f2fe78bSCy Schubert 	{ ERR_SPNEGO_NO_MECHS_AVAILABLE,
1750*7f2fe78bSCy Schubert 	  N_("SPNEGO cannot find mechanisms to negotiate") },
1751*7f2fe78bSCy Schubert 	{ ERR_SPNEGO_NO_CREDS_ACQUIRED,
1752*7f2fe78bSCy Schubert 	  N_("SPNEGO failed to acquire creds") },
1753*7f2fe78bSCy Schubert 	{ ERR_SPNEGO_NO_MECH_FROM_ACCEPTOR,
1754*7f2fe78bSCy Schubert 	  N_("SPNEGO acceptor did not select a mechanism") },
1755*7f2fe78bSCy Schubert 	{ ERR_SPNEGO_NEGOTIATION_FAILED,
1756*7f2fe78bSCy Schubert 	  N_("SPNEGO failed to negotiate a mechanism") },
1757*7f2fe78bSCy Schubert 	{ ERR_SPNEGO_NO_TOKEN_FROM_ACCEPTOR,
1758*7f2fe78bSCy Schubert 	  N_("SPNEGO acceptor did not return a valid token") },
1759*7f2fe78bSCy Schubert 	{ ERR_NEGOEX_INVALID_MESSAGE_SIGNATURE,
1760*7f2fe78bSCy Schubert 	  N_("Invalid NegoEx signature") },
1761*7f2fe78bSCy Schubert 	{ ERR_NEGOEX_INVALID_MESSAGE_TYPE,
1762*7f2fe78bSCy Schubert 	  N_("Invalid NegoEx message type") },
1763*7f2fe78bSCy Schubert 	{ ERR_NEGOEX_INVALID_MESSAGE_SIZE,
1764*7f2fe78bSCy Schubert 	  N_("Invalid NegoEx message size") },
1765*7f2fe78bSCy Schubert 	{ ERR_NEGOEX_INVALID_CONVERSATION_ID,
1766*7f2fe78bSCy Schubert 	  N_("Invalid NegoEx conversation ID") },
1767*7f2fe78bSCy Schubert 	{ ERR_NEGOEX_AUTH_SCHEME_NOT_FOUND,
1768*7f2fe78bSCy Schubert 	  N_("NegoEx authentication scheme not found") },
1769*7f2fe78bSCy Schubert 	{ ERR_NEGOEX_MISSING_NEGO_MESSAGE,
1770*7f2fe78bSCy Schubert 	  N_("Missing NegoEx negotiate message") },
1771*7f2fe78bSCy Schubert 	{ ERR_NEGOEX_MISSING_AP_REQUEST_MESSAGE,
1772*7f2fe78bSCy Schubert 	  N_("Missing NegoEx authentication protocol request message") },
1773*7f2fe78bSCy Schubert 	{ ERR_NEGOEX_NO_AVAILABLE_MECHS,
1774*7f2fe78bSCy Schubert 	  N_("No mutually supported NegoEx authentication schemes") },
1775*7f2fe78bSCy Schubert 	{ ERR_NEGOEX_NO_VERIFY_KEY,
1776*7f2fe78bSCy Schubert 	  N_("No NegoEx verify key") },
1777*7f2fe78bSCy Schubert 	{ ERR_NEGOEX_UNKNOWN_CHECKSUM_SCHEME,
1778*7f2fe78bSCy Schubert 	  N_("Unknown NegoEx checksum scheme") },
1779*7f2fe78bSCy Schubert 	{ ERR_NEGOEX_INVALID_CHECKSUM,
1780*7f2fe78bSCy Schubert 	  N_("Invalid NegoEx checksum") },
1781*7f2fe78bSCy Schubert 	{ ERR_NEGOEX_UNSUPPORTED_CRITICAL_EXTENSION,
1782*7f2fe78bSCy Schubert 	  N_("Unsupported critical NegoEx extension") },
1783*7f2fe78bSCy Schubert 	{ ERR_NEGOEX_UNSUPPORTED_VERSION,
1784*7f2fe78bSCy Schubert 	  N_("Unsupported NegoEx version") },
1785*7f2fe78bSCy Schubert 	{ ERR_NEGOEX_MESSAGE_OUT_OF_SEQUENCE,
1786*7f2fe78bSCy Schubert 	  N_("NegoEx message out of sequence") },
1787*7f2fe78bSCy Schubert };
1788*7f2fe78bSCy Schubert 
1789*7f2fe78bSCy Schubert /*ARGSUSED*/
1790*7f2fe78bSCy Schubert OM_uint32 KRB5_CALLCONV
spnego_gss_display_status(OM_uint32 * minor_status,OM_uint32 status_value,int status_type,gss_OID mech_type,OM_uint32 * message_context,gss_buffer_t status_string)1791*7f2fe78bSCy Schubert spnego_gss_display_status(
1792*7f2fe78bSCy Schubert 		OM_uint32 *minor_status,
1793*7f2fe78bSCy Schubert 		OM_uint32 status_value,
1794*7f2fe78bSCy Schubert 		int status_type,
1795*7f2fe78bSCy Schubert 		gss_OID mech_type,
1796*7f2fe78bSCy Schubert 		OM_uint32 *message_context,
1797*7f2fe78bSCy Schubert 		gss_buffer_t status_string)
1798*7f2fe78bSCy Schubert {
1799*7f2fe78bSCy Schubert 	OM_uint32 maj = GSS_S_COMPLETE;
1800*7f2fe78bSCy Schubert 	const char *msg;
1801*7f2fe78bSCy Schubert 	size_t i;
1802*7f2fe78bSCy Schubert 	int ret;
1803*7f2fe78bSCy Schubert 
1804*7f2fe78bSCy Schubert 	*message_context = 0;
1805*7f2fe78bSCy Schubert 	for (i = 0; i < sizeof(msg_table) / sizeof(*msg_table); i++) {
1806*7f2fe78bSCy Schubert 		if (status_value == msg_table[i].status) {
1807*7f2fe78bSCy Schubert 			msg = dgettext(KRB5_TEXTDOMAIN, msg_table[i].msg);
1808*7f2fe78bSCy Schubert 			*status_string = make_err_msg(msg);
1809*7f2fe78bSCy Schubert 			return GSS_S_COMPLETE;
1810*7f2fe78bSCy Schubert 		}
1811*7f2fe78bSCy Schubert 	}
1812*7f2fe78bSCy Schubert 
1813*7f2fe78bSCy Schubert 	/* Not one of our minor codes; might be from a mech.  Call back
1814*7f2fe78bSCy Schubert 	 * to gss_display_status, but first check for recursion. */
1815*7f2fe78bSCy Schubert 	if (k5_getspecific(K5_KEY_GSS_SPNEGO_STATUS) != NULL) {
1816*7f2fe78bSCy Schubert 		/* Perhaps we returned a com_err code like ENOMEM. */
1817*7f2fe78bSCy Schubert 		const char *err = error_message(status_value);
1818*7f2fe78bSCy Schubert 		*status_string = make_err_msg(err);
1819*7f2fe78bSCy Schubert 		return GSS_S_COMPLETE;
1820*7f2fe78bSCy Schubert 	}
1821*7f2fe78bSCy Schubert 	/* Set a non-null pointer value; doesn't matter which one. */
1822*7f2fe78bSCy Schubert 	ret = k5_setspecific(K5_KEY_GSS_SPNEGO_STATUS, &ret);
1823*7f2fe78bSCy Schubert 	if (ret != 0) {
1824*7f2fe78bSCy Schubert 		*minor_status = ret;
1825*7f2fe78bSCy Schubert 		return GSS_S_FAILURE;
1826*7f2fe78bSCy Schubert 	}
1827*7f2fe78bSCy Schubert 
1828*7f2fe78bSCy Schubert 	maj = gss_display_status(minor_status, status_value,
1829*7f2fe78bSCy Schubert 				 status_type, mech_type,
1830*7f2fe78bSCy Schubert 				 message_context, status_string);
1831*7f2fe78bSCy Schubert 	/* This is unlikely to fail; not much we can do if it does. */
1832*7f2fe78bSCy Schubert 	(void)k5_setspecific(K5_KEY_GSS_SPNEGO_STATUS, NULL);
1833*7f2fe78bSCy Schubert 
1834*7f2fe78bSCy Schubert 	return maj;
1835*7f2fe78bSCy Schubert }
1836*7f2fe78bSCy Schubert 
1837*7f2fe78bSCy Schubert 
1838*7f2fe78bSCy Schubert /*ARGSUSED*/
1839*7f2fe78bSCy Schubert OM_uint32 KRB5_CALLCONV
spnego_gss_import_name(OM_uint32 * minor_status,gss_buffer_t input_name_buffer,gss_OID input_name_type,gss_name_t * output_name)1840*7f2fe78bSCy Schubert spnego_gss_import_name(
1841*7f2fe78bSCy Schubert 		    OM_uint32 *minor_status,
1842*7f2fe78bSCy Schubert 		    gss_buffer_t input_name_buffer,
1843*7f2fe78bSCy Schubert 		    gss_OID input_name_type,
1844*7f2fe78bSCy Schubert 		    gss_name_t *output_name)
1845*7f2fe78bSCy Schubert {
1846*7f2fe78bSCy Schubert 	OM_uint32 status;
1847*7f2fe78bSCy Schubert 
1848*7f2fe78bSCy Schubert 	dsyslog("Entering import_name\n");
1849*7f2fe78bSCy Schubert 
1850*7f2fe78bSCy Schubert 	status = gss_import_name(minor_status, input_name_buffer,
1851*7f2fe78bSCy Schubert 			input_name_type, output_name);
1852*7f2fe78bSCy Schubert 
1853*7f2fe78bSCy Schubert 	dsyslog("Leaving import_name\n");
1854*7f2fe78bSCy Schubert 	return (status);
1855*7f2fe78bSCy Schubert }
1856*7f2fe78bSCy Schubert 
1857*7f2fe78bSCy Schubert /*ARGSUSED*/
1858*7f2fe78bSCy Schubert OM_uint32 KRB5_CALLCONV
spnego_gss_release_name(OM_uint32 * minor_status,gss_name_t * input_name)1859*7f2fe78bSCy Schubert spnego_gss_release_name(
1860*7f2fe78bSCy Schubert 			OM_uint32 *minor_status,
1861*7f2fe78bSCy Schubert 			gss_name_t *input_name)
1862*7f2fe78bSCy Schubert {
1863*7f2fe78bSCy Schubert 	OM_uint32 status;
1864*7f2fe78bSCy Schubert 
1865*7f2fe78bSCy Schubert 	dsyslog("Entering release_name\n");
1866*7f2fe78bSCy Schubert 
1867*7f2fe78bSCy Schubert 	status = gss_release_name(minor_status, input_name);
1868*7f2fe78bSCy Schubert 
1869*7f2fe78bSCy Schubert 	dsyslog("Leaving release_name\n");
1870*7f2fe78bSCy Schubert 	return (status);
1871*7f2fe78bSCy Schubert }
1872*7f2fe78bSCy Schubert 
1873*7f2fe78bSCy Schubert /*ARGSUSED*/
1874*7f2fe78bSCy Schubert OM_uint32 KRB5_CALLCONV
spnego_gss_duplicate_name(OM_uint32 * minor_status,const gss_name_t input_name,gss_name_t * output_name)1875*7f2fe78bSCy Schubert spnego_gss_duplicate_name(
1876*7f2fe78bSCy Schubert 			OM_uint32 *minor_status,
1877*7f2fe78bSCy Schubert 			const gss_name_t input_name,
1878*7f2fe78bSCy Schubert 			gss_name_t *output_name)
1879*7f2fe78bSCy Schubert {
1880*7f2fe78bSCy Schubert 	OM_uint32 status;
1881*7f2fe78bSCy Schubert 
1882*7f2fe78bSCy Schubert 	dsyslog("Entering duplicate_name\n");
1883*7f2fe78bSCy Schubert 
1884*7f2fe78bSCy Schubert 	status = gss_duplicate_name(minor_status, input_name, output_name);
1885*7f2fe78bSCy Schubert 
1886*7f2fe78bSCy Schubert 	dsyslog("Leaving duplicate_name\n");
1887*7f2fe78bSCy Schubert 	return (status);
1888*7f2fe78bSCy Schubert }
1889*7f2fe78bSCy Schubert 
1890*7f2fe78bSCy Schubert OM_uint32 KRB5_CALLCONV
spnego_gss_inquire_cred(OM_uint32 * minor_status,gss_cred_id_t cred_handle,gss_name_t * name,OM_uint32 * lifetime,int * cred_usage,gss_OID_set * mechanisms)1891*7f2fe78bSCy Schubert spnego_gss_inquire_cred(
1892*7f2fe78bSCy Schubert 			OM_uint32 *minor_status,
1893*7f2fe78bSCy Schubert 			gss_cred_id_t cred_handle,
1894*7f2fe78bSCy Schubert 			gss_name_t *name,
1895*7f2fe78bSCy Schubert 			OM_uint32 *lifetime,
1896*7f2fe78bSCy Schubert 			int *cred_usage,
1897*7f2fe78bSCy Schubert 			gss_OID_set *mechanisms)
1898*7f2fe78bSCy Schubert {
1899*7f2fe78bSCy Schubert 	OM_uint32 status;
1900*7f2fe78bSCy Schubert 	spnego_gss_cred_id_t spcred = NULL;
1901*7f2fe78bSCy Schubert 	gss_cred_id_t creds = GSS_C_NO_CREDENTIAL;
1902*7f2fe78bSCy Schubert 	OM_uint32 tmp_minor_status;
1903*7f2fe78bSCy Schubert 	OM_uint32 initiator_lifetime, acceptor_lifetime;
1904*7f2fe78bSCy Schubert 
1905*7f2fe78bSCy Schubert 	dsyslog("Entering inquire_cred\n");
1906*7f2fe78bSCy Schubert 
1907*7f2fe78bSCy Schubert 	/*
1908*7f2fe78bSCy Schubert 	 * To avoid infinite recursion, if GSS_C_NO_CREDENTIAL is
1909*7f2fe78bSCy Schubert 	 * supplied we call gss_inquire_cred_by_mech() on the
1910*7f2fe78bSCy Schubert 	 * first non-SPNEGO mechanism.
1911*7f2fe78bSCy Schubert 	 */
1912*7f2fe78bSCy Schubert 	spcred = (spnego_gss_cred_id_t)cred_handle;
1913*7f2fe78bSCy Schubert 	if (spcred == NULL) {
1914*7f2fe78bSCy Schubert 		status = get_available_mechs(minor_status,
1915*7f2fe78bSCy Schubert 			GSS_C_NO_NAME,
1916*7f2fe78bSCy Schubert 			GSS_C_BOTH,
1917*7f2fe78bSCy Schubert 			GSS_C_NO_CRED_STORE,
1918*7f2fe78bSCy Schubert 			&creds,
1919*7f2fe78bSCy Schubert 			mechanisms, NULL);
1920*7f2fe78bSCy Schubert 		if (status != GSS_S_COMPLETE) {
1921*7f2fe78bSCy Schubert 			dsyslog("Leaving inquire_cred\n");
1922*7f2fe78bSCy Schubert 			return (status);
1923*7f2fe78bSCy Schubert 		}
1924*7f2fe78bSCy Schubert 
1925*7f2fe78bSCy Schubert 		if ((*mechanisms)->count == 0) {
1926*7f2fe78bSCy Schubert 			gss_release_cred(&tmp_minor_status, &creds);
1927*7f2fe78bSCy Schubert 			gss_release_oid_set(&tmp_minor_status, mechanisms);
1928*7f2fe78bSCy Schubert 			dsyslog("Leaving inquire_cred\n");
1929*7f2fe78bSCy Schubert 			return (GSS_S_DEFECTIVE_CREDENTIAL);
1930*7f2fe78bSCy Schubert 		}
1931*7f2fe78bSCy Schubert 
1932*7f2fe78bSCy Schubert 		assert((*mechanisms)->elements != NULL);
1933*7f2fe78bSCy Schubert 
1934*7f2fe78bSCy Schubert 		status = gss_inquire_cred_by_mech(minor_status,
1935*7f2fe78bSCy Schubert 			creds,
1936*7f2fe78bSCy Schubert 			&(*mechanisms)->elements[0],
1937*7f2fe78bSCy Schubert 			name,
1938*7f2fe78bSCy Schubert 			&initiator_lifetime,
1939*7f2fe78bSCy Schubert 			&acceptor_lifetime,
1940*7f2fe78bSCy Schubert 			cred_usage);
1941*7f2fe78bSCy Schubert 		if (status != GSS_S_COMPLETE) {
1942*7f2fe78bSCy Schubert 			gss_release_cred(&tmp_minor_status, &creds);
1943*7f2fe78bSCy Schubert 			dsyslog("Leaving inquire_cred\n");
1944*7f2fe78bSCy Schubert 			return (status);
1945*7f2fe78bSCy Schubert 		}
1946*7f2fe78bSCy Schubert 
1947*7f2fe78bSCy Schubert 		if (lifetime != NULL)
1948*7f2fe78bSCy Schubert 			*lifetime = (*cred_usage == GSS_C_ACCEPT) ?
1949*7f2fe78bSCy Schubert 				acceptor_lifetime : initiator_lifetime;
1950*7f2fe78bSCy Schubert 
1951*7f2fe78bSCy Schubert 		gss_release_cred(&tmp_minor_status, &creds);
1952*7f2fe78bSCy Schubert 	} else {
1953*7f2fe78bSCy Schubert 		status = gss_inquire_cred(minor_status, spcred->mcred,
1954*7f2fe78bSCy Schubert 					  name, lifetime,
1955*7f2fe78bSCy Schubert 					  cred_usage, mechanisms);
1956*7f2fe78bSCy Schubert 	}
1957*7f2fe78bSCy Schubert 
1958*7f2fe78bSCy Schubert 	dsyslog("Leaving inquire_cred\n");
1959*7f2fe78bSCy Schubert 
1960*7f2fe78bSCy Schubert 	return (status);
1961*7f2fe78bSCy Schubert }
1962*7f2fe78bSCy Schubert 
1963*7f2fe78bSCy Schubert /*ARGSUSED*/
1964*7f2fe78bSCy Schubert OM_uint32 KRB5_CALLCONV
spnego_gss_compare_name(OM_uint32 * minor_status,const gss_name_t name1,const gss_name_t name2,int * name_equal)1965*7f2fe78bSCy Schubert spnego_gss_compare_name(
1966*7f2fe78bSCy Schubert 			OM_uint32 *minor_status,
1967*7f2fe78bSCy Schubert 			const gss_name_t name1,
1968*7f2fe78bSCy Schubert 			const gss_name_t name2,
1969*7f2fe78bSCy Schubert 			int *name_equal)
1970*7f2fe78bSCy Schubert {
1971*7f2fe78bSCy Schubert 	OM_uint32 status = GSS_S_COMPLETE;
1972*7f2fe78bSCy Schubert 	dsyslog("Entering compare_name\n");
1973*7f2fe78bSCy Schubert 
1974*7f2fe78bSCy Schubert 	status = gss_compare_name(minor_status, name1, name2, name_equal);
1975*7f2fe78bSCy Schubert 
1976*7f2fe78bSCy Schubert 	dsyslog("Leaving compare_name\n");
1977*7f2fe78bSCy Schubert 	return (status);
1978*7f2fe78bSCy Schubert }
1979*7f2fe78bSCy Schubert 
1980*7f2fe78bSCy Schubert /*ARGSUSED*/
1981*7f2fe78bSCy Schubert /*ARGSUSED*/
1982*7f2fe78bSCy Schubert OM_uint32 KRB5_CALLCONV
spnego_gss_display_name(OM_uint32 * minor_status,gss_name_t input_name,gss_buffer_t output_name_buffer,gss_OID * output_name_type)1983*7f2fe78bSCy Schubert spnego_gss_display_name(
1984*7f2fe78bSCy Schubert 			OM_uint32 *minor_status,
1985*7f2fe78bSCy Schubert 			gss_name_t input_name,
1986*7f2fe78bSCy Schubert 			gss_buffer_t output_name_buffer,
1987*7f2fe78bSCy Schubert 			gss_OID *output_name_type)
1988*7f2fe78bSCy Schubert {
1989*7f2fe78bSCy Schubert 	OM_uint32 status = GSS_S_COMPLETE;
1990*7f2fe78bSCy Schubert 	dsyslog("Entering display_name\n");
1991*7f2fe78bSCy Schubert 
1992*7f2fe78bSCy Schubert 	status = gss_display_name(minor_status, input_name,
1993*7f2fe78bSCy Schubert 			output_name_buffer, output_name_type);
1994*7f2fe78bSCy Schubert 
1995*7f2fe78bSCy Schubert 	dsyslog("Leaving display_name\n");
1996*7f2fe78bSCy Schubert 	return (status);
1997*7f2fe78bSCy Schubert }
1998*7f2fe78bSCy Schubert 
1999*7f2fe78bSCy Schubert 
2000*7f2fe78bSCy Schubert /*ARGSUSED*/
2001*7f2fe78bSCy Schubert OM_uint32 KRB5_CALLCONV
spnego_gss_inquire_names_for_mech(OM_uint32 * minor_status,gss_OID mechanism,gss_OID_set * name_types)2002*7f2fe78bSCy Schubert spnego_gss_inquire_names_for_mech(
2003*7f2fe78bSCy Schubert 				OM_uint32	*minor_status,
2004*7f2fe78bSCy Schubert 				gss_OID		mechanism,
2005*7f2fe78bSCy Schubert 				gss_OID_set	*name_types)
2006*7f2fe78bSCy Schubert {
2007*7f2fe78bSCy Schubert 	OM_uint32   major, minor;
2008*7f2fe78bSCy Schubert 
2009*7f2fe78bSCy Schubert 	dsyslog("Entering inquire_names_for_mech\n");
2010*7f2fe78bSCy Schubert 	/*
2011*7f2fe78bSCy Schubert 	 * We only know how to handle our own mechanism.
2012*7f2fe78bSCy Schubert 	 */
2013*7f2fe78bSCy Schubert 	if ((mechanism != GSS_C_NULL_OID) &&
2014*7f2fe78bSCy Schubert 	    !g_OID_equal(gss_mech_spnego, mechanism)) {
2015*7f2fe78bSCy Schubert 		*minor_status = 0;
2016*7f2fe78bSCy Schubert 		return (GSS_S_FAILURE);
2017*7f2fe78bSCy Schubert 	}
2018*7f2fe78bSCy Schubert 
2019*7f2fe78bSCy Schubert 	major = gss_create_empty_oid_set(minor_status, name_types);
2020*7f2fe78bSCy Schubert 	if (major == GSS_S_COMPLETE) {
2021*7f2fe78bSCy Schubert 		/* Now add our members. */
2022*7f2fe78bSCy Schubert 		if (((major = gss_add_oid_set_member(minor_status,
2023*7f2fe78bSCy Schubert 				(gss_OID) GSS_C_NT_USER_NAME,
2024*7f2fe78bSCy Schubert 				name_types)) == GSS_S_COMPLETE) &&
2025*7f2fe78bSCy Schubert 		    ((major = gss_add_oid_set_member(minor_status,
2026*7f2fe78bSCy Schubert 				(gss_OID) GSS_C_NT_MACHINE_UID_NAME,
2027*7f2fe78bSCy Schubert 				name_types)) == GSS_S_COMPLETE) &&
2028*7f2fe78bSCy Schubert 		    ((major = gss_add_oid_set_member(minor_status,
2029*7f2fe78bSCy Schubert 				(gss_OID) GSS_C_NT_STRING_UID_NAME,
2030*7f2fe78bSCy Schubert 				name_types)) == GSS_S_COMPLETE)) {
2031*7f2fe78bSCy Schubert 			major = gss_add_oid_set_member(minor_status,
2032*7f2fe78bSCy Schubert 				(gss_OID) GSS_C_NT_HOSTBASED_SERVICE,
2033*7f2fe78bSCy Schubert 				name_types);
2034*7f2fe78bSCy Schubert 		}
2035*7f2fe78bSCy Schubert 
2036*7f2fe78bSCy Schubert 		if (major != GSS_S_COMPLETE)
2037*7f2fe78bSCy Schubert 			(void) gss_release_oid_set(&minor, name_types);
2038*7f2fe78bSCy Schubert 	}
2039*7f2fe78bSCy Schubert 
2040*7f2fe78bSCy Schubert 	dsyslog("Leaving inquire_names_for_mech\n");
2041*7f2fe78bSCy Schubert 	return (major);
2042*7f2fe78bSCy Schubert }
2043*7f2fe78bSCy Schubert 
2044*7f2fe78bSCy Schubert OM_uint32 KRB5_CALLCONV
spnego_gss_unwrap(OM_uint32 * minor_status,gss_ctx_id_t context_handle,gss_buffer_t input_message_buffer,gss_buffer_t output_message_buffer,int * conf_state,gss_qop_t * qop_state)2045*7f2fe78bSCy Schubert spnego_gss_unwrap(
2046*7f2fe78bSCy Schubert 		OM_uint32 *minor_status,
2047*7f2fe78bSCy Schubert 		gss_ctx_id_t context_handle,
2048*7f2fe78bSCy Schubert 		gss_buffer_t input_message_buffer,
2049*7f2fe78bSCy Schubert 		gss_buffer_t output_message_buffer,
2050*7f2fe78bSCy Schubert 		int *conf_state,
2051*7f2fe78bSCy Schubert 		gss_qop_t *qop_state)
2052*7f2fe78bSCy Schubert {
2053*7f2fe78bSCy Schubert 	OM_uint32 ret;
2054*7f2fe78bSCy Schubert 	spnego_gss_ctx_id_t sc = (spnego_gss_ctx_id_t)context_handle;
2055*7f2fe78bSCy Schubert 
2056*7f2fe78bSCy Schubert 	if (sc->ctx_handle == GSS_C_NO_CONTEXT)
2057*7f2fe78bSCy Schubert 		return (GSS_S_NO_CONTEXT);
2058*7f2fe78bSCy Schubert 
2059*7f2fe78bSCy Schubert 	ret = gss_unwrap(minor_status,
2060*7f2fe78bSCy Schubert 			sc->ctx_handle,
2061*7f2fe78bSCy Schubert 			input_message_buffer,
2062*7f2fe78bSCy Schubert 			output_message_buffer,
2063*7f2fe78bSCy Schubert 			conf_state,
2064*7f2fe78bSCy Schubert 			qop_state);
2065*7f2fe78bSCy Schubert 
2066*7f2fe78bSCy Schubert 	return (ret);
2067*7f2fe78bSCy Schubert }
2068*7f2fe78bSCy Schubert 
2069*7f2fe78bSCy Schubert OM_uint32 KRB5_CALLCONV
spnego_gss_wrap(OM_uint32 * minor_status,gss_ctx_id_t context_handle,int conf_req_flag,gss_qop_t qop_req,gss_buffer_t input_message_buffer,int * conf_state,gss_buffer_t output_message_buffer)2070*7f2fe78bSCy Schubert spnego_gss_wrap(
2071*7f2fe78bSCy Schubert 		OM_uint32 *minor_status,
2072*7f2fe78bSCy Schubert 		gss_ctx_id_t context_handle,
2073*7f2fe78bSCy Schubert 		int conf_req_flag,
2074*7f2fe78bSCy Schubert 		gss_qop_t qop_req,
2075*7f2fe78bSCy Schubert 		gss_buffer_t input_message_buffer,
2076*7f2fe78bSCy Schubert 		int *conf_state,
2077*7f2fe78bSCy Schubert 		gss_buffer_t output_message_buffer)
2078*7f2fe78bSCy Schubert {
2079*7f2fe78bSCy Schubert 	OM_uint32 ret;
2080*7f2fe78bSCy Schubert 	spnego_gss_ctx_id_t sc = (spnego_gss_ctx_id_t)context_handle;
2081*7f2fe78bSCy Schubert 
2082*7f2fe78bSCy Schubert 	if (sc->ctx_handle == GSS_C_NO_CONTEXT)
2083*7f2fe78bSCy Schubert 		return (GSS_S_NO_CONTEXT);
2084*7f2fe78bSCy Schubert 
2085*7f2fe78bSCy Schubert 	ret = gss_wrap(minor_status,
2086*7f2fe78bSCy Schubert 		    sc->ctx_handle,
2087*7f2fe78bSCy Schubert 		    conf_req_flag,
2088*7f2fe78bSCy Schubert 		    qop_req,
2089*7f2fe78bSCy Schubert 		    input_message_buffer,
2090*7f2fe78bSCy Schubert 		    conf_state,
2091*7f2fe78bSCy Schubert 		    output_message_buffer);
2092*7f2fe78bSCy Schubert 
2093*7f2fe78bSCy Schubert 	return (ret);
2094*7f2fe78bSCy Schubert }
2095*7f2fe78bSCy Schubert 
2096*7f2fe78bSCy Schubert OM_uint32 KRB5_CALLCONV
spnego_gss_process_context_token(OM_uint32 * minor_status,const gss_ctx_id_t context_handle,const gss_buffer_t token_buffer)2097*7f2fe78bSCy Schubert spnego_gss_process_context_token(
2098*7f2fe78bSCy Schubert 				OM_uint32	*minor_status,
2099*7f2fe78bSCy Schubert 				const gss_ctx_id_t context_handle,
2100*7f2fe78bSCy Schubert 				const gss_buffer_t token_buffer)
2101*7f2fe78bSCy Schubert {
2102*7f2fe78bSCy Schubert 	OM_uint32 ret;
2103*7f2fe78bSCy Schubert 	spnego_gss_ctx_id_t sc = (spnego_gss_ctx_id_t)context_handle;
2104*7f2fe78bSCy Schubert 
2105*7f2fe78bSCy Schubert 	/* SPNEGO doesn't have its own context tokens. */
2106*7f2fe78bSCy Schubert 	if (!sc->opened)
2107*7f2fe78bSCy Schubert 		return (GSS_S_DEFECTIVE_TOKEN);
2108*7f2fe78bSCy Schubert 
2109*7f2fe78bSCy Schubert 	ret = gss_process_context_token(minor_status,
2110*7f2fe78bSCy Schubert 					sc->ctx_handle,
2111*7f2fe78bSCy Schubert 					token_buffer);
2112*7f2fe78bSCy Schubert 
2113*7f2fe78bSCy Schubert 	return (ret);
2114*7f2fe78bSCy Schubert }
2115*7f2fe78bSCy Schubert 
2116*7f2fe78bSCy Schubert OM_uint32 KRB5_CALLCONV
spnego_gss_delete_sec_context(OM_uint32 * minor_status,gss_ctx_id_t * context_handle,gss_buffer_t output_token)2117*7f2fe78bSCy Schubert spnego_gss_delete_sec_context(
2118*7f2fe78bSCy Schubert 			    OM_uint32 *minor_status,
2119*7f2fe78bSCy Schubert 			    gss_ctx_id_t *context_handle,
2120*7f2fe78bSCy Schubert 			    gss_buffer_t output_token)
2121*7f2fe78bSCy Schubert {
2122*7f2fe78bSCy Schubert 	OM_uint32 ret = GSS_S_COMPLETE;
2123*7f2fe78bSCy Schubert 	spnego_gss_ctx_id_t *ctx =
2124*7f2fe78bSCy Schubert 		    (spnego_gss_ctx_id_t *)context_handle;
2125*7f2fe78bSCy Schubert 
2126*7f2fe78bSCy Schubert 	*minor_status = 0;
2127*7f2fe78bSCy Schubert 
2128*7f2fe78bSCy Schubert 	if (context_handle == NULL)
2129*7f2fe78bSCy Schubert 		return (GSS_S_FAILURE);
2130*7f2fe78bSCy Schubert 
2131*7f2fe78bSCy Schubert 	if (*ctx == NULL)
2132*7f2fe78bSCy Schubert 		return (GSS_S_COMPLETE);
2133*7f2fe78bSCy Schubert 
2134*7f2fe78bSCy Schubert 	(void) gss_delete_sec_context(minor_status, &(*ctx)->ctx_handle,
2135*7f2fe78bSCy Schubert 				      output_token);
2136*7f2fe78bSCy Schubert 	(void) release_spnego_ctx(ctx);
2137*7f2fe78bSCy Schubert 
2138*7f2fe78bSCy Schubert 	return (ret);
2139*7f2fe78bSCy Schubert }
2140*7f2fe78bSCy Schubert 
2141*7f2fe78bSCy Schubert OM_uint32 KRB5_CALLCONV
spnego_gss_context_time(OM_uint32 * minor_status,const gss_ctx_id_t context_handle,OM_uint32 * time_rec)2142*7f2fe78bSCy Schubert spnego_gss_context_time(
2143*7f2fe78bSCy Schubert 			OM_uint32	*minor_status,
2144*7f2fe78bSCy Schubert 			const gss_ctx_id_t context_handle,
2145*7f2fe78bSCy Schubert 			OM_uint32	*time_rec)
2146*7f2fe78bSCy Schubert {
2147*7f2fe78bSCy Schubert 	OM_uint32 ret;
2148*7f2fe78bSCy Schubert 	spnego_gss_ctx_id_t sc = (spnego_gss_ctx_id_t)context_handle;
2149*7f2fe78bSCy Schubert 
2150*7f2fe78bSCy Schubert 	if (sc->ctx_handle == GSS_C_NO_CONTEXT)
2151*7f2fe78bSCy Schubert 		return (GSS_S_NO_CONTEXT);
2152*7f2fe78bSCy Schubert 
2153*7f2fe78bSCy Schubert 	ret = gss_context_time(minor_status,
2154*7f2fe78bSCy Schubert 			    sc->ctx_handle,
2155*7f2fe78bSCy Schubert 			    time_rec);
2156*7f2fe78bSCy Schubert 	return (ret);
2157*7f2fe78bSCy Schubert }
2158*7f2fe78bSCy Schubert #ifndef LEAN_CLIENT
2159*7f2fe78bSCy Schubert OM_uint32 KRB5_CALLCONV
spnego_gss_export_sec_context(OM_uint32 * minor_status,gss_ctx_id_t * context_handle,gss_buffer_t interprocess_token)2160*7f2fe78bSCy Schubert spnego_gss_export_sec_context(
2161*7f2fe78bSCy Schubert 			    OM_uint32	  *minor_status,
2162*7f2fe78bSCy Schubert 			    gss_ctx_id_t *context_handle,
2163*7f2fe78bSCy Schubert 			    gss_buffer_t interprocess_token)
2164*7f2fe78bSCy Schubert {
2165*7f2fe78bSCy Schubert 	OM_uint32 ret;
2166*7f2fe78bSCy Schubert 	spnego_gss_ctx_id_t sc = *(spnego_gss_ctx_id_t *)context_handle;
2167*7f2fe78bSCy Schubert 
2168*7f2fe78bSCy Schubert 	/* We don't currently support exporting partially established
2169*7f2fe78bSCy Schubert 	 * contexts. */
2170*7f2fe78bSCy Schubert 	if (!sc->opened)
2171*7f2fe78bSCy Schubert 		return GSS_S_UNAVAILABLE;
2172*7f2fe78bSCy Schubert 
2173*7f2fe78bSCy Schubert 	ret = gss_export_sec_context(minor_status,
2174*7f2fe78bSCy Schubert 				    &sc->ctx_handle,
2175*7f2fe78bSCy Schubert 				    interprocess_token);
2176*7f2fe78bSCy Schubert 	if (sc->ctx_handle == GSS_C_NO_CONTEXT) {
2177*7f2fe78bSCy Schubert 		release_spnego_ctx(&sc);
2178*7f2fe78bSCy Schubert 		*context_handle = GSS_C_NO_CONTEXT;
2179*7f2fe78bSCy Schubert 	}
2180*7f2fe78bSCy Schubert 	return (ret);
2181*7f2fe78bSCy Schubert }
2182*7f2fe78bSCy Schubert 
2183*7f2fe78bSCy Schubert OM_uint32 KRB5_CALLCONV
spnego_gss_import_sec_context(OM_uint32 * minor_status,const gss_buffer_t interprocess_token,gss_ctx_id_t * context_handle)2184*7f2fe78bSCy Schubert spnego_gss_import_sec_context(
2185*7f2fe78bSCy Schubert 	OM_uint32		*minor_status,
2186*7f2fe78bSCy Schubert 	const gss_buffer_t	interprocess_token,
2187*7f2fe78bSCy Schubert 	gss_ctx_id_t		*context_handle)
2188*7f2fe78bSCy Schubert {
2189*7f2fe78bSCy Schubert 	OM_uint32 ret, tmpmin;
2190*7f2fe78bSCy Schubert 	gss_ctx_id_t mctx;
2191*7f2fe78bSCy Schubert 	spnego_gss_ctx_id_t sc;
2192*7f2fe78bSCy Schubert 	int initiate, opened;
2193*7f2fe78bSCy Schubert 
2194*7f2fe78bSCy Schubert 	ret = gss_import_sec_context(minor_status, interprocess_token, &mctx);
2195*7f2fe78bSCy Schubert 	if (ret != GSS_S_COMPLETE)
2196*7f2fe78bSCy Schubert 		return ret;
2197*7f2fe78bSCy Schubert 
2198*7f2fe78bSCy Schubert 	ret = gss_inquire_context(&tmpmin, mctx, NULL, NULL, NULL, NULL, NULL,
2199*7f2fe78bSCy Schubert 				  &initiate, &opened);
2200*7f2fe78bSCy Schubert 	if (ret != GSS_S_COMPLETE || !opened) {
2201*7f2fe78bSCy Schubert 		/* We don't currently support importing partially established
2202*7f2fe78bSCy Schubert 		 * contexts. */
2203*7f2fe78bSCy Schubert 		(void) gss_delete_sec_context(&tmpmin, &mctx, GSS_C_NO_BUFFER);
2204*7f2fe78bSCy Schubert 		return GSS_S_FAILURE;
2205*7f2fe78bSCy Schubert 	}
2206*7f2fe78bSCy Schubert 
2207*7f2fe78bSCy Schubert 	sc = create_spnego_ctx(initiate);
2208*7f2fe78bSCy Schubert 	if (sc == NULL) {
2209*7f2fe78bSCy Schubert 		(void) gss_delete_sec_context(&tmpmin, &mctx, GSS_C_NO_BUFFER);
2210*7f2fe78bSCy Schubert 		return GSS_S_FAILURE;
2211*7f2fe78bSCy Schubert 	}
2212*7f2fe78bSCy Schubert 	sc->ctx_handle = mctx;
2213*7f2fe78bSCy Schubert 	sc->opened = 1;
2214*7f2fe78bSCy Schubert 	*context_handle = (gss_ctx_id_t)sc;
2215*7f2fe78bSCy Schubert 	return GSS_S_COMPLETE;
2216*7f2fe78bSCy Schubert }
2217*7f2fe78bSCy Schubert #endif /* LEAN_CLIENT */
2218*7f2fe78bSCy Schubert 
2219*7f2fe78bSCy Schubert OM_uint32 KRB5_CALLCONV
spnego_gss_inquire_context(OM_uint32 * minor_status,const gss_ctx_id_t context_handle,gss_name_t * src_name,gss_name_t * targ_name,OM_uint32 * lifetime_rec,gss_OID * mech_type,OM_uint32 * ctx_flags,int * locally_initiated,int * opened)2220*7f2fe78bSCy Schubert spnego_gss_inquire_context(
2221*7f2fe78bSCy Schubert 			OM_uint32	*minor_status,
2222*7f2fe78bSCy Schubert 			const gss_ctx_id_t context_handle,
2223*7f2fe78bSCy Schubert 			gss_name_t	*src_name,
2224*7f2fe78bSCy Schubert 			gss_name_t	*targ_name,
2225*7f2fe78bSCy Schubert 			OM_uint32	*lifetime_rec,
2226*7f2fe78bSCy Schubert 			gss_OID		*mech_type,
2227*7f2fe78bSCy Schubert 			OM_uint32	*ctx_flags,
2228*7f2fe78bSCy Schubert 			int		*locally_initiated,
2229*7f2fe78bSCy Schubert 			int		*opened)
2230*7f2fe78bSCy Schubert {
2231*7f2fe78bSCy Schubert 	OM_uint32 ret = GSS_S_COMPLETE;
2232*7f2fe78bSCy Schubert 	spnego_gss_ctx_id_t sc = (spnego_gss_ctx_id_t)context_handle;
2233*7f2fe78bSCy Schubert 
2234*7f2fe78bSCy Schubert 	if (src_name != NULL)
2235*7f2fe78bSCy Schubert 		*src_name = GSS_C_NO_NAME;
2236*7f2fe78bSCy Schubert 	if (targ_name != NULL)
2237*7f2fe78bSCy Schubert 		*targ_name = GSS_C_NO_NAME;
2238*7f2fe78bSCy Schubert 	if (lifetime_rec != NULL)
2239*7f2fe78bSCy Schubert 		*lifetime_rec = 0;
2240*7f2fe78bSCy Schubert 	if (mech_type != NULL)
2241*7f2fe78bSCy Schubert 		*mech_type = (gss_OID)gss_mech_spnego;
2242*7f2fe78bSCy Schubert 	if (ctx_flags != NULL)
2243*7f2fe78bSCy Schubert 		*ctx_flags = 0;
2244*7f2fe78bSCy Schubert 	if (locally_initiated != NULL)
2245*7f2fe78bSCy Schubert 		*locally_initiated = sc->initiate;
2246*7f2fe78bSCy Schubert 	if (opened != NULL)
2247*7f2fe78bSCy Schubert 		*opened = sc->opened;
2248*7f2fe78bSCy Schubert 
2249*7f2fe78bSCy Schubert 	if (sc->ctx_handle != GSS_C_NO_CONTEXT) {
2250*7f2fe78bSCy Schubert 		ret = gss_inquire_context(minor_status, sc->ctx_handle,
2251*7f2fe78bSCy Schubert 					  src_name, targ_name, lifetime_rec,
2252*7f2fe78bSCy Schubert 					  mech_type, ctx_flags, NULL, NULL);
2253*7f2fe78bSCy Schubert 	}
2254*7f2fe78bSCy Schubert 
2255*7f2fe78bSCy Schubert 	if (!sc->opened) {
2256*7f2fe78bSCy Schubert 		/*
2257*7f2fe78bSCy Schubert 		 * We are still doing SPNEGO negotiation, so report SPNEGO as
2258*7f2fe78bSCy Schubert 		 * the OID.  After negotiation is complete we will report the
2259*7f2fe78bSCy Schubert 		 * underlying mechanism OID.
2260*7f2fe78bSCy Schubert 		 */
2261*7f2fe78bSCy Schubert 		if (mech_type != NULL)
2262*7f2fe78bSCy Schubert 			*mech_type = (gss_OID)gss_mech_spnego;
2263*7f2fe78bSCy Schubert 
2264*7f2fe78bSCy Schubert 		/*
2265*7f2fe78bSCy Schubert 		 * Remove flags we don't support with partially-established
2266*7f2fe78bSCy Schubert 		 * contexts.  (Change this to keep GSS_C_TRANS_FLAG if we add
2267*7f2fe78bSCy Schubert 		 * support for exporting partial SPNEGO contexts.)
2268*7f2fe78bSCy Schubert 		 */
2269*7f2fe78bSCy Schubert 		if (ctx_flags != NULL) {
2270*7f2fe78bSCy Schubert 			*ctx_flags &= ~GSS_C_PROT_READY_FLAG;
2271*7f2fe78bSCy Schubert 			*ctx_flags &= ~GSS_C_TRANS_FLAG;
2272*7f2fe78bSCy Schubert 		}
2273*7f2fe78bSCy Schubert 	}
2274*7f2fe78bSCy Schubert 
2275*7f2fe78bSCy Schubert 	return (ret);
2276*7f2fe78bSCy Schubert }
2277*7f2fe78bSCy Schubert 
2278*7f2fe78bSCy Schubert OM_uint32 KRB5_CALLCONV
spnego_gss_wrap_size_limit(OM_uint32 * minor_status,const gss_ctx_id_t context_handle,int conf_req_flag,gss_qop_t qop_req,OM_uint32 req_output_size,OM_uint32 * max_input_size)2279*7f2fe78bSCy Schubert spnego_gss_wrap_size_limit(
2280*7f2fe78bSCy Schubert 	OM_uint32	*minor_status,
2281*7f2fe78bSCy Schubert 	const gss_ctx_id_t context_handle,
2282*7f2fe78bSCy Schubert 	int		conf_req_flag,
2283*7f2fe78bSCy Schubert 	gss_qop_t	qop_req,
2284*7f2fe78bSCy Schubert 	OM_uint32	req_output_size,
2285*7f2fe78bSCy Schubert 	OM_uint32	*max_input_size)
2286*7f2fe78bSCy Schubert {
2287*7f2fe78bSCy Schubert 	OM_uint32 ret;
2288*7f2fe78bSCy Schubert 	spnego_gss_ctx_id_t sc = (spnego_gss_ctx_id_t)context_handle;
2289*7f2fe78bSCy Schubert 
2290*7f2fe78bSCy Schubert 	if (sc->ctx_handle == GSS_C_NO_CONTEXT)
2291*7f2fe78bSCy Schubert 		return (GSS_S_NO_CONTEXT);
2292*7f2fe78bSCy Schubert 
2293*7f2fe78bSCy Schubert 	ret = gss_wrap_size_limit(minor_status,
2294*7f2fe78bSCy Schubert 				sc->ctx_handle,
2295*7f2fe78bSCy Schubert 				conf_req_flag,
2296*7f2fe78bSCy Schubert 				qop_req,
2297*7f2fe78bSCy Schubert 				req_output_size,
2298*7f2fe78bSCy Schubert 				max_input_size);
2299*7f2fe78bSCy Schubert 	return (ret);
2300*7f2fe78bSCy Schubert }
2301*7f2fe78bSCy Schubert 
2302*7f2fe78bSCy Schubert OM_uint32 KRB5_CALLCONV
spnego_gss_localname(OM_uint32 * minor_status,const gss_name_t pname,const gss_const_OID mech_type,gss_buffer_t localname)2303*7f2fe78bSCy Schubert spnego_gss_localname(OM_uint32 *minor_status, const gss_name_t pname,
2304*7f2fe78bSCy Schubert 		     const gss_const_OID mech_type, gss_buffer_t localname)
2305*7f2fe78bSCy Schubert {
2306*7f2fe78bSCy Schubert 	return gss_localname(minor_status, pname, GSS_C_NO_OID, localname);
2307*7f2fe78bSCy Schubert }
2308*7f2fe78bSCy Schubert 
2309*7f2fe78bSCy Schubert OM_uint32 KRB5_CALLCONV
spnego_gss_get_mic(OM_uint32 * minor_status,const gss_ctx_id_t context_handle,gss_qop_t qop_req,const gss_buffer_t message_buffer,gss_buffer_t message_token)2310*7f2fe78bSCy Schubert spnego_gss_get_mic(
2311*7f2fe78bSCy Schubert 		OM_uint32 *minor_status,
2312*7f2fe78bSCy Schubert 		const gss_ctx_id_t context_handle,
2313*7f2fe78bSCy Schubert 		gss_qop_t  qop_req,
2314*7f2fe78bSCy Schubert 		const gss_buffer_t message_buffer,
2315*7f2fe78bSCy Schubert 		gss_buffer_t message_token)
2316*7f2fe78bSCy Schubert {
2317*7f2fe78bSCy Schubert 	OM_uint32 ret;
2318*7f2fe78bSCy Schubert 	spnego_gss_ctx_id_t sc = (spnego_gss_ctx_id_t)context_handle;
2319*7f2fe78bSCy Schubert 
2320*7f2fe78bSCy Schubert 	if (sc->ctx_handle == GSS_C_NO_CONTEXT)
2321*7f2fe78bSCy Schubert 		return (GSS_S_NO_CONTEXT);
2322*7f2fe78bSCy Schubert 
2323*7f2fe78bSCy Schubert 	ret = gss_get_mic(minor_status,
2324*7f2fe78bSCy Schubert 		    sc->ctx_handle,
2325*7f2fe78bSCy Schubert 		    qop_req,
2326*7f2fe78bSCy Schubert 		    message_buffer,
2327*7f2fe78bSCy Schubert 		    message_token);
2328*7f2fe78bSCy Schubert 	return (ret);
2329*7f2fe78bSCy Schubert }
2330*7f2fe78bSCy Schubert 
2331*7f2fe78bSCy Schubert OM_uint32 KRB5_CALLCONV
spnego_gss_verify_mic(OM_uint32 * minor_status,const gss_ctx_id_t context_handle,const gss_buffer_t msg_buffer,const gss_buffer_t token_buffer,gss_qop_t * qop_state)2332*7f2fe78bSCy Schubert spnego_gss_verify_mic(
2333*7f2fe78bSCy Schubert 		OM_uint32 *minor_status,
2334*7f2fe78bSCy Schubert 		const gss_ctx_id_t context_handle,
2335*7f2fe78bSCy Schubert 		const gss_buffer_t msg_buffer,
2336*7f2fe78bSCy Schubert 		const gss_buffer_t token_buffer,
2337*7f2fe78bSCy Schubert 		gss_qop_t *qop_state)
2338*7f2fe78bSCy Schubert {
2339*7f2fe78bSCy Schubert 	OM_uint32 ret;
2340*7f2fe78bSCy Schubert 	spnego_gss_ctx_id_t sc = (spnego_gss_ctx_id_t)context_handle;
2341*7f2fe78bSCy Schubert 
2342*7f2fe78bSCy Schubert 	if (sc->ctx_handle == GSS_C_NO_CONTEXT)
2343*7f2fe78bSCy Schubert 		return (GSS_S_NO_CONTEXT);
2344*7f2fe78bSCy Schubert 
2345*7f2fe78bSCy Schubert 	ret = gss_verify_mic(minor_status,
2346*7f2fe78bSCy Schubert 			    sc->ctx_handle,
2347*7f2fe78bSCy Schubert 			    msg_buffer,
2348*7f2fe78bSCy Schubert 			    token_buffer,
2349*7f2fe78bSCy Schubert 			    qop_state);
2350*7f2fe78bSCy Schubert 	return (ret);
2351*7f2fe78bSCy Schubert }
2352*7f2fe78bSCy Schubert 
2353*7f2fe78bSCy Schubert OM_uint32 KRB5_CALLCONV
spnego_gss_inquire_sec_context_by_oid(OM_uint32 * minor_status,const gss_ctx_id_t context_handle,const gss_OID desired_object,gss_buffer_set_t * data_set)2354*7f2fe78bSCy Schubert spnego_gss_inquire_sec_context_by_oid(
2355*7f2fe78bSCy Schubert 		OM_uint32 *minor_status,
2356*7f2fe78bSCy Schubert 		const gss_ctx_id_t context_handle,
2357*7f2fe78bSCy Schubert 		const gss_OID desired_object,
2358*7f2fe78bSCy Schubert 		gss_buffer_set_t *data_set)
2359*7f2fe78bSCy Schubert {
2360*7f2fe78bSCy Schubert 	OM_uint32 ret;
2361*7f2fe78bSCy Schubert 	spnego_gss_ctx_id_t sc = (spnego_gss_ctx_id_t)context_handle;
2362*7f2fe78bSCy Schubert 
2363*7f2fe78bSCy Schubert 	/* There are no SPNEGO-specific OIDs for this function. */
2364*7f2fe78bSCy Schubert 	if (sc->ctx_handle == GSS_C_NO_CONTEXT)
2365*7f2fe78bSCy Schubert 		return (GSS_S_UNAVAILABLE);
2366*7f2fe78bSCy Schubert 
2367*7f2fe78bSCy Schubert 	ret = gss_inquire_sec_context_by_oid(minor_status,
2368*7f2fe78bSCy Schubert 			    sc->ctx_handle,
2369*7f2fe78bSCy Schubert 			    desired_object,
2370*7f2fe78bSCy Schubert 			    data_set);
2371*7f2fe78bSCy Schubert 	return (ret);
2372*7f2fe78bSCy Schubert }
2373*7f2fe78bSCy Schubert 
2374*7f2fe78bSCy Schubert OM_uint32 KRB5_CALLCONV
spnego_gss_inquire_cred_by_oid(OM_uint32 * minor_status,const gss_cred_id_t cred_handle,const gss_OID desired_object,gss_buffer_set_t * data_set)2375*7f2fe78bSCy Schubert spnego_gss_inquire_cred_by_oid(
2376*7f2fe78bSCy Schubert 		OM_uint32 *minor_status,
2377*7f2fe78bSCy Schubert 		const gss_cred_id_t cred_handle,
2378*7f2fe78bSCy Schubert 		const gss_OID desired_object,
2379*7f2fe78bSCy Schubert 		gss_buffer_set_t *data_set)
2380*7f2fe78bSCy Schubert {
2381*7f2fe78bSCy Schubert 	OM_uint32 ret;
2382*7f2fe78bSCy Schubert 	spnego_gss_cred_id_t spcred = (spnego_gss_cred_id_t)cred_handle;
2383*7f2fe78bSCy Schubert 	gss_cred_id_t mcred;
2384*7f2fe78bSCy Schubert 	mcred = (spcred == NULL) ? GSS_C_NO_CREDENTIAL : spcred->mcred;
2385*7f2fe78bSCy Schubert 	ret = gss_inquire_cred_by_oid(minor_status,
2386*7f2fe78bSCy Schubert 				mcred,
2387*7f2fe78bSCy Schubert 				desired_object,
2388*7f2fe78bSCy Schubert 				data_set);
2389*7f2fe78bSCy Schubert 	return (ret);
2390*7f2fe78bSCy Schubert }
2391*7f2fe78bSCy Schubert 
2392*7f2fe78bSCy Schubert /* This is the same OID as KRB5_NO_CI_FLAGS_X_OID. */
2393*7f2fe78bSCy Schubert #define NO_CI_FLAGS_X_OID_LENGTH 6
2394*7f2fe78bSCy Schubert #define NO_CI_FLAGS_X_OID "\x2a\x85\x70\x2b\x0d\x1d"
2395*7f2fe78bSCy Schubert static const gss_OID_desc no_ci_flags_oid[] = {
2396*7f2fe78bSCy Schubert 	{NO_CI_FLAGS_X_OID_LENGTH, NO_CI_FLAGS_X_OID},
2397*7f2fe78bSCy Schubert };
2398*7f2fe78bSCy Schubert 
2399*7f2fe78bSCy Schubert OM_uint32 KRB5_CALLCONV
spnego_gss_set_cred_option(OM_uint32 * minor_status,gss_cred_id_t * cred_handle,const gss_OID desired_object,const gss_buffer_t value)2400*7f2fe78bSCy Schubert spnego_gss_set_cred_option(
2401*7f2fe78bSCy Schubert 		OM_uint32 *minor_status,
2402*7f2fe78bSCy Schubert 		gss_cred_id_t *cred_handle,
2403*7f2fe78bSCy Schubert 		const gss_OID desired_object,
2404*7f2fe78bSCy Schubert 		const gss_buffer_t value)
2405*7f2fe78bSCy Schubert {
2406*7f2fe78bSCy Schubert 	OM_uint32 ret;
2407*7f2fe78bSCy Schubert 	OM_uint32 tmp_minor_status;
2408*7f2fe78bSCy Schubert 	spnego_gss_cred_id_t spcred = (spnego_gss_cred_id_t)*cred_handle;
2409*7f2fe78bSCy Schubert 	gss_cred_id_t mcred;
2410*7f2fe78bSCy Schubert 
2411*7f2fe78bSCy Schubert 	mcred = (spcred == NULL) ? GSS_C_NO_CREDENTIAL : spcred->mcred;
2412*7f2fe78bSCy Schubert 	ret = gss_set_cred_option(minor_status,
2413*7f2fe78bSCy Schubert 				  &mcred,
2414*7f2fe78bSCy Schubert 				  desired_object,
2415*7f2fe78bSCy Schubert 				  value);
2416*7f2fe78bSCy Schubert 	if (ret == GSS_S_COMPLETE && spcred == NULL) {
2417*7f2fe78bSCy Schubert 		/*
2418*7f2fe78bSCy Schubert 		 * If the mechanism allocated a new credential handle, then
2419*7f2fe78bSCy Schubert 		 * we need to wrap it up in an SPNEGO credential handle.
2420*7f2fe78bSCy Schubert 		 */
2421*7f2fe78bSCy Schubert 
2422*7f2fe78bSCy Schubert 		ret = create_spnego_cred(minor_status, mcred, &spcred);
2423*7f2fe78bSCy Schubert 		if (ret != GSS_S_COMPLETE) {
2424*7f2fe78bSCy Schubert 			gss_release_cred(&tmp_minor_status, &mcred);
2425*7f2fe78bSCy Schubert 			return (ret);
2426*7f2fe78bSCy Schubert 		}
2427*7f2fe78bSCy Schubert 		*cred_handle = (gss_cred_id_t)spcred;
2428*7f2fe78bSCy Schubert 	}
2429*7f2fe78bSCy Schubert 
2430*7f2fe78bSCy Schubert 	if (ret != GSS_S_COMPLETE)
2431*7f2fe78bSCy Schubert 		return (ret);
2432*7f2fe78bSCy Schubert 
2433*7f2fe78bSCy Schubert 	/* Recognize KRB5_NO_CI_FLAGS_X_OID and avoid asking for integrity. */
2434*7f2fe78bSCy Schubert 	if (g_OID_equal(desired_object, no_ci_flags_oid))
2435*7f2fe78bSCy Schubert 		spcred->no_ask_integ = 1;
2436*7f2fe78bSCy Schubert 
2437*7f2fe78bSCy Schubert 	return (GSS_S_COMPLETE);
2438*7f2fe78bSCy Schubert }
2439*7f2fe78bSCy Schubert 
2440*7f2fe78bSCy Schubert OM_uint32 KRB5_CALLCONV
spnego_gss_set_sec_context_option(OM_uint32 * minor_status,gss_ctx_id_t * context_handle,const gss_OID desired_object,const gss_buffer_t value)2441*7f2fe78bSCy Schubert spnego_gss_set_sec_context_option(
2442*7f2fe78bSCy Schubert 		OM_uint32 *minor_status,
2443*7f2fe78bSCy Schubert 		gss_ctx_id_t *context_handle,
2444*7f2fe78bSCy Schubert 		const gss_OID desired_object,
2445*7f2fe78bSCy Schubert 		const gss_buffer_t value)
2446*7f2fe78bSCy Schubert {
2447*7f2fe78bSCy Schubert 	OM_uint32 ret;
2448*7f2fe78bSCy Schubert 	spnego_gss_ctx_id_t sc = (spnego_gss_ctx_id_t)*context_handle;
2449*7f2fe78bSCy Schubert 
2450*7f2fe78bSCy Schubert 	/* There are no SPNEGO-specific OIDs for this function, and we cannot
2451*7f2fe78bSCy Schubert 	 * construct an empty SPNEGO context with it. */
2452*7f2fe78bSCy Schubert 	if (sc == NULL || sc->ctx_handle == GSS_C_NO_CONTEXT)
2453*7f2fe78bSCy Schubert 		return (GSS_S_UNAVAILABLE);
2454*7f2fe78bSCy Schubert 
2455*7f2fe78bSCy Schubert 	ret = gss_set_sec_context_option(minor_status,
2456*7f2fe78bSCy Schubert 			    &sc->ctx_handle,
2457*7f2fe78bSCy Schubert 			    desired_object,
2458*7f2fe78bSCy Schubert 			    value);
2459*7f2fe78bSCy Schubert 	return (ret);
2460*7f2fe78bSCy Schubert }
2461*7f2fe78bSCy Schubert 
2462*7f2fe78bSCy Schubert OM_uint32 KRB5_CALLCONV
spnego_gss_wrap_aead(OM_uint32 * minor_status,gss_ctx_id_t context_handle,int conf_req_flag,gss_qop_t qop_req,gss_buffer_t input_assoc_buffer,gss_buffer_t input_payload_buffer,int * conf_state,gss_buffer_t output_message_buffer)2463*7f2fe78bSCy Schubert spnego_gss_wrap_aead(OM_uint32 *minor_status,
2464*7f2fe78bSCy Schubert 		     gss_ctx_id_t context_handle,
2465*7f2fe78bSCy Schubert 		     int conf_req_flag,
2466*7f2fe78bSCy Schubert 		     gss_qop_t qop_req,
2467*7f2fe78bSCy Schubert 		     gss_buffer_t input_assoc_buffer,
2468*7f2fe78bSCy Schubert 		     gss_buffer_t input_payload_buffer,
2469*7f2fe78bSCy Schubert 		     int *conf_state,
2470*7f2fe78bSCy Schubert 		     gss_buffer_t output_message_buffer)
2471*7f2fe78bSCy Schubert {
2472*7f2fe78bSCy Schubert 	OM_uint32 ret;
2473*7f2fe78bSCy Schubert 	spnego_gss_ctx_id_t sc = (spnego_gss_ctx_id_t)context_handle;
2474*7f2fe78bSCy Schubert 
2475*7f2fe78bSCy Schubert 	if (sc->ctx_handle == GSS_C_NO_CONTEXT)
2476*7f2fe78bSCy Schubert 		return (GSS_S_NO_CONTEXT);
2477*7f2fe78bSCy Schubert 
2478*7f2fe78bSCy Schubert 	ret = gss_wrap_aead(minor_status,
2479*7f2fe78bSCy Schubert 			    sc->ctx_handle,
2480*7f2fe78bSCy Schubert 			    conf_req_flag,
2481*7f2fe78bSCy Schubert 			    qop_req,
2482*7f2fe78bSCy Schubert 			    input_assoc_buffer,
2483*7f2fe78bSCy Schubert 			    input_payload_buffer,
2484*7f2fe78bSCy Schubert 			    conf_state,
2485*7f2fe78bSCy Schubert 			    output_message_buffer);
2486*7f2fe78bSCy Schubert 
2487*7f2fe78bSCy Schubert 	return (ret);
2488*7f2fe78bSCy Schubert }
2489*7f2fe78bSCy Schubert 
2490*7f2fe78bSCy Schubert OM_uint32 KRB5_CALLCONV
spnego_gss_unwrap_aead(OM_uint32 * minor_status,gss_ctx_id_t context_handle,gss_buffer_t input_message_buffer,gss_buffer_t input_assoc_buffer,gss_buffer_t output_payload_buffer,int * conf_state,gss_qop_t * qop_state)2491*7f2fe78bSCy Schubert spnego_gss_unwrap_aead(OM_uint32 *minor_status,
2492*7f2fe78bSCy Schubert 		       gss_ctx_id_t context_handle,
2493*7f2fe78bSCy Schubert 		       gss_buffer_t input_message_buffer,
2494*7f2fe78bSCy Schubert 		       gss_buffer_t input_assoc_buffer,
2495*7f2fe78bSCy Schubert 		       gss_buffer_t output_payload_buffer,
2496*7f2fe78bSCy Schubert 		       int *conf_state,
2497*7f2fe78bSCy Schubert 		       gss_qop_t *qop_state)
2498*7f2fe78bSCy Schubert {
2499*7f2fe78bSCy Schubert 	OM_uint32 ret;
2500*7f2fe78bSCy Schubert 	spnego_gss_ctx_id_t sc = (spnego_gss_ctx_id_t)context_handle;
2501*7f2fe78bSCy Schubert 
2502*7f2fe78bSCy Schubert 	if (sc->ctx_handle == GSS_C_NO_CONTEXT)
2503*7f2fe78bSCy Schubert 		return (GSS_S_NO_CONTEXT);
2504*7f2fe78bSCy Schubert 
2505*7f2fe78bSCy Schubert 	ret = gss_unwrap_aead(minor_status,
2506*7f2fe78bSCy Schubert 			      sc->ctx_handle,
2507*7f2fe78bSCy Schubert 			      input_message_buffer,
2508*7f2fe78bSCy Schubert 			      input_assoc_buffer,
2509*7f2fe78bSCy Schubert 			      output_payload_buffer,
2510*7f2fe78bSCy Schubert 			      conf_state,
2511*7f2fe78bSCy Schubert 			      qop_state);
2512*7f2fe78bSCy Schubert 	return (ret);
2513*7f2fe78bSCy Schubert }
2514*7f2fe78bSCy Schubert 
2515*7f2fe78bSCy Schubert OM_uint32 KRB5_CALLCONV
spnego_gss_wrap_iov(OM_uint32 * minor_status,gss_ctx_id_t context_handle,int conf_req_flag,gss_qop_t qop_req,int * conf_state,gss_iov_buffer_desc * iov,int iov_count)2516*7f2fe78bSCy Schubert spnego_gss_wrap_iov(OM_uint32 *minor_status,
2517*7f2fe78bSCy Schubert 		    gss_ctx_id_t context_handle,
2518*7f2fe78bSCy Schubert 		    int conf_req_flag,
2519*7f2fe78bSCy Schubert 		    gss_qop_t qop_req,
2520*7f2fe78bSCy Schubert 		    int *conf_state,
2521*7f2fe78bSCy Schubert 		    gss_iov_buffer_desc *iov,
2522*7f2fe78bSCy Schubert 		    int iov_count)
2523*7f2fe78bSCy Schubert {
2524*7f2fe78bSCy Schubert 	OM_uint32 ret;
2525*7f2fe78bSCy Schubert 	spnego_gss_ctx_id_t sc = (spnego_gss_ctx_id_t)context_handle;
2526*7f2fe78bSCy Schubert 
2527*7f2fe78bSCy Schubert 	if (sc->ctx_handle == GSS_C_NO_CONTEXT)
2528*7f2fe78bSCy Schubert 		return (GSS_S_NO_CONTEXT);
2529*7f2fe78bSCy Schubert 
2530*7f2fe78bSCy Schubert 	ret = gss_wrap_iov(minor_status,
2531*7f2fe78bSCy Schubert 			   sc->ctx_handle,
2532*7f2fe78bSCy Schubert 			   conf_req_flag,
2533*7f2fe78bSCy Schubert 			   qop_req,
2534*7f2fe78bSCy Schubert 			   conf_state,
2535*7f2fe78bSCy Schubert 			   iov,
2536*7f2fe78bSCy Schubert 			   iov_count);
2537*7f2fe78bSCy Schubert 	return (ret);
2538*7f2fe78bSCy Schubert }
2539*7f2fe78bSCy Schubert 
2540*7f2fe78bSCy Schubert OM_uint32 KRB5_CALLCONV
spnego_gss_unwrap_iov(OM_uint32 * minor_status,gss_ctx_id_t context_handle,int * conf_state,gss_qop_t * qop_state,gss_iov_buffer_desc * iov,int iov_count)2541*7f2fe78bSCy Schubert spnego_gss_unwrap_iov(OM_uint32 *minor_status,
2542*7f2fe78bSCy Schubert 		      gss_ctx_id_t context_handle,
2543*7f2fe78bSCy Schubert 		      int *conf_state,
2544*7f2fe78bSCy Schubert 		      gss_qop_t *qop_state,
2545*7f2fe78bSCy Schubert 		      gss_iov_buffer_desc *iov,
2546*7f2fe78bSCy Schubert 		      int iov_count)
2547*7f2fe78bSCy Schubert {
2548*7f2fe78bSCy Schubert 	OM_uint32 ret;
2549*7f2fe78bSCy Schubert 	spnego_gss_ctx_id_t sc = (spnego_gss_ctx_id_t)context_handle;
2550*7f2fe78bSCy Schubert 
2551*7f2fe78bSCy Schubert 	if (sc->ctx_handle == GSS_C_NO_CONTEXT)
2552*7f2fe78bSCy Schubert 		return (GSS_S_NO_CONTEXT);
2553*7f2fe78bSCy Schubert 
2554*7f2fe78bSCy Schubert 	ret = gss_unwrap_iov(minor_status,
2555*7f2fe78bSCy Schubert 			     sc->ctx_handle,
2556*7f2fe78bSCy Schubert 			     conf_state,
2557*7f2fe78bSCy Schubert 			     qop_state,
2558*7f2fe78bSCy Schubert 			     iov,
2559*7f2fe78bSCy Schubert 			     iov_count);
2560*7f2fe78bSCy Schubert 	return (ret);
2561*7f2fe78bSCy Schubert }
2562*7f2fe78bSCy Schubert 
2563*7f2fe78bSCy Schubert OM_uint32 KRB5_CALLCONV
spnego_gss_wrap_iov_length(OM_uint32 * minor_status,gss_ctx_id_t context_handle,int conf_req_flag,gss_qop_t qop_req,int * conf_state,gss_iov_buffer_desc * iov,int iov_count)2564*7f2fe78bSCy Schubert spnego_gss_wrap_iov_length(OM_uint32 *minor_status,
2565*7f2fe78bSCy Schubert 			   gss_ctx_id_t context_handle,
2566*7f2fe78bSCy Schubert 			   int conf_req_flag,
2567*7f2fe78bSCy Schubert 			   gss_qop_t qop_req,
2568*7f2fe78bSCy Schubert 			   int *conf_state,
2569*7f2fe78bSCy Schubert 			   gss_iov_buffer_desc *iov,
2570*7f2fe78bSCy Schubert 			   int iov_count)
2571*7f2fe78bSCy Schubert {
2572*7f2fe78bSCy Schubert 	OM_uint32 ret;
2573*7f2fe78bSCy Schubert 	spnego_gss_ctx_id_t sc = (spnego_gss_ctx_id_t)context_handle;
2574*7f2fe78bSCy Schubert 
2575*7f2fe78bSCy Schubert 	if (sc->ctx_handle == GSS_C_NO_CONTEXT)
2576*7f2fe78bSCy Schubert 		return (GSS_S_NO_CONTEXT);
2577*7f2fe78bSCy Schubert 
2578*7f2fe78bSCy Schubert 	ret = gss_wrap_iov_length(minor_status,
2579*7f2fe78bSCy Schubert 				  sc->ctx_handle,
2580*7f2fe78bSCy Schubert 				  conf_req_flag,
2581*7f2fe78bSCy Schubert 				  qop_req,
2582*7f2fe78bSCy Schubert 				  conf_state,
2583*7f2fe78bSCy Schubert 				  iov,
2584*7f2fe78bSCy Schubert 				  iov_count);
2585*7f2fe78bSCy Schubert 	return (ret);
2586*7f2fe78bSCy Schubert }
2587*7f2fe78bSCy Schubert 
2588*7f2fe78bSCy Schubert 
2589*7f2fe78bSCy Schubert OM_uint32 KRB5_CALLCONV
spnego_gss_complete_auth_token(OM_uint32 * minor_status,const gss_ctx_id_t context_handle,gss_buffer_t input_message_buffer)2590*7f2fe78bSCy Schubert spnego_gss_complete_auth_token(
2591*7f2fe78bSCy Schubert 		OM_uint32 *minor_status,
2592*7f2fe78bSCy Schubert 		const gss_ctx_id_t context_handle,
2593*7f2fe78bSCy Schubert 		gss_buffer_t input_message_buffer)
2594*7f2fe78bSCy Schubert {
2595*7f2fe78bSCy Schubert 	OM_uint32 ret;
2596*7f2fe78bSCy Schubert 	spnego_gss_ctx_id_t sc = (spnego_gss_ctx_id_t)context_handle;
2597*7f2fe78bSCy Schubert 
2598*7f2fe78bSCy Schubert 	if (sc->ctx_handle == GSS_C_NO_CONTEXT)
2599*7f2fe78bSCy Schubert 		return (GSS_S_UNAVAILABLE);
2600*7f2fe78bSCy Schubert 
2601*7f2fe78bSCy Schubert 	ret = gss_complete_auth_token(minor_status,
2602*7f2fe78bSCy Schubert 				      sc->ctx_handle,
2603*7f2fe78bSCy Schubert 				      input_message_buffer);
2604*7f2fe78bSCy Schubert 	return (ret);
2605*7f2fe78bSCy Schubert }
2606*7f2fe78bSCy Schubert 
2607*7f2fe78bSCy Schubert OM_uint32 KRB5_CALLCONV
spnego_gss_acquire_cred_impersonate_name(OM_uint32 * minor_status,const gss_cred_id_t impersonator_cred_handle,const gss_name_t desired_name,OM_uint32 time_req,gss_OID_set desired_mechs,gss_cred_usage_t cred_usage,gss_cred_id_t * output_cred_handle,gss_OID_set * actual_mechs,OM_uint32 * time_rec)2608*7f2fe78bSCy Schubert spnego_gss_acquire_cred_impersonate_name(OM_uint32 *minor_status,
2609*7f2fe78bSCy Schubert 					 const gss_cred_id_t impersonator_cred_handle,
2610*7f2fe78bSCy Schubert 					 const gss_name_t desired_name,
2611*7f2fe78bSCy Schubert 					 OM_uint32 time_req,
2612*7f2fe78bSCy Schubert 					 gss_OID_set desired_mechs,
2613*7f2fe78bSCy Schubert 					 gss_cred_usage_t cred_usage,
2614*7f2fe78bSCy Schubert 					 gss_cred_id_t *output_cred_handle,
2615*7f2fe78bSCy Schubert 					 gss_OID_set *actual_mechs,
2616*7f2fe78bSCy Schubert 					 OM_uint32 *time_rec)
2617*7f2fe78bSCy Schubert {
2618*7f2fe78bSCy Schubert 	OM_uint32 status, tmpmin;
2619*7f2fe78bSCy Schubert 	gss_OID_set amechs = GSS_C_NULL_OID_SET;
2620*7f2fe78bSCy Schubert 	spnego_gss_cred_id_t imp_spcred = NULL, out_spcred = NULL;
2621*7f2fe78bSCy Schubert 	gss_cred_id_t imp_mcred, out_mcred = GSS_C_NO_CREDENTIAL;
2622*7f2fe78bSCy Schubert 
2623*7f2fe78bSCy Schubert 	dsyslog("Entering spnego_gss_acquire_cred_impersonate_name\n");
2624*7f2fe78bSCy Schubert 
2625*7f2fe78bSCy Schubert 	if (actual_mechs)
2626*7f2fe78bSCy Schubert 		*actual_mechs = NULL;
2627*7f2fe78bSCy Schubert 
2628*7f2fe78bSCy Schubert 	if (time_rec)
2629*7f2fe78bSCy Schubert 		*time_rec = 0;
2630*7f2fe78bSCy Schubert 
2631*7f2fe78bSCy Schubert 	imp_spcred = (spnego_gss_cred_id_t)impersonator_cred_handle;
2632*7f2fe78bSCy Schubert 	imp_mcred = imp_spcred ? imp_spcred->mcred : GSS_C_NO_CREDENTIAL;
2633*7f2fe78bSCy Schubert 	status = gss_inquire_cred(minor_status, imp_mcred, NULL, NULL,
2634*7f2fe78bSCy Schubert 				  NULL, &amechs);
2635*7f2fe78bSCy Schubert 	if (status != GSS_S_COMPLETE)
2636*7f2fe78bSCy Schubert 		return status;
2637*7f2fe78bSCy Schubert 
2638*7f2fe78bSCy Schubert 	status = gss_acquire_cred_impersonate_name(minor_status, imp_mcred,
2639*7f2fe78bSCy Schubert 						   desired_name, time_req,
2640*7f2fe78bSCy Schubert 						   amechs, cred_usage,
2641*7f2fe78bSCy Schubert 						   &out_mcred, actual_mechs,
2642*7f2fe78bSCy Schubert 						   time_rec);
2643*7f2fe78bSCy Schubert 	if (status != GSS_S_COMPLETE)
2644*7f2fe78bSCy Schubert 		goto cleanup;
2645*7f2fe78bSCy Schubert 
2646*7f2fe78bSCy Schubert 	status = create_spnego_cred(minor_status, out_mcred, &out_spcred);
2647*7f2fe78bSCy Schubert 	if (status != GSS_S_COMPLETE)
2648*7f2fe78bSCy Schubert 		goto cleanup;
2649*7f2fe78bSCy Schubert 
2650*7f2fe78bSCy Schubert 	out_mcred = GSS_C_NO_CREDENTIAL;
2651*7f2fe78bSCy Schubert 	*output_cred_handle = (gss_cred_id_t)out_spcred;
2652*7f2fe78bSCy Schubert 
2653*7f2fe78bSCy Schubert cleanup:
2654*7f2fe78bSCy Schubert 	(void) gss_release_oid_set(&tmpmin, &amechs);
2655*7f2fe78bSCy Schubert 	(void) gss_release_cred(&tmpmin, &out_mcred);
2656*7f2fe78bSCy Schubert 
2657*7f2fe78bSCy Schubert 	dsyslog("Leaving spnego_gss_acquire_cred_impersonate_name\n");
2658*7f2fe78bSCy Schubert 	return (status);
2659*7f2fe78bSCy Schubert }
2660*7f2fe78bSCy Schubert 
2661*7f2fe78bSCy Schubert OM_uint32 KRB5_CALLCONV
spnego_gss_acquire_cred_with_password(OM_uint32 * minor_status,const gss_name_t desired_name,const gss_buffer_t password,OM_uint32 time_req,const gss_OID_set desired_mechs,gss_cred_usage_t cred_usage,gss_cred_id_t * output_cred_handle,gss_OID_set * actual_mechs,OM_uint32 * time_rec)2662*7f2fe78bSCy Schubert spnego_gss_acquire_cred_with_password(OM_uint32 *minor_status,
2663*7f2fe78bSCy Schubert 				      const gss_name_t desired_name,
2664*7f2fe78bSCy Schubert 				      const gss_buffer_t password,
2665*7f2fe78bSCy Schubert 				      OM_uint32 time_req,
2666*7f2fe78bSCy Schubert 				      const gss_OID_set desired_mechs,
2667*7f2fe78bSCy Schubert 				      gss_cred_usage_t cred_usage,
2668*7f2fe78bSCy Schubert 				      gss_cred_id_t *output_cred_handle,
2669*7f2fe78bSCy Schubert 				      gss_OID_set *actual_mechs,
2670*7f2fe78bSCy Schubert 				      OM_uint32 *time_rec)
2671*7f2fe78bSCy Schubert {
2672*7f2fe78bSCy Schubert 	OM_uint32 status, tmpmin;
2673*7f2fe78bSCy Schubert 	gss_OID_set amechs = GSS_C_NULL_OID_SET;
2674*7f2fe78bSCy Schubert 	gss_cred_id_t mcred = NULL;
2675*7f2fe78bSCy Schubert 	spnego_gss_cred_id_t spcred = NULL;
2676*7f2fe78bSCy Schubert 
2677*7f2fe78bSCy Schubert 	dsyslog("Entering spnego_gss_acquire_cred_with_password\n");
2678*7f2fe78bSCy Schubert 
2679*7f2fe78bSCy Schubert 	if (actual_mechs)
2680*7f2fe78bSCy Schubert 		*actual_mechs = NULL;
2681*7f2fe78bSCy Schubert 
2682*7f2fe78bSCy Schubert 	if (time_rec)
2683*7f2fe78bSCy Schubert 		*time_rec = 0;
2684*7f2fe78bSCy Schubert 
2685*7f2fe78bSCy Schubert 	status = get_available_mechs(minor_status, desired_name,
2686*7f2fe78bSCy Schubert 				     cred_usage, GSS_C_NO_CRED_STORE,
2687*7f2fe78bSCy Schubert 				     NULL, &amechs, NULL);
2688*7f2fe78bSCy Schubert 	if (status != GSS_S_COMPLETE)
2689*7f2fe78bSCy Schubert 	    goto cleanup;
2690*7f2fe78bSCy Schubert 
2691*7f2fe78bSCy Schubert 	status = gss_acquire_cred_with_password(minor_status, desired_name,
2692*7f2fe78bSCy Schubert 						password, time_req, amechs,
2693*7f2fe78bSCy Schubert 						cred_usage, &mcred,
2694*7f2fe78bSCy Schubert 						actual_mechs, time_rec);
2695*7f2fe78bSCy Schubert 	if (status != GSS_S_COMPLETE)
2696*7f2fe78bSCy Schubert 	    goto cleanup;
2697*7f2fe78bSCy Schubert 
2698*7f2fe78bSCy Schubert 	status = create_spnego_cred(minor_status, mcred, &spcred);
2699*7f2fe78bSCy Schubert 	if (status != GSS_S_COMPLETE)
2700*7f2fe78bSCy Schubert 		goto cleanup;
2701*7f2fe78bSCy Schubert 
2702*7f2fe78bSCy Schubert 	mcred = GSS_C_NO_CREDENTIAL;
2703*7f2fe78bSCy Schubert 	*output_cred_handle = (gss_cred_id_t)spcred;
2704*7f2fe78bSCy Schubert 
2705*7f2fe78bSCy Schubert cleanup:
2706*7f2fe78bSCy Schubert 
2707*7f2fe78bSCy Schubert 	(void) gss_release_oid_set(&tmpmin, &amechs);
2708*7f2fe78bSCy Schubert 	(void) gss_release_cred(&tmpmin, &mcred);
2709*7f2fe78bSCy Schubert 
2710*7f2fe78bSCy Schubert 	dsyslog("Leaving spnego_gss_acquire_cred_with_password\n");
2711*7f2fe78bSCy Schubert 	return (status);
2712*7f2fe78bSCy Schubert }
2713*7f2fe78bSCy Schubert 
2714*7f2fe78bSCy Schubert OM_uint32 KRB5_CALLCONV
spnego_gss_display_name_ext(OM_uint32 * minor_status,gss_name_t name,gss_OID display_as_name_type,gss_buffer_t display_name)2715*7f2fe78bSCy Schubert spnego_gss_display_name_ext(OM_uint32 *minor_status,
2716*7f2fe78bSCy Schubert 			    gss_name_t name,
2717*7f2fe78bSCy Schubert 			    gss_OID display_as_name_type,
2718*7f2fe78bSCy Schubert 			    gss_buffer_t display_name)
2719*7f2fe78bSCy Schubert {
2720*7f2fe78bSCy Schubert 	OM_uint32 ret;
2721*7f2fe78bSCy Schubert 	ret = gss_display_name_ext(minor_status,
2722*7f2fe78bSCy Schubert 				   name,
2723*7f2fe78bSCy Schubert 				   display_as_name_type,
2724*7f2fe78bSCy Schubert 				   display_name);
2725*7f2fe78bSCy Schubert 	return (ret);
2726*7f2fe78bSCy Schubert }
2727*7f2fe78bSCy Schubert 
2728*7f2fe78bSCy Schubert 
2729*7f2fe78bSCy Schubert OM_uint32 KRB5_CALLCONV
spnego_gss_inquire_name(OM_uint32 * minor_status,gss_name_t name,int * name_is_MN,gss_OID * MN_mech,gss_buffer_set_t * attrs)2730*7f2fe78bSCy Schubert spnego_gss_inquire_name(OM_uint32 *minor_status,
2731*7f2fe78bSCy Schubert 			gss_name_t name,
2732*7f2fe78bSCy Schubert 			int *name_is_MN,
2733*7f2fe78bSCy Schubert 			gss_OID *MN_mech,
2734*7f2fe78bSCy Schubert 			gss_buffer_set_t *attrs)
2735*7f2fe78bSCy Schubert {
2736*7f2fe78bSCy Schubert 	OM_uint32 ret;
2737*7f2fe78bSCy Schubert 	ret = gss_inquire_name(minor_status,
2738*7f2fe78bSCy Schubert 			       name,
2739*7f2fe78bSCy Schubert 			       name_is_MN,
2740*7f2fe78bSCy Schubert 			       MN_mech,
2741*7f2fe78bSCy Schubert 			       attrs);
2742*7f2fe78bSCy Schubert 	return (ret);
2743*7f2fe78bSCy Schubert }
2744*7f2fe78bSCy Schubert 
2745*7f2fe78bSCy Schubert OM_uint32 KRB5_CALLCONV
spnego_gss_get_name_attribute(OM_uint32 * minor_status,gss_name_t name,gss_buffer_t attr,int * authenticated,int * complete,gss_buffer_t value,gss_buffer_t display_value,int * more)2746*7f2fe78bSCy Schubert spnego_gss_get_name_attribute(OM_uint32 *minor_status,
2747*7f2fe78bSCy Schubert 			      gss_name_t name,
2748*7f2fe78bSCy Schubert 			      gss_buffer_t attr,
2749*7f2fe78bSCy Schubert 			      int *authenticated,
2750*7f2fe78bSCy Schubert 			      int *complete,
2751*7f2fe78bSCy Schubert 			      gss_buffer_t value,
2752*7f2fe78bSCy Schubert 			      gss_buffer_t display_value,
2753*7f2fe78bSCy Schubert 			      int *more)
2754*7f2fe78bSCy Schubert {
2755*7f2fe78bSCy Schubert 	OM_uint32 ret;
2756*7f2fe78bSCy Schubert 	ret = gss_get_name_attribute(minor_status,
2757*7f2fe78bSCy Schubert 				     name,
2758*7f2fe78bSCy Schubert 				     attr,
2759*7f2fe78bSCy Schubert 				     authenticated,
2760*7f2fe78bSCy Schubert 				     complete,
2761*7f2fe78bSCy Schubert 				     value,
2762*7f2fe78bSCy Schubert 				     display_value,
2763*7f2fe78bSCy Schubert 				     more);
2764*7f2fe78bSCy Schubert 	return (ret);
2765*7f2fe78bSCy Schubert }
2766*7f2fe78bSCy Schubert 
2767*7f2fe78bSCy Schubert OM_uint32 KRB5_CALLCONV
spnego_gss_set_name_attribute(OM_uint32 * minor_status,gss_name_t name,int complete,gss_buffer_t attr,gss_buffer_t value)2768*7f2fe78bSCy Schubert spnego_gss_set_name_attribute(OM_uint32 *minor_status,
2769*7f2fe78bSCy Schubert 			      gss_name_t name,
2770*7f2fe78bSCy Schubert 			      int complete,
2771*7f2fe78bSCy Schubert 			      gss_buffer_t attr,
2772*7f2fe78bSCy Schubert 			      gss_buffer_t value)
2773*7f2fe78bSCy Schubert {
2774*7f2fe78bSCy Schubert 	OM_uint32 ret;
2775*7f2fe78bSCy Schubert 	ret = gss_set_name_attribute(minor_status,
2776*7f2fe78bSCy Schubert 				     name,
2777*7f2fe78bSCy Schubert 				     complete,
2778*7f2fe78bSCy Schubert 				     attr,
2779*7f2fe78bSCy Schubert 				     value);
2780*7f2fe78bSCy Schubert 	return (ret);
2781*7f2fe78bSCy Schubert }
2782*7f2fe78bSCy Schubert 
2783*7f2fe78bSCy Schubert OM_uint32 KRB5_CALLCONV
spnego_gss_delete_name_attribute(OM_uint32 * minor_status,gss_name_t name,gss_buffer_t attr)2784*7f2fe78bSCy Schubert spnego_gss_delete_name_attribute(OM_uint32 *minor_status,
2785*7f2fe78bSCy Schubert 				 gss_name_t name,
2786*7f2fe78bSCy Schubert 				 gss_buffer_t attr)
2787*7f2fe78bSCy Schubert {
2788*7f2fe78bSCy Schubert 	OM_uint32 ret;
2789*7f2fe78bSCy Schubert 	ret = gss_delete_name_attribute(minor_status,
2790*7f2fe78bSCy Schubert 					name,
2791*7f2fe78bSCy Schubert 					attr);
2792*7f2fe78bSCy Schubert 	return (ret);
2793*7f2fe78bSCy Schubert }
2794*7f2fe78bSCy Schubert 
2795*7f2fe78bSCy Schubert OM_uint32 KRB5_CALLCONV
spnego_gss_export_name_composite(OM_uint32 * minor_status,gss_name_t name,gss_buffer_t exp_composite_name)2796*7f2fe78bSCy Schubert spnego_gss_export_name_composite(OM_uint32 *minor_status,
2797*7f2fe78bSCy Schubert 				 gss_name_t name,
2798*7f2fe78bSCy Schubert 				 gss_buffer_t exp_composite_name)
2799*7f2fe78bSCy Schubert {
2800*7f2fe78bSCy Schubert 	OM_uint32 ret;
2801*7f2fe78bSCy Schubert 	ret = gss_export_name_composite(minor_status,
2802*7f2fe78bSCy Schubert 					name,
2803*7f2fe78bSCy Schubert 					exp_composite_name);
2804*7f2fe78bSCy Schubert 	return (ret);
2805*7f2fe78bSCy Schubert }
2806*7f2fe78bSCy Schubert 
2807*7f2fe78bSCy Schubert OM_uint32 KRB5_CALLCONV
spnego_gss_map_name_to_any(OM_uint32 * minor_status,gss_name_t name,int authenticated,gss_buffer_t type_id,gss_any_t * output)2808*7f2fe78bSCy Schubert spnego_gss_map_name_to_any(OM_uint32 *minor_status,
2809*7f2fe78bSCy Schubert 			   gss_name_t name,
2810*7f2fe78bSCy Schubert 			   int authenticated,
2811*7f2fe78bSCy Schubert 			   gss_buffer_t type_id,
2812*7f2fe78bSCy Schubert 			   gss_any_t *output)
2813*7f2fe78bSCy Schubert {
2814*7f2fe78bSCy Schubert 	OM_uint32 ret;
2815*7f2fe78bSCy Schubert 	ret = gss_map_name_to_any(minor_status,
2816*7f2fe78bSCy Schubert 				  name,
2817*7f2fe78bSCy Schubert 				  authenticated,
2818*7f2fe78bSCy Schubert 				  type_id,
2819*7f2fe78bSCy Schubert 				  output);
2820*7f2fe78bSCy Schubert 	return (ret);
2821*7f2fe78bSCy Schubert }
2822*7f2fe78bSCy Schubert 
2823*7f2fe78bSCy Schubert OM_uint32 KRB5_CALLCONV
spnego_gss_release_any_name_mapping(OM_uint32 * minor_status,gss_name_t name,gss_buffer_t type_id,gss_any_t * input)2824*7f2fe78bSCy Schubert spnego_gss_release_any_name_mapping(OM_uint32 *minor_status,
2825*7f2fe78bSCy Schubert 				    gss_name_t name,
2826*7f2fe78bSCy Schubert 				    gss_buffer_t type_id,
2827*7f2fe78bSCy Schubert 				    gss_any_t *input)
2828*7f2fe78bSCy Schubert {
2829*7f2fe78bSCy Schubert 	OM_uint32 ret;
2830*7f2fe78bSCy Schubert 	ret = gss_release_any_name_mapping(minor_status,
2831*7f2fe78bSCy Schubert 					   name,
2832*7f2fe78bSCy Schubert 					   type_id,
2833*7f2fe78bSCy Schubert 					   input);
2834*7f2fe78bSCy Schubert 	return (ret);
2835*7f2fe78bSCy Schubert }
2836*7f2fe78bSCy Schubert 
2837*7f2fe78bSCy Schubert OM_uint32 KRB5_CALLCONV
spnego_gss_pseudo_random(OM_uint32 * minor_status,gss_ctx_id_t context,int prf_key,const gss_buffer_t prf_in,ssize_t desired_output_len,gss_buffer_t prf_out)2838*7f2fe78bSCy Schubert spnego_gss_pseudo_random(OM_uint32 *minor_status,
2839*7f2fe78bSCy Schubert 			 gss_ctx_id_t context,
2840*7f2fe78bSCy Schubert 			 int prf_key,
2841*7f2fe78bSCy Schubert 			 const gss_buffer_t prf_in,
2842*7f2fe78bSCy Schubert 			 ssize_t desired_output_len,
2843*7f2fe78bSCy Schubert 			 gss_buffer_t prf_out)
2844*7f2fe78bSCy Schubert {
2845*7f2fe78bSCy Schubert 	OM_uint32 ret;
2846*7f2fe78bSCy Schubert 	spnego_gss_ctx_id_t sc = (spnego_gss_ctx_id_t)context;
2847*7f2fe78bSCy Schubert 
2848*7f2fe78bSCy Schubert 	if (sc->ctx_handle == GSS_C_NO_CONTEXT)
2849*7f2fe78bSCy Schubert 		return (GSS_S_NO_CONTEXT);
2850*7f2fe78bSCy Schubert 
2851*7f2fe78bSCy Schubert 	ret = gss_pseudo_random(minor_status,
2852*7f2fe78bSCy Schubert 				sc->ctx_handle,
2853*7f2fe78bSCy Schubert 				prf_key,
2854*7f2fe78bSCy Schubert 				prf_in,
2855*7f2fe78bSCy Schubert 				desired_output_len,
2856*7f2fe78bSCy Schubert 				prf_out);
2857*7f2fe78bSCy Schubert         return (ret);
2858*7f2fe78bSCy Schubert }
2859*7f2fe78bSCy Schubert 
2860*7f2fe78bSCy Schubert OM_uint32 KRB5_CALLCONV
spnego_gss_set_neg_mechs(OM_uint32 * minor_status,gss_cred_id_t cred_handle,const gss_OID_set mech_list)2861*7f2fe78bSCy Schubert spnego_gss_set_neg_mechs(OM_uint32 *minor_status,
2862*7f2fe78bSCy Schubert 			 gss_cred_id_t cred_handle,
2863*7f2fe78bSCy Schubert 			 const gss_OID_set mech_list)
2864*7f2fe78bSCy Schubert {
2865*7f2fe78bSCy Schubert 	OM_uint32 ret;
2866*7f2fe78bSCy Schubert 	spnego_gss_cred_id_t spcred = (spnego_gss_cred_id_t)cred_handle;
2867*7f2fe78bSCy Schubert 
2868*7f2fe78bSCy Schubert 	/* Store mech_list in spcred for use in negotiation logic. */
2869*7f2fe78bSCy Schubert 	gss_release_oid_set(minor_status, &spcred->neg_mechs);
2870*7f2fe78bSCy Schubert 	ret = generic_gss_copy_oid_set(minor_status, mech_list,
2871*7f2fe78bSCy Schubert 				       &spcred->neg_mechs);
2872*7f2fe78bSCy Schubert 	if (ret == GSS_S_COMPLETE) {
2873*7f2fe78bSCy Schubert 		(void) gss_set_neg_mechs(minor_status,
2874*7f2fe78bSCy Schubert 					 spcred->mcred,
2875*7f2fe78bSCy Schubert 					 spcred->neg_mechs);
2876*7f2fe78bSCy Schubert 	}
2877*7f2fe78bSCy Schubert 
2878*7f2fe78bSCy Schubert 	return (ret);
2879*7f2fe78bSCy Schubert }
2880*7f2fe78bSCy Schubert 
2881*7f2fe78bSCy Schubert #define SPNEGO_SASL_NAME	"SPNEGO"
2882*7f2fe78bSCy Schubert #define SPNEGO_SASL_NAME_LEN	(sizeof(SPNEGO_SASL_NAME) - 1)
2883*7f2fe78bSCy Schubert 
2884*7f2fe78bSCy Schubert OM_uint32 KRB5_CALLCONV
spnego_gss_inquire_mech_for_saslname(OM_uint32 * minor_status,const gss_buffer_t sasl_mech_name,gss_OID * mech_type)2885*7f2fe78bSCy Schubert spnego_gss_inquire_mech_for_saslname(OM_uint32 *minor_status,
2886*7f2fe78bSCy Schubert                                      const gss_buffer_t sasl_mech_name,
2887*7f2fe78bSCy Schubert                                      gss_OID *mech_type)
2888*7f2fe78bSCy Schubert {
2889*7f2fe78bSCy Schubert 	if (sasl_mech_name->length == SPNEGO_SASL_NAME_LEN &&
2890*7f2fe78bSCy Schubert 	    memcmp(sasl_mech_name->value, SPNEGO_SASL_NAME,
2891*7f2fe78bSCy Schubert 		   SPNEGO_SASL_NAME_LEN) == 0) {
2892*7f2fe78bSCy Schubert 		if (mech_type != NULL)
2893*7f2fe78bSCy Schubert 			*mech_type = (gss_OID)gss_mech_spnego;
2894*7f2fe78bSCy Schubert 		return (GSS_S_COMPLETE);
2895*7f2fe78bSCy Schubert 	}
2896*7f2fe78bSCy Schubert 
2897*7f2fe78bSCy Schubert 	return (GSS_S_BAD_MECH);
2898*7f2fe78bSCy Schubert }
2899*7f2fe78bSCy Schubert 
2900*7f2fe78bSCy Schubert OM_uint32 KRB5_CALLCONV
spnego_gss_inquire_saslname_for_mech(OM_uint32 * minor_status,const gss_OID desired_mech,gss_buffer_t sasl_mech_name,gss_buffer_t mech_name,gss_buffer_t mech_description)2901*7f2fe78bSCy Schubert spnego_gss_inquire_saslname_for_mech(OM_uint32 *minor_status,
2902*7f2fe78bSCy Schubert                                      const gss_OID desired_mech,
2903*7f2fe78bSCy Schubert                                      gss_buffer_t sasl_mech_name,
2904*7f2fe78bSCy Schubert                                      gss_buffer_t mech_name,
2905*7f2fe78bSCy Schubert                                      gss_buffer_t mech_description)
2906*7f2fe78bSCy Schubert {
2907*7f2fe78bSCy Schubert 	*minor_status = 0;
2908*7f2fe78bSCy Schubert 
2909*7f2fe78bSCy Schubert 	if (!g_OID_equal(desired_mech, gss_mech_spnego))
2910*7f2fe78bSCy Schubert 		return (GSS_S_BAD_MECH);
2911*7f2fe78bSCy Schubert 
2912*7f2fe78bSCy Schubert 	if (!g_make_string_buffer(SPNEGO_SASL_NAME, sasl_mech_name) ||
2913*7f2fe78bSCy Schubert 	    !g_make_string_buffer("spnego", mech_name) ||
2914*7f2fe78bSCy Schubert 	    !g_make_string_buffer("Simple and Protected GSS-API "
2915*7f2fe78bSCy Schubert 				  "Negotiation Mechanism", mech_description))
2916*7f2fe78bSCy Schubert 		goto fail;
2917*7f2fe78bSCy Schubert 
2918*7f2fe78bSCy Schubert 	return (GSS_S_COMPLETE);
2919*7f2fe78bSCy Schubert 
2920*7f2fe78bSCy Schubert fail:
2921*7f2fe78bSCy Schubert 	*minor_status = ENOMEM;
2922*7f2fe78bSCy Schubert 	return (GSS_S_FAILURE);
2923*7f2fe78bSCy Schubert }
2924*7f2fe78bSCy Schubert 
2925*7f2fe78bSCy Schubert OM_uint32 KRB5_CALLCONV
spnego_gss_inquire_attrs_for_mech(OM_uint32 * minor_status,gss_const_OID mech,gss_OID_set * mech_attrs,gss_OID_set * known_mech_attrs)2926*7f2fe78bSCy Schubert spnego_gss_inquire_attrs_for_mech(OM_uint32 *minor_status,
2927*7f2fe78bSCy Schubert 				  gss_const_OID mech,
2928*7f2fe78bSCy Schubert 				  gss_OID_set *mech_attrs,
2929*7f2fe78bSCy Schubert 				  gss_OID_set *known_mech_attrs)
2930*7f2fe78bSCy Schubert {
2931*7f2fe78bSCy Schubert 	OM_uint32 major, tmpMinor;
2932*7f2fe78bSCy Schubert 
2933*7f2fe78bSCy Schubert 	/* known_mech_attrs is handled by mechglue */
2934*7f2fe78bSCy Schubert 	*minor_status = 0;
2935*7f2fe78bSCy Schubert 
2936*7f2fe78bSCy Schubert 	if (mech_attrs == NULL)
2937*7f2fe78bSCy Schubert 	    return (GSS_S_COMPLETE);
2938*7f2fe78bSCy Schubert 
2939*7f2fe78bSCy Schubert 	major = gss_create_empty_oid_set(minor_status, mech_attrs);
2940*7f2fe78bSCy Schubert 	if (GSS_ERROR(major))
2941*7f2fe78bSCy Schubert 		goto cleanup;
2942*7f2fe78bSCy Schubert 
2943*7f2fe78bSCy Schubert #define MA_SUPPORTED(ma)    do {					\
2944*7f2fe78bSCy Schubert 		major = gss_add_oid_set_member(minor_status,		\
2945*7f2fe78bSCy Schubert 					       (gss_OID)ma, mech_attrs); \
2946*7f2fe78bSCy Schubert 		if (GSS_ERROR(major))					\
2947*7f2fe78bSCy Schubert 			goto cleanup;					\
2948*7f2fe78bSCy Schubert 	} while (0)
2949*7f2fe78bSCy Schubert 
2950*7f2fe78bSCy Schubert 	MA_SUPPORTED(GSS_C_MA_MECH_NEGO);
2951*7f2fe78bSCy Schubert 	MA_SUPPORTED(GSS_C_MA_ITOK_FRAMED);
2952*7f2fe78bSCy Schubert 
2953*7f2fe78bSCy Schubert cleanup:
2954*7f2fe78bSCy Schubert 	if (GSS_ERROR(major))
2955*7f2fe78bSCy Schubert 		gss_release_oid_set(&tmpMinor, mech_attrs);
2956*7f2fe78bSCy Schubert 
2957*7f2fe78bSCy Schubert 	return (major);
2958*7f2fe78bSCy Schubert }
2959*7f2fe78bSCy Schubert 
2960*7f2fe78bSCy Schubert OM_uint32 KRB5_CALLCONV
spnego_gss_export_cred(OM_uint32 * minor_status,gss_cred_id_t cred_handle,gss_buffer_t token)2961*7f2fe78bSCy Schubert spnego_gss_export_cred(OM_uint32 *minor_status,
2962*7f2fe78bSCy Schubert 		       gss_cred_id_t cred_handle,
2963*7f2fe78bSCy Schubert 		       gss_buffer_t token)
2964*7f2fe78bSCy Schubert {
2965*7f2fe78bSCy Schubert 	spnego_gss_cred_id_t spcred = (spnego_gss_cred_id_t)cred_handle;
2966*7f2fe78bSCy Schubert 
2967*7f2fe78bSCy Schubert 	return (gss_export_cred(minor_status, spcred->mcred, token));
2968*7f2fe78bSCy Schubert }
2969*7f2fe78bSCy Schubert 
2970*7f2fe78bSCy Schubert OM_uint32 KRB5_CALLCONV
spnego_gss_import_cred(OM_uint32 * minor_status,gss_buffer_t token,gss_cred_id_t * cred_handle)2971*7f2fe78bSCy Schubert spnego_gss_import_cred(OM_uint32 *minor_status,
2972*7f2fe78bSCy Schubert 		       gss_buffer_t token,
2973*7f2fe78bSCy Schubert 		       gss_cred_id_t *cred_handle)
2974*7f2fe78bSCy Schubert {
2975*7f2fe78bSCy Schubert 	OM_uint32 ret;
2976*7f2fe78bSCy Schubert 	spnego_gss_cred_id_t spcred;
2977*7f2fe78bSCy Schubert 	gss_cred_id_t mcred;
2978*7f2fe78bSCy Schubert 
2979*7f2fe78bSCy Schubert 	ret = gss_import_cred(minor_status, token, &mcred);
2980*7f2fe78bSCy Schubert 	if (GSS_ERROR(ret))
2981*7f2fe78bSCy Schubert 		return (ret);
2982*7f2fe78bSCy Schubert 
2983*7f2fe78bSCy Schubert 	ret = create_spnego_cred(minor_status, mcred, &spcred);
2984*7f2fe78bSCy Schubert 	if (GSS_ERROR(ret))
2985*7f2fe78bSCy Schubert 	    return (ret);
2986*7f2fe78bSCy Schubert 
2987*7f2fe78bSCy Schubert 	*cred_handle = (gss_cred_id_t)spcred;
2988*7f2fe78bSCy Schubert 	return (ret);
2989*7f2fe78bSCy Schubert }
2990*7f2fe78bSCy Schubert 
2991*7f2fe78bSCy Schubert OM_uint32 KRB5_CALLCONV
spnego_gss_get_mic_iov(OM_uint32 * minor_status,gss_ctx_id_t context_handle,gss_qop_t qop_req,gss_iov_buffer_desc * iov,int iov_count)2992*7f2fe78bSCy Schubert spnego_gss_get_mic_iov(OM_uint32 *minor_status, gss_ctx_id_t context_handle,
2993*7f2fe78bSCy Schubert 		       gss_qop_t qop_req, gss_iov_buffer_desc *iov,
2994*7f2fe78bSCy Schubert 		       int iov_count)
2995*7f2fe78bSCy Schubert {
2996*7f2fe78bSCy Schubert     spnego_gss_ctx_id_t sc = (spnego_gss_ctx_id_t)context_handle;
2997*7f2fe78bSCy Schubert 
2998*7f2fe78bSCy Schubert     if (sc->ctx_handle == GSS_C_NO_CONTEXT)
2999*7f2fe78bSCy Schubert 	    return (GSS_S_NO_CONTEXT);
3000*7f2fe78bSCy Schubert 
3001*7f2fe78bSCy Schubert     return gss_get_mic_iov(minor_status, sc->ctx_handle, qop_req, iov,
3002*7f2fe78bSCy Schubert 			   iov_count);
3003*7f2fe78bSCy Schubert }
3004*7f2fe78bSCy Schubert 
3005*7f2fe78bSCy Schubert OM_uint32 KRB5_CALLCONV
spnego_gss_verify_mic_iov(OM_uint32 * minor_status,gss_ctx_id_t context_handle,gss_qop_t * qop_state,gss_iov_buffer_desc * iov,int iov_count)3006*7f2fe78bSCy Schubert spnego_gss_verify_mic_iov(OM_uint32 *minor_status, gss_ctx_id_t context_handle,
3007*7f2fe78bSCy Schubert 			  gss_qop_t *qop_state, gss_iov_buffer_desc *iov,
3008*7f2fe78bSCy Schubert 			  int iov_count)
3009*7f2fe78bSCy Schubert {
3010*7f2fe78bSCy Schubert     spnego_gss_ctx_id_t sc = (spnego_gss_ctx_id_t)context_handle;
3011*7f2fe78bSCy Schubert 
3012*7f2fe78bSCy Schubert     if (sc->ctx_handle == GSS_C_NO_CONTEXT)
3013*7f2fe78bSCy Schubert 	    return (GSS_S_NO_CONTEXT);
3014*7f2fe78bSCy Schubert 
3015*7f2fe78bSCy Schubert     return gss_verify_mic_iov(minor_status, sc->ctx_handle, qop_state, iov,
3016*7f2fe78bSCy Schubert 			      iov_count);
3017*7f2fe78bSCy Schubert }
3018*7f2fe78bSCy Schubert 
3019*7f2fe78bSCy Schubert OM_uint32 KRB5_CALLCONV
spnego_gss_get_mic_iov_length(OM_uint32 * minor_status,gss_ctx_id_t context_handle,gss_qop_t qop_req,gss_iov_buffer_desc * iov,int iov_count)3020*7f2fe78bSCy Schubert spnego_gss_get_mic_iov_length(OM_uint32 *minor_status,
3021*7f2fe78bSCy Schubert 			      gss_ctx_id_t context_handle, gss_qop_t qop_req,
3022*7f2fe78bSCy Schubert 			      gss_iov_buffer_desc *iov, int iov_count)
3023*7f2fe78bSCy Schubert {
3024*7f2fe78bSCy Schubert     spnego_gss_ctx_id_t sc = (spnego_gss_ctx_id_t)context_handle;
3025*7f2fe78bSCy Schubert 
3026*7f2fe78bSCy Schubert     if (sc->ctx_handle == GSS_C_NO_CONTEXT)
3027*7f2fe78bSCy Schubert 	    return (GSS_S_NO_CONTEXT);
3028*7f2fe78bSCy Schubert 
3029*7f2fe78bSCy Schubert     return gss_get_mic_iov_length(minor_status, sc->ctx_handle, qop_req, iov,
3030*7f2fe78bSCy Schubert 				  iov_count);
3031*7f2fe78bSCy Schubert }
3032*7f2fe78bSCy Schubert 
3033*7f2fe78bSCy Schubert /*
3034*7f2fe78bSCy Schubert  * We will release everything but the ctx_handle so that it
3035*7f2fe78bSCy Schubert  * can be passed back to init/accept context. This routine should
3036*7f2fe78bSCy Schubert  * not be called until after the ctx_handle memory is assigned to
3037*7f2fe78bSCy Schubert  * the supplied context handle from init/accept context.
3038*7f2fe78bSCy Schubert  */
3039*7f2fe78bSCy Schubert static void
release_spnego_ctx(spnego_gss_ctx_id_t * ctx)3040*7f2fe78bSCy Schubert release_spnego_ctx(spnego_gss_ctx_id_t *ctx)
3041*7f2fe78bSCy Schubert {
3042*7f2fe78bSCy Schubert 	spnego_gss_ctx_id_t context;
3043*7f2fe78bSCy Schubert 	OM_uint32 minor_stat;
3044*7f2fe78bSCy Schubert 	context = *ctx;
3045*7f2fe78bSCy Schubert 
3046*7f2fe78bSCy Schubert 	if (context != NULL) {
3047*7f2fe78bSCy Schubert 		(void) gss_release_buffer(&minor_stat,
3048*7f2fe78bSCy Schubert 					&context->DER_mechTypes);
3049*7f2fe78bSCy Schubert 
3050*7f2fe78bSCy Schubert 		(void) gss_release_oid_set(&minor_stat, &context->mech_set);
3051*7f2fe78bSCy Schubert 
3052*7f2fe78bSCy Schubert 		(void) gss_release_name(&minor_stat, &context->internal_name);
3053*7f2fe78bSCy Schubert 		(void) gss_release_cred(&minor_stat, &context->deleg_cred);
3054*7f2fe78bSCy Schubert 
3055*7f2fe78bSCy Schubert 		negoex_release_context(context);
3056*7f2fe78bSCy Schubert 
3057*7f2fe78bSCy Schubert 		free(context);
3058*7f2fe78bSCy Schubert 		*ctx = NULL;
3059*7f2fe78bSCy Schubert 	}
3060*7f2fe78bSCy Schubert }
3061*7f2fe78bSCy Schubert 
3062*7f2fe78bSCy Schubert /*
3063*7f2fe78bSCy Schubert  * Can't use gss_indicate_mechs by itself to get available mechs for
3064*7f2fe78bSCy Schubert  * SPNEGO because it will also return the SPNEGO mech and we do not
3065*7f2fe78bSCy Schubert  * want to consider SPNEGO as an available security mech for
3066*7f2fe78bSCy Schubert  * negotiation. For this reason, get_available_mechs will return
3067*7f2fe78bSCy Schubert  * all available, non-deprecated mechs except SPNEGO and NegoEx-
3068*7f2fe78bSCy Schubert  * only mechanisms.
3069*7f2fe78bSCy Schubert  *
3070*7f2fe78bSCy Schubert  * Note that gss_acquire_cred_from(GSS_C_NO_OID_SET) will filter
3071*7f2fe78bSCy Schubert  * out hidden (GSS_C_MA_NOT_INDICATED) mechanisms such as NegoEx, so
3072*7f2fe78bSCy Schubert  * calling gss_indicate_mechs_by_attrs() also works around that.
3073*7f2fe78bSCy Schubert  *
3074*7f2fe78bSCy Schubert  * If a ptr to a creds list is given, this function will attempt
3075*7f2fe78bSCy Schubert  * to acquire creds for the creds given and trim the list of
3076*7f2fe78bSCy Schubert  * returned mechanisms to only those for which creds are valid.
3077*7f2fe78bSCy Schubert  *
3078*7f2fe78bSCy Schubert  */
3079*7f2fe78bSCy Schubert static OM_uint32
get_available_mechs(OM_uint32 * minor_status,gss_name_t name,gss_cred_usage_t usage,gss_const_key_value_set_t cred_store,gss_cred_id_t * creds,gss_OID_set * rmechs,OM_uint32 * time_rec)3080*7f2fe78bSCy Schubert get_available_mechs(OM_uint32 *minor_status,
3081*7f2fe78bSCy Schubert 	gss_name_t name, gss_cred_usage_t usage,
3082*7f2fe78bSCy Schubert 	gss_const_key_value_set_t cred_store,
3083*7f2fe78bSCy Schubert 	gss_cred_id_t *creds, gss_OID_set *rmechs, OM_uint32 *time_rec)
3084*7f2fe78bSCy Schubert {
3085*7f2fe78bSCy Schubert 	OM_uint32 major_status = GSS_S_COMPLETE, tmpmin;
3086*7f2fe78bSCy Schubert 	gss_OID_set mechs, goodmechs;
3087*7f2fe78bSCy Schubert 	gss_OID_set_desc except_attrs;
3088*7f2fe78bSCy Schubert 	gss_OID_desc attr_oids[3];
3089*7f2fe78bSCy Schubert 
3090*7f2fe78bSCy Schubert 	*rmechs = GSS_C_NO_OID_SET;
3091*7f2fe78bSCy Schubert 
3092*7f2fe78bSCy Schubert 	attr_oids[0] = *GSS_C_MA_DEPRECATED;
3093*7f2fe78bSCy Schubert 	attr_oids[1] = *GSS_C_MA_NOT_DFLT_MECH;
3094*7f2fe78bSCy Schubert 	attr_oids[2] = *GSS_C_MA_MECH_NEGO;     /* Exclude ourselves */
3095*7f2fe78bSCy Schubert 	except_attrs.count = sizeof(attr_oids) / sizeof(attr_oids[0]);
3096*7f2fe78bSCy Schubert 	except_attrs.elements = attr_oids;
3097*7f2fe78bSCy Schubert 	major_status = gss_indicate_mechs_by_attrs(minor_status,
3098*7f2fe78bSCy Schubert 						   GSS_C_NO_OID_SET,
3099*7f2fe78bSCy Schubert 						   &except_attrs,
3100*7f2fe78bSCy Schubert 						   GSS_C_NO_OID_SET, &mechs);
3101*7f2fe78bSCy Schubert 
3102*7f2fe78bSCy Schubert 	/*
3103*7f2fe78bSCy Schubert 	 * If the caller wanted a list of creds returned,
3104*7f2fe78bSCy Schubert 	 * trim the list of mechanisms down to only those
3105*7f2fe78bSCy Schubert 	 * for which the creds are valid.
3106*7f2fe78bSCy Schubert 	 */
3107*7f2fe78bSCy Schubert 	if (mechs->count > 0 && major_status == GSS_S_COMPLETE &&
3108*7f2fe78bSCy Schubert 	    creds != NULL) {
3109*7f2fe78bSCy Schubert 		major_status = gss_acquire_cred_from(minor_status, name,
3110*7f2fe78bSCy Schubert 						     GSS_C_INDEFINITE,
3111*7f2fe78bSCy Schubert 						     mechs, usage,
3112*7f2fe78bSCy Schubert 						     cred_store, creds,
3113*7f2fe78bSCy Schubert 						     &goodmechs, time_rec);
3114*7f2fe78bSCy Schubert 
3115*7f2fe78bSCy Schubert 		/*
3116*7f2fe78bSCy Schubert 		 * Drop the old list in favor of the new
3117*7f2fe78bSCy Schubert 		 * "trimmed" list.
3118*7f2fe78bSCy Schubert 		 */
3119*7f2fe78bSCy Schubert 		if (major_status == GSS_S_COMPLETE) {
3120*7f2fe78bSCy Schubert 			(void) gss_release_oid_set(&tmpmin, &mechs);
3121*7f2fe78bSCy Schubert 			mechs = goodmechs;
3122*7f2fe78bSCy Schubert 		}
3123*7f2fe78bSCy Schubert 	}
3124*7f2fe78bSCy Schubert 
3125*7f2fe78bSCy Schubert 	if (mechs->count > 0 && major_status == GSS_S_COMPLETE) {
3126*7f2fe78bSCy Schubert 		*rmechs = mechs;
3127*7f2fe78bSCy Schubert 	} else {
3128*7f2fe78bSCy Schubert 		(void) gss_release_oid_set(&tmpmin, &mechs);
3129*7f2fe78bSCy Schubert 		*minor_status = ERR_SPNEGO_NO_MECHS_AVAILABLE;
3130*7f2fe78bSCy Schubert 		map_errcode(minor_status);
3131*7f2fe78bSCy Schubert 		if (major_status == GSS_S_COMPLETE)
3132*7f2fe78bSCy Schubert 			major_status = GSS_S_FAILURE;
3133*7f2fe78bSCy Schubert 	}
3134*7f2fe78bSCy Schubert 
3135*7f2fe78bSCy Schubert 	return (major_status);
3136*7f2fe78bSCy Schubert }
3137*7f2fe78bSCy Schubert 
3138*7f2fe78bSCy Schubert /* Return true if mech asserts the GSS_C_MA_NEGOEX_AND_SPNEGO attribute. */
3139*7f2fe78bSCy Schubert static int
negoex_and_spnego(gss_OID mech)3140*7f2fe78bSCy Schubert negoex_and_spnego(gss_OID mech)
3141*7f2fe78bSCy Schubert {
3142*7f2fe78bSCy Schubert 	OM_uint32 ret, minor;
3143*7f2fe78bSCy Schubert 	gss_OID_set attrs;
3144*7f2fe78bSCy Schubert 	int present;
3145*7f2fe78bSCy Schubert 
3146*7f2fe78bSCy Schubert 	ret = gss_inquire_attrs_for_mech(&minor, mech, &attrs, NULL);
3147*7f2fe78bSCy Schubert 	if (ret != GSS_S_COMPLETE || attrs == GSS_C_NO_OID_SET)
3148*7f2fe78bSCy Schubert 		return 0;
3149*7f2fe78bSCy Schubert 
3150*7f2fe78bSCy Schubert 	(void) generic_gss_test_oid_set_member(&minor,
3151*7f2fe78bSCy Schubert 					       GSS_C_MA_NEGOEX_AND_SPNEGO,
3152*7f2fe78bSCy Schubert 					       attrs, &present);
3153*7f2fe78bSCy Schubert 	(void) gss_release_oid_set(&minor, &attrs);
3154*7f2fe78bSCy Schubert 	return present;
3155*7f2fe78bSCy Schubert }
3156*7f2fe78bSCy Schubert 
3157*7f2fe78bSCy Schubert /*
3158*7f2fe78bSCy Schubert  * Fill sc->mech_set with the SPNEGO-negotiable mechanism OIDs, and
3159*7f2fe78bSCy Schubert  * sc->negoex_mechs with an entry for each NegoEx-negotiable mechanism.  Take
3160*7f2fe78bSCy Schubert  * into account the mech set provided with gss_set_neg_mechs() if it exists.
3161*7f2fe78bSCy Schubert  */
3162*7f2fe78bSCy Schubert static OM_uint32
get_negotiable_mechs(OM_uint32 * minor_status,spnego_gss_ctx_id_t sc,spnego_gss_cred_id_t spcred,gss_cred_usage_t usage)3163*7f2fe78bSCy Schubert get_negotiable_mechs(OM_uint32 *minor_status, spnego_gss_ctx_id_t sc,
3164*7f2fe78bSCy Schubert 		     spnego_gss_cred_id_t spcred, gss_cred_usage_t usage)
3165*7f2fe78bSCy Schubert {
3166*7f2fe78bSCy Schubert 	OM_uint32 ret, tmpmin;
3167*7f2fe78bSCy Schubert 	gss_cred_id_t creds = GSS_C_NO_CREDENTIAL;
3168*7f2fe78bSCy Schubert 	gss_OID_set cred_mechs = GSS_C_NULL_OID_SET, mechs;
3169*7f2fe78bSCy Schubert 	unsigned int i;
3170*7f2fe78bSCy Schubert 	int present, added_negoex = 0;
3171*7f2fe78bSCy Schubert 	auth_scheme scheme;
3172*7f2fe78bSCy Schubert 
3173*7f2fe78bSCy Schubert 	if (spcred != NULL) {
3174*7f2fe78bSCy Schubert 		/* Get the list of mechs in the mechglue cred. */
3175*7f2fe78bSCy Schubert 		ret = gss_inquire_cred(minor_status, spcred->mcred, NULL,
3176*7f2fe78bSCy Schubert 				       NULL, NULL, &cred_mechs);
3177*7f2fe78bSCy Schubert 		if (ret != GSS_S_COMPLETE)
3178*7f2fe78bSCy Schubert 			return (ret);
3179*7f2fe78bSCy Schubert 	} else {
3180*7f2fe78bSCy Schubert 		/* Start with the list of available mechs. */
3181*7f2fe78bSCy Schubert 		ret = get_available_mechs(minor_status, GSS_C_NO_NAME, usage,
3182*7f2fe78bSCy Schubert 					  GSS_C_NO_CRED_STORE, &creds,
3183*7f2fe78bSCy Schubert 					  &cred_mechs, NULL);
3184*7f2fe78bSCy Schubert 		if (ret != GSS_S_COMPLETE)
3185*7f2fe78bSCy Schubert 			return (ret);
3186*7f2fe78bSCy Schubert 		gss_release_cred(&tmpmin, &creds);
3187*7f2fe78bSCy Schubert 	}
3188*7f2fe78bSCy Schubert 
3189*7f2fe78bSCy Schubert 	/* If gss_set_neg_mechs() was called, use that to determine the
3190*7f2fe78bSCy Schubert 	 * iteration order.  Otherwise iterate over the credential mechs. */
3191*7f2fe78bSCy Schubert 	mechs = (spcred != NULL && spcred->neg_mechs != GSS_C_NULL_OID_SET) ?
3192*7f2fe78bSCy Schubert 	    spcred->neg_mechs : cred_mechs;
3193*7f2fe78bSCy Schubert 
3194*7f2fe78bSCy Schubert 	ret = gss_create_empty_oid_set(minor_status, &sc->mech_set);
3195*7f2fe78bSCy Schubert 	if (ret != GSS_S_COMPLETE)
3196*7f2fe78bSCy Schubert 		goto cleanup;
3197*7f2fe78bSCy Schubert 
3198*7f2fe78bSCy Schubert 	for (i = 0; i < mechs->count; i++) {
3199*7f2fe78bSCy Schubert 		if (mechs != cred_mechs) {
3200*7f2fe78bSCy Schubert 			/* Intersect neg_mechs with cred_mechs. */
3201*7f2fe78bSCy Schubert 			gss_test_oid_set_member(&tmpmin, &mechs->elements[i],
3202*7f2fe78bSCy Schubert 						cred_mechs, &present);
3203*7f2fe78bSCy Schubert 			if (!present)
3204*7f2fe78bSCy Schubert 				continue;
3205*7f2fe78bSCy Schubert 		}
3206*7f2fe78bSCy Schubert 
3207*7f2fe78bSCy Schubert 		/* Query the auth scheme to see if this is a NegoEx mech. */
3208*7f2fe78bSCy Schubert 		ret = gssspi_query_mechanism_info(&tmpmin, &mechs->elements[i],
3209*7f2fe78bSCy Schubert 						  scheme);
3210*7f2fe78bSCy Schubert 		if (ret == GSS_S_COMPLETE) {
3211*7f2fe78bSCy Schubert 			/* Add an entry for this mech to the NegoEx list. */
3212*7f2fe78bSCy Schubert 			ret = negoex_add_auth_mech(minor_status, sc,
3213*7f2fe78bSCy Schubert 						   &mechs->elements[i],
3214*7f2fe78bSCy Schubert 						   scheme);
3215*7f2fe78bSCy Schubert 			if (ret != GSS_S_COMPLETE)
3216*7f2fe78bSCy Schubert 				goto cleanup;
3217*7f2fe78bSCy Schubert 
3218*7f2fe78bSCy Schubert 			/* Add the NegoEx OID to the SPNEGO list at the
3219*7f2fe78bSCy Schubert 			 * position of the first NegoEx mechanism. */
3220*7f2fe78bSCy Schubert 			if (!added_negoex) {
3221*7f2fe78bSCy Schubert 				ret = gss_add_oid_set_member(minor_status,
3222*7f2fe78bSCy Schubert 							     &negoex_mech,
3223*7f2fe78bSCy Schubert 							     &sc->mech_set);
3224*7f2fe78bSCy Schubert 				if (ret != GSS_S_COMPLETE)
3225*7f2fe78bSCy Schubert 					goto cleanup;
3226*7f2fe78bSCy Schubert 				added_negoex = 1;
3227*7f2fe78bSCy Schubert 			}
3228*7f2fe78bSCy Schubert 
3229*7f2fe78bSCy Schubert 			/* Skip this mech in the SPNEGO list unless it asks for
3230*7f2fe78bSCy Schubert 			 * direct SPNEGO negotiation. */
3231*7f2fe78bSCy Schubert 			if (!negoex_and_spnego(&mechs->elements[i]))
3232*7f2fe78bSCy Schubert 				continue;
3233*7f2fe78bSCy Schubert 		}
3234*7f2fe78bSCy Schubert 
3235*7f2fe78bSCy Schubert 		/* Add this mech to the SPNEGO list. */
3236*7f2fe78bSCy Schubert 		ret = gss_add_oid_set_member(minor_status, &mechs->elements[i],
3237*7f2fe78bSCy Schubert 					     &sc->mech_set);
3238*7f2fe78bSCy Schubert 		if (ret != GSS_S_COMPLETE)
3239*7f2fe78bSCy Schubert 			goto cleanup;
3240*7f2fe78bSCy Schubert 	}
3241*7f2fe78bSCy Schubert 
3242*7f2fe78bSCy Schubert 	*minor_status = 0;
3243*7f2fe78bSCy Schubert 
3244*7f2fe78bSCy Schubert cleanup:
3245*7f2fe78bSCy Schubert 	if (ret != GSS_S_COMPLETE || sc->mech_set->count == 0) {
3246*7f2fe78bSCy Schubert 		*minor_status = ERR_SPNEGO_NO_MECHS_AVAILABLE;
3247*7f2fe78bSCy Schubert 		map_errcode(minor_status);
3248*7f2fe78bSCy Schubert 		ret = GSS_S_FAILURE;
3249*7f2fe78bSCy Schubert 	}
3250*7f2fe78bSCy Schubert 
3251*7f2fe78bSCy Schubert 	gss_release_oid_set(&tmpmin, &cred_mechs);
3252*7f2fe78bSCy Schubert 	return (ret);
3253*7f2fe78bSCy Schubert }
3254*7f2fe78bSCy Schubert 
3255*7f2fe78bSCy Schubert /* following are token creation and reading routines */
3256*7f2fe78bSCy Schubert 
3257*7f2fe78bSCy Schubert /*
3258*7f2fe78bSCy Schubert  * If in contains a tagged OID encoding, return a copy of the contents as a
3259*7f2fe78bSCy Schubert  * gss_OID and advance in past the encoding.  Otherwise return NULL and do not
3260*7f2fe78bSCy Schubert  * advance in.
3261*7f2fe78bSCy Schubert  */
3262*7f2fe78bSCy Schubert static gss_OID
get_mech_oid(OM_uint32 * minor_status,struct k5input * in)3263*7f2fe78bSCy Schubert get_mech_oid(OM_uint32 *minor_status, struct k5input *in)
3264*7f2fe78bSCy Schubert {
3265*7f2fe78bSCy Schubert 	struct k5input oidrep;
3266*7f2fe78bSCy Schubert 	OM_uint32 status;
3267*7f2fe78bSCy Schubert 	gss_OID_desc oid;
3268*7f2fe78bSCy Schubert 	gss_OID mech_out = NULL;
3269*7f2fe78bSCy Schubert 
3270*7f2fe78bSCy Schubert 	if (!k5_der_get_value(in, MECH_OID, &oidrep))
3271*7f2fe78bSCy Schubert 		return (NULL);
3272*7f2fe78bSCy Schubert 
3273*7f2fe78bSCy Schubert 	oid.length = oidrep.len;
3274*7f2fe78bSCy Schubert 	oid.elements = (uint8_t *)oidrep.ptr;
3275*7f2fe78bSCy Schubert 	status = generic_gss_copy_oid(minor_status, &oid, &mech_out);
3276*7f2fe78bSCy Schubert 	if (status != GSS_S_COMPLETE) {
3277*7f2fe78bSCy Schubert 		map_errcode(minor_status);
3278*7f2fe78bSCy Schubert 		mech_out = NULL;
3279*7f2fe78bSCy Schubert 	}
3280*7f2fe78bSCy Schubert 
3281*7f2fe78bSCy Schubert 	return (mech_out);
3282*7f2fe78bSCy Schubert }
3283*7f2fe78bSCy Schubert 
3284*7f2fe78bSCy Schubert /*
3285*7f2fe78bSCy Schubert  * If in contains a tagged octet string encoding, return a copy of the contents
3286*7f2fe78bSCy Schubert  * as a gss_buffer_t and advance in past the encoding.  Otherwise return NULL
3287*7f2fe78bSCy Schubert  * and do not advance in.
3288*7f2fe78bSCy Schubert  */
3289*7f2fe78bSCy Schubert static gss_buffer_t
get_octet_string(struct k5input * in)3290*7f2fe78bSCy Schubert get_octet_string(struct k5input *in)
3291*7f2fe78bSCy Schubert {
3292*7f2fe78bSCy Schubert 	gss_buffer_t input_token;
3293*7f2fe78bSCy Schubert 	struct k5input ostr;
3294*7f2fe78bSCy Schubert 
3295*7f2fe78bSCy Schubert 	if (!k5_der_get_value(in, OCTET_STRING, &ostr))
3296*7f2fe78bSCy Schubert 		return (NULL);
3297*7f2fe78bSCy Schubert 
3298*7f2fe78bSCy Schubert 	input_token = (gss_buffer_t)malloc(sizeof (gss_buffer_desc));
3299*7f2fe78bSCy Schubert 	if (input_token == NULL)
3300*7f2fe78bSCy Schubert 		return (NULL);
3301*7f2fe78bSCy Schubert 
3302*7f2fe78bSCy Schubert 	input_token->length = ostr.len;
3303*7f2fe78bSCy Schubert 	if (input_token->length > 0) {
3304*7f2fe78bSCy Schubert 		input_token->value = gssalloc_malloc(input_token->length);
3305*7f2fe78bSCy Schubert 		if (input_token->value == NULL) {
3306*7f2fe78bSCy Schubert 			free(input_token);
3307*7f2fe78bSCy Schubert 			return (NULL);
3308*7f2fe78bSCy Schubert 		}
3309*7f2fe78bSCy Schubert 
3310*7f2fe78bSCy Schubert 		memcpy(input_token->value, ostr.ptr, input_token->length);
3311*7f2fe78bSCy Schubert 	} else {
3312*7f2fe78bSCy Schubert 		input_token->value = NULL;
3313*7f2fe78bSCy Schubert 	}
3314*7f2fe78bSCy Schubert 	return (input_token);
3315*7f2fe78bSCy Schubert }
3316*7f2fe78bSCy Schubert 
3317*7f2fe78bSCy Schubert /*
3318*7f2fe78bSCy Schubert  * verify that buff_in points to a sequence of der encoding. The mech
3319*7f2fe78bSCy Schubert  * set is the only sequence of encoded object in the token, so if it is
3320*7f2fe78bSCy Schubert  * a sequence of encoding, decode the mechset into a gss_OID_set and
3321*7f2fe78bSCy Schubert  * return it, advancing the buffer pointer.
3322*7f2fe78bSCy Schubert  */
3323*7f2fe78bSCy Schubert static gss_OID_set
get_mech_set(OM_uint32 * minor_status,struct k5input * in)3324*7f2fe78bSCy Schubert get_mech_set(OM_uint32 *minor_status, struct k5input *in)
3325*7f2fe78bSCy Schubert {
3326*7f2fe78bSCy Schubert 	gss_OID_set returned_mechSet;
3327*7f2fe78bSCy Schubert 	OM_uint32 major_status, tmpmin;
3328*7f2fe78bSCy Schubert 	struct k5input seq;
3329*7f2fe78bSCy Schubert 
3330*7f2fe78bSCy Schubert 	if (!k5_der_get_value(in, SEQUENCE_OF, &seq))
3331*7f2fe78bSCy Schubert 		return (NULL);
3332*7f2fe78bSCy Schubert 
3333*7f2fe78bSCy Schubert 	major_status = gss_create_empty_oid_set(minor_status,
3334*7f2fe78bSCy Schubert 						&returned_mechSet);
3335*7f2fe78bSCy Schubert 	if (major_status != GSS_S_COMPLETE)
3336*7f2fe78bSCy Schubert 		return (NULL);
3337*7f2fe78bSCy Schubert 
3338*7f2fe78bSCy Schubert 	while (!seq.status && seq.len > 0) {
3339*7f2fe78bSCy Schubert 		gss_OID_desc *oid = get_mech_oid(minor_status, &seq);
3340*7f2fe78bSCy Schubert 
3341*7f2fe78bSCy Schubert 		if (oid == NULL) {
3342*7f2fe78bSCy Schubert 			gss_release_oid_set(&tmpmin, &returned_mechSet);
3343*7f2fe78bSCy Schubert 			return (NULL);
3344*7f2fe78bSCy Schubert 		}
3345*7f2fe78bSCy Schubert 
3346*7f2fe78bSCy Schubert 		major_status = gss_add_oid_set_member(minor_status,
3347*7f2fe78bSCy Schubert 						      oid, &returned_mechSet);
3348*7f2fe78bSCy Schubert 		generic_gss_release_oid(minor_status, &oid);
3349*7f2fe78bSCy Schubert 		if (major_status != GSS_S_COMPLETE) {
3350*7f2fe78bSCy Schubert 			gss_release_oid_set(&tmpmin, &returned_mechSet);
3351*7f2fe78bSCy Schubert 			return (NULL);
3352*7f2fe78bSCy Schubert 		}
3353*7f2fe78bSCy Schubert 	}
3354*7f2fe78bSCy Schubert 
3355*7f2fe78bSCy Schubert 	return (returned_mechSet);
3356*7f2fe78bSCy Schubert }
3357*7f2fe78bSCy Schubert 
3358*7f2fe78bSCy Schubert /*
3359*7f2fe78bSCy Schubert  * Encode mechSet into buf.
3360*7f2fe78bSCy Schubert  */
3361*7f2fe78bSCy Schubert static int
put_mech_set(gss_OID_set mechSet,gss_buffer_t buffer_out)3362*7f2fe78bSCy Schubert put_mech_set(gss_OID_set mechSet, gss_buffer_t buffer_out)
3363*7f2fe78bSCy Schubert {
3364*7f2fe78bSCy Schubert 	uint8_t *ptr;
3365*7f2fe78bSCy Schubert 	size_t ilen, tlen, i;
3366*7f2fe78bSCy Schubert 	struct k5buf buf;
3367*7f2fe78bSCy Schubert 
3368*7f2fe78bSCy Schubert 	ilen = 0;
3369*7f2fe78bSCy Schubert 	for (i = 0; i < mechSet->count; i++)
3370*7f2fe78bSCy Schubert 	    ilen += k5_der_value_len(mechSet->elements[i].length);
3371*7f2fe78bSCy Schubert 	tlen = k5_der_value_len(ilen);
3372*7f2fe78bSCy Schubert 
3373*7f2fe78bSCy Schubert 	ptr = gssalloc_malloc(tlen);
3374*7f2fe78bSCy Schubert 	if (ptr == NULL)
3375*7f2fe78bSCy Schubert 		return -1;
3376*7f2fe78bSCy Schubert 	k5_buf_init_fixed(&buf, ptr, tlen);
3377*7f2fe78bSCy Schubert 
3378*7f2fe78bSCy Schubert 	k5_der_add_taglen(&buf, SEQUENCE_OF, ilen);
3379*7f2fe78bSCy Schubert 	for (i = 0; i < mechSet->count; i++) {
3380*7f2fe78bSCy Schubert 		k5_der_add_value(&buf, MECH_OID,
3381*7f2fe78bSCy Schubert 				 mechSet->elements[i].elements,
3382*7f2fe78bSCy Schubert 				 mechSet->elements[i].length);
3383*7f2fe78bSCy Schubert 	}
3384*7f2fe78bSCy Schubert 	assert(buf.len == tlen);
3385*7f2fe78bSCy Schubert 
3386*7f2fe78bSCy Schubert 	buffer_out->value = ptr;
3387*7f2fe78bSCy Schubert 	buffer_out->length = tlen;
3388*7f2fe78bSCy Schubert 	return 0;
3389*7f2fe78bSCy Schubert }
3390*7f2fe78bSCy Schubert 
3391*7f2fe78bSCy Schubert /* Decode SPNEGO request flags from the DER encoding of a bit string and set
3392*7f2fe78bSCy Schubert  * them in *ret_flags. */
3393*7f2fe78bSCy Schubert static OM_uint32
get_req_flags(struct k5input * in,OM_uint32 * req_flags)3394*7f2fe78bSCy Schubert get_req_flags(struct k5input *in, OM_uint32 *req_flags)
3395*7f2fe78bSCy Schubert {
3396*7f2fe78bSCy Schubert 	if (in->status || in->len != 4 ||
3397*7f2fe78bSCy Schubert 	    k5_input_get_byte(in) != BIT_STRING ||
3398*7f2fe78bSCy Schubert 	    k5_input_get_byte(in) != BIT_STRING_LENGTH ||
3399*7f2fe78bSCy Schubert 	    k5_input_get_byte(in) != BIT_STRING_PADDING)
3400*7f2fe78bSCy Schubert 		return GSS_S_DEFECTIVE_TOKEN;
3401*7f2fe78bSCy Schubert 
3402*7f2fe78bSCy Schubert 	*req_flags = k5_input_get_byte(in) >> 1;
3403*7f2fe78bSCy Schubert 	return GSS_S_COMPLETE;
3404*7f2fe78bSCy Schubert }
3405*7f2fe78bSCy Schubert 
3406*7f2fe78bSCy Schubert static OM_uint32
get_negTokenInit(OM_uint32 * minor_status,gss_buffer_t buf,gss_buffer_t der_mechSet,gss_OID_set * mechSet,OM_uint32 * req_flags,gss_buffer_t * mechtok,gss_buffer_t * mechListMIC)3407*7f2fe78bSCy Schubert get_negTokenInit(OM_uint32 *minor_status,
3408*7f2fe78bSCy Schubert 		 gss_buffer_t buf,
3409*7f2fe78bSCy Schubert 		 gss_buffer_t der_mechSet,
3410*7f2fe78bSCy Schubert 		 gss_OID_set *mechSet,
3411*7f2fe78bSCy Schubert 		 OM_uint32 *req_flags,
3412*7f2fe78bSCy Schubert 		 gss_buffer_t *mechtok,
3413*7f2fe78bSCy Schubert 		 gss_buffer_t *mechListMIC)
3414*7f2fe78bSCy Schubert {
3415*7f2fe78bSCy Schubert 	OM_uint32 err;
3416*7f2fe78bSCy Schubert 	struct k5input in, seq, field;
3417*7f2fe78bSCy Schubert 
3418*7f2fe78bSCy Schubert 	*minor_status = 0;
3419*7f2fe78bSCy Schubert 	der_mechSet->length = 0;
3420*7f2fe78bSCy Schubert 	der_mechSet->value = NULL;
3421*7f2fe78bSCy Schubert 	*mechSet = GSS_C_NO_OID_SET;
3422*7f2fe78bSCy Schubert 	*req_flags = 0;
3423*7f2fe78bSCy Schubert 	*mechtok = *mechListMIC = GSS_C_NO_BUFFER;
3424*7f2fe78bSCy Schubert 
3425*7f2fe78bSCy Schubert 	k5_input_init(&in, buf->value, buf->length);
3426*7f2fe78bSCy Schubert 
3427*7f2fe78bSCy Schubert 	/* Advance past the framing header. */
3428*7f2fe78bSCy Schubert 	err = verify_token_header(&in, gss_mech_spnego);
3429*7f2fe78bSCy Schubert 	if (err)
3430*7f2fe78bSCy Schubert 		return GSS_S_DEFECTIVE_TOKEN;
3431*7f2fe78bSCy Schubert 
3432*7f2fe78bSCy Schubert 	/* Advance past the [0] tag for the NegotiationToken choice. */
3433*7f2fe78bSCy Schubert 	if (!k5_der_get_value(&in, CONTEXT, &seq))
3434*7f2fe78bSCy Schubert 		return GSS_S_DEFECTIVE_TOKEN;
3435*7f2fe78bSCy Schubert 
3436*7f2fe78bSCy Schubert 	/* Advance past the SEQUENCE tag. */
3437*7f2fe78bSCy Schubert 	if (!k5_der_get_value(&seq, SEQUENCE, &seq))
3438*7f2fe78bSCy Schubert 		return GSS_S_DEFECTIVE_TOKEN;
3439*7f2fe78bSCy Schubert 
3440*7f2fe78bSCy Schubert 	/* Get the contents of the mechTypes field.  Reject an empty field here
3441*7f2fe78bSCy Schubert 	 * since we musn't allocate a zero-length buffer in the next step. */
3442*7f2fe78bSCy Schubert 	if (!k5_der_get_value(&seq, CONTEXT, &field) || field.len == 0)
3443*7f2fe78bSCy Schubert 		return GSS_S_DEFECTIVE_TOKEN;
3444*7f2fe78bSCy Schubert 
3445*7f2fe78bSCy Schubert 	/* Store a copy of the contents for MIC computation. */
3446*7f2fe78bSCy Schubert 	der_mechSet->value = gssalloc_malloc(field.len);
3447*7f2fe78bSCy Schubert 	if (der_mechSet->value == NULL)
3448*7f2fe78bSCy Schubert 		return GSS_S_FAILURE;
3449*7f2fe78bSCy Schubert 	memcpy(der_mechSet->value, field.ptr, field.len);
3450*7f2fe78bSCy Schubert 	der_mechSet->length = field.len;
3451*7f2fe78bSCy Schubert 
3452*7f2fe78bSCy Schubert 	/* Decode the contents into an OID set. */
3453*7f2fe78bSCy Schubert 	*mechSet = get_mech_set(minor_status, &field);
3454*7f2fe78bSCy Schubert 	if (*mechSet == NULL)
3455*7f2fe78bSCy Schubert 		return GSS_S_FAILURE;
3456*7f2fe78bSCy Schubert 
3457*7f2fe78bSCy Schubert 	if (k5_der_get_value(&seq, CONTEXT | 0x01, &field)) {
3458*7f2fe78bSCy Schubert 		err = get_req_flags(&field, req_flags);
3459*7f2fe78bSCy Schubert 		if (err != GSS_S_COMPLETE)
3460*7f2fe78bSCy Schubert 			return err;
3461*7f2fe78bSCy Schubert 	}
3462*7f2fe78bSCy Schubert 
3463*7f2fe78bSCy Schubert 	if (k5_der_get_value(&seq, CONTEXT | 0x02, &field)) {
3464*7f2fe78bSCy Schubert 		*mechtok = get_octet_string(&field);
3465*7f2fe78bSCy Schubert 		if (*mechtok == GSS_C_NO_BUFFER)
3466*7f2fe78bSCy Schubert 			return GSS_S_FAILURE;
3467*7f2fe78bSCy Schubert 	}
3468*7f2fe78bSCy Schubert 
3469*7f2fe78bSCy Schubert 	if (k5_der_get_value(&seq, CONTEXT | 0x03, &field)) {
3470*7f2fe78bSCy Schubert 		*mechListMIC = get_octet_string(&field);
3471*7f2fe78bSCy Schubert 		if (*mechListMIC == GSS_C_NO_BUFFER)
3472*7f2fe78bSCy Schubert 			return GSS_S_FAILURE;
3473*7f2fe78bSCy Schubert 	}
3474*7f2fe78bSCy Schubert 
3475*7f2fe78bSCy Schubert 	return seq.status ? GSS_S_DEFECTIVE_TOKEN : GSS_S_COMPLETE;
3476*7f2fe78bSCy Schubert }
3477*7f2fe78bSCy Schubert 
3478*7f2fe78bSCy Schubert /* Decode a NegotiationToken of type negTokenResp. */
3479*7f2fe78bSCy Schubert static OM_uint32
get_negTokenResp(OM_uint32 * minor_status,struct k5input * in,OM_uint32 * negState,gss_OID * supportedMech,gss_buffer_t * responseToken,gss_buffer_t * mechListMIC)3480*7f2fe78bSCy Schubert get_negTokenResp(OM_uint32 *minor_status, struct k5input *in,
3481*7f2fe78bSCy Schubert 		 OM_uint32 *negState, gss_OID *supportedMech,
3482*7f2fe78bSCy Schubert 		 gss_buffer_t *responseToken, gss_buffer_t *mechListMIC)
3483*7f2fe78bSCy Schubert {
3484*7f2fe78bSCy Schubert 	struct k5input seq, field, en;
3485*7f2fe78bSCy Schubert 
3486*7f2fe78bSCy Schubert 	*negState = UNSPECIFIED;
3487*7f2fe78bSCy Schubert 	*supportedMech = GSS_C_NO_OID;
3488*7f2fe78bSCy Schubert 	*responseToken = *mechListMIC = GSS_C_NO_BUFFER;
3489*7f2fe78bSCy Schubert 
3490*7f2fe78bSCy Schubert 	/* Advance past the [1] tag for the NegotiationToken choice. */
3491*7f2fe78bSCy Schubert 	if (!k5_der_get_value(in, CONTEXT | 0x01, &seq))
3492*7f2fe78bSCy Schubert 		return GSS_S_DEFECTIVE_TOKEN;
3493*7f2fe78bSCy Schubert 
3494*7f2fe78bSCy Schubert 	/* Advance seq past the SEQUENCE tag (historically this code allows the
3495*7f2fe78bSCy Schubert 	 * tag to be missing). */
3496*7f2fe78bSCy Schubert 	(void)k5_der_get_value(&seq, SEQUENCE, &seq);
3497*7f2fe78bSCy Schubert 
3498*7f2fe78bSCy Schubert 	if (k5_der_get_value(&seq, CONTEXT, &field)) {
3499*7f2fe78bSCy Schubert 		if (!k5_der_get_value(&field, ENUMERATED, &en))
3500*7f2fe78bSCy Schubert 			return GSS_S_DEFECTIVE_TOKEN;
3501*7f2fe78bSCy Schubert 		if (en.len != ENUMERATION_LENGTH)
3502*7f2fe78bSCy Schubert 			return GSS_S_DEFECTIVE_TOKEN;
3503*7f2fe78bSCy Schubert 		*negState = *en.ptr;
3504*7f2fe78bSCy Schubert 	}
3505*7f2fe78bSCy Schubert 
3506*7f2fe78bSCy Schubert 	if (k5_der_get_value(&seq, CONTEXT | 0x01, &field)) {
3507*7f2fe78bSCy Schubert 		*supportedMech = get_mech_oid(minor_status, &field);
3508*7f2fe78bSCy Schubert 		if (*supportedMech == GSS_C_NO_OID)
3509*7f2fe78bSCy Schubert 			return GSS_S_DEFECTIVE_TOKEN;
3510*7f2fe78bSCy Schubert 	}
3511*7f2fe78bSCy Schubert 
3512*7f2fe78bSCy Schubert 	if (k5_der_get_value(&seq, CONTEXT | 0x02, &field)) {
3513*7f2fe78bSCy Schubert 		*responseToken = get_octet_string(&field);
3514*7f2fe78bSCy Schubert 		if (*responseToken == GSS_C_NO_BUFFER)
3515*7f2fe78bSCy Schubert 			return GSS_S_DEFECTIVE_TOKEN;
3516*7f2fe78bSCy Schubert 	}
3517*7f2fe78bSCy Schubert 
3518*7f2fe78bSCy Schubert 	if (k5_der_get_value(&seq, CONTEXT | 0x04, &field)) {
3519*7f2fe78bSCy Schubert 		*mechListMIC = get_octet_string(&field);
3520*7f2fe78bSCy Schubert 
3521*7f2fe78bSCy Schubert                 /* Handle Windows 2000 duplicate response token */
3522*7f2fe78bSCy Schubert                 if (*responseToken &&
3523*7f2fe78bSCy Schubert                     ((*responseToken)->length == (*mechListMIC)->length) &&
3524*7f2fe78bSCy Schubert                     !memcmp((*responseToken)->value, (*mechListMIC)->value,
3525*7f2fe78bSCy Schubert                             (*responseToken)->length)) {
3526*7f2fe78bSCy Schubert 			OM_uint32 tmpmin;
3527*7f2fe78bSCy Schubert 
3528*7f2fe78bSCy Schubert 			gss_release_buffer(&tmpmin, *mechListMIC);
3529*7f2fe78bSCy Schubert 			free(*mechListMIC);
3530*7f2fe78bSCy Schubert 			*mechListMIC = NULL;
3531*7f2fe78bSCy Schubert 		}
3532*7f2fe78bSCy Schubert 	}
3533*7f2fe78bSCy Schubert 
3534*7f2fe78bSCy Schubert 	return seq.status ? GSS_S_DEFECTIVE_TOKEN : GSS_S_COMPLETE;
3535*7f2fe78bSCy Schubert }
3536*7f2fe78bSCy Schubert 
3537*7f2fe78bSCy Schubert /*
3538*7f2fe78bSCy Schubert  * This routine compares the received mechset to the mechset that
3539*7f2fe78bSCy Schubert  * this server can support. It looks sequentially through the mechset
3540*7f2fe78bSCy Schubert  * and the first one that matches what the server can support is
3541*7f2fe78bSCy Schubert  * chosen as the negotiated mechanism. If one is found, negResult
3542*7f2fe78bSCy Schubert  * is set to ACCEPT_INCOMPLETE if it's the first mech, REQUEST_MIC if
3543*7f2fe78bSCy Schubert  * it's not the first mech, otherwise we return NULL and negResult
3544*7f2fe78bSCy Schubert  * is set to REJECT. The returned pointer is an alias into
3545*7f2fe78bSCy Schubert  * received->elements and should not be freed.
3546*7f2fe78bSCy Schubert  *
3547*7f2fe78bSCy Schubert  * NOTE: There is currently no way to specify a preference order of
3548*7f2fe78bSCy Schubert  * mechanisms supported by the acceptor.
3549*7f2fe78bSCy Schubert  */
3550*7f2fe78bSCy Schubert static gss_OID
negotiate_mech(spnego_gss_ctx_id_t ctx,gss_OID_set received,OM_uint32 * negResult)3551*7f2fe78bSCy Schubert negotiate_mech(spnego_gss_ctx_id_t ctx, gss_OID_set received,
3552*7f2fe78bSCy Schubert 	       OM_uint32 *negResult)
3553*7f2fe78bSCy Schubert {
3554*7f2fe78bSCy Schubert 	size_t i, j;
3555*7f2fe78bSCy Schubert 	int wrong_krb5_oid;
3556*7f2fe78bSCy Schubert 
3557*7f2fe78bSCy Schubert 	for (i = 0; i < received->count; i++) {
3558*7f2fe78bSCy Schubert 		gss_OID mech_oid = &received->elements[i];
3559*7f2fe78bSCy Schubert 
3560*7f2fe78bSCy Schubert 		/* Accept wrong mechanism OID from MS clients */
3561*7f2fe78bSCy Schubert 		wrong_krb5_oid = 0;
3562*7f2fe78bSCy Schubert 		if (g_OID_equal(mech_oid, &gss_mech_krb5_wrong_oid)) {
3563*7f2fe78bSCy Schubert 			mech_oid = (gss_OID)&gss_mech_krb5_oid;
3564*7f2fe78bSCy Schubert 			wrong_krb5_oid = 1;
3565*7f2fe78bSCy Schubert 		}
3566*7f2fe78bSCy Schubert 
3567*7f2fe78bSCy Schubert 		for (j = 0; j < ctx->mech_set->count; j++) {
3568*7f2fe78bSCy Schubert 			if (g_OID_equal(mech_oid,
3569*7f2fe78bSCy Schubert 					&ctx->mech_set->elements[j])) {
3570*7f2fe78bSCy Schubert 				*negResult = (i == 0) ? ACCEPT_INCOMPLETE :
3571*7f2fe78bSCy Schubert 					REQUEST_MIC;
3572*7f2fe78bSCy Schubert 				return wrong_krb5_oid ?
3573*7f2fe78bSCy Schubert 					(gss_OID)&gss_mech_krb5_wrong_oid :
3574*7f2fe78bSCy Schubert 					&ctx->mech_set->elements[j];
3575*7f2fe78bSCy Schubert 			}
3576*7f2fe78bSCy Schubert 		}
3577*7f2fe78bSCy Schubert 	}
3578*7f2fe78bSCy Schubert 	*negResult = REJECT;
3579*7f2fe78bSCy Schubert 	return (NULL);
3580*7f2fe78bSCy Schubert }
3581*7f2fe78bSCy Schubert 
3582*7f2fe78bSCy Schubert /*
3583*7f2fe78bSCy Schubert  * the next two routines make a token buffer suitable for
3584*7f2fe78bSCy Schubert  * spnego_gss_display_status. These currently take the string
3585*7f2fe78bSCy Schubert  * in name and place it in the token. Eventually, if
3586*7f2fe78bSCy Schubert  * spnego_gss_display_status returns valid error messages,
3587*7f2fe78bSCy Schubert  * these routines will be changes to return the error string.
3588*7f2fe78bSCy Schubert  */
3589*7f2fe78bSCy Schubert static spnego_token_t
make_spnego_token(const char * name)3590*7f2fe78bSCy Schubert make_spnego_token(const char *name)
3591*7f2fe78bSCy Schubert {
3592*7f2fe78bSCy Schubert 	return (spnego_token_t)gssalloc_strdup(name);
3593*7f2fe78bSCy Schubert }
3594*7f2fe78bSCy Schubert 
3595*7f2fe78bSCy Schubert static gss_buffer_desc
make_err_msg(const char * name)3596*7f2fe78bSCy Schubert make_err_msg(const char *name)
3597*7f2fe78bSCy Schubert {
3598*7f2fe78bSCy Schubert 	gss_buffer_desc buffer;
3599*7f2fe78bSCy Schubert 
3600*7f2fe78bSCy Schubert 	if (name == NULL) {
3601*7f2fe78bSCy Schubert 		buffer.length = 0;
3602*7f2fe78bSCy Schubert 		buffer.value = NULL;
3603*7f2fe78bSCy Schubert 	} else {
3604*7f2fe78bSCy Schubert 		buffer.length = strlen(name)+1;
3605*7f2fe78bSCy Schubert 		buffer.value = make_spnego_token(name);
3606*7f2fe78bSCy Schubert 	}
3607*7f2fe78bSCy Schubert 
3608*7f2fe78bSCy Schubert 	return (buffer);
3609*7f2fe78bSCy Schubert }
3610*7f2fe78bSCy Schubert 
3611*7f2fe78bSCy Schubert /*
3612*7f2fe78bSCy Schubert  * Create the client side spnego token passed back to gss_init_sec_context
3613*7f2fe78bSCy Schubert  * and eventually up to the application program and over to the server.
3614*7f2fe78bSCy Schubert  *
3615*7f2fe78bSCy Schubert  * Use DER rules, definite length method per RFC 2478
3616*7f2fe78bSCy Schubert  */
3617*7f2fe78bSCy Schubert static int
make_spnego_tokenInit_msg(spnego_gss_ctx_id_t spnego_ctx,int negHintsCompat,gss_buffer_t mic,OM_uint32 req_flags,gss_buffer_t token,send_token_flag sendtoken,gss_buffer_t outbuf)3618*7f2fe78bSCy Schubert make_spnego_tokenInit_msg(spnego_gss_ctx_id_t spnego_ctx, int negHintsCompat,
3619*7f2fe78bSCy Schubert 			  gss_buffer_t mic, OM_uint32 req_flags,
3620*7f2fe78bSCy Schubert 			  gss_buffer_t token, send_token_flag sendtoken,
3621*7f2fe78bSCy Schubert 			  gss_buffer_t outbuf)
3622*7f2fe78bSCy Schubert {
3623*7f2fe78bSCy Schubert 	size_t f0len, f2len, f3len, fields_len, seq_len, choice_len;
3624*7f2fe78bSCy Schubert 	size_t mech_len, framed_len;
3625*7f2fe78bSCy Schubert 	uint8_t *t;
3626*7f2fe78bSCy Schubert 	struct k5buf buf;
3627*7f2fe78bSCy Schubert 
3628*7f2fe78bSCy Schubert 	if (outbuf == GSS_C_NO_BUFFER)
3629*7f2fe78bSCy Schubert 		return (-1);
3630*7f2fe78bSCy Schubert 
3631*7f2fe78bSCy Schubert 	outbuf->length = 0;
3632*7f2fe78bSCy Schubert 	outbuf->value = NULL;
3633*7f2fe78bSCy Schubert 
3634*7f2fe78bSCy Schubert 	/* Calculate the length of each field and the total fields length. */
3635*7f2fe78bSCy Schubert 	fields_len = 0;
3636*7f2fe78bSCy Schubert 	/* mechTypes [0] MechTypeList, previously assembled in spnego_ctx */
3637*7f2fe78bSCy Schubert 	f0len = spnego_ctx->DER_mechTypes.length;
3638*7f2fe78bSCy Schubert 	fields_len += k5_der_value_len(f0len);
3639*7f2fe78bSCy Schubert 	if (token != NULL) {
3640*7f2fe78bSCy Schubert 		/* mechToken [2] OCTET STRING OPTIONAL */
3641*7f2fe78bSCy Schubert 		f2len = k5_der_value_len(token->length);
3642*7f2fe78bSCy Schubert 		fields_len += k5_der_value_len(f2len);
3643*7f2fe78bSCy Schubert 	}
3644*7f2fe78bSCy Schubert 	if (mic != GSS_C_NO_BUFFER) {
3645*7f2fe78bSCy Schubert 		/* mechListMIC [3] OCTET STRING OPTIONAL */
3646*7f2fe78bSCy Schubert 		f3len = k5_der_value_len(mic->length);
3647*7f2fe78bSCy Schubert 		fields_len += k5_der_value_len(f3len);
3648*7f2fe78bSCy Schubert 	}
3649*7f2fe78bSCy Schubert 
3650*7f2fe78bSCy Schubert 	/* Calculate the length of the sequence and choice. */
3651*7f2fe78bSCy Schubert 	seq_len = k5_der_value_len(fields_len);
3652*7f2fe78bSCy Schubert 	choice_len = k5_der_value_len(seq_len);
3653*7f2fe78bSCy Schubert 
3654*7f2fe78bSCy Schubert 	/* Calculate the framed token length. */
3655*7f2fe78bSCy Schubert 	mech_len = k5_der_value_len(gss_mech_spnego->length);
3656*7f2fe78bSCy Schubert 	framed_len = k5_der_value_len(mech_len + choice_len);
3657*7f2fe78bSCy Schubert 
3658*7f2fe78bSCy Schubert 	/* Allocate space and prepare a buffer. */
3659*7f2fe78bSCy Schubert 	t = gssalloc_malloc(framed_len);
3660*7f2fe78bSCy Schubert 	if (t == NULL)
3661*7f2fe78bSCy Schubert 		return (-1);
3662*7f2fe78bSCy Schubert 	k5_buf_init_fixed(&buf, t, framed_len);
3663*7f2fe78bSCy Schubert 
3664*7f2fe78bSCy Schubert 	/* Add generic token framing. */
3665*7f2fe78bSCy Schubert 	k5_der_add_taglen(&buf, HEADER_ID, mech_len + choice_len);
3666*7f2fe78bSCy Schubert 	k5_der_add_value(&buf, MECH_OID, gss_mech_spnego->elements,
3667*7f2fe78bSCy Schubert 			 gss_mech_spnego->length);
3668*7f2fe78bSCy Schubert 
3669*7f2fe78bSCy Schubert 	/* Add NegotiationToken choice tag and NegTokenInit sequence tag. */
3670*7f2fe78bSCy Schubert 	k5_der_add_taglen(&buf, CONTEXT | 0x00, seq_len);
3671*7f2fe78bSCy Schubert 	k5_der_add_taglen(&buf, SEQUENCE, fields_len);
3672*7f2fe78bSCy Schubert 
3673*7f2fe78bSCy Schubert 	/* Add the already-encoded mechanism list as mechTypes. */
3674*7f2fe78bSCy Schubert 	k5_der_add_value(&buf, CONTEXT | 0x00, spnego_ctx->DER_mechTypes.value,
3675*7f2fe78bSCy Schubert 			 spnego_ctx->DER_mechTypes.length);
3676*7f2fe78bSCy Schubert 
3677*7f2fe78bSCy Schubert 	if (token != NULL) {
3678*7f2fe78bSCy Schubert 		k5_der_add_taglen(&buf, CONTEXT | 0x02, f2len);
3679*7f2fe78bSCy Schubert 		k5_der_add_value(&buf, OCTET_STRING, token->value,
3680*7f2fe78bSCy Schubert 				 token->length);
3681*7f2fe78bSCy Schubert 	}
3682*7f2fe78bSCy Schubert 
3683*7f2fe78bSCy Schubert 	if (mic != GSS_C_NO_BUFFER) {
3684*7f2fe78bSCy Schubert 		uint8_t id = negHintsCompat ? SEQUENCE : OCTET_STRING;
3685*7f2fe78bSCy Schubert 		k5_der_add_taglen(&buf, CONTEXT | 0x03, f3len);
3686*7f2fe78bSCy Schubert 		k5_der_add_value(&buf, id, mic->value, mic->length);
3687*7f2fe78bSCy Schubert 	}
3688*7f2fe78bSCy Schubert 
3689*7f2fe78bSCy Schubert 	assert(buf.len == framed_len);
3690*7f2fe78bSCy Schubert 	outbuf->length = framed_len;
3691*7f2fe78bSCy Schubert 	outbuf->value = t;
3692*7f2fe78bSCy Schubert 
3693*7f2fe78bSCy Schubert 	return (0);
3694*7f2fe78bSCy Schubert }
3695*7f2fe78bSCy Schubert 
3696*7f2fe78bSCy Schubert /*
3697*7f2fe78bSCy Schubert  * create the server side spnego token passed back to
3698*7f2fe78bSCy Schubert  * gss_accept_sec_context and eventually up to the application program
3699*7f2fe78bSCy Schubert  * and over to the client.
3700*7f2fe78bSCy Schubert  */
3701*7f2fe78bSCy Schubert static OM_uint32
make_spnego_tokenTarg_msg(uint8_t status,gss_OID mech_wanted,gss_buffer_t token,gss_buffer_t mic,send_token_flag sendtoken,gss_buffer_t outbuf)3702*7f2fe78bSCy Schubert make_spnego_tokenTarg_msg(uint8_t status, gss_OID mech_wanted,
3703*7f2fe78bSCy Schubert 			  gss_buffer_t token, gss_buffer_t mic,
3704*7f2fe78bSCy Schubert 			  send_token_flag sendtoken,
3705*7f2fe78bSCy Schubert 			  gss_buffer_t outbuf)
3706*7f2fe78bSCy Schubert {
3707*7f2fe78bSCy Schubert 	size_t f0len, f1len, f2len, f3len, fields_len, seq_len, choice_len;
3708*7f2fe78bSCy Schubert 	uint8_t *t;
3709*7f2fe78bSCy Schubert 	struct k5buf buf;
3710*7f2fe78bSCy Schubert 
3711*7f2fe78bSCy Schubert 	if (outbuf == GSS_C_NO_BUFFER)
3712*7f2fe78bSCy Schubert 		return (GSS_S_DEFECTIVE_TOKEN);
3713*7f2fe78bSCy Schubert 	if (sendtoken == INIT_TOKEN_SEND && mech_wanted == GSS_C_NO_OID)
3714*7f2fe78bSCy Schubert 		return (GSS_S_DEFECTIVE_TOKEN);
3715*7f2fe78bSCy Schubert 
3716*7f2fe78bSCy Schubert 	outbuf->length = 0;
3717*7f2fe78bSCy Schubert 	outbuf->value = NULL;
3718*7f2fe78bSCy Schubert 
3719*7f2fe78bSCy Schubert 	/* Calculate the length of each field and the total fields length. */
3720*7f2fe78bSCy Schubert 	fields_len = 0;
3721*7f2fe78bSCy Schubert 	/* negState [0] ENUMERATED { ... } OPTIONAL */
3722*7f2fe78bSCy Schubert 	f0len = k5_der_value_len(1);
3723*7f2fe78bSCy Schubert 	fields_len += k5_der_value_len(f0len);
3724*7f2fe78bSCy Schubert 	if (sendtoken == INIT_TOKEN_SEND) {
3725*7f2fe78bSCy Schubert 		/* supportedMech [1] MechType OPTIONAL */
3726*7f2fe78bSCy Schubert 		f1len = k5_der_value_len(mech_wanted->length);
3727*7f2fe78bSCy Schubert 		fields_len += k5_der_value_len(f1len);
3728*7f2fe78bSCy Schubert 	}
3729*7f2fe78bSCy Schubert 	if (token != NULL && token->length > 0) {
3730*7f2fe78bSCy Schubert 		/* mechToken [2] OCTET STRING OPTIONAL */
3731*7f2fe78bSCy Schubert 		f2len = k5_der_value_len(token->length);
3732*7f2fe78bSCy Schubert 		fields_len += k5_der_value_len(f2len);
3733*7f2fe78bSCy Schubert 	}
3734*7f2fe78bSCy Schubert 	if (mic != NULL) {
3735*7f2fe78bSCy Schubert 		/* mechListMIC [3] OCTET STRING OPTIONAL */
3736*7f2fe78bSCy Schubert 		f3len = k5_der_value_len(mic->length);
3737*7f2fe78bSCy Schubert 		fields_len += k5_der_value_len(f3len);
3738*7f2fe78bSCy Schubert 	}
3739*7f2fe78bSCy Schubert 
3740*7f2fe78bSCy Schubert 	/* Calculate the length of the sequence and choice. */
3741*7f2fe78bSCy Schubert 	seq_len = k5_der_value_len(fields_len);
3742*7f2fe78bSCy Schubert 	choice_len = k5_der_value_len(seq_len);
3743*7f2fe78bSCy Schubert 
3744*7f2fe78bSCy Schubert 	/* Allocate space and prepare a buffer. */
3745*7f2fe78bSCy Schubert 	t = gssalloc_malloc(choice_len);
3746*7f2fe78bSCy Schubert 	if (t == NULL)
3747*7f2fe78bSCy Schubert 		return (GSS_S_DEFECTIVE_TOKEN);
3748*7f2fe78bSCy Schubert 	k5_buf_init_fixed(&buf, t, choice_len);
3749*7f2fe78bSCy Schubert 
3750*7f2fe78bSCy Schubert 	/* Add the choice tag and begin the sequence. */
3751*7f2fe78bSCy Schubert 	k5_der_add_taglen(&buf, CONTEXT | 0x01, seq_len);
3752*7f2fe78bSCy Schubert 	k5_der_add_taglen(&buf, SEQUENCE, fields_len);
3753*7f2fe78bSCy Schubert 
3754*7f2fe78bSCy Schubert 	/* Add the negState field. */
3755*7f2fe78bSCy Schubert 	k5_der_add_taglen(&buf, CONTEXT | 0x00, f0len);
3756*7f2fe78bSCy Schubert 	k5_der_add_value(&buf, ENUMERATED, &status, 1);
3757*7f2fe78bSCy Schubert 
3758*7f2fe78bSCy Schubert 	if (sendtoken == INIT_TOKEN_SEND) {
3759*7f2fe78bSCy Schubert 		/* Add the supportedMech field. */
3760*7f2fe78bSCy Schubert 		k5_der_add_taglen(&buf, CONTEXT | 0x01, f1len);
3761*7f2fe78bSCy Schubert 		k5_der_add_value(&buf, MECH_OID, mech_wanted->elements,
3762*7f2fe78bSCy Schubert 				 mech_wanted->length);
3763*7f2fe78bSCy Schubert 	}
3764*7f2fe78bSCy Schubert 
3765*7f2fe78bSCy Schubert 	if (token != NULL && token->length > 0) {
3766*7f2fe78bSCy Schubert 		/* Add the mechToken field. */
3767*7f2fe78bSCy Schubert 		k5_der_add_taglen(&buf, CONTEXT | 0x02, f2len);
3768*7f2fe78bSCy Schubert 		k5_der_add_value(&buf, OCTET_STRING, token->value,
3769*7f2fe78bSCy Schubert 				 token->length);
3770*7f2fe78bSCy Schubert 	}
3771*7f2fe78bSCy Schubert 
3772*7f2fe78bSCy Schubert 	if (mic != NULL) {
3773*7f2fe78bSCy Schubert 		/* Add the mechListMIC field. */
3774*7f2fe78bSCy Schubert 		k5_der_add_taglen(&buf, CONTEXT | 0x03, f3len);
3775*7f2fe78bSCy Schubert 		k5_der_add_value(&buf, OCTET_STRING, mic->value, mic->length);
3776*7f2fe78bSCy Schubert 	}
3777*7f2fe78bSCy Schubert 
3778*7f2fe78bSCy Schubert 	assert(buf.len == choice_len);
3779*7f2fe78bSCy Schubert 	outbuf->length = choice_len;
3780*7f2fe78bSCy Schubert 	outbuf->value = t;
3781*7f2fe78bSCy Schubert 
3782*7f2fe78bSCy Schubert 	return (0);
3783*7f2fe78bSCy Schubert }
3784*7f2fe78bSCy Schubert 
3785*7f2fe78bSCy Schubert /* Advance in past the [APPLICATION 0] tag and thisMech field of an
3786*7f2fe78bSCy Schubert  * InitialContextToken encoding, checking that thisMech matches mech. */
3787*7f2fe78bSCy Schubert static int
verify_token_header(struct k5input * in,gss_OID_const mech)3788*7f2fe78bSCy Schubert verify_token_header(struct k5input *in, gss_OID_const mech)
3789*7f2fe78bSCy Schubert {
3790*7f2fe78bSCy Schubert 	gss_OID_desc oid;
3791*7f2fe78bSCy Schubert 	struct k5input field;
3792*7f2fe78bSCy Schubert 
3793*7f2fe78bSCy Schubert 	if (!k5_der_get_value(in, HEADER_ID, in))
3794*7f2fe78bSCy Schubert 		return (G_BAD_TOK_HEADER);
3795*7f2fe78bSCy Schubert 	if (!k5_der_get_value(in, MECH_OID, &field))
3796*7f2fe78bSCy Schubert 		return (G_BAD_TOK_HEADER);
3797*7f2fe78bSCy Schubert 
3798*7f2fe78bSCy Schubert 	oid.length = field.len;
3799*7f2fe78bSCy Schubert 	oid.elements = (uint8_t *)field.ptr;
3800*7f2fe78bSCy Schubert 	return g_OID_equal(&oid, mech) ? 0 : G_WRONG_MECH;
3801*7f2fe78bSCy Schubert }
3802*7f2fe78bSCy Schubert 
3803*7f2fe78bSCy Schubert /*
3804*7f2fe78bSCy Schubert  * Return non-zero if the oid is one of the kerberos mech oids,
3805*7f2fe78bSCy Schubert  * otherwise return zero.
3806*7f2fe78bSCy Schubert  *
3807*7f2fe78bSCy Schubert  * N.B. There are 3 oids that represent the kerberos mech:
3808*7f2fe78bSCy Schubert  * RFC-specified GSS_MECH_KRB5_OID,
3809*7f2fe78bSCy Schubert  * Old pre-RFC   GSS_MECH_KRB5_OLD_OID,
3810*7f2fe78bSCy Schubert  * Incorrect MS  GSS_MECH_KRB5_WRONG_OID
3811*7f2fe78bSCy Schubert  */
3812*7f2fe78bSCy Schubert 
3813*7f2fe78bSCy Schubert static int
is_kerb_mech(gss_OID oid)3814*7f2fe78bSCy Schubert is_kerb_mech(gss_OID oid)
3815*7f2fe78bSCy Schubert {
3816*7f2fe78bSCy Schubert 	int answer = 0;
3817*7f2fe78bSCy Schubert 	OM_uint32 minor;
3818*7f2fe78bSCy Schubert 	extern const gss_OID_set_desc * const gss_mech_set_krb5_both;
3819*7f2fe78bSCy Schubert 
3820*7f2fe78bSCy Schubert 	(void) gss_test_oid_set_member(&minor,
3821*7f2fe78bSCy Schubert 		oid, (gss_OID_set)gss_mech_set_krb5_both, &answer);
3822*7f2fe78bSCy Schubert 
3823*7f2fe78bSCy Schubert 	return (answer);
3824*7f2fe78bSCy Schubert }
3825