s4:torture: Adapt KDC canon test to Heimdal upstream changes
[Samba.git] / source4 / heimdal / lib / krb5 / mcache.c
blob4ccc415a2619c64acffce4a221f35fc67791dfac
1 /*
2 * Copyright (c) 1997-2004 Kungliga Tekniska Högskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
4 * All rights reserved.
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
10 * are met:
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
33 * SUCH DAMAGE.
36 #include "krb5_locl.h"
38 typedef struct krb5_mcache {
39 char *name;
40 unsigned int refcnt;
41 unsigned int anonymous:1;
42 unsigned int dead:1;
43 krb5_principal primary_principal;
44 struct link {
45 krb5_creds cred;
46 struct link *next;
47 } *creds;
48 struct krb5_mcache *next;
49 time_t mtime;
50 krb5_deltat kdc_offset;
51 HEIMDAL_MUTEX mutex;
52 } krb5_mcache;
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,
63 krb5_ccache id,
64 const char **name,
65 const char **col,
66 const char **sub)
68 if (name)
69 *name = MCACHE(id)->name;
70 if (col)
71 *col = NULL;
72 if (sub)
73 *sub = MCACHE(id)->name;
74 return 0;
77 static krb5_error_code
78 mcc_alloc(krb5_context context, const char *name, krb5_mcache **out)
80 krb5_mcache *m, *m_c;
81 size_t counter = 0;
82 int ret = 0;
83 unsigned create_anonymous = 0;
85 *out = NULL;
86 ALLOC(m, 1);
87 if(m == NULL)
88 return krb5_enomem(context);
90 again:
91 if (counter > 3) {
92 free(m->name);
93 free(m);
94 return EAGAIN; /* XXX */
96 if(name == NULL) {
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;
101 } else {
102 m->name = strdup(name);
104 if(ret < 0 || m->name == NULL) {
105 free(m);
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)
113 break;
114 if (m_c) {
115 free(m->name);
116 free(m);
117 if (name && !create_anonymous) {
118 /* We raced with another thread to create this cache */
119 m = m_c;
120 HEIMDAL_MUTEX_lock(&(m->mutex));
121 m->refcnt++;
122 HEIMDAL_MUTEX_unlock(&(m->mutex));
123 } else {
124 /* How likely are we to conflict on new_unique anyways?? */
125 counter++;
126 free(m->name);
127 m->name = NULL;
128 HEIMDAL_MUTEX_unlock(&mcc_mutex);
129 goto again;
131 HEIMDAL_MUTEX_unlock(&mcc_mutex);
132 *out = m;
133 return 0;
136 m->anonymous = create_anonymous;
137 m->dead = 0;
138 m->refcnt = 1;
139 m->primary_principal = NULL;
140 m->creds = NULL;
141 m->mtime = time(NULL);
142 m->kdc_offset = 0;
143 m->next = mcc_head;
144 HEIMDAL_MUTEX_init(&(m->mutex));
145 mcc_head = m;
146 HEIMDAL_MUTEX_unlock(&mcc_mutex);
147 *out = m;
148 return 0;
151 static krb5_error_code KRB5_CALLCONV
152 mcc_resolve_2(krb5_context context,
153 krb5_ccache *id,
154 const char *res,
155 const char *sub)
157 krb5_error_code ret;
158 krb5_mcache *m;
160 if ((ret = mcc_alloc(context, sub && *sub ? sub : res, &m)))
161 return ret;
163 (*id)->data.data = m;
164 (*id)->data.length = sizeof(*m);
166 return 0;
170 static krb5_error_code KRB5_CALLCONV
171 mcc_gen_new(krb5_context context, krb5_ccache *id)
173 krb5_error_code ret;
174 krb5_mcache *m;
176 if ((ret = mcc_alloc(context, NULL, &m)))
177 return ret;
179 (*id)->data.data = m;
180 (*id)->data.length = sizeof(*m);
182 return 0;
185 static void KRB5_CALLCONV
186 mcc_destroy_internal(krb5_context context,
187 krb5_mcache *m)
189 struct link *l;
191 if (m->primary_principal != NULL) {
192 krb5_free_principal (context, m->primary_principal);
193 m->primary_principal = NULL;
195 m->dead = 1;
197 l = m->creds;
198 while (l != NULL) {
199 struct link *old;
201 krb5_free_cred_contents (context, &l->cred);
202 old = l;
203 l = l->next;
204 free (old);
207 m->creds = NULL;
208 return;
211 static krb5_error_code KRB5_CALLCONV
212 mcc_initialize(krb5_context context,
213 krb5_ccache id,
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);
227 m->dead = 0;
228 m->kdc_offset = 0;
229 m->mtime = time(NULL);
230 ret = krb5_copy_principal (context,
231 primary_principal,
232 &m->primary_principal);
233 HEIMDAL_MUTEX_unlock(&(m->mutex));
234 return ret;
237 static int
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));
244 return 0;
246 if (MISDEAD(m)) {
247 free(m->name);
248 HEIMDAL_MUTEX_unlock(&(m->mutex));
249 return 1;
251 HEIMDAL_MUTEX_unlock(&(m->mutex));
252 return 0;
255 static krb5_error_code KRB5_CALLCONV
256 mcc_close(krb5_context context,
257 krb5_ccache id)
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);
265 return 0;
268 static krb5_error_code KRB5_CALLCONV
269 mcc_destroy(krb5_context context,
270 krb5_ccache id)
272 krb5_mcache **n, *m = MCACHE(id);
274 HEIMDAL_MUTEX_lock(&mcc_mutex);
275 HEIMDAL_MUTEX_lock(&(m->mutex));
276 if (m->refcnt == 0)
278 HEIMDAL_MUTEX_unlock(&(m->mutex));
279 HEIMDAL_MUTEX_unlock(&mcc_mutex);
280 krb5_abortx(context, "mcc_destroy: refcnt already 0");
283 if (!MISDEAD(m)) {
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) {
287 if(m == *n) {
288 *n = m->next;
289 break;
292 mcc_destroy_internal(context, m);
294 HEIMDAL_MUTEX_unlock(&(m->mutex));
295 HEIMDAL_MUTEX_unlock(&mcc_mutex);
296 return 0;
299 static krb5_error_code KRB5_CALLCONV
300 mcc_store_cred(krb5_context context,
301 krb5_ccache id,
302 krb5_creds *creds)
304 krb5_mcache *m = MCACHE(id);
305 krb5_error_code ret;
306 struct link *l;
308 HEIMDAL_MUTEX_lock(&(m->mutex));
309 if (MISDEAD(m))
311 HEIMDAL_MUTEX_unlock(&(m->mutex));
312 return ENOENT;
315 l = malloc (sizeof(*l));
316 if (l == NULL)
317 return krb5_enomem(context);
318 l->next = m->creds;
319 m->creds = l;
320 memset (&l->cred, 0, sizeof(l->cred));
321 ret = krb5_copy_creds_contents (context, creds, &l->cred);
322 if (ret) {
323 m->creds = l->next;
324 free (l);
325 HEIMDAL_MUTEX_unlock(&(m->mutex));
326 return ret;
328 m->mtime = time(NULL);
329 HEIMDAL_MUTEX_unlock(&(m->mutex));
330 return 0;
333 static krb5_error_code KRB5_CALLCONV
334 mcc_get_principal(krb5_context context,
335 krb5_ccache id,
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));
344 return ENOENT;
346 ret = krb5_copy_principal (context,
347 m->primary_principal,
348 principal);
349 HEIMDAL_MUTEX_unlock(&(m->mutex));
350 return ret;
353 static krb5_error_code KRB5_CALLCONV
354 mcc_get_first (krb5_context context,
355 krb5_ccache id,
356 krb5_cc_cursor *cursor)
358 krb5_mcache *m = MCACHE(id);
360 HEIMDAL_MUTEX_lock(&(m->mutex));
361 if (MISDEAD(m)) {
362 HEIMDAL_MUTEX_unlock(&(m->mutex));
363 return ENOENT;
365 *cursor = m->creds;
367 HEIMDAL_MUTEX_unlock(&(m->mutex));
368 return 0;
371 static krb5_error_code KRB5_CALLCONV
372 mcc_get_next (krb5_context context,
373 krb5_ccache id,
374 krb5_cc_cursor *cursor,
375 krb5_creds *creds)
377 krb5_mcache *m = MCACHE(id);
378 struct link *l;
380 HEIMDAL_MUTEX_lock(&(m->mutex));
381 if (MISDEAD(m)) {
382 HEIMDAL_MUTEX_unlock(&(m->mutex));
383 return ENOENT;
385 HEIMDAL_MUTEX_unlock(&(m->mutex));
387 l = *cursor;
388 if (l != NULL) {
389 *cursor = l->next;
390 return krb5_copy_creds_contents (context,
391 &l->cred,
392 creds);
393 } else
394 return KRB5_CC_END;
397 static krb5_error_code KRB5_CALLCONV
398 mcc_end_get (krb5_context context,
399 krb5_ccache id,
400 krb5_cc_cursor *cursor)
402 return 0;
405 static krb5_error_code KRB5_CALLCONV
406 mcc_remove_cred(krb5_context context,
407 krb5_ccache id,
408 krb5_flags which,
409 krb5_creds *mcreds)
411 krb5_mcache *m = MCACHE(id);
412 struct link **q, *p;
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)) {
418 *q = p->next;
419 krb5_free_cred_contents(context, &p->cred);
420 free(p);
421 m->mtime = time(NULL);
422 } else
423 q = &p->next;
425 HEIMDAL_MUTEX_unlock(&(m->mutex));
426 return 0;
429 static krb5_error_code KRB5_CALLCONV
430 mcc_set_flags(krb5_context context,
431 krb5_ccache id,
432 krb5_flags flags)
434 return 0; /* XXX */
437 struct mcache_iter {
438 krb5_mcache *cache;
441 static krb5_mcache *
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 */
448 if (next != NULL) {
449 HEIMDAL_MUTEX_lock(&(next->mutex));
450 next->refcnt++;
451 HEIMDAL_MUTEX_unlock(&(next->mutex));
452 next = next->next;
454 HEIMDAL_MUTEX_unlock(&mcc_mutex);
456 return next;
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));
465 if (iter == NULL)
466 return krb5_enomem(context);
468 iter->cache = mcc_get_cache_find_next_internal(mcc_head);
470 *cursor = iter;
471 return 0;
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;
478 krb5_error_code ret;
479 krb5_mcache *m;
481 if (iter->cache == NULL)
482 return KRB5_CC_END;
484 m = iter->cache;
485 iter->cache = mcc_get_cache_find_next_internal(m);
487 ret = _krb5_cc_allocate(context, &krb5_mcc_ops, id);
488 if (ret)
489 return ret;
491 (*id)->data.data = m;
492 (*id)->data.length = sizeof(*m);
494 return 0;
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;
502 if (iter->cache)
503 mcc_close_internal(iter->cache);
504 iter->cache = NULL;
505 free(iter);
506 return 0;
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);
513 struct link *creds;
514 krb5_principal principal;
515 krb5_mcache **n;
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) {
521 if(mfrom == *n) {
522 *n = mfrom->next;
523 break;
527 HEIMDAL_MUTEX_lock(&(mfrom->mutex));
528 HEIMDAL_MUTEX_lock(&(mto->mutex));
529 /* swap creds */
530 creds = mto->creds;
531 mto->creds = mfrom->creds;
532 mfrom->creds = creds;
533 /* swap principal */
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);
545 return 0;
548 static krb5_error_code KRB5_CALLCONV
549 mcc_default_name(krb5_context context, char **str)
551 *str = strdup("MEMORY:");
552 if (*str == NULL)
553 return krb5_enomem(context);
554 return 0;
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));
562 *mtime = m->mtime;
563 HEIMDAL_MUTEX_unlock(&(m->mutex));
564 return 0;
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));
574 return 0;
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));
584 return 0;
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,
596 "MEMORY",
597 NULL,
598 NULL,
599 mcc_gen_new,
600 mcc_initialize,
601 mcc_destroy,
602 mcc_close,
603 mcc_store_cred,
604 NULL, /* mcc_retrieve */
605 mcc_get_principal,
606 mcc_get_first,
607 mcc_get_next,
608 mcc_end_get,
609 mcc_remove_cred,
610 mcc_set_flags,
611 NULL,
612 mcc_get_cache_first,
613 mcc_get_cache_next,
614 mcc_end_cache_get,
615 mcc_move,
616 mcc_default_name,
617 NULL,
618 mcc_lastchange,
619 mcc_set_kdc_offset,
620 mcc_get_kdc_offset,
621 mcc_get_name_2,
622 mcc_resolve_2