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
40RCSID("$Id: acache.c 22099 2007-12-03 17:14:34Z lha $");
41
42/* XXX should we fetch these for each open ? */
43static HEIMDAL_MUTEX acc_mutex = HEIMDAL_MUTEX_INITIALIZER;
44static cc_initialize_func init_func;
45
46#ifdef HAVE_DLOPEN
47static void *cc_handle;
48#endif
49
50typedef struct krb5_acc {
51    char *cache_name;
52    cc_context_t context;
53    cc_ccache_t ccache;
54} krb5_acc;
55
56static krb5_error_code acc_close(krb5_context, krb5_ccache);
57
58#define ACACHE(X) ((krb5_acc *)(X)->data.data)
59
60static 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
74static krb5_error_code
75translate_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
85static krb5_error_code
86init_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
138static krb5_error_code
139make_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
256nomem:
257    ret = ENOMEM;
258    krb5_set_error_string(context, "malloc - out of memory");
259
260fail:
261    krb5_free_cred_contents(context, cred);
262    return ret;
263}
264
265static void
266free_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
285static krb5_error_code
286make_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
381fail:
382    free_ccred(cred);
383
384    krb5_clear_error_string(context);
385    return ret;
386}
387
388static char *
389get_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
405static const char*
406acc_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
423static krb5_error_code
424acc_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
453static krb5_error_code
454acc_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
488static krb5_error_code
489acc_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
506static krb5_error_code
507acc_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
529static krb5_error_code
530acc_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
549static krb5_error_code
550acc_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
567static krb5_error_code
568acc_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
601static krb5_error_code
602acc_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
628static krb5_error_code
629acc_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
652static krb5_error_code
653acc_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
679static krb5_error_code
680acc_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
689static krb5_error_code
690acc_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
763static krb5_error_code
764acc_set_flags(krb5_context context,
765	      krb5_ccache id,
766	      krb5_flags flags)
767{
768    return 0;
769}
770
771static krb5_error_code
772acc_get_version(krb5_context context,
773		krb5_ccache id)
774{
775    return 0;
776}
777
778struct cache_iter {
779    cc_context_t context;
780    cc_ccache_iterator_t iter;
781};
782
783static krb5_error_code
784acc_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
817static krb5_error_code
818acc_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
856static krb5_error_code
857acc_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
869static krb5_error_code
870acc_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
899static krb5_error_code
900acc_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
939const 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