11590Srgrimes/* $NetBSD$ */ 21590Srgrimes 31590Srgrimes/* 41590Srgrimes * Copyright (c) 2005, PADL Software Pty Ltd. 51590Srgrimes * All rights reserved. 61590Srgrimes * 71590Srgrimes * Portions Copyright (c) 2009 Apple Inc. All rights reserved. 81590Srgrimes * 91590Srgrimes * Redistribution and use in source and binary forms, with or without 101590Srgrimes * modification, are permitted provided that the following conditions 111590Srgrimes * are met: 121590Srgrimes * 131590Srgrimes * 1. Redistributions of source code must retain the above copyright 141590Srgrimes * notice, this list of conditions and the following disclaimer. 151590Srgrimes * 161590Srgrimes * 2. Redistributions in binary form must reproduce the above copyright 171590Srgrimes * notice, this list of conditions and the following disclaimer in the 181590Srgrimes * documentation and/or other materials provided with the distribution. 191590Srgrimes * 201590Srgrimes * 3. Neither the name of PADL Software nor the names of its contributors 211590Srgrimes * may be used to endorse or promote products derived from this software 221590Srgrimes * without specific prior written permission. 231590Srgrimes * 241590Srgrimes * THIS SOFTWARE IS PROVIDED BY PADL SOFTWARE AND CONTRIBUTORS ``AS IS'' AND 251590Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 261590Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 271590Srgrimes * ARE DISCLAIMED. IN NO EVENT SHALL PADL SOFTWARE OR CONTRIBUTORS BE LIABLE 281590Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 291590Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 301590Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 311590Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 321590Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 331590Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 341590Srgrimes * SUCH DAMAGE. 351590Srgrimes */ 361590Srgrimes 371590Srgrimes#include "krb5_locl.h" 3828794Scharnier 391590Srgrimes#ifdef HAVE_KCM 401590Srgrimes/* 411590Srgrimes * Client library for Kerberos Credentials Manager (KCM) daemon 421590Srgrimes */ 431590Srgrimes 4428794Scharnier#include <krb5/kcm.h> 451590Srgrimes#include <heim-ipc.h> 4628794Scharnier 4728794Scharnierstatic krb5_error_code 4848566Sbillfkcm_set_kdc_offset(krb5_context, krb5_ccache, krb5_deltat); 491590Srgrimes 501590Srgrimesstatic const char *kcm_ipc_name = "ANY:org.h5l.kcm"; 511590Srgrimes 521590Srgrimestypedef struct krb5_kcmcache { 531590Srgrimes char *name; 541590Srgrimes} krb5_kcmcache; 551590Srgrimes 561590Srgrimestypedef struct krb5_kcm_cursor { 5728794Scharnier unsigned long offset; 5829430Sache unsigned long length; 5919190Salex kcmuuid_t *uuids; 601590Srgrimes} *krb5_kcm_cursor; 611590Srgrimes 621590Srgrimes 6328794Scharnier#define KCMCACHE(X) ((krb5_kcmcache *)(X)->data.data) 6428794Scharnier#define CACHENAME(X) (KCMCACHE(X)->name) 651590Srgrimes#define KCMCURSOR(C) ((krb5_kcm_cursor)(C)) 6629430Sache 6728794Scharnierstatic HEIMDAL_MUTEX kcm_mutex = HEIMDAL_MUTEX_INITIALIZER; 6828794Scharnierstatic heim_ipc kcm_ipc = NULL; 6928794Scharnier 7028794Scharnierstatic krb5_error_code 7128794Scharnierkcm_send_request(krb5_context context, 7228794Scharnier krb5_storage *request, 731590Srgrimes krb5_data *response_data) 7428794Scharnier{ 751590Srgrimes krb5_error_code ret = 0; 761590Srgrimes krb5_data request_data; 771590Srgrimes 781590Srgrimes HEIMDAL_MUTEX_lock(&kcm_mutex); 791590Srgrimes if (kcm_ipc == NULL) 801590Srgrimes ret = heim_ipc_init_context(kcm_ipc_name, &kcm_ipc); 811590Srgrimes HEIMDAL_MUTEX_unlock(&kcm_mutex); 821590Srgrimes if (ret) 8328794Scharnier return KRB5_CC_NOSUPP; 841590Srgrimes 8529430Sache ret = krb5_storage_to_data(request, &request_data); 8629430Sache if (ret) { 871590Srgrimes krb5_clear_error_message(context); 881590Srgrimes return KRB5_CC_NOMEM; 891590Srgrimes } 901590Srgrimes 911590Srgrimes ret = heim_ipc_call(kcm_ipc, &request_data, response_data, NULL); 921590Srgrimes krb5_data_free(&request_data); 931590Srgrimes 9428794Scharnier if (ret) { 9528794Scharnier krb5_clear_error_message(context); 9628794Scharnier ret = KRB5_CC_NOSUPP; 9728794Scharnier } 9828794Scharnier 991590Srgrimes return ret; 1001590Srgrimes} 1011590Srgrimes 10228794ScharnierKRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 10328794Scharnierkrb5_kcm_storage_request(krb5_context context, 1041590Srgrimes uint16_t opcode, 1051590Srgrimes krb5_storage **storage_p) 1061590Srgrimes{ 1071590Srgrimes krb5_storage *sp; 1081590Srgrimes krb5_error_code ret; 1091590Srgrimes 1101590Srgrimes *storage_p = NULL; 1111590Srgrimes 1121590Srgrimes sp = krb5_storage_emem(); 1131590Srgrimes if (sp == NULL) { 11419190Salex krb5_set_error_message(context, KRB5_CC_NOMEM, N_("malloc: out of memory", "")); 11519193Salex return KRB5_CC_NOMEM; 11628794Scharnier } 11728794Scharnier 1181590Srgrimes /* Send MAJOR | VERSION | OPCODE */ 1191590Srgrimes ret = krb5_store_int8(sp, KCM_PROTOCOL_VERSION_MAJOR); 12028794Scharnier if (ret) 12128794Scharnier goto fail; 1221590Srgrimes ret = krb5_store_int8(sp, KCM_PROTOCOL_VERSION_MINOR); 1231590Srgrimes if (ret) 1241590Srgrimes goto fail; 12528794Scharnier ret = krb5_store_int16(sp, opcode); 1261590Srgrimes if (ret) 12729430Sache goto fail; 12828794Scharnier 1291590Srgrimes *storage_p = sp; 1301590Srgrimes fail: 13128794Scharnier if (ret) { 13228794Scharnier krb5_set_error_message(context, ret, 13328794Scharnier N_("Failed to encode KCM request", "")); 13428794Scharnier krb5_storage_free(sp); 13528794Scharnier } 13628794Scharnier 13728794Scharnier return ret; 1381590Srgrimes} 1391590Srgrimes 1401590Srgrimesstatic krb5_error_code 1411590Srgrimeskcm_alloc(krb5_context context, const char *name, krb5_ccache *id) 14228794Scharnier{ 1431590Srgrimes krb5_kcmcache *k; 1441590Srgrimes 1451590Srgrimes k = malloc(sizeof(*k)); 1461590Srgrimes if (k == NULL) { 1471590Srgrimes krb5_set_error_message(context, KRB5_CC_NOMEM, 1481590Srgrimes N_("malloc: out of memory", "")); 1491590Srgrimes return KRB5_CC_NOMEM; 1501590Srgrimes } 1511590Srgrimes 1521590Srgrimes if (name != NULL) { 1531590Srgrimes k->name = strdup(name); 1541590Srgrimes if (k->name == NULL) { 1551590Srgrimes free(k); 1561590Srgrimes krb5_set_error_message(context, KRB5_CC_NOMEM, 1571590Srgrimes N_("malloc: out of memory", "")); 1581590Srgrimes return KRB5_CC_NOMEM; 1591590Srgrimes } 1601590Srgrimes } else 1611590Srgrimes k->name = NULL; 1621590Srgrimes 1631590Srgrimes (*id)->data.data = k; 1641590Srgrimes (*id)->data.length = sizeof(*k); 1651590Srgrimes 1661590Srgrimes return 0; 1671590Srgrimes} 1681590Srgrimes 1691590SrgrimesKRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 1701590Srgrimeskrb5_kcm_call(krb5_context context, 1711590Srgrimes krb5_storage *request, 1721590Srgrimes krb5_storage **response_p, 1731590Srgrimes krb5_data *response_data_p) 17428794Scharnier{ 1751590Srgrimes krb5_data response_data; 1761590Srgrimes krb5_error_code ret; 1771590Srgrimes int32_t status; 1781590Srgrimes krb5_storage *response; 1791590Srgrimes 1801590Srgrimes if (response_p != NULL) 1811590Srgrimes *response_p = NULL; 1821590Srgrimes 1831590Srgrimes krb5_data_zero(&response_data); 18428794Scharnier 18528794Scharnier ret = kcm_send_request(context, request, &response_data); 1861590Srgrimes if (ret) 1871590Srgrimes return ret; 1881590Srgrimes 1891590Srgrimes response = krb5_storage_from_data(&response_data); 1901590Srgrimes if (response == NULL) { 1911590Srgrimes krb5_data_free(&response_data); 1921590Srgrimes return KRB5_CC_IO; 1931590Srgrimes } 1941590Srgrimes 1951590Srgrimes ret = krb5_ret_int32(response, &status); 1961590Srgrimes if (ret) { 1971590Srgrimes krb5_storage_free(response); 1981590Srgrimes krb5_data_free(&response_data); 1991590Srgrimes return KRB5_CC_FORMAT; 2001590Srgrimes } 2011590Srgrimes 2021590Srgrimes if (status) { 2031590Srgrimes krb5_storage_free(response); 2041590Srgrimes krb5_data_free(&response_data); 2051590Srgrimes return status; 2061590Srgrimes } 2071590Srgrimes 2081590Srgrimes if (response_p != NULL) { 2091590Srgrimes *response_data_p = response_data; 2101590Srgrimes *response_p = response; 21128794Scharnier 21228794Scharnier return 0; 2131590Srgrimes } 2141590Srgrimes 2151590Srgrimes krb5_storage_free(response); 2161590Srgrimes krb5_data_free(&response_data); 2171590Srgrimes 21828794Scharnier return 0; 2191590Srgrimes} 22028794Scharnier 2211590Srgrimesstatic void 2221590Srgrimeskcm_free(krb5_context context, krb5_ccache *id) 2231590Srgrimes{ 2241590Srgrimes krb5_kcmcache *k = KCMCACHE(*id); 2251590Srgrimes 2261590Srgrimes if (k != NULL) { 2271590Srgrimes if (k->name != NULL) 22828794Scharnier free(k->name); 2291590Srgrimes memset(k, 0, sizeof(*k)); 2301590Srgrimes krb5_data_free(&(*id)->data); 2311590Srgrimes } 2321590Srgrimes} 2331590Srgrimes 2341590Srgrimesstatic const char * 2351590Srgrimeskcm_get_name(krb5_context context, 2361590Srgrimes krb5_ccache id) 23719190Salex{ 2381590Srgrimes return CACHENAME(id); 2391590Srgrimes} 24028794Scharnier 2411590Srgrimesstatic krb5_error_code 2421590Srgrimeskcm_resolve(krb5_context context, krb5_ccache *id, const char *res) 2431590Srgrimes{ 2441590Srgrimes return kcm_alloc(context, res, id); 2451590Srgrimes} 2461590Srgrimes 2471590Srgrimes/* 2481590Srgrimes * Request: 2491590Srgrimes * 2501590Srgrimes * Response: 25128794Scharnier * NameZ 2521590Srgrimes */ 2531590Srgrimesstatic krb5_error_code 2541590Srgrimeskcm_gen_new(krb5_context context, krb5_ccache *id) 2551590Srgrimes{ 2561590Srgrimes krb5_kcmcache *k; 2571590Srgrimes krb5_error_code ret; 25828794Scharnier krb5_storage *request, *response; 25928794Scharnier krb5_data response_data; 2601590Srgrimes 2611590Srgrimes ret = kcm_alloc(context, NULL, id); 26248566Sbillf if (ret) 26328794Scharnier return ret; 2641590Srgrimes 2651590Srgrimes k = KCMCACHE(*id); 2661590Srgrimes 26748566Sbillf ret = krb5_kcm_storage_request(context, KCM_OP_GEN_NEW, &request); 2681590Srgrimes if (ret) { 26919190Salex kcm_free(context, id); 27028794Scharnier return ret; 27128794Scharnier } 2721590Srgrimes 2731590Srgrimes ret = krb5_kcm_call(context, request, &response, &response_data); 2741590Srgrimes if (ret) { 2751590Srgrimes krb5_storage_free(request); 2761590Srgrimes kcm_free(context, id); 2771590Srgrimes return ret; 2781590Srgrimes } 2791590Srgrimes 2801590Srgrimes ret = krb5_ret_stringz(response, &k->name); 2811590Srgrimes if (ret) 2821590Srgrimes ret = KRB5_CC_IO; 2831590Srgrimes 2841590Srgrimes krb5_storage_free(request); 2851590Srgrimes krb5_storage_free(response); 2861590Srgrimes krb5_data_free(&response_data); 2871590Srgrimes 2881590Srgrimes if (ret) 2891590Srgrimes kcm_free(context, id); 2901590Srgrimes 2911590Srgrimes return ret; 2921590Srgrimes} 29329430Sache 29429430Sache/* 2951590Srgrimes * Request: 2961590Srgrimes * NameZ 2971590Srgrimes * Principal 2981590Srgrimes * 2991590Srgrimes * Response: 3001590Srgrimes * 3011590Srgrimes */ 3021590Srgrimesstatic krb5_error_code 3031590Srgrimeskcm_initialize(krb5_context context, 30428794Scharnier krb5_ccache id, 3051590Srgrimes krb5_principal primary_principal) 30612097Sache{ 3071590Srgrimes krb5_error_code ret; 3081590Srgrimes krb5_kcmcache *k = KCMCACHE(id); 30928794Scharnier krb5_storage *request; 3101590Srgrimes 3111590Srgrimes ret = krb5_kcm_storage_request(context, KCM_OP_INITIALIZE, &request); 31211916Sache if (ret) 3131590Srgrimes return ret; 31429431Sache 31529431Sache ret = krb5_store_stringz(request, k->name); 31629433Sache if (ret) { 31729433Sache krb5_storage_free(request); 31829430Sache return ret; 31912097Sache } 32012097Sache 32112097Sache ret = krb5_store_principal(request, primary_principal); 32212097Sache if (ret) { 32312097Sache krb5_storage_free(request); 32412097Sache return ret; 32512097Sache } 32612097Sache 32712097Sache ret = krb5_kcm_call(context, request, NULL, NULL); 32812097Sache 32912097Sache krb5_storage_free(request); 3301590Srgrimes 3311590Srgrimes if (context->kdc_sec_offset) 3321590Srgrimes kcm_set_kdc_offset(context, id, context->kdc_sec_offset); 3331590Srgrimes 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}; 1162 1163 1164krb5_boolean 1165_krb5_kcm_is_running(krb5_context context) 1166{ 1167 krb5_error_code ret; 1168 krb5_ccache_data ccdata; 1169 krb5_ccache id = &ccdata; 1170 krb5_boolean running; 1171 1172 ret = kcm_alloc(context, NULL, &id); 1173 if (ret) 1174 return 0; 1175 1176 running = (_krb5_kcm_noop(context, id) == 0); 1177 1178 kcm_free(context, &id); 1179 1180 return running; 1181} 1182 1183/* 1184 * Request: 1185 * 1186 * Response: 1187 * 1188 */ 1189krb5_error_code 1190_krb5_kcm_noop(krb5_context context, 1191 krb5_ccache id) 1192{ 1193 krb5_error_code ret; 1194 krb5_storage *request; 1195 1196 ret = krb5_kcm_storage_request(context, KCM_OP_NOOP, &request); 1197 if (ret) 1198 return ret; 1199 1200 ret = krb5_kcm_call(context, request, NULL, NULL); 1201 1202 krb5_storage_free(request); 1203 return ret; 1204} 1205 1206 1207/* 1208 * Request: 1209 * NameZ 1210 * ServerPrincipalPresent 1211 * ServerPrincipal OPTIONAL 1212 * Key 1213 * 1214 * Repsonse: 1215 * 1216 */ 1217krb5_error_code 1218_krb5_kcm_get_initial_ticket(krb5_context context, 1219 krb5_ccache id, 1220 krb5_principal server, 1221 krb5_keyblock *key) 1222{ 1223 krb5_kcmcache *k = KCMCACHE(id); 1224 krb5_error_code ret; 1225 krb5_storage *request; 1226 1227 ret = krb5_kcm_storage_request(context, KCM_OP_GET_INITIAL_TICKET, &request); 1228 if (ret) 1229 return ret; 1230 1231 ret = krb5_store_stringz(request, k->name); 1232 if (ret) { 1233 krb5_storage_free(request); 1234 return ret; 1235 } 1236 1237 ret = krb5_store_int8(request, (server == NULL) ? 0 : 1); 1238 if (ret) { 1239 krb5_storage_free(request); 1240 return ret; 1241 } 1242 1243 if (server != NULL) { 1244 ret = krb5_store_principal(request, server); 1245 if (ret) { 1246 krb5_storage_free(request); 1247 return ret; 1248 } 1249 } 1250 1251 ret = krb5_store_keyblock(request, *key); 1252 if (ret) { 1253 krb5_storage_free(request); 1254 return ret; 1255 } 1256 1257 ret = krb5_kcm_call(context, request, NULL, NULL); 1258 1259 krb5_storage_free(request); 1260 return ret; 1261} 1262 1263 1264/* 1265 * Request: 1266 * NameZ 1267 * KDCFlags 1268 * EncryptionType 1269 * ServerPrincipal 1270 * 1271 * Repsonse: 1272 * 1273 */ 1274krb5_error_code 1275_krb5_kcm_get_ticket(krb5_context context, 1276 krb5_ccache id, 1277 krb5_kdc_flags flags, 1278 krb5_enctype enctype, 1279 krb5_principal server) 1280{ 1281 krb5_error_code ret; 1282 krb5_kcmcache *k = KCMCACHE(id); 1283 krb5_storage *request; 1284 1285 ret = krb5_kcm_storage_request(context, KCM_OP_GET_TICKET, &request); 1286 if (ret) 1287 return ret; 1288 1289 ret = krb5_store_stringz(request, k->name); 1290 if (ret) { 1291 krb5_storage_free(request); 1292 return ret; 1293 } 1294 1295 ret = krb5_store_int32(request, flags.i); 1296 if (ret) { 1297 krb5_storage_free(request); 1298 return ret; 1299 } 1300 1301 ret = krb5_store_int32(request, enctype); 1302 if (ret) { 1303 krb5_storage_free(request); 1304 return ret; 1305 } 1306 1307 ret = krb5_store_principal(request, server); 1308 if (ret) { 1309 krb5_storage_free(request); 1310 return ret; 1311 } 1312 1313 ret = krb5_kcm_call(context, request, NULL, NULL); 1314 1315 krb5_storage_free(request); 1316 return ret; 1317} 1318 1319#endif /* HAVE_KCM */ 1320