1 /* #pragma ident "@(#)g_acquire_cred.c 1.22 04/02/23 SMI" */ 2 3 /* 4 * Copyright 1996 by Sun Microsystems, Inc. 5 * 6 * Permission to use, copy, modify, distribute, and sell this software 7 * and its documentation for any purpose is hereby granted without fee, 8 * provided that the above copyright notice appears in all copies and 9 * that both that copyright notice and this permission notice appear in 10 * supporting documentation, and that the name of Sun Microsystems not be used 11 * in advertising or publicity pertaining to distribution of the software 12 * without specific, written prior permission. Sun Microsystems makes no 13 * representations about the suitability of this software for any 14 * purpose. It is provided "as is" without express or implied warranty. 15 * 16 * SUN MICROSYSTEMS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, 17 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO 18 * EVENT SHALL SUN MICROSYSTEMS BE LIABLE FOR ANY SPECIAL, INDIRECT OR 19 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF 20 * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR 21 * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 22 * PERFORMANCE OF THIS SOFTWARE. 23 */ 24 25 /* 26 * glue routine for gss_acquire_cred 27 */ 28 29 #include "mglueP.h" 30 #include <stdio.h> 31 #ifdef HAVE_STDLIB_H 32 #include <stdlib.h> 33 #endif 34 #include <string.h> 35 #include <errno.h> 36 #include <time.h> 37 38 static OM_uint32 39 val_acq_cred_args( 40 OM_uint32 *minor_status, 41 gss_name_t desired_name, 42 OM_uint32 time_req, 43 gss_OID_set desired_mechs, 44 int cred_usage, 45 gss_const_key_value_set_t cred_store, 46 gss_cred_id_t *output_cred_handle, 47 gss_OID_set *actual_mechs, 48 OM_uint32 *time_rec) 49 { 50 51 /* Initialize outputs. */ 52 53 if (minor_status != NULL) 54 *minor_status = 0; 55 56 if (output_cred_handle != NULL) 57 *output_cred_handle = GSS_C_NO_CREDENTIAL; 58 59 if (actual_mechs != NULL) 60 *actual_mechs = GSS_C_NULL_OID_SET; 61 62 if (time_rec != NULL) 63 *time_rec = 0; 64 65 /* Validate arguments. */ 66 67 if (minor_status == NULL) 68 return (GSS_S_CALL_INACCESSIBLE_WRITE); 69 70 if (output_cred_handle == NULL) 71 return (GSS_S_CALL_INACCESSIBLE_WRITE); 72 73 if (cred_usage != GSS_C_ACCEPT 74 && cred_usage != GSS_C_INITIATE 75 && cred_usage != GSS_C_BOTH) { 76 if (minor_status) { 77 *minor_status = EINVAL; 78 map_errcode(minor_status); 79 } 80 return GSS_S_FAILURE; 81 } 82 83 return (GSS_S_COMPLETE); 84 } 85 86 87 OM_uint32 KRB5_CALLCONV 88 gss_acquire_cred(OM_uint32 *minor_status, gss_name_t desired_name, 89 OM_uint32 time_req, gss_OID_set desired_mechs, 90 int cred_usage, gss_cred_id_t *output_cred_handle, 91 gss_OID_set *actual_mechs, OM_uint32 *time_rec) 92 { 93 return gss_acquire_cred_from(minor_status, desired_name, time_req, 94 desired_mechs, cred_usage, NULL, 95 output_cred_handle, actual_mechs, time_rec); 96 } 97 98 OM_uint32 KRB5_CALLCONV 99 gss_acquire_cred_from(OM_uint32 * minor_status, gss_name_t desired_name, 100 OM_uint32 time_req, gss_OID_set desired_mechs, 101 int cred_usage, gss_const_key_value_set_t cred_store, 102 gss_cred_id_t *output_cred_handle, 103 gss_OID_set *actual_mechs, OM_uint32 *time_rec) 104 { 105 OM_uint32 major = GSS_S_FAILURE, tmpMinor; 106 OM_uint32 first_major = GSS_S_COMPLETE, first_minor = 0; 107 OM_uint32 initTimeOut = 0, acceptTimeOut = 0, outTime = GSS_C_INDEFINITE; 108 gss_OID_set mechs = GSS_C_NO_OID_SET; 109 gss_OID_set_desc except_attrs; 110 gss_OID_desc attr_oids[2]; 111 unsigned int i; 112 gss_union_cred_t creds = NULL; 113 114 major = val_acq_cred_args(minor_status, 115 desired_name, 116 time_req, 117 desired_mechs, 118 cred_usage, 119 cred_store, 120 output_cred_handle, 121 actual_mechs, 122 time_rec); 123 if (major != GSS_S_COMPLETE) 124 goto cleanup; 125 126 /* 127 * if desired_mechs equals GSS_C_NULL_OID_SET, then try to 128 * acquire credentials for all non-deprecated mechanisms. 129 */ 130 if (desired_mechs == GSS_C_NULL_OID_SET) { 131 attr_oids[0] = *GSS_C_MA_DEPRECATED; 132 attr_oids[1] = *GSS_C_MA_NOT_DFLT_MECH; 133 except_attrs.count = 2; 134 except_attrs.elements = attr_oids; 135 major = gss_indicate_mechs_by_attrs(minor_status, GSS_C_NO_OID_SET, 136 &except_attrs, GSS_C_NO_OID_SET, 137 &mechs); 138 if (major != GSS_S_COMPLETE) 139 goto cleanup; 140 } else 141 mechs = desired_mechs; 142 143 if (mechs->count == 0) { 144 major = GSS_S_BAD_MECH; 145 goto cleanup; 146 } 147 148 /* allocate the output credential structure */ 149 creds = (gss_union_cred_t)calloc(1, sizeof (gss_union_cred_desc)); 150 if (creds == NULL) { 151 major = GSS_S_FAILURE; 152 *minor_status = ENOMEM; 153 goto cleanup; 154 } 155 156 creds->count = 0; 157 creds->loopback = creds; 158 159 /* for each requested mech attempt to obtain a credential */ 160 for (i = 0, major = GSS_S_UNAVAILABLE; i < mechs->count; i++) { 161 major = gss_add_cred_from(&tmpMinor, (gss_cred_id_t)creds, 162 desired_name, &mechs->elements[i], 163 cred_usage, time_req, time_req, 164 cred_store, NULL, NULL, 165 time_rec ? &initTimeOut : NULL, 166 time_rec ? &acceptTimeOut : NULL); 167 if (major == GSS_S_COMPLETE) { 168 /* update the credential's time */ 169 if (cred_usage == GSS_C_ACCEPT) { 170 if (outTime > acceptTimeOut) 171 outTime = acceptTimeOut; 172 } else if (cred_usage == GSS_C_INITIATE) { 173 if (outTime > initTimeOut) 174 outTime = initTimeOut; 175 } else { 176 /* 177 * time_rec is the lesser of the 178 * init/accept times 179 */ 180 if (initTimeOut > acceptTimeOut) 181 outTime = (outTime > acceptTimeOut) ? 182 acceptTimeOut : outTime; 183 else 184 outTime = (outTime > initTimeOut) ? 185 initTimeOut : outTime; 186 } 187 } else if (first_major == GSS_S_COMPLETE) { 188 first_major = major; 189 first_minor = tmpMinor; 190 } 191 } /* for */ 192 193 /* If we didn't get any creds, return the error status from the first mech 194 * (which is often the preferred one). */ 195 if (creds->count < 1) { 196 major = first_major; 197 *minor_status = first_minor; 198 goto cleanup; 199 } 200 major = GSS_S_COMPLETE; 201 202 /* 203 * fill in output parameters 204 * setup the actual mechs output parameter 205 */ 206 if (actual_mechs != NULL) { 207 major = gssint_make_public_oid_set(minor_status, creds->mechs_array, 208 creds->count, actual_mechs); 209 if (GSS_ERROR(major)) 210 goto cleanup; 211 } 212 213 if (time_rec) 214 *time_rec = outTime; 215 216 *output_cred_handle = (gss_cred_id_t)creds; 217 218 cleanup: 219 if (GSS_ERROR(major)) 220 gss_release_cred(&tmpMinor, (gss_cred_id_t *)&creds); 221 if (desired_mechs == GSS_C_NO_OID_SET) 222 generic_gss_release_oid_set(&tmpMinor, &mechs); 223 224 return (major); 225 } 226 227 static OM_uint32 228 val_add_cred_args( 229 OM_uint32 *minor_status, 230 gss_cred_id_t input_cred_handle, 231 gss_name_t desired_name, 232 gss_OID desired_mech, 233 gss_cred_usage_t cred_usage, 234 gss_const_key_value_set_t cred_store, 235 OM_uint32 initiator_time_req, 236 OM_uint32 acceptor_time_req, 237 gss_cred_id_t *output_cred_handle, 238 gss_OID_set *actual_mechs, 239 OM_uint32 *initiator_time_rec, 240 OM_uint32 *acceptor_time_rec) 241 { 242 243 /* Initialize outputs. */ 244 245 if (minor_status != NULL) 246 *minor_status = 0; 247 248 if (output_cred_handle != NULL) 249 *output_cred_handle = GSS_C_NO_CREDENTIAL; 250 251 if (actual_mechs != NULL) 252 *actual_mechs = GSS_C_NO_OID_SET; 253 254 if (acceptor_time_rec != NULL) 255 *acceptor_time_rec = 0; 256 257 if (initiator_time_rec != NULL) 258 *initiator_time_rec = 0; 259 260 /* Validate arguments. */ 261 262 if (minor_status == NULL) 263 return (GSS_S_CALL_INACCESSIBLE_WRITE); 264 265 if (input_cred_handle == GSS_C_NO_CREDENTIAL && 266 output_cred_handle == NULL) 267 return (GSS_S_CALL_INACCESSIBLE_WRITE | GSS_S_NO_CRED); 268 269 if (cred_usage != GSS_C_ACCEPT 270 && cred_usage != GSS_C_INITIATE 271 && cred_usage != GSS_C_BOTH) { 272 if (minor_status) { 273 *minor_status = EINVAL; 274 map_errcode(minor_status); 275 } 276 return GSS_S_FAILURE; 277 } 278 279 return (GSS_S_COMPLETE); 280 } 281 282 /* Copy a mechanism credential (with the mechanism given by mech_oid) as 283 * faithfully as possible. */ 284 static OM_uint32 285 copy_mech_cred(OM_uint32 *minor_status, gss_cred_id_t cred_in, 286 gss_OID mech_oid, gss_cred_id_t *cred_out) 287 { 288 OM_uint32 status, tmpmin; 289 gss_mechanism mech; 290 gss_buffer_desc buf; 291 gss_name_t name; 292 OM_uint32 life; 293 gss_cred_usage_t usage; 294 gss_OID_set_desc oidset; 295 296 mech = gssint_get_mechanism(mech_oid); 297 if (mech == NULL) 298 return (GSS_S_BAD_MECH); 299 if (mech->gss_export_cred != NULL && mech->gss_import_cred != NULL) { 300 status = mech->gss_export_cred(minor_status, cred_in, &buf); 301 if (status != GSS_S_COMPLETE) 302 return (status); 303 status = mech->gss_import_cred(minor_status, &buf, cred_out); 304 (void) gss_release_buffer(&tmpmin, &buf); 305 } else if (mech->gss_inquire_cred != NULL && 306 mech->gss_acquire_cred != NULL) { 307 status = mech->gss_inquire_cred(minor_status, cred_in, &name, &life, 308 &usage, NULL); 309 if (status != GSS_S_COMPLETE) 310 return (status); 311 oidset.count = 1; 312 oidset.elements = gssint_get_public_oid(mech_oid); 313 status = mech->gss_acquire_cred(minor_status, name, life, &oidset, 314 usage, cred_out, NULL, NULL); 315 gss_release_name(&tmpmin, &name); 316 } else { 317 status = GSS_S_UNAVAILABLE; 318 } 319 return (status); 320 } 321 322 /* Copy a union credential from cred_in to *cred_out. */ 323 static OM_uint32 324 copy_union_cred(OM_uint32 *minor_status, gss_cred_id_t cred_in, 325 gss_union_cred_t *cred_out) 326 { 327 OM_uint32 status, tmpmin; 328 gss_union_cred_t cred = (gss_union_cred_t)cred_in; 329 gss_union_cred_t ncred = NULL; 330 gss_cred_id_t tmpcred; 331 int i; 332 333 ncred = calloc(1, sizeof (*ncred)); 334 if (ncred == NULL) 335 goto oom; 336 ncred->mechs_array = calloc(cred->count, sizeof (*ncred->mechs_array)); 337 ncred->cred_array = calloc(cred->count, sizeof (*ncred->cred_array)); 338 if (ncred->mechs_array == NULL || ncred->cred_array == NULL) 339 goto oom; 340 ncred->count = cred->count; 341 342 for (i = 0; i < cred->count; i++) { 343 /* Copy this element's mechanism OID. */ 344 ncred->mechs_array[i].elements = malloc(cred->mechs_array[i].length); 345 if (ncred->mechs_array[i].elements == NULL) 346 goto oom; 347 g_OID_copy(&ncred->mechs_array[i], &cred->mechs_array[i]); 348 349 /* Copy this element's mechanism cred. */ 350 status = copy_mech_cred(minor_status, cred->cred_array[i], 351 &cred->mechs_array[i], &ncred->cred_array[i]); 352 if (status != GSS_S_COMPLETE) 353 goto error; 354 } 355 356 ncred->loopback = ncred; 357 *cred_out = ncred; 358 return GSS_S_COMPLETE; 359 360 oom: 361 status = GSS_S_FAILURE; 362 *minor_status = ENOMEM; 363 error: 364 tmpcred = (gss_cred_id_t)ncred; 365 (void) gss_release_cred(&tmpmin, &tmpcred); 366 return status; 367 } 368 369 /* V2 KRB5_CALLCONV */ 370 OM_uint32 KRB5_CALLCONV 371 gss_add_cred(OM_uint32 *minor_status, gss_cred_id_t input_cred_handle, 372 gss_name_t desired_name, gss_OID desired_mech, 373 gss_cred_usage_t cred_usage, OM_uint32 initiator_time_req, 374 OM_uint32 acceptor_time_req, gss_cred_id_t *output_cred_handle, 375 gss_OID_set *actual_mechs, OM_uint32 *initiator_time_rec, 376 OM_uint32 *acceptor_time_rec) 377 { 378 return gss_add_cred_from(minor_status, input_cred_handle, desired_name, 379 desired_mech, cred_usage, initiator_time_req, 380 acceptor_time_req, NULL, output_cred_handle, 381 actual_mechs, initiator_time_rec, 382 acceptor_time_rec); 383 } 384 385 OM_uint32 KRB5_CALLCONV 386 gss_add_cred_from(OM_uint32 *minor_status, gss_cred_id_t input_cred_handle, 387 gss_name_t desired_name, gss_OID desired_mech, 388 gss_cred_usage_t cred_usage, OM_uint32 initiator_time_req, 389 OM_uint32 acceptor_time_req, 390 gss_const_key_value_set_t cred_store, 391 gss_cred_id_t *output_cred_handle, gss_OID_set *actual_mechs, 392 OM_uint32 *initiator_time_rec, OM_uint32 *acceptor_time_rec) 393 { 394 OM_uint32 status, temp_minor_status; 395 OM_uint32 time_req, time_rec = 0, *time_recp = NULL; 396 gss_union_name_t union_name; 397 gss_union_cred_t union_cred; 398 gss_name_t internal_name = GSS_C_NO_NAME; 399 gss_name_t allocated_name = GSS_C_NO_NAME; 400 gss_mechanism mech; 401 gss_cred_id_t cred = NULL, tmpcred; 402 void *newptr, *oidbuf = NULL; 403 gss_OID_set_desc target_mechs; 404 gss_OID selected_mech = GSS_C_NO_OID; 405 406 status = val_add_cred_args(minor_status, 407 input_cred_handle, 408 desired_name, 409 desired_mech, 410 cred_usage, 411 cred_store, 412 initiator_time_req, 413 acceptor_time_req, 414 output_cred_handle, 415 actual_mechs, 416 initiator_time_rec, 417 acceptor_time_rec); 418 if (status != GSS_S_COMPLETE) 419 return (status); 420 421 status = gssint_select_mech_type(minor_status, desired_mech, 422 &selected_mech); 423 if (status != GSS_S_COMPLETE) 424 return (status); 425 426 mech = gssint_get_mechanism(selected_mech); 427 if (!mech) 428 return GSS_S_BAD_MECH; 429 else if (!mech->gss_acquire_cred) 430 return (GSS_S_UNAVAILABLE); 431 432 union_cred = (gss_union_cred_t)input_cred_handle; 433 if (union_cred != NULL && 434 gssint_get_mechanism_cred(union_cred, 435 selected_mech) != GSS_C_NO_CREDENTIAL) 436 return (GSS_S_DUPLICATE_ELEMENT); 437 438 if (union_cred == NULL) { 439 /* Create a new credential handle. */ 440 union_cred = malloc(sizeof (gss_union_cred_desc)); 441 if (union_cred == NULL) 442 return (GSS_S_FAILURE); 443 444 (void) memset(union_cred, 0, sizeof (gss_union_cred_desc)); 445 union_cred->loopback = union_cred; 446 } else if (output_cred_handle != NULL) { 447 /* Create a new credential handle with the mechanism credentials of the 448 * input handle plus the acquired mechanism credential. */ 449 status = copy_union_cred(minor_status, input_cred_handle, &union_cred); 450 if (status != GSS_S_COMPLETE) 451 return (status); 452 } 453 454 /* We may need to create a mechanism specific name. */ 455 if (desired_name != GSS_C_NO_NAME) { 456 union_name = (gss_union_name_t)desired_name; 457 if (union_name->mech_type && 458 g_OID_equal(union_name->mech_type, selected_mech)) { 459 internal_name = union_name->mech_name; 460 } else { 461 if (gssint_import_internal_name(minor_status, selected_mech, 462 union_name, &allocated_name) != 463 GSS_S_COMPLETE) { 464 status = GSS_S_BAD_NAME; 465 goto errout; 466 } 467 internal_name = allocated_name; 468 } 469 } 470 471 472 if (cred_usage == GSS_C_ACCEPT) 473 time_req = acceptor_time_req; 474 else if (cred_usage == GSS_C_INITIATE) 475 time_req = initiator_time_req; 476 else if (cred_usage == GSS_C_BOTH) 477 time_req = (acceptor_time_req > initiator_time_req) ? 478 acceptor_time_req : initiator_time_req; 479 else 480 time_req = 0; 481 482 target_mechs.count = 1; 483 target_mechs.elements = gssint_get_public_oid(selected_mech); 484 if (target_mechs.elements == NULL) { 485 status = GSS_S_FAILURE; 486 goto errout; 487 } 488 489 if (initiator_time_rec != NULL || acceptor_time_rec != NULL) 490 time_recp = &time_rec; 491 492 if (mech->gss_acquire_cred_from) { 493 status = mech->gss_acquire_cred_from(minor_status, internal_name, 494 time_req, &target_mechs, 495 cred_usage, cred_store, &cred, 496 NULL, time_recp); 497 } else if (cred_store == GSS_C_NO_CRED_STORE) { 498 status = mech->gss_acquire_cred(minor_status, internal_name, time_req, 499 &target_mechs, cred_usage, &cred, NULL, 500 time_recp); 501 } else { 502 status = GSS_S_UNAVAILABLE; 503 goto errout; 504 } 505 506 if (status != GSS_S_COMPLETE) { 507 map_error(minor_status, mech); 508 goto errout; 509 } 510 511 /* Extend the arrays in the union cred. */ 512 513 newptr = realloc(union_cred->mechs_array, 514 (union_cred->count + 1) * sizeof (gss_OID_desc)); 515 if (newptr == NULL) { 516 status = GSS_S_FAILURE; 517 goto errout; 518 } 519 union_cred->mechs_array = newptr; 520 521 newptr = realloc(union_cred->cred_array, 522 (union_cred->count + 1) * sizeof (gss_cred_id_t)); 523 if (newptr == NULL) { 524 status = GSS_S_FAILURE; 525 goto errout; 526 } 527 union_cred->cred_array = newptr; 528 529 if (acceptor_time_rec) 530 if (cred_usage == GSS_C_ACCEPT || cred_usage == GSS_C_BOTH) 531 *acceptor_time_rec = time_rec; 532 if (initiator_time_rec) 533 if (cred_usage == GSS_C_INITIATE || cred_usage == GSS_C_BOTH) 534 *initiator_time_rec = time_rec; 535 536 oidbuf = malloc(selected_mech->length); 537 if (oidbuf == NULL) 538 goto errout; 539 union_cred->mechs_array[union_cred->count].elements = oidbuf; 540 g_OID_copy(&union_cred->mechs_array[union_cred->count], selected_mech); 541 542 if (actual_mechs != NULL) { 543 status = gssint_make_public_oid_set(minor_status, 544 union_cred->mechs_array, 545 union_cred->count + 1, 546 actual_mechs); 547 if (GSS_ERROR(status)) 548 goto errout; 549 } 550 551 union_cred->cred_array[union_cred->count] = cred; 552 union_cred->count++; 553 if (output_cred_handle != NULL) 554 *output_cred_handle = (gss_cred_id_t)union_cred; 555 556 /* We're done with the internal name. Free it if we allocated it. */ 557 558 if (allocated_name) 559 (void) gssint_release_internal_name(&temp_minor_status, 560 selected_mech, 561 &allocated_name); 562 563 return (GSS_S_COMPLETE); 564 565 errout: 566 if (cred != NULL && mech->gss_release_cred) 567 mech->gss_release_cred(&temp_minor_status, &cred); 568 569 if (allocated_name) 570 (void) gssint_release_internal_name(&temp_minor_status, 571 selected_mech, 572 &allocated_name); 573 574 if (output_cred_handle != NULL && union_cred != NULL) { 575 tmpcred = union_cred; 576 (void) gss_release_cred(&temp_minor_status, &tmpcred); 577 } 578 579 free(oidbuf); 580 581 return (status); 582 } 583