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