anastasis

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

anastasis_authorization_plugin_file.c (10371B)


      1 /*
      2   This file is part of Anastasis
      3   Copyright (C) 2019 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.GPL.  If not, see <http://www.gnu.org/licenses/>
     15 */
     16 /**
     17  * @file anastasis_authorization_plugin_file.c
     18  * @brief authorization plugin file based for testing
     19  * @author Dominik Meister
     20  */
     21 #include "platform.h"
     22 #include "anastasis_authorization_plugin.h"
     23 #include <taler/taler_mhd_lib.h>
     24 #include <gnunet/gnunet_db_lib.h>
     25 #include "anastasis_database_lib.h"
     26 
     27 /**
     28  * How many retries do we allow per code?
     29  */
     30 #define INITIAL_RETRY_COUNTER 3
     31 
     32 
     33 /**
     34  * Saves the state of a authorization process
     35  */
     36 struct ANASTASIS_AUTHORIZATION_State
     37 {
     38   /**
     39    * UUID of the challenge which is authorised
     40    */
     41   struct ANASTASIS_CRYPTO_TruthUUIDP truth_uuid;
     42 
     43   /**
     44    * Code which is sent to the user (here saved into a file)
     45    */
     46   uint64_t code;
     47 
     48   /**
     49    * holds the truth information
     50    */
     51   char *filename;
     52 
     53   /**
     54    * closure
     55    */
     56   void *cls;
     57 };
     58 
     59 
     60 /**
     61  * Validate @a data is a well-formed input into the challenge method,
     62  * i.e. @a data is a well-formed phone number for sending an SMS, or
     63  * a well-formed e-mail address for sending an e-mail. Not expected to
     64  * check that the phone number or e-mail account actually exists.
     65  *
     66  * To be possibly used before issuing a 402 payment required to the client.
     67  *
     68  * @param cls closure with a `const struct ANASTASIS_AuthorizationContext *`
     69  * @param connection HTTP client request (for queuing response)
     70  * @param truth_mime mime type of @e data
     71  * @param data input to validate (i.e. is it a valid phone number, etc.)
     72  * @param data_length number of bytes in @a data
     73  * @return #GNUNET_OK if @a data is valid,
     74  *         #GNUNET_NO if @a data is invalid and a reply was successfully queued on @a connection
     75  *         #GNUNET_SYSERR if @a data invalid but we failed to queue a reply on @a connection
     76  */
     77 static enum GNUNET_GenericReturnValue
     78 file_validate (void *cls,
     79                struct MHD_Connection *connection,
     80                const char *truth_mime,
     81                const char *data,
     82                size_t data_length)
     83 {
     84   char *filename;
     85   bool flag;
     86 
     87   (void) cls;
     88   if (NULL == data)
     89     return GNUNET_SYSERR;
     90   filename = GNUNET_STRINGS_data_to_string_alloc (data,
     91                                                   data_length);
     92   flag = false;
     93   for (size_t i = 0; i<strlen (filename); i++)
     94   {
     95     if ( (filename[i] == ' ') ||
     96          (filename[i] == '/') )
     97     {
     98       flag = true;
     99       break;
    100     }
    101   }
    102   if (flag)
    103     return GNUNET_SYSERR;
    104   GNUNET_free (filename);
    105   return GNUNET_OK;
    106 }
    107 
    108 
    109 /**
    110  * Begin issuing authentication challenge to user based on @a data.
    111  * I.e. start to send SMS or e-mail or launch video identification.
    112  *
    113  * @param cls closure
    114  * @param trigger function to call when we made progress
    115  * @param trigger_cls closure for @a trigger
    116  * @param truth_uuid Identifier of the challenge, to be (if possible) included in the
    117  *             interaction with the user
    118  * @param code secret code that the user has to provide back to satisfy the challenge in
    119  *             the main anastasis protocol
    120  * @param data input to validate (i.e. is it a valid phone number, etc.)
    121  * @param data_length number of bytes in @a data
    122  * @return state to track progress on the authorization operation, NULL on failure
    123  */
    124 static struct ANASTASIS_AUTHORIZATION_State *
    125 file_start (void *cls,
    126             GNUNET_SCHEDULER_TaskCallback trigger,
    127             void *trigger_cls,
    128             const struct ANASTASIS_CRYPTO_TruthUUIDP *truth_uuid,
    129             uint64_t code,
    130             const void *data,
    131             size_t data_length)
    132 {
    133   const struct ANASTASIS_AuthorizationContext *ac = cls;
    134   struct ANASTASIS_AUTHORIZATION_State *as;
    135   enum GNUNET_DB_QueryStatus qs;
    136 
    137   /* If the user can show this challenge code, this
    138      plugin is already happy (no additional
    139      requirements), so mark this challenge as
    140      already satisfied from the start. */
    141   qs = ac->db->mark_challenge_code_satisfied (ac->db->cls,
    142                                               truth_uuid,
    143                                               code);
    144   if (qs <= 0)
    145   {
    146     GNUNET_break (0);
    147     return NULL;
    148   }
    149   as = GNUNET_new (struct ANASTASIS_AUTHORIZATION_State);
    150   as->cls = cls;
    151   as->truth_uuid = *truth_uuid;
    152   as->code = code;
    153   as->filename = GNUNET_strndup (data,
    154                                  data_length);
    155   return as;
    156 }
    157 
    158 
    159 /**
    160  * Begin issuing authentication challenge to user based on @a data.
    161  * I.e. start to send SMS or e-mail or launch video identification.
    162  *
    163  * @param as authorization state
    164  * @param connection HTTP client request (for queuing response, such as redirection to video portal)
    165  * @return state of the request
    166  */
    167 static enum ANASTASIS_AUTHORIZATION_ChallengeResult
    168 file_challenge (struct ANASTASIS_AUTHORIZATION_State *as,
    169                 struct MHD_Connection *connection)
    170 {
    171   const char *mime;
    172   const char *lang;
    173 
    174   mime = MHD_lookup_connection_value (connection,
    175                                       MHD_HEADER_KIND,
    176                                       MHD_HTTP_HEADER_ACCEPT);
    177   if (NULL == mime)
    178     mime = "text/plain";
    179   lang = MHD_lookup_connection_value (connection,
    180                                       MHD_HEADER_KIND,
    181                                       MHD_HTTP_HEADER_ACCEPT_LANGUAGE);
    182   if (NULL == lang)
    183     lang = "en";
    184   {
    185     FILE *f = fopen (as->filename, "w");
    186 
    187     if (NULL == f)
    188     {
    189       struct MHD_Response *resp;
    190       MHD_RESULT mres;
    191 
    192       GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
    193                                 "open",
    194                                 as->filename);
    195       resp = TALER_MHD_make_error (TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE,
    196                                    "open");
    197       mres = MHD_queue_response (connection,
    198                                  MHD_HTTP_INTERNAL_SERVER_ERROR,
    199                                  resp);
    200       MHD_destroy_response (resp);
    201       if (MHD_YES != mres)
    202         return ANASTASIS_AUTHORIZATION_CRES_FAILED_REPLY_FAILED;
    203       return ANASTASIS_AUTHORIZATION_CRES_FAILED;
    204     }
    205 
    206     /* print challenge code to file */
    207     if (0 >= fprintf (f,
    208                       "%lu",
    209                       as->code))
    210     {
    211       struct MHD_Response *resp;
    212       MHD_RESULT mres;
    213 
    214       GNUNET_break (0 == fclose (f));
    215       resp = TALER_MHD_make_error (TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE,
    216                                    "write");
    217       mres = MHD_queue_response (connection,
    218                                  MHD_HTTP_INTERNAL_SERVER_ERROR,
    219                                  resp);
    220       MHD_destroy_response (resp);
    221       if (MHD_YES != mres)
    222         return ANASTASIS_AUTHORIZATION_CRES_FAILED_REPLY_FAILED;
    223       return ANASTASIS_AUTHORIZATION_CRES_FAILED;
    224     }
    225     GNUNET_break (0 == fclose (f));
    226   }
    227 
    228   /* Build HTTP response */
    229   {
    230     struct MHD_Response *resp;
    231 
    232     if (0.0 < TALER_pattern_matches (mime,
    233                                      "application/json"))
    234     {
    235       resp = TALER_MHD_MAKE_JSON_PACK (
    236         GNUNET_JSON_pack_string ("challenge_type",
    237                                  "FILE_WRITTEN"),
    238         GNUNET_JSON_pack_string ("filename",
    239                                  as->filename));
    240     }
    241     else
    242     {
    243       size_t response_size;
    244       char *response;
    245 
    246       response_size = GNUNET_asprintf (&response,
    247                                        _ ("Challenge written to file"));
    248       resp = MHD_create_response_from_buffer (response_size,
    249                                               response,
    250                                               MHD_RESPMEM_MUST_COPY);
    251       GNUNET_free (response);
    252       TALER_MHD_add_global_headers (resp,
    253                                     false);
    254       GNUNET_break (MHD_YES ==
    255                     MHD_add_response_header (resp,
    256                                              MHD_HTTP_HEADER_CONTENT_TYPE,
    257                                              "text/plain"));
    258     }
    259 
    260     {
    261       MHD_RESULT mres;
    262 
    263       mres = MHD_queue_response (connection,
    264                                  MHD_HTTP_OK,
    265                                  resp);
    266       MHD_destroy_response (resp);
    267       if (MHD_YES != mres)
    268         return ANASTASIS_AUTHORIZATION_CRES_SUCCESS_REPLY_FAILED;
    269       return ANASTASIS_AUTHORIZATION_CRES_SUCCESS;
    270     }
    271   }
    272 }
    273 
    274 
    275 /**
    276  * Free internal state associated with @a as.
    277  *
    278  * @param as state to clean up
    279  */
    280 static void
    281 file_cleanup (struct ANASTASIS_AUTHORIZATION_State *as)
    282 {
    283   GNUNET_free (as->filename);
    284   GNUNET_free (as);
    285 }
    286 
    287 
    288 /**
    289  * Initialize File based authorization plugin
    290  *
    291  * @param cls a configuration instance
    292  * @return NULL on error, otherwise a `struct ANASTASIS_AuthorizationPlugin`
    293  */
    294 void *
    295 libanastasis_plugin_authorization_file_init (void *cls);
    296 
    297 /* declaration to fix compiler warning */
    298 void *
    299 libanastasis_plugin_authorization_file_init (void *cls)
    300 {
    301   const struct ANASTASIS_AuthorizationContext *ac = cls;
    302   struct ANASTASIS_AuthorizationPlugin *plugin;
    303 
    304   plugin = GNUNET_new (struct ANASTASIS_AuthorizationPlugin);
    305   plugin->cls = (void *) ac;
    306   plugin->retry_counter = INITIAL_RETRY_COUNTER;
    307   plugin->code_validity_period = GNUNET_TIME_UNIT_MINUTES;
    308   plugin->code_rotation_period = GNUNET_TIME_UNIT_MINUTES;
    309   plugin->code_retransmission_frequency = GNUNET_TIME_UNIT_MINUTES;
    310   plugin->validate = &file_validate;
    311   plugin->start = &file_start;
    312   plugin->challenge = &file_challenge;
    313   plugin->cleanup = &file_cleanup;
    314   return plugin;
    315 }
    316 
    317 
    318 /**
    319  * Unload authorization plugin
    320  *
    321  * @param cls a `struct ANASTASIS_AuthorizationPlugin`
    322  * @return NULL (always)
    323  */
    324 void *
    325 libanastasis_plugin_authorization_file_done (void *cls);
    326 
    327 /* declaration to fix compiler warning */
    328 void *
    329 libanastasis_plugin_authorization_file_done (void *cls)
    330 {
    331   struct ANASTASIS_AuthorizationPlugin *plugin = cls;
    332 
    333   GNUNET_free (plugin);
    334   return NULL;
    335 }