1/*
2 * lib/krb5/ccache/ccbase.c
3 *
4 * Copyright 1990,2004 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 * Registration functions for ccache.
28 */
29
30/*
31 * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
32 * Use is subject to license terms.
33 */
34
35
36#include "k5-int.h"
37#include "k5-thread.h"
38
39#include "fcc.h"
40#include "cc-int.h"
41
42struct krb5_cc_typelist {
43    const krb5_cc_ops *ops;
44    struct krb5_cc_typelist *next;
45};
46
47struct krb5_cc_typecursor {
48    struct krb5_cc_typelist *tptr;
49};
50/* typedef krb5_cc_typecursor in k5-int.h */
51
52extern const krb5_cc_ops krb5_mcc_ops;
53#ifdef USE_KEYRING_CCACHE
54extern const krb5_cc_ops krb5_krcc_ops;
55#endif
56
57#ifdef _WIN32
58extern const krb5_cc_ops krb5_lcc_ops;
59static struct krb5_cc_typelist cc_lcc_entry = { &krb5_lcc_ops, NULL };
60static struct krb5_cc_typelist cc_mcc_entry = { &krb5_mcc_ops, &cc_lcc_entry };
61#else
62
63#ifdef USE_CCAPI_V3
64extern const krb5_cc_ops krb5_cc_stdcc_ops;
65static struct krb5_cc_typelist cc_stdcc_entry = { &krb5_cc_stdcc_ops, NULL };
66static struct krb5_cc_typelist cc_mcc_entry = { &krb5_mcc_ops, &cc_stdcc_entry };
67#else
68
69static struct krb5_cc_typelist cc_mcc_entry = { &krb5_mcc_ops, NULL };
70#endif /* USE_CCAPI_V3 */
71
72#ifdef USE_KEYRING_CCACHE
73static struct krb5_cc_typelist cc_file_entry = { &krb5_cc_file_ops,
74						 &cc_mcc_entry };
75static struct krb5_cc_typelist cc_krcc_entry = { &krb5_krcc_ops,
76						 &cc_file_entry };
77#endif /* USE_KEYRING_CCACHE */
78#endif
79
80static struct krb5_cc_typelist cc_fcc_entry = { &krb5_cc_file_ops,
81						&cc_mcc_entry };
82#ifdef USE_KEYRING_CCACHE
83#define INITIAL_TYPEHEAD (&cc_krcc_entry)
84#else
85#define INITIAL_TYPEHEAD (&cc_fcc_entry)
86#endif
87static struct krb5_cc_typelist *cc_typehead = INITIAL_TYPEHEAD;
88static k5_mutex_t cc_typelist_lock = K5_MUTEX_PARTIAL_INITIALIZER;
89
90static krb5_error_code
91krb5int_cc_getops(krb5_context, const char *, const krb5_cc_ops **);
92
93int
94krb5int_cc_initialize(void)
95{
96    int err;
97
98    err = k5_mutex_finish_init(&krb5int_mcc_mutex);
99    if (err)
100	return err;
101    err = k5_mutex_finish_init(&cc_typelist_lock);
102    if (err)
103	return err;
104    err = k5_mutex_finish_init(&krb5int_cc_file_mutex);
105    if (err)
106	return err;
107#ifdef USE_KEYRING_CCACHE
108    err = k5_mutex_finish_init(&krb5int_krcc_mutex);
109    if (err)
110	return err;
111#endif
112    return 0;
113}
114
115void
116krb5int_cc_finalize(void)
117{
118    struct krb5_cc_typelist *t, *t_next;
119    k5_mutex_destroy(&cc_typelist_lock);
120    k5_mutex_destroy(&krb5int_cc_file_mutex);
121    k5_mutex_destroy(&krb5int_mcc_mutex);
122#ifdef USE_KEYRING_CCACHE
123    k5_mutex_destroy(&krb5int_krcc_mutex);
124#endif
125    for (t = cc_typehead; t != INITIAL_TYPEHEAD; t = t_next) {
126	t_next = t->next;
127	free(t);
128    }
129}
130
131
132/*
133 * Register a new credentials cache type
134 * If override is set, replace any existing ccache with that type tag
135 */
136
137krb5_error_code KRB5_CALLCONV
138krb5_cc_register(krb5_context context, krb5_cc_ops *ops, krb5_boolean override)
139{
140    struct krb5_cc_typelist *t;
141    krb5_error_code err;
142
143    err = k5_mutex_lock(&cc_typelist_lock);
144    if (err)
145	return err;
146    for (t = cc_typehead;t && strcmp(t->ops->prefix,ops->prefix);t = t->next)
147	;
148    if (t) {
149	if (override) {
150	    t->ops = ops;
151	    k5_mutex_unlock(&cc_typelist_lock);
152	    return 0;
153	} else {
154	    k5_mutex_unlock(&cc_typelist_lock);
155	    return KRB5_CC_TYPE_EXISTS;
156	}
157    }
158    if (!(t = (struct krb5_cc_typelist *) malloc(sizeof(*t)))) {
159	k5_mutex_unlock(&cc_typelist_lock);
160	return ENOMEM;
161    }
162    t->next = cc_typehead;
163    t->ops = ops;
164    cc_typehead = t;
165    k5_mutex_unlock(&cc_typelist_lock);
166    return 0;
167}
168
169/*
170 * Resolve a credential cache name into a cred. cache object.
171 *
172 * The name is currently constrained to be of the form "type:residual";
173 *
174 * The "type" portion corresponds to one of the predefined credential
175 * cache types, while the "residual" portion is specific to the
176 * particular cache type.
177 */
178
179#include <ctype.h>
180krb5_error_code KRB5_CALLCONV
181krb5_cc_resolve (krb5_context context, const char *name, krb5_ccache *cache)
182{
183    char *pfx, *cp;
184    const char *resid;
185    unsigned int pfxlen;
186    krb5_error_code err;
187    const krb5_cc_ops *ops;
188
189    /* Solaris Kerberos */
190    if (!name)
191        return KRB5_CC_BADNAME;
192
193    pfx = NULL;
194    cp = strchr (name, ':');
195    if (!cp) {
196	if (krb5_cc_dfl_ops)
197	    return (*krb5_cc_dfl_ops->resolve)(context, cache, name);
198	else
199	    return KRB5_CC_BADNAME;
200    }
201
202    pfxlen = cp - name;
203
204    if ( pfxlen == 1 && isalpha((unsigned char) name[0]) ) {
205        /* We found a drive letter not a prefix - use FILE */
206        pfx = strdup("FILE");
207        if (!pfx)
208            return ENOMEM;
209
210        resid = name;
211    } else {
212        resid = name + pfxlen + 1;
213
214        pfx = malloc (pfxlen+1);
215        if (!pfx)
216            return ENOMEM;
217
218        memcpy (pfx, name, pfxlen);
219        pfx[pfxlen] = '\0';
220    }
221
222    *cache = (krb5_ccache) 0;
223
224    err = krb5int_cc_getops(context, pfx, &ops);
225    if (pfx != NULL)
226	free(pfx);
227    if (err)
228	return err;
229
230    return ops->resolve(context, cache, resid);
231}
232
233/*
234 * cc_getops
235 *
236 * Internal function to return the ops vector for a given ccache
237 * prefix string.
238 */
239static krb5_error_code
240krb5int_cc_getops(
241    krb5_context context,
242    const char *pfx,
243    const krb5_cc_ops **ops)
244{
245    krb5_error_code err;
246    struct krb5_cc_typelist *tlist;
247
248    err = k5_mutex_lock(&cc_typelist_lock);
249    if (err)
250	return err;
251
252    for (tlist = cc_typehead; tlist; tlist = tlist->next) {
253	if (strcmp (tlist->ops->prefix, pfx) == 0) {
254	    *ops = tlist->ops;
255	    k5_mutex_unlock(&cc_typelist_lock);
256	    return 0;
257	}
258    }
259    k5_mutex_unlock(&cc_typelist_lock);
260    if (krb5_cc_dfl_ops && !strcmp (pfx, krb5_cc_dfl_ops->prefix)) {
261	*ops = krb5_cc_dfl_ops;
262	return 0;
263    }
264    return KRB5_CC_UNKNOWN_TYPE;
265}
266
267/*
268 * cc_new_unique
269 *
270 * Generate a new unique ccache, given a ccache type and a hint
271 * string.  Ignores the hint string for now.
272 */
273krb5_error_code KRB5_CALLCONV
274krb5_cc_new_unique(
275    krb5_context context,
276    const char *type,
277    const char *hint,
278    krb5_ccache *id)
279{
280    const krb5_cc_ops *ops;
281    krb5_error_code err;
282
283    *id = NULL;
284
285    err = krb5int_cc_getops(context, type, &ops);
286    if (err)
287	return err;
288
289    return ops->gen_new(context, id);
290}
291
292/*
293 * cc_typecursor
294 *
295 * Note: to avoid copying the typelist at cursor creation time, among
296 * other things, we assume that the only additions ever occur to the
297 * typelist.
298 */
299krb5_error_code
300krb5int_cc_typecursor_new(krb5_context context, krb5_cc_typecursor *t)
301{
302    krb5_error_code err = 0;
303    krb5_cc_typecursor n = NULL;
304
305    *t = NULL;
306    n = malloc(sizeof(*n));
307    if (n == NULL)
308	return ENOMEM;
309
310    err = k5_mutex_lock(&cc_typelist_lock);
311    if (err)
312	goto errout;
313    n->tptr = cc_typehead;
314    err = k5_mutex_unlock(&cc_typelist_lock);
315    if (err)
316	goto errout;
317
318    *t = n;
319errout:
320    if (err)
321	free(n);
322    return err;
323}
324
325krb5_error_code
326krb5int_cc_typecursor_next(
327    krb5_context context,
328    krb5_cc_typecursor t,
329    const krb5_cc_ops **ops)
330{
331    krb5_error_code err = 0;
332
333    *ops = NULL;
334    if (t->tptr == NULL)
335	return 0;
336
337    err = k5_mutex_lock(&cc_typelist_lock);
338    if (err)
339	goto errout;
340    *ops = t->tptr->ops;
341    t->tptr = t->tptr->next;
342    err = k5_mutex_unlock(&cc_typelist_lock);
343    if (err)
344	goto errout;
345
346errout:
347    return err;
348}
349
350krb5_error_code
351krb5int_cc_typecursor_free(krb5_context context, krb5_cc_typecursor *t)
352{
353    free(*t);
354    *t = NULL;
355    return 0;
356}
357