[age restriction] progress 8/n

More work towards support for extensions and age restriction

- updated gana
- added handler for DB-Event
- added TEH_extensions_init() and _done()
- added global for age restriction
- added stub for post handler
- added SQL-table for extension metadata
- added enum type for extensions and other data structures

Also:
- fixed some warnings -Wmaybe-unitialized
This commit is contained in:
Özgür Kesim 2021-12-23 15:15:33 +01:00
parent 62444504f7
commit 1b23857f2c
Signed by: oec
GPG Key ID: 3D76A56D79EDD9D7
18 changed files with 1032 additions and 43 deletions

@ -1 +1 @@
Subproject commit b123140349c3e3b300878d2e35cea1553c9a381d Subproject commit ce3c127a788d4bc35caf8472fa9799b45f8d2133

View File

@ -82,6 +82,7 @@ taler_exchange_httpd_SOURCES = \
taler-exchange-httpd_db.c taler-exchange-httpd_db.h \ taler-exchange-httpd_db.c taler-exchange-httpd_db.h \
taler-exchange-httpd_deposit.c taler-exchange-httpd_deposit.h \ taler-exchange-httpd_deposit.c taler-exchange-httpd_deposit.h \
taler-exchange-httpd_deposits_get.c taler-exchange-httpd_deposits_get.h \ taler-exchange-httpd_deposits_get.c taler-exchange-httpd_deposits_get.h \
taler-exchange-httpd_extensions.c taler-exchange-httpd_extensions.h \
taler-exchange-httpd_keys.c taler-exchange-httpd_keys.h \ taler-exchange-httpd_keys.c taler-exchange-httpd_keys.h \
taler-exchange-httpd_kyc-check.c taler-exchange-httpd_kyc-check.h \ taler-exchange-httpd_kyc-check.c taler-exchange-httpd_kyc-check.h \
taler-exchange-httpd_kyc-proof.c taler-exchange-httpd_kyc-proof.h \ taler-exchange-httpd_kyc-proof.c taler-exchange-httpd_kyc-proof.h \
@ -91,6 +92,7 @@ taler_exchange_httpd_SOURCES = \
taler-exchange-httpd_management_auditors.c \ taler-exchange-httpd_management_auditors.c \
taler-exchange-httpd_management_auditors_AP_disable.c \ taler-exchange-httpd_management_auditors_AP_disable.c \
taler-exchange-httpd_management_denominations_HDP_revoke.c \ taler-exchange-httpd_management_denominations_HDP_revoke.c \
taler-exchange-httpd_management_extensions.c \
taler-exchange-httpd_management_post_keys.c \ taler-exchange-httpd_management_post_keys.c \
taler-exchange-httpd_management_signkey_EP_revoke.c \ taler-exchange-httpd_management_signkey_EP_revoke.c \
taler-exchange-httpd_management_wire_enable.c \ taler-exchange-httpd_management_wire_enable.c \
@ -109,9 +111,6 @@ taler_exchange_httpd_SOURCES = \
taler-exchange-httpd_wire.c taler-exchange-httpd_wire.h \ taler-exchange-httpd_wire.c taler-exchange-httpd_wire.h \
taler-exchange-httpd_withdraw.c taler-exchange-httpd_withdraw.h taler-exchange-httpd_withdraw.c taler-exchange-httpd_withdraw.h
# taler-exchange-httpd_management_post_keys.c
taler_exchange_httpd_LDADD = \ taler_exchange_httpd_LDADD = \
$(LIBGCRYPT_LIBS) \ $(LIBGCRYPT_LIBS) \
$(top_builddir)/src/bank-lib/libtalerbank.la \ $(top_builddir)/src/bank-lib/libtalerbank.la \

View File

