heimdal:camellia: include roken.h
[heimdal.git] / lib / krb5 / acache.c
blob0ecda99348bb35936fb715b816f9872632499601
1 /*
2 * Copyright (c) 2004 - 2007 Kungliga Tekniska Högskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
4 * All rights reserved.
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
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.
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.
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.
34 #include "krb5_locl.h"
35 #include <krb5_ccapi.h>
36 #ifdef HAVE_DLFCN_H
37 #include <dlfcn.h>
38 #endif
40 static HEIMDAL_MUTEX acc_mutex = HEIMDAL_MUTEX_INITIALIZER;
41 static cc_initialize_func init_func;
42 #ifdef HAVE_DLOPEN
43 static void *cc_handle;
44 #endif
46 typedef struct krb5_acc {
47 char *cache_name;
48 cc_context_t context;
49 cc_ccache_t ccache;
50 } krb5_acc;
52 static krb5_error_code acc_close(krb5_context, krb5_ccache);
54 #define ACACHE(X) ((krb5_acc *)(X)->data.data)
56 static 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 }
71 static krb5_error_code
72 translate_cc_error(krb5_context context, cc_int32 error)
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;
82 static krb5_error_code
83 init_ccapi(krb5_context context)
85 const char *lib;
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;
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
105 #ifdef HAVE_DLOPEN
107 #ifndef RTLD_LAZY
108 #define RTLD_LAZY 0
109 #endif
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;
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;
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
139 static krb5_error_code
140 make_cred_from_ccred(krb5_context context,
141 const cc_credentials_v5_t *incred,
142 krb5_creds *cred)
144 krb5_error_code ret;
145 unsigned int i;
147 memset(cred, 0, sizeof(*cred));
149 ret = krb5_parse_name(context, incred->client, &cred->client);
150 if (ret)
151 goto fail;
153 ret = krb5_parse_name(context, incred->server, &cred->server);
154 if (ret)
155 goto fail;
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);
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;
170 ret = krb5_data_copy(&cred->ticket,
171 incred->ticket.data,
172 incred->ticket.length);
173 if (ret)
174 goto nomem;
176 ret = krb5_data_copy(&cred->second_ticket,
177 incred->second_ticket.data,
178 incred->second_ticket.length);
179 if (ret)
180 goto nomem;
182 cred->authdata.val = NULL;
183 cred->authdata.len = 0;
185 cred->addresses.val = NULL;
186 cred->addresses.len = 0;
188 for (i = 0; incred->authdata && incred->authdata[i]; i++)
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;
206 for (i = 0; incred->addresses && incred->addresses[i]; i++)
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;
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;
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;
255 return 0;
257 nomem:
258 ret = ENOMEM;
259 krb5_set_error_message(context, ret, N_("malloc: out of memory", "malloc"));
261 fail:
262 krb5_free_cred_contents(context, cred);
263 return ret;
266 static void
267 free_ccred(cc_credentials_v5_t *cred)
269 int i;
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]);
277 free(cred->addresses);
279 if (cred->server)
280 free(cred->server);
281 if (cred->client)
282 free(cred->client);
283 memset(cred, 0, sizeof(*cred));
286 static krb5_error_code
287 make_ccred_from_cred(krb5_context context,
288 const krb5_creds *incred,
289 cc_credentials_v5_t *cred)
291 krb5_error_code ret;
292 int i;
294 memset(cred, 0, sizeof(*cred));
296 ret = krb5_unparse_name(context, incred->client, &cred->client);
297 if (ret)
298 goto fail;
300 ret = krb5_unparse_name(context, incred->server, &cred->server);
301 if (ret)
302 goto fail;
304 cred->keyblock.type = incred->session.keytype;
305 cred->keyblock.length = incred->session.keyvalue.length;
306 cred->keyblock.data = incred->session.keyvalue.data;
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;
313 cred->ticket.length = incred->ticket.length;
314 cred->ticket.data = incred->ticket.data;
316 cred->second_ticket.length = incred->second_ticket.length;
317 cred->second_ticket.data = incred->second_ticket.data;
319 /* XXX this one should also be filled in */
320 cred->authdata = NULL;
322 cred->addresses = calloc(incred->addresses.len + 1,
323 sizeof(cred->addresses[0]));
324 if (cred->addresses == NULL) {
326 ret = ENOMEM;
327 goto fail;
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;
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;
345 memcpy(addr->data, incred->addresses.val[i].address.data,
346 addr->length);
347 cred->addresses[i] = addr;
349 cred->addresses[i] = NULL;
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;
381 return 0;
383 fail:
384 free_ccred(cred);
386 krb5_clear_error_message(context);
387 return ret;
390 static cc_int32
391 get_cc_name(krb5_acc *a)
393 cc_string_t name;
394 cc_int32 error;
396 error = (*a->ccache->func->get_name)(a->ccache, &name);
397 if (error)
398 return error;
400 a->cache_name = strdup(name->data);
401 (*name->func->release)(name);
402 if (a->cache_name == NULL)
403 return ccErrNoMem;
404 return ccNoError;
408 static const char*
409 acc_get_name(krb5_context context,
410 krb5_ccache id)
412 krb5_acc *a = ACACHE(id);
413 int32_t error;
415 if (a->cache_name == NULL) {
416 krb5_error_code ret;
417 krb5_principal principal;
418 char *name;
420 ret = _krb5_get_default_principal_local(context, &principal);
421 if (ret)
422 return NULL;
424 ret = krb5_unparse_name(context, principal, &name);
425 krb5_free_principal(context, principal);
426 if (ret)
427 return NULL;
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;
437 error = get_cc_name(a);
438 if (error)
439 return NULL;
442 return a->cache_name;
445 static krb5_error_code
446 acc_alloc(krb5_context context, krb5_ccache *id)
448 krb5_error_code ret;
449 cc_int32 error;
450 krb5_acc *a;
452 ret = init_ccapi(context);
453 if (ret)
454 return ret;
456 ret = krb5_data_alloc(&(*id)->data, sizeof(*a));
457 if (ret) {
458 krb5_clear_error_message(context);
459 return ret;
462 a = ACACHE(*id);
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);
470 a->cache_name = NULL;
472 return 0;
475 static krb5_error_code
476 acc_resolve(krb5_context context, krb5_ccache *id, const char *res)
478 krb5_error_code ret;
479 cc_int32 error;
480 krb5_acc *a;
482 ret = acc_alloc(context, id);
483 if (ret)
484 return ret;
486 a = ACACHE(*id);
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);
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;
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);
512 return 0;
515 static krb5_error_code
516 acc_gen_new(krb5_context context, krb5_ccache *id)
518 krb5_error_code ret;
519 krb5_acc *a;
521 ret = acc_alloc(context, id);
522 if (ret)
523 return ret;
525 a = ACACHE(*id);
527 a->ccache = NULL;
528 a->cache_name = NULL;
530 return 0;
533 static krb5_error_code
534 acc_initialize(krb5_context context,
535 krb5_ccache id,
536 krb5_principal primary_principal)
538 krb5_acc *a = ACACHE(id);
539 krb5_error_code ret;
540 int32_t error;
541 char *name;
543 ret = krb5_unparse_name(context, primary_principal, &name);
544 if (ret)
545 return ret;
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;
559 error = (*a->ccache->func->new_credentials_iterator)(a->ccache, &iter);
560 if (error) {
561 free(name);
562 return translate_cc_error(context, error);
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);
572 (*iter->func->release)(iter);
574 error = (*a->ccache->func->set_principal)(a->ccache,
575 cc_credentials_v5,
576 name);
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);
584 return translate_cc_error(context, error);
587 static krb5_error_code
588 acc_close(krb5_context context,
589 krb5_ccache id)
591 krb5_acc *a = ACACHE(id);
593 if (a->ccache) {
594 (*a->ccache->func->release)(a->ccache);
595 a->ccache = NULL;
597 if (a->cache_name) {
598 free(a->cache_name);
599 a->cache_name = NULL;
601 if (a->context) {
602 (*a->context->func->release)(a->context);
603 a->context = NULL;
605 krb5_data_free(&id->data);
606 return 0;
609 static krb5_error_code
610 acc_destroy(krb5_context context,
611 krb5_ccache id)
613 krb5_acc *a = ACACHE(id);
614 cc_int32 error = 0;
616 if (a->ccache) {
617 error = (*a->ccache->func->destroy)(a->ccache);
618 a->ccache = NULL;
620 if (a->context) {
621 error = (a->context->func->release)(a->context);
622 a->context = NULL;
624 return translate_cc_error(context, error);
627 static krb5_error_code
628 acc_store_cred(krb5_context context,
629 krb5_ccache id,
630 krb5_creds *creds)
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;
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;
644 cred.version = cc_credentials_v5;
645 cred.credentials.credentials_v5 = &v5cred;
647 ret = make_ccred_from_cred(context,
648 creds,
649 &v5cred);
650 if (ret)
651 return ret;
653 error = (*a->ccache->func->store_credentials)(a->ccache, &cred);
654 if (error)
655 ret = translate_cc_error(context, error);
657 free_ccred(&v5cred);
659 return ret;
662 static krb5_error_code
663 acc_get_principal(krb5_context context,
664 krb5_ccache id,
665 krb5_principal *principal)
667 krb5_acc *a = ACACHE(id);
668 krb5_error_code ret;
669 int32_t error;
670 cc_string_t name;
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;
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);
684 ret = krb5_parse_name(context, name->data, principal);
686 (*name->func->release)(name);
687 return ret;
690 static krb5_error_code
691 acc_get_first (krb5_context context,
692 krb5_ccache id,
693 krb5_cc_cursor *cursor)
695 cc_credentials_iterator_t iter;
696 krb5_acc *a = ACACHE(id);
697 int32_t error;
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;
705 error = (*a->ccache->func->new_credentials_iterator)(a->ccache, &iter);
706 if (error) {
707 krb5_clear_error_message(context);
708 return ENOENT;
710 *cursor = iter;
711 return 0;
715 static krb5_error_code
716 acc_get_next (krb5_context context,
717 krb5_ccache id,
718 krb5_cc_cursor *cursor,
719 krb5_creds *creds)
721 cc_credentials_iterator_t iter = *cursor;
722 cc_credentials_t cred;
723 krb5_error_code ret;
724 int32_t error;
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);
735 ret = make_cred_from_ccred(context,
736 cred->data->credentials.credentials_v5,
737 creds);
738 (*cred->func->release)(cred);
739 return ret;
742 static krb5_error_code
743 acc_end_get (krb5_context context,
744 krb5_ccache id,
745 krb5_cc_cursor *cursor)
747 cc_credentials_iterator_t iter = *cursor;
748 (*iter->func->release)(iter);
749 return 0;
752 static krb5_error_code
753 acc_remove_cred(krb5_context context,
754 krb5_ccache id,
755 krb5_flags which,
756 krb5_creds *cred)
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;
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;
771 if (cred->client) {
772 ret = krb5_unparse_name(context, cred->client, &client);
773 if (ret)
774 return ret;
775 } else
776 client = NULL;
778 ret = krb5_unparse_name(context, cred->server, &server);
779 if (ret) {
780 free(client);
781 return ret;
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);
791 ret = KRB5_CC_NOTFOUND;
792 while (1) {
793 cc_credentials_v5_t *v5cred;
795 error = (*iter->func->next)(iter, &ccred);
796 if (error)
797 break;
799 if (ccred->data->version != cc_credentials_v5)
800 goto next;
802 v5cred = ccred->data->credentials.credentials_v5;
804 if (client && strcmp(v5cred->client, client) != 0)
805 goto next;
807 if (strcmp(v5cred->server, server) != 0)
808 goto next;
810 (*a->ccache->func->remove_credentials)(a->ccache, ccred);
811 ret = 0;
812 next:
813 (*ccred->func->release)(ccred);
816 (*iter->func->release)(iter);
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);
825 return ret;
828 static krb5_error_code
829 acc_set_flags(krb5_context context,
830 krb5_ccache id,
831 krb5_flags flags)
833 return 0;
836 static int
837 acc_get_version(krb5_context context,
838 krb5_ccache id)
840 return 0;
843 struct cache_iter {
844 cc_context_t context;
845 cc_ccache_iterator_t iter;
848 static krb5_error_code
849 acc_get_cache_first(krb5_context context, krb5_cc_cursor *cursor)
851 struct cache_iter *iter;
852 krb5_error_code ret;
853 cc_int32 error;
855 ret = init_ccapi(context);
856 if (ret)
857 return ret;
859 iter = calloc(1, sizeof(*iter));
860 if (iter == NULL) {
861 krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
862 return ENOMEM;
865 error = (*init_func)(&iter->context, ccapi_version_3, NULL, NULL);
866 if (error) {
867 free(iter);
868 return translate_cc_error(context, error);
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;
878 *cursor = iter;
879 return 0;
882 static krb5_error_code
883 acc_get_cache_next(krb5_context context, krb5_cc_cursor cursor, krb5_ccache *id)
885 struct cache_iter *iter = cursor;
886 cc_ccache_t cache;
887 krb5_acc *a;
888 krb5_error_code ret;
889 int32_t error;
891 error = (*iter->iter->func->next)(iter->iter, &cache);
892 if (error)
893 return translate_cc_error(context, error);
895 ret = _krb5_cc_allocate(context, &krb5_acc_ops, id);
896 if (ret) {
897 (*cache->func->release)(cache);
898 return ret;
901 ret = acc_alloc(context, id);
902 if (ret) {
903 (*cache->func->release)(cache);
904 free(*id);
905 return ret;
908 a = ACACHE(*id);
909 a->ccache = cache;
911 error = get_cc_name(a);
912 if (error) {
913 acc_close(context, *id);
914 *id = NULL;
915 return translate_cc_error(context, error);
917 return 0;
920 static krb5_error_code
921 acc_end_cache_get(krb5_context context, krb5_cc_cursor cursor)
923 struct cache_iter *iter = cursor;
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;
933 static krb5_error_code
934 acc_move(krb5_context context, krb5_ccache from, krb5_ccache to)
936 krb5_acc *afrom = ACACHE(from);
937 krb5_acc *ato = ACACHE(to);
938 int32_t error;
940 if (ato->ccache == NULL) {
941 cc_string_t name;
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);
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);
958 error = (*ato->ccache->func->move)(afrom->ccache, ato->ccache);
960 acc_destroy(context, from);
962 return translate_cc_error(context, error);
965 static krb5_error_code
966 acc_get_default_name(krb5_context context, char **str)
968 krb5_error_code ret;
969 cc_context_t cc;
970 cc_string_t name;
971 int32_t error;
973 ret = init_ccapi(context);
974 if (ret)
975 return ret;
977 error = (*init_func)(&cc, ccapi_version_3, NULL, NULL);
978 if (error)
979 return translate_cc_error(context, error);
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);
987 asprintf(str, "API:%s", name->data);
988 (*name->func->release)(name);
989 (*cc->func->release)(cc);
991 if (*str == NULL) {
992 krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
993 return ENOMEM;
995 return 0;
998 static krb5_error_code
999 acc_set_default(krb5_context context, krb5_ccache id)
1001 krb5_acc *a = ACACHE(id);
1002 cc_int32 error;
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;
1010 error = (*a->ccache->func->set_default)(a->ccache);
1011 if (error)
1012 return translate_cc_error(context, error);
1014 return 0;
1017 static krb5_error_code
1018 acc_lastchange(krb5_context context, krb5_ccache id, krb5_timestamp *mtime)
1020 krb5_acc *a = ACACHE(id);
1021 cc_int32 error;
1022 cc_time_t t;
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;
1030 error = (*a->ccache->func->get_change_time)(a->ccache, &t);
1031 if (error)
1032 return translate_cc_error(context, error);
1034 *mtime = t;
1036 return 0;
1040 * Variable containing the API based credential cache implemention.
1042 * @ingroup krb5_ccache
1045 KRB5_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