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