1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /* lib/krb5/ccache/cc_keyring.c */
5 * The Regents of the University of Michigan
8 * Permission is granted to use, copy, create derivative works
9 * and redistribute this software and such derivative works
10 * for any purpose, so long as the name of The University of
11 * Michigan is not used in any advertising or publicity
12 * pertaining to the use of distribution of this software
13 * without specific, written prior authorization. If the
14 * above copyright notice or any other identification of the
15 * University of Michigan is included in any copy of any
16 * portion of this software, then the disclaimer below must
19 * THIS SOFTWARE IS PROVIDED AS IS, WITHOUT REPRESENTATION
20 * FROM THE UNIVERSITY OF MICHIGAN AS TO ITS FITNESS FOR ANY
21 * PURPOSE, AND WITHOUT WARRANTY BY THE UNIVERSITY OF
22 * MICHIGAN OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING
23 * WITHOUT LIMITATION THE IMPLIED WARRANTIES OF
24 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE
25 * REGENTS OF THE UNIVERSITY OF MICHIGAN SHALL NOT BE LIABLE
26 * FOR ANY DAMAGES, INCLUDING SPECIAL, INDIRECT, INCIDENTAL, OR
27 * CONSEQUENTIAL DAMAGES, WITH RESPECT TO ANY CLAIM ARISING
28 * OUT OF OR IN CONNECTION WITH THE USE OF THE SOFTWARE, EVEN
29 * IF IT HAS BEEN OR IS HEREAFTER ADVISED OF THE POSSIBILITY OF
33 * Copyright 1990,1991,1992,1993,1994,2000,2004 Massachusetts Institute of
34 * Technology. All Rights Reserved.
36 * Original stdio support copyright 1995 by Cygnus Support.
38 * Export of this software from the United States of America may
39 * require a specific license from the United States Government.
40 * It is the responsibility of any person or organization contemplating
41 * export to obtain such a license before exporting.
43 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
44 * distribute this software and its documentation for any purpose and
45 * without fee is hereby granted, provided that the above copyright
46 * notice appear in all copies and that both that copyright notice and
47 * this permission notice appear in supporting documentation, and that
48 * the name of M.I.T. not be used in advertising or publicity pertaining
49 * to distribution of the software without specific, written prior
50 * permission. Furthermore if you modify this software you must label
51 * your software as modified software and not distribute it in such a
52 * fashion that it might be confused with the original M.I.T. software.
53 * M.I.T. makes no representations about the suitability of
54 * this software for any purpose. It is provided "as is" without express
55 * or implied warranty.
59 * This file implements a collection-enabled credential cache type where the
60 * credentials are stored in the Linux keyring facility.
62 * A residual of this type can have three forms:
63 * anchor:collection:subsidiary
67 * The anchor name is "process", "thread", or "legacy" and determines where we
68 * search for keyring collections. In the third form, the anchor name is
69 * presumed to be "legacy". The anchor keyring for legacy caches is the
72 * If the subsidiary name is present, the residual identifies a single cache
73 * within a collection. Otherwise, the residual identifies the collection
74 * itself. When a residual identifying a collection is resolved, the
75 * collection's primary key is looked up (or initialized, using the collection
76 * name as the subsidiary name), and the resulting cache's name will use the
77 * first name form and will identify the primary cache.
79 * Keyring collections are named "_krb_<collection>" and are linked from the
80 * anchor keyring. The keys within a keyring collection are links to cache
81 * keyrings, plus a link to one user key named "krb_ccache:primary" which
82 * contains a serialized representation of the collection version (currently 1)
83 * and the primary name of the collection.
85 * Cache keyrings contain one user key per credential which contains a
86 * serialized representation of the credential. There is also one user key
87 * named "__krb5_princ__" which contains a serialized representation of the
88 * cache's default principal.
90 * If the anchor name is "legacy", then the initial primary cache (the one
91 * named with the collection name) is also linked to the session keyring, and
92 * we look for a cache in that location when initializing the collection. This
93 * extra link allows that cache to be visible to old versions of the KEYRING
94 * cache type, and allows us to see caches created by that code.
99 #ifdef USE_KEYRING_CCACHE
102 #include <keyutils.h>
109 void debug_print(char *fmt, ...); /* prototype to silence warning */
111 #define DEBUG_PRINT(x) debug_print x
113 debug_print(char *fmt, ...)
118 vfprintf(stderr, fmt, ap);
120 vsyslog(LOG_ERR, fmt, ap);
125 #define DEBUG_PRINT(x)
129 * We try to use the big_key key type for credentials except in legacy caches.
130 * We fall back to the user key type if the kernel does not support big_key.
131 * If the library doesn't support keyctl_get_persistent(), we don't even try
132 * big_key since the two features were added at the same time.
134 #ifdef HAVE_PERSISTENT_KEYRING
135 #define KRCC_CRED_KEY_TYPE "big_key"
137 #define KRCC_CRED_KEY_TYPE "user"
141 * We use the "user" key type for collection primary names, for cache principal
142 * names, and for credentials in legacy caches.
144 #define KRCC_KEY_TYPE_USER "user"
147 * We create ccaches as separate keyrings
149 #define KRCC_KEY_TYPE_KEYRING "keyring"
152 * Special name of the key within a ccache keyring
153 * holding principal information
155 #define KRCC_SPEC_PRINC_KEYNAME "__krb5_princ__"
158 * Special name for the key to communicate the name(s)
159 * of credentials caches to be used for requests.
160 * This should currently contain a single name, but
161 * in the future may contain a list that may be
162 * intelligently chosen from.
164 #define KRCC_SPEC_CCACHE_SET_KEYNAME "__krb5_cc_set__"
167 * This name identifies the key containing the name of the current primary
168 * cache within a collection.
170 #define KRCC_COLLECTION_PRIMARY "krb_ccache:primary"
173 * If the library context does not specify a keyring collection, unique ccaches
174 * will be created within this collection.
176 #define KRCC_DEFAULT_UNIQUE_COLLECTION "session:__krb5_unique__"
179 * Collection keyring names begin with this prefix. We use a prefix so that a
180 * cache keyring with the collection name itself can be linked directly into
181 * the anchor, for legacy session keyring compatibility.
183 #define KRCC_CCCOL_PREFIX "_krb_"
186 * For the "persistent" anchor type, we look up or create this fixed keyring
187 * name within the per-UID persistent keyring.
189 #define KRCC_PERSISTENT_KEYRING_NAME "_krb"
192 * Name of the key holding time offsets for the individual cache
194 #define KRCC_TIME_OFFSETS "__krb5_time_offsets__"
197 * Keyring name prefix and length of random name part
199 #define KRCC_NAME_PREFIX "krb_ccache_"
200 #define KRCC_NAME_RAND_CHARS 8
202 #define KRCC_COLLECTION_VERSION 1
204 #define KRCC_PERSISTENT_ANCHOR "persistent"
205 #define KRCC_PROCESS_ANCHOR "process"
206 #define KRCC_THREAD_ANCHOR "thread"
207 #define KRCC_SESSION_ANCHOR "session"
208 #define KRCC_USER_ANCHOR "user"
209 #define KRCC_LEGACY_ANCHOR "legacy"
211 typedef struct _krcc_cursor
215 key_serial_t princ_id;
216 key_serial_t offsets_id;
221 * This represents a credentials cache "file"
222 * where cache_id is the keyring serial number for
223 * this credentials cache "file". Each key
224 * in the keyring contains a separate key.
226 typedef struct _krcc_data
228 char *name; /* Name for this credentials cache */
229 k5_cc_mutex lock; /* synchronization */
230 key_serial_t collection_id; /* collection containing this cache keyring */
231 key_serial_t cache_id; /* keyring representing ccache */
232 key_serial_t princ_id; /* key holding principal info */
233 krb5_timestamp changetime;
234 krb5_boolean is_legacy_type;
238 k5_cc_mutex krb5int_krcc_mutex = K5_CC_MUTEX_PARTIAL_INITIALIZER;
240 extern const krb5_cc_ops krb5_krcc_ops;
242 static const char *KRB5_CALLCONV
243 krcc_get_name(krb5_context context, krb5_ccache id);
245 static krb5_error_code KRB5_CALLCONV
246 krcc_start_seq_get(krb5_context, krb5_ccache id, krb5_cc_cursor *cursor);
248 static krb5_error_code KRB5_CALLCONV
249 krcc_next_cred(krb5_context context, krb5_ccache id, krb5_cc_cursor *cursor,
252 static krb5_error_code KRB5_CALLCONV
253 krcc_end_seq_get(krb5_context context, krb5_ccache id, krb5_cc_cursor *cursor);
255 static krb5_error_code KRB5_CALLCONV
256 krcc_ptcursor_free(krb5_context context, krb5_cc_ptcursor *cursor);
258 static krb5_error_code clear_cache_keyring(krb5_context context,
261 static krb5_error_code make_krcc_data(const char *anchor_name,
262 const char *collection_name,
263 const char *subsidiary_name,
264 key_serial_t cache_id, key_serial_t
265 collection_id, krcc_data **datapp);
267 static krb5_error_code save_principal(krb5_context context, krb5_ccache id,
268 krb5_principal princ);
270 static krb5_error_code save_time_offsets(krb5_context context, krb5_ccache id,
272 int32_t usec_offset);
274 static krb5_error_code get_time_offsets(krb5_context context, krb5_ccache id,
275 int32_t *time_offset,
276 int32_t *usec_offset);
278 static void krcc_update_change_time(krcc_data *d);
280 /* Note the following is a stub function for Linux */
281 extern krb5_error_code krb5_change_cache(void);
284 * GET_PERSISTENT(uid) acquires the persistent keyring for uid, or falls back
285 * to the user keyring if uid matches the current effective uid.
289 get_persistent_fallback(uid_t uid)
291 return (uid == geteuid()) ? KEY_SPEC_USER_KEYRING : -1;
294 #ifdef HAVE_PERSISTENT_KEYRING
295 #define GET_PERSISTENT get_persistent_real
297 get_persistent_real(uid_t uid)
301 key = keyctl_get_persistent(uid, KEY_SPEC_PROCESS_KEYRING);
302 return (key == -1 && errno == ENOTSUP) ? get_persistent_fallback(uid) :
306 #define GET_PERSISTENT get_persistent_fallback
310 * If a process has no explicitly set session keyring, KEY_SPEC_SESSION_KEYRING
311 * will resolve to the user session keyring for ID lookup and reading, but in
312 * some kernel versions, writing to that special keyring will instead create a
313 * new empty session keyring for the process. We do not want that; the keys we
314 * create would be invisible to other processes. We can work around that
315 * behavior by explicitly writing to the user session keyring when it matches
316 * the session keyring. This function returns the keyring we should write to
317 * for the session anchor.
320 session_write_anchor()
324 s = keyctl_get_keyring_ID(KEY_SPEC_SESSION_KEYRING, 0);
325 u = keyctl_get_keyring_ID(KEY_SPEC_USER_SESSION_KEYRING, 0);
326 return (s == u) ? KEY_SPEC_USER_SESSION_KEYRING : KEY_SPEC_SESSION_KEYRING;
330 * Find or create a keyring within parent with the given name. If possess is
331 * nonzero, also make sure the key is linked from possess. This is necessary
332 * to ensure that we have possession rights on the key when the parent is the
333 * user or persistent keyring.
335 static krb5_error_code
336 find_or_create_keyring(key_serial_t parent, key_serial_t possess,
337 const char *name, key_serial_t *key_out)
342 key = keyctl_search(parent, KRCC_KEY_TYPE_KEYRING, name, possess);
345 key = add_key(KRCC_KEY_TYPE_KEYRING, name, NULL, 0, possess);
348 if (keyctl_link(key, parent) == -1)
351 key = add_key(KRCC_KEY_TYPE_KEYRING, name, NULL, 0, parent);
360 /* Parse a residual name into an anchor name, a collection name, and possibly a
361 * subsidiary name. */
362 static krb5_error_code
363 parse_residual(const char *residual, char **anchor_name_out,
364 char **collection_name_out, char **subsidiary_name_out)
367 char *anchor_name = NULL, *collection_name = NULL, *subsidiary_name = NULL;
370 *anchor_name_out = 0;
371 *collection_name_out = NULL;
372 *subsidiary_name_out = NULL;
374 /* Parse out the anchor name. Use the legacy anchor if not present. */
375 sep = strchr(residual, ':');
377 anchor_name = strdup(KRCC_LEGACY_ANCHOR);
378 if (anchor_name == NULL)
381 anchor_name = k5memdup0(residual, sep - residual, &ret);
382 if (anchor_name == NULL)
387 /* Parse out the collection and subsidiary name. */
388 sep = strchr(residual, ':');
390 collection_name = strdup(residual);
391 if (collection_name == NULL)
393 subsidiary_name = NULL;
395 collection_name = k5memdup0(residual, sep - residual, &ret);
396 if (collection_name == NULL)
398 subsidiary_name = strdup(sep + 1);
399 if (subsidiary_name == NULL)
403 *anchor_name_out = anchor_name;
404 *collection_name_out = collection_name;
405 *subsidiary_name_out = subsidiary_name;
410 free(collection_name);
411 free(subsidiary_name);
416 * Return true if residual identifies a subsidiary cache which should be linked
417 * into the anchor so it can be visible to old code. This is the case if the
418 * residual has the legacy anchor and the subsidiary name matches the
422 is_legacy_cache_name(const char *residual)
424 const char *sep, *aname, *cname, *sname;
425 size_t alen, clen, legacy_len = sizeof(KRCC_LEGACY_ANCHOR) - 1;
427 /* Get pointers to the anchor, collection, and subsidiary names. */
429 sep = strchr(residual, ':');
434 sep = strchr(cname, ':');
440 return alen == legacy_len && clen == strlen(sname) &&
441 strncmp(aname, KRCC_LEGACY_ANCHOR, alen) == 0 &&
442 strncmp(cname, sname, clen) == 0;
445 /* If the default cache name for context is a KEYRING cache, parse its residual
446 * string. Otherwise set all outputs to NULL. */
447 static krb5_error_code
448 get_default(krb5_context context, char **anchor_name_out,
449 char **collection_name_out, char **subsidiary_name_out)
453 *anchor_name_out = *collection_name_out = *subsidiary_name_out = NULL;
454 defname = krb5_cc_default_name(context);
455 if (defname == NULL || strncmp(defname, "KEYRING:", 8) != 0)
457 return parse_residual(defname + 8, anchor_name_out, collection_name_out,
458 subsidiary_name_out);
461 /* Create a residual identifying a subsidiary cache. */
462 static krb5_error_code
463 make_subsidiary_residual(const char *anchor_name, const char *collection_name,
464 const char *subsidiary_name, char **residual_out)
466 if (asprintf(residual_out, "%s:%s:%s", anchor_name, collection_name,
467 subsidiary_name) < 0) {
468 *residual_out = NULL;
474 /* Retrieve or create a keyring for collection_name within the anchor, and set
475 * *collection_id_out to its serial number. */
476 static krb5_error_code
477 get_collection(const char *anchor_name, const char *collection_name,
478 key_serial_t *collection_id_out)
481 key_serial_t persistent_id, anchor_id, possess_id = 0;
482 char *ckname, *cnend;
485 *collection_id_out = 0;
487 if (strcmp(anchor_name, KRCC_PERSISTENT_ANCHOR) == 0) {
489 * The collection name is a uid (or empty for the current effective
490 * uid), and we look up a fixed keyring name within the persistent
491 * keyring for that uid. We link it to the process keyring to ensure
492 * that we have possession rights on the collection key.
494 if (*collection_name != '\0') {
496 uidnum = strtol(collection_name, &cnend, 10);
497 if (errno || *cnend != '\0')
498 return KRB5_KCC_INVALID_UID;
502 persistent_id = GET_PERSISTENT(uidnum);
503 if (persistent_id == -1)
504 return KRB5_KCC_INVALID_UID;
505 return find_or_create_keyring(persistent_id, KEY_SPEC_PROCESS_KEYRING,
506 KRCC_PERSISTENT_KEYRING_NAME,
510 if (strcmp(anchor_name, KRCC_PROCESS_ANCHOR) == 0) {
511 anchor_id = KEY_SPEC_PROCESS_KEYRING;
512 } else if (strcmp(anchor_name, KRCC_THREAD_ANCHOR) == 0) {
513 anchor_id = KEY_SPEC_THREAD_KEYRING;
514 } else if (strcmp(anchor_name, KRCC_SESSION_ANCHOR) == 0) {
515 anchor_id = session_write_anchor();
516 } else if (strcmp(anchor_name, KRCC_USER_ANCHOR) == 0) {
517 /* The user keyring does not confer possession, so we need to link the
518 * collection to the process keyring to maintain possession rights. */
519 anchor_id = KEY_SPEC_USER_KEYRING;
520 possess_id = KEY_SPEC_PROCESS_KEYRING;
521 } else if (strcmp(anchor_name, KRCC_LEGACY_ANCHOR) == 0) {
522 anchor_id = session_write_anchor();
524 return KRB5_KCC_INVALID_ANCHOR;
527 /* Look up the collection keyring name within the anchor keyring. */
528 if (asprintf(&ckname, "%s%s", KRCC_CCCOL_PREFIX, collection_name) == -1)
530 ret = find_or_create_keyring(anchor_id, possess_id, ckname,
536 /* Store subsidiary_name into the primary index key for collection_id. */
537 static krb5_error_code
538 set_primary_name(krb5_context context, key_serial_t collection_id,
539 const char *subsidiary_name)
542 uint32_t len = strlen(subsidiary_name), plen = 8 + len;
543 unsigned char *payload;
545 payload = malloc(plen);
548 store_32_be(KRCC_COLLECTION_VERSION, payload);
549 store_32_be(len, payload + 4);
550 memcpy(payload + 8, subsidiary_name, len);
551 key = add_key(KRCC_KEY_TYPE_USER, KRCC_COLLECTION_PRIMARY,
552 payload, plen, collection_id);
554 return (key == -1) ? errno : 0;
557 static krb5_error_code
558 parse_index(krb5_context context, int32_t *version, char **primary,
559 const unsigned char *payload, size_t psize)
567 *version = load_32_be(payload);
568 len = load_32_be(payload + 4);
571 *primary = k5memdup0(payload + 8, len, &ret);
572 return (*primary == NULL) ? ret : 0;
576 * Get or initialize the primary name within collection_id and set
577 * *subsidiary_out to its value. If initializing a legacy collection, look
578 * for a legacy cache and add it to the collection.
580 static krb5_error_code
581 get_primary_name(krb5_context context, const char *anchor_name,
582 const char *collection_name, key_serial_t collection_id,
583 char **subsidiary_out)
586 key_serial_t primary_id, legacy;
587 void *payload = NULL;
590 char *subsidiary_name = NULL;
592 *subsidiary_out = NULL;
594 primary_id = keyctl_search(collection_id, KRCC_KEY_TYPE_USER,
595 KRCC_COLLECTION_PRIMARY, 0);
596 if (primary_id == -1) {
597 /* Initialize the primary key using the collection name. We can't name
598 * a key with the empty string, so map that to an arbitrary string. */
599 subsidiary_name = strdup((*collection_name == '\0') ? "tkt" :
601 if (subsidiary_name == NULL) {
605 ret = set_primary_name(context, collection_id, subsidiary_name);
609 if (strcmp(anchor_name, KRCC_LEGACY_ANCHOR) == 0) {
610 /* Look for a cache created by old code. If we find one, add it to
612 legacy = keyctl_search(KEY_SPEC_SESSION_KEYRING,
613 KRCC_KEY_TYPE_KEYRING, subsidiary_name, 0);
614 if (legacy != -1 && keyctl_link(legacy, collection_id) == -1) {
620 /* Read, parse, and free the primary key's payload. */
621 payloadlen = keyctl_read_alloc(primary_id, &payload);
622 if (payloadlen == -1) {
626 ret = parse_index(context, &version, &subsidiary_name, payload,
631 if (version != KRCC_COLLECTION_VERSION) {
632 ret = KRB5_KCC_UNKNOWN_VERSION;
637 *subsidiary_out = subsidiary_name;
638 subsidiary_name = NULL;
642 free(subsidiary_name);
647 * Create a keyring with a unique random name within collection_id. Set
648 * *subsidiary to its name and *cache_id_out to its key serial number.
650 static krb5_error_code
651 unique_keyring(krb5_context context, key_serial_t collection_id,
652 char **subsidiary_out, key_serial_t *cache_id_out)
656 char uniquename[sizeof(KRCC_NAME_PREFIX) + KRCC_NAME_RAND_CHARS];
657 int prefixlen = sizeof(KRCC_NAME_PREFIX) - 1;
660 *subsidiary_out = NULL;
663 memcpy(uniquename, KRCC_NAME_PREFIX, sizeof(KRCC_NAME_PREFIX));
664 k5_cc_mutex_lock(context, &krb5int_krcc_mutex);
666 /* Loop until we successfully create a new ccache keyring with
667 * a unique name, or we get an error. Limit to 100 tries. */
669 while (tries-- > 0) {
670 ret = krb5int_random_string(context, uniquename + prefixlen,
671 KRCC_NAME_RAND_CHARS);
675 key = keyctl_search(collection_id, KRCC_KEY_TYPE_KEYRING, uniquename,
678 /* Name does not already exist. Create it to reserve the name. */
679 key = add_key(KRCC_KEY_TYPE_KEYRING, uniquename, NULL, 0,
690 ret = KRB5_CC_BADNAME;
694 *subsidiary_out = strdup(uniquename);
695 if (*subsidiary_out == NULL) {
702 k5_cc_mutex_unlock(context, &krb5int_krcc_mutex);
706 static krb5_error_code
707 add_cred_key(const char *name, const void *payload, size_t plen,
708 key_serial_t cache_id, krb5_boolean legacy_type,
709 key_serial_t *key_out)
715 /* Try the preferred cred key type; fall back if no kernel support. */
716 key = add_key(KRCC_CRED_KEY_TYPE, name, payload, plen, cache_id);
720 } else if (errno != EINVAL && errno != ENODEV) {
724 /* Use the user key type. */
725 key = add_key(KRCC_KEY_TYPE_USER, name, payload, plen, cache_id);
733 update_keyring_expiration(krb5_context context, krb5_ccache id)
735 krcc_data *data = id->data;
736 krb5_cc_cursor cursor;
738 krb5_timestamp now, endtime = 0;
739 unsigned int timeout;
742 * We have no way to know what is the actual timeout set on the keyring.
743 * We also cannot keep track of it in a local variable as another process
744 * can always modify the keyring independently, so just always enumerate
745 * all keys and find out the highest endtime time.
748 /* Find the maximum endtime of all creds in the cache. */
749 if (krcc_start_seq_get(context, id, &cursor) != 0)
752 if (krcc_next_cred(context, id, &cursor, &creds) != 0)
754 if (ts_after(creds.times.endtime, endtime))
755 endtime = creds.times.endtime;
756 krb5_free_cred_contents(context, &creds);
758 (void)krcc_end_seq_get(context, id, &cursor);
760 if (endtime == 0) /* No creds with end times */
763 if (krb5_timeofday(context, &now) != 0)
766 /* Setting the timeout to zero would reset the timeout, so we set it to one
767 * second instead if creds are already expired. */
768 timeout = ts_after(endtime, now) ? ts_delta(endtime, now) : 1;
769 (void)keyctl_set_timeout(data->cache_id, timeout);
772 /* Create or overwrite the cache keyring, and set the default principal. */
773 static krb5_error_code KRB5_CALLCONV
774 krcc_initialize(krb5_context context, krb5_ccache id, krb5_principal princ)
776 krcc_data *data = (krcc_data *)id->data;
777 krb5_os_context os_ctx = &context->os_context;
779 const char *cache_name, *p;
781 k5_cc_mutex_lock(context, &data->lock);
783 ret = clear_cache_keyring(context, id);
787 if (!data->cache_id) {
788 /* The key didn't exist at resolve time. Check again and create the
789 * key if it still isn't there. */
790 p = strrchr(data->name, ':');
791 cache_name = (p != NULL) ? p + 1 : data->name;
792 ret = find_or_create_keyring(data->collection_id, 0, cache_name,
798 /* If this is the legacy cache in a legacy session collection, link it
799 * directly to the session keyring so that old code can see it. */
800 if (is_legacy_cache_name(data->name))
801 (void)keyctl_link(data->cache_id, session_write_anchor());
803 ret = save_principal(context, id, princ);
805 /* Save time offset if it is valid and this is not a legacy cache. Legacy
806 * applications would fail to parse the new key in the cache keyring. */
807 if (!is_legacy_cache_name(data->name) &&
808 (os_ctx->os_flags & KRB5_OS_TOFFSET_VALID)) {
809 ret = save_time_offsets(context, id, os_ctx->time_offset,
810 os_ctx->usec_offset);
817 k5_cc_mutex_unlock(context, &data->lock);
821 /* Release the ccache handle. */
822 static krb5_error_code KRB5_CALLCONV
823 krcc_close(krb5_context context, krb5_ccache id)
825 krcc_data *data = id->data;
827 k5_cc_mutex_destroy(&data->lock);
834 /* Clear out a ccache keyring, unlinking all keys within it. Call with the
836 static krb5_error_code
837 clear_cache_keyring(krb5_context context, krb5_ccache id)
839 krcc_data *data = id->data;
842 k5_cc_mutex_assert_locked(context, &data->lock);
844 DEBUG_PRINT(("clear_cache_keyring: cache_id %d, princ_id %d\n",
845 data->cache_id, data->princ_id));
847 if (data->cache_id) {
848 res = keyctl_clear(data->cache_id);
853 krcc_update_change_time(data);
858 /* Destroy the cache keyring and release the handle. */
859 static krb5_error_code KRB5_CALLCONV
860 krcc_destroy(krb5_context context, krb5_ccache id)
862 krb5_error_code ret = 0;
863 krcc_data *data = id->data;
866 k5_cc_mutex_lock(context, &data->lock);
868 clear_cache_keyring(context, id);
869 if (data->cache_id) {
870 res = keyctl_unlink(data->cache_id, data->collection_id);
873 DEBUG_PRINT(("unlinking key %d from ring %d: %s", data->cache_id,
874 data->collection_id, error_message(errno)));
876 /* If this is a legacy cache, unlink it from the session anchor. */
877 if (is_legacy_cache_name(data->name))
878 (void)keyctl_unlink(data->cache_id, session_write_anchor());
881 k5_cc_mutex_unlock(context, &data->lock);
882 k5_cc_mutex_destroy(&data->lock);
890 /* Create a cache handle for a cache ID. */
891 static krb5_error_code
892 make_cache(krb5_context context, key_serial_t collection_id,
893 key_serial_t cache_id, const char *anchor_name,
894 const char *collection_name, const char *subsidiary_name,
895 krb5_ccache *cache_out)
898 krb5_os_context os_ctx = &context->os_context;
899 krb5_ccache ccache = NULL;
901 key_serial_t pkey = 0;
903 /* Determine the key containing principal information, if present. */
904 pkey = keyctl_search(cache_id, KRCC_KEY_TYPE_USER, KRCC_SPEC_PRINC_KEYNAME,
909 ccache = malloc(sizeof(struct _krb5_ccache));
913 ret = make_krcc_data(anchor_name, collection_name, subsidiary_name,
914 cache_id, collection_id, &data);
920 data->princ_id = pkey;
921 ccache->ops = &krb5_krcc_ops;
923 ccache->magic = KV5M_CCACHE;
926 /* Look up time offsets if necessary. */
927 if ((context->library_options & KRB5_LIBOPT_SYNC_KDCTIME) &&
928 !(os_ctx->os_flags & KRB5_OS_TOFFSET_VALID)) {
929 if (get_time_offsets(context, ccache, &os_ctx->time_offset,
930 &os_ctx->usec_offset) == 0) {
931 os_ctx->os_flags &= ~KRB5_OS_TOFFSET_TIME;
932 os_ctx->os_flags |= KRB5_OS_TOFFSET_VALID;
939 /* Create a keyring ccache handle for the given residual string. */
940 static krb5_error_code KRB5_CALLCONV
941 krcc_resolve(krb5_context context, krb5_ccache *id, const char *residual)
944 key_serial_t collection_id, cache_id;
945 char *anchor_name = NULL, *collection_name = NULL, *subsidiary_name = NULL;
947 ret = parse_residual(residual, &anchor_name, &collection_name,
951 ret = get_collection(anchor_name, collection_name, &collection_id);
955 if (subsidiary_name == NULL) {
956 /* Retrieve or initialize the primary name for the collection. */
957 ret = get_primary_name(context, anchor_name, collection_name,
958 collection_id, &subsidiary_name);
963 /* Look up the cache keyring ID, if the cache is already initialized. */
964 cache_id = keyctl_search(collection_id, KRCC_KEY_TYPE_KEYRING,
969 ret = make_cache(context, collection_id, cache_id, anchor_name,
970 collection_name, subsidiary_name, id);
976 free(collection_name);
977 free(subsidiary_name);
981 /* Prepare for a sequential iteration over the cache keyring. */
982 static krb5_error_code KRB5_CALLCONV
983 krcc_start_seq_get(krb5_context context, krb5_ccache id,
984 krb5_cc_cursor *cursor)
986 krcc_cursor krcursor;
987 krcc_data *data = id->data;
991 k5_cc_mutex_lock(context, &data->lock);
993 if (!data->cache_id) {
994 k5_cc_mutex_unlock(context, &data->lock);
995 return KRB5_FCC_NOFILE;
998 size = keyctl_read_alloc(data->cache_id, &keys);
1000 DEBUG_PRINT(("Error getting from keyring: %s\n", strerror(errno)));
1001 k5_cc_mutex_unlock(context, &data->lock);
1005 krcursor = calloc(1, sizeof(*krcursor));
1006 if (krcursor == NULL) {
1008 k5_cc_mutex_unlock(context, &data->lock);
1009 return KRB5_CC_NOMEM;
1012 krcursor->princ_id = data->princ_id;
1013 krcursor->offsets_id = keyctl_search(data->cache_id, KRCC_KEY_TYPE_USER,
1014 KRCC_TIME_OFFSETS, 0);
1015 krcursor->numkeys = size / sizeof(key_serial_t);
1016 krcursor->keys = keys;
1018 k5_cc_mutex_unlock(context, &data->lock);
1023 /* Get the next credential from the cache keyring. */
1024 static krb5_error_code KRB5_CALLCONV
1025 krcc_next_cred(krb5_context context, krb5_ccache id, krb5_cc_cursor *cursor,
1028 krcc_cursor krcursor;
1029 krb5_error_code ret;
1031 void *payload = NULL;
1033 memset(creds, 0, sizeof(krb5_creds));
1035 /* The cursor has the entire list of keys. (Note that we don't support
1038 if (krcursor == NULL)
1041 /* If we're pointing past the end of the keys array, there are no more. */
1042 if (krcursor->currkey >= krcursor->numkeys)
1045 /* If we're pointing at the entry with the principal, or at the key
1046 * with the time offsets, skip it. */
1047 while (krcursor->keys[krcursor->currkey] == krcursor->princ_id ||
1048 krcursor->keys[krcursor->currkey] == krcursor->offsets_id) {
1049 krcursor->currkey++;
1050 /* Check if we have now reached the end */
1051 if (krcursor->currkey >= krcursor->numkeys)
1055 /* Read the key; the right size buffer will be allocated and returned. */
1056 psize = keyctl_read_alloc(krcursor->keys[krcursor->currkey], &payload);
1058 DEBUG_PRINT(("Error reading key %d: %s\n",
1059 krcursor->keys[krcursor->currkey],
1061 return KRB5_FCC_NOFILE;
1063 krcursor->currkey++;
1065 /* Unmarshal the credential using the file ccache version 4 format. */
1066 ret = k5_unmarshal_cred(payload, psize, 4, creds);
1071 /* Release an iteration cursor. */
1072 static krb5_error_code KRB5_CALLCONV
1073 krcc_end_seq_get(krb5_context context, krb5_ccache id, krb5_cc_cursor *cursor)
1075 krcc_cursor krcursor = *cursor;
1077 if (krcursor != NULL) {
1078 free(krcursor->keys);
1085 /* Create keyring data for a credential cache. */
1086 static krb5_error_code
1087 make_krcc_data(const char *anchor_name, const char *collection_name,
1088 const char *subsidiary_name, key_serial_t cache_id,
1089 key_serial_t collection_id, krcc_data **data_out)
1091 krb5_error_code ret;
1096 data = malloc(sizeof(krcc_data));
1098 return KRB5_CC_NOMEM;
1100 ret = k5_cc_mutex_init(&data->lock);
1106 ret = make_subsidiary_residual(anchor_name, collection_name,
1107 subsidiary_name, &data->name);
1109 k5_cc_mutex_destroy(&data->lock);
1114 data->cache_id = cache_id;
1115 data->collection_id = collection_id;
1116 data->changetime = 0;
1117 data->is_legacy_type = (strcmp(anchor_name, KRCC_LEGACY_ANCHOR) == 0);
1118 krcc_update_change_time(data);
1124 /* Create a new keyring cache with a unique name. */
1125 static krb5_error_code KRB5_CALLCONV
1126 krcc_generate_new(krb5_context context, krb5_ccache *id_out)
1128 krb5_ccache id = NULL;
1129 krb5_error_code ret;
1130 char *anchor_name = NULL, *collection_name = NULL, *subsidiary_name = NULL;
1131 char *new_subsidiary_name = NULL, *new_residual = NULL;
1133 key_serial_t collection_id;
1134 key_serial_t cache_id = 0;
1138 /* Determine the collection in which we will create the cache.*/
1139 ret = get_default(context, &anchor_name, &collection_name,
1143 if (anchor_name == NULL) {
1144 ret = parse_residual(KRCC_DEFAULT_UNIQUE_COLLECTION, &anchor_name,
1145 &collection_name, &subsidiary_name);
1149 if (subsidiary_name != NULL) {
1150 k5_setmsg(context, KRB5_DCC_CANNOT_CREATE,
1151 _("Can't create new subsidiary cache because default cache "
1152 "is already a subsidiary"));
1153 ret = KRB5_DCC_CANNOT_CREATE;
1157 /* Allocate memory */
1158 id = malloc(sizeof(struct _krb5_ccache));
1164 id->ops = &krb5_krcc_ops;
1166 /* Make a unique keyring within the chosen collection. */
1167 ret = get_collection(anchor_name, collection_name, &collection_id);
1170 ret = unique_keyring(context, collection_id, &new_subsidiary_name,
1175 ret = make_krcc_data(anchor_name, collection_name, new_subsidiary_name,
1176 cache_id, collection_id, &data);
1181 krb5_change_cache();
1185 free(collection_name);
1186 free(subsidiary_name);
1187 free(new_subsidiary_name);
1197 /* Return an alias to the residual string of the cache. */
1198 static const char *KRB5_CALLCONV
1199 krcc_get_name(krb5_context context, krb5_ccache id)
1201 return ((krcc_data *)id->data)->name;
1204 /* Retrieve a copy of the default principal, if the cache is initialized. */
1205 static krb5_error_code KRB5_CALLCONV
1206 krcc_get_principal(krb5_context context, krb5_ccache id,
1207 krb5_principal *princ_out)
1209 krcc_data *data = id->data;
1210 krb5_error_code ret;
1211 void *payload = NULL;
1215 k5_cc_mutex_lock(context, &data->lock);
1217 if (!data->cache_id || !data->princ_id) {
1218 ret = KRB5_FCC_NOFILE;
1219 k5_setmsg(context, ret, _("Credentials cache keyring '%s' not found"),
1224 psize = keyctl_read_alloc(data->princ_id, &payload);
1226 DEBUG_PRINT(("Reading principal key %d: %s\n",
1227 data->princ_id, strerror(errno)));
1232 /* Unmarshal the principal using the file ccache version 4 format. */
1233 ret = k5_unmarshal_princ(payload, psize, 4, princ_out);
1237 k5_cc_mutex_unlock(context, &data->lock);
1241 /* Search for a credential within the cache keyring. */
1242 static krb5_error_code KRB5_CALLCONV
1243 krcc_retrieve(krb5_context context, krb5_ccache id,
1244 krb5_flags whichfields, krb5_creds *mcreds,
1247 return k5_cc_retrieve_cred_default(context, id, whichfields, mcreds,
1251 /* Non-functional stub for removing a cred from the cache keyring. */
1252 static krb5_error_code KRB5_CALLCONV
1253 krcc_remove_cred(krb5_context context, krb5_ccache cache,
1254 krb5_flags flags, krb5_creds *creds)
1256 return KRB5_CC_NOSUPP;
1259 /* Set flags on the cache. (We don't care about any flags.) */
1260 static krb5_error_code KRB5_CALLCONV
1261 krcc_set_flags(krb5_context context, krb5_ccache id, krb5_flags flags)
1266 /* Get the current operational flags (of which we have none) for the cache. */
1267 static krb5_error_code KRB5_CALLCONV
1268 krcc_get_flags(krb5_context context, krb5_ccache id, krb5_flags *flags_out)
1274 /* Store a credential in the cache keyring. */
1275 static krb5_error_code KRB5_CALLCONV
1276 krcc_store(krb5_context context, krb5_ccache id, krb5_creds *creds)
1278 krb5_error_code ret;
1279 krcc_data *data = id->data;
1280 struct k5buf buf = EMPTY_K5BUF;
1281 char *keyname = NULL;
1282 key_serial_t cred_key;
1285 k5_cc_mutex_lock(context, &data->lock);
1287 if (!data->cache_id) {
1288 k5_cc_mutex_unlock(context, &data->lock);
1289 return KRB5_FCC_NOFILE;
1292 /* Get the service principal name and use it as the key name */
1293 ret = krb5_unparse_name(context, creds->server, &keyname);
1297 /* Serialize credential using the file ccache version 4 format. */
1298 k5_buf_init_dynamic_zap(&buf);
1299 k5_marshal_cred(&buf, 4, creds);
1300 ret = k5_buf_status(&buf);
1304 /* Add new key (credentials) into keyring */
1305 DEBUG_PRINT(("krcc_store: adding new key '%s' to keyring %d\n",
1306 keyname, data->cache_id));
1307 ret = add_cred_key(keyname, buf.data, buf.len, data->cache_id,
1308 data->is_legacy_type, &cred_key);
1312 krcc_update_change_time(data);
1314 /* Set appropriate timeouts on cache keys. */
1315 ret = krb5_timeofday(context, &now);
1319 if (ts_after(creds->times.endtime, now)) {
1320 (void)keyctl_set_timeout(cred_key,
1321 ts_delta(creds->times.endtime, now));
1324 update_keyring_expiration(context, id);
1328 krb5_free_unparsed_name(context, keyname);
1329 k5_cc_mutex_unlock(context, &data->lock);
1333 /* Get the cache's last modification time. (This is currently broken; it
1334 * returns only the last change made using this handle.) */
1335 static krb5_error_code KRB5_CALLCONV
1336 krcc_last_change_time(krb5_context context, krb5_ccache id,
1337 krb5_timestamp *change_time)
1339 krcc_data *data = id->data;
1341 k5_cc_mutex_lock(context, &data->lock);
1342 *change_time = data->changetime;
1343 k5_cc_mutex_unlock(context, &data->lock);
1347 /* Lock the cache handle against other threads. (This does not lock the cache
1348 * keyring against other processes.) */
1349 static krb5_error_code KRB5_CALLCONV
1350 krcc_lock(krb5_context context, krb5_ccache id)
1352 krcc_data *data = id->data;
1354 k5_cc_mutex_lock(context, &data->lock);
1358 /* Unlock the cache handle. */
1359 static krb5_error_code KRB5_CALLCONV
1360 krcc_unlock(krb5_context context, krb5_ccache id)
1362 krcc_data *data = id->data;
1364 k5_cc_mutex_unlock(context, &data->lock);
1368 static krb5_error_code
1369 save_principal(krb5_context context, krb5_ccache id, krb5_principal princ)
1371 krcc_data *data = id->data;
1372 krb5_error_code ret;
1374 key_serial_t newkey;
1376 k5_cc_mutex_assert_locked(context, &data->lock);
1378 /* Serialize princ using the file ccache version 4 format. */
1379 k5_buf_init_dynamic(&buf);
1380 k5_marshal_princ(&buf, 4, princ);
1381 if (k5_buf_status(&buf) != 0)
1384 /* Add new key into keyring */
1388 char *princname = NULL;
1389 rc = krb5_unparse_name(context, princ, &princname);
1390 DEBUG_PRINT(("save_principal: adding new key '%s' "
1391 "to keyring %d for principal '%s'\n",
1392 KRCC_SPEC_PRINC_KEYNAME, data->cache_id,
1393 rc ? "<unknown>" : princname));
1395 krb5_free_unparsed_name(context, princname);
1398 newkey = add_key(KRCC_KEY_TYPE_USER, KRCC_SPEC_PRINC_KEYNAME, buf.data,
1399 buf.len, data->cache_id);
1402 DEBUG_PRINT(("Error adding principal key: %s\n", strerror(ret)));
1404 data->princ_id = newkey;
1406 krcc_update_change_time(data);
1413 /* Add a key to the cache keyring containing the given time offsets. */
1414 static krb5_error_code
1415 save_time_offsets(krb5_context context, krb5_ccache id, int32_t time_offset,
1416 int32_t usec_offset)
1418 krcc_data *data = id->data;
1419 key_serial_t newkey;
1420 unsigned char payload[8];
1422 k5_cc_mutex_assert_locked(context, &data->lock);
1424 /* Prepare the payload. */
1425 store_32_be(time_offset, payload);
1426 store_32_be(usec_offset, payload + 4);
1428 /* Add new key into keyring. */
1429 newkey = add_key(KRCC_KEY_TYPE_USER, KRCC_TIME_OFFSETS, payload, 8,
1433 krcc_update_change_time(data);
1437 /* Retrieve and parse the key in the cache keyring containing time offsets. */
1438 static krb5_error_code
1439 get_time_offsets(krb5_context context, krb5_ccache id, int32_t *time_offset,
1440 int32_t *usec_offset)
1442 krcc_data *data = id->data;
1443 krb5_error_code ret = 0;
1445 void *payload = NULL;
1448 k5_cc_mutex_lock(context, &data->lock);
1450 if (!data->cache_id) {
1451 ret = KRB5_FCC_NOFILE;
1455 key = keyctl_search(data->cache_id, KRCC_KEY_TYPE_USER, KRCC_TIME_OFFSETS,
1462 psize = keyctl_read_alloc(key, &payload);
1464 DEBUG_PRINT(("Reading time offsets key %d: %s\n",
1465 key, strerror(errno)));
1474 *time_offset = load_32_be(payload);
1475 *usec_offset = load_32_be((char *)payload + 4);
1479 k5_cc_mutex_unlock(context, &data->lock);
1483 struct krcc_ptcursor_data {
1484 key_serial_t collection_id;
1486 char *collection_name;
1487 char *subsidiary_name;
1495 static krb5_error_code KRB5_CALLCONV
1496 krcc_ptcursor_new(krb5_context context, krb5_cc_ptcursor *cursor_out)
1498 struct krcc_ptcursor_data *ptd;
1499 krb5_cc_ptcursor cursor;
1500 krb5_error_code ret;
1506 cursor = k5alloc(sizeof(*cursor), &ret);
1509 ptd = k5alloc(sizeof(*ptd), &ret);
1512 cursor->ops = &krb5_krcc_ops;
1516 ret = get_default(context, &ptd->anchor_name, &ptd->collection_name,
1517 &ptd->subsidiary_name);
1521 /* If there is no default collection, return an empty cursor. */
1522 if (ptd->anchor_name == NULL) {
1523 *cursor_out = cursor;
1527 ret = get_collection(ptd->anchor_name, ptd->collection_name,
1528 &ptd->collection_id);
1532 if (ptd->subsidiary_name == NULL) {
1533 ret = get_primary_name(context, ptd->anchor_name,
1534 ptd->collection_name, ptd->collection_id,
1535 &ptd->primary_name);
1539 size = keyctl_read_alloc(ptd->collection_id, &keys);
1545 ptd->num_keys = size / sizeof(key_serial_t);
1548 *cursor_out = cursor;
1552 krcc_ptcursor_free(context, &cursor);
1556 static krb5_error_code KRB5_CALLCONV
1557 krcc_ptcursor_next(krb5_context context, krb5_cc_ptcursor cursor,
1558 krb5_ccache *cache_out)
1560 krb5_error_code ret;
1561 struct krcc_ptcursor_data *ptd = cursor->data;
1562 key_serial_t key, cache_id = 0;
1563 const char *first_name, *keytype, *sep, *subsidiary_name;
1565 char *description = NULL;
1569 /* No keyring available */
1570 if (ptd->collection_id == 0)
1574 /* Look for the primary cache for a collection cursor, or the
1575 * subsidiary cache for a subsidiary cursor. */
1577 first_name = (ptd->primary_name != NULL) ? ptd->primary_name :
1578 ptd->subsidiary_name;
1579 cache_id = keyctl_search(ptd->collection_id, KRCC_KEY_TYPE_KEYRING,
1581 if (cache_id != -1) {
1582 return make_cache(context, ptd->collection_id, cache_id,
1583 ptd->anchor_name, ptd->collection_name,
1584 first_name, cache_out);
1588 /* A subsidiary cursor yields at most the first cache. */
1589 if (ptd->subsidiary_name != NULL)
1592 keytype = KRCC_KEY_TYPE_KEYRING ";";
1593 keytypelen = strlen(keytype);
1595 for (; ptd->next_key < ptd->num_keys; ptd->next_key++) {
1596 /* Free any previously retrieved key description. */
1601 * Get the key description, which should have the form:
1602 * typename;UID;GID;permissions;description
1604 key = ptd->keys[ptd->next_key];
1605 if (keyctl_describe_alloc(key, &description) < 0)
1607 sep = strrchr(description, ';');
1610 subsidiary_name = sep + 1;
1612 /* Skip this key if it isn't a keyring. */
1613 if (strncmp(description, keytype, keytypelen) != 0)
1616 /* Don't repeat the primary cache. */
1617 if (strcmp(subsidiary_name, ptd->primary_name) == 0)
1620 /* We found a valid key */
1622 ret = make_cache(context, ptd->collection_id, key, ptd->anchor_name,
1623 ptd->collection_name, subsidiary_name, cache_out);
1632 static krb5_error_code KRB5_CALLCONV
1633 krcc_ptcursor_free(krb5_context context, krb5_cc_ptcursor *cursor)
1635 struct krcc_ptcursor_data *ptd = (*cursor)->data;
1638 free(ptd->anchor_name);
1639 free(ptd->collection_name);
1640 free(ptd->subsidiary_name);
1641 free(ptd->primary_name);
1650 static krb5_error_code KRB5_CALLCONV
1651 krcc_switch_to(krb5_context context, krb5_ccache cache)
1653 krcc_data *data = cache->data;
1654 krb5_error_code ret;
1655 char *anchor_name = NULL, *collection_name = NULL, *subsidiary_name = NULL;
1656 key_serial_t collection_id;
1658 ret = parse_residual(data->name, &anchor_name, &collection_name,
1662 ret = get_collection(anchor_name, collection_name, &collection_id);
1665 ret = set_primary_name(context, collection_id, subsidiary_name);
1669 free(collection_name);
1670 free(subsidiary_name);
1675 * Utility routine: called by krcc_* functions to keep
1676 * result of krcc_last_change_time up to date.
1677 * Value monotonically increases -- based on but not guaranteed to be actual
1682 krcc_update_change_time(krcc_data *data)
1684 krb5_timestamp now_time = time(NULL);
1685 data->changetime = ts_after(now_time, data->changetime) ?
1686 now_time : ts_incr(data->changetime, 1);
1690 * ccache implementation storing credentials in the Linux keyring facility
1691 * The default is to put them at the session keyring level.
1692 * If "KEYRING:process:" or "KEYRING:thread:" is specified, then they will
1693 * be stored at the process or thread level respectively.
1695 const krb5_cc_ops krb5_krcc_ops = {
1712 krcc_get_flags, /* added after 1.4 release */
1717 krcc_last_change_time, /* lastchange */
1718 NULL, /* wasdefault */
1724 #else /* !USE_KEYRING_CCACHE */
1727 * Export this, but it shouldn't be used.
1729 const krb5_cc_ops krb5_krcc_ops = {
1746 NULL, /* added after 1.4 release */
1757 #endif /* USE_KEYRING_CCACHE */