1/* $NetBSD: acache.c,v 1.2 2017/01/28 21:31:49 christos Exp $ */ 2 3/* 4 * Copyright (c) 2004 - 2007 Kungliga Tekniska H��gskolan 5 * (Royal Institute of Technology, Stockholm, Sweden). 6 * All rights reserved. 7 * 8 * Portions Copyright (c) 2009 Apple Inc. All rights reserved. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 14 * 1. Redistributions of source code must retain the above copyright 15 * notice, this list of conditions and the following disclaimer. 16 * 17 * 2. Redistributions in binary form must reproduce the above copyright 18 * notice, this list of conditions and the following disclaimer in the 19 * documentation and/or other materials provided with the distribution. 20 * 21 * 3. Neither the name of the Institute nor the names of its contributors 22 * may be used to endorse or promote products derived from this software 23 * without specific prior written permission. 24 * 25 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND 26 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 27 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 28 * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE 29 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 30 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 31 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 32 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 33 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 34 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 35 * SUCH DAMAGE. 36 */ 37 38#include "krb5_locl.h" 39#include <krb5/krb5_ccapi.h> 40#ifdef HAVE_DLFCN_H 41#include <dlfcn.h> 42#endif 43 44#ifndef KCM_IS_API_CACHE 45 46static HEIMDAL_MUTEX acc_mutex = HEIMDAL_MUTEX_INITIALIZER; 47static cc_initialize_func init_func; 48static void (KRB5_CALLCONV *set_target_uid)(uid_t); 49static void (KRB5_CALLCONV *clear_target)(void); 50 51#ifdef HAVE_DLOPEN 52static void *cc_handle; 53#endif 54 55typedef struct krb5_acc { 56 char *cache_name; 57 cc_context_t context; 58 cc_ccache_t ccache; 59} krb5_acc; 60 61static krb5_error_code KRB5_CALLCONV acc_close(krb5_context, krb5_ccache); 62 63#define ACACHE(X) ((krb5_acc *)(X)->data.data) 64 65static const struct { 66 cc_int32 error; 67 krb5_error_code ret; 68} cc_errors[] = { 69 { ccErrBadName, KRB5_CC_BADNAME }, 70 { ccErrCredentialsNotFound, KRB5_CC_NOTFOUND }, 71 { ccErrCCacheNotFound, KRB5_FCC_NOFILE }, 72 { ccErrContextNotFound, KRB5_CC_NOTFOUND }, 73 { ccIteratorEnd, KRB5_CC_END }, 74 { ccErrNoMem, KRB5_CC_NOMEM }, 75 { ccErrServerUnavailable, KRB5_CC_NOSUPP }, 76 { ccErrInvalidCCache, KRB5_CC_BADNAME }, 77 { ccNoError, 0 } 78}; 79 80static krb5_error_code 81translate_cc_error(krb5_context context, cc_int32 error) 82{ 83 size_t i; 84 krb5_clear_error_message(context); 85 for(i = 0; i < sizeof(cc_errors)/sizeof(cc_errors[0]); i++) 86 if (cc_errors[i].error == error) 87 return cc_errors[i].ret; 88 return KRB5_FCC_INTERNAL; 89} 90 91static krb5_error_code 92init_ccapi(krb5_context context) 93{ 94 const char *lib = NULL; 95 96 HEIMDAL_MUTEX_lock(&acc_mutex); 97 if (init_func) { 98 HEIMDAL_MUTEX_unlock(&acc_mutex); 99 if (context) 100 krb5_clear_error_message(context); 101 return 0; 102 } 103 104 if (context) 105 lib = krb5_config_get_string(context, NULL, 106 "libdefaults", "ccapi_library", 107 NULL); 108 if (lib == NULL) { 109#ifdef __APPLE__ 110 lib = "/System/Library/Frameworks/Kerberos.framework/Kerberos"; 111#elif defined(KRB5_USE_PATH_TOKENS) && defined(_WIN32) 112 lib = "%{LIBDIR}/libkrb5_cc.dll"; 113#else 114 lib = "/usr/lib/libkrb5_cc.so"; 115#endif 116 } 117 118#ifdef HAVE_DLOPEN 119 120#ifndef RTLD_LAZY 121#define RTLD_LAZY 0 122#endif 123#ifndef RTLD_LOCAL 124#define RTLD_LOCAL 0 125#endif 126 127#ifdef KRB5_USE_PATH_TOKENS 128 { 129 char * explib = NULL; 130 if (_krb5_expand_path_tokens(context, lib, 0, &explib) == 0) { 131 cc_handle = dlopen(explib, RTLD_LAZY|RTLD_LOCAL); 132 free(explib); 133 } 134 } 135#else 136 cc_handle = dlopen(lib, RTLD_LAZY|RTLD_LOCAL); 137#endif 138 139 if (cc_handle == NULL) { 140 HEIMDAL_MUTEX_unlock(&acc_mutex); 141 if (context) 142 krb5_set_error_message(context, KRB5_CC_NOSUPP, 143 N_("Failed to load API cache module %s", "file"), 144 lib); 145 return KRB5_CC_NOSUPP; 146 } 147 148 init_func = (cc_initialize_func)dlsym(cc_handle, "cc_initialize"); 149 set_target_uid = (void (KRB5_CALLCONV *)(uid_t)) 150 dlsym(cc_handle, "krb5_ipc_client_set_target_uid"); 151 clear_target = (void (KRB5_CALLCONV *)(void)) 152 dlsym(cc_handle, "krb5_ipc_client_clear_target"); 153 HEIMDAL_MUTEX_unlock(&acc_mutex); 154 if (init_func == NULL) { 155 if (context) 156 krb5_set_error_message(context, KRB5_CC_NOSUPP, 157 N_("Failed to find cc_initialize" 158 "in %s: %s", "file, error"), lib, dlerror()); 159 dlclose(cc_handle); 160 return KRB5_CC_NOSUPP; 161 } 162 163 return 0; 164#else 165 HEIMDAL_MUTEX_unlock(&acc_mutex); 166 if (context) 167 krb5_set_error_message(context, KRB5_CC_NOSUPP, 168 N_("no support for shared object", "")); 169 return KRB5_CC_NOSUPP; 170#endif 171} 172 173KRB5_LIB_FUNCTION void KRB5_LIB_CALL 174_heim_krb5_ipc_client_set_target_uid(uid_t uid) 175{ 176 init_ccapi(NULL); 177 if (set_target_uid != NULL) 178 (*set_target_uid)(uid); 179} 180 181KRB5_LIB_FUNCTION void KRB5_LIB_CALL 182_heim_krb5_ipc_client_clear_target(void) 183{ 184 init_ccapi(NULL); 185 if (clear_target != NULL) 186 (*clear_target)(); 187} 188 189static krb5_error_code 190make_cred_from_ccred(krb5_context context, 191 const cc_credentials_v5_t *incred, 192 krb5_creds *cred) 193{ 194 krb5_error_code ret; 195 unsigned int i; 196 197 memset(cred, 0, sizeof(*cred)); 198 199 ret = krb5_parse_name(context, incred->client, &cred->client); 200 if (ret) 201 goto fail; 202 203 ret = krb5_parse_name(context, incred->server, &cred->server); 204 if (ret) 205 goto fail; 206 207 cred->session.keytype = incred->keyblock.type; 208 cred->session.keyvalue.length = incred->keyblock.length; 209 cred->session.keyvalue.data = malloc(incred->keyblock.length); 210 if (cred->session.keyvalue.data == NULL) 211 goto nomem; 212 memcpy(cred->session.keyvalue.data, incred->keyblock.data, 213 incred->keyblock.length); 214 215 cred->times.authtime = incred->authtime; 216 cred->times.starttime = incred->starttime; 217 cred->times.endtime = incred->endtime; 218 cred->times.renew_till = incred->renew_till; 219 220 ret = krb5_data_copy(&cred->ticket, 221 incred->ticket.data, 222 incred->ticket.length); 223 if (ret) 224 goto nomem; 225 226 ret = krb5_data_copy(&cred->second_ticket, 227 incred->second_ticket.data, 228 incred->second_ticket.length); 229 if (ret) 230 goto nomem; 231 232 cred->authdata.val = NULL; 233 cred->authdata.len = 0; 234 235 cred->addresses.val = NULL; 236 cred->addresses.len = 0; 237 238 for (i = 0; incred->authdata && incred->authdata[i]; i++) 239 ; 240 241 if (i) { 242 cred->authdata.val = calloc(i, sizeof(cred->authdata.val[0])); 243 if (cred->authdata.val == NULL) 244 goto nomem; 245 cred->authdata.len = i; 246 for (i = 0; i < cred->authdata.len; i++) { 247 cred->authdata.val[i].ad_type = incred->authdata[i]->type; 248 ret = krb5_data_copy(&cred->authdata.val[i].ad_data, 249 incred->authdata[i]->data, 250 incred->authdata[i]->length); 251 if (ret) 252 goto nomem; 253 } 254 } 255 256 for (i = 0; incred->addresses && incred->addresses[i]; i++) 257 ; 258 259 if (i) { 260 cred->addresses.val = calloc(i, sizeof(cred->addresses.val[0])); 261 if (cred->addresses.val == NULL) 262 goto nomem; 263 cred->addresses.len = i; 264 265 for (i = 0; i < cred->addresses.len; i++) { 266 cred->addresses.val[i].addr_type = incred->addresses[i]->type; 267 ret = krb5_data_copy(&cred->addresses.val[i].address, 268 incred->addresses[i]->data, 269 incred->addresses[i]->length); 270 if (ret) 271 goto nomem; 272 } 273 } 274 275 cred->flags.i = 0; 276 if (incred->ticket_flags & KRB5_CCAPI_TKT_FLG_FORWARDABLE) 277 cred->flags.b.forwardable = 1; 278 if (incred->ticket_flags & KRB5_CCAPI_TKT_FLG_FORWARDED) 279 cred->flags.b.forwarded = 1; 280 if (incred->ticket_flags & KRB5_CCAPI_TKT_FLG_PROXIABLE) 281 cred->flags.b.proxiable = 1; 282 if (incred->ticket_flags & KRB5_CCAPI_TKT_FLG_PROXY) 283 cred->flags.b.proxy = 1; 284 if (incred->ticket_flags & KRB5_CCAPI_TKT_FLG_MAY_POSTDATE) 285 cred->flags.b.may_postdate = 1; 286 if (incred->ticket_flags & KRB5_CCAPI_TKT_FLG_POSTDATED) 287 cred->flags.b.postdated = 1; 288 if (incred->ticket_flags & KRB5_CCAPI_TKT_FLG_INVALID) 289 cred->flags.b.invalid = 1; 290 if (incred->ticket_flags & KRB5_CCAPI_TKT_FLG_RENEWABLE) 291 cred->flags.b.renewable = 1; 292 if (incred->ticket_flags & KRB5_CCAPI_TKT_FLG_INITIAL) 293 cred->flags.b.initial = 1; 294 if (incred->ticket_flags & KRB5_CCAPI_TKT_FLG_PRE_AUTH) 295 cred->flags.b.pre_authent = 1; 296 if (incred->ticket_flags & KRB5_CCAPI_TKT_FLG_HW_AUTH) 297 cred->flags.b.hw_authent = 1; 298 if (incred->ticket_flags & KRB5_CCAPI_TKT_FLG_TRANSIT_POLICY_CHECKED) 299 cred->flags.b.transited_policy_checked = 1; 300 if (incred->ticket_flags & KRB5_CCAPI_TKT_FLG_OK_AS_DELEGATE) 301 cred->flags.b.ok_as_delegate = 1; 302 if (incred->ticket_flags & KRB5_CCAPI_TKT_FLG_ANONYMOUS) 303 cred->flags.b.anonymous = 1; 304 305 return 0; 306 307nomem: 308 ret = krb5_enomem(context); 309 310fail: 311 krb5_free_cred_contents(context, cred); 312 return ret; 313} 314 315static void 316free_ccred(cc_credentials_v5_t *cred) 317{ 318 int i; 319 320 if (cred->addresses) { 321 for (i = 0; cred->addresses[i] != 0; i++) { 322 if (cred->addresses[i]->data) 323 free(cred->addresses[i]->data); 324 free(cred->addresses[i]); 325 } 326 free(cred->addresses); 327 } 328 if (cred->server) 329 free(cred->server); 330 if (cred->client) 331 free(cred->client); 332 memset(cred, 0, sizeof(*cred)); 333} 334 335static krb5_error_code 336make_ccred_from_cred(krb5_context context, 337 const krb5_creds *incred, 338 cc_credentials_v5_t *cred) 339{ 340 krb5_error_code ret; 341 size_t i; 342 343 memset(cred, 0, sizeof(*cred)); 344 345 ret = krb5_unparse_name(context, incred->client, &cred->client); 346 if (ret) 347 goto fail; 348 349 ret = krb5_unparse_name(context, incred->server, &cred->server); 350 if (ret) 351 goto fail; 352 353 cred->keyblock.type = incred->session.keytype; 354 cred->keyblock.length = incred->session.keyvalue.length; 355 cred->keyblock.data = incred->session.keyvalue.data; 356 357 cred->authtime = incred->times.authtime; 358 cred->starttime = incred->times.starttime; 359 cred->endtime = incred->times.endtime; 360 cred->renew_till = incred->times.renew_till; 361 362 cred->ticket.length = incred->ticket.length; 363 cred->ticket.data = incred->ticket.data; 364 365 cred->second_ticket.length = incred->second_ticket.length; 366 cred->second_ticket.data = incred->second_ticket.data; 367 368 /* XXX this one should also be filled in */ 369 cred->authdata = NULL; 370 371 cred->addresses = calloc(incred->addresses.len + 1, 372 sizeof(cred->addresses[0])); 373 if (cred->addresses == NULL) { 374 375 ret = ENOMEM; 376 goto fail; 377 } 378 379 for (i = 0; i < incred->addresses.len; i++) { 380 cc_data *addr; 381 addr = malloc(sizeof(*addr)); 382 if (addr == NULL) { 383 ret = ENOMEM; 384 goto fail; 385 } 386 addr->type = incred->addresses.val[i].addr_type; 387 addr->length = incred->addresses.val[i].address.length; 388 addr->data = malloc(addr->length); 389 if (addr->data == NULL) { 390 free(addr); 391 ret = ENOMEM; 392 goto fail; 393 } 394 memcpy(addr->data, incred->addresses.val[i].address.data, 395 addr->length); 396 cred->addresses[i] = addr; 397 } 398 cred->addresses[i] = NULL; 399 400 cred->ticket_flags = 0; 401 if (incred->flags.b.forwardable) 402 cred->ticket_flags |= KRB5_CCAPI_TKT_FLG_FORWARDABLE; 403 if (incred->flags.b.forwarded) 404 cred->ticket_flags |= KRB5_CCAPI_TKT_FLG_FORWARDED; 405 if (incred->flags.b.proxiable) 406 cred->ticket_flags |= KRB5_CCAPI_TKT_FLG_PROXIABLE; 407 if (incred->flags.b.proxy) 408 cred->ticket_flags |= KRB5_CCAPI_TKT_FLG_PROXY; 409 if (incred->flags.b.may_postdate) 410 cred->ticket_flags |= KRB5_CCAPI_TKT_FLG_MAY_POSTDATE; 411 if (incred->flags.b.postdated) 412 cred->ticket_flags |= KRB5_CCAPI_TKT_FLG_POSTDATED; 413 if (incred->flags.b.invalid) 414 cred->ticket_flags |= KRB5_CCAPI_TKT_FLG_INVALID; 415 if (incred->flags.b.renewable) 416 cred->ticket_flags |= KRB5_CCAPI_TKT_FLG_RENEWABLE; 417 if (incred->flags.b.initial) 418 cred->ticket_flags |= KRB5_CCAPI_TKT_FLG_INITIAL; 419 if (incred->flags.b.pre_authent) 420 cred->ticket_flags |= KRB5_CCAPI_TKT_FLG_PRE_AUTH; 421 if (incred->flags.b.hw_authent) 422 cred->ticket_flags |= KRB5_CCAPI_TKT_FLG_HW_AUTH; 423 if (incred->flags.b.transited_policy_checked) 424 cred->ticket_flags |= KRB5_CCAPI_TKT_FLG_TRANSIT_POLICY_CHECKED; 425 if (incred->flags.b.ok_as_delegate) 426 cred->ticket_flags |= KRB5_CCAPI_TKT_FLG_OK_AS_DELEGATE; 427 if (incred->flags.b.anonymous) 428 cred->ticket_flags |= KRB5_CCAPI_TKT_FLG_ANONYMOUS; 429 430 return 0; 431 432fail: 433 free_ccred(cred); 434 435 krb5_clear_error_message(context); 436 return ret; 437} 438 439static cc_int32 440get_cc_name(krb5_acc *a) 441{ 442 cc_string_t name; 443 cc_int32 error; 444 445 error = (*a->ccache->func->get_name)(a->ccache, &name); 446 if (error) 447 return error; 448 449 a->cache_name = strdup(name->data); 450 (*name->func->release)(name); 451 if (a->cache_name == NULL) 452 return ccErrNoMem; 453 return ccNoError; 454} 455 456 457static const char* KRB5_CALLCONV 458acc_get_name(krb5_context context, 459 krb5_ccache id) 460{ 461 krb5_acc *a = ACACHE(id); 462 int32_t error; 463 464 if (a->cache_name == NULL) { 465 krb5_error_code ret; 466 krb5_principal principal; 467 char *name; 468 469 ret = _krb5_get_default_principal_local(context, &principal); 470 if (ret) 471 return NULL; 472 473 ret = krb5_unparse_name(context, principal, &name); 474 krb5_free_principal(context, principal); 475 if (ret) 476 return NULL; 477 478 error = (*a->context->func->create_new_ccache)(a->context, 479 cc_credentials_v5, 480 name, 481 &a->ccache); 482 krb5_xfree(name); 483 if (error) 484 return NULL; 485 486 error = get_cc_name(a); 487 if (error) 488 return NULL; 489 } 490 491 return a->cache_name; 492} 493 494static krb5_error_code KRB5_CALLCONV 495acc_alloc(krb5_context context, krb5_ccache *id) 496{ 497 krb5_error_code ret; 498 cc_int32 error; 499 krb5_acc *a; 500 501 ret = init_ccapi(context); 502 if (ret) 503 return ret; 504 505 ret = krb5_data_alloc(&(*id)->data, sizeof(*a)); 506 if (ret) { 507 krb5_clear_error_message(context); 508 return ret; 509 } 510 511 a = ACACHE(*id); 512 513 error = (*init_func)(&a->context, ccapi_version_3, NULL, NULL); 514 if (error) { 515 krb5_data_free(&(*id)->data); 516 return translate_cc_error(context, error); 517 } 518 519 a->cache_name = NULL; 520 521 return 0; 522} 523 524static krb5_error_code KRB5_CALLCONV 525acc_resolve(krb5_context context, krb5_ccache *id, const char *res) 526{ 527 krb5_error_code ret; 528 cc_int32 error; 529 krb5_acc *a; 530 531 ret = acc_alloc(context, id); 532 if (ret) 533 return ret; 534 535 a = ACACHE(*id); 536 537 error = (*a->context->func->open_ccache)(a->context, res, &a->ccache); 538 if (error == ccNoError) { 539 cc_time_t offset; 540 error = get_cc_name(a); 541 if (error != ccNoError) { 542 acc_close(context, *id); 543 *id = NULL; 544 return translate_cc_error(context, error); 545 } 546 547 error = (*a->ccache->func->get_kdc_time_offset)(a->ccache, 548 cc_credentials_v5, 549 &offset); 550 if (error == 0) 551 context->kdc_sec_offset = offset; 552 553 } else if (error == ccErrCCacheNotFound) { 554 a->ccache = NULL; 555 a->cache_name = NULL; 556 } else { 557 *id = NULL; 558 return translate_cc_error(context, error); 559 } 560 561 return 0; 562} 563 564static krb5_error_code KRB5_CALLCONV 565acc_gen_new(krb5_context context, krb5_ccache *id) 566{ 567 krb5_error_code ret; 568 krb5_acc *a; 569 570 ret = acc_alloc(context, id); 571 if (ret) 572 return ret; 573 574 a = ACACHE(*id); 575 576 a->ccache = NULL; 577 a->cache_name = NULL; 578 579 return 0; 580} 581 582static krb5_error_code KRB5_CALLCONV 583acc_initialize(krb5_context context, 584 krb5_ccache id, 585 krb5_principal primary_principal) 586{ 587 krb5_acc *a = ACACHE(id); 588 krb5_error_code ret; 589 int32_t error; 590 char *name; 591 592 ret = krb5_unparse_name(context, primary_principal, &name); 593 if (ret) 594 return ret; 595 596 if (a->cache_name == NULL) { 597 error = (*a->context->func->create_new_ccache)(a->context, 598 cc_credentials_v5, 599 name, 600 &a->ccache); 601 free(name); 602 if (error == ccNoError) 603 error = get_cc_name(a); 604 } else { 605 cc_credentials_iterator_t iter; 606 cc_credentials_t ccred; 607 608 error = (*a->ccache->func->new_credentials_iterator)(a->ccache, &iter); 609 if (error) { 610 free(name); 611 return translate_cc_error(context, error); 612 } 613 614 while (1) { 615 error = (*iter->func->next)(iter, &ccred); 616 if (error) 617 break; 618 (*a->ccache->func->remove_credentials)(a->ccache, ccred); 619 (*ccred->func->release)(ccred); 620 } 621 (*iter->func->release)(iter); 622 623 error = (*a->ccache->func->set_principal)(a->ccache, 624 cc_credentials_v5, 625 name); 626 } 627 628 if (error == 0 && context->kdc_sec_offset) 629 error = (*a->ccache->func->set_kdc_time_offset)(a->ccache, 630 cc_credentials_v5, 631 context->kdc_sec_offset); 632 633 return translate_cc_error(context, error); 634} 635 636static krb5_error_code KRB5_CALLCONV 637acc_close(krb5_context context, 638 krb5_ccache id) 639{ 640 krb5_acc *a = ACACHE(id); 641 642 if (a->ccache) { 643 (*a->ccache->func->release)(a->ccache); 644 a->ccache = NULL; 645 } 646 if (a->cache_name) { 647 free(a->cache_name); 648 a->cache_name = NULL; 649 } 650 if (a->context) { 651 (*a->context->func->release)(a->context); 652 a->context = NULL; 653 } 654 krb5_data_free(&id->data); 655 return 0; 656} 657 658static krb5_error_code KRB5_CALLCONV 659acc_destroy(krb5_context context, 660 krb5_ccache id) 661{ 662 krb5_acc *a = ACACHE(id); 663 cc_int32 error = 0; 664 665 if (a->ccache) { 666 error = (*a->ccache->func->destroy)(a->ccache); 667 a->ccache = NULL; 668 } 669 if (a->context) { 670 error = (a->context->func->release)(a->context); 671 a->context = NULL; 672 } 673 return translate_cc_error(context, error); 674} 675 676static krb5_error_code KRB5_CALLCONV 677acc_store_cred(krb5_context context, 678 krb5_ccache id, 679 krb5_creds *creds) 680{ 681 krb5_acc *a = ACACHE(id); 682 cc_credentials_union cred; 683 cc_credentials_v5_t v5cred; 684 krb5_error_code ret; 685 cc_int32 error; 686 687 if (a->ccache == NULL) { 688 krb5_set_error_message(context, KRB5_CC_NOTFOUND, 689 N_("No API credential found", "")); 690 return KRB5_CC_NOTFOUND; 691 } 692 693 cred.version = cc_credentials_v5; 694 cred.credentials.credentials_v5 = &v5cred; 695 696 ret = make_ccred_from_cred(context, 697 creds, 698 &v5cred); 699 if (ret) 700 return ret; 701 702 error = (*a->ccache->func->store_credentials)(a->ccache, &cred); 703 if (error) 704 ret = translate_cc_error(context, error); 705 706 free_ccred(&v5cred); 707 708 return ret; 709} 710 711static krb5_error_code KRB5_CALLCONV 712acc_get_principal(krb5_context context, 713 krb5_ccache id, 714 krb5_principal *principal) 715{ 716 krb5_acc *a = ACACHE(id); 717 krb5_error_code ret; 718 int32_t error; 719 cc_string_t name; 720 721 if (a->ccache == NULL) { 722 krb5_set_error_message(context, KRB5_CC_NOTFOUND, 723 N_("No API credential found", "")); 724 return KRB5_CC_NOTFOUND; 725 } 726 727 error = (*a->ccache->func->get_principal)(a->ccache, 728 cc_credentials_v5, 729 &name); 730 if (error) 731 return translate_cc_error(context, error); 732 733 ret = krb5_parse_name(context, name->data, principal); 734 735 (*name->func->release)(name); 736 return ret; 737} 738 739static krb5_error_code KRB5_CALLCONV 740acc_get_first (krb5_context context, 741 krb5_ccache id, 742 krb5_cc_cursor *cursor) 743{ 744 cc_credentials_iterator_t iter; 745 krb5_acc *a = ACACHE(id); 746 int32_t error; 747 748 if (a->ccache == NULL) { 749 krb5_set_error_message(context, KRB5_CC_NOTFOUND, 750 N_("No API credential found", "")); 751 return KRB5_CC_NOTFOUND; 752 } 753 754 error = (*a->ccache->func->new_credentials_iterator)(a->ccache, &iter); 755 if (error) { 756 krb5_clear_error_message(context); 757 return ENOENT; 758 } 759 *cursor = iter; 760 return 0; 761} 762 763 764static krb5_error_code KRB5_CALLCONV 765acc_get_next (krb5_context context, 766 krb5_ccache id, 767 krb5_cc_cursor *cursor, 768 krb5_creds *creds) 769{ 770 cc_credentials_iterator_t iter = *cursor; 771 cc_credentials_t cred; 772 krb5_error_code ret; 773 int32_t error; 774 775 while (1) { 776 error = (*iter->func->next)(iter, &cred); 777 if (error) 778 return translate_cc_error(context, error); 779 if (cred->data->version == cc_credentials_v5) 780 break; 781 (*cred->func->release)(cred); 782 } 783 784 ret = make_cred_from_ccred(context, 785 cred->data->credentials.credentials_v5, 786 creds); 787 (*cred->func->release)(cred); 788 return ret; 789} 790 791static krb5_error_code KRB5_CALLCONV 792acc_end_get (krb5_context context, 793 krb5_ccache id, 794 krb5_cc_cursor *cursor) 795{ 796 cc_credentials_iterator_t iter = *cursor; 797 (*iter->func->release)(iter); 798 return 0; 799} 800 801static krb5_error_code KRB5_CALLCONV 802acc_remove_cred(krb5_context context, 803 krb5_ccache id, 804 krb5_flags which, 805 krb5_creds *cred) 806{ 807 cc_credentials_iterator_t iter; 808 krb5_acc *a = ACACHE(id); 809 cc_credentials_t ccred; 810 krb5_error_code ret; 811 cc_int32 error; 812 char *client, *server; 813 814 if (a->ccache == NULL) { 815 krb5_set_error_message(context, KRB5_CC_NOTFOUND, 816 N_("No API credential found", "")); 817 return KRB5_CC_NOTFOUND; 818 } 819 820 if (cred->client) { 821 ret = krb5_unparse_name(context, cred->client, &client); 822 if (ret) 823 return ret; 824 } else 825 client = NULL; 826 827 ret = krb5_unparse_name(context, cred->server, &server); 828 if (ret) { 829 free(client); 830 return ret; 831 } 832 833 error = (*a->ccache->func->new_credentials_iterator)(a->ccache, &iter); 834 if (error) { 835 free(server); 836 free(client); 837 return translate_cc_error(context, error); 838 } 839 840 ret = KRB5_CC_NOTFOUND; 841 while (1) { 842 cc_credentials_v5_t *v5cred; 843 844 error = (*iter->func->next)(iter, &ccred); 845 if (error) 846 break; 847 848 if (ccred->data->version != cc_credentials_v5) 849 goto next; 850 851 v5cred = ccred->data->credentials.credentials_v5; 852 853 if (client && strcmp(v5cred->client, client) != 0) 854 goto next; 855 856 if (strcmp(v5cred->server, server) != 0) 857 goto next; 858 859 (*a->ccache->func->remove_credentials)(a->ccache, ccred); 860 ret = 0; 861 next: 862 (*ccred->func->release)(ccred); 863 } 864 865 (*iter->func->release)(iter); 866 867 if (ret) 868 krb5_set_error_message(context, ret, 869 N_("Can't find credential %s in cache", 870 "principal"), server); 871 free(server); 872 free(client); 873 874 return ret; 875} 876 877static krb5_error_code KRB5_CALLCONV 878acc_set_flags(krb5_context context, 879 krb5_ccache id, 880 krb5_flags flags) 881{ 882 return 0; 883} 884 885static int KRB5_CALLCONV 886acc_get_version(krb5_context context, 887 krb5_ccache id) 888{ 889 return 0; 890} 891 892struct cache_iter { 893 cc_context_t context; 894 cc_ccache_iterator_t iter; 895}; 896 897static krb5_error_code KRB5_CALLCONV 898acc_get_cache_first(krb5_context context, krb5_cc_cursor *cursor) 899{ 900 struct cache_iter *iter; 901 krb5_error_code ret; 902 cc_int32 error; 903 904 ret = init_ccapi(context); 905 if (ret) 906 return ret; 907 908 iter = calloc(1, sizeof(*iter)); 909 if (iter == NULL) 910 return krb5_enomem(context); 911 912 error = (*init_func)(&iter->context, ccapi_version_3, NULL, NULL); 913 if (error) { 914 free(iter); 915 return translate_cc_error(context, error); 916 } 917 918 error = (*iter->context->func->new_ccache_iterator)(iter->context, 919 &iter->iter); 920 if (error) { 921 free(iter); 922 krb5_clear_error_message(context); 923 return ENOENT; 924 } 925 *cursor = iter; 926 return 0; 927} 928 929static krb5_error_code KRB5_CALLCONV 930acc_get_cache_next(krb5_context context, krb5_cc_cursor cursor, krb5_ccache *id) 931{ 932 struct cache_iter *iter = cursor; 933 cc_ccache_t cache; 934 krb5_acc *a; 935 krb5_error_code ret; 936 int32_t error; 937 938 error = (*iter->iter->func->next)(iter->iter, &cache); 939 if (error) 940 return translate_cc_error(context, error); 941 942 ret = _krb5_cc_allocate(context, &krb5_acc_ops, id); 943 if (ret) { 944 (*cache->func->release)(cache); 945 return ret; 946 } 947 948 ret = acc_alloc(context, id); 949 if (ret) { 950 (*cache->func->release)(cache); 951 free(*id); 952 return ret; 953 } 954 955 a = ACACHE(*id); 956 a->ccache = cache; 957 958 error = get_cc_name(a); 959 if (error) { 960 acc_close(context, *id); 961 *id = NULL; 962 return translate_cc_error(context, error); 963 } 964 return 0; 965} 966 967static krb5_error_code KRB5_CALLCONV 968acc_end_cache_get(krb5_context context, krb5_cc_cursor cursor) 969{ 970 struct cache_iter *iter = cursor; 971 972 (*iter->iter->func->release)(iter->iter); 973 iter->iter = NULL; 974 (*iter->context->func->release)(iter->context); 975 iter->context = NULL; 976 free(iter); 977 return 0; 978} 979 980static krb5_error_code KRB5_CALLCONV 981acc_move(krb5_context context, krb5_ccache from, krb5_ccache to) 982{ 983 krb5_acc *afrom = ACACHE(from); 984 krb5_acc *ato = ACACHE(to); 985 int32_t error; 986 987 if (ato->ccache == NULL) { 988 cc_string_t name; 989 990 error = (*afrom->ccache->func->get_principal)(afrom->ccache, 991 cc_credentials_v5, 992 &name); 993 if (error) 994 return translate_cc_error(context, error); 995 996 error = (*ato->context->func->create_new_ccache)(ato->context, 997 cc_credentials_v5, 998 name->data, 999 &ato->ccache); 1000 (*name->func->release)(name); 1001 if (error) 1002 return translate_cc_error(context, error); 1003 } 1004 1005 error = (*ato->ccache->func->move)(afrom->ccache, ato->ccache); 1006 1007 acc_destroy(context, from); 1008 1009 return translate_cc_error(context, error); 1010} 1011 1012static krb5_error_code KRB5_CALLCONV 1013acc_get_default_name(krb5_context context, char **str) 1014{ 1015 krb5_error_code ret; 1016 cc_context_t cc; 1017 cc_string_t name; 1018 int32_t error; 1019 1020 ret = init_ccapi(context); 1021 if (ret) 1022 return ret; 1023 1024 error = (*init_func)(&cc, ccapi_version_3, NULL, NULL); 1025 if (error) 1026 return translate_cc_error(context, error); 1027 1028 error = (*cc->func->get_default_ccache_name)(cc, &name); 1029 if (error) { 1030 (*cc->func->release)(cc); 1031 return translate_cc_error(context, error); 1032 } 1033 1034 error = asprintf(str, "API:%s", name->data); 1035 (*name->func->release)(name); 1036 (*cc->func->release)(cc); 1037 1038 if (error < 0 || *str == NULL) 1039 return krb5_enomem(context); 1040 return 0; 1041} 1042 1043static krb5_error_code KRB5_CALLCONV 1044acc_set_default(krb5_context context, krb5_ccache id) 1045{ 1046 krb5_acc *a = ACACHE(id); 1047 cc_int32 error; 1048 1049 if (a->ccache == NULL) { 1050 krb5_set_error_message(context, KRB5_CC_NOTFOUND, 1051 N_("No API credential found", "")); 1052 return KRB5_CC_NOTFOUND; 1053 } 1054 1055 error = (*a->ccache->func->set_default)(a->ccache); 1056 if (error) 1057 return translate_cc_error(context, error); 1058 1059 return 0; 1060} 1061 1062static krb5_error_code KRB5_CALLCONV 1063acc_lastchange(krb5_context context, krb5_ccache id, krb5_timestamp *mtime) 1064{ 1065 krb5_acc *a = ACACHE(id); 1066 cc_int32 error; 1067 cc_time_t t; 1068 1069 if (a->ccache == NULL) { 1070 krb5_set_error_message(context, KRB5_CC_NOTFOUND, 1071 N_("No API credential found", "")); 1072 return KRB5_CC_NOTFOUND; 1073 } 1074 1075 error = (*a->ccache->func->get_change_time)(a->ccache, &t); 1076 if (error) 1077 return translate_cc_error(context, error); 1078 1079 *mtime = t; 1080 1081 return 0; 1082} 1083 1084/** 1085 * Variable containing the API based credential cache implemention. 1086 * 1087 * @ingroup krb5_ccache 1088 */ 1089 1090KRB5_LIB_VARIABLE const krb5_cc_ops krb5_acc_ops = { 1091 KRB5_CC_OPS_VERSION, 1092 "API", 1093 acc_get_name, 1094 acc_resolve, 1095 acc_gen_new, 1096 acc_initialize, 1097 acc_destroy, 1098 acc_close, 1099 acc_store_cred, 1100 NULL, /* acc_retrieve */ 1101 acc_get_principal, 1102 acc_get_first, 1103 acc_get_next, 1104 acc_end_get, 1105 acc_remove_cred, 1106 acc_set_flags, 1107 acc_get_version, 1108 acc_get_cache_first, 1109 acc_get_cache_next, 1110 acc_end_cache_get, 1111 acc_move, 1112 acc_get_default_name, 1113 acc_set_default, 1114 acc_lastchange, 1115 NULL, 1116 NULL, 1117}; 1118 1119#endif 1120