r19604: This is a massive commit, and I appologise in advance for it's size.
[kai/samba.git] / source4 / heimdal / lib / gssapi / spnego / accept_sec_context.c
1 /*
2  * Copyright (c) 1997 - 2004 Kungliga Tekniska Högskolan
3  * (Royal Institute of Technology, Stockholm, Sweden). 
4  * Portions Copyright (c) 2004 PADL Software Pty Ltd.
5  *
6  * Redistribution and use in source and binary forms, with or without 
7  * modification, are permitted provided that the following conditions 
8  * are met: 
9  *
10  * 1. Redistributions of source code must retain the above copyright 
11  *    notice, this list of conditions and the following disclaimer. 
12  *
13  * 2. Redistributions in binary form must reproduce the above copyright 
14  *    notice, this list of conditions and the following disclaimer in the 
15  *    documentation and/or other materials provided with the distribution. 
16  *
17  * 3. Neither the name of the Institute nor the names of its contributors 
18  *    may be used to endorse or promote products derived from this software 
19  *    without specific prior written permission. 
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND 
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE 
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 
31  * SUCH DAMAGE. 
32  */
33
34 #include "spnego/spnego_locl.h"
35
36 RCSID("$Id: accept_sec_context.c,v 1.6 2006/10/07 22:26:57 lha Exp $");
37
38 OM_uint32
39 _gss_spnego_encode_response(OM_uint32 *minor_status,
40                             const NegTokenResp *resp,
41                             gss_buffer_t data,
42                             u_char **ret_buf)
43 {
44     OM_uint32 ret;
45     u_char *buf;
46     size_t buf_size, buf_len;
47
48     buf_size = 1024;
49     buf = malloc(buf_size);
50     if (buf == NULL) {
51         *minor_status = ENOMEM;
52         return GSS_S_FAILURE;
53     }
54
55     do {
56         ret = encode_NegTokenResp(buf + buf_size - 1,
57                                   buf_size,
58                                   resp, &buf_len);
59         if (ret == 0) {
60             size_t tmp;
61
62             ret = der_put_length_and_tag(buf + buf_size - buf_len - 1,
63                                          buf_size - buf_len,
64                                          buf_len,
65                                          ASN1_C_CONTEXT,
66                                          CONS,
67                                          1,
68                                          &tmp);
69             if (ret == 0)
70                 buf_len += tmp;
71         }
72         if (ret) {
73             if (ret == ASN1_OVERFLOW) {
74                 u_char *tmp;
75
76                 buf_size *= 2;
77                 tmp = realloc (buf, buf_size);
78                 if (tmp == NULL) {
79                     *minor_status = ENOMEM;
80                     free(buf);
81                     return GSS_S_FAILURE;
82                 }
83                 buf = tmp;
84             } else {
85                 *minor_status = ret;
86                 free(buf);
87                 return GSS_S_FAILURE;
88             }
89         }
90     } while (ret == ASN1_OVERFLOW);
91
92     data->value  = buf + buf_size - buf_len;
93     data->length = buf_len;
94     *ret_buf     = buf;
95
96     return GSS_S_COMPLETE;
97 }
98
99 static OM_uint32
100 send_reject (OM_uint32 *minor_status,
101              gss_buffer_t output_token)
102 {
103     NegTokenResp resp;
104     gss_buffer_desc data;
105     u_char *buf;
106     OM_uint32 ret;
107
108     ALLOC(resp.negResult, 1);
109     if (resp.negResult == NULL) {
110         *minor_status = ENOMEM;
111         return GSS_S_FAILURE;
112     }
113     *(resp.negResult)  = reject;
114     resp.supportedMech = NULL;
115     resp.responseToken = NULL;
116     resp.mechListMIC   = NULL;
117     
118     ret = _gss_spnego_encode_response (minor_status, &resp, &data, &buf);
119     free_NegTokenResp(&resp);
120     if (ret != GSS_S_COMPLETE)
121         return ret;
122
123     output_token->value = malloc(data.length);
124     if (output_token->value == NULL) {
125         *minor_status = ENOMEM;
126         ret = GSS_S_FAILURE;
127     } else {
128         output_token->length = data.length;
129         memcpy(output_token->value, data.value, output_token->length);
130     }
131     free(buf);
132     if (ret != GSS_S_COMPLETE)
133         return ret;
134     return GSS_S_BAD_MECH;
135 }
136
137 OM_uint32
138 _gss_spnego_indicate_mechtypelist (OM_uint32 *minor_status,
139                                    int includeMSCompatOID,
140                                    const gssspnego_cred cred_handle,
141                                    MechTypeList *mechtypelist,
142                                    gss_OID *preferred_mech)
143 {
144     OM_uint32 ret;
145     gss_OID_set supported_mechs = GSS_C_NO_OID_SET;
146     int i, count;
147
148     if (cred_handle != NULL) {
149         ret = gss_inquire_cred(minor_status,
150                                cred_handle->negotiated_cred_id,
151                                NULL,
152                                NULL,
153                                NULL,
154                                &supported_mechs);
155     } else {
156         ret = gss_indicate_mechs(minor_status, &supported_mechs);
157     }
158
159     if (ret != GSS_S_COMPLETE) {
160         return ret;
161     }
162
163     if (supported_mechs->count == 0) {
164         *minor_status = ENOENT;
165         gss_release_oid_set(minor_status, &supported_mechs);
166         return GSS_S_FAILURE;
167     }
168
169     count = supported_mechs->count;
170     if (includeMSCompatOID)
171         count++;
172
173     mechtypelist->len = 0;
174     mechtypelist->val = calloc(count, sizeof(MechType));
175     if (mechtypelist->val == NULL) {
176         *minor_status = ENOMEM;
177         gss_release_oid_set(minor_status, &supported_mechs);
178         return GSS_S_FAILURE;
179     }
180
181     for (i = 0; i < supported_mechs->count; i++) {
182         ret = _gss_spnego_add_mech_type(&supported_mechs->elements[i],
183                                         includeMSCompatOID,
184                                         mechtypelist);
185         if (ret != 0) {
186             *minor_status = ENOMEM;
187             ret = GSS_S_FAILURE;
188             break;
189         }
190     }
191
192     if (ret == GSS_S_COMPLETE && preferred_mech != NULL) {
193         ret = gss_duplicate_oid(minor_status,
194                                 &supported_mechs->elements[0],
195                                 preferred_mech);
196     }
197
198     if (ret != GSS_S_COMPLETE) {
199         free_MechTypeList(mechtypelist);
200         mechtypelist->len = 0;
201         mechtypelist->val = NULL;
202     }
203     gss_release_oid_set(minor_status, &supported_mechs);
204
205     return ret;
206 }
207
208 static OM_uint32
209 send_supported_mechs (OM_uint32 *minor_status,
210                       gss_buffer_t output_token)
211 {
212     NegTokenInit ni;
213     char hostname[MAXHOSTNAMELEN], *p;
214     gss_buffer_desc name_buf;
215     gss_OID name_type;
216     gss_name_t target_princ;
217     gss_name_t canon_princ;
218     OM_uint32 ret, minor;
219     u_char *buf;
220     size_t buf_size, buf_len;
221     gss_buffer_desc data;
222
223     memset(&ni, 0, sizeof(ni));
224
225     ni.reqFlags = NULL;
226     ni.mechToken = NULL;
227     ni.negHints = NULL;
228     ni.mechListMIC = NULL;
229
230     ret = _gss_spnego_indicate_mechtypelist(minor_status, 1,
231                                             NULL,
232                                             &ni.mechTypes, NULL);
233     if (ret != GSS_S_COMPLETE) {
234         return ret;
235     }
236
237     memset(&target_princ, 0, sizeof(target_princ));
238     if (gethostname(hostname, sizeof(hostname) - 1) != 0) {
239         *minor_status = errno;
240         free_NegTokenInit(&ni);
241         return GSS_S_FAILURE;
242     }
243
244     /* Send the constructed SAM name for this host */
245     for (p = hostname; *p != '\0' && *p != '.'; p++) {
246         *p = toupper((unsigned char)*p);
247     }
248     *p++ = '$';
249     *p = '\0';
250
251     name_buf.length = strlen(hostname);
252     name_buf.value = hostname;
253
254     ret = gss_import_name(minor_status, &name_buf,
255                           GSS_C_NO_OID,
256                           &target_princ);
257     if (ret != GSS_S_COMPLETE) {
258         return ret;
259     }
260
261     name_buf.length = 0;
262     name_buf.value = NULL;
263
264     /* Canonicalize the name using the preferred mechanism */
265     ret = gss_canonicalize_name(minor_status,
266                                 target_princ,
267                                 GSS_C_NO_OID,
268                                 &canon_princ);
269     if (ret != GSS_S_COMPLETE) {
270         gss_release_name(&minor, &target_princ);
271         return ret;
272     }
273
274     ret = gss_display_name(minor_status, canon_princ,
275                            &name_buf, &name_type);
276     if (ret != GSS_S_COMPLETE) {
277         gss_release_name(&minor, &canon_princ);
278         gss_release_name(&minor, &target_princ);
279         return ret;
280     }
281
282     gss_release_name(&minor, &canon_princ);
283     gss_release_name(&minor, &target_princ);
284
285     ALLOC(ni.negHints, 1);
286     if (ni.negHints == NULL) {
287         *minor_status = ENOMEM;
288         gss_release_buffer(&minor, &name_buf);
289         free_NegTokenInit(&ni);
290         return GSS_S_FAILURE;
291     }
292
293     ALLOC(ni.negHints->hintName, 1);
294     if (ni.negHints->hintName == NULL) {
295         *minor_status = ENOMEM;
296         gss_release_buffer(&minor, &name_buf);
297         free_NegTokenInit(&ni);
298         return GSS_S_FAILURE;
299     }
300
301     *(ni.negHints->hintName) = name_buf.value;
302     name_buf.value = NULL;
303     ni.negHints->hintAddress = NULL;
304
305     buf_size = 1024;
306     buf = malloc(buf_size);
307     if (buf == NULL) {
308         free_NegTokenInit(&ni);
309         *minor_status = ENOMEM;
310         return GSS_S_FAILURE;
311     }
312
313     do {
314         ret = encode_NegTokenInit(buf + buf_size - 1,
315                                   buf_size,
316                                   &ni, &buf_len);
317         if (ret == 0) {
318             size_t tmp;
319
320             ret = der_put_length_and_tag(buf + buf_size - buf_len - 1,
321                                          buf_size - buf_len,
322                                          buf_len,
323                                          ASN1_C_CONTEXT,
324                                          CONS,
325                                          0,
326                                          &tmp);
327             if (ret == 0)
328                 buf_len += tmp;
329         }
330         if (ret) {
331             if (ret == ASN1_OVERFLOW) {
332                 u_char *tmp;
333
334                 buf_size *= 2;
335                 tmp = realloc (buf, buf_size);
336                 if (tmp == NULL) {
337                     *minor_status = ENOMEM;
338                     free(buf);
339                     free_NegTokenInit(&ni);
340                     return GSS_S_FAILURE;
341                 }
342                 buf = tmp;
343             } else {
344                 *minor_status = ret;
345                 free(buf);
346                 free_NegTokenInit(&ni);
347                 return GSS_S_FAILURE;
348             }
349         }
350     } while (ret == ASN1_OVERFLOW);
351
352     data.value  = buf + buf_size - buf_len;
353     data.length = buf_len;
354
355     ret = gss_encapsulate_token(&data,
356                                 GSS_SPNEGO_MECHANISM,
357                                 output_token);
358     free (buf);
359     free_NegTokenInit (&ni);
360
361     if (ret != GSS_S_COMPLETE)
362         return ret;
363
364     *minor_status = 0;
365
366     return GSS_S_CONTINUE_NEEDED;
367 }
368
369 static OM_uint32
370 send_accept (OM_uint32 *minor_status,
371              gssspnego_ctx context_handle,
372              gss_buffer_t mech_token,
373              int initial_response,
374              gss_buffer_t mech_buf,
375              gss_buffer_t output_token)
376 {
377     NegTokenResp resp;
378     gss_buffer_desc data;
379     u_char *buf;
380     OM_uint32 ret;
381     gss_buffer_desc mech_mic_buf;
382
383     memset(&resp, 0, sizeof(resp));
384
385     ALLOC(resp.negResult, 1);
386     if (resp.negResult == NULL) {
387         *minor_status = ENOMEM;
388         return GSS_S_FAILURE;
389     }
390
391     if (context_handle->open) {
392         if (mech_token != GSS_C_NO_BUFFER
393             && mech_token->length != 0
394             && mech_buf != GSS_C_NO_BUFFER)
395             *(resp.negResult) = accept_incomplete;
396         else
397             *(resp.negResult) = accept_completed;
398     } else {
399         if (initial_response && context_handle->require_mic)
400             *(resp.negResult) = request_mic;
401         else
402             *(resp.negResult) = accept_incomplete;
403     }
404
405     if (initial_response) {
406         ALLOC(resp.supportedMech, 1);
407         if (resp.supportedMech == NULL) {
408             free_NegTokenResp(&resp);
409             *minor_status = ENOMEM;
410             return GSS_S_FAILURE;
411         }
412
413         ret = der_get_oid(context_handle->preferred_mech_type->elements,
414                           context_handle->preferred_mech_type->length,
415                           resp.supportedMech,
416                           NULL);
417         if (ret) {
418             free_NegTokenResp(&resp);
419             *minor_status = ENOMEM;
420             return GSS_S_FAILURE;
421         }
422     } else {
423         resp.supportedMech = NULL;
424     }
425
426     if (mech_token != GSS_C_NO_BUFFER && mech_token->length != 0) {
427         ALLOC(resp.responseToken, 1);
428         if (resp.responseToken == NULL) {
429             free_NegTokenResp(&resp);
430             *minor_status = ENOMEM;
431             return GSS_S_FAILURE;
432         }
433         resp.responseToken->length = mech_token->length;
434         resp.responseToken->data   = mech_token->value;
435         mech_token->length = 0;
436         mech_token->value  = NULL;
437     } else {
438         resp.responseToken = NULL;
439     }
440
441     if (mech_buf != GSS_C_NO_BUFFER) {
442         ALLOC(resp.mechListMIC, 1);
443         if (resp.mechListMIC == NULL) {
444             free_NegTokenResp(&resp);
445             *minor_status = ENOMEM;
446             return GSS_S_FAILURE;
447         }
448
449         ret = gss_get_mic(minor_status,
450                           context_handle->negotiated_ctx_id,
451                           0,
452                           mech_buf,
453                           &mech_mic_buf);
454         if (ret != GSS_S_COMPLETE) {
455             free_NegTokenResp(&resp);
456             return ret;
457         }
458
459         resp.mechListMIC->length = mech_mic_buf.length;
460         resp.mechListMIC->data   = mech_mic_buf.value;
461     } else
462         resp.mechListMIC = NULL;
463  
464     ret = _gss_spnego_encode_response (minor_status, &resp, &data, &buf);
465     if (ret != GSS_S_COMPLETE) {
466         free_NegTokenResp(&resp);
467         return ret;
468     }
469
470     /*
471      * The response should not be encapsulated, because
472      * it is a SubsequentContextToken (note though RFC 1964
473      * specifies encapsulation for all _Kerberos_ tokens).
474      */
475     output_token->value = malloc(data.length);
476     if (output_token->value == NULL) {
477         *minor_status = ENOMEM;
478         ret = GSS_S_FAILURE;
479     } else {
480         output_token->length = data.length;
481         memcpy(output_token->value, data.value, output_token->length);
482     }
483     free(buf);
484     if (ret != GSS_S_COMPLETE) {
485         free_NegTokenResp(&resp);
486         return ret;
487     }
488
489     ret = (*(resp.negResult) == accept_completed) ? GSS_S_COMPLETE :
490                                                     GSS_S_CONTINUE_NEEDED;
491     free_NegTokenResp(&resp);
492     return ret;
493 }
494
495
496 static OM_uint32
497 verify_mechlist_mic
498            (OM_uint32 *minor_status,
499             gssspnego_ctx context_handle,
500             gss_buffer_t mech_buf,
501             heim_octet_string *mechListMIC
502            )
503 {
504     OM_uint32 ret;
505     gss_buffer_desc mic_buf;
506
507     if (context_handle->verified_mic) {
508         /* This doesn't make sense, we've already verified it? */
509         *minor_status = 0;
510         return GSS_S_DUPLICATE_TOKEN;
511     }
512
513     if (mechListMIC == NULL) {
514         *minor_status = 0;
515         return GSS_S_DEFECTIVE_TOKEN;
516     }
517
518     mic_buf.length = mechListMIC->length;
519     mic_buf.value  = mechListMIC->data;
520
521     ret = gss_verify_mic(minor_status,
522                          context_handle->negotiated_ctx_id,
523                          mech_buf,
524                          &mic_buf,
525                          NULL);
526
527     if (ret != GSS_S_COMPLETE)
528         ret = GSS_S_DEFECTIVE_TOKEN;
529
530     return ret;
531 }
532
533 OM_uint32
534 _gss_spnego_accept_sec_context
535            (OM_uint32 * minor_status,
536             gss_ctx_id_t * context_handle,
537             const gss_cred_id_t acceptor_cred_handle,
538             const gss_buffer_t input_token_buffer,
539             const gss_channel_bindings_t input_chan_bindings,
540             gss_name_t * src_name,
541             gss_OID * mech_type,
542             gss_buffer_t output_token,
543             OM_uint32 * ret_flags,
544             OM_uint32 * time_rec,
545             gss_cred_id_t *delegated_cred_handle
546            )
547 {
548     OM_uint32 ret, ret2, minor;
549     NegTokenInit ni;
550     NegTokenResp na;
551     size_t ni_len, na_len;
552     int i;
553     gss_buffer_desc data;
554     size_t len, taglen;
555     int initialToken;
556     unsigned int negResult = accept_incomplete;
557     gss_buffer_t mech_input_token = GSS_C_NO_BUFFER;
558     gss_buffer_t mech_output_token = GSS_C_NO_BUFFER;
559     gss_buffer_desc mech_buf;
560     gss_OID preferred_mech_type = GSS_C_NO_OID;
561     gssspnego_ctx ctx;
562     gssspnego_cred acceptor_cred = (gssspnego_cred)acceptor_cred_handle;
563
564     *minor_status = 0;
565
566     output_token->length = 0;
567     output_token->value  = NULL;
568
569     if (src_name != NULL)
570         *src_name = GSS_C_NO_NAME;
571
572     if (mech_type != NULL)
573         *mech_type = GSS_C_NO_OID;
574
575     if (ret_flags != NULL)
576         *ret_flags = 0;
577
578     if (time_rec != NULL)
579         *time_rec = 0;
580
581     if (delegated_cred_handle != NULL)
582         *delegated_cred_handle = GSS_C_NO_CREDENTIAL;
583
584     mech_buf.value = NULL;
585
586     if (*context_handle == GSS_C_NO_CONTEXT) {
587         ret = _gss_spnego_alloc_sec_context(minor_status,
588                                             context_handle);
589         if (ret != GSS_S_COMPLETE)
590             return ret;
591
592         if (input_token_buffer->length == 0) {
593             return send_supported_mechs (minor_status,
594                                          output_token);
595         }
596     }
597
598     ctx = (gssspnego_ctx)*context_handle;
599
600     /*
601      * The GSS-API encapsulation is only present on the initial
602      * context token (negTokenInit).
603      */
604     ret = gss_decapsulate_token (input_token_buffer,
605                                  GSS_SPNEGO_MECHANISM,
606                                  &data);
607     initialToken = (ret == GSS_S_COMPLETE);
608
609     if (!initialToken) {
610         data.value  = input_token_buffer->value;
611         data.length = input_token_buffer->length;
612     }
613
614     ret = der_match_tag_and_length(data.value, data.length,
615                                    ASN1_C_CONTEXT, CONS,
616                                    initialToken ? 0 : 1,
617                                    &len, &taglen);
618     if (ret) {
619         *minor_status = ret;
620         return GSS_S_FAILURE;
621     }
622
623     if (len > data.length - taglen) {
624         *minor_status = ASN1_OVERRUN;
625         return GSS_S_FAILURE;
626     }
627
628     if (initialToken) {
629         ret = decode_NegTokenInit((const unsigned char *)data.value + taglen, 
630                                   len, &ni, &ni_len);
631     } else {
632         ret = decode_NegTokenResp((const unsigned char *)data.value + taglen, 
633                                   len, &na, &na_len);
634     }
635     if (ret) {
636         *minor_status = ret;
637         return GSS_S_DEFECTIVE_TOKEN;
638     }
639
640     if (!initialToken && na.negResult != NULL) {
641         negResult = *(na.negResult);
642     }
643
644     if (negResult == reject || negResult == request_mic) {
645         /* request_mic should only be sent by acceptor */
646         free_NegTokenResp(&na);
647         return GSS_S_DEFECTIVE_TOKEN;
648     }
649
650     if (initialToken) {
651         for (i = 0; i < ni.mechTypes.len; ++i) {
652             /* Call glue layer to find first mech we support */
653             ret = _gss_spnego_select_mech(minor_status, &ni.mechTypes.val[i],
654                                           &preferred_mech_type);
655             if (ret == 0)
656                 break;
657         }
658         if (preferred_mech_type == GSS_C_NO_OID) {
659             free_NegTokenInit(&ni);
660             return GSS_S_BAD_MECH;
661         }
662     }
663
664     HEIMDAL_MUTEX_lock(&ctx->ctx_id_mutex);
665
666     if (initialToken) {
667         ctx->preferred_mech_type = preferred_mech_type;
668         ctx->initiator_mech_types.len = ni.mechTypes.len;
669         ctx->initiator_mech_types.val = ni.mechTypes.val;
670         ni.mechTypes.len = 0;
671         ni.mechTypes.val = NULL;
672     }
673
674     {
675         gss_buffer_desc ibuf, obuf;
676         int require_mic, verify_mic, get_mic;
677         int require_response;
678         heim_octet_string *mic;
679
680         if (initialToken) {
681             if (ni.mechToken != NULL) {
682                 ibuf.length = ni.mechToken->length;
683                 ibuf.value = ni.mechToken->data;
684                 mech_input_token = &ibuf;
685             }
686         } else {
687             if (na.responseToken != NULL) {
688                 ibuf.length = na.responseToken->length;
689                 ibuf.value = na.responseToken->data;
690                 mech_input_token = &ibuf;
691             }
692         }
693
694         if (mech_input_token != GSS_C_NO_BUFFER) {
695             gss_cred_id_t mech_cred;
696             gss_cred_id_t mech_delegated_cred;
697             gss_cred_id_t *mech_delegated_cred_p;
698
699             if (acceptor_cred != NULL)
700                 mech_cred = acceptor_cred->negotiated_cred_id;
701             else
702                 mech_cred = GSS_C_NO_CREDENTIAL;
703
704             if (delegated_cred_handle != NULL) {
705                 mech_delegated_cred = GSS_C_NO_CREDENTIAL;
706                 mech_delegated_cred_p = &mech_delegated_cred;
707             } else {
708                 mech_delegated_cred_p = NULL;
709             }
710
711             if (ctx->mech_src_name != GSS_C_NO_NAME)
712                 gss_release_name(&minor, &ctx->mech_src_name);
713
714             if (ctx->delegated_cred_id != GSS_C_NO_CREDENTIAL)
715                 _gss_spnego_release_cred(&minor, &ctx->delegated_cred_id);
716
717             ret = gss_accept_sec_context(&minor,
718                                          &ctx->negotiated_ctx_id,
719                                          mech_cred,
720                                          mech_input_token,
721                                          input_chan_bindings,
722                                          &ctx->mech_src_name,
723                                          &ctx->negotiated_mech_type,
724                                          &obuf,
725                                          &ctx->mech_flags,
726                                          &ctx->mech_time_rec,
727                                          mech_delegated_cred_p);
728             if (ret == GSS_S_COMPLETE || ret == GSS_S_CONTINUE_NEEDED) {
729                 if (mech_delegated_cred_p != NULL &&
730                     mech_delegated_cred != GSS_C_NO_CREDENTIAL) {
731                     ret2 = _gss_spnego_alloc_cred(minor_status,
732                                                   mech_delegated_cred,
733                                                   &ctx->delegated_cred_id);
734                     if (ret2 != GSS_S_COMPLETE)
735                         ret = ret2;
736                 }
737                 mech_output_token = &obuf;
738             }
739             if (ret != GSS_S_COMPLETE && ret != GSS_S_CONTINUE_NEEDED) {
740                 if (initialToken)
741                     free_NegTokenInit(&ni);
742                 else
743                     free_NegTokenResp(&na);
744                 send_reject (minor_status, output_token);
745                 HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex);
746                 return ret;
747             }
748             if (ret == GSS_S_COMPLETE)
749                 ctx->open = 1;
750         } else
751             ret = GSS_S_COMPLETE;
752
753         ret2 = _gss_spnego_require_mechlist_mic(minor_status, 
754                                                 ctx,
755                                                 &require_mic);
756         if (ret2)
757             goto out;
758
759         ctx->require_mic = require_mic;
760
761         mic = initialToken ? ni.mechListMIC : na.mechListMIC;
762         if (mic != NULL)
763             require_mic = 1;
764
765         if (ctx->open && require_mic) {
766             if (mech_input_token == GSS_C_NO_BUFFER) { /* Even/One */
767                 verify_mic = 1;
768                 get_mic = 0;
769             } else if (mech_output_token != GSS_C_NO_BUFFER &&
770                        mech_output_token->length == 0) { /* Odd */
771                 get_mic = verify_mic = 1;
772             } else { /* Even/One */
773                 verify_mic = 0;
774                 get_mic = 1;
775             }
776
777             if (verify_mic || get_mic) {
778                 int eret;
779                 size_t buf_len;
780
781                 ASN1_MALLOC_ENCODE(MechTypeList, 
782                                    mech_buf.value, mech_buf.length,
783                                    &ctx->initiator_mech_types, &buf_len, eret);
784                 if (eret) {
785                     ret2 = GSS_S_FAILURE;
786                     *minor_status = eret;
787                     goto out;
788                 }
789                 if (mech_buf.length != buf_len)
790                     abort();
791             }
792
793             if (verify_mic) {
794                 ret2 = verify_mechlist_mic(minor_status, ctx, &mech_buf, mic);
795                 if (ret2) {
796                     if (get_mic)
797                         send_reject (minor_status, output_token);
798                     goto out;
799                 }
800
801                 ctx->verified_mic = 1;
802             }
803         } else
804             verify_mic = get_mic = 0;
805
806         if (ctx->mech_flags & GSS_C_DCE_STYLE)
807             require_response = (negResult != accept_completed);
808         else
809             require_response = 0;
810
811         /*
812          * Check whether we need to send a result: there should be only
813          * one accept_completed response sent in the entire negotiation
814          */
815         if ((mech_output_token != GSS_C_NO_BUFFER &&
816              mech_output_token->length != 0)
817             || require_response
818             || get_mic) {
819             ret2 = send_accept (minor_status,
820                                 ctx,
821                                 mech_output_token,
822                                 initialToken,
823                                 get_mic ? &mech_buf : NULL,
824                                 output_token);
825             if (ret2)
826                 goto out;
827         }
828
829      out:
830         if (ret2 != GSS_S_COMPLETE)
831             ret = ret2;
832         if (mech_output_token != NULL)
833             gss_release_buffer(&minor, mech_output_token);
834         if (mech_buf.value != NULL)
835             free(mech_buf.value);
836         if (initialToken)
837             free_NegTokenInit(&ni);
838         else
839             free_NegTokenResp(&na);
840     }
841
842     if (ret == GSS_S_COMPLETE) {
843         if (src_name != NULL) {
844             ret2 = gss_duplicate_name(minor_status,
845                                       ctx->mech_src_name,
846                                       src_name);
847             if (ret2 != GSS_S_COMPLETE)
848                 ret = ret2;
849         }
850         if (delegated_cred_handle != NULL) {
851             *delegated_cred_handle = ctx->delegated_cred_id;
852             ctx->delegated_cred_id = GSS_C_NO_CREDENTIAL;
853         }
854     }
855
856     if (mech_type != NULL)
857         *mech_type = ctx->negotiated_mech_type;
858     if (ret_flags != NULL)
859         *ret_flags = ctx->mech_flags;
860     if (time_rec != NULL)
861         *time_rec = ctx->mech_time_rec;
862
863     if (ret == GSS_S_COMPLETE || ret == GSS_S_CONTINUE_NEEDED) {
864         HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex);
865         return ret;
866     }
867
868     _gss_spnego_internal_delete_sec_context(&minor, context_handle,
869                                    GSS_C_NO_BUFFER);
870
871     return ret;
872 }
873