exchange

Base system with REST service to issue digital coins, run by the payment service provider
Log | Files | Refs | Submodules | README | LICENSE

taler-auditor-httpd.c (53363B)


      1 /*
      2   This file is part of TALER
      3   Copyright (C) 2014-2024 Taler Systems SA
      4 
      5   TALER 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   TALER 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   TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
     15 */
     16 
     17 /**
     18  * @file taler-auditor-httpd.c
     19  * @brief Serve the HTTP interface of the auditor
     20  * @defgroup request Request handling routines
     21  * @author Florian Dold
     22  * @author Benedikt Mueller
     23  * @author Christian Grothoff
     24  */
     25 #include "platform.h"
     26 #include <gnunet/gnunet_util_lib.h>
     27 #include <jansson.h>
     28 #include <microhttpd.h>
     29 #include <pthread.h>
     30 #include <sys/resource.h>
     31 #include "taler/taler_mhd_lib.h"
     32 #include "auditordb_lib.h"
     33 #include "exchangedb_lib.h"
     34 #include "taler-auditor-httpd_spa.h"
     35 #include "taler-auditor-httpd_put-deposit-confirmation.h"
     36 #include "taler-auditor-httpd_get-monitoring-deposit-confirmations.h"
     37 #include "taler-auditor-httpd_get-monitoring-amount-arithmetic-inconsistency.h"
     38 #include "taler-auditor-httpd_get-monitoring-coin-inconsistency.h"
     39 #include "taler-auditor-httpd_get-monitoring-row-inconsistency.h"
     40 #include "taler-auditor-httpd_get-monitoring-emergency.h"
     41 #include "taler-auditor-httpd_get-monitoring-emergency-by-count.h"
     42 #include "taler-auditor-httpd_get-monitoring-early-aggregation.h"
     43 #include                                                                \
     44   "taler-auditor-httpd_get-monitoring-denomination-key-validity-withdraw-inconsistency.h"
     45 #include "taler-auditor-httpd_get-monitoring-purse-not-closed-inconsistencies.h"
     46 #include \
     47   "taler-auditor-httpd_get-monitoring-reserve-balance-insufficient-inconsistency.h"
     48 #include "taler-auditor-httpd_get-monitoring-bad-sig-losses.h"
     49 #include "taler-auditor-httpd_get-monitoring-aml-holds.h"
     50 #include "taler-auditor-httpd_get-monitoring-closure-lags.h"
     51 #include "taler-auditor-httpd_mhd.h"
     52 #include "taler-auditor-httpd.h"
     53 #include "taler-auditor-httpd_delete-generic.h"
     54 #include "taler-auditor-httpd_patch-generic-suppressed.h"
     55 #include "taler-auditor-httpd_get-monitoring-kycauth-in-inconsistency.h"
     56 #include "taler-auditor-httpd_get-monitoring-reserve-in-inconsistency.h"
     57 #include "taler-auditor-httpd_get-monitoring-reserve-not-closed-inconsistency.h"
     58 #include "taler-auditor-httpd_get-monitoring-denominations-without-sigs.h"
     59 #include "taler-auditor-httpd_get-monitoring-misattribution-in-inconsistency.h"
     60 #include "taler-auditor-httpd_get-monitoring-reserves.h"
     61 #include "taler-auditor-httpd_get-monitoring-pending-deposits.h"
     62 #include "taler-auditor-httpd_get-monitoring-purses.h"
     63 #include "taler-auditor-httpd_get-monitoring-historic-denomination-revenue.h"
     64 #include "taler-auditor-httpd_get-monitoring-historic-reserve-summary.h"
     65 #include "taler-auditor-httpd_get-monitoring-denomination-pending.h"
     66 #include "taler-auditor-httpd_get-monitoring-wire-format-inconsistency.h"
     67 #include "taler-auditor-httpd_get-monitoring-wire-out-inconsistency.h"
     68 #include \
     69   "taler-auditor-httpd_get-monitoring-reserve-balance-summary-wrong-inconsistency.h"
     70 #include "taler-auditor-httpd_get-monitoring-row-minor-inconsistencies.h"
     71 #include "taler-auditor-httpd_get-monitoring-fee-time-inconsistency.h"
     72 #include "taler-auditor-httpd_get-monitoring-balances.h"
     73 #include "taler-auditor-httpd_get-monitoring-progress.h"
     74 #include "exchange-database/preflight.h"
     75 
     76 /**
     77  * Auditor protocol version string.
     78  *
     79  * Taler protocol version in the format CURRENT:REVISION:AGE
     80  * as used by GNU libtool.  See
     81  * https://www.gnu.org/software/libtool/manual/html_node/Libtool-versioning.html
     82  *
     83  * Please be very careful when updating and follow
     84  * https://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html#Updating-version-info
     85  * precisely.  Note that this version has NOTHING to do with the
     86  * release version, and the format is NOT the same that semantic
     87  * versioning uses either.
     88  */
     89 #define AUDITOR_PROTOCOL_VERSION "1:0:1"
     90 
     91 /**
     92  * Salt we use when doing the KDF for access.
     93  */
     94 #define KDF_SALT "auditor-standard-auth"
     95 
     96 /**
     97  * Backlog for listen operation on unix domain sockets.
     98  */
     99 #define UNIX_BACKLOG 500
    100 
    101 /**
    102  * Should we return "Connection: close" in each response?
    103  */
    104 static int auditor_connection_close;
    105 
    106 /**
    107  * The auditor's configuration.
    108  */
    109 static const struct GNUNET_CONFIGURATION_Handle *cfg;
    110 
    111 /**
    112  * Our auditor database context.
    113  */
    114 struct TALER_AUDITORDB_PostgresContext *TAH_apg;
    115 
    116 /**
    117  * Our exchange database context.
    118  */
    119 struct TALER_EXCHANGEDB_PostgresContext *TAH_epg;
    120 
    121 /**
    122  * Public key of this auditor.
    123  */
    124 static struct TALER_AuditorPublicKeyP auditor_pub;
    125 
    126 /**
    127  * Exchange master public key (according to the
    128  * configuration).  (global)
    129  */
    130 struct TALER_MasterPublicKeyP TAH_master_public_key;
    131 
    132 /**
    133  * Exchange master public key (according to the
    134  * configuration).  (global)
    135  */
    136 struct TALER_MasterPublicKeyP TAH_master_public_key;
    137 
    138 char *TAH_spa_dir;
    139 
    140 /**
    141  * Default timeout in seconds for HTTP requests.
    142  */
    143 static unsigned int connection_timeout = 30;
    144 
    145 /**
    146  * Return value from main()
    147  */
    148 static int global_ret;
    149 
    150 /**
    151  * Disables authentication checks.
    152  */
    153 static int disable_auth;
    154 
    155 /**
    156  * True if we started any HTTP daemon.
    157  */
    158 static bool have_daemons;
    159 
    160 /**
    161  * Our currency.
    162  */
    163 char *TAH_currency;
    164 
    165 /**
    166  * Authorization code to use.
    167  */
    168 static struct GNUNET_HashCode TAH_auth;
    169 
    170 /**
    171  * Prefix required for the access token.
    172  */
    173 #define RFC_8959_PREFIX "secret-token:"
    174 
    175 
    176 /**
    177  * Function called whenever MHD is done with a request.  If the
    178  * request was a POST, we may have stored a `struct Buffer *` in the
    179  * @a con_cls that might still need to be cleaned up.  Call the
    180  * respective function to free the memory.
    181  *
    182  * @param cls client-defined closure
    183  * @param connection connection handle
    184  * @param con_cls value as set by the last call to
    185  *        the #MHD_AccessHandlerCallback
    186  * @param toe reason for request termination
    187  * @see #MHD_OPTION_NOTIFY_COMPLETED
    188  * @ingroup request
    189  */
    190 static void
    191 handle_mhd_completion_callback (void *cls,
    192                                 struct MHD_Connection *connection,
    193                                 void **con_cls,
    194                                 enum MHD_RequestTerminationCode toe)
    195 {
    196   (void) cls;
    197   (void) connection;
    198   (void) toe;
    199   if (NULL == *con_cls)
    200     return;
    201   TALER_MHD_parse_post_cleanup_callback (*con_cls);
    202   *con_cls = NULL;
    203 }
    204 
    205 
    206 /**
    207  * Handle a "/config" request.
    208  *
    209  * @param rh context of the handler
    210  * @param connection the MHD connection to handle
    211  * @param[in,out] connection_cls the connection's closure (can be updated)
    212  * @param upload_data upload data
    213  * @param[in,out] upload_data_size number of bytes (left) in @a upload_data
    214  * @param args NULL-terminated array of remaining parts of the URI broken up at '/'
    215  * @return MHD result code
    216  */
    217 static enum MHD_Result
    218 handle_config (struct TAH_RequestHandler *rh,
    219                struct MHD_Connection *connection,
    220                void **connection_cls,
    221                const char *upload_data,
    222                size_t *upload_data_size,
    223                const char *const args[])
    224 {
    225   static json_t *ver; /* we build the response only once, keep around for next query! */
    226 
    227   (void) rh;
    228   (void) upload_data;
    229   (void) upload_data_size;
    230   (void) connection_cls;
    231   if (NULL == ver)
    232   {
    233     ver = GNUNET_JSON_PACK (
    234       GNUNET_JSON_pack_string ("name",
    235                                "taler-auditor"),
    236       GNUNET_JSON_pack_string ("version",
    237                                AUDITOR_PROTOCOL_VERSION),
    238       GNUNET_JSON_pack_string ("implementation",
    239                                "urn:net:taler:specs:taler-auditor:c-reference"),
    240       GNUNET_JSON_pack_string ("currency",
    241                                TAH_currency),
    242       GNUNET_JSON_pack_data_auto ("auditor_public_key",
    243                                   &auditor_pub),
    244       GNUNET_JSON_pack_data_auto ("exchange_master_public_key",
    245                                   &TAH_master_public_key));
    246   }
    247   if (NULL == ver)
    248   {
    249     GNUNET_break (0);
    250     return MHD_NO;
    251   }
    252   return TALER_MHD_reply_json (connection,
    253                                ver,
    254                                MHD_HTTP_OK);
    255 }
    256 
    257 
    258 /**
    259  * Extract the token from authorization header value @a auth.
    260  *
    261  * @param auth pointer to authorization header value,
    262  *        will be updated to point to the start of the token
    263  *        or set to NULL if header value is invalid
    264  */
    265 static void
    266 extract_token (const char **auth)
    267 {
    268   const char *bearer = "Bearer ";
    269   const char *tok = *auth;
    270 
    271   if (0 != strncmp (tok,
    272                     bearer,
    273                     strlen (bearer)))
    274   {
    275     *auth = NULL;
    276     return;
    277   }
    278   tok += strlen (bearer);
    279   while (' ' == *tok)
    280     tok++;
    281   if (0 != strncasecmp (tok,
    282                         RFC_8959_PREFIX,
    283                         strlen (RFC_8959_PREFIX)))
    284   {
    285     *auth = NULL;
    286     return;
    287   }
    288   *auth = tok;
    289 }
    290 
    291 
    292 static enum GNUNET_GenericReturnValue
    293 check_auth (const char *token)
    294 {
    295   struct GNUNET_HashCode val;
    296 
    297   if (NULL == token)
    298     return GNUNET_SYSERR;
    299   token += strlen (RFC_8959_PREFIX);
    300   GNUNET_assert (GNUNET_YES ==
    301                  GNUNET_CRYPTO_hkdf_gnunet (
    302                    &val,
    303                    sizeof (val),
    304                    KDF_SALT,
    305                    strlen (KDF_SALT),
    306                    token,
    307                    strlen (token)));
    308   /* We compare hashes instead of directly comparing
    309      tokens to minimize side-channel attacks on token length */
    310   return (0 ==
    311           GNUNET_memcmp_priv (&val,
    312                               &TAH_auth))
    313            ? GNUNET_OK
    314            : GNUNET_SYSERR;
    315 }
    316 
    317 
    318 /**
    319  * Handle incoming HTTP request.
    320  *
    321  * @param cls closure for MHD daemon (unused)
    322  * @param connection the connection
    323  * @param url the requested url
    324  * @param method the method (POST, GET, ...)
    325  * @param version HTTP version (ignored)
    326  * @param upload_data request data
    327  * @param upload_data_size size of @a upload_data in bytes
    328  * @param con_cls closure for request (a `struct Buffer *`)
    329  * @return MHD result code
    330  */
    331 static enum MHD_Result
    332 handle_mhd_request (void *cls,
    333                     struct MHD_Connection *connection,
    334                     const char *url,
    335                     const char *method,
    336                     const char *version,
    337                     const char *upload_data,
    338                     size_t *upload_data_size,
    339                     void **con_cls)
    340 {
    341   static struct TAH_RequestHandler handlers[] = {
    342     /* Our most popular handler (thus first!), used by merchants to
    343        probabilistically report us their deposit confirmations. */
    344     { .url = "/deposit-confirmation",
    345       .method = MHD_HTTP_METHOD_PUT,
    346       .mime_type = "application/json",
    347       .handler = &TAH_put_deposit_confirmation,
    348       .response_code = MHD_HTTP_OK},
    349     { .url = "/spa",
    350       .method = MHD_HTTP_METHOD_GET,
    351       .handler = &TAH_spa_handler},
    352     { .url = "/monitoring/deposit-confirmation",
    353       .method = MHD_HTTP_METHOD_GET,
    354       .mime_type = "application/json",
    355       .data = NULL,
    356       .data_size = 0,
    357       .handler = &TAH_get_monitoring_deposit_confirmations,
    358       .response_code = MHD_HTTP_OK,
    359       .requires_auth = true },
    360     { .url = "/monitoring/pending-deposits",
    361       .method = MHD_HTTP_METHOD_GET,
    362       .mime_type = "application/json",
    363       .data = NULL,
    364       .data_size = 0,
    365       .handler = &TAH_get_monitoring_pending_deposits,
    366       .response_code = MHD_HTTP_OK,
    367       .requires_auth = true },
    368     { .url = "/monitoring/early-aggregation",
    369       .method = MHD_HTTP_METHOD_GET,
    370       .mime_type = "application/json",
    371       .data = NULL,
    372       .data_size = 0,
    373       .handler = &TAH_get_monitoring_early_aggregation,
    374       .response_code = MHD_HTTP_OK,
    375       .requires_auth = true },
    376     { .url = "/monitoring/deposit-confirmation",
    377       .method = MHD_HTTP_METHOD_DELETE,
    378       .mime_type = "application/json",
    379       .data = NULL,
    380       .data_size = 0,
    381       .handler = &TAH_delete_generic,
    382       .response_code = MHD_HTTP_OK,
    383       .requires_auth = true,
    384       .table = TALER_AUDITORDB_DEPOSIT_CONFIRMATION },
    385     { .url = "/monitoring/amount-arithmetic-inconsistency",
    386       .method = MHD_HTTP_METHOD_GET,
    387       .mime_type = "application/json",
    388       .data = NULL,
    389       .data_size = 0,
    390       .handler = &TAH_get_monitoring_amount_arithmetic_inconsistency,
    391       .response_code = MHD_HTTP_OK,
    392       .requires_auth = true },
    393     { .url = "/monitoring/amount-arithmetic-inconsistency",
    394       .method = MHD_HTTP_METHOD_DELETE,
    395       .mime_type = "application/json",
    396       .data = NULL,
    397       .data_size = 0,
    398       .handler = &TAH_delete_generic,
    399       .response_code = MHD_HTTP_OK,
    400       .requires_auth = true,
    401       .table = TALER_AUDITORDB_AMOUNT_ARITHMETIC_INCONSISTENCY },
    402     { .url = "/monitoring/amount-arithmetic-inconsistency",
    403       .method = MHD_HTTP_METHOD_PATCH,
    404       .mime_type = "application/json",
    405       .data = NULL,
    406       .data_size = 0,
    407       .handler = &TAH_patch_generic_suppressed,
    408       .response_code = MHD_HTTP_OK,
    409       .requires_auth = true,
    410       .table = TALER_AUDITORDB_AMOUNT_ARITHMETIC_INCONSISTENCY },
    411     { .url = "/monitoring/coin-inconsistency",
    412       .method = MHD_HTTP_METHOD_GET,
    413       .mime_type = "application/json",
    414       .data = NULL,
    415       .data_size = 0,
    416       .handler = &TAH_get_monitoring_coin_inconsistency,
    417       .response_code = MHD_HTTP_OK,
    418       .requires_auth = true },
    419     { .url = "/monitoring/coin-inconsistency",
    420       .method = MHD_HTTP_METHOD_DELETE,
    421       .mime_type = "application/json",
    422       .data = NULL,
    423       .data_size = 0,
    424       .handler = &TAH_delete_generic,
    425       .response_code = MHD_HTTP_OK,
    426       .requires_auth = true,
    427       .table = TALER_AUDITORDB_COIN_INCONSISTENCY },
    428     { .url = "/monitoring/coin-inconsistency",
    429       .method = MHD_HTTP_METHOD_PATCH,
    430       .mime_type = "application/json",
    431       .data = NULL,
    432       .data_size = 0,
    433       .handler = &TAH_patch_generic_suppressed,
    434       .response_code = MHD_HTTP_OK,
    435       .requires_auth = true,
    436       .table = TALER_AUDITORDB_COIN_INCONSISTENCY },
    437     { .url = "/monitoring/row-inconsistency",
    438       .method = MHD_HTTP_METHOD_GET,
    439       .mime_type = "application/json",
    440       .data = NULL,
    441       .data_size = 0,
    442       .handler = &TAH_get_monitoring_row_inconsistency,
    443       .response_code = MHD_HTTP_OK,
    444       .requires_auth = true },
    445     { .url = "/monitoring/row-inconsistency",
    446       .method = MHD_HTTP_METHOD_DELETE,
    447       .mime_type = "application/json",
    448       .data = NULL,
    449       .data_size = 0,
    450       .handler = &TAH_delete_generic,
    451       .response_code = MHD_HTTP_OK,
    452       .requires_auth = true,
    453       .table = TALER_AUDITORDB_ROW_INCONSISTENCY},
    454     { .url = "/monitoring/row-inconsistency",
    455       .method = MHD_HTTP_METHOD_PATCH,
    456       .mime_type = "application/json",
    457       .data = NULL,
    458       .data_size = 0,
    459       .handler = &TAH_patch_generic_suppressed,
    460       .response_code = MHD_HTTP_OK,
    461       .requires_auth = true,
    462       .table = TALER_AUDITORDB_ROW_INCONSISTENCY },
    463     { .url = "/monitoring/bad-sig-losses",
    464       .method = MHD_HTTP_METHOD_GET,
    465       .mime_type = "application/json",
    466       .data = NULL,
    467       .data_size = 0,
    468       .handler = &TAH_get_monitoring_bad_sig_losses,
    469       .response_code = MHD_HTTP_OK,
    470       .requires_auth = true },
    471     { .url = "/monitoring/bad-sig-losses",
    472       .method = MHD_HTTP_METHOD_DELETE,
    473       .mime_type = "application/json",
    474       .data = NULL,
    475       .data_size = 0,
    476       .handler = &TAH_delete_generic,
    477       .response_code = MHD_HTTP_OK,
    478       .requires_auth = true,
    479       .table = TALER_AUDITORDB_BAD_SIG_LOSSES},
    480     { .url = "/monitoring/bad-sig-losses",
    481       .method = MHD_HTTP_METHOD_PATCH,
    482       .mime_type = "application/json",
    483       .data = NULL,
    484       .data_size = 0,
    485       .handler = &TAH_patch_generic_suppressed,
    486       .response_code = MHD_HTTP_OK,
    487       .requires_auth = true,
    488       .table = TALER_AUDITORDB_BAD_SIG_LOSSES },
    489     { .url = "/monitoring/aml-holds",
    490       .method = MHD_HTTP_METHOD_GET,
    491       .mime_type = "application/json",
    492       .data = NULL,
    493       .data_size = 0,
    494       .handler = &TAH_get_monitoring_aml_holds,
    495       .response_code = MHD_HTTP_OK,
    496       .requires_auth = true },
    497     { .url = "/monitoring/aml-holds",
    498       .method = MHD_HTTP_METHOD_DELETE,
    499       .mime_type = "application/json",
    500       .data = NULL,
    501       .data_size = 0,
    502       .handler = &TAH_delete_generic,
    503       .response_code = MHD_HTTP_OK,
    504       .requires_auth = true,
    505       .table = TALER_AUDITORDB_AML_HOLDS },
    506     { .url = "/monitoring/aml-holds",
    507       .method = MHD_HTTP_METHOD_PATCH,
    508       .mime_type = "application/json",
    509       .data = NULL,
    510       .data_size = 0,
    511       .handler = &TAH_patch_generic_suppressed,
    512       .response_code = MHD_HTTP_OK,
    513       .requires_auth = true,
    514       .table = TALER_AUDITORDB_AML_HOLDS },
    515     { .url = "/monitoring/closure-lags",
    516       .method = MHD_HTTP_METHOD_GET,
    517       .mime_type = "application/json",
    518       .data = NULL,
    519       .data_size = 0,
    520       .handler = &TAH_get_monitoring_closure_lags,
    521       .response_code = MHD_HTTP_OK,
    522       .requires_auth = true },
    523     { .url = "/monitoring/closure-lags",
    524       .method = MHD_HTTP_METHOD_DELETE,
    525       .mime_type = "application/json",
    526       .data = NULL,
    527       .data_size = 0,
    528       .handler = &TAH_delete_generic,
    529       .response_code = MHD_HTTP_OK,
    530       .requires_auth = true,
    531       .table = TALER_AUDITORDB_CLOSURE_LAGS },
    532     { .url = "/monitoring/closure-lags",
    533       .method = MHD_HTTP_METHOD_PATCH,
    534       .mime_type = "application/json",
    535       .data = NULL,
    536       .data_size = 0,
    537       .handler = &TAH_patch_generic_suppressed,
    538       .response_code = MHD_HTTP_OK,
    539       .requires_auth = true,
    540       .table = TALER_AUDITORDB_CLOSURE_LAGS },
    541     { .url = "/monitoring/emergency",
    542       .method = MHD_HTTP_METHOD_GET,
    543       .mime_type = "application/json",
    544       .data = NULL,
    545       .data_size = 0,
    546       .handler = &TAH_get_monitoring_emergency,
    547       .response_code = MHD_HTTP_OK,
    548       .requires_auth = true },
    549     { .url = "/monitoring/emergency",
    550       .method = MHD_HTTP_METHOD_DELETE,
    551       .mime_type = "application/json",
    552       .data = NULL,
    553       .data_size = 0,
    554       .handler = &TAH_delete_generic,
    555       .response_code = MHD_HTTP_OK,
    556       .requires_auth = true,
    557       .table = TALER_AUDITORDB_EMERGENCY },
    558     { .url = "/monitoring/emergency",
    559       .method = MHD_HTTP_METHOD_PATCH,
    560       .mime_type = "application/json",
    561       .data = NULL,
    562       .data_size = 0,
    563       .handler = &TAH_patch_generic_suppressed,
    564       .response_code = MHD_HTTP_OK,
    565       .requires_auth = true,
    566       .table = TALER_AUDITORDB_EMERGENCY  },
    567     { .url = "/monitoring/denomination-key-validity-withdraw-inconsistency",
    568       .method = MHD_HTTP_METHOD_GET,
    569       .mime_type = "application/json",
    570       .data = NULL,
    571       .data_size = 0,
    572       .handler =
    573         &TAH_get_monitoring_denomination_key_validity_withdraw_inconsistency,
    574       .response_code = MHD_HTTP_OK,
    575       .requires_auth = true },
    576     { .url = "/monitoring/denomination-key-validity-withdraw-inconsistency",
    577       .method = MHD_HTTP_METHOD_DELETE,
    578       .mime_type = "application/json",
    579       .data = NULL,
    580       .data_size = 0,
    581       .handler = &TAH_delete_generic,
    582       .response_code = MHD_HTTP_OK,
    583       .requires_auth = true,
    584       .table = TALER_AUDITORDB_DENOMINATION_KEY_VALIDITY_WITHDRAW_INCONSISTENCY}
    585     ,
    586     { .url = "/monitoring/denomination-key-validity-withdraw-inconsistency",
    587       .method = MHD_HTTP_METHOD_PATCH,
    588       .mime_type = "application/json",
    589       .data = NULL,
    590       .data_size = 0,
    591       .handler = &TAH_patch_generic_suppressed,
    592       .response_code = MHD_HTTP_OK,
    593       .requires_auth = true,
    594       .table = TALER_AUDITORDB_DENOMINATION_KEY_VALIDITY_WITHDRAW_INCONSISTENCY}
    595     ,
    596     { .url = "/monitoring/reserve-balance-insufficient-inconsistency",
    597       .method = MHD_HTTP_METHOD_GET,
    598       .mime_type = "application/json",
    599       .data = NULL,
    600       .data_size = 0,
    601       .handler = &TAH_get_monitoring_reserve_balance_insufficient_inconsistency,
    602       .response_code = MHD_HTTP_OK,
    603       .requires_auth = true },
    604     { .url = "/monitoring/reserve-balance-insufficient-inconsistency",
    605       .method = MHD_HTTP_METHOD_DELETE,
    606       .mime_type = "application/json",
    607       .data = NULL,
    608       .data_size = 0,
    609       .handler = &TAH_delete_generic,
    610       .response_code = MHD_HTTP_OK,
    611       .requires_auth = true,
    612       .table = TALER_AUDITORDB_RESERVE_BALANCE_INSUFFICIENT_INCONSISTENCY },
    613     { .url = "/monitoring/reserve-balance-insufficient-inconsistency",
    614       .method = MHD_HTTP_METHOD_PATCH,
    615       .mime_type = "application/json",
    616       .data = NULL,
    617       .data_size = 0,
    618       .handler = &TAH_patch_generic_suppressed,
    619       .response_code = MHD_HTTP_OK,
    620       .requires_auth = true,
    621       .table = TALER_AUDITORDB_RESERVE_BALANCE_INSUFFICIENT_INCONSISTENCY },
    622     { .url = "/monitoring/purse-not-closed-inconsistencies",
    623       .method = MHD_HTTP_METHOD_GET,
    624       .mime_type = "application/json",
    625       .data = NULL,
    626       .data_size = 0,
    627       .handler = &TAH_get_monitoring_purse_not_closed_inconsistencies,
    628       .response_code = MHD_HTTP_OK,
    629       .requires_auth = true },
    630     { .url = "/monitoring/purse-not-closed-inconsistencies",
    631       .method = MHD_HTTP_METHOD_DELETE,
    632       .mime_type = "application/json",
    633       .data = NULL,
    634       .data_size = 0,
    635       .handler = &TAH_delete_generic,
    636       .response_code = MHD_HTTP_OK,
    637       .requires_auth = true,
    638       .table = TALER_AUDITORDB_PURSE_NOT_CLOSED_INCONSISTENCY },
    639     { .url = "/monitoring/purse-not-closed-inconsistencies",
    640       .method = MHD_HTTP_METHOD_PATCH,
    641       .mime_type = "application/json",
    642       .data = NULL,
    643       .data_size = 0,
    644       .handler = &TAH_patch_generic_suppressed,
    645       .response_code = MHD_HTTP_OK,
    646       .requires_auth = true,
    647       .table = TALER_AUDITORDB_PURSE_NOT_CLOSED_INCONSISTENCY  },
    648     { .url = "/monitoring/emergency-by-count",
    649       .method = MHD_HTTP_METHOD_GET,
    650       .mime_type = "application/json",
    651       .data = NULL,
    652       .data_size = 0,
    653       .handler = &TAH_get_monitoring_emergency_by_count,
    654       .response_code = MHD_HTTP_OK,
    655       .requires_auth = true },
    656     { .url = "/monitoring/emergency-by-count",
    657       .method = MHD_HTTP_METHOD_DELETE,
    658       .mime_type = "application/json",
    659       .data = NULL,
    660       .data_size = 0,
    661       .handler = &TAH_delete_generic,
    662       .response_code = MHD_HTTP_OK,
    663       .requires_auth = true,
    664       .table = TALER_AUDITORDB_EMERGENCY_BY_COUNT },
    665     { .url = "/monitoring/emergency-by-count",
    666       .method = MHD_HTTP_METHOD_PATCH,
    667       .mime_type = "application/json",
    668       .data = NULL,
    669       .data_size = 0,
    670       .handler = &TAH_patch_generic_suppressed,
    671       .response_code = MHD_HTTP_OK,
    672       .requires_auth = true,
    673       .table = TALER_AUDITORDB_EMERGENCY_BY_COUNT },
    674     { .url = "/monitoring/reserve-in-inconsistency",
    675       .method = MHD_HTTP_METHOD_GET,
    676       .mime_type = "application/json",
    677       .data = NULL,
    678       .data_size = 0,
    679       .handler = &TAH_get_monitoring_reserve_in_inconsistency,
    680       .response_code = MHD_HTTP_OK,
    681       .requires_auth = true },
    682     { .url = "/monitoring/reserve-in-inconsistency",
    683       .method = MHD_HTTP_METHOD_DELETE,
    684       .mime_type = "application/json",
    685       .data = NULL,
    686       .data_size = 0,
    687       .handler = &TAH_delete_generic,
    688       .response_code = MHD_HTTP_OK,
    689       .requires_auth = true,
    690       .table = TALER_AUDITORDB_RESERVE_IN_INCONSISTENCY },
    691     { .url = "/monitoring/reserve-in-inconsistency",
    692       .method = MHD_HTTP_METHOD_PATCH,
    693       .mime_type = "application/json",
    694       .data = NULL,
    695       .data_size = 0,
    696       .handler = &TAH_patch_generic_suppressed,
    697       .response_code = MHD_HTTP_OK,
    698       .requires_auth = true,
    699       .table = TALER_AUDITORDB_RESERVE_IN_INCONSISTENCY  },
    700     { .url = "/monitoring/kycauth-in-inconsistency",
    701       .method = MHD_HTTP_METHOD_GET,
    702       .mime_type = "application/json",
    703       .data = NULL,
    704       .data_size = 0,
    705       .handler = &TAH_get_monitoring_kycauth_in_inconsistency,
    706       .response_code = MHD_HTTP_OK,
    707       .requires_auth = true },
    708     { .url = "/monitoring/kycauth-in-inconsistency",
    709       .method = MHD_HTTP_METHOD_DELETE,
    710       .mime_type = "application/json",
    711       .data = NULL,
    712       .data_size = 0,
    713       .handler = &TAH_delete_generic,
    714       .response_code = MHD_HTTP_OK,
    715       .requires_auth = true,
    716       .table = TALER_AUDITORDB_KYCAUTH_IN_INCONSISTENCY },
    717     { .url = "/monitoring/kycauth-in-inconsistency",
    718       .method = MHD_HTTP_METHOD_PATCH,
    719       .mime_type = "application/json",
    720       .data = NULL,
    721       .data_size = 0,
    722       .handler = &TAH_patch_generic_suppressed,
    723       .response_code = MHD_HTTP_OK,
    724       .requires_auth = true,
    725       .table = TALER_AUDITORDB_KYCAUTH_IN_INCONSISTENCY  },
    726     { .url = "/monitoring/reserve-not-closed-inconsistency",
    727       .method = MHD_HTTP_METHOD_GET,
    728       .mime_type = "application/json",
    729       .data = NULL,
    730       .data_size = 0,
    731       .handler = &TAH_get_monitoring_reserve_not_closed_inconsistency,
    732       .response_code = MHD_HTTP_OK,
    733       .requires_auth = true },
    734     { .url = "/monitoring/reserve-not-closed-inconsistency",
    735       .method = MHD_HTTP_METHOD_DELETE,
    736       .mime_type = "application/json",
    737       .data = NULL,
    738       .data_size = 0,
    739       .handler = &TAH_delete_generic,
    740       .response_code = MHD_HTTP_OK,
    741       .requires_auth = true,
    742       .table = TALER_AUDITORDB_RESERVE_NOT_CLOSED_INCONSISTENCY },
    743     { .url = "/monitoring/reserve-not-closed-inconsistency",
    744       .method = MHD_HTTP_METHOD_PATCH,
    745       .mime_type = "application/json",
    746       .data = NULL,
    747       .data_size = 0,
    748       .handler = &TAH_patch_generic_suppressed,
    749       .response_code = MHD_HTTP_OK,
    750       .requires_auth = true,
    751       .table = TALER_AUDITORDB_RESERVE_NOT_CLOSED_INCONSISTENCY },
    752     { .url = "/monitoring/denominations-without-sigs",
    753       .method = MHD_HTTP_METHOD_GET,
    754       .mime_type = "application/json",
    755       .data = NULL,
    756       .data_size = 0,
    757       .handler = &TAH_get_monitoring_denominations_without_sigs,
    758       .response_code = MHD_HTTP_OK,
    759       .requires_auth = true },
    760     { .url = "/monitoring/denominations-without-sigs",
    761       .method = MHD_HTTP_METHOD_DELETE,
    762       .mime_type = "application/json",
    763       .data = NULL,
    764       .data_size = 0,
    765       .handler = &TAH_delete_generic,
    766       .response_code = MHD_HTTP_OK,
    767       .requires_auth = true,
    768       .table = TALER_AUDITORDB_DENOMINATIONS_WITHOUT_SIG },
    769     { .url = "/monitoring/denominations-without-sigs",
    770       .method = MHD_HTTP_METHOD_PATCH,
    771       .mime_type = "application/json",
    772       .data = NULL,
    773       .data_size = 0,
    774       .handler = &TAH_patch_generic_suppressed,
    775       .response_code = MHD_HTTP_OK,
    776       .requires_auth = true,
    777       .table = TALER_AUDITORDB_DENOMINATIONS_WITHOUT_SIG },
    778     { .url = "/monitoring/misattribution-in-inconsistency",
    779       .method = MHD_HTTP_METHOD_GET,
    780       .mime_type = "application/json",
    781       .data = NULL,
    782       .data_size = 0,
    783       .handler = &TAH_get_monitoring_misattribution_in_inconsistency,
    784       .response_code = MHD_HTTP_OK,
    785       .requires_auth = true },
    786     { .url = "/monitoring/misattribution-in-inconsistency",
    787       .method = MHD_HTTP_METHOD_DELETE,
    788       .mime_type = "application/json",
    789       .data = NULL,
    790       .data_size = 0,
    791       .handler = &TAH_delete_generic,
    792       .response_code = MHD_HTTP_OK,
    793       .requires_auth = true,
    794       .table = TALER_AUDITORDB_MISATTRIBUTION_IN_INCONSISTENCY },
    795     { .url = "/monitoring/misattribution-in-inconsistency",
    796       .method = MHD_HTTP_METHOD_PATCH,
    797       .mime_type = "application/json",
    798       .data = NULL,
    799       .data_size = 0,
    800       .handler = &TAH_patch_generic_suppressed,
    801       .response_code = MHD_HTTP_OK,
    802       .requires_auth = true,
    803       .table = TALER_AUDITORDB_MISATTRIBUTION_IN_INCONSISTENCY },
    804     { .url = "/monitoring/reserves",
    805       .method = MHD_HTTP_METHOD_GET,
    806       .mime_type = "application/json",
    807       .data = NULL,
    808       .data_size = 0,
    809       .handler = &TAH_get_monitoring_reserves,
    810       .response_code = MHD_HTTP_OK,
    811       .requires_auth = true },
    812     { .url = "/monitoring/purses",
    813       .method = MHD_HTTP_METHOD_GET,
    814       .mime_type = "application/json",
    815       .data = NULL,
    816       .data_size = 0,
    817       .handler = &TAH_get_monitoring_purses,
    818       .response_code = MHD_HTTP_OK,
    819       .requires_auth = true },
    820     { .url = "/monitoring/historic-denomination-revenue",
    821       .method = MHD_HTTP_METHOD_GET,
    822       .mime_type = "application/json",
    823       .data = NULL,
    824       .data_size = 0,
    825       .handler = &TAH_get_monitoring_historic_denomination_revenue,
    826       .response_code = MHD_HTTP_OK,
    827       .requires_auth = true },
    828     { .url = "/monitoring/denomination-pending",
    829       .method = MHD_HTTP_METHOD_GET,
    830       .mime_type = "application/json",
    831       .data = NULL,
    832       .data_size = 0,
    833       .handler = &TAH_get_monitoring_denomination_pending,
    834       .response_code = MHD_HTTP_OK,
    835       .requires_auth = true },
    836     { .url = "/monitoring/denomination-pending",
    837       .method = MHD_HTTP_METHOD_DELETE,
    838       .mime_type = "application/json",
    839       .data = NULL,
    840       .data_size = 0,
    841       .handler = &TAH_delete_generic,
    842       .response_code = MHD_HTTP_OK,
    843       .requires_auth = true,
    844       .table = TALER_AUDITORDB_DENOMINATION_PENDING },
    845     { .url = "/monitoring/historic-reserve-summary",
    846       .method = MHD_HTTP_METHOD_GET,
    847       .mime_type = "application/json",
    848       .data = NULL,
    849       .data_size = 0,
    850       .handler = &TAH_get_monitoring_historic_reserve_summary,
    851       .response_code = MHD_HTTP_OK,
    852       .requires_auth = true },
    853     { .url = "/monitoring/wire-format-inconsistency",
    854       .method = MHD_HTTP_METHOD_GET,
    855       .mime_type = "application/json",
    856       .data = NULL,
    857       .data_size = 0,
    858       .handler = &TAH_get_monitoring_wire_format_inconsistency,
    859       .response_code = MHD_HTTP_OK,
    860       .requires_auth = true },
    861     { .url = "/monitoring/wire-format-inconsistency",
    862       .method = MHD_HTTP_METHOD_DELETE,
    863       .mime_type = "application/json",
    864       .data = NULL,
    865       .data_size = 0,
    866       .handler = &TAH_delete_generic,
    867       .response_code = MHD_HTTP_OK,
    868       .requires_auth = true,
    869       .table = TALER_AUDITORDB_WIRE_FORMAT_INCONSISTENCY },
    870     { .url = "/monitoring/wire-format-inconsistency",
    871       .method = MHD_HTTP_METHOD_PATCH,
    872       .mime_type = "application/json",
    873       .data = NULL,
    874       .data_size = 0,
    875       .handler = &TAH_patch_generic_suppressed,
    876       .response_code = MHD_HTTP_OK,
    877       .requires_auth = true,
    878       .table = TALER_AUDITORDB_WIRE_FORMAT_INCONSISTENCY },
    879     { .url = "/monitoring/wire-out-inconsistency",
    880       .method = MHD_HTTP_METHOD_GET,
    881       .mime_type = "application/json",
    882       .data = NULL,
    883       .data_size = 0,
    884       .handler = &TAH_get_monitoring_wire_out_inconsistency,
    885       .response_code = MHD_HTTP_OK,
    886       .requires_auth = true },
    887     { .url = "/monitoring/wire-out-inconsistency",
    888       .method = MHD_HTTP_METHOD_DELETE,
    889       .mime_type = "application/json",
    890       .data = NULL,
    891       .data_size = 0,
    892       .handler = &TAH_delete_generic,
    893       .response_code = MHD_HTTP_OK,
    894       .requires_auth = true,
    895       .table = TALER_AUDITORDB_WIRE_OUT_INCONSISTENCY },
    896     { .url = "/monitoring/wire-out-inconsistency",
    897       .method = MHD_HTTP_METHOD_PATCH,
    898       .mime_type = "application/json",
    899       .data = NULL,
    900       .data_size = 0,
    901       .handler = &TAH_patch_generic_suppressed,
    902       .response_code = MHD_HTTP_OK,
    903       .requires_auth = true,
    904       .table = TALER_AUDITORDB_WIRE_OUT_INCONSISTENCY },
    905     { .url = "/monitoring/reserve-balance-summary-wrong-inconsistency",
    906       .method = MHD_HTTP_METHOD_GET,
    907       .mime_type = "application/json",
    908       .data = NULL,
    909       .data_size = 0,
    910       .handler = &TAH_get_monitoring_reserve_balance_summary_wrong_inconsistency
    911       ,
    912       .response_code = MHD_HTTP_OK,
    913       .requires_auth = true },
    914     { .url = "/monitoring/reserve-balance-summary-wrong-inconsistency",
    915       .method = MHD_HTTP_METHOD_DELETE,
    916       .mime_type = "application/json",
    917       .data = NULL,
    918       .data_size = 0,
    919       .handler = &TAH_delete_generic,
    920       .response_code = MHD_HTTP_OK,
    921       .requires_auth = true,
    922       .table = TALER_AUDITORDB_RESERVE_BALANCE_SUMMARY_WRONG_INCONSISTENCY },
    923     { .url = "/monitoring/reserve-balance-summary-wrong-inconsistency",
    924       .method = MHD_HTTP_METHOD_PATCH,
    925       .mime_type = "application/json",
    926       .data = NULL,
    927       .data_size = 0,
    928       .handler = &TAH_patch_generic_suppressed,
    929       .response_code = MHD_HTTP_OK,
    930       .requires_auth = true,
    931       .table = TALER_AUDITORDB_RESERVE_BALANCE_SUMMARY_WRONG_INCONSISTENCY },
    932     { .url = "/monitoring/row-minor-inconsistencies",
    933       .method = MHD_HTTP_METHOD_GET,
    934       .mime_type = "application/json",
    935       .data = NULL,
    936       .data_size = 0,
    937       .handler = &TAH_get_monitoring_row_minor_inconsistencies,
    938       .response_code = MHD_HTTP_OK,
    939       .requires_auth = true },
    940     { .url = "/monitoring/row-minor-inconsistencies",
    941       .method = MHD_HTTP_METHOD_DELETE,
    942       .mime_type = "application/json",
    943       .data = NULL,
    944       .data_size = 0,
    945       .handler = &TAH_delete_generic,
    946       .response_code = MHD_HTTP_OK,
    947       .requires_auth = true,
    948       .table = TALER_AUDITORDB_ROW_MINOR_INCONSISTENCY },
    949     { .url = "/monitoring/row-minor-inconsistencies",
    950       .method = MHD_HTTP_METHOD_PATCH,
    951       .mime_type = "application/json",
    952       .data = NULL,
    953       .data_size = 0,
    954       .handler = &TAH_patch_generic_suppressed,
    955       .response_code = MHD_HTTP_OK,
    956       .requires_auth = true,
    957       .table = TALER_AUDITORDB_ROW_MINOR_INCONSISTENCY },
    958     { .url = "/monitoring/fee-time-inconsistency",
    959       .method = MHD_HTTP_METHOD_GET,
    960       .mime_type = "application/json",
    961       .data = NULL,
    962       .data_size = 0,
    963       .handler = &TAH_get_monitoring_fee_time_inconsistency,
    964       .response_code = MHD_HTTP_OK,
    965       .requires_auth = true },
    966     { .url = "/monitoring/fee-time-inconsistency",
    967       .method = MHD_HTTP_METHOD_DELETE,
    968       .mime_type = "application/json",
    969       .data = NULL,
    970       .data_size = 0,
    971       .handler = &TAH_delete_generic,
    972       .response_code = MHD_HTTP_OK,
    973       .requires_auth = true,
    974       .table =  TALER_AUDITORDB_FEE_TIME_INCONSISTENCY },
    975     { .url = "/monitoring/fee-time-inconsistency",
    976       .method = MHD_HTTP_METHOD_PATCH,
    977       .mime_type = "application/json",
    978       .data = NULL,
    979       .data_size = 0,
    980       .handler = &TAH_patch_generic_suppressed,
    981       .response_code = MHD_HTTP_OK,
    982       .requires_auth = true,
    983       .table =  TALER_AUDITORDB_FEE_TIME_INCONSISTENCY  },
    984     { .url = "/monitoring/balances",
    985       .method = MHD_HTTP_METHOD_GET,
    986       .mime_type = "application/json",
    987       .data = NULL,
    988       .data_size = 0,
    989       .handler = &TAH_get_monitoring_balances,
    990       .response_code = MHD_HTTP_OK,
    991       .requires_auth = true },
    992     { .url = "/monitoring/progress",
    993       .method = MHD_HTTP_METHOD_GET,
    994       .mime_type = "application/json",
    995       .data = NULL,
    996       .data_size = 0,
    997       .handler = &TAH_get_monitoring_progress,
    998       .response_code = MHD_HTTP_OK,
    999       .requires_auth = true },
   1000     { .url = "/config",
   1001       .method = MHD_HTTP_METHOD_GET,
   1002       .mime_type = "application/json",
   1003       .data = NULL,
   1004       .data_size = 0,
   1005       .handler = &handle_config,
   1006       .response_code = MHD_HTTP_OK,
   1007       .requires_auth = false },
   1008     /* /robots.txt: disallow everything */
   1009     { .url = "/robots.txt",
   1010       .method = MHD_HTTP_METHOD_GET,
   1011       .mime_type = "text/plain",
   1012       .data = "User-agent: *\nDisallow: /\n",
   1013       .data_size = 0,
   1014       .handler = &TAH_MHD_handler_static_response,
   1015       .response_code = MHD_HTTP_OK,
   1016       .requires_auth = false },
   1017     /* AGPL licensing page, redirect to source. As per the AGPL-license,
   1018        every deployment is required to offer the user a download of the
   1019        source. We make this easy by including a redirect t the source
   1020        here. */
   1021     { .url = "/agpl",
   1022       .method = MHD_HTTP_METHOD_GET,
   1023       .mime_type = "text/plain",
   1024       .data = NULL,
   1025       .data_size = 0,
   1026       .handler = &TAH_MHD_handler_agpl_redirect,
   1027       .response_code = MHD_HTTP_FOUND,
   1028       .requires_auth = false },
   1029     /* Landing page, for now tells humans to go away
   1030      * (NOTE: ideally, the reverse proxy will respond with a nicer page) */
   1031     { .url = "/",
   1032       .method = MHD_HTTP_METHOD_GET,
   1033       .mime_type = "text/plain",
   1034       .data =
   1035         "Hello, I'm the Taler auditor. This HTTP server is not for humans.\n",
   1036       .data_size = 0,
   1037       .handler = &TAH_MHD_handler_static_response,
   1038       .response_code = MHD_HTTP_OK,
   1039       .requires_auth = false },
   1040     { NULL, NULL, NULL, NULL, 0, NULL, 0, 0 }
   1041   };
   1042   unsigned int args_max = 3;
   1043   const char *args[args_max + 1];
   1044   size_t ulen = strlen (url) + 1;
   1045   char d[ulen];
   1046   /* const */ struct TAH_RequestHandler *match = NULL;
   1047   bool url_match = false;
   1048 
   1049   (void) cls;
   1050   (void) version;
   1051   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   1052               "Handling request for URL '%s'\n",
   1053               url);
   1054   if (0 == strcasecmp (method,
   1055                        MHD_HTTP_METHOD_HEAD))
   1056     method = MHD_HTTP_METHOD_GET; /* treat HEAD as GET here, MHD will do the rest */
   1057   if (0 == strcasecmp (method,
   1058                        MHD_HTTP_METHOD_OPTIONS) )
   1059     return TALER_MHD_reply_cors_preflight (connection);
   1060 
   1061   memset (&args,
   1062           0,
   1063           sizeof (args));
   1064   GNUNET_memcpy (d,
   1065                  url,
   1066                  ulen);
   1067   {
   1068     unsigned int i = 0;
   1069 
   1070     for (args[i] = strtok (d,
   1071                            "/");
   1072          NULL != args[i];
   1073          args[i] = strtok (NULL,
   1074                            "/"))
   1075     {
   1076       i++;
   1077       if (i >= args_max)
   1078       {
   1079         GNUNET_break_op (0);
   1080         goto not_found;
   1081       }
   1082     }
   1083   }
   1084 
   1085   for (unsigned int i = 0; NULL != handlers[i].url; i++)
   1086   {
   1087     /* const */ struct TAH_RequestHandler *rh = &handlers[i];
   1088 
   1089     if ( (0 == strcmp (url,
   1090                        rh->url)) ||
   1091          ( (0 == strncmp (url,
   1092                           rh->url,
   1093                           strlen (rh->url))) &&
   1094            ('/' == url[strlen (rh->url)]) ) )
   1095     {
   1096       url_match = true;
   1097       if ( (NULL == rh->method) ||
   1098            (0 == strcasecmp (method,
   1099                              rh->method)) )
   1100       {
   1101         match = rh;
   1102         GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   1103                     "Matched %s\n",
   1104                     rh->url);
   1105         break;
   1106       }
   1107     }
   1108   }
   1109   if (NULL == match)
   1110   {
   1111     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
   1112                 "Could not find handler for `%s'\n",
   1113                 url);
   1114     goto not_found;
   1115   }
   1116   if (match->requires_auth &&
   1117       (0 == disable_auth) )
   1118   {
   1119     const char *auth;
   1120 
   1121     auth = MHD_lookup_connection_value (connection,
   1122                                         MHD_HEADER_KIND,
   1123                                         MHD_HTTP_HEADER_AUTHORIZATION);
   1124     if (NULL == auth)
   1125     {
   1126       GNUNET_break_op (0);
   1127       return TALER_MHD_reply_with_error (
   1128         connection,
   1129         MHD_HTTP_UNAUTHORIZED,
   1130         TALER_EC_AUDITOR_GENERIC_UNAUTHORIZED,
   1131         "Check 'Authorization' header");
   1132     }
   1133     extract_token (&auth);
   1134     if (NULL == auth)
   1135       return TALER_MHD_reply_with_error (
   1136         connection,
   1137         MHD_HTTP_BAD_REQUEST,
   1138         TALER_EC_GENERIC_PARAMETER_MALFORMED,
   1139         "'" RFC_8959_PREFIX
   1140         "' prefix or 'Bearer' missing in 'Authorization' header");
   1141 
   1142     if (GNUNET_OK !=
   1143         check_auth (auth))
   1144     {
   1145       GNUNET_break_op (0);
   1146       return TALER_MHD_reply_with_error (
   1147         connection,
   1148         MHD_HTTP_UNAUTHORIZED,
   1149         TALER_EC_AUDITOR_GENERIC_UNAUTHORIZED,
   1150         "Check 'Authorization' header");
   1151     }
   1152   }
   1153 
   1154   return match->handler (match,
   1155                          connection,
   1156                          con_cls,
   1157                          upload_data,
   1158                          upload_data_size,
   1159                          args);
   1160 not_found:
   1161   if (url_match)
   1162   {
   1163     /* The URL exists, but not for the requested HTTP method: respond with
   1164        405 Method Not Allowed and an 'Allow' header listing the methods that
   1165        are supported for this URL (#9424). */
   1166     char allow[128] = "OPTIONS";
   1167     size_t aoff = strlen ("OPTIONS");
   1168     struct MHD_Response *resp;
   1169     enum MHD_Result ret;
   1170 
   1171     GNUNET_break_op (0);
   1172     /* OPTIONS is always supported (handled above); additionally list every
   1173        method registered for this URL. */
   1174     for (unsigned int i = 0; NULL != handlers[i].url; i++)
   1175     {
   1176       const struct TAH_RequestHandler *rh = &handlers[i];
   1177 
   1178       if (NULL == rh->method)
   1179         continue;
   1180       if ( (0 != strcmp (url,
   1181                          rh->url)) &&
   1182            ! ( (0 == strncmp (url,
   1183                               rh->url,
   1184                               strlen (rh->url))) &&
   1185                ('/' == url[strlen (rh->url)]) ) )
   1186         continue;
   1187       GNUNET_assert (aoff + strlen (rh->method) + 3 < sizeof (allow));
   1188       memcpy (&allow[aoff],
   1189               ", ",
   1190               2);
   1191       aoff += 2;
   1192       memcpy (&allow[aoff],
   1193               rh->method,
   1194               strlen (rh->method));
   1195       aoff += strlen (rh->method);
   1196       allow[aoff] = '\0';
   1197     }
   1198     resp = MHD_create_response_from_buffer (0,
   1199                                             NULL,
   1200                                             MHD_RESPMEM_PERSISTENT);
   1201     TALER_MHD_add_global_headers (resp,
   1202                                   false);
   1203     GNUNET_break (MHD_YES ==
   1204                   MHD_add_response_header (resp,
   1205                                            MHD_HTTP_HEADER_ALLOW,
   1206                                            allow));
   1207     ret = MHD_queue_response (connection,
   1208                               MHD_HTTP_METHOD_NOT_ALLOWED,
   1209                               resp);
   1210     MHD_destroy_response (resp);
   1211     return ret;
   1212   }
   1213 
   1214 #define NOT_FOUND \
   1215         "<html><title>404: not found</title><body>auditor endpoints have been moved to /monitoring/...</body></html>"
   1216   return TALER_MHD_reply_static (connection,
   1217                                  MHD_HTTP_NOT_FOUND,
   1218                                  "text/html",
   1219                                  NOT_FOUND,
   1220                                  strlen (NOT_FOUND));
   1221 #undef NOT_FOUND
   1222 }
   1223 
   1224 
   1225 /**
   1226  * Load configuration parameters for the auditor
   1227  * server into the corresponding global variables.
   1228  *
   1229  * @return #GNUNET_OK on success
   1230  */
   1231 static enum GNUNET_GenericReturnValue
   1232 auditor_serve_process_config (void)
   1233 {
   1234   if (NULL ==
   1235       (TAH_apg = TALER_AUDITORDB_connect (cfg)))
   1236   {
   1237     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   1238                 "Failed to initialize DB subsystem to interact with auditor database\n");
   1239     return GNUNET_SYSERR;
   1240   }
   1241   if (NULL ==
   1242       (TAH_epg = TALER_EXCHANGEDB_connect (cfg)))
   1243   {
   1244     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   1245                 "Failed to initialize DB subsystem to query exchange database\n");
   1246     return GNUNET_SYSERR;
   1247   }
   1248   if (GNUNET_SYSERR ==
   1249       TALER_EXCHANGEDB_preflight (TAH_epg))
   1250   {
   1251     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   1252                 "Failed to initialize DB subsystem to query exchange database\n");
   1253     return GNUNET_SYSERR;
   1254   }
   1255   if (GNUNET_OK !=
   1256       TALER_config_get_currency (cfg,
   1257                                  "exchange",
   1258                                  &TAH_currency))
   1259   {
   1260     return GNUNET_SYSERR;
   1261   }
   1262 
   1263   {
   1264     char *master_public_key_str;
   1265 
   1266     if (GNUNET_OK !=
   1267         GNUNET_CONFIGURATION_get_value_string (cfg,
   1268                                                "exchange",
   1269                                                "MASTER_PUBLIC_KEY",
   1270                                                &master_public_key_str))
   1271     {
   1272       GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
   1273                                  "exchange",
   1274                                  "MASTER_PUBLIC_KEY");
   1275       return GNUNET_SYSERR;
   1276     }
   1277     if (GNUNET_OK !=
   1278         GNUNET_CRYPTO_eddsa_public_key_from_string (
   1279           master_public_key_str,
   1280           strlen (master_public_key_str),
   1281           &TAH_master_public_key.eddsa_pub))
   1282     {
   1283       GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
   1284                                  "exchange",
   1285                                  "MASTER_PUBLIC_KEY",
   1286                                  "invalid base32 encoding for a master public key");
   1287       GNUNET_free (master_public_key_str);
   1288       return GNUNET_SYSERR;
   1289     }
   1290     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   1291                 "Launching auditor for exchange `%s'...\n",
   1292                 master_public_key_str);
   1293     GNUNET_free (master_public_key_str);
   1294   }
   1295 
   1296   {
   1297     char *pub;
   1298 
   1299     if (GNUNET_OK ==
   1300         GNUNET_CONFIGURATION_get_value_string (cfg,
   1301                                                "AUDITOR",
   1302                                                "PUBLIC_KEY",
   1303                                                &pub))
   1304     {
   1305       if (GNUNET_OK !=
   1306           GNUNET_CRYPTO_eddsa_public_key_from_string (pub,
   1307                                                       strlen (pub),
   1308                                                       &auditor_pub.eddsa_pub))
   1309       {
   1310         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   1311                     "Invalid public key given in auditor configuration.");
   1312         GNUNET_free (pub);
   1313         return GNUNET_SYSERR;
   1314       }
   1315       GNUNET_free (pub);
   1316       return GNUNET_OK;
   1317     }
   1318   }
   1319 
   1320   {
   1321     /* Fall back to trying to read private key */
   1322     char *auditor_key_file;
   1323     struct GNUNET_CRYPTO_EddsaPrivateKey eddsa_priv;
   1324 
   1325     if (GNUNET_OK !=
   1326         GNUNET_CONFIGURATION_get_value_filename (cfg,
   1327                                                  "auditor",
   1328                                                  "AUDITOR_PRIV_FILE",
   1329                                                  &auditor_key_file))
   1330     {
   1331       GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
   1332                                  "AUDITOR",
   1333                                  "PUBLIC_KEY");
   1334       GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
   1335                                  "AUDITOR",
   1336                                  "AUDITOR_PRIV_FILE");
   1337       return GNUNET_SYSERR;
   1338     }
   1339     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   1340                 "Loading auditor private key from %s\n",
   1341                 auditor_key_file);
   1342     if (GNUNET_OK !=
   1343         GNUNET_CRYPTO_eddsa_key_from_file (auditor_key_file,
   1344                                            GNUNET_NO,
   1345                                            &eddsa_priv))
   1346     {
   1347       /* Both failed, complain! */
   1348       GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
   1349                                  "AUDITOR",
   1350                                  "PUBLIC_KEY");
   1351       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   1352                   "Failed to initialize auditor key from file `%s'\n",
   1353                   auditor_key_file);
   1354       GNUNET_free (auditor_key_file);
   1355       return 1;
   1356     }
   1357     GNUNET_free (auditor_key_file);
   1358     GNUNET_CRYPTO_eddsa_key_get_public (&eddsa_priv,
   1359                                         &auditor_pub.eddsa_pub);
   1360   }
   1361   if (GNUNET_OK ==
   1362       GNUNET_CONFIGURATION_get_value_filename (cfg,
   1363                                                "auditor",
   1364                                                "SPA_DIR",
   1365                                                &TAH_spa_dir))
   1366   {
   1367     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   1368                 "Loading auditor SPA from %s\n",
   1369                 TAH_spa_dir);
   1370   }
   1371   else
   1372   {
   1373     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   1374                 "Loading exchange AML SPA from default location\n");
   1375   }
   1376   return GNUNET_OK;
   1377 }
   1378 
   1379 
   1380 /**
   1381  * Function run on shutdown.
   1382  *
   1383  * @param cls NULL
   1384  */
   1385 static void
   1386 do_shutdown (void *cls)
   1387 {
   1388   (void) cls;
   1389   TALER_MHD_daemons_halt ();
   1390   TEAH_put_deposit_confirmation_done ();
   1391   TALER_MHD_daemons_destroy ();
   1392   if (NULL != TAH_apg)
   1393   {
   1394     TALER_AUDITORDB_disconnect (TAH_apg);
   1395     TAH_apg = NULL;
   1396   }
   1397   if (NULL != TAH_epg)
   1398   {
   1399     TALER_EXCHANGEDB_disconnect (TAH_epg);
   1400     TAH_epg = NULL;
   1401   }
   1402 }
   1403 
   1404 
   1405 /**
   1406  * Callback invoked on every listen socket to start the
   1407  * respective MHD HTTP daemon.
   1408  *
   1409  * @param cls unused
   1410  * @param lsock the listen socket
   1411  */
   1412 static void
   1413 start_daemon (void *cls,
   1414               int lsock)
   1415 {
   1416   struct MHD_Daemon *mhd;
   1417 
   1418   (void) cls;
   1419   GNUNET_assert (-1 != lsock);
   1420   mhd = MHD_start_daemon (MHD_USE_SUSPEND_RESUME
   1421                           | MHD_USE_PIPE_FOR_SHUTDOWN
   1422                           | MHD_USE_DEBUG | MHD_USE_DUAL_STACK
   1423                           | MHD_USE_TCP_FASTOPEN,
   1424                           0,
   1425                           NULL, NULL,
   1426                           &handle_mhd_request, NULL,
   1427                           MHD_OPTION_LISTEN_SOCKET,
   1428                           lsock,
   1429                           MHD_OPTION_EXTERNAL_LOGGER,
   1430                           &TALER_MHD_handle_logs,
   1431                           NULL,
   1432                           MHD_OPTION_NOTIFY_COMPLETED,
   1433                           &handle_mhd_completion_callback,
   1434                           NULL,
   1435                           MHD_OPTION_CONNECTION_TIMEOUT,
   1436                           connection_timeout,
   1437                           MHD_OPTION_END);
   1438   if (NULL == mhd)
   1439   {
   1440     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   1441                 "Failed to launch HTTP daemon.\n");
   1442     GNUNET_SCHEDULER_shutdown ();
   1443     return;
   1444   }
   1445   have_daemons = true;
   1446   TALER_MHD_daemon_start (mhd);
   1447 }
   1448 
   1449 
   1450 /**
   1451  * Main function that will be run by the scheduler.
   1452  *
   1453  * @param cls closure
   1454  * @param args remaining command-line arguments
   1455  * @param cfgfile name of the configuration file used (for saving, can be
   1456  *        NULL!)
   1457  * @param config configuration
   1458  */
   1459 static void
   1460 run (void *cls,
   1461      char *const *args,
   1462      const char *cfgfile,
   1463      const struct GNUNET_CONFIGURATION_Handle *config)
   1464 {
   1465   enum TALER_MHD_GlobalOptions go;
   1466   enum GNUNET_GenericReturnValue ret;
   1467 
   1468   (void) cls;
   1469   (void) args;
   1470   (void) cfgfile;
   1471   if (0 == disable_auth)
   1472   {
   1473     const char *tok;
   1474 
   1475     tok = getenv ("TALER_AUDITOR_ACCESS_TOKEN");
   1476     if (NULL == tok)
   1477     {
   1478       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
   1479                   "TALER_AUDITOR_ACCESS_TOKEN environment variable not set. Disabling authentication\n");
   1480       disable_auth = 1;
   1481     }
   1482     else
   1483     {
   1484       GNUNET_assert (GNUNET_YES ==
   1485                      GNUNET_CRYPTO_hkdf_gnunet (
   1486                        &TAH_auth,
   1487                        sizeof (TAH_auth),
   1488                        KDF_SALT,
   1489                        strlen (KDF_SALT),
   1490                        tok,
   1491                        strlen (tok)));
   1492     }
   1493   }
   1494 
   1495   go = TALER_MHD_GO_NONE;
   1496   if (auditor_connection_close)
   1497     go |= TALER_MHD_GO_FORCE_CONNECTION_CLOSE;
   1498   TALER_MHD_setup (go);
   1499   cfg = config;
   1500 
   1501   GNUNET_SCHEDULER_add_shutdown (&do_shutdown,
   1502                                  NULL);
   1503   if (GNUNET_OK !=
   1504       auditor_serve_process_config ())
   1505   {
   1506     global_ret = EXIT_NOTCONFIGURED;
   1507     GNUNET_SCHEDULER_shutdown ();
   1508     return;
   1509   }
   1510   if (GNUNET_OK !=
   1511       TAH_spa_init ())
   1512   {
   1513     global_ret = EXIT_NOTCONFIGURED;
   1514     GNUNET_SCHEDULER_shutdown ();
   1515     return;
   1516   }
   1517   TEAH_put_deposit_confirmation_init ();
   1518   ret = TALER_MHD_listen_bind (cfg,
   1519                                "auditor",
   1520                                &start_daemon,
   1521                                NULL);
   1522   switch (ret)
   1523   {
   1524   case GNUNET_SYSERR:
   1525     global_ret = EXIT_NOTCONFIGURED;
   1526     GNUNET_SCHEDULER_shutdown ();
   1527     return;
   1528   case GNUNET_NO:
   1529     if (! have_daemons)
   1530     {
   1531       global_ret = EXIT_NOTCONFIGURED;
   1532       GNUNET_SCHEDULER_shutdown ();
   1533       return;
   1534     }
   1535     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
   1536                 "Could not open all configured listen sockets\n");
   1537     break;
   1538   case GNUNET_OK:
   1539     break;
   1540   }
   1541   global_ret = EXIT_SUCCESS;
   1542 }
   1543 
   1544 
   1545 /**
   1546  * The main function of the taler-auditor-httpd server ("the auditor").
   1547  *
   1548  * @param argc number of arguments from the command line
   1549  * @param argv command line arguments
   1550  * @return 0 ok, 1 on error
   1551  */
   1552 int
   1553 main (int argc,
   1554       char *const *argv)
   1555 {
   1556   const struct GNUNET_GETOPT_CommandLineOption options[] = {
   1557     GNUNET_GETOPT_option_flag ('C',
   1558                                "connection-close",
   1559                                "force HTTP connections to be closed after each request",
   1560                                &auditor_connection_close),
   1561     GNUNET_GETOPT_option_flag ('n',
   1562                                "no-authentication",
   1563                                "disable authentication checks",
   1564                                &disable_auth),
   1565     GNUNET_GETOPT_option_uint ('t',
   1566                                "timeout",
   1567                                "SECONDS",
   1568                                "after how long do connections timeout by default (in seconds)",
   1569                                &connection_timeout),
   1570     GNUNET_GETOPT_option_help (
   1571       TALER_AUDITOR_project_data (),
   1572       "HTTP server providing a RESTful API to access a Taler auditor"),
   1573     GNUNET_GETOPT_option_version (VERSION),
   1574     GNUNET_GETOPT_OPTION_END
   1575   };
   1576   int ret;
   1577 
   1578   ret = GNUNET_PROGRAM_run (
   1579     TALER_AUDITOR_project_data (),
   1580     argc, argv,
   1581     "taler-auditor-httpd",
   1582     "Taler auditor HTTP service",
   1583     options,
   1584     &run, NULL);
   1585   if (GNUNET_SYSERR == ret)
   1586     return EXIT_INVALIDARGUMENT;
   1587   if (GNUNET_NO == ret)
   1588     return EXIT_SUCCESS;
   1589   return global_ret;
   1590 }
   1591 
   1592 
   1593 /* end of taler-auditor-httpd.c */