Imported Upstream version 1.20.1
[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
352 k5_nonatomic_replace(krb5_context context, krb5_ccache ccache,
353                      krb5_principal princ, krb5_creds **creds)
354 {
355     krb5_error_code ret;
356     int i;
357
358     ret = krb5_cc_initialize(context, ccache, princ);
359     for (i = 0; !ret && creds[i] != NULL; creds++)
360         ret = krb5_cc_store_cred(context, ccache, creds[i]);
361     return ret;
362 }
363
364 static krb5_error_code
365 read_creds(krb5_context context, krb5_ccache ccache, krb5_creds ***creds_out)
366 {
367     krb5_error_code ret;
368     krb5_cc_cursor cur = NULL;
369     krb5_creds **list = NULL, *cred = NULL, **newptr;
370     int i;
371
372     *creds_out = NULL;
373
374     ret = krb5_cc_start_seq_get(context, ccache, &cur);
375     if (ret)
376         goto cleanup;
377
378     /* Allocate one extra entry so that list remains valid for freeing after
379      * we add the next entry and before we reallocate it. */
380     list = k5calloc(2, sizeof(*list), &ret);
381     if (list == NULL)
382         goto cleanup;
383
384     i = 0;
385     for (;;) {
386         cred = k5alloc(sizeof(*cred), &ret);
387         if (cred == NULL)
388             goto cleanup;
389         ret = krb5_cc_next_cred(context, ccache, &cur, cred);
390         if (ret == KRB5_CC_END)
391             break;
392         if (ret)
393             goto cleanup;
394         list[i++] = cred;
395         list[i] = NULL;
396         cred = NULL;
397
398         newptr = realloc(list, (i + 2) * sizeof(*list));
399         if (newptr == NULL) {
400             ret = ENOMEM;
401             goto cleanup;
402         }
403         list = newptr;
404         list[i + 1] = NULL;
405     }
406     ret = 0;
407
408     *creds_out = list;
409     list = NULL;
410
411 cleanup:
412     if (cur != NULL)
413         (void)krb5_cc_end_seq_get(context, ccache, &cur);
414     krb5_free_tgt_creds(context, list);
415     free(cred);
416     return ret;
417 }
418
419 krb5_error_code KRB5_CALLCONV
420 krb5_cc_move(krb5_context context, krb5_ccache src, krb5_ccache dst)
421 {
422     krb5_error_code ret;
423     krb5_principal princ = NULL;
424     krb5_creds **creds = NULL;
425
426     TRACE_CC_MOVE(context, src, dst);
427
428     ret = krb5_cc_get_principal(context, src, &princ);
429     if (ret)
430         goto cleanup;
431
432     ret = read_creds(context, src, &creds);
433     if (ret)
434         goto cleanup;
435
436     if (dst->ops->replace == NULL)
437         ret = k5_nonatomic_replace(context, dst, princ, creds);
438     else
439         ret = dst->ops->replace(context, dst, princ, creds);
440     if (ret)
441         goto cleanup;
442
443     ret = krb5_cc_destroy(context, src);
444
445 cleanup:
446     krb5_free_principal(context, princ);
447     krb5_free_tgt_creds(context, creds);
448     return ret;
449 }
450
451 krb5_boolean KRB5_CALLCONV
452 krb5_cc_support_switch(krb5_context context, const char *type)
453 {
454     const krb5_cc_ops *ops;
455     krb5_error_code err;
456
457     err = krb5int_cc_getops(context, type, &ops);
458     return (err ? FALSE : (ops->switch_to != NULL));
459 }
460
461 krb5_error_code
462 k5_cc_mutex_init(k5_cc_mutex *m)
463 {
464     krb5_error_code ret = 0;
465
466     ret = k5_mutex_init(&m->lock);
467     if (ret) return ret;
468     m->owner = NULL;
469     m->refcount = 0;
470
471     return ret;
472 }
473
474 krb5_error_code
475 k5_cc_mutex_finish_init(k5_cc_mutex *m)
476 {
477     krb5_error_code ret = 0;
478
479     ret = k5_mutex_finish_init(&m->lock);
480     if (ret) return ret;
481     m->owner = NULL;
482     m->refcount = 0;
483
484     return ret;
485 }
486
487 void
488 k5_cc_mutex_assert_locked(krb5_context context, k5_cc_mutex *m)
489 {
490 #ifdef DEBUG_THREADS
491     assert(m->refcount > 0);
492     assert(m->owner == context);
493 #endif
494     k5_assert_locked(&m->lock);
495 }
496
497 void
498 k5_cc_mutex_assert_unlocked(krb5_context context, k5_cc_mutex *m)
499 {
500 #ifdef DEBUG_THREADS
501     assert(m->refcount == 0);
502     assert(m->owner == NULL);
503 #endif
504     k5_assert_unlocked(&m->lock);
505 }
506
507 void
508 k5_cc_mutex_lock(krb5_context context, k5_cc_mutex *m)
509 {
510     /* not locked or already locked by another context */
511     if (m->owner != context) {
512         /* acquire lock, blocking until available */
513         k5_mutex_lock(&m->lock);
514         m->owner = context;
515         m->refcount = 1;
516     }
517     /* already locked by this context, just increase refcount */
518     else {
519         m->refcount++;
520     }
521 }
522
523 void
524 k5_cc_mutex_unlock(krb5_context context, k5_cc_mutex *m)
525 {
526     /* verify owner and sanity check refcount */
527     if ((m->owner != context) || (m->refcount < 1)) {
528         return;
529     }
530     /* decrement & unlock when count reaches zero */
531     m->refcount--;
532     if (m->refcount == 0) {
533         m->owner = NULL;
534         k5_mutex_unlock(&m->lock);
535     }
536 }
537
538 /* necessary to make reentrant locks play nice with krb5int_cc_finalize */
539 void
540 k5_cc_mutex_force_unlock(k5_cc_mutex *m)
541 {
542     m->refcount = 0;
543     m->owner = NULL;
544     if (m->refcount > 0) {
545         k5_mutex_unlock(&m->lock);
546     }
547 }
548
549 /*
550  * holds on to all pertype global locks as well as typelist lock
551  */
552
553 krb5_error_code
554 k5_cccol_lock(krb5_context context)
555 {
556     krb5_error_code ret = 0;
557
558     k5_cc_mutex_lock(context, &cccol_lock);
559     k5_mutex_lock(&cc_typelist_lock);
560     k5_cc_mutex_lock(context, &krb5int_cc_file_mutex);
561     k5_cc_mutex_lock(context, &krb5int_mcc_mutex);
562 #ifdef USE_KEYRING_CCACHE
563     k5_cc_mutex_lock(context, &krb5int_krcc_mutex);
564 #endif
565 #ifdef USE_CCAPI_V3
566     ret = krb5_stdccv3_context_lock(context);
567     if (ret) {
568         k5_cc_mutex_unlock(context, &krb5int_mcc_mutex);
569         k5_cc_mutex_unlock(context, &krb5int_cc_file_mutex);
570         k5_mutex_unlock(&cc_typelist_lock);
571         k5_cc_mutex_unlock(context, &cccol_lock);
572         return ret;
573     }
574 #endif
575     k5_mutex_unlock(&cc_typelist_lock);
576     return ret;
577 }
578
579 krb5_error_code
580 k5_cccol_unlock(krb5_context context)
581 {
582     krb5_error_code ret = 0;
583
584     /* sanity check */
585     k5_cc_mutex_assert_locked(context, &cccol_lock);
586
587     k5_mutex_lock(&cc_typelist_lock);
588
589     /* unlock each type in the opposite order */
590 #ifdef USE_CCAPI_V3
591     krb5_stdccv3_context_unlock(context);
592 #endif
593 #ifdef USE_KEYRING_CCACHE
594     k5_cc_mutex_assert_locked(context, &krb5int_krcc_mutex);
595     k5_cc_mutex_unlock(context, &krb5int_krcc_mutex);
596 #endif
597     k5_cc_mutex_assert_locked(context, &krb5int_mcc_mutex);
598     k5_cc_mutex_unlock(context, &krb5int_mcc_mutex);
599     k5_cc_mutex_assert_locked(context, &krb5int_cc_file_mutex);
600     k5_cc_mutex_unlock(context, &krb5int_cc_file_mutex);
601     k5_mutex_assert_locked(&cc_typelist_lock);
602
603     k5_mutex_unlock(&cc_typelist_lock);
604     k5_cc_mutex_unlock(context, &cccol_lock);
605
606     return ret;
607 }
608
609 /* necessary to make reentrant locks play nice with krb5int_cc_finalize */
610 void
611 k5_cccol_force_unlock()
612 {
613     /* sanity check */
614     if ((&cccol_lock)->refcount == 0) {
615         return;
616     }
617
618     k5_mutex_lock(&cc_typelist_lock);
619
620     /* unlock each type in the opposite order */
621 #ifdef USE_KEYRING_CCACHE
622     k5_cc_mutex_force_unlock(&krb5int_krcc_mutex);
623 #endif
624 #ifdef USE_CCAPI_V3
625     krb5_stdccv3_context_unlock(NULL);
626 #endif
627     k5_cc_mutex_force_unlock(&krb5int_mcc_mutex);
628     k5_cc_mutex_force_unlock(&krb5int_cc_file_mutex);
629
630     k5_mutex_unlock(&cc_typelist_lock);
631     k5_cc_mutex_force_unlock(&cccol_lock);
632 }