anastasis-httpd_truth-solve.c (48668B)
1 /* 2 This file is part of Anastasis 3 Copyright (C) 2019-2022 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_truth-solve.c 18 * @brief functions to handle incoming requests on /truth/$TID/solve 19 * @author Dennis Neufeld 20 * @author Dominik Meister 21 * @author Christian Grothoff 22 */ 23 #include "platform.h" 24 struct SolveContext; 25 #define TALER_MERCHANT_POST_PRIVATE_ORDERS_RESULT_CLOSURE struct SolveContext 26 #define TALER_MERCHANT_GET_PRIVATE_ORDER_RESULT_CLOSURE struct SolveContext 27 #include "anastasis-httpd.h" 28 #include "anastasis_service.h" 29 #include "anastasis-httpd_truth.h" 30 #include <gnunet/gnunet_util_lib.h> 31 #include <gnunet/gnunet_rest_lib.h> 32 #include "anastasis_authorization_lib.h" 33 #include <taler/taler_merchant_service.h> 34 #include <taler/taler_json_lib.h> 35 #include <taler/taler_mhd_lib.h> 36 #include <taler/merchant/post-private-orders.h> 37 #include <taler/merchant/get-private-orders-ORDER_ID.h> 38 39 /** 40 * What is the maximum frequency at which we allow 41 * clients to attempt to answer security questions? 42 */ 43 #define MAX_QUESTION_FREQ GNUNET_TIME_relative_multiply ( \ 44 GNUNET_TIME_UNIT_SECONDS, 30) 45 46 /** 47 * How long should the wallet check for auto-refunds before giving up? 48 */ 49 #define AUTO_REFUND_TIMEOUT GNUNET_TIME_relative_multiply ( \ 50 GNUNET_TIME_UNIT_MINUTES, 2) 51 52 53 /** 54 * How many retries do we allow per code? 55 */ 56 #define INITIAL_RETRY_COUNTER 3 57 58 59 struct SolveContext 60 { 61 62 /** 63 * Payment Identifier 64 */ 65 struct ANASTASIS_PaymentSecretP payment_identifier; 66 67 /** 68 * Public key of the challenge which is solved. 69 */ 70 struct ANASTASIS_CRYPTO_TruthUUIDP truth_uuid; 71 72 /** 73 * Key to decrypt the truth. 74 */ 75 struct ANASTASIS_CRYPTO_TruthKeyP truth_key; 76 77 /** 78 * Cost for paying the challenge, one entry per currency the wallet may 79 * settle in. Borrowed from the authorization plugin or from 80 * #AH_question_costs, both of which outlive the request. 81 */ 82 const struct TALER_AmountList *challenge_costs; 83 84 /** 85 * What the wallet actually paid, in the currency it chose. Only 86 * meaningful if @e have_payment is true. 87 */ 88 struct TALER_Amount challenge_paid; 89 90 /** 91 * Our handler context. 92 */ 93 struct TM_HandlerContext *hc; 94 95 /** 96 * Opaque parsing context. 97 */ 98 void *opaque_post_parsing_context; 99 100 /** 101 * Uploaded JSON data, NULL if upload is not yet complete. 102 */ 103 json_t *root; 104 105 /** 106 * Kept in DLL for shutdown handling while suspended. 107 */ 108 struct SolveContext *next; 109 110 /** 111 * Kept in DLL for shutdown handling while suspended. 112 */ 113 struct SolveContext *prev; 114 115 /** 116 * Connection handle for closing or resuming 117 */ 118 struct MHD_Connection *connection; 119 120 /** 121 * Reference to the authorization plugin which was loaded 122 */ 123 struct ANASTASIS_AuthorizationPlugin *authorization; 124 125 /** 126 * Status of the authorization 127 */ 128 struct ANASTASIS_AUTHORIZATION_State *as; 129 130 /** 131 * Used while we are awaiting proposal creation. 132 */ 133 struct TALER_MERCHANT_PostPrivateOrdersHandle *po; 134 135 /** 136 * Used while we are waiting payment. 137 */ 138 struct TALER_MERCHANT_GetPrivateOrderHandle *cpo; 139 140 /** 141 * HTTP response code to use on resume, if non-NULL. 142 */ 143 struct MHD_Response *resp; 144 145 /** 146 * Our entry in the #to_heap, or NULL. 147 */ 148 struct GNUNET_CONTAINER_HeapNode *hn; 149 150 /** 151 * Challenge response we got from the request. 152 */ 153 struct GNUNET_HashCode challenge_response; 154 155 /** 156 * How long do we wait at most for payment or 157 * authorization? 158 */ 159 struct GNUNET_TIME_Absolute timeout; 160 161 /** 162 * Random authorization code we are using. 163 */ 164 uint64_t code; 165 166 /** 167 * HTTP response code to use on resume, if resp is set. 168 */ 169 unsigned int response_code; 170 171 /** 172 * true if client did not provide a payment secret / order ID. 173 */ 174 bool no_payment_identifier_provided; 175 176 /** 177 * True if @e challenge_paid holds what the wallet paid for this 178 * challenge, as read back from the database. 179 */ 180 bool have_payment; 181 182 /** 183 * True if this entry is in the #gc_head DLL. 184 */ 185 bool in_list; 186 187 /** 188 * True if this entry is currently suspended. 189 */ 190 bool suspended; 191 192 }; 193 194 195 /** 196 * Head of linked list over all authorization processes 197 */ 198 static struct SolveContext *gc_head; 199 200 /** 201 * Tail of linked list over all authorization processes 202 */ 203 static struct SolveContext *gc_tail; 204 205 /** 206 * Task running #do_timeout(). 207 */ 208 static struct GNUNET_SCHEDULER_Task *to_task; 209 210 211 /** 212 * Generate a response telling the client that answering this 213 * challenge failed because the rate limit has been exceeded. 214 * 215 * @param gc request to answer for 216 * @return MHD status code 217 */ 218 static enum MHD_Result 219 reply_rate_limited (const struct SolveContext *gc) 220 { 221 if (NULL != gc->authorization) 222 return TALER_MHD_REPLY_JSON_PACK ( 223 gc->connection, 224 MHD_HTTP_TOO_MANY_REQUESTS, 225 TALER_MHD_PACK_EC (TALER_EC_ANASTASIS_TRUTH_RATE_LIMITED), 226 GNUNET_JSON_pack_uint64 ("request_limit", 227 gc->authorization->retry_counter), 228 GNUNET_JSON_pack_time_rel ("request_frequency", 229 gc->authorization->code_rotation_period)); 230 /* must be security question */ 231 return TALER_MHD_REPLY_JSON_PACK ( 232 gc->connection, 233 MHD_HTTP_TOO_MANY_REQUESTS, 234 TALER_MHD_PACK_EC (TALER_EC_ANASTASIS_TRUTH_RATE_LIMITED), 235 GNUNET_JSON_pack_uint64 ("request_limit", 236 INITIAL_RETRY_COUNTER), 237 GNUNET_JSON_pack_time_rel ("request_frequency", 238 MAX_QUESTION_FREQ)); 239 } 240 241 242 /** 243 * Timeout requests that are past their due date. 244 * 245 * @param cls NULL 246 */ 247 static void 248 do_timeout (void *cls) 249 { 250 struct SolveContext *gc; 251 252 (void) cls; 253 to_task = NULL; 254 while (NULL != 255 (gc = GNUNET_CONTAINER_heap_peek (AH_to_heap))) 256 { 257 if (GNUNET_TIME_absolute_is_future (gc->timeout)) 258 break; 259 if (gc->suspended) 260 { 261 /* Test needed as we may have a "concurrent" 262 wakeup from another task that did not clear 263 this entry from the heap before the 264 response process concluded. */ 265 gc->suspended = false; 266 MHD_resume_connection (gc->connection); 267 } 268 GNUNET_assert (NULL != gc->hn); 269 gc->hn = NULL; 270 GNUNET_assert (gc == 271 GNUNET_CONTAINER_heap_remove_root (AH_to_heap)); 272 } 273 if (NULL == gc) 274 return; 275 to_task = GNUNET_SCHEDULER_add_at (gc->timeout, 276 &do_timeout, 277 NULL); 278 } 279 280 281 void 282 AH_truth_solve_shutdown (void) 283 { 284 struct SolveContext *gc; 285 286 while (NULL != (gc = gc_head)) 287 { 288 GNUNET_CONTAINER_DLL_remove (gc_head, 289 gc_tail, 290 gc); 291 gc->in_list = false; 292 if (NULL != gc->cpo) 293 { 294 TALER_MERCHANT_get_private_order_cancel (gc->cpo); 295 gc->cpo = NULL; 296 } 297 if (NULL != gc->po) 298 { 299 TALER_MERCHANT_post_private_orders_cancel (gc->po); 300 gc->po = NULL; 301 } 302 if (gc->suspended) 303 { 304 gc->suspended = false; 305 MHD_resume_connection (gc->connection); 306 } 307 if (NULL != gc->as) 308 { 309 gc->authorization->cleanup (gc->as); 310 gc->as = NULL; 311 gc->authorization = NULL; 312 } 313 } 314 ANASTASIS_authorization_plugin_shutdown (); 315 if (NULL != to_task) 316 { 317 GNUNET_SCHEDULER_cancel (to_task); 318 to_task = NULL; 319 } 320 } 321 322 323 /** 324 * Callback used to notify the application about completed requests. 325 * Cleans up the requests data structures. 326 * 327 * @param[in,out] hc 328 */ 329 static void 330 request_done (struct TM_HandlerContext *hc) 331 { 332 struct SolveContext *gc = hc->ctx; 333 334 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 335 "Request completed\n"); 336 if (NULL == gc) 337 return; 338 hc->cc = NULL; 339 GNUNET_assert (! gc->suspended); 340 if (gc->in_list) 341 { 342 GNUNET_CONTAINER_DLL_remove (gc_head, 343 gc_tail, 344 gc); 345 gc->in_list = false; 346 } 347 if (NULL != gc->hn) 348 { 349 GNUNET_assert (gc == 350 GNUNET_CONTAINER_heap_remove_node (gc->hn)); 351 gc->hn = NULL; 352 } 353 if (NULL != gc->as) 354 { 355 gc->authorization->cleanup (gc->as); 356 gc->authorization = NULL; 357 gc->as = NULL; 358 } 359 if (NULL != gc->cpo) 360 { 361 TALER_MERCHANT_get_private_order_cancel (gc->cpo); 362 gc->cpo = NULL; 363 } 364 if (NULL != gc->po) 365 { 366 TALER_MERCHANT_post_private_orders_cancel (gc->po); 367 gc->po = NULL; 368 } 369 if (NULL != gc->root) 370 { 371 json_decref (gc->root); 372 gc->root = NULL; 373 } 374 TALER_MHD_parse_post_cleanup_callback (gc->opaque_post_parsing_context); 375 GNUNET_free (gc); 376 hc->ctx = NULL; 377 } 378 379 380 /** 381 * Transmit a payment request for @a order_id on @a connection 382 * 383 * @param gc context to make payment request for 384 */ 385 static void 386 make_payment_request (struct SolveContext *gc) 387 { 388 struct MHD_Response *resp; 389 390 resp = MHD_create_response_from_buffer (0, 391 NULL, 392 MHD_RESPMEM_PERSISTENT); 393 GNUNET_assert (NULL != resp); 394 TALER_MHD_add_global_headers (resp, 395 false); 396 { 397 char *hdr; 398 char *order_id; 399 const char *pfx; 400 const char *hn; 401 402 if (0 == strncasecmp ("https://", 403 AH_backend_url, 404 strlen ("https://"))) 405 { 406 pfx = "taler://"; 407 hn = &AH_backend_url[strlen ("https://")]; 408 } 409 else if (0 == strncasecmp ("http://", 410 AH_backend_url, 411 strlen ("http://"))) 412 { 413 pfx = "taler+http://"; 414 hn = &AH_backend_url[strlen ("http://")]; 415 } 416 else 417 { 418 /* This invariant holds as per check in anastasis-httpd.c */ 419 GNUNET_assert (0); 420 } 421 /* This invariant holds as per check in anastasis-httpd.c */ 422 GNUNET_assert (0 != strlen (hn)); 423 424 order_id = GNUNET_STRINGS_data_to_string_alloc ( 425 &gc->payment_identifier, 426 sizeof (gc->payment_identifier)); 427 GNUNET_asprintf (&hdr, 428 "%spay/%s%s/", 429 pfx, 430 hn, 431 order_id); 432 GNUNET_free (order_id); 433 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 434 "Sending payment request `%s'\n", 435 hdr); 436 GNUNET_break (MHD_YES == 437 MHD_add_response_header (resp, 438 ANASTASIS_HTTP_HEADER_TALER, 439 hdr)); 440 GNUNET_free (hdr); 441 } 442 gc->resp = resp; 443 gc->response_code = MHD_HTTP_PAYMENT_REQUIRED; 444 } 445 446 447 /** 448 * Callbacks of this type are used to serve the result of submitting a 449 * /contract request to a merchant. 450 * 451 * @param cls our `struct SolveContext` 452 * @param por response details 453 */ 454 static void 455 proposal_cb (struct SolveContext *gc, 456 const struct TALER_MERCHANT_PostPrivateOrdersResponse *por) 457 { 458 enum GNUNET_DB_QueryStatus qs; 459 460 gc->po = NULL; 461 GNUNET_assert (gc->in_list); 462 GNUNET_CONTAINER_DLL_remove (gc_head, 463 gc_tail, 464 gc); 465 gc->in_list = false; 466 GNUNET_assert (gc->suspended); 467 gc->suspended = false; 468 MHD_resume_connection (gc->connection); 469 AH_trigger_daemon (NULL); 470 if (MHD_HTTP_OK != por->hr.http_status) 471 { 472 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 473 "Backend returned status %u/%d\n", 474 por->hr.http_status, 475 (int) por->hr.ec); 476 GNUNET_break (0); 477 gc->resp = TALER_MHD_MAKE_JSON_PACK ( 478 GNUNET_JSON_pack_uint64 ("code", 479 TALER_EC_ANASTASIS_TRUTH_PAYMENT_CREATE_BACKEND_ERROR), 480 GNUNET_JSON_pack_string ("hint", 481 "Failed to setup order with merchant backend"), 482 GNUNET_JSON_pack_uint64 ("backend-ec", 483 por->hr.ec), 484 GNUNET_JSON_pack_uint64 ("backend-http-status", 485 por->hr.http_status), 486 GNUNET_JSON_pack_allow_null ( 487 GNUNET_JSON_pack_object_steal ("backend-reply", 488 (json_t *) por->hr.reply))); 489 gc->response_code = MHD_HTTP_BAD_GATEWAY; 490 return; 491 } 492 /* Records the asking price in the primary currency; which currency the 493 wallet settles in is only known once it has paid, and is written by 494 ANASTASIS_DB_update_to_challenge_payment_paid() then. */ 495 qs = ANASTASIS_DB_insert_challenge_payment ( 496 &gc->truth_uuid, 497 &gc->payment_identifier, 498 AH_primary_price (gc->challenge_costs)); 499 if (0 >= qs) 500 { 501 GNUNET_break (0); 502 gc->resp = TALER_MHD_make_error (TALER_EC_GENERIC_DB_STORE_FAILED, 503 "insert challenge payment"); 504 gc->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR; 505 return; 506 } 507 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 508 "Setup fresh order, creating payment request\n"); 509 make_payment_request (gc); 510 } 511 512 513 /** 514 * Callback to process a GET /check-payment request 515 * 516 * @param cls our `struct SolveContext` 517 * @param osr order status 518 */ 519 static void 520 check_payment_cb (struct SolveContext *gc, 521 const struct TALER_MERCHANT_GetPrivateOrderResponse *osr) 522 523 { 524 const struct TALER_MERCHANT_HttpResponse *hr = &osr->hr; 525 526 gc->cpo = NULL; 527 GNUNET_assert (gc->in_list); 528 GNUNET_CONTAINER_DLL_remove (gc_head, 529 gc_tail, 530 gc); 531 gc->in_list = false; 532 GNUNET_assert (gc->suspended); 533 gc->suspended = false; 534 MHD_resume_connection (gc->connection); 535 AH_trigger_daemon (NULL); 536 537 switch (hr->http_status) 538 { 539 case MHD_HTTP_OK: 540 GNUNET_assert (NULL != osr); 541 break; 542 case MHD_HTTP_NOT_FOUND: 543 /* We created this order before, how can it be not found now? */ 544 GNUNET_break (0); 545 gc->resp = TALER_MHD_make_error (TALER_EC_ANASTASIS_TRUTH_ORDER_DISAPPEARED, 546 NULL); 547 gc->response_code = MHD_HTTP_BAD_GATEWAY; 548 return; 549 case MHD_HTTP_BAD_GATEWAY: 550 gc->resp = TALER_MHD_make_error ( 551 TALER_EC_ANASTASIS_TRUTH_BACKEND_EXCHANGE_BAD, 552 NULL); 553 gc->response_code = MHD_HTTP_BAD_GATEWAY; 554 return; 555 case MHD_HTTP_GATEWAY_TIMEOUT: 556 gc->resp = TALER_MHD_make_error (TALER_EC_ANASTASIS_GENERIC_BACKEND_TIMEOUT, 557 "Timeout check payment status"); 558 GNUNET_assert (NULL != gc->resp); 559 gc->response_code = MHD_HTTP_GATEWAY_TIMEOUT; 560 return; 561 default: 562 { 563 char status[14]; 564 565 GNUNET_snprintf (status, 566 sizeof (status), 567 "%u", 568 hr->http_status); 569 gc->resp = TALER_MHD_make_error ( 570 TALER_EC_ANASTASIS_TRUTH_UNEXPECTED_PAYMENT_STATUS, 571 status); 572 GNUNET_assert (NULL != gc->resp); 573 gc->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR; 574 return; 575 } 576 } 577 578 GNUNET_assert (MHD_HTTP_OK == hr->http_status); 579 switch (osr->details.ok.status) 580 { 581 case TALER_MERCHANT_OSC_PAID: 582 { 583 enum GNUNET_DB_QueryStatus qs; 584 585 if (GNUNET_OK != 586 AH_paid_amount (osr, 587 &gc->challenge_paid)) 588 { 589 GNUNET_break (0); 590 gc->resp = TALER_MHD_make_error ( 591 TALER_EC_MERCHANT_GENERIC_DB_CONTRACT_CONTENT_INVALID, 592 "no amount given"); 593 gc->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR; 594 return; 595 } 596 gc->have_payment = true; 597 qs = ANASTASIS_DB_update_to_challenge_payment_paid ( 598 &gc->truth_uuid, 599 &gc->payment_identifier, 600 &gc->challenge_paid); 601 if (0 <= qs) 602 { 603 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 604 "Order has been paid, continuing with request processing\n") 605 ; 606 return; /* continue as planned */ 607 } 608 GNUNET_break (0); 609 gc->resp = TALER_MHD_make_error (TALER_EC_GENERIC_DB_STORE_FAILED, 610 "update to challenge payment paid"); 611 gc->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR; 612 return; /* continue as planned */ 613 } 614 case TALER_MERCHANT_OSC_CLAIMED: 615 case TALER_MERCHANT_OSC_UNPAID: 616 /* repeat payment request */ 617 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 618 "Order remains unpaid, sending payment request again\n"); 619 make_payment_request (gc); 620 return; 621 } 622 /* should never get here */ 623 GNUNET_break (0); 624 } 625 626 627 /** 628 * Helper function used to ask our backend to begin processing a 629 * payment for the user's account. May perform asynchronous 630 * operations by suspending the connection if required. 631 * 632 * @param gc context to begin payment for. 633 * @return MHD status code 634 */ 635 static enum MHD_Result 636 begin_payment (struct SolveContext *gc) 637 { 638 enum GNUNET_DB_QueryStatus qs; 639 char *order_id; 640 641 qs = ANASTASIS_DB_get_pending_challenge_payment ( 642 &gc->truth_uuid, 643 &gc->payment_identifier); 644 if (qs < 0) 645 { 646 GNUNET_break (0); 647 return TALER_MHD_reply_with_error (gc->connection, 648 MHD_HTTP_INTERNAL_SERVER_ERROR, 649 TALER_EC_GENERIC_DB_FETCH_FAILED, 650 "get pending challenge payment"); 651 } 652 GNUNET_assert (! gc->in_list); 653 gc->in_list = true; 654 GNUNET_CONTAINER_DLL_insert (gc_head, 655 gc_tail, 656 gc); 657 GNUNET_assert (! gc->suspended); 658 gc->suspended = true; 659 MHD_suspend_connection (gc->connection); 660 if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs) 661 { 662 /* We already created the order, check if it was paid */ 663 struct GNUNET_TIME_Relative timeout; 664 665 order_id = GNUNET_STRINGS_data_to_string_alloc ( 666 &gc->payment_identifier, 667 sizeof (gc->payment_identifier)); 668 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 669 "Order exists, checking payment status for order `%s'\n", 670 order_id); 671 timeout = GNUNET_TIME_absolute_get_remaining (gc->timeout); 672 gc->cpo = TALER_MERCHANT_get_private_order_create (AH_ctx, 673 AH_backend_url, 674 order_id); 675 GNUNET_assert (NULL != gc->cpo); 676 GNUNET_assert ( 677 GNUNET_OK == 678 TALER_MERCHANT_get_private_order_set_options ( 679 gc->cpo, 680 TALER_MERCHANT_get_private_order_option_timeout (timeout))); 681 GNUNET_assert (TALER_EC_NONE == 682 TALER_MERCHANT_get_private_order_start (gc->cpo, 683 &check_payment_cb, 684 gc)); 685 } 686 else 687 { 688 /* Create a fresh order */ 689 struct GNUNET_TIME_Timestamp pay_deadline; 690 691 GNUNET_CRYPTO_random_block (&gc->payment_identifier, 692 sizeof (struct ANASTASIS_PaymentSecretP)); 693 order_id = GNUNET_STRINGS_data_to_string_alloc ( 694 &gc->payment_identifier, 695 sizeof (gc->payment_identifier)); 696 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 697 "Creating fresh order `%s'\n", 698 order_id); 699 pay_deadline = GNUNET_TIME_relative_to_timestamp ( 700 ANASTASIS_CHALLENGE_OFFER_LIFETIME); 701 { 702 json_t *order; 703 704 order = AH_make_order (order_id, 705 "challenge fee for anastasis service", 706 gc->challenge_costs); 707 GNUNET_assert (0 == 708 json_object_update_new ( 709 order, 710 GNUNET_JSON_PACK ( 711 GNUNET_JSON_pack_time_rel ("auto_refund", 712 AUTO_REFUND_TIMEOUT), 713 GNUNET_JSON_pack_timestamp ("pay_deadline", 714 pay_deadline)))); 715 gc->po = TALER_MERCHANT_post_private_orders_create (AH_ctx, 716 AH_backend_url, 717 order); 718 json_decref (order); 719 } 720 GNUNET_assert ( 721 GNUNET_OK == 722 TALER_MERCHANT_post_private_orders_set_options ( 723 gc->po, 724 TALER_MERCHANT_post_private_orders_option_create_token (false), 725 TALER_MERCHANT_post_private_orders_option_refund_delay ( 726 AUTO_REFUND_TIMEOUT))); 727 GNUNET_assert (TALER_EC_NONE == 728 TALER_MERCHANT_post_private_orders_start (gc->po, 729 &proposal_cb, 730 gc)); 731 } 732 GNUNET_free (order_id); 733 AH_trigger_curl (); 734 return MHD_YES; 735 } 736 737 738 /** 739 * Load encrypted keyshare from db and return it to the client. 740 * 741 * @param truth_uuid UUID to the truth for the looup 742 * @param connection the connection to respond upon 743 * @return MHD status code 744 */ 745 static enum MHD_Result 746 return_key_share ( 747 const struct ANASTASIS_CRYPTO_TruthUUIDP *truth_uuid, 748 struct MHD_Connection *connection) 749 { 750 struct ANASTASIS_CRYPTO_EncryptedKeyShareP encrypted_keyshare; 751 752 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 753 "Returning key share of %s\n", 754 TALER_B2S (truth_uuid)); 755 { 756 enum GNUNET_DB_QueryStatus qs; 757 758 qs = ANASTASIS_DB_get_truth_key_share ( 759 truth_uuid, 760 &encrypted_keyshare); 761 switch (qs) 762 { 763 case GNUNET_DB_STATUS_HARD_ERROR: 764 case GNUNET_DB_STATUS_SOFT_ERROR: 765 GNUNET_break (0); 766 return TALER_MHD_reply_with_error (connection, 767 MHD_HTTP_INTERNAL_SERVER_ERROR, 768 TALER_EC_GENERIC_DB_FETCH_FAILED, 769 "get key share"); 770 case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: 771 /* this should be "impossible", after all the 772 client was able to solve the challenge! 773 (Exception: we deleted the truth via GC 774 just while the client was trying to recover. 775 Alas, highly unlikely...) */ 776 GNUNET_break (0); 777 return TALER_MHD_reply_with_error (connection, 778 MHD_HTTP_NOT_FOUND, 779 TALER_EC_ANASTASIS_TRUTH_KEY_SHARE_GONE, 780 NULL); 781 case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: 782 break; 783 } 784 } 785 786 { 787 struct MHD_Response *resp; 788 enum MHD_Result ret; 789 790 resp = MHD_create_response_from_buffer (sizeof (encrypted_keyshare), 791 &encrypted_keyshare, 792 MHD_RESPMEM_MUST_COPY); 793 TALER_MHD_add_global_headers (resp, 794 false); 795 ret = MHD_queue_response (connection, 796 MHD_HTTP_OK, 797 resp); 798 MHD_destroy_response (resp); 799 return ret; 800 } 801 } 802 803 804 /** 805 * Mark @a gc as suspended and update the respective 806 * data structures and jobs. 807 * 808 * @param[in,out] gc context of the suspended operation 809 */ 810 static void 811 gc_suspended (struct SolveContext *gc) 812 { 813 GNUNET_assert (NULL == gc->hn); 814 GNUNET_assert (! gc->suspended); 815 gc->suspended = true; 816 if (NULL == AH_to_heap) 817 AH_to_heap = GNUNET_CONTAINER_heap_create ( 818 GNUNET_CONTAINER_HEAP_ORDER_MIN); 819 gc->hn = GNUNET_CONTAINER_heap_insert (AH_to_heap, 820 gc, 821 gc->timeout.abs_value_us); 822 if (NULL != to_task) 823 { 824 GNUNET_SCHEDULER_cancel (to_task); 825 to_task = NULL; 826 } 827 { 828 struct SolveContext *rn; 829 830 rn = GNUNET_CONTAINER_heap_peek (AH_to_heap); 831 to_task = GNUNET_SCHEDULER_add_at (rn->timeout, 832 &do_timeout, 833 NULL); 834 } 835 } 836 837 838 /** 839 * Run the authorization method-specific 'process' function and continue 840 * based on its result with generating an HTTP response. 841 * 842 * @param connection the connection we are handling 843 * @param gc our overall handler context 844 */ 845 static enum MHD_Result 846 run_authorization_process (struct MHD_Connection *connection, 847 struct SolveContext *gc) 848 { 849 enum ANASTASIS_AUTHORIZATION_SolveResult ret; 850 851 GNUNET_assert (! gc->suspended); 852 if (NULL == gc->authorization->solve) 853 { 854 GNUNET_break (0); 855 return TALER_MHD_reply_with_error (gc->connection, 856 MHD_HTTP_INTERNAL_SERVER_ERROR, 857 TALER_EC_ANASTASIS_TRUTH_AUTHORIZATION_START_FAILED, 858 "solve method not implemented for authorization method"); 859 } 860 ret = gc->authorization->solve (gc->as, 861 gc->timeout, 862 &gc->challenge_response, 863 connection); 864 switch (ret) 865 { 866 case ANASTASIS_AUTHORIZATION_SRES_SUSPENDED: 867 /* connection was suspended */ 868 gc_suspended (gc); 869 return MHD_YES; 870 case ANASTASIS_AUTHORIZATION_SRES_FAILED: 871 gc->authorization->cleanup (gc->as); 872 gc->as = NULL; 873 return MHD_YES; 874 case ANASTASIS_AUTHORIZATION_SRES_FAILED_REPLY_FAILED: 875 gc->authorization->cleanup (gc->as); 876 gc->as = NULL; 877 return MHD_NO; 878 case ANASTASIS_AUTHORIZATION_SRES_FINISHED: 879 GNUNET_assert (! gc->suspended); 880 gc->authorization->cleanup (gc->as); 881 gc->as = NULL; 882 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 883 "Resuming with authorization successful!\n"); 884 if (gc->in_list) 885 { 886 GNUNET_CONTAINER_DLL_remove (gc_head, 887 gc_tail, 888 gc); 889 gc->in_list = false; 890 } 891 return MHD_YES; 892 } 893 GNUNET_break (0); 894 return MHD_NO; 895 } 896 897 898 /** 899 * Use the database to rate-limit queries to the authentication 900 * procedure, but without actually storing 'real' challenge codes. 901 * 902 * @param[in,out] gc context to rate limit requests for 903 * @return #GNUNET_OK if rate-limiting passes, 904 * #GNUNET_NO if a reply was sent (rate limited) 905 * #GNUNET_SYSERR if we failed and no reply 906 * was queued 907 */ 908 static enum GNUNET_GenericReturnValue 909 rate_limit (struct SolveContext *gc) 910 { 911 enum GNUNET_DB_QueryStatus qs; 912 struct GNUNET_TIME_Timestamp rt; 913 uint64_t code; 914 enum ANASTASIS_DB_CodeStatus cs; 915 struct GNUNET_HashCode hc; 916 bool satisfied; 917 uint64_t dummy; 918 919 rt = GNUNET_TIME_UNIT_FOREVER_TS; 920 qs = ANASTASIS_DB_insert_challenge_code ( 921 &gc->truth_uuid, 922 MAX_QUESTION_FREQ, 923 GNUNET_TIME_UNIT_HOURS, 924 INITIAL_RETRY_COUNTER, 925 &rt, 926 &code); 927 if (0 > qs) 928 { 929 GNUNET_break (0 < qs); 930 return (MHD_YES == 931 TALER_MHD_reply_with_error (gc->connection, 932 MHD_HTTP_INTERNAL_SERVER_ERROR, 933 TALER_EC_GENERIC_DB_FETCH_FAILED, 934 "insert_challenge_code (for rate limiting)")) 935 ? GNUNET_NO 936 : GNUNET_SYSERR; 937 } 938 if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs) 939 { 940 return (MHD_YES == 941 reply_rate_limited (gc)) 942 ? GNUNET_NO 943 : GNUNET_SYSERR; 944 } 945 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 946 "Using intentionally wrong answer to produce rate-limiting\n"); 947 /* decrement trial counter */ 948 ANASTASIS_hash_answer (code + 1, /* always use wrong answer */ 949 &hc); 950 cs = ANASTASIS_DB_do_verify_challenge_code ( 951 &gc->truth_uuid, 952 &hc, 953 &dummy, 954 &satisfied); 955 switch (cs) 956 { 957 case ANASTASIS_DB_CODE_STATUS_CHALLENGE_CODE_MISMATCH: 958 /* good, what we wanted */ 959 return GNUNET_OK; 960 case ANASTASIS_DB_CODE_STATUS_RATE_LIMITED: 961 /* the code we just obtained was already out of attempts */ 962 return (MHD_YES == 963 reply_rate_limited (gc)) 964 ? GNUNET_NO 965 : GNUNET_SYSERR; 966 case ANASTASIS_DB_CODE_STATUS_HARD_ERROR: 967 case ANASTASIS_DB_CODE_STATUS_SOFT_ERROR: 968 GNUNET_break (0); 969 return (MHD_YES == 970 TALER_MHD_reply_with_error (gc->connection, 971 MHD_HTTP_INTERNAL_SERVER_ERROR, 972 TALER_EC_GENERIC_DB_FETCH_FAILED, 973 "do_verify_challenge_code")) 974 ? GNUNET_NO 975 : GNUNET_SYSERR; 976 case ANASTASIS_DB_CODE_STATUS_NO_RESULTS: 977 return (MHD_YES == 978 reply_rate_limited (gc)) 979 ? GNUNET_NO 980 : GNUNET_SYSERR; 981 case ANASTASIS_DB_CODE_STATUS_VALID_CODE_STORED: 982 /* this should be impossible, we used code+1 */ 983 GNUNET_assert (0); 984 } 985 return GNUNET_SYSERR; 986 } 987 988 989 /** 990 * Handle special case of a security question where we do not 991 * generate a code. Rate limits answers against brute forcing. 992 * 993 * @param[in,out] gc request to handle 994 * @param decrypted_truth hash to check against 995 * @param decrypted_truth_size number of bytes in @a decrypted_truth 996 * @return MHD status code 997 */ 998 static enum MHD_Result 999 handle_security_question (struct SolveContext *gc, 1000 const void *decrypted_truth, 1001 size_t decrypted_truth_size) 1002 { 1003 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1004 "Handling security question challenge\n"); 1005 /* rate limit */ 1006 { 1007 enum GNUNET_GenericReturnValue ret; 1008 1009 ret = rate_limit (gc); 1010 if (GNUNET_OK != ret) 1011 return (GNUNET_NO == ret) ? MHD_YES : MHD_NO; 1012 } 1013 /* check reply matches truth */ 1014 if ( (decrypted_truth_size != sizeof (struct GNUNET_HashCode)) || 1015 (0 != memcmp (&gc->challenge_response, 1016 decrypted_truth, 1017 decrypted_truth_size)) ) 1018 { 1019 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1020 "Wrong answer provided to secure question had %u bytes, wanted %u\n", 1021 (unsigned int) decrypted_truth_size, 1022 (unsigned int) sizeof (struct GNUNET_HashCode)); 1023 return TALER_MHD_reply_with_error (gc->connection, 1024 MHD_HTTP_FORBIDDEN, 1025 TALER_EC_ANASTASIS_TRUTH_CHALLENGE_FAILED, 1026 NULL); 1027 } 1028 /* good, return the key share */ 1029 return return_key_share (&gc->truth_uuid, 1030 gc->connection); 1031 } 1032 1033 1034 /** 1035 * Handle special case of an answer being directly checked by the 1036 * plugin and not by our database. Also ensures that the 1037 * request is rate-limited. 1038 * 1039 * @param[in,out] gc request to handle 1040 * @param decrypted_truth hash to check against 1041 * @param decrypted_truth_size number of bytes in @a decrypted_truth 1042 * @return MHD status code 1043 */ 1044 static enum MHD_Result 1045 direct_validation (struct SolveContext *gc, 1046 const void *decrypted_truth, 1047 size_t decrypted_truth_size) 1048 { 1049 /* Non-random code, call plugin directly! */ 1050 enum ANASTASIS_AUTHORIZATION_SolveResult aar; 1051 enum GNUNET_GenericReturnValue ret; 1052 1053 ret = rate_limit (gc); 1054 if (GNUNET_OK != ret) 1055 return (GNUNET_NO == ret) ? MHD_YES : MHD_NO; 1056 gc->as = gc->authorization->start (gc->authorization->cls, 1057 &AH_trigger_daemon, 1058 NULL, 1059 &gc->truth_uuid, 1060 0LLU, 1061 decrypted_truth, 1062 decrypted_truth_size); 1063 if (NULL == gc->as) 1064 { 1065 GNUNET_break (0); 1066 return TALER_MHD_reply_with_error (gc->connection, 1067 MHD_HTTP_INTERNAL_SERVER_ERROR, 1068 TALER_EC_ANASTASIS_TRUTH_AUTHORIZATION_START_FAILED, 1069 NULL); 1070 } 1071 if (NULL == gc->authorization->solve) 1072 { 1073 GNUNET_break (0); 1074 return TALER_MHD_reply_with_error (gc->connection, 1075 MHD_HTTP_INTERNAL_SERVER_ERROR, 1076 TALER_EC_ANASTASIS_TRUTH_AUTHORIZATION_START_FAILED, 1077 "solve method not implemented for authorization method"); 1078 } 1079 aar = gc->authorization->solve (gc->as, 1080 gc->timeout, 1081 &gc->challenge_response, 1082 gc->connection); 1083 switch (aar) 1084 { 1085 case ANASTASIS_AUTHORIZATION_SRES_FAILED: 1086 return MHD_YES; 1087 case ANASTASIS_AUTHORIZATION_SRES_SUSPENDED: 1088 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1089 "Suspending request handling\n"); 1090 gc_suspended (gc); 1091 return MHD_YES; 1092 case ANASTASIS_AUTHORIZATION_SRES_FAILED_REPLY_FAILED: 1093 return MHD_NO; 1094 case ANASTASIS_AUTHORIZATION_SRES_FINISHED: 1095 return return_key_share (&gc->truth_uuid, 1096 gc->connection); 1097 } 1098 GNUNET_break (0); 1099 return MHD_NO; 1100 } 1101 1102 1103 /** 1104 * Handle special case of an answer being checked 1105 * by the plugin asynchronously (IBAN) after we inverted 1106 * the hash using the database. 1107 * 1108 * @param[in,out] gc request to handle 1109 * @param code validation code provided by the client 1110 * @param decrypted_truth hash to check against 1111 * @param decrypted_truth_size number of bytes in @a decrypted_truth 1112 * @return MHD status code 1113 */ 1114 static enum MHD_Result 1115 iban_validation (struct SolveContext *gc, 1116 uint64_t code, 1117 const void *decrypted_truth, 1118 size_t decrypted_truth_size) 1119 { 1120 enum ANASTASIS_AUTHORIZATION_SolveResult aar; 1121 1122 gc->as = gc->authorization->start (gc->authorization->cls, 1123 &AH_trigger_daemon, 1124 NULL, 1125 &gc->truth_uuid, 1126 code, 1127 decrypted_truth, 1128 decrypted_truth_size); 1129 if (NULL == gc->as) 1130 { 1131 GNUNET_break (0); 1132 return TALER_MHD_reply_with_error (gc->connection, 1133 MHD_HTTP_INTERNAL_SERVER_ERROR, 1134 TALER_EC_ANASTASIS_TRUTH_AUTHORIZATION_START_FAILED, 1135 NULL); 1136 } 1137 if (NULL == gc->authorization->solve) 1138 { 1139 GNUNET_break (0); 1140 return TALER_MHD_reply_with_error (gc->connection, 1141 MHD_HTTP_INTERNAL_SERVER_ERROR, 1142 TALER_EC_ANASTASIS_TRUTH_AUTHORIZATION_START_FAILED, 1143 "solve method not implemented for authorization method"); 1144 } 1145 aar = gc->authorization->solve (gc->as, 1146 gc->timeout, 1147 &gc->challenge_response, 1148 gc->connection); 1149 switch (aar) 1150 { 1151 case ANASTASIS_AUTHORIZATION_SRES_FAILED: 1152 return MHD_YES; 1153 case ANASTASIS_AUTHORIZATION_SRES_SUSPENDED: 1154 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1155 "Suspending request handling\n"); 1156 gc_suspended (gc); 1157 return MHD_YES; 1158 case ANASTASIS_AUTHORIZATION_SRES_FAILED_REPLY_FAILED: 1159 return MHD_NO; 1160 case ANASTASIS_AUTHORIZATION_SRES_FINISHED: 1161 return return_key_share (&gc->truth_uuid, 1162 gc->connection); 1163 } 1164 GNUNET_break (0); 1165 return MHD_NO; 1166 } 1167 1168 1169 enum MHD_Result 1170 AH_handler_truth_solve ( 1171 struct MHD_Connection *connection, 1172 struct TM_HandlerContext *hc, 1173 const struct ANASTASIS_CRYPTO_TruthUUIDP *truth_uuid, 1174 const char *upload_data, 1175 size_t *upload_data_size) 1176 { 1177 struct SolveContext *gc = hc->ctx; 1178 void *encrypted_truth; 1179 size_t encrypted_truth_size; 1180 void *decrypted_truth; 1181 size_t decrypted_truth_size; 1182 char *truth_mime = NULL; 1183 bool is_question; 1184 1185 if (NULL == gc) 1186 { 1187 /* Fresh request, do initial setup */ 1188 gc = GNUNET_new (struct SolveContext); 1189 gc->hc = hc; 1190 hc->ctx = gc; 1191 gc->connection = connection; 1192 gc->truth_uuid = *truth_uuid; 1193 gc->hc->cc = &request_done; 1194 gc->timeout = GNUNET_TIME_relative_to_absolute ( 1195 GNUNET_TIME_UNIT_SECONDS); 1196 TALER_MHD_parse_request_timeout (connection, 1197 &gc->timeout); 1198 } /* end of first-time initialization (if NULL == gc) */ 1199 else 1200 { 1201 /* might have been woken up by authorization plugin, 1202 so clear the flag. MDH called us, so we are 1203 clearly no longer suspended */ 1204 gc->suspended = false; 1205 if (NULL != gc->resp) 1206 { 1207 enum MHD_Result ret; 1208 1209 /* We generated a response asynchronously, queue that */ 1210 ret = MHD_queue_response (connection, 1211 gc->response_code, 1212 gc->resp); 1213 GNUNET_break (MHD_YES == ret); 1214 MHD_destroy_response (gc->resp); 1215 gc->resp = NULL; 1216 return ret; 1217 } 1218 if (NULL != gc->as) 1219 { 1220 /* Authorization process is "running", check what is going on */ 1221 GNUNET_assert (NULL != gc->authorization); 1222 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1223 "Continuing with running the authorization process\n"); 1224 GNUNET_assert (! gc->suspended); 1225 return run_authorization_process (connection, 1226 gc); 1227 } 1228 /* We get here if the async check for payment said this request 1229 was indeed paid! */ 1230 } 1231 1232 if (NULL == gc->root) 1233 { 1234 /* parse byte stream upload into JSON */ 1235 enum GNUNET_GenericReturnValue res; 1236 1237 res = TALER_MHD_parse_post_json (connection, 1238 &gc->opaque_post_parsing_context, 1239 upload_data, 1240 upload_data_size, 1241 &gc->root); 1242 if (GNUNET_SYSERR == res) 1243 { 1244 GNUNET_assert (NULL == gc->root); 1245 return MHD_NO; /* bad upload, could not even generate error */ 1246 } 1247 if ( (GNUNET_NO == res) || 1248 (NULL == gc->root) ) 1249 { 1250 GNUNET_assert (NULL == gc->root); 1251 return MHD_YES; /* so far incomplete upload or parser error */ 1252 } 1253 1254 /* 'root' is now initialized, parse JSON body */ 1255 { 1256 struct GNUNET_JSON_Specification spec[] = { 1257 GNUNET_JSON_spec_fixed_auto ("truth_decryption_key", 1258 &gc->truth_key), 1259 GNUNET_JSON_spec_fixed_auto ("h_response", 1260 &gc->challenge_response), 1261 GNUNET_JSON_spec_mark_optional ( 1262 GNUNET_JSON_spec_fixed_auto ("payment_secret", 1263 &gc->payment_identifier), 1264 &gc->no_payment_identifier_provided), 1265 GNUNET_JSON_spec_end () 1266 }; 1267 enum GNUNET_GenericReturnValue pres; 1268 1269 pres = TALER_MHD_parse_json_data (connection, 1270 gc->root, 1271 spec); 1272 if (GNUNET_SYSERR == pres) 1273 { 1274 GNUNET_break (0); 1275 return MHD_NO; /* hard failure */ 1276 } 1277 if (GNUNET_NO == pres) 1278 { 1279 GNUNET_break_op (0); 1280 return MHD_YES; /* failure */ 1281 } 1282 if (! gc->no_payment_identifier_provided) 1283 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1284 "Client provided payment identifier `%s'\n", 1285 TALER_B2S (&gc->payment_identifier)); 1286 } 1287 } 1288 1289 { 1290 /* load encrypted truth from DB; we may do this repeatedly 1291 while handling the same request, if payment was checked 1292 asynchronously! */ 1293 enum GNUNET_DB_QueryStatus qs; 1294 char *method; 1295 1296 qs = ANASTASIS_DB_get_truth ( 1297 &gc->truth_uuid, 1298 &encrypted_truth, 1299 &encrypted_truth_size, 1300 &truth_mime, 1301 &method); 1302 switch (qs) 1303 { 1304 case GNUNET_DB_STATUS_HARD_ERROR: 1305 case GNUNET_DB_STATUS_SOFT_ERROR: 1306 GNUNET_break (0); 1307 return TALER_MHD_reply_with_error (gc->connection, 1308 MHD_HTTP_INTERNAL_SERVER_ERROR, 1309 TALER_EC_GENERIC_DB_FETCH_FAILED, 1310 "get escrow challenge"); 1311 case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: 1312 return TALER_MHD_reply_with_error (connection, 1313 MHD_HTTP_NOT_FOUND, 1314 TALER_EC_ANASTASIS_TRUTH_UNKNOWN, 1315 NULL); 1316 case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: 1317 break; 1318 } 1319 is_question = (0 == strcmp ("question", 1320 method)); 1321 if (! is_question) 1322 { 1323 gc->authorization 1324 = ANASTASIS_authorization_plugin_load (method, 1325 AH_cfg); 1326 if (NULL == gc->authorization) 1327 { 1328 enum MHD_Result ret; 1329 1330 ret = TALER_MHD_reply_with_error ( 1331 connection, 1332 MHD_HTTP_INTERNAL_SERVER_ERROR, 1333 TALER_EC_ANASTASIS_TRUTH_AUTHORIZATION_METHOD_NO_LONGER_SUPPORTED, 1334 method); 1335 GNUNET_free (encrypted_truth); 1336 GNUNET_free (truth_mime); 1337 GNUNET_free (method); 1338 return ret; 1339 } 1340 gc->challenge_costs = &gc->authorization->costs; 1341 } 1342 else 1343 { 1344 gc->challenge_costs = &AH_question_costs; 1345 } 1346 GNUNET_free (method); 1347 } 1348 1349 /* check for payment */ 1350 if ( (is_question) || 1351 (! gc->authorization->payment_plugin_managed) ) 1352 { 1353 /* GNUNET_SYSERR is unreachable: anastasis-httpd refuses to start on 1354 a price list that mixes free and non-free currencies. */ 1355 GNUNET_assert (GNUNET_SYSERR != 1356 TALER_amount_list_check_uniform (gc->challenge_costs)); 1357 if (GNUNET_NO != 1358 TALER_amount_list_check_uniform (gc->challenge_costs)) 1359 { 1360 /* Check database to see if the transaction is paid for */ 1361 enum GNUNET_DB_QueryStatus qs; 1362 bool paid; 1363 1364 if (gc->no_payment_identifier_provided) 1365 { 1366 GNUNET_free (truth_mime); 1367 GNUNET_free (encrypted_truth); 1368 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1369 "Beginning payment, client did not provide payment identifier\n"); 1370 return begin_payment (gc); 1371 } 1372 qs = ANASTASIS_DB_get_challenge_payment ( 1373 &gc->payment_identifier, 1374 &gc->truth_uuid, 1375 &paid, 1376 &gc->challenge_paid); 1377 switch (qs) 1378 { 1379 case GNUNET_DB_STATUS_HARD_ERROR: 1380 case GNUNET_DB_STATUS_SOFT_ERROR: 1381 GNUNET_break (0); 1382 GNUNET_free (truth_mime); 1383 GNUNET_free (encrypted_truth); 1384 return TALER_MHD_reply_with_error (gc->connection, 1385 MHD_HTTP_INTERNAL_SERVER_ERROR, 1386 TALER_EC_GENERIC_DB_FETCH_FAILED, 1387 "get challenge payment"); 1388 case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: 1389 /* Create fresh payment identifier (cannot trust client) */ 1390 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1391 "Client-provided payment identifier is unknown.\n"); 1392 GNUNET_free (truth_mime); 1393 GNUNET_free (encrypted_truth); 1394 return begin_payment (gc); 1395 case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: 1396 if (! paid) 1397 { 1398 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1399 "Payment identifier known. Checking payment with client's payment identifier\n"); 1400 GNUNET_free (truth_mime); 1401 GNUNET_free (encrypted_truth); 1402 return begin_payment (gc); 1403 } 1404 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1405 "Payment confirmed\n"); 1406 gc->have_payment = true; 1407 break; 1408 } 1409 } 1410 else 1411 { 1412 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1413 "Request is free of charge\n"); 1414 } 1415 } 1416 1417 /* We've been paid, now validate the response */ 1418 /* decrypt encrypted_truth */ 1419 { 1420 enum GNUNET_GenericReturnValue res; 1421 1422 res = ANASTASIS_CRYPTO_truth_decrypt (&gc->truth_key, 1423 encrypted_truth, 1424 encrypted_truth_size, 1425 &decrypted_truth, 1426 &decrypted_truth_size); 1427 GNUNET_free (encrypted_truth); 1428 if (GNUNET_OK != res) 1429 { 1430 /* most likely, the decryption key is simply wrong */ 1431 GNUNET_break_op (0); 1432 GNUNET_free (truth_mime); 1433 return TALER_MHD_reply_with_error (connection, 1434 MHD_HTTP_BAD_REQUEST, 1435 TALER_EC_ANASTASIS_TRUTH_DECRYPTION_FAILED, 1436 NULL); 1437 } 1438 } 1439 1440 /* Special case for secure question: we do not generate a numeric challenge, 1441 but check that the hash matches */ 1442 if (is_question) 1443 { 1444 enum MHD_Result ret; 1445 1446 ret = handle_security_question (gc, 1447 decrypted_truth, 1448 decrypted_truth_size); 1449 GNUNET_free (truth_mime); 1450 GNUNET_free (decrypted_truth); 1451 return ret; 1452 } 1453 1454 /* Not security question, check for answer in DB */ 1455 { 1456 enum ANASTASIS_DB_CodeStatus cs; 1457 bool satisfied = false; 1458 uint64_t code; 1459 1460 GNUNET_free (truth_mime); 1461 if (gc->authorization->user_provided_code) 1462 { 1463 enum MHD_Result res; 1464 1465 if (GNUNET_TIME_absolute_is_past (gc->timeout)) 1466 { 1467 GNUNET_free (decrypted_truth); 1468 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1469 "Timeout with user provided code\n"); 1470 return TALER_MHD_reply_with_error (connection, 1471 MHD_HTTP_FORBIDDEN, 1472 TALER_EC_ANASTASIS_IBAN_MISSING_TRANSFER, 1473 "timeout awaiting validation"); 1474 } 1475 res = direct_validation (gc, 1476 decrypted_truth, 1477 decrypted_truth_size); 1478 GNUNET_free (decrypted_truth); 1479 return res; 1480 } 1481 1482 /* random code, check against database */ 1483 cs = ANASTASIS_DB_do_verify_challenge_code ( 1484 &gc->truth_uuid, 1485 &gc->challenge_response, 1486 &code, 1487 &satisfied); 1488 switch (cs) 1489 { 1490 case ANASTASIS_DB_CODE_STATUS_CHALLENGE_CODE_MISMATCH: 1491 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 1492 "Provided response does not match our stored challenge\n"); 1493 GNUNET_free (decrypted_truth); 1494 return TALER_MHD_reply_with_error (connection, 1495 MHD_HTTP_FORBIDDEN, 1496 TALER_EC_ANASTASIS_TRUTH_CHALLENGE_FAILED, 1497 NULL); 1498 case ANASTASIS_DB_CODE_STATUS_RATE_LIMITED: 1499 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1500 "Challenge is out of attempts, client must await a new code\n"); 1501 GNUNET_free (decrypted_truth); 1502 return reply_rate_limited (gc); 1503 case ANASTASIS_DB_CODE_STATUS_HARD_ERROR: 1504 case ANASTASIS_DB_CODE_STATUS_SOFT_ERROR: 1505 GNUNET_break (0); 1506 GNUNET_free (decrypted_truth); 1507 return TALER_MHD_reply_with_error (gc->connection, 1508 MHD_HTTP_INTERNAL_SERVER_ERROR, 1509 TALER_EC_GENERIC_DB_FETCH_FAILED, 1510 "do_verify_challenge_code"); 1511 case ANASTASIS_DB_CODE_STATUS_NO_RESULTS: 1512 GNUNET_free (decrypted_truth); 1513 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1514 "Specified challenge code %s was not issued\n", 1515 GNUNET_h2s (&gc->challenge_response)); 1516 return TALER_MHD_reply_with_error (connection, 1517 MHD_HTTP_FORBIDDEN, 1518 TALER_EC_ANASTASIS_TRUTH_CHALLENGE_UNKNOWN, 1519 "specific challenge code was not issued"); 1520 case ANASTASIS_DB_CODE_STATUS_VALID_CODE_STORED: 1521 if (! satisfied) 1522 { 1523 enum MHD_Result res; 1524 1525 res = iban_validation (gc, 1526 code, 1527 decrypted_truth, 1528 decrypted_truth_size); 1529 GNUNET_free (decrypted_truth); 1530 return res; 1531 } 1532 GNUNET_free (decrypted_truth); 1533 return return_key_share (&gc->truth_uuid, 1534 connection); 1535 default: 1536 GNUNET_break (0); 1537 return MHD_NO; 1538 } 1539 } 1540 }