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