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