2 * Copyright (c) 2004 - 2007 Kungliga Tekniska Högskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
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
34 #include "krb5_locl.h"
35 #include <krb5_ccapi.h>
40 RCSID("$Id: acache.c 22099 2007-12-03 17:14:34Z lha $");
42 /* XXX should we fetch these for each open ? */
43 static HEIMDAL_MUTEX acc_mutex = HEIMDAL_MUTEX_INITIALIZER;
44 static cc_initialize_func init_func;
47 static void *cc_handle;
50 typedef struct krb5_acc {
56 static krb5_error_code acc_close(krb5_context, krb5_ccache);
58 #define ACACHE(X) ((krb5_acc *)(X)->data.data)
64 { ccErrBadName, KRB5_CC_BADNAME },
65 { ccErrCredentialsNotFound, KRB5_CC_NOTFOUND },
66 { ccErrCCacheNotFound, KRB5_FCC_NOFILE },
67 { ccErrContextNotFound, KRB5_CC_NOTFOUND },
68 { ccIteratorEnd, KRB5_CC_END },
69 { ccErrNoMem, KRB5_CC_NOMEM },
70 { ccErrServerUnavailable, KRB5_CC_NOSUPP },
74 static krb5_error_code
75 translate_cc_error(krb5_context context, cc_int32 error)
78 krb5_clear_error_string(context);
79 for(i = 0; i < sizeof(cc_errors)/sizeof(cc_errors[0]); i++)
80 if (cc_errors[i].error == error)
81 return cc_errors[i].ret;
82 return KRB5_FCC_INTERNAL;
85 static krb5_error_code
86 init_ccapi(krb5_context context)
90 HEIMDAL_MUTEX_lock(&acc_mutex);
92 HEIMDAL_MUTEX_unlock(&acc_mutex);
93 krb5_clear_error_string(context);
97 lib = krb5_config_get_string(context, NULL,
98 "libdefaults", "ccapi_library",
102 lib = "/System/Library/Frameworks/Kerberos.framework/Kerberos";
104 lib = "/usr/lib/libkrb5_cc.so";
114 cc_handle = dlopen(lib, RTLD_LAZY);
115 if (cc_handle == NULL) {
116 HEIMDAL_MUTEX_unlock(&acc_mutex);
117 krb5_set_error_string(context, "Failed to load %s", lib);
118 return KRB5_CC_NOSUPP;
121 init_func = (cc_initialize_func)dlsym(cc_handle, "cc_initialize");
122 HEIMDAL_MUTEX_unlock(&acc_mutex);
123 if (init_func == NULL) {
124 krb5_set_error_string(context, "Failed to find cc_initialize"
125 "in %s: %s", lib, dlerror());
127 return KRB5_CC_NOSUPP;
132 HEIMDAL_MUTEX_unlock(&acc_mutex);
133 krb5_set_error_string(context, "no support for shared object");
134 return KRB5_CC_NOSUPP;
138 static krb5_error_code
139 make_cred_from_ccred(krb5_context context,
140 const cc_credentials_v5_t *incred,
146 memset(cred, 0, sizeof(*cred));
148 ret = krb5_parse_name(context, incred->client, &cred->client);
152 ret = krb5_parse_name(context, incred->server, &cred->server);
156 cred->session.keytype = incred->keyblock.type;
157 cred->session.keyvalue.length = incred->keyblock.length;
158 cred->session.keyvalue.data = malloc(incred->keyblock.length);
159 if (cred->session.keyvalue.data == NULL)
161 memcpy(cred->session.keyvalue.data, incred->keyblock.data,
162 incred->keyblock.length);
164 cred->times.authtime = incred->authtime;
165 cred->times.starttime = incred->starttime;
166 cred->times.endtime = incred->endtime;
167 cred->times.renew_till = incred->renew_till;
169 ret = krb5_data_copy(&cred->ticket,
171 incred->ticket.length);
175 ret = krb5_data_copy(&cred->second_ticket,
176 incred->second_ticket.data,
177 incred->second_ticket.length);
181 cred->authdata.val = NULL;
182 cred->authdata.len = 0;
184 cred->addresses.val = NULL;
185 cred->addresses.len = 0;
187 for (i = 0; incred->authdata && incred->authdata[i]; i++)
191 cred->authdata.val = calloc(i, sizeof(cred->authdata.val[0]));
192 if (cred->authdata.val == NULL)
194 cred->authdata.len = i;
195 for (i = 0; i < cred->authdata.len; i++) {
196 cred->authdata.val[i].ad_type = incred->authdata[i]->type;
197 ret = krb5_data_copy(&cred->authdata.val[i].ad_data,
198 incred->authdata[i]->data,
199 incred->authdata[i]->length);
205 for (i = 0; incred->addresses && incred->addresses[i]; i++)
209 cred->addresses.val = calloc(i, sizeof(cred->addresses.val[0]));
210 if (cred->addresses.val == NULL)
212 cred->addresses.len = i;
214 for (i = 0; i < cred->addresses.len; i++) {
215 cred->addresses.val[i].addr_type = incred->addresses[i]->type;
216 ret = krb5_data_copy(&cred->addresses.val[i].address,
217 incred->addresses[i]->data,
218 incred->addresses[i]->length);
225 if (incred->ticket_flags & KRB5_CCAPI_TKT_FLG_FORWARDABLE)
226 cred->flags.b.forwardable = 1;
227 if (incred->ticket_flags & KRB5_CCAPI_TKT_FLG_FORWARDED)
228 cred->flags.b.forwarded = 1;
229 if (incred->ticket_flags & KRB5_CCAPI_TKT_FLG_PROXIABLE)
230 cred->flags.b.proxiable = 1;
231 if (incred->ticket_flags & KRB5_CCAPI_TKT_FLG_PROXY)
232 cred->flags.b.proxy = 1;
233 if (incred->ticket_flags & KRB5_CCAPI_TKT_FLG_MAY_POSTDATE)
234 cred->flags.b.may_postdate = 1;
235 if (incred->ticket_flags & KRB5_CCAPI_TKT_FLG_POSTDATED)
236 cred->flags.b.postdated = 1;
237 if (incred->ticket_flags & KRB5_CCAPI_TKT_FLG_INVALID)
238 cred->flags.b.invalid = 1;
239 if (incred->ticket_flags & KRB5_CCAPI_TKT_FLG_RENEWABLE)
240 cred->flags.b.renewable = 1;
241 if (incred->ticket_flags & KRB5_CCAPI_TKT_FLG_INITIAL)
242 cred->flags.b.initial = 1;
243 if (incred->ticket_flags & KRB5_CCAPI_TKT_FLG_PRE_AUTH)
244 cred->flags.b.pre_authent = 1;
245 if (incred->ticket_flags & KRB5_CCAPI_TKT_FLG_HW_AUTH)
246 cred->flags.b.hw_authent = 1;
247 if (incred->ticket_flags & KRB5_CCAPI_TKT_FLG_TRANSIT_POLICY_CHECKED)
248 cred->flags.b.transited_policy_checked = 1;
249 if (incred->ticket_flags & KRB5_CCAPI_TKT_FLG_OK_AS_DELEGATE)
250 cred->flags.b.ok_as_delegate = 1;
251 if (incred->ticket_flags & KRB5_CCAPI_TKT_FLG_ANONYMOUS)
252 cred->flags.b.anonymous = 1;
258 krb5_set_error_string(context, "malloc - out of memory");
261 krb5_free_cred_contents(context, cred);
266 free_ccred(cc_credentials_v5_t *cred)
270 if (cred->addresses) {
271 for (i = 0; cred->addresses[i] != 0; i++) {
272 if (cred->addresses[i]->data)
273 free(cred->addresses[i]->data);
274 free(cred->addresses[i]);
276 free(cred->addresses);
282 memset(cred, 0, sizeof(*cred));
285 static krb5_error_code
286 make_ccred_from_cred(krb5_context context,
287 const krb5_creds *incred,
288 cc_credentials_v5_t *cred)
293 memset(cred, 0, sizeof(*cred));
295 ret = krb5_unparse_name(context, incred->client, &cred->client);
299 ret = krb5_unparse_name(context, incred->server, &cred->server);
303 cred->keyblock.type = incred->session.keytype;
304 cred->keyblock.length = incred->session.keyvalue.length;
305 cred->keyblock.data = incred->session.keyvalue.data;
307 cred->authtime = incred->times.authtime;
308 cred->starttime = incred->times.starttime;
309 cred->endtime = incred->times.endtime;
310 cred->renew_till = incred->times.renew_till;
312 cred->ticket.length = incred->ticket.length;
313 cred->ticket.data = incred->ticket.data;
315 cred->second_ticket.length = incred->second_ticket.length;
316 cred->second_ticket.data = incred->second_ticket.data;
318 /* XXX this one should also be filled in */
319 cred->authdata = NULL;
321 cred->addresses = calloc(incred->addresses.len + 1,
322 sizeof(cred->addresses[0]));
323 if (cred->addresses == NULL) {
329 for (i = 0; i < incred->addresses.len; i++) {
331 addr = malloc(sizeof(*addr));
336 addr->type = incred->addresses.val[i].addr_type;
337 addr->length = incred->addresses.val[i].address.length;
338 addr->data = malloc(addr->length);
339 if (addr->data == NULL) {
343 memcpy(addr->data, incred->addresses.val[i].address.data,
345 cred->addresses[i] = addr;
347 cred->addresses[i] = NULL;
349 cred->ticket_flags = 0;
350 if (incred->flags.b.forwardable)
351 cred->ticket_flags |= KRB5_CCAPI_TKT_FLG_FORWARDABLE;
352 if (incred->flags.b.forwarded)
353 cred->ticket_flags |= KRB5_CCAPI_TKT_FLG_FORWARDED;
354 if (incred->flags.b.proxiable)
355 cred->ticket_flags |= KRB5_CCAPI_TKT_FLG_PROXIABLE;
356 if (incred->flags.b.proxy)
357 cred->ticket_flags |= KRB5_CCAPI_TKT_FLG_PROXY;
358 if (incred->flags.b.may_postdate)
359 cred->ticket_flags |= KRB5_CCAPI_TKT_FLG_MAY_POSTDATE;
360 if (incred->flags.b.postdated)
361 cred->ticket_flags |= KRB5_CCAPI_TKT_FLG_POSTDATED;
362 if (incred->flags.b.invalid)
363 cred->ticket_flags |= KRB5_CCAPI_TKT_FLG_INVALID;
364 if (incred->flags.b.renewable)
365 cred->ticket_flags |= KRB5_CCAPI_TKT_FLG_RENEWABLE;
366 if (incred->flags.b.initial)
367 cred->ticket_flags |= KRB5_CCAPI_TKT_FLG_INITIAL;
368 if (incred->flags.b.pre_authent)
369 cred->ticket_flags |= KRB5_CCAPI_TKT_FLG_PRE_AUTH;
370 if (incred->flags.b.hw_authent)
371 cred->ticket_flags |= KRB5_CCAPI_TKT_FLG_HW_AUTH;
372 if (incred->flags.b.transited_policy_checked)
373 cred->ticket_flags |= KRB5_CCAPI_TKT_FLG_TRANSIT_POLICY_CHECKED;
374 if (incred->flags.b.ok_as_delegate)
375 cred->ticket_flags |= KRB5_CCAPI_TKT_FLG_OK_AS_DELEGATE;
376 if (incred->flags.b.anonymous)
377 cred->ticket_flags |= KRB5_CCAPI_TKT_FLG_ANONYMOUS;
384 krb5_clear_error_string(context);
389 get_cc_name(cc_ccache_t cache)
395 error = (*cache->func->get_name)(cache, &name);
399 str = strdup(name->data);
400 (*name->func->release)(name);
406 acc_get_name(krb5_context context,
409 krb5_acc *a = ACACHE(id);
413 name = get_cc_name(a->ccache);
415 krb5_set_error_string(context, "malloc: out of memory");
418 strlcpy(n, name, sizeof(n));
423 static krb5_error_code
424 acc_alloc(krb5_context context, krb5_ccache *id)
430 ret = init_ccapi(context);
434 ret = krb5_data_alloc(&(*id)->data, sizeof(*a));
436 krb5_clear_error_string(context);
442 error = (*init_func)(&a->context, ccapi_version_3, NULL, NULL);
444 krb5_data_free(&(*id)->data);
445 return translate_cc_error(context, error);
448 a->cache_name = NULL;
453 static krb5_error_code
454 acc_resolve(krb5_context context, krb5_ccache *id, const char *res)
460 ret = acc_alloc(context, id);
466 error = (*a->context->func->open_ccache)(a->context, res,
469 a->cache_name = get_cc_name(a->ccache);
470 if (a->cache_name == NULL) {
471 acc_close(context, *id);
473 krb5_set_error_string(context, "malloc: out of memory");
476 } else if (error == ccErrCCacheNotFound) {
478 a->cache_name = NULL;
482 return translate_cc_error(context, error);
488 static krb5_error_code
489 acc_gen_new(krb5_context context, krb5_ccache *id)
494 ret = acc_alloc(context, id);
501 a->cache_name = NULL;
506 static krb5_error_code
507 acc_initialize(krb5_context context,
509 krb5_principal primary_principal)
511 krb5_acc *a = ACACHE(id);
516 ret = krb5_unparse_name(context, primary_principal, &name);
520 error = (*a->context->func->create_new_ccache)(a->context,
526 return translate_cc_error(context, error);
529 static krb5_error_code
530 acc_close(krb5_context context,
533 krb5_acc *a = ACACHE(id);
536 (*a->ccache->func->release)(a->ccache);
541 a->cache_name = NULL;
543 (*a->context->func->release)(a->context);
545 krb5_data_free(&id->data);
549 static krb5_error_code
550 acc_destroy(krb5_context context,
553 krb5_acc *a = ACACHE(id);
557 error = (*a->ccache->func->destroy)(a->ccache);
561 error = (a->context->func->release)(a->context);
564 return translate_cc_error(context, error);
567 static krb5_error_code
568 acc_store_cred(krb5_context context,
572 krb5_acc *a = ACACHE(id);
573 cc_credentials_union cred;
574 cc_credentials_v5_t v5cred;
578 if (a->ccache == NULL) {
579 krb5_set_error_string(context, "No API credential found");
580 return KRB5_CC_NOTFOUND;
583 cred.version = cc_credentials_v5;
584 cred.credentials.credentials_v5 = &v5cred;
586 ret = make_ccred_from_cred(context,
592 error = (*a->ccache->func->store_credentials)(a->ccache, &cred);
594 ret = translate_cc_error(context, error);
601 static krb5_error_code
602 acc_get_principal(krb5_context context,
604 krb5_principal *principal)
606 krb5_acc *a = ACACHE(id);
611 if (a->ccache == NULL) {
612 krb5_set_error_string(context, "No API credential found");
613 return KRB5_CC_NOTFOUND;
616 error = (*a->ccache->func->get_principal)(a->ccache,
620 return translate_cc_error(context, error);
622 ret = krb5_parse_name(context, name->data, principal);
624 (*name->func->release)(name);
628 static krb5_error_code
629 acc_get_first (krb5_context context,
631 krb5_cc_cursor *cursor)
633 cc_credentials_iterator_t iter;
634 krb5_acc *a = ACACHE(id);
637 if (a->ccache == NULL) {
638 krb5_set_error_string(context, "No API credential found");
639 return KRB5_CC_NOTFOUND;
642 error = (*a->ccache->func->new_credentials_iterator)(a->ccache, &iter);
644 krb5_clear_error_string(context);
652 static krb5_error_code
653 acc_get_next (krb5_context context,
655 krb5_cc_cursor *cursor,
658 cc_credentials_iterator_t iter = *cursor;
659 cc_credentials_t cred;
664 error = (*iter->func->next)(iter, &cred);
666 return translate_cc_error(context, error);
667 if (cred->data->version == cc_credentials_v5)
669 (*cred->func->release)(cred);
672 ret = make_cred_from_ccred(context,
673 cred->data->credentials.credentials_v5,
675 (*cred->func->release)(cred);
679 static krb5_error_code
680 acc_end_get (krb5_context context,
682 krb5_cc_cursor *cursor)
684 cc_credentials_iterator_t iter = *cursor;
685 (*iter->func->release)(iter);
689 static krb5_error_code
690 acc_remove_cred(krb5_context context,
695 cc_credentials_iterator_t iter;
696 krb5_acc *a = ACACHE(id);
697 cc_credentials_t ccred;
700 char *client, *server;
702 if (a->ccache == NULL) {
703 krb5_set_error_string(context, "No API credential found");
704 return KRB5_CC_NOTFOUND;
708 ret = krb5_unparse_name(context, cred->client, &client);
714 ret = krb5_unparse_name(context, cred->server, &server);
720 error = (*a->ccache->func->new_credentials_iterator)(a->ccache, &iter);
724 return translate_cc_error(context, error);
727 ret = KRB5_CC_NOTFOUND;
729 cc_credentials_v5_t *v5cred;
731 error = (*iter->func->next)(iter, &ccred);
735 if (ccred->data->version != cc_credentials_v5)
738 v5cred = ccred->data->credentials.credentials_v5;
740 if (client && strcmp(v5cred->client, client) != 0)
743 if (strcmp(v5cred->server, server) != 0)
746 (*a->ccache->func->remove_credentials)(a->ccache, ccred);
749 (*ccred->func->release)(ccred);
752 (*iter->func->release)(iter);
755 krb5_set_error_string(context, "Can't find credential %s in cache",
763 static krb5_error_code
764 acc_set_flags(krb5_context context,
771 static krb5_error_code
772 acc_get_version(krb5_context context,
779 cc_context_t context;
780 cc_ccache_iterator_t iter;
783 static krb5_error_code
784 acc_get_cache_first(krb5_context context, krb5_cc_cursor *cursor)
786 struct cache_iter *iter;
790 ret = init_ccapi(context);
794 iter = calloc(1, sizeof(*iter));
796 krb5_set_error_string(context, "malloc - out of memory");
800 error = (*init_func)(&iter->context, ccapi_version_3, NULL, NULL);
803 return translate_cc_error(context, error);
806 error = (*iter->context->func->new_ccache_iterator)(iter->context,
810 krb5_clear_error_string(context);
817 static krb5_error_code
818 acc_get_cache_next(krb5_context context, krb5_cc_cursor cursor, krb5_ccache *id)
820 struct cache_iter *iter = cursor;
826 error = (*iter->iter->func->next)(iter->iter, &cache);
828 return translate_cc_error(context, error);
830 ret = _krb5_cc_allocate(context, &krb5_acc_ops, id);
832 (*cache->func->release)(cache);
836 ret = acc_alloc(context, id);
838 (*cache->func->release)(cache);
846 a->cache_name = get_cc_name(a->ccache);
847 if (a->cache_name == NULL) {
848 acc_close(context, *id);
850 krb5_set_error_string(context, "malloc: out of memory");
856 static krb5_error_code
857 acc_end_cache_get(krb5_context context, krb5_cc_cursor cursor)
859 struct cache_iter *iter = cursor;
861 (*iter->iter->func->release)(iter->iter);
863 (*iter->context->func->release)(iter->context);
864 iter->context = NULL;
869 static krb5_error_code
870 acc_move(krb5_context context, krb5_ccache from, krb5_ccache to)
872 krb5_acc *afrom = ACACHE(from);
873 krb5_acc *ato = ACACHE(to);
876 if (ato->ccache == NULL) {
879 error = (*afrom->ccache->func->get_principal)(afrom->ccache,
883 return translate_cc_error(context, error);
885 error = (*ato->context->func->create_new_ccache)(ato->context,
889 (*name->func->release)(name);
891 return translate_cc_error(context, error);
895 error = (*ato->ccache->func->move)(afrom->ccache, ato->ccache);
896 return translate_cc_error(context, error);
899 static krb5_error_code
900 acc_default_name(krb5_context context, char **str)
907 ret = init_ccapi(context);
911 error = (*init_func)(&cc, ccapi_version_3, NULL, NULL);
913 return translate_cc_error(context, error);
915 error = (*cc->func->get_default_ccache_name)(cc, &name);
917 (*cc->func->release)(cc);
918 return translate_cc_error(context, error);
921 asprintf(str, "API:%s", name->data);
922 (*name->func->release)(name);
923 (*cc->func->release)(cc);
926 krb5_set_error_string(context, "out of memory");
934 * Variable containing the API based credential cache implemention.
936 * @ingroup krb5_ccache
939 const krb5_cc_ops krb5_acc_ops = {
948 NULL, /* acc_retrieve */