xref: /freebsd/crypto/heimdal/lib/krb5/mcache.c (revision 271c3a9060f2ee55607ebe146523f888e1db2654)
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 
36 RCSID("$Id: mcache.c 22107 2007-12-03 17:22:51Z lha $");
37 
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 } krb5_mcache;
49 
50 static HEIMDAL_MUTEX mcc_mutex = HEIMDAL_MUTEX_INITIALIZER;
51 static struct krb5_mcache *mcc_head;
52 
53 #define	MCACHE(X)	((krb5_mcache *)(X)->data.data)
54 
55 #define MISDEAD(X)	((X)->dead)
56 
57 static const char*
58 mcc_get_name(krb5_context context,
59 	     krb5_ccache id)
60 {
61     return MCACHE(id)->name;
62 }
63 
64 static krb5_mcache *
65 mcc_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 
102 static krb5_error_code
103 mcc_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 
133 static krb5_error_code
134 mcc_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 
151 static krb5_error_code
152 mcc_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 
163 static int
164 mcc_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 
176 static krb5_error_code
177 mcc_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 
185 static krb5_error_code
186 mcc_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 
226 static krb5_error_code
227 mcc_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 
255 static krb5_error_code
256 mcc_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 
269 static krb5_error_code
270 mcc_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 
283 static krb5_error_code
284 mcc_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 
305 static krb5_error_code
306 mcc_end_get (krb5_context context,
307 	     krb5_ccache id,
308 	     krb5_cc_cursor *cursor)
309 {
310     return 0;
311 }
312 
313 static krb5_error_code
314 mcc_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 
332 static krb5_error_code
333 mcc_set_flags(krb5_context context,
334 	      krb5_ccache id,
335 	      krb5_flags flags)
336 {
337     return 0; /* XXX */
338 }
339 
340 struct mcache_iter {
341     krb5_mcache *cache;
342 };
343 
344 static krb5_error_code
345 mcc_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 
365 static krb5_error_code
366 mcc_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 
392 static krb5_error_code
393 mcc_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 
404 static krb5_error_code
405 mcc_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 
437 static krb5_error_code
438 mcc_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 
455 const 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