r19603: Make it easier to control the debug level of smbd.
[samba.git] / source4 / heimdal / lib / gssapi / init_sec_context.c
1 /*
2  * Copyright (c) 1997 - 2003 Kungliga Tekniska Högskolan
3  * (Royal Institute of Technology, Stockholm, Sweden). 
4  * All rights reserved. 
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 "gssapi_locl.h"
35
36 RCSID("$Id: init_sec_context.c,v 1.63 2006/05/05 10:27:13 lha Exp $");
37
38 /*
39  * copy the addresses from `input_chan_bindings' (if any) to
40  * the auth context `ac'
41  */
42
43 static OM_uint32
44 set_addresses (krb5_auth_context ac,
45                const gss_channel_bindings_t input_chan_bindings)               
46 {
47     /* Port numbers are expected to be in application_data.value, 
48      * initator's port first */ 
49
50     krb5_address initiator_addr, acceptor_addr;
51     krb5_error_code kret;
52        
53     if (input_chan_bindings == GSS_C_NO_CHANNEL_BINDINGS
54         || input_chan_bindings->application_data.length !=
55         2 * sizeof(ac->local_port))
56         return 0;
57
58     memset(&initiator_addr, 0, sizeof(initiator_addr));
59     memset(&acceptor_addr, 0, sizeof(acceptor_addr));
60        
61     ac->local_port =
62         *(int16_t *) input_chan_bindings->application_data.value;
63        
64     ac->remote_port =
65         *((int16_t *) input_chan_bindings->application_data.value + 1);
66        
67     kret = gss_address_to_krb5addr(input_chan_bindings->acceptor_addrtype,
68                                    &input_chan_bindings->acceptor_address,
69                                    ac->remote_port,
70                                    &acceptor_addr);
71     if (kret)
72         return kret;
73            
74     kret = gss_address_to_krb5addr(input_chan_bindings->initiator_addrtype,
75                                    &input_chan_bindings->initiator_address,
76                                    ac->local_port,
77                                    &initiator_addr);
78     if (kret) {
79         krb5_free_address (gssapi_krb5_context, &acceptor_addr);
80         return kret;
81     }
82        
83     kret = krb5_auth_con_setaddrs(gssapi_krb5_context,
84                                   ac,
85                                   &initiator_addr,  /* local address */
86                                   &acceptor_addr);  /* remote address */
87        
88     krb5_free_address (gssapi_krb5_context, &initiator_addr);
89     krb5_free_address (gssapi_krb5_context, &acceptor_addr);
90        
91 #if 0
92     free(input_chan_bindings->application_data.value);
93     input_chan_bindings->application_data.value = NULL;
94     input_chan_bindings->application_data.length = 0;
95 #endif
96
97     return kret;
98 }
99
100 OM_uint32
101 _gsskrb5_create_ctx(
102         OM_uint32 * minor_status,
103         gss_ctx_id_t * context_handle,
104         const gss_channel_bindings_t input_chan_bindings,
105         enum gss_ctx_id_t_state state)
106 {
107         krb5_error_code kret;
108
109         *context_handle = malloc(sizeof(**context_handle));
110         if (*context_handle == NULL) {
111                 *minor_status = ENOMEM;
112                 return GSS_S_FAILURE;
113         }
114         (*context_handle)->auth_context = NULL;
115         (*context_handle)->source       = NULL;
116         (*context_handle)->target       = NULL;
117         (*context_handle)->state        = state;
118         (*context_handle)->flags        = 0;
119         (*context_handle)->more_flags   = 0;
120         (*context_handle)->service_keyblock     = NULL;
121         (*context_handle)->ticket       = NULL;
122         krb5_data_zero(&(*context_handle)->fwd_data);
123         (*context_handle)->lifetime     = GSS_C_INDEFINITE;
124         (*context_handle)->order        = NULL;
125         HEIMDAL_MUTEX_init(&(*context_handle)->ctx_id_mutex);
126
127         kret = krb5_auth_con_init (gssapi_krb5_context,
128                                    &(*context_handle)->auth_context);
129         if (kret) {
130                 *minor_status = kret;
131                 gssapi_krb5_set_error_string ();
132
133                 HEIMDAL_MUTEX_destroy(&(*context_handle)->ctx_id_mutex);
134                 
135                 return GSS_S_FAILURE;
136         }
137
138         kret = set_addresses((*context_handle)->auth_context,
139                              input_chan_bindings);
140         if (kret) {
141                 *minor_status = kret;
142
143                 HEIMDAL_MUTEX_destroy(&(*context_handle)->ctx_id_mutex);
144
145                 krb5_auth_con_free(gssapi_krb5_context, (*context_handle)->auth_context);
146
147                 return GSS_S_BAD_BINDINGS;
148         }
149
150         /*
151          * We need a sequence number
152          */
153
154         krb5_auth_con_addflags(gssapi_krb5_context,
155                                (*context_handle)->auth_context,
156                                KRB5_AUTH_CONTEXT_DO_SEQUENCE,
157                                NULL);
158
159         return GSS_S_COMPLETE;
160 }
161
162 static OM_uint32
163 gsskrb5_get_creds(
164         OM_uint32 * minor_status,
165         krb5_ccache ccache,
166         gss_ctx_id_t * context_handle,
167         const gss_name_t target_name,
168         OM_uint32 time_req,
169         OM_uint32 * time_rec,
170         krb5_creds ** cred)
171 {
172         OM_uint32 ret;
173         krb5_error_code kret;
174         krb5_creds this_cred;
175         OM_uint32 lifetime_rec;
176
177         *cred = NULL;
178
179         kret = krb5_cc_get_principal(gssapi_krb5_context,
180                                      ccache,
181                                      &(*context_handle)->source);
182         if (kret) {
183                 gssapi_krb5_set_error_string ();
184                 *minor_status = kret;
185                 return GSS_S_FAILURE;
186         }
187
188         kret = krb5_copy_principal(gssapi_krb5_context,
189                                    target_name,
190                                    &(*context_handle)->target);
191         if (kret) {
192                 gssapi_krb5_set_error_string ();
193                 *minor_status = kret;
194                 return GSS_S_FAILURE;
195         }
196
197         memset(&this_cred, 0, sizeof(this_cred));
198         this_cred.client = (*context_handle)->source;
199         this_cred.server = (*context_handle)->target;
200
201         if (time_req && time_req != GSS_C_INDEFINITE) {
202                 krb5_timestamp ts;
203
204                 krb5_timeofday (gssapi_krb5_context, &ts);
205                 this_cred.times.endtime = ts + time_req;
206         } else {
207                 this_cred.times.endtime   = 0;
208         }
209
210         this_cred.session.keytype = KEYTYPE_NULL;
211
212         kret = krb5_get_credentials(gssapi_krb5_context,
213                                     0,
214                                     ccache,
215                                     &this_cred,
216                                     cred);
217         if (kret) {
218                 gssapi_krb5_set_error_string ();
219                 *minor_status = kret;
220                 return GSS_S_FAILURE;
221         }
222
223         (*context_handle)->lifetime = (*cred)->times.endtime;
224
225         ret = gssapi_lifetime_left(minor_status,
226                                    (*context_handle)->lifetime,
227                                    &lifetime_rec);
228         if (ret) return ret;
229
230         if (lifetime_rec == 0) {
231                 *minor_status = 0;
232                 return GSS_S_CONTEXT_EXPIRED;
233         }
234
235         if (time_rec) *time_rec = lifetime_rec;
236
237         return GSS_S_COMPLETE;
238 }
239
240 static OM_uint32
241 gsskrb5_initiator_ready(
242         OM_uint32 * minor_status,
243         gss_ctx_id_t * context_handle)
244 {
245         OM_uint32 ret;
246         int32_t seq_number;
247         int is_cfx = 0;
248         OM_uint32 flags = (*context_handle)->flags;
249
250         krb5_auth_getremoteseqnumber (gssapi_krb5_context,
251                                       (*context_handle)->auth_context,
252                                       &seq_number);
253
254         gsskrb5_is_cfx(*context_handle, &is_cfx);
255
256         ret = _gssapi_msg_order_create(minor_status,
257                                        &(*context_handle)->order,
258                                        _gssapi_msg_order_f(flags),
259                                        seq_number, 0, is_cfx);
260         if (ret) return ret;
261
262         (*context_handle)->state        = INITIATOR_READY;
263         (*context_handle)->more_flags   |= OPEN;
264
265         return GSS_S_COMPLETE;
266 }
267
268 /*
269  * handle delegated creds in init-sec-context
270  */
271
272 static void
273 do_delegation (krb5_auth_context ac,
274                krb5_ccache ccache,
275                krb5_creds *cred,
276                const gss_name_t target_name,
277                krb5_data *fwd_data,
278                OM_uint32 *flags)
279 {
280     krb5_creds creds;
281     krb5_kdc_flags fwd_flags;
282     krb5_error_code kret;
283        
284     memset (&creds, 0, sizeof(creds));
285     krb5_data_zero (fwd_data);
286        
287     kret = krb5_cc_get_principal(gssapi_krb5_context, ccache, &creds.client);
288     if (kret) 
289         goto out;
290        
291     kret = krb5_build_principal(gssapi_krb5_context,
292                                 &creds.server,
293                                 strlen(creds.client->realm),
294                                 creds.client->realm,
295                                 KRB5_TGS_NAME,
296                                 creds.client->realm,
297                                 NULL);
298     if (kret)
299         goto out; 
300        
301     creds.times.endtime = 0;
302        
303     fwd_flags.i = 0;
304     fwd_flags.b.forwarded = 1;
305     fwd_flags.b.forwardable = 1;
306        
307     if ( /*target_name->name.name_type != KRB5_NT_SRV_HST ||*/
308         target_name->name.name_string.len < 2) 
309         goto out;
310        
311     kret = krb5_get_forwarded_creds(gssapi_krb5_context,
312                                     ac,
313                                     ccache,
314                                     fwd_flags.i,
315                                     target_name->name.name_string.val[1],
316                                     &creds,
317                                     fwd_data);
318        
319  out:
320     if (kret)
321         *flags &= ~GSS_C_DELEG_FLAG;
322     else
323         *flags |= GSS_C_DELEG_FLAG;
324        
325     if (creds.client)
326         krb5_free_principal(gssapi_krb5_context, creds.client);
327     if (creds.server)
328         krb5_free_principal(gssapi_krb5_context, creds.server);
329 }
330
331 /*
332  * first stage of init-sec-context
333  */
334
335 static OM_uint32
336 gsskrb5_initiator_start
337 (OM_uint32 * minor_status,
338  krb5_ccache ccache,
339  gss_ctx_id_t * context_handle,
340  const gss_name_t target_name,
341  const gss_OID mech_type,
342  OM_uint32 req_flags,
343  OM_uint32 time_req,
344  const gss_channel_bindings_t input_chan_bindings,
345  const gss_buffer_t input_token,
346  gss_buffer_t output_token,
347  OM_uint32 * ret_flags,
348  OM_uint32 * time_rec
349     )
350 {
351     OM_uint32 ret = GSS_S_FAILURE;
352     krb5_error_code kret;
353     krb5_flags ap_options;
354     krb5_creds *cred = NULL;
355     krb5_data outbuf;
356     OM_uint32 flags;
357     krb5_data authenticator;
358     Checksum cksum;
359     krb5_enctype enctype;
360     krb5_data fwd_data;
361     int is_cfx;
362
363     krb5_data_zero(&outbuf);
364     krb5_data_zero(&fwd_data);
365
366         (*context_handle)->more_flags |= LOCAL;
367
368         /* We need to get the credentials for the requested target */
369         ret = gsskrb5_get_creds(minor_status,
370                                 ccache, 
371                                 context_handle,
372                                 target_name,
373                                 time_req,
374                                 time_rec,
375                                 &cred);
376         if (ret) return ret;
377
378         /*
379          * We need to setup some compat stuff, this assumes that context_handle->target is already set
380          */
381         ret = _gss_DES3_get_mic_compat(minor_status, *context_handle);
382         if (ret) return ret;
383
384         /* We need the key and a random local subkey */
385         {
386                 kret = krb5_auth_con_setkey(gssapi_krb5_context, 
387                              (*context_handle)->auth_context, 
388                              &cred->session);
389                 if (kret) {
390                         gssapi_krb5_set_error_string ();
391                         *minor_status = kret;
392                         return GSS_S_FAILURE;
393                 }
394
395                 kret = krb5_auth_con_generatelocalsubkey(gssapi_krb5_context, 
396                                                          (*context_handle)->auth_context,
397                                                          &cred->session);
398                 if (kret) {
399                         gssapi_krb5_set_error_string ();
400                         *minor_status = kret;
401                         return GSS_S_FAILURE;
402                 }
403         }
404
405         /* We need to prepare the flags used for this context */
406         {
407                 flags = 0;
408                 ap_options = 0;
409
410                 /* 
411                  * The KDC may have issued us a service ticket marked NOT
412                  * ok-as-delegate.  We may still wish to force the matter, and to
413                  * allow this we check a per-realm gssapi [appdefaults] config
414                  * option.  If ok-as-delegate in the config file is set to TRUE
415                  * (default FALSE) and our caller has so requested, we will still
416                  * attempt to forward the ticket.
417                  *
418                  * Otherwise, strip the GSS_C_DELEG_FLAG (so we don't attempt a
419                  * delegation)
420                  */
421                 if (!cred->flags.b.ok_as_delegate) {
422                         krb5_boolean delegate;
423                         
424                         krb5_appdefault_boolean(gssapi_krb5_context,
425                                                 "gssapi", target_name->realm,
426                                                 "ok-as-delegate", FALSE, &delegate);
427                         if (!delegate)
428                                 req_flags &= ~GSS_C_DELEG_FLAG;
429                 }
430
431                 if (req_flags & GSS_C_DELEG_FLAG) {
432                         do_delegation((*context_handle)->auth_context,
433                                       ccache, cred, target_name, &fwd_data, &flags);
434                 }
435
436                 if (req_flags & GSS_C_MUTUAL_FLAG) {
437                         flags |= GSS_C_MUTUAL_FLAG;
438                         ap_options |= AP_OPTS_MUTUAL_REQUIRED;
439                 }
440     
441                 if (req_flags & GSS_C_REPLAY_FLAG) {
442                         flags |= GSS_C_REPLAY_FLAG;
443                 }
444
445                 if (req_flags & GSS_C_SEQUENCE_FLAG) {
446                         flags |= GSS_C_SEQUENCE_FLAG;
447                 }
448
449                 if (req_flags & GSS_C_ANON_FLAG) {
450                         ;/* XXX */
451                 }
452
453                 if (req_flags & GSS_C_DCE_STYLE) {
454                         flags |= GSS_C_DCE_STYLE;
455                         /* GSS_C_DCE_STYLE implies GSS_C_MUTUAL_FLAG */
456                         flags |= GSS_C_MUTUAL_FLAG;
457                         ap_options |= AP_OPTS_MUTUAL_REQUIRED;
458                 }
459
460                 if (req_flags & GSS_C_IDENTIFY_FLAG) {
461                         flags |= GSS_C_IDENTIFY_FLAG;
462                 }
463
464                 if (req_flags & GSS_C_EXTENDED_ERROR_FLAG) {
465                         flags |= GSS_C_EXTENDED_ERROR_FLAG;
466                 }
467
468                 /* TODO: why are this always there? --metze */
469                 flags |= GSS_C_CONF_FLAG;
470                 flags |= GSS_C_INTEG_FLAG;
471                 flags |= GSS_C_TRANS_FLAG;
472
473                 if (ret_flags) *ret_flags = flags;
474                 (*context_handle)->flags = flags;
475         }
476
477         /* We need to generate the 8003 checksum */
478         {
479                 ret = gssapi_krb5_create_8003_checksum(minor_status,
480                                                        input_chan_bindings,
481                                                        flags,
482                                                        &fwd_data,
483                                                        &cksum);
484                 krb5_data_free (&fwd_data);
485                 if (ret) return ret;
486         }
487
488         enctype = (*context_handle)->auth_context->keyblock->keytype;
489
490         gsskrb5_is_cfx(*context_handle, &is_cfx);
491         
492         if (is_cfx != 0) {
493                 kret = krb5_auth_con_addflags(gssapi_krb5_context,
494                                               (*context_handle)->auth_context,
495                                               KRB5_AUTH_CONTEXT_USE_SUBKEY,
496                                               NULL);
497                 (*context_handle)->more_flags |= ACCEPTOR_SUBKEY;
498         }
499             
500         /* We need to create an Authenticator */
501         {
502                 kret = krb5_build_authenticator (gssapi_krb5_context,
503                                      (*context_handle)->auth_context,
504                                      enctype,
505                                      cred,
506                                      &cksum,
507                                      NULL,
508                                      &authenticator,
509                                      KRB5_KU_AP_REQ_AUTH);
510                 free_Checksum(&cksum);
511                 if (kret) {
512                         gssapi_krb5_set_error_string ();
513                         *minor_status = kret;
514                         return GSS_S_FAILURE;
515                 }
516         }
517
518         /* We need to create the AP_REQ */
519         {
520                 kret = krb5_build_ap_req(gssapi_krb5_context,
521                                          enctype,
522                                          cred,
523                                          ap_options,
524                                          authenticator,
525                                          &outbuf);
526                 if (kret) {
527                         gssapi_krb5_set_error_string ();
528                         *minor_status = kret;
529                         return GSS_S_FAILURE;
530                 }
531         }
532
533         /* We need to encapsulate the AP_REQ if GSS_C_DCE_STYLE isn't in use */
534         {
535                 if (!(flags & GSS_C_DCE_STYLE)) {
536                         ret = gssapi_krb5_encapsulate(minor_status, &outbuf, output_token,
537                                                       "\x01\x00", GSS_KRB5_MECHANISM);
538                         krb5_data_free (&outbuf);
539                         if (ret) return ret;
540                 } else {
541                         output_token->length = outbuf.length;
542                         output_token->value  = outbuf.data;
543                 }
544         }
545
546         /* We no longer need the creds */
547         krb5_free_creds(gssapi_krb5_context, cred);
548
549         /* We are done if GSS_C_MUTUAL_FLAG is in use */
550         if (flags & GSS_C_MUTUAL_FLAG) {
551                 (*context_handle)->state = INITIATOR_WAIT_FOR_MUTAL;
552                 return GSS_S_CONTINUE_NEEDED;
553         }
554
555         return gsskrb5_initiator_ready(minor_status, context_handle);
556 }
557
558 static OM_uint32
559 gsskrb5_initiator_wait_for_mutual(
560         OM_uint32 * minor_status,
561         krb5_ccache ccache,
562         gss_ctx_id_t * context_handle,
563         const gss_name_t target_name,
564         const gss_OID mech_type,
565         OM_uint32 req_flags,
566         OM_uint32 time_req,
567         const gss_channel_bindings_t input_chan_bindings,
568         const gss_buffer_t input_token,
569         gss_buffer_t output_token,
570         OM_uint32 * ret_flags,
571         OM_uint32 * time_rec)
572 {
573         OM_uint32 ret;
574         krb5_error_code kret;
575         krb5_data inbuf;
576         OM_uint32 flags = (*context_handle)->flags;
577         int32_t l_seq_number;
578         int32_t r_seq_number;
579         
580         /* We need to decapsulate the AP_REP if GSS_C_DCE_STYLE isn't in use */
581         {
582                 if (!(flags & GSS_C_DCE_STYLE)) {
583                         ret = gssapi_krb5_decapsulate(minor_status, input_token, &inbuf,
584                                                       "\x02\x00", GSS_KRB5_MECHANISM);
585                         if (ret) return ret;
586                 } else {
587                         inbuf.length    = input_token->length;
588                         inbuf.data      = input_token->value;
589                 }
590         }
591
592         /* We need to verify the AP_REP */ 
593         {
594                 krb5_ap_rep_enc_part *repl;
595
596                 kret = krb5_rd_rep(gssapi_krb5_context,
597                                    (*context_handle)->auth_context,
598                                    &inbuf,
599                                    &repl);
600                 if (kret) {
601                         gssapi_krb5_set_error_string ();
602                         *minor_status = kret;
603                         return GSS_S_FAILURE;
604                 }
605                 krb5_free_ap_rep_enc_part(gssapi_krb5_context, repl);
606         }
607
608         /* We need to check the liftime */
609         {
610                 OM_uint32 lifetime_rec;
611
612                 ret = gssapi_lifetime_left(minor_status,
613                                            (*context_handle)->lifetime,
614                                            &lifetime_rec);
615                 if (ret) return ret;
616
617                 if (lifetime_rec == 0) {
618                         return GSS_S_CONTEXT_EXPIRED;
619                 }
620         
621                 if (time_rec) *time_rec = lifetime_rec;
622         }
623
624         /* We need to give the caller the flags which are in use */
625         if (ret_flags) *ret_flags = (*context_handle)->flags;
626
627         /* We are done here if GSS_C_DCE_STYLE isn't in use */
628         if (!(flags & GSS_C_DCE_STYLE)) {
629                 return gsskrb5_initiator_ready(minor_status, context_handle);
630         }
631
632         /* 
633          * We need to set the local seq_number to the remote one just for the krb5_mk_rep(),
634          * and then we need to use the old local seq_number again for the GSS_Wrap() messages
635          */
636         {
637                 kret = krb5_auth_getremoteseqnumber(gssapi_krb5_context,
638                                                     (*context_handle)->auth_context,
639                                                     &r_seq_number);
640                 if (kret) {
641                         gssapi_krb5_set_error_string ();
642                         *minor_status = kret;
643                         return GSS_S_FAILURE;
644                 }
645
646                 kret = krb5_auth_con_getlocalseqnumber(gssapi_krb5_context,
647                                                     (*context_handle)->auth_context,
648                                                     &l_seq_number);
649                 if (kret) {
650                         gssapi_krb5_set_error_string ();
651                         *minor_status = kret;
652                         return GSS_S_FAILURE;
653                 }
654
655                 kret = krb5_auth_con_setlocalseqnumber(gssapi_krb5_context,
656                                                        (*context_handle)->auth_context,
657                                                        r_seq_number);   
658                 if (kret) {
659                         gssapi_krb5_set_error_string ();
660                         *minor_status = kret;
661                         return GSS_S_FAILURE;
662                 }
663         }
664
665         /* We need to create an AP_REP */ 
666         {
667                 krb5_data outbuf;
668
669                 kret = krb5_mk_rep(gssapi_krb5_context,
670                                    (*context_handle)->auth_context,
671                                    &outbuf);
672                 if (kret) {
673                         gssapi_krb5_set_error_string ();
674                         *minor_status = kret;
675                         return GSS_S_FAILURE;
676                 }
677
678                 output_token->length = outbuf.length;
679                 output_token->value  = outbuf.data;
680         }
681
682         /* We need to reset the local seq_number */
683         {
684                 kret = krb5_auth_con_setlocalseqnumber(gssapi_krb5_context,
685                                                        (*context_handle)->auth_context,
686                                                        l_seq_number);   
687                 if (kret) {
688                         gssapi_krb5_set_error_string ();
689                         *minor_status = kret;
690                         return GSS_S_FAILURE;
691                 }       
692         }
693
694         return gsskrb5_initiator_ready(minor_status, context_handle);
695 }
696
697 static OM_uint32
698 gsskrb5_init_sec_context
699            (OM_uint32 * minor_status,
700             const gss_cred_id_t initiator_cred_handle,
701             gss_ctx_id_t * context_handle,
702             const gss_name_t target_name,
703             const gss_OID mech_type,
704             OM_uint32 req_flags,
705             OM_uint32 time_req,
706             const gss_channel_bindings_t input_chan_bindings,
707             const gss_buffer_t input_token,
708             gss_OID * actual_mech_type,
709             gss_buffer_t output_token,
710             OM_uint32 * ret_flags,
711             OM_uint32 * time_rec
712            )
713 {
714         OM_uint32 ret;
715         krb5_error_code kret;
716         krb5_ccache ccache = NULL;
717
718         if (*context_handle == GSS_C_NO_CONTEXT) {
719                 ret = _gsskrb5_create_ctx(minor_status,
720                                           context_handle,
721                                           input_chan_bindings,
722                                           INITIATOR_START);
723                 if (ret) return ret;
724         }
725
726         if (actual_mech_type) *actual_mech_type = GSS_KRB5_MECHANISM;
727
728         if (initiator_cred_handle == GSS_C_NO_CREDENTIAL) {
729                 kret = krb5_cc_default (gssapi_krb5_context, &ccache);
730                 if (kret) {
731                         gssapi_krb5_set_error_string ();
732                         *minor_status = kret;
733                         return GSS_S_FAILURE;
734                 }
735         } else {
736                 ccache = initiator_cred_handle->ccache;
737         }
738
739         HEIMDAL_MUTEX_lock(&(*context_handle)->ctx_id_mutex);
740
741         switch ((*context_handle)->state) {
742         case INITIATOR_START:
743                 ret = gsskrb5_initiator_start(minor_status,
744                                               ccache,
745                                               context_handle,
746                                               target_name,
747                                               mech_type,
748                                               req_flags,
749                                               time_req,
750                                               input_chan_bindings,
751                                               input_token,
752                                               output_token,
753                                               ret_flags,
754                                               time_rec);
755                 break;
756         case INITIATOR_WAIT_FOR_MUTAL:
757                 ret = gsskrb5_initiator_wait_for_mutual(minor_status,
758                                                         ccache,
759                                                         context_handle,
760                                                         target_name,
761                                                         mech_type,
762                                                         req_flags,
763                                                         time_req,
764                                                         input_chan_bindings,
765                                                         input_token,
766                                                         output_token,
767                                                         ret_flags,
768                                                         time_rec);
769                 break;
770         case INITIATOR_READY:
771                 /* should this be GSS_S_BAD_STATUS ? --metze */
772
773                 /* We need to check the liftime */
774                 {
775                         OM_uint32 lifetime_rec;
776
777                         ret = gssapi_lifetime_left(minor_status,
778                                                    (*context_handle)->lifetime,
779                                                    &lifetime_rec);
780                         if (ret) break;
781
782                         if (lifetime_rec == 0) {
783                                 *minor_status = 0;
784                                 ret = GSS_S_CONTEXT_EXPIRED;
785                                 break;
786                         }
787
788                         if (time_rec) *time_rec = lifetime_rec;
789                 }
790
791                 /* We need to give the caller the flags which are in use */
792                 if (ret_flags) *ret_flags = (*context_handle)->flags;
793
794                 ret = GSS_S_COMPLETE;
795                 break;
796         default:
797                 /* TODO: is this correct here? --metze */
798                 ret =  GSS_S_BAD_STATUS;
799                 break;
800         }
801
802         if (initiator_cred_handle == GSS_C_NO_CREDENTIAL) {
803                 krb5_cc_close(gssapi_krb5_context, ccache);
804         }
805
806         HEIMDAL_MUTEX_unlock(&(*context_handle)->ctx_id_mutex);
807
808         return ret;
809 }
810
811 static OM_uint32
812 spnego_reply
813            (OM_uint32 * minor_status,
814             const gss_cred_id_t initiator_cred_handle,
815             gss_ctx_id_t * context_handle,
816             const gss_name_t target_name,
817             const gss_OID mech_type,
818             OM_uint32 req_flags,
819             OM_uint32 time_req,
820             const gss_channel_bindings_t input_chan_bindings,
821             const gss_buffer_t input_token,
822             gss_OID * actual_mech_type,
823             gss_buffer_t output_token,
824             OM_uint32 * ret_flags,
825             OM_uint32 * time_rec
826     )
827 {
828     OM_uint32 ret;
829     krb5_data indata;
830     NegTokenTarg targ;
831     u_char oidbuf[17];
832     size_t oidlen;
833     gss_buffer_desc sub_token;
834     ssize_t mech_len;
835     const u_char *p;
836     size_t len, taglen;
837     krb5_boolean require_mic;
838
839     output_token->length = 0;
840     output_token->value  = NULL;
841
842     /*
843      * SPNEGO doesn't include gss wrapping on SubsequentContextToken
844      * like the Kerberos 5 mech does. But lets check for it anyway.
845      */
846     
847     mech_len = gssapi_krb5_get_mech (input_token->value,
848                                      input_token->length,
849                                      &p);
850
851     if (mech_len < 0) {
852         indata.data = input_token->value;
853         indata.length = input_token->length;
854     } else if (mech_len == GSS_KRB5_MECHANISM->length
855         && memcmp(GSS_KRB5_MECHANISM->elements, p, mech_len) == 0)
856         return gsskrb5_init_sec_context (minor_status,
857                                          initiator_cred_handle,
858                                          context_handle,
859                                          target_name,
860                                          GSS_KRB5_MECHANISM,
861                                          req_flags,
862                                          time_req,
863                                          input_chan_bindings,
864                                          input_token,
865                                          actual_mech_type,
866                                          output_token,
867                                          ret_flags,
868                                          time_rec);
869     else if (mech_len == GSS_SPNEGO_MECHANISM->length
870              && memcmp(GSS_SPNEGO_MECHANISM->elements, p, mech_len) == 0){
871         ret = _gssapi_decapsulate (minor_status,
872                                    input_token,
873                                    &indata,
874                                    GSS_SPNEGO_MECHANISM);
875         if (ret)
876             return ret;
877     } else
878         return GSS_S_BAD_MECH;
879
880     ret = der_match_tag_and_length((const char *)indata.data,
881                                    indata.length,
882                                    ASN1_C_CONTEXT, CONS, 1, &len, &taglen);
883     if (ret) {
884         gssapi_krb5_set_status("Failed to decode NegToken choice");
885         *minor_status = ret;
886         return GSS_S_FAILURE;
887     }
888
889     if(len > indata.length - taglen) {
890         gssapi_krb5_set_status("Buffer overrun in NegToken choice");
891         *minor_status = ASN1_OVERRUN;
892         return GSS_S_FAILURE;
893     }
894
895     ret = decode_NegTokenTarg((const char *)indata.data + taglen, 
896                               len, &targ, NULL);
897     if (ret) {
898         gssapi_krb5_set_status("Failed to decode NegTokenTarg");
899         *minor_status = ret;
900         return GSS_S_FAILURE;
901     }
902
903     if (targ.negResult == NULL
904         || *(targ.negResult) == reject
905         || targ.supportedMech == NULL) {
906         free_NegTokenTarg(&targ);
907         return GSS_S_BAD_MECH;
908     }
909     
910     ret = der_put_oid(oidbuf + sizeof(oidbuf) - 1,
911                       sizeof(oidbuf),
912                       targ.supportedMech,
913                       &oidlen);
914     if (ret || oidlen != GSS_KRB5_MECHANISM->length
915         || memcmp(oidbuf + sizeof(oidbuf) - oidlen,
916                   GSS_KRB5_MECHANISM->elements,
917                   oidlen) != 0) {
918         free_NegTokenTarg(&targ);
919         return GSS_S_BAD_MECH;
920     }
921
922     if (targ.responseToken != NULL) {
923         sub_token.length = targ.responseToken->length;
924         sub_token.value  = targ.responseToken->data;
925     } else {
926         sub_token.length = 0;
927         sub_token.value  = NULL;
928     }
929
930     ret = gsskrb5_init_sec_context(minor_status,
931                                    initiator_cred_handle,
932                                    context_handle,
933                                    target_name,
934                                    GSS_KRB5_MECHANISM,
935                                    req_flags,
936                                    time_req,
937                                    input_chan_bindings,
938                                    &sub_token,
939                                    actual_mech_type,
940                                    output_token,
941                                    ret_flags,
942                                    time_rec);
943     if (ret) {
944         free_NegTokenTarg(&targ);
945         return ret;
946     }
947
948     /*
949      * Verify the mechListMIC if CFX was used; or if local policy
950      * dictated so.
951      */
952     ret = _gss_spnego_require_mechlist_mic(minor_status, *context_handle,
953                                            &require_mic);
954     if (ret) {
955         free_NegTokenTarg(&targ);
956         return ret;
957     }
958
959     if (require_mic) {
960         MechTypeList mechlist;
961         MechType m0;
962         size_t buf_len;
963         gss_buffer_desc mic_buf, mech_buf;
964
965         if (targ.mechListMIC == NULL) {
966             free_NegTokenTarg(&targ);
967             *minor_status = 0;
968             return GSS_S_BAD_MIC;
969         }
970
971         mechlist.len = 1;
972         mechlist.val = &m0;
973
974         ret = der_get_oid(GSS_KRB5_MECHANISM->elements,
975                           GSS_KRB5_MECHANISM->length,
976                           &m0,
977                           NULL);
978         if (ret) {
979             free_NegTokenTarg(&targ);
980             *minor_status = ENOMEM;
981             return GSS_S_FAILURE;
982         }
983
984         ASN1_MALLOC_ENCODE(MechTypeList, mech_buf.value, mech_buf.length,
985                            &mechlist, &buf_len, ret);
986         if (ret) {
987             free_NegTokenTarg(&targ);
988             free_oid(&m0);
989             *minor_status = ENOMEM;
990             return GSS_S_FAILURE;
991         }
992         if (mech_buf.length != buf_len)
993             abort();
994
995         mic_buf.length = targ.mechListMIC->length;
996         mic_buf.value  = targ.mechListMIC->data;
997
998         ret = gss_verify_mic(minor_status, *context_handle,
999                              &mech_buf, &mic_buf, NULL);
1000         free(mech_buf.value);
1001         free_oid(&m0);
1002     }
1003     free_NegTokenTarg(&targ);
1004     return ret;
1005 }
1006
1007 static OM_uint32
1008 spnego_initial
1009            (OM_uint32 * minor_status,
1010             const gss_cred_id_t initiator_cred_handle,
1011             gss_ctx_id_t * context_handle,
1012             const gss_name_t target_name,
1013             const gss_OID mech_type,
1014             OM_uint32 req_flags,
1015             OM_uint32 time_req,
1016             const gss_channel_bindings_t input_chan_bindings,
1017             const gss_buffer_t input_token,
1018             gss_OID * actual_mech_type,
1019             gss_buffer_t output_token,
1020             OM_uint32 * ret_flags,
1021             OM_uint32 * time_rec
1022            )
1023 {
1024     NegTokenInit ni;
1025     int ret;
1026     OM_uint32 sub, minor;
1027     gss_buffer_desc mech_token;
1028     u_char *buf;
1029     size_t buf_size, buf_len;
1030     krb5_data data;
1031
1032     memset (&ni, 0, sizeof(ni));
1033
1034     ALLOC(ni.mechTypes, 1);
1035     if (ni.mechTypes == NULL) {
1036         *minor_status = ENOMEM;
1037         return GSS_S_FAILURE;
1038     }
1039     ALLOC_SEQ(ni.mechTypes, 1);
1040     if (ni.mechTypes->val == NULL) {
1041         free_NegTokenInit(&ni);
1042         *minor_status = ENOMEM;
1043         return GSS_S_FAILURE;
1044     }
1045     ret = der_get_oid(GSS_KRB5_MECHANISM->elements,
1046                       GSS_KRB5_MECHANISM->length,
1047                       &ni.mechTypes->val[0],
1048                       NULL);
1049     if (ret) {
1050         free_NegTokenInit(&ni);
1051         *minor_status = ENOMEM;
1052         return GSS_S_FAILURE;
1053     }
1054
1055 #if 0
1056     ALLOC(ni.reqFlags, 1);
1057     if (ni.reqFlags == NULL) {
1058         free_NegTokenInit(&ni);
1059         *minor_status = ENOMEM;
1060         return GSS_S_FAILURE;
1061     }
1062     ni.reqFlags->delegFlag    = req_flags & GSS_C_DELEG_FLAG;
1063     ni.reqFlags->mutualFlag   = req_flags & GSS_C_MUTUAL_FLAG;
1064     ni.reqFlags->replayFlag   = req_flags & GSS_C_REPLAY_FLAG;
1065     ni.reqFlags->sequenceFlag = req_flags & GSS_C_SEQUENCE_FLAG;
1066     ni.reqFlags->anonFlag     = req_flags & GSS_C_ANON_FLAG;
1067     ni.reqFlags->confFlag     = req_flags & GSS_C_CONF_FLAG;
1068     ni.reqFlags->integFlag    = req_flags & GSS_C_INTEG_FLAG;
1069 #else
1070     ni.reqFlags = NULL;
1071 #endif
1072
1073     sub = gsskrb5_init_sec_context(&minor,
1074                                    initiator_cred_handle,
1075                                    context_handle,
1076                                    target_name,
1077                                    GSS_KRB5_MECHANISM,
1078                                    req_flags,
1079                                    time_req,
1080                                    input_chan_bindings,
1081                                    GSS_C_NO_BUFFER,
1082                                    actual_mech_type,
1083                                    &mech_token,
1084                                    ret_flags,
1085                                    time_rec);
1086     if (GSS_ERROR(sub)) {
1087         free_NegTokenInit(&ni);
1088         return sub;
1089     }
1090     if (mech_token.length != 0) {
1091         ALLOC(ni.mechToken, 1);
1092         if (ni.mechToken == NULL) {
1093             free_NegTokenInit(&ni);
1094             gss_release_buffer(&minor, &mech_token);
1095             *minor_status = ENOMEM;
1096             return GSS_S_FAILURE;
1097         }
1098         ni.mechToken->length = mech_token.length;
1099         ni.mechToken->data = malloc(mech_token.length);
1100         if (ni.mechToken->data == NULL && mech_token.length != 0) {
1101             free_NegTokenInit(&ni);
1102             gss_release_buffer(&minor, &mech_token);
1103             *minor_status = ENOMEM;
1104             return GSS_S_FAILURE;
1105         }
1106         memcpy(ni.mechToken->data, mech_token.value, mech_token.length);
1107         gss_release_buffer(&minor, &mech_token);
1108     } else
1109         ni.mechToken = NULL;
1110
1111     /* XXX ignore mech list mic for now */
1112     ni.mechListMIC = NULL;
1113
1114
1115     {
1116         NegotiationToken nt;
1117
1118         nt.element = choice_NegotiationToken_negTokenInit;
1119         nt.u.negTokenInit = ni;
1120
1121         ASN1_MALLOC_ENCODE(NegotiationToken, buf, buf_size,
1122                            &nt, &buf_len, ret);
1123         if (ret == 0 && buf_size != buf_len)
1124             abort();
1125     }
1126
1127     data.data   = buf;
1128     data.length = buf_size;
1129
1130     free_NegTokenInit(&ni);
1131     if (ret)
1132         return ret;
1133
1134     sub = _gssapi_encapsulate(minor_status,
1135                               &data,
1136                               output_token,
1137                               GSS_SPNEGO_MECHANISM);
1138     free (buf);
1139
1140     if (sub)
1141         return sub;
1142
1143     return GSS_S_CONTINUE_NEEDED;
1144 }
1145
1146 static OM_uint32
1147 spnego_init_sec_context
1148            (OM_uint32 * minor_status,
1149             const gss_cred_id_t initiator_cred_handle,
1150             gss_ctx_id_t * context_handle,
1151             const gss_name_t target_name,
1152             const gss_OID mech_type,
1153             OM_uint32 req_flags,
1154             OM_uint32 time_req,
1155             const gss_channel_bindings_t input_chan_bindings,
1156             const gss_buffer_t input_token,
1157             gss_OID * actual_mech_type,
1158             gss_buffer_t output_token,
1159             OM_uint32 * ret_flags,
1160             OM_uint32 * time_rec
1161            )
1162 {
1163     if (input_token == GSS_C_NO_BUFFER || input_token->length == 0)
1164         return spnego_initial (minor_status,
1165                                initiator_cred_handle,
1166                                context_handle,
1167                                target_name,
1168                                mech_type,
1169                                req_flags,
1170                                time_req,
1171                                input_chan_bindings,
1172                                input_token,
1173                                actual_mech_type,
1174                                output_token,
1175                                ret_flags,
1176                                time_rec);
1177     else
1178         return spnego_reply (minor_status,
1179                              initiator_cred_handle,
1180                              context_handle,
1181                              target_name,
1182                              mech_type,
1183                              req_flags,
1184                              time_req,
1185                              input_chan_bindings,
1186                              input_token,
1187                              actual_mech_type,
1188                              output_token,
1189                              ret_flags,
1190                              time_rec);
1191 }
1192
1193 /*
1194  * gss_init_sec_context
1195  */
1196
1197 OM_uint32 gss_init_sec_context
1198            (OM_uint32 * minor_status,
1199             const gss_cred_id_t initiator_cred_handle,
1200             gss_ctx_id_t * context_handle,
1201             const gss_name_t target_name,
1202             const gss_OID mech_type,
1203             OM_uint32 req_flags,
1204             OM_uint32 time_req,
1205             const gss_channel_bindings_t input_chan_bindings,
1206             const gss_buffer_t input_token,
1207             gss_OID * actual_mech_type,
1208             gss_buffer_t output_token,
1209             OM_uint32 * ret_flags,
1210             OM_uint32 * time_rec
1211            )
1212 {
1213     GSSAPI_KRB5_INIT ();
1214
1215     output_token->length = 0;
1216     output_token->value  = NULL;
1217
1218     if (ret_flags)
1219         *ret_flags = 0;
1220     if (time_rec)
1221         *time_rec = 0;
1222
1223     if (target_name == GSS_C_NO_NAME) {
1224         if (actual_mech_type)
1225             *actual_mech_type = GSS_C_NO_OID;
1226         *minor_status = 0;
1227         return GSS_S_BAD_NAME;
1228     }
1229
1230     if (mech_type == GSS_C_NO_OID || 
1231         gss_oid_equal(mech_type,  GSS_KRB5_MECHANISM))
1232         return gsskrb5_init_sec_context(minor_status,
1233                                         initiator_cred_handle,
1234                                         context_handle,
1235                                         target_name,
1236                                         mech_type,
1237                                         req_flags,
1238                                         time_req,
1239                                         input_chan_bindings,
1240                                         input_token,
1241                                         actual_mech_type,
1242                                         output_token,
1243                                         ret_flags,
1244                                         time_rec);
1245     else if (gss_oid_equal(mech_type, GSS_SPNEGO_MECHANISM))
1246         return spnego_init_sec_context (minor_status,
1247                                         initiator_cred_handle,
1248                                         context_handle,
1249                                         target_name,
1250                                         mech_type,
1251                                         req_flags,
1252                                         time_req,
1253                                         input_chan_bindings,
1254                                         input_token,
1255                                         actual_mech_type,
1256                                         output_token,
1257                                         ret_flags,
1258                                         time_rec);
1259     else
1260         return GSS_S_BAD_MECH;
1261 }