cdafc67bae6a097972fe4306ca1a2d0219666cc2
[mat/samba.git] / source4 / 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*
60 mcc_get_name(krb5_context context,
61              krb5_ccache id)
62 {
63     return MCACHE(id)->name;
64 }
65
66 static krb5_mcache *
67 mcc_alloc(const char *name)
68 {
69     krb5_mcache *m, *m_c;
70
71     ALLOC(m, 1);
72     if(m == NULL)
73         return NULL;
74     if(name == NULL)
75         asprintf(&m->name, "%p", m);
76     else
77         m->name = strdup(name);
78     if(m->name == NULL) {
79         free(m);
80         return NULL;
81     }
82     /* check for dups first */
83     HEIMDAL_MUTEX_lock(&mcc_mutex);
84     for (m_c = mcc_head; m_c != NULL; m_c = m_c->next)
85         if (strcmp(m->name, m_c->name) == 0)
86             break;
87     if (m_c) {
88         free(m->name);
89         free(m);
90         HEIMDAL_MUTEX_unlock(&mcc_mutex);
91         return NULL;
92     }
93
94     m->dead = 0;
95     m->refcnt = 1;
96     m->primary_principal = NULL;
97     m->creds = NULL;
98     m->mtime = time(NULL);
99     m->kdc_offset = 0;
100     m->next = mcc_head;
101     mcc_head = m;
102     HEIMDAL_MUTEX_unlock(&mcc_mutex);
103     return m;
104 }
105
106 static krb5_error_code
107 mcc_resolve(krb5_context context, krb5_ccache *id, const char *res)
108 {
109     krb5_mcache *m;
110
111     HEIMDAL_MUTEX_lock(&mcc_mutex);
112     for (m = mcc_head; m != NULL; m = m->next)
113         if (strcmp(m->name, res) == 0)
114             break;
115     HEIMDAL_MUTEX_unlock(&mcc_mutex);
116
117     if (m != NULL) {
118         m->refcnt++;
119         (*id)->data.data = m;
120         (*id)->data.length = sizeof(*m);
121         return 0;
122     }
123
124     m = mcc_alloc(res);
125     if (m == NULL) {
126         krb5_set_error_message(context, KRB5_CC_NOMEM,
127                                N_("malloc: out of memory", ""));
128         return KRB5_CC_NOMEM;
129     }
130
131     (*id)->data.data = m;
132     (*id)->data.length = sizeof(*m);
133
134     return 0;
135 }
136
137
138 static krb5_error_code
139 mcc_gen_new(krb5_context context, krb5_ccache *id)
140 {
141     krb5_mcache *m;
142
143     m = mcc_alloc(NULL);
144
145     if (m == NULL) {
146         krb5_set_error_message(context, KRB5_CC_NOMEM,
147                                N_("malloc: out of memory", ""));
148         return KRB5_CC_NOMEM;
149     }
150
151     (*id)->data.data = m;
152     (*id)->data.length = sizeof(*m);
153
154     return 0;
155 }
156
157 static krb5_error_code
158 mcc_initialize(krb5_context context,
159                krb5_ccache id,
160                krb5_principal primary_principal)
161 {
162     krb5_mcache *m = MCACHE(id);
163     m->dead = 0;
164     m->mtime = time(NULL);
165     return krb5_copy_principal (context,
166                                 primary_principal,
167                                 &m->primary_principal);
168 }
169
170 static int
171 mcc_close_internal(krb5_mcache *m)
172 {
173     if (--m->refcnt != 0)
174         return 0;
175
176     if (MISDEAD(m)) {
177         free (m->name);
178         return 1;
179     }
180     return 0;
181 }
182
183 static krb5_error_code
184 mcc_close(krb5_context context,
185           krb5_ccache id)
186 {
187     if (mcc_close_internal(MCACHE(id)))
188         krb5_data_free(&id->data);
189     return 0;
190 }
191
192 static krb5_error_code
193 mcc_destroy(krb5_context context,
194             krb5_ccache id)
195 {
196     krb5_mcache **n, *m = MCACHE(id);
197     struct link *l;
198
199     if (m->refcnt == 0)
200         krb5_abortx(context, "mcc_destroy: refcnt already 0");
201
202     if (!MISDEAD(m)) {
203         /* if this is an active mcache, remove it from the linked
204            list, and free all data */
205         HEIMDAL_MUTEX_lock(&mcc_mutex);
206         for(n = &mcc_head; n && *n; n = &(*n)->next) {
207             if(m == *n) {
208                 *n = m->next;
209                 break;
210             }
211         }
212         HEIMDAL_MUTEX_unlock(&mcc_mutex);
213         if (m->primary_principal != NULL) {
214             krb5_free_principal (context, m->primary_principal);
215             m->primary_principal = NULL;
216         }
217         m->dead = 1;
218
219         l = m->creds;
220         while (l != NULL) {
221             struct link *old;
222         
223             krb5_free_cred_contents (context, &l->cred);
224             old = l;
225             l = l->next;
226             free (old);
227         }
228         m->creds = NULL;
229     }
230     return 0;
231 }
232
233 static krb5_error_code
234 mcc_store_cred(krb5_context context,
235                krb5_ccache id,
236                krb5_creds *creds)
237 {
238     krb5_mcache *m = MCACHE(id);
239     krb5_error_code ret;
240     struct link *l;
241
242     if (MISDEAD(m))
243         return ENOENT;
244
245     l = malloc (sizeof(*l));
246     if (l == NULL) {
247         krb5_set_error_message(context, KRB5_CC_NOMEM,
248                                N_("malloc: out of memory", ""));
249         return KRB5_CC_NOMEM;
250     }
251     l->next = m->creds;
252     m->creds = l;
253     memset (&l->cred, 0, sizeof(l->cred));
254     ret = krb5_copy_creds_contents (context, creds, &l->cred);
255     if (ret) {
256         m->creds = l->next;
257         free (l);
258         return ret;
259     }
260     m->mtime = time(NULL);
261     return 0;
262 }
263
264 static krb5_error_code
265 mcc_get_principal(krb5_context context,
266                   krb5_ccache id,
267                   krb5_principal *principal)
268 {
269     krb5_mcache *m = MCACHE(id);
270
271     if (MISDEAD(m) || m->primary_principal == NULL)
272         return ENOENT;
273     return krb5_copy_principal (context,
274                                 m->primary_principal,
275                                 principal);
276 }
277
278 static krb5_error_code
279 mcc_get_first (krb5_context context,
280                krb5_ccache id,
281                krb5_cc_cursor *cursor)
282 {
283     krb5_mcache *m = MCACHE(id);
284
285     if (MISDEAD(m))
286         return ENOENT;
287
288     *cursor = m->creds;
289     return 0;
290 }
291
292 static krb5_error_code
293 mcc_get_next (krb5_context context,
294               krb5_ccache id,
295               krb5_cc_cursor *cursor,
296               krb5_creds *creds)
297 {
298     krb5_mcache *m = MCACHE(id);
299     struct link *l;
300
301     if (MISDEAD(m))
302         return ENOENT;
303
304     l = *cursor;
305     if (l != NULL) {
306         *cursor = l->next;
307         return krb5_copy_creds_contents (context,
308                                          &l->cred,
309                                          creds);
310     } else
311         return KRB5_CC_END;
312 }
313
314 static krb5_error_code
315 mcc_end_get (krb5_context context,
316              krb5_ccache id,
317              krb5_cc_cursor *cursor)
318 {
319     return 0;
320 }
321
322 static krb5_error_code
323 mcc_remove_cred(krb5_context context,
324                  krb5_ccache id,
325                  krb5_flags which,
326                  krb5_creds *mcreds)
327 {
328     krb5_mcache *m = MCACHE(id);
329     struct link **q, *p;
330     for(q = &m->creds, p = *q; p; p = *q) {
331         if(krb5_compare_creds(context, which, mcreds, &p->cred)) {
332             *q = p->next;
333             krb5_free_cred_contents(context, &p->cred);
334             free(p);
335             m->mtime = time(NULL);
336         } else
337             q = &p->next;
338     }
339     return 0;
340 }
341
342 static krb5_error_code
343 mcc_set_flags(krb5_context context,
344               krb5_ccache id,
345               krb5_flags flags)
346 {
347     return 0; /* XXX */
348 }
349                 
350 struct mcache_iter {
351     krb5_mcache *cache;
352 };
353
354 static krb5_error_code
355 mcc_get_cache_first(krb5_context context, krb5_cc_cursor *cursor)
356 {
357     struct mcache_iter *iter;
358
359     iter = calloc(1, sizeof(*iter));
360     if (iter == NULL) {
361         krb5_set_error_message(context, ENOMEM,
362                                N_("malloc: out of memory", ""));
363         return ENOMEM;
364     }
365
366     HEIMDAL_MUTEX_lock(&mcc_mutex);
367     iter->cache = mcc_head;
368     if (iter->cache)
369         iter->cache->refcnt++;
370     HEIMDAL_MUTEX_unlock(&mcc_mutex);
371
372     *cursor = iter;
373     return 0;
374 }
375
376 static krb5_error_code
377 mcc_get_cache_next(krb5_context context, krb5_cc_cursor cursor, krb5_ccache *id)
378 {
379     struct mcache_iter *iter = cursor;
380     krb5_error_code ret;
381     krb5_mcache *m;
382
383     if (iter->cache == NULL)
384         return KRB5_CC_END;
385
386     HEIMDAL_MUTEX_lock(&mcc_mutex);
387     m = iter->cache;
388     if (m->next)
389         m->next->refcnt++;
390     iter->cache = m->next;
391     HEIMDAL_MUTEX_unlock(&mcc_mutex);
392
393     ret = _krb5_cc_allocate(context, &krb5_mcc_ops, id);
394     if (ret)
395         return ret;
396
397     (*id)->data.data = m;
398     (*id)->data.length = sizeof(*m);
399
400     return 0;
401 }
402
403 static krb5_error_code
404 mcc_end_cache_get(krb5_context context, krb5_cc_cursor cursor)
405 {
406     struct mcache_iter *iter = cursor;
407
408     if (iter->cache)
409         mcc_close_internal(iter->cache);
410     iter->cache = NULL;
411     free(iter);
412     return 0;
413 }
414
415 static krb5_error_code
416 mcc_move(krb5_context context, krb5_ccache from, krb5_ccache to)
417 {
418     krb5_mcache *mfrom = MCACHE(from), *mto = MCACHE(to);
419     struct link *creds;
420     krb5_principal principal;
421     krb5_mcache **n;
422
423     HEIMDAL_MUTEX_lock(&mcc_mutex);
424
425     /* drop the from cache from the linked list to avoid lookups */
426     for(n = &mcc_head; n && *n; n = &(*n)->next) {
427         if(mfrom == *n) {
428             *n = mfrom->next;
429             break;
430         }
431     }
432
433     /* swap creds */
434     creds = mto->creds;
435     mto->creds = mfrom->creds;
436     mfrom->creds = creds;
437     /* swap principal */
438     principal = mto->primary_principal;
439     mto->primary_principal = mfrom->primary_principal;
440     mfrom->primary_principal = principal;
441
442     mto->mtime = mfrom->mtime = time(NULL);
443
444     HEIMDAL_MUTEX_unlock(&mcc_mutex);
445     mcc_destroy(context, from);
446
447     return 0;
448 }
449
450 static krb5_error_code
451 mcc_default_name(krb5_context context, char **str)
452 {
453     *str = strdup("MEMORY:");
454     if (*str == NULL) {
455         krb5_set_error_message(context, ENOMEM,
456                                N_("malloc: out of memory", ""));
457         return ENOMEM;
458     }
459     return 0;
460 }
461
462 static krb5_error_code
463 mcc_lastchange(krb5_context context, krb5_ccache id, krb5_timestamp *mtime)
464 {
465     *mtime = MCACHE(id)->mtime;
466     return 0;
467 }
468
469 static krb5_error_code
470 mcc_set_kdc_offset(krb5_context context, krb5_ccache id, krb5_deltat kdc_offset)
471 {
472     krb5_mcache *m = MCACHE(id);
473     m->kdc_offset = kdc_offset;
474     return 0;
475 }
476
477 static krb5_error_code
478 mcc_get_kdc_offset(krb5_context context, krb5_ccache id, krb5_deltat *kdc_offset)
479 {
480     krb5_mcache *m = MCACHE(id);
481     *kdc_offset = m->kdc_offset;
482     return 0;
483 }
484
485
486 /**
487  * Variable containing the MEMORY based credential cache implemention.
488  *
489  * @ingroup krb5_ccache
490  */
491
492 KRB5_LIB_VARIABLE const krb5_cc_ops krb5_mcc_ops = {
493     KRB5_CC_OPS_VERSION,
494     "MEMORY",
495     mcc_get_name,
496     mcc_resolve,
497     mcc_gen_new,
498     mcc_initialize,
499     mcc_destroy,
500     mcc_close,
501     mcc_store_cred,
502     NULL, /* mcc_retrieve */
503     mcc_get_principal,
504     mcc_get_first,
505     mcc_get_next,
506     mcc_end_get,
507     mcc_remove_cred,
508     mcc_set_flags,
509     NULL,
510     mcc_get_cache_first,
511     mcc_get_cache_next,
512     mcc_end_cache_get,
513     mcc_move,
514     mcc_default_name,
515     NULL,
516     mcc_lastchange,
517     mcc_set_kdc_offset,
518     mcc_get_kdc_offset
519 };