Convert DEBUG_REFERRALS to TRACE_* framework
[krb5.git] / src / lib / gssapi / krb5 / init_sec_context.c
1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /*
3  * Copyright 2000, 2002, 2003, 2007, 2008 by the Massachusetts Institute of
4  * Technology.  All Rights Reserved.
5  *
6  * Export of this software from the United States of America may
7  *   require a specific license from the United States Government.
8  *   It is the responsibility of any person or organization contemplating
9  *   export to obtain such a license before exporting.
10  *
11  * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
12  * distribute this software and its documentation for any purpose and
13  * without fee is hereby granted, provided that the above copyright
14  * notice appear in all copies and that both that copyright notice and
15  * this permission notice appear in supporting documentation, and that
16  * the name of M.I.T. not be used in advertising or publicity pertaining
17  * to distribution of the software without specific, written prior
18  * permission.  Furthermore if you modify this software you must label
19  * your software as modified software and not distribute it in such a
20  * fashion that it might be confused with the original M.I.T. software.
21  * M.I.T. makes no representations about the suitability of
22  * this software for any purpose.  It is provided "as is" without express
23  * or implied warranty.
24  */
25 /*
26  * Copyright 1993 by OpenVision Technologies, Inc.
27  *
28  * Permission to use, copy, modify, distribute, and sell this software
29  * and its documentation for any purpose is hereby granted without fee,
30  * provided that the above copyright notice appears in all copies and
31  * that both that copyright notice and this permission notice appear in
32  * supporting documentation, and that the name of OpenVision not be used
33  * in advertising or publicity pertaining to distribution of the software
34  * without specific, written prior permission. OpenVision makes no
35  * representations about the suitability of this software for any
36  * purpose.  It is provided "as is" without express or implied warranty.
37  *
38  * OPENVISION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
39  * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
40  * EVENT SHALL OPENVISION BE LIABLE FOR ANY SPECIAL, INDIRECT OR
41  * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
42  * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
43  * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
44  * PERFORMANCE OF THIS SOFTWARE.
45  */
46
47 /*
48  * Copyright (C) 1998 by the FundsXpress, INC.
49  *
50  * All rights reserved.
51  *
52  * Export of this software from the United States of America may require
53  * a specific license from the United States Government.  It is the
54  * responsibility of any person or organization contemplating export to
55  * obtain such a license before exporting.
56  *
57  * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
58  * distribute this software and its documentation for any purpose and
59  * without fee is hereby granted, provided that the above copyright
60  * notice appear in all copies and that both that copyright notice and
61  * this permission notice appear in supporting documentation, and that
62  * the name of FundsXpress. not be used in advertising or publicity pertaining
63  * to distribution of the software without specific, written prior
64  * permission.  FundsXpress makes no representations about the suitability of
65  * this software for any purpose.  It is provided "as is" without express
66  * or implied warranty.
67  *
68  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
69  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
70  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
71  */
72 /*
73  * Copyright (c) 2006-2008, Novell, Inc.
74  * All rights reserved.
75  *
76  * Redistribution and use in source and binary forms, with or without
77  * modification, are permitted provided that the following conditions are met:
78  *
79  *   * Redistributions of source code must retain the above copyright notice,
80  *       this list of conditions and the following disclaimer.
81  *   * Redistributions in binary form must reproduce the above copyright
82  *       notice, this list of conditions and the following disclaimer in the
83  *       documentation and/or other materials provided with the distribution.
84  *   * The copyright holder's name is not used to endorse or promote products
85  *       derived from this software without specific prior written permission.
86  *
87  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
88  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
89  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
90  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
91  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
92  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
93  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
94  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
95  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
96  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
97  * POSSIBILITY OF SUCH DAMAGE.
98  */
99
100 #include "k5-int.h"
101 #include "gssapiP_krb5.h"
102 #ifdef HAVE_MEMORY_H
103 #include <memory.h>
104 #endif
105 #include <stdlib.h>
106 #include <assert.h>
107
108 /*
109  * $Id$
110  */
111
112 /* XXX This is for debugging only!!!  Should become a real bitfield
113    at some point */
114 int krb5_gss_dbg_client_expcreds = 0;
115
116 /*
117  * Common code which fetches the correct krb5 credentials from the
118  * ccache.
119  */
120 static krb5_error_code get_credentials(context, cred, server, now,
121                                        endtime, out_creds)
122     krb5_context context;
123     krb5_gss_cred_id_t cred;
124     krb5_gss_name_t server;
125     krb5_timestamp now;
126     krb5_timestamp endtime;
127     krb5_creds **out_creds;
128 {
129     krb5_error_code     code;
130     krb5_creds          in_creds, evidence_creds, *result_creds = NULL;
131     krb5_flags          flags = 0;
132
133     *out_creds = NULL;
134
135     k5_mutex_assert_locked(&cred->lock);
136     memset(&in_creds, 0, sizeof(krb5_creds));
137     memset(&evidence_creds, 0, sizeof(krb5_creds));
138     in_creds.client = in_creds.server = NULL;
139
140     assert(cred->name != NULL);
141
142     /*
143      * Do constrained delegation if we have proxy credentials and
144      * we're not trying to get a ticket to ourselves (in which case
145      * we can just use the S4U2Self or evidence ticket directly).
146      */
147     if (cred->impersonator &&
148         !krb5_principal_compare(context, cred->impersonator, server->princ)) {
149         krb5_creds mcreds;
150
151         flags |= KRB5_GC_CANONICALIZE | KRB5_GC_CONSTRAINED_DELEGATION;
152
153         memset(&mcreds, 0, sizeof(mcreds));
154
155         mcreds.magic = KV5M_CREDS;
156         mcreds.server = cred->impersonator;
157         mcreds.client = cred->name->princ;
158
159         code = krb5_cc_retrieve_cred(context, cred->ccache,
160                                      KRB5_TC_MATCH_AUTHDATA, &mcreds,
161                                      &evidence_creds);
162         if (code)
163             goto cleanup;
164
165         assert(evidence_creds.ticket_flags & TKT_FLG_FORWARDABLE);
166
167         in_creds.client = cred->impersonator;
168         in_creds.second_ticket = evidence_creds.ticket;
169     } else {
170         in_creds.client = cred->name->princ;
171     }
172
173     in_creds.server = server->princ;
174     in_creds.times.endtime = endtime;
175     in_creds.authdata = NULL;
176     in_creds.keyblock.enctype = 0;
177
178     /*
179      * cred->name is immutable, so there is no need to acquire
180      * cred->name->lock.
181      */
182     if (cred->name->ad_context != NULL) {
183         code = krb5_authdata_export_authdata(context,
184                                              cred->name->ad_context,
185                                              AD_USAGE_TGS_REQ,
186                                              &in_creds.authdata);
187         if (code != 0)
188             goto cleanup;
189     }
190
191     /* Don't go out over the network if we used IAKERB */
192     if (cred->iakerb_mech)
193         flags |= KRB5_GC_CACHED;
194
195     code = krb5_get_credentials(context, flags, cred->ccache,
196                                 &in_creds, &result_creds);
197     if (code == KRB5_CC_NOTFOUND && cred->password.data != NULL &&
198         !cred->iakerb_mech) {
199         krb5_creds tgt_creds;
200
201         memset(&tgt_creds, 0, sizeof(tgt_creds));
202
203         /* No TGT in the ccache, but we can get one with the password. */
204         code = krb5_get_init_creds_password(context, &tgt_creds,
205                                             in_creds.client,
206                                             cred->password.data,
207                                             NULL, NULL,
208                                             0, NULL, NULL);
209         if (code)
210             goto cleanup;
211
212         code = krb5_cc_store_cred(context, cred->ccache, &tgt_creds);
213         if (code) {
214             krb5_free_cred_contents(context, &tgt_creds);
215             goto cleanup;
216         }
217         cred->tgt_expire = tgt_creds.times.endtime;
218         krb5_free_cred_contents(context, &tgt_creds);
219
220         code = krb5_get_credentials(context, flags, cred->ccache,
221                                     &in_creds, &result_creds);
222     }
223     if (code)
224         goto cleanup;
225
226     if (flags & KRB5_GC_CONSTRAINED_DELEGATION) {
227         if (!krb5_principal_compare(context, cred->name->princ,
228                                     result_creds->client)) {
229             /* server did not support constrained delegation */
230             code = KRB5_KDCREP_MODIFIED;
231             goto cleanup;
232         }
233     }
234
235     /*
236      * Enforce a stricter limit (without timeskew forgiveness at the
237      * boundaries) because accept_sec_context code is also similarly
238      * non-forgiving.
239      */
240     if (!krb5_gss_dbg_client_expcreds && result_creds->times.endtime < now) {
241         code = KRB5KRB_AP_ERR_TKT_EXPIRED;
242         goto cleanup;
243     }
244
245     *out_creds = result_creds;
246     result_creds = NULL;
247
248 cleanup:
249     krb5_free_authdata(context, in_creds.authdata);
250     krb5_free_cred_contents(context, &evidence_creds);
251     krb5_free_creds(context, result_creds);
252
253     return code;
254 }
255 struct gss_checksum_data {
256     krb5_gss_ctx_id_rec *ctx;
257     krb5_gss_cred_id_t cred;
258     krb5_checksum md5;
259     krb5_data checksum_data;
260     krb5_gss_ctx_ext_t exts;
261 };
262
263 #ifdef CFX_EXERCISE
264 #include "../../krb5/krb/auth_con.h"
265 #endif
266 static krb5_error_code KRB5_CALLCONV
267 make_gss_checksum (krb5_context context, krb5_auth_context auth_context,
268                    void *cksum_data, krb5_data **out)
269 {
270     krb5_error_code code;
271     krb5_int32 con_flags;
272     unsigned char *ptr;
273     struct gss_checksum_data *data = cksum_data;
274     krb5_data credmsg;
275     unsigned int junk;
276     krb5_data *finished = NULL;
277     krb5_key send_subkey;
278
279     data->checksum_data.data = 0;
280     credmsg.data = 0;
281     /* build the checksum field */
282
283     if (data->ctx->gss_flags & GSS_C_DELEG_FLAG) {
284         /* first get KRB_CRED message, so we know its length */
285
286         /* clear the time check flag that was set in krb5_auth_con_init() */
287         krb5_auth_con_getflags(context, auth_context, &con_flags);
288         krb5_auth_con_setflags(context, auth_context,
289                                con_flags & ~KRB5_AUTH_CONTEXT_DO_TIME);
290
291         assert(data->cred->name != NULL);
292
293         /*
294          * RFC 4121 4.1.1 specifies forwarded credentials must be encrypted in
295          * the session key, but krb5_fwd_tgt_creds will use the send subkey if
296          * it's set in the auth context.  Suppress the send subkey
297          * temporarily.
298          */
299         krb5_auth_con_getsendsubkey_k(context, auth_context, &send_subkey);
300         krb5_auth_con_setsendsubkey_k(context, auth_context, NULL);
301
302         code = krb5_fwd_tgt_creds(context, auth_context, 0,
303                                   data->cred->name->princ, data->ctx->there->princ,
304                                   data->cred->ccache, 1,
305                                   &credmsg);
306
307         /* Turn KRB5_AUTH_CONTEXT_DO_TIME back on and reset the send subkey. */
308         krb5_auth_con_setflags(context, auth_context, con_flags);
309         krb5_auth_con_setsendsubkey_k(context, auth_context, send_subkey);
310         krb5_k_free_key(context, send_subkey);
311
312         if (code) {
313             /* don't fail here; just don't accept/do the delegation
314                request */
315             data->ctx->gss_flags &= ~(GSS_C_DELEG_FLAG |
316                                       GSS_C_DELEG_POLICY_FLAG);
317
318             data->checksum_data.length = 24;
319         } else {
320             if (credmsg.length+28 > KRB5_INT16_MAX) {
321                 code = KRB5KRB_ERR_FIELD_TOOLONG;
322                 goto cleanup;
323             }
324
325             data->checksum_data.length = 28+credmsg.length;
326         }
327     } else {
328         data->checksum_data.length = 24;
329     }
330 #ifdef CFX_EXERCISE
331     if (data->ctx->auth_context->keyblock != NULL
332         && data->ctx->auth_context->keyblock->enctype == 18) {
333         srand(time(0) ^ getpid());
334         /* Our ftp client code stupidly assumes a base64-encoded
335            version of the token will fit in 10K, so don't make this
336            too big.  */
337         junk = rand() & 0xff;
338     } else
339         junk = 0;
340 #else
341     junk = 0;
342 #endif
343
344     assert(data->exts != NULL);
345
346     if (data->exts->iakerb.conv) {
347         krb5_key key;
348
349         code = krb5_auth_con_getsendsubkey_k(context, auth_context, &key);
350         if (code != 0)
351             goto cleanup;
352
353         code = iakerb_make_finished(context, key, data->exts->iakerb.conv,
354                                     &finished);
355         if (code != 0) {
356             krb5_k_free_key(context, key);
357             goto cleanup;
358         }
359
360         krb5_k_free_key(context, key);
361         data->checksum_data.length += 8 + finished->length;
362     }
363
364     data->checksum_data.length += junk;
365
366     /* now allocate a buffer to hold the checksum data and
367        (maybe) KRB_CRED msg */
368
369     if ((data->checksum_data.data =
370          (char *) xmalloc(data->checksum_data.length)) == NULL) {
371         code = ENOMEM;
372         goto cleanup;
373     }
374
375     ptr = (unsigned char *)data->checksum_data.data;
376
377     TWRITE_INT(ptr, data->md5.length, 0);
378     TWRITE_STR(ptr, data->md5.contents, data->md5.length);
379     TWRITE_INT(ptr, data->ctx->gss_flags, 0);
380
381     /* done with this, free it */
382     xfree(data->md5.contents);
383
384     if (credmsg.data) {
385         TWRITE_INT16(ptr, KRB5_GSS_FOR_CREDS_OPTION, 0);
386         TWRITE_INT16(ptr, credmsg.length, 0);
387         TWRITE_STR(ptr, credmsg.data, credmsg.length);
388     }
389     if (data->exts->iakerb.conv) {
390         TWRITE_INT(ptr, KRB5_GSS_EXTS_IAKERB_FINISHED, 1);
391         TWRITE_INT(ptr, finished->length, 1);
392         TWRITE_STR(ptr, finished->data, finished->length);
393     }
394     if (junk)
395         memset(ptr, 'i', junk);
396     *out = &data->checksum_data;
397     code = 0;
398 cleanup:
399     krb5_free_data_contents(context, &credmsg);
400     krb5_free_data(context, finished);
401     return code;
402 }
403
404 static krb5_error_code
405 make_ap_req_v1(context, ctx, cred, k_cred, ad_context,
406                chan_bindings, mech_type, token, exts)
407     krb5_context context;
408     krb5_gss_ctx_id_rec *ctx;
409     krb5_gss_cred_id_t cred;
410     krb5_creds *k_cred;
411     krb5_authdata_context ad_context;
412     gss_channel_bindings_t chan_bindings;
413     gss_OID mech_type;
414     gss_buffer_t token;
415     krb5_gss_ctx_ext_t exts;
416 {
417     krb5_flags mk_req_flags = 0;
418     krb5_error_code code;
419     struct gss_checksum_data cksum_struct;
420     krb5_checksum md5;
421     krb5_data ap_req;
422     unsigned char *ptr;
423     unsigned char *t;
424     unsigned int tlen;
425
426     k5_mutex_assert_locked(&cred->lock);
427     ap_req.data = 0;
428
429     /* compute the hash of the channel bindings */
430
431     if ((code = kg_checksum_channel_bindings(context, chan_bindings, &md5, 0)))
432         return(code);
433
434     krb5_auth_con_set_req_cksumtype(context, ctx->auth_context,
435                                     CKSUMTYPE_KG_CB);
436     cksum_struct.md5 = md5;
437     cksum_struct.ctx = ctx;
438     cksum_struct.cred = cred;
439     cksum_struct.checksum_data.data = NULL;
440     cksum_struct.exts = exts;
441     krb5_auth_con_set_checksum_func(context, ctx->auth_context,
442                                     make_gss_checksum, &cksum_struct);
443
444     /* call mk_req.  subkey and ap_req need to be used or destroyed */
445
446     mk_req_flags = AP_OPTS_USE_SUBKEY;
447
448     if (ctx->gss_flags & GSS_C_MUTUAL_FLAG)
449         mk_req_flags |= AP_OPTS_MUTUAL_REQUIRED | AP_OPTS_ETYPE_NEGOTIATION;
450
451     krb5_auth_con_set_authdata_context(context, ctx->auth_context, ad_context);
452     code = krb5_mk_req_extended(context, &ctx->auth_context, mk_req_flags,
453                                 NULL, k_cred, &ap_req);
454     krb5_auth_con_set_authdata_context(context, ctx->auth_context, NULL);
455     krb5_free_data_contents(context, &cksum_struct.checksum_data);
456     if (code)
457         goto cleanup;
458
459     /* store the interesting stuff from creds and authent */
460     ctx->krb_times = k_cred->times;
461     ctx->krb_flags = k_cred->ticket_flags;
462
463     /* build up the token */
464     if (ctx->gss_flags & GSS_C_DCE_STYLE) {
465         /*
466          * For DCE RPC, do not encapsulate the AP-REQ in the
467          * typical GSS wrapping.
468          */
469         code = data_to_gss(&ap_req, token);
470         if (code)
471             goto cleanup;
472     } else {
473         /* allocate space for the token */
474         tlen = g_token_size((gss_OID) mech_type, ap_req.length);
475
476         if ((t = (unsigned char *) gssalloc_malloc(tlen)) == NULL) {
477             code = ENOMEM;
478             goto cleanup;
479         }
480
481         /* fill in the buffer */
482         ptr = t;
483
484         g_make_token_header(mech_type, ap_req.length,
485                             &ptr, KG_TOK_CTX_AP_REQ);
486
487         TWRITE_STR(ptr, ap_req.data, ap_req.length);
488
489         /* pass it back */
490
491         token->length = tlen;
492         token->value = (void *) t;
493     }
494
495     code = 0;
496
497 cleanup:
498     if (ap_req.data)
499         krb5_free_data_contents(context, &ap_req);
500
501     return (code);
502 }
503
504 /*
505  * new_connection
506  *
507  * Do the grunt work of setting up a new context.
508  */
509 static OM_uint32
510 kg_new_connection(
511     OM_uint32 *minor_status,
512     krb5_gss_cred_id_t cred,
513     gss_ctx_id_t *context_handle,
514     gss_name_t target_name,
515     gss_OID mech_type,
516     OM_uint32 req_flags,
517     OM_uint32 time_req,
518     gss_channel_bindings_t input_chan_bindings,
519     gss_buffer_t input_token,
520     gss_OID *actual_mech_type,
521     gss_buffer_t output_token,
522     OM_uint32 *ret_flags,
523     OM_uint32 *time_rec,
524     krb5_context context,
525     krb5_gss_ctx_ext_t exts)
526 {
527     OM_uint32 major_status;
528     krb5_error_code code;
529     krb5_creds *k_cred = NULL;
530     krb5_gss_ctx_id_rec *ctx, *ctx_free;
531     krb5_timestamp now;
532     gss_buffer_desc token;
533     krb5_keyblock *keyblock;
534
535     k5_mutex_assert_locked(&cred->lock);
536     major_status = GSS_S_FAILURE;
537     token.length = 0;
538     token.value = NULL;
539
540     /* make sure the cred is usable for init */
541
542     if ((cred->usage != GSS_C_INITIATE) &&
543         (cred->usage != GSS_C_BOTH)) {
544         *minor_status = 0;
545         return(GSS_S_NO_CRED);
546     }
547
548     /* complain if the input token is non-null */
549
550     if (input_token != GSS_C_NO_BUFFER && input_token->length != 0) {
551         *minor_status = 0;
552         return(GSS_S_DEFECTIVE_TOKEN);
553     }
554
555     /* create the ctx */
556
557     if ((ctx = (krb5_gss_ctx_id_rec *) xmalloc(sizeof(krb5_gss_ctx_id_rec)))
558         == NULL) {
559         *minor_status = ENOMEM;
560         return(GSS_S_FAILURE);
561     }
562
563     /* fill in the ctx */
564     memset(ctx, 0, sizeof(krb5_gss_ctx_id_rec));
565     ctx->magic = KG_CONTEXT;
566     ctx_free = ctx;
567     if ((code = krb5_auth_con_init(context, &ctx->auth_context)))
568         goto cleanup;
569     krb5_auth_con_setflags(context, ctx->auth_context,
570                            KRB5_AUTH_CONTEXT_DO_SEQUENCE);
571
572     /* limit the encryption types negotiated (if requested) */
573     if (cred->req_enctypes) {
574         if ((code = krb5_set_default_tgs_enctypes(context,
575                                                   cred->req_enctypes))) {
576             goto cleanup;
577         }
578     }
579
580     ctx->initiate = 1;
581     ctx->gss_flags = (GSS_C_INTEG_FLAG | GSS_C_CONF_FLAG |
582                       GSS_C_TRANS_FLAG |
583                       ((req_flags) & (GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG |
584                                       GSS_C_SEQUENCE_FLAG | GSS_C_DELEG_FLAG |
585                                       GSS_C_DCE_STYLE | GSS_C_IDENTIFY_FLAG |
586                                       GSS_C_EXTENDED_ERROR_FLAG)));
587     ctx->seed_init = 0;
588     ctx->big_endian = 0;  /* all initiators do little-endian, as per spec */
589     ctx->seqstate = 0;
590
591     if (req_flags & GSS_C_DCE_STYLE)
592         ctx->gss_flags |= GSS_C_MUTUAL_FLAG;
593
594     if ((code = krb5_timeofday(context, &now)))
595         goto cleanup;
596
597     if (time_req == 0 || time_req == GSS_C_INDEFINITE) {
598         ctx->krb_times.endtime = 0;
599     } else {
600         ctx->krb_times.endtime = now + time_req;
601     }
602
603     if ((code = kg_duplicate_name(context, cred->name, &ctx->here)))
604         goto cleanup;
605
606     if ((code = kg_duplicate_name(context, (krb5_gss_name_t)target_name,
607                                   &ctx->there)))
608         goto cleanup;
609
610     code = get_credentials(context, cred, ctx->there, now,
611                            ctx->krb_times.endtime, &k_cred);
612     if (code)
613         goto cleanup;
614
615     ctx->krb_times = k_cred->times;
616
617     /*
618      * GSS_C_DELEG_POLICY_FLAG means to delegate only if the
619      * ok-as-delegate ticket flag is set.
620      */
621     if ((req_flags & GSS_C_DELEG_POLICY_FLAG)
622         && (k_cred->ticket_flags & TKT_FLG_OK_AS_DELEGATE))
623         ctx->gss_flags |= GSS_C_DELEG_FLAG | GSS_C_DELEG_POLICY_FLAG;
624
625     if (generic_gss_copy_oid(minor_status, mech_type, &ctx->mech_used)
626         != GSS_S_COMPLETE) {
627         code = *minor_status;
628         goto cleanup;
629     }
630     /*
631      * Now try to make it static if at all possible....
632      */
633     ctx->mech_used = krb5_gss_convert_static_mech_oid(ctx->mech_used);
634
635     {
636         /* gsskrb5 v1 */
637         krb5_int32 seq_temp;
638         if ((code = make_ap_req_v1(context, ctx,
639                                    cred, k_cred, ctx->here->ad_context,
640                                    input_chan_bindings,
641                                    mech_type, &token, exts))) {
642             if ((code == KRB5_FCC_NOFILE) || (code == KRB5_CC_NOTFOUND) ||
643                 (code == KG_EMPTY_CCACHE))
644                 major_status = GSS_S_NO_CRED;
645             if (code == KRB5KRB_AP_ERR_TKT_EXPIRED)
646                 major_status = GSS_S_CREDENTIALS_EXPIRED;
647             goto cleanup;
648         }
649
650         krb5_auth_con_getlocalseqnumber(context, ctx->auth_context, &seq_temp);
651         ctx->seq_send = seq_temp;
652         code = krb5_auth_con_getsendsubkey(context, ctx->auth_context,
653                                            &keyblock);
654         if (code != 0)
655             goto cleanup;
656         code = krb5_k_create_key(context, keyblock, &ctx->subkey);
657         krb5_free_keyblock(context, keyblock);
658         if (code != 0)
659             goto cleanup;
660     }
661
662     ctx->enc = NULL;
663     ctx->seq = NULL;
664     ctx->have_acceptor_subkey = 0;
665     code = kg_setup_keys(context, ctx, ctx->subkey, &ctx->cksumtype);
666     if (code != 0)
667         goto cleanup;
668
669     /* compute time_rec */
670     if (time_rec) {
671         if ((code = krb5_timeofday(context, &now)))
672             goto cleanup;
673         *time_rec = ctx->krb_times.endtime - now;
674     }
675
676     /* set the other returns */
677     *output_token = token;
678
679     if (ret_flags)
680         *ret_flags = ctx->gss_flags;
681
682     if (actual_mech_type)
683         *actual_mech_type = mech_type;
684
685     /* return successfully */
686
687     *context_handle = (gss_ctx_id_t) ctx;
688     ctx_free = NULL;
689     if (ctx->gss_flags & GSS_C_MUTUAL_FLAG) {
690         ctx->established = 0;
691         major_status = GSS_S_CONTINUE_NEEDED;
692     } else {
693         ctx->seq_recv = ctx->seq_send;
694         g_order_init(&(ctx->seqstate), ctx->seq_recv,
695                      (ctx->gss_flags & GSS_C_REPLAY_FLAG) != 0,
696                      (ctx->gss_flags & GSS_C_SEQUENCE_FLAG) != 0, ctx->proto);
697         ctx->gss_flags |= GSS_C_PROT_READY_FLAG;
698         ctx->established = 1;
699         major_status = GSS_S_COMPLETE;
700     }
701
702 cleanup:
703     krb5_free_creds(context, k_cred);
704     if (ctx_free) {
705         if (ctx_free->auth_context)
706             krb5_auth_con_free(context, ctx_free->auth_context);
707         if (ctx_free->here)
708             kg_release_name(context, &ctx_free->here);
709         if (ctx_free->there)
710             kg_release_name(context, &ctx_free->there);
711         if (ctx_free->subkey)
712             krb5_k_free_key(context, ctx_free->subkey);
713         xfree(ctx_free);
714     }
715
716     *minor_status = code;
717     return (major_status);
718 }
719
720 /*
721  * mutual_auth
722  *
723  * Handle the reply from the acceptor, if we're doing mutual auth.
724  */
725 static OM_uint32
726 mutual_auth(
727     OM_uint32 *minor_status,
728     gss_ctx_id_t *context_handle,
729     gss_name_t target_name,
730     gss_OID mech_type,
731     OM_uint32 req_flags,
732     OM_uint32 time_req,
733     gss_channel_bindings_t input_chan_bindings,
734     gss_buffer_t input_token,
735     gss_OID *actual_mech_type,
736     gss_buffer_t output_token,
737     OM_uint32 *ret_flags,
738     OM_uint32 *time_rec,
739     krb5_context context)
740 {
741     OM_uint32 major_status;
742     unsigned char *ptr;
743     char *sptr;
744     krb5_data ap_rep;
745     krb5_ap_rep_enc_part *ap_rep_data;
746     krb5_timestamp now;
747     krb5_gss_ctx_id_rec *ctx;
748     krb5_error *krb_error;
749     krb5_error_code code;
750     krb5int_access kaccess;
751
752     major_status = GSS_S_FAILURE;
753
754     code = krb5int_accessor (&kaccess, KRB5INT_ACCESS_VERSION);
755     if (code)
756         goto fail;
757
758     ctx = (krb5_gss_ctx_id_t) *context_handle;
759
760     /* make sure the context is non-established, and that certain
761        arguments are unchanged */
762
763     if ((ctx->established) ||
764         ((ctx->gss_flags & GSS_C_MUTUAL_FLAG) == 0)) {
765         code = KG_CONTEXT_ESTABLISHED;
766         goto fail;
767     }
768
769     if (! kg_compare_name(context, ctx->there, (krb5_gss_name_t)target_name)) {
770         (void)krb5_gss_delete_sec_context(minor_status,
771                                           context_handle, NULL);
772         code = 0;
773         major_status = GSS_S_BAD_NAME;
774         goto fail;
775     }
776
777     /* verify the token and leave the AP_REP message in ap_rep */
778
779     if (input_token == GSS_C_NO_BUFFER) {
780         (void)krb5_gss_delete_sec_context(minor_status,
781                                           context_handle, NULL);
782         code = 0;
783         major_status = GSS_S_DEFECTIVE_TOKEN;
784         goto fail;
785     }
786
787     ptr = (unsigned char *) input_token->value;
788
789     if (ctx->gss_flags & GSS_C_DCE_STYLE) {
790         /* Raw AP-REP */
791         ap_rep.length = input_token->length;
792         ap_rep.data = (char *)input_token->value;
793     } else if (g_verify_token_header(ctx->mech_used,
794                                      &(ap_rep.length),
795                                      &ptr, KG_TOK_CTX_AP_REP,
796                                      input_token->length, 1)) {
797         if (g_verify_token_header((gss_OID) ctx->mech_used,
798                                   &(ap_rep.length),
799                                   &ptr, KG_TOK_CTX_ERROR,
800                                   input_token->length, 1) == 0) {
801
802             /* Handle a KRB_ERROR message from the server */
803
804             sptr = (char *) ptr;           /* PC compiler bug */
805             TREAD_STR(sptr, ap_rep.data, ap_rep.length);
806
807             code = krb5_rd_error(context, &ap_rep, &krb_error);
808             if (code)
809                 goto fail;
810             if (krb_error->error)
811                 code = (krb5_error_code)krb_error->error + ERROR_TABLE_BASE_krb5;
812             else
813                 code = 0;
814             krb5_free_error(context, krb_error);
815             goto fail;
816         } else {
817             *minor_status = 0;
818             return(GSS_S_DEFECTIVE_TOKEN);
819         }
820     }
821
822     sptr = (char *) ptr;                      /* PC compiler bug */
823     TREAD_STR(sptr, ap_rep.data, ap_rep.length);
824
825     /* decode the ap_rep */
826     if ((code = krb5_rd_rep(context, ctx->auth_context, &ap_rep,
827                             &ap_rep_data))) {
828         /*
829          * XXX A hack for backwards compatiblity.
830          * To be removed in 1999 -- proven
831          */
832         krb5_auth_con_setuseruserkey(context, ctx->auth_context,
833                                      &ctx->subkey->keyblock);
834         if ((krb5_rd_rep(context, ctx->auth_context, &ap_rep,
835                          &ap_rep_data)))
836             goto fail;
837     }
838
839     /* store away the sequence number */
840     ctx->seq_recv = ap_rep_data->seq_number;
841     g_order_init(&(ctx->seqstate), ctx->seq_recv,
842                  (ctx->gss_flags & GSS_C_REPLAY_FLAG) != 0,
843                  (ctx->gss_flags & GSS_C_SEQUENCE_FLAG) !=0, ctx->proto);
844
845     if (ap_rep_data->subkey != NULL &&
846         (ctx->proto == 1 || (ctx->gss_flags & GSS_C_DCE_STYLE) ||
847          ap_rep_data->subkey->enctype != ctx->subkey->keyblock.enctype)) {
848         /* Keep acceptor's subkey.  */
849         ctx->have_acceptor_subkey = 1;
850         code = krb5_k_create_key(context, ap_rep_data->subkey,
851                                  &ctx->acceptor_subkey);
852         if (code) {
853             krb5_free_ap_rep_enc_part(context, ap_rep_data);
854             goto fail;
855         }
856         code = kg_setup_keys(context, ctx, ctx->acceptor_subkey,
857                              &ctx->acceptor_subkey_cksumtype);
858         if (code) {
859             krb5_free_ap_rep_enc_part(context, ap_rep_data);
860             goto fail;
861         }
862     }
863     /* free the ap_rep_data */
864     krb5_free_ap_rep_enc_part(context, ap_rep_data);
865
866     if (ctx->gss_flags & GSS_C_DCE_STYLE) {
867         krb5_data outbuf;
868
869         code = krb5_mk_rep_dce(context, ctx->auth_context, &outbuf);
870         if (code)
871             goto fail;
872
873         code = data_to_gss(&outbuf, output_token);
874         if (code)
875             goto fail;
876     }
877
878     /* set established */
879     ctx->established = 1;
880
881     /* set returns */
882
883     if (time_rec) {
884         if ((code = krb5_timeofday(context, &now)))
885             goto fail;
886         *time_rec = ctx->krb_times.endtime - now;
887     }
888
889     if (ret_flags)
890         *ret_flags = ctx->gss_flags;
891
892     if (actual_mech_type)
893         *actual_mech_type = mech_type;
894
895     /* success */
896
897     *minor_status = 0;
898     return GSS_S_COMPLETE;
899
900 fail:
901     (void)krb5_gss_delete_sec_context(minor_status, context_handle, NULL);
902
903     *minor_status = code;
904     return (major_status);
905 }
906
907 OM_uint32
908 krb5_gss_init_sec_context_ext(
909     OM_uint32 *minor_status,
910     gss_cred_id_t claimant_cred_handle,
911     gss_ctx_id_t *context_handle,
912     gss_name_t target_name,
913     gss_OID mech_type,
914     OM_uint32 req_flags,
915     OM_uint32 time_req,
916     gss_channel_bindings_t input_chan_bindings,
917     gss_buffer_t input_token,
918     gss_OID *actual_mech_type,
919     gss_buffer_t output_token,
920     OM_uint32 *ret_flags,
921     OM_uint32 *time_rec,
922     krb5_gss_ctx_ext_t exts)
923 {
924     krb5_context context;
925     gss_cred_id_t defcred = GSS_C_NO_CREDENTIAL;
926     krb5_gss_cred_id_t cred;
927     krb5_error_code kerr;
928     OM_uint32 major_status;
929     OM_uint32 tmp_min_stat;
930
931     if (*context_handle == GSS_C_NO_CONTEXT) {
932         kerr = krb5_gss_init_context(&context);
933         if (kerr) {
934             *minor_status = kerr;
935             return GSS_S_FAILURE;
936         }
937         if (GSS_ERROR(kg_sync_ccache_name(context, minor_status))) {
938             save_error_info(*minor_status, context);
939             krb5_free_context(context);
940             return GSS_S_FAILURE;
941         }
942     } else {
943         context = ((krb5_gss_ctx_id_rec *)*context_handle)->k5_context;
944     }
945
946     /* set up return values so they can be "freed" successfully */
947
948     major_status = GSS_S_FAILURE; /* Default major code */
949     output_token->length = 0;
950     output_token->value = NULL;
951     if (actual_mech_type)
952         *actual_mech_type = NULL;
953
954     /* verify the credential, or use the default */
955     /*SUPPRESS 29*/
956     if (claimant_cred_handle == GSS_C_NO_CREDENTIAL) {
957         major_status = kg_get_defcred(minor_status, &defcred);
958         if (major_status && GSS_ERROR(major_status)) {
959             if (*context_handle == GSS_C_NO_CONTEXT)
960                 krb5_free_context(context);
961             return(major_status);
962         }
963         claimant_cred_handle = defcred;
964     }
965
966     major_status = kg_cred_resolve(minor_status, context, claimant_cred_handle,
967                                    target_name);
968     if (GSS_ERROR(major_status)) {
969         save_error_info(*minor_status, context);
970         krb5_gss_release_cred(&tmp_min_stat, &defcred);
971         if (*context_handle == GSS_C_NO_CONTEXT)
972             krb5_free_context(context);
973         return(major_status);
974     }
975     cred = (krb5_gss_cred_id_t)claimant_cred_handle;
976
977     /* verify the mech_type */
978
979     if (mech_type == GSS_C_NULL_OID || g_OID_equal(mech_type, gss_mech_krb5)) {
980         mech_type = (gss_OID) gss_mech_krb5;
981     } else if (g_OID_equal(mech_type, gss_mech_krb5_old)) {
982         mech_type = (gss_OID) gss_mech_krb5_old;
983     } else if (g_OID_equal(mech_type, gss_mech_krb5_wrong)) {
984         mech_type = (gss_OID) gss_mech_krb5_wrong;
985     } else if (g_OID_equal(mech_type, gss_mech_iakerb)) {
986         mech_type = (gss_OID) gss_mech_iakerb;
987     } else {
988         k5_mutex_unlock(&cred->lock);
989         krb5_gss_release_cred(minor_status, &defcred);
990         *minor_status = 0;
991         if (*context_handle == GSS_C_NO_CONTEXT)
992             krb5_free_context(context);
993         return(GSS_S_BAD_MECH);
994     }
995
996     /* is this a new connection or not? */
997
998     /*SUPPRESS 29*/
999     if (*context_handle == GSS_C_NO_CONTEXT) {
1000         major_status = kg_new_connection(minor_status, cred, context_handle,
1001                                          target_name, mech_type, req_flags,
1002                                          time_req, input_chan_bindings,
1003                                          input_token, actual_mech_type,
1004                                          output_token, ret_flags, time_rec,
1005                                          context, exts);
1006         k5_mutex_unlock(&cred->lock);
1007         if (*context_handle == GSS_C_NO_CONTEXT) {
1008             save_error_info (*minor_status, context);
1009             krb5_free_context(context);
1010         } else
1011             ((krb5_gss_ctx_id_rec *) *context_handle)->k5_context = context;
1012     } else {
1013         /* mutual_auth doesn't care about the credentials */
1014         k5_mutex_unlock(&cred->lock);
1015         major_status = mutual_auth(minor_status, context_handle,
1016                                    target_name, mech_type, req_flags,
1017                                    time_req, input_chan_bindings,
1018                                    input_token, actual_mech_type,
1019                                    output_token, ret_flags, time_rec,
1020                                    context);
1021         /* If context_handle is now NO_CONTEXT, mutual_auth called
1022            delete_sec_context, which would've zapped the krb5 context
1023            too.  */
1024     }
1025
1026     krb5_gss_release_cred(&tmp_min_stat, &defcred);
1027     return(major_status);
1028 }
1029
1030 #ifndef _WIN32
1031 k5_mutex_t kg_kdc_flag_mutex = K5_MUTEX_PARTIAL_INITIALIZER;
1032 static int kdc_flag = 0;
1033 #endif
1034
1035 krb5_error_code
1036 krb5_gss_init_context (krb5_context *ctxp)
1037 {
1038     krb5_error_code err;
1039 #ifndef _WIN32
1040     int is_kdc;
1041 #endif
1042
1043     err = gss_krb5int_initialize_library();
1044     if (err)
1045         return err;
1046 #ifndef _WIN32
1047     err = k5_mutex_lock(&kg_kdc_flag_mutex);
1048     if (err)
1049         return err;
1050     is_kdc = kdc_flag;
1051     k5_mutex_unlock(&kg_kdc_flag_mutex);
1052
1053     if (is_kdc)
1054         return krb5int_init_context_kdc(ctxp);
1055 #endif
1056
1057     return krb5_init_context(ctxp);
1058 }
1059
1060 #ifndef _WIN32
1061 OM_uint32
1062 krb5int_gss_use_kdc_context(OM_uint32 *minor_status,
1063                             const gss_OID desired_mech,
1064                             const gss_OID desired_object,
1065                             gss_buffer_t value)
1066 {
1067     OM_uint32 err;
1068
1069     *minor_status = 0;
1070
1071     err = gss_krb5int_initialize_library();
1072     if (err)
1073         return err;
1074     *minor_status = k5_mutex_lock(&kg_kdc_flag_mutex);
1075     if (*minor_status) {
1076         return GSS_S_FAILURE;
1077     }
1078     kdc_flag = 1;
1079     k5_mutex_unlock(&kg_kdc_flag_mutex);
1080     return GSS_S_COMPLETE;
1081 }
1082 #endif
1083
1084 OM_uint32 KRB5_CALLCONV
1085 krb5_gss_init_sec_context(minor_status, claimant_cred_handle,
1086                           context_handle, target_name, mech_type,
1087                           req_flags, time_req, input_chan_bindings,
1088                           input_token, actual_mech_type, output_token,
1089                           ret_flags, time_rec)
1090     OM_uint32 *minor_status;
1091     gss_cred_id_t claimant_cred_handle;
1092     gss_ctx_id_t *context_handle;
1093     gss_name_t target_name;
1094     gss_OID mech_type;
1095     OM_uint32 req_flags;
1096     OM_uint32 time_req;
1097     gss_channel_bindings_t input_chan_bindings;
1098     gss_buffer_t input_token;
1099     gss_OID *actual_mech_type;
1100     gss_buffer_t output_token;
1101     OM_uint32 *ret_flags;
1102     OM_uint32 *time_rec;
1103 {
1104     krb5_gss_ctx_ext_rec exts;
1105
1106     memset(&exts, 0, sizeof(exts));
1107
1108     return krb5_gss_init_sec_context_ext(minor_status,
1109                                          claimant_cred_handle,
1110                                          context_handle,
1111                                          target_name,
1112                                          mech_type,
1113                                          req_flags,
1114                                          time_req,
1115                                          input_chan_bindings,
1116                                          input_token,
1117                                          actual_mech_type,
1118                                          output_token,
1119                                          ret_flags,
1120                                          time_rec,
1121                                          &exts);
1122 }