xref: /freebsd/crypto/heimdal/lib/krb5/acache.c (revision aa0a1e58)
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 };
962