LCOV - code coverage report
Current view: top level - third_party/heimdal/lib/krb5 - acache.c (source / functions) Hit Total Coverage
Test: coverage report for master 2f515e9b Lines: 14 618 2.3 %
Date: 2024-04-21 15:09:00 Functions: 2 30 6.7 %

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

Generated by: LCOV version 1.14