Home | History | Annotate | Line # | Download | only in krb5
cache.c revision 1.1.1.1.22.1
      1 /*	$NetBSD: cache.c,v 1.1.1.1.22.1 2014/08/10 06:47:30 tls Exp $	*/
      2 
      3 /*
      4  * Copyright (c) 1997 - 2008 Kungliga Tekniska Hgskolan
      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