]> CyberLeo.Net >> Repos - FreeBSD/releng/9.2.git/blob - crypto/heimdal/lib/krb5/acache.c
- Copy stable/9 to releng/9.2 as part of the 9.2-RELEASE cycle.
[FreeBSD/releng/9.2.git] / crypto / 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 22099 2007-12-03 17:14:34Z 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 char *
389 get_cc_name(cc_ccache_t cache)
390 {
391     cc_string_t name;
392     cc_int32 error;
393     char *str;
394
395     error = (*cache->func->get_name)(cache, &name);
396     if (error)
397         return NULL;
398
399     str = strdup(name->data);
400     (*name->func->release)(name);
401     return str;
402 }
403
404
405 static const char*
406 acc_get_name(krb5_context context,
407              krb5_ccache id)
408 {
409     krb5_acc *a = ACACHE(id);
410     static char n[255];
411     char *name;
412
413     name = get_cc_name(a->ccache);
414     if (name == NULL) {
415         krb5_set_error_string(context, "malloc: out of memory");
416         return NULL;
417     }
418     strlcpy(n, name, sizeof(n));
419     free(name);
420     return n;
421 }
422
423 static krb5_error_code
424 acc_alloc(krb5_context context, krb5_ccache *id)
425 {
426     krb5_error_code ret;
427     cc_int32 error;
428     krb5_acc *a;
429
430     ret = init_ccapi(context);
431     if (ret)
432         return ret;
433
434     ret = krb5_data_alloc(&(*id)->data, sizeof(*a));
435     if (ret) {
436         krb5_clear_error_string(context);
437         return ret;
438     }
439     
440     a = ACACHE(*id);
441
442     error = (*init_func)(&a->context, ccapi_version_3, NULL, NULL);
443     if (error) {
444         krb5_data_free(&(*id)->data);
445         return translate_cc_error(context, error);
446     }
447
448     a->cache_name = NULL;
449
450     return 0;
451 }
452
453 static krb5_error_code
454 acc_resolve(krb5_context context, krb5_ccache *id, const char *res)
455 {
456     krb5_error_code ret;
457     cc_int32 error;
458     krb5_acc *a;
459
460     ret = acc_alloc(context, id);
461     if (ret)
462         return ret;
463
464     a = ACACHE(*id);
465
466     error = (*a->context->func->open_ccache)(a->context, res,
467                                              &a->ccache);
468     if (error == 0) {
469         a->cache_name = get_cc_name(a->ccache);
470         if (a->cache_name == NULL) {
471             acc_close(context, *id);
472             *id = NULL;
473             krb5_set_error_string(context, "malloc: out of memory");
474             return ENOMEM;
475         }
476     } else if (error == ccErrCCacheNotFound) {
477         a->ccache = NULL;
478         a->cache_name = NULL;
479         error = 0;
480     } else {
481         *id = NULL;
482         return translate_cc_error(context, error);
483     }
484
485     return 0;
486 }
487
488 static krb5_error_code
489 acc_gen_new(krb5_context context, krb5_ccache *id)
490 {
491     krb5_error_code ret;
492     krb5_acc *a;
493
494     ret = acc_alloc(context, id);
495     if (ret)
496         return ret;
497
498     a = ACACHE(*id);
499
500     a->ccache = NULL;
501     a->cache_name = NULL;
502
503     return 0;
504 }
505
506 static krb5_error_code
507 acc_initialize(krb5_context context,
508                krb5_ccache id,
509                krb5_principal primary_principal)
510 {
511     krb5_acc *a = ACACHE(id);
512     krb5_error_code ret;
513     int32_t error;
514     char *name;
515
516     ret = krb5_unparse_name(context, primary_principal, &name);
517     if (ret)
518         return ret;
519
520     error = (*a->context->func->create_new_ccache)(a->context,
521                                                    cc_credentials_v5,
522                                                    name,
523                                                    &a->ccache);
524     free(name);
525
526     return translate_cc_error(context, error);
527 }
528
529 static krb5_error_code
530 acc_close(krb5_context context,
531           krb5_ccache id)
532 {
533     krb5_acc *a = ACACHE(id);
534
535     if (a->ccache) {
536         (*a->ccache->func->release)(a->ccache);
537         a->ccache = NULL;
538     }
539     if (a->cache_name) {
540         free(a->cache_name);
541         a->cache_name = NULL;
542     }
543     (*a->context->func->release)(a->context);
544     a->context = NULL;
545     krb5_data_free(&id->data);
546     return 0;
547 }
548
549 static krb5_error_code
550 acc_destroy(krb5_context context,
551             krb5_ccache id)
552 {
553     krb5_acc *a = ACACHE(id);
554     cc_int32 error = 0;
555
556     if (a->ccache) {
557         error = (*a->ccache->func->destroy)(a->ccache);
558         a->ccache = NULL;
559     }
560     if (a->context) {
561         error = (a->context->func->release)(a->context);
562         a->context = NULL;
563     }
564     return translate_cc_error(context, error);
565 }
566
567 static krb5_error_code
568 acc_store_cred(krb5_context context,
569                krb5_ccache id,
570                krb5_creds *creds)
571 {
572     krb5_acc *a = ACACHE(id);
573     cc_credentials_union cred;
574     cc_credentials_v5_t v5cred;
575     krb5_error_code ret;
576     cc_int32 error;
577     
578     if (a->ccache == NULL) {
579         krb5_set_error_string(context, "No API credential found");
580         return KRB5_CC_NOTFOUND;
581     }
582
583     cred.version = cc_credentials_v5;
584     cred.credentials.credentials_v5 = &v5cred;
585
586     ret = make_ccred_from_cred(context, 
587                                creds,
588                                &v5cred);
589     if (ret)
590         return ret;
591
592     error = (*a->ccache->func->store_credentials)(a->ccache, &cred);
593     if (error)
594         ret = translate_cc_error(context, error);
595
596     free_ccred(&v5cred);
597
598     return ret;
599 }
600
601 static krb5_error_code
602 acc_get_principal(krb5_context context,
603                   krb5_ccache id,
604                   krb5_principal *principal)
605 {
606     krb5_acc *a = ACACHE(id);
607     krb5_error_code ret;
608     int32_t error;
609     cc_string_t name;
610
611     if (a->ccache == NULL) {
612         krb5_set_error_string(context, "No API credential found");
613         return KRB5_CC_NOTFOUND;
614     }
615
616     error = (*a->ccache->func->get_principal)(a->ccache,
617                                               cc_credentials_v5,
618                                               &name);
619     if (error)
620         return translate_cc_error(context, error);
621     
622     ret = krb5_parse_name(context, name->data, principal);
623     
624     (*name->func->release)(name);
625     return ret;
626 }
627
628 static krb5_error_code
629 acc_get_first (krb5_context context,
630                krb5_ccache id,
631                krb5_cc_cursor *cursor)
632 {
633     cc_credentials_iterator_t iter;
634     krb5_acc *a = ACACHE(id);
635     int32_t error;
636     
637     if (a->ccache == NULL) {
638         krb5_set_error_string(context, "No API credential found");
639         return KRB5_CC_NOTFOUND;
640     }
641
642     error = (*a->ccache->func->new_credentials_iterator)(a->ccache, &iter);
643     if (error) {
644         krb5_clear_error_string(context);
645         return ENOENT;
646     }
647     *cursor = iter;
648     return 0;
649 }
650
651
652 static krb5_error_code
653 acc_get_next (krb5_context context,
654               krb5_ccache id,
655               krb5_cc_cursor *cursor,
656               krb5_creds *creds)
657 {
658     cc_credentials_iterator_t iter = *cursor;
659     cc_credentials_t cred;
660     krb5_error_code ret;
661     int32_t error;
662
663     while (1) {
664         error = (*iter->func->next)(iter, &cred);
665         if (error)
666             return translate_cc_error(context, error);
667         if (cred->data->version == cc_credentials_v5)
668             break;
669         (*cred->func->release)(cred);
670     }
671
672     ret = make_cred_from_ccred(context, 
673                                cred->data->credentials.credentials_v5,
674                                creds);
675     (*cred->func->release)(cred);
676     return ret;
677 }
678
679 static krb5_error_code
680 acc_end_get (krb5_context context,
681              krb5_ccache id,
682              krb5_cc_cursor *cursor)
683 {
684     cc_credentials_iterator_t iter = *cursor;
685     (*iter->func->release)(iter);
686     return 0;
687 }
688
689 static krb5_error_code
690 acc_remove_cred(krb5_context context,
691                 krb5_ccache id,
692                 krb5_flags which,
693                 krb5_creds *cred)
694 {
695     cc_credentials_iterator_t iter;
696     krb5_acc *a = ACACHE(id);
697     cc_credentials_t ccred;
698     krb5_error_code ret;
699     cc_int32 error;
700     char *client, *server;
701     
702     if (a->ccache == NULL) {
703         krb5_set_error_string(context, "No API credential found");
704         return KRB5_CC_NOTFOUND;
705     }
706
707     if (cred->client) {
708         ret = krb5_unparse_name(context, cred->client, &client);
709         if (ret)
710             return ret;
711     } else
712         client = NULL;
713
714     ret = krb5_unparse_name(context, cred->server, &server);
715     if (ret) {
716         free(client);
717         return ret;
718     }
719
720     error = (*a->ccache->func->new_credentials_iterator)(a->ccache, &iter);
721     if (error) {
722         free(server);
723         free(client);
724         return translate_cc_error(context, error);
725     }
726
727     ret = KRB5_CC_NOTFOUND;
728     while (1) {
729         cc_credentials_v5_t *v5cred;
730
731         error = (*iter->func->next)(iter, &ccred);
732         if (error)
733             break;
734
735         if (ccred->data->version != cc_credentials_v5)
736             goto next;
737
738         v5cred = ccred->data->credentials.credentials_v5;
739
740         if (client && strcmp(v5cred->client, client) != 0)
741             goto next;
742
743         if (strcmp(v5cred->server, server) != 0)
744             goto next;
745
746         (*a->ccache->func->remove_credentials)(a->ccache, ccred);
747         ret = 0;
748     next:
749         (*ccred->func->release)(ccred);
750     }
751
752     (*iter->func->release)(iter);
753
754     if (ret)
755         krb5_set_error_string(context, "Can't find credential %s in cache", 
756                               server);
757     free(server);
758     free(client);
759
760     return ret;
761 }
762
763 static krb5_error_code
764 acc_set_flags(krb5_context context,
765               krb5_ccache id,
766               krb5_flags flags)
767 {
768     return 0;
769 }
770
771 static krb5_error_code
772 acc_get_version(krb5_context context,
773                 krb5_ccache id)
774 {
775     return 0;
776 }
777                     
778 struct cache_iter {
779     cc_context_t context;
780     cc_ccache_iterator_t iter;
781 };
782
783 static krb5_error_code
784 acc_get_cache_first(krb5_context context, krb5_cc_cursor *cursor)
785 {
786     struct cache_iter *iter;
787     krb5_error_code ret;
788     cc_int32 error;
789
790     ret = init_ccapi(context);
791     if (ret)
792         return ret;
793
794     iter = calloc(1, sizeof(*iter));
795     if (iter == NULL) {
796         krb5_set_error_string(context, "malloc - out of memory");
797         return ENOMEM;
798     }
799
800     error = (*init_func)(&iter->context, ccapi_version_3, NULL, NULL);
801     if (error) {
802         free(iter);
803         return translate_cc_error(context, error);
804     }
805
806     error = (*iter->context->func->new_ccache_iterator)(iter->context,
807                                                         &iter->iter);
808     if (error) {
809         free(iter);
810         krb5_clear_error_string(context);
811         return ENOENT;
812     }
813     *cursor = iter;
814     return 0;
815 }
816
817 static krb5_error_code
818 acc_get_cache_next(krb5_context context, krb5_cc_cursor cursor, krb5_ccache *id)
819 {
820     struct cache_iter *iter = cursor;
821     cc_ccache_t cache;
822     krb5_acc *a;
823     krb5_error_code ret;
824     int32_t error;
825
826     error = (*iter->iter->func->next)(iter->iter, &cache);
827     if (error)
828         return translate_cc_error(context, error);
829
830     ret = _krb5_cc_allocate(context, &krb5_acc_ops, id);
831     if (ret) {
832         (*cache->func->release)(cache);
833         return ret;
834     }
835
836     ret = acc_alloc(context, id);
837     if (ret) {
838         (*cache->func->release)(cache);
839         free(*id);
840         return ret;
841     }
842
843     a = ACACHE(*id);
844     a->ccache = cache;
845
846     a->cache_name = get_cc_name(a->ccache);
847     if (a->cache_name == NULL) {
848         acc_close(context, *id);
849         *id = NULL;
850         krb5_set_error_string(context, "malloc: out of memory");
851         return ENOMEM;
852     }   
853     return 0;
854 }
855
856 static krb5_error_code
857 acc_end_cache_get(krb5_context context, krb5_cc_cursor cursor)
858 {
859     struct cache_iter *iter = cursor;
860
861     (*iter->iter->func->release)(iter->iter);
862     iter->iter = NULL;
863     (*iter->context->func->release)(iter->context);
864     iter->context = NULL;
865     free(iter);
866     return 0;
867 }
868
869 static krb5_error_code
870 acc_move(krb5_context context, krb5_ccache from, krb5_ccache to)
871 {
872     krb5_acc *afrom = ACACHE(from);
873     krb5_acc *ato = ACACHE(to);
874     int32_t error;
875
876     if (ato->ccache == NULL) {
877         cc_string_t name;
878
879         error = (*afrom->ccache->func->get_principal)(afrom->ccache,
880                                                       cc_credentials_v5,
881                                                       &name);
882         if (error)
883             return translate_cc_error(context, error);
884     
885         error = (*ato->context->func->create_new_ccache)(ato->context,
886                                                          cc_credentials_v5,
887                                                          name->data,
888                                                          &ato->ccache);
889         (*name->func->release)(name);
890         if (error)
891             return translate_cc_error(context, error);
892     }
893
894
895     error = (*ato->ccache->func->move)(afrom->ccache, ato->ccache);
896     return translate_cc_error(context, error);
897 }
898
899 static krb5_error_code
900 acc_default_name(krb5_context context, char **str)
901 {
902     krb5_error_code ret;
903     cc_context_t cc;
904     cc_string_t name;
905     int32_t error;
906
907     ret = init_ccapi(context);
908     if (ret)
909         return ret;
910
911     error = (*init_func)(&cc, ccapi_version_3, NULL, NULL);
912     if (error)
913         return translate_cc_error(context, error);
914
915     error = (*cc->func->get_default_ccache_name)(cc, &name);
916     if (error) {
917         (*cc->func->release)(cc);
918         return translate_cc_error(context, error);
919     }
920         
921     asprintf(str, "API:%s", name->data);
922     (*name->func->release)(name);
923     (*cc->func->release)(cc);
924
925     if (*str == NULL) {
926         krb5_set_error_string(context, "out of memory");
927         return ENOMEM;
928     }
929     return 0;
930 }
931
932
933 /**
934  * Variable containing the API based credential cache implemention.
935  *
936  * @ingroup krb5_ccache
937  */
938
939 const krb5_cc_ops krb5_acc_ops = {
940     "API",
941     acc_get_name,
942     acc_resolve,
943     acc_gen_new,
944     acc_initialize,
945     acc_destroy,
946     acc_close,
947     acc_store_cred,
948     NULL, /* acc_retrieve */
949     acc_get_principal,
950     acc_get_first,
951     acc_get_next,
952     acc_end_get,
953     acc_remove_cred,
954     acc_set_flags,
955     acc_get_version,
956     acc_get_cache_first,
957     acc_get_cache_next,
958     acc_end_cache_get,
959     acc_move,
960     acc_default_name
961 };