/* 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_extensions_policy.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 ( const 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[1024] = {0}; 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_snprintf (lib_name, sizeof(lib_name), "libtaler_extension_%s", name); /* Lower-case extension name, config is case-insensitive */ for (unsigned int i = 0; i < strlen (lib_name); i++) lib_name[i] = tolower (lib_name[i]); 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; struct GNUNET_JSON_Specification spec[] = { GNUNET_JSON_spec_boolean ("critical", critical), GNUNET_JSON_spec_string ("version", version), GNUNET_JSON_spec_json ("config", config), GNUNET_JSON_spec_end () }; *config = NULL; if (GNUNET_OK != (ret = GNUNET_JSON_parse (obj, spec, NULL, NULL))) return ret; return GNUNET_OK; } enum GNUNET_GenericReturnValue TALER_extensions_load_manifests ( const json_t *extensions) { const char *name; json_t *manifest; GNUNET_assert (NULL != extensions); GNUNET_assert (json_is_object (extensions)); json_object_foreach ((json_t *) 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 (config, NULL)) ) return GNUNET_SYSERR; /* This _should_ work now */ if (GNUNET_OK != extension->load_config (config, extension)) 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 const char *fulfillment2str[] = { [TALER_PolicyFulfillmentInitial] = "", [TALER_PolicyFulfillmentReady] = "Ready", [TALER_PolicyFulfillmentSuccess] = "Success", [TALER_PolicyFulfillmentFailure] = "Failure", [TALER_PolicyFulfillmentTimeout] = "Timeout", [TALER_PolicyFulfillmentInsufficient] = "Insufficient", }; const char * TALER_policy_fulfillment_state_str ( enum TALER_PolicyFulfillmentState state) { GNUNET_assert (TALER_PolicyFulfillmentStateCount > state); return fulfillment2str[state]; } enum GNUNET_GenericReturnValue TALER_extensions_create_policy_details ( const char *currency, const json_t *policy_options, struct TALER_PolicyDetails *details, 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_options) || (! json_is_object (policy_options))) { *error_hint = "invalid policy object"; return GNUNET_SYSERR; } jtype = json_object_get (policy_options, "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->create_policy_details)) { GNUNET_break (0); GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Unsupported extension policy '%s' requested\n", type); return GNUNET_NO; } /* Set state fields in the policy details to initial values. */ GNUNET_assert (GNUNET_OK == TALER_amount_set_zero (currency, &details->accumulated_total)); GNUNET_assert (GNUNET_OK == TALER_amount_set_zero (currency, &details->policy_fee)); details->deadline = GNUNET_TIME_UNIT_FOREVER_TS; details->fulfillment_state = TALER_PolicyFulfillmentInitial; details->no_policy_fulfillment_id = true; ret = extension->create_policy_details (currency, policy_options, details, error_hint); return ret; } /* end of extensions.c */