diff options
Diffstat (limited to 'src/extensions')
| -rw-r--r-- | src/extensions/Makefile.am | 5 | ||||
| -rw-r--r-- | src/extensions/age_restriction/Makefile.am | 33 | ||||
| -rw-r--r-- | src/extensions/age_restriction/extension_age_restriction.c (renamed from src/extensions/extension_age_restriction.c) | 345 | ||||
| -rw-r--r-- | src/extensions/age_restriction_helper.c | 74 | ||||
| -rw-r--r-- | src/extensions/extensions.c | 125 | ||||
| -rw-r--r-- | src/extensions/policy_auction/Makefile.am | 34 | ||||
| -rw-r--r-- | src/extensions/policy_auction/policy_auction.c | 731 | 
7 files changed, 1066 insertions, 281 deletions
diff --git a/src/extensions/Makefile.am b/src/extensions/Makefile.am index 5d4ed128..c867a951 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 00000000..e90c1962 --- /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_age_restriction_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 index 00a03841..697d066f 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'<c) -      return GNUNET_SYSERR; - -    val = 10 * val + c - '0'; - -    if (0>=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 @@ -127,6 +31,12 @@ TALER_age_mask_to_string (   */  /** + * @brief local configuration + */ + +static struct TALER_AgeRestrictionConfig AR_config = {0}; + +/**   * @brief implements the TALER_Extension.disable interface.   *   * @param ext Pointer to the current extension @@ -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,120 @@ 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 */ +  .has_config = true, /* we need to store configuration */ +  .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); -bool -TALER_extensions_age_restriction_is_configured () -{ -  return (0 != TE_age_restriction_config.mask.bits); -} +    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; +  } -struct TALER_AgeMask -TALER_extensions_age_restriction_ageMask () -{ -  return TE_age_restriction_config.mask; -} +  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; +  } -size_t -TALER_extensions_age_restriction_num_groups () -{ -  return TE_age_restriction_config.num_groups; +  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;  } -enum GNUNET_GenericReturnValue -TALER_JSON_parse_age_groups (const json_t *root, -                             struct TALER_AgeMask *mask) +/** + * @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)  { -  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 00000000..2cd77515 --- /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 <http://www.gnu.org/licenses/> + */ +/** + * @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/extensions.c b/src/extensions/extensions.c index 0df0bae3..95fb8cf0 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))) + +  /* 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, -                "Unsupported extension `%s` (section [%s]).\n", name, +                "Couldn't load extension library to `%s` (section [%s]).\n", +                name,                  section);      col->error = GNUNET_SYSERR;      return;    } -  if (GNUNET_OK != -      extension->load_taler_config ( -        (struct TALER_Extension *) extension, -        col->cfg)) + +  if (GNUNET_OK != add_extension (extension))    { +    /* TODO: Ignoring return values here */      GNUNET_log (GNUNET_ERROR_TYPE_ERROR, -                "Couldn't parse configuration for extension `%s` (section [%s]).\n", +                "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/extensions/policy_auction/Makefile.am b/src/extensions/policy_auction/Makefile.am new file mode 100644 index 00000000..cf44b95e --- /dev/null +++ b/src/extensions/policy_auction/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 an extension library + +plugindir = $(libdir)/taler + +plugin_LTLIBRARIES = \ +  libtaler_extension_policy_auction.la + +libtaler_extension_policy_auction_la_LDFLAGS = \ +  -version-info 0:0:0 \ +  -no-undefined + +libtaler_extension_policy_auction_la_SOURCES = \ +  policy_auction.c + +libtaler_extension_policy_auction_la_LIBADD = \ +  $(top_builddir)/src/json/libtalerjson.la \ +  $(top_builddir)/src/util/libtalerutil.la \ +  -lgnunetjson \ +  -lgnunetutil \ +  -ljansson \ +  $(XLIB) diff --git a/src/extensions/policy_auction/policy_auction.c b/src/extensions/policy_auction/policy_auction.c new file mode 100644 index 00000000..d1c3237c --- /dev/null +++ b/src/extensions/policy_auction/policy_auction.c @@ -0,0 +1,731 @@ +/* +   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 policy_auction.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 <microhttpd.h> + +#define POLICY_AUCTION "policy_auction" +#define LOG_PREFIX "[policy_auction] " +#define MAX_RESULT_SIZE 10 * 1024 + +/* Path to the replay program. */ +static char *replay_program; + +/* supported currency */ +static char *currency; + +/* This is basically BRANDT_Result with an extra string field */ +struct result +{ +  uint16_t bidder; +  uint16_t price_idx; +  const char    *price; +}; + +/* + * @brief Transcript information + * + */ +struct transcript +{ +  /* +   * The first couple of fields are from a JSON transcript +   */ + +  /* Public key of seller */ +  struct GNUNET_CRYPTO_EddsaPublicKey seller_pub; + +  /* Payto URL */ +  const char *payto; + +  /* Number of bidders + 1 (for seller) */ +  uint16_t n; + +  /* (n-1) public keys of bidders */ +  struct GNUNET_CRYPTO_EddsaPublicKey *bidder_pub; + +  /* Type of auction, see libbrandt */ +  uint16_t m; + +  /* Auction public outcome? */ +  bool public; + +  /* Start date of the auction */ +  struct GNUNET_TIME_Timestamp time_start; + +  /* End date of the auction */ +  struct GNUNET_TIME_Relative time_round; + +  /* Number of prices */ +  uint16_t k; + +  /* Prices, must be length k */ +  struct TALER_Amount *prices; + +  /* Expected winner(s), maybe NULL */ +  struct result *expected; +  size_t expected_len; + +  /* +   * These are the results from the replay via the external program. +   */ +  struct result *results; +  size_t results_len; +}; + +/** + * @brief returns an JSON with the error + */ +static enum GNUNET_GenericReturnValue +json_error (json_t **output, +            char *error, ...) +{ +  va_list ap; +  int n = 0; +  char buf[4096]; +  GNUNET_assert (error); + +  va_start (ap, error); +  n = vsprintf (buf, error, ap); +  va_end (ap); + +  GNUNET_log (GNUNET_ERROR_TYPE_INFO, +              LOG_PREFIX "got error: %s\n", +              n < 0 ? error: buf); + +  *output = json_pack ("{s:s}", +                       "error", +                       n < 0 ? error : buf); +  GNUNET_assert (*output); + +  return GNUNET_SYSERR; +}; + +/** + * @brief returns an JSON with the result + */ +#if 0 +static enum GNUNET_GenericReturnValue +json_result (json_t **output, +             const struct transcript *tr) +{ +  json_t *results; + +  GNUNET_assert (NULL != tr); + +  *output = json_object (); +  results = json_array (); +  GNUNET_assert (*output); +  GNUNET_assert (results); + +  for (size_t i = 0; i < tr->results_len; i++) +  { +    json_t *result = json_pack ("{s:i, s:s}", +                                "bidder", tr->results[i].bidder, +                                "price", tr->results[i].price); +    GNUNET_assert (result); + +    GNUNET_assert (-1 != +                   json_array_append_new (results, result)); +  } + +  GNUNET_assert (-1 != +                 json_object_set_new (*output, +                                      "winners", +                                      results)); + +  return GNUNET_OK; +} + + +#endif + + +/* + * @brief Parses a given json as transcript. + * + * @param[in] jtr JSON input + * @param[out] tr Parsed transcript data + * @param[out] jerror JSON output for errors + * @return GNUNET_OK on succes + * + * TODO: + *   - parse and verify signatures + */ +static enum GNUNET_GenericReturnValue +parse_transcript (const json_t *jtr, +                  struct transcript *tr, +                  json_t **jerror) +{ +  json_t *auc; + +  // TODO: struct GNUNET_CRYPTO_EddsaSignature sig; + +  GNUNET_assert (jtr); +  GNUNET_assert (tr); + +  // Parse auction +  { +    char *perr; +    unsigned int eline; +    struct GNUNET_JSON_Specification au_spec[] = { +      GNUNET_JSON_spec_bool ("public", &tr->public), +      GNUNET_JSON_spec_uint16 ("type", &tr->m), +      GNUNET_JSON_spec_fixed_auto ("pubkey", &tr->seller_pub), +      GNUNET_JSON_spec_timestamp ("time_start", &tr->time_start), +      GNUNET_JSON_spec_relative_time ("time_round", &tr->time_round), +      GNUNET_JSON_spec_string ("payto", &tr->payto), +      GNUNET_JSON_spec_end () +    }; + +    auc = json_object_get (jtr, "auction"); +    if (NULL == auc) +      return json_error (jerror, +                         "no auction found in transcript"); + +    if (GNUNET_OK != +        GNUNET_JSON_parse (auc, +                           au_spec, +                           (const char **) &perr, +                           &eline)) +      return json_error (jerror, +                         perr); + +    // Prices... +    { +      size_t idx; +      json_t *val; +      json_t *prices; + +      prices = json_object_get (auc, "prices"); +      if (! json_is_array (prices)) +        return json_error (jerror, +                           "no prices found"); + +      tr->k = json_array_size (prices); + +      tr->prices = GNUNET_new_array (tr->k, struct TALER_Amount); +      json_array_foreach (prices, idx, val) +      { +        struct GNUNET_JSON_Specification spec[] = { +          TALER_JSON_spec_amount (NULL, +                                  currency, +                                  &(tr->prices[idx])), +          GNUNET_JSON_spec_end (), +        }; + +        if (GNUNET_OK != +            GNUNET_JSON_parse (val, +                               spec, +                               NULL, +                               NULL)) +          return json_error (jerror, +                             "price no. %ld couldn't be parsed", +                             idx + 1); +      } +    } +  } + +  // Bidders +  { +    size_t idx; +    json_t *val; +    json_t *bidders; + +    bidders = json_object_get (jtr, "bidders"); +    if (! bidders || ! json_is_array (bidders)) +      return json_error (jerror, +                         "no bidders found"); + +    tr->n = json_array_size (bidders); + +    tr->bidder_pub = GNUNET_new_array (tr->n, struct +                                       GNUNET_CRYPTO_EddsaPublicKey); +    json_array_foreach (bidders, idx, val) +    { +      struct GNUNET_JSON_Specification spec[] = { +        GNUNET_JSON_spec_fixed_auto (NULL, +                                     &(tr->bidder_pub[idx])), +        GNUNET_JSON_spec_end (), +      }; +      if (GNUNET_OK != +          GNUNET_JSON_parse (val, +                             spec, +                             NULL, +                             NULL)) +        return json_error (jerror, +                           "bidder no %ld public key couldn't be parsed", +                           idx + 1); +    } +  } + +  // TODO: parse and verify signatures from bidders of the auction + + +  // Messages +  { +    size_t nm; +    json_t *messages = json_object_get (jtr, "transcript"); + +    if (! json_is_array (messages)) +      return json_error (jerror, +                         "no messages found"); + + +    nm = json_array_size (messages); + +    if (nm != (4 * tr->n)) +      return json_error (jerror, +                         "not the right no. of messages found"); + +    /* TODO: parse and evaluate signatures  */ +  } + +  // Winners +  { +    size_t idx; +    json_t *val; +    json_t *winners = json_object_get (jtr, "winners"); + +    if (! json_is_array (winners)) +    { +      GNUNET_log (GNUNET_ERROR_TYPE_WARNING, +                  LOG_PREFIX "winners not provided, continuing without\n"); +      goto DONE; +    } + +    tr->expected_len = json_array_size (winners); +    tr->expected = GNUNET_new_array (tr->expected_len, +                                     struct result); + +    json_array_foreach (winners, idx, val) { +      struct GNUNET_JSON_Specification spec[] = { +        GNUNET_JSON_spec_uint16 ("bidder", +                                 &(tr->expected[idx].bidder)), +        GNUNET_JSON_spec_uint16 ("price_idx", +                                 &(tr->expected[idx].price_idx)), +        GNUNET_JSON_spec_string ("price", +                                 &(tr->expected[idx].price)), +        GNUNET_JSON_spec_end () +      }; + +      if (GNUNET_OK != +          GNUNET_JSON_parse (val, +                             spec, +                             NULL, +                             NULL)) +        return json_error (jerror, +                           "couldn't parse winner no. %ld", +                           idx + 1); +    } +  } + +  // TODO: parse and evalue sig of seller + +// TODO: check for max values + +DONE: + +  *jerror = NULL; +  return GNUNET_OK; +} + + +/** + * @brief replay an auction using the external program + * + * @param[in] root The original JSON transcript + * @param[in] transcript The transcript object parsed so far + * @param[out] result The JSON result from the program + * @return GNUNET_OK on success + * + * TODO: Make this resumable + */ +static enum GNUNET_GenericReturnValue +replay_transcript (const json_t*root, +                   struct transcript *tr, +                   json_t **result) +{ +  struct GNUNET_DISK_PipeHandle *pi; +  struct GNUNET_DISK_PipeHandle *po; +  const struct GNUNET_DISK_FileHandle *fd; +  struct GNUNET_OS_Process *proc; + +  pi = GNUNET_DISK_pipe (GNUNET_DISK_PF_BLOCKING_WRITE); +  po = GNUNET_DISK_pipe (GNUNET_DISK_PF_BLOCKING_READ); +  proc = GNUNET_OS_start_process (GNUNET_OS_INHERIT_STD_ERR, +                                  pi, po, NULL, +                                  replay_program, +                                  replay_program, +                                  NULL); +  if (NULL == proc) +  { +    GNUNET_log (GNUNET_ERROR_TYPE_ERROR, +                LOG_PREFIX "couldn't create auction replay program '%s'\n", +                replay_program); + +    return json_error (result, "internal error"); +  } + +  // Write original transcript JSON to stdin +  { +    ssize_t sz; +    char *str; +    size_t str_len; + + +    fd = GNUNET_DISK_pipe_handle (pi, GNUNET_DISK_PIPE_END_WRITE); +    str = json_dumps (root, JSON_COMPACT); +    str_len = strlen (str); +    sz = GNUNET_DISK_file_write (fd, +                                 str, +                                 str_len); +    free (str); +    if (sz != str_len) +    { +      GNUNET_log (GNUNET_ERROR_TYPE_INFO, +                  LOG_PREFIX "couldn't write all data to replay_program\n"); +    } +  } + +  // Read output from stdout +  { +    ssize_t sz; +    char buf[MAX_RESULT_SIZE]; +    json_error_t error; +    json_t *res; + +    fd = GNUNET_DISK_pipe_handle (po, GNUNET_DISK_PIPE_END_READ); + +    sz = GNUNET_DISK_file_read (fd, +                                buf, +                                sizeof(buf)); +    if (GNUNET_SYSERR == sz) +    { +      GNUNET_log (GNUNET_ERROR_TYPE_INFO, +                  LOG_PREFIX "couldn't read data from replay_program\n"); +      return json_error (result, "internal error"); +    } + +    buf[sz] = 0; +    res = json_loads (buf, +                      JSON_DECODE_ANY, +                      &error); + +    if (! res) +    { +      GNUNET_log (GNUNET_ERROR_TYPE_INFO, +                  LOG_PREFIX +                  "couldn't parse response from replay_program: %s (for '%s')\n", +                  error.text, +                  buf); +      return json_error (result, error.text); +    } + +    // Handle error case first +    { +      json_t *err = json_object_get (res, +                                     "error"); +      if (NULL != err) +      { +        *result = json_copy (res); +        json_decref (res); +        return GNUNET_SYSERR; +      } +    } + +    // Parse the result +    { +      json_t *winners = json_object_get (res, +                                         "winners"); +      if ((NULL == winners) || +          (! json_is_array (winners))) +      { +        GNUNET_log (GNUNET_ERROR_TYPE_ERROR, +                    LOG_PREFIX +                    "replay program didn't return a known result type, instead: '%s'\n", +                    json_dumps (res, JSON_INDENT (2))); +        return json_error (result, "internal error"); +      } + +      { +        // TODO: check each winner with tr->expected, if applicable +        json_object_set (res, "exchange_sig", json_string ( +                           "sig(priv_E, winners)")); + +      } + +      // TODO: return own result object. +      *result = json_copy (res); +      json_decref (res); +    } + +  } + +  if (GNUNET_OK != GNUNET_OS_process_wait (proc)) +  { +    GNUNET_log (GNUNET_ERROR_TYPE_ERROR, +                LOG_PREFIX "error while launching auction replay program '%s'\n", +                replay_program); + +    json_object_clear (*result); +    return json_error (result, "internal error"); +  } + + +  return GNUNET_OK; +} + + +/** + * @brief implements the TALER_Extension.disable interface. + * + * @param ext Pointer to the current extension + */ +static void +auction_disable ( +  struct TALER_Extension *ext) +{ +  /* TODO: cleanup configuration */ +  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) +{ +  /* TODO: add configuration */ +  return GNUNET_JSON_PACK ( +    GNUNET_JSON_pack_bool ("critical", ext->critical), +    GNUNET_JSON_pack_string ("version", ext->version)); +} + + +/** + * @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) +{ +  /* TODO: add configuration */ +  ext->enabled = true; +  return GNUNET_OK; +} + + +/** + * @brief implements the TALER_Extension.http_get_handler + */ +static MHD_RESULT +auction_http_get_handler ( +  struct MHD_Connection *connection, +  const char *const args[]) +{ +  /* TODO: return some meta-data about supported version, limits, etc.*/ + +  GNUNET_log (GNUNET_ERROR_TYPE_WARNING, +              LOG_PREFIX "auction_http_get_handler not implemented yet\n"); + +  return TALER_MHD_reply_with_error (connection, +                                     MHD_HTTP_NOT_IMPLEMENTED, +                                     TALER_EC_EXCHANGE_GENERIC_OPERATION_UNKNOWN, +                                     "auction_http_get_handler not implemented yet\n"); + +} + + +/** + * @brief implements the TALER_Extension.http_post_handler + * + * TODO: make this non-blocking + */ +static MHD_RESULT +auction_http_post_handler ( +  struct MHD_Connection *connection, +  const json_t *root, +  const char *const args[]) +{ +  struct transcript tr = {}; +  enum GNUNET_GenericReturnValue ret; +  json_t *result1; +  json_t *result2; + +  ret = parse_transcript (root, +                          &tr, +                          &result1); +  if (GNUNET_OK != ret) +    return TALER_MHD_reply_json_steal (connection, +                                       result1, +                                       MHD_HTTP_BAD_REQUEST); +  GNUNET_assert (NULL == result1); + +  ret = replay_transcript (root, +                           &tr, +                           &result2); + +  return TALER_MHD_reply_json_steal (connection, +                                     result2, +                                     GNUNET_OK == ret? +                                     MHD_HTTP_OK : +                                     MHD_HTTP_BAD_REQUEST); +} + + +/* The extension struct for auctions of brandt-style */ +struct TALER_Extension TE_auction_brandt = { +  .type = TALER_Extension_PolicyAuction, +  .name = POLICY_AUCTION, +  .critical = false, +  .version = "0", +  .enabled = false, /* disabled per default */ +  .has_config = true, +  .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_get_handler = &auction_http_get_handler, +  .http_post_handler = &auction_http_post_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 * +libtaler_extension_policy_auction_init (void *arg) +{ +  const struct GNUNET_CONFIGURATION_Handle *cfg = arg; + + +  if (GNUNET_OK != +      TALER_config_get_currency (cfg, +                                 ¤cy)) +    return NULL; + +  if (GNUNET_SYSERR == +      GNUNET_CONFIGURATION_get_value_string (cfg, +                                             TALER_EXTENSION_SECTION_PREFIX +                                             POLICY_AUCTION, +                                             "REPLAY_PROGRAM", +                                             &replay_program)) +  { +    GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, +                               TALER_EXTENSION_SECTION_PREFIX POLICY_AUCTION, +                               "REPLAY_PROGRAM"); +    return NULL; +  } + +  /* check if replay_program is actually an executable */ +  { +    struct stat sb; + +    if (0 != stat (replay_program, &sb)) +    { +      GNUNET_log (GNUNET_ERROR_TYPE_ERROR, +                  LOG_PREFIX "replay_program '%s' not found\n", +                  replay_program); +      return NULL; +    } + +    if ( (sb.st_mode & S_IFDIR) || +         ! (sb.st_mode & S_IXUSR)) +    { +      GNUNET_log (GNUNET_ERROR_TYPE_ERROR, +                  LOG_PREFIX "replay_program '%s' is not an executable\n", +                  replay_program); +      return NULL; +    } + +  } + +  GNUNET_log (GNUNET_ERROR_TYPE_INFO, +              LOG_PREFIX "loading... using replay_program '%s'\n", +              replay_program); + +  /* TODO: read other config parameters and generate configuration */ + + +  return &TE_auction_brandt; +} + + +/** + * @brief Tear-down function for the extension. + *        Will be called by GNUNET_PLUGIN_unload. + * + * @param ignored + * @return null + */ +void * +libtaler_extension_policy_auction_done (void *arg) +{ +  auction_disable (&TE_auction_brandt); +  GNUNET_free (replay_program); +  replay_program = NULL; + +  return NULL; +} + + +/* end of policy_auction.c */  | 
