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