merchant

Merchant backend to process payments, run by merchants
Log | Files | Refs | Submodules | README | LICENSE

commit ff8c5abc7448b282a50f931df4ac9184c93bc818
parent 9f969c1bbea784d81eb231b3f7b82efd46093ea7
Author: Marcello Stanisci <marcello.stanisci@inria.fr>
Date:   Wed, 16 Sep 2015 16:44:58 +0200

fixing 3966 and refactoring the way
the HTTP server handles malformed queries.

Diffstat:
Msrc/backend-lib/taler-merchant-httpd_contract.c | 38++++++++++++++++++--------------------
Msrc/backend-lib/taler_merchant_contract_lib.h | 5++---
Msrc/backend/taler-merchant-httpd.c | 197+++++++++++++++++++++++++++++++++++--------------------------------------------
Dsrc/tests/merchant-contract-test | 0
Msrc/tests/merchant-contract-test.c | 5++---
5 files changed, 109 insertions(+), 136 deletions(-)

diff --git a/src/backend-lib/taler-merchant-httpd_contract.c b/src/backend-lib/taler-merchant-httpd_contract.c @@ -51,12 +51,11 @@ hash_wireformat (uint64_t nounce, const struct MERCHANT_WIREFORMAT_Sepa *wire) /** * Take from the frontend the (partly) generated contract and fill * the missing values in it; for example, the SEPA-aware values. -* Moreover, it stores the contract in the DB and does the signature of it. +* Moreover, it stores the contract in the DB. * @param contract parsed contract, originated by the frontend * @param db_conn the handle to the local DB -* @param kpriv merchant's private key * @param wire merchant's bank's details -* @param sig where to store the signature +* @param sig where to store the (subset of the) contract to be signed * @return pointer to the complete JSON; NULL upon errors */ @@ -64,11 +63,10 @@ hash_wireformat (uint64_t nounce, const struct MERCHANT_WIREFORMAT_Sepa *wire) of the contract. To expand to the full fledged version */ json_t * -MERCHANT_handle_contract (json_t *contract, +MERCHANT_handle_contract (json_t *j_contract, PGconn *db_conn, - struct GNUNET_CRYPTO_EddsaPrivateKey *kpriv, const struct MERCHANT_WIREFORMAT_Sepa *wire, - struct GNUNET_CRYPTO_EddsaSignature *sig) + struct ContractNBO *contract) { json_t *root; json_t *j_details; @@ -88,7 +86,6 @@ MERCHANT_handle_contract (json_t *contract, struct GNUNET_TIME_Absolute timestamp; struct TALER_Amount amount; struct TALER_AmountNBO amount_nbo; - struct ContractNBO contract_nbo; nounce = GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_NONCE, UINT64_MAX); // timing mgmt @@ -105,14 +102,16 @@ MERCHANT_handle_contract (json_t *contract, jh_wire = json_string (h_wire_enc); #ifdef DEBUG - str = json_dumps (contract, JSON_INDENT(2) | JSON_PRESERVE_ORDER); + str = json_dumps (j_contract, JSON_INDENT(2) | JSON_PRESERVE_ORDER); #endif - json_unpack (contract, "{s:o, s:I, s:o}", - "amount", &j_amount, - "trans_id", &j_trans_id, - "details", &j_details); + if (-1 == json_unpack (j_contract, "{s:o, s:I, s:o}", + "amount", &j_amount, + "trans_id", &j_trans_id, + "details", &j_details)) + + return NULL; /* needed for DB stuff */ TALER_json_to_amount (j_amount, &amount); @@ -137,16 +136,15 @@ MERCHANT_handle_contract (json_t *contract, a, nounce, product_id)); - contract_nbo.h_wire = h_wire; + contract->h_wire = h_wire; TALER_amount_hton (&amount_nbo, &amount); - contract_nbo.amount = amount_nbo; - contract_nbo.t = GNUNET_TIME_absolute_hton (timestamp); - contract_nbo.m = GNUNET_htonll ((uint64_t) j_trans_id); // safe? - GNUNET_CRYPTO_hash (a, strlen (a) + 1, &contract_nbo.h_contract_details); + contract->amount = amount_nbo; + contract->t = GNUNET_TIME_absolute_hton (timestamp); + contract->m = GNUNET_htonll ((uint64_t) j_trans_id); // safe? + GNUNET_CRYPTO_hash (a, strlen (a) + 1, &contract->h_contract_details); free (a); - contract_nbo.purpose.purpose = htonl (TALER_SIGNATURE_MERCHANT_CONTRACT); - contract_nbo.purpose.size = htonl (sizeof (struct ContractNBO)); - GNUNET_CRYPTO_eddsa_sign (kpriv, &contract_nbo.purpose, sig); + contract->purpose.purpose = htonl (TALER_SIGNATURE_MERCHANT_CONTRACT); + contract->purpose.size = htonl (sizeof (struct ContractNBO)); return root; } diff --git a/src/backend-lib/taler_merchant_contract_lib.h b/src/backend-lib/taler_merchant_contract_lib.h @@ -55,8 +55,7 @@ GNUNET_NETWORK_STRUCT_END of the contract. To expand to the full fledged version */ json_t * -MERCHANT_handle_contract (json_t *contract, +MERCHANT_handle_contract (json_t *j_contract, PGconn *db_conn, - struct GNUNET_CRYPTO_EddsaPrivateKey *kpriv, const struct MERCHANT_WIREFORMAT_Sepa *wire, - struct GNUNET_CRYPTO_EddsaSignature *sig); + struct ContractNBO *contract); diff --git a/src/backend/taler-merchant-httpd.c b/src/backend/taler-merchant-httpd.c @@ -23,14 +23,14 @@ #include "platform.h" #include <microhttpd.h> #include <jansson.h> -#include <gnunet/gnunet_crypto_lib.h> #include <gnunet/gnunet_util_lib.h> #include <taler/taler_json_lib.h> #include <taler/taler_mint_service.h> #include "taler-mint-httpd_parsing.h" #include "taler-mint-httpd_responses.h" -#include "taler_merchant_lib.h" +#include "merchant_db.h" #include "merchant.h" +#include "taler_merchant_lib.h" extern struct MERCHANT_WIREFORMAT_Sepa * TALER_MERCHANT_parse_wireformat_sepa (const struct GNUNET_CONFIGURATION_Handle *cfg); @@ -59,6 +59,11 @@ static long long unsigned port; struct GNUNET_CRYPTO_EddsaPrivateKey *privkey; /** + * File holding the merchant's private key + */ +char *keyfile; + +/** * The MHD Daemon */ static struct MHD_Daemon *mhd; @@ -124,6 +129,7 @@ struct Mint */ static struct GNUNET_CONTAINER_MultiPeerMap *mints_map; +#if FUTURE_USE /** * Return the given message to the other end of connection * @msg (0-terminated) message to show @@ -146,6 +152,7 @@ generate_message (struct MHD_Response **resp, const char *msg) // this parameter } +#endif /** * Generate the 'hello world' response @@ -170,6 +177,7 @@ generate_hello (struct MHD_Response **resp) // this parameter was preceded by a } +#ifdef PANIC_MGMT /** * Callback for catching serious error conditions from MHD. * @@ -190,6 +198,7 @@ mhd_panic_cb (void *cls, result = GNUNET_SYSERR; GNUNET_SCHEDULER_shutdown (); } +#endif /** * Manage a non 200 HTTP status. I.e. it shows a 'failure' page to @@ -215,7 +224,7 @@ failure_resp (struct MHD_Connection *connection, unsigned int status) </center></body></html>"; static char page_MHD_HTTP_METHOD_NOT_ALLOWED[]="\ <!DOCTYPE html> \ -<html><title>Method NOT allowe</title><body><center> \ +<html><title>Method NOT allowed</title><body><center> \ <h3>ONLY POSTs are allowed.</h3> \ </center></body></html>"; static char page_MHD_HTTP_INTERNAL_SERVER_ERROR[]="\ @@ -316,153 +325,122 @@ url_handler (void *cls, printf ("%s\n", url); unsigned int status; unsigned int no_destroy; - json_int_t prod_id; - json_int_t contract_id; - struct Contract *contract; - void *contract_and_desc; struct GNUNET_CRYPTO_EddsaSignature c_sig; - struct MHD_Response *resp; - struct TALER_Amount price; struct GNUNET_CRYPTO_EddsaPublicKey pub; - json_t *json_price; + struct ContractNBO contract; + struct MHD_Response *resp; + json_t *j_contract_complete; json_t *root; - json_t *contract_enc; - json_t *sig_enc; + json_t *j_sig_enc; json_t *eddsa_pub_enc; json_t *response; int res = GNUNET_SYSERR; - const char *desc; #define URL_HELLO "/hello" #define URL_CONTRACT "/contract" no_destroy = 0; resp = NULL; - contract = NULL; status = MHD_HTTP_INTERNAL_SERVER_ERROR; + if (0 == strncasecmp (url, URL_HELLO, sizeof (URL_HELLO))) + { + if (0 == strcmp (MHD_HTTP_METHOD_GET, method)) + status = generate_hello (&resp); + else { - if (0 == strcmp (MHD_HTTP_METHOD_GET, method)) - status = generate_hello (&resp); - else - GNUNET_break (0); + status = MHD_HTTP_METHOD_NOT_ALLOWED; } + } + + /* + * To be called by the frontend passing the contract with some "holes" + * which will be completed, stored in DB, signed, and returned + * + */ - // to be called by the frontend passing all the product's information - // which are relevant for the contract's generation if (0 == strncasecmp (url, URL_CONTRACT, sizeof (URL_CONTRACT))) { if (0 == strcmp (MHD_HTTP_METHOD_GET, method)) { status = MHD_HTTP_METHOD_NOT_ALLOWED; + goto end; } else res = TMH_PARSE_post_json (connection, - connection_cls, - upload_data, - upload_data_size, - &root); - + connection_cls, + upload_data, + upload_data_size, + &root); if (GNUNET_SYSERR == res) - status = MHD_HTTP_METHOD_NOT_ALLOWED; + { + status = MHD_HTTP_BAD_REQUEST; + goto end; + } /* the POST's body has to be fetched furthermore */ if ((GNUNET_NO == res) || (NULL == root)) return MHD_YES; - /* The frontend should supply a JSON in the follwoing format: + /* The frontend should supply a JSON in the format described in + http://link-to-specs : */ + if (NULL == (j_contract_complete = MERCHANT_handle_contract (root, + db_conn, + wire, + &contract))) { - - "desc" : string human-readable describing this deal, - "product" : uint64-like integer referring to the product in some - DB adminstered by the frontend, - "cid" : uint64-like integer, this contract's id - "price" : a 'struct TALER_Amount' in the Taler compliant JSON format } - - */ - - if ((res = json_unpack (root, "{s:s, s:I, s:I, s:o}", - "desc", - &desc, - "product", - &prod_id, - "cid", - &contract_id, - "price", - &json_price - ))) - /* still not possible to return a taler-compliant error message - since this JSON format is not among the taler officials ones */ - status = MHD_HTTP_BAD_REQUEST; - else - { + status = MHD_HTTP_INTERNAL_SERVER_ERROR; + goto end; + } - if (GNUNET_OK != TALER_json_to_amount (json_price, &price)) - /* still not possible to return a taler-compliant error message - since this JSON format is not among the taler officials ones */ - status = MHD_HTTP_BAD_REQUEST; - else - { - /* Let's generate this contract! */ - /* FIXME : change this. Not existent anymore */ - if (NULL == (contract_and_desc = generate_and_store_contract (desc, - contract_id, - prod_id, - &price, - &c_sig))) - { - status = MHD_HTTP_INTERNAL_SERVER_ERROR; - } - else - { - json_decref (root); - json_decref (json_price); - contract = (struct Contract *) contract_and_desc; - - /* the contract is good and stored in DB, produce now JSON to return. - As of now, the format is {"contract" : base32contract, - "sig" : contractSignature, - "eddsa_pub" : keyToCheckSignatureAgainst - } - - */ + GNUNET_CRYPTO_eddsa_sign (privkey, &contract.purpose, &c_sig); + + /** + * + * As of now, the format is + * + * {"contract" : {the contract in "plain" JSON}, + * "sig" : base32 encoding of the signed 'struct ContractNBO', + * "eddsa_pub" : base32 encoding of merchant's public key} + * + */ - sig_enc = TALER_json_from_eddsa_sig (&contract->purpose, &c_sig); - GNUNET_CRYPTO_eddsa_key_get_public (privkey, &pub); - eddsa_pub_enc = TALER_json_from_data ((void *) &pub, sizeof (pub)); - contract_enc = TALER_json_from_data ((void *) contract, - sizeof (*contract) + strlen (desc) + 1); - GNUNET_free (contract_and_desc); - - response = json_pack ("{s:o, s:o, s:o}", - "contract", contract_enc, - "sig", sig_enc, - "eddsa_pub", eddsa_pub_enc); - - TMH_RESPONSE_reply_json (connection, response, MHD_HTTP_OK); - return MHD_YES; - - } - } - } + j_sig_enc = TALER_json_from_eddsa_sig (&contract.purpose, &c_sig); + GNUNET_CRYPTO_eddsa_key_get_public (privkey, &pub); + eddsa_pub_enc = TALER_json_from_data ((void *) &pub, sizeof (pub)); + response = json_pack ("{s:o, s:o, s:o}", + "contract", j_contract_complete, + "sig", j_sig_enc, + "eddsa_pub", eddsa_pub_enc); + + TMH_RESPONSE_reply_json (connection, response, MHD_HTTP_OK); + return MHD_YES; + } + end: + if (NULL != resp) - { - EXITIF (MHD_YES != MHD_queue_response (connection, status, resp)); - if (!no_destroy) - MHD_destroy_response (resp); - } + { + EXITIF (MHD_YES != MHD_queue_response (connection, status, resp)); + return MHD_YES; + if (!no_destroy) + MHD_destroy_response (resp); + } else + { + EXITIF (GNUNET_OK != failure_resp (connection, status)); - return MHD_YES; + return MHD_YES; - EXITIF_exit: - result = GNUNET_SYSERR; - GNUNET_SCHEDULER_shutdown (); - return MHD_NO; + } + + EXITIF_exit: + result = GNUNET_SYSERR; + GNUNET_SCHEDULER_shutdown (); + return MHD_NO; } /** @@ -527,7 +505,6 @@ run (void *cls, char *const *args, const char *cfgfile, const struct GNUNET_CONFIGURATION_Handle *config) { - char *keyfile; unsigned int nmints; unsigned int cnt; struct MERCHANT_MintInfo *mint_infos; @@ -541,7 +518,7 @@ run (void *cls, char *const *args, const char *cfgfile, &do_shutdown, NULL); EXITIF (GNUNET_SYSERR == (nmints = TALER_MERCHANT_parse_mints (config, &mint_infos))); - + EXITIF (NULL == (wire = TALER_MERCHANT_parse_wireformat_sepa (config))); EXITIF (GNUNET_OK != GNUNET_CONFIGURATION_get_value_filename (config, "merchant", "KEYFILE", diff --git a/src/tests/merchant-contract-test b/src/tests/merchant-contract-test Binary files differ. diff --git a/src/tests/merchant-contract-test.c b/src/tests/merchant-contract-test.c @@ -93,7 +93,7 @@ run (void *cls, char *const *args, const char *cfgfile, struct TALER_Amount amount; int64_t t_id; int64_t p_id; - struct GNUNET_CRYPTO_EddsaSignature c_sig; + struct ContractNBO contract; struct GNUNET_TIME_Absolute deldate; db_conn = NULL; @@ -249,9 +249,8 @@ run (void *cls, char *const *args, const char *cfgfile, j_root = MERCHANT_handle_contract (j_fake_contract, db_conn, - privkey, wire, - &c_sig); + &contract); #if 1 str = json_dumps (j_root, JSON_INDENT(2) | JSON_PRESERVE_ORDER);