diff options
| author | Özgür Kesim <oec-taler@kesim.org> | 2021-12-27 23:24:48 +0100 | 
|---|---|---|
| committer | Özgür Kesim <oec-taler@kesim.org> | 2021-12-27 23:24:48 +0100 | 
| commit | ef4238874f6628a9ee4464ad3b70a7fde96d518b (patch) | |
| tree | 27ba7f32c4d32bce4c821ba6c6a2ed8791d2c30b /src/exchange | |
| parent | 070f442a1182c7c2a09c42e94ce202509ade1b77 (diff) | |
[age restriction] progress 9/n
More worke towards support for extensions and age restriction:
- taler-exchange-httpd_management_extensions.c almost completed
  - handling of request implemented
  - stub "set_extensions" for database transaction added
- utility functions added
  - TALER_exchange_offline_extension_agemask_{sign,verify}
  - TALER_agemask_parse_json
Diffstat (limited to 'src/exchange')
| -rw-r--r-- | src/exchange/taler-exchange-httpd.c | 21 | ||||
| -rw-r--r-- | src/exchange/taler-exchange-httpd.h | 6 | ||||
| -rw-r--r-- | src/exchange/taler-exchange-httpd_management_extensions.c | 578 | 
3 files changed, 287 insertions, 318 deletions
| diff --git a/src/exchange/taler-exchange-httpd.c b/src/exchange/taler-exchange-httpd.c index de4ffc7c..b435ee4a 100644 --- a/src/exchange/taler-exchange-httpd.c +++ b/src/exchange/taler-exchange-httpd.c @@ -53,6 +53,7 @@  #include "taler-exchange-httpd_withdraw.h"  #include "taler_exchangedb_lib.h"  #include "taler_exchangedb_plugin.h" +#include "taler_extensions.h"  #include <gnunet/gnunet_mhd_compat.h>  /** @@ -147,6 +148,25 @@ int TEH_check_invariants_flag;  bool TEH_suicide;  /** + * The global manifest with the list supported extensions, sorted by + * TALER_Extension_Type. + **/ +const struct TALER_Extension TEH_extensions[TALER_Extension_Max] = { +  [TALER_Extension_Peer2Peer] = { +    .type = TALER_Extension_Peer2Peer, +    .name = "peer2peer", +    .critical = false, +    .config = NULL, // disabled per default +  }, +  [TALER_Extension_AgeRestriction] = { +    .type = TALER_Extension_AgeRestriction, +    .name = "age_restriction", +    .critical = false, +    .config = NULL, // disabled per default +  }, +}; + +/**   * Value to return from main()   */  static int global_ret; @@ -184,7 +204,6 @@ struct GNUNET_CURL_Context *TEH_curl_ctx;   */  static struct GNUNET_CURL_RescheduleContext *exchange_curl_rc; -  /**   * Signature of functions that handle operations on coins.   * diff --git a/src/exchange/taler-exchange-httpd.h b/src/exchange/taler-exchange-httpd.h index cad74d2e..fa47af6f 100644 --- a/src/exchange/taler-exchange-httpd.h +++ b/src/exchange/taler-exchange-httpd.h @@ -26,6 +26,7 @@  #include <microhttpd.h>  #include "taler_json_lib.h"  #include "taler_crypto_lib.h" +#include "taler_extensions.h"  #include <gnunet/gnunet_mhd_compat.h> @@ -201,6 +202,11 @@ extern volatile bool MHD_terminating;  extern struct GNUNET_CURL_Context *TEH_curl_ctx;  /** + * The manifest of the available extensions + */ +extern const struct TALER_Extension TEH_extensions[TALER_Extension_Max]; + +/**   * @brief Struct describing an URL and the handler for it.   */  struct TEH_RequestHandler; diff --git a/src/exchange/taler-exchange-httpd_management_extensions.c b/src/exchange/taler-exchange-httpd_management_extensions.c index 96b42abb..6a771bf4 100644 --- a/src/exchange/taler-exchange-httpd_management_extensions.c +++ b/src/exchange/taler-exchange-httpd_management_extensions.c @@ -1,18 +1,18 @@  /* -  This file is part of TALER -  Copyright (C) 2021 Taler Systems SA +   This file is part of TALER +   Copyright (C) 2021 Taler Systems SA -  TALER is free software; you can redistribute it and/or modify it under the -  terms of the GNU Affero General Public License as published by the Free Software -  Foundation; either version 3, or (at your option) any later version. +   TALER is free software; you can redistribute it and/or modify it under the +   terms of the GNU Affero General Public License as published by the Free Software +   Foundation; either version 3, or (at your option) any later version. -  TALER is distributed in the hope that it will be useful, but WITHOUT ANY -  WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR -  A PARTICULAR PURPOSE.  See the GNU Affero General Public License for more details. +   TALER is distributed in the hope that it will be useful, but WITHOUT ANY +   WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR +   A PARTICULAR PURPOSE.  See the GNU Affero General Public License for more details. -  You should have received a copy of the GNU Affero General Public License along with -  TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/> -*/ +   You should have received a copy of the GNU Affero General Public License along with +   TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/> + */  /**   * @file taler-exchange-httpd_management_extensions.c   * @brief Handle request to POST /management/extensions @@ -23,260 +23,80 @@  #include <gnunet/gnunet_json_lib.h>  #include <jansson.h>  #include <microhttpd.h> -#include <pthread.h>  #include "taler_json_lib.h"  #include "taler_mhd_lib.h"  #include "taler_signatures.h"  #include "taler-exchange-httpd_management.h"  #include "taler-exchange-httpd_responses.h" +#include "taler_extensions.h" + + +struct Extension +{ +  enum TALER_Extension_Type type; +  json_t *config_json; +  // This union contains the parsed configuration for each extension. +  union +  { +    // configuration for the age restriction +    struct TALER_AgeMask mask; + +    /* TODO oec - peer2peer config */ +  }; +}; -#if 0  /** - * Function implementing database transaction to add offline signing keys. - * Runs the transaction logic; IF it returns a non-error code, the transaction - * logic MUST NOT queue a MHD response.  IF it returns an hard error, the - * transaction logic MUST queue a MHD response and set @a mhd_ret.  IF it - * returns the soft error code, the function MAY be called again to retry and - * MUST not queue a MHD response. + * Closure for the #set_extensions transaction + */ +struct SetExtensionsContext +{ +  uint32_t num_extensions; +  struct Extension *extensions; +  struct TALER_MasterSignatureP *extensions_sigs; +}; + +/** + * Function implementing database transaction to set the configuration of + * extensions.  It runs the transaction logic. + *  - IF it returns a non-error code, the transaction logic MUST NOT queue a + *    MHD response. + *  - IF it returns an hard error, the transaction logic MUST queue a MHD + *    response and set @a mhd_ret. + *  - IF it returns the soft error code, the function MAY be called again to + *    retry and MUST not queue a MHD response.   * - * @param cls closure with a `struct AddKeysContext` + * @param cls closure with a `struct SetExtensionsContext`   * @param connection MHD request which triggered the transaction   * @param[out] mhd_ret set to MHD response status for @a connection,   *             if transaction failed (!)   * @return transaction status   */  static enum GNUNET_DB_QueryStatus -add_keys (void *cls, -          struct MHD_Connection *connection, -          MHD_RESULT *mhd_ret) +set_extensions (void *cls, +                struct MHD_Connection *connection, +                MHD_RESULT *mhd_ret)  { -  struct AddKeysContext *akc = cls; - -  /* activate all denomination keys */ -  for (unsigned int i = 0; i<akc->nd_sigs; i++) -  { -    struct DenomSig *d = &akc->d_sigs[i]; -    enum GNUNET_DB_QueryStatus qs; -    bool is_active = false; -    struct TALER_EXCHANGEDB_DenominationKeyMetaData meta; -    struct TALER_DenominationPublicKey denom_pub; - -    /* For idempotency, check if the key is already active */ -    memset (&denom_pub, -            0, -            sizeof (denom_pub)); -    qs = TEH_plugin->lookup_denomination_key ( -      TEH_plugin->cls, -      &d->h_denom_pub, -      &meta); -    if (qs < 0) -    { -      if (GNUNET_DB_STATUS_SOFT_ERROR == qs) -        return qs; -      GNUNET_break (0); -      *mhd_ret = TALER_MHD_reply_with_error (connection, -                                             MHD_HTTP_INTERNAL_SERVER_ERROR, -                                             TALER_EC_GENERIC_DB_FETCH_FAILED, -                                             "lookup denomination key"); -      return qs; -    } -    if (0 == qs) -    { -      enum GNUNET_GenericReturnValue rv; - -      rv = TEH_keys_load_fees (&d->h_denom_pub, -                               &denom_pub, -                               &meta); -      switch (rv) -      { -      case GNUNET_SYSERR: -        *mhd_ret = TALER_MHD_reply_with_error ( -          connection, -          MHD_HTTP_INTERNAL_SERVER_ERROR, -          TALER_EC_EXCHANGE_GENERIC_BAD_CONFIGURATION, -          GNUNET_h2s (&d->h_denom_pub.hash)); -        return GNUNET_DB_STATUS_HARD_ERROR; -      case GNUNET_NO: -        *mhd_ret = TALER_MHD_reply_with_error ( -          connection, -          MHD_HTTP_NOT_FOUND, -          TALER_EC_EXCHANGE_GENERIC_DENOMINATION_KEY_UNKNOWN, -          GNUNET_h2s (&d->h_denom_pub.hash)); -        return GNUNET_DB_STATUS_HARD_ERROR; -      case GNUNET_OK: -        break; -      } -    } -    else -    { -      is_active = true; -    } - -    /* check signature is valid */ -    if (GNUNET_OK != -        TALER_exchange_offline_denom_validity_verify ( -          &d->h_denom_pub, -          meta.start, -          meta.expire_withdraw, -          meta.expire_deposit, -          meta.expire_legal, -          &meta.value, -          &meta.fee_withdraw, -          &meta.fee_deposit, -          &meta.fee_refresh, -          &meta.fee_refund, -          &TEH_master_public_key, -          &d->master_sig)) -    { -      GNUNET_break_op (0); -      *mhd_ret = TALER_MHD_reply_with_error ( -        connection, -        MHD_HTTP_FORBIDDEN, -        TALER_EC_EXCHANGE_MANAGEMENT_KEYS_DENOMKEY_ADD_SIGNATURE_INVALID, -        GNUNET_h2s (&d->h_denom_pub.hash)); -      if (! is_active) -        TALER_denom_pub_free (&denom_pub); -      return GNUNET_DB_STATUS_HARD_ERROR; -    } -    if (is_active) -    { -      GNUNET_log (GNUNET_ERROR_TYPE_INFO, -                  "Denomination key %s already active, skipping\n", -                  GNUNET_h2s (&d->h_denom_pub.hash)); -      continue; /* skip, already known */ -    } -    qs = TEH_plugin->add_denomination_key ( -      TEH_plugin->cls, -      &d->h_denom_pub, -      &denom_pub, -      &meta, -      &d->master_sig); -    TALER_denom_pub_free (&denom_pub); -    if (qs < 0) -    { -      if (GNUNET_DB_STATUS_SOFT_ERROR == qs) -        return qs; -      GNUNET_break (0); -      *mhd_ret = TALER_MHD_reply_with_error (connection, -                                             MHD_HTTP_INTERNAL_SERVER_ERROR, -                                             TALER_EC_GENERIC_DB_STORE_FAILED, -                                             "activate denomination key"); -      return qs; -    } -    GNUNET_log (GNUNET_ERROR_TYPE_INFO, -                "Added offline signature for denomination `%s'\n", -                GNUNET_h2s (&d->h_denom_pub.hash)); -    GNUNET_assert (0 != qs); -  } +  // struct SetExtensionContext *sec = cls; -  for (unsigned int i = 0; i<akc->ns_sigs; i++) -  { -    struct SigningSig *s = &akc->s_sigs[i]; -    enum GNUNET_DB_QueryStatus qs; -    bool is_active = false; -    struct TALER_EXCHANGEDB_SignkeyMetaData meta; - -    qs = TEH_plugin->lookup_signing_key ( -      TEH_plugin->cls, -      &s->exchange_pub, -      &meta); -    if (qs < 0) -    { -      if (GNUNET_DB_STATUS_SOFT_ERROR == qs) -        return qs; -      GNUNET_break (0); -      *mhd_ret = TALER_MHD_reply_with_error (connection, -                                             MHD_HTTP_INTERNAL_SERVER_ERROR, -                                             TALER_EC_GENERIC_DB_FETCH_FAILED, -                                             "lookup signing key"); -      return qs; -    } -    if (0 == qs) -    { -      if (GNUNET_OK != -          TEH_keys_get_timing (&s->exchange_pub, -                               &meta)) -      { -        /* For idempotency, check if the key is already active */ -        *mhd_ret = TALER_MHD_reply_with_error ( -          connection, -          MHD_HTTP_NOT_FOUND, -          TALER_EC_EXCHANGE_MANAGEMENT_KEYS_SIGNKEY_UNKNOWN, -          TALER_B2S (&s->exchange_pub)); -        return GNUNET_DB_STATUS_HARD_ERROR; -      } -    } -    else -    { -      is_active = true; /* if we pass, it's active! */ -    } - -    /* check signature is valid */ -    if (GNUNET_OK != -        TALER_exchange_offline_signkey_validity_verify ( -          &s->exchange_pub, -          meta.start, -          meta.expire_sign, -          meta.expire_legal, -          &TEH_master_public_key, -          &s->master_sig)) -    { -      GNUNET_break_op (0); -      *mhd_ret = TALER_MHD_reply_with_error ( -        connection, -        MHD_HTTP_FORBIDDEN, -        TALER_EC_EXCHANGE_MANAGEMENT_KEYS_SIGNKEY_ADD_SIGNATURE_INVALID, -        TALER_B2S (&s->exchange_pub)); -      return GNUNET_DB_STATUS_HARD_ERROR; -    } -    if (is_active) -    { -      GNUNET_log (GNUNET_ERROR_TYPE_INFO, -                  "Signing key %s already active, skipping\n", -                  TALER_B2S (&s->exchange_pub)); -      continue;   /* skip, already known */ -    } -    qs = TEH_plugin->activate_signing_key ( -      TEH_plugin->cls, -      &s->exchange_pub, -      &meta, -      &s->master_sig); -    if (qs < 0) -    { -      if (GNUNET_DB_STATUS_SOFT_ERROR == qs) -        return qs; -      GNUNET_break (0); -      *mhd_ret = TALER_MHD_reply_with_error (connection, -                                             MHD_HTTP_INTERNAL_SERVER_ERROR, -                                             TALER_EC_GENERIC_DB_STORE_FAILED, -                                             "activate signing key"); -      return qs; -    } -    GNUNET_log (GNUNET_ERROR_TYPE_INFO, -                "Added offline signature for signing key `%s'\n", -                TALER_B2S (&s->exchange_pub)); -    GNUNET_assert (0 != qs); -  } +  // TODO oec    return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT; /* only 'success', so >=0, matters here */  } -#endif -  MHD_RESULT  TEH_handler_management_post_extensions (    struct MHD_Connection *connection,    const json_t *root)  { -#if 0 -  json_t *denom_sigs; -  json_t *signkey_sigs; +  struct SetExtensionsContext sec = {0}; +  json_t *extensions; +  json_t *extensions_sigs;    struct GNUNET_JSON_Specification spec[] = { -    GNUNET_JSON_spec_json ("denom_sigs", -                           &denom_sigs), -    GNUNET_JSON_spec_json ("signkey_sigs", -                           &signkey_sigs), +    GNUNET_JSON_spec_json ("extensions", +                           &extensions), +    GNUNET_JSON_spec_json ("extensions_sigs", +                           &extensions_sigs),      GNUNET_JSON_spec_end ()    };    bool ok; @@ -293,8 +113,9 @@ TEH_handler_management_post_extensions (      if (GNUNET_NO == res)        return MHD_YES; /* failure */    } -  if (! (json_is_array (denom_sigs) && -         json_is_array (signkey_sigs)) ) + +  if (! (json_is_array (extensions) && +         json_is_array (extensions_sigs)) )    {      GNUNET_break_op (0);      GNUNET_JSON_parse_free (spec); @@ -302,113 +123,236 @@ TEH_handler_management_post_extensions (        connection,        MHD_HTTP_BAD_REQUEST,        TALER_EC_GENERIC_PARAMETER_MALFORMED, -      "array expected for denom_sigs and signkey_sigs"); +      "array expected for extensions and extensions_sig");    } + +  sec.num_extensions = json_array_size (extensions_sigs); +  if (json_array_size (extensions) != sec.num_extensions) +  { +    GNUNET_break_op (0); +    GNUNET_JSON_parse_free (spec); +    return TALER_MHD_reply_with_error ( +      connection, +      MHD_HTTP_BAD_REQUEST, +      TALER_EC_GENERIC_PARAMETER_MALFORMED, +      "arrays extensions and extensions_sig are not of equal size"); +  } +    GNUNET_log (GNUNET_ERROR_TYPE_INFO, -              "Received /management/keys\n"); -  akc.nd_sigs = json_array_size (denom_sigs); -  akc.d_sigs = GNUNET_new_array (akc.nd_sigs, -                                 struct DenomSig); +              "Received /management/extensions\n"); + +  sec.extensions = GNUNET_new_array (sec.num_extensions, +                                     struct Extension); +  sec.extensions_sigs = GNUNET_new_array (sec.num_extensions, +                                          struct TALER_MasterSignatureP);    ok = true; -  for (unsigned int i = 0; i<akc.nd_sigs; i++) + +  for (unsigned int i = 0; i<sec.num_extensions; i++)    { -    struct DenomSig *d = &akc.d_sigs[i]; -    struct GNUNET_JSON_Specification ispec[] = { -      GNUNET_JSON_spec_fixed_auto ("master_sig", -                                   &d->master_sig), -      GNUNET_JSON_spec_fixed_auto ("h_denom_pub", -                                   &d->h_denom_pub), -      GNUNET_JSON_spec_end () -    }; -    enum GNUNET_GenericReturnValue res; -    res = TALER_MHD_parse_json_data (connection, -                                     json_array_get (denom_sigs, -                                                     i), -                                     ispec); -    if (GNUNET_SYSERR == res) +    // 1. parse the extension      { -      ret = MHD_NO; /* hard failure */ -      ok = false; -      break; -    } -    if (GNUNET_NO == res) -    { -      ret = MHD_YES; -      ok = false; -      break; +      enum GNUNET_GenericReturnValue res; +      const char *name; +      struct GNUNET_JSON_Specification ispec[] = { +        GNUNET_JSON_spec_string ("extension", +                                 &name), +        GNUNET_JSON_spec_json ("config", +                               &sec.extensions[i].config_json), +        GNUNET_JSON_spec_end () +      }; + +      res = TALER_MHD_parse_json_array (connection, +                                        extensions, +                                        ispec, +                                        i, +                                        -1); +      if (GNUNET_SYSERR == res) +      { +        ret = MHD_NO; /* hard failure */ +        ok = false; +        break; +      } +      if (GNUNET_NO == res) +      { +        ret = MHD_YES; +        ok = false; +        break; +      } + +      // Make sure name refers to a supported extension +      { +        bool found = false; +        for (unsigned int k = 0; k < TALER_Extension_Max; k++) +        { +          if (0 == strncmp (name, +                            TEH_extensions[k].name, +                            strlen (TEH_extensions[k].name))) +          { +            sec.extensions[i].type = TEH_extensions[k].type; +            found = true; +            break; +          } +        } + +        if (! found) +        { +          GNUNET_free (sec.extensions); +          GNUNET_free (sec.extensions_sigs); +          GNUNET_JSON_parse_free (spec); +          GNUNET_JSON_parse_free (ispec); +          return TALER_MHD_reply_with_error ( +            connection, +            MHD_HTTP_BAD_REQUEST, +            TALER_EC_GENERIC_PARAMETER_MALFORMED, +            "invalid extension type"); +        } +      } + +      // We have a JSON object for the extension.  Increment its refcount and +      // free the parser. +      // TODO: is this correct? +      json_incref (sec.extensions[i].config_json); +      GNUNET_JSON_parse_free (ispec); + +      // Make sure the config is sound +      { +        switch (sec.extensions[i].type) +        { +        case TALER_Extension_AgeRestriction: +          if (GNUNET_OK != TALER_agemask_parse_json ( +                sec.extensions[i].config_json, +                &sec.extensions[i].mask)) +          { +            GNUNET_free (sec.extensions); +            GNUNET_free (sec.extensions_sigs); +            GNUNET_JSON_parse_free (spec); +            return TALER_MHD_reply_with_error ( +              connection, +              MHD_HTTP_BAD_REQUEST, +              TALER_EC_GENERIC_PARAMETER_MALFORMED, +              "invalid mask for age restriction"); +          } +          break; + +        case TALER_Extension_Peer2Peer:   /* TODO */ +          ok = false; +          ret = MHD_NO; +          goto BREAK; + +        default: +          /* not reachable */ +          GNUNET_log (GNUNET_ERROR_TYPE_ERROR, +                      "shouldn't be reached in handler for /management/extensions\n"); +          ok = false; +          ret = MHD_NO; +          goto BREAK; +        } +      }      } -  } -  if (! ok) -  { -    GNUNET_free (akc.d_sigs); -    GNUNET_JSON_parse_free (spec); -    GNUNET_log (GNUNET_ERROR_TYPE_ERROR, -                "Failure to handle /management/keys\n"); -    return ret; -  } -  akc.ns_sigs = json_array_size (signkey_sigs); -  akc.s_sigs = GNUNET_new_array (akc.ns_sigs, -                                 struct SigningSig); -  for (unsigned int i = 0; i<akc.ns_sigs; i++) -  { -    struct SigningSig *s = &akc.s_sigs[i]; -    struct GNUNET_JSON_Specification ispec[] = { -      GNUNET_JSON_spec_fixed_auto ("master_sig", -                                   &s->master_sig), -      GNUNET_JSON_spec_fixed_auto ("exchange_pub", -                                   &s->exchange_pub), -      GNUNET_JSON_spec_end () -    }; -    enum GNUNET_GenericReturnValue res; -    res = TALER_MHD_parse_json_data (connection, -                                     json_array_get (signkey_sigs, -                                                     i), -                                     ispec); -    if (GNUNET_SYSERR == res) +    // 2. parse the signature      { -      ret = MHD_NO; /* hard failure */ -      ok = false; -      break; +      enum GNUNET_GenericReturnValue res; +      struct GNUNET_JSON_Specification ispec[] = { +        GNUNET_JSON_spec_fixed_auto (NULL, +                                     &sec.extensions_sigs[i]), +        GNUNET_JSON_spec_end () +      }; + +      res = TALER_MHD_parse_json_array (connection, +                                        extensions_sigs, +                                        ispec, +                                        i, +                                        -1); +      if (GNUNET_SYSERR == res) +      { +        ret = MHD_NO; /* hard failure */ +        ok = false; +        break; +      } +      if (GNUNET_NO == res) +      { +        ret = MHD_YES; +        ok = false; +        break; +      }      } -    if (GNUNET_NO == res) + +    // 3. verify the signature      { -      ret = MHD_YES; -      ok = false; -      break; +      enum GNUNET_GenericReturnValue res; + +      switch (sec.extensions[i].type) +      { +      case TALER_Extension_AgeRestriction: +        res = TALER_exchange_offline_extension_agemask_verify ( +          sec.extensions[i].mask, +          &TEH_master_public_key, +          &sec.extensions_sigs[i]); +        if (GNUNET_OK != res) +        { +          GNUNET_free (sec.extensions); +          GNUNET_free (sec.extensions_sigs); +          GNUNET_JSON_parse_free (spec); +          return TALER_MHD_reply_with_error ( +            connection, +            MHD_HTTP_BAD_REQUEST, +            TALER_EC_GENERIC_PARAMETER_MALFORMED, +            "invalid signature for age mask"); +        } +        break; + +      case TALER_Extension_Peer2Peer:   /* TODO */ +        GNUNET_log (GNUNET_ERROR_TYPE_ERROR, +                    "Peer2peer not yet supported in handler for /management/extensions\n"); +        ok = false; +        ret = MHD_NO; +        goto BREAK; + +      default: +        GNUNET_log (GNUNET_ERROR_TYPE_ERROR, +                    "shouldn't be reached in handler for /management/extensions\n"); +        ok = false; +        ret = MHD_NO; +        /* not reachable */ +        goto BREAK; +      }      }    } + +BREAK:    if (! ok)    {      GNUNET_log (GNUNET_ERROR_TYPE_ERROR, -                "Failure to handle /management/keys\n"); -    GNUNET_free (akc.d_sigs); -    GNUNET_free (akc.s_sigs); +                "Failure to handle /management/extensions\n"); +    GNUNET_free (sec.extensions); +    GNUNET_free (sec.extensions_sigs);      GNUNET_JSON_parse_free (spec);      return ret;    } + +    GNUNET_log (GNUNET_ERROR_TYPE_INFO, -              "Received %u denomination and %u signing key signatures\n", -              akc.nd_sigs, -              akc.ns_sigs); +              "Received %u extensions\n", +              sec.num_extensions); +    {      enum GNUNET_GenericReturnValue res;      res = TEH_DB_run_transaction (connection, -                                  "add keys", +                                  "set extensions",                                    TEH_MT_OTHER,                                    &ret, -                                  &add_keys, -                                  &akc); -    GNUNET_free (akc.d_sigs); -    GNUNET_free (akc.s_sigs); +                                  &set_extensions, +                                  &sec); + +    GNUNET_free (sec.extensions); +    GNUNET_free (sec.extensions_sigs);      GNUNET_JSON_parse_free (spec);      if (GNUNET_SYSERR == res)        return ret;    } -  TEH_keys_update_states (); -#endif    return TALER_MHD_reply_static (      connection, | 
