acache.c revision 178825
1132451Sroberto/*
2182007Sroberto * Copyright (c) 2004 - 2007 Kungliga Tekniska H�gskolan
3182007Sroberto * (Royal Institute of Technology, Stockholm, Sweden).
4132451Sroberto * All rights reserved.
5132451Sroberto *
6132451Sroberto * Redistribution and use in source and binary forms, with or without
7132451Sroberto * modification, are permitted provided that the following conditions
8132451Sroberto * are met:
9182007Sroberto *
10182007Sroberto * 1. Redistributions of source code must retain the above copyright
11182007Sroberto *    notice, this list of conditions and the following disclaimer.
12182007Sroberto *
13182007Sroberto * 2. Redistributions in binary form must reproduce the above copyright
14182007Sroberto *    notice, this list of conditions and the following disclaimer in the
15182007Sroberto *    documentation and/or other materials provided with the distribution.
16132451Sroberto *
17132451Sroberto * 3. Neither the name of the Institute nor the names of its contributors
18182007Sroberto *    may be used to endorse or promote products derived from this software
19132451Sroberto *    without specific prior written permission.
20132451Sroberto *
21132451Sroberto * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
22132451Sroberto * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23132451Sroberto * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24132451Sroberto * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
25132451Sroberto * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26132451Sroberto * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27132451Sroberto * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28132451Sroberto * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29132451Sroberto * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30132451Sroberto * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31132451Sroberto * SUCH DAMAGE.
32132451Sroberto */
33132451Sroberto
34132451Sroberto#include "krb5_locl.h"
35132451Sroberto#include <krb5_ccapi.h>
36132451Sroberto#ifdef HAVE_DLFCN_H
37132451Sroberto#include <dlfcn.h>
38132451Sroberto#endif
39132451Sroberto
40132451SrobertoRCSID("$Id: acache.c 22099 2007-12-03 17:14:34Z lha $");
41132451Sroberto
42132451Sroberto/* XXX should we fetch these for each open ? */
43182007Srobertostatic HEIMDAL_MUTEX acc_mutex = HEIMDAL_MUTEX_INITIALIZER;
44132451Srobertostatic cc_initialize_func init_func;
45132451Sroberto
46132451Sroberto#ifdef HAVE_DLOPEN
47132451Srobertostatic void *cc_handle;
48132451Sroberto#endif
49132451Sroberto
50132451Srobertotypedef struct krb5_acc {
51132451Sroberto    char *cache_name;
52132451Sroberto    cc_context_t context;
53132451Sroberto    cc_ccache_t ccache;
54182007Sroberto} krb5_acc;
55182007Sroberto
56182007Srobertostatic krb5_error_code acc_close(krb5_context, krb5_ccache);
57132451Sroberto
58132451Sroberto#define ACACHE(X) ((krb5_acc *)(X)->data.data)
59132451Sroberto
60182007Srobertostatic const struct {
61132451Sroberto    cc_int32 error;
62132451Sroberto    krb5_error_code ret;
63182007Sroberto} cc_errors[] = {
64182007Sroberto    { ccErrBadName,		KRB5_CC_BADNAME },
65182007Sroberto    { ccErrCredentialsNotFound,	KRB5_CC_NOTFOUND },
66182007Sroberto    { ccErrCCacheNotFound,	KRB5_FCC_NOFILE },
67132451Sroberto    { ccErrContextNotFound,	KRB5_CC_NOTFOUND },
68182007Sroberto    { ccIteratorEnd,		KRB5_CC_END },
69132451Sroberto    { ccErrNoMem,		KRB5_CC_NOMEM },
70182007Sroberto    { ccErrServerUnavailable,	KRB5_CC_NOSUPP },
71182007Sroberto    { ccNoError,		0 }
72182007Sroberto};
73182007Sroberto
74182007Srobertostatic krb5_error_code
75182007Srobertotranslate_cc_error(krb5_context context, cc_int32 error)
76132451Sroberto{
77132451Sroberto    int i;
78132451Sroberto    krb5_clear_error_string(context);
79182007Sroberto    for(i = 0; i < sizeof(cc_errors)/sizeof(cc_errors[0]); i++)
80182007Sroberto	if (cc_errors[i].error == error)
81132451Sroberto	    return cc_errors[i].ret;
82182007Sroberto    return KRB5_FCC_INTERNAL;
83182007Sroberto}
84182007Sroberto
85182007Srobertostatic krb5_error_code
86182007Srobertoinit_ccapi(krb5_context context)
87182007Sroberto{
88132451Sroberto    const char *lib;
89132451Sroberto
90132451Sroberto    HEIMDAL_MUTEX_lock(&acc_mutex);
91132451Sroberto    if (init_func) {
92132451Sroberto	HEIMDAL_MUTEX_unlock(&acc_mutex);
93132451Sroberto	krb5_clear_error_string(context);
94132451Sroberto	return 0;
95132451Sroberto    }
96132451Sroberto
97132451Sroberto    lib = krb5_config_get_string(context, NULL,
98132451Sroberto				 "libdefaults", "ccapi_library",
99132451Sroberto				 NULL);
100132451Sroberto    if (lib == NULL) {
101132451Sroberto#ifdef __APPLE__
102132451Sroberto	lib = "/System/Library/Frameworks/Kerberos.framework/Kerberos";
103132451Sroberto#else
104132451Sroberto	lib = "/usr/lib/libkrb5_cc.so";
105182007Sroberto#endif
106182007Sroberto    }
107182007Sroberto
108182007Sroberto#ifdef HAVE_DLOPEN
109182007Sroberto
110182007Sroberto#ifndef RTLD_LAZY
111182007Sroberto#define RTLD_LAZY 0
112182007Sroberto#endif
113182007Sroberto
114182007Sroberto    cc_handle = dlopen(lib, RTLD_LAZY);
115132451Sroberto    if (cc_handle == NULL) {
116132451Sroberto	HEIMDAL_MUTEX_unlock(&acc_mutex);
117132451Sroberto	krb5_set_error_string(context, "Failed to load %s", lib);
118132451Sroberto	return KRB5_CC_NOSUPP;
119132451Sroberto    }
120132451Sroberto
121132451Sroberto    init_func = (cc_initialize_func)dlsym(cc_handle, "cc_initialize");
122132451Sroberto    HEIMDAL_MUTEX_unlock(&acc_mutex);
123132451Sroberto    if (init_func == NULL) {
124132451Sroberto	krb5_set_error_string(context, "Failed to find cc_initialize"
125132451Sroberto			      "in %s: %s", lib, dlerror());
126132451Sroberto	dlclose(cc_handle);
127132451Sroberto	return KRB5_CC_NOSUPP;
128132451Sroberto    }
129132451Sroberto
130132451Sroberto    return 0;
131132451Sroberto#else
132132451Sroberto    HEIMDAL_MUTEX_unlock(&acc_mutex);
133132451Sroberto    krb5_set_error_string(context, "no support for shared object");
134132451Sroberto    return KRB5_CC_NOSUPP;
135132451Sroberto#endif
136132451Sroberto}
137132451Sroberto
138132451Srobertostatic krb5_error_code
139132451Srobertomake_cred_from_ccred(krb5_context context,
140132451Sroberto		     const cc_credentials_v5_t *incred,
141132451Sroberto		     krb5_creds *cred)
142132451Sroberto{
143132451Sroberto    krb5_error_code ret;
144132451Sroberto    int i;
145132451Sroberto
146132451Sroberto    memset(cred, 0, sizeof(*cred));
147132451Sroberto
148132451Sroberto    ret = krb5_parse_name(context, incred->client, &cred->client);
149132451Sroberto    if (ret)
150132451Sroberto	goto fail;
151132451Sroberto
152132451Sroberto    ret = krb5_parse_name(context, incred->server, &cred->server);
153132451Sroberto    if (ret)
154132451Sroberto	goto fail;
155132451Sroberto
156132451Sroberto    cred->session.keytype = incred->keyblock.type;
157132451Sroberto    cred->session.keyvalue.length = incred->keyblock.length;
158132451Sroberto    cred->session.keyvalue.data = malloc(incred->keyblock.length);
159132451Sroberto    if (cred->session.keyvalue.data == NULL)
160132451Sroberto	goto nomem;
161132451Sroberto    memcpy(cred->session.keyvalue.data, incred->keyblock.data,
162132451Sroberto	   incred->keyblock.length);
163132451Sroberto
164132451Sroberto    cred->times.authtime = incred->authtime;
165132451Sroberto    cred->times.starttime = incred->starttime;
166132451Sroberto    cred->times.endtime = incred->endtime;
167132451Sroberto    cred->times.renew_till = incred->renew_till;
168132451Sroberto
169132451Sroberto    ret = krb5_data_copy(&cred->ticket,
170132451Sroberto			 incred->ticket.data,
171132451Sroberto			 incred->ticket.length);
172132451Sroberto    if (ret)
173132451Sroberto	goto nomem;
174132451Sroberto
175132451Sroberto    ret = krb5_data_copy(&cred->second_ticket,
176132451Sroberto			 incred->second_ticket.data,
177132451Sroberto			 incred->second_ticket.length);
178132451Sroberto    if (ret)
179132451Sroberto	goto nomem;
180132451Sroberto
181132451Sroberto    cred->authdata.val = NULL;
182132451Sroberto    cred->authdata.len = 0;
183132451Sroberto
184132451Sroberto    cred->addresses.val = NULL;
185132451Sroberto    cred->addresses.len = 0;
186132451Sroberto
187182007Sroberto    for (i = 0; incred->authdata && incred->authdata[i]; i++)
188132451Sroberto	;
189132451Sroberto
190132451Sroberto    if (i) {
191132451Sroberto	cred->authdata.val = calloc(i, sizeof(cred->authdata.val[0]));
192132451Sroberto	if (cred->authdata.val == NULL)
193182007Sroberto	    goto nomem;
194132451Sroberto	cred->authdata.len = i;
195132451Sroberto	for (i = 0; i < cred->authdata.len; i++) {
196182007Sroberto	    cred->authdata.val[i].ad_type = incred->authdata[i]->type;
197182007Sroberto	    ret = krb5_data_copy(&cred->authdata.val[i].ad_data,
198132451Sroberto				 incred->authdata[i]->data,
199132451Sroberto				 incred->authdata[i]->length);
200182007Sroberto	    if (ret)
201132451Sroberto		goto nomem;
202182007Sroberto	}
203132451Sroberto    }
204132451Sroberto
205132451Sroberto    for (i = 0; incred->addresses && incred->addresses[i]; i++)
206132451Sroberto	;
207182007Sroberto
208182007Sroberto    if (i) {
209132451Sroberto	cred->addresses.val = calloc(i, sizeof(cred->addresses.val[0]));
210132451Sroberto	if (cred->addresses.val == NULL)
211132451Sroberto	    goto nomem;
212132451Sroberto	cred->addresses.len = i;
213132451Sroberto
214182007Sroberto	for (i = 0; i < cred->addresses.len; i++) {
215132451Sroberto	    cred->addresses.val[i].addr_type = incred->addresses[i]->type;
216132451Sroberto	    ret = krb5_data_copy(&cred->addresses.val[i].address,
217132451Sroberto				 incred->addresses[i]->data,
218132451Sroberto				 incred->addresses[i]->length);
219132451Sroberto	    if (ret)
220132451Sroberto		goto nomem;
221132451Sroberto	}
222132451Sroberto    }
223132451Sroberto
224132451Sroberto    cred->flags.i = 0;
225132451Sroberto    if (incred->ticket_flags & KRB5_CCAPI_TKT_FLG_FORWARDABLE)
226132451Sroberto	cred->flags.b.forwardable = 1;
227132451Sroberto    if (incred->ticket_flags & KRB5_CCAPI_TKT_FLG_FORWARDED)
228132451Sroberto	cred->flags.b.forwarded = 1;
229132451Sroberto    if (incred->ticket_flags & KRB5_CCAPI_TKT_FLG_PROXIABLE)
230132451Sroberto	cred->flags.b.proxiable = 1;
231132451Sroberto    if (incred->ticket_flags & KRB5_CCAPI_TKT_FLG_PROXY)
232132451Sroberto	cred->flags.b.proxy = 1;
233132451Sroberto    if (incred->ticket_flags & KRB5_CCAPI_TKT_FLG_MAY_POSTDATE)
234132451Sroberto	cred->flags.b.may_postdate = 1;
235132451Sroberto    if (incred->ticket_flags & KRB5_CCAPI_TKT_FLG_POSTDATED)
236132451Sroberto	cred->flags.b.postdated = 1;
237132451Sroberto    if (incred->ticket_flags & KRB5_CCAPI_TKT_FLG_INVALID)
238132451Sroberto	cred->flags.b.invalid = 1;
239132451Sroberto    if (incred->ticket_flags & KRB5_CCAPI_TKT_FLG_RENEWABLE)
240132451Sroberto	cred->flags.b.renewable = 1;
241132451Sroberto    if (incred->ticket_flags & KRB5_CCAPI_TKT_FLG_INITIAL)
242132451Sroberto	cred->flags.b.initial = 1;
243132451Sroberto    if (incred->ticket_flags & KRB5_CCAPI_TKT_FLG_PRE_AUTH)
244132451Sroberto	cred->flags.b.pre_authent = 1;
245132451Sroberto    if (incred->ticket_flags & KRB5_CCAPI_TKT_FLG_HW_AUTH)
246132451Sroberto	cred->flags.b.hw_authent = 1;
247132451Sroberto    if (incred->ticket_flags & KRB5_CCAPI_TKT_FLG_TRANSIT_POLICY_CHECKED)
248132451Sroberto	cred->flags.b.transited_policy_checked = 1;
249132451Sroberto    if (incred->ticket_flags & KRB5_CCAPI_TKT_FLG_OK_AS_DELEGATE)
250132451Sroberto	cred->flags.b.ok_as_delegate = 1;
251132451Sroberto    if (incred->ticket_flags & KRB5_CCAPI_TKT_FLG_ANONYMOUS)
252132451Sroberto	cred->flags.b.anonymous = 1;
253132451Sroberto
254132451Sroberto    return 0;
255132451Sroberto
256132451Srobertonomem:
257132451Sroberto    ret = ENOMEM;
258132451Sroberto    krb5_set_error_string(context, "malloc - out of memory");
259132451Sroberto
260182007Srobertofail:
261132451Sroberto    krb5_free_cred_contents(context, cred);
262132451Sroberto    return ret;
263182007Sroberto}
264132451Sroberto
265132451Srobertostatic void
266132451Srobertofree_ccred(cc_credentials_v5_t *cred)
267132451Sroberto{
268132451Sroberto    int i;
269132451Sroberto
270132451Sroberto    if (cred->addresses) {
271132451Sroberto	for (i = 0; cred->addresses[i] != 0; i++) {
272132451Sroberto	    if (cred->addresses[i]->data)
273132451Sroberto		free(cred->addresses[i]->data);
274132451Sroberto	    free(cred->addresses[i]);
275182007Sroberto	}
276132451Sroberto	free(cred->addresses);
277182007Sroberto    }
278132451Sroberto    if (cred->server)
279132451Sroberto	free(cred->server);
280182007Sroberto    if (cred->client)
281182007Sroberto	free(cred->client);
282132451Sroberto    memset(cred, 0, sizeof(*cred));
283132451Sroberto}
284132451Sroberto
285182007Srobertostatic krb5_error_code
286182007Srobertomake_ccred_from_cred(krb5_context context,
287132451Sroberto		     const krb5_creds *incred,
288182007Sroberto		     cc_credentials_v5_t *cred)
289132451Sroberto{
290132451Sroberto    krb5_error_code ret;
291132451Sroberto    int i;
292132451Sroberto
293132451Sroberto    memset(cred, 0, sizeof(*cred));
294132451Sroberto
295132451Sroberto    ret = krb5_unparse_name(context, incred->client, &cred->client);
296132451Sroberto    if (ret)
297132451Sroberto	goto fail;
298132451Sroberto
299132451Sroberto    ret = krb5_unparse_name(context, incred->server, &cred->server);
300132451Sroberto    if (ret)
301132451Sroberto	goto fail;
302132451Sroberto
303132451Sroberto    cred->keyblock.type = incred->session.keytype;
304132451Sroberto    cred->keyblock.length = incred->session.keyvalue.length;
305182007Sroberto    cred->keyblock.data = incred->session.keyvalue.data;
306132451Sroberto
307182007Sroberto    cred->authtime = incred->times.authtime;
308182007Sroberto    cred->starttime = incred->times.starttime;
309182007Sroberto    cred->endtime = incred->times.endtime;
310182007Sroberto    cred->renew_till = incred->times.renew_till;
311182007Sroberto
312182007Sroberto    cred->ticket.length = incred->ticket.length;
313182007Sroberto    cred->ticket.data = incred->ticket.data;
314182007Sroberto
315132451Sroberto    cred->second_ticket.length = incred->second_ticket.length;
316132451Sroberto    cred->second_ticket.data = incred->second_ticket.data;
317182007Sroberto
318182007Sroberto    /* XXX this one should also be filled in */
319132451Sroberto    cred->authdata = NULL;
320182007Sroberto
321182007Sroberto    cred->addresses = calloc(incred->addresses.len + 1,
322182007Sroberto			     sizeof(cred->addresses[0]));
323182007Sroberto    if (cred->addresses == NULL) {
324182007Sroberto
325182007Sroberto	ret = ENOMEM;
326182007Sroberto	goto fail;
327182007Sroberto    }
328182007Sroberto
329182007Sroberto    for (i = 0; i < incred->addresses.len; i++) {
330182007Sroberto	cc_data *addr;
331182007Sroberto	addr = malloc(sizeof(*addr));
332182007Sroberto	if (addr == NULL) {
333182007Sroberto	    ret = ENOMEM;
334182007Sroberto	    goto fail;
335182007Sroberto	}
336182007Sroberto	addr->type = incred->addresses.val[i].addr_type;
337182007Sroberto	addr->length = incred->addresses.val[i].address.length;
338182007Sroberto	addr->data = malloc(addr->length);
339182007Sroberto	if (addr->data == NULL) {
340182007Sroberto	    ret = ENOMEM;
341182007Sroberto	    goto fail;
342182007Sroberto	}
343182007Sroberto	memcpy(addr->data, incred->addresses.val[i].address.data,
344182007Sroberto	       addr->length);
345182007Sroberto	cred->addresses[i] = addr;
346182007Sroberto    }
347132451Sroberto    cred->addresses[i] = NULL;
348132451Sroberto
349132451Sroberto    cred->ticket_flags = 0;
350132451Sroberto    if (incred->flags.b.forwardable)
351132451Sroberto	cred->ticket_flags |= KRB5_CCAPI_TKT_FLG_FORWARDABLE;
352132451Sroberto    if (incred->flags.b.forwarded)
353132451Sroberto	cred->ticket_flags |= KRB5_CCAPI_TKT_FLG_FORWARDED;
354132451Sroberto    if (incred->flags.b.proxiable)
355132451Sroberto	cred->ticket_flags |= KRB5_CCAPI_TKT_FLG_PROXIABLE;
356132451Sroberto    if (incred->flags.b.proxy)
357132451Sroberto	cred->ticket_flags |= KRB5_CCAPI_TKT_FLG_PROXY;
358132451Sroberto    if (incred->flags.b.may_postdate)
359182007Sroberto	cred->ticket_flags |= KRB5_CCAPI_TKT_FLG_MAY_POSTDATE;
360132451Sroberto    if (incred->flags.b.postdated)
361132451Sroberto	cred->ticket_flags |= KRB5_CCAPI_TKT_FLG_POSTDATED;
362132451Sroberto    if (incred->flags.b.invalid)
363132451Sroberto	cred->ticket_flags |= KRB5_CCAPI_TKT_FLG_INVALID;
364132451Sroberto    if (incred->flags.b.renewable)
365132451Sroberto	cred->ticket_flags |= KRB5_CCAPI_TKT_FLG_RENEWABLE;
366132451Sroberto    if (incred->flags.b.initial)
367132451Sroberto	cred->ticket_flags |= KRB5_CCAPI_TKT_FLG_INITIAL;
368132451Sroberto    if (incred->flags.b.pre_authent)
369182007Sroberto	cred->ticket_flags |= KRB5_CCAPI_TKT_FLG_PRE_AUTH;
370132451Sroberto    if (incred->flags.b.hw_authent)
371182007Sroberto	cred->ticket_flags |= KRB5_CCAPI_TKT_FLG_HW_AUTH;
372182007Sroberto    if (incred->flags.b.transited_policy_checked)
373182007Sroberto	cred->ticket_flags |= KRB5_CCAPI_TKT_FLG_TRANSIT_POLICY_CHECKED;
374182007Sroberto    if (incred->flags.b.ok_as_delegate)
375182007Sroberto	cred->ticket_flags |= KRB5_CCAPI_TKT_FLG_OK_AS_DELEGATE;
376132451Sroberto    if (incred->flags.b.anonymous)
377132451Sroberto	cred->ticket_flags |= KRB5_CCAPI_TKT_FLG_ANONYMOUS;
378132451Sroberto
379132451Sroberto    return 0;
380132451Sroberto
381132451Srobertofail:
382132451Sroberto    free_ccred(cred);
383132451Sroberto
384132451Sroberto    krb5_clear_error_string(context);
385132451Sroberto    return ret;
386132451Sroberto}
387132451Sroberto
388182007Srobertostatic char *
389182007Srobertoget_cc_name(cc_ccache_t cache)
390182007Sroberto{
391182007Sroberto    cc_string_t name;
392182007Sroberto    cc_int32 error;
393182007Sroberto    char *str;
394182007Sroberto
395182007Sroberto    error = (*cache->func->get_name)(cache, &name);
396182007Sroberto    if (error)
397132451Sroberto	return NULL;
398132451Sroberto
399132451Sroberto    str = strdup(name->data);
400132451Sroberto    (*name->func->release)(name);
401132451Sroberto    return str;
402132451Sroberto}
403132451Sroberto
404132451Sroberto
405132451Srobertostatic const char*
406132451Srobertoacc_get_name(krb5_context context,
407132451Sroberto	     krb5_ccache id)
408132451Sroberto{
409132451Sroberto    krb5_acc *a = ACACHE(id);
410132451Sroberto    static char n[255];
411182007Sroberto    char *name;
412182007Sroberto
413182007Sroberto    name = get_cc_name(a->ccache);
414182007Sroberto    if (name == NULL) {
415182007Sroberto	krb5_set_error_string(context, "malloc: out of memory");
416182007Sroberto	return NULL;
417182007Sroberto    }
418182007Sroberto    strlcpy(n, name, sizeof(n));
419182007Sroberto    free(name);
420182007Sroberto    return n;
421182007Sroberto}
422182007Sroberto
423182007Srobertostatic krb5_error_code
424182007Srobertoacc_alloc(krb5_context context, krb5_ccache *id)
425182007Sroberto{
426182007Sroberto    krb5_error_code ret;
427182007Sroberto    cc_int32 error;
428182007Sroberto    krb5_acc *a;
429182007Sroberto
430182007Sroberto    ret = init_ccapi(context);
431182007Sroberto    if (ret)
432182007Sroberto	return ret;
433182007Sroberto
434182007Sroberto    ret = krb5_data_alloc(&(*id)->data, sizeof(*a));
435182007Sroberto    if (ret) {
436182007Sroberto	krb5_clear_error_string(context);
437182007Sroberto	return ret;
438182007Sroberto    }
439182007Sroberto
440182007Sroberto    a = ACACHE(*id);
441182007Sroberto
442182007Sroberto    error = (*init_func)(&a->context, ccapi_version_3, NULL, NULL);
443182007Sroberto    if (error) {
444182007Sroberto	krb5_data_free(&(*id)->data);
445182007Sroberto	return translate_cc_error(context, error);
446182007Sroberto    }
447182007Sroberto
448182007Sroberto    a->cache_name = NULL;
449182007Sroberto
450182007Sroberto    return 0;
451182007Sroberto}
452182007Sroberto
453182007Srobertostatic krb5_error_code
454182007Srobertoacc_resolve(krb5_context context, krb5_ccache *id, const char *res)
455182007Sroberto{
456182007Sroberto    krb5_error_code ret;
457182007Sroberto    cc_int32 error;
458182007Sroberto    krb5_acc *a;
459182007Sroberto
460182007Sroberto    ret = acc_alloc(context, id);
461182007Sroberto    if (ret)
462132451Sroberto	return ret;
463182007Sroberto
464182007Sroberto    a = ACACHE(*id);
465182007Sroberto
466182007Sroberto    error = (*a->context->func->open_ccache)(a->context, res,
467182007Sroberto					     &a->ccache);
468182007Sroberto    if (error == 0) {
469182007Sroberto	a->cache_name = get_cc_name(a->ccache);
470182007Sroberto	if (a->cache_name == NULL) {
471182007Sroberto	    acc_close(context, *id);
472182007Sroberto	    *id = NULL;
473182007Sroberto	    krb5_set_error_string(context, "malloc: out of memory");
474182007Sroberto	    return ENOMEM;
475182007Sroberto	}
476182007Sroberto    } else if (error == ccErrCCacheNotFound) {
477182007Sroberto	a->ccache = NULL;
478182007Sroberto	a->cache_name = NULL;
479182007Sroberto	error = 0;
480182007Sroberto    } else {
481182007Sroberto	*id = NULL;
482182007Sroberto	return translate_cc_error(context, error);
483182007Sroberto    }
484182007Sroberto
485182007Sroberto    return 0;
486182007Sroberto}
487182007Sroberto
488182007Srobertostatic krb5_error_code
489182007Srobertoacc_gen_new(krb5_context context, krb5_ccache *id)
490182007Sroberto{
491182007Sroberto    krb5_error_code ret;
492182007Sroberto    krb5_acc *a;
493182007Sroberto
494182007Sroberto    ret = acc_alloc(context, id);
495182007Sroberto    if (ret)
496182007Sroberto	return ret;
497182007Sroberto
498182007Sroberto    a = ACACHE(*id);
499182007Sroberto
500182007Sroberto    a->ccache = NULL;
501182007Sroberto    a->cache_name = NULL;
502182007Sroberto
503182007Sroberto    return 0;
504182007Sroberto}
505182007Sroberto
506182007Srobertostatic krb5_error_code
507182007Srobertoacc_initialize(krb5_context context,
508182007Sroberto	       krb5_ccache id,
509182007Sroberto	       krb5_principal primary_principal)
510182007Sroberto{
511182007Sroberto    krb5_acc *a = ACACHE(id);
512182007Sroberto    krb5_error_code ret;
513182007Sroberto    int32_t error;
514182007Sroberto    char *name;
515182007Sroberto
516182007Sroberto    ret = krb5_unparse_name(context, primary_principal, &name);
517182007Sroberto    if (ret)
518182007Sroberto	return ret;
519182007Sroberto
520182007Sroberto    error = (*a->context->func->create_new_ccache)(a->context,
521182007Sroberto						   cc_credentials_v5,
522182007Sroberto						   name,
523182007Sroberto						   &a->ccache);
524182007Sroberto    free(name);
525182007Sroberto
526182007Sroberto    return translate_cc_error(context, error);
527182007Sroberto}
528182007Sroberto
529182007Srobertostatic krb5_error_code
530182007Srobertoacc_close(krb5_context context,
531182007Sroberto	  krb5_ccache id)
532182007Sroberto{
533182007Sroberto    krb5_acc *a = ACACHE(id);
534182007Sroberto
535182007Sroberto    if (a->ccache) {
536182007Sroberto	(*a->ccache->func->release)(a->ccache);
537182007Sroberto	a->ccache = NULL;
538182007Sroberto    }
539132451Sroberto    if (a->cache_name) {
540132451Sroberto	free(a->cache_name);
541132451Sroberto	a->cache_name = NULL;
542132451Sroberto    }
543132451Sroberto    (*a->context->func->release)(a->context);
544132451Sroberto    a->context = NULL;
545132451Sroberto    krb5_data_free(&id->data);
546132451Sroberto    return 0;
547132451Sroberto}
548132451Sroberto
549132451Srobertostatic krb5_error_code
550132451Srobertoacc_destroy(krb5_context context,
551132451Sroberto	    krb5_ccache id)
552132451Sroberto{
553132451Sroberto    krb5_acc *a = ACACHE(id);
554132451Sroberto    cc_int32 error = 0;
555132451Sroberto
556182007Sroberto    if (a->ccache) {
557182007Sroberto	error = (*a->ccache->func->destroy)(a->ccache);
558182007Sroberto	a->ccache = NULL;
559182007Sroberto    }
560132451Sroberto    if (a->context) {
561182007Sroberto	error = (a->context->func->release)(a->context);
562182007Sroberto	a->context = NULL;
563182007Sroberto    }
564182007Sroberto    return translate_cc_error(context, error);
565132451Sroberto}
566132451Sroberto
567132451Srobertostatic krb5_error_code
568132451Srobertoacc_store_cred(krb5_context context,
569182007Sroberto	       krb5_ccache id,
570182007Sroberto	       krb5_creds *creds)
571182007Sroberto{
572182007Sroberto    krb5_acc *a = ACACHE(id);
573182007Sroberto    cc_credentials_union cred;
574182007Sroberto    cc_credentials_v5_t v5cred;
575182007Sroberto    krb5_error_code ret;
576132451Sroberto    cc_int32 error;
577132451Sroberto
578132451Sroberto    if (a->ccache == NULL) {
579132451Sroberto	krb5_set_error_string(context, "No API credential found");
580132451Sroberto	return KRB5_CC_NOTFOUND;
581132451Sroberto    }
582182007Sroberto
583132451Sroberto    cred.version = cc_credentials_v5;
584132451Sroberto    cred.credentials.credentials_v5 = &v5cred;
585132451Sroberto
586132451Sroberto    ret = make_ccred_from_cred(context,
587132451Sroberto			       creds,
588132451Sroberto			       &v5cred);
589132451Sroberto    if (ret)
590132451Sroberto	return ret;
591132451Sroberto
592132451Sroberto    error = (*a->ccache->func->store_credentials)(a->ccache, &cred);
593132451Sroberto    if (error)
594132451Sroberto	ret = translate_cc_error(context, error);
595132451Sroberto
596132451Sroberto    free_ccred(&v5cred);
597182007Sroberto
598132451Sroberto    return ret;
599132451Sroberto}
600132451Sroberto
601132451Srobertostatic krb5_error_code
602132451Srobertoacc_get_principal(krb5_context context,
603132451Sroberto		  krb5_ccache id,
604132451Sroberto		  krb5_principal *principal)
605132451Sroberto{
606132451Sroberto    krb5_acc *a = ACACHE(id);
607182007Sroberto    krb5_error_code ret;
608132451Sroberto    int32_t error;
609132451Sroberto    cc_string_t name;
610132451Sroberto
611132451Sroberto    if (a->ccache == NULL) {
612132451Sroberto	krb5_set_error_string(context, "No API credential found");
613182007Sroberto	return KRB5_CC_NOTFOUND;
614132451Sroberto    }
615132451Sroberto
616132451Sroberto    error = (*a->ccache->func->get_principal)(a->ccache,
617132451Sroberto					      cc_credentials_v5,
618132451Sroberto					      &name);
619132451Sroberto    if (error)
620132451Sroberto	return translate_cc_error(context, error);
621132451Sroberto
622132451Sroberto    ret = krb5_parse_name(context, name->data, principal);
623132451Sroberto
624132451Sroberto    (*name->func->release)(name);
625132451Sroberto    return ret;
626132451Sroberto}
627132451Sroberto
628132451Srobertostatic krb5_error_code
629132451Srobertoacc_get_first (krb5_context context,
630132451Sroberto	       krb5_ccache id,
631132451Sroberto	       krb5_cc_cursor *cursor)
632132451Sroberto{
633132451Sroberto    cc_credentials_iterator_t iter;
634132451Sroberto    krb5_acc *a = ACACHE(id);
635132451Sroberto    int32_t error;
636132451Sroberto
637132451Sroberto    if (a->ccache == NULL) {
638182007Sroberto	krb5_set_error_string(context, "No API credential found");
639132451Sroberto	return KRB5_CC_NOTFOUND;
640132451Sroberto    }
641182007Sroberto
642132451Sroberto    error = (*a->ccache->func->new_credentials_iterator)(a->ccache, &iter);
643132451Sroberto    if (error) {
644132451Sroberto	krb5_clear_error_string(context);
645132451Sroberto	return ENOENT;
646132451Sroberto    }
647132451Sroberto    *cursor = iter;
648132451Sroberto    return 0;
649132451Sroberto}
650132451Sroberto
651132451Sroberto
652132451Srobertostatic krb5_error_code
653132451Srobertoacc_get_next (krb5_context context,
654132451Sroberto	      krb5_ccache id,
655132451Sroberto	      krb5_cc_cursor *cursor,
656182007Sroberto	      krb5_creds *creds)
657132451Sroberto{
658132451Sroberto    cc_credentials_iterator_t iter = *cursor;
659182007Sroberto    cc_credentials_t cred;
660182007Sroberto    krb5_error_code ret;
661182007Sroberto    int32_t error;
662182007Sroberto
663132451Sroberto    while (1) {
664132451Sroberto	error = (*iter->func->next)(iter, &cred);
665182007Sroberto	if (error)
666132451Sroberto	    return translate_cc_error(context, error);
667132451Sroberto	if (cred->data->version == cc_credentials_v5)
668132451Sroberto	    break;
669132451Sroberto	(*cred->func->release)(cred);
670132451Sroberto    }
671132451Sroberto
672182007Sroberto    ret = make_cred_from_ccred(context,
673182007Sroberto			       cred->data->credentials.credentials_v5,
674182007Sroberto			       creds);
675182007Sroberto    (*cred->func->release)(cred);
676182007Sroberto    return ret;
677182007Sroberto}
678182007Sroberto
679182007Srobertostatic krb5_error_code
680182007Srobertoacc_end_get (krb5_context context,
681182007Sroberto	     krb5_ccache id,
682182007Sroberto	     krb5_cc_cursor *cursor)
683132451Sroberto{
684132451Sroberto    cc_credentials_iterator_t iter = *cursor;
685132451Sroberto    (*iter->func->release)(iter);
686132451Sroberto    return 0;
687132451Sroberto}
688132451Sroberto
689182007Srobertostatic krb5_error_code
690132451Srobertoacc_remove_cred(krb5_context context,
691182007Sroberto		krb5_ccache id,
692132451Sroberto		krb5_flags which,
693182007Sroberto		krb5_creds *cred)
694182007Sroberto{
695132451Sroberto    cc_credentials_iterator_t iter;
696132451Sroberto    krb5_acc *a = ACACHE(id);
697132451Sroberto    cc_credentials_t ccred;
698132451Sroberto    krb5_error_code ret;
699132451Sroberto    cc_int32 error;
700132451Sroberto    char *client, *server;
701132451Sroberto
702132451Sroberto    if (a->ccache == NULL) {
703182007Sroberto	krb5_set_error_string(context, "No API credential found");
704132451Sroberto	return KRB5_CC_NOTFOUND;
705132451Sroberto    }
706132451Sroberto
707132451Sroberto    if (cred->client) {
708132451Sroberto	ret = krb5_unparse_name(context, cred->client, &client);
709132451Sroberto	if (ret)
710132451Sroberto	    return ret;
711132451Sroberto    } else
712132451Sroberto	client = NULL;
713132451Sroberto
714132451Sroberto    ret = krb5_unparse_name(context, cred->server, &server);
715132451Sroberto    if (ret) {
716132451Sroberto	free(client);
717132451Sroberto	return ret;
718132451Sroberto    }
719132451Sroberto
720132451Sroberto    error = (*a->ccache->func->new_credentials_iterator)(a->ccache, &iter);
721132451Sroberto    if (error) {
722132451Sroberto	free(server);
723132451Sroberto	free(client);
724132451Sroberto	return translate_cc_error(context, error);
725132451Sroberto    }
726182007Sroberto
727132451Sroberto    ret = KRB5_CC_NOTFOUND;
728182007Sroberto    while (1) {
729132451Sroberto	cc_credentials_v5_t *v5cred;
730132451Sroberto
731132451Sroberto	error = (*iter->func->next)(iter, &ccred);
732132451Sroberto	if (error)
733132451Sroberto	    break;
734132451Sroberto
735132451Sroberto	if (ccred->data->version != cc_credentials_v5)
736132451Sroberto	    goto next;
737132451Sroberto
738132451Sroberto	v5cred = ccred->data->credentials.credentials_v5;
739132451Sroberto
740132451Sroberto	if (client && strcmp(v5cred->client, client) != 0)
741132451Sroberto	    goto next;
742132451Sroberto
743132451Sroberto	if (strcmp(v5cred->server, server) != 0)
744132451Sroberto	    goto next;
745132451Sroberto
746132451Sroberto	(*a->ccache->func->remove_credentials)(a->ccache, ccred);
747132451Sroberto	ret = 0;
748182007Sroberto    next:
749132451Sroberto	(*ccred->func->release)(ccred);
750182007Sroberto    }
751132451Sroberto
752132451Sroberto    (*iter->func->release)(iter);
753132451Sroberto
754132451Sroberto    if (ret)
755132451Sroberto	krb5_set_error_string(context, "Can't find credential %s in cache",
756132451Sroberto			      server);
757132451Sroberto    free(server);
758132451Sroberto    free(client);
759132451Sroberto
760132451Sroberto    return ret;
761182007Sroberto}
762132451Sroberto
763132451Srobertostatic krb5_error_code
764132451Srobertoacc_set_flags(krb5_context context,
765132451Sroberto	      krb5_ccache id,
766132451Sroberto	      krb5_flags flags)
767132451Sroberto{
768132451Sroberto    return 0;
769132451Sroberto}
770132451Sroberto
771132451Srobertostatic krb5_error_code
772182007Srobertoacc_get_version(krb5_context context,
773132451Sroberto		krb5_ccache id)
774132451Sroberto{
775132451Sroberto    return 0;
776182007Sroberto}
777132451Sroberto
778132451Srobertostruct cache_iter {
779132451Sroberto    cc_context_t context;
780132451Sroberto    cc_ccache_iterator_t iter;
781132451Sroberto};
782132451Sroberto
783182007Srobertostatic krb5_error_code
784132451Srobertoacc_get_cache_first(krb5_context context, krb5_cc_cursor *cursor)
785132451Sroberto{
786182007Sroberto    struct cache_iter *iter;
787182007Sroberto    krb5_error_code ret;
788182007Sroberto    cc_int32 error;
789132451Sroberto
790182007Sroberto    ret = init_ccapi(context);
791132451Sroberto    if (ret)
792132451Sroberto	return ret;
793132451Sroberto
794132451Sroberto    iter = calloc(1, sizeof(*iter));
795132451Sroberto    if (iter == NULL) {
796132451Sroberto	krb5_set_error_string(context, "malloc - out of memory");
797132451Sroberto	return ENOMEM;
798132451Sroberto    }
799132451Sroberto
800132451Sroberto    error = (*init_func)(&iter->context, ccapi_version_3, NULL, NULL);
801132451Sroberto    if (error) {
802132451Sroberto	free(iter);
803132451Sroberto	return translate_cc_error(context, error);
804132451Sroberto    }
805132451Sroberto
806132451Sroberto    error = (*iter->context->func->new_ccache_iterator)(iter->context,
807132451Sroberto							&iter->iter);
808132451Sroberto    if (error) {
809132451Sroberto	free(iter);
810132451Sroberto	krb5_clear_error_string(context);
811182007Sroberto	return ENOENT;
812132451Sroberto    }
813132451Sroberto    *cursor = iter;
814132451Sroberto    return 0;
815132451Sroberto}
816132451Sroberto
817132451Srobertostatic krb5_error_code
818132451Srobertoacc_get_cache_next(krb5_context context, krb5_cc_cursor cursor, krb5_ccache *id)
819132451Sroberto{
820132451Sroberto    struct cache_iter *iter = cursor;
821182007Sroberto    cc_ccache_t cache;
822132451Sroberto    krb5_acc *a;
823132451Sroberto    krb5_error_code ret;
824132451Sroberto    int32_t error;
825132451Sroberto
826132451Sroberto    error = (*iter->iter->func->next)(iter->iter, &cache);
827182007Sroberto    if (error)
828132451Sroberto	return translate_cc_error(context, error);
829132451Sroberto
830132451Sroberto    ret = _krb5_cc_allocate(context, &krb5_acc_ops, id);
831132451Sroberto    if (ret) {
832132451Sroberto	(*cache->func->release)(cache);
833132451Sroberto	return ret;
834132451Sroberto    }
835132451Sroberto
836182007Sroberto    ret = acc_alloc(context, id);
837182007Sroberto    if (ret) {
838182007Sroberto	(*cache->func->release)(cache);
839182007Sroberto	free(*id);
840182007Sroberto	return ret;
841132451Sroberto    }
842132451Sroberto
843132451Sroberto    a = ACACHE(*id);
844132451Sroberto    a->ccache = cache;
845132451Sroberto
846182007Sroberto    a->cache_name = get_cc_name(a->ccache);
847182007Sroberto    if (a->cache_name == NULL) {
848182007Sroberto	acc_close(context, *id);
849132451Sroberto	*id = NULL;
850132451Sroberto	krb5_set_error_string(context, "malloc: out of memory");
851182007Sroberto	return ENOMEM;
852132451Sroberto    }
853132451Sroberto    return 0;
854132451Sroberto}
855132451Sroberto
856132451Srobertostatic krb5_error_code
857182007Srobertoacc_end_cache_get(krb5_context context, krb5_cc_cursor cursor)
858132451Sroberto{
859132451Sroberto    struct cache_iter *iter = cursor;
860182007Sroberto
861132451Sroberto    (*iter->iter->func->release)(iter->iter);
862132451Sroberto    iter->iter = NULL;
863132451Sroberto    (*iter->context->func->release)(iter->context);
864132451Sroberto    iter->context = NULL;
865182007Sroberto    free(iter);
866182007Sroberto    return 0;
867182007Sroberto}
868132451Sroberto
869132451Srobertostatic krb5_error_code
870132451Srobertoacc_move(krb5_context context, krb5_ccache from, krb5_ccache to)
871132451Sroberto{
872132451Sroberto    krb5_acc *afrom = ACACHE(from);
873132451Sroberto    krb5_acc *ato = ACACHE(to);
874132451Sroberto    int32_t error;
875182007Sroberto
876132451Sroberto    if (ato->ccache == NULL) {
877132451Sroberto	cc_string_t name;
878132451Sroberto
879132451Sroberto	error = (*afrom->ccache->func->get_principal)(afrom->ccache,
880132451Sroberto						      cc_credentials_v5,
881132451Sroberto						      &name);
882132451Sroberto	if (error)
883132451Sroberto	    return translate_cc_error(context, error);
884132451Sroberto
885182007Sroberto	error = (*ato->context->func->create_new_ccache)(ato->context,
886132451Sroberto							 cc_credentials_v5,
887132451Sroberto							 name->data,
888132451Sroberto							 &ato->ccache);
889132451Sroberto	(*name->func->release)(name);
890132451Sroberto	if (error)
891132451Sroberto	    return translate_cc_error(context, error);
892132451Sroberto    }
893132451Sroberto
894132451Sroberto
895132451Sroberto    error = (*ato->ccache->func->move)(afrom->ccache, ato->ccache);
896132451Sroberto    return translate_cc_error(context, error);
897132451Sroberto}
898182007Sroberto
899182007Srobertostatic krb5_error_code
900132451Srobertoacc_default_name(krb5_context context, char **str)
901182007Sroberto{
902132451Sroberto    krb5_error_code ret;
903182007Sroberto    cc_context_t cc;
904182007Sroberto    cc_string_t name;
905132451Sroberto    int32_t error;
906132451Sroberto
907132451Sroberto    ret = init_ccapi(context);
908132451Sroberto    if (ret)
909132451Sroberto	return ret;
910182007Sroberto
911132451Sroberto    error = (*init_func)(&cc, ccapi_version_3, NULL, NULL);
912132451Sroberto    if (error)
913132451Sroberto	return translate_cc_error(context, error);
914132451Sroberto
915132451Sroberto    error = (*cc->func->get_default_ccache_name)(cc, &name);
916182007Sroberto    if (error) {
917182007Sroberto	(*cc->func->release)(cc);
918182007Sroberto	return translate_cc_error(context, error);
919132451Sroberto    }
920132451Sroberto
921132451Sroberto    asprintf(str, "API:%s", name->data);
922182007Sroberto    (*name->func->release)(name);
923182007Sroberto    (*cc->func->release)(cc);
924182007Sroberto
925182007Sroberto    if (*str == NULL) {
926182007Sroberto	krb5_set_error_string(context, "out of memory");
927182007Sroberto	return ENOMEM;
928182007Sroberto    }
929182007Sroberto    return 0;
930182007Sroberto}
931182007Sroberto
932182007Sroberto
933182007Sroberto/**
934132451Sroberto * Variable containing the API based credential cache implemention.
935182007Sroberto *
936182007Sroberto * @ingroup krb5_ccache
937182007Sroberto */
938182007Sroberto
939182007Srobertoconst krb5_cc_ops krb5_acc_ops = {
940182007Sroberto    "API",
941132451Sroberto    acc_get_name,
942132451Sroberto    acc_resolve,
943132451Sroberto    acc_gen_new,
944132451Sroberto    acc_initialize,
945132451Sroberto    acc_destroy,
946132451Sroberto    acc_close,
947132451Sroberto    acc_store_cred,
948132451Sroberto    NULL, /* acc_retrieve */
949132451Sroberto    acc_get_principal,
950182007Sroberto    acc_get_first,
951182007Sroberto    acc_get_next,
952182007Sroberto    acc_end_get,
953132451Sroberto    acc_remove_cred,
954182007Sroberto    acc_set_flags,
955182007Sroberto    acc_get_version,
956182007Sroberto    acc_get_cache_first,
957182007Sroberto    acc_get_cache_next,
958182007Sroberto    acc_end_cache_get,
959182007Sroberto    acc_move,
960182007Sroberto    acc_default_name
961182007Sroberto};
962182007Sroberto