s4:torture: Adapt KDC canon test to Heimdal upstream changes
[samba.git] / source4 / heimdal / lib / gssapi / spnego / init_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_locl.h"
35
36 #define GSISC(name) \
37 static                                                             \
38 OM_uint32 name(OM_uint32 *, gss_const_cred_id_t, gssspnego_ctx,    \
39                gss_const_name_t, gss_const_OID,                    \
40                OM_uint32, OM_uint32, const gss_channel_bindings_t, \
41                gss_const_buffer_t, gss_buffer_t,                   \
42                OM_uint32 *, OM_uint32 *)
43
44 GSISC(spnego_initial);
45 GSISC(spnego_reply);
46 GSISC(wait_server_mic);
47 GSISC(step_completed);
48
49
50  /*
51   * Is target_name an sane target for `mech´.
52   */
53
54 static OM_uint32
55 initiator_approved(OM_uint32 *minor_status,
56                    void *userptr,
57                    gss_const_name_t target_name,
58                    gss_const_cred_id_t cred,
59                    gss_OID mech)
60 {
61     OM_uint32 min_stat, maj_stat;
62     gss_ctx_id_t ctx = GSS_C_NO_CONTEXT;
63     gss_buffer_desc out;
64     struct gssspnego_optimistic_ctx *sel = userptr;
65     gss_OID negotiated_mech_type = GSS_C_NO_OID;
66     OM_uint32 flags = 0, time_rec = 0;
67     auth_scheme scheme;
68     int negoex = 0;
69
70     maj_stat = gss_init_sec_context(&min_stat,
71                                     cred,
72                                     &ctx,
73                                     sel->target_name,
74                                     mech,
75                                     sel->req_flags,
76                                     sel->time_req,
77                                     sel->input_chan_bindings,
78                                     GSS_C_NO_BUFFER,
79                                     &negotiated_mech_type,
80                                     &out,
81                                     &flags,
82                                     &time_rec);
83     if (GSS_ERROR(maj_stat)) {
84         gss_mg_collect_error(mech, maj_stat, min_stat);
85         *minor_status = min_stat;
86         return maj_stat;
87     }
88
89     if (gssspi_query_mechanism_info(&min_stat, mech, scheme) == GSS_S_COMPLETE)
90         negoex = 1;
91
92     if (sel->preferred_mech_type == GSS_C_NO_OID) {
93         sel->preferred_mech_type = mech;
94         sel->negotiated_mech_type = negotiated_mech_type;
95         sel->optimistic_token = out;
96         sel->optimistic_flags = flags;
97         sel->optimistic_time_rec = time_rec;
98         sel->gssctx = ctx;
99         if (maj_stat == GSS_S_COMPLETE)
100             sel->complete = 1;
101         if (negoex)
102             memcpy(sel->scheme, scheme, GUID_LENGTH);
103     } else {
104         gss_release_buffer(&min_stat, &out);
105         gss_delete_sec_context(&min_stat, &ctx, NULL);
106     }
107
108     maj_stat = GSS_S_COMPLETE;
109
110     if (negoex) {
111         maj_stat = _gss_negoex_add_auth_mech(minor_status, sel->spnegoctx,
112                                              mech, scheme);
113     }
114
115     return maj_stat;
116 }
117
118 /*
119  * Send a reply. Note that we only need to send a reply if we
120  * need to send a MIC or a mechanism token. Otherwise, we can
121  * return an empty buffer.
122  *
123  * The return value of this will be returned to the API, so it
124  * must return GSS_S_CONTINUE_NEEDED if a token was generated.
125  */
126 static OM_uint32
127 make_reply(OM_uint32 *minor_status,
128            gssspnego_ctx ctx,
129            gss_buffer_t mech_token,
130            gss_buffer_t output_token)
131 {
132     NegotiationToken nt;
133     gss_buffer_desc mic_buf;
134     OM_uint32 ret, minor;
135     size_t size;
136     NegStateEnum state;
137
138     memset(&nt, 0, sizeof(nt));
139
140     nt.element = choice_NegotiationToken_negTokenResp;
141
142     nt.u.negTokenResp.negState = NULL;
143     nt.u.negTokenResp.supportedMech = NULL;
144
145     output_token->length = 0;
146     output_token->value = NULL;
147
148     /* figure out our status */
149
150     if (ctx->flags.open) {
151         if (ctx->flags.verified_mic == 1 || ctx->flags.require_mic == 0)
152             state = accept_completed;
153         else
154             state = accept_incomplete;
155     } else  {
156         state = accept_incomplete;
157     }
158
159     if (mech_token->length == 0) {
160         nt.u.negTokenResp.responseToken = NULL;
161     } else {
162         ALLOC(nt.u.negTokenResp.responseToken, 1);
163         if (nt.u.negTokenResp.responseToken == NULL) {
164             free_NegotiationToken(&nt);
165             *minor_status = ENOMEM;
166             return GSS_S_FAILURE;
167         }
168         nt.u.negTokenResp.responseToken->length = mech_token->length;
169         nt.u.negTokenResp.responseToken->data   = mech_token->value;
170         mech_token->length = 0;
171         mech_token->value  = NULL;
172     }
173
174     /*
175      * XXX should limit when we send the MIC ?
176      */
177     if (ctx->flags.open && ctx->flags.sent_mic == 0) {
178
179         ctx->flags.sent_mic = 1;
180
181         ret = gss_get_mic(minor_status,
182                           ctx->negotiated_ctx_id,
183                           0,
184                           &ctx->NegTokenInit_mech_types,
185                           &mic_buf);
186         if (ret == GSS_S_COMPLETE) {
187             _gss_spnego_ntlm_reset_crypto(&minor, ctx, FALSE);
188
189             ALLOC(nt.u.negTokenResp.mechListMIC, 1);
190             if (nt.u.negTokenResp.mechListMIC == NULL) {
191                 gss_release_buffer(minor_status, &mic_buf);
192                 free_NegotiationToken(&nt);
193                 *minor_status = ENOMEM;
194                 return GSS_S_FAILURE;
195             }
196
197             nt.u.negTokenResp.mechListMIC->length = mic_buf.length;
198             nt.u.negTokenResp.mechListMIC->data   = mic_buf.value;
199             /* mic_buf free()d with nt */
200         } else if (ret == GSS_S_UNAVAILABLE) {
201             /* lets hope that its ok to not send te mechListMIC for broken mechs */
202             nt.u.negTokenResp.mechListMIC = NULL;
203             ctx->flags.require_mic = 0;
204         } else {
205             free_NegotiationToken(&nt);
206             *minor_status = ENOMEM;
207             return gss_mg_set_error_string(GSS_SPNEGO_MECHANISM,
208                                            ret, *minor_status,
209                                            "SPNEGO failed to sign MIC");
210         }
211     } else {
212         nt.u.negTokenResp.mechListMIC = NULL;
213     }
214
215     ALLOC(nt.u.negTokenResp.negState, 1);
216     if (nt.u.negTokenResp.negState == NULL) {
217         free_NegotiationToken(&nt);
218         *minor_status = ENOMEM;
219         return GSS_S_FAILURE;
220     }
221     *nt.u.negTokenResp.negState = state;
222
223     ASN1_MALLOC_ENCODE(NegotiationToken,
224                        output_token->value, output_token->length,
225                        &nt, &size, ret);
226     free_NegotiationToken(&nt);
227     if (ret) {
228         *minor_status = ret;
229         return GSS_S_FAILURE;
230     }
231
232     if (state != accept_completed)
233         return GSS_S_CONTINUE_NEEDED;
234
235     return GSS_S_COMPLETE;
236 }
237
238 static OM_uint32
239 spnego_initial(OM_uint32 * minor_status,
240                gss_const_cred_id_t cred,
241                gssspnego_ctx ctx,
242                gss_const_name_t target_name,
243                gss_const_OID mech_type,
244                OM_uint32 req_flags,
245                OM_uint32 time_req,
246                const gss_channel_bindings_t input_chan_bindings,
247                gss_const_buffer_t input_token,
248                gss_buffer_t output_token,
249                OM_uint32 * ret_flags,
250                OM_uint32 * time_rec)
251 {
252     NegotiationToken nt;
253     int ret;
254     OM_uint32 sub, minor;
255     gss_buffer_desc mech_token;
256     size_t size = 0;
257     gss_buffer_desc data;
258     struct gssspnego_optimistic_ctx sel;
259
260     *minor_status = 0;
261
262     memset(&nt, 0, sizeof(nt));
263
264     if (target_name == GSS_C_NO_NAME)
265         return GSS_S_BAD_NAME;
266
267     sub = gss_duplicate_name(&minor, target_name, &ctx->target_name);
268     if (GSS_ERROR(sub)) {
269         *minor_status = minor;
270         return sub;
271     }
272
273     nt.element = choice_NegotiationToken_negTokenInit;
274
275     ctx->flags.local = 1;
276
277     memset(&sel, 0, sizeof(sel));
278
279     sel.spnegoctx = ctx;
280     sel.target_name = ctx->target_name;
281     sel.preferred_mech_type = GSS_C_NO_OID;
282     sel.req_flags = req_flags;
283     sel.time_req = time_req;
284     sel.input_chan_bindings = (gss_channel_bindings_t)input_chan_bindings;
285
286     sub = _gss_spnego_indicate_mechtypelist(&minor,
287                                             ctx->target_name,
288                                             req_flags,
289                                             initiator_approved,
290                                             &sel,
291                                             0,
292                                             cred,
293                                             &nt.u.negTokenInit.mechTypes,
294                                             &ctx->preferred_mech_type);
295     if (GSS_ERROR(sub)) {
296         *minor_status = minor;
297         return sub;
298     }
299
300     _gss_spnego_log_mechTypes(&nt.u.negTokenInit.mechTypes);
301
302     nt.u.negTokenInit.reqFlags = NULL;
303
304     if (gss_oid_equal(ctx->preferred_mech_type, GSS_NEGOEX_MECHANISM)) {
305         struct negoex_auth_mech *mech;
306
307         sub = _gss_negoex_init(&minor,
308                                &sel,
309                                ctx,
310                                (gss_cred_id_t)cred,
311                                req_flags,
312                                time_req,
313                                input_chan_bindings,
314                                GSS_C_NO_BUFFER,
315                                &mech_token);
316         if (GSS_ERROR(sub)) {
317             free_NegotiationToken(&nt);
318             return gss_mg_set_error_string(GSS_C_NO_OID, sub, minor,
319                                            "NegoEx could not generate a context token");
320         }
321         mech = _gss_negoex_negotiated_mech(ctx);
322         ctx->flags.maybe_open = mech && mech->complete;
323         gss_release_buffer(&minor, &sel.optimistic_token);
324     } else {
325         /* optimistic token from selection context */
326         mech_token = sel.optimistic_token;
327         ctx->mech_flags = sel.optimistic_flags;
328         ctx->mech_time_rec = sel.optimistic_time_rec;
329         ctx->negotiated_mech_type = sel.negotiated_mech_type;
330         ctx->negotiated_ctx_id = sel.gssctx;
331         ctx->flags.maybe_open = sel.complete;
332     }
333
334     if (ctx->preferred_mech_type == GSS_C_NO_OID) {
335         free_NegotiationToken(&nt);
336         *minor_status = 0;
337         return gss_mg_set_error_string(GSS_C_NO_OID, GSS_S_NO_CONTEXT, 0,
338                                        "SPNEGO could not find a preferred mechanism");
339     }
340
341
342     if (mech_token.length != 0) {
343         ALLOC(nt.u.negTokenInit.mechToken, 1);
344         if (nt.u.negTokenInit.mechToken == NULL) {
345             free_NegotiationToken(&nt);
346             gss_release_buffer(&minor, &mech_token);
347             *minor_status = ENOMEM;
348             return GSS_S_FAILURE;
349         }
350         nt.u.negTokenInit.mechToken->length = mech_token.length;
351         nt.u.negTokenInit.mechToken->data = malloc(mech_token.length);
352         if (nt.u.negTokenInit.mechToken->data == NULL && mech_token.length != 0) {
353             free_NegotiationToken(&nt);
354             gss_release_buffer(&minor, &mech_token);
355             *minor_status = ENOMEM;
356             return GSS_S_FAILURE;
357         }
358         memcpy(nt.u.negTokenInit.mechToken->data, mech_token.value, mech_token.length);
359         gss_release_buffer(&minor, &mech_token);
360     } else
361         nt.u.negTokenInit.mechToken = NULL;
362
363     nt.u.negTokenInit.mechListMIC = NULL;
364
365     {
366         MechTypeList mt;
367
368         mt.len = nt.u.negTokenInit.mechTypes.len;
369         mt.val = nt.u.negTokenInit.mechTypes.val;
370
371         ASN1_MALLOC_ENCODE(MechTypeList,
372                            ctx->NegTokenInit_mech_types.value,
373                            ctx->NegTokenInit_mech_types.length,
374                            &mt, &size, ret);
375         if (ret) {
376             *minor_status = ret;
377             free_NegotiationToken(&nt);
378             return GSS_S_FAILURE;
379         }
380     }
381
382     ASN1_MALLOC_ENCODE(NegotiationToken, data.value, data.length, &nt, &size, ret);
383     free_NegotiationToken(&nt);
384     if (ret) {
385         return GSS_S_FAILURE;
386     }
387     if (data.length != size)
388         abort();
389
390     sub = gss_encapsulate_token(&data,
391                                 GSS_SPNEGO_MECHANISM,
392                                 output_token);
393     free (data.value);
394
395     if (sub) {
396         return sub;
397     }
398
399     if (ret_flags)
400         *ret_flags = ctx->mech_flags;
401     if (time_rec)
402         *time_rec = ctx->mech_time_rec;
403
404     ctx->initiator_state = spnego_reply;
405
406     return GSS_S_CONTINUE_NEEDED;
407 }
408
409 /*
410  *
411  */
412
413 static OM_uint32
414 spnego_reply(OM_uint32 * minor_status,
415              gss_const_cred_id_t cred,
416              gssspnego_ctx ctx,
417              gss_const_name_t target_name,
418              gss_const_OID mech_type,
419              OM_uint32 req_flags,
420              OM_uint32 time_req,
421              const gss_channel_bindings_t input_chan_bindings,
422              gss_const_buffer_t input_token,
423              gss_buffer_t output_token,
424              OM_uint32 * ret_flags,
425              OM_uint32 * time_rec)
426 {
427     OM_uint32 ret, minor;
428     NegotiationToken resp;
429     gss_buffer_desc mech_output_token;
430     NegStateEnum negState;
431
432     *minor_status = 0;
433
434     output_token->length = 0;
435     output_token->value  = NULL;
436
437     mech_output_token.length = 0;
438     mech_output_token.value = NULL;
439
440     ret = decode_NegotiationToken(input_token->value, input_token->length,
441                                   &resp, NULL);
442     if (ret)
443       return ret;
444
445     /* The SPNEGO token must be a negTokenResp */
446     if (resp.element != choice_NegotiationToken_negTokenResp) {
447         free_NegotiationToken(&resp);
448         *minor_status = 0;
449         return GSS_S_BAD_MECH;
450     }
451
452     /*
453      * When negState is absent, the actual state should be inferred from
454      * the state of the negotiated mechanism context. (RFC 4178 4.2.2.)
455      */
456     if (resp.u.negTokenResp.negState != NULL)
457         negState = *resp.u.negTokenResp.negState;
458     else
459         negState = accept_incomplete;
460
461     /*
462      * Pick up the mechanism that the acceptor selected, only pick up
463      * the first selection.
464      */
465
466     if (ctx->selected_mech_type == GSS_C_NO_OID && resp.u.negTokenResp.supportedMech) {
467         gss_OID_desc oid;
468         size_t len;
469
470         ctx->flags.seen_supported_mech = 1;
471
472         oid.length = (OM_uint32)der_length_oid(resp.u.negTokenResp.supportedMech);
473         oid.elements = malloc(oid.length);
474         if (oid.elements == NULL) {
475             free_NegotiationToken(&resp);
476             return GSS_S_BAD_MECH;
477         }
478         ret = der_put_oid(((uint8_t *)oid.elements) + oid.length - 1,
479                           oid.length,
480                           resp.u.negTokenResp.supportedMech,
481                           &len);
482         if (ret || len != oid.length) {
483             free(oid.elements);
484             free_NegotiationToken(&resp);
485             return GSS_S_BAD_MECH;
486         }
487
488         if (gss_oid_equal(GSS_SPNEGO_MECHANISM, &oid)) {
489             free(oid.elements);
490             free_NegotiationToken(&resp);
491             return gss_mg_set_error_string(GSS_SPNEGO_MECHANISM,
492                                            GSS_S_BAD_MECH, (*minor_status = EINVAL),
493                                            "SPNEGO acceptor picked SPNEGO??");
494         }
495
496         /* check if the acceptor took our optimistic token */
497         if (gss_oid_equal(ctx->preferred_mech_type, &oid)) {
498             ctx->selected_mech_type = ctx->preferred_mech_type;
499         } else if (gss_oid_equal(ctx->preferred_mech_type, GSS_KRB5_MECHANISM) &&
500                    gss_oid_equal(&oid, &_gss_spnego_mskrb_mechanism_oid_desc)) {
501             /* mis-encoded asn1 type from msft servers */
502             ctx->selected_mech_type = ctx->preferred_mech_type;
503         } else {
504             /* nope, lets start over */
505             gss_delete_sec_context(&minor, &ctx->negotiated_ctx_id,
506                                    GSS_C_NO_BUFFER);
507             ctx->negotiated_ctx_id = GSS_C_NO_CONTEXT;
508
509             if (gss_oid_equal(&oid, GSS_NEGOEX_MECHANISM))
510                 ctx->selected_mech_type = GSS_NEGOEX_MECHANISM;
511             else
512                 ctx->selected_mech_type = _gss_mg_support_mechanism(&oid);
513
514             /* XXX check that server pick a mechanism we proposed */
515             if (ctx->selected_mech_type == GSS_C_NO_OID) {
516                 free(oid.elements);
517                 free_NegotiationToken(&resp);
518                 return gss_mg_set_error_string(GSS_SPNEGO_MECHANISM,
519                                                GSS_S_BAD_MECH, (*minor_status = EINVAL),
520                                                "SPNEGO acceptor sent unsupported supportedMech");
521             }
522         }
523
524         _gss_spnego_log_mech("initiator selected mechanism", ctx->selected_mech_type);
525
526         free(oid.elements);
527
528     } else if (ctx->selected_mech_type == NULL) {
529         free_NegotiationToken(&resp);
530         return gss_mg_set_error_string(GSS_SPNEGO_MECHANISM,
531                                        GSS_S_BAD_MECH, (*minor_status = EINVAL),
532                                        "SPNEGO acceptor didn't send supportedMech");
533     }
534
535     /* if a token (of non zero length) pass to underlaying mech */
536     if ((resp.u.negTokenResp.responseToken != NULL && resp.u.negTokenResp.responseToken->length) ||
537         ctx->negotiated_ctx_id == GSS_C_NO_CONTEXT) {
538         gss_buffer_desc mech_input_token;
539
540         if (resp.u.negTokenResp.responseToken) {
541             mech_input_token.length = resp.u.negTokenResp.responseToken->length;
542             mech_input_token.value  = resp.u.negTokenResp.responseToken->data;
543         } else {
544             mech_input_token.length = 0;
545             mech_input_token.value = NULL;
546         }
547
548         /* Fall through as if the negotiated mechanism
549            was requested explicitly */
550         if (gss_oid_equal(ctx->selected_mech_type, GSS_NEGOEX_MECHANISM)) {
551             ret = _gss_negoex_init(&minor,
552                                    NULL, /* no optimistic token */
553                                    ctx,
554                                    (gss_cred_id_t)cred,
555                                    req_flags,
556                                    time_req,
557                                    input_chan_bindings,
558                                    &mech_input_token,
559                                    &mech_output_token);
560         } else {
561             ret = gss_init_sec_context(&minor,
562                                        cred,
563                                        &ctx->negotiated_ctx_id,
564                                        ctx->target_name,
565                                        ctx->selected_mech_type,
566                                        req_flags,
567                                        time_req,
568                                        input_chan_bindings,
569                                        &mech_input_token,
570                                        &ctx->negotiated_mech_type,
571                                        &mech_output_token,
572                                        &ctx->mech_flags,
573                                        &ctx->mech_time_rec);
574             if (GSS_ERROR(ret)) {
575                 gss_mg_collect_error(ctx->selected_mech_type, ret, minor);
576             }
577         }
578         /*
579          * If the acceptor rejected, we're out even if the inner context is
580          * now complete. Note that the rejection is not integrity-protected.
581          */
582         if (negState == reject)
583             ret = GSS_S_BAD_MECH;
584         if (GSS_ERROR(ret)) {
585             free_NegotiationToken(&resp);
586             *minor_status = minor;
587             return ret;
588         }
589         if (ret == GSS_S_COMPLETE) {
590             ctx->flags.open = 1;
591         }
592     } else if (negState == reject) {
593         free_NegotiationToken(&resp);
594         return gss_mg_set_error_string(GSS_SPNEGO_MECHANISM,
595                                        GSS_S_BAD_MECH, (*minor_status = EPERM),
596                                        "SPNEGO acceptor rejected initiator token");
597     } else if (negState == accept_completed) {
598         /*
599          * Note that the accept_completed isn't integrity-protected, but
600          * ctx->maybe_open can only be true if the inner context is fully
601          * established.
602          */
603         if (ctx->flags.maybe_open)
604             ctx->flags.open = 1;
605
606         if (!ctx->flags.open) {
607             free_NegotiationToken(&resp);
608             return gss_mg_set_error_string(GSS_SPNEGO_MECHANISM,
609                                            GSS_S_BAD_MECH, (*minor_status = EINVAL),
610                                            "SPNEGO acceptor sent acceptor complete, "
611                                            "but we are not complete yet");
612         }
613     }
614
615     if (negState == request_mic) {
616         ctx->flags.peer_require_mic = 1;
617     }
618
619     if (ctx->flags.open && ctx->flags.verified_mic == 0) {
620
621         ctx->flags.require_mic = 1; /* default is to require a MIC */
622         ctx->flags.safe_omit = _gss_spnego_safe_omit_mechlist_mic(ctx);
623         
624         /*
625          * If the peer sent mechListMIC, require it to verify ...
626          */
627         if (resp.u.negTokenResp.mechListMIC) {
628             heim_octet_string *m = resp.u.negTokenResp.mechListMIC;
629
630             /* ...unless its a windows 2000 server that sends the
631              * responseToken inside the mechListMIC too. We only
632              * accept this condition if would have been safe to omit
633              * anyway. */
634
635             if (ctx->flags.safe_omit
636                 && resp.u.negTokenResp.responseToken
637                 && der_heim_octet_string_cmp(m, resp.u.negTokenResp.responseToken) == 0)
638             {
639                 ctx->flags.require_mic = 0;
640             }
641         }
642
643     } else {
644         ctx->flags.require_mic = 0;
645     }
646
647     /*
648      * If we are supposed to check mic and have it, force checking now.
649      */
650
651     if (ctx->flags.require_mic && resp.u.negTokenResp.mechListMIC) {
652
653         ret = _gss_spnego_verify_mechtypes_mic(minor_status, ctx,
654                                                resp.u.negTokenResp.mechListMIC);
655         if (ret) {
656             free_NegotiationToken(&resp);
657             return ret;
658         }
659     }
660
661     /*
662      * Now that underlaying mech is open (conncted), we can figure out
663      * what nexd step to go to.
664      */
665
666     if (ctx->flags.open) {
667
668         if (negState == accept_completed && ctx->flags.safe_omit) {
669             ctx->initiator_state = step_completed;
670             ret = GSS_S_COMPLETE;
671         } else if (ctx->flags.require_mic != 0 && ctx->flags.verified_mic == 0) {
672             ctx->initiator_state = wait_server_mic;
673             ret = GSS_S_CONTINUE_NEEDED;
674         } else {
675             ctx->initiator_state = step_completed;
676             ret = GSS_S_COMPLETE;
677         }
678     }
679
680     if (negState != accept_completed ||
681         ctx->initiator_state != step_completed ||
682         mech_output_token.length)
683     {
684         OM_uint32 ret2;
685         ret2 = make_reply(minor_status, ctx,
686                           &mech_output_token,
687                           output_token);
688         if (ret2)
689             ret = ret2;
690     }
691
692     free_NegotiationToken(&resp);
693
694     gss_release_buffer(&minor, &mech_output_token);
695
696     if (ret_flags)
697         *ret_flags = ctx->mech_flags;
698     if (time_rec)
699         *time_rec = ctx->mech_time_rec;
700
701     return ret;
702 }
703
704 static OM_uint32
705 wait_server_mic(OM_uint32 * minor_status,
706                 gss_const_cred_id_t cred,
707                 gssspnego_ctx ctx,
708                 gss_const_name_t target_name,
709                 gss_const_OID mech_type,
710                 OM_uint32 req_flags,
711                 OM_uint32 time_req,
712                 const gss_channel_bindings_t input_chan_bindings,
713                 gss_const_buffer_t input_token,
714                 gss_buffer_t output_token,
715                 OM_uint32 * ret_flags,
716                 OM_uint32 * time_rec)
717 {
718     OM_uint32 major_status;
719     NegotiationToken resp;
720     int ret;
721
722     ret = decode_NegotiationToken(input_token->value, input_token->length, &resp, NULL);
723     if (ret)
724         return gss_mg_set_error_string(GSS_SPNEGO_MECHANISM,
725                                        GSS_S_BAD_MECH, ret,
726                                        "Failed to decode NegotiationToken");
727
728     if (resp.element != choice_NegotiationToken_negTokenResp
729         || resp.u.negTokenResp.negState == NULL
730         || *resp.u.negTokenResp.negState != accept_completed)
731     {
732         free_NegotiationToken(&resp);
733         return gss_mg_set_error_string(GSS_SPNEGO_MECHANISM,
734                                        GSS_S_BAD_MECH, (*minor_status = EINVAL),
735                                        "NegToken not accept_completed");
736     }
737
738     if (resp.u.negTokenResp.mechListMIC) {
739         major_status = _gss_spnego_verify_mechtypes_mic(minor_status, ctx,
740                                                         resp.u.negTokenResp.mechListMIC);
741     } else if (ctx->flags.safe_omit == 0) {
742         free_NegotiationToken(&resp);
743         return gss_mg_set_error_string(GSS_SPNEGO_MECHANISM,
744                                        GSS_S_BAD_MECH, (*minor_status = EINVAL),
745                                        "Waiting for MIC, but its missing in server request");
746     } else {
747         major_status = GSS_S_COMPLETE;
748     }
749
750     free_NegotiationToken(&resp);
751     if (major_status != GSS_S_COMPLETE)
752         return major_status;
753
754     ctx->flags.verified_mic = 1;
755     ctx->initiator_state = step_completed;
756
757     if (ret_flags)
758         *ret_flags = ctx->mech_flags;
759     if (time_rec)
760         *time_rec = ctx->mech_time_rec;
761
762     *minor_status = 0;
763     return GSS_S_COMPLETE;
764 }
765
766 static OM_uint32
767 step_completed(OM_uint32 * minor_status,
768                gss_const_cred_id_t cred,
769                gssspnego_ctx ctx,
770                gss_const_name_t name,
771                gss_const_OID mech_type,
772                OM_uint32 req_flags,
773                OM_uint32 time_req,
774                const gss_channel_bindings_t input_chan_bindings,
775                gss_const_buffer_t input_token,
776                gss_buffer_t output_token,
777                OM_uint32 * ret_flags,
778                OM_uint32 * time_rec)
779 {
780     return gss_mg_set_error_string(GSS_SPNEGO_MECHANISM,
781                                    GSS_S_BAD_STATUS, (*minor_status = EINVAL),
782                                    "SPNEGO called got ISC call one too many");
783 }
784
785 OM_uint32 GSSAPI_CALLCONV
786 _gss_spnego_init_sec_context(OM_uint32 * minor_status,
787                              gss_const_cred_id_t initiator_cred_handle,
788                              gss_ctx_id_t * context_handle,
789                              gss_const_name_t target_name,
790                              const gss_OID mech_type,
791                              OM_uint32 req_flags,
792                              OM_uint32 time_req,
793                              const gss_channel_bindings_t input_chan_bindings,
794                              const gss_buffer_t input_token,
795                              gss_OID * actual_mech_type,
796                              gss_buffer_t output_token,
797                              OM_uint32 * ret_flags,
798                              OM_uint32 * time_rec)
799 {
800     gssspnego_ctx ctx;
801     OM_uint32 ret;
802
803     if (*context_handle == GSS_C_NO_CONTEXT) {
804         ret = _gss_spnego_alloc_sec_context(minor_status, context_handle);
805         if (GSS_ERROR(ret))
806             return ret;
807
808         ctx = (gssspnego_ctx)*context_handle;
809
810         ctx->initiator_state = spnego_initial;
811     } else {
812         ctx = (gssspnego_ctx)*context_handle;
813     }
814
815
816     HEIMDAL_MUTEX_lock(&ctx->ctx_id_mutex);
817
818     do {
819         ret = ctx->initiator_state(minor_status, initiator_cred_handle, ctx, target_name,
820                                    mech_type, req_flags, time_req, input_chan_bindings, input_token,
821                                    output_token, ret_flags, time_rec);
822
823     } while (ret == GSS_S_COMPLETE &&
824              ctx->initiator_state != step_completed &&
825              output_token->length == 0);
826
827     /* destroy context in case of error */
828     if (GSS_ERROR(ret)) {
829         OM_uint32 junk;
830         _gss_spnego_internal_delete_sec_context(&junk, context_handle, GSS_C_NO_BUFFER);
831     } else {
832
833         HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex);
834
835         if (actual_mech_type)
836             *actual_mech_type = ctx->negotiated_mech_type;
837     }
838
839     return ret;
840 }
841