1 /* 2 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 3 * Use is subject to license terms. 4 */ 5 6 7 /* 8 * Copyright 2000,2002, 2003 by the Massachusetts Institute of Technology. 9 * All Rights Reserved. 10 * 11 * Export of this software from the United States of America may 12 * require a specific license from the United States Government. 13 * It is the responsibility of any person or organization contemplating 14 * export to obtain such a license before exporting. 15 * 16 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and 17 * distribute this software and its documentation for any purpose and 18 * without fee is hereby granted, provided that the above copyright 19 * notice appear in all copies and that both that copyright notice and 20 * this permission notice appear in supporting documentation, and that 21 * the name of M.I.T. not be used in advertising or publicity pertaining 22 * to distribution of the software without specific, written prior 23 * permission. Furthermore if you modify this software you must label 24 * your software as modified software and not distribute it in such a 25 * fashion that it might be confused with the original M.I.T. software. 26 * M.I.T. makes no representations about the suitability of 27 * this software for any purpose. It is provided "as is" without express 28 * or implied warranty. 29 * 30 */ 31 /* 32 * Copyright 1993 by OpenVision Technologies, Inc. 33 * 34 * Permission to use, copy, modify, distribute, and sell this software 35 * and its documentation for any purpose is hereby granted without fee, 36 * provided that the above copyright notice appears in all copies and 37 * that both that copyright notice and this permission notice appear in 38 * supporting documentation, and that the name of OpenVision not be used 39 * in advertising or publicity pertaining to distribution of the software 40 * without specific, written prior permission. OpenVision makes no 41 * representations about the suitability of this software for any 42 * purpose. It is provided "as is" without express or implied warranty. 43 * 44 * OPENVISION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, 45 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO 46 * EVENT SHALL OPENVISION BE LIABLE FOR ANY SPECIAL, INDIRECT OR 47 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF 48 * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR 49 * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 50 * PERFORMANCE OF THIS SOFTWARE. 51 */ 52 53 /* 54 * Copyright (C) 1998 by the FundsXpress, INC. 55 * 56 * All rights reserved. 57 * 58 * Export of this software from the United States of America may require 59 * a specific license from the United States Government. It is the 60 * responsibility of any person or organization contemplating export to 61 * obtain such a license before exporting. 62 * 63 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and 64 * distribute this software and its documentation for any purpose and 65 * without fee is hereby granted, provided that the above copyright 66 * notice appear in all copies and that both that copyright notice and 67 * this permission notice appear in supporting documentation, and that 68 * the name of FundsXpress. not be used in advertising or publicity pertaining 69 * to distribution of the software without specific, written prior 70 * permission. FundsXpress makes no representations about the suitability of 71 * this software for any purpose. It is provided "as is" without express 72 * or implied warranty. 73 * 74 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR 75 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 76 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. 77 */ 78 79 #include "k5-int.h" 80 #include "gss_libinit.h" 81 #include "gssapiP_krb5.h" 82 #include "mglueP.h" 83 #ifdef HAVE_MEMORY_H 84 #include <memory.h> 85 #endif 86 #include <stdlib.h> 87 #include <assert.h> 88 89 /* Solaris Kerberos start */ 90 static OM_uint32 get_default_cred(OM_uint32 *, void *, gss_cred_id_t *); 91 /* Solaris Kerberos end */ 92 93 /* 94 * $Id: init_sec_context.c 18721 2006-10-16 16:18:29Z epeisach $ 95 */ 96 97 /* XXX This is for debugging only!!! Should become a real bitfield 98 at some point */ 99 int krb5_gss_dbg_client_expcreds = 0; 100 101 /* 102 * Common code which fetches the correct krb5 credentials from the 103 * ccache. 104 */ 105 static krb5_error_code get_credentials(context, cred, server, now, 106 endtime, out_creds) 107 krb5_context context; 108 krb5_gss_cred_id_t cred; 109 krb5_principal server; 110 krb5_timestamp now; 111 krb5_timestamp endtime; 112 krb5_creds **out_creds; 113 { 114 krb5_error_code code; 115 krb5_creds in_creds; 116 117 k5_mutex_assert_locked(&cred->lock); 118 memset((char *) &in_creds, 0, sizeof(krb5_creds)); 119 120 if ((code = krb5_copy_principal(context, cred->princ, &in_creds.client))) 121 goto cleanup; 122 if ((code = krb5_copy_principal(context, server, &in_creds.server))) 123 goto cleanup; 124 in_creds.times.endtime = endtime; 125 126 in_creds.keyblock.enctype = 0; 127 128 code = krb5_get_credentials(context, 0, cred->ccache, 129 &in_creds, out_creds); 130 if (code) 131 goto cleanup; 132 133 /* 134 * Enforce a stricter limit (without timeskew forgiveness at the 135 * boundaries) because accept_sec_context code is also similarly 136 * non-forgiving. 137 */ 138 if (!krb5_gss_dbg_client_expcreds && *out_creds != NULL && 139 (*out_creds)->times.endtime < now) { 140 code = KRB5KRB_AP_ERR_TKT_EXPIRED; 141 goto cleanup; 142 } 143 144 cleanup: 145 if (in_creds.client) 146 krb5_free_principal(context, in_creds.client); 147 if (in_creds.server) 148 krb5_free_principal(context, in_creds.server); 149 return code; 150 } 151 struct gss_checksum_data { 152 krb5_gss_ctx_id_rec *ctx; 153 krb5_gss_cred_id_t cred; 154 krb5_checksum md5; 155 krb5_data checksum_data; 156 }; 157 158 #ifdef CFX_EXERCISE 159 #include "../../krb5/krb/auth_con.h" 160 #endif 161 static krb5_error_code KRB5_CALLCONV 162 make_gss_checksum (krb5_context context, krb5_auth_context auth_context, 163 void *cksum_data, krb5_data **out) 164 { 165 krb5_error_code code; 166 krb5_int32 con_flags; 167 unsigned char *ptr; 168 struct gss_checksum_data *data = cksum_data; 169 krb5_data credmsg; 170 unsigned int junk; 171 172 data->checksum_data.data = 0; 173 credmsg.data = 0; 174 /* build the checksum field */ 175 176 if (data->ctx->gss_flags & GSS_C_DELEG_FLAG) { 177 /* first get KRB_CRED message, so we know its length */ 178 179 /* clear the time check flag that was set in krb5_auth_con_init() */ 180 krb5_auth_con_getflags(context, auth_context, &con_flags); 181 krb5_auth_con_setflags(context, auth_context, 182 con_flags & ~KRB5_AUTH_CONTEXT_DO_TIME); 183 184 code = krb5_fwd_tgt_creds(context, auth_context, 0, 185 data->cred->princ, data->ctx->there, 186 data->cred->ccache, 1, 187 &credmsg); 188 189 /* turn KRB5_AUTH_CONTEXT_DO_TIME back on */ 190 krb5_auth_con_setflags(context, auth_context, con_flags); 191 192 if (code) { 193 /* don't fail here; just don't accept/do the delegation 194 request */ 195 data->ctx->gss_flags &= ~GSS_C_DELEG_FLAG; 196 197 data->checksum_data.length = 24; 198 } else { 199 if (credmsg.length+28 > KRB5_INT16_MAX) { 200 krb5_free_data_contents(context, &credmsg); 201 return(KRB5KRB_ERR_FIELD_TOOLONG); 202 } 203 204 data->checksum_data.length = 28+credmsg.length; 205 } 206 } else { 207 data->checksum_data.length = 24; 208 } 209 #ifdef CFX_EXERCISE 210 if (data->ctx->auth_context->keyblock != NULL 211 && data->ctx->auth_context->keyblock->enctype == 18) { 212 srand(time(0) ^ getpid()); 213 /* Our ftp client code stupidly assumes a base64-encoded 214 version of the token will fit in 10K, so don't make this 215 too big. */ 216 junk = rand() & 0xff; 217 } else 218 junk = 0; 219 #else 220 junk = 0; 221 #endif 222 223 data->checksum_data.length += junk; 224 225 /* now allocate a buffer to hold the checksum data and 226 (maybe) KRB_CRED msg */ 227 228 if ((data->checksum_data.data = 229 (char *) xmalloc(data->checksum_data.length)) == NULL) { 230 if (credmsg.data) 231 krb5_free_data_contents(context, &credmsg); 232 return(ENOMEM); 233 } 234 /* Solaris Kerberos */ 235 ptr = (uchar_t *)data->checksum_data.data; /* SUNW15resync */ 236 237 TWRITE_INT(ptr, data->md5.length, 0); 238 TWRITE_STR(ptr, (unsigned char *) data->md5.contents, data->md5.length); 239 TWRITE_INT(ptr, data->ctx->gss_flags, 0); 240 241 /* done with this, free it */ 242 xfree(data->md5.contents); 243 244 if (credmsg.data) { 245 TWRITE_INT16(ptr, KRB5_GSS_FOR_CREDS_OPTION, 0); 246 TWRITE_INT16(ptr, credmsg.length, 0); 247 TWRITE_STR(ptr, (unsigned char *) credmsg.data, credmsg.length); 248 249 /* free credmsg data */ 250 krb5_free_data_contents(context, &credmsg); 251 } 252 if (junk) 253 memset(ptr, 'i', junk); 254 *out = &data->checksum_data; 255 return 0; 256 } 257 258 static krb5_error_code 259 make_ap_req_v1(context, ctx, cred, k_cred, chan_bindings, mech_type, token) 260 krb5_context context; 261 krb5_gss_ctx_id_rec *ctx; 262 krb5_gss_cred_id_t cred; 263 krb5_creds *k_cred; 264 gss_channel_bindings_t chan_bindings; 265 gss_OID mech_type; 266 gss_buffer_t token; 267 { 268 krb5_flags mk_req_flags = 0; 269 krb5_error_code code; 270 struct gss_checksum_data cksum_struct; 271 krb5_checksum md5; 272 krb5_data ap_req; 273 krb5_data *checksum_data = NULL; 274 unsigned char *ptr; 275 unsigned char *t; 276 unsigned int tlen; 277 278 k5_mutex_assert_locked(&cred->lock); 279 ap_req.data = 0; 280 281 /* compute the hash of the channel bindings */ 282 283 if ((code = kg_checksum_channel_bindings(context, chan_bindings, &md5, 0))) 284 return(code); 285 286 krb5_auth_con_set_req_cksumtype(context, ctx->auth_context, 287 CKSUMTYPE_KG_CB); 288 cksum_struct.md5 = md5; 289 cksum_struct.ctx = ctx; 290 cksum_struct.cred = cred; 291 cksum_struct.checksum_data.data = NULL; 292 switch (k_cred->keyblock.enctype) { 293 case ENCTYPE_DES_CBC_CRC: 294 case ENCTYPE_DES_CBC_MD4: 295 case ENCTYPE_DES_CBC_MD5: 296 case ENCTYPE_DES3_CBC_SHA1: 297 code = make_gss_checksum(context, ctx->auth_context, &cksum_struct, 298 &checksum_data); 299 if (code) 300 goto cleanup; 301 break; 302 default: 303 krb5_auth_con_set_checksum_func(context, ctx->auth_context, 304 make_gss_checksum, &cksum_struct); 305 break; 306 } 307 308 309 /* call mk_req. subkey and ap_req need to be used or destroyed */ 310 311 mk_req_flags = AP_OPTS_USE_SUBKEY; 312 313 if (ctx->gss_flags & GSS_C_MUTUAL_FLAG) 314 mk_req_flags |= AP_OPTS_MUTUAL_REQUIRED; 315 316 code = krb5_mk_req_extended(context, &ctx->auth_context, mk_req_flags, 317 checksum_data, k_cred, &ap_req); 318 krb5_free_data_contents(context, &cksum_struct.checksum_data); 319 if (code) 320 goto cleanup; 321 322 /* store the interesting stuff from creds and authent */ 323 ctx->endtime = k_cred->times.endtime; 324 ctx->krb_flags = k_cred->ticket_flags; 325 326 /* build up the token */ 327 328 /* allocate space for the token */ 329 tlen = g_token_size((gss_OID) mech_type, ap_req.length); 330 331 if ((t = (unsigned char *) xmalloc(tlen)) == NULL) { 332 code = ENOMEM; 333 goto cleanup; 334 } 335 336 /* fill in the buffer */ 337 338 ptr = t; 339 340 g_make_token_header(mech_type, ap_req.length, 341 &ptr, KG_TOK_CTX_AP_REQ); 342 343 TWRITE_STR(ptr, (unsigned char *) ap_req.data, ap_req.length); 344 345 /* pass it back */ 346 347 token->length = tlen; 348 token->value = (void *) t; 349 350 code = 0; 351 352 cleanup: 353 if (checksum_data && checksum_data->data) 354 krb5_free_data_contents(context, checksum_data); 355 if (ap_req.data) 356 krb5_free_data_contents(context, &ap_req); 357 358 return (code); 359 } 360 361 /* 362 * setup_enc 363 * 364 * Fill in the encryption descriptors. Called after AP-REQ is made. 365 */ 366 static OM_uint32 367 setup_enc( 368 OM_uint32 *minor_status, 369 krb5_gss_ctx_id_rec *ctx, 370 krb5_context context) 371 { 372 krb5_error_code code; 373 int i; 374 krb5int_access kaccess; 375 376 code = krb5int_accessor (&kaccess, KRB5INT_ACCESS_VERSION); 377 if (code) 378 goto fail; 379 380 ctx->have_acceptor_subkey = 0; 381 ctx->proto = 0; 382 ctx->cksumtype = 0; 383 switch(ctx->subkey->enctype) { 384 case ENCTYPE_DES_CBC_MD5: 385 case ENCTYPE_DES_CBC_MD4: 386 case ENCTYPE_DES_CBC_CRC: 387 ctx->subkey->enctype = ENCTYPE_DES_CBC_RAW; 388 ctx->signalg = SGN_ALG_DES_MAC_MD5; 389 ctx->cksum_size = 8; 390 ctx->sealalg = SEAL_ALG_DES; 391 392 /* The encryption key is the session key XOR 393 0xf0f0f0f0f0f0f0f0. */ 394 if ((code = krb5_copy_keyblock(context, ctx->subkey, &ctx->enc))) 395 goto fail; 396 397 for (i=0; i<ctx->enc->length; i++) 398 ctx->enc->contents[i] ^= 0xf0; 399 400 goto copy_subkey_to_seq; 401 402 case ENCTYPE_DES3_CBC_SHA1: 403 /* MIT extension */ 404 ctx->subkey->enctype = ENCTYPE_DES3_CBC_RAW; 405 ctx->signalg = SGN_ALG_HMAC_SHA1_DES3_KD; 406 ctx->cksum_size = 20; 407 ctx->sealalg = SEAL_ALG_DES3KD; 408 409 copy_subkey: 410 code = krb5_copy_keyblock (context, ctx->subkey, &ctx->enc); 411 if (code) 412 goto fail; 413 copy_subkey_to_seq: 414 code = krb5_copy_keyblock (context, ctx->subkey, &ctx->seq); 415 if (code) { 416 krb5_free_keyblock (context, ctx->enc); 417 goto fail; 418 } 419 goto success; 420 421 case ENCTYPE_ARCFOUR_HMAC: 422 /* Microsoft extension */ 423 ctx->signalg = SGN_ALG_HMAC_MD5 ; 424 ctx->cksum_size = 8; 425 ctx->sealalg = SEAL_ALG_MICROSOFT_RC4 ; 426 427 goto copy_subkey; 428 429 default: 430 /* Fill some fields we shouldn't be using on this path 431 with garbage. */ 432 ctx->signalg = -10; 433 ctx->sealalg = -10; 434 435 ctx->proto = 1; 436 code = (*kaccess.krb5int_c_mandatory_cksumtype)(context, ctx->subkey->enctype, 437 &ctx->cksumtype); 438 if (code) 439 goto fail; 440 code = krb5_c_checksum_length(context, ctx->cksumtype, 441 &ctx->cksum_size); 442 if (code) 443 goto fail; 444 goto copy_subkey; 445 } 446 fail: 447 /* SUNW15resync - (as in prev snv code) add if-code and success label fix */ 448 if (code) { 449 *minor_status = code; 450 return GSS_S_FAILURE; 451 } 452 453 success: 454 return (GSS_S_COMPLETE); 455 } 456 457 /* 458 * new_connection 459 * 460 * Do the grunt work of setting up a new context. 461 */ 462 static OM_uint32 463 new_connection( 464 OM_uint32 *minor_status, 465 krb5_gss_cred_id_t cred, 466 gss_ctx_id_t *context_handle, 467 gss_name_t target_name, 468 gss_OID mech_type, 469 OM_uint32 req_flags, 470 OM_uint32 time_req, 471 gss_channel_bindings_t input_chan_bindings, 472 gss_buffer_t input_token, 473 gss_OID *actual_mech_type, 474 gss_buffer_t output_token, 475 OM_uint32 *ret_flags, 476 OM_uint32 *time_rec, 477 krb5_context context, 478 int default_mech) 479 { 480 OM_uint32 major_status; 481 krb5_error_code code; 482 krb5_creds *k_cred; 483 krb5_gss_ctx_id_rec *ctx, *ctx_free; 484 krb5_timestamp now; 485 gss_buffer_desc token; 486 487 k5_mutex_assert_locked(&cred->lock); 488 major_status = GSS_S_FAILURE; 489 token.length = 0; 490 token.value = NULL; 491 492 /* make sure the cred is usable for init */ 493 494 if ((cred->usage != GSS_C_INITIATE) && 495 (cred->usage != GSS_C_BOTH)) { 496 *minor_status = 0; 497 return(GSS_S_NO_CRED); 498 } 499 500 /* complain if the input token is non-null */ 501 502 if (input_token != GSS_C_NO_BUFFER && input_token->length != 0) { 503 *minor_status = 0; 504 return(GSS_S_DEFECTIVE_TOKEN); 505 } 506 507 /* create the ctx */ 508 509 if ((ctx = (krb5_gss_ctx_id_rec *) xmalloc(sizeof(krb5_gss_ctx_id_rec))) 510 == NULL) { 511 *minor_status = ENOMEM; 512 return(GSS_S_FAILURE); 513 } 514 515 /* fill in the ctx */ 516 memset(ctx, 0, sizeof(krb5_gss_ctx_id_rec)); 517 ctx_free = ctx; 518 if ((code = krb5_auth_con_init(context, &ctx->auth_context))) 519 goto fail; 520 krb5_auth_con_setflags(context, ctx->auth_context, 521 KRB5_AUTH_CONTEXT_DO_SEQUENCE); 522 523 /* limit the encryption types negotiated (if requested) */ 524 if (cred->req_enctypes) { 525 if ((code = krb5_set_default_tgs_enctypes(context, 526 cred->req_enctypes))) { 527 goto fail; 528 } 529 } 530 531 ctx->initiate = 1; 532 ctx->gss_flags = (GSS_C_INTEG_FLAG | GSS_C_CONF_FLAG | 533 GSS_C_TRANS_FLAG | 534 ((req_flags) & (GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG | 535 GSS_C_SEQUENCE_FLAG | GSS_C_DELEG_FLAG))); 536 ctx->seed_init = 0; 537 ctx->big_endian = 0; /* all initiators do little-endian, as per spec */ 538 ctx->seqstate = 0; 539 540 if ((code = krb5_timeofday(context, &now))) 541 goto fail; 542 543 if (time_req == 0 || time_req == GSS_C_INDEFINITE) { 544 ctx->endtime = 0; 545 } else { 546 ctx->endtime = now + time_req; 547 } 548 549 if ((code = krb5_copy_principal(context, cred->princ, &ctx->here))) 550 goto fail; 551 552 if ((code = krb5_copy_principal(context, (krb5_principal) target_name, 553 &ctx->there))) 554 goto fail; 555 556 code = get_credentials(context, cred, ctx->there, now, 557 ctx->endtime, &k_cred); 558 if (code) 559 goto fail; 560 561 if (default_mech) { 562 mech_type = (gss_OID) gss_mech_krb5; 563 } 564 565 if (generic_gss_copy_oid(minor_status, mech_type, &ctx->mech_used) 566 != GSS_S_COMPLETE) { 567 code = *minor_status; 568 goto fail; 569 } 570 /* 571 * Now try to make it static if at all possible.... 572 */ 573 ctx->mech_used = krb5_gss_convert_static_mech_oid(ctx->mech_used); 574 575 { 576 /* gsskrb5 v1 */ 577 krb5_ui_4 seq_temp; 578 if ((code = make_ap_req_v1(context, ctx, 579 cred, k_cred, input_chan_bindings, 580 mech_type, &token))) { 581 if ((code == KRB5_FCC_NOFILE) || (code == KRB5_CC_NOTFOUND) || 582 (code == KG_EMPTY_CCACHE)) 583 major_status = GSS_S_NO_CRED; 584 if (code == KRB5KRB_AP_ERR_TKT_EXPIRED) 585 major_status = GSS_S_CREDENTIALS_EXPIRED; 586 goto fail; 587 } 588 589 krb5_auth_con_getlocalseqnumber(context, ctx->auth_context, 590 (krb5_int32 *)&seq_temp); /* SUNW15resync */ 591 ctx->seq_send = seq_temp; 592 krb5_auth_con_getsendsubkey(context, ctx->auth_context, 593 &ctx->subkey); 594 } 595 596 major_status = setup_enc(minor_status, ctx, context); 597 598 if (k_cred) { 599 krb5_free_creds(context, k_cred); 600 k_cred = 0; 601 } 602 603 /* at this point, the context is constructed and valid, 604 hence, releaseable */ 605 606 /* intern the context handle */ 607 608 if (! kg_save_ctx_id((gss_ctx_id_t) ctx)) { 609 code = G_VALIDATE_FAILED; 610 goto fail; 611 } 612 *context_handle = (gss_ctx_id_t) ctx; 613 ctx_free = 0; 614 615 /* compute time_rec */ 616 if (time_rec) { 617 if ((code = krb5_timeofday(context, &now))) 618 goto fail; 619 *time_rec = ctx->endtime - now; 620 } 621 622 /* set the other returns */ 623 *output_token = token; 624 625 if (ret_flags) 626 *ret_flags = ctx->gss_flags; 627 628 if (actual_mech_type) 629 *actual_mech_type = mech_type; 630 631 /* return successfully */ 632 633 *minor_status = 0; 634 if (ctx->gss_flags & GSS_C_MUTUAL_FLAG) { 635 ctx->established = 0; 636 return(GSS_S_CONTINUE_NEEDED); 637 } else { 638 ctx->seq_recv = ctx->seq_send; 639 g_order_init(&(ctx->seqstate), ctx->seq_recv, 640 (ctx->gss_flags & GSS_C_REPLAY_FLAG) != 0, 641 (ctx->gss_flags & GSS_C_SEQUENCE_FLAG) != 0, ctx->proto); 642 ctx->gss_flags |= GSS_C_PROT_READY_FLAG; 643 ctx->established = 1; 644 return(GSS_S_COMPLETE); 645 } 646 647 fail: 648 if (ctx_free) { 649 if (ctx_free->auth_context) 650 krb5_auth_con_free(context, ctx_free->auth_context); 651 if (ctx_free->here) 652 krb5_free_principal(context, ctx_free->here); 653 if (ctx_free->there) 654 krb5_free_principal(context, ctx_free->there); 655 if (ctx_free->subkey) 656 krb5_free_keyblock(context, ctx_free->subkey); 657 xfree(ctx_free); 658 } else 659 (void)krb5_gss_delete_sec_context(minor_status, context_handle, NULL); 660 661 *minor_status = code; 662 return (major_status); 663 } 664 665 /* 666 * mutual_auth 667 * 668 * Handle the reply from the acceptor, if we're doing mutual auth. 669 */ 670 static OM_uint32 671 mutual_auth( 672 OM_uint32 *minor_status, 673 gss_ctx_id_t *context_handle, 674 gss_name_t target_name, 675 gss_OID mech_type, 676 OM_uint32 req_flags, 677 OM_uint32 time_req, 678 gss_channel_bindings_t input_chan_bindings, 679 gss_buffer_t input_token, 680 gss_OID *actual_mech_type, 681 gss_buffer_t output_token, 682 OM_uint32 *ret_flags, 683 OM_uint32 *time_rec, 684 krb5_context context) 685 { 686 OM_uint32 major_status; 687 unsigned char *ptr; 688 char *sptr; 689 krb5_data ap_rep; 690 krb5_ap_rep_enc_part *ap_rep_data; 691 krb5_timestamp now; 692 krb5_gss_ctx_id_rec *ctx; 693 krb5_error *krb_error; 694 krb5_error_code code; 695 krb5int_access kaccess; 696 697 major_status = GSS_S_FAILURE; 698 699 code = krb5int_accessor (&kaccess, KRB5INT_ACCESS_VERSION); 700 if (code) 701 goto fail; 702 703 /* validate the context handle */ 704 /*SUPPRESS 29*/ 705 if (! kg_validate_ctx_id(*context_handle)) { 706 *minor_status = (OM_uint32) G_VALIDATE_FAILED; 707 return(GSS_S_NO_CONTEXT); 708 } 709 710 ctx = (krb5_gss_ctx_id_t) *context_handle; 711 712 /* make sure the context is non-established, and that certain 713 arguments are unchanged */ 714 715 if ((ctx->established) || 716 ((ctx->gss_flags & GSS_C_MUTUAL_FLAG) == 0)) { 717 code = KG_CONTEXT_ESTABLISHED; 718 goto fail; 719 } 720 721 if (! krb5_principal_compare(context, ctx->there, 722 (krb5_principal) target_name)) { 723 (void)krb5_gss_delete_sec_context(minor_status, 724 context_handle, NULL); 725 code = 0; 726 major_status = GSS_S_BAD_NAME; 727 goto fail; 728 } 729 730 /* verify the token and leave the AP_REP message in ap_rep */ 731 732 if (input_token == GSS_C_NO_BUFFER) { 733 (void)krb5_gss_delete_sec_context(minor_status, 734 context_handle, NULL); 735 code = 0; 736 major_status = GSS_S_DEFECTIVE_TOKEN; 737 goto fail; 738 } 739 740 ptr = (unsigned char *) input_token->value; 741 742 if (g_verify_token_header(ctx->mech_used, 743 &(ap_rep.length), 744 &ptr, KG_TOK_CTX_AP_REP, 745 input_token->length, 1)) { 746 if (g_verify_token_header((gss_OID) ctx->mech_used, 747 &(ap_rep.length), 748 &ptr, KG_TOK_CTX_ERROR, 749 input_token->length, 1) == 0) { 750 751 /* Handle a KRB_ERROR message from the server */ 752 753 sptr = (char *) ptr; /* PC compiler bug */ 754 TREAD_STR(sptr, ap_rep.data, ap_rep.length); 755 756 code = krb5_rd_error(context, &ap_rep, &krb_error); 757 if (code) 758 goto fail; 759 if (krb_error->error) 760 code = krb_error->error + ERROR_TABLE_BASE_krb5; 761 else 762 code = 0; 763 krb5_free_error(context, krb_error); 764 goto fail; 765 } else { 766 *minor_status = 0; 767 return(GSS_S_DEFECTIVE_TOKEN); 768 } 769 } 770 771 sptr = (char *) ptr; /* PC compiler bug */ 772 TREAD_STR(sptr, ap_rep.data, ap_rep.length); 773 774 /* decode the ap_rep */ 775 if ((code = krb5_rd_rep(context, ctx->auth_context, &ap_rep, 776 &ap_rep_data))) { 777 /* 778 * XXX A hack for backwards compatiblity. 779 * To be removed in 1999 -- proven 780 */ 781 krb5_auth_con_setuseruserkey(context, ctx->auth_context, 782 ctx->subkey); 783 if ((krb5_rd_rep(context, ctx->auth_context, &ap_rep, 784 &ap_rep_data))) 785 goto fail; 786 } 787 788 /* store away the sequence number */ 789 ctx->seq_recv = ap_rep_data->seq_number; 790 g_order_init(&(ctx->seqstate), ctx->seq_recv, 791 (ctx->gss_flags & GSS_C_REPLAY_FLAG) != 0, 792 (ctx->gss_flags & GSS_C_SEQUENCE_FLAG) !=0, ctx->proto); 793 794 if (ctx->proto == 1 && ap_rep_data->subkey) { 795 /* Keep acceptor's subkey. */ 796 ctx->have_acceptor_subkey = 1; 797 code = krb5_copy_keyblock(context, ap_rep_data->subkey, 798 &ctx->acceptor_subkey); 799 if (code) 800 goto fail; 801 code = (*kaccess.krb5int_c_mandatory_cksumtype)(context, 802 ctx->acceptor_subkey->enctype, 803 &ctx->acceptor_subkey_cksumtype); 804 if (code) 805 goto fail; 806 } 807 808 /* free the ap_rep_data */ 809 krb5_free_ap_rep_enc_part(context, ap_rep_data); 810 811 /* set established */ 812 ctx->established = 1; 813 814 /* set returns */ 815 816 if (time_rec) { 817 if ((code = krb5_timeofday(context, &now))) 818 goto fail; 819 *time_rec = ctx->endtime - now; 820 } 821 822 if (ret_flags) 823 *ret_flags = ctx->gss_flags; 824 825 if (actual_mech_type) 826 *actual_mech_type = mech_type; 827 828 /* success */ 829 830 *minor_status = 0; 831 return GSS_S_COMPLETE; 832 833 fail: 834 (void)krb5_gss_delete_sec_context(minor_status, context_handle, NULL); 835 836 *minor_status = code; 837 return (major_status); 838 } 839 840 OM_uint32 841 krb5_gss_init_sec_context(minor_status, claimant_cred_handle, 842 context_handle, target_name, mech_type, 843 req_flags, time_req, input_chan_bindings, 844 input_token, actual_mech_type, output_token, 845 ret_flags, time_rec) 846 OM_uint32 *minor_status; 847 gss_cred_id_t claimant_cred_handle; 848 gss_ctx_id_t *context_handle; 849 gss_name_t target_name; 850 gss_OID mech_type; 851 OM_uint32 req_flags; 852 OM_uint32 time_req; 853 gss_channel_bindings_t input_chan_bindings; 854 gss_buffer_t input_token; 855 gss_OID *actual_mech_type; 856 gss_buffer_t output_token; 857 OM_uint32 *ret_flags; 858 OM_uint32 *time_rec; 859 { 860 krb5_context context; 861 krb5_gss_cred_id_t cred; 862 int err; 863 krb5_error_code kerr; 864 int default_mech = 0; 865 OM_uint32 major_status; 866 OM_uint32 tmp_min_stat; 867 868 if (*context_handle == GSS_C_NO_CONTEXT) { 869 kerr = krb5_gss_init_context(&context); 870 if (kerr) { 871 *minor_status = kerr; 872 return GSS_S_FAILURE; 873 } 874 if (GSS_ERROR(kg_sync_ccache_name(context, minor_status))) 875 return GSS_S_FAILURE; 876 } else { 877 context = ((krb5_gss_ctx_id_rec *)*context_handle)->k5_context; 878 } 879 880 /* set up return values so they can be "freed" successfully */ 881 882 major_status = GSS_S_FAILURE; /* Default major code */ 883 output_token->length = 0; 884 output_token->value = NULL; 885 if (actual_mech_type) 886 *actual_mech_type = NULL; 887 888 /* verify that the target_name is valid and usable */ 889 890 if (! kg_validate_name(target_name)) { 891 *minor_status = (OM_uint32) G_VALIDATE_FAILED; 892 if (*context_handle == GSS_C_NO_CONTEXT) 893 krb5_free_context(context); 894 return(GSS_S_CALL_BAD_STRUCTURE|GSS_S_BAD_NAME); 895 } 896 897 /* verify the credential, or use the default */ 898 /*SUPPRESS 29*/ 899 if (claimant_cred_handle == GSS_C_NO_CREDENTIAL) { 900 /* 901 * Solaris Kerberos: here we are using the Solaris specific 902 * function get_default_cred() to handle the special case of a 903 * root principal 904 */ 905 major_status = get_default_cred(minor_status, context, 906 (gss_cred_id_t *)&cred); 907 if (major_status && GSS_ERROR(major_status)) { 908 if (*context_handle == GSS_C_NO_CONTEXT) 909 krb5_free_context(context); 910 return(major_status); 911 } 912 } else { 913 major_status = krb5_gss_validate_cred(minor_status, claimant_cred_handle); 914 if (GSS_ERROR(major_status)) { 915 if (*context_handle == GSS_C_NO_CONTEXT) 916 krb5_free_context(context); 917 return(major_status); 918 } 919 cred = (krb5_gss_cred_id_t) claimant_cred_handle; 920 } 921 kerr = k5_mutex_lock(&cred->lock); 922 if (kerr) { 923 krb5_free_context(context); 924 *minor_status = kerr; 925 return GSS_S_FAILURE; 926 } 927 928 /* verify the mech_type */ 929 930 err = 0; 931 if (mech_type == GSS_C_NULL_OID) { 932 default_mech = 1; 933 if (cred->rfc_mech) { 934 mech_type = (gss_OID) gss_mech_krb5; 935 } else if (cred->prerfc_mech) { 936 mech_type = (gss_OID) gss_mech_krb5_old; 937 } else { 938 err = 1; 939 } 940 } else if (g_OID_equal(mech_type, gss_mech_krb5)) { 941 if (!cred->rfc_mech) 942 err = 1; 943 } else if (g_OID_equal(mech_type, gss_mech_krb5_old)) { 944 if (!cred->prerfc_mech) 945 err = 1; 946 } else if (g_OID_equal(mech_type, gss_mech_krb5_wrong)) { 947 if (!cred->rfc_mech) 948 err = 1; 949 } else { 950 err = 1; 951 } 952 953 if (err) { 954 k5_mutex_unlock(&cred->lock); 955 if (claimant_cred_handle == GSS_C_NO_CREDENTIAL) 956 krb5_gss_release_cred(minor_status, (gss_cred_id_t *)&cred); 957 *minor_status = 0; 958 if (*context_handle == GSS_C_NO_CONTEXT) 959 krb5_free_context(context); 960 return(GSS_S_BAD_MECH); 961 } 962 963 /* is this a new connection or not? */ 964 965 /*SUPPRESS 29*/ 966 if (*context_handle == GSS_C_NO_CONTEXT) { 967 major_status = new_connection(minor_status, cred, context_handle, 968 target_name, mech_type, req_flags, 969 time_req, input_chan_bindings, 970 input_token, actual_mech_type, 971 output_token, ret_flags, time_rec, 972 context, default_mech); 973 k5_mutex_unlock(&cred->lock); 974 if (*context_handle == GSS_C_NO_CONTEXT) 975 krb5_free_context(context); 976 else 977 ((krb5_gss_ctx_id_rec *) *context_handle)->k5_context = context; 978 } else { 979 /* mutual_auth doesn't care about the credentials */ 980 k5_mutex_unlock(&cred->lock); 981 major_status = mutual_auth(minor_status, context_handle, 982 target_name, mech_type, req_flags, 983 time_req, input_chan_bindings, 984 input_token, actual_mech_type, 985 output_token, ret_flags, time_rec, 986 context); 987 /* If context_handle is now NO_CONTEXT, mutual_auth called 988 delete_sec_context, which would've zapped the krb5 context 989 too. */ 990 } 991 992 if (claimant_cred_handle == GSS_C_NO_CREDENTIAL) 993 krb5_gss_release_cred(&tmp_min_stat, (gss_cred_id_t *)&cred); 994 995 return(major_status); 996 } 997 998 #ifndef _WIN32 999 k5_mutex_t kg_kdc_flag_mutex = K5_MUTEX_PARTIAL_INITIALIZER; 1000 static int kdc_flag = 0; 1001 #endif 1002 1003 krb5_error_code 1004 krb5_gss_init_context (krb5_context *ctxp) 1005 { 1006 krb5_error_code err; 1007 #ifndef _WIN32 1008 int is_kdc; 1009 #endif 1010 1011 err = gssint_initialize_library(); 1012 if (err) 1013 return err; 1014 #ifndef _WIN32 1015 err = k5_mutex_lock(&kg_kdc_flag_mutex); 1016 if (err) 1017 return err; 1018 is_kdc = kdc_flag; 1019 k5_mutex_unlock(&kg_kdc_flag_mutex); 1020 1021 if (is_kdc) 1022 return krb5int_init_context_kdc(ctxp); 1023 #endif 1024 1025 return krb5_init_context(ctxp); 1026 } 1027 1028 #ifndef _WIN32 1029 krb5_error_code 1030 krb5_gss_use_kdc_context() 1031 { 1032 krb5_error_code err; 1033 1034 err = gssint_initialize_library(); 1035 if (err) 1036 return err; 1037 err = k5_mutex_lock(&kg_kdc_flag_mutex); 1038 if (err) 1039 return err; 1040 kdc_flag = 1; 1041 k5_mutex_unlock(&kg_kdc_flag_mutex); 1042 return 0; 1043 } 1044 #endif 1045 1046 /* Solaris Kerberos specific routines start */ 1047 1048 #define ROOT_UID 0 1049 #define KRB5_DEFAULT_LIFE 60*60*10 1050 #define CACHE_FILENAME_LEN 35 1051 1052 extern int 1053 safechown(const char *src, uid_t uid, gid_t gid, int mode); 1054 1055 static krb5_boolean 1056 principal_ignore_inst_compare(context, princ1, princ2) 1057 krb5_context context; 1058 krb5_const_principal princ1; 1059 krb5_const_principal princ2; 1060 { 1061 krb5_int32 nelem; 1062 1063 nelem = krb5_princ_size(context, princ1); 1064 if (nelem != krb5_princ_size(context, princ2)) 1065 return FALSE; 1066 1067 if (! krb5_realm_compare(context, princ1, princ2)) 1068 return FALSE; 1069 1070 /* 1071 * Solaris Kerberos 1072 * If princ1 is elem1/metachar@REALM, compare just elem1 (and REALM). 1073 */ 1074 if (nelem == 2) { 1075 const krb5_data *p = krb5_princ_component(context, princ1, 1); 1076 1077 if (p->length == 1) { 1078 const char *s = p->data; 1079 1080 if (s[0] == '*') { 1081 const krb5_data *p1 = krb5_princ_component(context, princ1, 0); 1082 const krb5_data *p2 = krb5_princ_component(context, princ2, 0); 1083 1084 if (p1->length != p2->length || 1085 memcmp(p1->data, p2->data, p1->length)) 1086 return FALSE; 1087 1088 return TRUE; 1089 } 1090 } 1091 } 1092 1093 return FALSE; 1094 } 1095 1096 /* 1097 * Solaris Kerberos 1098 * This is a dup of krb5_ktfile_get_entry (sigh) but is necessary to 1099 * to get a custom princ compare above (principal_ignore_inst_compare) 1100 * and thus avoid mucking w/important krb5 internal 1101 * api (krb5_principal_compare) 1102 */ 1103 #include "../krb5/keytab/file/ktfile.h" 1104 1105 static krb5_error_code KRB5_CALLCONV 1106 ktfile_get_entry(context, id, principal, kvno, enctype, entry) 1107 krb5_context context; 1108 krb5_keytab id; 1109 krb5_const_principal principal; 1110 krb5_kvno kvno; 1111 krb5_enctype enctype; 1112 krb5_keytab_entry * entry; 1113 { 1114 krb5_keytab_entry cur_entry, new_entry; 1115 krb5_error_code kerror = 0; 1116 int found_wrong_kvno = 0; 1117 krb5_boolean similar; 1118 int kvno_offset = 0; 1119 1120 KRB5_LOG0(KRB5_INFO, "ktfile_get_entry() start\n"); 1121 1122 /* Open the keyfile for reading */ 1123 if ((kerror = krb5_ktfileint_openr(context, id))){ 1124 KRB5_LOG(KRB5_ERR, "ktfile_get_entry() end, ktfileint_openr() " 1125 "kerror= %d\n", kerror); 1126 return(kerror); 1127 } 1128 1129 /* 1130 * For efficiency and simplicity, we'll use a while true that 1131 * is exited with a break statement. 1132 */ 1133 cur_entry.principal = 0; 1134 cur_entry.vno = 0; 1135 cur_entry.key.contents = 0; 1136 /*CONSTCOND*/ 1137 while (TRUE) { 1138 if ((kerror = krb5_ktfileint_read_entry(context, id, &new_entry))) 1139 break; 1140 1141 /* 1142 * by the time this loop exits, it must either free cur_entry, 1143 * and copy new_entry there, or free new_entry. Otherwise, it 1144 * leaks. 1145 */ 1146 1147 /* 1148 * if the principal isn't the one requested, free new_entry 1149 * and continue to the next. 1150 */ 1151 1152 if (!principal_ignore_inst_compare(context, principal, 1153 new_entry.principal)) { 1154 krb5_kt_free_entry(context, &new_entry); 1155 continue; 1156 } 1157 1158 /* 1159 * if the enctype is not ignored and doesn't match, free new_entry 1160 * and continue to the next 1161 */ 1162 1163 if (enctype != IGNORE_ENCTYPE) { 1164 if ((kerror = krb5_c_enctype_compare(context, enctype, 1165 new_entry.key.enctype, 1166 &similar))) { 1167 krb5_kt_free_entry(context, &new_entry); 1168 break; 1169 } 1170 1171 if (!similar) { 1172 krb5_kt_free_entry(context, &new_entry); 1173 continue; 1174 } 1175 /* 1176 * Coerce the enctype of the output keyblock in case we 1177 * got an inexact match on the enctype. 1178 */ 1179 new_entry.key.enctype = enctype; 1180 } 1181 1182 if (kvno == IGNORE_VNO) { 1183 /* 1184 * if this is the first match, or if the new vno is 1185 * bigger, free the current and keep the new. Otherwise, 1186 * free the new. 1187 */ 1188 /* 1189 * A 1.2.x keytab contains only the low 8 bits of the key 1190 * version number. Since it can be much bigger, and thus 1191 * the 8-bit value can wrap, we need some heuristics to 1192 * figure out the "highest" numbered key if some numbers 1193 * close to 255 and some near 0 are used. 1194 * 1195 * The heuristic here: 1196 1197 * If we have any keys with versions over 240, then assume 1198 * that all version numbers 0-127 refer to 256+N instead. 1199 * Not perfect, but maybe good enough? 1200 */ 1201 1202 #define M(VNO) (((VNO) - kvno_offset + 256) % 256) 1203 1204 if (new_entry.vno > 240) 1205 kvno_offset = 128; 1206 if (! cur_entry.principal || 1207 M(new_entry.vno) > M(cur_entry.vno)) { 1208 krb5_kt_free_entry(context, &cur_entry); 1209 cur_entry = new_entry; 1210 } else { 1211 krb5_kt_free_entry(context, &new_entry); 1212 } 1213 } else { 1214 /* 1215 * if this kvno matches, free the current (will there ever 1216 * be one?), keep the new, and break out. Otherwise, remember 1217 * that we were here so we can return the right error, and 1218 * free the new 1219 */ 1220 /* 1221 * Yuck. The krb5-1.2.x keytab format only stores one byte 1222 * for the kvno, so we're toast if the kvno requested is 1223 * higher than that. Short-term workaround: only compare 1224 * the low 8 bits. 1225 */ 1226 1227 if (new_entry.vno == (kvno & 0xff)) { 1228 krb5_kt_free_entry(context, &cur_entry); 1229 cur_entry = new_entry; 1230 break; 1231 } else { 1232 found_wrong_kvno++; 1233 krb5_kt_free_entry(context, &new_entry); 1234 } 1235 } 1236 } 1237 1238 if (kerror == KRB5_KT_END) { 1239 if (cur_entry.principal) 1240 kerror = 0; 1241 else if (found_wrong_kvno) 1242 kerror = KRB5_KT_KVNONOTFOUND; 1243 else 1244 kerror = KRB5_KT_NOTFOUND; 1245 } 1246 if (kerror) { 1247 (void) krb5_ktfileint_close(context, id); 1248 krb5_kt_free_entry(context, &cur_entry); 1249 KRB5_LOG(KRB5_ERR,"ktfile_get_entry() end, kerror=" 1250 "%d\n", kerror); 1251 return kerror; 1252 } 1253 if ((kerror = krb5_ktfileint_close(context, id)) != 0) { 1254 krb5_kt_free_entry(context, &cur_entry); 1255 KRB5_LOG(KRB5_ERR,"ktfile_get_entry() end, ktfileint_close() " 1256 "kerror= %d\n", kerror); 1257 return kerror; 1258 } 1259 *entry = cur_entry; 1260 1261 /* Let us close the file before we leave */ 1262 (void) krb5_ktfileint_close(context, id); 1263 1264 KRB5_LOG0(KRB5_INFO, "ktfile_get_entry() end"); 1265 1266 return 0; 1267 } 1268 1269 1270 /* 1271 * Solaris Kerberos 1272 * Given a princ of name/instance@LOCALREALM, search the keytab 1273 * for a match of name and LOCALREALM and if found, return instance 1274 * as a string. 1275 * 1276 * Caller must free returned string. 1277 */ 1278 static krb5_error_code 1279 get_instance_keytab( 1280 krb5_context context, 1281 const char *sname, 1282 krb5_keytab keytab, 1283 char **instance) /* out */ 1284 { 1285 krb5_error_code ret=0; 1286 krb5_keytab_entry kt_ent; 1287 krb5_int32 nelem, free_kt_ent=0; 1288 register const krb5_data *p; 1289 char *realm=NULL, *s=NULL; 1290 krb5_principal client=NULL, princ=NULL; 1291 1292 if (!keytab) 1293 return EINVAL; 1294 1295 if (ret = krb5_get_default_realm(context, &realm)) 1296 return ret; 1297 1298 ret = krb5_build_principal(context, &client, strlen(realm), 1299 realm, sname, "*", 1300 (char *)0); 1301 if (ret) 1302 goto out; 1303 1304 ret = ktfile_get_entry(context, keytab, client, 1305 0, /* don't have vno available */ 1306 0, &kt_ent); 1307 if (ret) 1308 goto out; 1309 1310 free_kt_ent++; /* kt_ent is not a ptr */ 1311 1312 princ = kt_ent.principal; 1313 nelem = krb5_princ_size(context, princ); 1314 if (nelem != 2) { 1315 ret = KRB5_PRINC_NOMATCH; 1316 goto out; 1317 } 1318 1319 p = krb5_princ_component(context, princ, 1); 1320 s = calloc(p->length + sizeof(char), sizeof(char)); 1321 if (!s) { 1322 ret = ENOMEM; 1323 goto out; 1324 } 1325 1326 (void) memcpy(s, p->data, p->length); 1327 1328 1329 out: 1330 free(realm); 1331 if (client) 1332 krb5_free_principal(context, client); 1333 if (free_kt_ent) 1334 (void) krb5_kt_free_entry(context, &kt_ent); 1335 1336 if (ret == 0) 1337 *instance = s; 1338 return ret; 1339 } 1340 1341 static OM_uint32 1342 load_root_cred_using_keytab( 1343 OM_uint32 *minor_status, 1344 krb5_context context, 1345 const char *sname, 1346 int use_nodename) 1347 { 1348 krb5_creds my_creds; 1349 krb5_principal me; 1350 krb5_principal server; 1351 krb5_error_code code; 1352 krb5_ccache ccache = NULL; 1353 krb5_keytab keytab = NULL; 1354 krb5_timestamp now; 1355 krb5_deltat lifetime = KRB5_DEFAULT_LIFE; /* -l option */ 1356 krb5_get_init_creds_opt opt; 1357 krb5_data tgtname = { 1358 0, 1359 KRB5_TGS_NAME_SIZE, 1360 KRB5_TGS_NAME 1361 }; 1362 char *svcname = NULL; 1363 1364 KRB5_LOG0(KRB5_INFO, "load_root_cred_using_keytab() start \n"); 1365 1366 if (!sname) 1367 return (GSS_S_FAILURE); 1368 1369 memset((char *)&my_creds, 0, sizeof(my_creds)); 1370 1371 if (code = krb5_kt_default(context, &keytab)) { 1372 *minor_status = code; 1373 return (GSS_S_FAILURE); 1374 } 1375 1376 if (!use_nodename) { 1377 char *instance = NULL; 1378 1379 code = get_instance_keytab(context, sname, keytab, &instance); 1380 if (code == 0) { 1381 code = krb5_sname_to_principal(context, 1382 instance, sname, 1383 KRB5_NT_UNKNOWN, &me); 1384 free(instance); 1385 } 1386 } else { 1387 code = krb5_sname_to_principal(context, NULL, sname, 1388 KRB5_NT_SRV_HST, &me); 1389 } 1390 if (code) { 1391 (void) krb5_kt_close(context, keytab); 1392 *minor_status = code; 1393 return (GSS_S_FAILURE); 1394 } 1395 1396 my_creds.client = me; 1397 1398 if((code = krb5_build_principal_ext(context, &server, 1399 krb5_princ_realm(context, me)->length, 1400 krb5_princ_realm(context, me)->data, 1401 tgtname.length, tgtname.data, 1402 krb5_princ_realm(context, me)->length, 1403 krb5_princ_realm(context, me)->data, 1404 0))) { 1405 *minor_status = code; 1406 krb5_free_cred_contents(context, &my_creds); 1407 (void) krb5_kt_close(context, keytab); 1408 1409 return (GSS_S_FAILURE); 1410 } 1411 1412 my_creds.server = server; 1413 my_creds.times.starttime = 0; /* start timer 1414 * when request 1415 * gets to KDC 1416 */ 1417 if ((code = krb5_timeofday(context, &now))) { 1418 *minor_status = code; 1419 krb5_free_cred_contents(context, &my_creds); 1420 (void) krb5_kt_close(context, keytab); 1421 1422 return (GSS_S_FAILURE); 1423 } 1424 my_creds.times.endtime = now + lifetime; 1425 my_creds.times.renew_till = 0; 1426 1427 memset(&opt, 0, sizeof (opt)); 1428 krb5_get_init_creds_opt_init(&opt); 1429 krb5_get_init_creds_opt_set_tkt_life(&opt, lifetime); 1430 1431 code = krb5_unparse_name(context, server, &svcname); 1432 if (code != 0) { 1433 *minor_status = code; 1434 krb5_free_cred_contents(context, &my_creds); 1435 (void) krb5_kt_close(context, keytab); 1436 1437 return (GSS_S_FAILURE); 1438 } 1439 /* 1440 * Evidently (sigh), on success, krb5_get_init_creds_keytab 1441 * changes the my_creds princ ptrs so we need to free those 1442 * princs (me&server) as well as freeing all of my_creds contents. 1443 */ 1444 code = krb5_get_init_creds_keytab(context, 1445 &my_creds, me, keytab, 1446 0, svcname, &opt); 1447 1448 (void) krb5_kt_close(context, keytab); 1449 1450 if (svcname != NULL) 1451 free(svcname); 1452 if (code) { 1453 *minor_status = code; 1454 krb5_free_cred_contents(context, &my_creds); 1455 1456 return (GSS_S_FAILURE); 1457 } 1458 1459 krb5_free_principal(context, server); 1460 server = NULL; 1461 1462 code = krb5_cc_resolve (context, 1463 krb5_cc_default_name(context), 1464 &ccache); 1465 if (code != 0) { 1466 *minor_status = code; 1467 krb5_free_cred_contents(context, &my_creds); 1468 krb5_free_principal(context, me); 1469 1470 return (GSS_S_FAILURE); 1471 } 1472 code = krb5_cc_initialize (context, ccache, me); 1473 krb5_free_principal(context, me); 1474 me = NULL; 1475 if (code != 0) { 1476 *minor_status = code; 1477 krb5_free_cred_contents(context, &my_creds); 1478 (void) krb5_cc_close(context, ccache); 1479 1480 return (GSS_S_FAILURE); 1481 } 1482 1483 code = krb5_cc_store_cred(context, ccache, 1484 &my_creds); 1485 krb5_free_cred_contents(context, &my_creds); 1486 (void) krb5_cc_close(context, ccache); 1487 1488 if (code) { 1489 *minor_status = code; 1490 1491 KRB5_LOG(KRB5_ERR, "load_root_cred_using_keytab() end, error " 1492 "code = %d\n", code); 1493 1494 return (GSS_S_FAILURE); 1495 } 1496 1497 KRB5_LOG0(KRB5_INFO, "load_root_cred_using_keytab() end \n"); 1498 1499 return (GSS_S_COMPLETE); 1500 } 1501 1502 static OM_uint32 1503 renew_ccache(OM_uint32 *minor_status, krb5_context context, uid_t uid) 1504 { 1505 krb5_principal me; 1506 krb5_principal server; 1507 krb5_creds creds; 1508 krb5_creds tmpcreds; 1509 krb5_creds *out_creds; 1510 krb5_error_code code; 1511 krb5_ccache ccache = NULL; 1512 static char ccache_name_buf[CACHE_FILENAME_LEN]; 1513 int options = 0; 1514 krb5_data tgtname = { 1515 0, 1516 KRB5_TGS_NAME_SIZE, 1517 KRB5_TGS_NAME 1518 }; 1519 gid_t gid = getgid(); 1520 1521 memset((char *)&creds, 0, sizeof(creds)); 1522 memset((char *)&tmpcreds, 0, sizeof(creds)); 1523 1524 if ((code = krb5_cc_default(context, &ccache))) { 1525 *minor_status = code; 1526 (void) krb5_cc_close(context, ccache); 1527 return (GSS_S_FAILURE); 1528 } 1529 1530 if ((code = krb5_cc_get_principal(context, ccache, &me)) != 0) { 1531 *minor_status = code; 1532 (void) krb5_cc_close(context, ccache); 1533 return (GSS_S_FAILURE); 1534 } 1535 1536 creds.client = me; 1537 1538 if((code = krb5_build_principal_ext(context, &server, 1539 krb5_princ_realm(context, me)->length, 1540 krb5_princ_realm(context, me)->data, 1541 tgtname.length, tgtname.data, 1542 krb5_princ_realm(context, me)->length, 1543 krb5_princ_realm(context, me)->data, 1544 0))) { 1545 krb5_free_principal(context, me); 1546 (void) krb5_cc_close(context, ccache); 1547 *minor_status = code; 1548 return (GSS_S_FAILURE); 1549 } 1550 1551 creds.server = server; 1552 creds.ticket_flags = TKT_FLG_RENEWABLE; 1553 1554 if ((krb5_cc_retrieve_cred(context, ccache, KRB5_TC_MATCH_FLAGS, 1555 &creds, &tmpcreds))) { 1556 (void) krb5_cc_close(context, ccache); 1557 return (KDC_ERR_BADOPTION); 1558 } 1559 1560 creds.ticket_flags = 0; 1561 code = krb5_get_credentials_renew(context, options, ccache, 1562 &creds, &out_creds); 1563 krb5_free_cred_contents(context, &creds); 1564 krb5_free_cred_contents(context, &tmpcreds); 1565 1566 if (code) { 1567 *minor_status = code; 1568 return (GSS_S_FAILURE); 1569 } 1570 1571 krb5_free_creds(context, out_creds); 1572 snprintf(ccache_name_buf, CACHE_FILENAME_LEN, "/tmp/krb5cc_%d", 1573 uid, -1); 1574 code = safechown(ccache_name_buf, uid, gid, -1); 1575 1576 if (code == -1) { 1577 (void) krb5_cc_destroy(context, ccache); 1578 *minor_status = code; 1579 return (GSS_S_FAILURE); 1580 } 1581 1582 (void) krb5_cc_close(context, ccache); 1583 1584 return (GSS_S_COMPLETE); 1585 1586 } 1587 1588 /* 1589 * Solaris Kerberos: 1590 * We enforce a minimum refresh time on the root cred. This avoids problems for 1591 * the higher level communication protocol for having valid creds and 1592 * setting up a valid context, only to have it expire before or while 1593 * it is being used. For non root users we don't care since we do not refresh 1594 * there creds, they get what they can get. 1595 */ 1596 #define MIN_REFRESH_TIME 300 1597 #define MIN_RENEW_TIME 1500 1598 1599 /* get_default_cred() must be called with the krb5_mutex lock held */ 1600 static OM_uint32 1601 get_default_cred(OM_uint32 *minor_status, void *ct, gss_cred_id_t *cred_handle) 1602 { 1603 krb5_timestamp now; 1604 krb5_gss_cred_id_t cred; 1605 OM_uint32 major; 1606 OM_uint32 mntmp; 1607 /* 1608 * Solaris Kerberos 1609 * Use krb5_getuid() to select the mechanism to obtain the uid. 1610 */ 1611 uid_t uid = krb5_getuid(); 1612 krb5_context context = (krb5_context)ct; 1613 1614 KRB5_LOG0(KRB5_INFO, "get_default_cred() start\n"); 1615 1616 /* Get the default cred for user */ 1617 if (((major = kg_get_defcred(minor_status, cred_handle)) != NULL) && 1618 GSS_ERROR(major)) { 1619 1620 /* If we're not root we're done */ 1621 if (uid != ROOT_UID) 1622 return (major); 1623 1624 /* 1625 * Try and get root's cred in the cache using keytab. 1626 * 1627 * First try "root" and then try "host" - this allows 1628 * Secure NFS to use the host principal for mounting if 1629 * there is no root principal. 1630 * 1631 * Then try "host/<anything>" to match any instance (needed 1632 * for DHCP clients). 1633 */ 1634 major = load_root_cred_using_keytab(minor_status, 1635 context, "root", 1); 1636 1637 if (major != GSS_S_COMPLETE) 1638 major = load_root_cred_using_keytab(minor_status, 1639 context, "host", 1); 1640 if (major != GSS_S_COMPLETE) 1641 major = load_root_cred_using_keytab(minor_status, 1642 context, "host", 0); 1643 1644 if (major != GSS_S_COMPLETE) 1645 return (major); 1646 1647 /* We should have valid tgt now in the cache, so get it. */ 1648 major = kg_get_defcred(minor_status, cred_handle); 1649 1650 return (major); 1651 } 1652 1653 /* We've got a gss cred handle that is a kerberos cred handle. */ 1654 cred = (krb5_gss_cred_id_t)*cred_handle; 1655 1656 /* If we can't get the time, assume the worst. */ 1657 if (krb5_timeofday(context, &now)) { 1658 (void) krb5_gss_release_cred(&mntmp, cred_handle); 1659 return (GSS_S_CREDENTIALS_EXPIRED); 1660 } 1661 1662 /* If root's cred has expired re-get it */ 1663 if (cred->tgt_expire < now + MIN_REFRESH_TIME && uid == ROOT_UID) { 1664 (void) krb5_gss_release_cred(&mntmp, cred_handle); 1665 1666 major = load_root_cred_using_keytab(minor_status, 1667 context, "root", 1); 1668 1669 if (major != GSS_S_COMPLETE) 1670 major = load_root_cred_using_keytab(minor_status, 1671 context, "host", 1); 1672 1673 if (major != GSS_S_COMPLETE) 1674 major = load_root_cred_using_keytab(minor_status, 1675 context, "host", 0); 1676 1677 if (major != GSS_S_COMPLETE) 1678 return (major); 1679 1680 major = kg_get_defcred(minor_status, cred_handle); 1681 if (major != GSS_S_COMPLETE) 1682 return (major); 1683 1684 /* Any body else is SOL unless we can renew their credential cache */ 1685 } else if ((cred->tgt_expire < now + MIN_RENEW_TIME) && 1686 (cred->tgt_expire > now)) { 1687 (void) krb5_gss_release_cred(&mntmp, cred_handle); 1688 1689 major = renew_ccache(minor_status, context, uid); 1690 if ((major != GSS_S_COMPLETE) && 1691 (major != KDC_ERR_BADOPTION)) 1692 return (major); 1693 1694 major = kg_get_defcred(minor_status, cred_handle); 1695 if (major != GSS_S_COMPLETE) 1696 return (major); 1697 1698 } 1699 1700 /* Otherwise we got non expired creds */ 1701 1702 KRB5_LOG0(KRB5_INFO, "get_default_cred() end\n"); 1703 1704 return (GSS_S_COMPLETE); 1705 } 1706 1707 /* Solaris Kerberos specific routines end */ 1708