LCOV - code coverage report
Current view: top level - third_party/heimdal/lib/krb5 - krcache.c (source / functions) Hit Total Coverage
Test: coverage report for master 98b443d9 Lines: 33 924 3.6 %
Date: 2024-05-31 13:13:24 Functions: 4 48 8.3 %

          Line data    Source code
       1             : /*
       2             :  * Copyright (c) 2006 The Regents of the University of Michigan.
       3             :  * All rights reserved.
       4             :  *
       5             :  * Portions Copyright (c) 2018, AuriStor, Inc.
       6             :  *
       7             :  * Permission is granted to use, copy, create derivative works
       8             :  * and redistribute this software and such derivative works
       9             :  * for any purpose, so long as the name of The University of
      10             :  * Michigan is not used in any advertising or publicity
      11             :  * pertaining to the use of distribution of this software
      12             :  * without specific, written prior authorization.  If the
      13             :  * above copyright notice or any other identification of the
      14             :  * University of Michigan is included in any copy of any
      15             :  * portion of this software, then the disclaimer below must
      16             :  * also be included.
      17             :  *
      18             :  * THIS SOFTWARE IS PROVIDED AS IS, WITHOUT REPRESENTATION
      19             :  * FROM THE UNIVERSITY OF MICHIGAN AS TO ITS FITNESS FOR ANY
      20             :  * PURPOSE, AND WITHOUT WARRANTY BY THE UNIVERSITY OF
      21             :  * MICHIGAN OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING
      22             :  * WITHOUT LIMITATION THE IMPLIED WARRANTIES OF
      23             :  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE
      24             :  * REGENTS OF THE UNIVERSITY OF MICHIGAN SHALL NOT BE LIABLE
      25             :  * FOR ANY DAMAGES, INCLUDING SPECIAL, INDIRECT, INCIDENTAL, OR
      26             :  * CONSEQUENTIAL DAMAGES, WITH RESPECT TO ANY CLAIM ARISING
      27             :  * OUT OF OR IN CONNECTION WITH THE USE OF THE SOFTWARE, EVEN
      28             :  * IF IT HAS BEEN OR IS HEREAFTER ADVISED OF THE POSSIBILITY OF
      29             :  * SUCH DAMAGES.
      30             :  */
      31             : /*
      32             :  * Copyright 1990,1991,1992,1993,1994,2000,2004 Massachusetts Institute of
      33             :  * Technology.  All Rights Reserved.
      34             :  *
      35             :  * Original stdio support copyright 1995 by Cygnus Support.
      36             :  *
      37             :  * Export of this software from the United States of America may
      38             :  *   require a specific license from the United States Government.
      39             :  *   It is the responsibility of any person or organization contemplating
      40             :  *   export to obtain such a license before exporting.
      41             :  *
      42             :  * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
      43             :  * distribute this software and its documentation for any purpose and
      44             :  * without fee is hereby granted, provided that the above copyright
      45             :  * notice appear in all copies and that both that copyright notice and
      46             :  * this permission notice appear in supporting documentation, and that
      47             :  * the name of M.I.T. not be used in advertising or publicity pertaining
      48             :  * to distribution of the software without specific, written prior
      49             :  * permission.  Furthermore if you modify this software you must label
      50             :  * your software as modified software and not distribute it in such a
      51             :  * fashion that it might be confused with the original M.I.T. software.
      52             :  * M.I.T. makes no representations about the suitability of
      53             :  * this software for any purpose.  It is provided "as is" without express
      54             :  * or implied warranty.
      55             :  */
      56             : 
      57             : /*
      58             :  * This file implements a collection-enabled credential cache type where the
      59             :  * credentials are stored in the Linux keyring facility.
      60             :  *
      61             :  * A residual of this type can have three forms:
      62             :  *    anchor:collection:subsidiary
      63             :  *    anchor:collection
      64             :  *    collection
      65             :  *
      66             :  * The anchor name is "process", "thread", or "legacy" and determines where we
      67             :  * search for keyring collections.  In the third form, the anchor name is
      68             :  * presumed to be "legacy".  The anchor keyring for legacy caches is the
      69             :  * session keyring.
      70             :  *
      71             :  * If the subsidiary name is present, the residual identifies a single cache
      72             :  * within a collection.  Otherwise, the residual identifies the collection
      73             :  * itself.  When a residual identifying a collection is resolved, the
      74             :  * collection's primary key is looked up (or initialized, using the collection
      75             :  * name as the subsidiary name), and the resulting cache's name will use the
      76             :  * first name form and will identify the primary cache.
      77             :  *
      78             :  * Keyring collections are named "_krb_<collection>" and are linked from the
      79             :  * anchor keyring.  The keys within a keyring collection are links to cache
      80             :  * keyrings, plus a link to one user key named "krb_ccache:primary" which
      81             :  * contains a serialized representation of the collection version (currently 1)
      82             :  * and the primary name of the collection.
      83             :  *
      84             :  * Cache keyrings contain one user key per credential which contains a
      85             :  * serialized representation of the credential.  There is also one user key
      86             :  * named "__krb5_princ__" which contains a serialized representation of the
      87             :  * cache's default principal.
      88             :  *
      89             :  * If the anchor name is "legacy", then the initial primary cache (the one
      90             :  * named with the collection name) is also linked to the session keyring, and
      91             :  * we look for a cache in that location when initializing the collection.  This
      92             :  * extra link allows that cache to be visible to old versions of the KEYRING
      93             :  * cache type, and allows us to see caches created by that code.
      94             :  */
      95             : 
      96             : #include "krb5_locl.h"
      97             : 
      98             : #ifdef HAVE_KEYUTILS_H
      99             : 
     100             : #include <keyutils.h>
     101             : 
     102             : /*
     103             :  * We try to use the big_key key type for credentials except in legacy caches.
     104             :  * We fall back to the user key type if the kernel does not support big_key.
     105             :  * If the library doesn't support keyctl_get_persistent(), we don't even try
     106             :  * big_key since the two features were added at the same time.
     107             :  */
     108             : #ifdef HAVE_KEYCTL_GET_PERSISTENT
     109             : #define KRCC_CRED_KEY_TYPE              "big_key"
     110             : #else
     111             : #define KRCC_CRED_KEY_TYPE              "user"
     112             : #endif
     113             : 
     114             : /*
     115             :  * We use the "user" key type for collection primary names, for cache principal
     116             :  * names, and for credentials in legacy caches.
     117             :  */
     118             : #define KRCC_KEY_TYPE_USER              "user"
     119             : 
     120             : /*
     121             :  * We create ccaches as separate keyrings
     122             :  */
     123             : #define KRCC_KEY_TYPE_KEYRING           "keyring"
     124             : 
     125             : /*
     126             :  * Special name of the key within a ccache keyring
     127             :  * holding principal information
     128             :  */
     129             : #define KRCC_SPEC_PRINC_KEYNAME         "__krb5_princ__"
     130             : 
     131             : /*
     132             :  * Special name for the key to communicate the name(s)
     133             :  * of credentials caches to be used for requests.
     134             :  * This should currently contain a single name, but
     135             :  * in the future may contain a list that may be
     136             :  * intelligently chosen from.
     137             :  */
     138             : #define KRCC_SPEC_CCACHE_SET_KEYNAME    "__krb5_cc_set__"
     139             : 
     140             : /*
     141             :  * This name identifies the key containing the name of the current primary
     142             :  * cache within a collection.
     143             :  */
     144             : #define KRCC_COLLECTION_PRIMARY         "krb_ccache:primary"
     145             : 
     146             : /*
     147             :  * If the library context does not specify a keyring collection, unique ccaches
     148             :  * will be created within this collection.
     149             :  */
     150             : #define KRCC_DEFAULT_UNIQUE_COLLECTION  "session:__krb5_unique__"
     151             : 
     152             : /*
     153             :  * Collection keyring names begin with this prefix.  We use a prefix so that a
     154             :  * cache keyring with the collection name itself can be linked directly into
     155             :  * the anchor, for legacy session keyring compatibility.
     156             :  */
     157             : #define KRCC_CCCOL_PREFIX               "_krb_"
     158             : 
     159             : /*
     160             :  * For the "persistent" anchor type, we look up or create this fixed keyring
     161             :  * name within the per-UID persistent keyring.
     162             :  */
     163             : #define KRCC_PERSISTENT_KEYRING_NAME    "_krb"
     164             : 
     165             : /*
     166             :  * Name of the key holding time offsets for the individual cache
     167             :  */
     168             : #define KRCC_TIME_OFFSETS               "__krb5_time_offsets__"
     169             : 
     170             : /*
     171             :  * Keyring name prefix and length of random name part
     172             :  */
     173             : #define KRCC_NAME_PREFIX                "krb_ccache_"
     174             : #define KRCC_NAME_RAND_CHARS            8
     175             : 
     176             : #define KRCC_COLLECTION_VERSION         1
     177             : 
     178             : #define KRCC_PERSISTENT_ANCHOR          "persistent"
     179             : #define KRCC_PROCESS_ANCHOR             "process"
     180             : #define KRCC_THREAD_ANCHOR              "thread"
     181             : #define KRCC_SESSION_ANCHOR             "session"
     182             : #define KRCC_USER_ANCHOR                "user"
     183             : #define KRCC_LEGACY_ANCHOR              "legacy"
     184             : 
     185             : #if SIZEOF_KEY_SERIAL_T != 4
     186             : /* lockless implementation assumes 32-bit key serials */
     187             : #error only 32-bit key serial numbers supported by this version of keyring ccache
     188             : #endif
     189             : 
     190             : typedef heim_base_atomic(key_serial_t) atomic_key_serial_t;
     191             : 
     192             : typedef union _krb5_krcache_and_princ_id {
     193             :     heim_base_atomic(uint64_t) krcu_cache_and_princ_id;
     194             :     struct {
     195             :         atomic_key_serial_t cache_id;   /* keyring ID representing ccache */
     196             :         atomic_key_serial_t princ_id;   /* key ID holding principal info */
     197             :     } krcu_id;
     198             :     #define krcu_cache_id               krcu_id.cache_id
     199             :     #define krcu_princ_id               krcu_id.princ_id
     200             : } krb5_krcache_and_princ_id;
     201             : 
     202             : /*
     203             :  * This represents a credentials cache "file" where cache_id is the keyring
     204             :  * serial number for this credentials cache "file".  Each key in the keyring
     205             :  * contains a separate key.
     206             :  *
     207             :  * Thread-safe as long as caches are not destroyed whilst other threads are
     208             :  * using them.
     209             :  */
     210             : typedef struct _krb5_krcache {
     211             :     char *krc_name;                     /* Name for this credentials cache */
     212             :     char *krc_collection;
     213             :     char *krc_subsidiary;
     214             :     heim_base_atomic(krb5_timestamp) krc_changetime;    /* update time, does not decrease (mutable) */
     215             :     krb5_krcache_and_princ_id krc_id;   /* cache and principal IDs (mutable) */
     216             :     #define krc_cache_and_principal_id  krc_id.krcu_cache_and_princ_id
     217             :     #define krc_cache_id                krc_id.krcu_id.cache_id
     218             :     #define krc_princ_id                krc_id.krcu_id.princ_id
     219             :     key_serial_t krc_coll_id;           /* collection containing this cache keyring */
     220             :     krb5_boolean krc_is_legacy;         /* */
     221             : } krb5_krcache;
     222             : 
     223             : #define KRCACHE(X) ((krb5_krcache *)(X)->data.data)
     224             : 
     225             : static krb5_error_code KRB5_CALLCONV
     226             : krcc_get_first(krb5_context, krb5_ccache id, krb5_cc_cursor *cursor);
     227             : 
     228             : static krb5_error_code KRB5_CALLCONV
     229             : krcc_get_next(krb5_context context,
     230             :               krb5_ccache id,
     231             :               krb5_cc_cursor *cursor,
     232             :               krb5_creds *creds);
     233             : 
     234             : static krb5_error_code KRB5_CALLCONV
     235             : krcc_end_get(krb5_context context,
     236             :              krb5_ccache id,
     237             :              krb5_cc_cursor *cursor);
     238             : 
     239             : static krb5_error_code KRB5_CALLCONV
     240             : krcc_end_cache_get(krb5_context context, krb5_cc_cursor cursor);
     241             : 
     242             : static krb5_error_code
     243             : clear_cache_keyring(krb5_context context, atomic_key_serial_t *pcache_id);
     244             : 
     245             : static krb5_error_code
     246             : alloc_cache(krb5_context context,
     247             :             key_serial_t collection_id,
     248             :             key_serial_t cache_id,
     249             :             const char *anchor_name,
     250             :             const char *collection_name,
     251             :             const char *subsidiary_name,
     252             :             krb5_krcache **data);
     253             : 
     254             : static krb5_error_code
     255             : save_principal(krb5_context context,
     256             :                key_serial_t cache_id,
     257             :                krb5_const_principal princ,
     258             :                atomic_key_serial_t *pprinc_id);
     259             : 
     260             : static krb5_error_code
     261             : save_time_offsets(krb5_context context,
     262             :                   key_serial_t cache_id,
     263             :                   int32_t sec_offset,
     264             :                   int32_t usec_offset);
     265             : 
     266             : static void
     267             : update_change_time(krb5_context context,
     268             :                    krb5_timestamp now,
     269             :                    krb5_krcache *d);
     270             : 
     271             : /*
     272             :  * GET_PERSISTENT(uid) acquires the persistent keyring for uid, or falls back
     273             :  * to the user keyring if uid matches the current effective uid.
     274             :  */
     275             : 
     276             : static key_serial_t
     277           0 : get_persistent_fallback(uid_t uid)
     278             : {
     279           0 :     return (uid == geteuid()) ? KEY_SPEC_USER_KEYRING : -1;
     280             : }
     281             : 
     282             : #ifdef HAVE_KEYCTL_GET_PERSISTENT
     283             : #define GET_PERSISTENT get_persistent_real
     284             : static key_serial_t
     285           0 : get_persistent_real(uid_t uid)
     286             : {
     287           0 :     key_serial_t key;
     288             : 
     289           0 :     key = keyctl_get_persistent(uid, KEY_SPEC_PROCESS_KEYRING);
     290             : 
     291           0 :     return (key == -1 && errno == ENOTSUP) ? get_persistent_fallback(uid) : key;
     292             : }
     293             : #else
     294             : #define GET_PERSISTENT get_persistent_fallback
     295             : #endif
     296             : 
     297             : /*
     298             :  * If a process has no explicitly set session keyring, KEY_SPEC_SESSION_KEYRING
     299             :  * will resolve to the user session keyring for ID lookup and reading, but in
     300             :  * some kernel versions, writing to that special keyring will instead create a
     301             :  * new empty session keyring for the process.  We do not want that; the keys we
     302             :  * create would be invisible to other processes.  We can work around that
     303             :  * behavior by explicitly writing to the user session keyring when it matches
     304             :  * the session keyring.  This function returns the keyring we should write to
     305             :  * for the session anchor.
     306             :  */
     307             : static key_serial_t
     308           0 : session_write_anchor(void)
     309             : {
     310           0 :     key_serial_t s, u;
     311             : 
     312           0 :     s = keyctl_get_keyring_ID(KEY_SPEC_SESSION_KEYRING, 0);
     313           0 :     u = keyctl_get_keyring_ID(KEY_SPEC_USER_SESSION_KEYRING, 0);
     314             : 
     315           0 :     return (s == u) ? KEY_SPEC_USER_SESSION_KEYRING : KEY_SPEC_SESSION_KEYRING;
     316             : }
     317             : 
     318             : /*
     319             :  * Find or create a keyring within parent with the given name.  If possess is
     320             :  * nonzero, also make sure the key is linked from possess.  This is necessary
     321             :  * to ensure that we have possession rights on the key when the parent is the
     322             :  * user or persistent keyring.
     323             :  */
     324             : static krb5_error_code
     325           0 : find_or_create_keyring(key_serial_t parent,
     326             :                        key_serial_t possess,
     327             :                        const char *name,
     328             :                        atomic_key_serial_t *pkey)
     329             : {
     330           0 :     key_serial_t key;
     331             : 
     332           0 :     key = keyctl_search(parent, KRCC_KEY_TYPE_KEYRING, name, possess);
     333           0 :     if (key == -1) {
     334           0 :         if (possess != 0) {
     335           0 :             key = add_key(KRCC_KEY_TYPE_KEYRING, name, NULL, 0, possess);
     336           0 :             if (key == -1 || keyctl_link(key, parent) == -1)
     337           0 :                 return errno;
     338             :         } else {
     339           0 :             key = add_key(KRCC_KEY_TYPE_KEYRING, name, NULL, 0, parent);
     340           0 :             if (key == -1)
     341           0 :                 return errno;
     342             :         }
     343             :     }
     344             : 
     345           0 :     heim_base_atomic_store(pkey, key);
     346             : 
     347           0 :     return 0;
     348             : }
     349             : 
     350             : /*
     351             :  * Parse a residual name into an anchor name, a collection name, and possibly a
     352             :  * subsidiary name.
     353             :  */
     354             : static krb5_error_code
     355           0 : parse_residual(krb5_context context,
     356             :                const char *residual,
     357             :                char **panchor_name,
     358             :                char **pcollection_name,
     359             :                char **psubsidiary_name)
     360             : {
     361           0 :     char *anchor_name = NULL;
     362           0 :     char *collection_name = NULL;
     363           0 :     char *subsidiary_name = NULL;
     364           0 :     const char *sep;
     365             : 
     366           0 :     *panchor_name = NULL;
     367           0 :     *pcollection_name = NULL;
     368           0 :     *psubsidiary_name = NULL;
     369             : 
     370           0 :     if (residual == NULL)
     371           0 :         residual = "";
     372             : 
     373             :     /* Parse out the anchor name.  Use the legacy anchor if not present. */
     374           0 :     sep = strchr(residual, ':');
     375           0 :     if (sep == NULL) {
     376           0 :         anchor_name = strdup(KRCC_LEGACY_ANCHOR);
     377           0 :         if (anchor_name == NULL)
     378           0 :             goto nomem;
     379             :     } else {
     380           0 :         anchor_name = strndup(residual, sep - residual);
     381           0 :         if (anchor_name == NULL)
     382           0 :             goto nomem;
     383           0 :         residual = sep + 1;
     384             :     }
     385             : 
     386             :     /* Parse out the collection and subsidiary name. */
     387           0 :     sep = strchr(residual, ':');
     388           0 :     if (sep == NULL) {
     389           0 :         collection_name = strdup(residual);
     390           0 :         if (collection_name == NULL)
     391           0 :             goto nomem;
     392             :     } else {
     393           0 :         collection_name = strndup(residual, sep - residual);
     394           0 :         if (collection_name == NULL)
     395           0 :             goto nomem;
     396             : 
     397           0 :         subsidiary_name = strdup(sep + 1);
     398           0 :         if (subsidiary_name == NULL)
     399           0 :             goto nomem;
     400             :     }
     401             : 
     402           0 :     *panchor_name = anchor_name;
     403           0 :     *pcollection_name = collection_name;
     404           0 :     *psubsidiary_name = subsidiary_name;
     405             : 
     406           0 :     return 0;
     407             : 
     408           0 : nomem:
     409           0 :     free(anchor_name);
     410           0 :     free(collection_name);
     411           0 :     free(subsidiary_name);
     412             : 
     413           0 :     return krb5_enomem(context);
     414             : }
     415             : 
     416             : /*
     417             :  * Return TRUE if residual identifies a subsidiary cache which should be linked
     418             :  * into the anchor so it can be visible to old code.  This is the case if the
     419             :  * residual has the legacy anchor and the subsidiary name matches the
     420             :  * collection name.
     421             :  */
     422             : static krb5_boolean
     423           0 : is_legacy_cache_name_p(const char *residual)
     424             : {
     425           0 :     const char *sep, *aname, *cname, *sname;
     426           0 :     size_t alen, clen, legacy_len = sizeof(KRCC_LEGACY_ANCHOR) - 1;
     427             : 
     428             :     /* Get pointers to the anchor, collection, and subsidiary names. */
     429           0 :     aname = residual;
     430           0 :     sep = strchr(residual, ':');
     431           0 :     if (sep == NULL)
     432           0 :         return FALSE;
     433             : 
     434           0 :     alen = sep - aname;
     435           0 :     cname = sep + 1;
     436           0 :     sep = strchr(cname, ':');
     437           0 :     if (sep == NULL)
     438           0 :         return FALSE;
     439             : 
     440           0 :     clen = sep - cname;
     441           0 :     sname = sep + 1;
     442             : 
     443           0 :     return alen == legacy_len && clen == strlen(sname) &&
     444           0 :            strncmp(aname, KRCC_LEGACY_ANCHOR, alen) == 0 &&
     445           0 :            strncmp(cname, sname, clen) == 0;
     446             : }
     447             : 
     448             : /*
     449             :  * If the default cache name for context is a KEYRING cache, parse its residual
     450             :  * string.  Otherwise set all outputs to NULL.
     451             :  */
     452             : static krb5_error_code
     453          52 : get_default(krb5_context context,
     454             :             char **panchor_name,
     455             :             char **pcollection_name,
     456             :             char **psubsidiary_name)
     457             : {
     458           0 :     const char *defname;
     459             : 
     460          52 :     *panchor_name = *pcollection_name = *psubsidiary_name = NULL;
     461             : 
     462          52 :     defname = krb5_cc_default_name(context);
     463          52 :     if (defname == NULL || strncmp(defname, "KEYRING:", 8) != 0)
     464          52 :         return 0;
     465             : 
     466           0 :     return parse_residual(context, defname + 8,
     467             :                           panchor_name, pcollection_name, psubsidiary_name);
     468             : }
     469             : 
     470             : /* Create a residual identifying a subsidiary cache. */
     471             : static krb5_error_code
     472           0 : make_subsidiary_residual(krb5_context context,
     473             :                          const char *anchor_name,
     474             :                          const char *collection_name,
     475             :                          const char *subsidiary_name,
     476             :                          char **presidual)
     477             : {
     478           0 :     if (asprintf(presidual, "%s:%s:%s", anchor_name, collection_name,
     479             :                  subsidiary_name ? subsidiary_name : "tkt") < 0) {
     480           0 :         *presidual = NULL;
     481           0 :         return krb5_enomem(context);
     482             :     }
     483             : 
     484           0 :     return 0;
     485             : }
     486             : 
     487             : /*
     488             :  * Retrieve or create a keyring for collection_name within the anchor, and set
     489             :  * *collection_id to its serial number.
     490             :  */
     491             : static krb5_error_code
     492           0 : get_collection(krb5_context context,
     493             :                const char *anchor_name,
     494             :                const char *collection_name,
     495             :                atomic_key_serial_t *pcollection_id)
     496             : {
     497           0 :     krb5_error_code ret;
     498           0 :     key_serial_t persistent_id, anchor_id, possess_id = 0;
     499           0 :     char *ckname, *cnend;
     500           0 :     uid_t uidnum;
     501             : 
     502           0 :     heim_base_atomic_init(pcollection_id, 0);
     503             : 
     504           0 :     if (!anchor_name || !collection_name)
     505           0 :         return KRB5_KCC_INVALID_ANCHOR;
     506             : 
     507           0 :     if (strcmp(anchor_name, KRCC_PERSISTENT_ANCHOR) == 0) {
     508             :         /*
     509             :          * The collection name is a uid (or empty for the current effective
     510             :          * uid), and we look up a fixed keyring name within the persistent
     511             :          * keyring for that uid.  We link it to the process keyring to ensure
     512             :          * that we have possession rights on the collection key.
     513             :          */
     514           0 :         if (*collection_name != '\0') {
     515           0 :             errno = 0;
     516           0 :             uidnum = (uid_t)strtol(collection_name, &cnend, 10);
     517           0 :             if (errno || *cnend != '\0')
     518           0 :                 return KRB5_KCC_INVALID_UID;
     519             :         } else {
     520           0 :             uidnum = geteuid();
     521             :         }
     522             : 
     523           0 :         persistent_id = GET_PERSISTENT(uidnum);
     524           0 :         if (persistent_id == -1)
     525           0 :             return KRB5_KCC_INVALID_UID;
     526             : 
     527           0 :         return find_or_create_keyring(persistent_id, KEY_SPEC_PROCESS_KEYRING,
     528             :                                       KRCC_PERSISTENT_KEYRING_NAME,
     529             :                                       pcollection_id);
     530             :     }
     531             : 
     532           0 :     if (strcmp(anchor_name, KRCC_PROCESS_ANCHOR) == 0) {
     533           0 :         anchor_id = KEY_SPEC_PROCESS_KEYRING;
     534           0 :     } else if (strcmp(anchor_name, KRCC_THREAD_ANCHOR) == 0) {
     535           0 :         anchor_id = KEY_SPEC_THREAD_KEYRING;
     536           0 :     } else if (strcmp(anchor_name, KRCC_SESSION_ANCHOR) == 0) {
     537           0 :         anchor_id = session_write_anchor();
     538           0 :     } else if (strcmp(anchor_name, KRCC_USER_ANCHOR) == 0) {
     539             :         /*
     540             :          * The user keyring does not confer possession, so we need to link the
     541             :          * collection to the process keyring to maintain possession rights.
     542             :          */
     543           0 :         anchor_id = KEY_SPEC_USER_KEYRING;
     544           0 :         possess_id = KEY_SPEC_PROCESS_KEYRING;
     545           0 :     } else if (strcmp(anchor_name, KRCC_LEGACY_ANCHOR) == 0) {
     546           0 :         anchor_id = session_write_anchor();
     547             :     } else {
     548           0 :         return KRB5_KCC_INVALID_ANCHOR;
     549             :     }
     550             : 
     551             :     /* Look up the collection keyring name within the anchor keyring. */
     552           0 :     if (asprintf(&ckname, "%s%s", KRCC_CCCOL_PREFIX, collection_name) == -1)
     553           0 :         return krb5_enomem(context);
     554             : 
     555           0 :     ret = find_or_create_keyring(anchor_id, possess_id, ckname,
     556             :                                  pcollection_id);
     557           0 :     free(ckname);
     558             : 
     559           0 :     return ret;
     560             : }
     561             : 
     562             : /* Store subsidiary_name into the primary index key for collection_id. */
     563             : static krb5_error_code
     564           0 : set_primary_name(krb5_context context,
     565             :                  key_serial_t collection_id,
     566             :                  const char *subsidiary_name)
     567             : {
     568           0 :     krb5_error_code ret;
     569           0 :     krb5_storage *sp;
     570           0 :     krb5_data payload;
     571           0 :     key_serial_t key;
     572             : 
     573           0 :     sp = krb5_storage_emem();
     574           0 :     if (sp == NULL) {
     575           0 :         krb5_set_error_message(context, KRB5_CC_NOMEM, N_("malloc: out of memory", ""));
     576           0 :         return KRB5_CC_NOMEM;
     577             :     }
     578           0 :     krb5_storage_set_byteorder(sp, KRB5_STORAGE_BYTEORDER_BE);
     579             : 
     580           0 :     ret = krb5_store_int32(sp, KRCC_COLLECTION_VERSION);
     581           0 :     if (ret)
     582           0 :         goto cleanup;
     583             : 
     584           0 :     ret = krb5_store_string(sp, subsidiary_name);
     585           0 :     if (ret)
     586           0 :         goto cleanup;
     587             : 
     588           0 :     ret = krb5_storage_to_data(sp, &payload);
     589           0 :     if (ret)
     590           0 :         goto cleanup;
     591             : 
     592           0 :     key = add_key(KRCC_KEY_TYPE_USER, KRCC_COLLECTION_PRIMARY,
     593           0 :                   payload.data, payload.length, collection_id);
     594           0 :     ret = (key == -1) ? errno : 0;
     595           0 :     krb5_data_free(&payload);
     596             : 
     597           0 : cleanup:
     598           0 :     krb5_storage_free(sp);
     599             : 
     600           0 :     return ret;
     601             : }
     602             : 
     603             : static krb5_error_code
     604           0 : parse_index(krb5_context context,
     605             :             int32_t *version,
     606             :             char **primary,
     607             :             const unsigned char *payload,
     608             :             size_t psize)
     609             : {
     610           0 :     krb5_error_code ret;
     611           0 :     krb5_data payload_data;
     612           0 :     krb5_storage *sp;
     613             : 
     614           0 :     payload_data.length = psize;
     615           0 :     payload_data.data = rk_UNCONST(payload);
     616             : 
     617           0 :     sp = krb5_storage_from_data(&payload_data);
     618           0 :     if (sp == NULL)
     619           0 :         return KRB5_CC_NOMEM;
     620             : 
     621           0 :     krb5_storage_set_byteorder(sp, KRB5_STORAGE_BYTEORDER_BE);
     622             : 
     623           0 :     ret = krb5_ret_int32(sp, version);
     624           0 :     if (ret == 0)
     625           0 :         ret = krb5_ret_string(sp, primary);
     626             : 
     627           0 :     krb5_storage_free(sp);
     628             : 
     629           0 :     return ret;
     630             : }
     631             : 
     632             : /*
     633             :  * Get or initialize the primary name within collection_id and set
     634             :  * *subsidiary to its value.  If initializing a legacy collection, look
     635             :  * for a legacy cache and add it to the collection.
     636             :  */
     637             : static krb5_error_code
     638           0 : get_primary_name(krb5_context context,
     639             :                  const char *anchor_name,
     640             :                  const char *collection_name,
     641             :                  key_serial_t collection_id,
     642             :                  char **psubsidiary)
     643             : {
     644           0 :     krb5_error_code ret;
     645           0 :     key_serial_t primary_id, legacy;
     646           0 :     void *payload = NULL;
     647           0 :     int payloadlen;
     648           0 :     int32_t version;
     649           0 :     char *subsidiary_name = NULL;
     650             : 
     651           0 :     *psubsidiary = NULL;
     652             : 
     653           0 :     primary_id = keyctl_search(collection_id, KRCC_KEY_TYPE_USER,
     654             :                                KRCC_COLLECTION_PRIMARY, 0);
     655           0 :     if (primary_id == -1) {
     656             :         /*
     657             :          * Initialize the primary key using the collection name.  We can't name
     658             :          * a key with the empty string, so map that to an arbitrary string.
     659             :          */
     660           0 :         subsidiary_name = strdup((*collection_name == '\0') ? "tkt" :
     661             :                                  collection_name);
     662           0 :         if (subsidiary_name == NULL) {
     663           0 :             ret = krb5_enomem(context);
     664           0 :             goto cleanup;
     665             :         }
     666             : 
     667           0 :         ret = set_primary_name(context, collection_id, subsidiary_name);
     668           0 :         if (ret)
     669           0 :             goto cleanup;
     670             : 
     671           0 :         if (strcmp(anchor_name, KRCC_LEGACY_ANCHOR) == 0) {
     672             :             /*
     673             :              * Look for a cache created by old code. If we find one, add it to
     674             :              * the collection.
     675             :              */
     676           0 :             legacy = keyctl_search(KEY_SPEC_SESSION_KEYRING,
     677             :                                    KRCC_KEY_TYPE_KEYRING, subsidiary_name, 0);
     678           0 :             if (legacy != -1 && keyctl_link(legacy, collection_id) == -1) {
     679           0 :                 ret = errno;
     680           0 :                 goto cleanup;
     681             :             }
     682             :         }
     683             :     } else {
     684             :         /* Read, parse, and free the primary key's payload. */
     685           0 :         payloadlen = keyctl_read_alloc(primary_id, &payload);
     686           0 :         if (payloadlen == -1) {
     687           0 :             ret = errno;
     688           0 :             goto cleanup;
     689             :         }
     690           0 :         ret = parse_index(context, &version, &subsidiary_name, payload,
     691             :                           payloadlen);
     692           0 :         if (ret)
     693           0 :             goto cleanup;
     694             : 
     695           0 :         if (version != KRCC_COLLECTION_VERSION) {
     696           0 :             ret = KRB5_KCC_UNKNOWN_VERSION;
     697           0 :             goto cleanup;
     698             :         }
     699             :     }
     700             : 
     701           0 :     *psubsidiary = subsidiary_name;
     702           0 :     subsidiary_name = NULL;
     703             : 
     704           0 : cleanup:
     705           0 :     free(payload);
     706           0 :     free(subsidiary_name);
     707             : 
     708           0 :     return ret;
     709             : }
     710             : 
     711             : /*
     712             :  * Note: MIT keyring code uses krb5int_random_string() as if the second argument
     713             :  * is a character count rather than a size. The function below takes a character
     714             :  * count to match the usage in this file correctly.
     715             :  */
     716             : static krb5_error_code
     717           0 : generate_random_string(krb5_context context, char *s, size_t slen)
     718             : {
     719           0 :     static char chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
     720           0 :     char *p;
     721           0 :     size_t i;
     722             : 
     723           0 :     p = malloc(slen);
     724           0 :     if (p == NULL)
     725           0 :         return krb5_enomem(context);
     726             : 
     727           0 :     krb5_generate_random_block(p, slen);
     728             : 
     729           0 :     for (i = 0; i < slen; i++)
     730           0 :         s[i] = chars[p[i] % (sizeof(chars) - 1)];
     731             : 
     732           0 :     s[i] = '\0';
     733           0 :     free(p);
     734             : 
     735           0 :     return 0;
     736             : }
     737             : 
     738             : /*
     739             :  * Create a keyring with a unique random name within collection_id.  Set
     740             :  * *subsidiary to its name and *cache_id to its key serial number.
     741             :  */
     742             : static krb5_error_code
     743           0 : add_unique_keyring(krb5_context context,
     744             :                    key_serial_t collection_id,
     745             :                    char **psubsidiary,
     746             :                    key_serial_t *pcache_id)
     747             : {
     748           0 :     key_serial_t key;
     749           0 :     krb5_error_code ret;
     750           0 :     char uniquename[sizeof(KRCC_NAME_PREFIX) + KRCC_NAME_RAND_CHARS];
     751           0 :     int prefixlen = sizeof(KRCC_NAME_PREFIX) - 1;
     752           0 :     int tries;
     753             : 
     754           0 :     *psubsidiary = NULL;
     755           0 :     *pcache_id = 0;
     756             : 
     757           0 :     memcpy(uniquename, KRCC_NAME_PREFIX, sizeof(KRCC_NAME_PREFIX));
     758             : 
     759           0 :     for (key = -1, tries = 0; tries < 5; tries++) {
     760           0 :         ret = generate_random_string(context, uniquename + prefixlen,
     761             :                                      KRCC_NAME_RAND_CHARS);
     762           0 :         if (ret)
     763           0 :             return ret;
     764             : 
     765           0 :         key = keyctl_search(collection_id, KRCC_KEY_TYPE_KEYRING, uniquename, 0);
     766           0 :         if (key == -1) {
     767             :             /* Name does not already exist.  Create it to reserve the name. */
     768           0 :             key = add_key(KRCC_KEY_TYPE_KEYRING, uniquename, NULL, 0, collection_id);
     769           0 :             if (key == -1)
     770           0 :                 return errno;
     771           0 :             break;
     772             :         }
     773             :     }
     774             : 
     775           0 :     *psubsidiary = strdup(uniquename);
     776           0 :     if (*psubsidiary == NULL)
     777           0 :         return krb5_enomem(context);
     778             : 
     779           0 :     *pcache_id = key;
     780             : 
     781           0 :     return 0;
     782             : }
     783             : 
     784             : static krb5_error_code
     785           0 : add_cred_key(const char *name,
     786             :              const void *payload,
     787             :              size_t plen,
     788             :              key_serial_t cache_id,
     789             :              krb5_boolean legacy_type,
     790             :              key_serial_t *pkey)
     791             : {
     792           0 :     key_serial_t key;
     793             : 
     794           0 :     *pkey = -1;
     795             : 
     796           0 :     if (!legacy_type) {
     797             :         /* Try the preferred cred key type; fall back if no kernel support. */
     798           0 :         key = add_key(KRCC_CRED_KEY_TYPE, name, payload, plen, cache_id);
     799           0 :         if (key != -1) {
     800           0 :             *pkey = key;
     801           0 :             return 0;
     802           0 :         } else if (errno != EINVAL && errno != ENODEV)
     803           0 :             return errno;
     804             :     }
     805             : 
     806             :     /* Use the user key type. */
     807           0 :     key = add_key(KRCC_KEY_TYPE_USER, name, payload, plen, cache_id);
     808           0 :     if (key == -1)
     809           0 :         return errno;
     810             : 
     811           0 :     *pkey = key;
     812             : 
     813           0 :     return 0;
     814             : }
     815             : 
     816             : static void
     817           0 : update_keyring_expiration(krb5_context context,
     818             :                           krb5_ccache id,
     819             :                           key_serial_t cache_id,
     820             :                           krb5_timestamp now)
     821             : {
     822           0 :     krb5_cc_cursor cursor;
     823           0 :     krb5_creds creds;
     824           0 :     krb5_timestamp endtime = 0;
     825           0 :     unsigned int timeout;
     826             : 
     827             :     /*
     828             :      * We have no way to know what is the actual timeout set on the keyring.
     829             :      * We also cannot keep track of it in a local variable as another process
     830             :      * can always modify the keyring independently, so just always enumerate
     831             :      * all start TGT keys and find out the highest endtime time.
     832             :      */
     833           0 :     if (krcc_get_first(context, id, &cursor) != 0)
     834           0 :         return;
     835             : 
     836           0 :     for (;;) {
     837           0 :         if (krcc_get_next(context, id, &cursor, &creds) != 0)
     838           0 :             break;
     839           0 :         if (creds.times.endtime > endtime)
     840           0 :             endtime = creds.times.endtime;
     841           0 :         krb5_free_cred_contents(context, &creds);
     842             :     }
     843           0 :     (void) krcc_end_get(context, id, &cursor);
     844             : 
     845           0 :     if (endtime == 0)   /* No creds with end times */
     846           0 :         return;
     847             : 
     848             :     /*
     849             :      * Setting the timeout to zero would reset the timeout, so we set it to one
     850             :      * second instead if creds are already expired.
     851             :      */
     852           0 :     timeout = endtime > now ? endtime - now : 1;
     853           0 :     (void) keyctl_set_timeout(cache_id, timeout);
     854             : }
     855             : 
     856             : /*
     857             :  * Create or overwrite the cache keyring, and set the default principal.
     858             :  */
     859             : static krb5_error_code
     860           0 : initialize_internal(krb5_context context,
     861             :                     krb5_ccache id,
     862             :                     krb5_const_principal princ)
     863             : {
     864           0 :     krb5_krcache *data = KRCACHE(id);
     865           0 :     krb5_error_code ret;
     866           0 :     const char *cache_name, *p;
     867           0 :     krb5_krcache_and_princ_id ids;
     868             : 
     869           0 :     if (data == NULL)
     870           0 :         return krb5_einval(context, 2);
     871             : 
     872           0 :     memset(&ids, 0, sizeof(ids));
     873           0 :     ids.krcu_cache_and_princ_id = heim_base_atomic_load(&data->krc_cache_and_principal_id);
     874             : 
     875           0 :     ret = clear_cache_keyring(context, &ids.krcu_cache_id);
     876           0 :     if (ret)
     877           0 :         return ret;
     878             : 
     879           0 :     if (ids.krcu_cache_id == 0) {
     880             :         /*
     881             :          * The key didn't exist at resolve time, or was destroyed after resolving.
     882             :          * Check again and create the key if it still isn't there.
     883             :          */
     884           0 :         p = strrchr(data->krc_name, ':');
     885           0 :         cache_name = (p != NULL) ? p + 1 : data->krc_name;
     886           0 :         ret = find_or_create_keyring(data->krc_coll_id, 0, cache_name, &ids.krcu_cache_id);
     887           0 :         if (ret)
     888           0 :             return ret;
     889             :     }
     890             : 
     891             :     /*
     892             :      * If this is the legacy cache in a legacy session collection, link it
     893             :      * directly to the session keyring so that old code can see it.
     894             :      */
     895           0 :     if (is_legacy_cache_name_p(data->krc_name))
     896           0 :         (void) keyctl_link(ids.krcu_cache_id, session_write_anchor());
     897             : 
     898           0 :     if (princ != NULL) {
     899           0 :         ret = save_principal(context, ids.krcu_cache_id, princ, &ids.krcu_princ_id);
     900           0 :         if (ret)
     901           0 :             return ret;
     902             :     } else
     903           0 :         ids.krcu_princ_id = 0;
     904             : 
     905             :     /*
     906             :      * Save time offset if it is valid and this is not a legacy cache.  Legacy
     907             :      * applications would fail to parse the new key in the cache keyring.
     908             :      */
     909           0 :     if (context->kdc_sec_offset && !is_legacy_cache_name_p(data->krc_name)) {
     910           0 :         ret = save_time_offsets(context,
     911           0 :                                 ids.krcu_cache_id,
     912             :                                 context->kdc_sec_offset,
     913             :                                 context->kdc_usec_offset);
     914           0 :         if (ret)
     915           0 :             return ret;
     916             :     }
     917             : 
     918             :     /* update cache and principal IDs atomically */
     919           0 :     heim_base_atomic_store(&data->krc_cache_and_principal_id, ids.krcu_cache_and_princ_id);
     920             : 
     921           0 :     return 0;
     922             : }
     923             : 
     924             : static krb5_error_code KRB5_CALLCONV
     925           0 : krcc_initialize(krb5_context context, krb5_ccache id, krb5_principal princ)
     926             : {
     927           0 :     krb5_krcache *data = KRCACHE(id);
     928           0 :     krb5_error_code ret;
     929             : 
     930           0 :     if (data == NULL)
     931           0 :         return krb5_einval(context, 2);
     932             : 
     933           0 :     if (princ == NULL)
     934           0 :         return KRB5_CC_BADNAME;
     935             : 
     936           0 :     ret = initialize_internal(context, id, princ);
     937           0 :     if (ret == 0)
     938           0 :         update_change_time(context, 0, data);
     939             : 
     940           0 :     return ret;
     941             : }
     942             : 
     943             : /* Release the ccache handle. */
     944             : static krb5_error_code KRB5_CALLCONV
     945           0 : krcc_close(krb5_context context, krb5_ccache id)
     946             : {
     947           0 :     krb5_krcache *data = KRCACHE(id);
     948             : 
     949           0 :     if (data == NULL)
     950           0 :         return krb5_einval(context, 2);
     951             : 
     952           0 :     free(data->krc_subsidiary);
     953           0 :     free(data->krc_collection);
     954           0 :     free(data->krc_name);
     955           0 :     krb5_data_free(&id->data);
     956             : 
     957           0 :     return 0;
     958             : }
     959             : 
     960             : /*
     961             :  * Clear out a ccache keyring, unlinking all keys within it.
     962             :  */
     963             : static krb5_error_code
     964           0 : clear_cache_keyring(krb5_context context,
     965             :                     atomic_key_serial_t *pcache_id)
     966             : {
     967           0 :     int res;
     968           0 :     key_serial_t cache_id = heim_base_atomic_load(pcache_id);
     969             : 
     970           0 :     _krb5_debug(context, 10, "clear_cache_keyring: cache_id %d\n", cache_id);
     971             : 
     972           0 :     if (cache_id != 0) {
     973           0 :         res = keyctl_clear(cache_id);
     974           0 :         if (res == -1 && (errno == EACCES || errno == ENOKEY)) {
     975             :             /*
     976             :              * Possibly the keyring was destroyed between krcc_resolve() and now;
     977             :              * if we really don't have permission, we will fail later.
     978             :              */
     979           0 :             res = 0;
     980           0 :             heim_base_atomic_store(pcache_id, 0);
     981             :         }
     982           0 :         if (res == -1)
     983           0 :             return errno;
     984             :     }
     985             : 
     986           0 :     return 0;
     987             : }
     988             : 
     989             : /* Destroy the cache keyring */
     990             : static krb5_error_code KRB5_CALLCONV
     991           0 : krcc_destroy(krb5_context context, krb5_ccache id)
     992             : {
     993           0 :     krb5_error_code ret = 0;
     994           0 :     krb5_krcache *data = KRCACHE(id);
     995           0 :     int res;
     996             : 
     997           0 :     if (data == NULL)
     998           0 :         return krb5_einval(context, 2);
     999             : 
    1000             :     /* no atomics, destroy is not thread-safe */
    1001           0 :     (void) clear_cache_keyring(context, &data->krc_cache_id);
    1002             : 
    1003           0 :     if (data->krc_cache_id != 0) {
    1004           0 :         res = keyctl_unlink(data->krc_cache_id, data->krc_coll_id);
    1005           0 :         if (res < 0) {
    1006           0 :             ret = errno;
    1007           0 :             _krb5_debug(context, 10, "unlinking key %d from ring %d: %s",
    1008           0 :                         data->krc_cache_id, data->krc_coll_id, error_message(errno));
    1009             :         }
    1010             :         /* If this is a legacy cache, unlink it from the session anchor. */
    1011           0 :         if (is_legacy_cache_name_p(data->krc_name))
    1012           0 :             (void) keyctl_unlink(data->krc_cache_id, session_write_anchor());
    1013             :     }
    1014             : 
    1015           0 :     heim_base_atomic_store(&data->krc_princ_id, 0);
    1016             : 
    1017             :     /* krcc_close is called by libkrb5, do not double-free */
    1018           0 :     return ret;
    1019             : }
    1020             : 
    1021             : /* Create a cache handle for a cache ID. */
    1022             : static krb5_error_code
    1023           0 : make_cache(krb5_context context,
    1024             :            key_serial_t collection_id,
    1025             :            key_serial_t cache_id,
    1026             :            const char *anchor_name,
    1027             :            const char *collection_name,
    1028             :            const char *subsidiary_name,
    1029             :            krb5_ccache *cache)
    1030             : {
    1031           0 :     krb5_error_code ret;
    1032           0 :     krb5_krcache *data;
    1033           0 :     key_serial_t princ_id = 0;
    1034             : 
    1035             :     /* Determine the key containing principal information, if present. */
    1036           0 :     princ_id = keyctl_search(cache_id, KRCC_KEY_TYPE_USER, KRCC_SPEC_PRINC_KEYNAME, 0);
    1037           0 :     if (princ_id == -1)
    1038           0 :         princ_id = 0;
    1039             : 
    1040           0 :     ret = alloc_cache(context, collection_id, cache_id,
    1041             :                       anchor_name, collection_name, subsidiary_name, &data);
    1042           0 :     if (ret)
    1043           0 :         return ret;
    1044             : 
    1045           0 :     if (*cache == NULL) {
    1046           0 :         ret = _krb5_cc_allocate(context, &krb5_krcc_ops, cache);
    1047           0 :         if (ret) {
    1048           0 :             free(data->krc_name);
    1049           0 :             free(data);
    1050           0 :             return ret;
    1051             :         }
    1052             :     }
    1053             : 
    1054           0 :     data->krc_princ_id = princ_id;
    1055             : 
    1056           0 :     (*cache)->data.data = data;
    1057           0 :     (*cache)->data.length = sizeof(*data);
    1058             : 
    1059           0 :     return 0;
    1060             : }
    1061             : 
    1062             : /* Create a keyring ccache handle for the given residual string. */
    1063             : static krb5_error_code KRB5_CALLCONV
    1064           0 : krcc_resolve_2(krb5_context context,
    1065             :                krb5_ccache *id,
    1066             :                const char *residual,
    1067             :                const char *sub)
    1068             : {
    1069           0 :     krb5_error_code ret;
    1070           0 :     atomic_key_serial_t collection_id;
    1071           0 :     key_serial_t cache_id;
    1072           0 :     char *anchor_name = NULL, *collection_name = NULL, *subsidiary_name = NULL;
    1073             : 
    1074           0 :     ret = parse_residual(context, residual, &anchor_name, &collection_name,
    1075             :                          &subsidiary_name);
    1076           0 :     if (ret)
    1077           0 :         goto cleanup;
    1078           0 :     if (sub) {
    1079           0 :         free(subsidiary_name);
    1080           0 :         if ((subsidiary_name = strdup(sub)) == NULL) {
    1081           0 :             ret = krb5_enomem(context);
    1082           0 :             goto cleanup;
    1083             :         }
    1084             :     }
    1085             : 
    1086           0 :     ret = get_collection(context, anchor_name, collection_name, &collection_id);
    1087           0 :     if (ret)
    1088           0 :         goto cleanup;
    1089             : 
    1090           0 :     if (subsidiary_name == NULL) {
    1091             :         /* Retrieve or initialize the primary name for the collection. */
    1092           0 :         ret = get_primary_name(context, anchor_name, collection_name,
    1093           0 :                                collection_id, &subsidiary_name);
    1094           0 :         if (ret)
    1095           0 :             goto cleanup;
    1096             :     }
    1097             : 
    1098             :     /* Look up the cache keyring ID, if the cache is already initialized. */
    1099           0 :     cache_id = keyctl_search(collection_id, KRCC_KEY_TYPE_KEYRING,
    1100             :                              subsidiary_name, 0);
    1101           0 :     if (cache_id < 0)
    1102           0 :         cache_id = 0;
    1103             : 
    1104           0 :     ret = make_cache(context, collection_id, cache_id, anchor_name,
    1105             :                      collection_name, subsidiary_name, id);
    1106           0 :     if (ret)
    1107           0 :         goto cleanup;
    1108             : 
    1109           0 : cleanup:
    1110           0 :     free(anchor_name);
    1111           0 :     free(collection_name);
    1112           0 :     free(subsidiary_name);
    1113             : 
    1114           0 :     return ret;
    1115             : }
    1116             : 
    1117             : struct krcc_cursor {
    1118             :     size_t numkeys;
    1119             :     size_t currkey;
    1120             :     key_serial_t princ_id;
    1121             :     key_serial_t offsets_id;
    1122             :     key_serial_t *keys;
    1123             : };
    1124             : 
    1125             : /* Prepare for a sequential iteration over the cache keyring. */
    1126             : static krb5_error_code
    1127           0 : krcc_get_first(krb5_context context,
    1128             :                krb5_ccache id,
    1129             :                krb5_cc_cursor *cursor)
    1130             : {
    1131           0 :     struct krcc_cursor *krcursor;
    1132           0 :     krb5_krcache *data = KRCACHE(id);
    1133           0 :     key_serial_t cache_id;
    1134           0 :     void *keys;
    1135           0 :     long size;
    1136             : 
    1137           0 :     if (data == NULL)
    1138           0 :         return krb5_einval(context, 2);
    1139             : 
    1140           0 :     cache_id = heim_base_atomic_load(&data->krc_cache_id);
    1141           0 :     if (cache_id == 0)
    1142           0 :         return KRB5_FCC_NOFILE;
    1143             : 
    1144           0 :     size = keyctl_read_alloc(cache_id, &keys);
    1145           0 :     if (size == -1) {
    1146           0 :         _krb5_debug(context, 10, "Error getting from keyring: %s\n",
    1147           0 :                     strerror(errno));
    1148           0 :         return KRB5_CC_IO;
    1149             :     }
    1150             : 
    1151           0 :     krcursor = calloc(1, sizeof(*krcursor));
    1152           0 :     if (krcursor == NULL) {
    1153           0 :         free(keys);
    1154           0 :         return KRB5_CC_NOMEM;
    1155             :     }
    1156             : 
    1157           0 :     krcursor->princ_id = heim_base_atomic_load(&data->krc_princ_id);
    1158           0 :     krcursor->offsets_id = keyctl_search(cache_id, KRCC_KEY_TYPE_USER,
    1159             :                                          KRCC_TIME_OFFSETS, 0);
    1160           0 :     krcursor->numkeys = size / sizeof(key_serial_t);
    1161           0 :     krcursor->keys = keys;
    1162             : 
    1163           0 :     *cursor = krcursor;
    1164             : 
    1165           0 :     return 0;
    1166             : }
    1167             : 
    1168             : static krb5_error_code
    1169           0 : keyctl_read_krb5_data(key_serial_t keyid, krb5_data *payload)
    1170             : {
    1171           0 :     krb5_data_zero(payload);
    1172             : 
    1173           0 :     payload->length = keyctl_read_alloc(keyid, &payload->data);
    1174             : 
    1175           0 :     return (payload->length == -1) ? KRB5_FCC_NOFILE : 0;
    1176             : }
    1177             : 
    1178             : /* Get the next credential from the cache keyring. */
    1179             : static krb5_error_code KRB5_CALLCONV
    1180           0 : krcc_get_next(krb5_context context,
    1181             :               krb5_ccache id,
    1182             :               krb5_cc_cursor *cursor,
    1183             :               krb5_creds *creds)
    1184             : {
    1185           0 :     struct krcc_cursor *krcursor;
    1186           0 :     krb5_error_code ret;
    1187           0 :     krb5_data payload;
    1188           0 :     krb5_storage *sp;
    1189             : 
    1190           0 :     memset(creds, 0, sizeof(krb5_creds));
    1191             : 
    1192           0 :     krcursor = *cursor;
    1193           0 :     if (krcursor == NULL)
    1194           0 :         return KRB5_CC_END;
    1195             : 
    1196           0 :     if (krcursor->currkey >= krcursor->numkeys)
    1197           0 :         return KRB5_CC_END;
    1198             : 
    1199             :     /*
    1200             :      * If we're pointing at the entry with the principal, or at the key
    1201             :      * with the time offsets, skip it.
    1202             :      */
    1203           0 :     while (krcursor->keys[krcursor->currkey] == krcursor->princ_id ||
    1204           0 :            krcursor->keys[krcursor->currkey] == krcursor->offsets_id) {
    1205           0 :         krcursor->currkey++;
    1206           0 :         if (krcursor->currkey >= krcursor->numkeys)
    1207           0 :             return KRB5_CC_END;
    1208             :     }
    1209             : 
    1210           0 :     ret = keyctl_read_krb5_data(krcursor->keys[krcursor->currkey], &payload);
    1211           0 :     if (ret) {
    1212           0 :         _krb5_debug(context, 10, "Error reading key %d: %s\n",
    1213           0 :                     krcursor->keys[krcursor->currkey],
    1214           0 :                     strerror(errno));
    1215           0 :         return ret;
    1216             :     }
    1217           0 :     krcursor->currkey++;
    1218             : 
    1219           0 :     sp = krb5_storage_from_data(&payload);
    1220           0 :     if (sp == NULL) {
    1221           0 :         ret = KRB5_CC_IO;
    1222             :     } else {
    1223           0 :         ret = krb5_ret_creds(sp, creds);
    1224           0 :         krb5_storage_free(sp);
    1225             :     }
    1226             : 
    1227           0 :     krb5_data_free(&payload);
    1228             : 
    1229           0 :     return ret;
    1230             : }
    1231             : 
    1232             : /* Release an iteration cursor. */
    1233             : static krb5_error_code KRB5_CALLCONV
    1234           0 : krcc_end_get(krb5_context context, krb5_ccache id, krb5_cc_cursor *cursor)
    1235             : {
    1236           0 :     struct krcc_cursor *krcursor = *cursor;
    1237             : 
    1238           0 :     if (krcursor != NULL) {
    1239           0 :         free(krcursor->keys);
    1240           0 :         free(krcursor);
    1241             :     }
    1242             : 
    1243           0 :     *cursor = NULL;
    1244             : 
    1245           0 :     return 0;
    1246             : }
    1247             : 
    1248             : /* Create keyring data for a credential cache. */
    1249             : static krb5_error_code
    1250           0 : alloc_cache(krb5_context context,
    1251             :             key_serial_t collection_id,
    1252             :             key_serial_t cache_id,
    1253             :             const char *anchor_name,
    1254             :             const char *collection_name,
    1255             :             const char *subsidiary_name,
    1256             :             krb5_krcache **pdata)
    1257             : {
    1258           0 :     krb5_error_code ret;
    1259           0 :     krb5_krcache *data;
    1260             : 
    1261           0 :     *pdata = NULL;
    1262             : 
    1263           0 :     data = calloc(1, sizeof(*data));
    1264           0 :     if (data == NULL)
    1265           0 :         return KRB5_CC_NOMEM;
    1266             : 
    1267           0 :     ret = make_subsidiary_residual(context, anchor_name, collection_name,
    1268             :                                    subsidiary_name, &data->krc_name);
    1269           0 :     if (ret ||
    1270           0 :         (data->krc_collection = strdup(collection_name)) == NULL ||
    1271           0 :         (data->krc_subsidiary = strdup(subsidiary_name ? subsidiary_name : "tkt")) == NULL) {
    1272           0 :         if (data) {
    1273           0 :             free(data->krc_collection);
    1274           0 :             free(data->krc_name);
    1275             :         }
    1276           0 :         free(data);
    1277           0 :         if (ret == 0)
    1278           0 :             ret = krb5_enomem(context);
    1279           0 :         return ret;
    1280             :     }
    1281             : 
    1282           0 :     heim_base_atomic_init(&data->krc_princ_id, 0);
    1283           0 :     heim_base_atomic_init(&data->krc_cache_id, cache_id);
    1284           0 :     data->krc_coll_id = collection_id;
    1285           0 :     data->krc_changetime = 0;
    1286           0 :     data->krc_is_legacy = (strcmp(anchor_name, KRCC_LEGACY_ANCHOR) == 0);
    1287             : 
    1288           0 :     update_change_time(context, 0, data);
    1289             : 
    1290           0 :     *pdata = data;
    1291             : 
    1292           0 :     return 0;
    1293             : }
    1294             : 
    1295             : /* Create a new keyring cache with a unique name. */
    1296             : static krb5_error_code KRB5_CALLCONV
    1297           0 : krcc_gen_new(krb5_context context, krb5_ccache *id)
    1298             : {
    1299           0 :     krb5_error_code ret;
    1300           0 :     char *anchor_name, *collection_name, *subsidiary_name;
    1301           0 :     char *new_subsidiary_name = NULL, *new_residual = NULL;
    1302           0 :     krb5_krcache *data;
    1303           0 :     atomic_key_serial_t collection_id;
    1304           0 :     key_serial_t cache_id = 0;
    1305             : 
    1306             :     /* Determine the collection in which we will create the cache.*/
    1307           0 :     ret = get_default(context, &anchor_name, &collection_name,
    1308             :                       &subsidiary_name);
    1309           0 :     if (ret)
    1310           0 :         return ret;
    1311             : 
    1312           0 :     if (anchor_name == NULL) {
    1313           0 :         ret = parse_residual(context, KRCC_DEFAULT_UNIQUE_COLLECTION, &anchor_name,
    1314             :                              &collection_name, &subsidiary_name);
    1315           0 :         if (ret)
    1316           0 :             return ret;
    1317             :     }
    1318           0 :     if (subsidiary_name != NULL) {
    1319           0 :         krb5_set_error_message(context, KRB5_DCC_CANNOT_CREATE,
    1320           0 :                 N_("Can't create new subsidiary cache because default cache "
    1321             :                    "is already a subsidiary", ""));
    1322           0 :         ret = KRB5_DCC_CANNOT_CREATE;
    1323           0 :         goto cleanup;
    1324             :     }
    1325             : 
    1326             :     /* Make a unique keyring within the chosen collection. */
    1327           0 :     ret = get_collection(context, anchor_name, collection_name, &collection_id);
    1328           0 :     if (ret)
    1329           0 :         goto cleanup;
    1330             : 
    1331           0 :     ret = add_unique_keyring(context, collection_id, &new_subsidiary_name, &cache_id);
    1332           0 :     if (ret)
    1333           0 :         goto cleanup;
    1334             : 
    1335           0 :     ret = alloc_cache(context, collection_id, cache_id,
    1336             :                       anchor_name, collection_name, new_subsidiary_name,
    1337             :                       &data);
    1338           0 :     if (ret)
    1339           0 :         goto cleanup;
    1340             : 
    1341           0 :     (*id)->data.data = data;
    1342           0 :     (*id)->data.length = sizeof(*data);
    1343             : 
    1344           0 : cleanup:
    1345           0 :     free(anchor_name);
    1346           0 :     free(collection_name);
    1347           0 :     free(subsidiary_name);
    1348           0 :     free(new_subsidiary_name);
    1349           0 :     free(new_residual);
    1350             : 
    1351           0 :     return ret;
    1352             : }
    1353             : 
    1354             : /* Return an alias to the residual string of the cache. */
    1355             : static krb5_error_code KRB5_CALLCONV
    1356           0 : krcc_get_name_2(krb5_context context,
    1357             :                 krb5_ccache id,
    1358             :                 const char **name,
    1359             :                 const char **collection_name,
    1360             :                 const char **subsidiary_name)
    1361             : {
    1362           0 :     krb5_krcache *data = KRCACHE(id);
    1363             : 
    1364           0 :     if (data == NULL)
    1365           0 :         return krb5_einval(context, 2);
    1366             : 
    1367           0 :     if (name)
    1368           0 :         *name = data->krc_name;
    1369           0 :     if (collection_name)
    1370           0 :         *collection_name = data->krc_collection;
    1371           0 :     if (subsidiary_name)
    1372           0 :         *subsidiary_name = data->krc_subsidiary;
    1373           0 :     return 0;
    1374             : }
    1375             : 
    1376             : /* Retrieve a copy of the default principal, if the cache is initialized. */
    1377             : static krb5_error_code KRB5_CALLCONV
    1378           0 : krcc_get_principal(krb5_context context,
    1379             :                    krb5_ccache id,
    1380             :                    krb5_principal *princ)
    1381             : {
    1382           0 :     krb5_krcache *data = KRCACHE(id);
    1383           0 :     krb5_error_code ret;
    1384           0 :     krb5_storage *sp = NULL;
    1385           0 :     krb5_data payload;
    1386           0 :     krb5_krcache_and_princ_id ids;
    1387             : 
    1388           0 :     krb5_data_zero(&payload);
    1389           0 :     *princ = NULL;
    1390             : 
    1391           0 :     if (data == NULL)
    1392           0 :         return krb5_einval(context, 2);
    1393             : 
    1394           0 :     memset(&ids, 0, sizeof(ids));
    1395           0 :     ids.krcu_cache_and_princ_id = heim_base_atomic_load(&data->krc_cache_and_principal_id);
    1396           0 :     if (ids.krcu_cache_id == 0 || ids.krcu_princ_id == 0) {
    1397           0 :         ret = KRB5_FCC_NOFILE;
    1398           0 :         krb5_set_error_message(context, ret,
    1399           0 :                                N_("Credentials cache keyring '%s' not found", ""),
    1400             :                                data->krc_name);
    1401           0 :         goto cleanup;
    1402             :     }
    1403             : 
    1404           0 :     ret = keyctl_read_krb5_data(ids.krcu_princ_id, &payload);
    1405           0 :     if (ret) {
    1406           0 :         _krb5_debug(context, 10, "Reading principal key %d: %s\n",
    1407           0 :                     ids.krcu_princ_id, strerror(errno));
    1408           0 :         goto cleanup;
    1409             :     }
    1410             : 
    1411           0 :     sp = krb5_storage_from_data(&payload);
    1412           0 :     if (sp == NULL) {
    1413           0 :         ret = KRB5_CC_IO;
    1414           0 :         goto cleanup;
    1415             :     }
    1416             : 
    1417           0 :     ret = krb5_ret_principal(sp, princ);
    1418           0 :     if (ret)
    1419           0 :         goto cleanup;
    1420             : 
    1421           0 : cleanup:
    1422           0 :     krb5_storage_free(sp);
    1423           0 :     krb5_data_free(&payload);
    1424             : 
    1425           0 :     return ret;
    1426             : }
    1427             : 
    1428             : /* Remove a cred from the cache keyring */
    1429             : static krb5_error_code KRB5_CALLCONV
    1430           0 : krcc_remove_cred(krb5_context context, krb5_ccache id,
    1431             :                  krb5_flags which, krb5_creds *mcred)
    1432             : {
    1433           0 :     krb5_krcache *data = KRCACHE(id);
    1434           0 :     krb5_error_code ret, ret2;
    1435           0 :     krb5_cc_cursor cursor;
    1436           0 :     krb5_creds found_cred;
    1437           0 :     krb5_krcache_and_princ_id ids;
    1438             : 
    1439           0 :     if (data == NULL)
    1440           0 :         return krb5_einval(context, 2);
    1441             : 
    1442           0 :     ret = krcc_get_first(context, id, &cursor);
    1443           0 :     if (ret)
    1444           0 :         return ret;
    1445             : 
    1446           0 :     memset(&ids, 0, sizeof(ids));
    1447           0 :     ids.krcu_cache_and_princ_id = heim_base_atomic_load(&data->krc_cache_and_principal_id);
    1448             : 
    1449           0 :     while ((ret = krcc_get_next(context, id, &cursor, &found_cred)) == 0) {
    1450           0 :         struct krcc_cursor *krcursor = cursor;
    1451             : 
    1452           0 :         if (!krb5_compare_creds(context, which, mcred, &found_cred)) {
    1453           0 :             krb5_free_cred_contents(context, &found_cred);
    1454           0 :             continue;
    1455             :         }
    1456             : 
    1457           0 :         _krb5_debug(context, 10, "Removing cred %d from cache_id %d, princ_id %d\n",
    1458           0 :                     krcursor->keys[krcursor->currkey - 1],
    1459           0 :                     ids.krcu_cache_id, ids.krcu_princ_id);
    1460             : 
    1461           0 :         keyctl_invalidate(krcursor->keys[krcursor->currkey - 1]);
    1462           0 :         krcursor->keys[krcursor->currkey - 1] = 0;
    1463           0 :         krb5_free_cred_contents(context, &found_cred);
    1464             :     }
    1465             : 
    1466           0 :     ret2 = krcc_end_get(context, id, &cursor);
    1467           0 :     if (ret == KRB5_CC_END)
    1468           0 :         ret = ret2;
    1469             : 
    1470           0 :     return ret;
    1471             : }
    1472             : 
    1473             : /* Set flags on the cache.  (We don't care about any flags.) */
    1474             : static krb5_error_code KRB5_CALLCONV
    1475           0 : krcc_set_flags(krb5_context context, krb5_ccache id, krb5_flags flags)
    1476             : {
    1477           0 :     return 0;
    1478             : }
    1479             : 
    1480             : static int KRB5_CALLCONV
    1481           0 : krcc_get_version(krb5_context context, krb5_ccache id)
    1482             : {
    1483           0 :     return 0;
    1484             : }
    1485             :  
    1486             : /* Store a credential in the cache keyring. */
    1487             : static krb5_error_code KRB5_CALLCONV
    1488           0 : krcc_store(krb5_context context, krb5_ccache id, krb5_creds *creds)
    1489             : {
    1490           0 :     krb5_error_code ret;
    1491           0 :     krb5_krcache *data = KRCACHE(id);
    1492           0 :     krb5_storage *sp = NULL;
    1493           0 :     char *keyname = NULL;
    1494           0 :     key_serial_t cred_key, cache_id;
    1495           0 :     krb5_timestamp now;
    1496           0 :     krb5_data payload;
    1497             : 
    1498           0 :     krb5_data_zero(&payload);
    1499             : 
    1500           0 :     if (data == NULL)
    1501           0 :         return krb5_einval(context, 2);
    1502             : 
    1503           0 :     cache_id = heim_base_atomic_load(&data->krc_cache_id);
    1504           0 :     if (cache_id == 0)
    1505           0 :         return KRB5_FCC_NOFILE;
    1506             : 
    1507           0 :     ret = krb5_unparse_name(context, creds->server, &keyname);
    1508           0 :     if (ret)
    1509           0 :         goto cleanup;
    1510             : 
    1511           0 :     sp = krb5_storage_emem();
    1512           0 :     if (sp == NULL) {
    1513           0 :         krb5_set_error_message(context, KRB5_CC_NOMEM, N_("malloc: out of memory", ""));
    1514           0 :         ret = KRB5_CC_NOMEM;
    1515           0 :         goto cleanup;
    1516             :     }
    1517             : 
    1518           0 :     ret = krb5_store_creds(sp, creds);
    1519           0 :     if (ret)
    1520           0 :         goto cleanup;
    1521             : 
    1522           0 :     ret = krb5_storage_to_data(sp, &payload);
    1523           0 :     if (ret)
    1524           0 :         goto cleanup;
    1525             : 
    1526           0 :     _krb5_debug(context, 10, "krcc_store: adding new key '%s' to keyring %d\n",
    1527             :                 keyname, cache_id);
    1528           0 :     ret = add_cred_key(keyname, payload.data, payload.length, cache_id,
    1529             :                        data->krc_is_legacy, &cred_key);
    1530           0 :     if (ret)
    1531           0 :         goto cleanup;
    1532             : 
    1533           0 :     ret = krb5_timeofday(context, &now);
    1534           0 :     if (ret)
    1535           0 :         goto cleanup;
    1536             : 
    1537           0 :     update_change_time(context, now, data);
    1538             : 
    1539             :     /* Set timeout on credential key */
    1540           0 :     if (creds->times.endtime > now)
    1541           0 :         (void) keyctl_set_timeout(cred_key, creds->times.endtime - now);
    1542             : 
    1543             :     /* Set timeout on credential cache keyring */
    1544           0 :     update_keyring_expiration(context, id, cache_id, now);
    1545             : 
    1546           0 : cleanup:
    1547           0 :     krb5_data_free(&payload);
    1548           0 :     krb5_storage_free(sp);
    1549           0 :     krb5_xfree(keyname);
    1550             : 
    1551           0 :     return ret;
    1552             : }
    1553             : 
    1554             : /*
    1555             :  * Get the cache's last modification time.  (This is currently broken; it
    1556             :  * returns only the last change made using this handle.)
    1557             :  */
    1558             : static krb5_error_code KRB5_CALLCONV
    1559           0 : krcc_lastchange(krb5_context context,
    1560             :                 krb5_ccache id,
    1561             :                 krb5_timestamp *change_time)
    1562             : {
    1563           0 :     krb5_krcache *data = KRCACHE(id);
    1564             : 
    1565           0 :     if (data == NULL)
    1566           0 :         return krb5_einval(context, 2);
    1567             : 
    1568           0 :     *change_time = heim_base_atomic_load(&data->krc_changetime);
    1569             : 
    1570           0 :     return 0;
    1571             : }
    1572             : 
    1573             : static krb5_error_code
    1574           0 : save_principal(krb5_context context,
    1575             :                key_serial_t cache_id,
    1576             :                krb5_const_principal princ,
    1577             :                atomic_key_serial_t *pprinc_id)
    1578             : {
    1579           0 :     krb5_error_code ret;
    1580           0 :     krb5_storage *sp;
    1581           0 :     key_serial_t newkey;
    1582           0 :     krb5_data payload;
    1583             : 
    1584           0 :     krb5_data_zero(&payload);
    1585             : 
    1586           0 :     sp = krb5_storage_emem();
    1587           0 :     if (sp == NULL) {
    1588           0 :         krb5_set_error_message(context, KRB5_CC_NOMEM, N_("malloc: out of memory", ""));
    1589           0 :         return KRB5_CC_NOMEM;
    1590             :     }
    1591             : 
    1592           0 :     ret = krb5_store_principal(sp, princ);
    1593           0 :     if (ret) {
    1594           0 :         krb5_storage_free(sp);
    1595           0 :         return ret;
    1596             :     }
    1597             : 
    1598           0 :     ret = krb5_storage_to_data(sp, &payload);
    1599           0 :     if (ret) {
    1600           0 :         krb5_storage_free(sp);
    1601           0 :         return ret;
    1602             :     }
    1603             : 
    1604           0 :     krb5_storage_free(sp);
    1605             :     {
    1606           0 :         krb5_error_code tmp;
    1607           0 :         char *princname = NULL;
    1608             : 
    1609           0 :         tmp = krb5_unparse_name(context, princ, &princname);
    1610           0 :         _krb5_debug(context, 10, "save_principal: adding new key '%s' "
    1611             :                     "to keyring %d for principal '%s'\n",
    1612             :                     KRCC_SPEC_PRINC_KEYNAME, cache_id,
    1613             :                     tmp ? "<unknown>" : princname);
    1614           0 :         if (tmp == 0)
    1615           0 :             krb5_xfree(princname);
    1616             :     }
    1617             : 
    1618             :     /* Add new key into keyring */
    1619           0 :     newkey = add_key(KRCC_KEY_TYPE_USER, KRCC_SPEC_PRINC_KEYNAME,
    1620           0 :                      payload.data, payload.length, cache_id);
    1621           0 :     if (newkey == -1) {
    1622           0 :         ret = errno;
    1623           0 :         _krb5_debug(context, 10, "Error adding principal key: %s\n", strerror(ret));
    1624             :     } else {
    1625           0 :         ret = 0;
    1626           0 :         heim_base_atomic_store(pprinc_id, newkey);
    1627             :     }
    1628             : 
    1629           0 :     krb5_data_free(&payload);
    1630             : 
    1631           0 :     return ret;
    1632             : }
    1633             : 
    1634             : /* Add a key to the cache keyring containing the given time offsets. */
    1635             : static krb5_error_code
    1636           0 : save_time_offsets(krb5_context context,
    1637             :                   key_serial_t cache_id,
    1638             :                   int32_t sec_offset,
    1639             :                   int32_t usec_offset)
    1640             : {
    1641           0 :     krb5_error_code ret;
    1642           0 :     key_serial_t newkey;
    1643           0 :     krb5_storage *sp;
    1644           0 :     krb5_data payload;
    1645             : 
    1646           0 :     krb5_data_zero(&payload);
    1647             : 
    1648           0 :     sp = krb5_storage_emem();
    1649           0 :     if (sp == NULL) {
    1650           0 :         krb5_set_error_message(context, KRB5_CC_NOMEM, N_("malloc: out of memory", ""));
    1651           0 :         return KRB5_CC_NOMEM;
    1652             :     }
    1653             : 
    1654           0 :     krb5_storage_set_byteorder(sp, KRB5_STORAGE_BYTEORDER_BE);
    1655             : 
    1656           0 :     ret = krb5_store_int32(sp, sec_offset);
    1657           0 :     if (ret == 0)
    1658           0 :         ret = krb5_store_int32(sp, usec_offset);
    1659           0 :     if (ret) {
    1660           0 :         krb5_storage_free(sp);
    1661           0 :         return ret;
    1662             :     }
    1663             : 
    1664           0 :     ret = krb5_storage_to_data(sp, &payload);
    1665           0 :     if (ret) {
    1666           0 :         krb5_storage_free(sp);
    1667           0 :         return ret;
    1668             :     }
    1669             : 
    1670           0 :     krb5_storage_free(sp);
    1671             : 
    1672           0 :     newkey = add_key(KRCC_KEY_TYPE_USER, KRCC_TIME_OFFSETS, payload.data,
    1673             :                      payload.length, cache_id);
    1674           0 :     ret = newkey == -1 ? errno : 0;
    1675             : 
    1676           0 :     krb5_data_free(&payload);
    1677             : 
    1678           0 :     return ret;
    1679             : }
    1680             : 
    1681             : static krb5_error_code KRB5_CALLCONV
    1682           0 : krcc_set_kdc_offset(krb5_context context, krb5_ccache id, krb5_deltat offset)
    1683             : {
    1684           0 :     krb5_krcache *data = KRCACHE(id);
    1685           0 :     key_serial_t cache_id;
    1686           0 :     krb5_error_code ret;
    1687             : 
    1688           0 :     if (data == NULL)
    1689           0 :         return krb5_einval(context, 2);
    1690             : 
    1691           0 :     cache_id = heim_base_atomic_load(&data->krc_cache_id);
    1692             :  
    1693           0 :     ret = save_time_offsets(context, cache_id, (int32_t)offset, 0);
    1694           0 :     if (ret == 0)
    1695           0 :         update_change_time(context, 0, data);
    1696             : 
    1697           0 :     return ret;
    1698             : }
    1699             : 
    1700             : /* Retrieve and parse the key in the cache keyring containing time offsets. */
    1701             : static krb5_error_code KRB5_CALLCONV
    1702           0 : krcc_get_kdc_offset(krb5_context context,
    1703             :                     krb5_ccache id,
    1704             :                     krb5_deltat *offset)
    1705             : {
    1706           0 :     krb5_krcache *data = KRCACHE(id);
    1707           0 :     krb5_error_code ret = 0;
    1708           0 :     key_serial_t key, cache_id;
    1709           0 :     krb5_storage *sp = NULL;
    1710           0 :     krb5_data payload;
    1711           0 :     int32_t sec_offset = 0;
    1712             : 
    1713           0 :     if (data == NULL)
    1714           0 :         return krb5_einval(context, 2);
    1715             : 
    1716           0 :     krb5_data_zero(&payload);
    1717             : 
    1718           0 :     cache_id = heim_base_atomic_load(&data->krc_cache_id);
    1719           0 :     if (cache_id == 0) {
    1720           0 :         ret = KRB5_FCC_NOFILE;
    1721           0 :         goto cleanup;
    1722             :     }
    1723             : 
    1724           0 :     key = keyctl_search(cache_id, KRCC_KEY_TYPE_USER, KRCC_TIME_OFFSETS, 0);
    1725           0 :     if (key == -1) {
    1726           0 :         ret = ENOENT;
    1727           0 :         goto cleanup;
    1728             :     }
    1729             : 
    1730           0 :     ret = keyctl_read_krb5_data(key, &payload);
    1731           0 :     if (ret) {
    1732           0 :         _krb5_debug(context, 10, "Reading time offsets key %d: %s\n",
    1733           0 :                     key, strerror(errno));
    1734           0 :         goto cleanup;
    1735             :     }
    1736             : 
    1737           0 :     sp = krb5_storage_from_data(&payload);
    1738           0 :     if (sp == NULL) {
    1739           0 :         ret = krb5_enomem(context);;
    1740           0 :         goto cleanup;
    1741             :     }
    1742             : 
    1743           0 :     krb5_storage_set_byteorder(sp, KRB5_STORAGE_BYTEORDER_BE);
    1744             : 
    1745           0 :     ret = krb5_ret_int32(sp, &sec_offset);
    1746             :     /*
    1747             :      * We can't output nor use the usec_offset here, so we don't bother to read
    1748             :      * it, though we do write it.
    1749             :      */
    1750             : 
    1751           0 : cleanup:
    1752           0 :     *offset = sec_offset;
    1753           0 :     krb5_storage_free(sp);
    1754           0 :     krb5_data_free(&payload);
    1755           0 :     return ret;
    1756             : }
    1757             : 
    1758             : struct krcc_iter {
    1759             :     atomic_key_serial_t collection_id;
    1760             :     char *anchor_name;
    1761             :     char *collection_name;
    1762             :     char *subsidiary_name;
    1763             :     char *primary_name;
    1764             :     krb5_boolean first;
    1765             :     long num_keys;
    1766             :     long next_key;
    1767             :     key_serial_t *keys;
    1768             : };
    1769             : 
    1770             : static krb5_error_code KRB5_CALLCONV
    1771          52 : krcc_get_cache_first(krb5_context context, krb5_cc_cursor *cursor)
    1772             : {
    1773           0 :     struct krcc_iter *iter;
    1774           0 :     krb5_error_code ret;
    1775           0 :     void *keys;
    1776           0 :     long size;
    1777             : 
    1778          52 :     *cursor = NULL;
    1779             : 
    1780          52 :     iter = calloc(1, sizeof(*iter));
    1781          52 :     if (iter == NULL) {
    1782           0 :         ret = krb5_enomem(context);
    1783           0 :         goto error;
    1784             :     }
    1785          52 :     iter->first = TRUE;
    1786             : 
    1787          52 :     ret = get_default(context, &iter->anchor_name, &iter->collection_name,
    1788             :                       &iter->subsidiary_name);
    1789          52 :     if (ret)
    1790           0 :         goto error;
    1791             : 
    1792             :     /* If there is no default collection, return an empty cursor. */
    1793          52 :     if (iter->anchor_name == NULL) {
    1794          52 :         *cursor = iter;
    1795          52 :         return 0;
    1796             :     }
    1797             : 
    1798           0 :     ret = get_collection(context, iter->anchor_name, iter->collection_name,
    1799             :                          &iter->collection_id);
    1800           0 :     if (ret)
    1801           0 :         goto error;
    1802             : 
    1803           0 :     if (iter->subsidiary_name == NULL) {
    1804           0 :         ret = get_primary_name(context, iter->anchor_name,
    1805           0 :                                iter->collection_name, iter->collection_id,
    1806             :                                &iter->primary_name);
    1807           0 :         if (ret)
    1808           0 :             goto error;
    1809             : 
    1810           0 :         size = keyctl_read_alloc(iter->collection_id, &keys);
    1811           0 :         if (size == -1) {
    1812           0 :             ret = errno;
    1813           0 :             goto error;
    1814             :         }
    1815           0 :         iter->keys = keys;
    1816           0 :         iter->num_keys = size / sizeof(key_serial_t);
    1817             :     }
    1818             : 
    1819           0 :     *cursor = iter;
    1820             : 
    1821           0 :     return 0;
    1822             : 
    1823           0 : error:
    1824           0 :     krcc_end_cache_get(context, iter);
    1825             : 
    1826           0 :     return ret;
    1827             : }
    1828             : 
    1829             : static krb5_error_code KRB5_CALLCONV
    1830          52 : krcc_get_cache_next(krb5_context context,
    1831             :                     krb5_cc_cursor cursor,
    1832             :                     krb5_ccache *cache)
    1833             : {
    1834           0 :     krb5_error_code ret;
    1835          52 :     struct krcc_iter *iter = cursor;
    1836          52 :     key_serial_t key, cache_id = 0;
    1837           0 :     const char *first_name, *keytype, *sep, *subsidiary_name;
    1838           0 :     size_t keytypelen;
    1839          52 :     char *description = NULL;
    1840             : 
    1841          52 :     *cache = NULL;
    1842             : 
    1843             :     /* No keyring available */
    1844          52 :     if (iter->collection_id == 0)
    1845          52 :         return KRB5_CC_END;
    1846             : 
    1847           0 :     if (iter->first) {
    1848             :         /*
    1849             :          * Look for the primary cache for a collection cursor, or the
    1850             :          * subsidiary cache for a subsidiary cursor.
    1851             :          */
    1852           0 :         iter->first = FALSE;
    1853           0 :         first_name = (iter->primary_name != NULL) ? iter->primary_name :
    1854             :                      iter->subsidiary_name;
    1855           0 :         cache_id = keyctl_search(iter->collection_id, KRCC_KEY_TYPE_KEYRING,
    1856             :                                  first_name, 0);
    1857           0 :         if (cache_id != -1) {
    1858           0 :             return make_cache(context, iter->collection_id, cache_id,
    1859           0 :                               iter->anchor_name, iter->collection_name,
    1860             :                               first_name, cache);
    1861             :         }
    1862             :     }
    1863             : 
    1864             :     /* A subsidiary cursor yields at most the first cache. */
    1865           0 :     if (iter->subsidiary_name != NULL)
    1866           0 :         return KRB5_CC_END;
    1867             : 
    1868           0 :     keytype = KRCC_KEY_TYPE_KEYRING ";";
    1869           0 :     keytypelen = strlen(keytype);
    1870             : 
    1871           0 :     for (ret = KRB5_CC_END; iter->next_key < iter->num_keys; iter->next_key++) {
    1872           0 :         free(description);
    1873           0 :         description = NULL;
    1874             : 
    1875             :         /*
    1876             :          * Get the key description, which should have the form:
    1877             :          *   typename;UID;GID;permissions;description
    1878             :          */
    1879           0 :         key = iter->keys[iter->next_key];
    1880           0 :         if (keyctl_describe_alloc(key, &description) < 0)
    1881           0 :             continue;
    1882           0 :         sep = strrchr(description, ';');
    1883           0 :         if (sep == NULL)
    1884           0 :             continue;
    1885           0 :         subsidiary_name = sep + 1;
    1886             : 
    1887             :         /* Skip this key if it isn't a keyring. */
    1888           0 :         if (strncmp(description, keytype, keytypelen) != 0)
    1889           0 :             continue;
    1890             : 
    1891             :         /* Don't repeat the primary cache. */
    1892           0 :         if (iter->primary_name &&
    1893           0 :             strcmp(subsidiary_name, iter->primary_name) == 0)
    1894           0 :             continue;
    1895             : 
    1896             :         /* We found a valid key */
    1897           0 :         iter->next_key++;
    1898           0 :         ret = make_cache(context, iter->collection_id, key, iter->anchor_name,
    1899           0 :                          iter->collection_name, subsidiary_name, cache);
    1900           0 :         break;
    1901             :     }
    1902             : 
    1903           0 :     free(description);
    1904             : 
    1905           0 :     return ret;
    1906             : }
    1907             : 
    1908             : static krb5_error_code KRB5_CALLCONV
    1909          52 : krcc_end_cache_get(krb5_context context, krb5_cc_cursor cursor)
    1910             : {
    1911          52 :     struct krcc_iter *iter = cursor;
    1912             : 
    1913          52 :     if (iter != NULL) {
    1914          52 :         free(iter->anchor_name);
    1915          52 :         free(iter->collection_name);
    1916          52 :         free(iter->subsidiary_name);
    1917          52 :         free(iter->primary_name);
    1918          52 :         free(iter->keys);
    1919             : 
    1920          52 :         memset(iter, 0, sizeof(*iter));
    1921          52 :         free(iter);
    1922             :     }
    1923             : 
    1924          52 :     return 0;
    1925             : }
    1926             : 
    1927             : static krb5_error_code KRB5_CALLCONV
    1928           0 : krcc_set_default(krb5_context context, krb5_ccache id)
    1929             : {
    1930           0 :     krb5_krcache *data = KRCACHE(id);
    1931           0 :     krb5_error_code ret;
    1932           0 :     char *anchor_name, *collection_name, *subsidiary_name;
    1933           0 :     atomic_key_serial_t collection_id;
    1934             : 
    1935           0 :     if (data == NULL)
    1936           0 :         return krb5_einval(context, 2);
    1937             : 
    1938           0 :     ret = parse_residual(context, data->krc_name,
    1939             :                          &anchor_name, &collection_name, &subsidiary_name);
    1940           0 :     if (ret)
    1941           0 :         goto cleanup;
    1942             : 
    1943           0 :     ret = get_collection(context, anchor_name, collection_name, &collection_id);
    1944           0 :     if (ret)
    1945           0 :         goto cleanup;
    1946             : 
    1947           0 :     ret = set_primary_name(context, collection_id, subsidiary_name);
    1948           0 :     if (ret)
    1949           0 :         goto cleanup;
    1950             : 
    1951           0 : cleanup:
    1952           0 :     free(anchor_name);
    1953           0 :     free(collection_name);
    1954           0 :     free(subsidiary_name);
    1955             : 
    1956           0 :     return ret;
    1957             : }
    1958             : 
    1959             : /*
    1960             :  * Utility routine: called by krcc_* functions to keep
    1961             :  * result of krcc_last_change_time up to date.
    1962             :  */
    1963             : static void
    1964           0 : update_change_time(krb5_context context, krb5_timestamp now, krb5_krcache *data)
    1965             : {
    1966           0 :     krb5_timestamp old;
    1967             : 
    1968           0 :     if (now == 0)
    1969           0 :         krb5_timeofday(context, &now);
    1970             : 
    1971           0 :     old = heim_base_exchange_time_t(&data->krc_changetime, now);
    1972           0 :     if (old > now) /* don't go backwards */
    1973           0 :         heim_base_atomic_store(&data->krc_changetime, old + 1);
    1974           0 : }
    1975             : 
    1976             : static int
    1977           0 : move_key_to_new_keyring(key_serial_t parent, key_serial_t key,
    1978             :                         char *desc, int desc_len, void *data)
    1979             : {
    1980           0 :     key_serial_t cache_id = *(key_serial_t *)data;
    1981             : 
    1982           0 :     if (parent) {
    1983           0 :         if (keyctl_link(key, cache_id) == -1 ||
    1984           0 :             keyctl_unlink(key, parent) == -1)
    1985           0 :             return -1;
    1986             :     }
    1987             : 
    1988           0 :     return 0;
    1989             : }
    1990             : 
    1991             : /* Move contents of one ccache to another; destroys from cache */
    1992             : static krb5_error_code KRB5_CALLCONV
    1993           0 : krcc_move(krb5_context context, krb5_ccache from, krb5_ccache to)
    1994             : {
    1995           0 :     krb5_krcache *krfrom = KRCACHE(from);
    1996           0 :     krb5_krcache *krto = KRCACHE(to);
    1997           0 :     krb5_error_code ret;
    1998           0 :     krb5_timestamp now;
    1999           0 :     key_serial_t to_cache_id;
    2000             : 
    2001           0 :     if (krfrom == NULL || krto == NULL)
    2002           0 :         return krb5_einval(context, 2);
    2003             : 
    2004           0 :     ret = initialize_internal(context, to, NULL);
    2005           0 :     if (ret)
    2006           0 :         return ret;
    2007             : 
    2008           0 :     krb5_timeofday(context, &now);
    2009           0 :     to_cache_id = heim_base_atomic_load(&krto->krc_cache_id);
    2010             : 
    2011           0 :     if (krfrom->krc_cache_id != 0) {
    2012           0 :         ret = recursive_key_scan(krfrom->krc_cache_id,
    2013             :                                  move_key_to_new_keyring, &to_cache_id);
    2014           0 :         if (ret)
    2015           0 :             return KRB5_CC_IO;
    2016             : 
    2017           0 :         if (keyctl_unlink(krfrom->krc_cache_id, krfrom->krc_coll_id) == -1)
    2018           0 :             return errno;
    2019             : 
    2020           0 :         heim_base_exchange_32(&krto->krc_princ_id, krfrom->krc_princ_id);
    2021             :     }
    2022             : 
    2023           0 :     update_change_time(context, now, krto);
    2024           0 :     krb5_cc_destroy(context, from);
    2025           0 :     return 0;
    2026             : }
    2027             : 
    2028             : static krb5_error_code KRB5_CALLCONV
    2029           0 : krcc_get_default_name(krb5_context context, char **str)
    2030             : {
    2031           0 :     *str = strdup("KEYRING:");
    2032           0 :     if (*str == NULL)
    2033           0 :         return krb5_enomem(context);
    2034             : 
    2035           0 :     return 0;
    2036             : }
    2037             : 
    2038             : /*
    2039             :  * ccache implementation storing credentials in the Linux keyring facility
    2040             :  * The default is to put them at the session keyring level.
    2041             :  * If "KEYRING:process:" or "KEYRING:thread:" is specified, then they will
    2042             :  * be stored at the process or thread level respectively.
    2043             :  */
    2044             : KRB5_LIB_VARIABLE const krb5_cc_ops krb5_krcc_ops = {
    2045             :     KRB5_CC_OPS_VERSION_5,
    2046             :     "KEYRING",
    2047             :     NULL,
    2048             :     NULL,
    2049             :     krcc_gen_new,
    2050             :     krcc_initialize,
    2051             :     krcc_destroy,
    2052             :     krcc_close,
    2053             :     krcc_store,
    2054             :     NULL,                   /* retrieve */
    2055             :     krcc_get_principal,
    2056             :     krcc_get_first,
    2057             :     krcc_get_next,
    2058             :     krcc_end_get,
    2059             :     krcc_remove_cred,
    2060             :     krcc_set_flags,
    2061             :     krcc_get_version,
    2062             :     krcc_get_cache_first,
    2063             :     krcc_get_cache_next,
    2064             :     krcc_end_cache_get,
    2065             :     krcc_move,
    2066             :     krcc_get_default_name,
    2067             :     krcc_set_default,
    2068             :     krcc_lastchange,
    2069             :     krcc_set_kdc_offset,
    2070             :     krcc_get_kdc_offset,
    2071             :     krcc_get_name_2,
    2072             :     krcc_resolve_2
    2073             : };
    2074             : 
    2075             : #endif /* HAVE_KEYUTILS_H */

Generated by: LCOV version 1.14