r10066: This is the second in my patches to work on Samba4's kerberos support,
[tprouty/samba.git] / source / heimdal / lib / gssapi / accept_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: accept_sec_context.c,v 1.53 2005/05/29 15:12:41 lha Exp $");
37
38 HEIMDAL_MUTEX gssapi_keytab_mutex = HEIMDAL_MUTEX_INITIALIZER;
39 krb5_keytab gssapi_krb5_keytab;
40
41 OM_uint32
42 gsskrb5_register_acceptor_identity (const char *identity)
43 {
44     krb5_error_code ret;
45
46     ret = gssapi_krb5_init();
47     if(ret)
48         return GSS_S_FAILURE;
49     
50     HEIMDAL_MUTEX_lock(&gssapi_keytab_mutex);
51
52     if(gssapi_krb5_keytab != NULL) {
53         krb5_kt_close(gssapi_krb5_context, gssapi_krb5_keytab);
54         gssapi_krb5_keytab = NULL;
55     }
56     if (identity == NULL) {
57         ret = krb5_kt_default(gssapi_krb5_context, &gssapi_krb5_keytab);
58     } else {
59         char *p;
60
61         asprintf(&p, "FILE:%s", identity);
62         if(p == NULL) {
63             HEIMDAL_MUTEX_unlock(&gssapi_keytab_mutex);
64             return GSS_S_FAILURE;
65         }
66         ret = krb5_kt_resolve(gssapi_krb5_context, p, &gssapi_krb5_keytab);
67         free(p);
68     }
69     HEIMDAL_MUTEX_unlock(&gssapi_keytab_mutex);
70     if(ret)
71         return GSS_S_FAILURE;
72     return GSS_S_COMPLETE;
73 }
74
75 void
76 gsskrb5_is_cfx(gss_ctx_id_t context_handle, int *is_cfx)
77 {
78     krb5_keyblock *key;
79     int acceptor = (context_handle->more_flags & LOCAL) == 0;
80
81     if (acceptor) {
82         if (context_handle->auth_context->local_subkey)
83             key = context_handle->auth_context->local_subkey;
84         else
85             key = context_handle->auth_context->remote_subkey;
86     } else {
87         if (context_handle->auth_context->remote_subkey)
88             key = context_handle->auth_context->remote_subkey;
89         else
90             key = context_handle->auth_context->local_subkey;
91     }
92     if (key == NULL)
93         key = context_handle->auth_context->keyblock;
94
95     if (key == NULL)
96         return;
97             
98     switch (key->keytype) {
99     case ETYPE_DES_CBC_CRC:
100     case ETYPE_DES_CBC_MD4:
101     case ETYPE_DES_CBC_MD5:
102     case ETYPE_DES3_CBC_MD5:
103     case ETYPE_DES3_CBC_SHA1:
104     case ETYPE_ARCFOUR_HMAC_MD5:
105     case ETYPE_ARCFOUR_HMAC_MD5_56:
106         break;
107     default :
108         *is_cfx = 1;
109         if ((acceptor && context_handle->auth_context->local_subkey) ||
110             (!acceptor && context_handle->auth_context->remote_subkey))
111             context_handle->more_flags |= ACCEPTOR_SUBKEY;
112         break;
113     }
114 }
115
116
117 static OM_uint32
118 gsskrb5_accept_delegated_token
119            (OM_uint32 * minor_status,
120             gss_ctx_id_t * context_handle,
121             gss_cred_id_t * delegated_cred_handle)
122 {
123     krb5_data *fwd_data = &(*context_handle)->fwd_data;
124     OM_uint32 *flags = &(*context_handle)->flags;
125     krb5_principal principal = (*context_handle)->source;
126     krb5_ccache ccache = NULL;
127     krb5_error_code kret;
128     int32_t ac_flags, ret;
129     gss_cred_id_t handle = NULL;
130       
131     if (delegated_cred_handle == NULL) {
132         /* XXX Create a new delegated_cred_handle? */
133
134         ret = 0;
135
136         kret = krb5_cc_default (gssapi_krb5_context, &ccache);
137         if (kret) {
138             *flags &= ~GSS_C_DELEG_FLAG;
139             goto end_fwd;
140         }
141     } else {
142
143         *delegated_cred_handle = NULL;
144         
145         handle = calloc(1, sizeof(*handle));
146         if (handle == NULL) {
147             ret = GSS_S_FAILURE;
148             *minor_status = ENOMEM;
149             krb5_set_error_string(gssapi_krb5_context, "out of memory");
150             gssapi_krb5_set_error_string();
151             *flags &= ~GSS_C_DELEG_FLAG;
152             goto end_fwd;
153         }
154         if ((ret = gss_duplicate_name(minor_status, principal,
155                                       &handle->principal)) != 0) {
156             *flags &= ~GSS_C_DELEG_FLAG;
157             ret = 0;
158             goto end_fwd;
159         }
160         kret = krb5_cc_gen_new (gssapi_krb5_context,
161                                 &krb5_mcc_ops,
162                                 &handle->ccache);
163         if (kret) {
164             *flags &= ~GSS_C_DELEG_FLAG;
165             ret = 0;
166             goto end_fwd;
167         }
168         ccache = handle->ccache;
169
170         ret = gss_create_empty_oid_set(minor_status, &handle->mechanisms);
171         if (ret) {
172             *flags &= ~GSS_C_DELEG_FLAG;
173             goto end_fwd;
174         }
175         ret = gss_add_oid_set_member(minor_status, GSS_KRB5_MECHANISM,
176                                      &handle->mechanisms);
177         if (ret) {
178             *flags &= ~GSS_C_DELEG_FLAG;
179             goto end_fwd;
180         }
181     }
182
183     kret = krb5_cc_initialize(gssapi_krb5_context, ccache, principal);
184     if (kret) {
185         *flags &= ~GSS_C_DELEG_FLAG;
186         ret = 0;
187         goto end_fwd;
188     }
189       
190     krb5_auth_con_removeflags(gssapi_krb5_context,
191                               (*context_handle)->auth_context,
192                               KRB5_AUTH_CONTEXT_DO_TIME,
193                               &ac_flags);
194     kret = krb5_rd_cred2(gssapi_krb5_context,
195                          (*context_handle)->auth_context,
196                          ccache,
197                          fwd_data);
198     if (kret)
199         gssapi_krb5_set_error_string();
200     krb5_auth_con_setflags(gssapi_krb5_context,
201                            (*context_handle)->auth_context,
202                            ac_flags);
203     if (kret) {
204         *flags &= ~GSS_C_DELEG_FLAG;
205         ret = GSS_S_FAILURE;
206         *minor_status = kret;
207         goto end_fwd;
208     }
209  end_fwd:
210     /* if there was some kind of failure, clean up internal structures */
211     if ((*flags & GSS_C_DELEG_FLAG) == 0) {
212         if (handle) {
213             if (handle->principal)
214                 gss_release_name(minor_status, &handle->principal);
215             if (handle->mechanisms)
216                 gss_release_oid_set(NULL, &handle->mechanisms);
217             if (handle->ccache)
218                 krb5_cc_destroy(gssapi_krb5_context, handle->ccache);
219             free(handle);
220             handle = NULL;
221         }
222     }
223     if (delegated_cred_handle == NULL) {
224         if (ccache)
225             krb5_cc_close(gssapi_krb5_context, ccache);
226     }
227     if (handle)
228         *delegated_cred_handle = handle;
229
230     return ret;
231 }
232
233 static OM_uint32
234 gsskrb5_acceptor_ready(
235         OM_uint32 * minor_status,
236         gss_ctx_id_t * context_handle,
237         gss_cred_id_t * delegated_cred_handle)
238 {
239         OM_uint32 ret;
240         int32_t seq_number;
241         int is_cfx = 0;
242         u_int32_t flags = (*context_handle)->flags;
243
244         krb5_auth_getremoteseqnumber (gssapi_krb5_context,
245                                       (*context_handle)->auth_context,
246                                       &seq_number);
247
248         gsskrb5_is_cfx(*context_handle, &is_cfx);
249
250         ret = _gssapi_msg_order_create(minor_status,
251                                        &(*context_handle)->order,
252                                        _gssapi_msg_order_f(flags),
253                                        seq_number, 0, is_cfx);
254         if (ret) return ret;
255
256         if (!(flags & GSS_C_MUTUAL_FLAG) && _gssapi_msg_order_f(flags)) {
257                 krb5_auth_con_setlocalseqnumber(gssapi_krb5_context,
258                                                 (*context_handle)->auth_context,
259                                                 seq_number);
260         }
261
262         /*
263          * We should handle the delegation ticket, in case it's there
264          */
265         if ((*context_handle)->fwd_data.length > 0 && (flags & GSS_C_DELEG_FLAG)) {
266                 ret = gsskrb5_accept_delegated_token(minor_status,
267                                                      context_handle,
268                                                      delegated_cred_handle);
269                 if (ret) return ret;
270         }
271
272         (*context_handle)->state        = ACCEPTOR_READY;
273         (*context_handle)->more_flags   |= OPEN;
274
275         return GSS_S_COMPLETE;
276 }
277 static OM_uint32
278 gsskrb5_acceptor_start
279            (OM_uint32 * minor_status,
280             gss_ctx_id_t * context_handle,
281             const gss_cred_id_t acceptor_cred_handle,
282             const gss_buffer_t input_token_buffer,
283             const gss_channel_bindings_t input_chan_bindings,
284             gss_name_t * src_name,
285             gss_OID * mech_type,
286             gss_buffer_t output_token,
287             OM_uint32 * ret_flags,
288             OM_uint32 * time_rec,
289             gss_cred_id_t * delegated_cred_handle
290            )
291 {
292     krb5_error_code kret;
293     OM_uint32 ret = GSS_S_COMPLETE;
294     krb5_data indata;
295     krb5_flags ap_options;
296     OM_uint32 flags;
297     krb5_ticket *ticket = NULL;
298     krb5_keytab keytab = NULL;
299     krb5_keyblock *keyblock = NULL;
300     krb5_data fwd_data;
301     int is_cfx = 0;
302
303     krb5_data_zero (&fwd_data);
304
305     /*
306      * We may, or may not, have an escapsulation.
307      */
308     ret = gssapi_krb5_decapsulate (minor_status,
309                                    input_token_buffer,
310                                    &indata,
311                                    "\x01\x00",
312                                    GSS_KRB5_MECHANISM);
313
314     if (ret) {
315         /* No OID wrapping apparently available. */
316         indata.length   = input_token_buffer->length;
317         indata.data     = input_token_buffer->value;
318     }
319
320     /*
321      * We need to get our keytab
322      */
323     if (acceptor_cred_handle == GSS_C_NO_CREDENTIAL) {
324         if (gssapi_krb5_keytab != NULL) {
325             keytab = gssapi_krb5_keytab;
326         }
327     } else if (acceptor_cred_handle->keytab != NULL) {
328         keytab = acceptor_cred_handle->keytab;
329     }
330     
331     /*
332      * We need to check the ticket and create the AP-REP packet
333      */
334     kret = krb5_rd_req_return_keyblock(gssapi_krb5_context,
335                                        &(*context_handle)->auth_context,
336                                        &indata,
337                                        (acceptor_cred_handle == GSS_C_NO_CREDENTIAL) ? NULL : acceptor_cred_handle->principal,
338                                        keytab,
339                                        &ap_options,
340                                        &ticket,
341                                        &keyblock);
342     if (kret) {
343         ret = GSS_S_FAILURE;
344         *minor_status = kret;
345         gssapi_krb5_set_error_string ();
346         return ret;
347     }
348     
349     /*
350      * We need to remember some data on the context_handle
351      */
352     (*context_handle)->ticket = ticket;
353     (*context_handle)->service_keyblock = keyblock;
354     (*context_handle)->lifetime = ticket->ticket.endtime;
355     
356     /*
357      * We need to copy the principal names to the context and the calling layer
358      */
359     kret = krb5_copy_principal(gssapi_krb5_context,
360                                ticket->client,
361                                &(*context_handle)->source);
362     if (kret) {
363         ret = GSS_S_FAILURE;
364         *minor_status = kret;
365         gssapi_krb5_set_error_string ();
366     }
367
368     kret = krb5_copy_principal (gssapi_krb5_context,
369                                 ticket->server,
370                                 &(*context_handle)->target);
371     if (kret) {
372         ret = GSS_S_FAILURE;
373         *minor_status = kret;
374         gssapi_krb5_set_error_string ();
375         return ret;
376     }
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) {
383         return ret;
384     }
385
386     if (src_name != NULL) {
387         kret = krb5_copy_principal (gssapi_krb5_context,
388                                     ticket->client,
389                                     src_name);
390         if (kret) {
391             ret = GSS_S_FAILURE;
392             *minor_status = kret;
393             gssapi_krb5_set_error_string ();
394             return ret;
395         }
396     }
397
398     /*
399      * We need to get the flags out of the 8003 checksum
400      */
401     {
402         krb5_authenticator authenticator;
403       
404         kret = krb5_auth_con_getauthenticator(gssapi_krb5_context,
405                                               (*context_handle)->auth_context,
406                                               &authenticator);
407         if(kret) {
408             ret = GSS_S_FAILURE;
409             *minor_status = kret;
410             gssapi_krb5_set_error_string ();
411             return ret;
412         }
413
414         ret = gssapi_krb5_verify_8003_checksum(minor_status,
415                                                input_chan_bindings,
416                                                authenticator->cksum,
417                                                &flags,
418                                                &fwd_data);
419         krb5_free_authenticator(gssapi_krb5_context, &authenticator);
420         if (ret)
421             if (ret) return ret;
422     }
423     
424     if(flags & GSS_C_MUTUAL_FLAG) {
425             krb5_data outbuf;
426             
427             gsskrb5_is_cfx(*context_handle, &is_cfx);
428             
429             if (is_cfx != 0 
430                 || (ap_options & AP_OPTS_USE_SUBKEY)) {
431                     kret = krb5_auth_con_addflags(gssapi_krb5_context,
432                                                   (*context_handle)->auth_context,
433                                                   KRB5_AUTH_CONTEXT_USE_SUBKEY,
434                                                   NULL);
435                     (*context_handle)->more_flags |= ACCEPTOR_SUBKEY;
436             }
437             
438             kret = krb5_mk_rep(gssapi_krb5_context,
439                                (*context_handle)->auth_context,
440                                &outbuf);
441             if (kret) {
442                     *minor_status = kret;
443                     gssapi_krb5_set_error_string ();
444                     return GSS_S_FAILURE;
445             }
446             
447             if (!(flags & GSS_C_DCE_STYLE)) {
448                     ret = gssapi_krb5_encapsulate(minor_status,
449                                                   &outbuf,
450                                                   output_token,
451                                                   "\x02\x00",
452                                                   GSS_KRB5_MECHANISM);
453                     krb5_data_free (&outbuf);
454                     if (ret) return ret;
455             } else {
456                     output_token->length        = outbuf.length;
457                     output_token->value = outbuf.data;
458             }
459     }
460     
461     /*
462      * We need to send the flags back to the caller
463      */
464     flags |= GSS_C_TRANS_FLAG;
465
466     if (ret_flags)
467         *ret_flags = flags;
468     
469     /* And remember them for later */
470     
471     (*context_handle)->lifetime = ticket->ticket.endtime;
472     (*context_handle)->flags = flags;
473     (*context_handle)->more_flags |= OPEN;
474     
475     if (mech_type)
476         *mech_type = GSS_KRB5_MECHANISM;
477     
478     if (time_rec) {
479         ret = gssapi_lifetime_left(minor_status,
480                                    (*context_handle)->lifetime,
481                                    time_rec);
482         if (ret)
483             if (ret) return ret;
484     }
485
486     /*
487      * When GSS_C_DCE_STYLE is in use, we need ask for a AP-REP from the client
488      */
489     if (flags & GSS_C_DCE_STYLE) {
490             (*context_handle)->state = ACCEPTOR_WAIT_FOR_DCESTYLE;
491             return GSS_S_CONTINUE_NEEDED;
492     }
493
494     return gsskrb5_acceptor_ready(minor_status, context_handle, delegated_cred_handle);
495 }
496
497 static OM_uint32
498 gsskrb5_acceptor_wait_for_dcestyle(
499         OM_uint32 * minor_status,
500         gss_ctx_id_t * context_handle,
501         const gss_cred_id_t acceptor_cred_handle,
502         const gss_buffer_t input_token_buffer,
503         const gss_channel_bindings_t input_chan_bindings,
504         gss_name_t * src_name,
505         gss_OID * mech_type,
506         gss_buffer_t output_token,
507         OM_uint32 * ret_flags,
508         OM_uint32 * time_rec,
509         gss_cred_id_t * delegated_cred_handle)
510 {
511         OM_uint32 ret;
512         krb5_error_code kret;
513         krb5_data inbuf;
514         OM_uint32 r_seq_number;
515         OM_uint32 l_seq_number;
516         
517         /* We know it's GSS_C_DCE_STYLE so we don't need to decapsulate the AP_REP */
518         inbuf.length    = input_token_buffer->length;
519         inbuf.data      = input_token_buffer->value;
520
521         /* 
522          * We need to remeber the old remote seq_number, then check if the client has replied with our local seq_number,
523          * and then reset the remote seq_number to the old value 
524          */
525         {
526                 kret = krb5_auth_con_getlocalseqnumber(gssapi_krb5_context,
527                                                     (*context_handle)->auth_context,
528                                                     &l_seq_number);
529                 if (kret) {
530                         gssapi_krb5_set_error_string ();
531                         *minor_status = kret;
532                         return GSS_S_FAILURE;
533                 }
534
535                 kret = krb5_auth_getremoteseqnumber(gssapi_krb5_context,
536                                                     (*context_handle)->auth_context,
537                                                     &r_seq_number);
538                 if (kret) {
539                         gssapi_krb5_set_error_string ();
540                         *minor_status = kret;
541                         return GSS_S_FAILURE;
542                 }
543
544                 kret = krb5_auth_con_setremoteseqnumber(gssapi_krb5_context,
545                                                     (*context_handle)->auth_context,
546                                                     l_seq_number);
547                 if (kret) {
548                         gssapi_krb5_set_error_string ();
549                         *minor_status = kret;
550                         return GSS_S_FAILURE;
551                 }
552         }
553
554         /* We need to verify the AP_REP, but we need to flag that this
555            is DCE_STYLE, so don't check the timestamps this time 
556         */ 
557         {
558                 krb5_ap_rep_enc_part *repl;
559                 int32_t auth_flags;
560                 
561                 kret = krb5_auth_con_removeflags(gssapi_krb5_context,
562                                                  (*context_handle)->auth_context,
563                                                  KRB5_AUTH_CONTEXT_DO_TIME, &auth_flags);
564                 if (kret) { /* Can't happen */
565                         gssapi_krb5_set_error_string ();
566                         *minor_status = kret;
567                         return GSS_S_FAILURE;
568                 }
569
570                 kret = krb5_rd_rep(gssapi_krb5_context,
571                                    (*context_handle)->auth_context,
572                                    &inbuf,
573                                    &repl);
574                 if (kret) {
575                         gssapi_krb5_set_error_string ();
576                         *minor_status = kret;
577                         return GSS_S_FAILURE;
578                 }
579                 
580                 /* Because the inbuf above is a final leg from client
581                  * to server, we don't have a use for a 'reply'
582                  * here */
583                 krb5_free_ap_rep_enc_part(gssapi_krb5_context, repl);
584
585                 /* Do no harm, put the flags back */
586                 kret = krb5_auth_con_setflags(gssapi_krb5_context,
587                                               (*context_handle)->auth_context,
588                                               auth_flags);
589                 if (kret) { /* Can't happen */
590                         gssapi_krb5_set_error_string ();
591                         *minor_status = kret;
592                         return GSS_S_FAILURE;
593                 }
594         }
595
596         /* We need to check the liftime */
597         {
598                 OM_uint32 lifetime_rec;
599
600                 ret = gssapi_lifetime_left(minor_status,
601                                            (*context_handle)->lifetime,
602                                            &lifetime_rec);
603                 if (ret) return ret;
604
605                 if (lifetime_rec == 0) {
606                         return GSS_S_CONTEXT_EXPIRED;
607                 }
608         
609                 if (time_rec) *time_rec = lifetime_rec;
610         }
611
612         /* We need to give the caller the flags which are in use */
613         if (ret_flags) *ret_flags = (*context_handle)->flags;
614
615         if (src_name) {
616                 kret = krb5_copy_principal(gssapi_krb5_context,
617                                            (*context_handle)->source,
618                                            src_name);
619                 if (kret) {
620                         *minor_status = kret;
621                         gssapi_krb5_set_error_string ();
622                         return GSS_S_FAILURE;
623                 }
624         }
625
626         /*
627          * After the krb5_rd_rep() the remote and local seq_number should be the same,
628          * because the client just replies the seq_number from our AP-REP in its AP-REP,
629          * but then the client uses the seq_number from its AP-REQ for GSS_wrap()
630          */
631         {
632                 OM_uint32 tmp_r_seq_number;
633                 OM_uint32 tmp_l_seq_number;
634
635                 kret = krb5_auth_getremoteseqnumber(gssapi_krb5_context,
636                                                     (*context_handle)->auth_context,
637                                                     &tmp_r_seq_number);
638                 if (kret) {
639                         gssapi_krb5_set_error_string ();
640                         *minor_status = kret;
641                         return GSS_S_FAILURE;
642                 }
643
644                 kret = krb5_auth_con_getlocalseqnumber(gssapi_krb5_context,
645                                                     (*context_handle)->auth_context,
646                                                     &tmp_l_seq_number);
647                 if (kret) {
648                         gssapi_krb5_set_error_string ();
649                         *minor_status = kret;
650                         return GSS_S_FAILURE;
651                 }
652
653                 /*
654                  * Here we check if the client has responsed with our local seq_number,
655                  */
656                 if (tmp_r_seq_number != tmp_l_seq_number) {
657                         return GSS_S_UNSEQ_TOKEN;
658                 }
659         }
660
661         /*
662          * We need to reset the remote seq_number, because the client will use,
663          * the old one for the GSS_wrap() calls
664          */
665         {
666                 kret = krb5_auth_con_setremoteseqnumber(gssapi_krb5_context,
667                                                        (*context_handle)->auth_context,
668                                                        r_seq_number);   
669                 if (kret) {
670                         gssapi_krb5_set_error_string ();
671                         *minor_status = kret;
672                         return GSS_S_FAILURE;
673                 }
674         }
675
676         return gsskrb5_acceptor_ready(minor_status, context_handle, delegated_cred_handle);
677 }
678
679 static OM_uint32
680 gsskrb5_accept_sec_context
681            (OM_uint32 * minor_status,
682             gss_ctx_id_t * context_handle,
683             const gss_cred_id_t acceptor_cred_handle,
684             const gss_buffer_t input_token_buffer,
685             const gss_channel_bindings_t input_chan_bindings,
686             gss_name_t * src_name,
687             gss_OID * mech_type,
688             gss_buffer_t output_token,
689             OM_uint32 * ret_flags,
690             OM_uint32 * time_rec,
691             gss_cred_id_t * delegated_cred_handle
692            )
693 {
694     OM_uint32 ret = GSS_S_COMPLETE;
695     krb5_data fwd_data;
696     gss_ctx_id_t local_context;
697
698     GSSAPI_KRB5_INIT();
699
700     krb5_data_zero (&fwd_data);
701     output_token->length = 0;
702     output_token->value = NULL;
703
704     if (src_name != NULL)
705         *src_name = NULL;
706     if (mech_type)
707         *mech_type = GSS_KRB5_MECHANISM;
708
709     if (*context_handle == GSS_C_NO_CONTEXT) {
710         ret = _gsskrb5_create_ctx(minor_status,
711                                   &local_context,
712                                   input_chan_bindings,
713                                   ACCEPTOR_START);
714         if (ret) return ret;
715     } else {
716         local_context = *context_handle;
717     }
718     
719     /*
720      * TODO: check the channel_bindings 
721      * (above just sets them to krb5 layer)
722      */
723
724     HEIMDAL_MUTEX_lock(&(local_context)->ctx_id_mutex);
725     
726     switch ((local_context)->state) {
727     case ACCEPTOR_START:
728         ret = gsskrb5_acceptor_start(minor_status,
729                                      &local_context,
730                                      acceptor_cred_handle,
731                                      input_token_buffer,
732                                      input_chan_bindings,
733                                      src_name,
734                                      mech_type,
735                                      output_token,
736                                      ret_flags,
737                                      time_rec,
738                                      delegated_cred_handle);
739         break;
740     case ACCEPTOR_WAIT_FOR_DCESTYLE:
741         ret = gsskrb5_acceptor_wait_for_dcestyle(minor_status,
742                                                  &local_context,
743                                                  acceptor_cred_handle,
744                                                  input_token_buffer,
745                                                  input_chan_bindings,
746                                                  src_name,
747                                                  mech_type,
748                                                  output_token,
749                                                  ret_flags,
750                                                  time_rec,
751                                                  delegated_cred_handle);
752         break;
753     case ACCEPTOR_READY:
754         /* this function should not be called after it has returned GSS_S_COMPLETE */
755         ret =  GSS_S_BAD_STATUS;
756         break;
757     default:
758         /* TODO: is this correct here? --metze */
759         ret =  GSS_S_BAD_STATUS;
760         break;
761     }
762     
763     HEIMDAL_MUTEX_unlock(&(local_context)->ctx_id_mutex);
764     
765     if (*context_handle == GSS_C_NO_CONTEXT) {
766         if (ret == GSS_S_COMPLETE || ret == GSS_S_CONTINUE_NEEDED) {
767             *context_handle = local_context;
768         } else {
769             gss_delete_sec_context(minor_status, 
770                                    &local_context, 
771                                    NULL);
772         }
773     }
774
775     return ret;
776 }
777
778 static OM_uint32
779 code_NegTokenArg(OM_uint32 *minor_status,
780                  const NegTokenTarg *targ,
781                  krb5_data *data,
782                  u_char **ret_buf)
783 {
784     OM_uint32 ret;
785     u_char *buf;
786     size_t buf_size, buf_len;
787
788     buf_size = 1024;
789     buf = malloc(buf_size);
790     if (buf == NULL) {
791         *minor_status = ENOMEM;
792         return GSS_S_FAILURE;
793     }
794
795     do {
796         ret = encode_NegTokenTarg(buf + buf_size - 1,
797                                   buf_size,
798                                   targ, &buf_len);
799         if (ret == 0) {
800             size_t tmp;
801
802             ret = der_put_length_and_tag(buf + buf_size - buf_len - 1,
803                                          buf_size - buf_len,
804                                          buf_len,
805                                          ASN1_C_CONTEXT,
806                                          CONS,
807                                          1,
808                                          &tmp);
809             if (ret == 0)
810                 buf_len += tmp;
811         }
812         if (ret) {
813             if (ret == ASN1_OVERFLOW) {
814                 u_char *tmp;
815
816                 buf_size *= 2;
817                 tmp = realloc (buf, buf_size);
818                 if (tmp == NULL) {
819                     *minor_status = ENOMEM;
820                     free(buf);
821                     return GSS_S_FAILURE;
822                 }
823                 buf = tmp;
824             } else {
825                 *minor_status = ret;
826                 free(buf);
827                 return GSS_S_FAILURE;
828             }
829         }
830     } while (ret == ASN1_OVERFLOW);
831
832     data->data   = buf + buf_size - buf_len;
833     data->length = buf_len;
834     *ret_buf     = buf;
835     return GSS_S_COMPLETE;
836 }
837
838 static OM_uint32
839 send_reject (OM_uint32 *minor_status,
840              gss_buffer_t output_token)
841 {
842     NegTokenTarg targ;
843     krb5_data data;
844     u_char *buf;
845     OM_uint32 ret;
846
847     ALLOC(targ.negResult, 1);
848     if (targ.negResult == NULL) {
849         *minor_status = ENOMEM;
850         return GSS_S_FAILURE;
851     }
852     *(targ.negResult) = reject;
853     targ.supportedMech = NULL;
854     targ.responseToken = NULL;
855     targ.mechListMIC   = NULL;
856     
857     ret = code_NegTokenArg (minor_status, &targ, &data, &buf);
858     free_NegTokenTarg(&targ);
859     if (ret)
860         return ret;
861
862 #if 0
863     ret = _gssapi_encapsulate(minor_status,
864                               &data,
865                               output_token,
866                               GSS_SPNEGO_MECHANISM);
867 #else
868     output_token->value = malloc(data.length);
869     if (output_token->value == NULL) {
870         *minor_status = ENOMEM;
871         ret = GSS_S_FAILURE;
872     } else {
873         output_token->length = data.length;
874         memcpy(output_token->value, data.data, output_token->length);
875     }
876 #endif
877     free(buf);
878     if (ret)
879         return ret;
880     return GSS_S_BAD_MECH;
881 }
882
883 static OM_uint32
884 send_accept (OM_uint32 *minor_status,
885              OM_uint32 major_status,
886              gss_buffer_t output_token,
887              gss_buffer_t mech_token,
888              gss_ctx_id_t context_handle,
889              const MechTypeList *mechtypelist)
890 {
891     NegTokenTarg targ;
892     krb5_data data;
893     u_char *buf;
894     OM_uint32 ret;
895     gss_buffer_desc mech_buf, mech_mic_buf;
896     krb5_boolean require_mic;
897
898     memset(&targ, 0, sizeof(targ));
899     ALLOC(targ.negResult, 1);
900     if (targ.negResult == NULL) {
901         *minor_status = ENOMEM;
902         return GSS_S_FAILURE;
903     }
904     *(targ.negResult) = accept_completed;
905
906     ALLOC(targ.supportedMech, 1);
907     if (targ.supportedMech == NULL) {
908         free_NegTokenTarg(&targ);
909         *minor_status = ENOMEM;
910         return GSS_S_FAILURE;
911     }
912
913     ret = der_get_oid(GSS_KRB5_MECHANISM->elements,
914                       GSS_KRB5_MECHANISM->length,
915                       targ.supportedMech,
916                       NULL);
917     if (ret) {
918         free_NegTokenTarg(&targ);
919         *minor_status = ENOMEM;
920         return GSS_S_FAILURE;
921     }
922
923     if (mech_token != NULL && mech_token->length != 0) {
924         ALLOC(targ.responseToken, 1);
925         if (targ.responseToken == NULL) {
926             free_NegTokenTarg(&targ);
927             *minor_status = ENOMEM;
928             return GSS_S_FAILURE;
929         }
930         targ.responseToken->length = mech_token->length;
931         targ.responseToken->data   = mech_token->value;
932         mech_token->length = 0;
933         mech_token->value  = NULL;
934     } else {
935         targ.responseToken = NULL;
936     }
937
938     ret = _gss_spnego_require_mechlist_mic(minor_status, context_handle,
939                                            &require_mic);
940     if (ret) {
941         free_NegTokenTarg(&targ);
942         return ret;
943     }
944
945     if (major_status == GSS_S_COMPLETE && require_mic) {
946         size_t buf_len;
947
948         ALLOC(targ.mechListMIC, 1);
949         if (targ.mechListMIC == NULL) {
950             free_NegTokenTarg(&targ);
951             *minor_status = ENOMEM;
952             return GSS_S_FAILURE;
953         }
954         
955         ASN1_MALLOC_ENCODE(MechTypeList, mech_buf.value, mech_buf.length,
956                            mechtypelist, &buf_len, ret);
957         if (ret) {
958             free_NegTokenTarg(&targ);
959             return ret;
960         }
961         if (mech_buf.length != buf_len)
962             abort();
963
964         ret = gss_get_mic(minor_status, context_handle, 0, &mech_buf,
965                           &mech_mic_buf);
966         free (mech_buf.value);
967         if (ret) {
968             free_NegTokenTarg(&targ);
969             return ret;
970         }
971
972         targ.mechListMIC->length = mech_mic_buf.length;
973         targ.mechListMIC->data   = mech_mic_buf.value;
974     } else
975         targ.mechListMIC = NULL;
976
977     ret = code_NegTokenArg (minor_status, &targ, &data, &buf);
978     free_NegTokenTarg(&targ);
979     if (ret)
980         return ret;
981
982 #if 0
983     ret = _gssapi_encapsulate(minor_status,
984                               &data,
985                               output_token,
986                               GSS_SPNEGO_MECHANISM);
987 #else
988     output_token->value = malloc(data.length);
989     if (output_token->value == NULL) {
990         *minor_status = ENOMEM;
991         ret = GSS_S_FAILURE;
992     } else {
993         output_token->length = data.length;
994         memcpy(output_token->value, data.data, output_token->length);
995     }
996 #endif
997     free(buf);
998     if (ret)
999         return ret;
1000     return GSS_S_COMPLETE;
1001 }
1002
1003 static OM_uint32
1004 spnego_accept_sec_context
1005            (OM_uint32 * minor_status,
1006             gss_ctx_id_t * context_handle,
1007             const gss_cred_id_t acceptor_cred_handle,
1008             const gss_buffer_t input_token_buffer,
1009             const gss_channel_bindings_t input_chan_bindings,
1010             gss_name_t * src_name,
1011             gss_OID * mech_type,
1012             gss_buffer_t output_token,
1013             OM_uint32 * ret_flags,
1014             OM_uint32 * time_rec,
1015             gss_cred_id_t * delegated_cred_handle
1016            )
1017 {
1018     OM_uint32 ret, ret2;
1019     NegTokenInit ni;
1020     size_t ni_len;
1021     int i;
1022     int found = 0;
1023     krb5_data data;
1024     size_t len, taglen;
1025
1026     output_token->length = 0;
1027     output_token->value  = NULL;
1028
1029     ret = _gssapi_decapsulate (minor_status,
1030                                input_token_buffer,
1031                                &data,
1032                                GSS_SPNEGO_MECHANISM);
1033     if (ret)
1034         return ret;
1035
1036     ret = der_match_tag_and_length(data.data, data.length,
1037                                    ASN1_C_CONTEXT, CONS, 0, &len, &taglen);
1038     if (ret)
1039         return ret;
1040
1041     if(len > data.length - taglen)
1042         return ASN1_OVERRUN;
1043
1044     ret = decode_NegTokenInit((const char *)data.data + taglen, len,
1045                               &ni, &ni_len);
1046     if (ret)
1047         return GSS_S_DEFECTIVE_TOKEN;
1048
1049     if (ni.mechTypes == NULL) {
1050         free_NegTokenInit(&ni);
1051         return send_reject (minor_status, output_token);
1052     }
1053
1054     for (i = 0; !found && i < ni.mechTypes->len; ++i) {
1055         char mechbuf[17];
1056         size_t mech_len;
1057
1058         ret = der_put_oid (mechbuf + sizeof(mechbuf) - 1,
1059                            sizeof(mechbuf),
1060                            &ni.mechTypes->val[i],
1061                            &mech_len);
1062         if (ret) {
1063             free_NegTokenInit(&ni);
1064             return GSS_S_DEFECTIVE_TOKEN;
1065         }
1066         if (mech_len == GSS_KRB5_MECHANISM->length
1067             && memcmp(GSS_KRB5_MECHANISM->elements,
1068                       mechbuf + sizeof(mechbuf) - mech_len,
1069                       mech_len) == 0)
1070             found = 1;
1071     }
1072     if (found) {
1073         gss_buffer_desc ibuf, obuf;
1074         gss_buffer_t ot = NULL;
1075         OM_uint32 minor;
1076
1077         if (ni.mechToken != NULL) {
1078             ibuf.length = ni.mechToken->length;
1079             ibuf.value  = ni.mechToken->data;
1080
1081             ret = gsskrb5_accept_sec_context(&minor,
1082                                              context_handle,
1083                                              acceptor_cred_handle,
1084                                              &ibuf,
1085                                              input_chan_bindings,
1086                                              src_name,
1087                                              mech_type,
1088                                              &obuf,
1089                                              ret_flags,
1090                                              time_rec,
1091                                              delegated_cred_handle);
1092             if (ret == GSS_S_COMPLETE || ret == GSS_S_CONTINUE_NEEDED) {
1093                 ot = &obuf;
1094             } else {
1095                 free_NegTokenInit(&ni);
1096                 send_reject (minor_status, output_token);
1097                 return ret;
1098             }
1099         }
1100         ret2 = send_accept (minor_status, ret, output_token, ot,
1101                            *context_handle, ni.mechTypes);
1102         if (ret2 != GSS_S_COMPLETE)
1103             ret = ret2;
1104         if (ot != NULL)
1105             gss_release_buffer(&minor, ot);
1106         free_NegTokenInit(&ni);
1107         return ret;
1108     } else {
1109         free_NegTokenInit(&ni);
1110         return send_reject (minor_status, output_token);
1111     }
1112 }
1113
1114 OM_uint32
1115 gss_accept_sec_context
1116            (OM_uint32 * minor_status,
1117             gss_ctx_id_t * context_handle,
1118             const gss_cred_id_t acceptor_cred_handle,
1119             const gss_buffer_t input_token_buffer,
1120             const gss_channel_bindings_t input_chan_bindings,
1121             gss_name_t * src_name,
1122             gss_OID * mech_type,
1123             gss_buffer_t output_token,
1124             OM_uint32 * ret_flags,
1125             OM_uint32 * time_rec,
1126             gss_cred_id_t * delegated_cred_handle
1127            )
1128 {
1129     OM_uint32 ret;
1130     ssize_t mech_len;
1131     const u_char *p;
1132
1133     *minor_status = 0;
1134
1135     mech_len = gssapi_krb5_get_mech (input_token_buffer->value,
1136                                      input_token_buffer->length,
1137                                      &p);
1138
1139     /* This could be 'dce style' kerberos, where the OID is missing :-( */
1140     if ((mech_len < 0) || ((mech_len == GSS_KRB5_MECHANISM->length)
1141                            && memcmp(p, GSS_KRB5_MECHANISM->elements, mech_len) == 0))
1142         ret = gsskrb5_accept_sec_context(minor_status,
1143                                          context_handle,
1144                                          acceptor_cred_handle,
1145                                          input_token_buffer,
1146                                          input_chan_bindings,
1147                                          src_name,
1148                                          mech_type,
1149                                          output_token,
1150                                          ret_flags,
1151                                          time_rec,
1152                                          delegated_cred_handle);
1153     else if (mech_len == GSS_SPNEGO_MECHANISM->length
1154              && memcmp(p, GSS_SPNEGO_MECHANISM->elements, mech_len) == 0)
1155         ret = spnego_accept_sec_context(minor_status,
1156                                         context_handle,
1157                                         acceptor_cred_handle,
1158                                         input_token_buffer,
1159                                         input_chan_bindings,
1160                                         src_name,
1161                                         mech_type,
1162                                         output_token,
1163                                         ret_flags,
1164                                         time_rec,
1165                                         delegated_cred_handle);
1166     else
1167         return GSS_S_BAD_MECH;
1168
1169     return ret;
1170 }