@ -1799,7 +1799,8 @@ do_shutdown (void *cls)
TEH_kyc_proof_cleanup (); TEH_kyc_proof_cleanup ();
if (NULL != mhd) if (NULL != mhd)
MHD_stop_daemon (mhd); MHD_stop_daemon (mhd);
TEH_WIRE_done (); TEH_wire_done ();
TEH_extensions_done ();
TEH_keys_finished (); TEH_keys_finished ();
if (NULL != TEH_plugin) if (NULL != TEH_plugin)
{ {
@ -1860,6 +1861,13 @@ run (void *cls,
GNUNET_SCHEDULER_shutdown (); GNUNET_SCHEDULER_shutdown ();
return; return;
} }
if (GNUNET_OK !=
TEH_extensions_init ())
{
global_ret = EXIT_FAILURE;
GNUNET_SCHEDULER_shutdown ();
return;
}
if (GNUNET_OK != if (GNUNET_OK !=
TEH_keys_init ()) TEH_keys_init ())
{ {

View File

@ -0,0 +1,110 @@
/*
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 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/>
*/
/**
* @file taler-exchange-httpd_extensions.c
* @brief Handle extensions (age-restriction, peer2peer)
* @author Özgür Kesim
*/
#include "platform.h"
#include <gnunet/gnunet_json_lib.h>
#include "taler_dbevents.h"
#include "taler-exchange-httpd_responses.h"
#include "taler-exchange-httpd_extensions.h"
#include "taler_json_lib.h"
#include "taler_mhd_lib.h"
#include <jansson.h>
/**
* Handler listening for extensions updates by other exchange
* services.
*/
static struct GNUNET_DB_EventHandler *extensions_eh;
/**
* Function called whenever another exchange process has updated
* the extensions data in the database.
*
* @param cls NULL
* @param extra unused
* @param extra_size number of bytes in @a extra unused
*/
static void
extension_update_event_cb (void *cls,
const void *extra,
size_t extra_size)
{
(void) cls;
(void) extra;
(void) extra_size;
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Received /management/extensions update event\n");
}
enum GNUNET_GenericReturnValue
TEH_extensions_init ()
{
struct GNUNET_DB_EventHeaderP es = {
.size = htons (sizeof (es)),
.type = htons (TALER_DBEVENT_EXCHANGE_EXTENSIONS_UPDATED),
};
extensions_eh = TEH_plugin->event_listen (TEH_plugin->cls,
GNUNET_TIME_UNIT_FOREVER_REL,
&es,
&extension_update_event_cb,
NULL);
if (NULL == extensions_eh)
{
GNUNET_break (0);
return GNUNET_SYSERR;
}
return GNUNET_OK;
}
void
TEH_extensions_done ()
{
if (NULL != extensions_eh)
{
TEH_plugin->event_listen_cancel (TEH_plugin->cls,
extensions_eh);
extensions_eh = NULL;
}
}
void
TEH_extensions_update_state (void)
{
/* TODO */
#if 0
struct GNUNET_DB_EventHeaderP es = {
.size = htons (sizeof (es)),
.type = htons (TALER_DBEVENT_EXCHANGE_WIRE_UPDATED),
};
TEH_plugin->event_notify (TEH_plugin->cls,
&es,
NULL,
0);
#endif
}
/* end of taler-exchange-httpd_extensions.c */

View File

@ -0,0 +1,51 @@
/*
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 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/>
*/
/**
* @file taler-exchange-httpd_extensions.h
* @brief Manage extensions
* @author Özgür Kesim
*/
#ifndef TALER_EXCHANGE_HTTPD_EXTENSIONS_H
#define TALER_EXCHANGE_HTTPD_EXTENSIONS_H
#include <gnunet/gnunet_util_lib.h>
#include <microhttpd.h>
#include "taler-exchange-httpd.h"
/**
* Initialize extensions
*
* @return #GNUNET_OK on success
*/
enum GNUNET_GenericReturnValue
TEH_extensions_init (void);
/**
* Terminate the extension subsystem
*/
void
TEH_extensions_done (void);
/**
* Something changed in the database. Rebuild the extension state metadata.
* This function should be called if the exchange learns about a new signature
* from our master key.
*/
void
TEH_extensions_update_state (void);
#endif

View File

@ -750,7 +750,7 @@ load_age_mask (const char*section_name)
struct TALER_AgeMask age_mask = {0}; struct TALER_AgeMask age_mask = {0};
/* FIXME-oec: get age_mask from database, not from config */ /* FIXME-oec: get age_mask from database, not from config */
if (TALER_EXTENSION_OK != TALER_get_age_mask (TEH_cfg, &age_mask)) if (TALER_Extension_OK != TALER_get_age_mask (TEH_cfg, &age_mask))
{ {
GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
TALER_EXTENSION_SECTION_AGE_RESTRICTION, TALER_EXTENSION_SECTION_AGE_RESTRICTION,
@ -761,6 +761,9 @@ load_age_mask (const char*section_name)
if (age_mask.mask == 0) if (age_mask.mask == 0)
{ {
/* Age restriction support is not enabled. Ignore the AGE_RESTRICTED field
* for the particular denomination and simply return the null_mask
*/
return null_mask; return null_mask;
} }

View File

@ -135,4 +135,25 @@ TEH_handler_management_post_wire_fees (
const json_t *root); const json_t *root);
/**
* Handle a POST "/management/extensions" request.
*
* @param connection the MHD connection to handle
* @param root uploaded JSON data
* @return MHD result code
*/
MHD_RESULT
TEH_handler_management_post_extensions (
struct MHD_Connection *connection,
const json_t *root);
/**
* Initialize extension configuration handling.
*
* @return #GNUNET_OK on success
*/
enum GNUNET_GenericReturnValue
TEH_extensions_init (void);
#endif #endif

View File

@ -0,0 +1,422 @@
/*
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 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/>
*/
/**
* @file taler-exchange-httpd_management_extensions.c
* @brief Handle request to POST /management/extensions
* @author Özgür Kesim
*/
#include "platform.h"
#include <gnunet/gnunet_util_lib.h>
#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"
#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.
*
* @param cls closure with a `struct AddKeysContext`
* @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)
{
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);
}
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);
}
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 GNUNET_JSON_Specification spec[] = {
GNUNET_JSON_spec_json ("denom_sigs",
&denom_sigs),
GNUNET_JSON_spec_json ("signkey_sigs",
&signkey_sigs),
GNUNET_JSON_spec_end ()
};
bool ok;
MHD_RESULT ret;
{
enum GNUNET_GenericReturnValue res;
res = TALER_MHD_parse_json_data (connection,
root,
spec);
if (GNUNET_SYSERR == res)
return MHD_NO; /* hard failure */
if (GNUNET_NO == res)
return MHD_YES; /* failure */
}
if (! (json_is_array (denom_sigs) &&
json_is_array (signkey_sigs)) )
{
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,
"array expected for denom_sigs and signkey_sigs");
}
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);
ok = true;
for (unsigned int i = 0; i<akc.nd_sigs; 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)
{
ret = MHD_NO; /* hard failure */
ok = false;
break;
}
if (GNUNET_NO == res)
{
ret = MHD_YES;
ok = false;
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)
{
ret = MHD_NO; /* hard failure */
ok = false;
break;
}
if (GNUNET_NO == res)
{
ret = MHD_YES;
ok = false;
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);
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);
{
enum GNUNET_GenericReturnValue res;
res = TEH_DB_run_transaction (connection,
"add keys",
TEH_MT_OTHER,
&ret,
&add_keys,
&akc);
GNUNET_free (akc.d_sigs);
GNUNET_free (akc.s_sigs);
GNUNET_JSON_parse_free (spec);
if (GNUNET_SYSERR == res)
return ret;
}
TEH_keys_update_states ();
#endif
return TALER_MHD_reply_static (
connection,
MHD_HTTP_NO_CONTENT,
NULL,
NULL,
0);
}
/* end of taler-exchange-httpd_management_management_post_extensions.c */

