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 45static krb5_error_code 46kcm_set_kdc_offset(krb5_context context, krb5_ccache id, krb5_deltat kdc_offset); 47static krb5_error_code 48kcm_get_kdc_offset(krb5_context context, krb5_ccache id, krb5_deltat *kdc_offset); 49 50 51static const char *kcm_ipc_name = "ANY:org.h5l.kcm"; 52 53typedef struct krb5_kcmcache { 54 char *name; 55 krb5_uuid uuid; 56} krb5_kcmcache; 57 58typedef struct krb5_kcm_cursor { 59 unsigned long offset; 60 unsigned long length; 61 kcmuuid_t *uuids; 62} *krb5_kcm_cursor; 63 64 65#define KCMCACHE(X) ((krb5_kcmcache *)(X)->data.data) 66#define CACHENAME(X) (KCMCACHE(X)->name) 67#define KCMCURSOR(C) ((krb5_kcm_cursor)(C)) 68 69static heim_ipc kcm_ipc = NULL; 70 71static void 72kcm_init_ipc(void *ctx) 73{ 74 heim_ipc_init_context(kcm_ipc_name, &kcm_ipc); 75} 76 77static krb5_error_code 78kcm_send_request(krb5_context context, 79 krb5_storage *request, 80 krb5_data *response_data) 81{ 82 static heim_base_once_t init_once = HEIM_BASE_ONCE_INIT; 83 krb5_error_code ret = 0; 84 krb5_data request_data; 85 86 heim_base_once_f(&init_once, NULL, kcm_init_ipc); 87 88 if (kcm_ipc == NULL) { 89 krb5_set_error_message(context, ENOENT, "Failed to open kcm init"); 90 return ENOENT; 91 } 92 93 ret = krb5_storage_to_data(request, &request_data); 94 if (ret) { 95 krb5_clear_error_message(context); 96 return KRB5_CC_NOMEM; 97 } 98 99 ret = heim_ipc_call(kcm_ipc, &request_data, response_data, NULL); 100 krb5_data_free(&request_data); 101 102 if (ret) { 103 krb5_clear_error_message(context); 104 ret = KRB5_CC_NOSUPP; 105 } 106 107 return ret; 108} 109 110KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 111krb5_kcm_storage_request(krb5_context context, 112 uint16_t opcode, 113 krb5_storage **storage_p) 114{ 115 krb5_storage *sp; 116 krb5_error_code ret; 117 118 *storage_p = NULL; 119 120 sp = krb5_storage_emem(); 121 if (sp == NULL) { 122 krb5_set_error_message(context, KRB5_CC_NOMEM, N_("malloc: out of memory", "")); 123 return KRB5_CC_NOMEM; 124 } 125 126 /* Send MAJOR | VERSION | OPCODE */ 127 ret = krb5_store_int8(sp, KCM_PROTOCOL_VERSION_MAJOR); 128 if (ret) 129 goto fail; 130 ret = krb5_store_int8(sp, KCM_PROTOCOL_VERSION_MINOR); 131 if (ret) 132 goto fail; 133 ret = krb5_store_int16(sp, opcode); 134 if (ret) 135 goto fail; 136 137 *storage_p = sp; 138 fail: 139 if (ret) { 140 krb5_set_error_message(context, ret, 141 N_("Failed to encode KCM request", "")); 142 krb5_storage_free(sp); 143 } 144 145 return ret; 146} 147 148static krb5_error_code 149kcm_alloc(krb5_context context, const char *name, krb5_ccache *id) 150{ 151 krb5_kcmcache *k; 152 153 k = malloc(sizeof(*k)); 154 if (k == NULL) { 155 krb5_set_error_message(context, KRB5_CC_NOMEM, 156 N_("malloc: out of memory", "")); 157 return KRB5_CC_NOMEM; 158 } 159 160 if (name != NULL) { 161 k->name = strdup(name); 162 if (k->name == NULL) { 163 free(k); 164 krb5_set_error_message(context, KRB5_CC_NOMEM, 165 N_("malloc: out of memory", "")); 166 return KRB5_CC_NOMEM; 167 } 168 } else 169 k->name = NULL; 170 171 (*id)->data.data = k; 172 (*id)->data.length = sizeof(*k); 173 174 return 0; 175} 176 177KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 178krb5_kcm_call(krb5_context context, 179 krb5_storage *request, 180 krb5_storage **response_p, 181 krb5_data *response_data_p) 182{ 183 krb5_data response_data; 184 krb5_error_code ret; 185 int32_t status; 186 krb5_storage *response; 187 188 if (response_p != NULL) 189 *response_p = NULL; 190 191 krb5_data_zero(&response_data); 192 193 ret = kcm_send_request(context, request, &response_data); 194 if (ret) 195 return ret; 196 197 response = krb5_storage_from_data(&response_data); 198 if (response == NULL) { 199 krb5_data_free(&response_data); 200 return KRB5_CC_IO; 201 } 202 203 ret = krb5_ret_int32(response, &status); 204 if (ret) { 205 krb5_storage_free(response); 206 krb5_data_free(&response_data); 207 return KRB5_CC_FORMAT; 208 } 209 210 if (status) { 211 krb5_storage_free(response); 212 krb5_data_free(&response_data); 213 return status; 214 } 215 216 if (response_p != NULL) { 217 *response_data_p = response_data; 218 *response_p = response; 219 220 return 0; 221 } 222 223 krb5_storage_free(response); 224 krb5_data_free(&response_data); 225 226 return 0; 227} 228 229static void 230kcm_free(krb5_context context, krb5_ccache *id) 231{ 232 krb5_kcmcache *k = KCMCACHE(*id); 233 234 if (k != NULL) { 235 if (k->name != NULL) 236 free(k->name); 237 memset(k, 0, sizeof(*k)); 238 krb5_data_free(&(*id)->data); 239 } 240} 241 242static const char * 243kcm_get_name(krb5_context context, 244 krb5_ccache id) 245{ 246 return CACHENAME(id); 247} 248 249static krb5_error_code 250kcm_resolve(krb5_context context, krb5_ccache *id, const char *res) 251{ 252 return kcm_alloc(context, res, id); 253} 254 255/* 256 * Request: 257 * 258 * Response: 259 * NameZ 260 */ 261static krb5_error_code 262kcm_gen_new(krb5_context context, krb5_ccache *id) 263{ 264 uuid_string_t uuidstr; 265 krb5_error_code ret; 266 krb5_kcmcache *k; 267 uuid_t uuid; 268 269 ret = kcm_alloc(context, NULL, id); 270 if (ret) 271 return ret; 272 273 k = KCMCACHE(*id); 274 275 uuid_generate_random(uuid); 276 uuid_unparse(uuid, uuidstr); 277 278 k->name = strdup(uuidstr); 279 if (k->name == NULL) 280 ret = ENOMEM; 281 282 if (ret) 283 kcm_free(context, id); 284 285 return ret; 286} 287 288/* 289 * Request: 290 * NameZ 291 * Principal 292 * 293 * Response: 294 * 295 */ 296static krb5_error_code 297kcm_initialize(krb5_context context, 298 krb5_ccache id, 299 krb5_principal primary_principal) 300{ 301 krb5_error_code ret; 302 krb5_kcmcache *k = KCMCACHE(id); 303 krb5_storage *request; 304 305 ret = krb5_kcm_storage_request(context, KCM_OP_INITIALIZE, &request); 306 if (ret) 307 return ret; 308 309 ret = krb5_store_stringz(request, k->name); 310 if (ret) { 311 krb5_storage_free(request); 312 return ret; 313 } 314 315 ret = krb5_store_principal(request, primary_principal); 316 if (ret) { 317 krb5_storage_free(request); 318 return ret; 319 } 320 321 ret = krb5_kcm_call(context, request, NULL, NULL); 322 323 krb5_storage_free(request); 324 325 if (context->kdc_sec_offset) 326 kcm_set_kdc_offset(context, id, context->kdc_sec_offset); 327 328 return ret; 329} 330 331static krb5_error_code 332kcm_close(krb5_context context, 333 krb5_ccache id) 334{ 335 kcm_free(context, &id); 336 return 0; 337} 338 339/* 340 * Request: 341 * NameZ 342 * 343 * Response: 344 * 345 */ 346static krb5_error_code 347kcm_destroy(krb5_context context, 348 krb5_ccache id) 349{ 350 krb5_error_code ret; 351 krb5_kcmcache *k = KCMCACHE(id); 352 krb5_storage *request; 353 354 ret = krb5_kcm_storage_request(context, KCM_OP_DESTROY, &request); 355 if (ret) 356 return ret; 357 358 ret = krb5_store_stringz(request, k->name); 359 if (ret) { 360 krb5_storage_free(request); 361 return ret; 362 } 363 364 ret = krb5_kcm_call(context, request, NULL, NULL); 365 366 krb5_storage_free(request); 367 return ret; 368} 369 370/* 371 * Request: 372 * NameZ 373 * Creds 374 * 375 * Response: 376 * 377 */ 378static krb5_error_code 379kcm_store_cred(krb5_context context, 380 krb5_ccache id, 381 krb5_creds *creds) 382{ 383 krb5_error_code ret; 384 krb5_kcmcache *k = KCMCACHE(id); 385 krb5_storage *request; 386 387 ret = krb5_kcm_storage_request(context, KCM_OP_STORE, &request); 388 if (ret) 389 return ret; 390 391 ret = krb5_store_stringz(request, k->name); 392 if (ret) { 393 krb5_storage_free(request); 394 return ret; 395 } 396 397 ret = krb5_store_creds(request, creds); 398 if (ret) { 399 krb5_storage_free(request); 400 return ret; 401 } 402 403 ret = krb5_kcm_call(context, request, NULL, NULL); 404 405 krb5_storage_free(request); 406 return ret; 407} 408 409/* 410 * Request: 411 * NameZ 412 * WhichFields 413 * MatchCreds 414 * 415 * Response: 416 * Creds 417 * 418 */ 419static krb5_error_code 420kcm_retrieve(krb5_context context, 421 krb5_ccache id, 422 krb5_flags which, 423 const krb5_creds *mcred, 424 krb5_creds *creds) 425{ 426 krb5_error_code ret; 427 krb5_kcmcache *k = KCMCACHE(id); 428 krb5_storage *request, *response; 429 krb5_data response_data; 430 431 ret = krb5_kcm_storage_request(context, KCM_OP_RETRIEVE, &request); 432 if (ret) 433 return ret; 434 435 ret = krb5_store_stringz(request, k->name); 436 if (ret) { 437 krb5_storage_free(request); 438 return ret; 439 } 440 441 ret = krb5_store_int32(request, which); 442 if (ret) { 443 krb5_storage_free(request); 444 return ret; 445 } 446 447 ret = krb5_store_creds_tag(request, rk_UNCONST(mcred)); 448 if (ret) { 449 krb5_storage_free(request); 450 return ret; 451 } 452 453 ret = krb5_kcm_call(context, request, &response, &response_data); 454 if (ret) { 455 krb5_storage_free(request); 456 return ret; 457 } 458 459 ret = krb5_ret_creds(response, creds); 460 if (ret) 461 ret = KRB5_CC_IO; 462 463 krb5_storage_free(request); 464 krb5_storage_free(response); 465 krb5_data_free(&response_data); 466 467 return ret; 468} 469 470/* 471 * Request: 472 * NameZ 473 * 474 * Response: 475 * Principal 476 */ 477static krb5_error_code 478kcm_get_principal(krb5_context context, 479 krb5_ccache id, 480 krb5_principal *principal) 481{ 482 krb5_error_code ret; 483 krb5_kcmcache *k = KCMCACHE(id); 484 krb5_storage *request, *response; 485 krb5_data response_data; 486 487 ret = krb5_kcm_storage_request(context, KCM_OP_GET_PRINCIPAL, &request); 488 if (ret) 489 return ret; 490 491 ret = krb5_store_stringz(request, k->name); 492 if (ret) { 493 krb5_storage_free(request); 494 return ret; 495 } 496 497 ret = krb5_kcm_call(context, request, &response, &response_data); 498 if (ret) { 499 krb5_storage_free(request); 500 return ret; 501 } 502 503 ret = krb5_ret_principal(response, principal); 504 if (ret) 505 ret = KRB5_CC_IO; 506 507 krb5_storage_free(request); 508 krb5_storage_free(response); 509 krb5_data_free(&response_data); 510 511 return ret; 512} 513 514/* 515 * Request: 516 * NameZ 517 * 518 * Response: 519 * Cursor 520 * 521 */ 522static krb5_error_code 523kcm_get_first (krb5_context context, 524 krb5_ccache id, 525 krb5_cc_cursor *cursor) 526{ 527 krb5_error_code ret; 528 krb5_kcm_cursor c; 529 krb5_kcmcache *k = KCMCACHE(id); 530 krb5_storage *request, *response; 531 krb5_data response_data; 532 533 ret = krb5_kcm_storage_request(context, KCM_OP_GET_CRED_UUID_LIST, &request); 534 if (ret) 535 return ret; 536 537 ret = krb5_store_stringz(request, k->name); 538 if (ret) { 539 krb5_storage_free(request); 540 return ret; 541 } 542 543 ret = krb5_kcm_call(context, request, &response, &response_data); 544 krb5_storage_free(request); 545 if (ret) 546 return ret; 547 548 c = calloc(1, sizeof(*c)); 549 if (c == NULL) { 550 ret = ENOMEM; 551 krb5_set_error_message(context, ret, 552 N_("malloc: out of memory", "")); 553 return ret; 554 } 555 556 while (1) { 557 ssize_t sret; 558 kcmuuid_t uuid; 559 void *ptr; 560 561 sret = krb5_storage_read(response, &uuid, sizeof(uuid)); 562 if (sret == 0) { 563 ret = 0; 564 break; 565 } else if (sret != sizeof(uuid)) { 566 ret = EINVAL; 567 break; 568 } 569 570 ptr = realloc(c->uuids, sizeof(c->uuids[0]) * (c->length + 1)); 571 if (ptr == NULL) { 572 free(c->uuids); 573 free(c); 574 krb5_set_error_message(context, ENOMEM, 575 N_("malloc: out of memory", "")); 576 return ENOMEM; 577 } 578 c->uuids = ptr; 579 580 memcpy(&c->uuids[c->length], &uuid, sizeof(uuid)); 581 c->length += 1; 582 } 583 584 krb5_storage_free(response); 585 krb5_data_free(&response_data); 586 587 if (ret) { 588 free(c->uuids); 589 free(c); 590 return ret; 591 } 592 593 *cursor = c; 594 595 return 0; 596} 597 598/* 599 * Request: 600 * NameZ 601 * Cursor 602 * 603 * Response: 604 * Creds 605 */ 606static krb5_error_code 607kcm_get_next (krb5_context context, 608 krb5_ccache id, 609 krb5_cc_cursor *cursor, 610 krb5_creds *creds) 611{ 612 krb5_error_code ret; 613 krb5_kcmcache *k = KCMCACHE(id); 614 krb5_kcm_cursor c = KCMCURSOR(*cursor); 615 krb5_storage *request, *response; 616 krb5_data response_data; 617 ssize_t sret; 618 619 again: 620 621 if (c->offset >= c->length) 622 return KRB5_CC_END; 623 624 ret = krb5_kcm_storage_request(context, KCM_OP_GET_CRED_BY_UUID, &request); 625 if (ret) 626 return ret; 627 628 ret = krb5_store_stringz(request, k->name); 629 if (ret) { 630 krb5_storage_free(request); 631 return ret; 632 } 633 634 sret = krb5_storage_write(request, 635 &c->uuids[c->offset], 636 sizeof(c->uuids[c->offset])); 637 c->offset++; 638 if (sret != sizeof(c->uuids[c->offset])) { 639 krb5_storage_free(request); 640 krb5_clear_error_message(context); 641 return ENOMEM; 642 } 643 644 ret = krb5_kcm_call(context, request, &response, &response_data); 645 krb5_storage_free(request); 646 if (ret == KRB5_CC_END) { 647 goto again; 648 } else if (ret) 649 return ret; 650 651 ret = krb5_ret_creds(response, creds); 652 if (ret) 653 ret = KRB5_CC_IO; 654 655 krb5_storage_free(response); 656 krb5_data_free(&response_data); 657 658 return ret; 659} 660 661/* 662 * Request: 663 * NameZ 664 * Cursor 665 * 666 * Response: 667 * 668 */ 669static krb5_error_code 670kcm_end_get (krb5_context context, 671 krb5_ccache id, 672 krb5_cc_cursor *cursor) 673{ 674 krb5_kcm_cursor c = KCMCURSOR(*cursor); 675 676 free(c->uuids); 677 free(c); 678 679 *cursor = NULL; 680 681 return 0; 682} 683 684/* 685 * Request: 686 * NameZ 687 * WhichFields 688 * MatchCreds 689 * 690 * Response: 691 * 692 */ 693static krb5_error_code 694kcm_remove_cred(krb5_context context, 695 krb5_ccache id, 696 krb5_flags which, 697 krb5_creds *cred) 698{ 699 krb5_error_code ret; 700 krb5_kcmcache *k = KCMCACHE(id); 701 krb5_storage *request; 702 703 ret = krb5_kcm_storage_request(context, KCM_OP_REMOVE_CRED, &request); 704 if (ret) 705 return ret; 706 707 ret = krb5_store_stringz(request, k->name); 708 if (ret) { 709 krb5_storage_free(request); 710 return ret; 711 } 712 713 ret = krb5_store_int32(request, which); 714 if (ret) { 715 krb5_storage_free(request); 716 return ret; 717 } 718 719 ret = krb5_store_creds_tag(request, cred); 720 if (ret) { 721 krb5_storage_free(request); 722 return ret; 723 } 724 725 ret = krb5_kcm_call(context, request, NULL, NULL); 726 727 krb5_storage_free(request); 728 return ret; 729} 730 731static krb5_error_code 732kcm_set_flags(krb5_context context, 733 krb5_ccache id, 734 krb5_flags flags) 735{ 736 krb5_error_code ret; 737 krb5_kcmcache *k = KCMCACHE(id); 738 krb5_storage *request; 739 740 ret = krb5_kcm_storage_request(context, KCM_OP_SET_FLAGS, &request); 741 if (ret) 742 return ret; 743 744 ret = krb5_store_stringz(request, k->name); 745 if (ret) { 746 krb5_storage_free(request); 747 return ret; 748 } 749 750 ret = krb5_store_int32(request, flags); 751 if (ret) { 752 krb5_storage_free(request); 753 return ret; 754 } 755 756 ret = krb5_kcm_call(context, request, NULL, NULL); 757 758 krb5_storage_free(request); 759 return ret; 760} 761 762static int 763kcm_get_version(krb5_context context, 764 krb5_ccache id) 765{ 766 return 0; 767} 768 769/* 770 * Send nothing 771 * get back list of uuids 772 */ 773 774static krb5_error_code 775kcm_get_cache_first(krb5_context context, krb5_cc_cursor *cursor) 776{ 777 krb5_error_code ret; 778 krb5_kcm_cursor c; 779 krb5_storage *request, *response; 780 krb5_data response_data; 781 782 *cursor = NULL; 783 784 c = calloc(1, sizeof(*c)); 785 if (c == NULL) { 786 ret = ENOMEM; 787 krb5_set_error_message(context, ret, 788 N_("malloc: out of memory", "")); 789 goto out; 790 } 791 792 ret = krb5_kcm_storage_request(context, KCM_OP_GET_CACHE_UUID_LIST, &request); 793 if (ret) 794 goto out; 795 796 ret = krb5_kcm_call(context, request, &response, &response_data); 797 krb5_storage_free(request); 798 if (ret) 799 goto out; 800 801 while (1) { 802 ssize_t sret; 803 kcmuuid_t uuid; 804 void *ptr; 805 806 sret = krb5_storage_read(response, &uuid, sizeof(uuid)); 807 if (sret == 0) { 808 ret = 0; 809 break; 810 } else if (sret != sizeof(uuid)) { 811 ret = EINVAL; 812 goto out; 813 } 814 815 ptr = realloc(c->uuids, sizeof(c->uuids[0]) * (c->length + 1)); 816 if (ptr == NULL) { 817 ret = ENOMEM; 818 krb5_set_error_message(context, ret, 819 N_("malloc: out of memory", "")); 820 goto out; 821 } 822 c->uuids = ptr; 823 824 memcpy(&c->uuids[c->length], &uuid, sizeof(uuid)); 825 c->length += 1; 826 } 827 828 krb5_storage_free(response); 829 krb5_data_free(&response_data); 830 831 out: 832 if (ret && c) { 833 free(c->uuids); 834 free(c); 835 } else 836 *cursor = c; 837 838 return ret; 839} 840 841/* 842 * Send uuid 843 * Recv cache name 844 */ 845 846static krb5_error_code 847kcm_get_cache_next(krb5_context context, krb5_cc_cursor cursor, const krb5_cc_ops *ops, krb5_ccache *id) 848{ 849 krb5_error_code ret; 850 krb5_kcm_cursor c = KCMCURSOR(cursor); 851 krb5_storage *request, *response; 852 krb5_data response_data; 853 ssize_t sret; 854 char *name; 855 856 *id = NULL; 857 858 again: 859 860 if (c->offset >= c->length) 861 return KRB5_CC_END; 862 863 ret = krb5_kcm_storage_request(context, KCM_OP_GET_CACHE_BY_UUID, &request); 864 if (ret) 865 return ret; 866 867 sret = krb5_storage_write(request, 868 &c->uuids[c->offset], 869 sizeof(c->uuids[c->offset])); 870 c->offset++; 871 if (sret != sizeof(c->uuids[c->offset])) { 872 krb5_storage_free(request); 873 krb5_clear_error_message(context); 874 return ENOMEM; 875 } 876 877 ret = krb5_kcm_call(context, request, &response, &response_data); 878 krb5_storage_free(request); 879 if (ret == KRB5_FCC_NOFILE) { 880 /* cache no longer exists, try next */ 881 goto again; 882 } else if (ret) 883 return ret; 884 885 ret = krb5_ret_stringz(response, &name); 886 krb5_storage_free(response); 887 krb5_data_free(&response_data); 888 if (ret) 889 return ret; 890 891 ret = _krb5_cc_allocate(context, ops, id); 892 if (ret == 0) 893 ret = kcm_alloc(context, name, id); 894 krb5_xfree(name); 895 896 return ret; 897} 898 899static krb5_error_code 900kcm_get_cache_next_kcm(krb5_context context, krb5_cc_cursor cursor, krb5_ccache *id) 901{ 902#ifndef KCM_IS_API_CACHE 903 return kcm_get_cache_next(context, cursor, &krb5_kcm_ops, id); 904#else 905 return KRB5_CC_END; 906#endif 907} 908 909static krb5_error_code 910kcm_get_cache_next_api(krb5_context context, krb5_cc_cursor cursor, krb5_ccache *id) 911{ 912 return kcm_get_cache_next(context, cursor, &krb5_akcm_ops, id); 913} 914 915 916static krb5_error_code 917kcm_end_cache_get(krb5_context context, krb5_cc_cursor cursor) 918{ 919 krb5_kcm_cursor c = KCMCURSOR(cursor); 920 921 free(c->uuids); 922 free(c); 923 return 0; 924} 925 926 927static krb5_error_code 928kcm_move(krb5_context context, krb5_ccache from, krb5_ccache to) 929{ 930 krb5_error_code ret; 931 krb5_kcmcache *oldk = KCMCACHE(from); 932 krb5_kcmcache *newk = KCMCACHE(to); 933 krb5_storage *request; 934 935 ret = krb5_kcm_storage_request(context, KCM_OP_MOVE_CACHE, &request); 936 if (ret) 937 return ret; 938 939 ret = krb5_store_stringz(request, oldk->name); 940 if (ret) { 941 krb5_storage_free(request); 942 return ret; 943 } 944 945 ret = krb5_store_stringz(request, newk->name); 946 if (ret) { 947 krb5_storage_free(request); 948 return ret; 949 } 950 ret = krb5_kcm_call(context, request, NULL, NULL); 951 952 krb5_storage_free(request); 953 return ret; 954} 955 956static krb5_error_code 957kcm_get_default_name(krb5_context context, const krb5_cc_ops *ops, 958 const char *defstr, char **str) 959{ 960 krb5_error_code ret; 961 krb5_storage *request, *response; 962 krb5_data response_data; 963 char *name; 964 965 *str = NULL; 966 967 ret = krb5_kcm_storage_request(context, KCM_OP_GET_DEFAULT_CACHE, &request); 968 if (ret) 969 return ret; 970 971 ret = krb5_kcm_call(context, request, &response, &response_data); 972 krb5_storage_free(request); 973 if (ret) 974 return _krb5_expand_default_cc_name(context, defstr, str); 975 976 ret = krb5_ret_stringz(response, &name); 977 krb5_storage_free(response); 978 krb5_data_free(&response_data); 979 if (ret) 980 return ret; 981 982 asprintf(str, "%s:%s", ops->prefix, name); 983 free(name); 984 if (str == NULL) 985 return ENOMEM; 986 987 return 0; 988} 989 990static krb5_error_code 991kcm_get_default_name_api(krb5_context context, char **str) 992{ 993 return kcm_get_default_name(context, &krb5_akcm_ops, 994 KRB5_DEFAULT_CCNAME_KCM_API, str); 995} 996 997static krb5_error_code 998kcm_get_default_name_kcm(krb5_context context, char **str) 999{ 1000 return kcm_get_default_name(context, &krb5_kcm_ops, 1001 KRB5_DEFAULT_CCNAME_KCM_KCM, str); 1002} 1003 1004static krb5_error_code 1005kcm_set_default(krb5_context context, krb5_ccache id) 1006{ 1007 krb5_error_code ret; 1008 krb5_storage *request; 1009 krb5_kcmcache *k = KCMCACHE(id); 1010 1011 ret = krb5_kcm_storage_request(context, KCM_OP_SET_DEFAULT_CACHE, &request); 1012 if (ret) 1013 return ret; 1014 1015 ret = krb5_store_stringz(request, k->name); 1016 if (ret) { 1017 krb5_storage_free(request); 1018 return ret; 1019 } 1020 1021 ret = krb5_kcm_call(context, request, NULL, NULL); 1022 krb5_storage_free(request); 1023 1024 return ret; 1025} 1026 1027static krb5_error_code 1028kcm_lastchange(krb5_context context, krb5_ccache id, krb5_timestamp *mtime) 1029{ 1030 *mtime = time(NULL); 1031 return 0; 1032} 1033 1034static krb5_error_code 1035kcm_set_kdc_offset(krb5_context context, krb5_ccache id, krb5_deltat kdc_offset) 1036{ 1037 krb5_kcmcache *k = KCMCACHE(id); 1038 krb5_error_code ret; 1039 krb5_storage *request; 1040 1041 ret = krb5_kcm_storage_request(context, KCM_OP_SET_KDC_OFFSET, &request); 1042 if (ret) 1043 return ret; 1044 1045 ret = krb5_store_stringz(request, k->name); 1046 if (ret) { 1047 krb5_storage_free(request); 1048 return ret; 1049 } 1050 ret = krb5_store_int32(request, (uint32_t)kdc_offset); 1051 if (ret) { 1052 krb5_storage_free(request); 1053 return ret; 1054 } 1055 1056 ret = krb5_kcm_call(context, request, NULL, NULL); 1057 krb5_storage_free(request); 1058 1059 return ret; 1060} 1061 1062static krb5_error_code 1063kcm_get_kdc_offset(krb5_context context, krb5_ccache id, krb5_deltat *kdc_offset) 1064{ 1065 krb5_kcmcache *k = KCMCACHE(id); 1066 krb5_error_code ret; 1067 krb5_storage *request, *response; 1068 krb5_data response_data; 1069 int32_t offset; 1070 1071 ret = krb5_kcm_storage_request(context, KCM_OP_GET_KDC_OFFSET, &request); 1072 if (ret) 1073 return ret; 1074 1075 ret = krb5_store_stringz(request, k->name); 1076 if (ret) { 1077 krb5_storage_free(request); 1078 return ret; 1079 } 1080 1081 ret = krb5_kcm_call(context, request, &response, &response_data); 1082 krb5_storage_free(request); 1083 if (ret) 1084 return ret; 1085 1086 ret = krb5_ret_int32(response, &offset); 1087 krb5_storage_free(response); 1088 krb5_data_free(&response_data); 1089 if (ret) 1090 return ret; 1091 1092 *kdc_offset = offset; 1093 1094 return 0; 1095} 1096 1097static krb5_error_code 1098kcm_hold(krb5_context context, krb5_ccache id) 1099{ 1100 krb5_storage *request; 1101 krb5_kcmcache *k = KCMCACHE(id); 1102 krb5_error_code ret; 1103 1104 ret = krb5_kcm_storage_request(context, KCM_OP_RETAIN_KCRED, &request); 1105 if (ret) 1106 return ret; 1107 1108 ret = krb5_store_stringz(request, k->name); 1109 if (ret) { 1110 krb5_storage_free(request); 1111 return ret; 1112 } 1113 1114 ret = krb5_kcm_call(context, request, NULL, NULL); 1115 krb5_storage_free(request); 1116 1117 return ret; 1118} 1119 1120static krb5_error_code 1121kcm_unhold(krb5_context context, krb5_ccache id) 1122{ 1123 krb5_storage *request; 1124 krb5_kcmcache *k = KCMCACHE(id); 1125 krb5_error_code ret; 1126 1127 ret = krb5_kcm_storage_request(context, KCM_OP_RELEASE_KCRED, &request); 1128 if (ret) 1129 return ret; 1130 1131 ret = krb5_store_stringz(request, k->name); 1132 if (ret) { 1133 krb5_storage_free(request); 1134 return ret; 1135 } 1136 1137 ret = krb5_kcm_call(context, request, NULL, NULL); 1138 krb5_storage_free(request); 1139 1140 return ret; 1141} 1142 1143static krb5_error_code 1144kcm_get_uuid(krb5_context context, krb5_ccache id, krb5_uuid uuid) 1145{ 1146 krb5_storage *request, *response; 1147 krb5_kcmcache *k = KCMCACHE(id); 1148 krb5_data response_data; 1149 krb5_error_code ret; 1150 ssize_t sret; 1151 1152 ret = krb5_kcm_storage_request(context, KCM_OP_GET_UUID, &request); 1153 if (ret) 1154 return ret; 1155 1156 ret = krb5_store_stringz(request, k->name); 1157 if (ret) { 1158 krb5_storage_free(request); 1159 return ret; 1160 } 1161 1162 ret = krb5_kcm_call(context, request, &response, &response_data); 1163 krb5_storage_free(request); 1164 if (ret) 1165 return ret; 1166 1167 sret = krb5_storage_read(response, uuid, sizeof(krb5_uuid)); 1168 krb5_storage_free(response); 1169 if (sret != sizeof(krb5_uuid)) 1170 return KRB5_CC_IO; 1171 1172 return 0; 1173} 1174 1175static krb5_error_code 1176resolve_by_uuid_oid(krb5_context context, const krb5_cc_ops *ops, krb5_ccache id, krb5_uuid uuid) 1177{ 1178 krb5_storage *request, *response; 1179 krb5_data response_data; 1180 krb5_error_code ret; 1181 char *name; 1182 ssize_t sret; 1183 1184 ret = krb5_kcm_storage_request(context, KCM_OP_GET_CACHE_BY_UUID, &request); 1185 if (ret) 1186 return ret; 1187 1188 sret = krb5_storage_write(request, uuid, sizeof(krb5_uuid)); 1189 if (sret != sizeof(krb5_uuid)) { 1190 krb5_storage_free(request); 1191 krb5_clear_error_message(context); 1192 return ENOMEM; 1193 } 1194 1195 ret = krb5_kcm_call(context, request, &response, &response_data); 1196 krb5_storage_free(request); 1197 if (ret) 1198 return ret; 1199 1200 ret = krb5_ret_stringz(response, &name); 1201 krb5_storage_free(response); 1202 krb5_data_free(&response_data); 1203 if (ret) 1204 return ret; 1205 1206 ret = kcm_alloc(context, name, &id); 1207 krb5_xfree(name); 1208 1209 return ret; 1210} 1211 1212static krb5_error_code 1213kcm_resolve_by_uuid_oid_kcm(krb5_context context, krb5_ccache id, krb5_uuid uuid) 1214{ 1215 return resolve_by_uuid_oid(context, &krb5_kcm_ops, id, uuid); 1216} 1217 1218static krb5_error_code 1219kcm_resolve_by_uuid_oid_api(krb5_context context, krb5_ccache id, krb5_uuid uuid) 1220{ 1221 return resolve_by_uuid_oid(context, &krb5_akcm_ops, id, uuid); 1222} 1223 1224 1225/** 1226 * Variable containing the KCM based credential cache implemention. 1227 * 1228 * @ingroup krb5_ccache 1229 */ 1230 1231KRB5_LIB_VARIABLE const krb5_cc_ops krb5_kcm_ops = { 1232 KRB5_CC_OPS_VERSION, 1233 "KCM", 1234 kcm_get_name, 1235 kcm_resolve, 1236 kcm_gen_new, 1237 kcm_initialize, 1238 kcm_destroy, 1239 kcm_close, 1240 kcm_store_cred, 1241 kcm_retrieve, 1242 kcm_get_principal, 1243 kcm_get_first, 1244 kcm_get_next, 1245 kcm_end_get, 1246 kcm_remove_cred, 1247 kcm_set_flags, 1248 kcm_get_version, 1249 kcm_get_cache_first, 1250 kcm_get_cache_next_kcm, 1251 kcm_end_cache_get, 1252 kcm_move, 1253 kcm_get_default_name_kcm, 1254 kcm_set_default, 1255 kcm_lastchange, 1256 kcm_set_kdc_offset, 1257 kcm_get_kdc_offset, 1258 kcm_hold, 1259 kcm_unhold, 1260 kcm_get_uuid, 1261 kcm_resolve_by_uuid_oid_kcm 1262}; 1263 1264KRB5_LIB_VARIABLE const krb5_cc_ops krb5_akcm_ops = { 1265 KRB5_CC_OPS_VERSION, 1266 "API", 1267 kcm_get_name, 1268 kcm_resolve, 1269 kcm_gen_new, 1270 kcm_initialize, 1271 kcm_destroy, 1272 kcm_close, 1273 kcm_store_cred, 1274 kcm_retrieve, 1275 kcm_get_principal, 1276 kcm_get_first, 1277 kcm_get_next, 1278 kcm_end_get, 1279 kcm_remove_cred, 1280 kcm_set_flags, 1281 kcm_get_version, 1282 kcm_get_cache_first, 1283 kcm_get_cache_next_api, 1284 kcm_end_cache_get, 1285 kcm_move, 1286 kcm_get_default_name_api, 1287 kcm_set_default, 1288 kcm_lastchange, 1289 kcm_set_kdc_offset, 1290 kcm_get_kdc_offset, 1291 kcm_hold, 1292 kcm_unhold, 1293 kcm_get_uuid, 1294 kcm_resolve_by_uuid_oid_api 1295}; 1296 1297 1298KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL 1299_krb5_kcm_is_running(krb5_context context) 1300{ 1301 krb5_error_code ret; 1302 krb5_ccache_data ccdata; 1303 krb5_ccache id = &ccdata; 1304 krb5_boolean running; 1305 1306 ret = kcm_alloc(context, NULL, &id); 1307 if (ret) 1308 return 0; 1309 1310 running = (_krb5_kcm_noop(context, id) == 0); 1311 1312 kcm_free(context, &id); 1313 1314 return running; 1315} 1316 1317/* 1318 * Request: 1319 * 1320 * Response: 1321 * 1322 */ 1323KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 1324_krb5_kcm_noop(krb5_context context, 1325 krb5_ccache id) 1326{ 1327 krb5_error_code ret; 1328 krb5_storage *request; 1329 1330 ret = krb5_kcm_storage_request(context, KCM_OP_NOOP, &request); 1331 if (ret) 1332 return ret; 1333 1334 ret = krb5_kcm_call(context, request, NULL, NULL); 1335 krb5_storage_free(request); 1336 1337 return ret; 1338} 1339 1340 1341/* 1342 * Request: 1343 * NameZ 1344 * 1345 * Request: 1346 * NameZ 1347 * ClientPrincipal 1348 * ServerPrincipalPresent 1349 * ServerPrincipal OPTIONAL 1350 * Password 1351 * 1352 * Repsonse: 1353 * 1354 */ 1355KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 1356_krb5_kcm_get_initial_ticket(krb5_context context, 1357 krb5_ccache id, 1358 krb5_principal client, 1359 krb5_principal server, 1360 const char *password) 1361{ 1362 krb5_kcmcache *k = KCMCACHE(id); 1363 krb5_error_code ret; 1364 krb5_storage *request; 1365 1366 if (id->ops != &krb5_kcm_ops && id->ops != &krb5_akcm_ops) { 1367 krb5_set_error_message(context, EINVAL, "Cache is not a KCM cache"); 1368 return EINVAL; 1369 } 1370 1371 ret = krb5_kcm_storage_request(context, KCM_OP_GET_INITIAL_TICKET, &request); 1372 if (ret) 1373 return ret; 1374 1375 ret = krb5_store_stringz(request, k->name); 1376 if (ret) { 1377 krb5_storage_free(request); 1378 return ret; 1379 } 1380 1381 ret = krb5_store_principal(request, client); 1382 if (ret) { 1383 krb5_storage_free(request); 1384 return ret; 1385 } 1386 1387 ret = krb5_store_int8(request, (server == NULL) ? 0 : 1); 1388 if (ret) { 1389 krb5_storage_free(request); 1390 return ret; 1391 } 1392 1393 if (server != NULL) { 1394 ret = krb5_store_principal(request, server); 1395 if (ret) { 1396 krb5_storage_free(request); 1397 return ret; 1398 } 1399 } 1400 1401 ret = krb5_store_stringz(request, password); 1402 if (ret) { 1403 krb5_storage_free(request); 1404 return ret; 1405 } 1406 1407 ret = krb5_kcm_call(context, request, NULL, NULL); 1408 krb5_storage_free(request); 1409 1410 return ret; 1411} 1412 1413KRB5_LIB_FUNCTION const char * KRB5_LIB_CALL 1414_krb5_kcm_get_status(int status) 1415{ 1416 const char *msg[] = { 1417 "start", 1418 "success", 1419 "fail", 1420 "stop" 1421 }; 1422 if (status >= 0 && status < sizeof(msg) / sizeof(msg[0])) 1423 return msg[status]; 1424 return "unknown"; 1425} 1426 1427/* 1428 * Request: 1429 * NameZ 1430 * KDCFlags 1431 * EncryptionType 1432 * ServerPrincipal 1433 * 1434 * Repsonse: 1435 * 1436 */ 1437KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 1438_krb5_kcm_get_ticket(krb5_context context, 1439 krb5_ccache id, 1440 krb5_kdc_flags flags, 1441 krb5_enctype enctype, 1442 krb5_principal server) 1443{ 1444 krb5_error_code ret; 1445 krb5_kcmcache *k = KCMCACHE(id); 1446 krb5_storage *request; 1447 1448 ret = krb5_kcm_storage_request(context, KCM_OP_GET_TICKET, &request); 1449 if (ret) 1450 return ret; 1451 1452 ret = krb5_store_stringz(request, k->name); 1453 if (ret) { 1454 krb5_storage_free(request); 1455 return ret; 1456 } 1457 1458 ret = krb5_store_int32(request, flags.i); 1459 if (ret) { 1460 krb5_storage_free(request); 1461 return ret; 1462 } 1463 1464 ret = krb5_store_int32(request, enctype); 1465 if (ret) { 1466 krb5_storage_free(request); 1467 return ret; 1468 } 1469 1470 ret = krb5_store_principal(request, server); 1471 if (ret) { 1472 krb5_storage_free(request); 1473 return ret; 1474 } 1475 1476 ret = krb5_kcm_call(context, request, NULL, NULL); 1477 krb5_storage_free(request); 1478 1479 return ret; 1480} 1481 1482/* 1483 * Request: 1484 * 1485 * Response: 1486 * 1487 */ 1488krb5_error_code 1489_krb5_kcm_ntlm_challenge(krb5_context context, int op __attribute((__unused__)), 1490 uint8_t chal[8]) 1491{ 1492 krb5_error_code ret; 1493 krb5_ssize_t sret; 1494 krb5_storage *request; 1495 1496 ret = krb5_kcm_storage_request(context, KCM_OP_ADD_NTLM_CHALLENGE, &request); 1497 if (ret) 1498 return ret; 1499 1500 sret = krb5_storage_write(request, chal, 8); 1501 if (sret != 8) { 1502 ret = EINVAL; 1503 goto out; 1504 } 1505 1506 ret = krb5_kcm_call(context, request, NULL, NULL); 1507 1508 out: 1509 krb5_storage_free(request); 1510 return ret; 1511} 1512 1513 1514#endif /* HAVE_KCM */ 1515