anastasis

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

anastasis-httpd.c (28629B)


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