xref: /freebsd/crypto/heimdal/lib/krb5/cache.c (revision 1669d8afc64812c8d2d1d147ae1fd42ff441e1b1)
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 
36 RCSID("$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 
44 krb5_error_code
45 krb5_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 
85 static krb5_error_code
86 allocate_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 
113 krb5_error_code
114 krb5_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 
143 krb5_error_code
144 krb5_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 
164 const char*
165 krb5_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 
175 const char*
176 krb5_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 
186 const krb5_cc_ops *
187 krb5_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 
196 krb5_error_code
197 krb5_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 
227 const char*
228 krb5_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 
241 krb5_error_code
242 krb5_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 
257 krb5_error_code
258 krb5_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 
271 krb5_error_code
272 krb5_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 
287 krb5_error_code
288 krb5_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 
302 krb5_error_code
303 krb5_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 
316 krb5_error_code
317 krb5_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 
342 krb5_error_code
343 krb5_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 
356 krb5_error_code
357 krb5_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 
370 krb5_error_code
371 krb5_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 
383 krb5_error_code
384 krb5_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 
395 krb5_error_code
396 krb5_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 
414 krb5_error_code
415 krb5_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 
426 krb5_error_code
427 krb5_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 
462 krb5_error_code
463 krb5_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