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