Home | History | Annotate | Line # | Download | only in krb5
      1 /*	$NetBSD: acache.c,v 1.2 2017/01/28 21:31:49 christos Exp $	*/
      2 
      3 /*
      4  * Copyright (c) 2004 - 2007 Kungliga Tekniska Hgskolan
      5  * (Royal Institute of Technology, Stockholm, Sweden).
      6  * All rights reserved.
      7  *
      8  * Portions Copyright (c) 2009 Apple Inc. All rights reserved.
      9  *
     10  * Redistribution and use in source and binary forms, with or without
     11  * modification, are permitted provided that the following conditions
     12  * are met:
     13  *
     14  * 1. Redistributions of source code must retain the above copyright
     15  *    notice, this list of conditions and the following disclaimer.
     16  *
     17  * 2. Redistributions in binary form must reproduce the above copyright
     18  *    notice, this list of conditions and the following disclaimer in the
     19  *    documentation and/or other materials provided with the distribution.
     20  *
     21  * 3. Neither the name of the Institute nor the names of its contributors
     22  *    may be used to endorse or promote products derived from this software
     23  *    without specific prior written permission.
     24  *
     25  * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
     26  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     27  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     28  * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
     29  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     30  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     31  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     32  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     33  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     34  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     35  * SUCH DAMAGE.
     36  */
     37 
     38 #include "krb5_locl.h"
     39 #include <krb5/krb5_ccapi.h>
     40 #ifdef HAVE_DLFCN_H
     41 #include <dlfcn.h>
     42 #endif
     43 
     44 #ifndef KCM_IS_API_CACHE
     45 
     46 static HEIMDAL_MUTEX acc_mutex = HEIMDAL_MUTEX_INITIALIZER;
     47 static cc_initialize_func init_func;
     48 static void (KRB5_CALLCONV *set_target_uid)(uid_t);
     49 static void (KRB5_CALLCONV *clear_target)(void);
     50 
     51 #ifdef HAVE_DLOPEN
     52 static void *cc_handle;
     53 #endif
     54 
     55 typedef struct krb5_acc {
     56     char *cache_name;
     57     cc_context_t context;
     58     cc_ccache_t ccache;
     59 } krb5_acc;
     60 
     61 static krb5_error_code KRB5_CALLCONV acc_close(krb5_context, krb5_ccache);
     62 
     63 #define ACACHE(X) ((krb5_acc *)(X)->data.data)
     64 
     65 static const struct {
     66     cc_int32 error;
     67     krb5_error_code ret;
     68 } cc_errors[] = {
     69     { ccErrBadName,		KRB5_CC_BADNAME },
     70     { ccErrCredentialsNotFound,	KRB5_CC_NOTFOUND },
     71     { ccErrCCacheNotFound,	KRB5_FCC_NOFILE },
     72     { ccErrContextNotFound,	KRB5_CC_NOTFOUND },
     73     { ccIteratorEnd,		KRB5_CC_END },
     74     { ccErrNoMem,		KRB5_CC_NOMEM },
     75     { ccErrServerUnavailable,	KRB5_CC_NOSUPP },
     76     { ccErrInvalidCCache,	KRB5_CC_BADNAME },
     77     { ccNoError,		0 }
     78 };
     79 
     80 static krb5_error_code
     81 translate_cc_error(krb5_context context, cc_int32 error)
     82 {
     83     size_t i;
     84     krb5_clear_error_message(context);
     85     for(i = 0; i < sizeof(cc_errors)/sizeof(cc_errors[0]); i++)
     86 	if (cc_errors[i].error == error)
     87 	    return cc_errors[i].ret;
     88     return KRB5_FCC_INTERNAL;
     89 }
     90 
     91 static krb5_error_code
     92 init_ccapi(krb5_context context)
     93 {
     94     const char *lib = NULL;
     95 
     96     HEIMDAL_MUTEX_lock(&acc_mutex);
     97     if (init_func) {
     98 	HEIMDAL_MUTEX_unlock(&acc_mutex);
     99 	if (context)
    100 	    krb5_clear_error_message(context);
    101 	return 0;
    102     }
    103 
    104     if (context)
    105 	lib = krb5_config_get_string(context, NULL,
    106 				     "libdefaults", "ccapi_library",
    107 				     NULL);
    108     if (lib == NULL) {
    109 #ifdef __APPLE__
    110 	lib = "/System/Library/Frameworks/Kerberos.framework/Kerberos";
    111 #elif defined(KRB5_USE_PATH_TOKENS) && defined(_WIN32)
    112 	lib = "%{LIBDIR}/libkrb5_cc.dll";
    113 #else
    114 	lib = "/usr/lib/libkrb5_cc.so";
    115 #endif
    116     }
    117 
    118 #ifdef HAVE_DLOPEN
    119 
    120 #ifndef RTLD_LAZY
    121 #define RTLD_LAZY 0
    122 #endif
    123 #ifndef RTLD_LOCAL
    124 #define RTLD_LOCAL 0
    125 #endif
    126 
    127 #ifdef KRB5_USE_PATH_TOKENS
    128     {
    129       char * explib = NULL;
    130       if (_krb5_expand_path_tokens(context, lib, 0, &explib) == 0) {
    131 	cc_handle = dlopen(explib, RTLD_LAZY|RTLD_LOCAL);
    132 	free(explib);
    133       }
    134     }
    135 #else
    136     cc_handle = dlopen(lib, RTLD_LAZY|RTLD_LOCAL);
    137 #endif
    138 
    139     if (cc_handle == NULL) {
    140 	HEIMDAL_MUTEX_unlock(&acc_mutex);
    141 	if (context)
    142 	    krb5_set_error_message(context, KRB5_CC_NOSUPP,
    143 				   N_("Failed to load API cache module %s", "file"),
    144 				   lib);
    145 	return KRB5_CC_NOSUPP;
    146     }
    147 
    148     init_func = (cc_initialize_func)dlsym(cc_handle, "cc_initialize");
    149     set_target_uid = (void (KRB5_CALLCONV *)(uid_t))
    150 	dlsym(cc_handle, "krb5_ipc_client_set_target_uid");
    151     clear_target = (void (KRB5_CALLCONV *)(void))
    152 	dlsym(cc_handle, "krb5_ipc_client_clear_target");
    153     HEIMDAL_MUTEX_unlock(&acc_mutex);
    154     if (init_func == NULL) {
    155 	if (context)
    156 	    krb5_set_error_message(context, KRB5_CC_NOSUPP,
    157 				   N_("Failed to find cc_initialize"
    158 				      "in %s: %s", "file, error"), lib, dlerror());
    159 	dlclose(cc_handle);
    160 	return KRB5_CC_NOSUPP;
    161     }
    162 
    163     return 0;
    164 #else
    165     HEIMDAL_MUTEX_unlock(&acc_mutex);
    166     if (context)
    167 	krb5_set_error_message(context, KRB5_CC_NOSUPP,
    168 			       N_("no support for shared object", ""));
    169     return KRB5_CC_NOSUPP;
    170 #endif
    171 }
    172 
    173 KRB5_LIB_FUNCTION void KRB5_LIB_CALL
    174 _heim_krb5_ipc_client_set_target_uid(uid_t uid)
    175 {
    176     init_ccapi(NULL);
    177     if (set_target_uid != NULL)
    178         (*set_target_uid)(uid);
    179 }
    180 
    181 KRB5_LIB_FUNCTION void KRB5_LIB_CALL
    182 _heim_krb5_ipc_client_clear_target(void)
    183 {
    184     init_ccapi(NULL);
    185     if (clear_target != NULL)
    186         (*clear_target)();
    187 }
    188 
    189 static krb5_error_code
    190 make_cred_from_ccred(krb5_context context,
    191 		     const cc_credentials_v5_t *incred,
    192 		     krb5_creds *cred)
    193 {
    194     krb5_error_code ret;
    195     unsigned int i;
    196 
    197     memset(cred, 0, sizeof(*cred));
    198 
    199     ret = krb5_parse_name(context, incred->client, &cred->client);
    200     if (ret)
    201 	goto fail;
    202 
    203     ret = krb5_parse_name(context, incred->server, &cred->server);
    204     if (ret)
    205 	goto fail;
    206 
    207     cred->session.keytype = incred->keyblock.type;
    208     cred->session.keyvalue.length = incred->keyblock.length;
    209     cred->session.keyvalue.data = malloc(incred->keyblock.length);
    210     if (cred->session.keyvalue.data == NULL)
    211 	goto nomem;
    212     memcpy(cred->session.keyvalue.data, incred->keyblock.data,
    213 	   incred->keyblock.length);
    214 
    215     cred->times.authtime = incred->authtime;
    216     cred->times.starttime = incred->starttime;
    217     cred->times.endtime = incred->endtime;
    218     cred->times.renew_till = incred->renew_till;
    219 
    220     ret = krb5_data_copy(&cred->ticket,
    221 			 incred->ticket.data,
    222 			 incred->ticket.length);
    223     if (ret)
    224 	goto nomem;
    225 
    226     ret = krb5_data_copy(&cred->second_ticket,
    227 			 incred->second_ticket.data,
    228 			 incred->second_ticket.length);
    229     if (ret)
    230 	goto nomem;
    231 
    232     cred->authdata.val = NULL;
    233     cred->authdata.len = 0;
    234 
    235     cred->addresses.val = NULL;
    236     cred->addresses.len = 0;
    237 
    238     for (i = 0; incred->authdata && incred->authdata[i]; i++)
    239 	;
    240 
    241     if (i) {
    242 	cred->authdata.val = calloc(i, sizeof(cred->authdata.val[0]));
    243 	if (cred->authdata.val == NULL)
    244 	    goto nomem;
    245 	cred->authdata.len = i;
    246 	for (i = 0; i < cred->authdata.len; i++) {
    247 	    cred->authdata.val[i].ad_type = incred->authdata[i]->type;
    248 	    ret = krb5_data_copy(&cred->authdata.val[i].ad_data,
    249 				 incred->authdata[i]->data,
    250 				 incred->authdata[i]->length);
    251 	    if (ret)
    252 		goto nomem;
    253 	}
    254     }
    255 
    256     for (i = 0; incred->addresses && incred->addresses[i]; i++)
    257 	;
    258 
    259     if (i) {
    260 	cred->addresses.val = calloc(i, sizeof(cred->addresses.val[0]));
    261 	if (cred->addresses.val == NULL)
    262 	    goto nomem;
    263 	cred->addresses.len = i;
    264 
    265 	for (i = 0; i < cred->addresses.len; i++) {
    266 	    cred->addresses.val[i].addr_type = incred->addresses[i]->type;
    267 	    ret = krb5_data_copy(&cred->addresses.val[i].address,
    268 				 incred->addresses[i]->data,
    269 				 incred->addresses[i]->length);
    270 	    if (ret)
    271 		goto nomem;
    272 	}
    273     }
    274 
    275     cred->flags.i = 0;
    276     if (incred->ticket_flags & KRB5_CCAPI_TKT_FLG_FORWARDABLE)
    277 	cred->flags.b.forwardable = 1;
    278     if (incred->ticket_flags & KRB5_CCAPI_TKT_FLG_FORWARDED)
    279 	cred->flags.b.forwarded = 1;
    280     if (incred->ticket_flags & KRB5_CCAPI_TKT_FLG_PROXIABLE)
    281 	cred->flags.b.proxiable = 1;
    282     if (incred->ticket_flags & KRB5_CCAPI_TKT_FLG_PROXY)
    283 	cred->flags.b.proxy = 1;
    284     if (incred->ticket_flags & KRB5_CCAPI_TKT_FLG_MAY_POSTDATE)
    285 	cred->flags.b.may_postdate = 1;
    286     if (incred->ticket_flags & KRB5_CCAPI_TKT_FLG_POSTDATED)
    287 	cred->flags.b.postdated = 1;
    288     if (incred->ticket_flags & KRB5_CCAPI_TKT_FLG_INVALID)
    289 	cred->flags.b.invalid = 1;
    290     if (incred->ticket_flags & KRB5_CCAPI_TKT_FLG_RENEWABLE)
    291 	cred->flags.b.renewable = 1;
    292     if (incred->ticket_flags & KRB5_CCAPI_TKT_FLG_INITIAL)
    293 	cred->flags.b.initial = 1;
    294     if (incred->ticket_flags & KRB5_CCAPI_TKT_FLG_PRE_AUTH)
    295 	cred->flags.b.pre_authent = 1;
    296     if (incred->ticket_flags & KRB5_CCAPI_TKT_FLG_HW_AUTH)
    297 	cred->flags.b.hw_authent = 1;
    298     if (incred->ticket_flags & KRB5_CCAPI_TKT_FLG_TRANSIT_POLICY_CHECKED)
    299 	cred->flags.b.transited_policy_checked = 1;
    300     if (incred->ticket_flags & KRB5_CCAPI_TKT_FLG_OK_AS_DELEGATE)
    301 	cred->flags.b.ok_as_delegate = 1;
    302     if (incred->ticket_flags & KRB5_CCAPI_TKT_FLG_ANONYMOUS)
    303 	cred->flags.b.anonymous = 1;
    304 
    305     return 0;
    306 
    307 nomem:
    308     ret = krb5_enomem(context);
    309 
    310 fail:
    311     krb5_free_cred_contents(context, cred);
    312     return ret;
    313 }
    314 
    315 static void
    316 free_ccred(cc_credentials_v5_t *cred)
    317 {
    318     int i;
    319 
    320     if (cred->addresses) {
    321 	for (i = 0; cred->addresses[i] != 0; i++) {
    322 	    if (cred->addresses[i]->data)
    323 		free(cred->addresses[i]->data);
    324 	    free(cred->addresses[i]);
    325 	}
    326 	free(cred->addresses);
    327     }
    328     if (cred->server)
    329 	free(cred->server);
    330     if (cred->client)
    331 	free(cred->client);
    332     memset(cred, 0, sizeof(*cred));
    333 }
    334 
    335 static krb5_error_code
    336 make_ccred_from_cred(krb5_context context,
    337 		     const krb5_creds *incred,
    338 		     cc_credentials_v5_t *cred)
    339 {
    340     krb5_error_code ret;
    341     size_t i;
    342 
    343     memset(cred, 0, sizeof(*cred));
    344 
    345     ret = krb5_unparse_name(context, incred->client, &cred->client);
    346     if (ret)
    347 	goto fail;
    348 
    349     ret = krb5_unparse_name(context, incred->server, &cred->server);
    350     if (ret)
    351 	goto fail;
    352 
    353     cred->keyblock.type = incred->session.keytype;
    354     cred->keyblock.length = incred->session.keyvalue.length;
    355     cred->keyblock.data = incred->session.keyvalue.data;
    356 
    357     cred->authtime = incred->times.authtime;
    358     cred->starttime = incred->times.starttime;
    359     cred->endtime = incred->times.endtime;
    360     cred->renew_till = incred->times.renew_till;
    361 
    362     cred->ticket.length = incred->ticket.length;
    363     cred->ticket.data = incred->ticket.data;
    364 
    365     cred->second_ticket.length = incred->second_ticket.length;
    366     cred->second_ticket.data = incred->second_ticket.data;
    367 
    368     /* XXX this one should also be filled in */
    369     cred->authdata = NULL;
    370 
    371     cred->addresses = calloc(incred->addresses.len + 1,
    372 			     sizeof(cred->addresses[0]));
    373     if (cred->addresses == NULL) {
    374 
    375 	ret = ENOMEM;
    376 	goto fail;
    377     }
    378 
    379     for (i = 0; i < incred->addresses.len; i++) {
    380 	cc_data *addr;
    381 	addr = malloc(sizeof(*addr));
    382 	if (addr == NULL) {
    383 	    ret = ENOMEM;
    384 	    goto fail;
    385 	}
    386 	addr->type = incred->addresses.val[i].addr_type;
    387 	addr->length = incred->addresses.val[i].address.length;
    388 	addr->data = malloc(addr->length);
    389 	if (addr->data == NULL) {
    390 	    free(addr);
    391 	    ret = ENOMEM;
    392 	    goto fail;
    393 	}
    394 	memcpy(addr->data, incred->addresses.val[i].address.data,
    395 	       addr->length);
    396 	cred->addresses[i] = addr;
    397     }
    398     cred->addresses[i] = NULL;
    399 
    400     cred->ticket_flags = 0;
    401     if (incred->flags.b.forwardable)
    402 	cred->ticket_flags |= KRB5_CCAPI_TKT_FLG_FORWARDABLE;
    403     if (incred->flags.b.forwarded)
    404 	cred->ticket_flags |= KRB5_CCAPI_TKT_FLG_FORWARDED;
    405     if (incred->flags.b.proxiable)
    406 	cred->ticket_flags |= KRB5_CCAPI_TKT_FLG_PROXIABLE;
    407     if (incred->flags.b.proxy)
    408 	cred->ticket_flags |= KRB5_CCAPI_TKT_FLG_PROXY;
    409     if (incred->flags.b.may_postdate)
    410 	cred->ticket_flags |= KRB5_CCAPI_TKT_FLG_MAY_POSTDATE;
    411     if (incred->flags.b.postdated)
    412 	cred->ticket_flags |= KRB5_CCAPI_TKT_FLG_POSTDATED;
    413     if (incred->flags.b.invalid)
    414 	cred->ticket_flags |= KRB5_CCAPI_TKT_FLG_INVALID;
    415     if (incred->flags.b.renewable)
    416 	cred->ticket_flags |= KRB5_CCAPI_TKT_FLG_RENEWABLE;
    417     if (incred->flags.b.initial)
    418 	cred->ticket_flags |= KRB5_CCAPI_TKT_FLG_INITIAL;
    419     if (incred->flags.b.pre_authent)
    420 	cred->ticket_flags |= KRB5_CCAPI_TKT_FLG_PRE_AUTH;
    421     if (incred->flags.b.hw_authent)
    422 	cred->ticket_flags |= KRB5_CCAPI_TKT_FLG_HW_AUTH;
    423     if (incred->flags.b.transited_policy_checked)
    424 	cred->ticket_flags |= KRB5_CCAPI_TKT_FLG_TRANSIT_POLICY_CHECKED;
    425     if (incred->flags.b.ok_as_delegate)
    426 	cred->ticket_flags |= KRB5_CCAPI_TKT_FLG_OK_AS_DELEGATE;
    427     if (incred->flags.b.anonymous)
    428 	cred->ticket_flags |= KRB5_CCAPI_TKT_FLG_ANONYMOUS;
    429 
    430     return 0;
    431 
    432 fail:
    433     free_ccred(cred);
    434 
    435     krb5_clear_error_message(context);
    436     return ret;
    437 }
    438 
    439 static cc_int32
    440 get_cc_name(krb5_acc *a)
    441 {
    442     cc_string_t name;
    443     cc_int32 error;
    444 
    445     error = (*a->ccache->func->get_name)(a->ccache, &name);
    446     if (error)
    447 	return error;
    448 
    449     a->cache_name = strdup(name->data);
    450     (*name->func->release)(name);
    451     if (a->cache_name == NULL)
    452 	return ccErrNoMem;
    453     return ccNoError;
    454 }
    455 
    456 
    457 static const char* KRB5_CALLCONV
    458 acc_get_name(krb5_context context,
    459 	     krb5_ccache id)
    460 {
    461     krb5_acc *a = ACACHE(id);
    462     int32_t error;
    463 
    464     if (a->cache_name == NULL) {
    465 	krb5_error_code ret;
    466 	krb5_principal principal;
    467 	char *name;
    468 
    469 	ret = _krb5_get_default_principal_local(context, &principal);
    470 	if (ret)
    471 	    return NULL;
    472 
    473 	ret = krb5_unparse_name(context, principal, &name);
    474 	krb5_free_principal(context, principal);
    475 	if (ret)
    476 	    return NULL;
    477 
    478 	error = (*a->context->func->create_new_ccache)(a->context,
    479 						       cc_credentials_v5,
    480 						       name,
    481 						       &a->ccache);
    482 	krb5_xfree(name);
    483 	if (error)
    484 	    return NULL;
    485 
    486 	error = get_cc_name(a);
    487 	if (error)
    488 	    return NULL;
    489     }
    490 
    491     return a->cache_name;
    492 }
    493 
    494 static krb5_error_code KRB5_CALLCONV
    495 acc_alloc(krb5_context context, krb5_ccache *id)
    496 {
    497     krb5_error_code ret;
    498     cc_int32 error;
    499     krb5_acc *a;
    500 
    501     ret = init_ccapi(context);
    502     if (ret)
    503 	return ret;
    504 
    505     ret = krb5_data_alloc(&(*id)->data, sizeof(*a));
    506     if (ret) {
    507 	krb5_clear_error_message(context);
    508 	return ret;
    509     }
    510 
    511     a = ACACHE(*id);
    512 
    513     error = (*init_func)(&a->context, ccapi_version_3, NULL, NULL);
    514     if (error) {
    515 	krb5_data_free(&(*id)->data);
    516 	return translate_cc_error(context, error);
    517     }
    518 
    519     a->cache_name = NULL;
    520 
    521     return 0;
    522 }
    523 
    524 static krb5_error_code KRB5_CALLCONV
    525 acc_resolve(krb5_context context, krb5_ccache *id, const char *res)
    526 {
    527     krb5_error_code ret;
    528     cc_int32 error;
    529     krb5_acc *a;
    530 
    531     ret = acc_alloc(context, id);
    532     if (ret)
    533 	return ret;
    534 
    535     a = ACACHE(*id);
    536 
    537     error = (*a->context->func->open_ccache)(a->context, res, &a->ccache);
    538     if (error == ccNoError) {
    539 	cc_time_t offset;
    540 	error = get_cc_name(a);
    541 	if (error != ccNoError) {
    542 	    acc_close(context, *id);
    543 	    *id = NULL;
    544 	    return translate_cc_error(context, error);
    545 	}
    546 
    547 	error = (*a->ccache->func->get_kdc_time_offset)(a->ccache,
    548 							cc_credentials_v5,
    549 							&offset);
    550 	if (error == 0)
    551 	    context->kdc_sec_offset = offset;
    552 
    553     } else if (error == ccErrCCacheNotFound) {
    554 	a->ccache = NULL;
    555 	a->cache_name = NULL;
    556     } else {
    557 	*id = NULL;
    558 	return translate_cc_error(context, error);
    559     }
    560 
    561     return 0;
    562 }
    563 
    564 static krb5_error_code KRB5_CALLCONV
    565 acc_gen_new(krb5_context context, krb5_ccache *id)
    566 {
    567     krb5_error_code ret;
    568     krb5_acc *a;
    569 
    570     ret = acc_alloc(context, id);
    571     if (ret)
    572 	return ret;
    573 
    574     a = ACACHE(*id);
    575 
    576     a->ccache = NULL;
    577     a->cache_name = NULL;
    578 
    579     return 0;
    580 }
    581 
    582 static krb5_error_code KRB5_CALLCONV
    583 acc_initialize(krb5_context context,
    584 	       krb5_ccache id,
    585 	       krb5_principal primary_principal)
    586 {
    587     krb5_acc *a = ACACHE(id);
    588     krb5_error_code ret;
    589     int32_t error;
    590     char *name;
    591 
    592     ret = krb5_unparse_name(context, primary_principal, &name);
    593     if (ret)
    594 	return ret;
    595 
    596     if (a->cache_name == NULL) {
    597 	error = (*a->context->func->create_new_ccache)(a->context,
    598 						       cc_credentials_v5,
    599 						       name,
    600 						       &a->ccache);
    601 	free(name);
    602 	if (error == ccNoError)
    603 	    error = get_cc_name(a);
    604     } else {
    605 	cc_credentials_iterator_t iter;
    606 	cc_credentials_t ccred;
    607 
    608 	error = (*a->ccache->func->new_credentials_iterator)(a->ccache, &iter);
    609 	if (error) {
    610 	    free(name);
    611 	    return translate_cc_error(context, error);
    612 	}
    613 
    614 	while (1) {
    615 	    error = (*iter->func->next)(iter, &ccred);
    616 	    if (error)
    617 		break;
    618 	    (*a->ccache->func->remove_credentials)(a->ccache, ccred);
    619 	    (*ccred->func->release)(ccred);
    620 	}
    621 	(*iter->func->release)(iter);
    622 
    623 	error = (*a->ccache->func->set_principal)(a->ccache,
    624 						  cc_credentials_v5,
    625 						  name);
    626     }
    627 
    628     if (error == 0 && context->kdc_sec_offset)
    629 	error = (*a->ccache->func->set_kdc_time_offset)(a->ccache,
    630 							cc_credentials_v5,
    631 							context->kdc_sec_offset);
    632 
    633     return translate_cc_error(context, error);
    634 }
    635 
    636 static krb5_error_code KRB5_CALLCONV
    637 acc_close(krb5_context context,
    638 	  krb5_ccache id)
    639 {
    640     krb5_acc *a = ACACHE(id);
    641 
    642     if (a->ccache) {
    643 	(*a->ccache->func->release)(a->ccache);
    644 	a->ccache = NULL;
    645     }
    646     if (a->cache_name) {
    647 	free(a->cache_name);
    648 	a->cache_name = NULL;
    649     }
    650     if (a->context) {
    651 	(*a->context->func->release)(a->context);
    652 	a->context = NULL;
    653     }
    654     krb5_data_free(&id->data);
    655     return 0;
    656 }
    657 
    658 static krb5_error_code KRB5_CALLCONV
    659 acc_destroy(krb5_context context,
    660 	    krb5_ccache id)
    661 {
    662     krb5_acc *a = ACACHE(id);
    663     cc_int32 error = 0;
    664 
    665     if (a->ccache) {
    666 	error = (*a->ccache->func->destroy)(a->ccache);
    667 	a->ccache = NULL;
    668     }
    669     if (a->context) {
    670 	error = (a->context->func->release)(a->context);
    671 	a->context = NULL;
    672     }
    673     return translate_cc_error(context, error);
    674 }
    675 
    676 static krb5_error_code KRB5_CALLCONV
    677 acc_store_cred(krb5_context context,
    678 	       krb5_ccache id,
    679 	       krb5_creds *creds)
    680 {
    681     krb5_acc *a = ACACHE(id);
    682     cc_credentials_union cred;
    683     cc_credentials_v5_t v5cred;
    684     krb5_error_code ret;
    685     cc_int32 error;
    686 
    687     if (a->ccache == NULL) {
    688 	krb5_set_error_message(context, KRB5_CC_NOTFOUND,
    689 			       N_("No API credential found", ""));
    690 	return KRB5_CC_NOTFOUND;
    691     }
    692 
    693     cred.version = cc_credentials_v5;
    694     cred.credentials.credentials_v5 = &v5cred;
    695 
    696     ret = make_ccred_from_cred(context,
    697 			       creds,
    698 			       &v5cred);
    699     if (ret)
    700 	return ret;
    701 
    702     error = (*a->ccache->func->store_credentials)(a->ccache, &cred);
    703     if (error)
    704 	ret = translate_cc_error(context, error);
    705 
    706     free_ccred(&v5cred);
    707 
    708     return ret;
    709 }
    710 
    711 static krb5_error_code KRB5_CALLCONV
    712 acc_get_principal(krb5_context context,
    713 		  krb5_ccache id,
    714 		  krb5_principal *principal)
    715 {
    716     krb5_acc *a = ACACHE(id);
    717     krb5_error_code ret;
    718     int32_t error;
    719     cc_string_t name;
    720 
    721     if (a->ccache == NULL) {
    722 	krb5_set_error_message(context, KRB5_CC_NOTFOUND,
    723 			       N_("No API credential found", ""));
    724 	return KRB5_CC_NOTFOUND;
    725     }
    726 
    727     error = (*a->ccache->func->get_principal)(a->ccache,
    728 					      cc_credentials_v5,
    729 					      &name);
    730     if (error)
    731 	return translate_cc_error(context, error);
    732 
    733     ret = krb5_parse_name(context, name->data, principal);
    734 
    735     (*name->func->release)(name);
    736     return ret;
    737 }
    738 
    739 static krb5_error_code KRB5_CALLCONV
    740 acc_get_first (krb5_context context,
    741 	       krb5_ccache id,
    742 	       krb5_cc_cursor *cursor)
    743 {
    744     cc_credentials_iterator_t iter;
    745     krb5_acc *a = ACACHE(id);
    746     int32_t error;
    747 
    748     if (a->ccache == NULL) {
    749 	krb5_set_error_message(context, KRB5_CC_NOTFOUND,
    750 			       N_("No API credential found", ""));
    751 	return KRB5_CC_NOTFOUND;
    752     }
    753 
    754     error = (*a->ccache->func->new_credentials_iterator)(a->ccache, &iter);
    755     if (error) {
    756 	krb5_clear_error_message(context);
    757 	return ENOENT;
    758     }
    759     *cursor = iter;
    760     return 0;
    761 }
    762 
    763 
    764 static krb5_error_code KRB5_CALLCONV
    765 acc_get_next (krb5_context context,
    766 	      krb5_ccache id,
    767 	      krb5_cc_cursor *cursor,
    768 	      krb5_creds *creds)
    769 {
    770     cc_credentials_iterator_t iter = *cursor;
    771     cc_credentials_t cred;
    772     krb5_error_code ret;
    773     int32_t error;
    774 
    775     while (1) {
    776 	error = (*iter->func->next)(iter, &cred);
    777 	if (error)
    778 	    return translate_cc_error(context, error);
    779 	if (cred->data->version == cc_credentials_v5)
    780 	    break;
    781 	(*cred->func->release)(cred);
    782     }
    783 
    784     ret = make_cred_from_ccred(context,
    785 			       cred->data->credentials.credentials_v5,
    786 			       creds);
    787     (*cred->func->release)(cred);
    788     return ret;
    789 }
    790 
    791 static krb5_error_code KRB5_CALLCONV
    792 acc_end_get (krb5_context context,
    793 	     krb5_ccache id,
    794 	     krb5_cc_cursor *cursor)
    795 {
    796     cc_credentials_iterator_t iter = *cursor;
    797     (*iter->func->release)(iter);
    798     return 0;
    799 }
    800 
    801 static krb5_error_code KRB5_CALLCONV
    802 acc_remove_cred(krb5_context context,
    803 		krb5_ccache id,
    804 		krb5_flags which,
    805 		krb5_creds *cred)
    806 {
    807     cc_credentials_iterator_t iter;
    808     krb5_acc *a = ACACHE(id);
    809     cc_credentials_t ccred;
    810     krb5_error_code ret;
    811     cc_int32 error;
    812     char *client, *server;
    813 
    814     if (a->ccache == NULL) {
    815 	krb5_set_error_message(context, KRB5_CC_NOTFOUND,
    816 			       N_("No API credential found", ""));
    817 	return KRB5_CC_NOTFOUND;
    818     }
    819 
    820     if (cred->client) {
    821 	ret = krb5_unparse_name(context, cred->client, &client);
    822 	if (ret)
    823 	    return ret;
    824     } else
    825 	client = NULL;
    826 
    827     ret = krb5_unparse_name(context, cred->server, &server);
    828     if (ret) {
    829 	free(client);
    830 	return ret;
    831     }
    832 
    833     error = (*a->ccache->func->new_credentials_iterator)(a->ccache, &iter);
    834     if (error) {
    835 	free(server);
    836 	free(client);
    837 	return translate_cc_error(context, error);
    838     }
    839 
    840     ret = KRB5_CC_NOTFOUND;
    841     while (1) {
    842 	cc_credentials_v5_t *v5cred;
    843 
    844 	error = (*iter->func->next)(iter, &ccred);
    845 	if (error)
    846 	    break;
    847 
    848 	if (ccred->data->version != cc_credentials_v5)
    849 	    goto next;
    850 
    851 	v5cred = ccred->data->credentials.credentials_v5;
    852 
    853 	if (client && strcmp(v5cred->client, client) != 0)
    854 	    goto next;
    855 
    856 	if (strcmp(v5cred->server, server) != 0)
    857 	    goto next;
    858 
    859 	(*a->ccache->func->remove_credentials)(a->ccache, ccred);
    860 	ret = 0;
    861     next:
    862 	(*ccred->func->release)(ccred);
    863     }
    864 
    865     (*iter->func->release)(iter);
    866 
    867     if (ret)
    868 	krb5_set_error_message(context, ret,
    869 			       N_("Can't find credential %s in cache",
    870 				 "principal"), server);
    871     free(server);
    872     free(client);
    873 
    874     return ret;
    875 }
    876 
    877 static krb5_error_code KRB5_CALLCONV
    878 acc_set_flags(krb5_context context,
    879 	      krb5_ccache id,
    880 	      krb5_flags flags)
    881 {
    882     return 0;
    883 }
    884 
    885 static int KRB5_CALLCONV
    886 acc_get_version(krb5_context context,
    887 		krb5_ccache id)
    888 {
    889     return 0;
    890 }
    891 
    892 struct cache_iter {
    893     cc_context_t context;
    894     cc_ccache_iterator_t iter;
    895 };
    896 
    897 static krb5_error_code KRB5_CALLCONV
    898 acc_get_cache_first(krb5_context context, krb5_cc_cursor *cursor)
    899 {
    900     struct cache_iter *iter;
    901     krb5_error_code ret;
    902     cc_int32 error;
    903 
    904     ret = init_ccapi(context);
    905     if (ret)
    906 	return ret;
    907 
    908     iter = calloc(1, sizeof(*iter));
    909     if (iter == NULL)
    910 	return krb5_enomem(context);
    911 
    912     error = (*init_func)(&iter->context, ccapi_version_3, NULL, NULL);
    913     if (error) {
    914 	free(iter);
    915 	return translate_cc_error(context, error);
    916     }
    917 
    918     error = (*iter->context->func->new_ccache_iterator)(iter->context,
    919 							&iter->iter);
    920     if (error) {
    921 	free(iter);
    922 	krb5_clear_error_message(context);
    923 	return ENOENT;
    924     }
    925     *cursor = iter;
    926     return 0;
    927 }
    928 
    929 static krb5_error_code KRB5_CALLCONV
    930 acc_get_cache_next(krb5_context context, krb5_cc_cursor cursor, krb5_ccache *id)
    931 {
    932     struct cache_iter *iter = cursor;
    933     cc_ccache_t cache;
    934     krb5_acc *a;
    935     krb5_error_code ret;
    936     int32_t error;
    937 
    938     error = (*iter->iter->func->next)(iter->iter, &cache);
    939     if (error)
    940 	return translate_cc_error(context, error);
    941 
    942     ret = _krb5_cc_allocate(context, &krb5_acc_ops, id);
    943     if (ret) {
    944 	(*cache->func->release)(cache);
    945 	return ret;
    946     }
    947 
    948     ret = acc_alloc(context, id);
    949     if (ret) {
    950 	(*cache->func->release)(cache);
    951 	free(*id);
    952 	return ret;
    953     }
    954 
    955     a = ACACHE(*id);
    956     a->ccache = cache;
    957 
    958     error = get_cc_name(a);
    959     if (error) {
    960 	acc_close(context, *id);
    961 	*id = NULL;
    962 	return translate_cc_error(context, error);
    963     }
    964     return 0;
    965 }
    966 
    967 static krb5_error_code KRB5_CALLCONV
    968 acc_end_cache_get(krb5_context context, krb5_cc_cursor cursor)
    969 {
    970     struct cache_iter *iter = cursor;
    971 
    972     (*iter->iter->func->release)(iter->iter);
    973     iter->iter = NULL;
    974     (*iter->context->func->release)(iter->context);
    975     iter->context = NULL;
    976     free(iter);
    977     return 0;
    978 }
    979 
    980 static krb5_error_code KRB5_CALLCONV
    981 acc_move(krb5_context context, krb5_ccache from, krb5_ccache to)
    982 {
    983     krb5_acc *afrom = ACACHE(from);
    984     krb5_acc *ato = ACACHE(to);
    985     int32_t error;
    986 
    987     if (ato->ccache == NULL) {
    988 	cc_string_t name;
    989 
    990 	error = (*afrom->ccache->func->get_principal)(afrom->ccache,
    991 						      cc_credentials_v5,
    992 						      &name);
    993 	if (error)
    994 	    return translate_cc_error(context, error);
    995 
    996 	error = (*ato->context->func->create_new_ccache)(ato->context,
    997 							 cc_credentials_v5,
    998 							 name->data,
    999 							 &ato->ccache);
   1000 	(*name->func->release)(name);
   1001 	if (error)
   1002 	    return translate_cc_error(context, error);
   1003     }
   1004 
   1005     error = (*ato->ccache->func->move)(afrom->ccache, ato->ccache);
   1006 
   1007     acc_destroy(context, from);
   1008 
   1009     return translate_cc_error(context, error);
   1010 }
   1011 
   1012 static krb5_error_code KRB5_CALLCONV
   1013 acc_get_default_name(krb5_context context, char **str)
   1014 {
   1015     krb5_error_code ret;
   1016     cc_context_t cc;
   1017     cc_string_t name;
   1018     int32_t error;
   1019 
   1020     ret = init_ccapi(context);
   1021     if (ret)
   1022 	return ret;
   1023 
   1024     error = (*init_func)(&cc, ccapi_version_3, NULL, NULL);
   1025     if (error)
   1026 	return translate_cc_error(context, error);
   1027 
   1028     error = (*cc->func->get_default_ccache_name)(cc, &name);
   1029     if (error) {
   1030 	(*cc->func->release)(cc);
   1031 	return translate_cc_error(context, error);
   1032     }
   1033 
   1034     error = asprintf(str, "API:%s", name->data);
   1035     (*name->func->release)(name);
   1036     (*cc->func->release)(cc);
   1037 
   1038     if (error < 0 || *str == NULL)
   1039 	return krb5_enomem(context);
   1040     return 0;
   1041 }
   1042 
   1043 static krb5_error_code KRB5_CALLCONV
   1044 acc_set_default(krb5_context context, krb5_ccache id)
   1045 {
   1046     krb5_acc *a = ACACHE(id);
   1047     cc_int32 error;
   1048 
   1049     if (a->ccache == NULL) {
   1050 	krb5_set_error_message(context, KRB5_CC_NOTFOUND,
   1051 			       N_("No API credential found", ""));
   1052 	return KRB5_CC_NOTFOUND;
   1053     }
   1054 
   1055     error = (*a->ccache->func->set_default)(a->ccache);
   1056     if (error)
   1057 	return translate_cc_error(context, error);
   1058 
   1059     return 0;
   1060 }
   1061 
   1062 static krb5_error_code KRB5_CALLCONV
   1063 acc_lastchange(krb5_context context, krb5_ccache id, krb5_timestamp *mtime)
   1064 {
   1065     krb5_acc *a = ACACHE(id);
   1066     cc_int32 error;
   1067     cc_time_t t;
   1068 
   1069     if (a->ccache == NULL) {
   1070 	krb5_set_error_message(context, KRB5_CC_NOTFOUND,
   1071 			       N_("No API credential found", ""));
   1072 	return KRB5_CC_NOTFOUND;
   1073     }
   1074 
   1075     error = (*a->ccache->func->get_change_time)(a->ccache, &t);
   1076     if (error)
   1077 	return translate_cc_error(context, error);
   1078 
   1079     *mtime = t;
   1080 
   1081     return 0;
   1082 }
   1083 
   1084 /**
   1085  * Variable containing the API based credential cache implemention.
   1086  *
   1087  * @ingroup krb5_ccache
   1088  */
   1089 
   1090 KRB5_LIB_VARIABLE const krb5_cc_ops krb5_acc_ops = {
   1091     KRB5_CC_OPS_VERSION,
   1092     "API",
   1093     acc_get_name,
   1094     acc_resolve,
   1095     acc_gen_new,
   1096     acc_initialize,
   1097     acc_destroy,
   1098     acc_close,
   1099     acc_store_cred,
   1100     NULL, /* acc_retrieve */
   1101     acc_get_principal,
   1102     acc_get_first,
   1103     acc_get_next,
   1104     acc_end_get,
   1105     acc_remove_cred,
   1106     acc_set_flags,
   1107     acc_get_version,
   1108     acc_get_cache_first,
   1109     acc_get_cache_next,
   1110     acc_end_cache_get,
   1111     acc_move,
   1112     acc_get_default_name,
   1113     acc_set_default,
   1114     acc_lastchange,
   1115     NULL,
   1116     NULL,
   1117 };
   1118 
   1119 #endif
   1120