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