aboutsummaryrefslogtreecommitdiff
path: root/src/exchange/taler-exchange-httpd_management_extensions.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/exchange/taler-exchange-httpd_management_extensions.c')
-rw-r--r--src/exchange/taler-exchange-httpd_management_extensions.c377
1 files changed, 199 insertions, 178 deletions
diff --git a/src/exchange/taler-exchange-httpd_management_extensions.c b/src/exchange/taler-exchange-httpd_management_extensions.c
index 6a771bf43..96b855c3c 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;
}