exchange

Base system with REST service to issue digital coins, run by the payment service provider
Log | Files | Refs | Submodules | README | LICENSE

commit 5119a75cbc5473498a17855745a991df95e746d5
parent 8f0afa4b2734cb5d8bbe3bcbdc0c0999ebf7d066
Author: Christian Grothoff <christian@grothoff.org>
Date:   Mon,  2 Sep 2024 15:46:22 +0200

-fix warnings

Diffstat:
Msrc/auditordb/test_auditordb.c | 21+++++++++------------
Msrc/exchange/taler-exchange-aggregator.c | 12+++++++++++-
Msrc/exchange/taler-exchange-httpd_age-withdraw.c | 5+++--
Msrc/exchange/taler-exchange-httpd_batch-deposit.c | 20+++++++++++++-------
Msrc/exchange/taler-exchange-httpd_batch-withdraw.c | 5+++--
Msrc/exchange/taler-exchange-httpd_common_kyc.c | 223++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----
Msrc/exchange/taler-exchange-httpd_common_kyc.h | 38++++++++++++++++++++++++++++++++++++++
Msrc/exchange/taler-exchange-httpd_kyc-wallet.c | 3++-
Msrc/exchange/taler-exchange-httpd_purses_merge.c | 8+++++---
Msrc/exchange/taler-exchange-httpd_reserves_close.c | 5+++--
Msrc/exchange/taler-exchange-httpd_reserves_purse.c | 8+++++---
Msrc/exchange/taler-exchange-httpd_responses.c | 20+++++++++++++++++---
Msrc/exchange/taler-exchange-httpd_responses.h | 12+++++++++---
Msrc/exchangedb/exchange_do_trigger_kyc_rule_for_account.sql | 31++++++++++++++++++++++++++-----
Msrc/exchangedb/pg_get_kyc_rules.c | 13+++++++++++--
Msrc/exchangedb/pg_get_kyc_rules.h | 3+++
Msrc/exchangedb/pg_trigger_kyc_rule_for_account.c | 20+++++++++++++++-----
Msrc/exchangedb/pg_trigger_kyc_rule_for_account.h | 13++++++++++---
Msrc/include/taler_exchange_service.h | 7++++++-
Msrc/include/taler_exchangedb_plugin.h | 29++++++++++++++++++++++++++---
Msrc/json/test_json.c | 4+++-
Msrc/lib/exchange_api_batch_deposit.c | 3+++
22 files changed, 431 insertions(+), 72 deletions(-)

