kcm.c revision 1.1.1.1.4.1
1/* $NetBSD: kcm.c,v 1.1.1.1.4.1 2014/05/22 13:21:28 yamt 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 47static krb5_error_code 48kcm_set_kdc_offset(krb5_context, krb5_ccache, krb5_deltat); 49 50static const char *kcm_ipc_name = "ANY:org.h5l.kcm"; 51 52typedef struct krb5_kcmcache { 53 char *name; 54} krb5_kcmcache; 55 56typedef 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 67static HEIMDAL_MUTEX kcm_mutex = HEIMDAL_MUTEX_INITIALIZER; 68static heim_ipc kcm_ipc = NULL; 69 70static krb5_error_code 71kcm_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 102KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 103krb5_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 140static krb5_error_code 141kcm_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 169KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 170krb5_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 221static void 222kcm_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 234static const char * 235kcm_get_name(krb5_context context, 236 krb5_ccache id) 237{ 238 return CACHENAME(id); 239} 240 241static krb5_error_code 242kcm_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 */ 253static krb5_error_code 254kcm_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 */ 302static krb5_error_code 303kcm_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 337static krb5_error_code 338kcm_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 */ 352static krb5_error_code 353kcm_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 */ 384static krb5_error_code 385kcm_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 */ 426static krb5_error_code 427kcm_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 */ 485static krb5_error_code 486kcm_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 */ 530static krb5_error_code 531kcm_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 = ENOMEM; 559 krb5_set_error_message(context, ret, 560 N_("malloc: out of memory", "")); 561 return ret; 562 } 563 564 while (1) { 565 ssize_t sret; 566 kcmuuid_t uuid; 567 void *ptr; 568 569 sret = krb5_storage_read(response, &uuid, sizeof(uuid)); 570 if (sret == 0) { 571 ret = 0; 572 break; 573 } else if (sret != sizeof(uuid)) { 574 ret = EINVAL; 575 break; 576 } 577 578 ptr = realloc(c->uuids, sizeof(c->uuids[0]) * (c->length + 1)); 579 if (ptr == NULL) { 580 free(c->uuids); 581 free(c); 582 krb5_set_error_message(context, ENOMEM, 583 N_("malloc: out of memory", "")); 584 return ENOMEM; 585 } 586 c->uuids = ptr; 587 588 memcpy(&c->uuids[c->length], &uuid, sizeof(uuid)); 589 c->length += 1; 590 } 591 592 krb5_storage_free(response); 593 krb5_data_free(&response_data); 594 595 if (ret) { 596 free(c->uuids); 597 free(c); 598 return ret; 599 } 600 601 *cursor = c; 602 603 return 0; 604} 605 606/* 607 * Request: 608 * NameZ 609 * Cursor 610 * 611 * Response: 612 * Creds 613 */ 614static krb5_error_code 615kcm_get_next (krb5_context context, 616 krb5_ccache id, 617 krb5_cc_cursor *cursor, 618 krb5_creds *creds) 619{ 620 krb5_error_code ret; 621 krb5_kcmcache *k = KCMCACHE(id); 622 krb5_kcm_cursor c = KCMCURSOR(*cursor); 623 krb5_storage *request, *response; 624 krb5_data response_data; 625 ssize_t sret; 626 627 again: 628 629 if (c->offset >= c->length) 630 return KRB5_CC_END; 631 632 ret = krb5_kcm_storage_request(context, KCM_OP_GET_CRED_BY_UUID, &request); 633 if (ret) 634 return ret; 635 636 ret = krb5_store_stringz(request, k->name); 637 if (ret) { 638 krb5_storage_free(request); 639 return ret; 640 } 641 642 sret = krb5_storage_write(request, 643 &c->uuids[c->offset], 644 sizeof(c->uuids[c->offset])); 645 c->offset++; 646 if (sret != sizeof(c->uuids[c->offset])) { 647 krb5_storage_free(request); 648 krb5_clear_error_message(context); 649 return ENOMEM; 650 } 651 652 ret = krb5_kcm_call(context, request, &response, &response_data); 653 krb5_storage_free(request); 654 if (ret == KRB5_CC_END) { 655 goto again; 656 } 657 658 ret = krb5_ret_creds(response, creds); 659 if (ret) 660 ret = KRB5_CC_IO; 661 662 krb5_storage_free(response); 663 krb5_data_free(&response_data); 664 665 return ret; 666} 667 668/* 669 * Request: 670 * NameZ 671 * Cursor 672 * 673 * Response: 674 * 675 */ 676static krb5_error_code 677kcm_end_get (krb5_context context, 678 krb5_ccache id, 679 krb5_cc_cursor *cursor) 680{ 681 krb5_kcm_cursor c = KCMCURSOR(*cursor); 682 683 free(c->uuids); 684 free(c); 685 686 *cursor = NULL; 687 688 return 0; 689} 690 691/* 692 * Request: 693 * NameZ 694 * WhichFields 695 * MatchCreds 696 * 697 * Response: 698 * 699 */ 700static krb5_error_code 701kcm_remove_cred(krb5_context context, 702 krb5_ccache id, 703 krb5_flags which, 704 krb5_creds *cred) 705{ 706 krb5_error_code ret; 707 krb5_kcmcache *k = KCMCACHE(id); 708 krb5_storage *request; 709 710 ret = krb5_kcm_storage_request(context, KCM_OP_REMOVE_CRED, &request); 711 if (ret) 712 return ret; 713 714 ret = krb5_store_stringz(request, k->name); 715 if (ret) { 716 krb5_storage_free(request); 717 return ret; 718 } 719 720 ret = krb5_store_int32(request, which); 721 if (ret) { 722 krb5_storage_free(request); 723 return ret; 724 } 725 726 ret = krb5_store_creds_tag(request, cred); 727 if (ret) { 728 krb5_storage_free(request); 729 return ret; 730 } 731 732 ret = krb5_kcm_call(context, request, NULL, NULL); 733 734 krb5_storage_free(request); 735 return ret; 736} 737 738static krb5_error_code 739kcm_set_flags(krb5_context context, 740 krb5_ccache id, 741 krb5_flags flags) 742{ 743 krb5_error_code ret; 744 krb5_kcmcache *k = KCMCACHE(id); 745 krb5_storage *request; 746 747 ret = krb5_kcm_storage_request(context, KCM_OP_SET_FLAGS, &request); 748 if (ret) 749 return ret; 750 751 ret = krb5_store_stringz(request, k->name); 752 if (ret) { 753 krb5_storage_free(request); 754 return ret; 755 } 756 757 ret = krb5_store_int32(request, flags); 758 if (ret) { 759 krb5_storage_free(request); 760 return ret; 761 } 762 763 ret = krb5_kcm_call(context, request, NULL, NULL); 764 765 krb5_storage_free(request); 766 return ret; 767} 768 769static int 770kcm_get_version(krb5_context context, 771 krb5_ccache id) 772{ 773 return 0; 774} 775 776/* 777 * Send nothing 778 * get back list of uuids 779 */ 780 781static krb5_error_code 782kcm_get_cache_first(krb5_context context, krb5_cc_cursor *cursor) 783{ 784 krb5_error_code ret; 785 krb5_kcm_cursor c; 786 krb5_storage *request, *response; 787 krb5_data response_data; 788 789 *cursor = NULL; 790 791 c = calloc(1, sizeof(*c)); 792 if (c == NULL) { 793 ret = ENOMEM; 794 krb5_set_error_message(context, ret, 795 N_("malloc: out of memory", "")); 796 goto out; 797 } 798 799 ret = krb5_kcm_storage_request(context, KCM_OP_GET_CACHE_UUID_LIST, &request); 800 if (ret) 801 goto out; 802 803 ret = krb5_kcm_call(context, request, &response, &response_data); 804 krb5_storage_free(request); 805 if (ret) 806 goto out; 807 808 while (1) { 809 ssize_t sret; 810 kcmuuid_t uuid; 811 void *ptr; 812 813 sret = krb5_storage_read(response, &uuid, sizeof(uuid)); 814 if (sret == 0) { 815 ret = 0; 816 break; 817 } else if (sret != sizeof(uuid)) { 818 ret = EINVAL; 819 goto out; 820 } 821 822 ptr = realloc(c->uuids, sizeof(c->uuids[0]) * (c->length + 1)); 823 if (ptr == NULL) { 824 ret = ENOMEM; 825 krb5_set_error_message(context, ret, 826 N_("malloc: out of memory", "")); 827 goto out; 828 } 829 c->uuids = ptr; 830 831 memcpy(&c->uuids[c->length], &uuid, sizeof(uuid)); 832 c->length += 1; 833 } 834 835 krb5_storage_free(response); 836 krb5_data_free(&response_data); 837 838 out: 839 if (ret && c) { 840 free(c->uuids); 841 free(c); 842 } else 843 *cursor = c; 844 845 return ret; 846} 847 848/* 849 * Send uuid 850 * Recv cache name 851 */ 852 853static krb5_error_code 854kcm_get_cache_next(krb5_context context, krb5_cc_cursor cursor, const krb5_cc_ops *ops, krb5_ccache *id) 855{ 856 krb5_error_code ret; 857 krb5_kcm_cursor c = KCMCURSOR(cursor); 858 krb5_storage *request, *response; 859 krb5_data response_data; 860 ssize_t sret; 861 char *name; 862 863 *id = NULL; 864 865 again: 866 867 if (c->offset >= c->length) 868 return KRB5_CC_END; 869 870 ret = krb5_kcm_storage_request(context, KCM_OP_GET_CACHE_BY_UUID, &request); 871 if (ret) 872 return ret; 873 874 sret = krb5_storage_write(request, 875 &c->uuids[c->offset], 876 sizeof(c->uuids[c->offset])); 877 c->offset++; 878 if (sret != sizeof(c->uuids[c->offset])) { 879 krb5_storage_free(request); 880 krb5_clear_error_message(context); 881 return ENOMEM; 882 } 883 884 ret = krb5_kcm_call(context, request, &response, &response_data); 885 krb5_storage_free(request); 886 if (ret == KRB5_CC_END) 887 goto again; 888 889 ret = krb5_ret_stringz(response, &name); 890 krb5_storage_free(response); 891 krb5_data_free(&response_data); 892 893 if (ret == 0) { 894 ret = _krb5_cc_allocate(context, ops, id); 895 if (ret == 0) 896 ret = kcm_alloc(context, name, id); 897 krb5_xfree(name); 898 } 899 900 return ret; 901} 902 903static krb5_error_code 904kcm_get_cache_next_kcm(krb5_context context, krb5_cc_cursor cursor, krb5_ccache *id) 905{ 906#ifndef KCM_IS_API_CACHE 907 return kcm_get_cache_next(context, cursor, &krb5_kcm_ops, id); 908#else 909 return KRB5_CC_END; 910#endif 911} 912 913static krb5_error_code 914kcm_get_cache_next_api(krb5_context context, krb5_cc_cursor cursor, krb5_ccache *id) 915{ 916 return kcm_get_cache_next(context, cursor, &krb5_akcm_ops, id); 917} 918 919 920static krb5_error_code 921kcm_end_cache_get(krb5_context context, krb5_cc_cursor cursor) 922{ 923 krb5_kcm_cursor c = KCMCURSOR(cursor); 924 925 free(c->uuids); 926 free(c); 927 return 0; 928} 929 930 931static krb5_error_code 932kcm_move(krb5_context context, krb5_ccache from, krb5_ccache to) 933{ 934 krb5_error_code ret; 935 krb5_kcmcache *oldk = KCMCACHE(from); 936 krb5_kcmcache *newk = KCMCACHE(to); 937 krb5_storage *request; 938 939 ret = krb5_kcm_storage_request(context, KCM_OP_MOVE_CACHE, &request); 940 if (ret) 941 return ret; 942 943 ret = krb5_store_stringz(request, oldk->name); 944 if (ret) { 945 krb5_storage_free(request); 946 return ret; 947 } 948 949 ret = krb5_store_stringz(request, newk->name); 950 if (ret) { 951 krb5_storage_free(request); 952 return ret; 953 } 954 ret = krb5_kcm_call(context, request, NULL, NULL); 955 956 krb5_storage_free(request); 957 return ret; 958} 959 960static krb5_error_code 961kcm_get_default_name(krb5_context context, const krb5_cc_ops *ops, 962 const char *defstr, char **str) 963{ 964 krb5_error_code ret; 965 krb5_storage *request, *response; 966 krb5_data response_data; 967 char *name; 968 969 *str = NULL; 970 971 ret = krb5_kcm_storage_request(context, KCM_OP_GET_DEFAULT_CACHE, &request); 972 if (ret) 973 return ret; 974 975 ret = krb5_kcm_call(context, request, &response, &response_data); 976 krb5_storage_free(request); 977 if (ret) 978 return _krb5_expand_default_cc_name(context, defstr, str); 979 980 ret = krb5_ret_stringz(response, &name); 981 krb5_storage_free(response); 982 krb5_data_free(&response_data); 983 if (ret) 984 return ret; 985 986 asprintf(str, "%s:%s", ops->prefix, name); 987 free(name); 988 if (str == NULL) 989 return ENOMEM; 990 991 return 0; 992} 993 994static krb5_error_code 995kcm_get_default_name_api(krb5_context context, char **str) 996{ 997 return kcm_get_default_name(context, &krb5_akcm_ops, 998 KRB5_DEFAULT_CCNAME_KCM_API, str); 999} 1000 1001static krb5_error_code 1002kcm_get_default_name_kcm(krb5_context context, char **str) 1003{ 1004 return kcm_get_default_name(context, &krb5_kcm_ops, 1005 KRB5_DEFAULT_CCNAME_KCM_KCM, str); 1006} 1007 1008static krb5_error_code 1009kcm_set_default(krb5_context context, krb5_ccache id) 1010{ 1011 krb5_error_code ret; 1012 krb5_storage *request; 1013 krb5_kcmcache *k = KCMCACHE(id); 1014 1015 ret = krb5_kcm_storage_request(context, KCM_OP_SET_DEFAULT_CACHE, &request); 1016 if (ret) 1017 return ret; 1018 1019 ret = krb5_store_stringz(request, k->name); 1020 if (ret) { 1021 krb5_storage_free(request); 1022 return ret; 1023 } 1024 1025 ret = krb5_kcm_call(context, request, NULL, NULL); 1026 krb5_storage_free(request); 1027 1028 return ret; 1029} 1030 1031static krb5_error_code 1032kcm_lastchange(krb5_context context, krb5_ccache id, krb5_timestamp *mtime) 1033{ 1034 *mtime = time(NULL); 1035 return 0; 1036} 1037 1038static krb5_error_code 1039kcm_set_kdc_offset(krb5_context context, krb5_ccache id, krb5_deltat kdc_offset) 1040{ 1041 krb5_kcmcache *k = KCMCACHE(id); 1042 krb5_error_code ret; 1043 krb5_storage *request; 1044 1045 ret = krb5_kcm_storage_request(context, KCM_OP_SET_KDC_OFFSET, &request); 1046 if (ret) 1047 return ret; 1048 1049 ret = krb5_store_stringz(request, k->name); 1050 if (ret) { 1051 krb5_storage_free(request); 1052 return ret; 1053 } 1054 ret = krb5_store_int32(request, kdc_offset); 1055 if (ret) { 1056 krb5_storage_free(request); 1057 return ret; 1058 } 1059 1060 ret = krb5_kcm_call(context, request, NULL, NULL); 1061 krb5_storage_free(request); 1062 1063 return ret; 1064} 1065 1066static krb5_error_code 1067kcm_get_kdc_offset(krb5_context context, krb5_ccache id, krb5_deltat *kdc_offset) 1068{ 1069 krb5_kcmcache *k = KCMCACHE(id); 1070 krb5_error_code ret; 1071 krb5_storage *request, *response; 1072 krb5_data response_data; 1073 int32_t offset; 1074 1075 ret = krb5_kcm_storage_request(context, KCM_OP_GET_KDC_OFFSET, &request); 1076 if (ret) 1077 return ret; 1078 1079 ret = krb5_store_stringz(request, k->name); 1080 if (ret) { 1081 krb5_storage_free(request); 1082 return ret; 1083 } 1084 1085 ret = krb5_kcm_call(context, request, &response, &response_data); 1086 krb5_storage_free(request); 1087 if (ret) 1088 return ret; 1089 1090 ret = krb5_ret_int32(response, &offset); 1091 krb5_storage_free(response); 1092 krb5_data_free(&response_data); 1093 if (ret) 1094 return ret; 1095 1096 *kdc_offset = offset; 1097 1098 return 0; 1099} 1100 1101/** 1102 * Variable containing the KCM based credential cache implemention. 1103 * 1104 * @ingroup krb5_ccache 1105 */ 1106 1107KRB5_LIB_VARIABLE const krb5_cc_ops krb5_kcm_ops = { 1108 KRB5_CC_OPS_VERSION, 1109 "KCM", 1110 kcm_get_name, 1111 kcm_resolve, 1112 kcm_gen_new, 1113 kcm_initialize, 1114 kcm_destroy, 1115 kcm_close, 1116 kcm_store_cred, 1117 NULL /* kcm_retrieve */, 1118 kcm_get_principal, 1119 kcm_get_first, 1120 kcm_get_next, 1121 kcm_end_get, 1122 kcm_remove_cred, 1123 kcm_set_flags, 1124 kcm_get_version, 1125 kcm_get_cache_first, 1126 kcm_get_cache_next_kcm, 1127 kcm_end_cache_get, 1128 kcm_move, 1129 kcm_get_default_name_kcm, 1130 kcm_set_default, 1131 kcm_lastchange, 1132 kcm_set_kdc_offset, 1133 kcm_get_kdc_offset 1134}; 1135 1136KRB5_LIB_VARIABLE const krb5_cc_ops krb5_akcm_ops = { 1137 KRB5_CC_OPS_VERSION, 1138 "API", 1139 kcm_get_name, 1140 kcm_resolve, 1141 kcm_gen_new, 1142 kcm_initialize, 1143 kcm_destroy, 1144 kcm_close, 1145 kcm_store_cred, 1146 NULL /* kcm_retrieve */, 1147 kcm_get_principal, 1148 kcm_get_first, 1149 kcm_get_next, 1150 kcm_end_get, 1151 kcm_remove_cred, 1152 kcm_set_flags, 1153 kcm_get_version, 1154 kcm_get_cache_first, 1155 kcm_get_cache_next_api, 1156 kcm_end_cache_get, 1157 kcm_move, 1158 kcm_get_default_name_api, 1159 kcm_set_default, 1160 kcm_lastchange, 1161 NULL, 1162 NULL 1163}; 1164 1165 1166KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL 1167_krb5_kcm_is_running(krb5_context context) 1168{ 1169 krb5_error_code ret; 1170 krb5_ccache_data ccdata; 1171 krb5_ccache id = &ccdata; 1172 krb5_boolean running; 1173 1174 ret = kcm_alloc(context, NULL, &id); 1175 if (ret) 1176 return 0; 1177 1178 running = (_krb5_kcm_noop(context, id) == 0); 1179 1180 kcm_free(context, &id); 1181 1182 return running; 1183} 1184 1185/* 1186 * Request: 1187 * 1188 * Response: 1189 * 1190 */ 1191KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 1192_krb5_kcm_noop(krb5_context context, 1193 krb5_ccache id) 1194{ 1195 krb5_error_code ret; 1196 krb5_storage *request; 1197 1198 ret = krb5_kcm_storage_request(context, KCM_OP_NOOP, &request); 1199 if (ret) 1200 return ret; 1201 1202 ret = krb5_kcm_call(context, request, NULL, NULL); 1203 1204 krb5_storage_free(request); 1205 return ret; 1206} 1207 1208 1209/* 1210 * Request: 1211 * NameZ 1212 * ServerPrincipalPresent 1213 * ServerPrincipal OPTIONAL 1214 * Key 1215 * 1216 * Repsonse: 1217 * 1218 */ 1219KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 1220_krb5_kcm_get_initial_ticket(krb5_context context, 1221 krb5_ccache id, 1222 krb5_principal server, 1223 krb5_keyblock *key) 1224{ 1225 krb5_kcmcache *k = KCMCACHE(id); 1226 krb5_error_code ret; 1227 krb5_storage *request; 1228 1229 ret = krb5_kcm_storage_request(context, KCM_OP_GET_INITIAL_TICKET, &request); 1230 if (ret) 1231 return ret; 1232 1233 ret = krb5_store_stringz(request, k->name); 1234 if (ret) { 1235 krb5_storage_free(request); 1236 return ret; 1237 } 1238 1239 ret = krb5_store_int8(request, (server == NULL) ? 0 : 1); 1240 if (ret) { 1241 krb5_storage_free(request); 1242 return ret; 1243 } 1244 1245 if (server != NULL) { 1246 ret = krb5_store_principal(request, server); 1247 if (ret) { 1248 krb5_storage_free(request); 1249 return ret; 1250 } 1251 } 1252 1253 ret = krb5_store_keyblock(request, *key); 1254 if (ret) { 1255 krb5_storage_free(request); 1256 return ret; 1257 } 1258 1259 ret = krb5_kcm_call(context, request, NULL, NULL); 1260 1261 krb5_storage_free(request); 1262 return ret; 1263} 1264 1265 1266/* 1267 * Request: 1268 * NameZ 1269 * KDCFlags 1270 * EncryptionType 1271 * ServerPrincipal 1272 * 1273 * Repsonse: 1274 * 1275 */ 1276KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 1277_krb5_kcm_get_ticket(krb5_context context, 1278 krb5_ccache id, 1279 krb5_kdc_flags flags, 1280 krb5_enctype enctype, 1281 krb5_principal server) 1282{ 1283 krb5_error_code ret; 1284 krb5_kcmcache *k = KCMCACHE(id); 1285 krb5_storage *request; 1286 1287 ret = krb5_kcm_storage_request(context, KCM_OP_GET_TICKET, &request); 1288 if (ret) 1289 return ret; 1290 1291 ret = krb5_store_stringz(request, k->name); 1292 if (ret) { 1293 krb5_storage_free(request); 1294 return ret; 1295 } 1296 1297 ret = krb5_store_int32(request, flags.i); 1298 if (ret) { 1299 krb5_storage_free(request); 1300 return ret; 1301 } 1302 1303 ret = krb5_store_int32(request, enctype); 1304 if (ret) { 1305 krb5_storage_free(request); 1306 return ret; 1307 } 1308 1309 ret = krb5_store_principal(request, server); 1310 if (ret) { 1311 krb5_storage_free(request); 1312 return ret; 1313 } 1314 1315 ret = krb5_kcm_call(context, request, NULL, NULL); 1316 1317 krb5_storage_free(request); 1318 return ret; 1319} 1320 1321#endif /* HAVE_KCM */ 1322