4c2cf3bce423b4dc9c1a5f9405ded6f45890212b
[abartlet/lorikeet-heimdal.git/.git] / lib / krb5 / cache.c
1 /*
2  * Copyright (c) 1997 - 2008 Kungliga Tekniska Högskolan
3  * (Royal Institute of Technology, Stockholm, Sweden).
4  * All rights reserved.
5  *
6  * Portions Copyright (c) 2009 Apple Inc. All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  *
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  *
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  *
19  * 3. Neither the name of the Institute nor the names of its contributors
20  *    may be used to endorse or promote products derived from this software
21  *    without specific prior written permission.
22  *
23  * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
24  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26  * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
27  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33  * SUCH DAMAGE.
34  */
35
36 #include "krb5_locl.h"
37
38 /**
39  * @page krb5_ccache_intro The credential cache functions
40  * @section section_krb5_ccache Kerberos credential caches
41  * 
42  * krb5_ccache structure holds a Kerberos credential cache.
43  *
44  * Heimdal support the follow types of credential caches:
45  *
46  * - SCC
47  *   Store the credential in a database
48  * - FILE
49  *   Store the credential in memory
50  * - MEMORY
51  *   Store the credential in memory
52  * - API
53  *   A credential cache server based solution for Mac OS X
54  * - KCM
55  *   A credential cache server based solution for all platforms
56  *
57  * @subsection Example
58  *
59  * This is a minimalistic version of klist:
60 @code
61 #include <krb5.h>
62
63 int
64 main (int argc, char **argv)
65 {
66     krb5_context context;
67     krb5_cc_cursor cursor;
68     krb5_error_code ret;
69     krb5_ccache id;
70     krb5_creds creds;
71
72     if (krb5_init_context (&context) != 0)
73         errx(1, "krb5_context");
74
75     ret = krb5_cc_default (context, &id);
76     if (ret)
77         krb5_err(context, 1, ret, "krb5_cc_default");
78
79     ret = krb5_cc_start_seq_get(context, id, &cursor);
80     if (ret)
81         krb5_err(context, 1, ret, "krb5_cc_start_seq_get");
82
83     while((ret = krb5_cc_next_cred(context, id, &cursor, &creds)) == 0){
84         char *principal;
85
86         krb5_unparse_name_short(context, creds.server, &principal);
87         printf("principal: %s\\n", principal);
88         free(principal);
89         krb5_free_cred_contents (context, &creds);
90     }
91     ret = krb5_cc_end_seq_get(context, id, &cursor);
92     if (ret)
93         krb5_err(context, 1, ret, "krb5_cc_end_seq_get");
94
95     krb5_cc_close(context, id);
96
97     krb5_free_context(context);
98     return 0;
99 }
100 * @endcode
101 */
102
103 /**
104  * Add a new ccache type with operations `ops', overwriting any
105  * existing one if `override'.
106  *
107  * @param context a Keberos context
108  * @param ops type of plugin symbol
109  * @param override flag to select if the registration is to overide
110  * an existing ops with the same name.
111  *
112  * @return Return an error code or 0, see krb5_get_error_message().
113  *
114  * @ingroup krb5_ccache
115  */
116
117 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
118 krb5_cc_register(krb5_context context,
119                  const krb5_cc_ops *ops,
120                  krb5_boolean override)
121 {
122     int i;
123
124     for(i = 0; i < context->num_cc_ops && context->cc_ops[i]->prefix; i++) {
125         if(strcmp(context->cc_ops[i]->prefix, ops->prefix) == 0) {
126             if(!override) {
127                 krb5_set_error_message(context,
128                                        KRB5_CC_TYPE_EXISTS,
129                                        N_("cache type %s already exists", "type"),
130                                        ops->prefix);
131                 return KRB5_CC_TYPE_EXISTS;
132             }
133             break;
134         }
135     }
136     if(i == context->num_cc_ops) {
137         const krb5_cc_ops **o = realloc(context->cc_ops,
138                                         (context->num_cc_ops + 1) *
139                                         sizeof(context->cc_ops[0]));
140         if(o == NULL) {
141             krb5_set_error_message(context, KRB5_CC_NOMEM,
142                                    N_("malloc: out of memory", ""));
143             return KRB5_CC_NOMEM;
144         }
145         context->cc_ops = o;
146         context->cc_ops[context->num_cc_ops] = NULL;
147         context->num_cc_ops++;
148     }
149     context->cc_ops[i] = ops;
150     return 0;
151 }
152
153 /*
154  * Allocate the memory for a `id' and the that function table to
155  * `ops'. Returns 0 or and error code.
156  */
157
158 krb5_error_code
159 _krb5_cc_allocate(krb5_context context,
160                   const krb5_cc_ops *ops,
161                   krb5_ccache *id)
162 {
163     krb5_ccache p;
164
165     p = malloc (sizeof(*p));
166     if(p == NULL) {
167         krb5_set_error_message(context, KRB5_CC_NOMEM,
168                                N_("malloc: out of memory", ""));
169         return KRB5_CC_NOMEM;
170     }
171     p->ops = ops;
172     *id = p;
173
174     return 0;
175 }
176
177 /*
178  * Allocate memory for a new ccache in `id' with operations `ops'
179  * and name `residual'. Return 0 or an error code.
180  */
181
182 static krb5_error_code
183 allocate_ccache (krb5_context context,
184                  const krb5_cc_ops *ops,
185                  const char *residual,
186                  krb5_ccache *id)
187 {
188     krb5_error_code ret;
189 #ifdef KRB5_USE_PATH_TOKENS
190     char * exp_residual = NULL;
191
192     ret = _krb5_expand_path_tokens(context, residual, &exp_residual);
193     if (ret)
194         return ret;
195
196     residual = exp_residual;
197 #endif
198
199     ret = _krb5_cc_allocate(context, ops, id);
200     if (ret) {
201 #ifdef KRB5_USE_PATH_TOKENS
202         if (exp_residual)
203             free(exp_residual);
204 #endif
205         return ret;
206     }
207
208     ret = (*id)->ops->resolve(context, id, residual);
209     if(ret)
210         free(*id);
211
212 #ifdef KRB5_USE_PATH_TOKENS
213     if (exp_residual)
214         free(exp_residual);
215 #endif
216
217     return ret;
218 }
219
220 static int
221 is_possible_path_name(const char * name)
222 {
223     const char * colon;
224
225     if ((colon = strchr(name, ':')) == NULL)
226         return TRUE;
227
228 #ifdef _WIN32
229     /* <drive letter>:\path\to\cache ? */
230
231     if (colon == name + 1 &&
232         strchr(colon + 1, ':') == NULL)
233         return TRUE;
234 #endif
235
236     return FALSE;
237 }
238
239 /**
240  * Find and allocate a ccache in `id' from the specification in `residual'.
241  * If the ccache name doesn't contain any colon, interpret it as a file name.
242  *
243  * @param context a Keberos context.
244  * @param name string name of a credential cache.
245  * @param id return pointer to a found credential cache.
246  *
247  * @return Return 0 or an error code. In case of an error, id is set
248  * to NULL, see krb5_get_error_message().
249  *
250  * @ingroup krb5_ccache
251  */
252
253
254 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
255 krb5_cc_resolve(krb5_context context,
256                 const char *name,
257                 krb5_ccache *id)
258 {
259     int i;
260
261     *id = NULL;
262
263     for(i = 0; i < context->num_cc_ops && context->cc_ops[i]->prefix; i++) {
264         size_t prefix_len = strlen(context->cc_ops[i]->prefix);
265
266         if(strncmp(context->cc_ops[i]->prefix, name, prefix_len) == 0
267            && name[prefix_len] == ':') {
268             return allocate_ccache (context, context->cc_ops[i],
269                                     name + prefix_len + 1,
270                                     id);
271         }
272     }
273     if (is_possible_path_name(name))
274         return allocate_ccache (context, &krb5_fcc_ops, name, id);
275     else {
276         krb5_set_error_message(context, KRB5_CC_UNKNOWN_TYPE,
277                                N_("unknown ccache type %s", "name"), name);
278         return KRB5_CC_UNKNOWN_TYPE;
279     }
280 }
281
282 /**
283  * Generates a new unique ccache of `type` in `id'. If `type' is NULL,
284  * the library chooses the default credential cache type. The supplied
285  * `hint' (that can be NULL) is a string that the credential cache
286  * type can use to base the name of the credential on, this is to make
287  * it easier for the user to differentiate the credentials.
288  *
289  * @return Return an error code or 0, see krb5_get_error_message().
290  *
291  * @ingroup krb5_ccache
292  */
293
294 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
295 krb5_cc_new_unique(krb5_context context, const char *type,
296                    const char *hint, krb5_ccache *id)
297 {
298     const krb5_cc_ops *ops;
299     krb5_error_code ret;
300
301     ops = krb5_cc_get_prefix_ops(context, type);
302     if (ops == NULL) {
303         krb5_set_error_message(context, KRB5_CC_UNKNOWN_TYPE,
304                               "Credential cache type %s is unknown", type);
305         return KRB5_CC_UNKNOWN_TYPE;
306     }
307
308     ret = _krb5_cc_allocate(context, ops, id);
309     if (ret)
310         return ret;
311     ret = (*id)->ops->gen_new(context, id);
312     if (ret) {
313         free(*id);
314         *id = NULL;
315     }
316     return ret;
317 }
318
319 /**
320  * Return the name of the ccache `id'
321  *
322  * @ingroup krb5_ccache
323  */
324
325
326 KRB5_LIB_FUNCTION const char* KRB5_LIB_CALL
327 krb5_cc_get_name(krb5_context context,
328                  krb5_ccache id)
329 {
330     return id->ops->get_name(context, id);
331 }
332
333 /**
334  * Return the type of the ccache `id'.
335  *
336  * @ingroup krb5_ccache
337  */
338
339
340 KRB5_LIB_FUNCTION const char* KRB5_LIB_CALL
341 krb5_cc_get_type(krb5_context context,
342                  krb5_ccache id)
343 {
344     return id->ops->prefix;
345 }
346
347 /**
348  * Return the complete resolvable name the cache
349
350  * @param context a Keberos context
351  * @param id return pointer to a found credential cache
352  * @param str the returned name of a credential cache, free with krb5_xfree()
353  *
354  * @return Returns 0 or an error (and then *str is set to NULL).
355  *
356  * @ingroup krb5_ccache
357  */
358
359
360 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
361 krb5_cc_get_full_name(krb5_context context,
362                       krb5_ccache id,
363                       char **str)
364 {
365     const char *type, *name;
366
367     *str = NULL;
368
369     type = krb5_cc_get_type(context, id);
370     if (type == NULL) {
371         krb5_set_error_message(context, KRB5_CC_UNKNOWN_TYPE,
372                                "cache have no name of type");
373         return KRB5_CC_UNKNOWN_TYPE;
374     }
375
376     name = krb5_cc_get_name(context, id);
377     if (name == NULL) {
378         krb5_set_error_message(context, KRB5_CC_BADNAME,
379                                "cache of type %s have no name", type);
380         return KRB5_CC_BADNAME;
381     }
382
383     if (asprintf(str, "%s:%s", type, name) == -1) {
384         krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
385         *str = NULL;
386         return ENOMEM;
387     }
388     return 0;
389 }
390
391 /**
392  * Return krb5_cc_ops of a the ccache `id'.
393  *
394  * @ingroup krb5_ccache
395  */
396
397
398 const krb5_cc_ops *
399 krb5_cc_get_ops(krb5_context context, krb5_ccache id)
400 {
401     return id->ops;
402 }
403
404 /*
405  * Expand variables in `str' into `res'
406  */
407
408 krb5_error_code
409 _krb5_expand_default_cc_name(krb5_context context, const char *str, char **res)
410 {
411     return _krb5_expand_path_tokens(context, str, res);
412 }
413
414 /*
415  * Return non-zero if envirnoment that will determine default krb5cc
416  * name has changed.
417  */
418
419 static int
420 environment_changed(krb5_context context)
421 {
422     const char *e;
423
424     /* if the cc name was set, don't change it */
425     if (context->default_cc_name_set)
426         return 0;
427
428     /* XXX performance: always ask KCM/API if default name has changed */
429     if (context->default_cc_name &&
430         (strncmp(context->default_cc_name, "KCM:", 4) == 0 ||
431          strncmp(context->default_cc_name, "API:", 4) == 0))
432         return 1;
433
434     if(issuid())
435         return 0;
436
437     e = getenv("KRB5CCNAME");
438     if (e == NULL) {
439         if (context->default_cc_name_env) {
440             free(context->default_cc_name_env);
441             context->default_cc_name_env = NULL;
442             return 1;
443         }
444     } else {
445         if (context->default_cc_name_env == NULL)
446             return 1;
447         if (strcmp(e, context->default_cc_name_env) != 0)
448             return 1;
449     }
450     return 0;
451 }
452
453 /**
454  * Switch the default default credential cache for a specific
455  * credcache type (and name for some implementations).
456  *
457  * @return Return an error code or 0, see krb5_get_error_message().
458  *
459  * @ingroup krb5_ccache
460  */
461
462 krb5_error_code KRB5_LIB_FUNCTION
463 krb5_cc_switch(krb5_context context, krb5_ccache id)
464 {
465
466     if (id->ops->set_default == NULL)
467         return 0;
468
469     return (*id->ops->set_default)(context, id);
470 }
471
472 /**
473  * Return true if the default credential cache support switch
474  *
475  * @ingroup krb5_ccache
476  */
477
478 krb5_boolean KRB5_LIB_FUNCTION
479 krb5_cc_support_switch(krb5_context context, const char *type)
480 {
481     const krb5_cc_ops *ops;
482
483     ops = krb5_cc_get_prefix_ops(context, type);
484     if (ops && ops->set_default)
485         return 1;
486     return FALSE;
487 }
488
489 /**
490  * Set the default cc name for `context' to `name'.
491  *
492  * @ingroup krb5_ccache
493  */
494
495 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
496 krb5_cc_set_default_name(krb5_context context, const char *name)
497 {
498     krb5_error_code ret = 0;
499     char *p = NULL, *exp_p = NULL;
500
501     if (name == NULL) {
502         const char *e = NULL;
503
504         if(!issuid()) {
505             e = getenv("KRB5CCNAME");
506             if (e) {
507                 p = strdup(e);
508                 if (context->default_cc_name_env)
509                     free(context->default_cc_name_env);
510                 context->default_cc_name_env = strdup(e);
511             }
512         }
513         if (e == NULL) {
514             e = krb5_config_get_string(context, NULL, "libdefaults",
515                                        "default_cc_name", NULL);
516             if (e) {
517                 ret = _krb5_expand_default_cc_name(context, e, &p);
518                 if (ret)
519                     return ret;
520             }
521             if (e == NULL) {
522                 const krb5_cc_ops *ops = KRB5_DEFAULT_CCTYPE;
523                 e = krb5_config_get_string(context, NULL, "libdefaults",
524                                            "default_cc_type", NULL);
525                 if (e) {
526                     ops = krb5_cc_get_prefix_ops(context, e);
527                     if (ops == NULL) {
528                         krb5_set_error_message(context,
529                                                KRB5_CC_UNKNOWN_TYPE,
530                                                "Credential cache type %s "
531                                               "is unknown", e);
532                         return KRB5_CC_UNKNOWN_TYPE;
533                     }
534                 }
535                 ret = (*ops->get_default_name)(context, &p);
536                 if (ret)
537                     return ret;
538             }
539         }
540         context->default_cc_name_set = 0;
541     } else {
542         p = strdup(name);
543         context->default_cc_name_set = 1;
544     }
545
546     if (p == NULL) {
547         krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
548         return ENOMEM;
549     }
550
551     ret = _krb5_expand_path_tokens(context, p, &exp_p);
552     free(p);
553     if (ret)
554         return ret;
555
556     if (context->default_cc_name)
557         free(context->default_cc_name);
558
559     context->default_cc_name = exp_p;
560
561     return 0;
562 }
563
564 /**
565  * Return a pointer to a context static string containing the default
566  * ccache name.
567  *
568  * @return String to the default credential cache name.
569  *
570  * @ingroup krb5_ccache
571  */
572
573
574 KRB5_LIB_FUNCTION const char* KRB5_LIB_CALL
575 krb5_cc_default_name(krb5_context context)
576 {
577     if (context->default_cc_name == NULL || environment_changed(context))
578         krb5_cc_set_default_name(context, NULL);
579
580     return context->default_cc_name;
581 }
582
583 /**
584  * Open the default ccache in `id'.
585  *
586  * @return Return an error code or 0, see krb5_get_error_message().
587  *
588  * @ingroup krb5_ccache
589  */
590
591
592 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
593 krb5_cc_default(krb5_context context,
594                 krb5_ccache *id)
595 {
596     const char *p = krb5_cc_default_name(context);
597
598     if (p == NULL) {
599         krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
600         return ENOMEM;
601     }
602     return krb5_cc_resolve(context, p, id);
603 }
604
605 /**
606  * Create a new ccache in `id' for `primary_principal'.
607  *
608  * @return Return an error code or 0, see krb5_get_error_message().
609  *
610  * @ingroup krb5_ccache
611  */
612
613
614 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
615 krb5_cc_initialize(krb5_context context,
616                    krb5_ccache id,
617                    krb5_principal primary_principal)
618 {
619     return (*id->ops->init)(context, id, primary_principal);
620 }
621
622
623 /**
624  * Remove the ccache `id'.
625  *
626  * @return Return an error code or 0, see krb5_get_error_message().
627  *
628  * @ingroup krb5_ccache
629  */
630
631
632 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
633 krb5_cc_destroy(krb5_context context,
634                 krb5_ccache id)
635 {
636     krb5_error_code ret;
637
638     ret = (*id->ops->destroy)(context, id);
639     krb5_cc_close (context, id);
640     return ret;
641 }
642
643 /**
644  * Stop using the ccache `id' and free the related resources.
645  *
646  * @return Return an error code or 0, see krb5_get_error_message().
647  *
648  * @ingroup krb5_ccache
649  */
650
651
652 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
653 krb5_cc_close(krb5_context context,
654               krb5_ccache id)
655 {
656     krb5_error_code ret;
657     ret = (*id->ops->close)(context, id);
658     free(id);
659     return ret;
660 }
661
662 /**
663  * Store `creds' in the ccache `id'.
664  *
665  * @return Return an error code or 0, see krb5_get_error_message().
666  *
667  * @ingroup krb5_ccache
668  */
669
670
671 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
672 krb5_cc_store_cred(krb5_context context,
673                    krb5_ccache id,
674                    krb5_creds *creds)
675 {
676     return (*id->ops->store)(context, id, creds);
677 }
678
679 /**
680  * Retrieve the credential identified by `mcreds' (and `whichfields')
681  * from `id' in `creds'. 'creds' must be free by the caller using
682  * krb5_free_cred_contents.
683  *
684  * @param context A Kerberos 5 context
685  * @param id a Kerberos 5 credential cache
686  * @param whichfields what fields to use for matching credentials, same
687  *        flags as whichfields in krb5_compare_creds()
688  * @param mcreds template credential to use for comparing
689  * @param creds returned credential, free with krb5_free_cred_contents()
690  *
691  * @return Return an error code or 0, see krb5_get_error_message().
692  *
693  * @ingroup krb5_ccache
694  */
695
696
697 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
698 krb5_cc_retrieve_cred(krb5_context context,
699                       krb5_ccache id,
700                       krb5_flags whichfields,
701                       const krb5_creds *mcreds,
702                       krb5_creds *creds)
703 {
704     krb5_error_code ret;
705     krb5_cc_cursor cursor;
706
707     if (id->ops->retrieve != NULL) {
708         return (*id->ops->retrieve)(context, id, whichfields,
709                                     mcreds, creds);
710     }
711
712     ret = krb5_cc_start_seq_get(context, id, &cursor);
713     if (ret)
714         return ret;
715     while((ret = krb5_cc_next_cred(context, id, &cursor, creds)) == 0){
716         if(krb5_compare_creds(context, whichfields, mcreds, creds)){
717             ret = 0;
718             break;
719         }
720         krb5_free_cred_contents (context, creds);
721     }
722     krb5_cc_end_seq_get(context, id, &cursor);
723     return ret;
724 }
725
726 /**
727  * Return the principal of `id' in `principal'.
728  *
729  * @return Return an error code or 0, see krb5_get_error_message().
730  *
731  * @ingroup krb5_ccache
732  */
733
734
735 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
736 krb5_cc_get_principal(krb5_context context,
737                       krb5_ccache id,
738                       krb5_principal *principal)
739 {
740     return (*id->ops->get_princ)(context, id, principal);
741 }
742
743 /**
744  * Start iterating over `id', `cursor' is initialized to the
745  * beginning.  Caller must free the cursor with krb5_cc_end_seq_get().
746  *
747  * @return Return an error code or 0, see krb5_get_error_message().
748  *
749  * @ingroup krb5_ccache
750  */
751
752
753 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
754 krb5_cc_start_seq_get (krb5_context context,
755                        const krb5_ccache id,
756                        krb5_cc_cursor *cursor)
757 {
758     return (*id->ops->get_first)(context, id, cursor);
759 }
760
761 /**
762  * Retrieve the next cred pointed to by (`id', `cursor') in `creds'
763  * and advance `cursor'.
764  *
765  * @return Return an error code or 0, see krb5_get_error_message().
766  *
767  * @ingroup krb5_ccache
768  */
769
770
771 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
772 krb5_cc_next_cred (krb5_context context,
773                    const krb5_ccache id,
774                    krb5_cc_cursor *cursor,
775                    krb5_creds *creds)
776 {
777     return (*id->ops->get_next)(context, id, cursor, creds);
778 }
779
780 /**
781  * Destroy the cursor `cursor'.
782  *
783  * @ingroup krb5_ccache
784  */
785
786
787 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
788 krb5_cc_end_seq_get (krb5_context context,
789                      const krb5_ccache id,
790                      krb5_cc_cursor *cursor)
791 {
792     return (*id->ops->end_get)(context, id, cursor);
793 }
794
795 /**
796  * Remove the credential identified by `cred', `which' from `id'.
797  *
798  * @ingroup krb5_ccache
799  */
800
801
802 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
803 krb5_cc_remove_cred(krb5_context context,
804                     krb5_ccache id,
805                     krb5_flags which,
806                     krb5_creds *cred)
807 {
808     if(id->ops->remove_cred == NULL) {
809         krb5_set_error_message(context,
810                                EACCES,
811                                "ccache %s does not support remove_cred",
812                                id->ops->prefix);
813         return EACCES; /* XXX */
814     }
815     return (*id->ops->remove_cred)(context, id, which, cred);
816 }
817
818 /**
819  * Set the flags of `id' to `flags'.
820  *
821  * @ingroup krb5_ccache
822  */
823
824
825 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
826 krb5_cc_set_flags(krb5_context context,
827                   krb5_ccache id,
828                   krb5_flags flags)
829 {
830     return (*id->ops->set_flags)(context, id, flags);
831 }
832                 
833 /**
834  * Get the flags of `id', store them in `flags'.
835  *
836  * @ingroup krb5_ccache
837  */
838
839 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
840 krb5_cc_get_flags(krb5_context context,
841                   krb5_ccache id,
842                   krb5_flags *flags)
843 {
844     *flags = 0;
845     return 0;
846 }
847
848 /**
849  * Copy the contents of `from' to `to' if the given match function
850  * return true.
851  *
852  * @param context A Kerberos 5 context.
853  * @param from the cache to copy data from.
854  * @param to the cache to copy data to.
855  * @param match a match function that should return TRUE if cred argument should be copied, if NULL, all credentials are copied.
856  * @param matchctx context passed to match function.
857  * @param matched set to true if there was a credential that matched, may be NULL.
858  *
859  * @return Return an error code or 0, see krb5_get_error_message().
860  *
861  * @ingroup krb5_ccache
862  */
863
864 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
865 krb5_cc_copy_match_f(krb5_context context,
866                      const krb5_ccache from,
867                      krb5_ccache to,
868                      krb5_boolean (*match)(krb5_context, void *, const krb5_creds *),
869                      void *matchctx,
870                      unsigned int *matched)
871 {
872     krb5_error_code ret;
873     krb5_cc_cursor cursor;
874     krb5_creds cred;
875     krb5_principal princ;
876
877     if (matched)
878         *matched = 0;
879
880     ret = krb5_cc_get_principal(context, from, &princ);
881     if (ret)
882         return ret;
883     ret = krb5_cc_initialize(context, to, princ);
884     if (ret) {
885         krb5_free_principal(context, princ);
886         return ret;
887     }
888     ret = krb5_cc_start_seq_get(context, from, &cursor);
889     if (ret) {
890         krb5_free_principal(context, princ);
891         return ret;
892     }
893
894     while ((ret = krb5_cc_next_cred(context, from, &cursor, &cred)) == 0) {
895            if (match == NULL || (*match)(context, matchctx, &cred) == 0) {
896                if (matched)
897                    (*matched)++;
898                ret = krb5_cc_store_cred(context, to, &cred);
899                if (ret)
900                    break;
901            }
902            krb5_free_cred_contents(context, &cred);
903     }
904     krb5_cc_end_seq_get(context, from, &cursor);
905     krb5_free_principal(context, princ);
906     if (ret == KRB5_CC_END)
907         ret = 0;
908     return ret;
909 }
910
911 /**
912  * Just like krb5_cc_copy_match_f(), but copy everything.
913  *
914  * @ingroup @krb5_ccache
915  */
916
917 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
918 krb5_cc_copy_cache(krb5_context context,
919                    const krb5_ccache from,
920                    krb5_ccache to)
921 {
922     return krb5_cc_copy_match_f(context, from, to, NULL, NULL, NULL);
923 }
924
925 /**
926  * Return the version of `id'.
927  *
928  * @ingroup krb5_ccache
929  */
930
931
932 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
933 krb5_cc_get_version(krb5_context context,
934                     const krb5_ccache id)
935 {
936     if(id->ops->get_version)
937         return (*id->ops->get_version)(context, id);
938     else
939         return 0;
940 }
941
942 /**
943  * Clear `mcreds' so it can be used with krb5_cc_retrieve_cred
944  *
945  * @ingroup krb5_ccache
946  */
947
948
949 KRB5_LIB_FUNCTION void KRB5_LIB_CALL
950 krb5_cc_clear_mcred(krb5_creds *mcred)
951 {
952     memset(mcred, 0, sizeof(*mcred));
953 }
954
955 /**
956  * Get the cc ops that is registered in `context' to handle the
957  * prefix. prefix can be a complete credential cache name or a
958  * prefix, the function will only use part up to the first colon (:)
959  * if there is one. If prefix the argument is NULL, the default ccache
960  * implemtation is returned.
961  *
962  * @return Returns NULL if ops not found.
963  *
964  * @ingroup krb5_ccache
965  */
966
967
968 const krb5_cc_ops *
969 krb5_cc_get_prefix_ops(krb5_context context, const char *prefix)
970 {
971     char *p, *p1;
972     int i;
973
974     if (prefix == NULL)
975         return KRB5_DEFAULT_CCTYPE;
976     if (prefix[0] == '/')
977         return &krb5_fcc_ops;
978
979     p = strdup(prefix);
980     if (p == NULL) {
981         krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
982         return NULL;
983     }
984     p1 = strchr(p, ':');
985     if (p1)
986         *p1 = '\0';
987
988     for(i = 0; i < context->num_cc_ops && context->cc_ops[i]->prefix; i++) {
989         if(strcmp(context->cc_ops[i]->prefix, p) == 0) {
990             free(p);
991             return context->cc_ops[i];
992         }
993     }
994     free(p);
995     return NULL;
996 }
997
998 struct krb5_cc_cache_cursor_data {
999     const krb5_cc_ops *ops;
1000     krb5_cc_cursor cursor;
1001 };
1002
1003 /**
1004  * Start iterating over all caches of specified type. See also
1005  * krb5_cccol_cursor_new().
1006
1007  * @param context A Kerberos 5 context
1008  * @param type optional type to iterate over, if NULL, the default cache is used.
1009  * @param cursor cursor should be freed with krb5_cc_cache_end_seq_get().
1010  *
1011  * @return Return an error code or 0, see krb5_get_error_message().
1012  *
1013  * @ingroup krb5_ccache
1014  */
1015
1016
1017 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1018 krb5_cc_cache_get_first (krb5_context context,
1019                          const char *type,
1020                          krb5_cc_cache_cursor *cursor)
1021 {
1022     const krb5_cc_ops *ops;
1023     krb5_error_code ret;
1024
1025     if (type == NULL)
1026         type = krb5_cc_default_name(context);
1027
1028     ops = krb5_cc_get_prefix_ops(context, type);
1029     if (ops == NULL) {
1030         krb5_set_error_message(context, KRB5_CC_UNKNOWN_TYPE,
1031                                "Unknown type \"%s\" when iterating "
1032                                "trying to iterate the credential caches", type);
1033         return KRB5_CC_UNKNOWN_TYPE;
1034     }
1035
1036     if (ops->get_cache_first == NULL) {
1037         krb5_set_error_message(context, KRB5_CC_NOSUPP,
1038                                N_("Credential cache type %s doesn't support "
1039                                  "iterations over caches", "type"),
1040                                ops->prefix);
1041         return KRB5_CC_NOSUPP;
1042     }
1043
1044     *cursor = calloc(1, sizeof(**cursor));
1045     if (*cursor == NULL) {
1046         krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
1047         return ENOMEM;
1048     }
1049
1050     (*cursor)->ops = ops;
1051
1052     ret = ops->get_cache_first(context, &(*cursor)->cursor);
1053     if (ret) {
1054         free(*cursor);
1055         *cursor = NULL;
1056     }
1057     return ret;
1058 }
1059
1060 /**
1061  * Retrieve the next cache pointed to by (`cursor') in `id'
1062  * and advance `cursor'.
1063  *
1064  * @param context A Kerberos 5 context
1065  * @param cursor the iterator cursor, returned by krb5_cc_cache_get_first()
1066  * @param id next ccache
1067  *
1068  * @return Return 0 or an error code. Returns KRB5_CC_END when the end
1069  *         of caches is reached, see krb5_get_error_message().
1070  *
1071  * @ingroup krb5_ccache
1072  */
1073
1074
1075 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1076 krb5_cc_cache_next (krb5_context context,
1077                    krb5_cc_cache_cursor cursor,
1078                    krb5_ccache *id)
1079 {
1080     return cursor->ops->get_cache_next(context, cursor->cursor, id);
1081 }
1082
1083 /**
1084  * Destroy the cursor `cursor'.
1085  *
1086  * @return Return an error code or 0, see krb5_get_error_message().
1087  *
1088  * @ingroup krb5_ccache
1089  */
1090
1091
1092 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1093 krb5_cc_cache_end_seq_get (krb5_context context,
1094                            krb5_cc_cache_cursor cursor)
1095 {
1096     krb5_error_code ret;
1097     ret = cursor->ops->end_cache_get(context, cursor->cursor);
1098     cursor->ops = NULL;
1099     free(cursor);
1100     return ret;
1101 }
1102
1103 /**
1104  * Search for a matching credential cache that have the
1105  * `principal' as the default principal. On success, `id' needs to be
1106  * freed with krb5_cc_close() or krb5_cc_destroy().
1107  *
1108  * @param context A Kerberos 5 context
1109  * @param client The principal to search for
1110  * @param id the returned credential cache
1111  *
1112  * @return On failure, error code is returned and `id' is set to NULL.
1113  *
1114  * @ingroup krb5_ccache
1115  */
1116
1117
1118 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1119 krb5_cc_cache_match (krb5_context context,
1120                      krb5_principal client,
1121                      krb5_ccache *id)
1122 {
1123     krb5_cccol_cursor cursor;
1124     krb5_error_code ret;
1125     krb5_ccache cache = NULL;
1126
1127     *id = NULL;
1128
1129     ret = krb5_cccol_cursor_new (context, &cursor);
1130     if (ret)
1131         return ret;
1132
1133     while (krb5_cccol_cursor_next (context, cursor, &cache) == 0 && cache != NULL) {
1134         krb5_principal principal;
1135
1136         ret = krb5_cc_get_principal(context, cache, &principal);
1137         if (ret == 0) {
1138             krb5_boolean match;
1139         
1140             match = krb5_principal_compare(context, principal, client);
1141             krb5_free_principal(context, principal);
1142             if (match)
1143                 break;
1144         }
1145
1146         krb5_cc_close(context, cache);
1147         cache = NULL;
1148     }
1149
1150     krb5_cccol_cursor_free(context, &cursor);
1151
1152     if (cache == NULL) {
1153         char *str;
1154
1155         krb5_unparse_name(context, client, &str);
1156
1157         krb5_set_error_message(context, KRB5_CC_NOTFOUND,
1158                                N_("Principal %s not found in any "
1159                                   "credential cache", ""),
1160                                str ? str : "<out of memory>");
1161         if (str)
1162             free(str);
1163         return KRB5_CC_NOTFOUND;
1164     }
1165     *id = cache;
1166
1167     return 0;
1168 }
1169
1170 /**
1171  * Move the content from one credential cache to another. The
1172  * operation is an atomic switch.
1173  *
1174  * @param context a Keberos context
1175  * @param from the credential cache to move the content from
1176  * @param to the credential cache to move the content to
1177
1178  * @return On sucess, from is freed. On failure, error code is
1179  * returned and from and to are both still allocated, see krb5_get_error_message().
1180  *
1181  * @ingroup krb5_ccache
1182  */
1183
1184 krb5_error_code
1185 krb5_cc_move(krb5_context context, krb5_ccache from, krb5_ccache to)
1186 {
1187     krb5_error_code ret;
1188
1189     if (strcmp(from->ops->prefix, to->ops->prefix) != 0) {
1190         krb5_set_error_message(context, KRB5_CC_NOSUPP,
1191                                N_("Moving credentials between diffrent "
1192                                  "types not yet supported", ""));
1193         return KRB5_CC_NOSUPP;
1194     }
1195
1196     ret = (*to->ops->move)(context, from, to);
1197     if (ret == 0) {
1198         memset(from, 0, sizeof(*from));
1199         free(from);
1200     }
1201     return ret;
1202 }
1203
1204 #define KRB5_CONF_NAME "krb5_ccache_conf_data"
1205 #define KRB5_REALM_NAME "X-CACHECONF:"
1206
1207 static krb5_error_code
1208 build_conf_principals(krb5_context context, krb5_ccache id,
1209                       krb5_const_principal principal,
1210                       const char *name, krb5_creds *cred)
1211 {
1212     krb5_principal client;
1213     krb5_error_code ret;
1214     char *pname = NULL;
1215
1216     memset(cred, 0, sizeof(*cred));
1217
1218     ret = krb5_cc_get_principal(context, id, &client);
1219     if (ret)
1220         return ret;
1221
1222     if (principal) {
1223         ret = krb5_unparse_name(context, principal, &pname);
1224         if (ret)
1225             return ret;
1226     }
1227
1228     ret = krb5_make_principal(context, &cred->server,
1229                               KRB5_REALM_NAME,
1230                               KRB5_CONF_NAME, name, pname, NULL);
1231     free(pname);
1232     if (ret) {
1233         krb5_free_principal(context, client);
1234         return ret;
1235     }
1236     ret = krb5_copy_principal(context, client, &cred->client);
1237     krb5_free_principal(context, client);
1238     return ret;
1239 }
1240                 
1241 /**
1242  * Return TRUE (non zero) if the principal is a configuration
1243  * principal (generated part of krb5_cc_set_config()). Returns FALSE
1244  * (zero) if not a configuration principal.
1245  *
1246  * @param context a Keberos context
1247  * @param principal principal to check if it a configuration principal
1248  *
1249  * @ingroup krb5_ccache
1250  */
1251
1252 KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL
1253 krb5_is_config_principal(krb5_context context,
1254                          krb5_const_principal principal)
1255 {
1256     if (strcmp(principal->realm, KRB5_REALM_NAME) != 0)
1257         return FALSE;
1258
1259     if (principal->name.name_string.len == 0 ||
1260         strcmp(principal->name.name_string.val[0], KRB5_CONF_NAME) != 0)
1261         return FALSE;
1262         
1263     return TRUE;
1264 }
1265
1266 /**
1267  * Store some configuration for the credential cache in the cache.
1268  * Existing configuration under the same name is over-written.
1269  *
1270  * @param context a Keberos context
1271  * @param id the credential cache to store the data for
1272  * @param principal configuration for a specific principal, if
1273  * NULL, global for the whole cache.
1274  * @param name name under which the configuraion is stored.
1275  * @param data data to store, if NULL, configure is removed.
1276  *
1277  * @ingroup krb5_ccache
1278  */
1279
1280 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1281 krb5_cc_set_config(krb5_context context, krb5_ccache id,
1282                    krb5_const_principal principal,
1283                    const char *name, krb5_data *data)
1284 {
1285     krb5_error_code ret;
1286     krb5_creds cred;
1287
1288     ret = build_conf_principals(context, id, principal, name, &cred);
1289     if (ret)
1290         goto out;
1291
1292     /* Remove old configuration */
1293     ret = krb5_cc_remove_cred(context, id, 0, &cred);
1294     if (ret && ret != KRB5_CC_NOTFOUND)
1295         goto out;
1296
1297     if (data) {
1298         /* not that anyone care when this expire */
1299         cred.times.authtime = time(NULL);
1300         cred.times.endtime = cred.times.authtime + 3600 * 24 * 30;
1301         
1302         ret = krb5_data_copy(&cred.ticket, data->data, data->length);
1303         if (ret)
1304             goto out;
1305         
1306         ret = krb5_cc_store_cred(context, id, &cred);
1307     }
1308
1309 out:
1310     krb5_free_cred_contents (context, &cred);
1311     return ret;
1312 }
1313
1314 /**
1315  * Get some configuration for the credential cache in the cache.
1316  *
1317  * @param context a Keberos context
1318  * @param id the credential cache to store the data for
1319  * @param principal configuration for a specific principal, if
1320  * NULL, global for the whole cache.
1321  * @param name name under which the configuraion is stored.
1322  * @param data data to fetched, free with krb5_data_free()
1323  *
1324  * @ingroup krb5_ccache
1325  */
1326
1327
1328 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1329 krb5_cc_get_config(krb5_context context, krb5_ccache id,
1330                    krb5_const_principal principal,
1331                    const char *name, krb5_data *data)
1332 {
1333     krb5_creds mcred, cred;
1334     krb5_error_code ret;
1335
1336     memset(&cred, 0, sizeof(cred));
1337     krb5_data_zero(data);
1338
1339     ret = build_conf_principals(context, id, principal, name, &mcred);
1340     if (ret)
1341         goto out;
1342
1343     ret = krb5_cc_retrieve_cred(context, id, 0, &mcred, &cred);
1344     if (ret)
1345         goto out;
1346
1347     ret = krb5_data_copy(data, cred.ticket.data, cred.ticket.length);
1348
1349 out:
1350     krb5_free_cred_contents (context, &cred);
1351     krb5_free_cred_contents (context, &mcred);
1352     return ret;
1353 }
1354
1355 /*
1356  *
1357  */
1358
1359 struct krb5_cccol_cursor_data {
1360     int idx;
1361     krb5_cc_cache_cursor cursor;
1362 };
1363
1364 /**
1365  * Get a new cache interation cursor that will interate over all
1366  * credentials caches independent of type.
1367  *
1368  * @param context a Keberos context
1369  * @param cursor passed into krb5_cccol_cursor_next() and free with krb5_cccol_cursor_free().
1370  *
1371  * @return Returns 0 or and error code, see krb5_get_error_message().
1372  *
1373  * @ingroup krb5_ccache
1374  */
1375
1376 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1377 krb5_cccol_cursor_new(krb5_context context, krb5_cccol_cursor *cursor)
1378 {
1379     *cursor = calloc(1, sizeof(**cursor));
1380     if (*cursor == NULL) {
1381         krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
1382         return ENOMEM;
1383     }
1384     (*cursor)->idx = 0;
1385     (*cursor)->cursor = NULL;
1386
1387     return 0;
1388 }
1389
1390 /**
1391  * Get next credential cache from the iteration. 
1392  *
1393  * @param context A Kerberos 5 context
1394  * @param cursor the iteration cursor
1395  * @param cache the returned cursor, pointer is set to NULL on failure
1396  *        and a cache on success. The returned cache needs to be freed
1397  *        with krb5_cc_close() or destroyed with krb5_cc_destroy().
1398  *        MIT Kerberos behavies slightly diffrent and sets cache to NULL
1399  *        when all caches are iterated over and return 0.
1400  *
1401  * @return Return 0 or and error, KRB5_CC_END is returned at the end
1402  *        of iteration. See krb5_get_error_message().
1403  *
1404  * @ingroup krb5_ccache
1405  */
1406
1407
1408 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1409 krb5_cccol_cursor_next(krb5_context context, krb5_cccol_cursor cursor,
1410                        krb5_ccache *cache)
1411 {
1412     krb5_error_code ret;
1413     
1414     *cache = NULL;
1415
1416     while (cursor->idx < context->num_cc_ops) {
1417
1418         if (cursor->cursor == NULL) {
1419             ret = krb5_cc_cache_get_first (context, 
1420                                            context->cc_ops[cursor->idx]->prefix,
1421                                            &cursor->cursor);
1422             if (ret) {
1423                 cursor->idx++;
1424                 continue;
1425             }
1426         }
1427         ret = krb5_cc_cache_next(context, cursor->cursor, cache);
1428         if (ret == 0)
1429             break;
1430
1431         krb5_cc_cache_end_seq_get(context, cursor->cursor);
1432         cursor->cursor = NULL;
1433         if (ret != KRB5_CC_END)
1434             break;
1435
1436         cursor->idx++;
1437     }
1438     if (cursor->idx >= context->num_cc_ops) {
1439         krb5_set_error_message(context, KRB5_CC_END,
1440                                N_("Reached end of credential caches", ""));
1441         return KRB5_CC_END;
1442     }
1443
1444     return 0;
1445 }
1446
1447 /**
1448  * End an iteration and free all resources, can be done before end is reached.
1449  *
1450  * @param context A Kerberos 5 context
1451  * @param cursor the iteration cursor to be freed.
1452  *
1453  * @return Return 0 or and error, KRB5_CC_END is returned at the end
1454  *        of iteration. See krb5_get_error_message().
1455  *
1456  * @ingroup krb5_ccache
1457  */
1458
1459 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1460 krb5_cccol_cursor_free(krb5_context context, krb5_cccol_cursor *cursor)
1461 {
1462     krb5_cccol_cursor c = *cursor;
1463
1464     *cursor = NULL;
1465     if (c) {
1466         if (c->cursor)
1467             krb5_cc_cache_end_seq_get(context, c->cursor);
1468         free(c);
1469     }
1470     return 0;
1471 }
1472
1473 /**
1474  * Return the last time the credential cache was modified.
1475  *
1476  * @param context A Kerberos 5 context
1477  * @param id The credential cache to probe
1478  * @param mtime the last modification time, set to 0 on error.
1479
1480  * @return Return 0 or and error. See krb5_get_error_message().
1481  *
1482  * @ingroup krb5_ccache
1483  */
1484
1485
1486 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1487 krb5_cc_last_change_time(krb5_context context,
1488                          krb5_ccache id, 
1489                          krb5_timestamp *mtime)
1490 {
1491     *mtime = 0;
1492     return (*id->ops->lastchange)(context, id, mtime);
1493 }
1494
1495 /**
1496  * Return the last modfication time for a cache collection. The query
1497  * can be limited to a specific cache type. If the function return 0
1498  * and mtime is 0, there was no credentials in the caches.
1499  *
1500  * @param context A Kerberos 5 context
1501  * @param type The credential cache to probe, if NULL, all type are traversed.
1502  * @param mtime the last modification time, set to 0 on error.
1503
1504  * @return Return 0 or and error. See krb5_get_error_message().
1505  *
1506  * @ingroup krb5_ccache
1507  */
1508
1509 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1510 krb5_cccol_last_change_time(krb5_context context,
1511                             const char *type,
1512                             krb5_timestamp *mtime)
1513 {
1514     krb5_cccol_cursor cursor;
1515     krb5_error_code ret;
1516     krb5_ccache id;
1517     krb5_timestamp t = 0;
1518
1519     *mtime = 0;
1520
1521     ret = krb5_cccol_cursor_new (context, &cursor);
1522     if (ret)
1523         return ret;
1524
1525     while (krb5_cccol_cursor_next(context, cursor, &id) == 0 && id != NULL) {
1526
1527         if (type && strcmp(krb5_cc_get_type(context, id), type) != 0)
1528             continue;
1529
1530         ret = krb5_cc_last_change_time(context, id, &t);
1531         krb5_cc_close(context, id);
1532         if (ret)
1533             continue;
1534         if (t > *mtime)
1535             *mtime = t;
1536     }
1537
1538     krb5_cccol_cursor_free(context, &cursor);
1539
1540     return 0;
1541 }
1542 /**
1543  * Return a friendly name on credential cache. Free the result with krb5_xfree().
1544  *
1545  * @return Return an error code or 0, see krb5_get_error_message().
1546  *
1547  * @ingroup krb5_ccache
1548  */
1549
1550 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1551 krb5_cc_get_friendly_name(krb5_context context,
1552                           krb5_ccache id,
1553                           char **name)
1554 {
1555     krb5_error_code ret;
1556     krb5_data data;
1557
1558     ret = krb5_cc_get_config(context, id, NULL, "FriendlyName", &data);
1559     if (ret) {
1560         krb5_principal principal;
1561         ret = krb5_cc_get_principal(context, id, &principal);
1562         if (ret)
1563             return ret;
1564         ret = krb5_unparse_name(context, principal, name);
1565         krb5_free_principal(context, principal);
1566     } else {
1567         ret = asprintf(name, "%.*s", (int)data.length, (char *)data.data);
1568         krb5_data_free(&data);
1569         if (ret <= 0) {
1570             ret = ENOMEM;
1571             krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
1572         } else
1573             ret = 0;
1574     }
1575
1576     return ret;
1577 }
1578
1579 /**
1580  * Set the friendly name on credential cache.
1581  *
1582  * @return Return an error code or 0, see krb5_get_error_message().
1583  *
1584  * @ingroup krb5_ccache
1585  */
1586
1587 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1588 krb5_cc_set_friendly_name(krb5_context context,
1589                           krb5_ccache id,
1590                           const char *name)
1591 {
1592     krb5_data data;
1593
1594     data.data = rk_UNCONST(name);
1595     data.length = strlen(name);
1596
1597     return krb5_cc_set_config(context, id, NULL, "FriendlyName", &data);
1598 }
1599
1600 /**
1601  * Get the lifetime of the initial ticket in the cache
1602  *
1603  * Get the lifetime of the initial ticket in the cache, if the initial
1604  * ticket was not found, the error code KRB5_CC_END is returned.
1605  *
1606  * @param context A Kerberos 5 context.
1607  * @param id a credential cache
1608  * @param t the relative lifetime of the initial ticket
1609  *
1610  * @return Return an error code or 0, see krb5_get_error_message().
1611  *
1612  * @ingroup krb5_ccache
1613  */
1614
1615 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1616 krb5_cc_get_lifetime(krb5_context context, krb5_ccache id, time_t *t)
1617 {
1618     krb5_cc_cursor cursor;
1619     krb5_error_code ret;
1620     krb5_creds cred;
1621     time_t now;
1622
1623     *t = 0;
1624     now = time(NULL);
1625     
1626     ret = krb5_cc_start_seq_get(context, id, &cursor);
1627     if (ret)
1628         return ret;
1629
1630     while ((ret = krb5_cc_next_cred(context, id, &cursor, &cred)) == 0) {
1631         if (cred.flags.b.initial) {
1632             if (now < cred.times.endtime)
1633                 *t = cred.times.endtime - now;
1634             krb5_free_cred_contents(context, &cred);
1635             break;
1636         }
1637         krb5_free_cred_contents(context, &cred);
1638     }
1639     
1640     krb5_cc_end_seq_get(context, id, &cursor);
1641
1642     return ret;
1643 }
1644
1645 /**
1646  * Set the time offset betwen the client and the KDC
1647  *
1648  * If the backend doesn't support KDC offset, use the context global setting.
1649  *
1650  * @param context A Kerberos 5 context.
1651  * @param id a credential cache
1652  * @param offset the offset in seconds
1653  *
1654  * @return Return an error code or 0, see krb5_get_error_message().
1655  *
1656  * @ingroup krb5_ccache
1657  */
1658
1659 krb5_error_code
1660 krb5_cc_set_kdc_offset(krb5_context context, krb5_ccache id, krb5_deltat offset)
1661 {
1662     if (id->ops->set_kdc_offset == NULL) {
1663         context->kdc_sec_offset = offset;
1664         context->kdc_usec_offset = 0;
1665         return 0;
1666     }
1667     return (*id->ops->set_kdc_offset)(context, id, offset);
1668 }
1669
1670 /**
1671  * Get the time offset betwen the client and the KDC
1672  *
1673  * If the backend doesn't support KDC offset, use the context global setting.
1674  *
1675  * @param context A Kerberos 5 context.
1676  * @param id a credential cache
1677  * @param offset the offset in seconds
1678  *
1679  * @return Return an error code or 0, see krb5_get_error_message().
1680  *
1681  * @ingroup krb5_ccache
1682  */
1683
1684 krb5_error_code
1685 krb5_cc_get_kdc_offset(krb5_context context, krb5_ccache id, krb5_deltat *offset)
1686 {
1687     if (id->ops->get_kdc_offset == NULL) {
1688         *offset = context->kdc_sec_offset;
1689         return 0;
1690     }
1691     return (*id->ops->get_kdc_offset)(context, id, offset);
1692 }