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