s4:torture: Adapt KDC canon test to Heimdal upstream changes
[samba.git] / third_party / heimdal / lib / gssapi / spnego / accept_sec_context.c
1 /*
2  * Copyright (c) 1997 - 2006 Kungliga Tekniska Högskolan
3  * (Royal Institute of Technology, Stockholm, Sweden).
4  * Portions Copyright (c) 2004 PADL Software Pty Ltd.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  *
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  *
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * 3. Neither the name of the Institute nor the names of its contributors
18  *    may be used to endorse or promote products derived from this software
19  *    without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  */
33
34 #include "spnego_locl.h"
35
36 static OM_uint32
37 send_reject (OM_uint32 *minor_status,
38              gss_const_buffer_t mech_token,
39              gss_buffer_t output_token)
40 {
41     NegotiationToken nt;
42     size_t size;
43     heim_octet_string responseToken;
44
45     nt.element = choice_NegotiationToken_negTokenResp;
46
47     ALLOC(nt.u.negTokenResp.negState, 1);
48     if (nt.u.negTokenResp.negState == NULL) {
49         *minor_status = ENOMEM;
50         return GSS_S_FAILURE;
51     }
52     *(nt.u.negTokenResp.negState)  = reject;
53     nt.u.negTokenResp.supportedMech = NULL;
54     nt.u.negTokenResp.responseToken = NULL;
55
56     if (mech_token != GSS_C_NO_BUFFER && mech_token->value != NULL) {
57         responseToken.length = mech_token->length;
58         responseToken.data   = mech_token->value;
59         nt.u.negTokenResp.responseToken = &responseToken;
60      } else
61         nt.u.negTokenResp.responseToken = NULL;
62     nt.u.negTokenResp.mechListMIC   = NULL;
63
64     ASN1_MALLOC_ENCODE(NegotiationToken,
65                        output_token->value, output_token->length, &nt,
66                        &size, *minor_status);
67     nt.u.negTokenResp.responseToken = NULL; /* allocated on stack */
68     free_NegotiationToken(&nt);
69     if (*minor_status != 0)
70         return GSS_S_FAILURE;
71
72     return GSS_S_BAD_MECH;
73 }
74
75 static OM_uint32
76 acceptor_approved(OM_uint32 *minor_status,
77                   void *userptr,
78                   gss_const_name_t target_name,
79                   gss_const_cred_id_t cred_handle,
80                   gss_OID mech)
81 {
82     gss_cred_id_t cred = GSS_C_NO_CREDENTIAL;
83     gss_OID_set oidset = GSS_C_NO_OID_SET;
84     OM_uint32 junk, ret;
85
86     if (target_name == GSS_C_NO_NAME)
87         return GSS_S_COMPLETE;
88
89     if (gss_oid_equal(mech, GSS_NEGOEX_MECHANISM)) {
90         size_t i;
91
92         ret = _gss_spnego_indicate_mechs(minor_status, &oidset);
93         if (ret != GSS_S_COMPLETE)
94             return ret;
95
96         /* before committing to NegoEx, check we can negotiate a mech */
97         for (i = 0; i < oidset->count; i++) {
98             gss_OID inner_mech = &oidset->elements[i];
99
100             if (_gss_negoex_mech_p(inner_mech)) {
101                 ret = acceptor_approved(minor_status, userptr,
102                                         target_name, cred_handle,
103                                         inner_mech);
104                 if (ret == GSS_S_COMPLETE)
105                     break;
106             }
107         }
108     } else if (cred_handle != GSS_C_NO_CREDENTIAL) {
109         ret = gss_inquire_cred_by_mech(minor_status, cred_handle, mech,
110                                        NULL, NULL, NULL, NULL);
111     } else {
112         ret = gss_create_empty_oid_set(minor_status, &oidset);
113         if (ret == GSS_S_COMPLETE)
114             ret = gss_add_oid_set_member(minor_status, mech, &oidset);
115         if (ret == GSS_S_COMPLETE)
116             ret = gss_acquire_cred(minor_status, target_name,
117                                    GSS_C_INDEFINITE, oidset,
118                                GSS_C_ACCEPT, &cred, NULL, NULL);
119     }
120
121     gss_release_oid_set(&junk, &oidset);
122     gss_release_cred(&junk, &cred);
123
124     return ret;
125 }
126
127 static OM_uint32
128 send_supported_mechs (OM_uint32 *minor_status,
129                       gssspnego_ctx ctx,
130                       gss_const_cred_id_t acceptor_cred,
131                       gss_buffer_t output_token)
132 {
133     NegotiationToken2 nt;
134     size_t buf_len = 0;
135     gss_buffer_desc data;
136     OM_uint32 ret;
137
138     memset(&nt, 0, sizeof(nt));
139
140     nt.element = choice_NegotiationToken2_negTokenInit;
141     nt.u.negTokenInit.reqFlags = NULL;
142     nt.u.negTokenInit.mechToken = NULL;
143     nt.u.negTokenInit.negHints = NULL;
144
145     ret = _gss_spnego_indicate_mechtypelist(minor_status, GSS_C_NO_NAME, 0,
146                                             acceptor_approved, ctx, 1, acceptor_cred,
147                                             &nt.u.negTokenInit.mechTypes, NULL);
148     if (ret != GSS_S_COMPLETE) {
149         return ret;
150     }
151
152     ALLOC(nt.u.negTokenInit.negHints, 1);
153     if (nt.u.negTokenInit.negHints == NULL) {
154         *minor_status = ENOMEM;
155         free_NegotiationToken2(&nt);
156         return GSS_S_FAILURE;
157     }
158
159     ALLOC(nt.u.negTokenInit.negHints->hintName, 1);
160     if (nt.u.negTokenInit.negHints->hintName == NULL) {
161         *minor_status = ENOMEM;
162         free_NegotiationToken2(&nt);
163         return GSS_S_FAILURE;
164     }
165
166     *nt.u.negTokenInit.negHints->hintName = strdup("not_defined_in_RFC4178@please_ignore");
167     nt.u.negTokenInit.negHints->hintAddress = NULL;
168
169     ASN1_MALLOC_ENCODE(NegotiationToken2,
170                        data.value, data.length, &nt, &buf_len, ret);
171     free_NegotiationToken2(&nt);
172     if (ret) {
173         *minor_status = ret;
174         return GSS_S_FAILURE;
175     }
176     if (data.length != buf_len) {
177         abort();
178         UNREACHABLE(return GSS_S_FAILURE);
179     }
180
181     ret = gss_encapsulate_token(&data, GSS_SPNEGO_MECHANISM, output_token);
182
183     free (data.value);
184
185     if (ret != GSS_S_COMPLETE)
186         return ret;
187
188     *minor_status = 0;
189
190     return GSS_S_CONTINUE_NEEDED;
191 }
192
193 static OM_uint32
194 send_accept (OM_uint32 *minor_status,
195              gssspnego_ctx context_handle,
196              int optimistic_mech_ok,
197              gss_buffer_t mech_token,
198              gss_const_OID selected_mech, /* valid on initial response only */
199              gss_buffer_t mech_buf,
200              gss_buffer_t output_token)
201 {
202     int initial_response = (selected_mech != GSS_C_NO_OID);
203     NegotiationToken nt;
204     OM_uint32 ret, minor;
205     gss_buffer_desc mech_mic_buf;
206     size_t size;
207
208     memset(&nt, 0, sizeof(nt));
209
210     nt.element = choice_NegotiationToken_negTokenResp;
211
212     ALLOC(nt.u.negTokenResp.negState, 1);
213     if (nt.u.negTokenResp.negState == NULL) {
214         *minor_status = ENOMEM;
215         return GSS_S_FAILURE;
216     }
217
218     if (context_handle->flags.open) {
219         if (mech_token != GSS_C_NO_BUFFER
220             && mech_token->length != 0
221             && mech_buf != GSS_C_NO_BUFFER)
222             *(nt.u.negTokenResp.negState)  = accept_incomplete;
223         else
224             *(nt.u.negTokenResp.negState)  = accept_completed;
225     } else {
226         if (initial_response && !optimistic_mech_ok)
227             *(nt.u.negTokenResp.negState)  = request_mic;
228         else
229             *(nt.u.negTokenResp.negState)  = accept_incomplete;
230     }
231
232     if (initial_response) {
233         ALLOC(nt.u.negTokenResp.supportedMech, 1);
234         if (nt.u.negTokenResp.supportedMech == NULL) {
235             *minor_status = ENOMEM;
236             ret = GSS_S_FAILURE;
237             goto out;
238         }
239
240         ret = der_get_oid(selected_mech->elements,
241                           selected_mech->length,
242                           nt.u.negTokenResp.supportedMech,
243                           NULL);
244         if (ret) {
245             *minor_status = ENOMEM;
246             ret = GSS_S_FAILURE;
247             goto out;
248         }
249
250         _gss_spnego_log_mech("acceptor sending selected mech", selected_mech);
251     } else {
252         nt.u.negTokenResp.supportedMech = NULL;
253     }
254
255     if (mech_token != GSS_C_NO_BUFFER && mech_token->length != 0) {
256         ALLOC(nt.u.negTokenResp.responseToken, 1);
257         if (nt.u.negTokenResp.responseToken == NULL) {
258             *minor_status = ENOMEM;
259             ret = GSS_S_FAILURE;
260             goto out;
261         }
262         nt.u.negTokenResp.responseToken->length = mech_token->length;
263         nt.u.negTokenResp.responseToken->data   = mech_token->value;
264         mech_token->length = 0;
265         mech_token->value  = NULL;
266     } else {
267         nt.u.negTokenResp.responseToken = NULL;
268     }
269
270     if (mech_buf != GSS_C_NO_BUFFER) {
271         ret = gss_get_mic(minor_status,
272                           context_handle->negotiated_ctx_id,
273                           0,
274                           mech_buf,
275                           &mech_mic_buf);
276         if (ret == GSS_S_COMPLETE) {
277             _gss_spnego_ntlm_reset_crypto(&minor, context_handle, FALSE);
278
279             ALLOC(nt.u.negTokenResp.mechListMIC, 1);
280             if (nt.u.negTokenResp.mechListMIC == NULL) {
281                 gss_release_buffer(minor_status, &mech_mic_buf);
282                 *minor_status = ENOMEM;
283                 ret = GSS_S_FAILURE;
284                 goto out;
285             }
286             nt.u.negTokenResp.mechListMIC->length = mech_mic_buf.length;
287             nt.u.negTokenResp.mechListMIC->data   = mech_mic_buf.value;
288         } else if (ret == GSS_S_UNAVAILABLE) {
289             nt.u.negTokenResp.mechListMIC = NULL;
290         } else {
291             goto out;
292         }
293
294     } else
295         nt.u.negTokenResp.mechListMIC = NULL;
296
297     ASN1_MALLOC_ENCODE(NegotiationToken,
298                        output_token->value, output_token->length,
299                        &nt, &size, ret);
300     if (ret) {
301         *minor_status = ENOMEM;
302         ret = GSS_S_FAILURE;
303         goto out;
304     }
305
306     /*
307      * The response should not be encapsulated, because
308      * it is a SubsequentContextToken (note though RFC 1964
309      * specifies encapsulation for all _Kerberos_ tokens).
310      */
311
312     if (*(nt.u.negTokenResp.negState) == accept_completed)
313         ret = GSS_S_COMPLETE;
314     else
315         ret = GSS_S_CONTINUE_NEEDED;
316
317  out:
318     free_NegotiationToken(&nt);
319     return ret;
320 }
321
322 /*
323  * Return the default acceptor identity based on the local hostname
324  * or the GSSAPI_SPNEGO_NAME environment variable.
325  */
326
327 static OM_uint32
328 default_acceptor_name(OM_uint32 *minor_status,
329                       gss_name_t *namep)
330 {
331     OM_uint32 major_status;
332     gss_buffer_desc namebuf;
333     char *str = NULL, *host, hostname[MAXHOSTNAMELEN];
334
335     *namep = GSS_C_NO_NAME;
336
337     host = secure_getenv("GSSAPI_SPNEGO_NAME");
338     if (host == NULL) {
339         int rv;
340
341         if (gethostname(hostname, sizeof(hostname)) != 0) {
342             *minor_status = errno;
343             return GSS_S_FAILURE;
344         }
345
346         rv = asprintf(&str, "host@%s", hostname);
347         if (rv < 0 || str == NULL) {
348             *minor_status = ENOMEM;
349             return GSS_S_FAILURE;
350         }
351         host = str;
352     }
353
354     namebuf.length = strlen(host);
355     namebuf.value = host;
356
357     major_status = gss_import_name(minor_status, &namebuf,
358                                    GSS_C_NT_HOSTBASED_SERVICE, namep);
359
360     free(str);
361
362     return major_status;
363 }
364
365 /*
366  * Determine whether the mech in mechType can be negotiated. If the
367  * mech is NegoEx, make NegoEx mechanisms available for negotiation.
368  */
369
370 static OM_uint32
371 select_mech(OM_uint32 *minor_status,
372             gssspnego_ctx ctx,
373             gss_const_cred_id_t cred,
374             gss_const_OID_set supported_mechs,
375             MechType *mechType,
376             int verify_p, /* set on non-optimistic tokens */
377             gss_const_OID *advertised_mech_p)
378 {
379     char mechbuf[64];
380     size_t mech_len;
381     gss_OID_desc oid;
382     gss_OID selected_mech = GSS_C_NO_OID;
383     OM_uint32 ret, junk;
384     int negoex_proposed = FALSE, negoex_selected = FALSE;
385     int includeMSCompatOID = FALSE;
386     size_t i;
387
388     *minor_status = 0;
389     *advertised_mech_p = GSS_C_NO_OID; /* deals with broken MS OID */
390
391     ctx->selected_mech_type = GSS_C_NO_OID;
392
393     ret = der_put_oid ((unsigned char *)mechbuf + sizeof(mechbuf) - 1,
394                        sizeof(mechbuf),
395                        mechType,
396                        &mech_len);
397     if (ret)
398         return GSS_S_DEFECTIVE_TOKEN;
399
400     oid.length   = (OM_uint32)mech_len;
401     oid.elements = mechbuf + sizeof(mechbuf) - mech_len;
402
403     if (gss_oid_equal(&oid, GSS_NEGOEX_MECHANISM))
404         negoex_proposed = TRUE;
405     else if (gss_oid_equal(&oid, &_gss_spnego_mskrb_mechanism_oid_desc))
406         includeMSCompatOID = TRUE;
407
408     for (i = 0; i < supported_mechs->count; i++) {
409         gss_OID iter = &supported_mechs->elements[i];
410         auth_scheme scheme;
411         int is_negoex_mech = /* mechanism is negotiable under NegoEx */
412             gssspi_query_mechanism_info(&junk, iter, scheme) == GSS_S_COMPLETE;
413
414         if (is_negoex_mech && negoex_proposed) {
415             ret = _gss_negoex_add_auth_mech(minor_status, ctx, iter, scheme);
416             if (ret != GSS_S_COMPLETE)
417                 break;
418
419             negoex_selected = TRUE;
420         }
421
422         if (gss_oid_equal(includeMSCompatOID ? GSS_KRB5_MECHANISM : &oid, iter)) {
423             ret = _gss_intern_oid(minor_status, iter, &selected_mech);
424             if (ret != GSS_S_COMPLETE)
425                 return ret;
426
427             break;
428         }
429     }
430
431     /* always prefer NegoEx if a mechanism supported both */
432     if (negoex_selected)
433         selected_mech = GSS_NEGOEX_MECHANISM;
434     if (selected_mech == GSS_C_NO_OID)
435         ret = GSS_S_BAD_MECH;
436     if (ret != GSS_S_COMPLETE)
437         return ret;
438
439     heim_assert(!gss_oid_equal(selected_mech, GSS_SPNEGO_MECHANISM),
440                 "SPNEGO should not be able to negotiate itself");
441
442     if (verify_p) {
443         gss_name_t name = GSS_C_NO_NAME;
444
445         /*
446          * If we do not have a credential, acquire a default name as a hint
447          * to acceptor_approved() so it can attempt to acquire a default
448          * credential.
449          */
450         if (cred == GSS_C_NO_CREDENTIAL) {
451             ret = default_acceptor_name(minor_status, &name);
452             if (ret != GSS_S_COMPLETE)
453                 return ret;
454         }
455
456         ret = acceptor_approved(minor_status, ctx, name, cred, selected_mech);
457
458         gss_release_name(&junk, &name);
459     } else {
460         /* Stash optimistic mech for use by _gss_spnego_require_mechlist_mic() */
461         ret = gss_duplicate_oid(minor_status, &oid, &ctx->preferred_mech_type);
462     }
463
464     if (ret == GSS_S_COMPLETE) {
465         *minor_status = 0;
466
467         *advertised_mech_p = ctx->selected_mech_type = selected_mech;
468
469         /* if the initiator used the broken MS OID, send that instead */
470         if (includeMSCompatOID && gss_oid_equal(selected_mech, GSS_KRB5_MECHANISM))
471             *advertised_mech_p = &_gss_spnego_mskrb_mechanism_oid_desc;
472     }
473
474     return ret;
475 }
476
477
478 static OM_uint32
479 acceptor_complete(OM_uint32 * minor_status,
480                   gssspnego_ctx ctx,
481                   int *get_mic,
482                   gss_buffer_t mech_input_token,
483                   gss_buffer_t mech_output_token,
484                   heim_octet_string *mic,
485                   gss_buffer_t output_token)
486 {
487     gss_buffer_desc buf = GSS_C_EMPTY_BUFFER;
488     OM_uint32 ret;
489     int verify_mic;
490
491     ctx->flags.require_mic = 1;
492     ctx->flags.safe_omit = _gss_spnego_safe_omit_mechlist_mic(ctx);
493
494     if (ctx->flags.open) {
495         if (mech_input_token == GSS_C_NO_BUFFER) { /* Even/One */
496             verify_mic = 1;
497             *get_mic = 0;
498         } else if (mech_output_token != GSS_C_NO_BUFFER &&
499                    mech_output_token->length == 0) { /* Odd */
500             *get_mic = verify_mic = 1;
501         } else { /* Even/One */
502             verify_mic = 0;
503             *get_mic = 1;
504         }
505
506         /*
507          * Change from previous versions: do not generate a MIC if not
508          * necessary. This conforms to RFC4178 s.5 ("if the accepted
509          * mechanism is the most preferred mechanism of both the initiator
510          * and acceptor, then the MIC token exchange... is OPTIONAL"),
511          * and is consistent with MIT and Windows behavior.
512          */
513         if (ctx->flags.safe_omit)
514             *get_mic = 0;
515
516         if (verify_mic && mic == NULL && ctx->flags.safe_omit) {
517             /*
518              * Peer is old and didn't send a mic while we expected
519              * one, but since it safe to omit, let do that
520              */
521         } else if (verify_mic) {
522             ret = _gss_spnego_verify_mechtypes_mic(minor_status, ctx, mic);
523             if (ret) {
524                 if (*get_mic)
525                     send_reject(minor_status, GSS_C_NO_BUFFER, output_token);
526                 if (buf.value)
527                     free(buf.value);
528                 return ret;
529             }
530         }
531     } else
532         *get_mic = 0;
533
534     return GSS_S_COMPLETE;
535 }
536
537 /*
538  * Call gss_accept_sec_context() via mechglue or NegoEx, depending on
539  * whether mech_oid is NegoEx.
540  */
541
542 static OM_uint32
543 mech_accept(OM_uint32 *minor_status,
544             gssspnego_ctx ctx,
545             gss_const_cred_id_t acceptor_cred_handle,
546             gss_const_buffer_t input_token_buffer,
547             const gss_channel_bindings_t input_chan_bindings,
548             gss_buffer_t output_token,
549             gss_cred_id_t *delegated_cred_handle)
550 {
551     OM_uint32 ret, junk;
552
553     heim_assert(ctx->selected_mech_type != GSS_C_NO_OID,
554                 "mech_accept called with no selected mech");
555
556     if (gss_oid_equal(ctx->selected_mech_type, GSS_NEGOEX_MECHANISM)) {
557         ret = _gss_negoex_accept(minor_status,
558                                  ctx,
559                                  (gss_cred_id_t)acceptor_cred_handle,
560                                  input_token_buffer,
561                                  input_chan_bindings,
562                                  output_token,
563                                  delegated_cred_handle);
564     } else {
565         if (ctx->mech_src_name != GSS_C_NO_NAME)
566             gss_release_name(&junk, &ctx->mech_src_name);
567
568         ret = gss_accept_sec_context(minor_status,
569                                      &ctx->negotiated_ctx_id,
570                                      acceptor_cred_handle,
571                                      (gss_buffer_t)input_token_buffer,
572                                      input_chan_bindings,
573                                      &ctx->mech_src_name,
574                                      &ctx->negotiated_mech_type,
575                                      output_token,
576                                      &ctx->mech_flags,
577                                      &ctx->mech_time_rec,
578                                      delegated_cred_handle);
579         if (GSS_ERROR(ret))
580             gss_mg_collect_error(ctx->negotiated_mech_type, ret, *minor_status);
581         else if (ctx->negotiated_mech_type != GSS_C_NO_OID &&
582             !gss_oid_equal(ctx->negotiated_mech_type, ctx->selected_mech_type))
583             _gss_mg_log(1, "spnego client didn't send the mech they said they would");
584     }
585
586     return ret;
587 }
588
589 static OM_uint32 GSSAPI_CALLCONV
590 acceptor_start
591            (OM_uint32 * minor_status,
592             gss_ctx_id_t * context_handle,
593             gss_const_cred_id_t acceptor_cred_handle,
594             const gss_buffer_t input_token_buffer,
595             const gss_channel_bindings_t input_chan_bindings,
596             gss_name_t * src_name,
597             gss_OID * mech_type,
598             gss_buffer_t output_token,
599             OM_uint32 * ret_flags,
600             OM_uint32 * time_rec,
601             gss_cred_id_t *delegated_cred_handle
602            )
603 {
604     OM_uint32 ret, junk;
605     NegotiationToken nt;
606     gss_OID_set supported_mechs = GSS_C_NO_OID_SET;
607     size_t size;
608     NegTokenInit *ni;
609     gss_buffer_desc data;
610     gss_buffer_t mech_input_token = GSS_C_NO_BUFFER;
611     gss_buffer_desc mech_output_token;
612     gssspnego_ctx ctx;
613     int get_mic = 0, first_ok = 0, canonical_order;
614     gss_const_OID advertised_mech = GSS_C_NO_OID;
615
616     memset(&nt, 0, sizeof(nt));
617
618     mech_output_token.value = NULL;
619     mech_output_token.length = 0;
620
621     if (input_token_buffer->length == 0)
622         return send_supported_mechs (minor_status, NULL,
623                                      acceptor_cred_handle, output_token);
624
625     ret = _gss_spnego_alloc_sec_context(minor_status, context_handle);
626     if (ret != GSS_S_COMPLETE)
627         return ret;
628
629     ctx = (gssspnego_ctx)*context_handle;
630
631     HEIMDAL_MUTEX_lock(&ctx->ctx_id_mutex);
632
633     /*
634      * The GSS-API encapsulation is only present on the initial
635      * context token (negTokenInit).
636      */
637     ret = gss_decapsulate_token (input_token_buffer,
638                                  GSS_SPNEGO_MECHANISM,
639                                  &data);
640     if (ret)
641         goto out;
642
643     ret = decode_NegotiationToken(data.value, data.length, &nt, &size);
644     gss_release_buffer(minor_status, &data);
645     if (ret) {
646         *minor_status = ret;
647         ret = GSS_S_DEFECTIVE_TOKEN;
648         goto out;
649     }
650     if (nt.element != choice_NegotiationToken_negTokenInit) {
651         *minor_status = 0;
652         ret = GSS_S_DEFECTIVE_TOKEN;
653         goto out;
654     }
655     ni = &nt.u.negTokenInit;
656
657     if (ni->mechTypes.len < 1) {
658         free_NegotiationToken(&nt);
659         *minor_status = 0;
660         ret = GSS_S_DEFECTIVE_TOKEN;
661         goto out;
662     }
663
664     _gss_spnego_log_mechTypes(&ni->mechTypes);
665
666     {
667         MechTypeList mt;
668         int kret;
669
670         mt.len = ni->mechTypes.len;
671         mt.val = ni->mechTypes.val;
672
673         ASN1_MALLOC_ENCODE(MechTypeList,
674                            ctx->NegTokenInit_mech_types.value,
675                            ctx->NegTokenInit_mech_types.length,
676                            &mt, &size, kret);
677         if (kret) {
678             *minor_status = kret;
679             ret = GSS_S_FAILURE;
680             goto out;
681         }
682     }
683
684     if (acceptor_cred_handle != GSS_C_NO_CREDENTIAL)
685         ret = _gss_spnego_inquire_cred_mechs(minor_status,
686                                              acceptor_cred_handle,
687                                              &supported_mechs,
688                                              &canonical_order);
689     else
690         ret = _gss_spnego_indicate_mechs(minor_status, &supported_mechs);
691     if (ret != GSS_S_COMPLETE)
692         goto out;
693
694     /*
695      * First we try the opportunistic token if we have support for it,
696      * don't try to verify we have credential for the token,
697      * gss_accept_sec_context() will (hopefully) tell us that.
698      * If that failes,
699      */
700
701     ret = select_mech(minor_status,
702                       ctx,
703                       acceptor_cred_handle,
704                       supported_mechs,
705                       &ni->mechTypes.val[0],
706                       0, /* optimistic token */
707                       &advertised_mech);
708
709     if (ret == GSS_S_COMPLETE && ni->mechToken != NULL) {
710         gss_buffer_desc ibuf;
711
712         ibuf.length = ni->mechToken->length;
713         ibuf.value = ni->mechToken->data;
714         mech_input_token = &ibuf;
715
716         _gss_spnego_log_mech("acceptor selected opportunistic mech", ctx->selected_mech_type);
717
718         ret = mech_accept(&junk,
719                           ctx,
720                           acceptor_cred_handle,
721                           mech_input_token,
722                           input_chan_bindings,
723                           &mech_output_token,
724                           delegated_cred_handle);
725         if (ret == GSS_S_COMPLETE || ret == GSS_S_CONTINUE_NEEDED) {
726             first_ok = 1;
727         } else {
728             ctx->selected_mech_type = GSS_C_NO_OID;
729         }
730
731         if (ret == GSS_S_COMPLETE) {
732             ret = acceptor_complete(minor_status,
733                                     ctx,
734                                     &get_mic,
735                                     mech_input_token,
736                                     &mech_output_token,
737                                     ni->mechListMIC,
738                                     output_token);
739             if (ret != GSS_S_COMPLETE)
740                 goto out;
741
742             ctx->flags.open = 1;
743         }
744     } else {
745         *minor_status = 0;
746         HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex);
747         return gss_mg_set_error_string(GSS_C_NO_OID, GSS_S_NO_CONTEXT,
748                                        *minor_status,
749                                        "SPNEGO acceptor didn't find a prefered mechanism");
750     }
751
752     /*
753      * If opportunistic token failed, lets try the other mechs.
754      */
755
756     if (!first_ok) {
757         size_t j;
758
759         /* Call glue layer to find first mech we support */
760         for (j = 1; j < ni->mechTypes.len; ++j) {
761             ret = select_mech(&junk,
762                               ctx,
763                               acceptor_cred_handle,
764                               supported_mechs,
765                               &ni->mechTypes.val[j],
766                               1, /* not optimistic token */
767                               &advertised_mech);
768             if (ret == GSS_S_COMPLETE) {
769                 _gss_spnego_log_mech("acceptor selected non-opportunistic mech",
770                                      ctx->selected_mech_type);
771                 break;
772             }
773         }
774     }
775     if (ctx->selected_mech_type == GSS_C_NO_OID) {
776         heim_assert(ret != GSS_S_COMPLETE, "no oid and no error code?");
777         *minor_status = junk;
778         goto out;
779     }
780
781     /* The initial token always has a response */
782     ret = send_accept(minor_status,
783                       ctx,
784                       first_ok,
785                       &mech_output_token,
786                       advertised_mech,
787                       get_mic ? &ctx->NegTokenInit_mech_types : NULL,
788                       output_token);
789     if (ret)
790         goto out;
791
792 out:
793     gss_release_oid_set(&junk, &supported_mechs);
794     if (mech_output_token.value != NULL)
795         gss_release_buffer(&junk, &mech_output_token);
796     free_NegotiationToken(&nt);
797
798
799     if (ret == GSS_S_COMPLETE) {
800         if (src_name != NULL && ctx->mech_src_name != GSS_C_NO_NAME)
801             ret = gss_duplicate_name(minor_status,
802                                      ctx->mech_src_name,
803                                      src_name);
804     }
805
806     if (mech_type != NULL)
807         *mech_type = ctx->negotiated_mech_type;
808     if (ret_flags != NULL)
809         *ret_flags = ctx->mech_flags;
810     if (time_rec != NULL)
811         *time_rec = ctx->mech_time_rec;
812
813     if (ret == GSS_S_COMPLETE || ret == GSS_S_CONTINUE_NEEDED) {
814         HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex);
815         return ret;
816     }
817
818     _gss_spnego_internal_delete_sec_context(&junk, context_handle,
819                                             GSS_C_NO_BUFFER);
820
821     return ret;
822 }
823
824
825 static OM_uint32 GSSAPI_CALLCONV
826 acceptor_continue
827            (OM_uint32 * minor_status,
828             gss_ctx_id_t * context_handle,
829             gss_const_cred_id_t acceptor_cred_handle,
830             const gss_buffer_t input_token_buffer,
831             const gss_channel_bindings_t input_chan_bindings,
832             gss_name_t * src_name,
833             gss_OID * mech_type,
834             gss_buffer_t output_token,
835             OM_uint32 * ret_flags,
836             OM_uint32 * time_rec,
837             gss_cred_id_t *delegated_cred_handle
838            )
839 {
840     OM_uint32 ret, ret2, minor, junk;
841     NegotiationToken nt;
842     size_t nt_len;
843     NegTokenResp *na;
844     unsigned int negState = accept_incomplete;
845     gss_buffer_t mech_input_token = GSS_C_NO_BUFFER;
846     gss_buffer_t mech_output_token = GSS_C_NO_BUFFER;
847     gssspnego_ctx ctx;
848
849     ctx = (gssspnego_ctx)*context_handle;
850
851     /*
852      * The GSS-API encapsulation is only present on the initial
853      * context token (negTokenInit).
854      */
855
856     ret = decode_NegotiationToken(input_token_buffer->value,
857                                   input_token_buffer->length,
858                                   &nt, &nt_len);
859     if (ret) {
860         *minor_status = ret;
861         return GSS_S_DEFECTIVE_TOKEN;
862     }
863     if (nt.element != choice_NegotiationToken_negTokenResp) {
864         *minor_status = 0;
865         return GSS_S_DEFECTIVE_TOKEN;
866     }
867     na = &nt.u.negTokenResp;
868
869     if (na->negState != NULL) {
870         negState = *(na->negState);
871     }
872
873     HEIMDAL_MUTEX_lock(&ctx->ctx_id_mutex);
874
875     {
876         gss_buffer_desc ibuf, obuf;
877         int get_mic = 0;
878         int require_response;
879
880         if (na->responseToken != NULL) {
881             ibuf.length = na->responseToken->length;
882             ibuf.value = na->responseToken->data;
883             mech_input_token = &ibuf;
884         } else {
885             ibuf.value = NULL;
886             ibuf.length = 0;
887         }
888
889         if (mech_input_token != GSS_C_NO_BUFFER) {
890
891             ret = mech_accept(minor_status,
892                               ctx,
893                               acceptor_cred_handle,
894                               mech_input_token,
895                               input_chan_bindings,
896                               &obuf,
897                               delegated_cred_handle);
898             mech_output_token = &obuf;
899             if (ret != GSS_S_COMPLETE && ret != GSS_S_CONTINUE_NEEDED) {
900                 free_NegotiationToken(&nt);
901                 send_reject(&junk, mech_output_token, output_token);
902                 gss_release_buffer(&junk, mech_output_token);
903                 HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex);
904                 return ret;
905             }
906             if (ret == GSS_S_COMPLETE)
907                 ctx->flags.open = 1;
908         } else
909             ret = GSS_S_COMPLETE;
910
911         if (ret == GSS_S_COMPLETE)
912             ret = acceptor_complete(minor_status,
913                                     ctx,
914                                     &get_mic,
915                                     mech_input_token,
916                                     mech_output_token,
917                                     na->mechListMIC,
918                                     output_token);
919
920         if (ctx->mech_flags & GSS_C_DCE_STYLE)
921             require_response = (negState != accept_completed);
922         else
923             require_response = 0;
924
925         /*
926          * Check whether we need to send a result: there should be only
927          * one accept_completed response sent in the entire negotiation
928          */
929         if ((mech_output_token != GSS_C_NO_BUFFER &&
930              mech_output_token->length != 0)
931             || (ctx->flags.open && negState == accept_incomplete)
932             || require_response
933             || get_mic) {
934             ret2 = send_accept (minor_status,
935                                 ctx,
936                                 0, /* ignored on subsequent tokens */
937                                 mech_output_token,
938                                 GSS_C_NO_OID,
939                                 get_mic ? &ctx->NegTokenInit_mech_types : NULL,
940                                 output_token);
941             if (ret2)
942                 goto out;
943         } else
944             ret2 = GSS_S_COMPLETE;
945
946      out:
947         if (ret2 != GSS_S_COMPLETE)
948             ret = ret2;
949         if (mech_output_token != NULL)
950             gss_release_buffer(&minor, mech_output_token);
951         free_NegotiationToken(&nt);
952     }
953
954     if (ret == GSS_S_COMPLETE) {
955         if (src_name != NULL && ctx->mech_src_name != GSS_C_NO_NAME)
956             ret = gss_duplicate_name(minor_status,
957                                      ctx->mech_src_name,
958                                      src_name);
959     }
960
961     if (mech_type != NULL)
962         *mech_type = ctx->negotiated_mech_type;
963     if (ret_flags != NULL)
964         *ret_flags = ctx->mech_flags;
965     if (time_rec != NULL)
966         *time_rec = ctx->mech_time_rec;
967
968     if (ret == GSS_S_COMPLETE || ret == GSS_S_CONTINUE_NEEDED) {
969         HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex);
970         return ret;
971     }
972
973     _gss_spnego_internal_delete_sec_context(&minor, context_handle,
974                                    GSS_C_NO_BUFFER);
975
976     return ret;
977 }
978
979 OM_uint32 GSSAPI_CALLCONV
980 _gss_spnego_accept_sec_context
981            (OM_uint32 * minor_status,
982             gss_ctx_id_t * context_handle,
983             gss_const_cred_id_t acceptor_cred_handle,
984             const gss_buffer_t input_token_buffer,
985             const gss_channel_bindings_t input_chan_bindings,
986             gss_name_t * src_name,
987             gss_OID * mech_type,
988             gss_buffer_t output_token,
989             OM_uint32 * ret_flags,
990             OM_uint32 * time_rec,
991             gss_cred_id_t *delegated_cred_handle
992            )
993 {
994     _gss_accept_sec_context_t *func;
995
996     *minor_status = 0;
997
998     output_token->length = 0;
999     output_token->value  = NULL;
1000
1001     if (src_name != NULL)
1002         *src_name = GSS_C_NO_NAME;
1003     if (mech_type != NULL)
1004         *mech_type = GSS_C_NO_OID;
1005     if (ret_flags != NULL)
1006         *ret_flags = 0;
1007     if (time_rec != NULL)
1008         *time_rec = 0;
1009     if (delegated_cred_handle != NULL)
1010         *delegated_cred_handle = GSS_C_NO_CREDENTIAL;
1011
1012
1013     if (*context_handle == GSS_C_NO_CONTEXT)
1014         func = acceptor_start;
1015     else
1016         func = acceptor_continue;
1017
1018
1019     return (*func)(minor_status, context_handle, acceptor_cred_handle,
1020                    input_token_buffer, input_chan_bindings,
1021                    src_name, mech_type, output_token, ret_flags,
1022                    time_rec, delegated_cred_handle);
1023 }