LCOV - code coverage report
Current view: top level - third_party/heimdal/lib/krb5 - cache.c (source / functions) Hit Total Coverage
Test: coverage report for master 2f515e9b Lines: 432 744 58.1 %
Date: 2024-04-21 15:09:00 Functions: 44 62 71.0 %

          Line data    Source code
       1             : /*
       2             :  * Copyright (c) 1997 - 2008 Kungliga Tekniska Högskolan
       3             :  * (Royal Institute of Technology, Stockholm, Sweden).
       4             :  * All rights reserved.
       5             :  *
       6             :  * Portions Copyright (c) 2009 Apple Inc. All rights reserved.
       7             :  *
       8             :  * Redistribution and use in source and binary forms, with or without
       9             :  * modification, are permitted provided that the following conditions
      10             :  * are met:
      11             :  *
      12             :  * 1. Redistributions of source code must retain the above copyright
      13             :  *    notice, this list of conditions and the following disclaimer.
      14             :  *
      15             :  * 2. Redistributions in binary form must reproduce the above copyright
      16             :  *    notice, this list of conditions and the following disclaimer in the
      17             :  *    documentation and/or other materials provided with the distribution.
      18             :  *
      19             :  * 3. Neither the name of the Institute nor the names of its contributors
      20             :  *    may be used to endorse or promote products derived from this software
      21             :  *    without specific prior written permission.
      22             :  *
      23             :  * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
      24             :  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
      25             :  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
      26             :  * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
      27             :  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
      28             :  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
      29             :  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
      30             :  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
      31             :  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
      32             :  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
      33             :  * SUCH DAMAGE.
      34             :  */
      35             : 
      36             : #include "krb5_locl.h"
      37             : 
      38             : /**
      39             :  * @page krb5_ccache_intro The credential cache functions
      40             :  * @section section_krb5_ccache Kerberos credential caches
      41             :  *
      42             :  * krb5_ccache structure holds a Kerberos credential cache.
      43             :  *
      44             :  * Heimdal support the follow types of credential caches:
      45             :  *
      46             :  * - SCC
      47             :  *   Store the credential in a database
      48             :  * - FILE
      49             :  *   Store the credential in memory
      50             :  * - MEMORY
      51             :  *   Store the credential in memory
      52             :  * - API
      53             :  *   A credential cache server based solution for Mac OS X
      54             :  * - KCM
      55             :  *   A credential cache server based solution for all platforms
      56             :  *
      57             :  * @subsection Example
      58             :  *
      59             :  * This is a minimalistic version of klist:
      60             : @code
      61             : #include <krb5.h>
      62             : 
      63             : int
      64             : main (int argc, char **argv)
      65             : {
      66             :     krb5_context context;
      67             :     krb5_cc_cursor cursor;
      68             :     krb5_error_code ret;
      69             :     krb5_ccache id;
      70             :     krb5_creds creds;
      71             : 
      72             :     if (krb5_init_context (&context) != 0)
      73             :         errx(1, "krb5_context");
      74             : 
      75             :     ret = krb5_cc_default (context, &id);
      76             :     if (ret)
      77             :         krb5_err(context, 1, ret, "krb5_cc_default");
      78             : 
      79             :     ret = krb5_cc_start_seq_get(context, id, &cursor);
      80             :     if (ret)
      81             :         krb5_err(context, 1, ret, "krb5_cc_start_seq_get");
      82             : 
      83             :     while((ret = krb5_cc_next_cred(context, id, &cursor, &creds)) == 0){
      84             :         char *principal;
      85             : 
      86             :         krb5_unparse_name(context, creds.server, &principal);
      87             :         printf("principal: %s\\n", principal);
      88             :         free(principal);
      89             :         krb5_free_cred_contents (context, &creds);
      90             :     }
      91             :     ret = krb5_cc_end_seq_get(context, id, &cursor);
      92             :     if (ret)
      93             :         krb5_err(context, 1, ret, "krb5_cc_end_seq_get");
      94             : 
      95             :     krb5_cc_close(context, id);
      96             : 
      97             :     krb5_free_context(context);
      98             :     return 0;
      99             : }
     100             : * @endcode
     101             : */
     102             : 
     103             : static const krb5_cc_ops *
     104             : cc_get_prefix_ops(krb5_context context,
     105             :                   const char *prefix,
     106             :                   const char **residual);
     107             : 
     108             : /**
     109             :  * Add a new ccache type with operations `ops', overwriting any
     110             :  * existing one if `override'.
     111             :  *
     112             :  * @param context a Kerberos context
     113             :  * @param ops type of plugin symbol
     114             :  * @param override flag to select if the registration is to overide
     115             :  * an existing ops with the same name.
     116             :  *
     117             :  * @return Return an error code or 0, see krb5_get_error_message().
     118             :  *
     119             :  * @ingroup krb5_ccache
     120             :  */
     121             : 
     122             : KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
     123     3960020 : krb5_cc_register(krb5_context context,
     124             :                  const krb5_cc_ops *ops,
     125             :                  krb5_boolean override)
     126             : {
     127       97550 :     int i;
     128             : 
     129    11880060 :     for(i = 0; i < context->num_cc_ops && context->cc_ops[i]->prefix; i++) {
     130     7920040 :         if(strcmp(context->cc_ops[i]->prefix, ops->prefix) == 0) {
     131           0 :             if(!override) {
     132           0 :                 krb5_set_error_message(context,
     133             :                                        KRB5_CC_TYPE_EXISTS,
     134           0 :                                        N_("cache type %s already exists", "type"),
     135           0 :                                        ops->prefix);
     136           0 :                 return KRB5_CC_TYPE_EXISTS;
     137             :             }
     138           0 :             break;
     139             :         }
     140             :     }
     141     3960020 :     if(i == context->num_cc_ops) {
     142     3960020 :         const krb5_cc_ops **o = realloc(rk_UNCONST(context->cc_ops),
     143     3960020 :                                         (context->num_cc_ops + 1) *
     144             :                                         sizeof(context->cc_ops[0]));
     145     3960020 :         if(o == NULL) {
     146           0 :             krb5_set_error_message(context, KRB5_CC_NOMEM,
     147           0 :                                    N_("malloc: out of memory", ""));
     148           0 :             return KRB5_CC_NOMEM;
     149             :         }
     150     3960020 :         context->cc_ops = o;
     151     3960020 :         context->cc_ops[context->num_cc_ops] = NULL;
     152     3960020 :         context->num_cc_ops++;
     153             :     }
     154     3960020 :     context->cc_ops[i] = ops;
     155     3960020 :     return 0;
     156             : }
     157             : 
     158             : /*
     159             :  * Allocate the memory for a `id' and the that function table to
     160             :  * `ops'. Returns 0 or and error code.
     161             :  */
     162             : 
     163             : KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
     164      338802 : _krb5_cc_allocate(krb5_context context,
     165             :                   const krb5_cc_ops *ops,
     166             :                   krb5_ccache *id)
     167             : {
     168        4286 :     krb5_ccache p;
     169             : 
     170      338802 :     p = calloc(1, sizeof(*p));
     171      338802 :     if (p == NULL) {
     172           0 :         krb5_set_error_message(context, KRB5_CC_NOMEM,
     173           0 :                                N_("malloc: out of memory", ""));
     174           0 :         return KRB5_CC_NOMEM;
     175             :     }
     176      338802 :     p->ops = ops;
     177      338802 :     *id = p;
     178             : 
     179      338802 :     return 0;
     180             : }
     181             : 
     182             : /*
     183             :  * Allocate memory for a new ccache in `id' with operations `ops'
     184             :  * and name `residual'. Return 0 or an error code.
     185             :  */
     186             : 
     187             : static krb5_error_code
     188      315517 : allocate_ccache(krb5_context context,
     189             :                 const krb5_cc_ops *ops,
     190             :                 const char *residual,
     191             :                 const char *subsidiary,
     192             :                 krb5_ccache *id)
     193             : {
     194      315517 :     krb5_error_code ret = 0;
     195      315517 :     char *exp_residual = NULL;
     196        4286 :     int filepath;
     197             : 
     198      631034 :     filepath = (strcmp("FILE", ops->prefix) == 0
     199      260987 :                  || strcmp("DIR", ops->prefix) == 0
     200      576504 :                  || strcmp("SCC", ops->prefix) == 0);
     201             : 
     202      315517 :     if (residual)
     203      315517 :         ret = _krb5_expand_path_tokens(context, residual, filepath, &exp_residual);
     204      315517 :     if (ret == 0)
     205      315517 :         ret = _krb5_cc_allocate(context, ops, id);
     206             : 
     207      315517 :     if (ret == 0) {
     208      315517 :         if ((*id)->ops->version < KRB5_CC_OPS_VERSION_5
     209      315517 :             || (*id)->ops->resolve_2 == NULL) {
     210           0 :             ret = (*id)->ops->resolve(context, id, exp_residual);
     211             :         } else {
     212      315517 :             ret = (*id)->ops->resolve_2(context, id, exp_residual, subsidiary);
     213             :         }
     214             :     }
     215      315517 :     if (ret) {
     216           0 :         free(*id);
     217           0 :         *id = NULL;
     218             :     }
     219      315517 :     free(exp_residual);
     220      315517 :     return ret;
     221             : }
     222             : 
     223             : 
     224             : /**
     225             :  * Find and allocate a ccache in `id' from the specification in `residual'.
     226             :  * If the ccache name doesn't contain any colon, interpret it as a file name.
     227             :  *
     228             :  * @param context a Kerberos context.
     229             :  * @param name string name of a credential cache.
     230             :  * @param id return pointer to a found credential cache.
     231             :  *
     232             :  * @return Return 0 or an error code. In case of an error, id is set
     233             :  * to NULL, see krb5_get_error_message().
     234             :  *
     235             :  * @ingroup krb5_ccache
     236             :  */
     237             : 
     238             : 
     239             : KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
     240      315517 : krb5_cc_resolve(krb5_context context,
     241             :                 const char *name,
     242             :                 krb5_ccache *id)
     243             : {
     244        4286 :     const krb5_cc_ops *ops;
     245      315517 :     const char *residual = NULL;
     246             : 
     247      315517 :     *id = NULL;
     248             : 
     249      315517 :     ops = cc_get_prefix_ops(context, name, &residual);
     250      315517 :     if (ops == NULL)
     251         563 :         ops = &krb5_fcc_ops; /* residual will point to name */
     252             : 
     253      315517 :     return allocate_ccache(context, ops, residual, NULL, id);
     254             : }
     255             : 
     256             : #ifdef _WIN32
     257             : static const char *
     258             : get_default_cc_type_win32(krb5_context context)
     259             : {
     260             :     krb5_error_code ret;
     261             :     krb5_ccache id;
     262             : 
     263             :     /*
     264             :      * If the MSLSA ccache type has a principal name,
     265             :      * use it as the default.
     266             :      */
     267             :     ret = krb5_cc_resolve(context, "MSLSA:", &id);
     268             :     if (ret == 0) {
     269             :         krb5_principal princ;
     270             :         ret = krb5_cc_get_principal(context, id, &princ);
     271             :         krb5_cc_close(context, id);
     272             :         if (ret == 0) {
     273             :             krb5_free_principal(context, princ);
     274             :             return "MSLSA";
     275             :         }
     276             :     }
     277             : 
     278             :     /*
     279             :      * If the API: ccache can be resolved,
     280             :      * use it as the default.
     281             :      */
     282             :     ret = krb5_cc_resolve(context, "API:", &id);
     283             :     if (ret == 0) {
     284             :         krb5_cc_close(context, id);
     285             :         return "API";
     286             :     }
     287             : 
     288             :     return NULL;
     289             : }
     290             : #endif /* _WIN32 */
     291             : 
     292             : static const char *
     293          35 : get_default_cc_type(krb5_context context, int simple)
     294             : {
     295           0 :     const char *def_ccname;
     296           0 :     const char *def_cctype =
     297          35 :         krb5_config_get_string_default(context, NULL,
     298          35 :                                        secure_getenv("KRB5CCTYPE"),
     299             :                                        "libdefaults", "default_cc_type", NULL);
     300           0 :     const char *def_cccol =
     301          35 :         krb5_config_get_string(context, NULL, "libdefaults",
     302             :                                "default_cc_collection", NULL);
     303           0 :     const krb5_cc_ops *ops;
     304             : 
     305          35 :     if (!simple && (def_ccname = krb5_cc_default_name(context))) {
     306           0 :         ops = cc_get_prefix_ops(context, def_ccname, NULL);
     307           0 :         if (ops)
     308           0 :             return ops->prefix;
     309             :     }
     310          35 :     if (!def_cctype && def_cccol) {
     311           0 :         ops = cc_get_prefix_ops(context, def_cccol, NULL);
     312           0 :         if (ops)
     313           0 :             return ops->prefix;
     314             :     }
     315             : #ifdef _WIN32
     316             :     if (def_cctype == NULL)
     317             :         def_cctype = get_default_cc_type_win32(context);
     318             : #endif
     319          35 :     if (def_cctype == NULL)
     320          35 :         def_cctype = KRB5_DEFAULT_CCTYPE->prefix;
     321          35 :     return def_cctype;
     322             : }
     323             : 
     324             : /**
     325             :  * Find and allocate a ccache in `id' for the subsidiary cache named by
     326             :  * `subsidiary' in the collection named by `collection'.
     327             :  *
     328             :  * @param context a Kerberos context.
     329             :  * @param cctype string name of a credential cache collection type.
     330             :  * @param collection string name of a credential cache collection.
     331             :  * @param subsidiary string name of a credential cache in a collection.
     332             :  * @param id return pointer to a found credential cache.
     333             :  *
     334             :  * @return Return 0 or an error code. In case of an error, id is set
     335             :  * to NULL, see krb5_get_error_message().
     336             :  *
     337             :  * @ingroup krb5_ccache
     338             :  */
     339             : 
     340             : 
     341             : KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
     342           0 : krb5_cc_resolve_sub(krb5_context context,
     343             :                     const char *cctype,
     344             :                     const char *collection,
     345             :                     const char *subsidiary,
     346             :                     krb5_ccache *id)
     347             : {
     348           0 :     const krb5_cc_ops *ops = NULL;
     349             : 
     350           0 :     *id = NULL;
     351             : 
     352             :     /* Get the cctype from the collection, maybe */
     353           0 :     if (cctype == NULL && collection)
     354           0 :         ops = cc_get_prefix_ops(context, collection, &collection);
     355             : 
     356           0 :     if (ops == NULL)
     357           0 :         ops = cc_get_prefix_ops(context, get_default_cc_type(context, 0), NULL);
     358             : 
     359           0 :     if (ops == NULL) {
     360           0 :         krb5_set_error_message(context, KRB5_CC_UNKNOWN_TYPE,
     361           0 :                                N_("unknown ccache type %s", ""), cctype);
     362           0 :         return KRB5_CC_UNKNOWN_TYPE;
     363             :     }
     364             : 
     365           0 :     return allocate_ccache(context, ops, collection, subsidiary, id);
     366             : }
     367             : 
     368             : 
     369             : /**
     370             :  * Find and allocate a ccache in `id' from the specification in `residual', but
     371             :  * specific to the given principal `principal' by using the principal name as
     372             :  * the name of a "subsidiary" credentials cache in the collection named by
     373             :  * `name'.  If the ccache name doesn't contain any colon, interpret it as a
     374             :  * file name.
     375             :  *
     376             :  * @param context a Kerberos context.
     377             :  * @param name string name of a credential cache.
     378             :  * @param principal principal name of desired credentials.
     379             :  * @param id return pointer to a found credential cache.
     380             :  *
     381             :  * @return Return 0 or an error code. In case of an error, id is set
     382             :  * to NULL, see krb5_get_error_message().
     383             :  *
     384             :  * @ingroup krb5_ccache
     385             :  */
     386             : 
     387             : KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
     388           0 : krb5_cc_resolve_for(krb5_context context,
     389             :                     const char *cctype,
     390             :                     const char *name,
     391             :                     krb5_const_principal principal,
     392             :                     krb5_ccache *id)
     393             : {
     394           0 :     krb5_error_code ret;
     395           0 :     char *p, *s;
     396             : 
     397           0 :     *id = NULL;
     398             : 
     399           0 :     ret = krb5_unparse_name(context, principal, &p);
     400           0 :     if (ret)
     401           0 :         return ret;
     402             :     /*
     403             :      * Subsidiary components cannot have various chars in them that are used as
     404             :      * separators.  ':' is used for subsidiary separators in all ccache types
     405             :      * except FILE, where '+' is used instead because we can't use ':' in file
     406             :      * paths on Windows and because ':' is not in the POSIX safe set.
     407             :      */
     408           0 :     for (s = p; *s; s++) {
     409           0 :         switch (s[0]) {
     410           0 :         case ':':
     411             :         case '+':
     412             :         case '/':
     413             :         case '\\':
     414           0 :             s[0] = '-';
     415           0 :         default: break;
     416             :         }
     417             :     }
     418           0 :     ret = krb5_cc_resolve_sub(context, cctype, name, p, id);
     419           0 :     free(p);
     420           0 :     return ret;
     421             : }
     422             : 
     423             : /**
     424             :  * Generates a new unique ccache of `type` in `id'. If `type' is NULL,
     425             :  * the library chooses the default credential cache type. The supplied
     426             :  * `hint' (that can be NULL) is a string that the credential cache
     427             :  * type can use to base the name of the credential on, this is to make
     428             :  * it easier for the user to differentiate the credentials.
     429             :  *
     430             :  * @return Return an error code or 0, see krb5_get_error_message().
     431             :  *
     432             :  * @ingroup krb5_ccache
     433             :  */
     434             : 
     435             : KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
     436       23285 : krb5_cc_new_unique(krb5_context context, const char *type,
     437             :                    const char *hint, krb5_ccache *id)
     438             : {
     439           0 :     const krb5_cc_ops *ops;
     440           0 :     krb5_error_code ret;
     441             : 
     442       23285 :     if (type == NULL)
     443          35 :         type = get_default_cc_type(context, 1);
     444             : 
     445       23285 :     ops = krb5_cc_get_prefix_ops(context, type);
     446       23285 :     if (ops == NULL) {
     447           0 :         krb5_set_error_message(context, KRB5_CC_UNKNOWN_TYPE,
     448             :                               "Credential cache type %s is unknown", type);
     449           0 :         return KRB5_CC_UNKNOWN_TYPE;
     450             :     }
     451             : 
     452       23285 :     ret = _krb5_cc_allocate(context, ops, id);
     453       23285 :     if (ret)
     454           0 :         return ret;
     455       23285 :     ret = (*id)->ops->gen_new(context, id);
     456       23285 :     if (ret) {
     457           0 :         free(*id);
     458           0 :         *id = NULL;
     459             :     }
     460       23285 :     return ret;
     461             : }
     462             : 
     463             : /**
     464             :  * Return the name of the ccache `id'
     465             :  *
     466             :  * @ingroup krb5_ccache
     467             :  */
     468             : 
     469             : 
     470             : KRB5_LIB_FUNCTION const char* KRB5_LIB_CALL
     471      102461 : krb5_cc_get_name(krb5_context context,
     472             :                  krb5_ccache id)
     473             : {
     474      102461 :     const char *name = NULL;
     475             : 
     476      102461 :     if (id->ops->version < KRB5_CC_OPS_VERSION_5
     477      102461 :         || id->ops->get_name_2 == NULL)
     478           0 :         return id->ops->get_name(context, id);
     479             : 
     480      102461 :     (void) id->ops->get_name_2(context, id, &name, NULL, NULL);
     481      102461 :     return name;
     482             : }
     483             : 
     484             : /**
     485             :  * Return the name of the ccache collection associated with `id'
     486             :  *
     487             :  * @ingroup krb5_ccache
     488             :  */
     489             : 
     490             : 
     491             : KRB5_LIB_FUNCTION const char* KRB5_LIB_CALL
     492           0 : krb5_cc_get_collection(krb5_context context, krb5_ccache id)
     493             : {
     494           0 :     const char *name = NULL;
     495             : 
     496           0 :     if (id->ops->version < KRB5_CC_OPS_VERSION_5
     497           0 :         || id->ops->get_name_2 == NULL)
     498           0 :         return NULL;
     499             : 
     500           0 :     (void) id->ops->get_name_2(context, id, NULL, &name, NULL);
     501           0 :     return name;
     502             : }
     503             : 
     504             : /**
     505             :  * Return the name of the subsidiary ccache of `id'
     506             :  *
     507             :  * @ingroup krb5_ccache
     508             :  */
     509             : 
     510             : 
     511             : KRB5_LIB_FUNCTION const char* KRB5_LIB_CALL
     512           0 : krb5_cc_get_subsidiary(krb5_context context, krb5_ccache id)
     513             : {
     514           0 :     const char *name = NULL;
     515             : 
     516           0 :     if (id->ops->version >= KRB5_CC_OPS_VERSION_5
     517           0 :         && id->ops->get_name_2 != NULL)
     518           0 :         (void) id->ops->get_name_2(context, id, NULL, NULL, &name);
     519           0 :     return name;
     520             : }
     521             : 
     522             : /**
     523             :  * Return the type of the ccache `id'.
     524             :  *
     525             :  * @ingroup krb5_ccache
     526             :  */
     527             : 
     528             : 
     529             : KRB5_LIB_FUNCTION const char* KRB5_LIB_CALL
     530      188063 : krb5_cc_get_type(krb5_context context,
     531             :                  krb5_ccache id)
     532             : {
     533      188063 :     return id->ops->prefix;
     534             : }
     535             : 
     536             : /**
     537             :  * Return the complete resolvable name the cache
     538             : 
     539             :  * @param context a Kerberos context
     540             :  * @param id return pointer to a found credential cache
     541             :  * @param str the returned name of a credential cache, free with krb5_xfree()
     542             :  *
     543             :  * @return Returns 0 or an error (and then *str is set to NULL).
     544             :  *
     545             :  * @ingroup krb5_ccache
     546             :  */
     547             : 
     548             : 
     549             : KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
     550      102461 : krb5_cc_get_full_name(krb5_context context,
     551             :                       krb5_ccache id,
     552             :                       char **str)
     553             : {
     554        1466 :     const char *type, *name;
     555             : 
     556      102461 :     *str = NULL;
     557             : 
     558      102461 :     type = krb5_cc_get_type(context, id);
     559      102461 :     if (type == NULL) {
     560           0 :         krb5_set_error_message(context, KRB5_CC_UNKNOWN_TYPE,
     561             :                                "cache has no name of type");
     562           0 :         return KRB5_CC_UNKNOWN_TYPE;
     563             :     }
     564             : 
     565      102461 :     name = krb5_cc_get_name(context, id);
     566      102461 :     if (name == NULL) {
     567           0 :         krb5_set_error_message(context, KRB5_CC_BADNAME,
     568             :                                "cache of type %s has no name", type);
     569           0 :         return KRB5_CC_BADNAME;
     570             :     }
     571             : 
     572      102461 :     if (asprintf(str, "%s:%s", type, name) == -1) {
     573           0 :         *str = NULL;
     574           0 :         return krb5_enomem(context);
     575             :     }
     576      100995 :     return 0;
     577             : }
     578             : 
     579             : /**
     580             :  * Return krb5_cc_ops of a the ccache `id'.
     581             :  *
     582             :  * @ingroup krb5_ccache
     583             :  */
     584             : 
     585             : 
     586             : KRB5_LIB_FUNCTION const krb5_cc_ops * KRB5_LIB_CALL
     587           0 : krb5_cc_get_ops(krb5_context context, krb5_ccache id)
     588             : {
     589           0 :     return id->ops;
     590             : }
     591             : 
     592             : /*
     593             :  * Expand variables in `str' into `res'
     594             :  */
     595             : 
     596             : KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
     597           0 : _krb5_expand_default_cc_name(krb5_context context, const char *str, char **res)
     598             : {
     599           0 :     int filepath;
     600             : 
     601           0 :     filepath = (strncmp("FILE:", str, 5) == 0
     602           0 :                  || strncmp("DIR:", str, 4) == 0
     603           0 :                  || strncmp("SCC:", str, 4) == 0);
     604             : 
     605           0 :     return _krb5_expand_path_tokens(context, str, filepath, res);
     606             : }
     607             : 
     608             : /*
     609             :  * Return non-zero if envirnoment that will determine default krb5cc
     610             :  * name has changed.
     611             :  */
     612             : 
     613             : static int
     614         194 : environment_changed(krb5_context context)
     615             : {
     616           0 :     const char *e;
     617             : 
     618             :     /* if the cc name was set, don't change it */
     619         194 :     if (context->default_cc_name_set)
     620           0 :         return 0;
     621             : 
     622             :     /* XXX performance: always ask KCM/API if default name has changed */
     623         194 :     if (context->default_cc_name &&
     624         194 :         (strncmp(context->default_cc_name, "KCM:", 4) == 0 ||
     625         194 :          strncmp(context->default_cc_name, "API:", 4) == 0))
     626           0 :         return 1;
     627             : 
     628         194 :     e = secure_getenv("KRB5CCNAME");
     629         194 :     if (e == NULL) {
     630           0 :         if (context->default_cc_name_env) {
     631           0 :             free(context->default_cc_name_env);
     632           0 :             context->default_cc_name_env = NULL;
     633           0 :             return 1;
     634             :         }
     635             :     } else {
     636         194 :         if (context->default_cc_name_env == NULL)
     637           0 :             return 1;
     638         194 :         if (strcmp(e, context->default_cc_name_env) != 0)
     639           0 :             return 1;
     640             :     }
     641         194 :     return 0;
     642             : }
     643             : 
     644             : /**
     645             :  * Switch the default default credential cache for a specific
     646             :  * credcache type (and name for some implementations).
     647             :  *
     648             :  * @return Return an error code or 0, see krb5_get_error_message().
     649             :  *
     650             :  * @ingroup krb5_ccache
     651             :  */
     652             : 
     653             : KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
     654         113 : krb5_cc_switch(krb5_context context, krb5_ccache id)
     655             : {
     656             : #ifdef _WIN32
     657             :     _krb5_set_default_cc_name_to_registry(context, id);
     658             : #endif
     659             : 
     660         113 :     if (id->ops->version == KRB5_CC_OPS_VERSION_0
     661         113 :         || id->ops->set_default == NULL)
     662           0 :         return 0;
     663             : 
     664         113 :     return (*id->ops->set_default)(context, id);
     665             : }
     666             : 
     667             : /**
     668             :  * Return true if the default credential cache support switch
     669             :  *
     670             :  * @ingroup krb5_ccache
     671             :  */
     672             : 
     673             : KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL
     674          52 : krb5_cc_support_switch(krb5_context context, const char *type)
     675             : {
     676           0 :     const krb5_cc_ops *ops;
     677             : 
     678          52 :     ops = krb5_cc_get_prefix_ops(context, type);
     679          52 :     if (ops && ops->version > KRB5_CC_OPS_VERSION_0 && ops->set_default)
     680          52 :         return 1;
     681           0 :     return FALSE;
     682             : }
     683             : 
     684             : /**
     685             :  * Set the default cc name for `context' to `name'.
     686             :  *
     687             :  * @ingroup krb5_ccache
     688             :  */
     689             : 
     690             : KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
     691       85395 : krb5_cc_set_default_name(krb5_context context, const char *name)
     692             : {
     693       85395 :     krb5_error_code ret = 0;
     694       85395 :     char *p = NULL;
     695             : 
     696       85395 :     if (name == NULL) {
     697         437 :         const char *e;
     698             : 
     699       85395 :         if ((e = secure_getenv("KRB5CCNAME"))) {
     700       85395 :             if ((p = strdup(e)) == NULL)
     701           0 :                 return krb5_enomem(context);
     702             : 
     703       85395 :             free(context->default_cc_name_env);
     704       85395 :             context->default_cc_name_env = p;
     705             : 
     706       85395 :             if ((p = strdup(e)) == NULL)
     707           0 :                 return krb5_enomem(context);
     708             : 
     709             :             /*
     710             :              * We're resetting the default ccache name.  Recall that we got
     711             :              * this from the environment, which might change.
     712             :              */
     713       85395 :             context->default_cc_name_set = 0;
     714           0 :         } else if ((e = krb5_cc_configured_default_name(context))) {
     715           0 :             if ((p = strdup(e)) == NULL)
     716           0 :                 return krb5_enomem(context);
     717             : 
     718             :             /*
     719             :              * Since $KRB5CCNAME was not set, and since we got the default
     720             :              * ccache name from configuration, we'll not want
     721             :              * environment_changed() to return true to avoid re-doing the
     722             :              * krb5_cc_configured_default_name() call unnecessarily.
     723             :              *
     724             :              * XXX Perhaps if we got the ccache name from the registry then
     725             :              *     we'd want to recheck it?  If so we might need an indication
     726             :              *     from krb5_cc_configured_default_name() about that!
     727             :              */
     728           0 :             context->default_cc_name_set = 1;
     729             :         }
     730             :     } else {
     731           0 :         int filepath = (strncmp("FILE:", name, 5) == 0 ||
     732           0 :                         strncmp("DIR:",  name, 4) == 0 ||
     733           0 :                         strncmp("SCC:",  name, 4) == 0);
     734             : 
     735           0 :         ret = _krb5_expand_path_tokens(context, name, filepath, &p);
     736           0 :         if (ret)
     737           0 :             return ret;
     738             : 
     739             :         /*
     740             :          * Since the default ccache name was set explicitly, we won't want
     741             :          * environment_changed() to return true until the default ccache name
     742             :          * is reset.
     743             :          */
     744           0 :         context->default_cc_name_set = 1;
     745             :     }
     746             : 
     747       85395 :     free(context->default_cc_name);
     748       85395 :     context->default_cc_name = p;
     749       85395 :     return 0;
     750             : }
     751             : 
     752             : /**
     753             :  * Return a pointer to a context static string containing the default
     754             :  * ccache name.
     755             :  *
     756             :  * @return String to the default credential cache name.
     757             :  *
     758             :  * @ingroup krb5_ccache
     759             :  */
     760             : 
     761             : 
     762             : KRB5_LIB_FUNCTION const char* KRB5_LIB_CALL
     763       85589 : krb5_cc_default_name(krb5_context context)
     764             : {
     765       85589 :     if (context->default_cc_name == NULL || environment_changed(context))
     766       85395 :         krb5_cc_set_default_name(context, NULL);
     767             : 
     768       85589 :     return context->default_cc_name;
     769             : }
     770             : 
     771             : KRB5_LIB_FUNCTION const char * KRB5_LIB_CALL
     772           0 : krb5_cc_configured_default_name(krb5_context context)
     773             : {
     774           0 :     krb5_error_code ret = 0;
     775             : #ifdef _WIN32
     776             :     krb5_ccache id;
     777             : #endif
     778           0 :     const char *cfg;
     779           0 :     char *expanded;
     780           0 :     const krb5_cc_ops *ops;
     781             : 
     782           0 :     if (context->configured_default_cc_name)
     783           0 :         return context->configured_default_cc_name;
     784             : 
     785             : #ifdef _WIN32
     786             :     if ((expanded = _krb5_get_default_cc_name_from_registry(context)))
     787             :         return context->configured_default_cc_name = expanded;
     788             : #endif
     789             : 
     790             :     /* If there's a configured default, expand the tokens and use it */
     791           0 :     cfg = krb5_config_get_string(context, NULL, "libdefaults",
     792             :                                  "default_cc_name", NULL);
     793           0 :     if (cfg == NULL)
     794           0 :         cfg = krb5_config_get_string(context, NULL, "libdefaults",
     795             :                                      "default_ccache_name", NULL);
     796           0 :     if (cfg) {
     797           0 :         ret = _krb5_expand_default_cc_name(context, cfg, &expanded);
     798           0 :         if (ret) {
     799           0 :             krb5_set_error_message(context, ret,
     800             :                                    "token expansion failed for %s", cfg);
     801           0 :             return NULL;
     802             :         }
     803           0 :         return context->configured_default_cc_name = expanded;
     804             :     }
     805             : 
     806             :     /* Else try a configured default ccache type's default */
     807           0 :     cfg = get_default_cc_type(context, 1);
     808           0 :     if ((ops = krb5_cc_get_prefix_ops(context, cfg)) == NULL) {
     809           0 :         krb5_set_error_message(context, KRB5_CC_UNKNOWN_TYPE,
     810             :                                "unknown configured credential cache "
     811             :                                "type %s", cfg);
     812           0 :         return NULL;
     813             :     }
     814             : 
     815             :     /* The get_default_name() method expands any tokens */
     816           0 :     ret = (*ops->get_default_name)(context, &expanded);
     817           0 :     if (ret) {
     818           0 :         krb5_set_error_message(context, ret, "failed to find a default "
     819             :                                "ccache for default ccache type %s", cfg);
     820           0 :         return NULL;
     821             :     }
     822           0 :     return context->configured_default_cc_name = expanded;
     823             : }
     824             : 
     825             : KRB5_LIB_FUNCTION char * KRB5_LIB_CALL
     826           0 : krb5_cccol_get_default_ccname(krb5_context context)
     827             : {
     828           0 :     const char *cfg = get_default_cc_type(context, 1);
     829           0 :     char *cccol_default_ccname;
     830           0 :     const krb5_cc_ops *ops = krb5_cc_get_prefix_ops(context, cfg);
     831             : 
     832           0 :     (void) (*ops->get_default_name)(context, &cccol_default_ccname);
     833           0 :     return cccol_default_ccname;
     834             : }
     835             : 
     836             : /**
     837             :  * Open the default ccache in `id'.
     838             :  *
     839             :  * @return Return an error code or 0, see krb5_get_error_message().
     840             :  *
     841             :  * @ingroup krb5_ccache
     842             :  */
     843             : 
     844             : KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
     845       66291 : krb5_cc_default(krb5_context context,
     846             :                 krb5_ccache *id)
     847             : {
     848       66291 :     const char *p = krb5_cc_default_name(context);
     849             : 
     850       66291 :     *id = NULL;
     851       66291 :     if (p == NULL)
     852           0 :         return krb5_enomem(context);
     853       66291 :     return krb5_cc_resolve(context, p, id);
     854             : }
     855             : 
     856             : /**
     857             :  * Open the named subsidiary cache from the default ccache collection in `id'.
     858             :  *
     859             :  * @return Return an error code or 0, see krb5_get_error_message().
     860             :  *
     861             :  * @ingroup krb5_ccache
     862             :  */
     863             : 
     864             : KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
     865           0 : krb5_cc_default_sub(krb5_context context,
     866             :                     const char *subsidiary,
     867             :                     krb5_ccache *id)
     868             : {
     869           0 :     return krb5_cc_resolve_sub(context, get_default_cc_type(context, 0), NULL,
     870             :                                subsidiary, id);
     871             : }
     872             : 
     873             : /**
     874             :  * Open the default ccache in `id' that corresponds to the given principal.
     875             :  *
     876             :  * @return Return an error code or 0, see krb5_get_error_message().
     877             :  *
     878             :  * @ingroup krb5_ccache
     879             :  */
     880             : 
     881             : KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
     882           0 : krb5_cc_default_for(krb5_context context,
     883             :                     krb5_const_principal principal,
     884             :                     krb5_ccache *id)
     885             : {
     886           0 :     return krb5_cc_resolve_for(context, get_default_cc_type(context, 0), NULL,
     887             :                                principal, id);
     888             : }
     889             : 
     890             : /**
     891             :  * Create a new ccache in `id' for `primary_principal'.
     892             :  *
     893             :  * @return Return an error code or 0, see krb5_get_error_message().
     894             :  *
     895             :  * @ingroup krb5_ccache
     896             :  */
     897             : 
     898             : 
     899             : KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
     900      230248 : krb5_cc_initialize(krb5_context context,
     901             :                    krb5_ccache id,
     902             :                    krb5_principal primary_principal)
     903             : {
     904        3264 :     krb5_error_code ret;
     905             : 
     906      230248 :     ret = (*id->ops->init)(context, id, primary_principal);
     907      230248 :     if (ret == 0) {
     908      230248 :         id->cc_kx509_done = 0;
     909      230248 :         id->cc_initialized = 1;
     910      230248 :         id->cc_need_start_realm = 1;
     911      230248 :         id->cc_start_tgt_stored = 0;
     912             :     }
     913      230248 :     return ret;
     914             : }
     915             : 
     916             : 
     917             : /**
     918             :  * Remove the ccache `id'.
     919             :  *
     920             :  * @return Return an error code or 0, see krb5_get_error_message().
     921             :  *
     922             :  * @ingroup krb5_ccache
     923             :  */
     924             : 
     925             : 
     926             : KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
     927      156039 : krb5_cc_destroy(krb5_context context,
     928             :                 krb5_ccache id)
     929             : {
     930      156039 :     krb5_error_code ret2 = 0;
     931        2382 :     krb5_error_code ret;
     932        2382 :     krb5_data d;
     933             : 
     934             :     /*
     935             :      * Destroy associated hx509 PKIX credential store created by krb5_kx509*().
     936             :      */
     937      156039 :     if (krb5_cc_get_config(context, id, NULL, "kx509store", &d) == 0) {
     938           0 :         char *name;
     939             : 
     940           0 :         if ((name = strndup(d.data, d.length)) == NULL) {
     941           0 :             ret2 = krb5_enomem(context);
     942             :         } else {
     943           0 :             hx509_certs certs;
     944           0 :             ret = hx509_certs_init(context->hx509ctx, name, 0, NULL, &certs);
     945           0 :             if (ret == 0)
     946           0 :                 ret2 = hx509_certs_destroy(context->hx509ctx, &certs);
     947             :             else
     948           0 :                 hx509_certs_free(&certs);
     949           0 :             free(name);
     950             :         }
     951             :     }
     952             : 
     953      156039 :     ret = (*id->ops->destroy)(context, id);
     954      156039 :     (void) krb5_cc_close(context, id);
     955      156039 :     return ret ? ret : ret2;
     956             : }
     957             : 
     958             : /**
     959             :  * Stop using the ccache `id' and free the related resources.
     960             :  *
     961             :  * @return Return an error code or 0, see krb5_get_error_message().
     962             :  *
     963             :  * @ingroup krb5_ccache
     964             :  */
     965             : 
     966             : 
     967             : KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
     968      337180 : krb5_cc_close(krb5_context context,
     969             :               krb5_ccache id)
     970             : {
     971        4275 :     krb5_error_code ret;
     972             : 
     973      337180 :     if (!id)
     974           0 :         return 0;
     975             : 
     976             :     /*
     977             :      * We want to automatically acquire a PKIX credential using kx509.
     978             :      *
     979             :      * This can be slow if we're generating an RSA key.  Plus it means talking
     980             :      * to the KDC.
     981             :      *
     982             :      * We only want to do this when:
     983             :      *
     984             :      *  - krb5_cc_initialize() was called on this ccache handle,
     985             :      *  - a start TGT was stored (actually, a cross-realm TGT would do),
     986             :      *
     987             :      * and
     988             :      *
     989             :      *  - we aren't creating a gss_cred_id_t for a delegated credential.
     990             :      *
     991             :      * We only have a heuristic for the last condition: that `id' is not a
     992             :      * MEMORY ccache, which is what's used for delegated credentials.
     993             :      *
     994             :      * We really only want to do this when storing a credential in a user's
     995             :      * default ccache, but we leave it to krb5_kx509() to do that check.
     996             :      *
     997             :      * XXX Perhaps we should do what krb5_kx509() does here, and just call
     998             :      *     krb5_kx509_ext() (renamed to krb5_kx509()).  Then we wouldn't need
     999             :      *     the delegated cred handle heuristic.
    1000             :      */
    1001      337180 :     if (id->cc_initialized && id->cc_start_tgt_stored && !id->cc_kx509_done &&
    1002       84625 :         strcmp("MEMORY", krb5_cc_get_type(context, id)) != 0) {
    1003           0 :         krb5_boolean enabled;
    1004             : 
    1005         284 :         krb5_appdefault_boolean(context, NULL, NULL, "enable_kx509", FALSE,
    1006             :                                 &enabled);
    1007         284 :         if (enabled) {
    1008           0 :             _krb5_debug(context, 2, "attempting to fetch a certificate using "
    1009             :                         "kx509");
    1010           0 :             ret = krb5_kx509(context, id, NULL);
    1011           0 :             if (ret)
    1012           0 :                 _krb5_debug(context, 2, "failed to fetch a certificate");
    1013             :             else
    1014           0 :                 _krb5_debug(context, 2, "fetched a certificate");
    1015             :         }
    1016             :     }
    1017             : 
    1018      337180 :     ret = (*id->ops->close)(context, id);
    1019      337180 :     free(id);
    1020      337180 :     return ret;
    1021             : }
    1022             : 
    1023             : /**
    1024             :  * Store `creds' in the ccache `id'.
    1025             :  *
    1026             :  * @return Return an error code or 0, see krb5_get_error_message().
    1027             :  *
    1028             :  * @ingroup krb5_ccache
    1029             :  */
    1030             : 
    1031             : 
    1032             : KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
    1033      335583 : krb5_cc_store_cred(krb5_context context,
    1034             :                    krb5_ccache id,
    1035             :                    krb5_creds *creds)
    1036             : {
    1037        5425 :     krb5_error_code ret;
    1038        5425 :     krb5_data realm;
    1039      335583 :     const char *cfg = "";
    1040             : 
    1041             :     /* Automatic cc_config-setting and other actions */
    1042      669826 :     if (krb5_principal_get_num_comp(context, creds->server) > 1 &&
    1043      334243 :         krb5_is_config_principal(context, creds->server))
    1044      158610 :         cfg = krb5_principal_get_comp_string(context, creds->server, 1);
    1045             : 
    1046      335583 :     if (id->cc_initialized && !id->cc_need_start_realm &&
    1047       77690 :         strcmp(cfg, "start_realm") == 0)
    1048         135 :         return 0;
    1049             : 
    1050      335448 :     ret = (*id->ops->store)(context, id, creds);
    1051      335448 :     if (ret)
    1052           0 :         return ret;
    1053             : 
    1054      335448 :     if (id->cc_initialized && !id->cc_start_tgt_stored &&
    1055      158558 :         id->cc_need_start_realm &&
    1056      158558 :         krb5_principal_is_root_krbtgt(context, creds->server)) {
    1057             :         /* Mark the first root TGT's realm as the start realm */
    1058       86125 :         id->cc_start_tgt_stored = 1;
    1059       86125 :         realm.length = strlen(creds->server->realm);
    1060       86125 :         realm.data = creds->server->realm;
    1061       86125 :         (void) krb5_cc_set_config(context, id, NULL, "start_realm", &realm);
    1062       86125 :         id->cc_need_start_realm = 0;
    1063      249323 :     } else if (id->cc_initialized && id->cc_start_tgt_stored &&
    1064       91620 :                !id->cc_kx509_done && strcmp(cfg, "kx509cert") == 0) {
    1065             :         /*
    1066             :          * Do not attempt kx509 at cc close time -- we're copying a ccache and
    1067             :          * we've already got a cert (and private key).
    1068             :          */
    1069           0 :         id->cc_kx509_done = 1;
    1070      249323 :     } else if (id->cc_initialized && id->cc_start_tgt_stored &&
    1071       91620 :                !id->cc_kx509_done && strcmp(cfg, "kx509_service_status") == 0) {
    1072             :         /*
    1073             :          * Do not attempt kx509 at cc close time -- we're copying a ccache and
    1074             :          * we know the kx509 service is not available.
    1075             :          */
    1076           0 :         id->cc_kx509_done = 1;
    1077      249323 :     } else if (id->cc_initialized && strcmp(cfg, "start_realm") == 0) {
    1078             :         /*
    1079             :          * If the caller is storing a start_realm ccconfig, then stop looking
    1080             :          * for root TGTs to mark as the start_realm.
    1081             :          *
    1082             :          * By honoring any start_realm cc config stored, we interop both, with
    1083             :          * ccache implementations that don't preserve insertion order, and
    1084             :          * Kerberos implementations that store this cc config before the TGT.
    1085             :          */
    1086      158185 :         id->cc_need_start_realm = 0;
    1087             :     }
    1088      330023 :     return ret;
    1089             : }
    1090             : 
    1091             : /**
    1092             :  * Retrieve the credential identified by `mcreds' (and `whichfields')
    1093             :  * from `id' in `creds'. 'creds' must be free by the caller using
    1094             :  * krb5_free_cred_contents.
    1095             :  *
    1096             :  * @param context A Kerberos 5 context
    1097             :  * @param id a Kerberos 5 credential cache
    1098             :  * @param whichfields what fields to use for matching credentials, same
    1099             :  *        flags as whichfields in krb5_compare_creds()
    1100             :  * @param mcreds template credential to use for comparing
    1101             :  * @param creds returned credential, free with krb5_free_cred_contents()
    1102             :  *
    1103             :  * @return Return an error code or 0, see krb5_get_error_message().
    1104             :  *
    1105             :  * @ingroup krb5_ccache
    1106             :  */
    1107             : 
    1108             : 
    1109             : KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
    1110      501781 : krb5_cc_retrieve_cred(krb5_context context,
    1111             :                       krb5_ccache id,
    1112             :                       krb5_flags whichfields,
    1113             :                       const krb5_creds *mcreds,
    1114             :                       krb5_creds *creds)
    1115             : {
    1116       11779 :     krb5_error_code ret;
    1117       11779 :     krb5_cc_cursor cursor;
    1118             : 
    1119      501781 :     if (id->ops->retrieve != NULL) {
    1120           0 :         return (*id->ops->retrieve)(context, id, whichfields,
    1121             :                                     mcreds, creds);
    1122             :     }
    1123             : 
    1124      501781 :     ret = krb5_cc_start_seq_get(context, id, &cursor);
    1125      501781 :     if (ret)
    1126           0 :         return ret;
    1127     1380497 :     while((ret = krb5_cc_next_cred(context, id, &cursor, creds)) == 0){
    1128     1130174 :         if(krb5_compare_creds(context, whichfields, mcreds, creds)){
    1129      244971 :             ret = 0;
    1130      244971 :             break;
    1131             :         }
    1132      878716 :         krb5_free_cred_contents (context, creds);
    1133             :     }
    1134      501781 :     krb5_cc_end_seq_get(context, id, &cursor);
    1135      501781 :     return ret;
    1136             : }
    1137             : 
    1138             : /**
    1139             :  * Return the principal of `id' in `principal'.
    1140             :  *
    1141             :  * @return Return an error code or 0, see krb5_get_error_message().
    1142             :  *
    1143             :  * @ingroup krb5_ccache
    1144             :  */
    1145             : 
    1146             : 
    1147             : KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
    1148      854027 : krb5_cc_get_principal(krb5_context context,
    1149             :                       krb5_ccache id,
    1150             :                       krb5_principal *principal)
    1151             : {
    1152      854027 :     return (*id->ops->get_princ)(context, id, principal);
    1153             : }
    1154             : 
    1155             : /**
    1156             :  * Start iterating over `id', `cursor' is initialized to the
    1157             :  * beginning.  Caller must free the cursor with krb5_cc_end_seq_get().
    1158             :  *
    1159             :  * @return Return an error code or 0, see krb5_get_error_message().
    1160             :  *
    1161             :  * @ingroup krb5_ccache
    1162             :  */
    1163             : 
    1164             : 
    1165             : KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
    1166      713489 : krb5_cc_start_seq_get (krb5_context context,
    1167             :                        const krb5_ccache id,
    1168             :                        krb5_cc_cursor *cursor)
    1169             : {
    1170      713489 :     return (*id->ops->get_first)(context, id, cursor);
    1171             : }
    1172             : 
    1173             : /**
    1174             :  * Retrieve the next cred pointed to by (`id', `cursor') in `creds'
    1175             :  * and advance `cursor'.
    1176             :  *
    1177             :  * @return Return an error code or 0, see krb5_get_error_message().
    1178             :  *
    1179             :  * @ingroup krb5_ccache
    1180             :  */
    1181             : 
    1182             : 
    1183             : KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
    1184     1909473 : krb5_cc_next_cred (krb5_context context,
    1185             :                    const krb5_ccache id,
    1186             :                    krb5_cc_cursor *cursor,
    1187             :                    krb5_creds *creds)
    1188             : {
    1189     1909473 :     return (*id->ops->get_next)(context, id, cursor, creds);
    1190             : }
    1191             : 
    1192             : /**
    1193             :  * Destroy the cursor `cursor'.
    1194             :  *
    1195             :  * @ingroup krb5_ccache
    1196             :  */
    1197             : 
    1198             : 
    1199             : KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
    1200      713489 : krb5_cc_end_seq_get (krb5_context context,
    1201             :                      const krb5_ccache id,
    1202             :                      krb5_cc_cursor *cursor)
    1203             : {
    1204      713489 :     return (*id->ops->end_get)(context, id, cursor);
    1205             : }
    1206             : 
    1207             : /**
    1208             :  * Remove the credential identified by `cred', `which' from `id'.
    1209             :  *
    1210             :  * @ingroup krb5_ccache
    1211             :  */
    1212             : 
    1213             : 
    1214             : KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
    1215       86273 : krb5_cc_remove_cred(krb5_context context,
    1216             :                     krb5_ccache id,
    1217             :                     krb5_flags which,
    1218             :                     krb5_creds *cred)
    1219             : {
    1220       86273 :     if(id->ops->remove_cred == NULL) {
    1221           0 :         krb5_set_error_message(context,
    1222             :                                EACCES,
    1223             :                                "ccache %s does not support remove_cred",
    1224           0 :                                id->ops->prefix);
    1225           0 :         return EACCES; /* XXX */
    1226             :     }
    1227       86273 :     return (*id->ops->remove_cred)(context, id, which, cred);
    1228             : }
    1229             : 
    1230             : /**
    1231             :  * Set the flags of `id' to `flags'.
    1232             :  *
    1233             :  * @ingroup krb5_ccache
    1234             :  */
    1235             : 
    1236             : 
    1237             : KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
    1238           0 : krb5_cc_set_flags(krb5_context context,
    1239             :                   krb5_ccache id,
    1240             :                   krb5_flags flags)
    1241             : {
    1242           0 :     return (*id->ops->set_flags)(context, id, flags);
    1243             : }
    1244             : 
    1245             : /**
    1246             :  * Get the flags of `id', store them in `flags'.
    1247             :  *
    1248             :  * @ingroup krb5_ccache
    1249             :  */
    1250             : 
    1251             : KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
    1252           0 : krb5_cc_get_flags(krb5_context context,
    1253             :                   krb5_ccache id,
    1254             :                   krb5_flags *flags)
    1255             : {
    1256           0 :     *flags = 0;
    1257           0 :     return 0;
    1258             : }
    1259             : 
    1260             : /**
    1261             :  * Copy the contents of `from' to `to' if the given match function
    1262             :  * return true.
    1263             :  *
    1264             :  * @param context A Kerberos 5 context.
    1265             :  * @param from the cache to copy data from.
    1266             :  * @param to the cache to copy data to.
    1267             :  * @param match a match function that should return TRUE if cred argument should be copied, if NULL, all credentials are copied.
    1268             :  * @param matchctx context passed to match function.
    1269             :  * @param matched set to true if there was a credential that matched, may be NULL.
    1270             :  *
    1271             :  * @return Return an error code or 0, see krb5_get_error_message().
    1272             :  *
    1273             :  * @ingroup krb5_ccache
    1274             :  */
    1275             : 
    1276             : KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
    1277       72201 : krb5_cc_copy_match_f(krb5_context context,
    1278             :                      const krb5_ccache from,
    1279             :                      krb5_ccache to,
    1280             :                      krb5_boolean (*match)(krb5_context, void *, const krb5_creds *),
    1281             :                      void *matchctx,
    1282             :                      unsigned int *matched)
    1283             : {
    1284         917 :     krb5_error_code ret;
    1285         917 :     krb5_cc_cursor cursor;
    1286         917 :     krb5_creds cred;
    1287         917 :     krb5_principal princ;
    1288             : 
    1289       72201 :     if (matched)
    1290           0 :         *matched = 0;
    1291             : 
    1292       72201 :     ret = krb5_cc_get_principal(context, from, &princ);
    1293       72201 :     if (ret)
    1294           0 :         return ret;
    1295       72201 :     ret = krb5_cc_initialize(context, to, princ);
    1296       72201 :     if (ret) {
    1297           0 :         krb5_free_principal(context, princ);
    1298           0 :         return ret;
    1299             :     }
    1300       72201 :     ret = krb5_cc_start_seq_get(context, from, &cursor);
    1301       72201 :     if (ret) {
    1302           0 :         krb5_free_principal(context, princ);
    1303           0 :         return ret;
    1304             :     }
    1305             : 
    1306      217007 :     while ((ret = krb5_cc_next_cred(context, from, &cursor, &cred)) == 0) {
    1307      144806 :            if (match == NULL || (*match)(context, matchctx, &cred)) {
    1308      144806 :                if (matched)
    1309           0 :                    (*matched)++;
    1310      144806 :                ret = krb5_cc_store_cred(context, to, &cred);
    1311      144806 :                if (ret)
    1312           0 :                    break;
    1313             :            }
    1314      144806 :            krb5_free_cred_contents(context, &cred);
    1315             :     }
    1316       72201 :     krb5_cc_end_seq_get(context, from, &cursor);
    1317       72201 :     krb5_free_principal(context, princ);
    1318       72201 :     if (ret == KRB5_CC_END)
    1319       72201 :         ret = 0;
    1320       71284 :     return ret;
    1321             : }
    1322             : 
    1323             : /**
    1324             :  * Just like krb5_cc_copy_match_f(), but copy everything.
    1325             :  *
    1326             :  * @ingroup @krb5_ccache
    1327             :  */
    1328             : 
    1329             : KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
    1330         335 : krb5_cc_copy_cache(krb5_context context,
    1331             :                    const krb5_ccache from,
    1332             :                    krb5_ccache to)
    1333             : {
    1334         335 :     return krb5_cc_copy_match_f(context, from, to, NULL, NULL, NULL);
    1335             : }
    1336             : 
    1337             : /**
    1338             :  * Return the version of `id'.
    1339             :  *
    1340             :  * @ingroup krb5_ccache
    1341             :  */
    1342             : 
    1343             : 
    1344             : KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
    1345           0 : krb5_cc_get_version(krb5_context context,
    1346             :                     const krb5_ccache id)
    1347             : {
    1348           0 :     if(id->ops->get_version)
    1349           0 :         return (*id->ops->get_version)(context, id);
    1350             :     else
    1351           0 :         return 0;
    1352             : }
    1353             : 
    1354             : /**
    1355             :  * Clear `mcreds' so it can be used with krb5_cc_retrieve_cred
    1356             :  *
    1357             :  * @ingroup krb5_ccache
    1358             :  */
    1359             : 
    1360             : 
    1361             : KRB5_LIB_FUNCTION void KRB5_LIB_CALL
    1362       32299 : krb5_cc_clear_mcred(krb5_creds *mcred)
    1363             : {
    1364       32299 :     memset(mcred, 0, sizeof(*mcred));
    1365       32299 : }
    1366             : 
    1367             : /**
    1368             :  * Get the cc ops that is registered in `context' to handle the
    1369             :  * prefix. prefix can be a complete credential cache name or a
    1370             :  * prefix, the function will only use part up to the first colon (:)
    1371             :  * if there is one. If prefix the argument is NULL, the default ccache
    1372             :  * implemtation is returned.
    1373             :  *
    1374             :  * @return Returns NULL if ops not found.
    1375             :  *
    1376             :  * @ingroup krb5_ccache
    1377             :  */
    1378             : 
    1379             : 
    1380             : KRB5_LIB_FUNCTION const krb5_cc_ops * KRB5_LIB_CALL
    1381       23597 : krb5_cc_get_prefix_ops(krb5_context context, const char *prefix)
    1382             : {
    1383       23597 :     return cc_get_prefix_ops(context, prefix, NULL);
    1384             : }
    1385             : 
    1386             : /**
    1387             :  * Get the cc ops that is registered in `context' to handle the
    1388             :  * prefix. prefix can be a complete credential cache name or a
    1389             :  * prefix, the function will only use part up to the first colon (:)
    1390             :  * if there is one. If prefix the argument is NULL, the default ccache
    1391             :  * implementation is returned.
    1392             :  *
    1393             :  * If residual is non-NULL, it is set to the residual component of
    1394             :  * prefix (if present) or the prefix itself.
    1395             :  *
    1396             :  * @return Returns NULL if ops not found.
    1397             :  *
    1398             :  * @ingroup krb5_ccache
    1399             :  */
    1400             : 
    1401             : 
    1402             : static const krb5_cc_ops *
    1403      339114 : cc_get_prefix_ops(krb5_context context,
    1404             :                   const char *prefix,
    1405             :                   const char **residual)
    1406             : {
    1407        4286 :     int i;
    1408             : 
    1409      339114 :     if (residual)
    1410      315517 :         *residual = prefix;
    1411             : 
    1412      339114 :     if (prefix == NULL)
    1413           0 :         return KRB5_DEFAULT_CCTYPE;
    1414             : 
    1415             :     /* Is absolute path? Or UNC path? */
    1416      339114 :     if (ISPATHSEP(prefix[0]))
    1417       26146 :         return &krb5_fcc_ops;
    1418             : 
    1419             : #ifdef _WIN32
    1420             :     /* Is drive letter? */
    1421             :     if (isalpha((unsigned char)prefix[0]) && prefix[1] == ':')
    1422             :         return &krb5_fcc_ops;
    1423             : #endif
    1424             : 
    1425     1196560 :     for(i = 0; i < context->num_cc_ops && context->cc_ops[i]->prefix; i++) {
    1426     1195997 :         size_t prefix_len = strlen(context->cc_ops[i]->prefix);
    1427             : 
    1428     1195997 :         if (strncmp(context->cc_ops[i]->prefix, prefix, prefix_len) == 0 &&
    1429      312361 :             (prefix[prefix_len] == ':' || prefix[prefix_len] == '\0')) {
    1430      312361 :             if (residual) {
    1431      288764 :                 if (prefix[prefix_len] == ':' && prefix[prefix_len + 1] != '\0')
    1432      288764 :                     *residual = &prefix[prefix_len + 1];
    1433             :                 else
    1434           0 :                     *residual = NULL;
    1435             :             }
    1436             : 
    1437      312361 :             return context->cc_ops[i];
    1438             :         }
    1439             :     }
    1440             : 
    1441         563 :     return NULL;
    1442             : }
    1443             : 
    1444             : struct krb5_cc_cache_cursor_data {
    1445             :     const krb5_cc_ops *ops;
    1446             :     krb5_cc_cursor cursor;
    1447             : };
    1448             : 
    1449             : /**
    1450             :  * Start iterating over all caches of specified type. See also
    1451             :  * krb5_cccol_cursor_new().
    1452             : 
    1453             :  * @param context A Kerberos 5 context
    1454             :  * @param type optional type to iterate over, if NULL, the default cache is used.
    1455             :  * @param cursor cursor should be freed with krb5_cc_cache_end_seq_get().
    1456             :  *
    1457             :  * @return Return an error code or 0, see krb5_get_error_message().
    1458             :  *
    1459             :  * @ingroup krb5_ccache
    1460             :  */
    1461             : 
    1462             : 
    1463             : KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
    1464         260 : krb5_cc_cache_get_first (krb5_context context,
    1465             :                          const char *type,
    1466             :                          krb5_cc_cache_cursor *cursor)
    1467             : {
    1468           0 :     const krb5_cc_ops *ops;
    1469           0 :     krb5_error_code ret;
    1470             : 
    1471         260 :     if (type == NULL)
    1472           0 :         type = krb5_cc_default_name(context);
    1473             : 
    1474         260 :     ops = krb5_cc_get_prefix_ops(context, type);
    1475         260 :     if (ops == NULL) {
    1476           0 :         krb5_set_error_message(context, KRB5_CC_UNKNOWN_TYPE,
    1477             :                                "Unknown type \"%s\" when iterating "
    1478             :                                "trying to iterate the credential caches", type);
    1479           0 :         return KRB5_CC_UNKNOWN_TYPE;
    1480             :     }
    1481             : 
    1482         260 :     if (ops->get_cache_first == NULL) {
    1483           0 :         krb5_set_error_message(context, KRB5_CC_NOSUPP,
    1484           0 :                                N_("Credential cache type %s doesn't support "
    1485             :                                  "iterations over caches", "type"),
    1486           0 :                                ops->prefix);
    1487           0 :         return KRB5_CC_NOSUPP;
    1488             :     }
    1489             : 
    1490         260 :     *cursor = calloc(1, sizeof(**cursor));
    1491         260 :     if (*cursor == NULL)
    1492           0 :         return krb5_enomem(context);
    1493             : 
    1494         260 :     (*cursor)->ops = ops;
    1495             : 
    1496         260 :     ret = ops->get_cache_first(context, &(*cursor)->cursor);
    1497         260 :     if (ret) {
    1498         104 :         free(*cursor);
    1499         104 :         *cursor = NULL;
    1500             :     }
    1501         260 :     return ret;
    1502             : }
    1503             : 
    1504             : /**
    1505             :  * Retrieve the next cache pointed to by (`cursor') in `id'
    1506             :  * and advance `cursor'.
    1507             :  *
    1508             :  * @param context A Kerberos 5 context
    1509             :  * @param cursor the iterator cursor, returned by krb5_cc_cache_get_first()
    1510             :  * @param id next ccache
    1511             :  *
    1512             :  * @return Return 0 or an error code. Returns KRB5_CC_END when the end
    1513             :  *         of caches is reached, see krb5_get_error_message().
    1514             :  *
    1515             :  * @ingroup krb5_ccache
    1516             :  */
    1517             : 
    1518             : 
    1519             : KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
    1520         156 : krb5_cc_cache_next (krb5_context context,
    1521             :                    krb5_cc_cache_cursor cursor,
    1522             :                    krb5_ccache *id)
    1523             : {
    1524         156 :     return cursor->ops->get_cache_next(context, cursor->cursor, id);
    1525             : }
    1526             : 
    1527             : /**
    1528             :  * Destroy the cursor `cursor'.
    1529             :  *
    1530             :  * @return Return an error code or 0, see krb5_get_error_message().
    1531             :  *
    1532             :  * @ingroup krb5_ccache
    1533             :  */
    1534             : 
    1535             : 
    1536             : KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
    1537         156 : krb5_cc_cache_end_seq_get (krb5_context context,
    1538             :                            krb5_cc_cache_cursor cursor)
    1539             : {
    1540           0 :     krb5_error_code ret;
    1541         156 :     ret = cursor->ops->end_cache_get(context, cursor->cursor);
    1542         156 :     cursor->ops = NULL;
    1543         156 :     free(cursor);
    1544         156 :     return ret;
    1545             : }
    1546             : 
    1547             : /**
    1548             :  * Search for a matching credential cache that have the
    1549             :  * `principal' as the default principal. On success, `id' needs to be
    1550             :  * freed with krb5_cc_close() or krb5_cc_destroy().
    1551             :  *
    1552             :  * @param context A Kerberos 5 context
    1553             :  * @param client The principal to search for
    1554             :  * @param id the returned credential cache
    1555             :  *
    1556             :  * @return On failure, error code is returned and `id' is set to NULL.
    1557             :  *
    1558             :  * @ingroup krb5_ccache
    1559             :  */
    1560             : 
    1561             : 
    1562             : KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
    1563          52 : krb5_cc_cache_match (krb5_context context,
    1564             :                      krb5_principal client,
    1565             :                      krb5_ccache *id)
    1566             : {
    1567           0 :     krb5_cccol_cursor cursor;
    1568           0 :     krb5_error_code ret;
    1569          52 :     krb5_ccache cache = NULL;
    1570          52 :     krb5_ccache expired_match = NULL;
    1571             : 
    1572          52 :     *id = NULL;
    1573             : 
    1574          52 :     ret = krb5_cccol_cursor_new (context, &cursor);
    1575          52 :     if (ret)
    1576           0 :         return ret;
    1577             : 
    1578          52 :     while (krb5_cccol_cursor_next(context, cursor, &cache) == 0 && cache != NULL) {
    1579           0 :         krb5_principal principal;
    1580           0 :         krb5_boolean match;
    1581           0 :         time_t lifetime;
    1582             : 
    1583           0 :         ret = krb5_cc_get_principal(context, cache, &principal);
    1584           0 :         if (ret)
    1585           0 :             goto next;
    1586             : 
    1587           0 :         if (client->name.name_string.len == 0)
    1588           0 :             match = (strcmp(client->realm, principal->realm) == 0);
    1589             :         else
    1590           0 :             match = krb5_principal_compare(context, principal, client);
    1591           0 :         krb5_free_principal(context, principal);
    1592             : 
    1593           0 :         if (!match)
    1594           0 :             goto next;
    1595             : 
    1596           0 :         if (expired_match == NULL &&
    1597           0 :             (krb5_cc_get_lifetime(context, cache, &lifetime) != 0 || lifetime == 0)) {
    1598           0 :             expired_match = cache;
    1599           0 :             cache = NULL;
    1600           0 :             goto next;
    1601             :         }
    1602           0 :         break;
    1603             : 
    1604           0 :     next:
    1605           0 :         if (cache)
    1606           0 :             krb5_cc_close(context, cache);
    1607           0 :         cache = NULL;
    1608             :     }
    1609             : 
    1610          52 :     krb5_cccol_cursor_free(context, &cursor);
    1611             : 
    1612          52 :     if (cache == NULL && expired_match) {
    1613           0 :         cache = expired_match;
    1614           0 :         expired_match = NULL;
    1615          52 :     } else if (expired_match) {
    1616           0 :         krb5_cc_close(context, expired_match);
    1617          52 :     } else if (cache == NULL) {
    1618           0 :         char *str;
    1619             : 
    1620          52 :         (void) krb5_unparse_name(context, client, &str);
    1621          52 :         krb5_set_error_message(context, KRB5_CC_NOTFOUND,
    1622          52 :                                N_("Principal %s not found in any "
    1623             :                                   "credential cache", ""),
    1624          52 :                                str ? str : "<out of memory>");
    1625          52 :         if (str)
    1626          52 :             free(str);
    1627          52 :         return KRB5_CC_NOTFOUND;
    1628             :     }
    1629             : 
    1630           0 :     *id = cache;
    1631             : 
    1632           0 :     return 0;
    1633             : }
    1634             : 
    1635             : /**
    1636             :  * Move the content from one credential cache to another. The
    1637             :  * operation is an atomic switch.
    1638             :  *
    1639             :  * @param context a Kerberos context
    1640             :  * @param from the credential cache to move the content from
    1641             :  * @param to the credential cache to move the content to
    1642             : 
    1643             :  * @return On sucess, from is destroyed and closed. On failure, error code is
    1644             :  *         returned and from and to are both still allocated; see
    1645             :  *         krb5_get_error_message().
    1646             :  *
    1647             :  * @ingroup krb5_ccache
    1648             :  */
    1649             : 
    1650             : KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
    1651         137 : krb5_cc_move(krb5_context context, krb5_ccache from, krb5_ccache to)
    1652             : {
    1653         137 :     krb5_error_code ret = ENOTSUP;
    1654         137 :     krb5_principal princ = NULL;
    1655             : 
    1656         137 :     if (to->ops->move &&
    1657         137 :         strcmp(from->ops->prefix, to->ops->prefix) == 0) {
    1658             :         /*
    1659             :          * NOTE: to->ops->move() is expected to call
    1660             :          *       krb5_cc_destroy(context, from) on success.
    1661             :          */
    1662         137 :         ret = (*to->ops->move)(context, from, to);
    1663         137 :         if (ret == 0)
    1664           0 :             return 0;
    1665         137 :         if (ret != EXDEV && ret != ENOTSUP && ret != KRB5_CC_NOSUPP &&
    1666           0 :             ret != KRB5_FCC_INTERNAL)
    1667           0 :             return ret;
    1668             :         /* Fallback to high-level copy */
    1669             :     }   /* Else        high-level copy */
    1670             : 
    1671             :     /*
    1672             :      * Initialize destination, copy the source's contents to the destination,
    1673             :      * then destroy the source on success.
    1674             :      *
    1675             :      * It'd be nice if we could destroy any half-built destination if the copy
    1676             :      * fails, but the interface is not documented as doing so.
    1677             :      */
    1678         137 :     ret = krb5_cc_get_principal(context, from, &princ);
    1679         137 :     if (ret == 0)
    1680         137 :         ret = krb5_cc_initialize(context, to, princ);
    1681         137 :     krb5_free_principal(context, princ);
    1682         137 :     if (ret == 0)
    1683         137 :         ret = krb5_cc_copy_cache(context, from, to);
    1684         137 :     if (ret == 0)
    1685         137 :         krb5_cc_destroy(context, from);
    1686         137 :     return ret;
    1687             : }
    1688             : 
    1689             : #define KRB5_CONF_NAME "krb5_ccache_conf_data"
    1690             : #define KRB5_REALM_NAME "X-CACHECONF:"
    1691             : 
    1692             : static krb5_error_code
    1693      453675 : build_conf_principals(krb5_context context, krb5_ccache id,
    1694             :                       krb5_const_principal principal,
    1695             :                       const char *name, krb5_creds *cred)
    1696             : {
    1697        8476 :     krb5_principal client;
    1698        8476 :     krb5_error_code ret;
    1699      453675 :     char *pname = NULL;
    1700             : 
    1701      453675 :     memset(cred, 0, sizeof(*cred));
    1702             : 
    1703      453675 :     ret = krb5_cc_get_principal(context, id, &client);
    1704      453675 :     if (ret)
    1705        1155 :         return ret;
    1706             : 
    1707      452520 :     if (principal) {
    1708       24373 :         ret = krb5_unparse_name(context, principal, &pname);
    1709       24373 :         if (ret)
    1710           0 :             return ret;
    1711             :     }
    1712             : 
    1713      452520 :     ret = krb5_make_principal(context, &cred->server,
    1714             :                               KRB5_REALM_NAME,
    1715             :                               KRB5_CONF_NAME, name, pname, NULL);
    1716      452520 :     free(pname);
    1717      452520 :     if (ret) {
    1718           0 :         krb5_free_principal(context, client);
    1719           0 :         return ret;
    1720             :     }
    1721      452520 :     ret = krb5_copy_principal(context, client, &cred->client);
    1722      452520 :     krb5_free_principal(context, client);
    1723      452520 :     return ret;
    1724             : }
    1725             : 
    1726             : /**
    1727             :  * Return TRUE (non zero) if the principal is a configuration
    1728             :  * principal (generated part of krb5_cc_set_config()). Returns FALSE
    1729             :  * (zero) if not a configuration principal.
    1730             :  *
    1731             :  * @param context a Kerberos context
    1732             :  * @param principal principal to check if it a configuration principal
    1733             :  *
    1734             :  * @ingroup krb5_ccache
    1735             :  */
    1736             : 
    1737             : KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL
    1738      506416 : krb5_is_config_principal(krb5_context context,
    1739             :                          krb5_const_principal principal)
    1740             : {
    1741      506416 :     if (strcmp(principal->realm, KRB5_REALM_NAME) != 0)
    1742      206168 :         return FALSE;
    1743             : 
    1744      297206 :     if (principal->name.name_string.len == 0 ||
    1745      297206 :         strcmp(principal->name.name_string.val[0], KRB5_CONF_NAME) != 0)
    1746           0 :         return FALSE;
    1747             : 
    1748      292476 :     return TRUE;
    1749             : }
    1750             : 
    1751             : /**
    1752             :  * Store some configuration for the credential cache in the cache.
    1753             :  * Existing configuration under the same name is over-written.
    1754             :  *
    1755             :  * @param context a Kerberos context
    1756             :  * @param id the credential cache to store the data for
    1757             :  * @param principal configuration for a specific principal, if
    1758             :  * NULL, global for the whole cache.
    1759             :  * @param name name under which the configuraion is stored.
    1760             :  * @param data data to store, if NULL, configure is removed.
    1761             :  *
    1762             :  * @ingroup krb5_ccache
    1763             :  */
    1764             : 
    1765             : KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
    1766       86271 : krb5_cc_set_config(krb5_context context, krb5_ccache id,
    1767             :                    krb5_const_principal principal,
    1768             :                    const char *name, krb5_data *data)
    1769             : {
    1770        1466 :     krb5_error_code ret;
    1771        1466 :     krb5_creds cred;
    1772             : 
    1773       86271 :     ret = build_conf_principals(context, id, principal, name, &cred);
    1774       86271 :     if (ret)
    1775           0 :         goto out;
    1776             : 
    1777             :     /* Remove old configuration */
    1778       86271 :     ret = krb5_cc_remove_cred(context, id, 0, &cred);
    1779       86271 :     if (ret && ret != KRB5_CC_NOTFOUND && ret != KRB5_CC_NOSUPP &&
    1780           0 :         ret != KRB5_FCC_INTERNAL)
    1781           0 :         goto out;
    1782             : 
    1783       86271 :     if (data) {
    1784             :         /* not that anyone care when this expire */
    1785       86271 :         cred.times.authtime = time(NULL);
    1786       86271 :         cred.times.endtime = cred.times.authtime + 3600 * 24 * 30;
    1787             : 
    1788       86271 :         ret = krb5_data_copy(&cred.ticket, data->data, data->length);
    1789       86271 :         if (ret)
    1790           0 :             goto out;
    1791             : 
    1792       86271 :         ret = krb5_cc_store_cred(context, id, &cred);
    1793             :     }
    1794             : 
    1795           0 : out:
    1796       86271 :     krb5_free_cred_contents (context, &cred);
    1797       86271 :     return ret;
    1798             : }
    1799             : 
    1800             : /**
    1801             :  * Get some configuration for the credential cache in the cache.
    1802             :  *
    1803             :  * @param context a Kerberos context
    1804             :  * @param id the credential cache to store the data for
    1805             :  * @param principal configuration for a specific principal, if
    1806             :  * NULL, global for the whole cache.
    1807             :  * @param name name under which the configuraion is stored.
    1808             :  * @param data data to fetched, free with krb5_data_free()
    1809             :  * @return 0 on success, KRB5_CC_NOTFOUND or KRB5_CC_END if not found,
    1810             :  *           or other system error.
    1811             :  *
    1812             :  * @ingroup krb5_ccache
    1813             :  */
    1814             : 
    1815             : 
    1816             : KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
    1817      367404 : krb5_cc_get_config(krb5_context context, krb5_ccache id,
    1818             :                    krb5_const_principal principal,
    1819             :                    const char *name, krb5_data *data)
    1820             : {
    1821        7010 :     krb5_creds mcred, cred;
    1822        7010 :     krb5_error_code ret;
    1823             : 
    1824      367404 :     memset(&cred, 0, sizeof(cred));
    1825      367404 :     krb5_data_zero(data);
    1826             : 
    1827      367404 :     ret = build_conf_principals(context, id, principal, name, &mcred);
    1828      367404 :     if (ret)
    1829        1155 :         goto out;
    1830             : 
    1831      366249 :     ret = krb5_cc_retrieve_cred(context, id, 0, &mcred, &cred);
    1832      366249 :     if (ret)
    1833      223978 :         goto out;
    1834             : 
    1835      142271 :     ret = krb5_data_copy(data, cred.ticket.data, cred.ticket.length);
    1836             : 
    1837      367404 : out:
    1838      367404 :     krb5_free_cred_contents (context, &cred);
    1839      367404 :     krb5_free_cred_contents (context, &mcred);
    1840      367404 :     return ret;
    1841             : }
    1842             : 
    1843             : /*
    1844             :  *
    1845             :  */
    1846             : 
    1847             : struct krb5_cccol_cursor_data {
    1848             :     int idx;
    1849             :     krb5_cc_cache_cursor cursor;
    1850             : };
    1851             : 
    1852             : /**
    1853             :  * Get a new cache interation cursor that will interate over all
    1854             :  * credentials caches independent of type.
    1855             :  *
    1856             :  * @param context a Kerberos context
    1857             :  * @param cursor passed into krb5_cccol_cursor_next() and free with krb5_cccol_cursor_free().
    1858             :  *
    1859             :  * @return Returns 0 or and error code, see krb5_get_error_message().
    1860             :  *
    1861             :  * @ingroup krb5_ccache
    1862             :  */
    1863             : 
    1864             : KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
    1865          52 : krb5_cccol_cursor_new(krb5_context context, krb5_cccol_cursor *cursor)
    1866             : {
    1867          52 :     *cursor = calloc(1, sizeof(**cursor));
    1868          52 :     if (*cursor == NULL)
    1869           0 :         return krb5_enomem(context);
    1870          52 :     (*cursor)->idx = 0;
    1871          52 :     (*cursor)->cursor = NULL;
    1872             : 
    1873          52 :     return 0;
    1874             : }
    1875             : 
    1876             : /**
    1877             :  * Get next credential cache from the iteration.
    1878             :  *
    1879             :  * @param context A Kerberos 5 context
    1880             :  * @param cursor the iteration cursor
    1881             :  * @param cache the returned cursor, pointer is set to NULL on failure
    1882             :  *        and a cache on success. The returned cache needs to be freed
    1883             :  *        with krb5_cc_close() or destroyed with krb5_cc_destroy().
    1884             :  *        MIT Kerberos behavies slightly diffrent and sets cache to NULL
    1885             :  *        when all caches are iterated over and return 0.
    1886             :  *
    1887             :  * @return Return 0 or and error, KRB5_CC_END is returned at the end
    1888             :  *        of iteration. See krb5_get_error_message().
    1889             :  *
    1890             :  * @ingroup krb5_ccache
    1891             :  */
    1892             : 
    1893             : 
    1894             : KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
    1895          52 : krb5_cccol_cursor_next(krb5_context context, krb5_cccol_cursor cursor,
    1896             :                        krb5_ccache *cache)
    1897             : {
    1898          52 :     krb5_error_code ret = 0;
    1899             : 
    1900          52 :     *cache = NULL;
    1901             : 
    1902         312 :     while (cursor->idx < context->num_cc_ops) {
    1903             : 
    1904         260 :         if (cursor->cursor == NULL) {
    1905         260 :             ret = krb5_cc_cache_get_first (context,
    1906         260 :                                            context->cc_ops[cursor->idx]->prefix,
    1907             :                                            &cursor->cursor);
    1908         260 :             if (ret) {
    1909         104 :                 cursor->idx++;
    1910         104 :                 continue;
    1911             :             }
    1912             :         }
    1913         156 :         ret = krb5_cc_cache_next(context, cursor->cursor, cache);
    1914         156 :         if (ret == 0)
    1915           0 :             break;
    1916             : 
    1917         156 :         krb5_cc_cache_end_seq_get(context, cursor->cursor);
    1918         156 :         cursor->cursor = NULL;
    1919         156 :         if (ret != KRB5_CC_END)
    1920           0 :             break;
    1921             : 
    1922         156 :         cursor->idx++;
    1923             :     }
    1924          52 :     if (cursor->idx >= context->num_cc_ops) {
    1925          52 :         krb5_set_error_message(context, KRB5_CC_END,
    1926          52 :                                N_("Reached end of credential caches", ""));
    1927          52 :         return KRB5_CC_END;
    1928             :     }
    1929             : 
    1930           0 :     return ret;
    1931             : }
    1932             : 
    1933             : /**
    1934             :  * End an iteration and free all resources, can be done before end is reached.
    1935             :  *
    1936             :  * @param context A Kerberos 5 context
    1937             :  * @param cursor the iteration cursor to be freed.
    1938             :  *
    1939             :  * @return Return 0 or and error, KRB5_CC_END is returned at the end
    1940             :  *        of iteration. See krb5_get_error_message().
    1941             :  *
    1942             :  * @ingroup krb5_ccache
    1943             :  */
    1944             : 
    1945             : KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
    1946          52 : krb5_cccol_cursor_free(krb5_context context, krb5_cccol_cursor *cursor)
    1947             : {
    1948          52 :     krb5_cccol_cursor c = *cursor;
    1949             : 
    1950          52 :     *cursor = NULL;
    1951          52 :     if (c) {
    1952          52 :         if (c->cursor)
    1953           0 :             krb5_cc_cache_end_seq_get(context, c->cursor);
    1954          52 :         free(c);
    1955             :     }
    1956          52 :     return 0;
    1957             : }
    1958             : 
    1959             : /**
    1960             :  * Return the last time the credential cache was modified.
    1961             :  *
    1962             :  * @param context A Kerberos 5 context
    1963             :  * @param id The credential cache to probe
    1964             :  * @param mtime the last modification time, set to 0 on error.
    1965             : 
    1966             :  * @return Return 0 or and error. See krb5_get_error_message().
    1967             :  *
    1968             :  * @ingroup krb5_ccache
    1969             :  */
    1970             : 
    1971             : 
    1972             : KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
    1973           0 : krb5_cc_last_change_time(krb5_context context,
    1974             :                          krb5_ccache id,
    1975             :                          krb5_timestamp *mtime)
    1976             : {
    1977           0 :     *mtime = 0;
    1978             : 
    1979           0 :     if (id->ops->version < KRB5_CC_OPS_VERSION_2
    1980           0 :          || id->ops->lastchange == NULL)
    1981           0 :         return KRB5_CC_NOSUPP;
    1982             : 
    1983           0 :     return (*id->ops->lastchange)(context, id, mtime);
    1984             : }
    1985             : 
    1986             : /**
    1987             :  * Return the last modfication time for a cache collection. The query
    1988             :  * can be limited to a specific cache type. If the function return 0
    1989             :  * and mtime is 0, there was no credentials in the caches.
    1990             :  *
    1991             :  * @param context A Kerberos 5 context
    1992             :  * @param type The credential cache to probe, if NULL, all type are traversed.
    1993             :  * @param mtime the last modification time, set to 0 on error.
    1994             : 
    1995             :  * @return Return 0 or and error. See krb5_get_error_message().
    1996             :  *
    1997             :  * @ingroup krb5_ccache
    1998             :  */
    1999             : 
    2000             : KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
    2001           0 : krb5_cccol_last_change_time(krb5_context context,
    2002             :                             const char *type,
    2003             :                             krb5_timestamp *mtime)
    2004             : {
    2005           0 :     krb5_cccol_cursor cursor;
    2006           0 :     krb5_error_code ret;
    2007           0 :     krb5_ccache id;
    2008           0 :     krb5_timestamp t = 0;
    2009             : 
    2010           0 :     *mtime = 0;
    2011             : 
    2012           0 :     ret = krb5_cccol_cursor_new (context, &cursor);
    2013           0 :     if (ret)
    2014           0 :         return ret;
    2015             : 
    2016           0 :     while (krb5_cccol_cursor_next(context, cursor, &id) == 0 && id != NULL) {
    2017             : 
    2018           0 :         if (type && strcmp(krb5_cc_get_type(context, id), type) != 0)
    2019           0 :             continue;
    2020             : 
    2021           0 :         ret = krb5_cc_last_change_time(context, id, &t);
    2022           0 :         krb5_cc_close(context, id);
    2023           0 :         if (ret)
    2024           0 :             continue;
    2025           0 :         if (t > *mtime)
    2026           0 :             *mtime = t;
    2027             :     }
    2028             : 
    2029           0 :     krb5_cccol_cursor_free(context, &cursor);
    2030             : 
    2031           0 :     return 0;
    2032             : }
    2033             : /**
    2034             :  * Return a friendly name on credential cache. Free the result with krb5_xfree().
    2035             :  *
    2036             :  * @return Return an error code or 0, see krb5_get_error_message().
    2037             :  *
    2038             :  * @ingroup krb5_ccache
    2039             :  */
    2040             : 
    2041             : KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
    2042           0 : krb5_cc_get_friendly_name(krb5_context context,
    2043             :                           krb5_ccache id,
    2044             :                           char **name)
    2045             : {
    2046           0 :     krb5_error_code ret;
    2047           0 :     krb5_data data;
    2048             : 
    2049           0 :     ret = krb5_cc_get_config(context, id, NULL, "FriendlyName", &data);
    2050           0 :     if (ret) {
    2051           0 :         krb5_principal principal;
    2052           0 :         ret = krb5_cc_get_principal(context, id, &principal);
    2053           0 :         if (ret)
    2054           0 :             return ret;
    2055           0 :         ret = krb5_unparse_name(context, principal, name);
    2056           0 :         krb5_free_principal(context, principal);
    2057             :     } else {
    2058           0 :         ret = asprintf(name, "%.*s", (int)data.length, (char *)data.data);
    2059           0 :         krb5_data_free(&data);
    2060           0 :         if (ret <= 0)
    2061           0 :             ret = krb5_enomem(context);
    2062             :         else
    2063           0 :             ret = 0;
    2064             :     }
    2065             : 
    2066           0 :     return ret;
    2067             : }
    2068             : 
    2069             : /**
    2070             :  * Set the friendly name on credential cache.
    2071             :  *
    2072             :  * @return Return an error code or 0, see krb5_get_error_message().
    2073             :  *
    2074             :  * @ingroup krb5_ccache
    2075             :  */
    2076             : 
    2077             : KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
    2078           0 : krb5_cc_set_friendly_name(krb5_context context,
    2079             :                           krb5_ccache id,
    2080             :                           const char *name)
    2081             : {
    2082           0 :     krb5_data data;
    2083             : 
    2084           0 :     data.data = rk_UNCONST(name);
    2085           0 :     data.length = strlen(name);
    2086             : 
    2087           0 :     return krb5_cc_set_config(context, id, NULL, "FriendlyName", &data);
    2088             : }
    2089             : 
    2090             : /**
    2091             :  * Get the lifetime of the initial ticket in the cache
    2092             :  *
    2093             :  * Get the lifetime of the initial ticket in the cache, if the initial
    2094             :  * ticket was not found, the error code KRB5_CC_END is returned.
    2095             :  *
    2096             :  * @param context A Kerberos 5 context.
    2097             :  * @param id a credential cache
    2098             :  * @param t the relative lifetime of the initial ticket
    2099             :  *
    2100             :  * @return Return an error code or 0, see krb5_get_error_message().
    2101             :  *
    2102             :  * @ingroup krb5_ccache
    2103             :  */
    2104             : 
    2105             : KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
    2106      139051 : krb5_cc_get_lifetime(krb5_context context, krb5_ccache id, time_t *t)
    2107             : {
    2108        2347 :     krb5_data config_start_realm;
    2109        2347 :     char *start_realm;
    2110        2347 :     krb5_cc_cursor cursor;
    2111        2347 :     krb5_error_code ret;
    2112        2347 :     krb5_creds cred;
    2113      139051 :     time_t now, endtime = 0;
    2114             : 
    2115      139051 :     *t = 0;
    2116      139051 :     krb5_timeofday(context, &now);
    2117             : 
    2118      139051 :     ret = krb5_cc_get_config(context, id, NULL, "start_realm", &config_start_realm);
    2119      139051 :     if (ret == 0) {
    2120      138927 :         start_realm = strndup(config_start_realm.data, config_start_realm.length);
    2121      138927 :         krb5_data_free(&config_start_realm);
    2122             :     } else {
    2123           0 :         krb5_principal client;
    2124             : 
    2125         124 :         ret = krb5_cc_get_principal(context, id, &client);
    2126         124 :         if (ret)
    2127           0 :             return ret;
    2128         124 :         start_realm = strdup(krb5_principal_get_realm(context, client));
    2129         124 :         krb5_free_principal(context, client);
    2130             :     }
    2131      139051 :     if (start_realm == NULL)
    2132           0 :         return krb5_enomem(context);
    2133             : 
    2134      139051 :     ret = krb5_cc_start_seq_get(context, id, &cursor);
    2135      139051 :     if (ret) {
    2136           0 :         free(start_realm);
    2137           0 :         return ret;
    2138             :     }
    2139             : 
    2140      310897 :     while ((ret = krb5_cc_next_cred(context, id, &cursor, &cred)) == 0) {
    2141             :         /**
    2142             :          * If we find the start krbtgt in the cache, use that as the lifespan.
    2143             :          */
    2144      310821 :         if (krb5_principal_is_root_krbtgt(context, cred.server) &&
    2145      138975 :             strcmp(cred.server->realm, start_realm) == 0) {
    2146      138975 :             if (now < cred.times.endtime)
    2147      138975 :                 endtime = cred.times.endtime;
    2148      138975 :             krb5_free_cred_contents(context, &cred);
    2149      138975 :             break;
    2150             :         }
    2151             :         /*
    2152             :          * Skip config entries
    2153             :          */
    2154      171846 :         if (krb5_is_config_principal(context, cred.server)) {
    2155      138596 :             krb5_free_cred_contents(context, &cred);
    2156      138596 :             continue;
    2157             :         }
    2158             :         /**
    2159             :          * If there was no krbtgt, use the shortest lifetime of
    2160             :          * service tickets that have yet to expire.  If all
    2161             :          * credentials are expired, krb5_cc_get_lifetime() will fail.
    2162             :          */
    2163       33250 :         if ((endtime == 0 || cred.times.endtime < endtime) && now < cred.times.endtime)
    2164        5721 :             endtime = cred.times.endtime;
    2165       33250 :         krb5_free_cred_contents(context, &cred);
    2166             :     }
    2167      139051 :     free(start_realm);
    2168             : 
    2169             :     /* if we found an endtime use that */
    2170      139051 :     if (endtime) {
    2171      139051 :         *t = endtime - now;
    2172      139051 :         ret = 0;
    2173             :     }
    2174             : 
    2175      139051 :     krb5_cc_end_seq_get(context, id, &cursor);
    2176             : 
    2177      139051 :     return ret;
    2178             : }
    2179             : 
    2180             : /**
    2181             :  * Set the time offset betwen the client and the KDC
    2182             :  *
    2183             :  * If the backend doesn't support KDC offset, use the context global setting.
    2184             :  *
    2185             :  * @param context A Kerberos 5 context.
    2186             :  * @param id a credential cache
    2187             :  * @param offset the offset in seconds
    2188             :  *
    2189             :  * @return Return an error code or 0, see krb5_get_error_message().
    2190             :  *
    2191             :  * @ingroup krb5_ccache
    2192             :  */
    2193             : 
    2194             : KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
    2195           0 : krb5_cc_set_kdc_offset(krb5_context context, krb5_ccache id, krb5_deltat offset)
    2196             : {
    2197           0 :     if (id->ops->version < KRB5_CC_OPS_VERSION_3
    2198           0 :         || id->ops->set_kdc_offset == NULL) {
    2199           0 :         context->kdc_sec_offset = offset;
    2200           0 :         context->kdc_usec_offset = 0;
    2201           0 :         return 0;
    2202             :     }
    2203           0 :     return (*id->ops->set_kdc_offset)(context, id, offset);
    2204             : }
    2205             : 
    2206             : /**
    2207             :  * Get the time offset betwen the client and the KDC
    2208             :  *
    2209             :  * If the backend doesn't support KDC offset, use the context global setting.
    2210             :  *
    2211             :  * @param context A Kerberos 5 context.
    2212             :  * @param id a credential cache
    2213             :  * @param offset the offset in seconds
    2214             :  *
    2215             :  * @return Return an error code or 0, see krb5_get_error_message().
    2216             :  *
    2217             :  * @ingroup krb5_ccache
    2218             :  */
    2219             : 
    2220             : KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
    2221       21789 : krb5_cc_get_kdc_offset(krb5_context context, krb5_ccache id, krb5_deltat *offset)
    2222             : {
    2223       21789 :     if (id->ops->version < KRB5_CC_OPS_VERSION_3
    2224       21789 :         || id->ops->get_kdc_offset == NULL) {
    2225           0 :         *offset = context->kdc_sec_offset;
    2226           0 :         return 0;
    2227             :     }
    2228       21789 :     return (*id->ops->get_kdc_offset)(context, id, offset);
    2229             : }
    2230             : 
    2231             : #ifdef _WIN32
    2232             : #define REGPATH_MIT_KRB5 "SOFTWARE\\MIT\\Kerberos5"
    2233             : 
    2234             : static char *
    2235             : _get_default_cc_name_from_registry(krb5_context context, HKEY hkBase)
    2236             : {
    2237             :     HKEY hk_k5 = 0;
    2238             :     LONG code;
    2239             :     char *ccname = NULL;
    2240             : 
    2241             :     code = RegOpenKeyEx(hkBase,
    2242             :                         REGPATH_MIT_KRB5,
    2243             :                         0, KEY_READ, &hk_k5);
    2244             : 
    2245             :     if (code != ERROR_SUCCESS)
    2246             :         return NULL;
    2247             : 
    2248             :     ccname = heim_parse_reg_value_as_string(context->hcontext, hk_k5, "ccname",
    2249             :                                             REG_NONE, 0);
    2250             : 
    2251             :     RegCloseKey(hk_k5);
    2252             : 
    2253             :     return ccname;
    2254             : }
    2255             : 
    2256             : KRB5_LIB_FUNCTION char * KRB5_LIB_CALL
    2257             : _krb5_get_default_cc_name_from_registry(krb5_context context)
    2258             : {
    2259             :     char *ccname;
    2260             : 
    2261             :     ccname = _get_default_cc_name_from_registry(context, HKEY_CURRENT_USER);
    2262             :     if (ccname == NULL)
    2263             :         ccname = _get_default_cc_name_from_registry(context,
    2264             :                                                     HKEY_LOCAL_MACHINE);
    2265             : 
    2266             :     return ccname;
    2267             : }
    2268             : 
    2269             : KRB5_LIB_FUNCTION int KRB5_LIB_CALL
    2270             : _krb5_set_default_cc_name_to_registry(krb5_context context, krb5_ccache id)
    2271             : {
    2272             :     HKEY hk_k5 = 0;
    2273             :     LONG code;
    2274             :     int ret = -1;
    2275             :     char * ccname = NULL;
    2276             : 
    2277             :     code = RegOpenKeyEx(HKEY_CURRENT_USER,
    2278             :                         REGPATH_MIT_KRB5,
    2279             :                         0, KEY_READ|KEY_WRITE, &hk_k5);
    2280             : 
    2281             :     if (code != ERROR_SUCCESS)
    2282             :         return -1;
    2283             : 
    2284             :     ret = asprintf(&ccname, "%s:%s", krb5_cc_get_type(context, id), krb5_cc_get_name(context, id));
    2285             :     if (ret < 0)
    2286             :         goto cleanup;
    2287             : 
    2288             :     ret = heim_store_string_to_reg_value(context->hcontext, hk_k5, "ccname",
    2289             :                                          REG_SZ, ccname, -1, 0);
    2290             : 
    2291             :   cleanup:
    2292             : 
    2293             :     if (ccname)
    2294             :         free(ccname);
    2295             : 
    2296             :     RegCloseKey(hk_k5);
    2297             : 
    2298             :     return ret;
    2299             : }
    2300             : #endif

Generated by: LCOV version 1.14