From 81d0e570be0db784e98fdb7ad63f9b65c6745be3 Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Thu, 19 Aug 2021 13:41:30 +0200 Subject: -implement DB triggers and check for inbound wire transfers in IBAN plugin --- src/authorization/Makefile.am | 3 +- .../anastasis-helper-authorization-iban.c | 50 +++++++- .../anastasis_authorization_plugin_iban.c | 134 ++++++++++++++++++++- src/authorization/iban.c | 43 +++++++ src/authorization/iban.h | 2 +- 5 files changed, 224 insertions(+), 8 deletions(-) create mode 100644 src/authorization/iban.c (limited to 'src') diff --git a/src/authorization/Makefile.am b/src/authorization/Makefile.am index c2ae0a5..0687129 100644 --- a/src/authorization/Makefile.am +++ b/src/authorization/Makefile.am @@ -15,7 +15,7 @@ pkgdata_DATA = \ EXTRA_DIST = \ $(pkgdata_DATA) \ - iban.h + iban.h iban.c if USE_COVERAGE @@ -32,6 +32,7 @@ anastasis_helper_authorization_iban_LDADD = \ $(top_builddir)/src/stasis/libanastasisdb.la \ $(top_builddir)/src/authorization/libanastasiseufin/libanastasiseufin.la \ $(top_builddir)/src/util/libanastasisutil.la \ + -ltalermhd \ -ltalerutil \ -lgnunetcurl \ -lgnunetutil \ diff --git a/src/authorization/anastasis-helper-authorization-iban.c b/src/authorization/anastasis-helper-authorization-iban.c index 0d3200a..d509c0e 100644 --- a/src/authorization/anastasis-helper-authorization-iban.c +++ b/src/authorization/anastasis-helper-authorization-iban.c @@ -17,10 +17,6 @@ * @file anastasis-helper-authorization-iban.c * @brief Process that watches for wire transfers to Anastasis bank account * @author Christian Grothoff - * - * TODO: - * - needs to add DB triggers to notify main service of inbound activity - * - needs man page */ #include "platform.h" #include "anastasis_eufin_lib.h" @@ -31,6 +27,7 @@ #include #include #include +#include "iban.h" /** * How long to wait for an HTTP reply if there @@ -107,6 +104,37 @@ static int test_mode; static struct GNUNET_SCHEDULER_Task *task; +/** + * Notify anastasis-http that we received @a amount + * from @a sender_account_uri with @a code. + * + * @param sender_account_uri payto:// URI of the sending account + * @param code numeric code used in the wire transfer subject + * @param amount the amount that was wired + */ +static void +notify (const char *sender_account_uri, + uint64_t code, + const struct TALER_Amount *amount) +{ + struct IbanEventP ev = { + .header.type = htons (TALER_DBEVENT_ANASTASIS_AUTH_IBAN_TRANSFER), + .header.size = htons (sizeof (ev)), + .code = GNUNET_htonll (code) + }; + const char *as; + + GNUNET_CRYPTO_hash (sender_account_uri, + strlen (sender_account_uri), + &ev.debit_iban_hash); + as = TALER_amount2s (amount); + db_plugin->event_notify (db_plugin->cls, + &ev.header, + as, + strlen (as)); +} + + /** * We're being aborted with CTRL-C (or SIGTERM). Shut down. * @@ -152,6 +180,9 @@ static void find_transfers (void *cls); +#include "iban.c" + + /** * Callbacks of this type are used to serve the result of asking * the bank for the transaction history. @@ -232,6 +263,17 @@ history_cb (void *cls, break; } latest_row_off = serial_id; + { + uint64_t code; + + if (GNUNET_OK != + extract_code (details->wire_subject, + &code)) + return GNUNET_OK; + notify (details->debit_account_uri, + code, + &details->amount); + } return GNUNET_OK; } diff --git a/src/authorization/anastasis_authorization_plugin_iban.c b/src/authorization/anastasis_authorization_plugin_iban.c index eee8e7e..4f43d3f 100644 --- a/src/authorization/anastasis_authorization_plugin_iban.c +++ b/src/authorization/anastasis_authorization_plugin_iban.c @@ -28,6 +28,16 @@ #include "anastasis_util_lib.h" #include "iban.h" +/** + * How long is a code valid once generated? Very long + * here as we do not want to refuse authentication + * just because the user took a while to execute the + * wire transfer (and then get back to their recovery + * operation). + */ +#define CODE_VALIDITY_PERIOD GNUNET_TIME_UNIT_MONTHS + + /** * Saves the State of a authorization plugin. */ @@ -367,7 +377,116 @@ respond_with_challenge (struct ANASTASIS_AUTHORIZATION_State *as, return ANASTASIS_AUTHORIZATION_RES_SUCCESS_REPLY_FAILED; return ANASTASIS_AUTHORIZATION_RES_SUCCESS; } +} + + +#include "iban.c" + + +/** + * Check if the @a wire_subject matches the challenge in the context + * and if the @a amount is sufficient. If so, return true. + * + * @param cls a `const struct ANASTASIS_AUTHORIZATION_State *` + * @param amount the amount that was transferred + * @param wire_subject a wire subject we received + * @return true if the wire transfer satisfied the check + */ +static bool +check_payment_ok (void *cls, + const struct TALER_Amount *amount, + const char *wire_subject) +{ + const struct ANASTASIS_AUTHORIZATION_State *as = cls; + struct IBAN_Context *ctx = as->ctx; + uint64_t code; + + if (GNUNET_OK != + extract_code (wire_subject, + &code)) + return false; + if (GNUNET_OK != + TALER_amount_cmp_currency (&ctx->expected_amount, + amount)) + { + /* currency wrong!? */ + GNUNET_break (0); + return false; + } + if (1 == + TALER_amount_cmp (&ctx->expected_amount, + amount)) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Amount `%s' insufficient for authorization\n", + TALER_amount2s (amount)); + return false; + } + return (code == as->code); +} + +/** + * Check if we have received a wire transfer with a subject + * authorizing the disclosure of the credential in the meantime. + * + * @param as state to check for + * @return WTS_SUCCESS if a transfer was received, + * WTS_NOT_READY if no transfer was received, + * WTS_FAILED_WITH_REPLY if we had an internal error and queued a reply + * WTS_FAILED_WITHOUT_REPLY if we had an internal error and failed to queue a reply + */ +static enum +{ + WTS_SUCCESS, + WTS_NOT_READY, + WTS_FAILED_WITH_REPLY, + WTS_FAILED_WITHOUT_REPLY +} +test_wire_transfers (struct ANASTASIS_AUTHORIZATION_State *as) +{ + struct IBAN_Context *ctx = as->ctx; + struct ANASTASIS_DatabasePlugin *db = ctx->ac->db; + enum GNUNET_DB_QueryStatus qs; + struct GNUNET_TIME_Absolute now; + struct GNUNET_TIME_Absolute limit; + char *debit_account_uri; + + now = GNUNET_TIME_absolute_get (); + limit = GNUNET_TIME_absolute_subtract (now, + CODE_VALIDITY_PERIOD); + GNUNET_asprintf (&debit_account_uri, + "payto://iban/%s", + as->iban_number); + qs = db->test_auth_iban_payment ( + db->cls, + debit_account_uri, + limit, + &check_payment_ok, + as); + GNUNET_free (debit_account_uri); + switch (qs) + { + case GNUNET_DB_STATUS_HARD_ERROR: + case GNUNET_DB_STATUS_SOFT_ERROR: + return (MHD_YES == + TALER_MHD_reply_with_error (as->connection, + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_EC_GENERIC_DB_FETCH_FAILED, + NULL)) + ? WTS_FAILED_WITH_REPLY + : WTS_FAILED_WITHOUT_REPLY; + case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: + return WTS_NOT_READY; + case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: + break; + } + qs = db->mark_challenge_code_satisfied ( + db->cls, + &as->truth_uuid, + as->code); + GNUNET_break (qs > 0); + return WTS_SUCCESS; } @@ -423,6 +542,17 @@ iban_process (struct ANASTASIS_AUTHORIZATION_State *as, return ANASTASIS_AUTHORIZATION_RES_FAILED_REPLY_FAILED; return ANASTASIS_AUTHORIZATION_RES_FAILED; case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: + switch (test_wire_transfers (as)) + { + case WTS_SUCCESS: + return ANASTASIS_AUTHORIZATION_RES_FINISHED; + case WTS_NOT_READY: + break; /* continue below */ + case WTS_FAILED_WITH_REPLY: + return ANASTASIS_AUTHORIZATION_RES_FAILED; + case WTS_FAILED_WITHOUT_REPLY: + return ANASTASIS_AUTHORIZATION_RES_FAILED_REPLY_FAILED; + } if (GNUNET_TIME_absolute_is_future (timeout)) { as->connection = connection; @@ -529,9 +659,9 @@ libanastasis_plugin_authorization_iban_init (void *cls) ctx->ac = ac; plugin = GNUNET_new (struct ANASTASIS_AuthorizationPlugin); plugin->payment_plugin_managed = true; - plugin->code_validity_period = GNUNET_TIME_UNIT_MONTHS; + plugin->code_validity_period = CODE_VALIDITY_PERIOD; plugin->code_rotation_period = GNUNET_TIME_UNIT_WEEKS; - plugin->code_retransmission_frequency = GNUNET_TIME_UNIT_FOREVER_REL; + plugin->code_retransmission_frequency = GNUNET_TIME_UNIT_FOREVER_REL; /* not applicable */ plugin->cls = ctx; plugin->validate = &iban_validate; plugin->start = &iban_start; diff --git a/src/authorization/iban.c b/src/authorization/iban.c new file mode 100644 index 0000000..9547790 --- /dev/null +++ b/src/authorization/iban.c @@ -0,0 +1,43 @@ +/** + * Extract a numeric @a code from a @a wire_subject. + * Also checks that the @a wire_subject contains the + * string "anastasis". + * + * @param wire_subject wire subject to extract @a code from + * @param[out] code where to write the extracted code + * @return #GNUNET_OK if a @a code was extracted + */ +static enum GNUNET_GenericReturnValue +extract_code (const char *wire_subject, + uint64_t *code) +{ + unsigned long long c; + const char *pos; + + if (0 != + strcasestr (wire_subject, + "anastasis")) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Keyword 'anastasis' missing in subject `%s', ignoring transfer\n", + wire_subject); + return GNUNET_SYSERR; + } + pos = wire_subject; + while ( ('\0' != *pos) && + (! isdigit ((int) *pos)) ) + pos++; + if (1 != + sscanf (pos, + "%llu", + &c)) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Did not find any code number in subject `%s', ignoring transfer\n", + wire_subject); + return GNUNET_SYSERR; + } + + *code = (uint64_t) c; + return GNUNET_OK; +} diff --git a/src/authorization/iban.h b/src/authorization/iban.h index c95c00d..70db7ea 100644 --- a/src/authorization/iban.h +++ b/src/authorization/iban.h @@ -32,7 +32,7 @@ GNUNET_NETWORK_STRUCT_BEGIN struct IbanEventP { /** - * Header of type #TALER_DBEVENT_TYPE_ANASTASIS_AUTH_IBAN_TRANSFER. + * Header of type #TALER_DBEVENT_ANASTASIS_AUTH_IBAN_TRANSFER. */ struct GNUNET_DB_EventHeaderP header; -- cgit v1.2.3