heimdal:camellia: include roken.h
[heimdal.git] / lib / krb5 / mcache.c
blob78ef68db3d1cf2af97fbaad2311420e97d270e3b
1 /*
2 * Copyright (c) 1997-2004 Kungliga Tekniska Högskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
4 * All rights reserved.
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
17 * 3. Neither the name of the Institute nor the names of its contributors
18 * may be used to endorse or promote products derived from this software
19 * without specific prior written permission.
21 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
34 #include "krb5_locl.h"
36 typedef struct krb5_mcache {
37 char *name;
38 unsigned int refcnt;
39 int dead;
40 krb5_principal primary_principal;
41 struct link {
42 krb5_creds cred;
43 struct link *next;
44 } *creds;
45 struct krb5_mcache *next;
46 time_t mtime;
47 } krb5_mcache;
49 static HEIMDAL_MUTEX mcc_mutex = HEIMDAL_MUTEX_INITIALIZER;
50 static struct krb5_mcache *mcc_head;
52 #define MCACHE(X) ((krb5_mcache *)(X)->data.data)
54 #define MISDEAD(X) ((X)->dead)
56 static const char*
57 mcc_get_name(krb5_context context,
58 krb5_ccache id)
60 return MCACHE(id)->name;
63 static krb5_mcache *
64 mcc_alloc(const char *name)
66 krb5_mcache *m, *m_c;
68 ALLOC(m, 1);
69 if(m == NULL)
70 return NULL;
71 if(name == NULL)
72 asprintf(&m->name, "%p", m);
73 else
74 m->name = strdup(name);
75 if(m->name == NULL) {
76 free(m);
77 return NULL;
79 /* check for dups first */
80 HEIMDAL_MUTEX_lock(&mcc_mutex);
81 for (m_c = mcc_head; m_c != NULL; m_c = m_c->next)
82 if (strcmp(m->name, m_c->name) == 0)
83 break;
84 if (m_c) {
85 free(m->name);
86 free(m);
87 HEIMDAL_MUTEX_unlock(&mcc_mutex);
88 return NULL;
91 m->dead = 0;
92 m->refcnt = 1;
93 m->primary_principal = NULL;
94 m->creds = NULL;
95 m->mtime = time(NULL);
96 m->next = mcc_head;
97 mcc_head = m;
98 HEIMDAL_MUTEX_unlock(&mcc_mutex);
99 return m;
102 static krb5_error_code
103 mcc_resolve(krb5_context context, krb5_ccache *id, const char *res)
105 krb5_mcache *m;
107 HEIMDAL_MUTEX_lock(&mcc_mutex);
108 for (m = mcc_head; m != NULL; m = m->next)
109 if (strcmp(m->name, res) == 0)
110 break;
111 HEIMDAL_MUTEX_unlock(&mcc_mutex);
113 if (m != NULL) {
114 m->refcnt++;
115 (*id)->data.data = m;
116 (*id)->data.length = sizeof(*m);
117 return 0;
120 m = mcc_alloc(res);
121 if (m == NULL) {
122 krb5_set_error_message(context, KRB5_CC_NOMEM,
123 N_("malloc: out of memory", ""));
124 return KRB5_CC_NOMEM;
127 (*id)->data.data = m;
128 (*id)->data.length = sizeof(*m);
130 return 0;
134 static krb5_error_code
135 mcc_gen_new(krb5_context context, krb5_ccache *id)
137 krb5_mcache *m;
139 m = mcc_alloc(NULL);
141 if (m == NULL) {
142 krb5_set_error_message(context, KRB5_CC_NOMEM,
143 N_("malloc: out of memory", ""));
144 return KRB5_CC_NOMEM;
147 (*id)->data.data = m;
148 (*id)->data.length = sizeof(*m);
150 return 0;
153 static krb5_error_code
154 mcc_initialize(krb5_context context,
155 krb5_ccache id,
156 krb5_principal primary_principal)
158 krb5_mcache *m = MCACHE(id);
159 m->dead = 0;
160 m->mtime = time(NULL);
161 return krb5_copy_principal (context,
162 primary_principal,
163 &m->primary_principal);
166 static int
167 mcc_close_internal(krb5_mcache *m)
169 if (--m->refcnt != 0)
170 return 0;
172 if (MISDEAD(m)) {
173 free (m->name);
174 return 1;
176 return 0;
179 static krb5_error_code
180 mcc_close(krb5_context context,
181 krb5_ccache id)
183 if (mcc_close_internal(MCACHE(id)))
184 krb5_data_free(&id->data);
185 return 0;
188 static krb5_error_code
189 mcc_destroy(krb5_context context,
190 krb5_ccache id)
192 krb5_mcache **n, *m = MCACHE(id);
193 struct link *l;
195 if (m->refcnt == 0)
196 krb5_abortx(context, "mcc_destroy: refcnt already 0");
198 if (!MISDEAD(m)) {
199 /* if this is an active mcache, remove it from the linked
200 list, and free all data */
201 HEIMDAL_MUTEX_lock(&mcc_mutex);
202 for(n = &mcc_head; n && *n; n = &(*n)->next) {
203 if(m == *n) {
204 *n = m->next;
205 break;
208 HEIMDAL_MUTEX_unlock(&mcc_mutex);
209 if (m->primary_principal != NULL) {
210 krb5_free_principal (context, m->primary_principal);
211 m->primary_principal = NULL;
213 m->dead = 1;
215 l = m->creds;
216 while (l != NULL) {
217 struct link *old;
219 krb5_free_cred_contents (context, &l->cred);
220 old = l;
221 l = l->next;
222 free (old);
224 m->creds = NULL;
226 return 0;
229 static krb5_error_code
230 mcc_store_cred(krb5_context context,
231 krb5_ccache id,
232 krb5_creds *creds)
234 krb5_mcache *m = MCACHE(id);
235 krb5_error_code ret;
236 struct link *l;
238 if (MISDEAD(m))
239 return ENOENT;
241 l = malloc (sizeof(*l));
242 if (l == NULL) {
243 krb5_set_error_message(context, KRB5_CC_NOMEM,
244 N_("malloc: out of memory", ""));
245 return KRB5_CC_NOMEM;
247 l->next = m->creds;
248 m->creds = l;
249 memset (&l->cred, 0, sizeof(l->cred));
250 ret = krb5_copy_creds_contents (context, creds, &l->cred);
251 if (ret) {
252 m->creds = l->next;
253 free (l);
254 return ret;
256 m->mtime = time(NULL);
257 return 0;
260 static krb5_error_code
261 mcc_get_principal(krb5_context context,
262 krb5_ccache id,
263 krb5_principal *principal)
265 krb5_mcache *m = MCACHE(id);
267 if (MISDEAD(m) || m->primary_principal == NULL)
268 return ENOENT;
269 return krb5_copy_principal (context,
270 m->primary_principal,
271 principal);
274 static krb5_error_code
275 mcc_get_first (krb5_context context,
276 krb5_ccache id,
277 krb5_cc_cursor *cursor)
279 krb5_mcache *m = MCACHE(id);
281 if (MISDEAD(m))
282 return ENOENT;
284 *cursor = m->creds;
285 return 0;
288 static krb5_error_code
289 mcc_get_next (krb5_context context,
290 krb5_ccache id,
291 krb5_cc_cursor *cursor,
292 krb5_creds *creds)
294 krb5_mcache *m = MCACHE(id);
295 struct link *l;
297 if (MISDEAD(m))
298 return ENOENT;
300 l = *cursor;
301 if (l != NULL) {
302 *cursor = l->next;
303 return krb5_copy_creds_contents (context,
304 &l->cred,
305 creds);
306 } else
307 return KRB5_CC_END;
310 static krb5_error_code
311 mcc_end_get (krb5_context context,
312 krb5_ccache id,
313 krb5_cc_cursor *cursor)
315 return 0;
318 static krb5_error_code
319 mcc_remove_cred(krb5_context context,
320 krb5_ccache id,
321 krb5_flags which,
322 krb5_creds *mcreds)
324 krb5_mcache *m = MCACHE(id);
325 struct link **q, *p;
326 for(q = &m->creds, p = *q; p; p = *q) {
327 if(krb5_compare_creds(context, which, mcreds, &p->cred)) {
328 *q = p->next;
329 krb5_free_cred_contents(context, &p->cred);
330 free(p);
331 m->mtime = time(NULL);
332 } else
333 q = &p->next;
335 return 0;
338 static krb5_error_code
339 mcc_set_flags(krb5_context context,
340 krb5_ccache id,
341 krb5_flags flags)
343 return 0; /* XXX */
346 struct mcache_iter {
347 krb5_mcache *cache;
350 static krb5_error_code
351 mcc_get_cache_first(krb5_context context, krb5_cc_cursor *cursor)
353 struct mcache_iter *iter;
355 iter = calloc(1, sizeof(*iter));
356 if (iter == NULL) {
357 krb5_set_error_message(context, ENOMEM,
358 N_("malloc: out of memory", ""));
359 return ENOMEM;
362 HEIMDAL_MUTEX_lock(&mcc_mutex);
363 iter->cache = mcc_head;
364 if (iter->cache)
365 iter->cache->refcnt++;
366 HEIMDAL_MUTEX_unlock(&mcc_mutex);
368 *cursor = iter;
369 return 0;
372 static krb5_error_code
373 mcc_get_cache_next(krb5_context context, krb5_cc_cursor cursor, krb5_ccache *id)
375 struct mcache_iter *iter = cursor;
376 krb5_error_code ret;
377 krb5_mcache *m;
379 if (iter->cache == NULL)
380 return KRB5_CC_END;
382 HEIMDAL_MUTEX_lock(&mcc_mutex);
383 m = iter->cache;
384 if (m->next)
385 m->next->refcnt++;
386 iter->cache = m->next;
387 HEIMDAL_MUTEX_unlock(&mcc_mutex);
389 ret = _krb5_cc_allocate(context, &krb5_mcc_ops, id);
390 if (ret)
391 return ret;
393 (*id)->data.data = m;
394 (*id)->data.length = sizeof(*m);
396 return 0;
399 static krb5_error_code
400 mcc_end_cache_get(krb5_context context, krb5_cc_cursor cursor)
402 struct mcache_iter *iter = cursor;
404 if (iter->cache)
405 mcc_close_internal(iter->cache);
406 iter->cache = NULL;
407 free(iter);
408 return 0;
411 static krb5_error_code
412 mcc_move(krb5_context context, krb5_ccache from, krb5_ccache to)
414 krb5_mcache *mfrom = MCACHE(from), *mto = MCACHE(to);
415 struct link *creds;
416 krb5_principal principal;
417 krb5_mcache **n;
419 HEIMDAL_MUTEX_lock(&mcc_mutex);
421 /* drop the from cache from the linked list to avoid lookups */
422 for(n = &mcc_head; n && *n; n = &(*n)->next) {
423 if(mfrom == *n) {
424 *n = mfrom->next;
425 break;
429 /* swap creds */
430 creds = mto->creds;
431 mto->creds = mfrom->creds;
432 mfrom->creds = creds;
433 /* swap principal */
434 principal = mto->primary_principal;
435 mto->primary_principal = mfrom->primary_principal;
436 mfrom->primary_principal = principal;
438 mto->mtime = mfrom->mtime = time(NULL);
440 HEIMDAL_MUTEX_unlock(&mcc_mutex);
441 mcc_destroy(context, from);
443 return 0;
446 static krb5_error_code
447 mcc_default_name(krb5_context context, char **str)
449 *str = strdup("MEMORY:");
450 if (*str == NULL) {
451 krb5_set_error_message(context, ENOMEM,
452 N_("malloc: out of memory", ""));
453 return ENOMEM;
455 return 0;
458 static krb5_error_code
459 mcc_lastchange(krb5_context context, krb5_ccache id, krb5_timestamp *mtime)
461 *mtime = MCACHE(id)->mtime;
462 return 0;
467 * Variable containing the MEMORY based credential cache implemention.
469 * @ingroup krb5_ccache
472 KRB5_LIB_VARIABLE const krb5_cc_ops krb5_mcc_ops = {
473 KRB5_CC_OPS_VERSION,
474 "MEMORY",
475 mcc_get_name,
476 mcc_resolve,
477 mcc_gen_new,
478 mcc_initialize,
479 mcc_destroy,
480 mcc_close,
481 mcc_store_cred,
482 NULL, /* mcc_retrieve */
483 mcc_get_principal,
484 mcc_get_first,
485 mcc_get_next,
486 mcc_end_get,
487 mcc_remove_cred,
488 mcc_set_flags,
489 NULL,
490 mcc_get_cache_first,
491 mcc_get_cache_next,
492 mcc_end_cache_get,
493 mcc_move,
494 mcc_default_name,
495 NULL,
496 mcc_lastchange