aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorÖzgür Kesim <oec-taler@kesim.org>2022-01-23 01:31:02 +0100
committerÖzgür Kesim <oec-taler@kesim.org>2022-01-23 01:36:21 +0100
commit8684a9bfea9223808e33edca9f91b8bd76379fd0 (patch)
tree2354ad02b8ea515fe2de64cb8a42ca078f9f8b64
parent1962ed6b0b44c6c7d3503b3340da1be147e25f87 (diff)
[age_restriction] progress 13/n
- major refactoring of extensions - extensions live now in a separate library, libtalerextensions - refactored all components using age_restriction accordingly - plumbing for plugin support for extensions roughly layed down
-rw-r--r--configure.ac1
-rw-r--r--src/Makefile.am1
-rw-r--r--src/benchmark/Makefile.am1
-rw-r--r--src/exchange-tools/Makefile.am1
-rw-r--r--src/exchange-tools/taler-exchange-offline.c196
-rw-r--r--src/exchange/Makefile.am1
-rw-r--r--src/exchange/taler-exchange-httpd.c5
-rw-r--r--src/exchange/taler-exchange-httpd.h10
-rw-r--r--src/exchange/taler-exchange-httpd_extensions.c131
-rw-r--r--src/exchange/taler-exchange-httpd_keys.c49
-rw-r--r--src/exchange/taler-exchange-httpd_management_extensions.c38
-rw-r--r--src/extensions/Makefile.am30
-rw-r--r--src/extensions/extension_age_restriction.c321
-rw-r--r--src/extensions/extensions.c333
-rw-r--r--src/include/taler_exchange_service.h1
-rw-r--r--src/include/taler_extensions.h150
-rw-r--r--src/include/taler_json_lib.h6
-rw-r--r--src/json/json.c4
-rw-r--r--src/json/json_helper.c2
-rw-r--r--src/lib/Makefile.am1
-rw-r--r--src/lib/exchange_api_handle.c71
-rw-r--r--src/util/Makefile.am2
-rw-r--r--src/util/extension_age_restriction.c169
-rw-r--r--src/util/extensions.c49
24 files changed, 936 insertions, 637 deletions
diff --git a/configure.ac b/configure.ac
index 6995b9b3e..99d2e534c 100644
--- a/configure.ac
+++ b/configure.ac
@@ -541,6 +541,7 @@ AC_CONFIG_FILES([Makefile
src/exchange/Makefile
src/exchangedb/Makefile
src/exchange-tools/Makefile
+ src/extensions/Makefile
src/lib/Makefile
src/testing/Makefile
src/benchmark/Makefile
diff --git a/src/Makefile.am b/src/Makefile.am
index 64d6020f4..5d46850c2 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -18,6 +18,7 @@ SUBDIRS = \
include \
util \
json \
+ extensions \
curl \
$(PQ_DIR) \
$(SQ_DIR) \
diff --git a/src/benchmark/Makefile.am b/src/benchmark/Makefile.am
index 7c5ceefd6..87f1e7e5d 100644
--- a/src/benchmark/Makefile.am
+++ b/src/benchmark/Makefile.am
@@ -54,6 +54,7 @@ taler_exchange_benchmark_LDADD = \
$(top_builddir)/src/bank-lib/libtalerbank.la \
$(top_builddir)/src/json/libtalerjson.la \
$(top_builddir)/src/util/libtalerutil.la \
+ $(top_builddir)/src/extensions/libtalerextensions.la \
-lgnunetjson \
-lgnunetcurl \
-lgnunetutil \
diff --git a/src/exchange-tools/Makefile.am b/src/exchange-tools/Makefile.am
index b233a4b3a..ae53cb30b 100644
--- a/src/exchange-tools/Makefile.am
+++ b/src/exchange-tools/Makefile.am
@@ -25,6 +25,7 @@ taler_exchange_offline_LDADD = \
$(top_builddir)/src/lib/libtalerexchange.la \
$(top_builddir)/src/json/libtalerjson.la \
$(top_builddir)/src/util/libtalerutil.la \
+ $(top_builddir)/src/extensions/libtalerextensions.la \
-lgnunetjson \
-lgnunetcurl \
-ljansson \
diff --git a/src/exchange-tools/taler-exchange-offline.c b/src/exchange-tools/taler-exchange-offline.c
index 8db1fc9fa..6ad345ebf 100644
--- a/src/exchange-tools/taler-exchange-offline.c
+++ b/src/exchange-tools/taler-exchange-offline.c
@@ -1798,7 +1798,8 @@ upload_extensions (const char *exchange_url,
{
struct TALER_ExtensionConfigHash h_config;
- if (GNUNET_OK != TALER_extension_config_hash (extensions, &h_config))
+ if (GNUNET_OK !=
+ TALER_JSON_extensions_config_hash (extensions, &h_config))
{
GNUNET_JSON_parse_free (spec);
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
@@ -3506,176 +3507,30 @@ do_setup (char *const *args)
}
-/**
- * struct extension carries the information about an extension together with
- * callbacks to parse the configuration and marshal it as JSON
+/*
+ * Print the current extensions as configured
*/
-struct extension
-{
- char *name;
- bool enabled;
- bool critical;
- char *version;
- void *config;
-
- enum GNUNET_GenericReturnValue (*parse_config)(struct extension *this,
- const char *section);
- json_t *(*config_json)(const struct extension *this);
-};
-
-#define EXT_PREFIX "exchange-extension-"
-
-#define DEFAULT_AGE_GROUPS "8:10:12:14:16:18:21"
-
-static enum GNUNET_GenericReturnValue
-age_restriction_parse_config (struct extension *this, const char *section)
-{
- char *age_groups = NULL;
- struct TALER_AgeMask mask = {0};
- enum GNUNET_GenericReturnValue ret;
-
- ret = GNUNET_CONFIGURATION_get_value_yesno (kcfg, section, "ENABLED");
-
- this->enabled = (GNUNET_YES == ret);
-
- if (! this->enabled)
- return GNUNET_OK;
-
- if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (kcfg,
- section,
- "AGE_GROUPS",
- &age_groups))
- age_groups = DEFAULT_AGE_GROUPS;
-
- if (GNUNET_OK != TALER_parse_age_group_string (age_groups, &mask))
- {
- GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
- section,
- "AGE_GROUPS");
- test_shutdown ();
- global_ret = EXIT_NOTCONFIGURED;
- return GNUNET_SYSERR;
- }
-
- /* Don't look here. We just store the mask in/as the pointer .*/
- this->config = (void *) (size_t) mask.mask;
- return GNUNET_OK;
-}
-
-
-static json_t *
-age_restriction_json (const struct extension *this)
-{
- struct TALER_AgeMask mask;
- json_t *conf;
-
- if (! this->enabled)
- return NULL;
-
- /* Don't look here. We just restore the mask from/as the pointer .*/
- mask.mask = (uint32_t) (size_t) this->config;
-
- conf = GNUNET_JSON_PACK (
- GNUNET_JSON_pack_string (
- "age_groups",
- TALER_age_mask_to_string (&mask)));
-
- return GNUNET_JSON_PACK (
- GNUNET_JSON_pack_bool ("critical",
- this->critical),
- GNUNET_JSON_pack_string ("version",
- this->version),
- GNUNET_JSON_pack_object_steal ("config", conf));
-}
-
-
-static struct extension extensions[] = {
- {
- .name = "age_restriction",
- .version = "1",
- .config = 0,
- .parse_config = &age_restriction_parse_config,
- .config_json = &age_restriction_json,
- },
- /* TODO: add p2p here */
- {0},
-};
-
-
-static const struct extension*
-get_extension (const char *extension)
-{
- for (const struct extension *known = extensions;
- NULL != known->name;
- known++)
- {
- if (0 == strncasecmp (extension,
- known->name,
- strlen (known->name)))
- return known;
- }
- return NULL;
-}
-
-
static void
-collect_extensions (void *cls, const char *section)
+do_extensions_show (char *const *args)
{
- json_t *obj = (json_t *) cls;
- const char *name;
- const struct extension *extension;
-
- if (0 != global_ret)
- return;
-
- if (0 != strncasecmp (section,
- EXT_PREFIX,
- sizeof(EXT_PREFIX) - 1))
- {
- return;
- }
- name = section + sizeof(EXT_PREFIX) - 1;
+ json_t *obj = json_object ();
+ json_t *exts = json_object ();
+ const struct TALER_Extension *it;
- if (NULL == (extension = get_extension (name)))
+ TALER_extensions_init ();
+ if (GNUNET_OK != TALER_extensions_load_taler_config (kcfg))
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Unsupported extension `%s` (section [%s]).\n", name,
- section);
- test_shutdown ();
- global_ret = EXIT_NOTCONFIGURED;
+ "error while loading taler config for extensions\n");
return;
}
- if (GNUNET_OK != extension->parse_config ((struct extension *) extension,
- section))
- {
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Couldn't parse configuration for extension `%s` (section [%s]).\n",
- name,
- section);
- test_shutdown ();
- global_ret = EXIT_NOTCONFIGURED;
- return;
- }
-
- json_object_set (obj, name, extension->config_json (extension));
-}
-
-
-/*
- * Print the current extensions as configured
- */
-static void
-do_extensions_show (char *const *args)
-{
+ for (it = TALER_extensions_get_head ();
+ NULL != it;
+ it = it->next)
+ json_object_set (exts, it->name, it->config_to_json (it));
- json_t *obj = json_object ();
- json_t *exts = json_object ();
-
- GNUNET_CONFIGURATION_iterate_sections (kcfg,
- &collect_extensions,
- exts);
json_object_set (obj, "extensions", exts);
GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE, "%s\n",
@@ -3695,13 +3550,24 @@ do_extensions_sign (char *const *args)
json_t *extensions = json_object ();
struct TALER_ExtensionConfigHash h_config;
struct TALER_MasterSignatureP sig;
+ const struct TALER_Extension *it;
+
+ TALER_extensions_init ();
+
+ if (GNUNET_OK != TALER_extensions_load_taler_config (kcfg))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "error while loading taler config for extensions\n");
+ return;
+ }
- GNUNET_CONFIGURATION_iterate_sections (kcfg,
- &collect_extensions,
- extensions);
+ for (it = TALER_extensions_get_head ();
+ NULL != it;
+ it = it->next)
+ json_object_set (extensions, it->name, it->config_to_json (it));
- // TODO: check size of extensions?
- if (GNUNET_OK != TALER_extension_config_hash (extensions, &h_config))
+ if (GNUNET_OK !=
+ TALER_JSON_extensions_config_hash (extensions, &h_config))
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"error while hashing config for extensions\n");
diff --git a/src/exchange/Makefile.am b/src/exchange/Makefile.am
index e7688f735..44487a3a7 100644
--- a/src/exchange/Makefile.am
+++ b/src/exchange/Makefile.am
@@ -119,6 +119,7 @@ taler_exchange_httpd_LDADD = \
$(top_builddir)/src/json/libtalerjson.la \
$(top_builddir)/src/exchangedb/libtalerexchangedb.la \
$(top_builddir)/src/util/libtalerutil.la \
+ $(top_builddir)/src/extensions/libtalerextensions.la \
-lmicrohttpd \
-lgnunetcurl \
-lgnunetutil \
diff --git a/src/exchange/taler-exchange-httpd.c b/src/exchange/taler-exchange-httpd.c
index 5fe707304..ae5847d11 100644
--- a/src/exchange/taler-exchange-httpd.c
+++ b/src/exchange/taler-exchange-httpd.c
@@ -148,11 +148,6 @@ int TEH_check_invariants_flag;
bool TEH_suicide;
/**
- * Global register of extensions
- */
-struct TALER_Extension **TEH_extensions;
-
-/**
* Signature of the configuration of all enabled extensions,
* signed by the exchange's offline master key with purpose
* TALER_SIGNATURE_MASTER_EXTENSION.
diff --git a/src/exchange/taler-exchange-httpd.h b/src/exchange/taler-exchange-httpd.h
index 017d5520b..d3b1ba84a 100644
--- a/src/exchange/taler-exchange-httpd.h
+++ b/src/exchange/taler-exchange-httpd.h
@@ -201,21 +201,11 @@ extern volatile bool MHD_terminating;
*/
extern struct GNUNET_CURL_Context *TEH_curl_ctx;
-/**
- * The manifest of the available extensions, NULL terminated
- */
-extern struct TALER_Extension **TEH_extensions;
-
/*
* Signature of the offline master key of all enabled extensions' configuration
*/
extern struct TALER_MasterSignatureP TEH_extensions_sig;
-/* TODO: this will not work anymore, once we have plugable extensions */
-#define TEH_extension_enabled(ext) (0 <= ext && TALER_Extension_MaxPredefined > \
- ext && \
- NULL != TEH_extensions[ext]->config)
-
/**
* @brief Struct describing an URL and the handler for it.
*/
diff --git a/src/exchange/taler-exchange-httpd_extensions.c b/src/exchange/taler-exchange-httpd_extensions.c
index 0245797d4..8edb24d40 100644
--- a/src/exchange/taler-exchange-httpd_extensions.c
+++ b/src/exchange/taler-exchange-httpd_extensions.c
@@ -28,106 +28,6 @@
#include <jansson.h>
/**
- * @brief implements the TALER_Extension.disable interface.
- */
-void
-age_restriction_disable (struct TALER_Extension *this)
-{
- if (NULL == this)
- return;
-
- this->config = NULL;
-
- if (NULL != this->config_json)
- {
- json_decref (this->config_json);
- this->config_json = NULL;
- }
-}
-
-
-/**
- * @brief implements the TALER_Extension.parse_and_set_config interface.
- * @param this if NULL, only tests the configuration
- * @param config the configuration as json
- */
-static enum GNUNET_GenericReturnValue
-age_restriction_parse_and_set_config (struct TALER_Extension *this,
- json_t *config)
-{
- struct TALER_AgeMask mask = {0};
- enum GNUNET_GenericReturnValue ret;
-
- ret = TALER_agemask_parse_json (config, &mask);
- if (GNUNET_OK != ret)
- return ret;
-
- /* only testing the parser */
- if (this == NULL)
- return GNUNET_OK;
-
- if (TALER_Extension_AgeRestriction != this->type)
- return GNUNET_SYSERR;
-
- if (NULL != this->config)
- GNUNET_free (this->config);
-
- this->config = GNUNET_malloc (sizeof(struct TALER_AgeMask));
- GNUNET_memcpy (this->config, &mask, sizeof(struct TALER_AgeMask));
-
- if (NULL != this->config_json)
- json_decref (this->config_json);
-
- this->config_json = config;
-
- return GNUNET_OK;
-}
-
-
-/**
- * @brief implements the TALER_Extension.test_config interface.
- */
-static enum GNUNET_GenericReturnValue
-age_restriction_test_config (const json_t *config)
-{
- struct TALER_AgeMask mask = {0};
-
- return TALER_agemask_parse_json (config, &mask);
-}
-
-
-/* The extension for age restriction */
-static struct TALER_Extension extension_age_restriction = {
- .type = TALER_Extension_AgeRestriction,
- .name = "age_restriction",
- .critical = false,
- .version = "1",
- .config = NULL, // disabled per default
- .config_json = NULL,
- .disable = &age_restriction_disable,
- .test_config = &age_restriction_test_config,
- .parse_and_set_config = &age_restriction_parse_and_set_config,
-};
-
-/**
- * Create a list with the extensions for Age Restriction (and later Peer2Peer,
- * ...)
- */
-static struct TALER_Extension **
-get_known_extensions ()
-{
-
- struct TALER_Extension **list = GNUNET_new_array (
- TALER_Extension_MaxPredefined + 1,
- struct TALER_Extension *);
- list[TALER_Extension_AgeRestriction] = &extension_age_restriction;
- list[TALER_Extension_MaxPredefined] = NULL;
-
- return list;
-}
-
-
-/**
* Handler listening for extensions updates by other exchange
* services.
*/
@@ -148,6 +48,7 @@ extension_update_event_cb (void *cls,
{
(void) cls;
enum TALER_Extension_Type type;
+ const struct TALER_Extension *extension;
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Received extensions update event\n");
@@ -161,12 +62,15 @@ extension_update_event_cb (void *cls,
}
type = *(enum TALER_Extension_Type *) extra;
- /* TODO: This check will not work once we have plugable extensions */
- if (type <0 || type >= TALER_Extension_MaxPredefined)
+
+
+ /* Get the corresponding extension */
+ extension = TALER_extensions_get_by_type (type);
+ if (NULL == extension)
{
GNUNET_break (0);
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Oops, incorrect type for TALER_Extension_type\n");
+ "Oops, unknown extension type: %d\n", type);
return;
}
@@ -174,13 +78,10 @@ extension_update_event_cb (void *cls,
{
char *config_str = NULL;
enum GNUNET_DB_QueryStatus qs;
- struct TALER_Extension *extension;
json_error_t err;
json_t *config;
enum GNUNET_GenericReturnValue ret;
- extension = TEH_extensions[type];
-
qs = TEH_plugin->get_extension_config (TEH_plugin->cls,
extension->name,
&config_str);
@@ -193,10 +94,10 @@ extension_update_event_cb (void *cls,
return;
}
- // No config found -> extension is disabled
+ // No config found -> disable extension
if (NULL == config_str)
{
- extension->disable (extension);
+ extension->disable ((struct TALER_Extension *) extension);
return;
}
@@ -214,7 +115,10 @@ extension_update_event_cb (void *cls,
}
// Call the parser for the extension
- ret = extension->parse_and_set_config (extension, config);
+ ret = extension->load_json_config (
+ (struct TALER_Extension *) extension,
+ config);
+
if (GNUNET_OK != ret)
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
@@ -229,8 +133,7 @@ extension_update_event_cb (void *cls,
enum GNUNET_GenericReturnValue
TEH_extensions_init ()
{
- /* Populate the known extensions. */
- TEH_extensions = get_known_extensions ();
+ TALER_extensions_init ();
/* Set the event handler for updates */
struct GNUNET_DB_EventHeaderP ev = {
@@ -249,8 +152,10 @@ TEH_extensions_init ()
}
/* Trigger the initial load of configuration from the db */
- for (struct TALER_Extension **it = TEH_extensions; NULL != *it; it++)
- extension_update_event_cb (NULL, &(*it)->type, sizeof((*it)->type));
+ for (const struct TALER_Extension *it = TALER_extensions_get_head ();
+ NULL != it->next;
+ it = it->next)
+ extension_update_event_cb (NULL, &it->type, sizeof(it->type));
return GNUNET_OK;
}
diff --git a/src/exchange/taler-exchange-httpd_keys.c b/src/exchange/taler-exchange-httpd_keys.c
index 7c64cdb75..de9b81cdd 100644
--- a/src/exchange/taler-exchange-httpd_keys.c
+++ b/src/exchange/taler-exchange-httpd_keys.c
@@ -744,14 +744,14 @@ load_age_mask (const char*section_name)
{
static const struct TALER_AgeMask null_mask = {0};
struct TALER_AgeMask age_mask = {0};
+ /* TODO: optimize by putting this into global? */
const struct TALER_Extension *age_ext =
- TEH_extensions[TALER_Extension_AgeRestriction];
+ TALER_extensions_get_by_type (TALER_Extension_AgeRestriction);
// Get the age mask from the extension, if configured
- if (NULL != age_ext->config)
- {
+ /* TODO: optimize by putting this into global? */
+ if (TALER_extensions_is_enabled (age_ext))
age_mask = *(struct TALER_AgeMask *) age_ext->config;
- }
if (age_mask.mask == 0)
{
@@ -1706,14 +1706,14 @@ create_krd (struct TEH_KeyStateHandle *ksh,
// Signal support for the configured, enabled extensions.
{
json_t *extensions = json_object ();
- bool has_extensions;
+ bool has_extensions = false;
+ bool age_restriction_enabled = false;
/* Fill in the configurations of the enabled extensions */
- for (struct TALER_Extension **it = TEH_extensions;
- NULL != *it;
- it++)
+ for (const struct TALER_Extension *extension = TALER_extensions_get_head ();
+ NULL != extension;
+ extension = extension->next)
{
- const struct TALER_Extension *extension = *it;
json_t *ext;
json_t *config_json;
int r;
@@ -1722,7 +1722,10 @@ create_krd (struct TEH_KeyStateHandle *ksh,
if (NULL == extension->config)
continue;
+ /* flag our findings so far */
has_extensions = true;
+ age_restriction_enabled = (extension->type ==
+ TALER_Extension_AgeRestriction);
GNUNET_assert (NULL != extension->config_json);
@@ -1743,7 +1746,6 @@ create_krd (struct TEH_KeyStateHandle *ksh,
extensions,
extension->name,
ext);
-
GNUNET_assert (0 == r);
}
@@ -1768,20 +1770,20 @@ create_krd (struct TEH_KeyStateHandle *ksh,
r = json_object_update (keys, sig);
GNUNET_assert (0 == r);
}
- }
+ // Special case for age restrictions: if enabled, provide the lits of
+ // age-restricted denominations.
+ if (age_restriction_enabled &&
+ NULL != age_restricted_denoms)
+ {
+ GNUNET_assert (
+ 0 ==
+ json_object_set_new (
+ keys,
+ "age_restricted_denoms",
+ age_restricted_denoms));
+ }
- // Special case for age restrictions: if enabled, provide the lits of
- // age-restricted denominations.
- if (TEH_extension_enabled (TALER_Extension_AgeRestriction) &&
- NULL != age_restricted_denoms)
- {
- GNUNET_assert (
- 0 ==
- json_object_set_new (
- keys,
- "age_restricted_denoms",
- age_restricted_denoms));
}
@@ -1884,7 +1886,8 @@ finish_keys_response (struct TEH_KeyStateHandle *ksh)
GNUNET_assert (NULL != denoms);
// If age restriction is enabled, initialize the array of age restricted denoms.
- if (TEH_extension_enabled (TALER_Extension_AgeRestriction))
+ /* TODO: optimize by putting this into global? */
+ if (TALER_extensions_is_enabled_type (TALER_Extension_AgeRestriction))
{
age_restricted_denoms = json_array ();
GNUNET_assert (NULL != age_restricted_denoms);
diff --git a/src/exchange/taler-exchange-httpd_management_extensions.c b/src/exchange/taler-exchange-httpd_management_extensions.c
index 17db8e8c4..17b000067 100644
--- a/src/exchange/taler-exchange-httpd_management_extensions.c
+++ b/src/exchange/taler-exchange-httpd_management_extensions.c
@@ -79,14 +79,14 @@ set_extensions (void *cls,
for (uint32_t i = 0; i<sec->num_extensions; i++)
{
struct Extension *ext = &sec->extensions[i];
+ const struct TALER_Extension *taler_ext;
enum GNUNET_DB_QueryStatus qs;
char *config;
- /* Sanity check.
- * TODO: This will not work anymore, once we have plugable extensions
- */
- if (0 > ext->type || TALER_Extension_MaxPredefined <= ext->type)
+ taler_ext = TALER_extensions_get_by_type (ext->type);
+ if (NULL == taler_ext)
{
+ /* No such extension found */
GNUNET_break (0);
return GNUNET_DB_STATUS_HARD_ERROR;
}
@@ -104,7 +104,7 @@ set_extensions (void *cls,
qs = TEH_plugin->set_extension_config (
TEH_plugin->cls,
- TEH_extensions[ext->type]->name,
+ taler_ext->name,
config);
if (qs < 0)
@@ -183,17 +183,11 @@ TEH_handler_management_post_extensions (
/* Verify the signature */
{
struct TALER_ExtensionConfigHash h_config;
- if (GNUNET_OK != TALER_extension_config_hash (extensions, &h_config))
- {
- GNUNET_JSON_parse_free (top_spec);
- return TALER_MHD_reply_with_error (
- connection,
- MHD_HTTP_BAD_REQUEST,
- TALER_EC_GENERIC_PARAMETER_MALFORMED,
- "invalid object, non-hashable");
- }
- if (GNUNET_OK != TALER_exchange_offline_extension_config_hash_verify (
+ if (GNUNET_OK !=
+ TALER_JSON_extensions_config_hash (extensions, &h_config) ||
+ GNUNET_OK !=
+ TALER_exchange_offline_extension_config_hash_verify (
&h_config,
&TEH_master_public_key,
&sec.extensions_sig))
@@ -207,7 +201,6 @@ TEH_handler_management_post_extensions (
}
}
-
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Received /management/extensions\n");
@@ -217,7 +210,7 @@ TEH_handler_management_post_extensions (
/* Now parse individual extensions and signatures from those objects. */
{
- const struct TALER_Extension *extension;
+ const struct TALER_Extension *extension = NULL;
const char *name;
json_t *config;
int idx = 0;
@@ -225,11 +218,8 @@ TEH_handler_management_post_extensions (
json_object_foreach (extensions, name, config){
/* 1. Make sure name refers to a supported extension */
- if (GNUNET_OK != TALER_extension_get_by_name (name,
- (const struct
- TALER_Extension **)
- TEH_extensions,
- &extension))
+ extension = TALER_extensions_get_by_name (name);
+ if (NULL == extension)
{
ret = TALER_MHD_reply_with_error (
connection,
@@ -243,7 +233,9 @@ TEH_handler_management_post_extensions (
sec.extensions[idx].type = extension->type;
/* 2. Make sure the config is sound */
- if (GNUNET_OK != extension->test_config (sec.extensions[idx].config))
+ if (GNUNET_OK !=
+ extension->test_json_config (
+ sec.extensions[idx].config))
{
ret = TALER_MHD_reply_with_error (
connection,
diff --git a/src/extensions/Makefile.am b/src/extensions/Makefile.am
new file mode 100644
index 000000000..792b7eeb3
--- /dev/null
+++ b/src/extensions/Makefile.am
@@ -0,0 +1,30 @@
+# This Makefile.am is in the public domain
+
+AM_CPPFLAGS = \
+ -I$(top_srcdir)/src/include \
+ $(LIBGCRYPT_CFLAGS) \
+ $(POSTGRESQL_CPPFLAGS)
+
+if USE_COVERAGE
+ AM_CFLAGS = --coverage -O0
+ XLIB = -lgcov
+endif
+
+
+# Libraries
+
+lib_LTLIBRARIES = \
+ libtalerextensions.la
+
+libtalerextensions_la_LDFLAGS = \
+ -version-info 0:0:0 \
+ -no-undefined
+
+libtalerextensions_la_SOURCES = \
+ extensions.c \
+ extension_age_restriction.c
+
+libtalerextensions_la_LIBADD = \
+ -lgnunetjson \
+ -ljansson \
+ $(XLIB)
diff --git a/src/extensions/extension_age_restriction.c b/src/extensions/extension_age_restriction.c
new file mode 100644
index 000000000..a9ffb7f1a
--- /dev/null
+++ b/src/extensions/extension_age_restriction.c
@@ -0,0 +1,321 @@
+/*
+ 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 extension_age_restriction.c
+ * @brief Utility functions regarding age restriction
+ * @author Özgür Kesim
+ */
+#include "platform.h"
+#include "taler_util.h"
+#include "taler_extensions.h"
+#include "stdint.h"
+
+
+/**
+ * @param groups String representation of the age groups. Must be of the form
+ * a:b:...:n:m
+ * with
+ * 0 < a < b <...< n < m < 32
+ * @param[out] mask Bit representation of the age groups.
+ * @return Error if string was invalid, OK otherwise.
+ */
+enum GNUNET_GenericReturnValue
+TALER_parse_age_group_string (
+ const char *groups,
+ struct TALER_AgeMask *mask)
+{
+
+ const char *pos = groups;
+ unsigned int prev = 0;
+ unsigned int val = 0;
+ char c;
+
+ while (*pos)
+ {
+ c = *pos++;
+ if (':' == c)
+ {
+ if (prev >= val)
+ return GNUNET_SYSERR;
+
+ mask->mask |= 1 << val;
+ prev = val;
+ val = 0;
+ continue;
+ }
+
+ if ('0'>c || '9'<c)
+ return GNUNET_SYSERR;
+
+ val = 10 * val + c - '0';
+
+ if (0>=val || 32<=val)
+ return GNUNET_SYSERR;
+ }
+
+ if (0>val || 32<=val || prev>=val)
+ return GNUNET_SYSERR;
+
+ mask->mask |= (1 << val);
+ mask->mask |= 1; // mark zeroth group, too
+
+ return GNUNET_OK;
+}
+
+
+/**
+ * Encodes the age mask into a string, like "8:10:12:14:16:18:21"
+ *
+ * @param mask Age mask
+ * @return String representation of the age mask, allocated by GNUNET_malloc.
+ * Can be used as value in the TALER config.
+ */
+char *
+TALER_age_mask_to_string (
+ const struct TALER_AgeMask *m)
+{
+ uint32_t mask = m->mask;
+ unsigned int n = 0;
+ char *buf = GNUNET_malloc (32 * 3); // max characters possible
+ char *pos = buf;
+
+ if (NULL == buf)
+ {
+ return buf;
+ }
+
+ while (mask != 0)
+ {
+ mask >>= 1;
+ n++;
+ if (0 == (mask & 1))
+ {
+ continue;
+ }
+
+ if (n > 9)
+ {
+ *(pos++) = '0' + n / 10;
+ }
+ *(pos++) = '0' + n % 10;
+
+ if (0 != (mask >> 1))
+ {
+ *(pos++) = ':';
+ }
+ }
+ return buf;
+}
+
+
+/* ==================================================
+ *
+ * Age Restriction TALER_Extension imlementation
+ *
+ * ==================================================
+ */
+
+
+/**
+ * @brief implements the TALER_Extension.disable interface.
+ */
+void
+age_restriction_disable (
+ struct TALER_Extension *this)
+{
+ if (NULL == this)
+ return;
+
+ this->config = NULL;
+
+ if (NULL != this->config_json)
+ {
+ json_decref (this->config_json);
+ this->config_json = NULL;
+ }
+}
+
+
+/**
+ * @brief implements the TALER_Extension.load_taler_config interface.
+ * @param cfg Handle to the GNUNET configuration
+ * @param[out] enabled Set to true if age restriction is enabled in the config, false otherwise.
+ * @param[out] mask Mask for age restriction. Will be 0 if age restriction was not enabled in the config.
+ * @return Error if extension for age restriction was set, but age groups were
+ * invalid, OK otherwise.
+ */
+static enum GNUNET_GenericReturnValue
+age_restriction_load_taler_config (
+ struct TALER_Extension *this,
+ const struct GNUNET_CONFIGURATION_Handle *cfg)
+{
+ char *groups = NULL;
+ enum GNUNET_GenericReturnValue ret = GNUNET_SYSERR;
+ struct TALER_AgeMask mask = {0};
+
+ if ((GNUNET_YES !=
+ GNUNET_CONFIGURATION_have_value (cfg,
+ TALER_EXTENSION_SECTION_AGE_RESTRICTION,
+ "ENABLED"))
+ ||
+ (GNUNET_YES !=
+ GNUNET_CONFIGURATION_get_value_yesno (cfg,
+ TALER_EXTENSION_SECTION_AGE_RESTRICTION,
+ "ENABLED")))
+ {
+ /* Age restriction is not enabled */
+ this->config = NULL;
+ this->config_json = NULL;
+ return GNUNET_OK;
+ }
+
+ /* Age restriction is enabled, extract age groups */
+ if ((GNUNET_YES ==
+ GNUNET_CONFIGURATION_have_value (cfg,
+ TALER_EXTENSION_SECTION_AGE_RESTRICTION,
+ "AGE_GROUPS"))
+ &&
+ (GNUNET_YES !=
+ GNUNET_CONFIGURATION_get_value_string (cfg,
+ TALER_EXTENSION_SECTION_AGE_RESTRICTION,
+ "AGE_GROUPS",
+ &groups)))
+ return GNUNET_SYSERR;
+
+
+ mask.mask = TALER_EXTENSION_AGE_RESTRICTION_DEFAULT_AGE_MASK;
+
+ ret = GNUNET_OK;
+
+ if (groups != NULL)
+ {
+ ret = TALER_parse_age_group_string (groups, &mask);
+ if (GNUNET_OK != ret)
+ mask.mask = TALER_EXTENSION_AGE_RESTRICTION_DEFAULT_AGE_MASK;
+ }
+
+ if (GNUNET_OK == ret)
+ this->config = (void *) (size_t) mask.mask;
+
+ GNUNET_free (groups);
+ return ret;
+}
+
+
+/**
+ * @brief implements the TALER_Extension.load_json_config interface.
+ * @param this if NULL, only tests the configuration
+ * @param config the configuration as json
+ */
+static enum GNUNET_GenericReturnValue
+age_restriction_load_json_config (
+ struct TALER_Extension *this,
+ json_t *config)
+{
+ struct TALER_AgeMask mask = {0};
+ enum GNUNET_GenericReturnValue ret;
+
+ ret = TALER_JSON_parse_agemask (config, &mask);
+ if (GNUNET_OK != ret)
+ return ret;
+
+ /* only testing the parser */
+ if (this == NULL)
+ return GNUNET_OK;
+
+ if (TALER_Extension_AgeRestriction != this->type)
+ return GNUNET_SYSERR;
+
+ if (NULL != this->config)
+ GNUNET_free (this->config);
+
+ this->config = GNUNET_malloc (sizeof(struct TALER_AgeMask));
+ GNUNET_memcpy (this->config, &mask, sizeof(struct TALER_AgeMask));
+
+ if (NULL != this->config_json)
+ json_decref (this->config_json);
+
+ this->config_json = config;
+
+ return GNUNET_OK;
+}
+
+
+/**
+ * @brief implements the TALER_Extension.load_json_config interface.
+ * @param this if NULL, only tests the configuration
+ * @param config the configuration as json
+ */
+json_t *
+age_restriction_config_to_json (
+ const struct TALER_Extension *this)
+{
+ struct TALER_AgeMask mask;
+ char *mask_str;
+ json_t *conf;
+
+ GNUNET_assert (NULL != this);
+ GNUNET_assert (NULL != this->config);
+
+ if (NULL != this->config_json)
+ {
+ return json_copy (this->config_json);
+ }
+
+ mask.mask = (uint32_t) (size_t) this->config;
+ mask_str = TALER_age_mask_to_string (&mask);
+ conf = GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_string ("age_groups", mask_str)
+ );
+
+ return GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_bool ("critical", this->critical),
+ GNUNET_JSON_pack_string ("version", this->version),
+ GNUNET_JSON_pack_object_steal ("config", conf)
+ );
+}
+
+
+/**
+ * @brief implements the TALER_Extension.test_json_config interface.
+ */
+static enum GNUNET_GenericReturnValue
+age_restriction_test_json_config (
+ const json_t *config)
+{
+ struct TALER_AgeMask mask = {0};
+
+ return TALER_JSON_parse_agemask (config, &mask);
+}
+
+
+/* The extension for age restriction */
+struct TALER_Extension _extension_age_restriction = {
+ .next = NULL,
+ .type = TALER_Extension_AgeRestriction,
+ .name = "age_restriction",
+ .critical = false,
+ .version = "1",
+ .config = NULL, // disabled per default
+ .config_json = NULL,
+ .disable = &age_restriction_disable,
+ .test_json_config = &age_restriction_test_json_config,
+ .load_json_config = &age_restriction_load_json_config,
+ .config_to_json = &age_restriction_config_to_json,
+ .load_taler_config = &age_restriction_load_taler_config,
+};
+
+/* end of extension_age_restriction.c */
diff --git a/src/extensions/extensions.c b/src/extensions/extensions.c
new file mode 100644
index 000000000..55d970c57
--- /dev/null
+++ b/src/extensions/extensions.c
@@ -0,0 +1,333 @@
+/*
+ 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_Extension *_extensions = NULL;
+static bool _initialized = false;
+
+void
+TALER_extensions_init ()
+{
+ extern struct TALER_Extension _extension_age_restriction;
+ if (! _initialized)
+ _extensions = &_extension_age_restriction;
+
+ _initialized = true;
+}
+
+
+const struct TALER_Extension *
+TALER_extensions_get_head ()
+{
+ return _extensions;
+}
+
+
+enum GNUNET_GenericReturnValue
+TALER_extensions_add (
+ const struct TALER_Extension *new)
+{
+ struct TALER_Extension *ext;
+
+ if (_initialized)
+ return GNUNET_SYSERR;
+
+ GNUNET_assert (NULL != _extensions);
+
+ /* Sanity checks */
+ if (NULL == new ||
+ NULL == new->name ||
+ NULL == new->version ||
+ NULL == new->disable ||
+ NULL == new->test_json_config ||
+ NULL == new->load_json_config ||
+ NULL == new->config_to_json ||
+ NULL == new->load_taler_config ||
+ NULL == new->next)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "invalid extension\n");
+ return GNUNET_SYSERR;
+ }
+
+ /* Check for collisions */
+ for (ext = _extensions; NULL != ext; ext = ext->next)
+ {
+ if (new->type == ext->type ||
+ 0 == strcmp (new->name, ext->name))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "extension collision\n");
+ return GNUNET_NO;
+ }
+ }
+
+ /* No collisions found, so add this extension to the list */
+ ext->next = (struct TALER_Extension *) new;
+
+ return GNUNET_OK;
+}
+
+
+const struct TALER_Extension *
+TALER_extensions_get_by_type (
+ enum TALER_Extension_Type type)
+{
+
+ for (const struct TALER_Extension *it = _extensions;
+ NULL != it;
+ it = it->next)
+ {
+ if (it->type == type)
+ return it;
+ }
+
+ /* 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 &&
+ TALER_extensions_is_enabled (ext));
+}
+
+
+const struct TALER_Extension *
+TALER_extensions_get_by_name (
+ const char *name)
+{
+ for (const struct TALER_Extension *it = _extensions;
+ NULL != it;
+ it = it->next)
+ {
+ if (0 == strcmp (name, it->name))
+ return it;
+ }
+ /* No extension found. */
+ return NULL;
+}
+
+
+enum GNUNET_GenericReturnValue
+config_hash_verify (
+ const struct TALER_ExtensionConfigHash *h_config,
+ const struct TALER_MasterPublicKeyP *master_pub,
+ const struct TALER_MasterSignatureP *master_sig
+ )
+{
+ struct TALER_MasterExtensionConfigurationPS ec = {
+ .purpose.purpose = htonl (TALER_SIGNATURE_MASTER_EXTENSION),
+ .purpose.size = htonl (sizeof(ec)),
+ .h_config = *h_config
+ };
+
+ return GNUNET_CRYPTO_eddsa_verify (
+ TALER_SIGNATURE_MASTER_EXTENSION,
+ &ec,
+ &master_sig->eddsa_signature,
+ &master_pub->eddsa_pub);
+}
+
+
+enum GNUNET_GenericReturnValue
+TALER_extensions_verify_json_config_signature (
+ json_t *extensions,
+ struct TALER_MasterSignatureP *extensions_sig,
+ struct TALER_MasterPublicKeyP *master_pub)
+{
+ struct TALER_ExtensionConfigHash h_config;
+
+ if (GNUNET_OK !=
+ TALER_JSON_extensions_config_hash (extensions, &h_config))
+ return GNUNET_SYSERR;
+
+ if (GNUNET_OK != config_hash_verify (
+ &h_config,
+ master_pub,
+ extensions_sig))
+ return GNUNET_NO;
+
+ return GNUNET_OK;
+}
+
+
+struct load_conf_closure
+{
+ const struct GNUNET_CONFIGURATION_Handle *cfg;
+ enum GNUNET_GenericReturnValue error;
+};
+
+static void
+collect_extensions (
+ void *cls,
+ const char *section)
+{
+ struct load_conf_closure *col = cls;
+ const char *name;
+ const 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;
+
+ if (NULL == (extension = TALER_extensions_get_by_name (name)))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Unsupported extension `%s` (section [%s]).\n", name,
+ section);
+ col->error = GNUNET_SYSERR;
+ return;
+ }
+
+ if (GNUNET_OK !=
+ extension->load_taler_config (
+ (struct TALER_Extension *) extension,
+ col->cfg))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Couldn't parse configuration for extension `%s` (section [%s]).\n",
+ name,
+ section);
+ col->error = GNUNET_SYSERR;
+ return;
+ }
+}
+
+
+enum GNUNET_GenericReturnValue
+TALER_extensions_load_taler_config (
+ const struct GNUNET_CONFIGURATION_Handle *cfg)
+{
+ struct load_conf_closure col = {
+ .cfg = cfg,
+ .error = GNUNET_OK,
+ };
+
+ GNUNET_CONFIGURATION_iterate_sections (cfg,
+ &collect_extensions,
+ &col);
+ return col.error;
+}
+
+
+static enum GNUNET_GenericReturnValue
+is_json_extension_config (
+ 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 ()
+ };
+
+ ret = GNUNET_JSON_parse (obj, spec, NULL, NULL);
+ if (GNUNET_OK == ret)
+ GNUNET_JSON_parse_free (spec);
+
+ return ret;
+}
+
+
+enum GNUNET_GenericReturnValue
+TALER_extensions_load_json_config (
+ json_t *extensions)
+{
+ const char*name;
+ json_t *blob;
+
+ GNUNET_assert (NULL != extensions);
+ GNUNET_assert (json_is_object (extensions));
+
+ json_object_foreach (extensions, name, blob)
+ {
+ int critical;
+ const char *version;
+ json_t *config;
+ const struct TALER_Extension *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 !=
+ is_json_extension_config (
+ blob, &critical, &version, &config))
+ return GNUNET_SYSERR;
+
+ if (critical != extension->critical
+ || 0 != strcmp (version, extension->version) // TODO: libtool compare?
+ || NULL == config
+ || GNUNET_OK != extension->test_json_config (config))
+ return GNUNET_SYSERR;
+
+ /* This _should_ work now */
+ if (GNUNET_OK !=
+ extension->load_json_config ((struct TALER_Extension *) extension,
+ config))
+ return GNUNET_SYSERR;
+ }
+
+ /* make sure to disable all extensions that weren't mentioned in the json */
+ for (const struct TALER_Extension *it = TALER_extensions_get_head ();
+ NULL != it;
+ it = it->next)
+ {
+ if (NULL == json_object_get (extensions, it->name))
+ it->disable ((struct TALER_Extension *) it);
+ }
+
+ return GNUNET_OK;
+}
+
+
+/* end of extensions.c */
diff --git a/src/include/taler_exchange_service.h b/src/include/taler_exchange_service.h
index 5bc87cf47..caa61c5f1 100644
--- a/src/include/taler_exchange_service.h
+++ b/src/include/taler_exchange_service.h
@@ -3159,5 +3159,4 @@ void
TALER_EXCHANGE_add_auditor_denomination_cancel (
struct TALER_EXCHANGE_AuditorAddDenominationHandle *ah);
-
#endif /* _TALER_EXCHANGE_SERVICE_H */
diff --git a/src/include/taler_extensions.h b/src/include/taler_extensions.h
index 7199304d9..f00f3ed56 100644
--- a/src/include/taler_extensions.h
+++ b/src/include/taler_extensions.h
@@ -40,6 +40,9 @@ enum TALER_Extension_Type
*/
struct TALER_Extension
{
+ /* simple linked list */
+ struct TALER_Extension *next;
+
enum TALER_Extension_Type type;
char *name;
bool critical;
@@ -48,28 +51,125 @@ struct TALER_Extension
json_t *config_json;
void (*disable)(struct TALER_Extension *this);
- enum GNUNET_GenericReturnValue (*test_config)(const json_t *config);
- enum GNUNET_GenericReturnValue (*parse_and_set_config)(struct
- TALER_Extension *this,
- json_t *config);
+
+ enum GNUNET_GenericReturnValue (*test_json_config)(
+ const json_t *config);
+
+ enum GNUNET_GenericReturnValue (*load_json_config)(
+ struct TALER_Extension *this,
+ json_t *config);
+
+ json_t *(*config_to_json)(
+ const struct TALER_Extension *this);
+
+ enum GNUNET_GenericReturnValue (*load_taler_config)(
+ struct TALER_Extension *this,
+ const struct GNUNET_CONFIGURATION_Handle *cfg);
};
/**
* Generic functions for extensions
*/
+void
+TALER_extensions_init ();
+
+/*
+ * Sets the configuration of the extensions from the given TALER configuration
+ *
+ * @param cfg Handle to the TALER configuration
+ * @return GNUNET_OK on success, GNUNET_SYSERR if unknown extensions were found
+ * or any particular configuration couldn't be parsed.
+ */
+enum GNUNET_GenericReturnValue
+TALER_extensions_load_taler_config (
+ const struct GNUNET_CONFIGURATION_Handle *cfg);
+
+/*
+ * Returns the head of the linked list of extensions
+ */
+const struct TALER_Extension *
+TALER_extensions_get_head ();
+
+/*
+ * Adds an extension to the linked list of extensions
+ *
+ * @param new_extension the new extension to be added
+ * @return GNUNET_OK on success, GNUNET_SYSERR if the extension is invalid
+ * (missing fields), GNUNET_NO if there is already an extension with that name
+ * or type.
+ */
+enum GNUNET_GenericReturnValue
+TALER_extensions_add (
+ const struct TALER_Extension *new_extension);
+
+/**
+ * Finds and returns a supported extension by a given type.
+ *
+ * @param type type of the extension to lookup
+ * @return extension found, or NULL (should not happen!)
+ */
+const struct TALER_Extension *
+TALER_extensions_get_by_type (
+ enum TALER_Extension_Type type);
+
+
/**
* Finds and returns a supported extension by a given name.
*
* @param name name of the extension to lookup
- * @param extensions list of TALER_Extensions as haystack, terminated by a NULL-entry
- * @param[out] ext set to the extension, if found, NULL otherwise
- * @return GNUNET_OK if extension was found, GNUNET_NO otherwise
+ * @return the extension, if found, NULL otherwise
+ */
+const struct TALER_Extension *
+TALER_extensions_get_by_name (
+ const char *name);
+
+#define TALER_extensions_is_enabled(ext) (NULL != (ext)->config)
+
+/**
+ * Check if a given type of an extension is enabled
+ *
+ * @param type type of to check
+ * @return true enabled, false if not enabled, will assert if type is not found.
+ */
+bool
+TALER_extensions_is_enabled_type (
+ enum TALER_Extension_Type type);
+
+
+/*
+ * Verify the signature of a given JSON object for extensions with the master
+ * key of the exchange.
+ *
+ * The JSON object must be of type ExchangeKeysResponse as described in
+ * https://docs.taler.net/design-documents/006-extensions.html#exchange
+ *
+ * @param extensions JSON object with the extension configuration
+ * @param extensions_sig signature of the hash of the JSON object
+ * @param master_pub public key to verify the signature
+ * @return GNUNET_OK on success, GNUNET_SYSERR when hashing of the JSON fails
+ * and GNUNET_NO if the signature couldn't be verified.
+ */
+enum GNUNET_GenericReturnValue
+TALER_extensions_verify_json_config_signature (
+ json_t *extensions,
+ struct TALER_MasterSignatureP *extensions_sig,
+ struct TALER_MasterPublicKeyP *master_pub);
+
+/*
+ * Sets the configuration of the extensions from a given JSON object.
+ *
+ * The JSON object must be of type ExchangeKeysResponse as described in
+ * https://docs.taler.net/design-documents/006-extensions.html#exchange
+ *
+ * @param cfg Handle to the TALER configuration
+ * @return GNUNET_OK on success, GNUNET_SYSERR if unknown extensions were found
+ * or any particular configuration couldn't be parsed.
*/
enum GNUNET_GenericReturnValue
-TALER_extension_get_by_name (const char *name,
- const struct TALER_Extension **extensions,
- const struct TALER_Extension **ext);
+TALER_extensions_load_json_config (
+ json_t *extensions);
+
/*
* TALER Age Restriction Extension
@@ -82,9 +182,11 @@ TALER_extension_get_by_name (const char *name,
* The default age mask represents the age groups
* 0-7, 8-9, 10-11, 12-13, 14-15, 16-17, 18-20, 21-...
*/
-#define TALER_EXTENSION_DEFAULT_AGE_MASK (1 | 1 << 8 | 1 << 10 | 1 << 12 | 1 \
- << 14 | 1 << 16 | 1 << 18 | 1 \
- << 21)
+#define TALER_EXTENSION_AGE_RESTRICTION_DEFAULT_AGE_MASK (1 | 1 << 8 | 1 << 10 \
+ | 1 << 12 | 1 << 14 \
+ | 1 << 16 | 1 << 18 \
+ | 1 << 21)
+#define TALER_EXTENSION_AGE_RESTRICTION_DEFAULT_AGE_GROUPS "8:10:12:14:16:18:21"
/**
* @brief Parses a string as a list of age groups.
@@ -104,8 +206,9 @@ TALER_extension_get_by_name (const char *name,
* @return Error, if age groups were invalid, OK otherwise.
*/
enum GNUNET_GenericReturnValue
-TALER_parse_age_group_string (const char *groups,
- struct TALER_AgeMask *mask);
+TALER_parse_age_group_string (
+ const char *groups,
+ struct TALER_AgeMask *mask);
/**
* Encodes the age mask into a string, like "8:10:12:14:16:18:21"
@@ -115,21 +218,8 @@ TALER_parse_age_group_string (const char *groups,
* Can be used as value in the TALER config.
*/
char *
-TALER_age_mask_to_string (const struct TALER_AgeMask *mask);
-
-
-/**
- * @brief Reads the age groups from the configuration and sets the
- * corresponding age mask.
- *
- * @param cfg
- * @param[out] mask for age restriction, will be set to 0 if age restriction is disabled.
- * @return Error if extension for age restriction was set but age groups were
- * invalid, OK otherwise.
- */
-enum GNUNET_GenericReturnValue
-TALER_get_age_mask (const struct GNUNET_CONFIGURATION_Handle *cfg,
- struct TALER_AgeMask *mask);
+TALER_age_mask_to_string (
+ const struct TALER_AgeMask *mask);
/*
diff --git a/src/include/taler_json_lib.h b/src/include/taler_json_lib.h
index 102b3a6ff..2a101d269 100644
--- a/src/include/taler_json_lib.h
+++ b/src/include/taler_json_lib.h
@@ -549,8 +549,8 @@ TALER_deposit_extension_hash (const json_t *extensions,
* @return GNUNET_OK on success, GNUNET_SYSERR on failure
*/
enum GNUNET_GenericReturnValue
-TALER_extension_config_hash (const json_t *config,
- struct TALER_ExtensionConfigHash *eh);
+TALER_JSON_extensions_config_hash (const json_t *config,
+ struct TALER_ExtensionConfigHash *eh);
/**
* Parses a JSON object { "extension": "age_restriction", "mask": <uint32> }.
@@ -560,7 +560,7 @@ TALER_extension_config_hash (const json_t *config,
* @return #GNUNET_OK on success and #GNUNET_SYSERR on failure.
*/
enum GNUNET_GenericReturnValue
-TALER_agemask_parse_json (const json_t *root,
+TALER_JSON_parse_agemask (const json_t *root,
struct TALER_AgeMask *mask);
#endif /* TALER_JSON_LIB_H_ */
diff --git a/src/json/json.c b/src/json/json.c
index 956aad1a5..705cfe92b 100644
--- a/src/json/json.c
+++ b/src/json/json.c
@@ -1010,8 +1010,8 @@ TALER_deposit_extension_hash (const json_t *extensions,
enum GNUNET_GenericReturnValue
-TALER_extension_config_hash (const json_t *config,
- struct TALER_ExtensionConfigHash *ech)
+TALER_JSON_extensions_config_hash (const json_t *config,
+ struct TALER_ExtensionConfigHash *ech)
{
return dump_and_hash (config,
"taler-extension-configuration",
diff --git a/src/json/json_helper.c b/src/json/json_helper.c
index 3b4da5595..1942d09bd 100644
--- a/src/json/json_helper.c
+++ b/src/json/json_helper.c
@@ -660,7 +660,7 @@ TALER_JSON_spec_i18n_str (const char *name,
enum GNUNET_GenericReturnValue
-TALER_agemask_parse_json (const json_t *root,
+TALER_JSON_parse_agemask (const json_t *root,
struct TALER_AgeMask *mask)
{
const char *name;
diff --git a/src/lib/Makefile.am b/src/lib/Makefile.am
index dd4c527d5..3398bdf14 100644
--- a/src/lib/Makefile.am
+++ b/src/lib/Makefile.am
@@ -57,6 +57,7 @@ libtalerexchange_la_LIBADD = \
$(top_builddir)/src/json/libtalerjson.la \
$(top_builddir)/src/curl/libtalercurl.la \
$(top_builddir)/src/util/libtalerutil.la \
+ $(top_builddir)/src/extensions/libtalerextensions.la \
-lgnunetcurl \
-lgnunetjson \
-lgnunetutil \
diff --git a/src/lib/exchange_api_handle.c b/src/lib/exchange_api_handle.c
index aea09a81f..cf3d69d6a 100644
--- a/src/lib/exchange_api_handle.c
+++ b/src/lib/exchange_api_handle.c
@@ -795,50 +795,39 @@ decode_keys_json (const json_t *resp_obj,
}
/* Parse the supported extension(s): age-restriction. */
- /* TODO: maybe lift this into a FP in TALER_Extension ? */
+ /* TODO: maybe lift all this into a FP in TALER_Extension ? */
{
- json_t *age_restriction = json_object_get (resp_obj,
- "age_restriction");
-
- if (NULL != age_restriction)
- {
- bool critical;
- const char *version;
- const char *age_groups;
- struct GNUNET_JSON_Specification spec[] = {
- GNUNET_JSON_spec_bool ("critical",
- &critical),
- GNUNET_JSON_spec_string ("version",
- &version),
- GNUNET_JSON_spec_string ("age_groups",
- &age_groups),
- GNUNET_JSON_spec_end ()
- };
-
- if (GNUNET_OK !=
- GNUNET_JSON_parse (age_restriction,
- spec,
- NULL, NULL))
- {
- GNUNET_break_op (0);
- return GNUNET_SYSERR;
- }
+ struct TALER_MasterSignatureP extensions_sig = {0};
+ json_t *extensions = NULL;
+ struct GNUNET_JSON_Specification ext_spec[] = {
+ GNUNET_JSON_spec_mark_optional (
+ GNUNET_JSON_spec_json ("extensions",
+ &extensions)),
+ GNUNET_JSON_spec_mark_optional (
+ GNUNET_JSON_spec_fixed_auto (
+ "extensions_sig",
+ &extensions_sig)),
+ GNUNET_JSON_spec_end ()
+ };
- if (critical || // do we care?
- 0 != strncmp (version, "1", 1) ) /* TODO: better compatibility check */
- {
- GNUNET_break_op (0);
- return GNUNET_SYSERR;
- }
+ /* 1. Search for extensions in the response to /keys */
+ EXITIF (GNUNET_OK !=
+ GNUNET_JSON_parse (resp_obj,
+ ext_spec,
+ NULL, NULL));
- if (GNUNET_OK !=
- TALER_parse_age_group_string (age_groups,
- &key_data->age_mask))
- {
- // TODO: print more specific error?
- GNUNET_break_op (0);
- return GNUNET_SYSERR;
- }
+ if (NULL != extensions)
+ {
+ /* 2. We have an extensions object. Verify its signature. */
+ EXITIF (GNUNET_OK !=
+ TALER_extensions_verify_json_config_signature (
+ extensions,
+ &extensions_sig,
+ &key_data->master_pub));
+
+ /* 3. Parse and set the the configuration of the extensions accordingly */
+ EXITIF (GNUNET_OK !=
+ TALER_extensions_load_json_config (extensions));
}
}
diff --git a/src/util/Makefile.am b/src/util/Makefile.am
index 55ebb4dff..35e580347 100644
--- a/src/util/Makefile.am
+++ b/src/util/Makefile.am
@@ -72,8 +72,6 @@ libtalerutil_la_SOURCES = \
crypto_wire.c \
denom.c \
exchange_signatures.c \
- extensions.c \
- extension_age_restriction.c \
getopt.c \
lang.c \
iban.c \
diff --git a/src/util/extension_age_restriction.c b/src/util/extension_age_restriction.c
deleted file mode 100644
index 0b04c7d7b..000000000
--- a/src/util/extension_age_restriction.c
+++ /dev/null
@@ -1,169 +0,0 @@
-/*
- This file is part of TALER
- Copyright (C) 2014-2020 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 extension_age_restriction.c
- * @brief Utility functions regarding age restriction
- * @author Özgür Kesim
- */
-#include "platform.h"
-#include "taler_util.h"
-#include "taler_extensions.h"
-#include "stdint.h"
-
-/**
- *
- * @param cfg Handle to the GNUNET configuration
- * @param[out] Mask for age restriction. Will be 0 if age restriction was not enabled in the config.
- * @return Error if extension for age restriction was set, but age groups were
- * invalid, OK otherwise.
- */
-enum GNUNET_GenericReturnValue
-TALER_get_age_mask (const struct GNUNET_CONFIGURATION_Handle *cfg,
- struct TALER_AgeMask *mask)
-{
- char *groups;
- enum GNUNET_GenericReturnValue ret = GNUNET_SYSERR;
-
- if ((GNUNET_YES != GNUNET_CONFIGURATION_have_value (cfg,
- TALER_EXTENSION_SECTION_AGE_RESTRICTION,
- "ENABLED")) ||
- (GNUNET_YES != GNUNET_CONFIGURATION_get_value_yesno (cfg,
- TALER_EXTENSION_SECTION_AGE_RESTRICTION,
- "ENABLED")))
- {
- /* Age restriction is not enabled */
- mask->mask = 0;
- return GNUNET_OK;
- }
-
- /* Age restriction is enabled, extract age groups */
- if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (cfg,
- TALER_EXTENSION_SECTION_AGE_RESTRICTION,
- "AGE_GROUPS",
- &groups))
- {
- /* FIXME: log error? */
- return GNUNET_SYSERR;
- }
- if (groups == NULL)
- {
- /* No groups defined in config, return default_age_mask */
- mask->mask = TALER_EXTENSION_DEFAULT_AGE_MASK;
- return GNUNET_OK;
- }
-
- ret = TALER_parse_age_group_string (groups, mask);
- GNUNET_free (groups);
- return ret;
-}
-
-
-/**
- * @param groups String representation of the age groups. Must be of the form
- * a:b:...:n:m
- * with
- * 0 < a < b <...< n < m < 32
- * @param[out] mask Bit representation of the age groups.
- * @return Error if string was invalid, OK otherwise.
- */
-enum GNUNET_GenericReturnValue
-TALER_parse_age_group_string (const char *groups,
- struct TALER_AgeMask *mask)
-{
-
- const char *pos = groups;
- unsigned int prev = 0;
- unsigned int val = 0;
- char c;
-
- while (*pos)
- {
- c = *pos++;
- if (':' == c)
- {
- if (prev >= val)
- return GNUNET_SYSERR;
-
- mask->mask |= 1 << val;
- prev = val;
- val = 0;
- continue;
- }
-
- if ('0'>c || '9'<c)
- return GNUNET_SYSERR;
-
- val = 10 * val + c - '0';
-
- if (0>=val || 32<=val)
- return GNUNET_SYSERR;
- }
-
- if (0>val || 32<=val || prev>=val)
- return GNUNET_SYSERR;
-
- mask->mask |= (1 << val);
- mask->mask |= 1; // mark zeroth group, too
-
- return GNUNET_OK;
-}
-
-
-/**
- * Encodes the age mask into a string, like "8:10:12:14:16:18:21"
- *
- * @param mask Age mask
- * @return String representation of the age mask, allocated by GNUNET_malloc.
- * Can be used as value in the TALER config.
- */
-char *
-TALER_age_mask_to_string (const struct TALER_AgeMask *m)
-{
- uint32_t mask = m->mask;
- unsigned int n = 0;
- char *buf = GNUNET_malloc (32 * 3); // max characters possible
- char *pos = buf;
-
- if (NULL == buf)
- {
- return buf;
- }
-
- while (mask != 0)
- {
- mask >>= 1;
- n++;
- if (0 == (mask & 1))
- {
- continue;
- }
-
- if (n > 9)
- {
- *(pos++) = '0' + n / 10;
- }
- *(pos++) = '0' + n % 10;
-
- if (0 != (mask >> 1))
- {
- *(pos++) = ':';
- }
- }
- return buf;
-}
-
-
-/* end of extension_age_restriction.c */
diff --git a/src/util/extensions.c b/src/util/extensions.c
deleted file mode 100644
index 87dd16b4d..000000000
--- a/src/util/extensions.c
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- This file is part of TALER
- Copyright (C) 2014-2021 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_extensions.h"
-#include "stdint.h"
-
-enum GNUNET_GenericReturnValue
-TALER_extension_get_by_name (const char *name,
- const struct TALER_Extension **extensions,
- const struct TALER_Extension **ext)
-{
-
- const struct TALER_Extension *it = *extensions;
-
- for (; NULL != it; it++)
- {
- if (0 == strncmp (name,
- it->name,
- strlen (it->name)))
- {
- *ext = it;
- return GNUNET_OK;
- }
- }
-
- return GNUNET_NO;
-}
-
-
-/* end of extensions.c */