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