summaryrefslogtreecommitdiff
path: root/src/extensions/extensions.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/extensions/extensions.c')
-rw-r--r--src/extensions/extensions.c333
1 files changed, 333 insertions, 0 deletions
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 */