challenger-httpd_challenge.c (28252B)
1 /* 2 This file is part of Challenger 3 Copyright (C) 2023, 2024 Taler Systems SA 4 5 Challenger 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 Challenger 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 Challenger; see the file COPYING. If not, see <http://www.gnu.org/licenses/> 15 */ 16 /** 17 * @file challenger-httpd_challenge.c 18 * @brief functions to handle incoming /challenge requests 19 * @author Christian Grothoff 20 */ 21 #include "platform.h" 22 #include "challenger-httpd.h" 23 #include <regex.h> 24 #include <gnunet/gnunet_util_lib.h> 25 #include <gnunet/gnunet_pq_lib.h> 26 #include "challenger-httpd_challenge.h" 27 #include <taler/taler_json_lib.h> 28 #include <taler/taler_templating_lib.h> 29 #include <taler/taler_signatures.h> 30 #include "challenger-database/challenge_set_address_and_pin.h" 31 #include "challenger-database/address_get.h" 32 #include "challenger-httpd_common.h" 33 34 /** 35 * Maximum number of retries for the database interaction. 36 */ 37 #define MAX_RETRIES 3 38 39 /** 40 * Set to 1 to dump addresses into the log. 41 */ 42 #define DEBUG 0 43 44 /** 45 * Context for a /challenge operation. 46 */ 47 struct ChallengeContext 48 { 49 50 /** 51 * Nonce of the operation. 52 */ 53 struct CHALLENGER_ValidationNonceP nonce; 54 55 /** 56 * Kept in a DLL. 57 */ 58 struct ChallengeContext *next; 59 60 /** 61 * Kept in a DLL. 62 */ 63 struct ChallengeContext *prev; 64 65 /** 66 * Our handler context. 67 */ 68 struct CH_HandlerContext *hc; 69 70 /** 71 * Handle to the helper process. 72 */ 73 struct GNUNET_Process *child; 74 75 /** 76 * Handle to wait for @e child 77 */ 78 struct GNUNET_ChildWaitHandle *cwh; 79 80 /** 81 * Handle for processing uploaded data. 82 */ 83 struct MHD_PostProcessor *pp; 84 85 /** 86 * Where we store the collected address data. 87 */ 88 json_t *address; 89 90 /** 91 * Last key during POST processing. 92 */ 93 char *last_key; 94 95 /** 96 * Uploaded data during POST processing. 97 */ 98 char *data; 99 100 /** 101 * Where to redirect the client on errors? 102 */ 103 char *client_redirect_uri; 104 105 /** 106 * OAuth2 state. 107 */ 108 char *state; 109 110 /** 111 * Buffer used by #TALER_MHD_parse_post_json(). Or NULL. 112 */ 113 void *jbuffer; 114 115 /** 116 * When did we transmit last? 117 */ 118 struct GNUNET_TIME_Absolute last_tx_time; 119 120 /** 121 * Exit code from helper. 122 */ 123 unsigned long int exit_code; 124 125 /** 126 * Number of bytes in @a address, excluding 0-terminator. 127 */ 128 size_t data_len; 129 130 /** 131 * Our tan. 132 */ 133 uint32_t tan; 134 135 /** 136 * How many attempts does the user have left? 137 */ 138 uint32_t pin_attempts_left; 139 140 /** 141 * How did the helper die? 142 */ 143 enum GNUNET_OS_ProcessStatusType pst; 144 145 /** 146 * Connection status. #GNUNET_OK to continue 147 * normally, #GNUNET_NO if an error was already 148 * returned, #GNUNET_SYSERR if we failed to 149 * return an error and should just return #MHD_NO. 150 */ 151 enum GNUNET_GenericReturnValue status; 152 153 /** 154 * #GNUNET_YES if we are suspended in #bc_head, 155 * #GNUNET_NO if operating normally, 156 * #GNUNET_SYSERR if resumed by shutdown (end with #MHD_NO) 157 */ 158 enum GNUNET_GenericReturnValue suspended; 159 160 /** 161 * True if the provided address was refused, usually because 162 * the user tried too many different addresses already. 163 */ 164 bool address_refused; 165 166 /** 167 * Should we retransmit the PIN? 168 */ 169 bool retransmit; 170 171 /** 172 * Is the challenge already solved? 173 */ 174 bool solved; 175 176 /** 177 * Did we do the DB interaction? 178 */ 179 bool db_finished; 180 181 /** 182 * Is the upload in JSON? 183 */ 184 bool is_json; 185 }; 186 187 188 /** 189 * Head of suspended challenger contexts. 190 */ 191 struct ChallengeContext *bc_head; 192 193 /** 194 * Tail of suspended challenger contexts. 195 */ 196 struct ChallengeContext *bc_tail; 197 198 199 void 200 CH_wakeup_challenge_on_shutdown () 201 { 202 struct ChallengeContext *bc; 203 204 while (NULL != (bc = bc_head)) 205 { 206 GNUNET_CONTAINER_DLL_remove (bc_head, 207 bc_tail, 208 bc); 209 MHD_resume_connection (bc->hc->connection); 210 bc->suspended = GNUNET_SYSERR; 211 } 212 } 213 214 215 /** 216 * Function called to clean up a backup context. 217 * 218 * @param cls a `struct ChallengeContext` 219 */ 220 static void 221 cleanup_ctx (void *cls) 222 { 223 struct ChallengeContext *bc = cls; 224 225 if (NULL != bc->pp) 226 { 227 GNUNET_break_op (MHD_YES == 228 MHD_destroy_post_processor (bc->pp)); 229 } 230 if (NULL != bc->cwh) 231 { 232 GNUNET_wait_child_cancel (bc->cwh); 233 bc->cwh = NULL; 234 } 235 if (NULL != bc->child) 236 { 237 GNUNET_break (GNUNET_OK == 238 GNUNET_process_kill (bc->child, 239 SIGKILL)); 240 GNUNET_break (GNUNET_OK == 241 GNUNET_process_wait (bc->child, 242 true, 243 NULL, 244 NULL)); 245 GNUNET_process_destroy (bc->child); 246 bc->child = NULL; 247 } 248 TALER_MHD_parse_post_cleanup_callback (bc->jbuffer); 249 json_decref (bc->address); 250 GNUNET_free (bc->data); 251 GNUNET_free (bc->state); 252 GNUNET_free (bc->last_key); 253 GNUNET_free (bc->client_redirect_uri); 254 GNUNET_free (bc); 255 } 256 257 258 /** 259 * Generate error reply in the format requested by 260 * the client. 261 * 262 * @param bc our context 263 * @param template error template to use 264 * @param http_status HTTP status to return 265 * @param ec error code to return 266 * @param hint human-readable hint to give 267 */ 268 static enum MHD_Result 269 reply_error (struct ChallengeContext *bc, 270 const char *template, 271 unsigned int http_status, 272 enum TALER_ErrorCode ec, 273 const char *hint) 274 { 275 struct CH_HandlerContext *hc = bc->hc; 276 277 return TALER_MHD_reply_with_error ( 278 hc->connection, 279 http_status, 280 ec, 281 hint); 282 } 283 284 285 /** 286 * Function called when our PIN transmission helper has terminated. 287 * 288 * @param cls our `struct ChallengeContext *` 289 * @param type type of the process 290 * @param exit_code status code of the process 291 */ 292 static void 293 child_done_cb (void *cls, 294 enum GNUNET_OS_ProcessStatusType type, 295 long unsigned int exit_code) 296 { 297 struct ChallengeContext *bc = cls; 298 struct GNUNET_AsyncScopeSave old_scope; 299 300 GNUNET_async_scope_enter (&bc->hc->async_scope_id, 301 &old_scope); 302 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 303 "Child done with exit code %d/%llu\n", 304 (int) type, 305 (unsigned long long) exit_code); 306 GNUNET_process_destroy (bc->child); 307 bc->child = NULL; 308 bc->cwh = NULL; 309 bc->pst = type; 310 bc->exit_code = exit_code; 311 bc->suspended = GNUNET_NO; 312 MHD_resume_connection (bc->hc->connection); 313 GNUNET_CONTAINER_DLL_remove (bc_head, 314 bc_tail, 315 bc); 316 CH_trigger_daemon (); 317 GNUNET_async_scope_restore (&old_scope); 318 } 319 320 321 /** 322 * Transmit the TAN to the given address. 323 * 324 * @param[in,out] bc context to submit TAN for 325 */ 326 static void 327 send_tan (struct ChallengeContext *bc) 328 { 329 struct GNUNET_DISK_PipeHandle *p; 330 struct GNUNET_DISK_FileHandle *pipe_stdin; 331 void *msg; 332 size_t msg_len; 333 334 p = GNUNET_DISK_pipe (GNUNET_DISK_PF_BLOCKING_RW); 335 if (NULL == p) 336 { 337 enum MHD_Result mres; 338 339 GNUNET_break (0); 340 mres = TALER_MHD_reply_with_error ( 341 bc->hc->connection, 342 MHD_HTTP_BAD_GATEWAY, 343 TALER_EC_CHALLENGER_HELPER_EXEC_FAILED, 344 "pipe"); 345 bc->status = (MHD_YES == mres) 346 ? GNUNET_NO 347 : GNUNET_SYSERR; 348 return; 349 } 350 { 351 char *address; 352 353 address = json_dumps (bc->address, 354 JSON_COMPACT); 355 #if DEBUG 356 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 357 "Running auth command `%s' on address `%s'\n", 358 CH_auth_command, 359 address); 360 #endif 361 bc->child = GNUNET_process_create (GNUNET_OS_INHERIT_STD_ERR); 362 GNUNET_assert (GNUNET_OK == 363 GNUNET_process_set_options ( 364 bc->child, 365 GNUNET_process_option_inherit_rpipe (p, 366 STDIN_FILENO))); 367 if (GNUNET_OK != 368 GNUNET_process_run_command_va (bc->child, 369 CH_auth_command, 370 CH_auth_command, 371 address, 372 NULL)) 373 { 374 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, 375 "exec"); 376 GNUNET_process_destroy (bc->child); 377 bc->child = NULL; 378 } 379 free (address); 380 } 381 if (NULL == bc->child) 382 { 383 enum MHD_Result mres; 384 385 GNUNET_break (0); 386 GNUNET_break (GNUNET_OK == 387 GNUNET_DISK_pipe_close (p)); 388 mres = TALER_MHD_reply_with_error ( 389 bc->hc->connection, 390 MHD_HTTP_BAD_GATEWAY, 391 TALER_EC_CHALLENGER_HELPER_EXEC_FAILED, 392 "exec"); 393 bc->status = (MHD_YES == mres) 394 ? GNUNET_NO 395 : GNUNET_SYSERR; 396 return; 397 } 398 pipe_stdin = GNUNET_DISK_pipe_detach_end (p, 399 GNUNET_DISK_PIPE_END_WRITE); 400 GNUNET_assert (NULL != pipe_stdin); 401 GNUNET_break (GNUNET_OK == 402 GNUNET_DISK_pipe_close (p)); 403 if (0 != CH_message_template_len) 404 { 405 json_t *root; 406 int mret; 407 408 root = GNUNET_JSON_PACK ( 409 GNUNET_JSON_pack_object_incref ("address", 410 bc->address), 411 GNUNET_JSON_pack_int64 ("pin", 412 bc->tan)); 413 mret = TALER_TEMPLATING_fill2 (CH_message_template, 414 CH_message_template_len, 415 root, 416 &msg, 417 &msg_len); 418 json_decref (root); 419 if (0 != mret) 420 { 421 enum MHD_Result mres; 422 423 GNUNET_break (0); 424 mres = TALER_MHD_reply_with_error ( 425 bc->hc->connection, 426 MHD_HTTP_INTERNAL_SERVER_ERROR, 427 TALER_EC_GENERIC_FAILED_TO_EXPAND_TEMPLATE, 428 NULL); 429 GNUNET_DISK_file_close (pipe_stdin); 430 bc->status = (MHD_YES == mres) 431 ? GNUNET_NO 432 : GNUNET_SYSERR; 433 return; 434 } 435 } 436 else 437 { 438 char *cmsg; 439 440 GNUNET_asprintf (&cmsg, 441 "PIN: %u", 442 (unsigned int) bc->tan); 443 msg_len = strlen (cmsg); 444 msg = cmsg; 445 } 446 { 447 const char *off = msg; 448 size_t left = msg_len; 449 450 while (0 != left) 451 { 452 ssize_t ret; 453 454 ret = GNUNET_DISK_file_write (pipe_stdin, 455 off, 456 left); 457 if (ret <= 0) 458 { 459 enum MHD_Result mres; 460 461 GNUNET_break (0); 462 mres = TALER_MHD_reply_with_error ( 463 bc->hc->connection, 464 MHD_HTTP_BAD_GATEWAY, 465 TALER_EC_CHALLENGER_HELPER_EXEC_FAILED, 466 "write"); 467 GNUNET_DISK_file_close (pipe_stdin); 468 GNUNET_free (msg); 469 bc->status = (MHD_YES == mres) 470 ? GNUNET_NO 471 : GNUNET_SYSERR; 472 return; 473 } 474 off += (size_t) ret; 475 left -= (size_t) ret; 476 } 477 GNUNET_DISK_file_close (pipe_stdin); 478 } 479 GNUNET_free (msg); 480 bc->cwh = GNUNET_wait_child (bc->child, 481 &child_done_cb, 482 bc); 483 MHD_suspend_connection (bc->hc->connection); 484 bc->suspended = GNUNET_YES; 485 GNUNET_CONTAINER_DLL_insert (bc_head, 486 bc_tail, 487 bc); 488 } 489 490 491 /** 492 * Iterator over key-value pairs where the value may be made available 493 * in increments and/or may not be zero-terminated. Used for 494 * processing POST data. 495 * 496 * @param cls a `struct ChallengeContext *` 497 * @param kind type of the value, always #MHD_POSTDATA_KIND when called from MHD 498 * @param key 0-terminated key for the value 499 * @param filename name of the uploaded file, NULL if not known 500 * @param content_type mime-type of the data, NULL if not known 501 * @param transfer_encoding encoding of the data, NULL if not known 502 * @param data pointer to @a size bytes of data at the 503 * specified offset 504 * @param off offset of data in the overall value 505 * @param size number of bytes in @a data available 506 * @return #MHD_YES to continue iterating, 507 * #MHD_NO to abort the iteration 508 */ 509 static enum MHD_Result 510 post_iter (void *cls, 511 enum MHD_ValueKind kind, 512 const char *key, 513 const char *filename, 514 const char *content_type, 515 const char *transfer_encoding, 516 const char *data, 517 uint64_t off, 518 size_t size) 519 { 520 struct ChallengeContext *bc = cls; 521 522 (void) filename; 523 (void) content_type; 524 (void) transfer_encoding; 525 (void) off; 526 if (MHD_POSTDATA_KIND != kind) 527 return MHD_YES; 528 if ( (NULL != bc->last_key) && 529 (0 != strcmp (key, 530 bc->last_key)) ) 531 { 532 GNUNET_assert (0 == 533 json_object_set_new (bc->address, 534 bc->last_key, 535 json_string (bc->data))); 536 GNUNET_free (bc->data); 537 bc->data_len = 0; 538 GNUNET_free (bc->last_key); 539 } 540 if (NULL == bc->last_key) 541 { 542 bc->last_key = GNUNET_strdup (key); 543 } 544 bc->data = GNUNET_realloc (bc->data, 545 bc->data_len + size + 1); 546 memcpy (bc->data + bc->data_len, 547 data, 548 size); 549 bc->data_len += size; 550 bc->data[bc->data_len] = '\0'; 551 return MHD_YES; 552 } 553 554 555 /** 556 * Check if the given address satisfies our restrictions. 557 * 558 * @param address address data provided by the client 559 * @return NULL on success, otherwise the key that failed 560 */ 561 static const char * 562 check_restrictions (const json_t *address) 563 { 564 const char *key; 565 const json_t *val; 566 567 json_object_foreach ((json_t *) address, key, val) 568 { 569 const char *str = json_string_value (val); 570 const char *regex = json_string_value ( 571 json_object_get ( 572 json_object_get (CH_restrictions, 573 key), 574 "regex")); 575 regex_t re; 576 577 if (0 == strcasecmp (key, 578 "read_only")) 579 continue; 580 if (NULL == str) 581 return key; 582 if (NULL == regex) 583 continue; 584 if (0 != regcomp (&re, 585 regex, 586 REG_EXTENDED)) 587 { 588 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 589 "Invalid regex `%s' address restriction specified for `%s'\n", 590 regex, 591 key); 592 continue; 593 } 594 if (0 != regexec (&re, 595 str, 596 0, 597 NULL, 598 0)) 599 { 600 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 601 "Client input `%s' rejected as it does not match address restriction `%s' specified for `%s'\n", 602 str, 603 regex, 604 key); 605 return key; 606 } 607 regfree (&re); 608 } 609 return NULL; 610 } 611 612 613 enum MHD_Result 614 CH_handler_challenge (struct CH_HandlerContext *hc, 615 const char *upload_data, 616 size_t *upload_data_size) 617 { 618 struct ChallengeContext *bc = hc->ctx; 619 620 if (NULL == bc) 621 { 622 /* first call, setup internals */ 623 bc = GNUNET_new (struct ChallengeContext); 624 bc->status = GNUNET_OK; 625 bc->hc = hc; 626 hc->cc = &cleanup_ctx; 627 hc->ctx = bc; 628 bc->pst = GNUNET_OS_PROCESS_UNKNOWN; 629 bc->tan 630 = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_NONCE, 631 100000000); 632 bc->pp = MHD_create_post_processor (hc->connection, 633 1024, 634 &post_iter, 635 bc); 636 if (GNUNET_OK != 637 GNUNET_STRINGS_string_to_data (hc->path, 638 strlen (hc->path), 639 &bc->nonce, 640 sizeof (bc->nonce))) 641 { 642 GNUNET_break_op (0); 643 return TALER_MHD_reply_with_error ( 644 hc->connection, 645 MHD_HTTP_NOT_FOUND, 646 TALER_EC_GENERIC_PARAMETER_MISSING, 647 hc->path); 648 } 649 { 650 const char *ct; 651 652 ct = MHD_lookup_connection_value (hc->connection, 653 MHD_HEADER_KIND, 654 MHD_HTTP_HEADER_CONTENT_TYPE); 655 bc->is_json = ( (NULL != ct) && 656 (0 == strcasecmp (ct, 657 "application/json")) ); 658 if (! bc->is_json) 659 bc->address = json_object (); 660 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 661 "Processing /challenge upload with %s encoding...\n", 662 ct); 663 } 664 TALER_MHD_check_content_length (hc->connection, 665 1024); 666 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 667 "Awaiting /challenge upload (%u)...\n", 668 (unsigned int) *upload_data_size); 669 return MHD_YES; 670 } 671 GNUNET_assert (GNUNET_YES != bc->suspended); 672 if (GNUNET_SYSERR == bc->suspended) 673 { 674 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 675 "/challenge ends in shutdown\n"); 676 return MHD_NO; 677 } 678 /* Handle case where helper process failed */ 679 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 680 "Checking child process status: %d / %d (%s)\n", 681 (int) bc->pst, 682 (int) bc->exit_code, 683 NULL == bc->child ? "dead" : "running"); 684 if ( (GNUNET_OS_PROCESS_UNKNOWN != bc->pst) && 685 (NULL == bc->child) && 686 ( (GNUNET_OS_PROCESS_EXITED != bc->pst) || 687 (0 != bc->exit_code) ) ) 688 { 689 char es[32]; 690 691 GNUNET_break (0); 692 GNUNET_snprintf (es, 693 sizeof (es), 694 "%u/%d", 695 (unsigned int) bc->exit_code, 696 bc->pst); 697 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 698 "Helper failed with status %d/%d\n", 699 (int) bc->pst, 700 (int) bc->exit_code); 701 return TALER_MHD_reply_with_error ( 702 hc->connection, 703 MHD_HTTP_BAD_GATEWAY, 704 TALER_EC_CHALLENGER_HELPER_EXEC_FAILED, 705 es); 706 } 707 /* handle upload */ 708 if (bc->is_json) 709 { 710 if (NULL == bc->address) 711 { 712 enum GNUNET_GenericReturnValue res; 713 714 res = TALER_MHD_parse_post_json (hc->connection, 715 &bc->jbuffer, 716 upload_data, 717 upload_data_size, 718 &bc->address); 719 if (GNUNET_SYSERR != res) 720 return MHD_YES; 721 GNUNET_break (0); 722 return MHD_NO; 723 } 724 else 725 { 726 GNUNET_break (0 == *upload_data_size); 727 } 728 } 729 else if (0 != *upload_data_size) 730 { 731 enum MHD_Result res; 732 733 res = MHD_post_process (bc->pp, 734 upload_data, 735 *upload_data_size); 736 *upload_data_size = 0; 737 if (MHD_YES == res) 738 return MHD_YES; 739 GNUNET_break (0); 740 return MHD_NO; 741 } 742 if (NULL != bc->last_key) 743 { 744 GNUNET_assert (0 == 745 json_object_set_new (bc->address, 746 bc->last_key, 747 json_string (bc->data))); 748 GNUNET_free (bc->data); 749 bc->data_len = 0; 750 GNUNET_free (bc->last_key); 751 } 752 #if DEBUG 753 { 754 char *address; 755 756 address = json_dumps (bc->address, 757 JSON_COMPACT); 758 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 759 "Submitted address is `%s'\n", 760 address); 761 free (address); 762 } 763 #endif 764 { 765 const char *bad_field; 766 767 bad_field = check_restrictions (bc->address); 768 if (NULL != bad_field) 769 { 770 GNUNET_break_op (0); 771 return reply_error (bc, 772 "invalid-request", 773 MHD_HTTP_BAD_REQUEST, 774 TALER_EC_GENERIC_PARAMETER_MALFORMED, 775 bad_field); 776 } 777 } 778 if (! bc->db_finished) 779 { 780 for (unsigned int r = 0; r < MAX_RETRIES; r++) 781 { 782 enum GNUNET_DB_QueryStatus qs; 783 json_t *old_address; 784 const json_t *ro; 785 786 GNUNET_assert (NULL == bc->client_redirect_uri); 787 qs = CHALLENGERDB_address_get (CH_context, 788 &bc->nonce, 789 &old_address); 790 switch (qs) 791 { 792 case GNUNET_DB_STATUS_HARD_ERROR: 793 GNUNET_break (0); 794 return reply_error (bc, 795 "internal-error", 796 MHD_HTTP_INTERNAL_SERVER_ERROR, 797 TALER_EC_GENERIC_DB_FETCH_FAILED, 798 "validation-get"); 799 case GNUNET_DB_STATUS_SOFT_ERROR: 800 if (r < MAX_RETRIES - 1) 801 continue; 802 GNUNET_break (0); 803 return reply_error (bc, 804 "internal-error", 805 MHD_HTTP_INTERNAL_SERVER_ERROR, 806 TALER_EC_GENERIC_DB_FETCH_FAILED, 807 "validation-get"); 808 case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: 809 GNUNET_break_op (0); 810 return reply_error (bc, 811 "validation-unknown", 812 MHD_HTTP_NOT_FOUND, 813 TALER_EC_CHALLENGER_GENERIC_VALIDATION_UNKNOWN, 814 NULL); 815 case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: 816 break; 817 } 818 ro = json_object_get (old_address, 819 "read_only"); 820 if ( (NULL != ro) && 821 (json_boolean_value (ro)) && 822 (1 != json_equal (old_address, 823 bc->address)) ) 824 { 825 GNUNET_break_op (0); 826 json_decref (old_address); 827 return reply_error (bc, 828 "address-read-only", 829 MHD_HTTP_FORBIDDEN, 830 TALER_EC_CHALLENGER_CLIENT_FORBIDDEN_READ_ONLY, 831 NULL); 832 } 833 json_decref (old_address); 834 835 qs = CHALLENGERDB_challenge_set_address_and_pin (CH_context, 836 &bc->nonce, 837 bc->address, 838 CH_validation_duration, 839 &bc->tan, 840 &bc->state, 841 &bc->last_tx_time, 842 &bc->pin_attempts_left, 843 &bc->retransmit, 844 &bc->client_redirect_uri, 845 &bc->address_refused, 846 &bc->solved); 847 switch (qs) 848 { 849 case GNUNET_DB_STATUS_HARD_ERROR: 850 GNUNET_break (0); 851 return reply_error (bc, 852 "internal-error", 853 MHD_HTTP_INTERNAL_SERVER_ERROR, 854 TALER_EC_GENERIC_DB_STORE_FAILED, 855 "set-address-and-pin"); 856 case GNUNET_DB_STATUS_SOFT_ERROR: 857 if (r < MAX_RETRIES - 1) 858 continue; 859 GNUNET_break (0); 860 return reply_error (bc, 861 "internal-error", 862 MHD_HTTP_INTERNAL_SERVER_ERROR, 863 TALER_EC_GENERIC_DB_STORE_FAILED, 864 "set-address-and-pin"); 865 case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: 866 GNUNET_break_op (0); 867 return reply_error (bc, 868 "validation-unknown", 869 MHD_HTTP_NOT_FOUND, 870 TALER_EC_CHALLENGER_GENERIC_VALIDATION_UNKNOWN, 871 NULL); 872 case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: 873 break; 874 } 875 break; 876 } 877 bc->db_finished = true; 878 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 879 "Database interaction done (challenge %s)\n", 880 bc->solved ? "solved" : "unsolved"); 881 if (bc->solved) 882 { 883 enum GNUNET_GenericReturnValue ret; 884 char *url; 885 struct MHD_Response *response; 886 json_t *args; 887 888 ret = CH_build_full_redirect_url (&bc->nonce, 889 hc->connection, 890 &url); 891 if (GNUNET_OK != ret) 892 return (GNUNET_NO == ret) ? MHD_YES : MHD_NO; 893 args = GNUNET_JSON_PACK ( 894 GNUNET_JSON_pack_string ("type", 895 "completed"), 896 GNUNET_JSON_pack_string ("redirect_url", 897 url) 898 ); 899 GNUNET_free (url); 900 response = TALER_MHD_make_json (args); 901 ret = MHD_queue_response (hc->connection, 902 MHD_HTTP_OK, 903 response); 904 MHD_destroy_response (response); 905 return ret; 906 } 907 if (bc->address_refused) 908 { 909 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 910 "Address changes exhausted address change limit for this process\n"); 911 return reply_error (bc, 912 "unauthorized_client", 913 MHD_HTTP_TOO_MANY_REQUESTS, 914 TALER_EC_CHALLENGER_TOO_MANY_ATTEMPTS, 915 "client exceeded authorization attempts limit (too many addresses attempted)"); 916 917 } 918 if (0 == bc->pin_attempts_left) 919 { 920 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 921 "Address changes exhausted PIN limit for this address\n"); 922 return reply_error (bc, 923 "unauthorized_client", 924 MHD_HTTP_TOO_MANY_REQUESTS, 925 TALER_EC_CHALLENGER_TOO_MANY_ATTEMPTS, 926 "client exceeded authorization attempts limit (too many PINs)"); 927 } 928 929 if (bc->retransmit) 930 { 931 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 932 "Transmitting PIN\n"); 933 /* (Re)transmit PIN/TAN */ 934 send_tan (bc); 935 if (GNUNET_YES == bc->suspended) 936 { 937 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 938 "Suspending request after PIN transmission\n"); 939 return MHD_YES; 940 } 941 /* Did we already try to generate a response? */ 942 if (GNUNET_OK != bc->status) 943 return (GNUNET_NO == bc->status) 944 ? MHD_YES 945 : MHD_NO; 946 } 947 } 948 949 { 950 json_t *args; 951 struct MHD_Response *resp; 952 unsigned int http_status; 953 enum MHD_Result res; 954 955 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 956 "Returning success from challenge with %u attempts left\n", 957 (unsigned int) bc->pin_attempts_left); 958 args = GNUNET_JSON_PACK ( 959 GNUNET_JSON_pack_uint64 ("attempts_left", 960 bc->pin_attempts_left), 961 GNUNET_JSON_pack_string ("type", 962 "created"), 963 GNUNET_JSON_pack_object_incref ("address", 964 bc->address), 965 GNUNET_JSON_pack_bool ("transmitted", 966 bc->retransmit), 967 GNUNET_JSON_pack_timestamp ("retransmission_time", 968 GNUNET_TIME_absolute_to_timestamp ( 969 GNUNET_TIME_absolute_add ( 970 bc->last_tx_time, 971 CH_validation_duration))) 972 ); 973 http_status = MHD_HTTP_OK; 974 resp = TALER_MHD_make_json_steal (args); 975 GNUNET_break (MHD_YES == 976 MHD_add_response_header (resp, 977 MHD_HTTP_HEADER_CACHE_CONTROL, 978 "no-store,no-cache")); 979 res = MHD_queue_response (hc->connection, 980 http_status, 981 resp); 982 MHD_destroy_response (resp); 983 return res; 984 } 985 }