r8302: import mini HEIMDAL into the tree
[samba.git] / source4 / heimdal / lib / krb5 / cache.c
1 /*
2  * Copyright (c) 1997-2005 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.71 2005/06/16 20:19:57 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 KRB5_LIB_FUNCTION
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 KRB5_LIB_FUNCTION
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 KRB5_LIB_FUNCTION
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  * Generates a new unique ccache of `type` in `id'. If `type' is NULL,
162  * the library chooses the default credential cache type. The supplied
163  * `hint' (that can be NULL) is a string that the credential cache
164  * type can use to base the name of the credential on, this is to make
165  * its easier for the user to differentiate the credentials.
166  *
167  *  Returns 0 or an error code.
168  */
169
170 krb5_error_code KRB5_LIB_FUNCTION
171 krb5_cc_new_unique(krb5_context context, const char *type, 
172                    const char *hint, krb5_ccache *id)
173 {
174     const krb5_cc_ops *ops;
175
176     if (type == NULL)
177         type = "FILE";
178
179     ops = krb5_cc_get_prefix_ops(context, type);
180     if (ops == NULL) {
181         krb5_set_error_string(context, "Credential cache type %s is unknown",
182                               type);
183         return KRB5_CC_UNKNOWN_TYPE;
184     }
185
186     return krb5_cc_gen_new(context, ops, id);
187 }
188
189 /*
190  * Return the name of the ccache `id'
191  */
192
193 const char* KRB5_LIB_FUNCTION
194 krb5_cc_get_name(krb5_context context,
195                  krb5_ccache id)
196 {
197     return id->ops->get_name(context, id);
198 }
199
200 /*
201  * Return the type of the ccache `id'.
202  */
203
204 const char* KRB5_LIB_FUNCTION
205 krb5_cc_get_type(krb5_context context,
206                  krb5_ccache id)
207 {
208     return id->ops->prefix;
209 }
210
211 /*
212  * Return krb5_cc_ops of a the ccache `id'.
213  */
214
215 const krb5_cc_ops *
216 krb5_cc_get_ops(krb5_context context, krb5_ccache id)
217 {
218     return id->ops;
219 }
220
221 /*
222  * Expand variables in `str' into `res'
223  */
224
225 krb5_error_code
226 _krb5_expand_default_cc_name(krb5_context context, const char *str, char **res)
227 {
228     size_t tlen, len = 0;
229     char *tmp, *tmp2, *append;
230
231     *res = NULL;
232
233     while (str && *str) {
234         tmp = strstr(str, "%{");
235         if (tmp && tmp != str) {
236             append = malloc((tmp - str) + 1);
237             if (append) {
238                 memcpy(append, str, tmp - str);
239                 append[tmp - str] = '\0';
240             }
241             str = tmp;
242         } else if (tmp) {
243             tmp2 = strchr(tmp, '}');
244             if (tmp2 == NULL) {
245                 free(*res);
246                 *res = NULL;
247                 krb5_set_error_string(context, "variable missing }");
248                 return KRB5_CONFIG_BADFORMAT;
249             }
250             if (strncasecmp(tmp, "%{uid}", 6) == 0)
251                 asprintf(&append, "%u", (unsigned)getuid());
252             else if (strncasecmp(tmp, "%{null}", 7) == 0)
253                 append = strdup("");
254             else {
255                 free(*res);
256                 *res = NULL;
257                 krb5_set_error_string(context, 
258                                       "expand default cache unknown "
259                                       "variable \"%.*s\"",
260                                       (int)(tmp2 - tmp) - 2, tmp + 2);
261                 return KRB5_CONFIG_BADFORMAT;
262             }
263             str = tmp2 + 1;
264         } else {
265             append = strdup(str);
266             str = NULL;
267         }
268         if (append == NULL) {
269             free(*res);
270             res = NULL;
271             krb5_set_error_string(context, "malloc - out of memory");
272             return ENOMEM;
273         }
274         
275         tlen = strlen(append);
276         tmp = realloc(*res, len + tlen + 1);
277         if (tmp == NULL) {
278             free(*res);
279             *res = NULL;
280             krb5_set_error_string(context, "malloc - out of memory");
281             return ENOMEM;
282         }
283         *res = tmp;
284         memcpy(*res + len, append, tlen + 1);
285         len = len + tlen;
286         free(append);
287     }    
288     return 0;
289 }
290
291 /*
292  * Set the default cc name for `context' to `name'.
293  */
294
295 krb5_error_code KRB5_LIB_FUNCTION
296 krb5_cc_set_default_name(krb5_context context, const char *name)
297 {
298     krb5_error_code ret = 0;
299     char *p;
300
301     if (name == NULL) {
302         const char *e = NULL;
303
304         if(!issuid()) {
305             e = getenv("KRB5CCNAME");
306             if (e)
307                 p = strdup(e);
308         }
309         if (e == NULL) {
310             e = krb5_config_get_string(context, NULL, "libdefaults",
311                                        "default_cc_name", NULL);
312             if (e) {
313                 ret = _krb5_expand_default_cc_name(context, e, &p);
314                 if (ret)
315                     return ret;
316             }
317         }
318         if (e == NULL)
319             asprintf(&p,"FILE:/tmp/krb5cc_%u", (unsigned)getuid());
320     } else
321         p = strdup(name);
322
323     if (p == NULL) {
324         krb5_set_error_string(context, "malloc - out of memory");
325         return ENOMEM;
326     }
327
328     if (context->default_cc_name)
329         free(context->default_cc_name);
330
331     context->default_cc_name = p;
332
333     return ret;
334 }
335
336 /*
337  * Return a pointer to a context static string containing the default
338  * ccache name.
339  */
340
341 const char* KRB5_LIB_FUNCTION
342 krb5_cc_default_name(krb5_context context)
343 {
344     if (context->default_cc_name == NULL)
345         krb5_cc_set_default_name(context, NULL);
346
347     return context->default_cc_name;
348 }
349
350 /*
351  * Open the default ccache in `id'.
352  * Return 0 or an error code.
353  */
354
355 krb5_error_code KRB5_LIB_FUNCTION
356 krb5_cc_default(krb5_context context,
357                 krb5_ccache *id)
358 {
359     const char *p = krb5_cc_default_name(context);
360
361     if (p == NULL) {
362         krb5_set_error_string(context, "malloc - out of memory");
363         return ENOMEM;
364     }
365     return krb5_cc_resolve(context, p, id);
366 }
367
368 /*
369  * Create a new ccache in `id' for `primary_principal'.
370  * Return 0 or an error code.
371  */
372
373 krb5_error_code KRB5_LIB_FUNCTION
374 krb5_cc_initialize(krb5_context context,
375                    krb5_ccache id,
376                    krb5_principal primary_principal)
377 {
378     return id->ops->init(context, id, primary_principal);
379 }
380
381
382 /*
383  * Remove the ccache `id'.
384  * Return 0 or an error code.
385  */
386
387 krb5_error_code KRB5_LIB_FUNCTION
388 krb5_cc_destroy(krb5_context context,
389                 krb5_ccache id)
390 {
391     krb5_error_code ret;
392
393     ret = id->ops->destroy(context, id);
394     krb5_cc_close (context, id);
395     return ret;
396 }
397
398 /*
399  * Stop using the ccache `id' and free the related resources.
400  * Return 0 or an error code.
401  */
402
403 krb5_error_code KRB5_LIB_FUNCTION
404 krb5_cc_close(krb5_context context,
405               krb5_ccache id)
406 {
407     krb5_error_code ret;
408     ret = id->ops->close(context, id);
409     free(id);
410     return ret;
411 }
412
413 /*
414  * Store `creds' in the ccache `id'.
415  * Return 0 or an error code.
416  */
417
418 krb5_error_code KRB5_LIB_FUNCTION
419 krb5_cc_store_cred(krb5_context context,
420                    krb5_ccache id,
421                    krb5_creds *creds)
422 {
423     return id->ops->store(context, id, creds);
424 }
425
426 /*
427  * Retrieve the credential identified by `mcreds' (and `whichfields')
428  * from `id' in `creds'.
429  * Return 0 or an error code.
430  */
431
432 krb5_error_code KRB5_LIB_FUNCTION
433 krb5_cc_retrieve_cred(krb5_context context,
434                       krb5_ccache id,
435                       krb5_flags whichfields,
436                       const krb5_creds *mcreds,
437                       krb5_creds *creds)
438 {
439     krb5_error_code ret;
440     krb5_cc_cursor cursor;
441
442     if (id->ops->retrieve != NULL) {
443         return id->ops->retrieve(context, id, whichfields,
444                                  mcreds, creds);
445     }
446
447     krb5_cc_start_seq_get(context, id, &cursor);
448     while((ret = krb5_cc_next_cred(context, id, &cursor, creds)) == 0){
449         if(krb5_compare_creds(context, whichfields, mcreds, creds)){
450             ret = 0;
451             break;
452         }
453         krb5_free_cred_contents (context, creds);
454     }
455     krb5_cc_end_seq_get(context, id, &cursor);
456     return ret;
457 }
458
459 /*
460  * Return the principal of `id' in `principal'.
461  * Return 0 or an error code.
462  */
463
464 krb5_error_code KRB5_LIB_FUNCTION
465 krb5_cc_get_principal(krb5_context context,
466                       krb5_ccache id,
467                       krb5_principal *principal)
468 {
469     return id->ops->get_princ(context, id, principal);
470 }
471
472 /*
473  * Start iterating over `id', `cursor' is initialized to the
474  * beginning.
475  * Return 0 or an error code.
476  */
477
478 krb5_error_code KRB5_LIB_FUNCTION
479 krb5_cc_start_seq_get (krb5_context context,
480                        const krb5_ccache id,
481                        krb5_cc_cursor *cursor)
482 {
483     return id->ops->get_first(context, id, cursor);
484 }
485
486 /*
487  * Retrieve the next cred pointed to by (`id', `cursor') in `creds'
488  * and advance `cursor'.
489  * Return 0 or an error code.
490  */
491
492 krb5_error_code KRB5_LIB_FUNCTION
493 krb5_cc_next_cred (krb5_context context,
494                    const krb5_ccache id,
495                    krb5_cc_cursor *cursor,
496                    krb5_creds *creds)
497 {
498     return id->ops->get_next(context, id, cursor, creds);
499 }
500
501 /* like krb5_cc_next_cred, but allow for selective retrieval */
502
503 krb5_error_code KRB5_LIB_FUNCTION
504 krb5_cc_next_cred_match(krb5_context context,
505                         const krb5_ccache id,
506                         krb5_cc_cursor * cursor,
507                         krb5_creds * creds,
508                         krb5_flags whichfields,
509                         const krb5_creds * mcreds)
510 {
511     krb5_error_code ret;
512     while (1) {
513         ret = krb5_cc_next_cred(context, id, cursor, creds);
514         if (ret)
515             return ret;
516         if (mcreds == NULL || krb5_compare_creds(context, whichfields, mcreds, creds))
517             return 0;
518         krb5_free_cred_contents(context, creds);
519     }
520 }
521
522 /*
523  * Destroy the cursor `cursor'.
524  */
525
526 krb5_error_code KRB5_LIB_FUNCTION
527 krb5_cc_end_seq_get (krb5_context context,
528                      const krb5_ccache id,
529                      krb5_cc_cursor *cursor)
530 {
531     return id->ops->end_get(context, id, cursor);
532 }
533
534 /*
535  * Remove the credential identified by `cred', `which' from `id'.
536  */
537
538 krb5_error_code KRB5_LIB_FUNCTION
539 krb5_cc_remove_cred(krb5_context context,
540                     krb5_ccache id,
541                     krb5_flags which,
542                     krb5_creds *cred)
543 {
544     if(id->ops->remove_cred == NULL) {
545         krb5_set_error_string(context,
546                               "ccache %s does not support remove_cred",
547                               id->ops->prefix);
548         return EACCES; /* XXX */
549     }
550     return (*id->ops->remove_cred)(context, id, which, cred);
551 }
552
553 /*
554  * Set the flags of `id' to `flags'.
555  */
556
557 krb5_error_code KRB5_LIB_FUNCTION
558 krb5_cc_set_flags(krb5_context context,
559                   krb5_ccache id,
560                   krb5_flags flags)
561 {
562     return id->ops->set_flags(context, id, flags);
563 }
564                     
565 /*
566  * Copy the contents of `from' to `to'.
567  */
568
569 krb5_error_code KRB5_LIB_FUNCTION
570 krb5_cc_copy_cache_match(krb5_context context,
571                          const krb5_ccache from,
572                          krb5_ccache to,
573                          krb5_flags whichfields,
574                          const krb5_creds * mcreds,
575                          unsigned int *matched)
576 {
577     krb5_error_code ret;
578     krb5_cc_cursor cursor;
579     krb5_creds cred;
580     krb5_principal princ;
581
582     ret = krb5_cc_get_principal(context, from, &princ);
583     if (ret)
584         return ret;
585     ret = krb5_cc_initialize(context, to, princ);
586     if (ret) {
587         krb5_free_principal(context, princ);
588         return ret;
589     }
590     ret = krb5_cc_start_seq_get(context, from, &cursor);
591     if (ret) {
592         krb5_free_principal(context, princ);
593         return ret;
594     }
595     if (matched)
596         *matched = 0;
597     while (ret == 0 &&
598            krb5_cc_next_cred_match(context, from, &cursor, &cred,
599                                    whichfields, mcreds) == 0) {
600         if (matched)
601             (*matched)++;
602         ret = krb5_cc_store_cred(context, to, &cred);
603         krb5_free_cred_contents(context, &cred);
604     }
605     krb5_cc_end_seq_get(context, from, &cursor);
606     krb5_free_principal(context, princ);
607     return ret;
608 }
609
610 krb5_error_code KRB5_LIB_FUNCTION
611 krb5_cc_copy_cache(krb5_context context,
612                    const krb5_ccache from,
613                    krb5_ccache to)
614 {
615     return krb5_cc_copy_cache_match(context, from, to, 0, NULL, NULL);
616 }
617
618 /*
619  * Return the version of `id'.
620  */
621
622 krb5_error_code KRB5_LIB_FUNCTION
623 krb5_cc_get_version(krb5_context context,
624                     const krb5_ccache id)
625 {
626     if(id->ops->get_version)
627         return id->ops->get_version(context, id);
628     else
629         return 0;
630 }
631
632 /*
633  * Clear `mcreds' so it can be used with krb5_cc_retrieve_cred
634  */
635
636 void KRB5_LIB_FUNCTION
637 krb5_cc_clear_mcred(krb5_creds *mcred)
638 {
639     memset(mcred, 0, sizeof(*mcred));
640 }
641
642 /*
643  * Get the cc ops that is registered in `context' to handle the
644  * `prefix'. Returns NULL if ops not found.
645  */
646
647 const krb5_cc_ops *
648 krb5_cc_get_prefix_ops(krb5_context context, const char *prefix)
649 {
650     int i;
651
652     for(i = 0; i < context->num_cc_ops && context->cc_ops[i].prefix; i++) {
653         if(strcmp(context->cc_ops[i].prefix, prefix) == 0)
654             return &context->cc_ops[i];
655     }
656     return NULL;
657 }