diff options
author | Christian Grothoff <christian@grothoff.org> | 2020-12-14 17:12:25 +0100 |
---|---|---|
committer | Christian Grothoff <christian@grothoff.org> | 2020-12-14 17:12:48 +0100 |
commit | 04f2e9a4d5b64000b5e29b4603c9bc57c732c4f7 (patch) | |
tree | 5022fd75fe7dd2facd939c71f39657fab7964da9 | |
parent | b5d88fc2d1832fd27bdd7df0860c07ae3c61312c (diff) |
(start to) remove logic no longer needed with new key management
42 files changed, 799 insertions, 3836 deletions
diff --git a/src/exchange/Makefile.am b/src/exchange/Makefile.am index bea212ede..037237524 100644 --- a/src/exchange/Makefile.am +++ b/src/exchange/Makefile.am @@ -83,8 +83,8 @@ taler_exchange_httpd_SOURCES = \ 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_keys.c taler-exchange-httpd_keys.h \ - taler-exchange-httpd_keystate.c taler-exchange-httpd_keystate.h \ taler-exchange-httpd_link.c taler-exchange-httpd_link.h \ + taler-exchange-httpd_loop.c taler-exchange-httpd_loop.h \ taler-exchange-httpd_management.h \ taler-exchange-httpd_management_auditors.c \ taler-exchange-httpd_management_auditors_AP_disable.c \ @@ -103,7 +103,7 @@ taler_exchange_httpd_SOURCES = \ taler-exchange-httpd_responses.c taler-exchange-httpd_responses.h \ taler-exchange-httpd_terms.c taler-exchange-httpd_terms.h \ taler-exchange-httpd_transfers_get.c taler-exchange-httpd_transfers_get.h \ - taler-exchange-httpd_wire.c taler-exchange-httpd_wire.h \ + taler-exchange-httpd_wire2.c taler-exchange-httpd_wire.h \ taler-exchange-httpd_withdraw.c taler-exchange-httpd_withdraw.h # taler-exchange-httpd_management_post_keys.c diff --git a/src/exchange/taler-exchange-httpd.c b/src/exchange/taler-exchange-httpd.c index 8ee82ce19..19bba0b8b 100644 --- a/src/exchange/taler-exchange-httpd.c +++ b/src/exchange/taler-exchange-httpd.c @@ -31,9 +31,9 @@ #include "taler-exchange-httpd_auditors.h" #include "taler-exchange-httpd_deposit.h" #include "taler-exchange-httpd_deposits_get.h" -#include "taler-exchange-httpd_keystate.h" #include "taler-exchange-httpd_keys.h" #include "taler-exchange-httpd_link.h" +#include "taler-exchange-httpd_loop.h" #include "taler-exchange-httpd_management.h" #include "taler-exchange-httpd_melt.h" #include "taler-exchange-httpd_mhd.h" @@ -45,6 +45,7 @@ #include "taler-exchange-httpd_transfers_get.h" #include "taler-exchange-httpd_wire.h" #include "taler-exchange-httpd_withdraw.h" +#include "taler_exchangedb_lib.h" #include "taler_exchangedb_plugin.h" #include <gnunet/gnunet_mhd_compat.h> @@ -1158,21 +1159,11 @@ exchange_serve_process_config (void) "Launching exchange with public key `%s'...\n", GNUNET_p2s (&TEH_master_public_key.eddsa_pub)); - if (GNUNET_OK != - TEH_WIRE_init (TEH_cfg)) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Failed to setup wire subsystem\n"); - return GNUNET_SYSERR; - } - - if (NULL == (TEH_plugin = TALER_EXCHANGEDB_plugin_load (TEH_cfg))) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to initialize DB subsystem\n"); - TEH_WIRE_done (); return GNUNET_SYSERR; } @@ -1185,7 +1176,6 @@ exchange_serve_process_config (void) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to setup HTTPd subsystem\n"); - TEH_WIRE_done (); return GNUNET_SYSERR; } return GNUNET_OK; @@ -1489,8 +1479,8 @@ run_main_loop (int fh, ret = TEH_keys_init (); if (GNUNET_OK == ret) { - ret = TEH_KS_loop (); - TEH_keys_done (); + ret = TEH_loop_run (); + TEH_loop_done (); } switch (ret) { @@ -1703,7 +1693,10 @@ main (int argc, } /* initialize #internal_key_state with an RC of 1 */ - ret = TEH_KS_init (); + if (GNUNET_OK != + TEH_WIRE_init ()) + return 42; + ret = TEH_loop_init (); if (GNUNET_OK == ret) { #if HAVE_DEVELOPER @@ -1726,8 +1719,8 @@ main (int argc, ret = run_main_loop (fh, argv); } - /* release #internal_key_state */ - TEH_KS_free (); + /* release signal handlers */ + TEH_loop_done (); } TALER_EXCHANGEDB_plugin_unload (TEH_plugin); TEH_WIRE_done (); diff --git a/src/exchange/taler-exchange-httpd_db.c b/src/exchange/taler-exchange-httpd_db.c index 14a5b5d5b..8574224af 100644 --- a/src/exchange/taler-exchange-httpd_db.c +++ b/src/exchange/taler-exchange-httpd_db.c @@ -25,7 +25,7 @@ #include "taler_json_lib.h" #include "taler_mhd_lib.h" #include "taler-exchange-httpd_responses.h" -#include "taler-exchange-httpd_keystate.h" + /** * How often should we retry a transaction before giving up diff --git a/src/exchange/taler-exchange-httpd_deposit.c b/src/exchange/taler-exchange-httpd_deposit.c index e8ca04f8c..2f7d49c93 100644 --- a/src/exchange/taler-exchange-httpd_deposit.c +++ b/src/exchange/taler-exchange-httpd_deposit.c @@ -32,7 +32,7 @@ #include "taler_mhd_lib.h" #include "taler-exchange-httpd_deposit.h" #include "taler-exchange-httpd_responses.h" -#include "taler-exchange-httpd_keystate.h" +#include "taler_exchangedb_lib.h" #include "taler-exchange-httpd_keys.h" @@ -430,29 +430,17 @@ TEH_handler_deposit (struct MHD_Connection *connection, (void) GNUNET_TIME_round_abs (&dc.exchange_timestamp); /* check denomination exists and is valid */ { - struct TEH_KS_StateHandle *key_state; struct TEH_DenominationKey *dk; enum TALER_ErrorCode ec; unsigned int hc; struct GNUNET_TIME_Absolute now; - key_state = TEH_KS_acquire (dc.exchange_timestamp); - if (NULL == key_state) - { - TALER_LOG_ERROR ("Lacking keys to operate\n"); - GNUNET_JSON_parse_free (spec); - return TALER_MHD_reply_with_error (connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_EXCHANGE_GENERIC_BAD_CONFIGURATION, - "no keys"); - } dk = TEH_keys_denomination_by_hash (&deposit.coin.denom_pub_hash, &ec, &hc); if (NULL == dk) { TALER_LOG_DEBUG ("Unknown denomination key in /deposit request\n"); - TEH_KS_release (key_state); GNUNET_JSON_parse_free (spec); return TALER_MHD_reply_with_error (connection, hc, @@ -463,7 +451,6 @@ TEH_handler_deposit (struct MHD_Connection *connection, if (now.abs_value_us >= dk->meta.expire_deposit.abs_value_us) { /* This denomination is past the expiration time for deposits */ - TEH_KS_release (key_state); GNUNET_JSON_parse_free (spec); return TALER_MHD_reply_with_error ( connection, @@ -474,7 +461,6 @@ TEH_handler_deposit (struct MHD_Connection *connection, if (now.abs_value_us < dk->meta.start.abs_value_us) { /* This denomination is not yet valid */ - TEH_KS_release (key_state); GNUNET_JSON_parse_free (spec); return TALER_MHD_reply_with_error ( connection, @@ -485,7 +471,6 @@ TEH_handler_deposit (struct MHD_Connection *connection, if (dk->recoup_possible) { /* This denomination has been revoked */ - TEH_KS_release (key_state); GNUNET_JSON_parse_free (spec); return TALER_MHD_reply_with_error ( connection, @@ -500,7 +485,6 @@ TEH_handler_deposit (struct MHD_Connection *connection, &deposit.deposit_fee) ) { GNUNET_break_op (0); - TEH_KS_release (key_state); GNUNET_JSON_parse_free (spec); return TALER_MHD_reply_with_error (connection, MHD_HTTP_BAD_REQUEST, @@ -513,7 +497,6 @@ TEH_handler_deposit (struct MHD_Connection *connection, &dk->denom_pub)) { TALER_LOG_WARNING ("Invalid coin passed for /deposit\n"); - TEH_KS_release (key_state); GNUNET_JSON_parse_free (spec); return TALER_MHD_reply_with_error (connection, MHD_HTTP_UNAUTHORIZED, @@ -521,7 +504,6 @@ TEH_handler_deposit (struct MHD_Connection *connection, NULL); } dc.value = dk->meta.value; - TEH_KS_release (key_state); } if (0 < TALER_amount_cmp (&deposit.deposit_fee, &deposit.amount_with_fee)) diff --git a/src/exchange/taler-exchange-httpd_deposits_get.c b/src/exchange/taler-exchange-httpd_deposits_get.c index a4932a1ed..90f28b4e3 100644 --- a/src/exchange/taler-exchange-httpd_deposits_get.c +++ b/src/exchange/taler-exchange-httpd_deposits_get.c @@ -26,7 +26,6 @@ #include "taler_json_lib.h" #include "taler_mhd_lib.h" #include "taler_signatures.h" -#include "taler-exchange-httpd_keystate.h" #include "taler-exchange-httpd_keys.h" #include "taler-exchange-httpd_deposits_get.h" #include "taler-exchange-httpd_responses.h" diff --git a/src/exchange/taler-exchange-httpd_keystate.c b/src/exchange/taler-exchange-httpd_keystate.c deleted file mode 100644 index 8d5a18510..000000000 --- a/src/exchange/taler-exchange-httpd_keystate.c +++ /dev/null @@ -1,2587 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2014--2020 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_keystate.c - * @brief management of our coin signing keys - * @author Florian Dold - * @author Benedikt Mueller - * @author Christian Grothoff - */ -#include "platform.h" -#include <pthread.h> -#include "taler_json_lib.h" -#include "taler_mhd_lib.h" -#include "taler-exchange-httpd_keystate.h" -#include "taler-exchange-httpd_responses.h" -#include "taler_exchangedb_plugin.h" - - -/** - * Taler protocol version in the format CURRENT:REVISION:AGE - * as used by GNU libtool. See - * https://www.gnu.org/software/libtool/manual/html_node/Libtool-versioning.html - * - * Please be very careful when updating and follow - * https://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html#Updating-version-info - * precisely. Note that this version has NOTHING to do with the - * release version, and the format is NOT the same that semantic - * versioning uses either. - * - * When changing this version, you likely want to also update - * #TALER_PROTOCOL_CURRENT and #TALER_PROTOCOL_AGE in - * exchange_api_handle.c! - */ -#define EXCHANGE_PROTOCOL_VERSION "8:0:0" - - -/** - * Signatures of an auditor over a denomination key of this exchange. - */ -struct AuditorSignature -{ - /** - * We store the signatures in a DLL. - */ - struct AuditorSignature *prev; - - /** - * We store the signatures in a DLL. - */ - struct AuditorSignature *next; - - /** - * A signature from the auditor. - */ - struct TALER_AuditorSignatureP asig; - - /** - * Public key of the auditor. - */ - struct TALER_AuditorPublicKeyP apub; - - /** - * URL of the auditor. Allocated at the end of this struct. - */ - const char *auditor_url; - -}; - - -/** - * Entry in sorted array of denomination keys. Sorted by starting - * "start" time (validity period) of the `struct - * TALER_DenominationKeyValidityPS`. - */ -struct DenominationKeyEntry -{ - - /** - * Reference to the public key. - * (Must also be in the `denomkey_map`). - */ - const struct TALER_EXCHANGEDB_DenominationKey *dki; - - /** - * Head of DLL of signatures for this @e dki. - */ - struct AuditorSignature *as_head; - - /** - * Tail of DLL of signatures for this @e dki. - */ - struct AuditorSignature *as_tail; - - /** - * Hash of the public denomination key. - */ - struct GNUNET_HashCode denom_key_hash; - -}; - - -/** - * Entry in (sorted) array with possible pre-build responses for /keys. - * We keep pre-build responses for the various (valid) cherry-picking - * values around. - */ -struct KeysResponseData -{ - - /** - * Response to return if the client supports (deflate) compression. - */ - struct MHD_Response *response_compressed; - - /** - * Response to return if the client does not support compression. - */ - struct MHD_Response *response_uncompressed; - - /** - * Cherry-picking timestamp the client must have set for this - * response to be valid. 0 if this is the "full" response. - * The client's request must include this date or a higher one - * for this response to be applicable. - */ - struct GNUNET_TIME_Absolute cherry_pick_date; - -}; - - -/** - * State we keep around while building an individual entry in the - * `struct KeysResponseData` array, i.e. the global state for ONE of - * the responses. - */ -struct ResponseBuilderContext -{ - - /** - * Hash context we used to combine the hashes of all denomination - * keys into one big hash for signing. - */ - struct GNUNET_HashContext *hash_context; - - /** - * JSON array with denomination key information. - */ - json_t *denom_keys_array; - - /** - * JSON array with auditor information. - */ - json_t *auditors_array; - - /** - * Keys after what issue date do we care about? - */ - struct GNUNET_TIME_Absolute last_issue_date; - - /** - * Flag set to #GNUNET_SYSERR on internal errors - */ - int error; - -}; - - -/** - * State we keep around while building the `struct KeysResponseData` - * array, i.e. the global state for all of the responses. - */ -struct ResponseFactoryContext -{ - - /** - * JSON array with revoked denomination keys. Every response - * always returns the full list (cherry picking does not apply - * for key revocations, as we cannot sort those by issue date). - */ - json_t *recoup_array; - - /** - * JSON array with signing keys. Every response includes the full - * list, as it should be quite short anyway, and for simplicity the - * client only communicates the one time stamp of the last - * denomination key it knows when cherry picking. - */ - json_t *sign_keys_array; - - /** - * Sorted array of denomination keys. Length is @e denomkey_array_length. - * Entries are sorted by the validity period's starting time. - */ - struct DenominationKeyEntry *denomkey_array; - - /** - * The main key state we are building everything for. - */ - struct TEH_KS_StateHandle *key_state; - - /** - * Time stamp used as "now". - */ - struct GNUNET_TIME_Absolute now; - - /** - * Length of the @e denomkey_array. - */ - unsigned int denomkey_array_length; -}; - - -/** - * Snapshot of the (coin and signing) keys (including private keys) of - * the exchange. There can be multiple instances of this struct, as it is - * reference counted and only destroyed once the last user is done - * with it. The current instance is acquired using - * #TEH_KS_acquire(). Using this function increases the - * reference count. The contents of this structure (except for the - * reference counter) should be considered READ-ONLY until it is - * ultimately destroyed (as there can be many concurrent users). - */ -struct TEH_KS_StateHandle -{ - - /** - * Mapping from denomination keys to denomination key issue struct. - * Used to lookup the key by hash. - */ - struct GNUNET_CONTAINER_MultiHashMap *denomkey_map; - - /** - * Mapping from revoked denomination keys to denomination key issue struct. - * Used to lookup the key by hash. - */ - struct GNUNET_CONTAINER_MultiHashMap *revoked_map; - - /** - * Sorted array of responses to /keys (MUST be sorted by cherry-picking date) of - * length @e krd_array_length; - */ - struct KeysResponseData *krd_array; - - /** - * When did we initiate the key reloading? - */ - struct GNUNET_TIME_Absolute reload_time; - - /** - * When is the next key invalid and we have to reload? (We also - * reload on SIGUSR1.) - */ - struct GNUNET_TIME_Absolute next_reload; - - /** - * When does the first active denomination key expire (for deposit)? - */ - struct GNUNET_TIME_Absolute min_dk_expire; - - /** - * Exchange signing key that should be used currently. - */ - struct TALER_EXCHANGEDB_PrivateSigningKeyInformationP current_sign_key_issue; - - /** - * Reference count. The struct is released when the RC hits zero. Once - * this object is aliased, the reference counter must only be changed while - * holding the #internal_key_state_mutex. - */ - unsigned int refcnt; - - /** - * Length of the @e krd_array. - */ - unsigned int krd_array_length; -}; - - -/** - * Exchange key state. This is the long-term, read-only internal global state, - * which the various threads "lock" to use in read-only ways. We eventually - * create a completely new object "on the side" and then start to return - * the new read-only object to threads that ask. Once none of the threads - * use the previous object (RC drops to zero), we discard it. - * - * Thus, this instance should never be used directly, instead reserve - * access via #TEH_KS_acquire() and release it via #TEH_KS_release(). - * - * As long as MHD threads are running, access to this field requires - * locking the #internal_key_state_mutex. - */ -static struct TEH_KS_StateHandle *internal_key_state; - -/** - * Mutex protecting access to #internal_key_state. - */ -static pthread_mutex_t internal_key_state_mutex = PTHREAD_MUTEX_INITIALIZER; - -/** - * Configuration value LOOKAHEAD_PROVIDE that says for how far in the - * future we want to provide exchange key information to clients. - */ -static struct GNUNET_TIME_Relative conf_duration_provide; - - -/* ************************** Clean up logic *********************** */ - - -/** - * Release memory used by @a rfc. - * - * @param rfc factory to release (but do not #GNUNET_free() rfc itself!) - */ -static void -destroy_response_factory (struct ResponseFactoryContext *rfc) -{ - if (NULL != rfc->recoup_array) - { - json_decref (rfc->recoup_array); - rfc->recoup_array = NULL; - } - if (NULL != rfc->sign_keys_array) - { - json_decref (rfc->sign_keys_array); - rfc->sign_keys_array = NULL; - } - for (unsigned int i = 0; i<rfc->denomkey_array_length; i++) - { - struct DenominationKeyEntry *dke = &rfc->denomkey_array[i]; - struct AuditorSignature *as; - - while (NULL != (as = dke->as_head)) - { - GNUNET_CONTAINER_DLL_remove (dke->as_head, - dke->as_tail, - as); - GNUNET_free (as); - } - } - GNUNET_array_grow (rfc->denomkey_array, - rfc->denomkey_array_length, - 0); -} - - -/** - * Release memory used by @a rbc. - * - * @param rbc memory to release, excluding @a rbc itself - */ -static void -destroy_response_builder (struct ResponseBuilderContext *rbc) -{ - if (NULL != rbc->denom_keys_array) - { - json_decref (rbc->denom_keys_array); - rbc->denom_keys_array = NULL; - } - if (NULL != rbc->auditors_array) - { - json_decref (rbc->auditors_array); - rbc->auditors_array = NULL; - } - if (NULL != rbc->hash_context) - { - GNUNET_CRYPTO_hash_context_abort (rbc->hash_context); - rbc->hash_context = NULL; - } -} - - -/** - * Iterator for freeing denomination keys. - * - * @param cls closure with the `struct TEH_KS_StateHandle` (unused) - * @param key hash of the denomination key (unused) - * @param value coin details, a `struct TALER_EXCHANGEDB_DenominationKey` - * @return #GNUNET_OK to continue to iterate, - * #GNUNET_NO to stop iteration with no error, - * #GNUNET_SYSERR to abort iteration with error! - */ -static int -free_denom_key (void *cls, - const struct GNUNET_HashCode *key, - void *value) -{ - struct TALER_EXCHANGEDB_DenominationKey *dki = value; - - (void) cls; - (void) key; - if (NULL != dki->denom_priv.rsa_private_key) - GNUNET_CRYPTO_rsa_private_key_free (dki->denom_priv.rsa_private_key); - GNUNET_CRYPTO_rsa_public_key_free (dki->denom_pub.rsa_public_key); - GNUNET_free (dki); - return GNUNET_OK; -} - - -/** - * Internal function to free key state. Reference count must be at zero. - * - * @param key_state the key state to free - */ -static void -ks_free (struct TEH_KS_StateHandle *key_state) -{ - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "KS release called (%p)\n", - key_state); - GNUNET_assert (0 == key_state->refcnt); - if (NULL != key_state->denomkey_map) - { - GNUNET_CONTAINER_multihashmap_iterate (key_state->denomkey_map, - &free_denom_key, - key_state); - GNUNET_CONTAINER_multihashmap_destroy (key_state->denomkey_map); - key_state->denomkey_map = NULL; - } - if (NULL != key_state->revoked_map) - { - GNUNET_CONTAINER_multihashmap_iterate (key_state->revoked_map, - &free_denom_key, - key_state); - GNUNET_CONTAINER_multihashmap_destroy (key_state->revoked_map); - key_state->revoked_map = NULL; - } - for (unsigned int i = 0; i<key_state->krd_array_length; i++) - { - struct KeysResponseData *krd = &key_state->krd_array[i]; - - if (NULL != krd->response_compressed) - MHD_destroy_response (krd->response_compressed); - if (NULL != krd->response_uncompressed) - MHD_destroy_response (krd->response_uncompressed); - } - GNUNET_array_grow (key_state->krd_array, - key_state->krd_array_length, - 0); - GNUNET_free (key_state); -} - - -/* ************************* Signal logic ************************** */ - -/** - * Pipe used for signaling reloading of our key state. - */ -static int reload_pipe[2] = { -1, -1 }; - - -/** - * Handle a signal, writing relevant signal numbers to the pipe. - * - * @param signal_number the signal number - */ -static void -handle_signal (int signal_number) -{ - char c = (char) signal_number; /* never seen a signal_number > 127 on any platform */ - - (void) ! write (reload_pipe[1], - &c, - 1); - /* While one might like to "handle errors" here, even logging via fprintf() - isn't safe inside of a signal handler. So there is nothing we safely CAN - do. OTOH, also very little that can go wrong in practice. Calling _exit() - on errors might be a possibility, but that might do more harm than good. */// -} - - -/* ************************** State builder ************************ */ - - -/** - * Convert the public part of denomination key data to a JSON object. - * - * @param pk public key of the denomination - * @param dki the denomination key issue information - * @return a JSON object describing the denomination key issue (public part) - */ -static json_t * -denom_key_issue_to_json ( - const struct TALER_DenominationPublicKey *pk, - const struct TALER_EXCHANGEDB_DenominationKeyInformationP *dki) -{ - struct TALER_Amount value; - struct TALER_Amount fee_withdraw; - struct TALER_Amount fee_deposit; - struct TALER_Amount fee_refresh; - struct TALER_Amount fee_refund; - - TALER_amount_ntoh (&value, - &dki->properties.value); - TALER_amount_ntoh (&fee_withdraw, - &dki->properties.fee_withdraw); - TALER_amount_ntoh (&fee_deposit, - &dki->properties.fee_deposit); - TALER_amount_ntoh (&fee_refresh, - &dki->properties.fee_refresh); - TALER_amount_ntoh (&fee_refund, - &dki->properties.fee_refund); - return - json_pack ("{s:o, s:o, s:o, s:o, s:o," - " s:o, s:o, s:o, s:o, s:o," - " s:o}", - "master_sig", - GNUNET_JSON_from_data_auto (&dki->signature), - "stamp_start", - GNUNET_JSON_from_time_abs (GNUNET_TIME_absolute_ntoh ( - dki->properties.start)), - "stamp_expire_withdraw", - GNUNET_JSON_from_time_abs (GNUNET_TIME_absolute_ntoh ( - dki->properties.expire_withdraw)), - "stamp_expire_deposit", - GNUNET_JSON_from_time_abs (GNUNET_TIME_absolute_ntoh ( - dki->properties.expire_deposit)), - "stamp_expire_legal", - GNUNET_JSON_from_time_abs (GNUNET_TIME_absolute_ntoh ( - dki->properties.expire_legal)), - /* 5 entries until here */ - "denom_pub", - GNUNET_JSON_from_rsa_public_key (pk->rsa_public_key), - "value", - TALER_JSON_from_amount (&value), - "fee_withdraw", - TALER_JSON_from_amount (&fee_withdraw), - "fee_deposit", - TALER_JSON_from_amount (&fee_deposit), - "fee_refresh", - TALER_JSON_from_amount (&fee_refresh), - /* 10 entries until here */ - "fee_refund", - TALER_JSON_from_amount (&fee_refund)); -} - - -/** - * Store a copy of @a dki in @a map. - * - * @param map hash map to store @a dki in - * @param dki information to store in @a map - * @return #GNUNET_OK on success, - * #GNUNET_NO if such an entry already exists - */ -static int -store_in_map (struct GNUNET_CONTAINER_MultiHashMap *map, - const struct TALER_EXCHANGEDB_DenominationKey *dki) -{ - /* First, we verify that the @a dki is actually well-formed. While it comes - from our own hard disk, there is the possibility of misconfiguration - (i.e. bogus file in the directory), or that the administrator used the - wrong master public key, and we should not accept entries that are not - well-formed. */// - { - const struct TALER_EXCHANGEDB_DenominationKeyInformationP *dkip; - struct TALER_DenominationKeyValidityPS denom_key_issue; - - dkip = &dki->issue; - denom_key_issue = dkip->properties; - /* The above is straight from our disk. We should not trust - that it is well-formed, so we setup crucial fields ourselves. */ - denom_key_issue.purpose.purpose - = htonl (TALER_SIGNATURE_MASTER_DENOMINATION_KEY_VALIDITY); - denom_key_issue.purpose.size - = htonl (sizeof (struct TALER_DenominationKeyValidityPS)); - denom_key_issue.master = TEH_master_public_key; - - /* Check that the data we read matches our expectations */ - if (0 != - GNUNET_memcmp (&denom_key_issue, - &dkip->properties)) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Invalid fields in denomination key `%s'\n", - GNUNET_h2s (&dkip->properties.denom_hash)); - return GNUNET_SYSERR; - } - - /* Also check the signature by the master public key */ - if (GNUNET_SYSERR == - GNUNET_CRYPTO_eddsa_verify ( - TALER_SIGNATURE_MASTER_DENOMINATION_KEY_VALIDITY, - &denom_key_issue, - &dkip->signature.eddsa_signature, - &TEH_master_public_key.eddsa_pub)) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Invalid signature on denomination key `%s'\n", - GNUNET_h2s (&dkip->properties.denom_hash)); - return GNUNET_SYSERR; - } - } - - /* We need to make a deep copy of the @a dki, as the original was allocated - elsewhere and will be freed by the caller. */ - { - struct TALER_EXCHANGEDB_DenominationKey *d2; - - d2 = GNUNET_new (struct TALER_EXCHANGEDB_DenominationKey); - d2->issue = dki->issue; - if (GNUNET_OK != - GNUNET_CONTAINER_multihashmap_put (map, - &d2->issue.properties.denom_hash, - d2, - GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)) - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Duplicate denomination key `%s'\n", - GNUNET_h2s (&d2->issue.properties.denom_hash)); - GNUNET_free (d2); - return GNUNET_NO; - } - - /* finish *deep* part of deep copy */ - if (NULL != dki->denom_priv.rsa_private_key) - { - /* we might be past the withdraw period, so private key could have been deleted, - so only try to (deep) copy if non-NULL. */ - d2->denom_priv.rsa_private_key - = GNUNET_CRYPTO_rsa_private_key_dup (dki->denom_priv.rsa_private_key); - } - d2->denom_pub.rsa_public_key - = GNUNET_CRYPTO_rsa_public_key_dup (dki->denom_pub.rsa_public_key); - } - return GNUNET_OK; -} - - -/** - * Closure for #add_revocations_transaction(). - */ -struct AddRevocationContext -{ - /** - * Denomination key that is revoked. - */ - const struct TALER_EXCHANGEDB_DenominationKey *dki; - - /** - * Signature affirming the revocation. - */ - const struct TALER_MasterSignatureP *revocation_master_sig; -}; - - -/** - * Execute transaction to add revocations. - * - * @param cls closure with the `struct AddRevocationContext *` - * @param connection NULL - * @param session database session to use - * @param[out] mhd_ret not used - * @return transaction status - */ -static enum GNUNET_DB_QueryStatus -add_revocations_transaction (void *cls, - struct MHD_Connection *connection, - struct TALER_EXCHANGEDB_Session *session, - MHD_RESULT *mhd_ret) -{ - struct AddRevocationContext *arc = cls; - enum GNUNET_DB_QueryStatus qs; - struct TALER_MasterSignatureP master_sig; - uint64_t rowid; - - (void) connection; - (void) mhd_ret; - qs = TEH_plugin->get_denomination_revocation (TEH_plugin->cls, - session, - &arc->dki->issue.properties. - denom_hash, - &master_sig, - &rowid); - if (0 > qs) - return qs; /* failure */ - if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs) - return qs; /* already exists == success */ - return TEH_plugin->insert_denomination_revocation (TEH_plugin->cls, - session, - &arc->dki->issue.properties - .denom_hash, - arc->revocation_master_sig); -} - - -/** - * Execute transaction to add a denomination to the DB. - * - * @param cls closure with the `const struct TALER_EXCHANGEDB_DenominationKey *` - * @param connection NULL - * @param session database session to use - * @param[out] mhd_ret not used - * @return transaction status - */ -static enum GNUNET_DB_QueryStatus -add_denomination_transaction (void *cls, - struct MHD_Connection *connection, - struct TALER_EXCHANGEDB_Session *session, - MHD_RESULT *mhd_ret) -{ - const struct TALER_EXCHANGEDB_DenominationKey *dki = cls; - enum GNUNET_DB_QueryStatus qs; - struct TALER_EXCHANGEDB_DenominationKeyInformationP issue_exists; - - (void) connection; - (void) mhd_ret; - qs = TEH_plugin->get_denomination_info (TEH_plugin->cls, - session, - &dki->issue.properties.denom_hash, - &issue_exists); - if (0 > qs) - return qs; - if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs) - return qs; - return TEH_plugin->insert_denomination_info (TEH_plugin->cls, - session, - &dki->denom_pub, - &dki->issue); -} - - -/** - * Iterator for (re)loading/initializing denomination keys. - * - * @param cls closure with a `struct ResponseFactoryContext *` - * @param dki the denomination key issue - * @param alias coin alias - * @return #GNUNET_OK to continue to iterate, - * #GNUNET_NO to stop iteration with no error, - * #GNUNET_SYSERR to abort iteration with error! - */ -static int -reload_keys_denom_iter (void *cls, - const char *alias, - const struct TALER_EXCHANGEDB_DenominationKey *dki) -{ - struct ResponseFactoryContext *rfc = cls; - struct TEH_KS_StateHandle *key_state = rfc->key_state; - struct GNUNET_TIME_Absolute start; - struct GNUNET_TIME_Absolute horizon; - struct GNUNET_TIME_Absolute expire_deposit; - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Loading denomination key `%s' (%s)\n", - alias, - GNUNET_h2s (&dki->issue.properties.denom_hash)); - expire_deposit = GNUNET_TIME_absolute_ntoh ( - dki->issue.properties.expire_deposit); - if (expire_deposit.abs_value_us < rfc->now.abs_value_us) - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Skipping expired denomination key `%s'\n", - alias); - return GNUNET_OK; - } - if (0 != GNUNET_memcmp (&dki->issue.properties.master, - &TEH_master_public_key)) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Master key in denomination key file `%s' does not match! Skipping it.\n", - alias); - return GNUNET_OK; - } - - horizon = GNUNET_TIME_absolute_add (rfc->now, - conf_duration_provide); - start = GNUNET_TIME_absolute_ntoh (dki->issue.properties.start); - if (start.abs_value_us > horizon.abs_value_us) - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Skipping future denomination key `%s' (%s), validity starts at %s\n", - alias, - GNUNET_h2s (&dki->issue.properties.denom_hash), - GNUNET_STRINGS_absolute_time_to_string (start)); - return GNUNET_OK; - } - - if (GNUNET_OK != - TEH_DB_run_transaction (NULL, - "add denomination key", - NULL, - &add_denomination_transaction, - (void *) dki)) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Could not persist denomination key %s in DB. Committing suicide via SIGTERM.\n", - GNUNET_h2s (&dki->issue.properties.denom_hash)); - handle_signal (SIGTERM); - return GNUNET_SYSERR; - } - GNUNET_assert (NULL != dki->denom_priv.rsa_private_key); - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Adding denomination key `%s' (%s) to active set\n", - alias, - GNUNET_h2s (&dki->issue.properties.denom_hash)); - if (GNUNET_NO /* entry already exists */ == - store_in_map (key_state->denomkey_map, - dki)) - return GNUNET_OK; /* do not update expiration if entry exists */ - key_state->min_dk_expire = GNUNET_TIME_absolute_min (key_state->min_dk_expire, - expire_deposit); - return GNUNET_OK; -} - - -/** - * Iterator for revocation of denomination keys. - * - * @param cls closure with a `struct ResponseFactoryContext *` - * @param denom_hash hash of revoked denomination public key - * @param revocation_master_sig signature showing @a denom_hash was revoked - * @return #GNUNET_OK to continue to iterate, - * #GNUNET_NO to stop iteration with no error, - * #GNUNET_SYSERR to abort iteration with error! - */ -static int -revocations_iter (void *cls, - const struct GNUNET_HashCode *denom_hash, - const struct TALER_MasterSignatureP *revocation_master_sig) -{ - struct ResponseFactoryContext *rfc = cls; - struct TEH_KS_StateHandle *key_state = rfc->key_state; - struct AddRevocationContext arc; - struct TALER_EXCHANGEDB_DenominationKey *dki; - - dki = GNUNET_CONTAINER_multihashmap_get (key_state->denomkey_map, - denom_hash); - if (NULL == dki) - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Revoked denomination `%s' unknown (or duplicate file), ignoring revocation\n", - GNUNET_h2s (denom_hash)); - return GNUNET_OK; - - } - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Adding denomination key `%s' to revocation set\n", - GNUNET_h2s (denom_hash)); - GNUNET_assert (GNUNET_YES == - GNUNET_CONTAINER_multihashmap_remove (key_state->denomkey_map, - denom_hash, - dki)); - GNUNET_assert (GNUNET_YES == - GNUNET_CONTAINER_multihashmap_put (key_state->revoked_map, - &dki->issue.properties. - denom_hash, - dki, - GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)); - /* Try to insert revocation into DB */ - arc.dki = dki; - arc.revocation_master_sig = revocation_master_sig; - if (GNUNET_OK != - TEH_DB_run_transaction (NULL, - "add denomination key revocation", - NULL, - &add_revocations_transaction, - &arc)) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Failed to add revocation to database. This is fatal. Committing suicide via SIGTERM.\n"); - handle_signal (SIGTERM); - return GNUNET_SYSERR; - } - - { - json_t *obj; - - obj = json_pack ("{s:o}", - "h_denom_pub", - GNUNET_JSON_from_data_auto (denom_hash)); - if (NULL == obj) - { - GNUNET_break (0); - return GNUNET_SYSERR; - } - if (0 != - json_array_append_new (rfc->recoup_array, - obj)) - { - GNUNET_break (0); - return GNUNET_SYSERR; - } - } - return GNUNET_OK; -} - - -/** - * Convert the public part of a sign key issue to a JSON object. - * - * @param ski the sign key issue - * @param ski_sig signature over @a ski - * @return a JSON object describing the sign key issue (public part) - */ -static json_t * -sign_key_issue_to_json (const struct TALER_ExchangeSigningKeyValidityPS *ski, - const struct TALER_MasterSignatureP *ski_sig) -{ - return - json_pack ("{s:o, s:o, s:o, s:o, s:o}", - "stamp_start", - GNUNET_JSON_from_time_abs (GNUNET_TIME_absolute_ntoh ( - ski->start)), - "stamp_expire", - GNUNET_JSON_from_time_abs (GNUNET_TIME_absolute_ntoh ( - ski->expire)), - "stamp_end", - GNUNET_JSON_from_time_abs (GNUNET_TIME_absolute_ntoh (ski->end)), - "master_sig", - GNUNET_JSON_from_data_auto (ski_sig), - "key", - GNUNET_JSON_from_data_auto (&ski->signkey_pub)); -} - - -/** - * Iterator for sign keys. Adds current and near-future signing keys - * to the `sign_keys_array` and stores the current one in the - * `key_state`. - * - * @param cls closure with the `struct ResponseFactoryContext *` - * @param filename name of the file the key came from - * @param ski the sign key issue - * @return #GNUNET_OK to continue to iterate, - * #GNUNET_NO to stop iteration with no error, - * #GNUNET_SYSERR to abort iteration with error! - */ -static int -reload_keys_sign_iter ( - void *cls, - const char *filename, - const struct TALER_EXCHANGEDB_PrivateSigningKeyInformationP *ski) -{ - struct ResponseFactoryContext *rfc = cls; - struct TEH_KS_StateHandle *key_state = rfc->key_state; - struct GNUNET_TIME_Absolute now; - struct GNUNET_TIME_Absolute horizon; - - horizon = GNUNET_TIME_relative_to_absolute (conf_duration_provide); - if (GNUNET_TIME_absolute_ntoh (ski->issue.start).abs_value_us > - horizon.abs_value_us) - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Skipping future signing key `%s'\n", - filename); - return GNUNET_OK; - } - now = GNUNET_TIME_absolute_get (); - if (GNUNET_TIME_absolute_ntoh (ski->issue.expire).abs_value_us < - now.abs_value_us) - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Skipping expired signing key `%s'\n", - filename); - return GNUNET_OK; - } - - if (0 != GNUNET_memcmp (&ski->issue.master_public_key, - &TEH_master_public_key)) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Master key in signing key file `%s' does not match! Skipping it.\n", - filename); - return GNUNET_OK; - } - - /* The signkey is valid at this time, check if it's more recent than - what we have so far! */ - if ( (GNUNET_TIME_absolute_ntoh ( - key_state->current_sign_key_issue.issue.start).abs_value_us < - GNUNET_TIME_absolute_ntoh (ski->issue.start).abs_value_us) && - (GNUNET_TIME_absolute_ntoh (ski->issue.start).abs_value_us < - now.abs_value_us) ) - { - /* We use the most recent one, if it is valid now (not just in the near future) */ - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Found signing key valid until `%s'\n", - GNUNET_STRINGS_absolute_time_to_string ( - GNUNET_TIME_absolute_ntoh ( - key_state->current_sign_key_issue.issue.end))); - key_state->current_sign_key_issue = *ski; - } - if (0 != - json_array_append_new (rfc->sign_keys_array, - sign_key_issue_to_json (&ski->issue, - &ski->master_sig))) - { - GNUNET_break (0); - return GNUNET_SYSERR; - } - return GNUNET_OK; -} - - -/** - * @brief Iterator called with auditor information. - * Check that the @a mpub actually matches this exchange, and then - * add the auditor information to our /keys response (if it is - * (still) applicable). - * - * @param cls closure with the `struct ResponseFactoryContext *` - * @param apub the auditor's public key - * @param auditor_url URL of the auditor - * @param mpub the exchange's public key (as expected by the auditor) - * @param dki_len length of @a dki and @a asigs - * @param asigs array with the auditor's signatures, of length @a dki_len - * @param dki array of denomination coin data signed by the auditor - * @return #GNUNET_OK to continue to iterate, - * #GNUNET_NO to stop iteration with no error, - * #GNUNET_SYSERR to abort iteration with error! - */ -static int -reload_auditor_iter (void *cls, - const struct TALER_AuditorPublicKeyP *apub, - const char *auditor_url, - const struct TALER_MasterPublicKeyP *mpub, - unsigned int dki_len, - const struct TALER_AuditorSignatureP *asigs, - const struct TALER_DenominationKeyValidityPS *dki) -{ - struct ResponseFactoryContext *rfc = cls; - struct TEH_KS_StateHandle *key_state = rfc->key_state; - - /* Check if the signature is at least for this exchange. */ - if (0 != memcmp (&mpub->eddsa_pub, - &TEH_master_public_key, - sizeof (struct GNUNET_CRYPTO_EddsaPublicKey))) - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Auditing information provided for a different exchange, ignored\n"); - return GNUNET_OK; - } - /* Filter the auditor information for those for which the - keys actually match the denomination keys that are active right now */ - for (unsigned int i = 0; i<dki_len; i++) - { - int matched; - - if (GNUNET_YES != - GNUNET_CONTAINER_multihashmap_contains (key_state->denomkey_map, - &dki[i].denom_hash)) - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Found auditor signature for DK `%s', but key is not in active map\n", - GNUNET_h2s (&dki[i].denom_hash)); - continue; - } - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Found auditor signature for DK `%s'\n", - GNUNET_h2s (&dki[i].denom_hash)); - /* Note: the array is sorted, we could theoretically - speed this up using a binary search. */ - matched = GNUNET_NO; - for (unsigned int j = 0; j<rfc->denomkey_array_length; j++) - { - struct DenominationKeyEntry *dke = &rfc->denomkey_array[j]; - struct AuditorSignature *as; - - if (0 != - memcmp (&dki[i].denom_hash, - &dke->dki->issue.properties.denom_hash, - sizeof (struct GNUNET_HashCode))) - continue; - if (0 != - memcmp (&dki[i], - &dke->dki->issue.properties, - sizeof (struct TALER_DenominationKeyValidityPS))) - { - /* if the hash is the same, the properties should also match! */ - GNUNET_break (0); - continue; - } - as = GNUNET_malloc (sizeof (struct AuditorSignature) - + strlen (auditor_url) + 1); - as->asig = asigs[i]; - as->apub = *apub; - as->auditor_url = (const char *) &as[1]; - memcpy (&as[1], - auditor_url, - strlen (auditor_url) + 1); - GNUNET_CONTAINER_DLL_insert (dke->as_head, - dke->as_tail, - as); - matched = GNUNET_YES; - break; - } - if (GNUNET_NO == matched) - { - GNUNET_break (0); - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "DK `%s' is in active map, but not in array!?\n", - GNUNET_h2s (&dki[i].denom_hash)); - } - } - return GNUNET_OK; -} - - -/** - * Initialize the `denomkey_array`. We are called once per - * array index, which is tracked in `denomkey_array_length` (the - * array will be of sufficient size). Set the pointer to the - * denomination key and increment the `denomkey_array_length`. - * - * @param cls a `struct ResponseFactoryContext` - * @param denom_hash hash of a denomination key - * @param value a `struct TALER_EXCHANGEDB_DenominationKey *` - * @return #GNUNET_OK - */ -static int -initialize_denomkey_array (void *cls, - const struct GNUNET_HashCode *denom_hash, - void *value) -{ - struct ResponseFactoryContext *rfc = cls; - struct TALER_EXCHANGEDB_DenominationKey *dki = value; - - rfc->denomkey_array[rfc->denomkey_array_length].denom_key_hash = *denom_hash; - rfc->denomkey_array[rfc->denomkey_array_length++].dki = dki; - return GNUNET_OK; -} - - -/** - * Comparator used to sort the `struct DenominationKeyEntry` array - * by the validity period's starting time of the keys. Must match - * the expected sorting by cherry_pick_date (which is based on the - * issue.properties.start) used in #krd_search_comparator. - * - * @param k1 a `struct DenominationKeyEntry *` - * @param k2 a `struct DenominationKeyEntry *` - * @return -1 if k1 starts before k2, - * 1 if k2 starts before k1, - * 0 if they start at the same time - */ -static int -denomkey_array_sort_comparator (const void *k1, - const void *k2) -{ - const struct DenominationKeyEntry *dke1 = k1; - const struct DenominationKeyEntry *dke2 = k2; - struct GNUNET_TIME_Absolute d1 - = GNUNET_TIME_absolute_ntoh (dke1->dki->issue.properties.start); - struct GNUNET_TIME_Absolute d2 - = GNUNET_TIME_absolute_ntoh (dke2->dki->issue.properties.start); - - if (d1.abs_value_us < d2.abs_value_us) - return -1; - if (d1.abs_value_us > d2.abs_value_us) - return 1; - return 0; -} - - -/** - * Produce HTTP "Date:" header. - * - * @param at time to write to @a date - * @param[out] date where to write the header, with - * at least 128 bytes available space. - */ -static void -get_date_string (struct GNUNET_TIME_Absolute at, - char date[128]) -{ - static const char *const days[] = - { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" }; - static const char *const mons[] = - { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", - "Nov", "Dec"}; - struct tm now; - time_t t; -#if ! defined(HAVE_C11_GMTIME_S) && ! defined(HAVE_W32_GMTIME_S) && \ - ! defined(HAVE_GMTIME_R) - struct tm*pNow; -#endif - - date[0] = 0; - t = (time_t) (at.abs_value_us / 1000LL / 1000LL); -#if defined(HAVE_C11_GMTIME_S) - if (NULL == gmtime_s (&t, &now)) - return; -#elif defined(HAVE_W32_GMTIME_S) - if (0 != gmtime_s (&now, &t)) - return; -#elif defined(HAVE_GMTIME_R) - if (NULL == gmtime_r (&t, &now)) - return; -#else - pNow = gmtime (&t); - if (NULL == pNow) - return; - now = *pNow; -#endif - sprintf (date, - "%3s, %02u %3s %04u %02u:%02u:%02u GMT", - days[now.tm_wday % 7], - (unsigned int) now.tm_mday, - mons[now.tm_mon % 12], - (unsigned int) (1900 + now.tm_year), - (unsigned int) now.tm_hour, - (unsigned int) now.tm_min, - (unsigned int) now.tm_sec); -} - - -/** - * Add the headers we want to set for every /keys response. - * - * @param key_state the key state to use - * @param[in,out] response the response to modify - * @return #GNUNET_OK on success - */ -static int -setup_general_response_headers (const struct TEH_KS_StateHandle *key_state, - struct MHD_Response *response) -{ - char dat[128]; - - TALER_MHD_add_global_headers (response); - GNUNET_break (MHD_YES == - MHD_add_response_header (response, - MHD_HTTP_HEADER_CONTENT_TYPE, - "application/json")); - get_date_string (key_state->reload_time, - dat); - GNUNET_break (MHD_YES == - MHD_add_response_header (response, - MHD_HTTP_HEADER_LAST_MODIFIED, - dat)); - if (0 != key_state->next_reload.abs_value_us) - { - struct GNUNET_TIME_Absolute m; - - m = GNUNET_TIME_relative_to_absolute (TEH_max_keys_caching); - m = GNUNET_TIME_absolute_min (m, - key_state->next_reload); - get_date_string (m, - dat); - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Setting /keys 'Expires' header to '%s'\n", - dat); - GNUNET_break (MHD_YES == - MHD_add_response_header (response, - MHD_HTTP_HEADER_EXPIRES, - dat)); - } - return GNUNET_OK; -} - - -/** - * Information about an auditor to be added. - */ -struct AuditorEntry -{ - /** - * URL of the auditor (allocated still as part of a - * `struct AuditorSignature`, do not free!). - */ - const char *auditor_url; - - /** - * Public key of the auditor (allocated still as part of a - * `struct AuditorSignature`, do not free!). - */ - const struct TALER_AuditorPublicKeyP *apub; - - /** - * Array of denomination keys and auditor signatures. - */ - json_t *ar; - -}; - - -/** - * Free auditor entry from the hash map. - * - * @param cls NULL - * @param key unused - * @param value a `struct AuditorEntry` to free - * @return #GNUNET_OK (to continue to iterate) - */ -static int -free_auditor_entry (void *cls, - const struct GNUNET_HashCode *key, - void *value) -{ - struct AuditorEntry *ae = value; - - (void) cls; - (void) key; - json_decref (ae->ar); - GNUNET_free (ae); - return GNUNET_OK; -} - - -/** - * Convert auditor entries from the hash map to entries - * in the auditor array, free the auditor entry as well. - * - * @param cls a `struct ResponseBuilderContext *` - * @param key unused - * @param value a `struct AuditorEntry` to add to the `auditors_array` - * @return #GNUNET_OK (to continue to iterate), #GNUNET_SYSERR to terminate with error - */ -static int -add_auditor_entry (void *cls, - const struct GNUNET_HashCode *key, - void *value) -{ - struct ResponseBuilderContext *rbc = cls; - struct AuditorEntry *ae = value; - json_t *ao; - - (void) key; - ao = json_pack ("{s:o, s:s, s:o}", - "denomination_keys", ae->ar, - "auditor_url", ae->auditor_url, - "auditor_pub", GNUNET_JSON_from_data_auto (ae->apub)); - if (NULL == ao) - { - GNUNET_break (0); - return GNUNET_SYSERR; - } - if (0 != - json_array_append_new (rbc->auditors_array, - ao)) - { - GNUNET_break (0); - return GNUNET_SYSERR; - } - GNUNET_free (ae); - return GNUNET_OK; -} - - -/** - * Initialize @a krd for the given @a cherry_pick_date using - * the key data in @a rfc. This function actually builds the - * respective JSON replies (compressed and uncompressed). - * - * @param rfc factory with key material - * @param[out] krd response object to initialize - * @param denom_off offset in the @a rfc's `denomkey_array` at which - * keys beyond the @a cherry_pick_date are stored - * @param cherry_pick_date cut-off date to use - * @return #GNUNET_OK on success, #GNUNET_SYSERR on error - */ -static int -build_keys_response (const struct ResponseFactoryContext *rfc, - struct KeysResponseData *krd, - unsigned int denom_off, - struct GNUNET_TIME_Absolute cherry_pick_date) -{ - struct ResponseBuilderContext rbc; - json_t *keys; - struct TALER_ExchangeKeySetPS ks; - struct TALER_ExchangeSignatureP sig; - char *keys_json; - struct GNUNET_TIME_Relative reserve_closing_delay; - void *keys_jsonz; - size_t keys_jsonz_size; - int comp; - - krd->cherry_pick_date = cherry_pick_date; - - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Creating /keys for cherry pick date %s\n", - GNUNET_STRINGS_absolute_time_to_string (cherry_pick_date)); - - /* Initialize `rbc` */ - memset (&rbc, - 0, - sizeof (rbc)); - rbc.denom_keys_array = json_array (); - if (NULL == rbc.denom_keys_array) - { - GNUNET_break (0); - return GNUNET_SYSERR; - } - rbc.auditors_array = json_array (); - if (NULL == rbc.auditors_array) - { - destroy_response_builder (&rbc); - GNUNET_break (0); - return GNUNET_SYSERR; - } - rbc.hash_context = GNUNET_CRYPTO_hash_context_start (); - - /* Go over relevant denomination keys. */ - { - struct GNUNET_CONTAINER_MultiHashMap *auditors; - - auditors = GNUNET_CONTAINER_multihashmap_create (4, - GNUNET_NO); - for (unsigned int i = denom_off; i<rfc->denomkey_array_length; i++) - { - /* Add denomination key to the response */ - const struct DenominationKeyEntry *dke - = &rfc->denomkey_array[i]; - const struct GNUNET_HashCode *denom_key_hash - = &dke->denom_key_hash; - - GNUNET_CRYPTO_hash_context_read (rbc.hash_context, - denom_key_hash, - sizeof (struct GNUNET_HashCode)); - if (0 != - json_array_append_new (rbc.denom_keys_array, - denom_key_issue_to_json (&dke->dki->denom_pub, - &dke->dki->issue))) - { - GNUNET_break (0); - destroy_response_builder (&rbc); - return GNUNET_SYSERR; - } - - /* Add auditor data */ - for (const struct AuditorSignature *as = dke->as_head; - NULL != as; - as = as->next) - { - struct GNUNET_HashCode ahash; - struct AuditorEntry *ae; - - GNUNET_CRYPTO_hash (&as->apub, - sizeof (as->apub), - &ahash); - ae = GNUNET_CONTAINER_multihashmap_get (auditors, - &ahash); - if (NULL == ae) - { - ae = GNUNET_new (struct AuditorEntry); - ae->auditor_url = as->auditor_url; - ae->ar = json_array (); - ae->apub = &as->apub; - GNUNET_assert (GNUNET_YES == - GNUNET_CONTAINER_multihashmap_put (auditors, - &ahash, - ae, - GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)); - } - if (0 != - json_array_append_new (ae->ar, - json_pack ("{s:o, s:o}", - "denom_pub_h", - GNUNET_JSON_from_data_auto ( - denom_key_hash), - "auditor_sig", - GNUNET_JSON_from_data_auto ( - &as->asig)))) - { - destroy_response_builder (&rbc); - GNUNET_break (0); - GNUNET_CONTAINER_multihashmap_iterate (auditors, - &free_auditor_entry, - NULL); - GNUNET_CONTAINER_multihashmap_destroy (auditors); - return GNUNET_SYSERR; - } - } - } - - GNUNET_CONTAINER_multihashmap_iterate (auditors, - &add_auditor_entry, - &rbc); - GNUNET_CONTAINER_multihashmap_destroy (auditors); - } - - /* Sign hash over denomination keys */ - ks.purpose.size = htonl (sizeof (ks)); - ks.purpose.purpose = htonl (TALER_SIGNATURE_EXCHANGE_KEY_SET); - ks.list_issue_date = GNUNET_TIME_absolute_hton (rfc->key_state->reload_time); - GNUNET_CRYPTO_hash_context_finish (rbc.hash_context, - &ks.hc); - rbc.hash_context = NULL; - GNUNET_CRYPTO_eddsa_sign ( - &rfc->key_state->current_sign_key_issue.signkey_priv.eddsa_priv, - &ks, - &sig.eddsa_signature); - if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_time (TEH_cfg, - "exchangedb", - "IDLE_RESERVE_EXPIRATION_TIME", - &reserve_closing_delay)) - { - GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, - "exchangedb", - "IDLE_RESERVE_EXPIRATION_TIME"); - /* use default */ - reserve_closing_delay = GNUNET_TIME_relative_multiply ( - GNUNET_TIME_UNIT_WEEKS, - 4); - } - /* Build /keys response */ - keys = json_pack ("{s:s, s:o, s:o, s:O, s:O," - " s:o, s:o, s:o, s:o, s:o}", - /* 1-5 */ - "version", EXCHANGE_PROTOCOL_VERSION, - "master_public_key", GNUNET_JSON_from_data_auto ( - &TEH_master_public_key), - "reserve_closing_delay", GNUNET_JSON_from_time_rel ( - reserve_closing_delay), - "signkeys", rfc->sign_keys_array, - "recoup", rfc->recoup_array, - /* 6-10 */ - "denoms", rbc.denom_keys_array, - "auditors", rbc.auditors_array, - "list_issue_date", GNUNET_JSON_from_time_abs ( - rfc->key_state->reload_time), - "eddsa_pub", GNUNET_JSON_from_data_auto ( - &rfc->key_state->current_sign_key_issue.issue.signkey_pub), - "eddsa_sig", GNUNET_JSON_from_data_auto (&sig)); - if (NULL == keys) - { - destroy_response_builder (&rbc); - GNUNET_break (0); - return GNUNET_SYSERR; - } - rbc.denom_keys_array = NULL; - rbc.auditors_array = NULL; - destroy_response_builder (&rbc); - - /* Convert /keys response to UTF8-String */ - keys_json = json_dumps (keys, - JSON_INDENT (2)); - json_decref (keys); - if (NULL == keys_json) - { - GNUNET_break (0); - return GNUNET_SYSERR; - } - /* Keep copy for later compression... */ - keys_jsonz = GNUNET_strdup (keys_json); - keys_jsonz_size = strlen (keys_json); - - /* Create uncompressed response */ - krd->response_uncompressed - = MHD_create_response_from_buffer (keys_jsonz_size, - keys_json, - MHD_RESPMEM_MUST_FREE); - if (NULL == krd->response_uncompressed) - { - GNUNET_break (0); - GNUNET_free (keys_json); - GNUNET_free (keys_jsonz); - return GNUNET_SYSERR; - } - if (GNUNET_OK != - setup_general_response_headers (rfc->key_state, - krd->response_uncompressed)) - { - GNUNET_break (0); - GNUNET_free (keys_jsonz); - return GNUNET_SYSERR; - } - - /* Also compute compressed version of /keys response */ - comp = TALER_MHD_body_compress (&keys_jsonz, - &keys_jsonz_size); - krd->response_compressed - = MHD_create_response_from_buffer (keys_jsonz_size, - keys_jsonz, - MHD_RESPMEM_MUST_FREE); - if (NULL == krd->response_compressed) - { - GNUNET_break (0); - GNUNET_free (keys_jsonz); - return GNUNET_SYSERR; - } - /* If the response is actually compressed, set the - respective header. */ - if ( (MHD_YES == comp) && - (MHD_YES != - MHD_add_response_header (krd->response_compressed, - MHD_HTTP_HEADER_CONTENT_ENCODING, - "deflate")) ) - { - GNUNET_break (0); - return GNUNET_SYSERR; - } - if (GNUNET_OK != - setup_general_response_headers (rfc->key_state, - krd->response_compressed)) - { - GNUNET_break (0); - return GNUNET_SYSERR; - } - return GNUNET_OK; -} - - -/** - * Function called with information about the exchange's denomination - * keys based on what is known in the database. Used to learn our - * public keys (after the private keys are deleted, we still need to - * have the public keys around for a while to verify signatures). - * - * This function checks if the @a denom_pub is already known to us, - * and if not adds it to our set. - * - * @param cls closure, a `struct ResponseFactoryContext *` - * @param denom_pub public key of the denomination - * @param issue detailed information about the denomination (value, expiration times, fees) - */ -static void -reload_public_denoms_cb ( - void *cls, - const struct TALER_DenominationPublicKey *denom_pub, - const struct TALER_EXCHANGEDB_DenominationKeyInformationP *issue) -{ - struct ResponseFactoryContext *rfc = cls; - struct TALER_EXCHANGEDB_DenominationKey dki; - int ret; - - if (rfc->now.abs_value_us > GNUNET_TIME_absolute_ntoh - (issue->properties.expire_legal).abs_value_us) - { - /* Expired key, discard. */ - return; - } - - if (NULL != - GNUNET_CONTAINER_multihashmap_get (rfc->key_state->denomkey_map, - &issue->properties.denom_hash)) - return; /* exists / known */ - if (NULL != - GNUNET_CONTAINER_multihashmap_get (rfc->key_state->revoked_map, - &issue->properties.denom_hash)) - return; /* exists / known */ - /* zero-out, just for future-proofing */ - memset (&dki, - 0, - sizeof (dki)); - dki.denom_priv.rsa_private_key = NULL; /* not available! */ - dki.denom_pub.rsa_public_key = denom_pub->rsa_public_key; - dki.issue = *issue; - ret = store_in_map (rfc->key_state->denomkey_map, - &dki /* makes a deep copy of dki */); - if (GNUNET_SYSERR == ret) - { - GNUNET_break (0); - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Signature wrong on denomination key `%s' (skipping)!\n", - GNUNET_h2s (&issue->properties.denom_hash)); - return; - } - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Loaded denomination key %s from DB, no private key (hopefully revoked!)\n", - GNUNET_h2s (&issue->properties.denom_hash)); - /* we can assert here as we checked for duplicates just above */ - GNUNET_assert (GNUNET_OK == ret); -} - - -/** - * Actual "main" logic that builds the state which this module - * evolves around. This function will import the key data from - * the exchangedb module and convert it into (1) internally used - * lookup tables, and (2) HTTP responses to be returned from - * /keys. - * - * State returned is to be freed with #ks_free() -- but only - * once the reference counter has again hit zero. - * - * @return NULL on error (usually pretty fatal...) - */ -static struct TEH_KS_StateHandle * -make_fresh_key_state (struct GNUNET_TIME_Absolute now) -{ - struct TEH_KS_StateHandle *key_state; - struct ResponseFactoryContext rfc; - struct GNUNET_TIME_Absolute last; - unsigned int off; - enum GNUNET_DB_QueryStatus qs; - - memset (&rfc, - 0, - sizeof (rfc)); - rfc.recoup_array = json_array (); - if (NULL == rfc.recoup_array) - { - GNUNET_break (0); - return NULL; - } - rfc.sign_keys_array = json_array (); - if (NULL == rfc.sign_keys_array) - { - GNUNET_break (0); - json_decref (rfc.recoup_array); - return NULL; - } - - key_state = GNUNET_new (struct TEH_KS_StateHandle); - rfc.key_state = key_state; - rfc.now = now; - key_state->min_dk_expire = GNUNET_TIME_UNIT_FOREVER_ABS; - key_state->denomkey_map = GNUNET_CONTAINER_multihashmap_create (32, - GNUNET_NO); - key_state->revoked_map = GNUNET_CONTAINER_multihashmap_create (4, - GNUNET_NO); - key_state->reload_time = GNUNET_TIME_absolute_get (); - GNUNET_TIME_round_abs (&key_state->reload_time); - - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Loading keys from `%s'\n", - TEH_exchange_directory); - /* Initialize the 'denomkey_map' and the 'revoked_map' and - 'rfc.recoup_array' */ - if (-1 == - TALER_EXCHANGEDB_denomination_keys_iterate (TEH_exchange_directory, - &reload_keys_denom_iter, - &rfc)) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Failed to load denomination keys from `%s'.\n", - TEH_exchange_directory); - ks_free (key_state); - json_decref (rfc.recoup_array); - json_decref (rfc.sign_keys_array); - return NULL; - } - - /* We do not get expired DKIs from - TALER_EXCHANGEDB_denomination_keys_iterate(), so we must fetch - the old keys (where we only have the public keys) from the - database! */ - qs = TEH_plugin->iterate_denomination_info (TEH_plugin->cls, - &reload_public_denoms_cb, - &rfc); - GNUNET_break (0 <= qs); /* warn, but continue, fingers crossed */ - - /* process revocations */ - if (-1 == - TALER_EXCHANGEDB_revocations_iterate (TEH_revocation_directory, - &TEH_master_public_key, - &revocations_iter, - &rfc)) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Failed to load denomination keys from `%s'.\n", - TEH_exchange_directory); - ks_free (key_state); - json_decref (rfc.recoup_array); - json_decref (rfc.sign_keys_array); - return NULL; - } - - /* Initialize `current_sign_key_issue` and `rfc.sign_keys_array` */ - TALER_EXCHANGEDB_signing_keys_iterate (TEH_exchange_directory, - &reload_keys_sign_iter, - &rfc); - if (0 != - memcmp (&key_state->current_sign_key_issue.issue.master_public_key, - &TEH_master_public_key, - sizeof (struct TALER_MasterPublicKeyP))) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Have no signing key. Bad configuration.\n"); - ks_free (key_state); - destroy_response_factory (&rfc); - return NULL; - } - - /* sanity check */ - if (0 == GNUNET_CONTAINER_multihashmap_size (key_state->denomkey_map)) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Have no denomination keys. Bad configuration.\n"); - ks_free (key_state); - destroy_response_factory (&rfc); - return NULL; - } - - /* Initialize and sort the `denomkey_array` */ - rfc.denomkey_array - = GNUNET_new_array (GNUNET_CONTAINER_multihashmap_size ( - key_state->denomkey_map), - struct DenominationKeyEntry); - GNUNET_CONTAINER_multihashmap_iterate (key_state->denomkey_map, - &initialize_denomkey_array, - &rfc); - GNUNET_assert (rfc.denomkey_array_length == - GNUNET_CONTAINER_multihashmap_size (key_state->denomkey_map)); - qsort (rfc.denomkey_array, - rfc.denomkey_array_length, - sizeof (struct DenominationKeyEntry), - &denomkey_array_sort_comparator); - - /* Complete `denomkey_array` by adding auditor signature data */ - TALER_EXCHANGEDB_auditor_iterate (TEH_cfg, - &reload_auditor_iter, - &rfc); - /* Sanity check: do we have auditors for all denomination keys? */ - for (unsigned int i = 0; i<rfc.denomkey_array_length; i++) - { - const struct DenominationKeyEntry *dke - = &rfc.denomkey_array[i]; - - if (NULL == dke->as_head) - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Denomination key `%s' at %p not signed by any auditor!\n", - GNUNET_h2s (&dke->denom_key_hash), - dke); - } - - /* Determine size of `krd_array` by counting number of discrete - denomination key starting times. */ - last = GNUNET_TIME_UNIT_ZERO_ABS; - key_state->krd_array_length = 0; - off = 1; /* reserve one slot for the "no keys" response */ - for (unsigned int i = 0; i<rfc.denomkey_array_length; i++) - { - const struct DenominationKeyEntry *dke - = &rfc.denomkey_array[i]; - struct GNUNET_TIME_Absolute d - = GNUNET_TIME_absolute_ntoh (dke->dki->issue.properties.start); - - if (last.abs_value_us == d.abs_value_us) - continue; - /* must be monotonically increasing as per qsort() call above: */ - GNUNET_assert (last.abs_value_us < d.abs_value_us); - last = d; - off++; - } - - /* Compute next automatic reload time */ - key_state->next_reload = - GNUNET_TIME_absolute_min (GNUNET_TIME_absolute_ntoh ( - key_state->current_sign_key_issue.issue.expire), - key_state->min_dk_expire); - GNUNET_assert (0 != key_state->next_reload.abs_value_us); - - - /* Initialize `krd_array` */ - key_state->krd_array_length = off; - key_state->krd_array - = GNUNET_new_array (key_state->krd_array_length, - struct KeysResponseData); - off = 0; - last = GNUNET_TIME_UNIT_ZERO_ABS; - for (unsigned int i = 0; i<rfc.denomkey_array_length; i++) - { - const struct DenominationKeyEntry *dke - = &rfc.denomkey_array[i]; - struct GNUNET_TIME_Absolute d - = GNUNET_TIME_absolute_ntoh (dke->dki->issue.properties.start); - - if (last.abs_value_us == d.abs_value_us) - continue; - if (GNUNET_OK != - build_keys_response (&rfc, - &key_state->krd_array[off++], - i, - last)) - { - /* Fail hard, will be caught via test on `off` below */ - GNUNET_break (0); - off = key_state->krd_array_length; /* flag as 'invalid' */ - break; - } - last = d; - } - - /* Finally, build an `empty` response without denomination keys - for requests past the last known denomination key start date */ - if ( (off + 1 < key_state->krd_array_length) || - (GNUNET_OK != - build_keys_response (&rfc, - &key_state->krd_array[off++], - rfc.denomkey_array_length, - last)) ) - { - GNUNET_break (0); - ks_free (key_state); - destroy_response_factory (&rfc); - return NULL; - } - - /* Clean up intermediary state we don't need anymore and return - new key_state! */ - destroy_response_factory (&rfc); - return key_state; -} - - -/* ************************** Persistent part ********************** */ - -/** - * Release key state, free if necessary (if reference count gets to zero). - * - * @param location name of the function in which the lock is acquired - * @param key_state the key state to release - */ -void -TEH_KS_release_ (const char *location, - struct TEH_KS_StateHandle *key_state) -{ - int do_free; - - GNUNET_assert (0 == pthread_mutex_lock (&internal_key_state_mutex)); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "KS released at %s (%p/%d)\n", - location, - key_state, - key_state->refcnt); - GNUNET_assert (0 < key_state->refcnt); - key_state->refcnt--; - do_free = (0 == key_state->refcnt); - GNUNET_assert ( (! do_free) || - (key_state != internal_key_state) ); - GNUNET_assert (0 == pthread_mutex_unlock (&internal_key_state_mutex)); - if (do_free) - ks_free (key_state); -} - - -/** - * Acquire the key state of the exchange. Updates keys if necessary. - * For every call to #TEH_KS_acquire(), a matching call - * to #TEH_KS_release() must be made. - * - * @param now for what timestamp should we acquire the key state - * @param location name of the function in which the lock is acquired - * @return the key state, NULL on error (usually pretty fatal) - */ -struct TEH_KS_StateHandle * -TEH_KS_acquire_ (struct GNUNET_TIME_Absolute now, - const char *location) -{ - struct TEH_KS_StateHandle *key_state; - struct TEH_KS_StateHandle *os; - - os = NULL; - GNUNET_assert (0 == pthread_mutex_lock (&internal_key_state_mutex)); - /* If the current internal key state is missing (failed to load one on - startup?) or expired, we try to setup a fresh one even without having - gotten SIGUSR1 */ - if ( ( (NULL != internal_key_state) && - (internal_key_state->next_reload.abs_value_us <= now.abs_value_us) ) || - (NULL == internal_key_state) ) - { - os = internal_key_state; - internal_key_state = make_fresh_key_state (now); - if (NULL != internal_key_state) - internal_key_state->refcnt = 1; /* alias from #internal_key_state */ - if (NULL != os) - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "KS released in acquire due to expiration\n"); - GNUNET_assert (0 < os->refcnt); - os->refcnt--; /* #internal_key_state alias dropped */ - if (0 != os->refcnt) - os = NULL; /* do NOT release yet, otherwise release after unlocking */ - } - } - if (NULL == internal_key_state) - { - /* We tried and failed to setup #internal_key_state */ - GNUNET_assert (0 == pthread_mutex_unlock (&internal_key_state_mutex)); - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Failed to initialize key state\n"); - if (NULL != os) - ks_free (os); - return NULL; - } - key_state = internal_key_state; - key_state->refcnt++; /* returning an alias, increment RC */ - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "KS acquired at %s (%p/%d)\n", - location, - key_state, - key_state->refcnt); - GNUNET_assert (0 == pthread_mutex_unlock (&internal_key_state_mutex)); - if (NULL != os) - ks_free (os); - return key_state; -} - - -/** - * Look up the issue for a denom public key. Note that the result - * is only valid while the @a key_state is not released! - * - * @param key_state state to look in - * @param denom_pub_hash hash of denomination public key - * @param use purpose for which the key is being located - * @param[out] ec set to the error code, in case the operation failed - * @param[out] hc set to the HTTP status code to use - * @return the denomination key issue, - * or NULL if denom_pub could not be found (or is not valid at this time for the given @a use) - */ -struct TALER_EXCHANGEDB_DenominationKey * -TEH_KS_denomination_key_lookup_by_hash ( - const struct TEH_KS_StateHandle *key_state, - const struct GNUNET_HashCode *denom_pub_hash, - enum TEH_KS_DenominationKeyUse use, - enum TALER_ErrorCode *ec, - unsigned int *hc) -{ - struct TALER_EXCHANGEDB_DenominationKey *dki; - struct GNUNET_TIME_Absolute now; - const struct GNUNET_CONTAINER_MultiHashMap *map; - - map = (TEH_KS_DKU_RECOUP == use) - ? key_state->revoked_map - : key_state->denomkey_map; - dki = GNUNET_CONTAINER_multihashmap_get (map, - denom_pub_hash); - if ( (NULL == dki) && - (TEH_KS_DKU_ZOMBIE == use) ) - dki = GNUNET_CONTAINER_multihashmap_get (key_state->revoked_map, - denom_pub_hash); - if (NULL == dki) - { - *hc = MHD_HTTP_NOT_FOUND; - *ec = TALER_EC_EXCHANGE_GENERIC_DENOMINATION_KEY_UNKNOWN; - return NULL; - } - now = GNUNET_TIME_absolute_get (); - if (now.abs_value_us < - GNUNET_TIME_absolute_ntoh (dki->issue.properties.start).abs_value_us) - { - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Not returning DKI for %s, as start time is in the future\n", - GNUNET_h2s (denom_pub_hash)); - *hc = MHD_HTTP_PRECONDITION_FAILED; - *ec = TALER_EC_EXCHANGE_GENERIC_DENOMINATION_VALIDITY_IN_FUTURE; - return NULL; - } - now = GNUNET_TIME_absolute_get (); - switch (use) - { - case TEH_KS_DKU_WITHDRAW: - if (now.abs_value_us > - GNUNET_TIME_absolute_ntoh ( - dki->issue.properties.expire_withdraw).abs_value_us) - { - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Not returning DKI for %s, as time to create coins has passed\n", - GNUNET_h2s (denom_pub_hash)); - *ec = TALER_EC_EXCHANGE_GENERIC_DENOMINATION_EXPIRED; - *hc = MHD_HTTP_GONE; - return NULL; - } - if (NULL == dki->denom_priv.rsa_private_key) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Not returning DKI of %s for WITHDRAW operation as we lack the private key, even though the withdraw period did not yet expire!\n", - GNUNET_h2s (denom_pub_hash)); - *ec = TALER_EC_EXCHANGE_WITHDRAW_DENOMINATION_KEY_LOST; - *hc = MHD_HTTP_SERVICE_UNAVAILABLE; - return NULL; - } - break; - case TEH_KS_DKU_DEPOSIT: - if (now.abs_value_us > - GNUNET_TIME_absolute_ntoh ( - dki->issue.properties.expire_deposit).abs_value_us) - { - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Not returning DKI for %s, as time to spend coin has passed\n", - GNUNET_h2s (denom_pub_hash)); - *ec = TALER_EC_EXCHANGE_GENERIC_DENOMINATION_EXPIRED; - *hc = MHD_HTTP_GONE; - return NULL; - } - break; - case TEH_KS_DKU_RECOUP: - if (now.abs_value_us > - GNUNET_TIME_absolute_ntoh ( - dki->issue.properties.expire_deposit).abs_value_us) - { - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Not returning DKI for %s, as time to recoup coin has passed\n", - GNUNET_h2s (denom_pub_hash)); - *ec = TALER_EC_EXCHANGE_GENERIC_DENOMINATION_EXPIRED; - *hc = MHD_HTTP_GONE; - return NULL; - } - break; - case TEH_KS_DKU_ZOMBIE: - if (now.abs_value_us > - GNUNET_TIME_absolute_ntoh ( - dki->issue.properties.expire_legal).abs_value_us) - { - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Not returning DKI for %s, as legal expiration of coin has passed\n", - GNUNET_h2s (denom_pub_hash)); - *ec = TALER_EC_EXCHANGE_GENERIC_DENOMINATION_EXPIRED; - *hc = MHD_HTTP_GONE; - return NULL; - } - break; - } - return dki; -} - - -/** - * Call #handle_signal() to pass the received signal via - * the control pipe. - */ -static void -handle_sigusr1 (void) -{ - handle_signal (SIGUSR1); -} - - -/** - * Call #handle_signal() to pass the received signal via - * the control pipe. - */ -static void -handle_sigint (void) -{ - handle_signal (SIGINT); -} - - -/** - * Call #handle_signal() to pass the received signal via - * the control pipe. - */ -static void -handle_sigterm (void) -{ - handle_signal (SIGTERM); -} - - -/** - * Call #handle_signal() to pass the received signal via - * the control pipe. - */ -static void -handle_sighup (void) -{ - handle_signal (SIGHUP); -} - - -/** - * Call #handle_signal() to pass the received signal via - * the control pipe. - */ -static void -handle_sigchld (void) -{ - handle_signal (SIGCHLD); -} - - -/** - * Read signals from a pipe in a loop, and reload keys from disk if - * SIGUSR1 is received, terminate if SIGTERM/SIGINT is received, and - * restart if SIGHUP is received. - * - * @return #GNUNET_SYSERR on errors, - * #GNUNET_OK to terminate normally - * #GNUNET_NO to restart an update version of the binary - */ -int -TEH_KS_loop (void) -{ - int ret; - - ret = 2; - while (2 == ret) - { - char c; - ssize_t res; - - errno = 0; - res = read (reload_pipe[0], - &c, - 1); - if ((res < 0) && (EINTR != errno)) - { - GNUNET_break (0); - ret = GNUNET_SYSERR; - break; - } - if (EINTR == errno) - continue; - switch (c) - { - case SIGUSR1: - { - struct TEH_KS_StateHandle *fs; - struct TEH_KS_StateHandle *os; - - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "(re-)loading keys\n"); - /* Create fresh key state before critical region */ - fs = make_fresh_key_state (GNUNET_TIME_absolute_get ()); - if (NULL == fs) - { - /* Ok, that went badly, terminate process */ - ret = GNUNET_SYSERR; - break; - } - fs->refcnt = 1; /* we'll alias from #internal_key_state soon */ - /* swap active key state in critical region */ - GNUNET_assert (0 == pthread_mutex_lock (&internal_key_state_mutex)); - os = internal_key_state; - internal_key_state = fs; - if (NULL != os) - { - GNUNET_assert (0 < os->refcnt); - os->refcnt--; /* removed #internal_key_state reference */ - if (0 != os->refcnt) - os = NULL; /* other aliases are still active, do not yet free */ - } - GNUNET_assert (0 == pthread_mutex_unlock (&internal_key_state_mutex)); - if (NULL != os) - ks_free (os); /* RC did hit zero, free */ - } - break; - case SIGTERM: - case SIGINT: - /* terminate */ - ret = GNUNET_OK; - break; - case SIGHUP: - /* restart updated binary */ - ret = GNUNET_NO; - break; -#if HAVE_DEVELOPER - case SIGCHLD: - /* running in test-mode, test finished, terminate */ - ret = GNUNET_OK; - break; -#endif - default: - /* unexpected character */ - GNUNET_break (0); - break; - } - } - return ret; -} - - -static struct GNUNET_SIGNAL_Context *sigusr1; -static struct GNUNET_SIGNAL_Context *sigterm; -static struct GNUNET_SIGNAL_Context *sigint; -static struct GNUNET_SIGNAL_Context *sighup; -static struct GNUNET_SIGNAL_Context *sigchld; - - -/** - * Setup initial #internal_key_state and our signal handlers. - */ -int -TEH_KS_init (void) -{ - if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_time (TEH_cfg, - "exchange", - "LOOKAHEAD_PROVIDE", - &conf_duration_provide)) - { - GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, - "exchange", - "LOOKAHEAD_PROVIDE", - "time value required"); - return GNUNET_SYSERR; - } - if (0 == conf_duration_provide.rel_value_us) - { - GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, - "exchange", - "LOOKAHEAD_PROVIDE", - "value cannot be zero"); - return GNUNET_SYSERR; - } - if (0 != pipe (reload_pipe)) - { - GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, - "pipe"); - return GNUNET_SYSERR; - } - sigusr1 = GNUNET_SIGNAL_handler_install (SIGUSR1, - &handle_sigusr1); - sigterm = GNUNET_SIGNAL_handler_install (SIGTERM, - &handle_sigterm); - sigint = GNUNET_SIGNAL_handler_install (SIGINT, - &handle_sigint); - sighup = GNUNET_SIGNAL_handler_install (SIGHUP, - &handle_sighup); - sigchld = GNUNET_SIGNAL_handler_install (SIGCHLD, - &handle_sigchld); - /* no need to lock here, as we are still single-threaded */ - internal_key_state = make_fresh_key_state (GNUNET_TIME_absolute_get ()); - if (NULL == internal_key_state) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Failed to setup initial key state. This exchange cannot work.\n"); - return GNUNET_SYSERR; - } - internal_key_state->refcnt = 1; - return GNUNET_OK; -} - - -/** - * Finally release #internal_key_state and our signal handlers. - */ -void -TEH_KS_free (void) -{ - struct TEH_KS_StateHandle *ks; - - /* Note: locking is no longer be required, as we are again - single-threaded. */ - ks = internal_key_state; - if (NULL != ks) - { - GNUNET_assert (1 == ks->refcnt); - ks->refcnt--; - ks_free (ks); - } - if (NULL != sigusr1) - { - GNUNET_SIGNAL_handler_uninstall (sigusr1); - sigusr1 = NULL; - } - if (NULL != sigterm) - { - GNUNET_SIGNAL_handler_uninstall (sigterm); - sigterm = NULL; - } - if (NULL != sigint) - { - GNUNET_SIGNAL_handler_uninstall (sigint); - sigint = NULL; - } - if (NULL != sighup) - { - GNUNET_SIGNAL_handler_uninstall (sighup); - sighup = NULL; - } - if (NULL != sigchld) - { - GNUNET_SIGNAL_handler_uninstall (sigchld); - sigchld = NULL; - } - if (-1 != reload_pipe[0]) - { - GNUNET_break (0 == close (reload_pipe[0])); - GNUNET_break (0 == close (reload_pipe[1])); - reload_pipe[0] = reload_pipe[1] = -1; - } -} - - -/** - * Sign the message in @a purpose with the exchange's signing key. - * - * The @a purpose data is the beginning of the data of which the signature is - * to be created. The `size` field in @a purpose must correctly indicate the - * number of bytes of the data structure, including its header. Use - * #TEH_KS_sign() instead of calling this function directly! - * - * @param purpose the message to sign - * @param[out] pub set to the current public signing key of the exchange - * @param[out] sig signature over purpose using current signing key - * @return #GNUNET_OK on success, #GNUNET_SYSERR if we lack key material - */ -int -TEH_KS_sign_ (const struct GNUNET_CRYPTO_EccSignaturePurpose *purpose, - struct TALER_ExchangePublicKeyP *pub, - struct TALER_ExchangeSignatureP *sig) -{ - struct TEH_KS_StateHandle *key_state; - - key_state = TEH_KS_acquire (GNUNET_TIME_absolute_get ()); - if (NULL == key_state) - { - /* This *can* happen if the exchange's keys are not properly maintained - (i.e. administrator forgets to provision us with non-expired signing - keys or to send signal to reload keys after provisioning). */ - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Cannot sign request, no valid signing keys available. Were they properly provisioned and did you signal the exchange to reload the keys?\n"); - return GNUNET_SYSERR; - } - *pub = key_state->current_sign_key_issue.issue.signkey_pub; - GNUNET_assert (GNUNET_OK == - GNUNET_CRYPTO_eddsa_sign_ ( - &key_state->current_sign_key_issue.signkey_priv.eddsa_priv, - purpose, - &sig->eddsa_signature)); - TEH_KS_release (key_state); - return GNUNET_OK; -} - - -/** - * Comparator used for a binary search by cherry_pick_date for @a key in the - * `struct KeysResponseData` array. See libc's qsort() and bsearch() functions. - * - * @param key pointer to a `struct GNUNET_TIME_Absolute` - * @param value pointer to a `struct KeysResponseData` array entry - * @return 0 if time matches, -1 if key is smaller, 1 if key is larger - */ -static int -krd_search_comparator (const void *key, - const void *value) -{ - const struct GNUNET_TIME_Absolute *kd = key; - const struct KeysResponseData *krd = value; - - if (kd->abs_value_us > krd->cherry_pick_date.abs_value_us) - return 1; - if (kd->abs_value_us < krd->cherry_pick_date.abs_value_us) - return -1; - return 0; -} - - -/** - * Function to call to handle the request by sending - * back static data from the @a rh. - * - * @param rh context of the handler - * @param connection the MHD connection to handle - * @param args array of additional options (must be empty for this function) - * @return MHD result code - */ -MHD_RESULT -TEH_handler_keys (const struct TEH_RequestHandler *rh, - struct MHD_Connection *connection, - const char *const args[]) -{ - MHD_RESULT ret; - const char *have_cherrypick; - const char *have_fakenow; - struct GNUNET_TIME_Absolute last_issue_date; - struct GNUNET_TIME_Absolute now; - const struct KeysResponseData *krd; - - (void) rh; - (void) args; - have_cherrypick = MHD_lookup_connection_value (connection, - MHD_GET_ARGUMENT_KIND, - "last_issue_date"); - if (NULL != have_cherrypick) - { - unsigned long long cherrypickn; - - if (1 != - sscanf (have_cherrypick, - "%llu", - &cherrypickn)) - { - GNUNET_break_op (0); - return TALER_MHD_reply_with_error (connection, - MHD_HTTP_BAD_REQUEST, - TALER_EC_GENERIC_PARAMETER_MALFORMED, - have_cherrypick); - } - /* The following multiplication may overflow; but this should not really - be a problem, as giving back 'older' data than what the client asks for - (given that the client asks for data in the distant future) is not - problematic */ - last_issue_date.abs_value_us = (uint64_t) cherrypickn * 1000000LLU; - } - else - { - last_issue_date.abs_value_us = 0LLU; - } - now = GNUNET_TIME_absolute_get (); - have_fakenow = MHD_lookup_connection_value (connection, - MHD_GET_ARGUMENT_KIND, - "now"); - if (NULL != have_fakenow) - { - unsigned long long fakenown; - - if (1 != - sscanf (have_fakenow, - "%llu", - &fakenown)) - { - GNUNET_break_op (0); - return TALER_MHD_reply_with_error (connection, - MHD_HTTP_FORBIDDEN, - TALER_EC_GENERIC_PARAMETER_MALFORMED, - have_fakenow); - } - if (TEH_allow_keys_timetravel) - { - /* The following multiplication may overflow; but this should not really - be a problem, as giving back 'older' data than what the client asks for - (given that the client asks for data in the distant future) is not - problematic */ - now.abs_value_us = (uint64_t) fakenown * 1000000LLU; - } - else - { - /* Option not allowed by configuration */ - return TALER_MHD_reply_with_error (connection, - MHD_HTTP_FORBIDDEN, - TALER_EC_EXCHANGE_KEYS_TIMETRAVEL_FORBIDDEN, - NULL); - } - } - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Handling request for /keys (%s/%s)\n", - have_cherrypick, - have_fakenow); - { - struct TEH_KS_StateHandle *key_state; - - key_state = TEH_KS_acquire (now); - if (NULL == key_state) - { - /* Maybe client picked time stamp too far in the future? In that case, - #MHD_HTTP_INTERNAL_SERVER_ERROR might be misleading, could be more like a - NOT_FOUND situation. But, OTOH, for 'sane' clients it is more likely - to be our fault, so let's speculatively assume we are to blame ;-) */ - return TALER_MHD_reply_with_error (connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_EXCHANGE_GENERIC_BAD_CONFIGURATION, - NULL); - } - krd = bsearch (&last_issue_date, - key_state->krd_array, - key_state->krd_array_length, - sizeof (struct KeysResponseData), - &krd_search_comparator); - - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Filtering /keys by cherry pick date %s found entry %u/%u\n", - GNUNET_STRINGS_absolute_time_to_string (last_issue_date), - (unsigned int) (krd - key_state->krd_array), - key_state->krd_array_length); - if ( (NULL == krd) && - (key_state->krd_array_length > 0) ) - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Client provided invalid cherry picking timestamp %s, returning full response\n", - GNUNET_STRINGS_absolute_time_to_string (last_issue_date)); - krd = &key_state->krd_array[0]; - } - if (NULL == krd) - { - /* Maybe client picked time stamp too far in the future? In that case, - "INTERNAL_SERVER_ERROR" might be misleading, could be more like a - NOT_FOUND situation. But, OTOH, for 'sane' clients it is more likely - to be our fault, so let's speculatively assume we are to blame ;-) */// - GNUNET_break (0); - TEH_KS_release (key_state); - return TALER_MHD_reply_with_error (connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_EXCHANGE_GENERIC_KEYS_MISSING, - NULL); - } - ret = MHD_queue_response (connection, - MHD_HTTP_OK, - (MHD_YES == TALER_MHD_can_compress (connection)) - ? krd->response_compressed - : krd->response_uncompressed); - TEH_KS_release (key_state); - } - return ret; -} - - -/* end of taler-exchange-httpd_keystate.c */ diff --git a/src/exchange/taler-exchange-httpd_keystate.h b/src/exchange/taler-exchange-httpd_keystate.h deleted file mode 100644 index 86bdc59b1..000000000 --- a/src/exchange/taler-exchange-httpd_keystate.h +++ /dev/null @@ -1,229 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2014, 2015 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 exchange/taler-exchange-httpd_keystate.h - * @brief management of our private signing keys (denomination keys) - * @author Florian Dold - * @author Benedikt Mueller - * @author Christian Grothoff - */ -#ifndef TALER_EXCHANGE_HTTPD_KEYSTATE_H -#define TALER_EXCHANGE_HTTPD_KEYSTATE_H - -#include <gnunet/gnunet_util_lib.h> -#include <microhttpd.h> -#include "taler-exchange-httpd.h" -#include "taler_error_codes.h" -#include "taler_exchangedb_lib.h" - - -/** - * Snapshot of the (coin and signing) - * keys (including private keys) of the exchange. - */ -struct TEH_KS_StateHandle; - - -/** - * Acquire the key state of the exchange. Updates keys if necessary. - * For every call to #TEH_KS_acquire(), a matching call - * to #TEH_KS_release() must be made. - * - * @param now for what timestamp should we acquire the key state - * @param location name of the function in which the lock is acquired - * @return the key state, NULL on error (usually pretty fatal) - */ -struct TEH_KS_StateHandle * -TEH_KS_acquire_ (struct GNUNET_TIME_Absolute now, - const char *location); - - -/** - * Release key state, free if necessary (if reference count gets to zero). - * - * @param location name of the function in which the lock is acquired - * @param key_state the key state to release - */ -void -TEH_KS_release_ (const char *location, - struct TEH_KS_StateHandle *key_state); - - -/** - * Acquire the key state of the exchange. Updates keys if necessary. - * For every call to #TEH_KS_acquire(), a matching call - * to #TEH_KS_release() must be made. - * - * @param now current time snapshot; either true, or given by the - * client via the "now" URL parameter of "/keys". - * @return the key state - */ -#define TEH_KS_acquire(now) TEH_KS_acquire_ (now, __FUNCTION__) - - -/** - * Release key state, free if necessary (if reference count gets to zero). - * - * @param key_state the key state to release - */ -#define TEH_KS_release(key_state) TEH_KS_release_ (__FUNCTION__, key_state) - - -/** - * Setup initial #internal_key_state and our signal handlers. - * - * @return #GNUNET_OK on success - */ -int -TEH_KS_init (void); - - -/** - * Finally, release #internal_key_state and our signal handlers. - */ -void -TEH_KS_free (void); - - -/** - * Denomination key lookups can be for signing of fresh coins - * or to validate signatures on existing coins. As the validity - * periods for a key differ, the caller must specify which - * use is relevant for the current operation. - */ -enum TEH_KS_DenominationKeyUse -{ - - /** - * The denomination key is to be used for a withdraw or reveal operation. - */ - TEH_KS_DKU_WITHDRAW, - - /** - * The denomination key is to be used for a deposit or melt operation. - */ - TEH_KS_DKU_DEPOSIT, - - /** - * The denomination key is to be used for a recoup operation, or to - * melt a coin that was deposited (or melted) before the revocation. - */ - TEH_KS_DKU_RECOUP, - - /** - * The key is to be used for a refresh + recoup operation, - * i.e. it is an old coin that regained value from a - * recoup on a new coin derived from the old coin. - */ - TEH_KS_DKU_ZOMBIE - -}; - - -/** - * Look up the issue for a denom public key. Note that the result - * is only valid while the @a key_state is not released! - * - * @param key_state state to look in - * @param denom_pub_hash hash of denomination public key - * @param use purpose for which the key is being located - * @param[out] ec set to the error code, in case the operation failed - * @param[out] hc set to the HTTP status code to use - * @return the denomination key issue, - * or NULL if denom_pub could not be found (or is not valid at this time for the given @a use) - */ -struct TALER_EXCHANGEDB_DenominationKey * -TEH_KS_denomination_key_lookup_by_hash ( - const struct TEH_KS_StateHandle *key_state, - const struct GNUNET_HashCode *denom_pub_hash, - enum TEH_KS_DenominationKeyUse use, - enum TALER_ErrorCode *ec, - unsigned int *hc); - - -/** - * Read signals from a pipe in a loop, and reload keys from disk if - * SIGUSR1 is received, terminate if SIGTERM/SIGINT is received, and - * restart if SIGHUP is received. - * - * @return #GNUNET_SYSERR on errors, - * #GNUNET_OK to terminate normally - * #GNUNET_NO to restart an update version of the binary - */ -int -TEH_KS_loop (void); - - -/** - * Sign the message in @a purpose with the exchange's signing - * key. - * - * The @a purpose data is the beginning of the data of which the signature is - * to be created. The `size` field in @a purpose must correctly indicate the - * number of bytes of the data structure, including its header. Use - * #TEH_KS_sign() instead of calling this function directly! - * - * @param purpose the message to sign - * @param[out] pub set to the current public signing key of the exchange - * @param[out] sig signature over purpose using current signing key - * @return #GNUNET_OK on success, #GNUNET_SYSERR if we lack key material - */ -int -TEH_KS_sign_ (const struct GNUNET_CRYPTO_EccSignaturePurpose *purpose, - struct TALER_ExchangePublicKeyP *pub, - struct TALER_ExchangeSignatureP *sig); - -/** - * @ingroup crypto - * @brief EdDSA sign a given block. - * - * The @a ps data must be a fixed-size struct for which the signature is to be - * created. The `size` field in @a ps->purpose must correctly indicate the - * number of bytes of the data structure, including its header. - * - * @param ps packed struct with what to sign, MUST begin with a purpose - * @param[out] pub where to store the public key to use for the signing - * @param[out] sig where to write the signature - */ -#define TEH_KS_sign(ps,pub,sig) \ - ({ \ - /* check size is set correctly */ \ - GNUNET_assert (htonl ((ps)->purpose.size) == \ - sizeof (*ps)); \ - /* check 'ps' begins with the purpose */ \ - GNUNET_static_assert (((void*) (ps)) == \ - ((void*) &(ps)->purpose)); \ - TEH_KS_sign_ (&(ps)->purpose, \ - pub, \ - sig); \ - }) - - -/** - * Handle a "/keys" request - * - * @param rh context of the handler - * @param connection the MHD connection to handle - * @param args array of additional options (must be empty for this function) - * @return MHD result code - */ -MHD_RESULT -TEH_handler_keys (const struct TEH_RequestHandler *rh, - struct MHD_Connection *connection, - const char *const args[]); - - -#endif diff --git a/src/exchange/taler-exchange-httpd_link.c b/src/exchange/taler-exchange-httpd_link.c index 1ad5eff09..3edb25b29 100644 --- a/src/exchange/taler-exchange-httpd_link.c +++ b/src/exchange/taler-exchange-httpd_link.c @@ -28,7 +28,6 @@ #include "taler-exchange-httpd_mhd.h" #include "taler-exchange-httpd_link.h" #include "taler-exchange-httpd_responses.h" -#include "taler-exchange-httpd_keystate.h" /** diff --git a/src/exchange/taler-exchange-httpd_loop.c b/src/exchange/taler-exchange-httpd_loop.c new file mode 100644 index 000000000..086fbc874 --- /dev/null +++ b/src/exchange/taler-exchange-httpd_loop.c @@ -0,0 +1,209 @@ +/* + This file is part of TALER + Copyright (C) 2014--2020 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_loop.c + * @brief management of our main loop + * @author Florian Dold + * @author Benedikt Mueller + * @author Christian Grothoff + */ +#include "platform.h" +#include <pthread.h> +#include "taler-exchange-httpd_loop.h" + + +/* ************************* Signal logic ************************** */ + +/** + * Pipe used for signaling reloading of our key state. + */ +static int reload_pipe[2] = { -1, -1 }; + + +/** + * Handle a signal, writing relevant signal numbers to the pipe. + * + * @param signal_number the signal number + */ +static void +handle_signal (int signal_number) +{ + char c = (char) signal_number; /* never seen a signal_number > 127 on any platform */ + + (void) ! write (reload_pipe[1], + &c, + 1); + /* While one might like to "handle errors" here, even logging via fprintf() + isn't safe inside of a signal handler. So there is nothing we safely CAN + do. OTOH, also very little that can go wrong in practice. Calling _exit() + on errors might be a possibility, but that might do more harm than good. */// +} + + +/** + * Call #handle_signal() to pass the received signal via + * the control pipe. + */ +static void +handle_sigint (void) +{ + handle_signal (SIGINT); +} + + +/** + * Call #handle_signal() to pass the received signal via + * the control pipe. + */ +static void +handle_sigterm (void) +{ + handle_signal (SIGTERM); +} + + +/** + * Call #handle_signal() to pass the received signal via + * the control pipe. + */ +static void +handle_sighup (void) +{ + handle_signal (SIGHUP); +} + + +/** + * Call #handle_signal() to pass the received signal via + * the control pipe. + */ +static void +handle_sigchld (void) +{ + handle_signal (SIGCHLD); +} + + +int +TEH_loop_run (void) +{ + int ret; + + ret = 2; + while (2 == ret) + { + char c; + ssize_t res; + + errno = 0; + res = read (reload_pipe[0], + &c, + 1); + if ((res < 0) && (EINTR != errno)) + { + GNUNET_break (0); + ret = GNUNET_SYSERR; + break; + } + if (EINTR == errno) + continue; + switch (c) + { + case SIGTERM: + case SIGINT: + /* terminate */ + ret = GNUNET_OK; + break; + case SIGHUP: + /* restart updated binary */ + ret = GNUNET_NO; + break; +#if HAVE_DEVELOPER + case SIGCHLD: + /* running in test-mode, test finished, terminate */ + ret = GNUNET_OK; + break; +#endif + default: + /* unexpected character */ + GNUNET_break (0); + break; + } + } + return ret; +} + + +static struct GNUNET_SIGNAL_Context *sigterm; +static struct GNUNET_SIGNAL_Context *sigint; +static struct GNUNET_SIGNAL_Context *sighup; +static struct GNUNET_SIGNAL_Context *sigchld; + + +int +TEH_loop_init (void) +{ + if (0 != pipe (reload_pipe)) + { + GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, + "pipe"); + return GNUNET_SYSERR; + } + sigterm = GNUNET_SIGNAL_handler_install (SIGTERM, + &handle_sigterm); + sigint = GNUNET_SIGNAL_handler_install (SIGINT, + &handle_sigint); + sighup = GNUNET_SIGNAL_handler_install (SIGHUP, + &handle_sighup); + sigchld = GNUNET_SIGNAL_handler_install (SIGCHLD, + &handle_sigchld); + return GNUNET_OK; +} + + +void +TEH_loop_done (void) +{ + if (NULL != sigterm) + { + GNUNET_SIGNAL_handler_uninstall (sigterm); + sigterm = NULL; + } + if (NULL != sigint) + { + GNUNET_SIGNAL_handler_uninstall (sigint); + sigint = NULL; + } + if (NULL != sighup) + { + GNUNET_SIGNAL_handler_uninstall (sighup); + sighup = NULL; + } + if (NULL != sigchld) + { + GNUNET_SIGNAL_handler_uninstall (sigchld); + sigchld = NULL; + } + if (-1 != reload_pipe[0]) + { + GNUNET_break (0 == close (reload_pipe[0])); + GNUNET_break (0 == close (reload_pipe[1])); + reload_pipe[0] = reload_pipe[1] = -1; + } +} + + +/* end of taler-exchange-httpd_loop.c */ diff --git a/src/exchange/taler-exchange-httpd_loop.h b/src/exchange/taler-exchange-httpd_loop.h new file mode 100644 index 000000000..700f56776 --- /dev/null +++ b/src/exchange/taler-exchange-httpd_loop.h @@ -0,0 +1,60 @@ +/* + This file is part of TALER + Copyright (C) 2014, 2015 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 exchange/taler-exchange-httpd_loop.h + * @brief management of our main loop + * @author Florian Dold + * @author Benedikt Mueller + * @author Christian Grothoff + */ +#ifndef TALER_EXCHANGE_HTTPD_LOOP_H +#define TALER_EXCHANGE_HTTPD_LOOP_H + +#include <gnunet/gnunet_util_lib.h> +#include <microhttpd.h> +#include "taler-exchange-httpd.h" + + +/** + * Setup our signal handlers. + * + * @return #GNUNET_OK on success + */ +int +TEH_loop_init (void); + + +/** + * Finally, tear down our signal handlers. + */ +void +TEH_loop_done (void); + + +/** + * Read signals from a pipe in a loop, and reload keys from disk if + * SIGUSR1 is received, terminate if SIGTERM/SIGINT is received, and + * restart if SIGHUP is received. + * + * @return #GNUNET_SYSERR on errors, + * #GNUNET_OK to terminate normally + * #GNUNET_NO to restart an update version of the binary + */ +int +TEH_loop_run (void); + + +#endif diff --git a/src/exchange/taler-exchange-httpd_management_auditors.c b/src/exchange/taler-exchange-httpd_management_auditors.c index 1a2494dae..acb8f2c58 100644 --- a/src/exchange/taler-exchange-httpd_management_auditors.c +++ b/src/exchange/taler-exchange-httpd_management_auditors.c @@ -28,7 +28,7 @@ #include "taler_mhd_lib.h" #include "taler-exchange-httpd_management.h" #include "taler-exchange-httpd_responses.h" -#include "taler-exchange-httpd_keystate.h" + /** * Closure for the #add_auditor transaction. diff --git a/src/exchange/taler-exchange-httpd_management_auditors_AP_disable.c b/src/exchange/taler-exchange-httpd_management_auditors_AP_disable.c index e360c1a52..222af60ec 100644 --- a/src/exchange/taler-exchange-httpd_management_auditors_AP_disable.c +++ b/src/exchange/taler-exchange-httpd_management_auditors_AP_disable.c @@ -28,7 +28,7 @@ #include "taler_mhd_lib.h" #include "taler-exchange-httpd_management.h" #include "taler-exchange-httpd_responses.h" -#include "taler-exchange-httpd_keystate.h" + /** * Closure for the #del_auditor transaction. diff --git a/src/exchange/taler-exchange-httpd_management_denominations_HDP_revoke.c b/src/exchange/taler-exchange-httpd_management_denominations_HDP_revoke.c index 8fb5b0839..23e6cfb49 100644 --- a/src/exchange/taler-exchange-httpd_management_denominations_HDP_revoke.c +++ b/src/exchange/taler-exchange-httpd_management_denominations_HDP_revoke.c @@ -28,7 +28,6 @@ #include "taler_mhd_lib.h" #include "taler-exchange-httpd_management.h" #include "taler-exchange-httpd_responses.h" -#include "taler-exchange-httpd_keystate.h" #include "taler-exchange-httpd_keys.h" diff --git a/src/exchange/taler-exchange-httpd_management_signkey_EP_revoke.c b/src/exchange/taler-exchange-httpd_management_signkey_EP_revoke.c index 3a84296cd..273c05d58 100644 --- a/src/exchange/taler-exchange-httpd_management_signkey_EP_revoke.c +++ b/src/exchange/taler-exchange-httpd_management_signkey_EP_revoke.c @@ -28,7 +28,6 @@ #include "taler_mhd_lib.h" #include "taler-exchange-httpd_management.h" #include "taler-exchange-httpd_responses.h" -#include "taler-exchange-httpd_keystate.h" #include "taler-exchange-httpd_keys.h" diff --git a/src/exchange/taler-exchange-httpd_management_wire.c b/src/exchange/taler-exchange-httpd_management_wire.c index c462bfc31..5454125f4 100644 --- a/src/exchange/taler-exchange-httpd_management_wire.c +++ b/src/exchange/taler-exchange-httpd_management_wire.c @@ -29,6 +29,7 @@ #include "taler_signatures.h" #include "taler-exchange-httpd_management.h" #include "taler-exchange-httpd_responses.h" +#include "taler-exchange-httpd_wire.h" /** @@ -202,6 +203,7 @@ TEH_handler_management_denominations_wire ( &awc); if (qs < 0) return ret; + TEH_wire_update_state (); return TALER_MHD_reply_static ( connection, MHD_HTTP_NO_CONTENT, diff --git a/src/exchange/taler-exchange-httpd_management_wire_disable.c b/src/exchange/taler-exchange-httpd_management_wire_disable.c index 51b811604..67cf30150 100644 --- a/src/exchange/taler-exchange-httpd_management_wire_disable.c +++ b/src/exchange/taler-exchange-httpd_management_wire_disable.c @@ -28,7 +28,8 @@ #include "taler_mhd_lib.h" #include "taler-exchange-httpd_management.h" #include "taler-exchange-httpd_responses.h" -#include "taler-exchange-httpd_keystate.h" +#include "taler-exchange-httpd_wire.h" + /** * Closure for the #del_wire transaction. @@ -182,6 +183,7 @@ TEH_handler_management_denominations_wire_disable ( &awc); if (qs < 0) return ret; + TEH_wire_update_state (); return TALER_MHD_reply_static ( connection, MHD_HTTP_NO_CONTENT, diff --git a/src/exchange/taler-exchange-httpd_management_wire_fees.c b/src/exchange/taler-exchange-httpd_management_wire_fees.c index 9878821c1..0a011bc03 100644 --- a/src/exchange/taler-exchange-httpd_management_wire_fees.c +++ b/src/exchange/taler-exchange-httpd_management_wire_fees.c @@ -29,6 +29,7 @@ #include "taler_signatures.h" #include "taler-exchange-httpd_management.h" #include "taler-exchange-httpd_responses.h" +#include "taler-exchange-httpd_wire.h" /** @@ -247,6 +248,7 @@ TEH_handler_management_post_wire_fees ( &afc); if (qs < 0) return ret; + TEH_wire_update_state (); return TALER_MHD_reply_static ( connection, MHD_HTTP_NO_CONTENT, diff --git a/src/exchange/taler-exchange-httpd_melt.c b/src/exchange/taler-exchange-httpd_melt.c index 76cf52ebe..143e3f3ac 100644 --- a/src/exchange/taler-exchange-httpd_melt.c +++ b/src/exchange/taler-exchange-httpd_melt.c @@ -29,8 +29,8 @@ #include "taler-exchange-httpd_mhd.h" #include "taler-exchange-httpd_melt.h" #include "taler-exchange-httpd_responses.h" -#include "taler-exchange-httpd_keystate.h" #include "taler-exchange-httpd_keys.h" +#include "taler_exchangedb_lib.h" /** @@ -463,136 +463,114 @@ static MHD_RESULT check_for_denomination_key (struct MHD_Connection *connection, struct MeltContext *rmc) { - struct TEH_KS_StateHandle *key_state; + /* Baseline: check if deposits/refreshs are generally + simply still allowed for this denomination */ + struct TEH_DenominationKey *dk; + unsigned int hc; + enum TALER_ErrorCode ec; + struct GNUNET_TIME_Absolute now; - key_state = TEH_KS_acquire (GNUNET_TIME_absolute_get ()); - if (NULL == key_state) + dk = TEH_keys_denomination_by_hash ( + &rmc->refresh_session.coin.denom_pub_hash, + &ec, + &hc); + if (NULL == dk) { - TALER_LOG_ERROR ("Lacking keys to operate\n"); - return TALER_MHD_reply_with_error (connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_EXCHANGE_GENERIC_BAD_CONFIGURATION, - "no keys"); + return TALER_MHD_reply_with_error ( + connection, + MHD_HTTP_NOT_FOUND, + TALER_EC_EXCHANGE_GENERIC_DENOMINATION_KEY_UNKNOWN, + NULL); } - + now = GNUNET_TIME_absolute_get (); + if (now.abs_value_us >= dk->meta.expire_legal.abs_value_us) { - /* Baseline: check if deposits/refreshs are generally - simply still allowed for this denomination */ - struct TEH_DenominationKey *dk; - unsigned int hc; - enum TALER_ErrorCode ec; - struct GNUNET_TIME_Absolute now; - - dk = TEH_keys_denomination_by_hash ( - &rmc->refresh_session.coin.denom_pub_hash, - &ec, - &hc); - if (NULL == dk) + /* Way too late now, even zombies have expired */ + return TALER_MHD_reply_with_error ( + connection, + MHD_HTTP_GONE, + TALER_EC_EXCHANGE_GENERIC_DENOMINATION_EXPIRED, + NULL); + } + if (now.abs_value_us < dk->meta.start.abs_value_us) + { + /* This denomination is not yet valid */ + return TALER_MHD_reply_with_error ( + connection, + MHD_HTTP_PRECONDITION_FAILED, + TALER_EC_EXCHANGE_GENERIC_DENOMINATION_VALIDITY_IN_FUTURE, + NULL); + } + if (now.abs_value_us >= dk->meta.expire_deposit.abs_value_us) + { + /* We are past deposit expiration time, but maybe this is a zombie? */ + struct GNUNET_HashCode denom_hash; + enum GNUNET_DB_QueryStatus qs; + + /* Check that the coin is dirty (we have seen it before), as we will + not just allow melting of a *fresh* coin where the denomination was + revoked (those must be recouped) */ + qs = TEH_plugin->get_coin_denomination ( + TEH_plugin->cls, + NULL, + &rmc->refresh_session.coin.coin_pub, + &denom_hash); + if (0 > qs) { - TEH_KS_release (key_state); - return TALER_MHD_reply_with_error ( - connection, - MHD_HTTP_NOT_FOUND, - TALER_EC_EXCHANGE_GENERIC_DENOMINATION_KEY_UNKNOWN, - NULL); + /* There is no good reason for a serialization failure here: */ + GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR != qs); + return TALER_MHD_reply_with_error (connection, + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_EC_GENERIC_DB_FETCH_FAILED, + "coin denomination"); } - now = GNUNET_TIME_absolute_get (); - if (now.abs_value_us >= dk->meta.expire_legal.abs_value_us) + /* sanity check */ + GNUNET_break (0 == + GNUNET_memcmp (&denom_hash, + &rmc->refresh_session.coin.denom_pub_hash)); + if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs) { - /* Way too late now, even zombies have expired */ - TEH_KS_release (key_state); + /* We never saw this coin before, so _this_ justification is not OK */ return TALER_MHD_reply_with_error ( connection, MHD_HTTP_GONE, TALER_EC_EXCHANGE_GENERIC_DENOMINATION_EXPIRED, NULL); } - if (now.abs_value_us < dk->meta.start.abs_value_us) - { - /* This denomination is not yet valid */ - TEH_KS_release (key_state); - return TALER_MHD_reply_with_error ( - connection, - MHD_HTTP_PRECONDITION_FAILED, - TALER_EC_EXCHANGE_GENERIC_DENOMINATION_VALIDITY_IN_FUTURE, - NULL); - } - if (now.abs_value_us >= dk->meta.expire_deposit.abs_value_us) + else { - /* We are past deposit expiration time, but maybe this is a zombie? */ - struct GNUNET_HashCode denom_hash; - enum GNUNET_DB_QueryStatus qs; - - /* Check that the coin is dirty (we have seen it before), as we will - not just allow melting of a *fresh* coin where the denomination was - revoked (those must be recouped) */ - qs = TEH_plugin->get_coin_denomination ( - TEH_plugin->cls, - NULL, - &rmc->refresh_session.coin.coin_pub, - &denom_hash); - if (0 > qs) - { - TEH_KS_release (key_state); - /* There is no good reason for a serialization failure here: */ - GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR != qs); - return TALER_MHD_reply_with_error (connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_GENERIC_DB_FETCH_FAILED, - "coin denomination"); - } - /* sanity check */ - GNUNET_break (0 == - GNUNET_memcmp (&denom_hash, - &rmc->refresh_session.coin.denom_pub_hash)); - if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs) - { - /* We never saw this coin before, so _this_ justification is not OK */ - TEH_KS_release (key_state); - return TALER_MHD_reply_with_error ( - connection, - MHD_HTTP_GONE, - TALER_EC_EXCHANGE_GENERIC_DENOMINATION_EXPIRED, - NULL); - } - else - { - /* Minor optimization: no need to run the - "ensure_coin_known" part of the transaction */ - rmc->coin_is_dirty = true; - } - rmc->zombie_required = true; /* check later that zombie is satisfied */ + /* Minor optimization: no need to run the + "ensure_coin_known" part of the transaction */ + rmc->coin_is_dirty = true; } + rmc->zombie_required = true; /* check later that zombie is satisfied */ + } - rmc->coin_refresh_fee = dk->meta.fee_refresh; - rmc->coin_value = dk->meta.value; - /* check client used sane currency */ - if (GNUNET_YES != - TALER_amount_cmp_currency (&rmc->refresh_session.amount_with_fee, - &rmc->coin_value) ) - { - GNUNET_break_op (0); - TEH_KS_release (key_state); - return TALER_MHD_reply_with_error ( - connection, - MHD_HTTP_BAD_REQUEST, - TALER_EC_GENERIC_CURRENCY_MISMATCH, - rmc->refresh_session.amount_with_fee.currency); - } - /* check coin is actually properly signed */ - if (GNUNET_OK != - TALER_test_coin_valid (&rmc->refresh_session.coin, - &dk->denom_pub)) - { - GNUNET_break_op (0); - TEH_KS_release (key_state); - return TALER_MHD_reply_with_error (connection, - MHD_HTTP_FORBIDDEN, - TALER_EC_EXCHANGE_DENOMINATION_SIGNATURE_INVALID, - NULL); - } + rmc->coin_refresh_fee = dk->meta.fee_refresh; + rmc->coin_value = dk->meta.value; + /* check client used sane currency */ + if (GNUNET_YES != + TALER_amount_cmp_currency (&rmc->refresh_session.amount_with_fee, + &rmc->coin_value) ) + { + GNUNET_break_op (0); + return TALER_MHD_reply_with_error ( + connection, + MHD_HTTP_BAD_REQUEST, + TALER_EC_GENERIC_CURRENCY_MISMATCH, + rmc->refresh_session.amount_with_fee.currency); + } + /* check coin is actually properly signed */ + if (GNUNET_OK != + TALER_test_coin_valid (&rmc->refresh_session.coin, + &dk->denom_pub)) + { + GNUNET_break_op (0); + return TALER_MHD_reply_with_error (connection, + MHD_HTTP_FORBIDDEN, + TALER_EC_EXCHANGE_DENOMINATION_SIGNATURE_INVALID, + NULL); } - TEH_KS_release (key_state); /* sanity-check that "total melt amount > melt fee" */ if (0 < diff --git a/src/exchange/taler-exchange-httpd_recoup.c b/src/exchange/taler-exchange-httpd_recoup.c index aa521d66b..f63bf0722 100644 --- a/src/exchange/taler-exchange-httpd_recoup.c +++ b/src/exchange/taler-exchange-httpd_recoup.c @@ -30,9 +30,8 @@ #include "taler_mhd_lib.h" #include "taler-exchange-httpd_recoup.h" #include "taler-exchange-httpd_responses.h" -#include "taler-exchange-httpd_keystate.h" #include "taler-exchange-httpd_keys.h" - +#include "taler_exchangedb_lib.h" /** * Closure for #recoup_transaction. @@ -366,124 +365,103 @@ verify_and_execute_recoup (struct MHD_Connection *connection, size_t coin_ev_size; enum TALER_ErrorCode ec; unsigned int hc; + struct GNUNET_TIME_Absolute now; /* check denomination exists and is in recoup mode */ + dk = TEH_keys_denomination_by_hash (&coin->denom_pub_hash, + &ec, + &hc); + if (NULL == dk) { - struct TEH_KS_StateHandle *key_state; - struct GNUNET_TIME_Absolute now; + TALER_LOG_WARNING ( + "Denomination key in recoup request not in recoup mode\n"); + return TALER_MHD_reply_with_error (connection, + hc, + ec, + NULL); + } - key_state = TEH_KS_acquire (GNUNET_TIME_absolute_get ()); - if (NULL == key_state) - { - TALER_LOG_ERROR ("Lacking keys to operate\n"); - return TALER_MHD_reply_with_error (connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_EXCHANGE_GENERIC_BAD_CONFIGURATION, - "no keys"); - } - dk = TEH_keys_denomination_by_hash (&coin->denom_pub_hash, - &ec, - &hc); - if (NULL == dk) - { - TEH_KS_release (key_state); - TALER_LOG_WARNING ( - "Denomination key in recoup request not in recoup mode\n"); - return TALER_MHD_reply_with_error (connection, - hc, - ec, - NULL); - } + now = GNUNET_TIME_absolute_get (); + if (now.abs_value_us >= dk->meta.expire_deposit.abs_value_us) + { + /* This denomination is past the expiration time for recoup */ + return TALER_MHD_reply_with_error ( + connection, + MHD_HTTP_GONE, + TALER_EC_EXCHANGE_GENERIC_DENOMINATION_EXPIRED, + NULL); + } + if (now.abs_value_us < dk->meta.start.abs_value_us) + { + /* This denomination is not yet valid */ + return TALER_MHD_reply_with_error ( + connection, + MHD_HTTP_PRECONDITION_FAILED, + TALER_EC_EXCHANGE_GENERIC_DENOMINATION_VALIDITY_IN_FUTURE, + NULL); + } + if (! dk->recoup_possible) + { + /* This denomination is not eligible for recoup */ + return TALER_MHD_reply_with_error ( + connection, + MHD_HTTP_NOT_FOUND, + TALER_EC_EXCHANGE_RECOUP_NOT_ELIGIBLE, + NULL); + } - now = GNUNET_TIME_absolute_get (); - if (now.abs_value_us >= dk->meta.expire_deposit.abs_value_us) - { - /* This denomination is past the expiration time for recoup */ - TEH_KS_release (key_state); - return TALER_MHD_reply_with_error ( - connection, - MHD_HTTP_GONE, - TALER_EC_EXCHANGE_GENERIC_DENOMINATION_EXPIRED, - NULL); - } - if (now.abs_value_us < dk->meta.start.abs_value_us) - { - /* This denomination is not yet valid */ - TEH_KS_release (key_state); - return TALER_MHD_reply_with_error ( - connection, - MHD_HTTP_PRECONDITION_FAILED, - TALER_EC_EXCHANGE_GENERIC_DENOMINATION_VALIDITY_IN_FUTURE, - NULL); - } - if (! dk->recoup_possible) - { - /* This denomination is not eligible for recoup */ - TEH_KS_release (key_state); - return TALER_MHD_reply_with_error ( - connection, - MHD_HTTP_NOT_FOUND, - TALER_EC_EXCHANGE_RECOUP_NOT_ELIGIBLE, - NULL); - } + pc.value = dk->meta.value; - pc.value = dk->meta.value; + /* check denomination signature */ + if (GNUNET_YES != + TALER_test_coin_valid (coin, + &dk->denom_pub)) + { + TALER_LOG_WARNING ("Invalid coin passed for recoup\n"); + return TALER_MHD_reply_with_error (connection, + MHD_HTTP_FORBIDDEN, + TALER_EC_EXCHANGE_DENOMINATION_SIGNATURE_INVALID, + NULL); + } - /* check denomination signature */ - if (GNUNET_YES != - TALER_test_coin_valid (coin, - &dk->denom_pub)) - { - TALER_LOG_WARNING ("Invalid coin passed for recoup\n"); - TEH_KS_release (key_state); - return TALER_MHD_reply_with_error (connection, - MHD_HTTP_FORBIDDEN, - TALER_EC_EXCHANGE_DENOMINATION_SIGNATURE_INVALID, - NULL); - } + /* check recoup request signature */ + { + struct TALER_RecoupRequestPS pr = { + .purpose.purpose = htonl (TALER_SIGNATURE_WALLET_COIN_RECOUP), + .purpose.size = htonl (sizeof (struct TALER_RecoupRequestPS)), + .coin_pub = coin->coin_pub, + .h_denom_pub = coin->denom_pub_hash, + .coin_blind = *coin_bks + }; - /* check recoup request signature */ - { - struct TALER_RecoupRequestPS pr = { - .purpose.purpose = htonl (TALER_SIGNATURE_WALLET_COIN_RECOUP), - .purpose.size = htonl (sizeof (struct TALER_RecoupRequestPS)), - .coin_pub = coin->coin_pub, - .h_denom_pub = coin->denom_pub_hash, - .coin_blind = *coin_bks - }; - - if (GNUNET_OK != - GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_WALLET_COIN_RECOUP, - &pr, - &coin_sig->eddsa_signature, - &coin->coin_pub.eddsa_pub)) - { - TALER_LOG_WARNING ("Invalid signature on recoup request\n"); - TEH_KS_release (key_state); - return TALER_MHD_reply_with_error (connection, - MHD_HTTP_FORBIDDEN, - TALER_EC_EXCHANGE_RECOUP_SIGNATURE_INVALID, - NULL); - } - } - GNUNET_CRYPTO_hash (&coin->coin_pub.eddsa_pub, - sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey), - &c_hash); - if (GNUNET_YES != - TALER_rsa_blind (&c_hash, - &coin_bks->bks, - dk->denom_pub.rsa_public_key, - &coin_ev, - &coin_ev_size)) + if (GNUNET_OK != + GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_WALLET_COIN_RECOUP, + &pr, + &coin_sig->eddsa_signature, + &coin->coin_pub.eddsa_pub)) { - GNUNET_break (0); - TEH_KS_release (key_state); + TALER_LOG_WARNING ("Invalid signature on recoup request\n"); return TALER_MHD_reply_with_error (connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_EXCHANGE_RECOUP_BLINDING_FAILED, + MHD_HTTP_FORBIDDEN, + TALER_EC_EXCHANGE_RECOUP_SIGNATURE_INVALID, NULL); } - TEH_KS_release (key_state); + } + GNUNET_CRYPTO_hash (&coin->coin_pub.eddsa_pub, + sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey), + &c_hash); + if (GNUNET_YES != + TALER_rsa_blind (&c_hash, + &coin_bks->bks, + dk->denom_pub.rsa_public_key, + &coin_ev, + &coin_ev_size)) + { + GNUNET_break (0); + return TALER_MHD_reply_with_error (connection, + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_EC_EXCHANGE_RECOUP_BLINDING_FAILED, + NULL); } GNUNET_CRYPTO_hash (coin_ev, coin_ev_size, diff --git a/src/exchange/taler-exchange-httpd_refreshes_reveal.c b/src/exchange/taler-exchange-httpd_refreshes_reveal.c index 9b3a42f97..9533ad532 100644 --- a/src/exchange/taler-exchange-httpd_refreshes_reveal.c +++ b/src/exchange/taler-exchange-httpd_refreshes_reveal.c @@ -28,7 +28,6 @@ #include "taler-exchange-httpd_mhd.h" #include "taler-exchange-httpd_refreshes_reveal.h" #include "taler-exchange-httpd_responses.h" -#include "taler-exchange-httpd_keystate.h" #include "taler-exchange-httpd_keys.h" @@ -521,9 +520,8 @@ refreshes_reveal_persist (void *cls, /** - * Resolve denomination hashes using the @a key_state + * Resolve denomination hashes. * - * @param key_state the key state * @param connection the MHD connection to handle * @param rctx context for the operation, partially built at this time * @param link_sigs_json link signatures in JSON format @@ -532,8 +530,7 @@ refreshes_reveal_persist (void *cls, * @return MHD result code */ static MHD_RESULT -resolve_refreshes_reveal_denominations (struct TEH_KS_StateHandle *key_state, - struct MHD_Connection *connection, +resolve_refreshes_reveal_denominations (struct MHD_Connection *connection, struct RevealContext *rctx, const json_t *link_sigs_json, const json_t *new_denoms_h_json, @@ -905,28 +902,11 @@ handle_refreshes_reveal_json (struct MHD_Connection *connection, return (GNUNET_NO == res) ? MHD_YES : MHD_NO; } - { - struct TEH_KS_StateHandle *key_state; - int ret; - - key_state = TEH_KS_acquire (GNUNET_TIME_absolute_get ()); - if (NULL == key_state) - { - TALER_LOG_ERROR ("Lacking keys to operate\n"); - return TALER_MHD_reply_with_error (connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_EXCHANGE_GENERIC_KEYS_MISSING, - NULL); - } - ret = resolve_refreshes_reveal_denominations (key_state, - connection, - rctx, - link_sigs_json, - new_denoms_h_json, - coin_evs); - TEH_KS_release (key_state); - return ret; - } + return resolve_refreshes_reveal_denominations (connection, + rctx, + link_sigs_json, + new_denoms_h_json, + coin_evs); } diff --git a/src/exchange/taler-exchange-httpd_refund.c b/src/exchange/taler-exchange-httpd_refund.c index 6bb943483..ace6e28f4 100644 --- a/src/exchange/taler-exchange-httpd_refund.c +++ b/src/exchange/taler-exchange-httpd_refund.c @@ -32,7 +32,6 @@ #include "taler_mhd_lib.h" #include "taler-exchange-httpd_refund.h" #include "taler-exchange-httpd_responses.h" -#include "taler-exchange-httpd_keystate.h" #include "taler-exchange-httpd_keys.h" @@ -448,52 +447,36 @@ verify_and_execute_refund (struct MHD_Connection *connection, } { - struct TEH_KS_StateHandle *key_state; - - key_state = TEH_KS_acquire (GNUNET_TIME_absolute_get ()); - if (NULL == key_state) + /* Obtain information about the coin's denomination! */ + struct TEH_DenominationKey *dk; + unsigned int hc; + enum TALER_ErrorCode ec; + + dk = TEH_keys_denomination_by_hash (&denom_hash, + &ec, + &hc); + if (NULL == dk) { - TALER_LOG_ERROR ("Lacking keys to operate\n"); + /* DKI not found, but we do have a coin with this DK in our database; + not good... */ + GNUNET_break (0); return TALER_MHD_reply_with_error (connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_EXCHANGE_GENERIC_BAD_CONFIGURATION, - "no keys"); + hc, + ec, + NULL); } - /* Obtain information about the coin's denomination! */ - { - struct TEH_DenominationKey *dk; - unsigned int hc; - enum TALER_ErrorCode ec; - - dk = TEH_keys_denomination_by_hash (&denom_hash, - &ec, - &hc); - if (NULL == dk) - { - /* DKI not found, but we do have a coin with this DK in our database; - not good... */ - GNUNET_break (0); - TEH_KS_release (key_state); - return TALER_MHD_reply_with_error (connection, - hc, - ec, - NULL); - } - if (GNUNET_TIME_absolute_get ().abs_value_us >= - dk->meta.expire_deposit.abs_value_us) - { - /* This denomination is past the expiration time for deposits, and thus refunds */ - TEH_KS_release (key_state); - return TALER_MHD_reply_with_error ( - connection, - MHD_HTTP_GONE, - TALER_EC_EXCHANGE_GENERIC_DENOMINATION_EXPIRED, - NULL); - } - refund->details.refund_fee = dk->meta.fee_refund; + if (GNUNET_TIME_absolute_get ().abs_value_us >= + dk->meta.expire_deposit.abs_value_us) + { + /* This denomination is past the expiration time for deposits, and thus refunds */ + return TALER_MHD_reply_with_error ( + connection, + MHD_HTTP_GONE, + TALER_EC_EXCHANGE_GENERIC_DENOMINATION_EXPIRED, + NULL); } - TEH_KS_release (key_state); + refund->details.refund_fee = dk->meta.fee_refund; } /* Finally run the actual transaction logic */ diff --git a/src/exchange/taler-exchange-httpd_reserves_get.c b/src/exchange/taler-exchange-httpd_reserves_get.c index a5ebaabf1..b901afbbc 100644 --- a/src/exchange/taler-exchange-httpd_reserves_get.c +++ b/src/exchange/taler-exchange-httpd_reserves_get.c @@ -27,7 +27,6 @@ #include "taler_json_lib.h" #include "taler-exchange-httpd_reserves_get.h" #include "taler-exchange-httpd_responses.h" -#include "taler-exchange-httpd_keystate.h" /** diff --git a/src/exchange/taler-exchange-httpd_responses.c b/src/exchange/taler-exchange-httpd_responses.c index c0ec6d95d..ba050e9f7 100644 --- a/src/exchange/taler-exchange-httpd_responses.c +++ b/src/exchange/taler-exchange-httpd_responses.c @@ -28,7 +28,6 @@ #include "taler_util.h" #include "taler_json_lib.h" #include "taler_mhd_lib.h" -#include "taler-exchange-httpd_keystate.h" #include "taler-exchange-httpd_keys.h" diff --git a/src/exchange/taler-exchange-httpd_transfers_get.c b/src/exchange/taler-exchange-httpd_transfers_get.c index b7f24f23f..578b94284 100644 --- a/src/exchange/taler-exchange-httpd_transfers_get.c +++ b/src/exchange/taler-exchange-httpd_transfers_get.c @@ -25,7 +25,6 @@ #include <pthread.h> #include "taler_signatures.h" #include "taler-exchange-httpd_keys.h" -#include "taler-exchange-httpd_keystate.h" #include "taler-exchange-httpd_transfers_get.h" #include "taler-exchange-httpd_responses.h" #include "taler_json_lib.h" diff --git a/src/exchange/taler-exchange-httpd_wire.c b/src/exchange/taler-exchange-httpd_wire.c deleted file mode 100644 index 471fa4fad..000000000 --- a/src/exchange/taler-exchange-httpd_wire.c +++ /dev/null @@ -1,399 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2015-2020 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_wire.c - * @brief Handle /wire requests - * @author Christian Grothoff - */ -#include "platform.h" -#include <gnunet/gnunet_json_lib.h> -#include "taler-exchange-httpd_keystate.h" -#include "taler-exchange-httpd_responses.h" -#include "taler-exchange-httpd_wire.h" -#include "taler_json_lib.h" -#include "taler_mhd_lib.h" -#include <jansson.h> - -/** - * Cached JSON for /wire response. - */ -static json_t *wire_methods; - -/** - * Array of wire methods supported by this exchange. - */ -static json_t *wire_accounts_array; - -/** - * Object mapping wire methods to the respective fee structure. - */ -static json_t *wire_fee_object; - - -/** - * Convert fee structure to JSON result to be returned - * as part of a /wire response. - * - * @param af fee structure to convert - * @return NULL on error, otherwise json data structure for /wire. - */ -static json_t * -fees_to_json (struct TALER_EXCHANGEDB_AggregateFees *af) -{ - json_t *a; - - a = json_array (); - if (NULL == a) - { - GNUNET_break (0); /* out of memory? */ - return NULL; - } - while (NULL != af) - { - if ( (GNUNET_NO == GNUNET_TIME_round_abs (&af->start_date)) || - (GNUNET_NO == GNUNET_TIME_round_abs (&af->end_date)) ) - { - GNUNET_break (0); /* bad timestamps, should not happen */ - json_decref (a); - return NULL; - } - if (0 != - json_array_append_new (a, - json_pack ("{s:o, s:o, s:o, s:o, s:o}", - "wire_fee", TALER_JSON_from_amount ( - &af->wire_fee), - "closing_fee", - TALER_JSON_from_amount ( - &af->closing_fee), - "start_date", - GNUNET_JSON_from_time_abs ( - af->start_date), - "end_date", - GNUNET_JSON_from_time_abs ( - af->end_date), - "sig", GNUNET_JSON_from_data_auto ( - &af->master_sig)))) - { - GNUNET_break (0); /* out of memory? */ - json_decref (a); - return NULL; - } - af = af->next; - } - return a; -} - - -/** - * Obtain fee structure for @a method wire transfers. - * - * @param method method to load fees for - * @return JSON object (to be freed by caller) with fee structure - */ -static json_t * -get_fees (const char *method) -{ - struct TALER_EXCHANGEDB_AggregateFees *af; - struct GNUNET_TIME_Absolute now; - - af = TALER_EXCHANGEDB_fees_read (TEH_cfg, - method); - now = GNUNET_TIME_absolute_get (); - while ( (NULL != af) && - (af->end_date.abs_value_us < now.abs_value_us) ) - { - struct TALER_EXCHANGEDB_AggregateFees *n = af->next; - - GNUNET_free (af); - af = n; - } - if (NULL == af) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Failed to find current wire transfer fees for `%s' at time %s\n", - method, - GNUNET_STRINGS_absolute_time_to_string (now)); - return NULL; - } - { - json_t *j; - - j = fees_to_json (af); - TALER_EXCHANGEDB_fees_free (af); - return j; - } -} - - -/** - * Load wire fees for @a method. - * - * @param method wire method to load fee structure for - * @return #GNUNET_OK on success - */ -static int -load_fee (const char *method) -{ - json_t *fees; - - if (NULL != json_object_get (wire_fee_object, - method)) - return GNUNET_OK; /* already have them */ - fees = get_fees (method); - if (NULL == fees) - return GNUNET_SYSERR; - /* Add fees to #wire_fee_object */ - if (0 != - json_object_set_new (wire_fee_object, - method, - fees)) - { - GNUNET_break (0); - return GNUNET_SYSERR; - } - return GNUNET_OK; -} - - -/** - * Initialize account; checks if @a ai has /wire information, and if so, - * adds the /wire information (if included) to our responses. - * - * @param cls pointer to `int` to set to #GNUNET_SYSERR on errors - * @param ai details about the account we should load the wire details for - */ -static void -load_account (void *cls, - const struct TALER_EXCHANGEDB_AccountInfo *ai) -{ - int *ret = cls; - - if ( (NULL != ai->wire_response_filename) && - (GNUNET_YES == ai->credit_enabled) ) - { - json_t *wire_s; - json_error_t error; - - if (NULL == (wire_s = json_load_file (ai->wire_response_filename, - JSON_REJECT_DUPLICATES, - &error))) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Failed to parse `%s': %s at %d:%d (%d)\n", - ai->wire_response_filename, - error.text, - error.line, - error.column, - error.position); - *ret = GNUNET_SYSERR; - return; - } - - { - char *url; - - if (NULL == (url = TALER_JSON_wire_to_payto (wire_s))) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Wire response file `%s' malformed\n", - ai->wire_response_filename); - json_decref (wire_s); - *ret = GNUNET_SYSERR; - return; - } - if (0 != strcasecmp (url, - ai->payto_uri)) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "URL in wire response file `%s' does not match URL in configuration (%s vs %s)!\n", - ai->wire_response_filename, - url, - ai->payto_uri); - json_decref (wire_s); - GNUNET_free (url); - *ret = GNUNET_SYSERR; - return; - } - GNUNET_free (url); - } - /* Provide friendly error message if user forgot to sign wire response. */ - if (NULL == json_object_get (wire_s, - "master_sig")) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Wire response file `%s' has not been signed." - " Use taler-exchange-wire to sign it.\n", - ai->wire_response_filename); - json_decref (wire_s); - *ret = GNUNET_SYSERR; - return; - } - if (GNUNET_OK != - TALER_JSON_exchange_wire_signature_check (wire_s, - &TEH_master_public_key)) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Invalid signature in `%s' for public key `%s'\n", - ai->wire_response_filename, - GNUNET_p2s (&TEH_master_public_key.eddsa_pub)); - json_decref (wire_s); - *ret = GNUNET_SYSERR; - return; - } - if (GNUNET_OK == - load_fee (ai->method)) - { - if (0 != - json_array_append_new (wire_accounts_array, - wire_s)) - { - GNUNET_break (0); - *ret = GNUNET_SYSERR; - } - } - else - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Wire fees not specified for `%s'\n", - ai->method); - *ret = GNUNET_SYSERR; - } - } - else if (GNUNET_YES == ai->debit_enabled) - { - if (GNUNET_OK != - load_fee (ai->method)) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Wire transfer fees for `%s' are not given correctly\n", - ai->method); - *ret = GNUNET_SYSERR; - return; - } - } -} - - -/** - * Handle a "/wire" request. - * - * @param rh context of the handler - * @param connection the MHD connection to handle - * @param args array of additional options (must be empty for this function) - * @return MHD result code - */ -MHD_RESULT -TEH_handler_wire (const struct TEH_RequestHandler *rh, - struct MHD_Connection *connection, - const char *const args[]) -{ - (void) rh; - (void) args; - GNUNET_assert (NULL != wire_methods); - return TALER_MHD_reply_json (connection, - wire_methods, - MHD_HTTP_OK); -} - - -/** - * Initialize wire subsystem. - * - * @param cfg configuration to use - * @return #GNUNET_OK on success, #GNUNET_SYSERR if we found no valid - * wire methods - */ -int -TEH_WIRE_init (const struct GNUNET_CONFIGURATION_Handle *cfg) -{ - wire_accounts_array = json_array (); - if (NULL == wire_accounts_array) - { - GNUNET_break (0); - return GNUNET_SYSERR; - } - wire_fee_object = json_object (); - if (NULL == wire_fee_object) - { - GNUNET_break (0); - TEH_WIRE_done (); - return GNUNET_SYSERR; - } - { - int ret; - - ret = GNUNET_OK; - TALER_EXCHANGEDB_find_accounts (cfg, - &load_account, - &ret); - if (GNUNET_OK != ret) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Error setting up bank accounts\n"); - TEH_WIRE_done (); - return GNUNET_SYSERR; - } - } - if ( (0 == json_array_size (wire_accounts_array)) || - (0 == json_object_size (wire_fee_object)) ) - { - TEH_WIRE_done (); - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "No bank accounts configured\n"); - return GNUNET_SYSERR; - } - wire_methods = json_pack ("{s:O, s:O, s:o}", - "accounts", wire_accounts_array, - "fees", wire_fee_object, - "master_public_key", - GNUNET_JSON_from_data_auto ( - &TEH_master_public_key)); - if (NULL == wire_methods) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Failed to find properly configured wire transfer method\n"); - TEH_WIRE_done (); - return GNUNET_SYSERR; - } - return GNUNET_OK; -} - - -/** - * Clean up wire subsystem. - */ -void -TEH_WIRE_done () -{ - if (NULL != wire_methods) - { - json_decref (wire_methods); - wire_methods = NULL; - } - if (NULL != wire_fee_object) - { - json_decref (wire_fee_object); - wire_fee_object = NULL; - } - if (NULL != wire_accounts_array) - { - json_decref (wire_accounts_array); - wire_accounts_array = NULL; - } -} - - -/* end of taler-exchange-httpd_wire.c */ diff --git a/src/exchange/taler-exchange-httpd_wire.h b/src/exchange/taler-exchange-httpd_wire.h index 7df874320..b07bfcfb2 100644 --- a/src/exchange/taler-exchange-httpd_wire.h +++ b/src/exchange/taler-exchange-httpd_wire.h @@ -29,12 +29,10 @@ /** * Initialize wire subsystem. * - * @param cfg configuration to use - * @return #GNUNET_OK on success, #GNUNET_SYSERR if we found no valid - * wire methods + * @return #GNUNET_OK on success, #GNUNET_SYSERR on error. */ int -TEH_WIRE_init (const struct GNUNET_CONFIGURATION_Handle *cfg); +TEH_WIRE_init (void); /** @@ -45,6 +43,19 @@ TEH_WIRE_done (void); /** + * Something changed in the database. Rebuild the wire replies. This function + * should be called if the exchange learns about a new signature from our + * master key. + * + * (We do not do so immediately, but merely signal to all threads that they + * need to rebuild their wire state upon the next call to + * #wire_get_state()). + */ +void +TEH_wire_update_state (void); + + +/** * Handle a "/wire" request. * * @param rh context of the handler diff --git a/src/exchange/taler-exchange-httpd_wire2.c b/src/exchange/taler-exchange-httpd_wire2.c index a80557b46..b4f60b725 100644 --- a/src/exchange/taler-exchange-httpd_wire2.c +++ b/src/exchange/taler-exchange-httpd_wire2.c @@ -20,7 +20,6 @@ */ #include "platform.h" #include <gnunet/gnunet_json_lib.h> -#include "taler-exchange-httpd_keystate.h" #include "taler-exchange-httpd_responses.h" #include "taler-exchange-httpd_wire.h" #include "taler_json_lib.h" @@ -77,6 +76,47 @@ destroy_wire_state (struct WireStateHandle *wsh) /** + * Free memory assciated with wire state. Signature + * suitable for pthread_key_create(). + * + * @param[in] cls the `struct WireStateHandle` to destroy + */static void +destroy_wire_state_cb (void *cls) +{ + struct WireStateHandle *wsh = cls; + + destroy_wire_state (wsh); +} + + +/** + * Initialize WIRE submodule. + * + * @return #GNUNET_OK on success + */ +int +TEH_WIRE_init () +{ + if (0 != + pthread_key_create (&wire_state, + &destroy_wire_state_cb)) + return GNUNET_SYSERR; + return GNUNET_OK; +} + + +/** + * Fully clean up our state. + */ +void +TEH_WIRE_done () +{ + GNUNET_assert (0 == + pthread_key_delete (wire_state)); +} + + +/** * Add information about a wire account to @a cls. * * @param cls a `json_t *` object to expand with wire account details @@ -95,9 +135,9 @@ add_wire_account (void *cls, json_array_append_new ( a, json_pack ("{s:s, s:o}", - "url", /* "payto_uri" would be better, but this is the name in the spec */ + "payto_uri", payto_uri, - "sig", + "master_sig", GNUNET_JSON_from_data_auto (master_sig)))) { GNUNET_break (0); /* out of memory!? */ @@ -182,7 +222,7 @@ build_wire_state (void) json_array_foreach (wire_accounts_array, index, account) { char *wire_method; const char *payto_uri = json_string_value (json_object_get (account, - "url")); + "payto_uri")); GNUNET_assert (NULL != payto_uri); wire_method = TALER_payto_get_method (payto_uri); if (NULL == json_object_get (wire_fee_object, @@ -230,17 +270,8 @@ build_wire_state (void) } -/** - * Something changed in the database. Rebuild the wire replies. This function - * should be called if the exchange learns about a new signature from our - * master key. - * - * (We do not do so immediately, but merely signal to all threads that they - * need to rebuild their wire state upon the next call to - * #wire_get_state()). - */ void -TEH_wire_update_state () +TEH_wire_update_state (void) { __sync_fetch_and_add (&wire_generation, 1); diff --git a/src/exchange/taler-exchange-httpd_withdraw.c b/src/exchange/taler-exchange-httpd_withdraw.c index 035273bc6..68a8e5fd0 100644 --- a/src/exchange/taler-exchange-httpd_withdraw.c +++ b/src/exchange/taler-exchange-httpd_withdraw.c @@ -30,7 +30,6 @@ #include "taler_mhd_lib.h" #include "taler-exchange-httpd_withdraw.h" #include "taler-exchange-httpd_responses.h" -#include "taler-exchange-httpd_keystate.h" #include "taler-exchange-httpd_keys.h" @@ -125,11 +124,6 @@ struct WithdrawContext char *blinded_msg; /** - * Key state to use to inspect previous withdrawal values. - */ - struct TEH_KS_StateHandle *key_state; - - /** * Number of bytes in @e blinded_msg. */ size_t blinded_msg_len; @@ -384,16 +378,6 @@ TEH_handler_withdraw (const struct TEH_RequestHandler *rh, if (GNUNET_OK != res) return (GNUNET_SYSERR == res) ? MHD_NO : MHD_YES; } - wc.key_state = TEH_KS_acquire (GNUNET_TIME_absolute_get ()); - if (NULL == wc.key_state) - { - TALER_LOG_ERROR ("Lacking keys to operate\n"); - GNUNET_JSON_parse_free (spec); - return TALER_MHD_reply_with_error (connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_EXCHANGE_GENERIC_BAD_CONFIGURATION, - "no keys"); - } { unsigned int hc; enum TALER_ErrorCode ec; @@ -405,7 +389,6 @@ TEH_handler_withdraw (const struct TEH_RequestHandler *rh, if (NULL == dk) { GNUNET_JSON_parse_free (spec); - TEH_KS_release (wc.key_state); return TALER_MHD_reply_with_error (connection, hc, ec, @@ -415,7 +398,6 @@ TEH_handler_withdraw (const struct TEH_RequestHandler *rh, if (now.abs_value_us >= dk->meta.expire_withdraw.abs_value_us) { /* This denomination is past the expiration time for withdraws */ - TEH_KS_release (wc.key_state); GNUNET_JSON_parse_free (spec); return TALER_MHD_reply_with_error ( connection, @@ -426,7 +408,6 @@ TEH_handler_withdraw (const struct TEH_RequestHandler *rh, if (now.abs_value_us < dk->meta.start.abs_value_us) { /* This denomination is not yet valid */ - TEH_KS_release (wc.key_state); GNUNET_JSON_parse_free (spec); return TALER_MHD_reply_with_error ( connection, @@ -437,7 +418,6 @@ TEH_handler_withdraw (const struct TEH_RequestHandler *rh, if (dk->recoup_possible) { /* This denomination has been revoked */ - TEH_KS_release (wc.key_state); GNUNET_JSON_parse_free (spec); return TALER_MHD_reply_with_error ( connection, @@ -454,7 +434,6 @@ TEH_handler_withdraw (const struct TEH_RequestHandler *rh, &dk->meta.fee_withdraw)) { GNUNET_JSON_parse_free (spec); - TEH_KS_release (wc.key_state); return TALER_MHD_reply_with_error (connection, MHD_HTTP_INTERNAL_SERVER_ERROR, TALER_EC_EXCHANGE_WITHDRAW_AMOUNT_FEE_OVERFLOW, @@ -483,7 +462,6 @@ TEH_handler_withdraw (const struct TEH_RequestHandler *rh, TALER_LOG_WARNING ( "Client supplied invalid signature for withdraw request\n"); GNUNET_JSON_parse_free (spec); - TEH_KS_release (wc.key_state); return TALER_MHD_reply_with_error (connection, MHD_HTTP_FORBIDDEN, TALER_EC_EXCHANGE_WITHDRAW_RESERVE_SIGNATURE_INVALID, @@ -501,7 +479,6 @@ TEH_handler_withdraw (const struct TEH_RequestHandler *rh, { GNUNET_break (0); GNUNET_JSON_parse_free (spec); - TEH_KS_release (wc.key_state); return TALER_MHD_reply_with_ec (connection, ec, NULL); @@ -519,7 +496,6 @@ TEH_handler_withdraw (const struct TEH_RequestHandler *rh, &withdraw_transaction, &wc)) { - TEH_KS_release (wc.key_state); /* Even if #withdraw_transaction() failed, it may have created a signature (or we might have done it optimistically above). */ if (NULL != wc.collectable.sig.rsa_signature) @@ -530,7 +506,6 @@ TEH_handler_withdraw (const struct TEH_RequestHandler *rh, } /* Clean up and send back final (positive) response */ - TEH_KS_release (wc.key_state); GNUNET_JSON_parse_free (spec); { diff --git a/src/exchange/test_taler_exchange_httpd.sh b/src/exchange/test_taler_exchange_httpd.sh index 943488911..e8dc46af8 100755 --- a/src/exchange/test_taler_exchange_httpd.sh +++ b/src/exchange/test_taler_exchange_httpd.sh @@ -27,7 +27,7 @@ unset XDG_CONFIG_HOME echo -n "Launching exchange ..." PREFIX= # Uncomment this line to run with valgrind... -# PREFIX="valgrind --leak-check=yes --track-fds=yes --error-exitcode=1 --log-file=valgrind.%p" +#PREFIX="valgrind --leak-check=yes --track-fds=yes --error-exitcode=1 --log-file=valgrind.%p" # Setup database taler-exchange-dbinit -c test_taler_exchange_httpd.conf &> /dev/null diff --git a/src/include/taler_testing_lib.h b/src/include/taler_testing_lib.h index a5a0b4354..1722a8338 100644 --- a/src/include/taler_testing_lib.h +++ b/src/include/taler_testing_lib.h @@ -447,13 +447,6 @@ struct TALER_TESTING_Interpreter char *exchange_url; /** - * #GNUNET_OK if key state should be reloaded. NOTE: this - * field can be removed because a new "send signal" command - * has been introduced. - */ - int reload_keys; - - /** * Is the interpreter running (#GNUNET_YES) or waiting * for /keys (#GNUNET_NO)? */ @@ -1228,54 +1221,6 @@ TALER_TESTING_cmd_exec_transfer (const char *label, /** - * Make the "keyup" CMD. - * - * @param label command label. - * @param config_filename configuration filename. - * @return the command. - */ -struct TALER_TESTING_Command -TALER_TESTING_cmd_exec_keyup (const char *label, - const char *config_filename); - - -/** - * Make the "keyup" CMD, with "--timestamp" option. - * - * @param label command label. - * @param config_filename configuration filename. - * @param now Unix timestamp representing the fake "now". - * @return the command. - */ -struct TALER_TESTING_Command -TALER_TESTING_cmd_exec_keyup_with_now (const char *label, - const char *config_filename, - struct GNUNET_TIME_Absolute now); - - -/** - * Make a "check keys" command. This type of command - * checks whether the number of denomination keys from - * @a exchange matches @a num_denom_keys. - * - * @param label command label - * @param generation when this command is run, exactly @a - * generation /keys downloads took place. If the number - * of downloads is less than @a generation, the logic will - * first make sure that @a generation downloads are done, - * and _then_ execute the rest of the command. - * @param num_denom_keys expected number of denomination keys. - * @param now timestamp to use when fetching keys - * @return the command. - */ -struct TALER_TESTING_Command -TALER_TESTING_cmd_check_keys_with_now (const char *label, - unsigned int generation, - unsigned int num_denom_keys, - struct GNUNET_TIME_Absolute now); - - -/** * Make a "auditor sign" CMD. * * @param label command label @@ -1840,27 +1785,21 @@ TALER_TESTING_cmd_wait_service (const char *label, /** - * Make a "check keys" command. This type of command - * checks whether the number of denomination keys from - * @a exchange matches @a num_denom_keys. + * Make a "check keys" command. * * @param label command label * @param generation how many /keys responses are expected to * have been returned when this CMD will be run. - * @param num_denom_keys expected number of denomination keys. - * * @return the command. */ struct TALER_TESTING_Command TALER_TESTING_cmd_check_keys (const char *label, - unsigned int generation, - unsigned int num_denom_keys); + unsigned int generation); /** * Make a "check keys" command that forcedly does NOT cherry pick; - * just redownload the whole /keys. Then checks whether the number - * of denomination keys from @a exchange matches @a num_denom_keys. + * just redownload the whole /keys. * * @param label command label * @param generation when this command is run, exactly @a @@ -1868,20 +1807,15 @@ TALER_TESTING_cmd_check_keys (const char *label, * of downloads is less than @a generation, the logic will * first make sure that @a generation downloads are done, * and _then_ execute the rest of the command. - * @param num_denom_keys expected number of denomination keys. * @return the command. */ struct TALER_TESTING_Command TALER_TESTING_cmd_check_keys_pull_all_keys (const char *label, - unsigned int generation, - unsigned int num_denom_keys); + unsigned int generation); /** - * Make a "check keys" command. This type of command - * checks whether the number of denomination keys from - * @a exchange matches @a num_denom_keys. Additionally, - * it lets the user set a last denom issue date to be + * Make a "check keys" command. It lets the user set a last denom issue date to be * used in the request for /keys. * * @param label command label @@ -1890,17 +1824,15 @@ TALER_TESTING_cmd_check_keys_pull_all_keys (const char *label, * of downloads is less than @a generation, the logic will * first make sure that @a generation downloads are done, * and _then_ execute the rest of the command. - * @param num_denom_keys expected number of denomination keys. * @param last_denom_date date to be set in the "last_denom_issue" * URL parameter of /keys. * @return the command. */ struct TALER_TESTING_Command -TALER_TESTING_cmd_check_keys_with_last_denom (const char *label, - unsigned int generation, - unsigned int num_denom_keys, - struct GNUNET_TIME_Absolute - last_denom_date); +TALER_TESTING_cmd_check_keys_with_last_denom ( + const char *label, + unsigned int generation, + struct GNUNET_TIME_Absolute last_denom_date); /** @@ -2170,6 +2102,22 @@ TALER_TESTING_cmd_exec_offline_sign_keys (const char *label, /** + * Sign a wire fee. + * + * @param label command label. + * @param config_filename configuration filename. + * @param wire_fee the wire fee to affirm (for the current year) + * @param closing_fee the closing fee to affirm (for the current year) + * @return the command + */ +struct TALER_TESTING_Command +TALER_TESTING_cmd_exec_offline_sign_fees (const char *label, + const char *config_filename, + const char *wire_fee, + const char *closing_fee); + + +/** * Revoke an exchange denomination key. * * @param label command label. diff --git a/src/testing/Makefile.am b/src/testing/Makefile.am index ceba62201..0e09b1462 100644 --- a/src/testing/Makefile.am +++ b/src/testing/Makefile.am @@ -56,10 +56,10 @@ libtalertesting_la_SOURCES = \ testing_api_cmd_exec_aggregator.c \ testing_api_cmd_exec_auditor-sign.c \ testing_api_cmd_exec_closer.c \ - testing_api_cmd_exec_keyup.c \ testing_api_cmd_exec_transfer.c \ testing_api_cmd_exec_wirewatch.c \ testing_api_cmd_insert_deposit.c \ + testing_api_cmd_offline_sign_fees.c \ testing_api_cmd_offline_sign_keys.c \ testing_api_cmd_set_wire_fee.c \ testing_api_cmd_recoup.c \ diff --git a/src/testing/test_auditor_api.c b/src/testing/test_auditor_api.c index 48f03119e..c4f890e62 100644 --- a/src/testing/test_auditor_api.c +++ b/src/testing/test_auditor_api.c @@ -630,8 +630,7 @@ run (void *cls, TALER_TESTING_cmd_exec_offline_sign_keys ("offline-sign-future-keys", CONFIG_FILE), TALER_TESTING_cmd_check_keys_pull_all_keys ("refetch /keys", - 2, - 270 /* FIXME: wrong number... */), + 2), CMD_RUN_AUDITOR ("virgin-auditor"), TALER_TESTING_cmd_exchanges_with_url ("check-exchange", MHD_HTTP_OK, diff --git a/src/testing/test_exchange_api.c b/src/testing/test_exchange_api.c index f24a3ff73..18f7237b6 100644 --- a/src/testing/test_exchange_api.c +++ b/src/testing/test_exchange_api.c @@ -956,9 +956,12 @@ run (void *cls, false), TALER_TESTING_cmd_exec_offline_sign_keys ("offline-sign-future-keys", CONFIG_FILE), + TALER_TESTING_cmd_exec_offline_sign_fees ("offline-sign-fees", + CONFIG_FILE, + "EUR:0.01", + "EUR:0.01"), TALER_TESTING_cmd_check_keys_pull_all_keys ("refetch /keys", - 1, - 270 /* FIXME: wrong number... */), + 1), TALER_TESTING_cmd_batch ("wire", wire), TALER_TESTING_cmd_batch ("withdraw", diff --git a/src/testing/test_exchange_api_overlapping_keys_bug.c b/src/testing/test_exchange_api_overlapping_keys_bug.c index 19093243e..86f75f935 100644 --- a/src/testing/test_exchange_api_overlapping_keys_bug.c +++ b/src/testing/test_exchange_api_overlapping_keys_bug.c @@ -81,14 +81,11 @@ run (void *cls, TALER_TESTING_cmd_exec_offline_sign_keys ("offline-sign-future-keys", CONFIG_FILE), TALER_TESTING_cmd_check_keys_pull_all_keys ("refetch /keys", - 1, - 1 /* FIXME: wrong number... */), + 1), TALER_TESTING_cmd_check_keys ("first-download", - 1, 1), /* Causes GET /keys?last_denom_issue=0 */ TALER_TESTING_cmd_check_keys_with_last_denom ("second-download", - 3, 1, GNUNET_TIME_UNIT_ZERO_ABS), TALER_TESTING_cmd_end () diff --git a/src/testing/test_exchange_api_revocation.c b/src/testing/test_exchange_api_revocation.c index 555891497..94e162b9a 100644 --- a/src/testing/test_exchange_api_revocation.c +++ b/src/testing/test_exchange_api_revocation.c @@ -72,8 +72,7 @@ run (void *cls, TALER_TESTING_cmd_exec_offline_sign_keys ("offline-sign-future-keys", CONFIG_FILE), TALER_TESTING_cmd_check_keys_pull_all_keys ("refetch /keys", - 1, - 270 /* FIXME: wrong number... */), + 1), /** * Fill reserve with EUR:10.02, as withdraw fee is 1 ct per * config. diff --git a/src/testing/test_exchange_api_twisted.c b/src/testing/test_exchange_api_twisted.c index 05867f89b..818b54deb 100644 --- a/src/testing/test_exchange_api_twisted.c +++ b/src/testing/test_exchange_api_twisted.c @@ -221,8 +221,7 @@ run (void *cls, "Wed, 19 Jan 586524 08:01:49 GMT"), TALER_TESTING_cmd_check_keys_pull_all_keys ( "check-keys-expiration-0", - 2, - 270), + 2), /** * Run some normal commands after this to make sure everything is fine. */ @@ -244,8 +243,7 @@ run (void *cls, TALER_TESTING_cmd_exec_offline_sign_keys ("offline-sign-future-keys", CONFIG_FILE), TALER_TESTING_cmd_check_keys_pull_all_keys ("refetch /keys", - 1, - 270 /* FIXME: wrong number... */), + 1), TALER_TESTING_cmd_batch ("refresh-reveal-409-conflict", refresh_409_conflict), TALER_TESTING_cmd_batch ("refund", diff --git a/src/testing/test_exchange_management_api.c b/src/testing/test_exchange_management_api.c index 18f6dedf6..b53926fd2 100644 --- a/src/testing/test_exchange_management_api.c +++ b/src/testing/test_exchange_management_api.c @@ -143,8 +143,7 @@ run (void *cls, TALER_TESTING_cmd_exec_offline_sign_keys ("download-future-keys", CONFIG_FILE), TALER_TESTING_cmd_check_keys_pull_all_keys ("refetch /keys", - 1, - 270 /* FIXME: wrong number... */), + 1), TALER_TESTING_cmd_end () }; diff --git a/src/testing/test_taler_exchange_wirewatch.c b/src/testing/test_taler_exchange_wirewatch.c index 169c959be..08c5936fd 100644 --- a/src/testing/test_taler_exchange_wirewatch.c +++ b/src/testing/test_taler_exchange_wirewatch.c @@ -92,8 +92,7 @@ run (void *cls, TALER_TESTING_cmd_exec_offline_sign_keys ("offline-sign-future-keys", config_filename), TALER_TESTING_cmd_check_keys_pull_all_keys ("refetch /keys", - 1, - 58 /* FIXME: wrong number... */), + 1), TALER_TESTING_cmd_check_bank_empty ("expect-empty-transactions-on-start"), CMD_EXEC_AGGREGATOR ("run-aggregator-on-empty"), TALER_TESTING_cmd_exec_wirewatch ("run-wirewatch-on-empty", diff --git a/src/testing/testing_api_cmd_check_keys.c b/src/testing/testing_api_cmd_check_keys.c index 20dbfb85b..bdf142ddc 100644 --- a/src/testing/testing_api_cmd_check_keys.c +++ b/src/testing/testing_api_cmd_check_keys.c @@ -47,12 +47,6 @@ struct CheckKeysState unsigned int generation; /** - * How many denomination keys the exchange is - * supposed to have. - */ - unsigned int num_denom_keys; - - /** * If this value is GNUNET_YES, then the "cherry * picking" facility is turned off; whole /keys is * downloaded. @@ -156,19 +150,6 @@ check_keys_run (void *cls, return; } #endif - /* "/keys" was updated, let's check they were OK! */ - if (cks->num_denom_keys != is->keys->num_denom_keys) - { - /* Did not get the expected number of denomination keys! */ - GNUNET_break (0); - TALER_LOG_ERROR ("Got %u keys in step %s, expected %u\n", - is->keys->num_denom_keys, - cmd->label, - cks->num_denom_keys); - TALER_TESTING_interpreter_fail (is); - return; - } - /* Let's unset the fake now before moving on. */ TALER_EXCHANGE_unset_now (is->exchange); TALER_TESTING_interpreter_next (is); @@ -192,36 +173,16 @@ check_keys_cleanup (void *cls, } -/** - * Make a "check keys" command. This type of command - * checks whether the number of denomination keys from - * @a exchange matches @a num_denom_keys. Additionally, - * it lets the user set a last denom issue date to be - * used in the request for /keys. - * - * @param label command label - * @param generation when this command is run, exactly @a - * generation /keys downloads took place. If the number - * of downloads is less than @a generation, the logic will - * first make sure that @a generation downloads are done, - * and _then_ execute the rest of the command. - * @param num_denom_keys expected number of denomination keys. - * @param last_denom_date date to be set in the "last_denom_issue" - * URL parameter of /keys. - * @return the command. - */ struct TALER_TESTING_Command -TALER_TESTING_cmd_check_keys_with_last_denom (const char *label, - unsigned int generation, - unsigned int num_denom_keys, - struct GNUNET_TIME_Absolute - last_denom_date) +TALER_TESTING_cmd_check_keys_with_last_denom ( + const char *label, + unsigned int generation, + struct GNUNET_TIME_Absolute last_denom_date) { struct CheckKeysState *cks; cks = GNUNET_new (struct CheckKeysState); cks->generation = generation; - cks->num_denom_keys = num_denom_keys; cks->set_last_denom = GNUNET_YES; cks->last_denom_date = last_denom_date; { @@ -237,74 +198,14 @@ TALER_TESTING_cmd_check_keys_with_last_denom (const char *label, } -/** - * Make a "check keys" command. This type of command - * checks whether the number of denomination keys from - * @a exchange matches @a num_denom_keys. - * - * @param label command label - * @param generation when this command is run, exactly @a - * generation /keys downloads took place. If the number - * of downloads is less than @a generation, the logic will - * first make sure that @a generation downloads are done, - * and _then_ execute the rest of the command. - * @param num_denom_keys expected number of denomination keys. - * @return the command. - */ struct TALER_TESTING_Command TALER_TESTING_cmd_check_keys (const char *label, - unsigned int generation, - unsigned int num_denom_keys) -{ - struct CheckKeysState *cks; - - cks = GNUNET_new (struct CheckKeysState); - cks->generation = generation; - cks->num_denom_keys = num_denom_keys; - { - struct TALER_TESTING_Command cmd = { - .cls = cks, - .label = label, - .run = &check_keys_run, - .cleanup = &check_keys_cleanup - }; - - return cmd; - } -} - - -/** - * Make a "check keys" command. This type of command - * checks whether the number of denomination keys from - * @a exchange matches @a num_denom_keys. - * - * @param label command label - * @param generation when this command is run, exactly @a - * generation /keys downloads took place. If the number - * of downloads is less than @a generation, the logic will - * first make sure that @a generation downloads are done, - * and _then_ execute the rest of the command. - * @param num_denom_keys expected number of denomination keys. - * @param now timestamp to use when fetching keys - * @return the command. - */ -struct TALER_TESTING_Command -TALER_TESTING_cmd_check_keys_with_now (const char *label, - unsigned int generation, - unsigned int num_denom_keys, - struct GNUNET_TIME_Absolute now) + unsigned int generation) { struct CheckKeysState *cks; cks = GNUNET_new (struct CheckKeysState); cks->generation = generation; - cks->num_denom_keys = num_denom_keys; - cks->now = now; - cks->with_now = GNUNET_YES; - - /* Force to NOT cherry pick, otherwise they conflict. */ - cks->pull_all_keys = GNUNET_YES; { struct TALER_TESTING_Command cmd = { .cls = cks, @@ -318,30 +219,14 @@ TALER_TESTING_cmd_check_keys_with_now (const char *label, } -/** - * Make a "check keys" command that forcedly does NOT cherry pick; - * just redownload the whole /keys. Then checks whether the number - * of denomination keys from @a exchange matches @a num_denom_keys. - * - * @param label command label - * @param generation when this command is run, exactly @a - * generation /keys downloads took place. If the number - * of downloads is less than @a generation, the logic will - * first make sure that @a generation downloads are done, - * and _then_ execute the rest of the command. - * @param num_denom_keys expected number of denomination keys. - * @return the command. - */ struct TALER_TESTING_Command TALER_TESTING_cmd_check_keys_pull_all_keys (const char *label, - unsigned int generation, - unsigned int num_denom_keys) + unsigned int generation) { struct CheckKeysState *cks; cks = GNUNET_new (struct CheckKeysState); cks->generation = generation; - cks->num_denom_keys = num_denom_keys; cks->pull_all_keys = GNUNET_YES; { struct TALER_TESTING_Command cmd = { diff --git a/src/testing/testing_api_cmd_offline_sign_fees.c b/src/testing/testing_api_cmd_offline_sign_fees.c new file mode 100644 index 000000000..500953202 --- /dev/null +++ b/src/testing/testing_api_cmd_offline_sign_fees.c @@ -0,0 +1,192 @@ +/* + This file is part of TALER + Copyright (C) 2020 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 testing/testing_api_cmd_offline_sign_fees.c + * @brief run the taler-exchange-offline command to download, sign and upload wire fees + * @author Marcello Stanisci + * @author Christian Grothoff + */ +#include "platform.h" +#include "taler_json_lib.h" +#include <gnunet/gnunet_curl_lib.h> +#include "taler_signatures.h" +#include "taler_testing_lib.h" + + +/** + * State for a "offlinesign" CMD. + */ +struct OfflineSignState +{ + + /** + * Process for the "offlinesign" command. + */ + struct GNUNET_OS_Process *offlinesign_proc; + + /** + * Configuration file used by the command. + */ + const char *config_filename; + + /** + * The wire fee to sign. + */ + const char *wire_fee_s; + + /** + * The closing fee to sign. + */ + const char *closing_fee_s; + +}; + + +/** + * Run the command; calls the `taler-exchange-offline' program. + * + * @param cls closure. + * @param cmd the commaind being run. + * @param is interpreter state. + */ +static void +offlinesign_run (void *cls, + const struct TALER_TESTING_Command *cmd, + struct TALER_TESTING_Interpreter *is) +{ + struct OfflineSignState *ks = cls; + + ks->offlinesign_proc + = GNUNET_OS_start_process ( + GNUNET_OS_INHERIT_STD_ALL, + NULL, NULL, NULL, + "taler-exchange-offline", + "taler-exchange-offline", + "-c", ks->config_filename, + "-L", "INFO", + "wire-fee", + "now", + "x-taler-bank", + ks->wire_fee_s, + ks->closing_fee_s, + "upload", + NULL); + if (NULL == ks->offlinesign_proc) + { + GNUNET_break (0); + TALER_TESTING_interpreter_fail (is); + return; + } + TALER_TESTING_wait_for_sigchld (is); +} + + +/** + * Free the state of a "offlinesign" CMD, and possibly kills its + * process if it did not terminate correctly. + * + * @param cls closure. + * @param cmd the command being freed. + */ +static void +offlinesign_cleanup (void *cls, + const struct TALER_TESTING_Command *cmd) +{ + struct OfflineSignState *ks = cls; + + (void) cmd; + if (NULL != ks->offlinesign_proc) + { + GNUNET_break (0 == + GNUNET_OS_process_kill (ks->offlinesign_proc, + SIGKILL)); + GNUNET_OS_process_wait (ks->offlinesign_proc); + GNUNET_OS_process_destroy (ks->offlinesign_proc); + ks->offlinesign_proc = NULL; + } + GNUNET_free (ks); +} + + +/** + * Offer "offlinesign" CMD internal data to other commands. + * + * @param cls closure. + * @param[out] ret result + * @param trait name of the trait. + * @param index index number of the object to offer. + * @return #GNUNET_OK on success. + */ +static int +offlinesign_traits (void *cls, + const void **ret, + const char *trait, + unsigned int index) +{ + struct OfflineSignState *ks = cls; + struct TALER_TESTING_Trait traits[] = { + TALER_TESTING_make_trait_process (0, + &ks->offlinesign_proc), + TALER_TESTING_trait_end () + }; + + return TALER_TESTING_get_trait (traits, + ret, + trait, + index); +} + + +/** + * Sign a wire fee. + * + * @param label command label. + * @param config_filename configuration filename. + * @param wire_fee the wire fee to affirm (for the current year) + * @param closing_fee the closing fee to affirm (for the current year) + * @return the command + */ +struct TALER_TESTING_Command +TALER_TESTING_cmd_exec_offline_sign_fees (const char *label, + const char *config_filename, + const char *wire_fee, + const char *closing_fee) +{ + struct OfflineSignState *ks; + + ks = GNUNET_new (struct OfflineSignState); + ks->config_filename = config_filename; + ks->wire_fee_s = wire_fee; + ks->closing_fee_s = closing_fee; + { + struct TALER_TESTING_Command cmd = { + .cls = ks, + .label = label, + .run = &offlinesign_run, + .cleanup = &offlinesign_cleanup, + .traits = &offlinesign_traits + }; + + return cmd; + } +} + + +/* end of testing_api_cmd_exec_offline_sign_fees.c */ diff --git a/src/testing/testing_api_cmd_revoke.c b/src/testing/testing_api_cmd_revoke.c index 8863110bd..f17f351ec 100644 --- a/src/testing/testing_api_cmd_revoke.c +++ b/src/testing/testing_api_cmd_revoke.c @@ -181,8 +181,6 @@ revoke_run (void *cls, } GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Revoke is ongoing..\n"); - - is->reload_keys = GNUNET_OK; TALER_TESTING_wait_for_sigchld (is); } diff --git a/src/testing/testing_api_loop.c b/src/testing/testing_api_loop.c index f269274ec..c89073a18 100644 --- a/src/testing/testing_api_loop.c +++ b/src/testing/testing_api_loop.c @@ -463,24 +463,6 @@ maint_child_death (void *cls) return; } - // FIXME: remove reload_keys, obsolete! - if (GNUNET_OK == is->reload_keys) - { - if (NULL == is->exchanged) - { - GNUNET_break (0); - } - else - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Triggering key state reload at exchange\n"); - GNUNET_break (0 == - GNUNET_OS_process_kill (is->exchanged, - SIGUSR1)); - sleep (5); /* make sure signal was received and processed */ - } - } - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Dead child, go on with next command.\n"); TALER_TESTING_interpreter_next (is); |