r23456: Update Samba4 to current lorikeet-heimdal.
[ab/samba.git/.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  * 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 19834 2007-01-11 09:26:21Z 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 const krb5_cc_ops krb5_mcc_ops = {
405     "MEMORY",
406     mcc_get_name,
407     mcc_resolve,
408     mcc_gen_new,
409     mcc_initialize,
410     mcc_destroy,
411     mcc_close,
412     mcc_store_cred,
413     NULL, /* mcc_retrieve */
414     mcc_get_principal,
415     mcc_get_first,
416     mcc_get_next,
417     mcc_end_get,
418     mcc_remove_cred,
419     mcc_set_flags,
420     NULL,
421     mcc_get_cache_first,
422     mcc_get_cache_next,
423     mcc_end_cache_get
424 };