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