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