frosix-httpd.c (35943B)
1 /* 2 This file is part of Frosix 3 (C) 2020-2022 Anastasis SARL 4 5 Frosix 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 Frosix 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 Frosix; see the file COPYING. If not, see <http://www.gnu.org/licenses/> 15 */ 16 /** 17 * @file backend/frosix-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 * @author Joel Urech 23 */ 24 #include "platform.h" 25 #include "frosix-httpd.h" 26 #include "frosix-httpd_mhd.h" 27 #include "frosix-httpd_terms.h" 28 #include "frosix-httpd_config.h" 29 #include "frosix-httpd_seed.h" 30 #include "frosix-httpd_dkg.h" 31 #include "frosix-httpd_sig.h" 32 #include "frosix-httpd_auth.h" 33 #include "frosix_database_lib.h" 34 #include "frosix_util_lib.h" 35 #include "gnunet/gnunet_curl_lib.h" 36 #include "taler/taler_util.h" 37 38 39 /** 40 * Annual fee for the backup account. 41 */ 42 struct TALER_Amount FH_annual_fee; 43 44 /** 45 * Fee for a truth upload. 46 */ 47 struct TALER_Amount FH_signature_creation_fee; 48 49 /** 50 * Amount of insurance. 51 */ 52 // struct TALER_Amount FH_insurance; 53 54 /** 55 * Cost for secure question truth download. 56 */ 57 struct TALER_Amount FH_question_cost; 58 59 /** 60 * Our configuration. 61 */ 62 const struct GNUNET_CONFIGURATION_Handle *FH_cfg; 63 64 /** 65 * Our Taler backend to process payments. 66 */ 67 char *FH_backend_url; 68 69 /** 70 * Our fulfillment URL. 71 */ 72 char *FH_fulfillment_url; 73 74 /** 75 * Our business name. 76 */ 77 char *FH_business_name; 78 79 /** 80 * Our provider salt. 81 */ 82 struct FROSIX_ProviderSaltP FH_provider_salt; 83 84 /** 85 * Our secret provider salt 86 */ 87 struct FROSIX_SecretProviderSaltP FH_secret_provider_salt; 88 89 /** 90 * FIXME 91 */ 92 struct GNUNET_CRYPTO_EddsaPrivateKey FH_priv_sig_key; 93 94 /** 95 * FIXME 96 */ 97 struct GNUNET_CRYPTO_EddsaPublicKey FH_pub_sig_key; 98 99 /** 100 * Number of policy uploads permitted per annual fee payment. 101 */ 102 unsigned long long FH_post_counter = 64LLU; 103 104 /** 105 * Our context for making HTTP requests. 106 */ 107 struct GNUNET_CURL_Context *FH_ctx; 108 109 /** 110 * Should a "Connection: close" header be added to each HTTP response? 111 */ 112 static int FH_connection_close; 113 114 /** 115 * Task running the HTTP server. 116 */ 117 static struct GNUNET_SCHEDULER_Task *mhd_task; 118 119 /** 120 * Heap for processing timeouts of requests. 121 */ 122 struct GNUNET_CONTAINER_Heap *FH_to_heap; 123 124 /** 125 * Global return code 126 */ 127 static int global_result; 128 129 /** 130 * The MHD Daemon 131 */ 132 static struct MHD_Daemon *mhd; 133 134 /** 135 * Connection handle to the our database 136 */ 137 struct FROSIX_DatabasePlugin *db; 138 139 /** 140 * Reschedule context for #FH_ctx. 141 */ 142 static struct GNUNET_CURL_RescheduleContext *rc; 143 144 /** 145 * Set if we should immediately MHD_run() again. 146 */ 147 static int triggered; 148 149 /** 150 * Username and password to use for client authentication 151 * (optional). 152 */ 153 static char *userpass; 154 155 /** 156 * Type of the client's TLS certificate (optional). 157 */ 158 static char *certtype; 159 160 /** 161 * File with the client's TLS certificate (optional). 162 */ 163 static char *certfile; 164 165 /** 166 * File with the client's TLS private key (optional). 167 */ 168 static char *keyfile; 169 170 /** 171 * This value goes in the Authorization:-header. 172 */ 173 static char *apikey; 174 175 /** 176 * Passphrase to decrypt client's TLS private key file (optional). 177 */ 178 static char *keypass; 179 180 181 /** 182 * Function that queries MHD's select sets and 183 * starts the task waiting for them. 184 */ 185 static struct GNUNET_SCHEDULER_Task * 186 prepare_daemon (void); 187 188 189 /** 190 * Call MHD to process pending requests and then go back 191 * and schedule the next run. 192 * 193 * @param cls the `struct MHD_Daemon` of the HTTP server to run 194 */ 195 static void 196 run_daemon (void *cls) 197 { 198 (void) cls; 199 mhd_task = NULL; 200 do { 201 triggered = 0; 202 GNUNET_assert (MHD_YES == MHD_run (mhd)); 203 } while (0 != triggered); 204 mhd_task = prepare_daemon (); 205 } 206 207 208 /** 209 * Kick MHD to run now, to be called after MHD_resume_connection(). 210 * Basically, we need to explicitly resume MHD's event loop whenever 211 * we made progress serving a request. This function re-schedules 212 * the task processing MHD's activities to run immediately. 213 * 214 * @param cls NULL 215 */ 216 void 217 FH_trigger_daemon (void *cls) 218 { 219 (void) cls; 220 if (NULL != mhd_task) 221 { 222 GNUNET_SCHEDULER_cancel (mhd_task); 223 mhd_task = GNUNET_SCHEDULER_add_now (&run_daemon, 224 NULL); 225 } 226 else 227 { 228 triggered = 1; 229 } 230 } 231 232 233 /** 234 * Kick GNUnet Curl scheduler to begin curl interactions. 235 */ 236 void 237 FH_trigger_curl (void) 238 { 239 GNUNET_CURL_gnunet_scheduler_reschedule (&rc); 240 } 241 242 243 /** 244 * A client has requested the given url using the given method 245 * (MHD_HTTP_METHOD_GET, MHD_HTTP_METHOD_PUT, 246 * MHD_HTTP_METHOD_DELETE, MHD_HTTP_METHOD_POST, etc). The callback 247 * must call MHD callbacks to provide content to give back to the 248 * client and return an HTTP status code (i.e. MHD_HTTP_OK, 249 * MHD_HTTP_NOT_FOUND, etc.). 250 * 251 * @param cls argument given together with the function 252 * pointer when the handler was registered with MHD 253 * @param connection MHD connection handle with further request details 254 * @param url the requested url 255 * @param method the HTTP method used (MHD_HTTP_METHOD_GET, 256 * MHD_HTTP_METHOD_PUT, etc.) 257 * @param version the HTTP version string (i.e. 258 * MHD_HTTP_VERSION_1_1) 259 * @param upload_data the data being uploaded (excluding HEADERS, 260 * for a POST that fits into memory and that is encoded 261 * with a supported encoding, the POST data will NOT be 262 * given in upload_data and is instead available as 263 * part of MHD_get_connection_values(); very large POST 264 * data *will* be made available incrementally in 265 * @a upload_data) 266 * @param upload_data_size set initially to the size of the 267 * @a upload_data provided; the method must update this 268 * value to the number of bytes NOT processed; 269 * @param con_cls pointer that the callback can set to some 270 * address and that will be preserved by MHD for future 271 * calls for this request; since the access handler may 272 * be called many times (i.e., for a PUT/POST operation 273 * with plenty of upload data) this allows the application 274 * to easily associate some request-specific state. 275 * If necessary, this state can be cleaned up in the 276 * global MHD_RequestCompletedCallback (which 277 * can be set with the MHD_OPTION_NOTIFY_COMPLETED). 278 * Initially, `*con_cls` will be NULL. 279 * @return #MHD_YES if the connection was handled successfully, 280 * #MHD_NO if the socket must be closed due to a serious 281 * error while handling the request 282 */ 283 static MHD_RESULT 284 url_handler (void *cls, 285 struct MHD_Connection *connection, 286 const char *url, 287 const char *method, 288 const char *version, 289 const char *upload_data, 290 size_t *upload_data_size, 291 void **con_cls) 292 { 293 static struct FH_RequestHandler handlers[] = { 294 /* Landing page, tell humans to go away. */ 295 { "/", MHD_HTTP_METHOD_GET, "text/plain", 296 "Hello, I'm Frosix. This HTTP server is not for humans.\n", 0, 297 &TMH_MHD_handler_static_response, MHD_HTTP_OK }, 298 { "/agpl", MHD_HTTP_METHOD_GET, "text/plain", 299 NULL, 0, 300 &TMH_MHD_handler_agpl_redirect, MHD_HTTP_FOUND }, 301 { "/terms", MHD_HTTP_METHOD_GET, NULL, 302 NULL, 0, 303 &FH_handler_privacy, MHD_HTTP_OK }, 304 { "/privacy", MHD_HTTP_METHOD_GET, NULL, 305 NULL, 0, 306 &FH_handler_terms, MHD_HTTP_OK }, 307 { "/config", MHD_HTTP_METHOD_GET, "text/json", 308 NULL, 0, 309 &FH_handler_config, MHD_HTTP_OK }, 310 { "/seed", MHD_HTTP_METHOD_GET, "arraybuffer", 311 NULL, 0, 312 &FH_seed_get, MHD_HTTP_OK }, 313 {NULL, NULL, NULL, NULL, 0, 0 } 314 }; 315 static struct FH_RequestHandler h404 = { 316 "", NULL, "text/html", 317 "<html><title>404: not found</title></html>", 0, 318 &TMH_MHD_handler_static_response, MHD_HTTP_NOT_FOUND 319 }; 320 static struct FH_RequestHandler h405 = { 321 "", NULL, "text/html", 322 "<html><title>405: method not allowed</title></html>", 0, 323 &TMH_MHD_handler_static_response, MHD_HTTP_METHOD_NOT_ALLOWED 324 }; 325 struct TM_HandlerContext *hc = *con_cls; 326 const char *correlation_id = NULL; 327 bool path_matched; 328 329 if (NULL == hc) 330 { 331 struct GNUNET_AsyncScopeId aid; 332 333 GNUNET_async_scope_fresh (&aid); 334 /* We only read the correlation ID on the first callback for every client */ 335 correlation_id = MHD_lookup_connection_value (connection, 336 MHD_HEADER_KIND, 337 "Frosix-Correlation-Id"); 338 if ((NULL != correlation_id) && 339 (GNUNET_YES != GNUNET_CURL_is_valid_scope_id (correlation_id))) 340 { 341 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 342 "Invalid incoming correlation ID\n"); 343 correlation_id = NULL; 344 } 345 hc = GNUNET_new (struct TM_HandlerContext); 346 *con_cls = hc; 347 hc->async_scope_id = aid; 348 hc->url = url; 349 } 350 if (0 == strcasecmp (method, 351 MHD_HTTP_METHOD_HEAD)) 352 method = MHD_HTTP_METHOD_GET; /* MHD will throw away the body */ 353 354 GNUNET_SCHEDULER_begin_async_scope (&hc->async_scope_id); 355 if (NULL != correlation_id) 356 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 357 "Handling request for (%s) URL '%s', correlation_id=%s\n", 358 method, 359 url, 360 correlation_id); 361 else 362 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 363 "Handling request (%s) for URL '%s'\n", 364 method, 365 url); 366 367 /* POST /dkg-commitment/ID */ 368 if (0 == strncmp (url, 369 "/dkg-commitment/", 370 strlen ("/dkg-commitment/"))) 371 { 372 // we only accept post for this endpoint 373 if (0 != strcmp (method, MHD_HTTP_METHOD_POST)) 374 { 375 return TMH_MHD_handler_static_response (&h405, 376 connection); 377 } 378 379 // get ID 380 struct FROSIX_DkgRequestIdP id; 381 const char *pub_key_str; 382 size_t len; 383 384 pub_key_str = &url[strlen ("/dkg-commitment/")]; 385 len = strlen (pub_key_str); 386 387 if (GNUNET_OK != 388 GNUNET_STRINGS_string_to_data ( 389 pub_key_str, 390 len, 391 &id, 392 sizeof (id))) 393 { 394 GNUNET_break_op (0); 395 return TALER_MHD_reply_with_error (connection, 396 MHD_HTTP_BAD_REQUEST, 397 TALER_EC_GENERIC_PARAMETER_MALFORMED, 398 "ID"); 399 } 400 401 return FH_handler_dkg_commitment_post (connection, 402 hc, 403 &id, 404 &FH_provider_salt, 405 &FH_secret_provider_salt, 406 &FH_priv_sig_key, 407 &FH_pub_sig_key, 408 upload_data, 409 upload_data_size); 410 } 411 412 /* POST /dkg-shares/ID */ 413 if (0 == strncmp (url, 414 "/dkg-shares/", 415 strlen ("/dkg-shares/"))) 416 { 417 // we only accept post for this endpoint 418 if (0 != strcmp (method, MHD_HTTP_METHOD_POST)) 419 { 420 return TMH_MHD_handler_static_response (&h405, 421 connection); 422 } 423 424 // get ID 425 struct FROSIX_DkgRequestIdP id; 426 const char *pub_key_str; 427 size_t len; 428 429 pub_key_str = &url[strlen ("/dkg-shares/")]; 430 len = strlen (pub_key_str); 431 432 if (GNUNET_OK != 433 GNUNET_STRINGS_string_to_data ( 434 pub_key_str, 435 len, 436 &id, 437 sizeof (id))) 438 { 439 GNUNET_break_op (0); 440 return TALER_MHD_reply_with_error (connection, 441 MHD_HTTP_BAD_REQUEST, 442 TALER_EC_GENERIC_PARAMETER_MALFORMED, 443 "ID"); 444 } 445 446 return FH_handler_dkg_shares_post (connection, 447 hc, 448 &id, 449 &FH_provider_salt, 450 &FH_secret_provider_salt, 451 upload_data, 452 upload_data_size); 453 } 454 455 /* POST /dkg-key/ID */ 456 if (0 == strncmp (url, 457 "/dkg-key/", 458 strlen ("/dkg-key/"))) 459 { 460 // we only accept post and delete for this endpoint 461 if (0 != strcmp (method, MHD_HTTP_METHOD_POST) && 462 0 != strcmp (method, MHD_HTTP_METHOD_DELETE)) 463 { 464 return TMH_MHD_handler_static_response (&h405, 465 connection); 466 } 467 468 // get ID 469 struct FROSIX_DkgRequestIdP id; 470 const char *pub_key_str; 471 size_t len; 472 473 pub_key_str = &url[strlen ("/dkg-key/")]; 474 len = strlen (pub_key_str); 475 476 if (GNUNET_OK != 477 GNUNET_STRINGS_string_to_data ( 478 pub_key_str, 479 len, 480 &id, 481 sizeof (id))) 482 { 483 GNUNET_break_op (0); 484 return TALER_MHD_reply_with_error (connection, 485 MHD_HTTP_BAD_REQUEST, 486 TALER_EC_GENERIC_PARAMETER_MALFORMED, 487 "ID"); 488 } 489 490 if (0 == strcmp (method, MHD_HTTP_METHOD_POST)) 491 { 492 /* create new key */ 493 return FH_handler_dkg_key_post (connection, 494 hc, 495 &id, 496 &FH_provider_salt, 497 &FH_secret_provider_salt, 498 &FH_priv_sig_key, 499 upload_data, 500 upload_data_size); 501 } 502 else 503 { 504 /* delete existing key */ 505 return FH_handler_key_delete (connection, 506 hc, 507 &id); 508 } 509 } 510 511 /* POST /auth-challenge/ID */ 512 if (0 == strncmp (url, 513 "/auth-challenge/", 514 strlen ("/auth-challenge/"))) 515 { 516 // we only accept post for this endpoint 517 if (0 != strcmp (method, MHD_HTTP_METHOD_POST)) 518 { 519 return TMH_MHD_handler_static_response (&h405, 520 connection); 521 } 522 523 // get ID 524 struct FROSIX_ChallengeRequestIdP id; 525 const char *pub_key_str; 526 size_t len; 527 528 pub_key_str = &url[strlen ("/auth-challenge/")]; 529 len = strlen (pub_key_str); 530 531 if (GNUNET_OK != 532 GNUNET_STRINGS_string_to_data ( 533 pub_key_str, 534 len, 535 &id, 536 sizeof (id))) 537 { 538 GNUNET_break_op (0); 539 return TALER_MHD_reply_with_error (connection, 540 MHD_HTTP_BAD_REQUEST, 541 TALER_EC_GENERIC_PARAMETER_MALFORMED, 542 "ID"); 543 } 544 545 return FH_handler_auth_challenge_post (connection, 546 hc, 547 &id, 548 upload_data, 549 upload_data_size); 550 } 551 552 /* POST /sig-commitment/ID */ 553 if (0 == strncmp (url, 554 "/sig-commitment/", 555 strlen ("/sig-commitment/"))) 556 { 557 // we only accept post for this endpoint 558 if (0 != strcmp (method, MHD_HTTP_METHOD_POST)) 559 { 560 return TMH_MHD_handler_static_response (&h405, 561 connection); 562 } 563 564 // get ID 565 struct FROSIX_SigRequestIdP id; 566 const char *pub_key_str; 567 size_t len; 568 569 pub_key_str = &url[strlen ("/sig-commitment/")]; 570 len = strlen (pub_key_str); 571 572 if (GNUNET_OK != 573 GNUNET_STRINGS_string_to_data ( 574 pub_key_str, 575 len, 576 &id, 577 sizeof (id))) 578 { 579 GNUNET_break_op (0); 580 return TALER_MHD_reply_with_error (connection, 581 MHD_HTTP_BAD_REQUEST, 582 TALER_EC_GENERIC_PARAMETER_MALFORMED, 583 "ID"); 584 } 585 586 return FH_handler_sig_commitment_post (connection, 587 hc, 588 &id, 589 upload_data, 590 upload_data_size); 591 } 592 593 /* POST /sig-share/ID */ 594 if (0 == strncmp (url, 595 "/sig-share/", 596 strlen ("/sig-share/"))) 597 { 598 // we only accept post for this endpoint 599 if (0 != strcmp (method, MHD_HTTP_METHOD_POST)) 600 { 601 return TMH_MHD_handler_static_response (&h405, 602 connection); 603 } 604 605 // get ID 606 struct FROSIX_SigRequestIdP id; 607 const char *pub_key_str; 608 size_t len; 609 610 pub_key_str = &url[strlen ("/sig-share/")]; 611 len = strlen (pub_key_str); 612 613 if (GNUNET_OK != 614 GNUNET_STRINGS_string_to_data ( 615 pub_key_str, 616 len, 617 &id, 618 sizeof (id))) 619 { 620 GNUNET_break_op (0); 621 return TALER_MHD_reply_with_error (connection, 622 MHD_HTTP_BAD_REQUEST, 623 TALER_EC_GENERIC_PARAMETER_MALFORMED, 624 "ID"); 625 } 626 627 return FH_handler_sig_share_post (connection, 628 hc, 629 &id, 630 upload_data, 631 upload_data_size); 632 } 633 634 path_matched = false; 635 for (unsigned int i = 0; NULL != handlers[i].url; i++) 636 { 637 struct FH_RequestHandler *rh = &handlers[i]; 638 639 if (0 == strcmp (url, 640 rh->url)) 641 { 642 path_matched = true; 643 if (0 == strcasecmp (method, 644 MHD_HTTP_METHOD_OPTIONS)) 645 { 646 return TALER_MHD_reply_cors_preflight (connection); 647 } 648 if ( (NULL == rh->method) || 649 (0 == strcasecmp (method, 650 rh->method)) ) 651 { 652 return rh->handler (rh, 653 connection); 654 } 655 } 656 } 657 if (path_matched) 658 return TMH_MHD_handler_static_response (&h405, 659 connection); 660 return TMH_MHD_handler_static_response (&h404, 661 connection); 662 } 663 664 665 /** 666 * Shutdown task (magically invoked when the application is being 667 * quit) 668 * 669 * @param cls NULL 670 */ 671 static void 672 do_shutdown (void *cls) 673 { 674 (void) cls; 675 // FIXME 676 // AH_resume_all_bc (); 677 // AH_truth_challenge_shutdown (); 678 // AH_truth_solve_shutdown (); 679 // AH_truth_upload_shutdown (); 680 if (NULL != mhd_task) 681 { 682 GNUNET_SCHEDULER_cancel (mhd_task); 683 mhd_task = NULL; 684 } 685 if (NULL != FH_ctx) 686 { 687 GNUNET_CURL_fini (FH_ctx); 688 FH_ctx = NULL; 689 } 690 if (NULL != rc) 691 { 692 GNUNET_CURL_gnunet_rc_destroy (rc); 693 rc = NULL; 694 } 695 if (NULL != mhd) 696 { 697 MHD_stop_daemon (mhd); 698 mhd = NULL; 699 } 700 if (NULL != db) 701 { 702 FROSIX_DB_plugin_unload (db); 703 db = NULL; 704 } 705 if (NULL != FH_to_heap) 706 { 707 GNUNET_CONTAINER_heap_destroy (FH_to_heap); 708 FH_to_heap = NULL; 709 } 710 } 711 712 713 /** 714 * Function called whenever MHD is done with a request. If the 715 * request was a POST, we may have stored a `struct Buffer *` in the 716 * @a con_cls that might still need to be cleaned up. Call the 717 * respective function to free the memory. 718 * 719 * @param cls client-defined closure 720 * @param connection connection handle 721 * @param con_cls value as set by the last call to 722 * the #MHD_AccessHandlerCallback 723 * @param toe reason for request termination 724 * @see #MHD_OPTION_NOTIFY_COMPLETED 725 * @ingroup request 726 */ 727 static void 728 handle_mhd_completion_callback (void *cls, 729 struct MHD_Connection *connection, 730 void **con_cls, 731 enum MHD_RequestTerminationCode toe) 732 { 733 struct TM_HandlerContext *hc = *con_cls; 734 struct GNUNET_AsyncScopeSave old_scope; 735 736 (void) cls; 737 (void) connection; 738 if (NULL == hc) 739 return; 740 GNUNET_async_scope_enter (&hc->async_scope_id, 741 &old_scope); 742 { 743 #if MHD_VERSION >= 0x00097304 744 const union MHD_ConnectionInfo *ci; 745 unsigned int http_status = 0; 746 747 ci = MHD_get_connection_info (connection, 748 MHD_CONNECTION_INFO_HTTP_STATUS); 749 if (NULL != ci) 750 http_status = ci->http_status; 751 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 752 "Request for `%s' completed with HTTP status %u (%d)\n", 753 hc->url, 754 http_status, 755 toe); 756 #else 757 (void) connection; 758 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 759 "Request for `%s' completed (%d)\n", 760 hc->url, 761 toe); 762 #endif 763 } 764 if (NULL != hc->cc) 765 hc->cc (hc); 766 GNUNET_free (hc); 767 *con_cls = NULL; 768 } 769 770 771 /** 772 * Function that queries MHD's select sets and 773 * starts the task waiting for them. 774 * 775 * @return task handle for the daemon 776 */ 777 static struct GNUNET_SCHEDULER_Task * 778 prepare_daemon (void) 779 { 780 struct GNUNET_SCHEDULER_Task *ret; 781 fd_set rs; 782 fd_set ws; 783 fd_set es; 784 struct GNUNET_NETWORK_FDSet *wrs; 785 struct GNUNET_NETWORK_FDSet *wws; 786 int max; 787 MHD_UNSIGNED_LONG_LONG timeout; 788 int haveto; 789 struct GNUNET_TIME_Relative tv; 790 791 FD_ZERO (&rs); 792 FD_ZERO (&ws); 793 FD_ZERO (&es); 794 wrs = GNUNET_NETWORK_fdset_create (); 795 wws = GNUNET_NETWORK_fdset_create (); 796 max = -1; 797 GNUNET_assert (MHD_YES == 798 MHD_get_fdset (mhd, 799 &rs, 800 &ws, 801 &es, 802 &max)); 803 haveto = MHD_get_timeout (mhd, &timeout); 804 if (haveto == MHD_YES) 805 tv = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS, 806 timeout); 807 else 808 tv = GNUNET_TIME_UNIT_FOREVER_REL; 809 GNUNET_NETWORK_fdset_copy_native (wrs, &rs, max + 1); 810 GNUNET_NETWORK_fdset_copy_native (wws, &ws, max + 1); 811 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 812 "Adding run_daemon select task\n"); 813 ret = GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_HIGH, 814 tv, 815 wrs, 816 wws, 817 &run_daemon, 818 NULL); 819 GNUNET_NETWORK_fdset_destroy (wrs); 820 GNUNET_NETWORK_fdset_destroy (wws); 821 return ret; 822 } 823 824 825 /** 826 * Main function that will be run by the scheduler. 827 * 828 * @param cls closure 829 * @param args remaining command-line arguments 830 * @param cfgfile name of the configuration file used (for saving, can be 831 * NULL!) 832 * @param config configuration 833 */ 834 static void 835 run (void *cls, 836 char *const *args, 837 const char *cfgfile, 838 const struct GNUNET_CONFIGURATION_Handle *config) 839 { 840 int fh; 841 uint16_t port; 842 enum TALER_MHD_GlobalOptions go; 843 844 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 845 "Starting frosix-httpd\n"); 846 go = TALER_MHD_GO_NONE; 847 if (FH_connection_close) 848 go |= TALER_MHD_GO_FORCE_CONNECTION_CLOSE; 849 FH_load_terms (config); 850 TALER_MHD_setup (go); 851 FH_cfg = config; 852 global_result = GNUNET_SYSERR; 853 GNUNET_SCHEDULER_add_shutdown (&do_shutdown, 854 NULL); 855 if (GNUNET_OK != 856 TALER_config_get_amount (config, 857 "authorization-question", 858 "COST", 859 &FH_question_cost)) 860 { 861 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, 862 "authorization-question", 863 "COST"); 864 GNUNET_SCHEDULER_shutdown (); 865 return; 866 } 867 if (GNUNET_OK != 868 TALER_config_get_amount (config, 869 "frosix", 870 "ANNUAL_FEE", 871 &FH_annual_fee)) 872 { 873 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, 874 "frosix", 875 "ANNUAL_FEE"); 876 GNUNET_SCHEDULER_shutdown (); 877 return; 878 } 879 if (GNUNET_OK != 880 TALER_config_get_amount (config, 881 "frosix", 882 "SIGNATURE_CREATION_FEE", 883 &FH_signature_creation_fee)) 884 { 885 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, 886 "frosix", 887 "SIGNATURE_CREATION_FEE"); 888 GNUNET_SCHEDULER_shutdown (); 889 return; 890 } 891 if (GNUNET_OK != 892 GNUNET_CONFIGURATION_get_value_string (config, 893 "frosix-merchant-backend", 894 "PAYMENT_BACKEND_URL", 895 &FH_backend_url)) 896 { 897 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, 898 "frosix-merchant-backend", 899 "PAYMENT_BACKEND_URL"); 900 GNUNET_SCHEDULER_shutdown (); 901 return; 902 } 903 if ( (0 != strncasecmp ("https://", 904 FH_backend_url, 905 strlen ("https://"))) && 906 (0 != strncasecmp ("http://", 907 FH_backend_url, 908 strlen ("http://"))) ) 909 { 910 GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, 911 "frosix-merchant-backend", 912 "PAYMENT_BACKEND_URL", 913 "Must be HTTP(S) URL"); 914 GNUNET_SCHEDULER_shutdown (); 915 return; 916 } 917 918 if ( (0 == strcasecmp ("https://", 919 FH_backend_url)) || 920 (0 == strcasecmp ("http://", 921 FH_backend_url)) ) 922 { 923 GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, 924 "frosix-merchant-backend", 925 "PAYMENT_BACKEND_URL", 926 "Must have domain name"); 927 GNUNET_SCHEDULER_shutdown (); 928 return; 929 } 930 931 if (GNUNET_OK != 932 GNUNET_CONFIGURATION_get_value_string (config, 933 "frosix", 934 "FULFILLMENT_URL", 935 &FH_fulfillment_url)) 936 { 937 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, 938 "frosix", 939 "FULFILLMENT_URL"); 940 GNUNET_SCHEDULER_shutdown (); 941 return; 942 } 943 944 if (GNUNET_OK != 945 GNUNET_CONFIGURATION_get_value_string (config, 946 "frosix", 947 "BUSINESS_NAME", 948 &FH_business_name)) 949 { 950 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, 951 "frosix", 952 "BUSINESS_NAME"); 953 GNUNET_SCHEDULER_shutdown (); 954 return; 955 } 956 957 { 958 char *provider_salt; 959 if (GNUNET_OK != 960 GNUNET_CONFIGURATION_get_value_string (config, 961 "frosix", 962 "PROVIDER_SALT", 963 &provider_salt)) 964 { 965 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, 966 "frosix", 967 "PROVIDER_SALT"); 968 GNUNET_SCHEDULER_shutdown (); 969 return; 970 } 971 GNUNET_assert (GNUNET_YES == 972 GNUNET_CRYPTO_kdf (&FH_provider_salt, 973 sizeof (FH_provider_salt), 974 "frosix-provider-salt", 975 strlen ("frosix-provider-salt"), 976 provider_salt, 977 strlen (provider_salt), 978 NULL, 979 0)); 980 GNUNET_free (provider_salt); 981 } 982 983 { 984 char *secret_provider_salt; 985 if (GNUNET_OK != 986 GNUNET_CONFIGURATION_get_value_string (config, 987 "frosix", 988 "SECRET_PROVIDER_SALT", 989 &secret_provider_salt)) 990 { 991 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, 992 "frosix", 993 "SECRET_PROVIDER_SALT"); 994 GNUNET_SCHEDULER_shutdown (); 995 return; 996 } 997 GNUNET_assert (GNUNET_YES == 998 GNUNET_CRYPTO_kdf (&FH_secret_provider_salt, 999 sizeof (FH_secret_provider_salt), 1000 "secret-frosix-provider-salt", 1001 strlen ("secret-frosix-provider-salt"), 1002 secret_provider_salt, 1003 strlen (secret_provider_salt), 1004 NULL, 1005 0)); 1006 GNUNET_free (secret_provider_salt); 1007 } 1008 1009 { 1010 char *private_signature_key; 1011 1012 if (GNUNET_OK != 1013 GNUNET_CONFIGURATION_get_value_string (config, 1014 "frosix", 1015 "PRIVATE_SIG_KEY", 1016 &private_signature_key)) 1017 { 1018 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, 1019 "frosix", 1020 "PRIVATE_SIG_KEY"); 1021 GNUNET_SCHEDULER_shutdown (); 1022 return; 1023 } 1024 1025 GNUNET_assert (GNUNET_YES == 1026 GNUNET_STRINGS_string_to_data (private_signature_key, 1027 strlen ( 1028 private_signature_key), 1029 &FH_priv_sig_key, 1030 sizeof (FH_priv_sig_key))); 1031 1032 GNUNET_CRYPTO_eddsa_key_get_public (&FH_priv_sig_key, 1033 &FH_pub_sig_key); 1034 1035 GNUNET_free (private_signature_key); 1036 } 1037 1038 /* setup HTTP client event loop */ 1039 FH_ctx = GNUNET_CURL_init (&GNUNET_CURL_gnunet_scheduler_reschedule, 1040 &rc); 1041 rc = GNUNET_CURL_gnunet_rc_create (FH_ctx); 1042 if (NULL != userpass) 1043 GNUNET_CURL_set_userpass (FH_ctx, 1044 userpass); 1045 if (NULL != keyfile) 1046 GNUNET_CURL_set_tlscert (FH_ctx, 1047 certtype, 1048 certfile, 1049 keyfile, 1050 keypass); 1051 if (NULL == apikey) 1052 { 1053 (void) GNUNET_CONFIGURATION_get_value_string (config, 1054 "frosix-merchant-backend", 1055 "API_KEY", 1056 &apikey); 1057 } 1058 if (NULL != apikey) 1059 { 1060 char *auth_header; 1061 1062 GNUNET_asprintf (&auth_header, 1063 "%s: %s", 1064 MHD_HTTP_HEADER_AUTHORIZATION, 1065 apikey); 1066 if (GNUNET_OK != 1067 GNUNET_CURL_append_header (FH_ctx, 1068 auth_header)) 1069 { 1070 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 1071 "Failed so set %s header, trying without\n", 1072 MHD_HTTP_HEADER_AUTHORIZATION); 1073 } 1074 GNUNET_free (auth_header); 1075 } 1076 1077 if (NULL == 1078 (db = FROSIX_DB_plugin_load (config))) 1079 { 1080 GNUNET_SCHEDULER_shutdown (); 1081 return; 1082 } 1083 1084 if (GNUNET_OK != 1085 db->connect (db->cls)) 1086 { 1087 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 1088 "Database not setup. Did you run frosix-dbinit?\n"); 1089 GNUNET_SCHEDULER_shutdown (); 1090 return; 1091 } 1092 1093 fh = TALER_MHD_bind (config, 1094 "frosix", 1095 &port); 1096 if ( (0 == port) && 1097 (-1 == fh) ) 1098 { 1099 GNUNET_SCHEDULER_shutdown (); 1100 return; 1101 } 1102 mhd = MHD_start_daemon (MHD_USE_SUSPEND_RESUME | MHD_USE_DUAL_STACK, 1103 port, 1104 NULL, NULL, 1105 &url_handler, NULL, 1106 MHD_OPTION_LISTEN_SOCKET, fh, 1107 MHD_OPTION_NOTIFY_COMPLETED, 1108 &handle_mhd_completion_callback, NULL, 1109 MHD_OPTION_CONNECTION_TIMEOUT, (unsigned 1110 int) 10 /* 10s */, 1111 MHD_OPTION_END); 1112 if (NULL == mhd) 1113 { 1114 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 1115 "Failed to launch HTTP service (port %u in use?), exiting.\n", 1116 port); 1117 GNUNET_SCHEDULER_shutdown (); 1118 return; 1119 } 1120 global_result = GNUNET_OK; 1121 mhd_task = prepare_daemon (); 1122 } 1123 1124 1125 /** 1126 * The main function of the serve tool 1127 * 1128 * @param argc number of arguments from the command line 1129 * @param argv command line arguments 1130 * @return 0 ok, 1 on error 1131 */ 1132 int 1133 main (int argc, 1134 char *const *argv) 1135 { 1136 enum GNUNET_GenericReturnValue res; 1137 struct GNUNET_GETOPT_CommandLineOption options[] = { 1138 GNUNET_GETOPT_option_string ('A', 1139 "auth", 1140 "USERNAME:PASSWORD", 1141 "use the given USERNAME and PASSWORD for client authentication", 1142 &userpass), 1143 GNUNET_GETOPT_option_flag ('C', 1144 "connection-close", 1145 "force HTTP connections to be closed after each request", 1146 &FH_connection_close), 1147 GNUNET_GETOPT_option_string ('k', 1148 "key", 1149 "KEYFILE", 1150 "file with the private TLS key for TLS client authentication", 1151 &keyfile), 1152 GNUNET_GETOPT_option_string ('p', 1153 "pass", 1154 "KEYFILEPASSPHRASE", 1155 "passphrase needed to decrypt the TLS client private key file", 1156 &keypass), 1157 GNUNET_GETOPT_option_string ('K', 1158 "apikey", 1159 "APIKEY", 1160 "API key to use in the HTTP request to the merchant backend", 1161 &apikey), 1162 GNUNET_GETOPT_option_string ('t', 1163 "type", 1164 "CERTTYPE", 1165 "type of the TLS client certificate, defaults to PEM if not specified", 1166 &certtype), 1167 GNUNET_GETOPT_OPTION_END 1168 }; 1169 1170 /* FIRST get the libtalerutil initialization out 1171 of the way. Then throw that one away, and force 1172 the FROSIX defaults to be used! */ 1173 (void) TALER_project_data_default (); 1174 GNUNET_OS_init (FROSIX_project_data_default ()); 1175 res = GNUNET_PROGRAM_run (argc, argv, 1176 "frosix-httpd", 1177 "Frosix HTTP interface", 1178 options, &run, NULL); 1179 if (GNUNET_SYSERR == res) 1180 return 3; 1181 if (GNUNET_NO == res) 1182 return 0; 1183 return (GNUNET_OK == global_result) ? 0 : 1; 1184 }