Merge branch 'v4-0-test' of ssh://git.samba.org/data/git/samba into v4-0-test
[tprouty/samba.git] / source / heimdal / lib / krb5 / cache.c
1 /*
2  * Copyright (c) 1997 - 2007 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  *
42  * @param context a Keberos context
43  * @param ops type of plugin symbol
44  * @param override flag to select if the registration is to overide
45  * an existing ops with the same name.
46  *
47  * @return Return an error code or 0.
48  *
49  * @ingroup krb5_ccache
50  */
51
52 krb5_error_code KRB5_LIB_FUNCTION
53 krb5_cc_register(krb5_context context, 
54                  const krb5_cc_ops *ops, 
55                  krb5_boolean override)
56 {
57     int i;
58
59     for(i = 0; i < context->num_cc_ops && context->cc_ops[i].prefix; i++) {
60         if(strcmp(context->cc_ops[i].prefix, ops->prefix) == 0) {
61             if(!override) {
62                 krb5_set_error_message(context, 
63                                        KRB5_CC_TYPE_EXISTS,
64                                        "ccache type %s already exists",
65                                        ops->prefix);
66                 return KRB5_CC_TYPE_EXISTS;
67             }
68             break;
69         }
70     }
71     if(i == context->num_cc_ops) {
72         krb5_cc_ops *o = realloc(context->cc_ops,
73                                  (context->num_cc_ops + 1) *
74                                  sizeof(*context->cc_ops));
75         if(o == NULL) {
76             krb5_set_error_message(context, KRB5_CC_NOMEM, 
77                                    "malloc: out of memory");
78             return KRB5_CC_NOMEM;
79         }
80         context->num_cc_ops++;
81         context->cc_ops = o;
82         memset(context->cc_ops + i, 0, 
83                (context->num_cc_ops - i) * sizeof(*context->cc_ops));
84     }
85     memcpy(&context->cc_ops[i], ops, sizeof(context->cc_ops[i]));
86     return 0;
87 }
88
89 /*
90  * Allocate the memory for a `id' and the that function table to
91  * `ops'. Returns 0 or and error code.
92  */
93
94 krb5_error_code
95 _krb5_cc_allocate(krb5_context context, 
96                   const krb5_cc_ops *ops,
97                   krb5_ccache *id)
98 {
99     krb5_ccache p;
100
101     p = malloc (sizeof(*p));
102     if(p == NULL) {
103         krb5_set_error_message(context, KRB5_CC_NOMEM, "malloc: out of memory");
104         return KRB5_CC_NOMEM;
105     }
106     p->ops = ops;
107     *id = p;
108
109     return 0;
110 }
111
112 /*
113  * Allocate memory for a new ccache in `id' with operations `ops'
114  * and name `residual'. Return 0 or an error code.
115  */
116
117 static krb5_error_code
118 allocate_ccache (krb5_context context,
119                  const krb5_cc_ops *ops,
120                  const char *residual,
121                  krb5_ccache *id)
122 {
123     krb5_error_code ret;
124
125     ret = _krb5_cc_allocate(context, ops, id);
126     if (ret)
127         return ret;
128     ret = (*id)->ops->resolve(context, id, residual);
129     if(ret)
130         free(*id);
131     return ret;
132 }
133
134 /**
135  * Find and allocate a ccache in `id' from the specification in `residual'.
136  * If the ccache name doesn't contain any colon, interpret it as a file name.
137  *
138  * @param context a Keberos context.
139  * @param name string name of a credential cache.
140  * @param id return pointer to a found credential cache.
141  *
142  * @return Return 0 or an error code. In case of an error, id is set
143  * to NULL.
144  *
145  * @ingroup krb5_ccache
146  */
147
148
149 krb5_error_code KRB5_LIB_FUNCTION
150 krb5_cc_resolve(krb5_context context,
151                 const char *name,
152                 krb5_ccache *id)
153 {
154     int i;
155
156     *id = NULL;
157
158     for(i = 0; i < context->num_cc_ops && context->cc_ops[i].prefix; i++) {
159         size_t prefix_len = strlen(context->cc_ops[i].prefix);
160
161         if(strncmp(context->cc_ops[i].prefix, name, prefix_len) == 0
162            && name[prefix_len] == ':') {
163             return allocate_ccache (context, &context->cc_ops[i],
164                                     name + prefix_len + 1,
165                                     id);
166         }
167     }
168     if (strchr (name, ':') == NULL)
169         return allocate_ccache (context, &krb5_fcc_ops, name, id);
170     else {
171         krb5_set_error_message(context, KRB5_CC_UNKNOWN_TYPE,
172                                "unknown ccache type %s", name);
173         return KRB5_CC_UNKNOWN_TYPE;
174     }
175 }
176
177 /**
178  * Generate a new ccache of type `ops' in `id'.
179  *
180  * @return Return 0 or an error code.
181  *
182  * @ingroup krb5_ccache
183  */
184
185
186 krb5_error_code KRB5_LIB_FUNCTION
187 krb5_cc_gen_new(krb5_context context,
188                 const krb5_cc_ops *ops,
189                 krb5_ccache *id)
190 {
191     return krb5_cc_new_unique(context, ops->prefix, NULL, id);
192 }
193
194 /**
195  * Generates a new unique ccache of `type` in `id'. If `type' is NULL,
196  * the library chooses the default credential cache type. The supplied
197  * `hint' (that can be NULL) is a string that the credential cache
198  * type can use to base the name of the credential on, this is to make
199  * it easier for the user to differentiate the credentials.
200  *
201  * @return Returns 0 or an error code.
202  *
203  * @ingroup krb5_ccache
204  */
205
206 krb5_error_code KRB5_LIB_FUNCTION
207 krb5_cc_new_unique(krb5_context context, const char *type, 
208                    const char *hint, krb5_ccache *id)
209 {
210     const krb5_cc_ops *ops;
211     krb5_error_code ret;
212
213     ops = krb5_cc_get_prefix_ops(context, type);
214     if (ops == NULL) {
215         krb5_set_error_message(context, KRB5_CC_UNKNOWN_TYPE,
216                               "Credential cache type %s is unknown", type);
217         return KRB5_CC_UNKNOWN_TYPE;
218     }
219
220     ret = _krb5_cc_allocate(context, ops, id);
221     if (ret)
222         return ret;
223     return (*id)->ops->gen_new(context, id);
224 }
225
226 /**
227  * Return the name of the ccache `id'
228  *
229  * @ingroup krb5_ccache
230  */
231
232
233 const char* KRB5_LIB_FUNCTION
234 krb5_cc_get_name(krb5_context context,
235                  krb5_ccache id)
236 {
237     return id->ops->get_name(context, id);
238 }
239
240 /**
241  * Return the type of the ccache `id'.
242  *
243  * @ingroup krb5_ccache
244  */
245
246
247 const char* KRB5_LIB_FUNCTION
248 krb5_cc_get_type(krb5_context context,
249                  krb5_ccache id)
250 {
251     return id->ops->prefix;
252 }
253
254 /**
255  * Return the complete resolvable name the ccache `id' in `str´.
256  * `str` should be freed with free(3).
257  * Returns 0 or an error (and then *str is set to NULL).
258  *
259  * @ingroup krb5_ccache
260  */
261
262
263 krb5_error_code KRB5_LIB_FUNCTION
264 krb5_cc_get_full_name(krb5_context context,
265                       krb5_ccache id,
266                       char **str)
267 {
268     const char *type, *name;
269
270     *str = NULL;
271
272     type = krb5_cc_get_type(context, id);
273     if (type == NULL) {
274         krb5_set_error_message(context, KRB5_CC_UNKNOWN_TYPE,
275                                "cache have no name of type");
276         return KRB5_CC_UNKNOWN_TYPE;
277     }
278
279     name = krb5_cc_get_name(context, id);
280     if (name == NULL) {
281         krb5_set_error_message(context, KRB5_CC_BADNAME,
282                                "cache of type %s have no name", type);
283         return KRB5_CC_BADNAME;
284     }
285     
286     if (asprintf(str, "%s:%s", type, name) == -1) {
287         krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
288         *str = NULL;
289         return ENOMEM;
290     }
291     return 0;
292 }
293
294 /**
295  * Return krb5_cc_ops of a the ccache `id'.
296  *
297  * @ingroup krb5_ccache
298  */
299
300
301 const krb5_cc_ops *
302 krb5_cc_get_ops(krb5_context context, krb5_ccache id)
303 {
304     return id->ops;
305 }
306
307 /*
308  * Expand variables in `str' into `res'
309  */
310
311 krb5_error_code
312 _krb5_expand_default_cc_name(krb5_context context, const char *str, char **res)
313 {
314     size_t tlen, len = 0;
315     char *tmp, *tmp2, *append;
316
317     *res = NULL;
318
319     while (str && *str) {
320         tmp = strstr(str, "%{");
321         if (tmp && tmp != str) {
322             append = malloc((tmp - str) + 1);
323             if (append) {
324                 memcpy(append, str, tmp - str);
325                 append[tmp - str] = '\0';
326             }
327             str = tmp;
328         } else if (tmp) {
329             tmp2 = strchr(tmp, '}');
330             if (tmp2 == NULL) {
331                 free(*res);
332                 *res = NULL;
333                 krb5_set_error_message(context, KRB5_CONFIG_BADFORMAT,
334                                        "variable missing }");
335                 return KRB5_CONFIG_BADFORMAT;
336             }
337             if (strncasecmp(tmp, "%{uid}", 6) == 0)
338                 asprintf(&append, "%u", (unsigned)getuid());
339             else if (strncasecmp(tmp, "%{null}", 7) == 0)
340                 append = strdup("");
341             else {
342                 free(*res);
343                 *res = NULL;
344                 krb5_set_error_message(context, 
345                                        KRB5_CONFIG_BADFORMAT,
346                                        "expand default cache unknown "
347                                        "variable \"%.*s\"",
348                                        (int)(tmp2 - tmp) - 2, tmp + 2);
349                 return KRB5_CONFIG_BADFORMAT;
350             }
351             str = tmp2 + 1;
352         } else {
353             append = strdup(str);
354             str = NULL;
355         }
356         if (append == NULL) {
357             free(*res);
358             *res = NULL;
359             krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
360             return ENOMEM;
361         }
362         
363         tlen = strlen(append);
364         tmp = realloc(*res, len + tlen + 1);
365         if (tmp == NULL) {
366             free(append);
367             free(*res);
368             *res = NULL;
369             krb5_set_error_message(context, ENOMEM, 
370                                    "malloc: out of memory");
371             return ENOMEM;
372         }
373         *res = tmp;
374         memcpy(*res + len, append, tlen + 1);
375         len = len + tlen;
376         free(append);
377     }    
378     return 0;
379 }
380
381 /*
382  * Return non-zero if envirnoment that will determine default krb5cc
383  * name has changed.
384  */
385
386 static int
387 environment_changed(krb5_context context)
388 {
389     const char *e;
390
391     /* if the cc name was set, don't change it */
392     if (context->default_cc_name_set)
393         return 0;
394
395     if(issuid())
396         return 0;
397
398     e = getenv("KRB5CCNAME");
399     if (e == NULL) {
400         if (context->default_cc_name_env) {
401             free(context->default_cc_name_env);
402             context->default_cc_name_env = NULL;
403             return 1;
404         }
405     } else {
406         if (context->default_cc_name_env == NULL)
407             return 1;
408         if (strcmp(e, context->default_cc_name_env) != 0)
409             return 1;
410     }
411     return 0;
412 }
413
414 /**
415  * Switch the default default credential cache for a specific
416  * credcache type (and name for some implementations).
417  *
418  * @return Returns 0 or an error code.
419  *
420  * @ingroup krb5_ccache
421  */
422
423 krb5_error_code
424 krb5_cc_switch(krb5_context context, krb5_ccache id)
425 {
426
427     if (id->ops->set_default == NULL)
428         return 0;
429
430     return (*id->ops->set_default)(context, id);
431 }
432
433 /**
434  * Set the default cc name for `context' to `name'.
435  *
436  * @ingroup krb5_ccache
437  */
438
439 krb5_error_code KRB5_LIB_FUNCTION
440 krb5_cc_set_default_name(krb5_context context, const char *name)
441 {
442     krb5_error_code ret = 0;
443     char *p;
444
445     if (name == NULL) {
446         const char *e = NULL;
447
448         if(!issuid()) {
449             e = getenv("KRB5CCNAME");
450             if (e) {
451                 p = strdup(e);
452                 if (context->default_cc_name_env)
453                     free(context->default_cc_name_env);
454                 context->default_cc_name_env = strdup(e);
455             }
456         }
457         if (e == NULL) {
458             e = krb5_config_get_string(context, NULL, "libdefaults",
459                                        "default_cc_name", NULL);
460             if (e) {
461                 ret = _krb5_expand_default_cc_name(context, e, &p);
462                 if (ret)
463                     return ret;
464             }
465             if (e == NULL) {
466                 const krb5_cc_ops *ops = KRB5_DEFAULT_CCTYPE;
467                 e = krb5_config_get_string(context, NULL, "libdefaults",
468                                            "default_cc_type", NULL);
469                 if (e) {
470                     ops = krb5_cc_get_prefix_ops(context, e);
471                     if (ops == NULL) {
472                         krb5_set_error_message(context,
473                                                KRB5_CC_UNKNOWN_TYPE,
474                                                "Credential cache type %s "
475                                               "is unknown", e);
476                         return KRB5_CC_UNKNOWN_TYPE;
477                     }
478                 }
479                 ret = (*ops->get_default_name)(context, &p);
480                 if (ret)
481                     return ret;
482             }
483         }
484         context->default_cc_name_set = 0;
485     } else {
486         p = strdup(name);
487         context->default_cc_name_set = 1;
488     }
489
490     if (p == NULL) {
491         krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
492         return ENOMEM;
493     }
494
495     if (context->default_cc_name)
496         free(context->default_cc_name);
497
498     context->default_cc_name = p;
499
500     return ret;
501 }
502
503 /**
504  * Return a pointer to a context static string containing the default
505  * ccache name.
506  *
507  * @return String to the default credential cache name.
508  *
509  * @ingroup krb5_ccache
510  */
511
512
513 const char* KRB5_LIB_FUNCTION
514 krb5_cc_default_name(krb5_context context)
515 {
516     if (context->default_cc_name == NULL || environment_changed(context))
517         krb5_cc_set_default_name(context, NULL);
518
519     return context->default_cc_name;
520 }
521
522 /**
523  * Open the default ccache in `id'.
524  *
525  * @return Return 0 or an error code.
526  *
527  * @ingroup krb5_ccache
528  */
529
530
531 krb5_error_code KRB5_LIB_FUNCTION
532 krb5_cc_default(krb5_context context,
533                 krb5_ccache *id)
534 {
535     const char *p = krb5_cc_default_name(context);
536
537     if (p == NULL) {
538         krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
539         return ENOMEM;
540     }
541     return krb5_cc_resolve(context, p, id);
542 }
543
544 /**
545  * Create a new ccache in `id' for `primary_principal'.
546  *
547  * @return Return 0 or an error code.
548  *
549  * @ingroup krb5_ccache
550  */
551
552
553 krb5_error_code KRB5_LIB_FUNCTION
554 krb5_cc_initialize(krb5_context context,
555                    krb5_ccache id,
556                    krb5_principal primary_principal)
557 {
558     return (*id->ops->init)(context, id, primary_principal);
559 }
560
561
562 /**
563  * Remove the ccache `id'.
564  *
565  * @return Return 0 or an error code.
566  *
567  * @ingroup krb5_ccache
568  */
569
570
571 krb5_error_code KRB5_LIB_FUNCTION
572 krb5_cc_destroy(krb5_context context,
573                 krb5_ccache id)
574 {
575     krb5_error_code ret;
576
577     ret = (*id->ops->destroy)(context, id);
578     krb5_cc_close (context, id);
579     return ret;
580 }
581
582 /**
583  * Stop using the ccache `id' and free the related resources.
584  *
585  * @return Return 0 or an error code.
586  *
587  * @ingroup krb5_ccache
588  */
589
590
591 krb5_error_code KRB5_LIB_FUNCTION
592 krb5_cc_close(krb5_context context,
593               krb5_ccache id)
594 {
595     krb5_error_code ret;
596     ret = (*id->ops->close)(context, id);
597     free(id);
598     return ret;
599 }
600
601 /**
602  * Store `creds' in the ccache `id'.
603  *
604  * @return Return 0 or an error code.
605  *
606  * @ingroup krb5_ccache
607  */
608
609
610 krb5_error_code KRB5_LIB_FUNCTION
611 krb5_cc_store_cred(krb5_context context,
612                    krb5_ccache id,
613                    krb5_creds *creds)
614 {
615     return (*id->ops->store)(context, id, creds);
616 }
617
618 /**
619  * Retrieve the credential identified by `mcreds' (and `whichfields')
620  * from `id' in `creds'. 'creds' must be free by the caller using
621  * krb5_free_cred_contents.
622  *
623  * @return Return 0 or an error code.
624  *
625  * @ingroup krb5_ccache
626  */
627
628
629 krb5_error_code KRB5_LIB_FUNCTION
630 krb5_cc_retrieve_cred(krb5_context context,
631                       krb5_ccache id,
632                       krb5_flags whichfields,
633                       const krb5_creds *mcreds,
634                       krb5_creds *creds)
635 {
636     krb5_error_code ret;
637     krb5_cc_cursor cursor;
638
639     if (id->ops->retrieve != NULL) {
640         return (*id->ops->retrieve)(context, id, whichfields,
641                                     mcreds, creds);
642     }
643
644     ret = krb5_cc_start_seq_get(context, id, &cursor);
645     if (ret)
646         return ret;
647     while((ret = krb5_cc_next_cred(context, id, &cursor, creds)) == 0){
648         if(krb5_compare_creds(context, whichfields, mcreds, creds)){
649             ret = 0;
650             break;
651         }
652         krb5_free_cred_contents (context, creds);
653     }
654     krb5_cc_end_seq_get(context, id, &cursor);
655     return ret;
656 }
657
658 /**
659  * Return the principal of `id' in `principal'.
660  *
661  * @return Return 0 or an error code.
662  *
663  * @ingroup krb5_ccache
664  */
665
666
667 krb5_error_code KRB5_LIB_FUNCTION
668 krb5_cc_get_principal(krb5_context context,
669                       krb5_ccache id,
670                       krb5_principal *principal)
671 {
672     return (*id->ops->get_princ)(context, id, principal);
673 }
674
675 /**
676  * Start iterating over `id', `cursor' is initialized to the
677  * beginning.
678  *
679  * @return Return 0 or an error code.
680  *
681  * @ingroup krb5_ccache
682  */
683
684
685 krb5_error_code KRB5_LIB_FUNCTION
686 krb5_cc_start_seq_get (krb5_context context,
687                        const krb5_ccache id,
688                        krb5_cc_cursor *cursor)
689 {
690     return (*id->ops->get_first)(context, id, cursor);
691 }
692
693 /**
694  * Retrieve the next cred pointed to by (`id', `cursor') in `creds'
695  * and advance `cursor'.
696  *
697  * @return Return 0 or an error code.
698  *
699  * @ingroup krb5_ccache
700  */
701
702
703 krb5_error_code KRB5_LIB_FUNCTION
704 krb5_cc_next_cred (krb5_context context,
705                    const krb5_ccache id,
706                    krb5_cc_cursor *cursor,
707                    krb5_creds *creds)
708 {
709     return (*id->ops->get_next)(context, id, cursor, creds);
710 }
711
712 /**
713  * Like krb5_cc_next_cred, but allow for selective retrieval
714  *
715  * @ingroup krb5_ccache
716  */
717
718
719 krb5_error_code KRB5_LIB_FUNCTION
720 krb5_cc_next_cred_match(krb5_context context,
721                         const krb5_ccache id,
722                         krb5_cc_cursor * cursor,
723                         krb5_creds * creds,
724                         krb5_flags whichfields,
725                         const krb5_creds * mcreds)
726 {
727     krb5_error_code ret;
728     while (1) {
729         ret = krb5_cc_next_cred(context, id, cursor, creds);
730         if (ret)
731             return ret;
732         if (mcreds == NULL || krb5_compare_creds(context, whichfields, mcreds, creds))
733             return 0;
734         krb5_free_cred_contents(context, creds);
735     }
736 }
737
738 /**
739  * Destroy the cursor `cursor'.
740  *
741  * @ingroup krb5_ccache
742  */
743
744
745 krb5_error_code KRB5_LIB_FUNCTION
746 krb5_cc_end_seq_get (krb5_context context,
747                      const krb5_ccache id,
748                      krb5_cc_cursor *cursor)
749 {
750     return (*id->ops->end_get)(context, id, cursor);
751 }
752
753 /**
754  * Remove the credential identified by `cred', `which' from `id'.
755  *
756  * @ingroup krb5_ccache
757  */
758
759
760 krb5_error_code KRB5_LIB_FUNCTION
761 krb5_cc_remove_cred(krb5_context context,
762                     krb5_ccache id,
763                     krb5_flags which,
764                     krb5_creds *cred)
765 {
766     if(id->ops->remove_cred == NULL) {
767         krb5_set_error_message(context,
768                                EACCES,
769                                "ccache %s does not support remove_cred",
770                                id->ops->prefix);
771         return EACCES; /* XXX */
772     }
773     return (*id->ops->remove_cred)(context, id, which, cred);
774 }
775
776 /**
777  * Set the flags of `id' to `flags'.
778  *
779  * @ingroup krb5_ccache
780  */
781
782
783 krb5_error_code KRB5_LIB_FUNCTION
784 krb5_cc_set_flags(krb5_context context,
785                   krb5_ccache id,
786                   krb5_flags flags)
787 {
788     return (*id->ops->set_flags)(context, id, flags);
789 }
790                     
791 /**
792  * Copy the contents of `from' to `to'.
793  *
794  * @ingroup krb5_ccache
795  */
796
797
798 krb5_error_code KRB5_LIB_FUNCTION
799 krb5_cc_copy_cache_match(krb5_context context,
800                          const krb5_ccache from,
801                          krb5_ccache to,
802                          krb5_flags whichfields,
803                          const krb5_creds * mcreds,
804                          unsigned int *matched)
805 {
806     krb5_error_code ret;
807     krb5_cc_cursor cursor;
808     krb5_creds cred;
809     krb5_principal princ;
810
811     ret = krb5_cc_get_principal(context, from, &princ);
812     if (ret)
813         return ret;
814     ret = krb5_cc_initialize(context, to, princ);
815     if (ret) {
816         krb5_free_principal(context, princ);
817         return ret;
818     }
819     ret = krb5_cc_start_seq_get(context, from, &cursor);
820     if (ret) {
821         krb5_free_principal(context, princ);
822         return ret;
823     }
824     if (matched)
825         *matched = 0;
826     while (ret == 0 &&
827            krb5_cc_next_cred_match(context, from, &cursor, &cred,
828                                    whichfields, mcreds) == 0) {
829         if (matched)
830             (*matched)++;
831         ret = krb5_cc_store_cred(context, to, &cred);
832         krb5_free_cred_contents(context, &cred);
833     }
834     krb5_cc_end_seq_get(context, from, &cursor);
835     krb5_free_principal(context, princ);
836     return ret;
837 }
838
839 /**
840  * Just like krb5_cc_copy_cache_match, but copy everything.
841  *
842  * @ingroup krb5_ccache
843  */
844
845
846 krb5_error_code KRB5_LIB_FUNCTION
847 krb5_cc_copy_cache(krb5_context context,
848                    const krb5_ccache from,
849                    krb5_ccache to)
850 {
851     return krb5_cc_copy_cache_match(context, from, to, 0, NULL, NULL);
852 }
853
854 /**
855  * Return the version of `id'.
856  *
857  * @ingroup krb5_ccache
858  */
859
860
861 krb5_error_code KRB5_LIB_FUNCTION
862 krb5_cc_get_version(krb5_context context,
863                     const krb5_ccache id)
864 {
865     if(id->ops->get_version)
866         return (*id->ops->get_version)(context, id);
867     else
868         return 0;
869 }
870
871 /**
872  * Clear `mcreds' so it can be used with krb5_cc_retrieve_cred
873  *
874  * @ingroup krb5_ccache
875  */
876
877
878 void KRB5_LIB_FUNCTION
879 krb5_cc_clear_mcred(krb5_creds *mcred)
880 {
881     memset(mcred, 0, sizeof(*mcred));
882 }
883
884 /**
885  * Get the cc ops that is registered in `context' to handle the
886  * prefix. prefix can be a complete credential cache name or a
887  * prefix, the function will only use part up to the first colon (:)
888  * if there is one. If prefix the argument is NULL, the default ccache
889  * implemtation is returned.
890  
891  * @return Returns NULL if ops not found.
892  *
893  * @ingroup krb5_ccache
894  */
895
896
897 const krb5_cc_ops *
898 krb5_cc_get_prefix_ops(krb5_context context, const char *prefix)
899 {
900     char *p, *p1;
901     int i;
902     
903     if (prefix == NULL)
904         return KRB5_DEFAULT_CCTYPE;
905     if (prefix[0] == '/')
906         return &krb5_fcc_ops;
907
908     p = strdup(prefix);
909     if (p == NULL) {
910         krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
911         return NULL;
912     }
913     p1 = strchr(p, ':');
914     if (p1)
915         *p1 = '\0';
916
917     for(i = 0; i < context->num_cc_ops && context->cc_ops[i].prefix; i++) {
918         if(strcmp(context->cc_ops[i].prefix, p) == 0) {
919             free(p);
920             return &context->cc_ops[i];
921         }
922     }
923     free(p);
924     return NULL;
925 }
926
927 struct krb5_cc_cache_cursor_data {
928     const krb5_cc_ops *ops;
929     krb5_cc_cursor cursor;
930 };
931
932 /**
933  * Start iterating over all caches of `type'. If `type' is NULL, the
934  * default type is * used. `cursor' is initialized to the beginning.
935  *
936  * @return Return 0 or an error code.
937  *
938  * @ingroup krb5_ccache
939  */
940
941
942 krb5_error_code KRB5_LIB_FUNCTION
943 krb5_cc_cache_get_first (krb5_context context,
944                          const char *type,
945                          krb5_cc_cache_cursor *cursor)
946 {
947     const krb5_cc_ops *ops;
948     krb5_error_code ret;
949
950     if (type == NULL)
951         type = krb5_cc_default_name(context);
952
953     ops = krb5_cc_get_prefix_ops(context, type);
954     if (ops == NULL) {
955         krb5_set_error_message(context, KRB5_CC_UNKNOWN_TYPE,
956                                "Unknown type \"%s\" when iterating "
957                                "trying to iterate the credential caches", type);
958         return KRB5_CC_UNKNOWN_TYPE;
959     }
960
961     if (ops->get_cache_first == NULL) {
962         krb5_set_error_message(context, KRB5_CC_NOSUPP,
963                                "Credential cache type %s doesn't support "
964                                "iterations over caches", ops->prefix);
965         return KRB5_CC_NOSUPP;
966     }
967
968     *cursor = calloc(1, sizeof(**cursor));
969     if (*cursor == NULL) {
970         krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
971         return ENOMEM;
972     }
973
974     (*cursor)->ops = ops;
975
976     ret = ops->get_cache_first(context, &(*cursor)->cursor);
977     if (ret) {
978         free(*cursor);
979         *cursor = NULL;
980     }
981     return ret;
982 }
983
984 /**
985  * Retrieve the next cache pointed to by (`cursor') in `id'
986  * and advance `cursor'.
987  *
988  * @return Return 0 or an error code.
989  *
990  * @ingroup krb5_ccache
991  */
992
993
994 krb5_error_code KRB5_LIB_FUNCTION
995 krb5_cc_cache_next (krb5_context context,
996                    krb5_cc_cache_cursor cursor,
997                    krb5_ccache *id)
998 {
999     return cursor->ops->get_cache_next(context, cursor->cursor, id);
1000 }
1001
1002 /**
1003  * Destroy the cursor `cursor'.
1004  *
1005  * @return Return 0 or an error code.
1006  *
1007  * @ingroup krb5_ccache
1008  */
1009
1010
1011 krb5_error_code KRB5_LIB_FUNCTION
1012 krb5_cc_cache_end_seq_get (krb5_context context,
1013                            krb5_cc_cache_cursor cursor)
1014 {
1015     krb5_error_code ret;
1016     ret = cursor->ops->end_cache_get(context, cursor->cursor);
1017     cursor->ops = NULL;
1018     free(cursor);
1019     return ret;
1020 }
1021
1022 /**
1023  * Search for a matching credential cache of type `type' that have the
1024  * `principal' as the default principal. If NULL is used for `type',
1025  * the default type is used. On success, `id' needs to be freed with
1026  * krb5_cc_close or krb5_cc_destroy.
1027  *
1028  * @return On failure, error code is returned and `id' is set to NULL.
1029  *
1030  * @ingroup krb5_ccache
1031  */
1032
1033
1034 krb5_error_code KRB5_LIB_FUNCTION
1035 krb5_cc_cache_match (krb5_context context,
1036                      krb5_principal client,
1037                      const char *type,
1038                      krb5_ccache *id)
1039 {
1040     krb5_cc_cache_cursor cursor;
1041     krb5_error_code ret;
1042     krb5_ccache cache = NULL;
1043
1044     *id = NULL;
1045
1046     ret = krb5_cc_cache_get_first (context, type, &cursor);
1047     if (ret)
1048         return ret;
1049
1050     while ((ret = krb5_cc_cache_next (context, cursor, &cache)) == 0) {
1051         krb5_principal principal;
1052
1053         ret = krb5_cc_get_principal(context, cache, &principal);
1054         if (ret == 0) {
1055             krb5_boolean match;
1056             
1057             match = krb5_principal_compare(context, principal, client);
1058             krb5_free_principal(context, principal);
1059             if (match)
1060                 break;
1061         }
1062
1063         krb5_cc_close(context, cache);
1064         cache = NULL;
1065     }
1066
1067     krb5_cc_cache_end_seq_get(context, cursor);
1068
1069     if (cache == NULL) {
1070         char *str;
1071
1072         krb5_unparse_name(context, client, &str);
1073
1074         krb5_set_error_message(context, KRB5_CC_NOTFOUND,
1075                                "Principal %s not found in a "
1076                                "credential cache", 
1077                                str ? str : "<out of memory>");
1078         if (str)
1079             free(str);
1080         return KRB5_CC_NOTFOUND;
1081     }
1082     *id = cache;
1083
1084     return 0;
1085 }
1086
1087 /**
1088  * Move the content from one credential cache to another. The
1089  * operation is an atomic switch. 
1090  *
1091  * @param context a Keberos context
1092  * @param from the credential cache to move the content from
1093  * @param to the credential cache to move the content to
1094
1095  * @return On sucess, from is freed. On failure, error code is
1096  * returned and from and to are both still allocated.
1097  *
1098  * @ingroup krb5_ccache
1099  */
1100
1101 krb5_error_code
1102 krb5_cc_move(krb5_context context, krb5_ccache from, krb5_ccache to)
1103 {
1104     krb5_error_code ret;
1105
1106     if (strcmp(from->ops->prefix, to->ops->prefix) != 0) {
1107         krb5_set_error_message(context, KRB5_CC_NOSUPP,
1108                                "Moving credentials between diffrent "
1109                                "types not yet supported");
1110         return KRB5_CC_NOSUPP;
1111     }
1112
1113     ret = (*to->ops->move)(context, from, to);
1114     if (ret == 0) {
1115         memset(from, 0, sizeof(*from));
1116         free(from);
1117     }
1118     return ret;
1119 }
1120
1121 #define KRB5_CONF_NAME "@krb5_ccache_conf_data"
1122
1123 static krb5_error_code
1124 build_conf_principals(krb5_context context, krb5_ccache id,
1125                       krb5_const_principal principal,
1126                       const char *name, krb5_creds *cred)
1127 {
1128     krb5_principal client;
1129     krb5_error_code ret;
1130     char *pname = NULL;
1131
1132     memset(cred, 0, sizeof(*cred));
1133
1134     ret = krb5_cc_get_principal(context, id, &client);
1135     if (ret)
1136         return ret;
1137        
1138     if (principal) {
1139         ret = krb5_unparse_name(context, principal, &pname);
1140         if (ret)
1141             return ret;
1142     }
1143
1144     ret = krb5_make_principal(context, &cred->server,
1145                               krb5_principal_get_realm(context, client),
1146                               KRB5_CONF_NAME, name, pname, NULL);
1147     free(pname);
1148     if (ret) {
1149         krb5_free_principal(context, client);
1150         return ret;
1151     }
1152     ret = krb5_copy_principal(context, client, &cred->client);
1153     krb5_free_principal(context, client);
1154     return ret;
1155 }
1156                       
1157 /**
1158  * Store some configuration for the credential cache in the cache.
1159  * Existing configuration under the same name is over-written.
1160  *
1161  * @param context a Keberos context
1162  * @param id the credential cache to store the data for
1163  * @param principal configuration for a specific principal, if
1164  * NULL, global for the whole cache.
1165  * @param name name under which the configuraion is stored.
1166  * @param data data to store
1167  */
1168
1169 krb5_error_code KRB5_LIB_FUNCTION
1170 krb5_cc_set_config(krb5_context context, krb5_ccache id, 
1171                    krb5_const_principal principal,
1172                    const char *name, krb5_data *data)
1173 {
1174     krb5_error_code ret;
1175     krb5_creds cred;
1176
1177     ret = build_conf_principals(context, id, principal, name, &cred);
1178     if (ret)
1179         goto out;
1180
1181     /* Remove old configuration */
1182     ret = krb5_cc_remove_cred(context, id, 0, &cred);
1183     if (ret)
1184         goto out;
1185
1186     /* not that anyone care when this expire */
1187     cred.times.authtime = time(NULL);
1188     cred.times.endtime = cred.times.authtime + 3600 * 24 * 30; 
1189
1190     ret = krb5_data_copy(&cred.ticket, data->data, data->length);
1191     if (ret)
1192         goto out;
1193
1194     ret = krb5_cc_store_cred(context, id, &cred);
1195
1196 out:
1197     krb5_free_cred_contents (context, &cred);
1198     return ret;
1199 }
1200
1201 /**
1202  * Get some configuration for the credential cache in the cache.
1203  *
1204  * @param context a Keberos context
1205  * @param id the credential cache to store the data for
1206  * @param principal configuration for a specific principal, if
1207  * NULL, global for the whole cache.
1208  * @param name name under which the configuraion is stored.
1209  * @param data data to fetched, free with krb5_data_free()
1210  */
1211
1212
1213 krb5_error_code KRB5_LIB_FUNCTION
1214 krb5_cc_get_config(krb5_context context, krb5_ccache id,
1215                    krb5_const_principal principal,
1216                    const char *name, krb5_data *data)
1217 {
1218     krb5_creds mcred, cred;
1219     krb5_error_code ret;
1220
1221     memset(&cred, 0, sizeof(cred));
1222     krb5_data_zero(data);
1223
1224     ret = build_conf_principals(context, id, principal, name, &mcred);
1225     if (ret)
1226         goto out;
1227
1228     ret = krb5_cc_retrieve_cred(context, id, 0, &mcred, &cred);
1229     if (ret)
1230         goto out;
1231
1232     ret = krb5_data_copy(data, cred.ticket.data, cred.ticket.length);
1233
1234 out:
1235     krb5_free_cred_contents (context, &cred);
1236     krb5_free_cred_contents (context, &mcred);
1237     return ret;
1238 }
1239