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