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