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