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