1/* $NetBSD: cache.c,v 1.2 2017/01/28 21:31:49 christos Exp $ */ 2 3/* 4 * Copyright (c) 1997 - 2008 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 40/** 41 * @page krb5_ccache_intro The credential cache functions 42 * @section section_krb5_ccache Kerberos credential caches 43 * 44 * krb5_ccache structure holds a Kerberos credential cache. 45 * 46 * Heimdal support the follow types of credential caches: 47 * 48 * - SCC 49 * Store the credential in a database 50 * - FILE 51 * Store the credential in memory 52 * - MEMORY 53 * Store the credential in memory 54 * - API 55 * A credential cache server based solution for Mac OS X 56 * - KCM 57 * A credential cache server based solution for all platforms 58 * 59 * @subsection Example 60 * 61 * This is a minimalistic version of klist: 62@code 63#include <krb5/krb5.h> 64 65int 66main (int argc, char **argv) 67{ 68 krb5_context context; 69 krb5_cc_cursor cursor; 70 krb5_error_code ret; 71 krb5_ccache id; 72 krb5_creds creds; 73 74 if (krb5_init_context (&context) != 0) 75 errx(1, "krb5_context"); 76 77 ret = krb5_cc_default (context, &id); 78 if (ret) 79 krb5_err(context, 1, ret, "krb5_cc_default"); 80 81 ret = krb5_cc_start_seq_get(context, id, &cursor); 82 if (ret) 83 krb5_err(context, 1, ret, "krb5_cc_start_seq_get"); 84 85 while((ret = krb5_cc_next_cred(context, id, &cursor, &creds)) == 0){ 86 char *principal; 87 88 krb5_unparse_name(context, creds.server, &principal); 89 printf("principal: %s\\n", principal); 90 free(principal); 91 krb5_free_cred_contents (context, &creds); 92 } 93 ret = krb5_cc_end_seq_get(context, id, &cursor); 94 if (ret) 95 krb5_err(context, 1, ret, "krb5_cc_end_seq_get"); 96 97 krb5_cc_close(context, id); 98 99 krb5_free_context(context); 100 return 0; 101} 102* @endcode 103*/ 104 105/** 106 * Add a new ccache type with operations `ops', overwriting any 107 * existing one if `override'. 108 * 109 * @param context a Keberos context 110 * @param ops type of plugin symbol 111 * @param override flag to select if the registration is to overide 112 * an existing ops with the same name. 113 * 114 * @return Return an error code or 0, see krb5_get_error_message(). 115 * 116 * @ingroup krb5_ccache 117 */ 118 119KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 120krb5_cc_register(krb5_context context, 121 const krb5_cc_ops *ops, 122 krb5_boolean override) 123{ 124 int i; 125 126 for(i = 0; i < context->num_cc_ops && context->cc_ops[i]->prefix; i++) { 127 if(strcmp(context->cc_ops[i]->prefix, ops->prefix) == 0) { 128 if(!override) { 129 krb5_set_error_message(context, 130 KRB5_CC_TYPE_EXISTS, 131 N_("cache type %s already exists", "type"), 132 ops->prefix); 133 return KRB5_CC_TYPE_EXISTS; 134 } 135 break; 136 } 137 } 138 if(i == context->num_cc_ops) { 139 const krb5_cc_ops **o = realloc(rk_UNCONST(context->cc_ops), 140 (context->num_cc_ops + 1) * 141 sizeof(context->cc_ops[0])); 142 if(o == NULL) { 143 krb5_set_error_message(context, KRB5_CC_NOMEM, 144 N_("malloc: out of memory", "")); 145 return KRB5_CC_NOMEM; 146 } 147 context->cc_ops = o; 148 context->cc_ops[context->num_cc_ops] = NULL; 149 context->num_cc_ops++; 150 } 151 context->cc_ops[i] = ops; 152 return 0; 153} 154 155/* 156 * Allocate the memory for a `id' and the that function table to 157 * `ops'. Returns 0 or and error code. 158 */ 159 160KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 161_krb5_cc_allocate(krb5_context context, 162 const krb5_cc_ops *ops, 163 krb5_ccache *id) 164{ 165 krb5_ccache p; 166 167 p = calloc(1, sizeof(*p)); 168 if (p == NULL) { 169 krb5_set_error_message(context, KRB5_CC_NOMEM, 170 N_("malloc: out of memory", "")); 171 return KRB5_CC_NOMEM; 172 } 173 p->ops = ops; 174 *id = p; 175 176 return 0; 177} 178 179/* 180 * Allocate memory for a new ccache in `id' with operations `ops' 181 * and name `residual'. Return 0 or an error code. 182 */ 183 184static krb5_error_code 185allocate_ccache (krb5_context context, 186 const krb5_cc_ops *ops, 187 const char *residual, 188 krb5_ccache *id) 189{ 190 krb5_error_code ret; 191#ifdef KRB5_USE_PATH_TOKENS 192 char * exp_residual = NULL; 193 int filepath; 194 195 filepath = (strcmp("FILE", ops->prefix) == 0 196 || strcmp("DIR", ops->prefix) == 0 197 || strcmp("SCC", ops->prefix) == 0); 198 199 ret = _krb5_expand_path_tokens(context, residual, filepath, &exp_residual); 200 if (ret) 201 return ret; 202 203 residual = exp_residual; 204#endif 205 206 ret = _krb5_cc_allocate(context, ops, id); 207 if (ret) { 208#ifdef KRB5_USE_PATH_TOKENS 209 if (exp_residual) 210 free(exp_residual); 211#endif 212 return ret; 213 } 214 215 ret = (*id)->ops->resolve(context, id, residual); 216 if(ret) { 217 free(*id); 218 *id = NULL; 219 } 220 221#ifdef KRB5_USE_PATH_TOKENS 222 if (exp_residual) 223 free(exp_residual); 224#endif 225 226 return ret; 227} 228 229static int 230is_possible_path_name(const char * name) 231{ 232 const char * colon; 233 234 if ((colon = strchr(name, ':')) == NULL) 235 return TRUE; 236 237#ifdef _WIN32 238 /* <drive letter>:\path\to\cache ? */ 239 240 if (colon == name + 1 && 241 strchr(colon + 1, ':') == NULL) 242 return TRUE; 243#endif 244 245 return FALSE; 246} 247 248/** 249 * Find and allocate a ccache in `id' from the specification in `residual'. 250 * If the ccache name doesn't contain any colon, interpret it as a file name. 251 * 252 * @param context a Keberos context. 253 * @param name string name of a credential cache. 254 * @param id return pointer to a found credential cache. 255 * 256 * @return Return 0 or an error code. In case of an error, id is set 257 * to NULL, see krb5_get_error_message(). 258 * 259 * @ingroup krb5_ccache 260 */ 261 262 263KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 264krb5_cc_resolve(krb5_context context, 265 const char *name, 266 krb5_ccache *id) 267{ 268 int i; 269 270 *id = NULL; 271 272 for(i = 0; i < context->num_cc_ops && context->cc_ops[i]->prefix; i++) { 273 size_t prefix_len = strlen(context->cc_ops[i]->prefix); 274 275 if(strncmp(context->cc_ops[i]->prefix, name, prefix_len) == 0 276 && name[prefix_len] == ':') { 277 return allocate_ccache (context, context->cc_ops[i], 278 name + prefix_len + 1, 279 id); 280 } 281 } 282 if (is_possible_path_name(name)) 283 return allocate_ccache (context, &krb5_fcc_ops, name, id); 284 else { 285 krb5_set_error_message(context, KRB5_CC_UNKNOWN_TYPE, 286 N_("unknown ccache type %s", "name"), name); 287 return KRB5_CC_UNKNOWN_TYPE; 288 } 289} 290 291/** 292 * Generates a new unique ccache of `type` in `id'. If `type' is NULL, 293 * the library chooses the default credential cache type. The supplied 294 * `hint' (that can be NULL) is a string that the credential cache 295 * type can use to base the name of the credential on, this is to make 296 * it easier for the user to differentiate the credentials. 297 * 298 * @return Return an error code or 0, see krb5_get_error_message(). 299 * 300 * @ingroup krb5_ccache 301 */ 302 303KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 304krb5_cc_new_unique(krb5_context context, const char *type, 305 const char *hint, krb5_ccache *id) 306{ 307 const krb5_cc_ops *ops; 308 krb5_error_code ret; 309 310 ops = krb5_cc_get_prefix_ops(context, type); 311 if (ops == NULL) { 312 krb5_set_error_message(context, KRB5_CC_UNKNOWN_TYPE, 313 "Credential cache type %s is unknown", type); 314 return KRB5_CC_UNKNOWN_TYPE; 315 } 316 317 ret = _krb5_cc_allocate(context, ops, id); 318 if (ret) 319 return ret; 320 ret = (*id)->ops->gen_new(context, id); 321 if (ret) { 322 free(*id); 323 *id = NULL; 324 } 325 return ret; 326} 327 328/** 329 * Return the name of the ccache `id' 330 * 331 * @ingroup krb5_ccache 332 */ 333 334 335KRB5_LIB_FUNCTION const char* KRB5_LIB_CALL 336krb5_cc_get_name(krb5_context context, 337 krb5_ccache id) 338{ 339 return id->ops->get_name(context, id); 340} 341 342/** 343 * Return the type of the ccache `id'. 344 * 345 * @ingroup krb5_ccache 346 */ 347 348 349KRB5_LIB_FUNCTION const char* KRB5_LIB_CALL 350krb5_cc_get_type(krb5_context context, 351 krb5_ccache id) 352{ 353 return id->ops->prefix; 354} 355 356/** 357 * Return the complete resolvable name the cache 358 359 * @param context a Keberos context 360 * @param id return pointer to a found credential cache 361 * @param str the returned name of a credential cache, free with krb5_xfree() 362 * 363 * @return Returns 0 or an error (and then *str is set to NULL). 364 * 365 * @ingroup krb5_ccache 366 */ 367 368 369KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 370krb5_cc_get_full_name(krb5_context context, 371 krb5_ccache id, 372 char **str) 373{ 374 const char *type, *name; 375 376 *str = NULL; 377 378 type = krb5_cc_get_type(context, id); 379 if (type == NULL) { 380 krb5_set_error_message(context, KRB5_CC_UNKNOWN_TYPE, 381 "cache have no name of type"); 382 return KRB5_CC_UNKNOWN_TYPE; 383 } 384 385 name = krb5_cc_get_name(context, id); 386 if (name == NULL) { 387 krb5_set_error_message(context, KRB5_CC_BADNAME, 388 "cache of type %s have no name", type); 389 return KRB5_CC_BADNAME; 390 } 391 392 if (asprintf(str, "%s:%s", type, name) == -1) { 393 *str = NULL; 394 return krb5_enomem(context); 395 } 396 return 0; 397} 398 399/** 400 * Return krb5_cc_ops of a the ccache `id'. 401 * 402 * @ingroup krb5_ccache 403 */ 404 405 406KRB5_LIB_FUNCTION const krb5_cc_ops * KRB5_LIB_CALL 407krb5_cc_get_ops(krb5_context context, krb5_ccache id) 408{ 409 return id->ops; 410} 411 412/* 413 * Expand variables in `str' into `res' 414 */ 415 416KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 417_krb5_expand_default_cc_name(krb5_context context, const char *str, char **res) 418{ 419 int filepath; 420 421 filepath = (strncmp("FILE:", str, 5) == 0 422 || strncmp("DIR:", str, 4) == 0 423 || strncmp("SCC:", str, 4) == 0); 424 425 return _krb5_expand_path_tokens(context, str, filepath, res); 426} 427 428/* 429 * Return non-zero if envirnoment that will determine default krb5cc 430 * name has changed. 431 */ 432 433static int 434environment_changed(krb5_context context) 435{ 436 const char *e; 437 438 /* if the cc name was set, don't change it */ 439 if (context->default_cc_name_set) 440 return 0; 441 442 /* XXX performance: always ask KCM/API if default name has changed */ 443 if (context->default_cc_name && 444 (strncmp(context->default_cc_name, "KCM:", 4) == 0 || 445 strncmp(context->default_cc_name, "API:", 4) == 0)) 446 return 1; 447 448 if(issuid()) 449 return 0; 450 451 e = getenv("KRB5CCNAME"); 452 if (e == NULL) { 453 if (context->default_cc_name_env) { 454 free(context->default_cc_name_env); 455 context->default_cc_name_env = NULL; 456 return 1; 457 } 458 } else { 459 if (context->default_cc_name_env == NULL) 460 return 1; 461 if (strcmp(e, context->default_cc_name_env) != 0) 462 return 1; 463 } 464 return 0; 465} 466 467/** 468 * Switch the default default credential cache for a specific 469 * credcache type (and name for some implementations). 470 * 471 * @return Return an error code or 0, see krb5_get_error_message(). 472 * 473 * @ingroup krb5_ccache 474 */ 475 476KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 477krb5_cc_switch(krb5_context context, krb5_ccache id) 478{ 479#ifdef _WIN32 480 _krb5_set_default_cc_name_to_registry(context, id); 481#endif 482 483 if (id->ops->set_default == NULL) 484 return 0; 485 486 return (*id->ops->set_default)(context, id); 487} 488 489/** 490 * Return true if the default credential cache support switch 491 * 492 * @ingroup krb5_ccache 493 */ 494 495KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL 496krb5_cc_support_switch(krb5_context context, const char *type) 497{ 498 const krb5_cc_ops *ops; 499 500 ops = krb5_cc_get_prefix_ops(context, type); 501 if (ops && ops->set_default) 502 return 1; 503 return FALSE; 504} 505 506/** 507 * Set the default cc name for `context' to `name'. 508 * 509 * @ingroup krb5_ccache 510 */ 511 512KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 513krb5_cc_set_default_name(krb5_context context, const char *name) 514{ 515 krb5_error_code ret = 0; 516 char *p = NULL, *exp_p = NULL; 517 int filepath; 518 const krb5_cc_ops *ops = KRB5_DEFAULT_CCTYPE; 519 520 if (name == NULL) { 521 const char *e = NULL; 522 523 if (!issuid()) { 524 e = getenv("KRB5CCNAME"); 525 if (e) { 526 p = strdup(e); 527 if (context->default_cc_name_env) 528 free(context->default_cc_name_env); 529 context->default_cc_name_env = strdup(e); 530 } 531 } 532 533#ifdef _WIN32 534 if (p == NULL) { 535 p = _krb5_get_default_cc_name_from_registry(context); 536 } 537#endif 538 if (p == NULL) { 539 e = krb5_config_get_string(context, NULL, "libdefaults", 540 "default_cc_name", NULL); 541 if (e) { 542 ret = _krb5_expand_default_cc_name(context, e, &p); 543 if (ret) 544 return ret; 545 } 546 } 547 if (p == NULL) { 548 e = krb5_config_get_string(context, NULL, "libdefaults", 549 "default_cc_type", NULL); 550 if (e) { 551 ops = krb5_cc_get_prefix_ops(context, e); 552 if (ops == NULL) { 553 krb5_set_error_message(context, 554 KRB5_CC_UNKNOWN_TYPE, 555 "Credential cache type %s " 556 "is unknown", e); 557 return KRB5_CC_UNKNOWN_TYPE; 558 } 559 } 560 } 561#ifdef _WIN32 562 if (p == NULL) { 563 /* 564 * If the MSLSA ccache type has a principal name, 565 * use it as the default. 566 */ 567 krb5_ccache id; 568 ret = krb5_cc_resolve(context, "MSLSA:", &id); 569 if (ret == 0) { 570 krb5_principal princ; 571 ret = krb5_cc_get_principal(context, id, &princ); 572 if (ret == 0) { 573 krb5_free_principal(context, princ); 574 p = strdup("MSLSA:"); 575 } 576 krb5_cc_close(context, id); 577 } 578 } 579 if (p == NULL) { 580 /* 581 * If the API:krb5cc ccache can be resolved, 582 * use it as the default. 583 */ 584 krb5_ccache api_id; 585 ret = krb5_cc_resolve(context, "API:krb5cc", &api_id); 586 if (ret == 0) 587 krb5_cc_close(context, api_id); 588 } 589 /* Otherwise, fallback to the FILE ccache */ 590#endif 591 if (p == NULL) { 592 ret = (*ops->get_default_name)(context, &p); 593 if (ret) 594 return ret; 595 } 596 context->default_cc_name_set = 0; 597 } else { 598 p = strdup(name); 599 if (p == NULL) 600 return krb5_enomem(context); 601 context->default_cc_name_set = 1; 602 } 603 604 filepath = (strncmp("FILE:", p, 5) == 0 605 || strncmp("DIR:", p, 4) == 0 606 || strncmp("SCC:", p, 4) == 0); 607 608 ret = _krb5_expand_path_tokens(context, p, filepath, &exp_p); 609 free(p); 610 p = exp_p; 611 if (ret) 612 return ret; 613 614 if (context->default_cc_name) 615 free(context->default_cc_name); 616 617 context->default_cc_name = p; 618 619 return 0; 620} 621 622/** 623 * Return a pointer to a context static string containing the default 624 * ccache name. 625 * 626 * @return String to the default credential cache name. 627 * 628 * @ingroup krb5_ccache 629 */ 630 631 632KRB5_LIB_FUNCTION const char* KRB5_LIB_CALL 633krb5_cc_default_name(krb5_context context) 634{ 635 if (context->default_cc_name == NULL || environment_changed(context)) 636 krb5_cc_set_default_name(context, NULL); 637 638 return context->default_cc_name; 639} 640 641/** 642 * Open the default ccache in `id'. 643 * 644 * @return Return an error code or 0, see krb5_get_error_message(). 645 * 646 * @ingroup krb5_ccache 647 */ 648 649 650KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 651krb5_cc_default(krb5_context context, 652 krb5_ccache *id) 653{ 654 const char *p = krb5_cc_default_name(context); 655 656 if (p == NULL) 657 return krb5_enomem(context); 658 return krb5_cc_resolve(context, p, id); 659} 660 661/** 662 * Create a new ccache in `id' for `primary_principal'. 663 * 664 * @return Return an error code or 0, see krb5_get_error_message(). 665 * 666 * @ingroup krb5_ccache 667 */ 668 669 670KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 671krb5_cc_initialize(krb5_context context, 672 krb5_ccache id, 673 krb5_principal primary_principal) 674{ 675 krb5_error_code ret; 676 677 ret = (*id->ops->init)(context, id, primary_principal); 678 if (ret == 0) 679 id->initialized = 1; 680 return ret; 681} 682 683 684/** 685 * Remove the ccache `id'. 686 * 687 * @return Return an error code or 0, see krb5_get_error_message(). 688 * 689 * @ingroup krb5_ccache 690 */ 691 692 693KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 694krb5_cc_destroy(krb5_context context, 695 krb5_ccache id) 696{ 697 krb5_error_code ret; 698 699 ret = (*id->ops->destroy)(context, id); 700 krb5_cc_close (context, id); 701 return ret; 702} 703 704/** 705 * Stop using the ccache `id' and free the related resources. 706 * 707 * @return Return an error code or 0, see krb5_get_error_message(). 708 * 709 * @ingroup krb5_ccache 710 */ 711 712 713KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 714krb5_cc_close(krb5_context context, 715 krb5_ccache id) 716{ 717 krb5_error_code ret; 718 ret = (*id->ops->close)(context, id); 719 free(id); 720 return ret; 721} 722 723/** 724 * Store `creds' in the ccache `id'. 725 * 726 * @return Return an error code or 0, see krb5_get_error_message(). 727 * 728 * @ingroup krb5_ccache 729 */ 730 731 732KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 733krb5_cc_store_cred(krb5_context context, 734 krb5_ccache id, 735 krb5_creds *creds) 736{ 737 krb5_error_code ret; 738 krb5_data realm; 739 740 ret = (*id->ops->store)(context, id, creds); 741 742 /* Look for and mark the first root TGT's realm as the start realm */ 743 if (ret == 0 && id->initialized && 744 krb5_principal_is_root_krbtgt(context, creds->server)) { 745 746 id->initialized = 0; 747 realm.length = strlen(creds->server->realm); 748 realm.data = creds->server->realm; 749 (void) krb5_cc_set_config(context, id, NULL, "start_realm", &realm); 750 } else if (ret == 0 && id->initialized && 751 krb5_is_config_principal(context, creds->server) && 752 strcmp(creds->server->name.name_string.val[1], "start_realm") == 0) { 753 754 /* 755 * But if the caller is storing a start_realm ccconfig, then 756 * stop looking for root TGTs to mark as the start_realm. 757 * 758 * By honoring any start_realm cc config stored, we interop 759 * both, with ccache implementations that don't preserve 760 * insertion order, and Kerberos implementations that store this 761 * cc config before the TGT. 762 */ 763 id->initialized = 0; 764 } 765 return ret; 766} 767 768/** 769 * Retrieve the credential identified by `mcreds' (and `whichfields') 770 * from `id' in `creds'. 'creds' must be free by the caller using 771 * krb5_free_cred_contents. 772 * 773 * @param context A Kerberos 5 context 774 * @param id a Kerberos 5 credential cache 775 * @param whichfields what fields to use for matching credentials, same 776 * flags as whichfields in krb5_compare_creds() 777 * @param mcreds template credential to use for comparing 778 * @param creds returned credential, free with krb5_free_cred_contents() 779 * 780 * @return Return an error code or 0, see krb5_get_error_message(). 781 * 782 * @ingroup krb5_ccache 783 */ 784 785 786KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 787krb5_cc_retrieve_cred(krb5_context context, 788 krb5_ccache id, 789 krb5_flags whichfields, 790 const krb5_creds *mcreds, 791 krb5_creds *creds) 792{ 793 krb5_error_code ret; 794 krb5_cc_cursor cursor; 795 796 if (id->ops->retrieve != NULL) { 797 return (*id->ops->retrieve)(context, id, whichfields, 798 mcreds, creds); 799 } 800 801 ret = krb5_cc_start_seq_get(context, id, &cursor); 802 if (ret) 803 return ret; 804 while((ret = krb5_cc_next_cred(context, id, &cursor, creds)) == 0){ 805 if(krb5_compare_creds(context, whichfields, mcreds, creds)){ 806 ret = 0; 807 break; 808 } 809 krb5_free_cred_contents (context, creds); 810 } 811 krb5_cc_end_seq_get(context, id, &cursor); 812 return ret; 813} 814 815/** 816 * Return the principal of `id' in `principal'. 817 * 818 * @return Return an error code or 0, see krb5_get_error_message(). 819 * 820 * @ingroup krb5_ccache 821 */ 822 823 824KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 825krb5_cc_get_principal(krb5_context context, 826 krb5_ccache id, 827 krb5_principal *principal) 828{ 829 return (*id->ops->get_princ)(context, id, principal); 830} 831 832/** 833 * Start iterating over `id', `cursor' is initialized to the 834 * beginning. Caller must free the cursor with krb5_cc_end_seq_get(). 835 * 836 * @return Return an error code or 0, see krb5_get_error_message(). 837 * 838 * @ingroup krb5_ccache 839 */ 840 841 842KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 843krb5_cc_start_seq_get (krb5_context context, 844 const krb5_ccache id, 845 krb5_cc_cursor *cursor) 846{ 847 return (*id->ops->get_first)(context, id, cursor); 848} 849 850/** 851 * Retrieve the next cred pointed to by (`id', `cursor') in `creds' 852 * and advance `cursor'. 853 * 854 * @return Return an error code or 0, see krb5_get_error_message(). 855 * 856 * @ingroup krb5_ccache 857 */ 858 859 860KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 861krb5_cc_next_cred (krb5_context context, 862 const krb5_ccache id, 863 krb5_cc_cursor *cursor, 864 krb5_creds *creds) 865{ 866 return (*id->ops->get_next)(context, id, cursor, creds); 867} 868 869/** 870 * Destroy the cursor `cursor'. 871 * 872 * @ingroup krb5_ccache 873 */ 874 875 876KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 877krb5_cc_end_seq_get (krb5_context context, 878 const krb5_ccache id, 879 krb5_cc_cursor *cursor) 880{ 881 return (*id->ops->end_get)(context, id, cursor); 882} 883 884/** 885 * Remove the credential identified by `cred', `which' from `id'. 886 * 887 * @ingroup krb5_ccache 888 */ 889 890 891KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 892krb5_cc_remove_cred(krb5_context context, 893 krb5_ccache id, 894 krb5_flags which, 895 krb5_creds *cred) 896{ 897 if(id->ops->remove_cred == NULL) { 898 krb5_set_error_message(context, 899 EACCES, 900 "ccache %s does not support remove_cred", 901 id->ops->prefix); 902 return EACCES; /* XXX */ 903 } 904 return (*id->ops->remove_cred)(context, id, which, cred); 905} 906 907/** 908 * Set the flags of `id' to `flags'. 909 * 910 * @ingroup krb5_ccache 911 */ 912 913 914KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 915krb5_cc_set_flags(krb5_context context, 916 krb5_ccache id, 917 krb5_flags flags) 918{ 919 return (*id->ops->set_flags)(context, id, flags); 920} 921 922/** 923 * Get the flags of `id', store them in `flags'. 924 * 925 * @ingroup krb5_ccache 926 */ 927 928KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 929krb5_cc_get_flags(krb5_context context, 930 krb5_ccache id, 931 krb5_flags *flags) 932{ 933 *flags = 0; 934 return 0; 935} 936 937/** 938 * Copy the contents of `from' to `to' if the given match function 939 * return true. 940 * 941 * @param context A Kerberos 5 context. 942 * @param from the cache to copy data from. 943 * @param to the cache to copy data to. 944 * @param match a match function that should return TRUE if cred argument should be copied, if NULL, all credentials are copied. 945 * @param matchctx context passed to match function. 946 * @param matched set to true if there was a credential that matched, may be NULL. 947 * 948 * @return Return an error code or 0, see krb5_get_error_message(). 949 * 950 * @ingroup krb5_ccache 951 */ 952 953KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 954krb5_cc_copy_match_f(krb5_context context, 955 const krb5_ccache from, 956 krb5_ccache to, 957 krb5_boolean (*match)(krb5_context, void *, const krb5_creds *), 958 void *matchctx, 959 unsigned int *matched) 960{ 961 krb5_error_code ret; 962 krb5_cc_cursor cursor; 963 krb5_creds cred; 964 krb5_principal princ; 965 966 if (matched) 967 *matched = 0; 968 969 ret = krb5_cc_get_principal(context, from, &princ); 970 if (ret) 971 return ret; 972 ret = krb5_cc_initialize(context, to, princ); 973 if (ret) { 974 krb5_free_principal(context, princ); 975 return ret; 976 } 977 ret = krb5_cc_start_seq_get(context, from, &cursor); 978 if (ret) { 979 krb5_free_principal(context, princ); 980 return ret; 981 } 982 983 while ((ret = krb5_cc_next_cred(context, from, &cursor, &cred)) == 0) { 984 if (match == NULL || (*match)(context, matchctx, &cred)) { 985 if (matched) 986 (*matched)++; 987 ret = krb5_cc_store_cred(context, to, &cred); 988 if (ret) 989 break; 990 } 991 krb5_free_cred_contents(context, &cred); 992 } 993 krb5_cc_end_seq_get(context, from, &cursor); 994 krb5_free_principal(context, princ); 995 if (ret == KRB5_CC_END) 996 ret = 0; 997 return ret; 998} 999 1000/** 1001 * Just like krb5_cc_copy_match_f(), but copy everything. 1002 * 1003 * @ingroup @krb5_ccache 1004 */ 1005 1006KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 1007krb5_cc_copy_cache(krb5_context context, 1008 const krb5_ccache from, 1009 krb5_ccache to) 1010{ 1011 return krb5_cc_copy_match_f(context, from, to, NULL, NULL, NULL); 1012} 1013 1014/** 1015 * Return the version of `id'. 1016 * 1017 * @ingroup krb5_ccache 1018 */ 1019 1020 1021KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 1022krb5_cc_get_version(krb5_context context, 1023 const krb5_ccache id) 1024{ 1025 if(id->ops->get_version) 1026 return (*id->ops->get_version)(context, id); 1027 else 1028 return 0; 1029} 1030 1031/** 1032 * Clear `mcreds' so it can be used with krb5_cc_retrieve_cred 1033 * 1034 * @ingroup krb5_ccache 1035 */ 1036 1037 1038KRB5_LIB_FUNCTION void KRB5_LIB_CALL 1039krb5_cc_clear_mcred(krb5_creds *mcred) 1040{ 1041 memset(mcred, 0, sizeof(*mcred)); 1042} 1043 1044/** 1045 * Get the cc ops that is registered in `context' to handle the 1046 * prefix. prefix can be a complete credential cache name or a 1047 * prefix, the function will only use part up to the first colon (:) 1048 * if there is one. If prefix the argument is NULL, the default ccache 1049 * implemtation is returned. 1050 * 1051 * @return Returns NULL if ops not found. 1052 * 1053 * @ingroup krb5_ccache 1054 */ 1055 1056 1057KRB5_LIB_FUNCTION const krb5_cc_ops * KRB5_LIB_CALL 1058krb5_cc_get_prefix_ops(krb5_context context, const char *prefix) 1059{ 1060 char *p, *p1; 1061 int i; 1062 1063 if (prefix == NULL) 1064 return KRB5_DEFAULT_CCTYPE; 1065 1066 /* Is absolute path? Or UNC path? */ 1067 if (ISPATHSEP(prefix[0])) 1068 return &krb5_fcc_ops; 1069 1070#ifdef _WIN32 1071 /* Is drive letter? */ 1072 if (isalpha(prefix[0]) && prefix[1] == ':') 1073 return &krb5_fcc_ops; 1074#endif 1075 1076 p = strdup(prefix); 1077 if (p == NULL) { 1078 krb5_enomem(context); 1079 return NULL; 1080 } 1081 p1 = strchr(p, ':'); 1082 if (p1) 1083 *p1 = '\0'; 1084 1085 for(i = 0; i < context->num_cc_ops && context->cc_ops[i]->prefix; i++) { 1086 if(strcmp(context->cc_ops[i]->prefix, p) == 0) { 1087 free(p); 1088 return context->cc_ops[i]; 1089 } 1090 } 1091 free(p); 1092 return NULL; 1093} 1094 1095struct krb5_cc_cache_cursor_data { 1096 const krb5_cc_ops *ops; 1097 krb5_cc_cursor cursor; 1098}; 1099 1100/** 1101 * Start iterating over all caches of specified type. See also 1102 * krb5_cccol_cursor_new(). 1103 1104 * @param context A Kerberos 5 context 1105 * @param type optional type to iterate over, if NULL, the default cache is used. 1106 * @param cursor cursor should be freed with krb5_cc_cache_end_seq_get(). 1107 * 1108 * @return Return an error code or 0, see krb5_get_error_message(). 1109 * 1110 * @ingroup krb5_ccache 1111 */ 1112 1113 1114KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 1115krb5_cc_cache_get_first (krb5_context context, 1116 const char *type, 1117 krb5_cc_cache_cursor *cursor) 1118{ 1119 const krb5_cc_ops *ops; 1120 krb5_error_code ret; 1121 1122 if (type == NULL) 1123 type = krb5_cc_default_name(context); 1124 1125 ops = krb5_cc_get_prefix_ops(context, type); 1126 if (ops == NULL) { 1127 krb5_set_error_message(context, KRB5_CC_UNKNOWN_TYPE, 1128 "Unknown type \"%s\" when iterating " 1129 "trying to iterate the credential caches", type); 1130 return KRB5_CC_UNKNOWN_TYPE; 1131 } 1132 1133 if (ops->get_cache_first == NULL) { 1134 krb5_set_error_message(context, KRB5_CC_NOSUPP, 1135 N_("Credential cache type %s doesn't support " 1136 "iterations over caches", "type"), 1137 ops->prefix); 1138 return KRB5_CC_NOSUPP; 1139 } 1140 1141 *cursor = calloc(1, sizeof(**cursor)); 1142 if (*cursor == NULL) 1143 return krb5_enomem(context); 1144 1145 (*cursor)->ops = ops; 1146 1147 ret = ops->get_cache_first(context, &(*cursor)->cursor); 1148 if (ret) { 1149 free(*cursor); 1150 *cursor = NULL; 1151 } 1152 return ret; 1153} 1154 1155/** 1156 * Retrieve the next cache pointed to by (`cursor') in `id' 1157 * and advance `cursor'. 1158 * 1159 * @param context A Kerberos 5 context 1160 * @param cursor the iterator cursor, returned by krb5_cc_cache_get_first() 1161 * @param id next ccache 1162 * 1163 * @return Return 0 or an error code. Returns KRB5_CC_END when the end 1164 * of caches is reached, see krb5_get_error_message(). 1165 * 1166 * @ingroup krb5_ccache 1167 */ 1168 1169 1170KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 1171krb5_cc_cache_next (krb5_context context, 1172 krb5_cc_cache_cursor cursor, 1173 krb5_ccache *id) 1174{ 1175 return cursor->ops->get_cache_next(context, cursor->cursor, id); 1176} 1177 1178/** 1179 * Destroy the cursor `cursor'. 1180 * 1181 * @return Return an error code or 0, see krb5_get_error_message(). 1182 * 1183 * @ingroup krb5_ccache 1184 */ 1185 1186 1187KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 1188krb5_cc_cache_end_seq_get (krb5_context context, 1189 krb5_cc_cache_cursor cursor) 1190{ 1191 krb5_error_code ret; 1192 ret = cursor->ops->end_cache_get(context, cursor->cursor); 1193 cursor->ops = NULL; 1194 free(cursor); 1195 return ret; 1196} 1197 1198/** 1199 * Search for a matching credential cache that have the 1200 * `principal' as the default principal. On success, `id' needs to be 1201 * freed with krb5_cc_close() or krb5_cc_destroy(). 1202 * 1203 * @param context A Kerberos 5 context 1204 * @param client The principal to search for 1205 * @param id the returned credential cache 1206 * 1207 * @return On failure, error code is returned and `id' is set to NULL. 1208 * 1209 * @ingroup krb5_ccache 1210 */ 1211 1212 1213KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 1214krb5_cc_cache_match (krb5_context context, 1215 krb5_principal client, 1216 krb5_ccache *id) 1217{ 1218 krb5_cccol_cursor cursor; 1219 krb5_error_code ret; 1220 krb5_ccache cache = NULL; 1221 krb5_ccache expired_match = NULL; 1222 1223 *id = NULL; 1224 1225 ret = krb5_cccol_cursor_new (context, &cursor); 1226 if (ret) 1227 return ret; 1228 1229 while (krb5_cccol_cursor_next(context, cursor, &cache) == 0 && cache != NULL) { 1230 krb5_principal principal; 1231 krb5_boolean match; 1232 time_t lifetime; 1233 1234 ret = krb5_cc_get_principal(context, cache, &principal); 1235 if (ret) 1236 goto next; 1237 1238 if (client->name.name_string.len == 0) 1239 match = (strcmp(client->realm, principal->realm) == 0); 1240 else 1241 match = krb5_principal_compare(context, principal, client); 1242 krb5_free_principal(context, principal); 1243 1244 if (!match) 1245 goto next; 1246 1247 if (expired_match == NULL && 1248 (krb5_cc_get_lifetime(context, cache, &lifetime) != 0 || lifetime == 0)) { 1249 expired_match = cache; 1250 cache = NULL; 1251 goto next; 1252 } 1253 break; 1254 1255 next: 1256 if (cache) 1257 krb5_cc_close(context, cache); 1258 cache = NULL; 1259 } 1260 1261 krb5_cccol_cursor_free(context, &cursor); 1262 1263 if (cache == NULL && expired_match) { 1264 cache = expired_match; 1265 expired_match = NULL; 1266 } else if (expired_match) { 1267 krb5_cc_close(context, expired_match); 1268 } else if (cache == NULL) { 1269 char *str; 1270 1271 krb5_unparse_name(context, client, &str); 1272 1273 krb5_set_error_message(context, KRB5_CC_NOTFOUND, 1274 N_("Principal %s not found in any " 1275 "credential cache", ""), 1276 str ? str : "<out of memory>"); 1277 if (str) 1278 free(str); 1279 return KRB5_CC_NOTFOUND; 1280 } 1281 1282 *id = cache; 1283 1284 return 0; 1285} 1286 1287/** 1288 * Move the content from one credential cache to another. The 1289 * operation is an atomic switch. 1290 * 1291 * @param context a Keberos context 1292 * @param from the credential cache to move the content from 1293 * @param to the credential cache to move the content to 1294 1295 * @return On sucess, from is freed. On failure, error code is 1296 * returned and from and to are both still allocated, see krb5_get_error_message(). 1297 * 1298 * @ingroup krb5_ccache 1299 */ 1300 1301KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 1302krb5_cc_move(krb5_context context, krb5_ccache from, krb5_ccache to) 1303{ 1304 krb5_error_code ret; 1305 1306 if (strcmp(from->ops->prefix, to->ops->prefix) != 0) { 1307 krb5_set_error_message(context, KRB5_CC_NOSUPP, 1308 N_("Moving credentials between diffrent " 1309 "types not yet supported", "")); 1310 return KRB5_CC_NOSUPP; 1311 } 1312 1313 ret = (*to->ops->move)(context, from, to); 1314 if (ret == 0) { 1315 memset(from, 0, sizeof(*from)); 1316 free(from); 1317 } 1318 return ret; 1319} 1320 1321#define KRB5_CONF_NAME "krb5_ccache_conf_data" 1322#define KRB5_REALM_NAME "X-CACHECONF:" 1323 1324static krb5_error_code 1325build_conf_principals(krb5_context context, krb5_ccache id, 1326 krb5_const_principal principal, 1327 const char *name, krb5_creds *cred) 1328{ 1329 krb5_principal client; 1330 krb5_error_code ret; 1331 char *pname = NULL; 1332 1333 memset(cred, 0, sizeof(*cred)); 1334 1335 ret = krb5_cc_get_principal(context, id, &client); 1336 if (ret) 1337 return ret; 1338 1339 if (principal) { 1340 ret = krb5_unparse_name(context, principal, &pname); 1341 if (ret) 1342 return ret; 1343 } 1344 1345 ret = krb5_make_principal(context, &cred->server, 1346 KRB5_REALM_NAME, 1347 KRB5_CONF_NAME, name, pname, NULL); 1348 free(pname); 1349 if (ret) { 1350 krb5_free_principal(context, client); 1351 return ret; 1352 } 1353 ret = krb5_copy_principal(context, client, &cred->client); 1354 krb5_free_principal(context, client); 1355 return ret; 1356} 1357 1358/** 1359 * Return TRUE (non zero) if the principal is a configuration 1360 * principal (generated part of krb5_cc_set_config()). Returns FALSE 1361 * (zero) if not a configuration principal. 1362 * 1363 * @param context a Keberos context 1364 * @param principal principal to check if it a configuration principal 1365 * 1366 * @ingroup krb5_ccache 1367 */ 1368 1369KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL 1370krb5_is_config_principal(krb5_context context, 1371 krb5_const_principal principal) 1372{ 1373 if (strcmp(principal->realm, KRB5_REALM_NAME) != 0) 1374 return FALSE; 1375 1376 if (principal->name.name_string.len == 0 || 1377 strcmp(principal->name.name_string.val[0], KRB5_CONF_NAME) != 0) 1378 return FALSE; 1379 1380 return TRUE; 1381} 1382 1383/** 1384 * Store some configuration for the credential cache in the cache. 1385 * Existing configuration under the same name is over-written. 1386 * 1387 * @param context a Keberos context 1388 * @param id the credential cache to store the data for 1389 * @param principal configuration for a specific principal, if 1390 * NULL, global for the whole cache. 1391 * @param name name under which the configuraion is stored. 1392 * @param data data to store, if NULL, configure is removed. 1393 * 1394 * @ingroup krb5_ccache 1395 */ 1396 1397KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 1398krb5_cc_set_config(krb5_context context, krb5_ccache id, 1399 krb5_const_principal principal, 1400 const char *name, krb5_data *data) 1401{ 1402 krb5_error_code ret; 1403 krb5_creds cred; 1404 1405 ret = build_conf_principals(context, id, principal, name, &cred); 1406 if (ret) 1407 goto out; 1408 1409 /* Remove old configuration */ 1410 ret = krb5_cc_remove_cred(context, id, 0, &cred); 1411 if (ret && ret != KRB5_CC_NOTFOUND) 1412 goto out; 1413 1414 if (data) { 1415 /* not that anyone care when this expire */ 1416 cred.times.authtime = time(NULL); 1417 cred.times.endtime = cred.times.authtime + 3600 * 24 * 30; 1418 1419 ret = krb5_data_copy(&cred.ticket, data->data, data->length); 1420 if (ret) 1421 goto out; 1422 1423 ret = krb5_cc_store_cred(context, id, &cred); 1424 } 1425 1426out: 1427 krb5_free_cred_contents (context, &cred); 1428 return ret; 1429} 1430 1431/** 1432 * Get some configuration for the credential cache in the cache. 1433 * 1434 * @param context a Keberos context 1435 * @param id the credential cache to store the data for 1436 * @param principal configuration for a specific principal, if 1437 * NULL, global for the whole cache. 1438 * @param name name under which the configuraion is stored. 1439 * @param data data to fetched, free with krb5_data_free() 1440 * 1441 * @ingroup krb5_ccache 1442 */ 1443 1444 1445KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 1446krb5_cc_get_config(krb5_context context, krb5_ccache id, 1447 krb5_const_principal principal, 1448 const char *name, krb5_data *data) 1449{ 1450 krb5_creds mcred, cred; 1451 krb5_error_code ret; 1452 1453 memset(&cred, 0, sizeof(cred)); 1454 krb5_data_zero(data); 1455 1456 ret = build_conf_principals(context, id, principal, name, &mcred); 1457 if (ret) 1458 goto out; 1459 1460 ret = krb5_cc_retrieve_cred(context, id, 0, &mcred, &cred); 1461 if (ret) 1462 goto out; 1463 1464 ret = krb5_data_copy(data, cred.ticket.data, cred.ticket.length); 1465 1466out: 1467 krb5_free_cred_contents (context, &cred); 1468 krb5_free_cred_contents (context, &mcred); 1469 return ret; 1470} 1471 1472/* 1473 * 1474 */ 1475 1476struct krb5_cccol_cursor_data { 1477 int idx; 1478 krb5_cc_cache_cursor cursor; 1479}; 1480 1481/** 1482 * Get a new cache interation cursor that will interate over all 1483 * credentials caches independent of type. 1484 * 1485 * @param context a Keberos context 1486 * @param cursor passed into krb5_cccol_cursor_next() and free with krb5_cccol_cursor_free(). 1487 * 1488 * @return Returns 0 or and error code, see krb5_get_error_message(). 1489 * 1490 * @ingroup krb5_ccache 1491 */ 1492 1493KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 1494krb5_cccol_cursor_new(krb5_context context, krb5_cccol_cursor *cursor) 1495{ 1496 *cursor = calloc(1, sizeof(**cursor)); 1497 if (*cursor == NULL) 1498 return krb5_enomem(context); 1499 (*cursor)->idx = 0; 1500 (*cursor)->cursor = NULL; 1501 1502 return 0; 1503} 1504 1505/** 1506 * Get next credential cache from the iteration. 1507 * 1508 * @param context A Kerberos 5 context 1509 * @param cursor the iteration cursor 1510 * @param cache the returned cursor, pointer is set to NULL on failure 1511 * and a cache on success. The returned cache needs to be freed 1512 * with krb5_cc_close() or destroyed with krb5_cc_destroy(). 1513 * MIT Kerberos behavies slightly diffrent and sets cache to NULL 1514 * when all caches are iterated over and return 0. 1515 * 1516 * @return Return 0 or and error, KRB5_CC_END is returned at the end 1517 * of iteration. See krb5_get_error_message(). 1518 * 1519 * @ingroup krb5_ccache 1520 */ 1521 1522 1523KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 1524krb5_cccol_cursor_next(krb5_context context, krb5_cccol_cursor cursor, 1525 krb5_ccache *cache) 1526{ 1527 krb5_error_code ret; 1528 1529 *cache = NULL; 1530 1531 while (cursor->idx < context->num_cc_ops) { 1532 1533 if (cursor->cursor == NULL) { 1534 ret = krb5_cc_cache_get_first (context, 1535 context->cc_ops[cursor->idx]->prefix, 1536 &cursor->cursor); 1537 if (ret) { 1538 cursor->idx++; 1539 continue; 1540 } 1541 } 1542 ret = krb5_cc_cache_next(context, cursor->cursor, cache); 1543 if (ret == 0) 1544 break; 1545 1546 krb5_cc_cache_end_seq_get(context, cursor->cursor); 1547 cursor->cursor = NULL; 1548 if (ret != KRB5_CC_END) 1549 break; 1550 1551 cursor->idx++; 1552 } 1553 if (cursor->idx >= context->num_cc_ops) { 1554 krb5_set_error_message(context, KRB5_CC_END, 1555 N_("Reached end of credential caches", "")); 1556 return KRB5_CC_END; 1557 } 1558 1559 return 0; 1560} 1561 1562/** 1563 * End an iteration and free all resources, can be done before end is reached. 1564 * 1565 * @param context A Kerberos 5 context 1566 * @param cursor the iteration cursor to be freed. 1567 * 1568 * @return Return 0 or and error, KRB5_CC_END is returned at the end 1569 * of iteration. See krb5_get_error_message(). 1570 * 1571 * @ingroup krb5_ccache 1572 */ 1573 1574KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 1575krb5_cccol_cursor_free(krb5_context context, krb5_cccol_cursor *cursor) 1576{ 1577 krb5_cccol_cursor c = *cursor; 1578 1579 *cursor = NULL; 1580 if (c) { 1581 if (c->cursor) 1582 krb5_cc_cache_end_seq_get(context, c->cursor); 1583 free(c); 1584 } 1585 return 0; 1586} 1587 1588/** 1589 * Return the last time the credential cache was modified. 1590 * 1591 * @param context A Kerberos 5 context 1592 * @param id The credential cache to probe 1593 * @param mtime the last modification time, set to 0 on error. 1594 1595 * @return Return 0 or and error. See krb5_get_error_message(). 1596 * 1597 * @ingroup krb5_ccache 1598 */ 1599 1600 1601KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 1602krb5_cc_last_change_time(krb5_context context, 1603 krb5_ccache id, 1604 krb5_timestamp *mtime) 1605{ 1606 *mtime = 0; 1607 return (*id->ops->lastchange)(context, id, mtime); 1608} 1609 1610/** 1611 * Return the last modfication time for a cache collection. The query 1612 * can be limited to a specific cache type. If the function return 0 1613 * and mtime is 0, there was no credentials in the caches. 1614 * 1615 * @param context A Kerberos 5 context 1616 * @param type The credential cache to probe, if NULL, all type are traversed. 1617 * @param mtime the last modification time, set to 0 on error. 1618 1619 * @return Return 0 or and error. See krb5_get_error_message(). 1620 * 1621 * @ingroup krb5_ccache 1622 */ 1623 1624KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 1625krb5_cccol_last_change_time(krb5_context context, 1626 const char *type, 1627 krb5_timestamp *mtime) 1628{ 1629 krb5_cccol_cursor cursor; 1630 krb5_error_code ret; 1631 krb5_ccache id; 1632 krb5_timestamp t = 0; 1633 1634 *mtime = 0; 1635 1636 ret = krb5_cccol_cursor_new (context, &cursor); 1637 if (ret) 1638 return ret; 1639 1640 while (krb5_cccol_cursor_next(context, cursor, &id) == 0 && id != NULL) { 1641 1642 if (type && strcmp(krb5_cc_get_type(context, id), type) != 0) 1643 continue; 1644 1645 ret = krb5_cc_last_change_time(context, id, &t); 1646 krb5_cc_close(context, id); 1647 if (ret) 1648 continue; 1649 if (t > *mtime) 1650 *mtime = t; 1651 } 1652 1653 krb5_cccol_cursor_free(context, &cursor); 1654 1655 return 0; 1656} 1657/** 1658 * Return a friendly name on credential cache. Free the result with krb5_xfree(). 1659 * 1660 * @return Return an error code or 0, see krb5_get_error_message(). 1661 * 1662 * @ingroup krb5_ccache 1663 */ 1664 1665KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 1666krb5_cc_get_friendly_name(krb5_context context, 1667 krb5_ccache id, 1668 char **name) 1669{ 1670 krb5_error_code ret; 1671 krb5_data data; 1672 1673 ret = krb5_cc_get_config(context, id, NULL, "FriendlyName", &data); 1674 if (ret) { 1675 krb5_principal principal; 1676 ret = krb5_cc_get_principal(context, id, &principal); 1677 if (ret) 1678 return ret; 1679 ret = krb5_unparse_name(context, principal, name); 1680 krb5_free_principal(context, principal); 1681 } else { 1682 ret = asprintf(name, "%.*s", (int)data.length, (char *)data.data); 1683 krb5_data_free(&data); 1684 if (ret <= 0) 1685 ret = krb5_enomem(context); 1686 else 1687 ret = 0; 1688 } 1689 1690 return ret; 1691} 1692 1693/** 1694 * Set the friendly name on credential cache. 1695 * 1696 * @return Return an error code or 0, see krb5_get_error_message(). 1697 * 1698 * @ingroup krb5_ccache 1699 */ 1700 1701KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 1702krb5_cc_set_friendly_name(krb5_context context, 1703 krb5_ccache id, 1704 const char *name) 1705{ 1706 krb5_data data; 1707 1708 data.data = rk_UNCONST(name); 1709 data.length = strlen(name); 1710 1711 return krb5_cc_set_config(context, id, NULL, "FriendlyName", &data); 1712} 1713 1714/** 1715 * Get the lifetime of the initial ticket in the cache 1716 * 1717 * Get the lifetime of the initial ticket in the cache, if the initial 1718 * ticket was not found, the error code KRB5_CC_END is returned. 1719 * 1720 * @param context A Kerberos 5 context. 1721 * @param id a credential cache 1722 * @param t the relative lifetime of the initial ticket 1723 * 1724 * @return Return an error code or 0, see krb5_get_error_message(). 1725 * 1726 * @ingroup krb5_ccache 1727 */ 1728 1729KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 1730krb5_cc_get_lifetime(krb5_context context, krb5_ccache id, time_t *t) 1731{ 1732 krb5_data config_start_realm; 1733 char *start_realm; 1734 krb5_cc_cursor cursor; 1735 krb5_error_code ret; 1736 krb5_creds cred; 1737 time_t now, endtime = 0; 1738 1739 *t = 0; 1740 krb5_timeofday(context, &now); 1741 1742 ret = krb5_cc_get_config(context, id, NULL, "start_realm", &config_start_realm); 1743 if (ret == 0) { 1744 start_realm = strndup(config_start_realm.data, config_start_realm.length); 1745 krb5_data_free(&config_start_realm); 1746 } else { 1747 krb5_principal client; 1748 1749 ret = krb5_cc_get_principal(context, id, &client); 1750 if (ret) 1751 return ret; 1752 start_realm = strdup(krb5_principal_get_realm(context, client)); 1753 krb5_free_principal(context, client); 1754 } 1755 if (start_realm == NULL) 1756 return krb5_enomem(context); 1757 1758 ret = krb5_cc_start_seq_get(context, id, &cursor); 1759 if (ret) { 1760 free(start_realm); 1761 return ret; 1762 } 1763 1764 while ((ret = krb5_cc_next_cred(context, id, &cursor, &cred)) == 0) { 1765 /** 1766 * If we find the start krbtgt in the cache, use that as the lifespan. 1767 */ 1768 if (krb5_principal_is_root_krbtgt(context, cred.server) && 1769 strcmp(cred.server->realm, start_realm) == 0) { 1770 if (now < cred.times.endtime) 1771 endtime = cred.times.endtime; 1772 krb5_free_cred_contents(context, &cred); 1773 break; 1774 } 1775 /* 1776 * Skip config entries 1777 */ 1778 if (krb5_is_config_principal(context, cred.server)) { 1779 krb5_free_cred_contents(context, &cred); 1780 continue; 1781 } 1782 /** 1783 * If there was no krbtgt, use the shortest lifetime of 1784 * service tickets that have yet to expire. If all 1785 * credentials are expired, krb5_cc_get_lifetime() will fail. 1786 */ 1787 if ((endtime == 0 || cred.times.endtime < endtime) && now < cred.times.endtime) 1788 endtime = cred.times.endtime; 1789 krb5_free_cred_contents(context, &cred); 1790 } 1791 free(start_realm); 1792 1793 /* if we found an endtime use that */ 1794 if (endtime) { 1795 *t = endtime - now; 1796 ret = 0; 1797 } 1798 1799 krb5_cc_end_seq_get(context, id, &cursor); 1800 1801 return ret; 1802} 1803 1804/** 1805 * Set the time offset betwen the client and the KDC 1806 * 1807 * If the backend doesn't support KDC offset, use the context global setting. 1808 * 1809 * @param context A Kerberos 5 context. 1810 * @param id a credential cache 1811 * @param offset the offset in seconds 1812 * 1813 * @return Return an error code or 0, see krb5_get_error_message(). 1814 * 1815 * @ingroup krb5_ccache 1816 */ 1817 1818KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 1819krb5_cc_set_kdc_offset(krb5_context context, krb5_ccache id, krb5_deltat offset) 1820{ 1821 if (id->ops->set_kdc_offset == NULL) { 1822 context->kdc_sec_offset = offset; 1823 context->kdc_usec_offset = 0; 1824 return 0; 1825 } 1826 return (*id->ops->set_kdc_offset)(context, id, offset); 1827} 1828 1829/** 1830 * Get the time offset betwen the client and the KDC 1831 * 1832 * If the backend doesn't support KDC offset, use the context global setting. 1833 * 1834 * @param context A Kerberos 5 context. 1835 * @param id a credential cache 1836 * @param offset the offset in seconds 1837 * 1838 * @return Return an error code or 0, see krb5_get_error_message(). 1839 * 1840 * @ingroup krb5_ccache 1841 */ 1842 1843KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 1844krb5_cc_get_kdc_offset(krb5_context context, krb5_ccache id, krb5_deltat *offset) 1845{ 1846 if (id->ops->get_kdc_offset == NULL) { 1847 *offset = context->kdc_sec_offset; 1848 return 0; 1849 } 1850 return (*id->ops->get_kdc_offset)(context, id, offset); 1851} 1852 1853#ifdef _WIN32 1854#define REGPATH_MIT_KRB5 "SOFTWARE\\MIT\\Kerberos5" 1855 1856static char * 1857_get_default_cc_name_from_registry(krb5_context context, HKEY hkBase) 1858{ 1859 HKEY hk_k5 = 0; 1860 LONG code; 1861 char *ccname = NULL; 1862 1863 code = RegOpenKeyEx(hkBase, 1864 REGPATH_MIT_KRB5, 1865 0, KEY_READ, &hk_k5); 1866 1867 if (code != ERROR_SUCCESS) 1868 return NULL; 1869 1870 ccname = _krb5_parse_reg_value_as_string(context, hk_k5, "ccname", 1871 REG_NONE, 0); 1872 1873 RegCloseKey(hk_k5); 1874 1875 return ccname; 1876} 1877 1878KRB5_LIB_FUNCTION char * KRB5_LIB_CALL 1879_krb5_get_default_cc_name_from_registry(krb5_context context) 1880{ 1881 char *ccname; 1882 1883 ccname = _get_default_cc_name_from_registry(context, HKEY_CURRENT_USER); 1884 if (ccname == NULL) 1885 ccname = _get_default_cc_name_from_registry(context, 1886 HKEY_LOCAL_MACHINE); 1887 1888 return ccname; 1889} 1890 1891KRB5_LIB_FUNCTION int KRB5_LIB_CALL 1892_krb5_set_default_cc_name_to_registry(krb5_context context, krb5_ccache id) 1893{ 1894 HKEY hk_k5 = 0; 1895 LONG code; 1896 int ret = -1; 1897 char * ccname = NULL; 1898 1899 code = RegOpenKeyEx(HKEY_CURRENT_USER, 1900 REGPATH_MIT_KRB5, 1901 0, KEY_READ|KEY_WRITE, &hk_k5); 1902 1903 if (code != ERROR_SUCCESS) 1904 return -1; 1905 1906 ret = asprintf(&ccname, "%s:%s", krb5_cc_get_type(context, id), krb5_cc_get_name(context, id)); 1907 if (ret < 0) 1908 goto cleanup; 1909 1910 ret = _krb5_store_string_to_reg_value(context, hk_k5, "ccname", 1911 REG_SZ, ccname, -1, 0); 1912 1913 cleanup: 1914 1915 if (ccname) 1916 free(ccname); 1917 1918 RegCloseKey(hk_k5); 1919 1920 return ret; 1921} 1922#endif 1923