anastasis-httpd_truth-solve.c (47590B)
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 = db->record_challenge_payment (db->cls, 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 = db->update_challenge_payment (db->cls, 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 = db->lookup_challenge_payment (db->cls, 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 (GNUNET_CRYPTO_QUALITY_WEAK, 662 &gc->payment_identifier, 663 sizeof (struct ANASTASIS_PaymentSecretP)); 664 order_id = GNUNET_STRINGS_data_to_string_alloc ( 665 &gc->payment_identifier, 666 sizeof (gc->payment_identifier)); 667 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 668 "Creating fresh order `%s'\n", 669 order_id); 670 pay_deadline = GNUNET_TIME_relative_to_timestamp ( 671 ANASTASIS_CHALLENGE_OFFER_LIFETIME); 672 { 673 json_t *order; 674 675 order = GNUNET_JSON_PACK ( 676 TALER_JSON_pack_amount ("amount", 677 &gc->challenge_cost), 678 GNUNET_JSON_pack_string ("summary", 679 "challenge fee for anastasis service"), 680 GNUNET_JSON_pack_string ("order_id", 681 order_id), 682 GNUNET_JSON_pack_time_rel ("auto_refund", 683 AUTO_REFUND_TIMEOUT), 684 GNUNET_JSON_pack_timestamp ("pay_deadline", 685 pay_deadline)); 686 gc->po = TALER_MERCHANT_post_private_orders_create (AH_ctx, 687 AH_backend_url, 688 order); 689 json_decref (order); 690 } 691 GNUNET_assert ( 692 GNUNET_OK == 693 TALER_MERCHANT_post_private_orders_set_options ( 694 gc->po, 695 TALER_MERCHANT_post_private_orders_option_create_token (false), 696 TALER_MERCHANT_post_private_orders_option_refund_delay ( 697 AUTO_REFUND_TIMEOUT))); 698 GNUNET_assert (TALER_EC_NONE == 699 TALER_MERCHANT_post_private_orders_start (gc->po, 700 &proposal_cb, 701 gc)); 702 } 703 GNUNET_free (order_id); 704 AH_trigger_curl (); 705 return MHD_YES; 706 } 707 708 709 /** 710 * Load encrypted keyshare from db and return it to the client. 711 * 712 * @param truth_uuid UUID to the truth for the looup 713 * @param connection the connection to respond upon 714 * @return MHD status code 715 */ 716 static enum MHD_Result 717 return_key_share ( 718 const struct ANASTASIS_CRYPTO_TruthUUIDP *truth_uuid, 719 struct MHD_Connection *connection) 720 { 721 struct ANASTASIS_CRYPTO_EncryptedKeyShareP encrypted_keyshare; 722 723 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 724 "Returning key share of %s\n", 725 TALER_B2S (truth_uuid)); 726 { 727 enum GNUNET_DB_QueryStatus qs; 728 729 qs = db->get_key_share (db->cls, 730 truth_uuid, 731 &encrypted_keyshare); 732 switch (qs) 733 { 734 case GNUNET_DB_STATUS_HARD_ERROR: 735 case GNUNET_DB_STATUS_SOFT_ERROR: 736 GNUNET_break (0); 737 return TALER_MHD_reply_with_error (connection, 738 MHD_HTTP_INTERNAL_SERVER_ERROR, 739 TALER_EC_GENERIC_DB_FETCH_FAILED, 740 "get key share"); 741 case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: 742 /* this should be "impossible", after all the 743 client was able to solve the challenge! 744 (Exception: we deleted the truth via GC 745 just while the client was trying to recover. 746 Alas, highly unlikely...) */ 747 GNUNET_break (0); 748 return TALER_MHD_reply_with_error (connection, 749 MHD_HTTP_NOT_FOUND, 750 TALER_EC_ANASTASIS_TRUTH_KEY_SHARE_GONE, 751 NULL); 752 case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: 753 break; 754 } 755 } 756 757 { 758 struct MHD_Response *resp; 759 enum MHD_Result ret; 760 761 resp = MHD_create_response_from_buffer (sizeof (encrypted_keyshare), 762 &encrypted_keyshare, 763 MHD_RESPMEM_MUST_COPY); 764 TALER_MHD_add_global_headers (resp, 765 false); 766 ret = MHD_queue_response (connection, 767 MHD_HTTP_OK, 768 resp); 769 MHD_destroy_response (resp); 770 return ret; 771 } 772 } 773 774 775 /** 776 * Mark @a gc as suspended and update the respective 777 * data structures and jobs. 778 * 779 * @param[in,out] gc context of the suspended operation 780 */ 781 static void 782 gc_suspended (struct SolveContext *gc) 783 { 784 GNUNET_assert (NULL == gc->hn); 785 GNUNET_assert (! gc->suspended); 786 gc->suspended = true; 787 if (NULL == AH_to_heap) 788 AH_to_heap = GNUNET_CONTAINER_heap_create ( 789 GNUNET_CONTAINER_HEAP_ORDER_MIN); 790 gc->hn = GNUNET_CONTAINER_heap_insert (AH_to_heap, 791 gc, 792 gc->timeout.abs_value_us); 793 if (NULL != to_task) 794 { 795 GNUNET_SCHEDULER_cancel (to_task); 796 to_task = NULL; 797 } 798 { 799 struct SolveContext *rn; 800 801 rn = GNUNET_CONTAINER_heap_peek (AH_to_heap); 802 to_task = GNUNET_SCHEDULER_add_at (rn->timeout, 803 &do_timeout, 804 NULL); 805 } 806 } 807 808 809 /** 810 * Run the authorization method-specific 'process' function and continue 811 * based on its result with generating an HTTP response. 812 * 813 * @param connection the connection we are handling 814 * @param gc our overall handler context 815 */ 816 static enum MHD_Result 817 run_authorization_process (struct MHD_Connection *connection, 818 struct SolveContext *gc) 819 { 820 enum ANASTASIS_AUTHORIZATION_SolveResult ret; 821 822 GNUNET_assert (! gc->suspended); 823 if (NULL == gc->authorization->solve) 824 { 825 GNUNET_break (0); 826 return TALER_MHD_reply_with_error (gc->connection, 827 MHD_HTTP_INTERNAL_SERVER_ERROR, 828 TALER_EC_ANASTASIS_TRUTH_AUTHORIZATION_START_FAILED, 829 "solve method not implemented for authorization method"); 830 } 831 ret = gc->authorization->solve (gc->as, 832 gc->timeout, 833 &gc->challenge_response, 834 connection); 835 switch (ret) 836 { 837 case ANASTASIS_AUTHORIZATION_SRES_SUSPENDED: 838 /* connection was suspended */ 839 gc_suspended (gc); 840 return MHD_YES; 841 case ANASTASIS_AUTHORIZATION_SRES_FAILED: 842 gc->authorization->cleanup (gc->as); 843 gc->as = NULL; 844 return MHD_YES; 845 case ANASTASIS_AUTHORIZATION_SRES_FAILED_REPLY_FAILED: 846 gc->authorization->cleanup (gc->as); 847 gc->as = NULL; 848 return MHD_NO; 849 case ANASTASIS_AUTHORIZATION_SRES_FINISHED: 850 GNUNET_assert (! gc->suspended); 851 gc->authorization->cleanup (gc->as); 852 gc->as = NULL; 853 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 854 "Resuming with authorization successful!\n"); 855 if (gc->in_list) 856 { 857 GNUNET_CONTAINER_DLL_remove (gc_head, 858 gc_tail, 859 gc); 860 gc->in_list = false; 861 } 862 return MHD_YES; 863 } 864 GNUNET_break (0); 865 return MHD_NO; 866 } 867 868 869 /** 870 * Use the database to rate-limit queries to the authentication 871 * procedure, but without actually storing 'real' challenge codes. 872 * 873 * @param[in,out] gc context to rate limit requests for 874 * @return #GNUNET_OK if rate-limiting passes, 875 * #GNUNET_NO if a reply was sent (rate limited) 876 * #GNUNET_SYSERR if we failed and no reply 877 * was queued 878 */ 879 static enum GNUNET_GenericReturnValue 880 rate_limit (struct SolveContext *gc) 881 { 882 enum GNUNET_DB_QueryStatus qs; 883 struct GNUNET_TIME_Timestamp rt; 884 uint64_t code; 885 enum ANASTASIS_DB_CodeStatus cs; 886 struct GNUNET_HashCode hc; 887 bool satisfied; 888 uint64_t dummy; 889 890 rt = GNUNET_TIME_UNIT_FOREVER_TS; 891 qs = db->create_challenge_code (db->cls, 892 &gc->truth_uuid, 893 MAX_QUESTION_FREQ, 894 GNUNET_TIME_UNIT_HOURS, 895 INITIAL_RETRY_COUNTER, 896 &rt, 897 &code); 898 if (0 > qs) 899 { 900 GNUNET_break (0 < qs); 901 return (MHD_YES == 902 TALER_MHD_reply_with_error (gc->connection, 903 MHD_HTTP_INTERNAL_SERVER_ERROR, 904 TALER_EC_GENERIC_DB_FETCH_FAILED, 905 "create_challenge_code (for rate limiting)")) 906 ? GNUNET_NO 907 : GNUNET_SYSERR; 908 } 909 if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs) 910 { 911 return (MHD_YES == 912 reply_rate_limited (gc)) 913 ? GNUNET_NO 914 : GNUNET_SYSERR; 915 } 916 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 917 "Using intentionally wrong answer to produce rate-limiting\n"); 918 /* decrement trial counter */ 919 ANASTASIS_hash_answer (code + 1, /* always use wrong answer */ 920 &hc); 921 cs = db->verify_challenge_code (db->cls, 922 &gc->truth_uuid, 923 &hc, 924 &dummy, 925 &satisfied); 926 switch (cs) 927 { 928 case ANASTASIS_DB_CODE_STATUS_CHALLENGE_CODE_MISMATCH: 929 /* good, what we wanted */ 930 return GNUNET_OK; 931 case ANASTASIS_DB_CODE_STATUS_HARD_ERROR: 932 case ANASTASIS_DB_CODE_STATUS_SOFT_ERROR: 933 GNUNET_break (0); 934 return (MHD_YES == 935 TALER_MHD_reply_with_error (gc->connection, 936 MHD_HTTP_INTERNAL_SERVER_ERROR, 937 TALER_EC_GENERIC_DB_FETCH_FAILED, 938 "verify_challenge_code")) 939 ? GNUNET_NO 940 : GNUNET_SYSERR; 941 case ANASTASIS_DB_CODE_STATUS_NO_RESULTS: 942 return (MHD_YES == 943 reply_rate_limited (gc)) 944 ? GNUNET_NO 945 : GNUNET_SYSERR; 946 case ANASTASIS_DB_CODE_STATUS_VALID_CODE_STORED: 947 /* this should be impossible, we used code+1 */ 948 GNUNET_assert (0); 949 } 950 return GNUNET_SYSERR; 951 } 952 953 954 /** 955 * Handle special case of a security question where we do not 956 * generate a code. Rate limits answers against brute forcing. 957 * 958 * @param[in,out] gc request to handle 959 * @param decrypted_truth hash to check against 960 * @param decrypted_truth_size number of bytes in @a decrypted_truth 961 * @return MHD status code 962 */ 963 static enum MHD_Result 964 handle_security_question (struct SolveContext *gc, 965 const void *decrypted_truth, 966 size_t decrypted_truth_size) 967 { 968 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 969 "Handling security question challenge\n"); 970 /* rate limit */ 971 { 972 enum GNUNET_GenericReturnValue ret; 973 974 ret = rate_limit (gc); 975 if (GNUNET_OK != ret) 976 return (GNUNET_NO == ret) ? MHD_YES : MHD_NO; 977 } 978 /* check reply matches truth */ 979 if ( (decrypted_truth_size != sizeof (struct GNUNET_HashCode)) || 980 (0 != memcmp (&gc->challenge_response, 981 decrypted_truth, 982 decrypted_truth_size)) ) 983 { 984 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 985 "Wrong answer provided to secure question had %u bytes, wanted %u\n", 986 (unsigned int) decrypted_truth_size, 987 (unsigned int) sizeof (struct GNUNET_HashCode)); 988 return TALER_MHD_reply_with_error (gc->connection, 989 MHD_HTTP_FORBIDDEN, 990 TALER_EC_ANASTASIS_TRUTH_CHALLENGE_FAILED, 991 NULL); 992 } 993 /* good, return the key share */ 994 return return_key_share (&gc->truth_uuid, 995 gc->connection); 996 } 997 998 999 /** 1000 * Handle special case of an answer being directly checked by the 1001 * plugin and not by our database. Also ensures that the 1002 * request is rate-limited. 1003 * 1004 * @param[in,out] gc request to handle 1005 * @param decrypted_truth hash to check against 1006 * @param decrypted_truth_size number of bytes in @a decrypted_truth 1007 * @return MHD status code 1008 */ 1009 static enum MHD_Result 1010 direct_validation (struct SolveContext *gc, 1011 const void *decrypted_truth, 1012 size_t decrypted_truth_size) 1013 { 1014 /* Non-random code, call plugin directly! */ 1015 enum ANASTASIS_AUTHORIZATION_SolveResult aar; 1016 enum GNUNET_GenericReturnValue ret; 1017 1018 ret = rate_limit (gc); 1019 if (GNUNET_OK != ret) 1020 return (GNUNET_NO == ret) ? MHD_YES : MHD_NO; 1021 gc->as = gc->authorization->start (gc->authorization->cls, 1022 &AH_trigger_daemon, 1023 NULL, 1024 &gc->truth_uuid, 1025 0LLU, 1026 decrypted_truth, 1027 decrypted_truth_size); 1028 if (NULL == gc->as) 1029 { 1030 GNUNET_break (0); 1031 return TALER_MHD_reply_with_error (gc->connection, 1032 MHD_HTTP_INTERNAL_SERVER_ERROR, 1033 TALER_EC_ANASTASIS_TRUTH_AUTHORIZATION_START_FAILED, 1034 NULL); 1035 } 1036 if (NULL == gc->authorization->solve) 1037 { 1038 GNUNET_break (0); 1039 return TALER_MHD_reply_with_error (gc->connection, 1040 MHD_HTTP_INTERNAL_SERVER_ERROR, 1041 TALER_EC_ANASTASIS_TRUTH_AUTHORIZATION_START_FAILED, 1042 "solve method not implemented for authorization method"); 1043 } 1044 aar = gc->authorization->solve (gc->as, 1045 gc->timeout, 1046 &gc->challenge_response, 1047 gc->connection); 1048 switch (aar) 1049 { 1050 case ANASTASIS_AUTHORIZATION_SRES_FAILED: 1051 return MHD_YES; 1052 case ANASTASIS_AUTHORIZATION_SRES_SUSPENDED: 1053 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1054 "Suspending request handling\n"); 1055 gc_suspended (gc); 1056 return MHD_YES; 1057 case ANASTASIS_AUTHORIZATION_SRES_FAILED_REPLY_FAILED: 1058 return MHD_NO; 1059 case ANASTASIS_AUTHORIZATION_SRES_FINISHED: 1060 return return_key_share (&gc->truth_uuid, 1061 gc->connection); 1062 } 1063 GNUNET_break (0); 1064 return MHD_NO; 1065 } 1066 1067 1068 /** 1069 * Handle special case of an answer being checked 1070 * by the plugin asynchronously (IBAN) after we inverted 1071 * the hash using the database. 1072 * 1073 * @param[in,out] gc request to handle 1074 * @param code validation code provided by the client 1075 * @param decrypted_truth hash to check against 1076 * @param decrypted_truth_size number of bytes in @a decrypted_truth 1077 * @return MHD status code 1078 */ 1079 static enum MHD_Result 1080 iban_validation (struct SolveContext *gc, 1081 uint64_t code, 1082 const void *decrypted_truth, 1083 size_t decrypted_truth_size) 1084 { 1085 enum ANASTASIS_AUTHORIZATION_SolveResult aar; 1086 1087 gc->as = gc->authorization->start (gc->authorization->cls, 1088 &AH_trigger_daemon, 1089 NULL, 1090 &gc->truth_uuid, 1091 code, 1092 decrypted_truth, 1093 decrypted_truth_size); 1094 if (NULL == gc->as) 1095 { 1096 GNUNET_break (0); 1097 return TALER_MHD_reply_with_error (gc->connection, 1098 MHD_HTTP_INTERNAL_SERVER_ERROR, 1099 TALER_EC_ANASTASIS_TRUTH_AUTHORIZATION_START_FAILED, 1100 NULL); 1101 } 1102 if (NULL == gc->authorization->solve) 1103 { 1104 GNUNET_break (0); 1105 return TALER_MHD_reply_with_error (gc->connection, 1106 MHD_HTTP_INTERNAL_SERVER_ERROR, 1107 TALER_EC_ANASTASIS_TRUTH_AUTHORIZATION_START_FAILED, 1108 "solve method not implemented for authorization method"); 1109 } 1110 aar = gc->authorization->solve (gc->as, 1111 gc->timeout, 1112 &gc->challenge_response, 1113 gc->connection); 1114 switch (aar) 1115 { 1116 case ANASTASIS_AUTHORIZATION_SRES_FAILED: 1117 return MHD_YES; 1118 case ANASTASIS_AUTHORIZATION_SRES_SUSPENDED: 1119 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1120 "Suspending request handling\n"); 1121 gc_suspended (gc); 1122 return MHD_YES; 1123 case ANASTASIS_AUTHORIZATION_SRES_FAILED_REPLY_FAILED: 1124 return MHD_NO; 1125 case ANASTASIS_AUTHORIZATION_SRES_FINISHED: 1126 return return_key_share (&gc->truth_uuid, 1127 gc->connection); 1128 } 1129 GNUNET_break (0); 1130 return MHD_NO; 1131 } 1132 1133 1134 enum MHD_Result 1135 AH_handler_truth_solve ( 1136 struct MHD_Connection *connection, 1137 struct TM_HandlerContext *hc, 1138 const struct ANASTASIS_CRYPTO_TruthUUIDP *truth_uuid, 1139 const char *upload_data, 1140 size_t *upload_data_size) 1141 { 1142 struct SolveContext *gc = hc->ctx; 1143 void *encrypted_truth; 1144 size_t encrypted_truth_size; 1145 void *decrypted_truth; 1146 size_t decrypted_truth_size; 1147 char *truth_mime = NULL; 1148 bool is_question; 1149 1150 if (NULL == gc) 1151 { 1152 /* Fresh request, do initial setup */ 1153 gc = GNUNET_new (struct SolveContext); 1154 gc->hc = hc; 1155 hc->ctx = gc; 1156 gc->connection = connection; 1157 gc->truth_uuid = *truth_uuid; 1158 gc->hc->cc = &request_done; 1159 gc->timeout = GNUNET_TIME_relative_to_absolute ( 1160 GNUNET_TIME_UNIT_SECONDS); 1161 TALER_MHD_parse_request_timeout (connection, 1162 &gc->timeout); 1163 } /* end of first-time initialization (if NULL == gc) */ 1164 else 1165 { 1166 /* might have been woken up by authorization plugin, 1167 so clear the flag. MDH called us, so we are 1168 clearly no longer suspended */ 1169 gc->suspended = false; 1170 if (NULL != gc->resp) 1171 { 1172 enum MHD_Result ret; 1173 1174 /* We generated a response asynchronously, queue that */ 1175 ret = MHD_queue_response (connection, 1176 gc->response_code, 1177 gc->resp); 1178 GNUNET_break (MHD_YES == ret); 1179 MHD_destroy_response (gc->resp); 1180 gc->resp = NULL; 1181 return ret; 1182 } 1183 if (NULL != gc->as) 1184 { 1185 /* Authorization process is "running", check what is going on */ 1186 GNUNET_assert (NULL != gc->authorization); 1187 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1188 "Continuing with running the authorization process\n"); 1189 GNUNET_assert (! gc->suspended); 1190 return run_authorization_process (connection, 1191 gc); 1192 } 1193 /* We get here if the async check for payment said this request 1194 was indeed paid! */ 1195 } 1196 1197 if (NULL == gc->root) 1198 { 1199 /* parse byte stream upload into JSON */ 1200 enum GNUNET_GenericReturnValue res; 1201 1202 res = TALER_MHD_parse_post_json (connection, 1203 &gc->opaque_post_parsing_context, 1204 upload_data, 1205 upload_data_size, 1206 &gc->root); 1207 if (GNUNET_SYSERR == res) 1208 { 1209 GNUNET_assert (NULL == gc->root); 1210 return MHD_NO; /* bad upload, could not even generate error */ 1211 } 1212 if ( (GNUNET_NO == res) || 1213 (NULL == gc->root) ) 1214 { 1215 GNUNET_assert (NULL == gc->root); 1216 return MHD_YES; /* so far incomplete upload or parser error */ 1217 } 1218 1219 /* 'root' is now initialized, parse JSON body */ 1220 { 1221 struct GNUNET_JSON_Specification spec[] = { 1222 GNUNET_JSON_spec_fixed_auto ("truth_decryption_key", 1223 &gc->truth_key), 1224 GNUNET_JSON_spec_fixed_auto ("h_response", 1225 &gc->challenge_response), 1226 GNUNET_JSON_spec_mark_optional ( 1227 GNUNET_JSON_spec_fixed_auto ("payment_secret", 1228 &gc->payment_identifier), 1229 &gc->no_payment_identifier_provided), 1230 GNUNET_JSON_spec_end () 1231 }; 1232 enum GNUNET_GenericReturnValue pres; 1233 1234 pres = TALER_MHD_parse_json_data (connection, 1235 gc->root, 1236 spec); 1237 if (GNUNET_SYSERR == pres) 1238 { 1239 GNUNET_break (0); 1240 return MHD_NO; /* hard failure */ 1241 } 1242 if (GNUNET_NO == pres) 1243 { 1244 GNUNET_break_op (0); 1245 return MHD_YES; /* failure */ 1246 } 1247 if (! gc->no_payment_identifier_provided) 1248 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1249 "Client provided payment identifier `%s'\n", 1250 TALER_B2S (&gc->payment_identifier)); 1251 } 1252 } 1253 1254 { 1255 /* load encrypted truth from DB; we may do this repeatedly 1256 while handling the same request, if payment was checked 1257 asynchronously! */ 1258 enum GNUNET_DB_QueryStatus qs; 1259 char *method; 1260 1261 qs = db->get_escrow_challenge (db->cls, 1262 &gc->truth_uuid, 1263 &encrypted_truth, 1264 &encrypted_truth_size, 1265 &truth_mime, 1266 &method); 1267 switch (qs) 1268 { 1269 case GNUNET_DB_STATUS_HARD_ERROR: 1270 case GNUNET_DB_STATUS_SOFT_ERROR: 1271 GNUNET_break (0); 1272 return TALER_MHD_reply_with_error (gc->connection, 1273 MHD_HTTP_INTERNAL_SERVER_ERROR, 1274 TALER_EC_GENERIC_DB_FETCH_FAILED, 1275 "get escrow challenge"); 1276 case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: 1277 return TALER_MHD_reply_with_error (connection, 1278 MHD_HTTP_NOT_FOUND, 1279 TALER_EC_ANASTASIS_TRUTH_UNKNOWN, 1280 NULL); 1281 case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: 1282 break; 1283 } 1284 is_question = (0 == strcmp ("question", 1285 method)); 1286 if (! is_question) 1287 { 1288 gc->authorization 1289 = ANASTASIS_authorization_plugin_load (method, 1290 db, 1291 AH_cfg); 1292 if (NULL == gc->authorization) 1293 { 1294 enum MHD_Result ret; 1295 1296 ret = TALER_MHD_reply_with_error ( 1297 connection, 1298 MHD_HTTP_INTERNAL_SERVER_ERROR, 1299 TALER_EC_ANASTASIS_TRUTH_AUTHORIZATION_METHOD_NO_LONGER_SUPPORTED, 1300 method); 1301 GNUNET_free (encrypted_truth); 1302 GNUNET_free (truth_mime); 1303 GNUNET_free (method); 1304 return ret; 1305 } 1306 gc->challenge_cost = gc->authorization->cost; 1307 } 1308 else 1309 { 1310 gc->challenge_cost = AH_question_cost; 1311 } 1312 GNUNET_free (method); 1313 } 1314 1315 /* check for payment */ 1316 if ( (is_question) || 1317 (! gc->authorization->payment_plugin_managed) ) 1318 { 1319 if (! TALER_amount_is_zero (&gc->challenge_cost)) 1320 { 1321 /* Check database to see if the transaction is paid for */ 1322 enum GNUNET_DB_QueryStatus qs; 1323 bool paid; 1324 1325 if (gc->no_payment_identifier_provided) 1326 { 1327 GNUNET_free (truth_mime); 1328 GNUNET_free (encrypted_truth); 1329 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1330 "Beginning payment, client did not provide payment identifier\n"); 1331 return begin_payment (gc); 1332 } 1333 qs = db->check_challenge_payment (db->cls, 1334 &gc->payment_identifier, 1335 &gc->truth_uuid, 1336 &paid); 1337 switch (qs) 1338 { 1339 case GNUNET_DB_STATUS_HARD_ERROR: 1340 case GNUNET_DB_STATUS_SOFT_ERROR: 1341 GNUNET_break (0); 1342 GNUNET_free (truth_mime); 1343 GNUNET_free (encrypted_truth); 1344 return TALER_MHD_reply_with_error (gc->connection, 1345 MHD_HTTP_INTERNAL_SERVER_ERROR, 1346 TALER_EC_GENERIC_DB_FETCH_FAILED, 1347 "check challenge payment"); 1348 case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: 1349 /* Create fresh payment identifier (cannot trust client) */ 1350 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1351 "Client-provided payment identifier is unknown.\n"); 1352 GNUNET_free (truth_mime); 1353 GNUNET_free (encrypted_truth); 1354 return begin_payment (gc); 1355 case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: 1356 if (! paid) 1357 { 1358 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1359 "Payment identifier known. Checking payment with client's payment identifier\n"); 1360 GNUNET_free (truth_mime); 1361 GNUNET_free (encrypted_truth); 1362 return begin_payment (gc); 1363 } 1364 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1365 "Payment confirmed\n"); 1366 break; 1367 } 1368 } 1369 else 1370 { 1371 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1372 "Request is free of charge\n"); 1373 } 1374 } 1375 1376 /* We've been paid, now validate the response */ 1377 /* decrypt encrypted_truth */ 1378 ANASTASIS_CRYPTO_truth_decrypt (&gc->truth_key, 1379 encrypted_truth, 1380 encrypted_truth_size, 1381 &decrypted_truth, 1382 &decrypted_truth_size); 1383 GNUNET_free (encrypted_truth); 1384 if (NULL == decrypted_truth) 1385 { 1386 /* most likely, the decryption key is simply wrong */ 1387 GNUNET_break_op (0); 1388 GNUNET_free (truth_mime); 1389 return TALER_MHD_reply_with_error (connection, 1390 MHD_HTTP_BAD_REQUEST, 1391 TALER_EC_ANASTASIS_TRUTH_DECRYPTION_FAILED, 1392 NULL); 1393 } 1394 1395 /* Special case for secure question: we do not generate a numeric challenge, 1396 but check that the hash matches */ 1397 if (is_question) 1398 { 1399 enum MHD_Result ret; 1400 1401 ret = handle_security_question (gc, 1402 decrypted_truth, 1403 decrypted_truth_size); 1404 GNUNET_free (truth_mime); 1405 GNUNET_free (decrypted_truth); 1406 return ret; 1407 } 1408 1409 /* Not security question, check for answer in DB */ 1410 { 1411 enum ANASTASIS_DB_CodeStatus cs; 1412 bool satisfied = false; 1413 uint64_t code; 1414 1415 GNUNET_free (truth_mime); 1416 if (gc->authorization->user_provided_code) 1417 { 1418 enum MHD_Result res; 1419 1420 if (GNUNET_TIME_absolute_is_past (gc->timeout)) 1421 { 1422 GNUNET_free (decrypted_truth); 1423 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1424 "Timeout with user provided code\n"); 1425 return TALER_MHD_reply_with_error (connection, 1426 MHD_HTTP_FORBIDDEN, 1427 TALER_EC_ANASTASIS_IBAN_MISSING_TRANSFER, 1428 "timeout awaiting validation"); 1429 } 1430 res = direct_validation (gc, 1431 decrypted_truth, 1432 decrypted_truth_size); 1433 GNUNET_free (decrypted_truth); 1434 return res; 1435 } 1436 1437 /* random code, check against database */ 1438 cs = db->verify_challenge_code (db->cls, 1439 &gc->truth_uuid, 1440 &gc->challenge_response, 1441 &code, 1442 &satisfied); 1443 switch (cs) 1444 { 1445 case ANASTASIS_DB_CODE_STATUS_CHALLENGE_CODE_MISMATCH: 1446 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 1447 "Provided response does not match our stored challenge\n"); 1448 GNUNET_free (decrypted_truth); 1449 return TALER_MHD_reply_with_error (connection, 1450 MHD_HTTP_FORBIDDEN, 1451 TALER_EC_ANASTASIS_TRUTH_CHALLENGE_FAILED, 1452 NULL); 1453 case ANASTASIS_DB_CODE_STATUS_HARD_ERROR: 1454 case ANASTASIS_DB_CODE_STATUS_SOFT_ERROR: 1455 GNUNET_break (0); 1456 GNUNET_free (decrypted_truth); 1457 return TALER_MHD_reply_with_error (gc->connection, 1458 MHD_HTTP_INTERNAL_SERVER_ERROR, 1459 TALER_EC_GENERIC_DB_FETCH_FAILED, 1460 "verify_challenge_code"); 1461 case ANASTASIS_DB_CODE_STATUS_NO_RESULTS: 1462 GNUNET_free (decrypted_truth); 1463 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1464 "Specified challenge code %s was not issued\n", 1465 GNUNET_h2s (&gc->challenge_response)); 1466 return TALER_MHD_reply_with_error (connection, 1467 MHD_HTTP_FORBIDDEN, 1468 TALER_EC_ANASTASIS_TRUTH_CHALLENGE_UNKNOWN, 1469 "specific challenge code was not issued"); 1470 case ANASTASIS_DB_CODE_STATUS_VALID_CODE_STORED: 1471 if (! satisfied) 1472 { 1473 enum MHD_Result res; 1474 1475 res = iban_validation (gc, 1476 code, 1477 decrypted_truth, 1478 decrypted_truth_size); 1479 GNUNET_free (decrypted_truth); 1480 return res; 1481 } 1482 GNUNET_free (decrypted_truth); 1483 return return_key_share (&gc->truth_uuid, 1484 connection); 1485 default: 1486 GNUNET_break (0); 1487 return MHD_NO; 1488 } 1489 } 1490 }