taler-exchange-offline.c (170849B)
1 /* 2 This file is part of TALER 3 Copyright (C) 2020-2026 Taler Systems SA 4 5 TALER 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 TALER 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 TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> 15 */ 16 /** 17 * @file taler-exchange-offline.c 18 * @brief Support for operations involving the exchange's offline master key. 19 * @author Christian Grothoff 20 */ 21 #include "taler/platform.h" 22 #include <gnunet/gnunet_json_lib.h> 23 #include <gnunet/gnunet_util_lib.h> 24 #include "taler/taler_json_lib.h" 25 26 struct AmlStaffRequest; 27 #define TALER_EXCHANGE_POST_MANAGEMENT_AML_OFFICERS_RESULT_CLOSURE \ 28 struct AmlStaffRequest 29 #include "taler/taler-exchange/post-management-aml-officers.h" 30 31 struct DenomRevocationRequest; 32 #define TALER_EXCHANGE_POST_MANAGEMENT_DENOMINATIONS_REVOKE_RESULT_CLOSURE \ 33 struct DenomRevocationRequest 34 #include \ 35 "taler/taler-exchange/post-management-denominations-H_DENOM_PUB-revoke.h" 36 37 struct SignkeyRevocationRequest; 38 #define TALER_EXCHANGE_POST_MANAGEMENT_SIGNKEYS_REVOKE_RESULT_CLOSURE \ 39 struct SignkeyRevocationRequest 40 #include \ 41 "taler/taler-exchange/post-management-signkeys-EXCHANGE_PUB-revoke.h" 42 43 struct AuditorAddRequest; 44 #define TALER_EXCHANGE_POST_MANAGEMENT_AUDITORS_RESULT_CLOSURE \ 45 struct AuditorAddRequest 46 #include "taler/taler-exchange/post-management-auditors.h" 47 48 struct AuditorDelRequest; 49 #define TALER_EXCHANGE_POST_MANAGEMENT_AUDITORS_DISABLE_RESULT_CLOSURE \ 50 struct AuditorDelRequest 51 #include "taler/taler-exchange/post-management-auditors-AUDITOR_PUB-disable.h" 52 53 struct WireAddRequest; 54 #define TALER_EXCHANGE_POST_MANAGEMENT_WIRE_RESULT_CLOSURE \ 55 struct WireAddRequest 56 #include "taler/taler-exchange/post-management-wire.h" 57 58 struct WireDelRequest; 59 #define TALER_EXCHANGE_POST_MANAGEMENT_WIRE_DISABLE_RESULT_CLOSURE \ 60 struct WireDelRequest 61 #include "taler/taler-exchange/post-management-wire-disable.h" 62 63 struct WireFeeRequest; 64 #define TALER_EXCHANGE_POST_MANAGEMENT_WIRE_FEES_RESULT_CLOSURE \ 65 struct WireFeeRequest 66 #include "taler/taler-exchange/post-management-wire-fee.h" 67 68 struct GlobalFeeRequest; 69 #define TALER_EXCHANGE_POST_MANAGEMENT_GLOBAL_FEES_RESULT_CLOSURE \ 70 struct GlobalFeeRequest 71 #include "taler/taler-exchange/post-management-global-fees.h" 72 73 struct DrainProfitsRequest; 74 #define TALER_EXCHANGE_POST_MANAGEMENT_DRAIN_RESULT_CLOSURE \ 75 struct DrainProfitsRequest 76 #include "taler/taler-exchange/post-management-drain.h" 77 78 struct UploadKeysRequest; 79 #define TALER_EXCHANGE_POST_MANAGEMENT_KEYS_RESULT_CLOSURE \ 80 struct UploadKeysRequest 81 #include "taler/taler-exchange/post-management-keys.h" 82 83 struct UploadExtensionsRequest; 84 #define TALER_EXCHANGE_POST_MANAGEMENT_EXTENSIONS_RESULT_CLOSURE \ 85 struct UploadExtensionsRequest 86 #include "taler/taler-exchange/post-management-extensions.h" 87 88 struct PartnerAddRequest; 89 #define TALER_EXCHANGE_POST_MANAGEMENT_PARTNERS_RESULT_CLOSURE \ 90 struct PartnerAddRequest 91 #include "taler/taler-exchange/post-management-partners.h" 92 93 #define TALER_EXCHANGE_GET_MANAGEMENT_KEYS_RESULT_CLOSURE \ 94 char *const 95 #include "taler/taler-exchange/get-management-keys.h" 96 97 #include "taler/taler_extensions.h" 98 #include <regex.h> 99 100 101 /** 102 * Name of the input for the 'sign' and 'show' operation. 103 * The last component --by convention-- identifies the protocol version 104 * and should be incremented whenever the JSON format of the 'argument' changes. 105 */ 106 #define OP_INPUT_KEYS "exchange-input-keys-0" 107 108 /** 109 * Name of the operation to 'disable auditor' 110 * The last component --by convention-- identifies the protocol version 111 * and should be incremented whenever the JSON format of the 'argument' changes. 112 */ 113 #define OP_DISABLE_AUDITOR "exchange-disable-auditor-0" 114 115 /** 116 * Name of the operation to 'enable auditor' 117 * The last component --by convention-- identifies the protocol version 118 * and should be incremented whenever the JSON format of the 'argument' changes. 119 */ 120 #define OP_ENABLE_AUDITOR "exchange-enable-auditor-0" 121 122 /** 123 * Name of the operation to 'enable wire' 124 * The last component --by convention-- identifies the protocol version 125 * and should be incremented whenever the JSON format of the 'argument' changes. 126 */ 127 #define OP_ENABLE_WIRE "exchange-enable-wire-0" 128 129 /** 130 * Name of the operation to 'disable wire' 131 * The last component --by convention-- identifies the protocol version 132 * and should be incremented whenever the JSON format of the 'argument' changes. 133 */ 134 #define OP_DISABLE_WIRE "exchange-disable-wire-0" 135 136 /** 137 * Name of the operation to set a 'wire-fee' 138 * The last component --by convention-- identifies the protocol version 139 * and should be incremented whenever the JSON format of the 'argument' changes. 140 */ 141 #define OP_SET_WIRE_FEE "exchange-set-wire-fee-0" 142 143 /** 144 * Name of the operation to set a 'global-fee' 145 * The last component --by convention-- identifies the protocol version 146 * and should be incremented whenever the JSON format of the 'argument' changes. 147 */ 148 #define OP_SET_GLOBAL_FEE "exchange-set-global-fee-0" 149 150 /** 151 * Name of the operation to 'upload' key signatures 152 * The last component --by convention-- identifies the protocol version 153 * and should be incremented whenever the JSON format of the 'argument' changes. 154 */ 155 #define OP_UPLOAD_SIGS "exchange-upload-sigs-0" 156 157 /** 158 * Name of the operation to 'revoke-denomination' key 159 * The last component --by convention-- identifies the protocol version 160 * and should be incremented whenever the JSON format of the 'argument' changes. 161 */ 162 #define OP_REVOKE_DENOMINATION "exchange-revoke-denomination-0" 163 164 /** 165 * Name of the operation to 'revoke-signkey' 166 * The last component --by convention-- identifies the protocol version 167 * and should be incremented whenever the JSON format of the 'argument' changes. 168 */ 169 #define OP_REVOKE_SIGNKEY "exchange-revoke-signkey-0" 170 171 /** 172 * Show the offline signing key. 173 * The last component --by convention-- identifies the protocol version 174 * and should be incremented whenever the JSON format of the 'argument' changes. 175 */ 176 #define OP_SETUP "exchange-setup-0" 177 178 /** 179 * sign the enabled and configured extensions. 180 */ 181 #define OP_EXTENSIONS "exchange-extensions-0" 182 183 /** 184 * Generate message to drain profits. 185 */ 186 #define OP_DRAIN_PROFITS "exchange-drain-profits-0" 187 188 /** 189 * Setup AML staff. 190 */ 191 #define OP_UPDATE_AML_STAFF "exchange-add-aml-staff-0" 192 193 /** 194 * Setup partner exchange for wad transfers. 195 */ 196 #define OP_ADD_PARTNER "exchange-add-partner-0" 197 198 /** 199 * Our private key, initialized in #load_offline_key(). 200 */ 201 static struct TALER_MasterPrivateKeyP master_priv; 202 203 /** 204 * Our public key, initialized in #load_offline_key(). 205 */ 206 static struct TALER_MasterPublicKeyP master_pub; 207 208 /** 209 * Our context for making HTTP requests. 210 */ 211 static struct GNUNET_CURL_Context *ctx; 212 213 /** 214 * Reschedule context for #ctx. 215 */ 216 static struct GNUNET_CURL_RescheduleContext *rc; 217 218 /** 219 * Handle to the exchange's configuration 220 */ 221 static const struct GNUNET_CONFIGURATION_Handle *kcfg; 222 223 /** 224 * Age restriction configuration 225 */ 226 static bool ar_enabled = false; 227 static struct TALER_AgeRestrictionConfig ar_config = {0}; 228 229 /** 230 * Return value from main(). 231 */ 232 static int global_ret; 233 234 /** 235 * Input to consume. 236 */ 237 static json_t *in; 238 239 /** 240 * Array of actions to perform. 241 */ 242 static json_t *out; 243 244 /** 245 * Currency we have configured. 246 */ 247 static char *currency; 248 249 /** 250 * URL of the exchange we are interacting with 251 * as per our configuration. 252 */ 253 static char *CFG_exchange_url; 254 255 /** 256 * A subcommand supported by this program. 257 */ 258 struct SubCommand 259 { 260 /** 261 * Name of the command. 262 */ 263 const char *name; 264 265 /** 266 * Help text for the command. 267 */ 268 const char *help; 269 270 /** 271 * Function implementing the command. 272 * 273 * @param args subsequent command line arguments (char **) 274 */ 275 void (*cb)(char *const *args); 276 }; 277 278 279 /** 280 * Data structure for denomination revocation requests. 281 */ 282 struct DenomRevocationRequest 283 { 284 285 /** 286 * Kept in a DLL. 287 */ 288 struct DenomRevocationRequest *next; 289 290 /** 291 * Kept in a DLL. 292 */ 293 struct DenomRevocationRequest *prev; 294 295 /** 296 * Operation handle. 297 */ 298 struct TALER_EXCHANGE_PostManagementDenominationsRevokeHandle *h; 299 300 /** 301 * Array index of the associated command. 302 */ 303 size_t idx; 304 }; 305 306 307 /** 308 * Data structure for signkey revocation requests. 309 */ 310 struct SignkeyRevocationRequest 311 { 312 313 /** 314 * Kept in a DLL. 315 */ 316 struct SignkeyRevocationRequest *next; 317 318 /** 319 * Kept in a DLL. 320 */ 321 struct SignkeyRevocationRequest *prev; 322 323 /** 324 * Operation handle. 325 */ 326 struct TALER_EXCHANGE_PostManagementSignkeysRevokeHandle *h; 327 328 /** 329 * Array index of the associated command. 330 */ 331 size_t idx; 332 }; 333 334 335 /** 336 * Data structure for auditor add requests. 337 */ 338 struct AuditorAddRequest 339 { 340 341 /** 342 * Kept in a DLL. 343 */ 344 struct AuditorAddRequest *next; 345 346 /** 347 * Kept in a DLL. 348 */ 349 struct AuditorAddRequest *prev; 350 351 /** 352 * Operation handle. 353 */ 354 struct TALER_EXCHANGE_PostManagementAuditorsHandle *h; 355 356 /** 357 * Array index of the associated command. 358 */ 359 size_t idx; 360 }; 361 362 363 /** 364 * Data structure for auditor del requests. 365 */ 366 struct AuditorDelRequest 367 { 368 369 /** 370 * Kept in a DLL. 371 */ 372 struct AuditorDelRequest *next; 373 374 /** 375 * Kept in a DLL. 376 */ 377 struct AuditorDelRequest *prev; 378 379 /** 380 * Operation handle. 381 */ 382 struct TALER_EXCHANGE_PostManagementAuditorsDisableHandle *h; 383 384 /** 385 * Array index of the associated command. 386 */ 387 size_t idx; 388 }; 389 390 391 /** 392 * Data structure for wire add requests. 393 */ 394 struct WireAddRequest 395 { 396 397 /** 398 * Kept in a DLL. 399 */ 400 struct WireAddRequest *next; 401 402 /** 403 * Kept in a DLL. 404 */ 405 struct WireAddRequest *prev; 406 407 /** 408 * Operation handle. 409 */ 410 struct TALER_EXCHANGE_PostManagementWireHandle *h; 411 412 /** 413 * Array index of the associated command. 414 */ 415 size_t idx; 416 }; 417 418 419 /** 420 * Data structure for wire del requests. 421 */ 422 struct WireDelRequest 423 { 424 425 /** 426 * Kept in a DLL. 427 */ 428 struct WireDelRequest *next; 429 430 /** 431 * Kept in a DLL. 432 */ 433 struct WireDelRequest *prev; 434 435 /** 436 * Operation handle. 437 */ 438 struct TALER_EXCHANGE_PostManagementWireDisableHandle *h; 439 440 /** 441 * Array index of the associated command. 442 */ 443 size_t idx; 444 }; 445 446 447 /** 448 * Data structure for announcing wire fees. 449 */ 450 struct WireFeeRequest 451 { 452 453 /** 454 * Kept in a DLL. 455 */ 456 struct WireFeeRequest *next; 457 458 /** 459 * Kept in a DLL. 460 */ 461 struct WireFeeRequest *prev; 462 463 /** 464 * Operation handle. 465 */ 466 struct TALER_EXCHANGE_PostManagementWireFeesHandle *h; 467 468 /** 469 * Array index of the associated command. 470 */ 471 size_t idx; 472 }; 473 474 475 /** 476 * Data structure for draining profits. 477 */ 478 struct DrainProfitsRequest 479 { 480 481 /** 482 * Kept in a DLL. 483 */ 484 struct DrainProfitsRequest *next; 485 486 /** 487 * Kept in a DLL. 488 */ 489 struct DrainProfitsRequest *prev; 490 491 /** 492 * Operation handle. 493 */ 494 struct TALER_EXCHANGE_PostManagementDrainHandle *h; 495 496 /** 497 * Array index of the associated command. 498 */ 499 size_t idx; 500 }; 501 502 503 /** 504 * Data structure for announcing global fees. 505 */ 506 struct GlobalFeeRequest 507 { 508 509 /** 510 * Kept in a DLL. 511 */ 512 struct GlobalFeeRequest *next; 513 514 /** 515 * Kept in a DLL. 516 */ 517 struct GlobalFeeRequest *prev; 518 519 /** 520 * Operation handle. 521 */ 522 struct TALER_EXCHANGE_PostManagementGlobalFeesHandle *h; 523 524 /** 525 * Array index of the associated command. 526 */ 527 size_t idx; 528 }; 529 530 531 /** 532 * Ongoing /keys request. 533 */ 534 struct UploadKeysRequest 535 { 536 /** 537 * Kept in a DLL. 538 */ 539 struct UploadKeysRequest *next; 540 541 /** 542 * Kept in a DLL. 543 */ 544 struct UploadKeysRequest *prev; 545 546 /** 547 * Operation handle. 548 */ 549 struct TALER_EXCHANGE_PostManagementKeysHandle *h; 550 551 /** 552 * Operation index. 553 */ 554 size_t idx; 555 }; 556 557 /** 558 * Ongoing /management/extensions request. 559 */ 560 struct UploadExtensionsRequest 561 { 562 /** 563 * Kept in a DLL. 564 */ 565 struct UploadExtensionsRequest *next; 566 567 /** 568 * Kept in a DLL. 569 */ 570 struct UploadExtensionsRequest *prev; 571 572 /** 573 * Operation handle. 574 */ 575 struct TALER_EXCHANGE_PostManagementExtensionsHandle *h; 576 577 /** 578 * Operation index. 579 */ 580 size_t idx; 581 }; 582 583 584 /** 585 * Data structure for AML staff requests. 586 */ 587 struct AmlStaffRequest 588 { 589 590 /** 591 * Kept in a DLL. 592 */ 593 struct AmlStaffRequest *next; 594 595 /** 596 * Kept in a DLL. 597 */ 598 struct AmlStaffRequest *prev; 599 600 /** 601 * Operation handle. 602 */ 603 struct TALER_EXCHANGE_PostManagementAmlOfficersHandle *h; 604 605 /** 606 * Array index of the associated command. 607 */ 608 size_t idx; 609 }; 610 611 612 /** 613 * Data structure for partner add requests. 614 */ 615 struct PartnerAddRequest 616 { 617 618 /** 619 * Kept in a DLL. 620 */ 621 struct PartnerAddRequest *next; 622 623 /** 624 * Kept in a DLL. 625 */ 626 struct PartnerAddRequest *prev; 627 628 /** 629 * Operation handle. 630 */ 631 struct TALER_EXCHANGE_PostManagementPartnersHandle *h; 632 633 /** 634 * Array index of the associated command. 635 */ 636 size_t idx; 637 }; 638 639 640 /** 641 * Next work item to perform. 642 */ 643 static struct GNUNET_SCHEDULER_Task *nxt; 644 645 /** 646 * Handle for #do_download. 647 */ 648 static struct TALER_EXCHANGE_GetManagementKeysHandle *mgkh; 649 650 /** 651 * Active AML staff change requests. 652 */ 653 static struct AmlStaffRequest *asr_head; 654 655 /** 656 * Active AML staff change requests. 657 */ 658 static struct AmlStaffRequest *asr_tail; 659 660 /** 661 * Active partner add requests. 662 */ 663 static struct PartnerAddRequest *par_head; 664 665 /** 666 * Active partner add requests. 667 */ 668 static struct PartnerAddRequest *par_tail; 669 670 /** 671 * Active denomiantion revocation requests. 672 */ 673 static struct DenomRevocationRequest *drr_head; 674 675 /** 676 * Active denomiantion revocation requests. 677 */ 678 static struct DenomRevocationRequest *drr_tail; 679 680 /** 681 * Active signkey revocation requests. 682 */ 683 static struct SignkeyRevocationRequest *srr_head; 684 685 /** 686 * Active signkey revocation requests. 687 */ 688 static struct SignkeyRevocationRequest *srr_tail; 689 690 /** 691 * Active auditor add requests. 692 */ 693 static struct AuditorAddRequest *aar_head; 694 695 /** 696 * Active auditor add requests. 697 */ 698 static struct AuditorAddRequest *aar_tail; 699 700 /** 701 * Active auditor del requests. 702 */ 703 static struct AuditorDelRequest *adr_head; 704 705 /** 706 * Active auditor del requests. 707 */ 708 static struct AuditorDelRequest *adr_tail; 709 710 /** 711 * Active wire add requests. 712 */ 713 static struct WireAddRequest *war_head; 714 715 /** 716 * Active wire add requests. 717 */ 718 static struct WireAddRequest *war_tail; 719 720 /** 721 * Active wire del requests. 722 */ 723 static struct WireDelRequest *wdr_head; 724 725 /** 726 * Active wire del requests. 727 */ 728 static struct WireDelRequest *wdr_tail; 729 730 /** 731 * Active wire fee requests. 732 */ 733 static struct WireFeeRequest *wfr_head; 734 735 /** 736 * Active wire fee requests. 737 */ 738 static struct WireFeeRequest *wfr_tail; 739 740 /** 741 * Active global fee requests. 742 */ 743 static struct GlobalFeeRequest *gfr_head; 744 745 /** 746 * Active global fee requests. 747 */ 748 static struct GlobalFeeRequest *gfr_tail; 749 750 /** 751 * Active keys upload requests. 752 */ 753 static struct UploadKeysRequest *ukr_head; 754 755 /** 756 * Active keys upload requests. 757 */ 758 static struct UploadKeysRequest *ukr_tail; 759 760 /** 761 * Active extensions upload requests. 762 */ 763 static struct UploadExtensionsRequest *uer_head; 764 765 /** 766 * Active extensions upload requests. 767 */ 768 static struct UploadExtensionsRequest *uer_tail; 769 770 /** 771 * Active drain profits requests. 772 */ 773 struct DrainProfitsRequest *dpr_head; 774 775 /** 776 * Active drain profits requests. 777 */ 778 static struct DrainProfitsRequest *dpr_tail; 779 780 781 /** 782 * Shutdown task. Invoked when the application is being terminated. 783 * 784 * @param cls NULL 785 */ 786 static void 787 do_shutdown (void *cls) 788 { 789 (void) cls; 790 791 { 792 struct AmlStaffRequest *asr; 793 794 while (NULL != (asr = asr_head)) 795 { 796 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 797 "Aborting incomplete AML staff update #%u\n", 798 (unsigned int) asr->idx); 799 TALER_EXCHANGE_post_management_aml_officers_cancel (asr->h); 800 GNUNET_CONTAINER_DLL_remove (asr_head, 801 asr_tail, 802 asr); 803 GNUNET_free (asr); 804 } 805 } 806 { 807 struct PartnerAddRequest *par; 808 809 while (NULL != (par = par_head)) 810 { 811 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 812 "Aborting incomplete partner add request #%u\n", 813 (unsigned int) par->idx); 814 TALER_EXCHANGE_post_management_partners_cancel (par->h); 815 GNUNET_CONTAINER_DLL_remove (par_head, 816 par_tail, 817 par); 818 GNUNET_free (par); 819 } 820 } 821 { 822 struct DenomRevocationRequest *drr; 823 824 while (NULL != (drr = drr_head)) 825 { 826 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 827 "Aborting incomplete denomination revocation #%u\n", 828 (unsigned int) drr->idx); 829 TALER_EXCHANGE_post_management_denominations_revoke_cancel (drr->h); 830 GNUNET_CONTAINER_DLL_remove (drr_head, 831 drr_tail, 832 drr); 833 GNUNET_free (drr); 834 } 835 } 836 { 837 struct SignkeyRevocationRequest *srr; 838 839 while (NULL != (srr = srr_head)) 840 { 841 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 842 "Aborting incomplete signkey revocation #%u\n", 843 (unsigned int) srr->idx); 844 TALER_EXCHANGE_post_management_signkeys_revoke_cancel (srr->h); 845 GNUNET_CONTAINER_DLL_remove (srr_head, 846 srr_tail, 847 srr); 848 GNUNET_free (srr); 849 } 850 } 851 852 { 853 struct AuditorAddRequest *aar; 854 855 while (NULL != (aar = aar_head)) 856 { 857 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 858 "Aborting incomplete auditor add #%u\n", 859 (unsigned int) aar->idx); 860 TALER_EXCHANGE_post_management_auditors_cancel (aar->h); 861 GNUNET_CONTAINER_DLL_remove (aar_head, 862 aar_tail, 863 aar); 864 GNUNET_free (aar); 865 } 866 } 867 { 868 struct AuditorDelRequest *adr; 869 870 while (NULL != (adr = adr_head)) 871 { 872 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 873 "Aborting incomplete auditor del #%u\n", 874 (unsigned int) adr->idx); 875 TALER_EXCHANGE_post_management_auditors_disable_cancel (adr->h); 876 GNUNET_CONTAINER_DLL_remove (adr_head, 877 adr_tail, 878 adr); 879 GNUNET_free (adr); 880 } 881 } 882 { 883 struct WireAddRequest *war; 884 885 while (NULL != (war = war_head)) 886 { 887 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 888 "Aborting incomplete wire add #%u\n", 889 (unsigned int) war->idx); 890 TALER_EXCHANGE_post_management_wire_cancel (war->h); 891 GNUNET_CONTAINER_DLL_remove (war_head, 892 war_tail, 893 war); 894 GNUNET_free (war); 895 } 896 } 897 { 898 struct WireDelRequest *wdr; 899 900 while (NULL != (wdr = wdr_head)) 901 { 902 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 903 "Aborting incomplete wire del #%u\n", 904 (unsigned int) wdr->idx); 905 TALER_EXCHANGE_post_management_wire_disable_cancel (wdr->h); 906 GNUNET_CONTAINER_DLL_remove (wdr_head, 907 wdr_tail, 908 wdr); 909 GNUNET_free (wdr); 910 } 911 } 912 { 913 struct WireFeeRequest *wfr; 914 915 while (NULL != (wfr = wfr_head)) 916 { 917 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 918 "Aborting incomplete wire fee #%u\n", 919 (unsigned int) wfr->idx); 920 TALER_EXCHANGE_post_management_wire_fees_cancel (wfr->h); 921 GNUNET_CONTAINER_DLL_remove (wfr_head, 922 wfr_tail, 923 wfr); 924 GNUNET_free (wfr); 925 } 926 } 927 { 928 struct GlobalFeeRequest *gfr; 929 930 while (NULL != (gfr = gfr_head)) 931 { 932 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 933 "Aborting incomplete global fee #%u\n", 934 (unsigned int) gfr->idx); 935 TALER_EXCHANGE_post_management_global_fees_cancel (gfr->h); 936 GNUNET_CONTAINER_DLL_remove (gfr_head, 937 gfr_tail, 938 gfr); 939 GNUNET_free (gfr); 940 } 941 } 942 { 943 struct UploadKeysRequest *ukr; 944 945 while (NULL != (ukr = ukr_head)) 946 { 947 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 948 "Aborting incomplete key signature upload #%u\n", 949 (unsigned int) ukr->idx); 950 TALER_EXCHANGE_post_management_keys_cancel (ukr->h); 951 GNUNET_CONTAINER_DLL_remove (ukr_head, 952 ukr_tail, 953 ukr); 954 GNUNET_free (ukr); 955 } 956 } 957 { 958 struct UploadExtensionsRequest *uer; 959 960 while (NULL != (uer = uer_head)) 961 { 962 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 963 "Aborting incomplete extensions signature upload #%u\n", 964 (unsigned int) uer->idx); 965 TALER_EXCHANGE_post_management_extensions_cancel (uer->h); 966 GNUNET_CONTAINER_DLL_remove (uer_head, 967 uer_tail, 968 uer); 969 GNUNET_free (uer); 970 } 971 } 972 973 { 974 struct DrainProfitsRequest *dpr; 975 976 while (NULL != (dpr = dpr_head)) 977 { 978 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 979 "Aborting incomplete drain profits request #%u\n", 980 (unsigned int) dpr->idx); 981 TALER_EXCHANGE_post_management_drain_cancel (dpr->h); 982 GNUNET_CONTAINER_DLL_remove (dpr_head, 983 dpr_tail, 984 dpr); 985 GNUNET_free (dpr); 986 } 987 } 988 989 if (NULL != out) 990 { 991 if (EXIT_SUCCESS == global_ret) 992 json_dumpf (out, 993 stdout, 994 JSON_INDENT (2)); 995 json_decref (out); 996 out = NULL; 997 } 998 if (NULL != in) 999 { 1000 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 1001 "Input not consumed!\n"); 1002 json_decref (in); 1003 in = NULL; 1004 } 1005 if (NULL != nxt) 1006 { 1007 GNUNET_SCHEDULER_cancel (nxt); 1008 nxt = NULL; 1009 } 1010 if (NULL != mgkh) 1011 { 1012 TALER_EXCHANGE_get_management_keys_cancel (mgkh); 1013 mgkh = NULL; 1014 } 1015 if (NULL != ctx) 1016 { 1017 GNUNET_CURL_fini (ctx); 1018 ctx = NULL; 1019 } 1020 if (NULL != rc) 1021 { 1022 GNUNET_CURL_gnunet_rc_destroy (rc); 1023 rc = NULL; 1024 } 1025 } 1026 1027 1028 /** 1029 * Test if we should shut down because all tasks are done. 1030 */ 1031 static void 1032 test_shutdown (void) 1033 { 1034 if ( (NULL == drr_head) && 1035 (NULL == par_head) && 1036 (NULL == asr_head) && 1037 (NULL == srr_head) && 1038 (NULL == aar_head) && 1039 (NULL == adr_head) && 1040 (NULL == war_head) && 1041 (NULL == wdr_head) && 1042 (NULL == wfr_head) && 1043 (NULL == gfr_head) && 1044 (NULL == ukr_head) && 1045 (NULL == uer_head) && 1046 (NULL == dpr_head) && 1047 (NULL == mgkh) && 1048 (NULL == nxt) ) 1049 GNUNET_SCHEDULER_shutdown (); 1050 } 1051 1052 1053 /** 1054 * Function to continue processing the next command. 1055 * 1056 * @param cls must be a `char *const*` with the array of 1057 * command-line arguments to process next 1058 */ 1059 static void 1060 work (void *cls); 1061 1062 1063 /** 1064 * Function to schedule job to process the next command. 1065 * 1066 * @param args the array of command-line arguments to process next 1067 */ 1068 static void 1069 next (char *const *args) 1070 { 1071 GNUNET_assert (NULL == nxt); 1072 if (NULL == args[0]) 1073 { 1074 test_shutdown (); 1075 return; 1076 } 1077 nxt = GNUNET_SCHEDULER_add_now (&work, 1078 (void *) args); 1079 } 1080 1081 1082 /** 1083 * Add an operation to the #out JSON array for processing later. 1084 * 1085 * @param op_name name of the operation 1086 * @param op_value values for the operation (consumed) 1087 */ 1088 static void 1089 output_operation (const char *op_name, 1090 json_t *op_value) 1091 { 1092 json_t *action; 1093 1094 GNUNET_break (NULL != op_value); 1095 if (NULL == out) 1096 { 1097 out = json_array (); 1098 GNUNET_assert (NULL != out); 1099 } 1100 action = GNUNET_JSON_PACK ( 1101 GNUNET_JSON_pack_string ("operation", 1102 op_name), 1103 GNUNET_JSON_pack_object_steal ("arguments", 1104 op_value)); 1105 GNUNET_assert (0 == 1106 json_array_append_new (out, 1107 action)); 1108 } 1109 1110 1111 /** 1112 * Information about a subroutine for an upload. 1113 */ 1114 struct UploadHandler 1115 { 1116 /** 1117 * Key to trigger this subroutine. 1118 */ 1119 const char *key; 1120 1121 /** 1122 * Function implementing an upload. 1123 * 1124 * @param exchange_url URL of the exchange 1125 * @param idx index of the operation we are performing 1126 * @param value arguments to drive the upload. 1127 */ 1128 void (*cb)(const char *exchange_url, 1129 size_t idx, 1130 const json_t *value); 1131 1132 }; 1133 1134 1135 /** 1136 * Load the offline key (if not yet done). Triggers shutdown on failure. 1137 * 1138 * @param do_create #GNUNET_YES if the key may be created 1139 * @return #GNUNET_OK on success 1140 */ 1141 static enum GNUNET_GenericReturnValue 1142 load_offline_key (int do_create) 1143 { 1144 static bool done; 1145 int ret; 1146 char *fn; 1147 1148 if (done) 1149 return GNUNET_OK; 1150 if (GNUNET_OK != 1151 GNUNET_CONFIGURATION_get_value_filename (kcfg, 1152 "exchange-offline", 1153 "MASTER_PRIV_FILE", 1154 &fn)) 1155 { 1156 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, 1157 "exchange-offline", 1158 "MASTER_PRIV_FILE"); 1159 test_shutdown (); 1160 return GNUNET_SYSERR; 1161 } 1162 if (GNUNET_YES != 1163 GNUNET_DISK_file_test (fn)) 1164 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1165 "Exchange master private key `%s' does not exist yet, creating it!\n", 1166 fn); 1167 ret = GNUNET_CRYPTO_eddsa_key_from_file (fn, 1168 do_create, 1169 &master_priv.eddsa_priv); 1170 if (GNUNET_SYSERR == ret) 1171 { 1172 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 1173 "Failed to initialize master key from file `%s': %s\n", 1174 fn, 1175 "could not create file"); 1176 GNUNET_free (fn); 1177 test_shutdown (); 1178 return GNUNET_SYSERR; 1179 } 1180 GNUNET_free (fn); 1181 GNUNET_CRYPTO_eddsa_key_get_public (&master_priv.eddsa_priv, 1182 &master_pub.eddsa_pub); 1183 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1184 "Using master public key %s\n", 1185 TALER_B2S (&master_pub)); 1186 done = true; 1187 return GNUNET_OK; 1188 } 1189 1190 1191 /** 1192 * Function called with information about the post revocation operation result. 1193 * 1194 * @param drr the revocation request 1195 * @param dr response data 1196 */ 1197 static void 1198 denom_revocation_cb ( 1199 struct DenomRevocationRequest *drr, 1200 const struct TALER_EXCHANGE_PostManagementDenominationsRevokeResponse *dr) 1201 { 1202 const struct TALER_EXCHANGE_HttpResponse *hr = &dr->hr; 1203 1204 if (MHD_HTTP_NO_CONTENT != hr->http_status) 1205 { 1206 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 1207 "Upload failed for command %u with status %u: %s (%s)\n", 1208 (unsigned int) drr->idx, 1209 hr->http_status, 1210 hr->hint, 1211 TALER_JSON_get_error_hint (hr->reply)); 1212 global_ret = EXIT_FAILURE; 1213 } 1214 GNUNET_CONTAINER_DLL_remove (drr_head, 1215 drr_tail, 1216 drr); 1217 GNUNET_free (drr); 1218 test_shutdown (); 1219 } 1220 1221 1222 /** 1223 * Upload denomination revocation request data. 1224 * 1225 * @param exchange_url base URL of the exchange 1226 * @param idx index of the operation we are performing (for logging) 1227 * @param value arguments for denomination revocation 1228 */ 1229 static void 1230 upload_denom_revocation (const char *exchange_url, 1231 size_t idx, 1232 const json_t *value) 1233 { 1234 struct TALER_MasterSignatureP master_sig; 1235 struct TALER_DenominationHashP h_denom_pub; 1236 struct DenomRevocationRequest *drr; 1237 const char *err_name; 1238 unsigned int err_line; 1239 struct GNUNET_JSON_Specification spec[] = { 1240 GNUNET_JSON_spec_fixed_auto ("h_denom_pub", 1241 &h_denom_pub), 1242 GNUNET_JSON_spec_fixed_auto ("master_sig", 1243 &master_sig), 1244 GNUNET_JSON_spec_end () 1245 }; 1246 1247 if (GNUNET_OK != 1248 GNUNET_JSON_parse (value, 1249 spec, 1250 &err_name, 1251 &err_line)) 1252 { 1253 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 1254 "Invalid input for denomination revocation: %s#%u at %u (skipping)\n", 1255 err_name, 1256 err_line, 1257 (unsigned int) idx); 1258 json_dumpf (value, 1259 stderr, 1260 JSON_INDENT (2)); 1261 global_ret = EXIT_FAILURE; 1262 GNUNET_SCHEDULER_shutdown (); 1263 return; 1264 } 1265 drr = GNUNET_new (struct DenomRevocationRequest); 1266 drr->idx = idx; 1267 drr->h = 1268 TALER_EXCHANGE_post_management_denominations_revoke_create (ctx, 1269 exchange_url, 1270 &h_denom_pub, 1271 &master_sig); 1272 TALER_EXCHANGE_post_management_denominations_revoke_start (drr->h, 1273 & 1274 denom_revocation_cb, 1275 drr); 1276 GNUNET_CONTAINER_DLL_insert (drr_head, 1277 drr_tail, 1278 drr); 1279 } 1280 1281 1282 /** 1283 * Function called with information about the post revocation operation result. 1284 * 1285 * @param srr the revocation request 1286 * @param sr response data 1287 */ 1288 static void 1289 signkey_revocation_cb ( 1290 struct SignkeyRevocationRequest *srr, 1291 const struct TALER_EXCHANGE_PostManagementSignkeysRevokeResponse *sr) 1292 { 1293 const struct TALER_EXCHANGE_HttpResponse *hr = &sr->hr; 1294 1295 if (MHD_HTTP_NO_CONTENT != hr->http_status) 1296 { 1297 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 1298 "Upload failed for command %u with status %u: %s (%s)\n", 1299 (unsigned int) srr->idx, 1300 hr->http_status, 1301 hr->hint, 1302 TALER_JSON_get_error_hint (hr->reply)); 1303 global_ret = EXIT_FAILURE; 1304 } 1305 GNUNET_CONTAINER_DLL_remove (srr_head, 1306 srr_tail, 1307 srr); 1308 GNUNET_free (srr); 1309 test_shutdown (); 1310 } 1311 1312 1313 /** 1314 * Upload signkey revocation request data. 1315 * 1316 * @param exchange_url base URL of the exchange 1317 * @param idx index of the operation we are performing (for logging) 1318 * @param value arguments for denomination revocation 1319 */ 1320 static void 1321 upload_signkey_revocation (const char *exchange_url, 1322 size_t idx, 1323 const json_t *value) 1324 { 1325 struct TALER_MasterSignatureP master_sig; 1326 struct TALER_ExchangePublicKeyP exchange_pub; 1327 struct SignkeyRevocationRequest *srr; 1328 const char *err_name; 1329 unsigned int err_line; 1330 struct GNUNET_JSON_Specification spec[] = { 1331 GNUNET_JSON_spec_fixed_auto ("exchange_pub", 1332 &exchange_pub), 1333 GNUNET_JSON_spec_fixed_auto ("master_sig", 1334 &master_sig), 1335 GNUNET_JSON_spec_end () 1336 }; 1337 1338 if (GNUNET_OK != 1339 GNUNET_JSON_parse (value, 1340 spec, 1341 &err_name, 1342 &err_line)) 1343 { 1344 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 1345 "Invalid input for signkey revocation: %s#%u at %u (skipping)\n", 1346 err_name, 1347 err_line, 1348 (unsigned int) idx); 1349 json_dumpf (value, 1350 stderr, 1351 JSON_INDENT (2)); 1352 global_ret = EXIT_FAILURE; 1353 GNUNET_SCHEDULER_shutdown (); 1354 return; 1355 } 1356 srr = GNUNET_new (struct SignkeyRevocationRequest); 1357 srr->idx = idx; 1358 srr->h = 1359 TALER_EXCHANGE_post_management_signkeys_revoke_create (ctx, 1360 exchange_url, 1361 &exchange_pub, 1362 &master_sig); 1363 TALER_EXCHANGE_post_management_signkeys_revoke_start (srr->h, 1364 &signkey_revocation_cb, 1365 srr); 1366 GNUNET_CONTAINER_DLL_insert (srr_head, 1367 srr_tail, 1368 srr); 1369 } 1370 1371 1372 /** 1373 * Function called with information about the post auditor add operation result. 1374 * 1375 * @param aar the auditor add request 1376 * @param mer response data 1377 */ 1378 static void 1379 auditor_add_cb ( 1380 struct AuditorAddRequest *aar, 1381 const struct TALER_EXCHANGE_PostManagementAuditorsResponse *mer) 1382 { 1383 const struct TALER_EXCHANGE_HttpResponse *hr = &mer->hr; 1384 1385 if (MHD_HTTP_NO_CONTENT != hr->http_status) 1386 { 1387 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 1388 "Upload failed for command %u with status %u: %s (%s)\n", 1389 (unsigned int) aar->idx, 1390 hr->http_status, 1391 TALER_ErrorCode_get_hint (hr->ec), 1392 hr->hint); 1393 global_ret = EXIT_FAILURE; 1394 } 1395 GNUNET_CONTAINER_DLL_remove (aar_head, 1396 aar_tail, 1397 aar); 1398 GNUNET_free (aar); 1399 test_shutdown (); 1400 } 1401 1402 1403 /** 1404 * Upload auditor add data. 1405 * 1406 * @param exchange_url base URL of the exchange 1407 * @param idx index of the operation we are performing (for logging) 1408 * @param value arguments for denomination revocation 1409 */ 1410 static void 1411 upload_auditor_add (const char *exchange_url, 1412 size_t idx, 1413 const json_t *value) 1414 { 1415 struct TALER_MasterSignatureP master_sig; 1416 const char *auditor_url; 1417 const char *auditor_name; 1418 struct GNUNET_TIME_Timestamp start_time; 1419 struct TALER_AuditorPublicKeyP auditor_pub; 1420 struct AuditorAddRequest *aar; 1421 const char *err_name; 1422 unsigned int err_line; 1423 struct GNUNET_JSON_Specification spec[] = { 1424 TALER_JSON_spec_web_url ("auditor_url", 1425 &auditor_url), 1426 GNUNET_JSON_spec_string ("auditor_name", 1427 &auditor_name), 1428 GNUNET_JSON_spec_timestamp ("validity_start", 1429 &start_time), 1430 GNUNET_JSON_spec_fixed_auto ("auditor_pub", 1431 &auditor_pub), 1432 GNUNET_JSON_spec_fixed_auto ("master_sig", 1433 &master_sig), 1434 GNUNET_JSON_spec_end () 1435 }; 1436 1437 if (GNUNET_OK != 1438 GNUNET_JSON_parse (value, 1439 spec, 1440 &err_name, 1441 &err_line)) 1442 { 1443 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 1444 "Invalid input for adding auditor: %s#%u at %u (skipping)\n", 1445 err_name, 1446 err_line, 1447 (unsigned int) idx); 1448 json_dumpf (value, 1449 stderr, 1450 JSON_INDENT (2)); 1451 global_ret = EXIT_FAILURE; 1452 GNUNET_SCHEDULER_shutdown (); 1453 return; 1454 } 1455 aar = GNUNET_new (struct AuditorAddRequest); 1456 aar->idx = idx; 1457 aar->h = 1458 TALER_EXCHANGE_post_management_auditors_create (ctx, 1459 exchange_url, 1460 &auditor_pub, 1461 auditor_url, 1462 auditor_name, 1463 start_time, 1464 &master_sig); 1465 TALER_EXCHANGE_post_management_auditors_start (aar->h, 1466 &auditor_add_cb, 1467 aar); 1468 GNUNET_CONTAINER_DLL_insert (aar_head, 1469 aar_tail, 1470 aar); 1471 } 1472 1473 1474 /** 1475 * Function called with information about the post auditor del operation result. 1476 * 1477 * @param adr auditor delete request 1478 * @param mdr response data 1479 */ 1480 static void 1481 auditor_del_cb ( 1482 struct AuditorDelRequest *adr, 1483 const struct TALER_EXCHANGE_PostManagementAuditorsDisableResponse *mdr) 1484 { 1485 const struct TALER_EXCHANGE_HttpResponse *hr = &mdr->hr; 1486 1487 if (MHD_HTTP_NO_CONTENT != hr->http_status) 1488 { 1489 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 1490 "Upload failed for command %u with status %u: %s (%s)\n", 1491 (unsigned int) adr->idx, 1492 hr->http_status, 1493 TALER_ErrorCode_get_hint (hr->ec), 1494 hr->hint); 1495 global_ret = EXIT_FAILURE; 1496 } 1497 GNUNET_CONTAINER_DLL_remove (adr_head, 1498 adr_tail, 1499 adr); 1500 GNUNET_free (adr); 1501 test_shutdown (); 1502 } 1503 1504 1505 /** 1506 * Upload auditor del data. 1507 * 1508 * @param exchange_url base URL of the exchange 1509 * @param idx index of the operation we are performing (for logging) 1510 * @param value arguments for denomination revocation 1511 */ 1512 static void 1513 upload_auditor_del (const char *exchange_url, 1514 size_t idx, 1515 const json_t *value) 1516 { 1517 struct TALER_AuditorPublicKeyP auditor_pub; 1518 struct TALER_MasterSignatureP master_sig; 1519 struct GNUNET_TIME_Timestamp end_time; 1520 struct AuditorDelRequest *adr; 1521 const char *err_name; 1522 unsigned int err_line; 1523 struct GNUNET_JSON_Specification spec[] = { 1524 GNUNET_JSON_spec_fixed_auto ("auditor_pub", 1525 &auditor_pub), 1526 GNUNET_JSON_spec_timestamp ("validity_end", 1527 &end_time), 1528 GNUNET_JSON_spec_fixed_auto ("master_sig", 1529 &master_sig), 1530 GNUNET_JSON_spec_end () 1531 }; 1532 1533 if (GNUNET_OK != 1534 GNUNET_JSON_parse (value, 1535 spec, 1536 &err_name, 1537 &err_line)) 1538 { 1539 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 1540 "Invalid input to disable auditor: %s#%u at %u (skipping)\n", 1541 err_name, 1542 err_line, 1543 (unsigned int) idx); 1544 json_dumpf (value, 1545 stderr, 1546 JSON_INDENT (2)); 1547 global_ret = EXIT_FAILURE; 1548 GNUNET_SCHEDULER_shutdown (); 1549 return; 1550 } 1551 adr = GNUNET_new (struct AuditorDelRequest); 1552 adr->idx = idx; 1553 adr->h = 1554 TALER_EXCHANGE_post_management_auditors_disable_create (ctx, 1555 exchange_url, 1556 &auditor_pub, 1557 end_time, 1558 &master_sig); 1559 TALER_EXCHANGE_post_management_auditors_disable_start (adr->h, 1560 &auditor_del_cb, 1561 adr); 1562 GNUNET_CONTAINER_DLL_insert (adr_head, 1563 adr_tail, 1564 adr); 1565 } 1566 1567 1568 /** 1569 * Function called with information about the post wire add operation result. 1570 * 1571 * @param war the wire add request 1572 * @param wer response data 1573 */ 1574 static void 1575 wire_add_cb ( 1576 struct WireAddRequest *war, 1577 const struct TALER_EXCHANGE_PostManagementWireResponse *wer) 1578 { 1579 const struct TALER_EXCHANGE_HttpResponse *hr = &wer->hr; 1580 1581 if (MHD_HTTP_NO_CONTENT != hr->http_status) 1582 { 1583 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 1584 "Upload failed for command %u with status %u: %s (%s)\n", 1585 (unsigned int) war->idx, 1586 hr->http_status, 1587 TALER_ErrorCode_get_hint (hr->ec), 1588 hr->hint); 1589 global_ret = EXIT_FAILURE; 1590 } 1591 GNUNET_CONTAINER_DLL_remove (war_head, 1592 war_tail, 1593 war); 1594 GNUNET_free (war); 1595 test_shutdown (); 1596 } 1597 1598 1599 /** 1600 * Upload wire add data. 1601 * 1602 * @param exchange_url base URL of the exchange 1603 * @param idx index of the operation we are performing (for logging) 1604 * @param value arguments for denomination revocation 1605 */ 1606 static void 1607 upload_wire_add (const char *exchange_url, 1608 size_t idx, 1609 const json_t *value) 1610 { 1611 struct TALER_MasterSignatureP master_sig_add; 1612 struct TALER_MasterSignatureP master_sig_wire; 1613 struct TALER_FullPayto payto_uri; 1614 struct GNUNET_TIME_Timestamp start_time; 1615 struct WireAddRequest *war; 1616 const char *err_name; 1617 const char *conversion_url = NULL; 1618 const char *open_banking_gateway = NULL; 1619 const char *wire_transfer_gateway = NULL; 1620 const char *bank_label = NULL; 1621 int64_t priority = 0; 1622 const json_t *debit_restrictions; 1623 const json_t *credit_restrictions; 1624 unsigned int err_line; 1625 struct GNUNET_JSON_Specification spec[] = { 1626 TALER_JSON_spec_full_payto_uri ("payto_uri", 1627 &payto_uri), 1628 GNUNET_JSON_spec_mark_optional ( 1629 TALER_JSON_spec_web_url ("conversion_url", 1630 &conversion_url), 1631 NULL), 1632 GNUNET_JSON_spec_mark_optional ( 1633 TALER_JSON_spec_web_url ("open_banking_gateway", 1634 &open_banking_gateway), 1635 NULL), 1636 GNUNET_JSON_spec_mark_optional ( 1637 TALER_JSON_spec_web_url ("wire_transfer_gateway", 1638 &wire_transfer_gateway), 1639 NULL), 1640 GNUNET_JSON_spec_mark_optional ( 1641 GNUNET_JSON_spec_string ("bank_label", 1642 &bank_label), 1643 NULL), 1644 GNUNET_JSON_spec_int64 ("priority", 1645 &priority), 1646 GNUNET_JSON_spec_array_const ("debit_restrictions", 1647 &debit_restrictions), 1648 GNUNET_JSON_spec_array_const ("credit_restrictions", 1649 &credit_restrictions), 1650 GNUNET_JSON_spec_timestamp ("validity_start", 1651 &start_time), 1652 GNUNET_JSON_spec_fixed_auto ("master_sig_add", 1653 &master_sig_add), 1654 GNUNET_JSON_spec_fixed_auto ("master_sig_wire", 1655 &master_sig_wire), 1656 GNUNET_JSON_spec_end () 1657 }; 1658 1659 if (GNUNET_OK != 1660 GNUNET_JSON_parse (value, 1661 spec, 1662 &err_name, 1663 &err_line)) 1664 { 1665 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 1666 "Invalid input for adding wire account: %s#%u at %u (skipping)\n", 1667 err_name, 1668 err_line, 1669 (unsigned int) idx); 1670 json_dumpf (value, 1671 stderr, 1672 JSON_INDENT (2)); 1673 global_ret = EXIT_FAILURE; 1674 GNUNET_SCHEDULER_shutdown (); 1675 return; 1676 } 1677 { 1678 char *wire_method; 1679 1680 wire_method = TALER_payto_get_method (payto_uri.full_payto); 1681 if (NULL == wire_method) 1682 { 1683 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 1684 "payto:// URI `%s' is malformed\n", 1685 payto_uri.full_payto); 1686 global_ret = EXIT_FAILURE; 1687 GNUNET_SCHEDULER_shutdown (); 1688 return; 1689 } 1690 GNUNET_free (wire_method); 1691 } 1692 { 1693 char *msg = TALER_payto_validate (payto_uri); 1694 1695 if (NULL != msg) 1696 { 1697 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 1698 "payto URI is malformed: %s\n", 1699 msg); 1700 GNUNET_free (msg); 1701 GNUNET_SCHEDULER_shutdown (); 1702 global_ret = EXIT_INVALIDARGUMENT; 1703 return; 1704 } 1705 } 1706 war = GNUNET_new (struct WireAddRequest); 1707 war->idx = idx; 1708 war->h = 1709 TALER_EXCHANGE_post_management_wire_create (ctx, 1710 exchange_url, 1711 payto_uri, 1712 start_time, 1713 &master_sig_add, 1714 &master_sig_wire); 1715 TALER_EXCHANGE_post_management_wire_set_options ( 1716 war->h, 1717 TALER_EXCHANGE_post_management_wire_option_bank_label (bank_label), 1718 TALER_EXCHANGE_post_management_wire_option_conversion_url (conversion_url), 1719 TALER_EXCHANGE_post_management_wire_option_open_banking_gateway ( 1720 open_banking_gateway), 1721 TALER_EXCHANGE_post_management_wire_option_wire_transfer_gateway ( 1722 wire_transfer_gateway), 1723 TALER_EXCHANGE_post_management_wire_option_debit_restrictions ( 1724 debit_restrictions), 1725 TALER_EXCHANGE_post_management_wire_option_credit_restrictions ( 1726 credit_restrictions), 1727 TALER_EXCHANGE_post_management_wire_option_priority (priority)); 1728 TALER_EXCHANGE_post_management_wire_start (war->h, 1729 &wire_add_cb, 1730 war); 1731 GNUNET_CONTAINER_DLL_insert (war_head, 1732 war_tail, 1733 war); 1734 } 1735 1736 1737 /** 1738 * Function called with information about the post wire del operation result. 1739 * 1740 * @param wdr request to delete wire account 1741 * @param wdres response data 1742 */ 1743 static void 1744 wire_del_cb ( 1745 struct WireDelRequest *wdr, 1746 const struct TALER_EXCHANGE_PostManagementWireDisableResponse *wdres) 1747 { 1748 const struct TALER_EXCHANGE_HttpResponse *hr = &wdres->hr; 1749 1750 if (MHD_HTTP_NO_CONTENT != hr->http_status) 1751 { 1752 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 1753 "Upload failed for command %u with status %u: %s (%s)\n", 1754 (unsigned int) wdr->idx, 1755 hr->http_status, 1756 TALER_ErrorCode_get_hint (hr->ec), 1757 hr->hint); 1758 global_ret = EXIT_FAILURE; 1759 } 1760 GNUNET_CONTAINER_DLL_remove (wdr_head, 1761 wdr_tail, 1762 wdr); 1763 GNUNET_free (wdr); 1764 test_shutdown (); 1765 } 1766 1767 1768 /** 1769 * Upload wire del data. 1770 * 1771 * @param exchange_url base URL of the exchange 1772 * @param idx index of the operation we are performing (for logging) 1773 * @param value arguments for denomination revocation 1774 */ 1775 static void 1776 upload_wire_del (const char *exchange_url, 1777 size_t idx, 1778 const json_t *value) 1779 { 1780 struct TALER_MasterSignatureP master_sig; 1781 struct TALER_FullPayto payto_uri; 1782 struct GNUNET_TIME_Timestamp end_time; 1783 struct WireDelRequest *wdr; 1784 const char *err_name; 1785 unsigned int err_line; 1786 struct GNUNET_JSON_Specification spec[] = { 1787 TALER_JSON_spec_full_payto_uri ("payto_uri", 1788 &payto_uri), 1789 GNUNET_JSON_spec_timestamp ("validity_end", 1790 &end_time), 1791 GNUNET_JSON_spec_fixed_auto ("master_sig", 1792 &master_sig), 1793 GNUNET_JSON_spec_end () 1794 }; 1795 1796 if (GNUNET_OK != 1797 GNUNET_JSON_parse (value, 1798 spec, 1799 &err_name, 1800 &err_line)) 1801 { 1802 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 1803 "Invalid input to disable wire account: %s#%u at %u (skipping)\n", 1804 err_name, 1805 err_line, 1806 (unsigned int) idx); 1807 json_dumpf (value, 1808 stderr, 1809 JSON_INDENT (2)); 1810 global_ret = EXIT_FAILURE; 1811 GNUNET_SCHEDULER_shutdown (); 1812 return; 1813 } 1814 wdr = GNUNET_new (struct WireDelRequest); 1815 wdr->idx = idx; 1816 wdr->h = 1817 TALER_EXCHANGE_post_management_wire_disable_create (ctx, 1818 exchange_url, 1819 payto_uri, 1820 end_time, 1821 &master_sig); 1822 TALER_EXCHANGE_post_management_wire_disable_start (wdr->h, 1823 &wire_del_cb, 1824 wdr); 1825 GNUNET_CONTAINER_DLL_insert (wdr_head, 1826 wdr_tail, 1827 wdr); 1828 } 1829 1830 1831 /** 1832 * Function called with information about the post wire fee operation result. 1833 * 1834 * @param wfr the wire fee request 1835 * @param swr response data 1836 */ 1837 static void 1838 wire_fee_cb ( 1839 struct WireFeeRequest *wfr, 1840 const struct TALER_EXCHANGE_PostManagementWireFeesResponse *swr) 1841 { 1842 const struct TALER_EXCHANGE_HttpResponse *hr = &swr->hr; 1843 1844 if (MHD_HTTP_NO_CONTENT != hr->http_status) 1845 { 1846 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 1847 "Upload failed for command %u with status %u: %s (%s)\n", 1848 (unsigned int) wfr->idx, 1849 hr->http_status, 1850 TALER_ErrorCode_get_hint (hr->ec), 1851 hr->hint); 1852 global_ret = EXIT_FAILURE; 1853 } 1854 GNUNET_CONTAINER_DLL_remove (wfr_head, 1855 wfr_tail, 1856 wfr); 1857 GNUNET_free (wfr); 1858 test_shutdown (); 1859 } 1860 1861 1862 /** 1863 * Upload wire fee. 1864 * 1865 * @param exchange_url base URL of the exchange 1866 * @param idx index of the operation we are performing (for logging) 1867 * @param value arguments for denomination revocation 1868 */ 1869 static void 1870 upload_wire_fee (const char *exchange_url, 1871 size_t idx, 1872 const json_t *value) 1873 { 1874 struct TALER_MasterSignatureP master_sig; 1875 const char *wire_method; 1876 struct WireFeeRequest *wfr; 1877 const char *err_name; 1878 unsigned int err_line; 1879 struct TALER_WireFeeSet fees; 1880 struct GNUNET_TIME_Timestamp start_time; 1881 struct GNUNET_TIME_Timestamp end_time; 1882 struct GNUNET_JSON_Specification spec[] = { 1883 GNUNET_JSON_spec_string ("wire_method", 1884 &wire_method), 1885 TALER_JSON_spec_amount ("wire_fee", 1886 currency, 1887 &fees.wire), 1888 TALER_JSON_spec_amount ("closing_fee", 1889 currency, 1890 &fees.closing), 1891 GNUNET_JSON_spec_timestamp ("start_time", 1892 &start_time), 1893 GNUNET_JSON_spec_timestamp ("end_time", 1894 &end_time), 1895 GNUNET_JSON_spec_fixed_auto ("master_sig", 1896 &master_sig), 1897 GNUNET_JSON_spec_end () 1898 }; 1899 1900 if (GNUNET_OK != 1901 GNUNET_JSON_parse (value, 1902 spec, 1903 &err_name, 1904 &err_line)) 1905 { 1906 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 1907 "Invalid input to set wire fee: %s#%u at %u (skipping)\n", 1908 err_name, 1909 err_line, 1910 (unsigned int) idx); 1911 json_dumpf (value, 1912 stderr, 1913 JSON_INDENT (2)); 1914 global_ret = EXIT_FAILURE; 1915 GNUNET_SCHEDULER_shutdown (); 1916 return; 1917 } 1918 wfr = GNUNET_new (struct WireFeeRequest); 1919 wfr->idx = idx; 1920 wfr->h = 1921 TALER_EXCHANGE_post_management_wire_fees_create (ctx, 1922 exchange_url, 1923 wire_method, 1924 start_time, 1925 end_time, 1926 &fees, 1927 &master_sig); 1928 TALER_EXCHANGE_post_management_wire_fees_start (wfr->h, 1929 &wire_fee_cb, 1930 wfr); 1931 GNUNET_CONTAINER_DLL_insert (wfr_head, 1932 wfr_tail, 1933 wfr); 1934 } 1935 1936 1937 /** 1938 * Function called with information about the post global fee operation result. 1939 * 1940 * @param gfr the global fee request 1941 * @param gr response data 1942 */ 1943 static void 1944 global_fee_cb ( 1945 struct GlobalFeeRequest *gfr, 1946 const struct TALER_EXCHANGE_PostManagementGlobalFeesResponse *gr) 1947 { 1948 const struct TALER_EXCHANGE_HttpResponse *hr = &gr->hr; 1949 1950 if (MHD_HTTP_NO_CONTENT != hr->http_status) 1951 { 1952 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 1953 "Upload failed for command %u with status %u: %s (%s)\n", 1954 (unsigned int) gfr->idx, 1955 hr->http_status, 1956 TALER_ErrorCode_get_hint (hr->ec), 1957 hr->hint); 1958 global_ret = EXIT_FAILURE; 1959 } 1960 GNUNET_CONTAINER_DLL_remove (gfr_head, 1961 gfr_tail, 1962 gfr); 1963 GNUNET_free (gfr); 1964 test_shutdown (); 1965 } 1966 1967 1968 /** 1969 * Upload global fee. 1970 * 1971 * @param exchange_url base URL of the exchange 1972 * @param idx index of the operation we are performing (for logging) 1973 * @param value arguments for denomination revocation 1974 */ 1975 static void 1976 upload_global_fee (const char *exchange_url, 1977 size_t idx, 1978 const json_t *value) 1979 { 1980 struct TALER_MasterSignatureP master_sig; 1981 struct GlobalFeeRequest *gfr; 1982 const char *err_name; 1983 unsigned int err_line; 1984 struct TALER_GlobalFeeSet fees; 1985 struct GNUNET_TIME_Timestamp start_time; 1986 struct GNUNET_TIME_Timestamp end_time; 1987 struct GNUNET_TIME_Relative purse_timeout; 1988 struct GNUNET_TIME_Relative history_expiration; 1989 uint32_t purse_account_limit; 1990 struct GNUNET_JSON_Specification spec[] = { 1991 TALER_JSON_spec_amount ("history_fee", 1992 currency, 1993 &fees.history), 1994 TALER_JSON_spec_amount ("account_fee", 1995 currency, 1996 &fees.account), 1997 TALER_JSON_spec_amount ("purse_fee", 1998 currency, 1999 &fees.purse), 2000 GNUNET_JSON_spec_relative_time ("purse_timeout", 2001 &purse_timeout), 2002 GNUNET_JSON_spec_relative_time ("history_expiration", 2003 &history_expiration), 2004 GNUNET_JSON_spec_uint32 ("purse_account_limit", 2005 &purse_account_limit), 2006 GNUNET_JSON_spec_timestamp ("start_time", 2007 &start_time), 2008 GNUNET_JSON_spec_timestamp ("end_time", 2009 &end_time), 2010 GNUNET_JSON_spec_fixed_auto ("master_sig", 2011 &master_sig), 2012 GNUNET_JSON_spec_end () 2013 }; 2014 2015 if (GNUNET_OK != 2016 GNUNET_JSON_parse (value, 2017 spec, 2018 &err_name, 2019 &err_line)) 2020 { 2021 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 2022 "Invalid input to set wire fee: %s#%u at %u (skipping)\n", 2023 err_name, 2024 err_line, 2025 (unsigned int) idx); 2026 json_dumpf (value, 2027 stderr, 2028 JSON_INDENT (2)); 2029 global_ret = EXIT_FAILURE; 2030 GNUNET_SCHEDULER_shutdown (); 2031 return; 2032 } 2033 gfr = GNUNET_new (struct GlobalFeeRequest); 2034 gfr->idx = idx; 2035 gfr->h = 2036 TALER_EXCHANGE_post_management_global_fees_create (ctx, 2037 exchange_url, 2038 start_time, 2039 end_time, 2040 &fees, 2041 purse_timeout, 2042 history_expiration, 2043 purse_account_limit, 2044 &master_sig); 2045 TALER_EXCHANGE_post_management_global_fees_start (gfr->h, 2046 &global_fee_cb, 2047 gfr); 2048 GNUNET_CONTAINER_DLL_insert (gfr_head, 2049 gfr_tail, 2050 gfr); 2051 } 2052 2053 2054 /** 2055 * Function called with information about the drain profits operation. 2056 * 2057 * @param dpr the drain profits request 2058 * @param mdr response data 2059 */ 2060 static void 2061 drain_profits_cb ( 2062 struct DrainProfitsRequest *dpr, 2063 const struct TALER_EXCHANGE_PostManagementDrainResponse *mdr) 2064 { 2065 const struct TALER_EXCHANGE_HttpResponse *hr = &mdr->hr; 2066 2067 if (MHD_HTTP_NO_CONTENT != hr->http_status) 2068 { 2069 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 2070 "Upload failed for command %u with status %u: %s (%s)\n", 2071 (unsigned int) dpr->idx, 2072 hr->http_status, 2073 TALER_ErrorCode_get_hint (hr->ec), 2074 hr->hint); 2075 global_ret = EXIT_FAILURE; 2076 } 2077 GNUNET_CONTAINER_DLL_remove (dpr_head, 2078 dpr_tail, 2079 dpr); 2080 GNUNET_free (dpr); 2081 test_shutdown (); 2082 } 2083 2084 2085 /** 2086 * Upload drain profit action. 2087 * 2088 * @param exchange_url base URL of the exchange 2089 * @param idx index of the operation we are performing (for logging) 2090 * @param value arguments for drain profits 2091 */ 2092 static void 2093 upload_drain (const char *exchange_url, 2094 size_t idx, 2095 const json_t *value) 2096 { 2097 struct TALER_WireTransferIdentifierRawP wtid; 2098 struct TALER_MasterSignatureP master_sig; 2099 const char *err_name; 2100 unsigned int err_line; 2101 struct TALER_Amount amount; 2102 struct GNUNET_TIME_Timestamp date; 2103 struct TALER_FullPayto payto_uri; 2104 const char *account_section; 2105 struct DrainProfitsRequest *dpr; 2106 struct GNUNET_JSON_Specification spec[] = { 2107 GNUNET_JSON_spec_fixed_auto ("wtid", 2108 &wtid), 2109 TALER_JSON_spec_amount ("amount", 2110 currency, 2111 &amount), 2112 GNUNET_JSON_spec_timestamp ("date", 2113 &date), 2114 GNUNET_JSON_spec_string ("account_section", 2115 &account_section), 2116 TALER_JSON_spec_full_payto_uri ("payto_uri", 2117 &payto_uri), 2118 GNUNET_JSON_spec_fixed_auto ("master_sig", 2119 &master_sig), 2120 GNUNET_JSON_spec_end () 2121 }; 2122 2123 if (GNUNET_OK != 2124 GNUNET_JSON_parse (value, 2125 spec, 2126 &err_name, 2127 &err_line)) 2128 { 2129 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 2130 "Invalid input to drain profits: %s#%u at %u (skipping)\n", 2131 err_name, 2132 err_line, 2133 (unsigned int) idx); 2134 json_dumpf (value, 2135 stderr, 2136 JSON_INDENT (2)); 2137 global_ret = EXIT_FAILURE; 2138 GNUNET_SCHEDULER_shutdown (); 2139 return; 2140 } 2141 dpr = GNUNET_new (struct DrainProfitsRequest); 2142 dpr->idx = idx; 2143 dpr->h = 2144 TALER_EXCHANGE_post_management_drain_create (ctx, 2145 exchange_url, 2146 &wtid, 2147 &amount, 2148 date, 2149 account_section, 2150 payto_uri, 2151 &master_sig); 2152 TALER_EXCHANGE_post_management_drain_start (dpr->h, 2153 &drain_profits_cb, 2154 dpr); 2155 GNUNET_CONTAINER_DLL_insert (dpr_head, 2156 dpr_tail, 2157 dpr); 2158 } 2159 2160 2161 /** 2162 * Function called with information about the post upload keys operation result. 2163 * 2164 * @param ukr the upload keys request 2165 * @param mr response data 2166 */ 2167 static void 2168 keys_cb ( 2169 struct UploadKeysRequest *ukr, 2170 const struct TALER_EXCHANGE_PostManagementKeysResponse *mr) 2171 { 2172 const struct TALER_EXCHANGE_HttpResponse *hr = &mr->hr; 2173 2174 if (MHD_HTTP_NO_CONTENT != hr->http_status) 2175 { 2176 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 2177 "Upload failed for command %u with status %u: %s (%s)\n", 2178 (unsigned int) ukr->idx, 2179 hr->http_status, 2180 TALER_ErrorCode_get_hint (hr->ec), 2181 hr->hint); 2182 global_ret = EXIT_FAILURE; 2183 } 2184 GNUNET_CONTAINER_DLL_remove (ukr_head, 2185 ukr_tail, 2186 ukr); 2187 GNUNET_free (ukr); 2188 test_shutdown (); 2189 } 2190 2191 2192 /** 2193 * Upload (denomination and signing) key master signatures. 2194 * 2195 * @param exchange_url base URL of the exchange 2196 * @param idx index of the operation we are performing (for logging) 2197 * @param value arguments for POSTing keys 2198 */ 2199 static void 2200 upload_keys (const char *exchange_url, 2201 size_t idx, 2202 const json_t *value) 2203 { 2204 struct TALER_EXCHANGE_ManagementPostKeysData pkd; 2205 struct UploadKeysRequest *ukr; 2206 const char *err_name; 2207 unsigned int err_line; 2208 const json_t *denom_sigs; 2209 const json_t *signkey_sigs; 2210 struct GNUNET_JSON_Specification spec[] = { 2211 GNUNET_JSON_spec_array_const ("denom_sigs", 2212 &denom_sigs), 2213 GNUNET_JSON_spec_array_const ("signkey_sigs", 2214 &signkey_sigs), 2215 GNUNET_JSON_spec_end () 2216 }; 2217 bool ok = true; 2218 2219 if (GNUNET_OK != 2220 GNUNET_JSON_parse (value, 2221 spec, 2222 &err_name, 2223 &err_line)) 2224 { 2225 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 2226 "Invalid input to 'upload': %s#%u (skipping)\n", 2227 err_name, 2228 err_line); 2229 json_dumpf (value, 2230 stderr, 2231 JSON_INDENT (2)); 2232 global_ret = EXIT_FAILURE; 2233 GNUNET_SCHEDULER_shutdown (); 2234 return; 2235 } 2236 pkd.num_sign_sigs = json_array_size (signkey_sigs); 2237 pkd.num_denom_sigs = json_array_size (denom_sigs); 2238 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 2239 "Uploading %u denomination and %u signing key signatures\n", 2240 pkd.num_denom_sigs, 2241 pkd.num_sign_sigs); 2242 pkd.sign_sigs = GNUNET_new_array ( 2243 pkd.num_sign_sigs, 2244 struct TALER_EXCHANGE_SigningKeySignature); 2245 pkd.denom_sigs = GNUNET_new_array ( 2246 pkd.num_denom_sigs, 2247 struct TALER_EXCHANGE_DenominationKeySignature); 2248 for (unsigned int i = 0; i<pkd.num_sign_sigs; i++) 2249 { 2250 struct TALER_EXCHANGE_SigningKeySignature *ss = &pkd.sign_sigs[i]; 2251 json_t *val = json_array_get (signkey_sigs, 2252 i); 2253 struct GNUNET_JSON_Specification ispec[] = { 2254 GNUNET_JSON_spec_fixed_auto ("exchange_pub", 2255 &ss->exchange_pub), 2256 GNUNET_JSON_spec_fixed_auto ("master_sig", 2257 &ss->master_sig), 2258 GNUNET_JSON_spec_end () 2259 }; 2260 2261 if (GNUNET_OK != 2262 GNUNET_JSON_parse (val, 2263 ispec, 2264 &err_name, 2265 &err_line)) 2266 { 2267 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 2268 "Invalid input for signkey validity: %s#%u at %u (aborting)\n", 2269 err_name, 2270 err_line, 2271 i); 2272 json_dumpf (val, 2273 stderr, 2274 JSON_INDENT (2)); 2275 ok = false; 2276 } 2277 } 2278 for (unsigned int i = 0; i<pkd.num_denom_sigs; i++) 2279 { 2280 struct TALER_EXCHANGE_DenominationKeySignature *ds = &pkd.denom_sigs[i]; 2281 json_t *val = json_array_get (denom_sigs, 2282 i); 2283 struct GNUNET_JSON_Specification ispec[] = { 2284 GNUNET_JSON_spec_fixed_auto ("h_denom_pub", 2285 &ds->h_denom_pub), 2286 GNUNET_JSON_spec_fixed_auto ("master_sig", 2287 &ds->master_sig), 2288 GNUNET_JSON_spec_end () 2289 }; 2290 2291 if (GNUNET_OK != 2292 GNUNET_JSON_parse (val, 2293 ispec, 2294 &err_name, 2295 &err_line)) 2296 { 2297 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 2298 "Invalid input for denomination validity: %s#%u at %u (aborting)\n", 2299 err_name, 2300 err_line, 2301 i); 2302 json_dumpf (val, 2303 stderr, 2304 JSON_INDENT (2)); 2305 ok = false; 2306 } 2307 } 2308 2309 if (ok) 2310 { 2311 ukr = GNUNET_new (struct UploadKeysRequest); 2312 ukr->idx = idx; 2313 ukr->h = 2314 TALER_EXCHANGE_post_management_keys_create (ctx, 2315 exchange_url, 2316 &pkd); 2317 TALER_EXCHANGE_post_management_keys_start (ukr->h, 2318 &keys_cb, 2319 ukr); 2320 GNUNET_CONTAINER_DLL_insert (ukr_head, 2321 ukr_tail, 2322 ukr); 2323 } 2324 else 2325 { 2326 global_ret = EXIT_FAILURE; 2327 GNUNET_SCHEDULER_shutdown (); 2328 } 2329 GNUNET_free (pkd.sign_sigs); 2330 GNUNET_free (pkd.denom_sigs); 2331 } 2332 2333 2334 /** 2335 * Function called with information about the post upload extensions operation result. 2336 * 2337 * @param uer extension upload request 2338 * @param er response data 2339 */ 2340 static void 2341 extensions_cb ( 2342 struct UploadExtensionsRequest *uer, 2343 const struct TALER_EXCHANGE_PostManagementExtensionsResponse *er) 2344 { 2345 const struct TALER_EXCHANGE_HttpResponse *hr = &er->hr; 2346 2347 if (MHD_HTTP_NO_CONTENT != hr->http_status) 2348 { 2349 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 2350 "Upload failed for command %u with status %u: %s (%s)\n", 2351 (unsigned int) uer->idx, 2352 hr->http_status, 2353 TALER_ErrorCode_get_hint (hr->ec), 2354 hr->hint); 2355 global_ret = EXIT_FAILURE; 2356 } 2357 GNUNET_CONTAINER_DLL_remove (uer_head, 2358 uer_tail, 2359 uer); 2360 GNUNET_free (uer); 2361 test_shutdown (); 2362 } 2363 2364 2365 /** 2366 * Upload extension configuration 2367 * 2368 * @param exchange_url base URL of the exchange 2369 * @param idx index of the operation we are performing (for logging) 2370 * @param value arguments for POSTing configurations of extensions 2371 */ 2372 static void 2373 upload_extensions (const char *exchange_url, 2374 size_t idx, 2375 const json_t *value) 2376 { 2377 const json_t *extensions; 2378 struct TALER_MasterSignatureP sig; 2379 const char *err_name; 2380 unsigned int err_line; 2381 struct GNUNET_JSON_Specification spec[] = { 2382 GNUNET_JSON_spec_object_const ("extensions", 2383 &extensions), 2384 GNUNET_JSON_spec_fixed_auto ("extensions_sig", 2385 &sig), 2386 GNUNET_JSON_spec_end () 2387 }; 2388 2389 /* 1. Parse the signed extensions */ 2390 if (GNUNET_OK != 2391 GNUNET_JSON_parse (value, 2392 spec, 2393 &err_name, 2394 &err_line)) 2395 { 2396 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 2397 "Invalid input to set extensions: %s#%u at %u (skipping)\n", 2398 err_name, 2399 err_line, 2400 (unsigned int) idx); 2401 json_dumpf (value, 2402 stderr, 2403 JSON_INDENT (2)); 2404 global_ret = EXIT_FAILURE; 2405 GNUNET_SCHEDULER_shutdown (); 2406 return; 2407 } 2408 2409 /* 2. Verify the signature */ 2410 { 2411 struct TALER_ExtensionManifestsHashP h_manifests; 2412 2413 if (GNUNET_OK != 2414 TALER_JSON_extensions_manifests_hash (extensions, 2415 &h_manifests)) 2416 { 2417 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 2418 "couldn't hash extensions' manifests\n"); 2419 global_ret = EXIT_FAILURE; 2420 GNUNET_SCHEDULER_shutdown (); 2421 return; 2422 } 2423 2424 if (GNUNET_OK != 2425 load_offline_key (GNUNET_NO)) 2426 return; 2427 2428 if (GNUNET_OK != TALER_exchange_offline_extension_manifests_hash_verify ( 2429 &h_manifests, 2430 &master_pub, 2431 &sig)) 2432 { 2433 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 2434 "invalid signature for extensions\n"); 2435 global_ret = EXIT_FAILURE; 2436 GNUNET_SCHEDULER_shutdown (); 2437 return; 2438 } 2439 } 2440 2441 /* 3. Upload the extensions */ 2442 { 2443 struct TALER_EXCHANGE_ManagementPostExtensionsData ped = { 2444 .extensions = extensions, 2445 .extensions_sig = sig, 2446 }; 2447 struct UploadExtensionsRequest *uer 2448 = GNUNET_new (struct UploadExtensionsRequest); 2449 2450 uer->idx = idx; 2451 uer->h = TALER_EXCHANGE_post_management_extensions_create ( 2452 ctx, 2453 exchange_url, 2454 &ped); 2455 TALER_EXCHANGE_post_management_extensions_start (uer->h, 2456 &extensions_cb, 2457 uer); 2458 GNUNET_CONTAINER_DLL_insert (uer_head, 2459 uer_tail, 2460 uer); 2461 } 2462 } 2463 2464 2465 /** 2466 * Function called with information about the add partner operation. 2467 * 2468 * @param par the request 2469 * @param apr response data 2470 */ 2471 static void 2472 add_partner_cb ( 2473 struct PartnerAddRequest *par, 2474 const struct TALER_EXCHANGE_PostManagementPartnersResponse *apr) 2475 { 2476 const struct TALER_EXCHANGE_HttpResponse *hr = &apr->hr; 2477 2478 if (MHD_HTTP_NO_CONTENT != hr->http_status) 2479 { 2480 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 2481 "Upload failed for command %u with status %u: %s (%s)\n", 2482 (unsigned int) par->idx, 2483 hr->http_status, 2484 TALER_ErrorCode_get_hint (hr->ec), 2485 hr->hint); 2486 global_ret = EXIT_FAILURE; 2487 } 2488 GNUNET_CONTAINER_DLL_remove (par_head, 2489 par_tail, 2490 par); 2491 GNUNET_free (par); 2492 test_shutdown (); 2493 } 2494 2495 2496 /** 2497 * Add partner action. 2498 * 2499 * @param exchange_url base URL of the exchange 2500 * @param idx index of the operation we are performing (for logging) 2501 * @param value arguments for add partner 2502 */ 2503 static void 2504 add_partner (const char *exchange_url, 2505 size_t idx, 2506 const json_t *value) 2507 { 2508 struct TALER_MasterPublicKeyP partner_pub; 2509 struct GNUNET_TIME_Timestamp start_date; 2510 struct GNUNET_TIME_Timestamp end_date; 2511 struct GNUNET_TIME_Relative wad_frequency; 2512 struct TALER_Amount wad_fee; 2513 const char *partner_base_url; 2514 struct TALER_MasterSignatureP master_sig; 2515 struct PartnerAddRequest *par; 2516 struct GNUNET_JSON_Specification spec[] = { 2517 GNUNET_JSON_spec_fixed_auto ("partner_pub", 2518 &partner_pub), 2519 TALER_JSON_spec_amount ("wad_fee", 2520 currency, 2521 &wad_fee), 2522 GNUNET_JSON_spec_relative_time ("wad_frequency", 2523 &wad_frequency), 2524 GNUNET_JSON_spec_timestamp ("start_date", 2525 &start_date), 2526 GNUNET_JSON_spec_timestamp ("end_date", 2527 &end_date), 2528 TALER_JSON_spec_web_url ("partner_base_url", 2529 &partner_base_url), 2530 GNUNET_JSON_spec_fixed_auto ("master_sig", 2531 &master_sig), 2532 GNUNET_JSON_spec_end () 2533 }; 2534 const char *err_name; 2535 unsigned int err_line; 2536 2537 if (GNUNET_OK != 2538 GNUNET_JSON_parse (value, 2539 spec, 2540 &err_name, 2541 &err_line)) 2542 { 2543 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 2544 "Invalid input to add partner: %s#%u at %u (skipping)\n", 2545 err_name, 2546 err_line, 2547 (unsigned int) idx); 2548 json_dumpf (value, 2549 stderr, 2550 JSON_INDENT (2)); 2551 global_ret = EXIT_FAILURE; 2552 GNUNET_SCHEDULER_shutdown (); 2553 return; 2554 } 2555 par = GNUNET_new (struct PartnerAddRequest); 2556 par->idx = idx; 2557 par->h = 2558 TALER_EXCHANGE_post_management_partners_create (ctx, 2559 exchange_url, 2560 &partner_pub, 2561 start_date, 2562 end_date, 2563 wad_frequency, 2564 &wad_fee, 2565 partner_base_url, 2566 &master_sig); 2567 TALER_EXCHANGE_post_management_partners_start (par->h, 2568 &add_partner_cb, 2569 par); 2570 GNUNET_CONTAINER_DLL_insert (par_head, 2571 par_tail, 2572 par); 2573 } 2574 2575 2576 /** 2577 * Function called with information about the AML officer update operation. 2578 * 2579 * @param asr the request 2580 * @param ar response data 2581 */ 2582 static void 2583 update_aml_officer_cb ( 2584 TALER_EXCHANGE_POST_MANAGEMENT_AML_OFFICERS_RESULT_CLOSURE *asr, 2585 const struct TALER_EXCHANGE_PostManagementAmlOfficersResponse *ar) 2586 { 2587 const struct TALER_EXCHANGE_HttpResponse *hr = &ar->hr; 2588 2589 if (MHD_HTTP_NO_CONTENT != hr->http_status) 2590 { 2591 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 2592 "Upload failed for command %u with status %u: %s (%s)\n", 2593 (unsigned int) asr->idx, 2594 hr->http_status, 2595 TALER_ErrorCode_get_hint (hr->ec), 2596 hr->hint); 2597 global_ret = EXIT_FAILURE; 2598 } 2599 GNUNET_CONTAINER_DLL_remove (asr_head, 2600 asr_tail, 2601 asr); 2602 GNUNET_free (asr); 2603 test_shutdown (); 2604 } 2605 2606 2607 /** 2608 * Upload AML staff action. 2609 * 2610 * @param exchange_url base URL of the exchange 2611 * @param idx index of the operation we are performing (for logging) 2612 * @param value arguments for AML staff change 2613 */ 2614 static void 2615 update_aml_staff (const char *exchange_url, 2616 size_t idx, 2617 const json_t *value) 2618 { 2619 struct TALER_AmlOfficerPublicKeyP officer_pub; 2620 const char *officer_name; 2621 struct GNUNET_TIME_Timestamp change_date; 2622 bool is_active; 2623 bool read_only; 2624 struct TALER_MasterSignatureP master_sig; 2625 struct AmlStaffRequest *asr; 2626 struct GNUNET_JSON_Specification spec[] = { 2627 GNUNET_JSON_spec_fixed_auto ("officer_pub", 2628 &officer_pub), 2629 GNUNET_JSON_spec_timestamp ("change_date", 2630 &change_date), 2631 GNUNET_JSON_spec_bool ("is_active", 2632 &is_active), 2633 GNUNET_JSON_spec_bool ("read_only", 2634 &read_only), 2635 GNUNET_JSON_spec_string ("officer_name", 2636 &officer_name), 2637 GNUNET_JSON_spec_fixed_auto ("master_sig", 2638 &master_sig), 2639 GNUNET_JSON_spec_end () 2640 }; 2641 const char *err_name; 2642 unsigned int err_line; 2643 2644 if (GNUNET_OK != 2645 GNUNET_JSON_parse (value, 2646 spec, 2647 &err_name, 2648 &err_line)) 2649 { 2650 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 2651 "Invalid input to AML staff update: %s#%u at %u (skipping)\n", 2652 err_name, 2653 err_line, 2654 (unsigned int) idx); 2655 json_dumpf (value, 2656 stderr, 2657 JSON_INDENT (2)); 2658 global_ret = EXIT_FAILURE; 2659 GNUNET_SCHEDULER_shutdown (); 2660 return; 2661 } 2662 asr = GNUNET_new (struct AmlStaffRequest); 2663 asr->idx = idx; 2664 asr->h = TALER_EXCHANGE_post_management_aml_officers_create ( 2665 ctx, 2666 exchange_url, 2667 &officer_pub, 2668 officer_name, 2669 change_date, 2670 is_active, 2671 read_only, 2672 &master_sig); 2673 GNUNET_CONTAINER_DLL_insert (asr_head, 2674 asr_tail, 2675 asr); 2676 GNUNET_assert (TALER_EC_NONE == 2677 TALER_EXCHANGE_post_management_aml_officers_start ( 2678 asr->h, 2679 &update_aml_officer_cb, 2680 asr)); 2681 } 2682 2683 2684 /** 2685 * Perform uploads based on the JSON in #out. 2686 * 2687 * @param exchange_url base URL of the exchange to use 2688 */ 2689 static void 2690 trigger_upload (const char *exchange_url) 2691 { 2692 struct UploadHandler uhs[] = { 2693 { 2694 .key = OP_REVOKE_DENOMINATION, 2695 .cb = &upload_denom_revocation 2696 }, 2697 { 2698 .key = OP_REVOKE_SIGNKEY, 2699 .cb = &upload_signkey_revocation 2700 }, 2701 { 2702 .key = OP_ENABLE_AUDITOR, 2703 .cb = &upload_auditor_add 2704 }, 2705 { 2706 .key = OP_DISABLE_AUDITOR, 2707 .cb = &upload_auditor_del 2708 }, 2709 { 2710 .key = OP_ENABLE_WIRE, 2711 .cb = &upload_wire_add 2712 }, 2713 { 2714 .key = OP_DISABLE_WIRE, 2715 .cb = &upload_wire_del 2716 }, 2717 { 2718 .key = OP_SET_WIRE_FEE, 2719 .cb = &upload_wire_fee 2720 }, 2721 { 2722 .key = OP_SET_GLOBAL_FEE, 2723 .cb = &upload_global_fee 2724 }, 2725 { 2726 .key = OP_UPLOAD_SIGS, 2727 .cb = &upload_keys 2728 }, 2729 { 2730 .key = OP_DRAIN_PROFITS, 2731 .cb = &upload_drain 2732 }, 2733 { 2734 .key = OP_EXTENSIONS, 2735 .cb = &upload_extensions 2736 }, 2737 { 2738 .key = OP_UPDATE_AML_STAFF, 2739 .cb = &update_aml_staff 2740 }, 2741 { 2742 .key = OP_ADD_PARTNER, 2743 .cb = &add_partner 2744 }, 2745 /* array termination */ 2746 { 2747 .key = NULL 2748 } 2749 }; 2750 size_t index; 2751 json_t *obj; 2752 2753 json_array_foreach (out, index, obj) { 2754 bool found = false; 2755 const char *key; 2756 const json_t *value; 2757 2758 key = json_string_value (json_object_get (obj, "operation")); 2759 value = json_object_get (obj, "arguments"); 2760 if (NULL == key) 2761 { 2762 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 2763 "Malformed JSON input\n"); 2764 global_ret = EXIT_FAILURE; 2765 GNUNET_SCHEDULER_shutdown (); 2766 return; 2767 } 2768 /* block of code that uses key and value */ 2769 for (unsigned int i = 0; NULL != uhs[i].key; i++) 2770 { 2771 if (0 == strcasecmp (key, 2772 uhs[i].key)) 2773 { 2774 2775 found = true; 2776 uhs[i].cb (exchange_url, 2777 index, 2778 value); 2779 break; 2780 } 2781 } 2782 if (! found) 2783 { 2784 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 2785 "Upload does not know how to handle `%s'\n", 2786 key); 2787 global_ret = EXIT_FAILURE; 2788 GNUNET_SCHEDULER_shutdown (); 2789 return; 2790 } 2791 } 2792 } 2793 2794 2795 /** 2796 * Upload operation result (signatures) to exchange. 2797 * 2798 * @param args the array of command-line arguments to process next 2799 */ 2800 static void 2801 do_upload (char *const *args) 2802 { 2803 (void) args; 2804 if (NULL != in) 2805 { 2806 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 2807 "Downloaded data was not consumed, refusing upload\n"); 2808 GNUNET_SCHEDULER_shutdown (); 2809 global_ret = EXIT_FAILURE; 2810 return; 2811 } 2812 if (NULL == out) 2813 { 2814 json_error_t err; 2815 2816 out = json_loadf (stdin, 2817 JSON_REJECT_DUPLICATES, 2818 &err); 2819 if (NULL == out) 2820 { 2821 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 2822 "Failed to read JSON input: %s at %d:%s (offset: %d)\n", 2823 err.text, 2824 err.line, 2825 err.source, 2826 err.position); 2827 GNUNET_SCHEDULER_shutdown (); 2828 global_ret = EXIT_FAILURE; 2829 return; 2830 } 2831 } 2832 if (! json_is_array (out)) 2833 { 2834 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 2835 "Error: expected JSON array for `upload` command\n"); 2836 GNUNET_SCHEDULER_shutdown (); 2837 global_ret = EXIT_FAILURE; 2838 return; 2839 } 2840 if ( (NULL == CFG_exchange_url) && 2841 (GNUNET_OK != 2842 GNUNET_CONFIGURATION_get_value_string (kcfg, 2843 "exchange", 2844 "BASE_URL", 2845 &CFG_exchange_url)) ) 2846 { 2847 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, 2848 "exchange", 2849 "BASE_URL"); 2850 global_ret = EXIT_NOTCONFIGURED; 2851 GNUNET_SCHEDULER_shutdown (); 2852 return; 2853 } 2854 trigger_upload (CFG_exchange_url); 2855 json_decref (out); 2856 out = NULL; 2857 } 2858 2859 2860 /** 2861 * Revoke denomination key. 2862 * 2863 * @param args the array of command-line arguments to process next; 2864 * args[0] must be the hash of the denomination key to revoke 2865 */ 2866 static void 2867 do_revoke_denomination_key (char *const *args) 2868 { 2869 struct TALER_DenominationHashP h_denom_pub; 2870 struct TALER_MasterSignatureP master_sig; 2871 2872 if (NULL != in) 2873 { 2874 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 2875 "Downloaded data was not consumed, refusing revocation\n"); 2876 GNUNET_SCHEDULER_shutdown (); 2877 global_ret = EXIT_FAILURE; 2878 return; 2879 } 2880 if ( (NULL == args[0]) || 2881 (GNUNET_OK != 2882 GNUNET_STRINGS_string_to_data (args[0], 2883 strlen (args[0]), 2884 &h_denom_pub, 2885 sizeof (h_denom_pub))) ) 2886 { 2887 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 2888 "You must specify a denomination key with this subcommand\n"); 2889 GNUNET_SCHEDULER_shutdown (); 2890 global_ret = EXIT_INVALIDARGUMENT; 2891 return; 2892 } 2893 if (GNUNET_OK != 2894 load_offline_key (GNUNET_NO)) 2895 return; 2896 TALER_exchange_offline_denomination_revoke_sign (&h_denom_pub, 2897 &master_priv, 2898 &master_sig); 2899 output_operation (OP_REVOKE_DENOMINATION, 2900 GNUNET_JSON_PACK ( 2901 GNUNET_JSON_pack_data_auto ("h_denom_pub", 2902 &h_denom_pub), 2903 GNUNET_JSON_pack_data_auto ("master_sig", 2904 &master_sig))); 2905 next (args + 1); 2906 } 2907 2908 2909 /** 2910 * Revoke signkey. 2911 * 2912 * @param args the array of command-line arguments to process next; 2913 * args[0] must be the hash of the denomination key to revoke 2914 */ 2915 static void 2916 do_revoke_signkey (char *const *args) 2917 { 2918 struct TALER_ExchangePublicKeyP exchange_pub; 2919 struct TALER_MasterSignatureP master_sig; 2920 2921 if (NULL != in) 2922 { 2923 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 2924 "Downloaded data was not consumed, refusing revocation\n"); 2925 GNUNET_SCHEDULER_shutdown (); 2926 global_ret = EXIT_FAILURE; 2927 return; 2928 } 2929 if ( (NULL == args[0]) || 2930 (GNUNET_OK != 2931 GNUNET_STRINGS_string_to_data (args[0], 2932 strlen (args[0]), 2933 &exchange_pub, 2934 sizeof (exchange_pub))) ) 2935 { 2936 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 2937 "You must specify an exchange signing key with this subcommand\n"); 2938 GNUNET_SCHEDULER_shutdown (); 2939 global_ret = EXIT_INVALIDARGUMENT; 2940 return; 2941 } 2942 if (GNUNET_OK != 2943 load_offline_key (GNUNET_NO)) 2944 return; 2945 TALER_exchange_offline_signkey_revoke_sign (&exchange_pub, 2946 &master_priv, 2947 &master_sig); 2948 output_operation (OP_REVOKE_SIGNKEY, 2949 GNUNET_JSON_PACK ( 2950 GNUNET_JSON_pack_data_auto ("exchange_pub", 2951 &exchange_pub), 2952 GNUNET_JSON_pack_data_auto ("master_sig", 2953 &master_sig))); 2954 next (args + 1); 2955 } 2956 2957 2958 /** 2959 * Add auditor. 2960 * 2961 * @param args the array of command-line arguments to process next; 2962 * args[0] must be the auditor's public key, args[1] the auditor's 2963 * API base URL, and args[2] the auditor's name. 2964 */ 2965 static void 2966 do_add_auditor (char *const *args) 2967 { 2968 struct TALER_MasterSignatureP master_sig; 2969 struct TALER_AuditorPublicKeyP auditor_pub; 2970 struct GNUNET_TIME_Timestamp now; 2971 2972 if (NULL != in) 2973 { 2974 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 2975 "Downloaded data was not consumed, not adding auditor\n"); 2976 GNUNET_SCHEDULER_shutdown (); 2977 global_ret = EXIT_FAILURE; 2978 return; 2979 } 2980 if ( (NULL == args[0]) || 2981 (GNUNET_OK != 2982 GNUNET_STRINGS_string_to_data (args[0], 2983 strlen (args[0]), 2984 &auditor_pub, 2985 sizeof (auditor_pub))) ) 2986 { 2987 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 2988 "You must specify an auditor public key as first argument for this subcommand\n"); 2989 GNUNET_SCHEDULER_shutdown (); 2990 global_ret = EXIT_INVALIDARGUMENT; 2991 return; 2992 } 2993 2994 if ( (NULL == args[1]) || 2995 (0 != strncmp ("http", 2996 args[1], 2997 strlen ("http"))) || 2998 (NULL == args[2]) ) 2999 { 3000 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 3001 "You must specify an auditor URI and auditor name as 2nd and 3rd arguments to this subcommand\n"); 3002 GNUNET_SCHEDULER_shutdown (); 3003 global_ret = EXIT_INVALIDARGUMENT; 3004 return; 3005 } 3006 if (GNUNET_OK != 3007 load_offline_key (GNUNET_NO)) 3008 return; 3009 now = GNUNET_TIME_timestamp_get (); 3010 TALER_exchange_offline_auditor_add_sign (&auditor_pub, 3011 args[1], 3012 now, 3013 &master_priv, 3014 &master_sig); 3015 output_operation (OP_ENABLE_AUDITOR, 3016 GNUNET_JSON_PACK ( 3017 GNUNET_JSON_pack_string ("auditor_url", 3018 args[1]), 3019 GNUNET_JSON_pack_string ("auditor_name", 3020 args[2]), 3021 GNUNET_JSON_pack_timestamp ("validity_start", 3022 now), 3023 GNUNET_JSON_pack_data_auto ("auditor_pub", 3024 &auditor_pub), 3025 GNUNET_JSON_pack_data_auto ("master_sig", 3026 &master_sig))); 3027 next (args + 3); 3028 } 3029 3030 3031 /** 3032 * Disable auditor account. 3033 * 3034 * @param args the array of command-line arguments to process next; 3035 * args[0] must be the hash of the denomination key to revoke 3036 */ 3037 static void 3038 do_del_auditor (char *const *args) 3039 { 3040 struct TALER_MasterSignatureP master_sig; 3041 struct TALER_AuditorPublicKeyP auditor_pub; 3042 struct GNUNET_TIME_Timestamp now; 3043 3044 if (NULL != in) 3045 { 3046 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 3047 "Downloaded data was not consumed, not deleting auditor account\n"); 3048 GNUNET_SCHEDULER_shutdown (); 3049 global_ret = EXIT_FAILURE; 3050 return; 3051 } 3052 if ( (NULL == args[0]) || 3053 (GNUNET_OK != 3054 GNUNET_STRINGS_string_to_data (args[0], 3055 strlen (args[0]), 3056 &auditor_pub, 3057 sizeof (auditor_pub))) ) 3058 { 3059 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 3060 "You must specify an auditor public key as first argument for this subcommand\n"); 3061 GNUNET_SCHEDULER_shutdown (); 3062 global_ret = EXIT_INVALIDARGUMENT; 3063 return; 3064 } 3065 if (GNUNET_OK != 3066 load_offline_key (GNUNET_NO)) 3067 return; 3068 now = GNUNET_TIME_timestamp_get (); 3069 TALER_exchange_offline_auditor_del_sign (&auditor_pub, 3070 now, 3071 &master_priv, 3072 &master_sig); 3073 output_operation (OP_DISABLE_AUDITOR, 3074 GNUNET_JSON_PACK ( 3075 GNUNET_JSON_pack_data_auto ("auditor_pub", 3076 &auditor_pub), 3077 GNUNET_JSON_pack_timestamp ("validity_end", 3078 now), 3079 GNUNET_JSON_pack_data_auto ("master_sig", 3080 &master_sig))); 3081 next (args + 1); 3082 } 3083 3084 3085 /** 3086 * Parse account restriction. 3087 * 3088 * @param args the array of command-line arguments to process next 3089 * @param[in,out] restrictions JSON array to update 3090 * @return -1 on error, otherwise number of arguments from @a args that were used 3091 */ 3092 static int 3093 parse_restriction (char *const *args, 3094 json_t *restrictions) 3095 { 3096 if (NULL == args[0]) 3097 { 3098 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 3099 "Restriction TYPE argument missing\n"); 3100 return -1; 3101 } 3102 if (0 == strcmp (args[0], 3103 "deny")) 3104 { 3105 GNUNET_assert (0 == 3106 json_array_append_new ( 3107 restrictions, 3108 GNUNET_JSON_PACK ( 3109 GNUNET_JSON_pack_string ("type", 3110 "deny")))); 3111 return 1; 3112 } 3113 if (0 == strcmp (args[0], 3114 "regex")) 3115 { 3116 json_t *i18n; 3117 json_error_t err; 3118 3119 if ( (NULL == args[1]) || 3120 (NULL == args[2]) || 3121 (NULL == args[3]) ) 3122 { 3123 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 3124 "Mandatory arguments for restriction of type `regex' missing (REGEX, HINT, HINT-I18 required)\n"); 3125 return -1; 3126 } 3127 { 3128 regex_t ex; 3129 3130 if (0 != regcomp (&ex, 3131 args[1], 3132 REG_NOSUB | REG_EXTENDED)) 3133 { 3134 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 3135 "Invalid regular expression `%s'\n", 3136 args[1]); 3137 return -1; 3138 } 3139 regfree (&ex); 3140 } 3141 3142 i18n = json_loads (args[3], 3143 JSON_REJECT_DUPLICATES, 3144 &err); 3145 if (NULL == i18n) 3146 { 3147 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 3148 "Invalid JSON for restriction of type `regex': `%s` at %d\n", 3149 args[3], 3150 err.position); 3151 return -1; 3152 } 3153 GNUNET_assert (0 == 3154 json_array_append_new ( 3155 restrictions, 3156 GNUNET_JSON_PACK ( 3157 GNUNET_JSON_pack_string ("type", 3158 "regex"), 3159 GNUNET_JSON_pack_string ("payto_regex", 3160 args[1]), 3161 GNUNET_JSON_pack_string ("human_hint", 3162 args[2]), 3163 GNUNET_JSON_pack_object_steal ("human_hint_i18n", 3164 i18n) 3165 ))); 3166 return 4; 3167 } 3168 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 3169 "Restriction TYPE `%s' unsupported\n", 3170 args[0]); 3171 return -1; 3172 } 3173 3174 3175 /** 3176 * Add wire account. 3177 * 3178 * @param args the array of command-line arguments to process next; 3179 * args[0] must be the hash of the denomination key to revoke 3180 */ 3181 static void 3182 do_add_wire (char *const *args) 3183 { 3184 struct TALER_MasterSignatureP master_sig_add; 3185 struct TALER_MasterSignatureP master_sig_wire; 3186 struct GNUNET_TIME_Timestamp now; 3187 const char *conversion_url = NULL; 3188 const char *open_banking_gateway = NULL; 3189 const char *wire_transfer_gateway = NULL; 3190 const char *bank_label = NULL; 3191 int64_t priority = 0; 3192 json_t *debit_restrictions; 3193 json_t *credit_restrictions; 3194 unsigned int num_args = 1; 3195 struct TALER_FullPayto payto_uri = { 3196 .full_payto = args[0] 3197 }; 3198 3199 if (NULL != in) 3200 { 3201 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 3202 "Downloaded data was not consumed, not adding wire account\n"); 3203 GNUNET_SCHEDULER_shutdown (); 3204 global_ret = EXIT_FAILURE; 3205 return; 3206 } 3207 if (NULL == args[0]) 3208 { 3209 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 3210 "You must specify a payto://-URI with this subcommand\n"); 3211 GNUNET_SCHEDULER_shutdown (); 3212 global_ret = EXIT_INVALIDARGUMENT; 3213 return; 3214 } 3215 if (GNUNET_OK != 3216 load_offline_key (GNUNET_NO)) 3217 return; 3218 { 3219 char *msg = TALER_payto_validate (payto_uri); 3220 3221 if (NULL != msg) 3222 { 3223 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 3224 "payto URI `%s' is malformed: %s\n", 3225 payto_uri.full_payto, 3226 msg); 3227 GNUNET_free (msg); 3228 GNUNET_SCHEDULER_shutdown (); 3229 global_ret = EXIT_INVALIDARGUMENT; 3230 return; 3231 } 3232 } 3233 now = GNUNET_TIME_timestamp_get (); 3234 { 3235 char *wire_method; 3236 3237 wire_method = TALER_payto_get_method (payto_uri.full_payto); 3238 if (NULL == wire_method) 3239 { 3240 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 3241 "payto:// URI `%s' is malformed\n", 3242 payto_uri.full_payto); 3243 global_ret = EXIT_INVALIDARGUMENT; 3244 GNUNET_SCHEDULER_shutdown (); 3245 return; 3246 } 3247 GNUNET_free (wire_method); 3248 } 3249 debit_restrictions = json_array (); 3250 GNUNET_assert (NULL != debit_restrictions); 3251 credit_restrictions = json_array (); 3252 GNUNET_assert (NULL != credit_restrictions); 3253 while (NULL != args[num_args]) 3254 { 3255 if (0 == strcmp (args[num_args], 3256 "conversion-url")) 3257 { 3258 num_args++; 3259 conversion_url = args[num_args]; 3260 if (NULL == conversion_url) 3261 { 3262 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 3263 "'conversion-url' requires an argument\n"); 3264 global_ret = EXIT_INVALIDARGUMENT; 3265 GNUNET_SCHEDULER_shutdown (); 3266 json_decref (debit_restrictions); 3267 json_decref (credit_restrictions); 3268 return; 3269 } 3270 if (! TALER_is_web_url (conversion_url)) 3271 { 3272 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 3273 "'conversion-url' must refer to HTTP(S) endpoint, `%s' is invalid\n", 3274 conversion_url); 3275 global_ret = EXIT_INVALIDARGUMENT; 3276 GNUNET_SCHEDULER_shutdown (); 3277 json_decref (debit_restrictions); 3278 json_decref (credit_restrictions); 3279 return; 3280 } 3281 num_args++; 3282 continue; 3283 } 3284 3285 if (0 == strcmp (args[num_args], 3286 "open-banking-gateway")) 3287 { 3288 num_args++; 3289 open_banking_gateway = args[num_args]; 3290 if (NULL == open_banking_gateway) 3291 { 3292 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 3293 "'open-banking-gateway' requires an argument\n"); 3294 global_ret = EXIT_INVALIDARGUMENT; 3295 GNUNET_SCHEDULER_shutdown (); 3296 json_decref (debit_restrictions); 3297 json_decref (credit_restrictions); 3298 return; 3299 } 3300 if (! TALER_is_web_url (open_banking_gateway)) 3301 { 3302 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 3303 "'open-banking-gateway' must refer to HTTP(S) endpoint, `%s' is invalid\n", 3304 open_banking_gateway); 3305 global_ret = EXIT_INVALIDARGUMENT; 3306 GNUNET_SCHEDULER_shutdown (); 3307 json_decref (debit_restrictions); 3308 json_decref (credit_restrictions); 3309 return; 3310 } 3311 num_args++; 3312 continue; 3313 } 3314 3315 if (0 == strcmp (args[num_args], 3316 "wire-transfer-gateway")) 3317 { 3318 num_args++; 3319 wire_transfer_gateway = args[num_args]; 3320 if (NULL == wire_transfer_gateway) 3321 { 3322 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 3323 "'wire-transfer-gateway' requires an argument\n"); 3324 global_ret = EXIT_INVALIDARGUMENT; 3325 GNUNET_SCHEDULER_shutdown (); 3326 json_decref (debit_restrictions); 3327 json_decref (credit_restrictions); 3328 return; 3329 } 3330 if (! TALER_is_web_url (wire_transfer_gateway)) 3331 { 3332 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 3333 "'wire-transfer-gateway' must refer to HTTP(S) endpoint, `%s' is invalid\n", 3334 wire_transfer_gateway); 3335 global_ret = EXIT_INVALIDARGUMENT; 3336 GNUNET_SCHEDULER_shutdown (); 3337 json_decref (debit_restrictions); 3338 json_decref (credit_restrictions); 3339 return; 3340 } 3341 num_args++; 3342 continue; 3343 } 3344 3345 if (0 == strcmp (args[num_args], 3346 "credit-restriction")) 3347 { 3348 int iret; 3349 3350 num_args++; 3351 iret = parse_restriction (&args[num_args], 3352 credit_restrictions); 3353 if (iret <= 0) 3354 { 3355 global_ret = EXIT_INVALIDARGUMENT; 3356 GNUNET_SCHEDULER_shutdown (); 3357 json_decref (debit_restrictions); 3358 json_decref (credit_restrictions); 3359 return; 3360 } 3361 num_args += iret; 3362 continue; 3363 } 3364 if (0 == strcmp (args[num_args], 3365 "debit-restriction")) 3366 { 3367 int iret; 3368 3369 num_args++; 3370 iret = parse_restriction (&args[num_args], 3371 debit_restrictions); 3372 if (iret <= 0) 3373 { 3374 global_ret = EXIT_INVALIDARGUMENT; 3375 GNUNET_SCHEDULER_shutdown (); 3376 json_decref (debit_restrictions); 3377 json_decref (credit_restrictions); 3378 return; 3379 } 3380 num_args += iret; 3381 continue; 3382 } 3383 if (0 == strcmp (args[num_args], 3384 "display-hint")) 3385 { 3386 long long p; 3387 char dummy; 3388 3389 num_args++; 3390 if ( (NULL == args[num_args]) || 3391 (NULL == args[num_args + 1]) ) 3392 { 3393 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 3394 "'display-hint' requires at least two arguments (priority and label)\n"); 3395 global_ret = EXIT_INVALIDARGUMENT; 3396 GNUNET_SCHEDULER_shutdown (); 3397 json_decref (debit_restrictions); 3398 json_decref (credit_restrictions); 3399 return; 3400 } 3401 if (1 != sscanf (args[num_args], 3402 "%lld%c", 3403 &p, 3404 &dummy)) 3405 { 3406 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 3407 "Priority argument `%s' is not a number\n", 3408 args[num_args]); 3409 global_ret = EXIT_INVALIDARGUMENT; 3410 GNUNET_SCHEDULER_shutdown (); 3411 json_decref (debit_restrictions); 3412 json_decref (credit_restrictions); 3413 return; 3414 } 3415 priority = (int64_t) p; 3416 num_args++; 3417 bank_label = args[num_args]; 3418 num_args++; 3419 continue; 3420 } 3421 break; 3422 } 3423 TALER_exchange_offline_wire_add_sign (payto_uri, 3424 conversion_url, 3425 open_banking_gateway, 3426 wire_transfer_gateway, 3427 debit_restrictions, 3428 credit_restrictions, 3429 now, 3430 &master_priv, 3431 &master_sig_add); 3432 TALER_exchange_wire_signature_make (payto_uri, 3433 conversion_url, 3434 open_banking_gateway, 3435 wire_transfer_gateway, 3436 debit_restrictions, 3437 credit_restrictions, 3438 &master_priv, 3439 &master_sig_wire); 3440 output_operation (OP_ENABLE_WIRE, 3441 GNUNET_JSON_PACK ( 3442 TALER_JSON_pack_full_payto ("payto_uri", 3443 payto_uri), 3444 GNUNET_JSON_pack_array_steal ("debit_restrictions", 3445 debit_restrictions), 3446 GNUNET_JSON_pack_array_steal ("credit_restrictions", 3447 credit_restrictions), 3448 GNUNET_JSON_pack_allow_null ( 3449 GNUNET_JSON_pack_string ("conversion_url", 3450 conversion_url)), 3451 GNUNET_JSON_pack_allow_null ( 3452 GNUNET_JSON_pack_string ("open_banking_gateway", 3453 open_banking_gateway)), 3454 GNUNET_JSON_pack_allow_null ( 3455 GNUNET_JSON_pack_string ("wire_transfer_gateway", 3456 wire_transfer_gateway)), 3457 GNUNET_JSON_pack_allow_null ( 3458 GNUNET_JSON_pack_string ("bank_label", 3459 bank_label)), 3460 GNUNET_JSON_pack_int64 ("priority", 3461 priority), 3462 GNUNET_JSON_pack_timestamp ("validity_start", 3463 now), 3464 GNUNET_JSON_pack_data_auto ("master_sig_add", 3465 &master_sig_add), 3466 GNUNET_JSON_pack_data_auto ("master_sig_wire", 3467 &master_sig_wire))); 3468 next (args + num_args); 3469 } 3470 3471 3472 /** 3473 * Disable wire account. 3474 * 3475 * @param args the array of command-line arguments to process next; 3476 * args[0] must be the payto URI of the account to disable 3477 */ 3478 static void 3479 do_del_wire (char *const *args) 3480 { 3481 struct TALER_MasterSignatureP master_sig; 3482 struct GNUNET_TIME_Timestamp now; 3483 struct TALER_FullPayto payto_uri = { 3484 .full_payto = args[0] 3485 }; 3486 3487 if (NULL != in) 3488 { 3489 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 3490 "Downloaded data was not consumed, not deleting wire account\n") 3491 ; 3492 GNUNET_SCHEDULER_shutdown (); 3493 global_ret = EXIT_FAILURE; 3494 return; 3495 } 3496 if (NULL == args[0]) 3497 { 3498 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 3499 "You must specify a payto://-URI with this subcommand\n"); 3500 GNUNET_SCHEDULER_shutdown (); 3501 global_ret = EXIT_INVALIDARGUMENT; 3502 return; 3503 } 3504 if (GNUNET_OK != 3505 load_offline_key (GNUNET_NO)) 3506 return; 3507 now = GNUNET_TIME_timestamp_get (); 3508 TALER_exchange_offline_wire_del_sign (payto_uri, 3509 now, 3510 &master_priv, 3511 &master_sig); 3512 output_operation (OP_DISABLE_WIRE, 3513 GNUNET_JSON_PACK ( 3514 TALER_JSON_pack_full_payto ("payto_uri", 3515 payto_uri), 3516 GNUNET_JSON_pack_timestamp ("validity_end", 3517 now), 3518 GNUNET_JSON_pack_data_auto ("master_sig", 3519 &master_sig))); 3520 next (args + 1); 3521 } 3522 3523 3524 /** 3525 * Set wire fees for the given year. 3526 * 3527 * @param args the array of command-line arguments to process next; 3528 * args[0] must be the year, args[1] the wire method, args[2] the wire fee and args[3] 3529 * the closing fee. 3530 */ 3531 static void 3532 do_set_wire_fee (char *const *args) 3533 { 3534 struct TALER_MasterSignatureP master_sig; 3535 char dummy; 3536 unsigned int year; 3537 struct TALER_WireFeeSet fees; 3538 struct GNUNET_TIME_Timestamp start_time; 3539 struct GNUNET_TIME_Timestamp end_time; 3540 3541 if (NULL != in) 3542 { 3543 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 3544 "Downloaded data was not consumed, not setting wire fee\n"); 3545 GNUNET_SCHEDULER_shutdown (); 3546 global_ret = EXIT_FAILURE; 3547 return; 3548 } 3549 if ( (NULL == args[0]) || 3550 (NULL == args[1]) || 3551 (NULL == args[2]) || 3552 (NULL == args[3]) || 3553 ( (1 != sscanf (args[0], 3554 "%u%c", 3555 &year, 3556 &dummy)) && 3557 (0 != strcasecmp ("now", 3558 args[0])) ) || 3559 (GNUNET_OK != 3560 TALER_string_to_amount (args[2], 3561 &fees.wire)) || 3562 (GNUNET_OK != 3563 TALER_string_to_amount (args[3], 3564 &fees.closing)) ) 3565 { 3566 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 3567 "You must use YEAR, METHOD, WIRE-FEE, and CLOSING-FEE as arguments for this subcommand\n"); 3568 GNUNET_SCHEDULER_shutdown (); 3569 global_ret = EXIT_INVALIDARGUMENT; 3570 return; 3571 } 3572 if (0 == strcasecmp ("now", 3573 args[0])) 3574 year = GNUNET_TIME_get_current_year (); 3575 if (GNUNET_OK != 3576 load_offline_key (GNUNET_NO)) 3577 return; 3578 start_time = GNUNET_TIME_absolute_to_timestamp ( 3579 GNUNET_TIME_year_to_time (year)); 3580 end_time = GNUNET_TIME_absolute_to_timestamp ( 3581 GNUNET_TIME_year_to_time (year + 1)); 3582 3583 TALER_exchange_offline_wire_fee_sign (args[1], 3584 start_time, 3585 end_time, 3586 &fees, 3587 &master_priv, 3588 &master_sig); 3589 output_operation (OP_SET_WIRE_FEE, 3590 GNUNET_JSON_PACK ( 3591 GNUNET_JSON_pack_string ("wire_method", 3592 args[1]), 3593 GNUNET_JSON_pack_timestamp ("start_time", 3594 start_time), 3595 GNUNET_JSON_pack_timestamp ("end_time", 3596 end_time), 3597 TALER_JSON_pack_amount ("wire_fee", 3598 &fees.wire), 3599 TALER_JSON_pack_amount ("closing_fee", 3600 &fees.closing), 3601 GNUNET_JSON_pack_data_auto ("master_sig", 3602 &master_sig))); 3603 next (args + 4); 3604 } 3605 3606 3607 /** 3608 * Set global fees for the given year. 3609 * 3610 * @param args the array of command-line arguments to process next; 3611 * args[0] must be the year, args[1] the history fee, args[2] 3612 * the account fee and args[3] the purse fee. These are followed by args[4] purse timeout, 3613 * args[5] history expiration. Last is args[6] the (free) purse account limit. 3614 */ 3615 static void 3616 do_set_global_fee (char *const *args) 3617 { 3618 struct TALER_MasterSignatureP master_sig; 3619 char dummy; 3620 unsigned int year; 3621 struct TALER_GlobalFeeSet fees; 3622 struct GNUNET_TIME_Relative purse_timeout; 3623 struct GNUNET_TIME_Relative history_expiration; 3624 unsigned int purse_account_limit; 3625 struct GNUNET_TIME_Timestamp start_time; 3626 struct GNUNET_TIME_Timestamp end_time; 3627 3628 if (NULL != in) 3629 { 3630 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 3631 "Downloaded data was not consumed, not setting global fee\n"); 3632 GNUNET_SCHEDULER_shutdown (); 3633 global_ret = EXIT_FAILURE; 3634 return; 3635 } 3636 if ( (NULL == args[0]) || 3637 (NULL == args[1]) || 3638 (NULL == args[2]) || 3639 (NULL == args[3]) || 3640 (NULL == args[4]) || 3641 (NULL == args[5]) || 3642 (NULL == args[6]) ) 3643 { 3644 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 3645 "You must use YEAR, HISTORY-FEE, ACCOUNT-FEE, PURSE-FEE, PURSE-TIMEOUT, HISTORY-EXPIRATION and PURSE-ACCOUNT-LIMIT as arguments for this subcommand\n"); 3646 GNUNET_SCHEDULER_shutdown (); 3647 global_ret = EXIT_INVALIDARGUMENT; 3648 return; 3649 } 3650 if ( (1 != sscanf (args[0], 3651 "%u%c", 3652 &year, 3653 &dummy)) && 3654 (0 != strcasecmp ("now", 3655 args[0])) ) 3656 { 3657 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 3658 "Invalid YEAR given for 'global-fee' subcommand\n"); 3659 GNUNET_SCHEDULER_shutdown (); 3660 global_ret = EXIT_INVALIDARGUMENT; 3661 return; 3662 } 3663 if ( (GNUNET_OK != 3664 TALER_string_to_amount (args[1], 3665 &fees.history)) || 3666 (GNUNET_OK != 3667 TALER_string_to_amount (args[2], 3668 &fees.account)) || 3669 (GNUNET_OK != 3670 TALER_string_to_amount (args[3], 3671 &fees.purse)) ) 3672 { 3673 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 3674 "Invalid amount given for 'global-fee' subcommand\n"); 3675 GNUNET_SCHEDULER_shutdown (); 3676 global_ret = EXIT_INVALIDARGUMENT; 3677 return; 3678 } 3679 if (GNUNET_OK != 3680 GNUNET_STRINGS_fancy_time_to_relative (args[4], 3681 &purse_timeout)) 3682 { 3683 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 3684 "Invalid purse timeout `%s' given for 'global-fee' subcommand\n", 3685 args[4]); 3686 GNUNET_SCHEDULER_shutdown (); 3687 global_ret = EXIT_INVALIDARGUMENT; 3688 return; 3689 } 3690 if (GNUNET_OK != 3691 GNUNET_STRINGS_fancy_time_to_relative (args[5], 3692 &history_expiration)) 3693 { 3694 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 3695 "Invalid history expiratoin `%s' given for 'global-fee' subcommand\n", 3696 args[5]); 3697 GNUNET_SCHEDULER_shutdown (); 3698 global_ret = EXIT_INVALIDARGUMENT; 3699 return; 3700 } 3701 if (1 != sscanf (args[6], 3702 "%u%c", 3703 &purse_account_limit, 3704 &dummy)) 3705 { 3706 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 3707 "Invalid purse account limit given for 'global-fee' subcommand\n"); 3708 GNUNET_SCHEDULER_shutdown (); 3709 global_ret = EXIT_INVALIDARGUMENT; 3710 return; 3711 } 3712 if (0 == strcasecmp ("now", 3713 args[0])) 3714 year = GNUNET_TIME_get_current_year (); 3715 if (GNUNET_OK != 3716 load_offline_key (GNUNET_NO)) 3717 return; 3718 start_time = GNUNET_TIME_absolute_to_timestamp ( 3719 GNUNET_TIME_year_to_time (year)); 3720 end_time = GNUNET_TIME_absolute_to_timestamp ( 3721 GNUNET_TIME_year_to_time (year + 1)); 3722 3723 TALER_exchange_offline_global_fee_sign (start_time, 3724 end_time, 3725 &fees, 3726 purse_timeout, 3727 history_expiration, 3728 (uint32_t) purse_account_limit, 3729 &master_priv, 3730 &master_sig); 3731 output_operation (OP_SET_GLOBAL_FEE, 3732 GNUNET_JSON_PACK ( 3733 GNUNET_JSON_pack_timestamp ("start_time", 3734 start_time), 3735 GNUNET_JSON_pack_timestamp ("end_time", 3736 end_time), 3737 TALER_JSON_pack_amount ("history_fee", 3738 &fees.history), 3739 TALER_JSON_pack_amount ("account_fee", 3740 &fees.account), 3741 TALER_JSON_pack_amount ("purse_fee", 3742 &fees.purse), 3743 GNUNET_JSON_pack_time_rel ("purse_timeout", 3744 purse_timeout), 3745 GNUNET_JSON_pack_time_rel ("history_expiration", 3746 history_expiration), 3747 GNUNET_JSON_pack_uint64 ("purse_account_limit", 3748 (uint32_t) purse_account_limit), 3749 GNUNET_JSON_pack_data_auto ("master_sig", 3750 &master_sig))); 3751 next (args + 7); 3752 } 3753 3754 3755 /** 3756 * Drain profits from exchange's escrow account to 3757 * regular exchange account. 3758 * 3759 * @param args the array of command-line arguments to process next; 3760 * args[0] must be the amount, 3761 * args[1] must be the section of the escrow account to drain 3762 * args[2] must be the payto://-URI of the target account 3763 */ 3764 static void 3765 do_drain (char *const *args) 3766 { 3767 struct TALER_WireTransferIdentifierRawP wtid; 3768 struct GNUNET_TIME_Timestamp date; 3769 struct TALER_Amount amount; 3770 const char *account_section; 3771 struct TALER_FullPayto payto_uri; 3772 struct TALER_MasterSignatureP master_sig; 3773 char *err; 3774 3775 if (NULL != in) 3776 { 3777 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 3778 "Downloaded data was not consumed, refusing drain\n"); 3779 GNUNET_SCHEDULER_shutdown (); 3780 global_ret = EXIT_FAILURE; 3781 return; 3782 } 3783 if ( (NULL == args[0]) || 3784 (NULL == args[1]) || 3785 (NULL == args[2]) || 3786 (GNUNET_OK != 3787 TALER_string_to_amount (args[0], 3788 &amount)) ) 3789 { 3790 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 3791 "Drain requires an amount, section name and target payto://-URI as arguments\n"); 3792 GNUNET_SCHEDULER_shutdown (); 3793 global_ret = EXIT_INVALIDARGUMENT; 3794 return; 3795 } 3796 if ( (NULL == args[0]) || 3797 (NULL == args[1]) || 3798 (NULL == args[2]) ) 3799 { 3800 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 3801 "Drain requires an amount, section name and target payto://-URI as arguments\n"); 3802 GNUNET_SCHEDULER_shutdown (); 3803 global_ret = EXIT_INVALIDARGUMENT; 3804 return; 3805 } 3806 if (GNUNET_OK != 3807 TALER_string_to_amount (args[0], 3808 &amount)) 3809 { 3810 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 3811 "Invalid amount `%s' specified for drain\n", 3812 args[0]); 3813 GNUNET_SCHEDULER_shutdown (); 3814 global_ret = EXIT_INVALIDARGUMENT; 3815 return; 3816 } 3817 account_section = args[1]; 3818 payto_uri.full_payto = args[2]; 3819 err = TALER_payto_validate (payto_uri); 3820 if (NULL != err) 3821 { 3822 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 3823 "Invalid payto://-URI `%s' specified for drain: %s\n", 3824 payto_uri.full_payto, 3825 err); 3826 GNUNET_free (err); 3827 GNUNET_SCHEDULER_shutdown (); 3828 global_ret = EXIT_INVALIDARGUMENT; 3829 return; 3830 } 3831 if (GNUNET_OK != 3832 load_offline_key (GNUNET_NO)) 3833 return; 3834 GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_NONCE, 3835 &wtid, 3836 sizeof (wtid)); 3837 date = GNUNET_TIME_timestamp_get (); 3838 TALER_exchange_offline_profit_drain_sign (&wtid, 3839 date, 3840 &amount, 3841 account_section, 3842 payto_uri, 3843 &master_priv, 3844 &master_sig); 3845 output_operation (OP_DRAIN_PROFITS, 3846 GNUNET_JSON_PACK ( 3847 GNUNET_JSON_pack_data_auto ("wtid", 3848 &wtid), 3849 GNUNET_JSON_pack_string ("account_section", 3850 account_section), 3851 TALER_JSON_pack_full_payto ("payto_uri", 3852 payto_uri), 3853 TALER_JSON_pack_amount ("amount", 3854 &amount), 3855 GNUNET_JSON_pack_timestamp ("date", 3856 date), 3857 GNUNET_JSON_pack_data_auto ("master_sig", 3858 &master_sig))); 3859 next (args + 3); 3860 } 3861 3862 3863 /** 3864 * Add partner. 3865 * 3866 * @param args the array of command-line arguments to process next; 3867 * args[0] must be the partner's master public key, args[1] the partner's 3868 * API base URL, args[2] the wad fee, args[3] the wad frequency, and 3869 * args[4] the year (including possibly 'now') 3870 */ 3871 static void 3872 do_add_partner (char *const *args) 3873 { 3874 struct TALER_MasterPublicKeyP partner_pub; 3875 struct GNUNET_TIME_Timestamp start_date; 3876 struct GNUNET_TIME_Timestamp end_date; 3877 struct GNUNET_TIME_Relative wad_frequency; 3878 struct TALER_Amount wad_fee; 3879 const char *partner_base_url; 3880 struct TALER_MasterSignatureP master_sig; 3881 char dummy; 3882 unsigned int year; 3883 3884 if (NULL != in) 3885 { 3886 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 3887 "Downloaded data was not consumed, not adding partner\n"); 3888 GNUNET_SCHEDULER_shutdown (); 3889 global_ret = EXIT_FAILURE; 3890 return; 3891 } 3892 if ( (NULL == args[0]) || 3893 (GNUNET_OK != 3894 GNUNET_STRINGS_string_to_data (args[0], 3895 strlen (args[0]), 3896 &partner_pub, 3897 sizeof (partner_pub))) ) 3898 { 3899 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 3900 "You must specify the partner master public key as first argument for this subcommand\n"); 3901 GNUNET_SCHEDULER_shutdown (); 3902 global_ret = EXIT_INVALIDARGUMENT; 3903 return; 3904 } 3905 if ( (NULL == args[1]) || 3906 (0 != strncmp ("http", 3907 args[1], 3908 strlen ("http"))) ) 3909 { 3910 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 3911 "You must specify the partner's base URL as the 2nd argument to this subcommand\n"); 3912 GNUNET_SCHEDULER_shutdown (); 3913 global_ret = EXIT_INVALIDARGUMENT; 3914 return; 3915 } 3916 partner_base_url = args[1]; 3917 if ( (NULL == args[2]) || 3918 (GNUNET_OK != 3919 TALER_string_to_amount (args[2], 3920 &wad_fee)) ) 3921 { 3922 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 3923 "Invalid amount `%s' specified for wad fee of partner\n", 3924 args[2]); 3925 GNUNET_SCHEDULER_shutdown (); 3926 global_ret = EXIT_INVALIDARGUMENT; 3927 return; 3928 } 3929 if ( (NULL == args[3]) || 3930 (GNUNET_OK != 3931 GNUNET_STRINGS_fancy_time_to_relative (args[3], 3932 &wad_frequency)) ) 3933 { 3934 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 3935 "Invalid wad frequency `%s' specified for add partner\n", 3936 args[3]); 3937 GNUNET_SCHEDULER_shutdown (); 3938 global_ret = EXIT_INVALIDARGUMENT; 3939 return; 3940 } 3941 if ( (NULL == args[4]) || 3942 ( (1 != sscanf (args[4], 3943 "%u%c", 3944 &year, 3945 &dummy)) && 3946 (0 != strcasecmp ("now", 3947 args[4])) ) ) 3948 { 3949 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 3950 "Invalid year `%s' specified for add partner\n", 3951 args[4]); 3952 GNUNET_SCHEDULER_shutdown (); 3953 global_ret = EXIT_INVALIDARGUMENT; 3954 return; 3955 } 3956 if (0 == strcasecmp ("now", 3957 args[4])) 3958 year = GNUNET_TIME_get_current_year (); 3959 start_date = GNUNET_TIME_absolute_to_timestamp ( 3960 GNUNET_TIME_year_to_time (year)); 3961 end_date = GNUNET_TIME_absolute_to_timestamp ( 3962 GNUNET_TIME_year_to_time (year + 1)); 3963 3964 if (GNUNET_OK != 3965 load_offline_key (GNUNET_NO)) 3966 return; 3967 TALER_exchange_offline_partner_details_sign (&partner_pub, 3968 start_date, 3969 end_date, 3970 wad_frequency, 3971 &wad_fee, 3972 partner_base_url, 3973 &master_priv, 3974 &master_sig); 3975 output_operation (OP_ADD_PARTNER, 3976 GNUNET_JSON_PACK ( 3977 GNUNET_JSON_pack_string ("partner_base_url", 3978 partner_base_url), 3979 GNUNET_JSON_pack_time_rel ("wad_frequency", 3980 wad_frequency), 3981 GNUNET_JSON_pack_timestamp ("start_date", 3982 start_date), 3983 GNUNET_JSON_pack_timestamp ("end_date", 3984 end_date), 3985 GNUNET_JSON_pack_data_auto ("partner_pub", 3986 &partner_pub), 3987 GNUNET_JSON_pack_data_auto ("master_sig", 3988 &master_sig))); 3989 next (args + 5); 3990 } 3991 3992 3993 /** 3994 * Enable or disable AML staff. 3995 * 3996 * @param is_active true to enable, false to disable 3997 * @param args the array of command-line arguments to process next; args[0] must be the AML staff's public key, args[1] the AML staff's legal name, and if @a is_active then args[2] rw (read write) or ro (read only) 3998 */ 3999 static void 4000 do_set_aml_staff (bool is_active, 4001 char *const *args) 4002 { 4003 struct TALER_AmlOfficerPublicKeyP officer_pub; 4004 const char *officer_name; 4005 bool read_only; 4006 struct TALER_MasterSignatureP master_sig; 4007 struct GNUNET_TIME_Timestamp now 4008 = GNUNET_TIME_timestamp_get (); 4009 4010 if (NULL != in) 4011 { 4012 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 4013 "Downloaded data was not consumed, not updating AML staff status\n"); 4014 GNUNET_SCHEDULER_shutdown (); 4015 global_ret = EXIT_FAILURE; 4016 return; 4017 } 4018 if ( (NULL == args[0]) || 4019 (GNUNET_OK != 4020 GNUNET_STRINGS_string_to_data (args[0], 4021 strlen (args[0]), 4022 &officer_pub, 4023 sizeof (officer_pub))) ) 4024 { 4025 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 4026 "You must specify the AML officer's public key as first argument for this subcommand\n"); 4027 GNUNET_SCHEDULER_shutdown (); 4028 global_ret = EXIT_INVALIDARGUMENT; 4029 return; 4030 } 4031 if (NULL == args[1]) 4032 { 4033 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 4034 "You must specify the officer's legal name as the 2nd argument to this subcommand\n"); 4035 GNUNET_SCHEDULER_shutdown (); 4036 global_ret = EXIT_INVALIDARGUMENT; 4037 return; 4038 } 4039 officer_name = args[1]; 4040 if (is_active) 4041 { 4042 if ( (NULL == args[2]) || 4043 ( (0 != strcmp (args[2], 4044 "ro")) && 4045 (0 != strcmp (args[2], 4046 "rw")) ) ) 4047 { 4048 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 4049 "You must specify 'ro' or 'rw' (and not `%s') for the access level\n", 4050 args[2]); 4051 GNUNET_SCHEDULER_shutdown (); 4052 global_ret = EXIT_INVALIDARGUMENT; 4053 return; 4054 } 4055 read_only = (0 == strcmp (args[2], 4056 "ro")); 4057 } 4058 else 4059 { 4060 read_only = true; 4061 } 4062 if (GNUNET_OK != 4063 load_offline_key (GNUNET_NO)) 4064 return; 4065 TALER_exchange_offline_aml_officer_status_sign (&officer_pub, 4066 officer_name, 4067 now, 4068 is_active, 4069 read_only, 4070 &master_priv, 4071 &master_sig); 4072 output_operation (OP_UPDATE_AML_STAFF, 4073 GNUNET_JSON_PACK ( 4074 GNUNET_JSON_pack_string ("officer_name", 4075 officer_name), 4076 GNUNET_JSON_pack_timestamp ("change_date", 4077 now), 4078 GNUNET_JSON_pack_bool ("is_active", 4079 is_active), 4080 GNUNET_JSON_pack_bool ("read_only", 4081 read_only), 4082 GNUNET_JSON_pack_data_auto ("officer_pub", 4083 &officer_pub), 4084 GNUNET_JSON_pack_data_auto ("master_sig", 4085 &master_sig))); 4086 next (args + (is_active ? 3 : 2)); 4087 } 4088 4089 4090 /** 4091 * Disable AML staff. 4092 * 4093 * @param args the array of command-line arguments to process next; 4094 * args[0] must be the AML staff's public key, args[1] the AML staff's legal name, args[2] rw (read write) or ro (read only) 4095 */ 4096 static void 4097 disable_aml_staff (char *const *args) 4098 { 4099 do_set_aml_staff (false, 4100 args); 4101 } 4102 4103 4104 /** 4105 * Enable AML staff. 4106 * 4107 * @param args the array of command-line arguments to process next; 4108 * args[0] must be the AML staff's public key, args[1] the AML staff's legal name, args[2] rw (read write) or ro (read only) 4109 */ 4110 static void 4111 enable_aml_staff (char *const *args) 4112 { 4113 do_set_aml_staff (true, 4114 args); 4115 } 4116 4117 4118 /** 4119 * Function called with information about future keys. Dumps the JSON output 4120 * (on success), either into an internal buffer or to stdout (depending on 4121 * whether there are subsequent commands). 4122 * 4123 * @param args the remaining args 4124 * @param mgr response data 4125 */ 4126 static void 4127 download_cb (char *const *args, 4128 const struct TALER_EXCHANGE_GetManagementKeysResponse *mgr) 4129 { 4130 const struct TALER_EXCHANGE_HttpResponse *hr = &mgr->hr; 4131 4132 mgkh = NULL; 4133 switch (hr->http_status) 4134 { 4135 case MHD_HTTP_OK: 4136 break; 4137 default: 4138 if (0 != hr->http_status) 4139 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 4140 "Failed to download keys from `%s': %s (HTTP status: %u/%u)\n", 4141 CFG_exchange_url, 4142 hr->hint, 4143 hr->http_status, 4144 (unsigned int) hr->ec); 4145 else 4146 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 4147 "Failed to download keys from `%s' (no HTTP response)\n", 4148 CFG_exchange_url); 4149 GNUNET_SCHEDULER_shutdown (); 4150 global_ret = EXIT_FAILURE; 4151 return; 4152 } 4153 in = GNUNET_JSON_PACK ( 4154 GNUNET_JSON_pack_string ("operation", 4155 OP_INPUT_KEYS), 4156 GNUNET_JSON_pack_object_incref ("arguments", 4157 (json_t *) hr->reply)); 4158 if (NULL == args[0]) 4159 { 4160 json_dumpf (in, 4161 stdout, 4162 JSON_INDENT (2)); 4163 json_decref (in); 4164 in = NULL; 4165 } 4166 next (args); 4167 } 4168 4169 4170 /** 4171 * Download future keys. 4172 * 4173 * @param args the array of command-line arguments to process next 4174 */ 4175 static void 4176 do_download (char *const *args) 4177 { 4178 if ( (NULL == CFG_exchange_url) && 4179 (GNUNET_OK != 4180 GNUNET_CONFIGURATION_get_value_string (kcfg, 4181 "exchange", 4182 "BASE_URL", 4183 &CFG_exchange_url)) ) 4184 { 4185 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, 4186 "exchange", 4187 "BASE_URL"); 4188 GNUNET_SCHEDULER_shutdown (); 4189 global_ret = EXIT_NOTCONFIGURED; 4190 return; 4191 } 4192 mgkh = TALER_EXCHANGE_get_management_keys_create (ctx, 4193 CFG_exchange_url); 4194 TALER_EXCHANGE_get_management_keys_start (mgkh, 4195 &download_cb, 4196 (void *) args); 4197 } 4198 4199 4200 /** 4201 * Check that the security module keys are the same as before. If we had no 4202 * keys in store before, remember them (Trust On First Use). 4203 * 4204 * @param secmset security module keys 4205 * @return #GNUNET_OK if keys match with what we have in store 4206 * #GNUNET_NO if we had nothing in store but now do 4207 * #GNUNET_SYSERR if keys changed from what we remember or other error 4208 */ 4209 static enum GNUNET_GenericReturnValue 4210 tofu_check (const struct TALER_SecurityModulePublicKeySetP *secmset) 4211 { 4212 char *fn; 4213 struct TALER_SecurityModulePublicKeySetP oldset; 4214 ssize_t ret; 4215 4216 if (GNUNET_OK != 4217 GNUNET_CONFIGURATION_get_value_filename (kcfg, 4218 "exchange-offline", 4219 "SECM_TOFU_FILE", 4220 &fn)) 4221 { 4222 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, 4223 "exchange-offline", 4224 "SECM_TOFU_FILE"); 4225 return GNUNET_SYSERR; 4226 } 4227 if (GNUNET_OK == 4228 GNUNET_DISK_file_test (fn)) 4229 { 4230 ret = GNUNET_DISK_fn_read (fn, 4231 &oldset, 4232 sizeof (oldset)); 4233 if (GNUNET_SYSERR != ret) 4234 { 4235 if (ret != sizeof (oldset)) 4236 { 4237 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 4238 "File `%s' corrupt\n", 4239 fn); 4240 GNUNET_free (fn); 4241 return GNUNET_SYSERR; 4242 } 4243 /* TOFU check */ 4244 if (0 != memcmp (&oldset, 4245 secmset, 4246 sizeof (*secmset))) 4247 { 4248 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 4249 "Fatal: security module keys changed (file `%s')!\n", 4250 fn); 4251 GNUNET_free (fn); 4252 return GNUNET_SYSERR; 4253 } 4254 GNUNET_free (fn); 4255 return GNUNET_OK; 4256 } 4257 } 4258 4259 { 4260 char *key; 4261 4262 /* check against SECMOD-keys pinned in configuration */ 4263 if (GNUNET_OK == 4264 GNUNET_CONFIGURATION_get_value_string (kcfg, 4265 "exchange-offline", 4266 "SECM_ESIGN_PUBKEY", 4267 &key)) 4268 { 4269 struct TALER_SecurityModulePublicKeyP k; 4270 4271 if (GNUNET_OK != 4272 GNUNET_STRINGS_string_to_data (key, 4273 strlen (key), 4274 &k, 4275 sizeof (k))) 4276 { 4277 GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, 4278 "exchange-offline", 4279 "SECM_ESIGN_PUBKEY", 4280 "key malformed"); 4281 GNUNET_free (key); 4282 GNUNET_free (fn); 4283 return GNUNET_SYSERR; 4284 } 4285 GNUNET_free (key); 4286 if (0 != 4287 GNUNET_memcmp (&k, 4288 &secmset->eddsa)) 4289 { 4290 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 4291 "ESIGN security module key does not match SECM_ESIGN_PUBKEY in configuration\n"); 4292 GNUNET_free (fn); 4293 return GNUNET_SYSERR; 4294 } 4295 } 4296 if (GNUNET_OK == 4297 GNUNET_CONFIGURATION_get_value_string (kcfg, 4298 "exchange-offline", 4299 "SECM_DENOM_PUBKEY", 4300 &key)) 4301 { 4302 struct TALER_SecurityModulePublicKeyP k; 4303 4304 if (GNUNET_OK != 4305 GNUNET_STRINGS_string_to_data (key, 4306 strlen (key), 4307 &k, 4308 sizeof (k))) 4309 { 4310 GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, 4311 "exchange-offline", 4312 "SECM_DENOM_PUBKEY", 4313 "key malformed"); 4314 GNUNET_free (key); 4315 GNUNET_free (fn); 4316 return GNUNET_SYSERR; 4317 } 4318 GNUNET_free (key); 4319 if (0 != 4320 GNUNET_memcmp (&k, 4321 &secmset->rsa)) 4322 { 4323 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 4324 "DENOM security module key does not match SECM_DENOM_PUBKEY in configuration\n"); 4325 GNUNET_free (fn); 4326 return GNUNET_SYSERR; 4327 } 4328 } 4329 if (GNUNET_OK == 4330 GNUNET_CONFIGURATION_get_value_string (kcfg, 4331 "exchange-offline", 4332 "SECM_DENOM_CS_PUBKEY", 4333 &key)) 4334 { 4335 struct TALER_SecurityModulePublicKeyP k; 4336 4337 if (GNUNET_OK != 4338 GNUNET_STRINGS_string_to_data (key, 4339 strlen (key), 4340 &k, 4341 sizeof (k))) 4342 { 4343 GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, 4344 "exchange-offline", 4345 "SECM_DENOM_CS_PUBKEY", 4346 "key malformed"); 4347 GNUNET_free (key); 4348 GNUNET_free (fn); 4349 return GNUNET_SYSERR; 4350 } 4351 GNUNET_free (key); 4352 if (0 != 4353 GNUNET_memcmp (&k, 4354 &secmset->cs)) 4355 { 4356 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 4357 "DENOM security module key does not match SECM_DENOM_CS_PUBKEY in configuration\n"); 4358 GNUNET_free (fn); 4359 return GNUNET_SYSERR; 4360 } 4361 } 4362 } 4363 if (GNUNET_OK != 4364 GNUNET_DISK_directory_create_for_file (fn)) 4365 { 4366 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 4367 "Failed create directory to store key material in file `%s'\n", 4368 fn); 4369 GNUNET_free (fn); 4370 return GNUNET_SYSERR; 4371 } 4372 /* persist keys for future runs */ 4373 if (GNUNET_OK != 4374 GNUNET_DISK_fn_write (fn, 4375 secmset, 4376 sizeof (oldset), 4377 GNUNET_DISK_PERM_USER_READ)) 4378 { 4379 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 4380 "Failed to store key material in file `%s'\n", 4381 fn); 4382 GNUNET_free (fn); 4383 return GNUNET_SYSERR; 4384 } 4385 GNUNET_free (fn); 4386 return GNUNET_NO; 4387 } 4388 4389 4390 /** 4391 * Output @a signkeys for human consumption. 4392 * 4393 * @param secm_pub security module public key used to sign the denominations 4394 * @param signkeys keys to output 4395 * @return #GNUNET_OK on success 4396 */ 4397 static enum GNUNET_GenericReturnValue 4398 show_signkeys (const struct TALER_SecurityModulePublicKeyP *secm_pub, 4399 const json_t *signkeys) 4400 { 4401 size_t index; 4402 json_t *value; 4403 4404 json_array_foreach (signkeys, index, value) { 4405 const char *err_name; 4406 unsigned int err_line; 4407 struct TALER_ExchangePublicKeyP exchange_pub; 4408 struct TALER_SecurityModuleSignatureP secm_sig; 4409 struct GNUNET_TIME_Timestamp start_time; 4410 struct GNUNET_TIME_Timestamp sign_end; 4411 struct GNUNET_TIME_Timestamp legal_end; 4412 struct GNUNET_TIME_Relative duration; 4413 struct GNUNET_JSON_Specification spec[] = { 4414 GNUNET_JSON_spec_timestamp ("stamp_start", 4415 &start_time), 4416 GNUNET_JSON_spec_timestamp ("stamp_expire", 4417 &sign_end), 4418 GNUNET_JSON_spec_timestamp ("stamp_end", 4419 &legal_end), 4420 GNUNET_JSON_spec_fixed_auto ("key", 4421 &exchange_pub), 4422 GNUNET_JSON_spec_fixed_auto ("signkey_secmod_sig", 4423 &secm_sig), 4424 GNUNET_JSON_spec_end () 4425 }; 4426 4427 if (GNUNET_OK != 4428 GNUNET_JSON_parse (value, 4429 spec, 4430 &err_name, 4431 &err_line)) 4432 { 4433 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 4434 "Invalid input for signing key to 'show': %s#%u at %u (skipping)\n", 4435 err_name, 4436 err_line, 4437 (unsigned int) index); 4438 json_dumpf (value, 4439 stderr, 4440 JSON_INDENT (2)); 4441 global_ret = EXIT_FAILURE; 4442 GNUNET_SCHEDULER_shutdown (); 4443 return GNUNET_SYSERR; 4444 } 4445 duration = GNUNET_TIME_absolute_get_difference (start_time.abs_time, 4446 sign_end.abs_time); 4447 if (GNUNET_OK != 4448 TALER_exchange_secmod_eddsa_verify (&exchange_pub, 4449 start_time, 4450 duration, 4451 secm_pub, 4452 &secm_sig)) 4453 { 4454 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 4455 "Invalid security module signature for signing key %s (aborting)\n", 4456 TALER_B2S (&exchange_pub)); 4457 global_ret = EXIT_FAILURE; 4458 GNUNET_SCHEDULER_shutdown (); 4459 return GNUNET_SYSERR; 4460 } 4461 { 4462 char *legal_end_s; 4463 4464 legal_end_s = GNUNET_strdup ( 4465 GNUNET_TIME_timestamp2s (legal_end)); 4466 printf ("EXCHANGE-KEY %s starting at %s (used for: %s, legal end: %s)\n", 4467 TALER_B2S (&exchange_pub), 4468 GNUNET_TIME_timestamp2s (start_time), 4469 GNUNET_TIME_relative2s (duration, 4470 false), 4471 legal_end_s); 4472 GNUNET_free (legal_end_s); 4473 } 4474 } 4475 return GNUNET_OK; 4476 } 4477 4478 4479 /** 4480 * Output @a denomkeys for human consumption. 4481 * 4482 * @param secm_pub_rsa security module public key used to sign the RSA denominations 4483 * @param secm_pub_cs security module public key used to sign the CS denominations 4484 * @param denomkeys keys to output 4485 * @return #GNUNET_OK on success 4486 */ 4487 static enum GNUNET_GenericReturnValue 4488 show_denomkeys (const struct TALER_SecurityModulePublicKeyP *secm_pub_rsa, 4489 const struct TALER_SecurityModulePublicKeyP *secm_pub_cs, 4490 const json_t *denomkeys) 4491 { 4492 size_t index; 4493 json_t *value; 4494 4495 json_array_foreach (denomkeys, index, value) { 4496 const char *err_name; 4497 unsigned int err_line; 4498 const char *section_name; 4499 struct TALER_DenominationPublicKey denom_pub; 4500 struct GNUNET_TIME_Timestamp stamp_start; 4501 struct GNUNET_TIME_Timestamp stamp_expire_withdraw; 4502 struct GNUNET_TIME_Timestamp stamp_expire_deposit; 4503 struct GNUNET_TIME_Timestamp stamp_expire_legal; 4504 struct TALER_Amount coin_value; 4505 struct TALER_DenomFeeSet fees; 4506 struct TALER_SecurityModuleSignatureP secm_sig; 4507 struct GNUNET_JSON_Specification spec[] = { 4508 GNUNET_JSON_spec_string ("section_name", 4509 §ion_name), 4510 TALER_JSON_spec_denom_pub ("denom_pub", 4511 &denom_pub), 4512 TALER_JSON_spec_amount ("value", 4513 currency, 4514 &coin_value), 4515 TALER_JSON_SPEC_DENOM_FEES ("fee", 4516 currency, 4517 &fees), 4518 GNUNET_JSON_spec_timestamp ("stamp_start", 4519 &stamp_start), 4520 GNUNET_JSON_spec_timestamp ("stamp_expire_withdraw", 4521 &stamp_expire_withdraw), 4522 GNUNET_JSON_spec_timestamp ("stamp_expire_deposit", 4523 &stamp_expire_deposit), 4524 GNUNET_JSON_spec_timestamp ("stamp_expire_legal", 4525 &stamp_expire_legal), 4526 GNUNET_JSON_spec_fixed_auto ("denom_secmod_sig", 4527 &secm_sig), 4528 GNUNET_JSON_spec_end () 4529 }; 4530 struct GNUNET_TIME_Relative duration; 4531 struct TALER_DenominationHashP h_denom_pub; 4532 enum GNUNET_GenericReturnValue ok; 4533 4534 if (GNUNET_OK != 4535 GNUNET_JSON_parse (value, 4536 spec, 4537 &err_name, 4538 &err_line)) 4539 { 4540 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 4541 "Invalid input for denomination key to 'show': %s#%u at %u (skipping)\n", 4542 err_name, 4543 err_line, 4544 (unsigned int) index); 4545 json_dumpf (value, 4546 stderr, 4547 JSON_INDENT (2)); 4548 GNUNET_JSON_parse_free (spec); 4549 global_ret = EXIT_FAILURE; 4550 GNUNET_SCHEDULER_shutdown (); 4551 return GNUNET_SYSERR; 4552 } 4553 duration = GNUNET_TIME_absolute_get_difference ( 4554 stamp_start.abs_time, 4555 stamp_expire_withdraw.abs_time); 4556 TALER_denom_pub_hash (&denom_pub, 4557 &h_denom_pub); 4558 switch (denom_pub.bsign_pub_key->cipher) 4559 { 4560 case GNUNET_CRYPTO_BSA_RSA: 4561 { 4562 struct TALER_RsaPubHashP h_rsa; 4563 4564 TALER_rsa_pub_hash (denom_pub.bsign_pub_key->details.rsa_public_key, 4565 &h_rsa); 4566 ok = TALER_exchange_secmod_rsa_verify (&h_rsa, 4567 section_name, 4568 stamp_start, 4569 duration, 4570 secm_pub_rsa, 4571 &secm_sig); 4572 } 4573 break; 4574 case GNUNET_CRYPTO_BSA_CS: 4575 { 4576 struct TALER_CsPubHashP h_cs; 4577 4578 TALER_cs_pub_hash (&denom_pub.bsign_pub_key->details.cs_public_key, 4579 &h_cs); 4580 ok = TALER_exchange_secmod_cs_verify (&h_cs, 4581 section_name, 4582 stamp_start, 4583 duration, 4584 secm_pub_cs, 4585 &secm_sig); 4586 } 4587 break; 4588 default: 4589 GNUNET_break (0); 4590 ok = GNUNET_SYSERR; 4591 break; 4592 } 4593 if (GNUNET_OK != ok) 4594 { 4595 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 4596 "Invalid security module signature for denomination key %s (aborting)\n", 4597 GNUNET_h2s (&h_denom_pub.hash)); 4598 global_ret = EXIT_FAILURE; 4599 GNUNET_SCHEDULER_shutdown (); 4600 return GNUNET_SYSERR; 4601 } 4602 4603 { 4604 char *withdraw_fee_s; 4605 char *deposit_fee_s; 4606 char *refresh_fee_s; 4607 char *refund_fee_s; 4608 char *deposit_s; 4609 char *legal_s; 4610 4611 withdraw_fee_s = TALER_amount_to_string (&fees.withdraw); 4612 deposit_fee_s = TALER_amount_to_string (&fees.deposit); 4613 refresh_fee_s = TALER_amount_to_string (&fees.refresh); 4614 refund_fee_s = TALER_amount_to_string (&fees.refund); 4615 deposit_s = GNUNET_strdup ( 4616 GNUNET_TIME_timestamp2s (stamp_expire_deposit)); 4617 legal_s = GNUNET_strdup ( 4618 GNUNET_TIME_timestamp2s (stamp_expire_legal)); 4619 4620 printf ( 4621 "DENOMINATION-KEY(%s) %s of value %s starting at %s " 4622 "(used for: %s, deposit until: %s legal end: %s) with fees %s/%s/%s/%s\n", 4623 section_name, 4624 TALER_B2S (&h_denom_pub), 4625 TALER_amount2s (&coin_value), 4626 GNUNET_TIME_timestamp2s (stamp_start), 4627 GNUNET_TIME_relative2s (duration, 4628 false), 4629 deposit_s, 4630 legal_s, 4631 withdraw_fee_s, 4632 deposit_fee_s, 4633 refresh_fee_s, 4634 refund_fee_s); 4635 GNUNET_free (withdraw_fee_s); 4636 GNUNET_free (deposit_fee_s); 4637 GNUNET_free (refresh_fee_s); 4638 GNUNET_free (refund_fee_s); 4639 GNUNET_free (deposit_s); 4640 GNUNET_free (legal_s); 4641 } 4642 4643 GNUNET_JSON_parse_free (spec); 4644 } 4645 return GNUNET_OK; 4646 } 4647 4648 4649 /** 4650 * Parse the input of exchange keys for the 'show' and 'sign' commands. 4651 * 4652 * @param command_name name of the command, for logging 4653 * @return NULL on error, otherwise the keys details to be free'd by caller 4654 */ 4655 static json_t * 4656 parse_keys_input (const char *command_name) 4657 { 4658 const char *op_str; 4659 json_t *keys; 4660 struct GNUNET_JSON_Specification spec[] = { 4661 GNUNET_JSON_spec_json ("arguments", 4662 &keys), 4663 GNUNET_JSON_spec_string ("operation", 4664 &op_str), 4665 GNUNET_JSON_spec_end () 4666 }; 4667 const char *err_name; 4668 unsigned int err_line; 4669 4670 if (NULL == in) 4671 { 4672 json_error_t err; 4673 4674 in = json_loadf (stdin, 4675 JSON_REJECT_DUPLICATES, 4676 &err); 4677 if (NULL == in) 4678 { 4679 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 4680 "Failed to read JSON input: %s at %d:%s (offset: %d)\n", 4681 err.text, 4682 err.line, 4683 err.source, 4684 err.position); 4685 global_ret = EXIT_FAILURE; 4686 GNUNET_SCHEDULER_shutdown (); 4687 return NULL; 4688 } 4689 } 4690 if (GNUNET_OK != 4691 GNUNET_JSON_parse (in, 4692 spec, 4693 &err_name, 4694 &err_line)) 4695 { 4696 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 4697 "Invalid input to '%s': %s#%u (skipping)\n", 4698 command_name, 4699 err_name, 4700 err_line); 4701 json_dumpf (in, 4702 stderr, 4703 JSON_INDENT (2)); 4704 global_ret = EXIT_FAILURE; 4705 GNUNET_SCHEDULER_shutdown (); 4706 return NULL; 4707 } 4708 if (0 != strcmp (op_str, 4709 OP_INPUT_KEYS)) 4710 { 4711 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 4712 "Invalid input to '%s' : operation is `%s', expected `%s'\n", 4713 command_name, 4714 op_str, 4715 OP_INPUT_KEYS); 4716 GNUNET_JSON_parse_free (spec); 4717 return NULL; 4718 } 4719 json_decref (in); 4720 in = NULL; 4721 return keys; 4722 } 4723 4724 4725 /** 4726 * Show future keys. 4727 * 4728 * @param args the array of command-line arguments to process next 4729 */ 4730 static void 4731 do_show (char *const *args) 4732 { 4733 json_t *keys; 4734 const char *err_name; 4735 unsigned int err_line; 4736 const json_t *denomkeys; 4737 const json_t *signkeys; 4738 struct TALER_MasterPublicKeyP mpub; 4739 struct TALER_SecurityModulePublicKeySetP secmset; 4740 struct GNUNET_JSON_Specification spec[] = { 4741 GNUNET_JSON_spec_array_const ("future_denoms", 4742 &denomkeys), 4743 GNUNET_JSON_spec_array_const ("future_signkeys", 4744 &signkeys), 4745 GNUNET_JSON_spec_fixed_auto ("master_pub", 4746 &mpub), 4747 GNUNET_JSON_spec_fixed_auto ("denom_secmod_public_key", 4748 &secmset.rsa), 4749 GNUNET_JSON_spec_fixed_auto ("denom_secmod_cs_public_key", 4750 &secmset.cs), 4751 GNUNET_JSON_spec_fixed_auto ("signkey_secmod_public_key", 4752 &secmset.eddsa), 4753 GNUNET_JSON_spec_end () 4754 }; 4755 4756 keys = parse_keys_input ("show"); 4757 if (NULL == keys) 4758 return; 4759 4760 if (GNUNET_OK != 4761 load_offline_key (GNUNET_NO)) 4762 return; 4763 if (GNUNET_OK != 4764 GNUNET_JSON_parse (keys, 4765 spec, 4766 &err_name, 4767 &err_line)) 4768 { 4769 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 4770 "Invalid input to 'show': %s #%u (skipping)\n", 4771 err_name, 4772 err_line); 4773 json_dumpf (in, 4774 stderr, 4775 JSON_INDENT (2)); 4776 global_ret = EXIT_FAILURE; 4777 GNUNET_SCHEDULER_shutdown (); 4778 json_decref (keys); 4779 return; 4780 } 4781 if (0 != 4782 GNUNET_memcmp (&master_pub, 4783 &mpub)) 4784 { 4785 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 4786 "Fatal: exchange uses different master key!\n"); 4787 global_ret = EXIT_FAILURE; 4788 GNUNET_SCHEDULER_shutdown (); 4789 json_decref (keys); 4790 return; 4791 } 4792 if (GNUNET_SYSERR == 4793 tofu_check (&secmset)) 4794 { 4795 global_ret = EXIT_FAILURE; 4796 GNUNET_SCHEDULER_shutdown (); 4797 json_decref (keys); 4798 return; 4799 } 4800 if ( (GNUNET_OK != 4801 show_signkeys (&secmset.eddsa, 4802 signkeys)) || 4803 (GNUNET_OK != 4804 show_denomkeys (&secmset.rsa, 4805 &secmset.cs, 4806 denomkeys)) ) 4807 { 4808 global_ret = EXIT_FAILURE; 4809 GNUNET_SCHEDULER_shutdown (); 4810 json_decref (keys); 4811 return; 4812 } 4813 json_decref (keys); 4814 next (args); 4815 } 4816 4817 4818 /** 4819 * Sign @a signkeys with offline key. 4820 * 4821 * @param secm_pub security module public key used to sign the denominations 4822 * @param signkeys keys to output 4823 * @param[in,out] result array where to output the signatures 4824 * @return #GNUNET_OK on success 4825 */ 4826 static enum GNUNET_GenericReturnValue 4827 sign_signkeys (const struct TALER_SecurityModulePublicKeyP *secm_pub, 4828 const json_t *signkeys, 4829 json_t *result) 4830 { 4831 size_t index; 4832 json_t *value; 4833 4834 json_array_foreach (signkeys, index, value) { 4835 const char *err_name; 4836 unsigned int err_line; 4837 struct TALER_ExchangePublicKeyP exchange_pub; 4838 struct TALER_SecurityModuleSignatureP secm_sig; 4839 struct GNUNET_TIME_Timestamp start_time; 4840 struct GNUNET_TIME_Timestamp sign_end; 4841 struct GNUNET_TIME_Timestamp legal_end; 4842 struct GNUNET_TIME_Relative duration; 4843 struct GNUNET_JSON_Specification spec[] = { 4844 GNUNET_JSON_spec_timestamp ("stamp_start", 4845 &start_time), 4846 GNUNET_JSON_spec_timestamp ("stamp_expire", 4847 &sign_end), 4848 GNUNET_JSON_spec_timestamp ("stamp_end", 4849 &legal_end), 4850 GNUNET_JSON_spec_fixed_auto ("key", 4851 &exchange_pub), 4852 GNUNET_JSON_spec_fixed_auto ("signkey_secmod_sig", 4853 &secm_sig), 4854 GNUNET_JSON_spec_end () 4855 }; 4856 4857 if (GNUNET_OK != 4858 GNUNET_JSON_parse (value, 4859 spec, 4860 &err_name, 4861 &err_line)) 4862 { 4863 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 4864 "Invalid input for signing key to 'show': %s #%u at %u (skipping)\n", 4865 err_name, 4866 err_line, 4867 (unsigned int) index); 4868 json_dumpf (value, 4869 stderr, 4870 JSON_INDENT (2)); 4871 global_ret = EXIT_FAILURE; 4872 GNUNET_SCHEDULER_shutdown (); 4873 return GNUNET_SYSERR; 4874 } 4875 4876 duration = GNUNET_TIME_absolute_get_difference (start_time.abs_time, 4877 sign_end.abs_time); 4878 if (GNUNET_OK != 4879 TALER_exchange_secmod_eddsa_verify (&exchange_pub, 4880 start_time, 4881 duration, 4882 secm_pub, 4883 &secm_sig)) 4884 { 4885 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 4886 "Invalid security module signature for signing key %s (aborting)\n", 4887 TALER_B2S (&exchange_pub)); 4888 global_ret = EXIT_FAILURE; 4889 GNUNET_SCHEDULER_shutdown (); 4890 GNUNET_JSON_parse_free (spec); 4891 return GNUNET_SYSERR; 4892 } 4893 { 4894 struct TALER_MasterSignatureP master_sig; 4895 4896 TALER_exchange_offline_signkey_validity_sign (&exchange_pub, 4897 start_time, 4898 sign_end, 4899 legal_end, 4900 &master_priv, 4901 &master_sig); 4902 GNUNET_assert (0 == 4903 json_array_append_new ( 4904 result, 4905 GNUNET_JSON_PACK ( 4906 GNUNET_JSON_pack_data_auto ("exchange_pub", 4907 &exchange_pub), 4908 GNUNET_JSON_pack_data_auto ("master_sig", 4909 &master_sig)))); 4910 } 4911 GNUNET_JSON_parse_free (spec); 4912 } 4913 return GNUNET_OK; 4914 } 4915 4916 4917 /** 4918 * Looks up the AGE_RESTRICTED setting for a denomination in the config and 4919 * returns the age restriction (mask) accordingly. 4920 * 4921 * @param section_name Section in the configuration for the particular 4922 * denomination. 4923 */ 4924 static struct TALER_AgeMask 4925 load_age_mask (const char*section_name) 4926 { 4927 static const struct TALER_AgeMask null_mask = {0}; 4928 enum GNUNET_GenericReturnValue ret; 4929 4930 if (! ar_enabled) 4931 return null_mask; 4932 4933 if (GNUNET_OK != (GNUNET_CONFIGURATION_have_value ( 4934 kcfg, 4935 section_name, 4936 "AGE_RESTRICTED"))) 4937 return null_mask; 4938 4939 ret = GNUNET_CONFIGURATION_get_value_yesno (kcfg, 4940 section_name, 4941 "AGE_RESTRICTED"); 4942 if (GNUNET_SYSERR == ret) 4943 GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, 4944 section_name, 4945 "AGE_RESTRICTED", 4946 "Value must be YES or NO\n"); 4947 if (GNUNET_YES == ret) 4948 return ar_config.mask; 4949 4950 return null_mask; 4951 } 4952 4953 4954 /** 4955 * Sign @a denomkeys with offline key. 4956 * 4957 * @param secm_pub_rsa security module public key used to sign the RSA denominations 4958 * @param secm_pub_cs security module public key used to sign the CS denominations 4959 * @param denomkeys keys to output 4960 * @param[in,out] result array where to output the signatures 4961 * @return #GNUNET_OK on success 4962 */ 4963 static enum GNUNET_GenericReturnValue 4964 sign_denomkeys (const struct TALER_SecurityModulePublicKeyP *secm_pub_rsa, 4965 const struct TALER_SecurityModulePublicKeyP *secm_pub_cs, 4966 const json_t *denomkeys, 4967 json_t *result) 4968 { 4969 size_t index; 4970 json_t *value; 4971 4972 json_array_foreach (denomkeys, index, value) { 4973 const char *err_name; 4974 unsigned int err_line; 4975 const char *section_name; 4976 struct TALER_DenominationPublicKey denom_pub; 4977 struct GNUNET_TIME_Timestamp stamp_start; 4978 struct GNUNET_TIME_Timestamp stamp_expire_withdraw; 4979 struct GNUNET_TIME_Timestamp stamp_expire_deposit; 4980 struct GNUNET_TIME_Timestamp stamp_expire_legal; 4981 struct TALER_Amount coin_value; 4982 struct TALER_DenomFeeSet fees; 4983 struct TALER_SecurityModuleSignatureP secm_sig; 4984 struct GNUNET_JSON_Specification spec[] = { 4985 GNUNET_JSON_spec_string ("section_name", 4986 §ion_name), 4987 TALER_JSON_spec_denom_pub ("denom_pub", 4988 &denom_pub), 4989 TALER_JSON_spec_amount ("value", 4990 currency, 4991 &coin_value), 4992 TALER_JSON_SPEC_DENOM_FEES ("fee", 4993 currency, 4994 &fees), 4995 GNUNET_JSON_spec_timestamp ("stamp_start", 4996 &stamp_start), 4997 GNUNET_JSON_spec_timestamp ("stamp_expire_withdraw", 4998 &stamp_expire_withdraw), 4999 GNUNET_JSON_spec_timestamp ("stamp_expire_deposit", 5000 &stamp_expire_deposit), 5001 GNUNET_JSON_spec_timestamp ("stamp_expire_legal", 5002 &stamp_expire_legal), 5003 GNUNET_JSON_spec_fixed_auto ("denom_secmod_sig", 5004 &secm_sig), 5005 GNUNET_JSON_spec_end () 5006 }; 5007 struct GNUNET_TIME_Relative duration; 5008 struct TALER_DenominationHashP h_denom_pub; 5009 5010 if (GNUNET_OK != 5011 GNUNET_JSON_parse (value, 5012 spec, 5013 &err_name, 5014 &err_line)) 5015 { 5016 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 5017 "Invalid input for denomination key to 'sign': %s #%u at %u (skipping)\n", 5018 err_name, 5019 err_line, 5020 (unsigned int) index); 5021 json_dumpf (value, 5022 stderr, 5023 JSON_INDENT (2)); 5024 GNUNET_JSON_parse_free (spec); 5025 global_ret = EXIT_FAILURE; 5026 GNUNET_SCHEDULER_shutdown (); 5027 return GNUNET_SYSERR; 5028 } 5029 duration = GNUNET_TIME_absolute_get_difference ( 5030 stamp_start.abs_time, 5031 stamp_expire_withdraw.abs_time); 5032 5033 /* Load the age mask, if applicable to this denomination */ 5034 denom_pub.age_mask = load_age_mask (section_name); 5035 5036 TALER_denom_pub_hash (&denom_pub, 5037 &h_denom_pub); 5038 5039 switch (denom_pub.bsign_pub_key->cipher) 5040 { 5041 case GNUNET_CRYPTO_BSA_RSA: 5042 { 5043 struct TALER_RsaPubHashP h_rsa; 5044 5045 TALER_rsa_pub_hash (denom_pub.bsign_pub_key->details.rsa_public_key, 5046 &h_rsa); 5047 if (GNUNET_OK != 5048 TALER_exchange_secmod_rsa_verify (&h_rsa, 5049 section_name, 5050 stamp_start, 5051 duration, 5052 secm_pub_rsa, 5053 &secm_sig)) 5054 { 5055 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 5056 "Invalid security module signature for denomination key %s (aborting)\n", 5057 GNUNET_h2s (&h_denom_pub.hash)); 5058 global_ret = EXIT_FAILURE; 5059 GNUNET_SCHEDULER_shutdown (); 5060 GNUNET_JSON_parse_free (spec); 5061 return GNUNET_SYSERR; 5062 } 5063 } 5064 break; 5065 case GNUNET_CRYPTO_BSA_CS: 5066 { 5067 struct TALER_CsPubHashP h_cs; 5068 5069 TALER_cs_pub_hash (&denom_pub.bsign_pub_key->details.cs_public_key, 5070 &h_cs); 5071 if (GNUNET_OK != 5072 TALER_exchange_secmod_cs_verify (&h_cs, 5073 section_name, 5074 stamp_start, 5075 duration, 5076 secm_pub_cs, 5077 &secm_sig)) 5078 { 5079 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 5080 "Invalid security module signature for denomination key %s (aborting)\n", 5081 GNUNET_h2s (&h_denom_pub.hash)); 5082 global_ret = EXIT_FAILURE; 5083 GNUNET_SCHEDULER_shutdown (); 5084 GNUNET_JSON_parse_free (spec); 5085 return GNUNET_SYSERR; 5086 } 5087 } 5088 break; 5089 default: 5090 global_ret = EXIT_FAILURE; 5091 GNUNET_SCHEDULER_shutdown (); 5092 GNUNET_JSON_parse_free (spec); 5093 return GNUNET_SYSERR; 5094 } 5095 5096 { 5097 struct TALER_MasterSignatureP master_sig; 5098 5099 TALER_exchange_offline_denom_validity_sign (&h_denom_pub, 5100 stamp_start, 5101 stamp_expire_withdraw, 5102 stamp_expire_deposit, 5103 stamp_expire_legal, 5104 &coin_value, 5105 &fees, 5106 &master_priv, 5107 &master_sig); 5108 GNUNET_assert (0 == 5109 json_array_append_new ( 5110 result, 5111 GNUNET_JSON_PACK ( 5112 GNUNET_JSON_pack_data_auto ("h_denom_pub", 5113 &h_denom_pub), 5114 GNUNET_JSON_pack_data_auto ("master_sig", 5115 &master_sig)))); 5116 } 5117 GNUNET_JSON_parse_free (spec); 5118 } 5119 return GNUNET_OK; 5120 } 5121 5122 5123 /** 5124 * Sign future keys. 5125 * 5126 * @param args the array of command-line arguments to process next 5127 */ 5128 static void 5129 do_sign (char *const *args) 5130 { 5131 json_t *keys; 5132 const char *err_name; 5133 unsigned int err_line; 5134 const json_t *denomkeys; 5135 const json_t *signkeys; 5136 struct TALER_MasterPublicKeyP mpub; 5137 struct TALER_SecurityModulePublicKeySetP secmset; 5138 struct GNUNET_JSON_Specification spec[] = { 5139 GNUNET_JSON_spec_array_const ("future_denoms", 5140 &denomkeys), 5141 GNUNET_JSON_spec_array_const ("future_signkeys", 5142 &signkeys), 5143 GNUNET_JSON_spec_fixed_auto ("master_pub", 5144 &mpub), 5145 GNUNET_JSON_spec_fixed_auto ("denom_secmod_public_key", 5146 &secmset.rsa), 5147 GNUNET_JSON_spec_fixed_auto ("denom_secmod_cs_public_key", 5148 &secmset.cs), 5149 GNUNET_JSON_spec_fixed_auto ("signkey_secmod_public_key", 5150 &secmset.eddsa), 5151 GNUNET_JSON_spec_end () 5152 }; 5153 5154 keys = parse_keys_input ("sign"); 5155 if (NULL == keys) 5156 return; 5157 if (GNUNET_OK != 5158 load_offline_key (GNUNET_NO)) 5159 { 5160 json_decref (keys); 5161 return; 5162 } 5163 if (GNUNET_OK != 5164 GNUNET_JSON_parse (keys, 5165 spec, 5166 &err_name, 5167 &err_line)) 5168 { 5169 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 5170 "Invalid input to 'sign' : %s #%u (skipping)\n", 5171 err_name, 5172 err_line); 5173 json_dumpf (in, 5174 stderr, 5175 JSON_INDENT (2)); 5176 global_ret = EXIT_FAILURE; 5177 GNUNET_SCHEDULER_shutdown (); 5178 json_decref (keys); 5179 return; 5180 } 5181 if (0 != 5182 GNUNET_memcmp (&master_pub, 5183 &mpub)) 5184 { 5185 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 5186 "Fatal: exchange uses different master key!\n"); 5187 global_ret = EXIT_FAILURE; 5188 GNUNET_SCHEDULER_shutdown (); 5189 json_decref (keys); 5190 return; 5191 } 5192 if (GNUNET_SYSERR == 5193 tofu_check (&secmset)) 5194 { 5195 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 5196 "Fatal: security module keys changed!\n"); 5197 global_ret = EXIT_FAILURE; 5198 GNUNET_SCHEDULER_shutdown (); 5199 json_decref (keys); 5200 return; 5201 } 5202 { 5203 json_t *signkey_sig_array = json_array (); 5204 json_t *denomkey_sig_array = json_array (); 5205 5206 GNUNET_assert (NULL != signkey_sig_array); 5207 GNUNET_assert (NULL != denomkey_sig_array); 5208 if ( (GNUNET_OK != 5209 sign_signkeys (&secmset.eddsa, 5210 signkeys, 5211 signkey_sig_array)) || 5212 (GNUNET_OK != 5213 sign_denomkeys (&secmset.rsa, 5214 &secmset.cs, 5215 denomkeys, 5216 denomkey_sig_array)) ) 5217 { 5218 global_ret = EXIT_FAILURE; 5219 GNUNET_SCHEDULER_shutdown (); 5220 json_decref (signkey_sig_array); 5221 json_decref (denomkey_sig_array); 5222 json_decref (keys); 5223 return; 5224 } 5225 5226 output_operation (OP_UPLOAD_SIGS, 5227 GNUNET_JSON_PACK ( 5228 GNUNET_JSON_pack_array_steal ("denom_sigs", 5229 denomkey_sig_array), 5230 GNUNET_JSON_pack_array_steal ("signkey_sigs", 5231 signkey_sig_array))); 5232 } 5233 json_decref (keys); 5234 next (args); 5235 } 5236 5237 5238 /** 5239 * Setup and output offline signing key. 5240 * 5241 * @param args the array of command-line arguments to process next 5242 */ 5243 static void 5244 do_setup (char *const *args) 5245 { 5246 if (GNUNET_OK != 5247 load_offline_key (GNUNET_YES)) 5248 { 5249 global_ret = EXIT_NOPERMISSION; 5250 return; 5251 } 5252 if (NULL != *args) 5253 { 5254 output_operation (OP_SETUP, 5255 GNUNET_JSON_PACK ( 5256 GNUNET_JSON_pack_data_auto ("exchange_offline_pub", 5257 &master_pub))); 5258 } 5259 5260 else 5261 { 5262 char *pub_s; 5263 5264 pub_s = GNUNET_STRINGS_data_to_string_alloc (&master_pub, 5265 sizeof (master_pub)); 5266 fprintf (stdout, 5267 "%s\n", 5268 pub_s); 5269 GNUNET_free (pub_s); 5270 } 5271 if ( (NULL != *args) && 5272 (0 == strcmp (*args, 5273 "-")) ) 5274 args++; 5275 next (args); 5276 } 5277 5278 5279 /** 5280 * Print the current extensions as configured 5281 * 5282 * @param args the array of command-line arguments to process next 5283 */ 5284 static void 5285 do_extensions_show (char *const *args) 5286 { 5287 const struct TALER_Extensions *it; 5288 json_t *exts = json_object (); 5289 json_t *obj; 5290 5291 GNUNET_assert (NULL != exts); 5292 for (it = TALER_extensions_get_head (); 5293 NULL != it && NULL != it->extension; 5294 it = it->next) 5295 { 5296 const struct TALER_Extension *extension = it->extension; 5297 int ret; 5298 5299 ret = json_object_set_new (exts, 5300 extension->name, 5301 extension->manifest (extension)); 5302 GNUNET_assert (-1 != ret); 5303 } 5304 5305 obj = GNUNET_JSON_PACK ( 5306 GNUNET_JSON_pack_object_steal ("extensions", 5307 exts)); 5308 GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE, 5309 "%s\n", 5310 json_dumps (obj, 5311 JSON_INDENT (2))); 5312 json_decref (obj); 5313 next (args); 5314 } 5315 5316 5317 /** 5318 * Sign the configurations of the enabled extensions 5319 */ 5320 static void 5321 do_extensions_sign (char *const *args) 5322 { 5323 json_t *extensions = json_object (); 5324 struct TALER_ExtensionManifestsHashP h_manifests; 5325 struct TALER_MasterSignatureP sig; 5326 const struct TALER_Extensions *it; 5327 bool found = false; 5328 json_t *obj; 5329 5330 GNUNET_assert (NULL != extensions); 5331 for (it = TALER_extensions_get_head (); 5332 NULL != it && NULL != it->extension; 5333 it = it->next) 5334 { 5335 const struct TALER_Extension *ext = it->extension; 5336 GNUNET_assert (ext); 5337 5338 found = true; 5339 5340 GNUNET_assert (0 == 5341 json_object_set_new (extensions, 5342 ext->name, 5343 ext->manifest (ext))); 5344 } 5345 5346 if (! found) 5347 return; 5348 5349 if (GNUNET_OK != 5350 TALER_JSON_extensions_manifests_hash (extensions, 5351 &h_manifests)) 5352 { 5353 json_decref (extensions); 5354 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 5355 "error while hashing manifest for extensions\n"); 5356 return; 5357 } 5358 5359 if (GNUNET_OK != 5360 load_offline_key (GNUNET_NO)) 5361 { 5362 json_decref (extensions); 5363 return; 5364 } 5365 5366 TALER_exchange_offline_extension_manifests_hash_sign (&h_manifests, 5367 &master_priv, 5368 &sig); 5369 obj = GNUNET_JSON_PACK ( 5370 GNUNET_JSON_pack_object_steal ("extensions", 5371 extensions), 5372 GNUNET_JSON_pack_data_auto ( 5373 "extensions_sig", 5374 &sig)); 5375 5376 output_operation (OP_EXTENSIONS, 5377 obj); 5378 next (args); 5379 } 5380 5381 5382 /** 5383 * Dispatch @a args in the @a cmds array. 5384 * 5385 * @param args arguments with subcommand to dispatch 5386 * @param cmds array of possible subcommands to call 5387 */ 5388 static void 5389 cmd_handler (char *const *args, 5390 const struct SubCommand *cmds) 5391 { 5392 nxt = NULL; 5393 for (unsigned int i = 0; NULL != cmds[i].name; i++) 5394 { 5395 if (0 == strcasecmp (cmds[i].name, 5396 args[0])) 5397 { 5398 cmds[i].cb (&args[1]); 5399 return; 5400 } 5401 } 5402 5403 if (0 != strcasecmp ("help", 5404 args[0])) 5405 { 5406 GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE, 5407 "Unexpected command `%s'\n", 5408 args[0]); 5409 global_ret = EXIT_INVALIDARGUMENT; 5410 } 5411 5412 GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE, 5413 "Supported subcommands:\n"); 5414 for (unsigned int i = 0; NULL != cmds[i].name; i++) 5415 { 5416 GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE, 5417 "- %s: %s\n", 5418 cmds[i].name, 5419 cmds[i].help); 5420 } 5421 json_decref (out); 5422 out = NULL; 5423 GNUNET_SCHEDULER_shutdown (); 5424 } 5425 5426 5427 static void 5428 do_work_extensions (char *const *args) 5429 { 5430 struct SubCommand cmds[] = { 5431 { 5432 .name = "show", 5433 .help = 5434 "show the extensions in the Taler-config and their configured parameters", 5435 .cb = &do_extensions_show 5436 }, 5437 { 5438 .name = "sign", 5439 .help = 5440 "sign the configuration of the extensions and publish it with the exchange", 5441 .cb = &do_extensions_sign 5442 }, 5443 { 5444 .name = NULL, 5445 } 5446 }; 5447 5448 if (NULL == args[0]) 5449 { 5450 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 5451 "You must provide a subcommand: `show` or `sign`.\n"); 5452 GNUNET_SCHEDULER_shutdown (); 5453 global_ret = EXIT_INVALIDARGUMENT; 5454 return; 5455 } 5456 5457 cmd_handler (args, cmds); 5458 } 5459 5460 5461 static void 5462 work (void *cls) 5463 { 5464 char *const *args = cls; 5465 struct SubCommand cmds[] = { 5466 { 5467 .name = "setup", 5468 .help = 5469 "initialize offline key signing material and display public offline key", 5470 .cb = &do_setup 5471 }, 5472 { 5473 .name = "download", 5474 .help = 5475 "obtain future public keys from exchange (to be performed online!)", 5476 .cb = &do_download 5477 }, 5478 { 5479 .name = "show", 5480 .help = 5481 "display future public keys from exchange for human review (pass '-' as argument to disable consuming input)", 5482 .cb = &do_show 5483 }, 5484 { 5485 .name = "sign", 5486 .help = "sign all future public keys from the input", 5487 .cb = &do_sign 5488 }, 5489 { 5490 .name = "revoke-denomination", 5491 .help = 5492 "revoke denomination key (hash of public key must be given as argument)", 5493 .cb = &do_revoke_denomination_key 5494 }, 5495 { 5496 .name = "revoke-signkey", 5497 .help = 5498 "revoke exchange online signing key (public key must be given as argument)", 5499 .cb = &do_revoke_signkey 5500 }, 5501 { 5502 .name = "enable-auditor", 5503 .help = 5504 "enable auditor for the exchange (auditor-public key, auditor-URI and auditor-name must be given as arguments)", 5505 .cb = &do_add_auditor 5506 }, 5507 { 5508 .name = "disable-auditor", 5509 .help = 5510 "disable auditor at the exchange (auditor-public key must be given as argument)", 5511 .cb = &do_del_auditor 5512 }, 5513 { 5514 .name = "enable-account", 5515 .help = 5516 "enable wire account of the exchange (payto-URI must be given as argument; for optional arguments see man page)", 5517 .cb = &do_add_wire 5518 }, 5519 { 5520 .name = "disable-account", 5521 .help = 5522 "disable wire account of the exchange (payto-URI must be given as argument)", 5523 .cb = &do_del_wire 5524 }, 5525 { 5526 .name = "wire-fee", 5527 .help = 5528 "sign wire fees for the given year (year, wire method, wire fee, and closing fee must be given as arguments)", 5529 .cb = &do_set_wire_fee 5530 }, 5531 { 5532 .name = "global-fee", 5533 .help = 5534 "sign global fees for the given year (year, history fee, account fee, purse fee, purse timeout, history expiration and the maximum number of free purses per account must be given as arguments)", 5535 .cb = &do_set_global_fee 5536 }, 5537 { 5538 .name = "drain", 5539 .help = 5540 "drain profits from exchange escrow account to regular exchange operator account (amount, debit account configuration section and credit account payto://-URI must be given as arguments)", 5541 .cb = &do_drain 5542 }, 5543 { 5544 .name = "add-partner", 5545 .help = 5546 "add partner exchange for P2P wad transfers (partner master public key, partner base URL, wad fee, wad frequency and validity year must be given as arguments)", 5547 .cb = &do_add_partner 5548 }, 5549 { 5550 .name = "aml-enable", 5551 .help = 5552 "enable AML staff member (staff member public key, legal name and rw (read write) or ro (read only) must be given as arguments)", 5553 .cb = &enable_aml_staff 5554 }, 5555 { 5556 .name = "aml-disable", 5557 .help = 5558 "disable AML staff member (staff member public key and legal name must be given as arguments)", 5559 .cb = &disable_aml_staff 5560 }, 5561 { 5562 .name = "upload", 5563 .help = 5564 "upload operation result to exchange (to be performed online!)", 5565 .cb = &do_upload 5566 }, 5567 { 5568 .name = "extensions", 5569 .help = "subcommands for extension handling", 5570 .cb = &do_work_extensions 5571 }, 5572 /* list terminator */ 5573 { 5574 .name = NULL, 5575 } 5576 }; 5577 (void) cls; 5578 5579 cmd_handler (args, cmds); 5580 } 5581 5582 5583 /** 5584 * Main function that will be run. 5585 * 5586 * @param cls closure 5587 * @param args remaining command-line arguments 5588 * @param cfgfile name of the configuration file used (for saving, can be NULL!) 5589 * @param cfg configuration 5590 */ 5591 static void 5592 run (void *cls, 5593 char *const *args, 5594 const char *cfgfile, 5595 const struct GNUNET_CONFIGURATION_Handle *cfg) 5596 { 5597 (void) cls; 5598 (void) cfgfile; 5599 kcfg = cfg; 5600 5601 /* load extensions */ 5602 GNUNET_assert (GNUNET_OK == 5603 TALER_extensions_init (kcfg)); 5604 5605 /* setup age restriction, if applicable */ 5606 { 5607 const struct TALER_AgeRestrictionConfig *arc; 5608 5609 if (NULL != 5610 (arc = TALER_extensions_get_age_restriction_config ())) 5611 { 5612 ar_config = *arc; 5613 ar_enabled = true; 5614 } 5615 } 5616 5617 5618 if (GNUNET_OK != 5619 TALER_config_get_currency (kcfg, 5620 "exchange", 5621 ¤cy)) 5622 { 5623 global_ret = EXIT_NOTCONFIGURED; 5624 return; 5625 } 5626 5627 ctx = GNUNET_CURL_init (&GNUNET_CURL_gnunet_scheduler_reschedule, 5628 &rc); 5629 rc = GNUNET_CURL_gnunet_rc_create (ctx); 5630 GNUNET_SCHEDULER_add_shutdown (&do_shutdown, 5631 NULL); 5632 next (args); 5633 } 5634 5635 5636 /** 5637 * The main function of the taler-exchange-offline tool. This tool is used to 5638 * create the signing and denomination keys for the exchange. It uses the 5639 * long-term offline private key and generates signatures with it. It also 5640 * supports online operations with the exchange to download its input data and 5641 * to upload its results. Those online operations should be performed on 5642 * another machine in production! 5643 * 5644 * @param argc number of arguments from the command line 5645 * @param argv command line arguments 5646 * @return 0 ok, 1 on error 5647 */ 5648 int 5649 main (int argc, 5650 char *const *argv) 5651 { 5652 struct GNUNET_GETOPT_CommandLineOption options[] = { 5653 GNUNET_GETOPT_OPTION_END 5654 }; 5655 enum GNUNET_GenericReturnValue ret; 5656 5657 ret = GNUNET_PROGRAM_run ( 5658 TALER_EXCHANGE_project_data (), 5659 argc, argv, 5660 "taler-exchange-offline", 5661 gettext_noop ("Operations for offline signing for a Taler exchange"), 5662 options, 5663 &run, NULL); 5664 if (GNUNET_SYSERR == ret) 5665 return EXIT_INVALIDARGUMENT; 5666 if (GNUNET_NO == ret) 5667 return EXIT_SUCCESS; 5668 return global_ret; 5669 } 5670 5671 5672 /* end of taler-exchange-offline.c */