challenger

OAuth 2.0-based authentication service that validates user can receive messages at a certain address
Log | Files | Refs | Submodules | README | LICENSE

challenger-httpd_setup.c (8576B)


      1 /*
      2   This file is part of Challenger
      3   Copyright (C) 2023, 2024 Taler Systems SA
      4 
      5   Challenger 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   Challenger 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   Challenger; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
     15 */
     16 /**
     17  * @file challenger-httpd_setup.c
     18  * @brief functions to handle incoming requests for setups
     19  * @author Christian Grothoff
     20  */
     21 #include "platform.h"
     22 #include "challenger-httpd.h"
     23 #include <gnunet/gnunet_util_lib.h>
     24 #include <gnunet/gnunet_db_lib.h>
     25 #include "challenger-httpd_setup.h"
     26 #include "challenger-httpd_common.h"
     27 #include "challenger-database/setup_nonce.h"
     28 #include "challenger-database/client_check.h"
     29 
     30 
     31 /**
     32  * Maximum number of retries for the database interaction.
     33  */
     34 #define MAX_RETRIES 3
     35 
     36 /**
     37  * Set to 1 to dump addresses into the log.
     38  */
     39 #define DEBUG 0
     40 
     41 struct SetupContext
     42 {
     43 
     44   /**
     45    * Our handler context.
     46    */
     47   struct CH_HandlerContext *hc;
     48 
     49   /**
     50    * Opaque parsing context.
     51    */
     52   void *opaque_post_parsing_context;
     53 
     54   /**
     55    * Uploaded JSON data, NULL if upload is not yet complete.
     56    */
     57   json_t *root;
     58 
     59 };
     60 
     61 
     62 /**
     63  * Callback to clean up the request context.
     64  *
     65  * @param[in,out] ctx a `struct SetupContext` to clean up
     66  */
     67 static void
     68 request_done (void *ctx)
     69 {
     70   struct SetupContext *sc = ctx;
     71 
     72   if (NULL == ctx)
     73     return;
     74   if (NULL != sc->root)
     75   {
     76     json_decref (sc->root);
     77     sc->root = NULL;
     78   }
     79   TALER_MHD_parse_post_cleanup_callback (sc->opaque_post_parsing_context);
     80   GNUNET_free (sc);
     81 }
     82 
     83 
     84 enum MHD_Result
     85 CH_handler_setup (struct CH_HandlerContext *hc,
     86                   const char *upload_data,
     87                   size_t *upload_data_size)
     88 {
     89   struct SetupContext *sc = hc->ctx;
     90   unsigned long long client_id;
     91   const char *client_secret;
     92   const char *cl;
     93 
     94   if (NULL == sc)
     95   {
     96     /* Fresh request, do initial setup */
     97     sc = GNUNET_new (struct SetupContext);
     98     sc->hc = hc;
     99     hc->cc = &request_done;
    100     hc->ctx = sc;
    101     return MHD_YES;
    102   }
    103   cl = MHD_lookup_connection_value (hc->connection,
    104                                     MHD_HEADER_KIND,
    105                                     MHD_HTTP_HEADER_CONTENT_LENGTH);
    106   /* If we have a body (Content-length set and not zero) and we
    107      are not yet done parsing it, try to parse it! */
    108   if ( (NULL == sc->root) &&
    109        (NULL != cl) &&
    110        (0 != strcmp ("0",
    111                      cl)) )
    112   {
    113     /* parse byte stream upload into JSON */
    114     enum GNUNET_GenericReturnValue res;
    115 
    116 #if DEBUG
    117     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    118                 "Handling upload of `%.*s' (%s)\n",
    119                 (int) *upload_data_size,
    120                 upload_data,
    121                 cl);
    122 #endif
    123     res = TALER_MHD_parse_post_json (hc->connection,
    124                                      &sc->opaque_post_parsing_context,
    125                                      upload_data,
    126                                      upload_data_size,
    127                                      &sc->root);
    128     if (GNUNET_SYSERR == res)
    129     {
    130       GNUNET_assert (NULL == sc->root);
    131       return MHD_NO; /* bad upload, could not even generate error */
    132     }
    133     if ( (GNUNET_NO == res) ||
    134          (NULL == sc->root) )
    135     {
    136       GNUNET_assert (NULL == sc->root);
    137       return MHD_YES; /* so far incomplete upload or parser error */
    138     }
    139     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    140                 "Address data uploaded in /setup\n");
    141 #if DEBUG
    142     json_dumpf (sc->root,
    143                 stderr,
    144                 JSON_INDENT (2));
    145 #endif
    146   }
    147 
    148   {
    149     char dummy;
    150 
    151     if (1 != sscanf (hc->path,
    152                      "%llu%c",
    153                      &client_id,
    154                      &dummy))
    155     {
    156       GNUNET_break_op (0);
    157       return TALER_MHD_reply_with_error (hc->connection,
    158                                          MHD_HTTP_BAD_REQUEST,
    159                                          TALER_EC_GENERIC_PARAMETER_MALFORMED,
    160                                          "client_id");
    161     }
    162   }
    163   client_secret = CH_get_client_secret (hc->connection);
    164   if (NULL == client_secret)
    165   {
    166     GNUNET_break_op (0);
    167     return TALER_MHD_reply_with_error (hc->connection,
    168                                        MHD_HTTP_BAD_REQUEST,
    169                                        TALER_EC_GENERIC_PARAMETER_MISSING,
    170                                        MHD_HTTP_HEADER_AUTHORIZATION);
    171   }
    172   {
    173     const json_t *ro;
    174 
    175     ro = json_object_get (sc->root,
    176                           "read_only");
    177     if ( (NULL != ro) &&
    178          (! json_is_boolean (ro)) )
    179     {
    180       GNUNET_break_op (0);
    181       return TALER_MHD_reply_with_error (hc->connection,
    182                                          MHD_HTTP_BAD_REQUEST,
    183                                          TALER_EC_GENERIC_PARAMETER_MALFORMED,
    184                                          "read_only");
    185     }
    186   }
    187   for (unsigned int r = 0; r<MAX_RETRIES; r++)
    188   {
    189     enum GNUNET_DB_QueryStatus qs;
    190     char *client_url = NULL;
    191 
    192     qs = CHALLENGERDB_client_check (CH_context,
    193                                     (uint64_t) client_id,
    194                                     client_secret,
    195                                     1,
    196                                     &client_url);
    197     switch (qs)
    198     {
    199     case GNUNET_DB_STATUS_HARD_ERROR:
    200       GNUNET_break (0);
    201       return TALER_MHD_reply_with_error (hc->connection,
    202                                          MHD_HTTP_INTERNAL_SERVER_ERROR,
    203                                          TALER_EC_GENERIC_DB_FETCH_FAILED,
    204                                          NULL);
    205     case GNUNET_DB_STATUS_SOFT_ERROR:
    206       if (r < MAX_RETRIES - 1)
    207         continue;
    208       GNUNET_break (0);
    209       return TALER_MHD_reply_with_error (hc->connection,
    210                                          MHD_HTTP_INTERNAL_SERVER_ERROR,
    211                                          TALER_EC_GENERIC_DB_FETCH_FAILED,
    212                                          NULL);
    213     case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
    214       return TALER_MHD_reply_with_error (hc->connection,
    215                                          MHD_HTTP_NOT_FOUND,
    216                                          TALER_EC_CHALLENGER_GENERIC_CLIENT_UNKNOWN,
    217                                          NULL);
    218     case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
    219       break;
    220     }
    221     GNUNET_free (client_url);
    222     break;
    223   }
    224 
    225   {
    226     struct CHALLENGER_ValidationNonceP nonce;
    227     struct GNUNET_TIME_Absolute expiration_time;
    228     enum GNUNET_DB_QueryStatus qs;
    229 
    230     expiration_time = GNUNET_TIME_relative_to_absolute (CH_validation_duration);
    231     GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_NONCE,
    232                                 &nonce,
    233                                 sizeof (nonce));
    234     qs = CHALLENGERDB_setup_nonce (CH_context,
    235                                    client_id,
    236                                    &nonce,
    237                                    expiration_time,
    238                                    sc->root);
    239     switch (qs)
    240     {
    241     case GNUNET_DB_STATUS_HARD_ERROR:
    242     case GNUNET_DB_STATUS_SOFT_ERROR:
    243       GNUNET_break (0);
    244       return TALER_MHD_reply_with_error (hc->connection,
    245                                          MHD_HTTP_INTERNAL_SERVER_ERROR,
    246                                          TALER_EC_GENERIC_DB_STORE_FAILED,
    247                                          "setup_nonce");
    248     case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
    249       GNUNET_break (0);
    250       return TALER_MHD_reply_with_error (hc->connection,
    251                                          MHD_HTTP_INTERNAL_SERVER_ERROR,
    252                                          TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE,
    253                                          "no results from setup_nonce");
    254     case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
    255       break;
    256     }
    257     {
    258       char *nstr;
    259 
    260       nstr = GNUNET_STRINGS_data_to_string_alloc (&nonce,
    261                                                   sizeof (nonce));
    262       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    263                   "/setup returns nonce `%s'\n",
    264                   nstr);
    265       GNUNET_free (nstr);
    266     }
    267     return TALER_MHD_REPLY_JSON_PACK (
    268       hc->connection,
    269       MHD_HTTP_OK,
    270       GNUNET_JSON_pack_data_auto ("nonce",
    271                                   &nonce));
    272   }
    273 }