anastasis

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

anastasis-httpd_policy.c (9561B)


      1 /*
      2   This file is part of Anastasis
      3   Copyright (C) 2019, 2021 Anastasis SARL
      4 
      5   Anastasis is free software; you can redistribute it and/or modify it under the
      6   terms of the GNU Affero 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 Affero General Public License for more details.
     12 
     13   You should have received a copy of the GNU Affero General Public License along with
     14   Anastasis; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
     15 */
     16 /**
     17  * @file anastasis-httpd_policy.c
     18  * @brief functions to handle incoming requests on /policy/
     19  * @author Dennis Neufeld
     20  * @author Dominik Meister
     21  * @author Christian Grothoff
     22  */
     23 #include "platform.h"
     24 #include "anastasis-httpd.h"
     25 #include "anastasis-httpd_policy.h"
     26 #include "anastasis_service.h"
     27 #include <gnunet/gnunet_util_lib.h>
     28 #include <gnunet/gnunet_rest_lib.h>
     29 #include <taler/taler_json_lib.h>
     30 #include <taler/taler_merchant_service.h>
     31 #include <taler/taler_signatures.h>
     32 
     33 
     34 /**
     35  * Return the current recoverydocument of @a account on @a connection.
     36  *
     37  * @param connection MHD connection to use
     38  * @param account_pub account to query
     39  * @return MHD result code
     40  */
     41 static MHD_RESULT
     42 return_policy (struct MHD_Connection *connection,
     43                const struct ANASTASIS_CRYPTO_AccountPublicKeyP *account_pub)
     44 {
     45   enum GNUNET_DB_QueryStatus qs;
     46   struct MHD_Response *resp;
     47   struct ANASTASIS_AccountSignatureP account_sig;
     48   struct GNUNET_HashCode recovery_data_hash;
     49   const char *version_s;
     50   char version_b[14];
     51   uint32_t version;
     52   void *res_recovery_data;
     53   size_t res_recovery_data_size;
     54 
     55   version_s = MHD_lookup_connection_value (connection,
     56                                            MHD_GET_ARGUMENT_KIND,
     57                                            "version");
     58   if (NULL != version_s)
     59   {
     60     char dummy;
     61 
     62     if (1 != sscanf (version_s,
     63                      "%u%c",
     64                      &version,
     65                      &dummy))
     66     {
     67       return TALER_MHD_reply_with_error (connection,
     68                                          MHD_HTTP_BAD_REQUEST,
     69                                          TALER_EC_GENERIC_PARAMETER_MALFORMED,
     70                                          "version");
     71     }
     72     qs = db->get_recovery_document (db->cls,
     73                                     account_pub,
     74                                     version,
     75                                     &account_sig,
     76                                     &recovery_data_hash,
     77                                     &res_recovery_data_size,
     78                                     &res_recovery_data);
     79   }
     80   else
     81   {
     82     qs = db->get_latest_recovery_document (db->cls,
     83                                            account_pub,
     84                                            &account_sig,
     85                                            &recovery_data_hash,
     86                                            &res_recovery_data_size,
     87                                            &res_recovery_data,
     88                                            &version);
     89     GNUNET_snprintf (version_b,
     90                      sizeof (version_b),
     91                      "%u",
     92                      (unsigned int) version);
     93     version_s = version_b;
     94   }
     95   switch (qs)
     96   {
     97   case GNUNET_DB_STATUS_HARD_ERROR:
     98     GNUNET_break (0);
     99     return TALER_MHD_reply_with_error (connection,
    100                                        MHD_HTTP_INTERNAL_SERVER_ERROR,
    101                                        TALER_EC_GENERIC_DB_FETCH_FAILED,
    102                                        "get_recovery_document");
    103   case GNUNET_DB_STATUS_SOFT_ERROR:
    104     GNUNET_break (0);
    105     return TALER_MHD_reply_with_error (connection,
    106                                        MHD_HTTP_INTERNAL_SERVER_ERROR,
    107                                        TALER_EC_GENERIC_DB_SOFT_FAILURE,
    108                                        "get_recovery_document");
    109   case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
    110     return TALER_MHD_reply_with_error (connection,
    111                                        MHD_HTTP_NOT_FOUND,
    112                                        TALER_EC_ANASTASIS_POLICY_NOT_FOUND,
    113                                        NULL);
    114   case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
    115     /* interesting case below */
    116     break;
    117   }
    118   resp = MHD_create_response_from_buffer (res_recovery_data_size,
    119                                           res_recovery_data,
    120                                           MHD_RESPMEM_MUST_FREE);
    121   TALER_MHD_add_global_headers (resp,
    122                                 false);
    123   {
    124     char *sig_s;
    125     char *etag;
    126     char *etagq;
    127 
    128     sig_s = GNUNET_STRINGS_data_to_string_alloc (&account_sig,
    129                                                  sizeof (account_sig));
    130     GNUNET_break (MHD_YES ==
    131                   MHD_add_response_header (resp,
    132                                            ANASTASIS_HTTP_HEADER_POLICY_SIGNATURE,
    133                                            sig_s));
    134     GNUNET_free (sig_s);
    135     GNUNET_break (MHD_YES ==
    136                   MHD_add_response_header (resp,
    137                                            ANASTASIS_HTTP_HEADER_POLICY_VERSION,
    138                                            version_s));
    139     etag = GNUNET_STRINGS_data_to_string_alloc (&recovery_data_hash,
    140                                                 sizeof (recovery_data_hash));
    141     GNUNET_asprintf (&etagq,
    142                      "\"%s\"",
    143                      etag);
    144     GNUNET_free (etag);
    145     GNUNET_break (MHD_YES ==
    146                   MHD_add_response_header (resp,
    147                                            MHD_HTTP_HEADER_ETAG,
    148                                            etagq));
    149     GNUNET_free (etagq);
    150   }
    151   {
    152     MHD_RESULT ret;
    153 
    154     ret = MHD_queue_response (connection,
    155                               MHD_HTTP_OK,
    156                               resp);
    157     MHD_destroy_response (resp);
    158     return ret;
    159   }
    160 }
    161 
    162 
    163 MHD_RESULT
    164 AH_policy_get (struct MHD_Connection *connection,
    165                const struct ANASTASIS_CRYPTO_AccountPublicKeyP *account_pub)
    166 {
    167   struct GNUNET_HashCode recovery_data_hash;
    168   enum ANASTASIS_DB_AccountStatus as;
    169   MHD_RESULT ret;
    170   uint32_t version;
    171   struct GNUNET_TIME_Timestamp expiration;
    172 
    173   as = db->lookup_account (db->cls,
    174                            account_pub,
    175                            &expiration,
    176                            &recovery_data_hash,
    177                            &version);
    178   switch (as)
    179   {
    180   case ANASTASIS_DB_ACCOUNT_STATUS_PAYMENT_REQUIRED:
    181     return TALER_MHD_reply_with_error (connection,
    182                                        MHD_HTTP_NOT_FOUND,
    183                                        TALER_EC_SYNC_ACCOUNT_UNKNOWN,
    184                                        NULL);
    185   case ANASTASIS_DB_ACCOUNT_STATUS_HARD_ERROR:
    186     GNUNET_break (0);
    187     return TALER_MHD_reply_with_error (connection,
    188                                        MHD_HTTP_INTERNAL_SERVER_ERROR,
    189                                        TALER_EC_GENERIC_DB_FETCH_FAILED,
    190                                        "lookup account");
    191   case ANASTASIS_DB_ACCOUNT_STATUS_NO_RESULTS:
    192     {
    193       struct MHD_Response *resp;
    194 
    195       resp = MHD_create_response_from_buffer (0,
    196                                               NULL,
    197                                               MHD_RESPMEM_PERSISTENT);
    198       TALER_MHD_add_global_headers (resp,
    199                                     false);
    200       ret = MHD_queue_response (connection,
    201                                 MHD_HTTP_NO_CONTENT,
    202                                 resp);
    203       MHD_destroy_response (resp);
    204     }
    205     return ret;
    206   case ANASTASIS_DB_ACCOUNT_STATUS_VALID_HASH_RETURNED:
    207     {
    208       const char *inm;
    209 
    210       inm = MHD_lookup_connection_value (connection,
    211                                          MHD_HEADER_KIND,
    212                                          MHD_HTTP_HEADER_IF_NONE_MATCH);
    213       if ( (NULL != inm) &&
    214            (2 < strlen (inm)) &&
    215            ('"' == inm[0]) &&
    216            ('"' == inm[strlen (inm) - 1]) )
    217       {
    218         struct GNUNET_HashCode inm_h;
    219 
    220         if (GNUNET_OK !=
    221             GNUNET_STRINGS_string_to_data (inm + 1,
    222                                            strlen (inm) - 2,
    223                                            &inm_h,
    224                                            sizeof (inm_h)))
    225         {
    226           GNUNET_break_op (0);
    227           return TALER_MHD_reply_with_error (connection,
    228                                              MHD_HTTP_BAD_REQUEST,
    229                                              TALER_EC_ANASTASIS_POLICY_BAD_IF_NONE_MATCH,
    230                                              "Etag must be a base32-encoded SHA-512 hash");
    231         }
    232         if (0 == GNUNET_memcmp (&inm_h,
    233                                 &recovery_data_hash))
    234         {
    235           struct MHD_Response *resp;
    236 
    237           resp = MHD_create_response_from_buffer (0,
    238                                                   NULL,
    239                                                   MHD_RESPMEM_PERSISTENT);
    240           TALER_MHD_add_global_headers (resp,
    241                                         false);
    242           ret = MHD_queue_response (connection,
    243                                     MHD_HTTP_NOT_MODIFIED,
    244                                     resp);
    245           MHD_destroy_response (resp);
    246           return ret;
    247         }
    248       }
    249     }
    250     /* We have a result, should fetch and return it! */
    251     break;
    252   }
    253   return return_policy (connection,
    254                         account_pub);
    255 }