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