anastasis_api_backup_redux.c (146531B)
1 /* 2 This file is part of Anastasis 3 Copyright (C) 2020-2023 Anastasis SARL 4 5 Anastasis is free software; you can redistribute it and/or modify it under the 6 terms of the GNU 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 General Public License for more details. 12 13 You should have received a copy of the GNU General Public License along with 14 Anastasis; see the file COPYING.GPL. If not, see <http://www.gnu.org/licenses/> 15 */ 16 /** 17 * @file reducer/anastasis_api_backup_redux.c 18 * @brief anastasis reducer backup api 19 * @author Christian Grothoff 20 * @author Dominik Meister 21 * @author Dennis Neufeld 22 */ 23 24 #include "platform.h" 25 #include "anastasis_redux.h" 26 #include "anastasis_api_redux.h" 27 #include <taler/taler_merchant_service.h> 28 29 /** 30 * How long do Anastasis providers store data if the service 31 * is free? Must match #ANASTASIS_MAX_YEARS_STORAGE from 32 * anastasis-httpd.h. 33 */ 34 #define ANASTASIS_FREE_STORAGE GNUNET_TIME_relative_multiply ( \ 35 GNUNET_TIME_UNIT_YEARS, 5) 36 37 /** 38 * CPU limiter: do not evaluate more than 16k 39 * possible policy combinations to find the "best" 40 * policy. 41 */ 42 #define MAX_EVALUATIONS (1024 * 16) 43 44 45 #define GENERATE_STRING(STRING) #STRING, 46 static const char *backup_strings[] = { 47 ANASTASIS_BACKUP_STATES (GENERATE_STRING) 48 }; 49 #undef GENERATE_STRING 50 51 52 /** 53 * Linked list of costs. 54 */ 55 struct Costs 56 { 57 58 /** 59 * Kept in a LL. 60 */ 61 struct Costs *next; 62 63 /** 64 * Cost in one of the currencies. 65 */ 66 struct TALER_Amount cost; 67 }; 68 69 70 /** 71 * Add amount from @a cost to @a my_cost list. 72 * 73 * @param[in,out] my_cost pointer to list to modify 74 * @param cost amount to add 75 */ 76 static void 77 add_cost (struct Costs **my_cost, 78 const struct TALER_Amount *cost) 79 { 80 for (struct Costs *pos = *my_cost; 81 NULL != pos; 82 pos = pos->next) 83 { 84 if (GNUNET_OK != 85 TALER_amount_cmp_currency (&pos->cost, 86 cost)) 87 continue; 88 GNUNET_assert (0 <= 89 TALER_amount_add (&pos->cost, 90 &pos->cost, 91 cost)); 92 return; 93 } 94 { 95 struct Costs *nc; 96 97 nc = GNUNET_new (struct Costs); 98 nc->cost = *cost; 99 nc->next = *my_cost; 100 *my_cost = nc; 101 } 102 } 103 104 105 /** 106 * Add amount from @a cost to @a my_cost list. 107 * 108 * @param[in,out] my_cost pointer to list to modify 109 * @param cost amount to add 110 */ 111 static void 112 add_costs (struct Costs **my_cost, 113 const struct Costs *costs) 114 { 115 for (const struct Costs *pos = costs; 116 NULL != pos; 117 pos = pos->next) 118 { 119 add_cost (my_cost, 120 &pos->cost); 121 } 122 } 123 124 125 enum ANASTASIS_BackupState 126 ANASTASIS_backup_state_from_string_ (const char *state_string) 127 { 128 for (enum ANASTASIS_BackupState i = 0; 129 i < sizeof (backup_strings) / sizeof(*backup_strings); 130 i++) 131 if (0 == strcmp (state_string, 132 backup_strings[i])) 133 return i; 134 return ANASTASIS_BACKUP_STATE_INVALID; 135 } 136 137 138 const char * 139 ANASTASIS_backup_state_to_string_ (enum ANASTASIS_BackupState bs) 140 { 141 if ( (bs < 0) || 142 (bs >= sizeof (backup_strings) / sizeof(*backup_strings)) ) 143 { 144 GNUNET_break_op (0); 145 return NULL; 146 } 147 return backup_strings[bs]; 148 } 149 150 151 /** 152 * Update the 'backup_state' field of @a state to @a new_backup_state. 153 * 154 * @param[in,out] state the state to transition 155 * @param new_backup_state the state to transition to 156 */ 157 static void 158 set_state (json_t *state, 159 enum ANASTASIS_BackupState new_backup_state) 160 { 161 GNUNET_assert ( 162 0 == 163 json_object_set_new ( 164 state, 165 "backup_state", 166 json_string (ANASTASIS_backup_state_to_string_ (new_backup_state)))); 167 } 168 169 170 /** 171 * Returns an initial ANASTASIS backup state (CONTINENT_SELECTING). 172 * 173 * @param cfg handle for gnunet configuration 174 * @return NULL on failure 175 */ 176 json_t * 177 ANASTASIS_backup_start (const struct GNUNET_CONFIGURATION_Handle *cfg) 178 { 179 json_t *initial_state; 180 const char *external_reducer = ANASTASIS_REDUX_probe_external_reducer (); 181 182 if (NULL != external_reducer) 183 { 184 int pipefd_stdout[2]; 185 pid_t pid = 0; 186 int status; 187 FILE *reducer_stdout; 188 189 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 190 "Using external reducer '%s' for backup start status\n", 191 external_reducer); 192 193 GNUNET_assert (0 == pipe (pipefd_stdout)); 194 pid = fork (); 195 if (pid == 0) 196 { 197 GNUNET_assert (0 == 198 close (pipefd_stdout[0])); 199 GNUNET_assert (STDOUT_FILENO == 200 dup2 (pipefd_stdout[1], 201 STDOUT_FILENO)); 202 execlp (external_reducer, 203 external_reducer, 204 "-b", 205 NULL); 206 GNUNET_assert (0); 207 } 208 209 GNUNET_assert (0 == 210 close (pipefd_stdout[1])); 211 reducer_stdout = fdopen (pipefd_stdout[0], 212 "r"); 213 GNUNET_assert (NULL != reducer_stdout); 214 { 215 json_error_t err; 216 217 initial_state = json_loadf (reducer_stdout, 218 0, 219 &err); 220 221 if (NULL == initial_state) 222 { 223 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 224 "External reducer did not output valid JSON: %s:%d:%d %s\n", 225 err.source, 226 err.line, 227 err.column, 228 err.text); 229 GNUNET_assert (0 == fclose (reducer_stdout)); 230 waitpid (pid, &status, 0); 231 return NULL; 232 } 233 } 234 235 GNUNET_assert (NULL != initial_state); 236 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 237 "Waiting for external reducer to terminate.\n"); 238 GNUNET_assert (0 == fclose (reducer_stdout)); 239 reducer_stdout = NULL; 240 waitpid (pid, &status, 0); 241 242 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 243 "External reducer finished with exit status '%d'\n", 244 status); 245 return initial_state; 246 } 247 248 (void) cfg; 249 initial_state = ANASTASIS_REDUX_load_continents_ (); 250 if (NULL == initial_state) 251 return NULL; 252 GNUNET_assert ( 253 0 == 254 json_object_set_new (initial_state, 255 "reducer_type", 256 json_string ("backup"))); 257 set_state (initial_state, 258 ANASTASIS_BACKUP_STATE_CONTINENT_SELECTING); 259 return initial_state; 260 } 261 262 263 /** 264 * Test if @a challenge_size is small enough for the provider's 265 * @a size_limit_in_mb. 266 * 267 * We add 1024 to @a challenge_size here as a "safety margin" as 268 * the encrypted challenge has some additional headers around it 269 * 270 * @param size_limit_in_mb provider's upload limit 271 * @param challenge_size actual binary size of the challenge 272 * @return true if this fits 273 */ 274 static bool 275 challenge_size_ok (uint32_t size_limit_in_mb, 276 size_t challenge_size) 277 { 278 return (size_limit_in_mb * 1024LLU * 1024LLU >= 279 challenge_size + 1024LLU); 280 } 281 282 283 /** 284 * DispatchHandler/Callback function which is called for a 285 * "add_authentication" action. 286 * Returns an #ANASTASIS_ReduxAction if operation is async. 287 * 288 * @param state state to operate on 289 * @param arguments arguments to use for operation on state 290 * @param cb callback to call during/after operation 291 * @param cb_cls callback closure 292 * @return NULL 293 */ 294 static struct ANASTASIS_ReduxAction * 295 add_authentication (json_t *state, 296 const json_t *arguments, 297 ANASTASIS_ActionCallback cb, 298 void *cb_cls) 299 { 300 json_t *auth_providers; 301 json_t *method; 302 const char *method_type; 303 void *challenge; 304 size_t challenge_size; 305 struct GNUNET_JSON_Specification spec[] = { 306 GNUNET_JSON_spec_string ("type", 307 &method_type), 308 GNUNET_JSON_spec_varsize ("challenge", 309 &challenge, 310 &challenge_size), 311 GNUNET_JSON_spec_end () 312 }; 313 314 auth_providers = json_object_get (state, 315 "authentication_providers"); 316 if (NULL == auth_providers) 317 { 318 GNUNET_break (0); 319 ANASTASIS_redux_fail_ (cb, 320 cb_cls, 321 TALER_EC_ANASTASIS_REDUCER_STATE_INVALID, 322 "'authentication_providers' missing"); 323 return NULL; 324 } 325 326 method = json_object_get (arguments, 327 "authentication_method"); 328 if (NULL == method) 329 { 330 GNUNET_break (0); 331 ANASTASIS_redux_fail_ (cb, 332 cb_cls, 333 TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID, 334 "'authentication_method' required"); 335 return NULL; 336 } 337 if (GNUNET_OK != 338 GNUNET_JSON_parse (method, 339 spec, 340 NULL, NULL)) 341 { 342 GNUNET_break (0); 343 json_dumpf (method, 344 stderr, 345 JSON_INDENT (2)); 346 ANASTASIS_redux_fail_ (cb, 347 cb_cls, 348 TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID, 349 "'authentication_method' content malformed"); 350 return NULL; 351 } 352 /* Check we know at least one provider that supports this method */ 353 { 354 bool found = false; 355 bool too_big = false; 356 json_t *details; 357 const char *url; 358 359 json_object_foreach (auth_providers, url, details) 360 { 361 const json_t *methods = NULL; 362 json_t *imethod; 363 size_t index; 364 uint32_t size_limit_in_mb = 0; 365 const char *status; 366 uint32_t http_status = 0; 367 struct GNUNET_JSON_Specification ispec[] = { 368 GNUNET_JSON_spec_string ("status", 369 &status), 370 GNUNET_JSON_spec_mark_optional ( 371 GNUNET_JSON_spec_uint32 ("storage_limit_in_megabytes", 372 &size_limit_in_mb), 373 NULL), 374 GNUNET_JSON_spec_mark_optional ( 375 GNUNET_JSON_spec_uint32 ("http_status", 376 &http_status), 377 NULL), 378 GNUNET_JSON_spec_mark_optional ( 379 GNUNET_JSON_spec_array_const ("methods", 380 &methods), 381 NULL), 382 GNUNET_JSON_spec_end () 383 }; 384 385 if (GNUNET_OK != 386 GNUNET_JSON_parse (details, 387 ispec, 388 NULL, NULL)) 389 { 390 GNUNET_break (0); 391 continue; 392 } 393 if (0 != strcmp (status, 394 "ok")) 395 continue; 396 if (MHD_HTTP_OK != http_status) 397 continue; /* skip providers that are down */ 398 if ( (NULL == methods) || 399 (0 == size_limit_in_mb) ) 400 { 401 GNUNET_break (0); 402 continue; 403 } 404 json_array_foreach (methods, index, imethod) 405 { 406 const char *type; 407 408 type = json_string_value (json_object_get (imethod, 409 "type")); 410 GNUNET_break (NULL != type); 411 if ( (NULL != type) && 412 (0 == strcmp (type, 413 method_type)) ) 414 { 415 found = true; 416 break; 417 } 418 } 419 if (! challenge_size_ok (size_limit_in_mb, 420 challenge_size)) 421 { 422 /* Challenge data too big for this provider. Try to find another one. 423 Note: we add 1024 to challenge-size here as a "safety margin" as 424 the encrypted challenge has some additional headers around it */ 425 too_big = true; 426 found = false; 427 } 428 if (found) 429 break; 430 } 431 if (! found) 432 { 433 if (too_big) 434 { 435 ANASTASIS_redux_fail_ (cb, 436 cb_cls, 437 TALER_EC_ANASTASIS_REDUCER_CHALLENGE_DATA_TOO_BIG, 438 method_type); 439 } 440 else 441 { 442 ANASTASIS_redux_fail_ (cb, 443 cb_cls, 444 TALER_EC_ANASTASIS_REDUCER_AUTHENTICATION_METHOD_NOT_SUPPORTED, 445 method_type); 446 } 447 GNUNET_JSON_parse_free (spec); 448 return NULL; 449 } 450 } 451 GNUNET_JSON_parse_free (spec); 452 453 /* append provided method to our array */ 454 { 455 json_t *auth_method_arr; 456 457 auth_method_arr = json_object_get (state, 458 "authentication_methods"); 459 if (NULL == auth_method_arr) 460 { 461 auth_method_arr = json_array (); 462 GNUNET_assert (0 == json_object_set_new (state, 463 "authentication_methods", 464 auth_method_arr)); 465 } 466 if (! json_is_array (auth_method_arr)) 467 { 468 GNUNET_break (0); 469 ANASTASIS_redux_fail_ (cb, 470 cb_cls, 471 TALER_EC_ANASTASIS_REDUCER_STATE_INVALID, 472 "'authentication_methods' must be an array"); 473 return NULL; 474 } 475 GNUNET_assert (0 == 476 json_array_append (auth_method_arr, 477 method)); 478 cb (cb_cls, 479 TALER_EC_NONE, 480 state); 481 } 482 return NULL; 483 } 484 485 486 /** 487 * DispatchHandler/Callback function which is called for a 488 * "delete_authentication" action. 489 * Returns an #ANASTASIS_ReduxAction if operation is async. 490 * 491 * @param state state to operate on 492 * @param arguments arguments to use for operation on state 493 * @param cb callback to call during/after operation 494 * @param cb_cls callback closure 495 * @return NULL 496 */ 497 static struct ANASTASIS_ReduxAction * 498 del_authentication (json_t *state, 499 const json_t *arguments, 500 ANASTASIS_ActionCallback cb, 501 void *cb_cls) 502 { 503 json_t *idx; 504 json_t *auth_method_arr; 505 506 auth_method_arr = json_object_get (state, 507 "authentication_methods"); 508 if (! json_is_array (auth_method_arr)) 509 { 510 ANASTASIS_redux_fail_ (cb, 511 cb_cls, 512 TALER_EC_ANASTASIS_REDUCER_STATE_INVALID, 513 "'authentication_methods' must be an array"); 514 return NULL; 515 } 516 if (NULL == arguments) 517 { 518 ANASTASIS_redux_fail_ (cb, 519 cb_cls, 520 TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID, 521 "arguments missing"); 522 return NULL; 523 } 524 idx = json_object_get (arguments, 525 "authentication_method"); 526 if ( (NULL == idx) || 527 (! json_is_integer (idx)) ) 528 { 529 ANASTASIS_redux_fail_ (cb, 530 cb_cls, 531 TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID, 532 "'authentication_method' must be a number"); 533 return NULL; 534 } 535 536 { 537 size_t index = (size_t) json_integer_value (idx); 538 539 if (0 != json_array_remove (auth_method_arr, 540 index)) 541 { 542 ANASTASIS_redux_fail_ (cb, 543 cb_cls, 544 TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID_FOR_STATE, 545 "removal failed"); 546 return NULL; 547 } 548 } 549 cb (cb_cls, 550 TALER_EC_NONE, 551 state); 552 return NULL; 553 } 554 555 556 /* ********************** done_authentication ******************** */ 557 558 /** 559 * Which provider would be used for the given challenge, 560 * and at what cost? 561 */ 562 struct PolicyEntry 563 { 564 /** 565 * URL of the provider. 566 */ 567 const char *provider_name; 568 569 /** 570 * Recovery fee. 571 */ 572 struct Costs *usage_fee; 573 }; 574 575 576 /** 577 * Map from challenges to providers. 578 */ 579 struct PolicyMap 580 { 581 /** 582 * Kept in a DLL. 583 */ 584 struct PolicyMap *next; 585 586 /** 587 * Kept in a DLL. 588 */ 589 struct PolicyMap *prev; 590 591 /** 592 * Array of proividers selected for each challenge, 593 * with associated costs. 594 * Length of the array will be 'req_methods'. 595 */ 596 struct PolicyEntry *providers; 597 598 /** 599 * Diversity score for this policy mapping. 600 */ 601 unsigned int diversity; 602 603 }; 604 605 606 /** 607 * Array of challenges for a policy, and DLL with 608 * possible mappings of challenges to providers. 609 */ 610 struct Policy 611 { 612 613 /** 614 * Kept in DLL of all possible policies. 615 */ 616 struct Policy *next; 617 618 /** 619 * Kept in DLL of all possible policies. 620 */ 621 struct Policy *prev; 622 623 /** 624 * Head of DLL. 625 */ 626 struct PolicyMap *pm_head; 627 628 /** 629 * Tail of DLL. 630 */ 631 struct PolicyMap *pm_tail; 632 633 /** 634 * Challenges selected for this policy. 635 * Length of the array will be 'req_methods'. 636 */ 637 unsigned int *challenges; 638 639 }; 640 641 642 /** 643 * Information for running done_authentication() logic. 644 */ 645 struct PolicyBuilder 646 { 647 /** 648 * Authentication providers available overall, from our state. 649 */ 650 json_t *providers; 651 652 /** 653 * Authentication methods available overall, from our state. 654 */ 655 const json_t *methods; 656 657 /** 658 * Head of DLL of all possible policies. 659 */ 660 struct Policy *p_head; 661 662 /** 663 * Tail of DLL of all possible policies. 664 */ 665 struct Policy *p_tail; 666 667 /** 668 * Array of authentication policies to be computed. 669 */ 670 json_t *policies; 671 672 /** 673 * Array of length @e req_methods. 674 */ 675 unsigned int *m_idx; 676 677 /** 678 * Array of length @e req_methods identifying a set of providers selected 679 * for each authentication method, while we are trying to compute the 680 * 'best' allocation of providers to authentication methods. 681 * Only valid during the go_with() function. 682 */ 683 const char **best_sel; 684 685 /** 686 * Error hint to return on failure. Set if @e ec is not #TALER_EC_NONE. 687 */ 688 const char *hint; 689 690 /** 691 * Policy we are currently building maps for. 692 */ 693 struct Policy *current_policy; 694 695 /** 696 * LL of costs associated with the currently preferred 697 * policy. 698 */ 699 struct Costs *best_cost; 700 701 /** 702 * Array of 'best' policy maps found so far, 703 * ordered by policy. 704 */ 705 struct PolicyMap *best_map; 706 707 /** 708 * Array of the currency policy maps under evaluation 709 * by find_best_map(). 710 */ 711 struct PolicyMap *curr_map; 712 713 /** 714 * How many mappings have we evaluated so far? 715 * Used to limit the computation by aborting after 716 * #MAX_EVALUATIONS trials. 717 */ 718 unsigned int evaluations; 719 720 /** 721 * Overall number of challenges provided by the user. 722 */ 723 unsigned int num_methods; 724 725 /** 726 * Number of challenges that must be satisfied to recover the secret. 727 * Derived from the total number of challenges entered by the user. 728 */ 729 unsigned int req_methods; 730 731 /** 732 * Number of different Anastasis providers selected in @e best_sel. 733 * Only valid during the go_with() function. 734 */ 735 unsigned int best_diversity; 736 737 /** 738 * Number of identical challenges duplicated at 739 * various providers in the best case. Smaller is 740 * better. 741 */ 742 unsigned int best_duplicates; 743 744 /** 745 * Error code to return, #TALER_EC_NONE on success. 746 */ 747 enum TALER_ErrorCode ec; 748 749 }; 750 751 752 /** 753 * Free @a costs LL. 754 * 755 * @param[in] costs linked list to free 756 */ 757 static void 758 free_costs (struct Costs *costs) 759 { 760 while (NULL != costs) 761 { 762 struct Costs *next = costs->next; 763 764 GNUNET_free (costs); 765 costs = next; 766 } 767 } 768 769 770 /** 771 * Check if providers @a p1 and @a p2 have equivalent 772 * methods and cost structures. 773 * 774 * @param pb policy builder with list of providers 775 * @param p1 name of provider to compare 776 * @param p2 name of provider to compare 777 * @return true if the providers are fully equivalent 778 */ 779 static bool 780 equiv_provider (const struct PolicyBuilder *pb, 781 const char *p1, 782 const char *p2) 783 { 784 const json_t *j1; 785 const json_t *j2; 786 const json_t *m1; 787 const json_t *m2; 788 struct TALER_Amount uc1; 789 struct TALER_Amount uc2; 790 791 j1 = json_object_get (pb->providers, 792 p1); 793 j2 = json_object_get (pb->providers, 794 p2); 795 if ( (NULL == j1) || 796 (NULL == j2) ) 797 { 798 GNUNET_break (0); 799 return false; 800 } 801 802 { 803 struct GNUNET_JSON_Specification s1[] = { 804 GNUNET_JSON_spec_array_const ("methods", 805 &m1), 806 TALER_JSON_spec_amount_any ("truth_upload_fee", 807 &uc1), 808 GNUNET_JSON_spec_end () 809 }; 810 811 if (GNUNET_OK != 812 GNUNET_JSON_parse (j1, 813 s1, 814 NULL, NULL)) 815 { 816 GNUNET_break (0); 817 return false; 818 } 819 } 820 821 { 822 struct GNUNET_JSON_Specification s2[] = { 823 GNUNET_JSON_spec_array_const ("methods", 824 &m2), 825 TALER_JSON_spec_amount_any ("truth_upload_fee", 826 &uc2), 827 GNUNET_JSON_spec_end () 828 }; 829 830 if (GNUNET_OK != 831 GNUNET_JSON_parse (j2, 832 s2, 833 NULL, NULL)) 834 { 835 GNUNET_break (0); 836 return false; 837 } 838 } 839 840 if ( (GNUNET_OK != 841 TALER_amount_cmp_currency (&uc1, 842 &uc2)) || 843 (0 != 844 TALER_amount_cmp (&uc1, 845 &uc2)) ) 846 return false; 847 848 if (json_array_size (m1) != json_array_size (m2)) 849 return false; 850 { 851 size_t idx1; 852 json_t *e1; 853 854 json_array_foreach (m1, idx1, e1) 855 { 856 const char *type1; 857 struct TALER_Amount fee1; 858 struct GNUNET_JSON_Specification s1[] = { 859 GNUNET_JSON_spec_string ("type", 860 &type1), 861 TALER_JSON_spec_amount_any ("usage_fee", 862 &fee1), 863 GNUNET_JSON_spec_end () 864 }; 865 bool matched = false; 866 867 if (GNUNET_OK != 868 GNUNET_JSON_parse (e1, 869 s1, 870 NULL, NULL)) 871 { 872 GNUNET_break (0); 873 return false; 874 } 875 { 876 size_t idx2; 877 json_t *e2; 878 879 json_array_foreach (m2, idx2, e2) 880 { 881 const char *type2; 882 struct TALER_Amount fee2; 883 struct GNUNET_JSON_Specification s2[] = { 884 GNUNET_JSON_spec_string ("type", 885 &type2), 886 TALER_JSON_spec_amount_any ("usage_fee", 887 &fee2), 888 GNUNET_JSON_spec_end () 889 }; 890 891 if (GNUNET_OK != 892 GNUNET_JSON_parse (e2, 893 s2, 894 NULL, NULL)) 895 { 896 GNUNET_break (0); 897 return false; 898 } 899 if ( (0 == strcmp (type1, 900 type2)) && 901 (GNUNET_OK == 902 TALER_amount_cmp_currency (&fee1, 903 &fee2)) && 904 (0 == TALER_amount_cmp (&fee1, 905 &fee2)) ) 906 { 907 matched = true; 908 break; 909 } 910 } 911 } 912 if (! matched) 913 return false; 914 } 915 } 916 return true; 917 } 918 919 920 /** 921 * Evaluate the cost/benefit of the provider selection in @a prov_sel 922 * and if it is better then the best known one in @a pb, update @a pb. 923 * 924 * @param[in,out] pb our operational context 925 * @param[in,out] prov_sel array of req_methods provider indices to complete 926 */ 927 static void 928 eval_provider_selection (struct PolicyBuilder *pb, 929 const char *prov_sel[]) 930 { 931 unsigned int curr_diversity; 932 struct PolicyEntry policy_ent[pb->req_methods]; 933 934 memset (policy_ent, 935 0, 936 sizeof (policy_ent)); 937 for (unsigned int i = 0; i < pb->req_methods; i++) 938 { 939 const json_t *method_obj = json_array_get (pb->methods, 940 pb->m_idx[i]); 941 const json_t *provider_cfg = json_object_get (pb->providers, 942 prov_sel[i]); 943 const json_t *provider_methods; 944 const char *method_type; 945 json_t *md; 946 size_t index; 947 bool found = false; 948 uint32_t size_limit_in_mb; 949 struct TALER_Amount upload_cost; 950 struct GNUNET_JSON_Specification pspec[] = { 951 GNUNET_JSON_spec_uint32 ("storage_limit_in_megabytes", 952 &size_limit_in_mb), 953 GNUNET_JSON_spec_array_const ("methods", 954 &provider_methods), 955 TALER_JSON_spec_amount_any ("truth_upload_fee", 956 &upload_cost), 957 GNUNET_JSON_spec_end () 958 }; 959 void *challenge; 960 size_t challenge_size; 961 struct GNUNET_JSON_Specification mspec[] = { 962 GNUNET_JSON_spec_string ("type", 963 &method_type), 964 GNUNET_JSON_spec_varsize ("challenge", 965 &challenge, 966 &challenge_size), 967 GNUNET_JSON_spec_end () 968 }; 969 970 policy_ent[i].provider_name = prov_sel[i]; 971 if (GNUNET_OK != 972 GNUNET_JSON_parse (method_obj, 973 mspec, 974 NULL, NULL)) 975 { 976 GNUNET_break (0); 977 pb->ec = TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID; 978 pb->hint = "'authentication_method' content malformed"; 979 goto cleanup; 980 } 981 982 if (MHD_HTTP_OK != 983 json_integer_value (json_object_get (provider_cfg, 984 "http_status"))) 985 { 986 GNUNET_JSON_parse_free (mspec); 987 goto cleanup; 988 } 989 if (GNUNET_OK != 990 GNUNET_JSON_parse (provider_cfg, 991 pspec, 992 NULL, NULL)) 993 { 994 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 995 "Skipping provider %s: no suitable configuration\n", 996 prov_sel[i]); 997 GNUNET_JSON_parse_free (mspec); 998 goto cleanup; 999 } 1000 json_array_foreach (provider_methods, index, md) 1001 { 1002 const char *type; 1003 struct TALER_Amount method_cost; 1004 struct GNUNET_JSON_Specification spec[] = { 1005 GNUNET_JSON_spec_string ("type", 1006 &type), 1007 TALER_JSON_spec_amount_any ("usage_fee", 1008 &method_cost), 1009 GNUNET_JSON_spec_end () 1010 }; 1011 1012 if (GNUNET_OK != 1013 GNUNET_JSON_parse (md, 1014 spec, 1015 NULL, NULL)) 1016 { 1017 GNUNET_break (0); 1018 pb->ec = TALER_EC_ANASTASIS_REDUCER_STATE_INVALID; 1019 pb->hint = "'methods' of provider"; 1020 goto cleanup; 1021 } 1022 if ( (0 == strcmp (type, 1023 method_type)) && 1024 (challenge_size_ok (size_limit_in_mb, 1025 challenge_size) ) ) 1026 { 1027 found = true; 1028 1029 add_cost (&policy_ent[i].usage_fee, 1030 &method_cost); 1031 add_cost (&policy_ent[i].usage_fee, 1032 &upload_cost); 1033 } 1034 } 1035 if (! found) 1036 { 1037 /* Provider does not OFFER this method, combination not possible. 1038 Cost is basically 'infinite', but we simply then skip this. */ 1039 GNUNET_JSON_parse_free (mspec); 1040 goto cleanup; 1041 } 1042 GNUNET_JSON_parse_free (mspec); 1043 } 1044 1045 /* calculate provider diversity by counting number of different 1046 providers selected */ 1047 curr_diversity = 0; 1048 for (unsigned int i = 0; i < pb->req_methods; i++) 1049 { 1050 bool found = false; 1051 1052 for (unsigned int j = 0; j < i; j++) 1053 { 1054 if (prov_sel[i] == prov_sel[j]) 1055 { 1056 found = true; 1057 break; 1058 } 1059 } 1060 if (! found) 1061 curr_diversity++; 1062 } 1063 #if DEBUG 1064 fprintf (stderr, 1065 "Diversity: %u (best: %u)\n", 1066 curr_diversity, 1067 pb->best_diversity); 1068 #endif 1069 if (curr_diversity < pb->best_diversity) 1070 { 1071 /* do not allow combinations that are bad 1072 for provider diversity */ 1073 goto cleanup; 1074 } 1075 if (curr_diversity > pb->best_diversity) 1076 { 1077 /* drop existing policies, they are all worse */ 1078 struct PolicyMap *m; 1079 1080 while (NULL != (m = pb->current_policy->pm_head)) 1081 { 1082 GNUNET_CONTAINER_DLL_remove (pb->current_policy->pm_head, 1083 pb->current_policy->pm_tail, 1084 m); 1085 for (unsigned int i = 0; i<pb->req_methods; i++) 1086 { 1087 free_costs (m->providers[i].usage_fee); 1088 m->providers[i].usage_fee = NULL; 1089 } 1090 GNUNET_free (m->providers); 1091 GNUNET_free (m); 1092 } 1093 pb->best_diversity = curr_diversity; 1094 } 1095 if (NULL == pb->p_head) 1096 { 1097 /* For the first policy, check for equivalent 1098 policy mapping existing: we 1099 do not want to do spend CPU time investigating 1100 purely equivalent permutations */ 1101 for (struct PolicyMap *m = pb->current_policy->pm_head; 1102 NULL != m; 1103 m = m->next) 1104 { 1105 bool equiv = true; 1106 for (unsigned int i = 0; i<pb->req_methods; i++) 1107 { 1108 if (! equiv_provider (pb, 1109 m->providers[i].provider_name, 1110 policy_ent[i].provider_name)) 1111 { 1112 equiv = false; 1113 break; 1114 } 1115 } 1116 if (equiv) 1117 { 1118 /* equivalent to known allocation */ 1119 goto cleanup; 1120 } 1121 } 1122 } 1123 1124 /* Add possible mapping to result list */ 1125 { 1126 struct PolicyMap *m; 1127 1128 m = GNUNET_new (struct PolicyMap); 1129 m->providers = GNUNET_new_array (pb->req_methods, 1130 struct PolicyEntry); 1131 memcpy (m->providers, 1132 policy_ent, 1133 sizeof (struct PolicyEntry) * pb->req_methods); 1134 m->diversity = curr_diversity; 1135 GNUNET_CONTAINER_DLL_insert (pb->current_policy->pm_head, 1136 pb->current_policy->pm_tail, 1137 m); 1138 } 1139 return; 1140 cleanup: 1141 for (unsigned int i = 0; i<pb->req_methods; i++) 1142 free_costs (policy_ent[i].usage_fee); 1143 } 1144 1145 1146 /** 1147 * Recursively compute possible combination(s) of provider candidates 1148 * in @e prov_sel. The selection is complete up to index @a i. Calls 1149 * eval_provider_selection() upon a feasible provider selection for 1150 * evaluation, resulting in "better" combinations being persisted in 1151 * @a pb. 1152 * 1153 * @param[in,out] pb our operational context 1154 * @param[in,out] prov_sel array of req_methods provider URLs to complete 1155 * @param i index up to which @a prov_sel is already initialized 1156 */ 1157 static void 1158 provider_candidate (struct PolicyBuilder *pb, 1159 const char *prov_sel[], 1160 unsigned int i) 1161 { 1162 const char *url; 1163 json_t *pconfig; 1164 1165 json_object_foreach (pb->providers, url, pconfig) 1166 { 1167 const char *status; 1168 uint32_t http_status = 0; 1169 struct GNUNET_JSON_Specification spec[] = { 1170 GNUNET_JSON_spec_string ("status", 1171 &status), 1172 GNUNET_JSON_spec_mark_optional ( 1173 GNUNET_JSON_spec_uint32 ("http_status", 1174 &http_status), 1175 NULL), 1176 GNUNET_JSON_spec_end () 1177 }; 1178 1179 if (GNUNET_OK != 1180 GNUNET_JSON_parse (pconfig, 1181 spec, 1182 NULL, NULL)) 1183 { 1184 GNUNET_break (0); 1185 continue; 1186 } 1187 if ( (MHD_HTTP_OK != http_status) || 1188 (0 == strcmp (status, 1189 "disabled")) ) 1190 { 1191 GNUNET_JSON_parse_free (spec); 1192 continue; 1193 } 1194 GNUNET_JSON_parse_free (spec); 1195 prov_sel[i] = url; 1196 if (i == pb->req_methods - 1) 1197 { 1198 eval_provider_selection (pb, 1199 prov_sel); 1200 if (TALER_EC_NONE != pb->ec) 1201 break; 1202 continue; 1203 } 1204 provider_candidate (pb, 1205 prov_sel, 1206 i + 1); 1207 } 1208 } 1209 1210 1211 /** 1212 * Using the selection of authentication methods from @a pb in 1213 * "m_idx", compute the best choice of providers. 1214 * 1215 * @param[in,out] pb our operational context 1216 */ 1217 static void 1218 go_with (struct PolicyBuilder *pb) 1219 { 1220 const char *prov_sel[pb->req_methods]; 1221 struct Policy *policy; 1222 1223 /* compute provider selection */ 1224 policy = GNUNET_new (struct Policy); 1225 policy->challenges = GNUNET_new_array (pb->req_methods, 1226 unsigned int); 1227 memcpy (policy->challenges, 1228 pb->m_idx, 1229 pb->req_methods * sizeof (unsigned int)); 1230 pb->current_policy = policy; 1231 pb->best_diversity = 0; 1232 provider_candidate (pb, 1233 prov_sel, 1234 0); 1235 GNUNET_CONTAINER_DLL_insert (pb->p_head, 1236 pb->p_tail, 1237 policy); 1238 pb->current_policy = NULL; 1239 } 1240 1241 1242 /** 1243 * Recursively computes all possible subsets of length "req_methods" 1244 * from an array of length "num_methods", calling "go_with" on each of 1245 * those subsets (in "m_idx"). 1246 * 1247 * @param[in,out] pb our operational context 1248 * @param i offset up to which the "m_idx" has been computed 1249 */ 1250 static void 1251 method_candidate (struct PolicyBuilder *pb, 1252 unsigned int i) 1253 { 1254 unsigned int start; 1255 unsigned int *m_idx = pb->m_idx; 1256 1257 start = (i > 0) ? m_idx[i - 1] + 1 : 0; 1258 for (unsigned int j = start; j < pb->num_methods; j++) 1259 { 1260 m_idx[i] = j; 1261 if (i == pb->req_methods - 1) 1262 { 1263 #if DEBUG 1264 fprintf (stderr, 1265 "Suggesting: "); 1266 for (unsigned int k = 0; k<pb->req_methods; k++) 1267 { 1268 fprintf (stderr, 1269 "%u ", 1270 m_idx[k]); 1271 } 1272 fprintf (stderr, "\n"); 1273 #endif 1274 go_with (pb); 1275 continue; 1276 } 1277 method_candidate (pb, 1278 i + 1); 1279 } 1280 } 1281 1282 1283 /** 1284 * Compare two cost lists. 1285 * 1286 * @param my cost to compare 1287 * @param be cost to compare 1288 * @return 0 if costs are estimated equal, 1289 * 1 if @a my < @a be 1290 * -1 if @a my > @a be 1291 */ 1292 static int 1293 compare_costs (const struct Costs *my, 1294 const struct Costs *be) 1295 { 1296 int ranking = 0; 1297 1298 for (const struct Costs *cmp = be; 1299 NULL != cmp; 1300 cmp = cmp->next) 1301 { 1302 bool found = false; 1303 1304 for (const struct Costs *pos = my; 1305 NULL != pos; 1306 pos = pos->next) 1307 { 1308 if (GNUNET_OK != 1309 TALER_amount_cmp_currency (&cmp->cost, 1310 &pos->cost)) 1311 continue; 1312 found = true; 1313 } 1314 if (! found) 1315 ranking--; /* new policy has no cost in this currency */ 1316 } 1317 1318 for (const struct Costs *pos = my; 1319 NULL != pos; 1320 pos = pos->next) 1321 { 1322 bool found = false; 1323 1324 for (const struct Costs *cmp = be; 1325 NULL != cmp; 1326 cmp = cmp->next) 1327 { 1328 if (GNUNET_OK != 1329 TALER_amount_cmp_currency (&cmp->cost, 1330 &pos->cost)) 1331 continue; 1332 found = true; 1333 switch (TALER_amount_cmp (&cmp->cost, 1334 &pos->cost)) 1335 { 1336 case -1: /* cmp < pos */ 1337 ranking--; 1338 break; 1339 case 0: 1340 break; 1341 case 1: /* cmp > pos */ 1342 ranking++; 1343 break; 1344 } 1345 break; 1346 } 1347 if (! found) 1348 ranking++; /* old policy has no cost in this currency */ 1349 } 1350 if (0 == ranking) 1351 return 0; 1352 return (0 > ranking) ? -1 : 1; 1353 } 1354 1355 1356 /** 1357 * Evaluate the combined policy map stack in the ``curr_map`` of @a pb 1358 * and compare to the current best cost. If we are better, save the 1359 * stack in the ``best_map``. 1360 * 1361 * @param[in,out] pb policy builder we evaluate for 1362 * @param num_policies length of the ``curr_map`` array 1363 */ 1364 static void 1365 evaluate_map (struct PolicyBuilder *pb, 1366 unsigned int num_policies) 1367 { 1368 struct Costs *my_cost = NULL; 1369 unsigned int i = 0; 1370 unsigned int duplicates = 0; 1371 int ccmp; 1372 1373 #if DEBUG 1374 fprintf (stderr, 1375 "Checking...\n"); 1376 #endif 1377 /* calculate cost */ 1378 for (const struct Policy *p = pb->p_head; 1379 NULL != p; 1380 p = p->next) 1381 { 1382 const struct PolicyMap *pm = &pb->curr_map[i++]; 1383 1384 #if DEBUG 1385 fprintf (stderr, 1386 "Evaluating %p (%u): ", 1387 p, 1388 pm->diversity); 1389 for (unsigned int k = 0; k<pb->req_methods; k++) 1390 { 1391 const struct PolicyEntry *pe = &pm->providers[k]; 1392 1393 fprintf (stderr, 1394 "%u->%s ", 1395 p->challenges[k], 1396 pe->provider_name); 1397 } 1398 fprintf (stderr, "\n"); 1399 #endif 1400 for (unsigned int j = 0; j<pb->req_methods; j++) 1401 { 1402 const struct PolicyEntry *pe = &pm->providers[j]; 1403 unsigned int cv = p->challenges[j]; 1404 bool found = false; 1405 unsigned int i2 = 0; 1406 1407 /* check for duplicates */ 1408 for (const struct Policy *p2 = pb->p_head; 1409 p2 != p; 1410 p2 = p2->next) 1411 { 1412 const struct PolicyMap *pm2 = &pb->curr_map[i2++]; 1413 1414 for (unsigned int j2 = 0; j2<pb->req_methods; j2++) 1415 { 1416 const struct PolicyEntry *pe2 = &pm2->providers[j2]; 1417 unsigned int cv2 = p2->challenges[j2]; 1418 1419 if (cv != cv2) 1420 continue; /* different challenge */ 1421 if (0 == strcmp (pe->provider_name, 1422 pe2->provider_name)) 1423 found = true; /* same challenge&provider! */ 1424 else 1425 duplicates++; /* penalty for same challenge at two providers */ 1426 } 1427 } 1428 if (! found) 1429 { 1430 add_costs (&my_cost, 1431 pe->usage_fee); 1432 } 1433 } 1434 } 1435 1436 ccmp = -1; /* non-zero if 'best_duplicates' is UINT_MAX */ 1437 if ( (UINT_MAX != pb->best_duplicates) && 1438 (0 > (ccmp = compare_costs (my_cost, 1439 pb->best_cost))) ) 1440 { 1441 /* new method not clearly better, do not use it */ 1442 free_costs (my_cost); 1443 #if DEBUG 1444 fprintf (stderr, 1445 "... useless\n"); 1446 #endif 1447 return; 1448 } 1449 if ( (0 == ccmp) && 1450 (duplicates > pb->best_duplicates) ) 1451 { 1452 /* new method is cost-equal, but looses on duplicates, 1453 do not use it */ 1454 free_costs (my_cost); 1455 #if DEBUG 1456 fprintf (stderr, 1457 "... useless\n"); 1458 #endif 1459 return; 1460 } 1461 /* new method is better (or first), set as best */ 1462 #if DEBUG 1463 fprintf (stderr, 1464 "New best: %u duplicates, %s cost\n", 1465 duplicates, 1466 TALER_amount2s (&my_cost->cost)); 1467 #endif 1468 free_costs (pb->best_cost); 1469 pb->best_cost = my_cost; 1470 pb->best_duplicates = duplicates; 1471 memcpy (pb->best_map, 1472 pb->curr_map, 1473 sizeof (struct PolicyMap) * num_policies); 1474 } 1475 1476 1477 /** 1478 * Try all policy maps for @a pos and evaluate the 1479 * resulting total cost, saving the best result in 1480 * @a pb. 1481 * 1482 * @param[in,out] pb policy builder context 1483 * @param pos policy we are currently looking at maps for 1484 * @param off index of @a pos for the policy map 1485 */ 1486 static void 1487 find_best_map (struct PolicyBuilder *pb, 1488 struct Policy *pos, 1489 unsigned int off) 1490 { 1491 if (NULL == pos) 1492 { 1493 evaluate_map (pb, 1494 off); 1495 pb->evaluations++; 1496 return; 1497 } 1498 for (struct PolicyMap *pm = pos->pm_head; 1499 NULL != pm; 1500 pm = pm->next) 1501 { 1502 pb->curr_map[off] = *pm; 1503 find_best_map (pb, 1504 pos->next, 1505 off + 1); 1506 if (pb->evaluations >= MAX_EVALUATIONS) 1507 break; 1508 } 1509 } 1510 1511 1512 /** 1513 * Select cheapest policy combinations and add them to the JSON ``policies`` 1514 * array in @a pb 1515 * 1516 * @param[in,out] pb policy builder with our state 1517 */ 1518 static void 1519 select_policies (struct PolicyBuilder *pb) 1520 { 1521 unsigned int cnt = 0; 1522 1523 for (struct Policy *p = pb->p_head; 1524 NULL != p; 1525 p = p->next) 1526 cnt++; 1527 { 1528 struct PolicyMap best[cnt]; 1529 struct PolicyMap curr[cnt]; 1530 unsigned int off; 1531 1532 pb->best_map = best; 1533 pb->curr_map = curr; 1534 pb->best_duplicates = UINT_MAX; /* worst */ 1535 find_best_map (pb, 1536 pb->p_head, 1537 0); 1538 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1539 "Assessed %u/%u policies\n", 1540 pb->evaluations, 1541 (unsigned int) MAX_EVALUATIONS); 1542 off = 0; 1543 for (struct Policy *p = pb->p_head; 1544 NULL != p; 1545 p = p->next) 1546 { 1547 struct PolicyMap *pm = &best[off++]; 1548 json_t *method_arr; 1549 1550 #if DEBUG 1551 fprintf (stderr, 1552 "Best map (%u): ", 1553 pm->diversity); 1554 for (unsigned int k = 0; k<pb->req_methods; k++) 1555 { 1556 fprintf (stderr, 1557 "%u->%s ", 1558 p->challenges[k], 1559 pm->providers[k].provider_name); 1560 } 1561 fprintf (stderr, "\n"); 1562 #endif 1563 /* Convert "best" selection into 'policies' array */ 1564 method_arr = json_array (); 1565 GNUNET_assert (NULL != method_arr); 1566 for (unsigned int i = 0; i < pb->req_methods; i++) 1567 { 1568 json_t *policy_method = GNUNET_JSON_PACK ( 1569 GNUNET_JSON_pack_uint64 ("authentication_method", 1570 p->challenges[i]), 1571 GNUNET_JSON_pack_string ("provider", 1572 pm->providers[i].provider_name)); 1573 1574 GNUNET_assert (0 == 1575 json_array_append_new (method_arr, 1576 policy_method)); 1577 } 1578 { 1579 json_t *policy = GNUNET_JSON_PACK ( 1580 GNUNET_JSON_pack_array_steal ( 1581 "methods", 1582 method_arr)); 1583 1584 GNUNET_assert (0 == 1585 json_array_append_new (pb->policies, 1586 policy)); 1587 } 1588 } 1589 } 1590 } 1591 1592 1593 /** 1594 * Clean up @a pb, in particular the policies DLL. 1595 * 1596 * @param[in] pb builder to clean up 1597 */ 1598 static void 1599 clean_pb (struct PolicyBuilder *pb) 1600 { 1601 struct Policy *p; 1602 1603 while (NULL != (p = pb->p_head)) 1604 { 1605 struct PolicyMap *pm; 1606 1607 while (NULL != (pm = p->pm_head)) 1608 { 1609 GNUNET_CONTAINER_DLL_remove (p->pm_head, 1610 p->pm_tail, 1611 pm); 1612 for (unsigned int i = 0; i<pb->req_methods; i++) 1613 free_costs (pm->providers[i].usage_fee); 1614 GNUNET_free (pm->providers); 1615 GNUNET_free (pm); 1616 } 1617 GNUNET_CONTAINER_DLL_remove (pb->p_head, 1618 pb->p_tail, 1619 p); 1620 GNUNET_free (p->challenges); 1621 GNUNET_free (p); 1622 } 1623 free_costs (pb->best_cost); 1624 } 1625 1626 1627 /** 1628 * DispatchHandler/Callback function which is called for a 1629 * "done_authentication" action. Automaticially computes policies 1630 * based on available Anastasis providers and challenges provided by 1631 * the user. 1632 * 1633 * @param state state to operate on 1634 * @param arguments arguments to use for operation on state 1635 * @param cb callback to call during/after operation 1636 * @param cb_cls callback closure 1637 * @return NULL 1638 */ 1639 static struct ANASTASIS_ReduxAction * 1640 done_authentication (json_t *state, 1641 const json_t *arguments, 1642 ANASTASIS_ActionCallback cb, 1643 void *cb_cls) 1644 { 1645 struct PolicyBuilder pb = { 1646 .ec = TALER_EC_NONE 1647 }; 1648 json_t *providers; 1649 json_t *policy_providers; 1650 1651 pb.providers = json_object_get (state, 1652 "authentication_providers"); 1653 if ( (NULL == pb.providers) || 1654 (! json_is_object (pb.providers) ) ) 1655 { 1656 ANASTASIS_redux_fail_ (cb, 1657 cb_cls, 1658 TALER_EC_ANASTASIS_REDUCER_STATE_INVALID, 1659 "'authentication_providers' must be provided"); 1660 return NULL; 1661 } 1662 pb.methods = json_object_get (state, 1663 "authentication_methods"); 1664 if ( (NULL == pb.methods) || 1665 (! json_is_array (pb.methods)) || 1666 (json_array_size (pb.methods) > UINT_MAX) ) 1667 { 1668 ANASTASIS_redux_fail_ (cb, 1669 cb_cls, 1670 TALER_EC_ANASTASIS_REDUCER_STATE_INVALID, 1671 "'authentication_methods' must be provided"); 1672 return NULL; 1673 } 1674 pb.num_methods 1675 = (unsigned int) json_array_size (pb.methods); 1676 switch (pb.num_methods) 1677 { 1678 case 0: 1679 ANASTASIS_redux_fail_ (cb, 1680 cb_cls, 1681 TALER_EC_ANASTASIS_REDUCER_STATE_INVALID, 1682 "'authentication_methods' must not be empty"); 1683 return NULL; 1684 case 1: 1685 ANASTASIS_redux_fail_ (cb, 1686 cb_cls, 1687 TALER_EC_ANASTASIS_REDUCER_STATE_INVALID, 1688 "Two factor authentication (2-FA) is required"); 1689 return NULL; 1690 case 2: 1691 pb.req_methods = pb.num_methods; 1692 break; 1693 case 3: 1694 case 4: 1695 pb.req_methods = pb.num_methods - 1; 1696 break; 1697 case 5: 1698 case 6: 1699 pb.req_methods = pb.num_methods - 2; 1700 break; 1701 case 7: 1702 pb.req_methods = pb.num_methods - 3; 1703 break; 1704 default: 1705 /* cap at 4 for auto-generation, algorithm 1706 to compute mapping gets too expensive 1707 otherwise. */ 1708 pb.req_methods = 4; 1709 break; 1710 } 1711 { 1712 unsigned int m_idx[pb.req_methods]; 1713 1714 /* select req_methods from num_methods. */ 1715 pb.m_idx = m_idx; 1716 method_candidate (&pb, 1717 0); 1718 } 1719 pb.policies = json_array (); 1720 select_policies (&pb); 1721 clean_pb (&pb); 1722 if (TALER_EC_NONE != pb.ec) 1723 { 1724 json_decref (pb.policies); 1725 ANASTASIS_redux_fail_ (cb, 1726 cb_cls, 1727 pb.ec, 1728 pb.hint); 1729 return NULL; 1730 } 1731 GNUNET_assert (0 == 1732 json_object_set_new (state, 1733 "policies", 1734 pb.policies)); 1735 providers = json_object_get (arguments, 1736 "providers"); 1737 if (NULL == providers) 1738 { 1739 /* Setup a providers array from all working providers */ 1740 json_t *available = json_object_get (state, 1741 "authentication_providers"); 1742 const char *url; 1743 json_t *details; 1744 1745 policy_providers = json_array (); 1746 GNUNET_assert (NULL != policy_providers); 1747 json_object_foreach (available, url, details) 1748 { 1749 json_t *provider; 1750 struct ANASTASIS_CRYPTO_ProviderSaltP salt; 1751 1752 if (GNUNET_OK != 1753 ANASTASIS_reducer_lookup_salt (state, 1754 url, 1755 &salt)) 1756 continue; /* skip providers that are down */ 1757 provider = GNUNET_JSON_PACK ( 1758 GNUNET_JSON_pack_string ("provider_url", 1759 url)); 1760 GNUNET_assert (NULL != provider); 1761 GNUNET_assert (0 == 1762 json_array_append_new (policy_providers, 1763 provider)); 1764 } 1765 } 1766 else 1767 { 1768 /* Setup a providers array from all working providers */ 1769 size_t off; 1770 json_t *url; 1771 1772 policy_providers = json_array (); 1773 json_array_foreach (providers, off, url) 1774 { 1775 json_t *provider; 1776 struct ANASTASIS_CRYPTO_ProviderSaltP salt; 1777 const char *url_str; 1778 1779 url_str = json_string_value (url); 1780 if ( (NULL == url_str) || 1781 (GNUNET_OK != 1782 ANASTASIS_reducer_lookup_salt (state, 1783 url_str, 1784 &salt)) ) 1785 { 1786 GNUNET_break (0); 1787 ANASTASIS_redux_fail_ (cb, 1788 cb_cls, 1789 TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID, 1790 "unworkable provider requested"); 1791 return NULL; 1792 } 1793 provider = GNUNET_JSON_PACK ( 1794 GNUNET_JSON_pack_string ("provider_url", 1795 url_str)); 1796 GNUNET_assert (0 == 1797 json_array_append_new (policy_providers, 1798 provider)); 1799 } 1800 } 1801 if (0 == json_array_size (policy_providers)) 1802 { 1803 json_decref (policy_providers); 1804 ANASTASIS_redux_fail_ (cb, 1805 cb_cls, 1806 TALER_EC_ANASTASIS_REDUCER_STATE_INVALID, 1807 "no workable providers in state"); 1808 return NULL; 1809 } 1810 GNUNET_assert (0 == 1811 json_object_set_new (state, 1812 "policy_providers", 1813 policy_providers)); 1814 set_state (state, 1815 ANASTASIS_BACKUP_STATE_POLICIES_REVIEWING); 1816 cb (cb_cls, 1817 TALER_EC_NONE, 1818 state); 1819 return NULL; 1820 } 1821 1822 1823 /* ******************** add_provider ******************* */ 1824 1825 1826 /** 1827 * DispatchHandler/Callback function which is called for a 1828 * "add_provider" action. Adds another Anastasis provider 1829 * to the list of available providers for storing information. 1830 * 1831 * @param state state to operate on 1832 * @param arguments arguments with a provider URL to add 1833 * @param cb callback to call during/after operation 1834 * @param cb_cls callback closure 1835 */ 1836 static struct ANASTASIS_ReduxAction * 1837 add_provider (json_t *state, 1838 const json_t *arguments, 1839 ANASTASIS_ActionCallback cb, 1840 void *cb_cls) 1841 { 1842 if (ANASTASIS_add_provider_ (state, 1843 arguments, 1844 cb, 1845 cb_cls)) 1846 return NULL; 1847 return ANASTASIS_REDUX_backup_begin_ (state, 1848 NULL, 1849 cb, 1850 cb_cls); 1851 } 1852 1853 1854 /* ******************** add_policy ******************* */ 1855 1856 1857 /** 1858 * DispatchHandler/Callback function which is called for a 1859 * "add_policy" action. 1860 * 1861 * @param state state to operate on 1862 * @param arguments arguments to use for operation on state 1863 * @param cb callback to call during/after operation 1864 * @param cb_cls callback closure 1865 * @return NULL 1866 */ 1867 static struct ANASTASIS_ReduxAction * 1868 add_policy (json_t *state, 1869 const json_t *arguments, 1870 ANASTASIS_ActionCallback cb, 1871 void *cb_cls) 1872 { 1873 const json_t *arg_array; 1874 json_t *policies; 1875 const json_t *auth_providers; 1876 const json_t *auth_methods; 1877 json_t *methods; 1878 1879 if (NULL == arguments) 1880 { 1881 GNUNET_break (0); 1882 ANASTASIS_redux_fail_ (cb, 1883 cb_cls, 1884 TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID, 1885 "arguments missing"); 1886 return NULL; 1887 } 1888 arg_array = json_object_get (arguments, 1889 "policy"); 1890 if (! json_is_array (arg_array)) 1891 { 1892 GNUNET_break (0); 1893 ANASTASIS_redux_fail_ (cb, 1894 cb_cls, 1895 TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID, 1896 "'policy' not an array"); 1897 return NULL; 1898 } 1899 policies = json_object_get (state, 1900 "policies"); 1901 if (! json_is_array (policies)) 1902 { 1903 GNUNET_break (0); 1904 ANASTASIS_redux_fail_ (cb, 1905 cb_cls, 1906 TALER_EC_ANASTASIS_REDUCER_STATE_INVALID, 1907 "'policies' not an array"); 1908 return NULL; 1909 } 1910 auth_providers = json_object_get (state, 1911 "authentication_providers"); 1912 if (! json_is_object (auth_providers)) 1913 { 1914 GNUNET_break (0); 1915 ANASTASIS_redux_fail_ (cb, 1916 cb_cls, 1917 TALER_EC_ANASTASIS_REDUCER_STATE_INVALID, 1918 "'auth_providers' not an object"); 1919 return NULL; 1920 } 1921 auth_methods = json_object_get (state, 1922 "authentication_methods"); 1923 if (! json_is_array (auth_methods)) 1924 { 1925 GNUNET_break (0); 1926 ANASTASIS_redux_fail_ (cb, 1927 cb_cls, 1928 TALER_EC_ANASTASIS_REDUCER_STATE_INVALID, 1929 "'auth_methods' not an array"); 1930 return NULL; 1931 } 1932 1933 methods = json_array (); 1934 GNUNET_assert (NULL != methods); 1935 1936 /* Add all methods from 'arg_array' to 'methods' */ 1937 { 1938 size_t aindex; 1939 json_t *method; 1940 1941 json_array_foreach (arg_array, aindex, method) 1942 { 1943 const char *provider_url; 1944 uint32_t method_idx; 1945 const char *method_type; 1946 const json_t *prov_methods; 1947 struct GNUNET_JSON_Specification ispec[] = { 1948 GNUNET_JSON_spec_string ("provider", 1949 &provider_url), 1950 GNUNET_JSON_spec_uint32 ("authentication_method", 1951 &method_idx), 1952 GNUNET_JSON_spec_end () 1953 }; 1954 1955 if (GNUNET_OK != 1956 GNUNET_JSON_parse (method, 1957 ispec, 1958 NULL, NULL)) 1959 { 1960 GNUNET_break (0); 1961 json_decref (methods); 1962 ANASTASIS_redux_fail_ (cb, 1963 cb_cls, 1964 TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID, 1965 "'method' details malformed"); 1966 return NULL; 1967 } 1968 1969 { 1970 const json_t *prov_cfg; 1971 uint32_t limit; 1972 const char *status; 1973 uint32_t http_status = 0; 1974 struct GNUNET_JSON_Specification spec[] = { 1975 GNUNET_JSON_spec_string ("status", 1976 &status), 1977 GNUNET_JSON_spec_mark_optional ( 1978 GNUNET_JSON_spec_uint32 ("http_status", 1979 &http_status), 1980 NULL), 1981 GNUNET_JSON_spec_uint32 ("storage_limit_in_megabytes", 1982 &limit), 1983 GNUNET_JSON_spec_array_const ("methods", 1984 &prov_methods), 1985 GNUNET_JSON_spec_end () 1986 }; 1987 1988 prov_cfg = json_object_get (auth_providers, 1989 provider_url); 1990 if (NULL == prov_cfg) 1991 { 1992 GNUNET_break (0); 1993 json_decref (methods); 1994 ANASTASIS_redux_fail_ (cb, 1995 cb_cls, 1996 TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID, 1997 "provider URL unknown"); 1998 return NULL; 1999 } 2000 if (GNUNET_OK != 2001 GNUNET_JSON_parse (prov_cfg, 2002 spec, 2003 NULL, NULL)) 2004 { 2005 /* skip provider, likely was down */ 2006 json_decref (methods); 2007 continue; 2008 } 2009 if ( (MHD_HTTP_OK != http_status) || 2010 (0 != strcmp (status, 2011 "ok")) ) 2012 { 2013 /* skip provider, disabled or down */ 2014 json_decref (methods); 2015 continue; 2016 } 2017 } 2018 2019 { 2020 const json_t *auth_method; 2021 2022 auth_method = json_array_get (auth_methods, 2023 method_idx); 2024 if (NULL == auth_method) 2025 { 2026 GNUNET_break (0); 2027 json_decref (methods); 2028 ANASTASIS_redux_fail_ (cb, 2029 cb_cls, 2030 TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID, 2031 "authentication method unknown"); 2032 return NULL; 2033 } 2034 method_type = json_string_value (json_object_get (auth_method, 2035 "type")); 2036 if (NULL == method_type) 2037 { 2038 GNUNET_break (0); 2039 json_decref (methods); 2040 ANASTASIS_redux_fail_ (cb, 2041 cb_cls, 2042 TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID, 2043 "authentication method must be a string"); 2044 return NULL; 2045 } 2046 } 2047 2048 { 2049 bool found = false; 2050 size_t mindex; 2051 json_t *pm; 2052 json_array_foreach (prov_methods, mindex, pm) 2053 { 2054 struct TALER_Amount method_cost; 2055 const char *type; 2056 struct GNUNET_JSON_Specification spec[] = { 2057 GNUNET_JSON_spec_string ("type", 2058 &type), 2059 TALER_JSON_spec_amount_any ("usage_fee", 2060 &method_cost), 2061 GNUNET_JSON_spec_end () 2062 }; 2063 2064 if (GNUNET_OK != 2065 GNUNET_JSON_parse (pm, 2066 spec, 2067 NULL, NULL)) 2068 { 2069 GNUNET_break (0); 2070 json_decref (methods); 2071 ANASTASIS_redux_fail_ (cb, 2072 cb_cls, 2073 TALER_EC_ANASTASIS_REDUCER_STATE_INVALID, 2074 "provider authentication method specification invalid"); 2075 return NULL; 2076 } 2077 if (0 != strcmp (type, 2078 method_type)) 2079 continue; 2080 found = true; 2081 break; 2082 } 2083 if (! found) 2084 { 2085 GNUNET_break (0); 2086 json_decref (methods); 2087 ANASTASIS_redux_fail_ (cb, 2088 cb_cls, 2089 TALER_EC_ANASTASIS_REDUCER_STATE_INVALID, 2090 "selected provider does not support authentication method"); 2091 return NULL; 2092 } 2093 } 2094 GNUNET_assert (0 == 2095 json_array_append (methods, 2096 method)); 2097 } /* end of json_array_foreach (arg_array, mindex, method) */ 2098 } 2099 2100 /* add new policy to array of existing policies */ 2101 { 2102 json_t *policy; 2103 json_t *idx; 2104 2105 policy = GNUNET_JSON_PACK ( 2106 GNUNET_JSON_pack_array_steal ("methods", 2107 methods)); 2108 idx = json_object_get (arguments, 2109 "policy_index"); 2110 if ( (NULL == idx) || 2111 (! json_is_integer (idx)) ) 2112 { 2113 GNUNET_assert (0 == 2114 json_array_append_new (policies, 2115 policy)); 2116 } 2117 else 2118 { 2119 GNUNET_assert (0 == 2120 json_array_insert_new (policies, 2121 json_integer_value (idx), 2122 policy)); 2123 } 2124 } 2125 2126 cb (cb_cls, 2127 TALER_EC_NONE, 2128 state); 2129 return NULL; 2130 } 2131 2132 2133 /* ******************** update_policy ******************* */ 2134 2135 2136 /** 2137 * DispatchHandler/Callback function which is called for a 2138 * "update_policy" action. 2139 * 2140 * @param state state to operate on 2141 * @param arguments arguments to use for operation on state 2142 * @param cb callback to call during/after operation 2143 * @param cb_cls callback closure 2144 * @return NULL 2145 */ 2146 static struct ANASTASIS_ReduxAction * 2147 update_policy (json_t *state, 2148 const json_t *arguments, 2149 ANASTASIS_ActionCallback cb, 2150 void *cb_cls) 2151 { 2152 const json_t *idx; 2153 size_t index; 2154 json_t *policy_arr; 2155 2156 if (NULL == arguments) 2157 { 2158 GNUNET_break (0); 2159 ANASTASIS_redux_fail_ (cb, 2160 cb_cls, 2161 TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID, 2162 "arguments missing"); 2163 return NULL; 2164 } 2165 idx = json_object_get (arguments, 2166 "policy_index"); 2167 if (! json_is_integer (idx)) 2168 { 2169 GNUNET_break (0); 2170 ANASTASIS_redux_fail_ (cb, 2171 cb_cls, 2172 TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID, 2173 "'policy_index' must be an integer"); 2174 return NULL; 2175 } 2176 index = json_integer_value (idx); 2177 policy_arr = json_object_get (state, 2178 "policies"); 2179 if (! json_is_array (policy_arr)) 2180 { 2181 GNUNET_break (0); 2182 ANASTASIS_redux_fail_ (cb, 2183 cb_cls, 2184 TALER_EC_ANASTASIS_REDUCER_STATE_INVALID, 2185 "'policies' must be an array"); 2186 return NULL; 2187 } 2188 if (0 != json_array_remove (policy_arr, 2189 index)) 2190 { 2191 GNUNET_break (0); 2192 ANASTASIS_redux_fail_ (cb, 2193 cb_cls, 2194 TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID_FOR_STATE, 2195 "removal failed"); 2196 return NULL; 2197 } 2198 return add_policy (state, 2199 arguments, 2200 cb, 2201 cb_cls); 2202 } 2203 2204 2205 /* ******************** del_policy ******************* */ 2206 2207 2208 /** 2209 * DispatchHandler/Callback function which is called for a 2210 * "delete_policy" action. 2211 * 2212 * @param state state to operate on 2213 * @param arguments arguments to use for operation on state 2214 * @param cb callback to call during/after operation 2215 * @param cb_cls callback closure 2216 * @return NULL 2217 */ 2218 static struct ANASTASIS_ReduxAction * 2219 del_policy (json_t *state, 2220 const json_t *arguments, 2221 ANASTASIS_ActionCallback cb, 2222 void *cb_cls) 2223 { 2224 const json_t *idx; 2225 size_t index; 2226 json_t *policy_arr; 2227 2228 if (NULL == arguments) 2229 { 2230 ANASTASIS_redux_fail_ (cb, 2231 cb_cls, 2232 TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID, 2233 "arguments missing"); 2234 return NULL; 2235 } 2236 idx = json_object_get (arguments, 2237 "policy_index"); 2238 if (! json_is_integer (idx)) 2239 { 2240 ANASTASIS_redux_fail_ (cb, 2241 cb_cls, 2242 TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID, 2243 "'policy_index' must be an integer"); 2244 return NULL; 2245 } 2246 index = json_integer_value (idx); 2247 policy_arr = json_object_get (state, 2248 "policies"); 2249 if (! json_is_array (policy_arr)) 2250 { 2251 ANASTASIS_redux_fail_ (cb, 2252 cb_cls, 2253 TALER_EC_ANASTASIS_REDUCER_STATE_INVALID, 2254 "'policies' must be an array"); 2255 return NULL; 2256 } 2257 if (0 != json_array_remove (policy_arr, 2258 index)) 2259 { 2260 ANASTASIS_redux_fail_ (cb, 2261 cb_cls, 2262 TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID_FOR_STATE, 2263 "removal failed"); 2264 return NULL; 2265 } 2266 cb (cb_cls, 2267 TALER_EC_NONE, 2268 state); 2269 return NULL; 2270 } 2271 2272 2273 /* ******************** del_challenge ******************* */ 2274 2275 2276 /** 2277 * DispatchHandler/Callback function which is called for a 2278 * "delete_challenge" action. 2279 * 2280 * @param state state to operate on 2281 * @param arguments arguments to use for operation on state 2282 * @param cb callback to call during/after operation 2283 * @param cb_cls callback closure 2284 * @return NULL 2285 */ 2286 static struct ANASTASIS_ReduxAction * 2287 del_challenge (json_t *state, 2288 const json_t *arguments, 2289 ANASTASIS_ActionCallback cb, 2290 void *cb_cls) 2291 { 2292 const json_t *pidx; 2293 const json_t *cidx; 2294 size_t index; 2295 json_t *policy_arr; 2296 json_t *policy; 2297 json_t *method_arr; 2298 2299 if (NULL == arguments) 2300 { 2301 ANASTASIS_redux_fail_ (cb, 2302 cb_cls, 2303 TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID, 2304 "arguments missing"); 2305 return NULL; 2306 } 2307 pidx = json_object_get (arguments, 2308 "policy_index"); 2309 cidx = json_object_get (arguments, 2310 "challenge_index"); 2311 if (! json_is_integer (pidx)) 2312 { 2313 ANASTASIS_redux_fail_ (cb, 2314 cb_cls, 2315 TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID, 2316 "'policy_index' must be an integer"); 2317 return NULL; 2318 } 2319 if (! json_is_integer (cidx)) 2320 { 2321 ANASTASIS_redux_fail_ (cb, 2322 cb_cls, 2323 TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID, 2324 "'challenge_index' must be an integer"); 2325 return NULL; 2326 } 2327 index = json_integer_value (pidx); 2328 policy_arr = json_object_get (state, 2329 "policies"); 2330 if (! json_is_array (policy_arr)) 2331 { 2332 ANASTASIS_redux_fail_ (cb, 2333 cb_cls, 2334 TALER_EC_ANASTASIS_REDUCER_STATE_INVALID, 2335 "'policies' must be an array"); 2336 return NULL; 2337 } 2338 policy = json_array_get (policy_arr, 2339 index); 2340 if (NULL == policy) 2341 { 2342 ANASTASIS_redux_fail_ (cb, 2343 cb_cls, 2344 TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID, 2345 "'policy_index' out of range"); 2346 return NULL; 2347 } 2348 method_arr = json_object_get (policy, 2349 "methods"); 2350 if (NULL == method_arr) 2351 { 2352 ANASTASIS_redux_fail_ (cb, 2353 cb_cls, 2354 TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID_FOR_STATE, 2355 "methods missing in policy"); 2356 return NULL; 2357 } 2358 index = json_integer_value (cidx); 2359 if (0 != json_array_remove (method_arr, 2360 index)) 2361 { 2362 ANASTASIS_redux_fail_ (cb, 2363 cb_cls, 2364 TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID_FOR_STATE, 2365 "removal failed"); 2366 return NULL; 2367 } 2368 cb (cb_cls, 2369 TALER_EC_NONE, 2370 state); 2371 return NULL; 2372 } 2373 2374 2375 /* ********************** done_policy_review ***************** */ 2376 2377 2378 /** 2379 * Calculate how many years of service we need 2380 * from the desired @a expiration time, 2381 * rounding up. 2382 * 2383 * @param expiration desired expiration time 2384 * @return number of years of service to pay for 2385 */ 2386 static unsigned int 2387 expiration_to_years (struct GNUNET_TIME_Timestamp expiration) 2388 { 2389 struct GNUNET_TIME_Relative rem; 2390 unsigned int years; 2391 2392 rem = GNUNET_TIME_absolute_get_remaining (expiration.abs_time); 2393 years = rem.rel_value_us / GNUNET_TIME_UNIT_YEARS.rel_value_us; 2394 if (0 != rem.rel_value_us % GNUNET_TIME_UNIT_YEARS.rel_value_us) 2395 years++; 2396 return years; 2397 } 2398 2399 2400 /** 2401 * Update @a state such that the earliest expiration for 2402 * any truth or policy is @a expiration. Recalculate 2403 * the ``upload_fees`` array with the associated costs. 2404 * 2405 * @param[in,out] state our state to update 2406 * @param expiration new expiration to enforce 2407 * @return #GNUNET_OK on success, 2408 * #GNUNET_SYSERR if the state is invalid 2409 */ 2410 static enum GNUNET_GenericReturnValue 2411 update_expiration_cost (json_t *state, 2412 struct GNUNET_TIME_Timestamp expiration) 2413 { 2414 struct Costs *costs = NULL; 2415 unsigned int years; 2416 json_t *providers; 2417 bool is_free = true; 2418 2419 providers = json_object_get (state, 2420 "authentication_providers"); 2421 if (! json_is_object (providers)) 2422 { 2423 GNUNET_break (0); 2424 return GNUNET_SYSERR; 2425 } 2426 2427 years = expiration_to_years (expiration); 2428 2429 /* go over all providers and add up cost */ 2430 { 2431 const char *url; 2432 json_t *provider; 2433 2434 json_object_foreach (providers, url, provider) 2435 { 2436 struct TALER_Amount annual_fee; 2437 const char *status; 2438 uint32_t http_status = 0; 2439 struct GNUNET_JSON_Specification pspec[] = { 2440 GNUNET_JSON_spec_string ("status", 2441 &status), 2442 GNUNET_JSON_spec_mark_optional ( 2443 GNUNET_JSON_spec_uint32 ("http_status", 2444 &http_status), 2445 NULL), 2446 TALER_JSON_spec_amount_any ("annual_fee", 2447 &annual_fee), 2448 GNUNET_JSON_spec_end () 2449 }; 2450 struct TALER_Amount fee; 2451 2452 if (GNUNET_OK != 2453 GNUNET_JSON_parse (provider, 2454 pspec, 2455 NULL, NULL)) 2456 { 2457 /* likely down, skip */ 2458 continue; 2459 } 2460 if ( (MHD_HTTP_OK != http_status) || 2461 (0 != strcmp (status, 2462 "ok")) ) 2463 continue; /* skip providers that are down or disabled */ 2464 if (0 > 2465 TALER_amount_multiply (&fee, 2466 &annual_fee, 2467 years)) 2468 { 2469 GNUNET_break (0); 2470 return GNUNET_SYSERR; 2471 } 2472 add_cost (&costs, 2473 &fee); 2474 } 2475 } 2476 2477 /* go over all truths and add up cost */ 2478 { 2479 unsigned int off = 0; 2480 unsigned int len = 0; 2481 struct AlreadySeen 2482 { 2483 uint32_t method; 2484 const char *provider_url; 2485 } *seen = NULL; 2486 json_t *policies; 2487 size_t pidx; 2488 json_t *policy; 2489 2490 policies = json_object_get (state, 2491 "policies"); 2492 json_array_foreach (policies, pidx, policy) 2493 { 2494 json_t *methods; 2495 json_t *method; 2496 size_t midx; 2497 2498 methods = json_object_get (policy, 2499 "methods"); 2500 json_array_foreach (methods, midx, method) 2501 { 2502 const char *provider_url; 2503 uint32_t method_idx; 2504 2505 struct GNUNET_JSON_Specification spec[] = { 2506 GNUNET_JSON_spec_string ("provider", 2507 &provider_url), 2508 GNUNET_JSON_spec_uint32 ("authentication_method", 2509 &method_idx), 2510 GNUNET_JSON_spec_end () 2511 }; 2512 2513 if (GNUNET_OK != 2514 GNUNET_JSON_parse (method, 2515 spec, 2516 NULL, NULL)) 2517 { 2518 GNUNET_break (0); 2519 return GNUNET_SYSERR; 2520 } 2521 /* check if we have seen this one before */ 2522 { 2523 bool found = false; 2524 2525 for (unsigned int i = 0; i<off; i++) 2526 if ( (seen[i].method == method_idx) && 2527 (0 == strcmp (seen[i].provider_url, 2528 provider_url)) ) 2529 found = true; 2530 if (found) 2531 continue; /* skip */ 2532 } 2533 if (off == len) 2534 { 2535 GNUNET_array_grow (seen, 2536 len, 2537 4 + len * 2); 2538 } 2539 seen[off].method = method_idx; 2540 seen[off].provider_url = provider_url; 2541 off++; 2542 { 2543 struct TALER_Amount upload_cost; 2544 const char *status; 2545 uint32_t http_status = 0; 2546 struct GNUNET_JSON_Specification pspec[] = { 2547 GNUNET_JSON_spec_string ("status", 2548 &status), 2549 GNUNET_JSON_spec_mark_optional ( 2550 GNUNET_JSON_spec_uint32 ("http_status", 2551 &http_status), 2552 NULL), 2553 TALER_JSON_spec_amount_any ("truth_upload_fee", 2554 &upload_cost), 2555 GNUNET_JSON_spec_end () 2556 }; 2557 struct TALER_Amount fee; 2558 const json_t *provider_cfg 2559 = json_object_get (providers, 2560 provider_url); 2561 2562 if (GNUNET_OK != 2563 GNUNET_JSON_parse (provider_cfg, 2564 pspec, 2565 NULL, NULL)) 2566 { 2567 GNUNET_break (0); 2568 return GNUNET_SYSERR; 2569 } 2570 if ( (MHD_HTTP_OK != http_status) || 2571 (0 != strcmp (status, 2572 "ok")) ) 2573 { 2574 GNUNET_break (0); 2575 return GNUNET_SYSERR; 2576 } 2577 if (0 > 2578 TALER_amount_multiply (&fee, 2579 &upload_cost, 2580 years)) 2581 { 2582 GNUNET_break (0); 2583 return GNUNET_SYSERR; 2584 } 2585 add_cost (&costs, 2586 &fee); 2587 } 2588 } 2589 } 2590 GNUNET_array_grow (seen, 2591 len, 2592 0); 2593 } 2594 2595 /* convert 'costs' into state */ 2596 { 2597 json_t *arr; 2598 2599 arr = json_array (); 2600 GNUNET_assert (NULL != arr); 2601 while (NULL != costs) 2602 { 2603 struct Costs *nxt = costs->next; 2604 2605 if (! TALER_amount_is_zero (&costs->cost)) 2606 { 2607 json_t *ao; 2608 2609 ao = GNUNET_JSON_PACK ( 2610 TALER_JSON_pack_amount ("fee", 2611 &costs->cost)); 2612 GNUNET_assert (0 == 2613 json_array_append_new (arr, 2614 ao)); 2615 is_free = false; 2616 } 2617 GNUNET_free (costs); 2618 costs = nxt; 2619 } 2620 GNUNET_assert (0 == 2621 json_object_set_new (state, 2622 "upload_fees", 2623 arr)); 2624 } 2625 2626 if (is_free) 2627 expiration = GNUNET_TIME_relative_to_timestamp (ANASTASIS_FREE_STORAGE); 2628 /* update 'expiration' in state */ 2629 { 2630 json_t *eo; 2631 2632 eo = GNUNET_JSON_from_timestamp (expiration); 2633 GNUNET_assert (0 == 2634 json_object_set_new (state, 2635 "expiration", 2636 eo)); 2637 } 2638 2639 2640 return GNUNET_OK; 2641 } 2642 2643 2644 /** 2645 * DispatchHandler/Callback function which is called for a 2646 * "done_policy_review" action. 2647 * 2648 * @param state state to operate on 2649 * @param arguments arguments to use for operation on state 2650 * @param cb callback to call during/after operation 2651 * @param cb_cls callback closure 2652 * @return NULL 2653 */ 2654 static struct ANASTASIS_ReduxAction * 2655 done_policy_review (json_t *state, 2656 const json_t *arguments, 2657 ANASTASIS_ActionCallback cb, 2658 void *cb_cls) 2659 { 2660 const json_t *policy_arr; 2661 2662 policy_arr = json_object_get (state, 2663 "policies"); 2664 if (0 == json_array_size (policy_arr)) 2665 { 2666 ANASTASIS_redux_fail_ (cb, 2667 cb_cls, 2668 TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID_FOR_STATE, 2669 "no policies specified"); 2670 return NULL; 2671 } 2672 { 2673 struct GNUNET_TIME_Timestamp exp 2674 = GNUNET_TIME_UNIT_ZERO_TS; 2675 struct GNUNET_JSON_Specification spec[] = { 2676 GNUNET_JSON_spec_mark_optional ( 2677 GNUNET_JSON_spec_timestamp ("expiration", 2678 &exp), 2679 NULL), 2680 GNUNET_JSON_spec_end () 2681 }; 2682 2683 if (GNUNET_OK != 2684 GNUNET_JSON_parse (state, 2685 spec, 2686 NULL, NULL)) 2687 { 2688 ANASTASIS_redux_fail_ (cb, 2689 cb_cls, 2690 TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID_FOR_STATE, 2691 "invalid expiration specified"); 2692 return NULL; 2693 } 2694 if (GNUNET_TIME_absolute_is_zero (exp.abs_time)) 2695 exp = GNUNET_TIME_relative_to_timestamp (GNUNET_TIME_UNIT_YEARS); 2696 if (GNUNET_OK != 2697 update_expiration_cost (state, 2698 exp)) 2699 { 2700 ANASTASIS_redux_fail_ (cb, 2701 cb_cls, 2702 TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID_FOR_STATE, 2703 "could not calculate expiration cost"); 2704 return NULL; 2705 } 2706 } 2707 set_state (state, 2708 ANASTASIS_BACKUP_STATE_SECRET_EDITING); 2709 cb (cb_cls, 2710 TALER_EC_NONE, 2711 state); 2712 return NULL; 2713 } 2714 2715 2716 /** 2717 * Information we keep for an upload() operation. 2718 */ 2719 struct UploadContext; 2720 2721 2722 /** 2723 * Maps a TruthUpload to a policy and recovery method where this 2724 * truth is used. 2725 */ 2726 struct PolicyMethodReference 2727 { 2728 /** 2729 * Offset into the "policies" array. 2730 */ 2731 unsigned int policy_index; 2732 2733 /** 2734 * Offset into the "methods" array (of the policy selected 2735 * by @e policy_index). 2736 */ 2737 unsigned int method_index; 2738 2739 }; 2740 2741 2742 /** 2743 * Entry we keep per truth upload. 2744 */ 2745 struct TruthUpload 2746 { 2747 2748 /** 2749 * Kept in a DLL. 2750 */ 2751 struct TruthUpload *next; 2752 2753 /** 2754 * Kept in a DLL. 2755 */ 2756 struct TruthUpload *prev; 2757 2758 /** 2759 * Handle to the actual upload operation. 2760 */ 2761 struct ANASTASIS_TruthUpload *tu; 2762 2763 /** 2764 * Upload context this operation is part of. 2765 */ 2766 struct UploadContext *uc; 2767 2768 /** 2769 * Truth resulting from the upload, if any. 2770 */ 2771 struct ANASTASIS_Truth *t; 2772 2773 /** 2774 * A taler://pay/-URI with a request to pay the annual fee for 2775 * the service. Set if payment is required. 2776 */ 2777 char *payment_request; 2778 2779 /** 2780 * Which policies and methods does this truth affect? 2781 */ 2782 struct PolicyMethodReference *policies; 2783 2784 /** 2785 * Where are we uploading to? 2786 */ 2787 char *provider_url; 2788 2789 /** 2790 * Which challenge object are we uploading? 2791 */ 2792 uint32_t am_idx; 2793 2794 /** 2795 * Length of the @e policies array. 2796 */ 2797 unsigned int policies_length; 2798 2799 /** 2800 * Status of the upload. 2801 */ 2802 enum ANASTASIS_UploadStatus us; 2803 2804 /** 2805 * Taler error code of the upload. 2806 */ 2807 enum TALER_ErrorCode ec; 2808 2809 }; 2810 2811 2812 /** 2813 * Information we keep for an upload() operation. 2814 */ 2815 struct UploadContext 2816 { 2817 /** 2818 * Recovery action returned to caller for aborting the operation. 2819 */ 2820 struct ANASTASIS_ReduxAction ra; 2821 2822 /** 2823 * Function to call upon completion. 2824 */ 2825 ANASTASIS_ActionCallback cb; 2826 2827 /** 2828 * Closure for @e cb. 2829 */ 2830 void *cb_cls; 2831 2832 /** 2833 * Our state. 2834 */ 2835 json_t *state; 2836 2837 /** 2838 * Master secret sharing operation, NULL if not yet running. 2839 */ 2840 struct ANASTASIS_SecretShare *ss; 2841 2842 /** 2843 * Head of DLL of truth uploads. 2844 */ 2845 struct TruthUpload *tues_head; 2846 2847 /** 2848 * Tail of DLL of truth uploads. 2849 */ 2850 struct TruthUpload *tues_tail; 2851 2852 /** 2853 * Timeout to use for the operation, from the arguments. 2854 */ 2855 struct GNUNET_TIME_Relative timeout; 2856 2857 /** 2858 * For how many years should we pay? 2859 */ 2860 unsigned int years; 2861 2862 }; 2863 2864 2865 /** 2866 * Function called when the #upload transition is being aborted. 2867 * 2868 * @param cls a `struct UploadContext` 2869 */ 2870 static void 2871 upload_cancel_cb (void *cls) 2872 { 2873 struct UploadContext *uc = cls; 2874 struct TruthUpload *tue; 2875 2876 while (NULL != (tue = uc->tues_head)) 2877 { 2878 GNUNET_CONTAINER_DLL_remove (uc->tues_head, 2879 uc->tues_tail, 2880 tue); 2881 if (NULL != tue->tu) 2882 { 2883 ANASTASIS_truth_upload_cancel (tue->tu); 2884 tue->tu = NULL; 2885 } 2886 if (NULL != tue->t) 2887 { 2888 ANASTASIS_truth_free (tue->t); 2889 tue->t = NULL; 2890 } 2891 GNUNET_free (tue->provider_url); 2892 GNUNET_free (tue->payment_request); 2893 GNUNET_free (tue->policies); 2894 GNUNET_free (tue); 2895 } 2896 if (NULL != uc->ss) 2897 { 2898 ANASTASIS_secret_share_cancel (uc->ss); 2899 uc->ss = NULL; 2900 } 2901 json_decref (uc->state); 2902 GNUNET_free (uc); 2903 } 2904 2905 2906 /** 2907 * Take all of the ongoing truth uploads and serialize them into the @a uc 2908 * state. 2909 * 2910 * @param[in,out] uc context to take truth uploads from and to update state of 2911 */ 2912 static void 2913 serialize_truth (struct UploadContext *uc) 2914 { 2915 json_t *policies; 2916 2917 policies = json_object_get (uc->state, 2918 "policies"); 2919 GNUNET_assert (json_is_array (policies)); 2920 for (struct TruthUpload *tue = uc->tues_head; 2921 NULL != tue; 2922 tue = tue->next) 2923 { 2924 if (NULL == tue->t) 2925 continue; 2926 for (unsigned int i = 0; i<tue->policies_length; i++) 2927 { 2928 const struct PolicyMethodReference *pmr = &tue->policies[i]; 2929 json_t *policy = json_array_get (policies, 2930 pmr->policy_index); 2931 json_t *methods = json_object_get (policy, 2932 "methods"); 2933 json_t *auth_method = json_array_get (methods, 2934 pmr->method_index); 2935 json_t *truth = ANASTASIS_truth_to_json (tue->t); 2936 2937 GNUNET_assert (0 == 2938 json_object_set_new (truth, 2939 "upload_status", 2940 json_integer (tue->us))); 2941 GNUNET_assert (NULL != policy); 2942 GNUNET_assert (NULL != methods); 2943 GNUNET_assert (NULL != auth_method); 2944 GNUNET_assert (NULL != truth); 2945 GNUNET_assert (0 == 2946 json_object_set_new (auth_method, 2947 "truth", 2948 truth)); 2949 } 2950 } 2951 } 2952 2953 2954 /** 2955 * Test if the given @a provider_url is used by any of the 2956 * authentication methods and thus the provider should be 2957 * considered mandatory for storing the policy. 2958 * 2959 * @param state state to inspect 2960 * @param provider_url provider to test 2961 * @return false if the provider can be removed from policy 2962 * upload considerations without causing a problem 2963 */ 2964 static bool 2965 provider_required (const json_t *state, 2966 const char *provider_url) 2967 { 2968 json_t *policies 2969 = json_object_get (state, 2970 "policies"); 2971 size_t pidx; 2972 json_t *policy; 2973 2974 json_array_foreach (policies, pidx, policy) 2975 { 2976 json_t *methods = json_object_get (policy, 2977 "methods"); 2978 size_t midx; 2979 json_t *method; 2980 2981 json_array_foreach (methods, midx, method) 2982 { 2983 const char *provider 2984 = json_string_value (json_object_get (method, 2985 "provider")); 2986 2987 if (NULL == provider) 2988 { 2989 GNUNET_break (0); 2990 continue; 2991 } 2992 if (0 == strcmp (provider, 2993 provider_url)) 2994 return true; 2995 } 2996 } 2997 return false; 2998 } 2999 3000 3001 /** 3002 * All truth uploads are done, begin with uploading the policy. 3003 * 3004 * @param[in,out] uc context for the operation 3005 */ 3006 static void 3007 share_secret (struct UploadContext *uc); 3008 3009 3010 /** 3011 * Function called with the results of a #ANASTASIS_secret_share(). 3012 * 3013 * @param cls closure with a `struct UploadContext *` 3014 * @param sr share result 3015 */ 3016 static void 3017 secret_share_result_cb (void *cls, 3018 const struct ANASTASIS_ShareResult *sr) 3019 { 3020 struct UploadContext *uc = cls; 3021 3022 uc->ss = NULL; 3023 switch (sr->ss) 3024 { 3025 case ANASTASIS_SHARE_STATUS_SUCCESS: 3026 /* Just to be safe, delete the "core_secret" so that it is not 3027 accidentally preserved anywhere */ 3028 (void) json_object_del (uc->state, 3029 "core_secret"); 3030 { 3031 json_t *sa = json_object (); 3032 3033 GNUNET_assert (NULL != sa); 3034 for (unsigned int i = 0; i<sr->details.success.num_providers; i++) 3035 { 3036 const struct ANASTASIS_ProviderSuccessStatus *pssi 3037 = &sr->details.success.pss[i]; 3038 json_t *d; 3039 3040 d = GNUNET_JSON_PACK ( 3041 GNUNET_JSON_pack_uint64 ("policy_version", 3042 pssi->policy_version), 3043 GNUNET_JSON_pack_timestamp ("policy_expiration", 3044 pssi->policy_expiration)); 3045 GNUNET_assert (NULL != d); 3046 GNUNET_assert (0 == 3047 json_object_set_new (sa, 3048 pssi->provider_url, 3049 d)); 3050 } 3051 GNUNET_assert (0 == 3052 json_object_set_new (uc->state, 3053 "success_details", 3054 sa)); 3055 } 3056 set_state (uc->state, 3057 ANASTASIS_BACKUP_STATE_BACKUP_FINISHED); 3058 uc->cb (uc->cb_cls, 3059 TALER_EC_NONE, 3060 uc->state); 3061 break; 3062 case ANASTASIS_SHARE_STATUS_PAYMENT_REQUIRED: 3063 { 3064 json_t *ra; 3065 json_t *providers; 3066 3067 providers = json_object_get (uc->state, 3068 "policy_providers"); 3069 set_state (uc->state, 3070 ANASTASIS_BACKUP_STATE_POLICIES_PAYING); 3071 serialize_truth (uc); 3072 ra = json_array (); 3073 GNUNET_assert (NULL != ra); 3074 for (unsigned int i = 0; i< 3075 sr->details.payment_required.payment_requests_length; i++) 3076 { 3077 const struct ANASTASIS_SharePaymentRequest *spr; 3078 json_t *pr; 3079 size_t off; 3080 json_t *provider; 3081 3082 spr = &sr->details.payment_required.payment_requests[i]; 3083 pr = GNUNET_JSON_PACK ( 3084 GNUNET_JSON_pack_string ("payto", 3085 spr->payment_request_url), 3086 GNUNET_JSON_pack_string ("provider", 3087 spr->provider_url)); 3088 GNUNET_assert (0 == 3089 json_array_append_new (ra, 3090 pr)); 3091 json_array_foreach (providers, off, provider) 3092 { 3093 const char *purl = json_string_value (json_object_get (provider, 3094 "provider_url") 3095 ); 3096 3097 if (NULL == purl) 3098 { 3099 GNUNET_break (0); 3100 ANASTASIS_redux_fail_ (uc->cb, 3101 uc->cb_cls, 3102 TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID, 3103 "policy_providers array contents are invalid"); 3104 json_decref (ra); 3105 return; 3106 } 3107 if (0 == strcmp (purl, 3108 spr->provider_url)) 3109 { 3110 json_t *psj; 3111 3112 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 3113 "Remembering payment secret for provider `%s'\n", 3114 spr->provider_url); 3115 psj = GNUNET_JSON_from_data_auto (&spr->payment_secret); 3116 GNUNET_assert (0 == 3117 json_object_set_new (provider, 3118 "payment_secret", 3119 psj)); 3120 } 3121 } 3122 } 3123 GNUNET_assert (0 == 3124 json_object_set_new (uc->state, 3125 "policy_payment_requests", 3126 ra)); 3127 } 3128 uc->cb (uc->cb_cls, 3129 TALER_EC_NONE, 3130 uc->state); 3131 break; 3132 case ANASTASIS_SHARE_STATUS_PROVIDER_FAILED: 3133 { 3134 json_t *details; 3135 3136 if (! provider_required (uc->state, 3137 sr->details.provider_failure.provider_url)) 3138 { 3139 /* try again without that provider */ 3140 json_t *provider; 3141 json_t *providers; 3142 size_t idx; 3143 3144 provider 3145 = json_object_get ( 3146 json_object_get (uc->state, 3147 "authentication_providers"), 3148 sr->details.provider_failure.provider_url); 3149 GNUNET_break (0 == 3150 json_object_set_new (provider, 3151 "status", 3152 json_string ("disabled"))); 3153 providers 3154 = json_object_get (uc->state, 3155 "policy_providers"); 3156 json_array_foreach (providers, idx, provider) 3157 { 3158 const char *url 3159 = json_string_value (json_object_get (provider, 3160 "provider_url")); 3161 3162 if ( (NULL != url) && 3163 (0 == strcmp (sr->details.provider_failure.provider_url, 3164 url)) ) 3165 { 3166 GNUNET_break (0 == 3167 json_array_remove (providers, 3168 idx)); 3169 break; 3170 } 3171 } 3172 share_secret (uc); 3173 return; 3174 } 3175 details = GNUNET_JSON_PACK ( 3176 GNUNET_JSON_pack_uint64 ("http_status", 3177 sr->details.provider_failure.http_status), 3178 GNUNET_JSON_pack_uint64 ("code", 3179 sr->details.provider_failure.ec), 3180 GNUNET_JSON_pack_string ("hint", 3181 TALER_ErrorCode_get_hint ( 3182 sr->details.provider_failure.ec)), 3183 GNUNET_JSON_pack_string ("provider_url", 3184 sr->details.provider_failure.provider_url)); 3185 uc->cb (uc->cb_cls, 3186 TALER_EC_ANASTASIS_REDUCER_BACKUP_PROVIDER_FAILED, 3187 details); 3188 json_decref (details); 3189 } 3190 break; 3191 default: 3192 GNUNET_break (0); 3193 ANASTASIS_redux_fail_ (uc->cb, 3194 uc->cb_cls, 3195 TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE, 3196 "unexpected share result"); 3197 break; 3198 } 3199 upload_cancel_cb (uc); 3200 } 3201 3202 3203 /** 3204 * All truth uploads are done, begin with uploading the policy. 3205 * 3206 * @param[in,out] uc context for the operation 3207 */ 3208 static void 3209 share_secret (struct UploadContext *uc) 3210 { 3211 const json_t *user_id; 3212 const json_t *core_secret; 3213 const json_t *jpolicies; 3214 const json_t *providers = NULL; 3215 size_t policies_len; 3216 const char *secret_name = NULL; 3217 unsigned int pds_len; 3218 struct GNUNET_TIME_Relative timeout = GNUNET_TIME_UNIT_ZERO; 3219 struct GNUNET_JSON_Specification spec[] = { 3220 GNUNET_JSON_spec_object_const ("identity_attributes", 3221 &user_id), 3222 GNUNET_JSON_spec_array_const ("policies", 3223 &jpolicies), 3224 GNUNET_JSON_spec_mark_optional ( 3225 GNUNET_JSON_spec_array_const ("policy_providers", 3226 &providers), 3227 NULL), 3228 GNUNET_JSON_spec_object_const ("core_secret", 3229 &core_secret), 3230 GNUNET_JSON_spec_mark_optional ( 3231 GNUNET_JSON_spec_string ("secret_name", 3232 &secret_name), 3233 NULL), 3234 GNUNET_JSON_spec_end () 3235 }; 3236 3237 if (GNUNET_OK != 3238 GNUNET_JSON_parse (uc->state, 3239 spec, 3240 NULL, NULL)) 3241 { 3242 ANASTASIS_redux_fail_ (uc->cb, 3243 uc->cb_cls, 3244 TALER_EC_ANASTASIS_REDUCER_STATE_INVALID, 3245 "State parsing failed when preparing to share secret"); 3246 upload_cancel_cb (uc); 3247 return; 3248 } 3249 3250 { 3251 json_t *args; 3252 struct GNUNET_JSON_Specification pspec[] = { 3253 GNUNET_JSON_spec_mark_optional ( 3254 GNUNET_JSON_spec_relative_time ("timeout", 3255 &timeout), 3256 NULL), 3257 GNUNET_JSON_spec_end () 3258 }; 3259 3260 args = json_object_get (uc->state, 3261 "pay_arguments"); 3262 if ( (NULL != args) && 3263 (GNUNET_OK != 3264 GNUNET_JSON_parse (args, 3265 pspec, 3266 NULL, NULL)) ) 3267 { 3268 json_dumpf (args, 3269 stderr, 3270 JSON_INDENT (2)); 3271 GNUNET_break (0); 3272 ANASTASIS_redux_fail_ (uc->cb, 3273 uc->cb_cls, 3274 TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID, 3275 NULL); 3276 upload_cancel_cb (uc); 3277 return; 3278 } 3279 } 3280 3281 policies_len = json_array_size (jpolicies); 3282 if (0 == policies_len) 3283 { 3284 ANASTASIS_redux_fail_ (uc->cb, 3285 uc->cb_cls, 3286 TALER_EC_ANASTASIS_REDUCER_STATE_INVALID, 3287 "State parsing failed checks when preparing to share secret"); 3288 upload_cancel_cb (uc); 3289 return; 3290 } 3291 3292 if (json_array_size (providers) > UINT_MAX) 3293 { 3294 GNUNET_break_op (0); 3295 ANASTASIS_redux_fail_ (uc->cb, 3296 uc->cb_cls, 3297 TALER_EC_ANASTASIS_REDUCER_STATE_INVALID, 3298 "provider array excessively long"); 3299 upload_cancel_cb (uc); 3300 return; 3301 } 3302 pds_len 3303 = (unsigned int) json_array_size (providers); 3304 if (0 == pds_len) 3305 { 3306 ANASTASIS_redux_fail_ (uc->cb, 3307 uc->cb_cls, 3308 TALER_EC_ANASTASIS_REDUCER_STATE_INVALID, 3309 "no workable providers in state"); 3310 upload_cancel_cb (uc); 3311 return; 3312 } 3313 3314 { 3315 struct ANASTASIS_Policy *vpolicies[policies_len]; 3316 const struct ANASTASIS_Policy *policies[policies_len]; 3317 struct ANASTASIS_ProviderDetails pds[pds_len]; 3318 3319 /* initialize policies/vpolicies arrays */ 3320 memset (pds, 3321 0, 3322 sizeof (pds)); 3323 for (size_t i = 0; i<policies_len; i++) 3324 { 3325 const json_t *policy = json_array_get (jpolicies, 3326 i); 3327 const json_t *jmethods = json_object_get (policy, 3328 "methods"); 3329 unsigned int methods_len; 3330 3331 if ( (! json_is_array (jmethods)) || 3332 (0 == json_array_size (jmethods)) || 3333 (json_array_size (jmethods) > UINT_MAX) ) 3334 { 3335 GNUNET_break (0); 3336 ANASTASIS_redux_fail_ (uc->cb, 3337 uc->cb_cls, 3338 TALER_EC_ANASTASIS_REDUCER_STATE_INVALID, 3339 "'methods' must be an array of sane length"); 3340 upload_cancel_cb (uc); 3341 return; 3342 } 3343 methods_len 3344 = (unsigned int) json_array_size (jmethods); 3345 { 3346 struct ANASTASIS_Policy *p; 3347 struct ANASTASIS_Truth *truths[methods_len]; 3348 const struct ANASTASIS_Truth *ctruths[methods_len]; 3349 3350 for (unsigned int j = 0; j<methods_len; j++) 3351 { 3352 const json_t *jmethod = json_array_get (jmethods, 3353 j); 3354 const json_t *jtruth = NULL; 3355 uint32_t truth_index; 3356 const char *provider_url; 3357 struct GNUNET_JSON_Specification ispec[] = { 3358 GNUNET_JSON_spec_mark_optional ( 3359 GNUNET_JSON_spec_object_const ("truth", 3360 &jtruth), 3361 NULL), 3362 GNUNET_JSON_spec_string ("provider", 3363 &provider_url), 3364 GNUNET_JSON_spec_uint32 ("authentication_method", 3365 &truth_index), 3366 GNUNET_JSON_spec_end () 3367 }; 3368 3369 GNUNET_break (NULL != jmethod); 3370 if (GNUNET_OK != 3371 GNUNET_JSON_parse (jmethod, 3372 ispec, 3373 NULL, NULL)) 3374 { 3375 GNUNET_break (0); 3376 for (unsigned int k = 0; k<j; k++) 3377 ANASTASIS_truth_free (truths[k]); 3378 ANASTASIS_redux_fail_ (uc->cb, 3379 uc->cb_cls, 3380 TALER_EC_ANASTASIS_REDUCER_STATE_INVALID, 3381 "'truth' failed to decode"); 3382 upload_cancel_cb (uc); 3383 return; 3384 } 3385 if (NULL != jtruth) 3386 { 3387 /* Get truth by deserializing from state */ 3388 truths[j] = ANASTASIS_truth_from_json (jtruth); 3389 if (NULL == truths[j]) 3390 { 3391 GNUNET_break (0); 3392 for (unsigned int k = 0; k<j; k++) 3393 ANASTASIS_truth_free (truths[k]); 3394 ANASTASIS_redux_fail_ (uc->cb, 3395 uc->cb_cls, 3396 TALER_EC_ANASTASIS_REDUCER_STATE_INVALID, 3397 "'truth' failed to decode"); 3398 upload_cancel_cb (uc); 3399 return; 3400 } 3401 } 3402 else 3403 { 3404 bool found = false; 3405 /* Maybe we never serialized the truth; find it in our DLL */ 3406 for (struct TruthUpload *tue = uc->tues_head; 3407 NULL != tue; 3408 tue = tue->next) 3409 { 3410 GNUNET_break (NULL != tue->t); 3411 if ( (tue->am_idx == truth_index) && 3412 (0 == strcmp (provider_url, 3413 tue->provider_url)) ) 3414 { 3415 /* Duplicate truth object */ 3416 json_t *jt = ANASTASIS_truth_to_json (tue->t); 3417 3418 GNUNET_assert (NULL != jt); 3419 truths[j] = ANASTASIS_truth_from_json (jt); 3420 GNUNET_assert (NULL != truths[j]); 3421 json_decref (jt); 3422 found = true; 3423 break; 3424 } 3425 } 3426 if (! found) 3427 { 3428 GNUNET_break (0); 3429 for (unsigned int k = 0; k<j; k++) 3430 ANASTASIS_truth_free (truths[k]); 3431 ANASTASIS_redux_fail_ (uc->cb, 3432 uc->cb_cls, 3433 TALER_EC_ANASTASIS_REDUCER_STATE_INVALID, 3434 "'truth' failed to decode"); 3435 upload_cancel_cb (uc); 3436 return; 3437 } 3438 } 3439 ctruths[j] = truths[j]; 3440 } 3441 p = ANASTASIS_policy_create (ctruths, 3442 methods_len); 3443 vpolicies[i] = p; 3444 policies[i] = p; 3445 for (unsigned int k = 0; k<methods_len; k++) 3446 ANASTASIS_truth_free (truths[k]); 3447 } 3448 } 3449 3450 /* initialize 'pds' array */ 3451 for (unsigned int i = 0; i<pds_len; i++) 3452 { 3453 json_t *pdj = json_array_get (providers, 3454 i); 3455 struct GNUNET_JSON_Specification ispec[] = { 3456 GNUNET_JSON_spec_mark_optional ( 3457 GNUNET_JSON_spec_fixed_auto ("payment_secret", 3458 &pds[i].payment_secret), 3459 NULL), 3460 GNUNET_JSON_spec_string ("provider_url", 3461 &pds[i].provider_url), 3462 GNUNET_JSON_spec_end () 3463 }; 3464 3465 if ( (GNUNET_OK != 3466 GNUNET_JSON_parse (pdj, 3467 ispec, 3468 NULL, NULL)) || 3469 (GNUNET_OK != 3470 ANASTASIS_reducer_lookup_salt (uc->state, 3471 pds[i].provider_url, 3472 &pds[i].provider_salt)) ) 3473 { 3474 GNUNET_break (0); 3475 ANASTASIS_redux_fail_ (uc->cb, 3476 uc->cb_cls, 3477 TALER_EC_ANASTASIS_REDUCER_STATE_INVALID, 3478 "'providers' entry malformed"); 3479 for (unsigned int p = 0; p<policies_len; p++) 3480 ANASTASIS_policy_destroy (vpolicies[p]); 3481 upload_cancel_cb (uc); 3482 return; 3483 } 3484 } 3485 3486 { 3487 char *secret; 3488 size_t secret_size; 3489 3490 secret = json_dumps (core_secret, 3491 JSON_COMPACT | JSON_SORT_KEYS); 3492 GNUNET_assert (NULL != secret); 3493 secret_size = strlen (secret); 3494 uc->ss = ANASTASIS_secret_share (ANASTASIS_REDUX_ctx_, 3495 user_id, 3496 pds, 3497 pds_len, 3498 policies, 3499 policies_len, 3500 uc->years, 3501 timeout, 3502 &secret_share_result_cb, 3503 uc, 3504 secret_name, 3505 secret, 3506 secret_size); 3507 GNUNET_free (secret); 3508 } 3509 for (unsigned int i = 0; i<policies_len; i++) 3510 ANASTASIS_policy_destroy (vpolicies[i]); 3511 } 3512 if (NULL == uc->ss) 3513 { 3514 GNUNET_break (0); 3515 ANASTASIS_redux_fail_ (uc->cb, 3516 uc->cb_cls, 3517 TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE, 3518 "Failed to begin secret sharing"); 3519 upload_cancel_cb (uc); 3520 return; 3521 } 3522 } 3523 3524 3525 /** 3526 * Some truth uploads require payment, serialize state and 3527 * request payment to be executed by the application. 3528 * 3529 * @param[in,out] uc context for the operation 3530 */ 3531 static void 3532 request_truth_payment (struct UploadContext *uc) 3533 { 3534 json_t *payments; 3535 3536 payments = json_array (); 3537 GNUNET_assert (NULL != payments); 3538 serialize_truth (uc); 3539 for (struct TruthUpload *tue = uc->tues_head; 3540 NULL != tue; 3541 tue = tue->next) 3542 { 3543 if (NULL == tue->payment_request) 3544 continue; 3545 GNUNET_assert ( 3546 0 == 3547 json_array_append_new (payments, 3548 json_string ( 3549 tue->payment_request))); 3550 } 3551 GNUNET_assert (0 == 3552 json_object_set_new (uc->state, 3553 "payments", 3554 payments)); 3555 set_state (uc->state, 3556 ANASTASIS_BACKUP_STATE_TRUTHS_PAYING); 3557 uc->cb (uc->cb_cls, 3558 TALER_EC_NONE, 3559 uc->state); 3560 upload_cancel_cb (uc); 3561 } 3562 3563 3564 /** 3565 * We may be finished with all (active) asynchronous operations. 3566 * Check if any are pending and continue accordingly. 3567 * 3568 * @param[in,out] uc context for the operation 3569 */ 3570 static void 3571 check_upload_finished (struct UploadContext *uc) 3572 { 3573 bool pay = false; 3574 bool active = false; 3575 3576 for (struct TruthUpload *tue = uc->tues_head; 3577 NULL != tue; 3578 tue = tue->next) 3579 { 3580 if (TALER_EC_NONE != tue->ec) 3581 { 3582 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 3583 "Truth upload failed with error %d\n", 3584 (int) tue->ec); 3585 uc->cb (uc->cb_cls, 3586 tue->ec, 3587 NULL); 3588 upload_cancel_cb (uc); 3589 return; 3590 } 3591 if (NULL != tue->tu) 3592 active = true; 3593 if (NULL != tue->payment_request) 3594 pay = true; 3595 } 3596 if (active) 3597 return; 3598 if (pay) 3599 { 3600 request_truth_payment (uc); 3601 return; 3602 } 3603 share_secret (uc); 3604 } 3605 3606 3607 /** 3608 * Upload result information. The resulting truth object can be used 3609 * to create policies. If payment is required, the @a taler_pay_url 3610 * is returned and the operation must be retried after payment. 3611 * Callee MUST free @a t using ANASTASIS_truth_free(). 3612 * 3613 * @param cls closure with a `struct TruthUpload` 3614 * @param t truth object to create policies, NULL on failure 3615 * @param ud upload details 3616 */ 3617 static void 3618 truth_upload_cb (void *cls, 3619 struct ANASTASIS_Truth *t, 3620 const struct ANASTASIS_UploadDetails *ud) 3621 { 3622 struct TruthUpload *tue = cls; 3623 3624 tue->tu = NULL; 3625 tue->t = t; 3626 tue->ec = ud->ec; 3627 tue->us = ud->us; 3628 if (ANASTASIS_US_PAYMENT_REQUIRED == ud->us) 3629 { 3630 tue->payment_request = GNUNET_strdup ( 3631 ud->details.payment.payment_request); 3632 } 3633 check_upload_finished (tue->uc); 3634 } 3635 3636 3637 /** 3638 * Check if we still need to create a new truth object for the truth 3639 * identified by @a provider_url and @a am_idx. If so, create it from 3640 * @a truth for policy reference @a pmr. If such a truth object 3641 * already exists, append @a pmr to its list of reasons. 3642 * 3643 * @param[in,out] uc our upload context 3644 * @param pmr policy method combination that requires the truth 3645 * @param provider_url the URL of the Anastasis provider to upload 3646 * the truth to, used to check for existing entries 3647 * @param am_idx index of the authentication method, used to check for existing entries 3648 * @param[in] truth object representing already uploaded truth, reference captured! 3649 * @param[in,out] async_truth pointer to counter with the number of ongoing uploads, 3650 * updated 3651 * @param auth_method object with the challenge details, to generate the truth 3652 * @return #GNUNET_SYSERR error requiring abort, 3653 * #GNUNET_OK on success 3654 */ 3655 static int 3656 add_truth_object (struct UploadContext *uc, 3657 const struct PolicyMethodReference *pmr, 3658 const char *provider_url, 3659 uint32_t am_idx, 3660 json_t *truth, 3661 unsigned int *async_truth, 3662 json_t *auth_method) 3663 { 3664 /* check if we are already uploading this truth */ 3665 struct TruthUpload *tue; 3666 bool must_upload = true; 3667 3668 for (tue = uc->tues_head; 3669 NULL != tue; 3670 tue = tue->next) 3671 { 3672 if ( (0 == strcmp (tue->provider_url, 3673 provider_url)) && 3674 (am_idx == tue->am_idx) ) 3675 { 3676 GNUNET_array_append (tue->policies, 3677 tue->policies_length, 3678 *pmr); 3679 break; 3680 } 3681 } 3682 3683 if (NULL == tue) 3684 { 3685 /* Create new entry */ 3686 tue = GNUNET_new (struct TruthUpload); 3687 3688 GNUNET_CONTAINER_DLL_insert (uc->tues_head, 3689 uc->tues_tail, 3690 tue); 3691 tue->uc = uc; 3692 tue->policies = GNUNET_new (struct PolicyMethodReference); 3693 *tue->policies = *pmr; 3694 tue->provider_url = GNUNET_strdup (provider_url); 3695 tue->am_idx = am_idx; 3696 tue->policies_length = 1; 3697 } 3698 3699 { 3700 uint32_t status = UINT32_MAX; 3701 struct GNUNET_JSON_Specification spec[] = { 3702 GNUNET_JSON_spec_mark_optional ( 3703 GNUNET_JSON_spec_uint32 ("upload_status", 3704 &status), 3705 NULL), 3706 GNUNET_JSON_spec_end () 3707 }; 3708 if (GNUNET_OK != 3709 GNUNET_JSON_parse (truth, 3710 spec, 3711 NULL, NULL)) 3712 { 3713 GNUNET_break (0); 3714 return GNUNET_SYSERR; 3715 } 3716 3717 must_upload = (ANASTASIS_US_SUCCESS != status); 3718 } 3719 3720 if (NULL == tue->t) 3721 { 3722 tue->t = ANASTASIS_truth_from_json (truth); 3723 if (NULL == tue->t) 3724 { 3725 GNUNET_break (0); 3726 return GNUNET_SYSERR; 3727 } 3728 } 3729 3730 if ( (NULL != tue->tu) && 3731 (! must_upload) ) 3732 { 3733 ANASTASIS_truth_upload_cancel (tue->tu); 3734 (*async_truth)--; 3735 tue->tu = NULL; 3736 return GNUNET_OK; 3737 } 3738 3739 if ( (NULL == tue->tu) && 3740 (must_upload) ) 3741 { 3742 struct ANASTASIS_CRYPTO_ProviderSaltP salt; 3743 struct ANASTASIS_CRYPTO_UserIdentifierP id; 3744 void *truth_data; 3745 size_t truth_data_size; 3746 struct GNUNET_JSON_Specification spec[] = { 3747 GNUNET_JSON_spec_varsize ("challenge", 3748 &truth_data, 3749 &truth_data_size), 3750 GNUNET_JSON_spec_end () 3751 }; 3752 3753 if (GNUNET_OK != 3754 ANASTASIS_reducer_lookup_salt (uc->state, 3755 provider_url, 3756 &salt)) 3757 { 3758 GNUNET_break (0); 3759 return GNUNET_SYSERR; 3760 } 3761 if (GNUNET_OK != 3762 GNUNET_JSON_parse (auth_method, 3763 spec, 3764 NULL, NULL)) 3765 { 3766 json_dumpf (auth_method, 3767 stderr, 3768 JSON_INDENT (2)); 3769 GNUNET_break (0); 3770 return GNUNET_SYSERR; 3771 } 3772 { 3773 json_t *user_id; 3774 3775 user_id = json_object_get (uc->state, 3776 "identity_attributes"); 3777 if (! json_is_object (user_id)) 3778 { 3779 GNUNET_break (0); 3780 return GNUNET_SYSERR; 3781 } 3782 ANASTASIS_CRYPTO_user_identifier_derive (user_id, 3783 &salt, 3784 &id); 3785 } 3786 tue->tu = ANASTASIS_truth_upload3 (ANASTASIS_REDUX_ctx_, 3787 &id, 3788 tue->t, 3789 truth_data, 3790 truth_data_size, 3791 uc->years, 3792 uc->timeout, 3793 &truth_upload_cb, 3794 tue); 3795 GNUNET_JSON_parse_free (spec); 3796 tue->t = NULL; 3797 (*async_truth)++; 3798 } 3799 3800 if ( (NULL != tue->tu) && 3801 (NULL != tue->t) ) 3802 { 3803 /* no point in having both */ 3804 ANASTASIS_truth_free (tue->t); 3805 tue->t = NULL; 3806 } 3807 return GNUNET_OK; 3808 } 3809 3810 3811 /** 3812 * Check if we still need to upload the truth identified by 3813 * @a provider_url and @a am_idx. If so, upload it for 3814 * policy reference @a pmr. If the upload is already queued, 3815 * append @a pmr to its list of reasons. 3816 * 3817 * @param[in,out] uc our upload context 3818 * @param pmr policy method combination that requires the truth 3819 * @param provider_url the URL of the Anastasis provider to upload 3820 * the truth to, used to check for existing entries 3821 * @param am_idx index of the authentication method, used to check for existing entries 3822 * @param auth_method object with the challenge details, to generate the truth 3823 * @return #GNUNET_SYSERR on error requiring abort, 3824 * #GNUNET_NO if no new truth upload was generated (@a pmr was appended) 3825 * #GNUNET_OK if a new truth upload was initiated 3826 */ 3827 static int 3828 check_truth_upload (struct UploadContext *uc, 3829 const struct PolicyMethodReference *pmr, 3830 const char *provider_url, 3831 uint32_t am_idx, 3832 json_t *auth_method) 3833 { 3834 json_t *user_id; 3835 json_t *jtruth; 3836 struct TruthUpload *tue; 3837 3838 user_id = json_object_get (uc->state, 3839 "identity_attributes"); 3840 if (! json_is_object (user_id)) 3841 { 3842 GNUNET_break (0); 3843 return GNUNET_SYSERR; 3844 } 3845 3846 /* check if we are already uploading this truth */ 3847 for (tue = uc->tues_head; 3848 NULL != tue; 3849 tue = tue->next) 3850 { 3851 if ( (0 == strcmp (tue->provider_url, 3852 provider_url)) && 3853 (am_idx == tue->am_idx) ) 3854 { 3855 GNUNET_array_append (tue->policies, 3856 tue->policies_length, 3857 *pmr); 3858 return GNUNET_NO; 3859 } 3860 } 3861 3862 /* need new upload */ 3863 tue = GNUNET_new (struct TruthUpload); 3864 { 3865 json_t *policies = json_object_get (uc->state, 3866 "policies"); 3867 json_t *policy = json_array_get (policies, 3868 pmr->policy_index); 3869 json_t *methods = json_object_get (policy, 3870 "methods"); 3871 json_t *method = json_array_get (methods, 3872 pmr->method_index); 3873 3874 jtruth = json_object_get (method, 3875 "truth"); 3876 } 3877 3878 { 3879 const char *type; 3880 const char *mime_type = NULL; 3881 const char *instructions = NULL; 3882 void *truth_data; 3883 size_t truth_data_size; 3884 struct GNUNET_JSON_Specification spec[] = { 3885 GNUNET_JSON_spec_string ("type", 3886 &type), 3887 GNUNET_JSON_spec_mark_optional ( 3888 GNUNET_JSON_spec_string ("mime_type", 3889 &mime_type), 3890 NULL), 3891 GNUNET_JSON_spec_mark_optional ( 3892 GNUNET_JSON_spec_string ("instructions", 3893 &instructions), 3894 NULL), 3895 GNUNET_JSON_spec_varsize ("challenge", 3896 &truth_data, 3897 &truth_data_size), 3898 GNUNET_JSON_spec_end () 3899 }; 3900 struct ANASTASIS_CRYPTO_ProviderSaltP provider_salt; 3901 struct ANASTASIS_CRYPTO_UserIdentifierP id; 3902 3903 if (GNUNET_OK != 3904 GNUNET_JSON_parse (auth_method, 3905 spec, 3906 NULL, NULL)) 3907 { 3908 json_dumpf (auth_method, 3909 stderr, 3910 JSON_INDENT (2)); 3911 GNUNET_break (0); 3912 GNUNET_free (tue); 3913 return GNUNET_SYSERR; 3914 } 3915 GNUNET_CONTAINER_DLL_insert (uc->tues_head, 3916 uc->tues_tail, 3917 tue); 3918 tue->uc = uc; 3919 tue->policies = GNUNET_new (struct PolicyMethodReference); 3920 *tue->policies = *pmr; 3921 tue->provider_url = GNUNET_strdup (provider_url); 3922 tue->am_idx = am_idx; 3923 tue->policies_length = 1; 3924 if (GNUNET_OK != 3925 ANASTASIS_reducer_lookup_salt (uc->state, 3926 provider_url, 3927 &provider_salt)) 3928 { 3929 GNUNET_break (0); 3930 GNUNET_JSON_parse_free (spec); 3931 upload_cancel_cb (uc); 3932 return GNUNET_SYSERR; 3933 } 3934 ANASTASIS_CRYPTO_user_identifier_derive (user_id, 3935 &provider_salt, 3936 &id); 3937 { 3938 struct ANASTASIS_CRYPTO_TruthUUIDP uuid; 3939 struct ANASTASIS_CRYPTO_QuestionSaltP question_salt; 3940 struct ANASTASIS_CRYPTO_TruthKeyP truth_key; 3941 struct ANASTASIS_CRYPTO_KeyShareP key_share; 3942 struct ANASTASIS_CRYPTO_NonceP nonce; 3943 struct GNUNET_JSON_Specification jspec[] = { 3944 GNUNET_JSON_spec_fixed_auto ("salt", 3945 &question_salt), 3946 GNUNET_JSON_spec_fixed_auto ("truth_key", 3947 &truth_key), 3948 GNUNET_JSON_spec_fixed_auto ("nonce", 3949 &nonce), 3950 GNUNET_JSON_spec_fixed_auto ("uuid", 3951 &uuid), 3952 GNUNET_JSON_spec_fixed_auto ("key_share", 3953 &key_share), 3954 GNUNET_JSON_spec_end () 3955 }; 3956 3957 if (GNUNET_OK != 3958 GNUNET_JSON_parse (jtruth, 3959 jspec, 3960 NULL, NULL)) 3961 { 3962 tue->tu = ANASTASIS_truth_upload (ANASTASIS_REDUX_ctx_, 3963 &id, 3964 provider_url, 3965 type, 3966 instructions, 3967 mime_type, 3968 &provider_salt, 3969 truth_data, 3970 truth_data_size, 3971 uc->years, 3972 uc->timeout, 3973 &truth_upload_cb, 3974 tue); 3975 } 3976 else 3977 { 3978 tue->tu = ANASTASIS_truth_upload2 (ANASTASIS_REDUX_ctx_, 3979 &id, 3980 provider_url, 3981 type, 3982 instructions, 3983 mime_type, 3984 &provider_salt, 3985 truth_data, 3986 truth_data_size, 3987 uc->years, 3988 uc->timeout, 3989 &nonce, 3990 &uuid, 3991 &question_salt, 3992 &truth_key, 3993 &key_share, 3994 &truth_upload_cb, 3995 tue); 3996 } 3997 } 3998 if (NULL == tue->tu) 3999 { 4000 GNUNET_break (0); 4001 GNUNET_JSON_parse_free (spec); 4002 upload_cancel_cb (uc); 4003 return GNUNET_SYSERR; 4004 } 4005 GNUNET_JSON_parse_free (spec); 4006 return GNUNET_OK; 4007 } 4008 } 4009 4010 4011 /** 4012 * Function to upload truths and recovery document policies. 4013 * Ultimately transitions to failed state (allowing user to go back 4014 * and change providers/policies), or payment, or finished. 4015 * 4016 * @param state state to operate on 4017 * @param cb callback (#ANASTASIS_ActionCallback) to call after upload 4018 * @param cb_cls callback closure 4019 */ 4020 static struct ANASTASIS_ReduxAction * 4021 upload (json_t *state, 4022 ANASTASIS_ActionCallback cb, 4023 void *cb_cls) 4024 { 4025 struct UploadContext *uc; 4026 json_t *auth_methods; 4027 json_t *policies; 4028 struct GNUNET_TIME_Timestamp expiration; 4029 struct GNUNET_JSON_Specification spec[] = { 4030 GNUNET_JSON_spec_timestamp ("expiration", 4031 &expiration), 4032 GNUNET_JSON_spec_end () 4033 }; 4034 4035 if (GNUNET_OK != 4036 GNUNET_JSON_parse (state, 4037 spec, 4038 NULL, NULL)) 4039 { 4040 ANASTASIS_redux_fail_ (cb, 4041 cb_cls, 4042 TALER_EC_ANASTASIS_REDUCER_STATE_INVALID, 4043 "'expiration' missing"); 4044 return NULL; 4045 } 4046 auth_methods = json_object_get (state, 4047 "authentication_methods"); 4048 if ( (! json_is_array (auth_methods)) || 4049 (0 == json_array_size (auth_methods)) ) 4050 { 4051 ANASTASIS_redux_fail_ (cb, 4052 cb_cls, 4053 TALER_EC_ANASTASIS_REDUCER_STATE_INVALID, 4054 "'authentication_methods' must be non-empty array"); 4055 return NULL; 4056 } 4057 policies = json_object_get (state, 4058 "policies"); 4059 if ( (! json_is_array (policies)) || 4060 (0 == json_array_size (policies)) ) 4061 { 4062 ANASTASIS_redux_fail_ (cb, 4063 cb_cls, 4064 TALER_EC_ANASTASIS_REDUCER_STATE_INVALID, 4065 "'policies' must be non-empty array"); 4066 return NULL; 4067 } 4068 4069 uc = GNUNET_new (struct UploadContext); 4070 uc->ra.cleanup = &upload_cancel_cb; 4071 uc->ra.cleanup_cls = uc; 4072 uc->cb = cb; 4073 uc->cb_cls = cb_cls; 4074 uc->state = json_incref (state); 4075 uc->years = expiration_to_years (expiration); 4076 4077 { 4078 json_t *args; 4079 struct GNUNET_JSON_Specification pspec[] = { 4080 GNUNET_JSON_spec_mark_optional ( 4081 GNUNET_JSON_spec_relative_time ("timeout", 4082 &uc->timeout), 4083 NULL), 4084 GNUNET_JSON_spec_end () 4085 }; 4086 4087 args = json_object_get (uc->state, 4088 "pay_arguments"); 4089 if ( (NULL != args) && 4090 (GNUNET_OK != 4091 GNUNET_JSON_parse (args, 4092 pspec, 4093 NULL, NULL)) ) 4094 { 4095 json_dumpf (args, 4096 stderr, 4097 JSON_INDENT (2)); 4098 GNUNET_break (0); 4099 ANASTASIS_redux_fail_ (cb, 4100 cb_cls, 4101 TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID, 4102 "'timeout' must be valid delay"); 4103 4104 return NULL; 4105 } 4106 } 4107 4108 { 4109 json_t *policy; 4110 size_t pindex; 4111 unsigned int async_truth = 0; 4112 4113 json_array_foreach (policies, pindex, policy) 4114 { 4115 json_t *methods = json_object_get (policy, 4116 "methods"); 4117 json_t *auth_method; 4118 size_t mindex; 4119 4120 if ( (! json_is_array (methods)) || 4121 (0 == json_array_size (policies)) ) 4122 { 4123 ANASTASIS_redux_fail_ (cb, 4124 cb_cls, 4125 TALER_EC_ANASTASIS_REDUCER_STATE_INVALID, 4126 "'policies' must be non-empty array"); 4127 upload_cancel_cb (uc); 4128 return NULL; 4129 } 4130 json_array_foreach (methods, mindex, auth_method) 4131 { 4132 uint32_t am_idx; 4133 const char *provider_url; 4134 json_t *truth = NULL; 4135 struct GNUNET_JSON_Specification ispec[] = { 4136 GNUNET_JSON_spec_string ("provider", 4137 &provider_url), 4138 GNUNET_JSON_spec_uint32 ("authentication_method", 4139 &am_idx), 4140 GNUNET_JSON_spec_mark_optional ( 4141 GNUNET_JSON_spec_json ("truth", 4142 &truth), 4143 NULL), 4144 GNUNET_JSON_spec_end () 4145 }; 4146 4147 if (GNUNET_OK != 4148 GNUNET_JSON_parse (auth_method, 4149 ispec, 4150 NULL, NULL)) 4151 { 4152 ANASTASIS_redux_fail_ (cb, 4153 cb_cls, 4154 TALER_EC_ANASTASIS_REDUCER_STATE_INVALID, 4155 "'method' data malformed"); 4156 upload_cancel_cb (uc); 4157 return NULL; 4158 } 4159 { 4160 struct PolicyMethodReference pmr = { 4161 .policy_index = pindex, 4162 .method_index = mindex 4163 }; 4164 json_t *amj; 4165 4166 amj = json_array_get (auth_methods, 4167 am_idx); 4168 if (NULL == amj) 4169 { 4170 ANASTASIS_redux_fail_ (cb, 4171 cb_cls, 4172 TALER_EC_ANASTASIS_REDUCER_STATE_INVALID, 4173 "'authentication_method' refers to invalid authorization index malformed"); 4174 upload_cancel_cb (uc); 4175 GNUNET_JSON_parse_free (spec); 4176 return NULL; 4177 } 4178 if (NULL == truth) 4179 { 4180 int ret; 4181 4182 ret = check_truth_upload (uc, 4183 &pmr, 4184 provider_url, 4185 am_idx, 4186 amj); 4187 if (GNUNET_SYSERR == ret) 4188 { 4189 GNUNET_JSON_parse_free (spec); 4190 ANASTASIS_redux_fail_ (cb, 4191 cb_cls, 4192 TALER_EC_ANASTASIS_REDUCER_STATE_INVALID, 4193 NULL); 4194 return NULL; 4195 } 4196 if (GNUNET_OK == ret) 4197 async_truth++; 4198 } 4199 else 4200 { 4201 int ret; 4202 4203 ret = add_truth_object (uc, 4204 &pmr, 4205 provider_url, 4206 am_idx, 4207 truth, 4208 &async_truth, 4209 amj); 4210 if (GNUNET_SYSERR == ret) 4211 { 4212 GNUNET_JSON_parse_free (spec); 4213 ANASTASIS_redux_fail_ (cb, 4214 cb_cls, 4215 TALER_EC_ANASTASIS_REDUCER_STATE_INVALID, 4216 NULL); 4217 return NULL; 4218 } 4219 } 4220 } 4221 GNUNET_JSON_parse_free (spec); 4222 } /* end for all methods of policy */ 4223 } /* end for all policies */ 4224 if (async_truth > 0) 4225 return &uc->ra; 4226 } 4227 share_secret (uc); 4228 if (NULL == uc->ss) 4229 return NULL; 4230 return &uc->ra; 4231 } 4232 4233 4234 /** 4235 * Test if the core secret @a secret_size is small enough to be stored 4236 * at all providers, which have a minimum upload limit of @a min_limit_in_mb. 4237 * 4238 * For now, we do not precisely calculate the size of the recovery document, 4239 * and simply assume that the instructions (i.e. security questions) are all 4240 * relatively small (aka sane), and that the number of authentication methods 4241 * and recovery policies is similarly small so that all of this meta data 4242 * fits in 512 kb (which is VERY big). 4243 * 4244 * Even with the minimum permitted upload limit of 1 MB (which is likely, 4245 * given that there is hardly a reason for providers to offer more), this 4246 * leaves 512 kb for the @a secret_size, which should be plenty (given 4247 * that this is supposed to be for a master key, and not the actual data). 4248 * 4249 * @param state our state, could be used in the future to calculate the 4250 * size of the recovery document without the core secret 4251 * @param secret_size size of the core secret 4252 * @param min_limit_in_mb minimum upload size of all providers 4253 */ 4254 static bool 4255 core_secret_fits (const json_t *state, 4256 size_t secret_size, 4257 uint32_t min_limit_in_mb) 4258 { 4259 return (min_limit_in_mb * 1024LL * 1024LL > 4260 512LLU * 1024LLU + secret_size); 4261 } 4262 4263 4264 /** 4265 * Check if the upload size limit is satisfied. 4266 * 4267 * @param state our state 4268 * @param jsecret the uploaded secret 4269 * @return #GNUNET_OK if @a secret_size works for all providers, 4270 * #GNUNET_NO if the @a secret_size is too big, 4271 * #GNUNET_SYSERR if a provider has a limit of 0 4272 */ 4273 static enum GNUNET_GenericReturnValue 4274 check_upload_size_limit (json_t *state, 4275 const json_t *jsecret) 4276 { 4277 uint32_t min_limit = UINT32_MAX; 4278 json_t *aps = json_object_get (state, 4279 "authentication_providers"); 4280 const char *url; 4281 json_t *ap; 4282 size_t secret_size; 4283 4284 { 4285 char *secret; 4286 4287 secret = json_dumps (jsecret, 4288 JSON_COMPACT | JSON_SORT_KEYS); 4289 GNUNET_assert (NULL != secret); 4290 secret_size = strlen (secret); 4291 GNUNET_free (secret); 4292 } 4293 4294 /* We calculate the minimum upload limit of all possible providers; 4295 this is under the (simplified) assumption that we store the 4296 recovery document at all providers; this may be changed later, 4297 see #6760. */ 4298 json_object_foreach (aps, url, ap) 4299 { 4300 uint32_t limit = 0; 4301 const char *status; 4302 uint32_t http_status = 0; 4303 struct GNUNET_JSON_Specification spec[] = { 4304 GNUNET_JSON_spec_string ("status", 4305 &status), 4306 GNUNET_JSON_spec_mark_optional ( 4307 GNUNET_JSON_spec_uint32 ("http_status", 4308 &http_status), 4309 NULL), 4310 GNUNET_JSON_spec_mark_optional ( 4311 GNUNET_JSON_spec_uint32 ("storage_limit_in_megabytes", 4312 &limit), 4313 NULL), 4314 GNUNET_JSON_spec_end () 4315 }; 4316 4317 if (GNUNET_OK != 4318 GNUNET_JSON_parse (ap, 4319 spec, 4320 NULL, NULL)) 4321 { 4322 /* skip malformed provider, likely /config failed */ 4323 GNUNET_break_op (0); 4324 continue; 4325 } 4326 if ( (MHD_HTTP_OK != http_status) || 4327 (0 != strcmp (status, 4328 "ok")) ) 4329 continue; 4330 if (0 == limit) 4331 return GNUNET_SYSERR; 4332 min_limit = GNUNET_MIN (min_limit, 4333 limit); 4334 } 4335 if (! core_secret_fits (state, 4336 secret_size, 4337 min_limit)) 4338 return GNUNET_NO; 4339 return GNUNET_OK; 4340 } 4341 4342 4343 /** 4344 * DispatchHandler/Callback function which is called for a 4345 * "enter_secret" action. 4346 * 4347 * @param state state to operate on 4348 * @param arguments arguments to use for operation on state 4349 * @param cb callback to call during/after operation 4350 * @param cb_cls callback closure 4351 * @return NULL 4352 */ 4353 static struct ANASTASIS_ReduxAction * 4354 enter_secret (json_t *state, 4355 const json_t *arguments, 4356 ANASTASIS_ActionCallback cb, 4357 void *cb_cls) 4358 { 4359 json_t *jsecret; 4360 struct GNUNET_TIME_Timestamp expiration 4361 = GNUNET_TIME_UNIT_ZERO_TS; 4362 struct GNUNET_JSON_Specification spec[] = { 4363 GNUNET_JSON_spec_json ("secret", 4364 &jsecret), 4365 GNUNET_JSON_spec_mark_optional ( 4366 GNUNET_JSON_spec_timestamp ("expiration", 4367 &expiration), 4368 NULL), 4369 GNUNET_JSON_spec_end () 4370 }; 4371 4372 if (NULL == arguments) 4373 { 4374 ANASTASIS_redux_fail_ (cb, 4375 cb_cls, 4376 TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID, 4377 "arguments missing"); 4378 return NULL; 4379 } 4380 if (GNUNET_OK != 4381 GNUNET_JSON_parse (arguments, 4382 spec, 4383 NULL, NULL)) 4384 { 4385 ANASTASIS_redux_fail_ (cb, 4386 cb_cls, 4387 TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID, 4388 "'secret' argument required"); 4389 return NULL; 4390 } 4391 4392 /* check upload size limit */ 4393 { 4394 enum GNUNET_GenericReturnValue ret; 4395 4396 ret = check_upload_size_limit (state, 4397 jsecret); 4398 switch (ret) 4399 { 4400 case GNUNET_SYSERR: 4401 ANASTASIS_redux_fail_ (cb, 4402 cb_cls, 4403 TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID, 4404 "provider has an upload limit of 0"); 4405 return NULL; 4406 case GNUNET_NO: 4407 ANASTASIS_redux_fail_ (cb, 4408 cb_cls, 4409 TALER_EC_ANASTASIS_REDUCER_SECRET_TOO_BIG, 4410 NULL); 4411 return NULL; 4412 default: 4413 break; 4414 } 4415 } 4416 if (! GNUNET_TIME_absolute_is_zero (expiration.abs_time)) 4417 { 4418 if (GNUNET_OK != 4419 update_expiration_cost (state, 4420 expiration)) 4421 { 4422 ANASTASIS_redux_fail_ (cb, 4423 cb_cls, 4424 TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID_FOR_STATE, 4425 "could not calculate expiration cost"); 4426 return NULL; 4427 } 4428 } 4429 GNUNET_assert (0 == 4430 json_object_set (state, 4431 "core_secret", 4432 jsecret)); 4433 cb (cb_cls, 4434 TALER_EC_NONE, 4435 state); 4436 GNUNET_JSON_parse_free (spec); 4437 return NULL; 4438 } 4439 4440 4441 /** 4442 * DispatchHandler/Callback function which is called for a 4443 * "clear_secret" action. 4444 * 4445 * @param state state to operate on 4446 * @param arguments arguments to use for operation on state 4447 * @param cb callback to call during/after operation 4448 * @param cb_cls callback closure 4449 * @return NULL 4450 */ 4451 static struct ANASTASIS_ReduxAction * 4452 clear_secret (json_t *state, 4453 const json_t *arguments, 4454 ANASTASIS_ActionCallback cb, 4455 void *cb_cls) 4456 { 4457 if (0 != 4458 json_object_del (state, 4459 "core_secret")) 4460 { 4461 ANASTASIS_redux_fail_ (cb, 4462 cb_cls, 4463 TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID, 4464 "'core_secret' not set"); 4465 return NULL; 4466 } 4467 cb (cb_cls, 4468 TALER_EC_NONE, 4469 state); 4470 return NULL; 4471 } 4472 4473 4474 /** 4475 * DispatchHandler/Callback function which is called for an 4476 * "enter_secret_name" action. 4477 * 4478 * @param state state to operate on 4479 * @param arguments arguments to use for operation on state 4480 * @param cb callback to call during/after operation 4481 * @param cb_cls callback closure 4482 * @return NULL 4483 */ 4484 static struct ANASTASIS_ReduxAction * 4485 enter_secret_name (json_t *state, 4486 const json_t *arguments, 4487 ANASTASIS_ActionCallback cb, 4488 void *cb_cls) 4489 { 4490 const char *secret_name = NULL; 4491 struct GNUNET_JSON_Specification spec[] = { 4492 GNUNET_JSON_spec_string ("name", 4493 &secret_name), 4494 GNUNET_JSON_spec_end () 4495 }; 4496 4497 if (NULL == arguments) 4498 { 4499 ANASTASIS_redux_fail_ (cb, 4500 cb_cls, 4501 TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID, 4502 "arguments missing"); 4503 return NULL; 4504 } 4505 if (GNUNET_OK != 4506 GNUNET_JSON_parse (arguments, 4507 spec, 4508 NULL, NULL)) 4509 { 4510 ANASTASIS_redux_fail_ (cb, 4511 cb_cls, 4512 TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID, 4513 "'name' argument required"); 4514 return NULL; 4515 } 4516 4517 GNUNET_assert (0 == 4518 json_object_set_new (state, 4519 "secret_name", 4520 json_string (secret_name))); 4521 cb (cb_cls, 4522 TALER_EC_NONE, 4523 state); 4524 GNUNET_JSON_parse_free (spec); 4525 return NULL; 4526 } 4527 4528 4529 /** 4530 * DispatchHandler/Callback function which is called for the 4531 * "update_expiration" action in the "secret editing" state. 4532 * Updates how long we are to store the truth and policies 4533 * and computes the new cost. 4534 * 4535 * @param state state to operate on 4536 * @param arguments arguments to use for operation on state 4537 * @param cb callback to call during/after operation 4538 * @param cb_cls callback closure 4539 * @return NULL (synchronous operation) 4540 */ 4541 static struct ANASTASIS_ReduxAction * 4542 update_expiration (json_t *state, 4543 const json_t *arguments, 4544 ANASTASIS_ActionCallback cb, 4545 void *cb_cls) 4546 { 4547 struct GNUNET_TIME_Timestamp expiration; 4548 struct GNUNET_JSON_Specification spec[] = { 4549 GNUNET_JSON_spec_timestamp ("expiration", 4550 &expiration), 4551 GNUNET_JSON_spec_end () 4552 }; 4553 4554 if (NULL == arguments) 4555 { 4556 GNUNET_break (0); 4557 ANASTASIS_redux_fail_ (cb, 4558 cb_cls, 4559 TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID, 4560 "arguments missing"); 4561 return NULL; 4562 } 4563 if (GNUNET_OK != 4564 GNUNET_JSON_parse (arguments, 4565 spec, 4566 NULL, NULL)) 4567 { 4568 GNUNET_break (0); 4569 ANASTASIS_redux_fail_ (cb, 4570 cb_cls, 4571 TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID, 4572 "'expiration' argument required"); 4573 return NULL; 4574 } 4575 if (GNUNET_OK != 4576 update_expiration_cost (state, 4577 expiration)) 4578 { 4579 GNUNET_break (0); 4580 ANASTASIS_redux_fail_ (cb, 4581 cb_cls, 4582 TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID_FOR_STATE, 4583 "could not calculate expiration cost"); 4584 return NULL; 4585 } 4586 cb (cb_cls, 4587 TALER_EC_NONE, 4588 state); 4589 return NULL; 4590 } 4591 4592 4593 /** 4594 * DispatchHandler/Callback function which is called for the 4595 * "next" action in the "secret editing" state. 4596 * Returns an #ANASTASIS_ReduxAction as operation is async. 4597 * 4598 * @param state state to operate on 4599 * @param arguments arguments to use for operation on state 4600 * @param cb callback to call during/after operation 4601 * @param cb_cls callback closure 4602 */ 4603 static struct ANASTASIS_ReduxAction * 4604 finish_secret (json_t *state, 4605 const json_t *arguments, 4606 ANASTASIS_ActionCallback cb, 4607 void *cb_cls) 4608 { 4609 json_t *core_secret; 4610 struct GNUNET_JSON_Specification spec[] = { 4611 GNUNET_JSON_spec_json ("core_secret", 4612 &core_secret), 4613 GNUNET_JSON_spec_end () 4614 }; 4615 4616 if (GNUNET_OK != 4617 GNUNET_JSON_parse (state, 4618 spec, 4619 NULL, NULL)) 4620 { 4621 ANASTASIS_redux_fail_ (cb, 4622 cb_cls, 4623 TALER_EC_ANASTASIS_REDUCER_STATE_INVALID, 4624 "State parsing failed: 'core_secret' is missing"); 4625 return NULL; 4626 } 4627 4628 /* check upload size limit */ 4629 { 4630 enum GNUNET_GenericReturnValue ret; 4631 4632 ret = check_upload_size_limit (state, 4633 core_secret); 4634 switch (ret) 4635 { 4636 case GNUNET_SYSERR: 4637 ANASTASIS_redux_fail_ (cb, 4638 cb_cls, 4639 TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID, 4640 "provider has an upload limit of 0"); 4641 GNUNET_JSON_parse_free (spec); 4642 return NULL; 4643 case GNUNET_NO: 4644 ANASTASIS_redux_fail_ (cb, 4645 cb_cls, 4646 TALER_EC_ANASTASIS_REDUCER_SECRET_TOO_BIG, 4647 NULL); 4648 GNUNET_JSON_parse_free (spec); 4649 return NULL; 4650 default: 4651 break; 4652 } 4653 } 4654 4655 GNUNET_JSON_parse_free (spec); 4656 return upload (state, 4657 cb, 4658 cb_cls); 4659 } 4660 4661 4662 /** 4663 * DispatchHandler/Callback function which is called for a 4664 * "pay" action. 4665 * Returns an #ANASTASIS_ReduxAction as operation is async. 4666 * 4667 * @param state state to operate on 4668 * @param arguments arguments to use for operation on state 4669 * @param cb callback to call during/after operation 4670 * @param cb_cls callback closure 4671 */ 4672 static struct ANASTASIS_ReduxAction * 4673 pay_truths_backup (json_t *state, 4674 const json_t *arguments, 4675 ANASTASIS_ActionCallback cb, 4676 void *cb_cls) 4677 { 4678 /* Clear 'payments' if it exists */ 4679 (void) json_object_del (state, 4680 "payments"); 4681 if (NULL != arguments) 4682 GNUNET_assert (0 == 4683 json_object_set (state, 4684 "pay_arguments", 4685 (json_t *) arguments)); 4686 return upload (state, 4687 cb, 4688 cb_cls); 4689 } 4690 4691 4692 /** 4693 * DispatchHandler/Callback function which is called for a 4694 * "pay" action. 4695 * Returns an #ANASTASIS_ReduxAction as operation is async. 4696 * 4697 * @param state state to operate on 4698 * @param arguments arguments to use for operation on state 4699 * @param cb callback to call during/after operation 4700 * @param cb_cls callback closure 4701 */ 4702 static struct ANASTASIS_ReduxAction * 4703 pay_policies_backup (json_t *state, 4704 const json_t *arguments, 4705 ANASTASIS_ActionCallback cb, 4706 void *cb_cls) 4707 { 4708 /* Clear 'policy_payment_requests' if it exists */ 4709 (void) json_object_del (state, 4710 "policy_payment_requests"); 4711 if (NULL != arguments) 4712 GNUNET_assert (0 == 4713 json_object_set (state, 4714 "pay_arguments", 4715 (json_t *) arguments)); 4716 return upload (state, 4717 cb, 4718 cb_cls); 4719 } 4720 4721 4722 /** 4723 * DispatchHandler/Callback function which is called for a 4724 * "back" action if state is "FINISHED". 4725 * 4726 * @param state state to operate on 4727 * @param arguments arguments to use for operation on state 4728 * @param cb callback to call during/after operation 4729 * @param cb_cls callback closure 4730 * @return NULL 4731 */ 4732 static struct ANASTASIS_ReduxAction * 4733 back_finished (json_t *state, 4734 const json_t *arguments, 4735 ANASTASIS_ActionCallback cb, 4736 void *cb_cls) 4737 { 4738 set_state (state, 4739 ANASTASIS_BACKUP_STATE_SECRET_EDITING); 4740 cb (cb_cls, 4741 TALER_EC_NONE, 4742 state); 4743 return NULL; 4744 } 4745 4746 4747 /** 4748 * Signature of callback function that implements a state transition. 4749 * 4750 * @param state current state 4751 * @param arguments arguments for the state transition 4752 * @param cb function to call when done 4753 * @param cb_cls closure for @a cb 4754 */ 4755 typedef struct ANASTASIS_ReduxAction * 4756 (*DispatchHandler)(json_t *state, 4757 const json_t *arguments, 4758 ANASTASIS_ActionCallback cb, 4759 void *cb_cls); 4760 4761 4762 struct ANASTASIS_ReduxAction * 4763 ANASTASIS_backup_action_ (json_t *state, 4764 const char *action, 4765 const json_t *arguments, 4766 ANASTASIS_ActionCallback cb, 4767 void *cb_cls) 4768 { 4769 struct Dispatcher 4770 { 4771 enum ANASTASIS_BackupState backup_state; 4772 const char *backup_action; 4773 DispatchHandler fun; 4774 } dispatchers[] = { 4775 { 4776 ANASTASIS_BACKUP_STATE_AUTHENTICATIONS_EDITING, 4777 "add_authentication", 4778 &add_authentication 4779 }, 4780 { 4781 ANASTASIS_BACKUP_STATE_AUTHENTICATIONS_EDITING, 4782 "delete_authentication", 4783 &del_authentication 4784 }, 4785 { 4786 ANASTASIS_BACKUP_STATE_AUTHENTICATIONS_EDITING, 4787 "next", 4788 &done_authentication 4789 }, 4790 { 4791 ANASTASIS_BACKUP_STATE_AUTHENTICATIONS_EDITING, 4792 "add_provider", 4793 &add_provider 4794 }, 4795 { 4796 ANASTASIS_BACKUP_STATE_AUTHENTICATIONS_EDITING, 4797 "poll_providers", 4798 &ANASTASIS_REDUX_poll_providers_ 4799 }, 4800 { 4801 ANASTASIS_BACKUP_STATE_AUTHENTICATIONS_EDITING, 4802 "back", 4803 &ANASTASIS_back_generic_decrement_ 4804 }, 4805 { 4806 ANASTASIS_BACKUP_STATE_POLICIES_REVIEWING, 4807 "add_policy", 4808 &add_policy 4809 }, 4810 { 4811 ANASTASIS_BACKUP_STATE_POLICIES_REVIEWING, 4812 "update_policy", 4813 &update_policy 4814 }, 4815 { 4816 ANASTASIS_BACKUP_STATE_POLICIES_REVIEWING, 4817 "delete_policy", 4818 &del_policy 4819 }, 4820 { 4821 ANASTASIS_BACKUP_STATE_POLICIES_REVIEWING, 4822 "delete_challenge", 4823 &del_challenge 4824 }, 4825 { 4826 ANASTASIS_BACKUP_STATE_POLICIES_REVIEWING, 4827 "next", 4828 &done_policy_review 4829 }, 4830 { 4831 ANASTASIS_BACKUP_STATE_POLICIES_REVIEWING, 4832 "back", 4833 &ANASTASIS_back_generic_decrement_ 4834 }, 4835 { 4836 ANASTASIS_BACKUP_STATE_SECRET_EDITING, 4837 "enter_secret", 4838 &enter_secret 4839 }, 4840 { 4841 ANASTASIS_BACKUP_STATE_SECRET_EDITING, 4842 "clear_secret", 4843 &clear_secret 4844 }, 4845 { 4846 ANASTASIS_BACKUP_STATE_SECRET_EDITING, 4847 "enter_secret_name", 4848 &enter_secret_name 4849 }, 4850 { 4851 ANASTASIS_BACKUP_STATE_SECRET_EDITING, 4852 "back", 4853 &ANASTASIS_back_generic_decrement_ 4854 }, 4855 { 4856 ANASTASIS_BACKUP_STATE_SECRET_EDITING, 4857 "update_expiration", 4858 &update_expiration 4859 }, 4860 { 4861 ANASTASIS_BACKUP_STATE_SECRET_EDITING, 4862 "next", 4863 &finish_secret 4864 }, 4865 { 4866 ANASTASIS_BACKUP_STATE_TRUTHS_PAYING, 4867 "pay", 4868 &pay_truths_backup 4869 }, 4870 { 4871 ANASTASIS_BACKUP_STATE_POLICIES_PAYING, 4872 "pay", 4873 &pay_policies_backup 4874 }, 4875 { 4876 ANASTASIS_BACKUP_STATE_BACKUP_FINISHED, 4877 "back", 4878 &back_finished 4879 }, 4880 { ANASTASIS_BACKUP_STATE_INVALID, NULL, NULL } 4881 }; 4882 const char *s = json_string_value (json_object_get (state, 4883 "backup_state")); 4884 enum ANASTASIS_BackupState bs; 4885 4886 GNUNET_assert (NULL != s); /* holds as per invariant of caller */ 4887 bs = ANASTASIS_backup_state_from_string_ (s); 4888 if (ANASTASIS_BACKUP_STATE_INVALID == bs) 4889 { 4890 ANASTASIS_redux_fail_ (cb, 4891 cb_cls, 4892 TALER_EC_ANASTASIS_REDUCER_STATE_INVALID, 4893 "unknown 'backup_state'"); 4894 return NULL; 4895 } 4896 for (unsigned int i = 0; NULL != dispatchers[i].fun; i++) 4897 { 4898 if ( (bs == dispatchers[i].backup_state) && 4899 (0 == strcmp (action, 4900 dispatchers[i].backup_action)) ) 4901 { 4902 return dispatchers[i].fun (state, 4903 arguments, 4904 cb, 4905 cb_cls); 4906 } 4907 } 4908 ANASTASIS_redux_fail_ (cb, 4909 cb_cls, 4910 TALER_EC_ANASTASIS_REDUCER_ACTION_INVALID, 4911 action); 4912 return NULL; 4913 } 4914 4915 4916 /** 4917 * State for a #ANASTASIS_REDUX_backup_begin_() operation. 4918 */ 4919 struct BackupStartState; 4920 4921 4922 /** 4923 * Entry in the list of all known applicable Anastasis providers. 4924 * Used to wait for it to complete downloading /config. 4925 */ 4926 struct BackupStartStateProviderEntry 4927 { 4928 /** 4929 * Kept in a DLL. 4930 */ 4931 struct BackupStartStateProviderEntry *next; 4932 4933 /** 4934 * Kept in a DLL. 4935 */ 4936 struct BackupStartStateProviderEntry *prev; 4937 4938 /** 4939 * Main operation this entry is part of. 4940 */ 4941 struct BackupStartState *bss; 4942 4943 /** 4944 * Resulting provider information, NULL if not (yet) available. 4945 */ 4946 json_t *istate; 4947 4948 /** 4949 * Ongoing reducer action to obtain /config, NULL if completed. 4950 */ 4951 struct ANASTASIS_ReduxAction *ra; 4952 4953 /** 4954 * Final result of the operation (once completed). 4955 */ 4956 enum TALER_ErrorCode ec; 4957 }; 4958 4959 4960 struct BackupStartState 4961 { 4962 /** 4963 * Head of list of provider /config operations we are doing. 4964 */ 4965 struct BackupStartStateProviderEntry *pe_head; 4966 4967 /** 4968 * Tail of list of provider /config operations we are doing. 4969 */ 4970 struct BackupStartStateProviderEntry *pe_tail; 4971 4972 /** 4973 * State we are updating. 4974 */ 4975 json_t *state; 4976 4977 /** 4978 * Function to call when we are done. 4979 */ 4980 ANASTASIS_ActionCallback cb; 4981 4982 /** 4983 * Closure for @e cb. 4984 */ 4985 void *cb_cls; 4986 4987 /** 4988 * Redux action we returned to our controller. 4989 */ 4990 struct ANASTASIS_ReduxAction ra; 4991 4992 /** 4993 * Number of provider /config operations in @e ba_head that 4994 * are still awaiting completion. 4995 */ 4996 unsigned int pending; 4997 }; 4998 4999 5000 /** 5001 * The backup start operation is being aborted, terminate. 5002 * 5003 * @param cls a `struct BackupStartState *` 5004 */ 5005 static void 5006 abort_backup_begin_cb (void *cls) 5007 { 5008 struct BackupStartState *bss = cls; 5009 struct BackupStartStateProviderEntry *pe; 5010 5011 while (NULL != (pe = bss->pe_head)) 5012 { 5013 GNUNET_CONTAINER_DLL_remove (bss->pe_head, 5014 bss->pe_tail, 5015 pe); 5016 if (NULL != pe->ra) 5017 pe->ra->cleanup (pe->ra->cleanup_cls); 5018 json_decref (pe->istate); 5019 GNUNET_free (pe); 5020 } 5021 json_decref (bss->state); 5022 GNUNET_free (bss); 5023 } 5024 5025 5026 /** 5027 * We finished downloading /config from all providers, merge 5028 * into the main state, trigger the continuation and free our 5029 * state. 5030 * 5031 * @param[in] bss main state to merge into 5032 */ 5033 static void 5034 providers_complete (struct BackupStartState *bss) 5035 { 5036 struct BackupStartStateProviderEntry *pe; 5037 json_t *tlist; 5038 5039 tlist = json_object_get (bss->state, 5040 "authentication_providers"); 5041 if (NULL == tlist) 5042 { 5043 tlist = json_object (); 5044 GNUNET_assert (NULL != tlist); 5045 GNUNET_assert (0 == 5046 json_object_set_new (bss->state, 5047 "authentication_providers", 5048 tlist)); 5049 } 5050 while (NULL != (pe = bss->pe_head)) 5051 { 5052 json_t *provider_list; 5053 5054 GNUNET_CONTAINER_DLL_remove (bss->pe_head, 5055 bss->pe_tail, 5056 pe); 5057 provider_list = json_object_get (pe->istate, 5058 "authentication_providers"); 5059 /* merge provider_list into tlist (overriding existing entries) */ 5060 if (NULL != provider_list) 5061 { 5062 const char *url; 5063 json_t *value; 5064 5065 json_object_foreach (provider_list, url, value) { 5066 GNUNET_assert (0 == 5067 json_object_set (tlist, 5068 url, 5069 value)); 5070 } 5071 } 5072 json_decref (pe->istate); 5073 GNUNET_free (pe); 5074 } 5075 bss->cb (bss->cb_cls, 5076 TALER_EC_NONE, 5077 bss->state); 5078 json_decref (bss->state); 5079 GNUNET_free (bss); 5080 } 5081 5082 5083 /** 5084 * Function called when the complete information about a provider 5085 * was added to @a new_state. 5086 * 5087 * @param cls a `struct BackupStartStateProviderEntry` 5088 * @param error error code 5089 * @param new_state resulting new state 5090 */ 5091 static void 5092 provider_added_cb (void *cls, 5093 enum TALER_ErrorCode error, 5094 json_t *new_state) 5095 { 5096 struct BackupStartStateProviderEntry *pe = cls; 5097 5098 pe->ra = NULL; 5099 pe->istate = json_incref (new_state); 5100 pe->ec = error; 5101 pe->bss->pending--; 5102 if (0 == pe->bss->pending) 5103 providers_complete (pe->bss); 5104 } 5105 5106 5107 struct ANASTASIS_ReduxAction * 5108 ANASTASIS_REDUX_backup_begin_ (json_t *state, 5109 const json_t *arguments, 5110 ANASTASIS_ActionCallback cb, 5111 void *cb_cls) 5112 { 5113 json_t *provider_list; 5114 struct BackupStartState *bss; 5115 5116 provider_list = json_object_get (state, 5117 "authentication_providers"); 5118 if (NULL == provider_list) 5119 { 5120 GNUNET_break (0); 5121 ANASTASIS_redux_fail_ (cb, 5122 cb_cls, 5123 TALER_EC_ANASTASIS_REDUCER_STATE_INVALID, 5124 "'authentication_providers' missing"); 5125 return NULL; 5126 } 5127 bss = GNUNET_new (struct BackupStartState); 5128 bss->state = json_incref (state); 5129 bss->cb = cb; 5130 bss->cb_cls = cb_cls; 5131 bss->ra.cleanup_cls = bss; 5132 bss->ra.cleanup = &abort_backup_begin_cb; 5133 bss->pending = 1; /* decremented after initialization loop */ 5134 5135 { 5136 json_t *prov; 5137 const char *url; 5138 json_object_foreach (provider_list, url, prov) { 5139 struct BackupStartStateProviderEntry *pe; 5140 json_t *istate; 5141 const char *status; 5142 struct GNUNET_JSON_Specification spec[] = { 5143 GNUNET_JSON_spec_string ("status", 5144 &status), 5145 GNUNET_JSON_spec_end () 5146 }; 5147 5148 if (GNUNET_OK != 5149 GNUNET_JSON_parse (prov, 5150 spec, 5151 NULL, NULL)) 5152 { 5153 /* skip malformed provider entry */ 5154 GNUNET_break_op (0); 5155 continue; 5156 } 5157 if (0 == strcmp (status, 5158 "disabled")) 5159 continue; 5160 pe = GNUNET_new (struct BackupStartStateProviderEntry); 5161 pe->bss = bss; 5162 istate = json_object (); 5163 GNUNET_assert (NULL != istate); 5164 GNUNET_CONTAINER_DLL_insert (bss->pe_head, 5165 bss->pe_tail, 5166 pe); 5167 pe->ra = ANASTASIS_REDUX_add_provider_to_state_ (url, 5168 istate, 5169 &provider_added_cb, 5170 pe); 5171 json_decref (istate); 5172 if (NULL != pe->ra) 5173 bss->pending++; 5174 } 5175 } 5176 bss->pending--; 5177 if (0 == bss->pending) 5178 { 5179 providers_complete (bss); 5180 return NULL; 5181 } 5182 return &bss->ra; 5183 }