diff options
Diffstat (limited to 'src/exchange')
| -rw-r--r-- | src/exchange/taler-exchange-httpd.c | 56 | ||||
| -rw-r--r-- | src/exchange/taler-exchange-httpd.h | 7 | ||||
| -rw-r--r-- | src/exchange/taler-exchange-httpd_extensions.c | 219 | ||||
| -rw-r--r-- | src/exchange/taler-exchange-httpd_extensions.h | 8 | ||||
| -rw-r--r-- | src/exchange/taler-exchange-httpd_keys.c | 111 | ||||
| -rw-r--r-- | src/exchange/taler-exchange-httpd_management_extensions.c | 377 |
6 files changed, 499 insertions, 279 deletions
diff --git a/src/exchange/taler-exchange-httpd.c b/src/exchange/taler-exchange-httpd.c index b435ee4a..59398c6f 100644 --- a/src/exchange/taler-exchange-httpd.c +++ b/src/exchange/taler-exchange-httpd.c @@ -1,18 +1,18 @@ /* - This file is part of TALER - Copyright (C) 2014-2021 Taler Systems SA + 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 Affero General Public License as published by the Free Software - Foundation; either version 3, or (at your option) any later version. + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU Affero 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 Affero General Public License for more details. + 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 Affero General Public License for more details. - You should have received a copy of the GNU Affero General Public License along with - TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> -*/ + You should have received a copy of the GNU Affero General Public License along with + TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> + */ /** * @file taler-exchange-httpd.c * @brief Serve the HTTP interface of the exchange @@ -148,23 +148,9 @@ int TEH_check_invariants_flag; bool TEH_suicide; /** - * The global manifest with the list supported extensions, sorted by - * TALER_Extension_Type. - **/ -const struct TALER_Extension TEH_extensions[TALER_Extension_Max] = { - [TALER_Extension_Peer2Peer] = { - .type = TALER_Extension_Peer2Peer, - .name = "peer2peer", - .critical = false, - .config = NULL, // disabled per default - }, - [TALER_Extension_AgeRestriction] = { - .type = TALER_Extension_AgeRestriction, - .name = "age_restriction", - .critical = false, - .config = NULL, // disabled per default - }, -}; + * Global register of extensions + */ +struct TALER_Extension **TEH_extensions; /** * Value to return from main() @@ -485,7 +471,7 @@ proceed_with_handler (struct TEH_RequestContext *rc, if (GNUNET_SYSERR == res) { GNUNET_assert (NULL == root); - return MHD_NO; /* bad upload, could not even generate error */ + return MHD_NO; /* bad upload, could not even generate error */ } if ( (GNUNET_NO == res) || (NULL == root) ) @@ -528,8 +514,8 @@ proceed_with_handler (struct TEH_RequestContext *rc, sizeof (emsg), "Got %u/%u segments for %s request ('%s')", (NULL == args[i - 1]) - ? i - 1 - : i + ((NULL != fin) ? 1 : 0), + ? i - 1 + : i + ((NULL != fin) ? 1 : 0), rh->nargs, rh->url, url); @@ -553,7 +539,7 @@ proceed_with_handler (struct TEH_RequestContext *rc, root, args); else /* We also only have "POST" or "GET" in the API for at this point - (OPTIONS/HEAD are taken care of earlier) */ + (OPTIONS/HEAD are taken care of earlier) */ ret = rh->handler.get (rc, args); } @@ -1120,7 +1106,7 @@ handle_mhd_request (void *cls, if (0 == strcasecmp (method, MHD_HTTP_METHOD_HEAD)) - method = MHD_HTTP_METHOD_GET; /* treat HEAD as GET here, MHD will do the rest */ + method = MHD_HTTP_METHOD_GET; /* treat HEAD as GET here, MHD will do the rest */ /* parse first part of URL */ { @@ -1954,8 +1940,8 @@ run (void *cls, MHD_OPTION_CONNECTION_TIMEOUT, connection_timeout, (0 == allow_address_reuse) - ? MHD_OPTION_END - : MHD_OPTION_LISTENING_ADDRESS_REUSE, + ? MHD_OPTION_END + : MHD_OPTION_LISTENING_ADDRESS_REUSE, (unsigned int) allow_address_reuse, MHD_OPTION_END); if (NULL == mhd) diff --git a/src/exchange/taler-exchange-httpd.h b/src/exchange/taler-exchange-httpd.h index fa47af6f..4f04029e 100644 --- a/src/exchange/taler-exchange-httpd.h +++ b/src/exchange/taler-exchange-httpd.h @@ -202,9 +202,12 @@ extern volatile bool MHD_terminating; extern struct GNUNET_CURL_Context *TEH_curl_ctx; /** - * The manifest of the available extensions + * The manifest of the available extensions, NULL terminated */ -extern const struct TALER_Extension TEH_extensions[TALER_Extension_Max]; +extern struct TALER_Extension **TEH_extensions; + +#define TEH_extension_enabled(ext) (0 <= ext && TALER_Extension_Max > 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 98092bd0..8723bebc 100644 --- a/src/exchange/taler-exchange-httpd_extensions.c +++ b/src/exchange/taler-exchange-httpd_extensions.c @@ -24,8 +24,107 @@ #include "taler-exchange-httpd_extensions.h" #include "taler_json_lib.h" #include "taler_mhd_lib.h" +#include "taler_extensions.h" #include <jansson.h> +/** + * @brief implements the TALER_Extension.parse_and_set_config interface. + */ +static enum GNUNET_GenericReturnValue +age_restriction_parse_and_set_config (struct TALER_Extension *this, + const json_t *config) +{ + enum GNUNET_GenericReturnValue ret; + struct TALER_AgeMask mask = {0}; + + 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)); + } + + return GNUNET_OK; +} + + +/** + * @brief implements the TALER_Extension.test_config interface. + */ +static enum GNUNET_GenericReturnValue +age_restriction_test_config (const json_t *config) +{ + return age_restriction_parse_and_set_config (NULL, config); +} + + +/** + * @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; +} + + +/* The extension for age restriction */ +static struct TALER_Extension extension_age_restriction = { + .type = TALER_Extension_AgeRestriction, + .name = "age_restriction", + .critical = false, + .config = NULL, // disabled per default + .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 + */ +static struct TALER_Extension ** +get_known_extensions () +{ + + struct TALER_Extension **list = GNUNET_new_array (TALER_Extension_Max + 1, + struct TALER_Extension *); + list[TALER_Extension_AgeRestriction] = &extension_age_restriction; + list[TALER_Extension_Peer2Peer] = &extension_peer2peer; + list[TALER_Extension_Max] = NULL; + + return list; +} + /** * Handler listening for extensions updates by other exchange @@ -33,7 +132,6 @@ */ static struct GNUNET_DB_EventHandler *extensions_eh; - /** * Function called whenever another exchange process has updated * the extensions data in the database. @@ -48,30 +146,99 @@ extension_update_event_cb (void *cls, size_t extra_size) { (void) cls; - (void) extra; - (void) extra_size; + enum TALER_Extension_Type type; + GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Received /management/extensions update event\n"); + "Received extensions update event\n"); + + if (sizeof(enum TALER_Extension_Type) != extra_size) + { + GNUNET_break (0); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Oops, incorrect size of extra for TALER_Extension_type\n"); + return; + } + + type = *(enum TALER_Extension_Type *) extra; + if (type <0 || type >= TALER_Extension_Max) + { + GNUNET_break (0); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Oops, incorrect type for TALER_Extension_type\n"); + return; + } + + // Get the config from the database as string + { + char *config_str; + 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, + extension->name, + &config_str); + + if (qs < 0) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Couldn't get extension config\n"); + GNUNET_break (0); + return; + } + + // Parse the string as JSON + config = json_loads (config_str, JSON_DECODE_ANY, &err); + if (NULL == config) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Failed to parse config for extension `%s' as JSON: %s (%s)\n", + extension->name, + err.text, + err.source); + GNUNET_break (0); + return; + } + + // Call the parser for the extension + ret = extension->parse_and_set_config (extension, config); + if (GNUNET_OK != ret) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Couldn't parse configuration for extension %s from the database", + extension->name); + GNUNET_break (0); + } + } } enum GNUNET_GenericReturnValue TEH_extensions_init () { - struct GNUNET_DB_EventHeaderP es = { - .size = htons (sizeof (es)), - .type = htons (TALER_DBEVENT_EXCHANGE_EXTENSIONS_UPDATED), - }; - - extensions_eh = TEH_plugin->event_listen (TEH_plugin->cls, - GNUNET_TIME_UNIT_FOREVER_REL, - &es, - &extension_update_event_cb, - NULL); - if (NULL == extensions_eh) + TEH_extensions = get_known_extensions (); + { - GNUNET_break (0); - return GNUNET_SYSERR; + 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; + } } return GNUNET_OK; } @@ -89,22 +256,4 @@ TEH_extensions_done () } -void -TEH_extensions_update_state (void) -{ - /* TODO */ -#if 0 - struct GNUNET_DB_EventHeaderP es = { - .size = htons (sizeof (es)), - .type = htons (TALER_DBEVENT_EXCHANGE_WIRE_UPDATED), - }; - - TEH_plugin->event_notify (TEH_plugin->cls, - &es, - NULL, - 0); -#endif -} - - /* end of taler-exchange-httpd_extensions.c */ diff --git a/src/exchange/taler-exchange-httpd_extensions.h b/src/exchange/taler-exchange-httpd_extensions.h index 3c86e266..4659b653 100644 --- a/src/exchange/taler-exchange-httpd_extensions.h +++ b/src/exchange/taler-exchange-httpd_extensions.h @@ -40,12 +40,4 @@ TEH_extensions_init (void); void TEH_extensions_done (void); -/** - * Something changed in the database. Rebuild the extension state metadata. - * This function should be called if the exchange learns about a new signature - * from our master key. - */ -void -TEH_extensions_update_state (void); - #endif diff --git a/src/exchange/taler-exchange-httpd_keys.c b/src/exchange/taler-exchange-httpd_keys.c index 5d747677..30bbe8eb 100644 --- a/src/exchange/taler-exchange-httpd_keys.c +++ b/src/exchange/taler-exchange-httpd_keys.c @@ -736,10 +736,6 @@ destroy_key_helpers (struct HelperState *hs) * Looks up the AGE_RESTRICTED setting for a denomination in the config and * returns the age restriction (mask) accordingly. * - * FIXME: The mask is currently taken from the config. However, It MUST come - * from the database where it has been persisted after a signed call to the - * /management/extension API (TODO). - * * @param section_name Section in the configuration for the particular * denomination. */ @@ -748,15 +744,13 @@ load_age_mask (const char*section_name) { static const struct TALER_AgeMask null_mask = {0}; struct TALER_AgeMask age_mask = {0}; + const struct TALER_Extension *age_ext = + TEH_extensions[TALER_Extension_AgeRestriction]; - /* FIXME-oec: get age_mask from database, not from config */ - if (TALER_Extension_OK != TALER_get_age_mask (TEH_cfg, &age_mask)) + // Get the age mask from the extension, if configured + if (NULL != age_ext->config) { - GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, - TALER_EXTENSION_SECTION_AGE_RESTRICTION, - "AGE_GROUPS", - "must be of form a:b:...:n:m, where 0<a<b<...<n<m<32\n"); - return null_mask; + age_mask = *(struct TALER_AgeMask *) age_ext->config; } if (age_mask.mask == 0) @@ -1450,7 +1444,6 @@ struct DenomKeyCtx * valid denomination keys? */ struct GNUNET_TIME_Relative min_dk_frequency; - }; @@ -1613,6 +1606,7 @@ setup_general_response_headers (struct TEH_KeyStateHandle *ksh, * @param signkeys list of sign keys to return * @param recoup list of revoked keys to return * @param denoms list of denominations to return + * @param age_restricted_denoms list of age restricted denominations to return, can be NULL * @return #GNUNET_OK on success */ static enum GNUNET_GenericReturnValue @@ -1621,7 +1615,8 @@ create_krd (struct TEH_KeyStateHandle *ksh, struct GNUNET_TIME_Timestamp last_cpd, json_t *signkeys, json_t *recoup, - json_t *denoms) + json_t *denoms, + json_t *age_restricted_denoms) { struct KeysResponseData krd; struct TALER_ExchangePublicKeyP exchange_pub; @@ -1693,6 +1688,8 @@ create_krd (struct TEH_KeyStateHandle *ksh, GNUNET_JSON_pack_data_auto ("eddsa_sig", &exchange_sig)); GNUNET_assert (NULL != keys); + + // Set wallet limit if KYC is configured if ( (TEH_KYC_NONE != TEH_kyc_config.mode) && (GNUNET_OK == TALER_amount_is_valid (&TEH_kyc_config.wallet_balance_limit)) ) @@ -1706,6 +1703,40 @@ 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. + 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 ( + keys, + "age_restricted_denoms", + age_restricted_denoms)); + } + + // TODO: signal support and configuration for the P2P extension, once + // implemented. + { char *keys_json; void *keys_jsonz; @@ -1772,7 +1803,8 @@ finish_keys_response (struct TEH_KeyStateHandle *ksh) { json_t *recoup; struct SignKeyCtx sctx; - json_t *denoms; + json_t *denoms = NULL; + json_t *age_restricted_denoms = NULL; struct GNUNET_TIME_Timestamp last_cpd; struct GNUNET_CONTAINER_Heap *heap; struct GNUNET_HashContext *hash_context; @@ -1802,6 +1834,14 @@ finish_keys_response (struct TEH_KeyStateHandle *ksh) } denoms = json_array (); GNUNET_assert (NULL != denoms); + + // If age restriction is enabled, initialize the array of age restricted denoms. + if (TEH_extension_enabled (TALER_Extension_AgeRestriction)) + { + age_restricted_denoms = json_array (); + GNUNET_assert (NULL != age_restricted_denoms); + } + last_cpd = GNUNET_TIME_UNIT_ZERO_TS; hash_context = GNUNET_CRYPTO_hash_context_start (); { @@ -1826,7 +1866,8 @@ finish_keys_response (struct TEH_KeyStateHandle *ksh) last_cpd, sctx.signkeys, recoup, - denoms)) + denoms, + age_restricted_denoms)) { GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Failed to generate key response data for %s\n", @@ -1837,6 +1878,8 @@ finish_keys_response (struct TEH_KeyStateHandle *ksh) /* intentionally empty */; GNUNET_CONTAINER_heap_destroy (heap); json_decref (denoms); + if (NULL != age_restricted_denoms) + json_decref (age_restricted_denoms); json_decref (sctx.signkeys); json_decref (recoup); return GNUNET_SYSERR; @@ -1846,10 +1889,12 @@ finish_keys_response (struct TEH_KeyStateHandle *ksh) GNUNET_CRYPTO_hash_context_read (hash_context, &dk->h_denom_pub, sizeof (struct GNUNET_HashCode)); - GNUNET_assert ( - 0 == - json_array_append_new ( - denoms, + + { + json_t *denom; + json_t *array; + + denom = GNUNET_JSON_PACK ( GNUNET_JSON_pack_data_auto ("master_sig", &dk->master_sig), @@ -1872,7 +1917,26 @@ finish_keys_response (struct TEH_KeyStateHandle *ksh) TALER_JSON_pack_amount ("fee_refresh", &dk->meta.fee_refresh), TALER_JSON_pack_amount ("fee_refund", - &dk->meta.fee_refund)))); + &dk->meta.fee_refund)); + + /* Put the denom into the correct array - denoms or age_restricted_denoms - + * depending on the settings and the properties of the denomination */ + if (NULL != age_restricted_denoms && + 0 != dk->meta.age_restrictions.mask) + { + array = age_restricted_denoms; + } + else + { + array = denoms; + } + + GNUNET_assert ( + 0 == + json_array_append_new ( + array, + denom)); + } } } GNUNET_CONTAINER_heap_destroy (heap); @@ -1888,12 +1952,15 @@ finish_keys_response (struct TEH_KeyStateHandle *ksh) last_cpd, sctx.signkeys, recoup, - denoms)) + denoms, + age_restricted_denoms)) { GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Failed to generate key response data for %s\n", GNUNET_TIME_timestamp2s (last_cpd)); json_decref (denoms); + if (NULL != age_restricted_denoms) + json_decref (age_restricted_denoms); json_decref (sctx.signkeys); json_decref (recoup); return GNUNET_SYSERR; @@ -1909,6 +1976,8 @@ finish_keys_response (struct TEH_KeyStateHandle *ksh) json_decref (sctx.signkeys); json_decref (recoup); json_decref (denoms); + if (NULL != age_restricted_denoms) + json_decref (age_restricted_denoms); return GNUNET_OK; } diff --git a/src/exchange/taler-exchange-httpd_management_extensions.c b/src/exchange/taler-exchange-httpd_management_extensions.c index 6a771bf4..96b855c3 100644 --- a/src/exchange/taler-exchange-httpd_management_extensions.c +++ b/src/exchange/taler-exchange-httpd_management_extensions.c @@ -29,21 +29,17 @@ #include "taler-exchange-httpd_management.h" #include "taler-exchange-httpd_responses.h" #include "taler_extensions.h" +#include "taler_dbevents.h" +/** + * Extension carries the necessary data for a particular extension. + * + */ struct Extension { enum TALER_Extension_Type type; - json_t *config_json; - - // This union contains the parsed configuration for each extension. - union - { - // configuration for the age restriction - struct TALER_AgeMask mask; - - /* TODO oec - peer2peer config */ - }; + json_t *config; }; /** @@ -56,6 +52,38 @@ struct SetExtensionsContext struct TALER_MasterSignatureP *extensions_sigs; }; + +/** + * @brief verifies the signature a configuration with the offline master key. + * + * @param config configuration of an extension given as JSON object + * @param master_priv offline master public key of the exchange + * @param[out] master_sig signature + * @return GNUNET_OK on success, GNUNET_SYSERR otherwise + */ +static enum GNUNET_GenericReturnValue +config_verify ( + const json_t *config, + const struct TALER_MasterPublicKeyP *master_pub, + const struct TALER_MasterSignatureP *master_sig + ) +{ + enum GNUNET_GenericReturnValue ret; + struct TALER_ExtensionConfigHash h_config; + + ret = TALER_extension_config_hash (config, &h_config); + if (GNUNET_OK != ret) + { + GNUNET_break (0); + return ret; + } + + return TALER_exchange_offline_extension_config_hash_verify (h_config, + master_pub, + master_sig); +} + + /** * Function implementing database transaction to set the configuration of * extensions. It runs the transaction logic. @@ -77,9 +105,68 @@ set_extensions (void *cls, struct MHD_Connection *connection, MHD_RESULT *mhd_ret) { - // struct SetExtensionContext *sec = cls; + struct SetExtensionsContext *sec = cls; + + /* save the configurations of all extensions */ + for (uint32_t i = 0; i<sec->num_extensions; i++) + { + struct Extension *ext = &sec->extensions[i]; + struct TALER_MasterSignatureP *sig = &sec->extensions_sigs[i]; + enum GNUNET_DB_QueryStatus qs; + char *config; + + /* Sanity check. + * TODO: replace with general API to retrieve the extension-handler + */ + if (0 > ext->type || TALER_Extension_Max <= ext->type) + { + GNUNET_break (0); + return GNUNET_DB_STATUS_HARD_ERROR; + } + + config = json_dumps (ext->config, JSON_COMPACT | JSON_SORT_KEYS); + if (NULL == config) + { + GNUNET_break (0); + *mhd_ret = TALER_MHD_reply_with_error (connection, + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_EC_GENERIC_JSON_INVALID, + "convert configuration to string"); + return GNUNET_DB_STATUS_HARD_ERROR; + } + + qs = TEH_plugin->set_extension_config ( + TEH_plugin->cls, + TEH_extensions[ext->type]->name, + config, + sig); + + if (qs < 0) + { + if (GNUNET_DB_STATUS_SOFT_ERROR == qs) + return qs; + GNUNET_break (0); + *mhd_ret = TALER_MHD_reply_with_error (connection, + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_EC_GENERIC_DB_STORE_FAILED, + "save extension configuration"); + } + + /* Success, trigger event */ + { + enum TALER_Extension_Type *type = &sec->extensions[i].type; + struct GNUNET_DB_EventHeaderP ev = { + .size = htons (sizeof (ev)), + .type = htons (TALER_DBEVENT_EXCHANGE_EXTENSIONS_UPDATED) + }; + TEH_plugin->event_notify (TEH_plugin->cls, + &ev, + type, + sizeof(*type)); + } + + } - // TODO oec return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT; /* only 'success', so >=0, matters here */ } @@ -92,50 +179,51 @@ TEH_handler_management_post_extensions ( struct SetExtensionsContext sec = {0}; json_t *extensions; json_t *extensions_sigs; - struct GNUNET_JSON_Specification spec[] = { + struct GNUNET_JSON_Specification top_spec[] = { GNUNET_JSON_spec_json ("extensions", &extensions), GNUNET_JSON_spec_json ("extensions_sigs", &extensions_sigs), GNUNET_JSON_spec_end () }; - bool ok; MHD_RESULT ret; + // Parse the top level json structure { enum GNUNET_GenericReturnValue res; res = TALER_MHD_parse_json_data (connection, root, - spec); + top_spec); if (GNUNET_SYSERR == res) return MHD_NO; /* hard failure */ if (GNUNET_NO == res) return MHD_YES; /* failure */ } + // Ensure we have two arrays of the same size if (! (json_is_array (extensions) && json_is_array (extensions_sigs)) ) { GNUNET_break_op (0); - GNUNET_JSON_parse_free (spec); + GNUNET_JSON_parse_free (top_spec); return TALER_MHD_reply_with_error ( connection, MHD_HTTP_BAD_REQUEST, TALER_EC_GENERIC_PARAMETER_MALFORMED, - "array expected for extensions and extensions_sig"); + "array expected for extensions and extensions_sigs"); } sec.num_extensions = json_array_size (extensions_sigs); if (json_array_size (extensions) != sec.num_extensions) { GNUNET_break_op (0); - GNUNET_JSON_parse_free (spec); + GNUNET_JSON_parse_free (top_spec); return TALER_MHD_reply_with_error ( connection, MHD_HTTP_BAD_REQUEST, TALER_EC_GENERIC_PARAMETER_MALFORMED, - "arrays extensions and extensions_sig are not of equal size"); + "arrays extensions and extensions_sigs are not of the same size"); } GNUNET_log (GNUNET_ERROR_TYPE_INFO, @@ -145,116 +233,59 @@ TEH_handler_management_post_extensions ( struct Extension); sec.extensions_sigs = GNUNET_new_array (sec.num_extensions, struct TALER_MasterSignatureP); - ok = true; + // Now parse individual extensions and signatures from those arrays. for (unsigned int i = 0; i<sec.num_extensions; i++) { - - // 1. parse the extension + // 1. parse the extension out of the json + enum GNUNET_GenericReturnValue res; + const struct TALER_Extension *extension; + const char *name; + struct GNUNET_JSON_Specification ext_spec[] = { + GNUNET_JSON_spec_string ("extension", + &name), + GNUNET_JSON_spec_json ("config", + &sec.extensions[i].config), + GNUNET_JSON_spec_end () + }; + + res = TALER_MHD_parse_json_array (connection, + extensions, + ext_spec, + i, + -1); + if (GNUNET_SYSERR == res) { - enum GNUNET_GenericReturnValue res; - const char *name; - struct GNUNET_JSON_Specification ispec[] = { - GNUNET_JSON_spec_string ("extension", - &name), - GNUNET_JSON_spec_json ("config", - &sec.extensions[i].config_json), - GNUNET_JSON_spec_end () - }; - - res = TALER_MHD_parse_json_array (connection, - extensions, - ispec, - i, - -1); - if (GNUNET_SYSERR == res) - { - ret = MHD_NO; /* hard failure */ - ok = false; - break; - } - if (GNUNET_NO == res) - { - ret = MHD_YES; - ok = false; - break; - } - - // Make sure name refers to a supported extension - { - bool found = false; - for (unsigned int k = 0; k < TALER_Extension_Max; k++) - { - if (0 == strncmp (name, - TEH_extensions[k].name, - strlen (TEH_extensions[k].name))) - { - sec.extensions[i].type = TEH_extensions[k].type; - found = true; - break; - } - } - - if (! found) - { - GNUNET_free (sec.extensions); - GNUNET_free (sec.extensions_sigs); - GNUNET_JSON_parse_free (spec); - GNUNET_JSON_parse_free (ispec); - return TALER_MHD_reply_with_error ( - connection, - MHD_HTTP_BAD_REQUEST, - TALER_EC_GENERIC_PARAMETER_MALFORMED, - "invalid extension type"); - } - } - - // We have a JSON object for the extension. Increment its refcount and - // free the parser. - // TODO: is this correct? - json_incref (sec.extensions[i].config_json); - GNUNET_JSON_parse_free (ispec); + ret = MHD_NO; /* hard failure */ + goto CLEANUP; + } + if (GNUNET_NO == res) + { + ret = MHD_YES; + goto CLEANUP; + } - // Make sure the config is sound - { - switch (sec.extensions[i].type) - { - case TALER_Extension_AgeRestriction: - if (GNUNET_OK != TALER_agemask_parse_json ( - sec.extensions[i].config_json, - &sec.extensions[i].mask)) - { - GNUNET_free (sec.extensions); - GNUNET_free (sec.extensions_sigs); - GNUNET_JSON_parse_free (spec); - return TALER_MHD_reply_with_error ( - connection, - MHD_HTTP_BAD_REQUEST, - TALER_EC_GENERIC_PARAMETER_MALFORMED, - "invalid mask for age restriction"); - } - break; - - case TALER_Extension_Peer2Peer: /* TODO */ - ok = false; - ret = MHD_NO; - goto BREAK; - - default: - /* not reachable */ - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "shouldn't be reached in handler for /management/extensions\n"); - ok = false; - ret = MHD_NO; - goto BREAK; - } - } + /* 2. Make sure name refers to a supported extension */ + if (GNUNET_OK != TALER_extension_get_by_name (name, + (const struct + TALER_Extension **) + TEH_extensions, + &extension)) + { + ret = TALER_MHD_reply_with_error ( + connection, + MHD_HTTP_BAD_REQUEST, + TALER_EC_GENERIC_PARAMETER_MALFORMED, + "invalid extension type"); + goto CLEANUP; } - // 2. parse the signature + sec.extensions[i].type = extension->type; + + /* 3. Extract the signature out of the json array */ { enum GNUNET_GenericReturnValue res; - struct GNUNET_JSON_Specification ispec[] = { + struct GNUNET_JSON_Specification sig_spec[] = { GNUNET_JSON_spec_fixed_auto (NULL, &sec.extensions_sigs[i]), GNUNET_JSON_spec_end () @@ -262,81 +293,61 @@ TEH_handler_management_post_extensions ( res = TALER_MHD_parse_json_array (connection, extensions_sigs, - ispec, + sig_spec, i, -1); if (GNUNET_SYSERR == res) { ret = MHD_NO; /* hard failure */ - ok = false; - break; + goto CLEANUP; } if (GNUNET_NO == res) { ret = MHD_YES; - ok = false; - break; + goto CLEANUP; } } - // 3. verify the signature + /* 4. Verify the signature of the config */ + if (GNUNET_OK != config_verify ( + sec.extensions[i].config, + &TEH_master_public_key, + &sec.extensions_sigs[i])) { - enum GNUNET_GenericReturnValue res; + ret = TALER_MHD_reply_with_error ( + connection, + MHD_HTTP_BAD_REQUEST, + TALER_EC_GENERIC_PARAMETER_MALFORMED, + "invalid signature for extension"); + goto CLEANUP; + } + + /* 5. Make sure the config is sound */ + if (GNUNET_OK != extension->test_config (sec.extensions[i].config)) + { + GNUNET_JSON_parse_free (ext_spec); + ret = TALER_MHD_reply_with_error ( + connection, + MHD_HTTP_BAD_REQUEST, + TALER_EC_GENERIC_PARAMETER_MALFORMED, + "invalid configuration for extension"); + goto CLEANUP; - switch (sec.extensions[i].type) - { - case TALER_Extension_AgeRestriction: - res = TALER_exchange_offline_extension_agemask_verify ( - sec.extensions[i].mask, - &TEH_master_public_key, - &sec.extensions_sigs[i]); - if (GNUNET_OK != res) - { - GNUNET_free (sec.extensions); - GNUNET_free (sec.extensions_sigs); - GNUNET_JSON_parse_free (spec); - return TALER_MHD_reply_with_error ( - connection, - MHD_HTTP_BAD_REQUEST, - TALER_EC_GENERIC_PARAMETER_MALFORMED, - "invalid signature for age mask"); - } - break; - - case TALER_Extension_Peer2Peer: /* TODO */ - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Peer2peer not yet supported in handler for /management/extensions\n"); - ok = false; - ret = MHD_NO; - goto BREAK; - - default: - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "shouldn't be reached in handler for /management/extensions\n"); - ok = false; - ret = MHD_NO; - /* not reachable */ - goto BREAK; - } } - } -BREAK: - if (! ok) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Failure to handle /management/extensions\n"); - GNUNET_free (sec.extensions); - GNUNET_free (sec.extensions_sigs); - GNUNET_JSON_parse_free (spec); - return ret; - } + /* We have a validly signed JSON object for the extension. + * Increment its refcount and free the parser for the extension. + */ + json_incref (sec.extensions[i].config); + GNUNET_JSON_parse_free (ext_spec); + } /* for-loop */ GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Received %u extensions\n", sec.num_extensions); + // now run the transaction to persist the configurations { enum GNUNET_GenericReturnValue res; @@ -347,19 +358,29 @@ BREAK: &set_extensions, &sec); - GNUNET_free (sec.extensions); - GNUNET_free (sec.extensions_sigs); - GNUNET_JSON_parse_free (spec); if (GNUNET_SYSERR == res) - return ret; + goto CLEANUP; } - return TALER_MHD_reply_static ( + ret = TALER_MHD_reply_static ( connection, MHD_HTTP_NO_CONTENT, NULL, NULL, 0); + +CLEANUP: + for (unsigned int i = 0; i < sec.num_extensions; i++) + { + if (NULL != sec.extensions[i].config) + { + json_decref (sec.extensions[i].config); + } + } + GNUNET_free (sec.extensions); + GNUNET_free (sec.extensions_sigs); + GNUNET_JSON_parse_free (top_spec); + return ret; } |
