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