]> CyberLeo.Net >> Repos - FreeBSD/releng/10.2.git/blob - crypto/heimdal/lib/gssapi/spnego/accept_sec_context.c
- Copy stable/10@285827 to releng/10.2 in preparation for 10.2-RC1
[FreeBSD/releng/10.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_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         if (preferred_mech_type == GSS_C_NO_OID) {
623             HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex);
624             free_NegotiationToken(&nt);
625             return ret;
626         }
627
628         ctx->preferred_mech_type = preferred_mech_type;
629     }
630
631     /*
632      * The initial token always have a response
633      */
634
635     ret = send_accept (minor_status,
636                        ctx,
637                        &mech_output_token,
638                        1,
639                        get_mic ? &mech_buf : NULL,
640                        output_token);
641     if (ret)
642         goto out;
643
644 out:
645     if (mech_output_token.value != NULL)
646         gss_release_buffer(&junk, &mech_output_token);
647     if (mech_buf.value != NULL) {
648         free(mech_buf.value);
649         mech_buf.value = NULL;
650     }
651     free_NegotiationToken(&nt);
652
653
654     if (ret == GSS_S_COMPLETE) {
655         if (src_name != NULL && ctx->mech_src_name != NULL) {
656             spnego_name name;
657
658             name = calloc(1, sizeof(*name));
659             if (name) {
660                 name->mech = ctx->mech_src_name;
661                 ctx->mech_src_name = NULL;
662                 *src_name = (gss_name_t)name;
663             }
664         }
665     }
666
667     if (mech_type != NULL)
668         *mech_type = ctx->negotiated_mech_type;
669     if (ret_flags != NULL)
670         *ret_flags = ctx->mech_flags;
671     if (time_rec != NULL)
672         *time_rec = ctx->mech_time_rec;
673
674     if (ret == GSS_S_COMPLETE || ret == GSS_S_CONTINUE_NEEDED) {
675         HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex);
676         return ret;
677     }
678
679     _gss_spnego_internal_delete_sec_context(&junk, context_handle,
680                                             GSS_C_NO_BUFFER);
681
682     return ret;
683 }
684
685
686 static OM_uint32 GSSAPI_CALLCONV
687 acceptor_continue
688            (OM_uint32 * minor_status,
689             gss_ctx_id_t * context_handle,
690             const gss_cred_id_t acceptor_cred_handle,
691             const gss_buffer_t input_token_buffer,
692             const gss_channel_bindings_t input_chan_bindings,
693             gss_name_t * src_name,
694             gss_OID * mech_type,
695             gss_buffer_t output_token,
696             OM_uint32 * ret_flags,
697             OM_uint32 * time_rec,
698             gss_cred_id_t *delegated_cred_handle
699            )
700 {
701     OM_uint32 ret, ret2, minor;
702     NegotiationToken nt;
703     size_t nt_len;
704     NegTokenResp *na;
705     unsigned int negResult = accept_incomplete;
706     gss_buffer_t mech_input_token = GSS_C_NO_BUFFER;
707     gss_buffer_t mech_output_token = GSS_C_NO_BUFFER;
708     gss_buffer_desc mech_buf;
709     gssspnego_ctx ctx;
710
711     mech_buf.value = NULL;
712
713     ctx = (gssspnego_ctx)*context_handle;
714
715     /*
716      * The GSS-API encapsulation is only present on the initial
717      * context token (negTokenInit).
718      */
719
720     ret = decode_NegotiationToken(input_token_buffer->value,
721                                   input_token_buffer->length,
722                                   &nt, &nt_len);
723     if (ret) {
724         *minor_status = ret;
725         return GSS_S_DEFECTIVE_TOKEN;
726     }
727     if (nt.element != choice_NegotiationToken_negTokenResp) {
728         *minor_status = 0;
729         return GSS_S_DEFECTIVE_TOKEN;
730     }
731     na = &nt.u.negTokenResp;
732
733     if (na->negResult != NULL) {
734         negResult = *(na->negResult);
735     }
736
737     HEIMDAL_MUTEX_lock(&ctx->ctx_id_mutex);
738
739     {
740         gss_buffer_desc ibuf, obuf;
741         int require_mic, get_mic = 0;
742         int require_response;
743         heim_octet_string *mic;
744
745         if (na->responseToken != NULL) {
746             ibuf.length = na->responseToken->length;
747             ibuf.value = na->responseToken->data;
748             mech_input_token = &ibuf;
749         } else {
750             ibuf.value = NULL;
751             ibuf.length = 0;
752         }
753
754         if (mech_input_token != GSS_C_NO_BUFFER) {
755
756             if (ctx->mech_src_name != GSS_C_NO_NAME)
757                 gss_release_name(&minor, &ctx->mech_src_name);
758
759             ret = gss_accept_sec_context(&minor,
760                                          &ctx->negotiated_ctx_id,
761                                          acceptor_cred_handle,
762                                          mech_input_token,
763                                          input_chan_bindings,
764                                          &ctx->mech_src_name,
765                                          &ctx->negotiated_mech_type,
766                                          &obuf,
767                                          &ctx->mech_flags,
768                                          &ctx->mech_time_rec,
769                                          delegated_cred_handle);
770
771             if (ret == GSS_S_COMPLETE || ret == GSS_S_CONTINUE_NEEDED) {
772                 mech_output_token = &obuf;
773             }
774             if (ret != GSS_S_COMPLETE && ret != GSS_S_CONTINUE_NEEDED) {
775                 free_NegotiationToken(&nt);
776                 gss_mg_collect_error(ctx->negotiated_mech_type, ret, minor);
777                 send_reject (minor_status, output_token);
778                 HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex);
779                 return ret;
780             }
781             if (ret == GSS_S_COMPLETE)
782                 ctx->open = 1;
783         } else
784             ret = GSS_S_COMPLETE;
785
786         ret2 = _gss_spnego_require_mechlist_mic(minor_status,
787                                                 ctx,
788                                                 &require_mic);
789         if (ret2)
790             goto out;
791
792         ctx->require_mic = require_mic;
793
794         mic = na->mechListMIC;
795         if (mic != NULL)
796             require_mic = 1;
797
798         if (ret == GSS_S_COMPLETE)
799             ret = acceptor_complete(minor_status,
800                                     ctx,
801                                     &get_mic,
802                                     &mech_buf,
803                                     mech_input_token,
804                                     mech_output_token,
805                                     na->mechListMIC,
806                                     output_token);
807
808         if (ctx->mech_flags & GSS_C_DCE_STYLE)
809             require_response = (negResult != accept_completed);
810         else
811             require_response = 0;
812
813         /*
814          * Check whether we need to send a result: there should be only
815          * one accept_completed response sent in the entire negotiation
816          */
817         if ((mech_output_token != GSS_C_NO_BUFFER &&
818              mech_output_token->length != 0)
819             || (ctx->open && negResult == accept_incomplete)
820             || require_response
821             || get_mic) {
822             ret2 = send_accept (minor_status,
823                                 ctx,
824                                 mech_output_token,
825                                 0,
826                                 get_mic ? &mech_buf : NULL,
827                                 output_token);
828             if (ret2)
829                 goto out;
830         }
831
832      out:
833         if (ret2 != GSS_S_COMPLETE)
834             ret = ret2;
835         if (mech_output_token != NULL)
836             gss_release_buffer(&minor, mech_output_token);
837         if (mech_buf.value != NULL)
838             free(mech_buf.value);
839         free_NegotiationToken(&nt);
840     }
841
842     if (ret == GSS_S_COMPLETE) {
843         if (src_name != NULL && ctx->mech_src_name != NULL) {
844             spnego_name name;
845
846             name = calloc(1, sizeof(*name));
847             if (name) {
848                 name->mech = ctx->mech_src_name;
849                 ctx->mech_src_name = NULL;
850                 *src_name = (gss_name_t)name;
851             }
852         }
853     }
854
855     if (mech_type != NULL)
856         *mech_type = ctx->negotiated_mech_type;
857     if (ret_flags != NULL)
858         *ret_flags = ctx->mech_flags;
859     if (time_rec != NULL)
860         *time_rec = ctx->mech_time_rec;
861
862     if (ret == GSS_S_COMPLETE || ret == GSS_S_CONTINUE_NEEDED) {
863         HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex);
864         return ret;
865     }
866
867     _gss_spnego_internal_delete_sec_context(&minor, context_handle,
868                                    GSS_C_NO_BUFFER);
869
870     return ret;
871 }
872
873 OM_uint32 GSSAPI_CALLCONV
874 _gss_spnego_accept_sec_context
875            (OM_uint32 * minor_status,
876             gss_ctx_id_t * context_handle,
877             const gss_cred_id_t acceptor_cred_handle,
878             const gss_buffer_t input_token_buffer,
879             const gss_channel_bindings_t input_chan_bindings,
880             gss_name_t * src_name,
881             gss_OID * mech_type,
882             gss_buffer_t output_token,
883             OM_uint32 * ret_flags,
884             OM_uint32 * time_rec,
885             gss_cred_id_t *delegated_cred_handle
886            )
887 {
888     _gss_accept_sec_context_t *func;
889
890     *minor_status = 0;
891
892     output_token->length = 0;
893     output_token->value  = NULL;
894
895     if (src_name != NULL)
896         *src_name = GSS_C_NO_NAME;
897     if (mech_type != NULL)
898         *mech_type = GSS_C_NO_OID;
899     if (ret_flags != NULL)
900         *ret_flags = 0;
901     if (time_rec != NULL)
902         *time_rec = 0;
903     if (delegated_cred_handle != NULL)
904         *delegated_cred_handle = GSS_C_NO_CREDENTIAL;
905
906
907     if (*context_handle == GSS_C_NO_CONTEXT)
908         func = acceptor_start;
909     else
910         func = acceptor_continue;
911
912
913     return (*func)(minor_status, context_handle, acceptor_cred_handle,
914                    input_token_buffer, input_chan_bindings,
915                    src_name, mech_type, output_token, ret_flags,
916                    time_rec, delegated_cred_handle);
917 }