From 04c7e0bb337dd88dde60293d94d2e192a8fc2ff5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=96zg=C3=BCr=20Kesim?= Date: Sun, 2 Oct 2022 18:05:58 +0200 Subject: [PATCH] Refactor extensions - Extensions are now compiled as shared libraries and loaded at runtime according to the TALER configuration. - So far, only age restriction is loaded as extension. - Groundwork for extension auction_brandt started. --- src/Makefile.am | 2 + src/exchange-tools/taler-exchange-offline.c | 89 +++-- src/exchange/taler-exchange-httpd.c | 61 ++- src/exchange/taler-exchange-httpd.h | 9 +- .../taler-exchange-httpd_extensions.c | 59 ++- src/exchange/taler-exchange-httpd_keys.c | 76 ++-- ...ler-exchange-httpd_management_extensions.c | 3 + .../taler-exchange-httpd_refreshes_reveal.c | 9 +- src/extensions/Makefile.am | 5 +- src/extensions/age_restriction/Makefile.am | 33 ++ .../extension_age_restriction.c | 356 ++++++------------ src/extensions/age_restriction_helper.c | 74 ++++ src/extensions/auction_brandt/Makefile.am | 34 ++ .../auction_brandt/extension_auction_brandt.c | 182 +++++++++ src/extensions/extensions.c | 137 ++++--- src/include/taler_extensions.h | 159 +++----- src/include/taler_util.h | 59 +++ src/lib/exchange_api_handle.c | 16 +- src/testing/test_exchange_api.c | 3 - src/testing/test_exchange_p2p.c | 3 - src/testing/testing_api_cmd_batch_withdraw.c | 2 +- src/testing/testing_api_cmd_withdraw.c | 2 +- src/testing/testing_api_helpers_exchange.c | 14 +- src/util/Makefile.am | 1 + src/util/age_restriction.c | 111 ++++++ 25 files changed, 983 insertions(+), 516 deletions(-) create mode 100644 src/extensions/age_restriction/Makefile.am rename src/extensions/{ => age_restriction}/extension_age_restriction.c (53%) create mode 100644 src/extensions/age_restriction_helper.c create mode 100644 src/extensions/auction_brandt/Makefile.am create mode 100644 src/extensions/auction_brandt/extension_auction_brandt.c diff --git a/src/Makefile.am b/src/Makefile.am index 9fa748d7f..827740612 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -32,5 +32,7 @@ SUBDIRS = \ auditor \ lib \ exchange-tools \ + extensions/auction_brandt \ + extensions/age_restriction \ testing \ benchmark diff --git a/src/exchange-tools/taler-exchange-offline.c b/src/exchange-tools/taler-exchange-offline.c index 60a39df96..e7cb94b58 100644 --- a/src/exchange-tools/taler-exchange-offline.c +++ b/src/exchange-tools/taler-exchange-offline.c @@ -137,6 +137,11 @@ static struct GNUNET_CURL_RescheduleContext *rc; */ static const struct GNUNET_CONFIGURATION_Handle *kcfg; +/** + * Age restriction configuration + */ +static struct TALER_AgeRestrictionConfig ar_config = {0}; + /** * Return value from main(). */ @@ -163,11 +168,6 @@ static char *currency; */ static char *CFG_exchange_url; -/** - * If age restriction is enabled, the age mask to be used - */ -static struct TALER_AgeMask age_mask = {0}; - /** * A subcommand supported by this program. */ @@ -2386,6 +2386,7 @@ do_upload (char *const *args) return; } trigger_upload (CFG_exchange_url); + json_decref (out); out = NULL; } @@ -3888,7 +3889,7 @@ load_age_mask (const char*section_name) static const struct TALER_AgeMask null_mask = {0}; enum GNUNET_GenericReturnValue ret; - if (age_mask.bits == 0) + if (! ar_config.enabled) return null_mask; if (GNUNET_OK != (GNUNET_CONFIGURATION_have_value ( @@ -3900,14 +3901,14 @@ load_age_mask (const char*section_name) ret = GNUNET_CONFIGURATION_get_value_yesno (kcfg, section_name, "AGE_RESTRICTED"); - if (GNUNET_YES == ret) - return age_mask; - if (GNUNET_SYSERR == ret) GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, section_name, "AGE_RESTRICTED", "Value must be YES or NO\n"); + if (GNUNET_YES == ret) + return ar_config.mask; + return null_mask; } @@ -4248,7 +4249,7 @@ do_setup (char *const *args) static void do_extensions_show (char *const *args) { - const struct TALER_Extension *it; + const struct TALER_Extensions *it; json_t *exts = json_object (); json_t *obj; @@ -4258,8 +4259,9 @@ do_extensions_show (char *const *args) it = it->next) GNUNET_assert (0 == json_object_set_new (exts, - it->name, - it->config_to_json (it))); + it->extension->name, + it->extension->config_to_json ( + it->extension))); obj = GNUNET_JSON_PACK ( GNUNET_JSON_pack_object_steal ("extensions", exts)); @@ -4268,7 +4270,7 @@ do_extensions_show (char *const *args) json_dumps (obj, JSON_INDENT (2))); json_decref (obj); - next (args + 1); + next (args); } @@ -4281,25 +4283,29 @@ do_extensions_sign (char *const *args) json_t *extensions = json_object (); struct TALER_ExtensionConfigHashP h_config; struct TALER_MasterSignatureP sig; - const struct TALER_Extension *it; + const struct TALER_Extensions *it; + bool found = false; json_t *obj; GNUNET_assert (NULL != extensions); - if (GNUNET_OK != - TALER_extensions_load_taler_config (kcfg)) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "error while loading taler config for extensions\n"); - json_decref (extensions); - return; - } for (it = TALER_extensions_get_head (); NULL != it; it = it->next) + { + const struct TALER_Extension *ext = it->extension; + GNUNET_assert (ext); + + found = true; + GNUNET_assert (0 == json_object_set_new (extensions, - it->name, - it->config_to_json (it))); + ext->name, + ext->config_to_json ( + ext))); + } + + if (! found) + return; if (GNUNET_OK != TALER_JSON_extensions_config_hash (extensions, @@ -4327,9 +4333,10 @@ do_extensions_sign (char *const *args) GNUNET_JSON_pack_data_auto ( "extensions_sig", &sig)); + output_operation (OP_EXTENSIONS, obj); - next (args + 1); + next (args); } @@ -4396,6 +4403,7 @@ do_work_extensions (char *const *args) } }; + if (NULL == args[0]) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, @@ -4530,6 +4538,24 @@ run (void *cls, (void) cls; (void) cfgfile; kcfg = cfg; + + + /* load extensions */ + GNUNET_assert (GNUNET_OK == + TALER_extensions_load (kcfg)); + + /* setup age restriction, if applicable */ + { + const struct TALER_AgeRestrictionConfig *arc; + + if (NULL != + (arc = TALER_extensions_get_age_restriction_config ())) + { + ar_config = *arc; + ar_config.enabled = true; + } + } + if (GNUNET_OK != TALER_config_get_currency (kcfg, ¤cy)) @@ -4538,18 +4564,6 @@ run (void *cls, return; } - /* load age mask, if age restriction is enabled */ - GNUNET_assert (GNUNET_OK == - TALER_extension_age_restriction_register ()); - - if (GNUNET_OK != TALER_extensions_load_taler_config (kcfg)) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "error while loading taler config for extensions\n"); - return; - } - age_mask = TALER_extensions_age_restriction_ageMask (); - ctx = GNUNET_CURL_init (&GNUNET_CURL_gnunet_scheduler_reschedule, &rc); rc = GNUNET_CURL_gnunet_rc_create (ctx); @@ -4579,7 +4593,6 @@ main (int argc, GNUNET_GETOPT_OPTION_END }; enum GNUNET_GenericReturnValue ret; - /* force linker to link against libtalerutil; if we do not do this, the linker may "optimize" libtalerutil away and skip #TALER_OS_init(), which we do need */ diff --git a/src/exchange/taler-exchange-httpd.c b/src/exchange/taler-exchange-httpd.c index 496d3d29f..d7651d792 100644 --- a/src/exchange/taler-exchange-httpd.c +++ b/src/exchange/taler-exchange-httpd.c @@ -97,6 +97,13 @@ static int allow_address_reuse; */ const struct GNUNET_CONFIGURATION_Handle *TEH_cfg; +/** + * Configuration of age restriction + * + * Set after loading the library, enabled in database event handler. + */ +struct TALER_AgeRestrictionConfig TEH_age_restriction_config = {0}; + /** * Handle to the HTTP server. */ @@ -138,11 +145,6 @@ char *TEH_currency; */ char *TEH_base_url; -/** - * Age restriction flags and mask - */ -bool TEH_age_restriction_enabled = true; - /** * Default timeout in seconds for HTTP requests. */ @@ -170,6 +172,7 @@ bool TEH_suicide; * TALER_SIGNATURE_MASTER_EXTENSION. */ struct TALER_MasterSignatureP TEH_extensions_sig; +bool TEH_extensions_signed = false; /** * Value to return from main() @@ -1038,6 +1041,46 @@ handle_post_auditors (struct TEH_RequestContext *rc, } +/** + * Handle POST "/extensions/..." requests. + * + * @param rc request context + * @param root uploaded JSON data + * @param args array of additional options + * @return MHD result code + */ +static MHD_RESULT +handle_post_extensions (struct TEH_RequestContext *rc, + const json_t *root, + const char *const args[]) +{ + const struct TALER_Extension *ext = NULL; + + if (NULL == args[0]) + { + GNUNET_break_op (0); + return r404 (rc->connection, + "/extensions/$EXTENSION"); + } + + ext = TALER_extensions_get_by_name (args[0]); + if (NULL == ext) + { + GNUNET_break_op (0); + return r404 (rc->connection, + "/extensions/$EXTENSION unknown"); + } + + if (NULL == ext->http_post_handler) + return MHD_HTTP_NOT_IMPLEMENTED; + + return ext->http_post_handler ( + rc->connection, + root, + &args[1]); +} + + /** * Handle incoming HTTP request. * @@ -1255,6 +1298,14 @@ handle_mhd_request (void *cls, .nargs = 4, .nargs_is_upper_bound = true }, + /* extensions endpoints */ + { + .url = "extensions", + .method = MHD_HTTP_METHOD_POST, + .handler.post = &handle_post_extensions, + .nargs = 4, /* Arbitrary upper bound */ + .nargs_is_upper_bound = true, + }, /* mark end of list */ { .url = NULL diff --git a/src/exchange/taler-exchange-httpd.h b/src/exchange/taler-exchange-httpd.h index 0fda5ed8d..77aab249f 100644 --- a/src/exchange/taler-exchange-httpd.h +++ b/src/exchange/taler-exchange-httpd.h @@ -197,11 +197,6 @@ extern struct TALER_EXCHANGEDB_Plugin *TEH_plugin; */ extern char *TEH_currency; -/* - * Age restriction extension state - */ -extern bool TEH_age_restriction_enabled; - /** * Our (externally visible) base URL. */ @@ -221,6 +216,7 @@ extern struct GNUNET_CURL_Context *TEH_curl_ctx; * Signature of the offline master key of all enabled extensions' configuration */ extern struct TALER_MasterSignatureP TEH_extensions_sig; +extern bool TEH_extensions_signed; /** * @brief Struct describing an URL and the handler for it. @@ -366,4 +362,7 @@ struct TEH_RequestHandler }; +/* Age restriction configuration */ +extern struct TALER_AgeRestrictionConfig TEH_age_restriction_config; + #endif diff --git a/src/exchange/taler-exchange-httpd_extensions.c b/src/exchange/taler-exchange-httpd_extensions.c index d6c26f6f4..2a99d7a2f 100644 --- a/src/exchange/taler-exchange-httpd_extensions.c +++ b/src/exchange/taler-exchange-httpd_extensions.c @@ -100,6 +100,9 @@ extension_update_event_cb (void *cls, // No config found -> disable extension if (NULL == config_str) { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "No configuration found for extension %s, disabling it\n", + extension->name); extension->disable ((struct TALER_Extension *) extension); return; } @@ -114,28 +117,40 @@ extension_update_event_cb (void *cls, err.text, err.source); GNUNET_break (0); + free(config_str); return; } // Call the parser for the extension ret = extension->load_json_config ( (struct TALER_Extension *) extension, - config); + json_object_get(config, "config")); if (GNUNET_OK != ret) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Couldn't parse configuration for extension %s from the database", - extension->name); + "Couldn't parse configuration for extension %s from the database: %s\n", + extension->name, + config_str); GNUNET_break (0); } + + free(config_str); + json_decref(config); } /* Special case age restriction: Update global flag and mask */ if (TALER_Extension_AgeRestriction == type) { - TEH_age_restriction_enabled = - TALER_extensions_age_restriction_is_enabled (); + const struct TALER_AgeRestrictionConfig *conf = + TALER_extensions_get_age_restriction_config (); + if (NULL != conf) + TEH_age_restriction_config = *conf; + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "[age restriction] DB event has changed the config to %s with mask: %s\n", + conf->enabled ? "enabled": "disabled", + TALER_age_mask_to_string (&conf->mask)); + } } @@ -143,14 +158,21 @@ extension_update_event_cb (void *cls, enum GNUNET_GenericReturnValue TEH_extensions_init () { - GNUNET_assert (GNUNET_OK == - TALER_extension_age_restriction_register ()); - /* Set the event handler for updates */ struct GNUNET_DB_EventHeaderP ev = { .size = htons (sizeof (ev)), .type = htons (TALER_DBEVENT_EXCHANGE_EXTENSIONS_UPDATED), }; + + /* Load the shared libraries first */ + if (GNUNET_OK != + TALER_extensions_load (TEH_cfg)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "failed to load extensions"); + return GNUNET_SYSERR; + } + extensions_eh = TEH_plugin->event_listen (TEH_plugin->cls, GNUNET_TIME_UNIT_FOREVER_REL, &ev, @@ -162,17 +184,20 @@ TEH_extensions_init () return GNUNET_SYSERR; } - /* FIXME #7270: shall we load the extensions from the config right away? - * We do have to for now, as otherwise denominations with age restriction - * will not have the age mask set right upon initial generation. - */ - TALER_extensions_load_taler_config (TEH_cfg); - /* Trigger the initial load of configuration from the db */ - for (const struct TALER_Extension *it = TALER_extensions_get_head (); - NULL != it->next; + for (const struct TALER_Extensions *it = TALER_extensions_get_head (); + NULL != it && NULL != it->extension; it = it->next) - extension_update_event_cb (NULL, &it->type, sizeof(it->type)); + { + const struct TALER_Extension *ext = it->extension; + char *conf = json_dumps (ext->config_to_json (ext), JSON_COMPACT); + + TEH_plugin->set_extension_config (TEH_plugin->cls, + ext->name, + conf); + extension_update_event_cb (NULL, &ext->type, sizeof(ext->type)); + free (conf); + } return GNUNET_OK; } diff --git a/src/exchange/taler-exchange-httpd_keys.c b/src/exchange/taler-exchange-httpd_keys.c index a6ad9976d..4fb8a717f 100644 --- a/src/exchange/taler-exchange-httpd_keys.c +++ b/src/exchange/taler-exchange-httpd_keys.c @@ -815,10 +815,7 @@ static struct TALER_AgeMask load_age_mask (const char*section_name) { static const struct TALER_AgeMask null_mask = {0}; - struct TALER_AgeMask age_mask = TALER_extensions_age_restriction_ageMask (); - - if (age_mask.bits == 0) - return null_mask; + enum GNUNET_GenericReturnValue ret; if (GNUNET_OK != (GNUNET_CONFIGURATION_have_value ( TEH_cfg, @@ -826,22 +823,29 @@ load_age_mask (const char*section_name) "AGE_RESTRICTED"))) return null_mask; + if (GNUNET_SYSERR == + (ret = GNUNET_CONFIGURATION_get_value_yesno (TEH_cfg, + section_name, + "AGE_RESTRICTED"))) { - enum GNUNET_GenericReturnValue ret; - - if (GNUNET_SYSERR == - (ret = GNUNET_CONFIGURATION_get_value_yesno (TEH_cfg, - section_name, - "AGE_RESTRICTED"))) - { - GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, - section_name, - "AGE_RESTRICTED", - "Value must be YES or NO\n"); - return null_mask; - } + GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, + section_name, + "AGE_RESTRICTED", + "Value must be YES or NO\n"); + return null_mask; } - return age_mask; + + if (GNUNET_OK == ret) + { + if (! TEH_age_restriction_config.enabled) + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "age restriction set in section %s, yet, age restriction is not enabled\n", + section_name); + return TEH_age_restriction_config.mask; + } + + + return null_mask; } @@ -1898,34 +1902,29 @@ create_krd (struct TEH_KeyStateHandle *ksh, bool has_extensions = false; /* Fill in the configurations of the enabled extensions */ - for (const struct TALER_Extension *extension = TALER_extensions_get_head (); - NULL != extension; - extension = extension->next) + for (const struct TALER_Extensions *iter = TALER_extensions_get_head (); + NULL != iter && NULL != iter->extension; + iter = iter->next) { + const struct TALER_Extension *extension = iter->extension; json_t *ext; - json_t *config_json; int r; - /* skip if not configured == disabled */ - if (NULL == extension->config || - NULL == extension->config_json) + /* skip if not enabled */ + if (! extension->enabled) continue; /* flag our findings so far */ 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_JSON_pack_object_incref ("config", + extension->config_json) ); GNUNET_assert (NULL != ext); @@ -1948,12 +1947,16 @@ create_krd (struct TEH_KeyStateHandle *ksh, extensions); GNUNET_assert (0 == r); - sig = GNUNET_JSON_PACK ( - GNUNET_JSON_pack_data_auto ("extensions_sig", - &TEH_extensions_sig)); + /* Add the signature of the extensions, if it is not zero */ + if (TEH_extensions_signed) + { + sig = GNUNET_JSON_PACK ( + GNUNET_JSON_pack_data_auto ("extensions_sig", + &TEH_extensions_sig)); - r = json_object_update (keys, sig); - GNUNET_assert (0 == r); + r = json_object_update (keys, sig); + GNUNET_assert (0 == r); + } } else { @@ -2560,6 +2563,7 @@ build_key_state (struct HelperState *hs, true); return NULL; } + /* NOTE: ONLY fetches non-revoked AND master-signed signkeys! */ qs = TEH_plugin->iterate_active_signkeys (TEH_plugin->cls, &signkey_info_cb, diff --git a/src/exchange/taler-exchange-httpd_management_extensions.c b/src/exchange/taler-exchange-httpd_management_extensions.c index a663b1b06..c70ad25e6 100644 --- a/src/exchange/taler-exchange-httpd_management_extensions.c +++ b/src/exchange/taler-exchange-httpd_management_extensions.c @@ -108,6 +108,8 @@ set_extensions (void *cls, taler_ext->name, config); + free (config); + if (qs < 0) { if (GNUNET_DB_STATUS_SOFT_ERROR == qs) @@ -137,6 +139,7 @@ set_extensions (void *cls, /* All extensions configured, update the signature */ TEH_extensions_sig = sec->extensions_sig; + TEH_extensions_signed = true; return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT; /* only 'success', so >=0, matters here */ } diff --git a/src/exchange/taler-exchange-httpd_refreshes_reveal.c b/src/exchange/taler-exchange-httpd_refreshes_reveal.c index a25d6ff43..09ab572b2 100644 --- a/src/exchange/taler-exchange-httpd_refreshes_reveal.c +++ b/src/exchange/taler-exchange-httpd_refreshes_reveal.c @@ -603,7 +603,7 @@ resolve_refreshes_reveal_denominations ( &rrc->coin_envelope_hash); } - if (TEH_age_restriction_enabled && + if (TEH_age_restriction_config.enabled && ((NULL == old_age_commitment_json) != TALER_AgeCommitmentHash_isNullOrZero ( &rctx->melt.session.coin.h_age_commitment))) @@ -614,7 +614,7 @@ resolve_refreshes_reveal_denominations ( /* Reconstruct the old age commitment and verify its hash matches the one * from the melt request */ - if (TEH_age_restriction_enabled && + if (TEH_age_restriction_config.enabled && (NULL != old_age_commitment_json)) { enum GNUNET_GenericReturnValue res; @@ -623,8 +623,7 @@ resolve_refreshes_reveal_denominations ( bool failed = true; /* Has been checked in handle_refreshes_reveal_json() */ - GNUNET_assert (ng == - TALER_extensions_age_restriction_num_groups ()); + GNUNET_assert (ng == TEH_age_restriction_config.num_groups); rctx->old_age_commitment = GNUNET_new (struct TALER_AgeCommitment); oac = rctx->old_age_commitment; @@ -931,7 +930,7 @@ handle_refreshes_reveal_json (struct MHD_Connection *connection, /* Sanity check of age commitment: If it was provided, it _must_ be an array * of the size the # of age groups */ if (NULL != old_age_commitment_json - && TALER_extensions_age_restriction_num_groups () != + && TEH_age_restriction_config.num_groups != json_array_size (old_age_commitment_json)) { GNUNET_break_op (0); diff --git a/src/extensions/Makefile.am b/src/extensions/Makefile.am index 5d4ed128e..c867a9512 100644 --- a/src/extensions/Makefile.am +++ b/src/extensions/Makefile.am @@ -11,7 +11,7 @@ if USE_COVERAGE endif -# Libraries +# Basic extension handling library lib_LTLIBRARIES = \ libtalerextensions.la @@ -22,7 +22,7 @@ libtalerextensions_la_LDFLAGS = \ libtalerextensions_la_SOURCES = \ extensions.c \ - extension_age_restriction.c + age_restriction_helper.c libtalerextensions_la_LIBADD = \ $(top_builddir)/src/json/libtalerjson.la \ @@ -31,3 +31,4 @@ libtalerextensions_la_LIBADD = \ -lgnunetutil \ -ljansson \ $(XLIB) + diff --git a/src/extensions/age_restriction/Makefile.am b/src/extensions/age_restriction/Makefile.am new file mode 100644 index 000000000..c81fd0b95 --- /dev/null +++ b/src/extensions/age_restriction/Makefile.am @@ -0,0 +1,33 @@ +# 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 + +# Age restriction as extension library + +plugindir = $(libdir)/taler + +plugin_LTLIBRARIES = \ + libtaler_extension_age_restriction.la + +libtaler_extension_age_restriction_la_LDFLAGS = \ + -version-info 0:0:0 \ + -no-undefined + +libtaler_extension_age_restriction_la_SOURCES = \ + extension_age_restriction.c + +libtaler_extension_auctionbrandt_la_LIBADD = \ + $(top_builddir)/src/json/libtalerjson.la \ + $(top_builddir)/src/util/libtalerutil.la \ + -lgnunetjson \ + -lgnunetutil \ + -ljansson \ + $(XLIB) diff --git a/src/extensions/extension_age_restriction.c b/src/extensions/age_restriction/extension_age_restriction.c similarity index 53% rename from src/extensions/extension_age_restriction.c rename to src/extensions/age_restriction/extension_age_restriction.c index 00a038415..1399fbc7a 100644 --- a/src/extensions/extension_age_restriction.c +++ b/src/extensions/age_restriction/extension_age_restriction.c @@ -23,102 +23,6 @@ #include "taler_extensions.h" #include "stdint.h" -/** - * Carries all the information we need for age restriction - */ -struct age_restriction_config -{ - struct TALER_AgeMask mask; - size_t num_groups; -}; - -/** - * Global config for this extension - */ -static struct age_restriction_config TE_age_restriction_config = {0}; - -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->bits |= 1 << val; - prev = val; - val = 0; - continue; - } - - if ('0'>c || '9'=val || 32<=val) - return GNUNET_SYSERR; - } - - if (32<=val || prev>=val) - return GNUNET_SYSERR; - - mask->bits |= (1 << val); - mask->bits |= 1; // mark zeroth group, too - - return GNUNET_OK; -} - - -char * -TALER_age_mask_to_string ( - const struct TALER_AgeMask *mask) -{ - uint32_t bits = mask->bits; - unsigned int n = 0; - char *buf = GNUNET_malloc (32 * 3); // max characters possible - char *pos = buf; - - if (NULL == buf) - { - return buf; - } - - while (bits != 0) - { - bits >>= 1; - n++; - if (0 == (bits & 1)) - { - continue; - } - - if (n > 9) - { - *(pos++) = '0' + n / 10; - } - *(pos++) = '0' + n % 10; - - if (0 != (bits >> 1)) - { - *(pos++) = ':'; - } - } - return buf; -} - - /* ================================================== * * Age Restriction TALER_Extension implementation @@ -126,6 +30,12 @@ TALER_age_mask_to_string ( * ================================================== */ +/** + * @brief local configuration + */ + +static struct TALER_AgeRestrictionConfig AR_config = {0}; + /** * @brief implements the TALER_Extension.disable interface. * @@ -138,6 +48,7 @@ age_restriction_disable ( if (NULL == ext) return; + ext->enabled = false; ext->config = NULL; if (NULL != ext->config_json) @@ -146,86 +57,9 @@ age_restriction_disable ( ext->config_json = NULL; } - TE_age_restriction_config.mask.bits = 0; - TE_age_restriction_config.num_groups = 0; -} - - -/** - * @brief implements the TALER_Extension.load_taler_config interface. - * - * @param ext Pointer to the current extension - * @param cfg Handle to the GNUNET configuration - * @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 *ext, - 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 */ - ext->config = NULL; - ext->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.bits = 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.bits = TALER_EXTENSION_AGE_RESTRICTION_DEFAULT_AGE_MASK; - } - - if (GNUNET_OK == ret) - { - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "setting age mask to %x with #groups: %d\n", mask.bits, - __builtin_popcount (mask.bits) - 1); - TE_age_restriction_config.mask.bits = mask.bits; - TE_age_restriction_config.num_groups = __builtin_popcount (mask.bits) - 1; /* no underflow, first bit always set */ - ext->config = &TE_age_restriction_config; - - /* Note: we do now have TE_age_restriction_config set, however - * ext->config_json is NOT set, i.e. the extension is not yet active! For - * age restriction to become active, load_json_config must have been - * called. */ - } - - - GNUNET_free (groups); - return ret; + AR_config.enabled = false; + AR_config.mask.bits = 0; + AR_config.num_groups = 0; } @@ -254,24 +88,25 @@ age_restriction_load_json_config ( if (TALER_Extension_AgeRestriction != ext->type) return GNUNET_SYSERR; - TE_age_restriction_config.mask.bits = mask.bits; - TE_age_restriction_config.num_groups = 0; - if (mask.bits > 0) { /* if the mask is not zero, the first bit MUST be set */ if (0 == (mask.bits & 1)) return GNUNET_SYSERR; - TE_age_restriction_config.num_groups = __builtin_popcount (mask.bits) - 1; + AR_config.mask.bits = mask.bits; + AR_config.num_groups = __builtin_popcount (mask.bits) - 1; } - ext->config = &TE_age_restriction_config; + AR_config.enabled = true; + ext->config = &AR_config; if (NULL != ext->config_json) json_decref (ext->config_json); - ext->config_json = jconfig; + ext->enabled = true; + ext->config_json = json_copy(jconfig); + json_decref(jconfig); GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "loaded new age restriction config with age groups: %s\n", @@ -296,6 +131,13 @@ age_restriction_config_to_json ( GNUNET_assert (NULL != ext); + if (! ext->enabled) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "age restriction not enabled"); + return json_null (); + } + if (NULL == ext->config) { GNUNET_log (GNUNET_ERROR_TYPE_WARNING, @@ -308,11 +150,13 @@ age_restriction_config_to_json ( return json_copy (ext->config_json); } - mask_str = TALER_age_mask_to_string (&TE_age_restriction_config.mask); + mask_str = TALER_age_mask_to_string (&AR_config.mask); conf = GNUNET_JSON_PACK ( GNUNET_JSON_pack_string ("age_groups", mask_str) ); + free(mask_str); + return GNUNET_JSON_PACK ( GNUNET_JSON_pack_bool ("critical", ext->critical), GNUNET_JSON_pack_string ("version", ext->version), @@ -338,71 +182,119 @@ age_restriction_test_json_config ( /* The extension for age restriction */ -struct TALER_Extension TE_age_restriction = { - .next = NULL, +struct TALER_Extension TE_extension_age_restriction = { .type = TALER_Extension_AgeRestriction, .name = "age_restriction", .critical = false, .version = "1", - .config = NULL, // disabled per default + .enabled = false, /* disabled per default */ + .config = NULL, .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, + .http_post_handler = NULL, }; -enum GNUNET_GenericReturnValue -TALER_extension_age_restriction_register () + +/** + * @brief implements the init() function for GNUNET_PLUGIN_load + * + * @param arg Pointer to the GNUNET_CONFIGURATION_Handle + * @return pointer to TALER_Extension on success or NULL otherwise. + */ +void * +libtaler_extension_age_restriction_init (void *arg) { - return TALER_extensions_add (&TE_age_restriction); + const struct GNUNET_CONFIGURATION_Handle *cfg = arg; + char *groups = NULL; + 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"))) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "[age restriction] no section %s found in configuration\n", + TALER_EXTENSION_SECTION_AGE_RESTRICTION); + + return NULL; + } + + /* 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))) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "[age restriction] AGE_GROUPS in %s is not a string\n", + TALER_EXTENSION_SECTION_AGE_RESTRICTION); + + return NULL; + } + + mask.bits = TALER_EXTENSION_AGE_RESTRICTION_DEFAULT_AGE_MASK; + + if ((groups != NULL) && + (GNUNET_OK != TALER_parse_age_group_string (groups, &mask))) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "[age restriction] couldn't parse age groups: '%s'\n", + groups); + return NULL; + } + + AR_config.mask = mask; + AR_config.num_groups = __builtin_popcount (mask.bits) - 1; /* no underflow, first bit always set */ + AR_config.enabled = true; + + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "[age restriction] setting age mask to %s with #groups: %d\n", + TALER_age_mask_to_string(&AR_config.mask), + __builtin_popcount (AR_config.mask.bits) - 1); + + TE_extension_age_restriction.config = &AR_config; + TE_extension_age_restriction.enabled = true; + + /* Note: we do now have TE_age_restriction_config set, however + * ext->config_json is NOT set, i.e. the extension is not yet active! For + * age restriction to become active, load_json_config must have been + * called. */ + + GNUNET_free (groups); + return &TE_extension_age_restriction; } -bool -TALER_extensions_age_restriction_is_configured () +/** + * @brief implements the done() function for GNUNET_PLUGIN_load + * + * @param cfg unsued + * @return pointer to TALER_Extension on success or NULL otherwise. + */ +void * +libtaler_extension_age_restriction_done (void *arg) { - return (0 != TE_age_restriction_config.mask.bits); -} - - -struct TALER_AgeMask -TALER_extensions_age_restriction_ageMask () -{ - return TE_age_restriction_config.mask; -} - - -size_t -TALER_extensions_age_restriction_num_groups () -{ - return TE_age_restriction_config.num_groups; -} - - -enum GNUNET_GenericReturnValue -TALER_JSON_parse_age_groups (const json_t *root, - struct TALER_AgeMask *mask) -{ - enum GNUNET_GenericReturnValue ret; - const char *str; - struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_string ("age_groups", - &str), - GNUNET_JSON_spec_end () - }; - - ret = GNUNET_JSON_parse (root, - spec, - NULL, - NULL); - if (GNUNET_OK == ret) - TALER_parse_age_group_string (str, mask); - - GNUNET_JSON_parse_free (spec); - - return ret; + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "[age restriction] disabling and unloading"); + AR_config.enabled = 0; + AR_config.mask.bits = 0; + AR_config.num_groups = 0; + return NULL; } diff --git a/src/extensions/age_restriction_helper.c b/src/extensions/age_restriction_helper.c new file mode 100644 index 000000000..2cd77515f --- /dev/null +++ b/src/extensions/age_restriction_helper.c @@ -0,0 +1,74 @@ +/* + This file is part of TALER + Copyright (C) 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 + */ +/** + * @file age_restriction_helper.c + * @brief Helper functions for age restriction + * @author Özgür Kesim + */ + +#include "platform.h" +#include "taler_util.h" +#include "taler_signatures.h" +#include "taler_extensions.h" +#include "stdint.h" + + +const struct TALER_AgeRestrictionConfig * +TALER_extensions_get_age_restriction_config () +{ + const struct TALER_Extension *ext; + + ext = TALER_extensions_get_by_type (TALER_Extension_AgeRestriction); + if (NULL == ext) + return NULL; + + return ext->config; +} + + +bool +TALER_extensions_is_age_restriction_enabled () +{ + const struct TALER_Extension *ext; + + ext = TALER_extensions_get_by_type (TALER_Extension_AgeRestriction); + if (NULL == ext) + return false; + + return ext->enabled; +} + + +struct TALER_AgeMask +TALER_extensions_get_age_restriction_mask () +{ + const struct TALER_Extension *ext; + const struct TALER_AgeRestrictionConfig *conf; + + ext = TALER_extensions_get_by_type (TALER_Extension_AgeRestriction); + + if ((NULL == ext) || + (NULL == ext->config) || + (! ext->enabled)) + return (struct TALER_AgeMask) {0} + ; + + conf = ext->config; + return conf->mask; +} + + +/* end age_restriction_helper.c */ diff --git a/src/extensions/auction_brandt/Makefile.am b/src/extensions/auction_brandt/Makefile.am new file mode 100644 index 000000000..f616c1646 --- /dev/null +++ b/src/extensions/auction_brandt/Makefile.am @@ -0,0 +1,34 @@ +# 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 + + +# Auction of Brandt type as extension library + +plugindir = $(libdir)/taler + +plugin_LTLIBRARIES = \ + libtaler_extension_auction_brandt.la + +libtaler_extension_auction_brandt_la_LDFLAGS = \ + -version-info 0:0:0 \ + -no-undefined + +libtaler_extension_auction_brandt_la_SOURCES = \ + extension_auction_brandt.c + +libtaler_extension_auction_brandt_la_LIBADD = \ + $(top_builddir)/src/json/libtalerjson.la \ + $(top_builddir)/src/util/libtalerutil.la \ + -lgnunetjson \ + -lgnunetutil \ + -ljansson \ + $(XLIB) diff --git a/src/extensions/auction_brandt/extension_auction_brandt.c b/src/extensions/auction_brandt/extension_auction_brandt.c new file mode 100644 index 000000000..a6efd94b9 --- /dev/null +++ b/src/extensions/auction_brandt/extension_auction_brandt.c @@ -0,0 +1,182 @@ +/* + 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 + */ +/** + * @file extension_auction_brandt.c + * @brief Extension for replay of auctions of type brandt + * @author Özgür Kesim + */ +#include "platform.h" +#include "taler_util.h" +#include "taler_extensions.h" +#include "../../exchange/taler-exchange-httpd.h" +#include "taler_mhd_lib.h" +#include "stdint.h" +#include + +#define EXTENSION_NAME "auction_brandt" + +/* Path to the replay program. */ +static char *replay_program; + +/** + * @brief implements the TALER_Extension.disable interface. + * + * @param ext Pointer to the current extension + */ +static void +auction_disable ( + struct TALER_Extension *ext) +{ + ext->enabled = false; +} + + +/** + * @brief implements the TALER_Extension.test_json_config interface. + * + * @param config configuration as json_t* to test + * @return #GNUNET_OK on success, #GNUNET_SYSERR otherwise. + */ +static enum GNUNET_GenericReturnValue +auction_test_json_config ( + const json_t *config) +{ + /* This extension has no configuration */ + return GNUNET_OK; +} + + +/** + * @brief implements the TALER_Extension.config_to_json interface. + * + * @param ext if NULL, only tests the configuration + * @return configuration as json_t* object, maybe NULL + */ +static json_t * +auction_config_to_json ( + const struct TALER_Extension *ext) +{ + /* This extension has no configuration */ + return json_null (); +} + + +/** + * @brief implements the TALER_Extension.load_json_config interface. + * + * @param ext if NULL, only tests the configuration + * @param jconfig the configuration as json + */ +static enum GNUNET_GenericReturnValue +auction_load_json_config ( + struct TALER_Extension *ext, + json_t *jconfig) +{ + /* This extension has no configuration */ + ext->enabled = true; + return GNUNET_OK; +} + + +/** + * @brief implements the TALER_Extension.http_post_handler + */ + +static MHD_RESULT +auction_http_post_handler ( + struct MHD_Connection *connection, + const json_t *root, + const char *const args[]) +{ + /* TODO */ + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "auction_http_post_handler not implemented yet"); + return MHD_HTTP_NOT_IMPLEMENTED; +} + + +/* The extension struct for auctions of brandt-style */ +struct TALER_Extension TE_auction_brandt = { + .type = 0x0815, /* TODO: where do we get this from? */ + .name = EXTENSION_NAME, + .critical = false, + .version = "0", + .enabled = false, /* disabled per default */ + .config = NULL, + .config_json = NULL, + .disable = &auction_disable, + .test_json_config = &auction_test_json_config, + .load_json_config = &auction_load_json_config, + .config_to_json = &auction_config_to_json, + .http_post_handler = &auction_http_post_handler, +}; + + +/* TODO: sql handler */ + +/** + * =========================================== + * Handler for GNUNET_PLUGIN_load and _unload + * =========================================== + */ + +/** + * @brief Initialization function for the extension. + * Will be called by GNUNET_PLUGIN_load. + * + * @param arg Configuration - ptr to GNUNET_CONFIGURATION_Handle + * @return Pointer to TE_auction_brandt + */ +struct TALER_Extension * +init (void *arg) +{ + const struct GNUNET_CONFIGURATION_Handle *cfg = arg; + + if (GNUNET_SYSERR == + GNUNET_CONFIGURATION_get_value_string (cfg, + "extension_" EXTENSION_NAME, + "replay_program", + &replay_program)) + { + GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, + "extension_" EXTENSION_NAME, + "replay_program"); + return NULL; + } + + return &TE_auction_brandt; +} + +/** + * @brief Tear-down function for the extension. + * Will be called by GNUNET_PLUGIN_unload. + * + * @param ignored + * @return null + */ +void * +done (void *arg) +{ + auction_disable(&TE_auction_brandt); + GNUNET_free(replay_program); + replay_program=NULL; + + return NULL; +} + + + +/* end of extension_auction_brandt.c */ diff --git a/src/extensions/extensions.c b/src/extensions/extensions.c index 0df0bae36..95fb8cf00 100644 --- a/src/extensions/extensions.c +++ b/src/extensions/extensions.c @@ -24,21 +24,22 @@ #include "taler_extensions.h" #include "stdint.h" - /* head of the list of all registered extensions */ -static struct TALER_Extension *TE_extensions = NULL; +static struct TALER_Extensions TE_extensions = { + .next = NULL, + .extension = NULL, +}; - -const struct TALER_Extension * +const struct TALER_Extensions * TALER_extensions_get_head () { - return TE_extensions; + return &TE_extensions; } -enum GNUNET_GenericReturnValue -TALER_extensions_add ( - struct TALER_Extension *extension) +static enum GNUNET_GenericReturnValue +add_extension ( + const struct TALER_Extension *extension) { /* Sanity checks */ if ((NULL == extension) || @@ -47,28 +48,30 @@ TALER_extensions_add ( (NULL == extension->disable) || (NULL == extension->test_json_config) || (NULL == extension->load_json_config) || - (NULL == extension->config_to_json) || - (NULL == extension->load_taler_config)) + (NULL == extension->config_to_json)) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "invalid extension\n"); return GNUNET_SYSERR; } - if (NULL == TE_extensions) /* first extension ?*/ - TE_extensions = (struct TALER_Extension *) extension; + if (NULL == TE_extensions.extension) /* first extension ?*/ + TE_extensions.extension = extension; else { - struct TALER_Extension *iter; - struct TALER_Extension *last; + struct TALER_Extensions *iter; + struct TALER_Extensions *last; /* Check for collisions */ - for (iter = TE_extensions; NULL != iter; iter = iter->next) + for (iter = &TE_extensions; + NULL != iter && NULL != iter->extension; + iter = iter->next) { + const struct TALER_Extension *ext = iter->extension; last = iter; - if (extension->type == iter->type || + if (extension->type == ext->type || 0 == strcasecmp (extension->name, - iter->name)) + ext->name)) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "extension collision for `%s'\n", @@ -78,7 +81,11 @@ TALER_extensions_add ( } /* No collisions found, so add this extension to the list */ - last->next = extension; + { + struct TALER_Extensions *extn = GNUNET_new (struct TALER_Extensions); + extn->extension = extension; + last->next = extn; + } } return GNUNET_OK; @@ -89,12 +96,12 @@ const struct TALER_Extension * TALER_extensions_get_by_type ( enum TALER_Extension_Type type) { - for (const struct TALER_Extension *it = TE_extensions; - NULL != it; + for (const struct TALER_Extensions *it = &TE_extensions; + NULL != it && NULL != it->extension; it = it->next) { - if (it->type == type) - return it; + if (it->extension->type == type) + return it->extension; } /* No extension found. */ @@ -109,8 +116,7 @@ TALER_extensions_is_enabled_type ( const struct TALER_Extension *ext = TALER_extensions_get_by_type (type); - return (NULL != ext && - TALER_extensions_is_enabled (ext)); + return (NULL != ext && ext->enabled); } @@ -118,14 +124,15 @@ const struct TALER_Extension * TALER_extensions_get_by_name ( const char *name) { - for (const struct TALER_Extension *it = TE_extensions; + for (const struct TALER_Extensions *it = &TE_extensions; NULL != it; it = it->next) { - if (0 == strcasecmp (name, it->name)) - return it; + if (0 == strcasecmp (name, it->extension->name)) + return it->extension; } - /* No extension found. */ + /* No extension found, try to load it. */ + return NULL; } @@ -178,7 +185,8 @@ configure_extension ( { struct LoadConfClosure *col = cls; const char *name; - const struct TALER_Extension *extension; + char *lib_name; + struct TALER_Extension *extension; if (GNUNET_OK != col->error) return; @@ -190,33 +198,49 @@ configure_extension ( 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)) + /* Load the extension library */ + GNUNET_asprintf (&lib_name, + "libtaler_extension_%s", + name); + extension = GNUNET_PLUGIN_load ( + lib_name, + (void *) col->cfg); + if (NULL == extension) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Couldn't parse configuration for extension `%s` (section [%s]).\n", + "Couldn't load extension library to `%s` (section [%s]).\n", name, section); col->error = GNUNET_SYSERR; return; } + + + if (GNUNET_OK != add_extension (extension)) + { + /* TODO: Ignoring return values here */ + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Couldn't add extension `%s` (section [%s]).\n", + name, + section); + col->error = GNUNET_SYSERR; + GNUNET_PLUGIN_unload ( + lib_name, + (void *) col->cfg); + return; + } + + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "extension library '%s' loaded\n", + lib_name); } +static bool extensions_loaded = false; + enum GNUNET_GenericReturnValue -TALER_extensions_load_taler_config ( +TALER_extensions_load ( const struct GNUNET_CONFIGURATION_Handle *cfg) { struct LoadConfClosure col = { @@ -224,9 +248,16 @@ TALER_extensions_load_taler_config ( .error = GNUNET_OK, }; + if (extensions_loaded) + return GNUNET_OK; + GNUNET_CONFIGURATION_iterate_sections (cfg, &configure_extension, &col); + + if (GNUNET_OK == col.error) + extensions_loaded = true; + return col.error; } @@ -309,28 +340,16 @@ TALER_extensions_load_json_config ( } /* make sure to disable all extensions that weren't mentioned in the json */ - for (const struct TALER_Extension *it = TALER_extensions_get_head (); + for (const struct TALER_Extensions *it = TALER_extensions_get_head (); NULL != it; it = it->next) { - if (NULL == json_object_get (extensions, it->name)) - it->disable ((struct TALER_Extension *) it); + if (NULL == json_object_get (extensions, it->extension->name)) + it->extension->disable ((struct TALER_Extension *) it); } return GNUNET_OK; } -bool -TALER_extensions_age_restriction_is_enabled () -{ - const struct TALER_Extension *age = - TALER_extensions_get_by_type (TALER_Extension_AgeRestriction); - - return (NULL != age && - NULL != age->config_json && - TALER_extensions_age_restriction_is_configured ()); -} - - /* end of extensions.c */ diff --git a/src/include/taler_extensions.h b/src/include/taler_extensions.h index 8e1823cce..40b14402f 100644 --- a/src/include/taler_extensions.h +++ b/src/include/taler_extensions.h @@ -24,6 +24,7 @@ #include #include "taler_crypto_lib.h" #include "taler_json_lib.h" +#include "taler_mhd_lib.h" #define TALER_EXTENSION_SECTION_PREFIX "exchange-extension-" @@ -34,6 +35,17 @@ enum TALER_Extension_Type TALER_Extension_MaxPredefined = 1 // Must be last of the predefined }; + +/* + * @brief simply linked list of extensions + */ + +struct TALER_Extensions +{ + struct TALER_Extensions *next; + const struct TALER_Extension *extension; +}; + /* * @brief Represents the implementation of an extension. * @@ -41,14 +53,12 @@ enum TALER_Extension_Type */ struct TALER_Extension { - /* simple linked list */ - struct TALER_Extension *next; - enum TALER_Extension_Type type; char *name; bool critical; char *version; void *config; + bool enabled; json_t *config_json; void (*disable)(struct TALER_Extension *ext); @@ -63,9 +73,11 @@ struct TALER_Extension json_t *(*config_to_json)( const struct TALER_Extension *ext); - enum GNUNET_GenericReturnValue (*load_taler_config)( - struct TALER_Extension *ext, - const struct GNUNET_CONFIGURATION_Handle *cfg); + MHD_RESULT (*http_post_handler)( + struct MHD_Connection *connection, + const json_t *root, + const char *const args[]); + }; /** @@ -73,15 +85,15 @@ struct TALER_Extension */ /* - * @brief Sets the configuration of the extensions from the given TALER - * configuration. + * @brief Loads the extensions as shared libraries, as specified in 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 ( +TALER_extensions_load ( const struct GNUNET_CONFIGURATION_Handle *cfg); /* @@ -118,25 +130,13 @@ TALER_extensions_load_json_config ( /* * @brief Returns the head of the linked list of extensions. */ -const struct TALER_Extension * +const struct TALER_Extensions * TALER_extensions_get_head (); -/* - * @brief 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 ( - struct TALER_Extension *new_extension); - /** * @brief Finds and returns a supported extension by a given type. * - * @param type type of the extension to lookup + * @param type of the extension to lookup * @return extension found, or NULL (should not happen!) */ const struct TALER_Extension * @@ -154,8 +154,6 @@ const struct TALER_Extension * TALER_extensions_get_by_name ( const char *name); -#define TALER_extensions_is_enabled(ext) (NULL != (ext)->config) - /** * @brief Check if a given type of an extension is enabled * @@ -166,6 +164,15 @@ bool TALER_extensions_is_enabled_type ( enum TALER_Extension_Type type); +/** + * @brief Check if an extension is enabled + * + * @param extension The extension handler. + * @return true enabled, false if not enabled, will assert if type is not found. + */ +bool +TALER_extensions_is_enabled ( + const struct TALER_Extension *extension); /* * Verify the signature of a given JSON object for extensions with the master @@ -189,6 +196,9 @@ TALER_extensions_verify_json_config_signature ( /* * TALER Age Restriction Extension + * + * This extension is special insofar as it directly interacts with coins and + * denominations. */ #define TALER_EXTENSION_SECTION_AGE_RESTRICTION (TALER_EXTENSION_SECTION_PREFIX \ @@ -204,98 +214,41 @@ TALER_extensions_verify_json_config_signature ( | 1 << 21) #define TALER_EXTENSION_AGE_RESTRICTION_DEFAULT_AGE_GROUPS "8:10:12:14:16:18:21" -/** - * @brief Registers the extension for age restriction to the list extensions + +/* + * @brief Configuration for Age Restriction */ -enum GNUNET_GenericReturnValue -TALER_extension_age_restriction_register (); +struct TALER_AgeRestrictionConfig +{ + bool enabled; + struct TALER_AgeMask mask; + uint8_t num_groups; +}; + /** - * @brief Parses a string as a list of age groups. + * @brief Retrieve the age restriction configuration * - * The string must consist of a colon-separated list of increasing integers - * between 0 and 31. Each entry represents the beginning of a new age group. - * F.e. the string - * - * "8:10:12:14:16:18:21" - * - * represents the following list of eight age groups: - * - * | Group | Ages | - * | -----:|:------------- | - * | 0 | 0, 1, ..., 7 | - * | 1 | 8, 9 | - * | 2 | 10, 11 | - * | 3 | 12, 13 | - * | 4 | 14, 15 | - * | 5 | 16, 17 | - * | 6 | 18, 19, 20 | - * | 7 | 21, ... | - * - * which is then encoded as a bit mask with the corresponding bits set: - * - * 31 24 16 8 0 - * | | | | | - * oooooooo oo1oo1o1 o1o1o1o1 ooooooo1 - * - * @param groups String representation of age groups - * @param[out] mask Mask representation for age restriction. - * @return Error, if age groups were invalid, OK otherwise. + * @return age restriction configuration if present, otherwise NULL. */ -enum GNUNET_GenericReturnValue -TALER_parse_age_group_string ( - const char *groups, - struct TALER_AgeMask *mask); +const struct TALER_AgeRestrictionConfig * +TALER_extensions_get_age_restriction_config (); /** - * @brief Encodes the age mask into a string, like "8:10:12:14:16:18:21" + * @brief Check if age restriction is enabled * - * @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 *mask); - -/** - * @brief Returns true when age restriction is configured and enabled. + * @return true, if age restriction is loaded, configured and enabled; otherwise false. */ bool -TALER_extensions_age_restriction_is_enabled (); +TALER_extensions_is_age_restriction_enabled (); /** - * @brief Returns true when age restriction is configured (might not be - * _enabled_, though). - */ -bool -TALER_extensions_age_restriction_is_configured (); - -/** - * @brief Returns the currently set age mask. Note that even if age - * restriction is not enabled, the age mask might be have a non-zero value. + * @brief Return the age mask for age restriction + * + * @return configured age mask, if age restriction is loaded, configured and enabled; otherwise zero mask. */ struct TALER_AgeMask -TALER_extensions_age_restriction_ageMask (); - - -/** - * @brief Returns the amount of age groups defined. 0 means no age restriction - * enabled. - */ -size_t -TALER_extensions_age_restriction_num_groups (); - -/** - * @brief Parses a JSON object { "age_groups": "a:b:...y:z" }. - * - * @param root is the json object - * @param[out] mask on success, will contain the age mask - * @return #GNUNET_OK on success and #GNUNET_SYSERR on failure. - */ -enum GNUNET_GenericReturnValue -TALER_JSON_parse_age_groups (const json_t *root, - struct TALER_AgeMask *mask); +TALER_extensions_get_age_restriction_mask (); /* diff --git a/src/include/taler_util.h b/src/include/taler_util.h index 079f72ed6..e821fc195 100644 --- a/src/include/taler_util.h +++ b/src/include/taler_util.h @@ -492,5 +492,64 @@ char *strchrnul (const char *s, int c); #endif +/** + * @brief Parses a string as a list of age groups. + * + * The string must consist of a colon-separated list of increasing integers + * between 0 and 31. Each entry represents the beginning of a new age group. + * F.e. the string + * + * "8:10:12:14:16:18:21" + * + * represents the following list of eight age groups: + * + * | Group | Ages | + * | -----:|:------------- | + * | 0 | 0, 1, ..., 7 | + * | 1 | 8, 9 | + * | 2 | 10, 11 | + * | 3 | 12, 13 | + * | 4 | 14, 15 | + * | 5 | 16, 17 | + * | 6 | 18, 19, 20 | + * | 7 | 21, ... | + * + * which is then encoded as a bit mask with the corresponding bits set: + * + * 31 24 16 8 0 + * | | | | | + * oooooooo oo1oo1o1 o1o1o1o1 ooooooo1 + * + * @param groups String representation of age groups + * @param[out] mask Mask representation for age restriction. + * @return Error, if age groups were invalid, OK otherwise. + */ +enum GNUNET_GenericReturnValue +TALER_parse_age_group_string ( + const char *groups, + struct TALER_AgeMask *mask); + +/** + * @brief 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 *mask); + +/** + * @brief Parses a JSON object { "age_groups": "a:b:...y:z" }. + * + * @param root is the json object + * @param[out] mask on success, will contain the age mask + * @return #GNUNET_OK on success and #GNUNET_SYSERR on failure. + */ +enum GNUNET_GenericReturnValue +TALER_JSON_parse_age_groups (const json_t *root, + struct TALER_AgeMask *mask); + #endif diff --git a/src/lib/exchange_api_handle.c b/src/lib/exchange_api_handle.c index 707e5cf5b..add37a80f 100644 --- a/src/lib/exchange_api_handle.c +++ b/src/lib/exchange_api_handle.c @@ -902,16 +902,19 @@ decode_keys_json (const json_t *resp_obj, { struct TALER_MasterSignatureP extensions_sig = {0}; json_t *extensions = NULL; + bool no_extensions = false; + bool no_signature = false; + struct GNUNET_JSON_Specification ext_spec[] = { GNUNET_JSON_spec_mark_optional ( GNUNET_JSON_spec_json ("extensions", &extensions), - NULL), + &no_extensions), GNUNET_JSON_spec_mark_optional ( GNUNET_JSON_spec_fixed_auto ( "extensions_sig", &extensions_sig), - NULL), + &no_signature), GNUNET_JSON_spec_end () }; @@ -921,7 +924,12 @@ decode_keys_json (const json_t *resp_obj, ext_spec, NULL, NULL)); - if (NULL != extensions) + + if (! no_extensions && no_signature) + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "found extensions without signature\n"); + + if (! no_extensions && ! no_signature) { /* 2. We have an extensions object. Verify its signature. */ EXITIF (GNUNET_OK != @@ -936,7 +944,7 @@ decode_keys_json (const json_t *resp_obj, } /* 4. assuming we might have now a new value for age_mask, set it in key_data */ - key_data->age_mask = TALER_extensions_age_restriction_ageMask (); + key_data->age_mask = TALER_extensions_get_age_restriction_mask (); } /** diff --git a/src/testing/test_exchange_api.c b/src/testing/test_exchange_api.c index f30701c34..2dfda067b 100644 --- a/src/testing/test_exchange_api.c +++ b/src/testing/test_exchange_api.c @@ -1315,9 +1315,6 @@ main (int argc, "INFO", NULL); - GNUNET_assert (GNUNET_OK == - TALER_extension_age_restriction_register ()); - cipher = GNUNET_TESTING_get_testname_from_underscore (argv[0]); GNUNET_assert (NULL != cipher); uses_cs = (0 == strcmp (cipher, "cs")); diff --git a/src/testing/test_exchange_p2p.c b/src/testing/test_exchange_p2p.c index 726dc3157..da723ba7b 100644 --- a/src/testing/test_exchange_p2p.c +++ b/src/testing/test_exchange_p2p.c @@ -417,9 +417,6 @@ main (int argc, "INFO", NULL); - GNUNET_assert (GNUNET_OK == - TALER_extension_age_restriction_register ()); - cipher = GNUNET_TESTING_get_testname_from_underscore (argv[0]); GNUNET_assert (NULL != cipher); uses_cs = (0 == strcmp (cipher, "cs")); diff --git a/src/testing/testing_api_cmd_batch_withdraw.c b/src/testing/testing_api_cmd_batch_withdraw.c index b68ca99f7..3b32b5ada 100644 --- a/src/testing/testing_api_cmd_batch_withdraw.c +++ b/src/testing/testing_api_cmd_batch_withdraw.c @@ -490,7 +490,7 @@ TALER_TESTING_cmd_batch_withdraw (const char *label, acp = GNUNET_new (struct TALER_AgeCommitmentProof); hac = GNUNET_new (struct TALER_AgeCommitmentHash); - mask = TALER_extensions_age_restriction_ageMask (); + mask = TALER_extensions_get_age_restriction_mask(); GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK, &seed, sizeof(seed)); diff --git a/src/testing/testing_api_cmd_withdraw.c b/src/testing/testing_api_cmd_withdraw.c index ce2c49ccc..3e2f45c4b 100644 --- a/src/testing/testing_api_cmd_withdraw.c +++ b/src/testing/testing_api_cmd_withdraw.c @@ -590,7 +590,7 @@ TALER_TESTING_cmd_withdraw_amount (const char *label, acp = GNUNET_new (struct TALER_AgeCommitmentProof); hac = GNUNET_new (struct TALER_AgeCommitmentHash); - mask = TALER_extensions_age_restriction_ageMask (); + mask = TALER_extensions_get_age_restriction_mask(); GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK, &seed, sizeof(seed)); diff --git a/src/testing/testing_api_helpers_exchange.c b/src/testing/testing_api_helpers_exchange.c index d813021bd..f31ef2741 100644 --- a/src/testing/testing_api_helpers_exchange.c +++ b/src/testing/testing_api_helpers_exchange.c @@ -314,8 +314,13 @@ sign_keys_for_exchange (void *cls, char *exchange_master_pub; int ret; - /* Load the age restriction mask from the configuration */ - TALER_extensions_load_taler_config (cfg); + /* Load the extensions */ + if (GNUNET_OK != TALER_extensions_load (cfg)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "couldn't load extensions"); + return GNUNET_SYSERR; + } if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (cfg, @@ -396,6 +401,7 @@ TALER_TESTING_prepare_exchange (const char *config_filename, TALER_TESTING_auditor_db_reset (config_filename)) return GNUNET_NO; } + if (GNUNET_OK != GNUNET_CONFIGURATION_parse_and_run (config_filename, &sign_keys_for_exchange, @@ -709,6 +715,10 @@ TALER_TESTING_setup_with_exchange_cfg ( char *base_url; int result; + /* Load extensions */ + if (GNUNET_OK != TALER_extensions_load (cfg)) + return GNUNET_SYSERR; + if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (cfg, "exchange", diff --git a/src/util/Makefile.am b/src/util/Makefile.am index 94edac021..acafdae57 100644 --- a/src/util/Makefile.am +++ b/src/util/Makefile.am @@ -105,6 +105,7 @@ libtalerutil_la_SOURCES = \ libtalerutil_la_LIBADD = \ -lgnunetutil \ + -lgnunetjson \ -lsodium \ -ljansson \ $(LIBGCRYPT_LIBS) \ diff --git a/src/util/age_restriction.c b/src/util/age_restriction.c index b87c8543e..f4ac9abea 100644 --- a/src/util/age_restriction.c +++ b/src/util/age_restriction.c @@ -21,6 +21,7 @@ #include "platform.h" #include "taler_util.h" #include "taler_signatures.h" +#include #include void @@ -436,3 +437,113 @@ TALER_age_commitment_proof_free ( cp->commitment.keys = NULL; } } + + +enum GNUNET_GenericReturnValue +TALER_JSON_parse_age_groups (const json_t *root, + struct TALER_AgeMask *mask) +{ + enum GNUNET_GenericReturnValue ret; + const char *str; + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_string ("age_groups", + &str), + GNUNET_JSON_spec_end () + }; + + ret = GNUNET_JSON_parse (root, + spec, + NULL, + NULL); + if (GNUNET_OK == ret) + TALER_parse_age_group_string (str, mask); + + GNUNET_JSON_parse_free (spec); + + return ret; +} + + +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->bits |= 1 << val; + prev = val; + val = 0; + continue; + } + + if ('0'>c || '9'=val || 32<=val) + return GNUNET_SYSERR; + } + + if (32<=val || prev>=val) + return GNUNET_SYSERR; + + mask->bits |= (1 << val); + mask->bits |= 1; // mark zeroth group, too + + return GNUNET_OK; +} + + +char * +TALER_age_mask_to_string ( + const struct TALER_AgeMask *mask) +{ + uint32_t bits = mask->bits; + unsigned int n = 0; + char *buf = GNUNET_malloc (32 * 3); // max characters possible + char *pos = buf; + + if (NULL == buf) + { + return buf; + } + + while (bits != 0) + { + bits >>= 1; + n++; + if (0 == (bits & 1)) + { + continue; + } + + if (n > 9) + { + *(pos++) = '0' + n / 10; + } + *(pos++) = '0' + n % 10; + + if (0 != (bits >> 1)) + { + *(pos++) = ':'; + } + } + return buf; +} + + +/* end util/age_restriction.c */