View File

@ -130,7 +130,7 @@ TEH_wire_init ()
void void
TEH_WIRE_done () TEH_wire_done ()
{ {
if (NULL != wire_state) if (NULL != wire_state)
{ {

View File

@ -30,7 +30,7 @@
* Clean up wire subsystem. * Clean up wire subsystem.
*/ */
void void
TEH_WIRE_done (void); TEH_wire_done (void);
/** /**

View File

@ -280,6 +280,22 @@ COMMENT ON TABLE signkey_revocations
IS 'Table storing which online signing keys have been revoked'; IS 'Table storing which online signing keys have been revoked';
CREATE TABLE IF NOT EXISTS extensions
(extension_id BIGSERIAL UNIQUE
,name VARCHAR NOT NULL UNIQUE
,config BYTEA NOT NULL
,config_sig BYTEA NOT NULL
);
COMMENT ON TABLE extensions
IS 'Configurations of the activated extensions';
COMMENT ON COLUMN extensions.name
IS 'Name of the extension';
COMMENT ON COLUMN extensions.config
IS 'Configuration of the extension as JSON-blob';
COMMENT ON COLUMN extensions.config
IS 'Signature of the configuration of an extension, signed with the master key of the exchange';
CREATE TABLE IF NOT EXISTS known_coins CREATE TABLE IF NOT EXISTS known_coins
(known_coin_id BIGSERIAL UNIQUE (known_coin_id BIGSERIAL UNIQUE
,coin_pub BYTEA NOT NULL PRIMARY KEY CHECK (LENGTH(coin_pub)=32) ,coin_pub BYTEA NOT NULL PRIMARY KEY CHECK (LENGTH(coin_pub)=32)

View File

@ -1,18 +1,18 @@
/* /*
This file is part of TALER This file is part of TALER
Copyright (C) 2014-2021 Taler Systems SA Copyright (C) 2014-2021 Taler Systems SA
TALER is free software; you can redistribute it and/or modify it under the 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 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. 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 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 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. 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 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/> TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/ */
/** /**
* @file include/taler_exchange_service.h * @file include/taler_exchange_service.h
* @brief C interface of libtalerexchange, a C library to use exchange's HTTP API * @brief C interface of libtalerexchange, a C library to use exchange's HTTP API
@ -1527,7 +1527,7 @@ typedef void
* *
* @param exchange the exchange handle; the exchange must be ready to operate * @param exchange the exchange handle; the exchange must be ready to operate
* @param refresh_data the refresh data as returned from * @param refresh_data the refresh data as returned from
#TALER_EXCHANGE_refresh_prepare()) #TALER_EXCHANGE_refresh_prepare())
* @param melt_cb the callback to call with the result * @param melt_cb the callback to call with the result
* @param melt_cb_cls closure for @a melt_cb * @param melt_cb_cls closure for @a melt_cb
* @return a handle for this request; NULL if the argument was invalid. * @return a handle for this request; NULL if the argument was invalid.
@ -1593,7 +1593,7 @@ struct TALER_EXCHANGE_RefreshesRevealHandle;
* *
* @param exchange the exchange handle; the exchange must be ready to operate * @param exchange the exchange handle; the exchange must be ready to operate
* @param refresh_data the refresh data as returned from * @param refresh_data the refresh data as returned from
#TALER_EXCHANGE_refresh_prepare()) #TALER_EXCHANGE_refresh_prepare())
* @param noreveal_index response from the exchange to the * @param noreveal_index response from the exchange to the
* #TALER_EXCHANGE_melt() invocation * #TALER_EXCHANGE_melt() invocation
* @param reveal_cb the callback to call with the final result of the * @param reveal_cb the callback to call with the final result of the
@ -2615,6 +2615,62 @@ void
TALER_EXCHANGE_post_management_keys_cancel ( TALER_EXCHANGE_post_management_keys_cancel (
struct TALER_EXCHANGE_ManagementPostKeysHandle *ph); struct TALER_EXCHANGE_ManagementPostKeysHandle *ph);
/**
* Information needed for a POST /management/extensions operation.
*/
struct TALER_EXCHANGE_ManagementPostExtensionsData
{
struct TALER_Extension *extensions;
struct TALER_MasterSignatureP *extension_sigs;
uint32_t num_extensions;
};
/**
* Function called with information about the post extensions operation result.
*
* @param cls closure
* @param hr HTTP response data
*/
typedef void
(*TALER_EXCHANGE_ManagementPostExtensionsCallback) (
void *cls,
const struct TALER_EXCHANGE_HttpResponse *hr);
/**
* @brief Handle for a POST /management/extensions request.
*/
struct TALER_EXCHANGE_ManagementPostExtensionsHandle;
/**
* FIXME-oec: Provide correct explanation of this function.
*
* @param ctx the context
* @param url HTTP base URL for the exchange
* @param pkd signature data to POST
* @param cb function to call with the exchange's result
* @param cb_cls closure for @a cb
* @return the request handle; NULL upon error
*/
struct TALER_EXCHANGE_ManagementPostExtensionsHandle *
TALER_EXCHANGE_management_post_extensions (
struct GNUNET_CURL_Context *ctx,
const char *url,
const struct TALER_EXCHANGE_ManagementPostExtensionsData *pkd,
TALER_EXCHANGE_ManagementPostExtensionsCallback cb,
void *cb_cls);
/**
* Cancel #TALER_EXCHANGE_post_management_extensions() operation.
*
* @param ph handle of the operation to cancel
*/
void
TALER_EXCHANGE_post_management_extensions_cancel (
struct TALER_EXCHANGE_ManagementPostExtensionsHandle *ph);
/** /**
* Function called with information about the post revocation operation result. * Function called with information about the post revocation operation result.
* *

View File

@ -4038,6 +4038,9 @@ struct TALER_EXCHANGEDB_Plugin
enum GNUNET_DB_QueryStatus enum GNUNET_DB_QueryStatus
(*delete_revolving_shards)(void *cls); (*delete_revolving_shards)(void *cls);
/**
* TODO-oec: add function for adding extension config
*/
}; };

View File

@ -26,21 +26,70 @@
#define TALER_EXTENSION_SECTION_PREFIX "exchange-extension-" #define TALER_EXTENSION_SECTION_PREFIX "exchange-extension-"
enum TALER_EXTENSION_ReturnValue enum TALER_Extension_ReturnValue
{ {
TALER_EXTENSION_OK = 0, TALER_Extension_OK = 0,
TALER_EXTENSION_ERROR_PARSING = 1, TALER_Extension_ERROR_PARSING = 1,
TALER_EXTENSION_ERROR_INVALID = 2, TALER_Extension_ERROR_INVALID = 2,
TALER_EXTENSION_ERROR_SYS = 3 TALER_Extension_ERROR_SYS = 3
};
enum TALER_Extension_Type
{
TALER_Extension_Peer2Peer = 0,
TALER_Extension_AgeRestriction = 1,
TALER_Extension_Max = 2
};
struct TALER_Extension
{
enum TALER_Extension_Type type;
char *name;
bool critical;
void *config;
size_t config_size;
};
struct TALER_Peer2Peer_Config
{
// FIXME
};
/**
* TEH_extensions is the global manifest with the list supported extensions,
* sorted by TALER_Extension_Type.
*
* TODO: Mutex?
*
**/
struct TALER_Extension TEH_extensions[TALER_Extension_Max] = {
[TALER_Extension_Peer2Peer] = {
.type = TALER_Extension_Peer2Peer,
.name = "peer2peer",
.critical = false,
.config_size = sizeof(struct TALER_Peer2Peer_Config),
},
[TALER_Extension_AgeRestriction] = {
.type = TALER_Extension_AgeRestriction,
.name = "age_restriction",
.critical = false,
.config_size = sizeof(struct TALER_AgeMask),
},
}; };
/* /*
* TALER Age Restriction Extensions * TALER Peer2Peer Extension
* FIXME
*/
/*
* TALER Age Restriction Extension
*/ */
#define TALER_EXTENSION_SECTION_AGE_RESTRICTION (TALER_EXTENSION_SECTION_PREFIX \ #define TALER_EXTENSION_SECTION_AGE_RESTRICTION (TALER_EXTENSION_SECTION_PREFIX \
"agerestriction") "age_restriction")
/** /**
* The default age mask represents the age groups * The default age mask represents the age groups
@ -55,7 +104,7 @@ enum TALER_EXTENSION_ReturnValue
* @param[out] mask Mask representation for age restriction. * @param[out] mask Mask representation for age restriction.
* @return Error, if age groups were invalid, OK otherwise. * @return Error, if age groups were invalid, OK otherwise.
*/ */
enum TALER_EXTENSION_ReturnValue enum TALER_Extension_ReturnValue
TALER_parse_age_group_string (char *groups, TALER_parse_age_group_string (char *groups,
struct TALER_AgeMask *mask); struct TALER_AgeMask *mask);
@ -66,8 +115,7 @@ TALER_parse_age_group_string (char *groups,
* @return Error if extension for age restriction was set but age groups were * @return Error if extension for age restriction was set but age groups were
* invalid, OK otherwise. * invalid, OK otherwise.
*/ */
enum TALER_EXTENSION_ReturnValue enum TALER_Extension_ReturnValue
TALER_get_age_mask (const struct GNUNET_CONFIGURATION_Handle *cfg, TALER_get_age_mask (const struct GNUNET_CONFIGURATION_Handle *cfg,
struct TALER_AgeMask *mask); struct TALER_AgeMask *mask);
#endif #endif

View File

@ -35,6 +35,7 @@ libtalerexchange_la_SOURCES = \
exchange_api_management_auditor_enable.c \ exchange_api_management_auditor_enable.c \
exchange_api_management_get_keys.c \ exchange_api_management_get_keys.c \
exchange_api_management_post_keys.c \ exchange_api_management_post_keys.c \
exchange_api_management_post_extensions.c \
exchange_api_management_revoke_denomination_key.c \ exchange_api_management_revoke_denomination_key.c \
exchange_api_management_revoke_signing_key.c \ exchange_api_management_revoke_signing_key.c \
exchange_api_management_set_wire_fee.c \ exchange_api_management_set_wire_fee.c \

View File

@ -0,0 +1,251 @@
/*
This file is part of TALER
Copyright (C) 2015-2021 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 lib/exchange_api_management_post_extensions.c
* @brief functions to handle the settings for extensions (p2p and age restriction)
* @author Özgür Kesim
*/
#include "platform.h"
#include "taler_json_lib.h"
#include <gnunet/gnunet_curl_lib.h>
#include "taler_extensions.h"
#include "taler_exchange_service.h"
#include "taler_signatures.h"
#include "taler_curl_lib.h"
#include "taler_json_lib.h"
/**
* @brief Handle for a POST /management/extensions request.
*/
struct TALER_EXCHANGE_ManagementPostExtensionsHandle
{
/**
* The url for this request.
*/
char *url;
/**
* Minor context that holds body and headers.
*/
struct TALER_CURL_PostContext post_ctx;
/**
* Handle for the request.
*/
struct GNUNET_CURL_Job *job;
/**
* Function to call with the result.
*/
TALER_EXCHANGE_ManagementPostExtensionsCallback cb;
/**
* Closure for @a cb.
*/
void *cb_cls;
/**
* Reference to the execution context.
*/
struct GNUNET_CURL_Context *ctx;
};
/**
* Function called when we're done processing the
* HTTP POST /management/extensions request.
*
* @param cls the `struct TALER_EXCHANGE_ManagementPostExtensionsHandle *`
* @param response_code HTTP response code, 0 on error
* @param response response body, NULL if not in JSON
*/
static void
handle_post_extensions_finished (void *cls,
long response_code,
const void *response)
{
struct TALER_EXCHANGE_ManagementPostExtensionsHandle *ph = cls;
const json_t *json = response;
struct TALER_EXCHANGE_HttpResponse hr = {
.http_status = (unsigned int) response_code,
.reply = json
};
ph->job = NULL;
switch (response_code)
{
case MHD_HTTP_NO_CONTENT:
break;
case MHD_HTTP_FORBIDDEN:
hr.ec = TALER_JSON_get_error_code (json);
hr.hint = TALER_JSON_get_error_hint (json);
break;
case MHD_HTTP_NOT_FOUND:
hr.ec = TALER_JSON_get_error_code (json);
hr.hint = TALER_JSON_get_error_hint (json);
break;
default:
/* unexpected response code */
GNUNET_break_op (0);
hr.ec = TALER_JSON_get_error_code (json);
hr.hint = TALER_JSON_get_error_hint (json);
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Unexpected response code %u/%d for exchange management post extensions\n",
(unsigned int) response_code,
(int) hr.ec);
break;
}
if (NULL != ph->cb)
{
ph->cb (ph->cb_cls,
&hr);
ph->cb = NULL;
}
TALER_EXCHANGE_post_management_extensions_cancel (ph);
}
struct TALER_EXCHANGE_ManagementPostExtensionsHandle *
TALER_EXCHANGE_management_post_extensions (
struct GNUNET_CURL_Context *ctx,
const char *url,
const struct TALER_EXCHANGE_ManagementPostExtensionsData *pkd,
TALER_EXCHANGE_ManagementPostKeysCallback cb,
void *cb_cls)
{
struct TALER_EXCHANGE_ManagementPostExtensionsHandle *ph;
// FIXME-oec: TODO!
CURL *eh = NULL;
json_t *body;
json_t *extensions = NULL;
json_t *extension_sigs = NULL;
ph = GNUNET_new (struct TALER_EXCHANGE_ManagementPostExtensionsHandle);
ph->cb = cb;
ph->cb_cls = cb_cls;
ph->ctx = ctx;
ph->url = TALER_url_join (url,
"management/extensions",
NULL);
if (NULL == ph->url)
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Could not construct request URL.\n");
GNUNET_free (ph);
return NULL;
}
extensions = json_array ();
GNUNET_assert (NULL != extensions);
for (unsigned int i = 0; i<pkd->num_extensions; i++)
{
json_t *config;
const struct TALER_AgeMask *mask;
const struct TALER_Extension *ext
= &pkd->extensions[i];
switch (ext->type)
{
// TODO: case TALER_Extension_Peer2Peer
case TALER_Extension_AgeRestriction:
mask = (struct TALER_AgeMask *) (&ext->config);
config = GNUNET_JSON_PACK (GNUNET_JSON_pack_data_auto ("mask",
&mask->mask));
GNUNET_assert (NULL != config);
break;
default:
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Extension not supported.\n");
}
GNUNET_assert (0 ==
json_array_append_new (
extensions,
GNUNET_JSON_PACK (
GNUNET_JSON_pack_data_auto ("extension",
&ext->name),
GNUNET_JSON_pack_data_auto ("config",
config)
)));
}
extension_sigs = json_array ();
GNUNET_assert (NULL != extension_sigs);
for (unsigned int i = 0; i<pkd->num_extensions; i++)
{
const struct TALER_MasterSignatureP *sks
= &pkd->extension_sigs[i];
GNUNET_assert (0 ==
json_array_append_new (
extension_sigs,
GNUNET_JSON_PACK (
GNUNET_JSON_pack_data_auto ("extension_sig",
&sks->eddsa_signature))));
}
body = GNUNET_JSON_PACK (
GNUNET_JSON_pack_array_steal ("extensions",
extensions),
GNUNET_JSON_pack_array_steal ("extension_sigs",
extension_sigs));
eh = curl_easy_init ();
GNUNET_assert (NULL != eh);
if (GNUNET_OK !=
TALER_curl_easy_post (&ph->post_ctx,
eh,
body))
{
GNUNET_break (0);
json_decref (body);
GNUNET_free (ph->url);
GNUNET_free (eh);
return NULL;
}
json_decref (body);
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Requesting URL '%s'\n",
ph->url);
GNUNET_assert (CURLE_OK == curl_easy_setopt (eh,
CURLOPT_URL,
ph->url));
ph->job = GNUNET_CURL_job_add2 (ctx,
eh,
ph->post_ctx.headers,
&handle_post_extensions_finished,
ph);
if (NULL == ph->job)
{
TALER_EXCHANGE_post_management_extensions_cancel (ph);
return NULL;
}
return ph;
}
void
TALER_EXCHANGE_post_management_extensions_cancel (
struct TALER_EXCHANGE_ManagementPostExtensionsHandle *ph)
{
if (NULL != ph->job)
{
GNUNET_CURL_job_cancel (ph->job);
ph->job = NULL;
}
TALER_curl_easy_post_finished (&ph->post_ctx);
GNUNET_free (ph->url);
GNUNET_free (ph);
}

