Add cache iteration funcations. Add internal allocation function for
[abartlet/lorikeet-heimdal.git/.git] / 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$");
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 the memory for a `id' and the that function table to
81  * `ops'. Returns 0 or and error code.
82  */
83
84 krb5_error_code
85 _krb5_cc_allocate(krb5_context context, 
86                   const krb5_cc_ops *ops,
87                   krb5_ccache *id)
88 {
89     krb5_ccache p;
90
91     p = malloc (sizeof(*p));
92     if(p == NULL) {
93         krb5_set_error_string(context, "malloc: out of memory");
94         return KRB5_CC_NOMEM;
95     }
96     p->ops = ops;
97     *id = p;
98
99     return 0;
100 }
101
102 /*
103  * Allocate memory for a new ccache in `id' with operations `ops'
104  * and name `residual'.
105  * Return 0 or an error code.
106  */
107
108 static krb5_error_code
109 allocate_ccache (krb5_context context,
110                  const krb5_cc_ops *ops,
111                  const char *residual,
112                  krb5_ccache *id)
113 {
114     krb5_error_code ret;
115
116     ret = _krb5_cc_allocate(context, ops, id);
117     if (ret)
118         return ret;
119     ret = (*id)->ops->resolve(context, id, residual);
120     if(ret)
121         free(*id);
122     return ret;
123 }
124
125 /*
126  * Find and allocate a ccache in `id' from the specification in `residual'.
127  * If the ccache name doesn't contain any colon, interpret it as a file name.
128  * Return 0 or an error code.
129  */
130
131 krb5_error_code KRB5_LIB_FUNCTION
132 krb5_cc_resolve(krb5_context context,
133                 const char *name,
134                 krb5_ccache *id)
135 {
136     int i;
137
138     for(i = 0; i < context->num_cc_ops && context->cc_ops[i].prefix; i++) {
139         size_t prefix_len = strlen(context->cc_ops[i].prefix);
140
141         if(strncmp(context->cc_ops[i].prefix, name, prefix_len) == 0
142            && name[prefix_len] == ':') {
143             return allocate_ccache (context, &context->cc_ops[i],
144                                     name + prefix_len + 1,
145                                     id);
146         }
147     }
148     if (strchr (name, ':') == NULL)
149         return allocate_ccache (context, &krb5_fcc_ops, name, id);
150     else {
151         krb5_set_error_string(context, "unknown ccache type %s", name);
152         return KRB5_CC_UNKNOWN_TYPE;
153     }
154 }
155
156 /*
157  * Generate a new ccache of type `ops' in `id'.
158  * Return 0 or an error code.
159  */
160
161 krb5_error_code KRB5_LIB_FUNCTION
162 krb5_cc_gen_new(krb5_context context,
163                 const krb5_cc_ops *ops,
164                 krb5_ccache *id)
165 {
166     krb5_error_code ret;
167
168     ret = _krb5_cc_allocate(context, ops, id);
169     if (ret)
170         return ret;
171     return (*id)->ops->gen_new(context, id);
172 }
173
174 /*
175  * Generates a new unique ccache of `type` in `id'. If `type' is NULL,
176  * the library chooses the default credential cache type. The supplied
177  * `hint' (that can be NULL) is a string that the credential cache
178  * type can use to base the name of the credential on, this is to make
179  * its easier for the user to differentiate the credentials.
180  *
181  *  Returns 0 or an error code.
182  */
183
184 krb5_error_code KRB5_LIB_FUNCTION
185 krb5_cc_new_unique(krb5_context context, const char *type, 
186                    const char *hint, krb5_ccache *id)
187 {
188     const krb5_cc_ops *ops;
189
190     if (type == NULL)
191         type = "FILE";
192
193     ops = krb5_cc_get_prefix_ops(context, type);
194     if (ops == NULL) {
195         krb5_set_error_string(context, "Credential cache type %s is unknown",
196                               type);
197         return KRB5_CC_UNKNOWN_TYPE;
198     }
199
200     return krb5_cc_gen_new(context, ops, id);
201 }
202
203 /*
204  * Return the name of the ccache `id'
205  */
206
207 const char* KRB5_LIB_FUNCTION
208 krb5_cc_get_name(krb5_context context,
209                  krb5_ccache id)
210 {
211     return id->ops->get_name(context, id);
212 }
213
214 /*
215  * Return the type of the ccache `id'.
216  */
217
218 const char* KRB5_LIB_FUNCTION
219 krb5_cc_get_type(krb5_context context,
220                  krb5_ccache id)
221 {
222     return id->ops->prefix;
223 }
224
225 /*
226  * Return krb5_cc_ops of a the ccache `id'.
227  */
228
229 const krb5_cc_ops *
230 krb5_cc_get_ops(krb5_context context, krb5_ccache id)
231 {
232     return id->ops;
233 }
234
235 /*
236  * Expand variables in `str' into `res'
237  */
238
239 krb5_error_code
240 _krb5_expand_default_cc_name(krb5_context context, const char *str, char **res)
241 {
242     size_t tlen, len = 0;
243     char *tmp, *tmp2, *append;
244
245     *res = NULL;
246
247     while (str && *str) {
248         tmp = strstr(str, "%{");
249         if (tmp && tmp != str) {
250             append = malloc((tmp - str) + 1);
251             if (append) {
252                 memcpy(append, str, tmp - str);
253                 append[tmp - str] = '\0';
254             }
255             str = tmp;
256         } else if (tmp) {
257             tmp2 = strchr(tmp, '}');
258             if (tmp2 == NULL) {
259                 free(*res);
260                 *res = NULL;
261                 krb5_set_error_string(context, "variable missing }");
262                 return KRB5_CONFIG_BADFORMAT;
263             }
264             if (strncasecmp(tmp, "%{uid}", 6) == 0)
265                 asprintf(&append, "%u", (unsigned)getuid());
266             else if (strncasecmp(tmp, "%{null}", 7) == 0)
267                 append = strdup("");
268             else {
269                 free(*res);
270                 *res = NULL;
271                 krb5_set_error_string(context, 
272                                       "expand default cache unknown "
273                                       "variable \"%.*s\"",
274                                       (int)(tmp2 - tmp) - 2, tmp + 2);
275                 return KRB5_CONFIG_BADFORMAT;
276             }
277             str = tmp2 + 1;
278         } else {
279             append = strdup(str);
280             str = NULL;
281         }
282         if (append == NULL) {
283             free(*res);
284             res = NULL;
285             krb5_set_error_string(context, "malloc - out of memory");
286             return ENOMEM;
287         }
288         
289         tlen = strlen(append);
290         tmp = realloc(*res, len + tlen + 1);
291         if (tmp == NULL) {
292             free(*res);
293             *res = NULL;
294             krb5_set_error_string(context, "malloc - out of memory");
295             return ENOMEM;
296         }
297         *res = tmp;
298         memcpy(*res + len, append, tlen + 1);
299         len = len + tlen;
300         free(append);
301     }    
302     return 0;
303 }
304
305 /*
306  * Set the default cc name for `context' to `name'.
307  */
308
309 krb5_error_code KRB5_LIB_FUNCTION
310 krb5_cc_set_default_name(krb5_context context, const char *name)
311 {
312     krb5_error_code ret = 0;
313     char *p;
314
315     if (name == NULL) {
316         const char *e = NULL;
317
318         if(!issuid()) {
319             e = getenv("KRB5CCNAME");
320             if (e)
321                 p = strdup(e);
322         }
323         if (e == NULL) {
324             e = krb5_config_get_string(context, NULL, "libdefaults",
325                                        "default_cc_name", NULL);
326             if (e) {
327                 ret = _krb5_expand_default_cc_name(context, e, &p);
328                 if (ret)
329                     return ret;
330             }
331         }
332         if (e == NULL)
333             asprintf(&p,"FILE:/tmp/krb5cc_%u", (unsigned)getuid());
334     } else
335         p = strdup(name);
336
337     if (p == NULL) {
338         krb5_set_error_string(context, "malloc - out of memory");
339         return ENOMEM;
340     }
341
342     if (context->default_cc_name)
343         free(context->default_cc_name);
344
345     context->default_cc_name = p;
346
347     return ret;
348 }
349
350 /*
351  * Return a pointer to a context static string containing the default
352  * ccache name.
353  */
354
355 const char* KRB5_LIB_FUNCTION
356 krb5_cc_default_name(krb5_context context)
357 {
358     if (context->default_cc_name == NULL)
359         krb5_cc_set_default_name(context, NULL);
360
361     return context->default_cc_name;
362 }
363
364 /*
365  * Open the default ccache in `id'.
366  * Return 0 or an error code.
367  */
368
369 krb5_error_code KRB5_LIB_FUNCTION
370 krb5_cc_default(krb5_context context,
371                 krb5_ccache *id)
372 {
373     const char *p = krb5_cc_default_name(context);
374
375     if (p == NULL) {
376         krb5_set_error_string(context, "malloc - out of memory");
377         return ENOMEM;
378     }
379     return krb5_cc_resolve(context, p, id);
380 }
381
382 /*
383  * Create a new ccache in `id' for `primary_principal'.
384  * Return 0 or an error code.
385  */
386
387 krb5_error_code KRB5_LIB_FUNCTION
388 krb5_cc_initialize(krb5_context context,
389                    krb5_ccache id,
390                    krb5_principal primary_principal)
391 {
392     return id->ops->init(context, id, primary_principal);
393 }
394
395
396 /*
397  * Remove the ccache `id'.
398  * Return 0 or an error code.
399  */
400
401 krb5_error_code KRB5_LIB_FUNCTION
402 krb5_cc_destroy(krb5_context context,
403                 krb5_ccache id)
404 {
405     krb5_error_code ret;
406
407     ret = id->ops->destroy(context, id);
408     krb5_cc_close (context, id);
409     return ret;
410 }
411
412 /*
413  * Stop using the ccache `id' and free the related resources.
414  * Return 0 or an error code.
415  */
416
417 krb5_error_code KRB5_LIB_FUNCTION
418 krb5_cc_close(krb5_context context,
419               krb5_ccache id)
420 {
421     krb5_error_code ret;
422     ret = id->ops->close(context, id);
423     free(id);
424     return ret;
425 }
426
427 /*
428  * Store `creds' in the ccache `id'.
429  * Return 0 or an error code.
430  */
431
432 krb5_error_code KRB5_LIB_FUNCTION
433 krb5_cc_store_cred(krb5_context context,
434                    krb5_ccache id,
435                    krb5_creds *creds)
436 {
437     return id->ops->store(context, id, creds);
438 }
439
440 /*
441  * Retrieve the credential identified by `mcreds' (and `whichfields')
442  * from `id' in `creds'.
443  * Return 0 or an error code.
444  */
445
446 krb5_error_code KRB5_LIB_FUNCTION
447 krb5_cc_retrieve_cred(krb5_context context,
448                       krb5_ccache id,
449                       krb5_flags whichfields,
450                       const krb5_creds *mcreds,
451                       krb5_creds *creds)
452 {
453     krb5_error_code ret;
454     krb5_cc_cursor cursor;
455
456     if (id->ops->retrieve != NULL) {
457         return id->ops->retrieve(context, id, whichfields,
458                                  mcreds, creds);
459     }
460
461     krb5_cc_start_seq_get(context, id, &cursor);
462     while((ret = krb5_cc_next_cred(context, id, &cursor, creds)) == 0){
463         if(krb5_compare_creds(context, whichfields, mcreds, creds)){
464             ret = 0;
465             break;
466         }
467         krb5_free_cred_contents (context, creds);
468     }
469     krb5_cc_end_seq_get(context, id, &cursor);
470     return ret;
471 }
472
473 /*
474  * Return the principal of `id' in `principal'.
475  * Return 0 or an error code.
476  */
477
478 krb5_error_code KRB5_LIB_FUNCTION
479 krb5_cc_get_principal(krb5_context context,
480                       krb5_ccache id,
481                       krb5_principal *principal)
482 {
483     return id->ops->get_princ(context, id, principal);
484 }
485
486 /*
487  * Start iterating over `id', `cursor' is initialized to the
488  * beginning.
489  * Return 0 or an error code.
490  */
491
492 krb5_error_code KRB5_LIB_FUNCTION
493 krb5_cc_start_seq_get (krb5_context context,
494                        const krb5_ccache id,
495                        krb5_cc_cursor *cursor)
496 {
497     return id->ops->get_first(context, id, cursor);
498 }
499
500 /*
501  * Retrieve the next cred pointed to by (`id', `cursor') in `creds'
502  * and advance `cursor'.
503  * Return 0 or an error code.
504  */
505
506 krb5_error_code KRB5_LIB_FUNCTION
507 krb5_cc_next_cred (krb5_context context,
508                    const krb5_ccache id,
509                    krb5_cc_cursor *cursor,
510                    krb5_creds *creds)
511 {
512     return id->ops->get_next(context, id, cursor, creds);
513 }
514
515 /* like krb5_cc_next_cred, but allow for selective retrieval */
516
517 krb5_error_code KRB5_LIB_FUNCTION
518 krb5_cc_next_cred_match(krb5_context context,
519                         const krb5_ccache id,
520                         krb5_cc_cursor * cursor,
521                         krb5_creds * creds,
522                         krb5_flags whichfields,
523                         const krb5_creds * mcreds)
524 {
525     krb5_error_code ret;
526     while (1) {
527         ret = krb5_cc_next_cred(context, id, cursor, creds);
528         if (ret)
529             return ret;
530         if (mcreds == NULL || krb5_compare_creds(context, whichfields, mcreds, creds))
531             return 0;
532         krb5_free_cred_contents(context, creds);
533     }
534 }
535
536 /*
537  * Destroy the cursor `cursor'.
538  */
539
540 krb5_error_code KRB5_LIB_FUNCTION
541 krb5_cc_end_seq_get (krb5_context context,
542                      const krb5_ccache id,
543                      krb5_cc_cursor *cursor)
544 {
545     return id->ops->end_get(context, id, cursor);
546 }
547
548 /*
549  * Remove the credential identified by `cred', `which' from `id'.
550  */
551
552 krb5_error_code KRB5_LIB_FUNCTION
553 krb5_cc_remove_cred(krb5_context context,
554                     krb5_ccache id,
555                     krb5_flags which,
556                     krb5_creds *cred)
557 {
558     if(id->ops->remove_cred == NULL) {
559         krb5_set_error_string(context,
560                               "ccache %s does not support remove_cred",
561                               id->ops->prefix);
562         return EACCES; /* XXX */
563     }
564     return (*id->ops->remove_cred)(context, id, which, cred);
565 }
566
567 /*
568  * Set the flags of `id' to `flags'.
569  */
570
571 krb5_error_code KRB5_LIB_FUNCTION
572 krb5_cc_set_flags(krb5_context context,
573                   krb5_ccache id,
574                   krb5_flags flags)
575 {
576     return id->ops->set_flags(context, id, flags);
577 }
578                     
579 /*
580  * Copy the contents of `from' to `to'.
581  */
582
583 krb5_error_code KRB5_LIB_FUNCTION
584 krb5_cc_copy_cache_match(krb5_context context,
585                          const krb5_ccache from,
586                          krb5_ccache to,
587                          krb5_flags whichfields,
588                          const krb5_creds * mcreds,
589                          unsigned int *matched)
590 {
591     krb5_error_code ret;
592     krb5_cc_cursor cursor;
593     krb5_creds cred;
594     krb5_principal princ;
595
596     ret = krb5_cc_get_principal(context, from, &princ);
597     if (ret)
598         return ret;
599     ret = krb5_cc_initialize(context, to, princ);
600     if (ret) {
601         krb5_free_principal(context, princ);
602         return ret;
603     }
604     ret = krb5_cc_start_seq_get(context, from, &cursor);
605     if (ret) {
606         krb5_free_principal(context, princ);
607         return ret;
608     }
609     if (matched)
610         *matched = 0;
611     while (ret == 0 &&
612            krb5_cc_next_cred_match(context, from, &cursor, &cred,
613                                    whichfields, mcreds) == 0) {
614         if (matched)
615             (*matched)++;
616         ret = krb5_cc_store_cred(context, to, &cred);
617         krb5_free_cred_contents(context, &cred);
618     }
619     krb5_cc_end_seq_get(context, from, &cursor);
620     krb5_free_principal(context, princ);
621     return ret;
622 }
623
624 krb5_error_code KRB5_LIB_FUNCTION
625 krb5_cc_copy_cache(krb5_context context,
626                    const krb5_ccache from,
627                    krb5_ccache to)
628 {
629     return krb5_cc_copy_cache_match(context, from, to, 0, NULL, NULL);
630 }
631
632 /*
633  * Return the version of `id'.
634  */
635
636 krb5_error_code KRB5_LIB_FUNCTION
637 krb5_cc_get_version(krb5_context context,
638                     const krb5_ccache id)
639 {
640     if(id->ops->get_version)
641         return id->ops->get_version(context, id);
642     else
643         return 0;
644 }
645
646 /*
647  * Clear `mcreds' so it can be used with krb5_cc_retrieve_cred
648  */
649
650 void KRB5_LIB_FUNCTION
651 krb5_cc_clear_mcred(krb5_creds *mcred)
652 {
653     memset(mcred, 0, sizeof(*mcred));
654 }
655
656 /*
657  * Get the cc ops that is registered in `context' to handle the
658  * `prefix'. `prefix' can be a complete credential cache name or a
659  * prefix, the function will only use part up to the first colon (:)
660  * if there is one.  Returns NULL if ops not found.
661  */
662
663 const krb5_cc_ops *
664 krb5_cc_get_prefix_ops(krb5_context context, const char *prefix)
665 {
666     char *p, *p1;
667     int i;
668     
669     p = strdup(prefix);
670     if (p == NULL) {
671         krb5_set_error_string(context, "malloc - out of memory");
672         return NULL;
673     }
674     p1 = strchr(p, ':');
675     if (p1)
676         *p1 = '\0';
677
678     for(i = 0; i < context->num_cc_ops && context->cc_ops[i].prefix; i++) {
679         if(strcmp(context->cc_ops[i].prefix, p) == 0) {
680             free(p);
681             return &context->cc_ops[i];
682         }
683     }
684     free(p);
685     return NULL;
686 }
687
688 struct krb5_cc_cache_cursor_data {
689     const krb5_cc_ops *ops;
690     krb5_cc_cursor cursor;
691 };
692
693 /*
694  * Start iterating over all caches of `type'. If `type' is NULL, the
695  * default type is * used. `cursor' is initialized to the beginning.
696  * Return 0 or an error code.
697  */
698
699 krb5_error_code KRB5_LIB_FUNCTION
700 krb5_cc_cache_get_first (krb5_context context,
701                          const char *type,
702                          krb5_cc_cache_cursor *cursor)
703 {
704     const krb5_cc_ops *ops;
705     krb5_error_code ret;
706
707     if (type == NULL)
708         type = krb5_cc_default_name(context);
709
710     ops = krb5_cc_get_prefix_ops(context, type);
711     if (ops == NULL) {
712         krb5_set_error_string(context, "Unknown type \"%s\" when iterating "
713                               "trying to iterate the credential caches", type);
714         return KRB5_CC_UNKNOWN_TYPE;
715     }
716
717     if (ops->get_cache_first == NULL) {
718         krb5_set_error_string(context, "Credential cache type %s doesn't support "
719                               "iterations over caches", ops->prefix);
720         return KRB5_CC_NOSUPP;
721     }
722
723     *cursor = calloc(1, sizeof(**cursor));
724     if (*cursor == NULL) {
725         krb5_set_error_string(context, "malloc - out of memory");
726         return ENOMEM;
727     }
728
729     (*cursor)->ops = ops;
730
731     ret = ops->get_cache_first(context, &(*cursor)->cursor);
732     if (ret) {
733         free(*cursor);
734         *cursor = NULL;
735     }
736     return ret;
737 }
738
739 /*
740  * Retrieve the next cache pointed to by (`cursor') in `id'
741  * and advance `cursor'.
742  * Return 0 or an error code.
743  */
744
745 krb5_error_code KRB5_LIB_FUNCTION
746 krb5_cc_cache_next (krb5_context context,
747                    krb5_cc_cache_cursor cursor,
748                    krb5_ccache *id)
749 {
750     return cursor->ops->get_cache_next(context, cursor->cursor, id);
751 }
752
753 /*
754  * Destroy the cursor `cursor'.
755  */
756
757 krb5_error_code KRB5_LIB_FUNCTION
758 krb5_cc_cache_end_seq_get (krb5_context context,
759                            krb5_cc_cache_cursor cursor)
760 {
761     krb5_error_code ret;
762     ret = cursor->ops->end_cache_get(context, cursor->cursor);
763     cursor->ops = NULL;
764     free(cursor);
765     return ret;
766 }
767