lib/krb5: krb5_init_creds_set_service fail if set_realm fails
[heimdal.git] / lib / krb5 / krcache.c
blob395b8deb094217acb97e937d7daebe73fba90d81
1 /*
2 * Copyright (c) 2006 The Regents of the University of Michigan.
3 * All rights reserved.
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
16 * also be included.
18 * THIS SOFTWARE IS PROVIDED AS IS, WITHOUT REPRESENTATION
19 * FROM THE UNIVERSITY OF MICHIGAN AS TO ITS FITNESS FOR ANY
20 * PURPOSE, AND WITHOUT WARRANTY BY THE UNIVERSITY OF
21 * MICHIGAN OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING
22 * WITHOUT LIMITATION THE IMPLIED WARRANTIES OF
23 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE
24 * REGENTS OF THE UNIVERSITY OF MICHIGAN SHALL NOT BE LIABLE
25 * FOR ANY DAMAGES, INCLUDING SPECIAL, INDIRECT, INCIDENTAL, OR
26 * CONSEQUENTIAL DAMAGES, WITH RESPECT TO ANY CLAIM ARISING
27 * OUT OF OR IN CONNECTION WITH THE USE OF THE SOFTWARE, EVEN
28 * IF IT HAS BEEN OR IS HEREAFTER ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGES.
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
63 * anchor:collection
64 * collection
66 * The anchor name is "process", "thread", or "legacy" and determines where we
67 * search for keyring collections. In the third form, the anchor name is
68 * presumed to be "legacy". The anchor keyring for legacy caches is the
69 * session keyring.
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"
110 #else
111 #define KRCC_CRED_KEY_TYPE "user"
112 #endif
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
188 #endif
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;
194 struct {
195 atomic_key_serial_t cache_id; /* keyring ID representing ccache */
196 atomic_key_serial_t princ_id; /* key ID holding principal info */
197 } krcu_id;
198 #define krcu_cache_id krcu_id.cache_id
199 #define krcu_princ_id krcu_id.princ_id
200 } krb5_krcache_and_princ_id;
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
208 * using them.
210 typedef struct _krb5_krcache {
211 char *krc_name; /* Name for this credentials cache */
212 char *krc_collection;
213 char *krc_subsidiary;
214 heim_base_atomic(krb5_timestamp) krc_changetime; /* update time, does not decrease (mutable) */
215 krb5_krcache_and_princ_id krc_id; /* cache and principal IDs (mutable) */
216 #define krc_cache_and_principal_id krc_id.krcu_cache_and_princ_id
217 #define krc_cache_id krc_id.krcu_id.cache_id
218 #define krc_princ_id krc_id.krcu_id.princ_id
219 key_serial_t krc_coll_id; /* collection containing this cache keyring */
220 krb5_boolean krc_is_legacy; /* */
221 } krb5_krcache;
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,
230 krb5_ccache id,
231 krb5_cc_cursor *cursor,
232 krb5_creds *creds);
234 static krb5_error_code KRB5_CALLCONV
235 krcc_end_get(krb5_context context,
236 krb5_ccache id,
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,
263 int32_t sec_offset,
264 int32_t usec_offset);
266 static void
267 update_change_time(krb5_context context,
268 krb5_timestamp now,
269 krb5_krcache *d);
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.
276 static key_serial_t
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
284 static key_serial_t
285 get_persistent_real(uid_t uid)
287 key_serial_t key;
289 key = keyctl_get_persistent(uid, KEY_SPEC_PROCESS_KEYRING);
291 return (key == -1 && errno == ENOTSUP) ? get_persistent_fallback(uid) : key;
293 #else
294 #define GET_PERSISTENT get_persistent_fallback
295 #endif
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.
307 static key_serial_t
308 session_write_anchor(void)
310 key_serial_t s, u;
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,
327 const char *name,
328 atomic_key_serial_t *pkey)
330 key_serial_t key;
332 key = keyctl_search(parent, KRCC_KEY_TYPE_KEYRING, name, possess);
333 if (key == -1) {
334 if (possess != 0) {
335 key = add_key(KRCC_KEY_TYPE_KEYRING, name, NULL, 0, possess);
336 if (key == -1 || keyctl_link(key, parent) == -1)
337 return errno;
338 } else {
339 key = add_key(KRCC_KEY_TYPE_KEYRING, name, NULL, 0, parent);
340 if (key == -1)
341 return errno;
345 heim_base_atomic_store(pkey, key);
347 return 0;
351 * Parse a residual name into an anchor name, a collection name, and possibly a
352 * subsidiary name.
354 static krb5_error_code
355 parse_residual(krb5_context context,
356 const char *residual,
357 char **panchor_name,
358 char **pcollection_name,
359 char **psubsidiary_name)
361 char *anchor_name = NULL;
362 char *collection_name = NULL;
363 char *subsidiary_name = NULL;
364 const char *sep;
366 *panchor_name = NULL;
367 *pcollection_name = NULL;
368 *psubsidiary_name = NULL;
370 /* Parse out the anchor name. Use the legacy anchor if not present. */
371 sep = strchr(residual, ':');
372 if (sep == NULL) {
373 anchor_name = strdup(KRCC_LEGACY_ANCHOR);
374 if (anchor_name == NULL)
375 goto nomem;
376 } else {
377 anchor_name = strndup(residual, sep - residual);
378 if (anchor_name == NULL)
379 goto nomem;
380 residual = sep + 1;
383 /* Parse out the collection and subsidiary name. */
384 sep = strchr(residual, ':');
385 if (sep == NULL) {
386 collection_name = strdup(residual);
387 if (collection_name == NULL)
388 goto nomem;
389 } else {
390 collection_name = strndup(residual, sep - residual);
391 if (collection_name == NULL)
392 goto nomem;
394 subsidiary_name = strdup(sep + 1);
395 if (subsidiary_name == NULL)
396 goto nomem;
399 *panchor_name = anchor_name;
400 *pcollection_name = collection_name;
401 *psubsidiary_name = subsidiary_name;
403 return 0;
405 nomem:
406 free(anchor_name);
407 free(collection_name);
408 free(subsidiary_name);
410 return krb5_enomem(context);
414 * Return TRUE if residual identifies a subsidiary cache which should be linked
415 * into the anchor so it can be visible to old code. This is the case if the
416 * residual has the legacy anchor and the subsidiary name matches the
417 * collection name.
419 static krb5_boolean
420 is_legacy_cache_name_p(const char *residual)
422 const char *sep, *aname, *cname, *sname;
423 size_t alen, clen, legacy_len = sizeof(KRCC_LEGACY_ANCHOR) - 1;
425 /* Get pointers to the anchor, collection, and subsidiary names. */
426 aname = residual;
427 sep = strchr(residual, ':');
428 if (sep == NULL)
429 return FALSE;
431 alen = sep - aname;
432 cname = sep + 1;
433 sep = strchr(cname, ':');
434 if (sep == NULL)
435 return FALSE;
437 clen = sep - cname;
438 sname = sep + 1;
440 return alen == legacy_len && clen == strlen(sname) &&
441 strncmp(aname, KRCC_LEGACY_ANCHOR, alen) == 0 &&
442 strncmp(cname, sname, clen) == 0;
446 * If the default cache name for context is a KEYRING cache, parse its residual
447 * string. Otherwise set all outputs to NULL.
449 static krb5_error_code
450 get_default(krb5_context context,
451 char **panchor_name,
452 char **pcollection_name,
453 char **psubsidiary_name)
455 const char *defname;
457 *panchor_name = *pcollection_name = *psubsidiary_name = NULL;
459 defname = krb5_cc_default_name(context);
460 if (defname == NULL || strncmp(defname, "KEYRING:", 8) != 0)
461 return 0;
463 return parse_residual(context, defname + 8,
464 panchor_name, pcollection_name, psubsidiary_name);
467 /* Create a residual identifying a subsidiary cache. */
468 static krb5_error_code
469 make_subsidiary_residual(krb5_context context,
470 const char *anchor_name,
471 const char *collection_name,
472 const char *subsidiary_name,
473 char **presidual)
475 if (asprintf(presidual, "%s:%s:%s", anchor_name, collection_name,
476 subsidiary_name ? subsidiary_name : "tkt") < 0) {
477 *presidual = NULL;
478 return krb5_enomem(context);
481 return 0;
485 * Retrieve or create a keyring for collection_name within the anchor, and set
486 * *collection_id to its serial number.
488 static krb5_error_code
489 get_collection(krb5_context context,
490 const char *anchor_name,
491 const char *collection_name,
492 atomic_key_serial_t *pcollection_id)
494 krb5_error_code ret;
495 key_serial_t persistent_id, anchor_id, possess_id = 0;
496 char *ckname, *cnend;
497 uid_t uidnum;
499 heim_base_atomic_init(pcollection_id, 0);
501 if (!anchor_name || !collection_name)
502 return KRB5_KCC_INVALID_ANCHOR;
504 if (strcmp(anchor_name, KRCC_PERSISTENT_ANCHOR) == 0) {
506 * The collection name is a uid (or empty for the current effective
507 * uid), and we look up a fixed keyring name within the persistent
508 * keyring for that uid. We link it to the process keyring to ensure
509 * that we have possession rights on the collection key.
511 if (*collection_name != '\0') {
512 errno = 0;
513 uidnum = (uid_t)strtol(collection_name, &cnend, 10);
514 if (errno || *cnend != '\0')
515 return KRB5_KCC_INVALID_UID;
516 } else {
517 uidnum = geteuid();
520 persistent_id = GET_PERSISTENT(uidnum);
521 if (persistent_id == -1)
522 return KRB5_KCC_INVALID_UID;
524 return find_or_create_keyring(persistent_id, KEY_SPEC_PROCESS_KEYRING,
525 KRCC_PERSISTENT_KEYRING_NAME,
526 pcollection_id);
529 if (strcmp(anchor_name, KRCC_PROCESS_ANCHOR) == 0) {
530 anchor_id = KEY_SPEC_PROCESS_KEYRING;
531 } else if (strcmp(anchor_name, KRCC_THREAD_ANCHOR) == 0) {
532 anchor_id = KEY_SPEC_THREAD_KEYRING;
533 } else if (strcmp(anchor_name, KRCC_SESSION_ANCHOR) == 0) {
534 anchor_id = session_write_anchor();
535 } else if (strcmp(anchor_name, KRCC_USER_ANCHOR) == 0) {
537 * The user keyring does not confer possession, so we need to link the
538 * collection to the process keyring to maintain possession rights.
540 anchor_id = KEY_SPEC_USER_KEYRING;
541 possess_id = KEY_SPEC_PROCESS_KEYRING;
542 } else if (strcmp(anchor_name, KRCC_LEGACY_ANCHOR) == 0) {
543 anchor_id = session_write_anchor();
544 } else {
545 return KRB5_KCC_INVALID_ANCHOR;
548 /* Look up the collection keyring name within the anchor keyring. */
549 if (asprintf(&ckname, "%s%s", KRCC_CCCOL_PREFIX, collection_name) == -1)
550 return krb5_enomem(context);
552 ret = find_or_create_keyring(anchor_id, possess_id, ckname,
553 pcollection_id);
554 free(ckname);
556 return ret;
559 /* Store subsidiary_name into the primary index key for collection_id. */
560 static krb5_error_code
561 set_primary_name(krb5_context context,
562 key_serial_t collection_id,
563 const char *subsidiary_name)
565 krb5_error_code ret;
566 krb5_storage *sp;
567 krb5_data payload;
568 key_serial_t key;
570 sp = krb5_storage_emem();
571 if (sp == NULL) {
572 krb5_set_error_message(context, KRB5_CC_NOMEM, N_("malloc: out of memory", ""));
573 return KRB5_CC_NOMEM;
575 krb5_storage_set_byteorder(sp, KRB5_STORAGE_BYTEORDER_BE);
577 ret = krb5_store_int32(sp, KRCC_COLLECTION_VERSION);
578 if (ret)
579 goto cleanup;
581 ret = krb5_store_string(sp, subsidiary_name);
582 if (ret)
583 goto cleanup;
585 ret = krb5_storage_to_data(sp, &payload);
586 if (ret)
587 goto cleanup;
589 key = add_key(KRCC_KEY_TYPE_USER, KRCC_COLLECTION_PRIMARY,
590 payload.data, payload.length, collection_id);
591 ret = (key == -1) ? errno : 0;
592 krb5_data_free(&payload);
594 cleanup:
595 krb5_storage_free(sp);
597 return ret;
600 static krb5_error_code
601 parse_index(krb5_context context,
602 int32_t *version,
603 char **primary,
604 const unsigned char *payload,
605 size_t psize)
607 krb5_error_code ret;
608 krb5_data payload_data;
609 krb5_storage *sp;
611 payload_data.length = psize;
612 payload_data.data = rk_UNCONST(payload);
614 sp = krb5_storage_from_data(&payload_data);
615 if (sp == NULL)
616 return KRB5_CC_NOMEM;
618 krb5_storage_set_byteorder(sp, KRB5_STORAGE_BYTEORDER_BE);
620 ret = krb5_ret_int32(sp, version);
621 if (ret == 0)
622 ret = krb5_ret_string(sp, primary);
624 krb5_storage_free(sp);
626 return ret;
630 * Get or initialize the primary name within collection_id and set
631 * *subsidiary to its value. If initializing a legacy collection, look
632 * for a legacy cache and add it to the collection.
634 static krb5_error_code
635 get_primary_name(krb5_context context,
636 const char *anchor_name,
637 const char *collection_name,
638 key_serial_t collection_id,
639 char **psubsidiary)
641 krb5_error_code ret;
642 key_serial_t primary_id, legacy;
643 void *payload = NULL;
644 int payloadlen;
645 int32_t version;
646 char *subsidiary_name = NULL;
648 *psubsidiary = NULL;
650 primary_id = keyctl_search(collection_id, KRCC_KEY_TYPE_USER,
651 KRCC_COLLECTION_PRIMARY, 0);
652 if (primary_id == -1) {
654 * Initialize the primary key using the collection name. We can't name
655 * a key with the empty string, so map that to an arbitrary string.
657 subsidiary_name = strdup((*collection_name == '\0') ? "tkt" :
658 collection_name);
659 if (subsidiary_name == NULL) {
660 ret = krb5_enomem(context);
661 goto cleanup;
664 ret = set_primary_name(context, collection_id, subsidiary_name);
665 if (ret)
666 goto cleanup;
668 if (strcmp(anchor_name, KRCC_LEGACY_ANCHOR) == 0) {
670 * Look for a cache created by old code. If we find one, add it to
671 * the collection.
673 legacy = keyctl_search(KEY_SPEC_SESSION_KEYRING,
674 KRCC_KEY_TYPE_KEYRING, subsidiary_name, 0);
675 if (legacy != -1 && keyctl_link(legacy, collection_id) == -1) {
676 ret = errno;
677 goto cleanup;
680 } else {
681 /* Read, parse, and free the primary key's payload. */
682 payloadlen = keyctl_read_alloc(primary_id, &payload);
683 if (payloadlen == -1) {
684 ret = errno;
685 goto cleanup;
687 ret = parse_index(context, &version, &subsidiary_name, payload,
688 payloadlen);
689 if (ret)
690 goto cleanup;
692 if (version != KRCC_COLLECTION_VERSION) {
693 ret = KRB5_KCC_UNKNOWN_VERSION;
694 goto cleanup;
698 *psubsidiary = subsidiary_name;
699 subsidiary_name = NULL;
701 cleanup:
702 free(payload);
703 free(subsidiary_name);
705 return ret;
709 * Note: MIT keyring code uses krb5int_random_string() as if the second argument
710 * is a character count rather than a size. The function below takes a character
711 * count to match the usage in this file correctly.
713 static krb5_error_code
714 generate_random_string(krb5_context context, char *s, size_t slen)
716 static char chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
717 char *p;
718 size_t i;
720 p = malloc(slen);
721 if (p == NULL)
722 return krb5_enomem(context);
724 krb5_generate_random_block(p, slen);
726 for (i = 0; i < slen; i++)
727 s[i] = chars[p[i] % (sizeof(chars) - 1)];
729 s[i] = '\0';
730 free(p);
732 return 0;
736 * Create a keyring with a unique random name within collection_id. Set
737 * *subsidiary to its name and *cache_id to its key serial number.
739 static krb5_error_code
740 add_unique_keyring(krb5_context context,
741 key_serial_t collection_id,
742 char **psubsidiary,
743 key_serial_t *pcache_id)
745 key_serial_t key;
746 krb5_error_code ret;
747 char uniquename[sizeof(KRCC_NAME_PREFIX) + KRCC_NAME_RAND_CHARS];
748 int prefixlen = sizeof(KRCC_NAME_PREFIX) - 1;
749 int tries;
751 *psubsidiary = NULL;
752 *pcache_id = 0;
754 memcpy(uniquename, KRCC_NAME_PREFIX, sizeof(KRCC_NAME_PREFIX));
756 for (key = -1, tries = 0; tries < 5; tries++) {
757 ret = generate_random_string(context, uniquename + prefixlen,
758 KRCC_NAME_RAND_CHARS);
759 if (ret)
760 return ret;
762 key = keyctl_search(collection_id, KRCC_KEY_TYPE_KEYRING, uniquename, 0);
763 if (key == -1) {
764 /* Name does not already exist. Create it to reserve the name. */
765 key = add_key(KRCC_KEY_TYPE_KEYRING, uniquename, NULL, 0, collection_id);
766 if (key == -1)
767 return errno;
768 break;
772 *psubsidiary = strdup(uniquename);
773 if (*psubsidiary == NULL)
774 return krb5_enomem(context);
776 *pcache_id = key;
778 return 0;
781 static krb5_error_code
782 add_cred_key(const char *name,
783 const void *payload,
784 size_t plen,
785 key_serial_t cache_id,
786 krb5_boolean legacy_type,
787 key_serial_t *pkey)
789 key_serial_t key;
791 *pkey = -1;
793 if (!legacy_type) {
794 /* Try the preferred cred key type; fall back if no kernel support. */
795 key = add_key(KRCC_CRED_KEY_TYPE, name, payload, plen, cache_id);
796 if (key != -1) {
797 *pkey = key;
798 return 0;
799 } else if (errno != EINVAL && errno != ENODEV)
800 return errno;
803 /* Use the user key type. */
804 key = add_key(KRCC_KEY_TYPE_USER, name, payload, plen, cache_id);
805 if (key == -1)
806 return errno;
808 *pkey = key;
810 return 0;
813 static void
814 update_keyring_expiration(krb5_context context,
815 krb5_ccache id,
816 key_serial_t cache_id,
817 krb5_timestamp now)
819 krb5_cc_cursor cursor;
820 krb5_creds creds;
821 krb5_timestamp endtime = 0;
822 unsigned int timeout;
825 * We have no way to know what is the actual timeout set on the keyring.
826 * We also cannot keep track of it in a local variable as another process
827 * can always modify the keyring independently, so just always enumerate
828 * all start TGT keys and find out the highest endtime time.
830 if (krcc_get_first(context, id, &cursor) != 0)
831 return;
833 for (;;) {
834 if (krcc_get_next(context, id, &cursor, &creds) != 0)
835 break;
836 if (creds.times.endtime > endtime)
837 endtime = creds.times.endtime;
838 krb5_free_cred_contents(context, &creds);
840 (void) krcc_end_get(context, id, &cursor);
842 if (endtime == 0) /* No creds with end times */
843 return;
846 * Setting the timeout to zero would reset the timeout, so we set it to one
847 * second instead if creds are already expired.
849 timeout = endtime > now ? endtime - now : 1;
850 (void) keyctl_set_timeout(cache_id, timeout);
854 * Create or overwrite the cache keyring, and set the default principal.
856 static krb5_error_code
857 initialize_internal(krb5_context context,
858 krb5_ccache id,
859 krb5_const_principal princ)
861 krb5_krcache *data = KRCACHE(id);
862 krb5_error_code ret;
863 const char *cache_name, *p;
864 krb5_krcache_and_princ_id ids;
866 if (data == NULL)
867 return krb5_einval(context, 2);
869 memset(&ids, 0, sizeof(ids));
870 ids.krcu_cache_and_princ_id = heim_base_atomic_load(&data->krc_cache_and_principal_id);
872 ret = clear_cache_keyring(context, &ids.krcu_cache_id);
873 if (ret)
874 return ret;
876 if (ids.krcu_cache_id == 0) {
878 * The key didn't exist at resolve time, or was destroyed after resolving.
879 * Check again and create the key if it still isn't there.
881 p = strrchr(data->krc_name, ':');
882 cache_name = (p != NULL) ? p + 1 : data->krc_name;
883 ret = find_or_create_keyring(data->krc_coll_id, 0, cache_name, &ids.krcu_cache_id);
884 if (ret)
885 return ret;
889 * If this is the legacy cache in a legacy session collection, link it
890 * directly to the session keyring so that old code can see it.
892 if (is_legacy_cache_name_p(data->krc_name))
893 (void) keyctl_link(ids.krcu_cache_id, session_write_anchor());
895 if (princ != NULL) {
896 ret = save_principal(context, ids.krcu_cache_id, princ, &ids.krcu_princ_id);
897 if (ret)
898 return ret;
899 } else
900 ids.krcu_princ_id = 0;
903 * Save time offset if it is valid and this is not a legacy cache. Legacy
904 * applications would fail to parse the new key in the cache keyring.
906 if (context->kdc_sec_offset && !is_legacy_cache_name_p(data->krc_name)) {
907 ret = save_time_offsets(context,
908 ids.krcu_cache_id,
909 context->kdc_sec_offset,
910 context->kdc_usec_offset);
911 if (ret)
912 return ret;
915 /* update cache and principal IDs atomically */
916 heim_base_atomic_store(&data->krc_cache_and_principal_id, ids.krcu_cache_and_princ_id);
918 return 0;
921 static krb5_error_code KRB5_CALLCONV
922 krcc_initialize(krb5_context context, krb5_ccache id, krb5_principal princ)
924 krb5_krcache *data = KRCACHE(id);
925 krb5_error_code ret;
927 if (data == NULL)
928 return krb5_einval(context, 2);
930 if (princ == NULL)
931 return KRB5_CC_BADNAME;
933 ret = initialize_internal(context, id, princ);
934 if (ret == 0)
935 update_change_time(context, 0, data);
937 return ret;
940 /* Release the ccache handle. */
941 static krb5_error_code KRB5_CALLCONV
942 krcc_close(krb5_context context, krb5_ccache id)
944 krb5_krcache *data = KRCACHE(id);
946 if (data == NULL)
947 return krb5_einval(context, 2);
949 free(data->krc_subsidiary);
950 free(data->krc_collection);
951 free(data->krc_name);
952 krb5_data_free(&id->data);
954 return 0;
958 * Clear out a ccache keyring, unlinking all keys within it.
960 static krb5_error_code
961 clear_cache_keyring(krb5_context context,
962 atomic_key_serial_t *pcache_id)
964 int res;
965 key_serial_t cache_id = heim_base_atomic_load(pcache_id);
967 _krb5_debug(context, 10, "clear_cache_keyring: cache_id %d\n", cache_id);
969 if (cache_id != 0) {
970 res = keyctl_clear(cache_id);
971 if (res == -1 && (errno == EACCES || errno == ENOKEY)) {
973 * Possibly the keyring was destroyed between krcc_resolve() and now;
974 * if we really don't have permission, we will fail later.
976 res = 0;
977 heim_base_atomic_store(pcache_id, 0);
979 if (res == -1)
980 return errno;
983 return 0;
986 /* Destroy the cache keyring */
987 static krb5_error_code KRB5_CALLCONV
988 krcc_destroy(krb5_context context, krb5_ccache id)
990 krb5_error_code ret = 0;
991 krb5_krcache *data = KRCACHE(id);
992 int res;
994 if (data == NULL)
995 return krb5_einval(context, 2);
997 /* no atomics, destroy is not thread-safe */
998 (void) clear_cache_keyring(context, &data->krc_cache_id);
1000 if (data->krc_cache_id != 0) {
1001 res = keyctl_unlink(data->krc_cache_id, data->krc_coll_id);
1002 if (res < 0) {
1003 ret = errno;
1004 _krb5_debug(context, 10, "unlinking key %d from ring %d: %s",
1005 data->krc_cache_id, data->krc_coll_id, error_message(errno));
1007 /* If this is a legacy cache, unlink it from the session anchor. */
1008 if (is_legacy_cache_name_p(data->krc_name))
1009 (void) keyctl_unlink(data->krc_cache_id, session_write_anchor());
1012 heim_base_atomic_store(&data->krc_princ_id, 0);
1014 /* krcc_close is called by libkrb5, do not double-free */
1015 return ret;
1018 /* Create a cache handle for a cache ID. */
1019 static krb5_error_code
1020 make_cache(krb5_context context,
1021 key_serial_t collection_id,
1022 key_serial_t cache_id,
1023 const char *anchor_name,
1024 const char *collection_name,
1025 const char *subsidiary_name,
1026 krb5_ccache *cache)
1028 krb5_error_code ret;
1029 krb5_krcache *data;
1030 key_serial_t princ_id = 0;
1032 /* Determine the key containing principal information, if present. */
1033 princ_id = keyctl_search(cache_id, KRCC_KEY_TYPE_USER, KRCC_SPEC_PRINC_KEYNAME, 0);
1034 if (princ_id == -1)
1035 princ_id = 0;
1037 ret = alloc_cache(context, collection_id, cache_id,
1038 anchor_name, collection_name, subsidiary_name, &data);
1039 if (ret)
1040 return ret;
1042 if (*cache == NULL) {
1043 ret = _krb5_cc_allocate(context, &krb5_krcc_ops, cache);
1044 if (ret) {
1045 free(data->krc_name);
1046 free(data);
1047 return ret;
1051 data->krc_princ_id = princ_id;
1053 (*cache)->data.data = data;
1054 (*cache)->data.length = sizeof(*data);
1056 return 0;
1059 /* Create a keyring ccache handle for the given residual string. */
1060 static krb5_error_code KRB5_CALLCONV
1061 krcc_resolve_2(krb5_context context,
1062 krb5_ccache *id,
1063 const char *residual,
1064 const char *sub)
1066 krb5_error_code ret;
1067 atomic_key_serial_t collection_id;
1068 key_serial_t cache_id;
1069 char *anchor_name = NULL, *collection_name = NULL, *subsidiary_name = NULL;
1071 ret = parse_residual(context, residual, &anchor_name, &collection_name,
1072 &subsidiary_name);
1073 if (ret)
1074 goto cleanup;
1075 if (sub) {
1076 free(subsidiary_name);
1077 if ((subsidiary_name = strdup(sub)) == NULL) {
1078 ret = krb5_enomem(context);
1079 goto cleanup;
1083 ret = get_collection(context, anchor_name, collection_name, &collection_id);
1084 if (ret)
1085 goto cleanup;
1087 if (subsidiary_name == NULL) {
1088 /* Retrieve or initialize the primary name for the collection. */
1089 ret = get_primary_name(context, anchor_name, collection_name,
1090 collection_id, &subsidiary_name);
1091 if (ret)
1092 goto cleanup;
1095 /* Look up the cache keyring ID, if the cache is already initialized. */
1096 cache_id = keyctl_search(collection_id, KRCC_KEY_TYPE_KEYRING,
1097 subsidiary_name, 0);
1098 if (cache_id < 0)
1099 cache_id = 0;
1101 ret = make_cache(context, collection_id, cache_id, anchor_name,
1102 collection_name, subsidiary_name, id);
1103 if (ret)
1104 goto cleanup;
1106 cleanup:
1107 free(anchor_name);
1108 free(collection_name);
1109 free(subsidiary_name);
1111 return ret;
1114 struct krcc_cursor {
1115 size_t numkeys;
1116 size_t currkey;
1117 key_serial_t princ_id;
1118 key_serial_t offsets_id;
1119 key_serial_t *keys;
1122 /* Prepare for a sequential iteration over the cache keyring. */
1123 static krb5_error_code
1124 krcc_get_first(krb5_context context,
1125 krb5_ccache id,
1126 krb5_cc_cursor *cursor)
1128 struct krcc_cursor *krcursor;
1129 krb5_krcache *data = KRCACHE(id);
1130 key_serial_t cache_id;
1131 void *keys;
1132 long size;
1134 if (data == NULL)
1135 return krb5_einval(context, 2);
1137 cache_id = heim_base_atomic_load(&data->krc_cache_id);
1138 if (cache_id == 0)
1139 return KRB5_FCC_NOFILE;
1141 size = keyctl_read_alloc(cache_id, &keys);
1142 if (size == -1) {
1143 _krb5_debug(context, 10, "Error getting from keyring: %s\n",
1144 strerror(errno));
1145 return KRB5_CC_IO;
1148 krcursor = calloc(1, sizeof(*krcursor));
1149 if (krcursor == NULL) {
1150 free(keys);
1151 return KRB5_CC_NOMEM;
1154 krcursor->princ_id = heim_base_atomic_load(&data->krc_princ_id);
1155 krcursor->offsets_id = keyctl_search(cache_id, KRCC_KEY_TYPE_USER,
1156 KRCC_TIME_OFFSETS, 0);
1157 krcursor->numkeys = size / sizeof(key_serial_t);
1158 krcursor->keys = keys;
1160 *cursor = krcursor;
1162 return 0;
1165 static krb5_error_code
1166 keyctl_read_krb5_data(key_serial_t keyid, krb5_data *payload)
1168 krb5_data_zero(payload);
1170 payload->length = keyctl_read_alloc(keyid, &payload->data);
1172 return (payload->length == -1) ? KRB5_FCC_NOFILE : 0;
1175 /* Get the next credential from the cache keyring. */
1176 static krb5_error_code KRB5_CALLCONV
1177 krcc_get_next(krb5_context context,
1178 krb5_ccache id,
1179 krb5_cc_cursor *cursor,
1180 krb5_creds *creds)
1182 struct krcc_cursor *krcursor;
1183 krb5_error_code ret;
1184 krb5_data payload;
1185 krb5_storage *sp;
1187 memset(creds, 0, sizeof(krb5_creds));
1189 krcursor = *cursor;
1190 if (krcursor == NULL)
1191 return KRB5_CC_END;
1193 if (krcursor->currkey >= krcursor->numkeys)
1194 return KRB5_CC_END;
1197 * If we're pointing at the entry with the principal, or at the key
1198 * with the time offsets, skip it.
1200 while (krcursor->keys[krcursor->currkey] == krcursor->princ_id ||
1201 krcursor->keys[krcursor->currkey] == krcursor->offsets_id) {
1202 krcursor->currkey++;
1203 if (krcursor->currkey >= krcursor->numkeys)
1204 return KRB5_CC_END;
1207 ret = keyctl_read_krb5_data(krcursor->keys[krcursor->currkey], &payload);
1208 if (ret) {
1209 _krb5_debug(context, 10, "Error reading key %d: %s\n",
1210 krcursor->keys[krcursor->currkey],
1211 strerror(errno));
1212 return ret;
1214 krcursor->currkey++;
1216 sp = krb5_storage_from_data(&payload);
1217 if (sp == NULL) {
1218 ret = KRB5_CC_IO;
1219 } else {
1220 ret = krb5_ret_creds(sp, creds);
1221 krb5_storage_free(sp);
1224 krb5_data_free(&payload);
1226 return ret;
1229 /* Release an iteration cursor. */
1230 static krb5_error_code KRB5_CALLCONV
1231 krcc_end_get(krb5_context context, krb5_ccache id, krb5_cc_cursor *cursor)
1233 struct krcc_cursor *krcursor = *cursor;
1235 if (krcursor != NULL) {
1236 free(krcursor->keys);
1237 free(krcursor);
1240 *cursor = NULL;
1242 return 0;
1245 /* Create keyring data for a credential cache. */
1246 static krb5_error_code
1247 alloc_cache(krb5_context context,
1248 key_serial_t collection_id,
1249 key_serial_t cache_id,
1250 const char *anchor_name,
1251 const char *collection_name,
1252 const char *subsidiary_name,
1253 krb5_krcache **pdata)
1255 krb5_error_code ret;
1256 krb5_krcache *data;
1258 *pdata = NULL;
1260 data = calloc(1, sizeof(*data));
1261 if (data == NULL)
1262 return KRB5_CC_NOMEM;
1264 ret = make_subsidiary_residual(context, anchor_name, collection_name,
1265 subsidiary_name, &data->krc_name);
1266 if (ret ||
1267 (data->krc_collection = strdup(collection_name)) == NULL ||
1268 (data->krc_subsidiary = strdup(subsidiary_name ? subsidiary_name : "tkt")) == NULL) {
1269 if (data) {
1270 free(data->krc_collection);
1271 free(data->krc_name);
1273 free(data);
1274 if (ret == 0)
1275 ret = krb5_enomem(context);
1276 return ret;
1279 heim_base_atomic_init(&data->krc_princ_id, 0);
1280 heim_base_atomic_init(&data->krc_cache_id, cache_id);
1281 data->krc_coll_id = collection_id;
1282 data->krc_changetime = 0;
1283 data->krc_is_legacy = (strcmp(anchor_name, KRCC_LEGACY_ANCHOR) == 0);
1285 update_change_time(context, 0, data);
1287 *pdata = data;
1289 return 0;
1292 /* Create a new keyring cache with a unique name. */
1293 static krb5_error_code KRB5_CALLCONV
1294 krcc_gen_new(krb5_context context, krb5_ccache *id)
1296 krb5_error_code ret;
1297 char *anchor_name, *collection_name, *subsidiary_name;
1298 char *new_subsidiary_name = NULL, *new_residual = NULL;
1299 krb5_krcache *data;
1300 atomic_key_serial_t collection_id;
1301 key_serial_t cache_id = 0;
1303 /* Determine the collection in which we will create the cache.*/
1304 ret = get_default(context, &anchor_name, &collection_name,
1305 &subsidiary_name);
1306 if (ret)
1307 return ret;
1309 if (anchor_name == NULL) {
1310 ret = parse_residual(context, KRCC_DEFAULT_UNIQUE_COLLECTION, &anchor_name,
1311 &collection_name, &subsidiary_name);
1312 if (ret)
1313 return ret;
1315 if (subsidiary_name != NULL) {
1316 krb5_set_error_message(context, KRB5_DCC_CANNOT_CREATE,
1317 N_("Can't create new subsidiary cache because default cache "
1318 "is already a subsidiary", ""));
1319 ret = KRB5_DCC_CANNOT_CREATE;
1320 goto cleanup;
1323 /* Make a unique keyring within the chosen collection. */
1324 ret = get_collection(context, anchor_name, collection_name, &collection_id);
1325 if (ret)
1326 goto cleanup;
1328 ret = add_unique_keyring(context, collection_id, &new_subsidiary_name, &cache_id);
1329 if (ret)
1330 goto cleanup;
1332 ret = alloc_cache(context, collection_id, cache_id,
1333 anchor_name, collection_name, new_subsidiary_name,
1334 &data);
1335 if (ret)
1336 goto cleanup;
1338 (*id)->data.data = data;
1339 (*id)->data.length = sizeof(*data);
1341 cleanup:
1342 free(anchor_name);
1343 free(collection_name);
1344 free(subsidiary_name);
1345 free(new_subsidiary_name);
1346 free(new_residual);
1348 return ret;
1351 /* Return an alias to the residual string of the cache. */
1352 static krb5_error_code KRB5_CALLCONV
1353 krcc_get_name_2(krb5_context context,
1354 krb5_ccache id,
1355 const char **name,
1356 const char **collection_name,
1357 const char **subsidiary_name)
1359 krb5_krcache *data = KRCACHE(id);
1361 if (data == NULL)
1362 return krb5_einval(context, 2);
1364 if (name)
1365 *name = data->krc_name;
1366 if (collection_name)
1367 *collection_name = data->krc_collection;
1368 if (subsidiary_name)
1369 *subsidiary_name = data->krc_subsidiary;
1370 return 0;
1373 /* Retrieve a copy of the default principal, if the cache is initialized. */
1374 static krb5_error_code KRB5_CALLCONV
1375 krcc_get_principal(krb5_context context,
1376 krb5_ccache id,
1377 krb5_principal *princ)
1379 krb5_krcache *data = KRCACHE(id);
1380 krb5_error_code ret;
1381 krb5_storage *sp = NULL;
1382 krb5_data payload;
1383 krb5_krcache_and_princ_id ids;
1385 krb5_data_zero(&payload);
1386 *princ = NULL;
1388 if (data == NULL)
1389 return krb5_einval(context, 2);
1391 memset(&ids, 0, sizeof(ids));
1392 ids.krcu_cache_and_princ_id = heim_base_atomic_load(&data->krc_cache_and_principal_id);
1393 if (ids.krcu_cache_id == 0 || ids.krcu_princ_id == 0) {
1394 ret = KRB5_FCC_NOFILE;
1395 krb5_set_error_message(context, ret,
1396 N_("Credentials cache keyring '%s' not found", ""),
1397 data->krc_name);
1398 goto cleanup;
1401 ret = keyctl_read_krb5_data(ids.krcu_princ_id, &payload);
1402 if (ret) {
1403 _krb5_debug(context, 10, "Reading principal key %d: %s\n",
1404 ids.krcu_princ_id, strerror(errno));
1405 goto cleanup;
1408 sp = krb5_storage_from_data(&payload);
1409 if (sp == NULL) {
1410 ret = KRB5_CC_IO;
1411 goto cleanup;
1414 ret = krb5_ret_principal(sp, princ);
1415 if (ret)
1416 goto cleanup;
1418 cleanup:
1419 krb5_storage_free(sp);
1420 krb5_data_free(&payload);
1422 return ret;
1425 /* Remove a cred from the cache keyring */
1426 static krb5_error_code KRB5_CALLCONV
1427 krcc_remove_cred(krb5_context context, krb5_ccache id,
1428 krb5_flags which, krb5_creds *mcred)
1430 krb5_krcache *data = KRCACHE(id);
1431 krb5_error_code ret, ret2;
1432 krb5_cc_cursor cursor;
1433 krb5_creds found_cred;
1434 krb5_krcache_and_princ_id ids;
1436 if (data == NULL)
1437 return krb5_einval(context, 2);
1439 ret = krcc_get_first(context, id, &cursor);
1440 if (ret)
1441 return ret;
1443 memset(&ids, 0, sizeof(ids));
1444 ids.krcu_cache_and_princ_id = heim_base_atomic_load(&data->krc_cache_and_principal_id);
1446 while ((ret = krcc_get_next(context, id, &cursor, &found_cred)) == 0) {
1447 struct krcc_cursor *krcursor = cursor;
1449 if (!krb5_compare_creds(context, which, mcred, &found_cred)) {
1450 krb5_free_cred_contents(context, &found_cred);
1451 continue;
1454 _krb5_debug(context, 10, "Removing cred %d from cache_id %d, princ_id %d\n",
1455 krcursor->keys[krcursor->currkey - 1],
1456 ids.krcu_cache_id, ids.krcu_princ_id);
1458 keyctl_invalidate(krcursor->keys[krcursor->currkey - 1]);
1459 krcursor->keys[krcursor->currkey - 1] = 0;
1460 krb5_free_cred_contents(context, &found_cred);
1463 ret2 = krcc_end_get(context, id, &cursor);
1464 if (ret == KRB5_CC_END)
1465 ret = ret2;
1467 return ret;
1470 /* Set flags on the cache. (We don't care about any flags.) */
1471 static krb5_error_code KRB5_CALLCONV
1472 krcc_set_flags(krb5_context context, krb5_ccache id, krb5_flags flags)
1474 return 0;
1477 static int KRB5_CALLCONV
1478 krcc_get_version(krb5_context context, krb5_ccache id)
1480 return 0;
1483 /* Store a credential in the cache keyring. */
1484 static krb5_error_code KRB5_CALLCONV
1485 krcc_store(krb5_context context, krb5_ccache id, krb5_creds *creds)
1487 krb5_error_code ret;
1488 krb5_krcache *data = KRCACHE(id);
1489 krb5_storage *sp = NULL;
1490 char *keyname = NULL;
1491 key_serial_t cred_key, cache_id;
1492 krb5_timestamp now;
1493 krb5_data payload;
1495 krb5_data_zero(&payload);
1497 if (data == NULL)
1498 return krb5_einval(context, 2);
1500 cache_id = heim_base_atomic_load(&data->krc_cache_id);
1501 if (cache_id == 0)
1502 return KRB5_FCC_NOFILE;
1504 ret = krb5_unparse_name(context, creds->server, &keyname);
1505 if (ret)
1506 goto cleanup;
1508 sp = krb5_storage_emem();
1509 if (sp == NULL) {
1510 krb5_set_error_message(context, KRB5_CC_NOMEM, N_("malloc: out of memory", ""));
1511 ret = KRB5_CC_NOMEM;
1512 goto cleanup;
1515 ret = krb5_store_creds(sp, creds);
1516 if (ret)
1517 goto cleanup;
1519 ret = krb5_storage_to_data(sp, &payload);
1520 if (ret)
1521 goto cleanup;
1523 _krb5_debug(context, 10, "krcc_store: adding new key '%s' to keyring %d\n",
1524 keyname, cache_id);
1525 ret = add_cred_key(keyname, payload.data, payload.length, cache_id,
1526 data->krc_is_legacy, &cred_key);
1527 if (ret)
1528 goto cleanup;
1530 ret = krb5_timeofday(context, &now);
1531 if (ret)
1532 goto cleanup;
1534 update_change_time(context, now, data);
1536 /* Set timeout on credential key */
1537 if (creds->times.endtime > now)
1538 (void) keyctl_set_timeout(cred_key, creds->times.endtime - now);
1540 /* Set timeout on credential cache keyring */
1541 update_keyring_expiration(context, id, cache_id, now);
1543 cleanup:
1544 krb5_data_free(&payload);
1545 krb5_storage_free(sp);
1546 krb5_xfree(keyname);
1548 return ret;
1552 * Get the cache's last modification time. (This is currently broken; it
1553 * returns only the last change made using this handle.)
1555 static krb5_error_code KRB5_CALLCONV
1556 krcc_lastchange(krb5_context context,
1557 krb5_ccache id,
1558 krb5_timestamp *change_time)
1560 krb5_krcache *data = KRCACHE(id);
1562 if (data == NULL)
1563 return krb5_einval(context, 2);
1565 *change_time = heim_base_atomic_load(&data->krc_changetime);
1567 return 0;
1570 static krb5_error_code
1571 save_principal(krb5_context context,
1572 key_serial_t cache_id,
1573 krb5_const_principal princ,
1574 atomic_key_serial_t *pprinc_id)
1576 krb5_error_code ret;
1577 krb5_storage *sp;
1578 key_serial_t newkey;
1579 krb5_data payload;
1581 krb5_data_zero(&payload);
1583 sp = krb5_storage_emem();
1584 if (sp == NULL) {
1585 krb5_set_error_message(context, KRB5_CC_NOMEM, N_("malloc: out of memory", ""));
1586 return KRB5_CC_NOMEM;
1589 ret = krb5_store_principal(sp, princ);
1590 if (ret) {
1591 krb5_storage_free(sp);
1592 return ret;
1595 ret = krb5_storage_to_data(sp, &payload);
1596 if (ret) {
1597 krb5_storage_free(sp);
1598 return ret;
1601 krb5_storage_free(sp);
1603 krb5_error_code tmp;
1604 char *princname = NULL;
1606 tmp = krb5_unparse_name(context, princ, &princname);
1607 _krb5_debug(context, 10, "save_principal: adding new key '%s' "
1608 "to keyring %d for principal '%s'\n",
1609 KRCC_SPEC_PRINC_KEYNAME, cache_id,
1610 tmp ? "<unknown>" : princname);
1611 if (tmp == 0)
1612 krb5_xfree(princname);
1615 /* Add new key into keyring */
1616 newkey = add_key(KRCC_KEY_TYPE_USER, KRCC_SPEC_PRINC_KEYNAME,
1617 payload.data, payload.length, cache_id);
1618 if (newkey == -1) {
1619 ret = errno;
1620 _krb5_debug(context, 10, "Error adding principal key: %s\n", strerror(ret));
1621 } else {
1622 ret = 0;
1623 heim_base_atomic_store(pprinc_id, newkey);
1626 krb5_data_free(&payload);
1628 return ret;
1631 /* Add a key to the cache keyring containing the given time offsets. */
1632 static krb5_error_code
1633 save_time_offsets(krb5_context context,
1634 key_serial_t cache_id,
1635 int32_t sec_offset,
1636 int32_t usec_offset)
1638 krb5_error_code ret;
1639 key_serial_t newkey;
1640 krb5_storage *sp;
1641 krb5_data payload;
1643 krb5_data_zero(&payload);
1645 sp = krb5_storage_emem();
1646 if (sp == NULL) {
1647 krb5_set_error_message(context, KRB5_CC_NOMEM, N_("malloc: out of memory", ""));
1648 return KRB5_CC_NOMEM;
1651 krb5_storage_set_byteorder(sp, KRB5_STORAGE_BYTEORDER_BE);
1653 ret = krb5_store_int32(sp, sec_offset);
1654 if (ret == 0)
1655 ret = krb5_store_int32(sp, usec_offset);
1656 if (ret) {
1657 krb5_storage_free(sp);
1658 return ret;
1661 ret = krb5_storage_to_data(sp, &payload);
1662 if (ret) {
1663 krb5_storage_free(sp);
1664 return ret;
1667 krb5_storage_free(sp);
1669 newkey = add_key(KRCC_KEY_TYPE_USER, KRCC_TIME_OFFSETS, payload.data,
1670 payload.length, cache_id);
1671 ret = newkey == -1 ? errno : 0;
1673 krb5_data_free(&payload);
1675 return ret;
1678 static krb5_error_code KRB5_CALLCONV
1679 krcc_set_kdc_offset(krb5_context context, krb5_ccache id, krb5_deltat offset)
1681 krb5_krcache *data = KRCACHE(id);
1682 key_serial_t cache_id;
1683 krb5_error_code ret;
1685 if (data == NULL)
1686 return krb5_einval(context, 2);
1688 cache_id = heim_base_atomic_load(&data->krc_cache_id);
1690 ret = save_time_offsets(context, cache_id, (int32_t)offset, 0);
1691 if (ret == 0)
1692 update_change_time(context, 0, data);
1694 return ret;
1697 /* Retrieve and parse the key in the cache keyring containing time offsets. */
1698 static krb5_error_code KRB5_CALLCONV
1699 krcc_get_kdc_offset(krb5_context context,
1700 krb5_ccache id,
1701 krb5_deltat *offset)
1703 krb5_krcache *data = KRCACHE(id);
1704 krb5_error_code ret = 0;
1705 key_serial_t key, cache_id;
1706 krb5_storage *sp = NULL;
1707 krb5_data payload;
1708 int32_t sec_offset, usec_offset;
1710 if (data == NULL)
1711 return krb5_einval(context, 2);
1713 krb5_data_zero(&payload);
1715 cache_id = heim_base_atomic_load(&data->krc_cache_id);
1716 if (cache_id == 0) {
1717 ret = KRB5_FCC_NOFILE;
1718 goto cleanup;
1721 key = keyctl_search(cache_id, KRCC_KEY_TYPE_USER, KRCC_TIME_OFFSETS, 0);
1722 if (key == -1) {
1723 ret = ENOENT;
1724 goto cleanup;
1727 ret = keyctl_read_krb5_data(key, &payload);
1728 if (ret) {
1729 _krb5_debug(context, 10, "Reading time offsets key %d: %s\n",
1730 key, strerror(errno));
1731 goto cleanup;
1734 sp = krb5_storage_from_data(&payload);
1735 if (sp == NULL) {
1736 ret = KRB5_CC_IO;
1737 goto cleanup;
1740 krb5_storage_set_byteorder(sp, KRB5_STORAGE_BYTEORDER_BE);
1742 ret = krb5_ret_int32(sp, &sec_offset);
1743 if (ret == 0)
1744 krb5_ret_int32(sp, &usec_offset);
1745 if (ret) {
1746 ret = KRB5_CC_END;
1747 goto cleanup;
1750 *offset = sec_offset;
1752 cleanup:
1753 krb5_storage_free(sp);
1754 krb5_data_free(&payload);
1756 return ret;
1759 struct krcc_iter {
1760 atomic_key_serial_t collection_id;
1761 char *anchor_name;
1762 char *collection_name;
1763 char *subsidiary_name;
1764 char *primary_name;
1765 krb5_boolean first;
1766 long num_keys;
1767 long next_key;
1768 key_serial_t *keys;
1771 static krb5_error_code KRB5_CALLCONV
1772 krcc_get_cache_first(krb5_context context, krb5_cc_cursor *cursor)
1774 struct krcc_iter *iter;
1775 krb5_error_code ret;
1776 void *keys;
1777 long size;
1779 *cursor = NULL;
1781 iter = calloc(1, sizeof(*iter));
1782 if (iter == NULL) {
1783 ret = krb5_enomem(context);
1784 goto error;
1786 iter->first = TRUE;
1788 ret = get_default(context, &iter->anchor_name, &iter->collection_name,
1789 &iter->subsidiary_name);
1790 if (ret)
1791 goto error;
1793 /* If there is no default collection, return an empty cursor. */
1794 if (iter->anchor_name == NULL) {
1795 *cursor = iter;
1796 return 0;
1799 ret = get_collection(context, iter->anchor_name, iter->collection_name,
1800 &iter->collection_id);
1801 if (ret)
1802 goto error;
1804 if (iter->subsidiary_name == NULL) {
1805 ret = get_primary_name(context, iter->anchor_name,
1806 iter->collection_name, iter->collection_id,
1807 &iter->primary_name);
1808 if (ret)
1809 goto error;
1811 size = keyctl_read_alloc(iter->collection_id, &keys);
1812 if (size == -1) {
1813 ret = errno;
1814 goto error;
1816 iter->keys = keys;
1817 iter->num_keys = size / sizeof(key_serial_t);
1820 *cursor = iter;
1822 return 0;
1824 error:
1825 krcc_end_cache_get(context, iter);
1827 return ret;
1830 static krb5_error_code KRB5_CALLCONV
1831 krcc_get_cache_next(krb5_context context,
1832 krb5_cc_cursor cursor,
1833 krb5_ccache *cache)
1835 krb5_error_code ret;
1836 struct krcc_iter *iter = cursor;
1837 key_serial_t key, cache_id = 0;
1838 const char *first_name, *keytype, *sep, *subsidiary_name;
1839 size_t keytypelen;
1840 char *description = NULL;
1842 *cache = NULL;
1844 /* No keyring available */
1845 if (iter->collection_id == 0)
1846 return KRB5_CC_END;
1848 if (iter->first) {
1850 * Look for the primary cache for a collection cursor, or the
1851 * subsidiary cache for a subsidiary cursor.
1853 iter->first = FALSE;
1854 first_name = (iter->primary_name != NULL) ? iter->primary_name :
1855 iter->subsidiary_name;
1856 cache_id = keyctl_search(iter->collection_id, KRCC_KEY_TYPE_KEYRING,
1857 first_name, 0);
1858 if (cache_id != -1) {
1859 return make_cache(context, iter->collection_id, cache_id,
1860 iter->anchor_name, iter->collection_name,
1861 first_name, cache);
1865 /* A subsidiary cursor yields at most the first cache. */
1866 if (iter->subsidiary_name != NULL)
1867 return KRB5_CC_END;
1869 keytype = KRCC_KEY_TYPE_KEYRING ";";
1870 keytypelen = strlen(keytype);
1872 for (ret = KRB5_CC_END; iter->next_key < iter->num_keys; iter->next_key++) {
1873 free(description);
1874 description = NULL;
1877 * Get the key description, which should have the form:
1878 * typename;UID;GID;permissions;description
1880 key = iter->keys[iter->next_key];
1881 if (keyctl_describe_alloc(key, &description) < 0)
1882 continue;
1883 sep = strrchr(description, ';');
1884 if (sep == NULL)
1885 continue;
1886 subsidiary_name = sep + 1;
1888 /* Skip this key if it isn't a keyring. */
1889 if (strncmp(description, keytype, keytypelen) != 0)
1890 continue;
1892 /* Don't repeat the primary cache. */
1893 if (iter->primary_name &&
1894 strcmp(subsidiary_name, iter->primary_name) == 0)
1895 continue;
1897 /* We found a valid key */
1898 iter->next_key++;
1899 ret = make_cache(context, iter->collection_id, key, iter->anchor_name,
1900 iter->collection_name, subsidiary_name, cache);
1901 break;
1904 free(description);
1906 return ret;
1909 static krb5_error_code KRB5_CALLCONV
1910 krcc_end_cache_get(krb5_context context, krb5_cc_cursor cursor)
1912 struct krcc_iter *iter = cursor;
1914 if (iter != NULL) {
1915 free(iter->anchor_name);
1916 free(iter->collection_name);
1917 free(iter->subsidiary_name);
1918 free(iter->primary_name);
1919 free(iter->keys);
1921 memset(iter, 0, sizeof(*iter));
1922 free(iter);
1925 return 0;
1928 static krb5_error_code KRB5_CALLCONV
1929 krcc_set_default(krb5_context context, krb5_ccache id)
1931 krb5_krcache *data = KRCACHE(id);
1932 krb5_error_code ret;
1933 char *anchor_name, *collection_name, *subsidiary_name;
1934 atomic_key_serial_t collection_id;
1936 if (data == NULL)
1937 return krb5_einval(context, 2);
1939 ret = parse_residual(context, data->krc_name,
1940 &anchor_name, &collection_name, &subsidiary_name);
1941 if (ret)
1942 goto cleanup;
1944 ret = get_collection(context, anchor_name, collection_name, &collection_id);
1945 if (ret)
1946 goto cleanup;
1948 ret = set_primary_name(context, collection_id, subsidiary_name);
1949 if (ret)
1950 goto cleanup;
1952 cleanup:
1953 free(anchor_name);
1954 free(collection_name);
1955 free(subsidiary_name);
1957 return ret;
1961 * Utility routine: called by krcc_* functions to keep
1962 * result of krcc_last_change_time up to date.
1964 static void
1965 update_change_time(krb5_context context, krb5_timestamp now, krb5_krcache *data)
1967 krb5_timestamp old;
1969 if (now == 0)
1970 krb5_timeofday(context, &now);
1972 old = heim_base_exchange_time_t(&data->krc_changetime, now);
1973 if (old > now) /* don't go backwards */
1974 heim_base_atomic_store(&data->krc_changetime, old + 1);
1977 static int
1978 move_key_to_new_keyring(key_serial_t parent, key_serial_t key,
1979 char *desc, int desc_len, void *data)
1981 key_serial_t cache_id = *(key_serial_t *)data;
1983 if (parent) {
1984 if (keyctl_link(key, cache_id) == -1 ||
1985 keyctl_unlink(key, parent) == -1)
1986 return -1;
1989 return 0;
1992 /* Move contents of one ccache to another; destroys from cache */
1993 static krb5_error_code KRB5_CALLCONV
1994 krcc_move(krb5_context context, krb5_ccache from, krb5_ccache to)
1996 krb5_krcache *krfrom = KRCACHE(from);
1997 krb5_krcache *krto = KRCACHE(to);
1998 krb5_error_code ret;
1999 krb5_timestamp now;
2000 key_serial_t to_cache_id;
2002 if (krfrom == NULL || krto == NULL)
2003 return krb5_einval(context, 2);
2005 ret = initialize_internal(context, to, NULL);
2006 if (ret)
2007 return ret;
2009 krb5_timeofday(context, &now);
2010 to_cache_id = heim_base_atomic_load(&krto->krc_cache_id);
2012 if (krfrom->krc_cache_id != 0) {
2013 ret = recursive_key_scan(krfrom->krc_cache_id,
2014 move_key_to_new_keyring, &to_cache_id);
2015 if (ret)
2016 return KRB5_CC_IO;
2018 if (keyctl_unlink(krfrom->krc_cache_id, krfrom->krc_coll_id) == -1)
2019 return errno;
2021 heim_base_exchange_32(&krto->krc_princ_id, krfrom->krc_princ_id);
2024 update_change_time(context, now, krto);
2025 krb5_cc_destroy(context, from);
2026 return 0;
2029 static krb5_error_code KRB5_CALLCONV
2030 krcc_get_default_name(krb5_context context, char **str)
2032 *str = strdup("KEYRING:");
2033 if (*str == NULL)
2034 return krb5_enomem(context);
2036 return 0;
2040 * ccache implementation storing credentials in the Linux keyring facility
2041 * The default is to put them at the session keyring level.
2042 * If "KEYRING:process:" or "KEYRING:thread:" is specified, then they will
2043 * be stored at the process or thread level respectively.
2045 KRB5_LIB_VARIABLE const krb5_cc_ops krb5_krcc_ops = {
2046 KRB5_CC_OPS_VERSION_5,
2047 "KEYRING",
2048 NULL,
2049 NULL,
2050 krcc_gen_new,
2051 krcc_initialize,
2052 krcc_destroy,
2053 krcc_close,
2054 krcc_store,
2055 NULL, /* retrieve */
2056 krcc_get_principal,
2057 krcc_get_first,
2058 krcc_get_next,
2059 krcc_end_get,
2060 krcc_remove_cred,
2061 krcc_set_flags,
2062 krcc_get_version,
2063 krcc_get_cache_first,
2064 krcc_get_cache_next,
2065 krcc_end_cache_get,
2066 krcc_move,
2067 krcc_get_default_name,
2068 krcc_set_default,
2069 krcc_lastchange,
2070 krcc_set_kdc_offset,
2071 krcc_get_kdc_offset,
2072 krcc_get_name_2,
2073 krcc_resolve_2
2076 #endif /* HAVE_KEYUTILS_H */