1 /* 2 * Copyright 2005 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 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 /* 34 * Copyright 1993 by OpenVision Technologies, Inc. 35 * 36 * Permission to use, copy, modify, distribute, and sell this software 37 * and its documentation for any purpose is hereby granted without fee, 38 * provided that the above copyright notice appears in all copies and 39 * that both that copyright notice and this permission notice appear in 40 * supporting documentation, and that the name of OpenVision not be used 41 * in advertising or publicity pertaining to distribution of the software 42 * without specific, written prior permission. OpenVision makes no 43 * representations about the suitability of this software for any 44 * purpose. It is provided "as is" without express or implied warranty. 45 * 46 * OPENVISION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, 47 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO 48 * EVENT SHALL OPENVISION BE LIABLE FOR ANY SPECIAL, INDIRECT OR 49 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF 50 * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR 51 * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 52 * PERFORMANCE OF THIS SOFTWARE. 53 */ 54 55 /* 56 * Copyright (C) 1998 by the FundsXpress, INC. 57 * 58 * All rights reserved. 59 * 60 * Export of this software from the United States of America may require 61 * a specific license from the United States Government. It is the 62 * responsibility of any person or organization contemplating export to 63 * obtain such a license before exporting. 64 * 65 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and 66 * distribute this software and its documentation for any purpose and 67 * without fee is hereby granted, provided that the above copyright 68 * notice appear in all copies and that both that copyright notice and 69 * this permission notice appear in supporting documentation, and that 70 * the name of FundsXpress. not be used in advertising or publicity pertaining 71 * to distribution of the software without specific, written prior 72 * permission. FundsXpress makes no representations about the suitability of 73 * this software for any purpose. It is provided "as is" without express 74 * or implied warranty. 75 * 76 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR 77 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 78 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. 79 */ 80 81 #include <gssapiP_krb5.h> 82 #include <k5-int.h> 83 84 #ifdef HAVE_STRING_H 85 #include <string.h> 86 #else 87 #include <strings.h> 88 #endif 89 90 /* 91 * $Id: acquire_cred.c,v 1.25.6.2 2000/05/22 20:41:32 meeroh Exp $ 92 */ 93 94 /* get credentials corresponding to a key in the krb5 keytab. 95 If the default name is requested, return the name in output_princ. 96 If output_princ is non-NULL, the caller will use or free it, regardless 97 of the return value. 98 If successful, set the keytab-specific fields in cred 99 */ 100 101 static OM_uint32 102 acquire_accept_cred(context, minor_status, desired_name, output_princ, cred) 103 krb5_context context; 104 OM_uint32 *minor_status; 105 gss_name_t desired_name; 106 krb5_principal *output_princ; 107 krb5_gss_cred_id_rec *cred; 108 { 109 krb5_error_code code; 110 krb5_principal princ; 111 krb5_keytab kt; 112 krb5_keytab_entry entry; 113 114 *output_princ = NULL; 115 cred->keytab = NULL; 116 117 /* open the default keytab */ 118 119 if ((code = krb5_kt_default(context, &kt))) { 120 *minor_status = code; 121 /* NOTE: GSS_S_CRED_UNAVAIL is not RFC 2743 compliant */ 122 return(GSS_S_NO_CRED); 123 } 124 125 /* figure out what principal to use. If the default name is 126 requested, use the default sn2princ output */ 127 128 if (desired_name == (gss_name_t) NULL) { 129 if ((code = krb5_sname_to_principal(context, NULL, NULL, KRB5_NT_SRV_HST, 130 &princ))) { 131 (void) krb5_kt_close(context, kt); 132 *minor_status = code; 133 return(GSS_S_FAILURE); 134 } 135 *output_princ = princ; 136 } else { 137 princ = (krb5_principal) desired_name; 138 } 139 140 code = krb5_kt_get_entry(context, kt, princ, 0, 0, &entry); 141 if (code) { 142 (void) krb5_kt_close(context, kt); 143 if (code == KRB5_KT_NOTFOUND) 144 *minor_status = KG_KEYTAB_NOMATCH; 145 else 146 *minor_status = code; 147 148 if (*output_princ != NULL) { 149 krb5_free_principal(context, *output_princ); 150 *output_princ = NULL; 151 } 152 153 return(GSS_S_FAILURE); 154 } 155 156 krb5_kt_free_entry(context, &entry); 157 158 /* hooray. we made it */ 159 160 cred->keytab = kt; 161 162 /* Open the replay cache for this principal. */ 163 if ((code = krb5_get_server_rcache(context, 164 krb5_princ_component(context, princ, 0), 165 &cred->rcache))) { 166 *minor_status = code; 167 return(GSS_S_FAILURE); 168 } 169 170 return(GSS_S_COMPLETE); 171 } 172 173 /* get credentials corresponding to the default credential cache. 174 If the default name is requested, return the name in output_princ. 175 If output_princ is non-NULL, the caller will use or free it, regardless 176 of the return value. 177 If successful, set the ccache-specific fields in cred. 178 */ 179 180 static OM_uint32 181 acquire_init_cred(context, minor_status, desired_name, output_princ, cred) 182 krb5_context context; 183 OM_uint32 *minor_status; 184 gss_name_t desired_name; 185 krb5_principal *output_princ; 186 krb5_gss_cred_id_rec *cred; 187 { 188 krb5_error_code code; 189 krb5_ccache ccache; 190 krb5_principal princ, tmp_princ; 191 krb5_flags flags; 192 krb5_cc_cursor cur; 193 krb5_creds creds; 194 int got_endtime; 195 196 cred->ccache = NULL; 197 198 /* SUNW14resync - do we need this? */ 199 #if 0 200 /* load the GSS ccache name into the kg_context */ 201 if (GSS_ERROR(kg_sync_ccache_name(context, minor_status))) 202 return(GSS_S_FAILURE); 203 #endif 204 205 /* open the default credential cache */ 206 207 code = krb5int_cc_default(context, &ccache); 208 if (code) { 209 *minor_status = code; 210 return(GSS_S_NO_CRED); 211 } 212 213 /* turn off OPENCLOSE mode while extensive frobbing is going on */ 214 /* 215 * SUNW14resync 216 * Added calls to krb5_cc_set_flags(... KRB5_TC_OPENCLOSE) 217 * on the error returns cuz the 1.4 krb5_cc_close does not always close 218 * the file like it used to and caused STC test gss.27 to fail. 219 */ 220 flags = 0; /* turns off OPENCLOSE mode */ 221 if ((code = krb5_cc_set_flags(context, ccache, flags)) != 0) { 222 (void)krb5_cc_close(context, ccache); 223 *minor_status = code; 224 return(GSS_S_NO_CRED); 225 } 226 227 /* get out the principal name and see if it matches */ 228 229 if ((code = krb5_cc_get_principal(context, ccache, &princ)) != 0) { 230 (void)krb5_cc_set_flags(context, ccache, KRB5_TC_OPENCLOSE); 231 (void)krb5_cc_close(context, ccache); 232 *minor_status = code; 233 return(GSS_S_FAILURE); 234 } 235 236 if (desired_name != (gss_name_t) NULL) { 237 if (! krb5_principal_compare(context, princ, (krb5_principal) desired_name)) { 238 (void)krb5_free_principal(context, princ); 239 (void)krb5_cc_set_flags(context, ccache, KRB5_TC_OPENCLOSE); 240 (void)krb5_cc_close(context, ccache); 241 *minor_status = KG_CCACHE_NOMATCH; 242 return(GSS_S_NO_CRED); 243 } 244 (void)krb5_free_principal(context, princ); 245 princ = (krb5_principal) desired_name; 246 } else { 247 *output_princ = princ; 248 } 249 250 /* iterate over the ccache, find the tgt */ 251 252 if ((code = krb5_cc_start_seq_get(context, ccache, &cur)) != 0) { 253 (void)krb5_cc_set_flags(context, ccache, KRB5_TC_OPENCLOSE); 254 (void)krb5_cc_close(context, ccache); 255 *minor_status = code; 256 return(GSS_S_FAILURE); 257 } 258 259 /* this is hairy. If there's a tgt for the principal's local realm 260 in here, that's what we want for the expire time. But if 261 there's not, then we want to use the first key. */ 262 263 got_endtime = 0; 264 265 code = krb5_build_principal_ext(context, &tmp_princ, 266 krb5_princ_realm(context, princ)->length, 267 krb5_princ_realm(context, princ)->data, 268 6, "krbtgt", 269 krb5_princ_realm(context, princ)->length, 270 krb5_princ_realm(context, princ)->data, 271 0); 272 if (code) { 273 (void)krb5_cc_set_flags(context, ccache, KRB5_TC_OPENCLOSE); 274 (void)krb5_cc_close(context, ccache); 275 *minor_status = code; 276 return(GSS_S_FAILURE); 277 } 278 while ((code = krb5_cc_next_cred(context, ccache, &cur, &creds)) == 0) { 279 if (krb5_principal_compare(context, tmp_princ, creds.server)) { 280 cred->tgt_expire = creds.times.endtime; 281 got_endtime = 1; 282 *minor_status = 0; 283 code = 0; 284 krb5_free_cred_contents(context, &creds); 285 break; 286 } 287 if (got_endtime == 0) { 288 cred->tgt_expire = creds.times.endtime; 289 got_endtime = 1; 290 } 291 krb5_free_cred_contents(context, &creds); 292 } 293 krb5_free_principal(context, tmp_princ); 294 295 if (code && code != KRB5_CC_END) { 296 /* this means some error occurred reading the ccache */ 297 (void)krb5_cc_end_seq_get(context, ccache, &cur); 298 (void)krb5_cc_set_flags(context, ccache, KRB5_TC_OPENCLOSE); 299 (void)krb5_cc_close(context, ccache); 300 *minor_status = code; 301 return(GSS_S_FAILURE); 302 } else if (! got_endtime) { 303 /* this means the ccache was entirely empty */ 304 (void)krb5_cc_end_seq_get(context, ccache, &cur); 305 (void)krb5_cc_set_flags(context, ccache, KRB5_TC_OPENCLOSE); 306 (void)krb5_cc_close(context, ccache); 307 *minor_status = KG_EMPTY_CCACHE; 308 return(GSS_S_FAILURE); 309 } else { 310 /* this means that we found an endtime to use. */ 311 if ((code = krb5_cc_end_seq_get(context, ccache, &cur)) != 0) { 312 (void)krb5_cc_set_flags(context, ccache, KRB5_TC_OPENCLOSE); 313 (void)krb5_cc_close(context, ccache); 314 *minor_status = code; 315 return(GSS_S_FAILURE); 316 } 317 flags = KRB5_TC_OPENCLOSE; /* turns on OPENCLOSE mode */ 318 if ((code = krb5_cc_set_flags(context, ccache, flags)) != 0) { 319 (void)krb5_cc_close(context, ccache); 320 *minor_status = code; 321 return(GSS_S_FAILURE); 322 } 323 } 324 325 /* the credentials match and are valid */ 326 327 cred->ccache = ccache; 328 /* minor_status is set while we are iterating over the ccache */ 329 return(GSS_S_COMPLETE); 330 } 331 332 OM_uint32 333 krb5_gss_acquire_cred(ctx, minor_status, desired_name, time_req, 334 desired_mechs, cred_usage, output_cred_handle, 335 actual_mechs, time_rec) 336 void *ctx; 337 OM_uint32 *minor_status; 338 gss_name_t desired_name; 339 OM_uint32 time_req; 340 gss_OID_set desired_mechs; 341 gss_cred_usage_t cred_usage; 342 gss_cred_id_t *output_cred_handle; 343 gss_OID_set *actual_mechs; 344 OM_uint32 *time_rec; 345 { 346 OM_uint32 ret; 347 348 mutex_lock(&krb5_mutex); 349 ret = krb5_gss_acquire_cred_no_lock(ctx, minor_status, desired_name, 350 time_req, desired_mechs, cred_usage, output_cred_handle, 351 actual_mechs, time_rec); 352 mutex_unlock(&krb5_mutex); 353 return(ret); 354 } 355 356 /*ARGSUSED*/ 357 OM_uint32 358 krb5_gss_acquire_cred_no_lock(ctx, minor_status, desired_name, time_req, 359 desired_mechs, cred_usage, output_cred_handle, 360 actual_mechs, time_rec) 361 void *ctx; 362 OM_uint32 *minor_status; 363 gss_name_t desired_name; 364 OM_uint32 time_req; 365 gss_OID_set desired_mechs; 366 gss_cred_usage_t cred_usage; 367 gss_cred_id_t *output_cred_handle; 368 gss_OID_set *actual_mechs; 369 OM_uint32 *time_rec; 370 { 371 krb5_context context; 372 size_t i; 373 krb5_gss_cred_id_t cred; 374 gss_OID_set ret_mechs = GSS_C_NULL_OID_SET; 375 const gss_OID_set_desc * valid_mechs; 376 int req_old, req_new; 377 OM_uint32 ret; 378 krb5_error_code code; 379 380 /* Solaris Kerberos: for MT safety, we avoid the use of a default 381 * context via kg_get_context() */ 382 #if 0 383 if (GSS_ERROR(kg_get_context(minor_status, &context))) 384 return(GSS_S_FAILURE); 385 #endif 386 387 context = ctx; 388 389 /* make sure all outputs are valid */ 390 391 *output_cred_handle = NULL; 392 if (actual_mechs) 393 *actual_mechs = NULL; 394 if (time_rec) 395 *time_rec = 0; 396 397 /* validate the name */ 398 399 /*SUPPRESS 29*/ 400 if ((desired_name != (gss_name_t) NULL) && 401 (! kg_validate_name(desired_name))) { 402 *minor_status = (OM_uint32) G_VALIDATE_FAILED; 403 return(GSS_S_CALL_BAD_STRUCTURE|GSS_S_BAD_NAME); 404 } 405 406 /* verify that the requested mechanism set is the default, or 407 contains krb5 */ 408 409 if (desired_mechs == GSS_C_NULL_OID_SET) { 410 valid_mechs = gss_mech_set_krb5_both; 411 req_old = 1; 412 req_new = 1; 413 } else { 414 req_old = 0; 415 req_new = 0; 416 417 for (i=0; i<desired_mechs->count; i++) { 418 if (g_OID_equal(gss_mech_krb5_old, &(desired_mechs->elements[i]))) 419 req_old++; 420 if (g_OID_equal(gss_mech_krb5, &(desired_mechs->elements[i]))) 421 req_new++; 422 } 423 424 if (!req_old && !req_new) { 425 *minor_status = 0; 426 return(GSS_S_BAD_MECH); 427 } 428 } 429 430 /* create the gss cred structure */ 431 432 if ((cred = 433 (krb5_gss_cred_id_t) xmalloc(sizeof(krb5_gss_cred_id_rec))) == NULL) { 434 *minor_status = ENOMEM; 435 return(GSS_S_FAILURE); 436 } 437 memset(cred, 0, sizeof(krb5_gss_cred_id_rec)); 438 439 cred->usage = cred_usage; 440 cred->princ = NULL; 441 cred->actual_mechs = valid_mechs; 442 cred->prerfc_mech = req_old; 443 cred->rfc_mech = req_new; 444 445 cred->keytab = NULL; 446 cred->ccache = NULL; 447 448 if ((cred_usage != GSS_C_INITIATE) && 449 (cred_usage != GSS_C_ACCEPT) && 450 (cred_usage != GSS_C_BOTH)) { 451 xfree(cred); 452 *minor_status = (OM_uint32) G_BAD_USAGE; 453 return(GSS_S_FAILURE); 454 } 455 456 /* if requested, acquire credentials for accepting */ 457 /* this will fill in cred->princ if the desired_name is not specified */ 458 459 if ((cred_usage == GSS_C_ACCEPT) || 460 (cred_usage == GSS_C_BOTH)) 461 if ((ret = acquire_accept_cred(context, minor_status, desired_name, 462 &(cred->princ), cred)) 463 != GSS_S_COMPLETE) { 464 if (cred->princ) 465 krb5_free_principal(context, cred->princ); 466 xfree(cred); 467 /* minor_status set by acquire_accept_cred() */ 468 return(ret); 469 } 470 471 /* if requested, acquire credentials for initiation */ 472 /* this will fill in cred->princ if it wasn't set above, and 473 the desired_name is not specified */ 474 475 if ((cred_usage == GSS_C_INITIATE) || 476 (cred_usage == GSS_C_BOTH)) 477 if ((ret = 478 acquire_init_cred(context, minor_status, 479 cred->princ?(gss_name_t)cred->princ:desired_name, 480 &(cred->princ), cred)) 481 != GSS_S_COMPLETE) { 482 if (cred->keytab) 483 (void) krb5_kt_close(context, cred->keytab); 484 if (cred->princ) 485 krb5_free_principal(context, cred->princ); 486 xfree(cred); 487 /* minor_status set by acquire_init_cred() */ 488 return(ret); 489 } 490 491 /* if the princ wasn't filled in already, fill it in now */ 492 493 if (!cred->princ) 494 if ((code = krb5_copy_principal(context, (krb5_principal) desired_name, 495 &(cred->princ)))) { 496 if (cred->ccache) 497 (void)krb5_cc_close(context, cred->ccache); 498 if (cred->keytab) 499 (void)krb5_kt_close(context, cred->keytab); 500 xfree(cred); 501 *minor_status = code; 502 return(GSS_S_FAILURE); 503 } 504 505 /*** at this point, the cred structure has been completely created */ 506 507 /* compute time_rec */ 508 509 if (cred_usage == GSS_C_ACCEPT) { 510 if (time_rec) 511 *time_rec = GSS_C_INDEFINITE; 512 } else { 513 krb5_timestamp now; 514 515 if ((code = krb5_timeofday(context, &now))) { 516 if (cred->ccache) 517 (void)krb5_cc_close(context, cred->ccache); 518 if (cred->keytab) 519 (void)krb5_kt_close(context, cred->keytab); 520 if (cred->princ) 521 krb5_free_principal(context, cred->princ); 522 xfree(cred); 523 *minor_status = code; 524 return(GSS_S_FAILURE); 525 } 526 527 if (time_rec) 528 *time_rec = (cred->tgt_expire > now) ? (cred->tgt_expire - now) : 0; 529 } 530 531 /* create mechs */ 532 533 if (actual_mechs) { 534 if (GSS_ERROR(ret = gss_create_empty_oid_set(minor_status, 535 &ret_mechs)) || 536 (cred->prerfc_mech && 537 GSS_ERROR(ret = gss_add_oid_set_member(minor_status, 538 (gss_OID) gss_mech_krb5_old, 539 &ret_mechs))) || 540 (cred->rfc_mech && 541 GSS_ERROR(ret = gss_add_oid_set_member(minor_status, 542 (gss_OID) gss_mech_krb5, 543 &ret_mechs)))) { 544 if (cred->ccache) 545 (void)krb5_cc_close(context, cred->ccache); 546 if (cred->keytab) 547 (void)krb5_kt_close(context, cred->keytab); 548 if (cred->princ) 549 krb5_free_principal(context, cred->princ); 550 xfree(cred); 551 /* (*minor_status) set above */ 552 return(ret); 553 } 554 } 555 556 /* intern the credential handle */ 557 558 if (! kg_save_cred_id((gss_cred_id_t) cred)) { 559 (void) gss_release_oid_set(NULL, &ret_mechs); 560 free(ret_mechs->elements); 561 free(ret_mechs); 562 if (cred->ccache) 563 (void)krb5_cc_close(context, cred->ccache); 564 if (cred->keytab) 565 (void)krb5_kt_close(context, cred->keytab); 566 if (cred->princ) 567 krb5_free_principal(context, cred->princ); 568 xfree(cred); 569 *minor_status = (OM_uint32) G_VALIDATE_FAILED; 570 return(GSS_S_FAILURE); 571 } 572 573 /* return success */ 574 575 *minor_status = 0; 576 *output_cred_handle = (gss_cred_id_t) cred; 577 if (actual_mechs) 578 *actual_mechs = ret_mechs; 579 return(GSS_S_COMPLETE); 580 } 581