xref: /freebsd/crypto/heimdal/lib/krb5/cache.c (revision 4b2eaea43fec8e8792be611dea204071a10b655a)
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 
36 RCSID("$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 
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 a pointer to a static string containing the default ccache name.
184  */
185 
186 const char*
187 krb5_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 
208 krb5_error_code
209 krb5_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 
222 krb5_error_code
223 krb5_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 
236 krb5_error_code
237 krb5_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 
252 krb5_error_code
253 krb5_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 
267 krb5_error_code
268 krb5_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 
281 krb5_error_code
282 krb5_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 
307 krb5_error_code
308 krb5_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 
321 krb5_error_code
322 krb5_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 
335 krb5_error_code
336 krb5_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 
348 krb5_error_code
349 krb5_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 
360 krb5_error_code
361 krb5_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 
379 krb5_error_code
380 krb5_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 
391 krb5_error_code
392 krb5_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 
427 krb5_error_code
428 krb5_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