frosix

Multiparty signature service (experimental)
Log | Files | Refs | README | LICENSE

frosix-httpd.c (35943B)


      1 /*
      2   This file is part of Frosix
      3   (C) 2020-2022 Anastasis SARL
      4 
      5   Frosix 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   Frosix 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   Frosix; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
     15 */
     16 /**
     17  * @file backend/frosix-httpd.c
     18  * @brief HTTP serving layer intended to provide basic backup operations
     19  * @author Christian Grothoff
     20  * @author Dennis Neufeld
     21  * @author Dominik Meister
     22  * @author Joel Urech
     23  */
     24 #include "platform.h"
     25 #include "frosix-httpd.h"
     26 #include "frosix-httpd_mhd.h"
     27 #include "frosix-httpd_terms.h"
     28 #include "frosix-httpd_config.h"
     29 #include "frosix-httpd_seed.h"
     30 #include "frosix-httpd_dkg.h"
     31 #include "frosix-httpd_sig.h"
     32 #include "frosix-httpd_auth.h"
     33 #include "frosix_database_lib.h"
     34 #include "frosix_util_lib.h"
     35 #include "gnunet/gnunet_curl_lib.h"
     36 #include "taler/taler_util.h"
     37 
     38 
     39 /**
     40  * Annual fee for the backup account.
     41  */
     42 struct TALER_Amount FH_annual_fee;
     43 
     44 /**
     45  * Fee for a truth upload.
     46  */
     47 struct TALER_Amount FH_signature_creation_fee;
     48 
     49 /**
     50  * Amount of insurance.
     51  */
     52 // struct TALER_Amount FH_insurance;
     53 
     54 /**
     55  * Cost for secure question truth download.
     56  */
     57 struct TALER_Amount FH_question_cost;
     58 
     59 /**
     60  * Our configuration.
     61  */
     62 const struct GNUNET_CONFIGURATION_Handle *FH_cfg;
     63 
     64 /**
     65  * Our Taler backend to process payments.
     66  */
     67 char *FH_backend_url;
     68 
     69 /**
     70  * Our fulfillment URL.
     71  */
     72 char *FH_fulfillment_url;
     73 
     74 /**
     75  * Our business name.
     76  */
     77 char *FH_business_name;
     78 
     79 /**
     80  * Our provider salt.
     81  */
     82 struct FROSIX_ProviderSaltP FH_provider_salt;
     83 
     84 /**
     85  * Our secret provider salt
     86 */
     87 struct FROSIX_SecretProviderSaltP FH_secret_provider_salt;
     88 
     89 /**
     90  * FIXME
     91 */
     92 struct GNUNET_CRYPTO_EddsaPrivateKey FH_priv_sig_key;
     93 
     94 /**
     95  * FIXME
     96 */
     97 struct GNUNET_CRYPTO_EddsaPublicKey FH_pub_sig_key;
     98 
     99 /**
    100  * Number of policy uploads permitted per annual fee payment.
    101  */
    102 unsigned long long FH_post_counter = 64LLU;
    103 
    104 /**
    105  * Our context for making HTTP requests.
    106  */
    107 struct GNUNET_CURL_Context *FH_ctx;
    108 
    109 /**
    110  * Should a "Connection: close" header be added to each HTTP response?
    111  */
    112 static int FH_connection_close;
    113 
    114 /**
    115  * Task running the HTTP server.
    116  */
    117 static struct GNUNET_SCHEDULER_Task *mhd_task;
    118 
    119 /**
    120  * Heap for processing timeouts of requests.
    121  */
    122 struct GNUNET_CONTAINER_Heap *FH_to_heap;
    123 
    124 /**
    125  * Global return code
    126  */
    127 static int global_result;
    128 
    129 /**
    130  * The MHD Daemon
    131  */
    132 static struct MHD_Daemon *mhd;
    133 
    134 /**
    135  * Connection handle to the our database
    136  */
    137 struct FROSIX_DatabasePlugin *db;
    138 
    139 /**
    140  * Reschedule context for #FH_ctx.
    141  */
    142 static struct GNUNET_CURL_RescheduleContext *rc;
    143 
    144 /**
    145  * Set if we should immediately MHD_run() again.
    146  */
    147 static int triggered;
    148 
    149 /**
    150  * Username and password to use for client authentication
    151  * (optional).
    152  */
    153 static char *userpass;
    154 
    155 /**
    156  * Type of the client's TLS certificate (optional).
    157  */
    158 static char *certtype;
    159 
    160 /**
    161  * File with the client's TLS certificate (optional).
    162  */
    163 static char *certfile;
    164 
    165 /**
    166  * File with the client's TLS private key (optional).
    167  */
    168 static char *keyfile;
    169 
    170 /**
    171  * This value goes in the Authorization:-header.
    172  */
    173 static char *apikey;
    174 
    175 /**
    176  * Passphrase to decrypt client's TLS private key file (optional).
    177  */
    178 static char *keypass;
    179 
    180 
    181 /**
    182  * Function that queries MHD's select sets and
    183  * starts the task waiting for them.
    184  */
    185 static struct GNUNET_SCHEDULER_Task *
    186 prepare_daemon (void);
    187 
    188 
    189 /**
    190  * Call MHD to process pending requests and then go back
    191  * and schedule the next run.
    192  *
    193  * @param cls the `struct MHD_Daemon` of the HTTP server to run
    194  */
    195 static void
    196 run_daemon (void *cls)
    197 {
    198   (void) cls;
    199   mhd_task = NULL;
    200   do {
    201     triggered = 0;
    202     GNUNET_assert (MHD_YES == MHD_run (mhd));
    203   } while (0 != triggered);
    204   mhd_task = prepare_daemon ();
    205 }
    206 
    207 
    208 /**
    209  * Kick MHD to run now, to be called after MHD_resume_connection().
    210  * Basically, we need to explicitly resume MHD's event loop whenever
    211  * we made progress serving a request.  This function re-schedules
    212  * the task processing MHD's activities to run immediately.
    213  *
    214  * @param cls NULL
    215  */
    216 void
    217 FH_trigger_daemon (void *cls)
    218 {
    219   (void) cls;
    220   if (NULL != mhd_task)
    221   {
    222     GNUNET_SCHEDULER_cancel (mhd_task);
    223     mhd_task = GNUNET_SCHEDULER_add_now (&run_daemon,
    224                                          NULL);
    225   }
    226   else
    227   {
    228     triggered = 1;
    229   }
    230 }
    231 
    232 
    233 /**
    234  * Kick GNUnet Curl scheduler to begin curl interactions.
    235  */
    236 void
    237 FH_trigger_curl (void)
    238 {
    239   GNUNET_CURL_gnunet_scheduler_reschedule (&rc);
    240 }
    241 
    242 
    243 /**
    244  * A client has requested the given url using the given method
    245  * (MHD_HTTP_METHOD_GET, MHD_HTTP_METHOD_PUT,
    246  * MHD_HTTP_METHOD_DELETE, MHD_HTTP_METHOD_POST, etc).  The callback
    247  * must call MHD callbacks to provide content to give back to the
    248  * client and return an HTTP status code (i.e. MHD_HTTP_OK,
    249  * MHD_HTTP_NOT_FOUND, etc.).
    250  *
    251  * @param cls argument given together with the function
    252  *        pointer when the handler was registered with MHD
    253  * @param connection MHD connection handle with further request details
    254  * @param url the requested url
    255  * @param method the HTTP method used (MHD_HTTP_METHOD_GET,
    256  *        MHD_HTTP_METHOD_PUT, etc.)
    257  * @param version the HTTP version string (i.e.
    258  *        MHD_HTTP_VERSION_1_1)
    259  * @param upload_data the data being uploaded (excluding HEADERS,
    260  *        for a POST that fits into memory and that is encoded
    261  *        with a supported encoding, the POST data will NOT be
    262  *        given in upload_data and is instead available as
    263  *        part of MHD_get_connection_values(); very large POST
    264  *        data *will* be made available incrementally in
    265  *        @a upload_data)
    266  * @param upload_data_size set initially to the size of the
    267  *        @a upload_data provided; the method must update this
    268  *        value to the number of bytes NOT processed;
    269  * @param con_cls pointer that the callback can set to some
    270  *        address and that will be preserved by MHD for future
    271  *        calls for this request; since the access handler may
    272  *        be called many times (i.e., for a PUT/POST operation
    273  *        with plenty of upload data) this allows the application
    274  *        to easily associate some request-specific state.
    275  *        If necessary, this state can be cleaned up in the
    276  *        global MHD_RequestCompletedCallback (which
    277  *        can be set with the MHD_OPTION_NOTIFY_COMPLETED).
    278  *        Initially, `*con_cls` will be NULL.
    279  * @return #MHD_YES if the connection was handled successfully,
    280  *         #MHD_NO if the socket must be closed due to a serious
    281  *         error while handling the request
    282  */
    283 static MHD_RESULT
    284 url_handler (void *cls,
    285              struct MHD_Connection *connection,
    286              const char *url,
    287              const char *method,
    288              const char *version,
    289              const char *upload_data,
    290              size_t *upload_data_size,
    291              void **con_cls)
    292 {
    293   static struct FH_RequestHandler handlers[] = {
    294     /* Landing page, tell humans to go away. */
    295     { "/", MHD_HTTP_METHOD_GET, "text/plain",
    296       "Hello, I'm Frosix. This HTTP server is not for humans.\n", 0,
    297       &TMH_MHD_handler_static_response, MHD_HTTP_OK },
    298     { "/agpl", MHD_HTTP_METHOD_GET, "text/plain",
    299       NULL, 0,
    300       &TMH_MHD_handler_agpl_redirect, MHD_HTTP_FOUND },
    301     { "/terms", MHD_HTTP_METHOD_GET, NULL,
    302       NULL, 0,
    303       &FH_handler_privacy, MHD_HTTP_OK },
    304     { "/privacy", MHD_HTTP_METHOD_GET, NULL,
    305       NULL, 0,
    306       &FH_handler_terms, MHD_HTTP_OK },
    307     { "/config", MHD_HTTP_METHOD_GET, "text/json",
    308       NULL, 0,
    309       &FH_handler_config, MHD_HTTP_OK },
    310     { "/seed", MHD_HTTP_METHOD_GET, "arraybuffer",
    311       NULL, 0,
    312       &FH_seed_get, MHD_HTTP_OK },
    313     {NULL, NULL, NULL, NULL, 0, 0 }
    314   };
    315   static struct FH_RequestHandler h404 = {
    316     "", NULL, "text/html",
    317     "<html><title>404: not found</title></html>", 0,
    318     &TMH_MHD_handler_static_response, MHD_HTTP_NOT_FOUND
    319   };
    320   static struct FH_RequestHandler h405 = {
    321     "", NULL, "text/html",
    322     "<html><title>405: method not allowed</title></html>", 0,
    323     &TMH_MHD_handler_static_response, MHD_HTTP_METHOD_NOT_ALLOWED
    324   };
    325   struct TM_HandlerContext *hc = *con_cls;
    326   const char *correlation_id = NULL;
    327   bool path_matched;
    328 
    329   if (NULL == hc)
    330   {
    331     struct GNUNET_AsyncScopeId aid;
    332 
    333     GNUNET_async_scope_fresh (&aid);
    334     /* We only read the correlation ID on the first callback for every client */
    335     correlation_id = MHD_lookup_connection_value (connection,
    336                                                   MHD_HEADER_KIND,
    337                                                   "Frosix-Correlation-Id");
    338     if ((NULL != correlation_id) &&
    339         (GNUNET_YES != GNUNET_CURL_is_valid_scope_id (correlation_id)))
    340     {
    341       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    342                   "Invalid incoming correlation ID\n");
    343       correlation_id = NULL;
    344     }
    345     hc = GNUNET_new (struct TM_HandlerContext);
    346     *con_cls = hc;
    347     hc->async_scope_id = aid;
    348     hc->url = url;
    349   }
    350   if (0 == strcasecmp (method,
    351                        MHD_HTTP_METHOD_HEAD))
    352     method = MHD_HTTP_METHOD_GET; /* MHD will throw away the body */
    353 
    354   GNUNET_SCHEDULER_begin_async_scope (&hc->async_scope_id);
    355   if (NULL != correlation_id)
    356     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    357                 "Handling request for (%s) URL '%s', correlation_id=%s\n",
    358                 method,
    359                 url,
    360                 correlation_id);
    361   else
    362     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    363                 "Handling request (%s) for URL '%s'\n",
    364                 method,
    365                 url);
    366 
    367   /* POST /dkg-commitment/ID */
    368   if (0 == strncmp (url,
    369                     "/dkg-commitment/",
    370                     strlen ("/dkg-commitment/")))
    371   {
    372     // we only accept post for this endpoint
    373     if (0 != strcmp (method, MHD_HTTP_METHOD_POST))
    374     {
    375       return TMH_MHD_handler_static_response (&h405,
    376                                               connection);
    377     }
    378 
    379     // get ID
    380     struct FROSIX_DkgRequestIdP id;
    381     const char *pub_key_str;
    382     size_t len;
    383 
    384     pub_key_str = &url[strlen ("/dkg-commitment/")];
    385     len = strlen (pub_key_str);
    386 
    387     if (GNUNET_OK !=
    388         GNUNET_STRINGS_string_to_data (
    389           pub_key_str,
    390           len,
    391           &id,
    392           sizeof (id)))
    393     {
    394       GNUNET_break_op (0);
    395       return TALER_MHD_reply_with_error (connection,
    396                                          MHD_HTTP_BAD_REQUEST,
    397                                          TALER_EC_GENERIC_PARAMETER_MALFORMED,
    398                                          "ID");
    399     }
    400 
    401     return FH_handler_dkg_commitment_post (connection,
    402                                            hc,
    403                                            &id,
    404                                            &FH_provider_salt,
    405                                            &FH_secret_provider_salt,
    406                                            &FH_priv_sig_key,
    407                                            &FH_pub_sig_key,
    408                                            upload_data,
    409                                            upload_data_size);
    410   }
    411 
    412   /* POST /dkg-shares/ID */
    413   if (0 == strncmp (url,
    414                     "/dkg-shares/",
    415                     strlen ("/dkg-shares/")))
    416   {
    417     // we only accept post for this endpoint
    418     if (0 != strcmp (method, MHD_HTTP_METHOD_POST))
    419     {
    420       return TMH_MHD_handler_static_response (&h405,
    421                                               connection);
    422     }
    423 
    424     // get ID
    425     struct FROSIX_DkgRequestIdP id;
    426     const char *pub_key_str;
    427     size_t len;
    428 
    429     pub_key_str = &url[strlen ("/dkg-shares/")];
    430     len = strlen (pub_key_str);
    431 
    432     if (GNUNET_OK !=
    433         GNUNET_STRINGS_string_to_data (
    434           pub_key_str,
    435           len,
    436           &id,
    437           sizeof (id)))
    438     {
    439       GNUNET_break_op (0);
    440       return TALER_MHD_reply_with_error (connection,
    441                                          MHD_HTTP_BAD_REQUEST,
    442                                          TALER_EC_GENERIC_PARAMETER_MALFORMED,
    443                                          "ID");
    444     }
    445 
    446     return FH_handler_dkg_shares_post (connection,
    447                                        hc,
    448                                        &id,
    449                                        &FH_provider_salt,
    450                                        &FH_secret_provider_salt,
    451                                        upload_data,
    452                                        upload_data_size);
    453   }
    454 
    455   /* POST /dkg-key/ID */
    456   if (0 == strncmp (url,
    457                     "/dkg-key/",
    458                     strlen ("/dkg-key/")))
    459   {
    460     // we only accept post and delete for this endpoint
    461     if (0 != strcmp (method, MHD_HTTP_METHOD_POST) &&
    462         0 != strcmp (method, MHD_HTTP_METHOD_DELETE))
    463     {
    464       return TMH_MHD_handler_static_response (&h405,
    465                                               connection);
    466     }
    467 
    468     // get ID
    469     struct FROSIX_DkgRequestIdP id;
    470     const char *pub_key_str;
    471     size_t len;
    472 
    473     pub_key_str = &url[strlen ("/dkg-key/")];
    474     len = strlen (pub_key_str);
    475 
    476     if (GNUNET_OK !=
    477         GNUNET_STRINGS_string_to_data (
    478           pub_key_str,
    479           len,
    480           &id,
    481           sizeof (id)))
    482     {
    483       GNUNET_break_op (0);
    484       return TALER_MHD_reply_with_error (connection,
    485                                          MHD_HTTP_BAD_REQUEST,
    486                                          TALER_EC_GENERIC_PARAMETER_MALFORMED,
    487                                          "ID");
    488     }
    489 
    490     if (0 == strcmp (method, MHD_HTTP_METHOD_POST))
    491     {
    492       /* create new key */
    493       return FH_handler_dkg_key_post (connection,
    494                                       hc,
    495                                       &id,
    496                                       &FH_provider_salt,
    497                                       &FH_secret_provider_salt,
    498                                       &FH_priv_sig_key,
    499                                       upload_data,
    500                                       upload_data_size);
    501     }
    502     else
    503     {
    504       /* delete existing key */
    505       return FH_handler_key_delete (connection,
    506                                     hc,
    507                                     &id);
    508     }
    509   }
    510 
    511   /* POST /auth-challenge/ID */
    512   if (0 == strncmp (url,
    513                     "/auth-challenge/",
    514                     strlen ("/auth-challenge/")))
    515   {
    516     // we only accept post for this endpoint
    517     if (0 != strcmp (method, MHD_HTTP_METHOD_POST))
    518     {
    519       return TMH_MHD_handler_static_response (&h405,
    520                                               connection);
    521     }
    522 
    523     // get ID
    524     struct FROSIX_ChallengeRequestIdP id;
    525     const char *pub_key_str;
    526     size_t len;
    527 
    528     pub_key_str = &url[strlen ("/auth-challenge/")];
    529     len = strlen (pub_key_str);
    530 
    531     if (GNUNET_OK !=
    532         GNUNET_STRINGS_string_to_data (
    533           pub_key_str,
    534           len,
    535           &id,
    536           sizeof (id)))
    537     {
    538       GNUNET_break_op (0);
    539       return TALER_MHD_reply_with_error (connection,
    540                                          MHD_HTTP_BAD_REQUEST,
    541                                          TALER_EC_GENERIC_PARAMETER_MALFORMED,
    542                                          "ID");
    543     }
    544 
    545     return FH_handler_auth_challenge_post (connection,
    546                                            hc,
    547                                            &id,
    548                                            upload_data,
    549                                            upload_data_size);
    550   }
    551 
    552   /* POST /sig-commitment/ID */
    553   if (0 == strncmp (url,
    554                     "/sig-commitment/",
    555                     strlen ("/sig-commitment/")))
    556   {
    557     // we only accept post for this endpoint
    558     if (0 != strcmp (method, MHD_HTTP_METHOD_POST))
    559     {
    560       return TMH_MHD_handler_static_response (&h405,
    561                                               connection);
    562     }
    563 
    564     // get ID
    565     struct FROSIX_SigRequestIdP id;
    566     const char *pub_key_str;
    567     size_t len;
    568 
    569     pub_key_str = &url[strlen ("/sig-commitment/")];
    570     len = strlen (pub_key_str);
    571 
    572     if (GNUNET_OK !=
    573         GNUNET_STRINGS_string_to_data (
    574           pub_key_str,
    575           len,
    576           &id,
    577           sizeof (id)))
    578     {
    579       GNUNET_break_op (0);
    580       return TALER_MHD_reply_with_error (connection,
    581                                          MHD_HTTP_BAD_REQUEST,
    582                                          TALER_EC_GENERIC_PARAMETER_MALFORMED,
    583                                          "ID");
    584     }
    585 
    586     return FH_handler_sig_commitment_post (connection,
    587                                            hc,
    588                                            &id,
    589                                            upload_data,
    590                                            upload_data_size);
    591   }
    592 
    593   /* POST /sig-share/ID */
    594   if (0 == strncmp (url,
    595                     "/sig-share/",
    596                     strlen ("/sig-share/")))
    597   {
    598     // we only accept post for this endpoint
    599     if (0 != strcmp (method, MHD_HTTP_METHOD_POST))
    600     {
    601       return TMH_MHD_handler_static_response (&h405,
    602                                               connection);
    603     }
    604 
    605     // get ID
    606     struct FROSIX_SigRequestIdP id;
    607     const char *pub_key_str;
    608     size_t len;
    609 
    610     pub_key_str = &url[strlen ("/sig-share/")];
    611     len = strlen (pub_key_str);
    612 
    613     if (GNUNET_OK !=
    614         GNUNET_STRINGS_string_to_data (
    615           pub_key_str,
    616           len,
    617           &id,
    618           sizeof (id)))
    619     {
    620       GNUNET_break_op (0);
    621       return TALER_MHD_reply_with_error (connection,
    622                                          MHD_HTTP_BAD_REQUEST,
    623                                          TALER_EC_GENERIC_PARAMETER_MALFORMED,
    624                                          "ID");
    625     }
    626 
    627     return FH_handler_sig_share_post (connection,
    628                                       hc,
    629                                       &id,
    630                                       upload_data,
    631                                       upload_data_size);
    632   }
    633 
    634   path_matched = false;
    635   for (unsigned int i = 0; NULL != handlers[i].url; i++)
    636   {
    637     struct FH_RequestHandler *rh = &handlers[i];
    638 
    639     if (0 == strcmp (url,
    640                      rh->url))
    641     {
    642       path_matched = true;
    643       if (0 == strcasecmp (method,
    644                            MHD_HTTP_METHOD_OPTIONS))
    645       {
    646         return TALER_MHD_reply_cors_preflight (connection);
    647       }
    648       if ( (NULL == rh->method) ||
    649            (0 == strcasecmp (method,
    650                              rh->method)) )
    651       {
    652         return rh->handler (rh,
    653                             connection);
    654       }
    655     }
    656   }
    657   if (path_matched)
    658     return TMH_MHD_handler_static_response (&h405,
    659                                             connection);
    660   return TMH_MHD_handler_static_response (&h404,
    661                                           connection);
    662 }
    663 
    664 
    665 /**
    666  * Shutdown task (magically invoked when the application is being
    667  * quit)
    668  *
    669  * @param cls NULL
    670  */
    671 static void
    672 do_shutdown (void *cls)
    673 {
    674   (void) cls;
    675   // FIXME
    676   // AH_resume_all_bc ();
    677   // AH_truth_challenge_shutdown ();
    678   // AH_truth_solve_shutdown ();
    679   // AH_truth_upload_shutdown ();
    680   if (NULL != mhd_task)
    681   {
    682     GNUNET_SCHEDULER_cancel (mhd_task);
    683     mhd_task = NULL;
    684   }
    685   if (NULL != FH_ctx)
    686   {
    687     GNUNET_CURL_fini (FH_ctx);
    688     FH_ctx = NULL;
    689   }
    690   if (NULL != rc)
    691   {
    692     GNUNET_CURL_gnunet_rc_destroy (rc);
    693     rc = NULL;
    694   }
    695   if (NULL != mhd)
    696   {
    697     MHD_stop_daemon (mhd);
    698     mhd = NULL;
    699   }
    700   if (NULL != db)
    701   {
    702     FROSIX_DB_plugin_unload (db);
    703     db = NULL;
    704   }
    705   if (NULL != FH_to_heap)
    706   {
    707     GNUNET_CONTAINER_heap_destroy (FH_to_heap);
    708     FH_to_heap = NULL;
    709   }
    710 }
    711 
    712 
    713 /**
    714  * Function called whenever MHD is done with a request.  If the
    715  * request was a POST, we may have stored a `struct Buffer *` in the
    716  * @a con_cls that might still need to be cleaned up.  Call the
    717  * respective function to free the memory.
    718  *
    719  * @param cls client-defined closure
    720  * @param connection connection handle
    721  * @param con_cls value as set by the last call to
    722  *        the #MHD_AccessHandlerCallback
    723  * @param toe reason for request termination
    724  * @see #MHD_OPTION_NOTIFY_COMPLETED
    725  * @ingroup request
    726  */
    727 static void
    728 handle_mhd_completion_callback (void *cls,
    729                                 struct MHD_Connection *connection,
    730                                 void **con_cls,
    731                                 enum MHD_RequestTerminationCode toe)
    732 {
    733   struct TM_HandlerContext *hc = *con_cls;
    734   struct GNUNET_AsyncScopeSave old_scope;
    735 
    736   (void) cls;
    737   (void) connection;
    738   if (NULL == hc)
    739     return;
    740   GNUNET_async_scope_enter (&hc->async_scope_id,
    741                             &old_scope);
    742   {
    743 #if MHD_VERSION >= 0x00097304
    744     const union MHD_ConnectionInfo *ci;
    745     unsigned int http_status = 0;
    746 
    747     ci = MHD_get_connection_info (connection,
    748                                   MHD_CONNECTION_INFO_HTTP_STATUS);
    749     if (NULL != ci)
    750       http_status = ci->http_status;
    751     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    752                 "Request for `%s' completed with HTTP status %u (%d)\n",
    753                 hc->url,
    754                 http_status,
    755                 toe);
    756 #else
    757     (void) connection;
    758     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    759                 "Request for `%s' completed (%d)\n",
    760                 hc->url,
    761                 toe);
    762 #endif
    763   }
    764   if (NULL != hc->cc)
    765     hc->cc (hc);
    766   GNUNET_free (hc);
    767   *con_cls = NULL;
    768 }
    769 
    770 
    771 /**
    772  * Function that queries MHD's select sets and
    773  * starts the task waiting for them.
    774  *
    775  * @return task handle for the daemon
    776  */
    777 static struct GNUNET_SCHEDULER_Task *
    778 prepare_daemon (void)
    779 {
    780   struct GNUNET_SCHEDULER_Task *ret;
    781   fd_set rs;
    782   fd_set ws;
    783   fd_set es;
    784   struct GNUNET_NETWORK_FDSet *wrs;
    785   struct GNUNET_NETWORK_FDSet *wws;
    786   int max;
    787   MHD_UNSIGNED_LONG_LONG timeout;
    788   int haveto;
    789   struct GNUNET_TIME_Relative tv;
    790 
    791   FD_ZERO (&rs);
    792   FD_ZERO (&ws);
    793   FD_ZERO (&es);
    794   wrs = GNUNET_NETWORK_fdset_create ();
    795   wws = GNUNET_NETWORK_fdset_create ();
    796   max = -1;
    797   GNUNET_assert (MHD_YES ==
    798                  MHD_get_fdset (mhd,
    799                                 &rs,
    800                                 &ws,
    801                                 &es,
    802                                 &max));
    803   haveto = MHD_get_timeout (mhd, &timeout);
    804   if (haveto == MHD_YES)
    805     tv = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS,
    806                                         timeout);
    807   else
    808     tv = GNUNET_TIME_UNIT_FOREVER_REL;
    809   GNUNET_NETWORK_fdset_copy_native (wrs, &rs, max + 1);
    810   GNUNET_NETWORK_fdset_copy_native (wws, &ws, max + 1);
    811   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    812               "Adding run_daemon select task\n");
    813   ret = GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_HIGH,
    814                                      tv,
    815                                      wrs,
    816                                      wws,
    817                                      &run_daemon,
    818                                      NULL);
    819   GNUNET_NETWORK_fdset_destroy (wrs);
    820   GNUNET_NETWORK_fdset_destroy (wws);
    821   return ret;
    822 }
    823 
    824 
    825 /**
    826  * Main function that will be run by the scheduler.
    827  *
    828  * @param cls closure
    829  * @param args remaining command-line arguments
    830  * @param cfgfile name of the configuration file used (for saving, can be
    831  *        NULL!)
    832  * @param config configuration
    833  */
    834 static void
    835 run (void *cls,
    836      char *const *args,
    837      const char *cfgfile,
    838      const struct GNUNET_CONFIGURATION_Handle *config)
    839 {
    840   int fh;
    841   uint16_t port;
    842   enum TALER_MHD_GlobalOptions go;
    843 
    844   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    845               "Starting frosix-httpd\n");
    846   go = TALER_MHD_GO_NONE;
    847   if (FH_connection_close)
    848     go |= TALER_MHD_GO_FORCE_CONNECTION_CLOSE;
    849   FH_load_terms (config);
    850   TALER_MHD_setup (go);
    851   FH_cfg = config;
    852   global_result = GNUNET_SYSERR;
    853   GNUNET_SCHEDULER_add_shutdown (&do_shutdown,
    854                                  NULL);
    855   if (GNUNET_OK !=
    856       TALER_config_get_amount (config,
    857                                "authorization-question",
    858                                "COST",
    859                                &FH_question_cost))
    860   {
    861     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
    862                                "authorization-question",
    863                                "COST");
    864     GNUNET_SCHEDULER_shutdown ();
    865     return;
    866   }
    867   if (GNUNET_OK !=
    868       TALER_config_get_amount (config,
    869                                "frosix",
    870                                "ANNUAL_FEE",
    871                                &FH_annual_fee))
    872   {
    873     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
    874                                "frosix",
    875                                "ANNUAL_FEE");
    876     GNUNET_SCHEDULER_shutdown ();
    877     return;
    878   }
    879   if (GNUNET_OK !=
    880       TALER_config_get_amount (config,
    881                                "frosix",
    882                                "SIGNATURE_CREATION_FEE",
    883                                &FH_signature_creation_fee))
    884   {
    885     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
    886                                "frosix",
    887                                "SIGNATURE_CREATION_FEE");
    888     GNUNET_SCHEDULER_shutdown ();
    889     return;
    890   }
    891   if (GNUNET_OK !=
    892       GNUNET_CONFIGURATION_get_value_string (config,
    893                                              "frosix-merchant-backend",
    894                                              "PAYMENT_BACKEND_URL",
    895                                              &FH_backend_url))
    896   {
    897     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
    898                                "frosix-merchant-backend",
    899                                "PAYMENT_BACKEND_URL");
    900     GNUNET_SCHEDULER_shutdown ();
    901     return;
    902   }
    903   if ( (0 != strncasecmp ("https://",
    904                           FH_backend_url,
    905                           strlen ("https://"))) &&
    906        (0 != strncasecmp ("http://",
    907                           FH_backend_url,
    908                           strlen ("http://"))) )
    909   {
    910     GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
    911                                "frosix-merchant-backend",
    912                                "PAYMENT_BACKEND_URL",
    913                                "Must be HTTP(S) URL");
    914     GNUNET_SCHEDULER_shutdown ();
    915     return;
    916   }
    917 
    918   if ( (0 == strcasecmp ("https://",
    919                          FH_backend_url)) ||
    920        (0 == strcasecmp ("http://",
    921                          FH_backend_url)) )
    922   {
    923     GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
    924                                "frosix-merchant-backend",
    925                                "PAYMENT_BACKEND_URL",
    926                                "Must have domain name");
    927     GNUNET_SCHEDULER_shutdown ();
    928     return;
    929   }
    930 
    931   if (GNUNET_OK !=
    932       GNUNET_CONFIGURATION_get_value_string (config,
    933                                              "frosix",
    934                                              "FULFILLMENT_URL",
    935                                              &FH_fulfillment_url))
    936   {
    937     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
    938                                "frosix",
    939                                "FULFILLMENT_URL");
    940     GNUNET_SCHEDULER_shutdown ();
    941     return;
    942   }
    943 
    944   if (GNUNET_OK !=
    945       GNUNET_CONFIGURATION_get_value_string (config,
    946                                              "frosix",
    947                                              "BUSINESS_NAME",
    948                                              &FH_business_name))
    949   {
    950     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
    951                                "frosix",
    952                                "BUSINESS_NAME");
    953     GNUNET_SCHEDULER_shutdown ();
    954     return;
    955   }
    956 
    957   {
    958     char *provider_salt;
    959     if (GNUNET_OK !=
    960         GNUNET_CONFIGURATION_get_value_string (config,
    961                                                "frosix",
    962                                                "PROVIDER_SALT",
    963                                                &provider_salt))
    964     {
    965       GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
    966                                  "frosix",
    967                                  "PROVIDER_SALT");
    968       GNUNET_SCHEDULER_shutdown ();
    969       return;
    970     }
    971     GNUNET_assert (GNUNET_YES ==
    972                    GNUNET_CRYPTO_kdf (&FH_provider_salt,
    973                                       sizeof (FH_provider_salt),
    974                                       "frosix-provider-salt",
    975                                       strlen ("frosix-provider-salt"),
    976                                       provider_salt,
    977                                       strlen (provider_salt),
    978                                       NULL,
    979                                       0));
    980     GNUNET_free (provider_salt);
    981   }
    982 
    983   {
    984     char *secret_provider_salt;
    985     if (GNUNET_OK !=
    986         GNUNET_CONFIGURATION_get_value_string (config,
    987                                                "frosix",
    988                                                "SECRET_PROVIDER_SALT",
    989                                                &secret_provider_salt))
    990     {
    991       GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
    992                                  "frosix",
    993                                  "SECRET_PROVIDER_SALT");
    994       GNUNET_SCHEDULER_shutdown ();
    995       return;
    996     }
    997     GNUNET_assert (GNUNET_YES ==
    998                    GNUNET_CRYPTO_kdf (&FH_secret_provider_salt,
    999                                       sizeof (FH_secret_provider_salt),
   1000                                       "secret-frosix-provider-salt",
   1001                                       strlen ("secret-frosix-provider-salt"),
   1002                                       secret_provider_salt,
   1003                                       strlen (secret_provider_salt),
   1004                                       NULL,
   1005                                       0));
   1006     GNUNET_free (secret_provider_salt);
   1007   }
   1008 
   1009   {
   1010     char *private_signature_key;
   1011 
   1012     if (GNUNET_OK !=
   1013         GNUNET_CONFIGURATION_get_value_string (config,
   1014                                                "frosix",
   1015                                                "PRIVATE_SIG_KEY",
   1016                                                &private_signature_key))
   1017     {
   1018       GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
   1019                                  "frosix",
   1020                                  "PRIVATE_SIG_KEY");
   1021       GNUNET_SCHEDULER_shutdown ();
   1022       return;
   1023     }
   1024 
   1025     GNUNET_assert (GNUNET_YES ==
   1026                    GNUNET_STRINGS_string_to_data (private_signature_key,
   1027                                                   strlen (
   1028                                                     private_signature_key),
   1029                                                   &FH_priv_sig_key,
   1030                                                   sizeof (FH_priv_sig_key)));
   1031 
   1032     GNUNET_CRYPTO_eddsa_key_get_public (&FH_priv_sig_key,
   1033                                         &FH_pub_sig_key);
   1034 
   1035     GNUNET_free (private_signature_key);
   1036   }
   1037 
   1038   /* setup HTTP client event loop */
   1039   FH_ctx = GNUNET_CURL_init (&GNUNET_CURL_gnunet_scheduler_reschedule,
   1040                              &rc);
   1041   rc = GNUNET_CURL_gnunet_rc_create (FH_ctx);
   1042   if (NULL != userpass)
   1043     GNUNET_CURL_set_userpass (FH_ctx,
   1044                               userpass);
   1045   if (NULL != keyfile)
   1046     GNUNET_CURL_set_tlscert (FH_ctx,
   1047                              certtype,
   1048                              certfile,
   1049                              keyfile,
   1050                              keypass);
   1051   if (NULL == apikey)
   1052   {
   1053     (void) GNUNET_CONFIGURATION_get_value_string (config,
   1054                                                   "frosix-merchant-backend",
   1055                                                   "API_KEY",
   1056                                                   &apikey);
   1057   }
   1058   if (NULL != apikey)
   1059   {
   1060     char *auth_header;
   1061 
   1062     GNUNET_asprintf (&auth_header,
   1063                      "%s: %s",
   1064                      MHD_HTTP_HEADER_AUTHORIZATION,
   1065                      apikey);
   1066     if (GNUNET_OK !=
   1067         GNUNET_CURL_append_header (FH_ctx,
   1068                                    auth_header))
   1069     {
   1070       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   1071                   "Failed so set %s header, trying without\n",
   1072                   MHD_HTTP_HEADER_AUTHORIZATION);
   1073     }
   1074     GNUNET_free (auth_header);
   1075   }
   1076 
   1077   if (NULL ==
   1078       (db = FROSIX_DB_plugin_load (config)))
   1079   {
   1080     GNUNET_SCHEDULER_shutdown ();
   1081     return;
   1082   }
   1083 
   1084   if (GNUNET_OK !=
   1085       db->connect (db->cls))
   1086   {
   1087     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   1088                 "Database not setup. Did you run frosix-dbinit?\n");
   1089     GNUNET_SCHEDULER_shutdown ();
   1090     return;
   1091   }
   1092 
   1093   fh = TALER_MHD_bind (config,
   1094                        "frosix",
   1095                        &port);
   1096   if ( (0 == port) &&
   1097        (-1 == fh) )
   1098   {
   1099     GNUNET_SCHEDULER_shutdown ();
   1100     return;
   1101   }
   1102   mhd = MHD_start_daemon (MHD_USE_SUSPEND_RESUME | MHD_USE_DUAL_STACK,
   1103                           port,
   1104                           NULL, NULL,
   1105                           &url_handler, NULL,
   1106                           MHD_OPTION_LISTEN_SOCKET, fh,
   1107                           MHD_OPTION_NOTIFY_COMPLETED,
   1108                           &handle_mhd_completion_callback, NULL,
   1109                           MHD_OPTION_CONNECTION_TIMEOUT, (unsigned
   1110                                                           int) 10 /* 10s */,
   1111                           MHD_OPTION_END);
   1112   if (NULL == mhd)
   1113   {
   1114     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   1115                 "Failed to launch HTTP service (port %u in use?), exiting.\n",
   1116                 port);
   1117     GNUNET_SCHEDULER_shutdown ();
   1118     return;
   1119   }
   1120   global_result = GNUNET_OK;
   1121   mhd_task = prepare_daemon ();
   1122 }
   1123 
   1124 
   1125 /**
   1126  * The main function of the serve tool
   1127  *
   1128  * @param argc number of arguments from the command line
   1129  * @param argv command line arguments
   1130  * @return 0 ok, 1 on error
   1131  */
   1132 int
   1133 main (int argc,
   1134       char *const *argv)
   1135 {
   1136   enum GNUNET_GenericReturnValue res;
   1137   struct GNUNET_GETOPT_CommandLineOption options[] = {
   1138     GNUNET_GETOPT_option_string ('A',
   1139                                  "auth",
   1140                                  "USERNAME:PASSWORD",
   1141                                  "use the given USERNAME and PASSWORD for client authentication",
   1142                                  &userpass),
   1143     GNUNET_GETOPT_option_flag ('C',
   1144                                "connection-close",
   1145                                "force HTTP connections to be closed after each request",
   1146                                &FH_connection_close),
   1147     GNUNET_GETOPT_option_string ('k',
   1148                                  "key",
   1149                                  "KEYFILE",
   1150                                  "file with the private TLS key for TLS client authentication",
   1151                                  &keyfile),
   1152     GNUNET_GETOPT_option_string ('p',
   1153                                  "pass",
   1154                                  "KEYFILEPASSPHRASE",
   1155                                  "passphrase needed to decrypt the TLS client private key file",
   1156                                  &keypass),
   1157     GNUNET_GETOPT_option_string ('K',
   1158                                  "apikey",
   1159                                  "APIKEY",
   1160                                  "API key to use in the HTTP request to the merchant backend",
   1161                                  &apikey),
   1162     GNUNET_GETOPT_option_string ('t',
   1163                                  "type",
   1164                                  "CERTTYPE",
   1165                                  "type of the TLS client certificate, defaults to PEM if not specified",
   1166                                  &certtype),
   1167     GNUNET_GETOPT_OPTION_END
   1168   };
   1169 
   1170   /* FIRST get the libtalerutil initialization out
   1171      of the way. Then throw that one away, and force
   1172      the FROSIX defaults to be used! */
   1173   (void) TALER_project_data_default ();
   1174   GNUNET_OS_init (FROSIX_project_data_default ());
   1175   res = GNUNET_PROGRAM_run (argc, argv,
   1176                             "frosix-httpd",
   1177                             "Frosix HTTP interface",
   1178                             options, &run, NULL);
   1179   if (GNUNET_SYSERR == res)
   1180     return 3;
   1181   if (GNUNET_NO == res)
   1182     return 0;
   1183   return (GNUNET_OK == global_result) ? 0 : 1;
   1184 }