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
)
87 return krb5_enomem(context
);
93 return EAGAIN
; /* XXX */
96 ret
= asprintf(&m
->name
, "u%p-%llu", m
, (unsigned long long)counter
);
98 m
->name
= strdup(name
);
99 if(ret
< 0 || m
->name
== NULL
) {
101 return krb5_enomem(context
);
103 if (strcmp(m
->name
, "anonymous") == 0) {
104 HEIMDAL_MUTEX_init(&(m
->mutex
));
108 m
->primary_principal
= NULL
;
110 m
->mtime
= time(NULL
);
117 /* check for dups first */
118 HEIMDAL_MUTEX_lock(&mcc_mutex
);
119 for (m_c
= mcc_head
; m_c
!= NULL
; m_c
= m_c
->next
)
120 if (strcmp(m
->name
, m_c
->name
) == 0)
124 /* We raced with another thread to create this cache */
128 HEIMDAL_MUTEX_lock(&(m
->mutex
));
130 HEIMDAL_MUTEX_unlock(&(m
->mutex
));
132 /* How likely are we to conflict on new_unique anyways?? */
136 HEIMDAL_MUTEX_unlock(&mcc_mutex
);
139 HEIMDAL_MUTEX_unlock(&mcc_mutex
);
147 m
->primary_principal
= NULL
;
149 m
->mtime
= time(NULL
);
152 HEIMDAL_MUTEX_init(&(m
->mutex
));
154 HEIMDAL_MUTEX_unlock(&mcc_mutex
);
159 static krb5_error_code KRB5_CALLCONV
160 mcc_resolve_2(krb5_context context
,
168 if ((ret
= mcc_alloc(context
, sub
&& *sub
? sub
: res
, &m
)))
171 (*id
)->data
.data
= m
;
172 (*id
)->data
.length
= sizeof(*m
);
178 static krb5_error_code KRB5_CALLCONV
179 mcc_gen_new(krb5_context context
, krb5_ccache
*id
)
184 if ((ret
= mcc_alloc(context
, NULL
, &m
)))
187 (*id
)->data
.data
= m
;
188 (*id
)->data
.length
= sizeof(*m
);
193 static void KRB5_CALLCONV
194 mcc_destroy_internal(krb5_context context
,
199 if (m
->primary_principal
!= NULL
) {
200 krb5_free_principal (context
, m
->primary_principal
);
201 m
->primary_principal
= NULL
;
209 krb5_free_cred_contents (context
, &l
->cred
);
219 static krb5_error_code KRB5_CALLCONV
220 mcc_initialize(krb5_context context
,
222 krb5_principal primary_principal
)
224 krb5_mcache
*m
= MCACHE(id
);
225 krb5_error_code ret
= 0;
226 HEIMDAL_MUTEX_lock(&(m
->mutex
));
227 heim_assert(m
->refcnt
!= 0, "resurection released mcache");
229 * It's important to destroy any existing
230 * creds here, that matches the baheviour
231 * of all other backends and also the
232 * MEMORY: backend in MIT.
234 mcc_destroy_internal(context
, m
);
236 m
->kdc_offset
= context
->kdc_sec_offset
;
237 m
->mtime
= time(NULL
);
238 ret
= krb5_copy_principal (context
,
240 &m
->primary_principal
);
241 HEIMDAL_MUTEX_unlock(&(m
->mutex
));
246 mcc_close_internal(krb5_mcache
*m
)
248 HEIMDAL_MUTEX_lock(&(m
->mutex
));
249 heim_assert(m
->refcnt
!= 0, "closed dead cache mcache");
250 if (--m
->refcnt
!= 0) {
251 HEIMDAL_MUTEX_unlock(&(m
->mutex
));
256 HEIMDAL_MUTEX_unlock(&(m
->mutex
));
259 HEIMDAL_MUTEX_unlock(&(m
->mutex
));
263 static krb5_error_code KRB5_CALLCONV
264 mcc_close(krb5_context context
,
267 krb5_mcache
*m
= MCACHE(id
);
269 if (mcc_close_internal(MCACHE(id
))) {
270 HEIMDAL_MUTEX_destroy(&(m
->mutex
));
271 krb5_data_free(&id
->data
);
276 static krb5_error_code KRB5_CALLCONV
277 mcc_destroy(krb5_context context
,
280 krb5_mcache
**n
, *m
= MCACHE(id
);
283 HEIMDAL_MUTEX_lock(&(m
->mutex
));
284 if (m
->refcnt
== 0) {
285 HEIMDAL_MUTEX_unlock(&(m
->mutex
));
286 krb5_abortx(context
, "mcc_destroy: refcnt already 0");
289 mcc_destroy_internal(context
, m
);
290 HEIMDAL_MUTEX_unlock(&(m
->mutex
));
294 HEIMDAL_MUTEX_lock(&mcc_mutex
);
295 HEIMDAL_MUTEX_lock(&(m
->mutex
));
298 HEIMDAL_MUTEX_unlock(&(m
->mutex
));
299 HEIMDAL_MUTEX_unlock(&mcc_mutex
);
300 krb5_abortx(context
, "mcc_destroy: refcnt already 0");
304 /* if this is an active mcache, remove it from the linked
305 list, and free all data */
306 for(n
= &mcc_head
; n
&& *n
; n
= &(*n
)->next
) {
312 mcc_destroy_internal(context
, m
);
314 HEIMDAL_MUTEX_unlock(&(m
->mutex
));
315 HEIMDAL_MUTEX_unlock(&mcc_mutex
);
319 static krb5_error_code KRB5_CALLCONV
320 mcc_store_cred(krb5_context context
,
324 krb5_mcache
*m
= MCACHE(id
);
328 HEIMDAL_MUTEX_lock(&(m
->mutex
));
331 HEIMDAL_MUTEX_unlock(&(m
->mutex
));
335 l
= malloc (sizeof(*l
));
337 return krb5_enomem(context
);
340 memset (&l
->cred
, 0, sizeof(l
->cred
));
341 ret
= krb5_copy_creds_contents (context
, creds
, &l
->cred
);
345 HEIMDAL_MUTEX_unlock(&(m
->mutex
));
348 m
->mtime
= time(NULL
);
349 HEIMDAL_MUTEX_unlock(&(m
->mutex
));
353 static krb5_error_code KRB5_CALLCONV
354 mcc_get_principal(krb5_context context
,
356 krb5_principal
*principal
)
358 krb5_mcache
*m
= MCACHE(id
);
359 krb5_error_code ret
= 0;
361 HEIMDAL_MUTEX_lock(&(m
->mutex
));
362 if (MISDEAD(m
) || m
->primary_principal
== NULL
) {
363 HEIMDAL_MUTEX_unlock(&(m
->mutex
));
366 ret
= krb5_copy_principal (context
,
367 m
->primary_principal
,
369 HEIMDAL_MUTEX_unlock(&(m
->mutex
));
373 static krb5_error_code KRB5_CALLCONV
374 mcc_get_first (krb5_context context
,
376 krb5_cc_cursor
*cursor
)
378 krb5_mcache
*m
= MCACHE(id
);
380 HEIMDAL_MUTEX_lock(&(m
->mutex
));
382 HEIMDAL_MUTEX_unlock(&(m
->mutex
));
387 HEIMDAL_MUTEX_unlock(&(m
->mutex
));
391 static krb5_error_code KRB5_CALLCONV
392 mcc_get_next (krb5_context context
,
394 krb5_cc_cursor
*cursor
,
397 krb5_mcache
*m
= MCACHE(id
);
400 HEIMDAL_MUTEX_lock(&(m
->mutex
));
402 HEIMDAL_MUTEX_unlock(&(m
->mutex
));
405 HEIMDAL_MUTEX_unlock(&(m
->mutex
));
410 return krb5_copy_creds_contents (context
,
417 static krb5_error_code KRB5_CALLCONV
418 mcc_end_get (krb5_context context
,
420 krb5_cc_cursor
*cursor
)
425 static krb5_error_code KRB5_CALLCONV
426 mcc_remove_cred(krb5_context context
,
431 krb5_mcache
*m
= MCACHE(id
);
434 HEIMDAL_MUTEX_lock(&(m
->mutex
));
436 for(q
= &m
->creds
, p
= *q
; p
; p
= *q
) {
437 if(krb5_compare_creds(context
, which
, mcreds
, &p
->cred
)) {
439 krb5_free_cred_contents(context
, &p
->cred
);
441 m
->mtime
= time(NULL
);
445 HEIMDAL_MUTEX_unlock(&(m
->mutex
));
449 static krb5_error_code KRB5_CALLCONV
450 mcc_set_flags(krb5_context context
,
461 static krb5_error_code KRB5_CALLCONV
462 mcc_get_cache_first(krb5_context context
, krb5_cc_cursor
*cursor
)
464 struct mcache_iter
*iter
;
466 iter
= calloc(1, sizeof(*iter
));
468 return krb5_enomem(context
);
470 HEIMDAL_MUTEX_lock(&mcc_mutex
);
471 iter
->cache
= mcc_head
;
473 HEIMDAL_MUTEX_lock(&(iter
->cache
->mutex
));
474 iter
->cache
->refcnt
++;
475 HEIMDAL_MUTEX_unlock(&(iter
->cache
->mutex
));
477 HEIMDAL_MUTEX_unlock(&mcc_mutex
);
483 static krb5_error_code KRB5_CALLCONV
484 mcc_get_cache_next(krb5_context context
, krb5_cc_cursor cursor
, krb5_ccache
*id
)
486 struct mcache_iter
*iter
= cursor
;
490 if (iter
->cache
== NULL
)
493 HEIMDAL_MUTEX_lock(&mcc_mutex
);
497 HEIMDAL_MUTEX_lock(&(m
->next
->mutex
));
499 HEIMDAL_MUTEX_unlock(&(m
->next
->mutex
));
502 iter
->cache
= m
->next
;
503 HEIMDAL_MUTEX_unlock(&mcc_mutex
);
505 ret
= _krb5_cc_allocate(context
, &krb5_mcc_ops
, id
);
509 (*id
)->data
.data
= m
;
510 (*id
)->data
.length
= sizeof(*m
);
515 static krb5_error_code KRB5_CALLCONV
516 mcc_end_cache_get(krb5_context context
, krb5_cc_cursor cursor
)
518 struct mcache_iter
*iter
= cursor
;
521 mcc_close_internal(iter
->cache
);
527 static krb5_error_code KRB5_CALLCONV
528 mcc_move(krb5_context context
, krb5_ccache from
, krb5_ccache to
)
530 krb5_mcache
*mfrom
= MCACHE(from
), *mto
= MCACHE(to
);
532 krb5_principal principal
;
535 HEIMDAL_MUTEX_lock(&mcc_mutex
);
537 /* drop the from cache from the linked list to avoid lookups */
538 for(n
= &mcc_head
; n
&& *n
; n
= &(*n
)->next
) {
545 HEIMDAL_MUTEX_lock(&(mfrom
->mutex
));
546 HEIMDAL_MUTEX_lock(&(mto
->mutex
));
549 mto
->creds
= mfrom
->creds
;
550 mfrom
->creds
= creds
;
552 principal
= mto
->primary_principal
;
553 mto
->primary_principal
= mfrom
->primary_principal
;
554 mfrom
->primary_principal
= principal
;
556 mto
->mtime
= mfrom
->mtime
= time(NULL
);
558 HEIMDAL_MUTEX_unlock(&(mfrom
->mutex
));
559 HEIMDAL_MUTEX_unlock(&(mto
->mutex
));
560 HEIMDAL_MUTEX_unlock(&mcc_mutex
);
562 krb5_cc_destroy(context
, from
);
566 static krb5_error_code KRB5_CALLCONV
567 mcc_default_name(krb5_context context
, char **str
)
569 *str
= strdup("MEMORY:");
571 return krb5_enomem(context
);
575 static krb5_error_code KRB5_CALLCONV
576 mcc_lastchange(krb5_context context
, krb5_ccache id
, krb5_timestamp
*mtime
)
578 krb5_mcache
*m
= MCACHE(id
);
579 HEIMDAL_MUTEX_lock(&(m
->mutex
));
581 HEIMDAL_MUTEX_unlock(&(m
->mutex
));
585 static krb5_error_code KRB5_CALLCONV
586 mcc_set_kdc_offset(krb5_context context
, krb5_ccache id
, krb5_deltat kdc_offset
)
588 krb5_mcache
*m
= MCACHE(id
);
589 HEIMDAL_MUTEX_lock(&(m
->mutex
));
590 m
->kdc_offset
= kdc_offset
;
591 HEIMDAL_MUTEX_unlock(&(m
->mutex
));
595 static krb5_error_code KRB5_CALLCONV
596 mcc_get_kdc_offset(krb5_context context
, krb5_ccache id
, krb5_deltat
*kdc_offset
)
598 krb5_mcache
*m
= MCACHE(id
);
599 HEIMDAL_MUTEX_lock(&(m
->mutex
));
600 *kdc_offset
= m
->kdc_offset
;
601 HEIMDAL_MUTEX_unlock(&(m
->mutex
));
607 * Variable containing the MEMORY based credential cache implementation.
609 * @ingroup krb5_ccache
612 KRB5_LIB_VARIABLE
const krb5_cc_ops krb5_mcc_ops
= {
613 KRB5_CC_OPS_VERSION_5
,
622 NULL
, /* mcc_retrieve */