anastasis-httpd_policy-upload.c (43896B)
1 /* 2 This file is part of Anastasis 3 Copyright (C) 2021 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 anastasis-httpd_policy.c 18 * @brief functions to handle incoming requests on /policy/ 19 * @author Dennis Neufeld 20 * @author Dominik Meister 21 * @author Christian Grothoff 22 */ 23 #include "platform.h" 24 struct PolicyUploadContext; 25 #define TALER_MERCHANT_POST_PRIVATE_ORDERS_RESULT_CLOSURE struct \ 26 PolicyUploadContext 27 #define TALER_MERCHANT_GET_PRIVATE_ORDER_RESULT_CLOSURE struct \ 28 PolicyUploadContext 29 #include "anastasis-httpd.h" 30 #include "anastasis-httpd_policy.h" 31 #include "anastasis_service.h" 32 #include <gnunet/gnunet_util_lib.h> 33 #include <gnunet/gnunet_rest_lib.h> 34 #include <taler/taler_json_lib.h> 35 #include <taler/taler_merchant_service.h> 36 #include <taler/taler_signatures.h> 37 #include <taler/merchant/post-private-orders.h> 38 #include <taler/merchant/get-private-orders-ORDER_ID.h> 39 40 /** 41 * How long do we hold an HTTP client connection if 42 * we are awaiting payment before giving up? 43 */ 44 #define CHECK_PAYMENT_GENERIC_TIMEOUT GNUNET_TIME_relative_multiply ( \ 45 GNUNET_TIME_UNIT_SECONDS, 30) 46 47 48 /** 49 * Context for an upload operation. 50 */ 51 struct PolicyUploadContext 52 { 53 54 /** 55 * Signature of the account holder. 56 */ 57 struct ANASTASIS_AccountSignatureP account_sig; 58 59 /** 60 * Public key of the account holder. 61 */ 62 struct ANASTASIS_CRYPTO_AccountPublicKeyP account; 63 64 /** 65 * Hash of the upload we are receiving right now (as promised 66 * by the client, to be verified!). 67 */ 68 struct GNUNET_HashCode new_policy_upload_hash; 69 70 /** 71 * Hash context for the upload. 72 */ 73 struct GNUNET_HashContext *hash_ctx; 74 75 /** 76 * Kept in DLL for shutdown handling while suspended. 77 */ 78 struct PolicyUploadContext *next; 79 80 /** 81 * Kept in DLL for shutdown handling while suspended. 82 */ 83 struct PolicyUploadContext *prev; 84 85 /** 86 * Used while suspended for resumption. 87 */ 88 struct MHD_Connection *con; 89 90 /** 91 * Upload, with as many bytes as we have received so far. 92 */ 93 char *upload; 94 95 /** 96 * Meta data uploaded by the client, or NULL for none. 97 */ 98 void *meta_data; 99 100 /** 101 * Number of bytes in @e meta_data. 102 */ 103 size_t meta_data_size; 104 105 /** 106 * Used while we are awaiting proposal creation. 107 */ 108 struct TALER_MERCHANT_PostPrivateOrdersHandle *po; 109 110 /** 111 * Used while we are waiting payment. 112 */ 113 struct TALER_MERCHANT_GetPrivateOrderHandle *cpo; 114 115 /** 116 * HTTP response code to use on resume, if non-NULL. 117 */ 118 struct MHD_Response *resp; 119 120 /** 121 * Order under which the client promised payment, or NULL. 122 */ 123 const char *order_id; 124 125 /** 126 * Payment Identifier 127 */ 128 struct ANASTASIS_PaymentSecretP payment_identifier; 129 130 /** 131 * Creation time of the payment record named by @e payment_identifier, 132 * zero if there is none. Non-zero means the client is asking about a 133 * payment we already know, so the answer is a repeated payment request 134 * rather than a fresh order. 135 */ 136 struct GNUNET_TIME_Timestamp existing_pi_timestamp; 137 138 /** 139 * When does the operation timeout? 140 */ 141 struct GNUNET_TIME_Absolute timeout; 142 143 /** 144 * How long must the account be valid? Determines whether we should 145 * trigger payment, and if so how much. 146 */ 147 struct GNUNET_TIME_Timestamp end_date; 148 149 /** 150 * How long is the account already valid? 151 * Determines how much the user needs to pay. 152 */ 153 struct GNUNET_TIME_Timestamp paid_until; 154 155 /** 156 * Expected total upload size. 157 */ 158 size_t upload_size; 159 160 /** 161 * Current offset for the upload. 162 */ 163 size_t upload_off; 164 165 /** 166 * HTTP response code to use on resume, if resp is set. 167 */ 168 unsigned int response_code; 169 170 /** 171 * For how many years does the client still have 172 * to pay? 173 */ 174 unsigned int years_to_pay; 175 176 /** 177 * true if client provided a payment secret / order ID? 178 */ 179 bool payment_identifier_provided; 180 181 }; 182 183 184 /** 185 * Kept in DLL for shutdown handling while suspended. 186 */ 187 static struct PolicyUploadContext *puc_head; 188 189 /** 190 * Kept in DLL for shutdown handling while suspended. 191 */ 192 static struct PolicyUploadContext *puc_tail; 193 194 195 /** 196 * Service is shutting down, resume all MHD connections NOW. 197 */ 198 void 199 AH_resume_all_bc () 200 { 201 struct PolicyUploadContext *puc; 202 203 while (NULL != (puc = puc_head)) 204 { 205 GNUNET_CONTAINER_DLL_remove (puc_head, 206 puc_tail, 207 puc); 208 if (NULL != puc->po) 209 { 210 TALER_MERCHANT_post_private_orders_cancel (puc->po); 211 puc->po = NULL; 212 } 213 if (NULL != puc->cpo) 214 { 215 TALER_MERCHANT_get_private_order_cancel (puc->cpo); 216 puc->cpo = NULL; 217 } 218 MHD_resume_connection (puc->con); 219 } 220 } 221 222 223 /** 224 * Function called to clean up a backup context. 225 * 226 * @param hc a `struct PolicyUploadContext` 227 */ 228 static void 229 cleanup_ctx (struct TM_HandlerContext *hc) 230 { 231 struct PolicyUploadContext *puc = hc->ctx; 232 233 if (NULL != puc->po) 234 TALER_MERCHANT_post_private_orders_cancel (puc->po); 235 if (NULL != puc->cpo) 236 TALER_MERCHANT_get_private_order_cancel (puc->cpo); 237 if (NULL != puc->hash_ctx) 238 GNUNET_CRYPTO_hash_context_abort (puc->hash_ctx); 239 if (NULL != puc->resp) 240 MHD_destroy_response (puc->resp); 241 GNUNET_free (puc->upload); 242 GNUNET_free (puc->meta_data); 243 GNUNET_free (puc); 244 } 245 246 247 /** 248 * Transmit a payment request for @a order_id on @a connection 249 * 250 * @param[in,out] puc details about the operation 251 * @return #GNUNET_OK on success 252 */ 253 static int 254 make_payment_request (struct PolicyUploadContext *puc) 255 { 256 struct MHD_Response *resp; 257 258 /* request payment via Taler */ 259 resp = MHD_create_response_from_buffer (0, 260 NULL, 261 MHD_RESPMEM_PERSISTENT); 262 if (NULL == resp) 263 { 264 GNUNET_break (0); 265 return GNUNET_SYSERR; 266 } 267 TALER_MHD_add_global_headers (resp, 268 false); 269 { 270 char *hdr; 271 const char *pfx; 272 char *hn; 273 274 if (0 == strncasecmp ("https://", 275 AH_backend_url, 276 strlen ("https://"))) 277 { 278 pfx = "taler://"; 279 hn = &AH_backend_url[strlen ("https://")]; 280 } 281 else if (0 == strncasecmp ("http://", 282 AH_backend_url, 283 strlen ("http://"))) 284 { 285 pfx = "taler+http://"; 286 hn = &AH_backend_url[strlen ("http://")]; 287 } 288 else 289 { 290 GNUNET_break (0); 291 MHD_destroy_response (resp); 292 return GNUNET_SYSERR; 293 } 294 if (0 == strlen (hn)) 295 { 296 GNUNET_break (0); 297 MHD_destroy_response (resp); 298 return GNUNET_SYSERR; 299 } 300 { 301 char *order_id; 302 303 order_id = GNUNET_STRINGS_data_to_string_alloc ( 304 &puc->payment_identifier, 305 sizeof (puc->payment_identifier)); 306 GNUNET_asprintf (&hdr, 307 "%spay/%s%s/", 308 pfx, 309 hn, 310 order_id); 311 GNUNET_free (order_id); 312 } 313 GNUNET_break (MHD_YES == 314 MHD_add_response_header (resp, 315 ANASTASIS_HTTP_HEADER_TALER, 316 hdr)); 317 GNUNET_free (hdr); 318 } 319 puc->resp = resp; 320 puc->response_code = MHD_HTTP_PAYMENT_REQUIRED; 321 return GNUNET_OK; 322 } 323 324 325 /** 326 * Callbacks of this type are used to serve the result of submitting a 327 * POST /private/orders request to a merchant. 328 * 329 * @param cls our `struct PolicyUploadContext` 330 * @param por response details 331 */ 332 static void 333 proposal_cb (struct PolicyUploadContext *puc, 334 const struct TALER_MERCHANT_PostPrivateOrdersResponse *por) 335 { 336 enum GNUNET_DB_QueryStatus qs; 337 338 puc->po = NULL; 339 GNUNET_CONTAINER_DLL_remove (puc_head, 340 puc_tail, 341 puc); 342 MHD_resume_connection (puc->con); 343 AH_trigger_daemon (NULL); 344 if (MHD_HTTP_OK != por->hr.http_status) 345 { 346 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 347 "Backend returned status %u/%d when trying to setup order\n", 348 por->hr.http_status, 349 (int) por->hr.ec); 350 puc->resp = TALER_MHD_MAKE_JSON_PACK ( 351 GNUNET_JSON_pack_uint64 ("code", 352 TALER_EC_SYNC_PAYMENT_CREATE_BACKEND_ERROR), 353 GNUNET_JSON_pack_string ("hint", 354 "Failed to setup order with merchant backend"), 355 GNUNET_JSON_pack_uint64 ("backend-ec", 356 por->hr.ec), 357 GNUNET_JSON_pack_uint64 ("backend-http-status", 358 por->hr.http_status), 359 GNUNET_JSON_pack_allow_null ( 360 GNUNET_JSON_pack_object_incref ("backend-reply", 361 (json_t *) por->hr.reply))); 362 puc->response_code = MHD_HTTP_BAD_GATEWAY; 363 return; 364 } 365 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 366 "Storing payment request for order `%s'\n", 367 por->details.ok.order_id); 368 369 /* The row records the asking price in the primary currency: which of 370 the offered currencies the wallet will settle in is not known until 371 it actually pays. */ 372 qs = ANASTASIS_DB_do_insert_recdoc_payment ( 373 &puc->account, 374 (uint32_t) AH_post_counter, 375 &puc->payment_identifier, 376 AH_primary_price (&AH_annual_fees)); 377 if (0 >= qs) 378 { 379 GNUNET_break (0); 380 puc->resp = TALER_MHD_make_error ( 381 TALER_EC_GENERIC_DB_STORE_FAILED, 382 "insert recdoc payment"); 383 puc->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR; 384 return; 385 } 386 if (GNUNET_OK != 387 make_payment_request (puc)) 388 { 389 GNUNET_break (0); 390 puc->resp = TALER_MHD_make_error ( 391 TALER_EC_GENERIC_DB_STORE_FAILED, 392 "failed to initiate payment"); 393 puc->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR; 394 } 395 } 396 397 398 /** 399 * Callback to process a GET /check-payment request 400 * 401 * @param cls our `struct PolicyUploadContext` 402 * @param osr order status 403 */ 404 static void 405 check_payment_cb (struct PolicyUploadContext *puc, 406 const struct TALER_MERCHANT_GetPrivateOrderResponse *osr) 407 { 408 const struct TALER_MERCHANT_HttpResponse *hr = &osr->hr; 409 410 /* refunds are not supported, verify */ 411 puc->cpo = NULL; 412 GNUNET_CONTAINER_DLL_remove (puc_head, 413 puc_tail, 414 puc); 415 MHD_resume_connection (puc->con); 416 AH_trigger_daemon (NULL); 417 switch (hr->http_status) 418 { 419 case MHD_HTTP_OK: 420 GNUNET_assert (NULL != osr); 421 break; /* processed below */ 422 case MHD_HTTP_UNAUTHORIZED: 423 puc->resp = TALER_MHD_make_error ( 424 TALER_EC_ANASTASIS_GENERIC_PAYMENT_CHECK_UNAUTHORIZED, 425 NULL); 426 puc->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR; 427 return; 428 default: 429 puc->resp = TALER_MHD_make_error ( 430 TALER_EC_ANASTASIS_GENERIC_BACKEND_ERROR, 431 "failed to initiate payment"); 432 puc->response_code = MHD_HTTP_BAD_GATEWAY; 433 return; 434 } 435 436 GNUNET_assert (MHD_HTTP_OK == hr->http_status); 437 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 438 "Payment status checked: %d\n", 439 osr->details.ok.status); 440 switch (osr->details.ok.status) 441 { 442 case TALER_MERCHANT_OSC_PAID: 443 { 444 enum GNUNET_DB_QueryStatus qs; 445 unsigned int years; 446 struct GNUNET_TIME_Relative paid_until; 447 struct TALER_Amount amount; 448 const struct TALER_Amount *fee; 449 450 if (GNUNET_OK != 451 AH_paid_amount (osr, 452 &amount)) 453 { 454 GNUNET_break (0); 455 puc->resp = TALER_MHD_make_error ( 456 TALER_EC_MERCHANT_GENERIC_DB_CONTRACT_CONTENT_INVALID, 457 "no amount given"); 458 puc->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR; 459 return; /* continue as planned */ 460 } 461 /* Divide by the fee in the currency actually paid, not by "the" 462 fee: with several on offer the two need not be the same one. 463 Reachable in production if an operator drops a currency while an 464 order priced in it is still unpaid, hence an error and not an 465 assertion. */ 466 fee = TALER_amount_list_find (&AH_annual_fees, 467 amount.currency); 468 if (NULL == fee) 469 { 470 GNUNET_break (0); 471 puc->resp = TALER_MHD_make_error ( 472 TALER_EC_ANASTASIS_GENERIC_BACKEND_ERROR, 473 "order was paid in a currency this provider does not offer"); 474 puc->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR; 475 return; /* continue as planned */ 476 } 477 years = TALER_amount_divide2 (&amount, 478 fee); 479 paid_until = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_YEARS, 480 years); 481 /* add 1 week grace period, otherwise if a user 482 wants to pay for 1 year, the first seconds 483 would have passed between making the payment 484 and our subsequent check if +1 year was 485 paid... So we actually say 1 year = 52 weeks 486 on the server, while the client calculates 487 with 365 days. */ 488 paid_until = GNUNET_TIME_relative_add (paid_until, 489 GNUNET_TIME_UNIT_WEEKS); 490 491 qs = ANASTASIS_DB_do_update_account_lifetime ( 492 &puc->account, 493 &puc->payment_identifier, 494 GNUNET_TIME_timestamp_get (), 495 paid_until, 496 &puc->paid_until); 497 if (0 <= qs) 498 return; /* continue as planned */ 499 GNUNET_break (0); 500 puc->resp = TALER_MHD_make_error ( 501 TALER_EC_GENERIC_DB_FETCH_FAILED, 502 "do update account lifetime"); 503 puc->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR; 504 return; /* continue as planned */ 505 } 506 case TALER_MERCHANT_OSC_UNPAID: 507 case TALER_MERCHANT_OSC_CLAIMED: 508 break; 509 } 510 if (! GNUNET_TIME_absolute_is_zero (puc->existing_pi_timestamp.abs_time)) 511 { 512 /* repeat payment request */ 513 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 514 "Repeating payment request\n"); 515 if (GNUNET_OK != 516 make_payment_request (puc)) 517 { 518 GNUNET_break (0); 519 puc->resp = TALER_MHD_make_error ( 520 TALER_EC_GENERIC_DB_STORE_FAILED, 521 "failed to initiate payment"); 522 puc->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR; 523 } 524 return; 525 } 526 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 527 "Timeout waiting for payment\n"); 528 puc->resp = TALER_MHD_make_error (TALER_EC_SYNC_PAYMENT_GENERIC_TIMEOUT, 529 "Timeout awaiting promised payment"); 530 GNUNET_assert (NULL != puc->resp); 531 puc->response_code = MHD_HTTP_REQUEST_TIMEOUT; 532 } 533 534 535 /** 536 * Helper function used to ask our backend to await 537 * a payment for the user's account. 538 * 539 * @param puc context to begin payment for. 540 */ 541 static void 542 await_payment (struct PolicyUploadContext *puc) 543 { 544 struct GNUNET_TIME_Relative timeout 545 = GNUNET_TIME_absolute_get_remaining (puc->timeout); 546 547 GNUNET_CONTAINER_DLL_insert (puc_head, 548 puc_tail, 549 puc); 550 MHD_suspend_connection (puc->con); 551 { 552 char *order_id; 553 554 order_id = GNUNET_STRINGS_data_to_string_alloc ( 555 &puc->payment_identifier, 556 sizeof(struct ANASTASIS_PaymentSecretP)); 557 puc->cpo = TALER_MERCHANT_get_private_order_create (AH_ctx, 558 AH_backend_url, 559 order_id); 560 GNUNET_assert (NULL != puc->cpo); 561 GNUNET_free (order_id); 562 GNUNET_assert ( 563 GNUNET_OK == 564 TALER_MERCHANT_get_private_order_set_options ( 565 puc->cpo, 566 TALER_MERCHANT_get_private_order_option_timeout (timeout))); 567 GNUNET_assert ( 568 TALER_EC_NONE == 569 TALER_MERCHANT_get_private_order_start (puc->cpo, 570 &check_payment_cb, 571 puc)); 572 } 573 AH_trigger_curl (); 574 } 575 576 577 /** 578 * Helper function used to ask our backend to begin processing a 579 * payment for the user's account. May perform asynchronous 580 * operations by suspending the connection if required. 581 * 582 * @param puc context to begin payment for. 583 * @return MHD status code 584 */ 585 static enum MHD_Result 586 begin_payment (struct PolicyUploadContext *puc) 587 { 588 json_t *order; 589 590 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 591 "Suspending connection while creating order at `%s'\n", 592 AH_backend_url); 593 { 594 char *order_id; 595 struct TALER_AmountList upload_fees; 596 597 TALER_amount_list_copy (&upload_fees, 598 &AH_annual_fees); 599 if (GNUNET_OK != 600 TALER_amount_list_multiply (&upload_fees, 601 puc->years_to_pay)) 602 { 603 GNUNET_break_op (0); 604 TALER_amount_list_free (&upload_fees); 605 return TALER_MHD_reply_with_error (puc->con, 606 MHD_HTTP_BAD_REQUEST, 607 TALER_EC_GENERIC_PARAMETER_MALFORMED, 608 "storage_duration_years"); 609 } 610 611 order_id = GNUNET_STRINGS_data_to_string_alloc ( 612 &puc->payment_identifier, 613 sizeof(struct ANASTASIS_PaymentSecretP)); 614 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 615 "Creating order for %u years with payment of %s\n", 616 puc->years_to_pay, 617 TALER_amount_list2s (&upload_fees)); 618 order = AH_make_order (order_id, 619 "Anastasis policy storage fee", 620 &upload_fees); 621 TALER_amount_list_free (&upload_fees); 622 GNUNET_free (order_id); 623 GNUNET_assert (0 == 624 json_object_set_new ( 625 order, 626 "products", 627 json_pack ("[{s:s,s:I,s:s}]", 628 "description", "policy storage fee", 629 "quantity", (json_int_t) puc->years_to_pay, 630 "unit", "years"))); 631 } 632 /* Only link into the list once we are actually about to suspend: the list 633 exists to resume suspended connections at shutdown, and cleanup_ctx() 634 frees the context without unlinking it, so an error return above would 635 otherwise leave a dangling entry behind. */ 636 GNUNET_CONTAINER_DLL_insert (puc_head, 637 puc_tail, 638 puc); 639 MHD_suspend_connection (puc->con); 640 puc->po = TALER_MERCHANT_post_private_orders_create (AH_ctx, 641 AH_backend_url, 642 order); 643 GNUNET_assert (NULL != puc->po); 644 GNUNET_assert ( 645 GNUNET_OK == 646 TALER_MERCHANT_post_private_orders_set_options ( 647 puc->po, 648 TALER_MERCHANT_post_private_orders_option_create_token (false))); 649 GNUNET_assert (TALER_EC_NONE == 650 TALER_MERCHANT_post_private_orders_start (puc->po, 651 &proposal_cb, 652 puc)); 653 AH_trigger_curl (); 654 json_decref (order); 655 return MHD_YES; 656 } 657 658 659 /** 660 * Prepare to receive a payment, possibly requesting it, or just waiting 661 * for it to be completed by the client. 662 * 663 * @param puc context to prepare payment for 664 * @return MHD status 665 */ 666 static enum MHD_Result 667 prepare_payment (struct PolicyUploadContext *puc) 668 { 669 if (! puc->payment_identifier_provided) 670 { 671 GNUNET_CRYPTO_random_block ( 672 &puc->payment_identifier, 673 sizeof (struct ANASTASIS_PaymentSecretP)); 674 puc->payment_identifier_provided = true; 675 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 676 "No payment identifier, initiating payment\n"); 677 return begin_payment (puc); 678 } 679 await_payment (puc); 680 return MHD_YES; 681 } 682 683 684 enum MHD_Result 685 AH_handler_policy_post ( 686 struct MHD_Connection *connection, 687 struct TM_HandlerContext *hc, 688 const struct ANASTASIS_CRYPTO_AccountPublicKeyP *account_pub, 689 const char *recovery_data, 690 size_t *recovery_data_size) 691 { 692 struct PolicyUploadContext *puc = hc->ctx; 693 694 if (NULL == puc) 695 { 696 /* first call, setup internals */ 697 puc = GNUNET_new (struct PolicyUploadContext); 698 hc->ctx = puc; 699 hc->cc = &cleanup_ctx; 700 puc->con = connection; 701 702 TALER_MHD_parse_request_header_auto (connection, 703 ANASTASIS_HTTP_HEADER_PAYMENT_IDENTIFIER, 704 &puc->payment_identifier, 705 puc->payment_identifier_provided); 706 puc->account = *account_pub; 707 708 /* check for meta-data */ 709 { 710 const char *metas; 711 712 metas = MHD_lookup_connection_value (connection, 713 MHD_HEADER_KIND, 714 ANASTASIS_HTTP_HEADER_POLICY_META_DATA); 715 if (NULL == metas) 716 { 717 GNUNET_break_op (0); 718 return TALER_MHD_reply_with_error ( 719 connection, 720 MHD_HTTP_BAD_REQUEST, 721 TALER_EC_GENERIC_HTTP_HEADERS_MALFORMED, 722 ANASTASIS_HTTP_HEADER_POLICY_META_DATA 723 " header must be present"); 724 } 725 if (GNUNET_OK != 726 GNUNET_STRINGS_string_to_data_alloc (metas, 727 strlen (metas), 728 &puc->meta_data, 729 &puc->meta_data_size)) 730 { 731 GNUNET_break_op (0); 732 return TALER_MHD_reply_with_error ( 733 connection, 734 MHD_HTTP_BAD_REQUEST, 735 TALER_EC_GENERIC_HTTP_HEADERS_MALFORMED, 736 ANASTASIS_HTTP_HEADER_POLICY_META_DATA 737 " header must include a base32-encoded value"); 738 } 739 } 740 TALER_MHD_parse_request_header_auto_t (connection, 741 ANASTASIS_HTTP_HEADER_POLICY_SIGNATURE, 742 &puc->account_sig); 743 { 744 /* Check if header contains an ETAG */ 745 const char *etag; 746 747 etag = MHD_lookup_connection_value (connection, 748 MHD_HEADER_KIND, 749 MHD_HTTP_HEADER_IF_NONE_MATCH); 750 if ( (NULL == etag) || 751 (2 >= strlen (etag)) || 752 ('"' != etag[0]) || 753 ('"' != etag[strlen (etag) - 1]) || 754 (GNUNET_OK != 755 GNUNET_STRINGS_string_to_data (etag + 1, 756 strlen (etag) - 2, 757 &puc->new_policy_upload_hash, 758 sizeof (puc->new_policy_upload_hash)) 759 ) ) 760 { 761 GNUNET_break_op (0); 762 return TALER_MHD_reply_with_error (connection, 763 MHD_HTTP_BAD_REQUEST, 764 TALER_EC_ANASTASIS_POLICY_BAD_IF_MATCH, 765 MHD_HTTP_HEADER_IF_NONE_MATCH 766 " header must include a base32-encoded SHA-512 hash"); 767 } 768 } 769 /* validate signature */ 770 { 771 struct ANASTASIS_UploadSignaturePS usp = { 772 .purpose.size = htonl (sizeof (usp)), 773 .purpose.purpose = htonl (TALER_SIGNATURE_ANASTASIS_POLICY_UPLOAD), 774 .new_recovery_data_hash = puc->new_policy_upload_hash 775 }; 776 777 if (GNUNET_OK != 778 GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_ANASTASIS_POLICY_UPLOAD, 779 &usp, 780 &puc->account_sig.eddsa_sig, 781 &account_pub->pub)) 782 { 783 GNUNET_break_op (0); 784 return TALER_MHD_reply_with_error (connection, 785 MHD_HTTP_FORBIDDEN, 786 TALER_EC_ANASTASIS_POLICY_BAD_SIGNATURE, 787 ANASTASIS_HTTP_HEADER_POLICY_SIGNATURE); 788 } 789 } 790 791 /* Only now, with the account signature verified, do we allocate the 792 upload buffer: sizing it from the client-declared Content-Length before 793 checking who is asking lets anyone force repeated multi-megabyte 794 allocations. */ 795 /* now setup 'puc' */ 796 { 797 const char *lens; 798 unsigned long len; 799 char dummy; 800 801 lens = MHD_lookup_connection_value (connection, 802 MHD_HEADER_KIND, 803 MHD_HTTP_HEADER_CONTENT_LENGTH); 804 if ( (NULL == lens) || 805 (1 != sscanf (lens, 806 "%lu%c", 807 &len, 808 &dummy)) ) 809 { 810 GNUNET_break_op (0); 811 return TALER_MHD_reply_with_error ( 812 connection, 813 MHD_HTTP_BAD_REQUEST, 814 (NULL == lens) 815 ? TALER_EC_ANASTASIS_GENERIC_MISSING_CONTENT_LENGTH 816 : TALER_EC_ANASTASIS_GENERIC_MALFORMED_CONTENT_LENGTH, 817 NULL); 818 } 819 if (len / 1024 / 1024 >= AH_upload_limit_mb) 820 { 821 GNUNET_break_op (0); 822 return TALER_MHD_reply_with_error (connection, 823 MHD_HTTP_PAYLOAD_TOO_LARGE, 824 TALER_EC_SYNC_MALFORMED_CONTENT_LENGTH, 825 "Content-length value not acceptable"); 826 } 827 puc->upload = GNUNET_malloc_large (len); 828 if (NULL == puc->upload) 829 { 830 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, 831 "malloc"); 832 return TALER_MHD_reply_with_error (connection, 833 MHD_HTTP_PAYLOAD_TOO_LARGE, 834 TALER_EC_ANASTASIS_POLICY_OUT_OF_MEMORY_ON_CONTENT_LENGTH, 835 NULL); 836 } 837 puc->upload_size = (size_t) len; 838 } 839 840 puc->timeout = GNUNET_TIME_relative_to_absolute ( 841 CHECK_PAYMENT_GENERIC_TIMEOUT); 842 TALER_MHD_parse_request_timeout (connection, 843 &puc->timeout); 844 845 /* check if the client insists on paying */ 846 { 847 const char *req; 848 unsigned int years; 849 850 req = MHD_lookup_connection_value (connection, 851 MHD_GET_ARGUMENT_KIND, 852 "storage_duration"); 853 if (NULL != req) 854 { 855 char dummy; 856 857 if (1 != sscanf (req, 858 "%u%c", 859 &years, 860 &dummy)) 861 { 862 GNUNET_break_op (0); 863 return TALER_MHD_reply_with_error (connection, 864 MHD_HTTP_BAD_REQUEST, 865 TALER_EC_GENERIC_PARAMETER_MALFORMED, 866 "storage_duration (must be non-negative number)"); 867 } 868 if (years > ANASTASIS_MAX_YEARS_STORAGE) 869 { 870 GNUNET_break_op (0); 871 return TALER_MHD_reply_with_error ( 872 connection, 873 MHD_HTTP_BAD_REQUEST, 874 TALER_EC_GENERIC_PARAMETER_MALFORMED, 875 "storage_duration (exceeds maximum storage duration)"); 876 } 877 } 878 else 879 { 880 years = 1; 881 } 882 puc->end_date = GNUNET_TIME_relative_to_timestamp ( 883 GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_YEARS, 884 years)); 885 } 886 887 /* get ready to hash (done here as we may go async for payments next) */ 888 puc->hash_ctx = GNUNET_CRYPTO_hash_context_start (); 889 890 /* Check database to see if the transaction is permissible */ 891 { 892 struct GNUNET_TIME_Relative rem; 893 894 rem = GNUNET_TIME_absolute_get_remaining (puc->end_date.abs_time); 895 puc->years_to_pay = rem.rel_value_us 896 / GNUNET_TIME_UNIT_YEARS.rel_value_us; 897 if (0 != (rem.rel_value_us % GNUNET_TIME_UNIT_YEARS.rel_value_us)) 898 puc->years_to_pay++; 899 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 900 "Calculated years to pay to be %u until %s\n", 901 puc->years_to_pay, 902 GNUNET_TIME_absolute2s (puc->end_date.abs_time)); 903 904 if (puc->payment_identifier_provided) 905 { 906 /* check if payment identifier is valid (existing and paid) */ 907 bool paid = false; 908 bool valid_counter = false; 909 enum GNUNET_DB_QueryStatus qs; 910 911 qs = ANASTASIS_DB_get_recdoc_payment ( 912 &puc->account, 913 &puc->payment_identifier, 914 &paid, 915 &valid_counter, 916 &puc->existing_pi_timestamp); 917 if (qs < 0) 918 return TALER_MHD_reply_with_error (puc->con, 919 MHD_HTTP_INTERNAL_SERVER_ERROR, 920 TALER_EC_GENERIC_DB_FETCH_FAILED, 921 NULL); 922 if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs) 923 { 924 /* The client named a payment identifier we have no record of. The 925 result specification was not touched in that case, so we must not 926 look at @e paid or @e valid_counter; treat it as unpaid. */ 927 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 928 "Given payment identifier is unknown, initiating payment\n"); 929 puc->payment_identifier_provided = false; 930 if (0 == puc->years_to_pay) 931 puc->years_to_pay = 1; 932 return prepare_payment (puc); 933 } 934 935 if ( (! paid) || 936 (! valid_counter) ) 937 { 938 if (! valid_counter) 939 { 940 puc->payment_identifier_provided = false; 941 if (0 == puc->years_to_pay) 942 puc->years_to_pay = 1; 943 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 944 "Too many uploads with this payment identifier, initiating fresh payment\n") 945 ; 946 } 947 else 948 { 949 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 950 "Given payment identifier not known to be paid, initiating payment\n"); 951 } 952 return prepare_payment (puc); 953 } 954 } 955 956 if (! puc->payment_identifier_provided) 957 { 958 enum GNUNET_DB_QueryStatus qs; 959 struct GNUNET_TIME_Relative rel; 960 961 /* generate fresh payment identifier */ 962 GNUNET_CRYPTO_random_block (&puc->payment_identifier, 963 sizeof (struct ANASTASIS_PaymentSecretP)); 964 /* GNUNET_SYSERR is unreachable: a price list that mixes free and 965 non-free currencies makes anastasis-httpd refuse to start. */ 966 GNUNET_assert (GNUNET_SYSERR != 967 TALER_amount_list_check_uniform (&AH_annual_fees)); 968 if (GNUNET_NO != 969 TALER_amount_list_check_uniform (&AH_annual_fees)) 970 { 971 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 972 "No payment identifier, requesting payment\n"); 973 return begin_payment (puc); 974 } 975 /* Cost is zero, fake "zero" payment having happened */ 976 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 977 "Policy upload is free, allowing upload without payment\n"); 978 qs = ANASTASIS_DB_do_insert_recdoc_payment ( 979 account_pub, 980 AH_post_counter, 981 &puc->payment_identifier, 982 AH_primary_price (&AH_annual_fees)); 983 if (qs <= 0) 984 return TALER_MHD_reply_with_error (puc->con, 985 MHD_HTTP_INTERNAL_SERVER_ERROR, 986 TALER_EC_GENERIC_DB_FETCH_FAILED, 987 NULL); 988 rel = GNUNET_TIME_relative_multiply ( 989 GNUNET_TIME_UNIT_YEARS, 990 ANASTASIS_MAX_YEARS_STORAGE); 991 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 992 "Policy lifetime is %s (%u years)\n", 993 GNUNET_TIME_relative2s (rel, 994 true), 995 ANASTASIS_MAX_YEARS_STORAGE); 996 qs = ANASTASIS_DB_do_update_account_lifetime ( 997 account_pub, 998 &puc->payment_identifier, 999 GNUNET_TIME_relative_to_timestamp (rel), 1000 GNUNET_TIME_UNIT_ZERO, 1001 &puc->paid_until); 1002 if (qs <= 0) 1003 { 1004 GNUNET_break (0); 1005 return TALER_MHD_reply_with_error (puc->con, 1006 MHD_HTTP_INTERNAL_SERVER_ERROR, 1007 TALER_EC_GENERIC_DB_FETCH_FAILED, 1008 NULL); 1009 } 1010 } 1011 } 1012 1013 /* Check if existing policy matches upload (and if, skip it) */ 1014 { 1015 struct GNUNET_HashCode hash; 1016 enum ANASTASIS_DB_AccountStatus as; 1017 uint32_t version; 1018 struct GNUNET_TIME_Timestamp now; 1019 struct GNUNET_TIME_Relative rem; 1020 1021 as = ANASTASIS_DB_get_account ( 1022 account_pub, 1023 &puc->paid_until, 1024 &hash, 1025 &version); 1026 now = GNUNET_TIME_timestamp_get (); 1027 if (GNUNET_TIME_timestamp_cmp (puc->paid_until, 1028 <, 1029 now)) 1030 puc->paid_until = now; 1031 rem = GNUNET_TIME_absolute_get_difference (puc->paid_until.abs_time, 1032 puc->end_date.abs_time); 1033 puc->years_to_pay = rem.rel_value_us 1034 / GNUNET_TIME_UNIT_YEARS.rel_value_us; 1035 if (0 != (rem.rel_value_us % GNUNET_TIME_UNIT_YEARS.rel_value_us)) 1036 puc->years_to_pay++; 1037 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1038 "Calculated years to pay to be %u until %s\n", 1039 puc->years_to_pay, 1040 GNUNET_TIME_absolute2s (puc->end_date.abs_time)); 1041 if ( (ANASTASIS_DB_ACCOUNT_STATUS_VALID_HASH_RETURNED == as) && 1042 (0 != puc->years_to_pay) ) 1043 { 1044 /* user requested extension, force payment */ 1045 as = ANASTASIS_DB_ACCOUNT_STATUS_PAYMENT_REQUIRED; 1046 } 1047 switch (as) 1048 { 1049 case ANASTASIS_DB_ACCOUNT_STATUS_PAYMENT_REQUIRED: 1050 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1051 "Expiration too low, initiating payment\n"); 1052 return prepare_payment (puc); 1053 case ANASTASIS_DB_ACCOUNT_STATUS_HARD_ERROR: 1054 return TALER_MHD_reply_with_error (puc->con, 1055 MHD_HTTP_INTERNAL_SERVER_ERROR, 1056 TALER_EC_GENERIC_DB_FETCH_FAILED, 1057 NULL); 1058 case ANASTASIS_DB_ACCOUNT_STATUS_NO_RESULTS: 1059 /* continue below */ 1060 break; 1061 case ANASTASIS_DB_ACCOUNT_STATUS_VALID_HASH_RETURNED: 1062 if (0 == GNUNET_memcmp (&hash, 1063 &puc->new_policy_upload_hash)) 1064 { 1065 /* Refuse upload: we already have that backup! */ 1066 struct MHD_Response *resp; 1067 enum MHD_Result ret; 1068 char version_s[14]; 1069 1070 GNUNET_snprintf (version_s, 1071 sizeof (version_s), 1072 "%u", 1073 (unsigned int) version); 1074 resp = MHD_create_response_from_buffer (0, 1075 NULL, 1076 MHD_RESPMEM_PERSISTENT); 1077 TALER_MHD_add_global_headers (resp, 1078 false); 1079 GNUNET_break (MHD_YES == 1080 MHD_add_response_header (resp, 1081 ANASTASIS_HTTP_HEADER_POLICY_VERSION, 1082 version_s)); 1083 ret = MHD_queue_response (connection, 1084 MHD_HTTP_NOT_MODIFIED, 1085 resp); 1086 GNUNET_break (MHD_YES == ret); 1087 MHD_destroy_response (resp); 1088 return ret; 1089 } 1090 break; 1091 } 1092 } 1093 /* ready to begin! */ 1094 return MHD_YES; 1095 } 1096 1097 if (NULL != puc->resp) 1098 { 1099 enum MHD_Result ret; 1100 1101 /* We generated a response asynchronously, queue that */ 1102 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1103 "Returning asynchronously generated response with HTTP status %u\n", 1104 puc->response_code); 1105 ret = MHD_queue_response (connection, 1106 puc->response_code, 1107 puc->resp); 1108 GNUNET_break (MHD_YES == ret); 1109 MHD_destroy_response (puc->resp); 1110 puc->resp = NULL; 1111 return ret; 1112 } 1113 1114 /* handle upload */ 1115 if (0 != *recovery_data_size) 1116 { 1117 /* This is NOT an MHD invariant we may assume: with the default client 1118 discipline MHD accepts a request carrying both a "Content-Length" and 1119 "Transfer-Encoding: chunked", ignores the former for framing (but keeps 1120 it retrievable, which is where puc->upload_size came from) and then 1121 hands us however many bytes the chunked body actually contains. So a 1122 client can send more than it declared; refuse it rather than write past 1123 the end of the buffer. */ 1124 if (puc->upload_off + *recovery_data_size > puc->upload_size) 1125 { 1126 GNUNET_break_op (0); 1127 return TALER_MHD_reply_with_error ( 1128 connection, 1129 MHD_HTTP_BAD_REQUEST, 1130 TALER_EC_ANASTASIS_GENERIC_MALFORMED_CONTENT_LENGTH, 1131 "upload is larger than the declared content length"); 1132 } 1133 memcpy (&puc->upload[puc->upload_off], 1134 recovery_data, 1135 *recovery_data_size); 1136 puc->upload_off += *recovery_data_size; 1137 GNUNET_CRYPTO_hash_context_read (puc->hash_ctx, 1138 recovery_data, 1139 *recovery_data_size); 1140 *recovery_data_size = 0; 1141 return MHD_YES; 1142 } 1143 1144 if ( (0 == puc->upload_off) && 1145 (0 != puc->upload_size) && 1146 (NULL == puc->resp) ) 1147 { 1148 /* wait for upload */ 1149 return MHD_YES; 1150 } 1151 1152 /* finished with upload, check hash */ 1153 if (NULL != puc->hash_ctx) 1154 { 1155 struct GNUNET_HashCode our_hash; 1156 1157 GNUNET_CRYPTO_hash_context_finish (puc->hash_ctx, 1158 &our_hash); 1159 puc->hash_ctx = NULL; 1160 if (0 != GNUNET_memcmp (&our_hash, 1161 &puc->new_policy_upload_hash)) 1162 { 1163 GNUNET_break_op (0); 1164 return TALER_MHD_reply_with_error (connection, 1165 MHD_HTTP_BAD_REQUEST, 1166 TALER_EC_ANASTASIS_POLICY_INVALID_UPLOAD, 1167 "Data uploaded does not match Etag promise"); 1168 } 1169 } 1170 1171 /* store backup to database */ 1172 { 1173 enum ANASTASIS_DB_StoreStatus ss; 1174 uint32_t version = UINT32_MAX; 1175 char version_s[14]; 1176 char expir_s[32]; 1177 1178 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1179 "Uploading recovery document\n"); 1180 ss = ANASTASIS_DB_do_insert_recovery_document ( 1181 &puc->account, 1182 &puc->account_sig, 1183 &puc->new_policy_upload_hash, 1184 puc->upload, 1185 puc->upload_size, 1186 puc->meta_data, 1187 puc->meta_data_size, 1188 &puc->payment_identifier, 1189 &version); 1190 GNUNET_snprintf (version_s, 1191 sizeof (version_s), 1192 "%u", 1193 (unsigned int) version); 1194 GNUNET_snprintf (expir_s, 1195 sizeof (expir_s), 1196 "%llu", 1197 (unsigned long long) 1198 (puc->paid_until.abs_time.abs_value_us 1199 / GNUNET_TIME_UNIT_SECONDS.rel_value_us)); 1200 switch (ss) 1201 { 1202 case ANASTASIS_DB_STORE_STATUS_STORE_LIMIT_EXCEEDED: 1203 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1204 "Storage request limit exceeded, requesting payment\n"); 1205 /* This implies that a NEW payment is required, so the order we are 1206 about to create must have a fresh ID. Re-using the identifier the 1207 client supplied would ask the merchant for an order that already 1208 exists, which would not be helpful. */ 1209 GNUNET_CRYPTO_random_block (&puc->payment_identifier, 1210 sizeof (struct ANASTASIS_PaymentSecretP)); 1211 puc->payment_identifier_provided = true; 1212 return begin_payment (puc); 1213 case ANASTASIS_DB_STORE_STATUS_PAYMENT_REQUIRED: 1214 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1215 "Policy store operation requires payment\n"); 1216 /* This again implies that a NEW payment is required, so the order we are 1217 about to create must have a fresh ID. Re-using the identifier the 1218 client supplied would ask the merchant for an order that already 1219 exists, which would not be helpful. */ 1220 GNUNET_CRYPTO_random_block (&puc->payment_identifier, 1221 sizeof (struct ANASTASIS_PaymentSecretP)); 1222 puc->payment_identifier_provided = true; 1223 return begin_payment (puc); 1224 case ANASTASIS_DB_STORE_STATUS_HARD_ERROR: 1225 case ANASTASIS_DB_STORE_STATUS_SOFT_ERROR: 1226 return TALER_MHD_reply_with_error (puc->con, 1227 MHD_HTTP_INTERNAL_SERVER_ERROR, 1228 TALER_EC_GENERIC_DB_FETCH_FAILED, 1229 NULL); 1230 case ANASTASIS_DB_STORE_STATUS_NO_RESULTS: 1231 { 1232 /* database says nothing actually changed, 304 (could 1233 theoretically happen if another equivalent upload succeeded 1234 since we last checked!) */ 1235 struct MHD_Response *resp; 1236 enum MHD_Result ret; 1237 1238 resp = MHD_create_response_from_buffer (0, 1239 NULL, 1240 MHD_RESPMEM_PERSISTENT); 1241 TALER_MHD_add_global_headers (resp, 1242 false); 1243 GNUNET_break (MHD_YES == 1244 MHD_add_response_header (resp, 1245 "Anastasis-Version", 1246 version_s)); 1247 ret = MHD_queue_response (connection, 1248 MHD_HTTP_NOT_MODIFIED, 1249 resp); 1250 GNUNET_break (MHD_YES == ret); 1251 MHD_destroy_response (resp); 1252 return ret; 1253 } 1254 case ANASTASIS_DB_STORE_STATUS_SUCCESS: 1255 /* generate main (204) standard success reply */ 1256 { 1257 struct MHD_Response *resp; 1258 enum MHD_Result ret; 1259 1260 resp = MHD_create_response_from_buffer (0, 1261 NULL, 1262 MHD_RESPMEM_PERSISTENT); 1263 TALER_MHD_add_global_headers (resp, 1264 false); 1265 GNUNET_break (MHD_YES == 1266 MHD_add_response_header (resp, 1267 ANASTASIS_HTTP_HEADER_POLICY_VERSION, 1268 version_s)); 1269 GNUNET_break (MHD_YES == 1270 MHD_add_response_header (resp, 1271 ANASTASIS_HTTP_HEADER_POLICY_EXPIRATION, 1272 expir_s)); 1273 ret = MHD_queue_response (connection, 1274 MHD_HTTP_NO_CONTENT, 1275 resp); 1276 GNUNET_break (MHD_YES == ret); 1277 MHD_destroy_response (resp); 1278 return ret; 1279 } 1280 } 1281 } 1282 GNUNET_break (0); 1283 return MHD_NO; 1284 }