1afe26f1e39dae042c68a37f69fa792a49a8efa9
[sfrench/samba-autobuild/.git] / source4 / 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/spnego_locl.h"
35
36 RCSID("$Id: accept_sec_context.c 21461 2007-07-10 14:01:13Z lha $");
37
38 static OM_uint32
39 send_reject (OM_uint32 *minor_status,
40              gss_buffer_t output_token)
41 {
42     NegotiationToken nt;
43     size_t size;
44
45     nt.element = choice_NegotiationToken_negTokenResp;
46
47     ALLOC(nt.u.negTokenResp.negResult, 1);
48     if (nt.u.negTokenResp.negResult == NULL) {
49         *minor_status = ENOMEM;
50         return GSS_S_FAILURE;
51     }
52     *(nt.u.negTokenResp.negResult)  = reject;
53     nt.u.negTokenResp.supportedMech = NULL;
54     nt.u.negTokenResp.responseToken = NULL;
55     nt.u.negTokenResp.mechListMIC   = NULL;
56     
57     ASN1_MALLOC_ENCODE(NegotiationToken,
58                        output_token->value, output_token->length, &nt,
59                        &size, *minor_status);
60     free_NegotiationToken(&nt);
61     if (*minor_status != 0)
62         return GSS_S_FAILURE;
63
64     return GSS_S_BAD_MECH;
65 }
66
67 static OM_uint32
68 acceptor_approved(gss_name_t target_name, gss_OID mech)
69 {
70     gss_cred_id_t cred = GSS_C_NO_CREDENTIAL;
71     gss_OID_set oidset;
72     OM_uint32 junk, ret;
73
74     if (target_name == GSS_C_NO_NAME)
75         return GSS_S_COMPLETE;
76
77     gss_create_empty_oid_set(&junk, &oidset);
78     gss_add_oid_set_member(&junk, mech, &oidset);
79     
80     ret = gss_acquire_cred(&junk, target_name, GSS_C_INDEFINITE, oidset,
81                            GSS_C_ACCEPT, &cred, NULL, NULL);
82     gss_release_oid_set(&junk, &oidset);
83     if (ret != GSS_S_COMPLETE)
84         return ret;
85     gss_release_cred(&junk, &cred);
86     
87     return GSS_S_COMPLETE;
88 }
89
90 static OM_uint32
91 send_supported_mechs (OM_uint32 *minor_status,
92                       gss_buffer_t output_token)
93 {
94     NegotiationTokenWin nt;
95     char hostname[MAXHOSTNAMELEN + 1], *p;
96     gss_buffer_desc name_buf;
97     gss_OID name_type;
98     gss_name_t target_princ;
99     gss_name_t canon_princ;
100     OM_uint32 minor;
101     size_t buf_len;
102     gss_buffer_desc data;
103     OM_uint32 ret;
104
105     memset(&nt, 0, sizeof(nt));
106
107     nt.element = choice_NegotiationTokenWin_negTokenInit;
108     nt.u.negTokenInit.reqFlags = NULL;
109     nt.u.negTokenInit.mechToken = NULL;
110     nt.u.negTokenInit.negHints = NULL;
111
112     ret = _gss_spnego_indicate_mechtypelist(minor_status, GSS_C_NO_NAME,
113                                             acceptor_approved, 1, NULL,
114                                             &nt.u.negTokenInit.mechTypes, NULL);
115     if (ret != GSS_S_COMPLETE) {
116         return ret;
117     }
118
119     memset(&target_princ, 0, sizeof(target_princ));
120     if (gethostname(hostname, sizeof(hostname) - 2) != 0) {
121         *minor_status = errno;
122         free_NegotiationTokenWin(&nt);
123         return GSS_S_FAILURE;
124     }
125     hostname[sizeof(hostname) - 1] = '\0';
126
127     /* Send the constructed SAM name for this host */
128     for (p = hostname; *p != '\0' && *p != '.'; p++) {
129         *p = toupper((unsigned char)*p);
130     }
131     *p++ = '$';
132     *p = '\0';
133
134     name_buf.length = strlen(hostname);
135     name_buf.value = hostname;
136
137     ret = gss_import_name(minor_status, &name_buf,
138                           GSS_C_NO_OID,
139                           &target_princ);
140     if (ret != GSS_S_COMPLETE) {
141         free_NegotiationTokenWin(&nt);
142         return ret;
143     }
144
145     name_buf.length = 0;
146     name_buf.value = NULL;
147
148     /* Canonicalize the name using the preferred mechanism */
149     ret = gss_canonicalize_name(minor_status,
150                                 target_princ,
151                                 GSS_C_NO_OID,
152                                 &canon_princ);
153     if (ret != GSS_S_COMPLETE) {
154         free_NegotiationTokenWin(&nt);
155         gss_release_name(&minor, &target_princ);
156         return ret;
157     }
158
159     ret = gss_display_name(minor_status, canon_princ,
160                            &name_buf, &name_type);
161     if (ret != GSS_S_COMPLETE) {
162         free_NegotiationTokenWin(&nt);
163         gss_release_name(&minor, &canon_princ);
164         gss_release_name(&minor, &target_princ);
165         return ret;
166     }
167
168     gss_release_name(&minor, &canon_princ);
169     gss_release_name(&minor, &target_princ);
170
171     ALLOC(nt.u.negTokenInit.negHints, 1);
172     if (nt.u.negTokenInit.negHints == NULL) {
173         *minor_status = ENOMEM;
174         gss_release_buffer(&minor, &name_buf);
175         free_NegotiationTokenWin(&nt);
176         return GSS_S_FAILURE;
177     }
178
179     ALLOC(nt.u.negTokenInit.negHints->hintName, 1);
180     if (nt.u.negTokenInit.negHints->hintName == NULL) {
181         *minor_status = ENOMEM;
182         gss_release_buffer(&minor, &name_buf);
183         free_NegotiationTokenWin(&nt);
184         return GSS_S_FAILURE;
185     }
186
187     *(nt.u.negTokenInit.negHints->hintName) = name_buf.value;
188     name_buf.value = NULL;
189     nt.u.negTokenInit.negHints->hintAddress = NULL;
190
191     ASN1_MALLOC_ENCODE(NegotiationTokenWin, 
192                        data.value, data.length, &nt, &buf_len, ret);
193     free_NegotiationTokenWin(&nt);
194     if (ret) {
195         return ret;
196     }
197     if (data.length != buf_len)
198         abort();
199
200     ret = gss_encapsulate_token(&data, GSS_SPNEGO_MECHANISM, output_token);
201
202     free (data.value);
203
204     if (ret != GSS_S_COMPLETE)
205         return ret;
206
207     *minor_status = 0;
208
209     return GSS_S_CONTINUE_NEEDED;
210 }
211
212 static OM_uint32
213 send_accept (OM_uint32 *minor_status,
214              gssspnego_ctx context_handle,
215              gss_buffer_t mech_token,
216              int initial_response,
217              gss_buffer_t mech_buf,
218              gss_buffer_t output_token)
219 {
220     NegotiationToken nt;
221     OM_uint32 ret;
222     gss_buffer_desc mech_mic_buf;
223     size_t size;
224
225     memset(&nt, 0, sizeof(nt));
226
227     nt.element = choice_NegotiationToken_negTokenResp;
228
229     ALLOC(nt.u.negTokenResp.negResult, 1);
230     if (nt.u.negTokenResp.negResult == NULL) {
231         *minor_status = ENOMEM;
232         return GSS_S_FAILURE;
233     }
234
235     if (context_handle->open) {
236         if (mech_token != GSS_C_NO_BUFFER
237             && mech_token->length != 0
238             && mech_buf != GSS_C_NO_BUFFER)
239             *(nt.u.negTokenResp.negResult)  = accept_incomplete;
240         else
241             *(nt.u.negTokenResp.negResult)  = accept_completed;
242     } else {
243         if (initial_response && context_handle->require_mic)
244             *(nt.u.negTokenResp.negResult)  = request_mic;
245         else
246             *(nt.u.negTokenResp.negResult)  = accept_incomplete;
247     }
248
249     if (initial_response) {
250         ALLOC(nt.u.negTokenResp.supportedMech, 1);
251         if (nt.u.negTokenResp.supportedMech == NULL) {
252             free_NegotiationToken(&nt);
253             *minor_status = ENOMEM;
254             return GSS_S_FAILURE;
255         }
256
257         ret = der_get_oid(context_handle->preferred_mech_type->elements,
258                           context_handle->preferred_mech_type->length,
259                           nt.u.negTokenResp.supportedMech,
260                           NULL);
261         if (ret) {
262             free_NegotiationToken(&nt);
263             *minor_status = ENOMEM;
264             return GSS_S_FAILURE;
265         }
266     } else {
267         nt.u.negTokenResp.supportedMech = NULL;
268     }
269
270     if (mech_token != GSS_C_NO_BUFFER && mech_token->length != 0) {
271         ALLOC(nt.u.negTokenResp.responseToken, 1);
272         if (nt.u.negTokenResp.responseToken == NULL) {
273             free_NegotiationToken(&nt);
274             *minor_status = ENOMEM;
275             return GSS_S_FAILURE;
276         }
277         nt.u.negTokenResp.responseToken->length = mech_token->length;
278         nt.u.negTokenResp.responseToken->data   = mech_token->value;
279         mech_token->length = 0;
280         mech_token->value  = NULL;
281     } else {
282         nt.u.negTokenResp.responseToken = NULL;
283     }
284
285     if (mech_buf != GSS_C_NO_BUFFER) {
286         ret = gss_get_mic(minor_status,
287                           context_handle->negotiated_ctx_id,
288                           0,
289                           mech_buf,
290                           &mech_mic_buf);
291         if (ret == GSS_S_COMPLETE) {
292             ALLOC(nt.u.negTokenResp.mechListMIC, 1);
293             if (nt.u.negTokenResp.mechListMIC == NULL) {
294                 gss_release_buffer(minor_status, &mech_mic_buf);
295                 free_NegotiationToken(&nt);
296                 *minor_status = ENOMEM;
297                 return GSS_S_FAILURE;
298             }
299             nt.u.negTokenResp.mechListMIC->length = mech_mic_buf.length;
300             nt.u.negTokenResp.mechListMIC->data   = mech_mic_buf.value;
301         } else if (ret == GSS_S_UNAVAILABLE) {
302             nt.u.negTokenResp.mechListMIC = NULL;
303         } else {
304             free_NegotiationToken(&nt);
305             return ret;
306         }
307
308     } else
309         nt.u.negTokenResp.mechListMIC = NULL;
310  
311     ASN1_MALLOC_ENCODE(NegotiationToken,
312                        output_token->value, output_token->length,
313                        &nt, &size, ret);
314     if (ret) {
315         free_NegotiationToken(&nt);
316         *minor_status = ret;
317         return GSS_S_FAILURE;
318     }
319
320     /*
321      * The response should not be encapsulated, because
322      * it is a SubsequentContextToken (note though RFC 1964
323      * specifies encapsulation for all _Kerberos_ tokens).
324      */
325
326     if (*(nt.u.negTokenResp.negResult) == accept_completed)
327         ret = GSS_S_COMPLETE;
328     else
329         ret = GSS_S_CONTINUE_NEEDED;
330     free_NegotiationToken(&nt);
331     return ret;
332 }
333
334
335 static OM_uint32
336 verify_mechlist_mic
337            (OM_uint32 *minor_status,
338             gssspnego_ctx context_handle,
339             gss_buffer_t mech_buf,
340             heim_octet_string *mechListMIC
341            )
342 {
343     OM_uint32 ret;
344     gss_buffer_desc mic_buf;
345
346     if (context_handle->verified_mic) {
347         /* This doesn't make sense, we've already verified it? */
348         *minor_status = 0;
349         return GSS_S_DUPLICATE_TOKEN;
350     }
351
352     if (mechListMIC == NULL) {
353         *minor_status = 0;
354         return GSS_S_DEFECTIVE_TOKEN;
355     }
356
357     mic_buf.length = mechListMIC->length;
358     mic_buf.value  = mechListMIC->data;
359
360     ret = gss_verify_mic(minor_status,
361                          context_handle->negotiated_ctx_id,
362                          mech_buf,
363                          &mic_buf,
364                          NULL);
365
366     if (ret != GSS_S_COMPLETE)
367         ret = GSS_S_DEFECTIVE_TOKEN;
368
369     return ret;
370 }
371
372 static OM_uint32
373 select_mech(OM_uint32 *minor_status, MechType *mechType, int verify_p,
374             gss_OID *mech_p)
375 {
376     char mechbuf[64];
377     size_t mech_len;
378     gss_OID_desc oid;
379     OM_uint32 ret, junk;
380
381     ret = der_put_oid ((unsigned char *)mechbuf + sizeof(mechbuf) - 1,
382                        sizeof(mechbuf),
383                        mechType,
384                        &mech_len);
385     if (ret) {
386         return GSS_S_DEFECTIVE_TOKEN;
387     }
388
389     oid.length   = mech_len;
390     oid.elements = mechbuf + sizeof(mechbuf) - mech_len;
391
392     if (gss_oid_equal(&oid, GSS_SPNEGO_MECHANISM)) {
393         return GSS_S_BAD_MECH;
394     }
395
396     *minor_status = 0;
397
398     /* Translate broken MS Kebreros OID */
399     if (gss_oid_equal(&oid, &_gss_spnego_mskrb_mechanism_oid_desc)) {
400         gssapi_mech_interface mech;
401
402         mech = __gss_get_mechanism(&_gss_spnego_krb5_mechanism_oid_desc);
403         if (mech == NULL)
404             return GSS_S_BAD_MECH;
405
406         ret = gss_duplicate_oid(minor_status,
407                                 &_gss_spnego_mskrb_mechanism_oid_desc,
408                                 mech_p);
409     } else {
410         gssapi_mech_interface mech;
411
412         mech = __gss_get_mechanism(&oid);
413         if (mech == NULL)
414             return GSS_S_BAD_MECH;
415
416         ret = gss_duplicate_oid(minor_status,
417                                 &mech->gm_mech_oid,
418                                 mech_p);
419     }
420
421     if (verify_p) {
422         gss_name_t name = GSS_C_NO_NAME;
423         gss_buffer_desc namebuf;
424         char *str = NULL, *host, hostname[MAXHOSTNAMELEN];
425
426         host = getenv("GSSAPI_SPNEGO_NAME");
427         if (host == NULL || issuid()) {
428             if (gethostname(hostname, sizeof(hostname)) != 0) {
429                 *minor_status = errno;
430                 return GSS_S_FAILURE;
431             }
432             asprintf(&str, "host@%s", hostname);
433             host = str;
434         }
435
436         namebuf.length = strlen(host);
437         namebuf.value = host;
438
439         ret = gss_import_name(minor_status, &namebuf,
440                               GSS_C_NT_HOSTBASED_SERVICE, &name);
441         if (str)
442             free(str);
443         if (ret != GSS_S_COMPLETE)
444             return ret;
445
446         ret = acceptor_approved(name, *mech_p);
447         gss_release_name(&junk, &name);
448     }
449
450     return ret;
451 }
452
453
454 static OM_uint32
455 acceptor_complete(OM_uint32 * minor_status,
456                   gssspnego_ctx ctx,
457                   int *get_mic,
458                   gss_buffer_t mech_buf,
459                   gss_buffer_t mech_input_token,
460                   gss_buffer_t mech_output_token,
461                   heim_octet_string *mic,
462                   gss_buffer_t output_token)
463 {
464     OM_uint32 ret;
465     int require_mic, verify_mic;
466     gss_buffer_desc buf;
467
468     buf.length = 0;
469     buf.value = NULL;
470
471     ret = _gss_spnego_require_mechlist_mic(minor_status, ctx, &require_mic);
472     if (ret)
473         return ret;
474     
475     ctx->require_mic = require_mic;
476
477     if (mic != NULL)
478         require_mic = 1;
479     
480     if (ctx->open && require_mic) {
481         if (mech_input_token == GSS_C_NO_BUFFER) { /* Even/One */
482             verify_mic = 1;
483             *get_mic = 0;
484         } else if (mech_output_token != GSS_C_NO_BUFFER &&
485                    mech_output_token->length == 0) { /* Odd */
486             *get_mic = verify_mic = 1;
487         } else { /* Even/One */
488             verify_mic = 0;
489             *get_mic = 1;
490         }
491         
492         if (verify_mic || get_mic) {
493             int eret;
494             size_t buf_len;
495             
496             ASN1_MALLOC_ENCODE(MechTypeList, 
497                                mech_buf->value, mech_buf->length,
498                                &ctx->initiator_mech_types, &buf_len, eret);
499             if (eret) {
500                 *minor_status = eret;
501                 return GSS_S_FAILURE;
502             }
503             if (buf.length != buf_len)
504                 abort();
505         }
506         
507         if (verify_mic) {
508             ret = verify_mechlist_mic(minor_status, ctx, mech_buf, mic);
509             if (ret) {
510                 if (get_mic)
511                     send_reject (minor_status, output_token);
512                 if (buf.value)
513                     free(buf.value);
514                 return ret;
515             }
516             ctx->verified_mic = 1;
517         }
518         if (buf.value)
519             free(buf.value);
520
521     } else
522         *get_mic = verify_mic = 0;
523     
524     return GSS_S_COMPLETE;
525 }
526
527
528 static OM_uint32
529 acceptor_start
530            (OM_uint32 * minor_status,
531             gss_ctx_id_t * context_handle,
532             const gss_cred_id_t acceptor_cred_handle,
533             const gss_buffer_t input_token_buffer,
534             const gss_channel_bindings_t input_chan_bindings,
535             gss_name_t * src_name,
536             gss_OID * mech_type,
537             gss_buffer_t output_token,
538             OM_uint32 * ret_flags,
539             OM_uint32 * time_rec,
540             gss_cred_id_t *delegated_cred_handle
541            )
542 {
543     OM_uint32 ret, junk, minor;
544     NegotiationToken nt;
545     size_t nt_len;
546     NegTokenInit *ni;
547     int i;
548     gss_buffer_desc data;
549     gss_buffer_t mech_input_token = GSS_C_NO_BUFFER;
550     gss_buffer_desc mech_output_token;
551     gss_buffer_desc mech_buf;
552     gss_OID preferred_mech_type = GSS_C_NO_OID;
553     gssspnego_ctx ctx;
554     gssspnego_cred acceptor_cred = (gssspnego_cred)acceptor_cred_handle;
555     int get_mic = 0;
556     int first_ok = 0;
557
558     mech_output_token.value = NULL;
559     mech_output_token.length = 0;
560     mech_buf.value = NULL;
561
562     if (input_token_buffer->length == 0)
563         return send_supported_mechs (minor_status, output_token);
564         
565     ret = _gss_spnego_alloc_sec_context(minor_status, context_handle);
566     if (ret != GSS_S_COMPLETE)
567         return ret;
568
569     ctx = (gssspnego_ctx)*context_handle;
570
571     /*
572      * The GSS-API encapsulation is only present on the initial
573      * context token (negTokenInit).
574      */
575     ret = gss_decapsulate_token (input_token_buffer,
576                                  GSS_SPNEGO_MECHANISM,
577                                  &data);
578     if (ret)
579         return ret;
580
581     ret = decode_NegotiationToken(data.value, data.length, &nt, &nt_len);
582     gss_release_buffer(minor_status, &data);
583     if (ret) {
584         *minor_status = ret;
585         return GSS_S_DEFECTIVE_TOKEN;
586     }
587     if (nt.element != choice_NegotiationToken_negTokenInit) {
588         *minor_status = 0;
589         return GSS_S_DEFECTIVE_TOKEN;
590     }
591     ni = &nt.u.negTokenInit;
592
593     if (ni->mechTypes.len < 1) {
594         free_NegotiationToken(&nt);
595         *minor_status = 0;
596         return GSS_S_DEFECTIVE_TOKEN;
597     }
598
599     HEIMDAL_MUTEX_lock(&ctx->ctx_id_mutex);
600
601     ret = copy_MechTypeList(&ni->mechTypes, &ctx->initiator_mech_types);
602     if (ret) {
603         HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex);
604         free_NegotiationToken(&nt);
605         *minor_status = ret;
606         return GSS_S_FAILURE;
607     }
608
609     /*
610      * First we try the opportunistic token if we have support for it,
611      * don't try to verify we have credential for the token,
612      * gss_accept_sec_context will (hopefully) tell us that.
613      * If that failes, 
614      */
615
616     ret = select_mech(minor_status,
617                       &ni->mechTypes.val[0], 
618                       0,
619                       &preferred_mech_type);
620
621     if (ret == 0 && ni->mechToken != NULL) {
622         gss_cred_id_t mech_delegated_cred = GSS_C_NO_CREDENTIAL;
623         gss_cred_id_t mech_cred;
624         gss_buffer_desc ibuf;
625
626         ibuf.length = ni->mechToken->length;
627         ibuf.value = ni->mechToken->data;
628         mech_input_token = &ibuf;
629
630         if (acceptor_cred != NULL)
631             mech_cred = acceptor_cred->negotiated_cred_id;
632         else
633             mech_cred = GSS_C_NO_CREDENTIAL;
634         
635         if (ctx->mech_src_name != GSS_C_NO_NAME)
636             gss_release_name(&minor, &ctx->mech_src_name);
637         
638         if (ctx->delegated_cred_id != GSS_C_NO_CREDENTIAL)
639             _gss_spnego_release_cred(&minor, &ctx->delegated_cred_id);
640         
641         ret = gss_accept_sec_context(&minor,
642                                      &ctx->negotiated_ctx_id,
643                                      mech_cred,
644                                      mech_input_token,
645                                      input_chan_bindings,
646                                      &ctx->mech_src_name,
647                                      &ctx->negotiated_mech_type,
648                                      &mech_output_token,
649                                      &ctx->mech_flags,
650                                      &ctx->mech_time_rec,
651                                      &mech_delegated_cred);
652         if (ret == GSS_S_COMPLETE || ret == GSS_S_CONTINUE_NEEDED) {
653             ctx->preferred_mech_type = preferred_mech_type;
654             ctx->negotiated_mech_type = preferred_mech_type;
655             if (ret == GSS_S_COMPLETE)
656                 ctx->open = 1;
657
658             if (mech_delegated_cred && delegated_cred_handle)
659                 ret = _gss_spnego_alloc_cred(minor_status,
660                                              mech_delegated_cred,
661                                              delegated_cred_handle);
662             else
663                 gss_release_cred(&junk, &mech_delegated_cred);
664
665             ret = acceptor_complete(minor_status,
666                                     ctx,
667                                     &get_mic,
668                                     &mech_buf,
669                                     mech_input_token,
670                                     &mech_output_token,
671                                     ni->mechListMIC,
672                                     output_token);
673             if (ret != GSS_S_COMPLETE)
674                 goto out;
675
676             first_ok = 1;
677         }
678     }
679
680     /*
681      * If opportunistic token failed, lets try the other mechs.
682      */
683
684     if (!first_ok) {
685
686         /* Call glue layer to find first mech we support */
687         for (i = 1; i < ni->mechTypes.len; ++i) {
688             ret = select_mech(minor_status,
689                               &ni->mechTypes.val[i],
690                               1,
691                               &preferred_mech_type);
692             if (ret == 0)
693                 break;
694         }
695         if (preferred_mech_type == GSS_C_NO_OID) {
696             HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex);
697             free_NegotiationToken(&nt);
698             return GSS_S_BAD_MECH;
699         }
700
701         ctx->preferred_mech_type = preferred_mech_type;
702         ctx->negotiated_mech_type = preferred_mech_type;
703     }
704
705     /*
706      * The initial token always have a response
707      */
708
709     ret = send_accept (minor_status,
710                        ctx,
711                        &mech_output_token,
712                        1,
713                        get_mic ? &mech_buf : NULL,
714                        output_token);
715     if (ret)
716         goto out;
717     
718 out:
719     if (mech_output_token.value != NULL)
720         gss_release_buffer(&minor, &mech_output_token);
721     if (mech_buf.value != NULL) {
722         free(mech_buf.value);
723         mech_buf.value = NULL;
724     }
725     free_NegotiationToken(&nt);
726
727
728     if (ret == GSS_S_COMPLETE) {
729         if (src_name != NULL && ctx->mech_src_name != NULL) {
730             spnego_name name;
731
732             name = calloc(1, sizeof(*name));
733             if (name) {
734                 name->mech = ctx->mech_src_name;
735                 ctx->mech_src_name = NULL;
736                 *src_name = (gss_name_t)name;
737             }
738         }
739         if (delegated_cred_handle != NULL) {
740             *delegated_cred_handle = ctx->delegated_cred_id;
741             ctx->delegated_cred_id = GSS_C_NO_CREDENTIAL;
742         }
743     }
744     
745     if (mech_type != NULL)
746         *mech_type = ctx->negotiated_mech_type;
747     if (ret_flags != NULL)
748         *ret_flags = ctx->mech_flags;
749     if (time_rec != NULL)
750         *time_rec = ctx->mech_time_rec;
751
752     if (ret == GSS_S_COMPLETE || ret == GSS_S_CONTINUE_NEEDED) {
753         HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex);
754         return ret;
755     }
756
757     _gss_spnego_internal_delete_sec_context(&minor, context_handle,
758                                             GSS_C_NO_BUFFER);
759     
760     return ret;
761 }
762
763
764 static OM_uint32
765 acceptor_continue
766            (OM_uint32 * minor_status,
767             gss_ctx_id_t * context_handle,
768             const gss_cred_id_t acceptor_cred_handle,
769             const gss_buffer_t input_token_buffer,
770             const gss_channel_bindings_t input_chan_bindings,
771             gss_name_t * src_name,
772             gss_OID * mech_type,
773             gss_buffer_t output_token,
774             OM_uint32 * ret_flags,
775             OM_uint32 * time_rec,
776             gss_cred_id_t *delegated_cred_handle
777            )
778 {
779     OM_uint32 ret, ret2, minor;
780     NegotiationToken nt;
781     size_t nt_len;
782     NegTokenResp *na;
783     unsigned int negResult = accept_incomplete;
784     gss_buffer_t mech_input_token = GSS_C_NO_BUFFER;
785     gss_buffer_t mech_output_token = GSS_C_NO_BUFFER;
786     gss_buffer_desc mech_buf;
787     gssspnego_ctx ctx;
788     gssspnego_cred acceptor_cred = (gssspnego_cred)acceptor_cred_handle;
789
790     mech_buf.value = NULL;
791
792     ctx = (gssspnego_ctx)*context_handle;
793
794     /*
795      * The GSS-API encapsulation is only present on the initial
796      * context token (negTokenInit).
797      */
798
799     ret = decode_NegotiationToken(input_token_buffer->value, 
800                                   input_token_buffer->length,
801                                   &nt, &nt_len);
802     if (ret) {
803         *minor_status = ret;
804         return GSS_S_DEFECTIVE_TOKEN;
805     }
806     if (nt.element != choice_NegotiationToken_negTokenResp) {
807         *minor_status = 0;
808         return GSS_S_DEFECTIVE_TOKEN;
809     }
810     na = &nt.u.negTokenResp;
811
812     if (na->negResult != NULL) {
813         negResult = *(na->negResult);
814     }
815
816     HEIMDAL_MUTEX_lock(&ctx->ctx_id_mutex);
817
818     {
819         gss_buffer_desc ibuf, obuf;
820         int require_mic, get_mic = 0;
821         int require_response;
822         heim_octet_string *mic;
823
824         if (na->responseToken != NULL) {
825             ibuf.length = na->responseToken->length;
826             ibuf.value = na->responseToken->data;
827             mech_input_token = &ibuf;
828         } else {
829             ibuf.value = NULL;
830             ibuf.length = 0;
831         }
832
833         if (mech_input_token != GSS_C_NO_BUFFER) {
834             gss_cred_id_t mech_cred;
835             gss_cred_id_t mech_delegated_cred;
836             gss_cred_id_t *mech_delegated_cred_p;
837
838             if (acceptor_cred != NULL)
839                 mech_cred = acceptor_cred->negotiated_cred_id;
840             else
841                 mech_cred = GSS_C_NO_CREDENTIAL;
842
843             if (delegated_cred_handle != NULL) {
844                 mech_delegated_cred = GSS_C_NO_CREDENTIAL;
845                 mech_delegated_cred_p = &mech_delegated_cred;
846             } else {
847                 mech_delegated_cred_p = NULL;
848             }
849
850             if (ctx->mech_src_name != GSS_C_NO_NAME)
851                 gss_release_name(&minor, &ctx->mech_src_name);
852
853             if (ctx->delegated_cred_id != GSS_C_NO_CREDENTIAL)
854                 _gss_spnego_release_cred(&minor, &ctx->delegated_cred_id);
855
856             ret = gss_accept_sec_context(&minor,
857                                          &ctx->negotiated_ctx_id,
858                                          mech_cred,
859                                          mech_input_token,
860                                          input_chan_bindings,
861                                          &ctx->mech_src_name,
862                                          &ctx->negotiated_mech_type,
863                                          &obuf,
864                                          &ctx->mech_flags,
865                                          &ctx->mech_time_rec,
866                                          mech_delegated_cred_p);
867             if (ret == GSS_S_COMPLETE || ret == GSS_S_CONTINUE_NEEDED) {
868                 if (mech_delegated_cred_p != NULL &&
869                     mech_delegated_cred != GSS_C_NO_CREDENTIAL) {
870                     ret2 = _gss_spnego_alloc_cred(minor_status,
871                                                   mech_delegated_cred,
872                                                   &ctx->delegated_cred_id);
873                     if (ret2 != GSS_S_COMPLETE)
874                         ret = ret2;
875                 }
876                 mech_output_token = &obuf;
877             }
878             if (ret != GSS_S_COMPLETE && ret != GSS_S_CONTINUE_NEEDED) {
879                 free_NegotiationToken(&nt);
880                 send_reject (minor_status, output_token);
881                 HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex);
882                 return ret;
883             }
884             if (ret == GSS_S_COMPLETE)
885                 ctx->open = 1;
886         } else
887             ret = GSS_S_COMPLETE;
888
889         ret2 = _gss_spnego_require_mechlist_mic(minor_status, 
890                                                 ctx,
891                                                 &require_mic);
892         if (ret2)
893             goto out;
894
895         ctx->require_mic = require_mic;
896
897         mic = na->mechListMIC;
898         if (mic != NULL)
899             require_mic = 1;
900
901         if (ret == GSS_S_COMPLETE)
902             ret = acceptor_complete(minor_status,
903                                     ctx,
904                                     &get_mic,
905                                     &mech_buf,
906                                     mech_input_token,
907                                     mech_output_token,
908                                     na->mechListMIC,
909                                     output_token);
910
911         if (ctx->mech_flags & GSS_C_DCE_STYLE)
912             require_response = (negResult != accept_completed);
913         else
914             require_response = 0;
915
916         /*
917          * Check whether we need to send a result: there should be only
918          * one accept_completed response sent in the entire negotiation
919          */
920         if ((mech_output_token != GSS_C_NO_BUFFER &&
921              mech_output_token->length != 0)
922             || (ctx->open && negResult == accept_incomplete)
923             || require_response
924             || get_mic) {
925             ret2 = send_accept (minor_status,
926                                 ctx,
927                                 mech_output_token,
928                                 0,
929                                 get_mic ? &mech_buf : NULL,
930                                 output_token);
931             if (ret2)
932                 goto out;
933         }
934
935      out:
936         if (ret2 != GSS_S_COMPLETE)
937             ret = ret2;
938         if (mech_output_token != NULL)
939             gss_release_buffer(&minor, mech_output_token);
940         if (mech_buf.value != NULL)
941             free(mech_buf.value);
942         free_NegotiationToken(&nt);
943     }
944
945     if (ret == GSS_S_COMPLETE) {
946         if (src_name != NULL && ctx->mech_src_name != NULL) {
947             spnego_name name;
948
949             name = calloc(1, sizeof(*name));
950             if (name) {
951                 name->mech = ctx->mech_src_name;
952                 ctx->mech_src_name = NULL;
953                 *src_name = (gss_name_t)name;
954             }
955         }
956         if (delegated_cred_handle != NULL) {
957             *delegated_cred_handle = ctx->delegated_cred_id;
958             ctx->delegated_cred_id = GSS_C_NO_CREDENTIAL;
959         }
960     }
961
962     if (mech_type != NULL)
963         *mech_type = ctx->negotiated_mech_type;
964     if (ret_flags != NULL)
965         *ret_flags = ctx->mech_flags;
966     if (time_rec != NULL)
967         *time_rec = ctx->mech_time_rec;
968
969     if (ret == GSS_S_COMPLETE || ret == GSS_S_CONTINUE_NEEDED) {
970         HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex);
971         return ret;
972     }
973
974     _gss_spnego_internal_delete_sec_context(&minor, context_handle,
975                                    GSS_C_NO_BUFFER);
976
977     return ret;
978 }
979
980 OM_uint32
981 _gss_spnego_accept_sec_context
982            (OM_uint32 * minor_status,
983             gss_ctx_id_t * context_handle,
984             const gss_cred_id_t acceptor_cred_handle,
985             const gss_buffer_t input_token_buffer,
986             const gss_channel_bindings_t input_chan_bindings,
987             gss_name_t * src_name,
988             gss_OID * mech_type,
989             gss_buffer_t output_token,
990             OM_uint32 * ret_flags,
991             OM_uint32 * time_rec,
992             gss_cred_id_t *delegated_cred_handle
993            )
994 {
995     _gss_accept_sec_context_t *func;
996
997     *minor_status = 0;
998
999     output_token->length = 0;
1000     output_token->value  = NULL;
1001
1002     if (src_name != NULL)
1003         *src_name = GSS_C_NO_NAME;
1004     if (mech_type != NULL)
1005         *mech_type = GSS_C_NO_OID;
1006     if (ret_flags != NULL)
1007         *ret_flags = 0;
1008     if (time_rec != NULL)
1009         *time_rec = 0;
1010     if (delegated_cred_handle != NULL)
1011         *delegated_cred_handle = GSS_C_NO_CREDENTIAL;
1012
1013
1014     if (*context_handle == GSS_C_NO_CONTEXT) 
1015         func = acceptor_start;
1016     else
1017         func = acceptor_continue;
1018     
1019
1020     return (*func)(minor_status, context_handle, acceptor_cred_handle,
1021                    input_token_buffer, input_chan_bindings,
1022                    src_name, mech_type, output_token, ret_flags,
1023                    time_rec, delegated_cred_handle);
1024 }