anastasis

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

anastasis-httpd.c (28911B)


      1 /*
      2   This file is part of Anastasis
      3   (C) 2020-2025 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 backend/anastasis-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  */
     23 #include "platform.h"
     24 #include "anastasis-httpd.h"
     25 #include "anastasis_util_lib.h"
     26 #include "anastasis-httpd_mhd.h"
     27 #include "anastasis_database_lib.h"
     28 #include "anastasis-httpd_policy.h"
     29 #include "anastasis-httpd_policy-meta.h"
     30 #include "anastasis-httpd_truth.h"
     31 #include "anastasis-httpd_terms.h"
     32 #include "anastasis-httpd_config.h"
     33 
     34 
     35 /**
     36  * Upload limit to the service, in megabytes.
     37  */
     38 unsigned long long int AH_upload_limit_mb;
     39 
     40 /**
     41  * Annual fee for the backup account.
     42  */
     43 struct TALER_Amount AH_annual_fee;
     44 
     45 /**
     46  * Fee for a truth upload.
     47  */
     48 struct TALER_Amount AH_truth_upload_fee;
     49 
     50 /**
     51  * Amount of insurance.
     52  */
     53 struct TALER_Amount AH_insurance;
     54 
     55 /**
     56  * Cost for secure question truth download.
     57  */
     58 struct TALER_Amount AH_question_cost;
     59 
     60 /**
     61  * Our configuration.
     62  */
     63 const struct GNUNET_CONFIGURATION_Handle *AH_cfg;
     64 
     65 /**
     66  * Our Taler backend to process payments.
     67  */
     68 char *AH_backend_url;
     69 
     70 /**
     71  * Our fulfillment URL.
     72  */
     73 char *AH_fulfillment_url;
     74 
     75 /**
     76  * Our business name.
     77  */
     78 char *AH_business_name;
     79 
     80 /**
     81  * Our provider salt.
     82  */
     83 struct ANASTASIS_CRYPTO_ProviderSaltP AH_provider_salt;
     84 
     85 /**
     86  * Number of policy uploads permitted per annual fee payment.
     87  */
     88 unsigned long long AH_post_counter = 64LLU;
     89 
     90 /**
     91  * Our context for making HTTP requests.
     92  */
     93 struct GNUNET_CURL_Context *AH_ctx;
     94 
     95 /**
     96  * Should a "Connection: close" header be added to each HTTP response?
     97  */
     98 static int AH_connection_close;
     99 
    100 /**
    101  * True if we started any HTTP daemon.
    102  */
    103 static bool have_daemons;
    104 
    105 /**
    106  * Heap for processing timeouts of requests.
    107  */
    108 struct GNUNET_CONTAINER_Heap *AH_to_heap;
    109 
    110 /**
    111  * Global return code
    112  */
    113 static int global_result;
    114 
    115 /**
    116  * Connection handle to the our database
    117  */
    118 struct ANASTASIS_DatabasePlugin *db;
    119 
    120 /**
    121  * Reschedule context for #AH_ctx.
    122  */
    123 static struct GNUNET_CURL_RescheduleContext *rc;
    124 
    125 /**
    126  * Username and password to use for client authentication
    127  * (optional).
    128  */
    129 static char *userpass;
    130 
    131 /**
    132  * Type of the client's TLS certificate (optional).
    133  */
    134 static char *certtype;
    135 
    136 /**
    137  * File with the client's TLS certificate (optional).
    138  */
    139 static char *certfile;
    140 
    141 /**
    142  * File with the client's TLS private key (optional).
    143  */
    144 static char *keyfile;
    145 
    146 /**
    147  * This value goes in the Authorization:-header.
    148  */
    149 static char *apikey;
    150 
    151 /**
    152  * Passphrase to decrypt client's TLS private key file (optional).
    153  */
    154 static char *keypass;
    155 
    156 
    157 /**
    158  * Kick MHD to run now, to be called after MHD_resume_connection().
    159  * Basically, we need to explicitly resume MHD's event loop whenever
    160  * we made progress serving a request.  This function re-schedules
    161  * the task processing MHD's activities to run immediately.
    162  *
    163  * @param cls NULL -- FIXME: why cls?
    164  *
    165  * FIXME: maybe call directly?
    166  */
    167 void
    168 AH_trigger_daemon (void *cls)
    169 {
    170   TALER_MHD_daemon_trigger ();
    171 }
    172 
    173 
    174 /**
    175  * Kick GNUnet Curl scheduler to begin curl interactions.
    176  */
    177 void
    178 AH_trigger_curl (void)
    179 {
    180   GNUNET_CURL_gnunet_scheduler_reschedule (&rc);
    181 }
    182 
    183 
    184 /**
    185  * A client has requested the given url using the given method
    186  * (MHD_HTTP_METHOD_GET, MHD_HTTP_METHOD_PUT,
    187  * MHD_HTTP_METHOD_DELETE, MHD_HTTP_METHOD_POST, etc).  The callback
    188  * must call MHD callbacks to provide content to give back to the
    189  * client and return an HTTP status code (i.e. MHD_HTTP_OK,
    190  * MHD_HTTP_NOT_FOUND, etc.).
    191  *
    192  * @param cls argument given together with the function
    193  *        pointer when the handler was registered with MHD
    194  * @param connection MHD connection handle with further request details
    195  * @param url the requested url
    196  * @param method the HTTP method used (MHD_HTTP_METHOD_GET,
    197  *        MHD_HTTP_METHOD_PUT, etc.)
    198  * @param version the HTTP version string (i.e.
    199  *        MHD_HTTP_VERSION_1_1)
    200  * @param upload_data the data being uploaded (excluding HEADERS,
    201  *        for a POST that fits into memory and that is encoded
    202  *        with a supported encoding, the POST data will NOT be
    203  *        given in upload_data and is instead available as
    204  *        part of MHD_get_connection_values(); very large POST
    205  *        data *will* be made available incrementally in
    206  *        @a upload_data)
    207  * @param upload_data_size set initially to the size of the
    208  *        @a upload_data provided; the method must update this
    209  *        value to the number of bytes NOT processed;
    210  * @param con_cls pointer that the callback can set to some
    211  *        address and that will be preserved by MHD for future
    212  *        calls for this request; since the access handler may
    213  *        be called many times (i.e., for a PUT/POST operation
    214  *        with plenty of upload data) this allows the application
    215  *        to easily associate some request-specific state.
    216  *        If necessary, this state can be cleaned up in the
    217  *        global MHD_RequestCompletedCallback (which
    218  *        can be set with the MHD_OPTION_NOTIFY_COMPLETED).
    219  *        Initially, `*con_cls` will be NULL.
    220  * @return #MHD_YES if the connection was handled successfully,
    221  *         #MHD_NO if the socket must be closed due to a serious
    222  *         error while handling the request
    223  */
    224 static MHD_RESULT
    225 url_handler (void *cls,
    226              struct MHD_Connection *connection,
    227              const char *url,
    228              const char *method,
    229              const char *version,
    230              const char *upload_data,
    231              size_t *upload_data_size,
    232              void **con_cls)
    233 {
    234   static struct AH_RequestHandler handlers[] = {
    235     /* Landing page, tell humans to go away. */
    236     { "/", MHD_HTTP_METHOD_GET, "text/plain",
    237       "Hello, I'm Anastasis. This HTTP server is not for humans.\n", 0,
    238       &TMH_MHD_handler_static_response, MHD_HTTP_OK },
    239     { "/agpl", MHD_HTTP_METHOD_GET, "text/plain",
    240       NULL, 0,
    241       &TMH_MHD_handler_agpl_redirect, MHD_HTTP_FOUND },
    242     { "/terms", MHD_HTTP_METHOD_GET, NULL,
    243       NULL, 0,
    244       &AH_handler_privacy, MHD_HTTP_OK },
    245     { "/privacy", MHD_HTTP_METHOD_GET, NULL,
    246       NULL, 0,
    247       &AH_handler_terms, MHD_HTTP_OK },
    248     { "/config", MHD_HTTP_METHOD_GET, "text/json",
    249       NULL, 0,
    250       &AH_handler_config, MHD_HTTP_OK },
    251     {NULL, NULL, NULL, NULL, 0, 0 }
    252   };
    253   static struct AH_RequestHandler h404 = {
    254     "", NULL, "text/html",
    255     "<html><title>404: not found</title></html>", 0,
    256     &TMH_MHD_handler_static_response, MHD_HTTP_NOT_FOUND
    257   };
    258   static struct AH_RequestHandler h405 = {
    259     "", NULL, "text/html",
    260     "<html><title>405: method not allowed</title></html>", 0,
    261     &TMH_MHD_handler_static_response, MHD_HTTP_METHOD_NOT_ALLOWED
    262   };
    263   struct TM_HandlerContext *hc = *con_cls;
    264   const char *correlation_id = NULL;
    265   bool path_matched;
    266 
    267   if (NULL == hc)
    268   {
    269     struct GNUNET_AsyncScopeId aid;
    270 
    271     GNUNET_async_scope_fresh (&aid);
    272     /* We only read the correlation ID on the first callback for every client */
    273     correlation_id = MHD_lookup_connection_value (connection,
    274                                                   MHD_HEADER_KIND,
    275                                                   "Anastasis-Correlation-Id");
    276     if ((NULL != correlation_id) &&
    277         (GNUNET_YES != GNUNET_CURL_is_valid_scope_id (correlation_id)))
    278     {
    279       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    280                   "Invalid incoming correlation ID\n");
    281       correlation_id = NULL;
    282     }
    283     hc = GNUNET_new (struct TM_HandlerContext);
    284     *con_cls = hc;
    285     hc->async_scope_id = aid;
    286     hc->url = url;
    287   }
    288   if (0 == strcasecmp (method,
    289                        MHD_HTTP_METHOD_HEAD))
    290     method = MHD_HTTP_METHOD_GET; /* MHD will throw away the body */
    291 
    292   GNUNET_SCHEDULER_begin_async_scope (&hc->async_scope_id);
    293   if (NULL != correlation_id)
    294     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    295                 "Handling request for (%s) URL '%s', correlation_id=%s\n",
    296                 method,
    297                 url,
    298                 correlation_id);
    299   else
    300     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    301                 "Handling request (%s) for URL '%s'\n",
    302                 method,
    303                 url);
    304   if (0 == strncmp (url,
    305                     "/policy/",
    306                     strlen ("/policy/")))
    307   {
    308     const char *account = url + strlen ("/policy/");
    309     const char *end = strchr (account, '/');
    310     struct ANASTASIS_CRYPTO_AccountPublicKeyP account_pub;
    311 
    312     if (GNUNET_OK !=
    313         GNUNET_STRINGS_string_to_data (
    314           account,
    315           (NULL == end)
    316           ? strlen (account)
    317           : end - account,
    318           &account_pub,
    319           sizeof (struct ANASTASIS_CRYPTO_AccountPublicKeyP)))
    320     {
    321       return TALER_MHD_reply_with_error (connection,
    322                                          MHD_HTTP_BAD_REQUEST,
    323                                          TALER_EC_GENERIC_PARAMETER_MALFORMED,
    324                                          "account public key");
    325     }
    326     if ( (NULL != end) &&
    327          (0 != strcmp (end,
    328                        "/meta")) )
    329       return TMH_MHD_handler_static_response (&h404,
    330                                               connection);
    331     if (0 == strcmp (method,
    332                      MHD_HTTP_METHOD_GET))
    333     {
    334       if (NULL == end)
    335         return AH_policy_get (connection,
    336                               &account_pub);
    337       return AH_policy_meta_get (connection,
    338                                  &account_pub);
    339     }
    340     if ( (0 == strcmp (method,
    341                        MHD_HTTP_METHOD_POST)) &&
    342          (NULL == end) )
    343     {
    344       return AH_handler_policy_post (connection,
    345                                      hc,
    346                                      &account_pub,
    347                                      upload_data,
    348                                      upload_data_size);
    349     }
    350     if (0 == strcmp (method,
    351                      MHD_HTTP_METHOD_OPTIONS))
    352     {
    353       return TALER_MHD_reply_cors_preflight (connection);
    354     }
    355     return TMH_MHD_handler_static_response (&h405,
    356                                             connection);
    357   }
    358   if (0 == strncmp (url,
    359                     "/truth/",
    360                     strlen ("/truth/")))
    361   {
    362     struct ANASTASIS_CRYPTO_TruthUUIDP tu;
    363     const char *pub_key_str;
    364     const char *end;
    365     size_t len;
    366 
    367     pub_key_str = &url[strlen ("/truth/")];
    368     end = strchr (pub_key_str,
    369                   '/');
    370     if (NULL == end)
    371       len = strlen (pub_key_str);
    372     else
    373       len = end - pub_key_str;
    374     if (GNUNET_OK !=
    375         GNUNET_STRINGS_string_to_data (
    376           pub_key_str,
    377           len,
    378           &tu,
    379           sizeof(tu)))
    380     {
    381       GNUNET_break_op (0);
    382       return TALER_MHD_reply_with_error (connection,
    383                                          MHD_HTTP_BAD_REQUEST,
    384                                          TALER_EC_GENERIC_PARAMETER_MALFORMED,
    385                                          "truth UUID");
    386     }
    387     if ( (NULL != end) &&
    388          (0 != strcmp (end, "/solve")) &&
    389          (0 != strcmp (end, "/challenge")) )
    390       return TMH_MHD_handler_static_response (&h404,
    391                                               connection);
    392     if (0 == strcmp (method,
    393                      MHD_HTTP_METHOD_OPTIONS))
    394       return TALER_MHD_reply_cors_preflight (connection);
    395     if (0 != strcmp (method,
    396                      MHD_HTTP_METHOD_POST))
    397       return TMH_MHD_handler_static_response (&h405,
    398                                               connection);
    399     if (NULL == end)
    400     {
    401       return AH_handler_truth_post (connection,
    402                                     hc,
    403                                     &tu,
    404                                     upload_data,
    405                                     upload_data_size);
    406     }
    407     if (0 == strcmp (end,
    408                      "/solve"))
    409     {
    410       return AH_handler_truth_solve (connection,
    411                                      hc,
    412                                      &tu,
    413                                      upload_data,
    414                                      upload_data_size);
    415     }
    416     if (0 == strcmp (end,
    417                      "/challenge"))
    418     {
    419       return AH_handler_truth_challenge (connection,
    420                                          hc,
    421                                          &tu,
    422                                          upload_data,
    423                                          upload_data_size);
    424     }
    425     /* should be impossible to get here */
    426     GNUNET_assert (0);
    427   } /* end of "/truth/" prefix */
    428   path_matched = false;
    429   for (unsigned int i = 0; NULL != handlers[i].url; i++)
    430   {
    431     struct AH_RequestHandler *rh = &handlers[i];
    432 
    433     if (0 == strcmp (url,
    434                      rh->url))
    435     {
    436       path_matched = true;
    437       if (0 == strcasecmp (method,
    438                            MHD_HTTP_METHOD_OPTIONS))
    439       {
    440         return TALER_MHD_reply_cors_preflight (connection);
    441       }
    442       if ( (NULL == rh->method) ||
    443            (0 == strcasecmp (method,
    444                              rh->method)) )
    445       {
    446         return rh->handler (rh,
    447                             connection);
    448       }
    449     }
    450   }
    451   if (path_matched)
    452     return TMH_MHD_handler_static_response (&h405,
    453                                             connection);
    454   return TMH_MHD_handler_static_response (&h404,
    455                                           connection);
    456 }
    457 
    458 
    459 /**
    460  * Shutdown task (magically invoked when the application is being
    461  * quit)
    462  *
    463  * @param cls NULL
    464  */
    465 static void
    466 do_shutdown (void *cls)
    467 {
    468   (void) cls;
    469   TALER_MHD_daemons_halt ();
    470   AH_resume_all_bc ();
    471   AH_truth_challenge_shutdown ();
    472   AH_truth_solve_shutdown ();
    473   AH_truth_upload_shutdown ();
    474   TALER_MHD_daemons_destroy ();
    475   if (NULL != AH_ctx)
    476   {
    477     GNUNET_CURL_fini (AH_ctx);
    478     AH_ctx = NULL;
    479   }
    480   if (NULL != rc)
    481   {
    482     GNUNET_CURL_gnunet_rc_destroy (rc);
    483     rc = NULL;
    484   }
    485   if (NULL != db)
    486   {
    487     ANASTASIS_DB_plugin_unload (db);
    488     db = NULL;
    489   }
    490   if (NULL != AH_to_heap)
    491   {
    492     GNUNET_CONTAINER_heap_destroy (AH_to_heap);
    493     AH_to_heap = NULL;
    494   }
    495 }
    496 
    497 
    498 /**
    499  * Function called whenever MHD is done with a request.  If the
    500  * request was a POST, we may have stored a `struct Buffer *` in the
    501  * @a con_cls that might still need to be cleaned up.  Call the
    502  * respective function to free the memory.
    503  *
    504  * @param cls client-defined closure
    505  * @param connection connection handle
    506  * @param con_cls value as set by the last call to
    507  *        the #MHD_AccessHandlerCallback
    508  * @param toe reason for request termination
    509  * @see #MHD_OPTION_NOTIFY_COMPLETED
    510  * @ingroup request
    511  */
    512 static void
    513 handle_mhd_completion_callback (void *cls,
    514                                 struct MHD_Connection *connection,
    515                                 void **con_cls,
    516                                 enum MHD_RequestTerminationCode toe)
    517 {
    518   struct TM_HandlerContext *hc = *con_cls;
    519   struct GNUNET_AsyncScopeSave old_scope;
    520 
    521   (void) cls;
    522   (void) connection;
    523   if (NULL == hc)
    524     return;
    525   GNUNET_async_scope_enter (&hc->async_scope_id,
    526                             &old_scope);
    527   {
    528 #if MHD_VERSION >= 0x00097304
    529     const union MHD_ConnectionInfo *ci;
    530     unsigned int http_status = 0;
    531 
    532     ci = MHD_get_connection_info (connection,
    533                                   MHD_CONNECTION_INFO_HTTP_STATUS);
    534     if (NULL != ci)
    535       http_status = ci->http_status;
    536     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    537                 "Request for `%s' completed with HTTP status %u (%d)\n",
    538                 hc->url,
    539                 http_status,
    540                 toe);
    541 #else
    542     (void) connection;
    543     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    544                 "Request for `%s' completed (%d)\n",
    545                 hc->url,
    546                 toe);
    547 #endif
    548   }
    549   if (NULL != hc->cc)
    550     hc->cc (hc);
    551   GNUNET_free (hc);
    552   *con_cls = NULL;
    553 }
    554 
    555 
    556 /**
    557  * Callback invoked on every listen socket to start the
    558  * respective MHD HTTP daemon.
    559  *
    560  * @param cls unused
    561  * @param lsock the listen socket
    562  */
    563 static void
    564 start_daemon (void *cls,
    565               int lsock)
    566 {
    567   struct MHD_Daemon *mhd;
    568 
    569   (void) cls;
    570   GNUNET_assert (-1 != lsock);
    571   mhd = MHD_start_daemon (MHD_USE_SUSPEND_RESUME | MHD_USE_DUAL_STACK,
    572                           0 /* port */,
    573                           NULL, NULL,
    574                           &url_handler, NULL,
    575                           MHD_OPTION_LISTEN_SOCKET, lsock,
    576                           MHD_OPTION_NOTIFY_COMPLETED,
    577                           &handle_mhd_completion_callback, NULL,
    578                           MHD_OPTION_CONNECTION_TIMEOUT, (unsigned
    579                                                           int) 10 /* 10s */,
    580                           MHD_OPTION_END);
    581   if (NULL == mhd)
    582   {
    583     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    584                 "Failed to launch HTTP service.\n");
    585     GNUNET_SCHEDULER_shutdown ();
    586     return;
    587   }
    588   have_daemons = true;
    589   TALER_MHD_daemon_start (mhd);
    590 }
    591 
    592 
    593 /**
    594  * Main function that will be run by the scheduler.
    595  *
    596  * @param cls closure
    597  * @param args remaining command-line arguments
    598  * @param cfgfile name of the configuration file used (for saving, can be
    599  *        NULL!)
    600  * @param config configuration
    601  */
    602 static void
    603 run (void *cls,
    604      char *const *args,
    605      const char *cfgfile,
    606      const struct GNUNET_CONFIGURATION_Handle *config)
    607 {
    608   enum TALER_MHD_GlobalOptions go;
    609 
    610   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    611               "Starting anastasis-httpd\n");
    612   go = TALER_MHD_GO_NONE;
    613   if (AH_connection_close)
    614     go |= TALER_MHD_GO_FORCE_CONNECTION_CLOSE;
    615   AH_load_terms (config);
    616   TALER_MHD_setup (go);
    617   AH_cfg = config;
    618   global_result = EXIT_NO_RESTART;
    619   GNUNET_SCHEDULER_add_shutdown (&do_shutdown,
    620                                  NULL);
    621   if (GNUNET_OK !=
    622       GNUNET_CONFIGURATION_get_value_number (config,
    623                                              "anastasis",
    624                                              "UPLOAD_LIMIT_MB",
    625                                              &AH_upload_limit_mb))
    626   {
    627     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
    628                                "anastasis",
    629                                "UPLOAD_LIMIT_MB");
    630     GNUNET_SCHEDULER_shutdown ();
    631     return;
    632   }
    633   if (GNUNET_OK !=
    634       TALER_config_get_amount (config,
    635                                "anastasis",
    636                                "INSURANCE",
    637                                &AH_insurance))
    638   {
    639     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
    640                                "anastasis",
    641                                "INSURANCE");
    642     GNUNET_SCHEDULER_shutdown ();
    643     return;
    644   }
    645   if (GNUNET_OK !=
    646       TALER_config_get_amount (config,
    647                                "authorization-question",
    648                                "COST",
    649                                &AH_question_cost))
    650   {
    651     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
    652                                "authorization-question",
    653                                "COST");
    654     GNUNET_SCHEDULER_shutdown ();
    655     return;
    656   }
    657   if (GNUNET_OK !=
    658       TALER_config_get_amount (config,
    659                                "anastasis",
    660                                "ANNUAL_FEE",
    661                                &AH_annual_fee))
    662   {
    663     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
    664                                "anastasis",
    665                                "ANNUAL_FEE");
    666     GNUNET_SCHEDULER_shutdown ();
    667     return;
    668   }
    669   if (GNUNET_OK !=
    670       TALER_config_get_amount (config,
    671                                "anastasis",
    672                                "TRUTH_UPLOAD_FEE",
    673                                &AH_truth_upload_fee))
    674   {
    675     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
    676                                "anastasis",
    677                                "TRUTH_UPLOAD_FEE");
    678     GNUNET_SCHEDULER_shutdown ();
    679     return;
    680   }
    681   if (GNUNET_OK !=
    682       GNUNET_CONFIGURATION_get_value_string (config,
    683                                              "anastasis-merchant-backend",
    684                                              "PAYMENT_BACKEND_URL",
    685                                              &AH_backend_url))
    686   {
    687     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
    688                                "anastasis-merchant-backend",
    689                                "PAYMENT_BACKEND_URL");
    690     GNUNET_SCHEDULER_shutdown ();
    691     return;
    692   }
    693   if ( (0 != strncasecmp ("https://",
    694                           AH_backend_url,
    695                           strlen ("https://"))) &&
    696        (0 != strncasecmp ("http://",
    697                           AH_backend_url,
    698                           strlen ("http://"))) )
    699   {
    700     GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
    701                                "anastasis-merchant-backend",
    702                                "PAYMENT_BACKEND_URL",
    703                                "Must be HTTP(S) URL");
    704     GNUNET_SCHEDULER_shutdown ();
    705     return;
    706   }
    707 
    708   if ( (0 == strcasecmp ("https://",
    709                          AH_backend_url)) ||
    710        (0 == strcasecmp ("http://",
    711                          AH_backend_url)) )
    712   {
    713     GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
    714                                "anastasis-merchant-backend",
    715                                "PAYMENT_BACKEND_URL",
    716                                "Must have domain name");
    717     GNUNET_SCHEDULER_shutdown ();
    718     return;
    719   }
    720 
    721   if (GNUNET_OK !=
    722       GNUNET_CONFIGURATION_get_value_string (config,
    723                                              "anastasis",
    724                                              "FULFILLMENT_URL",
    725                                              &AH_fulfillment_url))
    726   {
    727     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
    728                                "anastasis",
    729                                "FULFILLMENT_URL");
    730     GNUNET_SCHEDULER_shutdown ();
    731     return;
    732   }
    733   if (GNUNET_OK !=
    734       GNUNET_CONFIGURATION_get_value_number (config,
    735                                              "anastasis",
    736                                              "ANNUAL_POLICY_UPLOAD_LIMIT",
    737                                              &AH_post_counter))
    738   {
    739     /* only warn, we will use the default */
    740     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_WARNING,
    741                                "anastasis",
    742                                "ANNUAL_POLICY_UPLOAD_LIMIT");
    743   }
    744 
    745   if (GNUNET_OK !=
    746       GNUNET_CONFIGURATION_get_value_string (config,
    747                                              "anastasis",
    748                                              "BUSINESS_NAME",
    749                                              &AH_business_name))
    750   {
    751     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
    752                                "anastasis",
    753                                "BUSINESS_NAME");
    754     GNUNET_SCHEDULER_shutdown ();
    755     return;
    756   }
    757   {
    758     char *provider_salt;
    759 
    760     if (GNUNET_OK !=
    761         GNUNET_CONFIGURATION_get_value_string (config,
    762                                                "anastasis",
    763                                                "PROVIDER_SALT",
    764                                                &provider_salt))
    765     {
    766       GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
    767                                  "anastasis",
    768                                  "PROVIDER_SALT");
    769       GNUNET_SCHEDULER_shutdown ();
    770       return;
    771     }
    772     GNUNET_assert (GNUNET_YES ==
    773                    GNUNET_CRYPTO_kdf (&AH_provider_salt,
    774                                       sizeof (AH_provider_salt),
    775                                       "anastasis-provider-salt",
    776                                       strlen ("anastasis-provider-salt"),
    777                                       provider_salt,
    778                                       strlen (provider_salt),
    779                                       NULL,
    780                                       0));
    781     GNUNET_free (provider_salt);
    782   }
    783 
    784   /* setup HTTP client event loop */
    785   AH_ctx = GNUNET_CURL_init (&GNUNET_CURL_gnunet_scheduler_reschedule,
    786                              &rc);
    787   rc = GNUNET_CURL_gnunet_rc_create (AH_ctx);
    788   if (NULL != userpass)
    789     GNUNET_CURL_set_userpass (AH_ctx,
    790                               userpass);
    791   if (NULL != keyfile)
    792     GNUNET_CURL_set_tlscert (AH_ctx,
    793                              certtype,
    794                              certfile,
    795                              keyfile,
    796                              keypass);
    797   if (NULL == apikey)
    798   {
    799     (void) GNUNET_CONFIGURATION_get_value_string (config,
    800                                                   "anastasis-merchant-backend",
    801                                                   "API_KEY",
    802                                                   &apikey);
    803   }
    804   if (NULL != apikey)
    805   {
    806     char *auth_header;
    807 
    808     GNUNET_asprintf (&auth_header,
    809                      "%s: %s",
    810                      MHD_HTTP_HEADER_AUTHORIZATION,
    811                      apikey);
    812     if (GNUNET_OK !=
    813         GNUNET_CURL_append_header (AH_ctx,
    814                                    auth_header))
    815     {
    816       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    817                   "Failed so set %s header, trying without\n",
    818                   MHD_HTTP_HEADER_AUTHORIZATION);
    819     }
    820     GNUNET_free (auth_header);
    821   }
    822 
    823   if (NULL ==
    824       (db = ANASTASIS_DB_plugin_load (config,
    825                                       false)))
    826   {
    827     GNUNET_SCHEDULER_shutdown ();
    828     return;
    829   }
    830   if (GNUNET_OK !=
    831       db->connect (db->cls))
    832   {
    833     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    834                 "Database not setup. Did you run anastasis-dbinit?\n");
    835     GNUNET_SCHEDULER_shutdown ();
    836     return;
    837   }
    838 
    839   {
    840     enum GNUNET_GenericReturnValue ret;
    841 
    842     ret = TALER_MHD_listen_bind (config,
    843                                  "anastasis",
    844                                  &start_daemon,
    845                                  NULL);
    846     switch (ret)
    847     {
    848     case GNUNET_SYSERR:
    849       global_result = EXIT_NOTCONFIGURED;
    850       GNUNET_SCHEDULER_shutdown ();
    851       return;
    852     case GNUNET_NO:
    853       if (! have_daemons)
    854       {
    855         global_result = EXIT_NOTCONFIGURED;
    856         GNUNET_SCHEDULER_shutdown ();
    857         return;
    858       }
    859       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    860                   "Could not open all configured listen sockets\n");
    861       break;
    862     case GNUNET_OK:
    863       break;
    864     }
    865   }
    866   global_result = EXIT_SUCCESS;
    867 }
    868 
    869 
    870 /**
    871  * The main function of the serve tool
    872  *
    873  * @param argc number of arguments from the command line
    874  * @param argv command line arguments
    875  * @return 0 ok, 1 on error
    876  */
    877 int
    878 main (int argc,
    879       char *const *argv)
    880 {
    881   enum GNUNET_GenericReturnValue res;
    882   struct GNUNET_GETOPT_CommandLineOption options[] = {
    883     GNUNET_GETOPT_option_string ('A',
    884                                  "auth",
    885                                  "USERNAME:PASSWORD",
    886                                  "use the given USERNAME and PASSWORD for client authentication",
    887                                  &userpass),
    888     GNUNET_GETOPT_option_flag ('C',
    889                                "connection-close",
    890                                "force HTTP connections to be closed after each request",
    891                                &AH_connection_close),
    892     GNUNET_GETOPT_option_string ('k',
    893                                  "key",
    894                                  "KEYFILE",
    895                                  "file with the private TLS key for TLS client authentication",
    896                                  &keyfile),
    897     GNUNET_GETOPT_option_string ('p',
    898                                  "pass",
    899                                  "KEYFILEPASSPHRASE",
    900                                  "passphrase needed to decrypt the TLS client private key file",
    901                                  &keypass),
    902     GNUNET_GETOPT_option_string ('K',
    903                                  "apikey",
    904                                  "APIKEY",
    905                                  "API key to use in the HTTP request to the merchant backend",
    906                                  &apikey),
    907     GNUNET_GETOPT_option_string ('t',
    908                                  "type",
    909                                  "CERTTYPE",
    910                                  "type of the TLS client certificate, defaults to PEM if not specified",
    911                                  &certtype),
    912     GNUNET_GETOPT_OPTION_END
    913   };
    914 
    915   res = GNUNET_PROGRAM_run (ANASTASIS_project_data (),
    916                             argc, argv,
    917                             "anastasis-httpd",
    918                             "Anastasis HTTP interface",
    919                             options, &run, NULL);
    920   if (GNUNET_SYSERR == res)
    921     return 3;
    922   if (GNUNET_NO == res)
    923     return 0;
    924   return global_result;
    925 }