2 * Copyright (c) 2006 The Regents of the University of Michigan.
5 * Portions Copyright (c) 2018, AuriStor, Inc.
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
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
32 * Copyright 1990,1991,1992,1993,1994,2000,2004 Massachusetts Institute of
33 * Technology. All Rights Reserved.
35 * Original stdio support copyright 1995 by Cygnus Support.
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.
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.
58 * This file implements a collection-enabled credential cache type where the
59 * credentials are stored in the Linux keyring facility.
61 * A residual of this type can have three forms:
62 * anchor:collection:subsidiary
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
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.
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.
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.
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.
96 #include "krb5_locl.h"
98 #ifdef HAVE_KEYUTILS_H
100 #include <keyutils.h>
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.
108 #ifdef HAVE_KEYCTL_GET_PERSISTENT
109 #define KRCC_CRED_KEY_TYPE "big_key"
111 #define KRCC_CRED_KEY_TYPE "user"
115 * We use the "user" key type for collection primary names, for cache principal
116 * names, and for credentials in legacy caches.
118 #define KRCC_KEY_TYPE_USER "user"
121 * We create ccaches as separate keyrings
123 #define KRCC_KEY_TYPE_KEYRING "keyring"
126 * Special name of the key within a ccache keyring
127 * holding principal information
129 #define KRCC_SPEC_PRINC_KEYNAME "__krb5_princ__"
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.
138 #define KRCC_SPEC_CCACHE_SET_KEYNAME "__krb5_cc_set__"
141 * This name identifies the key containing the name of the current primary
142 * cache within a collection.
144 #define KRCC_COLLECTION_PRIMARY "krb_ccache:primary"
147 * If the library context does not specify a keyring collection, unique ccaches
148 * will be created within this collection.
150 #define KRCC_DEFAULT_UNIQUE_COLLECTION "session:__krb5_unique__"
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.
157 #define KRCC_CCCOL_PREFIX "_krb_"
160 * For the "persistent" anchor type, we look up or create this fixed keyring
161 * name within the per-UID persistent keyring.
163 #define KRCC_PERSISTENT_KEYRING_NAME "_krb"
166 * Name of the key holding time offsets for the individual cache
168 #define KRCC_TIME_OFFSETS "__krb5_time_offsets__"
171 * Keyring name prefix and length of random name part
173 #define KRCC_NAME_PREFIX "krb_ccache_"
174 #define KRCC_NAME_RAND_CHARS 8
176 #define KRCC_COLLECTION_VERSION 1
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"
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
190 typedef heim_base_atomic(key_serial_t
) atomic_key_serial_t
;
192 typedef union _krb5_krcache_and_princ_id
{
193 heim_base_atomic(uint64_t) krcu_cache_and_princ_id
;
195 atomic_key_serial_t cache_id
; /* keyring ID representing ccache */
196 atomic_key_serial_t princ_id
; /* key ID holding principal info */
198 #define krcu_cache_id krcu_id.cache_id
199 #define krcu_princ_id krcu_id.princ_id
200 } krb5_krcache_and_princ_id
;
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.
207 * Thread-safe as long as caches are not destroyed whilst other threads are
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
; /* */
223 #define KRCACHE(X) ((krb5_krcache *)(X)->data.data)
225 static krb5_error_code KRB5_CALLCONV
226 krcc_get_first(krb5_context
, krb5_ccache id
, krb5_cc_cursor
*cursor
);
228 static krb5_error_code KRB5_CALLCONV
229 krcc_get_next(krb5_context context
,
231 krb5_cc_cursor
*cursor
,
234 static krb5_error_code KRB5_CALLCONV
235 krcc_end_get(krb5_context context
,
237 krb5_cc_cursor
*cursor
);
239 static krb5_error_code KRB5_CALLCONV
240 krcc_end_cache_get(krb5_context context
, krb5_cc_cursor cursor
);
242 static krb5_error_code
243 clear_cache_keyring(krb5_context context
, atomic_key_serial_t
*pcache_id
);
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
);
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
);
260 static krb5_error_code
261 save_time_offsets(krb5_context context
,
262 key_serial_t cache_id
,
264 int32_t usec_offset
);
267 update_change_time(krb5_context context
,
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.
277 get_persistent_fallback(uid_t uid
)
279 return (uid
== geteuid()) ? KEY_SPEC_USER_KEYRING
: -1;
282 #ifdef HAVE_KEYCTL_GET_PERSISTENT
283 #define GET_PERSISTENT get_persistent_real
285 get_persistent_real(uid_t uid
)
289 key
= keyctl_get_persistent(uid
, KEY_SPEC_PROCESS_KEYRING
);
291 return (key
== -1 && errno
== ENOTSUP
) ? get_persistent_fallback(uid
) : key
;
294 #define GET_PERSISTENT get_persistent_fallback
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.
308 session_write_anchor(void)
312 s
= keyctl_get_keyring_ID(KEY_SPEC_SESSION_KEYRING
, 0);
313 u
= keyctl_get_keyring_ID(KEY_SPEC_USER_SESSION_KEYRING
, 0);
315 return (s
== u
) ? KEY_SPEC_USER_SESSION_KEYRING
: KEY_SPEC_SESSION_KEYRING
;
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.
324 static krb5_error_code
325 find_or_create_keyring(key_serial_t parent
,
326 key_serial_t possess
,
328 atomic_key_serial_t
*pkey
)
332 key
= keyctl_search(parent
, KRCC_KEY_TYPE_KEYRING
, name
, possess
);
335 key
= add_key(KRCC_KEY_TYPE_KEYRING
, name
, NULL
, 0, possess
);
336 if (key
== -1 || keyctl_link(key
, parent
) == -1)
339 key
= add_key(KRCC_KEY_TYPE_KEYRING
, name
, NULL
, 0, parent
);
345 heim_base_atomic_store(pkey
, key
);
351 * Parse a residual name into an anchor name, a collection name, and possibly a
354 static krb5_error_code
355 parse_residual(krb5_context context
,
356 const char *residual
,
358 char **pcollection_name
,
359 char **psubsidiary_name
)
361 char *anchor_name
= NULL
;
362 char *collection_name
= NULL
;
363 char *subsidiary_name
= NULL
;
366 *panchor_name
= NULL
;
367 *pcollection_name
= NULL
;
368 *psubsidiary_name
= NULL
;
370 if (residual
== NULL
)
373 /* Parse out the anchor name. Use the legacy anchor if not present. */
374 sep
= strchr(residual
, ':');
376 anchor_name
= strdup(KRCC_LEGACY_ANCHOR
);
377 if (anchor_name
== NULL
)
380 anchor_name
= strndup(residual
, sep
- residual
);
381 if (anchor_name
== NULL
)
386 /* Parse out the collection and subsidiary name. */
387 sep
= strchr(residual
, ':');
389 collection_name
= strdup(residual
);
390 if (collection_name
== NULL
)
393 collection_name
= strndup(residual
, sep
- residual
);
394 if (collection_name
== NULL
)
397 subsidiary_name
= strdup(sep
+ 1);
398 if (subsidiary_name
== NULL
)
402 *panchor_name
= anchor_name
;
403 *pcollection_name
= collection_name
;
404 *psubsidiary_name
= subsidiary_name
;
410 free(collection_name
);
411 free(subsidiary_name
);
413 return krb5_enomem(context
);
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
423 is_legacy_cache_name_p(const char *residual
)
425 const char *sep
, *aname
, *cname
, *sname
;
426 size_t alen
, clen
, legacy_len
= sizeof(KRCC_LEGACY_ANCHOR
) - 1;
428 /* Get pointers to the anchor, collection, and subsidiary names. */
430 sep
= strchr(residual
, ':');
436 sep
= strchr(cname
, ':');
443 return alen
== legacy_len
&& clen
== strlen(sname
) &&
444 strncmp(aname
, KRCC_LEGACY_ANCHOR
, alen
) == 0 &&
445 strncmp(cname
, sname
, clen
) == 0;
449 * If the default cache name for context is a KEYRING cache, parse its residual
450 * string. Otherwise set all outputs to NULL.
452 static krb5_error_code
453 get_default(krb5_context context
,
455 char **pcollection_name
,
456 char **psubsidiary_name
)
460 *panchor_name
= *pcollection_name
= *psubsidiary_name
= NULL
;
462 defname
= krb5_cc_default_name(context
);
463 if (defname
== NULL
|| strncmp(defname
, "KEYRING:", 8) != 0)
466 return parse_residual(context
, defname
+ 8,
467 panchor_name
, pcollection_name
, psubsidiary_name
);
470 /* Create a residual identifying a subsidiary cache. */
471 static krb5_error_code
472 make_subsidiary_residual(krb5_context context
,
473 const char *anchor_name
,
474 const char *collection_name
,
475 const char *subsidiary_name
,
478 if (asprintf(presidual
, "%s:%s:%s", anchor_name
, collection_name
,
479 subsidiary_name
? subsidiary_name
: "tkt") < 0) {
481 return krb5_enomem(context
);
488 * Retrieve or create a keyring for collection_name within the anchor, and set
489 * *collection_id to its serial number.
491 static krb5_error_code
492 get_collection(krb5_context context
,
493 const char *anchor_name
,
494 const char *collection_name
,
495 atomic_key_serial_t
*pcollection_id
)
498 key_serial_t persistent_id
, anchor_id
, possess_id
= 0;
499 char *ckname
, *cnend
;
502 heim_base_atomic_init(pcollection_id
, 0);
504 if (!anchor_name
|| !collection_name
)
505 return KRB5_KCC_INVALID_ANCHOR
;
507 if (strcmp(anchor_name
, KRCC_PERSISTENT_ANCHOR
) == 0) {
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.
514 if (*collection_name
!= '\0') {
516 uidnum
= (uid_t
)strtol(collection_name
, &cnend
, 10);
517 if (errno
|| *cnend
!= '\0')
518 return KRB5_KCC_INVALID_UID
;
523 persistent_id
= GET_PERSISTENT(uidnum
);
524 if (persistent_id
== -1)
525 return KRB5_KCC_INVALID_UID
;
527 return find_or_create_keyring(persistent_id
, KEY_SPEC_PROCESS_KEYRING
,
528 KRCC_PERSISTENT_KEYRING_NAME
,
532 if (strcmp(anchor_name
, KRCC_PROCESS_ANCHOR
) == 0) {
533 anchor_id
= KEY_SPEC_PROCESS_KEYRING
;
534 } else if (strcmp(anchor_name
, KRCC_THREAD_ANCHOR
) == 0) {
535 anchor_id
= KEY_SPEC_THREAD_KEYRING
;
536 } else if (strcmp(anchor_name
, KRCC_SESSION_ANCHOR
) == 0) {
537 anchor_id
= session_write_anchor();
538 } else if (strcmp(anchor_name
, KRCC_USER_ANCHOR
) == 0) {
540 * The user keyring does not confer possession, so we need to link the
541 * collection to the process keyring to maintain possession rights.
543 anchor_id
= KEY_SPEC_USER_KEYRING
;
544 possess_id
= KEY_SPEC_PROCESS_KEYRING
;
545 } else if (strcmp(anchor_name
, KRCC_LEGACY_ANCHOR
) == 0) {
546 anchor_id
= session_write_anchor();
548 return KRB5_KCC_INVALID_ANCHOR
;
551 /* Look up the collection keyring name within the anchor keyring. */
552 if (asprintf(&ckname
, "%s%s", KRCC_CCCOL_PREFIX
, collection_name
) == -1)
553 return krb5_enomem(context
);
555 ret
= find_or_create_keyring(anchor_id
, possess_id
, ckname
,
562 /* Store subsidiary_name into the primary index key for collection_id. */
563 static krb5_error_code
564 set_primary_name(krb5_context context
,
565 key_serial_t collection_id
,
566 const char *subsidiary_name
)
573 sp
= krb5_storage_emem();
575 krb5_set_error_message(context
, KRB5_CC_NOMEM
, N_("malloc: out of memory", ""));
576 return KRB5_CC_NOMEM
;
578 krb5_storage_set_byteorder(sp
, KRB5_STORAGE_BYTEORDER_BE
);
580 ret
= krb5_store_int32(sp
, KRCC_COLLECTION_VERSION
);
584 ret
= krb5_store_string(sp
, subsidiary_name
);
588 ret
= krb5_storage_to_data(sp
, &payload
);
592 key
= add_key(KRCC_KEY_TYPE_USER
, KRCC_COLLECTION_PRIMARY
,
593 payload
.data
, payload
.length
, collection_id
);
594 ret
= (key
== -1) ? errno
: 0;
595 krb5_data_free(&payload
);
598 krb5_storage_free(sp
);
603 static krb5_error_code
604 parse_index(krb5_context context
,
607 const unsigned char *payload
,
611 krb5_data payload_data
;
614 payload_data
.length
= psize
;
615 payload_data
.data
= rk_UNCONST(payload
);
617 sp
= krb5_storage_from_data(&payload_data
);
619 return KRB5_CC_NOMEM
;
621 krb5_storage_set_byteorder(sp
, KRB5_STORAGE_BYTEORDER_BE
);
623 ret
= krb5_ret_int32(sp
, version
);
625 ret
= krb5_ret_string(sp
, primary
);
627 krb5_storage_free(sp
);
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.
637 static krb5_error_code
638 get_primary_name(krb5_context context
,
639 const char *anchor_name
,
640 const char *collection_name
,
641 key_serial_t collection_id
,
645 key_serial_t primary_id
, legacy
;
646 void *payload
= NULL
;
649 char *subsidiary_name
= NULL
;
653 primary_id
= keyctl_search(collection_id
, KRCC_KEY_TYPE_USER
,
654 KRCC_COLLECTION_PRIMARY
, 0);
655 if (primary_id
== -1) {
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.
660 subsidiary_name
= strdup((*collection_name
== '\0') ? "tkt" :
662 if (subsidiary_name
== NULL
) {
663 ret
= krb5_enomem(context
);
667 ret
= set_primary_name(context
, collection_id
, subsidiary_name
);
671 if (strcmp(anchor_name
, KRCC_LEGACY_ANCHOR
) == 0) {
673 * Look for a cache created by old code. If we find one, add it to
676 legacy
= keyctl_search(KEY_SPEC_SESSION_KEYRING
,
677 KRCC_KEY_TYPE_KEYRING
, subsidiary_name
, 0);
678 if (legacy
!= -1 && keyctl_link(legacy
, collection_id
) == -1) {
684 /* Read, parse, and free the primary key's payload. */
685 payloadlen
= keyctl_read_alloc(primary_id
, &payload
);
686 if (payloadlen
== -1) {
690 ret
= parse_index(context
, &version
, &subsidiary_name
, payload
,
695 if (version
!= KRCC_COLLECTION_VERSION
) {
696 ret
= KRB5_KCC_UNKNOWN_VERSION
;
701 *psubsidiary
= subsidiary_name
;
702 subsidiary_name
= NULL
;
706 free(subsidiary_name
);
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.
716 static krb5_error_code
717 generate_random_string(krb5_context context
, char *s
, size_t slen
)
719 static char chars
[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
725 return krb5_enomem(context
);
727 krb5_generate_random_block(p
, slen
);
729 for (i
= 0; i
< slen
; i
++)
730 s
[i
] = chars
[p
[i
] % (sizeof(chars
) - 1)];
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.
742 static krb5_error_code
743 add_unique_keyring(krb5_context context
,
744 key_serial_t collection_id
,
746 key_serial_t
*pcache_id
)
750 char uniquename
[sizeof(KRCC_NAME_PREFIX
) + KRCC_NAME_RAND_CHARS
];
751 int prefixlen
= sizeof(KRCC_NAME_PREFIX
) - 1;
757 memcpy(uniquename
, KRCC_NAME_PREFIX
, sizeof(KRCC_NAME_PREFIX
));
759 for (key
= -1, tries
= 0; tries
< 5; tries
++) {
760 ret
= generate_random_string(context
, uniquename
+ prefixlen
,
761 KRCC_NAME_RAND_CHARS
);
765 key
= keyctl_search(collection_id
, KRCC_KEY_TYPE_KEYRING
, uniquename
, 0);
767 /* Name does not already exist. Create it to reserve the name. */
768 key
= add_key(KRCC_KEY_TYPE_KEYRING
, uniquename
, NULL
, 0, collection_id
);
775 *psubsidiary
= strdup(uniquename
);
776 if (*psubsidiary
== NULL
)
777 return krb5_enomem(context
);
784 static krb5_error_code
785 add_cred_key(const char *name
,
788 key_serial_t cache_id
,
789 krb5_boolean legacy_type
,
797 /* Try the preferred cred key type; fall back if no kernel support. */
798 key
= add_key(KRCC_CRED_KEY_TYPE
, name
, payload
, plen
, cache_id
);
802 } else if (errno
!= EINVAL
&& errno
!= ENODEV
)
806 /* Use the user key type. */
807 key
= add_key(KRCC_KEY_TYPE_USER
, name
, payload
, plen
, cache_id
);
817 update_keyring_expiration(krb5_context context
,
819 key_serial_t cache_id
,
822 krb5_cc_cursor cursor
;
824 krb5_timestamp endtime
= 0;
825 unsigned int timeout
;
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.
833 if (krcc_get_first(context
, id
, &cursor
) != 0)
837 if (krcc_get_next(context
, id
, &cursor
, &creds
) != 0)
839 if (creds
.times
.endtime
> endtime
)
840 endtime
= creds
.times
.endtime
;
841 krb5_free_cred_contents(context
, &creds
);
843 (void) krcc_end_get(context
, id
, &cursor
);
845 if (endtime
== 0) /* No creds with end times */
849 * Setting the timeout to zero would reset the timeout, so we set it to one
850 * second instead if creds are already expired.
852 timeout
= endtime
> now
? endtime
- now
: 1;
853 (void) keyctl_set_timeout(cache_id
, timeout
);
857 * Create or overwrite the cache keyring, and set the default principal.
859 static krb5_error_code
860 initialize_internal(krb5_context context
,
862 krb5_const_principal princ
)
864 krb5_krcache
*data
= KRCACHE(id
);
866 const char *cache_name
, *p
;
867 krb5_krcache_and_princ_id ids
;
870 return krb5_einval(context
, 2);
872 memset(&ids
, 0, sizeof(ids
));
873 ids
.krcu_cache_and_princ_id
= heim_base_atomic_load(&data
->krc_cache_and_principal_id
);
875 ret
= clear_cache_keyring(context
, &ids
.krcu_cache_id
);
879 if (ids
.krcu_cache_id
== 0) {
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.
884 p
= strrchr(data
->krc_name
, ':');
885 cache_name
= (p
!= NULL
) ? p
+ 1 : data
->krc_name
;
886 ret
= find_or_create_keyring(data
->krc_coll_id
, 0, cache_name
, &ids
.krcu_cache_id
);
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.
895 if (is_legacy_cache_name_p(data
->krc_name
))
896 (void) keyctl_link(ids
.krcu_cache_id
, session_write_anchor());
899 ret
= save_principal(context
, ids
.krcu_cache_id
, princ
, &ids
.krcu_princ_id
);
903 ids
.krcu_princ_id
= 0;
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.
909 if (context
->kdc_sec_offset
&& !is_legacy_cache_name_p(data
->krc_name
)) {
910 ret
= save_time_offsets(context
,
912 context
->kdc_sec_offset
,
913 context
->kdc_usec_offset
);
918 /* update cache and principal IDs atomically */
919 heim_base_atomic_store(&data
->krc_cache_and_principal_id
, ids
.krcu_cache_and_princ_id
);
924 static krb5_error_code KRB5_CALLCONV
925 krcc_initialize(krb5_context context
, krb5_ccache id
, krb5_principal princ
)
927 krb5_krcache
*data
= KRCACHE(id
);
931 return krb5_einval(context
, 2);
934 return KRB5_CC_BADNAME
;
936 ret
= initialize_internal(context
, id
, princ
);
938 update_change_time(context
, 0, data
);
943 /* Release the ccache handle. */
944 static krb5_error_code KRB5_CALLCONV
945 krcc_close(krb5_context context
, krb5_ccache id
)
947 krb5_krcache
*data
= KRCACHE(id
);
950 return krb5_einval(context
, 2);
952 free(data
->krc_subsidiary
);
953 free(data
->krc_collection
);
954 free(data
->krc_name
);
955 krb5_data_free(&id
->data
);
961 * Clear out a ccache keyring, unlinking all keys within it.
963 static krb5_error_code
964 clear_cache_keyring(krb5_context context
,
965 atomic_key_serial_t
*pcache_id
)
968 key_serial_t cache_id
= heim_base_atomic_load(pcache_id
);
970 _krb5_debug(context
, 10, "clear_cache_keyring: cache_id %d\n", cache_id
);
973 res
= keyctl_clear(cache_id
);
974 if (res
== -1 && (errno
== EACCES
|| errno
== ENOKEY
)) {
976 * Possibly the keyring was destroyed between krcc_resolve() and now;
977 * if we really don't have permission, we will fail later.
980 heim_base_atomic_store(pcache_id
, 0);
989 /* Destroy the cache keyring */
990 static krb5_error_code KRB5_CALLCONV
991 krcc_destroy(krb5_context context
, krb5_ccache id
)
993 krb5_error_code ret
= 0;
994 krb5_krcache
*data
= KRCACHE(id
);
998 return krb5_einval(context
, 2);
1000 /* no atomics, destroy is not thread-safe */
1001 (void) clear_cache_keyring(context
, &data
->krc_cache_id
);
1003 if (data
->krc_cache_id
!= 0) {
1004 res
= keyctl_unlink(data
->krc_cache_id
, data
->krc_coll_id
);
1007 _krb5_debug(context
, 10, "unlinking key %d from ring %d: %s",
1008 data
->krc_cache_id
, data
->krc_coll_id
, error_message(errno
));
1010 /* If this is a legacy cache, unlink it from the session anchor. */
1011 if (is_legacy_cache_name_p(data
->krc_name
))
1012 (void) keyctl_unlink(data
->krc_cache_id
, session_write_anchor());
1015 heim_base_atomic_store(&data
->krc_princ_id
, 0);
1017 /* krcc_close is called by libkrb5, do not double-free */
1021 /* Create a cache handle for a cache ID. */
1022 static krb5_error_code
1023 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
,
1031 krb5_error_code ret
;
1033 key_serial_t princ_id
= 0;
1035 /* Determine the key containing principal information, if present. */
1036 princ_id
= keyctl_search(cache_id
, KRCC_KEY_TYPE_USER
, KRCC_SPEC_PRINC_KEYNAME
, 0);
1040 ret
= alloc_cache(context
, collection_id
, cache_id
,
1041 anchor_name
, collection_name
, subsidiary_name
, &data
);
1045 if (*cache
== NULL
) {
1046 ret
= _krb5_cc_allocate(context
, &krb5_krcc_ops
, cache
);
1048 free(data
->krc_name
);
1054 data
->krc_princ_id
= princ_id
;
1056 (*cache
)->data
.data
= data
;
1057 (*cache
)->data
.length
= sizeof(*data
);
1062 /* Create a keyring ccache handle for the given residual string. */
1063 static krb5_error_code KRB5_CALLCONV
1064 krcc_resolve_2(krb5_context context
,
1066 const char *residual
,
1069 krb5_error_code ret
;
1070 atomic_key_serial_t collection_id
;
1071 key_serial_t cache_id
;
1072 char *anchor_name
= NULL
, *collection_name
= NULL
, *subsidiary_name
= NULL
;
1074 ret
= parse_residual(context
, residual
, &anchor_name
, &collection_name
,
1079 free(subsidiary_name
);
1080 if ((subsidiary_name
= strdup(sub
)) == NULL
) {
1081 ret
= krb5_enomem(context
);
1086 ret
= get_collection(context
, anchor_name
, collection_name
, &collection_id
);
1090 if (subsidiary_name
== NULL
) {
1091 /* Retrieve or initialize the primary name for the collection. */
1092 ret
= get_primary_name(context
, anchor_name
, collection_name
,
1093 collection_id
, &subsidiary_name
);
1098 /* Look up the cache keyring ID, if the cache is already initialized. */
1099 cache_id
= keyctl_search(collection_id
, KRCC_KEY_TYPE_KEYRING
,
1100 subsidiary_name
, 0);
1104 ret
= make_cache(context
, collection_id
, cache_id
, anchor_name
,
1105 collection_name
, subsidiary_name
, id
);
1111 free(collection_name
);
1112 free(subsidiary_name
);
1117 struct krcc_cursor
{
1120 key_serial_t princ_id
;
1121 key_serial_t offsets_id
;
1125 /* Prepare for a sequential iteration over the cache keyring. */
1126 static krb5_error_code
1127 krcc_get_first(krb5_context context
,
1129 krb5_cc_cursor
*cursor
)
1131 struct krcc_cursor
*krcursor
;
1132 krb5_krcache
*data
= KRCACHE(id
);
1133 key_serial_t cache_id
;
1138 return krb5_einval(context
, 2);
1140 cache_id
= heim_base_atomic_load(&data
->krc_cache_id
);
1142 return KRB5_FCC_NOFILE
;
1144 size
= keyctl_read_alloc(cache_id
, &keys
);
1146 _krb5_debug(context
, 10, "Error getting from keyring: %s\n",
1151 krcursor
= calloc(1, sizeof(*krcursor
));
1152 if (krcursor
== NULL
) {
1154 return KRB5_CC_NOMEM
;
1157 krcursor
->princ_id
= heim_base_atomic_load(&data
->krc_princ_id
);
1158 krcursor
->offsets_id
= keyctl_search(cache_id
, KRCC_KEY_TYPE_USER
,
1159 KRCC_TIME_OFFSETS
, 0);
1160 krcursor
->numkeys
= size
/ sizeof(key_serial_t
);
1161 krcursor
->keys
= keys
;
1168 static krb5_error_code
1169 keyctl_read_krb5_data(key_serial_t keyid
, krb5_data
*payload
)
1171 krb5_data_zero(payload
);
1173 payload
->length
= keyctl_read_alloc(keyid
, &payload
->data
);
1175 return (payload
->length
== -1) ? KRB5_FCC_NOFILE
: 0;
1178 /* Get the next credential from the cache keyring. */
1179 static krb5_error_code KRB5_CALLCONV
1180 krcc_get_next(krb5_context context
,
1182 krb5_cc_cursor
*cursor
,
1185 struct krcc_cursor
*krcursor
;
1186 krb5_error_code ret
;
1190 memset(creds
, 0, sizeof(krb5_creds
));
1193 if (krcursor
== NULL
)
1196 if (krcursor
->currkey
>= krcursor
->numkeys
)
1200 * If we're pointing at the entry with the principal, or at the key
1201 * with the time offsets, skip it.
1203 while (krcursor
->keys
[krcursor
->currkey
] == krcursor
->princ_id
||
1204 krcursor
->keys
[krcursor
->currkey
] == krcursor
->offsets_id
) {
1205 krcursor
->currkey
++;
1206 if (krcursor
->currkey
>= krcursor
->numkeys
)
1210 ret
= keyctl_read_krb5_data(krcursor
->keys
[krcursor
->currkey
], &payload
);
1212 _krb5_debug(context
, 10, "Error reading key %d: %s\n",
1213 krcursor
->keys
[krcursor
->currkey
],
1217 krcursor
->currkey
++;
1219 sp
= krb5_storage_from_data(&payload
);
1223 ret
= krb5_ret_creds(sp
, creds
);
1224 krb5_storage_free(sp
);
1227 krb5_data_free(&payload
);
1232 /* Release an iteration cursor. */
1233 static krb5_error_code KRB5_CALLCONV
1234 krcc_end_get(krb5_context context
, krb5_ccache id
, krb5_cc_cursor
*cursor
)
1236 struct krcc_cursor
*krcursor
= *cursor
;
1238 if (krcursor
!= NULL
) {
1239 free(krcursor
->keys
);
1248 /* Create keyring data for a credential cache. */
1249 static krb5_error_code
1250 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
)
1258 krb5_error_code ret
;
1263 data
= calloc(1, sizeof(*data
));
1265 return KRB5_CC_NOMEM
;
1267 ret
= make_subsidiary_residual(context
, anchor_name
, collection_name
,
1268 subsidiary_name
, &data
->krc_name
);
1270 (data
->krc_collection
= strdup(collection_name
)) == NULL
||
1271 (data
->krc_subsidiary
= strdup(subsidiary_name
? subsidiary_name
: "tkt")) == NULL
) {
1273 free(data
->krc_collection
);
1274 free(data
->krc_name
);
1278 ret
= krb5_enomem(context
);
1282 heim_base_atomic_init(&data
->krc_princ_id
, 0);
1283 heim_base_atomic_init(&data
->krc_cache_id
, cache_id
);
1284 data
->krc_coll_id
= collection_id
;
1285 data
->krc_changetime
= 0;
1286 data
->krc_is_legacy
= (strcmp(anchor_name
, KRCC_LEGACY_ANCHOR
) == 0);
1288 update_change_time(context
, 0, data
);
1295 /* Create a new keyring cache with a unique name. */
1296 static krb5_error_code KRB5_CALLCONV
1297 krcc_gen_new(krb5_context context
, krb5_ccache
*id
)
1299 krb5_error_code ret
;
1300 char *anchor_name
, *collection_name
, *subsidiary_name
;
1301 char *new_subsidiary_name
= NULL
, *new_residual
= NULL
;
1303 atomic_key_serial_t collection_id
;
1304 key_serial_t cache_id
= 0;
1306 /* Determine the collection in which we will create the cache.*/
1307 ret
= get_default(context
, &anchor_name
, &collection_name
,
1312 if (anchor_name
== NULL
) {
1313 ret
= parse_residual(context
, KRCC_DEFAULT_UNIQUE_COLLECTION
, &anchor_name
,
1314 &collection_name
, &subsidiary_name
);
1318 if (subsidiary_name
!= NULL
) {
1319 krb5_set_error_message(context
, KRB5_DCC_CANNOT_CREATE
,
1320 N_("Can't create new subsidiary cache because default cache "
1321 "is already a subsidiary", ""));
1322 ret
= KRB5_DCC_CANNOT_CREATE
;
1326 /* Make a unique keyring within the chosen collection. */
1327 ret
= get_collection(context
, anchor_name
, collection_name
, &collection_id
);
1331 ret
= add_unique_keyring(context
, collection_id
, &new_subsidiary_name
, &cache_id
);
1335 ret
= alloc_cache(context
, collection_id
, cache_id
,
1336 anchor_name
, collection_name
, new_subsidiary_name
,
1341 (*id
)->data
.data
= data
;
1342 (*id
)->data
.length
= sizeof(*data
);
1346 free(collection_name
);
1347 free(subsidiary_name
);
1348 free(new_subsidiary_name
);
1354 /* Return an alias to the residual string of the cache. */
1355 static krb5_error_code KRB5_CALLCONV
1356 krcc_get_name_2(krb5_context context
,
1359 const char **collection_name
,
1360 const char **subsidiary_name
)
1362 krb5_krcache
*data
= KRCACHE(id
);
1365 return krb5_einval(context
, 2);
1368 *name
= data
->krc_name
;
1369 if (collection_name
)
1370 *collection_name
= data
->krc_collection
;
1371 if (subsidiary_name
)
1372 *subsidiary_name
= data
->krc_subsidiary
;
1376 /* Retrieve a copy of the default principal, if the cache is initialized. */
1377 static krb5_error_code KRB5_CALLCONV
1378 krcc_get_principal(krb5_context context
,
1380 krb5_principal
*princ
)
1382 krb5_krcache
*data
= KRCACHE(id
);
1383 krb5_error_code ret
;
1384 krb5_storage
*sp
= NULL
;
1386 krb5_krcache_and_princ_id ids
;
1388 krb5_data_zero(&payload
);
1392 return krb5_einval(context
, 2);
1394 memset(&ids
, 0, sizeof(ids
));
1395 ids
.krcu_cache_and_princ_id
= heim_base_atomic_load(&data
->krc_cache_and_principal_id
);
1396 if (ids
.krcu_cache_id
== 0 || ids
.krcu_princ_id
== 0) {
1397 ret
= KRB5_FCC_NOFILE
;
1398 krb5_set_error_message(context
, ret
,
1399 N_("Credentials cache keyring '%s' not found", ""),
1404 ret
= keyctl_read_krb5_data(ids
.krcu_princ_id
, &payload
);
1406 _krb5_debug(context
, 10, "Reading principal key %d: %s\n",
1407 ids
.krcu_princ_id
, strerror(errno
));
1411 sp
= krb5_storage_from_data(&payload
);
1417 ret
= krb5_ret_principal(sp
, princ
);
1422 krb5_storage_free(sp
);
1423 krb5_data_free(&payload
);
1428 /* Remove a cred from the cache keyring */
1429 static krb5_error_code KRB5_CALLCONV
1430 krcc_remove_cred(krb5_context context
, krb5_ccache id
,
1431 krb5_flags which
, krb5_creds
*mcred
)
1433 krb5_krcache
*data
= KRCACHE(id
);
1434 krb5_error_code ret
, ret2
;
1435 krb5_cc_cursor cursor
;
1436 krb5_creds found_cred
;
1437 krb5_krcache_and_princ_id ids
;
1440 return krb5_einval(context
, 2);
1442 ret
= krcc_get_first(context
, id
, &cursor
);
1446 memset(&ids
, 0, sizeof(ids
));
1447 ids
.krcu_cache_and_princ_id
= heim_base_atomic_load(&data
->krc_cache_and_principal_id
);
1449 while ((ret
= krcc_get_next(context
, id
, &cursor
, &found_cred
)) == 0) {
1450 struct krcc_cursor
*krcursor
= cursor
;
1452 if (!krb5_compare_creds(context
, which
, mcred
, &found_cred
)) {
1453 krb5_free_cred_contents(context
, &found_cred
);
1457 _krb5_debug(context
, 10, "Removing cred %d from cache_id %d, princ_id %d\n",
1458 krcursor
->keys
[krcursor
->currkey
- 1],
1459 ids
.krcu_cache_id
, ids
.krcu_princ_id
);
1461 keyctl_invalidate(krcursor
->keys
[krcursor
->currkey
- 1]);
1462 krcursor
->keys
[krcursor
->currkey
- 1] = 0;
1463 krb5_free_cred_contents(context
, &found_cred
);
1466 ret2
= krcc_end_get(context
, id
, &cursor
);
1467 if (ret
== KRB5_CC_END
)
1473 /* Set flags on the cache. (We don't care about any flags.) */
1474 static krb5_error_code KRB5_CALLCONV
1475 krcc_set_flags(krb5_context context
, krb5_ccache id
, krb5_flags flags
)
1480 static int KRB5_CALLCONV
1481 krcc_get_version(krb5_context context
, krb5_ccache id
)
1486 /* Store a credential in the cache keyring. */
1487 static krb5_error_code KRB5_CALLCONV
1488 krcc_store(krb5_context context
, krb5_ccache id
, krb5_creds
*creds
)
1490 krb5_error_code ret
;
1491 krb5_krcache
*data
= KRCACHE(id
);
1492 krb5_storage
*sp
= NULL
;
1493 char *keyname
= NULL
;
1494 key_serial_t cred_key
, cache_id
;
1498 krb5_data_zero(&payload
);
1501 return krb5_einval(context
, 2);
1503 cache_id
= heim_base_atomic_load(&data
->krc_cache_id
);
1505 return KRB5_FCC_NOFILE
;
1507 ret
= krb5_unparse_name(context
, creds
->server
, &keyname
);
1511 sp
= krb5_storage_emem();
1513 krb5_set_error_message(context
, KRB5_CC_NOMEM
, N_("malloc: out of memory", ""));
1514 ret
= KRB5_CC_NOMEM
;
1518 ret
= krb5_store_creds(sp
, creds
);
1522 ret
= krb5_storage_to_data(sp
, &payload
);
1526 _krb5_debug(context
, 10, "krcc_store: adding new key '%s' to keyring %d\n",
1528 ret
= add_cred_key(keyname
, payload
.data
, payload
.length
, cache_id
,
1529 data
->krc_is_legacy
, &cred_key
);
1533 ret
= krb5_timeofday(context
, &now
);
1537 update_change_time(context
, now
, data
);
1539 /* Set timeout on credential key */
1540 if (creds
->times
.endtime
> now
)
1541 (void) keyctl_set_timeout(cred_key
, creds
->times
.endtime
- now
);
1543 /* Set timeout on credential cache keyring */
1544 update_keyring_expiration(context
, id
, cache_id
, now
);
1547 krb5_data_free(&payload
);
1548 krb5_storage_free(sp
);
1549 krb5_xfree(keyname
);
1555 * Get the cache's last modification time. (This is currently broken; it
1556 * returns only the last change made using this handle.)
1558 static krb5_error_code KRB5_CALLCONV
1559 krcc_lastchange(krb5_context context
,
1561 krb5_timestamp
*change_time
)
1563 krb5_krcache
*data
= KRCACHE(id
);
1566 return krb5_einval(context
, 2);
1568 *change_time
= heim_base_atomic_load(&data
->krc_changetime
);
1573 static krb5_error_code
1574 save_principal(krb5_context context
,
1575 key_serial_t cache_id
,
1576 krb5_const_principal princ
,
1577 atomic_key_serial_t
*pprinc_id
)
1579 krb5_error_code ret
;
1581 key_serial_t newkey
;
1584 krb5_data_zero(&payload
);
1586 sp
= krb5_storage_emem();
1588 krb5_set_error_message(context
, KRB5_CC_NOMEM
, N_("malloc: out of memory", ""));
1589 return KRB5_CC_NOMEM
;
1592 ret
= krb5_store_principal(sp
, princ
);
1594 krb5_storage_free(sp
);
1598 ret
= krb5_storage_to_data(sp
, &payload
);
1600 krb5_storage_free(sp
);
1604 krb5_storage_free(sp
);
1606 krb5_error_code tmp
;
1607 char *princname
= NULL
;
1609 tmp
= krb5_unparse_name(context
, princ
, &princname
);
1610 _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
);
1615 krb5_xfree(princname
);
1618 /* Add new key into keyring */
1619 newkey
= add_key(KRCC_KEY_TYPE_USER
, KRCC_SPEC_PRINC_KEYNAME
,
1620 payload
.data
, payload
.length
, cache_id
);
1623 _krb5_debug(context
, 10, "Error adding principal key: %s\n", strerror(ret
));
1626 heim_base_atomic_store(pprinc_id
, newkey
);
1629 krb5_data_free(&payload
);
1634 /* Add a key to the cache keyring containing the given time offsets. */
1635 static krb5_error_code
1636 save_time_offsets(krb5_context context
,
1637 key_serial_t cache_id
,
1639 int32_t usec_offset
)
1641 krb5_error_code ret
;
1642 key_serial_t newkey
;
1646 krb5_data_zero(&payload
);
1648 sp
= krb5_storage_emem();
1650 krb5_set_error_message(context
, KRB5_CC_NOMEM
, N_("malloc: out of memory", ""));
1651 return KRB5_CC_NOMEM
;
1654 krb5_storage_set_byteorder(sp
, KRB5_STORAGE_BYTEORDER_BE
);
1656 ret
= krb5_store_int32(sp
, sec_offset
);
1658 ret
= krb5_store_int32(sp
, usec_offset
);
1660 krb5_storage_free(sp
);
1664 ret
= krb5_storage_to_data(sp
, &payload
);
1666 krb5_storage_free(sp
);
1670 krb5_storage_free(sp
);
1672 newkey
= add_key(KRCC_KEY_TYPE_USER
, KRCC_TIME_OFFSETS
, payload
.data
,
1673 payload
.length
, cache_id
);
1674 ret
= newkey
== -1 ? errno
: 0;
1676 krb5_data_free(&payload
);
1681 static krb5_error_code KRB5_CALLCONV
1682 krcc_set_kdc_offset(krb5_context context
, krb5_ccache id
, krb5_deltat offset
)
1684 krb5_krcache
*data
= KRCACHE(id
);
1685 key_serial_t cache_id
;
1686 krb5_error_code ret
;
1689 return krb5_einval(context
, 2);
1691 cache_id
= heim_base_atomic_load(&data
->krc_cache_id
);
1693 ret
= save_time_offsets(context
, cache_id
, (int32_t)offset
, 0);
1695 update_change_time(context
, 0, data
);
1700 /* Retrieve and parse the key in the cache keyring containing time offsets. */
1701 static krb5_error_code KRB5_CALLCONV
1702 krcc_get_kdc_offset(krb5_context context
,
1704 krb5_deltat
*offset
)
1706 krb5_krcache
*data
= KRCACHE(id
);
1707 krb5_error_code ret
= 0;
1708 key_serial_t key
, cache_id
;
1709 krb5_storage
*sp
= NULL
;
1711 int32_t sec_offset
= 0;
1714 return krb5_einval(context
, 2);
1716 krb5_data_zero(&payload
);
1718 cache_id
= heim_base_atomic_load(&data
->krc_cache_id
);
1719 if (cache_id
== 0) {
1720 ret
= KRB5_FCC_NOFILE
;
1724 key
= keyctl_search(cache_id
, KRCC_KEY_TYPE_USER
, KRCC_TIME_OFFSETS
, 0);
1730 ret
= keyctl_read_krb5_data(key
, &payload
);
1732 _krb5_debug(context
, 10, "Reading time offsets key %d: %s\n",
1733 key
, strerror(errno
));
1737 sp
= krb5_storage_from_data(&payload
);
1739 ret
= krb5_enomem(context
);;
1743 krb5_storage_set_byteorder(sp
, KRB5_STORAGE_BYTEORDER_BE
);
1745 ret
= krb5_ret_int32(sp
, &sec_offset
);
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.
1752 *offset
= sec_offset
;
1753 krb5_storage_free(sp
);
1754 krb5_data_free(&payload
);
1759 atomic_key_serial_t collection_id
;
1761 char *collection_name
;
1762 char *subsidiary_name
;
1770 static krb5_error_code KRB5_CALLCONV
1771 krcc_get_cache_first(krb5_context context
, krb5_cc_cursor
*cursor
)
1773 struct krcc_iter
*iter
;
1774 krb5_error_code ret
;
1780 iter
= calloc(1, sizeof(*iter
));
1782 ret
= krb5_enomem(context
);
1787 ret
= get_default(context
, &iter
->anchor_name
, &iter
->collection_name
,
1788 &iter
->subsidiary_name
);
1792 /* If there is no default collection, return an empty cursor. */
1793 if (iter
->anchor_name
== NULL
) {
1798 ret
= get_collection(context
, iter
->anchor_name
, iter
->collection_name
,
1799 &iter
->collection_id
);
1803 if (iter
->subsidiary_name
== NULL
) {
1804 ret
= get_primary_name(context
, iter
->anchor_name
,
1805 iter
->collection_name
, iter
->collection_id
,
1806 &iter
->primary_name
);
1810 size
= keyctl_read_alloc(iter
->collection_id
, &keys
);
1816 iter
->num_keys
= size
/ sizeof(key_serial_t
);
1824 krcc_end_cache_get(context
, iter
);
1829 static krb5_error_code KRB5_CALLCONV
1830 krcc_get_cache_next(krb5_context context
,
1831 krb5_cc_cursor cursor
,
1834 krb5_error_code ret
;
1835 struct krcc_iter
*iter
= cursor
;
1836 key_serial_t key
, cache_id
= 0;
1837 const char *first_name
, *keytype
, *sep
, *subsidiary_name
;
1839 char *description
= NULL
;
1843 /* No keyring available */
1844 if (iter
->collection_id
== 0)
1849 * Look for the primary cache for a collection cursor, or the
1850 * subsidiary cache for a subsidiary cursor.
1852 iter
->first
= FALSE
;
1853 first_name
= (iter
->primary_name
!= NULL
) ? iter
->primary_name
:
1854 iter
->subsidiary_name
;
1855 cache_id
= keyctl_search(iter
->collection_id
, KRCC_KEY_TYPE_KEYRING
,
1857 if (cache_id
!= -1) {
1858 return make_cache(context
, iter
->collection_id
, cache_id
,
1859 iter
->anchor_name
, iter
->collection_name
,
1864 /* A subsidiary cursor yields at most the first cache. */
1865 if (iter
->subsidiary_name
!= NULL
)
1868 keytype
= KRCC_KEY_TYPE_KEYRING
";";
1869 keytypelen
= strlen(keytype
);
1871 for (ret
= KRB5_CC_END
; iter
->next_key
< iter
->num_keys
; iter
->next_key
++) {
1876 * Get the key description, which should have the form:
1877 * typename;UID;GID;permissions;description
1879 key
= iter
->keys
[iter
->next_key
];
1880 if (keyctl_describe_alloc(key
, &description
) < 0)
1882 sep
= strrchr(description
, ';');
1885 subsidiary_name
= sep
+ 1;
1887 /* Skip this key if it isn't a keyring. */
1888 if (strncmp(description
, keytype
, keytypelen
) != 0)
1891 /* Don't repeat the primary cache. */
1892 if (iter
->primary_name
&&
1893 strcmp(subsidiary_name
, iter
->primary_name
) == 0)
1896 /* We found a valid key */
1898 ret
= make_cache(context
, iter
->collection_id
, key
, iter
->anchor_name
,
1899 iter
->collection_name
, subsidiary_name
, cache
);
1908 static krb5_error_code KRB5_CALLCONV
1909 krcc_end_cache_get(krb5_context context
, krb5_cc_cursor cursor
)
1911 struct krcc_iter
*iter
= cursor
;
1914 free(iter
->anchor_name
);
1915 free(iter
->collection_name
);
1916 free(iter
->subsidiary_name
);
1917 free(iter
->primary_name
);
1920 memset(iter
, 0, sizeof(*iter
));
1927 static krb5_error_code KRB5_CALLCONV
1928 krcc_set_default(krb5_context context
, krb5_ccache id
)
1930 krb5_krcache
*data
= KRCACHE(id
);
1931 krb5_error_code ret
;
1932 char *anchor_name
, *collection_name
, *subsidiary_name
;
1933 atomic_key_serial_t collection_id
;
1936 return krb5_einval(context
, 2);
1938 ret
= parse_residual(context
, data
->krc_name
,
1939 &anchor_name
, &collection_name
, &subsidiary_name
);
1943 ret
= get_collection(context
, anchor_name
, collection_name
, &collection_id
);
1947 ret
= set_primary_name(context
, collection_id
, subsidiary_name
);
1953 free(collection_name
);
1954 free(subsidiary_name
);
1960 * Utility routine: called by krcc_* functions to keep
1961 * result of krcc_last_change_time up to date.
1964 update_change_time(krb5_context context
, krb5_timestamp now
, krb5_krcache
*data
)
1969 krb5_timeofday(context
, &now
);
1971 old
= heim_base_exchange_time_t(&data
->krc_changetime
, now
);
1972 if (old
> now
) /* don't go backwards */
1973 heim_base_atomic_store(&data
->krc_changetime
, old
+ 1);
1977 move_key_to_new_keyring(key_serial_t parent
, key_serial_t key
,
1978 char *desc
, int desc_len
, void *data
)
1980 key_serial_t cache_id
= *(key_serial_t
*)data
;
1983 if (keyctl_link(key
, cache_id
) == -1 ||
1984 keyctl_unlink(key
, parent
) == -1)
1991 /* Move contents of one ccache to another; destroys from cache */
1992 static krb5_error_code KRB5_CALLCONV
1993 krcc_move(krb5_context context
, krb5_ccache from
, krb5_ccache to
)
1995 krb5_krcache
*krfrom
= KRCACHE(from
);
1996 krb5_krcache
*krto
= KRCACHE(to
);
1997 krb5_error_code ret
;
1999 key_serial_t to_cache_id
;
2001 if (krfrom
== NULL
|| krto
== NULL
)
2002 return krb5_einval(context
, 2);
2004 ret
= initialize_internal(context
, to
, NULL
);
2008 krb5_timeofday(context
, &now
);
2009 to_cache_id
= heim_base_atomic_load(&krto
->krc_cache_id
);
2011 if (krfrom
->krc_cache_id
!= 0) {
2012 ret
= recursive_key_scan(krfrom
->krc_cache_id
,
2013 move_key_to_new_keyring
, &to_cache_id
);
2017 if (keyctl_unlink(krfrom
->krc_cache_id
, krfrom
->krc_coll_id
) == -1)
2020 heim_base_exchange_32(&krto
->krc_princ_id
, krfrom
->krc_princ_id
);
2023 update_change_time(context
, now
, krto
);
2024 krb5_cc_destroy(context
, from
);
2028 static krb5_error_code KRB5_CALLCONV
2029 krcc_get_default_name(krb5_context context
, char **str
)
2031 *str
= strdup("KEYRING:");
2033 return krb5_enomem(context
);
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.
2044 KRB5_LIB_VARIABLE
const krb5_cc_ops krb5_krcc_ops
= {
2045 KRB5_CC_OPS_VERSION_5
,
2054 NULL
, /* retrieve */
2062 krcc_get_cache_first
,
2063 krcc_get_cache_next
,
2066 krcc_get_default_name
,
2069 krcc_set_kdc_offset
,
2070 krcc_get_kdc_offset
,
2075 #endif /* HAVE_KEYUTILS_H */