heimdal: update to lorikeet-heimdal rev 801
[tprouty/samba.git] / source4 / heimdal / lib / krb5 / acache.c
1 /*
2  * Copyright (c) 2004 - 2007 Kungliga Tekniska Högskolan
3  * (Royal Institute of Technology, Stockholm, Sweden). 
4  * All rights reserved. 
5  *
6  * Redistribution and use in source and binary forms, with or without 
7  * modification, are permitted provided that the following conditions 
8  * are met: 
9  *
10  * 1. Redistributions of source code must retain the above copyright 
11  *    notice, this list of conditions and the following disclaimer. 
12  *
13  * 2. Redistributions in binary form must reproduce the above copyright 
14  *    notice, this list of conditions and the following disclaimer in the 
15  *    documentation and/or other materials provided with the distribution. 
16  *
17  * 3. Neither the name of the Institute nor the names of its contributors 
18  *    may be used to endorse or promote products derived from this software 
19  *    without specific prior written permission. 
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND 
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE 
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 
31  * SUCH DAMAGE. 
32  */
33
34 #include "krb5_locl.h"
35 #include <krb5_ccapi.h>
36 #ifdef HAVE_DLFCN_H
37 #include <dlfcn.h>
38 #endif
39
40 RCSID("$Id: acache.c 23316 2008-06-23 04:32:32Z lha $");
41
42 /* XXX should we fetch these for each open ? */
43 static HEIMDAL_MUTEX acc_mutex = HEIMDAL_MUTEX_INITIALIZER;
44 static cc_initialize_func init_func;
45
46 #ifdef HAVE_DLOPEN
47 static void *cc_handle; 
48 #endif
49
50 typedef struct krb5_acc {
51     char *cache_name;
52     cc_context_t context;
53     cc_ccache_t ccache;
54 } krb5_acc;
55
56 static krb5_error_code acc_close(krb5_context, krb5_ccache);
57
58 #define ACACHE(X) ((krb5_acc *)(X)->data.data)
59
60 static const struct {
61     cc_int32 error;
62     krb5_error_code ret;
63 } cc_errors[] = {
64     { ccErrBadName,             KRB5_CC_BADNAME },
65     { ccErrCredentialsNotFound, KRB5_CC_NOTFOUND },
66     { ccErrCCacheNotFound,      KRB5_FCC_NOFILE },
67     { ccErrContextNotFound,     KRB5_CC_NOTFOUND },
68     { ccIteratorEnd,            KRB5_CC_END },
69     { ccErrNoMem,               KRB5_CC_NOMEM },
70     { ccErrServerUnavailable,   KRB5_CC_NOSUPP },
71     { ccErrInvalidCCache,       KRB5_CC_BADNAME },
72     { ccNoError,                0 }
73 };
74
75 static krb5_error_code
76 translate_cc_error(krb5_context context, cc_int32 error)
77 {
78     int i;
79     krb5_clear_error_string(context);
80     for(i = 0; i < sizeof(cc_errors)/sizeof(cc_errors[0]); i++)
81         if (cc_errors[i].error == error)
82             return cc_errors[i].ret;
83     return KRB5_FCC_INTERNAL;
84 }
85
86 static krb5_error_code
87 init_ccapi(krb5_context context)
88 {
89     const char *lib;
90
91     HEIMDAL_MUTEX_lock(&acc_mutex);
92     if (init_func) {
93         HEIMDAL_MUTEX_unlock(&acc_mutex);
94         krb5_clear_error_string(context);
95         return 0;
96     }
97
98     lib = krb5_config_get_string(context, NULL,
99                                  "libdefaults", "ccapi_library", 
100                                  NULL);
101     if (lib == NULL) {
102 #ifdef __APPLE__
103         lib = "/System/Library/Frameworks/Kerberos.framework/Kerberos";
104 #else
105         lib = "/usr/lib/libkrb5_cc.so";
106 #endif
107     }
108
109 #ifdef HAVE_DLOPEN
110
111 #ifndef RTLD_LAZY
112 #define RTLD_LAZY 0
113 #endif
114
115     cc_handle = dlopen(lib, RTLD_LAZY);
116     if (cc_handle == NULL) {
117         HEIMDAL_MUTEX_unlock(&acc_mutex);
118         krb5_set_error_message(context, KRB5_CC_NOSUPP, 
119                                "Failed to load %s", lib);
120         return KRB5_CC_NOSUPP;
121     }
122
123     init_func = (cc_initialize_func)dlsym(cc_handle, "cc_initialize");
124     HEIMDAL_MUTEX_unlock(&acc_mutex);
125     if (init_func == NULL) {
126         krb5_set_error_message(context, KRB5_CC_NOSUPP,
127                                "Failed to find cc_initialize"
128                                "in %s: %s", lib, dlerror());
129         dlclose(cc_handle);
130         return KRB5_CC_NOSUPP;
131     }
132
133     return 0;
134 #else
135     HEIMDAL_MUTEX_unlock(&acc_mutex);
136     krb5_set_error_message(context, KRB5_CC_NOSUPP, "no support for shared object");
137     return KRB5_CC_NOSUPP;
138 #endif
139 }    
140
141 static krb5_error_code
142 make_cred_from_ccred(krb5_context context,
143                      const cc_credentials_v5_t *incred,
144                      krb5_creds *cred)
145 {
146     krb5_error_code ret;
147     unsigned int i;
148
149     memset(cred, 0, sizeof(*cred));
150
151     ret = krb5_parse_name(context, incred->client, &cred->client);
152     if (ret)
153         goto fail;
154
155     ret = krb5_parse_name(context, incred->server, &cred->server);
156     if (ret)
157         goto fail;
158
159     cred->session.keytype = incred->keyblock.type;
160     cred->session.keyvalue.length = incred->keyblock.length;
161     cred->session.keyvalue.data = malloc(incred->keyblock.length);
162     if (cred->session.keyvalue.data == NULL)
163         goto nomem;
164     memcpy(cred->session.keyvalue.data, incred->keyblock.data,
165            incred->keyblock.length);
166
167     cred->times.authtime = incred->authtime;
168     cred->times.starttime = incred->starttime;
169     cred->times.endtime = incred->endtime;
170     cred->times.renew_till = incred->renew_till;
171
172     ret = krb5_data_copy(&cred->ticket,
173                          incred->ticket.data,
174                          incred->ticket.length);
175     if (ret)
176         goto nomem;
177
178     ret = krb5_data_copy(&cred->second_ticket,
179                          incred->second_ticket.data,
180                          incred->second_ticket.length);
181     if (ret)
182         goto nomem;
183
184     cred->authdata.val = NULL;
185     cred->authdata.len = 0;
186     
187     cred->addresses.val = NULL;
188     cred->addresses.len = 0;
189     
190     for (i = 0; incred->authdata && incred->authdata[i]; i++)
191         ;
192     
193     if (i) {
194         cred->authdata.val = calloc(i, sizeof(cred->authdata.val[0]));
195         if (cred->authdata.val == NULL)
196             goto nomem;
197         cred->authdata.len = i;
198         for (i = 0; i < cred->authdata.len; i++) {
199             cred->authdata.val[i].ad_type = incred->authdata[i]->type;
200             ret = krb5_data_copy(&cred->authdata.val[i].ad_data,
201                                  incred->authdata[i]->data,
202                                  incred->authdata[i]->length);
203             if (ret)
204                 goto nomem;
205         }
206     }
207     
208     for (i = 0; incred->addresses && incred->addresses[i]; i++)
209         ;
210     
211     if (i) {
212         cred->addresses.val = calloc(i, sizeof(cred->addresses.val[0]));
213         if (cred->addresses.val == NULL)
214             goto nomem;
215         cred->addresses.len = i;
216         
217         for (i = 0; i < cred->addresses.len; i++) {
218             cred->addresses.val[i].addr_type = incred->addresses[i]->type;
219             ret = krb5_data_copy(&cred->addresses.val[i].address,
220                                  incred->addresses[i]->data,
221                                  incred->addresses[i]->length);
222             if (ret)
223                 goto nomem;
224         }
225     }
226     
227     cred->flags.i = 0;
228     if (incred->ticket_flags & KRB5_CCAPI_TKT_FLG_FORWARDABLE)
229         cred->flags.b.forwardable = 1;
230     if (incred->ticket_flags & KRB5_CCAPI_TKT_FLG_FORWARDED)
231         cred->flags.b.forwarded = 1;
232     if (incred->ticket_flags & KRB5_CCAPI_TKT_FLG_PROXIABLE)
233         cred->flags.b.proxiable = 1;
234     if (incred->ticket_flags & KRB5_CCAPI_TKT_FLG_PROXY)
235         cred->flags.b.proxy = 1;
236     if (incred->ticket_flags & KRB5_CCAPI_TKT_FLG_MAY_POSTDATE)
237         cred->flags.b.may_postdate = 1;
238     if (incred->ticket_flags & KRB5_CCAPI_TKT_FLG_POSTDATED)
239         cred->flags.b.postdated = 1;
240     if (incred->ticket_flags & KRB5_CCAPI_TKT_FLG_INVALID)
241         cred->flags.b.invalid = 1;
242     if (incred->ticket_flags & KRB5_CCAPI_TKT_FLG_RENEWABLE)
243         cred->flags.b.renewable = 1;
244     if (incred->ticket_flags & KRB5_CCAPI_TKT_FLG_INITIAL)
245         cred->flags.b.initial = 1;
246     if (incred->ticket_flags & KRB5_CCAPI_TKT_FLG_PRE_AUTH)
247         cred->flags.b.pre_authent = 1;
248     if (incred->ticket_flags & KRB5_CCAPI_TKT_FLG_HW_AUTH)
249         cred->flags.b.hw_authent = 1;
250     if (incred->ticket_flags & KRB5_CCAPI_TKT_FLG_TRANSIT_POLICY_CHECKED)
251         cred->flags.b.transited_policy_checked = 1;
252     if (incred->ticket_flags & KRB5_CCAPI_TKT_FLG_OK_AS_DELEGATE)
253         cred->flags.b.ok_as_delegate = 1;
254     if (incred->ticket_flags & KRB5_CCAPI_TKT_FLG_ANONYMOUS)
255         cred->flags.b.anonymous = 1;
256
257     return 0;
258     
259 nomem:
260     ret = ENOMEM;
261     krb5_set_error_message(context, ret, "malloc: out of memory");
262     
263 fail:
264     krb5_free_cred_contents(context, cred);
265     return ret;
266 }
267
268 static void
269 free_ccred(cc_credentials_v5_t *cred)
270 {
271     int i;
272
273     if (cred->addresses) {
274         for (i = 0; cred->addresses[i] != 0; i++) {
275             if (cred->addresses[i]->data)
276                 free(cred->addresses[i]->data);
277             free(cred->addresses[i]);
278         }
279         free(cred->addresses);
280     }
281     if (cred->server)
282         free(cred->server);
283     if (cred->client)
284         free(cred->client);
285     memset(cred, 0, sizeof(*cred));
286 }
287
288 static krb5_error_code
289 make_ccred_from_cred(krb5_context context,
290                      const krb5_creds *incred,
291                      cc_credentials_v5_t *cred)
292 {
293     krb5_error_code ret;
294     int i;
295
296     memset(cred, 0, sizeof(*cred));
297
298     ret = krb5_unparse_name(context, incred->client, &cred->client);
299     if (ret)
300         goto fail;
301
302     ret = krb5_unparse_name(context, incred->server, &cred->server);
303     if (ret)
304         goto fail;
305
306     cred->keyblock.type = incred->session.keytype;
307     cred->keyblock.length = incred->session.keyvalue.length;
308     cred->keyblock.data = incred->session.keyvalue.data;
309
310     cred->authtime = incred->times.authtime;
311     cred->starttime = incred->times.starttime;
312     cred->endtime = incred->times.endtime;
313     cred->renew_till = incred->times.renew_till;
314
315     cred->ticket.length = incred->ticket.length;
316     cred->ticket.data = incred->ticket.data;
317
318     cred->second_ticket.length = incred->second_ticket.length;
319     cred->second_ticket.data = incred->second_ticket.data;
320
321     /* XXX this one should also be filled in */
322     cred->authdata = NULL;
323     
324     cred->addresses = calloc(incred->addresses.len + 1, 
325                              sizeof(cred->addresses[0]));
326     if (cred->addresses == NULL) {
327
328         ret = ENOMEM;
329         goto fail;
330     }
331
332     for (i = 0; i < incred->addresses.len; i++) {
333         cc_data *addr;
334         addr = malloc(sizeof(*addr));
335         if (addr == NULL) {
336             ret = ENOMEM;
337             goto fail;
338         }
339         addr->type = incred->addresses.val[i].addr_type;
340         addr->length = incred->addresses.val[i].address.length;
341         addr->data = malloc(addr->length);
342         if (addr->data == NULL) {
343             ret = ENOMEM;
344             goto fail;
345         }
346         memcpy(addr->data, incred->addresses.val[i].address.data, 
347                addr->length);
348         cred->addresses[i] = addr;
349     }
350     cred->addresses[i] = NULL;
351
352     cred->ticket_flags = 0;
353     if (incred->flags.b.forwardable)
354         cred->ticket_flags |= KRB5_CCAPI_TKT_FLG_FORWARDABLE;
355     if (incred->flags.b.forwarded)
356         cred->ticket_flags |= KRB5_CCAPI_TKT_FLG_FORWARDED;
357     if (incred->flags.b.proxiable)
358         cred->ticket_flags |= KRB5_CCAPI_TKT_FLG_PROXIABLE;
359     if (incred->flags.b.proxy)
360         cred->ticket_flags |= KRB5_CCAPI_TKT_FLG_PROXY;
361     if (incred->flags.b.may_postdate)
362         cred->ticket_flags |= KRB5_CCAPI_TKT_FLG_MAY_POSTDATE;
363     if (incred->flags.b.postdated)
364         cred->ticket_flags |= KRB5_CCAPI_TKT_FLG_POSTDATED;
365     if (incred->flags.b.invalid)
366         cred->ticket_flags |= KRB5_CCAPI_TKT_FLG_INVALID;
367     if (incred->flags.b.renewable)
368         cred->ticket_flags |= KRB5_CCAPI_TKT_FLG_RENEWABLE;
369     if (incred->flags.b.initial)
370         cred->ticket_flags |= KRB5_CCAPI_TKT_FLG_INITIAL;
371     if (incred->flags.b.pre_authent)
372         cred->ticket_flags |= KRB5_CCAPI_TKT_FLG_PRE_AUTH;
373     if (incred->flags.b.hw_authent)
374         cred->ticket_flags |= KRB5_CCAPI_TKT_FLG_HW_AUTH;
375     if (incred->flags.b.transited_policy_checked)
376         cred->ticket_flags |= KRB5_CCAPI_TKT_FLG_TRANSIT_POLICY_CHECKED;
377     if (incred->flags.b.ok_as_delegate)
378         cred->ticket_flags |= KRB5_CCAPI_TKT_FLG_OK_AS_DELEGATE;
379     if (incred->flags.b.anonymous)
380         cred->ticket_flags |= KRB5_CCAPI_TKT_FLG_ANONYMOUS;
381
382     return 0;
383
384 fail:    
385     free_ccred(cred);
386
387     krb5_clear_error_string(context);
388     return ret;
389 }
390
391 static cc_int32
392 get_cc_name(krb5_acc *a)
393 {
394     cc_string_t name;
395     cc_int32 error;
396
397     error = (*a->ccache->func->get_name)(a->ccache, &name);
398     if (error)
399         return error;
400
401     a->cache_name = strdup(name->data);
402     (*name->func->release)(name);
403     if (a->cache_name == NULL)
404         return ccErrNoMem;
405     return ccNoError;
406 }
407
408
409 static const char*
410 acc_get_name(krb5_context context,
411              krb5_ccache id)
412 {
413     krb5_acc *a = ACACHE(id);
414     int32_t error;
415
416     if (a->cache_name == NULL) {
417         krb5_error_code ret;
418         krb5_principal principal;
419         char *name;
420
421         ret = _krb5_get_default_principal_local(context, &principal);
422         if (ret)
423             return NULL;
424
425         ret = krb5_unparse_name(context, principal, &name);
426         krb5_free_principal(context, principal);
427         if (ret)
428             return NULL;
429
430         error = (*a->context->func->create_new_ccache)(a->context,
431                                                        cc_credentials_v5,
432                                                        name,
433                                                        &a->ccache);
434         krb5_xfree(name);
435         if (error)
436             return NULL;
437
438         error = get_cc_name(a);
439         if (error)
440             return NULL;
441     }    
442
443     return a->cache_name;
444 }
445
446 static krb5_error_code
447 acc_alloc(krb5_context context, krb5_ccache *id)
448 {
449     krb5_error_code ret;
450     cc_int32 error;
451     krb5_acc *a;
452
453     ret = init_ccapi(context);
454     if (ret)
455         return ret;
456
457     ret = krb5_data_alloc(&(*id)->data, sizeof(*a));
458     if (ret) {
459         krb5_clear_error_string(context);
460         return ret;
461     }
462     
463     a = ACACHE(*id);
464
465     error = (*init_func)(&a->context, ccapi_version_3, NULL, NULL);
466     if (error) {
467         krb5_data_free(&(*id)->data);
468         return translate_cc_error(context, error);
469     }
470
471     a->cache_name = NULL;
472
473     return 0;
474 }
475
476 static krb5_error_code
477 acc_resolve(krb5_context context, krb5_ccache *id, const char *res)
478 {
479     krb5_error_code ret;
480     cc_int32 error;
481     krb5_acc *a;
482
483     ret = acc_alloc(context, id);
484     if (ret)
485         return ret;
486
487     a = ACACHE(*id);
488
489     error = (*a->context->func->open_ccache)(a->context, res, &a->ccache);
490     if (error == ccNoError) {
491         error = get_cc_name(a);
492         if (error != ccNoError) {
493             acc_close(context, *id);
494             *id = NULL;
495             return translate_cc_error(context, error);
496         }
497     } else if (error == ccErrCCacheNotFound) {
498         a->ccache = NULL;
499         a->cache_name = NULL;
500         error = 0;
501     } else {
502         *id = NULL;
503         return translate_cc_error(context, error);
504     }
505
506     return 0;
507 }
508
509 static krb5_error_code
510 acc_gen_new(krb5_context context, krb5_ccache *id)
511 {
512     krb5_error_code ret;
513     krb5_acc *a;
514
515     ret = acc_alloc(context, id);
516     if (ret)
517         return ret;
518
519     a = ACACHE(*id);
520
521     a->ccache = NULL;
522     a->cache_name = NULL;
523
524     return 0;
525 }
526
527 static krb5_error_code
528 acc_initialize(krb5_context context,
529                krb5_ccache id,
530                krb5_principal primary_principal)
531 {
532     krb5_acc *a = ACACHE(id);
533     krb5_error_code ret;
534     int32_t error;
535     char *name;
536
537     ret = krb5_unparse_name(context, primary_principal, &name);
538     if (ret)
539         return ret;
540
541     if (a->cache_name == NULL) {
542         error = (*a->context->func->create_new_ccache)(a->context,
543                                                        cc_credentials_v5,
544                                                        name,
545                                                        &a->ccache);
546         free(name);
547         if (error == ccNoError)
548             error = get_cc_name(a);
549     } else {
550         cc_credentials_iterator_t iter;
551         cc_credentials_t ccred;
552
553         error = (*a->ccache->func->new_credentials_iterator)(a->ccache, &iter);
554         if (error) {
555             free(name);
556             return translate_cc_error(context, error);
557         }
558
559         while (1) {
560             error = (*iter->func->next)(iter, &ccred);
561             if (error)
562                 break;
563             (*a->ccache->func->remove_credentials)(a->ccache, ccred);
564             (*ccred->func->release)(ccred);
565         }
566         (*iter->func->release)(iter);
567
568         error = (*a->ccache->func->set_principal)(a->ccache,
569                                                   cc_credentials_v5,
570                                                   name);
571     }
572
573     return translate_cc_error(context, error);
574 }
575
576 static krb5_error_code
577 acc_close(krb5_context context,
578           krb5_ccache id)
579 {
580     krb5_acc *a = ACACHE(id);
581
582     if (a->ccache) {
583         (*a->ccache->func->release)(a->ccache);
584         a->ccache = NULL;
585     }
586     if (a->cache_name) {
587         free(a->cache_name);
588         a->cache_name = NULL;
589     }
590     if (a->context) {
591         (*a->context->func->release)(a->context);
592         a->context = NULL;
593     }
594     krb5_data_free(&id->data);
595     return 0;
596 }
597
598 static krb5_error_code
599 acc_destroy(krb5_context context,
600             krb5_ccache id)
601 {
602     krb5_acc *a = ACACHE(id);
603     cc_int32 error = 0;
604
605     if (a->ccache) {
606         error = (*a->ccache->func->destroy)(a->ccache);
607         a->ccache = NULL;
608     }
609     if (a->context) {
610         error = (a->context->func->release)(a->context);
611         a->context = NULL;
612     }
613     return translate_cc_error(context, error);
614 }
615
616 static krb5_error_code
617 acc_store_cred(krb5_context context,
618                krb5_ccache id,
619                krb5_creds *creds)
620 {
621     krb5_acc *a = ACACHE(id);
622     cc_credentials_union cred;
623     cc_credentials_v5_t v5cred;
624     krb5_error_code ret;
625     cc_int32 error;
626     
627     if (a->ccache == NULL) {
628         krb5_set_error_message(context, KRB5_CC_NOTFOUND,
629                                "No API credential found");
630         return KRB5_CC_NOTFOUND;
631     }
632
633     cred.version = cc_credentials_v5;
634     cred.credentials.credentials_v5 = &v5cred;
635
636     ret = make_ccred_from_cred(context, 
637                                creds,
638                                &v5cred);
639     if (ret)
640         return ret;
641
642     error = (*a->ccache->func->store_credentials)(a->ccache, &cred);
643     if (error)
644         ret = translate_cc_error(context, error);
645
646     free_ccred(&v5cred);
647
648     return ret;
649 }
650
651 static krb5_error_code
652 acc_get_principal(krb5_context context,
653                   krb5_ccache id,
654                   krb5_principal *principal)
655 {
656     krb5_acc *a = ACACHE(id);
657     krb5_error_code ret;
658     int32_t error;
659     cc_string_t name;
660
661     if (a->ccache == NULL) {
662         krb5_set_error_message(context, KRB5_CC_NOTFOUND,
663                                "No API credential found");
664         return KRB5_CC_NOTFOUND;
665     }
666
667     error = (*a->ccache->func->get_principal)(a->ccache,
668                                               cc_credentials_v5,
669                                               &name);
670     if (error)
671         return translate_cc_error(context, error);
672     
673     ret = krb5_parse_name(context, name->data, principal);
674     
675     (*name->func->release)(name);
676     return ret;
677 }
678
679 static krb5_error_code
680 acc_get_first (krb5_context context,
681                krb5_ccache id,
682                krb5_cc_cursor *cursor)
683 {
684     cc_credentials_iterator_t iter;
685     krb5_acc *a = ACACHE(id);
686     int32_t error;
687     
688     if (a->ccache == NULL) {
689         krb5_set_error_message(context, KRB5_CC_NOTFOUND,
690                                "No API credential found");
691         return KRB5_CC_NOTFOUND;
692     }
693
694     error = (*a->ccache->func->new_credentials_iterator)(a->ccache, &iter);
695     if (error) {
696         krb5_clear_error_string(context);
697         return ENOENT;
698     }
699     *cursor = iter;
700     return 0;
701 }
702
703
704 static krb5_error_code
705 acc_get_next (krb5_context context,
706               krb5_ccache id,
707               krb5_cc_cursor *cursor,
708               krb5_creds *creds)
709 {
710     cc_credentials_iterator_t iter = *cursor;
711     cc_credentials_t cred;
712     krb5_error_code ret;
713     int32_t error;
714
715     while (1) {
716         error = (*iter->func->next)(iter, &cred);
717         if (error)
718             return translate_cc_error(context, error);
719         if (cred->data->version == cc_credentials_v5)
720             break;
721         (*cred->func->release)(cred);
722     }
723
724     ret = make_cred_from_ccred(context, 
725                                cred->data->credentials.credentials_v5,
726                                creds);
727     (*cred->func->release)(cred);
728     return ret;
729 }
730
731 static krb5_error_code
732 acc_end_get (krb5_context context,
733              krb5_ccache id,
734              krb5_cc_cursor *cursor)
735 {
736     cc_credentials_iterator_t iter = *cursor;
737     (*iter->func->release)(iter);
738     return 0;
739 }
740
741 static krb5_error_code
742 acc_remove_cred(krb5_context context,
743                 krb5_ccache id,
744                 krb5_flags which,
745                 krb5_creds *cred)
746 {
747     cc_credentials_iterator_t iter;
748     krb5_acc *a = ACACHE(id);
749     cc_credentials_t ccred;
750     krb5_error_code ret;
751     cc_int32 error;
752     char *client, *server;
753     
754     if (a->ccache == NULL) {
755         krb5_set_error_message(context, KRB5_CC_NOTFOUND,
756                                "No API credential found");
757         return KRB5_CC_NOTFOUND;
758     }
759
760     if (cred->client) {
761         ret = krb5_unparse_name(context, cred->client, &client);
762         if (ret)
763             return ret;
764     } else
765         client = NULL;
766
767     ret = krb5_unparse_name(context, cred->server, &server);
768     if (ret) {
769         free(client);
770         return ret;
771     }
772
773     error = (*a->ccache->func->new_credentials_iterator)(a->ccache, &iter);
774     if (error) {
775         free(server);
776         free(client);
777         return translate_cc_error(context, error);
778     }
779
780     ret = KRB5_CC_NOTFOUND;
781     while (1) {
782         cc_credentials_v5_t *v5cred;
783
784         error = (*iter->func->next)(iter, &ccred);
785         if (error)
786             break;
787
788         if (ccred->data->version != cc_credentials_v5)
789             goto next;
790
791         v5cred = ccred->data->credentials.credentials_v5;
792
793         if (client && strcmp(v5cred->client, client) != 0)
794             goto next;
795
796         if (strcmp(v5cred->server, server) != 0)
797             goto next;
798
799         (*a->ccache->func->remove_credentials)(a->ccache, ccred);
800         ret = 0;
801     next:
802         (*ccred->func->release)(ccred);
803     }
804
805     (*iter->func->release)(iter);
806
807     if (ret)
808         krb5_set_error_message(context, ret,
809                                "Can't find credential %s in cache", server);
810     free(server);
811     free(client);
812
813     return ret;
814 }
815
816 static krb5_error_code
817 acc_set_flags(krb5_context context,
818               krb5_ccache id,
819               krb5_flags flags)
820 {
821     return 0;
822 }
823
824 static int
825 acc_get_version(krb5_context context,
826                 krb5_ccache id)
827 {
828     return 0;
829 }
830                     
831 struct cache_iter {
832     cc_context_t context;
833     cc_ccache_iterator_t iter;
834 };
835
836 static krb5_error_code
837 acc_get_cache_first(krb5_context context, krb5_cc_cursor *cursor)
838 {
839     struct cache_iter *iter;
840     krb5_error_code ret;
841     cc_int32 error;
842
843     ret = init_ccapi(context);
844     if (ret)
845         return ret;
846
847     iter = calloc(1, sizeof(*iter));
848     if (iter == NULL) {
849         krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
850         return ENOMEM;
851     }
852
853     error = (*init_func)(&iter->context, ccapi_version_3, NULL, NULL);
854     if (error) {
855         free(iter);
856         return translate_cc_error(context, error);
857     }
858
859     error = (*iter->context->func->new_ccache_iterator)(iter->context,
860                                                         &iter->iter);
861     if (error) {
862         free(iter);
863         krb5_clear_error_string(context);
864         return ENOENT;
865     }
866     *cursor = iter;
867     return 0;
868 }
869
870 static krb5_error_code
871 acc_get_cache_next(krb5_context context, krb5_cc_cursor cursor, krb5_ccache *id)
872 {
873     struct cache_iter *iter = cursor;
874     cc_ccache_t cache;
875     krb5_acc *a;
876     krb5_error_code ret;
877     int32_t error;
878
879     error = (*iter->iter->func->next)(iter->iter, &cache);
880     if (error)
881         return translate_cc_error(context, error);
882
883     ret = _krb5_cc_allocate(context, &krb5_acc_ops, id);
884     if (ret) {
885         (*cache->func->release)(cache);
886         return ret;
887     }
888
889     ret = acc_alloc(context, id);
890     if (ret) {
891         (*cache->func->release)(cache);
892         free(*id);
893         return ret;
894     }
895
896     a = ACACHE(*id);
897     a->ccache = cache;
898
899     error = get_cc_name(a);
900     if (error) {
901         acc_close(context, *id);
902         *id = NULL;
903         return translate_cc_error(context, error);
904     }   
905     return 0;
906 }
907
908 static krb5_error_code
909 acc_end_cache_get(krb5_context context, krb5_cc_cursor cursor)
910 {
911     struct cache_iter *iter = cursor;
912
913     (*iter->iter->func->release)(iter->iter);
914     iter->iter = NULL;
915     (*iter->context->func->release)(iter->context);
916     iter->context = NULL;
917     free(iter);
918     return 0;
919 }
920
921 static krb5_error_code
922 acc_move(krb5_context context, krb5_ccache from, krb5_ccache to)
923 {
924     krb5_acc *afrom = ACACHE(from);
925     krb5_acc *ato = ACACHE(to);
926     int32_t error;
927
928     if (ato->ccache == NULL) {
929         cc_string_t name;
930
931         error = (*afrom->ccache->func->get_principal)(afrom->ccache,
932                                                       cc_credentials_v5,
933                                                       &name);
934         if (error)
935             return translate_cc_error(context, error);
936     
937         error = (*ato->context->func->create_new_ccache)(ato->context,
938                                                          cc_credentials_v5,
939                                                          name->data,
940                                                          &ato->ccache);
941         (*name->func->release)(name);
942         if (error)
943             return translate_cc_error(context, error);
944     }
945
946
947     error = (*ato->ccache->func->move)(afrom->ccache, ato->ccache);
948     return translate_cc_error(context, error);
949 }
950
951 static krb5_error_code
952 acc_get_default_name(krb5_context context, char **str)
953 {
954     krb5_error_code ret;
955     cc_context_t cc;
956     cc_string_t name;
957     int32_t error;
958
959     ret = init_ccapi(context);
960     if (ret)
961         return ret;
962
963     error = (*init_func)(&cc, ccapi_version_3, NULL, NULL);
964     if (error)
965         return translate_cc_error(context, error);
966
967     error = (*cc->func->get_default_ccache_name)(cc, &name);
968     if (error) {
969         (*cc->func->release)(cc);
970         return translate_cc_error(context, error);
971     }
972         
973     asprintf(str, "API:%s", name->data);
974     (*name->func->release)(name);
975     (*cc->func->release)(cc);
976
977     if (*str == NULL) {
978         krb5_set_error_message(context, ENOMEM, "out of memory");
979         return ENOMEM;
980     }
981     return 0;
982 }
983
984 static krb5_error_code
985 acc_set_default(krb5_context context, krb5_ccache id)
986 {
987     krb5_acc *a = ACACHE(id);
988     cc_int32 error;
989     
990     if (a->ccache == NULL) {
991         krb5_set_error_message(context, KRB5_CC_NOTFOUND,
992                                "No API credential found");
993         return KRB5_CC_NOTFOUND;
994     }
995
996     error = (*a->ccache->func->set_default)(a->ccache);
997     if (error)
998         return translate_cc_error(context, error);
999
1000     return 0;
1001 }
1002
1003 /**
1004  * Variable containing the API based credential cache implemention.
1005  *
1006  * @ingroup krb5_ccache
1007  */
1008
1009 KRB5_LIB_VARIABLE const krb5_cc_ops krb5_acc_ops = {
1010     KRB5_CC_OPS_VERSION,
1011     "API",
1012     acc_get_name,
1013     acc_resolve,
1014     acc_gen_new,
1015     acc_initialize,
1016     acc_destroy,
1017     acc_close,
1018     acc_store_cred,
1019     NULL, /* acc_retrieve */
1020     acc_get_principal,
1021     acc_get_first,
1022     acc_get_next,
1023     acc_end_get,
1024     acc_remove_cred,
1025     acc_set_flags,
1026     acc_get_version,
1027     acc_get_cache_first,
1028     acc_get_cache_next,
1029     acc_end_cache_get,
1030     acc_move,
1031     acc_get_default_name,
1032     acc_set_default
1033 };