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 22669 2008-03-09 23:39:25Z 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(krb5_acc *a)
394 error = (*a->ccache->func->get_name)(a->ccache, &name);
398 a->cache_name = strdup(name->data);
399 (*name->func->release)(name);
400 if (a->cache_name == NULL)
407 acc_get_name(krb5_context context,
410 krb5_acc *a = ACACHE(id);
413 if (a->cache_name == NULL) {
415 krb5_principal principal;
418 ret = _krb5_get_default_principal_local(context, &principal);
422 ret = krb5_unparse_name(context, principal, &name);
423 krb5_free_principal(context, principal);
427 error = (*a->context->func->create_new_ccache)(a->context,
435 error = get_cc_name(a);
440 return a->cache_name;
443 static krb5_error_code
444 acc_alloc(krb5_context context, krb5_ccache *id)
450 ret = init_ccapi(context);
454 ret = krb5_data_alloc(&(*id)->data, sizeof(*a));
456 krb5_clear_error_string(context);
462 error = (*init_func)(&a->context, ccapi_version_3, NULL, NULL);
464 krb5_data_free(&(*id)->data);
465 return translate_cc_error(context, error);
468 a->cache_name = NULL;
473 static krb5_error_code
474 acc_resolve(krb5_context context, krb5_ccache *id, const char *res)
480 ret = acc_alloc(context, id);
486 error = (*a->context->func->open_ccache)(a->context, res, &a->ccache);
487 if (error == ccNoError) {
488 error = get_cc_name(a);
489 if (error != ccNoError) {
490 acc_close(context, *id);
492 return translate_cc_error(context, error);
494 } else if (error == ccErrCCacheNotFound) {
496 a->cache_name = NULL;
500 return translate_cc_error(context, error);
506 static krb5_error_code
507 acc_gen_new(krb5_context context, krb5_ccache *id)
512 ret = acc_alloc(context, id);
519 a->cache_name = NULL;
524 static krb5_error_code
525 acc_initialize(krb5_context context,
527 krb5_principal primary_principal)
529 krb5_acc *a = ACACHE(id);
534 ret = krb5_unparse_name(context, primary_principal, &name);
538 if (a->cache_name == NULL) {
539 error = (*a->context->func->create_new_ccache)(a->context,
544 if (error == ccNoError)
545 error = get_cc_name(a);
547 cc_credentials_iterator_t iter;
548 cc_credentials_t ccred;
550 error = (*a->ccache->func->new_credentials_iterator)(a->ccache, &iter);
553 return translate_cc_error(context, error);
557 error = (*iter->func->next)(iter, &ccred);
560 (*a->ccache->func->remove_credentials)(a->ccache, ccred);
561 (*ccred->func->release)(ccred);
563 (*iter->func->release)(iter);
565 error = (*a->ccache->func->set_principal)(a->ccache,
570 return translate_cc_error(context, error);
573 static krb5_error_code
574 acc_close(krb5_context context,
577 krb5_acc *a = ACACHE(id);
580 (*a->ccache->func->release)(a->ccache);
585 a->cache_name = NULL;
587 (*a->context->func->release)(a->context);
589 krb5_data_free(&id->data);
593 static krb5_error_code
594 acc_destroy(krb5_context context,
597 krb5_acc *a = ACACHE(id);
601 error = (*a->ccache->func->destroy)(a->ccache);
605 error = (a->context->func->release)(a->context);
608 return translate_cc_error(context, error);
611 static krb5_error_code
612 acc_store_cred(krb5_context context,
616 krb5_acc *a = ACACHE(id);
617 cc_credentials_union cred;
618 cc_credentials_v5_t v5cred;
622 if (a->ccache == NULL) {
623 krb5_set_error_string(context, "No API credential found");
624 return KRB5_CC_NOTFOUND;
627 cred.version = cc_credentials_v5;
628 cred.credentials.credentials_v5 = &v5cred;
630 ret = make_ccred_from_cred(context,
636 error = (*a->ccache->func->store_credentials)(a->ccache, &cred);
638 ret = translate_cc_error(context, error);
645 static krb5_error_code
646 acc_get_principal(krb5_context context,
648 krb5_principal *principal)
650 krb5_acc *a = ACACHE(id);
655 if (a->ccache == NULL) {
656 krb5_set_error_string(context, "No API credential found");
657 return KRB5_CC_NOTFOUND;
660 error = (*a->ccache->func->get_principal)(a->ccache,
664 return translate_cc_error(context, error);
666 ret = krb5_parse_name(context, name->data, principal);
668 (*name->func->release)(name);
672 static krb5_error_code
673 acc_get_first (krb5_context context,
675 krb5_cc_cursor *cursor)
677 cc_credentials_iterator_t iter;
678 krb5_acc *a = ACACHE(id);
681 if (a->ccache == NULL) {
682 krb5_set_error_string(context, "No API credential found");
683 return KRB5_CC_NOTFOUND;
686 error = (*a->ccache->func->new_credentials_iterator)(a->ccache, &iter);
688 krb5_clear_error_string(context);
696 static krb5_error_code
697 acc_get_next (krb5_context context,
699 krb5_cc_cursor *cursor,
702 cc_credentials_iterator_t iter = *cursor;
703 cc_credentials_t cred;
708 error = (*iter->func->next)(iter, &cred);
710 return translate_cc_error(context, error);
711 if (cred->data->version == cc_credentials_v5)
713 (*cred->func->release)(cred);
716 ret = make_cred_from_ccred(context,
717 cred->data->credentials.credentials_v5,
719 (*cred->func->release)(cred);
723 static krb5_error_code
724 acc_end_get (krb5_context context,
726 krb5_cc_cursor *cursor)
728 cc_credentials_iterator_t iter = *cursor;
729 (*iter->func->release)(iter);
733 static krb5_error_code
734 acc_remove_cred(krb5_context context,
739 cc_credentials_iterator_t iter;
740 krb5_acc *a = ACACHE(id);
741 cc_credentials_t ccred;
744 char *client, *server;
746 if (a->ccache == NULL) {
747 krb5_set_error_string(context, "No API credential found");
748 return KRB5_CC_NOTFOUND;
752 ret = krb5_unparse_name(context, cred->client, &client);
758 ret = krb5_unparse_name(context, cred->server, &server);
764 error = (*a->ccache->func->new_credentials_iterator)(a->ccache, &iter);
768 return translate_cc_error(context, error);
771 ret = KRB5_CC_NOTFOUND;
773 cc_credentials_v5_t *v5cred;
775 error = (*iter->func->next)(iter, &ccred);
779 if (ccred->data->version != cc_credentials_v5)
782 v5cred = ccred->data->credentials.credentials_v5;
784 if (client && strcmp(v5cred->client, client) != 0)
787 if (strcmp(v5cred->server, server) != 0)
790 (*a->ccache->func->remove_credentials)(a->ccache, ccred);
793 (*ccred->func->release)(ccred);
796 (*iter->func->release)(iter);
799 krb5_set_error_string(context, "Can't find credential %s in cache",
807 static krb5_error_code
808 acc_set_flags(krb5_context context,
815 static krb5_error_code
816 acc_get_version(krb5_context context,
823 cc_context_t context;
824 cc_ccache_iterator_t iter;
827 static krb5_error_code
828 acc_get_cache_first(krb5_context context, krb5_cc_cursor *cursor)
830 struct cache_iter *iter;
834 ret = init_ccapi(context);
838 iter = calloc(1, sizeof(*iter));
840 krb5_set_error_string(context, "malloc - out of memory");
844 error = (*init_func)(&iter->context, ccapi_version_3, NULL, NULL);
847 return translate_cc_error(context, error);
850 error = (*iter->context->func->new_ccache_iterator)(iter->context,
854 krb5_clear_error_string(context);
861 static krb5_error_code
862 acc_get_cache_next(krb5_context context, krb5_cc_cursor cursor, krb5_ccache *id)
864 struct cache_iter *iter = cursor;
870 error = (*iter->iter->func->next)(iter->iter, &cache);
872 return translate_cc_error(context, error);
874 ret = _krb5_cc_allocate(context, &krb5_acc_ops, id);
876 (*cache->func->release)(cache);
880 ret = acc_alloc(context, id);
882 (*cache->func->release)(cache);
890 error = get_cc_name(a);
892 acc_close(context, *id);
894 return translate_cc_error(context, error);
899 static krb5_error_code
900 acc_end_cache_get(krb5_context context, krb5_cc_cursor cursor)
902 struct cache_iter *iter = cursor;
904 (*iter->iter->func->release)(iter->iter);
906 (*iter->context->func->release)(iter->context);
907 iter->context = NULL;
912 static krb5_error_code
913 acc_move(krb5_context context, krb5_ccache from, krb5_ccache to)
915 krb5_acc *afrom = ACACHE(from);
916 krb5_acc *ato = ACACHE(to);
919 if (ato->ccache == NULL) {
922 error = (*afrom->ccache->func->get_principal)(afrom->ccache,
926 return translate_cc_error(context, error);
928 error = (*ato->context->func->create_new_ccache)(ato->context,
932 (*name->func->release)(name);
934 return translate_cc_error(context, error);
938 error = (*ato->ccache->func->move)(afrom->ccache, ato->ccache);
939 return translate_cc_error(context, error);
942 static krb5_error_code
943 acc_default_name(krb5_context context, char **str)
950 ret = init_ccapi(context);
954 error = (*init_func)(&cc, ccapi_version_3, NULL, NULL);
956 return translate_cc_error(context, error);
958 error = (*cc->func->get_default_ccache_name)(cc, &name);
960 (*cc->func->release)(cc);
961 return translate_cc_error(context, error);
964 asprintf(str, "API:%s", name->data);
965 (*name->func->release)(name);
966 (*cc->func->release)(cc);
969 krb5_set_error_string(context, "out of memory");
977 * Variable containing the API based credential cache implemention.
979 * @ingroup krb5_ccache
982 const krb5_cc_ops krb5_acc_ops = {
991 NULL, /* acc_retrieve */