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