1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /* lib/krb5/ccache/cc_keyring.c */
3 /*
4 * Copyright (c) 2006
5 * The Regents of the University of Michigan
6 * ALL RIGHTS RESERVED
7 *
8 * Permission is granted to use, copy, create derivative works
9 * and redistribute this software and such derivative works
10 * for any purpose, so long as the name of The University of
11 * Michigan is not used in any advertising or publicity
12 * pertaining to the use of distribution of this software
13 * without specific, written prior authorization. If the
14 * above copyright notice or any other identification of the
15 * University of Michigan is included in any copy of any
16 * portion of this software, then the disclaimer below must
17 * also be included.
18 *
19 * THIS SOFTWARE IS PROVIDED AS IS, WITHOUT REPRESENTATION
20 * FROM THE UNIVERSITY OF MICHIGAN AS TO ITS FITNESS FOR ANY
21 * PURPOSE, AND WITHOUT WARRANTY BY THE UNIVERSITY OF
22 * MICHIGAN OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING
23 * WITHOUT LIMITATION THE IMPLIED WARRANTIES OF
24 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE
25 * REGENTS OF THE UNIVERSITY OF MICHIGAN SHALL NOT BE LIABLE
26 * FOR ANY DAMAGES, INCLUDING SPECIAL, INDIRECT, INCIDENTAL, OR
27 * CONSEQUENTIAL DAMAGES, WITH RESPECT TO ANY CLAIM ARISING
28 * OUT OF OR IN CONNECTION WITH THE USE OF THE SOFTWARE, EVEN
29 * IF IT HAS BEEN OR IS HEREAFTER ADVISED OF THE POSSIBILITY OF
30 * SUCH DAMAGES.
31 */
32 /*
33 * Copyright 1990,1991,1992,1993,1994,2000,2004 Massachusetts Institute of
34 * Technology. All Rights Reserved.
35 *
36 * Original stdio support copyright 1995 by Cygnus Support.
37 *
38 * Export of this software from the United States of America may
39 * require a specific license from the United States Government.
40 * It is the responsibility of any person or organization contemplating
41 * export to obtain such a license before exporting.
42 *
43 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
44 * distribute this software and its documentation for any purpose and
45 * without fee is hereby granted, provided that the above copyright
46 * notice appear in all copies and that both that copyright notice and
47 * this permission notice appear in supporting documentation, and that
48 * the name of M.I.T. not be used in advertising or publicity pertaining
49 * to distribution of the software without specific, written prior
50 * permission. Furthermore if you modify this software you must label
51 * your software as modified software and not distribute it in such a
52 * fashion that it might be confused with the original M.I.T. software.
53 * M.I.T. makes no representations about the suitability of
54 * this software for any purpose. It is provided "as is" without express
55 * or implied warranty.
56 */
57
58 /*
59 * This file implements a collection-enabled credential cache type where the
60 * credentials are stored in the Linux keyring facility.
61 *
62 * A residual of this type can have three forms:
63 * anchor:collection:subsidiary
64 * anchor:collection
65 * collection
66 *
67 * The anchor name is "process", "thread", or "legacy" and determines where we
68 * search for keyring collections. In the third form, the anchor name is
69 * presumed to be "legacy". The anchor keyring for legacy caches is the
70 * session keyring.
71 *
72 * If the subsidiary name is present, the residual identifies a single cache
73 * within a collection. Otherwise, the residual identifies the collection
74 * itself. When a residual identifying a collection is resolved, the
75 * collection's primary key is looked up (or initialized, using the collection
76 * name as the subsidiary name), and the resulting cache's name will use the
77 * first name form and will identify the primary cache.
78 *
79 * Keyring collections are named "_krb_<collection>" and are linked from the
80 * anchor keyring. The keys within a keyring collection are links to cache
81 * keyrings, plus a link to one user key named "krb_ccache:primary" which
82 * contains a serialized representation of the collection version (currently 1)
83 * and the primary name of the collection.
84 *
85 * Cache keyrings contain one user key per credential which contains a
86 * serialized representation of the credential. There is also one user key
87 * named "__krb5_princ__" which contains a serialized representation of the
88 * cache's default principal.
89 *
90 * If the anchor name is "legacy", then the initial primary cache (the one
91 * named with the collection name) is also linked to the session keyring, and
92 * we look for a cache in that location when initializing the collection. This
93 * extra link allows that cache to be visible to old versions of the KEYRING
94 * cache type, and allows us to see caches created by that code.
95 */
96
97 #include "cc-int.h"
98
99 #ifdef USE_KEYRING_CCACHE
100
101 #include <errno.h>
102 #include <keyutils.h>
103
104 #ifdef DEBUG
105 #define KRCC_DEBUG 1
106 #endif
107
108 #if KRCC_DEBUG
109 void debug_print(char *fmt, ...); /* prototype to silence warning */
110 #include <syslog.h>
111 #define DEBUG_PRINT(x) debug_print x
112 void
debug_print(char * fmt,...)113 debug_print(char *fmt, ...)
114 {
115 va_list ap;
116 va_start(ap, fmt);
117 #ifdef DEBUG_STDERR
118 vfprintf(stderr, fmt, ap);
119 #else
120 vsyslog(LOG_ERR, fmt, ap);
121 #endif
122 va_end(ap);
123 }
124 #else
125 #define DEBUG_PRINT(x)
126 #endif
127
128 /*
129 * We try to use the big_key key type for credentials except in legacy caches.
130 * We fall back to the user key type if the kernel does not support big_key.
131 * If the library doesn't support keyctl_get_persistent(), we don't even try
132 * big_key since the two features were added at the same time.
133 */
134 #ifdef HAVE_PERSISTENT_KEYRING
135 #define KRCC_CRED_KEY_TYPE "big_key"
136 #else
137 #define KRCC_CRED_KEY_TYPE "user"
138 #endif
139
140 /*
141 * We use the "user" key type for collection primary names, for cache principal
142 * names, and for credentials in legacy caches.
143 */
144 #define KRCC_KEY_TYPE_USER "user"
145
146 /*
147 * We create ccaches as separate keyrings
148 */
149 #define KRCC_KEY_TYPE_KEYRING "keyring"
150
151 /*
152 * Special name of the key within a ccache keyring
153 * holding principal information
154 */
155 #define KRCC_SPEC_PRINC_KEYNAME "__krb5_princ__"
156
157 /*
158 * Special name for the key to communicate the name(s)
159 * of credentials caches to be used for requests.
160 * This should currently contain a single name, but
161 * in the future may contain a list that may be
162 * intelligently chosen from.
163 */
164 #define KRCC_SPEC_CCACHE_SET_KEYNAME "__krb5_cc_set__"
165
166 /*
167 * This name identifies the key containing the name of the current primary
168 * cache within a collection.
169 */
170 #define KRCC_COLLECTION_PRIMARY "krb_ccache:primary"
171
172 /*
173 * If the library context does not specify a keyring collection, unique ccaches
174 * will be created within this collection.
175 */
176 #define KRCC_DEFAULT_UNIQUE_COLLECTION "session:__krb5_unique__"
177
178 /*
179 * Collection keyring names begin with this prefix. We use a prefix so that a
180 * cache keyring with the collection name itself can be linked directly into
181 * the anchor, for legacy session keyring compatibility.
182 */
183 #define KRCC_CCCOL_PREFIX "_krb_"
184
185 /*
186 * For the "persistent" anchor type, we look up or create this fixed keyring
187 * name within the per-UID persistent keyring.
188 */
189 #define KRCC_PERSISTENT_KEYRING_NAME "_krb"
190
191 /*
192 * Name of the key holding time offsets for the individual cache
193 */
194 #define KRCC_TIME_OFFSETS "__krb5_time_offsets__"
195
196 /*
197 * Keyring name prefix and length of random name part
198 */
199 #define KRCC_NAME_PREFIX "krb_ccache_"
200 #define KRCC_NAME_RAND_CHARS 8
201
202 #define KRCC_COLLECTION_VERSION 1
203
204 #define KRCC_PERSISTENT_ANCHOR "persistent"
205 #define KRCC_PROCESS_ANCHOR "process"
206 #define KRCC_THREAD_ANCHOR "thread"
207 #define KRCC_SESSION_ANCHOR "session"
208 #define KRCC_USER_ANCHOR "user"
209 #define KRCC_LEGACY_ANCHOR "legacy"
210
211 typedef struct _krcc_cursor
212 {
213 int numkeys;
214 int currkey;
215 key_serial_t princ_id;
216 key_serial_t offsets_id;
217 key_serial_t *keys;
218 } *krcc_cursor;
219
220 /*
221 * This represents a credentials cache "file"
222 * where cache_id is the keyring serial number for
223 * this credentials cache "file". Each key
224 * in the keyring contains a separate key.
225 */
226 typedef struct _krcc_data
227 {
228 char *name; /* Name for this credentials cache */
229 k5_cc_mutex lock; /* synchronization */
230 key_serial_t collection_id; /* collection containing this cache keyring */
231 key_serial_t cache_id; /* keyring representing ccache */
232 key_serial_t princ_id; /* key holding principal info */
233 krb5_boolean is_legacy_type;
234 } krcc_data;
235
236 /* Global mutex */
237 k5_cc_mutex krb5int_krcc_mutex = K5_CC_MUTEX_PARTIAL_INITIALIZER;
238
239 extern const krb5_cc_ops krb5_krcc_ops;
240
241 static const char *KRB5_CALLCONV
242 krcc_get_name(krb5_context context, krb5_ccache id);
243
244 static krb5_error_code KRB5_CALLCONV
245 krcc_start_seq_get(krb5_context, krb5_ccache id, krb5_cc_cursor *cursor);
246
247 static krb5_error_code KRB5_CALLCONV
248 krcc_next_cred(krb5_context context, krb5_ccache id, krb5_cc_cursor *cursor,
249 krb5_creds *creds);
250
251 static krb5_error_code KRB5_CALLCONV
252 krcc_end_seq_get(krb5_context context, krb5_ccache id, krb5_cc_cursor *cursor);
253
254 static krb5_error_code KRB5_CALLCONV
255 krcc_ptcursor_free(krb5_context context, krb5_cc_ptcursor *cursor);
256
257 static krb5_error_code clear_cache_keyring(krb5_context context,
258 krb5_ccache id);
259
260 static krb5_error_code make_krcc_data(const char *anchor_name,
261 const char *collection_name,
262 const char *subsidiary_name,
263 key_serial_t cache_id, key_serial_t
264 collection_id, krcc_data **datapp);
265
266 static krb5_error_code save_principal(krb5_context context, krb5_ccache id,
267 krb5_principal princ);
268
269 static krb5_error_code save_time_offsets(krb5_context context, krb5_ccache id,
270 int32_t time_offset,
271 int32_t usec_offset);
272
273 static krb5_error_code get_time_offsets(krb5_context context, krb5_ccache id,
274 int32_t *time_offset,
275 int32_t *usec_offset);
276
277 /* Note the following is a stub function for Linux */
278 extern krb5_error_code krb5_change_cache(void);
279
280 /*
281 * GET_PERSISTENT(uid) acquires the persistent keyring for uid, or falls back
282 * to the user keyring if uid matches the current effective uid.
283 */
284
285 static key_serial_t
get_persistent_fallback(uid_t uid)286 get_persistent_fallback(uid_t uid)
287 {
288 return (uid == geteuid()) ? KEY_SPEC_USER_KEYRING : -1;
289 }
290
291 #ifdef HAVE_PERSISTENT_KEYRING
292 #define GET_PERSISTENT get_persistent_real
293 static key_serial_t
get_persistent_real(uid_t uid)294 get_persistent_real(uid_t uid)
295 {
296 key_serial_t key;
297
298 key = keyctl_get_persistent(uid, KEY_SPEC_PROCESS_KEYRING);
299 return (key == -1 && errno == ENOTSUP) ? get_persistent_fallback(uid) :
300 key;
301 }
302 #else
303 #define GET_PERSISTENT get_persistent_fallback
304 #endif
305
306 /*
307 * If a process has no explicitly set session keyring, KEY_SPEC_SESSION_KEYRING
308 * will resolve to the user session keyring for ID lookup and reading, but in
309 * some kernel versions, writing to that special keyring will instead create a
310 * new empty session keyring for the process. We do not want that; the keys we
311 * create would be invisible to other processes. We can work around that
312 * behavior by explicitly writing to the user session keyring when it matches
313 * the session keyring. This function returns the keyring we should write to
314 * for the session anchor.
315 */
316 static key_serial_t
session_write_anchor(void)317 session_write_anchor(void)
318 {
319 key_serial_t s, u;
320
321 s = keyctl_get_keyring_ID(KEY_SPEC_SESSION_KEYRING, 0);
322 u = keyctl_get_keyring_ID(KEY_SPEC_USER_SESSION_KEYRING, 0);
323 return (s == u) ? KEY_SPEC_USER_SESSION_KEYRING : KEY_SPEC_SESSION_KEYRING;
324 }
325
326 /*
327 * Find or create a keyring within parent with the given name. If possess is
328 * nonzero, also make sure the key is linked from possess. This is necessary
329 * to ensure that we have possession rights on the key when the parent is the
330 * user or persistent keyring.
331 */
332 static krb5_error_code
find_or_create_keyring(key_serial_t parent,key_serial_t possess,const char * name,key_serial_t * key_out)333 find_or_create_keyring(key_serial_t parent, key_serial_t possess,
334 const char *name, key_serial_t *key_out)
335 {
336 key_serial_t key;
337
338 *key_out = -1;
339 key = keyctl_search(parent, KRCC_KEY_TYPE_KEYRING, name, possess);
340 if (key == -1) {
341 if (possess != 0) {
342 key = add_key(KRCC_KEY_TYPE_KEYRING, name, NULL, 0, possess);
343 if (key == -1)
344 return errno;
345 if (keyctl_link(key, parent) == -1)
346 return errno;
347 } else {
348 key = add_key(KRCC_KEY_TYPE_KEYRING, name, NULL, 0, parent);
349 if (key == -1)
350 return errno;
351 }
352 }
353 *key_out = key;
354 return 0;
355 }
356
357 /* Parse a residual name into an anchor name, a collection name, and possibly a
358 * subsidiary name. */
359 static krb5_error_code
parse_residual(const char * residual,char ** anchor_name_out,char ** collection_name_out,char ** subsidiary_name_out)360 parse_residual(const char *residual, char **anchor_name_out,
361 char **collection_name_out, char **subsidiary_name_out)
362 {
363 krb5_error_code ret;
364 char *anchor_name = NULL, *collection_name = NULL, *subsidiary_name = NULL;
365 const char *sep;
366
367 *anchor_name_out = 0;
368 *collection_name_out = NULL;
369 *subsidiary_name_out = NULL;
370
371 /* Parse out the anchor name. Use the legacy anchor if not present. */
372 sep = strchr(residual, ':');
373 if (sep == NULL) {
374 anchor_name = strdup(KRCC_LEGACY_ANCHOR);
375 if (anchor_name == NULL)
376 goto oom;
377 } else {
378 anchor_name = k5memdup0(residual, sep - residual, &ret);
379 if (anchor_name == NULL)
380 goto oom;
381 residual = sep + 1;
382 }
383
384 /* Parse out the collection and subsidiary name. */
385 sep = strchr(residual, ':');
386 if (sep == NULL) {
387 collection_name = strdup(residual);
388 if (collection_name == NULL)
389 goto oom;
390 subsidiary_name = NULL;
391 } else {
392 collection_name = k5memdup0(residual, sep - residual, &ret);
393 if (collection_name == NULL)
394 goto oom;
395 subsidiary_name = strdup(sep + 1);
396 if (subsidiary_name == NULL)
397 goto oom;
398 }
399
400 *anchor_name_out = anchor_name;
401 *collection_name_out = collection_name;
402 *subsidiary_name_out = subsidiary_name;
403 return 0;
404
405 oom:
406 free(anchor_name);
407 free(collection_name);
408 free(subsidiary_name);
409 return ENOMEM;
410 }
411
412 /*
413 * Return true if residual identifies a subsidiary cache which should be linked
414 * into the anchor so it can be visible to old code. This is the case if the
415 * residual has the legacy anchor and the subsidiary name matches the
416 * collection name.
417 */
418 static krb5_boolean
is_legacy_cache_name(const char * residual)419 is_legacy_cache_name(const char *residual)
420 {
421 const char *sep, *aname, *cname, *sname;
422 size_t alen, clen, legacy_len = sizeof(KRCC_LEGACY_ANCHOR) - 1;
423
424 /* Get pointers to the anchor, collection, and subsidiary names. */
425 aname = residual;
426 sep = strchr(residual, ':');
427 if (sep == NULL)
428 return FALSE;
429 alen = sep - aname;
430 cname = sep + 1;
431 sep = strchr(cname, ':');
432 if (sep == NULL)
433 return FALSE;
434 clen = sep - cname;
435 sname = sep + 1;
436
437 return alen == legacy_len && clen == strlen(sname) &&
438 strncmp(aname, KRCC_LEGACY_ANCHOR, alen) == 0 &&
439 strncmp(cname, sname, clen) == 0;
440 }
441
442 /* If the default cache name for context is a KEYRING cache, parse its residual
443 * string. Otherwise set all outputs to NULL. */
444 static krb5_error_code
get_default(krb5_context context,char ** anchor_name_out,char ** collection_name_out,char ** subsidiary_name_out)445 get_default(krb5_context context, char **anchor_name_out,
446 char **collection_name_out, char **subsidiary_name_out)
447 {
448 const char *defname;
449
450 *anchor_name_out = *collection_name_out = *subsidiary_name_out = NULL;
451 defname = krb5_cc_default_name(context);
452 if (defname == NULL || strncmp(defname, "KEYRING:", 8) != 0)
453 return 0;
454 return parse_residual(defname + 8, anchor_name_out, collection_name_out,
455 subsidiary_name_out);
456 }
457
458 /* Create a residual identifying a subsidiary cache. */
459 static krb5_error_code
make_subsidiary_residual(const char * anchor_name,const char * collection_name,const char * subsidiary_name,char ** residual_out)460 make_subsidiary_residual(const char *anchor_name, const char *collection_name,
461 const char *subsidiary_name, char **residual_out)
462 {
463 if (asprintf(residual_out, "%s:%s:%s", anchor_name, collection_name,
464 subsidiary_name) < 0) {
465 *residual_out = NULL;
466 return ENOMEM;
467 }
468 return 0;
469 }
470
471 /* Retrieve or create a keyring for collection_name within the anchor, and set
472 * *collection_id_out to its serial number. */
473 static krb5_error_code
get_collection(const char * anchor_name,const char * collection_name,key_serial_t * collection_id_out)474 get_collection(const char *anchor_name, const char *collection_name,
475 key_serial_t *collection_id_out)
476 {
477 krb5_error_code ret;
478 key_serial_t persistent_id, anchor_id, possess_id = 0;
479 char *ckname, *cnend;
480 long uidnum;
481
482 *collection_id_out = 0;
483
484 if (strcmp(anchor_name, KRCC_PERSISTENT_ANCHOR) == 0) {
485 /*
486 * The collection name is a uid (or empty for the current effective
487 * uid), and we look up a fixed keyring name within the persistent
488 * keyring for that uid. We link it to the process keyring to ensure
489 * that we have possession rights on the collection key.
490 */
491 if (*collection_name != '\0') {
492 errno = 0;
493 uidnum = strtol(collection_name, &cnend, 10);
494 if (errno || *cnend != '\0')
495 return KRB5_KCC_INVALID_UID;
496 } else {
497 uidnum = geteuid();
498 }
499 persistent_id = GET_PERSISTENT(uidnum);
500 if (persistent_id == -1)
501 return KRB5_KCC_INVALID_UID;
502 return find_or_create_keyring(persistent_id, KEY_SPEC_PROCESS_KEYRING,
503 KRCC_PERSISTENT_KEYRING_NAME,
504 collection_id_out);
505 }
506
507 if (strcmp(anchor_name, KRCC_PROCESS_ANCHOR) == 0) {
508 anchor_id = KEY_SPEC_PROCESS_KEYRING;
509 } else if (strcmp(anchor_name, KRCC_THREAD_ANCHOR) == 0) {
510 anchor_id = KEY_SPEC_THREAD_KEYRING;
511 } else if (strcmp(anchor_name, KRCC_SESSION_ANCHOR) == 0) {
512 anchor_id = session_write_anchor();
513 } else if (strcmp(anchor_name, KRCC_USER_ANCHOR) == 0) {
514 /* The user keyring does not confer possession, so we need to link the
515 * collection to the process keyring to maintain possession rights. */
516 anchor_id = KEY_SPEC_USER_KEYRING;
517 possess_id = KEY_SPEC_PROCESS_KEYRING;
518 } else if (strcmp(anchor_name, KRCC_LEGACY_ANCHOR) == 0) {
519 anchor_id = session_write_anchor();
520 } else {
521 return KRB5_KCC_INVALID_ANCHOR;
522 }
523
524 /* Look up the collection keyring name within the anchor keyring. */
525 if (asprintf(&ckname, "%s%s", KRCC_CCCOL_PREFIX, collection_name) == -1)
526 return ENOMEM;
527 ret = find_or_create_keyring(anchor_id, possess_id, ckname,
528 collection_id_out);
529 free(ckname);
530 return ret;
531 }
532
533 /* Store subsidiary_name into the primary index key for collection_id. */
534 static krb5_error_code
set_primary_name(krb5_context context,key_serial_t collection_id,const char * subsidiary_name)535 set_primary_name(krb5_context context, key_serial_t collection_id,
536 const char *subsidiary_name)
537 {
538 key_serial_t key;
539 uint32_t len = strlen(subsidiary_name), plen = 8 + len;
540 unsigned char *payload;
541
542 payload = malloc(plen);
543 if (payload == NULL)
544 return ENOMEM;
545 store_32_be(KRCC_COLLECTION_VERSION, payload);
546 store_32_be(len, payload + 4);
547 memcpy(payload + 8, subsidiary_name, len);
548 key = add_key(KRCC_KEY_TYPE_USER, KRCC_COLLECTION_PRIMARY,
549 payload, plen, collection_id);
550 free(payload);
551 return (key == -1) ? errno : 0;
552 }
553
554 static krb5_error_code
parse_index(krb5_context context,int32_t * version,char ** primary,const unsigned char * payload,size_t psize)555 parse_index(krb5_context context, int32_t *version, char **primary,
556 const unsigned char *payload, size_t psize)
557 {
558 krb5_error_code ret;
559 uint32_t len;
560
561 if (psize < 8)
562 return KRB5_CC_END;
563
564 *version = load_32_be(payload);
565 len = load_32_be(payload + 4);
566 if (len > psize - 8)
567 return KRB5_CC_END;
568 *primary = k5memdup0(payload + 8, len, &ret);
569 return (*primary == NULL) ? ret : 0;
570 }
571
572 /*
573 * Get or initialize the primary name within collection_id and set
574 * *subsidiary_out to its value. If initializing a legacy collection, look
575 * for a legacy cache and add it to the collection.
576 */
577 static krb5_error_code
get_primary_name(krb5_context context,const char * anchor_name,const char * collection_name,key_serial_t collection_id,char ** subsidiary_out)578 get_primary_name(krb5_context context, const char *anchor_name,
579 const char *collection_name, key_serial_t collection_id,
580 char **subsidiary_out)
581 {
582 krb5_error_code ret;
583 key_serial_t primary_id, legacy;
584 void *payload = NULL;
585 int payloadlen;
586 int32_t version;
587 char *subsidiary_name = NULL;
588
589 *subsidiary_out = NULL;
590
591 primary_id = keyctl_search(collection_id, KRCC_KEY_TYPE_USER,
592 KRCC_COLLECTION_PRIMARY, 0);
593 if (primary_id == -1) {
594 /* Initialize the primary key using the collection name. We can't name
595 * a key with the empty string, so map that to an arbitrary string. */
596 subsidiary_name = strdup((*collection_name == '\0') ? "tkt" :
597 collection_name);
598 if (subsidiary_name == NULL) {
599 ret = ENOMEM;
600 goto cleanup;
601 }
602 ret = set_primary_name(context, collection_id, subsidiary_name);
603 if (ret)
604 goto cleanup;
605
606 if (strcmp(anchor_name, KRCC_LEGACY_ANCHOR) == 0) {
607 /* Look for a cache created by old code. If we find one, add it to
608 * the collection. */
609 legacy = keyctl_search(KEY_SPEC_SESSION_KEYRING,
610 KRCC_KEY_TYPE_KEYRING, subsidiary_name, 0);
611 if (legacy != -1 && keyctl_link(legacy, collection_id) == -1) {
612 ret = errno;
613 goto cleanup;
614 }
615 }
616 } else {
617 /* Read, parse, and free the primary key's payload. */
618 payloadlen = keyctl_read_alloc(primary_id, &payload);
619 if (payloadlen == -1) {
620 ret = errno;
621 goto cleanup;
622 }
623 ret = parse_index(context, &version, &subsidiary_name, payload,
624 payloadlen);
625 if (ret)
626 goto cleanup;
627
628 if (version != KRCC_COLLECTION_VERSION) {
629 ret = KRB5_KCC_UNKNOWN_VERSION;
630 goto cleanup;
631 }
632 }
633
634 *subsidiary_out = subsidiary_name;
635 subsidiary_name = NULL;
636
637 cleanup:
638 free(payload);
639 free(subsidiary_name);
640 return ret;
641 }
642
643 /*
644 * Create a keyring with a unique random name within collection_id. Set
645 * *subsidiary to its name and *cache_id_out to its key serial number.
646 */
647 static krb5_error_code
unique_keyring(krb5_context context,key_serial_t collection_id,char ** subsidiary_out,key_serial_t * cache_id_out)648 unique_keyring(krb5_context context, key_serial_t collection_id,
649 char **subsidiary_out, key_serial_t *cache_id_out)
650 {
651 key_serial_t key;
652 krb5_error_code ret;
653 char uniquename[sizeof(KRCC_NAME_PREFIX) + KRCC_NAME_RAND_CHARS];
654 int prefixlen = sizeof(KRCC_NAME_PREFIX) - 1;
655 int tries;
656
657 *subsidiary_out = NULL;
658 *cache_id_out = 0;
659
660 memcpy(uniquename, KRCC_NAME_PREFIX, sizeof(KRCC_NAME_PREFIX));
661 k5_cc_mutex_lock(context, &krb5int_krcc_mutex);
662
663 /* Loop until we successfully create a new ccache keyring with
664 * a unique name, or we get an error. Limit to 100 tries. */
665 tries = 100;
666 while (tries-- > 0) {
667 ret = krb5int_random_string(context, uniquename + prefixlen,
668 KRCC_NAME_RAND_CHARS);
669 if (ret)
670 goto cleanup;
671
672 key = keyctl_search(collection_id, KRCC_KEY_TYPE_KEYRING, uniquename,
673 0);
674 if (key < 0) {
675 /* Name does not already exist. Create it to reserve the name. */
676 key = add_key(KRCC_KEY_TYPE_KEYRING, uniquename, NULL, 0,
677 collection_id);
678 if (key < 0) {
679 ret = errno;
680 goto cleanup;
681 }
682 break;
683 }
684 }
685
686 if (tries <= 0) {
687 ret = KRB5_CC_BADNAME;
688 goto cleanup;
689 }
690
691 *subsidiary_out = strdup(uniquename);
692 if (*subsidiary_out == NULL) {
693 ret = ENOMEM;
694 goto cleanup;
695 }
696 *cache_id_out = key;
697 ret = 0;
698 cleanup:
699 k5_cc_mutex_unlock(context, &krb5int_krcc_mutex);
700 return ret;
701 }
702
703 static krb5_error_code
add_cred_key(const char * name,const void * payload,size_t plen,key_serial_t cache_id,krb5_boolean legacy_type,key_serial_t * key_out)704 add_cred_key(const char *name, const void *payload, size_t plen,
705 key_serial_t cache_id, krb5_boolean legacy_type,
706 key_serial_t *key_out)
707 {
708 key_serial_t key;
709
710 *key_out = -1;
711 if (!legacy_type) {
712 /* Try the preferred cred key type; fall back if no kernel support. */
713 key = add_key(KRCC_CRED_KEY_TYPE, name, payload, plen, cache_id);
714 if (key != -1) {
715 *key_out = key;
716 return 0;
717 } else if (errno != EINVAL && errno != ENODEV) {
718 return errno;
719 }
720 }
721 /* Use the user key type. */
722 key = add_key(KRCC_KEY_TYPE_USER, name, payload, plen, cache_id);
723 if (key == -1)
724 return errno;
725 *key_out = key;
726 return 0;
727 }
728
729 static void
update_keyring_expiration(krb5_context context,krb5_ccache id)730 update_keyring_expiration(krb5_context context, krb5_ccache id)
731 {
732 krcc_data *data = id->data;
733 krb5_cc_cursor cursor;
734 krb5_creds creds;
735 krb5_timestamp now, endtime = 0;
736 unsigned int timeout;
737
738 /*
739 * We have no way to know what is the actual timeout set on the keyring.
740 * We also cannot keep track of it in a local variable as another process
741 * can always modify the keyring independently, so just always enumerate
742 * all keys and find out the highest endtime time.
743 */
744
745 /* Find the maximum endtime of all creds in the cache. */
746 if (krcc_start_seq_get(context, id, &cursor) != 0)
747 return;
748 for (;;) {
749 if (krcc_next_cred(context, id, &cursor, &creds) != 0)
750 break;
751 if (ts_after(creds.times.endtime, endtime))
752 endtime = creds.times.endtime;
753 krb5_free_cred_contents(context, &creds);
754 }
755 (void)krcc_end_seq_get(context, id, &cursor);
756
757 if (endtime == 0) /* No creds with end times */
758 return;
759
760 if (krb5_timeofday(context, &now) != 0)
761 return;
762
763 /* Setting the timeout to zero would reset the timeout, so we set it to one
764 * second instead if creds are already expired. */
765 timeout = ts_after(endtime, now) ? ts_interval(now, endtime) : 1;
766 (void)keyctl_set_timeout(data->cache_id, timeout);
767 }
768
769 /* Create or overwrite the cache keyring, and set the default principal. */
770 static krb5_error_code KRB5_CALLCONV
krcc_initialize(krb5_context context,krb5_ccache id,krb5_principal princ)771 krcc_initialize(krb5_context context, krb5_ccache id, krb5_principal princ)
772 {
773 krcc_data *data = (krcc_data *)id->data;
774 krb5_os_context os_ctx = &context->os_context;
775 krb5_error_code ret;
776 const char *cache_name, *p;
777
778 k5_cc_mutex_lock(context, &data->lock);
779
780 ret = clear_cache_keyring(context, id);
781 if (ret)
782 goto out;
783
784 if (!data->cache_id) {
785 /* The key didn't exist at resolve time. Check again and create the
786 * key if it still isn't there. */
787 p = strrchr(data->name, ':');
788 cache_name = (p != NULL) ? p + 1 : data->name;
789 ret = find_or_create_keyring(data->collection_id, 0, cache_name,
790 &data->cache_id);
791 if (ret)
792 goto out;
793 }
794
795 /* If this is the legacy cache in a legacy session collection, link it
796 * directly to the session keyring so that old code can see it. */
797 if (is_legacy_cache_name(data->name))
798 (void)keyctl_link(data->cache_id, session_write_anchor());
799
800 ret = save_principal(context, id, princ);
801
802 /* Save time offset if it is valid and this is not a legacy cache. Legacy
803 * applications would fail to parse the new key in the cache keyring. */
804 if (!is_legacy_cache_name(data->name) &&
805 (os_ctx->os_flags & KRB5_OS_TOFFSET_VALID)) {
806 ret = save_time_offsets(context, id, os_ctx->time_offset,
807 os_ctx->usec_offset);
808 }
809
810 if (ret == 0)
811 krb5_change_cache();
812
813 out:
814 k5_cc_mutex_unlock(context, &data->lock);
815 return ret;
816 }
817
818 /* Release the ccache handle. */
819 static krb5_error_code KRB5_CALLCONV
krcc_close(krb5_context context,krb5_ccache id)820 krcc_close(krb5_context context, krb5_ccache id)
821 {
822 krcc_data *data = id->data;
823
824 k5_cc_mutex_destroy(&data->lock);
825 free(data->name);
826 free(data);
827 free(id);
828 return 0;
829 }
830
831 /* Clear out a ccache keyring, unlinking all keys within it. Call with the
832 * mutex locked. */
833 static krb5_error_code
clear_cache_keyring(krb5_context context,krb5_ccache id)834 clear_cache_keyring(krb5_context context, krb5_ccache id)
835 {
836 krcc_data *data = id->data;
837 int res;
838
839 k5_cc_mutex_assert_locked(context, &data->lock);
840
841 DEBUG_PRINT(("clear_cache_keyring: cache_id %d, princ_id %d\n",
842 data->cache_id, data->princ_id));
843
844 if (data->cache_id) {
845 res = keyctl_clear(data->cache_id);
846 if (res != 0)
847 return errno;
848 }
849 data->princ_id = 0;
850
851 return 0;
852 }
853
854 /* Destroy the cache keyring and release the handle. */
855 static krb5_error_code KRB5_CALLCONV
krcc_destroy(krb5_context context,krb5_ccache id)856 krcc_destroy(krb5_context context, krb5_ccache id)
857 {
858 krb5_error_code ret = 0;
859 krcc_data *data = id->data;
860 int res;
861
862 k5_cc_mutex_lock(context, &data->lock);
863
864 clear_cache_keyring(context, id);
865 if (data->cache_id) {
866 res = keyctl_unlink(data->cache_id, data->collection_id);
867 if (res < 0) {
868 ret = errno;
869 DEBUG_PRINT(("unlinking key %d from ring %d: %s", data->cache_id,
870 data->collection_id, error_message(errno)));
871 }
872 /* If this is a legacy cache, unlink it from the session anchor. */
873 if (is_legacy_cache_name(data->name))
874 (void)keyctl_unlink(data->cache_id, session_write_anchor());
875 }
876
877 k5_cc_mutex_unlock(context, &data->lock);
878 k5_cc_mutex_destroy(&data->lock);
879 free(data->name);
880 free(data);
881 free(id);
882 krb5_change_cache();
883 return ret;
884 }
885
886 /* Create a cache handle for a cache ID. */
887 static krb5_error_code
make_cache(krb5_context context,key_serial_t collection_id,key_serial_t cache_id,const char * anchor_name,const char * collection_name,const char * subsidiary_name,krb5_ccache * cache_out)888 make_cache(krb5_context context, key_serial_t collection_id,
889 key_serial_t cache_id, const char *anchor_name,
890 const char *collection_name, const char *subsidiary_name,
891 krb5_ccache *cache_out)
892 {
893 krb5_error_code ret;
894 krb5_os_context os_ctx = &context->os_context;
895 krb5_ccache ccache = NULL;
896 krcc_data *data;
897 key_serial_t pkey = 0;
898
899 /* Determine the key containing principal information, if present. */
900 pkey = keyctl_search(cache_id, KRCC_KEY_TYPE_USER, KRCC_SPEC_PRINC_KEYNAME,
901 0);
902 if (pkey < 0)
903 pkey = 0;
904
905 ccache = malloc(sizeof(struct _krb5_ccache));
906 if (!ccache)
907 return ENOMEM;
908
909 ret = make_krcc_data(anchor_name, collection_name, subsidiary_name,
910 cache_id, collection_id, &data);
911 if (ret) {
912 free(ccache);
913 return ret;
914 }
915
916 data->princ_id = pkey;
917 ccache->ops = &krb5_krcc_ops;
918 ccache->data = data;
919 ccache->magic = KV5M_CCACHE;
920 *cache_out = ccache;
921
922 /* Look up time offsets if necessary. */
923 if ((context->library_options & KRB5_LIBOPT_SYNC_KDCTIME) &&
924 !(os_ctx->os_flags & KRB5_OS_TOFFSET_VALID)) {
925 if (get_time_offsets(context, ccache, &os_ctx->time_offset,
926 &os_ctx->usec_offset) == 0) {
927 os_ctx->os_flags &= ~KRB5_OS_TOFFSET_TIME;
928 os_ctx->os_flags |= KRB5_OS_TOFFSET_VALID;
929 }
930 }
931
932 return 0;
933 }
934
935 /* Create a keyring ccache handle for the given residual string. */
936 static krb5_error_code KRB5_CALLCONV
krcc_resolve(krb5_context context,krb5_ccache * id,const char * residual)937 krcc_resolve(krb5_context context, krb5_ccache *id, const char *residual)
938 {
939 krb5_error_code ret;
940 key_serial_t collection_id, cache_id;
941 char *anchor_name = NULL, *collection_name = NULL, *subsidiary_name = NULL;
942
943 ret = parse_residual(residual, &anchor_name, &collection_name,
944 &subsidiary_name);
945 if (ret)
946 goto cleanup;
947 ret = get_collection(anchor_name, collection_name, &collection_id);
948 if (ret)
949 goto cleanup;
950
951 if (subsidiary_name == NULL) {
952 /* Retrieve or initialize the primary name for the collection. */
953 ret = get_primary_name(context, anchor_name, collection_name,
954 collection_id, &subsidiary_name);
955 if (ret)
956 goto cleanup;
957 }
958
959 /* Look up the cache keyring ID, if the cache is already initialized. */
960 cache_id = keyctl_search(collection_id, KRCC_KEY_TYPE_KEYRING,
961 subsidiary_name, 0);
962 if (cache_id < 0)
963 cache_id = 0;
964
965 ret = make_cache(context, collection_id, cache_id, anchor_name,
966 collection_name, subsidiary_name, id);
967 if (ret)
968 goto cleanup;
969
970 cleanup:
971 free(anchor_name);
972 free(collection_name);
973 free(subsidiary_name);
974 return ret;
975 }
976
977 /* Prepare for a sequential iteration over the cache keyring. */
978 static krb5_error_code KRB5_CALLCONV
krcc_start_seq_get(krb5_context context,krb5_ccache id,krb5_cc_cursor * cursor)979 krcc_start_seq_get(krb5_context context, krb5_ccache id,
980 krb5_cc_cursor *cursor)
981 {
982 krcc_cursor krcursor;
983 krcc_data *data = id->data;
984 void *keys;
985 long size;
986
987 k5_cc_mutex_lock(context, &data->lock);
988
989 if (!data->cache_id) {
990 k5_cc_mutex_unlock(context, &data->lock);
991 return KRB5_FCC_NOFILE;
992 }
993
994 size = keyctl_read_alloc(data->cache_id, &keys);
995 if (size == -1) {
996 DEBUG_PRINT(("Error getting from keyring: %s\n", strerror(errno)));
997 k5_cc_mutex_unlock(context, &data->lock);
998 return KRB5_CC_IO;
999 }
1000
1001 krcursor = calloc(1, sizeof(*krcursor));
1002 if (krcursor == NULL) {
1003 free(keys);
1004 k5_cc_mutex_unlock(context, &data->lock);
1005 return KRB5_CC_NOMEM;
1006 }
1007
1008 krcursor->princ_id = data->princ_id;
1009 krcursor->offsets_id = keyctl_search(data->cache_id, KRCC_KEY_TYPE_USER,
1010 KRCC_TIME_OFFSETS, 0);
1011 krcursor->numkeys = size / sizeof(key_serial_t);
1012 krcursor->keys = keys;
1013
1014 k5_cc_mutex_unlock(context, &data->lock);
1015 *cursor = krcursor;
1016 return 0;
1017 }
1018
1019 /* Get the next credential from the cache keyring. */
1020 static krb5_error_code KRB5_CALLCONV
krcc_next_cred(krb5_context context,krb5_ccache id,krb5_cc_cursor * cursor,krb5_creds * creds)1021 krcc_next_cred(krb5_context context, krb5_ccache id, krb5_cc_cursor *cursor,
1022 krb5_creds *creds)
1023 {
1024 krcc_cursor krcursor;
1025 krb5_error_code ret;
1026 int psize;
1027 void *payload = NULL;
1028
1029 memset(creds, 0, sizeof(krb5_creds));
1030
1031 /* The cursor has the entire list of keys. */
1032 krcursor = *cursor;
1033 if (krcursor == NULL)
1034 return KRB5_CC_END;
1035
1036 while (krcursor->currkey < krcursor->numkeys) {
1037 /* If we're pointing at the entry with the principal, or at the key
1038 * with the time offsets, skip it. */
1039 if (krcursor->keys[krcursor->currkey] == krcursor->princ_id ||
1040 krcursor->keys[krcursor->currkey] == krcursor->offsets_id) {
1041 krcursor->currkey++;
1042 continue;
1043 }
1044
1045 /* Read the key; the right size buffer will be allocated and
1046 * returned. */
1047 psize = keyctl_read_alloc(krcursor->keys[krcursor->currkey],
1048 &payload);
1049 if (psize != -1) {
1050 krcursor->currkey++;
1051
1052 /* Unmarshal the cred using the file ccache version 4 format. */
1053 ret = k5_unmarshal_cred(payload, psize, 4, creds);
1054 free(payload);
1055 return ret;
1056 } else if (errno != ENOKEY && errno != EACCES) {
1057 DEBUG_PRINT(("Error reading key %d: %s\n",
1058 krcursor->keys[krcursor->currkey], strerror(errno)));
1059 return KRB5_FCC_NOFILE;
1060 }
1061
1062 /* The current key was unlinked, probably by a remove_cred call; move
1063 * on to the next one. */
1064 krcursor->currkey++;
1065 }
1066
1067 /* No more keys in keyring. */
1068 return KRB5_CC_END;
1069 }
1070
1071 /* Release an iteration cursor. */
1072 static krb5_error_code KRB5_CALLCONV
krcc_end_seq_get(krb5_context context,krb5_ccache id,krb5_cc_cursor * cursor)1073 krcc_end_seq_get(krb5_context context, krb5_ccache id, krb5_cc_cursor *cursor)
1074 {
1075 krcc_cursor krcursor = *cursor;
1076
1077 if (krcursor != NULL) {
1078 free(krcursor->keys);
1079 free(krcursor);
1080 }
1081 *cursor = NULL;
1082 return 0;
1083 }
1084
1085 /* Create keyring data for a credential cache. */
1086 static krb5_error_code
make_krcc_data(const char * anchor_name,const char * collection_name,const char * subsidiary_name,key_serial_t cache_id,key_serial_t collection_id,krcc_data ** data_out)1087 make_krcc_data(const char *anchor_name, const char *collection_name,
1088 const char *subsidiary_name, key_serial_t cache_id,
1089 key_serial_t collection_id, krcc_data **data_out)
1090 {
1091 krb5_error_code ret;
1092 krcc_data *data;
1093
1094 *data_out = NULL;
1095
1096 data = malloc(sizeof(krcc_data));
1097 if (data == NULL)
1098 return KRB5_CC_NOMEM;
1099
1100 ret = k5_cc_mutex_init(&data->lock);
1101 if (ret) {
1102 free(data);
1103 return ret;
1104 }
1105
1106 ret = make_subsidiary_residual(anchor_name, collection_name,
1107 subsidiary_name, &data->name);
1108 if (ret) {
1109 k5_cc_mutex_destroy(&data->lock);
1110 free(data);
1111 return ret;
1112 }
1113 data->princ_id = 0;
1114 data->cache_id = cache_id;
1115 data->collection_id = collection_id;
1116 data->is_legacy_type = (strcmp(anchor_name, KRCC_LEGACY_ANCHOR) == 0);
1117
1118 *data_out = data;
1119 return 0;
1120 }
1121
1122 /* Create a new keyring cache with a unique name. */
1123 static krb5_error_code KRB5_CALLCONV
krcc_generate_new(krb5_context context,krb5_ccache * id_out)1124 krcc_generate_new(krb5_context context, krb5_ccache *id_out)
1125 {
1126 krb5_ccache id = NULL;
1127 krb5_error_code ret;
1128 char *anchor_name = NULL, *collection_name = NULL, *subsidiary_name = NULL;
1129 char *new_subsidiary_name = NULL, *new_residual = NULL;
1130 krcc_data *data;
1131 key_serial_t collection_id;
1132 key_serial_t cache_id = 0;
1133
1134 *id_out = NULL;
1135
1136 /* Determine the collection in which we will create the cache.*/
1137 ret = get_default(context, &anchor_name, &collection_name,
1138 &subsidiary_name);
1139 if (ret)
1140 return ret;
1141 if (anchor_name == NULL) {
1142 ret = parse_residual(KRCC_DEFAULT_UNIQUE_COLLECTION, &anchor_name,
1143 &collection_name, &subsidiary_name);
1144 if (ret)
1145 return ret;
1146 }
1147 if (subsidiary_name != NULL) {
1148 k5_setmsg(context, KRB5_DCC_CANNOT_CREATE,
1149 _("Can't create new subsidiary cache because default cache "
1150 "is already a subsidiary"));
1151 ret = KRB5_DCC_CANNOT_CREATE;
1152 goto cleanup;
1153 }
1154
1155 /* Allocate memory */
1156 id = malloc(sizeof(struct _krb5_ccache));
1157 if (id == NULL) {
1158 ret = ENOMEM;
1159 goto cleanup;
1160 }
1161
1162 id->ops = &krb5_krcc_ops;
1163
1164 /* Make a unique keyring within the chosen collection. */
1165 ret = get_collection(anchor_name, collection_name, &collection_id);
1166 if (ret)
1167 goto cleanup;
1168 ret = unique_keyring(context, collection_id, &new_subsidiary_name,
1169 &cache_id);
1170 if (ret)
1171 goto cleanup;
1172
1173 ret = make_krcc_data(anchor_name, collection_name, new_subsidiary_name,
1174 cache_id, collection_id, &data);
1175 if (ret)
1176 goto cleanup;
1177
1178 id->data = data;
1179 krb5_change_cache();
1180
1181 cleanup:
1182 free(anchor_name);
1183 free(collection_name);
1184 free(subsidiary_name);
1185 free(new_subsidiary_name);
1186 free(new_residual);
1187 if (ret) {
1188 free(id);
1189 return ret;
1190 }
1191 *id_out = id;
1192 return 0;
1193 }
1194
1195 /* Return an alias to the residual string of the cache. */
1196 static const char *KRB5_CALLCONV
krcc_get_name(krb5_context context,krb5_ccache id)1197 krcc_get_name(krb5_context context, krb5_ccache id)
1198 {
1199 return ((krcc_data *)id->data)->name;
1200 }
1201
1202 /* Retrieve a copy of the default principal, if the cache is initialized. */
1203 static krb5_error_code KRB5_CALLCONV
krcc_get_principal(krb5_context context,krb5_ccache id,krb5_principal * princ_out)1204 krcc_get_principal(krb5_context context, krb5_ccache id,
1205 krb5_principal *princ_out)
1206 {
1207 krcc_data *data = id->data;
1208 krb5_error_code ret;
1209 void *payload = NULL;
1210 int psize;
1211
1212 *princ_out = NULL;
1213 k5_cc_mutex_lock(context, &data->lock);
1214
1215 if (!data->cache_id || !data->princ_id) {
1216 ret = KRB5_FCC_NOFILE;
1217 k5_setmsg(context, ret, _("Credentials cache keyring '%s' not found"),
1218 data->name);
1219 goto errout;
1220 }
1221
1222 psize = keyctl_read_alloc(data->princ_id, &payload);
1223 if (psize == -1) {
1224 DEBUG_PRINT(("Reading principal key %d: %s\n",
1225 data->princ_id, strerror(errno)));
1226 ret = KRB5_CC_IO;
1227 goto errout;
1228 }
1229
1230 /* Unmarshal the principal using the file ccache version 4 format. */
1231 ret = k5_unmarshal_princ(payload, psize, 4, princ_out);
1232
1233 errout:
1234 free(payload);
1235 k5_cc_mutex_unlock(context, &data->lock);
1236 return ret;
1237 }
1238
1239 /* Search for a credential within the cache keyring. */
1240 static krb5_error_code KRB5_CALLCONV
krcc_retrieve(krb5_context context,krb5_ccache id,krb5_flags whichfields,krb5_creds * mcreds,krb5_creds * creds)1241 krcc_retrieve(krb5_context context, krb5_ccache id,
1242 krb5_flags whichfields, krb5_creds *mcreds,
1243 krb5_creds *creds)
1244 {
1245 return k5_cc_retrieve_cred_default(context, id, whichfields, mcreds,
1246 creds);
1247 }
1248
1249 /* Remove a credential from the cache keyring. */
1250 static krb5_error_code KRB5_CALLCONV
krcc_remove_cred(krb5_context context,krb5_ccache cache,krb5_flags flags,krb5_creds * creds)1251 krcc_remove_cred(krb5_context context, krb5_ccache cache,
1252 krb5_flags flags, krb5_creds *creds)
1253 {
1254 krb5_error_code ret;
1255 krcc_data *data = cache->data;
1256 krb5_cc_cursor cursor;
1257 krb5_creds c;
1258 krcc_cursor krcursor;
1259 key_serial_t key;
1260 krb5_boolean match;
1261
1262 ret = krcc_start_seq_get(context, cache, &cursor);
1263 if (ret)
1264 return ret;
1265
1266 for (;;) {
1267 ret = krcc_next_cred(context, cache, &cursor, &c);
1268 if (ret)
1269 break;
1270 match = krb5int_cc_creds_match_request(context, flags, creds, &c);
1271 krb5_free_cred_contents(context, &c);
1272 if (match) {
1273 krcursor = cursor;
1274 key = krcursor->keys[krcursor->currkey - 1];
1275 if (keyctl_unlink(key, data->cache_id) == -1) {
1276 ret = errno;
1277 break;
1278 }
1279 }
1280 }
1281
1282 krcc_end_seq_get(context, cache, &cursor);
1283 return (ret == KRB5_CC_END) ? 0 : ret;
1284 }
1285
1286 /* Set flags on the cache. (We don't care about any flags.) */
1287 static krb5_error_code KRB5_CALLCONV
krcc_set_flags(krb5_context context,krb5_ccache id,krb5_flags flags)1288 krcc_set_flags(krb5_context context, krb5_ccache id, krb5_flags flags)
1289 {
1290 return 0;
1291 }
1292
1293 /* Get the current operational flags (of which we have none) for the cache. */
1294 static krb5_error_code KRB5_CALLCONV
krcc_get_flags(krb5_context context,krb5_ccache id,krb5_flags * flags_out)1295 krcc_get_flags(krb5_context context, krb5_ccache id, krb5_flags *flags_out)
1296 {
1297 *flags_out = 0;
1298 return 0;
1299 }
1300
1301 /* Store a credential in the cache keyring. */
1302 static krb5_error_code KRB5_CALLCONV
krcc_store(krb5_context context,krb5_ccache id,krb5_creds * creds)1303 krcc_store(krb5_context context, krb5_ccache id, krb5_creds *creds)
1304 {
1305 krb5_error_code ret;
1306 krcc_data *data = id->data;
1307 struct k5buf buf = EMPTY_K5BUF;
1308 char *keyname = NULL;
1309 key_serial_t cred_key;
1310 krb5_timestamp now;
1311
1312 k5_cc_mutex_lock(context, &data->lock);
1313
1314 if (!data->cache_id) {
1315 k5_cc_mutex_unlock(context, &data->lock);
1316 return KRB5_FCC_NOFILE;
1317 }
1318
1319 /* Get the service principal name and use it as the key name */
1320 ret = krb5_unparse_name(context, creds->server, &keyname);
1321 if (ret)
1322 goto errout;
1323
1324 /* Serialize credential using the file ccache version 4 format. */
1325 k5_buf_init_dynamic_zap(&buf);
1326 k5_marshal_cred(&buf, 4, creds);
1327 ret = k5_buf_status(&buf);
1328 if (ret)
1329 goto errout;
1330
1331 /* Add new key (credentials) into keyring */
1332 DEBUG_PRINT(("krcc_store: adding new key '%s' to keyring %d\n",
1333 keyname, data->cache_id));
1334 ret = add_cred_key(keyname, buf.data, buf.len, data->cache_id,
1335 data->is_legacy_type, &cred_key);
1336 if (ret)
1337 goto errout;
1338
1339 /* Set appropriate timeouts on cache keys. */
1340 ret = krb5_timeofday(context, &now);
1341 if (ret)
1342 goto errout;
1343
1344 if (ts_after(creds->times.endtime, now)) {
1345 (void)keyctl_set_timeout(cred_key,
1346 ts_interval(now, creds->times.endtime));
1347 }
1348
1349 update_keyring_expiration(context, id);
1350
1351 errout:
1352 k5_buf_free(&buf);
1353 krb5_free_unparsed_name(context, keyname);
1354 k5_cc_mutex_unlock(context, &data->lock);
1355 return ret;
1356 }
1357
1358 /* Lock the cache handle against other threads. (This does not lock the cache
1359 * keyring against other processes.) */
1360 static krb5_error_code KRB5_CALLCONV
krcc_lock(krb5_context context,krb5_ccache id)1361 krcc_lock(krb5_context context, krb5_ccache id)
1362 {
1363 krcc_data *data = id->data;
1364
1365 k5_cc_mutex_lock(context, &data->lock);
1366 return 0;
1367 }
1368
1369 /* Unlock the cache handle. */
1370 static krb5_error_code KRB5_CALLCONV
krcc_unlock(krb5_context context,krb5_ccache id)1371 krcc_unlock(krb5_context context, krb5_ccache id)
1372 {
1373 krcc_data *data = id->data;
1374
1375 k5_cc_mutex_unlock(context, &data->lock);
1376 return 0;
1377 }
1378
1379 static krb5_error_code
save_principal(krb5_context context,krb5_ccache id,krb5_principal princ)1380 save_principal(krb5_context context, krb5_ccache id, krb5_principal princ)
1381 {
1382 krcc_data *data = id->data;
1383 krb5_error_code ret;
1384 struct k5buf buf;
1385 key_serial_t newkey;
1386
1387 k5_cc_mutex_assert_locked(context, &data->lock);
1388
1389 /* Serialize princ using the file ccache version 4 format. */
1390 k5_buf_init_dynamic(&buf);
1391 k5_marshal_princ(&buf, 4, princ);
1392 if (k5_buf_status(&buf) != 0)
1393 return ENOMEM;
1394
1395 /* Add new key into keyring */
1396 #ifdef KRCC_DEBUG
1397 {
1398 krb5_error_code rc;
1399 char *princname = NULL;
1400 rc = krb5_unparse_name(context, princ, &princname);
1401 DEBUG_PRINT(("save_principal: adding new key '%s' "
1402 "to keyring %d for principal '%s'\n",
1403 KRCC_SPEC_PRINC_KEYNAME, data->cache_id,
1404 rc ? "<unknown>" : princname));
1405 if (rc == 0)
1406 krb5_free_unparsed_name(context, princname);
1407 }
1408 #endif
1409 newkey = add_key(KRCC_KEY_TYPE_USER, KRCC_SPEC_PRINC_KEYNAME, buf.data,
1410 buf.len, data->cache_id);
1411 if (newkey < 0) {
1412 ret = errno;
1413 DEBUG_PRINT(("Error adding principal key: %s\n", strerror(ret)));
1414 } else {
1415 data->princ_id = newkey;
1416 ret = 0;
1417 }
1418
1419 k5_buf_free(&buf);
1420 return ret;
1421 }
1422
1423 /* Add a key to the cache keyring containing the given time offsets. */
1424 static krb5_error_code
save_time_offsets(krb5_context context,krb5_ccache id,int32_t time_offset,int32_t usec_offset)1425 save_time_offsets(krb5_context context, krb5_ccache id, int32_t time_offset,
1426 int32_t usec_offset)
1427 {
1428 krcc_data *data = id->data;
1429 key_serial_t newkey;
1430 unsigned char payload[8];
1431
1432 k5_cc_mutex_assert_locked(context, &data->lock);
1433
1434 /* Prepare the payload. */
1435 store_32_be(time_offset, payload);
1436 store_32_be(usec_offset, payload + 4);
1437
1438 /* Add new key into keyring. */
1439 newkey = add_key(KRCC_KEY_TYPE_USER, KRCC_TIME_OFFSETS, payload, 8,
1440 data->cache_id);
1441 if (newkey == -1)
1442 return errno;
1443 return 0;
1444 }
1445
1446 /* Retrieve and parse the key in the cache keyring containing time offsets. */
1447 static krb5_error_code
get_time_offsets(krb5_context context,krb5_ccache id,int32_t * time_offset,int32_t * usec_offset)1448 get_time_offsets(krb5_context context, krb5_ccache id, int32_t *time_offset,
1449 int32_t *usec_offset)
1450 {
1451 krcc_data *data = id->data;
1452 krb5_error_code ret = 0;
1453 key_serial_t key;
1454 void *payload = NULL;
1455 int psize;
1456
1457 k5_cc_mutex_lock(context, &data->lock);
1458
1459 if (!data->cache_id) {
1460 ret = KRB5_FCC_NOFILE;
1461 goto errout;
1462 }
1463
1464 key = keyctl_search(data->cache_id, KRCC_KEY_TYPE_USER, KRCC_TIME_OFFSETS,
1465 0);
1466 if (key == -1) {
1467 ret = ENOENT;
1468 goto errout;
1469 }
1470
1471 psize = keyctl_read_alloc(key, &payload);
1472 if (psize == -1) {
1473 DEBUG_PRINT(("Reading time offsets key %d: %s\n",
1474 key, strerror(errno)));
1475 ret = KRB5_CC_IO;
1476 goto errout;
1477 }
1478
1479 if (psize < 8) {
1480 ret = KRB5_CC_END;
1481 goto errout;
1482 }
1483 *time_offset = load_32_be(payload);
1484 *usec_offset = load_32_be((char *)payload + 4);
1485
1486 errout:
1487 free(payload);
1488 k5_cc_mutex_unlock(context, &data->lock);
1489 return ret;
1490 }
1491
1492 struct krcc_ptcursor_data {
1493 key_serial_t collection_id;
1494 char *anchor_name;
1495 char *collection_name;
1496 char *subsidiary_name;
1497 char *primary_name;
1498 krb5_boolean first;
1499 long num_keys;
1500 long next_key;
1501 key_serial_t *keys;
1502 };
1503
1504 static krb5_error_code KRB5_CALLCONV
krcc_ptcursor_new(krb5_context context,krb5_cc_ptcursor * cursor_out)1505 krcc_ptcursor_new(krb5_context context, krb5_cc_ptcursor *cursor_out)
1506 {
1507 struct krcc_ptcursor_data *ptd;
1508 krb5_cc_ptcursor cursor;
1509 krb5_error_code ret;
1510 void *keys;
1511 long size;
1512
1513 *cursor_out = NULL;
1514
1515 cursor = k5alloc(sizeof(*cursor), &ret);
1516 if (cursor == NULL)
1517 return ENOMEM;
1518 ptd = k5alloc(sizeof(*ptd), &ret);
1519 if (ptd == NULL)
1520 goto error;
1521 cursor->ops = &krb5_krcc_ops;
1522 cursor->data = ptd;
1523 ptd->first = TRUE;
1524
1525 ret = get_default(context, &ptd->anchor_name, &ptd->collection_name,
1526 &ptd->subsidiary_name);
1527 if (ret)
1528 goto error;
1529
1530 /* If there is no default collection, return an empty cursor. */
1531 if (ptd->anchor_name == NULL) {
1532 *cursor_out = cursor;
1533 return 0;
1534 }
1535
1536 ret = get_collection(ptd->anchor_name, ptd->collection_name,
1537 &ptd->collection_id);
1538 if (ret)
1539 goto error;
1540
1541 if (ptd->subsidiary_name == NULL) {
1542 ret = get_primary_name(context, ptd->anchor_name,
1543 ptd->collection_name, ptd->collection_id,
1544 &ptd->primary_name);
1545 if (ret)
1546 goto error;
1547
1548 size = keyctl_read_alloc(ptd->collection_id, &keys);
1549 if (size == -1) {
1550 ret = errno;
1551 goto error;
1552 }
1553 ptd->keys = keys;
1554 ptd->num_keys = size / sizeof(key_serial_t);
1555 }
1556
1557 *cursor_out = cursor;
1558 return 0;
1559
1560 error:
1561 krcc_ptcursor_free(context, &cursor);
1562 return ret;
1563 }
1564
1565 static krb5_error_code KRB5_CALLCONV
krcc_ptcursor_next(krb5_context context,krb5_cc_ptcursor cursor,krb5_ccache * cache_out)1566 krcc_ptcursor_next(krb5_context context, krb5_cc_ptcursor cursor,
1567 krb5_ccache *cache_out)
1568 {
1569 krb5_error_code ret;
1570 struct krcc_ptcursor_data *ptd = cursor->data;
1571 key_serial_t key, cache_id = 0;
1572 const char *first_name, *keytype, *sep, *subsidiary_name;
1573 size_t keytypelen;
1574 char *description = NULL;
1575
1576 *cache_out = NULL;
1577
1578 /* No keyring available */
1579 if (ptd->collection_id == 0)
1580 return 0;
1581
1582 if (ptd->first) {
1583 /* Look for the primary cache for a collection cursor, or the
1584 * subsidiary cache for a subsidiary cursor. */
1585 ptd->first = FALSE;
1586 first_name = (ptd->primary_name != NULL) ? ptd->primary_name :
1587 ptd->subsidiary_name;
1588 cache_id = keyctl_search(ptd->collection_id, KRCC_KEY_TYPE_KEYRING,
1589 first_name, 0);
1590 if (cache_id != -1) {
1591 return make_cache(context, ptd->collection_id, cache_id,
1592 ptd->anchor_name, ptd->collection_name,
1593 first_name, cache_out);
1594 }
1595 }
1596
1597 /* A subsidiary cursor yields at most the first cache. */
1598 if (ptd->subsidiary_name != NULL)
1599 return 0;
1600
1601 keytype = KRCC_KEY_TYPE_KEYRING ";";
1602 keytypelen = strlen(keytype);
1603
1604 for (; ptd->next_key < ptd->num_keys; ptd->next_key++) {
1605 /* Free any previously retrieved key description. */
1606 free(description);
1607 description = NULL;
1608
1609 /*
1610 * Get the key description, which should have the form:
1611 * typename;UID;GID;permissions;description
1612 */
1613 key = ptd->keys[ptd->next_key];
1614 if (keyctl_describe_alloc(key, &description) < 0)
1615 continue;
1616 sep = strrchr(description, ';');
1617 if (sep == NULL)
1618 continue;
1619 subsidiary_name = sep + 1;
1620
1621 /* Skip this key if it isn't a keyring. */
1622 if (strncmp(description, keytype, keytypelen) != 0)
1623 continue;
1624
1625 /* Don't repeat the primary cache. */
1626 if (strcmp(subsidiary_name, ptd->primary_name) == 0)
1627 continue;
1628
1629 /* We found a valid key */
1630 ptd->next_key++;
1631 ret = make_cache(context, ptd->collection_id, key, ptd->anchor_name,
1632 ptd->collection_name, subsidiary_name, cache_out);
1633 free(description);
1634 return ret;
1635 }
1636
1637 free(description);
1638 return 0;
1639 }
1640
1641 static krb5_error_code KRB5_CALLCONV
krcc_ptcursor_free(krb5_context context,krb5_cc_ptcursor * cursor)1642 krcc_ptcursor_free(krb5_context context, krb5_cc_ptcursor *cursor)
1643 {
1644 struct krcc_ptcursor_data *ptd = (*cursor)->data;
1645
1646 if (ptd != NULL) {
1647 free(ptd->anchor_name);
1648 free(ptd->collection_name);
1649 free(ptd->subsidiary_name);
1650 free(ptd->primary_name);
1651 free(ptd->keys);
1652 free(ptd);
1653 }
1654 free(*cursor);
1655 *cursor = NULL;
1656 return 0;
1657 }
1658
1659 static krb5_error_code KRB5_CALLCONV
krcc_switch_to(krb5_context context,krb5_ccache cache)1660 krcc_switch_to(krb5_context context, krb5_ccache cache)
1661 {
1662 krcc_data *data = cache->data;
1663 krb5_error_code ret;
1664 char *anchor_name = NULL, *collection_name = NULL, *subsidiary_name = NULL;
1665 key_serial_t collection_id;
1666
1667 ret = parse_residual(data->name, &anchor_name, &collection_name,
1668 &subsidiary_name);
1669 if (ret)
1670 goto cleanup;
1671 ret = get_collection(anchor_name, collection_name, &collection_id);
1672 if (ret)
1673 goto cleanup;
1674 ret = set_primary_name(context, collection_id, subsidiary_name);
1675
1676 cleanup:
1677 free(anchor_name);
1678 free(collection_name);
1679 free(subsidiary_name);
1680 return ret;
1681 }
1682
1683 /*
1684 * ccache implementation storing credentials in the Linux keyring facility
1685 * The default is to put them at the session keyring level.
1686 * If "KEYRING:process:" or "KEYRING:thread:" is specified, then they will
1687 * be stored at the process or thread level respectively.
1688 */
1689 const krb5_cc_ops krb5_krcc_ops = {
1690 0,
1691 "KEYRING",
1692 krcc_get_name,
1693 krcc_resolve,
1694 krcc_generate_new,
1695 krcc_initialize,
1696 krcc_destroy,
1697 krcc_close,
1698 krcc_store,
1699 krcc_retrieve,
1700 krcc_get_principal,
1701 krcc_start_seq_get,
1702 krcc_next_cred,
1703 krcc_end_seq_get,
1704 krcc_remove_cred,
1705 krcc_set_flags,
1706 krcc_get_flags, /* added after 1.4 release */
1707 krcc_ptcursor_new,
1708 krcc_ptcursor_next,
1709 krcc_ptcursor_free,
1710 NULL, /* move */
1711 NULL, /* wasdefault */
1712 krcc_lock,
1713 krcc_unlock,
1714 krcc_switch_to,
1715 };
1716
1717 #else /* !USE_KEYRING_CCACHE */
1718
1719 /*
1720 * Export this, but it shouldn't be used.
1721 */
1722 const krb5_cc_ops krb5_krcc_ops = {
1723 0,
1724 "KEYRING",
1725 NULL,
1726 NULL,
1727 NULL,
1728 NULL,
1729 NULL,
1730 NULL,
1731 NULL,
1732 NULL,
1733 NULL,
1734 NULL,
1735 NULL,
1736 NULL,
1737 NULL,
1738 NULL,
1739 NULL, /* added after 1.4 release */
1740 NULL,
1741 NULL,
1742 NULL,
1743 NULL,
1744 NULL,
1745 NULL,
1746 NULL,
1747 NULL,
1748 };
1749 #endif /* USE_KEYRING_CCACHE */
1750