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