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