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