etc/services - sync with NetBSD-8
[minix.git] / crypto / external / bsd / heimdal / dist / lib / krb5 / mcache.c
bloba3966a80d49ff0ddeee076e3e9ab3368abc468e3
1 /* $NetBSD: mcache.c,v 1.1.1.2 2014/04/24 12:45:50 pettai Exp $ */
3 /*
4 * Copyright (c) 1997-2004 Kungliga Tekniska Högskolan
5 * (Royal Institute of Technology, Stockholm, Sweden).
6 * All rights reserved.
8 * Portions Copyright (c) 2009 Apple Inc. All rights reserved.
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
17 * 2. Redistributions in binary form must reproduce the above copyright
18 * notice, this list of conditions and the following disclaimer in the
19 * documentation and/or other materials provided with the distribution.
21 * 3. Neither the name of the Institute nor the names of its contributors
22 * may be used to endorse or promote products derived from this software
23 * without specific prior written permission.
25 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
26 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
28 * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
29 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
30 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
31 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
32 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
33 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
34 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
35 * SUCH DAMAGE.
38 #include "krb5_locl.h"
40 typedef struct krb5_mcache {
41 char *name;
42 unsigned int refcnt;
43 int dead;
44 krb5_principal primary_principal;
45 struct link {
46 krb5_creds cred;
47 struct link *next;
48 } *creds;
49 struct krb5_mcache *next;
50 time_t mtime;
51 krb5_deltat kdc_offset;
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 const char* KRB5_CALLCONV
62 mcc_get_name(krb5_context context,
63 krb5_ccache id)
65 return MCACHE(id)->name;
68 static krb5_mcache * KRB5_CALLCONV
69 mcc_alloc(const char *name)
71 krb5_mcache *m, *m_c;
72 int ret = 0;
74 ALLOC(m, 1);
75 if(m == NULL)
76 return NULL;
77 if(name == NULL)
78 ret = asprintf(&m->name, "%p", m);
79 else
80 m->name = strdup(name);
81 if(ret < 0 || m->name == NULL) {
82 free(m);
83 return NULL;
85 /* check for dups first */
86 HEIMDAL_MUTEX_lock(&mcc_mutex);
87 for (m_c = mcc_head; m_c != NULL; m_c = m_c->next)
88 if (strcmp(m->name, m_c->name) == 0)
89 break;
90 if (m_c) {
91 free(m->name);
92 free(m);
93 HEIMDAL_MUTEX_unlock(&mcc_mutex);
94 return NULL;
97 m->dead = 0;
98 m->refcnt = 1;
99 m->primary_principal = NULL;
100 m->creds = NULL;
101 m->mtime = time(NULL);
102 m->kdc_offset = 0;
103 m->next = mcc_head;
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 m->refcnt++;
122 (*id)->data.data = m;
123 (*id)->data.length = sizeof(*m);
124 return 0;
127 m = mcc_alloc(res);
128 if (m == NULL) {
129 krb5_set_error_message(context, KRB5_CC_NOMEM,
130 N_("malloc: out of memory", ""));
131 return KRB5_CC_NOMEM;
134 (*id)->data.data = m;
135 (*id)->data.length = sizeof(*m);
137 return 0;
141 static krb5_error_code KRB5_CALLCONV
142 mcc_gen_new(krb5_context context, krb5_ccache *id)
144 krb5_mcache *m;
146 m = mcc_alloc(NULL);
148 if (m == NULL) {
149 krb5_set_error_message(context, KRB5_CC_NOMEM,
150 N_("malloc: out of memory", ""));
151 return KRB5_CC_NOMEM;
154 (*id)->data.data = m;
155 (*id)->data.length = sizeof(*m);
157 return 0;
160 static krb5_error_code KRB5_CALLCONV
161 mcc_initialize(krb5_context context,
162 krb5_ccache id,
163 krb5_principal primary_principal)
165 krb5_mcache *m = MCACHE(id);
166 m->dead = 0;
167 m->mtime = time(NULL);
168 return krb5_copy_principal (context,
169 primary_principal,
170 &m->primary_principal);
173 static int
174 mcc_close_internal(krb5_mcache *m)
176 if (--m->refcnt != 0)
177 return 0;
179 if (MISDEAD(m)) {
180 free (m->name);
181 return 1;
183 return 0;
186 static krb5_error_code KRB5_CALLCONV
187 mcc_close(krb5_context context,
188 krb5_ccache id)
190 if (mcc_close_internal(MCACHE(id)))
191 krb5_data_free(&id->data);
192 return 0;
195 static krb5_error_code KRB5_CALLCONV
196 mcc_destroy(krb5_context context,
197 krb5_ccache id)
199 krb5_mcache **n, *m = MCACHE(id);
200 struct link *l;
202 if (m->refcnt == 0)
203 krb5_abortx(context, "mcc_destroy: refcnt already 0");
205 if (!MISDEAD(m)) {
206 /* if this is an active mcache, remove it from the linked
207 list, and free all data */
208 HEIMDAL_MUTEX_lock(&mcc_mutex);
209 for(n = &mcc_head; n && *n; n = &(*n)->next) {
210 if(m == *n) {
211 *n = m->next;
212 break;
215 HEIMDAL_MUTEX_unlock(&mcc_mutex);
216 if (m->primary_principal != NULL) {
217 krb5_free_principal (context, m->primary_principal);
218 m->primary_principal = NULL;
220 m->dead = 1;
222 l = m->creds;
223 while (l != NULL) {
224 struct link *old;
226 krb5_free_cred_contents (context, &l->cred);
227 old = l;
228 l = l->next;
229 free (old);
231 m->creds = NULL;
233 return 0;
236 static krb5_error_code KRB5_CALLCONV
237 mcc_store_cred(krb5_context context,
238 krb5_ccache id,
239 krb5_creds *creds)
241 krb5_mcache *m = MCACHE(id);
242 krb5_error_code ret;
243 struct link *l;
245 if (MISDEAD(m))
246 return ENOENT;
248 l = malloc (sizeof(*l));
249 if (l == NULL) {
250 krb5_set_error_message(context, KRB5_CC_NOMEM,
251 N_("malloc: out of memory", ""));
252 return KRB5_CC_NOMEM;
254 l->next = m->creds;
255 m->creds = l;
256 memset (&l->cred, 0, sizeof(l->cred));
257 ret = krb5_copy_creds_contents (context, creds, &l->cred);
258 if (ret) {
259 m->creds = l->next;
260 free (l);
261 return ret;
263 m->mtime = time(NULL);
264 return 0;
267 static krb5_error_code KRB5_CALLCONV
268 mcc_get_principal(krb5_context context,
269 krb5_ccache id,
270 krb5_principal *principal)
272 krb5_mcache *m = MCACHE(id);
274 if (MISDEAD(m) || m->primary_principal == NULL)
275 return ENOENT;
276 return krb5_copy_principal (context,
277 m->primary_principal,
278 principal);
281 static krb5_error_code KRB5_CALLCONV
282 mcc_get_first (krb5_context context,
283 krb5_ccache id,
284 krb5_cc_cursor *cursor)
286 krb5_mcache *m = MCACHE(id);
288 if (MISDEAD(m))
289 return ENOENT;
291 *cursor = m->creds;
292 return 0;
295 static krb5_error_code KRB5_CALLCONV
296 mcc_get_next (krb5_context context,
297 krb5_ccache id,
298 krb5_cc_cursor *cursor,
299 krb5_creds *creds)
301 krb5_mcache *m = MCACHE(id);
302 struct link *l;
304 if (MISDEAD(m))
305 return ENOENT;
307 l = *cursor;
308 if (l != NULL) {
309 *cursor = l->next;
310 return krb5_copy_creds_contents (context,
311 &l->cred,
312 creds);
313 } else
314 return KRB5_CC_END;
317 static krb5_error_code KRB5_CALLCONV
318 mcc_end_get (krb5_context context,
319 krb5_ccache id,
320 krb5_cc_cursor *cursor)
322 return 0;
325 static krb5_error_code KRB5_CALLCONV
326 mcc_remove_cred(krb5_context context,
327 krb5_ccache id,
328 krb5_flags which,
329 krb5_creds *mcreds)
331 krb5_mcache *m = MCACHE(id);
332 struct link **q, *p;
333 for(q = &m->creds, p = *q; p; p = *q) {
334 if(krb5_compare_creds(context, which, mcreds, &p->cred)) {
335 *q = p->next;
336 krb5_free_cred_contents(context, &p->cred);
337 free(p);
338 m->mtime = time(NULL);
339 } else
340 q = &p->next;
342 return 0;
345 static krb5_error_code KRB5_CALLCONV
346 mcc_set_flags(krb5_context context,
347 krb5_ccache id,
348 krb5_flags flags)
350 return 0; /* XXX */
353 struct mcache_iter {
354 krb5_mcache *cache;
357 static krb5_error_code KRB5_CALLCONV
358 mcc_get_cache_first(krb5_context context, krb5_cc_cursor *cursor)
360 struct mcache_iter *iter;
362 iter = calloc(1, sizeof(*iter));
363 if (iter == NULL) {
364 krb5_set_error_message(context, ENOMEM,
365 N_("malloc: out of memory", ""));
366 return ENOMEM;
369 HEIMDAL_MUTEX_lock(&mcc_mutex);
370 iter->cache = mcc_head;
371 if (iter->cache)
372 iter->cache->refcnt++;
373 HEIMDAL_MUTEX_unlock(&mcc_mutex);
375 *cursor = iter;
376 return 0;
379 static krb5_error_code KRB5_CALLCONV
380 mcc_get_cache_next(krb5_context context, krb5_cc_cursor cursor, krb5_ccache *id)
382 struct mcache_iter *iter = cursor;
383 krb5_error_code ret;
384 krb5_mcache *m;
386 if (iter->cache == NULL)
387 return KRB5_CC_END;
389 HEIMDAL_MUTEX_lock(&mcc_mutex);
390 m = iter->cache;
391 if (m->next)
392 m->next->refcnt++;
393 iter->cache = m->next;
394 HEIMDAL_MUTEX_unlock(&mcc_mutex);
396 ret = _krb5_cc_allocate(context, &krb5_mcc_ops, id);
397 if (ret)
398 return ret;
400 (*id)->data.data = m;
401 (*id)->data.length = sizeof(*m);
403 return 0;
406 static krb5_error_code KRB5_CALLCONV
407 mcc_end_cache_get(krb5_context context, krb5_cc_cursor cursor)
409 struct mcache_iter *iter = cursor;
411 if (iter->cache)
412 mcc_close_internal(iter->cache);
413 iter->cache = NULL;
414 free(iter);
415 return 0;
418 static krb5_error_code KRB5_CALLCONV
419 mcc_move(krb5_context context, krb5_ccache from, krb5_ccache to)
421 krb5_mcache *mfrom = MCACHE(from), *mto = MCACHE(to);
422 struct link *creds;
423 krb5_principal principal;
424 krb5_mcache **n;
426 HEIMDAL_MUTEX_lock(&mcc_mutex);
428 /* drop the from cache from the linked list to avoid lookups */
429 for(n = &mcc_head; n && *n; n = &(*n)->next) {
430 if(mfrom == *n) {
431 *n = mfrom->next;
432 break;
436 /* swap creds */
437 creds = mto->creds;
438 mto->creds = mfrom->creds;
439 mfrom->creds = creds;
440 /* swap principal */
441 principal = mto->primary_principal;
442 mto->primary_principal = mfrom->primary_principal;
443 mfrom->primary_principal = principal;
445 mto->mtime = mfrom->mtime = time(NULL);
447 HEIMDAL_MUTEX_unlock(&mcc_mutex);
448 mcc_destroy(context, from);
450 return 0;
453 static krb5_error_code KRB5_CALLCONV
454 mcc_default_name(krb5_context context, char **str)
456 *str = strdup("MEMORY:");
457 if (*str == NULL) {
458 krb5_set_error_message(context, ENOMEM,
459 N_("malloc: out of memory", ""));
460 return ENOMEM;
462 return 0;
465 static krb5_error_code KRB5_CALLCONV
466 mcc_lastchange(krb5_context context, krb5_ccache id, krb5_timestamp *mtime)
468 *mtime = MCACHE(id)->mtime;
469 return 0;
472 static krb5_error_code KRB5_CALLCONV
473 mcc_set_kdc_offset(krb5_context context, krb5_ccache id, krb5_deltat kdc_offset)
475 krb5_mcache *m = MCACHE(id);
476 m->kdc_offset = kdc_offset;
477 return 0;
480 static krb5_error_code KRB5_CALLCONV
481 mcc_get_kdc_offset(krb5_context context, krb5_ccache id, krb5_deltat *kdc_offset)
483 krb5_mcache *m = MCACHE(id);
484 *kdc_offset = m->kdc_offset;
485 return 0;
490 * Variable containing the MEMORY based credential cache implemention.
492 * @ingroup krb5_ccache
495 KRB5_LIB_VARIABLE const krb5_cc_ops krb5_mcc_ops = {
496 KRB5_CC_OPS_VERSION,
497 "MEMORY",
498 mcc_get_name,
499 mcc_resolve,
500 mcc_gen_new,
501 mcc_initialize,
502 mcc_destroy,
503 mcc_close,
504 mcc_store_cred,
505 NULL, /* mcc_retrieve */
506 mcc_get_principal,
507 mcc_get_first,
508 mcc_get_next,
509 mcc_end_get,
510 mcc_remove_cred,
511 mcc_set_flags,
512 NULL,
513 mcc_get_cache_first,
514 mcc_get_cache_next,
515 mcc_end_cache_get,
516 mcc_move,
517 mcc_default_name,
518 NULL,
519 mcc_lastchange,
520 mcc_set_kdc_offset,
521 mcc_get_kdc_offset