anastasis_authorization_plugin_post.c (26985B)
1 /* 2 This file is part of Anastasis 3 Copyright (C) 2021 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.GPL. If not, see <http://www.gnu.org/licenses/> 15 */ 16 /** 17 * @file anastasis_authorization_plugin_post.c 18 * @brief authorization plugin post based 19 * @author Christian Grothoff 20 */ 21 #include "platform.h" 22 #include "anastasis_authorization_plugin.h" 23 #include <taler/taler_mhd_lib.h> 24 #include <taler/taler_json_lib.h> 25 #include <jansson.h> 26 #include "anastasis_util_lib.h" 27 #include <gnunet/gnunet_db_lib.h> 28 #include "anastasis_database_lib.h" 29 30 /** 31 * How many retries do we allow per code? 32 */ 33 #define INITIAL_RETRY_COUNTER 3 34 35 36 /** 37 * Saves the State of a authorization plugin. 38 */ 39 struct PostContext 40 { 41 42 /** 43 * Command which is executed to run the plugin (some bash script or a 44 * command line argument) 45 */ 46 char *auth_command; 47 48 /** 49 * Messages of the plugin, read from a resource file. 50 */ 51 json_t *messages; 52 53 /** 54 * Argument passed to the "init" function of each 55 * plugin. 56 */ 57 const struct ANASTASIS_AuthorizationContext *ac; 58 }; 59 60 61 /** 62 * Saves the state of a authorization process 63 */ 64 struct ANASTASIS_AUTHORIZATION_State 65 { 66 /** 67 * Public key of the challenge which is authorised 68 */ 69 struct ANASTASIS_CRYPTO_TruthUUIDP truth_uuid; 70 71 /** 72 * Code which is sent to the user. 73 */ 74 uint64_t code; 75 76 /** 77 * Our plugin context. 78 */ 79 struct PostContext *ctx; 80 81 /** 82 * Function to call when we made progress. 83 */ 84 GNUNET_SCHEDULER_TaskCallback trigger; 85 86 /** 87 * Closure for @e trigger. 88 */ 89 void *trigger_cls; 90 91 /** 92 * holds the truth information 93 */ 94 json_t *post; 95 96 /** 97 * Handle to the helper process. 98 */ 99 struct GNUNET_Process *child; 100 101 /** 102 * Handle to wait for @e child 103 */ 104 struct GNUNET_ChildWaitHandle *cwh; 105 106 /** 107 * Our client connection, set if suspended. 108 */ 109 struct MHD_Connection *connection; 110 111 /** 112 * Message to send. 113 */ 114 char *msg; 115 116 /** 117 * Offset of transmission in msg. 118 */ 119 size_t msg_off; 120 121 /** 122 * Exit code from helper. 123 */ 124 long unsigned int exit_code; 125 126 /** 127 * How did the helper die? 128 */ 129 enum GNUNET_OS_ProcessStatusType pst; 130 131 132 }; 133 134 135 /** 136 * Returned by #get_message() when the configured messages file has no entry 137 * for the requested ID. This is an installation error, but it must not be 138 * allowed to reach GNUNET_asprintf() as a NULL format string. The text 139 * deliberately contains no printf conversions, so substituting it for any 140 * template is safe whatever argument list the caller passes. 141 */ 142 #define MISSING_MESSAGE \ 143 "The provider is misconfigured: a message template is missing." 144 145 146 /** 147 * Obtain internationalized message @a msg_id from @a ctx using 148 * language preferences of @a conn. 149 * 150 * @param messages JSON object to lookup message from 151 * @param conn connection to lookup message for 152 * @param msg_id unique message ID 153 * @return the requested message, or #MISSING_MESSAGE if it was not 154 * configured; never NULL 155 */ 156 static const char * 157 get_message (const json_t *messages, 158 struct MHD_Connection *conn, 159 const char *msg_id) 160 { 161 const char *accept_lang; 162 163 accept_lang = MHD_lookup_connection_value (conn, 164 MHD_HEADER_KIND, 165 MHD_HTTP_HEADER_ACCEPT_LANGUAGE); 166 if (NULL == accept_lang) 167 accept_lang = "en_US"; 168 { 169 const char *ret; 170 struct GNUNET_JSON_Specification spec[] = { 171 TALER_JSON_spec_i18n_string (msg_id, 172 accept_lang, 173 &ret), 174 GNUNET_JSON_spec_end () 175 }; 176 177 if (GNUNET_OK != 178 GNUNET_JSON_parse (messages, 179 spec, 180 NULL, NULL)) 181 { 182 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 183 "Message `%s' is missing from the configured messages file\n", 184 msg_id); 185 GNUNET_JSON_parse_free (spec); 186 return MISSING_MESSAGE; 187 } 188 GNUNET_JSON_parse_free (spec); 189 if (NULL == ret) 190 { 191 /* The parser of TALER_JSON_spec_i18n_string returns #GNUNET_OK even 192 when the field is absent or is not a string, in which case it stores 193 NULL; the check above therefore never fires for a missing message and 194 this one is what keeps NULL out of the format argument of the 195 GNUNET_asprintf() calls below. */ 196 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 197 "Message `%s' is missing or not a string in the configured messages file\n", 198 msg_id); 199 return MISSING_MESSAGE; 200 } 201 return ret; 202 } 203 } 204 205 206 /** 207 * Check that @a value cannot be mistaken for a command-line option. The 208 * address fields are handed to the operator-supplied COMMAND as positional 209 * arguments, and we do not know whether that helper uses an option parser or 210 * honours "--", so we refuse anything option-shaped rather than rely on it. 211 * 212 * @param value field value to check, may be NULL 213 * @return true if @a value is safe to pass as a positional argument 214 */ 215 static bool 216 is_safe_argument (const char *value) 217 { 218 return ( (NULL != value) && 219 ('-' != value[0]) ); 220 } 221 222 223 /** 224 * Validate @a data is a well-formed input into the challenge method, 225 * i.e. @a data is a well-formed phone number for sending an SMS, or 226 * a well-formed e-mail address for sending an e-mail. Not expected to 227 * check that the phone number or e-mail account actually exists. 228 * 229 * To be possibly used before issuing a 402 payment required to the client. 230 * 231 * @param cls closure 232 * @param connection HTTP client request (for queuing response) 233 * @param mime_type mime type of @e data 234 * @param data input to validate (i.e. is it a valid phone number, etc.) 235 * @param data_length number of bytes in @a data 236 * @return #GNUNET_OK if @a data is valid, 237 * #GNUNET_NO if @a data is invalid and a reply was successfully queued on @a connection 238 * #GNUNET_SYSERR if @a data invalid but we failed to queue a reply on @a connection 239 */ 240 static enum GNUNET_GenericReturnValue 241 post_validate (void *cls, 242 struct MHD_Connection *connection, 243 const char *mime_type, 244 const char *data, 245 size_t data_length) 246 { 247 struct PostContext *ctx = cls; 248 json_t *j; 249 json_error_t error; 250 const char *name; 251 const char *street; 252 const char *city; 253 const char *zip; 254 const char *country; 255 struct GNUNET_JSON_Specification spec[] = { 256 GNUNET_JSON_spec_string ("full_name", 257 &name), 258 GNUNET_JSON_spec_string ("street", 259 &street), 260 GNUNET_JSON_spec_string ("city", 261 &city), 262 GNUNET_JSON_spec_string ("postcode", 263 &zip), 264 GNUNET_JSON_spec_string ("country", 265 &country), 266 GNUNET_JSON_spec_end () 267 }; 268 269 (void) ctx; 270 j = json_loadb (data, 271 data_length, 272 JSON_REJECT_DUPLICATES, 273 &error); 274 if (NULL == j) 275 { 276 if (MHD_NO == 277 TALER_MHD_reply_with_error (connection, 278 MHD_HTTP_CONFLICT, 279 TALER_EC_ANASTASIS_POST_INVALID, 280 "JSON malformed")) 281 return GNUNET_SYSERR; 282 return GNUNET_NO; 283 } 284 285 if (GNUNET_OK != 286 GNUNET_JSON_parse (j, 287 spec, 288 NULL, NULL)) 289 { 290 GNUNET_break (0); 291 json_decref (j); 292 if (MHD_NO == 293 TALER_MHD_reply_with_error (connection, 294 MHD_HTTP_CONFLICT, 295 TALER_EC_ANASTASIS_POST_INVALID, 296 "JSON lacked required address information")) 297 return GNUNET_SYSERR; 298 return GNUNET_NO; 299 } 300 if ( (! is_safe_argument (name)) || 301 (! is_safe_argument (street)) || 302 (! is_safe_argument (city)) || 303 (! is_safe_argument (zip)) || 304 (! is_safe_argument (country)) ) 305 { 306 json_decref (j); 307 if (MHD_NO == 308 TALER_MHD_reply_with_error (connection, 309 MHD_HTTP_CONFLICT, 310 TALER_EC_ANASTASIS_POST_INVALID, 311 "address fields must not begin with `-'")) 312 return GNUNET_SYSERR; 313 return GNUNET_NO; 314 } 315 json_decref (j); 316 return GNUNET_OK; 317 } 318 319 320 /** 321 * Begin issuing authentication challenge to user based on @a data. 322 * I.e. start to send mail. 323 * 324 * @param cls closure 325 * @param trigger function to call when we made progress 326 * @param trigger_cls closure for @a trigger 327 * @param truth_uuid Identifier of the challenge, to be (if possible) included in the 328 * interaction with the user 329 * @param code secret code that the user has to provide back to satisfy the challenge in 330 * the main anastasis protocol 331 * @param data input to validate (i.e. is it a valid phone number, etc.) 332 * @param data_length number of bytes in @a data 333 * @return state to track progress on the authorization operation, NULL on failure 334 */ 335 static struct ANASTASIS_AUTHORIZATION_State * 336 post_start (void *cls, 337 GNUNET_SCHEDULER_TaskCallback trigger, 338 void *trigger_cls, 339 const struct ANASTASIS_CRYPTO_TruthUUIDP *truth_uuid, 340 uint64_t code, 341 const void *data, 342 size_t data_length) 343 { 344 struct PostContext *ctx = cls; 345 struct ANASTASIS_AUTHORIZATION_State *as; 346 json_error_t error; 347 enum GNUNET_DB_QueryStatus qs; 348 349 /* If the user can show this challenge code, this 350 plugin is already happy (no additional 351 requirements), so mark this challenge as 352 already satisfied from the start. */ 353 qs = ANASTASIS_DB_update_to_challenge_code_satisfied ( 354 truth_uuid, 355 code); 356 if (qs <= 0) 357 { 358 GNUNET_break (0); 359 return NULL; 360 } 361 as = GNUNET_new (struct ANASTASIS_AUTHORIZATION_State); 362 as->trigger = trigger; 363 as->trigger_cls = trigger_cls; 364 as->ctx = ctx; 365 as->truth_uuid = *truth_uuid; 366 as->code = code; 367 as->post = json_loadb (data, 368 data_length, 369 JSON_REJECT_DUPLICATES, 370 &error); 371 if (NULL == as->post) 372 { 373 GNUNET_break (0); 374 GNUNET_free (as); 375 return NULL; 376 } 377 return as; 378 } 379 380 381 /** 382 * Did a transmission helper report that the challenge was transmitted? 383 * 384 * Exit code 0 means the challenge was confirmed to have reached the address. 385 * The 200-210 band means a service accepted it for delivery without confirming 386 * that it arrived: 201 accepted by the provider, 202 suppressed as a duplicate 387 * of a message already in flight. 388 * 389 * The band deliberately does not start at 1: libgnunetutil reports a helper it 390 * failed to exec() as exit code 1, and that must not be mistaken for a 391 * delivery. 392 * 393 * @param exit_code exit status of the helper, which must have exited normally 394 * @return true if the challenge was transmitted 395 */ 396 static bool 397 helper_reported_success (unsigned long int exit_code) 398 { 399 return (0 == exit_code) || 400 ( (exit_code >= 200) && 401 (exit_code <= 210) ); 402 } 403 404 405 /** 406 * Map the exit status of the Post helper onto an HTTP status and error code. 407 * 408 * The helpers shipped with Challenger use a banded exit-code scheme documented 409 * in challenger-send-post(1): 0 and 200-210 mean the challenge was transmitted 410 * -- for physical mail always 201 (accepted for printing and dispatch), since 411 * delivery takes days and is never reported back -- 10-19 blames the address 412 * the user gave us, 20-29 is a recipient that is temporarily unreachable, 413 * 30-39 is the transmission provider and 40-49 is our own configuration. 414 * 415 * Codes we do not recognise are reported as an upstream failure rather than 416 * blamed on the user, so that a helper predating this scheme -- which used 417 * small ad-hoc exit codes -- never yields a client error. 418 * 419 * @param exit_code exit status of the helper, which must have exited normally 420 * @param[out] http_status set to the HTTP status to return 421 * @return error code to return, #TALER_EC_NONE if the challenge was transmitted 422 */ 423 static enum TALER_ErrorCode 424 classify_helper_status (unsigned long int exit_code, 425 unsigned int *http_status) 426 { 427 if (helper_reported_success (exit_code)) 428 { 429 *http_status = MHD_HTTP_OK; 430 return TALER_EC_NONE; 431 } 432 if ( (exit_code >= 10) && 433 (exit_code < 20) ) 434 { 435 *http_status = MHD_HTTP_CONFLICT; 436 return TALER_EC_ANASTASIS_POST_INVALID; 437 } 438 if ( (exit_code >= 20) && 439 (exit_code < 30) ) 440 { 441 *http_status = MHD_HTTP_SERVICE_UNAVAILABLE; 442 return TALER_EC_ANASTASIS_ADDRESS_UNREACHABLE; 443 } 444 if ( (exit_code >= 40) && 445 (exit_code < 50) ) 446 { 447 *http_status = MHD_HTTP_INTERNAL_SERVER_ERROR; 448 return TALER_EC_ANASTASIS_HELPER_MISCONFIGURED; 449 } 450 *http_status = MHD_HTTP_BAD_GATEWAY; 451 return TALER_EC_ANASTASIS_POST_HELPER_COMMAND_FAILED; 452 } 453 454 455 /** 456 * Function called when our Post helper has terminated. 457 * 458 * @param cls our `struct ANASTASIS_AUHTORIZATION_State` 459 * @param type type of the process 460 * @param exit_code status code of the process 461 */ 462 static void 463 post_done_cb (void *cls, 464 enum GNUNET_OS_ProcessStatusType type, 465 long unsigned int exit_code) 466 { 467 struct ANASTASIS_AUTHORIZATION_State *as = cls; 468 469 as->cwh = NULL; 470 if (NULL != as->child) 471 { 472 GNUNET_process_destroy (as->child); 473 as->child = NULL; 474 } 475 as->pst = type; 476 as->exit_code = exit_code; 477 MHD_resume_connection (as->connection); 478 as->trigger (as->trigger_cls); 479 } 480 481 482 /** 483 * Begin issuing authentication challenge to user based on @a data. 484 * I.e. start to send SMS or e-mail or launch video identification. 485 * 486 * @param as authorization state 487 * @param connection HTTP client request (for queuing response, such as redirection to video portal) 488 * @return state of the request 489 */ 490 static enum ANASTASIS_AUTHORIZATION_ChallengeResult 491 post_challenge (struct ANASTASIS_AUTHORIZATION_State *as, 492 struct MHD_Connection *connection) 493 { 494 const char *mime; 495 const char *lang; 496 enum MHD_Result mres; 497 const char *name; 498 const char *street; 499 const char *city; 500 const char *zip; 501 const char *country; 502 struct GNUNET_JSON_Specification spec[] = { 503 GNUNET_JSON_spec_string ("full_name", 504 &name), 505 GNUNET_JSON_spec_string ("street", 506 &street), 507 GNUNET_JSON_spec_string ("city", 508 &city), 509 GNUNET_JSON_spec_string ("postcode", 510 &zip), 511 GNUNET_JSON_spec_string ("country", 512 &country), 513 GNUNET_JSON_spec_end () 514 }; 515 516 mime = MHD_lookup_connection_value (connection, 517 MHD_HEADER_KIND, 518 MHD_HTTP_HEADER_ACCEPT); 519 if (NULL == mime) 520 mime = "text/plain"; 521 lang = MHD_lookup_connection_value (connection, 522 MHD_HEADER_KIND, 523 MHD_HTTP_HEADER_ACCEPT_LANGUAGE); 524 if (NULL == lang) 525 lang = "en"; 526 if (GNUNET_OK != 527 GNUNET_JSON_parse (as->post, 528 spec, 529 NULL, NULL)) 530 { 531 GNUNET_break (0); 532 mres = TALER_MHD_reply_with_error (connection, 533 MHD_HTTP_INTERNAL_SERVER_ERROR, 534 TALER_EC_ANASTASIS_POST_INVALID, 535 "address information incomplete"); 536 if (MHD_YES != mres) 537 return ANASTASIS_AUTHORIZATION_CRES_FAILED_REPLY_FAILED; 538 return ANASTASIS_AUTHORIZATION_CRES_FAILED; 539 } 540 /* Re-checked here and not only in #post_validate(), because truths stored 541 before that check existed would otherwise still reach the helper. */ 542 if ( (! is_safe_argument (name)) || 543 (! is_safe_argument (street)) || 544 (! is_safe_argument (city)) || 545 (! is_safe_argument (zip)) || 546 (! is_safe_argument (country)) ) 547 { 548 GNUNET_break_op (0); 549 mres = TALER_MHD_reply_with_error (connection, 550 MHD_HTTP_INTERNAL_SERVER_ERROR, 551 TALER_EC_ANASTASIS_POST_INVALID, 552 "address fields must not begin with `-'") 553 ; 554 if (MHD_YES != mres) 555 return ANASTASIS_AUTHORIZATION_CRES_FAILED_REPLY_FAILED; 556 return ANASTASIS_AUTHORIZATION_CRES_FAILED; 557 } 558 if (NULL == as->msg) 559 { 560 /* First time, start child process and feed pipe */ 561 struct GNUNET_DISK_PipeHandle *p; 562 struct GNUNET_DISK_FileHandle *pipe_stdin; 563 564 p = GNUNET_DISK_pipe (GNUNET_DISK_PF_BLOCKING_RW); 565 if (NULL == p) 566 { 567 mres = TALER_MHD_reply_with_error (connection, 568 MHD_HTTP_INTERNAL_SERVER_ERROR, 569 TALER_EC_ANASTASIS_POST_HELPER_EXEC_FAILED, 570 "pipe"); 571 if (MHD_YES != mres) 572 return ANASTASIS_AUTHORIZATION_CRES_FAILED_REPLY_FAILED; 573 return ANASTASIS_AUTHORIZATION_CRES_FAILED; 574 } 575 as->child = GNUNET_process_create (GNUNET_OS_INHERIT_STD_ERR); 576 GNUNET_assert (GNUNET_OK == 577 GNUNET_process_set_options ( 578 as->child, 579 GNUNET_process_option_inherit_rpipe (p, 580 STDIN_FILENO))); 581 if (GNUNET_OK != 582 GNUNET_process_run_command_va (as->child, 583 as->ctx->auth_command, 584 as->ctx->auth_command, 585 name, 586 street, 587 city, 588 zip, 589 country, 590 NULL)) 591 { 592 GNUNET_process_destroy (as->child); 593 as->child = NULL; 594 GNUNET_DISK_pipe_close (p); 595 mres = TALER_MHD_reply_with_error (connection, 596 MHD_HTTP_INTERNAL_SERVER_ERROR, 597 TALER_EC_ANASTASIS_POST_HELPER_EXEC_FAILED, 598 "exec"); 599 if (MHD_YES != mres) 600 return ANASTASIS_AUTHORIZATION_CRES_FAILED_REPLY_FAILED; 601 return ANASTASIS_AUTHORIZATION_CRES_FAILED; 602 } 603 pipe_stdin = GNUNET_DISK_pipe_detach_end (p, 604 GNUNET_DISK_PIPE_END_WRITE); 605 GNUNET_assert (NULL != pipe_stdin); 606 GNUNET_DISK_pipe_close (p); 607 GNUNET_asprintf (&as->msg, 608 get_message (as->ctx->messages, 609 connection, 610 "body"), 611 ANASTASIS_pin2s (as->code), 612 ANASTASIS_CRYPTO_uuid2s (&as->truth_uuid)); 613 { 614 const char *off = as->msg; 615 size_t left = strlen (off); 616 617 while (0 != left) 618 { 619 ssize_t ret; 620 621 ret = GNUNET_DISK_file_write (pipe_stdin, 622 off, 623 left); 624 if (ret <= 0) 625 { 626 mres = TALER_MHD_reply_with_error (connection, 627 MHD_HTTP_INTERNAL_SERVER_ERROR, 628 TALER_EC_ANASTASIS_POST_HELPER_EXEC_FAILED, 629 "write"); 630 if (MHD_YES != mres) 631 return ANASTASIS_AUTHORIZATION_CRES_FAILED_REPLY_FAILED; 632 return ANASTASIS_AUTHORIZATION_CRES_FAILED; 633 } 634 as->msg_off += ret; 635 off += ret; 636 left -= ret; 637 } 638 GNUNET_DISK_file_close (pipe_stdin); 639 } 640 as->cwh = GNUNET_wait_child (as->child, 641 &post_done_cb, 642 as); 643 as->connection = connection; 644 MHD_suspend_connection (connection); 645 return ANASTASIS_AUTHORIZATION_CRES_SUSPENDED; 646 } 647 if (NULL != as->cwh) 648 { 649 /* Spurious call, why are we here? */ 650 GNUNET_break (0); 651 MHD_suspend_connection (connection); 652 return ANASTASIS_AUTHORIZATION_CRES_SUSPENDED; 653 } 654 if ( (GNUNET_OS_PROCESS_EXITED != as->pst) || 655 (! helper_reported_success (as->exit_code)) ) 656 { 657 char es[32]; 658 unsigned int http_status; 659 enum TALER_ErrorCode ec; 660 661 if (GNUNET_OS_PROCESS_EXITED != as->pst) 662 { 663 /* Killed by a signal or otherwise abnormal: there is no exit code to 664 classify. */ 665 http_status = MHD_HTTP_BAD_GATEWAY; 666 ec = TALER_EC_ANASTASIS_POST_HELPER_COMMAND_FAILED; 667 } 668 else 669 { 670 ec = classify_helper_status (as->exit_code, 671 &http_status); 672 } 673 GNUNET_snprintf (es, 674 sizeof (es), 675 "%u/%d", 676 (unsigned int) as->exit_code, 677 as->pst); 678 mres = TALER_MHD_reply_with_error (connection, 679 http_status, 680 ec, 681 es); 682 if (MHD_YES != mres) 683 return ANASTASIS_AUTHORIZATION_CRES_FAILED_REPLY_FAILED; 684 return ANASTASIS_AUTHORIZATION_CRES_FAILED; 685 } 686 687 /* Build HTTP response */ 688 { 689 struct MHD_Response *resp; 690 691 if (0.0 < TALER_pattern_matches (mime, 692 "application/json")) 693 { 694 resp = TALER_MHD_MAKE_JSON_PACK ( 695 GNUNET_JSON_pack_string ("challenge_type", 696 "TAN_SENT"), 697 GNUNET_JSON_pack_string ("tan_address_hint", 698 zip)); 699 } 700 else 701 { 702 size_t reply_len; 703 char *reply; 704 705 reply_len = GNUNET_asprintf (&reply, 706 get_message (as->ctx->messages, 707 connection, 708 "instructions"), 709 zip); 710 resp = MHD_create_response_from_buffer (reply_len, 711 reply, 712 MHD_RESPMEM_MUST_COPY); 713 GNUNET_free (reply); 714 TALER_MHD_add_global_headers (resp, 715 false); 716 } 717 mres = MHD_queue_response (connection, 718 MHD_HTTP_OK, 719 resp); 720 MHD_destroy_response (resp); 721 if (MHD_YES != mres) 722 return ANASTASIS_AUTHORIZATION_CRES_SUCCESS_REPLY_FAILED; 723 return ANASTASIS_AUTHORIZATION_CRES_SUCCESS; 724 } 725 } 726 727 728 /** 729 * Free internal state associated with @a as. 730 * 731 * @param as state to clean up 732 */ 733 static void 734 post_cleanup (struct ANASTASIS_AUTHORIZATION_State *as) 735 { 736 if (NULL != as->cwh) 737 { 738 GNUNET_wait_child_cancel (as->cwh); 739 as->cwh = NULL; 740 } 741 if (NULL != as->child) 742 { 743 GNUNET_break (GNUNET_OK == 744 GNUNET_process_kill (as->child, 745 SIGKILL)); 746 GNUNET_break (GNUNET_OK == 747 GNUNET_process_wait (as->child, 748 true, 749 NULL, 750 NULL)); 751 GNUNET_process_destroy (as->child); 752 as->child = NULL; 753 } 754 GNUNET_free (as->msg); 755 json_decref (as->post); 756 GNUNET_free (as); 757 } 758 759 760 /** 761 * Initialize post based authorization plugin 762 * 763 * @param cls a configuration instance 764 * @return NULL on error, otherwise a `struct ANASTASIS_AuthorizationPlugin` 765 */ 766 void * 767 libanastasis_plugin_authorization_post_init (void *cls); 768 769 /* declaration to fix compiler warning */ 770 void * 771 libanastasis_plugin_authorization_post_init (void *cls) 772 { 773 const struct ANASTASIS_AuthorizationContext *ac = cls; 774 struct ANASTASIS_AuthorizationPlugin *plugin; 775 const struct GNUNET_CONFIGURATION_Handle *cfg = ac->cfg; 776 struct PostContext *ctx; 777 778 ctx = GNUNET_new (struct PostContext); 779 ctx->ac = ac; 780 { 781 char *fn; 782 json_error_t err; 783 char *tmp; 784 785 tmp = GNUNET_OS_installation_get_path (ANASTASIS_project_data (), 786 GNUNET_OS_IPK_DATADIR); 787 GNUNET_asprintf (&fn, 788 "%sauthorization-post-messages.json", 789 tmp); 790 GNUNET_free (tmp); 791 ctx->messages = json_load_file (fn, 792 JSON_REJECT_DUPLICATES, 793 &err); 794 if (NULL == ctx->messages) 795 { 796 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 797 "Failed to load messages from `%s': %s at %d:%d\n", 798 fn, 799 err.text, 800 err.line, 801 err.column); 802 GNUNET_free (fn); 803 GNUNET_free (ctx); 804 return NULL; 805 } 806 GNUNET_free (fn); 807 } 808 plugin = GNUNET_new (struct ANASTASIS_AuthorizationPlugin); 809 plugin->retry_counter = INITIAL_RETRY_COUNTER; 810 plugin->code_validity_period = GNUNET_TIME_UNIT_MONTHS; 811 plugin->code_rotation_period = GNUNET_TIME_UNIT_WEEKS; 812 plugin->code_retransmission_frequency 813 = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_DAYS, 814 2); 815 plugin->cls = ctx; 816 plugin->validate = &post_validate; 817 plugin->start = &post_start; 818 plugin->challenge = &post_challenge; 819 plugin->cleanup = &post_cleanup; 820 821 if (GNUNET_OK != 822 GNUNET_CONFIGURATION_get_value_string (cfg, 823 "authorization-post", 824 "COMMAND", 825 &ctx->auth_command)) 826 { 827 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, 828 "authorization-post", 829 "COMMAND"); 830 json_decref (ctx->messages); 831 GNUNET_free (ctx); 832 GNUNET_free (plugin); 833 return NULL; 834 } 835 return plugin; 836 } 837 838 839 /** 840 * Unload authorization plugin 841 * 842 * @param cls a `struct ANASTASIS_AuthorizationPlugin` 843 * @return NULL (always) 844 */ 845 void * 846 libanastasis_plugin_authorization_post_done (void *cls); 847 848 /* declaration to fix compiler warning */ 849 void * 850 libanastasis_plugin_authorization_post_done (void *cls) 851 { 852 struct ANASTASIS_AuthorizationPlugin *plugin = cls; 853 struct PostContext *ctx = plugin->cls; 854 855 GNUNET_free (ctx->auth_command); 856 json_decref (ctx->messages); 857 GNUNET_free (ctx); 858 GNUNET_free (plugin); 859 return NULL; 860 }