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>
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 },
71 { ccErrInvalidCCache, KRB5_CC_BADNAME },
75 static krb5_error_code
76 translate_cc_error(krb5_context context, cc_int32 error)
79 krb5_clear_error_string(context);
80 for(i = 0; i < sizeof(cc_errors)/sizeof(cc_errors[0]); i++)
81 if (cc_errors[i].error == error)
82 return cc_errors[i].ret;
83 return KRB5_FCC_INTERNAL;
86 static krb5_error_code
87 init_ccapi(krb5_context context)
91 HEIMDAL_MUTEX_lock(&acc_mutex);
93 HEIMDAL_MUTEX_unlock(&acc_mutex);
94 krb5_clear_error_string(context);
98 lib = krb5_config_get_string(context, NULL,
99 "libdefaults", "ccapi_library",
103 lib = "/System/Library/Frameworks/Kerberos.framework/Kerberos";
105 lib = "/usr/lib/libkrb5_cc.so";
115 cc_handle = dlopen(lib, RTLD_LAZY);
116 if (cc_handle == NULL) {
117 HEIMDAL_MUTEX_unlock(&acc_mutex);
118 krb5_set_error_message(context, KRB5_CC_NOSUPP,
119 "Failed to load %s", lib);
120 return KRB5_CC_NOSUPP;
123 init_func = (cc_initialize_func)dlsym(cc_handle, "cc_initialize");
124 HEIMDAL_MUTEX_unlock(&acc_mutex);
125 if (init_func == NULL) {
126 krb5_set_error_message(context, KRB5_CC_NOSUPP,
127 "Failed to find cc_initialize"
128 "in %s: %s", lib, dlerror());
130 return KRB5_CC_NOSUPP;
135 HEIMDAL_MUTEX_unlock(&acc_mutex);
136 krb5_set_error_message(context, KRB5_CC_NOSUPP, "no support for shared object");
137 return KRB5_CC_NOSUPP;
141 static krb5_error_code
142 make_cred_from_ccred(krb5_context context,
143 const cc_credentials_v5_t *incred,
149 memset(cred, 0, sizeof(*cred));
151 ret = krb5_parse_name(context, incred->client, &cred->client);
155 ret = krb5_parse_name(context, incred->server, &cred->server);
159 cred->session.keytype = incred->keyblock.type;
160 cred->session.keyvalue.length = incred->keyblock.length;
161 cred->session.keyvalue.data = malloc(incred->keyblock.length);
162 if (cred->session.keyvalue.data == NULL)
164 memcpy(cred->session.keyvalue.data, incred->keyblock.data,
165 incred->keyblock.length);
167 cred->times.authtime = incred->authtime;
168 cred->times.starttime = incred->starttime;
169 cred->times.endtime = incred->endtime;
170 cred->times.renew_till = incred->renew_till;
172 ret = krb5_data_copy(&cred->ticket,
174 incred->ticket.length);
178 ret = krb5_data_copy(&cred->second_ticket,
179 incred->second_ticket.data,
180 incred->second_ticket.length);
184 cred->authdata.val = NULL;
185 cred->authdata.len = 0;
187 cred->addresses.val = NULL;
188 cred->addresses.len = 0;
190 for (i = 0; incred->authdata && incred->authdata[i]; i++)
194 cred->authdata.val = calloc(i, sizeof(cred->authdata.val[0]));
195 if (cred->authdata.val == NULL)
197 cred->authdata.len = i;
198 for (i = 0; i < cred->authdata.len; i++) {
199 cred->authdata.val[i].ad_type = incred->authdata[i]->type;
200 ret = krb5_data_copy(&cred->authdata.val[i].ad_data,
201 incred->authdata[i]->data,
202 incred->authdata[i]->length);
208 for (i = 0; incred->addresses && incred->addresses[i]; i++)
212 cred->addresses.val = calloc(i, sizeof(cred->addresses.val[0]));
213 if (cred->addresses.val == NULL)
215 cred->addresses.len = i;
217 for (i = 0; i < cred->addresses.len; i++) {
218 cred->addresses.val[i].addr_type = incred->addresses[i]->type;
219 ret = krb5_data_copy(&cred->addresses.val[i].address,
220 incred->addresses[i]->data,
221 incred->addresses[i]->length);
228 if (incred->ticket_flags & KRB5_CCAPI_TKT_FLG_FORWARDABLE)
229 cred->flags.b.forwardable = 1;
230 if (incred->ticket_flags & KRB5_CCAPI_TKT_FLG_FORWARDED)
231 cred->flags.b.forwarded = 1;
232 if (incred->ticket_flags & KRB5_CCAPI_TKT_FLG_PROXIABLE)
233 cred->flags.b.proxiable = 1;
234 if (incred->ticket_flags & KRB5_CCAPI_TKT_FLG_PROXY)
235 cred->flags.b.proxy = 1;
236 if (incred->ticket_flags & KRB5_CCAPI_TKT_FLG_MAY_POSTDATE)
237 cred->flags.b.may_postdate = 1;
238 if (incred->ticket_flags & KRB5_CCAPI_TKT_FLG_POSTDATED)
239 cred->flags.b.postdated = 1;
240 if (incred->ticket_flags & KRB5_CCAPI_TKT_FLG_INVALID)
241 cred->flags.b.invalid = 1;
242 if (incred->ticket_flags & KRB5_CCAPI_TKT_FLG_RENEWABLE)
243 cred->flags.b.renewable = 1;
244 if (incred->ticket_flags & KRB5_CCAPI_TKT_FLG_INITIAL)
245 cred->flags.b.initial = 1;
246 if (incred->ticket_flags & KRB5_CCAPI_TKT_FLG_PRE_AUTH)
247 cred->flags.b.pre_authent = 1;
248 if (incred->ticket_flags & KRB5_CCAPI_TKT_FLG_HW_AUTH)
249 cred->flags.b.hw_authent = 1;
250 if (incred->ticket_flags & KRB5_CCAPI_TKT_FLG_TRANSIT_POLICY_CHECKED)
251 cred->flags.b.transited_policy_checked = 1;
252 if (incred->ticket_flags & KRB5_CCAPI_TKT_FLG_OK_AS_DELEGATE)
253 cred->flags.b.ok_as_delegate = 1;
254 if (incred->ticket_flags & KRB5_CCAPI_TKT_FLG_ANONYMOUS)
255 cred->flags.b.anonymous = 1;
261 krb5_set_error_message(context, ret, "malloc: out of memory");
264 krb5_free_cred_contents(context, cred);
269 free_ccred(cc_credentials_v5_t *cred)
273 if (cred->addresses) {
274 for (i = 0; cred->addresses[i] != 0; i++) {
275 if (cred->addresses[i]->data)
276 free(cred->addresses[i]->data);
277 free(cred->addresses[i]);
279 free(cred->addresses);
285 memset(cred, 0, sizeof(*cred));
288 static krb5_error_code
289 make_ccred_from_cred(krb5_context context,
290 const krb5_creds *incred,
291 cc_credentials_v5_t *cred)
296 memset(cred, 0, sizeof(*cred));
298 ret = krb5_unparse_name(context, incred->client, &cred->client);
302 ret = krb5_unparse_name(context, incred->server, &cred->server);
306 cred->keyblock.type = incred->session.keytype;
307 cred->keyblock.length = incred->session.keyvalue.length;
308 cred->keyblock.data = incred->session.keyvalue.data;
310 cred->authtime = incred->times.authtime;
311 cred->starttime = incred->times.starttime;
312 cred->endtime = incred->times.endtime;
313 cred->renew_till = incred->times.renew_till;
315 cred->ticket.length = incred->ticket.length;
316 cred->ticket.data = incred->ticket.data;
318 cred->second_ticket.length = incred->second_ticket.length;
319 cred->second_ticket.data = incred->second_ticket.data;
321 /* XXX this one should also be filled in */
322 cred->authdata = NULL;
324 cred->addresses = calloc(incred->addresses.len + 1,
325 sizeof(cred->addresses[0]));
326 if (cred->addresses == NULL) {
332 for (i = 0; i < incred->addresses.len; i++) {
334 addr = malloc(sizeof(*addr));
339 addr->type = incred->addresses.val[i].addr_type;
340 addr->length = incred->addresses.val[i].address.length;
341 addr->data = malloc(addr->length);
342 if (addr->data == NULL) {
346 memcpy(addr->data, incred->addresses.val[i].address.data,
348 cred->addresses[i] = addr;
350 cred->addresses[i] = NULL;
352 cred->ticket_flags = 0;
353 if (incred->flags.b.forwardable)
354 cred->ticket_flags |= KRB5_CCAPI_TKT_FLG_FORWARDABLE;
355 if (incred->flags.b.forwarded)
356 cred->ticket_flags |= KRB5_CCAPI_TKT_FLG_FORWARDED;
357 if (incred->flags.b.proxiable)
358 cred->ticket_flags |= KRB5_CCAPI_TKT_FLG_PROXIABLE;
359 if (incred->flags.b.proxy)
360 cred->ticket_flags |= KRB5_CCAPI_TKT_FLG_PROXY;
361 if (incred->flags.b.may_postdate)
362 cred->ticket_flags |= KRB5_CCAPI_TKT_FLG_MAY_POSTDATE;
363 if (incred->flags.b.postdated)
364 cred->ticket_flags |= KRB5_CCAPI_TKT_FLG_POSTDATED;
365 if (incred->flags.b.invalid)
366 cred->ticket_flags |= KRB5_CCAPI_TKT_FLG_INVALID;
367 if (incred->flags.b.renewable)
368 cred->ticket_flags |= KRB5_CCAPI_TKT_FLG_RENEWABLE;
369 if (incred->flags.b.initial)
370 cred->ticket_flags |= KRB5_CCAPI_TKT_FLG_INITIAL;
371 if (incred->flags.b.pre_authent)
372 cred->ticket_flags |= KRB5_CCAPI_TKT_FLG_PRE_AUTH;
373 if (incred->flags.b.hw_authent)
374 cred->ticket_flags |= KRB5_CCAPI_TKT_FLG_HW_AUTH;
375 if (incred->flags.b.transited_policy_checked)
376 cred->ticket_flags |= KRB5_CCAPI_TKT_FLG_TRANSIT_POLICY_CHECKED;
377 if (incred->flags.b.ok_as_delegate)
378 cred->ticket_flags |= KRB5_CCAPI_TKT_FLG_OK_AS_DELEGATE;
379 if (incred->flags.b.anonymous)
380 cred->ticket_flags |= KRB5_CCAPI_TKT_FLG_ANONYMOUS;
387 krb5_clear_error_string(context);
392 get_cc_name(krb5_acc *a)
397 error = (*a->ccache->func->get_name)(a->ccache, &name);
401 a->cache_name = strdup(name->data);
402 (*name->func->release)(name);
403 if (a->cache_name == NULL)
410 acc_get_name(krb5_context context,
413 krb5_acc *a = ACACHE(id);
416 if (a->cache_name == NULL) {
418 krb5_principal principal;
421 ret = _krb5_get_default_principal_local(context, &principal);
425 ret = krb5_unparse_name(context, principal, &name);
426 krb5_free_principal(context, principal);
430 error = (*a->context->func->create_new_ccache)(a->context,
438 error = get_cc_name(a);
443 return a->cache_name;
446 static krb5_error_code
447 acc_alloc(krb5_context context, krb5_ccache *id)
453 ret = init_ccapi(context);
457 ret = krb5_data_alloc(&(*id)->data, sizeof(*a));
459 krb5_clear_error_string(context);
465 error = (*init_func)(&a->context, ccapi_version_3, NULL, NULL);
467 krb5_data_free(&(*id)->data);
468 return translate_cc_error(context, error);
471 a->cache_name = NULL;
476 static krb5_error_code
477 acc_resolve(krb5_context context, krb5_ccache *id, const char *res)
483 ret = acc_alloc(context, id);
489 error = (*a->context->func->open_ccache)(a->context, res, &a->ccache);
490 if (error == ccNoError) {
491 error = get_cc_name(a);
492 if (error != ccNoError) {
493 acc_close(context, *id);
495 return translate_cc_error(context, error);
497 } else if (error == ccErrCCacheNotFound) {
499 a->cache_name = NULL;
503 return translate_cc_error(context, error);
509 static krb5_error_code
510 acc_gen_new(krb5_context context, krb5_ccache *id)
515 ret = acc_alloc(context, id);
522 a->cache_name = NULL;
527 static krb5_error_code
528 acc_initialize(krb5_context context,
530 krb5_principal primary_principal)
532 krb5_acc *a = ACACHE(id);
537 ret = krb5_unparse_name(context, primary_principal, &name);
541 if (a->cache_name == NULL) {
542 error = (*a->context->func->create_new_ccache)(a->context,
547 if (error == ccNoError)
548 error = get_cc_name(a);
550 cc_credentials_iterator_t iter;
551 cc_credentials_t ccred;
553 error = (*a->ccache->func->new_credentials_iterator)(a->ccache, &iter);
556 return translate_cc_error(context, error);
560 error = (*iter->func->next)(iter, &ccred);
563 (*a->ccache->func->remove_credentials)(a->ccache, ccred);
564 (*ccred->func->release)(ccred);
566 (*iter->func->release)(iter);
568 error = (*a->ccache->func->set_principal)(a->ccache,
573 return translate_cc_error(context, error);
576 static krb5_error_code
577 acc_close(krb5_context context,
580 krb5_acc *a = ACACHE(id);
583 (*a->ccache->func->release)(a->ccache);
588 a->cache_name = NULL;
591 (*a->context->func->release)(a->context);
594 krb5_data_free(&id->data);
598 static krb5_error_code
599 acc_destroy(krb5_context context,
602 krb5_acc *a = ACACHE(id);
606 error = (*a->ccache->func->destroy)(a->ccache);
610 error = (a->context->func->release)(a->context);
613 return translate_cc_error(context, error);
616 static krb5_error_code
617 acc_store_cred(krb5_context context,
621 krb5_acc *a = ACACHE(id);
622 cc_credentials_union cred;
623 cc_credentials_v5_t v5cred;
627 if (a->ccache == NULL) {
628 krb5_set_error_message(context, KRB5_CC_NOTFOUND,
629 "No API credential found");
630 return KRB5_CC_NOTFOUND;
633 cred.version = cc_credentials_v5;
634 cred.credentials.credentials_v5 = &v5cred;
636 ret = make_ccred_from_cred(context,
642 error = (*a->ccache->func->store_credentials)(a->ccache, &cred);
644 ret = translate_cc_error(context, error);
651 static krb5_error_code
652 acc_get_principal(krb5_context context,
654 krb5_principal *principal)
656 krb5_acc *a = ACACHE(id);
661 if (a->ccache == NULL) {
662 krb5_set_error_message(context, KRB5_CC_NOTFOUND,
663 "No API credential found");
664 return KRB5_CC_NOTFOUND;
667 error = (*a->ccache->func->get_principal)(a->ccache,
671 return translate_cc_error(context, error);
673 ret = krb5_parse_name(context, name->data, principal);
675 (*name->func->release)(name);
679 static krb5_error_code
680 acc_get_first (krb5_context context,
682 krb5_cc_cursor *cursor)
684 cc_credentials_iterator_t iter;
685 krb5_acc *a = ACACHE(id);
688 if (a->ccache == NULL) {
689 krb5_set_error_message(context, KRB5_CC_NOTFOUND,
690 "No API credential found");
691 return KRB5_CC_NOTFOUND;
694 error = (*a->ccache->func->new_credentials_iterator)(a->ccache, &iter);
696 krb5_clear_error_string(context);
704 static krb5_error_code
705 acc_get_next (krb5_context context,
707 krb5_cc_cursor *cursor,
710 cc_credentials_iterator_t iter = *cursor;
711 cc_credentials_t cred;
716 error = (*iter->func->next)(iter, &cred);
718 return translate_cc_error(context, error);
719 if (cred->data->version == cc_credentials_v5)
721 (*cred->func->release)(cred);
724 ret = make_cred_from_ccred(context,
725 cred->data->credentials.credentials_v5,
727 (*cred->func->release)(cred);
731 static krb5_error_code
732 acc_end_get (krb5_context context,
734 krb5_cc_cursor *cursor)
736 cc_credentials_iterator_t iter = *cursor;
737 (*iter->func->release)(iter);
741 static krb5_error_code
742 acc_remove_cred(krb5_context context,
747 cc_credentials_iterator_t iter;
748 krb5_acc *a = ACACHE(id);
749 cc_credentials_t ccred;
752 char *client, *server;
754 if (a->ccache == NULL) {
755 krb5_set_error_message(context, KRB5_CC_NOTFOUND,
756 "No API credential found");
757 return KRB5_CC_NOTFOUND;
761 ret = krb5_unparse_name(context, cred->client, &client);
767 ret = krb5_unparse_name(context, cred->server, &server);
773 error = (*a->ccache->func->new_credentials_iterator)(a->ccache, &iter);
777 return translate_cc_error(context, error);
780 ret = KRB5_CC_NOTFOUND;
782 cc_credentials_v5_t *v5cred;
784 error = (*iter->func->next)(iter, &ccred);
788 if (ccred->data->version != cc_credentials_v5)
791 v5cred = ccred->data->credentials.credentials_v5;
793 if (client && strcmp(v5cred->client, client) != 0)
796 if (strcmp(v5cred->server, server) != 0)
799 (*a->ccache->func->remove_credentials)(a->ccache, ccred);
802 (*ccred->func->release)(ccred);
805 (*iter->func->release)(iter);
808 krb5_set_error_message(context, ret,
809 "Can't find credential %s in cache", server);
816 static krb5_error_code
817 acc_set_flags(krb5_context context,
825 acc_get_version(krb5_context context,
832 cc_context_t context;
833 cc_ccache_iterator_t iter;
836 static krb5_error_code
837 acc_get_cache_first(krb5_context context, krb5_cc_cursor *cursor)
839 struct cache_iter *iter;
843 ret = init_ccapi(context);
847 iter = calloc(1, sizeof(*iter));
849 krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
853 error = (*init_func)(&iter->context, ccapi_version_3, NULL, NULL);
856 return translate_cc_error(context, error);
859 error = (*iter->context->func->new_ccache_iterator)(iter->context,
863 krb5_clear_error_string(context);
870 static krb5_error_code
871 acc_get_cache_next(krb5_context context, krb5_cc_cursor cursor, krb5_ccache *id)
873 struct cache_iter *iter = cursor;
879 error = (*iter->iter->func->next)(iter->iter, &cache);
881 return translate_cc_error(context, error);
883 ret = _krb5_cc_allocate(context, &krb5_acc_ops, id);
885 (*cache->func->release)(cache);
889 ret = acc_alloc(context, id);
891 (*cache->func->release)(cache);
899 error = get_cc_name(a);
901 acc_close(context, *id);
903 return translate_cc_error(context, error);
908 static krb5_error_code
909 acc_end_cache_get(krb5_context context, krb5_cc_cursor cursor)
911 struct cache_iter *iter = cursor;
913 (*iter->iter->func->release)(iter->iter);
915 (*iter->context->func->release)(iter->context);
916 iter->context = NULL;
921 static krb5_error_code
922 acc_move(krb5_context context, krb5_ccache from, krb5_ccache to)
924 krb5_acc *afrom = ACACHE(from);
925 krb5_acc *ato = ACACHE(to);
928 if (ato->ccache == NULL) {
931 error = (*afrom->ccache->func->get_principal)(afrom->ccache,
935 return translate_cc_error(context, error);
937 error = (*ato->context->func->create_new_ccache)(ato->context,
941 (*name->func->release)(name);
943 return translate_cc_error(context, error);
947 error = (*ato->ccache->func->move)(afrom->ccache, ato->ccache);
948 return translate_cc_error(context, error);
951 static krb5_error_code
952 acc_get_default_name(krb5_context context, char **str)
959 ret = init_ccapi(context);
963 error = (*init_func)(&cc, ccapi_version_3, NULL, NULL);
965 return translate_cc_error(context, error);
967 error = (*cc->func->get_default_ccache_name)(cc, &name);
969 (*cc->func->release)(cc);
970 return translate_cc_error(context, error);
973 asprintf(str, "API:%s", name->data);
974 (*name->func->release)(name);
975 (*cc->func->release)(cc);
978 krb5_set_error_message(context, ENOMEM, "out of memory");
984 static krb5_error_code
985 acc_set_default(krb5_context context, krb5_ccache id)
987 krb5_acc *a = ACACHE(id);
990 if (a->ccache == NULL) {
991 krb5_set_error_message(context, KRB5_CC_NOTFOUND,
992 "No API credential found");
993 return KRB5_CC_NOTFOUND;
996 error = (*a->ccache->func->set_default)(a->ccache);
998 return translate_cc_error(context, error);
1004 * Variable containing the API based credential cache implemention.
1006 * @ingroup krb5_ccache
1009 KRB5_LIB_VARIABLE const krb5_cc_ops krb5_acc_ops = {
1010 KRB5_CC_OPS_VERSION,
1019 NULL, /* acc_retrieve */
1027 acc_get_cache_first,
1031 acc_get_default_name,