Use anon realm for anonymous PKINIT
[heimdal.git] / lib / krb5 / mcache.c
blobcd4611c6c83315e4d8f1fb19583d0bed2358acf3
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 int dead;
42 krb5_principal primary_principal;
43 struct link {
44 krb5_creds cred;
45 struct link *next;
46 } *creds;
47 struct krb5_mcache *next;
48 time_t mtime;
49 krb5_deltat kdc_offset;
50 HEIMDAL_MUTEX mutex;
51 } krb5_mcache;
53 static HEIMDAL_MUTEX mcc_mutex = HEIMDAL_MUTEX_INITIALIZER;
54 static struct krb5_mcache *mcc_head;
56 #define MCACHE(X) ((krb5_mcache *)(X)->data.data)
58 #define MISDEAD(X) ((X)->dead)
60 static const char* KRB5_CALLCONV
61 mcc_get_name(krb5_context context,
62 krb5_ccache id)
64 return MCACHE(id)->name;
67 static krb5_mcache * KRB5_CALLCONV
68 mcc_alloc(const char *name)
70 krb5_mcache *m, *m_c;
71 int ret = 0;
73 ALLOC(m, 1);
74 if(m == NULL)
75 return NULL;
76 if(name == NULL)
77 ret = asprintf(&m->name, "%p", m);
78 else
79 m->name = strdup(name);
80 if(ret < 0 || m->name == NULL) {
81 free(m);
82 return NULL;
84 /* check for dups first */
85 HEIMDAL_MUTEX_lock(&mcc_mutex);
86 for (m_c = mcc_head; m_c != NULL; m_c = m_c->next)
87 if (strcmp(m->name, m_c->name) == 0)
88 break;
89 if (m_c) {
90 free(m->name);
91 free(m);
92 HEIMDAL_MUTEX_unlock(&mcc_mutex);
93 return NULL;
96 m->dead = 0;
97 m->refcnt = 1;
98 m->primary_principal = NULL;
99 m->creds = NULL;
100 m->mtime = time(NULL);
101 m->kdc_offset = 0;
102 m->next = mcc_head;
103 HEIMDAL_MUTEX_init(&(m->mutex));
104 mcc_head = m;
105 HEIMDAL_MUTEX_unlock(&mcc_mutex);
106 return m;
109 static krb5_error_code KRB5_CALLCONV
110 mcc_resolve(krb5_context context, krb5_ccache *id, const char *res)
112 krb5_mcache *m;
114 HEIMDAL_MUTEX_lock(&mcc_mutex);
115 for (m = mcc_head; m != NULL; m = m->next)
116 if (strcmp(m->name, res) == 0)
117 break;
118 HEIMDAL_MUTEX_unlock(&mcc_mutex);
120 if (m != NULL) {
121 HEIMDAL_MUTEX_lock(&(m->mutex));
122 m->refcnt++;
123 HEIMDAL_MUTEX_unlock(&(m->mutex));
124 (*id)->data.data = m;
125 (*id)->data.length = sizeof(*m);
126 return 0;
129 m = mcc_alloc(res);
130 if (m == NULL) {
131 krb5_set_error_message(context, KRB5_CC_NOMEM,
132 N_("malloc: out of memory", ""));
133 return KRB5_CC_NOMEM;
136 (*id)->data.data = m;
137 (*id)->data.length = sizeof(*m);
139 return 0;
143 static krb5_error_code KRB5_CALLCONV
144 mcc_gen_new(krb5_context context, krb5_ccache *id)
146 krb5_mcache *m;
148 m = mcc_alloc(NULL);
150 if (m == NULL) {
151 krb5_set_error_message(context, KRB5_CC_NOMEM,
152 N_("malloc: out of memory", ""));
153 return KRB5_CC_NOMEM;
156 (*id)->data.data = m;
157 (*id)->data.length = sizeof(*m);
159 return 0;
162 static krb5_error_code KRB5_CALLCONV
163 mcc_initialize(krb5_context context,
164 krb5_ccache id,
165 krb5_principal primary_principal)
167 krb5_mcache *m = MCACHE(id);
168 krb5_error_code ret = 0;
169 HEIMDAL_MUTEX_lock(&(m->mutex));
170 heim_assert(m->refcnt != 0, "resurection released mcache");
171 m->dead = 0;
172 m->mtime = time(NULL);
173 ret = krb5_copy_principal (context,
174 primary_principal,
175 &m->primary_principal);
176 HEIMDAL_MUTEX_unlock(&(m->mutex));
177 return ret;
180 static int
181 mcc_close_internal(krb5_mcache *m)
183 HEIMDAL_MUTEX_lock(&(m->mutex));
184 heim_assert(m->refcnt != 0, "closed dead cache mcache");
185 if (--m->refcnt != 0) {
186 HEIMDAL_MUTEX_unlock(&(m->mutex));
187 return 0;
189 if (MISDEAD(m)) {
190 free (m->name);
191 HEIMDAL_MUTEX_unlock(&(m->mutex));
192 return 1;
194 HEIMDAL_MUTEX_unlock(&(m->mutex));
195 return 0;
198 static krb5_error_code KRB5_CALLCONV
199 mcc_close(krb5_context context,
200 krb5_ccache id)
202 krb5_mcache *m = MCACHE(id);
204 if (mcc_close_internal(MCACHE(id))) {
205 HEIMDAL_MUTEX_destroy(&(m->mutex));
206 krb5_data_free(&id->data);
208 return 0;
211 static krb5_error_code KRB5_CALLCONV
212 mcc_destroy(krb5_context context,
213 krb5_ccache id)
215 krb5_mcache **n, *m = MCACHE(id);
216 struct link *l;
218 HEIMDAL_MUTEX_lock(&(m->mutex));
219 if (m->refcnt == 0)
221 HEIMDAL_MUTEX_unlock(&(m->mutex));
222 krb5_abortx(context, "mcc_destroy: refcnt already 0");
225 if (!MISDEAD(m)) {
226 /* if this is an active mcache, remove it from the linked
227 list, and free all data */
228 HEIMDAL_MUTEX_lock(&mcc_mutex);
229 for(n = &mcc_head; n && *n; n = &(*n)->next) {
230 if(m == *n) {
231 *n = m->next;
232 break;
235 HEIMDAL_MUTEX_unlock(&mcc_mutex);
236 if (m->primary_principal != NULL) {
237 krb5_free_principal (context, m->primary_principal);
238 m->primary_principal = NULL;
240 m->dead = 1;
242 l = m->creds;
243 while (l != NULL) {
244 struct link *old;
246 krb5_free_cred_contents (context, &l->cred);
247 old = l;
248 l = l->next;
249 free (old);
251 m->creds = NULL;
253 HEIMDAL_MUTEX_unlock(&(m->mutex));
254 return 0;
257 static krb5_error_code KRB5_CALLCONV
258 mcc_store_cred(krb5_context context,
259 krb5_ccache id,
260 krb5_creds *creds)
262 krb5_mcache *m = MCACHE(id);
263 krb5_error_code ret;
264 struct link *l;
266 HEIMDAL_MUTEX_lock(&(m->mutex));
267 if (MISDEAD(m))
269 HEIMDAL_MUTEX_unlock(&(m->mutex));
270 return ENOENT;
273 l = malloc (sizeof(*l));
274 if (l == NULL) {
275 krb5_set_error_message(context, KRB5_CC_NOMEM,
276 N_("malloc: out of memory", ""));
277 HEIMDAL_MUTEX_unlock(&(m->mutex));
278 return KRB5_CC_NOMEM;
280 l->next = m->creds;
281 m->creds = l;
282 memset (&l->cred, 0, sizeof(l->cred));
283 ret = krb5_copy_creds_contents (context, creds, &l->cred);
284 if (ret) {
285 m->creds = l->next;
286 free (l);
287 HEIMDAL_MUTEX_unlock(&(m->mutex));
288 return ret;
290 m->mtime = time(NULL);
291 HEIMDAL_MUTEX_unlock(&(m->mutex));
292 return 0;
295 static krb5_error_code KRB5_CALLCONV
296 mcc_get_principal(krb5_context context,
297 krb5_ccache id,
298 krb5_principal *principal)
300 krb5_mcache *m = MCACHE(id);
301 krb5_error_code ret = 0;
303 HEIMDAL_MUTEX_lock(&(m->mutex));
304 if (MISDEAD(m) || m->primary_principal == NULL) {
305 HEIMDAL_MUTEX_unlock(&(m->mutex));
306 return ENOENT;
308 ret = krb5_copy_principal (context,
309 m->primary_principal,
310 principal);
311 HEIMDAL_MUTEX_unlock(&(m->mutex));
312 return ret;
315 static krb5_error_code KRB5_CALLCONV
316 mcc_get_first (krb5_context context,
317 krb5_ccache id,
318 krb5_cc_cursor *cursor)
320 krb5_mcache *m = MCACHE(id);
322 HEIMDAL_MUTEX_lock(&(m->mutex));
323 if (MISDEAD(m)) {
324 HEIMDAL_MUTEX_unlock(&(m->mutex));
325 return ENOENT;
327 *cursor = m->creds;
329 HEIMDAL_MUTEX_unlock(&(m->mutex));
330 return 0;
333 static krb5_error_code KRB5_CALLCONV
334 mcc_get_next (krb5_context context,
335 krb5_ccache id,
336 krb5_cc_cursor *cursor,
337 krb5_creds *creds)
339 krb5_mcache *m = MCACHE(id);
340 struct link *l;
342 HEIMDAL_MUTEX_lock(&(m->mutex));
343 if (MISDEAD(m)) {
344 HEIMDAL_MUTEX_unlock(&(m->mutex));
345 return ENOENT;
347 HEIMDAL_MUTEX_unlock(&(m->mutex));
349 l = *cursor;
350 if (l != NULL) {
351 *cursor = l->next;
352 return krb5_copy_creds_contents (context,
353 &l->cred,
354 creds);
355 } else
356 return KRB5_CC_END;
359 static krb5_error_code KRB5_CALLCONV
360 mcc_end_get (krb5_context context,
361 krb5_ccache id,
362 krb5_cc_cursor *cursor)
364 return 0;
367 static krb5_error_code KRB5_CALLCONV
368 mcc_remove_cred(krb5_context context,
369 krb5_ccache id,
370 krb5_flags which,
371 krb5_creds *mcreds)
373 krb5_mcache *m = MCACHE(id);
374 struct link **q, *p;
376 HEIMDAL_MUTEX_lock(&(m->mutex));
378 for(q = &m->creds, p = *q; p; p = *q) {
379 if(krb5_compare_creds(context, which, mcreds, &p->cred)) {
380 *q = p->next;
381 krb5_free_cred_contents(context, &p->cred);
382 free(p);
383 m->mtime = time(NULL);
384 } else
385 q = &p->next;
387 HEIMDAL_MUTEX_unlock(&(m->mutex));
388 return 0;
391 static krb5_error_code KRB5_CALLCONV
392 mcc_set_flags(krb5_context context,
393 krb5_ccache id,
394 krb5_flags flags)
396 return 0; /* XXX */
399 struct mcache_iter {
400 krb5_mcache *cache;
403 static krb5_error_code KRB5_CALLCONV
404 mcc_get_cache_first(krb5_context context, krb5_cc_cursor *cursor)
406 struct mcache_iter *iter;
408 iter = calloc(1, sizeof(*iter));
409 if (iter == NULL)
410 return krb5_enomem(context);
412 HEIMDAL_MUTEX_lock(&mcc_mutex);
413 iter->cache = mcc_head;
414 if (iter->cache) {
415 HEIMDAL_MUTEX_lock(&(iter->cache->mutex));
416 iter->cache->refcnt++;
417 HEIMDAL_MUTEX_unlock(&(iter->cache->mutex));
419 HEIMDAL_MUTEX_unlock(&mcc_mutex);
421 *cursor = iter;
422 return 0;
425 static krb5_error_code KRB5_CALLCONV
426 mcc_get_cache_next(krb5_context context, krb5_cc_cursor cursor, krb5_ccache *id)
428 struct mcache_iter *iter = cursor;
429 krb5_error_code ret;
430 krb5_mcache *m;
432 if (iter->cache == NULL)
433 return KRB5_CC_END;
435 HEIMDAL_MUTEX_lock(&mcc_mutex);
436 m = iter->cache;
437 if (m->next)
439 HEIMDAL_MUTEX_lock(&(m->next->mutex));
440 m->next->refcnt++;
441 HEIMDAL_MUTEX_unlock(&(m->next->mutex));
444 iter->cache = m->next;
445 HEIMDAL_MUTEX_unlock(&mcc_mutex);
447 ret = _krb5_cc_allocate(context, &krb5_mcc_ops, id);
448 if (ret)
449 return ret;
451 (*id)->data.data = m;
452 (*id)->data.length = sizeof(*m);
454 return 0;
457 static krb5_error_code KRB5_CALLCONV
458 mcc_end_cache_get(krb5_context context, krb5_cc_cursor cursor)
460 struct mcache_iter *iter = cursor;
462 if (iter->cache)
463 mcc_close_internal(iter->cache);
464 iter->cache = NULL;
465 free(iter);
466 return 0;
469 static krb5_error_code KRB5_CALLCONV
470 mcc_move(krb5_context context, krb5_ccache from, krb5_ccache to)
472 krb5_mcache *mfrom = MCACHE(from), *mto = MCACHE(to);
473 struct link *creds;
474 krb5_principal principal;
475 krb5_mcache **n;
477 HEIMDAL_MUTEX_lock(&mcc_mutex);
479 /* drop the from cache from the linked list to avoid lookups */
480 for(n = &mcc_head; n && *n; n = &(*n)->next) {
481 if(mfrom == *n) {
482 *n = mfrom->next;
483 break;
487 HEIMDAL_MUTEX_lock(&(mfrom->mutex));
488 HEIMDAL_MUTEX_lock(&(mto->mutex));
489 /* swap creds */
490 creds = mto->creds;
491 mto->creds = mfrom->creds;
492 mfrom->creds = creds;
493 /* swap principal */
494 principal = mto->primary_principal;
495 mto->primary_principal = mfrom->primary_principal;
496 mfrom->primary_principal = principal;
498 mto->mtime = mfrom->mtime = time(NULL);
500 HEIMDAL_MUTEX_unlock(&(mfrom->mutex));
501 HEIMDAL_MUTEX_unlock(&(mto->mutex));
502 HEIMDAL_MUTEX_unlock(&mcc_mutex);
503 mcc_destroy(context, from);
505 return 0;
508 static krb5_error_code KRB5_CALLCONV
509 mcc_default_name(krb5_context context, char **str)
511 *str = strdup("MEMORY:");
512 if (*str == NULL)
513 return krb5_enomem(context);
514 return 0;
517 static krb5_error_code KRB5_CALLCONV
518 mcc_lastchange(krb5_context context, krb5_ccache id, krb5_timestamp *mtime)
520 krb5_mcache *m = MCACHE(id);
521 HEIMDAL_MUTEX_lock(&(m->mutex));
522 *mtime = m->mtime;
523 HEIMDAL_MUTEX_unlock(&(m->mutex));
524 return 0;
527 static krb5_error_code KRB5_CALLCONV
528 mcc_set_kdc_offset(krb5_context context, krb5_ccache id, krb5_deltat kdc_offset)
530 krb5_mcache *m = MCACHE(id);
531 HEIMDAL_MUTEX_lock(&(m->mutex));
532 m->kdc_offset = kdc_offset;
533 HEIMDAL_MUTEX_unlock(&(m->mutex));
534 return 0;
537 static krb5_error_code KRB5_CALLCONV
538 mcc_get_kdc_offset(krb5_context context, krb5_ccache id, krb5_deltat *kdc_offset)
540 krb5_mcache *m = MCACHE(id);
541 HEIMDAL_MUTEX_lock(&(m->mutex));
542 *kdc_offset = m->kdc_offset;
543 HEIMDAL_MUTEX_unlock(&(m->mutex));
544 return 0;
549 * Variable containing the MEMORY based credential cache implemention.
551 * @ingroup krb5_ccache
554 KRB5_LIB_VARIABLE const krb5_cc_ops krb5_mcc_ops = {
555 KRB5_CC_OPS_VERSION,
556 "MEMORY",
557 mcc_get_name,
558 mcc_resolve,
559 mcc_gen_new,
560 mcc_initialize,
561 mcc_destroy,
562 mcc_close,
563 mcc_store_cred,
564 NULL, /* mcc_retrieve */
565 mcc_get_principal,
566 mcc_get_first,
567 mcc_get_next,
568 mcc_end_get,
569 mcc_remove_cred,
570 mcc_set_flags,
571 NULL,
572 mcc_get_cache_first,
573 mcc_get_cache_next,
574 mcc_end_cache_get,
575 mcc_move,
576 mcc_default_name,
577 NULL,
578 mcc_lastchange,
579 mcc_set_kdc_offset,
580 mcc_get_kdc_offset