mcache.c revision 178826
1/*
2 * Copyright (c) 1997-2004 Kungliga Tekniska H�gskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 *
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.
16 *
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.
20 *
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.
32 */
33
34#include "krb5_locl.h"
35
36RCSID("$Id: mcache.c 22107 2007-12-03 17:22:51Z lha $");
37
38typedef 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} krb5_mcache;
49
50static HEIMDAL_MUTEX mcc_mutex = HEIMDAL_MUTEX_INITIALIZER;
51static struct krb5_mcache *mcc_head;
52
53#define	MCACHE(X)	((krb5_mcache *)(X)->data.data)
54
55#define MISDEAD(X)	((X)->dead)
56
57static const char*
58mcc_get_name(krb5_context context,
59	     krb5_ccache id)
60{
61    return MCACHE(id)->name;
62}
63
64static krb5_mcache *
65mcc_alloc(const char *name)
66{
67    krb5_mcache *m, *m_c;
68
69    ALLOC(m, 1);
70    if(m == NULL)
71	return NULL;
72    if(name == NULL)
73	asprintf(&m->name, "%p", m);
74    else
75	m->name = strdup(name);
76    if(m->name == NULL) {
77	free(m);
78	return NULL;
79    }
80    /* check for dups first */
81    HEIMDAL_MUTEX_lock(&mcc_mutex);
82    for (m_c = mcc_head; m_c != NULL; m_c = m_c->next)
83	if (strcmp(m->name, m_c->name) == 0)
84	    break;
85    if (m_c) {
86	free(m->name);
87	free(m);
88	HEIMDAL_MUTEX_unlock(&mcc_mutex);
89	return NULL;
90    }
91
92    m->dead = 0;
93    m->refcnt = 1;
94    m->primary_principal = NULL;
95    m->creds = NULL;
96    m->next = mcc_head;
97    mcc_head = m;
98    HEIMDAL_MUTEX_unlock(&mcc_mutex);
99    return m;
100}
101
102static krb5_error_code
103mcc_resolve(krb5_context context, krb5_ccache *id, const char *res)
104{
105    krb5_mcache *m;
106
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);
112
113    if (m != NULL) {
114	m->refcnt++;
115	(*id)->data.data = m;
116	(*id)->data.length = sizeof(*m);
117	return 0;
118    }
119
120    m = mcc_alloc(res);
121    if (m == NULL) {
122	krb5_set_error_string (context, "malloc: out of memory");
123	return KRB5_CC_NOMEM;
124    }
125
126    (*id)->data.data = m;
127    (*id)->data.length = sizeof(*m);
128
129    return 0;
130}
131
132
133static krb5_error_code
134mcc_gen_new(krb5_context context, krb5_ccache *id)
135{
136    krb5_mcache *m;
137
138    m = mcc_alloc(NULL);
139
140    if (m == NULL) {
141	krb5_set_error_string (context, "malloc: out of memory");
142	return KRB5_CC_NOMEM;
143    }
144
145    (*id)->data.data = m;
146    (*id)->data.length = sizeof(*m);
147
148    return 0;
149}
150
151static krb5_error_code
152mcc_initialize(krb5_context context,
153	       krb5_ccache id,
154	       krb5_principal primary_principal)
155{
156    krb5_mcache *m = MCACHE(id);
157    m->dead = 0;
158    return krb5_copy_principal (context,
159				primary_principal,
160				&m->primary_principal);
161}
162
163static int
164mcc_close_internal(krb5_mcache *m)
165{
166    if (--m->refcnt != 0)
167	return 0;
168
169    if (MISDEAD(m)) {
170	free (m->name);
171	return 1;
172    }
173    return 0;
174}
175
176static krb5_error_code
177mcc_close(krb5_context context,
178	  krb5_ccache id)
179{
180    if (mcc_close_internal(MCACHE(id)))
181	krb5_data_free(&id->data);
182    return 0;
183}
184
185static krb5_error_code
186mcc_destroy(krb5_context context,
187	    krb5_ccache id)
188{
189    krb5_mcache **n, *m = MCACHE(id);
190    struct link *l;
191
192    if (m->refcnt == 0)
193	krb5_abortx(context, "mcc_destroy: refcnt already 0");
194
195    if (!MISDEAD(m)) {
196	/* if this is an active mcache, remove it from the linked
197           list, and free all data */
198	HEIMDAL_MUTEX_lock(&mcc_mutex);
199	for(n = &mcc_head; n && *n; n = &(*n)->next) {
200	    if(m == *n) {
201		*n = m->next;
202		break;
203	    }
204	}
205	HEIMDAL_MUTEX_unlock(&mcc_mutex);
206	if (m->primary_principal != NULL) {
207	    krb5_free_principal (context, m->primary_principal);
208	    m->primary_principal = NULL;
209	}
210	m->dead = 1;
211
212	l = m->creds;
213	while (l != NULL) {
214	    struct link *old;
215
216	    krb5_free_cred_contents (context, &l->cred);
217	    old = l;
218	    l = l->next;
219	    free (old);
220	}
221	m->creds = NULL;
222    }
223    return 0;
224}
225
226static krb5_error_code
227mcc_store_cred(krb5_context context,
228	       krb5_ccache id,
229	       krb5_creds *creds)
230{
231    krb5_mcache *m = MCACHE(id);
232    krb5_error_code ret;
233    struct link *l;
234
235    if (MISDEAD(m))
236	return ENOENT;
237
238    l = malloc (sizeof(*l));
239    if (l == NULL) {
240	krb5_set_error_string (context, "malloc: out of memory");
241	return KRB5_CC_NOMEM;
242    }
243    l->next = m->creds;
244    m->creds = l;
245    memset (&l->cred, 0, sizeof(l->cred));
246    ret = krb5_copy_creds_contents (context, creds, &l->cred);
247    if (ret) {
248	m->creds = l->next;
249	free (l);
250	return ret;
251    }
252    return 0;
253}
254
255static krb5_error_code
256mcc_get_principal(krb5_context context,
257		  krb5_ccache id,
258		  krb5_principal *principal)
259{
260    krb5_mcache *m = MCACHE(id);
261
262    if (MISDEAD(m) || m->primary_principal == NULL)
263	return ENOENT;
264    return krb5_copy_principal (context,
265				m->primary_principal,
266				principal);
267}
268
269static krb5_error_code
270mcc_get_first (krb5_context context,
271	       krb5_ccache id,
272	       krb5_cc_cursor *cursor)
273{
274    krb5_mcache *m = MCACHE(id);
275
276    if (MISDEAD(m))
277	return ENOENT;
278
279    *cursor = m->creds;
280    return 0;
281}
282
283static krb5_error_code
284mcc_get_next (krb5_context context,
285	      krb5_ccache id,
286	      krb5_cc_cursor *cursor,
287	      krb5_creds *creds)
288{
289    krb5_mcache *m = MCACHE(id);
290    struct link *l;
291
292    if (MISDEAD(m))
293	return ENOENT;
294
295    l = *cursor;
296    if (l != NULL) {
297	*cursor = l->next;
298	return krb5_copy_creds_contents (context,
299					 &l->cred,
300					 creds);
301    } else
302	return KRB5_CC_END;
303}
304
305static krb5_error_code
306mcc_end_get (krb5_context context,
307	     krb5_ccache id,
308	     krb5_cc_cursor *cursor)
309{
310    return 0;
311}
312
313static krb5_error_code
314mcc_remove_cred(krb5_context context,
315		 krb5_ccache id,
316		 krb5_flags which,
317		 krb5_creds *mcreds)
318{
319    krb5_mcache *m = MCACHE(id);
320    struct link **q, *p;
321    for(q = &m->creds, p = *q; p; p = *q) {
322	if(krb5_compare_creds(context, which, mcreds, &p->cred)) {
323	    *q = p->next;
324	    krb5_free_cred_contents(context, &p->cred);
325	    free(p);
326	} else
327	    q = &p->next;
328    }
329    return 0;
330}
331
332static krb5_error_code
333mcc_set_flags(krb5_context context,
334	      krb5_ccache id,
335	      krb5_flags flags)
336{
337    return 0; /* XXX */
338}
339
340struct mcache_iter {
341    krb5_mcache *cache;
342};
343
344static krb5_error_code
345mcc_get_cache_first(krb5_context context, krb5_cc_cursor *cursor)
346{
347    struct mcache_iter *iter;
348
349    iter = calloc(1, sizeof(*iter));
350    if (iter == NULL) {
351	krb5_set_error_string(context, "malloc - out of memory");
352	return ENOMEM;
353    }
354
355    HEIMDAL_MUTEX_lock(&mcc_mutex);
356    iter->cache = mcc_head;
357    if (iter->cache)
358	iter->cache->refcnt++;
359    HEIMDAL_MUTEX_unlock(&mcc_mutex);
360
361    *cursor = iter;
362    return 0;
363}
364
365static krb5_error_code
366mcc_get_cache_next(krb5_context context, krb5_cc_cursor cursor, krb5_ccache *id)
367{
368    struct mcache_iter *iter = cursor;
369    krb5_error_code ret;
370    krb5_mcache *m;
371
372    if (iter->cache == NULL)
373	return KRB5_CC_END;
374
375    HEIMDAL_MUTEX_lock(&mcc_mutex);
376    m = iter->cache;
377    if (m->next)
378	m->next->refcnt++;
379    iter->cache = m->next;
380    HEIMDAL_MUTEX_unlock(&mcc_mutex);
381
382    ret = _krb5_cc_allocate(context, &krb5_mcc_ops, id);
383    if (ret)
384	return ret;
385
386    (*id)->data.data = m;
387    (*id)->data.length = sizeof(*m);
388
389    return 0;
390}
391
392static krb5_error_code
393mcc_end_cache_get(krb5_context context, krb5_cc_cursor cursor)
394{
395    struct mcache_iter *iter = cursor;
396
397    if (iter->cache)
398	mcc_close_internal(iter->cache);
399    iter->cache = NULL;
400    free(iter);
401    return 0;
402}
403
404static krb5_error_code
405mcc_move(krb5_context context, krb5_ccache from, krb5_ccache to)
406{
407    krb5_mcache *mfrom = MCACHE(from), *mto = MCACHE(to);
408    struct link *creds;
409    krb5_principal principal;
410    krb5_mcache **n;
411
412    HEIMDAL_MUTEX_lock(&mcc_mutex);
413
414    /* drop the from cache from the linked list to avoid lookups */
415    for(n = &mcc_head; n && *n; n = &(*n)->next) {
416	if(mfrom == *n) {
417	    *n = mfrom->next;
418	    break;
419	}
420    }
421
422    /* swap creds */
423    creds = mto->creds;
424    mto->creds = mfrom->creds;
425    mfrom->creds = creds;
426    /* swap principal */
427    principal = mto->primary_principal;
428    mto->primary_principal = mfrom->primary_principal;
429    mfrom->primary_principal = principal;
430
431    HEIMDAL_MUTEX_unlock(&mcc_mutex);
432    mcc_destroy(context, from);
433
434    return 0;
435}
436
437static krb5_error_code
438mcc_default_name(krb5_context context, char **str)
439{
440    *str = strdup("MEMORY:");
441    if (*str == NULL) {
442	krb5_set_error_string(context, "out of memory");
443	return ENOMEM;
444    }
445    return 0;
446}
447
448
449/**
450 * Variable containing the MEMORY based credential cache implemention.
451 *
452 * @ingroup krb5_ccache
453 */
454
455const krb5_cc_ops krb5_mcc_ops = {
456    "MEMORY",
457    mcc_get_name,
458    mcc_resolve,
459    mcc_gen_new,
460    mcc_initialize,
461    mcc_destroy,
462    mcc_close,
463    mcc_store_cred,
464    NULL, /* mcc_retrieve */
465    mcc_get_principal,
466    mcc_get_first,
467    mcc_get_next,
468    mcc_end_get,
469    mcc_remove_cred,
470    mcc_set_flags,
471    NULL,
472    mcc_get_cache_first,
473    mcc_get_cache_next,
474    mcc_end_cache_get,
475    mcc_move,
476    mcc_default_name
477};
478