anastasis

Credential backup and recovery protocol and service
Log | Files | Refs | Submodules | README | LICENSE

anastasis_api_truth_store.c (10166B)


      1 /*
      2   This file is part of Anastasis
      3   Copyright (C) 2020, 2021 Anastasis SARL
      4 
      5   Anastasis 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   Anastasis 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   Anastasis; see the file COPYING.GPL.  If not, see <http://www.gnu.org/licenses/>
     15 */
     16 /**
     17  * @file restclient/anastasis_api_truth_store.c
     18  * @brief Implementation of the /truth GET and POST
     19  * @author Christian Grothoff
     20  * @author Dennis Neufeld
     21  * @author Dominik Meister
     22  */
     23 #include "platform.h"
     24 #include <curl/curl.h>
     25 #include <jansson.h>
     26 #include <microhttpd.h> /* just for HTTP status codes */
     27 #include "anastasis_service.h"
     28 #include "anastasis_api_curl_defaults.h"
     29 #include <taler/taler_json_lib.h>
     30 #include <taler/taler_merchant_service.h>
     31 
     32 
     33 struct ANASTASIS_TruthStoreOperation
     34 {
     35   /**
     36    * Complete URL where the backend offers /truth
     37    */
     38   char *url;
     39 
     40   /**
     41    * Handle for the request.
     42    */
     43   struct GNUNET_CURL_Job *job;
     44 
     45   /**
     46    * The CURL context to connect to the backend
     47    */
     48   struct GNUNET_CURL_Context *ctx;
     49 
     50   /**
     51    * The callback to pass the backend response to
     52    */
     53   ANASTASIS_TruthStoreCallback cb;
     54 
     55   /**
     56    * Closure for @e cb.
     57    */
     58   void *cb_cls;
     59 
     60   /**
     61    * Reference to data (for cleanup).
     62    */
     63   char *data;
     64 
     65   /**
     66    * Payment URI we received from the service, or NULL.
     67    */
     68   char *pay_uri;
     69 };
     70 
     71 
     72 void
     73 ANASTASIS_truth_store_cancel (
     74   struct ANASTASIS_TruthStoreOperation *tso)
     75 {
     76   if (NULL != tso->job)
     77   {
     78     GNUNET_CURL_job_cancel (tso->job);
     79     tso->job = NULL;
     80   }
     81   GNUNET_free (tso->pay_uri);
     82   GNUNET_free (tso->url);
     83   GNUNET_free (tso->data);
     84   GNUNET_free (tso);
     85 }
     86 
     87 
     88 /**
     89  * Callback to process POST /truth response
     90  *
     91  * @param cls the `struct ANASTASIS_TruthStoreOperation`
     92  * @param response_code HTTP response code, 0 on error
     93  * @param data
     94  * @param data_size
     95  */
     96 static void
     97 handle_truth_store_finished (void *cls,
     98                              long response_code,
     99                              const void *data,
    100                              size_t data_size)
    101 {
    102   struct ANASTASIS_TruthStoreOperation *tso = cls;
    103   struct ANASTASIS_UploadDetails ud;
    104 
    105   tso->job = NULL;
    106   memset (&ud, 0, sizeof (ud));
    107   ud.http_status = response_code;
    108   ud.ec = TALER_EC_NONE;
    109   switch (response_code)
    110   {
    111   case 0:
    112     break;
    113   case MHD_HTTP_NO_CONTENT:
    114     ud.us = ANASTASIS_US_SUCCESS;
    115     break;
    116   case MHD_HTTP_NOT_MODIFIED:
    117     ud.us = ANASTASIS_US_SUCCESS;
    118     break;
    119   case MHD_HTTP_BAD_REQUEST:
    120     GNUNET_break (0);
    121     ud.ec = TALER_JSON_get_error_code2 (data,
    122                                         data_size);
    123     break;
    124   case MHD_HTTP_PAYMENT_REQUIRED:
    125     {
    126       struct TALER_MERCHANT_PayUriData pd;
    127 
    128       if ( (NULL == tso->pay_uri) ||
    129            (GNUNET_OK !=
    130             TALER_MERCHANT_parse_pay_uri (tso->pay_uri,
    131                                           &pd)) )
    132       {
    133         GNUNET_break_op (0);
    134         ud.ec = TALER_EC_ANASTASIS_GENERIC_INVALID_PAYMENT_REQUEST;
    135         break;
    136       }
    137       if (GNUNET_OK !=
    138           GNUNET_STRINGS_string_to_data (
    139             pd.order_id,
    140             strlen (pd.order_id),
    141             &ud.details.payment.ps,
    142             sizeof (ud.details.payment.ps)))
    143       {
    144         GNUNET_break (0);
    145         ud.ec = TALER_EC_ANASTASIS_GENERIC_INVALID_PAYMENT_REQUEST;
    146         TALER_MERCHANT_parse_pay_uri_free (&pd);
    147         break;
    148       }
    149       TALER_MERCHANT_parse_pay_uri_free (&pd);
    150     }
    151     ud.us = ANASTASIS_US_PAYMENT_REQUIRED;
    152     ud.details.payment.payment_request = tso->pay_uri;
    153     break;
    154   case MHD_HTTP_CONFLICT:
    155     ud.us = ANASTASIS_US_CONFLICTING_TRUTH;
    156     break;
    157   case MHD_HTTP_LENGTH_REQUIRED:
    158     GNUNET_break (0);
    159     break;
    160   case MHD_HTTP_REQUEST_ENTITY_TOO_LARGE:
    161     ud.ec = TALER_JSON_get_error_code2 (data,
    162                                         data_size);
    163     break;
    164   case MHD_HTTP_TOO_MANY_REQUESTS:
    165     ud.ec = TALER_JSON_get_error_code2 (data,
    166                                         data_size);
    167     break;
    168   case MHD_HTTP_INTERNAL_SERVER_ERROR:
    169     ud.ec = TALER_JSON_get_error_code2 (data,
    170                                         data_size);
    171     break;
    172   case MHD_HTTP_BAD_GATEWAY:
    173     ud.ec = TALER_JSON_get_error_code2 (data,
    174                                         data_size);
    175     break;
    176   default:
    177     ud.ec = TALER_JSON_get_error_code2 (data,
    178                                         data_size);
    179     GNUNET_break (0);
    180     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    181                 "Unexpected HTTP status code %u/%d\n",
    182                 (unsigned int) response_code,
    183                 ud.ec);
    184     break;
    185   }
    186   tso->cb (tso->cb_cls,
    187            &ud);
    188   tso->cb = NULL;
    189   ANASTASIS_truth_store_cancel (tso);
    190 }
    191 
    192 
    193 /**
    194  * Handle HTTP header received by curl.
    195  *
    196  * @param buffer one line of HTTP header data
    197  * @param size size of an item
    198  * @param nitems number of items passed
    199  * @param userdata our `struct ANASTASIS_StorePolicyOperation *`
    200  * @return `size * nitems`
    201  */
    202 static size_t
    203 handle_header (char *buffer,
    204                size_t size,
    205                size_t nitems,
    206                void *userdata)
    207 {
    208   struct ANASTASIS_TruthStoreOperation *tso = userdata;
    209   size_t total = size * nitems;
    210   char *ndup;
    211   const char *hdr_type;
    212   char *hdr_val;
    213   char *sp;
    214 
    215   ndup = GNUNET_strndup (buffer,
    216                          total);
    217   hdr_type = strtok_r (ndup,
    218                        ":",
    219                        &sp);
    220   if (NULL == hdr_type)
    221   {
    222     GNUNET_free (ndup);
    223     return total;
    224   }
    225   hdr_val = strtok_r (NULL,
    226                       "",
    227                       &sp);
    228   if (NULL == hdr_val)
    229   {
    230     GNUNET_free (ndup);
    231     return total;
    232   }
    233   if (' ' == *hdr_val)
    234     hdr_val++;
    235   if (0 == strcasecmp (hdr_type,
    236                        ANASTASIS_HTTP_HEADER_TALER))
    237   {
    238     size_t len;
    239 
    240     /* found payment URI we care about! */
    241     tso->pay_uri = GNUNET_strdup (hdr_val);
    242     len = strlen (tso->pay_uri);
    243     while ( (len > 0) &&
    244             ( ('\n' == tso->pay_uri[len - 1]) ||
    245               ('\r' == tso->pay_uri[len - 1]) ) )
    246     {
    247       len--;
    248       tso->pay_uri[len] = '\0';
    249     }
    250   }
    251   GNUNET_free (ndup);
    252   return total;
    253 }
    254 
    255 
    256 struct ANASTASIS_TruthStoreOperation *
    257 ANASTASIS_truth_store (
    258   struct GNUNET_CURL_Context *ctx,
    259   const char *backend_url,
    260   const struct ANASTASIS_CRYPTO_TruthUUIDP *uuid,
    261   const char *type,
    262   const struct ANASTASIS_CRYPTO_EncryptedKeyShareP *encrypted_keyshare,
    263   const char *truth_mime,
    264   size_t encrypted_truth_size,
    265   const void *encrypted_truth,
    266   uint32_t payment_years_requested,
    267   struct GNUNET_TIME_Relative payment_timeout,
    268   ANASTASIS_TruthStoreCallback cb,
    269   void *cb_cls)
    270 {
    271   struct ANASTASIS_TruthStoreOperation *tso;
    272   CURL *eh;
    273   char *json_str;
    274   unsigned long long tms;
    275 
    276   tms = (unsigned long long) (payment_timeout.rel_value_us
    277                               / GNUNET_TIME_UNIT_MILLISECONDS.rel_value_us);
    278   tso = GNUNET_new (struct ANASTASIS_TruthStoreOperation);
    279   {
    280     char *uuid_str;
    281     char *path;
    282     char timeout_ms[32];
    283 
    284     GNUNET_snprintf (timeout_ms,
    285                      sizeof (timeout_ms),
    286                      "%llu",
    287                      tms);
    288     uuid_str = GNUNET_STRINGS_data_to_string_alloc (uuid,
    289                                                     sizeof (*uuid));
    290     GNUNET_asprintf (&path,
    291                      "truth/%s",
    292                      uuid_str);
    293     tso->url = TALER_url_join (backend_url,
    294                                path,
    295                                "timeout_ms",
    296                                (0 != payment_timeout.rel_value_us)
    297                                ? timeout_ms
    298                                : NULL,
    299                                NULL);
    300     GNUNET_free (path);
    301     GNUNET_free (uuid_str);
    302   }
    303   {
    304     json_t *truth_data;
    305 
    306     truth_data = GNUNET_JSON_PACK (
    307       GNUNET_JSON_pack_data_auto ("key_share_data",
    308                                   encrypted_keyshare),
    309       GNUNET_JSON_pack_string ("type",
    310                                type),
    311       GNUNET_JSON_pack_data_varsize ("encrypted_truth",
    312                                      encrypted_truth,
    313                                      encrypted_truth_size),
    314       GNUNET_JSON_pack_allow_null (
    315         GNUNET_JSON_pack_string ("truth_mime",
    316                                  truth_mime)),
    317       GNUNET_JSON_pack_uint64 ("storage_duration_years",
    318                                payment_years_requested));
    319     json_str = json_dumps (truth_data,
    320                            JSON_COMPACT);
    321     GNUNET_assert (NULL != json_str);
    322     json_decref (truth_data);
    323   }
    324   tso->ctx = ctx;
    325   tso->data = json_str;
    326   tso->cb = cb;
    327   tso->cb_cls = cb_cls;
    328   eh = ANASTASIS_curl_easy_get_ (tso->url);
    329   if (0 != tms)
    330     GNUNET_assert (CURLE_OK ==
    331                    curl_easy_setopt (eh,
    332                                      CURLOPT_TIMEOUT_MS,
    333                                      (long) (tms + 5000)));
    334   GNUNET_assert (CURLE_OK ==
    335                  curl_easy_setopt (eh,
    336                                    CURLOPT_POSTFIELDS,
    337                                    json_str));
    338   GNUNET_assert (CURLE_OK ==
    339                  curl_easy_setopt (eh,
    340                                    CURLOPT_POSTFIELDSIZE,
    341                                    strlen (json_str)));
    342   GNUNET_assert (CURLE_OK ==
    343                  curl_easy_setopt (eh,
    344                                    CURLOPT_HEADERFUNCTION,
    345                                    &handle_header));
    346   GNUNET_assert (CURLE_OK ==
    347                  curl_easy_setopt (eh,
    348                                    CURLOPT_HEADERDATA,
    349                                    tso));
    350   tso->job = GNUNET_CURL_job_add_raw (ctx,
    351                                       eh,
    352                                       NULL,
    353                                       &handle_truth_store_finished,
    354                                       tso);
    355   return tso;
    356 }