440 lines
11 KiB
C
440 lines
11 KiB
C
/*
|
|
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 <http://www.gnu.org/licenses/>
|
|
*/
|
|
/**
|
|
* @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 (
|
|
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 char *fulfillment2str[] = {
|
|
[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 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;
|
|
}
|
|
|
|
details->deadline = GNUNET_TIME_UNIT_FOREVER_TS;
|
|
ret = extension->create_policy_details (policy_options,
|
|
details,
|
|
error_hint);
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
/* end of extensions.c */
|