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