libtommath: Fix possible integer overflow CVE-2023-36328
[heimdal.git] / lib / krb5 / acache.c
bloba80d7669c433ecba340344f2ffbeabb1786b9570
1 /*
2 * Copyright (c) 2004 - 2007 Kungliga Tekniska Högskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
4 * All rights reserved.
6 * Portions Copyright (c) 2009 Apple Inc. All rights reserved.
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
19 * 3. Neither the name of the Institute nor the names of its contributors
20 * may be used to endorse or promote products derived from this software
21 * without specific prior written permission.
23 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 * SUCH DAMAGE.
36 #include "krb5_locl.h"
37 #include <krb5_ccapi.h>
39 #ifndef KCM_IS_API_CACHE
41 static HEIMDAL_MUTEX acc_mutex = HEIMDAL_MUTEX_INITIALIZER;
42 static cc_initialize_func init_func;
43 static void (KRB5_CALLCONV *set_target_uid)(uid_t);
44 static void (KRB5_CALLCONV *clear_target)(void);
46 #ifdef HAVE_DLOPEN
47 static void *cc_handle;
48 #endif
50 typedef struct krb5_acc {
51 char *cache_name;
52 char *cache_subsidiary;
53 cc_context_t context;
54 cc_ccache_t ccache;
55 } krb5_acc;
57 static krb5_error_code KRB5_CALLCONV acc_close(krb5_context, krb5_ccache);
59 #define ACACHE(X) ((krb5_acc *)(X)->data.data)
61 static const struct {
62 cc_int32 error;
63 krb5_error_code ret;
64 } cc_errors[] = {
65 { ccErrBadName, KRB5_CC_BADNAME },
66 { ccErrCredentialsNotFound, KRB5_CC_NOTFOUND },
67 { ccErrCCacheNotFound, KRB5_FCC_NOFILE },
68 { ccErrContextNotFound, KRB5_CC_NOTFOUND },
69 { ccIteratorEnd, KRB5_CC_END },
70 { ccErrNoMem, KRB5_CC_NOMEM },
71 { ccErrServerUnavailable, KRB5_CC_NOSUPP },
72 { ccErrInvalidCCache, KRB5_CC_BADNAME },
73 { ccNoError, 0 }
76 static krb5_error_code
77 translate_cc_error(krb5_context context, cc_int32 error)
79 size_t i;
80 krb5_clear_error_message(context);
81 for(i = 0; i < sizeof(cc_errors)/sizeof(cc_errors[0]); i++)
82 if (cc_errors[i].error == error)
83 return cc_errors[i].ret;
84 return KRB5_FCC_INTERNAL;
87 static krb5_error_code
88 init_ccapi(krb5_context context)
90 const char *lib = NULL;
91 #ifdef HAVE_DLOPEN
92 char *explib = NULL;
93 #endif
95 HEIMDAL_MUTEX_lock(&acc_mutex);
96 if (init_func) {
97 HEIMDAL_MUTEX_unlock(&acc_mutex);
98 if (context)
99 krb5_clear_error_message(context);
100 return 0;
103 if (context)
104 lib = krb5_config_get_string(context, NULL,
105 "libdefaults", "ccapi_library",
106 NULL);
107 if (lib == NULL) {
108 #ifdef __APPLE__
109 lib = "/System/Library/Frameworks/Kerberos.framework/Kerberos";
110 #elif defined(_WIN32)
111 lib = "%{LIBDIR}/libkrb5_cc.dll";
112 #else
113 lib = "%{LIBDIR}/libkrb5_cc.so";
114 #endif
117 #ifdef HAVE_DLOPEN
119 if (_krb5_expand_path_tokens(context, lib, 0, &explib) == 0) {
120 cc_handle = dlopen(explib, RTLD_LAZY|RTLD_LOCAL|RTLD_GROUP);
121 free(explib);
124 if (cc_handle == NULL) {
125 HEIMDAL_MUTEX_unlock(&acc_mutex);
126 krb5_set_error_message(context, KRB5_CC_NOSUPP,
127 N_("Failed to load API cache module %s", "file"),
128 lib);
129 return KRB5_CC_NOSUPP;
132 init_func = (cc_initialize_func)dlsym(cc_handle, "cc_initialize");
133 set_target_uid = (void (KRB5_CALLCONV *)(uid_t))
134 dlsym(cc_handle, "krb5_ipc_client_set_target_uid");
135 clear_target = (void (KRB5_CALLCONV *)(void))
136 dlsym(cc_handle, "krb5_ipc_client_clear_target");
137 HEIMDAL_MUTEX_unlock(&acc_mutex);
138 if (init_func == NULL) {
139 krb5_set_error_message(context, KRB5_CC_NOSUPP,
140 N_("Failed to find cc_initialize"
141 "in %s: %s", "file, error"), lib, dlerror());
142 dlclose(cc_handle);
143 return KRB5_CC_NOSUPP;
146 return 0;
147 #else
148 HEIMDAL_MUTEX_unlock(&acc_mutex);
149 krb5_set_error_message(context, KRB5_CC_NOSUPP,
150 N_("no support for shared object", ""));
151 return KRB5_CC_NOSUPP;
152 #endif
155 KRB5_LIB_FUNCTION void KRB5_LIB_CALL
156 _heim_krb5_ipc_client_set_target_uid(uid_t uid)
158 init_ccapi(NULL);
159 if (set_target_uid != NULL)
160 (*set_target_uid)(uid);
163 KRB5_LIB_FUNCTION void KRB5_LIB_CALL
164 _heim_krb5_ipc_client_clear_target(void)
166 init_ccapi(NULL);
167 if (clear_target != NULL)
168 (*clear_target)();
171 static krb5_error_code
172 make_cred_from_ccred(krb5_context context,
173 const cc_credentials_v5_t *incred,
174 krb5_creds *cred)
176 krb5_error_code ret;
177 unsigned int i;
179 memset(cred, 0, sizeof(*cred));
181 ret = krb5_parse_name(context, incred->client, &cred->client);
182 if (ret)
183 goto fail;
185 ret = krb5_parse_name(context, incred->server, &cred->server);
186 if (ret)
187 goto fail;
189 cred->session.keytype = incred->keyblock.type;
190 cred->session.keyvalue.length = incred->keyblock.length;
191 cred->session.keyvalue.data = malloc(incred->keyblock.length);
192 if (cred->session.keyvalue.data == NULL)
193 goto nomem;
194 memcpy(cred->session.keyvalue.data, incred->keyblock.data,
195 incred->keyblock.length);
197 cred->times.authtime = incred->authtime;
198 cred->times.starttime = incred->starttime;
199 cred->times.endtime = incred->endtime;
200 cred->times.renew_till = incred->renew_till;
202 ret = krb5_data_copy(&cred->ticket,
203 incred->ticket.data,
204 incred->ticket.length);
205 if (ret)
206 goto nomem;
208 ret = krb5_data_copy(&cred->second_ticket,
209 incred->second_ticket.data,
210 incred->second_ticket.length);
211 if (ret)
212 goto nomem;
214 cred->authdata.val = NULL;
215 cred->authdata.len = 0;
217 cred->addresses.val = NULL;
218 cred->addresses.len = 0;
220 for (i = 0; incred->authdata && incred->authdata[i]; i++)
223 if (i) {
224 cred->authdata.val = calloc(i, sizeof(cred->authdata.val[0]));
225 if (cred->authdata.val == NULL)
226 goto nomem;
227 cred->authdata.len = i;
228 for (i = 0; i < cred->authdata.len; i++) {
229 cred->authdata.val[i].ad_type = incred->authdata[i]->type;
230 ret = krb5_data_copy(&cred->authdata.val[i].ad_data,
231 incred->authdata[i]->data,
232 incred->authdata[i]->length);
233 if (ret)
234 goto nomem;
238 for (i = 0; incred->addresses && incred->addresses[i]; i++)
241 if (i) {
242 cred->addresses.val = calloc(i, sizeof(cred->addresses.val[0]));
243 if (cred->addresses.val == NULL)
244 goto nomem;
245 cred->addresses.len = i;
247 for (i = 0; i < cred->addresses.len; i++) {
248 cred->addresses.val[i].addr_type = incred->addresses[i]->type;
249 ret = krb5_data_copy(&cred->addresses.val[i].address,
250 incred->addresses[i]->data,
251 incred->addresses[i]->length);
252 if (ret)
253 goto nomem;
257 cred->flags.i = 0;
258 if (incred->ticket_flags & KRB5_CCAPI_TKT_FLG_FORWARDABLE)
259 cred->flags.b.forwardable = 1;
260 if (incred->ticket_flags & KRB5_CCAPI_TKT_FLG_FORWARDED)
261 cred->flags.b.forwarded = 1;
262 if (incred->ticket_flags & KRB5_CCAPI_TKT_FLG_PROXIABLE)
263 cred->flags.b.proxiable = 1;
264 if (incred->ticket_flags & KRB5_CCAPI_TKT_FLG_PROXY)
265 cred->flags.b.proxy = 1;
266 if (incred->ticket_flags & KRB5_CCAPI_TKT_FLG_MAY_POSTDATE)
267 cred->flags.b.may_postdate = 1;
268 if (incred->ticket_flags & KRB5_CCAPI_TKT_FLG_POSTDATED)
269 cred->flags.b.postdated = 1;
270 if (incred->ticket_flags & KRB5_CCAPI_TKT_FLG_INVALID)
271 cred->flags.b.invalid = 1;
272 if (incred->ticket_flags & KRB5_CCAPI_TKT_FLG_RENEWABLE)
273 cred->flags.b.renewable = 1;
274 if (incred->ticket_flags & KRB5_CCAPI_TKT_FLG_INITIAL)
275 cred->flags.b.initial = 1;
276 if (incred->ticket_flags & KRB5_CCAPI_TKT_FLG_PRE_AUTH)
277 cred->flags.b.pre_authent = 1;
278 if (incred->ticket_flags & KRB5_CCAPI_TKT_FLG_HW_AUTH)
279 cred->flags.b.hw_authent = 1;
280 if (incred->ticket_flags & KRB5_CCAPI_TKT_FLG_TRANSIT_POLICY_CHECKED)
281 cred->flags.b.transited_policy_checked = 1;
282 if (incred->ticket_flags & KRB5_CCAPI_TKT_FLG_OK_AS_DELEGATE)
283 cred->flags.b.ok_as_delegate = 1;
284 if (incred->ticket_flags & KRB5_CCAPI_TKT_FLG_ANONYMOUS)
285 cred->flags.b.anonymous = 1;
287 return 0;
289 nomem:
290 ret = krb5_enomem(context);
292 fail:
293 krb5_free_cred_contents(context, cred);
294 return ret;
297 static void
298 free_ccred(cc_credentials_v5_t *cred)
300 int i;
302 if (cred->addresses) {
303 for (i = 0; cred->addresses[i] != 0; i++) {
304 if (cred->addresses[i]->data)
305 free(cred->addresses[i]->data);
306 free(cred->addresses[i]);
308 free(cred->addresses);
310 if (cred->server)
311 free(cred->server);
312 if (cred->client)
313 free(cred->client);
314 memset(cred, 0, sizeof(*cred));
317 static krb5_error_code
318 make_ccred_from_cred(krb5_context context,
319 const krb5_creds *incred,
320 cc_credentials_v5_t *cred)
322 krb5_error_code ret;
323 size_t i;
325 memset(cred, 0, sizeof(*cred));
327 ret = krb5_unparse_name(context, incred->client, &cred->client);
328 if (ret)
329 goto fail;
331 ret = krb5_unparse_name(context, incred->server, &cred->server);
332 if (ret)
333 goto fail;
335 cred->keyblock.type = incred->session.keytype;
336 cred->keyblock.length = incred->session.keyvalue.length;
337 cred->keyblock.data = incred->session.keyvalue.data;
339 cred->authtime = incred->times.authtime;
340 cred->starttime = incred->times.starttime;
341 cred->endtime = incred->times.endtime;
342 cred->renew_till = incred->times.renew_till;
344 cred->ticket.length = incred->ticket.length;
345 cred->ticket.data = incred->ticket.data;
347 cred->second_ticket.length = incred->second_ticket.length;
348 cred->second_ticket.data = incred->second_ticket.data;
350 /* XXX this one should also be filled in */
351 cred->authdata = NULL;
353 cred->addresses = calloc(incred->addresses.len + 1,
354 sizeof(cred->addresses[0]));
355 if (cred->addresses == NULL) {
357 ret = ENOMEM;
358 goto fail;
361 for (i = 0; i < incred->addresses.len; i++) {
362 cc_data *addr;
363 addr = malloc(sizeof(*addr));
364 if (addr == NULL) {
365 ret = ENOMEM;
366 goto fail;
368 addr->type = incred->addresses.val[i].addr_type;
369 addr->length = incred->addresses.val[i].address.length;
370 addr->data = malloc(addr->length);
371 if (addr->data == NULL) {
372 free(addr);
373 ret = ENOMEM;
374 goto fail;
376 memcpy(addr->data, incred->addresses.val[i].address.data,
377 addr->length);
378 cred->addresses[i] = addr;
380 cred->addresses[i] = NULL;
382 cred->ticket_flags = 0;
383 if (incred->flags.b.forwardable)
384 cred->ticket_flags |= KRB5_CCAPI_TKT_FLG_FORWARDABLE;
385 if (incred->flags.b.forwarded)
386 cred->ticket_flags |= KRB5_CCAPI_TKT_FLG_FORWARDED;
387 if (incred->flags.b.proxiable)
388 cred->ticket_flags |= KRB5_CCAPI_TKT_FLG_PROXIABLE;
389 if (incred->flags.b.proxy)
390 cred->ticket_flags |= KRB5_CCAPI_TKT_FLG_PROXY;
391 if (incred->flags.b.may_postdate)
392 cred->ticket_flags |= KRB5_CCAPI_TKT_FLG_MAY_POSTDATE;
393 if (incred->flags.b.postdated)
394 cred->ticket_flags |= KRB5_CCAPI_TKT_FLG_POSTDATED;
395 if (incred->flags.b.invalid)
396 cred->ticket_flags |= KRB5_CCAPI_TKT_FLG_INVALID;
397 if (incred->flags.b.renewable)
398 cred->ticket_flags |= KRB5_CCAPI_TKT_FLG_RENEWABLE;
399 if (incred->flags.b.initial)
400 cred->ticket_flags |= KRB5_CCAPI_TKT_FLG_INITIAL;
401 if (incred->flags.b.pre_authent)
402 cred->ticket_flags |= KRB5_CCAPI_TKT_FLG_PRE_AUTH;
403 if (incred->flags.b.hw_authent)
404 cred->ticket_flags |= KRB5_CCAPI_TKT_FLG_HW_AUTH;
405 if (incred->flags.b.transited_policy_checked)
406 cred->ticket_flags |= KRB5_CCAPI_TKT_FLG_TRANSIT_POLICY_CHECKED;
407 if (incred->flags.b.ok_as_delegate)
408 cred->ticket_flags |= KRB5_CCAPI_TKT_FLG_OK_AS_DELEGATE;
409 if (incred->flags.b.anonymous)
410 cred->ticket_flags |= KRB5_CCAPI_TKT_FLG_ANONYMOUS;
412 return 0;
414 fail:
415 free_ccred(cred);
417 krb5_clear_error_message(context);
418 return ret;
421 static cc_int32
422 get_cc_name(krb5_acc *a)
424 cc_string_t name;
425 cc_int32 error;
427 error = (*a->ccache->func->get_name)(a->ccache, &name);
428 if (error)
429 return error;
431 a->cache_name = strdup(name->data);
432 (*name->func->release)(name);
433 if (a->cache_name == NULL)
434 return ccErrNoMem;
435 return ccNoError;
439 static krb5_error_code KRB5_CALLCONV
440 acc_get_name_2(krb5_context context,
441 krb5_ccache id,
442 const char **name,
443 const char **colname,
444 const char **subsidiary)
446 krb5_error_code ret = 0;
447 krb5_acc *a = ACACHE(id);
448 int32_t error;
450 if (name)
451 *name = NULL;
452 if (colname)
453 *colname = NULL;
454 if (subsidiary)
455 *subsidiary = NULL;
456 if (a->cache_subsidiary == NULL) {
457 krb5_principal principal = NULL;
459 ret = _krb5_get_default_principal_local(context, &principal);
460 if (ret == 0)
461 ret = krb5_unparse_name(context, principal, &a->cache_subsidiary);
462 krb5_free_principal(context, principal);
463 if (ret)
464 return ret;
467 if (a->cache_name == NULL) {
468 error = (*a->context->func->create_new_ccache)(a->context,
469 cc_credentials_v5,
470 a->cache_subsidiary,
471 &a->ccache);
472 if (error == ccNoError)
473 error = get_cc_name(a);
474 if (error != ccNoError)
475 ret = translate_cc_error(context, error);
477 if (name)
478 *name = a->cache_name;
479 if (colname)
480 *colname = "";
481 if (subsidiary)
482 *subsidiary = a->cache_subsidiary;
483 return ret;
486 static krb5_error_code KRB5_CALLCONV
487 acc_alloc(krb5_context context, krb5_ccache *id)
489 krb5_error_code ret;
490 cc_int32 error;
491 krb5_acc *a;
493 ret = init_ccapi(context);
494 if (ret)
495 return ret;
497 ret = krb5_data_alloc(&(*id)->data, sizeof(*a));
498 if (ret) {
499 krb5_clear_error_message(context);
500 return ret;
503 a = ACACHE(*id);
504 a->cache_subsidiary = NULL;
505 a->cache_name = NULL;
506 a->context = NULL;
507 a->ccache = NULL;
509 error = (*init_func)(&a->context, ccapi_version_3, NULL, NULL);
510 if (error) {
511 krb5_data_free(&(*id)->data);
512 return translate_cc_error(context, error);
515 return 0;
518 static krb5_error_code KRB5_CALLCONV
519 acc_resolve_2(krb5_context context, krb5_ccache *id, const char *res, const char *sub)
521 krb5_error_code ret;
522 cc_time_t offset;
523 cc_int32 error;
524 krb5_acc *a;
525 char *s = NULL;
527 ret = acc_alloc(context, id);
528 if (ret)
529 return ret;
531 a = ACACHE(*id);
533 if (sub) {
535 * For API there's no such thing as a collection name, there's only the
536 * default collection. Though we could perhaps put a CCAPI shared
537 * object path in the collection name.
539 * So we'll treat (res && !sub) and (!res && sub) as the same cases.
541 * See also the KCM ccache type, where we have similar considerations.
543 if (asprintf(&s, "%s%s%s", res && *res ? res : "",
544 res && *res ? ":" : "", sub) == -1 || s == NULL ||
545 (a->cache_subsidiary = strdup(sub)) == NULL) {
546 acc_close(context, *id);
547 free(s);
548 return krb5_enomem(context);
550 res = s;
552 * XXX With a bit of extra refactoring we could use the collection name
553 * as the path to the shared object implementing CCAPI... For now we
554 * ignore the collection name.
558 error = (*a->context->func->open_ccache)(a->context, res, &a->ccache);
559 if (error == ccErrCCacheNotFound) {
560 a->ccache = NULL;
561 a->cache_name = NULL;
562 free(s);
563 return 0;
565 if (error == ccNoError)
566 error = get_cc_name(a);
567 if (error != ccNoError) {
568 acc_close(context, *id);
569 *id = NULL;
570 free(s);
571 return translate_cc_error(context, error);
574 error = (*a->ccache->func->get_kdc_time_offset)(a->ccache,
575 cc_credentials_v5,
576 &offset);
577 if (error == 0)
578 context->kdc_sec_offset = offset;
579 free(s);
580 return 0;
583 static krb5_error_code KRB5_CALLCONV
584 acc_gen_new(krb5_context context, krb5_ccache *id)
586 return acc_alloc(context, id);
589 static krb5_error_code KRB5_CALLCONV
590 acc_initialize(krb5_context context,
591 krb5_ccache id,
592 krb5_principal primary_principal)
594 krb5_acc *a = ACACHE(id);
595 krb5_error_code ret;
596 int32_t error;
597 char *name;
599 ret = krb5_unparse_name(context, primary_principal, &name);
600 if (ret)
601 return ret;
603 if (a->cache_name == NULL) {
604 error = (*a->context->func->create_new_ccache)(a->context,
605 cc_credentials_v5,
606 name,
607 &a->ccache);
608 free(name);
609 if (error == ccNoError)
610 error = get_cc_name(a);
611 } else {
612 cc_credentials_iterator_t iter;
613 cc_credentials_t ccred;
615 error = (*a->ccache->func->new_credentials_iterator)(a->ccache, &iter);
616 if (error) {
617 free(name);
618 return translate_cc_error(context, error);
621 while (1) {
622 error = (*iter->func->next)(iter, &ccred);
623 if (error)
624 break;
625 (*a->ccache->func->remove_credentials)(a->ccache, ccred);
626 (*ccred->func->release)(ccred);
628 (*iter->func->release)(iter);
630 error = (*a->ccache->func->set_principal)(a->ccache,
631 cc_credentials_v5,
632 name);
635 if (error == 0 && context->kdc_sec_offset)
636 error = (*a->ccache->func->set_kdc_time_offset)(a->ccache,
637 cc_credentials_v5,
638 context->kdc_sec_offset);
640 return translate_cc_error(context, error);
643 static krb5_error_code KRB5_CALLCONV
644 acc_close(krb5_context context,
645 krb5_ccache id)
647 krb5_acc *a = ACACHE(id);
649 if (a->ccache) {
650 (*a->ccache->func->release)(a->ccache);
651 a->ccache = NULL;
653 if (a->cache_name) {
654 free(a->cache_name);
655 a->cache_name = NULL;
657 if (a->context) {
658 (*a->context->func->release)(a->context);
659 a->context = NULL;
661 krb5_data_free(&id->data);
662 return 0;
665 static krb5_error_code KRB5_CALLCONV
666 acc_destroy(krb5_context context,
667 krb5_ccache id)
669 krb5_acc *a = ACACHE(id);
670 cc_int32 error = 0;
672 if (a->ccache) {
673 error = (*a->ccache->func->destroy)(a->ccache);
674 a->ccache = NULL;
676 if (a->context) {
677 error = (a->context->func->release)(a->context);
678 a->context = NULL;
680 return translate_cc_error(context, error);
683 static krb5_error_code KRB5_CALLCONV
684 acc_store_cred(krb5_context context,
685 krb5_ccache id,
686 krb5_creds *creds)
688 krb5_acc *a = ACACHE(id);
689 cc_credentials_union cred;
690 cc_credentials_v5_t v5cred;
691 krb5_error_code ret;
692 cc_int32 error;
694 if (a->ccache == NULL) {
695 krb5_set_error_message(context, KRB5_CC_NOTFOUND,
696 N_("No API credential found", ""));
697 return KRB5_CC_NOTFOUND;
700 cred.version = cc_credentials_v5;
701 cred.credentials.credentials_v5 = &v5cred;
703 ret = make_ccred_from_cred(context,
704 creds,
705 &v5cred);
706 if (ret)
707 return ret;
709 error = (*a->ccache->func->store_credentials)(a->ccache, &cred);
710 if (error)
711 ret = translate_cc_error(context, error);
713 free_ccred(&v5cred);
715 return ret;
718 static krb5_error_code KRB5_CALLCONV
719 acc_get_principal(krb5_context context,
720 krb5_ccache id,
721 krb5_principal *principal)
723 krb5_acc *a = ACACHE(id);
724 krb5_error_code ret;
725 int32_t error;
726 cc_string_t name;
728 if (a->ccache == NULL) {
729 krb5_set_error_message(context, KRB5_CC_NOTFOUND,
730 N_("No API credential found", ""));
731 return KRB5_CC_NOTFOUND;
734 error = (*a->ccache->func->get_principal)(a->ccache,
735 cc_credentials_v5,
736 &name);
737 if (error)
738 return translate_cc_error(context, error);
740 ret = krb5_parse_name(context, name->data, principal);
742 (*name->func->release)(name);
743 return ret;
746 static krb5_error_code KRB5_CALLCONV
747 acc_get_first (krb5_context context,
748 krb5_ccache id,
749 krb5_cc_cursor *cursor)
751 cc_credentials_iterator_t iter;
752 krb5_acc *a = ACACHE(id);
753 int32_t error;
755 if (a->ccache == NULL) {
756 krb5_set_error_message(context, KRB5_CC_NOTFOUND,
757 N_("No API credential found", ""));
758 return KRB5_CC_NOTFOUND;
761 error = (*a->ccache->func->new_credentials_iterator)(a->ccache, &iter);
762 if (error) {
763 krb5_clear_error_message(context);
764 return ENOENT;
766 *cursor = iter;
767 return 0;
771 static krb5_error_code KRB5_CALLCONV
772 acc_get_next (krb5_context context,
773 krb5_ccache id,
774 krb5_cc_cursor *cursor,
775 krb5_creds *creds)
777 cc_credentials_iterator_t iter = *cursor;
778 cc_credentials_t cred;
779 krb5_error_code ret;
780 int32_t error;
782 while (1) {
783 error = (*iter->func->next)(iter, &cred);
784 if (error)
785 return translate_cc_error(context, error);
786 if (cred->data->version == cc_credentials_v5)
787 break;
788 (*cred->func->release)(cred);
791 ret = make_cred_from_ccred(context,
792 cred->data->credentials.credentials_v5,
793 creds);
794 (*cred->func->release)(cred);
795 return ret;
798 static krb5_error_code KRB5_CALLCONV
799 acc_end_get (krb5_context context,
800 krb5_ccache id,
801 krb5_cc_cursor *cursor)
803 cc_credentials_iterator_t iter = *cursor;
804 (*iter->func->release)(iter);
805 return 0;
808 static krb5_error_code KRB5_CALLCONV
809 acc_remove_cred(krb5_context context,
810 krb5_ccache id,
811 krb5_flags which,
812 krb5_creds *cred)
814 cc_credentials_iterator_t iter;
815 krb5_acc *a = ACACHE(id);
816 cc_credentials_t ccred;
817 krb5_error_code ret;
818 cc_int32 error;
819 char *client, *server;
821 if (a->ccache == NULL) {
822 krb5_set_error_message(context, KRB5_CC_NOTFOUND,
823 N_("No API credential found", ""));
824 return KRB5_CC_NOTFOUND;
827 if (cred->client) {
828 ret = krb5_unparse_name(context, cred->client, &client);
829 if (ret)
830 return ret;
831 } else
832 client = NULL;
834 ret = krb5_unparse_name(context, cred->server, &server);
835 if (ret) {
836 free(client);
837 return ret;
840 error = (*a->ccache->func->new_credentials_iterator)(a->ccache, &iter);
841 if (error) {
842 free(server);
843 free(client);
844 return translate_cc_error(context, error);
847 ret = KRB5_CC_NOTFOUND;
848 while (1) {
849 cc_credentials_v5_t *v5cred;
851 error = (*iter->func->next)(iter, &ccred);
852 if (error)
853 break;
855 if (ccred->data->version != cc_credentials_v5)
856 goto next;
858 v5cred = ccred->data->credentials.credentials_v5;
860 if (client && strcmp(v5cred->client, client) != 0)
861 goto next;
863 if (strcmp(v5cred->server, server) != 0)
864 goto next;
866 (*a->ccache->func->remove_credentials)(a->ccache, ccred);
867 ret = 0;
868 next:
869 (*ccred->func->release)(ccred);
872 (*iter->func->release)(iter);
874 if (ret)
875 krb5_set_error_message(context, ret,
876 N_("Can't find credential %s in cache",
877 "principal"), server);
878 free(server);
879 free(client);
881 return ret;
884 static krb5_error_code KRB5_CALLCONV
885 acc_set_flags(krb5_context context,
886 krb5_ccache id,
887 krb5_flags flags)
889 return 0;
892 static int KRB5_CALLCONV
893 acc_get_version(krb5_context context,
894 krb5_ccache id)
896 return 0;
899 struct cache_iter {
900 cc_context_t context;
901 cc_ccache_iterator_t iter;
904 static krb5_error_code KRB5_CALLCONV
905 acc_get_cache_first(krb5_context context, krb5_cc_cursor *cursor)
907 struct cache_iter *iter;
908 krb5_error_code ret;
909 cc_int32 error;
911 ret = init_ccapi(context);
912 if (ret)
913 return ret;
915 iter = calloc(1, sizeof(*iter));
916 if (iter == NULL)
917 return krb5_enomem(context);
919 error = (*init_func)(&iter->context, ccapi_version_3, NULL, NULL);
920 if (error) {
921 free(iter);
922 return translate_cc_error(context, error);
925 error = (*iter->context->func->new_ccache_iterator)(iter->context,
926 &iter->iter);
927 if (error) {
928 free(iter);
929 krb5_clear_error_message(context);
930 return ENOENT;
932 *cursor = iter;
933 return 0;
936 static krb5_error_code KRB5_CALLCONV
937 acc_get_cache_next(krb5_context context, krb5_cc_cursor cursor, krb5_ccache *id)
939 struct cache_iter *iter = cursor;
940 cc_ccache_t cache;
941 krb5_acc *a;
942 krb5_error_code ret;
943 int32_t error;
945 error = (*iter->iter->func->next)(iter->iter, &cache);
946 if (error)
947 return translate_cc_error(context, error);
949 ret = _krb5_cc_allocate(context, &krb5_acc_ops, id);
950 if (ret) {
951 (*cache->func->release)(cache);
952 return ret;
955 ret = acc_alloc(context, id);
956 if (ret) {
957 (*cache->func->release)(cache);
958 free(*id);
959 return ret;
962 a = ACACHE(*id);
963 a->ccache = cache;
965 error = get_cc_name(a);
966 if (error) {
967 acc_close(context, *id);
968 *id = NULL;
969 return translate_cc_error(context, error);
971 return 0;
974 static krb5_error_code KRB5_CALLCONV
975 acc_end_cache_get(krb5_context context, krb5_cc_cursor cursor)
977 struct cache_iter *iter = cursor;
979 (*iter->iter->func->release)(iter->iter);
980 iter->iter = NULL;
981 (*iter->context->func->release)(iter->context);
982 iter->context = NULL;
983 free(iter);
984 return 0;
987 static krb5_error_code KRB5_CALLCONV
988 acc_move(krb5_context context, krb5_ccache from, krb5_ccache to)
990 krb5_error_code ret;
991 krb5_acc *afrom = ACACHE(from);
992 krb5_acc *ato = ACACHE(to);
993 int32_t error;
995 if (ato->ccache == NULL) {
996 cc_string_t name;
998 error = (*afrom->ccache->func->get_principal)(afrom->ccache,
999 cc_credentials_v5,
1000 &name);
1001 if (error)
1002 return translate_cc_error(context, error);
1004 error = (*ato->context->func->create_new_ccache)(ato->context,
1005 cc_credentials_v5,
1006 name->data,
1007 &ato->ccache);
1008 (*name->func->release)(name);
1009 if (error)
1010 return translate_cc_error(context, error);
1013 error = (*ato->ccache->func->move)(afrom->ccache, ato->ccache);
1014 ret = translate_cc_error(context, error);
1015 if (ret == 0)
1016 krb5_cc_destroy(context, from);
1017 return ret;
1020 static krb5_error_code KRB5_CALLCONV
1021 acc_get_default_name(krb5_context context, char **str)
1023 krb5_error_code ret;
1024 cc_context_t cc;
1025 cc_string_t name;
1026 int32_t error;
1028 ret = init_ccapi(context);
1029 if (ret)
1030 return ret;
1032 error = (*init_func)(&cc, ccapi_version_3, NULL, NULL);
1033 if (error)
1034 return translate_cc_error(context, error);
1036 error = (*cc->func->get_default_ccache_name)(cc, &name);
1037 if (error) {
1038 (*cc->func->release)(cc);
1039 return translate_cc_error(context, error);
1042 error = asprintf(str, "API:%s", name->data);
1043 (*name->func->release)(name);
1044 (*cc->func->release)(cc);
1046 if (error < 0 || *str == NULL)
1047 return krb5_enomem(context);
1048 return 0;
1051 static krb5_error_code KRB5_CALLCONV
1052 acc_set_default(krb5_context context, krb5_ccache id)
1054 krb5_acc *a = ACACHE(id);
1055 cc_int32 error;
1057 if (a->ccache == NULL) {
1058 krb5_set_error_message(context, KRB5_CC_NOTFOUND,
1059 N_("No API credential found", ""));
1060 return KRB5_CC_NOTFOUND;
1063 error = (*a->ccache->func->set_default)(a->ccache);
1064 if (error)
1065 return translate_cc_error(context, error);
1067 return 0;
1070 static krb5_error_code KRB5_CALLCONV
1071 acc_lastchange(krb5_context context, krb5_ccache id, krb5_timestamp *mtime)
1073 krb5_acc *a = ACACHE(id);
1074 cc_int32 error;
1075 cc_time_t t;
1077 if (a->ccache == NULL) {
1078 krb5_set_error_message(context, KRB5_CC_NOTFOUND,
1079 N_("No API credential found", ""));
1080 return KRB5_CC_NOTFOUND;
1083 error = (*a->ccache->func->get_change_time)(a->ccache, &t);
1084 if (error)
1085 return translate_cc_error(context, error);
1087 *mtime = t;
1089 return 0;
1093 * Variable containing the API based credential cache implementation.
1095 * @ingroup krb5_ccache
1098 KRB5_LIB_VARIABLE const krb5_cc_ops krb5_acc_ops = {
1099 KRB5_CC_OPS_VERSION_5,
1100 "API",
1101 NULL,
1102 NULL,
1103 acc_gen_new,
1104 acc_initialize,
1105 acc_destroy,
1106 acc_close,
1107 acc_store_cred,
1108 NULL, /* acc_retrieve */
1109 acc_get_principal,
1110 acc_get_first,
1111 acc_get_next,
1112 acc_end_get,
1113 acc_remove_cred,
1114 acc_set_flags,
1115 acc_get_version,
1116 acc_get_cache_first,
1117 acc_get_cache_next,
1118 acc_end_cache_get,
1119 acc_move,
1120 acc_get_default_name,
1121 acc_set_default,
1122 acc_lastchange,
1123 NULL,
1124 NULL,
1125 acc_get_name_2,
1126 acc_resolve_2
1129 #endif