/* This file is part of TALER Copyright (C) 2021-2022 Taler Systems SA TALER is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3, or (at your option) any later version. TALER is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with TALER; see the file COPYING. If not, see */ /** * @file extensions.c * @brief Utility functions for extensions * @author Özgür Kesim */ #include "platform.h" #include "taler_util.h" #include "taler_signatures.h" #include "taler_extensions.h" #include "stdint.h" /* head of the list of all registered extensions */ static struct TALER_Extensions TE_extensions = { .next = NULL, .extension = NULL, }; const struct TALER_Extensions * TALER_extensions_get_head () { return &TE_extensions; } static enum GNUNET_GenericReturnValue add_extension ( const struct TALER_Extension *extension) { /* Sanity checks */ if ((NULL == extension) || (NULL == extension->name) || (NULL == extension->version) || (NULL == extension->disable) || (NULL == extension->load_config) || (NULL == extension->manifest)) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "invalid extension\n"); return GNUNET_SYSERR; } if (NULL == TE_extensions.extension) /* first extension ?*/ TE_extensions.extension = extension; else { struct TALER_Extensions *iter; struct TALER_Extensions *last; /* Check for collisions */ for (iter = &TE_extensions; NULL != iter && NULL != iter->extension; iter = iter->next) { const struct TALER_Extension *ext = iter->extension; last = iter; if (extension->type == ext->type || 0 == strcasecmp (extension->name, ext->name)) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "extension collision for `%s'\n", extension->name); return GNUNET_NO; } } /* No collisions found, so add this extension to the list */ { struct TALER_Extensions *extn = GNUNET_new (struct TALER_Extensions); extn->extension = extension; last->next = extn; } } return GNUNET_OK; } const struct TALER_Extension * TALER_extensions_get_by_type ( enum TALER_Extension_Type type) { for (const struct TALER_Extensions *it = &TE_extensions; NULL != it && NULL != it->extension; it = it->next) { if (it->extension->type == type) return it->extension; } /* No extension found. */ return NULL; } bool TALER_extensions_is_enabled_type ( enum TALER_Extension_Type type) { const struct TALER_Extension *ext = TALER_extensions_get_by_type (type); return (NULL != ext && ext->enabled); } const struct TALER_Extension * TALER_extensions_get_by_name ( const char *name) { for (const struct TALER_Extensions *it = &TE_extensions; NULL != it; it = it->next) { if (0 == strcasecmp (name, it->extension->name)) return it->extension; } /* No extension found, try to load it. */ return NULL; } enum GNUNET_GenericReturnValue TALER_extensions_verify_manifests_signature ( json_t *manifests, struct TALER_MasterSignatureP *extensions_sig, struct TALER_MasterPublicKeyP *master_pub) { struct TALER_ExtensionManifestsHashP h_manifests; if (GNUNET_OK != TALER_JSON_extensions_manifests_hash (manifests, &h_manifests)) return GNUNET_SYSERR; if (GNUNET_OK != TALER_exchange_offline_extension_manifests_hash_verify ( &h_manifests, master_pub, extensions_sig)) return GNUNET_NO; return GNUNET_OK; } /* * Closure used in TALER_extensions_load_taler_config during call to * GNUNET_CONFIGURATION_iterate_sections with configure_extension. */ struct LoadConfClosure { const struct GNUNET_CONFIGURATION_Handle *cfg; enum GNUNET_GenericReturnValue error; }; /* * Used in TALER_extensions_load_taler_config during call to * GNUNET_CONFIGURATION_iterate_sections to load the configuration * of supported extensions. * * @param cls Closure of type LoadConfClosure * @param section name of the current section */ static void configure_extension ( void *cls, const char *section) { struct LoadConfClosure *col = cls; const char *name; char *lib_name; struct TALER_Extension *extension; if (GNUNET_OK != col->error) return; if (0 != strncasecmp (section, TALER_EXTENSION_SECTION_PREFIX, sizeof(TALER_EXTENSION_SECTION_PREFIX) - 1)) return; name = section + sizeof(TALER_EXTENSION_SECTION_PREFIX) - 1; /* Load the extension library */ GNUNET_asprintf (&lib_name, "libtaler_extension_%s", name); extension = GNUNET_PLUGIN_load ( lib_name, (void *) col->cfg); if (NULL == extension) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Couldn't load extension library to `%s` (section [%s]).\n", name, section); col->error = GNUNET_SYSERR; return; } if (GNUNET_OK != add_extension (extension)) { /* TODO: Ignoring return values here */ GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Couldn't add extension `%s` (section [%s]).\n", name, section); col->error = GNUNET_SYSERR; GNUNET_PLUGIN_unload ( lib_name, (void *) col->cfg); return; } GNUNET_log (GNUNET_ERROR_TYPE_INFO, "extension library '%s' loaded\n", lib_name); } static bool extensions_loaded = false; enum GNUNET_GenericReturnValue TALER_extensions_init ( const struct GNUNET_CONFIGURATION_Handle *cfg) { struct LoadConfClosure col = { .cfg = cfg, .error = GNUNET_OK, }; if (extensions_loaded) return GNUNET_OK; GNUNET_CONFIGURATION_iterate_sections (cfg, &configure_extension, &col); if (GNUNET_OK == col.error) extensions_loaded = true; return col.error; } enum GNUNET_GenericReturnValue TALER_extensions_parse_manifest ( json_t *obj, int *critical, const char **version, json_t **config) { enum GNUNET_GenericReturnValue ret; json_t *cfg; struct GNUNET_JSON_Specification spec[] = { GNUNET_JSON_spec_boolean ("critical", critical), GNUNET_JSON_spec_string ("version", version), GNUNET_JSON_spec_json ("config", &cfg), GNUNET_JSON_spec_end () }; if (GNUNET_OK != (ret = GNUNET_JSON_parse (obj, spec, NULL, NULL))) return ret; *config = json_copy (cfg); GNUNET_JSON_parse_free (spec); return GNUNET_OK; } enum GNUNET_GenericReturnValue TALER_extensions_load_manifests ( json_t *extensions) { const char*name; json_t *manifest; GNUNET_assert (NULL != extensions); GNUNET_assert (json_is_object (extensions)); json_object_foreach (extensions, name, manifest) { int critical; const char *version; json_t *config; struct TALER_Extension *extension = (struct TALER_Extension *) TALER_extensions_get_by_name (name); if (NULL == extension) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "no such extension: %s\n", name); return GNUNET_SYSERR; } /* load and verify criticality, version, etc. */ if (GNUNET_OK != TALER_extensions_parse_manifest ( manifest, &critical, &version, &config)) return GNUNET_SYSERR; if (critical != extension->critical || 0 != strcmp (version, extension->version) // TODO: libtool compare? || NULL == config || GNUNET_OK != extension->load_config (NULL, config)) return GNUNET_SYSERR; /* This _should_ work now */ if (GNUNET_OK != extension->load_config (extension, config)) return GNUNET_SYSERR; extension->enabled = true; } /* make sure to disable all extensions that weren't mentioned in the json */ for (const struct TALER_Extensions *it = TALER_extensions_get_head (); NULL != it; it = it->next) { if (NULL == json_object_get (extensions, it->extension->name)) it->extension->disable ((struct TALER_Extension *) it); } return GNUNET_OK; } /* * Policy related */ static char *fulfilment2str[] = { [TALER_PolicyFulfilmentPending] = "Pending", [TALER_PolicyFulfilmentSuccessTransfer] = "SuccessTransfer", [TALER_PolicyFulfilmentSuccessRefreshable] = "SuccessRefreshable", [TALER_PolicyFulfilmentFailureTransfer] = "FailureTransfer", [TALER_PolicyFulfilmentFailureRefreshable] = "FailureRefreshable", [TALER_PolicyFulfilmentTimeoutTransfer] = "TimeoutTransfer", [TALER_PolicyFulfilmentTimeoutRefreshable] = "TimeoutRefreshable", }; const char * TALER_policy_fulfilment_state_str ( enum TALER_PolicyFulfilmentState state) { GNUNET_assert (TALER_PolicyFulfilmentStateMax >= state); return fulfilment2str[state]; } enum GNUNET_GenericReturnValue TALER_extensions_extract_meta_data_from_policy_details ( const json_t *policy_details, struct GNUNET_HashCode *serial, struct GNUNET_TIME_Timestamp *deadline, enum TALER_PolicyFulfilmentState *state_on_deadline, const char **error_hint) { enum GNUNET_GenericReturnValue ret; const struct TALER_Extension *extension; const json_t *jtype; const char *type; *error_hint = NULL; if ((NULL == policy_details) || (! json_is_object (policy_details))) { *error_hint = "invalid policy object"; return GNUNET_SYSERR; } jtype = json_object_get (policy_details, "type"); if (NULL == jtype) { *error_hint = "no type in policy object"; return GNUNET_SYSERR; } type = json_string_value (jtype); if (NULL == type) { *error_hint = "invalid type in policy object"; return GNUNET_SYSERR; } extension = TALER_extensions_get_by_name (type); if ((NULL == extension) || (NULL == extension->parse_policy_details)) { GNUNET_break (0); GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Unsupported extension policy '%s' requested\n", type); return GNUNET_NO; } *deadline = GNUNET_TIME_UNIT_FOREVER_TS; ret = extension->parse_policy_details (policy_details, serial, deadline, state_on_deadline, error_hint); GNUNET_assert ((TALER_PolicyFulfilmentTimeoutRefreshable == *state_on_deadline) || (TALER_PolicyFulfilmentTimeoutTransfer == *state_on_deadline)); return ret; } struct TALER_PolicyFulfilmentOutcome * TALER_policy_fulfilment_outcome_new (size_t len) { struct TALER_PolicyFulfilmentOutcome *out; out = GNUNET_new (struct TALER_PolicyFulfilmentOutcome); out->len = len; out->positions = GNUNET_new_array (len, struct TALER_PolicyFulfilmentOutcomePosition); return out; } void TALER_policy_fulfilment_outcome_free ( struct TALER_PolicyFulfilmentOutcome *outcome) { if (NULL == outcome) return; if (NULL != outcome->positions) GNUNET_free (outcome->positions); GNUNET_free (outcome); } /* end of extensions.c */