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(minor_status, 89 desired_name, 90 time_req, 91 desired_mechs, 92 cred_usage, 93 output_cred_handle, 94 actual_mechs, 95 time_rec) 96 97 OM_uint32 * minor_status; 98 gss_name_t desired_name; 99 OM_uint32 time_req; 100 gss_OID_set desired_mechs; 101 int cred_usage; 102 gss_cred_id_t * output_cred_handle; 103 gss_OID_set * actual_mechs; 104 OM_uint32 * time_rec; 105 106 { 107 return gss_acquire_cred_from(minor_status, desired_name, time_req, 108 desired_mechs, cred_usage, NULL, 109 output_cred_handle, actual_mechs, time_rec); 110 } 111 112 OM_uint32 KRB5_CALLCONV 113 gss_acquire_cred_from(minor_status, 114 desired_name, 115 time_req, 116 desired_mechs, 117 cred_usage, 118 cred_store, 119 output_cred_handle, 120 actual_mechs, 121 time_rec) 122 123 OM_uint32 * minor_status; 124 gss_name_t desired_name; 125 OM_uint32 time_req; 126 gss_OID_set desired_mechs; 127 int cred_usage; 128 gss_const_key_value_set_t cred_store; 129 gss_cred_id_t * output_cred_handle; 130 gss_OID_set * actual_mechs; 131 OM_uint32 * time_rec; 132 133 { 134 OM_uint32 major = GSS_S_FAILURE, tmpMinor; 135 OM_uint32 first_major = GSS_S_COMPLETE, first_minor = 0; 136 OM_uint32 initTimeOut = 0, acceptTimeOut = 0, outTime = GSS_C_INDEFINITE; 137 gss_OID_set mechs = GSS_C_NO_OID_SET; 138 gss_OID_set_desc except_attrs; 139 gss_OID_desc attr_oids[2]; 140 unsigned int i; 141 gss_union_cred_t creds = NULL; 142 143 major = val_acq_cred_args(minor_status, 144 desired_name, 145 time_req, 146 desired_mechs, 147 cred_usage, 148 cred_store, 149 output_cred_handle, 150 actual_mechs, 151 time_rec); 152 if (major != GSS_S_COMPLETE) 153 goto cleanup; 154 155 /* 156 * if desired_mechs equals GSS_C_NULL_OID_SET, then try to 157 * acquire credentials for all non-deprecated mechanisms. 158 */ 159 if (desired_mechs == GSS_C_NULL_OID_SET) { 160 attr_oids[0] = *GSS_C_MA_DEPRECATED; 161 attr_oids[1] = *GSS_C_MA_NOT_DFLT_MECH; 162 except_attrs.count = 2; 163 except_attrs.elements = attr_oids; 164 major = gss_indicate_mechs_by_attrs(minor_status, GSS_C_NO_OID_SET, 165 &except_attrs, GSS_C_NO_OID_SET, 166 &mechs); 167 if (major != GSS_S_COMPLETE) 168 goto cleanup; 169 } else 170 mechs = desired_mechs; 171 172 if (mechs->count == 0) { 173 major = GSS_S_BAD_MECH; 174 goto cleanup; 175 } 176 177 /* allocate the output credential structure */ 178 creds = (gss_union_cred_t)calloc(1, sizeof (gss_union_cred_desc)); 179 if (creds == NULL) { 180 major = GSS_S_FAILURE; 181 *minor_status = ENOMEM; 182 goto cleanup; 183 } 184 185 creds->count = 0; 186 creds->loopback = creds; 187 188 /* for each requested mech attempt to obtain a credential */ 189 for (i = 0, major = GSS_S_UNAVAILABLE; i < mechs->count; i++) { 190 major = gss_add_cred_from(&tmpMinor, (gss_cred_id_t)creds, 191 desired_name, &mechs->elements[i], 192 cred_usage, time_req, time_req, 193 cred_store, NULL, NULL, 194 time_rec ? &initTimeOut : NULL, 195 time_rec ? &acceptTimeOut : NULL); 196 if (major == GSS_S_COMPLETE) { 197 /* update the credential's time */ 198 if (cred_usage == GSS_C_ACCEPT) { 199 if (outTime > acceptTimeOut) 200 outTime = acceptTimeOut; 201 } else if (cred_usage == GSS_C_INITIATE) { 202 if (outTime > initTimeOut) 203 outTime = initTimeOut; 204 } else { 205 /* 206 * time_rec is the lesser of the 207 * init/accept times 208 */ 209 if (initTimeOut > acceptTimeOut) 210 outTime = (outTime > acceptTimeOut) ? 211 acceptTimeOut : outTime; 212 else 213 outTime = (outTime > initTimeOut) ? 214 initTimeOut : outTime; 215 } 216 } else if (first_major == GSS_S_COMPLETE) { 217 first_major = major; 218 first_minor = tmpMinor; 219 } 220 } /* for */ 221 222 /* If we didn't get any creds, return the error status from the first mech 223 * (which is often the preferred one). */ 224 if (creds->count < 1) { 225 major = first_major; 226 *minor_status = first_minor; 227 goto cleanup; 228 } 229 major = GSS_S_COMPLETE; 230 231 /* 232 * fill in output parameters 233 * setup the actual mechs output parameter 234 */ 235 if (actual_mechs != NULL) { 236 major = gssint_make_public_oid_set(minor_status, creds->mechs_array, 237 creds->count, actual_mechs); 238 if (GSS_ERROR(major)) 239 goto cleanup; 240 } 241 242 if (time_rec) 243 *time_rec = outTime; 244 245 *output_cred_handle = (gss_cred_id_t)creds; 246 247 cleanup: 248 if (GSS_ERROR(major)) 249 gss_release_cred(&tmpMinor, (gss_cred_id_t *)&creds); 250 if (desired_mechs == GSS_C_NO_OID_SET) 251 generic_gss_release_oid_set(&tmpMinor, &mechs); 252 253 return (major); 254 } 255 256 static OM_uint32 257 val_add_cred_args( 258 OM_uint32 *minor_status, 259 gss_cred_id_t input_cred_handle, 260 gss_name_t desired_name, 261 gss_OID desired_mech, 262 gss_cred_usage_t cred_usage, 263 gss_const_key_value_set_t cred_store, 264 OM_uint32 initiator_time_req, 265 OM_uint32 acceptor_time_req, 266 gss_cred_id_t *output_cred_handle, 267 gss_OID_set *actual_mechs, 268 OM_uint32 *initiator_time_rec, 269 OM_uint32 *acceptor_time_rec) 270 { 271 272 /* Initialize outputs. */ 273 274 if (minor_status != NULL) 275 *minor_status = 0; 276 277 if (output_cred_handle != NULL) 278 *output_cred_handle = GSS_C_NO_CREDENTIAL; 279 280 if (actual_mechs != NULL) 281 *actual_mechs = GSS_C_NO_OID_SET; 282 283 if (acceptor_time_rec != NULL) 284 *acceptor_time_rec = 0; 285 286 if (initiator_time_rec != NULL) 287 *initiator_time_rec = 0; 288 289 /* Validate arguments. */ 290 291 if (minor_status == NULL) 292 return (GSS_S_CALL_INACCESSIBLE_WRITE); 293 294 if (input_cred_handle == GSS_C_NO_CREDENTIAL && 295 output_cred_handle == NULL) 296 return (GSS_S_CALL_INACCESSIBLE_WRITE | GSS_S_NO_CRED); 297 298 if (cred_usage != GSS_C_ACCEPT 299 && cred_usage != GSS_C_INITIATE 300 && cred_usage != GSS_C_BOTH) { 301 if (minor_status) { 302 *minor_status = EINVAL; 303 map_errcode(minor_status); 304 } 305 return GSS_S_FAILURE; 306 } 307 308 return (GSS_S_COMPLETE); 309 } 310 311 /* Copy a mechanism credential (with the mechanism given by mech_oid) as 312 * faithfully as possible. */ 313 static OM_uint32 314 copy_mech_cred(OM_uint32 *minor_status, gss_cred_id_t cred_in, 315 gss_OID mech_oid, gss_cred_id_t *cred_out) 316 { 317 OM_uint32 status, tmpmin; 318 gss_mechanism mech; 319 gss_buffer_desc buf; 320 gss_name_t name; 321 OM_uint32 life; 322 gss_cred_usage_t usage; 323 gss_OID_set_desc oidset; 324 325 mech = gssint_get_mechanism(mech_oid); 326 if (mech == NULL) 327 return (GSS_S_BAD_MECH); 328 if (mech->gss_export_cred != NULL && mech->gss_import_cred != NULL) { 329 status = mech->gss_export_cred(minor_status, cred_in, &buf); 330 if (status != GSS_S_COMPLETE) 331 return (status); 332 status = mech->gss_import_cred(minor_status, &buf, cred_out); 333 (void) gss_release_buffer(&tmpmin, &buf); 334 } else if (mech->gss_inquire_cred != NULL && 335 mech->gss_acquire_cred != NULL) { 336 status = mech->gss_inquire_cred(minor_status, cred_in, &name, &life, 337 &usage, NULL); 338 if (status != GSS_S_COMPLETE) 339 return (status); 340 oidset.count = 1; 341 oidset.elements = gssint_get_public_oid(mech_oid); 342 status = mech->gss_acquire_cred(minor_status, name, life, &oidset, 343 usage, cred_out, NULL, NULL); 344 gss_release_name(&tmpmin, &name); 345 } else { 346 status = GSS_S_UNAVAILABLE; 347 } 348 return (status); 349 } 350 351 /* Copy a union credential from cred_in to *cred_out. */ 352 static OM_uint32 353 copy_union_cred(OM_uint32 *minor_status, gss_cred_id_t cred_in, 354 gss_union_cred_t *cred_out) 355 { 356 OM_uint32 status, tmpmin; 357 gss_union_cred_t cred = (gss_union_cred_t)cred_in; 358 gss_union_cred_t ncred = NULL; 359 gss_cred_id_t tmpcred; 360 int i; 361 362 ncred = calloc(1, sizeof (*ncred)); 363 if (ncred == NULL) 364 goto oom; 365 ncred->mechs_array = calloc(cred->count, sizeof (*ncred->mechs_array)); 366 ncred->cred_array = calloc(cred->count, sizeof (*ncred->cred_array)); 367 if (ncred->mechs_array == NULL || ncred->cred_array == NULL) 368 goto oom; 369 ncred->count = cred->count; 370 371 for (i = 0; i < cred->count; i++) { 372 /* Copy this element's mechanism OID. */ 373 ncred->mechs_array[i].elements = malloc(cred->mechs_array[i].length); 374 if (ncred->mechs_array[i].elements == NULL) 375 goto oom; 376 g_OID_copy(&ncred->mechs_array[i], &cred->mechs_array[i]); 377 378 /* Copy this element's mechanism cred. */ 379 status = copy_mech_cred(minor_status, cred->cred_array[i], 380 &cred->mechs_array[i], &ncred->cred_array[i]); 381 if (status != GSS_S_COMPLETE) 382 goto error; 383 } 384 385 ncred->loopback = ncred; 386 *cred_out = ncred; 387 return GSS_S_COMPLETE; 388 389 oom: 390 status = GSS_S_FAILURE; 391 *minor_status = ENOMEM; 392 error: 393 tmpcred = (gss_cred_id_t)ncred; 394 (void) gss_release_cred(&tmpmin, &tmpcred); 395 return status; 396 } 397 398 /* V2 KRB5_CALLCONV */ 399 OM_uint32 KRB5_CALLCONV 400 gss_add_cred(minor_status, input_cred_handle, 401 desired_name, desired_mech, cred_usage, 402 initiator_time_req, acceptor_time_req, 403 output_cred_handle, actual_mechs, 404 initiator_time_rec, acceptor_time_rec) 405 OM_uint32 *minor_status; 406 gss_cred_id_t input_cred_handle; 407 gss_name_t desired_name; 408 gss_OID desired_mech; 409 gss_cred_usage_t cred_usage; 410 OM_uint32 initiator_time_req; 411 OM_uint32 acceptor_time_req; 412 gss_cred_id_t *output_cred_handle; 413 gss_OID_set *actual_mechs; 414 OM_uint32 *initiator_time_rec; 415 OM_uint32 *acceptor_time_rec; 416 { 417 return gss_add_cred_from(minor_status, input_cred_handle, desired_name, 418 desired_mech, cred_usage, initiator_time_req, 419 acceptor_time_req, NULL, output_cred_handle, 420 actual_mechs, initiator_time_rec, 421 acceptor_time_rec); 422 } 423 424 OM_uint32 KRB5_CALLCONV 425 gss_add_cred_from(minor_status, input_cred_handle, 426 desired_name, desired_mech, 427 cred_usage, 428 initiator_time_req, acceptor_time_req, 429 cred_store, 430 output_cred_handle, actual_mechs, 431 initiator_time_rec, acceptor_time_rec) 432 OM_uint32 *minor_status; 433 gss_cred_id_t input_cred_handle; 434 gss_name_t desired_name; 435 gss_OID desired_mech; 436 gss_cred_usage_t cred_usage; 437 OM_uint32 initiator_time_req; 438 OM_uint32 acceptor_time_req; 439 gss_const_key_value_set_t cred_store; 440 gss_cred_id_t *output_cred_handle; 441 gss_OID_set *actual_mechs; 442 OM_uint32 *initiator_time_rec; 443 OM_uint32 *acceptor_time_rec; 444 { 445 OM_uint32 status, temp_minor_status; 446 OM_uint32 time_req, time_rec = 0, *time_recp = NULL; 447 gss_union_name_t union_name; 448 gss_union_cred_t union_cred; 449 gss_name_t internal_name = GSS_C_NO_NAME; 450 gss_name_t allocated_name = GSS_C_NO_NAME; 451 gss_mechanism mech; 452 gss_cred_id_t cred = NULL, tmpcred; 453 void *newptr, *oidbuf = NULL; 454 gss_OID_set_desc target_mechs; 455 gss_OID selected_mech = GSS_C_NO_OID; 456 457 status = val_add_cred_args(minor_status, 458 input_cred_handle, 459 desired_name, 460 desired_mech, 461 cred_usage, 462 cred_store, 463 initiator_time_req, 464 acceptor_time_req, 465 output_cred_handle, 466 actual_mechs, 467 initiator_time_rec, 468 acceptor_time_rec); 469 if (status != GSS_S_COMPLETE) 470 return (status); 471 472 status = gssint_select_mech_type(minor_status, desired_mech, 473 &selected_mech); 474 if (status != GSS_S_COMPLETE) 475 return (status); 476 477 mech = gssint_get_mechanism(selected_mech); 478 if (!mech) 479 return GSS_S_BAD_MECH; 480 else if (!mech->gss_acquire_cred) 481 return (GSS_S_UNAVAILABLE); 482 483 union_cred = (gss_union_cred_t)input_cred_handle; 484 if (union_cred != NULL && 485 gssint_get_mechanism_cred(union_cred, 486 selected_mech) != GSS_C_NO_CREDENTIAL) 487 return (GSS_S_DUPLICATE_ELEMENT); 488 489 if (union_cred == NULL) { 490 /* Create a new credential handle. */ 491 union_cred = malloc(sizeof (gss_union_cred_desc)); 492 if (union_cred == NULL) 493 return (GSS_S_FAILURE); 494 495 (void) memset(union_cred, 0, sizeof (gss_union_cred_desc)); 496 union_cred->loopback = union_cred; 497 } else if (output_cred_handle != NULL) { 498 /* Create a new credential handle with the mechanism credentials of the 499 * input handle plus the acquired mechanism credential. */ 500 status = copy_union_cred(minor_status, input_cred_handle, &union_cred); 501 if (status != GSS_S_COMPLETE) 502 return (status); 503 } 504 505 /* We may need to create a mechanism specific name. */ 506 if (desired_name != GSS_C_NO_NAME) { 507 union_name = (gss_union_name_t)desired_name; 508 if (union_name->mech_type && 509 g_OID_equal(union_name->mech_type, selected_mech)) { 510 internal_name = union_name->mech_name; 511 } else { 512 if (gssint_import_internal_name(minor_status, selected_mech, 513 union_name, &allocated_name) != 514 GSS_S_COMPLETE) { 515 status = GSS_S_BAD_NAME; 516 goto errout; 517 } 518 internal_name = allocated_name; 519 } 520 } 521 522 523 if (cred_usage == GSS_C_ACCEPT) 524 time_req = acceptor_time_req; 525 else if (cred_usage == GSS_C_INITIATE) 526 time_req = initiator_time_req; 527 else if (cred_usage == GSS_C_BOTH) 528 time_req = (acceptor_time_req > initiator_time_req) ? 529 acceptor_time_req : initiator_time_req; 530 else 531 time_req = 0; 532 533 target_mechs.count = 1; 534 target_mechs.elements = gssint_get_public_oid(selected_mech); 535 if (target_mechs.elements == NULL) { 536 status = GSS_S_FAILURE; 537 goto errout; 538 } 539 540 if (initiator_time_rec != NULL || acceptor_time_rec != NULL) 541 time_recp = &time_rec; 542 543 if (mech->gss_acquire_cred_from) { 544 status = mech->gss_acquire_cred_from(minor_status, internal_name, 545 time_req, &target_mechs, 546 cred_usage, cred_store, &cred, 547 NULL, time_recp); 548 } else if (cred_store == GSS_C_NO_CRED_STORE) { 549 status = mech->gss_acquire_cred(minor_status, internal_name, time_req, 550 &target_mechs, cred_usage, &cred, NULL, 551 time_recp); 552 } else { 553 status = GSS_S_UNAVAILABLE; 554 goto errout; 555 } 556 557 if (status != GSS_S_COMPLETE) { 558 map_error(minor_status, mech); 559 goto errout; 560 } 561 562 /* Extend the arrays in the union cred. */ 563 564 newptr = realloc(union_cred->mechs_array, 565 (union_cred->count + 1) * sizeof (gss_OID_desc)); 566 if (newptr == NULL) { 567 status = GSS_S_FAILURE; 568 goto errout; 569 } 570 union_cred->mechs_array = newptr; 571 572 newptr = realloc(union_cred->cred_array, 573 (union_cred->count + 1) * sizeof (gss_cred_id_t)); 574 if (newptr == NULL) { 575 status = GSS_S_FAILURE; 576 goto errout; 577 } 578 union_cred->cred_array = newptr; 579 580 if (acceptor_time_rec) 581 if (cred_usage == GSS_C_ACCEPT || cred_usage == GSS_C_BOTH) 582 *acceptor_time_rec = time_rec; 583 if (initiator_time_rec) 584 if (cred_usage == GSS_C_INITIATE || cred_usage == GSS_C_BOTH) 585 *initiator_time_rec = time_rec; 586 587 oidbuf = malloc(selected_mech->length); 588 if (oidbuf == NULL) 589 goto errout; 590 union_cred->mechs_array[union_cred->count].elements = oidbuf; 591 g_OID_copy(&union_cred->mechs_array[union_cred->count], selected_mech); 592 593 if (actual_mechs != NULL) { 594 status = gssint_make_public_oid_set(minor_status, 595 union_cred->mechs_array, 596 union_cred->count + 1, 597 actual_mechs); 598 if (GSS_ERROR(status)) 599 goto errout; 600 } 601 602 union_cred->cred_array[union_cred->count] = cred; 603 union_cred->count++; 604 if (output_cred_handle != NULL) 605 *output_cred_handle = (gss_cred_id_t)union_cred; 606 607 /* We're done with the internal name. Free it if we allocated it. */ 608 609 if (allocated_name) 610 (void) gssint_release_internal_name(&temp_minor_status, 611 selected_mech, 612 &allocated_name); 613 614 return (GSS_S_COMPLETE); 615 616 errout: 617 if (cred != NULL && mech->gss_release_cred) 618 mech->gss_release_cred(&temp_minor_status, &cred); 619 620 if (allocated_name) 621 (void) gssint_release_internal_name(&temp_minor_status, 622 selected_mech, 623 &allocated_name); 624 625 if (output_cred_handle != NULL && union_cred != NULL) { 626 tmpcred = union_cred; 627 (void) gss_release_cred(&temp_minor_status, &tmpcred); 628 } 629 630 free(oidbuf); 631 632 return (status); 633 } 634