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