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