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