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 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 if (desired_name != GSS_C_NO_NAME) { 126 princ = (krb5_principal) desired_name; 127 if ((code = krb5_kt_get_entry(context, kt, princ, 0, 0, &entry))) { 128 (void) krb5_kt_close(context, kt); 129 if (code == KRB5_KT_NOTFOUND) 130 *minor_status = KG_KEYTAB_NOMATCH; 131 else 132 *minor_status = code; 133 /* NOTE: GSS_S_CRED_UNAVAIL is not RFC 2743 compliant */ 134 return(GSS_S_NO_CRED); 135 } 136 krb5_kt_free_entry(context, &entry); 137 138 /* Open the replay cache for this principal. */ 139 if ((code = krb5_get_server_rcache(context, 140 krb5_princ_component(context, princ, 0), 141 &cred->rcache))) { 142 *minor_status = code; 143 return(GSS_S_FAILURE); 144 } 145 146 } 147 148 /* hooray. we made it */ 149 150 cred->keytab = kt; 151 152 return(GSS_S_COMPLETE); 153 } 154 155 /* get credentials corresponding to the default credential cache. 156 If the default name is requested, return the name in output_princ. 157 If output_princ is non-NULL, the caller will use or free it, regardless 158 of the return value. 159 If successful, set the ccache-specific fields in cred. 160 */ 161 162 static OM_uint32 163 acquire_init_cred(context, minor_status, desired_name, output_princ, cred) 164 krb5_context context; 165 OM_uint32 *minor_status; 166 gss_name_t desired_name; 167 krb5_principal *output_princ; 168 krb5_gss_cred_id_rec *cred; 169 { 170 krb5_error_code code; 171 krb5_ccache ccache; 172 krb5_principal princ, tmp_princ; 173 krb5_flags flags; 174 krb5_cc_cursor cur; 175 krb5_creds creds; 176 int got_endtime; 177 178 cred->ccache = NULL; 179 180 /* SUNW14resync - do we need this? */ 181 #if 0 182 /* load the GSS ccache name into the kg_context */ 183 if (GSS_ERROR(kg_sync_ccache_name(context, minor_status))) 184 return(GSS_S_FAILURE); 185 #endif 186 187 /* open the default credential cache */ 188 189 code = krb5int_cc_default(context, &ccache); 190 if (code) { 191 *minor_status = code; 192 return(GSS_S_NO_CRED); 193 } 194 195 /* turn off OPENCLOSE mode while extensive frobbing is going on */ 196 /* 197 * SUNW14resync 198 * Added calls to krb5_cc_set_flags(... KRB5_TC_OPENCLOSE) 199 * on the error returns cuz the 1.4 krb5_cc_close does not always close 200 * the file like it used to and caused STC test gss.27 to fail. 201 */ 202 flags = 0; /* turns off OPENCLOSE mode */ 203 if ((code = krb5_cc_set_flags(context, ccache, flags)) != 0) { 204 (void)krb5_cc_close(context, ccache); 205 *minor_status = code; 206 return(GSS_S_NO_CRED); 207 } 208 209 /* get out the principal name and see if it matches */ 210 211 if ((code = krb5_cc_get_principal(context, ccache, &princ)) != 0) { 212 (void)krb5_cc_set_flags(context, ccache, KRB5_TC_OPENCLOSE); 213 (void)krb5_cc_close(context, ccache); 214 *minor_status = code; 215 return(GSS_S_FAILURE); 216 } 217 218 if (desired_name != (gss_name_t) NULL) { 219 if (! krb5_principal_compare(context, princ, (krb5_principal) desired_name)) { 220 (void)krb5_free_principal(context, princ); 221 (void)krb5_cc_set_flags(context, ccache, KRB5_TC_OPENCLOSE); 222 (void)krb5_cc_close(context, ccache); 223 *minor_status = KG_CCACHE_NOMATCH; 224 return(GSS_S_NO_CRED); 225 } 226 (void)krb5_free_principal(context, princ); 227 princ = (krb5_principal) desired_name; 228 } else { 229 *output_princ = princ; 230 } 231 232 /* iterate over the ccache, find the tgt */ 233 234 if ((code = krb5_cc_start_seq_get(context, ccache, &cur)) != 0) { 235 (void)krb5_cc_set_flags(context, ccache, KRB5_TC_OPENCLOSE); 236 (void)krb5_cc_close(context, ccache); 237 *minor_status = code; 238 return(GSS_S_FAILURE); 239 } 240 241 /* this is hairy. If there's a tgt for the principal's local realm 242 in here, that's what we want for the expire time. But if 243 there's not, then we want to use the first key. */ 244 245 got_endtime = 0; 246 247 code = krb5_build_principal_ext(context, &tmp_princ, 248 krb5_princ_realm(context, princ)->length, 249 krb5_princ_realm(context, princ)->data, 250 6, "krbtgt", 251 krb5_princ_realm(context, princ)->length, 252 krb5_princ_realm(context, princ)->data, 253 0); 254 if (code) { 255 (void)krb5_cc_set_flags(context, ccache, KRB5_TC_OPENCLOSE); 256 (void)krb5_cc_close(context, ccache); 257 *minor_status = code; 258 return(GSS_S_FAILURE); 259 } 260 while ((code = krb5_cc_next_cred(context, ccache, &cur, &creds)) == 0) { 261 if (krb5_principal_compare(context, tmp_princ, creds.server)) { 262 cred->tgt_expire = creds.times.endtime; 263 got_endtime = 1; 264 *minor_status = 0; 265 code = 0; 266 krb5_free_cred_contents(context, &creds); 267 break; 268 } 269 if (got_endtime == 0) { 270 cred->tgt_expire = creds.times.endtime; 271 got_endtime = 1; 272 } 273 krb5_free_cred_contents(context, &creds); 274 } 275 krb5_free_principal(context, tmp_princ); 276 277 if (code && code != KRB5_CC_END) { 278 /* this means some error occurred reading the ccache */ 279 (void)krb5_cc_end_seq_get(context, ccache, &cur); 280 (void)krb5_cc_set_flags(context, ccache, KRB5_TC_OPENCLOSE); 281 (void)krb5_cc_close(context, ccache); 282 *minor_status = code; 283 return(GSS_S_FAILURE); 284 } else if (! got_endtime) { 285 /* this means the ccache was entirely empty */ 286 (void)krb5_cc_end_seq_get(context, ccache, &cur); 287 (void)krb5_cc_set_flags(context, ccache, KRB5_TC_OPENCLOSE); 288 (void)krb5_cc_close(context, ccache); 289 *minor_status = KG_EMPTY_CCACHE; 290 return(GSS_S_FAILURE); 291 } else { 292 /* this means that we found an endtime to use. */ 293 if ((code = krb5_cc_end_seq_get(context, ccache, &cur)) != 0) { 294 (void)krb5_cc_set_flags(context, ccache, KRB5_TC_OPENCLOSE); 295 (void)krb5_cc_close(context, ccache); 296 *minor_status = code; 297 return(GSS_S_FAILURE); 298 } 299 flags = KRB5_TC_OPENCLOSE; /* turns on OPENCLOSE mode */ 300 if ((code = krb5_cc_set_flags(context, ccache, flags)) != 0) { 301 (void)krb5_cc_close(context, ccache); 302 *minor_status = code; 303 return(GSS_S_FAILURE); 304 } 305 } 306 307 /* the credentials match and are valid */ 308 309 cred->ccache = ccache; 310 /* minor_status is set while we are iterating over the ccache */ 311 return(GSS_S_COMPLETE); 312 } 313 314 OM_uint32 315 krb5_gss_acquire_cred(ctx, minor_status, desired_name, time_req, 316 desired_mechs, cred_usage, output_cred_handle, 317 actual_mechs, time_rec) 318 void *ctx; 319 OM_uint32 *minor_status; 320 gss_name_t desired_name; 321 OM_uint32 time_req; 322 gss_OID_set desired_mechs; 323 gss_cred_usage_t cred_usage; 324 gss_cred_id_t *output_cred_handle; 325 gss_OID_set *actual_mechs; 326 OM_uint32 *time_rec; 327 { 328 OM_uint32 ret; 329 330 mutex_lock(&krb5_mutex); 331 ret = krb5_gss_acquire_cred_no_lock(ctx, minor_status, desired_name, 332 time_req, desired_mechs, cred_usage, output_cred_handle, 333 actual_mechs, time_rec); 334 mutex_unlock(&krb5_mutex); 335 return(ret); 336 } 337 338 /*ARGSUSED*/ 339 OM_uint32 340 krb5_gss_acquire_cred_no_lock(ctx, minor_status, desired_name, time_req, 341 desired_mechs, cred_usage, output_cred_handle, 342 actual_mechs, time_rec) 343 void *ctx; 344 OM_uint32 *minor_status; 345 gss_name_t desired_name; 346 OM_uint32 time_req; 347 gss_OID_set desired_mechs; 348 gss_cred_usage_t cred_usage; 349 gss_cred_id_t *output_cred_handle; 350 gss_OID_set *actual_mechs; 351 OM_uint32 *time_rec; 352 { 353 krb5_context context; 354 size_t i; 355 krb5_gss_cred_id_t cred; 356 gss_OID_set ret_mechs = GSS_C_NULL_OID_SET; 357 const gss_OID_set_desc * valid_mechs; 358 int req_old, req_new; 359 OM_uint32 ret; 360 krb5_error_code code; 361 362 /* Solaris Kerberos: for MT safety, we avoid the use of a default 363 * context via kg_get_context() */ 364 #if 0 365 if (GSS_ERROR(kg_get_context(minor_status, &context))) 366 return(GSS_S_FAILURE); 367 #endif 368 369 context = ctx; 370 371 /* make sure all outputs are valid */ 372 373 *output_cred_handle = NULL; 374 if (actual_mechs) 375 *actual_mechs = NULL; 376 if (time_rec) 377 *time_rec = 0; 378 379 /* validate the name */ 380 381 /*SUPPRESS 29*/ 382 if ((desired_name != (gss_name_t) NULL) && 383 (! kg_validate_name(desired_name))) { 384 *minor_status = (OM_uint32) G_VALIDATE_FAILED; 385 return(GSS_S_CALL_BAD_STRUCTURE|GSS_S_BAD_NAME); 386 } 387 388 /* verify that the requested mechanism set is the default, or 389 contains krb5 */ 390 391 if (desired_mechs == GSS_C_NULL_OID_SET) { 392 valid_mechs = gss_mech_set_krb5_both; 393 req_old = 1; 394 req_new = 1; 395 } else { 396 req_old = 0; 397 req_new = 0; 398 399 for (i=0; i<desired_mechs->count; i++) { 400 if (g_OID_equal(gss_mech_krb5_old, &(desired_mechs->elements[i]))) 401 req_old++; 402 if (g_OID_equal(gss_mech_krb5, &(desired_mechs->elements[i]))) 403 req_new++; 404 } 405 406 if (!req_old && !req_new) { 407 *minor_status = 0; 408 return(GSS_S_BAD_MECH); 409 } 410 } 411 412 /* create the gss cred structure */ 413 414 if ((cred = 415 (krb5_gss_cred_id_t) xmalloc(sizeof(krb5_gss_cred_id_rec))) == NULL) { 416 *minor_status = ENOMEM; 417 return(GSS_S_FAILURE); 418 } 419 memset(cred, 0, sizeof(krb5_gss_cred_id_rec)); 420 421 cred->usage = cred_usage; 422 cred->princ = NULL; 423 cred->actual_mechs = valid_mechs; 424 cred->prerfc_mech = req_old; 425 cred->rfc_mech = req_new; 426 427 cred->keytab = NULL; 428 cred->ccache = NULL; 429 430 if ((cred_usage != GSS_C_INITIATE) && 431 (cred_usage != GSS_C_ACCEPT) && 432 (cred_usage != GSS_C_BOTH)) { 433 xfree(cred); 434 *minor_status = (OM_uint32) G_BAD_USAGE; 435 return(GSS_S_FAILURE); 436 } 437 438 /* if requested, acquire credentials for accepting */ 439 /* this will fill in cred->princ if the desired_name is not specified */ 440 441 if ((cred_usage == GSS_C_ACCEPT) || 442 (cred_usage == GSS_C_BOTH)) 443 if ((ret = acquire_accept_cred(context, minor_status, desired_name, 444 &(cred->princ), cred)) 445 != GSS_S_COMPLETE) { 446 if (cred->princ) 447 krb5_free_principal(context, cred->princ); 448 xfree(cred); 449 /* minor_status set by acquire_accept_cred() */ 450 return(ret); 451 } 452 453 /* if requested, acquire credentials for initiation */ 454 /* this will fill in cred->princ if it wasn't set above, and 455 the desired_name is not specified */ 456 457 if ((cred_usage == GSS_C_INITIATE) || 458 (cred_usage == GSS_C_BOTH)) 459 if ((ret = 460 acquire_init_cred(context, minor_status, 461 cred->princ?(gss_name_t)cred->princ:desired_name, 462 &(cred->princ), cred)) 463 != GSS_S_COMPLETE) { 464 if (cred->keytab) 465 (void) krb5_kt_close(context, cred->keytab); 466 if (cred->princ) 467 krb5_free_principal(context, cred->princ); 468 xfree(cred); 469 /* minor_status set by acquire_init_cred() */ 470 return(ret); 471 } 472 473 /* Solaris Kerberos: 474 * if the princ wasn't filled in already, fill it in now unless 475 * a cred with no associated princ is requested (will invoke default 476 * behaviour when gss_accept_init_context() is called). 477 */ 478 if (!cred->princ && (desired_name != GSS_C_NO_NAME)) 479 if ((code = krb5_copy_principal(context, (krb5_principal) desired_name, 480 &(cred->princ)))) { 481 if (cred->ccache) 482 (void)krb5_cc_close(context, cred->ccache); 483 if (cred->keytab) 484 (void)krb5_kt_close(context, cred->keytab); 485 xfree(cred); 486 *minor_status = code; 487 return(GSS_S_FAILURE); 488 } 489 490 /*** at this point, the cred structure has been completely created */ 491 492 /* compute time_rec */ 493 494 if (cred_usage == GSS_C_ACCEPT) { 495 if (time_rec) 496 *time_rec = GSS_C_INDEFINITE; 497 } else { 498 krb5_timestamp now; 499 500 if ((code = krb5_timeofday(context, &now))) { 501 if (cred->ccache) 502 (void)krb5_cc_close(context, cred->ccache); 503 if (cred->keytab) 504 (void)krb5_kt_close(context, cred->keytab); 505 if (cred->princ) 506 krb5_free_principal(context, cred->princ); 507 xfree(cred); 508 *minor_status = code; 509 return(GSS_S_FAILURE); 510 } 511 512 if (time_rec) 513 *time_rec = (cred->tgt_expire > now) ? (cred->tgt_expire - now) : 0; 514 } 515 516 /* create mechs */ 517 518 if (actual_mechs) { 519 if (GSS_ERROR(ret = gss_create_empty_oid_set(minor_status, 520 &ret_mechs)) || 521 (cred->prerfc_mech && 522 GSS_ERROR(ret = gss_add_oid_set_member(minor_status, 523 (gss_OID) gss_mech_krb5_old, 524 &ret_mechs))) || 525 (cred->rfc_mech && 526 GSS_ERROR(ret = gss_add_oid_set_member(minor_status, 527 (gss_OID) gss_mech_krb5, 528 &ret_mechs)))) { 529 if (cred->ccache) 530 (void)krb5_cc_close(context, cred->ccache); 531 if (cred->keytab) 532 (void)krb5_kt_close(context, cred->keytab); 533 if (cred->princ) 534 krb5_free_principal(context, cred->princ); 535 xfree(cred); 536 /* (*minor_status) set above */ 537 return(ret); 538 } 539 } 540 541 /* intern the credential handle */ 542 543 if (! kg_save_cred_id((gss_cred_id_t) cred)) { 544 (void) gss_release_oid_set(NULL, &ret_mechs); 545 free(ret_mechs->elements); 546 free(ret_mechs); 547 if (cred->ccache) 548 (void)krb5_cc_close(context, cred->ccache); 549 if (cred->keytab) 550 (void)krb5_kt_close(context, cred->keytab); 551 if (cred->princ) 552 krb5_free_principal(context, cred->princ); 553 xfree(cred); 554 *minor_status = (OM_uint32) G_VALIDATE_FAILED; 555 return(GSS_S_FAILURE); 556 } 557 558 /* return success */ 559 560 *minor_status = 0; 561 *output_cred_handle = (gss_cred_id_t) cred; 562 if (actual_mechs) 563 *actual_mechs = ret_mechs; 564 return(GSS_S_COMPLETE); 565 } 566