View File

@ -31,12 +31,12 @@
* @return Error if extension for age restriction was set, but age groups were * @return Error if extension for age restriction was set, but age groups were
* invalid, OK otherwise. * invalid, OK otherwise.
*/ */
enum TALER_EXTENSION_ReturnValue enum TALER_Extension_ReturnValue
TALER_get_age_mask (const struct GNUNET_CONFIGURATION_Handle *cfg, TALER_get_age_mask (const struct GNUNET_CONFIGURATION_Handle *cfg,
struct TALER_AgeMask *mask) struct TALER_AgeMask *mask)
{ {
char *groups; char *groups;
enum TALER_EXTENSION_ReturnValue ret = TALER_EXTENSION_ERROR_SYS; enum TALER_Extension_ReturnValue ret = TALER_Extension_ERROR_SYS;
if ((GNUNET_NO == GNUNET_CONFIGURATION_have_value (cfg, if ((GNUNET_NO == GNUNET_CONFIGURATION_have_value (cfg,
TALER_EXTENSION_SECTION_AGE_RESTRICTION, TALER_EXTENSION_SECTION_AGE_RESTRICTION,
@ -47,7 +47,7 @@ TALER_get_age_mask (const struct GNUNET_CONFIGURATION_Handle *cfg,
{ {
/* Age restriction is not enabled */ /* Age restriction is not enabled */
mask->mask = 0; mask->mask = 0;
return TALER_EXTENSION_OK; return TALER_Extension_OK;
} }
/* Age restriction is enabled, extract age groups */ /* Age restriction is enabled, extract age groups */
@ -57,13 +57,13 @@ TALER_get_age_mask (const struct GNUNET_CONFIGURATION_Handle *cfg,
&groups)) &groups))
{ {
/* FIXME: log error? */ /* FIXME: log error? */
return TALER_EXTENSION_ERROR_SYS; return TALER_Extension_ERROR_SYS;
} }
if (groups == NULL) if (groups == NULL)
{ {
/* No groups defined in config, return default_age_mask */ /* No groups defined in config, return default_age_mask */
mask->mask = TALER_EXTENSION_DEFAULT_AGE_MASK; mask->mask = TALER_EXTENSION_DEFAULT_AGE_MASK;
return TALER_EXTENSION_OK; return TALER_Extension_OK;
} }
ret = TALER_parse_age_group_string (groups, mask); ret = TALER_parse_age_group_string (groups, mask);
@ -80,11 +80,11 @@ TALER_get_age_mask (const struct GNUNET_CONFIGURATION_Handle *cfg,
* @param[out] mask Bit representation of the age groups. * @param[out] mask Bit representation of the age groups.
* @return Error if string was invalid, OK otherwise. * @return Error if string was invalid, OK otherwise.
*/ */
enum TALER_EXTENSION_ReturnValue enum TALER_Extension_ReturnValue
TALER_parse_age_group_string (char *groups, TALER_parse_age_group_string (char *groups,
struct TALER_AgeMask *mask) struct TALER_AgeMask *mask)
{ {
enum TALER_EXTENSION_ReturnValue ret = TALER_EXTENSION_ERROR_SYS; enum TALER_Extension_ReturnValue ret = TALER_Extension_ERROR_SYS;
char *pos; char *pos;
unsigned int prev = 0; unsigned int prev = 0;
unsigned int val; unsigned int val;
@ -105,14 +105,14 @@ TALER_parse_age_group_string (char *groups,
{ {
/* Invalid input */ /* Invalid input */
mask->mask = 0; mask->mask = 0;
ret = TALER_EXTENSION_ERROR_PARSING; ret = TALER_Extension_ERROR_PARSING;
break; break;
} }
else if ((0 >= val) || (32 <= val) || (prev >= val)) else if ((0 >= val) || (32 <= val) || (prev >= val))
{ {
/* Invalid value */ /* Invalid value */
mask->mask = 0; mask->mask = 0;
ret = TALER_EXTENSION_ERROR_INVALID; ret = TALER_Extension_ERROR_INVALID;
break; break;
} }
@ -123,7 +123,7 @@ TALER_parse_age_group_string (char *groups,
{ {
/* We reached the end. Mark zeroth age-group and exit. */ /* We reached the end. Mark zeroth age-group and exit. */
mask->mask |= 1; mask->mask |= 1;
ret = TALER_EXTENSION_OK; ret = TALER_Extension_OK;
break; break;
} }

View File

@ -232,8 +232,8 @@ TES_read_work (void *cls,
struct TES_Client *client = cls; struct TES_Client *client = cls;
char *buf = client->iobuf; char *buf = client->iobuf;
size_t off = 0; size_t off = 0;
uint16_t msize; uint16_t msize = 0;
const struct GNUNET_MessageHeader *hdr; const struct GNUNET_MessageHeader *hdr = NULL;
enum GNUNET_GenericReturnValue ret; enum GNUNET_GenericReturnValue ret;
do do