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