Merge from vendor branch HEIMDAL:
[dragonfly.git] / crypto / heimdal-0.6.3 / lib / krb5 / cache.c
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 }