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