LCOV - code coverage report
Current view: top level - exchange-lib - exchange_api_wire.c (source / functions) Hit Total Coverage
Test: rcoverage.info Lines: 93 128 72.7 %
Date: 2017-11-25 11:31:41 Functions: 7 7 100.0 %

          Line data    Source code
       1             : /*
       2             :   This file is part of TALER
       3             :   Copyright (C) 2014, 2015, 2016 Inria and GNUnet e.V.
       4             : 
       5             :   TALER is free software; you can redistribute it and/or modify it under the
       6             :   terms of the GNU General Public License as published by the Free Software
       7             :   Foundation; either version 3, or (at your option) any later version.
       8             : 
       9             :   TALER is distributed in the hope that it will be useful, but WITHOUT ANY
      10             :   WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
      11             :   A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
      12             : 
      13             :   You should have received a copy of the GNU General Public License along with
      14             :   TALER; see the file COPYING.  If not, see
      15             :   <http://www.gnu.org/licenses/>
      16             : */
      17             : /**
      18             :  * @file exchange-lib/exchange_api_wire.c
      19             :  * @brief Implementation of the /wire request of the exchange's HTTP API
      20             :  * @author Christian Grothoff
      21             :  */
      22             : #include "platform.h"
      23             : #include <curl/curl.h>
      24             : #include <jansson.h>
      25             : #include <microhttpd.h> /* just for HTTP status codes */
      26             : #include <gnunet/gnunet_util_lib.h>
      27             : #include <gnunet/gnunet_curl_lib.h>
      28             : #include "taler_exchange_service.h"
      29             : #include "taler_json_lib.h"
      30             : #include "taler_signatures.h"
      31             : #include "taler_wire_plugin.h"
      32             : #include "exchange_api_handle.h"
      33             : 
      34             : 
      35             : /**
      36             :  * @brief A Wire Handle
      37             :  */
      38             : struct TALER_EXCHANGE_WireHandle
      39             : {
      40             : 
      41             :   /**
      42             :    * The connection to exchange this request handle will use
      43             :    */
      44             :   struct TALER_EXCHANGE_Handle *exchange;
      45             : 
      46             :   /**
      47             :    * The url for this request.
      48             :    */
      49             :   char *url;
      50             : 
      51             :   /**
      52             :    * Handle for the request.
      53             :    */
      54             :   struct GNUNET_CURL_Job *job;
      55             : 
      56             :   /**
      57             :    * Function to call with the result.
      58             :    */
      59             :   TALER_EXCHANGE_WireResultCallback cb;
      60             : 
      61             :   /**
      62             :    * Closure for @a cb.
      63             :    */
      64             :   void *cb_cls;
      65             : 
      66             :   /**
      67             :    * Set to the "methods" JSON array returned by the
      68             :    * /wire request.
      69             :    */
      70             :   json_t *methods;
      71             : 
      72             :   /**
      73             :    * Current iteration offset in the @e methods array.
      74             :    */
      75             :   unsigned int methods_off;
      76             : 
      77             : };
      78             : 
      79             : 
      80             : /**
      81             :  * Verify that the signature on the "200 OK" response
      82             :  * for /wire/METHOD from the exchange is valid.
      83             :  *
      84             :  * @param wh wire handle with key material
      85             :  * @param method method to verify the reply for
      86             :  * @param json json reply with the signature
      87             :  * @return #GNUNET_SYSERR if @a json is invalid,
      88             :  *         #GNUNET_NO if the method is unknown,
      89             :  *         #GNUNET_OK if the json is valid
      90             :  */
      91             : static int
      92           4 : verify_wire_method_signature_ok (const struct TALER_EXCHANGE_WireHandle *wh,
      93             :                                  const char *method,
      94             :                                  const json_t *json)
      95             : {
      96             :   const struct TALER_EXCHANGE_Keys *key_state;
      97             :   struct TALER_WIRE_Plugin *plugin;
      98             :   char *lib_name;
      99             :   char *emsg;
     100             :   enum TALER_ErrorCode ec;
     101             : 
     102           4 :   key_state = TALER_EXCHANGE_get_keys (wh->exchange);
     103           4 :   (void) GNUNET_asprintf (&lib_name,
     104             :                           "libtaler_plugin_wire_%s",
     105             :                           method);
     106           4 :   plugin = GNUNET_PLUGIN_load (lib_name,
     107             :                                NULL);
     108           4 :   if (NULL == plugin)
     109             :   {
     110           0 :     GNUNET_free (lib_name);
     111           0 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     112             :                 "Wire transfer method `%s' not supported\n",
     113             :                 method);
     114           0 :     return GNUNET_NO;
     115             :   }
     116           4 :   plugin->library_name = lib_name;
     117           4 :   ec = plugin->wire_validate (plugin->cls,
     118             :                               json,
     119             :                               &key_state->master_pub,
     120             :                               &emsg);
     121           4 :   GNUNET_free_non_null (emsg);
     122           4 :   GNUNET_PLUGIN_unload (lib_name,
     123             :                         plugin);
     124           4 :   GNUNET_free (lib_name);
     125           4 :   return (TALER_EC_NONE == ec) ? GNUNET_OK : GNUNET_SYSERR;
     126             : }
     127             : 
     128             : 
     129             : /**
     130             :  * Function called when we're done processing the
     131             :  * HTTP /wire request.
     132             :  *
     133             :  * @param cls the `struct TALER_EXCHANGE_WireHandle`
     134             :  * @param response_code HTTP response code, 0 on error
     135             :  * @param json parsed JSON result, NULL on error
     136             :  */
     137             : static void
     138           2 : handle_wire_finished (void *cls,
     139             :                       long response_code,
     140             :                       const json_t *json)
     141             : {
     142           2 :   struct TALER_EXCHANGE_WireHandle *wh = cls;
     143           2 :   json_t *keep = NULL;
     144             : 
     145           2 :   wh->job = NULL;
     146           2 :   switch (response_code)
     147             :   {
     148             :   case 0:
     149           0 :     break;
     150             :   case MHD_HTTP_OK:
     151             :     {
     152             :       const struct TALER_EXCHANGE_Keys *keys;
     153             :       const struct TALER_MasterPublicKeyP *master_pub;
     154             :       const char *key;
     155             :       json_t *method;
     156             :       int ret;
     157             : 
     158             :       /* We 'keep' methods that we support and that are well-formed;
     159             :          we fail (by setting response_code=0) if any method that we do
     160             :          support fails to verify. */
     161           2 :       keep = json_object ();
     162           6 :       json_object_foreach ((json_t *) json, key, method) {
     163           4 :         ret = verify_wire_method_signature_ok (wh,
     164             :                                                key,
     165             :                                                method);
     166           4 :         if (GNUNET_SYSERR == ret)
     167             :         {
     168             :           /* bogus reply */
     169           0 :           GNUNET_break_op (0);
     170           0 :           response_code = 0;
     171             :         }
     172             :         /* GNUNET_NO: not understood by us, simply skip! */
     173           4 :         if (GNUNET_OK == ret)
     174             :         {
     175             :           /* supported and valid, keep! */
     176           4 :           json_object_set (keep,
     177             :                            key,
     178             :                            method);
     179             :         }
     180             :       }
     181             :       /* check fees */
     182           2 :       keys = TALER_EXCHANGE_get_keys (wh->exchange);
     183           2 :       if (NULL == keys)
     184           0 :         master_pub = NULL;
     185             :       else
     186           2 :         master_pub = &keys->master_pub;
     187           2 :       if (GNUNET_OK !=
     188           2 :           TALER_EXCHANGE_wire_get_fees (master_pub,
     189             :                                         keep,
     190             :                                         NULL,
     191             :                                         NULL))
     192             :       {
     193             :         /* bogus reply */
     194           0 :         GNUNET_break_op (0);
     195           0 :         response_code = 0;
     196             :       }
     197           2 :       if (0 != response_code)
     198             :       {
     199             :         /* all supported methods were valid, use 'keep' for 'json' */
     200           2 :         break;
     201             :       }
     202             :       /* some supported methods were invalid, release 'keep', preserve
     203             :          full 'json' for application-level error handling. */
     204           0 :       json_decref (keep);
     205           0 :       keep = NULL;
     206             :     }
     207           0 :     break;
     208             :   case MHD_HTTP_BAD_REQUEST:
     209             :     /* This should never happen, either us or the exchange is buggy
     210             :        (or API version conflict); just pass JSON reply to the application */
     211           0 :     break;
     212             :   case MHD_HTTP_NOT_FOUND:
     213             :     /* Nothing really to verify, this should never
     214             :        happen, we should pass the JSON reply to the application */
     215           0 :     break;
     216             :   case MHD_HTTP_INTERNAL_SERVER_ERROR:
     217             :     /* Server had an internal issue; we should retry, but this API
     218             :        leaves this to the application */
     219           0 :     break;
     220             :   default:
     221             :     /* unexpected response code */
     222           0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     223             :                 "Unexpected response code %u\n",
     224             :                 (unsigned int) response_code);
     225           0 :     GNUNET_break (0);
     226           0 :     response_code = 0;
     227           0 :     break;
     228             :   }
     229           2 :   wh->cb (wh->cb_cls,
     230             :           response_code,
     231             :           TALER_JSON_get_error_code (json),
     232             :           (NULL != keep) ? keep : json);
     233           2 :   if (NULL != keep)
     234           2 :     json_decref (keep);
     235           2 :   TALER_EXCHANGE_wire_cancel (wh);
     236           2 : }
     237             : 
     238             : 
     239             : /**
     240             :  * Obtain information about a exchange's wire instructions.
     241             :  * A exchange may provide wire instructions for creating
     242             :  * a reserve.  The wire instructions also indicate
     243             :  * which wire formats merchants may use with the exchange.
     244             :  * This API is typically used by a wallet for wiring
     245             :  * funds, and possibly by a merchant to determine
     246             :  * supported wire formats.
     247             :  *
     248             :  * Note that while we return the (main) response verbatim to the
     249             :  * caller for further processing, we do already verify that the
     250             :  * response is well-formed (i.e. that signatures included in the
     251             :  * response are all valid).  If the exchange's reply is not well-formed,
     252             :  * we return an HTTP status code of zero to @a cb.
     253             :  *
     254             :  * @param exchange the exchange handle; the exchange must be ready to operate
     255             :  * @param wire_cb the callback to call when a reply for this request is available
     256             :  * @param wire_cb_cls closure for the above callback
     257             :  * @return a handle for this request
     258             :  */
     259             : struct TALER_EXCHANGE_WireHandle *
     260           2 : TALER_EXCHANGE_wire (struct TALER_EXCHANGE_Handle *exchange,
     261             :                      TALER_EXCHANGE_WireResultCallback wire_cb,
     262             :                      void *wire_cb_cls)
     263             : {
     264             :   struct TALER_EXCHANGE_WireHandle *wh;
     265             :   struct GNUNET_CURL_Context *ctx;
     266             :   CURL *eh;
     267             : 
     268           2 :   if (GNUNET_YES !=
     269           2 :       MAH_handle_is_ready (exchange))
     270             :   {
     271           0 :     GNUNET_break (0);
     272           0 :     return NULL;
     273             :   }
     274           2 :   wh = GNUNET_new (struct TALER_EXCHANGE_WireHandle);
     275           2 :   wh->exchange = exchange;
     276           2 :   wh->cb = wire_cb;
     277           2 :   wh->cb_cls = wire_cb_cls;
     278           2 :   wh->url = MAH_path_to_url (exchange, "/wire");
     279             : 
     280           2 :   eh = curl_easy_init ();
     281           2 :   GNUNET_assert (CURLE_OK ==
     282             :                  curl_easy_setopt (eh,
     283             :                                    CURLOPT_URL,
     284             :                                    wh->url));
     285           2 :   ctx = MAH_handle_to_context (exchange);
     286           2 :   wh->job = GNUNET_CURL_job_add (ctx,
     287             :                          eh,
     288             :                          GNUNET_YES,
     289             :                          &handle_wire_finished,
     290             :                          wh);
     291           2 :   return wh;
     292             : }
     293             : 
     294             : 
     295             : /**
     296             :  * Cancel a wire information request.  This function cannot be used
     297             :  * on a request handle if a response is already served for it.
     298             :  *
     299             :  * @param wh the wire information request handle
     300             :  */
     301             : void
     302           2 : TALER_EXCHANGE_wire_cancel (struct TALER_EXCHANGE_WireHandle *wh)
     303             : {
     304           2 :   if (NULL != wh->job)
     305             :   {
     306           0 :     GNUNET_CURL_job_cancel (wh->job);
     307           0 :     wh->job = NULL;
     308             :   }
     309           2 :   if (NULL != wh->methods)
     310             :   {
     311           0 :     json_decref (wh->methods);
     312           0 :     wh->methods = NULL;
     313             :   }
     314           2 :   GNUNET_free (wh->url);
     315           2 :   GNUNET_free (wh);
     316           2 : }
     317             : 
     318             : 
     319             : /**
     320             :  * Parse wire @a fee and store the result in @a af.
     321             :  *
     322             :  * @param[out] af where to write the result
     323             :  * @param fee json AggregateTransferFee to parse
     324             :  * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
     325             :  */
     326             : static int
     327          24 : parse_json_fees (struct TALER_EXCHANGE_WireAggregateFees *af,
     328             :                  json_t *fee)
     329             : {
     330         120 :   struct GNUNET_JSON_Specification spec[] = {
     331          24 :     GNUNET_JSON_spec_fixed_auto ("sig",
     332             :                                  &af->master_sig),
     333          24 :     TALER_JSON_spec_amount ("wire_fee",
     334             :                             &af->wire_fee),
     335          24 :     TALER_JSON_spec_amount ("closing_fee",
     336             :                             &af->closing_fee),
     337          24 :     GNUNET_JSON_spec_absolute_time ("start_date",
     338             :                                     &af->start_date),
     339          24 :     GNUNET_JSON_spec_absolute_time ("end_date",
     340             :                                     &af->end_date),
     341             :     GNUNET_JSON_spec_end()
     342             :   };
     343             : 
     344          24 :   if (GNUNET_OK !=
     345          24 :       GNUNET_JSON_parse (fee,
     346             :                          spec,
     347             :                          NULL,
     348             :                          NULL))
     349             :   {
     350           0 :     GNUNET_break_op (0);
     351           0 :     return GNUNET_SYSERR;
     352             :   }
     353          24 :   return GNUNET_OK;
     354             : }
     355             : 
     356             : 
     357             : /**
     358             :  * Check the #TALER_SIGNATURE_MASTER_WIRE_FEES signature.
     359             :  *
     360             :  * @param af record to check
     361             :  * @param wire_method wire method to check against
     362             :  * @param master_pub expected signing key
     363             :  * @return #GNUNET_OK on success, #GNUNET_SYSERR on failure
     364             :  */
     365             : static int
     366          24 : check_sig (const struct TALER_EXCHANGE_WireAggregateFees *af,
     367             :            const char *wire_method,
     368             :            const struct TALER_MasterPublicKeyP *master_pub)
     369             : {
     370             :   struct TALER_MasterWireFeePS wp;
     371             : 
     372          24 :   wp.purpose.purpose = htonl (TALER_SIGNATURE_MASTER_WIRE_FEES);
     373          24 :   wp.purpose.size = htonl (sizeof (wp));
     374          24 :   GNUNET_CRYPTO_hash (wire_method,
     375          24 :                       strlen (wire_method) + 1,
     376             :                       &wp.h_wire_method);
     377          24 :   wp.start_date = GNUNET_TIME_absolute_hton (af->start_date);
     378          24 :   wp.end_date = GNUNET_TIME_absolute_hton (af->end_date);
     379          24 :   TALER_amount_hton (&wp.wire_fee,
     380             :                      &af->wire_fee);
     381          24 :   TALER_amount_hton (&wp.closing_fee,
     382             :                      &af->closing_fee);
     383          24 :   return GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_MASTER_WIRE_FEES,
     384             :                                      &wp.purpose,
     385             :                                      &af->master_sig.eddsa_signature,
     386             :                                      &master_pub->eddsa_pub);
     387             : }
     388             : 
     389             : 
     390             : /**
     391             :  * Obtain information about wire fees encoded in @a obj
     392             :  * by wire method.
     393             :  *
     394             :  * @param master_pub public key to use to verify signatures, NULL to not verify
     395             :  * @param obj wire information as encoded in the #TALER_EXCHANGE_WireResultCallback
     396             :  * @param cb callback to invoke for the fees
     397             :  * @param cb_cls closure for @a cb
     398             :  * @return #GNUNET_OK in success, #GNUNET_SYSERR if @a obj is ill-formed
     399             :  */
     400             : int
     401           4 : TALER_EXCHANGE_wire_get_fees (const struct TALER_MasterPublicKeyP *master_pub,
     402             :                               const json_t *obj,
     403             :                               TALER_EXCHANGE_WireFeeCallback cb,
     404             :                               void *cb_cls)
     405             : {
     406             :   const char *wire_method;
     407             :   json_t *value;
     408             : 
     409          12 :   json_object_foreach (((json_t *) obj), wire_method, value)
     410             :   {
     411             :     json_t *fees;
     412             :     size_t num_fees;
     413             : 
     414           8 :     fees = json_object_get (value, "fees");
     415           8 :     if ( (NULL == fees) ||
     416           8 :          (! json_is_array (fees)) )
     417             :     {
     418           0 :       GNUNET_break_op (0);
     419           0 :       return GNUNET_SYSERR;
     420             :     }
     421           8 :     num_fees = json_array_size (fees);
     422           8 :     if (num_fees > 1024)
     423             :     {
     424           0 :       GNUNET_break_op (0);
     425           0 :       return GNUNET_SYSERR;
     426             :     }
     427           8 :     {
     428           8 :       struct TALER_EXCHANGE_WireAggregateFees af[num_fees + 1];
     429             : 
     430          32 :       for (size_t i=0;i<num_fees;i++)
     431             :       {
     432          24 :         af[i].next = &af[i+1];
     433          24 :         if (GNUNET_OK !=
     434          24 :             parse_json_fees (&af[i],
     435             :                              json_array_get (fees,
     436             :                                              i)))
     437             :         {
     438           0 :           GNUNET_break_op (0);
     439           0 :           return GNUNET_SYSERR;
     440             :         }
     441          48 :         if ( (NULL != master_pub) &&
     442             :              (GNUNET_OK !=
     443          24 :               check_sig (&af[i],
     444             :                          wire_method,
     445             :                          master_pub)) )
     446             :         {
     447           0 :           GNUNET_break_op (0);
     448           0 :           return GNUNET_SYSERR;
     449             :         }
     450             :       }
     451           8 :       af[num_fees - 1].next = NULL;
     452           8 :       if (NULL != cb)
     453           4 :         cb (cb_cls,
     454             :             wire_method,
     455           4 :             &af[0]);
     456             :     }
     457             :   }
     458           4 :   return GNUNET_OK;
     459             : }
     460             : 
     461             : 
     462             : /* end of exchange_api_wire.c */

Generated by: LCOV version 1.13