1 /* 2 * Copyright (c) 2005, PADL Software Pty Ltd. 3 * All rights reserved. 4 * 5 * Portions Copyright (c) 2009 Apple Inc. All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 18 * 3. Neither the name of PADL Software nor the names of its contributors 19 * may be used to endorse or promote products derived from this software 20 * without specific prior written permission. 21 * 22 * THIS SOFTWARE IS PROVIDED BY PADL SOFTWARE AND CONTRIBUTORS ``AS IS'' AND 23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 25 * ARE DISCLAIMED. IN NO EVENT SHALL PADL SOFTWARE OR CONTRIBUTORS BE LIABLE 26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 32 * SUCH DAMAGE. 33 */ 34 35 #include "krb5_locl.h" 36 37 #ifdef HAVE_KCM 38 /* 39 * Client library for Kerberos Credentials Manager (KCM) daemon 40 */ 41 42 #include "kcm.h" 43 #include <heim-ipc.h> 44 45 static krb5_error_code 46 kcm_set_kdc_offset(krb5_context, krb5_ccache, krb5_deltat); 47 48 static const char *kcm_ipc_name = "ANY:org.h5l.kcm"; 49 50 typedef struct krb5_kcmcache { 51 char *name; 52 } krb5_kcmcache; 53 54 typedef struct krb5_kcm_cursor { 55 unsigned long offset; 56 unsigned long length; 57 kcmuuid_t *uuids; 58 } *krb5_kcm_cursor; 59 60 61 #define KCMCACHE(X) ((krb5_kcmcache *)(X)->data.data) 62 #define CACHENAME(X) (KCMCACHE(X)->name) 63 #define KCMCURSOR(C) ((krb5_kcm_cursor)(C)) 64 65 static HEIMDAL_MUTEX kcm_mutex = HEIMDAL_MUTEX_INITIALIZER; 66 static heim_ipc kcm_ipc = NULL; 67 68 static krb5_error_code 69 kcm_send_request(krb5_context context, 70 krb5_storage *request, 71 krb5_data *response_data) 72 { 73 krb5_error_code ret = 0; 74 krb5_data request_data; 75 76 HEIMDAL_MUTEX_lock(&kcm_mutex); 77 if (kcm_ipc == NULL) 78 ret = heim_ipc_init_context(kcm_ipc_name, &kcm_ipc); 79 HEIMDAL_MUTEX_unlock(&kcm_mutex); 80 if (ret) 81 return KRB5_CC_NOSUPP; 82 83 ret = krb5_storage_to_data(request, &request_data); 84 if (ret) { 85 krb5_clear_error_message(context); 86 return KRB5_CC_NOMEM; 87 } 88 89 ret = heim_ipc_call(kcm_ipc, &request_data, response_data, NULL); 90 krb5_data_free(&request_data); 91 92 if (ret) { 93 krb5_clear_error_message(context); 94 ret = KRB5_CC_NOSUPP; 95 } 96 97 return ret; 98 } 99 100 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 101 krb5_kcm_storage_request(krb5_context context, 102 uint16_t opcode, 103 krb5_storage **storage_p) 104 { 105 krb5_storage *sp; 106 krb5_error_code ret; 107 108 *storage_p = NULL; 109 110 sp = krb5_storage_emem(); 111 if (sp == NULL) { 112 krb5_set_error_message(context, KRB5_CC_NOMEM, N_("malloc: out of memory", "")); 113 return KRB5_CC_NOMEM; 114 } 115 116 /* Send MAJOR | VERSION | OPCODE */ 117 ret = krb5_store_int8(sp, KCM_PROTOCOL_VERSION_MAJOR); 118 if (ret) 119 goto fail; 120 ret = krb5_store_int8(sp, KCM_PROTOCOL_VERSION_MINOR); 121 if (ret) 122 goto fail; 123 ret = krb5_store_int16(sp, opcode); 124 if (ret) 125 goto fail; 126 127 *storage_p = sp; 128 fail: 129 if (ret) { 130 krb5_set_error_message(context, ret, 131 N_("Failed to encode KCM request", "")); 132 krb5_storage_free(sp); 133 } 134 135 return ret; 136 } 137 138 static krb5_error_code 139 kcm_alloc(krb5_context context, const char *name, krb5_ccache *id) 140 { 141 krb5_kcmcache *k; 142 143 k = malloc(sizeof(*k)); 144 if (k == NULL) { 145 krb5_set_error_message(context, KRB5_CC_NOMEM, 146 N_("malloc: out of memory", "")); 147 return KRB5_CC_NOMEM; 148 } 149 150 if (name != NULL) { 151 k->name = strdup(name); 152 if (k->name == NULL) { 153 free(k); 154 krb5_set_error_message(context, KRB5_CC_NOMEM, 155 N_("malloc: out of memory", "")); 156 return KRB5_CC_NOMEM; 157 } 158 } else 159 k->name = NULL; 160 161 (*id)->data.data = k; 162 (*id)->data.length = sizeof(*k); 163 164 return 0; 165 } 166 167 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 168 krb5_kcm_call(krb5_context context, 169 krb5_storage *request, 170 krb5_storage **response_p, 171 krb5_data *response_data_p) 172 { 173 krb5_data response_data; 174 krb5_error_code ret; 175 int32_t status; 176 krb5_storage *response; 177 178 if (response_p != NULL) 179 *response_p = NULL; 180 181 krb5_data_zero(&response_data); 182 183 ret = kcm_send_request(context, request, &response_data); 184 if (ret) 185 return ret; 186 187 response = krb5_storage_from_data(&response_data); 188 if (response == NULL) { 189 krb5_data_free(&response_data); 190 return KRB5_CC_IO; 191 } 192 193 ret = krb5_ret_int32(response, &status); 194 if (ret) { 195 krb5_storage_free(response); 196 krb5_data_free(&response_data); 197 return KRB5_CC_FORMAT; 198 } 199 200 if (status) { 201 krb5_storage_free(response); 202 krb5_data_free(&response_data); 203 return status; 204 } 205 206 if (response_p != NULL) { 207 *response_data_p = response_data; 208 *response_p = response; 209 210 return 0; 211 } 212 213 krb5_storage_free(response); 214 krb5_data_free(&response_data); 215 216 return 0; 217 } 218 219 static void 220 kcm_free(krb5_context context, krb5_ccache *id) 221 { 222 krb5_kcmcache *k = KCMCACHE(*id); 223 224 if (k != NULL) { 225 if (k->name != NULL) 226 free(k->name); 227 memset(k, 0, sizeof(*k)); 228 krb5_data_free(&(*id)->data); 229 } 230 } 231 232 static const char * 233 kcm_get_name(krb5_context context, 234 krb5_ccache id) 235 { 236 return CACHENAME(id); 237 } 238 239 static krb5_error_code 240 kcm_resolve(krb5_context context, krb5_ccache *id, const char *res) 241 { 242 return kcm_alloc(context, res, id); 243 } 244 245 /* 246 * Request: 247 * 248 * Response: 249 * NameZ 250 */ 251 static krb5_error_code 252 kcm_gen_new(krb5_context context, krb5_ccache *id) 253 { 254 krb5_kcmcache *k; 255 krb5_error_code ret; 256 krb5_storage *request, *response; 257 krb5_data response_data; 258 259 ret = kcm_alloc(context, NULL, id); 260 if (ret) 261 return ret; 262 263 k = KCMCACHE(*id); 264 265 ret = krb5_kcm_storage_request(context, KCM_OP_GEN_NEW, &request); 266 if (ret) { 267 kcm_free(context, id); 268 return ret; 269 } 270 271 ret = krb5_kcm_call(context, request, &response, &response_data); 272 if (ret) { 273 krb5_storage_free(request); 274 kcm_free(context, id); 275 return ret; 276 } 277 278 ret = krb5_ret_stringz(response, &k->name); 279 if (ret) 280 ret = KRB5_CC_IO; 281 282 krb5_storage_free(request); 283 krb5_storage_free(response); 284 krb5_data_free(&response_data); 285 286 if (ret) 287 kcm_free(context, id); 288 289 return ret; 290 } 291 292 /* 293 * Request: 294 * NameZ 295 * Principal 296 * 297 * Response: 298 * 299 */ 300 static krb5_error_code 301 kcm_initialize(krb5_context context, 302 krb5_ccache id, 303 krb5_principal primary_principal) 304 { 305 krb5_error_code ret; 306 krb5_kcmcache *k = KCMCACHE(id); 307 krb5_storage *request; 308 309 ret = krb5_kcm_storage_request(context, KCM_OP_INITIALIZE, &request); 310 if (ret) 311 return ret; 312 313 ret = krb5_store_stringz(request, k->name); 314 if (ret) { 315 krb5_storage_free(request); 316 return ret; 317 } 318 319 ret = krb5_store_principal(request, primary_principal); 320 if (ret) { 321 krb5_storage_free(request); 322 return ret; 323 } 324 325 ret = krb5_kcm_call(context, request, NULL, NULL); 326 327 krb5_storage_free(request); 328 329 if (context->kdc_sec_offset) 330 kcm_set_kdc_offset(context, id, context->kdc_sec_offset); 331 332 return ret; 333 } 334 335 static krb5_error_code 336 kcm_close(krb5_context context, 337 krb5_ccache id) 338 { 339 kcm_free(context, &id); 340 return 0; 341 } 342 343 /* 344 * Request: 345 * NameZ 346 * 347 * Response: 348 * 349 */ 350 static krb5_error_code 351 kcm_destroy(krb5_context context, 352 krb5_ccache id) 353 { 354 krb5_error_code ret; 355 krb5_kcmcache *k = KCMCACHE(id); 356 krb5_storage *request; 357 358 ret = krb5_kcm_storage_request(context, KCM_OP_DESTROY, &request); 359 if (ret) 360 return ret; 361 362 ret = krb5_store_stringz(request, k->name); 363 if (ret) { 364 krb5_storage_free(request); 365 return ret; 366 } 367 368 ret = krb5_kcm_call(context, request, NULL, NULL); 369 370 krb5_storage_free(request); 371 return ret; 372 } 373 374 /* 375 * Request: 376 * NameZ 377 * Creds 378 * 379 * Response: 380 * 381 */ 382 static krb5_error_code 383 kcm_store_cred(krb5_context context, 384 krb5_ccache id, 385 krb5_creds *creds) 386 { 387 krb5_error_code ret; 388 krb5_kcmcache *k = KCMCACHE(id); 389 krb5_storage *request; 390 391 ret = krb5_kcm_storage_request(context, KCM_OP_STORE, &request); 392 if (ret) 393 return ret; 394 395 ret = krb5_store_stringz(request, k->name); 396 if (ret) { 397 krb5_storage_free(request); 398 return ret; 399 } 400 401 ret = krb5_store_creds(request, creds); 402 if (ret) { 403 krb5_storage_free(request); 404 return ret; 405 } 406 407 ret = krb5_kcm_call(context, request, NULL, NULL); 408 409 krb5_storage_free(request); 410 return ret; 411 } 412 413 #if 0 414 /* 415 * Request: 416 * NameZ 417 * WhichFields 418 * MatchCreds 419 * 420 * Response: 421 * Creds 422 * 423 */ 424 static krb5_error_code 425 kcm_retrieve(krb5_context context, 426 krb5_ccache id, 427 krb5_flags which, 428 const krb5_creds *mcred, 429 krb5_creds *creds) 430 { 431 krb5_error_code ret; 432 krb5_kcmcache *k = KCMCACHE(id); 433 krb5_storage *request, *response; 434 krb5_data response_data; 435 436 ret = krb5_kcm_storage_request(context, KCM_OP_RETRIEVE, &request); 437 if (ret) 438 return ret; 439 440 ret = krb5_store_stringz(request, k->name); 441 if (ret) { 442 krb5_storage_free(request); 443 return ret; 444 } 445 446 ret = krb5_store_int32(request, which); 447 if (ret) { 448 krb5_storage_free(request); 449 return ret; 450 } 451 452 ret = krb5_store_creds_tag(request, rk_UNCONST(mcred)); 453 if (ret) { 454 krb5_storage_free(request); 455 return ret; 456 } 457 458 ret = krb5_kcm_call(context, request, &response, &response_data); 459 if (ret) { 460 krb5_storage_free(request); 461 return ret; 462 } 463 464 ret = krb5_ret_creds(response, creds); 465 if (ret) 466 ret = KRB5_CC_IO; 467 468 krb5_storage_free(request); 469 krb5_storage_free(response); 470 krb5_data_free(&response_data); 471 472 return ret; 473 } 474 #endif 475 476 /* 477 * Request: 478 * NameZ 479 * 480 * Response: 481 * Principal 482 */ 483 static krb5_error_code 484 kcm_get_principal(krb5_context context, 485 krb5_ccache id, 486 krb5_principal *principal) 487 { 488 krb5_error_code ret; 489 krb5_kcmcache *k = KCMCACHE(id); 490 krb5_storage *request, *response; 491 krb5_data response_data; 492 493 ret = krb5_kcm_storage_request(context, KCM_OP_GET_PRINCIPAL, &request); 494 if (ret) 495 return ret; 496 497 ret = krb5_store_stringz(request, k->name); 498 if (ret) { 499 krb5_storage_free(request); 500 return ret; 501 } 502 503 ret = krb5_kcm_call(context, request, &response, &response_data); 504 if (ret) { 505 krb5_storage_free(request); 506 return ret; 507 } 508 509 ret = krb5_ret_principal(response, principal); 510 if (ret) 511 ret = KRB5_CC_IO; 512 513 krb5_storage_free(request); 514 krb5_storage_free(response); 515 krb5_data_free(&response_data); 516 517 return ret; 518 } 519 520 /* 521 * Request: 522 * NameZ 523 * 524 * Response: 525 * Cursor 526 * 527 */ 528 static krb5_error_code 529 kcm_get_first (krb5_context context, 530 krb5_ccache id, 531 krb5_cc_cursor *cursor) 532 { 533 krb5_error_code ret; 534 krb5_kcm_cursor c; 535 krb5_kcmcache *k = KCMCACHE(id); 536 krb5_storage *request, *response; 537 krb5_data response_data; 538 539 ret = krb5_kcm_storage_request(context, KCM_OP_GET_CRED_UUID_LIST, &request); 540 if (ret) 541 return ret; 542 543 ret = krb5_store_stringz(request, k->name); 544 if (ret) { 545 krb5_storage_free(request); 546 return ret; 547 } 548 549 ret = krb5_kcm_call(context, request, &response, &response_data); 550 krb5_storage_free(request); 551 if (ret) 552 return ret; 553 554 c = calloc(1, sizeof(*c)); 555 if (c == NULL) { 556 ret = ENOMEM; 557 krb5_set_error_message(context, ret, 558 N_("malloc: out of memory", "")); 559 return ret; 560 } 561 562 while (1) { 563 ssize_t sret; 564 kcmuuid_t uuid; 565 void *ptr; 566 567 sret = krb5_storage_read(response, &uuid, sizeof(uuid)); 568 if (sret == 0) { 569 ret = 0; 570 break; 571 } else if (sret != sizeof(uuid)) { 572 ret = EINVAL; 573 break; 574 } 575 576 ptr = realloc(c->uuids, sizeof(c->uuids[0]) * (c->length + 1)); 577 if (ptr == NULL) { 578 free(c->uuids); 579 free(c); 580 krb5_set_error_message(context, ENOMEM, 581 N_("malloc: out of memory", "")); 582 return ENOMEM; 583 } 584 c->uuids = ptr; 585 586 memcpy(&c->uuids[c->length], &uuid, sizeof(uuid)); 587 c->length += 1; 588 } 589 590 krb5_storage_free(response); 591 krb5_data_free(&response_data); 592 593 if (ret) { 594 free(c->uuids); 595 free(c); 596 return ret; 597 } 598 599 *cursor = c; 600 601 return 0; 602 } 603 604 /* 605 * Request: 606 * NameZ 607 * Cursor 608 * 609 * Response: 610 * Creds 611 */ 612 static krb5_error_code 613 kcm_get_next (krb5_context context, 614 krb5_ccache id, 615 krb5_cc_cursor *cursor, 616 krb5_creds *creds) 617 { 618 krb5_error_code ret; 619 krb5_kcmcache *k = KCMCACHE(id); 620 krb5_kcm_cursor c = KCMCURSOR(*cursor); 621 krb5_storage *request, *response; 622 krb5_data response_data; 623 ssize_t sret; 624 625 again: 626 627 if (c->offset >= c->length) 628 return KRB5_CC_END; 629 630 ret = krb5_kcm_storage_request(context, KCM_OP_GET_CRED_BY_UUID, &request); 631 if (ret) 632 return ret; 633 634 ret = krb5_store_stringz(request, k->name); 635 if (ret) { 636 krb5_storage_free(request); 637 return ret; 638 } 639 640 sret = krb5_storage_write(request, 641 &c->uuids[c->offset], 642 sizeof(c->uuids[c->offset])); 643 c->offset++; 644 if (sret != sizeof(c->uuids[c->offset])) { 645 krb5_storage_free(request); 646 krb5_clear_error_message(context); 647 return ENOMEM; 648 } 649 650 ret = krb5_kcm_call(context, request, &response, &response_data); 651 krb5_storage_free(request); 652 if (ret == KRB5_CC_END) { 653 goto again; 654 } 655 656 ret = krb5_ret_creds(response, creds); 657 if (ret) 658 ret = KRB5_CC_IO; 659 660 krb5_storage_free(response); 661 krb5_data_free(&response_data); 662 663 return ret; 664 } 665 666 /* 667 * Request: 668 * NameZ 669 * Cursor 670 * 671 * Response: 672 * 673 */ 674 static krb5_error_code 675 kcm_end_get (krb5_context context, 676 krb5_ccache id, 677 krb5_cc_cursor *cursor) 678 { 679 krb5_kcm_cursor c = KCMCURSOR(*cursor); 680 681 free(c->uuids); 682 free(c); 683 684 *cursor = NULL; 685 686 return 0; 687 } 688 689 /* 690 * Request: 691 * NameZ 692 * WhichFields 693 * MatchCreds 694 * 695 * Response: 696 * 697 */ 698 static krb5_error_code 699 kcm_remove_cred(krb5_context context, 700 krb5_ccache id, 701 krb5_flags which, 702 krb5_creds *cred) 703 { 704 krb5_error_code ret; 705 krb5_kcmcache *k = KCMCACHE(id); 706 krb5_storage *request; 707 708 ret = krb5_kcm_storage_request(context, KCM_OP_REMOVE_CRED, &request); 709 if (ret) 710 return ret; 711 712 ret = krb5_store_stringz(request, k->name); 713 if (ret) { 714 krb5_storage_free(request); 715 return ret; 716 } 717 718 ret = krb5_store_int32(request, which); 719 if (ret) { 720 krb5_storage_free(request); 721 return ret; 722 } 723 724 ret = krb5_store_creds_tag(request, cred); 725 if (ret) { 726 krb5_storage_free(request); 727 return ret; 728 } 729 730 ret = krb5_kcm_call(context, request, NULL, NULL); 731 732 krb5_storage_free(request); 733 return ret; 734 } 735 736 static krb5_error_code 737 kcm_set_flags(krb5_context context, 738 krb5_ccache id, 739 krb5_flags flags) 740 { 741 krb5_error_code ret; 742 krb5_kcmcache *k = KCMCACHE(id); 743 krb5_storage *request; 744 745 ret = krb5_kcm_storage_request(context, KCM_OP_SET_FLAGS, &request); 746 if (ret) 747 return ret; 748 749 ret = krb5_store_stringz(request, k->name); 750 if (ret) { 751 krb5_storage_free(request); 752 return ret; 753 } 754 755 ret = krb5_store_int32(request, flags); 756 if (ret) { 757 krb5_storage_free(request); 758 return ret; 759 } 760 761 ret = krb5_kcm_call(context, request, NULL, NULL); 762 763 krb5_storage_free(request); 764 return ret; 765 } 766 767 static int 768 kcm_get_version(krb5_context context, 769 krb5_ccache id) 770 { 771 return 0; 772 } 773 774 /* 775 * Send nothing 776 * get back list of uuids 777 */ 778 779 static krb5_error_code 780 kcm_get_cache_first(krb5_context context, krb5_cc_cursor *cursor) 781 { 782 krb5_error_code ret; 783 krb5_kcm_cursor c; 784 krb5_storage *request, *response; 785 krb5_data response_data; 786 787 *cursor = NULL; 788 789 c = calloc(1, sizeof(*c)); 790 if (c == NULL) { 791 ret = ENOMEM; 792 krb5_set_error_message(context, ret, 793 N_("malloc: out of memory", "")); 794 goto out; 795 } 796 797 ret = krb5_kcm_storage_request(context, KCM_OP_GET_CACHE_UUID_LIST, &request); 798 if (ret) 799 goto out; 800 801 ret = krb5_kcm_call(context, request, &response, &response_data); 802 krb5_storage_free(request); 803 if (ret) 804 goto out; 805 806 while (1) { 807 ssize_t sret; 808 kcmuuid_t uuid; 809 void *ptr; 810 811 sret = krb5_storage_read(response, &uuid, sizeof(uuid)); 812 if (sret == 0) { 813 ret = 0; 814 break; 815 } else if (sret != sizeof(uuid)) { 816 ret = EINVAL; 817 goto out; 818 } 819 820 ptr = realloc(c->uuids, sizeof(c->uuids[0]) * (c->length + 1)); 821 if (ptr == NULL) { 822 ret = ENOMEM; 823 krb5_set_error_message(context, ret, 824 N_("malloc: out of memory", "")); 825 goto out; 826 } 827 c->uuids = ptr; 828 829 memcpy(&c->uuids[c->length], &uuid, sizeof(uuid)); 830 c->length += 1; 831 } 832 833 krb5_storage_free(response); 834 krb5_data_free(&response_data); 835 836 out: 837 if (ret && c) { 838 free(c->uuids); 839 free(c); 840 } else 841 *cursor = c; 842 843 return ret; 844 } 845 846 /* 847 * Send uuid 848 * Recv cache name 849 */ 850 851 static krb5_error_code 852 kcm_get_cache_next(krb5_context context, krb5_cc_cursor cursor, const krb5_cc_ops *ops, krb5_ccache *id) 853 { 854 krb5_error_code ret; 855 krb5_kcm_cursor c = KCMCURSOR(cursor); 856 krb5_storage *request, *response; 857 krb5_data response_data; 858 ssize_t sret; 859 char *name; 860 861 *id = NULL; 862 863 again: 864 865 if (c->offset >= c->length) 866 return KRB5_CC_END; 867 868 ret = krb5_kcm_storage_request(context, KCM_OP_GET_CACHE_BY_UUID, &request); 869 if (ret) 870 return ret; 871 872 sret = krb5_storage_write(request, 873 &c->uuids[c->offset], 874 sizeof(c->uuids[c->offset])); 875 c->offset++; 876 if (sret != sizeof(c->uuids[c->offset])) { 877 krb5_storage_free(request); 878 krb5_clear_error_message(context); 879 return ENOMEM; 880 } 881 882 ret = krb5_kcm_call(context, request, &response, &response_data); 883 krb5_storage_free(request); 884 if (ret == KRB5_CC_END) 885 goto again; 886 887 ret = krb5_ret_stringz(response, &name); 888 krb5_storage_free(response); 889 krb5_data_free(&response_data); 890 891 if (ret == 0) { 892 ret = _krb5_cc_allocate(context, ops, id); 893 if (ret == 0) 894 ret = kcm_alloc(context, name, id); 895 krb5_xfree(name); 896 } 897 898 return ret; 899 } 900 901 static krb5_error_code 902 kcm_get_cache_next_kcm(krb5_context context, krb5_cc_cursor cursor, krb5_ccache *id) 903 { 904 #ifndef KCM_IS_API_CACHE 905 return kcm_get_cache_next(context, cursor, &krb5_kcm_ops, id); 906 #else 907 return KRB5_CC_END; 908 #endif 909 } 910 911 static krb5_error_code 912 kcm_get_cache_next_api(krb5_context context, krb5_cc_cursor cursor, krb5_ccache *id) 913 { 914 return kcm_get_cache_next(context, cursor, &krb5_akcm_ops, id); 915 } 916 917 918 static krb5_error_code 919 kcm_end_cache_get(krb5_context context, krb5_cc_cursor cursor) 920 { 921 krb5_kcm_cursor c = KCMCURSOR(cursor); 922 923 free(c->uuids); 924 free(c); 925 return 0; 926 } 927 928 929 static krb5_error_code 930 kcm_move(krb5_context context, krb5_ccache from, krb5_ccache to) 931 { 932 krb5_error_code ret; 933 krb5_kcmcache *oldk = KCMCACHE(from); 934 krb5_kcmcache *newk = KCMCACHE(to); 935 krb5_storage *request; 936 937 ret = krb5_kcm_storage_request(context, KCM_OP_MOVE_CACHE, &request); 938 if (ret) 939 return ret; 940 941 ret = krb5_store_stringz(request, oldk->name); 942 if (ret) { 943 krb5_storage_free(request); 944 return ret; 945 } 946 947 ret = krb5_store_stringz(request, newk->name); 948 if (ret) { 949 krb5_storage_free(request); 950 return ret; 951 } 952 ret = krb5_kcm_call(context, request, NULL, NULL); 953 954 krb5_storage_free(request); 955 return ret; 956 } 957 958 static krb5_error_code 959 kcm_get_default_name(krb5_context context, const krb5_cc_ops *ops, 960 const char *defstr, char **str) 961 { 962 krb5_error_code ret; 963 krb5_storage *request, *response; 964 krb5_data response_data; 965 char *name; 966 967 *str = NULL; 968 969 ret = krb5_kcm_storage_request(context, KCM_OP_GET_DEFAULT_CACHE, &request); 970 if (ret) 971 return ret; 972 973 ret = krb5_kcm_call(context, request, &response, &response_data); 974 krb5_storage_free(request); 975 if (ret) 976 return _krb5_expand_default_cc_name(context, defstr, str); 977 978 ret = krb5_ret_stringz(response, &name); 979 krb5_storage_free(response); 980 krb5_data_free(&response_data); 981 if (ret) 982 return ret; 983 984 asprintf(str, "%s:%s", ops->prefix, name); 985 free(name); 986 if (str == NULL) 987 return ENOMEM; 988 989 return 0; 990 } 991 992 static krb5_error_code 993 kcm_get_default_name_api(krb5_context context, char **str) 994 { 995 return kcm_get_default_name(context, &krb5_akcm_ops, 996 KRB5_DEFAULT_CCNAME_KCM_API, str); 997 } 998 999 static krb5_error_code 1000 kcm_get_default_name_kcm(krb5_context context, char **str) 1001 { 1002 return kcm_get_default_name(context, &krb5_kcm_ops, 1003 KRB5_DEFAULT_CCNAME_KCM_KCM, str); 1004 } 1005 1006 static krb5_error_code 1007 kcm_set_default(krb5_context context, krb5_ccache id) 1008 { 1009 krb5_error_code ret; 1010 krb5_storage *request; 1011 krb5_kcmcache *k = KCMCACHE(id); 1012 1013 ret = krb5_kcm_storage_request(context, KCM_OP_SET_DEFAULT_CACHE, &request); 1014 if (ret) 1015 return ret; 1016 1017 ret = krb5_store_stringz(request, k->name); 1018 if (ret) { 1019 krb5_storage_free(request); 1020 return ret; 1021 } 1022 1023 ret = krb5_kcm_call(context, request, NULL, NULL); 1024 krb5_storage_free(request); 1025 1026 return ret; 1027 } 1028 1029 static krb5_error_code 1030 kcm_lastchange(krb5_context context, krb5_ccache id, krb5_timestamp *mtime) 1031 { 1032 *mtime = time(NULL); 1033 return 0; 1034 } 1035 1036 static krb5_error_code 1037 kcm_set_kdc_offset(krb5_context context, krb5_ccache id, krb5_deltat kdc_offset) 1038 { 1039 krb5_kcmcache *k = KCMCACHE(id); 1040 krb5_error_code ret; 1041 krb5_storage *request; 1042 1043 ret = krb5_kcm_storage_request(context, KCM_OP_SET_KDC_OFFSET, &request); 1044 if (ret) 1045 return ret; 1046 1047 ret = krb5_store_stringz(request, k->name); 1048 if (ret) { 1049 krb5_storage_free(request); 1050 return ret; 1051 } 1052 ret = krb5_store_int32(request, kdc_offset); 1053 if (ret) { 1054 krb5_storage_free(request); 1055 return ret; 1056 } 1057 1058 ret = krb5_kcm_call(context, request, NULL, NULL); 1059 krb5_storage_free(request); 1060 1061 return ret; 1062 } 1063 1064 static krb5_error_code 1065 kcm_get_kdc_offset(krb5_context context, krb5_ccache id, krb5_deltat *kdc_offset) 1066 { 1067 krb5_kcmcache *k = KCMCACHE(id); 1068 krb5_error_code ret; 1069 krb5_storage *request, *response; 1070 krb5_data response_data; 1071 int32_t offset; 1072 1073 ret = krb5_kcm_storage_request(context, KCM_OP_GET_KDC_OFFSET, &request); 1074 if (ret) 1075 return ret; 1076 1077 ret = krb5_store_stringz(request, k->name); 1078 if (ret) { 1079 krb5_storage_free(request); 1080 return ret; 1081 } 1082 1083 ret = krb5_kcm_call(context, request, &response, &response_data); 1084 krb5_storage_free(request); 1085 if (ret) 1086 return ret; 1087 1088 ret = krb5_ret_int32(response, &offset); 1089 krb5_storage_free(response); 1090 krb5_data_free(&response_data); 1091 if (ret) 1092 return ret; 1093 1094 *kdc_offset = offset; 1095 1096 return 0; 1097 } 1098 1099 /** 1100 * Variable containing the KCM based credential cache implemention. 1101 * 1102 * @ingroup krb5_ccache 1103 */ 1104 1105 KRB5_LIB_VARIABLE const krb5_cc_ops krb5_kcm_ops = { 1106 KRB5_CC_OPS_VERSION, 1107 "KCM", 1108 kcm_get_name, 1109 kcm_resolve, 1110 kcm_gen_new, 1111 kcm_initialize, 1112 kcm_destroy, 1113 kcm_close, 1114 kcm_store_cred, 1115 NULL /* kcm_retrieve */, 1116 kcm_get_principal, 1117 kcm_get_first, 1118 kcm_get_next, 1119 kcm_end_get, 1120 kcm_remove_cred, 1121 kcm_set_flags, 1122 kcm_get_version, 1123 kcm_get_cache_first, 1124 kcm_get_cache_next_kcm, 1125 kcm_end_cache_get, 1126 kcm_move, 1127 kcm_get_default_name_kcm, 1128 kcm_set_default, 1129 kcm_lastchange, 1130 kcm_set_kdc_offset, 1131 kcm_get_kdc_offset 1132 }; 1133 1134 KRB5_LIB_VARIABLE const krb5_cc_ops krb5_akcm_ops = { 1135 KRB5_CC_OPS_VERSION, 1136 "API", 1137 kcm_get_name, 1138 kcm_resolve, 1139 kcm_gen_new, 1140 kcm_initialize, 1141 kcm_destroy, 1142 kcm_close, 1143 kcm_store_cred, 1144 NULL /* kcm_retrieve */, 1145 kcm_get_principal, 1146 kcm_get_first, 1147 kcm_get_next, 1148 kcm_end_get, 1149 kcm_remove_cred, 1150 kcm_set_flags, 1151 kcm_get_version, 1152 kcm_get_cache_first, 1153 kcm_get_cache_next_api, 1154 kcm_end_cache_get, 1155 kcm_move, 1156 kcm_get_default_name_api, 1157 kcm_set_default, 1158 kcm_lastchange, 1159 NULL, 1160 NULL 1161 }; 1162 1163 1164 KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL 1165 _krb5_kcm_is_running(krb5_context context) 1166 { 1167 krb5_error_code ret; 1168 krb5_ccache_data ccdata; 1169 krb5_ccache id = &ccdata; 1170 krb5_boolean running; 1171 1172 ret = kcm_alloc(context, NULL, &id); 1173 if (ret) 1174 return 0; 1175 1176 running = (_krb5_kcm_noop(context, id) == 0); 1177 1178 kcm_free(context, &id); 1179 1180 return running; 1181 } 1182 1183 /* 1184 * Request: 1185 * 1186 * Response: 1187 * 1188 */ 1189 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 1190 _krb5_kcm_noop(krb5_context context, 1191 krb5_ccache id) 1192 { 1193 krb5_error_code ret; 1194 krb5_storage *request; 1195 1196 ret = krb5_kcm_storage_request(context, KCM_OP_NOOP, &request); 1197 if (ret) 1198 return ret; 1199 1200 ret = krb5_kcm_call(context, request, NULL, NULL); 1201 1202 krb5_storage_free(request); 1203 return ret; 1204 } 1205 1206 1207 /* 1208 * Request: 1209 * NameZ 1210 * ServerPrincipalPresent 1211 * ServerPrincipal OPTIONAL 1212 * Key 1213 * 1214 * Repsonse: 1215 * 1216 */ 1217 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 1218 _krb5_kcm_get_initial_ticket(krb5_context context, 1219 krb5_ccache id, 1220 krb5_principal server, 1221 krb5_keyblock *key) 1222 { 1223 krb5_kcmcache *k = KCMCACHE(id); 1224 krb5_error_code ret; 1225 krb5_storage *request; 1226 1227 ret = krb5_kcm_storage_request(context, KCM_OP_GET_INITIAL_TICKET, &request); 1228 if (ret) 1229 return ret; 1230 1231 ret = krb5_store_stringz(request, k->name); 1232 if (ret) { 1233 krb5_storage_free(request); 1234 return ret; 1235 } 1236 1237 ret = krb5_store_int8(request, (server == NULL) ? 0 : 1); 1238 if (ret) { 1239 krb5_storage_free(request); 1240 return ret; 1241 } 1242 1243 if (server != NULL) { 1244 ret = krb5_store_principal(request, server); 1245 if (ret) { 1246 krb5_storage_free(request); 1247 return ret; 1248 } 1249 } 1250 1251 ret = krb5_store_keyblock(request, *key); 1252 if (ret) { 1253 krb5_storage_free(request); 1254 return ret; 1255 } 1256 1257 ret = krb5_kcm_call(context, request, NULL, NULL); 1258 1259 krb5_storage_free(request); 1260 return ret; 1261 } 1262 1263 1264 /* 1265 * Request: 1266 * NameZ 1267 * KDCFlags 1268 * EncryptionType 1269 * ServerPrincipal 1270 * 1271 * Repsonse: 1272 * 1273 */ 1274 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 1275 _krb5_kcm_get_ticket(krb5_context context, 1276 krb5_ccache id, 1277 krb5_kdc_flags flags, 1278 krb5_enctype enctype, 1279 krb5_principal server) 1280 { 1281 krb5_error_code ret; 1282 krb5_kcmcache *k = KCMCACHE(id); 1283 krb5_storage *request; 1284 1285 ret = krb5_kcm_storage_request(context, KCM_OP_GET_TICKET, &request); 1286 if (ret) 1287 return ret; 1288 1289 ret = krb5_store_stringz(request, k->name); 1290 if (ret) { 1291 krb5_storage_free(request); 1292 return ret; 1293 } 1294 1295 ret = krb5_store_int32(request, flags.i); 1296 if (ret) { 1297 krb5_storage_free(request); 1298 return ret; 1299 } 1300 1301 ret = krb5_store_int32(request, enctype); 1302 if (ret) { 1303 krb5_storage_free(request); 1304 return ret; 1305 } 1306 1307 ret = krb5_store_principal(request, server); 1308 if (ret) { 1309 krb5_storage_free(request); 1310 return ret; 1311 } 1312 1313 ret = krb5_kcm_call(context, request, NULL, NULL); 1314 1315 krb5_storage_free(request); 1316 return ret; 1317 } 1318 1319 #endif /* HAVE_KCM */ 1320