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)
115 if (name
&& !create_anonymous
) {
116 /* 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
);
228 m
->kdc_offset
= context
->kdc_sec_offset
;
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 implementation.
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 */