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