From 1962ed6b0b44c6c7d3503b3340da1be147e25f87 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=96zg=C3=BCr=20Kesim?= Date: Sat, 22 Jan 2022 00:26:43 +0100 Subject: [PATCH] improvements in extension handling - extensions_sig is needed globally - keep original json with config of extension - fixed various bugs re: extension handling --- src/exchange/taler-exchange-httpd.c | 7 + src/exchange/taler-exchange-httpd.h | 5 + .../taler-exchange-httpd_extensions.c | 138 ++++++++++-------- src/exchange/taler-exchange-httpd_keys.c | 92 +++++++++--- ...ler-exchange-httpd_management_extensions.c | 12 +- src/exchangedb/plugin_exchangedb_postgres.c | 5 +- src/include/taler_extensions.h | 14 +- 7 files changed, 171 insertions(+), 102 deletions(-) diff --git a/src/exchange/taler-exchange-httpd.c b/src/exchange/taler-exchange-httpd.c index 59398c6fc..5fe707304 100644 --- a/src/exchange/taler-exchange-httpd.c +++ b/src/exchange/taler-exchange-httpd.c @@ -152,6 +152,13 @@ bool TEH_suicide; */ 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. + */ +struct TALER_MasterSignatureP TEH_extensions_sig; + /** * Value to return from main() */ diff --git a/src/exchange/taler-exchange-httpd.h b/src/exchange/taler-exchange-httpd.h index 39666379e..017d5520b 100644 --- a/src/exchange/taler-exchange-httpd.h +++ b/src/exchange/taler-exchange-httpd.h @@ -206,6 +206,11 @@ extern struct GNUNET_CURL_Context *TEH_curl_ctx; */ 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 && \ diff --git a/src/exchange/taler-exchange-httpd_extensions.c b/src/exchange/taler-exchange-httpd_extensions.c index 1a2c4552d..0245797d4 100644 --- a/src/exchange/taler-exchange-httpd_extensions.c +++ b/src/exchange/taler-exchange-httpd_extensions.c @@ -27,29 +27,58 @@ #include "taler_extensions.h" #include +/** + * @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, - const json_t *config) + json_t *config) { - enum GNUNET_GenericReturnValue ret; struct TALER_AgeMask mask = {0}; + enum GNUNET_GenericReturnValue ret; ret = TALER_agemask_parse_json (config, &mask); if (GNUNET_OK != ret) return ret; - if (this != NULL && TALER_Extension_AgeRestriction == this->type) - { - 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)); - } + /* 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; } @@ -61,28 +90,9 @@ age_restriction_parse_and_set_config (struct TALER_Extension *this, static enum GNUNET_GenericReturnValue age_restriction_test_config (const json_t *config) { - return age_restriction_parse_and_set_config (NULL, config); -} + struct TALER_AgeMask mask = {0}; - -/** - * @brief implements the TALER_Extension.config_to_json interface. - */ -static json_t * -age_restriction_config_to_json (const struct TALER_Extension *this) -{ - const struct TALER_AgeMask *mask; - if (NULL == this || TALER_Extension_AgeRestriction != this->type) - return NULL; - - mask = (struct TALER_AgeMask *) this->config; - json_t *config = GNUNET_JSON_PACK ( - GNUNET_JSON_pack_string ("extension", this->name), - GNUNET_JSON_pack_string ("mask", - TALER_age_mask_to_string (mask)) - ); - - return config; + return TALER_agemask_parse_json (config, &mask); } @@ -91,26 +101,17 @@ 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, - .config_to_json = &age_restriction_config_to_json, }; -/* TODO: The extension for peer2peer */ -static struct TALER_Extension extension_peer2peer = { - .type = TALER_Extension_Peer2Peer, - .name = "peer2peer", - .critical = false, - .config = NULL, // disabled per default - .test_config = NULL, // TODO - .parse_and_set_config = NULL, // TODO - .config_to_json = NULL, // TODO -}; - - /** - * Create a list with the extensions for Age Restriction and Peer2Peer + * Create a list with the extensions for Age Restriction (and later Peer2Peer, + * ...) */ static struct TALER_Extension ** get_known_extensions () @@ -120,7 +121,6 @@ get_known_extensions () TALER_Extension_MaxPredefined + 1, struct TALER_Extension *); list[TALER_Extension_AgeRestriction] = &extension_age_restriction; - list[TALER_Extension_Peer2Peer] = &extension_peer2peer; list[TALER_Extension_MaxPredefined] = NULL; return list; @@ -172,14 +172,13 @@ extension_update_event_cb (void *cls, // Get the config from the database as string { - char *config_str; + char *config_str = NULL; enum GNUNET_DB_QueryStatus qs; struct TALER_Extension *extension; json_error_t err; json_t *config; enum GNUNET_GenericReturnValue ret; - // TODO: make this a safe lookup extension = TEH_extensions[type]; qs = TEH_plugin->get_extension_config (TEH_plugin->cls, @@ -194,6 +193,13 @@ extension_update_event_cb (void *cls, return; } + // No config found -> extension is disabled + if (NULL == config_str) + { + extension->disable (extension); + return; + } + // Parse the string as JSON config = json_loads (config_str, JSON_DECODE_ANY, &err); if (NULL == config) @@ -223,25 +229,29 @@ extension_update_event_cb (void *cls, enum GNUNET_GenericReturnValue TEH_extensions_init () { + /* Populate the known extensions. */ TEH_extensions = get_known_extensions (); + /* Set the event handler for updates */ + struct GNUNET_DB_EventHeaderP ev = { + .size = htons (sizeof (ev)), + .type = htons (TALER_DBEVENT_EXCHANGE_EXTENSIONS_UPDATED), + }; + extensions_eh = TEH_plugin->event_listen (TEH_plugin->cls, + GNUNET_TIME_UNIT_FOREVER_REL, + &ev, + &extension_update_event_cb, + NULL); + if (NULL == extensions_eh) { - struct GNUNET_DB_EventHeaderP ev = { - .size = htons (sizeof (ev)), - .type = htons (TALER_DBEVENT_EXCHANGE_EXTENSIONS_UPDATED), - }; - - extensions_eh = TEH_plugin->event_listen (TEH_plugin->cls, - GNUNET_TIME_UNIT_FOREVER_REL, - &ev, - &extension_update_event_cb, - NULL); - if (NULL == extensions_eh) - { - GNUNET_break (0); - return GNUNET_SYSERR; - } + GNUNET_break (0); + return GNUNET_SYSERR; } + + /* 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)); + return GNUNET_OK; } diff --git a/src/exchange/taler-exchange-httpd_keys.c b/src/exchange/taler-exchange-httpd_keys.c index 30bbe8ebb..7c64cdb75 100644 --- a/src/exchange/taler-exchange-httpd_keys.c +++ b/src/exchange/taler-exchange-httpd_keys.c @@ -1703,29 +1703,79 @@ create_krd (struct TEH_KeyStateHandle *ksh, &TEH_kyc_config.wallet_balance_limit))); } - // Signal support for the age-restriction extension, if so configured, and - // add the array of age-restricted denominations. + // Signal support for the configured, enabled extensions. + { + json_t *extensions = json_object (); + bool has_extensions; + + /* Fill in the configurations of the enabled extensions */ + for (struct TALER_Extension **it = TEH_extensions; + NULL != *it; + it++) + { + const struct TALER_Extension *extension = *it; + json_t *ext; + json_t *config_json; + int r; + + /* skip if not configured == disabled */ + if (NULL == extension->config) + continue; + + has_extensions = true; + + GNUNET_assert (NULL != extension->config_json); + + config_json = json_copy (extension->config_json); + GNUNET_assert (NULL != config_json); + + ext = GNUNET_JSON_PACK ( + GNUNET_JSON_pack_bool ("critical", + extension->critical), + GNUNET_JSON_pack_string ("version", + extension->version), + GNUNET_JSON_pack_object_steal ("config", + config_json) + ); + GNUNET_assert (NULL != ext); + + r = json_object_set_new ( + extensions, + extension->name, + ext); + + GNUNET_assert (0 == r); + } + + /* Update the keys object with the extensions */ + if (has_extensions) + { + json_t *sig; + int r; + + r = json_object_set_new ( + keys, + "extensions", + extensions); + GNUNET_assert (0 == r); + + /* add extensions_sig */ + sig = GNUNET_JSON_PACK ( + GNUNET_JSON_pack_data_auto ("extensions_sig", + &TEH_extensions_sig)); + + /* update the keys object with extensions_sig */ + 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 (TEH_extension_enabled (TALER_Extension_AgeRestriction) && NULL != age_restricted_denoms) { - struct TALER_AgeMask *mask; - json_t *config; - - mask = (struct - TALER_AgeMask *) TEH_extensions[TALER_Extension_AgeRestriction]-> - config; - config = GNUNET_JSON_PACK ( - GNUNET_JSON_pack_bool ("critical", false), - GNUNET_JSON_pack_string ("version", "1"), - GNUNET_JSON_pack_string ("age_groups", TALER_age_mask_to_string (mask))); - GNUNET_assert (NULL != config); - GNUNET_assert ( - 0 == - json_object_set_new ( - keys, - "age_restriction", - config)); - GNUNET_assert ( 0 == json_object_set_new ( @@ -1734,8 +1784,6 @@ create_krd (struct TEH_KeyStateHandle *ksh, age_restricted_denoms)); } - // TODO: signal support and configuration for the P2P extension, once - // implemented. { char *keys_json; diff --git a/src/exchange/taler-exchange-httpd_management_extensions.c b/src/exchange/taler-exchange-httpd_management_extensions.c index 8476e669d..17db8e8c4 100644 --- a/src/exchange/taler-exchange-httpd_management_extensions.c +++ b/src/exchange/taler-exchange-httpd_management_extensions.c @@ -49,6 +49,7 @@ struct SetExtensionsContext { uint32_t num_extensions; struct Extension *extensions; + struct TALER_MasterSignatureP extensions_sig; }; /** @@ -132,6 +133,9 @@ set_extensions (void *cls, } + /* All extensions configured, update the signature */ + TEH_extensions_sig = sec->extensions_sig; + return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT; /* only 'success', so >=0, matters here */ } @@ -143,15 +147,14 @@ TEH_handler_management_post_extensions ( { MHD_RESULT ret; json_t *extensions; - struct TALER_MasterSignatureP sig = {0}; + struct SetExtensionsContext sec = {0}; struct GNUNET_JSON_Specification top_spec[] = { GNUNET_JSON_spec_json ("extensions", &extensions), GNUNET_JSON_spec_fixed_auto ("extensions_sig", - &sig), + &sec.extensions_sig), GNUNET_JSON_spec_end () }; - struct SetExtensionsContext sec = {0}; /* Parse the top level json structure */ { @@ -193,7 +196,7 @@ TEH_handler_management_post_extensions ( if (GNUNET_OK != TALER_exchange_offline_extension_config_hash_verify ( &h_config, &TEH_master_public_key, - &sig)) + &sec.extensions_sig)) { GNUNET_JSON_parse_free (top_spec); return TALER_MHD_reply_with_error ( @@ -248,7 +251,6 @@ TEH_handler_management_post_extensions ( TALER_EC_GENERIC_PARAMETER_MALFORMED, "invalid configuration for extension"); goto CLEANUP; - } /* We have a validly signed JSON object for the extension. Increment its diff --git a/src/exchangedb/plugin_exchangedb_postgres.c b/src/exchangedb/plugin_exchangedb_postgres.c index 918fc38ca..f9f0ce412 100644 --- a/src/exchangedb/plugin_exchangedb_postgres.c +++ b/src/exchangedb/plugin_exchangedb_postgres.c @@ -11455,6 +11455,7 @@ postgres_get_extension_config (void *cls, GNUNET_PQ_query_param_end }; bool is_null; + *config = NULL; struct GNUNET_PQ_ResultSpec rs[] = { GNUNET_PQ_result_spec_allow_null ( GNUNET_PQ_result_spec_string ("config", config), @@ -11467,10 +11468,6 @@ postgres_get_extension_config (void *cls, "get_extension_config", params, rs); - if (is_null) - { - *config = NULL; - } return qs; } diff --git a/src/include/taler_extensions.h b/src/include/taler_extensions.h index 31e5c6738..7199304d9 100644 --- a/src/include/taler_extensions.h +++ b/src/include/taler_extensions.h @@ -31,26 +31,27 @@ enum TALER_Extension_Type { TALER_Extension_AgeRestriction = 0, - TALER_Extension_Peer2Peer = 1, - TALER_Extension_MaxPredefined = 2 // Must be last + TALER_Extension_MaxPredefined = 1 // Must be last of the predefined }; /* * Represents the implementation of an extension. + * TODO: add documentation */ struct TALER_Extension { enum TALER_Extension_Type type; char *name; bool critical; - bool enabled; + char *version; void *config; + 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, - const json_t *config); - json_t *(*config_to_json)(const struct TALER_Extension *this); + json_t *config); }; /** @@ -132,8 +133,7 @@ TALER_get_age_mask (const struct GNUNET_CONFIGURATION_Handle *cfg, /* - * TALER Peer2Peer Extension - * TODO oec + * TODO: Add Peer2Peer Extension */ #endif