xref: /netbsd-src/crypto/external/bsd/heimdal/dist/lib/krb5/mcache.c (revision 181254a7b1bdde6873432bffef2d2decc4b5c22f)
1 /*	$NetBSD: mcache.c,v 1.2 2017/01/28 21:31:49 christos Exp $	*/
2 
3 /*
4  * Copyright (c) 1997-2004 Kungliga Tekniska Högskolan
5  * (Royal Institute of Technology, Stockholm, Sweden).
6  * All rights reserved.
7  *
8  * Portions Copyright (c) 2009 Apple Inc. All rights reserved.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  *
14  * 1. Redistributions of source code must retain the above copyright
15  *    notice, this list of conditions and the following disclaimer.
16  *
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.
20  *
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.
24  *
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.
36  */
37 
38 #include "krb5_locl.h"
39 
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     HEIMDAL_MUTEX mutex;
53 } krb5_mcache;
54 
55 static HEIMDAL_MUTEX mcc_mutex = HEIMDAL_MUTEX_INITIALIZER;
56 static struct krb5_mcache *mcc_head;
57 
58 #define	MCACHE(X)	((krb5_mcache *)(X)->data.data)
59 
60 #define MISDEAD(X)	((X)->dead)
61 
62 static const char* KRB5_CALLCONV
63 mcc_get_name(krb5_context context,
64 	     krb5_ccache id)
65 {
66     return MCACHE(id)->name;
67 }
68 
69 static krb5_mcache * KRB5_CALLCONV
70 mcc_alloc(const char *name)
71 {
72     krb5_mcache *m, *m_c;
73     int ret = 0;
74 
75     ALLOC(m, 1);
76     if(m == NULL)
77 	return NULL;
78     if(name == NULL)
79 	ret = asprintf(&m->name, "%p", m);
80     else
81 	m->name = strdup(name);
82     if(ret < 0 || m->name == NULL) {
83 	free(m);
84 	return NULL;
85     }
86     /* check for dups first */
87     HEIMDAL_MUTEX_lock(&mcc_mutex);
88     for (m_c = mcc_head; m_c != NULL; m_c = m_c->next)
89 	if (strcmp(m->name, m_c->name) == 0)
90 	    break;
91     if (m_c) {
92 	free(m->name);
93 	free(m);
94 	HEIMDAL_MUTEX_unlock(&mcc_mutex);
95 	return NULL;
96     }
97 
98     m->dead = 0;
99     m->refcnt = 1;
100     m->primary_principal = NULL;
101     m->creds = NULL;
102     m->mtime = time(NULL);
103     m->kdc_offset = 0;
104     m->next = mcc_head;
105     HEIMDAL_MUTEX_init(&(m->mutex));
106     mcc_head = m;
107     HEIMDAL_MUTEX_unlock(&mcc_mutex);
108     return m;
109 }
110 
111 static krb5_error_code KRB5_CALLCONV
112 mcc_resolve(krb5_context context, krb5_ccache *id, const char *res)
113 {
114     krb5_mcache *m;
115 
116     HEIMDAL_MUTEX_lock(&mcc_mutex);
117     for (m = mcc_head; m != NULL; m = m->next)
118 	if (strcmp(m->name, res) == 0)
119 	    break;
120     HEIMDAL_MUTEX_unlock(&mcc_mutex);
121 
122     if (m != NULL) {
123     	HEIMDAL_MUTEX_lock(&(m->mutex));
124     	m->refcnt++;
125     	HEIMDAL_MUTEX_unlock(&(m->mutex));
126     	(*id)->data.data = m;
127     	(*id)->data.length = sizeof(*m);
128     	return 0;
129     }
130 
131     m = mcc_alloc(res);
132     if (m == NULL) {
133 	krb5_set_error_message(context, KRB5_CC_NOMEM,
134 			       N_("malloc: out of memory", ""));
135 	return KRB5_CC_NOMEM;
136     }
137 
138     (*id)->data.data = m;
139     (*id)->data.length = sizeof(*m);
140 
141     return 0;
142 }
143 
144 
145 static krb5_error_code KRB5_CALLCONV
146 mcc_gen_new(krb5_context context, krb5_ccache *id)
147 {
148     krb5_mcache *m;
149 
150     m = mcc_alloc(NULL);
151 
152     if (m == NULL) {
153 	krb5_set_error_message(context, KRB5_CC_NOMEM,
154 			       N_("malloc: out of memory", ""));
155 	return KRB5_CC_NOMEM;
156     }
157 
158     (*id)->data.data = m;
159     (*id)->data.length = sizeof(*m);
160 
161     return 0;
162 }
163 
164 static void KRB5_CALLCONV
165 mcc_destroy_internal(krb5_context context,
166 		     krb5_mcache *m)
167 {
168     struct link *l;
169 
170     if (m->primary_principal != NULL) {
171 	krb5_free_principal (context, m->primary_principal);
172 	m->primary_principal = NULL;
173     }
174     m->dead = 1;
175 
176     l = m->creds;
177     while (l != NULL) {
178 	struct link *old;
179 
180 	krb5_free_cred_contents (context, &l->cred);
181 	old = l;
182 	l = l->next;
183 	free (old);
184     }
185 
186     m->creds = NULL;
187     return;
188 }
189 
190 static krb5_error_code KRB5_CALLCONV
191 mcc_initialize(krb5_context context,
192 	       krb5_ccache id,
193 	       krb5_principal primary_principal)
194 {
195     krb5_mcache *m = MCACHE(id);
196     krb5_error_code ret = 0;
197     HEIMDAL_MUTEX_lock(&(m->mutex));
198     heim_assert(m->refcnt != 0, "resurection released mcache");
199     /*
200      * It's important to destroy any existing
201      * creds here, that matches the baheviour
202      * of all other backends and also the
203      * MEMORY: backend in MIT.
204      */
205     mcc_destroy_internal(context, m);
206     m->dead = 0;
207     m->kdc_offset = 0;
208     m->mtime = time(NULL);
209     ret = krb5_copy_principal (context,
210 			       primary_principal,
211 			       &m->primary_principal);
212     HEIMDAL_MUTEX_unlock(&(m->mutex));
213     return ret;
214 }
215 
216 static int
217 mcc_close_internal(krb5_mcache *m)
218 {
219     HEIMDAL_MUTEX_lock(&(m->mutex));
220     heim_assert(m->refcnt != 0, "closed dead cache mcache");
221     if (--m->refcnt != 0) {
222 	HEIMDAL_MUTEX_unlock(&(m->mutex));
223 	return 0;
224     }
225     if (MISDEAD(m)) {
226 	free (m->name);
227 	HEIMDAL_MUTEX_unlock(&(m->mutex));
228 	return 1;
229     }
230     HEIMDAL_MUTEX_unlock(&(m->mutex));
231     return 0;
232 }
233 
234 static krb5_error_code KRB5_CALLCONV
235 mcc_close(krb5_context context,
236 	  krb5_ccache id)
237 {
238     krb5_mcache *m = MCACHE(id);
239 
240     if (mcc_close_internal(MCACHE(id))) {
241 	HEIMDAL_MUTEX_destroy(&(m->mutex));
242 	krb5_data_free(&id->data);
243     }
244     return 0;
245 }
246 
247 static krb5_error_code KRB5_CALLCONV
248 mcc_destroy(krb5_context context,
249 	    krb5_ccache id)
250 {
251     krb5_mcache **n, *m = MCACHE(id);
252 
253     HEIMDAL_MUTEX_lock(&(m->mutex));
254     if (m->refcnt == 0)
255     {
256     	HEIMDAL_MUTEX_unlock(&(m->mutex));
257     	krb5_abortx(context, "mcc_destroy: refcnt already 0");
258     }
259 
260     if (!MISDEAD(m)) {
261 	/* if this is an active mcache, remove it from the linked
262            list, and free all data */
263 	HEIMDAL_MUTEX_lock(&mcc_mutex);
264 	for(n = &mcc_head; n && *n; n = &(*n)->next) {
265 	    if(m == *n) {
266 		*n = m->next;
267 		break;
268 	    }
269 	}
270 	HEIMDAL_MUTEX_unlock(&mcc_mutex);
271 	mcc_destroy_internal(context, m);
272     }
273     HEIMDAL_MUTEX_unlock(&(m->mutex));
274     return 0;
275 }
276 
277 static krb5_error_code KRB5_CALLCONV
278 mcc_store_cred(krb5_context context,
279 	       krb5_ccache id,
280 	       krb5_creds *creds)
281 {
282     krb5_mcache *m = MCACHE(id);
283     krb5_error_code ret;
284     struct link *l;
285 
286     HEIMDAL_MUTEX_lock(&(m->mutex));
287     if (MISDEAD(m))
288     {
289     	HEIMDAL_MUTEX_unlock(&(m->mutex));
290     	return ENOENT;
291     }
292 
293     l = malloc (sizeof(*l));
294     if (l == NULL) {
295     	krb5_set_error_message(context, KRB5_CC_NOMEM,
296     			N_("malloc: out of memory", ""));
297     	HEIMDAL_MUTEX_unlock(&(m->mutex));
298     	return KRB5_CC_NOMEM;
299     }
300     l->next = m->creds;
301     m->creds = l;
302     memset (&l->cred, 0, sizeof(l->cred));
303     ret = krb5_copy_creds_contents (context, creds, &l->cred);
304     if (ret) {
305     	m->creds = l->next;
306     	free (l);
307     	HEIMDAL_MUTEX_unlock(&(m->mutex));
308     	return ret;
309     }
310     m->mtime = time(NULL);
311 	HEIMDAL_MUTEX_unlock(&(m->mutex));
312     return 0;
313 }
314 
315 static krb5_error_code KRB5_CALLCONV
316 mcc_get_principal(krb5_context context,
317 		  krb5_ccache id,
318 		  krb5_principal *principal)
319 {
320     krb5_mcache *m = MCACHE(id);
321     krb5_error_code ret = 0;
322 
323     HEIMDAL_MUTEX_lock(&(m->mutex));
324     if (MISDEAD(m) || m->primary_principal == NULL) {
325 	HEIMDAL_MUTEX_unlock(&(m->mutex));
326 	return ENOENT;
327     }
328     ret = krb5_copy_principal (context,
329 			       m->primary_principal,
330 			       principal);
331     HEIMDAL_MUTEX_unlock(&(m->mutex));
332     return ret;
333 }
334 
335 static krb5_error_code KRB5_CALLCONV
336 mcc_get_first (krb5_context context,
337 		krb5_ccache id,
338 		krb5_cc_cursor *cursor)
339 {
340     krb5_mcache *m = MCACHE(id);
341 
342     HEIMDAL_MUTEX_lock(&(m->mutex));
343     if (MISDEAD(m)) {
344 	HEIMDAL_MUTEX_unlock(&(m->mutex));
345 	return ENOENT;
346     }
347     *cursor = m->creds;
348 
349     HEIMDAL_MUTEX_unlock(&(m->mutex));
350     return 0;
351 }
352 
353 static krb5_error_code KRB5_CALLCONV
354 mcc_get_next (krb5_context context,
355 	      krb5_ccache id,
356 	      krb5_cc_cursor *cursor,
357 	      krb5_creds *creds)
358 {
359     krb5_mcache *m = MCACHE(id);
360     struct link *l;
361 
362     HEIMDAL_MUTEX_lock(&(m->mutex));
363     if (MISDEAD(m)) {
364 	HEIMDAL_MUTEX_unlock(&(m->mutex));
365 	return ENOENT;
366     }
367     HEIMDAL_MUTEX_unlock(&(m->mutex));
368 
369     l = *cursor;
370     if (l != NULL) {
371 	*cursor = l->next;
372 	return krb5_copy_creds_contents (context,
373 					 &l->cred,
374 					 creds);
375     } else
376 	return KRB5_CC_END;
377 }
378 
379 static krb5_error_code KRB5_CALLCONV
380 mcc_end_get (krb5_context context,
381 	     krb5_ccache id,
382 	     krb5_cc_cursor *cursor)
383 {
384     return 0;
385 }
386 
387 static krb5_error_code KRB5_CALLCONV
388 mcc_remove_cred(krb5_context context,
389 		 krb5_ccache id,
390 		 krb5_flags which,
391 		 krb5_creds *mcreds)
392 {
393     krb5_mcache *m = MCACHE(id);
394     struct link **q, *p;
395 
396     HEIMDAL_MUTEX_lock(&(m->mutex));
397 
398     for(q = &m->creds, p = *q; p; p = *q) {
399 	if(krb5_compare_creds(context, which, mcreds, &p->cred)) {
400 	    *q = p->next;
401 	    krb5_free_cred_contents(context, &p->cred);
402 	    free(p);
403 	    m->mtime = time(NULL);
404 	} else
405 	    q = &p->next;
406     }
407     HEIMDAL_MUTEX_unlock(&(m->mutex));
408     return 0;
409 }
410 
411 static krb5_error_code KRB5_CALLCONV
412 mcc_set_flags(krb5_context context,
413 	      krb5_ccache id,
414 	      krb5_flags flags)
415 {
416     return 0; /* XXX */
417 }
418 
419 struct mcache_iter {
420     krb5_mcache *cache;
421 };
422 
423 static krb5_error_code KRB5_CALLCONV
424 mcc_get_cache_first(krb5_context context, krb5_cc_cursor *cursor)
425 {
426     struct mcache_iter *iter;
427 
428     iter = calloc(1, sizeof(*iter));
429     if (iter == NULL)
430 	return krb5_enomem(context);
431 
432     HEIMDAL_MUTEX_lock(&mcc_mutex);
433     iter->cache = mcc_head;
434     if (iter->cache) {
435 	HEIMDAL_MUTEX_lock(&(iter->cache->mutex));
436 	iter->cache->refcnt++;
437 	HEIMDAL_MUTEX_unlock(&(iter->cache->mutex));
438     }
439     HEIMDAL_MUTEX_unlock(&mcc_mutex);
440 
441     *cursor = iter;
442     return 0;
443 }
444 
445 static krb5_error_code KRB5_CALLCONV
446 mcc_get_cache_next(krb5_context context, krb5_cc_cursor cursor, krb5_ccache *id)
447 {
448     struct mcache_iter *iter = cursor;
449     krb5_error_code ret;
450     krb5_mcache *m;
451 
452     if (iter->cache == NULL)
453 	return KRB5_CC_END;
454 
455     HEIMDAL_MUTEX_lock(&mcc_mutex);
456     m = iter->cache;
457     if (m->next)
458     {
459     	HEIMDAL_MUTEX_lock(&(m->next->mutex));
460     	m->next->refcnt++;
461     	HEIMDAL_MUTEX_unlock(&(m->next->mutex));
462     }
463 
464     iter->cache = m->next;
465     HEIMDAL_MUTEX_unlock(&mcc_mutex);
466 
467     ret = _krb5_cc_allocate(context, &krb5_mcc_ops, id);
468     if (ret)
469 	return ret;
470 
471     (*id)->data.data = m;
472     (*id)->data.length = sizeof(*m);
473 
474     return 0;
475 }
476 
477 static krb5_error_code KRB5_CALLCONV
478 mcc_end_cache_get(krb5_context context, krb5_cc_cursor cursor)
479 {
480     struct mcache_iter *iter = cursor;
481 
482     if (iter->cache)
483     	mcc_close_internal(iter->cache);
484     iter->cache = NULL;
485     free(iter);
486     return 0;
487 }
488 
489 static krb5_error_code KRB5_CALLCONV
490 mcc_move(krb5_context context, krb5_ccache from, krb5_ccache to)
491 {
492     krb5_mcache *mfrom = MCACHE(from), *mto = MCACHE(to);
493     struct link *creds;
494     krb5_principal principal;
495     krb5_mcache **n;
496 
497     HEIMDAL_MUTEX_lock(&mcc_mutex);
498 
499     /* drop the from cache from the linked list to avoid lookups */
500     for(n = &mcc_head; n && *n; n = &(*n)->next) {
501 	if(mfrom == *n) {
502 	    *n = mfrom->next;
503 	    break;
504 	}
505     }
506 
507     HEIMDAL_MUTEX_lock(&(mfrom->mutex));
508     HEIMDAL_MUTEX_lock(&(mto->mutex));
509     /* swap creds */
510     creds = mto->creds;
511     mto->creds = mfrom->creds;
512     mfrom->creds = creds;
513     /* swap principal */
514     principal = mto->primary_principal;
515     mto->primary_principal = mfrom->primary_principal;
516     mfrom->primary_principal = principal;
517 
518     mto->mtime = mfrom->mtime = time(NULL);
519 
520     HEIMDAL_MUTEX_unlock(&(mfrom->mutex));
521     HEIMDAL_MUTEX_unlock(&(mto->mutex));
522     HEIMDAL_MUTEX_unlock(&mcc_mutex);
523     mcc_destroy(context, from);
524 
525     return 0;
526 }
527 
528 static krb5_error_code KRB5_CALLCONV
529 mcc_default_name(krb5_context context, char **str)
530 {
531     *str = strdup("MEMORY:");
532     if (*str == NULL)
533 	return krb5_enomem(context);
534     return 0;
535 }
536 
537 static krb5_error_code KRB5_CALLCONV
538 mcc_lastchange(krb5_context context, krb5_ccache id, krb5_timestamp *mtime)
539 {
540     krb5_mcache *m = MCACHE(id);
541     HEIMDAL_MUTEX_lock(&(m->mutex));
542     *mtime = m->mtime;
543     HEIMDAL_MUTEX_unlock(&(m->mutex));
544     return 0;
545 }
546 
547 static krb5_error_code KRB5_CALLCONV
548 mcc_set_kdc_offset(krb5_context context, krb5_ccache id, krb5_deltat kdc_offset)
549 {
550     krb5_mcache *m = MCACHE(id);
551     HEIMDAL_MUTEX_lock(&(m->mutex));
552     m->kdc_offset = kdc_offset;
553     HEIMDAL_MUTEX_unlock(&(m->mutex));
554     return 0;
555 }
556 
557 static krb5_error_code KRB5_CALLCONV
558 mcc_get_kdc_offset(krb5_context context, krb5_ccache id, krb5_deltat *kdc_offset)
559 {
560     krb5_mcache *m = MCACHE(id);
561     HEIMDAL_MUTEX_lock(&(m->mutex));
562     *kdc_offset = m->kdc_offset;
563     HEIMDAL_MUTEX_unlock(&(m->mutex));
564     return 0;
565 }
566 
567 
568 /**
569  * Variable containing the MEMORY based credential cache implemention.
570  *
571  * @ingroup krb5_ccache
572  */
573 
574 KRB5_LIB_VARIABLE const krb5_cc_ops krb5_mcc_ops = {
575     KRB5_CC_OPS_VERSION,
576     "MEMORY",
577     mcc_get_name,
578     mcc_resolve,
579     mcc_gen_new,
580     mcc_initialize,
581     mcc_destroy,
582     mcc_close,
583     mcc_store_cred,
584     NULL, /* mcc_retrieve */
585     mcc_get_principal,
586     mcc_get_first,
587     mcc_get_next,
588     mcc_end_get,
589     mcc_remove_cred,
590     mcc_set_flags,
591     NULL,
592     mcc_get_cache_first,
593     mcc_get_cache_next,
594     mcc_end_cache_get,
595     mcc_move,
596     mcc_default_name,
597     NULL,
598     mcc_lastchange,
599     mcc_set_kdc_offset,
600     mcc_get_kdc_offset
601 };
602