1 /* ccapi/lib/ccapi_context.c */ 2 /* 3 * Copyright 2006, 2007 Massachusetts Institute of Technology. 4 * All Rights Reserved. 5 * 6 * Export of this software from the United States of America may 7 * require a specific license from the United States Government. 8 * It is the responsibility of any person or organization contemplating 9 * export to obtain such a license before exporting. 10 * 11 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and 12 * distribute this software and its documentation for any purpose and 13 * without fee is hereby granted, provided that the above copyright 14 * notice appear in all copies and that both that copyright notice and 15 * this permission notice appear in supporting documentation, and that 16 * the name of M.I.T. not be used in advertising or publicity pertaining 17 * to distribution of the software without specific, written prior 18 * permission. Furthermore if you modify this software you must label 19 * your software as modified software and not distribute it in such a 20 * fashion that it might be confused with the original M.I.T. software. 21 * M.I.T. makes no representations about the suitability of 22 * this software for any purpose. It is provided "as is" without express 23 * or implied warranty. 24 */ 25 26 #include "ccapi_context.h" 27 28 #include "k5-platform.h" 29 30 #include "ccapi_ccache.h" 31 #include "ccapi_ccache_iterator.h" 32 #include "ccapi_string.h" 33 #include "ccapi_ipc.h" 34 #include "ccapi_context_change_time.h" 35 #include "ccapi_err.h" 36 37 #include <CredentialsCache2.h> 38 39 typedef struct cci_context_d { 40 cc_context_f *functions; 41 #if TARGET_OS_MAC 42 cc_context_f *vector_functions; 43 #endif 44 cci_identifier_t identifier; 45 cc_uint32 synchronized; 46 cc_time_t last_wait_for_change_time; 47 } *cci_context_t; 48 49 /* ------------------------------------------------------------------------ */ 50 51 struct cci_context_d cci_context_initializer = { 52 NULL 53 VECTOR_FUNCTIONS_INITIALIZER, 54 NULL, 55 0, 56 0 57 }; 58 59 cc_context_f cci_context_f_initializer = { 60 ccapi_context_release, 61 ccapi_context_get_change_time, 62 ccapi_context_get_default_ccache_name, 63 ccapi_context_open_ccache, 64 ccapi_context_open_default_ccache, 65 ccapi_context_create_ccache, 66 ccapi_context_create_default_ccache, 67 ccapi_context_create_new_ccache, 68 ccapi_context_new_ccache_iterator, 69 ccapi_context_lock, 70 ccapi_context_unlock, 71 ccapi_context_compare, 72 ccapi_context_wait_for_change 73 }; 74 75 static cc_int32 cci_context_sync (cci_context_t in_context, 76 cc_uint32 in_launch); 77 78 #ifdef TARGET_OS_MAC 79 #pragma mark - 80 #endif 81 82 MAKE_INIT_FUNCTION(cci_process_init); 83 MAKE_FINI_FUNCTION(cci_process_fini); 84 85 /* ------------------------------------------------------------------------ */ 86 87 static int cci_process_init (void) 88 { 89 cc_int32 err = ccNoError; 90 91 if (!err) { 92 err = cci_context_change_time_thread_init (); 93 } 94 95 if (!err) { 96 err = cci_ipc_process_init (); 97 } 98 99 if (!err) { 100 add_error_table (&et_CAPI_error_table); 101 } 102 103 return err; 104 } 105 106 /* ------------------------------------------------------------------------ */ 107 108 static void cci_process_fini (void) 109 { 110 if (!INITIALIZER_RAN (cci_process_init) || PROGRAM_EXITING ()) { 111 return; 112 } 113 114 remove_error_table(&et_CAPI_error_table); 115 cci_context_change_time_thread_fini (); 116 } 117 118 119 #ifdef TARGET_OS_MAC 120 #pragma mark - 121 #endif 122 123 /* ------------------------------------------------------------------------ */ 124 125 cc_int32 cc_initialize (cc_context_t *out_context, 126 cc_int32 in_version, 127 cc_int32 *out_supported_version, 128 char const **out_vendor) 129 { 130 cc_int32 err = ccNoError; 131 cci_context_t context = NULL; 132 static char *vendor_string = "MIT Kerberos CCAPI"; 133 134 if (!out_context) { err = cci_check_error (ccErrBadParam); } 135 136 if (!err) { 137 err = CALL_INIT_FUNCTION (cci_process_init); 138 } 139 140 if (!err) { 141 switch (in_version) { 142 case ccapi_version_2: 143 case ccapi_version_3: 144 case ccapi_version_4: 145 case ccapi_version_5: 146 case ccapi_version_6: 147 case ccapi_version_7: 148 break; 149 150 default: 151 err = ccErrBadAPIVersion; 152 break; 153 } 154 } 155 156 if (!err) { 157 context = malloc (sizeof (*context)); 158 if (context) { 159 *context = cci_context_initializer; 160 } else { 161 err = cci_check_error (ccErrNoMem); 162 } 163 } 164 165 if (!err) { 166 context->functions = malloc (sizeof (*context->functions)); 167 if (context->functions) { 168 *context->functions = cci_context_f_initializer; 169 } else { 170 err = cci_check_error (ccErrNoMem); 171 } 172 } 173 174 if (!err) { 175 context->identifier = cci_identifier_uninitialized; 176 177 *out_context = (cc_context_t) context; 178 context = NULL; /* take ownership */ 179 180 if (out_supported_version) { 181 *out_supported_version = ccapi_version_max; 182 } 183 184 if (out_vendor) { 185 *out_vendor = vendor_string; 186 } 187 } 188 189 ccapi_context_release ((cc_context_t) context); 190 191 return cci_check_error (err); 192 } 193 194 #ifdef TARGET_OS_MAC 195 #pragma mark - 196 #endif 197 198 /* ------------------------------------------------------------------------ */ 199 /* 200 * Currently does not need to talk to the server since the server must 201 * handle cleaning up resources from crashed clients anyway. 202 * 203 * NOTE: if server communication is ever added here, make sure that 204 * krb5_stdcc_shutdown calls an internal function which does not talk to the 205 * server. krb5_stdcc_shutdown is called from thread fini functions and may 206 * crash talking to the server depending on what order the OS calls the fini 207 * functions (ie: if the ipc layer fini function is called first). 208 */ 209 210 cc_int32 ccapi_context_release (cc_context_t in_context) 211 { 212 cc_int32 err = ccNoError; 213 cci_context_t context = (cci_context_t) in_context; 214 215 if (!in_context) { err = ccErrBadParam; } 216 217 if (!err) { 218 cci_identifier_release (context->identifier); 219 free (context->functions); 220 free (context); 221 } 222 223 return err; 224 } 225 226 /* ------------------------------------------------------------------------ */ 227 228 cc_int32 ccapi_context_get_change_time (cc_context_t in_context, 229 cc_time_t *out_change_time) 230 { 231 cc_int32 err = ccNoError; 232 cci_context_t context = (cci_context_t) in_context; 233 k5_ipc_stream reply = NULL; 234 235 if (!in_context ) { err = cci_check_error (ccErrBadParam); } 236 if (!out_change_time) { err = cci_check_error (ccErrBadParam); } 237 238 if (!err) { 239 err = cci_context_sync (context, 0); 240 } 241 242 if (!err) { 243 err = cci_ipc_send_no_launch (cci_context_get_change_time_msg_id, 244 context->identifier, 245 NULL, &reply); 246 } 247 248 if (!err && krb5int_ipc_stream_size (reply) > 0) { 249 cc_time_t change_time = 0; 250 251 /* got a response from the server */ 252 err = krb5int_ipc_stream_read_time (reply, &change_time); 253 254 if (!err) { 255 err = cci_context_change_time_update (context->identifier, 256 change_time); 257 } 258 } 259 260 if (!err) { 261 err = cci_context_change_time_get (out_change_time); 262 } 263 264 krb5int_ipc_stream_release (reply); 265 266 return cci_check_error (err); 267 } 268 269 /* ------------------------------------------------------------------------ */ 270 271 cc_int32 ccapi_context_wait_for_change (cc_context_t in_context) 272 { 273 cc_int32 err = ccNoError; 274 cci_context_t context = (cci_context_t) in_context; 275 k5_ipc_stream request = NULL; 276 k5_ipc_stream reply = NULL; 277 278 if (!in_context) { err = cci_check_error (ccErrBadParam); } 279 280 if (!err) { 281 err = krb5int_ipc_stream_new (&request); 282 } 283 284 if (!err) { 285 err = krb5int_ipc_stream_write_time (request, context->last_wait_for_change_time); 286 } 287 288 if (!err) { 289 err = cci_context_sync (context, 1); 290 } 291 292 if (!err) { 293 err = cci_ipc_send (cci_context_wait_for_change_msg_id, 294 context->identifier, 295 request, 296 &reply); 297 } 298 299 if (!err) { 300 err = krb5int_ipc_stream_read_time (reply, &context->last_wait_for_change_time); 301 } 302 303 krb5int_ipc_stream_release (request); 304 krb5int_ipc_stream_release (reply); 305 306 return cci_check_error (err); 307 } 308 309 /* ------------------------------------------------------------------------ */ 310 311 cc_int32 ccapi_context_get_default_ccache_name (cc_context_t in_context, 312 cc_string_t *out_name) 313 { 314 cc_int32 err = ccNoError; 315 cci_context_t context = (cci_context_t) in_context; 316 k5_ipc_stream reply = NULL; 317 char *reply_name = NULL; 318 char *name = NULL; 319 320 if (!in_context) { err = cci_check_error (ccErrBadParam); } 321 if (!out_name ) { err = cci_check_error (ccErrBadParam); } 322 323 if (!err) { 324 err = cci_context_sync (context, 0); 325 } 326 327 if (!err) { 328 err = cci_ipc_send_no_launch (cci_context_get_default_ccache_name_msg_id, 329 context->identifier, 330 NULL, 331 &reply); 332 } 333 334 if (!err) { 335 if (krb5int_ipc_stream_size (reply) > 0) { 336 /* got a response from the server */ 337 err = krb5int_ipc_stream_read_string (reply, &reply_name); 338 339 if (!err) { 340 name = reply_name; 341 } 342 } else { 343 name = k_cci_context_initial_ccache_name; 344 } 345 } 346 347 if (!err) { 348 err = cci_string_new (out_name, name); 349 } 350 351 krb5int_ipc_stream_release (reply); 352 krb5int_ipc_stream_free_string (reply_name); 353 354 return cci_check_error (err); 355 } 356 357 /* ------------------------------------------------------------------------ */ 358 359 cc_int32 ccapi_context_open_ccache (cc_context_t in_context, 360 const char *in_name, 361 cc_ccache_t *out_ccache) 362 { 363 cc_int32 err = ccNoError; 364 cci_context_t context = (cci_context_t) in_context; 365 k5_ipc_stream request = NULL; 366 k5_ipc_stream reply = NULL; 367 cci_identifier_t identifier = NULL; 368 369 if (!in_context ) { err = cci_check_error (ccErrBadParam); } 370 if (!in_name ) { err = cci_check_error (ccErrBadParam); } 371 if (!out_ccache ) { err = cci_check_error (ccErrBadParam); } 372 373 if (!err) { 374 err = krb5int_ipc_stream_new (&request); 375 } 376 377 if (!err) { 378 err = krb5int_ipc_stream_write_string (request, in_name); 379 } 380 381 if (!err) { 382 err = cci_context_sync (context, 0); 383 } 384 385 if (!err) { 386 err = cci_ipc_send_no_launch (cci_context_open_ccache_msg_id, 387 context->identifier, 388 request, 389 &reply); 390 } 391 392 if (!err && !(krb5int_ipc_stream_size (reply) > 0)) { 393 err = ccErrCCacheNotFound; 394 } 395 396 if (!err) { 397 err = cci_identifier_read (&identifier, reply); 398 } 399 400 if (!err) { 401 err = cci_ccache_new (out_ccache, identifier); 402 } 403 404 cci_identifier_release (identifier); 405 krb5int_ipc_stream_release (reply); 406 krb5int_ipc_stream_release (request); 407 408 return cci_check_error (err); 409 } 410 411 /* ------------------------------------------------------------------------ */ 412 413 cc_int32 ccapi_context_open_default_ccache (cc_context_t in_context, 414 cc_ccache_t *out_ccache) 415 { 416 cc_int32 err = ccNoError; 417 cci_context_t context = (cci_context_t) in_context; 418 k5_ipc_stream reply = NULL; 419 cci_identifier_t identifier = NULL; 420 421 if (!in_context) { err = cci_check_error (ccErrBadParam); } 422 if (!out_ccache) { err = cci_check_error (ccErrBadParam); } 423 424 if (!err) { 425 err = cci_context_sync (context, 0); 426 } 427 428 if (!err) { 429 err = cci_ipc_send_no_launch (cci_context_open_default_ccache_msg_id, 430 context->identifier, 431 NULL, 432 &reply); 433 } 434 435 if (!err && !(krb5int_ipc_stream_size (reply) > 0)) { 436 err = ccErrCCacheNotFound; 437 } 438 439 if (!err) { 440 err = cci_identifier_read (&identifier, reply); 441 } 442 443 if (!err) { 444 err = cci_ccache_new (out_ccache, identifier); 445 } 446 447 cci_identifier_release (identifier); 448 krb5int_ipc_stream_release (reply); 449 450 return cci_check_error (err); 451 } 452 453 /* ------------------------------------------------------------------------ */ 454 455 cc_int32 ccapi_context_create_ccache (cc_context_t in_context, 456 const char *in_name, 457 cc_uint32 in_cred_vers, 458 const char *in_principal, 459 cc_ccache_t *out_ccache) 460 { 461 cc_int32 err = ccNoError; 462 cci_context_t context = (cci_context_t) in_context; 463 k5_ipc_stream request = NULL; 464 k5_ipc_stream reply = NULL; 465 cci_identifier_t identifier = NULL; 466 467 if (!in_context ) { err = cci_check_error (ccErrBadParam); } 468 if (!in_name ) { err = cci_check_error (ccErrBadParam); } 469 if (!in_principal) { err = cci_check_error (ccErrBadParam); } 470 if (!out_ccache ) { err = cci_check_error (ccErrBadParam); } 471 472 if (!err) { 473 err = krb5int_ipc_stream_new (&request); 474 } 475 476 if (!err) { 477 err = krb5int_ipc_stream_write_string (request, in_name); 478 } 479 480 if (!err) { 481 err = krb5int_ipc_stream_write_uint32 (request, in_cred_vers); 482 } 483 484 if (!err) { 485 err = krb5int_ipc_stream_write_string (request, in_principal); 486 } 487 488 if (!err) { 489 err = cci_context_sync (context, 1); 490 } 491 492 if (!err) { 493 err = cci_ipc_send (cci_context_create_ccache_msg_id, 494 context->identifier, 495 request, 496 &reply); 497 } 498 499 if (!err) { 500 err = cci_identifier_read (&identifier, reply); 501 } 502 503 if (!err) { 504 err = cci_ccache_new (out_ccache, identifier); 505 } 506 507 cci_identifier_release (identifier); 508 krb5int_ipc_stream_release (reply); 509 krb5int_ipc_stream_release (request); 510 511 return cci_check_error (err); 512 } 513 514 /* ------------------------------------------------------------------------ */ 515 516 cc_int32 ccapi_context_create_default_ccache (cc_context_t in_context, 517 cc_uint32 in_cred_vers, 518 const char *in_principal, 519 cc_ccache_t *out_ccache) 520 { 521 cc_int32 err = ccNoError; 522 cci_context_t context = (cci_context_t) in_context; 523 k5_ipc_stream request = NULL; 524 k5_ipc_stream reply = NULL; 525 cci_identifier_t identifier = NULL; 526 527 if (!in_context ) { err = cci_check_error (ccErrBadParam); } 528 if (!in_principal) { err = cci_check_error (ccErrBadParam); } 529 if (!out_ccache ) { err = cci_check_error (ccErrBadParam); } 530 531 if (!err) { 532 err = krb5int_ipc_stream_new (&request); 533 } 534 535 if (!err) { 536 err = krb5int_ipc_stream_write_uint32 (request, in_cred_vers); 537 } 538 539 if (!err) { 540 err = krb5int_ipc_stream_write_string (request, in_principal); 541 } 542 543 if (!err) { 544 err = cci_context_sync (context, 1); 545 } 546 547 if (!err) { 548 err = cci_ipc_send (cci_context_create_default_ccache_msg_id, 549 context->identifier, 550 request, 551 &reply); 552 } 553 554 if (!err) { 555 err = cci_identifier_read (&identifier, reply); 556 } 557 558 if (!err) { 559 err = cci_ccache_new (out_ccache, identifier); 560 } 561 562 cci_identifier_release (identifier); 563 krb5int_ipc_stream_release (reply); 564 krb5int_ipc_stream_release (request); 565 566 return cci_check_error (err); 567 } 568 569 /* ------------------------------------------------------------------------ */ 570 571 cc_int32 ccapi_context_create_new_ccache (cc_context_t in_context, 572 cc_uint32 in_cred_vers, 573 const char *in_principal, 574 cc_ccache_t *out_ccache) 575 { 576 cc_int32 err = ccNoError; 577 cci_context_t context = (cci_context_t) in_context; 578 k5_ipc_stream request = NULL; 579 k5_ipc_stream reply = NULL; 580 cci_identifier_t identifier = NULL; 581 582 if (!in_context ) { err = cci_check_error (ccErrBadParam); } 583 if (!in_principal) { err = cci_check_error (ccErrBadParam); } 584 if (!out_ccache ) { err = cci_check_error (ccErrBadParam); } 585 586 if (!err) { 587 err = krb5int_ipc_stream_new (&request); 588 } 589 590 if (!err) { 591 err = krb5int_ipc_stream_write_uint32 (request, in_cred_vers); 592 } 593 594 if (!err) { 595 err = krb5int_ipc_stream_write_string (request, in_principal); 596 } 597 598 if (!err) { 599 err = cci_context_sync (context, 1); 600 } 601 602 if (!err) { 603 err = cci_ipc_send (cci_context_create_new_ccache_msg_id, 604 context->identifier, 605 request, 606 &reply); 607 } 608 609 if (!err) { 610 err = cci_identifier_read (&identifier, reply); 611 } 612 613 if (!err) { 614 err = cci_ccache_new (out_ccache, identifier); 615 } 616 617 cci_identifier_release (identifier); 618 krb5int_ipc_stream_release (reply); 619 krb5int_ipc_stream_release (request); 620 621 return cci_check_error (err); 622 } 623 624 /* ------------------------------------------------------------------------ */ 625 626 cc_int32 ccapi_context_new_ccache_iterator (cc_context_t in_context, 627 cc_ccache_iterator_t *out_iterator) 628 { 629 cc_int32 err = ccNoError; 630 cci_context_t context = (cci_context_t) in_context; 631 k5_ipc_stream reply = NULL; 632 cci_identifier_t identifier = NULL; 633 634 if (!in_context ) { err = cci_check_error (ccErrBadParam); } 635 if (!out_iterator) { err = cci_check_error (ccErrBadParam); } 636 637 if (!err) { 638 err = cci_context_sync (context, 0); 639 } 640 641 if (!err) { 642 err = cci_ipc_send_no_launch (cci_context_new_ccache_iterator_msg_id, 643 context->identifier, 644 NULL, 645 &reply); 646 } 647 648 if (!err) { 649 if (krb5int_ipc_stream_size (reply) > 0) { 650 err = cci_identifier_read (&identifier, reply); 651 } else { 652 identifier = cci_identifier_uninitialized; 653 } 654 } 655 656 if (!err) { 657 err = cci_ccache_iterator_new (out_iterator, identifier); 658 } 659 660 krb5int_ipc_stream_release (reply); 661 cci_identifier_release (identifier); 662 663 return cci_check_error (err); 664 } 665 666 /* ------------------------------------------------------------------------ */ 667 668 cc_int32 ccapi_context_lock (cc_context_t in_context, 669 cc_uint32 in_lock_type, 670 cc_uint32 in_block) 671 { 672 cc_int32 err = ccNoError; 673 cci_context_t context = (cci_context_t) in_context; 674 k5_ipc_stream request = NULL; 675 676 if (!in_context) { err = cci_check_error (ccErrBadParam); } 677 678 if (!err) { 679 err = krb5int_ipc_stream_new (&request); 680 } 681 682 if (!err) { 683 err = krb5int_ipc_stream_write_uint32 (request, in_lock_type); 684 } 685 686 if (!err) { 687 err = krb5int_ipc_stream_write_uint32 (request, in_block); 688 } 689 690 if (!err) { 691 err = cci_context_sync (context, 1); 692 } 693 694 if (!err) { 695 err = cci_ipc_send (cci_context_lock_msg_id, 696 context->identifier, 697 request, 698 NULL); 699 } 700 701 krb5int_ipc_stream_release (request); 702 703 return cci_check_error (err); 704 } 705 706 /* ------------------------------------------------------------------------ */ 707 708 cc_int32 ccapi_context_unlock (cc_context_t in_context) 709 { 710 cc_int32 err = ccNoError; 711 cci_context_t context = (cci_context_t) in_context; 712 713 if (!in_context) { err = cci_check_error (ccErrBadParam); } 714 715 if (!err) { 716 err = cci_context_sync (context, 1); 717 } 718 719 if (!err) { 720 err = cci_ipc_send (cci_context_unlock_msg_id, 721 context->identifier, 722 NULL, 723 NULL); 724 } 725 726 return cci_check_error (err); 727 } 728 729 /* ------------------------------------------------------------------------ */ 730 731 cc_int32 ccapi_context_compare (cc_context_t in_context, 732 cc_context_t in_compare_to_context, 733 cc_uint32 *out_equal) 734 { 735 cc_int32 err = ccNoError; 736 cci_context_t context = (cci_context_t) in_context; 737 cci_context_t compare_to_context = (cci_context_t) in_compare_to_context; 738 739 if (!in_context ) { err = cci_check_error (ccErrBadParam); } 740 if (!in_compare_to_context) { err = cci_check_error (ccErrBadParam); } 741 if (!out_equal ) { err = cci_check_error (ccErrBadParam); } 742 743 if (!err) { 744 err = cci_context_sync (context, 0); 745 } 746 747 if (!err) { 748 err = cci_context_sync (compare_to_context, 0); 749 } 750 751 if (!err) { 752 /* If both contexts can't talk to the server, then 753 * we assume they are equivalent */ 754 err = cci_identifier_compare (context->identifier, 755 compare_to_context->identifier, 756 out_equal); 757 } 758 759 return cci_check_error (err); 760 } 761 762 #ifdef TARGET_OS_MAC 763 #pragma mark - 764 #endif 765 766 /* ------------------------------------------------------------------------ */ 767 768 static cc_int32 cci_context_sync (cci_context_t in_context, 769 cc_uint32 in_launch) 770 { 771 cc_int32 err = ccNoError; 772 cci_context_t context = (cci_context_t) in_context; 773 k5_ipc_stream reply = NULL; 774 cci_identifier_t new_identifier = NULL; 775 776 if (!in_context) { err = cci_check_error (ccErrBadParam); } 777 778 if (!err) { 779 /* Use the uninitialized identifier because we may be talking */ 780 /* to a different server which would reject our identifier and */ 781 /* the point of this message is to sync with the server's id */ 782 if (in_launch) { 783 err = cci_ipc_send (cci_context_sync_msg_id, 784 cci_identifier_uninitialized, 785 NULL, 786 &reply); 787 } else { 788 err = cci_ipc_send_no_launch (cci_context_sync_msg_id, 789 cci_identifier_uninitialized, 790 NULL, 791 &reply); 792 } 793 } 794 795 if (!err) { 796 if (krb5int_ipc_stream_size (reply) > 0) { 797 err = cci_identifier_read (&new_identifier, reply); 798 } else { 799 new_identifier = cci_identifier_uninitialized; 800 } 801 } 802 803 if (!err) { 804 cc_uint32 equal = 0; 805 806 err = cci_identifier_compare (context->identifier, new_identifier, &equal); 807 808 if (!err && !equal) { 809 if (context->identifier) { 810 cci_identifier_release (context->identifier); 811 } 812 context->identifier = new_identifier; 813 new_identifier = NULL; /* take ownership */ 814 } 815 } 816 817 if (!err && context->synchronized) { 818 err = cci_context_change_time_sync (context->identifier); 819 } 820 821 if (!err && !context->synchronized) { 822 /* Keep state about whether this is the first call to avoid always */ 823 /* modifying the global change time on the context's first ipc call. */ 824 context->synchronized = 1; 825 } 826 827 cci_identifier_release (new_identifier); 828 krb5int_ipc_stream_release (reply); 829 830 return cci_check_error (err); 831 } 832