Imported Upstream version 1.17
[platform/upstream/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, mcreds, *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     in_creds.client = cred->name->princ;
143     in_creds.server = server->princ;
144     in_creds.times.endtime = endtime;
145     in_creds.authdata = NULL;
146     in_creds.keyblock.enctype = 0;
147
148     /*
149      * cred->name is immutable, so there is no need to acquire
150      * cred->name->lock.
151      */
152     if (cred->name->ad_context != NULL) {
153         code = krb5_authdata_export_authdata(context,
154                                              cred->name->ad_context,
155                                              AD_USAGE_TGS_REQ,
156                                              &in_creds.authdata);
157         if (code != 0)
158             goto cleanup;
159     }
160
161     /*
162      * For IAKERB or constrained delegation, only check the cache in this step.
163      * For IAKERB we will ask the server to make any necessary TGS requests;
164      * for constrained delegation we will adjust in_creds and make an S4U2Proxy
165      * request below if the cache lookup fails.
166      */
167     if (cred->impersonator != NULL || cred->iakerb_mech)
168         flags |= KRB5_GC_CACHED;
169
170     code = krb5_get_credentials(context, flags, cred->ccache,
171                                 &in_creds, &result_creds);
172
173     /*
174      * Try constrained delegation if we have proxy credentials, unless
175      * we are trying to get a ticket to ourselves (in which case we could
176      * just use the evidence ticket directly from cache).
177      */
178     if (code == KRB5_CC_NOTFOUND && cred->impersonator != NULL &&
179         !cred->iakerb_mech &&
180         !krb5_principal_compare(context, cred->impersonator, server->princ)) {
181
182         memset(&mcreds, 0, sizeof(mcreds));
183         mcreds.magic = KV5M_CREDS;
184         mcreds.server = cred->impersonator;
185         mcreds.client = cred->name->princ;
186         code = krb5_cc_retrieve_cred(context, cred->ccache,
187                                      KRB5_TC_MATCH_AUTHDATA, &mcreds,
188                                      &evidence_creds);
189         if (code)
190             goto cleanup;
191
192         assert(evidence_creds.ticket_flags & TKT_FLG_FORWARDABLE);
193         in_creds.client = cred->impersonator;
194         in_creds.second_ticket = evidence_creds.ticket;
195         flags = KRB5_GC_CANONICALIZE | KRB5_GC_CONSTRAINED_DELEGATION;
196         code = krb5_get_credentials(context, flags, cred->ccache,
197                                     &in_creds, &result_creds);
198     }
199
200     if (code)
201         goto cleanup;
202
203     if (flags & KRB5_GC_CONSTRAINED_DELEGATION) {
204         if (!krb5_principal_compare(context, cred->name->princ,
205                                     result_creds->client)) {
206             /* server did not support constrained delegation */
207             code = KRB5_KDCREP_MODIFIED;
208             goto cleanup;
209         }
210     }
211
212     /*
213      * Enforce a stricter limit (without timeskew forgiveness at the
214      * boundaries) because accept_sec_context code is also similarly
215      * non-forgiving.
216      */
217     if (!krb5_gss_dbg_client_expcreds &&
218         ts_after(now, result_creds->times.endtime)) {
219         code = KRB5KRB_AP_ERR_TKT_EXPIRED;
220         goto cleanup;
221     }
222
223     *out_creds = result_creds;
224     result_creds = NULL;
225
226 cleanup:
227     krb5_free_authdata(context, in_creds.authdata);
228     krb5_free_cred_contents(context, &evidence_creds);
229     krb5_free_creds(context, result_creds);
230
231     return code;
232 }
233 struct gss_checksum_data {
234     krb5_gss_ctx_id_rec *ctx;
235     krb5_gss_cred_id_t cred;
236     krb5_checksum md5;
237     krb5_data checksum_data;
238     krb5_gss_ctx_ext_t exts;
239 };
240
241 #ifdef CFX_EXERCISE
242 #include "../../krb5/krb/auth_con.h"
243 #endif
244 static krb5_error_code KRB5_CALLCONV
245 make_gss_checksum (krb5_context context, krb5_auth_context auth_context,
246                    void *cksum_data, krb5_data **out)
247 {
248     krb5_error_code code;
249     krb5_int32 con_flags;
250     unsigned char *ptr;
251     struct gss_checksum_data *data = cksum_data;
252     krb5_data credmsg;
253     unsigned int junk;
254     krb5_data *finished = NULL;
255     krb5_key send_subkey;
256
257     data->checksum_data.data = 0;
258     credmsg.data = 0;
259     /* build the checksum field */
260
261     if (data->ctx->gss_flags & GSS_C_DELEG_FLAG) {
262         /* first get KRB_CRED message, so we know its length */
263
264         /* clear the time check flag that was set in krb5_auth_con_init() */
265         krb5_auth_con_getflags(context, auth_context, &con_flags);
266         krb5_auth_con_setflags(context, auth_context,
267                                con_flags & ~KRB5_AUTH_CONTEXT_DO_TIME);
268
269         assert(data->cred->name != NULL);
270
271         /*
272          * RFC 4121 4.1.1 specifies forwarded credentials must be encrypted in
273          * the session key, but krb5_fwd_tgt_creds will use the send subkey if
274          * it's set in the auth context.  Suppress the send subkey
275          * temporarily.
276          */
277         krb5_auth_con_getsendsubkey_k(context, auth_context, &send_subkey);
278         krb5_auth_con_setsendsubkey_k(context, auth_context, NULL);
279
280         code = krb5_fwd_tgt_creds(context, auth_context, 0,
281                                   data->cred->name->princ, data->ctx->there->princ,
282                                   data->cred->ccache, 1,
283                                   &credmsg);
284
285         /* Turn KRB5_AUTH_CONTEXT_DO_TIME back on and reset the send subkey. */
286         krb5_auth_con_setflags(context, auth_context, con_flags);
287         krb5_auth_con_setsendsubkey_k(context, auth_context, send_subkey);
288         krb5_k_free_key(context, send_subkey);
289
290         if (code) {
291             /* don't fail here; just don't accept/do the delegation
292                request */
293             data->ctx->gss_flags &= ~(GSS_C_DELEG_FLAG |
294                                       GSS_C_DELEG_POLICY_FLAG);
295
296             data->checksum_data.length = 24;
297         } else {
298             if (credmsg.length+28 > KRB5_INT16_MAX) {
299                 code = KRB5KRB_ERR_FIELD_TOOLONG;
300                 goto cleanup;
301             }
302
303             data->checksum_data.length = 28+credmsg.length;
304         }
305     } else {
306         data->checksum_data.length = 24;
307     }
308 #ifdef CFX_EXERCISE
309     if (data->ctx->auth_context->keyblock != NULL
310         && data->ctx->auth_context->keyblock->enctype == 18) {
311         srand(time(0) ^ getpid());
312         /* Our ftp client code stupidly assumes a base64-encoded
313            version of the token will fit in 10K, so don't make this
314            too big.  */
315         junk = rand() & 0xff;
316     } else
317         junk = 0;
318 #else
319     junk = 0;
320 #endif
321
322     assert(data->exts != NULL);
323
324     if (data->exts->iakerb.conv) {
325         krb5_key key;
326
327         code = krb5_auth_con_getsendsubkey_k(context, auth_context, &key);
328         if (code != 0)
329             goto cleanup;
330
331         code = iakerb_make_finished(context, key, data->exts->iakerb.conv,
332                                     &finished);
333         if (code != 0) {
334             krb5_k_free_key(context, key);
335             goto cleanup;
336         }
337
338         krb5_k_free_key(context, key);
339         data->checksum_data.length += 8 + finished->length;
340     }
341
342     data->checksum_data.length += junk;
343
344     /* now allocate a buffer to hold the checksum data and
345        (maybe) KRB_CRED msg */
346
347     if ((data->checksum_data.data =
348          (char *) xmalloc(data->checksum_data.length)) == NULL) {
349         code = ENOMEM;
350         goto cleanup;
351     }
352
353     ptr = (unsigned char *)data->checksum_data.data;
354
355     TWRITE_INT(ptr, data->md5.length, 0);
356     TWRITE_STR(ptr, data->md5.contents, data->md5.length);
357     TWRITE_INT(ptr, data->ctx->gss_flags, 0);
358
359     if (credmsg.data) {
360         TWRITE_INT16(ptr, KRB5_GSS_FOR_CREDS_OPTION, 0);
361         TWRITE_INT16(ptr, credmsg.length, 0);
362         TWRITE_STR(ptr, credmsg.data, credmsg.length);
363     }
364     if (data->exts->iakerb.conv) {
365         TWRITE_INT(ptr, KRB5_GSS_EXTS_IAKERB_FINISHED, 1);
366         TWRITE_INT(ptr, finished->length, 1);
367         TWRITE_STR(ptr, finished->data, finished->length);
368     }
369     if (junk)
370         memset(ptr, 'i', junk);
371     *out = &data->checksum_data;
372     code = 0;
373 cleanup:
374     krb5_free_data_contents(context, &credmsg);
375     krb5_free_data(context, finished);
376     return code;
377 }
378
379 static krb5_error_code
380 make_ap_req_v1(context, ctx, cred, k_cred, ad_context,
381                chan_bindings, mech_type, token, exts)
382     krb5_context context;
383     krb5_gss_ctx_id_rec *ctx;
384     krb5_gss_cred_id_t cred;
385     krb5_creds *k_cred;
386     krb5_authdata_context ad_context;
387     gss_channel_bindings_t chan_bindings;
388     gss_OID mech_type;
389     gss_buffer_t token;
390     krb5_gss_ctx_ext_t exts;
391 {
392     krb5_flags mk_req_flags = 0;
393     krb5_error_code code;
394     struct gss_checksum_data cksum_struct;
395     krb5_checksum md5;
396     krb5_data ap_req;
397     unsigned char *ptr;
398     unsigned char *t;
399     unsigned int tlen;
400
401     k5_mutex_assert_locked(&cred->lock);
402     ap_req.data = 0;
403
404     /* compute the hash of the channel bindings */
405
406     if ((code = kg_checksum_channel_bindings(context, chan_bindings, &md5)))
407         return(code);
408
409     krb5_auth_con_set_req_cksumtype(context, ctx->auth_context,
410                                     CKSUMTYPE_KG_CB);
411     cksum_struct.md5 = md5;
412     cksum_struct.ctx = ctx;
413     cksum_struct.cred = cred;
414     cksum_struct.checksum_data.data = NULL;
415     cksum_struct.exts = exts;
416     krb5_auth_con_set_checksum_func(context, ctx->auth_context,
417                                     make_gss_checksum, &cksum_struct);
418
419     /* call mk_req.  subkey and ap_req need to be used or destroyed */
420
421     mk_req_flags = AP_OPTS_USE_SUBKEY;
422
423     if (ctx->gss_flags & GSS_C_MUTUAL_FLAG)
424         mk_req_flags |= AP_OPTS_MUTUAL_REQUIRED | AP_OPTS_ETYPE_NEGOTIATION;
425
426     krb5_auth_con_set_authdata_context(context, ctx->auth_context, ad_context);
427     code = krb5_mk_req_extended(context, &ctx->auth_context, mk_req_flags,
428                                 NULL, k_cred, &ap_req);
429     krb5_auth_con_set_authdata_context(context, ctx->auth_context, NULL);
430     krb5_free_checksum_contents(context, &cksum_struct.md5);
431     krb5_free_data_contents(context, &cksum_struct.checksum_data);
432     if (code)
433         goto cleanup;
434
435     /* store the interesting stuff from creds and authent */
436     ctx->krb_times = k_cred->times;
437     ctx->krb_flags = k_cred->ticket_flags;
438
439     /* build up the token */
440     if (ctx->gss_flags & GSS_C_DCE_STYLE) {
441         /*
442          * For DCE RPC, do not encapsulate the AP-REQ in the
443          * typical GSS wrapping.
444          */
445         code = data_to_gss(&ap_req, token);
446         if (code)
447             goto cleanup;
448     } else {
449         /* allocate space for the token */
450         tlen = g_token_size((gss_OID) mech_type, ap_req.length);
451
452         if ((t = (unsigned char *) gssalloc_malloc(tlen)) == NULL) {
453             code = ENOMEM;
454             goto cleanup;
455         }
456
457         /* fill in the buffer */
458         ptr = t;
459
460         g_make_token_header(mech_type, ap_req.length,
461                             &ptr, KG_TOK_CTX_AP_REQ);
462
463         TWRITE_STR(ptr, ap_req.data, ap_req.length);
464
465         /* pass it back */
466
467         token->length = tlen;
468         token->value = (void *) t;
469     }
470
471     code = 0;
472
473 cleanup:
474     if (ap_req.data)
475         krb5_free_data_contents(context, &ap_req);
476
477     return (code);
478 }
479
480 /*
481  * new_connection
482  *
483  * Do the grunt work of setting up a new context.
484  */
485 static OM_uint32
486 kg_new_connection(
487     OM_uint32 *minor_status,
488     krb5_gss_cred_id_t cred,
489     gss_ctx_id_t *context_handle,
490     gss_name_t target_name,
491     gss_OID mech_type,
492     OM_uint32 req_flags,
493     OM_uint32 time_req,
494     gss_channel_bindings_t input_chan_bindings,
495     gss_buffer_t input_token,
496     gss_OID *actual_mech_type,
497     gss_buffer_t output_token,
498     OM_uint32 *ret_flags,
499     OM_uint32 *time_rec,
500     krb5_context context,
501     krb5_gss_ctx_ext_t exts)
502 {
503     OM_uint32 major_status;
504     krb5_error_code code;
505     krb5_creds *k_cred = NULL;
506     krb5_gss_ctx_id_rec *ctx, *ctx_free;
507     krb5_timestamp now;
508     gss_buffer_desc token;
509     krb5_keyblock *keyblock;
510
511     k5_mutex_assert_locked(&cred->lock);
512     major_status = GSS_S_FAILURE;
513     token.length = 0;
514     token.value = NULL;
515
516     /* make sure the cred is usable for init */
517
518     if ((cred->usage != GSS_C_INITIATE) &&
519         (cred->usage != GSS_C_BOTH)) {
520         *minor_status = 0;
521         return(GSS_S_NO_CRED);
522     }
523
524     /* complain if the input token is non-null */
525
526     if (input_token != GSS_C_NO_BUFFER && input_token->length != 0) {
527         *minor_status = 0;
528         return(GSS_S_DEFECTIVE_TOKEN);
529     }
530
531     /* create the ctx */
532
533     if ((ctx = (krb5_gss_ctx_id_rec *) xmalloc(sizeof(krb5_gss_ctx_id_rec)))
534         == NULL) {
535         *minor_status = ENOMEM;
536         return(GSS_S_FAILURE);
537     }
538
539     /* fill in the ctx */
540     memset(ctx, 0, sizeof(krb5_gss_ctx_id_rec));
541     ctx->magic = KG_CONTEXT;
542     ctx_free = ctx;
543     if ((code = krb5_auth_con_init(context, &ctx->auth_context)))
544         goto cleanup;
545     krb5_auth_con_setflags(context, ctx->auth_context,
546                            KRB5_AUTH_CONTEXT_DO_SEQUENCE);
547
548     /* limit the encryption types negotiated (if requested) */
549     if (cred->req_enctypes) {
550         if ((code = krb5_set_default_tgs_enctypes(context,
551                                                   cred->req_enctypes))) {
552             goto cleanup;
553         }
554     }
555
556     ctx->initiate = 1;
557     ctx->seed_init = 0;
558     ctx->seqstate = 0;
559
560     ctx->gss_flags = req_flags & (GSS_C_CONF_FLAG | GSS_C_INTEG_FLAG |
561                                   GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG |
562                                   GSS_C_SEQUENCE_FLAG | GSS_C_DELEG_FLAG |
563                                   GSS_C_DCE_STYLE | GSS_C_IDENTIFY_FLAG |
564                                   GSS_C_EXTENDED_ERROR_FLAG);
565     ctx->gss_flags |= GSS_C_TRANS_FLAG;
566     if (!cred->suppress_ci_flags)
567         ctx->gss_flags |= (GSS_C_CONF_FLAG | GSS_C_INTEG_FLAG);
568     if (req_flags & GSS_C_DCE_STYLE)
569         ctx->gss_flags |= GSS_C_MUTUAL_FLAG;
570
571     if ((code = krb5_timeofday(context, &now)))
572         goto cleanup;
573
574     if (time_req == 0 || time_req == GSS_C_INDEFINITE) {
575         ctx->krb_times.endtime = 0;
576     } else {
577         ctx->krb_times.endtime = ts_incr(now, time_req);
578     }
579
580     if ((code = kg_duplicate_name(context, cred->name, &ctx->here)))
581         goto cleanup;
582
583     if ((code = kg_duplicate_name(context, (krb5_gss_name_t)target_name,
584                                   &ctx->there)))
585         goto cleanup;
586
587     code = get_credentials(context, cred, ctx->there, now,
588                            ctx->krb_times.endtime, &k_cred);
589     if (code)
590         goto cleanup;
591
592     ctx->krb_times = k_cred->times;
593
594     /*
595      * GSS_C_DELEG_POLICY_FLAG means to delegate only if the
596      * ok-as-delegate ticket flag is set.
597      */
598     if ((req_flags & GSS_C_DELEG_POLICY_FLAG)
599         && (k_cred->ticket_flags & TKT_FLG_OK_AS_DELEGATE))
600         ctx->gss_flags |= GSS_C_DELEG_FLAG | GSS_C_DELEG_POLICY_FLAG;
601
602     if (generic_gss_copy_oid(minor_status, mech_type, &ctx->mech_used)
603         != GSS_S_COMPLETE) {
604         code = *minor_status;
605         goto cleanup;
606     }
607     /*
608      * Now try to make it static if at all possible....
609      */
610     ctx->mech_used = krb5_gss_convert_static_mech_oid(ctx->mech_used);
611
612     {
613         /* gsskrb5 v1 */
614         krb5_int32 seq_temp;
615         if ((code = make_ap_req_v1(context, ctx,
616                                    cred, k_cred, ctx->here->ad_context,
617                                    input_chan_bindings,
618                                    mech_type, &token, exts))) {
619             if ((code == KRB5_FCC_NOFILE) || (code == KRB5_CC_NOTFOUND) ||
620                 (code == KG_EMPTY_CCACHE))
621                 major_status = GSS_S_NO_CRED;
622             if (code == KRB5KRB_AP_ERR_TKT_EXPIRED)
623                 major_status = GSS_S_CREDENTIALS_EXPIRED;
624             goto cleanup;
625         }
626
627         krb5_auth_con_getlocalseqnumber(context, ctx->auth_context, &seq_temp);
628         ctx->seq_send = seq_temp;
629         code = krb5_auth_con_getsendsubkey(context, ctx->auth_context,
630                                            &keyblock);
631         if (code != 0)
632             goto cleanup;
633         code = krb5_k_create_key(context, keyblock, &ctx->subkey);
634         krb5_free_keyblock(context, keyblock);
635         if (code != 0)
636             goto cleanup;
637     }
638
639     ctx->enc = NULL;
640     ctx->seq = NULL;
641     ctx->have_acceptor_subkey = 0;
642     code = kg_setup_keys(context, ctx, ctx->subkey, &ctx->cksumtype);
643     if (code != 0)
644         goto cleanup;
645
646     if (!(ctx->gss_flags & GSS_C_MUTUAL_FLAG)) {
647         /* There will be no AP-REP, so set up sequence state now. */
648         ctx->seq_recv = ctx->seq_send;
649         code = g_seqstate_init(&ctx->seqstate, ctx->seq_recv,
650                                (ctx->gss_flags & GSS_C_REPLAY_FLAG) != 0,
651                                (ctx->gss_flags & GSS_C_SEQUENCE_FLAG) != 0,
652                                ctx->proto);
653         if (code != 0)
654             goto cleanup;
655     }
656
657     /* compute time_rec */
658     if (time_rec) {
659         if ((code = krb5_timeofday(context, &now)))
660             goto cleanup;
661         *time_rec = ts_delta(ctx->krb_times.endtime, now);
662     }
663
664     /* set the other returns */
665     *output_token = token;
666
667     if (ret_flags)
668         *ret_flags = ctx->gss_flags;
669
670     if (actual_mech_type)
671         *actual_mech_type = mech_type;
672
673     /* return successfully */
674
675     *context_handle = (gss_ctx_id_t) ctx;
676     ctx_free = NULL;
677     if (ctx->gss_flags & GSS_C_MUTUAL_FLAG) {
678         ctx->established = 0;
679         major_status = GSS_S_CONTINUE_NEEDED;
680     } else {
681         ctx->gss_flags |= GSS_C_PROT_READY_FLAG;
682         ctx->established = 1;
683         major_status = GSS_S_COMPLETE;
684     }
685
686 cleanup:
687     krb5_free_creds(context, k_cred);
688     if (ctx_free) {
689         if (ctx_free->auth_context)
690             krb5_auth_con_free(context, ctx_free->auth_context);
691         if (ctx_free->here)
692             kg_release_name(context, &ctx_free->here);
693         if (ctx_free->there)
694             kg_release_name(context, &ctx_free->there);
695         if (ctx_free->subkey)
696             krb5_k_free_key(context, ctx_free->subkey);
697         xfree(ctx_free);
698     }
699
700     *minor_status = code;
701     return (major_status);
702 }
703
704 /*
705  * mutual_auth
706  *
707  * Handle the reply from the acceptor, if we're doing mutual auth.
708  */
709 static OM_uint32
710 mutual_auth(
711     OM_uint32 *minor_status,
712     gss_ctx_id_t *context_handle,
713     gss_name_t target_name,
714     gss_OID mech_type,
715     OM_uint32 req_flags,
716     OM_uint32 time_req,
717     gss_channel_bindings_t input_chan_bindings,
718     gss_buffer_t input_token,
719     gss_OID *actual_mech_type,
720     gss_buffer_t output_token,
721     OM_uint32 *ret_flags,
722     OM_uint32 *time_rec,
723     krb5_context context)
724 {
725     OM_uint32 major_status;
726     unsigned char *ptr;
727     char *sptr;
728     krb5_data ap_rep;
729     krb5_ap_rep_enc_part *ap_rep_data;
730     krb5_timestamp now;
731     krb5_gss_ctx_id_rec *ctx;
732     krb5_error *krb_error;
733     krb5_error_code code;
734     krb5int_access kaccess;
735
736     major_status = GSS_S_FAILURE;
737
738     code = krb5int_accessor (&kaccess, KRB5INT_ACCESS_VERSION);
739     if (code)
740         goto fail;
741
742     ctx = (krb5_gss_ctx_id_t) *context_handle;
743
744     /* make sure the context is non-established, and that certain
745        arguments are unchanged */
746
747     if ((ctx->established) ||
748         ((ctx->gss_flags & GSS_C_MUTUAL_FLAG) == 0)) {
749         code = KG_CONTEXT_ESTABLISHED;
750         goto fail;
751     }
752
753     if (! kg_compare_name(context, ctx->there, (krb5_gss_name_t)target_name)) {
754         (void)krb5_gss_delete_sec_context(minor_status,
755                                           context_handle, NULL);
756         code = 0;
757         major_status = GSS_S_BAD_NAME;
758         goto fail;
759     }
760
761     /* verify the token and leave the AP_REP message in ap_rep */
762
763     if (input_token == GSS_C_NO_BUFFER) {
764         (void)krb5_gss_delete_sec_context(minor_status,
765                                           context_handle, NULL);
766         code = 0;
767         major_status = GSS_S_DEFECTIVE_TOKEN;
768         goto fail;
769     }
770
771     ptr = (unsigned char *) input_token->value;
772
773     if (ctx->gss_flags & GSS_C_DCE_STYLE) {
774         /* Raw AP-REP */
775         ap_rep.length = input_token->length;
776         ap_rep.data = (char *)input_token->value;
777     } else if (g_verify_token_header(ctx->mech_used,
778                                      &(ap_rep.length),
779                                      &ptr, KG_TOK_CTX_AP_REP,
780                                      input_token->length, 1)) {
781         if (g_verify_token_header((gss_OID) ctx->mech_used,
782                                   &(ap_rep.length),
783                                   &ptr, KG_TOK_CTX_ERROR,
784                                   input_token->length, 1) == 0) {
785
786             /* Handle a KRB_ERROR message from the server */
787
788             sptr = (char *) ptr;           /* PC compiler bug */
789             TREAD_STR(sptr, ap_rep.data, ap_rep.length);
790
791             code = krb5_rd_error(context, &ap_rep, &krb_error);
792             if (code)
793                 goto fail;
794             if (krb_error->error)
795                 code = (krb5_error_code)krb_error->error + ERROR_TABLE_BASE_krb5;
796             else
797                 code = 0;
798             krb5_free_error(context, krb_error);
799             goto fail;
800         } else {
801             *minor_status = 0;
802             return(GSS_S_DEFECTIVE_TOKEN);
803         }
804     }
805
806     sptr = (char *) ptr;                      /* PC compiler bug */
807     TREAD_STR(sptr, ap_rep.data, ap_rep.length);
808
809     /* decode the ap_rep */
810     if ((code = krb5_rd_rep(context, ctx->auth_context, &ap_rep,
811                             &ap_rep_data))) {
812         /*
813          * XXX A hack for backwards compatiblity.
814          * To be removed in 1999 -- proven
815          */
816         krb5_auth_con_setuseruserkey(context, ctx->auth_context,
817                                      &ctx->subkey->keyblock);
818         if ((krb5_rd_rep(context, ctx->auth_context, &ap_rep,
819                          &ap_rep_data)))
820             goto fail;
821     }
822
823     /* store away the sequence number */
824     ctx->seq_recv = ap_rep_data->seq_number;
825     code = g_seqstate_init(&ctx->seqstate, ctx->seq_recv,
826                            (ctx->gss_flags & GSS_C_REPLAY_FLAG) != 0,
827                            (ctx->gss_flags & GSS_C_SEQUENCE_FLAG) != 0,
828                            ctx->proto);
829     if (code) {
830         krb5_free_ap_rep_enc_part(context, ap_rep_data);
831         goto fail;
832     }
833
834     if (ap_rep_data->subkey != NULL &&
835         (ctx->proto == 1 || (ctx->gss_flags & GSS_C_DCE_STYLE) ||
836          ap_rep_data->subkey->enctype != ctx->subkey->keyblock.enctype)) {
837         /* Keep acceptor's subkey.  */
838         ctx->have_acceptor_subkey = 1;
839         code = krb5_k_create_key(context, ap_rep_data->subkey,
840                                  &ctx->acceptor_subkey);
841         if (code) {
842             krb5_free_ap_rep_enc_part(context, ap_rep_data);
843             goto fail;
844         }
845         code = kg_setup_keys(context, ctx, ctx->acceptor_subkey,
846                              &ctx->acceptor_subkey_cksumtype);
847         if (code) {
848             krb5_free_ap_rep_enc_part(context, ap_rep_data);
849             goto fail;
850         }
851     }
852     /* free the ap_rep_data */
853     krb5_free_ap_rep_enc_part(context, ap_rep_data);
854
855     if (ctx->gss_flags & GSS_C_DCE_STYLE) {
856         krb5_data outbuf;
857
858         code = krb5_mk_rep_dce(context, ctx->auth_context, &outbuf);
859         if (code)
860             goto fail;
861
862         code = data_to_gss(&outbuf, output_token);
863         if (code)
864             goto fail;
865     }
866
867     /* set established */
868     ctx->established = 1;
869
870     /* set returns */
871
872     if (time_rec) {
873         if ((code = krb5_timeofday(context, &now)))
874             goto fail;
875         *time_rec = ts_delta(ctx->krb_times.endtime, now);
876     }
877
878     if (ret_flags)
879         *ret_flags = ctx->gss_flags;
880
881     if (actual_mech_type)
882         *actual_mech_type = mech_type;
883
884     /* success */
885
886     *minor_status = 0;
887     return GSS_S_COMPLETE;
888
889 fail:
890     (void)krb5_gss_delete_sec_context(minor_status, context_handle, NULL);
891
892     *minor_status = code;
893     return (major_status);
894 }
895
896 OM_uint32
897 krb5_gss_init_sec_context_ext(
898     OM_uint32 *minor_status,
899     gss_cred_id_t claimant_cred_handle,
900     gss_ctx_id_t *context_handle,
901     gss_name_t target_name,
902     gss_OID mech_type,
903     OM_uint32 req_flags,
904     OM_uint32 time_req,
905     gss_channel_bindings_t input_chan_bindings,
906     gss_buffer_t input_token,
907     gss_OID *actual_mech_type,
908     gss_buffer_t output_token,
909     OM_uint32 *ret_flags,
910     OM_uint32 *time_rec,
911     krb5_gss_ctx_ext_t exts)
912 {
913     krb5_context context;
914     gss_cred_id_t defcred = GSS_C_NO_CREDENTIAL;
915     krb5_gss_cred_id_t cred;
916     krb5_error_code kerr;
917     OM_uint32 major_status;
918     OM_uint32 tmp_min_stat;
919
920     if (*context_handle == GSS_C_NO_CONTEXT) {
921         kerr = krb5_gss_init_context(&context);
922         if (kerr) {
923             *minor_status = kerr;
924             return GSS_S_FAILURE;
925         }
926         if (GSS_ERROR(kg_sync_ccache_name(context, minor_status))) {
927             save_error_info(*minor_status, context);
928             krb5_free_context(context);
929             return GSS_S_FAILURE;
930         }
931     } else {
932         context = ((krb5_gss_ctx_id_rec *)*context_handle)->k5_context;
933     }
934
935     /* set up return values so they can be "freed" successfully */
936
937     major_status = GSS_S_FAILURE; /* Default major code */
938     output_token->length = 0;
939     output_token->value = NULL;
940     if (actual_mech_type)
941         *actual_mech_type = NULL;
942
943     /* verify the mech_type */
944
945     if (mech_type == GSS_C_NULL_OID || g_OID_equal(mech_type, gss_mech_krb5)) {
946         mech_type = (gss_OID) gss_mech_krb5;
947     } else if (g_OID_equal(mech_type, gss_mech_krb5_old)) {
948         mech_type = (gss_OID) gss_mech_krb5_old;
949     } else if (g_OID_equal(mech_type, gss_mech_krb5_wrong)) {
950         mech_type = (gss_OID) gss_mech_krb5_wrong;
951     } else if (g_OID_equal(mech_type, gss_mech_iakerb)) {
952         mech_type = (gss_OID) gss_mech_iakerb;
953     } else {
954         *minor_status = 0;
955         if (*context_handle == GSS_C_NO_CONTEXT)
956             krb5_free_context(context);
957         return(GSS_S_BAD_MECH);
958     }
959
960     /* is this a new connection or not? */
961
962     /*SUPPRESS 29*/
963     if (*context_handle == GSS_C_NO_CONTEXT) {
964         /* verify the credential, or use the default */
965         /*SUPPRESS 29*/
966         if (claimant_cred_handle == GSS_C_NO_CREDENTIAL) {
967             major_status = kg_get_defcred(minor_status, &defcred);
968             if (major_status && GSS_ERROR(major_status)) {
969                 if (*context_handle == GSS_C_NO_CONTEXT)
970                     krb5_free_context(context);
971                 return(major_status);
972             }
973             claimant_cred_handle = defcred;
974         }
975
976         major_status = kg_cred_resolve(minor_status, context,
977                                        claimant_cred_handle, target_name);
978         if (GSS_ERROR(major_status)) {
979             save_error_info(*minor_status, context);
980             krb5_gss_release_cred(&tmp_min_stat, &defcred);
981             if (*context_handle == GSS_C_NO_CONTEXT)
982                 krb5_free_context(context);
983             return(major_status);
984         }
985         cred = (krb5_gss_cred_id_t)claimant_cred_handle;
986
987         major_status = kg_new_connection(minor_status, cred, context_handle,
988                                          target_name, mech_type, req_flags,
989                                          time_req, input_chan_bindings,
990                                          input_token, actual_mech_type,
991                                          output_token, ret_flags, time_rec,
992                                          context, exts);
993         k5_mutex_unlock(&cred->lock);
994         krb5_gss_release_cred(&tmp_min_stat, &defcred);
995         if (*context_handle == GSS_C_NO_CONTEXT) {
996             save_error_info (*minor_status, context);
997             krb5_free_context(context);
998         } else
999             ((krb5_gss_ctx_id_rec *) *context_handle)->k5_context = context;
1000     } else {
1001         /* mutual_auth doesn't care about the credentials */
1002         major_status = mutual_auth(minor_status, context_handle,
1003                                    target_name, mech_type, req_flags,
1004                                    time_req, input_chan_bindings,
1005                                    input_token, actual_mech_type,
1006                                    output_token, ret_flags, time_rec,
1007                                    context);
1008         /* If context_handle is now NO_CONTEXT, mutual_auth called
1009            delete_sec_context, which would've zapped the krb5 context
1010            too.  */
1011     }
1012
1013     return(major_status);
1014 }
1015
1016 #ifndef _WIN32
1017 k5_mutex_t kg_kdc_flag_mutex = K5_MUTEX_PARTIAL_INITIALIZER;
1018 static int kdc_flag = 0;
1019 #endif
1020
1021 krb5_error_code
1022 krb5_gss_init_context (krb5_context *ctxp)
1023 {
1024     krb5_error_code err;
1025 #ifndef _WIN32
1026     int is_kdc;
1027 #endif
1028
1029     err = gss_krb5int_initialize_library();
1030     if (err)
1031         return err;
1032 #ifndef _WIN32
1033     k5_mutex_lock(&kg_kdc_flag_mutex);
1034     is_kdc = kdc_flag;
1035     k5_mutex_unlock(&kg_kdc_flag_mutex);
1036
1037     if (is_kdc)
1038         return krb5int_init_context_kdc(ctxp);
1039 #endif
1040
1041     return krb5_init_context(ctxp);
1042 }
1043
1044 #ifndef _WIN32
1045 OM_uint32
1046 krb5int_gss_use_kdc_context(OM_uint32 *minor_status,
1047                             const gss_OID desired_mech,
1048                             const gss_OID desired_object,
1049                             gss_buffer_t value)
1050 {
1051     OM_uint32 err;
1052
1053     *minor_status = 0;
1054
1055     err = gss_krb5int_initialize_library();
1056     if (err)
1057         return err;
1058     k5_mutex_lock(&kg_kdc_flag_mutex);
1059     kdc_flag = 1;
1060     k5_mutex_unlock(&kg_kdc_flag_mutex);
1061     return GSS_S_COMPLETE;
1062 }
1063 #endif
1064
1065 OM_uint32 KRB5_CALLCONV
1066 krb5_gss_init_sec_context(minor_status, claimant_cred_handle,
1067                           context_handle, target_name, mech_type,
1068                           req_flags, time_req, input_chan_bindings,
1069                           input_token, actual_mech_type, output_token,
1070                           ret_flags, time_rec)
1071     OM_uint32 *minor_status;
1072     gss_cred_id_t claimant_cred_handle;
1073     gss_ctx_id_t *context_handle;
1074     gss_name_t target_name;
1075     gss_OID mech_type;
1076     OM_uint32 req_flags;
1077     OM_uint32 time_req;
1078     gss_channel_bindings_t input_chan_bindings;
1079     gss_buffer_t input_token;
1080     gss_OID *actual_mech_type;
1081     gss_buffer_t output_token;
1082     OM_uint32 *ret_flags;
1083     OM_uint32 *time_rec;
1084 {
1085     krb5_gss_ctx_ext_rec exts;
1086
1087     memset(&exts, 0, sizeof(exts));
1088
1089     return krb5_gss_init_sec_context_ext(minor_status,
1090                                          claimant_cred_handle,
1091                                          context_handle,
1092                                          target_name,
1093                                          mech_type,
1094                                          req_flags,
1095                                          time_req,
1096                                          input_chan_bindings,
1097                                          input_token,
1098                                          actual_mech_type,
1099                                          output_token,
1100                                          ret_flags,
1101                                          time_rec,
1102                                          &exts);
1103 }