anastasis

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

anastasis_api_policy_lookup.c (11012B)


      1 /*
      2   This file is part of ANASTASIS
      3   Copyright (C) 2014-2019 Anastasis SARL
      4 
      5   ANASTASIS is free software; you can redistribute it and/or modify
      6   it under the terms of the GNU General Public License as
      7   published by the Free Software Foundation; either version 2.1,
      8   or (at your option) any later version.
      9 
     10   ANASTASIS is distributed in the hope that it will be useful, but
     11   WITHOUT ANY WARRANTY; without even the implied warranty of
     12   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     13   GNU General Public License for more details.
     14 
     15   You should have received a copy of the GNU General Public
     16   License along with ANASTASIS; see the file COPYING.LGPL.  If not,
     17   see <http://www.gnu.org/licenses/>
     18 */
     19 
     20 /**
     21  * @file restclient/anastasis_api_policy_lookup.c
     22  * @brief Implementation of the /policy GET and POST
     23  * @author Christian Grothoff
     24  * @author Dennis Neufeld
     25  * @author Dominik Meister
     26  */
     27 #include "platform.h"
     28 #include <curl/curl.h>
     29 #include <jansson.h>
     30 #include <microhttpd.h> /* just for HTTP status codes */
     31 #include "anastasis_service.h"
     32 #include "anastasis_api_curl_defaults.h"
     33 #include <taler/taler_signatures.h>
     34 
     35 
     36 /**
     37  * @brief A Contract Operation Handle
     38  */
     39 struct ANASTASIS_PolicyLookupOperation
     40 {
     41 
     42   /**
     43    * The url for this request, including parameters.
     44    */
     45   char *url;
     46 
     47   /**
     48    * Handle for the request.
     49    */
     50   struct GNUNET_CURL_Job *job;
     51 
     52   /**
     53    * Function to call with the result.
     54    */
     55   ANASTASIS_PolicyLookupCallback cb;
     56 
     57   /**
     58    * Closure for @a cb.
     59    */
     60   void *cb_cls;
     61 
     62   /**
     63    * Reference to the execution context.
     64    */
     65   struct GNUNET_CURL_Context *ctx;
     66 
     67   /**
     68    * Public key of the account we are downloading from.
     69    */
     70   struct ANASTASIS_CRYPTO_AccountPublicKeyP account_pub;
     71 
     72   /**
     73    * Signature returned in the #ANASTASIS_HTTP_HEADER_POLICY_SIGNATURE
     74    * header, or all zeros for none.
     75    */
     76   struct ANASTASIS_AccountSignatureP account_sig;
     77 
     78   /**
     79    * Version of the policy, as claimed by the provider in the
     80    * #ANASTASIS_HTTP_HEADER_POLICY_VERSION header.
     81    */
     82   unsigned int version;
     83 
     84   /**
     85    * Version we asked for, or 0 if we asked for the latest one.
     86    */
     87   unsigned int requested_version;
     88 
     89   /**
     90    * True if we asked for a specific version, so that @e requested_version
     91    * must match what the provider returns.
     92    */
     93   bool version_requested;
     94 
     95 };
     96 
     97 
     98 void
     99 ANASTASIS_policy_lookup_cancel (struct ANASTASIS_PolicyLookupOperation *plo)
    100 {
    101   if (NULL != plo->job)
    102   {
    103     GNUNET_CURL_job_cancel (plo->job);
    104     plo->job = NULL;
    105   }
    106   GNUNET_free (plo->url);
    107   GNUNET_free (plo);
    108 }
    109 
    110 
    111 /**
    112  * Process GET /policy response
    113  */
    114 static void
    115 handle_policy_lookup_finished (void *cls,
    116                                long response_code,
    117                                const void *data,
    118                                size_t data_size)
    119 {
    120   struct ANASTASIS_PolicyLookupOperation *plo = cls;
    121   struct ANASTASIS_DownloadDetails dd = {
    122     .http_status = response_code
    123   };
    124 
    125   plo->job = NULL;
    126   switch (response_code)
    127   {
    128   case 0:
    129     /* Hard error */
    130     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    131                 "Backend didn't even return from GET /policy\n");
    132     break;
    133   case MHD_HTTP_OK:
    134     {
    135       struct ANASTASIS_UploadSignaturePS usp = {
    136         .purpose.purpose = htonl (TALER_SIGNATURE_ANASTASIS_POLICY_UPLOAD),
    137         .purpose.size = htonl (sizeof (usp)),
    138       };
    139 
    140       GNUNET_CRYPTO_hash (data,
    141                           data_size,
    142                           &usp.new_recovery_data_hash);
    143       if (GNUNET_OK !=
    144           GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_ANASTASIS_POLICY_UPLOAD,
    145                                       &usp,
    146                                       &plo->account_sig.eddsa_sig,
    147                                       &plo->account_pub.pub))
    148       {
    149         GNUNET_break_op (0);
    150         dd.http_status = 0;
    151         dd.ec = -1; // FIXME: needs new code in Gana!
    152         break;
    153       }
    154       if ( (plo->version_requested) &&
    155            (plo->version != plo->requested_version) )
    156       {
    157         /* The version header is not covered by any signature, so a provider
    158            can put whatever it likes there.  We can at least insist that a
    159            lookup for a specific version is answered with that version. */
    160         GNUNET_break_op (0);
    161         GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    162                     "Provider claims version %u for a lookup of version %u\n",
    163                     plo->version,
    164                     plo->requested_version);
    165         dd.http_status = 0;
    166         dd.ec = -1; // FIXME: needs new code in Gana!
    167         break;
    168       }
    169       /* Success, call callback with all details! */
    170       dd.details.ok.sig = plo->account_sig;
    171       dd.details.ok.curr_policy_hash = usp.new_recovery_data_hash;
    172       dd.details.ok.policy = data;
    173       dd.details.ok.policy_size = data_size;
    174       dd.details.ok.version = plo->version;
    175       plo->cb (plo->cb_cls,
    176                &dd);
    177       plo->cb = NULL;
    178       ANASTASIS_policy_lookup_cancel (plo);
    179       return;
    180     }
    181   case MHD_HTTP_BAD_REQUEST:
    182     /* This should never happen, either us or the anastasis server is buggy
    183        (or API version conflict); just pass JSON reply to the application */
    184     break;
    185   case MHD_HTTP_NOT_FOUND:
    186     /* Nothing really to verify */
    187     break;
    188   case MHD_HTTP_INTERNAL_SERVER_ERROR:
    189     /* Server had an internal issue; we should retry, but this API
    190        leaves this to the application */
    191     break;
    192   default:
    193     /* unexpected response code */
    194     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    195                 "Unexpected response code %u\n",
    196                 (unsigned int) response_code);
    197     GNUNET_break (0);
    198     break;
    199   }
    200   plo->cb (plo->cb_cls,
    201            &dd);
    202   plo->cb = NULL;
    203   ANASTASIS_policy_lookup_cancel (plo);
    204 }
    205 
    206 
    207 /**
    208  * Handle HTTP header received by curl.
    209  *
    210  * @param buffer one line of HTTP header data
    211  * @param size size of an item
    212  * @param nitems number of items passed
    213  * @param userdata our `struct ANASTASIS_PolicyLookupOperation *`
    214  * @return `size * nitems`
    215  */
    216 static size_t
    217 handle_header (char *buffer,
    218                size_t size,
    219                size_t nitems,
    220                void *userdata)
    221 {
    222   struct ANASTASIS_PolicyLookupOperation *plo = userdata;
    223   size_t total = size * nitems;
    224   char *ndup;
    225   const char *hdr_type;
    226   char *hdr_val;
    227   char *sp;
    228 
    229   ndup = GNUNET_strndup (buffer,
    230                          total);
    231   hdr_type = strtok_r (ndup,
    232                        ":",
    233                        &sp);
    234   if (NULL == hdr_type)
    235   {
    236     GNUNET_free (ndup);
    237     return total;
    238   }
    239   hdr_val = strtok_r (NULL,
    240                       "\n\r",
    241                       &sp);
    242   if (NULL == hdr_val)
    243   {
    244     GNUNET_free (ndup);
    245     return total;
    246   }
    247   if (' ' == *hdr_val)
    248     hdr_val++;
    249   if (0 == strcasecmp (hdr_type,
    250                        ANASTASIS_HTTP_HEADER_POLICY_SIGNATURE))
    251   {
    252     if (GNUNET_OK !=
    253         GNUNET_STRINGS_string_to_data (
    254           hdr_val,
    255           strlen (hdr_val),
    256           &plo->account_sig,
    257           sizeof (struct ANASTASIS_AccountSignatureP)))
    258     {
    259       GNUNET_break_op (0);
    260       GNUNET_free (ndup);
    261       return 0;
    262     }
    263   }
    264   if (0 == strcasecmp (hdr_type,
    265                        ANASTASIS_HTTP_HEADER_POLICY_VERSION))
    266   {
    267     char dummy;
    268 
    269     if (1 !=
    270         sscanf (hdr_val,
    271                 "%u%c",
    272                 &plo->version,
    273                 &dummy))
    274     {
    275       GNUNET_break_op (0);
    276       GNUNET_free (ndup);
    277       return 0;
    278     }
    279   }
    280   GNUNET_free (ndup);
    281   return total;
    282 }
    283 
    284 
    285 struct ANASTASIS_PolicyLookupOperation *
    286 ANASTASIS_policy_lookup (
    287   struct GNUNET_CURL_Context *ctx,
    288   const char *backend_url,
    289   const struct ANASTASIS_CRYPTO_AccountPublicKeyP *anastasis_pub,
    290   ANASTASIS_PolicyLookupCallback cb,
    291   void *cb_cls)
    292 {
    293   struct ANASTASIS_PolicyLookupOperation *plo;
    294   CURL *eh;
    295   char *acc_pub_str;
    296   char *path;
    297 
    298   GNUNET_assert (NULL != cb);
    299   plo = GNUNET_new (struct ANASTASIS_PolicyLookupOperation);
    300   plo->account_pub = *anastasis_pub;
    301   acc_pub_str = GNUNET_STRINGS_data_to_string_alloc (anastasis_pub,
    302                                                      sizeof (*anastasis_pub));
    303   GNUNET_asprintf (&path,
    304                    "policy/%s",
    305                    acc_pub_str);
    306   GNUNET_free (acc_pub_str);
    307   plo->url = TALER_url_join (backend_url,
    308                              path,
    309                              NULL);
    310   GNUNET_free (path);
    311   eh = ANASTASIS_curl_easy_get_ (plo->url);
    312   if (NULL == eh)
    313   {
    314     GNUNET_break (0);
    315     GNUNET_free (plo->url);
    316     GNUNET_free (plo);
    317     return NULL;
    318   }
    319   GNUNET_assert (CURLE_OK ==
    320                  curl_easy_setopt (eh,
    321                                    CURLOPT_HEADERFUNCTION,
    322                                    &handle_header));
    323   GNUNET_assert (CURLE_OK ==
    324                  curl_easy_setopt (eh,
    325                                    CURLOPT_HEADERDATA,
    326                                    plo));
    327   plo->cb = cb;
    328   plo->cb_cls = cb_cls;
    329   plo->job = GNUNET_CURL_job_add_raw (ctx,
    330                                       eh,
    331                                       NULL,
    332                                       &handle_policy_lookup_finished,
    333                                       plo);
    334   return plo;
    335 }
    336 
    337 
    338 struct ANASTASIS_PolicyLookupOperation *
    339 ANASTASIS_policy_lookup_version (
    340   struct GNUNET_CURL_Context *ctx,
    341   const char *backend_url,
    342   const struct ANASTASIS_CRYPTO_AccountPublicKeyP *anastasis_pub,
    343   ANASTASIS_PolicyLookupCallback cb,
    344   void *cb_cls,
    345   unsigned int version)
    346 {
    347   struct ANASTASIS_PolicyLookupOperation *plo;
    348   CURL *eh;
    349   char *acc_pub_str;
    350   char *path;
    351   char version_s[14];
    352 
    353   GNUNET_assert (NULL != cb);
    354   plo = GNUNET_new (struct ANASTASIS_PolicyLookupOperation);
    355   plo->account_pub = *anastasis_pub;
    356   acc_pub_str = GNUNET_STRINGS_data_to_string_alloc (anastasis_pub,
    357                                                      sizeof (*anastasis_pub));
    358   GNUNET_asprintf (&path,
    359                    "policy/%s",
    360                    acc_pub_str);
    361   GNUNET_free (acc_pub_str);
    362   plo->requested_version = version;
    363   plo->version_requested = true;
    364   GNUNET_snprintf (version_s,
    365                    sizeof (version_s),
    366                    "%u",
    367                    version);
    368   plo->url = TALER_url_join (backend_url,
    369                              path,
    370                              "version",
    371                              version_s,
    372                              NULL);
    373   GNUNET_free (path);
    374   eh = ANASTASIS_curl_easy_get_ (plo->url);
    375   GNUNET_assert (CURLE_OK ==
    376                  curl_easy_setopt (eh,
    377                                    CURLOPT_HEADERFUNCTION,
    378                                    &handle_header));
    379   GNUNET_assert (CURLE_OK ==
    380                  curl_easy_setopt (eh,
    381                                    CURLOPT_HEADERDATA,
    382                                    plo));
    383   plo->cb = cb;
    384   plo->cb_cls = cb_cls;
    385   plo->job = GNUNET_CURL_job_add_raw (ctx,
    386                                       eh,
    387                                       NULL,
    388                                       &handle_policy_lookup_finished,
    389                                       plo);
    390   return plo;
    391 }