diff --git a/src/auditordb/test_auditordb.c b/src/auditordb/test_auditordb.c @@ -185,6 +185,15 @@ run (void *cls) { struct GNUNET_CONFIGURATION_Handle *cfg = cls; uint64_t rowid; + struct TALER_Amount value; + struct TALER_Amount fee_withdraw; + struct TALER_Amount fee_deposit; + struct TALER_Amount fee_refresh; + struct TALER_Amount fee_refund; + struct TALER_ReservePublicKeyP reserve_pub; + struct TALER_DenominationPrivateKey denom_priv; + struct TALER_DenominationPublicKey denom_pub; + struct GNUNET_TIME_Timestamp date; GNUNET_log (GNUNET_ERROR_TYPE_INFO, "loading database plugin\n"); @@ -219,12 +228,6 @@ run (void *cls) GNUNET_log (GNUNET_ERROR_TYPE_INFO, "initializing\n"); - struct TALER_Amount value; - struct TALER_Amount fee_withdraw; - struct TALER_Amount fee_deposit; - struct TALER_Amount fee_refresh; - struct TALER_Amount fee_refund; - GNUNET_assert (GNUNET_OK == TALER_string_to_amount (CURRENCY ":1.000010", &value)); @@ -240,12 +243,6 @@ run (void *cls) GNUNET_assert (GNUNET_OK == TALER_string_to_amount (CURRENCY ":0.000014", &fee_refund)); - - struct TALER_ReservePublicKeyP reserve_pub; - struct TALER_DenominationPrivateKey denom_priv; - struct TALER_DenominationPublicKey denom_pub; - struct GNUNET_TIME_Timestamp date; - RND_BLK (&reserve_pub); RND_BLK (&rnd_hash); GNUNET_assert (GNUNET_OK == diff --git a/src/exchange/taler-exchange-aggregator.c b/src/exchange/taler-exchange-aggregator.c @@ -111,6 +111,12 @@ struct AggregationUnit */ bool have_transient; + /** + * Is the wrong merchant public key associated with + * the KYC data? + */ + bool bad_kyc_auth; + }; @@ -515,9 +521,11 @@ legitimization_satisfied (struct AggregationUnit *au_active) { json_t *jrules; + union TALER_AccountPublicKeyP account_pub; qs = db_plugin->get_kyc_rules (db_plugin->cls, &au_active->h_payto, + &account_pub, &jrules); if (qs < 0) { @@ -564,9 +572,11 @@ legitimization_satisfied (struct AggregationUnit *au_active) au_active->payto_uri, &au_active->h_payto, NULL, + &au_active->merchant_pub, jrule, TALER_KYCLOGIC_rule2priority (requirement), - &au_active->requirement_row); + &au_active->requirement_row, + &au_active->bad_kyc_auth); json_decref (jrule); if (qs < 0) { diff --git a/src/exchange/taler-exchange-httpd_age-withdraw.c b/src/exchange/taler-exchange-httpd_age-withdraw.c @@ -499,7 +499,8 @@ check_kyc_result (struct AgeWithdrawContext *awc) TEH_RESPONSE_reply_kyc_required ( awc->rc->connection, &awc->h_payto, - &awc->kyc)); + &awc->kyc, + false)); return; } awc->phase++; @@ -630,7 +631,7 @@ run_legi_check (struct AgeWithdrawContext *awc) TALER_KYCLOGIC_KYC_TRIGGER_WITHDRAW, payto_uri, &awc->h_payto, - NULL, + NULL, /* no account pub: this is about the origin account */ &withdraw_amount_cb, awc, &withdraw_legi_cb, diff --git a/src/exchange/taler-exchange-httpd_batch-deposit.c b/src/exchange/taler-exchange-httpd_batch-deposit.c @@ -160,6 +160,12 @@ struct BatchDepositContext * @e policy_json is NULL and @e h_policy will be all zero. */ bool has_no_policy; + + /** + * KYC failed because a KYC auth transfer is needed + * to establish the merchant_pub. + */ + bool bad_kyc_auth; }; @@ -488,7 +494,8 @@ static void bdc_phase_check_kyc_result (struct BatchDepositContext *bdc) { /* return final positive response */ - if (! bdc->kyc.ok) + if ( (! bdc->kyc.ok) || + (bdc->bad_kyc_auth) ) { if (check_request_idempotent (bdc)) { @@ -501,7 +508,8 @@ bdc_phase_check_kyc_result (struct BatchDepositContext *bdc) TEH_RESPONSE_reply_kyc_required ( bdc->rc->connection, &bdc->bd.wire_target_h_payto, - &bdc->kyc)); + &bdc->kyc, + bdc->bad_kyc_auth)); return; } bdc->phase = BDC_PHASE_TRANSACT; @@ -538,6 +546,7 @@ deposit_legi_cb ( return; } bdc->kyc = lcr->kyc; + bdc->bad_kyc_auth = lcr->bad_kyc_auth; bdc->phase = BDC_PHASE_CHECK_KYC_RESULT; } @@ -604,15 +613,12 @@ bdc_phase_kyc (struct BatchDepositContext *bdc) bdc->phase++; return; } - /* FIXME: this fails to check that the - merchant_pub used in this request - matches the registered public key */ - bdc->lch = TEH_legitimization_check ( + bdc->lch = TEH_legitimization_check2 ( &bdc->rc->async_scope_id, TALER_KYCLOGIC_KYC_TRIGGER_DEPOSIT, bdc->bd.receiver_wire_account, &bdc->bd.wire_target_h_payto, - NULL, /* account_pub */ + &bdc->bd.merchant_pub, &deposit_amount_cb, bdc, &deposit_legi_cb, diff --git a/src/exchange/taler-exchange-httpd_batch-withdraw.c b/src/exchange/taler-exchange-httpd_batch-withdraw.c @@ -571,7 +571,8 @@ check_kyc_result (struct BatchWithdrawContext *bwc) TEH_RESPONSE_reply_kyc_required ( bwc->rc->connection, &bwc->h_payto, - &bwc->kyc)); + &bwc->kyc, + false)); return; } bwc->phase++; @@ -702,7 +703,7 @@ run_legi_check (struct BatchWithdrawContext *bwc) TALER_KYCLOGIC_KYC_TRIGGER_WITHDRAW, payto_uri, &bwc->h_payto, - NULL, + NULL, /* no account pub: this is about the origin account */ &withdraw_amount_cb, bwc, &withdraw_legi_cb, diff --git a/src/exchange/taler-exchange-httpd_common_kyc.c b/src/exchange/taler-exchange-httpd_common_kyc.c @@ -922,16 +922,19 @@ TEH_kyc_fallback ( { json_t *jmeasures; enum GNUNET_DB_QueryStatus qs; + bool bad_kyc_auth; jmeasures = TALER_KYCLOGIC_check_to_measures (&kcc); qs = TEH_plugin->trigger_kyc_rule_for_account ( TEH_plugin->cls, NULL, /* account_id is already in wire targets */ account_id, - NULL, + NULL, /* account_pub */ + NULL, /* merchant_pub */ jmeasures, 65536, /* high priority (does it matter?) */ - &fb->requirement_row); + &fb->requirement_row, + &bad_kyc_auth); json_decref (jmeasures); fb->failure = (qs <= 0); fb->task = GNUNET_SCHEDULER_add_now (&return_fallback_result, @@ -1043,11 +1046,20 @@ struct TEH_LegitimizationCheckHandle struct TALER_PaytoHashP h_payto; /** - * Public key of the account. + * Public key of the account. Associates this public + * key with the account if @e have_account_pub is true. */ union TALER_AccountPublicKeyP account_pub; /** + * Public key of the merchant. Checks that the KYC + * data was actually provided for this merchant if + * @e have_merchant_pub is true, and if not rejects + * the operation. + */ + struct TALER_MerchantPublicKeyP merchant_pub; + + /** * Our request scope for logging. */ struct GNUNET_AsyncScopeId scope; @@ -1073,6 +1085,18 @@ struct TEH_LegitimizationCheckHandle * Do we have @e account_pub? */ bool have_account_pub; + + /** + * Do we have @e merchant_pub? + */ + bool have_merchant_pub; + + /** + * True if @a have_merchant_pub is true but the given + * merchant pub did not match the target_pub for the + * given @a h_payto. + */ + bool bad_kyc_auth; }; @@ -1176,8 +1200,23 @@ legi_check_aml_trigger_cb ( } -struct TEH_LegitimizationCheckHandle * -TEH_legitimization_check ( +/** + * Setup legitimization check. + * + * @param scope scope for logging + * @param et type of event we are checking + * @param payto_uri account we are checking for + * @param h_payto hash of @a payto_uri + * @param account_pub public key to enable for the + * KYC authorization, NULL if not known + * @param ai callback to get amounts involved historically + * @param ai_cls closure for @a ai + * @param result_cb function to call with the result + * @param result_cb_cls closure for @a result_cb + * @return handle for the operation + */ +static struct TEH_LegitimizationCheckHandle * +setup_legitimization_check ( const struct GNUNET_AsyncScopeId *scope, enum TALER_KYCLOGIC_KycTriggerEvent et, const char *payto_uri, @@ -1204,6 +1243,63 @@ TEH_legitimization_check ( lch->ai_cls = ai_cls; lch->result_cb = result_cb; lch->result_cb_cls = result_cb_cls; + return lch; +} + + +struct TEH_LegitimizationCheckHandle * +TEH_legitimization_check ( + const struct GNUNET_AsyncScopeId *scope, + enum TALER_KYCLOGIC_KycTriggerEvent et, + const char *payto_uri, + const struct TALER_PaytoHashP *h_payto, + const union TALER_AccountPublicKeyP *account_pub, + TALER_KYCLOGIC_KycAmountIterator ai, + void *ai_cls, + TEH_LegitimizationCheckCallback result_cb, + void *result_cb_cls) +{ + struct TEH_LegitimizationCheckHandle *lch; + + lch = setup_legitimization_check (scope, + et, + payto_uri, + h_payto, + account_pub, + ai, + ai_cls, + result_cb, + result_cb_cls); + legitimization_check_run (lch); + return lch; +} + + +struct TEH_LegitimizationCheckHandle * +TEH_legitimization_check2 ( + const struct GNUNET_AsyncScopeId *scope, + enum TALER_KYCLOGIC_KycTriggerEvent et, + const char *payto_uri, + const struct TALER_PaytoHashP *h_payto, + const struct TALER_MerchantPublicKeyP *merchant_pub, + TALER_KYCLOGIC_KycAmountIterator ai, + void *ai_cls, + TEH_LegitimizationCheckCallback result_cb, + void *result_cb_cls) +{ + struct TEH_LegitimizationCheckHandle *lch; + + lch = setup_legitimization_check (scope, + et, + payto_uri, + h_payto, + NULL, + ai, + ai_cls, + result_cb, + result_cb_cls); + lch->merchant_pub = *merchant_pub; + lch->have_merchant_pub = true; legitimization_check_run (lch); return lch; } @@ -1396,9 +1492,11 @@ run_check ( lch->payto_uri, &lch->h_payto, lch->have_account_pub ? &lch->account_pub : NULL, + lch->have_merchant_pub ? &lch->merchant_pub : NULL, jmeasures, 0, /* no particular priority */ - &lch->lcr.kyc.requirement_row); + &lch->lcr.kyc.requirement_row, + &lch->lcr.bad_kyc_auth); switch (qs) { case GNUNET_DB_STATUS_HARD_ERROR: @@ -1428,6 +1526,74 @@ cleanup: } +/** + * The KYC check failed because KYC auth is required + * to match and it does not. + * + * @param[in,out] lch legitimization check to fail + */ +static void +fail_kyc_auth (struct TEH_LegitimizationCheckHandle *lch) +{ + lch->lcr.kyc.requirement_row = 0; + lch->lcr.kyc.ok = false; + lch->lcr.bad_kyc_auth = true; + lch->lcr.expiration_date + = GNUNET_TIME_UNIT_FOREVER_TS; + memset (&lch->lcr.next_threshold, + 0, + sizeof (struct TALER_Amount)); + lch->lcr.http_status = 0; + lch->lcr.response = NULL; + lch->async_task + = GNUNET_SCHEDULER_add_now ( + &async_return_legi_result, + lch); +} + + +/** + * Function called to iterate over KYC-relevant + * transaction amounts for a particular time range. + * Called within a database transaction, so must + * not start a new one. + * + * Given that there *is* a KYC requirement, we also + * check if the kyc_auth_bad is set and react + * accordingly. + * + * @param cls closure, a `struct TEH_LegitimizationCheckHandle *` + * @param limit maximum time-range for which events + * should be fetched (timestamp in the past) + * @param cb function to call on each event found, + * events must be returned in reverse chronological + * order + * @param cb_cls closure for @a cb + * @return transaction status + */ +static enum GNUNET_DB_QueryStatus +amount_iterator_wrapper_cb ( + void *cls, + struct GNUNET_TIME_Absolute limit, + TALER_EXCHANGEDB_KycAmountCallback cb, + void *cb_cls) +{ + struct TEH_LegitimizationCheckHandle *lch = cls; + + if (lch->bad_kyc_auth) + { + /* We *do* have applicable KYC rules *and* the + target_pub does not match the merchant_pub, + so we indeed have a problem! */ + lch->lcr.bad_kyc_auth = true; + } + return lch->ai (lch->ai_cls, + limit, + cb, + cb_cls); +} + + static void legitimization_check_run ( struct TEH_LegitimizationCheckHandle *lch) @@ -1443,6 +1609,7 @@ legitimization_check_run ( /* AML/KYC disabled, just immediately return success! */ lch->lcr.kyc.requirement_row = 0; lch->lcr.kyc.ok = true; + lch->lcr.bad_kyc_auth = false; lch->lcr.expiration_date = GNUNET_TIME_UNIT_FOREVER_TS; memset (&lch->lcr.next_threshold, @@ -1461,24 +1628,47 @@ legitimization_check_run ( { json_t *jrules; + qs = TEH_plugin->get_kyc_rules (TEH_plugin->cls, &lch->h_payto, + &lch->lcr.kyc.account_pub, &jrules); - if (qs < 0) + switch (qs) { + case GNUNET_DB_STATUS_HARD_ERROR: + case GNUNET_DB_STATUS_SOFT_ERROR: GNUNET_break (0); legi_fail (lch, TALER_EC_GENERIC_DB_FETCH_FAILED, "get_kyc_rules"); GNUNET_async_scope_restore (&old_scope); return; - } - if (qs > 0) - { + case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: + if (lch->have_merchant_pub) + { + // FIXME: not quite correct: the absence of custom *jrules* does NOT + // imply that we had no target_pub! + lch->lcr.bad_kyc_auth = true; + } + break; + case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: + lch->lcr.kyc.have_account_pub + = ! GNUNET_is_zero (&lch->lcr.kyc.account_pub); + if ( (lch->have_merchant_pub) && + (0 != GNUNET_memcmp (&lch->merchant_pub, + &lch->lcr.kyc.account_pub.merchant_pub)) ) + { + /* We have custom rules, but the target_pub for + those custom rules does not match the + merchant_pub. Fail the KYC process! */ + fail_kyc_auth (lch); + return; + } lrs = TALER_KYCLOGIC_rules_parse (jrules); GNUNET_break (NULL != lrs); /* Fall back to default rules on parse error! */ json_decref (jrules); + break; } } @@ -1517,8 +1707,8 @@ legitimization_check_run ( qs = TALER_KYCLOGIC_kyc_test_required ( lch->et, lrs, - lch->ai, - lch->ai_cls, + &amount_iterator_wrapper_cb, + lch, &requirement, &lch->lcr.next_threshold); if (qs < 0) @@ -1530,6 +1720,11 @@ legitimization_check_run ( GNUNET_async_scope_restore (&old_scope); return; } + if (lch->lcr.bad_kyc_auth) + { + fail_kyc_auth (lch); + return; + } if (NULL == requirement) { @@ -1606,9 +1801,11 @@ legitimization_check_run ( lch->payto_uri, &lch->h_payto, lch->have_account_pub ? &lch->account_pub : NULL, + lch->have_merchant_pub ? &lch->merchant_pub : NULL, jmeasures, TALER_KYCLOGIC_rule2priority (requirement), - &lch->lcr.kyc.requirement_row); + &lch->lcr.kyc.requirement_row, + &lch->lcr.bad_kyc_auth); json_decref (jmeasures); } if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs) diff --git a/src/exchange/taler-exchange-httpd_common_kyc.h b/src/exchange/taler-exchange-httpd_common_kyc.h @@ -212,6 +212,14 @@ struct TEH_LegitimizationCheckResult unsigned int http_status; /** + * Set to true if the merchant public key does not + * match the public key we have on file for this + * target account (and thus a new KYC AUTH is + * required). + */ + bool bad_kyc_auth; + + /** * Response to return. Note that the response must * be queued or destroyed by the callee. NULL * if the legitimization check was successful and the handler should return @@ -268,6 +276,36 @@ TEH_legitimization_check ( /** + * Do legitimization check and enforce that the current + * public key associated with the account is the given + * merchant public key. + * + * @param scope scope for logging + * @param et type of event we are checking + * @param payto_uri account we are checking for + * @param h_payto hash of @a payto_uri + * @param merchant_pub public key that must match the + * KYC authorization + * @param ai callback to get amounts involved historically + * @param ai_cls closure for @a ai + * @param result_cb function to call with the result + * @param result_cb_cls closure for @a result_cb + * @return handle for the operation + */ +struct TEH_LegitimizationCheckHandle * +TEH_legitimization_check2 ( + const struct GNUNET_AsyncScopeId *scope, + enum TALER_KYCLOGIC_KycTriggerEvent et, + const char *payto_uri, + const struct TALER_PaytoHashP *h_payto, + const struct TALER_MerchantPublicKeyP *merchant_pub, + TALER_KYCLOGIC_KycAmountIterator ai, + void *ai_cls, + TEH_LegitimizationCheckCallback result_cb, + void *result_cb_cls); + + +/** * Cancel legitimization check. * * @param[in] lch handle of the check to cancel diff --git a/src/exchange/taler-exchange-httpd_kyc-wallet.c b/src/exchange/taler-exchange-httpd_kyc-wallet.c @@ -321,7 +321,8 @@ TEH_handler_kyc_wallet ( } return TEH_RESPONSE_reply_kyc_required (rc->connection, &krc->h_payto, - &krc->kyc); + &krc->kyc, + false); } diff --git a/src/exchange/taler-exchange-httpd_purses_merge.c b/src/exchange/taler-exchange-httpd_purses_merge.c @@ -763,9 +763,11 @@ TEH_handler_purses_merge ( pmc->response); } if (! pmc->kyc.ok) - return TEH_RESPONSE_reply_kyc_required (rc->connection, - &pmc->h_payto, - &pmc->kyc); + return TEH_RESPONSE_reply_kyc_required ( + rc->connection, + &pmc->h_payto, + &pmc->kyc, + false); /* execute merge transaction */ { diff --git a/src/exchange/taler-exchange-httpd_reserves_close.c b/src/exchange/taler-exchange-httpd_reserves_close.c @@ -343,7 +343,7 @@ reserve_close_transaction ( TALER_KYCLOGIC_KYC_TRIGGER_RESERVE_CLOSE, rcc->payto_uri, &rcc->kyc_payto, - NULL, /* no account_pub */ + NULL, /* no account_pub: this is about the origin/destination account */ &amount_it, rcc, &reserve_close_legi_cb, @@ -548,7 +548,8 @@ TEH_handler_reserves_close ( return TEH_RESPONSE_reply_kyc_required ( rc->connection, &rcc->kyc_payto, - &rcc->kyc); + &rcc->kyc, + false); } if (GNUNET_OK != diff --git a/src/exchange/taler-exchange-httpd_reserves_purse.c b/src/exchange/taler-exchange-httpd_reserves_purse.c @@ -850,9 +850,11 @@ TEH_handler_reserves_purse ( TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE, "requirement row not set"); } - return TEH_RESPONSE_reply_kyc_required (connection, - &rpc->h_payto, - &rpc->kyc); + return TEH_RESPONSE_reply_kyc_required ( + connection, + &rpc->h_payto, + &rpc->kyc, + false); } { diff --git a/src/exchange/taler-exchange-httpd_responses.c b/src/exchange/taler-exchange-httpd_responses.c @@ -338,9 +338,11 @@ TEH_RESPONSE_reply_purse_created ( MHD_RESULT -TEH_RESPONSE_reply_kyc_required (struct MHD_Connection *connection, - const struct TALER_PaytoHashP *h_payto, - const struct TALER_EXCHANGEDB_KycStatus *kyc) +TEH_RESPONSE_reply_kyc_required ( + struct MHD_Connection *connection, + const struct TALER_PaytoHashP *h_payto, + const struct TALER_EXCHANGEDB_KycStatus *kyc, + bool bad_kyc_auth) { return TALER_MHD_REPLY_JSON_PACK ( connection, @@ -348,6 +350,18 @@ TEH_RESPONSE_reply_kyc_required (struct MHD_Connection *connection, TALER_JSON_pack_ec (TALER_EC_EXCHANGE_GENERIC_KYC_REQUIRED), GNUNET_JSON_pack_data_auto ("h_payto", h_payto), + GNUNET_JSON_pack_allow_null ( + GNUNET_JSON_pack_data_auto ( + "account_pub", + (kyc->have_account_pub + ? &kyc->account_pub + : NULL))), + GNUNET_JSON_pack_allow_null ( + bad_kyc_auth + ? GNUNET_JSON_pack_bool ("bad_kyc_auth", + bad_kyc_auth) + : GNUNET_JSON_pack_string ("bad_kyc_auth", + NULL)), GNUNET_JSON_pack_uint64 ("requirement_row", kyc->requirement_row)); } diff --git a/src/exchange/taler-exchange-httpd_responses.h b/src/exchange/taler-exchange-httpd_responses.h @@ -89,12 +89,18 @@ TEH_RESPONSE_reply_reserve_age_restriction_required ( * @param connection connection to the client * @param h_payto account identifier to include in reply * @param kyc details about the KYC requirements + * @param bad_kyc_auth true if the target_pub of the + * @a h_payto account does not match the merchant_pub + * from the operation and thus a KYC AUTH transfer is + * required * @return MHD result code */ MHD_RESULT -TEH_RESPONSE_reply_kyc_required (struct MHD_Connection *connection, - const struct TALER_PaytoHashP *h_payto, - const struct TALER_EXCHANGEDB_KycStatus *kyc); +TEH_RESPONSE_reply_kyc_required ( + struct MHD_Connection *connection, + const struct TALER_PaytoHashP *h_payto, + const struct TALER_EXCHANGEDB_KycStatus *kyc, + bool bad_kyc_auth); /** diff --git a/src/exchangedb/exchange_do_trigger_kyc_rule_for_account.sql b/src/exchangedb/exchange_do_trigger_kyc_rule_for_account.sql @@ -14,30 +14,50 @@ -- TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> -- -CREATE OR REPLACE FUNCTION exchange_do_trigger_kyc_rule_for_account( +DROP FUNCTION IF EXISTS exchange_do_trigger_kyc_rule_for_account; + +CREATE FUNCTION exchange_do_trigger_kyc_rule_for_account( IN in_h_payto BYTEA, IN in_account_pub BYTEA, -- can be NULL + IN in_merchant_pub BYTEA, -- can be NULL IN in_payto_uri TEXT, -- can be NULL IN in_now INT8, IN in_jmeasures TEXT, IN in_display_priority INT4, - OUT out_legitimization_measure_serial_id INT8) + OUT out_legitimization_measure_serial_id INT8, + OUT out_bad_kyc_auth BOOL) LANGUAGE plpgsql AS $$ DECLARE + my_rec RECORD; my_access_token BYTEA; + my_account_pub BYTEA; BEGIN -- Note: in_payto_uri is allowed to be NULL *if* -- in_h_payto is already in wire_targets + SELECT - access_token + access_token + ,account_pub INTO - my_access_token + my_rec FROM wire_targets WHERE wire_target_h_payto=in_h_payto; -IF NOT FOUND +IF FOUND THEN + -- Extract details, determine if KYC auth matches. + my_access_token = my_rec.access_token; + my_account_pub = my_rec.account_pub; + IF in_merchant_pub IS NULL + THEN + out_bad_kyc_auth = FALSE; + ELSE + out_bad_kyc_auth = (my_account_pub = in_merchant_pub); + END IF; +ELSE + -- No constraint on merchant_pub, just create + -- the wire_target. INSERT INTO wire_targets (payto_uri ,wire_target_h_payto @@ -49,6 +69,7 @@ THEN RETURNING access_token INTO my_access_token; + out_bad_kyc_auth=TRUE; END IF; -- First check if a perfectly equivalent legi measure diff --git a/src/exchangedb/pg_get_kyc_rules.c b/src/exchangedb/pg_get_kyc_rules.c @@ -30,6 +30,7 @@ enum GNUNET_DB_QueryStatus TEH_PG_get_kyc_rules ( void *cls, const struct TALER_PaytoHashP *h_payto, + union TALER_AccountPublicKeyP *account_pub, json_t **jrules) { struct PostgresClosure *pg = cls; @@ -41,16 +42,24 @@ TEH_PG_get_kyc_rules ( GNUNET_PQ_query_param_end }; struct GNUNET_PQ_ResultSpec rs[] = { + GNUNET_PQ_result_spec_auto_from_type ("target_pub", + account_pub), TALER_PQ_result_spec_json ("jnew_rules", jrules), GNUNET_PQ_result_spec_end }; + memset (account_pub, + 0, + sizeof (*account_pub)); PREPARE (pg, "get_kyc_rules", "SELECT" - " jnew_rules" - " FROM legitimization_outcomes" + " wt.target_pub" + " ,lo.jnew_rules" + " FROM legitimization_outcomes lo" + " JOIN wire_targets wt" + " ON (lo.h_payto = wt.wire_target_h_payto)" " WHERE h_payto=$1" " AND expiration_time >= $2" " AND is_active;"); diff --git a/src/exchangedb/pg_get_kyc_rules.h b/src/exchangedb/pg_get_kyc_rules.h @@ -31,6 +31,8 @@ * * @param cls the @e cls of this struct with the plugin-specific state * @param h_payto account identifier + * @param[out] account_pub account public key the rules + * apply to (because this key was used in KYC auth) * @param[out] jrules set to the active KYC rules for the * given account, set to NULL if no custom rules are active * @return transaction status code @@ -39,6 +41,7 @@ enum GNUNET_DB_QueryStatus TEH_PG_get_kyc_rules ( void *cls, const struct TALER_PaytoHashP *h_payto, + union TALER_AccountPublicKeyP *account_pub, json_t **jrules); #endif diff --git a/src/exchangedb/pg_trigger_kyc_rule_for_account.c b/src/exchangedb/pg_trigger_kyc_rule_for_account.c @@ -31,19 +31,24 @@ TEH_PG_trigger_kyc_rule_for_account ( void *cls, const char *payto_uri, const struct TALER_PaytoHashP *h_payto, - const union TALER_AccountPublicKeyP *account_pub, + const union TALER_AccountPublicKeyP *set_account_pub, + const struct TALER_MerchantPublicKeyP *check_merchant_pub, const json_t *jmeasures, uint32_t display_priority, - uint64_t *requirement_row) + uint64_t *requirement_row, + bool *bad_kyc_auth) { struct PostgresClosure *pg = cls; struct GNUNET_TIME_Absolute now = GNUNET_TIME_absolute_get (); struct GNUNET_PQ_QueryParam params[] = { GNUNET_PQ_query_param_auto_from_type (h_payto), - NULL == account_pub + NULL == set_account_pub ? GNUNET_PQ_query_param_null () - : GNUNET_PQ_query_param_auto_from_type (account_pub), + : GNUNET_PQ_query_param_auto_from_type (set_account_pub), + NULL == check_merchant_pub + ? GNUNET_PQ_query_param_null () + : GNUNET_PQ_query_param_auto_from_type (check_merchant_pub), NULL == payto_uri ? GNUNET_PQ_query_param_null () : GNUNET_PQ_query_param_string (payto_uri), @@ -56,6 +61,9 @@ TEH_PG_trigger_kyc_rule_for_account ( GNUNET_PQ_result_spec_uint64 ( "legitimization_measure_serial_id", requirement_row), + GNUNET_PQ_result_spec_bool ( + "bad_kyc_auth", + bad_kyc_auth), GNUNET_PQ_result_spec_end }; @@ -64,8 +72,10 @@ TEH_PG_trigger_kyc_rule_for_account ( "SELECT" " out_legitimization_measure_serial_id" " AS legitimization_measure_serial_id" + " ,out_bad_kyc_auth" + " AS bad_kyc_auth" " FROM exchange_do_trigger_kyc_rule_for_account" - "($1, $2, $3, $4, $5, $6);"); + "($1, $2, $3, $4, $5, $6, $7);"); return GNUNET_PQ_eval_prepared_singleton_select ( pg->conn, diff --git a/src/exchangedb/pg_trigger_kyc_rule_for_account.h b/src/exchangedb/pg_trigger_kyc_rule_for_account.h @@ -34,11 +34,16 @@ * can be NULL if @a h_payto is already * guaranteed to be in wire_targets * @param h_payto hash of @a payto_uri - * @param account_pub public key to enable for the + * @param set_account_pub public key to enable for the * KYC authorization, NULL if not known + * @param check_merchant_pub public key that must already + * be enabled for a KYC authorzation for it to be + * valid, NULL if not known * @param jmeasures serialized MeasureSet to put in place * @param display_priority priority of the rule * @param[out] requirement_row set to legitimization requirement row for this check + * @param[out] bad_kyc_auth set if @a check_account_pub + * did not match the existing KYC auth * @return database transaction status */ enum GNUNET_DB_QueryStatus @@ -46,9 +51,11 @@ TEH_PG_trigger_kyc_rule_for_account ( void *cls, const char *payto_uri, const struct TALER_PaytoHashP *h_payto, - const union TALER_AccountPublicKeyP *account_pub, + const union TALER_AccountPublicKeyP *set_account_pub, + const struct TALER_MerchantPublicKeyP *check_merchant_pub, const json_t *jmeasures, uint32_t display_priority, - uint64_t *requirement_row); + uint64_t *requirement_row, + bool *bad_kyc_auth); #endif diff --git a/src/include/taler_exchange_service.h b/src/include/taler_exchange_service.h @@ -36,7 +36,7 @@ * Version of the Taler Exchange API, in hex. * Thus 0.8.4-1 = 0x00080401. */ -#define TALER_EXCHANGE_API_VERSION 0x00100002 +#define TALER_EXCHANGE_API_VERSION 0x00100003 /** * Information returned when a client needs to pass @@ -63,6 +63,11 @@ struct TALER_EXCHANGE_KycNeededRedirect */ uint64_t requirement_row; + /** + * Set to true if the KYC AUTH public key known to the exchange does not + * match the merchant public key associated with the deposit operation. + */ + bool bad_kyc_auth; }; diff --git a/src/include/taler_exchangedb_plugin.h b/src/include/taler_exchangedb_plugin.h @@ -2957,6 +2957,14 @@ struct TALER_EXCHANGEDB_CsRevealFreshCoinData */ struct TALER_EXCHANGEDB_KycStatus { + + /** + * Account public key that is currently associated + * with the account. Only set if @e have_account_pub + * is true. + */ + union TALER_AccountPublicKeyP account_pub; + /** * Number that identifies the KYC requirement the operation * was about. @@ -2964,6 +2972,11 @@ struct TALER_EXCHANGEDB_KycStatus uint64_t requirement_row; /** + * True if @e account_pub is set. + */ + bool have_account_pub; + + /** * True if the KYC status is "satisfied". */ bool ok; @@ -6898,11 +6911,16 @@ struct TALER_EXCHANGEDB_Plugin * can be NULL if @a h_payto is already * guaranteed to be in wire_targets * @param h_payto hash of @a payto_uri - * @param account_pub public key to enable for the + * @param set_account_pub public key to enable for the * KYC authorization, NULL if not known + * @param check_merchant_pub public key that must already + * be enabled for a KYC authorzation for it to be + * valid, NULL if not known * @param jmeasures serialized MeasureSet to put in place * @param display_priority priority of the rule * @param[out] requirement_row set to legitimization requirement row for this check + * @param[out] bad_kyc_auth set if @a check_account_pub + * did not match the existing KYC auth * @return database transaction status */ enum GNUNET_DB_QueryStatus @@ -6910,10 +6928,12 @@ struct TALER_EXCHANGEDB_Plugin void *cls, const char *payto_uri, const struct TALER_PaytoHashP *h_payto, - const union TALER_AccountPublicKeyP *account_pub, + const union TALER_AccountPublicKeyP *set_account_pub, + const struct TALER_MerchantPublicKeyP *check_merchant_pub, const json_t *jmeasures, uint32_t display_priority, - uint64_t *requirement_row); + uint64_t *requirement_row, + bool *bad_kyc_auth); /** @@ -7091,6 +7111,8 @@ struct TALER_EXCHANGEDB_Plugin * * @param cls the @e cls of this struct with the plugin-specific state * @param h_payto account identifier + * @param[out] account_pub account public key the rules + * apply to (because this key was used in KYC auth) * @param[out] jrules set to the active KYC rules for the * given account, set to NULL if no custom rules are active * @return transaction status code @@ -7099,6 +7121,7 @@ struct TALER_EXCHANGEDB_Plugin (*get_kyc_rules)( void *cls, const struct TALER_PaytoHashP *h_payto, + union TALER_AccountPublicKeyP *account_pub, json_t **jrules); diff --git a/src/json/test_json.c b/src/json/test_json.c @@ -76,9 +76,11 @@ path_cb (void *cls, json_t *parent) { struct TestPath_Closure *cmp = cls; + unsigned int i; + if (NULL == cmp) return; - unsigned int i = cmp->results_length; + i = cmp->results_length; if ((0 != strcmp (cmp->object_ids[i], object_id)) || (1 != json_equal (cmp->parents[i], diff --git a/src/lib/exchange_api_batch_deposit.c b/src/lib/exchange_api_batch_deposit.c @@ -473,6 +473,9 @@ handle_deposit_finished (void *cls, GNUNET_JSON_spec_uint64 ( "requirement_row", &dr->details.unavailable_for_legal_reasons.requirement_row), + GNUNET_JSON_spec_bool ( + "bad_kyc_auth", + &dr->details.unavailable_for_legal_reasons.bad_kyc_auth), GNUNET_JSON_spec_end () };