2 * Copyright (c) 1997-2004 Kungliga Tekniska Högskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
6 * Portions Copyright (c) 2009 Apple Inc. All rights reserved.
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
19 * 3. Neither the name of the Institute nor the names of its contributors
20 * may be used to endorse or promote products derived from this software
21 * without specific prior written permission.
23 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
36 #include "krb5_locl.h"
38 typedef struct krb5_mcache {
41 unsigned int anonymous:1;
43 krb5_principal primary_principal;
48 struct krb5_mcache *next;
50 krb5_deltat kdc_offset;
54 static HEIMDAL_MUTEX mcc_mutex = HEIMDAL_MUTEX_INITIALIZER;
55 static struct krb5_mcache *mcc_head;
57 #define MCACHE(X) ((krb5_mcache *)(X)->data.data)
59 #define MISDEAD(X) ((X)->dead)
61 static krb5_error_code KRB5_CALLCONV
62 mcc_get_name_2(krb5_context context,
69 *name = MCACHE(id)->name;
73 *sub = MCACHE(id)->name;
77 static krb5_error_code
78 mcc_alloc(krb5_context context, const char *name, krb5_mcache **out)
83 unsigned create_anonymous = 0;
88 return krb5_enomem(context);
94 return EAGAIN; /* XXX */
97 ret = asprintf(&m->name, "u%p-%llu", m, (unsigned long long)counter);
98 } else if (strcmp(name, "anonymous") == 0) {
99 ret = asprintf(&m->name, "anonymous-%p-%llu", m, (unsigned long long)counter);
100 create_anonymous = 1;
102 m->name = strdup(name);
104 if(ret < 0 || m->name == NULL) {
106 return krb5_enomem(context);
109 /* check for dups first */
110 HEIMDAL_MUTEX_lock(&mcc_mutex);
111 for (m_c = mcc_head; m_c != NULL; m_c = m_c->next)
112 if (strcmp(m->name, m_c->name) == 0)
117 if (name && !create_anonymous) {
118 /* We raced with another thread to create this cache */
120 HEIMDAL_MUTEX_lock(&(m->mutex));
122 HEIMDAL_MUTEX_unlock(&(m->mutex));
124 /* How likely are we to conflict on new_unique anyways?? */
128 HEIMDAL_MUTEX_unlock(&mcc_mutex);
131 HEIMDAL_MUTEX_unlock(&mcc_mutex);
136 m->anonymous = create_anonymous;
139 m->primary_principal = NULL;
141 m->mtime = time(NULL);
144 HEIMDAL_MUTEX_init(&(m->mutex));
146 HEIMDAL_MUTEX_unlock(&mcc_mutex);
151 static krb5_error_code KRB5_CALLCONV
152 mcc_resolve_2(krb5_context context,
160 if ((ret = mcc_alloc(context, sub && *sub ? sub : res, &m)))
163 (*id)->data.data = m;
164 (*id)->data.length = sizeof(*m);
170 static krb5_error_code KRB5_CALLCONV
171 mcc_gen_new(krb5_context context, krb5_ccache *id)
176 if ((ret = mcc_alloc(context, NULL, &m)))
179 (*id)->data.data = m;
180 (*id)->data.length = sizeof(*m);
185 static void KRB5_CALLCONV
186 mcc_destroy_internal(krb5_context context,
191 if (m->primary_principal != NULL) {
192 krb5_free_principal (context, m->primary_principal);
193 m->primary_principal = NULL;
201 krb5_free_cred_contents (context, &l->cred);
211 static krb5_error_code KRB5_CALLCONV
212 mcc_initialize(krb5_context context,
214 krb5_principal primary_principal)
216 krb5_mcache *m = MCACHE(id);
217 krb5_error_code ret = 0;
218 HEIMDAL_MUTEX_lock(&(m->mutex));
219 heim_assert(m->refcnt != 0, "resurection released mcache");
221 * It's important to destroy any existing
222 * creds here, that matches the baheviour
223 * of all other backends and also the
224 * MEMORY: backend in MIT.
226 mcc_destroy_internal(context, m);
229 m->mtime = time(NULL);
230 ret = krb5_copy_principal (context,
232 &m->primary_principal);
233 HEIMDAL_MUTEX_unlock(&(m->mutex));
238 mcc_close_internal(krb5_mcache *m)
240 HEIMDAL_MUTEX_lock(&(m->mutex));
241 heim_assert(m->refcnt != 0, "closed dead cache mcache");
242 if (--m->refcnt != 0) {
243 HEIMDAL_MUTEX_unlock(&(m->mutex));
248 HEIMDAL_MUTEX_unlock(&(m->mutex));
251 HEIMDAL_MUTEX_unlock(&(m->mutex));
255 static krb5_error_code KRB5_CALLCONV
256 mcc_close(krb5_context context,
259 krb5_mcache *m = MCACHE(id);
261 if (mcc_close_internal(MCACHE(id))) {
262 HEIMDAL_MUTEX_destroy(&(m->mutex));
263 krb5_data_free(&id->data);
268 static krb5_error_code KRB5_CALLCONV
269 mcc_destroy(krb5_context context,
272 krb5_mcache **n, *m = MCACHE(id);
274 HEIMDAL_MUTEX_lock(&mcc_mutex);
275 HEIMDAL_MUTEX_lock(&(m->mutex));
278 HEIMDAL_MUTEX_unlock(&(m->mutex));
279 HEIMDAL_MUTEX_unlock(&mcc_mutex);
280 krb5_abortx(context, "mcc_destroy: refcnt already 0");
284 /* if this is an active mcache, remove it from the linked
285 list, and free all data */
286 for(n = &mcc_head; n && *n; n = &(*n)->next) {
292 mcc_destroy_internal(context, m);
294 HEIMDAL_MUTEX_unlock(&(m->mutex));
295 HEIMDAL_MUTEX_unlock(&mcc_mutex);
299 static krb5_error_code KRB5_CALLCONV
300 mcc_store_cred(krb5_context context,
304 krb5_mcache *m = MCACHE(id);
308 HEIMDAL_MUTEX_lock(&(m->mutex));
311 HEIMDAL_MUTEX_unlock(&(m->mutex));
315 l = malloc (sizeof(*l));
317 return krb5_enomem(context);
320 memset (&l->cred, 0, sizeof(l->cred));
321 ret = krb5_copy_creds_contents (context, creds, &l->cred);
325 HEIMDAL_MUTEX_unlock(&(m->mutex));
328 m->mtime = time(NULL);
329 HEIMDAL_MUTEX_unlock(&(m->mutex));
333 static krb5_error_code KRB5_CALLCONV
334 mcc_get_principal(krb5_context context,
336 krb5_principal *principal)
338 krb5_mcache *m = MCACHE(id);
339 krb5_error_code ret = 0;
341 HEIMDAL_MUTEX_lock(&(m->mutex));
342 if (MISDEAD(m) || m->primary_principal == NULL) {
343 HEIMDAL_MUTEX_unlock(&(m->mutex));
346 ret = krb5_copy_principal (context,
347 m->primary_principal,
349 HEIMDAL_MUTEX_unlock(&(m->mutex));
353 static krb5_error_code KRB5_CALLCONV
354 mcc_get_first (krb5_context context,
356 krb5_cc_cursor *cursor)
358 krb5_mcache *m = MCACHE(id);
360 HEIMDAL_MUTEX_lock(&(m->mutex));
362 HEIMDAL_MUTEX_unlock(&(m->mutex));
367 HEIMDAL_MUTEX_unlock(&(m->mutex));
371 static krb5_error_code KRB5_CALLCONV
372 mcc_get_next (krb5_context context,
374 krb5_cc_cursor *cursor,
377 krb5_mcache *m = MCACHE(id);
380 HEIMDAL_MUTEX_lock(&(m->mutex));
382 HEIMDAL_MUTEX_unlock(&(m->mutex));
385 HEIMDAL_MUTEX_unlock(&(m->mutex));
390 return krb5_copy_creds_contents (context,
397 static krb5_error_code KRB5_CALLCONV
398 mcc_end_get (krb5_context context,
400 krb5_cc_cursor *cursor)
405 static krb5_error_code KRB5_CALLCONV
406 mcc_remove_cred(krb5_context context,
411 krb5_mcache *m = MCACHE(id);
414 HEIMDAL_MUTEX_lock(&(m->mutex));
416 for(q = &m->creds, p = *q; p; p = *q) {
417 if(krb5_compare_creds(context, which, mcreds, &p->cred)) {
419 krb5_free_cred_contents(context, &p->cred);
421 m->mtime = time(NULL);
425 HEIMDAL_MUTEX_unlock(&(m->mutex));
429 static krb5_error_code KRB5_CALLCONV
430 mcc_set_flags(krb5_context context,
442 mcc_get_cache_find_next_internal(krb5_mcache *next)
444 HEIMDAL_MUTEX_lock(&mcc_mutex);
445 for (; next != NULL && next->anonymous; next = next->next) {
446 /* noop: iterate over all anonymous entries */
449 HEIMDAL_MUTEX_lock(&(next->mutex));
451 HEIMDAL_MUTEX_unlock(&(next->mutex));
454 HEIMDAL_MUTEX_unlock(&mcc_mutex);
459 static krb5_error_code KRB5_CALLCONV
460 mcc_get_cache_first(krb5_context context, krb5_cc_cursor *cursor)
462 struct mcache_iter *iter;
464 iter = calloc(1, sizeof(*iter));
466 return krb5_enomem(context);
468 iter->cache = mcc_get_cache_find_next_internal(mcc_head);
474 static krb5_error_code KRB5_CALLCONV
475 mcc_get_cache_next(krb5_context context, krb5_cc_cursor cursor, krb5_ccache *id)
477 struct mcache_iter *iter = cursor;
481 if (iter->cache == NULL)
485 iter->cache = mcc_get_cache_find_next_internal(m);
487 ret = _krb5_cc_allocate(context, &krb5_mcc_ops, id);
491 (*id)->data.data = m;
492 (*id)->data.length = sizeof(*m);
497 static krb5_error_code KRB5_CALLCONV
498 mcc_end_cache_get(krb5_context context, krb5_cc_cursor cursor)
500 struct mcache_iter *iter = cursor;
503 mcc_close_internal(iter->cache);
509 static krb5_error_code KRB5_CALLCONV
510 mcc_move(krb5_context context, krb5_ccache from, krb5_ccache to)
512 krb5_mcache *mfrom = MCACHE(from), *mto = MCACHE(to);
514 krb5_principal principal;
517 HEIMDAL_MUTEX_lock(&mcc_mutex);
519 /* drop the from cache from the linked list to avoid lookups */
520 for(n = &mcc_head; n && *n; n = &(*n)->next) {
527 HEIMDAL_MUTEX_lock(&(mfrom->mutex));
528 HEIMDAL_MUTEX_lock(&(mto->mutex));
531 mto->creds = mfrom->creds;
532 mfrom->creds = creds;
534 principal = mto->primary_principal;
535 mto->primary_principal = mfrom->primary_principal;
536 mfrom->primary_principal = principal;
538 mto->mtime = mfrom->mtime = time(NULL);
540 HEIMDAL_MUTEX_unlock(&(mfrom->mutex));
541 HEIMDAL_MUTEX_unlock(&(mto->mutex));
542 HEIMDAL_MUTEX_unlock(&mcc_mutex);
544 krb5_cc_destroy(context, from);
548 static krb5_error_code KRB5_CALLCONV
549 mcc_default_name(krb5_context context, char **str)
551 *str = strdup("MEMORY:");
553 return krb5_enomem(context);
557 static krb5_error_code KRB5_CALLCONV
558 mcc_lastchange(krb5_context context, krb5_ccache id, krb5_timestamp *mtime)
560 krb5_mcache *m = MCACHE(id);
561 HEIMDAL_MUTEX_lock(&(m->mutex));
563 HEIMDAL_MUTEX_unlock(&(m->mutex));
567 static krb5_error_code KRB5_CALLCONV
568 mcc_set_kdc_offset(krb5_context context, krb5_ccache id, krb5_deltat kdc_offset)
570 krb5_mcache *m = MCACHE(id);
571 HEIMDAL_MUTEX_lock(&(m->mutex));
572 m->kdc_offset = kdc_offset;
573 HEIMDAL_MUTEX_unlock(&(m->mutex));
577 static krb5_error_code KRB5_CALLCONV
578 mcc_get_kdc_offset(krb5_context context, krb5_ccache id, krb5_deltat *kdc_offset)
580 krb5_mcache *m = MCACHE(id);
581 HEIMDAL_MUTEX_lock(&(m->mutex));
582 *kdc_offset = m->kdc_offset;
583 HEIMDAL_MUTEX_unlock(&(m->mutex));
589 * Variable containing the MEMORY based credential cache implemention.
591 * @ingroup krb5_ccache
594 KRB5_LIB_VARIABLE const krb5_cc_ops krb5_mcc_ops = {
595 KRB5_CC_OPS_VERSION_5,
604 NULL, /* mcc_retrieve */