xref: /freebsd-src/crypto/heimdal/lib/gssapi/spnego/init_sec_context.c (revision 6a068746777241722b2b32c5d0bc443a2a64d80b)
1c19800e8SDoug Rabson /*
2*ae771770SStanislav Sedov  * Copyright (c) 1997 - 2004 Kungliga Tekniska Högskolan
3c19800e8SDoug Rabson  * (Royal Institute of Technology, Stockholm, Sweden).
4c19800e8SDoug Rabson  * Portions Copyright (c) 2004 PADL Software Pty Ltd.
5c19800e8SDoug Rabson  *
6c19800e8SDoug Rabson  * Redistribution and use in source and binary forms, with or without
7c19800e8SDoug Rabson  * modification, are permitted provided that the following conditions
8c19800e8SDoug Rabson  * are met:
9c19800e8SDoug Rabson  *
10c19800e8SDoug Rabson  * 1. Redistributions of source code must retain the above copyright
11c19800e8SDoug Rabson  *    notice, this list of conditions and the following disclaimer.
12c19800e8SDoug Rabson  *
13c19800e8SDoug Rabson  * 2. Redistributions in binary form must reproduce the above copyright
14c19800e8SDoug Rabson  *    notice, this list of conditions and the following disclaimer in the
15c19800e8SDoug Rabson  *    documentation and/or other materials provided with the distribution.
16c19800e8SDoug Rabson  *
17c19800e8SDoug Rabson  * 3. Neither the name of the Institute nor the names of its contributors
18c19800e8SDoug Rabson  *    may be used to endorse or promote products derived from this software
19c19800e8SDoug Rabson  *    without specific prior written permission.
20c19800e8SDoug Rabson  *
21c19800e8SDoug Rabson  * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
22c19800e8SDoug Rabson  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23c19800e8SDoug Rabson  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24c19800e8SDoug Rabson  * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
25c19800e8SDoug Rabson  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26c19800e8SDoug Rabson  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27c19800e8SDoug Rabson  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28c19800e8SDoug Rabson  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29c19800e8SDoug Rabson  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30c19800e8SDoug Rabson  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31c19800e8SDoug Rabson  * SUCH DAMAGE.
32c19800e8SDoug Rabson  */
33c19800e8SDoug Rabson 
34*ae771770SStanislav Sedov #include "spnego_locl.h"
35c19800e8SDoug Rabson 
36c19800e8SDoug Rabson /*
37*ae771770SStanislav Sedov  * Is target_name an sane target for `mech´.
38c19800e8SDoug Rabson  */
39c19800e8SDoug Rabson 
40c19800e8SDoug Rabson static OM_uint32
initiator_approved(gss_name_t target_name,gss_OID mech)41c19800e8SDoug Rabson initiator_approved(gss_name_t target_name, gss_OID mech)
42c19800e8SDoug Rabson {
43c19800e8SDoug Rabson     OM_uint32 min_stat, maj_stat;
44c19800e8SDoug Rabson     gss_ctx_id_t ctx = GSS_C_NO_CONTEXT;
45c19800e8SDoug Rabson     gss_buffer_desc out;
46c19800e8SDoug Rabson 
47c19800e8SDoug Rabson     maj_stat = gss_init_sec_context(&min_stat,
48c19800e8SDoug Rabson 				    GSS_C_NO_CREDENTIAL,
49c19800e8SDoug Rabson 				    &ctx,
50c19800e8SDoug Rabson 				    target_name,
51c19800e8SDoug Rabson 				    mech,
52c19800e8SDoug Rabson 				    0,
53c19800e8SDoug Rabson 				    GSS_C_INDEFINITE,
54c19800e8SDoug Rabson 				    GSS_C_NO_CHANNEL_BINDINGS,
55c19800e8SDoug Rabson 				    GSS_C_NO_BUFFER,
56c19800e8SDoug Rabson 				    NULL,
57c19800e8SDoug Rabson 				    &out,
58c19800e8SDoug Rabson 				    NULL,
59c19800e8SDoug Rabson 				    NULL);
60*ae771770SStanislav Sedov     if (GSS_ERROR(maj_stat)) {
61*ae771770SStanislav Sedov 	gss_mg_collect_error(mech, maj_stat, min_stat);
62c19800e8SDoug Rabson 	return GSS_S_BAD_MECH;
63*ae771770SStanislav Sedov     }
64c19800e8SDoug Rabson     gss_release_buffer(&min_stat, &out);
65c19800e8SDoug Rabson     gss_delete_sec_context(&min_stat, &ctx, NULL);
66c19800e8SDoug Rabson 
67c19800e8SDoug Rabson     return GSS_S_COMPLETE;
68c19800e8SDoug Rabson }
69c19800e8SDoug Rabson 
70c19800e8SDoug Rabson /*
71c19800e8SDoug Rabson  * Send a reply. Note that we only need to send a reply if we
72c19800e8SDoug Rabson  * need to send a MIC or a mechanism token. Otherwise, we can
73c19800e8SDoug Rabson  * return an empty buffer.
74c19800e8SDoug Rabson  *
75c19800e8SDoug Rabson  * The return value of this will be returned to the API, so it
76c19800e8SDoug Rabson  * must return GSS_S_CONTINUE_NEEDED if a token was generated.
77c19800e8SDoug Rabson  */
78c19800e8SDoug Rabson static OM_uint32
spnego_reply_internal(OM_uint32 * minor_status,gssspnego_ctx context_handle,const gss_buffer_t mech_buf,gss_buffer_t mech_token,gss_buffer_t output_token)79c19800e8SDoug Rabson spnego_reply_internal(OM_uint32 *minor_status,
80c19800e8SDoug Rabson 		      gssspnego_ctx context_handle,
81c19800e8SDoug Rabson 		      const gss_buffer_t mech_buf,
82c19800e8SDoug Rabson 		      gss_buffer_t mech_token,
83c19800e8SDoug Rabson 		      gss_buffer_t output_token)
84c19800e8SDoug Rabson {
85c19800e8SDoug Rabson     NegotiationToken nt;
86c19800e8SDoug Rabson     gss_buffer_desc mic_buf;
87c19800e8SDoug Rabson     OM_uint32 ret;
88c19800e8SDoug Rabson     size_t size;
89c19800e8SDoug Rabson 
90c19800e8SDoug Rabson     if (mech_buf == GSS_C_NO_BUFFER && mech_token->length == 0) {
91c19800e8SDoug Rabson 	output_token->length = 0;
92c19800e8SDoug Rabson 	output_token->value = NULL;
93c19800e8SDoug Rabson 
94c19800e8SDoug Rabson 	return context_handle->open ? GSS_S_COMPLETE : GSS_S_FAILURE;
95c19800e8SDoug Rabson     }
96c19800e8SDoug Rabson 
97c19800e8SDoug Rabson     memset(&nt, 0, sizeof(nt));
98c19800e8SDoug Rabson 
99c19800e8SDoug Rabson     nt.element = choice_NegotiationToken_negTokenResp;
100c19800e8SDoug Rabson 
101c19800e8SDoug Rabson     ALLOC(nt.u.negTokenResp.negResult, 1);
102c19800e8SDoug Rabson     if (nt.u.negTokenResp.negResult == NULL) {
103c19800e8SDoug Rabson 	*minor_status = ENOMEM;
104c19800e8SDoug Rabson 	return GSS_S_FAILURE;
105c19800e8SDoug Rabson     }
106c19800e8SDoug Rabson 
107c19800e8SDoug Rabson     nt.u.negTokenResp.supportedMech = NULL;
108c19800e8SDoug Rabson 
109c19800e8SDoug Rabson     output_token->length = 0;
110c19800e8SDoug Rabson     output_token->value = NULL;
111c19800e8SDoug Rabson 
112c19800e8SDoug Rabson     if (mech_token->length == 0) {
113c19800e8SDoug Rabson 	nt.u.negTokenResp.responseToken = NULL;
114c19800e8SDoug Rabson 	*(nt.u.negTokenResp.negResult)  = accept_completed;
115c19800e8SDoug Rabson     } else {
116c19800e8SDoug Rabson 	ALLOC(nt.u.negTokenResp.responseToken, 1);
117c19800e8SDoug Rabson 	if (nt.u.negTokenResp.responseToken == NULL) {
118c19800e8SDoug Rabson 	    free_NegotiationToken(&nt);
119c19800e8SDoug Rabson 	    *minor_status = ENOMEM;
120c19800e8SDoug Rabson 	    return GSS_S_FAILURE;
121c19800e8SDoug Rabson 	}
122c19800e8SDoug Rabson 	nt.u.negTokenResp.responseToken->length = mech_token->length;
123c19800e8SDoug Rabson 	nt.u.negTokenResp.responseToken->data   = mech_token->value;
124c19800e8SDoug Rabson 	mech_token->length = 0;
125c19800e8SDoug Rabson 	mech_token->value  = NULL;
126c19800e8SDoug Rabson 
127c19800e8SDoug Rabson 	*(nt.u.negTokenResp.negResult)  = accept_incomplete;
128c19800e8SDoug Rabson     }
129c19800e8SDoug Rabson 
130c19800e8SDoug Rabson     if (mech_buf != GSS_C_NO_BUFFER) {
131c19800e8SDoug Rabson 
132c19800e8SDoug Rabson 	ret = gss_get_mic(minor_status,
133c19800e8SDoug Rabson 			  context_handle->negotiated_ctx_id,
134c19800e8SDoug Rabson 			  0,
135c19800e8SDoug Rabson 			  mech_buf,
136c19800e8SDoug Rabson 			  &mic_buf);
137c19800e8SDoug Rabson 	if (ret == GSS_S_COMPLETE) {
138c19800e8SDoug Rabson 	    ALLOC(nt.u.negTokenResp.mechListMIC, 1);
139c19800e8SDoug Rabson 	    if (nt.u.negTokenResp.mechListMIC == NULL) {
140c19800e8SDoug Rabson 		gss_release_buffer(minor_status, &mic_buf);
141c19800e8SDoug Rabson 		free_NegotiationToken(&nt);
142c19800e8SDoug Rabson 		*minor_status = ENOMEM;
143c19800e8SDoug Rabson 		return GSS_S_FAILURE;
144c19800e8SDoug Rabson 	    }
145c19800e8SDoug Rabson 
146c19800e8SDoug Rabson 	    nt.u.negTokenResp.mechListMIC->length = mic_buf.length;
147c19800e8SDoug Rabson 	    nt.u.negTokenResp.mechListMIC->data   = mic_buf.value;
148c19800e8SDoug Rabson 	} else if (ret == GSS_S_UNAVAILABLE) {
149c19800e8SDoug Rabson 	    nt.u.negTokenResp.mechListMIC = NULL;
150c19800e8SDoug Rabson 	} if (ret) {
151c19800e8SDoug Rabson 	    free_NegotiationToken(&nt);
152c19800e8SDoug Rabson 	    *minor_status = ENOMEM;
153c19800e8SDoug Rabson 	    return GSS_S_FAILURE;
154c19800e8SDoug Rabson 	}
155c19800e8SDoug Rabson     } else {
156c19800e8SDoug Rabson 	nt.u.negTokenResp.mechListMIC = NULL;
157c19800e8SDoug Rabson     }
158c19800e8SDoug Rabson 
159c19800e8SDoug Rabson     ASN1_MALLOC_ENCODE(NegotiationToken,
160c19800e8SDoug Rabson 		       output_token->value, output_token->length,
161c19800e8SDoug Rabson 		       &nt, &size, ret);
162c19800e8SDoug Rabson     if (ret) {
163c19800e8SDoug Rabson 	free_NegotiationToken(&nt);
164c19800e8SDoug Rabson 	*minor_status = ret;
165c19800e8SDoug Rabson 	return GSS_S_FAILURE;
166c19800e8SDoug Rabson     }
167c19800e8SDoug Rabson 
168c19800e8SDoug Rabson     if (*(nt.u.negTokenResp.negResult) == accept_completed)
169c19800e8SDoug Rabson 	ret = GSS_S_COMPLETE;
170c19800e8SDoug Rabson     else
171c19800e8SDoug Rabson 	ret = GSS_S_CONTINUE_NEEDED;
172c19800e8SDoug Rabson 
173c19800e8SDoug Rabson     free_NegotiationToken(&nt);
174c19800e8SDoug Rabson     return ret;
175c19800e8SDoug Rabson }
176c19800e8SDoug Rabson 
177c19800e8SDoug Rabson static OM_uint32
spnego_initial(OM_uint32 * minor_status,gss_cred_id_t cred,gss_ctx_id_t * context_handle,const gss_name_t target_name,const gss_OID mech_type,OM_uint32 req_flags,OM_uint32 time_req,const gss_channel_bindings_t input_chan_bindings,const gss_buffer_t input_token,gss_OID * actual_mech_type,gss_buffer_t output_token,OM_uint32 * ret_flags,OM_uint32 * time_rec)178c19800e8SDoug Rabson spnego_initial
179c19800e8SDoug Rabson            (OM_uint32 * minor_status,
180*ae771770SStanislav Sedov 	    gss_cred_id_t cred,
181c19800e8SDoug Rabson             gss_ctx_id_t * context_handle,
182c19800e8SDoug Rabson             const gss_name_t target_name,
183c19800e8SDoug Rabson             const gss_OID mech_type,
184c19800e8SDoug Rabson             OM_uint32 req_flags,
185c19800e8SDoug Rabson             OM_uint32 time_req,
186c19800e8SDoug Rabson             const gss_channel_bindings_t input_chan_bindings,
187c19800e8SDoug Rabson             const gss_buffer_t input_token,
188c19800e8SDoug Rabson             gss_OID * actual_mech_type,
189c19800e8SDoug Rabson             gss_buffer_t output_token,
190c19800e8SDoug Rabson             OM_uint32 * ret_flags,
191c19800e8SDoug Rabson             OM_uint32 * time_rec
192c19800e8SDoug Rabson     )
193c19800e8SDoug Rabson {
194c19800e8SDoug Rabson     NegTokenInit ni;
195c19800e8SDoug Rabson     int ret;
196c19800e8SDoug Rabson     OM_uint32 sub, minor;
197c19800e8SDoug Rabson     gss_buffer_desc mech_token;
198c19800e8SDoug Rabson     u_char *buf;
199c19800e8SDoug Rabson     size_t buf_size, buf_len;
200c19800e8SDoug Rabson     gss_buffer_desc data;
201c19800e8SDoug Rabson     size_t ni_len;
202c19800e8SDoug Rabson     gss_ctx_id_t context;
203c19800e8SDoug Rabson     gssspnego_ctx ctx;
204c19800e8SDoug Rabson     spnego_name name = (spnego_name)target_name;
205c19800e8SDoug Rabson 
206c19800e8SDoug Rabson     *minor_status = 0;
207c19800e8SDoug Rabson 
208c19800e8SDoug Rabson     memset (&ni, 0, sizeof(ni));
209c19800e8SDoug Rabson 
210c19800e8SDoug Rabson     *context_handle = GSS_C_NO_CONTEXT;
211c19800e8SDoug Rabson 
212c19800e8SDoug Rabson     if (target_name == GSS_C_NO_NAME)
213c19800e8SDoug Rabson 	return GSS_S_BAD_NAME;
214c19800e8SDoug Rabson 
215c19800e8SDoug Rabson     sub = _gss_spnego_alloc_sec_context(&minor, &context);
216c19800e8SDoug Rabson     if (GSS_ERROR(sub)) {
217c19800e8SDoug Rabson 	*minor_status = minor;
218c19800e8SDoug Rabson 	return sub;
219c19800e8SDoug Rabson     }
220c19800e8SDoug Rabson     ctx = (gssspnego_ctx)context;
221c19800e8SDoug Rabson 
222c19800e8SDoug Rabson     HEIMDAL_MUTEX_lock(&ctx->ctx_id_mutex);
223c19800e8SDoug Rabson 
224c19800e8SDoug Rabson     ctx->local = 1;
225c19800e8SDoug Rabson 
226c19800e8SDoug Rabson     sub = gss_import_name(&minor, &name->value, &name->type, &ctx->target_name);
227c19800e8SDoug Rabson     if (GSS_ERROR(sub)) {
228c19800e8SDoug Rabson 	*minor_status = minor;
229c19800e8SDoug Rabson 	_gss_spnego_internal_delete_sec_context(&minor, &context, GSS_C_NO_BUFFER);
230c19800e8SDoug Rabson 	return sub;
231c19800e8SDoug Rabson     }
232c19800e8SDoug Rabson 
233c19800e8SDoug Rabson     sub = _gss_spnego_indicate_mechtypelist(&minor,
234c19800e8SDoug Rabson 					    ctx->target_name,
235c19800e8SDoug Rabson 					    initiator_approved,
236c19800e8SDoug Rabson 					    0,
237c19800e8SDoug Rabson 					    cred,
238c19800e8SDoug Rabson 					    &ni.mechTypes,
239c19800e8SDoug Rabson 					    &ctx->preferred_mech_type);
240c19800e8SDoug Rabson     if (GSS_ERROR(sub)) {
241c19800e8SDoug Rabson 	*minor_status = minor;
242c19800e8SDoug Rabson 	_gss_spnego_internal_delete_sec_context(&minor, &context, GSS_C_NO_BUFFER);
243c19800e8SDoug Rabson 	return sub;
244c19800e8SDoug Rabson     }
245c19800e8SDoug Rabson 
246c19800e8SDoug Rabson     ni.reqFlags = NULL;
247c19800e8SDoug Rabson 
248c19800e8SDoug Rabson     /*
249c19800e8SDoug Rabson      * If we have a credential handle, use it to select the mechanism
250c19800e8SDoug Rabson      * that we will use
251c19800e8SDoug Rabson      */
252c19800e8SDoug Rabson 
253c19800e8SDoug Rabson     /* generate optimistic token */
254c19800e8SDoug Rabson     sub = gss_init_sec_context(&minor,
255*ae771770SStanislav Sedov 			       cred,
256c19800e8SDoug Rabson 			       &ctx->negotiated_ctx_id,
257c19800e8SDoug Rabson 			       ctx->target_name,
258c19800e8SDoug Rabson 			       ctx->preferred_mech_type,
259c19800e8SDoug Rabson 			       req_flags,
260c19800e8SDoug Rabson 			       time_req,
261c19800e8SDoug Rabson 			       input_chan_bindings,
262c19800e8SDoug Rabson 			       input_token,
263c19800e8SDoug Rabson 			       &ctx->negotiated_mech_type,
264c19800e8SDoug Rabson 			       &mech_token,
265c19800e8SDoug Rabson 			       &ctx->mech_flags,
266c19800e8SDoug Rabson 			       &ctx->mech_time_rec);
267c19800e8SDoug Rabson     if (GSS_ERROR(sub)) {
268c19800e8SDoug Rabson 	free_NegTokenInit(&ni);
269c19800e8SDoug Rabson 	*minor_status = minor;
270*ae771770SStanislav Sedov 	gss_mg_collect_error(ctx->preferred_mech_type, sub, minor);
271c19800e8SDoug Rabson 	_gss_spnego_internal_delete_sec_context(&minor, &context, GSS_C_NO_BUFFER);
272c19800e8SDoug Rabson 	return sub;
273c19800e8SDoug Rabson     }
274c19800e8SDoug Rabson     if (sub == GSS_S_COMPLETE)
275c19800e8SDoug Rabson 	ctx->maybe_open = 1;
276c19800e8SDoug Rabson 
277c19800e8SDoug Rabson     if (mech_token.length != 0) {
278c19800e8SDoug Rabson 	ALLOC(ni.mechToken, 1);
279c19800e8SDoug Rabson 	if (ni.mechToken == NULL) {
280c19800e8SDoug Rabson 	    free_NegTokenInit(&ni);
281c19800e8SDoug Rabson 	    gss_release_buffer(&minor, &mech_token);
282c19800e8SDoug Rabson 	    _gss_spnego_internal_delete_sec_context(&minor, &context, GSS_C_NO_BUFFER);
283c19800e8SDoug Rabson 	    *minor_status = ENOMEM;
284c19800e8SDoug Rabson 	    return GSS_S_FAILURE;
285c19800e8SDoug Rabson 	}
286c19800e8SDoug Rabson 	ni.mechToken->length = mech_token.length;
287c19800e8SDoug Rabson 	ni.mechToken->data = malloc(mech_token.length);
288c19800e8SDoug Rabson 	if (ni.mechToken->data == NULL && mech_token.length != 0) {
289c19800e8SDoug Rabson 	    free_NegTokenInit(&ni);
290c19800e8SDoug Rabson 	    gss_release_buffer(&minor, &mech_token);
291c19800e8SDoug Rabson 	    *minor_status = ENOMEM;
292c19800e8SDoug Rabson 	    _gss_spnego_internal_delete_sec_context(&minor, &context, GSS_C_NO_BUFFER);
293c19800e8SDoug Rabson 	    return GSS_S_FAILURE;
294c19800e8SDoug Rabson 	}
295c19800e8SDoug Rabson 	memcpy(ni.mechToken->data, mech_token.value, mech_token.length);
296c19800e8SDoug Rabson 	gss_release_buffer(&minor, &mech_token);
297c19800e8SDoug Rabson     } else
298c19800e8SDoug Rabson 	ni.mechToken = NULL;
299c19800e8SDoug Rabson 
300c19800e8SDoug Rabson     ni.mechListMIC = NULL;
301c19800e8SDoug Rabson 
302c19800e8SDoug Rabson     ni_len = length_NegTokenInit(&ni);
303c19800e8SDoug Rabson     buf_size = 1 + der_length_len(ni_len) + ni_len;
304c19800e8SDoug Rabson 
305c19800e8SDoug Rabson     buf = malloc(buf_size);
306c19800e8SDoug Rabson     if (buf == NULL) {
307c19800e8SDoug Rabson 	free_NegTokenInit(&ni);
308c19800e8SDoug Rabson 	*minor_status = ENOMEM;
309c19800e8SDoug Rabson 	_gss_spnego_internal_delete_sec_context(&minor, &context, GSS_C_NO_BUFFER);
310c19800e8SDoug Rabson 	return GSS_S_FAILURE;
311c19800e8SDoug Rabson     }
312c19800e8SDoug Rabson 
313c19800e8SDoug Rabson     ret = encode_NegTokenInit(buf + buf_size - 1,
314c19800e8SDoug Rabson 			      ni_len,
315c19800e8SDoug Rabson 			      &ni, &buf_len);
316c19800e8SDoug Rabson     if (ret == 0 && ni_len != buf_len)
317c19800e8SDoug Rabson 	abort();
318c19800e8SDoug Rabson 
319c19800e8SDoug Rabson     if (ret == 0) {
320c19800e8SDoug Rabson 	size_t tmp;
321c19800e8SDoug Rabson 
322c19800e8SDoug Rabson 	ret = der_put_length_and_tag(buf + buf_size - buf_len - 1,
323c19800e8SDoug Rabson 				     buf_size - buf_len,
324c19800e8SDoug Rabson 				     buf_len,
325c19800e8SDoug Rabson 				     ASN1_C_CONTEXT,
326c19800e8SDoug Rabson 				     CONS,
327c19800e8SDoug Rabson 				     0,
328c19800e8SDoug Rabson 				     &tmp);
329c19800e8SDoug Rabson 	if (ret == 0 && tmp + buf_len != buf_size)
330c19800e8SDoug Rabson 	    abort();
331c19800e8SDoug Rabson     }
332c19800e8SDoug Rabson     if (ret) {
333c19800e8SDoug Rabson 	*minor_status = ret;
334c19800e8SDoug Rabson 	free(buf);
335c19800e8SDoug Rabson 	free_NegTokenInit(&ni);
336c19800e8SDoug Rabson 	_gss_spnego_internal_delete_sec_context(&minor, &context, GSS_C_NO_BUFFER);
337c19800e8SDoug Rabson 	return GSS_S_FAILURE;
338c19800e8SDoug Rabson     }
339c19800e8SDoug Rabson 
340c19800e8SDoug Rabson     data.value  = buf;
341c19800e8SDoug Rabson     data.length = buf_size;
342c19800e8SDoug Rabson 
343c19800e8SDoug Rabson     ctx->initiator_mech_types.len = ni.mechTypes.len;
344c19800e8SDoug Rabson     ctx->initiator_mech_types.val = ni.mechTypes.val;
345c19800e8SDoug Rabson     ni.mechTypes.len = 0;
346c19800e8SDoug Rabson     ni.mechTypes.val = NULL;
347c19800e8SDoug Rabson 
348c19800e8SDoug Rabson     free_NegTokenInit(&ni);
349c19800e8SDoug Rabson 
350c19800e8SDoug Rabson     sub = gss_encapsulate_token(&data,
351c19800e8SDoug Rabson 				GSS_SPNEGO_MECHANISM,
352c19800e8SDoug Rabson 				output_token);
353c19800e8SDoug Rabson     free (buf);
354c19800e8SDoug Rabson 
355c19800e8SDoug Rabson     if (sub) {
356c19800e8SDoug Rabson 	_gss_spnego_internal_delete_sec_context(&minor, &context, GSS_C_NO_BUFFER);
357c19800e8SDoug Rabson 	return sub;
358c19800e8SDoug Rabson     }
359c19800e8SDoug Rabson 
360c19800e8SDoug Rabson     if (actual_mech_type)
361c19800e8SDoug Rabson 	*actual_mech_type = ctx->negotiated_mech_type;
362c19800e8SDoug Rabson     if (ret_flags)
363c19800e8SDoug Rabson 	*ret_flags = ctx->mech_flags;
364c19800e8SDoug Rabson     if (time_rec)
365c19800e8SDoug Rabson 	*time_rec = ctx->mech_time_rec;
366c19800e8SDoug Rabson 
367c19800e8SDoug Rabson     HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex);
368c19800e8SDoug Rabson 
369c19800e8SDoug Rabson     *context_handle = context;
370c19800e8SDoug Rabson 
371c19800e8SDoug Rabson     return GSS_S_CONTINUE_NEEDED;
372c19800e8SDoug Rabson }
373c19800e8SDoug Rabson 
374c19800e8SDoug Rabson static OM_uint32
spnego_reply(OM_uint32 * minor_status,const gss_cred_id_t cred,gss_ctx_id_t * context_handle,const gss_name_t target_name,const gss_OID mech_type,OM_uint32 req_flags,OM_uint32 time_req,const gss_channel_bindings_t input_chan_bindings,const gss_buffer_t input_token,gss_OID * actual_mech_type,gss_buffer_t output_token,OM_uint32 * ret_flags,OM_uint32 * time_rec)375c19800e8SDoug Rabson spnego_reply
376c19800e8SDoug Rabson            (OM_uint32 * minor_status,
377*ae771770SStanislav Sedov 	    const gss_cred_id_t cred,
378c19800e8SDoug Rabson             gss_ctx_id_t * context_handle,
379c19800e8SDoug Rabson             const gss_name_t target_name,
380c19800e8SDoug Rabson             const gss_OID mech_type,
381c19800e8SDoug Rabson             OM_uint32 req_flags,
382c19800e8SDoug Rabson             OM_uint32 time_req,
383c19800e8SDoug Rabson             const gss_channel_bindings_t input_chan_bindings,
384c19800e8SDoug Rabson             const gss_buffer_t input_token,
385c19800e8SDoug Rabson             gss_OID * actual_mech_type,
386c19800e8SDoug Rabson             gss_buffer_t output_token,
387c19800e8SDoug Rabson             OM_uint32 * ret_flags,
388c19800e8SDoug Rabson             OM_uint32 * time_rec
389c19800e8SDoug Rabson     )
390c19800e8SDoug Rabson {
391c19800e8SDoug Rabson     OM_uint32 ret, minor;
392*ae771770SStanislav Sedov     NegotiationToken resp;
393c19800e8SDoug Rabson     gss_OID_desc mech;
394c19800e8SDoug Rabson     int require_mic;
395*ae771770SStanislav Sedov     size_t buf_len = 0;
396c19800e8SDoug Rabson     gss_buffer_desc mic_buf, mech_buf;
397c19800e8SDoug Rabson     gss_buffer_desc mech_output_token;
398c19800e8SDoug Rabson     gssspnego_ctx ctx;
399c19800e8SDoug Rabson 
400c19800e8SDoug Rabson     *minor_status = 0;
401c19800e8SDoug Rabson 
402c19800e8SDoug Rabson     ctx = (gssspnego_ctx)*context_handle;
403c19800e8SDoug Rabson 
404c19800e8SDoug Rabson     output_token->length = 0;
405c19800e8SDoug Rabson     output_token->value  = NULL;
406c19800e8SDoug Rabson 
407c19800e8SDoug Rabson     mech_output_token.length = 0;
408c19800e8SDoug Rabson     mech_output_token.value = NULL;
409c19800e8SDoug Rabson 
410c19800e8SDoug Rabson     mech_buf.value = NULL;
411c19800e8SDoug Rabson     mech_buf.length = 0;
412c19800e8SDoug Rabson 
413*ae771770SStanislav Sedov     ret = decode_NegotiationToken(input_token->value, input_token->length,
414*ae771770SStanislav Sedov 				  &resp, NULL);
415c19800e8SDoug Rabson     if (ret)
416c19800e8SDoug Rabson       return ret;
417c19800e8SDoug Rabson 
418*ae771770SStanislav Sedov     if (resp.element != choice_NegotiationToken_negTokenResp) {
419*ae771770SStanislav Sedov 	free_NegotiationToken(&resp);
420*ae771770SStanislav Sedov 	*minor_status = 0;
421*ae771770SStanislav Sedov 	return GSS_S_BAD_MECH;
422c19800e8SDoug Rabson     }
423c19800e8SDoug Rabson 
424*ae771770SStanislav Sedov     if (resp.u.negTokenResp.negResult == NULL
425*ae771770SStanislav Sedov 	|| *(resp.u.negTokenResp.negResult) == reject
426*ae771770SStanislav Sedov 	/* || resp.u.negTokenResp.supportedMech == NULL */
427c19800e8SDoug Rabson 	)
428c19800e8SDoug Rabson     {
429*ae771770SStanislav Sedov 	free_NegotiationToken(&resp);
430c19800e8SDoug Rabson 	return GSS_S_BAD_MECH;
431c19800e8SDoug Rabson     }
432c19800e8SDoug Rabson 
433c19800e8SDoug Rabson     /*
434c19800e8SDoug Rabson      * Pick up the mechanism that the acceptor selected, only allow it
435c19800e8SDoug Rabson      * to be sent in packet.
436c19800e8SDoug Rabson      */
437c19800e8SDoug Rabson 
438c19800e8SDoug Rabson     HEIMDAL_MUTEX_lock(&ctx->ctx_id_mutex);
439c19800e8SDoug Rabson 
440*ae771770SStanislav Sedov     if (resp.u.negTokenResp.supportedMech) {
441c19800e8SDoug Rabson 
442c19800e8SDoug Rabson 	if (ctx->oidlen) {
443*ae771770SStanislav Sedov 	    free_NegotiationToken(&resp);
444c19800e8SDoug Rabson 	    HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex);
445c19800e8SDoug Rabson 	    return GSS_S_BAD_MECH;
446c19800e8SDoug Rabson 	}
447c19800e8SDoug Rabson 	ret = der_put_oid(ctx->oidbuf + sizeof(ctx->oidbuf) - 1,
448c19800e8SDoug Rabson 			  sizeof(ctx->oidbuf),
449*ae771770SStanislav Sedov 			  resp.u.negTokenResp.supportedMech,
450c19800e8SDoug Rabson 			  &ctx->oidlen);
451c19800e8SDoug Rabson 	/* Avoid recursively embedded SPNEGO */
452c19800e8SDoug Rabson 	if (ret || (ctx->oidlen == GSS_SPNEGO_MECHANISM->length &&
453c19800e8SDoug Rabson 		    memcmp(ctx->oidbuf + sizeof(ctx->oidbuf) - ctx->oidlen,
454c19800e8SDoug Rabson 			   GSS_SPNEGO_MECHANISM->elements,
455c19800e8SDoug Rabson 			   ctx->oidlen) == 0))
456c19800e8SDoug Rabson 	{
457*ae771770SStanislav Sedov 	    free_NegotiationToken(&resp);
458c19800e8SDoug Rabson 	    HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex);
459c19800e8SDoug Rabson 	    return GSS_S_BAD_MECH;
460c19800e8SDoug Rabson 	}
461c19800e8SDoug Rabson 
462c19800e8SDoug Rabson 	/* check if the acceptor took our optimistic token */
463c19800e8SDoug Rabson 	if (ctx->oidlen != ctx->preferred_mech_type->length ||
464c19800e8SDoug Rabson 	    memcmp(ctx->oidbuf + sizeof(ctx->oidbuf) - ctx->oidlen,
465c19800e8SDoug Rabson 		   ctx->preferred_mech_type->elements,
466c19800e8SDoug Rabson 		   ctx->oidlen) != 0)
467c19800e8SDoug Rabson 	{
468c19800e8SDoug Rabson 	    gss_delete_sec_context(&minor, &ctx->negotiated_ctx_id,
469c19800e8SDoug Rabson 				   GSS_C_NO_BUFFER);
470c19800e8SDoug Rabson 	    ctx->negotiated_ctx_id = GSS_C_NO_CONTEXT;
471c19800e8SDoug Rabson 	}
472c19800e8SDoug Rabson     } else if (ctx->oidlen == 0) {
473*ae771770SStanislav Sedov 	free_NegotiationToken(&resp);
474c19800e8SDoug Rabson 	HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex);
475c19800e8SDoug Rabson 	return GSS_S_BAD_MECH;
476c19800e8SDoug Rabson     }
477c19800e8SDoug Rabson 
478*ae771770SStanislav Sedov     /* if a token (of non zero length), or no context, pass to underlaying mech */
479*ae771770SStanislav Sedov     if ((resp.u.negTokenResp.responseToken != NULL && resp.u.negTokenResp.responseToken->length) ||
480c19800e8SDoug Rabson 	ctx->negotiated_ctx_id == GSS_C_NO_CONTEXT) {
481c19800e8SDoug Rabson 	gss_buffer_desc mech_input_token;
482c19800e8SDoug Rabson 
483*ae771770SStanislav Sedov 	if (resp.u.negTokenResp.responseToken) {
484*ae771770SStanislav Sedov 	    mech_input_token.length = resp.u.negTokenResp.responseToken->length;
485*ae771770SStanislav Sedov 	    mech_input_token.value  = resp.u.negTokenResp.responseToken->data;
486c19800e8SDoug Rabson 	} else {
487c19800e8SDoug Rabson 	    mech_input_token.length = 0;
488c19800e8SDoug Rabson 	    mech_input_token.value = NULL;
489c19800e8SDoug Rabson 	}
490c19800e8SDoug Rabson 
491c19800e8SDoug Rabson 
492c19800e8SDoug Rabson 	mech.length = ctx->oidlen;
493c19800e8SDoug Rabson 	mech.elements = ctx->oidbuf + sizeof(ctx->oidbuf) - ctx->oidlen;
494c19800e8SDoug Rabson 
495c19800e8SDoug Rabson 	/* Fall through as if the negotiated mechanism
496c19800e8SDoug Rabson 	   was requested explicitly */
497c19800e8SDoug Rabson 	ret = gss_init_sec_context(&minor,
498*ae771770SStanislav Sedov 				   cred,
499c19800e8SDoug Rabson 				   &ctx->negotiated_ctx_id,
500c19800e8SDoug Rabson 				   ctx->target_name,
501c19800e8SDoug Rabson 				   &mech,
502c19800e8SDoug Rabson 				   req_flags,
503c19800e8SDoug Rabson 				   time_req,
504c19800e8SDoug Rabson 				   input_chan_bindings,
505c19800e8SDoug Rabson 				   &mech_input_token,
506c19800e8SDoug Rabson 				   &ctx->negotiated_mech_type,
507c19800e8SDoug Rabson 				   &mech_output_token,
508c19800e8SDoug Rabson 				   &ctx->mech_flags,
509c19800e8SDoug Rabson 				   &ctx->mech_time_rec);
510c19800e8SDoug Rabson 	if (GSS_ERROR(ret)) {
511c19800e8SDoug Rabson 	    HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex);
512*ae771770SStanislav Sedov 	    free_NegotiationToken(&resp);
513*ae771770SStanislav Sedov 	    gss_mg_collect_error(&mech, ret, minor);
514c19800e8SDoug Rabson 	    *minor_status = minor;
515c19800e8SDoug Rabson 	    return ret;
516c19800e8SDoug Rabson 	}
517c19800e8SDoug Rabson 	if (ret == GSS_S_COMPLETE) {
518c19800e8SDoug Rabson 	    ctx->open = 1;
519c19800e8SDoug Rabson 	}
520*ae771770SStanislav Sedov     } else if (*(resp.u.negTokenResp.negResult) == accept_completed) {
521c19800e8SDoug Rabson 	if (ctx->maybe_open)
522c19800e8SDoug Rabson 	    ctx->open = 1;
523c19800e8SDoug Rabson     }
524c19800e8SDoug Rabson 
525*ae771770SStanislav Sedov     if (*(resp.u.negTokenResp.negResult) == request_mic) {
526c19800e8SDoug Rabson 	ctx->require_mic = 1;
527c19800e8SDoug Rabson     }
528c19800e8SDoug Rabson 
529c19800e8SDoug Rabson     if (ctx->open) {
530c19800e8SDoug Rabson 	/*
531c19800e8SDoug Rabson 	 * Verify the mechListMIC if one was provided or CFX was
532c19800e8SDoug Rabson 	 * used and a non-preferred mechanism was selected
533c19800e8SDoug Rabson 	 */
534*ae771770SStanislav Sedov 	if (resp.u.negTokenResp.mechListMIC != NULL) {
535c19800e8SDoug Rabson 	    require_mic = 1;
536c19800e8SDoug Rabson 	} else {
537c19800e8SDoug Rabson 	    ret = _gss_spnego_require_mechlist_mic(minor_status, ctx,
538c19800e8SDoug Rabson 						   &require_mic);
539c19800e8SDoug Rabson 	    if (ret) {
540c19800e8SDoug Rabson 		HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex);
541*ae771770SStanislav Sedov 		free_NegotiationToken(&resp);
542c19800e8SDoug Rabson 		gss_release_buffer(&minor, &mech_output_token);
543c19800e8SDoug Rabson 		return ret;
544c19800e8SDoug Rabson 	    }
545c19800e8SDoug Rabson 	}
546c19800e8SDoug Rabson     } else {
547c19800e8SDoug Rabson 	require_mic = 0;
548c19800e8SDoug Rabson     }
549c19800e8SDoug Rabson 
550c19800e8SDoug Rabson     if (require_mic) {
551c19800e8SDoug Rabson 	ASN1_MALLOC_ENCODE(MechTypeList, mech_buf.value, mech_buf.length,
552c19800e8SDoug Rabson 			   &ctx->initiator_mech_types, &buf_len, ret);
553c19800e8SDoug Rabson 	if (ret) {
554c19800e8SDoug Rabson 	    HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex);
555*ae771770SStanislav Sedov 	    free_NegotiationToken(&resp);
556c19800e8SDoug Rabson 	    gss_release_buffer(&minor, &mech_output_token);
557c19800e8SDoug Rabson 	    *minor_status = ret;
558c19800e8SDoug Rabson 	    return GSS_S_FAILURE;
559c19800e8SDoug Rabson 	}
560*ae771770SStanislav Sedov 	if (mech_buf.length != buf_len) {
561c19800e8SDoug Rabson 	    abort();
562*ae771770SStanislav Sedov             UNREACHABLE(return GSS_S_FAILURE);
563*ae771770SStanislav Sedov         }
564c19800e8SDoug Rabson 
565*ae771770SStanislav Sedov 	if (resp.u.negTokenResp.mechListMIC == NULL) {
566c19800e8SDoug Rabson 	    HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex);
567c19800e8SDoug Rabson 	    free(mech_buf.value);
568*ae771770SStanislav Sedov 	    free_NegotiationToken(&resp);
569c19800e8SDoug Rabson 	    *minor_status = 0;
570c19800e8SDoug Rabson 	    return GSS_S_DEFECTIVE_TOKEN;
571c19800e8SDoug Rabson 	}
572*ae771770SStanislav Sedov 	mic_buf.length = resp.u.negTokenResp.mechListMIC->length;
573*ae771770SStanislav Sedov 	mic_buf.value  = resp.u.negTokenResp.mechListMIC->data;
574c19800e8SDoug Rabson 
575c19800e8SDoug Rabson 	if (mech_output_token.length == 0) {
576c19800e8SDoug Rabson 	    ret = gss_verify_mic(minor_status,
577c19800e8SDoug Rabson 				 ctx->negotiated_ctx_id,
578c19800e8SDoug Rabson 				 &mech_buf,
579c19800e8SDoug Rabson 				 &mic_buf,
580c19800e8SDoug Rabson 				 NULL);
581c19800e8SDoug Rabson 	   if (ret) {
582c19800e8SDoug Rabson 		HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex);
583c19800e8SDoug Rabson 		free(mech_buf.value);
584c19800e8SDoug Rabson 		gss_release_buffer(&minor, &mech_output_token);
585*ae771770SStanislav Sedov 		free_NegotiationToken(&resp);
586c19800e8SDoug Rabson 		return GSS_S_DEFECTIVE_TOKEN;
587c19800e8SDoug Rabson 	    }
588c19800e8SDoug Rabson 	    ctx->verified_mic = 1;
589c19800e8SDoug Rabson 	}
590c19800e8SDoug Rabson     }
591c19800e8SDoug Rabson 
592c19800e8SDoug Rabson     ret = spnego_reply_internal(minor_status, ctx,
593c19800e8SDoug Rabson 				require_mic ? &mech_buf : NULL,
594c19800e8SDoug Rabson 				&mech_output_token,
595c19800e8SDoug Rabson 				output_token);
596c19800e8SDoug Rabson 
597c19800e8SDoug Rabson     if (mech_buf.value != NULL)
598c19800e8SDoug Rabson 	free(mech_buf.value);
599c19800e8SDoug Rabson 
600*ae771770SStanislav Sedov     free_NegotiationToken(&resp);
601c19800e8SDoug Rabson     gss_release_buffer(&minor, &mech_output_token);
602c19800e8SDoug Rabson 
603c19800e8SDoug Rabson     if (actual_mech_type)
604c19800e8SDoug Rabson 	*actual_mech_type = ctx->negotiated_mech_type;
605c19800e8SDoug Rabson     if (ret_flags)
606c19800e8SDoug Rabson 	*ret_flags = ctx->mech_flags;
607c19800e8SDoug Rabson     if (time_rec)
608c19800e8SDoug Rabson 	*time_rec = ctx->mech_time_rec;
609c19800e8SDoug Rabson 
610c19800e8SDoug Rabson     HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex);
611c19800e8SDoug Rabson     return ret;
612c19800e8SDoug Rabson }
613c19800e8SDoug Rabson 
614*ae771770SStanislav Sedov OM_uint32 GSSAPI_CALLCONV
_gss_spnego_init_sec_context(OM_uint32 * minor_status,const gss_cred_id_t initiator_cred_handle,gss_ctx_id_t * context_handle,const gss_name_t target_name,const gss_OID mech_type,OM_uint32 req_flags,OM_uint32 time_req,const gss_channel_bindings_t input_chan_bindings,const gss_buffer_t input_token,gss_OID * actual_mech_type,gss_buffer_t output_token,OM_uint32 * ret_flags,OM_uint32 * time_rec)615*ae771770SStanislav Sedov _gss_spnego_init_sec_context
616c19800e8SDoug Rabson            (OM_uint32 * minor_status,
617c19800e8SDoug Rabson             const gss_cred_id_t initiator_cred_handle,
618c19800e8SDoug Rabson             gss_ctx_id_t * context_handle,
619c19800e8SDoug Rabson             const gss_name_t target_name,
620c19800e8SDoug Rabson             const gss_OID mech_type,
621c19800e8SDoug Rabson             OM_uint32 req_flags,
622c19800e8SDoug Rabson             OM_uint32 time_req,
623c19800e8SDoug Rabson             const gss_channel_bindings_t input_chan_bindings,
624c19800e8SDoug Rabson             const gss_buffer_t input_token,
625c19800e8SDoug Rabson             gss_OID * actual_mech_type,
626c19800e8SDoug Rabson             gss_buffer_t output_token,
627c19800e8SDoug Rabson             OM_uint32 * ret_flags,
628c19800e8SDoug Rabson             OM_uint32 * time_rec
629c19800e8SDoug Rabson            )
630c19800e8SDoug Rabson {
631c19800e8SDoug Rabson     if (*context_handle == GSS_C_NO_CONTEXT)
632c19800e8SDoug Rabson 	return spnego_initial (minor_status,
633*ae771770SStanislav Sedov 			       initiator_cred_handle,
634c19800e8SDoug Rabson 			       context_handle,
635c19800e8SDoug Rabson 			       target_name,
636c19800e8SDoug Rabson 			       mech_type,
637c19800e8SDoug Rabson 			       req_flags,
638c19800e8SDoug Rabson 			       time_req,
639c19800e8SDoug Rabson 			       input_chan_bindings,
640c19800e8SDoug Rabson 			       input_token,
641c19800e8SDoug Rabson 			       actual_mech_type,
642c19800e8SDoug Rabson 			       output_token,
643c19800e8SDoug Rabson 			       ret_flags,
644c19800e8SDoug Rabson 			       time_rec);
645c19800e8SDoug Rabson     else
646c19800e8SDoug Rabson 	return spnego_reply (minor_status,
647*ae771770SStanislav Sedov 			     initiator_cred_handle,
648c19800e8SDoug Rabson 			     context_handle,
649c19800e8SDoug Rabson 			     target_name,
650c19800e8SDoug Rabson 			     mech_type,
651c19800e8SDoug Rabson 			     req_flags,
652c19800e8SDoug Rabson 			     time_req,
653c19800e8SDoug Rabson 			     input_chan_bindings,
654c19800e8SDoug Rabson 			     input_token,
655c19800e8SDoug Rabson 			     actual_mech_type,
656c19800e8SDoug Rabson 			     output_token,
657c19800e8SDoug Rabson 			     ret_flags,
658c19800e8SDoug Rabson 			     time_rec);
659c19800e8SDoug Rabson }
660c19800e8SDoug Rabson 
661