anastasis

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

anastasis_api_policy_lookup.c (9923B)


      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.
     80    */
     81   unsigned int version;
     82 
     83 };
     84 
     85 
     86 void
     87 ANASTASIS_policy_lookup_cancel (struct ANASTASIS_PolicyLookupOperation *plo)
     88 {
     89   if (NULL != plo->job)
     90   {
     91     GNUNET_CURL_job_cancel (plo->job);
     92     plo->job = NULL;
     93   }
     94   GNUNET_free (plo->url);
     95   GNUNET_free (plo);
     96 }
     97 
     98 
     99 /**
    100  * Process GET /policy response
    101  */
    102 static void
    103 handle_policy_lookup_finished (void *cls,
    104                                long response_code,
    105                                const void *data,
    106                                size_t data_size)
    107 {
    108   struct ANASTASIS_PolicyLookupOperation *plo = cls;
    109   struct ANASTASIS_DownloadDetails dd = {
    110     .http_status = response_code
    111   };
    112 
    113   plo->job = NULL;
    114   switch (response_code)
    115   {
    116   case 0:
    117     /* Hard error */
    118     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    119                 "Backend didn't even return from GET /policy\n");
    120     break;
    121   case MHD_HTTP_OK:
    122     {
    123       struct ANASTASIS_UploadSignaturePS usp = {
    124         .purpose.purpose = htonl (TALER_SIGNATURE_ANASTASIS_POLICY_UPLOAD),
    125         .purpose.size = htonl (sizeof (usp)),
    126       };
    127 
    128       GNUNET_CRYPTO_hash (data,
    129                           data_size,
    130                           &usp.new_recovery_data_hash);
    131       if (GNUNET_OK !=
    132           GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_ANASTASIS_POLICY_UPLOAD,
    133                                       &usp,
    134                                       &plo->account_sig.eddsa_sig,
    135                                       &plo->account_pub.pub))
    136       {
    137         GNUNET_break_op (0);
    138         dd.http_status = 0;
    139         dd.ec = -1; // FIXME: needs new code in Gana!
    140         break;
    141       }
    142       /* Success, call callback with all details! */
    143       dd.details.ok.sig = plo->account_sig;
    144       dd.details.ok.curr_policy_hash = usp.new_recovery_data_hash;
    145       dd.details.ok.policy = data;
    146       dd.details.ok.policy_size = data_size;
    147       dd.details.ok.version = plo->version;
    148       plo->cb (plo->cb_cls,
    149                &dd);
    150       plo->cb = NULL;
    151       ANASTASIS_policy_lookup_cancel (plo);
    152       return;
    153     }
    154   case MHD_HTTP_BAD_REQUEST:
    155     /* This should never happen, either us or the anastasis server is buggy
    156        (or API version conflict); just pass JSON reply to the application */
    157     break;
    158   case MHD_HTTP_NOT_FOUND:
    159     /* Nothing really to verify */
    160     break;
    161   case MHD_HTTP_INTERNAL_SERVER_ERROR:
    162     /* Server had an internal issue; we should retry, but this API
    163        leaves this to the application */
    164     break;
    165   default:
    166     /* unexpected response code */
    167     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    168                 "Unexpected response code %u\n",
    169                 (unsigned int) response_code);
    170     GNUNET_break (0);
    171     break;
    172   }
    173   plo->cb (plo->cb_cls,
    174            &dd);
    175   plo->cb = NULL;
    176   ANASTASIS_policy_lookup_cancel (plo);
    177 }
    178 
    179 
    180 /**
    181  * Handle HTTP header received by curl.
    182  *
    183  * @param buffer one line of HTTP header data
    184  * @param size size of an item
    185  * @param nitems number of items passed
    186  * @param userdata our `struct ANASTASIS_PolicyLookupOperation *`
    187  * @return `size * nitems`
    188  */
    189 static size_t
    190 handle_header (char *buffer,
    191                size_t size,
    192                size_t nitems,
    193                void *userdata)
    194 {
    195   struct ANASTASIS_PolicyLookupOperation *plo = userdata;
    196   size_t total = size * nitems;
    197   char *ndup;
    198   const char *hdr_type;
    199   char *hdr_val;
    200   char *sp;
    201 
    202   ndup = GNUNET_strndup (buffer,
    203                          total);
    204   hdr_type = strtok_r (ndup,
    205                        ":",
    206                        &sp);
    207   if (NULL == hdr_type)
    208   {
    209     GNUNET_free (ndup);
    210     return total;
    211   }
    212   hdr_val = strtok_r (NULL,
    213                       "\n\r",
    214                       &sp);
    215   if (NULL == hdr_val)
    216   {
    217     GNUNET_free (ndup);
    218     return total;
    219   }
    220   if (' ' == *hdr_val)
    221     hdr_val++;
    222   if (0 == strcasecmp (hdr_type,
    223                        ANASTASIS_HTTP_HEADER_POLICY_SIGNATURE))
    224   {
    225     if (GNUNET_OK !=
    226         GNUNET_STRINGS_string_to_data (
    227           hdr_val,
    228           strlen (hdr_val),
    229           &plo->account_sig,
    230           sizeof (struct ANASTASIS_AccountSignatureP)))
    231     {
    232       GNUNET_break_op (0);
    233       GNUNET_free (ndup);
    234       return 0;
    235     }
    236   }
    237   if (0 == strcasecmp (hdr_type,
    238                        ANASTASIS_HTTP_HEADER_POLICY_VERSION))
    239   {
    240     char dummy;
    241 
    242     if (1 !=
    243         sscanf (hdr_val,
    244                 "%u%c",
    245                 &plo->version,
    246                 &dummy))
    247     {
    248       GNUNET_break_op (0);
    249       GNUNET_free (ndup);
    250       return 0;
    251     }
    252   }
    253   GNUNET_free (ndup);
    254   return total;
    255 }
    256 
    257 
    258 struct ANASTASIS_PolicyLookupOperation *
    259 ANASTASIS_policy_lookup (
    260   struct GNUNET_CURL_Context *ctx,
    261   const char *backend_url,
    262   const struct ANASTASIS_CRYPTO_AccountPublicKeyP *anastasis_pub,
    263   ANASTASIS_PolicyLookupCallback cb,
    264   void *cb_cls)
    265 {
    266   struct ANASTASIS_PolicyLookupOperation *plo;
    267   CURL *eh;
    268   char *acc_pub_str;
    269   char *path;
    270 
    271   GNUNET_assert (NULL != cb);
    272   plo = GNUNET_new (struct ANASTASIS_PolicyLookupOperation);
    273   plo->account_pub = *anastasis_pub;
    274   acc_pub_str = GNUNET_STRINGS_data_to_string_alloc (anastasis_pub,
    275                                                      sizeof (*anastasis_pub));
    276   GNUNET_asprintf (&path,
    277                    "policy/%s",
    278                    acc_pub_str);
    279   GNUNET_free (acc_pub_str);
    280   plo->url = TALER_url_join (backend_url,
    281                              path,
    282                              NULL);
    283   GNUNET_free (path);
    284   eh = ANASTASIS_curl_easy_get_ (plo->url);
    285   if (NULL == eh)
    286   {
    287     GNUNET_break (0);
    288     GNUNET_free (plo->url);
    289     GNUNET_free (plo);
    290     return NULL;
    291   }
    292   GNUNET_assert (CURLE_OK ==
    293                  curl_easy_setopt (eh,
    294                                    CURLOPT_HEADERFUNCTION,
    295                                    &handle_header));
    296   GNUNET_assert (CURLE_OK ==
    297                  curl_easy_setopt (eh,
    298                                    CURLOPT_HEADERDATA,
    299                                    plo));
    300   plo->cb = cb;
    301   plo->cb_cls = cb_cls;
    302   plo->job = GNUNET_CURL_job_add_raw (ctx,
    303                                       eh,
    304                                       NULL,
    305                                       &handle_policy_lookup_finished,
    306                                       plo);
    307   return plo;
    308 }
    309 
    310 
    311 struct ANASTASIS_PolicyLookupOperation *
    312 ANASTASIS_policy_lookup_version (
    313   struct GNUNET_CURL_Context *ctx,
    314   const char *backend_url,
    315   const struct ANASTASIS_CRYPTO_AccountPublicKeyP *anastasis_pub,
    316   ANASTASIS_PolicyLookupCallback cb,
    317   void *cb_cls,
    318   unsigned int version)
    319 {
    320   struct ANASTASIS_PolicyLookupOperation *plo;
    321   CURL *eh;
    322   char *acc_pub_str;
    323   char *path;
    324   char version_s[14];
    325 
    326   GNUNET_assert (NULL != cb);
    327   plo = GNUNET_new (struct ANASTASIS_PolicyLookupOperation);
    328   plo->account_pub = *anastasis_pub;
    329   acc_pub_str = GNUNET_STRINGS_data_to_string_alloc (anastasis_pub,
    330                                                      sizeof (*anastasis_pub));
    331   GNUNET_asprintf (&path,
    332                    "policy/%s",
    333                    acc_pub_str);
    334   GNUNET_free (acc_pub_str);
    335   GNUNET_snprintf (version_s,
    336                    sizeof (version_s),
    337                    "%u",
    338                    version);
    339   plo->url = TALER_url_join (backend_url,
    340                              path,
    341                              "version",
    342                              version_s,
    343                              NULL);
    344   GNUNET_free (path);
    345   eh = ANASTASIS_curl_easy_get_ (plo->url);
    346   GNUNET_assert (CURLE_OK ==
    347                  curl_easy_setopt (eh,
    348                                    CURLOPT_HEADERFUNCTION,
    349                                    &handle_header));
    350   GNUNET_assert (CURLE_OK ==
    351                  curl_easy_setopt (eh,
    352                                    CURLOPT_HEADERDATA,
    353                                    plo));
    354   plo->cb = cb;
    355   plo->cb_cls = cb_cls;
    356   plo->job = GNUNET_CURL_job_add_raw (ctx,
    357                                       eh,
    358                                       NULL,
    359                                       &handle_policy_lookup_finished,
    360                                       plo);
    361   return plo;
    362 }