1 /* 2 * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 3 * Use is subject to license terms. 4 */ 5 6 7 /* 8 * Copyright 2000,2002, 2003 by the Massachusetts Institute of Technology. 9 * All Rights Reserved. 10 * 11 * Export of this software from the United States of America may 12 * require a specific license from the United States Government. 13 * It is the responsibility of any person or organization contemplating 14 * export to obtain such a license before exporting. 15 * 16 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and 17 * distribute this software and its documentation for any purpose and 18 * without fee is hereby granted, provided that the above copyright 19 * notice appear in all copies and that both that copyright notice and 20 * this permission notice appear in supporting documentation, and that 21 * the name of M.I.T. not be used in advertising or publicity pertaining 22 * to distribution of the software without specific, written prior 23 * permission. Furthermore if you modify this software you must label 24 * your software as modified software and not distribute it in such a 25 * fashion that it might be confused with the original M.I.T. software. 26 * M.I.T. makes no representations about the suitability of 27 * this software for any purpose. It is provided "as is" without express 28 * or implied warranty. 29 * 30 */ 31 /* 32 * Copyright 1993 by OpenVision Technologies, Inc. 33 * 34 * Permission to use, copy, modify, distribute, and sell this software 35 * and its documentation for any purpose is hereby granted without fee, 36 * provided that the above copyright notice appears in all copies and 37 * that both that copyright notice and this permission notice appear in 38 * supporting documentation, and that the name of OpenVision not be used 39 * in advertising or publicity pertaining to distribution of the software 40 * without specific, written prior permission. OpenVision makes no 41 * representations about the suitability of this software for any 42 * purpose. It is provided "as is" without express or implied warranty. 43 * 44 * OPENVISION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, 45 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO 46 * EVENT SHALL OPENVISION BE LIABLE FOR ANY SPECIAL, INDIRECT OR 47 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF 48 * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR 49 * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 50 * PERFORMANCE OF THIS SOFTWARE. 51 */ 52 53 /* 54 * Copyright (C) 1998 by the FundsXpress, INC. 55 * 56 * All rights reserved. 57 * 58 * Export of this software from the United States of America may require 59 * a specific license from the United States Government. It is the 60 * responsibility of any person or organization contemplating export to 61 * obtain such a license before exporting. 62 * 63 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and 64 * distribute this software and its documentation for any purpose and 65 * without fee is hereby granted, provided that the above copyright 66 * notice appear in all copies and that both that copyright notice and 67 * this permission notice appear in supporting documentation, and that 68 * the name of FundsXpress. not be used in advertising or publicity pertaining 69 * to distribution of the software without specific, written prior 70 * permission. FundsXpress makes no representations about the suitability of 71 * this software for any purpose. It is provided "as is" without express 72 * or implied warranty. 73 * 74 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR 75 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 76 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. 77 */ 78 79 /* Solaris Kerberos */ 80 #include <libintl.h> 81 #include <locale.h> 82 83 #include "k5-int.h" 84 #include "gss_libinit.h" 85 #include "gssapiP_krb5.h" 86 #include "mglueP.h" 87 #ifdef HAVE_MEMORY_H 88 #include <memory.h> 89 #endif 90 #include <stdlib.h> 91 #include <assert.h> 92 93 /* Solaris Kerberos start */ 94 static OM_uint32 get_default_cred(OM_uint32 *, void *, gss_cred_id_t *); 95 /* Solaris Kerberos end */ 96 97 /* 98 * $Id: init_sec_context.c 18721 2006-10-16 16:18:29Z epeisach $ 99 */ 100 101 /* XXX This is for debugging only!!! Should become a real bitfield 102 at some point */ 103 int krb5_gss_dbg_client_expcreds = 0; 104 105 /* 106 * Common code which fetches the correct krb5 credentials from the 107 * ccache. 108 */ 109 static krb5_error_code get_credentials(context, cred, server, now, 110 endtime, out_creds) 111 krb5_context context; 112 krb5_gss_cred_id_t cred; 113 krb5_principal server; 114 krb5_timestamp now; 115 krb5_timestamp endtime; 116 krb5_creds **out_creds; 117 { 118 krb5_error_code code; 119 krb5_creds in_creds; 120 121 k5_mutex_assert_locked(&cred->lock); 122 memset((char *) &in_creds, 0, sizeof(krb5_creds)); 123 124 if ((code = krb5_copy_principal(context, cred->princ, &in_creds.client))) 125 goto cleanup; 126 if ((code = krb5_copy_principal(context, server, &in_creds.server))) 127 goto cleanup; 128 in_creds.times.endtime = endtime; 129 130 in_creds.keyblock.enctype = 0; 131 132 code = krb5_get_credentials(context, 0, cred->ccache, 133 &in_creds, out_creds); 134 if (code) 135 goto cleanup; 136 137 /* 138 * Enforce a stricter limit (without timeskew forgiveness at the 139 * boundaries) because accept_sec_context code is also similarly 140 * non-forgiving. 141 */ 142 if (!krb5_gss_dbg_client_expcreds && *out_creds != NULL && 143 (*out_creds)->times.endtime < now) { 144 code = KRB5KRB_AP_ERR_TKT_EXPIRED; 145 goto cleanup; 146 } 147 148 cleanup: 149 if (in_creds.client) 150 krb5_free_principal(context, in_creds.client); 151 if (in_creds.server) 152 krb5_free_principal(context, in_creds.server); 153 return code; 154 } 155 struct gss_checksum_data { 156 krb5_gss_ctx_id_rec *ctx; 157 krb5_gss_cred_id_t cred; 158 krb5_checksum md5; 159 krb5_data checksum_data; 160 }; 161 162 #ifdef CFX_EXERCISE 163 #include "../../krb5/krb/auth_con.h" 164 #endif 165 static krb5_error_code KRB5_CALLCONV 166 make_gss_checksum (krb5_context context, krb5_auth_context auth_context, 167 void *cksum_data, krb5_data **out) 168 { 169 krb5_error_code code; 170 krb5_int32 con_flags; 171 unsigned char *ptr; 172 struct gss_checksum_data *data = cksum_data; 173 krb5_data credmsg; 174 unsigned int junk; 175 176 data->checksum_data.data = 0; 177 credmsg.data = 0; 178 /* build the checksum field */ 179 180 if (data->ctx->gss_flags & GSS_C_DELEG_FLAG) { 181 /* first get KRB_CRED message, so we know its length */ 182 183 /* clear the time check flag that was set in krb5_auth_con_init() */ 184 krb5_auth_con_getflags(context, auth_context, &con_flags); 185 krb5_auth_con_setflags(context, auth_context, 186 con_flags & ~KRB5_AUTH_CONTEXT_DO_TIME); 187 188 code = krb5_fwd_tgt_creds(context, auth_context, 0, 189 data->cred->princ, data->ctx->there, 190 data->cred->ccache, 1, 191 &credmsg); 192 193 /* turn KRB5_AUTH_CONTEXT_DO_TIME back on */ 194 krb5_auth_con_setflags(context, auth_context, con_flags); 195 196 if (code) { 197 /* don't fail here; just don't accept/do the delegation 198 request */ 199 data->ctx->gss_flags &= ~GSS_C_DELEG_FLAG; 200 201 data->checksum_data.length = 24; 202 } else { 203 if (credmsg.length+28 > KRB5_INT16_MAX) { 204 krb5_free_data_contents(context, &credmsg); 205 return(KRB5KRB_ERR_FIELD_TOOLONG); 206 } 207 208 data->checksum_data.length = 28+credmsg.length; 209 } 210 } else { 211 data->checksum_data.length = 24; 212 } 213 #ifdef CFX_EXERCISE 214 if (data->ctx->auth_context->keyblock != NULL 215 && data->ctx->auth_context->keyblock->enctype == 18) { 216 srand(time(0) ^ getpid()); 217 /* Our ftp client code stupidly assumes a base64-encoded 218 version of the token will fit in 10K, so don't make this 219 too big. */ 220 junk = rand() & 0xff; 221 } else 222 junk = 0; 223 #else 224 junk = 0; 225 #endif 226 227 data->checksum_data.length += junk; 228 229 /* now allocate a buffer to hold the checksum data and 230 (maybe) KRB_CRED msg */ 231 232 if ((data->checksum_data.data = 233 (char *) xmalloc(data->checksum_data.length)) == NULL) { 234 if (credmsg.data) 235 krb5_free_data_contents(context, &credmsg); 236 return(ENOMEM); 237 } 238 /* Solaris Kerberos */ 239 ptr = (uchar_t *)data->checksum_data.data; /* SUNW15resync */ 240 241 TWRITE_INT(ptr, data->md5.length, 0); 242 TWRITE_STR(ptr, (unsigned char *) data->md5.contents, data->md5.length); 243 TWRITE_INT(ptr, data->ctx->gss_flags, 0); 244 245 /* done with this, free it */ 246 xfree(data->md5.contents); 247 248 if (credmsg.data) { 249 TWRITE_INT16(ptr, KRB5_GSS_FOR_CREDS_OPTION, 0); 250 TWRITE_INT16(ptr, credmsg.length, 0); 251 TWRITE_STR(ptr, (unsigned char *) credmsg.data, credmsg.length); 252 253 /* free credmsg data */ 254 krb5_free_data_contents(context, &credmsg); 255 } 256 if (junk) 257 memset(ptr, 'i', junk); 258 *out = &data->checksum_data; 259 return 0; 260 } 261 262 static krb5_error_code 263 make_ap_req_v1(context, ctx, cred, k_cred, chan_bindings, mech_type, token) 264 krb5_context context; 265 krb5_gss_ctx_id_rec *ctx; 266 krb5_gss_cred_id_t cred; 267 krb5_creds *k_cred; 268 gss_channel_bindings_t chan_bindings; 269 gss_OID mech_type; 270 gss_buffer_t token; 271 { 272 krb5_flags mk_req_flags = 0; 273 krb5_error_code code; 274 struct gss_checksum_data cksum_struct; 275 krb5_checksum md5; 276 krb5_data ap_req; 277 krb5_data *checksum_data = NULL; 278 unsigned char *ptr; 279 unsigned char *t; 280 unsigned int tlen; 281 282 k5_mutex_assert_locked(&cred->lock); 283 ap_req.data = 0; 284 285 /* compute the hash of the channel bindings */ 286 287 if ((code = kg_checksum_channel_bindings(context, chan_bindings, &md5, 0))) 288 return(code); 289 290 krb5_auth_con_set_req_cksumtype(context, ctx->auth_context, 291 CKSUMTYPE_KG_CB); 292 cksum_struct.md5 = md5; 293 cksum_struct.ctx = ctx; 294 cksum_struct.cred = cred; 295 cksum_struct.checksum_data.data = NULL; 296 switch (k_cred->keyblock.enctype) { 297 case ENCTYPE_DES_CBC_CRC: 298 case ENCTYPE_DES_CBC_MD4: 299 case ENCTYPE_DES_CBC_MD5: 300 case ENCTYPE_DES3_CBC_SHA1: 301 code = make_gss_checksum(context, ctx->auth_context, &cksum_struct, 302 &checksum_data); 303 if (code) 304 goto cleanup; 305 break; 306 default: 307 krb5_auth_con_set_checksum_func(context, ctx->auth_context, 308 make_gss_checksum, &cksum_struct); 309 break; 310 } 311 312 313 /* call mk_req. subkey and ap_req need to be used or destroyed */ 314 315 mk_req_flags = AP_OPTS_USE_SUBKEY; 316 317 if (ctx->gss_flags & GSS_C_MUTUAL_FLAG) 318 mk_req_flags |= AP_OPTS_MUTUAL_REQUIRED; 319 320 code = krb5_mk_req_extended(context, &ctx->auth_context, mk_req_flags, 321 checksum_data, k_cred, &ap_req); 322 krb5_free_data_contents(context, &cksum_struct.checksum_data); 323 if (code) 324 goto cleanup; 325 326 /* store the interesting stuff from creds and authent */ 327 ctx->endtime = k_cred->times.endtime; 328 ctx->krb_flags = k_cred->ticket_flags; 329 330 /* build up the token */ 331 332 /* allocate space for the token */ 333 tlen = g_token_size((gss_OID) mech_type, ap_req.length); 334 335 if ((t = (unsigned char *) xmalloc(tlen)) == NULL) { 336 code = ENOMEM; 337 goto cleanup; 338 } 339 340 /* fill in the buffer */ 341 342 ptr = t; 343 344 g_make_token_header(mech_type, ap_req.length, 345 &ptr, KG_TOK_CTX_AP_REQ); 346 347 TWRITE_STR(ptr, (unsigned char *) ap_req.data, ap_req.length); 348 349 /* pass it back */ 350 351 token->length = tlen; 352 token->value = (void *) t; 353 354 code = 0; 355 356 cleanup: 357 if (checksum_data && checksum_data->data) 358 krb5_free_data_contents(context, checksum_data); 359 if (ap_req.data) 360 krb5_free_data_contents(context, &ap_req); 361 362 return (code); 363 } 364 365 /* 366 * setup_enc 367 * 368 * Fill in the encryption descriptors. Called after AP-REQ is made. 369 */ 370 static OM_uint32 371 setup_enc( 372 OM_uint32 *minor_status, 373 krb5_gss_ctx_id_rec *ctx, 374 krb5_context context) 375 { 376 krb5_error_code code; 377 int i; 378 krb5int_access kaccess; 379 380 code = krb5int_accessor (&kaccess, KRB5INT_ACCESS_VERSION); 381 if (code) 382 goto fail; 383 384 ctx->have_acceptor_subkey = 0; 385 ctx->proto = 0; 386 ctx->cksumtype = 0; 387 switch(ctx->subkey->enctype) { 388 case ENCTYPE_DES_CBC_MD5: 389 case ENCTYPE_DES_CBC_MD4: 390 case ENCTYPE_DES_CBC_CRC: 391 ctx->subkey->enctype = ENCTYPE_DES_CBC_RAW; 392 ctx->signalg = SGN_ALG_DES_MAC_MD5; 393 ctx->cksum_size = 8; 394 ctx->sealalg = SEAL_ALG_DES; 395 396 /* The encryption key is the session key XOR 397 0xf0f0f0f0f0f0f0f0. */ 398 if ((code = krb5_copy_keyblock(context, ctx->subkey, &ctx->enc))) 399 goto fail; 400 401 for (i=0; i<ctx->enc->length; i++) 402 ctx->enc->contents[i] ^= 0xf0; 403 404 goto copy_subkey_to_seq; 405 406 case ENCTYPE_DES3_CBC_SHA1: 407 /* MIT extension */ 408 ctx->subkey->enctype = ENCTYPE_DES3_CBC_RAW; 409 ctx->signalg = SGN_ALG_HMAC_SHA1_DES3_KD; 410 ctx->cksum_size = 20; 411 ctx->sealalg = SEAL_ALG_DES3KD; 412 413 copy_subkey: 414 code = krb5_copy_keyblock (context, ctx->subkey, &ctx->enc); 415 if (code) 416 goto fail; 417 copy_subkey_to_seq: 418 code = krb5_copy_keyblock (context, ctx->subkey, &ctx->seq); 419 if (code) { 420 krb5_free_keyblock (context, ctx->enc); 421 goto fail; 422 } 423 goto success; 424 425 case ENCTYPE_ARCFOUR_HMAC: 426 /* Microsoft extension */ 427 ctx->signalg = SGN_ALG_HMAC_MD5 ; 428 ctx->cksum_size = 8; 429 ctx->sealalg = SEAL_ALG_MICROSOFT_RC4 ; 430 431 goto copy_subkey; 432 433 default: 434 /* Fill some fields we shouldn't be using on this path 435 with garbage. */ 436 ctx->signalg = -10; 437 ctx->sealalg = -10; 438 439 ctx->proto = 1; 440 code = (*kaccess.krb5int_c_mandatory_cksumtype)(context, ctx->subkey->enctype, 441 &ctx->cksumtype); 442 if (code) 443 goto fail; 444 code = krb5_c_checksum_length(context, ctx->cksumtype, 445 &ctx->cksum_size); 446 if (code) 447 goto fail; 448 goto copy_subkey; 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 (GSS_S_COMPLETE); 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_t) *context_handle; 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 #ifndef _WIN32 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 #ifndef _WIN32 1012 int is_kdc; 1013 #endif 1014 1015 err = gssint_initialize_library(); 1016 if (err) 1017 return err; 1018 #ifndef _WIN32 1019 err = k5_mutex_lock(&kg_kdc_flag_mutex); 1020 if (err) 1021 return err; 1022 is_kdc = kdc_flag; 1023 k5_mutex_unlock(&kg_kdc_flag_mutex); 1024 1025 if (is_kdc) 1026 return krb5int_init_context_kdc(ctxp); 1027 #endif 1028 1029 return krb5_init_context(ctxp); 1030 } 1031 1032 #ifndef _WIN32 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 /* 1072 * Solaris Kerberos: 1073 * Don't bother to compare the realms as princ1 will always have a 1074 * referral realm set. 1075 */ 1076 1077 /* 1078 * Solaris Kerberos 1079 * If princ1 is elem1/metachar@REALM, compare just elem1 (and REALM). 1080 */ 1081 if (nelem == 2) { 1082 const krb5_data *p = krb5_princ_component(context, princ1, 1); 1083 1084 if (p->length == 1) { 1085 const char *s = p->data; 1086 1087 if (s[0] == '*') { 1088 const krb5_data *p1 = krb5_princ_component(context, princ1, 0); 1089 const krb5_data *p2 = krb5_princ_component(context, princ2, 0); 1090 1091 if (p1->length != p2->length || 1092 memcmp(p1->data, p2->data, p1->length)) 1093 return FALSE; 1094 1095 return TRUE; 1096 } 1097 } 1098 } 1099 1100 return FALSE; 1101 } 1102 1103 /* 1104 * Solaris Kerberos 1105 * This is a dup of krb5_ktfile_get_entry (sigh) but is necessary to 1106 * to get a custom princ compare above (principal_ignore_inst_compare) 1107 * and thus avoid mucking w/important krb5 internal 1108 * api (krb5_principal_compare) 1109 */ 1110 #include "../krb5/keytab/file/ktfile.h" 1111 1112 static krb5_error_code KRB5_CALLCONV 1113 ktfile_get_entry(context, id, principal, kvno, enctype, entry) 1114 krb5_context context; 1115 krb5_keytab id; 1116 krb5_const_principal principal; 1117 krb5_kvno kvno; 1118 krb5_enctype enctype; 1119 krb5_keytab_entry * entry; 1120 { 1121 krb5_keytab_entry cur_entry, new_entry; 1122 krb5_error_code kerror = 0; 1123 int found_wrong_kvno = 0; 1124 krb5_boolean similar; 1125 int kvno_offset = 0; 1126 1127 KRB5_LOG0(KRB5_INFO, "ktfile_get_entry() start\n"); 1128 1129 /* Open the keyfile for reading */ 1130 if ((kerror = krb5_ktfileint_openr(context, id))){ 1131 KRB5_LOG(KRB5_ERR, "ktfile_get_entry() end, ktfileint_openr() " 1132 "kerror= %d\n", kerror); 1133 return(kerror); 1134 } 1135 1136 /* 1137 * For efficiency and simplicity, we'll use a while true that 1138 * is exited with a break statement. 1139 */ 1140 cur_entry.principal = 0; 1141 cur_entry.vno = 0; 1142 cur_entry.key.contents = 0; 1143 /*CONSTCOND*/ 1144 while (TRUE) { 1145 if ((kerror = krb5_ktfileint_read_entry(context, id, &new_entry))) 1146 break; 1147 1148 /* 1149 * by the time this loop exits, it must either free cur_entry, 1150 * and copy new_entry there, or free new_entry. Otherwise, it 1151 * leaks. 1152 */ 1153 1154 /* 1155 * if the principal isn't the one requested, free new_entry 1156 * and continue to the next. 1157 */ 1158 1159 if (!principal_ignore_inst_compare(context, principal, 1160 new_entry.principal)) { 1161 krb5_kt_free_entry(context, &new_entry); 1162 continue; 1163 } 1164 1165 /* 1166 * if the enctype is not ignored and doesn't match, free new_entry 1167 * and continue to the next 1168 */ 1169 1170 if (enctype != IGNORE_ENCTYPE) { 1171 if ((kerror = krb5_c_enctype_compare(context, enctype, 1172 new_entry.key.enctype, 1173 &similar))) { 1174 krb5_kt_free_entry(context, &new_entry); 1175 break; 1176 } 1177 1178 if (!similar) { 1179 krb5_kt_free_entry(context, &new_entry); 1180 continue; 1181 } 1182 /* 1183 * Coerce the enctype of the output keyblock in case we 1184 * got an inexact match on the enctype. 1185 */ 1186 new_entry.key.enctype = enctype; 1187 } 1188 1189 if (kvno == IGNORE_VNO) { 1190 /* 1191 * if this is the first match, or if the new vno is 1192 * bigger, free the current and keep the new. Otherwise, 1193 * free the new. 1194 */ 1195 /* 1196 * A 1.2.x keytab contains only the low 8 bits of the key 1197 * version number. Since it can be much bigger, and thus 1198 * the 8-bit value can wrap, we need some heuristics to 1199 * figure out the "highest" numbered key if some numbers 1200 * close to 255 and some near 0 are used. 1201 * 1202 * The heuristic here: 1203 1204 * If we have any keys with versions over 240, then assume 1205 * that all version numbers 0-127 refer to 256+N instead. 1206 * Not perfect, but maybe good enough? 1207 */ 1208 1209 #define M(VNO) (((VNO) - kvno_offset + 256) % 256) 1210 1211 if (new_entry.vno > 240) 1212 kvno_offset = 128; 1213 if (! cur_entry.principal || 1214 M(new_entry.vno) > M(cur_entry.vno)) { 1215 krb5_kt_free_entry(context, &cur_entry); 1216 cur_entry = new_entry; 1217 } else { 1218 krb5_kt_free_entry(context, &new_entry); 1219 } 1220 } else { 1221 /* 1222 * if this kvno matches, free the current (will there ever 1223 * be one?), keep the new, and break out. Otherwise, remember 1224 * that we were here so we can return the right error, and 1225 * free the new 1226 */ 1227 /* 1228 * Yuck. The krb5-1.2.x keytab format only stores one byte 1229 * for the kvno, so we're toast if the kvno requested is 1230 * higher than that. Short-term workaround: only compare 1231 * the low 8 bits. 1232 */ 1233 1234 if (new_entry.vno == (kvno & 0xff)) { 1235 krb5_kt_free_entry(context, &cur_entry); 1236 cur_entry = new_entry; 1237 break; 1238 } else { 1239 found_wrong_kvno++; 1240 krb5_kt_free_entry(context, &new_entry); 1241 } 1242 } 1243 } 1244 1245 if (kerror == KRB5_KT_END) { 1246 if (cur_entry.principal) 1247 kerror = 0; 1248 else if (found_wrong_kvno) 1249 kerror = KRB5_KT_KVNONOTFOUND; 1250 else 1251 kerror = KRB5_KT_NOTFOUND; 1252 } 1253 if (kerror) { 1254 (void) krb5_ktfileint_close(context, id); 1255 krb5_kt_free_entry(context, &cur_entry); 1256 KRB5_LOG(KRB5_ERR,"ktfile_get_entry() end, kerror=" 1257 "%d\n", kerror); 1258 return kerror; 1259 } 1260 if ((kerror = krb5_ktfileint_close(context, id)) != 0) { 1261 krb5_kt_free_entry(context, &cur_entry); 1262 KRB5_LOG(KRB5_ERR,"ktfile_get_entry() end, ktfileint_close() " 1263 "kerror= %d\n", kerror); 1264 return kerror; 1265 } 1266 *entry = cur_entry; 1267 1268 /* Let us close the file before we leave */ 1269 (void) krb5_ktfileint_close(context, id); 1270 1271 KRB5_LOG0(KRB5_INFO, "ktfile_get_entry() end"); 1272 1273 return 0; 1274 } 1275 1276 1277 /* 1278 * Solaris Kerberos 1279 * Given a princ of name/instance@LOCALREALM, search the keytab 1280 * for a match of name and LOCALREALM and if found, return instance 1281 * as a string. 1282 * 1283 * Caller must free returned string. 1284 */ 1285 static krb5_error_code 1286 get_instance_keytab( 1287 krb5_context context, 1288 const char *sname, 1289 krb5_keytab keytab, 1290 char **instance) /* out */ 1291 { 1292 krb5_error_code ret=0; 1293 krb5_keytab_entry kt_ent; 1294 krb5_int32 nelem, free_kt_ent=0; 1295 register const krb5_data *p; 1296 char *realm=NULL, *s=NULL; 1297 krb5_principal client=NULL, princ=NULL; 1298 size_t realm_size = strlen(KRB5_REFERRAL_REALM) + 1; 1299 1300 if (!keytab) 1301 return EINVAL; 1302 1303 realm = malloc(realm_size); 1304 if (realm == NULL) 1305 return (ENOMEM); 1306 strlcpy(realm, KRB5_REFERRAL_REALM, realm_size); 1307 1308 ret = krb5_build_principal(context, &client, strlen(realm), 1309 realm, sname, "*", 1310 (char *)0); 1311 if (ret) 1312 goto out; 1313 1314 ret = ktfile_get_entry(context, keytab, client, 1315 0, /* don't have vno available */ 1316 0, &kt_ent); 1317 if (ret) 1318 goto out; 1319 1320 free_kt_ent++; /* kt_ent is not a ptr */ 1321 1322 princ = kt_ent.principal; 1323 nelem = krb5_princ_size(context, princ); 1324 if (nelem != 2) { 1325 ret = KRB5_PRINC_NOMATCH; 1326 goto out; 1327 } 1328 1329 p = krb5_princ_component(context, princ, 1); 1330 s = calloc(p->length + sizeof(char), sizeof(char)); 1331 if (!s) { 1332 ret = ENOMEM; 1333 goto out; 1334 } 1335 1336 (void) memcpy(s, p->data, p->length); 1337 1338 1339 out: 1340 free(realm); 1341 if (client) 1342 krb5_free_principal(context, client); 1343 if (free_kt_ent) 1344 (void) krb5_kt_free_entry(context, &kt_ent); 1345 1346 if (ret == 0) 1347 *instance = s; 1348 return ret; 1349 } 1350 1351 static OM_uint32 1352 load_root_cred_using_keytab( 1353 OM_uint32 *minor_status, 1354 krb5_context context, 1355 const char *sname, 1356 int use_nodename) 1357 { 1358 krb5_creds my_creds; 1359 krb5_principal me; 1360 krb5_principal server; 1361 krb5_error_code code; 1362 krb5_ccache ccache = NULL; 1363 krb5_keytab keytab = NULL; 1364 krb5_timestamp now; 1365 krb5_deltat lifetime = KRB5_DEFAULT_LIFE; /* -l option */ 1366 krb5_get_init_creds_opt opt; 1367 krb5_data tgtname = { 1368 0, 1369 KRB5_TGS_NAME_SIZE, 1370 KRB5_TGS_NAME 1371 }; 1372 char *svcname = NULL; 1373 1374 KRB5_LOG0(KRB5_INFO, "load_root_cred_using_keytab() start \n"); 1375 1376 if (!sname) 1377 return (GSS_S_FAILURE); 1378 1379 memset((char *)&my_creds, 0, sizeof(my_creds)); 1380 1381 if (code = krb5_kt_default(context, &keytab)) { 1382 *minor_status = code; 1383 return (GSS_S_FAILURE); 1384 } 1385 1386 if (!use_nodename) { 1387 char *instance = NULL; 1388 1389 code = get_instance_keytab(context, sname, keytab, &instance); 1390 if (code == 0) { 1391 code = krb5_sname_to_principal(context, 1392 instance, sname, 1393 KRB5_NT_UNKNOWN, &me); 1394 free(instance); 1395 } 1396 } else { 1397 code = krb5_sname_to_principal(context, NULL, sname, 1398 KRB5_NT_SRV_HST, &me); 1399 } 1400 1401 /* Solaris Kerberos */ 1402 if (code == 0 && krb5_is_referral_realm(&me->realm)) { 1403 krb5_data realm; 1404 code = krb5_kt_find_realm(context, keytab, me, &realm); 1405 if (code == 0) { 1406 krb5_free_data_contents(context, &me->realm); 1407 me->realm.length = realm.length; 1408 me->realm.data = realm.data; 1409 } else { 1410 /* Try to set a useful error message */ 1411 char *princ = NULL; 1412 krb5_unparse_name(context, me, &princ); 1413 1414 krb5_set_error_message(context, code, 1415 gettext("Failed to find realm for %s in keytab"), 1416 princ ? princ : "<unknown>"); 1417 if (princ) 1418 krb5_free_unparsed_name(context, princ); 1419 } 1420 } 1421 1422 if (code) { 1423 (void) krb5_kt_close(context, keytab); 1424 *minor_status = code; 1425 return (GSS_S_FAILURE); 1426 } 1427 1428 my_creds.client = me; 1429 1430 if((code = krb5_build_principal_ext(context, &server, 1431 krb5_princ_realm(context, me)->length, 1432 krb5_princ_realm(context, me)->data, 1433 tgtname.length, tgtname.data, 1434 krb5_princ_realm(context, me)->length, 1435 krb5_princ_realm(context, me)->data, 1436 0))) { 1437 *minor_status = code; 1438 krb5_free_cred_contents(context, &my_creds); 1439 (void) krb5_kt_close(context, keytab); 1440 1441 return (GSS_S_FAILURE); 1442 } 1443 1444 my_creds.server = server; 1445 my_creds.times.starttime = 0; /* start timer 1446 * when request 1447 * gets to KDC 1448 */ 1449 if ((code = krb5_timeofday(context, &now))) { 1450 *minor_status = code; 1451 krb5_free_cred_contents(context, &my_creds); 1452 (void) krb5_kt_close(context, keytab); 1453 1454 return (GSS_S_FAILURE); 1455 } 1456 my_creds.times.endtime = now + lifetime; 1457 my_creds.times.renew_till = 0; 1458 1459 memset(&opt, 0, sizeof (opt)); 1460 krb5_get_init_creds_opt_init(&opt); 1461 krb5_get_init_creds_opt_set_tkt_life(&opt, lifetime); 1462 1463 code = krb5_unparse_name(context, server, &svcname); 1464 if (code != 0) { 1465 *minor_status = code; 1466 krb5_free_cred_contents(context, &my_creds); 1467 (void) krb5_kt_close(context, keytab); 1468 1469 return (GSS_S_FAILURE); 1470 } 1471 /* 1472 * Evidently (sigh), on success, krb5_get_init_creds_keytab 1473 * changes the my_creds princ ptrs so we need to free those 1474 * princs (me&server) as well as freeing all of my_creds contents. 1475 */ 1476 code = krb5_get_init_creds_keytab(context, 1477 &my_creds, me, keytab, 1478 0, svcname, &opt); 1479 1480 (void) krb5_kt_close(context, keytab); 1481 1482 if (svcname != NULL) 1483 free(svcname); 1484 if (code) { 1485 *minor_status = code; 1486 krb5_free_cred_contents(context, &my_creds); 1487 1488 return (GSS_S_FAILURE); 1489 } 1490 1491 krb5_free_principal(context, server); 1492 server = NULL; 1493 1494 code = krb5_cc_resolve (context, 1495 krb5_cc_default_name(context), 1496 &ccache); 1497 if (code != 0) { 1498 *minor_status = code; 1499 krb5_free_cred_contents(context, &my_creds); 1500 krb5_free_principal(context, me); 1501 1502 return (GSS_S_FAILURE); 1503 } 1504 code = krb5_cc_initialize (context, ccache, me); 1505 krb5_free_principal(context, me); 1506 me = NULL; 1507 if (code != 0) { 1508 *minor_status = code; 1509 krb5_free_cred_contents(context, &my_creds); 1510 (void) krb5_cc_close(context, ccache); 1511 1512 return (GSS_S_FAILURE); 1513 } 1514 1515 code = krb5_cc_store_cred(context, ccache, 1516 &my_creds); 1517 krb5_free_cred_contents(context, &my_creds); 1518 (void) krb5_cc_close(context, ccache); 1519 1520 if (code) { 1521 *minor_status = code; 1522 1523 KRB5_LOG(KRB5_ERR, "load_root_cred_using_keytab() end, error " 1524 "code = %d\n", code); 1525 1526 return (GSS_S_FAILURE); 1527 } 1528 1529 KRB5_LOG0(KRB5_INFO, "load_root_cred_using_keytab() end \n"); 1530 1531 return (GSS_S_COMPLETE); 1532 } 1533 1534 static OM_uint32 1535 renew_ccache(OM_uint32 *minor_status, krb5_context context, uid_t uid) 1536 { 1537 krb5_principal me; 1538 krb5_principal server; 1539 krb5_creds creds; 1540 krb5_creds tmpcreds; 1541 krb5_creds *out_creds; 1542 krb5_error_code code; 1543 krb5_ccache ccache = NULL; 1544 static char ccache_name_buf[CACHE_FILENAME_LEN]; 1545 int options = 0; 1546 krb5_data tgtname = { 1547 0, 1548 KRB5_TGS_NAME_SIZE, 1549 KRB5_TGS_NAME 1550 }; 1551 gid_t gid = getgid(); 1552 1553 memset((char *)&creds, 0, sizeof(creds)); 1554 memset((char *)&tmpcreds, 0, sizeof(creds)); 1555 1556 if ((code = krb5_cc_default(context, &ccache))) { 1557 *minor_status = code; 1558 (void) krb5_cc_close(context, ccache); 1559 return (GSS_S_FAILURE); 1560 } 1561 1562 if ((code = krb5_cc_get_principal(context, ccache, &me)) != 0) { 1563 *minor_status = code; 1564 (void) krb5_cc_close(context, ccache); 1565 return (GSS_S_FAILURE); 1566 } 1567 1568 creds.client = me; 1569 1570 if((code = krb5_build_principal_ext(context, &server, 1571 krb5_princ_realm(context, me)->length, 1572 krb5_princ_realm(context, me)->data, 1573 tgtname.length, tgtname.data, 1574 krb5_princ_realm(context, me)->length, 1575 krb5_princ_realm(context, me)->data, 1576 0))) { 1577 krb5_free_principal(context, me); 1578 (void) krb5_cc_close(context, ccache); 1579 *minor_status = code; 1580 return (GSS_S_FAILURE); 1581 } 1582 1583 creds.server = server; 1584 creds.ticket_flags = TKT_FLG_RENEWABLE; 1585 1586 if ((krb5_cc_retrieve_cred(context, ccache, KRB5_TC_MATCH_FLAGS, 1587 &creds, &tmpcreds))) { 1588 (void) krb5_cc_close(context, ccache); 1589 return (KDC_ERR_BADOPTION); 1590 } 1591 1592 creds.ticket_flags = 0; 1593 code = krb5_get_credentials_renew(context, options, ccache, 1594 &creds, &out_creds); 1595 krb5_free_cred_contents(context, &creds); 1596 krb5_free_cred_contents(context, &tmpcreds); 1597 1598 if (code) { 1599 *minor_status = code; 1600 return (GSS_S_FAILURE); 1601 } 1602 1603 krb5_free_creds(context, out_creds); 1604 snprintf(ccache_name_buf, CACHE_FILENAME_LEN, "/tmp/krb5cc_%d", 1605 uid, -1); 1606 code = safechown(ccache_name_buf, uid, gid, -1); 1607 1608 if (code == -1) { 1609 (void) krb5_cc_destroy(context, ccache); 1610 *minor_status = code; 1611 return (GSS_S_FAILURE); 1612 } 1613 1614 (void) krb5_cc_close(context, ccache); 1615 1616 return (GSS_S_COMPLETE); 1617 1618 } 1619 1620 /* 1621 * Solaris Kerberos: 1622 * We enforce a minimum refresh time on the root cred. This avoids problems for 1623 * the higher level communication protocol for having valid creds and 1624 * setting up a valid context, only to have it expire before or while 1625 * it is being used. For non root users we don't care since we do not refresh 1626 * there creds, they get what they can get. 1627 */ 1628 #define MIN_REFRESH_TIME 300 1629 #define MIN_RENEW_TIME 1500 1630 1631 /* get_default_cred() must be called with the krb5_mutex lock held */ 1632 static OM_uint32 1633 get_default_cred(OM_uint32 *minor_status, void *ct, gss_cred_id_t *cred_handle) 1634 { 1635 krb5_timestamp now; 1636 krb5_gss_cred_id_t cred; 1637 OM_uint32 major; 1638 OM_uint32 mntmp; 1639 /* 1640 * Solaris Kerberos 1641 * Use krb5_getuid() to select the mechanism to obtain the uid. 1642 */ 1643 uid_t uid = krb5_getuid(); 1644 krb5_context context = (krb5_context)ct; 1645 1646 KRB5_LOG0(KRB5_INFO, "get_default_cred() start\n"); 1647 1648 /* Get the default cred for user */ 1649 if (((major = kg_get_defcred(minor_status, cred_handle)) != NULL) && 1650 GSS_ERROR(major)) { 1651 1652 /* If we're not root we're done */ 1653 if (uid != ROOT_UID) 1654 return (major); 1655 1656 /* 1657 * Try and get root's cred in the cache using keytab. 1658 * 1659 * First try "root" and then try "host" - this allows 1660 * Secure NFS to use the host principal for mounting if 1661 * there is no root principal. 1662 * 1663 * Then try "host/<anything>" to match any instance (needed 1664 * for DHCP clients). 1665 */ 1666 major = load_root_cred_using_keytab(minor_status, 1667 context, "root", 1); 1668 1669 if (major != GSS_S_COMPLETE) 1670 major = load_root_cred_using_keytab(minor_status, 1671 context, "host", 1); 1672 if (major != GSS_S_COMPLETE) 1673 major = load_root_cred_using_keytab(minor_status, 1674 context, "host", 0); 1675 1676 if (major != GSS_S_COMPLETE) 1677 return (major); 1678 1679 /* We should have valid tgt now in the cache, so get it. */ 1680 major = kg_get_defcred(minor_status, cred_handle); 1681 1682 return (major); 1683 } 1684 1685 /* We've got a gss cred handle that is a kerberos cred handle. */ 1686 cred = (krb5_gss_cred_id_t)*cred_handle; 1687 1688 /* If we can't get the time, assume the worst. */ 1689 if (krb5_timeofday(context, &now)) { 1690 (void) krb5_gss_release_cred(&mntmp, cred_handle); 1691 return (GSS_S_CREDENTIALS_EXPIRED); 1692 } 1693 1694 /* If root's cred has expired re-get it */ 1695 if (cred->tgt_expire < now + MIN_REFRESH_TIME && uid == ROOT_UID) { 1696 (void) krb5_gss_release_cred(&mntmp, cred_handle); 1697 1698 major = load_root_cred_using_keytab(minor_status, 1699 context, "root", 1); 1700 1701 if (major != GSS_S_COMPLETE) 1702 major = load_root_cred_using_keytab(minor_status, 1703 context, "host", 1); 1704 1705 if (major != GSS_S_COMPLETE) 1706 major = load_root_cred_using_keytab(minor_status, 1707 context, "host", 0); 1708 1709 if (major != GSS_S_COMPLETE) 1710 return (major); 1711 1712 major = kg_get_defcred(minor_status, cred_handle); 1713 if (major != GSS_S_COMPLETE) 1714 return (major); 1715 1716 /* Any body else is SOL unless we can renew their credential cache */ 1717 } else if ((cred->tgt_expire < now + MIN_RENEW_TIME) && 1718 (cred->tgt_expire > now)) { 1719 (void) krb5_gss_release_cred(&mntmp, cred_handle); 1720 1721 major = renew_ccache(minor_status, context, uid); 1722 if ((major != GSS_S_COMPLETE) && 1723 (major != KDC_ERR_BADOPTION)) 1724 return (major); 1725 1726 major = kg_get_defcred(minor_status, cred_handle); 1727 if (major != GSS_S_COMPLETE) 1728 return (major); 1729 1730 } 1731 1732 /* Otherwise we got non expired creds */ 1733 1734 KRB5_LOG0(KRB5_INFO, "get_default_cred() end\n"); 1735 1736 return (GSS_S_COMPLETE); 1737 } 1738 1739 /* Solaris Kerberos specific routines end */ 1740