cache.c revision 120945
1/*
2 * Copyright (c) 1997-2003 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.52 2003/03/16 18:23:59 lha 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 krb5_cc_ops of a the ccache `id'.
184 */
185
186const krb5_cc_ops *
187krb5_cc_get_ops(krb5_context context, krb5_ccache id)
188{
189    return id->ops;
190}
191
192/*
193 * Set the default cc name for `context' to `name'.
194 */
195
196krb5_error_code
197krb5_cc_set_default_name(krb5_context context, const char *name)
198{
199    krb5_error_code ret = 0;
200    char *p;
201
202    if (name == NULL) {
203	char *e;
204	e = getenv("KRB5CCNAME");
205	if (e)
206	    p = strdup(e);
207	else
208	    asprintf(&p,"FILE:/tmp/krb5cc_%u", (unsigned)getuid());
209    } else
210	p = strdup(name);
211
212    if (p == NULL)
213	return ENOMEM;
214
215    if (context->default_cc_name)
216	free(context->default_cc_name);
217
218    context->default_cc_name = p;
219
220    return ret;
221}
222
223/*
224 * Return a pointer to a context static string containing the default ccache name.
225 */
226
227const char*
228krb5_cc_default_name(krb5_context context)
229{
230    if (context->default_cc_name == NULL)
231	krb5_cc_set_default_name(context, NULL);
232
233    return context->default_cc_name;
234}
235
236/*
237 * Open the default ccache in `id'.
238 * Return 0 or an error code.
239 */
240
241krb5_error_code
242krb5_cc_default(krb5_context context,
243		krb5_ccache *id)
244{
245    const char *p = krb5_cc_default_name(context);
246
247    if (p == NULL)
248	return ENOMEM;
249    return krb5_cc_resolve(context, p, id);
250}
251
252/*
253 * Create a new ccache in `id' for `primary_principal'.
254 * Return 0 or an error code.
255 */
256
257krb5_error_code
258krb5_cc_initialize(krb5_context context,
259		   krb5_ccache id,
260		   krb5_principal primary_principal)
261{
262    return id->ops->init(context, id, primary_principal);
263}
264
265
266/*
267 * Remove the ccache `id'.
268 * Return 0 or an error code.
269 */
270
271krb5_error_code
272krb5_cc_destroy(krb5_context context,
273		krb5_ccache id)
274{
275    krb5_error_code ret;
276
277    ret = id->ops->destroy(context, id);
278    krb5_cc_close (context, id);
279    return ret;
280}
281
282/*
283 * Stop using the ccache `id' and free the related resources.
284 * Return 0 or an error code.
285 */
286
287krb5_error_code
288krb5_cc_close(krb5_context context,
289	      krb5_ccache id)
290{
291    krb5_error_code ret;
292    ret = id->ops->close(context, id);
293    free(id);
294    return ret;
295}
296
297/*
298 * Store `creds' in the ccache `id'.
299 * Return 0 or an error code.
300 */
301
302krb5_error_code
303krb5_cc_store_cred(krb5_context context,
304		   krb5_ccache id,
305		   krb5_creds *creds)
306{
307    return id->ops->store(context, id, creds);
308}
309
310/*
311 * Retrieve the credential identified by `mcreds' (and `whichfields')
312 * from `id' in `creds'.
313 * Return 0 or an error code.
314 */
315
316krb5_error_code
317krb5_cc_retrieve_cred(krb5_context context,
318		      krb5_ccache id,
319		      krb5_flags whichfields,
320		      const krb5_creds *mcreds,
321		      krb5_creds *creds)
322{
323    krb5_error_code ret;
324    krb5_cc_cursor cursor;
325    krb5_cc_start_seq_get(context, id, &cursor);
326    while((ret = krb5_cc_next_cred(context, id, &cursor, creds)) == 0){
327	if(krb5_compare_creds(context, whichfields, mcreds, creds)){
328	    ret = 0;
329	    break;
330	}
331	krb5_free_creds_contents (context, creds);
332    }
333    krb5_cc_end_seq_get(context, id, &cursor);
334    return ret;
335}
336
337/*
338 * Return the principal of `id' in `principal'.
339 * Return 0 or an error code.
340 */
341
342krb5_error_code
343krb5_cc_get_principal(krb5_context context,
344		      krb5_ccache id,
345		      krb5_principal *principal)
346{
347    return id->ops->get_princ(context, id, principal);
348}
349
350/*
351 * Start iterating over `id', `cursor' is initialized to the
352 * beginning.
353 * Return 0 or an error code.
354 */
355
356krb5_error_code
357krb5_cc_start_seq_get (krb5_context context,
358		       const krb5_ccache id,
359		       krb5_cc_cursor *cursor)
360{
361    return id->ops->get_first(context, id, cursor);
362}
363
364/*
365 * Retrieve the next cred pointed to by (`id', `cursor') in `creds'
366 * and advance `cursor'.
367 * Return 0 or an error code.
368 */
369
370krb5_error_code
371krb5_cc_next_cred (krb5_context context,
372		   const krb5_ccache id,
373		   krb5_cc_cursor *cursor,
374		   krb5_creds *creds)
375{
376    return id->ops->get_next(context, id, cursor, creds);
377}
378
379/*
380 * Destroy the cursor `cursor'.
381 */
382
383krb5_error_code
384krb5_cc_end_seq_get (krb5_context context,
385		     const krb5_ccache id,
386		     krb5_cc_cursor *cursor)
387{
388    return id->ops->end_get(context, id, cursor);
389}
390
391/*
392 * Remove the credential identified by `cred', `which' from `id'.
393 */
394
395krb5_error_code
396krb5_cc_remove_cred(krb5_context context,
397		    krb5_ccache id,
398		    krb5_flags which,
399		    krb5_creds *cred)
400{
401    if(id->ops->remove_cred == NULL) {
402	krb5_set_error_string(context,
403			      "ccache %s does not support remove_cred",
404			      id->ops->prefix);
405	return EACCES; /* XXX */
406    }
407    return (*id->ops->remove_cred)(context, id, which, cred);
408}
409
410/*
411 * Set the flags of `id' to `flags'.
412 */
413
414krb5_error_code
415krb5_cc_set_flags(krb5_context context,
416		  krb5_ccache id,
417		  krb5_flags flags)
418{
419    return id->ops->set_flags(context, id, flags);
420}
421
422/*
423 * Copy the contents of `from' to `to'.
424 */
425
426krb5_error_code
427krb5_cc_copy_cache(krb5_context context,
428		   const krb5_ccache from,
429		   krb5_ccache to)
430{
431    krb5_error_code ret;
432    krb5_cc_cursor cursor;
433    krb5_creds cred;
434    krb5_principal princ;
435
436    ret = krb5_cc_get_principal(context, from, &princ);
437    if(ret)
438	return ret;
439    ret = krb5_cc_initialize(context, to, princ);
440    if(ret){
441	krb5_free_principal(context, princ);
442	return ret;
443    }
444    ret = krb5_cc_start_seq_get(context, from, &cursor);
445    if(ret){
446	krb5_free_principal(context, princ);
447	return ret;
448    }
449    while(ret == 0 && krb5_cc_next_cred(context, from, &cursor, &cred) == 0){
450	ret = krb5_cc_store_cred(context, to, &cred);
451	krb5_free_creds_contents (context, &cred);
452    }
453    krb5_cc_end_seq_get(context, from, &cursor);
454    krb5_free_principal(context, princ);
455    return ret;
456}
457
458/*
459 * Return the version of `id'.
460 */
461
462krb5_error_code
463krb5_cc_get_version(krb5_context context,
464		    const krb5_ccache id)
465{
466    if(id->ops->get_version)
467	return id->ops->get_version(context, id);
468    else
469	return 0;
470}
471