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