]> CyberLeo.Net >> Repos - FreeBSD/releng/10.2.git/blob - crypto/heimdal/lib/krb5/mcache.c
- Copy stable/10@285827 to releng/10.2 in preparation for 10.2-RC1
[FreeBSD/releng/10.2.git] / crypto / heimdal / lib / krb5 / mcache.c
1 /*
2  * Copyright (c) 1997-2004 Kungliga Tekniska Högskolan
3  * (Royal Institute of Technology, Stockholm, Sweden).
4  * All rights reserved.
5  *
6  * Portions Copyright (c) 2009 Apple Inc. All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  *
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  *
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  *
19  * 3. Neither the name of the Institute nor the names of its contributors
20  *    may be used to endorse or promote products derived from this software
21  *    without specific prior written permission.
22  *
23  * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
24  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26  * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
27  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33  * SUCH DAMAGE.
34  */
35
36 #include "krb5_locl.h"
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     time_t mtime;
49     krb5_deltat kdc_offset;
50 } krb5_mcache;
51
52 static HEIMDAL_MUTEX mcc_mutex = HEIMDAL_MUTEX_INITIALIZER;
53 static struct krb5_mcache *mcc_head;
54
55 #define MCACHE(X)       ((krb5_mcache *)(X)->data.data)
56
57 #define MISDEAD(X)      ((X)->dead)
58
59 static const char* KRB5_CALLCONV
60 mcc_get_name(krb5_context context,
61              krb5_ccache id)
62 {
63     return MCACHE(id)->name;
64 }
65
66 static krb5_mcache * KRB5_CALLCONV
67 mcc_alloc(const char *name)
68 {
69     krb5_mcache *m, *m_c;
70     int ret = 0;
71
72     ALLOC(m, 1);
73     if(m == NULL)
74         return NULL;
75     if(name == NULL)
76         ret = asprintf(&m->name, "%p", m);
77     else
78         m->name = strdup(name);
79     if(ret < 0 || m->name == NULL) {
80         free(m);
81         return NULL;
82     }
83     /* check for dups first */
84     HEIMDAL_MUTEX_lock(&mcc_mutex);
85     for (m_c = mcc_head; m_c != NULL; m_c = m_c->next)
86         if (strcmp(m->name, m_c->name) == 0)
87             break;
88     if (m_c) {
89         free(m->name);
90         free(m);
91         HEIMDAL_MUTEX_unlock(&mcc_mutex);
92         return NULL;
93     }
94
95     m->dead = 0;
96     m->refcnt = 1;
97     m->primary_principal = NULL;
98     m->creds = NULL;
99     m->mtime = time(NULL);
100     m->kdc_offset = 0;
101     m->next = mcc_head;
102     mcc_head = m;
103     HEIMDAL_MUTEX_unlock(&mcc_mutex);
104     return m;
105 }
106
107 static krb5_error_code KRB5_CALLCONV
108 mcc_resolve(krb5_context context, krb5_ccache *id, const char *res)
109 {
110     krb5_mcache *m;
111
112     HEIMDAL_MUTEX_lock(&mcc_mutex);
113     for (m = mcc_head; m != NULL; m = m->next)
114         if (strcmp(m->name, res) == 0)
115             break;
116     HEIMDAL_MUTEX_unlock(&mcc_mutex);
117
118     if (m != NULL) {
119         m->refcnt++;
120         (*id)->data.data = m;
121         (*id)->data.length = sizeof(*m);
122         return 0;
123     }
124
125     m = mcc_alloc(res);
126     if (m == NULL) {
127         krb5_set_error_message(context, KRB5_CC_NOMEM,
128                                N_("malloc: out of memory", ""));
129         return KRB5_CC_NOMEM;
130     }
131
132     (*id)->data.data = m;
133     (*id)->data.length = sizeof(*m);
134
135     return 0;
136 }
137
138
139 static krb5_error_code KRB5_CALLCONV
140 mcc_gen_new(krb5_context context, krb5_ccache *id)
141 {
142     krb5_mcache *m;
143
144     m = mcc_alloc(NULL);
145
146     if (m == NULL) {
147         krb5_set_error_message(context, KRB5_CC_NOMEM,
148                                N_("malloc: out of memory", ""));
149         return KRB5_CC_NOMEM;
150     }
151
152     (*id)->data.data = m;
153     (*id)->data.length = sizeof(*m);
154
155     return 0;
156 }
157
158 static krb5_error_code KRB5_CALLCONV
159 mcc_initialize(krb5_context context,
160                krb5_ccache id,
161                krb5_principal primary_principal)
162 {
163     krb5_mcache *m = MCACHE(id);
164     m->dead = 0;
165     m->mtime = time(NULL);
166     return krb5_copy_principal (context,
167                                 primary_principal,
168                                 &m->primary_principal);
169 }
170
171 static int
172 mcc_close_internal(krb5_mcache *m)
173 {
174     if (--m->refcnt != 0)
175         return 0;
176
177     if (MISDEAD(m)) {
178         free (m->name);
179         return 1;
180     }
181     return 0;
182 }
183
184 static krb5_error_code KRB5_CALLCONV
185 mcc_close(krb5_context context,
186           krb5_ccache id)
187 {
188     if (mcc_close_internal(MCACHE(id)))
189         krb5_data_free(&id->data);
190     return 0;
191 }
192
193 static krb5_error_code KRB5_CALLCONV
194 mcc_destroy(krb5_context context,
195             krb5_ccache id)
196 {
197     krb5_mcache **n, *m = MCACHE(id);
198     struct link *l;
199
200     if (m->refcnt == 0)
201         krb5_abortx(context, "mcc_destroy: refcnt already 0");
202
203     if (!MISDEAD(m)) {
204         /* if this is an active mcache, remove it from the linked
205            list, and free all data */
206         HEIMDAL_MUTEX_lock(&mcc_mutex);
207         for(n = &mcc_head; n && *n; n = &(*n)->next) {
208             if(m == *n) {
209                 *n = m->next;
210                 break;
211             }
212         }
213         HEIMDAL_MUTEX_unlock(&mcc_mutex);
214         if (m->primary_principal != NULL) {
215             krb5_free_principal (context, m->primary_principal);
216             m->primary_principal = NULL;
217         }
218         m->dead = 1;
219
220         l = m->creds;
221         while (l != NULL) {
222             struct link *old;
223
224             krb5_free_cred_contents (context, &l->cred);
225             old = l;
226             l = l->next;
227             free (old);
228         }
229         m->creds = NULL;
230     }
231     return 0;
232 }
233
234 static krb5_error_code KRB5_CALLCONV
235 mcc_store_cred(krb5_context context,
236                krb5_ccache id,
237                krb5_creds *creds)
238 {
239     krb5_mcache *m = MCACHE(id);
240     krb5_error_code ret;
241     struct link *l;
242
243     if (MISDEAD(m))
244         return ENOENT;
245
246     l = malloc (sizeof(*l));
247     if (l == NULL) {
248         krb5_set_error_message(context, KRB5_CC_NOMEM,
249                                N_("malloc: out of memory", ""));
250         return KRB5_CC_NOMEM;
251     }
252     l->next = m->creds;
253     m->creds = l;
254     memset (&l->cred, 0, sizeof(l->cred));
255     ret = krb5_copy_creds_contents (context, creds, &l->cred);
256     if (ret) {
257         m->creds = l->next;
258         free (l);
259         return ret;
260     }
261     m->mtime = time(NULL);
262     return 0;
263 }
264
265 static krb5_error_code KRB5_CALLCONV
266 mcc_get_principal(krb5_context context,
267                   krb5_ccache id,
268                   krb5_principal *principal)
269 {
270     krb5_mcache *m = MCACHE(id);
271
272     if (MISDEAD(m) || m->primary_principal == NULL)
273         return ENOENT;
274     return krb5_copy_principal (context,
275                                 m->primary_principal,
276                                 principal);
277 }
278
279 static krb5_error_code KRB5_CALLCONV
280 mcc_get_first (krb5_context context,
281                krb5_ccache id,
282                krb5_cc_cursor *cursor)
283 {
284     krb5_mcache *m = MCACHE(id);
285
286     if (MISDEAD(m))
287         return ENOENT;
288
289     *cursor = m->creds;
290     return 0;
291 }
292
293 static krb5_error_code KRB5_CALLCONV
294 mcc_get_next (krb5_context context,
295               krb5_ccache id,
296               krb5_cc_cursor *cursor,
297               krb5_creds *creds)
298 {
299     krb5_mcache *m = MCACHE(id);
300     struct link *l;
301
302     if (MISDEAD(m))
303         return ENOENT;
304
305     l = *cursor;
306     if (l != NULL) {
307         *cursor = l->next;
308         return krb5_copy_creds_contents (context,
309                                          &l->cred,
310                                          creds);
311     } else
312         return KRB5_CC_END;
313 }
314
315 static krb5_error_code KRB5_CALLCONV
316 mcc_end_get (krb5_context context,
317              krb5_ccache id,
318              krb5_cc_cursor *cursor)
319 {
320     return 0;
321 }
322
323 static krb5_error_code KRB5_CALLCONV
324 mcc_remove_cred(krb5_context context,
325                  krb5_ccache id,
326                  krb5_flags which,
327                  krb5_creds *mcreds)
328 {
329     krb5_mcache *m = MCACHE(id);
330     struct link **q, *p;
331     for(q = &m->creds, p = *q; p; p = *q) {
332         if(krb5_compare_creds(context, which, mcreds, &p->cred)) {
333             *q = p->next;
334             krb5_free_cred_contents(context, &p->cred);
335             free(p);
336             m->mtime = time(NULL);
337         } else
338             q = &p->next;
339     }
340     return 0;
341 }
342
343 static krb5_error_code KRB5_CALLCONV
344 mcc_set_flags(krb5_context context,
345               krb5_ccache id,
346               krb5_flags flags)
347 {
348     return 0; /* XXX */
349 }
350
351 struct mcache_iter {
352     krb5_mcache *cache;
353 };
354
355 static krb5_error_code KRB5_CALLCONV
356 mcc_get_cache_first(krb5_context context, krb5_cc_cursor *cursor)
357 {
358     struct mcache_iter *iter;
359
360     iter = calloc(1, sizeof(*iter));
361     if (iter == NULL) {
362         krb5_set_error_message(context, ENOMEM,
363                                N_("malloc: out of memory", ""));
364         return ENOMEM;
365     }
366
367     HEIMDAL_MUTEX_lock(&mcc_mutex);
368     iter->cache = mcc_head;
369     if (iter->cache)
370         iter->cache->refcnt++;
371     HEIMDAL_MUTEX_unlock(&mcc_mutex);
372
373     *cursor = iter;
374     return 0;
375 }
376
377 static krb5_error_code KRB5_CALLCONV
378 mcc_get_cache_next(krb5_context context, krb5_cc_cursor cursor, krb5_ccache *id)
379 {
380     struct mcache_iter *iter = cursor;
381     krb5_error_code ret;
382     krb5_mcache *m;
383
384     if (iter->cache == NULL)
385         return KRB5_CC_END;
386
387     HEIMDAL_MUTEX_lock(&mcc_mutex);
388     m = iter->cache;
389     if (m->next)
390         m->next->refcnt++;
391     iter->cache = m->next;
392     HEIMDAL_MUTEX_unlock(&mcc_mutex);
393
394     ret = _krb5_cc_allocate(context, &krb5_mcc_ops, id);
395     if (ret)
396         return ret;
397
398     (*id)->data.data = m;
399     (*id)->data.length = sizeof(*m);
400
401     return 0;
402 }
403
404 static krb5_error_code KRB5_CALLCONV
405 mcc_end_cache_get(krb5_context context, krb5_cc_cursor cursor)
406 {
407     struct mcache_iter *iter = cursor;
408
409     if (iter->cache)
410         mcc_close_internal(iter->cache);
411     iter->cache = NULL;
412     free(iter);
413     return 0;
414 }
415
416 static krb5_error_code KRB5_CALLCONV
417 mcc_move(krb5_context context, krb5_ccache from, krb5_ccache to)
418 {
419     krb5_mcache *mfrom = MCACHE(from), *mto = MCACHE(to);
420     struct link *creds;
421     krb5_principal principal;
422     krb5_mcache **n;
423
424     HEIMDAL_MUTEX_lock(&mcc_mutex);
425
426     /* drop the from cache from the linked list to avoid lookups */
427     for(n = &mcc_head; n && *n; n = &(*n)->next) {
428         if(mfrom == *n) {
429             *n = mfrom->next;
430             break;
431         }
432     }
433
434     /* swap creds */
435     creds = mto->creds;
436     mto->creds = mfrom->creds;
437     mfrom->creds = creds;
438     /* swap principal */
439     principal = mto->primary_principal;
440     mto->primary_principal = mfrom->primary_principal;
441     mfrom->primary_principal = principal;
442
443     mto->mtime = mfrom->mtime = time(NULL);
444
445     HEIMDAL_MUTEX_unlock(&mcc_mutex);
446     mcc_destroy(context, from);
447
448     return 0;
449 }
450
451 static krb5_error_code KRB5_CALLCONV
452 mcc_default_name(krb5_context context, char **str)
453 {
454     *str = strdup("MEMORY:");
455     if (*str == NULL) {
456         krb5_set_error_message(context, ENOMEM,
457                                N_("malloc: out of memory", ""));
458         return ENOMEM;
459     }
460     return 0;
461 }
462
463 static krb5_error_code KRB5_CALLCONV
464 mcc_lastchange(krb5_context context, krb5_ccache id, krb5_timestamp *mtime)
465 {
466     *mtime = MCACHE(id)->mtime;
467     return 0;
468 }
469
470 static krb5_error_code KRB5_CALLCONV
471 mcc_set_kdc_offset(krb5_context context, krb5_ccache id, krb5_deltat kdc_offset)
472 {
473     krb5_mcache *m = MCACHE(id);
474     m->kdc_offset = kdc_offset;
475     return 0;
476 }
477
478 static krb5_error_code KRB5_CALLCONV
479 mcc_get_kdc_offset(krb5_context context, krb5_ccache id, krb5_deltat *kdc_offset)
480 {
481     krb5_mcache *m = MCACHE(id);
482     *kdc_offset = m->kdc_offset;
483     return 0;
484 }
485
486
487 /**
488  * Variable containing the MEMORY based credential cache implemention.
489  *
490  * @ingroup krb5_ccache
491  */
492
493 KRB5_LIB_VARIABLE const krb5_cc_ops krb5_mcc_ops = {
494     KRB5_CC_OPS_VERSION,
495     "MEMORY",
496     mcc_get_name,
497     mcc_resolve,
498     mcc_gen_new,
499     mcc_initialize,
500     mcc_destroy,
501     mcc_close,
502     mcc_store_cred,
503     NULL, /* mcc_retrieve */
504     mcc_get_principal,
505     mcc_get_first,
506     mcc_get_next,
507     mcc_end_get,
508     mcc_remove_cred,
509     mcc_set_flags,
510     NULL,
511     mcc_get_cache_first,
512     mcc_get_cache_next,
513     mcc_end_cache_get,
514     mcc_move,
515     mcc_default_name,
516     NULL,
517     mcc_lastchange,
518     mcc_set_kdc_offset,
519     mcc_get_kdc_offset
520 };