Fix autoconf 2.70 compatibility
[platform/upstream/krb5.git] / src / lib / krb5 / ccache / ccbase.c
1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /* lib/krb5/ccache/ccbase.c - Registration functions for ccache */
3 /*
4  * Copyright 1990,2004,2008 by the Massachusetts Institute of Technology.
5  * All Rights Reserved.
6  *
7  * Export of this software from the United States of America may
8  *   require a specific license from the United States Government.
9  *   It is the responsibility of any person or organization contemplating
10  *   export to obtain such a license before exporting.
11  *
12  * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
13  * distribute this software and its documentation for any purpose and
14  * without fee is hereby granted, provided that the above copyright
15  * notice appear in all copies and that both that copyright notice and
16  * this permission notice appear in supporting documentation, and that
17  * the name of M.I.T. not be used in advertising or publicity pertaining
18  * to distribution of the software without specific, written prior
19  * permission.  Furthermore if you modify this software you must label
20  * your software as modified software and not distribute it in such a
21  * fashion that it might be confused with the original M.I.T. software.
22  * M.I.T. makes no representations about the suitability of
23  * this software for any purpose.  It is provided "as is" without express
24  * or implied warranty.
25  */
26
27 #include "k5-int.h"
28 #include "k5-thread.h"
29
30 #include "fcc.h"
31 #include "cc-int.h"
32
33 struct krb5_cc_typelist {
34     const krb5_cc_ops *ops;
35     struct krb5_cc_typelist *next;
36 };
37
38 struct krb5_cc_typecursor {
39     struct krb5_cc_typelist *tptr;
40 };
41 /* typedef krb5_cc_typecursor in k5-int.h */
42
43 extern const krb5_cc_ops krb5_mcc_ops;
44
45 #define NEXT NULL
46
47 #ifdef _WIN32
48 extern const krb5_cc_ops krb5_lcc_ops;
49 static struct krb5_cc_typelist cc_lcc_entry = { &krb5_lcc_ops, NEXT };
50 #undef NEXT
51 #define NEXT &cc_lcc_entry
52 #endif
53
54 #ifdef USE_CCAPI_V3
55 extern const krb5_cc_ops krb5_cc_stdcc_ops;
56 static struct krb5_cc_typelist cc_stdcc_entry = { &krb5_cc_stdcc_ops, NEXT };
57 #undef NEXT
58 #define NEXT &cc_stdcc_entry
59 #endif
60
61 static struct krb5_cc_typelist cc_mcc_entry = { &krb5_mcc_ops, NEXT };
62 #undef NEXT
63 #define NEXT &cc_mcc_entry
64
65 #ifndef NO_FILE_CCACHE
66 static struct krb5_cc_typelist cc_fcc_entry = { &krb5_cc_file_ops, NEXT };
67 #undef NEXT
68 #define NEXT &cc_fcc_entry
69 #endif
70
71 #ifdef USE_KEYRING_CCACHE
72 extern const krb5_cc_ops krb5_krcc_ops;
73 static struct krb5_cc_typelist cc_krcc_entry = { &krb5_krcc_ops, NEXT };
74 #undef NEXT
75 #define NEXT &cc_krcc_entry
76 #endif /* USE_KEYRING_CCACHE */
77
78 #ifndef _WIN32
79 extern const krb5_cc_ops krb5_dcc_ops;
80 static struct krb5_cc_typelist cc_dcc_entry = { &krb5_dcc_ops, NEXT };
81 #undef NEXT
82 #define NEXT &cc_dcc_entry
83
84 extern const krb5_cc_ops krb5_kcm_ops;
85 static struct krb5_cc_typelist cc_kcm_entry = { &krb5_kcm_ops, NEXT };
86 #undef NEXT
87 #define NEXT &cc_kcm_entry
88 #endif /* not _WIN32 */
89
90
91 #define INITIAL_TYPEHEAD (NEXT)
92 static struct krb5_cc_typelist *cc_typehead = INITIAL_TYPEHEAD;
93 static k5_mutex_t cc_typelist_lock = K5_MUTEX_PARTIAL_INITIALIZER;
94
95 /* mutex for krb5_cccol_[un]lock */
96 static k5_cc_mutex cccol_lock = K5_CC_MUTEX_PARTIAL_INITIALIZER;
97
98 static krb5_error_code
99 krb5int_cc_getops(krb5_context, const char *, const krb5_cc_ops **);
100
101 int
102 krb5int_cc_initialize(void)
103 {
104     int err;
105
106     err = k5_cc_mutex_finish_init(&cccol_lock);
107     if (err)
108         return err;
109     err = k5_cc_mutex_finish_init(&krb5int_mcc_mutex);
110     if (err)
111         return err;
112     err = k5_mutex_finish_init(&cc_typelist_lock);
113     if (err)
114         return err;
115 #ifndef NO_FILE_CCACHE
116     err = k5_cc_mutex_finish_init(&krb5int_cc_file_mutex);
117     if (err)
118         return err;
119 #endif
120 #ifdef USE_KEYRING_CCACHE
121     err = k5_cc_mutex_finish_init(&krb5int_krcc_mutex);
122     if (err)
123         return err;
124 #endif
125     return 0;
126 }
127
128 void
129 krb5int_cc_finalize(void)
130 {
131     struct krb5_cc_typelist *t, *t_next;
132     k5_cccol_force_unlock();
133     k5_cc_mutex_destroy(&cccol_lock);
134     k5_mutex_destroy(&cc_typelist_lock);
135 #ifndef NO_FILE_CCACHE
136     k5_cc_mutex_destroy(&krb5int_cc_file_mutex);
137 #endif
138     k5_cc_mutex_destroy(&krb5int_mcc_mutex);
139 #ifdef USE_KEYRING_CCACHE
140     k5_cc_mutex_destroy(&krb5int_krcc_mutex);
141 #endif
142     for (t = cc_typehead; t != INITIAL_TYPEHEAD; t = t_next) {
143         t_next = t->next;
144         free(t);
145     }
146 }
147
148
149 /*
150  * Register a new credentials cache type
151  * If override is set, replace any existing ccache with that type tag
152  */
153
154 krb5_error_code KRB5_CALLCONV
155 krb5_cc_register(krb5_context context, const krb5_cc_ops *ops,
156                  krb5_boolean override)
157 {
158     struct krb5_cc_typelist *t;
159
160     k5_mutex_lock(&cc_typelist_lock);
161     for (t = cc_typehead;t && strcmp(t->ops->prefix,ops->prefix);t = t->next)
162         ;
163     if (t) {
164         if (override) {
165             t->ops = ops;
166             k5_mutex_unlock(&cc_typelist_lock);
167             return 0;
168         } else {
169             k5_mutex_unlock(&cc_typelist_lock);
170             return KRB5_CC_TYPE_EXISTS;
171         }
172     }
173     if (!(t = (struct krb5_cc_typelist *) malloc(sizeof(*t)))) {
174         k5_mutex_unlock(&cc_typelist_lock);
175         return ENOMEM;
176     }
177     t->next = cc_typehead;
178     t->ops = ops;
179     cc_typehead = t;
180     k5_mutex_unlock(&cc_typelist_lock);
181     return 0;
182 }
183
184 /*
185  * Resolve a credential cache name into a cred. cache object.
186  *
187  * The name is currently constrained to be of the form "type:residual";
188  *
189  * The "type" portion corresponds to one of the predefined credential
190  * cache types, while the "residual" portion is specific to the
191  * particular cache type.
192  */
193
194 #include <ctype.h>
195 krb5_error_code KRB5_CALLCONV
196 krb5_cc_resolve (krb5_context context, const char *name, krb5_ccache *cache)
197 {
198     char *pfx, *cp;
199     const char *resid;
200     unsigned int pfxlen;
201     krb5_error_code err;
202     const krb5_cc_ops *ops;
203
204     if (name == NULL)
205         return KRB5_CC_BADNAME;
206     pfx = NULL;
207     cp = strchr (name, ':');
208     if (!cp) {
209         if (krb5_cc_dfl_ops)
210             return (*krb5_cc_dfl_ops->resolve)(context, cache, name);
211         else
212             return KRB5_CC_BADNAME;
213     }
214
215     pfxlen = cp - name;
216
217     if ( pfxlen == 1 && isalpha((unsigned char) name[0]) ) {
218         /* We found a drive letter not a prefix - use FILE */
219         pfx = strdup("FILE");
220         if (!pfx)
221             return ENOMEM;
222
223         resid = name;
224     } else {
225         resid = name + pfxlen + 1;
226         pfx = k5memdup0(name, pfxlen, &err);
227         if (pfx == NULL)
228             return err;
229     }
230
231     *cache = (krb5_ccache) 0;
232
233     err = krb5int_cc_getops(context, pfx, &ops);
234     if (pfx != NULL)
235         free(pfx);
236     if (err)
237         return err;
238
239     return ops->resolve(context, cache, resid);
240 }
241
242 krb5_error_code KRB5_CALLCONV
243 krb5_cc_dup(krb5_context context, krb5_ccache in, krb5_ccache *out)
244 {
245     return in->ops->resolve(context, out, in->ops->get_name(context, in));
246 }
247
248 /*
249  * cc_getops
250  *
251  * Internal function to return the ops vector for a given ccache
252  * prefix string.
253  */
254 static krb5_error_code
255 krb5int_cc_getops(krb5_context context,
256                   const char *pfx,
257                   const krb5_cc_ops **ops)
258 {
259     struct krb5_cc_typelist *tlist;
260
261     k5_mutex_lock(&cc_typelist_lock);
262     for (tlist = cc_typehead; tlist; tlist = tlist->next) {
263         if (strcmp (tlist->ops->prefix, pfx) == 0) {
264             *ops = tlist->ops;
265             k5_mutex_unlock(&cc_typelist_lock);
266             return 0;
267         }
268     }
269     k5_mutex_unlock(&cc_typelist_lock);
270     if (krb5_cc_dfl_ops && !strcmp (pfx, krb5_cc_dfl_ops->prefix)) {
271         *ops = krb5_cc_dfl_ops;
272         return 0;
273     }
274     return KRB5_CC_UNKNOWN_TYPE;
275 }
276
277 /*
278  * cc_new_unique
279  *
280  * Generate a new unique ccache, given a ccache type and a hint
281  * string.  Ignores the hint string for now.
282  */
283 krb5_error_code KRB5_CALLCONV
284 krb5_cc_new_unique(
285     krb5_context context,
286     const char *type,
287     const char *hint,
288     krb5_ccache *id)
289 {
290     const krb5_cc_ops *ops;
291     krb5_error_code err;
292
293     *id = NULL;
294
295     TRACE_CC_NEW_UNIQUE(context, type);
296     err = krb5int_cc_getops(context, type, &ops);
297     if (err)
298         return err;
299
300     return ops->gen_new(context, id);
301 }
302
303 /*
304  * cc_typecursor
305  *
306  * Note: to avoid copying the typelist at cursor creation time, among
307  * other things, we assume that the only additions ever occur to the
308  * typelist.
309  */
310 krb5_error_code
311 krb5int_cc_typecursor_new(krb5_context context, krb5_cc_typecursor *t)
312 {
313     krb5_cc_typecursor n = NULL;
314
315     *t = NULL;
316     n = malloc(sizeof(*n));
317     if (n == NULL)
318         return ENOMEM;
319
320     k5_mutex_lock(&cc_typelist_lock);
321     n->tptr = cc_typehead;
322     k5_mutex_unlock(&cc_typelist_lock);
323     *t = n;
324     return 0;
325 }
326
327 krb5_error_code
328 krb5int_cc_typecursor_next(krb5_context context,
329                            krb5_cc_typecursor t,
330                            const krb5_cc_ops **ops)
331 {
332     *ops = NULL;
333     if (t->tptr == NULL)
334         return 0;
335
336     k5_mutex_lock(&cc_typelist_lock);
337     *ops = t->tptr->ops;
338     t->tptr = t->tptr->next;
339     k5_mutex_unlock(&cc_typelist_lock);
340     return 0;
341 }
342
343 krb5_error_code
344 krb5int_cc_typecursor_free(krb5_context context, krb5_cc_typecursor *t)
345 {
346     free(*t);
347     *t = NULL;
348     return 0;
349 }
350
351 krb5_error_code KRB5_CALLCONV
352 krb5_cc_move(krb5_context context, krb5_ccache src, krb5_ccache dst)
353 {
354     krb5_error_code ret = 0;
355     krb5_principal princ = NULL;
356
357     TRACE_CC_MOVE(context, src, dst);
358     ret = krb5_cccol_lock(context);
359     if (ret) {
360         return ret;
361     }
362
363     ret = krb5_cc_lock(context, src);
364     if (ret) {
365         krb5_cccol_unlock(context);
366         return ret;
367     }
368
369     ret = krb5_cc_get_principal(context, src, &princ);
370     if (!ret) {
371         ret = krb5_cc_initialize(context, dst, princ);
372     }
373     if (ret) {
374         krb5_cc_unlock(context, src);
375         krb5_cccol_unlock(context);
376         return ret;
377     }
378
379     ret = krb5_cc_lock(context, dst);
380     if (!ret) {
381         ret = krb5_cc_copy_creds(context, src, dst);
382         krb5_cc_unlock(context, dst);
383     }
384
385     krb5_cc_unlock(context, src);
386     if (!ret) {
387         ret = krb5_cc_destroy(context, src);
388     }
389     krb5_cccol_unlock(context);
390     if (princ) {
391         krb5_free_principal(context, princ);
392         princ = NULL;
393     }
394
395     return ret;
396 }
397
398 krb5_boolean KRB5_CALLCONV
399 krb5_cc_support_switch(krb5_context context, const char *type)
400 {
401     const krb5_cc_ops *ops;
402     krb5_error_code err;
403
404     err = krb5int_cc_getops(context, type, &ops);
405     return (err ? FALSE : (ops->switch_to != NULL));
406 }
407
408 krb5_error_code
409 k5_cc_mutex_init(k5_cc_mutex *m)
410 {
411     krb5_error_code ret = 0;
412
413     ret = k5_mutex_init(&m->lock);
414     if (ret) return ret;
415     m->owner = NULL;
416     m->refcount = 0;
417
418     return ret;
419 }
420
421 krb5_error_code
422 k5_cc_mutex_finish_init(k5_cc_mutex *m)
423 {
424     krb5_error_code ret = 0;
425
426     ret = k5_mutex_finish_init(&m->lock);
427     if (ret) return ret;
428     m->owner = NULL;
429     m->refcount = 0;
430
431     return ret;
432 }
433
434 void
435 k5_cc_mutex_assert_locked(krb5_context context, k5_cc_mutex *m)
436 {
437 #ifdef DEBUG_THREADS
438     assert(m->refcount > 0);
439     assert(m->owner == context);
440 #endif
441     k5_assert_locked(&m->lock);
442 }
443
444 void
445 k5_cc_mutex_assert_unlocked(krb5_context context, k5_cc_mutex *m)
446 {
447 #ifdef DEBUG_THREADS
448     assert(m->refcount == 0);
449     assert(m->owner == NULL);
450 #endif
451     k5_assert_unlocked(&m->lock);
452 }
453
454 void
455 k5_cc_mutex_lock(krb5_context context, k5_cc_mutex *m)
456 {
457     /* not locked or already locked by another context */
458     if (m->owner != context) {
459         /* acquire lock, blocking until available */
460         k5_mutex_lock(&m->lock);
461         m->owner = context;
462         m->refcount = 1;
463     }
464     /* already locked by this context, just increase refcount */
465     else {
466         m->refcount++;
467     }
468 }
469
470 void
471 k5_cc_mutex_unlock(krb5_context context, k5_cc_mutex *m)
472 {
473     /* verify owner and sanity check refcount */
474     if ((m->owner != context) || (m->refcount < 1)) {
475         return;
476     }
477     /* decrement & unlock when count reaches zero */
478     m->refcount--;
479     if (m->refcount == 0) {
480         m->owner = NULL;
481         k5_mutex_unlock(&m->lock);
482     }
483 }
484
485 /* necessary to make reentrant locks play nice with krb5int_cc_finalize */
486 void
487 k5_cc_mutex_force_unlock(k5_cc_mutex *m)
488 {
489     m->refcount = 0;
490     m->owner = NULL;
491     if (m->refcount > 0) {
492         k5_mutex_unlock(&m->lock);
493     }
494 }
495
496 /*
497  * holds on to all pertype global locks as well as typelist lock
498  */
499
500 krb5_error_code KRB5_CALLCONV
501 krb5_cccol_lock(krb5_context context)
502 {
503     krb5_error_code ret = 0;
504
505     k5_cc_mutex_lock(context, &cccol_lock);
506     k5_mutex_lock(&cc_typelist_lock);
507     k5_cc_mutex_lock(context, &krb5int_cc_file_mutex);
508     k5_cc_mutex_lock(context, &krb5int_mcc_mutex);
509 #ifdef USE_KEYRING_CCACHE
510     k5_cc_mutex_lock(context, &krb5int_krcc_mutex);
511 #endif
512 #ifdef USE_CCAPI_V3
513     ret = krb5_stdccv3_context_lock(context);
514 #endif
515     if (ret) {
516         k5_cc_mutex_unlock(context, &krb5int_mcc_mutex);
517         k5_cc_mutex_unlock(context, &krb5int_cc_file_mutex);
518         k5_mutex_unlock(&cc_typelist_lock);
519         k5_cc_mutex_unlock(context, &cccol_lock);
520         return ret;
521     }
522     k5_mutex_unlock(&cc_typelist_lock);
523     return ret;
524 }
525
526 krb5_error_code KRB5_CALLCONV
527 krb5_cccol_unlock(krb5_context context)
528 {
529     krb5_error_code ret = 0;
530
531     /* sanity check */
532     k5_cc_mutex_assert_locked(context, &cccol_lock);
533
534     k5_mutex_lock(&cc_typelist_lock);
535
536     /* unlock each type in the opposite order */
537 #ifdef USE_CCAPI_V3
538     krb5_stdccv3_context_unlock(context);
539 #endif
540 #ifdef USE_KEYRING_CCACHE
541     k5_cc_mutex_assert_locked(context, &krb5int_krcc_mutex);
542     k5_cc_mutex_unlock(context, &krb5int_krcc_mutex);
543 #endif
544     k5_cc_mutex_assert_locked(context, &krb5int_mcc_mutex);
545     k5_cc_mutex_unlock(context, &krb5int_mcc_mutex);
546     k5_cc_mutex_assert_locked(context, &krb5int_cc_file_mutex);
547     k5_cc_mutex_unlock(context, &krb5int_cc_file_mutex);
548     k5_mutex_assert_locked(&cc_typelist_lock);
549
550     k5_mutex_unlock(&cc_typelist_lock);
551     k5_cc_mutex_unlock(context, &cccol_lock);
552
553     return ret;
554 }
555
556 /* necessary to make reentrant locks play nice with krb5int_cc_finalize */
557 void
558 k5_cccol_force_unlock()
559 {
560     /* sanity check */
561     if ((&cccol_lock)->refcount == 0) {
562         return;
563     }
564
565     k5_mutex_lock(&cc_typelist_lock);
566
567     /* unlock each type in the opposite order */
568 #ifdef USE_KEYRING_CCACHE
569     k5_cc_mutex_force_unlock(&krb5int_krcc_mutex);
570 #endif
571 #ifdef USE_CCAPI_V3
572     krb5_stdccv3_context_unlock(NULL);
573 #endif
574     k5_cc_mutex_force_unlock(&krb5int_mcc_mutex);
575     k5_cc_mutex_force_unlock(&krb5int_cc_file_mutex);
576
577     k5_mutex_unlock(&cc_typelist_lock);
578     k5_cc_mutex_force_unlock(&cccol_lock);
579 }