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