cache.c revision 102644
1/*
2 * Copyright (c) 1997-2002 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
36RCSID("$Id: cache.c,v 1.49 2002/05/29 16:08:23 joda Exp $");
37
38/*
39 * Add a new ccache type with operations `ops', overwriting any
40 * existing one if `override'.
41 * Return an error code or 0.
42 */
43
44krb5_error_code
45krb5_cc_register(krb5_context context,
46		 const krb5_cc_ops *ops,
47		 krb5_boolean override)
48{
49    int i;
50
51    for(i = 0; i < context->num_cc_ops && context->cc_ops[i].prefix; i++) {
52	if(strcmp(context->cc_ops[i].prefix, ops->prefix) == 0) {
53	    if(!override) {
54		krb5_set_error_string(context,
55				      "ccache type %s already exists",
56				      ops->prefix);
57		return KRB5_CC_TYPE_EXISTS;
58	    }
59	    break;
60	}
61    }
62    if(i == context->num_cc_ops) {
63	krb5_cc_ops *o = realloc(context->cc_ops,
64				 (context->num_cc_ops + 1) *
65				 sizeof(*context->cc_ops));
66	if(o == NULL) {
67	    krb5_set_error_string(context, "malloc: out of memory");
68	    return KRB5_CC_NOMEM;
69	}
70	context->num_cc_ops++;
71	context->cc_ops = o;
72	memset(context->cc_ops + i, 0,
73	       (context->num_cc_ops - i) * sizeof(*context->cc_ops));
74    }
75    memcpy(&context->cc_ops[i], ops, sizeof(context->cc_ops[i]));
76    return 0;
77}
78
79/*
80 * Allocate memory for a new ccache in `id' with operations `ops'
81 * and name `residual'.
82 * Return 0 or an error code.
83 */
84
85static krb5_error_code
86allocate_ccache (krb5_context context,
87		 const krb5_cc_ops *ops,
88		 const char *residual,
89		 krb5_ccache *id)
90{
91    krb5_error_code ret;
92    krb5_ccache p;
93
94    p = malloc(sizeof(*p));
95    if(p == NULL) {
96	krb5_set_error_string(context, "malloc: out of memory");
97	return KRB5_CC_NOMEM;
98    }
99    p->ops = ops;
100    *id = p;
101    ret = p->ops->resolve(context, id, residual);
102    if(ret)
103	free(p);
104    return ret;
105}
106
107/*
108 * Find and allocate a ccache in `id' from the specification in `residual'.
109 * If the ccache name doesn't contain any colon, interpret it as a file name.
110 * Return 0 or an error code.
111 */
112
113krb5_error_code
114krb5_cc_resolve(krb5_context context,
115		const char *name,
116		krb5_ccache *id)
117{
118    int i;
119
120    for(i = 0; i < context->num_cc_ops && context->cc_ops[i].prefix; i++) {
121	size_t prefix_len = strlen(context->cc_ops[i].prefix);
122
123	if(strncmp(context->cc_ops[i].prefix, name, prefix_len) == 0
124	   && name[prefix_len] == ':') {
125	    return allocate_ccache (context, &context->cc_ops[i],
126				    name + prefix_len + 1,
127				    id);
128	}
129    }
130    if (strchr (name, ':') == NULL)
131	return allocate_ccache (context, &krb5_fcc_ops, name, id);
132    else {
133	krb5_set_error_string(context, "unknown ccache type %s", name);
134	return KRB5_CC_UNKNOWN_TYPE;
135    }
136}
137
138/*
139 * Generate a new ccache of type `ops' in `id'.
140 * Return 0 or an error code.
141 */
142
143krb5_error_code
144krb5_cc_gen_new(krb5_context context,
145		const krb5_cc_ops *ops,
146		krb5_ccache *id)
147{
148    krb5_ccache p;
149
150    p = malloc (sizeof(*p));
151    if (p == NULL) {
152	krb5_set_error_string(context, "malloc: out of memory");
153	return KRB5_CC_NOMEM;
154    }
155    p->ops = ops;
156    *id = p;
157    return p->ops->gen_new(context, id);
158}
159
160/*
161 * Return the name of the ccache `id'
162 */
163
164const char*
165krb5_cc_get_name(krb5_context context,
166		 krb5_ccache id)
167{
168    return id->ops->get_name(context, id);
169}
170
171/*
172 * Return the type of the ccache `id'.
173 */
174
175const char*
176krb5_cc_get_type(krb5_context context,
177		 krb5_ccache id)
178{
179    return id->ops->prefix;
180}
181
182/*
183 * Return a pointer to a static string containing the default ccache name.
184 */
185
186const char*
187krb5_cc_default_name(krb5_context context)
188{
189    static char name[1024];
190    char *p;
191
192    p = getenv("KRB5CCNAME");
193    if(p)
194	strlcpy (name, p, sizeof(name));
195    else
196	snprintf(name,
197		 sizeof(name),
198		 "FILE:/tmp/krb5cc_%u",
199		 (unsigned)getuid());
200    return name;
201}
202
203/*
204 * Open the default ccache in `id'.
205 * Return 0 or an error code.
206 */
207
208krb5_error_code
209krb5_cc_default(krb5_context context,
210		krb5_ccache *id)
211{
212    return krb5_cc_resolve(context,
213			   krb5_cc_default_name(context),
214			   id);
215}
216
217/*
218 * Create a new ccache in `id' for `primary_principal'.
219 * Return 0 or an error code.
220 */
221
222krb5_error_code
223krb5_cc_initialize(krb5_context context,
224		   krb5_ccache id,
225		   krb5_principal primary_principal)
226{
227    return id->ops->init(context, id, primary_principal);
228}
229
230
231/*
232 * Remove the ccache `id'.
233 * Return 0 or an error code.
234 */
235
236krb5_error_code
237krb5_cc_destroy(krb5_context context,
238		krb5_ccache id)
239{
240    krb5_error_code ret;
241
242    ret = id->ops->destroy(context, id);
243    krb5_cc_close (context, id);
244    return ret;
245}
246
247/*
248 * Stop using the ccache `id' and free the related resources.
249 * Return 0 or an error code.
250 */
251
252krb5_error_code
253krb5_cc_close(krb5_context context,
254	      krb5_ccache id)
255{
256    krb5_error_code ret;
257    ret = id->ops->close(context, id);
258    free(id);
259    return ret;
260}
261
262/*
263 * Store `creds' in the ccache `id'.
264 * Return 0 or an error code.
265 */
266
267krb5_error_code
268krb5_cc_store_cred(krb5_context context,
269		   krb5_ccache id,
270		   krb5_creds *creds)
271{
272    return id->ops->store(context, id, creds);
273}
274
275/*
276 * Retrieve the credential identified by `mcreds' (and `whichfields')
277 * from `id' in `creds'.
278 * Return 0 or an error code.
279 */
280
281krb5_error_code
282krb5_cc_retrieve_cred(krb5_context context,
283		      krb5_ccache id,
284		      krb5_flags whichfields,
285		      const krb5_creds *mcreds,
286		      krb5_creds *creds)
287{
288    krb5_error_code ret;
289    krb5_cc_cursor cursor;
290    krb5_cc_start_seq_get(context, id, &cursor);
291    while((ret = krb5_cc_next_cred(context, id, &cursor, creds)) == 0){
292	if(krb5_compare_creds(context, whichfields, mcreds, creds)){
293	    ret = 0;
294	    break;
295	}
296	krb5_free_creds_contents (context, creds);
297    }
298    krb5_cc_end_seq_get(context, id, &cursor);
299    return ret;
300}
301
302/*
303 * Return the principal of `id' in `principal'.
304 * Return 0 or an error code.
305 */
306
307krb5_error_code
308krb5_cc_get_principal(krb5_context context,
309		      krb5_ccache id,
310		      krb5_principal *principal)
311{
312    return id->ops->get_princ(context, id, principal);
313}
314
315/*
316 * Start iterating over `id', `cursor' is initialized to the
317 * beginning.
318 * Return 0 or an error code.
319 */
320
321krb5_error_code
322krb5_cc_start_seq_get (krb5_context context,
323		       const krb5_ccache id,
324		       krb5_cc_cursor *cursor)
325{
326    return id->ops->get_first(context, id, cursor);
327}
328
329/*
330 * Retrieve the next cred pointed to by (`id', `cursor') in `creds'
331 * and advance `cursor'.
332 * Return 0 or an error code.
333 */
334
335krb5_error_code
336krb5_cc_next_cred (krb5_context context,
337		   const krb5_ccache id,
338		   krb5_cc_cursor *cursor,
339		   krb5_creds *creds)
340{
341    return id->ops->get_next(context, id, cursor, creds);
342}
343
344/*
345 * Destroy the cursor `cursor'.
346 */
347
348krb5_error_code
349krb5_cc_end_seq_get (krb5_context context,
350		     const krb5_ccache id,
351		     krb5_cc_cursor *cursor)
352{
353    return id->ops->end_get(context, id, cursor);
354}
355
356/*
357 * Remove the credential identified by `cred', `which' from `id'.
358 */
359
360krb5_error_code
361krb5_cc_remove_cred(krb5_context context,
362		    krb5_ccache id,
363		    krb5_flags which,
364		    krb5_creds *cred)
365{
366    if(id->ops->remove_cred == NULL) {
367	krb5_set_error_string(context,
368			      "ccache %s does not support remove_cred",
369			      id->ops->prefix);
370	return EACCES; /* XXX */
371    }
372    return (*id->ops->remove_cred)(context, id, which, cred);
373}
374
375/*
376 * Set the flags of `id' to `flags'.
377 */
378
379krb5_error_code
380krb5_cc_set_flags(krb5_context context,
381		  krb5_ccache id,
382		  krb5_flags flags)
383{
384    return id->ops->set_flags(context, id, flags);
385}
386
387/*
388 * Copy the contents of `from' to `to'.
389 */
390
391krb5_error_code
392krb5_cc_copy_cache(krb5_context context,
393		   const krb5_ccache from,
394		   krb5_ccache to)
395{
396    krb5_error_code ret;
397    krb5_cc_cursor cursor;
398    krb5_creds cred;
399    krb5_principal princ;
400
401    ret = krb5_cc_get_principal(context, from, &princ);
402    if(ret)
403	return ret;
404    ret = krb5_cc_initialize(context, to, princ);
405    if(ret){
406	krb5_free_principal(context, princ);
407	return ret;
408    }
409    ret = krb5_cc_start_seq_get(context, from, &cursor);
410    if(ret){
411	krb5_free_principal(context, princ);
412	return ret;
413    }
414    while(ret == 0 && krb5_cc_next_cred(context, from, &cursor, &cred) == 0){
415	ret = krb5_cc_store_cred(context, to, &cred);
416	krb5_free_creds_contents (context, &cred);
417    }
418    krb5_cc_end_seq_get(context, from, &cursor);
419    krb5_free_principal(context, princ);
420    return ret;
421}
422
423/*
424 * Return the version of `id'.
425 */
426
427krb5_error_code
428krb5_cc_get_version(krb5_context context,
429		    const krb5_ccache id)
430{
431    if(id->ops->get_version)
432	return id->ops->get_version(context, id);
433    else
434	return 0;
435}
436