1/* 2 * Copyright (c) 1997 - 2008 Kungliga Tekniska H��gskolan 3 * (Royal Institute of Technology, Stockholm, Sweden). 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 17 * 3. Neither the name of the Institute nor the names of its contributors 18 * may be used to endorse or promote products derived from this software 19 * without specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND 22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE 25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31 * SUCH DAMAGE. 32 */ 33 34#include "krb5_locl.h" 35 36/** 37 * @page krb5_ccache_intro The credential cache functions 38 * @section section_krb5_ccache Kerberos credential caches 39 * 40 * krb5_ccache structure holds a Kerberos credential cache. 41 * 42 * Heimdal support the follow types of credential caches: 43 * 44 * - SCC 45 * Store the credential in a database 46 * - FILE 47 * Store the credential in memory 48 * - MEMORY 49 * Store the credential in memory 50 * - API 51 * A credential cache server based solution for Mac OS X 52 * - KCM 53 * A credential cache server based solution for all platforms 54 * 55 * @subsection Example 56 * 57 * This is a minimalistic version of klist: 58@code 59#include <krb5.h> 60 61int 62main (int argc, char **argv) 63{ 64 krb5_context context; 65 krb5_cc_cursor cursor; 66 krb5_error_code ret; 67 krb5_ccache id; 68 krb5_creds creds; 69 70 if (krb5_init_context (&context) != 0) 71 errx(1, "krb5_context"); 72 73 ret = krb5_cc_default (context, &id); 74 if (ret) 75 krb5_err(context, 1, ret, "krb5_cc_default"); 76 77 ret = krb5_cc_start_seq_get(context, id, &cursor); 78 if (ret) 79 krb5_err(context, 1, ret, "krb5_cc_start_seq_get"); 80 81 while((ret = krb5_cc_next_cred(context, id, &cursor, &creds)) == 0){ 82 char *principal; 83 84 krb5_unparse_name_short(context, creds.server, &principal); 85 printf("principal: %s\\n", principal); 86 free(principal); 87 krb5_free_cred_contents (context, &creds); 88 } 89 ret = krb5_cc_end_seq_get(context, id, &cursor); 90 if (ret) 91 krb5_err(context, 1, ret, "krb5_cc_end_seq_get"); 92 93 krb5_cc_close(context, id); 94 95 krb5_free_context(context); 96 return 0; 97} 98* @endcode 99*/ 100 101/** 102 * Add a new ccache type with operations `ops', overwriting any 103 * existing one if `override'. 104 * 105 * @param context a Keberos context 106 * @param ops type of plugin symbol 107 * @param override flag to select if the registration is to overide 108 * an existing ops with the same name. 109 * 110 * @return Return an error code or 0, see krb5_get_error_message(). 111 * 112 * @ingroup krb5_ccache 113 */ 114 115krb5_error_code KRB5_LIB_FUNCTION 116krb5_cc_register(krb5_context context, 117 const krb5_cc_ops *ops, 118 krb5_boolean override) 119{ 120 int i; 121 122 for(i = 0; i < context->num_cc_ops && context->cc_ops[i].prefix; i++) { 123 if(strcmp(context->cc_ops[i].prefix, ops->prefix) == 0) { 124 if(!override) { 125 krb5_set_error_message(context, 126 KRB5_CC_TYPE_EXISTS, 127 N_("cache type %s already exists", "type"), 128 ops->prefix); 129 return KRB5_CC_TYPE_EXISTS; 130 } 131 break; 132 } 133 } 134 if(i == context->num_cc_ops) { 135 krb5_cc_ops *o = realloc(context->cc_ops, 136 (context->num_cc_ops + 1) * 137 sizeof(*context->cc_ops)); 138 if(o == NULL) { 139 krb5_set_error_message(context, KRB5_CC_NOMEM, 140 N_("malloc: out of memory", "")); 141 return KRB5_CC_NOMEM; 142 } 143 context->num_cc_ops++; 144 context->cc_ops = o; 145 memset(context->cc_ops + i, 0, 146 (context->num_cc_ops - i) * sizeof(*context->cc_ops)); 147 } 148 memcpy(&context->cc_ops[i], ops, sizeof(context->cc_ops[i])); 149 return 0; 150} 151 152/* 153 * Allocate the memory for a `id' and the that function table to 154 * `ops'. Returns 0 or and error code. 155 */ 156 157krb5_error_code 158_krb5_cc_allocate(krb5_context context, 159 const krb5_cc_ops *ops, 160 krb5_ccache *id) 161{ 162 krb5_ccache p; 163 164 p = malloc (sizeof(*p)); 165 if(p == NULL) { 166 krb5_set_error_message(context, KRB5_CC_NOMEM, 167 N_("malloc: out of memory", "")); 168 return KRB5_CC_NOMEM; 169 } 170 p->ops = ops; 171 *id = p; 172 173 return 0; 174} 175 176/* 177 * Allocate memory for a new ccache in `id' with operations `ops' 178 * and name `residual'. Return 0 or an error code. 179 */ 180 181static krb5_error_code 182allocate_ccache (krb5_context context, 183 const krb5_cc_ops *ops, 184 const char *residual, 185 krb5_ccache *id) 186{ 187 krb5_error_code ret; 188 189 ret = _krb5_cc_allocate(context, ops, id); 190 if (ret) 191 return ret; 192 ret = (*id)->ops->resolve(context, id, residual); 193 if(ret) 194 free(*id); 195 return ret; 196} 197 198/** 199 * Find and allocate a ccache in `id' from the specification in `residual'. 200 * If the ccache name doesn't contain any colon, interpret it as a file name. 201 * 202 * @param context a Keberos context. 203 * @param name string name of a credential cache. 204 * @param id return pointer to a found credential cache. 205 * 206 * @return Return 0 or an error code. In case of an error, id is set 207 * to NULL, see krb5_get_error_message(). 208 * 209 * @ingroup krb5_ccache 210 */ 211 212 213krb5_error_code KRB5_LIB_FUNCTION 214krb5_cc_resolve(krb5_context context, 215 const char *name, 216 krb5_ccache *id) 217{ 218 int i; 219 220 *id = NULL; 221 222 for(i = 0; i < context->num_cc_ops && context->cc_ops[i].prefix; i++) { 223 size_t prefix_len = strlen(context->cc_ops[i].prefix); 224 225 if(strncmp(context->cc_ops[i].prefix, name, prefix_len) == 0 226 && name[prefix_len] == ':') { 227 return allocate_ccache (context, &context->cc_ops[i], 228 name + prefix_len + 1, 229 id); 230 } 231 } 232 if (strchr (name, ':') == NULL) 233 return allocate_ccache (context, &krb5_fcc_ops, name, id); 234 else { 235 krb5_set_error_message(context, KRB5_CC_UNKNOWN_TYPE, 236 N_("unknown ccache type %s", "name"), name); 237 return KRB5_CC_UNKNOWN_TYPE; 238 } 239} 240 241/** 242 * Generates a new unique ccache of `type` in `id'. If `type' is NULL, 243 * the library chooses the default credential cache type. The supplied 244 * `hint' (that can be NULL) is a string that the credential cache 245 * type can use to base the name of the credential on, this is to make 246 * it easier for the user to differentiate the credentials. 247 * 248 * @return Return an error code or 0, see krb5_get_error_message(). 249 * 250 * @ingroup krb5_ccache 251 */ 252 253krb5_error_code KRB5_LIB_FUNCTION 254krb5_cc_new_unique(krb5_context context, const char *type, 255 const char *hint, krb5_ccache *id) 256{ 257 const krb5_cc_ops *ops; 258 krb5_error_code ret; 259 260 ops = krb5_cc_get_prefix_ops(context, type); 261 if (ops == NULL) { 262 krb5_set_error_message(context, KRB5_CC_UNKNOWN_TYPE, 263 "Credential cache type %s is unknown", type); 264 return KRB5_CC_UNKNOWN_TYPE; 265 } 266 267 ret = _krb5_cc_allocate(context, ops, id); 268 if (ret) 269 return ret; 270 ret = (*id)->ops->gen_new(context, id); 271 if (ret) { 272 free(*id); 273 *id = NULL; 274 } 275 return ret; 276} 277 278/** 279 * Return the name of the ccache `id' 280 * 281 * @ingroup krb5_ccache 282 */ 283 284 285const char* KRB5_LIB_FUNCTION 286krb5_cc_get_name(krb5_context context, 287 krb5_ccache id) 288{ 289 return id->ops->get_name(context, id); 290} 291 292/** 293 * Return the type of the ccache `id'. 294 * 295 * @ingroup krb5_ccache 296 */ 297 298 299const char* KRB5_LIB_FUNCTION 300krb5_cc_get_type(krb5_context context, 301 krb5_ccache id) 302{ 303 return id->ops->prefix; 304} 305 306/** 307 * Return the complete resolvable name the ccache `id' in `str��. 308 * `str` should be freed with free(3). 309 * Returns 0 or an error (and then *str is set to NULL). 310 * 311 * @ingroup krb5_ccache 312 */ 313 314 315krb5_error_code KRB5_LIB_FUNCTION 316krb5_cc_get_full_name(krb5_context context, 317 krb5_ccache id, 318 char **str) 319{ 320 const char *type, *name; 321 322 *str = NULL; 323 324 type = krb5_cc_get_type(context, id); 325 if (type == NULL) { 326 krb5_set_error_message(context, KRB5_CC_UNKNOWN_TYPE, 327 "cache have no name of type"); 328 return KRB5_CC_UNKNOWN_TYPE; 329 } 330 331 name = krb5_cc_get_name(context, id); 332 if (name == NULL) { 333 krb5_set_error_message(context, KRB5_CC_BADNAME, 334 "cache of type %s have no name", type); 335 return KRB5_CC_BADNAME; 336 } 337 338 if (asprintf(str, "%s:%s", type, name) == -1) { 339 krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", "")); 340 *str = NULL; 341 return ENOMEM; 342 } 343 return 0; 344} 345 346/** 347 * Return krb5_cc_ops of a the ccache `id'. 348 * 349 * @ingroup krb5_ccache 350 */ 351 352 353const krb5_cc_ops * 354krb5_cc_get_ops(krb5_context context, krb5_ccache id) 355{ 356 return id->ops; 357} 358 359/* 360 * Expand variables in `str' into `res' 361 */ 362 363krb5_error_code 364_krb5_expand_default_cc_name(krb5_context context, const char *str, char **res) 365{ 366 size_t tlen, len = 0; 367 char *tmp, *tmp2, *append; 368 369 *res = NULL; 370 371 while (str && *str) { 372 tmp = strstr(str, "%{"); 373 if (tmp && tmp != str) { 374 append = malloc((tmp - str) + 1); 375 if (append) { 376 memcpy(append, str, tmp - str); 377 append[tmp - str] = '\0'; 378 } 379 str = tmp; 380 } else if (tmp) { 381 tmp2 = strchr(tmp, '}'); 382 if (tmp2 == NULL) { 383 free(*res); 384 *res = NULL; 385 krb5_set_error_message(context, KRB5_CONFIG_BADFORMAT, 386 "variable missing }"); 387 return KRB5_CONFIG_BADFORMAT; 388 } 389 if (strncasecmp(tmp, "%{uid}", 6) == 0) 390 asprintf(&append, "%u", (unsigned)getuid()); 391 else if (strncasecmp(tmp, "%{null}", 7) == 0) 392 append = strdup(""); 393 else { 394 free(*res); 395 *res = NULL; 396 krb5_set_error_message(context, 397 KRB5_CONFIG_BADFORMAT, 398 "expand default cache unknown " 399 "variable \"%.*s\"", 400 (int)(tmp2 - tmp) - 2, tmp + 2); 401 return KRB5_CONFIG_BADFORMAT; 402 } 403 str = tmp2 + 1; 404 } else { 405 append = strdup(str); 406 str = NULL; 407 } 408 if (append == NULL) { 409 free(*res); 410 *res = NULL; 411 krb5_set_error_message(context, ENOMEM, 412 N_("malloc: out of memory", "")); 413 return ENOMEM; 414 } 415 416 tlen = strlen(append); 417 tmp = realloc(*res, len + tlen + 1); 418 if (tmp == NULL) { 419 free(append); 420 free(*res); 421 *res = NULL; 422 krb5_set_error_message(context, ENOMEM, 423 N_("malloc: out of memory", "")); 424 return ENOMEM; 425 } 426 *res = tmp; 427 memcpy(*res + len, append, tlen + 1); 428 len = len + tlen; 429 free(append); 430 } 431 return 0; 432} 433 434/* 435 * Return non-zero if envirnoment that will determine default krb5cc 436 * name has changed. 437 */ 438 439static int 440environment_changed(krb5_context context) 441{ 442 const char *e; 443 444 /* if the cc name was set, don't change it */ 445 if (context->default_cc_name_set) 446 return 0; 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_error_code 477krb5_cc_switch(krb5_context context, krb5_ccache id) 478{ 479 480 if (id->ops->set_default == NULL) 481 return 0; 482 483 return (*id->ops->set_default)(context, id); 484} 485 486/** 487 * Set the default cc name for `context' to `name'. 488 * 489 * @ingroup krb5_ccache 490 */ 491 492krb5_error_code KRB5_LIB_FUNCTION 493krb5_cc_set_default_name(krb5_context context, const char *name) 494{ 495 krb5_error_code ret = 0; 496 char *p; 497 498 if (name == NULL) { 499 const char *e = NULL; 500 501 if(!issuid()) { 502 e = getenv("KRB5CCNAME"); 503 if (e) { 504 p = strdup(e); 505 if (context->default_cc_name_env) 506 free(context->default_cc_name_env); 507 context->default_cc_name_env = strdup(e); 508 } 509 } 510 if (e == NULL) { 511 e = krb5_config_get_string(context, NULL, "libdefaults", 512 "default_cc_name", NULL); 513 if (e) { 514 ret = _krb5_expand_default_cc_name(context, e, &p); 515 if (ret) 516 return ret; 517 } 518 if (e == NULL) { 519 const krb5_cc_ops *ops = KRB5_DEFAULT_CCTYPE; 520 e = krb5_config_get_string(context, NULL, "libdefaults", 521 "default_cc_type", NULL); 522 if (e) { 523 ops = krb5_cc_get_prefix_ops(context, e); 524 if (ops == NULL) { 525 krb5_set_error_message(context, 526 KRB5_CC_UNKNOWN_TYPE, 527 "Credential cache type %s " 528 "is unknown", e); 529 return KRB5_CC_UNKNOWN_TYPE; 530 } 531 } 532 ret = (*ops->get_default_name)(context, &p); 533 if (ret) 534 return ret; 535 } 536 } 537 context->default_cc_name_set = 0; 538 } else { 539 p = strdup(name); 540 context->default_cc_name_set = 1; 541 } 542 543 if (p == NULL) { 544 krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", "")); 545 return ENOMEM; 546 } 547 548 if (context->default_cc_name) 549 free(context->default_cc_name); 550 551 context->default_cc_name = p; 552 553 return ret; 554} 555 556/** 557 * Return a pointer to a context static string containing the default 558 * ccache name. 559 * 560 * @return String to the default credential cache name. 561 * 562 * @ingroup krb5_ccache 563 */ 564 565 566const char* KRB5_LIB_FUNCTION 567krb5_cc_default_name(krb5_context context) 568{ 569 if (context->default_cc_name == NULL || environment_changed(context)) 570 krb5_cc_set_default_name(context, NULL); 571 572 return context->default_cc_name; 573} 574 575/** 576 * Open the default ccache in `id'. 577 * 578 * @return Return an error code or 0, see krb5_get_error_message(). 579 * 580 * @ingroup krb5_ccache 581 */ 582 583 584krb5_error_code KRB5_LIB_FUNCTION 585krb5_cc_default(krb5_context context, 586 krb5_ccache *id) 587{ 588 const char *p = krb5_cc_default_name(context); 589 590 if (p == NULL) { 591 krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", "")); 592 return ENOMEM; 593 } 594 return krb5_cc_resolve(context, p, id); 595} 596 597/** 598 * Create a new ccache in `id' for `primary_principal'. 599 * 600 * @return Return an error code or 0, see krb5_get_error_message(). 601 * 602 * @ingroup krb5_ccache 603 */ 604 605 606krb5_error_code KRB5_LIB_FUNCTION 607krb5_cc_initialize(krb5_context context, 608 krb5_ccache id, 609 krb5_principal primary_principal) 610{ 611 return (*id->ops->init)(context, id, primary_principal); 612} 613 614 615/** 616 * Remove the ccache `id'. 617 * 618 * @return Return an error code or 0, see krb5_get_error_message(). 619 * 620 * @ingroup krb5_ccache 621 */ 622 623 624krb5_error_code KRB5_LIB_FUNCTION 625krb5_cc_destroy(krb5_context context, 626 krb5_ccache id) 627{ 628 krb5_error_code ret; 629 630 ret = (*id->ops->destroy)(context, id); 631 krb5_cc_close (context, id); 632 return ret; 633} 634 635/** 636 * Stop using the ccache `id' and free the related resources. 637 * 638 * @return Return an error code or 0, see krb5_get_error_message(). 639 * 640 * @ingroup krb5_ccache 641 */ 642 643 644krb5_error_code KRB5_LIB_FUNCTION 645krb5_cc_close(krb5_context context, 646 krb5_ccache id) 647{ 648 krb5_error_code ret; 649 ret = (*id->ops->close)(context, id); 650 free(id); 651 return ret; 652} 653 654/** 655 * Store `creds' in the ccache `id'. 656 * 657 * @return Return an error code or 0, see krb5_get_error_message(). 658 * 659 * @ingroup krb5_ccache 660 */ 661 662 663krb5_error_code KRB5_LIB_FUNCTION 664krb5_cc_store_cred(krb5_context context, 665 krb5_ccache id, 666 krb5_creds *creds) 667{ 668 return (*id->ops->store)(context, id, creds); 669} 670 671/** 672 * Retrieve the credential identified by `mcreds' (and `whichfields') 673 * from `id' in `creds'. 'creds' must be free by the caller using 674 * krb5_free_cred_contents. 675 * 676 * @return Return an error code or 0, see krb5_get_error_message(). 677 * 678 * @ingroup krb5_ccache 679 */ 680 681 682krb5_error_code KRB5_LIB_FUNCTION 683krb5_cc_retrieve_cred(krb5_context context, 684 krb5_ccache id, 685 krb5_flags whichfields, 686 const krb5_creds *mcreds, 687 krb5_creds *creds) 688{ 689 krb5_error_code ret; 690 krb5_cc_cursor cursor; 691 692 if (id->ops->retrieve != NULL) { 693 return (*id->ops->retrieve)(context, id, whichfields, 694 mcreds, creds); 695 } 696 697 ret = krb5_cc_start_seq_get(context, id, &cursor); 698 if (ret) 699 return ret; 700 while((ret = krb5_cc_next_cred(context, id, &cursor, creds)) == 0){ 701 if(krb5_compare_creds(context, whichfields, mcreds, creds)){ 702 ret = 0; 703 break; 704 } 705 krb5_free_cred_contents (context, creds); 706 } 707 krb5_cc_end_seq_get(context, id, &cursor); 708 return ret; 709} 710 711/** 712 * Return the principal of `id' in `principal'. 713 * 714 * @return Return an error code or 0, see krb5_get_error_message(). 715 * 716 * @ingroup krb5_ccache 717 */ 718 719 720krb5_error_code KRB5_LIB_FUNCTION 721krb5_cc_get_principal(krb5_context context, 722 krb5_ccache id, 723 krb5_principal *principal) 724{ 725 return (*id->ops->get_princ)(context, id, principal); 726} 727 728/** 729 * Start iterating over `id', `cursor' is initialized to the 730 * beginning. Caller must free the cursor with krb5_cc_end_seq_get(). 731 * 732 * @return Return an error code or 0, see krb5_get_error_message(). 733 * 734 * @ingroup krb5_ccache 735 */ 736 737 738krb5_error_code KRB5_LIB_FUNCTION 739krb5_cc_start_seq_get (krb5_context context, 740 const krb5_ccache id, 741 krb5_cc_cursor *cursor) 742{ 743 return (*id->ops->get_first)(context, id, cursor); 744} 745 746/** 747 * Retrieve the next cred pointed to by (`id', `cursor') in `creds' 748 * and advance `cursor'. 749 * 750 * @return Return an error code or 0, see krb5_get_error_message(). 751 * 752 * @ingroup krb5_ccache 753 */ 754 755 756krb5_error_code KRB5_LIB_FUNCTION 757krb5_cc_next_cred (krb5_context context, 758 const krb5_ccache id, 759 krb5_cc_cursor *cursor, 760 krb5_creds *creds) 761{ 762 return (*id->ops->get_next)(context, id, cursor, creds); 763} 764 765/** 766 * Destroy the cursor `cursor'. 767 * 768 * @ingroup krb5_ccache 769 */ 770 771 772krb5_error_code KRB5_LIB_FUNCTION 773krb5_cc_end_seq_get (krb5_context context, 774 const krb5_ccache id, 775 krb5_cc_cursor *cursor) 776{ 777 return (*id->ops->end_get)(context, id, cursor); 778} 779 780/** 781 * Remove the credential identified by `cred', `which' from `id'. 782 * 783 * @ingroup krb5_ccache 784 */ 785 786 787krb5_error_code KRB5_LIB_FUNCTION 788krb5_cc_remove_cred(krb5_context context, 789 krb5_ccache id, 790 krb5_flags which, 791 krb5_creds *cred) 792{ 793 if(id->ops->remove_cred == NULL) { 794 krb5_set_error_message(context, 795 EACCES, 796 "ccache %s does not support remove_cred", 797 id->ops->prefix); 798 return EACCES; /* XXX */ 799 } 800 return (*id->ops->remove_cred)(context, id, which, cred); 801} 802 803/** 804 * Set the flags of `id' to `flags'. 805 * 806 * @ingroup krb5_ccache 807 */ 808 809 810krb5_error_code KRB5_LIB_FUNCTION 811krb5_cc_set_flags(krb5_context context, 812 krb5_ccache id, 813 krb5_flags flags) 814{ 815 return (*id->ops->set_flags)(context, id, flags); 816} 817 818/** 819 * Get the flags of `id', store them in `flags'. 820 * 821 * @ingroup krb5_ccache 822 */ 823 824krb5_error_code KRB5_LIB_FUNCTION 825krb5_cc_get_flags(krb5_context context, 826 krb5_ccache id, 827 krb5_flags *flags) 828{ 829 *flags = 0; 830 return 0; 831} 832 833/** 834 * Copy the contents of `from' to `to' if the given match function 835 * return true. 836 * 837 * @param context A Kerberos 5 context. 838 * @param from the cache to copy data from. 839 * @param to the cache to copy data to. 840 * @param match a match function that should return TRUE if cred argument should be copied, if NULL, all credentials are copied. 841 * @param matchctx context passed to match function. 842 * @param matched set to true if there was a credential that matched, may be NULL. 843 * 844 * @return Return an error code or 0, see krb5_get_error_message(). 845 * 846 * @ingroup krb5_ccache 847 */ 848 849krb5_error_code KRB5_LIB_FUNCTION 850krb5_cc_copy_match_f(krb5_context context, 851 const krb5_ccache from, 852 krb5_ccache to, 853 krb5_boolean (*match)(krb5_context, void *, const krb5_creds *), 854 void *matchctx, 855 unsigned int *matched) 856{ 857 krb5_error_code ret; 858 krb5_cc_cursor cursor; 859 krb5_creds cred; 860 krb5_principal princ; 861 862 if (matched) 863 *matched = 0; 864 865 ret = krb5_cc_get_principal(context, from, &princ); 866 if (ret) 867 return ret; 868 ret = krb5_cc_initialize(context, to, princ); 869 if (ret) { 870 krb5_free_principal(context, princ); 871 return ret; 872 } 873 ret = krb5_cc_start_seq_get(context, from, &cursor); 874 if (ret) { 875 krb5_free_principal(context, princ); 876 return ret; 877 } 878 879 while ((ret = krb5_cc_next_cred(context, from, &cursor, &cred)) == 0) { 880 if (match == NULL || (*match)(context, matchctx, &cred) == 0) { 881 if (matched) 882 (*matched)++; 883 ret = krb5_cc_store_cred(context, to, &cred); 884 if (ret) 885 break; 886 } 887 krb5_free_cred_contents(context, &cred); 888 } 889 krb5_cc_end_seq_get(context, from, &cursor); 890 krb5_free_principal(context, princ); 891 if (ret == KRB5_CC_END) 892 ret = 0; 893 return ret; 894} 895 896/** 897 * Just like krb5_cc_copy_match_f(), but copy everything. 898 * 899 * @ingroup @krb5_ccache 900 */ 901 902krb5_error_code KRB5_LIB_FUNCTION 903krb5_cc_copy_cache(krb5_context context, 904 const krb5_ccache from, 905 krb5_ccache to) 906{ 907 return krb5_cc_copy_match_f(context, from, to, NULL, NULL, NULL); 908} 909 910/** 911 * Return the version of `id'. 912 * 913 * @ingroup krb5_ccache 914 */ 915 916 917krb5_error_code KRB5_LIB_FUNCTION 918krb5_cc_get_version(krb5_context context, 919 const krb5_ccache id) 920{ 921 if(id->ops->get_version) 922 return (*id->ops->get_version)(context, id); 923 else 924 return 0; 925} 926 927/** 928 * Clear `mcreds' so it can be used with krb5_cc_retrieve_cred 929 * 930 * @ingroup krb5_ccache 931 */ 932 933 934void KRB5_LIB_FUNCTION 935krb5_cc_clear_mcred(krb5_creds *mcred) 936{ 937 memset(mcred, 0, sizeof(*mcred)); 938} 939 940/** 941 * Get the cc ops that is registered in `context' to handle the 942 * prefix. prefix can be a complete credential cache name or a 943 * prefix, the function will only use part up to the first colon (:) 944 * if there is one. If prefix the argument is NULL, the default ccache 945 * implemtation is returned. 946 * 947 * @return Returns NULL if ops not found. 948 * 949 * @ingroup krb5_ccache 950 */ 951 952 953const krb5_cc_ops * 954krb5_cc_get_prefix_ops(krb5_context context, const char *prefix) 955{ 956 char *p, *p1; 957 int i; 958 959 if (prefix == NULL) 960 return KRB5_DEFAULT_CCTYPE; 961 if (prefix[0] == '/') 962 return &krb5_fcc_ops; 963 964 p = strdup(prefix); 965 if (p == NULL) { 966 krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", "")); 967 return NULL; 968 } 969 p1 = strchr(p, ':'); 970 if (p1) 971 *p1 = '\0'; 972 973 for(i = 0; i < context->num_cc_ops && context->cc_ops[i].prefix; i++) { 974 if(strcmp(context->cc_ops[i].prefix, p) == 0) { 975 free(p); 976 return &context->cc_ops[i]; 977 } 978 } 979 free(p); 980 return NULL; 981} 982 983struct krb5_cc_cache_cursor_data { 984 const krb5_cc_ops *ops; 985 krb5_cc_cursor cursor; 986}; 987 988/** 989 * Start iterating over all caches of specified type. See also 990 * krb5_cccol_cursor_new(). 991 992 * @param context A Kerberos 5 context 993 * @param type optional type to iterate over, if NULL, the default cache is used. 994 * @param cursor cursor should be freed with krb5_cc_cache_end_seq_get(). 995 * 996 * @return Return an error code or 0, see krb5_get_error_message(). 997 * 998 * @ingroup krb5_ccache 999 */ 1000 1001 1002krb5_error_code KRB5_LIB_FUNCTION 1003krb5_cc_cache_get_first (krb5_context context, 1004 const char *type, 1005 krb5_cc_cache_cursor *cursor) 1006{ 1007 const krb5_cc_ops *ops; 1008 krb5_error_code ret; 1009 1010 if (type == NULL) 1011 type = krb5_cc_default_name(context); 1012 1013 ops = krb5_cc_get_prefix_ops(context, type); 1014 if (ops == NULL) { 1015 krb5_set_error_message(context, KRB5_CC_UNKNOWN_TYPE, 1016 "Unknown type \"%s\" when iterating " 1017 "trying to iterate the credential caches", type); 1018 return KRB5_CC_UNKNOWN_TYPE; 1019 } 1020 1021 if (ops->get_cache_first == NULL) { 1022 krb5_set_error_message(context, KRB5_CC_NOSUPP, 1023 N_("Credential cache type %s doesn't support " 1024 "iterations over caches", "type"), 1025 ops->prefix); 1026 return KRB5_CC_NOSUPP; 1027 } 1028 1029 *cursor = calloc(1, sizeof(**cursor)); 1030 if (*cursor == NULL) { 1031 krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", "")); 1032 return ENOMEM; 1033 } 1034 1035 (*cursor)->ops = ops; 1036 1037 ret = ops->get_cache_first(context, &(*cursor)->cursor); 1038 if (ret) { 1039 free(*cursor); 1040 *cursor = NULL; 1041 } 1042 return ret; 1043} 1044 1045/** 1046 * Retrieve the next cache pointed to by (`cursor') in `id' 1047 * and advance `cursor'. 1048 * 1049 * @return Return 0 or an error code. Returns KRB5_CC_END when the end 1050 * of caches is reached, see krb5_get_error_message(). 1051 * 1052 * @ingroup krb5_ccache 1053 */ 1054 1055 1056krb5_error_code KRB5_LIB_FUNCTION 1057krb5_cc_cache_next (krb5_context context, 1058 krb5_cc_cache_cursor cursor, 1059 krb5_ccache *id) 1060{ 1061 return cursor->ops->get_cache_next(context, cursor->cursor, id); 1062} 1063 1064/** 1065 * Destroy the cursor `cursor'. 1066 * 1067 * @return Return an error code or 0, see krb5_get_error_message(). 1068 * 1069 * @ingroup krb5_ccache 1070 */ 1071 1072 1073krb5_error_code KRB5_LIB_FUNCTION 1074krb5_cc_cache_end_seq_get (krb5_context context, 1075 krb5_cc_cache_cursor cursor) 1076{ 1077 krb5_error_code ret; 1078 ret = cursor->ops->end_cache_get(context, cursor->cursor); 1079 cursor->ops = NULL; 1080 free(cursor); 1081 return ret; 1082} 1083 1084/** 1085 * Search for a matching credential cache that have the 1086 * `principal' as the default principal. On success, `id' needs to be 1087 * freed with krb5_cc_close() or krb5_cc_destroy(). 1088 * 1089 * @param context A Kerberos 5 context 1090 * @param client The principal to search for 1091 * @param id the returned credential cache 1092 * 1093 * @return On failure, error code is returned and `id' is set to NULL. 1094 * 1095 * @ingroup krb5_ccache 1096 */ 1097 1098 1099krb5_error_code KRB5_LIB_FUNCTION 1100krb5_cc_cache_match (krb5_context context, 1101 krb5_principal client, 1102 krb5_ccache *id) 1103{ 1104 krb5_cccol_cursor cursor; 1105 krb5_error_code ret; 1106 krb5_ccache cache = NULL; 1107 1108 *id = NULL; 1109 1110 ret = krb5_cccol_cursor_new (context, &cursor); 1111 if (ret) 1112 return ret; 1113 1114 while (krb5_cccol_cursor_next (context, cursor, &cache) == 0 && cache != NULL) { 1115 krb5_principal principal; 1116 1117 ret = krb5_cc_get_principal(context, cache, &principal); 1118 if (ret == 0) { 1119 krb5_boolean match; 1120 1121 match = krb5_principal_compare(context, principal, client); 1122 krb5_free_principal(context, principal); 1123 if (match) 1124 break; 1125 } 1126 1127 krb5_cc_close(context, cache); 1128 cache = NULL; 1129 } 1130 1131 krb5_cccol_cursor_free(context, &cursor); 1132 1133 if (cache == NULL) { 1134 char *str; 1135 1136 krb5_unparse_name(context, client, &str); 1137 1138 krb5_set_error_message(context, KRB5_CC_NOTFOUND, 1139 N_("Principal %s not found in any " 1140 "credential cache", ""), 1141 str ? str : "<out of memory>"); 1142 if (str) 1143 free(str); 1144 return KRB5_CC_NOTFOUND; 1145 } 1146 *id = cache; 1147 1148 return 0; 1149} 1150 1151/** 1152 * Move the content from one credential cache to another. The 1153 * operation is an atomic switch. 1154 * 1155 * @param context a Keberos context 1156 * @param from the credential cache to move the content from 1157 * @param to the credential cache to move the content to 1158 1159 * @return On sucess, from is freed. On failure, error code is 1160 * returned and from and to are both still allocated, see krb5_get_error_message(). 1161 * 1162 * @ingroup krb5_ccache 1163 */ 1164 1165krb5_error_code 1166krb5_cc_move(krb5_context context, krb5_ccache from, krb5_ccache to) 1167{ 1168 krb5_error_code ret; 1169 1170 if (strcmp(from->ops->prefix, to->ops->prefix) != 0) { 1171 krb5_set_error_message(context, KRB5_CC_NOSUPP, 1172 N_("Moving credentials between diffrent " 1173 "types not yet supported", "")); 1174 return KRB5_CC_NOSUPP; 1175 } 1176 1177 ret = (*to->ops->move)(context, from, to); 1178 if (ret == 0) { 1179 memset(from, 0, sizeof(*from)); 1180 free(from); 1181 } 1182 return ret; 1183} 1184 1185#define KRB5_CONF_NAME "krb5_ccache_conf_data" 1186#define KRB5_REALM_NAME "X-CACHECONF:" 1187 1188static krb5_error_code 1189build_conf_principals(krb5_context context, krb5_ccache id, 1190 krb5_const_principal principal, 1191 const char *name, krb5_creds *cred) 1192{ 1193 krb5_principal client; 1194 krb5_error_code ret; 1195 char *pname = NULL; 1196 1197 memset(cred, 0, sizeof(*cred)); 1198 1199 ret = krb5_cc_get_principal(context, id, &client); 1200 if (ret) 1201 return ret; 1202 1203 if (principal) { 1204 ret = krb5_unparse_name(context, principal, &pname); 1205 if (ret) 1206 return ret; 1207 } 1208 1209 ret = krb5_make_principal(context, &cred->server, 1210 KRB5_REALM_NAME, 1211 KRB5_CONF_NAME, name, pname, NULL); 1212 free(pname); 1213 if (ret) { 1214 krb5_free_principal(context, client); 1215 return ret; 1216 } 1217 ret = krb5_copy_principal(context, client, &cred->client); 1218 krb5_free_principal(context, client); 1219 return ret; 1220} 1221 1222/** 1223 * Return TRUE (non zero) if the principal is a configuration 1224 * principal (generated part of krb5_cc_set_config()). Returns FALSE 1225 * (zero) if not a configuration principal. 1226 * 1227 * @param context a Keberos context 1228 * @param principal principal to check if it a configuration principal 1229 * 1230 * @ingroup krb5_ccache 1231 */ 1232 1233krb5_boolean KRB5_LIB_FUNCTION 1234krb5_is_config_principal(krb5_context context, 1235 krb5_const_principal principal) 1236{ 1237 if (strcmp(principal->realm, KRB5_REALM_NAME) != 0) 1238 return FALSE; 1239 1240 if (principal->name.name_string.len == 0 || 1241 strcmp(principal->name.name_string.val[0], KRB5_CONF_NAME) != 0) 1242 return FALSE; 1243 1244 return TRUE; 1245} 1246 1247/** 1248 * Store some configuration for the credential cache in the cache. 1249 * Existing configuration under the same name is over-written. 1250 * 1251 * @param context a Keberos context 1252 * @param id the credential cache to store the data for 1253 * @param principal configuration for a specific principal, if 1254 * NULL, global for the whole cache. 1255 * @param name name under which the configuraion is stored. 1256 * @param data data to store, if NULL, configure is removed. 1257 * 1258 * @ingroup krb5_ccache 1259 */ 1260 1261krb5_error_code KRB5_LIB_FUNCTION 1262krb5_cc_set_config(krb5_context context, krb5_ccache id, 1263 krb5_const_principal principal, 1264 const char *name, krb5_data *data) 1265{ 1266 krb5_error_code ret; 1267 krb5_creds cred; 1268 1269 ret = build_conf_principals(context, id, principal, name, &cred); 1270 if (ret) 1271 goto out; 1272 1273 /* Remove old configuration */ 1274 ret = krb5_cc_remove_cred(context, id, 0, &cred); 1275 if (ret && ret != KRB5_CC_NOTFOUND) 1276 goto out; 1277 1278 if (data) { 1279 /* not that anyone care when this expire */ 1280 cred.times.authtime = time(NULL); 1281 cred.times.endtime = cred.times.authtime + 3600 * 24 * 30; 1282 1283 ret = krb5_data_copy(&cred.ticket, data->data, data->length); 1284 if (ret) 1285 goto out; 1286 1287 ret = krb5_cc_store_cred(context, id, &cred); 1288 } 1289 1290out: 1291 krb5_free_cred_contents (context, &cred); 1292 return ret; 1293} 1294 1295/** 1296 * Get some configuration for the credential cache in the cache. 1297 * 1298 * @param context a Keberos context 1299 * @param id the credential cache to store the data for 1300 * @param principal configuration for a specific principal, if 1301 * NULL, global for the whole cache. 1302 * @param name name under which the configuraion is stored. 1303 * @param data data to fetched, free with krb5_data_free() 1304 * 1305 * @ingroup krb5_ccache 1306 */ 1307 1308 1309krb5_error_code KRB5_LIB_FUNCTION 1310krb5_cc_get_config(krb5_context context, krb5_ccache id, 1311 krb5_const_principal principal, 1312 const char *name, krb5_data *data) 1313{ 1314 krb5_creds mcred, cred; 1315 krb5_error_code ret; 1316 1317 memset(&cred, 0, sizeof(cred)); 1318 krb5_data_zero(data); 1319 1320 ret = build_conf_principals(context, id, principal, name, &mcred); 1321 if (ret) 1322 goto out; 1323 1324 ret = krb5_cc_retrieve_cred(context, id, 0, &mcred, &cred); 1325 if (ret) 1326 goto out; 1327 1328 ret = krb5_data_copy(data, cred.ticket.data, cred.ticket.length); 1329 1330out: 1331 krb5_free_cred_contents (context, &cred); 1332 krb5_free_cred_contents (context, &mcred); 1333 return ret; 1334} 1335 1336/* 1337 * 1338 */ 1339 1340struct krb5_cccol_cursor { 1341 int idx; 1342 krb5_cc_cache_cursor cursor; 1343}; 1344 1345/** 1346 * Get a new cache interation cursor that will interate over all 1347 * credentials caches independent of type. 1348 * 1349 * @param context a Keberos context 1350 * @param cursor passed into krb5_cccol_cursor_next() and free with krb5_cccol_cursor_free(). 1351 * 1352 * @return Returns 0 or and error code, see krb5_get_error_message(). 1353 * 1354 * @ingroup krb5_ccache 1355 */ 1356 1357krb5_error_code KRB5_LIB_FUNCTION 1358krb5_cccol_cursor_new(krb5_context context, krb5_cccol_cursor *cursor) 1359{ 1360 *cursor = calloc(1, sizeof(**cursor)); 1361 if (*cursor == NULL) { 1362 krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", "")); 1363 return ENOMEM; 1364 } 1365 (*cursor)->idx = 0; 1366 (*cursor)->cursor = NULL; 1367 1368 return 0; 1369} 1370 1371/** 1372 * Get next credential cache from the iteration. 1373 * 1374 * @param context A Kerberos 5 context 1375 * @param cursor the iteration cursor 1376 * @param cache the returned cursor, pointer is set to NULL on failure 1377 * and a cache on success. The returned cache needs to be freed 1378 * with krb5_cc_close() or destroyed with krb5_cc_destroy(). 1379 * MIT Kerberos behavies slightly diffrent and sets cache to NULL 1380 * when all caches are iterated over and return 0. 1381 * 1382 * @return Return 0 or and error, KRB5_CC_END is returned at the end 1383 * of iteration. See krb5_get_error_message(). 1384 * 1385 * @ingroup krb5_ccache 1386 */ 1387 1388 1389krb5_error_code KRB5_LIB_FUNCTION 1390krb5_cccol_cursor_next(krb5_context context, krb5_cccol_cursor cursor, 1391 krb5_ccache *cache) 1392{ 1393 krb5_error_code ret; 1394 1395 *cache = NULL; 1396 1397 while (cursor->idx < context->num_cc_ops) { 1398 1399 if (cursor->cursor == NULL) { 1400 ret = krb5_cc_cache_get_first (context, 1401 context->cc_ops[cursor->idx].prefix, 1402 &cursor->cursor); 1403 if (ret) { 1404 cursor->idx++; 1405 continue; 1406 } 1407 } 1408 ret = krb5_cc_cache_next(context, cursor->cursor, cache); 1409 if (ret == 0) 1410 break; 1411 1412 krb5_cc_cache_end_seq_get(context, cursor->cursor); 1413 cursor->cursor = NULL; 1414 if (ret != KRB5_CC_END) 1415 break; 1416 1417 cursor->idx++; 1418 } 1419 if (cursor->idx >= context->num_cc_ops) { 1420 krb5_set_error_message(context, KRB5_CC_END, 1421 N_("Reached end of credential caches", "")); 1422 return KRB5_CC_END; 1423 } 1424 1425 return 0; 1426} 1427 1428/** 1429 * End an iteration and free all resources, can be done before end is reached. 1430 * 1431 * @param context A Kerberos 5 context 1432 * @param cursor the iteration cursor to be freed. 1433 * 1434 * @return Return 0 or and error, KRB5_CC_END is returned at the end 1435 * of iteration. See krb5_get_error_message(). 1436 * 1437 * @ingroup krb5_ccache 1438 */ 1439 1440krb5_error_code KRB5_LIB_FUNCTION 1441krb5_cccol_cursor_free(krb5_context context, krb5_cccol_cursor *cursor) 1442{ 1443 krb5_cccol_cursor c = *cursor; 1444 1445 *cursor = NULL; 1446 if (c) { 1447 if (c->cursor) 1448 krb5_cc_cache_end_seq_get(context, c->cursor); 1449 free(c); 1450 } 1451 return 0; 1452} 1453 1454/** 1455 * Return the last time the credential cache was modified. 1456 * 1457 * @param context A Kerberos 5 context 1458 * @param id The credential cache to probe 1459 * @param mtime the last modification time, set to 0 on error. 1460 1461 * @return Return 0 or and error. See krb5_get_error_message(). 1462 * 1463 * @ingroup krb5_ccache 1464 */ 1465 1466 1467krb5_error_code KRB5_LIB_FUNCTION 1468krb5_cc_last_change_time(krb5_context context, 1469 krb5_ccache id, 1470 krb5_timestamp *mtime) 1471{ 1472 *mtime = 0; 1473 return (*id->ops->lastchange)(context, id, mtime); 1474} 1475 1476/** 1477 * Return the last modfication time for a cache collection. The query 1478 * can be limited to a specific cache type. If the function return 0 1479 * and mtime is 0, there was no credentials in the caches. 1480 * 1481 * @param context A Kerberos 5 context 1482 * @param type The credential cache to probe, if NULL, all type are traversed. 1483 * @param mtime the last modification time, set to 0 on error. 1484 1485 * @return Return 0 or and error. See krb5_get_error_message(). 1486 * 1487 * @ingroup krb5_ccache 1488 */ 1489 1490krb5_error_code KRB5_LIB_FUNCTION 1491krb5_cccol_last_change_time(krb5_context context, 1492 const char *type, 1493 krb5_timestamp *mtime) 1494{ 1495 krb5_cccol_cursor cursor; 1496 krb5_error_code ret; 1497 krb5_ccache id; 1498 krb5_timestamp t = 0; 1499 1500 *mtime = 0; 1501 1502 ret = krb5_cccol_cursor_new (context, &cursor); 1503 if (ret) 1504 return ret; 1505 1506 while (krb5_cccol_cursor_next(context, cursor, &id) == 0 && id != NULL) { 1507 1508 if (type && strcmp(krb5_cc_get_type(context, id), type) != 0) 1509 continue; 1510 1511 ret = krb5_cc_last_change_time(context, id, &t); 1512 krb5_cc_close(context, id); 1513 if (ret) 1514 continue; 1515 if (t > *mtime) 1516 *mtime = t; 1517 } 1518 1519 krb5_cccol_cursor_free(context, &cursor); 1520 1521 return 0; 1522} 1523/** 1524 * Return a friendly name on credential cache. Free the result with krb5_xfree(). 1525 * 1526 * @return Return an error code or 0, see krb5_get_error_message(). 1527 * 1528 * @ingroup krb5_ccache 1529 */ 1530 1531krb5_error_code KRB5_LIB_FUNCTION 1532krb5_cc_get_friendly_name(krb5_context context, 1533 krb5_ccache id, 1534 char **name) 1535{ 1536 krb5_error_code ret; 1537 krb5_data data; 1538 1539 ret = krb5_cc_get_config(context, id, NULL, "FriendlyName", &data); 1540 if (ret) { 1541 krb5_principal principal; 1542 ret = krb5_cc_get_principal(context, id, &principal); 1543 if (ret) 1544 return ret; 1545 ret = krb5_unparse_name(context, principal, name); 1546 krb5_free_principal(context, principal); 1547 } else { 1548 ret = asprintf(name, "%.*s", (int)data.length, (char *)data.data); 1549 krb5_data_free(&data); 1550 if (ret <= 0) { 1551 ret = ENOMEM; 1552 krb5_set_error_message(context, ret, N_("malloc: out of memory", "")); 1553 } else 1554 ret = 0; 1555 } 1556 1557 return ret; 1558} 1559 1560/** 1561 * Set the friendly name on credential cache. 1562 * 1563 * @return Return an error code or 0, see krb5_get_error_message(). 1564 * 1565 * @ingroup krb5_ccache 1566 */ 1567 1568krb5_error_code KRB5_LIB_FUNCTION 1569krb5_cc_set_friendly_name(krb5_context context, 1570 krb5_ccache id, 1571 const char *name) 1572{ 1573 krb5_data data; 1574 1575 data.data = rk_UNCONST(name); 1576 data.length = strlen(name); 1577 1578 return krb5_cc_set_config(context, id, NULL, "FriendlyName", &data); 1579} 1580 1581/** 1582 * Get the lifetime of the initial ticket in the cache 1583 * 1584 * Get the lifetime of the initial ticket in the cache, if the initial 1585 * ticket was not found, the error code KRB5_CC_END is returned. 1586 * 1587 * @param context A Kerberos 5 context. 1588 * @param id a credential cache 1589 * @param t the relative lifetime of the initial ticket 1590 * 1591 * @return Return an error code or 0, see krb5_get_error_message(). 1592 * 1593 * @ingroup krb5_ccache 1594 */ 1595 1596krb5_error_code KRB5_LIB_FUNCTION 1597krb5_cc_get_lifetime(krb5_context context, krb5_ccache id, time_t *t) 1598{ 1599 krb5_cc_cursor cursor; 1600 krb5_error_code ret; 1601 krb5_creds cred; 1602 time_t now; 1603 1604 *t = 0; 1605 now = time(NULL); 1606 1607 ret = krb5_cc_start_seq_get(context, id, &cursor); 1608 if (ret) 1609 return ret; 1610 1611 while ((ret = krb5_cc_next_cred(context, id, &cursor, &cred)) == 0) { 1612 if (cred.flags.b.initial) { 1613 if (now < cred.times.endtime) 1614 *t = cred.times.endtime - now; 1615 krb5_free_cred_contents(context, &cred); 1616 goto out; 1617 } 1618 krb5_free_cred_contents(context, &cred); 1619 } 1620 1621 out: 1622 krb5_cc_end_seq_get(context, id, &cursor); 1623 1624 return ret; 1625} 1626