anastasis-httpd_truth-solve.c (47395B)
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 #include "anastasis-httpd.h" 25 #include "anastasis_service.h" 26 #include "anastasis-httpd_truth.h" 27 #include <gnunet/gnunet_util_lib.h> 28 #include <gnunet/gnunet_rest_lib.h> 29 #include "anastasis_authorization_lib.h" 30 #include <taler/taler_merchant_service.h> 31 #include <taler/taler_json_lib.h> 32 #include <taler/taler_mhd_lib.h> 33 #include <taler/taler-merchant/post-private-orders.h> 34 #include <taler/taler-merchant/get-private-orders-ORDER_ID.h> 35 36 /** 37 * What is the maximum frequency at which we allow 38 * clients to attempt to answer security questions? 39 */ 40 #define MAX_QUESTION_FREQ GNUNET_TIME_relative_multiply ( \ 41 GNUNET_TIME_UNIT_SECONDS, 30) 42 43 /** 44 * How long should the wallet check for auto-refunds before giving up? 45 */ 46 #define AUTO_REFUND_TIMEOUT GNUNET_TIME_relative_multiply ( \ 47 GNUNET_TIME_UNIT_MINUTES, 2) 48 49 50 /** 51 * How many retries do we allow per code? 52 */ 53 #define INITIAL_RETRY_COUNTER 3 54 55 56 struct SolveContext 57 { 58 59 /** 60 * Payment Identifier 61 */ 62 struct ANASTASIS_PaymentSecretP payment_identifier; 63 64 /** 65 * Public key of the challenge which is solved. 66 */ 67 struct ANASTASIS_CRYPTO_TruthUUIDP truth_uuid; 68 69 /** 70 * Key to decrypt the truth. 71 */ 72 struct ANASTASIS_CRYPTO_TruthKeyP truth_key; 73 74 /** 75 * Cost for paying the challenge. 76 */ 77 struct TALER_Amount challenge_cost; 78 79 /** 80 * Our handler context. 81 */ 82 struct TM_HandlerContext *hc; 83 84 /** 85 * Opaque parsing context. 86 */ 87 void *opaque_post_parsing_context; 88 89 /** 90 * Uploaded JSON data, NULL if upload is not yet complete. 91 */ 92 json_t *root; 93 94 /** 95 * Kept in DLL for shutdown handling while suspended. 96 */ 97 struct SolveContext *next; 98 99 /** 100 * Kept in DLL for shutdown handling while suspended. 101 */ 102 struct SolveContext *prev; 103 104 /** 105 * Connection handle for closing or resuming 106 */ 107 struct MHD_Connection *connection; 108 109 /** 110 * Reference to the authorization plugin which was loaded 111 */ 112 struct ANASTASIS_AuthorizationPlugin *authorization; 113 114 /** 115 * Status of the authorization 116 */ 117 struct ANASTASIS_AUTHORIZATION_State *as; 118 119 /** 120 * Used while we are awaiting proposal creation. 121 */ 122 struct TALER_MERCHANT_PostPrivateOrdersHandle *po; 123 124 /** 125 * Used while we are waiting payment. 126 */ 127 struct TALER_MERCHANT_GetPrivateOrderHandle *cpo; 128 129 /** 130 * HTTP response code to use on resume, if non-NULL. 131 */ 132 struct MHD_Response *resp; 133 134 /** 135 * Our entry in the #to_heap, or NULL. 136 */ 137 struct GNUNET_CONTAINER_HeapNode *hn; 138 139 /** 140 * Challenge response we got from the request. 141 */ 142 struct GNUNET_HashCode challenge_response; 143 144 /** 145 * How long do we wait at most for payment or 146 * authorization? 147 */ 148 struct GNUNET_TIME_Absolute timeout; 149 150 /** 151 * Random authorization code we are using. 152 */ 153 uint64_t code; 154 155 /** 156 * HTTP response code to use on resume, if resp is set. 157 */ 158 unsigned int response_code; 159 160 /** 161 * true if client did not provide a payment secret / order ID. 162 */ 163 bool no_payment_identifier_provided; 164 165 /** 166 * True if this entry is in the #gc_head DLL. 167 */ 168 bool in_list; 169 170 /** 171 * True if this entry is currently suspended. 172 */ 173 bool suspended; 174 175 }; 176 177 178 /** 179 * Head of linked list over all authorization processes 180 */ 181 static struct SolveContext *gc_head; 182 183 /** 184 * Tail of linked list over all authorization processes 185 */ 186 static struct SolveContext *gc_tail; 187 188 /** 189 * Task running #do_timeout(). 190 */ 191 static struct GNUNET_SCHEDULER_Task *to_task; 192 193 194 /** 195 * Generate a response telling the client that answering this 196 * challenge failed because the rate limit has been exceeded. 197 * 198 * @param gc request to answer for 199 * @return MHD status code 200 */ 201 static MHD_RESULT 202 reply_rate_limited (const struct SolveContext *gc) 203 { 204 if (NULL != gc->authorization) 205 return TALER_MHD_REPLY_JSON_PACK ( 206 gc->connection, 207 MHD_HTTP_TOO_MANY_REQUESTS, 208 TALER_MHD_PACK_EC (TALER_EC_ANASTASIS_TRUTH_RATE_LIMITED), 209 GNUNET_JSON_pack_uint64 ("request_limit", 210 gc->authorization->retry_counter), 211 GNUNET_JSON_pack_time_rel ("request_frequency", 212 gc->authorization->code_rotation_period)); 213 /* must be security question */ 214 return TALER_MHD_REPLY_JSON_PACK ( 215 gc->connection, 216 MHD_HTTP_TOO_MANY_REQUESTS, 217 TALER_MHD_PACK_EC (TALER_EC_ANASTASIS_TRUTH_RATE_LIMITED), 218 GNUNET_JSON_pack_uint64 ("request_limit", 219 INITIAL_RETRY_COUNTER), 220 GNUNET_JSON_pack_time_rel ("request_frequency", 221 MAX_QUESTION_FREQ)); 222 } 223 224 225 /** 226 * Timeout requests that are past their due date. 227 * 228 * @param cls NULL 229 */ 230 static void 231 do_timeout (void *cls) 232 { 233 struct SolveContext *gc; 234 235 (void) cls; 236 to_task = NULL; 237 while (NULL != 238 (gc = GNUNET_CONTAINER_heap_peek (AH_to_heap))) 239 { 240 if (GNUNET_TIME_absolute_is_future (gc->timeout)) 241 break; 242 if (gc->suspended) 243 { 244 /* Test needed as we may have a "concurrent" 245 wakeup from another task that did not clear 246 this entry from the heap before the 247 response process concluded. */ 248 gc->suspended = false; 249 MHD_resume_connection (gc->connection); 250 } 251 GNUNET_assert (NULL != gc->hn); 252 gc->hn = NULL; 253 GNUNET_assert (gc == 254 GNUNET_CONTAINER_heap_remove_root (AH_to_heap)); 255 } 256 if (NULL == gc) 257 return; 258 to_task = GNUNET_SCHEDULER_add_at (gc->timeout, 259 &do_timeout, 260 NULL); 261 } 262 263 264 void 265 AH_truth_solve_shutdown (void) 266 { 267 struct SolveContext *gc; 268 269 while (NULL != (gc = gc_head)) 270 { 271 GNUNET_CONTAINER_DLL_remove (gc_head, 272 gc_tail, 273 gc); 274 gc->in_list = false; 275 if (NULL != gc->cpo) 276 { 277 TALER_MERCHANT_get_private_order_cancel (gc->cpo); 278 gc->cpo = NULL; 279 } 280 if (NULL != gc->po) 281 { 282 TALER_MERCHANT_post_private_orders_cancel (gc->po); 283 gc->po = NULL; 284 } 285 if (gc->suspended) 286 { 287 gc->suspended = false; 288 MHD_resume_connection (gc->connection); 289 } 290 if (NULL != gc->as) 291 { 292 gc->authorization->cleanup (gc->as); 293 gc->as = NULL; 294 gc->authorization = NULL; 295 } 296 } 297 ANASTASIS_authorization_plugin_shutdown (); 298 if (NULL != to_task) 299 { 300 GNUNET_SCHEDULER_cancel (to_task); 301 to_task = NULL; 302 } 303 } 304 305 306 /** 307 * Callback used to notify the application about completed requests. 308 * Cleans up the requests data structures. 309 * 310 * @param[in,out] hc 311 */ 312 static void 313 request_done (struct TM_HandlerContext *hc) 314 { 315 struct SolveContext *gc = hc->ctx; 316 317 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 318 "Request completed\n"); 319 if (NULL == gc) 320 return; 321 hc->cc = NULL; 322 GNUNET_assert (! gc->suspended); 323 if (gc->in_list) 324 { 325 GNUNET_CONTAINER_DLL_remove (gc_head, 326 gc_tail, 327 gc); 328 gc->in_list = false; 329 } 330 if (NULL != gc->hn) 331 { 332 GNUNET_assert (gc == 333 GNUNET_CONTAINER_heap_remove_node (gc->hn)); 334 gc->hn = NULL; 335 } 336 if (NULL != gc->as) 337 { 338 gc->authorization->cleanup (gc->as); 339 gc->authorization = NULL; 340 gc->as = NULL; 341 } 342 if (NULL != gc->cpo) 343 { 344 TALER_MERCHANT_get_private_order_cancel (gc->cpo); 345 gc->cpo = NULL; 346 } 347 if (NULL != gc->po) 348 { 349 TALER_MERCHANT_post_private_orders_cancel (gc->po); 350 gc->po = NULL; 351 } 352 if (NULL != gc->root) 353 { 354 json_decref (gc->root); 355 gc->root = NULL; 356 } 357 TALER_MHD_parse_post_cleanup_callback (gc->opaque_post_parsing_context); 358 GNUNET_free (gc); 359 hc->ctx = NULL; 360 } 361 362 363 /** 364 * Transmit a payment request for @a order_id on @a connection 365 * 366 * @param gc context to make payment request for 367 */ 368 static void 369 make_payment_request (struct SolveContext *gc) 370 { 371 struct MHD_Response *resp; 372 373 resp = MHD_create_response_from_buffer (0, 374 NULL, 375 MHD_RESPMEM_PERSISTENT); 376 GNUNET_assert (NULL != resp); 377 TALER_MHD_add_global_headers (resp, 378 false); 379 { 380 char *hdr; 381 char *order_id; 382 const char *pfx; 383 const char *hn; 384 385 if (0 == strncasecmp ("https://", 386 AH_backend_url, 387 strlen ("https://"))) 388 { 389 pfx = "taler://"; 390 hn = &AH_backend_url[strlen ("https://")]; 391 } 392 else if (0 == strncasecmp ("http://", 393 AH_backend_url, 394 strlen ("http://"))) 395 { 396 pfx = "taler+http://"; 397 hn = &AH_backend_url[strlen ("http://")]; 398 } 399 else 400 { 401 /* This invariant holds as per check in anastasis-httpd.c */ 402 GNUNET_assert (0); 403 } 404 /* This invariant holds as per check in anastasis-httpd.c */ 405 GNUNET_assert (0 != strlen (hn)); 406 407 order_id = GNUNET_STRINGS_data_to_string_alloc ( 408 &gc->payment_identifier, 409 sizeof (gc->payment_identifier)); 410 GNUNET_asprintf (&hdr, 411 "%spay/%s%s/", 412 pfx, 413 hn, 414 order_id); 415 GNUNET_free (order_id); 416 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 417 "Sending payment request `%s'\n", 418 hdr); 419 GNUNET_break (MHD_YES == 420 MHD_add_response_header (resp, 421 ANASTASIS_HTTP_HEADER_TALER, 422 hdr)); 423 GNUNET_free (hdr); 424 } 425 gc->resp = resp; 426 gc->response_code = MHD_HTTP_PAYMENT_REQUIRED; 427 } 428 429 430 /** 431 * Callbacks of this type are used to serve the result of submitting a 432 * /contract request to a merchant. 433 * 434 * @param cls our `struct SolveContext` 435 * @param por response details 436 */ 437 static void 438 proposal_cb (void *cls, 439 const struct TALER_MERCHANT_PostPrivateOrdersResponse *por) 440 { 441 struct SolveContext *gc = cls; 442 enum GNUNET_DB_QueryStatus qs; 443 444 gc->po = NULL; 445 GNUNET_assert (gc->in_list); 446 GNUNET_CONTAINER_DLL_remove (gc_head, 447 gc_tail, 448 gc); 449 gc->in_list = false; 450 GNUNET_assert (gc->suspended); 451 gc->suspended = false; 452 MHD_resume_connection (gc->connection); 453 AH_trigger_daemon (NULL); 454 if (MHD_HTTP_OK != por->hr.http_status) 455 { 456 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 457 "Backend returned status %u/%d\n", 458 por->hr.http_status, 459 (int) por->hr.ec); 460 GNUNET_break (0); 461 gc->resp = TALER_MHD_MAKE_JSON_PACK ( 462 GNUNET_JSON_pack_uint64 ("code", 463 TALER_EC_ANASTASIS_TRUTH_PAYMENT_CREATE_BACKEND_ERROR), 464 GNUNET_JSON_pack_string ("hint", 465 "Failed to setup order with merchant backend"), 466 GNUNET_JSON_pack_uint64 ("backend-ec", 467 por->hr.ec), 468 GNUNET_JSON_pack_uint64 ("backend-http-status", 469 por->hr.http_status), 470 GNUNET_JSON_pack_allow_null ( 471 GNUNET_JSON_pack_object_steal ("backend-reply", 472 (json_t *) por->hr.reply))); 473 gc->response_code = MHD_HTTP_BAD_GATEWAY; 474 return; 475 } 476 qs = db->record_challenge_payment (db->cls, 477 &gc->truth_uuid, 478 &gc->payment_identifier, 479 &gc->challenge_cost); 480 if (0 >= qs) 481 { 482 GNUNET_break (0); 483 gc->resp = TALER_MHD_make_error (TALER_EC_GENERIC_DB_STORE_FAILED, 484 "record challenge payment"); 485 gc->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR; 486 return; 487 } 488 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 489 "Setup fresh order, creating payment request\n"); 490 make_payment_request (gc); 491 } 492 493 494 /** 495 * Callback to process a GET /check-payment request 496 * 497 * @param cls our `struct SolveContext` 498 * @param osr order status 499 */ 500 static void 501 check_payment_cb (void *cls, 502 const struct TALER_MERCHANT_GetPrivateOrderResponse *osr) 503 504 { 505 struct SolveContext *gc = cls; 506 const struct TALER_MERCHANT_HttpResponse *hr = &osr->hr; 507 508 gc->cpo = NULL; 509 GNUNET_assert (gc->in_list); 510 GNUNET_CONTAINER_DLL_remove (gc_head, 511 gc_tail, 512 gc); 513 gc->in_list = false; 514 GNUNET_assert (gc->suspended); 515 gc->suspended = false; 516 MHD_resume_connection (gc->connection); 517 AH_trigger_daemon (NULL); 518 519 switch (hr->http_status) 520 { 521 case MHD_HTTP_OK: 522 GNUNET_assert (NULL != osr); 523 break; 524 case MHD_HTTP_NOT_FOUND: 525 /* We created this order before, how can it be not found now? */ 526 GNUNET_break (0); 527 gc->resp = TALER_MHD_make_error (TALER_EC_ANASTASIS_TRUTH_ORDER_DISAPPEARED, 528 NULL); 529 gc->response_code = MHD_HTTP_BAD_GATEWAY; 530 return; 531 case MHD_HTTP_BAD_GATEWAY: 532 gc->resp = TALER_MHD_make_error ( 533 TALER_EC_ANASTASIS_TRUTH_BACKEND_EXCHANGE_BAD, 534 NULL); 535 gc->response_code = MHD_HTTP_BAD_GATEWAY; 536 return; 537 case MHD_HTTP_GATEWAY_TIMEOUT: 538 gc->resp = TALER_MHD_make_error (TALER_EC_ANASTASIS_GENERIC_BACKEND_TIMEOUT, 539 "Timeout check payment status"); 540 GNUNET_assert (NULL != gc->resp); 541 gc->response_code = MHD_HTTP_GATEWAY_TIMEOUT; 542 return; 543 default: 544 { 545 char status[14]; 546 547 GNUNET_snprintf (status, 548 sizeof (status), 549 "%u", 550 hr->http_status); 551 gc->resp = TALER_MHD_make_error ( 552 TALER_EC_ANASTASIS_TRUTH_UNEXPECTED_PAYMENT_STATUS, 553 status); 554 GNUNET_assert (NULL != gc->resp); 555 gc->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR; 556 return; 557 } 558 } 559 560 GNUNET_assert (MHD_HTTP_OK == hr->http_status); 561 switch (osr->details.ok.status) 562 { 563 case TALER_MERCHANT_OSC_PAID: 564 { 565 enum GNUNET_DB_QueryStatus qs; 566 567 qs = db->update_challenge_payment (db->cls, 568 &gc->truth_uuid, 569 &gc->payment_identifier); 570 if (0 <= qs) 571 { 572 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 573 "Order has been paid, continuing with request processing\n") 574 ; 575 return; /* continue as planned */ 576 } 577 GNUNET_break (0); 578 gc->resp = TALER_MHD_make_error (TALER_EC_GENERIC_DB_STORE_FAILED, 579 "update challenge payment"); 580 gc->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR; 581 return; /* continue as planned */ 582 } 583 case TALER_MERCHANT_OSC_CLAIMED: 584 case TALER_MERCHANT_OSC_UNPAID: 585 /* repeat payment request */ 586 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 587 "Order remains unpaid, sending payment request again\n"); 588 make_payment_request (gc); 589 return; 590 } 591 /* should never get here */ 592 GNUNET_break (0); 593 } 594 595 596 /** 597 * Helper function used to ask our backend to begin processing a 598 * payment for the user's account. May perform asynchronous 599 * operations by suspending the connection if required. 600 * 601 * @param gc context to begin payment for. 602 * @return MHD status code 603 */ 604 static MHD_RESULT 605 begin_payment (struct SolveContext *gc) 606 { 607 enum GNUNET_DB_QueryStatus qs; 608 char *order_id; 609 610 qs = db->lookup_challenge_payment (db->cls, 611 &gc->truth_uuid, 612 &gc->payment_identifier); 613 if (qs < 0) 614 { 615 GNUNET_break (0); 616 return TALER_MHD_reply_with_error (gc->connection, 617 MHD_HTTP_INTERNAL_SERVER_ERROR, 618 TALER_EC_GENERIC_DB_FETCH_FAILED, 619 "lookup challenge payment"); 620 } 621 GNUNET_assert (! gc->in_list); 622 gc->in_list = true; 623 GNUNET_CONTAINER_DLL_insert (gc_tail, 624 gc_head, 625 gc); 626 GNUNET_assert (! gc->suspended); 627 gc->suspended = true; 628 MHD_suspend_connection (gc->connection); 629 if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs) 630 { 631 /* We already created the order, check if it was paid */ 632 struct GNUNET_TIME_Relative timeout; 633 634 order_id = GNUNET_STRINGS_data_to_string_alloc ( 635 &gc->payment_identifier, 636 sizeof (gc->payment_identifier)); 637 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 638 "Order exists, checking payment status for order `%s'\n", 639 order_id); 640 timeout = GNUNET_TIME_absolute_get_remaining (gc->timeout); 641 gc->cpo = TALER_MERCHANT_get_private_order_create (AH_ctx, 642 AH_backend_url, 643 order_id); 644 GNUNET_assert (NULL != gc->cpo); 645 GNUNET_assert ( 646 GNUNET_OK == 647 TALER_MERCHANT_get_private_order_set_options ( 648 gc->cpo, 649 TALER_MERCHANT_get_private_order_option_timeout (timeout))); 650 GNUNET_assert (TALER_EC_NONE == 651 TALER_MERCHANT_get_private_order_start (gc->cpo, 652 &check_payment_cb, 653 gc)); 654 } 655 else 656 { 657 /* Create a fresh order */ 658 struct GNUNET_TIME_Timestamp pay_deadline; 659 660 GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK, 661 &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 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 = db->get_key_share (db->cls, 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 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 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 = db->create_challenge_code (db->cls, 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 = db->verify_challenge_code (db->cls, 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 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 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 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 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 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 = db->get_escrow_challenge (db->cls, 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 db, 1290 AH_cfg); 1291 if (NULL == gc->authorization) 1292 { 1293 MHD_RESULT ret; 1294 1295 ret = TALER_MHD_reply_with_error ( 1296 connection, 1297 MHD_HTTP_INTERNAL_SERVER_ERROR, 1298 TALER_EC_ANASTASIS_TRUTH_AUTHORIZATION_METHOD_NO_LONGER_SUPPORTED, 1299 method); 1300 GNUNET_free (encrypted_truth); 1301 GNUNET_free (truth_mime); 1302 GNUNET_free (method); 1303 return ret; 1304 } 1305 gc->challenge_cost = gc->authorization->cost; 1306 } 1307 else 1308 { 1309 gc->challenge_cost = AH_question_cost; 1310 } 1311 GNUNET_free (method); 1312 } 1313 1314 /* check for payment */ 1315 if ( (is_question) || 1316 (! gc->authorization->payment_plugin_managed) ) 1317 { 1318 if (! TALER_amount_is_zero (&gc->challenge_cost)) 1319 { 1320 /* Check database to see if the transaction is paid for */ 1321 enum GNUNET_DB_QueryStatus qs; 1322 bool paid; 1323 1324 if (gc->no_payment_identifier_provided) 1325 { 1326 GNUNET_free (truth_mime); 1327 GNUNET_free (encrypted_truth); 1328 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1329 "Beginning payment, client did not provide payment identifier\n"); 1330 return begin_payment (gc); 1331 } 1332 qs = db->check_challenge_payment (db->cls, 1333 &gc->payment_identifier, 1334 &gc->truth_uuid, 1335 &paid); 1336 switch (qs) 1337 { 1338 case GNUNET_DB_STATUS_HARD_ERROR: 1339 case GNUNET_DB_STATUS_SOFT_ERROR: 1340 GNUNET_break (0); 1341 GNUNET_free (truth_mime); 1342 GNUNET_free (encrypted_truth); 1343 return TALER_MHD_reply_with_error (gc->connection, 1344 MHD_HTTP_INTERNAL_SERVER_ERROR, 1345 TALER_EC_GENERIC_DB_FETCH_FAILED, 1346 "check challenge payment"); 1347 case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: 1348 /* Create fresh payment identifier (cannot trust client) */ 1349 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1350 "Client-provided payment identifier is unknown.\n"); 1351 GNUNET_free (truth_mime); 1352 GNUNET_free (encrypted_truth); 1353 return begin_payment (gc); 1354 case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: 1355 if (! paid) 1356 { 1357 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1358 "Payment identifier known. Checking payment with client's payment identifier\n"); 1359 GNUNET_free (truth_mime); 1360 GNUNET_free (encrypted_truth); 1361 return begin_payment (gc); 1362 } 1363 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1364 "Payment confirmed\n"); 1365 break; 1366 } 1367 } 1368 else 1369 { 1370 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1371 "Request is free of charge\n"); 1372 } 1373 } 1374 1375 /* We've been paid, now validate the response */ 1376 /* decrypt encrypted_truth */ 1377 ANASTASIS_CRYPTO_truth_decrypt (&gc->truth_key, 1378 encrypted_truth, 1379 encrypted_truth_size, 1380 &decrypted_truth, 1381 &decrypted_truth_size); 1382 GNUNET_free (encrypted_truth); 1383 if (NULL == decrypted_truth) 1384 { 1385 /* most likely, the decryption key is simply wrong */ 1386 GNUNET_break_op (0); 1387 GNUNET_free (truth_mime); 1388 return TALER_MHD_reply_with_error (connection, 1389 MHD_HTTP_BAD_REQUEST, 1390 TALER_EC_ANASTASIS_TRUTH_DECRYPTION_FAILED, 1391 NULL); 1392 } 1393 1394 /* Special case for secure question: we do not generate a numeric challenge, 1395 but check that the hash matches */ 1396 if (is_question) 1397 { 1398 MHD_RESULT ret; 1399 1400 ret = handle_security_question (gc, 1401 decrypted_truth, 1402 decrypted_truth_size); 1403 GNUNET_free (truth_mime); 1404 GNUNET_free (decrypted_truth); 1405 return ret; 1406 } 1407 1408 /* Not security question, check for answer in DB */ 1409 { 1410 enum ANASTASIS_DB_CodeStatus cs; 1411 bool satisfied = false; 1412 uint64_t code; 1413 1414 GNUNET_free (truth_mime); 1415 if (gc->authorization->user_provided_code) 1416 { 1417 MHD_RESULT res; 1418 1419 if (GNUNET_TIME_absolute_is_past (gc->timeout)) 1420 { 1421 GNUNET_free (decrypted_truth); 1422 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1423 "Timeout with user provided code\n"); 1424 return TALER_MHD_reply_with_error (connection, 1425 MHD_HTTP_FORBIDDEN, 1426 TALER_EC_ANASTASIS_IBAN_MISSING_TRANSFER, 1427 "timeout awaiting validation"); 1428 } 1429 res = direct_validation (gc, 1430 decrypted_truth, 1431 decrypted_truth_size); 1432 GNUNET_free (decrypted_truth); 1433 return res; 1434 } 1435 1436 /* random code, check against database */ 1437 cs = db->verify_challenge_code (db->cls, 1438 &gc->truth_uuid, 1439 &gc->challenge_response, 1440 &code, 1441 &satisfied); 1442 switch (cs) 1443 { 1444 case ANASTASIS_DB_CODE_STATUS_CHALLENGE_CODE_MISMATCH: 1445 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 1446 "Provided response does not match our stored challenge\n"); 1447 GNUNET_free (decrypted_truth); 1448 return TALER_MHD_reply_with_error (connection, 1449 MHD_HTTP_FORBIDDEN, 1450 TALER_EC_ANASTASIS_TRUTH_CHALLENGE_FAILED, 1451 NULL); 1452 case ANASTASIS_DB_CODE_STATUS_HARD_ERROR: 1453 case ANASTASIS_DB_CODE_STATUS_SOFT_ERROR: 1454 GNUNET_break (0); 1455 GNUNET_free (decrypted_truth); 1456 return TALER_MHD_reply_with_error (gc->connection, 1457 MHD_HTTP_INTERNAL_SERVER_ERROR, 1458 TALER_EC_GENERIC_DB_FETCH_FAILED, 1459 "verify_challenge_code"); 1460 case ANASTASIS_DB_CODE_STATUS_NO_RESULTS: 1461 GNUNET_free (decrypted_truth); 1462 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1463 "Specified challenge code %s was not issued\n", 1464 GNUNET_h2s (&gc->challenge_response)); 1465 return TALER_MHD_reply_with_error (connection, 1466 MHD_HTTP_FORBIDDEN, 1467 TALER_EC_ANASTASIS_TRUTH_CHALLENGE_UNKNOWN, 1468 "specific challenge code was not issued"); 1469 case ANASTASIS_DB_CODE_STATUS_VALID_CODE_STORED: 1470 if (! satisfied) 1471 { 1472 MHD_RESULT res; 1473 1474 res = iban_validation (gc, 1475 code, 1476 decrypted_truth, 1477 decrypted_truth_size); 1478 GNUNET_free (decrypted_truth); 1479 return res; 1480 } 1481 GNUNET_free (decrypted_truth); 1482 return return_key_share (&gc->truth_uuid, 1483 connection); 1484 default: 1485 GNUNET_break (0); 1486 return MHD_NO; 1487 } 1488 } 1489 }