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