anastasis-httpd_policy-upload.c (42017B)
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 qs = ANASTASIS_DB_do_insert_recdoc_payment ( 370 &puc->account, 371 (uint32_t) AH_post_counter, 372 &puc->payment_identifier, 373 &AH_annual_fee); 374 if (0 >= qs) 375 { 376 GNUNET_break (0); 377 puc->resp = TALER_MHD_make_error ( 378 TALER_EC_GENERIC_DB_STORE_FAILED, 379 "insert recdoc payment"); 380 puc->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR; 381 return; 382 } 383 if (GNUNET_OK != 384 make_payment_request (puc)) 385 { 386 GNUNET_break (0); 387 puc->resp = TALER_MHD_make_error ( 388 TALER_EC_GENERIC_DB_STORE_FAILED, 389 "failed to initiate payment"); 390 puc->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR; 391 } 392 } 393 394 395 /** 396 * Callback to process a GET /check-payment request 397 * 398 * @param cls our `struct PolicyUploadContext` 399 * @param osr order status 400 */ 401 static void 402 check_payment_cb (struct PolicyUploadContext *puc, 403 const struct TALER_MERCHANT_GetPrivateOrderResponse *osr) 404 { 405 const struct TALER_MERCHANT_HttpResponse *hr = &osr->hr; 406 407 /* refunds are not supported, verify */ 408 puc->cpo = NULL; 409 GNUNET_CONTAINER_DLL_remove (puc_head, 410 puc_tail, 411 puc); 412 MHD_resume_connection (puc->con); 413 AH_trigger_daemon (NULL); 414 switch (hr->http_status) 415 { 416 case MHD_HTTP_OK: 417 GNUNET_assert (NULL != osr); 418 break; /* processed below */ 419 case MHD_HTTP_UNAUTHORIZED: 420 puc->resp = TALER_MHD_make_error ( 421 TALER_EC_ANASTASIS_GENERIC_PAYMENT_CHECK_UNAUTHORIZED, 422 NULL); 423 puc->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR; 424 return; 425 default: 426 puc->resp = TALER_MHD_make_error ( 427 TALER_EC_ANASTASIS_GENERIC_BACKEND_ERROR, 428 "failed to initiate payment"); 429 puc->response_code = MHD_HTTP_BAD_GATEWAY; 430 return; 431 } 432 433 GNUNET_assert (MHD_HTTP_OK == hr->http_status); 434 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 435 "Payment status checked: %d\n", 436 osr->details.ok.status); 437 switch (osr->details.ok.status) 438 { 439 case TALER_MERCHANT_OSC_PAID: 440 { 441 enum GNUNET_DB_QueryStatus qs; 442 unsigned int years; 443 struct GNUNET_TIME_Relative paid_until; 444 const json_t *contract; 445 struct TALER_Amount amount; 446 struct GNUNET_JSON_Specification cspec[] = { 447 TALER_JSON_spec_amount_any ("amount", 448 &amount), 449 GNUNET_JSON_spec_end () 450 }; 451 452 contract = osr->details.ok.details.paid.contract_terms; 453 if (GNUNET_OK != 454 GNUNET_JSON_parse (contract, 455 cspec, 456 NULL, NULL)) 457 { 458 GNUNET_break (0); 459 puc->resp = TALER_MHD_make_error ( 460 TALER_EC_MERCHANT_GENERIC_DB_CONTRACT_CONTENT_INVALID, 461 "no amount given"); 462 puc->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR; 463 return; /* continue as planned */ 464 } 465 years = TALER_amount_divide2 (&amount, 466 &AH_annual_fee); 467 paid_until = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_YEARS, 468 years); 469 /* add 1 week grace period, otherwise if a user 470 wants to pay for 1 year, the first seconds 471 would have passed between making the payment 472 and our subsequent check if +1 year was 473 paid... So we actually say 1 year = 52 weeks 474 on the server, while the client calculates 475 with 365 days. */ 476 paid_until = GNUNET_TIME_relative_add (paid_until, 477 GNUNET_TIME_UNIT_WEEKS); 478 479 qs = ANASTASIS_DB_do_update_account_lifetime ( 480 &puc->account, 481 &puc->payment_identifier, 482 GNUNET_TIME_timestamp_get (), 483 paid_until, 484 &puc->paid_until); 485 if (0 <= qs) 486 return; /* continue as planned */ 487 GNUNET_break (0); 488 puc->resp = TALER_MHD_make_error ( 489 TALER_EC_GENERIC_DB_FETCH_FAILED, 490 "do update account lifetime"); 491 puc->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR; 492 return; /* continue as planned */ 493 } 494 case TALER_MERCHANT_OSC_UNPAID: 495 case TALER_MERCHANT_OSC_CLAIMED: 496 break; 497 } 498 if (! GNUNET_TIME_absolute_is_zero (puc->existing_pi_timestamp.abs_time)) 499 { 500 /* repeat payment request */ 501 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 502 "Repeating payment request\n"); 503 if (GNUNET_OK != 504 make_payment_request (puc)) 505 { 506 GNUNET_break (0); 507 puc->resp = TALER_MHD_make_error ( 508 TALER_EC_GENERIC_DB_STORE_FAILED, 509 "failed to initiate payment"); 510 puc->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR; 511 } 512 return; 513 } 514 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 515 "Timeout waiting for payment\n"); 516 puc->resp = TALER_MHD_make_error (TALER_EC_SYNC_PAYMENT_GENERIC_TIMEOUT, 517 "Timeout awaiting promised payment"); 518 GNUNET_assert (NULL != puc->resp); 519 puc->response_code = MHD_HTTP_REQUEST_TIMEOUT; 520 } 521 522 523 /** 524 * Helper function used to ask our backend to await 525 * a payment for the user's account. 526 * 527 * @param puc context to begin payment for. 528 */ 529 static void 530 await_payment (struct PolicyUploadContext *puc) 531 { 532 struct GNUNET_TIME_Relative timeout 533 = GNUNET_TIME_absolute_get_remaining (puc->timeout); 534 535 GNUNET_CONTAINER_DLL_insert (puc_head, 536 puc_tail, 537 puc); 538 MHD_suspend_connection (puc->con); 539 { 540 char *order_id; 541 542 order_id = GNUNET_STRINGS_data_to_string_alloc ( 543 &puc->payment_identifier, 544 sizeof(struct ANASTASIS_PaymentSecretP)); 545 puc->cpo = TALER_MERCHANT_get_private_order_create (AH_ctx, 546 AH_backend_url, 547 order_id); 548 GNUNET_assert (NULL != puc->cpo); 549 GNUNET_free (order_id); 550 GNUNET_assert ( 551 GNUNET_OK == 552 TALER_MERCHANT_get_private_order_set_options ( 553 puc->cpo, 554 TALER_MERCHANT_get_private_order_option_timeout (timeout))); 555 GNUNET_assert ( 556 TALER_EC_NONE == 557 TALER_MERCHANT_get_private_order_start (puc->cpo, 558 &check_payment_cb, 559 puc)); 560 } 561 AH_trigger_curl (); 562 } 563 564 565 /** 566 * Helper function used to ask our backend to begin processing a 567 * payment for the user's account. May perform asynchronous 568 * operations by suspending the connection if required. 569 * 570 * @param puc context to begin payment for. 571 * @return MHD status code 572 */ 573 static enum MHD_Result 574 begin_payment (struct PolicyUploadContext *puc) 575 { 576 json_t *order; 577 578 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 579 "Suspending connection while creating order at `%s'\n", 580 AH_backend_url); 581 { 582 char *order_id; 583 struct TALER_Amount upload_fee; 584 585 if (0 > 586 TALER_amount_multiply (&upload_fee, 587 &AH_annual_fee, 588 puc->years_to_pay)) 589 { 590 GNUNET_break_op (0); 591 return TALER_MHD_reply_with_error (puc->con, 592 MHD_HTTP_BAD_REQUEST, 593 TALER_EC_GENERIC_PARAMETER_MALFORMED, 594 "storage_duration_years"); 595 } 596 597 order_id = GNUNET_STRINGS_data_to_string_alloc ( 598 &puc->payment_identifier, 599 sizeof(struct ANASTASIS_PaymentSecretP)); 600 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 601 "Creating order for %u years with payment of %s\n", 602 puc->years_to_pay, 603 TALER_amount2s (&upload_fee)); 604 order = json_pack ("{s:o, s:s, s:[{s:s,s:I,s:s}], s:s }", 605 "amount", TALER_JSON_from_amount (&upload_fee), 606 "summary", "Anastasis policy storage fee", 607 "products", 608 "description", "policy storage fee", 609 "quantity", (json_int_t) puc->years_to_pay, 610 "unit", "years", 611 "order_id", order_id); 612 GNUNET_free (order_id); 613 if (NULL == order) 614 { 615 GNUNET_break (0); 616 return TALER_MHD_reply_with_error (puc->con, 617 MHD_HTTP_INTERNAL_SERVER_ERROR, 618 TALER_EC_GENERIC_JSON_ALLOCATION_FAILURE, 619 "could not create order"); 620 } 621 } 622 /* Only link into the list once we are actually about to suspend: the list 623 exists to resume suspended connections at shutdown, and cleanup_ctx() 624 frees the context without unlinking it, so an error return above would 625 otherwise leave a dangling entry behind. */ 626 GNUNET_CONTAINER_DLL_insert (puc_head, 627 puc_tail, 628 puc); 629 MHD_suspend_connection (puc->con); 630 puc->po = TALER_MERCHANT_post_private_orders_create (AH_ctx, 631 AH_backend_url, 632 order); 633 GNUNET_assert (NULL != puc->po); 634 GNUNET_assert ( 635 GNUNET_OK == 636 TALER_MERCHANT_post_private_orders_set_options ( 637 puc->po, 638 TALER_MERCHANT_post_private_orders_option_create_token (false))); 639 GNUNET_assert (TALER_EC_NONE == 640 TALER_MERCHANT_post_private_orders_start (puc->po, 641 &proposal_cb, 642 puc)); 643 AH_trigger_curl (); 644 json_decref (order); 645 return MHD_YES; 646 } 647 648 649 /** 650 * Prepare to receive a payment, possibly requesting it, or just waiting 651 * for it to be completed by the client. 652 * 653 * @param puc context to prepare payment for 654 * @return MHD status 655 */ 656 static enum MHD_Result 657 prepare_payment (struct PolicyUploadContext *puc) 658 { 659 if (! puc->payment_identifier_provided) 660 { 661 GNUNET_CRYPTO_random_block ( 662 &puc->payment_identifier, 663 sizeof (struct ANASTASIS_PaymentSecretP)); 664 puc->payment_identifier_provided = true; 665 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 666 "No payment identifier, initiating payment\n"); 667 return begin_payment (puc); 668 } 669 await_payment (puc); 670 return MHD_YES; 671 } 672 673 674 enum MHD_Result 675 AH_handler_policy_post ( 676 struct MHD_Connection *connection, 677 struct TM_HandlerContext *hc, 678 const struct ANASTASIS_CRYPTO_AccountPublicKeyP *account_pub, 679 const char *recovery_data, 680 size_t *recovery_data_size) 681 { 682 struct PolicyUploadContext *puc = hc->ctx; 683 684 if (NULL == puc) 685 { 686 /* first call, setup internals */ 687 puc = GNUNET_new (struct PolicyUploadContext); 688 hc->ctx = puc; 689 hc->cc = &cleanup_ctx; 690 puc->con = connection; 691 692 TALER_MHD_parse_request_header_auto (connection, 693 ANASTASIS_HTTP_HEADER_PAYMENT_IDENTIFIER, 694 &puc->payment_identifier, 695 puc->payment_identifier_provided); 696 puc->account = *account_pub; 697 698 /* check for meta-data */ 699 { 700 const char *metas; 701 702 metas = MHD_lookup_connection_value (connection, 703 MHD_HEADER_KIND, 704 ANASTASIS_HTTP_HEADER_POLICY_META_DATA); 705 if (NULL == metas) 706 { 707 GNUNET_break_op (0); 708 return TALER_MHD_reply_with_error ( 709 connection, 710 MHD_HTTP_BAD_REQUEST, 711 TALER_EC_GENERIC_HTTP_HEADERS_MALFORMED, 712 ANASTASIS_HTTP_HEADER_POLICY_META_DATA 713 " header must be present"); 714 } 715 if (GNUNET_OK != 716 GNUNET_STRINGS_string_to_data_alloc (metas, 717 strlen (metas), 718 &puc->meta_data, 719 &puc->meta_data_size)) 720 { 721 GNUNET_break_op (0); 722 return TALER_MHD_reply_with_error ( 723 connection, 724 MHD_HTTP_BAD_REQUEST, 725 TALER_EC_GENERIC_HTTP_HEADERS_MALFORMED, 726 ANASTASIS_HTTP_HEADER_POLICY_META_DATA 727 " header must include a base32-encoded value"); 728 } 729 } 730 TALER_MHD_parse_request_header_auto_t (connection, 731 ANASTASIS_HTTP_HEADER_POLICY_SIGNATURE, 732 &puc->account_sig); 733 { 734 /* Check if header contains an ETAG */ 735 const char *etag; 736 737 etag = MHD_lookup_connection_value (connection, 738 MHD_HEADER_KIND, 739 MHD_HTTP_HEADER_IF_NONE_MATCH); 740 if ( (NULL == etag) || 741 (2 >= strlen (etag)) || 742 ('"' != etag[0]) || 743 ('"' != etag[strlen (etag) - 1]) || 744 (GNUNET_OK != 745 GNUNET_STRINGS_string_to_data (etag + 1, 746 strlen (etag) - 2, 747 &puc->new_policy_upload_hash, 748 sizeof (puc->new_policy_upload_hash)) 749 ) ) 750 { 751 GNUNET_break_op (0); 752 return TALER_MHD_reply_with_error (connection, 753 MHD_HTTP_BAD_REQUEST, 754 TALER_EC_ANASTASIS_POLICY_BAD_IF_MATCH, 755 MHD_HTTP_HEADER_IF_NONE_MATCH 756 " header must include a base32-encoded SHA-512 hash"); 757 } 758 } 759 /* validate signature */ 760 { 761 struct ANASTASIS_UploadSignaturePS usp = { 762 .purpose.size = htonl (sizeof (usp)), 763 .purpose.purpose = htonl (TALER_SIGNATURE_ANASTASIS_POLICY_UPLOAD), 764 .new_recovery_data_hash = puc->new_policy_upload_hash 765 }; 766 767 if (GNUNET_OK != 768 GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_ANASTASIS_POLICY_UPLOAD, 769 &usp, 770 &puc->account_sig.eddsa_sig, 771 &account_pub->pub)) 772 { 773 GNUNET_break_op (0); 774 return TALER_MHD_reply_with_error (connection, 775 MHD_HTTP_FORBIDDEN, 776 TALER_EC_ANASTASIS_POLICY_BAD_SIGNATURE, 777 ANASTASIS_HTTP_HEADER_POLICY_SIGNATURE); 778 } 779 } 780 781 /* Only now, with the account signature verified, do we allocate the 782 upload buffer: sizing it from the client-declared Content-Length before 783 checking who is asking lets anyone force repeated multi-megabyte 784 allocations. */ 785 /* now setup 'puc' */ 786 { 787 const char *lens; 788 unsigned long len; 789 char dummy; 790 791 lens = MHD_lookup_connection_value (connection, 792 MHD_HEADER_KIND, 793 MHD_HTTP_HEADER_CONTENT_LENGTH); 794 if ( (NULL == lens) || 795 (1 != sscanf (lens, 796 "%lu%c", 797 &len, 798 &dummy)) ) 799 { 800 GNUNET_break_op (0); 801 return TALER_MHD_reply_with_error ( 802 connection, 803 MHD_HTTP_BAD_REQUEST, 804 (NULL == lens) 805 ? TALER_EC_ANASTASIS_GENERIC_MISSING_CONTENT_LENGTH 806 : TALER_EC_ANASTASIS_GENERIC_MALFORMED_CONTENT_LENGTH, 807 NULL); 808 } 809 if (len / 1024 / 1024 >= AH_upload_limit_mb) 810 { 811 GNUNET_break_op (0); 812 return TALER_MHD_reply_with_error (connection, 813 MHD_HTTP_PAYLOAD_TOO_LARGE, 814 TALER_EC_SYNC_MALFORMED_CONTENT_LENGTH, 815 "Content-length value not acceptable"); 816 } 817 puc->upload = GNUNET_malloc_large (len); 818 if (NULL == puc->upload) 819 { 820 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, 821 "malloc"); 822 return TALER_MHD_reply_with_error (connection, 823 MHD_HTTP_PAYLOAD_TOO_LARGE, 824 TALER_EC_ANASTASIS_POLICY_OUT_OF_MEMORY_ON_CONTENT_LENGTH, 825 NULL); 826 } 827 puc->upload_size = (size_t) len; 828 } 829 830 puc->timeout = GNUNET_TIME_relative_to_absolute ( 831 CHECK_PAYMENT_GENERIC_TIMEOUT); 832 TALER_MHD_parse_request_timeout (connection, 833 &puc->timeout); 834 835 /* check if the client insists on paying */ 836 { 837 const char *req; 838 unsigned int years; 839 840 req = MHD_lookup_connection_value (connection, 841 MHD_GET_ARGUMENT_KIND, 842 "storage_duration"); 843 if (NULL != req) 844 { 845 char dummy; 846 847 if (1 != sscanf (req, 848 "%u%c", 849 &years, 850 &dummy)) 851 { 852 GNUNET_break_op (0); 853 return TALER_MHD_reply_with_error (connection, 854 MHD_HTTP_BAD_REQUEST, 855 TALER_EC_GENERIC_PARAMETER_MALFORMED, 856 "storage_duration (must be non-negative number)"); 857 } 858 if (years > ANASTASIS_MAX_YEARS_STORAGE) 859 { 860 GNUNET_break_op (0); 861 return TALER_MHD_reply_with_error ( 862 connection, 863 MHD_HTTP_BAD_REQUEST, 864 TALER_EC_GENERIC_PARAMETER_MALFORMED, 865 "storage_duration (exceeds maximum storage duration)"); 866 } 867 } 868 else 869 { 870 years = 1; 871 } 872 puc->end_date = GNUNET_TIME_relative_to_timestamp ( 873 GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_YEARS, 874 years)); 875 } 876 877 /* get ready to hash (done here as we may go async for payments next) */ 878 puc->hash_ctx = GNUNET_CRYPTO_hash_context_start (); 879 880 /* Check database to see if the transaction is permissible */ 881 { 882 struct GNUNET_TIME_Relative rem; 883 884 rem = GNUNET_TIME_absolute_get_remaining (puc->end_date.abs_time); 885 puc->years_to_pay = rem.rel_value_us 886 / GNUNET_TIME_UNIT_YEARS.rel_value_us; 887 if (0 != (rem.rel_value_us % GNUNET_TIME_UNIT_YEARS.rel_value_us)) 888 puc->years_to_pay++; 889 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 890 "Calculated years to pay to be %u until %s\n", 891 puc->years_to_pay, 892 GNUNET_TIME_absolute2s (puc->end_date.abs_time)); 893 894 if (puc->payment_identifier_provided) 895 { 896 /* check if payment identifier is valid (existing and paid) */ 897 bool paid = false; 898 bool valid_counter = false; 899 enum GNUNET_DB_QueryStatus qs; 900 901 qs = ANASTASIS_DB_get_recdoc_payment ( 902 &puc->payment_identifier, 903 &paid, 904 &valid_counter, 905 &puc->existing_pi_timestamp); 906 if (qs < 0) 907 return TALER_MHD_reply_with_error (puc->con, 908 MHD_HTTP_INTERNAL_SERVER_ERROR, 909 TALER_EC_GENERIC_DB_FETCH_FAILED, 910 NULL); 911 if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs) 912 { 913 /* The client named a payment identifier we have no record of. The 914 result specification was not touched in that case, so we must not 915 look at @e paid or @e valid_counter; treat it as unpaid. */ 916 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 917 "Given payment identifier is unknown, initiating payment\n"); 918 puc->payment_identifier_provided = false; 919 if (0 == puc->years_to_pay) 920 puc->years_to_pay = 1; 921 return prepare_payment (puc); 922 } 923 924 if ( (! paid) || 925 (! valid_counter) ) 926 { 927 if (! valid_counter) 928 { 929 puc->payment_identifier_provided = false; 930 if (0 == puc->years_to_pay) 931 puc->years_to_pay = 1; 932 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 933 "Too many uploads with this payment identifier, initiating fresh payment\n") 934 ; 935 } 936 else 937 { 938 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 939 "Given payment identifier not known to be paid, initiating payment\n"); 940 } 941 return prepare_payment (puc); 942 } 943 } 944 945 if (! puc->payment_identifier_provided) 946 { 947 enum GNUNET_DB_QueryStatus qs; 948 struct GNUNET_TIME_Relative rel; 949 950 /* generate fresh payment identifier */ 951 GNUNET_CRYPTO_random_block (&puc->payment_identifier, 952 sizeof (struct ANASTASIS_PaymentSecretP)); 953 if (! TALER_amount_is_zero (&AH_annual_fee)) 954 { 955 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 956 "No payment identifier, requesting payment\n"); 957 return begin_payment (puc); 958 } 959 /* Cost is zero, fake "zero" payment having happened */ 960 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 961 "Policy upload is free, allowing upload without payment\n"); 962 qs = ANASTASIS_DB_do_insert_recdoc_payment ( 963 account_pub, 964 AH_post_counter, 965 &puc->payment_identifier, 966 &AH_annual_fee); 967 if (qs <= 0) 968 return TALER_MHD_reply_with_error (puc->con, 969 MHD_HTTP_INTERNAL_SERVER_ERROR, 970 TALER_EC_GENERIC_DB_FETCH_FAILED, 971 NULL); 972 rel = GNUNET_TIME_relative_multiply ( 973 GNUNET_TIME_UNIT_YEARS, 974 ANASTASIS_MAX_YEARS_STORAGE); 975 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 976 "Policy lifetime is %s (%u years)\n", 977 GNUNET_TIME_relative2s (rel, 978 true), 979 ANASTASIS_MAX_YEARS_STORAGE); 980 qs = ANASTASIS_DB_do_update_account_lifetime ( 981 account_pub, 982 &puc->payment_identifier, 983 GNUNET_TIME_relative_to_timestamp (rel), 984 GNUNET_TIME_UNIT_ZERO, 985 &puc->paid_until); 986 if (qs <= 0) 987 { 988 GNUNET_break (0); 989 return TALER_MHD_reply_with_error (puc->con, 990 MHD_HTTP_INTERNAL_SERVER_ERROR, 991 TALER_EC_GENERIC_DB_FETCH_FAILED, 992 NULL); 993 } 994 } 995 } 996 997 /* Check if existing policy matches upload (and if, skip it) */ 998 { 999 struct GNUNET_HashCode hash; 1000 enum ANASTASIS_DB_AccountStatus as; 1001 uint32_t version; 1002 struct GNUNET_TIME_Timestamp now; 1003 struct GNUNET_TIME_Relative rem; 1004 1005 as = ANASTASIS_DB_get_account ( 1006 account_pub, 1007 &puc->paid_until, 1008 &hash, 1009 &version); 1010 now = GNUNET_TIME_timestamp_get (); 1011 if (GNUNET_TIME_timestamp_cmp (puc->paid_until, 1012 <, 1013 now)) 1014 puc->paid_until = now; 1015 rem = GNUNET_TIME_absolute_get_difference (puc->paid_until.abs_time, 1016 puc->end_date.abs_time); 1017 puc->years_to_pay = rem.rel_value_us 1018 / GNUNET_TIME_UNIT_YEARS.rel_value_us; 1019 if (0 != (rem.rel_value_us % GNUNET_TIME_UNIT_YEARS.rel_value_us)) 1020 puc->years_to_pay++; 1021 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1022 "Calculated years to pay to be %u until %s\n", 1023 puc->years_to_pay, 1024 GNUNET_TIME_absolute2s (puc->end_date.abs_time)); 1025 if ( (ANASTASIS_DB_ACCOUNT_STATUS_VALID_HASH_RETURNED == as) && 1026 (0 != puc->years_to_pay) ) 1027 { 1028 /* user requested extension, force payment */ 1029 as = ANASTASIS_DB_ACCOUNT_STATUS_PAYMENT_REQUIRED; 1030 } 1031 switch (as) 1032 { 1033 case ANASTASIS_DB_ACCOUNT_STATUS_PAYMENT_REQUIRED: 1034 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1035 "Expiration too low, initiating payment\n"); 1036 return prepare_payment (puc); 1037 case ANASTASIS_DB_ACCOUNT_STATUS_HARD_ERROR: 1038 return TALER_MHD_reply_with_error (puc->con, 1039 MHD_HTTP_INTERNAL_SERVER_ERROR, 1040 TALER_EC_GENERIC_DB_FETCH_FAILED, 1041 NULL); 1042 case ANASTASIS_DB_ACCOUNT_STATUS_NO_RESULTS: 1043 /* continue below */ 1044 break; 1045 case ANASTASIS_DB_ACCOUNT_STATUS_VALID_HASH_RETURNED: 1046 if (0 == GNUNET_memcmp (&hash, 1047 &puc->new_policy_upload_hash)) 1048 { 1049 /* Refuse upload: we already have that backup! */ 1050 struct MHD_Response *resp; 1051 enum MHD_Result ret; 1052 char version_s[14]; 1053 1054 GNUNET_snprintf (version_s, 1055 sizeof (version_s), 1056 "%u", 1057 (unsigned int) version); 1058 resp = MHD_create_response_from_buffer (0, 1059 NULL, 1060 MHD_RESPMEM_PERSISTENT); 1061 TALER_MHD_add_global_headers (resp, 1062 false); 1063 GNUNET_break (MHD_YES == 1064 MHD_add_response_header (resp, 1065 ANASTASIS_HTTP_HEADER_POLICY_VERSION, 1066 version_s)); 1067 ret = MHD_queue_response (connection, 1068 MHD_HTTP_NOT_MODIFIED, 1069 resp); 1070 GNUNET_break (MHD_YES == ret); 1071 MHD_destroy_response (resp); 1072 return ret; 1073 } 1074 break; 1075 } 1076 } 1077 /* ready to begin! */ 1078 return MHD_YES; 1079 } 1080 1081 if (NULL != puc->resp) 1082 { 1083 enum MHD_Result ret; 1084 1085 /* We generated a response asynchronously, queue that */ 1086 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1087 "Returning asynchronously generated response with HTTP status %u\n", 1088 puc->response_code); 1089 ret = MHD_queue_response (connection, 1090 puc->response_code, 1091 puc->resp); 1092 GNUNET_break (MHD_YES == ret); 1093 MHD_destroy_response (puc->resp); 1094 puc->resp = NULL; 1095 return ret; 1096 } 1097 1098 /* handle upload */ 1099 if (0 != *recovery_data_size) 1100 { 1101 /* check MHD invariant */ 1102 GNUNET_assert (puc->upload_off + *recovery_data_size <= puc->upload_size); 1103 memcpy (&puc->upload[puc->upload_off], 1104 recovery_data, 1105 *recovery_data_size); 1106 puc->upload_off += *recovery_data_size; 1107 GNUNET_CRYPTO_hash_context_read (puc->hash_ctx, 1108 recovery_data, 1109 *recovery_data_size); 1110 *recovery_data_size = 0; 1111 return MHD_YES; 1112 } 1113 1114 if ( (0 == puc->upload_off) && 1115 (0 != puc->upload_size) && 1116 (NULL == puc->resp) ) 1117 { 1118 /* wait for upload */ 1119 return MHD_YES; 1120 } 1121 1122 /* finished with upload, check hash */ 1123 if (NULL != puc->hash_ctx) 1124 { 1125 struct GNUNET_HashCode our_hash; 1126 1127 GNUNET_CRYPTO_hash_context_finish (puc->hash_ctx, 1128 &our_hash); 1129 puc->hash_ctx = NULL; 1130 if (0 != GNUNET_memcmp (&our_hash, 1131 &puc->new_policy_upload_hash)) 1132 { 1133 GNUNET_break_op (0); 1134 return TALER_MHD_reply_with_error (connection, 1135 MHD_HTTP_BAD_REQUEST, 1136 TALER_EC_ANASTASIS_POLICY_INVALID_UPLOAD, 1137 "Data uploaded does not match Etag promise"); 1138 } 1139 } 1140 1141 /* store backup to database */ 1142 { 1143 enum ANASTASIS_DB_StoreStatus ss; 1144 uint32_t version = UINT32_MAX; 1145 char version_s[14]; 1146 char expir_s[32]; 1147 1148 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1149 "Uploading recovery document\n"); 1150 ss = ANASTASIS_DB_do_insert_recovery_document ( 1151 &puc->account, 1152 &puc->account_sig, 1153 &puc->new_policy_upload_hash, 1154 puc->upload, 1155 puc->upload_size, 1156 puc->meta_data, 1157 puc->meta_data_size, 1158 &puc->payment_identifier, 1159 &version); 1160 GNUNET_snprintf (version_s, 1161 sizeof (version_s), 1162 "%u", 1163 (unsigned int) version); 1164 GNUNET_snprintf (expir_s, 1165 sizeof (expir_s), 1166 "%llu", 1167 (unsigned long long) 1168 (puc->paid_until.abs_time.abs_value_us 1169 / GNUNET_TIME_UNIT_SECONDS.rel_value_us)); 1170 switch (ss) 1171 { 1172 case ANASTASIS_DB_STORE_STATUS_STORE_LIMIT_EXCEEDED: 1173 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1174 "Storage request limit exceeded, requesting payment\n"); 1175 if (! puc->payment_identifier_provided) 1176 { 1177 GNUNET_CRYPTO_random_block (&puc->payment_identifier, 1178 sizeof (struct ANASTASIS_PaymentSecretP)); 1179 puc->payment_identifier_provided = true; 1180 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1181 "Also no payment identifier, requesting payment\n"); 1182 } 1183 return begin_payment (puc); 1184 case ANASTASIS_DB_STORE_STATUS_PAYMENT_REQUIRED: 1185 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1186 "Policy store operation requires payment\n"); 1187 if (! puc->payment_identifier_provided) 1188 { 1189 GNUNET_CRYPTO_random_block (&puc->payment_identifier, 1190 sizeof (struct ANASTASIS_PaymentSecretP)); 1191 puc->payment_identifier_provided = true; 1192 } 1193 return begin_payment (puc); 1194 case ANASTASIS_DB_STORE_STATUS_HARD_ERROR: 1195 case ANASTASIS_DB_STORE_STATUS_SOFT_ERROR: 1196 return TALER_MHD_reply_with_error (puc->con, 1197 MHD_HTTP_INTERNAL_SERVER_ERROR, 1198 TALER_EC_GENERIC_DB_FETCH_FAILED, 1199 NULL); 1200 case ANASTASIS_DB_STORE_STATUS_NO_RESULTS: 1201 { 1202 /* database says nothing actually changed, 304 (could 1203 theoretically happen if another equivalent upload succeeded 1204 since we last checked!) */ 1205 struct MHD_Response *resp; 1206 enum MHD_Result ret; 1207 1208 resp = MHD_create_response_from_buffer (0, 1209 NULL, 1210 MHD_RESPMEM_PERSISTENT); 1211 TALER_MHD_add_global_headers (resp, 1212 false); 1213 GNUNET_break (MHD_YES == 1214 MHD_add_response_header (resp, 1215 "Anastasis-Version", 1216 version_s)); 1217 ret = MHD_queue_response (connection, 1218 MHD_HTTP_NOT_MODIFIED, 1219 resp); 1220 GNUNET_break (MHD_YES == ret); 1221 MHD_destroy_response (resp); 1222 return ret; 1223 } 1224 case ANASTASIS_DB_STORE_STATUS_SUCCESS: 1225 /* generate main (204) standard success reply */ 1226 { 1227 struct MHD_Response *resp; 1228 enum MHD_Result ret; 1229 1230 resp = MHD_create_response_from_buffer (0, 1231 NULL, 1232 MHD_RESPMEM_PERSISTENT); 1233 TALER_MHD_add_global_headers (resp, 1234 false); 1235 GNUNET_break (MHD_YES == 1236 MHD_add_response_header (resp, 1237 ANASTASIS_HTTP_HEADER_POLICY_VERSION, 1238 version_s)); 1239 GNUNET_break (MHD_YES == 1240 MHD_add_response_header (resp, 1241 ANASTASIS_HTTP_HEADER_POLICY_EXPIRATION, 1242 expir_s)); 1243 ret = MHD_queue_response (connection, 1244 MHD_HTTP_NO_CONTENT, 1245 resp); 1246 GNUNET_break (MHD_YES == ret); 1247 MHD_destroy_response (resp); 1248 return ret; 1249 } 1250 } 1251 } 1252 GNUNET_break (0); 1253 return MHD_NO; 1254 }