taler-exchange-offline.c (167468B)
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 *bank_label = NULL; 1619 int64_t priority = 0; 1620 const json_t *debit_restrictions; 1621 const json_t *credit_restrictions; 1622 unsigned int err_line; 1623 struct GNUNET_JSON_Specification spec[] = { 1624 TALER_JSON_spec_full_payto_uri ("payto_uri", 1625 &payto_uri), 1626 GNUNET_JSON_spec_mark_optional ( 1627 TALER_JSON_spec_web_url ("conversion_url", 1628 &conversion_url), 1629 NULL), 1630 GNUNET_JSON_spec_mark_optional ( 1631 GNUNET_JSON_spec_string ("bank_label", 1632 &bank_label), 1633 NULL), 1634 GNUNET_JSON_spec_int64 ("priority", 1635 &priority), 1636 GNUNET_JSON_spec_array_const ("debit_restrictions", 1637 &debit_restrictions), 1638 GNUNET_JSON_spec_array_const ("credit_restrictions", 1639 &credit_restrictions), 1640 GNUNET_JSON_spec_timestamp ("validity_start", 1641 &start_time), 1642 GNUNET_JSON_spec_fixed_auto ("master_sig_add", 1643 &master_sig_add), 1644 GNUNET_JSON_spec_fixed_auto ("master_sig_wire", 1645 &master_sig_wire), 1646 GNUNET_JSON_spec_end () 1647 }; 1648 1649 if (GNUNET_OK != 1650 GNUNET_JSON_parse (value, 1651 spec, 1652 &err_name, 1653 &err_line)) 1654 { 1655 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 1656 "Invalid input for adding wire account: %s#%u at %u (skipping)\n", 1657 err_name, 1658 err_line, 1659 (unsigned int) idx); 1660 json_dumpf (value, 1661 stderr, 1662 JSON_INDENT (2)); 1663 global_ret = EXIT_FAILURE; 1664 GNUNET_SCHEDULER_shutdown (); 1665 return; 1666 } 1667 { 1668 char *wire_method; 1669 1670 wire_method = TALER_payto_get_method (payto_uri.full_payto); 1671 if (NULL == wire_method) 1672 { 1673 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 1674 "payto:// URI `%s' is malformed\n", 1675 payto_uri.full_payto); 1676 global_ret = EXIT_FAILURE; 1677 GNUNET_SCHEDULER_shutdown (); 1678 return; 1679 } 1680 GNUNET_free (wire_method); 1681 } 1682 { 1683 char *msg = TALER_payto_validate (payto_uri); 1684 1685 if (NULL != msg) 1686 { 1687 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 1688 "payto URI is malformed: %s\n", 1689 msg); 1690 GNUNET_free (msg); 1691 GNUNET_SCHEDULER_shutdown (); 1692 global_ret = EXIT_INVALIDARGUMENT; 1693 return; 1694 } 1695 } 1696 war = GNUNET_new (struct WireAddRequest); 1697 war->idx = idx; 1698 war->h = 1699 TALER_EXCHANGE_post_management_wire_create (ctx, 1700 exchange_url, 1701 payto_uri, 1702 conversion_url, 1703 debit_restrictions, 1704 credit_restrictions, 1705 start_time, 1706 &master_sig_add, 1707 &master_sig_wire); 1708 TALER_EXCHANGE_post_management_wire_set_options ( 1709 war->h, 1710 TALER_EXCHANGE_post_management_wire_option_bank_label (bank_label), 1711 TALER_EXCHANGE_post_management_wire_option_priority (priority)); 1712 TALER_EXCHANGE_post_management_wire_start (war->h, 1713 &wire_add_cb, 1714 war); 1715 GNUNET_CONTAINER_DLL_insert (war_head, 1716 war_tail, 1717 war); 1718 } 1719 1720 1721 /** 1722 * Function called with information about the post wire del operation result. 1723 * 1724 * @param wdr request to delete wire account 1725 * @param wdres response data 1726 */ 1727 static void 1728 wire_del_cb ( 1729 struct WireDelRequest *wdr, 1730 const struct TALER_EXCHANGE_PostManagementWireDisableResponse *wdres) 1731 { 1732 const struct TALER_EXCHANGE_HttpResponse *hr = &wdres->hr; 1733 1734 if (MHD_HTTP_NO_CONTENT != hr->http_status) 1735 { 1736 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 1737 "Upload failed for command %u with status %u: %s (%s)\n", 1738 (unsigned int) wdr->idx, 1739 hr->http_status, 1740 TALER_ErrorCode_get_hint (hr->ec), 1741 hr->hint); 1742 global_ret = EXIT_FAILURE; 1743 } 1744 GNUNET_CONTAINER_DLL_remove (wdr_head, 1745 wdr_tail, 1746 wdr); 1747 GNUNET_free (wdr); 1748 test_shutdown (); 1749 } 1750 1751 1752 /** 1753 * Upload wire del data. 1754 * 1755 * @param exchange_url base URL of the exchange 1756 * @param idx index of the operation we are performing (for logging) 1757 * @param value arguments for denomination revocation 1758 */ 1759 static void 1760 upload_wire_del (const char *exchange_url, 1761 size_t idx, 1762 const json_t *value) 1763 { 1764 struct TALER_MasterSignatureP master_sig; 1765 struct TALER_FullPayto payto_uri; 1766 struct GNUNET_TIME_Timestamp end_time; 1767 struct WireDelRequest *wdr; 1768 const char *err_name; 1769 unsigned int err_line; 1770 struct GNUNET_JSON_Specification spec[] = { 1771 TALER_JSON_spec_full_payto_uri ("payto_uri", 1772 &payto_uri), 1773 GNUNET_JSON_spec_timestamp ("validity_end", 1774 &end_time), 1775 GNUNET_JSON_spec_fixed_auto ("master_sig", 1776 &master_sig), 1777 GNUNET_JSON_spec_end () 1778 }; 1779 1780 if (GNUNET_OK != 1781 GNUNET_JSON_parse (value, 1782 spec, 1783 &err_name, 1784 &err_line)) 1785 { 1786 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 1787 "Invalid input to disable wire account: %s#%u at %u (skipping)\n", 1788 err_name, 1789 err_line, 1790 (unsigned int) idx); 1791 json_dumpf (value, 1792 stderr, 1793 JSON_INDENT (2)); 1794 global_ret = EXIT_FAILURE; 1795 GNUNET_SCHEDULER_shutdown (); 1796 return; 1797 } 1798 wdr = GNUNET_new (struct WireDelRequest); 1799 wdr->idx = idx; 1800 wdr->h = 1801 TALER_EXCHANGE_post_management_wire_disable_create (ctx, 1802 exchange_url, 1803 payto_uri, 1804 end_time, 1805 &master_sig); 1806 TALER_EXCHANGE_post_management_wire_disable_start (wdr->h, 1807 &wire_del_cb, 1808 wdr); 1809 GNUNET_CONTAINER_DLL_insert (wdr_head, 1810 wdr_tail, 1811 wdr); 1812 } 1813 1814 1815 /** 1816 * Function called with information about the post wire fee operation result. 1817 * 1818 * @param wfr the wire fee request 1819 * @param swr response data 1820 */ 1821 static void 1822 wire_fee_cb ( 1823 struct WireFeeRequest *wfr, 1824 const struct TALER_EXCHANGE_PostManagementWireFeesResponse *swr) 1825 { 1826 const struct TALER_EXCHANGE_HttpResponse *hr = &swr->hr; 1827 1828 if (MHD_HTTP_NO_CONTENT != hr->http_status) 1829 { 1830 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 1831 "Upload failed for command %u with status %u: %s (%s)\n", 1832 (unsigned int) wfr->idx, 1833 hr->http_status, 1834 TALER_ErrorCode_get_hint (hr->ec), 1835 hr->hint); 1836 global_ret = EXIT_FAILURE; 1837 } 1838 GNUNET_CONTAINER_DLL_remove (wfr_head, 1839 wfr_tail, 1840 wfr); 1841 GNUNET_free (wfr); 1842 test_shutdown (); 1843 } 1844 1845 1846 /** 1847 * Upload wire fee. 1848 * 1849 * @param exchange_url base URL of the exchange 1850 * @param idx index of the operation we are performing (for logging) 1851 * @param value arguments for denomination revocation 1852 */ 1853 static void 1854 upload_wire_fee (const char *exchange_url, 1855 size_t idx, 1856 const json_t *value) 1857 { 1858 struct TALER_MasterSignatureP master_sig; 1859 const char *wire_method; 1860 struct WireFeeRequest *wfr; 1861 const char *err_name; 1862 unsigned int err_line; 1863 struct TALER_WireFeeSet fees; 1864 struct GNUNET_TIME_Timestamp start_time; 1865 struct GNUNET_TIME_Timestamp end_time; 1866 struct GNUNET_JSON_Specification spec[] = { 1867 GNUNET_JSON_spec_string ("wire_method", 1868 &wire_method), 1869 TALER_JSON_spec_amount ("wire_fee", 1870 currency, 1871 &fees.wire), 1872 TALER_JSON_spec_amount ("closing_fee", 1873 currency, 1874 &fees.closing), 1875 GNUNET_JSON_spec_timestamp ("start_time", 1876 &start_time), 1877 GNUNET_JSON_spec_timestamp ("end_time", 1878 &end_time), 1879 GNUNET_JSON_spec_fixed_auto ("master_sig", 1880 &master_sig), 1881 GNUNET_JSON_spec_end () 1882 }; 1883 1884 if (GNUNET_OK != 1885 GNUNET_JSON_parse (value, 1886 spec, 1887 &err_name, 1888 &err_line)) 1889 { 1890 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 1891 "Invalid input to set wire fee: %s#%u at %u (skipping)\n", 1892 err_name, 1893 err_line, 1894 (unsigned int) idx); 1895 json_dumpf (value, 1896 stderr, 1897 JSON_INDENT (2)); 1898 global_ret = EXIT_FAILURE; 1899 GNUNET_SCHEDULER_shutdown (); 1900 return; 1901 } 1902 wfr = GNUNET_new (struct WireFeeRequest); 1903 wfr->idx = idx; 1904 wfr->h = 1905 TALER_EXCHANGE_post_management_wire_fees_create (ctx, 1906 exchange_url, 1907 wire_method, 1908 start_time, 1909 end_time, 1910 &fees, 1911 &master_sig); 1912 TALER_EXCHANGE_post_management_wire_fees_start (wfr->h, 1913 &wire_fee_cb, 1914 wfr); 1915 GNUNET_CONTAINER_DLL_insert (wfr_head, 1916 wfr_tail, 1917 wfr); 1918 } 1919 1920 1921 /** 1922 * Function called with information about the post global fee operation result. 1923 * 1924 * @param gfr the global fee request 1925 * @param gr response data 1926 */ 1927 static void 1928 global_fee_cb ( 1929 struct GlobalFeeRequest *gfr, 1930 const struct TALER_EXCHANGE_PostManagementGlobalFeesResponse *gr) 1931 { 1932 const struct TALER_EXCHANGE_HttpResponse *hr = &gr->hr; 1933 1934 if (MHD_HTTP_NO_CONTENT != hr->http_status) 1935 { 1936 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 1937 "Upload failed for command %u with status %u: %s (%s)\n", 1938 (unsigned int) gfr->idx, 1939 hr->http_status, 1940 TALER_ErrorCode_get_hint (hr->ec), 1941 hr->hint); 1942 global_ret = EXIT_FAILURE; 1943 } 1944 GNUNET_CONTAINER_DLL_remove (gfr_head, 1945 gfr_tail, 1946 gfr); 1947 GNUNET_free (gfr); 1948 test_shutdown (); 1949 } 1950 1951 1952 /** 1953 * Upload global fee. 1954 * 1955 * @param exchange_url base URL of the exchange 1956 * @param idx index of the operation we are performing (for logging) 1957 * @param value arguments for denomination revocation 1958 */ 1959 static void 1960 upload_global_fee (const char *exchange_url, 1961 size_t idx, 1962 const json_t *value) 1963 { 1964 struct TALER_MasterSignatureP master_sig; 1965 struct GlobalFeeRequest *gfr; 1966 const char *err_name; 1967 unsigned int err_line; 1968 struct TALER_GlobalFeeSet fees; 1969 struct GNUNET_TIME_Timestamp start_time; 1970 struct GNUNET_TIME_Timestamp end_time; 1971 struct GNUNET_TIME_Relative purse_timeout; 1972 struct GNUNET_TIME_Relative history_expiration; 1973 uint32_t purse_account_limit; 1974 struct GNUNET_JSON_Specification spec[] = { 1975 TALER_JSON_spec_amount ("history_fee", 1976 currency, 1977 &fees.history), 1978 TALER_JSON_spec_amount ("account_fee", 1979 currency, 1980 &fees.account), 1981 TALER_JSON_spec_amount ("purse_fee", 1982 currency, 1983 &fees.purse), 1984 GNUNET_JSON_spec_relative_time ("purse_timeout", 1985 &purse_timeout), 1986 GNUNET_JSON_spec_relative_time ("history_expiration", 1987 &history_expiration), 1988 GNUNET_JSON_spec_uint32 ("purse_account_limit", 1989 &purse_account_limit), 1990 GNUNET_JSON_spec_timestamp ("start_time", 1991 &start_time), 1992 GNUNET_JSON_spec_timestamp ("end_time", 1993 &end_time), 1994 GNUNET_JSON_spec_fixed_auto ("master_sig", 1995 &master_sig), 1996 GNUNET_JSON_spec_end () 1997 }; 1998 1999 if (GNUNET_OK != 2000 GNUNET_JSON_parse (value, 2001 spec, 2002 &err_name, 2003 &err_line)) 2004 { 2005 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 2006 "Invalid input to set wire fee: %s#%u at %u (skipping)\n", 2007 err_name, 2008 err_line, 2009 (unsigned int) idx); 2010 json_dumpf (value, 2011 stderr, 2012 JSON_INDENT (2)); 2013 global_ret = EXIT_FAILURE; 2014 GNUNET_SCHEDULER_shutdown (); 2015 return; 2016 } 2017 gfr = GNUNET_new (struct GlobalFeeRequest); 2018 gfr->idx = idx; 2019 gfr->h = 2020 TALER_EXCHANGE_post_management_global_fees_create (ctx, 2021 exchange_url, 2022 start_time, 2023 end_time, 2024 &fees, 2025 purse_timeout, 2026 history_expiration, 2027 purse_account_limit, 2028 &master_sig); 2029 TALER_EXCHANGE_post_management_global_fees_start (gfr->h, 2030 &global_fee_cb, 2031 gfr); 2032 GNUNET_CONTAINER_DLL_insert (gfr_head, 2033 gfr_tail, 2034 gfr); 2035 } 2036 2037 2038 /** 2039 * Function called with information about the drain profits operation. 2040 * 2041 * @param dpr the drain profits request 2042 * @param mdr response data 2043 */ 2044 static void 2045 drain_profits_cb ( 2046 struct DrainProfitsRequest *dpr, 2047 const struct TALER_EXCHANGE_PostManagementDrainResponse *mdr) 2048 { 2049 const struct TALER_EXCHANGE_HttpResponse *hr = &mdr->hr; 2050 2051 if (MHD_HTTP_NO_CONTENT != hr->http_status) 2052 { 2053 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 2054 "Upload failed for command %u with status %u: %s (%s)\n", 2055 (unsigned int) dpr->idx, 2056 hr->http_status, 2057 TALER_ErrorCode_get_hint (hr->ec), 2058 hr->hint); 2059 global_ret = EXIT_FAILURE; 2060 } 2061 GNUNET_CONTAINER_DLL_remove (dpr_head, 2062 dpr_tail, 2063 dpr); 2064 GNUNET_free (dpr); 2065 test_shutdown (); 2066 } 2067 2068 2069 /** 2070 * Upload drain profit action. 2071 * 2072 * @param exchange_url base URL of the exchange 2073 * @param idx index of the operation we are performing (for logging) 2074 * @param value arguments for drain profits 2075 */ 2076 static void 2077 upload_drain (const char *exchange_url, 2078 size_t idx, 2079 const json_t *value) 2080 { 2081 struct TALER_WireTransferIdentifierRawP wtid; 2082 struct TALER_MasterSignatureP master_sig; 2083 const char *err_name; 2084 unsigned int err_line; 2085 struct TALER_Amount amount; 2086 struct GNUNET_TIME_Timestamp date; 2087 struct TALER_FullPayto payto_uri; 2088 const char *account_section; 2089 struct DrainProfitsRequest *dpr; 2090 struct GNUNET_JSON_Specification spec[] = { 2091 GNUNET_JSON_spec_fixed_auto ("wtid", 2092 &wtid), 2093 TALER_JSON_spec_amount ("amount", 2094 currency, 2095 &amount), 2096 GNUNET_JSON_spec_timestamp ("date", 2097 &date), 2098 GNUNET_JSON_spec_string ("account_section", 2099 &account_section), 2100 TALER_JSON_spec_full_payto_uri ("payto_uri", 2101 &payto_uri), 2102 GNUNET_JSON_spec_fixed_auto ("master_sig", 2103 &master_sig), 2104 GNUNET_JSON_spec_end () 2105 }; 2106 2107 if (GNUNET_OK != 2108 GNUNET_JSON_parse (value, 2109 spec, 2110 &err_name, 2111 &err_line)) 2112 { 2113 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 2114 "Invalid input to drain profits: %s#%u at %u (skipping)\n", 2115 err_name, 2116 err_line, 2117 (unsigned int) idx); 2118 json_dumpf (value, 2119 stderr, 2120 JSON_INDENT (2)); 2121 global_ret = EXIT_FAILURE; 2122 GNUNET_SCHEDULER_shutdown (); 2123 return; 2124 } 2125 dpr = GNUNET_new (struct DrainProfitsRequest); 2126 dpr->idx = idx; 2127 dpr->h = 2128 TALER_EXCHANGE_post_management_drain_create (ctx, 2129 exchange_url, 2130 &wtid, 2131 &amount, 2132 date, 2133 account_section, 2134 payto_uri, 2135 &master_sig); 2136 TALER_EXCHANGE_post_management_drain_start (dpr->h, 2137 &drain_profits_cb, 2138 dpr); 2139 GNUNET_CONTAINER_DLL_insert (dpr_head, 2140 dpr_tail, 2141 dpr); 2142 } 2143 2144 2145 /** 2146 * Function called with information about the post upload keys operation result. 2147 * 2148 * @param ukr the upload keys request 2149 * @param mr response data 2150 */ 2151 static void 2152 keys_cb ( 2153 struct UploadKeysRequest *ukr, 2154 const struct TALER_EXCHANGE_PostManagementKeysResponse *mr) 2155 { 2156 const struct TALER_EXCHANGE_HttpResponse *hr = &mr->hr; 2157 2158 if (MHD_HTTP_NO_CONTENT != hr->http_status) 2159 { 2160 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 2161 "Upload failed for command %u with status %u: %s (%s)\n", 2162 (unsigned int) ukr->idx, 2163 hr->http_status, 2164 TALER_ErrorCode_get_hint (hr->ec), 2165 hr->hint); 2166 global_ret = EXIT_FAILURE; 2167 } 2168 GNUNET_CONTAINER_DLL_remove (ukr_head, 2169 ukr_tail, 2170 ukr); 2171 GNUNET_free (ukr); 2172 test_shutdown (); 2173 } 2174 2175 2176 /** 2177 * Upload (denomination and signing) key master signatures. 2178 * 2179 * @param exchange_url base URL of the exchange 2180 * @param idx index of the operation we are performing (for logging) 2181 * @param value arguments for POSTing keys 2182 */ 2183 static void 2184 upload_keys (const char *exchange_url, 2185 size_t idx, 2186 const json_t *value) 2187 { 2188 struct TALER_EXCHANGE_ManagementPostKeysData pkd; 2189 struct UploadKeysRequest *ukr; 2190 const char *err_name; 2191 unsigned int err_line; 2192 const json_t *denom_sigs; 2193 const json_t *signkey_sigs; 2194 struct GNUNET_JSON_Specification spec[] = { 2195 GNUNET_JSON_spec_array_const ("denom_sigs", 2196 &denom_sigs), 2197 GNUNET_JSON_spec_array_const ("signkey_sigs", 2198 &signkey_sigs), 2199 GNUNET_JSON_spec_end () 2200 }; 2201 bool ok = true; 2202 2203 if (GNUNET_OK != 2204 GNUNET_JSON_parse (value, 2205 spec, 2206 &err_name, 2207 &err_line)) 2208 { 2209 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 2210 "Invalid input to 'upload': %s#%u (skipping)\n", 2211 err_name, 2212 err_line); 2213 json_dumpf (value, 2214 stderr, 2215 JSON_INDENT (2)); 2216 global_ret = EXIT_FAILURE; 2217 GNUNET_SCHEDULER_shutdown (); 2218 return; 2219 } 2220 pkd.num_sign_sigs = json_array_size (signkey_sigs); 2221 pkd.num_denom_sigs = json_array_size (denom_sigs); 2222 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 2223 "Uploading %u denomination and %u signing key signatures\n", 2224 pkd.num_denom_sigs, 2225 pkd.num_sign_sigs); 2226 pkd.sign_sigs = GNUNET_new_array ( 2227 pkd.num_sign_sigs, 2228 struct TALER_EXCHANGE_SigningKeySignature); 2229 pkd.denom_sigs = GNUNET_new_array ( 2230 pkd.num_denom_sigs, 2231 struct TALER_EXCHANGE_DenominationKeySignature); 2232 for (unsigned int i = 0; i<pkd.num_sign_sigs; i++) 2233 { 2234 struct TALER_EXCHANGE_SigningKeySignature *ss = &pkd.sign_sigs[i]; 2235 json_t *val = json_array_get (signkey_sigs, 2236 i); 2237 struct GNUNET_JSON_Specification ispec[] = { 2238 GNUNET_JSON_spec_fixed_auto ("exchange_pub", 2239 &ss->exchange_pub), 2240 GNUNET_JSON_spec_fixed_auto ("master_sig", 2241 &ss->master_sig), 2242 GNUNET_JSON_spec_end () 2243 }; 2244 2245 if (GNUNET_OK != 2246 GNUNET_JSON_parse (val, 2247 ispec, 2248 &err_name, 2249 &err_line)) 2250 { 2251 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 2252 "Invalid input for signkey validity: %s#%u at %u (aborting)\n", 2253 err_name, 2254 err_line, 2255 i); 2256 json_dumpf (val, 2257 stderr, 2258 JSON_INDENT (2)); 2259 ok = false; 2260 } 2261 } 2262 for (unsigned int i = 0; i<pkd.num_denom_sigs; i++) 2263 { 2264 struct TALER_EXCHANGE_DenominationKeySignature *ds = &pkd.denom_sigs[i]; 2265 json_t *val = json_array_get (denom_sigs, 2266 i); 2267 struct GNUNET_JSON_Specification ispec[] = { 2268 GNUNET_JSON_spec_fixed_auto ("h_denom_pub", 2269 &ds->h_denom_pub), 2270 GNUNET_JSON_spec_fixed_auto ("master_sig", 2271 &ds->master_sig), 2272 GNUNET_JSON_spec_end () 2273 }; 2274 2275 if (GNUNET_OK != 2276 GNUNET_JSON_parse (val, 2277 ispec, 2278 &err_name, 2279 &err_line)) 2280 { 2281 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 2282 "Invalid input for denomination validity: %s#%u at %u (aborting)\n", 2283 err_name, 2284 err_line, 2285 i); 2286 json_dumpf (val, 2287 stderr, 2288 JSON_INDENT (2)); 2289 ok = false; 2290 } 2291 } 2292 2293 if (ok) 2294 { 2295 ukr = GNUNET_new (struct UploadKeysRequest); 2296 ukr->idx = idx; 2297 ukr->h = 2298 TALER_EXCHANGE_post_management_keys_create (ctx, 2299 exchange_url, 2300 &pkd); 2301 TALER_EXCHANGE_post_management_keys_start (ukr->h, 2302 &keys_cb, 2303 ukr); 2304 GNUNET_CONTAINER_DLL_insert (ukr_head, 2305 ukr_tail, 2306 ukr); 2307 } 2308 else 2309 { 2310 global_ret = EXIT_FAILURE; 2311 GNUNET_SCHEDULER_shutdown (); 2312 } 2313 GNUNET_free (pkd.sign_sigs); 2314 GNUNET_free (pkd.denom_sigs); 2315 } 2316 2317 2318 /** 2319 * Function called with information about the post upload extensions operation result. 2320 * 2321 * @param uer extension upload request 2322 * @param er response data 2323 */ 2324 static void 2325 extensions_cb ( 2326 struct UploadExtensionsRequest *uer, 2327 const struct TALER_EXCHANGE_PostManagementExtensionsResponse *er) 2328 { 2329 const struct TALER_EXCHANGE_HttpResponse *hr = &er->hr; 2330 2331 if (MHD_HTTP_NO_CONTENT != hr->http_status) 2332 { 2333 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 2334 "Upload failed for command %u with status %u: %s (%s)\n", 2335 (unsigned int) uer->idx, 2336 hr->http_status, 2337 TALER_ErrorCode_get_hint (hr->ec), 2338 hr->hint); 2339 global_ret = EXIT_FAILURE; 2340 } 2341 GNUNET_CONTAINER_DLL_remove (uer_head, 2342 uer_tail, 2343 uer); 2344 GNUNET_free (uer); 2345 test_shutdown (); 2346 } 2347 2348 2349 /** 2350 * Upload extension configuration 2351 * 2352 * @param exchange_url base URL of the exchange 2353 * @param idx index of the operation we are performing (for logging) 2354 * @param value arguments for POSTing configurations of extensions 2355 */ 2356 static void 2357 upload_extensions (const char *exchange_url, 2358 size_t idx, 2359 const json_t *value) 2360 { 2361 const json_t *extensions; 2362 struct TALER_MasterSignatureP sig; 2363 const char *err_name; 2364 unsigned int err_line; 2365 struct GNUNET_JSON_Specification spec[] = { 2366 GNUNET_JSON_spec_object_const ("extensions", 2367 &extensions), 2368 GNUNET_JSON_spec_fixed_auto ("extensions_sig", 2369 &sig), 2370 GNUNET_JSON_spec_end () 2371 }; 2372 2373 /* 1. Parse the signed extensions */ 2374 if (GNUNET_OK != 2375 GNUNET_JSON_parse (value, 2376 spec, 2377 &err_name, 2378 &err_line)) 2379 { 2380 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 2381 "Invalid input to set extensions: %s#%u at %u (skipping)\n", 2382 err_name, 2383 err_line, 2384 (unsigned int) idx); 2385 json_dumpf (value, 2386 stderr, 2387 JSON_INDENT (2)); 2388 global_ret = EXIT_FAILURE; 2389 GNUNET_SCHEDULER_shutdown (); 2390 return; 2391 } 2392 2393 /* 2. Verify the signature */ 2394 { 2395 struct TALER_ExtensionManifestsHashP h_manifests; 2396 2397 if (GNUNET_OK != 2398 TALER_JSON_extensions_manifests_hash (extensions, 2399 &h_manifests)) 2400 { 2401 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 2402 "couldn't hash extensions' manifests\n"); 2403 global_ret = EXIT_FAILURE; 2404 GNUNET_SCHEDULER_shutdown (); 2405 return; 2406 } 2407 2408 if (GNUNET_OK != 2409 load_offline_key (GNUNET_NO)) 2410 return; 2411 2412 if (GNUNET_OK != TALER_exchange_offline_extension_manifests_hash_verify ( 2413 &h_manifests, 2414 &master_pub, 2415 &sig)) 2416 { 2417 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 2418 "invalid signature for extensions\n"); 2419 global_ret = EXIT_FAILURE; 2420 GNUNET_SCHEDULER_shutdown (); 2421 return; 2422 } 2423 } 2424 2425 /* 3. Upload the extensions */ 2426 { 2427 struct TALER_EXCHANGE_ManagementPostExtensionsData ped = { 2428 .extensions = extensions, 2429 .extensions_sig = sig, 2430 }; 2431 struct UploadExtensionsRequest *uer 2432 = GNUNET_new (struct UploadExtensionsRequest); 2433 2434 uer->idx = idx; 2435 uer->h = TALER_EXCHANGE_post_management_extensions_create ( 2436 ctx, 2437 exchange_url, 2438 &ped); 2439 TALER_EXCHANGE_post_management_extensions_start (uer->h, 2440 &extensions_cb, 2441 uer); 2442 GNUNET_CONTAINER_DLL_insert (uer_head, 2443 uer_tail, 2444 uer); 2445 } 2446 } 2447 2448 2449 /** 2450 * Function called with information about the add partner operation. 2451 * 2452 * @param par the request 2453 * @param apr response data 2454 */ 2455 static void 2456 add_partner_cb ( 2457 struct PartnerAddRequest *par, 2458 const struct TALER_EXCHANGE_PostManagementPartnersResponse *apr) 2459 { 2460 const struct TALER_EXCHANGE_HttpResponse *hr = &apr->hr; 2461 2462 if (MHD_HTTP_NO_CONTENT != hr->http_status) 2463 { 2464 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 2465 "Upload failed for command %u with status %u: %s (%s)\n", 2466 (unsigned int) par->idx, 2467 hr->http_status, 2468 TALER_ErrorCode_get_hint (hr->ec), 2469 hr->hint); 2470 global_ret = EXIT_FAILURE; 2471 } 2472 GNUNET_CONTAINER_DLL_remove (par_head, 2473 par_tail, 2474 par); 2475 GNUNET_free (par); 2476 test_shutdown (); 2477 } 2478 2479 2480 /** 2481 * Add partner action. 2482 * 2483 * @param exchange_url base URL of the exchange 2484 * @param idx index of the operation we are performing (for logging) 2485 * @param value arguments for add partner 2486 */ 2487 static void 2488 add_partner (const char *exchange_url, 2489 size_t idx, 2490 const json_t *value) 2491 { 2492 struct TALER_MasterPublicKeyP partner_pub; 2493 struct GNUNET_TIME_Timestamp start_date; 2494 struct GNUNET_TIME_Timestamp end_date; 2495 struct GNUNET_TIME_Relative wad_frequency; 2496 struct TALER_Amount wad_fee; 2497 const char *partner_base_url; 2498 struct TALER_MasterSignatureP master_sig; 2499 struct PartnerAddRequest *par; 2500 struct GNUNET_JSON_Specification spec[] = { 2501 GNUNET_JSON_spec_fixed_auto ("partner_pub", 2502 &partner_pub), 2503 TALER_JSON_spec_amount ("wad_fee", 2504 currency, 2505 &wad_fee), 2506 GNUNET_JSON_spec_relative_time ("wad_frequency", 2507 &wad_frequency), 2508 GNUNET_JSON_spec_timestamp ("start_date", 2509 &start_date), 2510 GNUNET_JSON_spec_timestamp ("end_date", 2511 &end_date), 2512 TALER_JSON_spec_web_url ("partner_base_url", 2513 &partner_base_url), 2514 GNUNET_JSON_spec_fixed_auto ("master_sig", 2515 &master_sig), 2516 GNUNET_JSON_spec_end () 2517 }; 2518 const char *err_name; 2519 unsigned int err_line; 2520 2521 if (GNUNET_OK != 2522 GNUNET_JSON_parse (value, 2523 spec, 2524 &err_name, 2525 &err_line)) 2526 { 2527 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 2528 "Invalid input to add partner: %s#%u at %u (skipping)\n", 2529 err_name, 2530 err_line, 2531 (unsigned int) idx); 2532 json_dumpf (value, 2533 stderr, 2534 JSON_INDENT (2)); 2535 global_ret = EXIT_FAILURE; 2536 GNUNET_SCHEDULER_shutdown (); 2537 return; 2538 } 2539 par = GNUNET_new (struct PartnerAddRequest); 2540 par->idx = idx; 2541 par->h = 2542 TALER_EXCHANGE_post_management_partners_create (ctx, 2543 exchange_url, 2544 &partner_pub, 2545 start_date, 2546 end_date, 2547 wad_frequency, 2548 &wad_fee, 2549 partner_base_url, 2550 &master_sig); 2551 TALER_EXCHANGE_post_management_partners_start (par->h, 2552 &add_partner_cb, 2553 par); 2554 GNUNET_CONTAINER_DLL_insert (par_head, 2555 par_tail, 2556 par); 2557 } 2558 2559 2560 /** 2561 * Function called with information about the AML officer update operation. 2562 * 2563 * @param asr the request 2564 * @param ar response data 2565 */ 2566 static void 2567 update_aml_officer_cb ( 2568 TALER_EXCHANGE_POST_MANAGEMENT_AML_OFFICERS_RESULT_CLOSURE *asr, 2569 const struct TALER_EXCHANGE_PostManagementAmlOfficersResponse *ar) 2570 { 2571 const struct TALER_EXCHANGE_HttpResponse *hr = &ar->hr; 2572 2573 if (MHD_HTTP_NO_CONTENT != hr->http_status) 2574 { 2575 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 2576 "Upload failed for command %u with status %u: %s (%s)\n", 2577 (unsigned int) asr->idx, 2578 hr->http_status, 2579 TALER_ErrorCode_get_hint (hr->ec), 2580 hr->hint); 2581 global_ret = EXIT_FAILURE; 2582 } 2583 GNUNET_CONTAINER_DLL_remove (asr_head, 2584 asr_tail, 2585 asr); 2586 GNUNET_free (asr); 2587 test_shutdown (); 2588 } 2589 2590 2591 /** 2592 * Upload AML staff action. 2593 * 2594 * @param exchange_url base URL of the exchange 2595 * @param idx index of the operation we are performing (for logging) 2596 * @param value arguments for AML staff change 2597 */ 2598 static void 2599 update_aml_staff (const char *exchange_url, 2600 size_t idx, 2601 const json_t *value) 2602 { 2603 struct TALER_AmlOfficerPublicKeyP officer_pub; 2604 const char *officer_name; 2605 struct GNUNET_TIME_Timestamp change_date; 2606 bool is_active; 2607 bool read_only; 2608 struct TALER_MasterSignatureP master_sig; 2609 struct AmlStaffRequest *asr; 2610 struct GNUNET_JSON_Specification spec[] = { 2611 GNUNET_JSON_spec_fixed_auto ("officer_pub", 2612 &officer_pub), 2613 GNUNET_JSON_spec_timestamp ("change_date", 2614 &change_date), 2615 GNUNET_JSON_spec_bool ("is_active", 2616 &is_active), 2617 GNUNET_JSON_spec_bool ("read_only", 2618 &read_only), 2619 GNUNET_JSON_spec_string ("officer_name", 2620 &officer_name), 2621 GNUNET_JSON_spec_fixed_auto ("master_sig", 2622 &master_sig), 2623 GNUNET_JSON_spec_end () 2624 }; 2625 const char *err_name; 2626 unsigned int err_line; 2627 2628 if (GNUNET_OK != 2629 GNUNET_JSON_parse (value, 2630 spec, 2631 &err_name, 2632 &err_line)) 2633 { 2634 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 2635 "Invalid input to AML staff update: %s#%u at %u (skipping)\n", 2636 err_name, 2637 err_line, 2638 (unsigned int) idx); 2639 json_dumpf (value, 2640 stderr, 2641 JSON_INDENT (2)); 2642 global_ret = EXIT_FAILURE; 2643 GNUNET_SCHEDULER_shutdown (); 2644 return; 2645 } 2646 asr = GNUNET_new (struct AmlStaffRequest); 2647 asr->idx = idx; 2648 asr->h = TALER_EXCHANGE_post_management_aml_officers_create ( 2649 ctx, 2650 exchange_url, 2651 &officer_pub, 2652 officer_name, 2653 change_date, 2654 is_active, 2655 read_only, 2656 &master_sig); 2657 GNUNET_CONTAINER_DLL_insert (asr_head, 2658 asr_tail, 2659 asr); 2660 GNUNET_assert (TALER_EC_NONE == 2661 TALER_EXCHANGE_post_management_aml_officers_start ( 2662 asr->h, 2663 &update_aml_officer_cb, 2664 asr)); 2665 } 2666 2667 2668 /** 2669 * Perform uploads based on the JSON in #out. 2670 * 2671 * @param exchange_url base URL of the exchange to use 2672 */ 2673 static void 2674 trigger_upload (const char *exchange_url) 2675 { 2676 struct UploadHandler uhs[] = { 2677 { 2678 .key = OP_REVOKE_DENOMINATION, 2679 .cb = &upload_denom_revocation 2680 }, 2681 { 2682 .key = OP_REVOKE_SIGNKEY, 2683 .cb = &upload_signkey_revocation 2684 }, 2685 { 2686 .key = OP_ENABLE_AUDITOR, 2687 .cb = &upload_auditor_add 2688 }, 2689 { 2690 .key = OP_DISABLE_AUDITOR, 2691 .cb = &upload_auditor_del 2692 }, 2693 { 2694 .key = OP_ENABLE_WIRE, 2695 .cb = &upload_wire_add 2696 }, 2697 { 2698 .key = OP_DISABLE_WIRE, 2699 .cb = &upload_wire_del 2700 }, 2701 { 2702 .key = OP_SET_WIRE_FEE, 2703 .cb = &upload_wire_fee 2704 }, 2705 { 2706 .key = OP_SET_GLOBAL_FEE, 2707 .cb = &upload_global_fee 2708 }, 2709 { 2710 .key = OP_UPLOAD_SIGS, 2711 .cb = &upload_keys 2712 }, 2713 { 2714 .key = OP_DRAIN_PROFITS, 2715 .cb = &upload_drain 2716 }, 2717 { 2718 .key = OP_EXTENSIONS, 2719 .cb = &upload_extensions 2720 }, 2721 { 2722 .key = OP_UPDATE_AML_STAFF, 2723 .cb = &update_aml_staff 2724 }, 2725 { 2726 .key = OP_ADD_PARTNER, 2727 .cb = &add_partner 2728 }, 2729 /* array termination */ 2730 { 2731 .key = NULL 2732 } 2733 }; 2734 size_t index; 2735 json_t *obj; 2736 2737 json_array_foreach (out, index, obj) { 2738 bool found = false; 2739 const char *key; 2740 const json_t *value; 2741 2742 key = json_string_value (json_object_get (obj, "operation")); 2743 value = json_object_get (obj, "arguments"); 2744 if (NULL == key) 2745 { 2746 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 2747 "Malformed JSON input\n"); 2748 global_ret = EXIT_FAILURE; 2749 GNUNET_SCHEDULER_shutdown (); 2750 return; 2751 } 2752 /* block of code that uses key and value */ 2753 for (unsigned int i = 0; NULL != uhs[i].key; i++) 2754 { 2755 if (0 == strcasecmp (key, 2756 uhs[i].key)) 2757 { 2758 2759 found = true; 2760 uhs[i].cb (exchange_url, 2761 index, 2762 value); 2763 break; 2764 } 2765 } 2766 if (! found) 2767 { 2768 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 2769 "Upload does not know how to handle `%s'\n", 2770 key); 2771 global_ret = EXIT_FAILURE; 2772 GNUNET_SCHEDULER_shutdown (); 2773 return; 2774 } 2775 } 2776 } 2777 2778 2779 /** 2780 * Upload operation result (signatures) to exchange. 2781 * 2782 * @param args the array of command-line arguments to process next 2783 */ 2784 static void 2785 do_upload (char *const *args) 2786 { 2787 (void) args; 2788 if (NULL != in) 2789 { 2790 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 2791 "Downloaded data was not consumed, refusing upload\n"); 2792 GNUNET_SCHEDULER_shutdown (); 2793 global_ret = EXIT_FAILURE; 2794 return; 2795 } 2796 if (NULL == out) 2797 { 2798 json_error_t err; 2799 2800 out = json_loadf (stdin, 2801 JSON_REJECT_DUPLICATES, 2802 &err); 2803 if (NULL == out) 2804 { 2805 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 2806 "Failed to read JSON input: %s at %d:%s (offset: %d)\n", 2807 err.text, 2808 err.line, 2809 err.source, 2810 err.position); 2811 GNUNET_SCHEDULER_shutdown (); 2812 global_ret = EXIT_FAILURE; 2813 return; 2814 } 2815 } 2816 if (! json_is_array (out)) 2817 { 2818 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 2819 "Error: expected JSON array for `upload` command\n"); 2820 GNUNET_SCHEDULER_shutdown (); 2821 global_ret = EXIT_FAILURE; 2822 return; 2823 } 2824 if ( (NULL == CFG_exchange_url) && 2825 (GNUNET_OK != 2826 GNUNET_CONFIGURATION_get_value_string (kcfg, 2827 "exchange", 2828 "BASE_URL", 2829 &CFG_exchange_url)) ) 2830 { 2831 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, 2832 "exchange", 2833 "BASE_URL"); 2834 global_ret = EXIT_NOTCONFIGURED; 2835 GNUNET_SCHEDULER_shutdown (); 2836 return; 2837 } 2838 trigger_upload (CFG_exchange_url); 2839 json_decref (out); 2840 out = NULL; 2841 } 2842 2843 2844 /** 2845 * Revoke denomination key. 2846 * 2847 * @param args the array of command-line arguments to process next; 2848 * args[0] must be the hash of the denomination key to revoke 2849 */ 2850 static void 2851 do_revoke_denomination_key (char *const *args) 2852 { 2853 struct TALER_DenominationHashP h_denom_pub; 2854 struct TALER_MasterSignatureP master_sig; 2855 2856 if (NULL != in) 2857 { 2858 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 2859 "Downloaded data was not consumed, refusing revocation\n"); 2860 GNUNET_SCHEDULER_shutdown (); 2861 global_ret = EXIT_FAILURE; 2862 return; 2863 } 2864 if ( (NULL == args[0]) || 2865 (GNUNET_OK != 2866 GNUNET_STRINGS_string_to_data (args[0], 2867 strlen (args[0]), 2868 &h_denom_pub, 2869 sizeof (h_denom_pub))) ) 2870 { 2871 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 2872 "You must specify a denomination key with this subcommand\n"); 2873 GNUNET_SCHEDULER_shutdown (); 2874 global_ret = EXIT_INVALIDARGUMENT; 2875 return; 2876 } 2877 if (GNUNET_OK != 2878 load_offline_key (GNUNET_NO)) 2879 return; 2880 TALER_exchange_offline_denomination_revoke_sign (&h_denom_pub, 2881 &master_priv, 2882 &master_sig); 2883 output_operation (OP_REVOKE_DENOMINATION, 2884 GNUNET_JSON_PACK ( 2885 GNUNET_JSON_pack_data_auto ("h_denom_pub", 2886 &h_denom_pub), 2887 GNUNET_JSON_pack_data_auto ("master_sig", 2888 &master_sig))); 2889 next (args + 1); 2890 } 2891 2892 2893 /** 2894 * Revoke signkey. 2895 * 2896 * @param args the array of command-line arguments to process next; 2897 * args[0] must be the hash of the denomination key to revoke 2898 */ 2899 static void 2900 do_revoke_signkey (char *const *args) 2901 { 2902 struct TALER_ExchangePublicKeyP exchange_pub; 2903 struct TALER_MasterSignatureP master_sig; 2904 2905 if (NULL != in) 2906 { 2907 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 2908 "Downloaded data was not consumed, refusing revocation\n"); 2909 GNUNET_SCHEDULER_shutdown (); 2910 global_ret = EXIT_FAILURE; 2911 return; 2912 } 2913 if ( (NULL == args[0]) || 2914 (GNUNET_OK != 2915 GNUNET_STRINGS_string_to_data (args[0], 2916 strlen (args[0]), 2917 &exchange_pub, 2918 sizeof (exchange_pub))) ) 2919 { 2920 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 2921 "You must specify an exchange signing key with this subcommand\n"); 2922 GNUNET_SCHEDULER_shutdown (); 2923 global_ret = EXIT_INVALIDARGUMENT; 2924 return; 2925 } 2926 if (GNUNET_OK != 2927 load_offline_key (GNUNET_NO)) 2928 return; 2929 TALER_exchange_offline_signkey_revoke_sign (&exchange_pub, 2930 &master_priv, 2931 &master_sig); 2932 output_operation (OP_REVOKE_SIGNKEY, 2933 GNUNET_JSON_PACK ( 2934 GNUNET_JSON_pack_data_auto ("exchange_pub", 2935 &exchange_pub), 2936 GNUNET_JSON_pack_data_auto ("master_sig", 2937 &master_sig))); 2938 next (args + 1); 2939 } 2940 2941 2942 /** 2943 * Add auditor. 2944 * 2945 * @param args the array of command-line arguments to process next; 2946 * args[0] must be the auditor's public key, args[1] the auditor's 2947 * API base URL, and args[2] the auditor's name. 2948 */ 2949 static void 2950 do_add_auditor (char *const *args) 2951 { 2952 struct TALER_MasterSignatureP master_sig; 2953 struct TALER_AuditorPublicKeyP auditor_pub; 2954 struct GNUNET_TIME_Timestamp now; 2955 2956 if (NULL != in) 2957 { 2958 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 2959 "Downloaded data was not consumed, not adding auditor\n"); 2960 GNUNET_SCHEDULER_shutdown (); 2961 global_ret = EXIT_FAILURE; 2962 return; 2963 } 2964 if ( (NULL == args[0]) || 2965 (GNUNET_OK != 2966 GNUNET_STRINGS_string_to_data (args[0], 2967 strlen (args[0]), 2968 &auditor_pub, 2969 sizeof (auditor_pub))) ) 2970 { 2971 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 2972 "You must specify an auditor public key as first argument for this subcommand\n"); 2973 GNUNET_SCHEDULER_shutdown (); 2974 global_ret = EXIT_INVALIDARGUMENT; 2975 return; 2976 } 2977 2978 if ( (NULL == args[1]) || 2979 (0 != strncmp ("http", 2980 args[1], 2981 strlen ("http"))) || 2982 (NULL == args[2]) ) 2983 { 2984 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 2985 "You must specify an auditor URI and auditor name as 2nd and 3rd arguments to this subcommand\n"); 2986 GNUNET_SCHEDULER_shutdown (); 2987 global_ret = EXIT_INVALIDARGUMENT; 2988 return; 2989 } 2990 if (GNUNET_OK != 2991 load_offline_key (GNUNET_NO)) 2992 return; 2993 now = GNUNET_TIME_timestamp_get (); 2994 TALER_exchange_offline_auditor_add_sign (&auditor_pub, 2995 args[1], 2996 now, 2997 &master_priv, 2998 &master_sig); 2999 output_operation (OP_ENABLE_AUDITOR, 3000 GNUNET_JSON_PACK ( 3001 GNUNET_JSON_pack_string ("auditor_url", 3002 args[1]), 3003 GNUNET_JSON_pack_string ("auditor_name", 3004 args[2]), 3005 GNUNET_JSON_pack_timestamp ("validity_start", 3006 now), 3007 GNUNET_JSON_pack_data_auto ("auditor_pub", 3008 &auditor_pub), 3009 GNUNET_JSON_pack_data_auto ("master_sig", 3010 &master_sig))); 3011 next (args + 3); 3012 } 3013 3014 3015 /** 3016 * Disable auditor account. 3017 * 3018 * @param args the array of command-line arguments to process next; 3019 * args[0] must be the hash of the denomination key to revoke 3020 */ 3021 static void 3022 do_del_auditor (char *const *args) 3023 { 3024 struct TALER_MasterSignatureP master_sig; 3025 struct TALER_AuditorPublicKeyP auditor_pub; 3026 struct GNUNET_TIME_Timestamp now; 3027 3028 if (NULL != in) 3029 { 3030 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 3031 "Downloaded data was not consumed, not deleting auditor account\n"); 3032 GNUNET_SCHEDULER_shutdown (); 3033 global_ret = EXIT_FAILURE; 3034 return; 3035 } 3036 if ( (NULL == args[0]) || 3037 (GNUNET_OK != 3038 GNUNET_STRINGS_string_to_data (args[0], 3039 strlen (args[0]), 3040 &auditor_pub, 3041 sizeof (auditor_pub))) ) 3042 { 3043 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 3044 "You must specify an auditor public key as first argument for this subcommand\n"); 3045 GNUNET_SCHEDULER_shutdown (); 3046 global_ret = EXIT_INVALIDARGUMENT; 3047 return; 3048 } 3049 if (GNUNET_OK != 3050 load_offline_key (GNUNET_NO)) 3051 return; 3052 now = GNUNET_TIME_timestamp_get (); 3053 TALER_exchange_offline_auditor_del_sign (&auditor_pub, 3054 now, 3055 &master_priv, 3056 &master_sig); 3057 output_operation (OP_DISABLE_AUDITOR, 3058 GNUNET_JSON_PACK ( 3059 GNUNET_JSON_pack_data_auto ("auditor_pub", 3060 &auditor_pub), 3061 GNUNET_JSON_pack_timestamp ("validity_end", 3062 now), 3063 GNUNET_JSON_pack_data_auto ("master_sig", 3064 &master_sig))); 3065 next (args + 1); 3066 } 3067 3068 3069 /** 3070 * Parse account restriction. 3071 * 3072 * @param args the array of command-line arguments to process next 3073 * @param[in,out] restrictions JSON array to update 3074 * @return -1 on error, otherwise number of arguments from @a args that were used 3075 */ 3076 static int 3077 parse_restriction (char *const *args, 3078 json_t *restrictions) 3079 { 3080 if (NULL == args[0]) 3081 { 3082 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 3083 "Restriction TYPE argument missing\n"); 3084 return -1; 3085 } 3086 if (0 == strcmp (args[0], 3087 "deny")) 3088 { 3089 GNUNET_assert (0 == 3090 json_array_append_new ( 3091 restrictions, 3092 GNUNET_JSON_PACK ( 3093 GNUNET_JSON_pack_string ("type", 3094 "deny")))); 3095 return 1; 3096 } 3097 if (0 == strcmp (args[0], 3098 "regex")) 3099 { 3100 json_t *i18n; 3101 json_error_t err; 3102 3103 if ( (NULL == args[1]) || 3104 (NULL == args[2]) || 3105 (NULL == args[3]) ) 3106 { 3107 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 3108 "Mandatory arguments for restriction of type `regex' missing (REGEX, HINT, HINT-I18 required)\n"); 3109 return -1; 3110 } 3111 { 3112 regex_t ex; 3113 3114 if (0 != regcomp (&ex, 3115 args[1], 3116 REG_NOSUB | REG_EXTENDED)) 3117 { 3118 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 3119 "Invalid regular expression `%s'\n", 3120 args[1]); 3121 return -1; 3122 } 3123 regfree (&ex); 3124 } 3125 3126 i18n = json_loads (args[3], 3127 JSON_REJECT_DUPLICATES, 3128 &err); 3129 if (NULL == i18n) 3130 { 3131 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 3132 "Invalid JSON for restriction of type `regex': `%s` at %d\n", 3133 args[3], 3134 err.position); 3135 return -1; 3136 } 3137 GNUNET_assert (0 == 3138 json_array_append_new ( 3139 restrictions, 3140 GNUNET_JSON_PACK ( 3141 GNUNET_JSON_pack_string ("type", 3142 "regex"), 3143 GNUNET_JSON_pack_string ("payto_regex", 3144 args[1]), 3145 GNUNET_JSON_pack_string ("human_hint", 3146 args[2]), 3147 GNUNET_JSON_pack_object_steal ("human_hint_i18n", 3148 i18n) 3149 ))); 3150 return 4; 3151 } 3152 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 3153 "Restriction TYPE `%s' unsupported\n", 3154 args[0]); 3155 return -1; 3156 } 3157 3158 3159 /** 3160 * Add wire account. 3161 * 3162 * @param args the array of command-line arguments to process next; 3163 * args[0] must be the hash of the denomination key to revoke 3164 */ 3165 static void 3166 do_add_wire (char *const *args) 3167 { 3168 struct TALER_MasterSignatureP master_sig_add; 3169 struct TALER_MasterSignatureP master_sig_wire; 3170 struct GNUNET_TIME_Timestamp now; 3171 const char *conversion_url = NULL; 3172 const char *bank_label = NULL; 3173 int64_t priority = 0; 3174 json_t *debit_restrictions; 3175 json_t *credit_restrictions; 3176 unsigned int num_args = 1; 3177 struct TALER_FullPayto payto_uri = { 3178 .full_payto = args[0] 3179 }; 3180 3181 if (NULL != in) 3182 { 3183 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 3184 "Downloaded data was not consumed, not adding wire account\n"); 3185 GNUNET_SCHEDULER_shutdown (); 3186 global_ret = EXIT_FAILURE; 3187 return; 3188 } 3189 if (NULL == args[0]) 3190 { 3191 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 3192 "You must specify a payto://-URI with this subcommand\n"); 3193 GNUNET_SCHEDULER_shutdown (); 3194 global_ret = EXIT_INVALIDARGUMENT; 3195 return; 3196 } 3197 if (GNUNET_OK != 3198 load_offline_key (GNUNET_NO)) 3199 return; 3200 { 3201 char *msg = TALER_payto_validate (payto_uri); 3202 3203 if (NULL != msg) 3204 { 3205 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 3206 "payto URI `%s' is malformed: %s\n", 3207 payto_uri.full_payto, 3208 msg); 3209 GNUNET_free (msg); 3210 GNUNET_SCHEDULER_shutdown (); 3211 global_ret = EXIT_INVALIDARGUMENT; 3212 return; 3213 } 3214 } 3215 now = GNUNET_TIME_timestamp_get (); 3216 { 3217 char *wire_method; 3218 3219 wire_method = TALER_payto_get_method (payto_uri.full_payto); 3220 if (NULL == wire_method) 3221 { 3222 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 3223 "payto:// URI `%s' is malformed\n", 3224 payto_uri.full_payto); 3225 global_ret = EXIT_INVALIDARGUMENT; 3226 GNUNET_SCHEDULER_shutdown (); 3227 return; 3228 } 3229 GNUNET_free (wire_method); 3230 } 3231 debit_restrictions = json_array (); 3232 GNUNET_assert (NULL != debit_restrictions); 3233 credit_restrictions = json_array (); 3234 GNUNET_assert (NULL != credit_restrictions); 3235 while (NULL != args[num_args]) 3236 { 3237 if (0 == strcmp (args[num_args], 3238 "conversion-url")) 3239 { 3240 num_args++; 3241 conversion_url = args[num_args]; 3242 if (NULL == conversion_url) 3243 { 3244 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 3245 "'conversion-url' requires an argument\n"); 3246 global_ret = EXIT_INVALIDARGUMENT; 3247 GNUNET_SCHEDULER_shutdown (); 3248 json_decref (debit_restrictions); 3249 json_decref (credit_restrictions); 3250 return; 3251 } 3252 if (! TALER_is_web_url (conversion_url)) 3253 { 3254 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 3255 "'conversion-url' must refer to HTTP(S) endpoint, `%s' is invalid\n", 3256 conversion_url); 3257 global_ret = EXIT_INVALIDARGUMENT; 3258 GNUNET_SCHEDULER_shutdown (); 3259 json_decref (debit_restrictions); 3260 json_decref (credit_restrictions); 3261 return; 3262 } 3263 num_args++; 3264 continue; 3265 } 3266 if (0 == strcmp (args[num_args], 3267 "credit-restriction")) 3268 { 3269 int iret; 3270 3271 num_args++; 3272 iret = parse_restriction (&args[num_args], 3273 credit_restrictions); 3274 if (iret <= 0) 3275 { 3276 global_ret = EXIT_INVALIDARGUMENT; 3277 GNUNET_SCHEDULER_shutdown (); 3278 json_decref (debit_restrictions); 3279 json_decref (credit_restrictions); 3280 return; 3281 } 3282 num_args += iret; 3283 continue; 3284 } 3285 if (0 == strcmp (args[num_args], 3286 "debit-restriction")) 3287 { 3288 int iret; 3289 3290 num_args++; 3291 iret = parse_restriction (&args[num_args], 3292 debit_restrictions); 3293 if (iret <= 0) 3294 { 3295 global_ret = EXIT_INVALIDARGUMENT; 3296 GNUNET_SCHEDULER_shutdown (); 3297 json_decref (debit_restrictions); 3298 json_decref (credit_restrictions); 3299 return; 3300 } 3301 num_args += iret; 3302 continue; 3303 } 3304 if (0 == strcmp (args[num_args], 3305 "display-hint")) 3306 { 3307 long long p; 3308 char dummy; 3309 3310 num_args++; 3311 if ( (NULL == args[num_args]) || 3312 (NULL == args[num_args + 1]) ) 3313 { 3314 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 3315 "'display-hint' requires at least two arguments (priority and label)\n"); 3316 global_ret = EXIT_INVALIDARGUMENT; 3317 GNUNET_SCHEDULER_shutdown (); 3318 json_decref (debit_restrictions); 3319 json_decref (credit_restrictions); 3320 return; 3321 } 3322 if (1 != sscanf (args[num_args], 3323 "%lld%c", 3324 &p, 3325 &dummy)) 3326 { 3327 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 3328 "Priority argument `%s' is not a number\n", 3329 args[num_args]); 3330 global_ret = EXIT_INVALIDARGUMENT; 3331 GNUNET_SCHEDULER_shutdown (); 3332 json_decref (debit_restrictions); 3333 json_decref (credit_restrictions); 3334 return; 3335 } 3336 priority = (int64_t) p; 3337 num_args++; 3338 bank_label = args[num_args]; 3339 num_args++; 3340 continue; 3341 } 3342 break; 3343 } 3344 TALER_exchange_offline_wire_add_sign (payto_uri, 3345 conversion_url, 3346 debit_restrictions, 3347 credit_restrictions, 3348 now, 3349 &master_priv, 3350 &master_sig_add); 3351 TALER_exchange_wire_signature_make (payto_uri, 3352 conversion_url, 3353 debit_restrictions, 3354 credit_restrictions, 3355 &master_priv, 3356 &master_sig_wire); 3357 output_operation (OP_ENABLE_WIRE, 3358 GNUNET_JSON_PACK ( 3359 TALER_JSON_pack_full_payto ("payto_uri", 3360 payto_uri), 3361 GNUNET_JSON_pack_array_steal ("debit_restrictions", 3362 debit_restrictions), 3363 GNUNET_JSON_pack_array_steal ("credit_restrictions", 3364 credit_restrictions), 3365 GNUNET_JSON_pack_allow_null ( 3366 GNUNET_JSON_pack_string ("conversion_url", 3367 conversion_url)), 3368 GNUNET_JSON_pack_allow_null ( 3369 GNUNET_JSON_pack_string ("bank_label", 3370 bank_label)), 3371 GNUNET_JSON_pack_int64 ("priority", 3372 priority), 3373 GNUNET_JSON_pack_timestamp ("validity_start", 3374 now), 3375 GNUNET_JSON_pack_data_auto ("master_sig_add", 3376 &master_sig_add), 3377 GNUNET_JSON_pack_data_auto ("master_sig_wire", 3378 &master_sig_wire))); 3379 next (args + num_args); 3380 } 3381 3382 3383 /** 3384 * Disable wire account. 3385 * 3386 * @param args the array of command-line arguments to process next; 3387 * args[0] must be the payto URI of the account to disable 3388 */ 3389 static void 3390 do_del_wire (char *const *args) 3391 { 3392 struct TALER_MasterSignatureP master_sig; 3393 struct GNUNET_TIME_Timestamp now; 3394 struct TALER_FullPayto payto_uri = { 3395 .full_payto = args[0] 3396 }; 3397 3398 if (NULL != in) 3399 { 3400 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 3401 "Downloaded data was not consumed, not deleting wire account\n") 3402 ; 3403 GNUNET_SCHEDULER_shutdown (); 3404 global_ret = EXIT_FAILURE; 3405 return; 3406 } 3407 if (NULL == args[0]) 3408 { 3409 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 3410 "You must specify a payto://-URI with this subcommand\n"); 3411 GNUNET_SCHEDULER_shutdown (); 3412 global_ret = EXIT_INVALIDARGUMENT; 3413 return; 3414 } 3415 if (GNUNET_OK != 3416 load_offline_key (GNUNET_NO)) 3417 return; 3418 now = GNUNET_TIME_timestamp_get (); 3419 TALER_exchange_offline_wire_del_sign (payto_uri, 3420 now, 3421 &master_priv, 3422 &master_sig); 3423 output_operation (OP_DISABLE_WIRE, 3424 GNUNET_JSON_PACK ( 3425 TALER_JSON_pack_full_payto ("payto_uri", 3426 payto_uri), 3427 GNUNET_JSON_pack_timestamp ("validity_end", 3428 now), 3429 GNUNET_JSON_pack_data_auto ("master_sig", 3430 &master_sig))); 3431 next (args + 1); 3432 } 3433 3434 3435 /** 3436 * Set wire fees for the given year. 3437 * 3438 * @param args the array of command-line arguments to process next; 3439 * args[0] must be the year, args[1] the wire method, args[2] the wire fee and args[3] 3440 * the closing fee. 3441 */ 3442 static void 3443 do_set_wire_fee (char *const *args) 3444 { 3445 struct TALER_MasterSignatureP master_sig; 3446 char dummy; 3447 unsigned int year; 3448 struct TALER_WireFeeSet fees; 3449 struct GNUNET_TIME_Timestamp start_time; 3450 struct GNUNET_TIME_Timestamp end_time; 3451 3452 if (NULL != in) 3453 { 3454 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 3455 "Downloaded data was not consumed, not setting wire fee\n"); 3456 GNUNET_SCHEDULER_shutdown (); 3457 global_ret = EXIT_FAILURE; 3458 return; 3459 } 3460 if ( (NULL == args[0]) || 3461 (NULL == args[1]) || 3462 (NULL == args[2]) || 3463 (NULL == args[3]) || 3464 ( (1 != sscanf (args[0], 3465 "%u%c", 3466 &year, 3467 &dummy)) && 3468 (0 != strcasecmp ("now", 3469 args[0])) ) || 3470 (GNUNET_OK != 3471 TALER_string_to_amount (args[2], 3472 &fees.wire)) || 3473 (GNUNET_OK != 3474 TALER_string_to_amount (args[3], 3475 &fees.closing)) ) 3476 { 3477 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 3478 "You must use YEAR, METHOD, WIRE-FEE, and CLOSING-FEE as arguments for this subcommand\n"); 3479 GNUNET_SCHEDULER_shutdown (); 3480 global_ret = EXIT_INVALIDARGUMENT; 3481 return; 3482 } 3483 if (0 == strcasecmp ("now", 3484 args[0])) 3485 year = GNUNET_TIME_get_current_year (); 3486 if (GNUNET_OK != 3487 load_offline_key (GNUNET_NO)) 3488 return; 3489 start_time = GNUNET_TIME_absolute_to_timestamp ( 3490 GNUNET_TIME_year_to_time (year)); 3491 end_time = GNUNET_TIME_absolute_to_timestamp ( 3492 GNUNET_TIME_year_to_time (year + 1)); 3493 3494 TALER_exchange_offline_wire_fee_sign (args[1], 3495 start_time, 3496 end_time, 3497 &fees, 3498 &master_priv, 3499 &master_sig); 3500 output_operation (OP_SET_WIRE_FEE, 3501 GNUNET_JSON_PACK ( 3502 GNUNET_JSON_pack_string ("wire_method", 3503 args[1]), 3504 GNUNET_JSON_pack_timestamp ("start_time", 3505 start_time), 3506 GNUNET_JSON_pack_timestamp ("end_time", 3507 end_time), 3508 TALER_JSON_pack_amount ("wire_fee", 3509 &fees.wire), 3510 TALER_JSON_pack_amount ("closing_fee", 3511 &fees.closing), 3512 GNUNET_JSON_pack_data_auto ("master_sig", 3513 &master_sig))); 3514 next (args + 4); 3515 } 3516 3517 3518 /** 3519 * Set global fees for the given year. 3520 * 3521 * @param args the array of command-line arguments to process next; 3522 * args[0] must be the year, args[1] the history fee, args[2] 3523 * the account fee and args[3] the purse fee. These are followed by args[4] purse timeout, 3524 * args[5] history expiration. Last is args[6] the (free) purse account limit. 3525 */ 3526 static void 3527 do_set_global_fee (char *const *args) 3528 { 3529 struct TALER_MasterSignatureP master_sig; 3530 char dummy; 3531 unsigned int year; 3532 struct TALER_GlobalFeeSet fees; 3533 struct GNUNET_TIME_Relative purse_timeout; 3534 struct GNUNET_TIME_Relative history_expiration; 3535 unsigned int purse_account_limit; 3536 struct GNUNET_TIME_Timestamp start_time; 3537 struct GNUNET_TIME_Timestamp end_time; 3538 3539 if (NULL != in) 3540 { 3541 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 3542 "Downloaded data was not consumed, not setting global fee\n"); 3543 GNUNET_SCHEDULER_shutdown (); 3544 global_ret = EXIT_FAILURE; 3545 return; 3546 } 3547 if ( (NULL == args[0]) || 3548 (NULL == args[1]) || 3549 (NULL == args[2]) || 3550 (NULL == args[3]) || 3551 (NULL == args[4]) || 3552 (NULL == args[5]) || 3553 (NULL == args[6]) ) 3554 { 3555 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 3556 "You must use YEAR, HISTORY-FEE, ACCOUNT-FEE, PURSE-FEE, PURSE-TIMEOUT, HISTORY-EXPIRATION and PURSE-ACCOUNT-LIMIT as arguments for this subcommand\n"); 3557 GNUNET_SCHEDULER_shutdown (); 3558 global_ret = EXIT_INVALIDARGUMENT; 3559 return; 3560 } 3561 if ( (1 != sscanf (args[0], 3562 "%u%c", 3563 &year, 3564 &dummy)) && 3565 (0 != strcasecmp ("now", 3566 args[0])) ) 3567 { 3568 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 3569 "Invalid YEAR given for 'global-fee' subcommand\n"); 3570 GNUNET_SCHEDULER_shutdown (); 3571 global_ret = EXIT_INVALIDARGUMENT; 3572 return; 3573 } 3574 if ( (GNUNET_OK != 3575 TALER_string_to_amount (args[1], 3576 &fees.history)) || 3577 (GNUNET_OK != 3578 TALER_string_to_amount (args[2], 3579 &fees.account)) || 3580 (GNUNET_OK != 3581 TALER_string_to_amount (args[3], 3582 &fees.purse)) ) 3583 { 3584 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 3585 "Invalid amount given for 'global-fee' subcommand\n"); 3586 GNUNET_SCHEDULER_shutdown (); 3587 global_ret = EXIT_INVALIDARGUMENT; 3588 return; 3589 } 3590 if (GNUNET_OK != 3591 GNUNET_STRINGS_fancy_time_to_relative (args[4], 3592 &purse_timeout)) 3593 { 3594 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 3595 "Invalid purse timeout `%s' given for 'global-fee' subcommand\n", 3596 args[4]); 3597 GNUNET_SCHEDULER_shutdown (); 3598 global_ret = EXIT_INVALIDARGUMENT; 3599 return; 3600 } 3601 if (GNUNET_OK != 3602 GNUNET_STRINGS_fancy_time_to_relative (args[5], 3603 &history_expiration)) 3604 { 3605 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 3606 "Invalid history expiratoin `%s' given for 'global-fee' subcommand\n", 3607 args[5]); 3608 GNUNET_SCHEDULER_shutdown (); 3609 global_ret = EXIT_INVALIDARGUMENT; 3610 return; 3611 } 3612 if (1 != sscanf (args[6], 3613 "%u%c", 3614 &purse_account_limit, 3615 &dummy)) 3616 { 3617 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 3618 "Invalid purse account limit given for 'global-fee' subcommand\n"); 3619 GNUNET_SCHEDULER_shutdown (); 3620 global_ret = EXIT_INVALIDARGUMENT; 3621 return; 3622 } 3623 if (0 == strcasecmp ("now", 3624 args[0])) 3625 year = GNUNET_TIME_get_current_year (); 3626 if (GNUNET_OK != 3627 load_offline_key (GNUNET_NO)) 3628 return; 3629 start_time = GNUNET_TIME_absolute_to_timestamp ( 3630 GNUNET_TIME_year_to_time (year)); 3631 end_time = GNUNET_TIME_absolute_to_timestamp ( 3632 GNUNET_TIME_year_to_time (year + 1)); 3633 3634 TALER_exchange_offline_global_fee_sign (start_time, 3635 end_time, 3636 &fees, 3637 purse_timeout, 3638 history_expiration, 3639 (uint32_t) purse_account_limit, 3640 &master_priv, 3641 &master_sig); 3642 output_operation (OP_SET_GLOBAL_FEE, 3643 GNUNET_JSON_PACK ( 3644 GNUNET_JSON_pack_timestamp ("start_time", 3645 start_time), 3646 GNUNET_JSON_pack_timestamp ("end_time", 3647 end_time), 3648 TALER_JSON_pack_amount ("history_fee", 3649 &fees.history), 3650 TALER_JSON_pack_amount ("account_fee", 3651 &fees.account), 3652 TALER_JSON_pack_amount ("purse_fee", 3653 &fees.purse), 3654 GNUNET_JSON_pack_time_rel ("purse_timeout", 3655 purse_timeout), 3656 GNUNET_JSON_pack_time_rel ("history_expiration", 3657 history_expiration), 3658 GNUNET_JSON_pack_uint64 ("purse_account_limit", 3659 (uint32_t) purse_account_limit), 3660 GNUNET_JSON_pack_data_auto ("master_sig", 3661 &master_sig))); 3662 next (args + 7); 3663 } 3664 3665 3666 /** 3667 * Drain profits from exchange's escrow account to 3668 * regular exchange account. 3669 * 3670 * @param args the array of command-line arguments to process next; 3671 * args[0] must be the amount, 3672 * args[1] must be the section of the escrow account to drain 3673 * args[2] must be the payto://-URI of the target account 3674 */ 3675 static void 3676 do_drain (char *const *args) 3677 { 3678 struct TALER_WireTransferIdentifierRawP wtid; 3679 struct GNUNET_TIME_Timestamp date; 3680 struct TALER_Amount amount; 3681 const char *account_section; 3682 struct TALER_FullPayto payto_uri; 3683 struct TALER_MasterSignatureP master_sig; 3684 char *err; 3685 3686 if (NULL != in) 3687 { 3688 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 3689 "Downloaded data was not consumed, refusing drain\n"); 3690 GNUNET_SCHEDULER_shutdown (); 3691 global_ret = EXIT_FAILURE; 3692 return; 3693 } 3694 if ( (NULL == args[0]) || 3695 (NULL == args[1]) || 3696 (NULL == args[2]) || 3697 (GNUNET_OK != 3698 TALER_string_to_amount (args[0], 3699 &amount)) ) 3700 { 3701 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 3702 "Drain requires an amount, section name and target payto://-URI as arguments\n"); 3703 GNUNET_SCHEDULER_shutdown (); 3704 global_ret = EXIT_INVALIDARGUMENT; 3705 return; 3706 } 3707 if ( (NULL == args[0]) || 3708 (NULL == args[1]) || 3709 (NULL == args[2]) ) 3710 { 3711 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 3712 "Drain requires an amount, section name and target payto://-URI as arguments\n"); 3713 GNUNET_SCHEDULER_shutdown (); 3714 global_ret = EXIT_INVALIDARGUMENT; 3715 return; 3716 } 3717 if (GNUNET_OK != 3718 TALER_string_to_amount (args[0], 3719 &amount)) 3720 { 3721 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 3722 "Invalid amount `%s' specified for drain\n", 3723 args[0]); 3724 GNUNET_SCHEDULER_shutdown (); 3725 global_ret = EXIT_INVALIDARGUMENT; 3726 return; 3727 } 3728 account_section = args[1]; 3729 payto_uri.full_payto = args[2]; 3730 err = TALER_payto_validate (payto_uri); 3731 if (NULL != err) 3732 { 3733 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 3734 "Invalid payto://-URI `%s' specified for drain: %s\n", 3735 payto_uri.full_payto, 3736 err); 3737 GNUNET_free (err); 3738 GNUNET_SCHEDULER_shutdown (); 3739 global_ret = EXIT_INVALIDARGUMENT; 3740 return; 3741 } 3742 if (GNUNET_OK != 3743 load_offline_key (GNUNET_NO)) 3744 return; 3745 GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_NONCE, 3746 &wtid, 3747 sizeof (wtid)); 3748 date = GNUNET_TIME_timestamp_get (); 3749 TALER_exchange_offline_profit_drain_sign (&wtid, 3750 date, 3751 &amount, 3752 account_section, 3753 payto_uri, 3754 &master_priv, 3755 &master_sig); 3756 output_operation (OP_DRAIN_PROFITS, 3757 GNUNET_JSON_PACK ( 3758 GNUNET_JSON_pack_data_auto ("wtid", 3759 &wtid), 3760 GNUNET_JSON_pack_string ("account_section", 3761 account_section), 3762 TALER_JSON_pack_full_payto ("payto_uri", 3763 payto_uri), 3764 TALER_JSON_pack_amount ("amount", 3765 &amount), 3766 GNUNET_JSON_pack_timestamp ("date", 3767 date), 3768 GNUNET_JSON_pack_data_auto ("master_sig", 3769 &master_sig))); 3770 next (args + 3); 3771 } 3772 3773 3774 /** 3775 * Add partner. 3776 * 3777 * @param args the array of command-line arguments to process next; 3778 * args[0] must be the partner's master public key, args[1] the partner's 3779 * API base URL, args[2] the wad fee, args[3] the wad frequency, and 3780 * args[4] the year (including possibly 'now') 3781 */ 3782 static void 3783 do_add_partner (char *const *args) 3784 { 3785 struct TALER_MasterPublicKeyP partner_pub; 3786 struct GNUNET_TIME_Timestamp start_date; 3787 struct GNUNET_TIME_Timestamp end_date; 3788 struct GNUNET_TIME_Relative wad_frequency; 3789 struct TALER_Amount wad_fee; 3790 const char *partner_base_url; 3791 struct TALER_MasterSignatureP master_sig; 3792 char dummy; 3793 unsigned int year; 3794 3795 if (NULL != in) 3796 { 3797 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 3798 "Downloaded data was not consumed, not adding partner\n"); 3799 GNUNET_SCHEDULER_shutdown (); 3800 global_ret = EXIT_FAILURE; 3801 return; 3802 } 3803 if ( (NULL == args[0]) || 3804 (GNUNET_OK != 3805 GNUNET_STRINGS_string_to_data (args[0], 3806 strlen (args[0]), 3807 &partner_pub, 3808 sizeof (partner_pub))) ) 3809 { 3810 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 3811 "You must specify the partner master public key as first argument for this subcommand\n"); 3812 GNUNET_SCHEDULER_shutdown (); 3813 global_ret = EXIT_INVALIDARGUMENT; 3814 return; 3815 } 3816 if ( (NULL == args[1]) || 3817 (0 != strncmp ("http", 3818 args[1], 3819 strlen ("http"))) ) 3820 { 3821 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 3822 "You must specify the partner's base URL as the 2nd argument to this subcommand\n"); 3823 GNUNET_SCHEDULER_shutdown (); 3824 global_ret = EXIT_INVALIDARGUMENT; 3825 return; 3826 } 3827 partner_base_url = args[1]; 3828 if ( (NULL == args[2]) || 3829 (GNUNET_OK != 3830 TALER_string_to_amount (args[2], 3831 &wad_fee)) ) 3832 { 3833 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 3834 "Invalid amount `%s' specified for wad fee of partner\n", 3835 args[2]); 3836 GNUNET_SCHEDULER_shutdown (); 3837 global_ret = EXIT_INVALIDARGUMENT; 3838 return; 3839 } 3840 if ( (NULL == args[3]) || 3841 (GNUNET_OK != 3842 GNUNET_STRINGS_fancy_time_to_relative (args[3], 3843 &wad_frequency)) ) 3844 { 3845 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 3846 "Invalid wad frequency `%s' specified for add partner\n", 3847 args[3]); 3848 GNUNET_SCHEDULER_shutdown (); 3849 global_ret = EXIT_INVALIDARGUMENT; 3850 return; 3851 } 3852 if ( (NULL == args[4]) || 3853 ( (1 != sscanf (args[4], 3854 "%u%c", 3855 &year, 3856 &dummy)) && 3857 (0 != strcasecmp ("now", 3858 args[4])) ) ) 3859 { 3860 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 3861 "Invalid year `%s' specified for add partner\n", 3862 args[4]); 3863 GNUNET_SCHEDULER_shutdown (); 3864 global_ret = EXIT_INVALIDARGUMENT; 3865 return; 3866 } 3867 if (0 == strcasecmp ("now", 3868 args[4])) 3869 year = GNUNET_TIME_get_current_year (); 3870 start_date = GNUNET_TIME_absolute_to_timestamp ( 3871 GNUNET_TIME_year_to_time (year)); 3872 end_date = GNUNET_TIME_absolute_to_timestamp ( 3873 GNUNET_TIME_year_to_time (year + 1)); 3874 3875 if (GNUNET_OK != 3876 load_offline_key (GNUNET_NO)) 3877 return; 3878 TALER_exchange_offline_partner_details_sign (&partner_pub, 3879 start_date, 3880 end_date, 3881 wad_frequency, 3882 &wad_fee, 3883 partner_base_url, 3884 &master_priv, 3885 &master_sig); 3886 output_operation (OP_ADD_PARTNER, 3887 GNUNET_JSON_PACK ( 3888 GNUNET_JSON_pack_string ("partner_base_url", 3889 partner_base_url), 3890 GNUNET_JSON_pack_time_rel ("wad_frequency", 3891 wad_frequency), 3892 GNUNET_JSON_pack_timestamp ("start_date", 3893 start_date), 3894 GNUNET_JSON_pack_timestamp ("end_date", 3895 end_date), 3896 GNUNET_JSON_pack_data_auto ("partner_pub", 3897 &partner_pub), 3898 GNUNET_JSON_pack_data_auto ("master_sig", 3899 &master_sig))); 3900 next (args + 5); 3901 } 3902 3903 3904 /** 3905 * Enable or disable AML staff. 3906 * 3907 * @param is_active true to enable, false to disable 3908 * @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) 3909 */ 3910 static void 3911 do_set_aml_staff (bool is_active, 3912 char *const *args) 3913 { 3914 struct TALER_AmlOfficerPublicKeyP officer_pub; 3915 const char *officer_name; 3916 bool read_only; 3917 struct TALER_MasterSignatureP master_sig; 3918 struct GNUNET_TIME_Timestamp now 3919 = GNUNET_TIME_timestamp_get (); 3920 3921 if (NULL != in) 3922 { 3923 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 3924 "Downloaded data was not consumed, not updating AML staff status\n"); 3925 GNUNET_SCHEDULER_shutdown (); 3926 global_ret = EXIT_FAILURE; 3927 return; 3928 } 3929 if ( (NULL == args[0]) || 3930 (GNUNET_OK != 3931 GNUNET_STRINGS_string_to_data (args[0], 3932 strlen (args[0]), 3933 &officer_pub, 3934 sizeof (officer_pub))) ) 3935 { 3936 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 3937 "You must specify the AML officer's public key as first argument for this subcommand\n"); 3938 GNUNET_SCHEDULER_shutdown (); 3939 global_ret = EXIT_INVALIDARGUMENT; 3940 return; 3941 } 3942 if (NULL == args[1]) 3943 { 3944 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 3945 "You must specify the officer's legal name as the 2nd argument to this subcommand\n"); 3946 GNUNET_SCHEDULER_shutdown (); 3947 global_ret = EXIT_INVALIDARGUMENT; 3948 return; 3949 } 3950 officer_name = args[1]; 3951 if (is_active) 3952 { 3953 if ( (NULL == args[2]) || 3954 ( (0 != strcmp (args[2], 3955 "ro")) && 3956 (0 != strcmp (args[2], 3957 "rw")) ) ) 3958 { 3959 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 3960 "You must specify 'ro' or 'rw' (and not `%s') for the access level\n", 3961 args[2]); 3962 GNUNET_SCHEDULER_shutdown (); 3963 global_ret = EXIT_INVALIDARGUMENT; 3964 return; 3965 } 3966 read_only = (0 == strcmp (args[2], 3967 "ro")); 3968 } 3969 else 3970 { 3971 read_only = true; 3972 } 3973 if (GNUNET_OK != 3974 load_offline_key (GNUNET_NO)) 3975 return; 3976 TALER_exchange_offline_aml_officer_status_sign (&officer_pub, 3977 officer_name, 3978 now, 3979 is_active, 3980 read_only, 3981 &master_priv, 3982 &master_sig); 3983 output_operation (OP_UPDATE_AML_STAFF, 3984 GNUNET_JSON_PACK ( 3985 GNUNET_JSON_pack_string ("officer_name", 3986 officer_name), 3987 GNUNET_JSON_pack_timestamp ("change_date", 3988 now), 3989 GNUNET_JSON_pack_bool ("is_active", 3990 is_active), 3991 GNUNET_JSON_pack_bool ("read_only", 3992 read_only), 3993 GNUNET_JSON_pack_data_auto ("officer_pub", 3994 &officer_pub), 3995 GNUNET_JSON_pack_data_auto ("master_sig", 3996 &master_sig))); 3997 next (args + (is_active ? 3 : 2)); 3998 } 3999 4000 4001 /** 4002 * Disable AML staff. 4003 * 4004 * @param args the array of command-line arguments to process next; 4005 * 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) 4006 */ 4007 static void 4008 disable_aml_staff (char *const *args) 4009 { 4010 do_set_aml_staff (false, 4011 args); 4012 } 4013 4014 4015 /** 4016 * Enable AML staff. 4017 * 4018 * @param args the array of command-line arguments to process next; 4019 * 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) 4020 */ 4021 static void 4022 enable_aml_staff (char *const *args) 4023 { 4024 do_set_aml_staff (true, 4025 args); 4026 } 4027 4028 4029 /** 4030 * Function called with information about future keys. Dumps the JSON output 4031 * (on success), either into an internal buffer or to stdout (depending on 4032 * whether there are subsequent commands). 4033 * 4034 * @param args the remaining args 4035 * @param mgr response data 4036 */ 4037 static void 4038 download_cb (char *const *args, 4039 const struct TALER_EXCHANGE_GetManagementKeysResponse *mgr) 4040 { 4041 const struct TALER_EXCHANGE_HttpResponse *hr = &mgr->hr; 4042 4043 mgkh = NULL; 4044 switch (hr->http_status) 4045 { 4046 case MHD_HTTP_OK: 4047 break; 4048 default: 4049 if (0 != hr->http_status) 4050 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 4051 "Failed to download keys from `%s': %s (HTTP status: %u/%u)\n", 4052 CFG_exchange_url, 4053 hr->hint, 4054 hr->http_status, 4055 (unsigned int) hr->ec); 4056 else 4057 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 4058 "Failed to download keys from `%s' (no HTTP response)\n", 4059 CFG_exchange_url); 4060 GNUNET_SCHEDULER_shutdown (); 4061 global_ret = EXIT_FAILURE; 4062 return; 4063 } 4064 in = GNUNET_JSON_PACK ( 4065 GNUNET_JSON_pack_string ("operation", 4066 OP_INPUT_KEYS), 4067 GNUNET_JSON_pack_object_incref ("arguments", 4068 (json_t *) hr->reply)); 4069 if (NULL == args[0]) 4070 { 4071 json_dumpf (in, 4072 stdout, 4073 JSON_INDENT (2)); 4074 json_decref (in); 4075 in = NULL; 4076 } 4077 next (args); 4078 } 4079 4080 4081 /** 4082 * Download future keys. 4083 * 4084 * @param args the array of command-line arguments to process next 4085 */ 4086 static void 4087 do_download (char *const *args) 4088 { 4089 if ( (NULL == CFG_exchange_url) && 4090 (GNUNET_OK != 4091 GNUNET_CONFIGURATION_get_value_string (kcfg, 4092 "exchange", 4093 "BASE_URL", 4094 &CFG_exchange_url)) ) 4095 { 4096 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, 4097 "exchange", 4098 "BASE_URL"); 4099 GNUNET_SCHEDULER_shutdown (); 4100 global_ret = EXIT_NOTCONFIGURED; 4101 return; 4102 } 4103 mgkh = TALER_EXCHANGE_get_management_keys_create (ctx, 4104 CFG_exchange_url); 4105 TALER_EXCHANGE_get_management_keys_start (mgkh, 4106 &download_cb, 4107 (void *) args); 4108 } 4109 4110 4111 /** 4112 * Check that the security module keys are the same as before. If we had no 4113 * keys in store before, remember them (Trust On First Use). 4114 * 4115 * @param secmset security module keys 4116 * @return #GNUNET_OK if keys match with what we have in store 4117 * #GNUNET_NO if we had nothing in store but now do 4118 * #GNUNET_SYSERR if keys changed from what we remember or other error 4119 */ 4120 static enum GNUNET_GenericReturnValue 4121 tofu_check (const struct TALER_SecurityModulePublicKeySetP *secmset) 4122 { 4123 char *fn; 4124 struct TALER_SecurityModulePublicKeySetP oldset; 4125 ssize_t ret; 4126 4127 if (GNUNET_OK != 4128 GNUNET_CONFIGURATION_get_value_filename (kcfg, 4129 "exchange-offline", 4130 "SECM_TOFU_FILE", 4131 &fn)) 4132 { 4133 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, 4134 "exchange-offline", 4135 "SECM_TOFU_FILE"); 4136 return GNUNET_SYSERR; 4137 } 4138 if (GNUNET_OK == 4139 GNUNET_DISK_file_test (fn)) 4140 { 4141 ret = GNUNET_DISK_fn_read (fn, 4142 &oldset, 4143 sizeof (oldset)); 4144 if (GNUNET_SYSERR != ret) 4145 { 4146 if (ret != sizeof (oldset)) 4147 { 4148 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 4149 "File `%s' corrupt\n", 4150 fn); 4151 GNUNET_free (fn); 4152 return GNUNET_SYSERR; 4153 } 4154 /* TOFU check */ 4155 if (0 != memcmp (&oldset, 4156 secmset, 4157 sizeof (*secmset))) 4158 { 4159 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 4160 "Fatal: security module keys changed (file `%s')!\n", 4161 fn); 4162 GNUNET_free (fn); 4163 return GNUNET_SYSERR; 4164 } 4165 GNUNET_free (fn); 4166 return GNUNET_OK; 4167 } 4168 } 4169 4170 { 4171 char *key; 4172 4173 /* check against SECMOD-keys pinned in configuration */ 4174 if (GNUNET_OK == 4175 GNUNET_CONFIGURATION_get_value_string (kcfg, 4176 "exchange-offline", 4177 "SECM_ESIGN_PUBKEY", 4178 &key)) 4179 { 4180 struct TALER_SecurityModulePublicKeyP k; 4181 4182 if (GNUNET_OK != 4183 GNUNET_STRINGS_string_to_data (key, 4184 strlen (key), 4185 &k, 4186 sizeof (k))) 4187 { 4188 GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, 4189 "exchange-offline", 4190 "SECM_ESIGN_PUBKEY", 4191 "key malformed"); 4192 GNUNET_free (key); 4193 GNUNET_free (fn); 4194 return GNUNET_SYSERR; 4195 } 4196 GNUNET_free (key); 4197 if (0 != 4198 GNUNET_memcmp (&k, 4199 &secmset->eddsa)) 4200 { 4201 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 4202 "ESIGN security module key does not match SECM_ESIGN_PUBKEY in configuration\n"); 4203 GNUNET_free (fn); 4204 return GNUNET_SYSERR; 4205 } 4206 } 4207 if (GNUNET_OK == 4208 GNUNET_CONFIGURATION_get_value_string (kcfg, 4209 "exchange-offline", 4210 "SECM_DENOM_PUBKEY", 4211 &key)) 4212 { 4213 struct TALER_SecurityModulePublicKeyP k; 4214 4215 if (GNUNET_OK != 4216 GNUNET_STRINGS_string_to_data (key, 4217 strlen (key), 4218 &k, 4219 sizeof (k))) 4220 { 4221 GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, 4222 "exchange-offline", 4223 "SECM_DENOM_PUBKEY", 4224 "key malformed"); 4225 GNUNET_free (key); 4226 GNUNET_free (fn); 4227 return GNUNET_SYSERR; 4228 } 4229 GNUNET_free (key); 4230 if (0 != 4231 GNUNET_memcmp (&k, 4232 &secmset->rsa)) 4233 { 4234 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 4235 "DENOM security module key does not match SECM_DENOM_PUBKEY in configuration\n"); 4236 GNUNET_free (fn); 4237 return GNUNET_SYSERR; 4238 } 4239 } 4240 if (GNUNET_OK == 4241 GNUNET_CONFIGURATION_get_value_string (kcfg, 4242 "exchange-offline", 4243 "SECM_DENOM_CS_PUBKEY", 4244 &key)) 4245 { 4246 struct TALER_SecurityModulePublicKeyP k; 4247 4248 if (GNUNET_OK != 4249 GNUNET_STRINGS_string_to_data (key, 4250 strlen (key), 4251 &k, 4252 sizeof (k))) 4253 { 4254 GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, 4255 "exchange-offline", 4256 "SECM_DENOM_CS_PUBKEY", 4257 "key malformed"); 4258 GNUNET_free (key); 4259 GNUNET_free (fn); 4260 return GNUNET_SYSERR; 4261 } 4262 GNUNET_free (key); 4263 if (0 != 4264 GNUNET_memcmp (&k, 4265 &secmset->cs)) 4266 { 4267 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 4268 "DENOM security module key does not match SECM_DENOM_CS_PUBKEY in configuration\n"); 4269 GNUNET_free (fn); 4270 return GNUNET_SYSERR; 4271 } 4272 } 4273 } 4274 if (GNUNET_OK != 4275 GNUNET_DISK_directory_create_for_file (fn)) 4276 { 4277 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 4278 "Failed create directory to store key material in file `%s'\n", 4279 fn); 4280 GNUNET_free (fn); 4281 return GNUNET_SYSERR; 4282 } 4283 /* persist keys for future runs */ 4284 if (GNUNET_OK != 4285 GNUNET_DISK_fn_write (fn, 4286 secmset, 4287 sizeof (oldset), 4288 GNUNET_DISK_PERM_USER_READ)) 4289 { 4290 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 4291 "Failed to store key material in file `%s'\n", 4292 fn); 4293 GNUNET_free (fn); 4294 return GNUNET_SYSERR; 4295 } 4296 GNUNET_free (fn); 4297 return GNUNET_NO; 4298 } 4299 4300 4301 /** 4302 * Output @a signkeys for human consumption. 4303 * 4304 * @param secm_pub security module public key used to sign the denominations 4305 * @param signkeys keys to output 4306 * @return #GNUNET_OK on success 4307 */ 4308 static enum GNUNET_GenericReturnValue 4309 show_signkeys (const struct TALER_SecurityModulePublicKeyP *secm_pub, 4310 const json_t *signkeys) 4311 { 4312 size_t index; 4313 json_t *value; 4314 4315 json_array_foreach (signkeys, index, value) { 4316 const char *err_name; 4317 unsigned int err_line; 4318 struct TALER_ExchangePublicKeyP exchange_pub; 4319 struct TALER_SecurityModuleSignatureP secm_sig; 4320 struct GNUNET_TIME_Timestamp start_time; 4321 struct GNUNET_TIME_Timestamp sign_end; 4322 struct GNUNET_TIME_Timestamp legal_end; 4323 struct GNUNET_TIME_Relative duration; 4324 struct GNUNET_JSON_Specification spec[] = { 4325 GNUNET_JSON_spec_timestamp ("stamp_start", 4326 &start_time), 4327 GNUNET_JSON_spec_timestamp ("stamp_expire", 4328 &sign_end), 4329 GNUNET_JSON_spec_timestamp ("stamp_end", 4330 &legal_end), 4331 GNUNET_JSON_spec_fixed_auto ("key", 4332 &exchange_pub), 4333 GNUNET_JSON_spec_fixed_auto ("signkey_secmod_sig", 4334 &secm_sig), 4335 GNUNET_JSON_spec_end () 4336 }; 4337 4338 if (GNUNET_OK != 4339 GNUNET_JSON_parse (value, 4340 spec, 4341 &err_name, 4342 &err_line)) 4343 { 4344 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 4345 "Invalid input for signing key to 'show': %s#%u at %u (skipping)\n", 4346 err_name, 4347 err_line, 4348 (unsigned int) index); 4349 json_dumpf (value, 4350 stderr, 4351 JSON_INDENT (2)); 4352 global_ret = EXIT_FAILURE; 4353 GNUNET_SCHEDULER_shutdown (); 4354 return GNUNET_SYSERR; 4355 } 4356 duration = GNUNET_TIME_absolute_get_difference (start_time.abs_time, 4357 sign_end.abs_time); 4358 if (GNUNET_OK != 4359 TALER_exchange_secmod_eddsa_verify (&exchange_pub, 4360 start_time, 4361 duration, 4362 secm_pub, 4363 &secm_sig)) 4364 { 4365 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 4366 "Invalid security module signature for signing key %s (aborting)\n", 4367 TALER_B2S (&exchange_pub)); 4368 global_ret = EXIT_FAILURE; 4369 GNUNET_SCHEDULER_shutdown (); 4370 return GNUNET_SYSERR; 4371 } 4372 { 4373 char *legal_end_s; 4374 4375 legal_end_s = GNUNET_strdup ( 4376 GNUNET_TIME_timestamp2s (legal_end)); 4377 printf ("EXCHANGE-KEY %s starting at %s (used for: %s, legal end: %s)\n", 4378 TALER_B2S (&exchange_pub), 4379 GNUNET_TIME_timestamp2s (start_time), 4380 GNUNET_TIME_relative2s (duration, 4381 false), 4382 legal_end_s); 4383 GNUNET_free (legal_end_s); 4384 } 4385 } 4386 return GNUNET_OK; 4387 } 4388 4389 4390 /** 4391 * Output @a denomkeys for human consumption. 4392 * 4393 * @param secm_pub_rsa security module public key used to sign the RSA denominations 4394 * @param secm_pub_cs security module public key used to sign the CS denominations 4395 * @param denomkeys keys to output 4396 * @return #GNUNET_OK on success 4397 */ 4398 static enum GNUNET_GenericReturnValue 4399 show_denomkeys (const struct TALER_SecurityModulePublicKeyP *secm_pub_rsa, 4400 const struct TALER_SecurityModulePublicKeyP *secm_pub_cs, 4401 const json_t *denomkeys) 4402 { 4403 size_t index; 4404 json_t *value; 4405 4406 json_array_foreach (denomkeys, index, value) { 4407 const char *err_name; 4408 unsigned int err_line; 4409 const char *section_name; 4410 struct TALER_DenominationPublicKey denom_pub; 4411 struct GNUNET_TIME_Timestamp stamp_start; 4412 struct GNUNET_TIME_Timestamp stamp_expire_withdraw; 4413 struct GNUNET_TIME_Timestamp stamp_expire_deposit; 4414 struct GNUNET_TIME_Timestamp stamp_expire_legal; 4415 struct TALER_Amount coin_value; 4416 struct TALER_DenomFeeSet fees; 4417 struct TALER_SecurityModuleSignatureP secm_sig; 4418 struct GNUNET_JSON_Specification spec[] = { 4419 GNUNET_JSON_spec_string ("section_name", 4420 §ion_name), 4421 TALER_JSON_spec_denom_pub ("denom_pub", 4422 &denom_pub), 4423 TALER_JSON_spec_amount ("value", 4424 currency, 4425 &coin_value), 4426 TALER_JSON_SPEC_DENOM_FEES ("fee", 4427 currency, 4428 &fees), 4429 GNUNET_JSON_spec_timestamp ("stamp_start", 4430 &stamp_start), 4431 GNUNET_JSON_spec_timestamp ("stamp_expire_withdraw", 4432 &stamp_expire_withdraw), 4433 GNUNET_JSON_spec_timestamp ("stamp_expire_deposit", 4434 &stamp_expire_deposit), 4435 GNUNET_JSON_spec_timestamp ("stamp_expire_legal", 4436 &stamp_expire_legal), 4437 GNUNET_JSON_spec_fixed_auto ("denom_secmod_sig", 4438 &secm_sig), 4439 GNUNET_JSON_spec_end () 4440 }; 4441 struct GNUNET_TIME_Relative duration; 4442 struct TALER_DenominationHashP h_denom_pub; 4443 enum GNUNET_GenericReturnValue ok; 4444 4445 if (GNUNET_OK != 4446 GNUNET_JSON_parse (value, 4447 spec, 4448 &err_name, 4449 &err_line)) 4450 { 4451 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 4452 "Invalid input for denomination key to 'show': %s#%u at %u (skipping)\n", 4453 err_name, 4454 err_line, 4455 (unsigned int) index); 4456 json_dumpf (value, 4457 stderr, 4458 JSON_INDENT (2)); 4459 GNUNET_JSON_parse_free (spec); 4460 global_ret = EXIT_FAILURE; 4461 GNUNET_SCHEDULER_shutdown (); 4462 return GNUNET_SYSERR; 4463 } 4464 duration = GNUNET_TIME_absolute_get_difference ( 4465 stamp_start.abs_time, 4466 stamp_expire_withdraw.abs_time); 4467 TALER_denom_pub_hash (&denom_pub, 4468 &h_denom_pub); 4469 switch (denom_pub.bsign_pub_key->cipher) 4470 { 4471 case GNUNET_CRYPTO_BSA_RSA: 4472 { 4473 struct TALER_RsaPubHashP h_rsa; 4474 4475 TALER_rsa_pub_hash (denom_pub.bsign_pub_key->details.rsa_public_key, 4476 &h_rsa); 4477 ok = TALER_exchange_secmod_rsa_verify (&h_rsa, 4478 section_name, 4479 stamp_start, 4480 duration, 4481 secm_pub_rsa, 4482 &secm_sig); 4483 } 4484 break; 4485 case GNUNET_CRYPTO_BSA_CS: 4486 { 4487 struct TALER_CsPubHashP h_cs; 4488 4489 TALER_cs_pub_hash (&denom_pub.bsign_pub_key->details.cs_public_key, 4490 &h_cs); 4491 ok = TALER_exchange_secmod_cs_verify (&h_cs, 4492 section_name, 4493 stamp_start, 4494 duration, 4495 secm_pub_cs, 4496 &secm_sig); 4497 } 4498 break; 4499 default: 4500 GNUNET_break (0); 4501 ok = GNUNET_SYSERR; 4502 break; 4503 } 4504 if (GNUNET_OK != ok) 4505 { 4506 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 4507 "Invalid security module signature for denomination key %s (aborting)\n", 4508 GNUNET_h2s (&h_denom_pub.hash)); 4509 global_ret = EXIT_FAILURE; 4510 GNUNET_SCHEDULER_shutdown (); 4511 return GNUNET_SYSERR; 4512 } 4513 4514 { 4515 char *withdraw_fee_s; 4516 char *deposit_fee_s; 4517 char *refresh_fee_s; 4518 char *refund_fee_s; 4519 char *deposit_s; 4520 char *legal_s; 4521 4522 withdraw_fee_s = TALER_amount_to_string (&fees.withdraw); 4523 deposit_fee_s = TALER_amount_to_string (&fees.deposit); 4524 refresh_fee_s = TALER_amount_to_string (&fees.refresh); 4525 refund_fee_s = TALER_amount_to_string (&fees.refund); 4526 deposit_s = GNUNET_strdup ( 4527 GNUNET_TIME_timestamp2s (stamp_expire_deposit)); 4528 legal_s = GNUNET_strdup ( 4529 GNUNET_TIME_timestamp2s (stamp_expire_legal)); 4530 4531 printf ( 4532 "DENOMINATION-KEY(%s) %s of value %s starting at %s " 4533 "(used for: %s, deposit until: %s legal end: %s) with fees %s/%s/%s/%s\n", 4534 section_name, 4535 TALER_B2S (&h_denom_pub), 4536 TALER_amount2s (&coin_value), 4537 GNUNET_TIME_timestamp2s (stamp_start), 4538 GNUNET_TIME_relative2s (duration, 4539 false), 4540 deposit_s, 4541 legal_s, 4542 withdraw_fee_s, 4543 deposit_fee_s, 4544 refresh_fee_s, 4545 refund_fee_s); 4546 GNUNET_free (withdraw_fee_s); 4547 GNUNET_free (deposit_fee_s); 4548 GNUNET_free (refresh_fee_s); 4549 GNUNET_free (refund_fee_s); 4550 GNUNET_free (deposit_s); 4551 GNUNET_free (legal_s); 4552 } 4553 4554 GNUNET_JSON_parse_free (spec); 4555 } 4556 return GNUNET_OK; 4557 } 4558 4559 4560 /** 4561 * Parse the input of exchange keys for the 'show' and 'sign' commands. 4562 * 4563 * @param command_name name of the command, for logging 4564 * @return NULL on error, otherwise the keys details to be free'd by caller 4565 */ 4566 static json_t * 4567 parse_keys_input (const char *command_name) 4568 { 4569 const char *op_str; 4570 json_t *keys; 4571 struct GNUNET_JSON_Specification spec[] = { 4572 GNUNET_JSON_spec_json ("arguments", 4573 &keys), 4574 GNUNET_JSON_spec_string ("operation", 4575 &op_str), 4576 GNUNET_JSON_spec_end () 4577 }; 4578 const char *err_name; 4579 unsigned int err_line; 4580 4581 if (NULL == in) 4582 { 4583 json_error_t err; 4584 4585 in = json_loadf (stdin, 4586 JSON_REJECT_DUPLICATES, 4587 &err); 4588 if (NULL == in) 4589 { 4590 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 4591 "Failed to read JSON input: %s at %d:%s (offset: %d)\n", 4592 err.text, 4593 err.line, 4594 err.source, 4595 err.position); 4596 global_ret = EXIT_FAILURE; 4597 GNUNET_SCHEDULER_shutdown (); 4598 return NULL; 4599 } 4600 } 4601 if (GNUNET_OK != 4602 GNUNET_JSON_parse (in, 4603 spec, 4604 &err_name, 4605 &err_line)) 4606 { 4607 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 4608 "Invalid input to '%s': %s#%u (skipping)\n", 4609 command_name, 4610 err_name, 4611 err_line); 4612 json_dumpf (in, 4613 stderr, 4614 JSON_INDENT (2)); 4615 global_ret = EXIT_FAILURE; 4616 GNUNET_SCHEDULER_shutdown (); 4617 return NULL; 4618 } 4619 if (0 != strcmp (op_str, 4620 OP_INPUT_KEYS)) 4621 { 4622 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 4623 "Invalid input to '%s' : operation is `%s', expected `%s'\n", 4624 command_name, 4625 op_str, 4626 OP_INPUT_KEYS); 4627 GNUNET_JSON_parse_free (spec); 4628 return NULL; 4629 } 4630 json_decref (in); 4631 in = NULL; 4632 return keys; 4633 } 4634 4635 4636 /** 4637 * Show future keys. 4638 * 4639 * @param args the array of command-line arguments to process next 4640 */ 4641 static void 4642 do_show (char *const *args) 4643 { 4644 json_t *keys; 4645 const char *err_name; 4646 unsigned int err_line; 4647 const json_t *denomkeys; 4648 const json_t *signkeys; 4649 struct TALER_MasterPublicKeyP mpub; 4650 struct TALER_SecurityModulePublicKeySetP secmset; 4651 struct GNUNET_JSON_Specification spec[] = { 4652 GNUNET_JSON_spec_array_const ("future_denoms", 4653 &denomkeys), 4654 GNUNET_JSON_spec_array_const ("future_signkeys", 4655 &signkeys), 4656 GNUNET_JSON_spec_fixed_auto ("master_pub", 4657 &mpub), 4658 GNUNET_JSON_spec_fixed_auto ("denom_secmod_public_key", 4659 &secmset.rsa), 4660 GNUNET_JSON_spec_fixed_auto ("denom_secmod_cs_public_key", 4661 &secmset.cs), 4662 GNUNET_JSON_spec_fixed_auto ("signkey_secmod_public_key", 4663 &secmset.eddsa), 4664 GNUNET_JSON_spec_end () 4665 }; 4666 4667 keys = parse_keys_input ("show"); 4668 if (NULL == keys) 4669 return; 4670 4671 if (GNUNET_OK != 4672 load_offline_key (GNUNET_NO)) 4673 return; 4674 if (GNUNET_OK != 4675 GNUNET_JSON_parse (keys, 4676 spec, 4677 &err_name, 4678 &err_line)) 4679 { 4680 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 4681 "Invalid input to 'show': %s #%u (skipping)\n", 4682 err_name, 4683 err_line); 4684 json_dumpf (in, 4685 stderr, 4686 JSON_INDENT (2)); 4687 global_ret = EXIT_FAILURE; 4688 GNUNET_SCHEDULER_shutdown (); 4689 json_decref (keys); 4690 return; 4691 } 4692 if (0 != 4693 GNUNET_memcmp (&master_pub, 4694 &mpub)) 4695 { 4696 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 4697 "Fatal: exchange uses different master key!\n"); 4698 global_ret = EXIT_FAILURE; 4699 GNUNET_SCHEDULER_shutdown (); 4700 json_decref (keys); 4701 return; 4702 } 4703 if (GNUNET_SYSERR == 4704 tofu_check (&secmset)) 4705 { 4706 global_ret = EXIT_FAILURE; 4707 GNUNET_SCHEDULER_shutdown (); 4708 json_decref (keys); 4709 return; 4710 } 4711 if ( (GNUNET_OK != 4712 show_signkeys (&secmset.eddsa, 4713 signkeys)) || 4714 (GNUNET_OK != 4715 show_denomkeys (&secmset.rsa, 4716 &secmset.cs, 4717 denomkeys)) ) 4718 { 4719 global_ret = EXIT_FAILURE; 4720 GNUNET_SCHEDULER_shutdown (); 4721 json_decref (keys); 4722 return; 4723 } 4724 json_decref (keys); 4725 next (args); 4726 } 4727 4728 4729 /** 4730 * Sign @a signkeys with offline key. 4731 * 4732 * @param secm_pub security module public key used to sign the denominations 4733 * @param signkeys keys to output 4734 * @param[in,out] result array where to output the signatures 4735 * @return #GNUNET_OK on success 4736 */ 4737 static enum GNUNET_GenericReturnValue 4738 sign_signkeys (const struct TALER_SecurityModulePublicKeyP *secm_pub, 4739 const json_t *signkeys, 4740 json_t *result) 4741 { 4742 size_t index; 4743 json_t *value; 4744 4745 json_array_foreach (signkeys, index, value) { 4746 const char *err_name; 4747 unsigned int err_line; 4748 struct TALER_ExchangePublicKeyP exchange_pub; 4749 struct TALER_SecurityModuleSignatureP secm_sig; 4750 struct GNUNET_TIME_Timestamp start_time; 4751 struct GNUNET_TIME_Timestamp sign_end; 4752 struct GNUNET_TIME_Timestamp legal_end; 4753 struct GNUNET_TIME_Relative duration; 4754 struct GNUNET_JSON_Specification spec[] = { 4755 GNUNET_JSON_spec_timestamp ("stamp_start", 4756 &start_time), 4757 GNUNET_JSON_spec_timestamp ("stamp_expire", 4758 &sign_end), 4759 GNUNET_JSON_spec_timestamp ("stamp_end", 4760 &legal_end), 4761 GNUNET_JSON_spec_fixed_auto ("key", 4762 &exchange_pub), 4763 GNUNET_JSON_spec_fixed_auto ("signkey_secmod_sig", 4764 &secm_sig), 4765 GNUNET_JSON_spec_end () 4766 }; 4767 4768 if (GNUNET_OK != 4769 GNUNET_JSON_parse (value, 4770 spec, 4771 &err_name, 4772 &err_line)) 4773 { 4774 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 4775 "Invalid input for signing key to 'show': %s #%u at %u (skipping)\n", 4776 err_name, 4777 err_line, 4778 (unsigned int) index); 4779 json_dumpf (value, 4780 stderr, 4781 JSON_INDENT (2)); 4782 global_ret = EXIT_FAILURE; 4783 GNUNET_SCHEDULER_shutdown (); 4784 return GNUNET_SYSERR; 4785 } 4786 4787 duration = GNUNET_TIME_absolute_get_difference (start_time.abs_time, 4788 sign_end.abs_time); 4789 if (GNUNET_OK != 4790 TALER_exchange_secmod_eddsa_verify (&exchange_pub, 4791 start_time, 4792 duration, 4793 secm_pub, 4794 &secm_sig)) 4795 { 4796 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 4797 "Invalid security module signature for signing key %s (aborting)\n", 4798 TALER_B2S (&exchange_pub)); 4799 global_ret = EXIT_FAILURE; 4800 GNUNET_SCHEDULER_shutdown (); 4801 GNUNET_JSON_parse_free (spec); 4802 return GNUNET_SYSERR; 4803 } 4804 { 4805 struct TALER_MasterSignatureP master_sig; 4806 4807 TALER_exchange_offline_signkey_validity_sign (&exchange_pub, 4808 start_time, 4809 sign_end, 4810 legal_end, 4811 &master_priv, 4812 &master_sig); 4813 GNUNET_assert (0 == 4814 json_array_append_new ( 4815 result, 4816 GNUNET_JSON_PACK ( 4817 GNUNET_JSON_pack_data_auto ("exchange_pub", 4818 &exchange_pub), 4819 GNUNET_JSON_pack_data_auto ("master_sig", 4820 &master_sig)))); 4821 } 4822 GNUNET_JSON_parse_free (spec); 4823 } 4824 return GNUNET_OK; 4825 } 4826 4827 4828 /** 4829 * Looks up the AGE_RESTRICTED setting for a denomination in the config and 4830 * returns the age restriction (mask) accordingly. 4831 * 4832 * @param section_name Section in the configuration for the particular 4833 * denomination. 4834 */ 4835 static struct TALER_AgeMask 4836 load_age_mask (const char*section_name) 4837 { 4838 static const struct TALER_AgeMask null_mask = {0}; 4839 enum GNUNET_GenericReturnValue ret; 4840 4841 if (! ar_enabled) 4842 return null_mask; 4843 4844 if (GNUNET_OK != (GNUNET_CONFIGURATION_have_value ( 4845 kcfg, 4846 section_name, 4847 "AGE_RESTRICTED"))) 4848 return null_mask; 4849 4850 ret = GNUNET_CONFIGURATION_get_value_yesno (kcfg, 4851 section_name, 4852 "AGE_RESTRICTED"); 4853 if (GNUNET_SYSERR == ret) 4854 GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, 4855 section_name, 4856 "AGE_RESTRICTED", 4857 "Value must be YES or NO\n"); 4858 if (GNUNET_YES == ret) 4859 return ar_config.mask; 4860 4861 return null_mask; 4862 } 4863 4864 4865 /** 4866 * Sign @a denomkeys with offline key. 4867 * 4868 * @param secm_pub_rsa security module public key used to sign the RSA denominations 4869 * @param secm_pub_cs security module public key used to sign the CS denominations 4870 * @param denomkeys keys to output 4871 * @param[in,out] result array where to output the signatures 4872 * @return #GNUNET_OK on success 4873 */ 4874 static enum GNUNET_GenericReturnValue 4875 sign_denomkeys (const struct TALER_SecurityModulePublicKeyP *secm_pub_rsa, 4876 const struct TALER_SecurityModulePublicKeyP *secm_pub_cs, 4877 const json_t *denomkeys, 4878 json_t *result) 4879 { 4880 size_t index; 4881 json_t *value; 4882 4883 json_array_foreach (denomkeys, index, value) { 4884 const char *err_name; 4885 unsigned int err_line; 4886 const char *section_name; 4887 struct TALER_DenominationPublicKey denom_pub; 4888 struct GNUNET_TIME_Timestamp stamp_start; 4889 struct GNUNET_TIME_Timestamp stamp_expire_withdraw; 4890 struct GNUNET_TIME_Timestamp stamp_expire_deposit; 4891 struct GNUNET_TIME_Timestamp stamp_expire_legal; 4892 struct TALER_Amount coin_value; 4893 struct TALER_DenomFeeSet fees; 4894 struct TALER_SecurityModuleSignatureP secm_sig; 4895 struct GNUNET_JSON_Specification spec[] = { 4896 GNUNET_JSON_spec_string ("section_name", 4897 §ion_name), 4898 TALER_JSON_spec_denom_pub ("denom_pub", 4899 &denom_pub), 4900 TALER_JSON_spec_amount ("value", 4901 currency, 4902 &coin_value), 4903 TALER_JSON_SPEC_DENOM_FEES ("fee", 4904 currency, 4905 &fees), 4906 GNUNET_JSON_spec_timestamp ("stamp_start", 4907 &stamp_start), 4908 GNUNET_JSON_spec_timestamp ("stamp_expire_withdraw", 4909 &stamp_expire_withdraw), 4910 GNUNET_JSON_spec_timestamp ("stamp_expire_deposit", 4911 &stamp_expire_deposit), 4912 GNUNET_JSON_spec_timestamp ("stamp_expire_legal", 4913 &stamp_expire_legal), 4914 GNUNET_JSON_spec_fixed_auto ("denom_secmod_sig", 4915 &secm_sig), 4916 GNUNET_JSON_spec_end () 4917 }; 4918 struct GNUNET_TIME_Relative duration; 4919 struct TALER_DenominationHashP h_denom_pub; 4920 4921 if (GNUNET_OK != 4922 GNUNET_JSON_parse (value, 4923 spec, 4924 &err_name, 4925 &err_line)) 4926 { 4927 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 4928 "Invalid input for denomination key to 'sign': %s #%u at %u (skipping)\n", 4929 err_name, 4930 err_line, 4931 (unsigned int) index); 4932 json_dumpf (value, 4933 stderr, 4934 JSON_INDENT (2)); 4935 GNUNET_JSON_parse_free (spec); 4936 global_ret = EXIT_FAILURE; 4937 GNUNET_SCHEDULER_shutdown (); 4938 return GNUNET_SYSERR; 4939 } 4940 duration = GNUNET_TIME_absolute_get_difference ( 4941 stamp_start.abs_time, 4942 stamp_expire_withdraw.abs_time); 4943 4944 /* Load the age mask, if applicable to this denomination */ 4945 denom_pub.age_mask = load_age_mask (section_name); 4946 4947 TALER_denom_pub_hash (&denom_pub, 4948 &h_denom_pub); 4949 4950 switch (denom_pub.bsign_pub_key->cipher) 4951 { 4952 case GNUNET_CRYPTO_BSA_RSA: 4953 { 4954 struct TALER_RsaPubHashP h_rsa; 4955 4956 TALER_rsa_pub_hash (denom_pub.bsign_pub_key->details.rsa_public_key, 4957 &h_rsa); 4958 if (GNUNET_OK != 4959 TALER_exchange_secmod_rsa_verify (&h_rsa, 4960 section_name, 4961 stamp_start, 4962 duration, 4963 secm_pub_rsa, 4964 &secm_sig)) 4965 { 4966 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 4967 "Invalid security module signature for denomination key %s (aborting)\n", 4968 GNUNET_h2s (&h_denom_pub.hash)); 4969 global_ret = EXIT_FAILURE; 4970 GNUNET_SCHEDULER_shutdown (); 4971 GNUNET_JSON_parse_free (spec); 4972 return GNUNET_SYSERR; 4973 } 4974 } 4975 break; 4976 case GNUNET_CRYPTO_BSA_CS: 4977 { 4978 struct TALER_CsPubHashP h_cs; 4979 4980 TALER_cs_pub_hash (&denom_pub.bsign_pub_key->details.cs_public_key, 4981 &h_cs); 4982 if (GNUNET_OK != 4983 TALER_exchange_secmod_cs_verify (&h_cs, 4984 section_name, 4985 stamp_start, 4986 duration, 4987 secm_pub_cs, 4988 &secm_sig)) 4989 { 4990 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 4991 "Invalid security module signature for denomination key %s (aborting)\n", 4992 GNUNET_h2s (&h_denom_pub.hash)); 4993 global_ret = EXIT_FAILURE; 4994 GNUNET_SCHEDULER_shutdown (); 4995 GNUNET_JSON_parse_free (spec); 4996 return GNUNET_SYSERR; 4997 } 4998 } 4999 break; 5000 default: 5001 global_ret = EXIT_FAILURE; 5002 GNUNET_SCHEDULER_shutdown (); 5003 GNUNET_JSON_parse_free (spec); 5004 return GNUNET_SYSERR; 5005 } 5006 5007 { 5008 struct TALER_MasterSignatureP master_sig; 5009 5010 TALER_exchange_offline_denom_validity_sign (&h_denom_pub, 5011 stamp_start, 5012 stamp_expire_withdraw, 5013 stamp_expire_deposit, 5014 stamp_expire_legal, 5015 &coin_value, 5016 &fees, 5017 &master_priv, 5018 &master_sig); 5019 GNUNET_assert (0 == 5020 json_array_append_new ( 5021 result, 5022 GNUNET_JSON_PACK ( 5023 GNUNET_JSON_pack_data_auto ("h_denom_pub", 5024 &h_denom_pub), 5025 GNUNET_JSON_pack_data_auto ("master_sig", 5026 &master_sig)))); 5027 } 5028 GNUNET_JSON_parse_free (spec); 5029 } 5030 return GNUNET_OK; 5031 } 5032 5033 5034 /** 5035 * Sign future keys. 5036 * 5037 * @param args the array of command-line arguments to process next 5038 */ 5039 static void 5040 do_sign (char *const *args) 5041 { 5042 json_t *keys; 5043 const char *err_name; 5044 unsigned int err_line; 5045 const json_t *denomkeys; 5046 const json_t *signkeys; 5047 struct TALER_MasterPublicKeyP mpub; 5048 struct TALER_SecurityModulePublicKeySetP secmset; 5049 struct GNUNET_JSON_Specification spec[] = { 5050 GNUNET_JSON_spec_array_const ("future_denoms", 5051 &denomkeys), 5052 GNUNET_JSON_spec_array_const ("future_signkeys", 5053 &signkeys), 5054 GNUNET_JSON_spec_fixed_auto ("master_pub", 5055 &mpub), 5056 GNUNET_JSON_spec_fixed_auto ("denom_secmod_public_key", 5057 &secmset.rsa), 5058 GNUNET_JSON_spec_fixed_auto ("denom_secmod_cs_public_key", 5059 &secmset.cs), 5060 GNUNET_JSON_spec_fixed_auto ("signkey_secmod_public_key", 5061 &secmset.eddsa), 5062 GNUNET_JSON_spec_end () 5063 }; 5064 5065 keys = parse_keys_input ("sign"); 5066 if (NULL == keys) 5067 return; 5068 if (GNUNET_OK != 5069 load_offline_key (GNUNET_NO)) 5070 { 5071 json_decref (keys); 5072 return; 5073 } 5074 if (GNUNET_OK != 5075 GNUNET_JSON_parse (keys, 5076 spec, 5077 &err_name, 5078 &err_line)) 5079 { 5080 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 5081 "Invalid input to 'sign' : %s #%u (skipping)\n", 5082 err_name, 5083 err_line); 5084 json_dumpf (in, 5085 stderr, 5086 JSON_INDENT (2)); 5087 global_ret = EXIT_FAILURE; 5088 GNUNET_SCHEDULER_shutdown (); 5089 json_decref (keys); 5090 return; 5091 } 5092 if (0 != 5093 GNUNET_memcmp (&master_pub, 5094 &mpub)) 5095 { 5096 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 5097 "Fatal: exchange uses different master key!\n"); 5098 global_ret = EXIT_FAILURE; 5099 GNUNET_SCHEDULER_shutdown (); 5100 json_decref (keys); 5101 return; 5102 } 5103 if (GNUNET_SYSERR == 5104 tofu_check (&secmset)) 5105 { 5106 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 5107 "Fatal: security module keys changed!\n"); 5108 global_ret = EXIT_FAILURE; 5109 GNUNET_SCHEDULER_shutdown (); 5110 json_decref (keys); 5111 return; 5112 } 5113 { 5114 json_t *signkey_sig_array = json_array (); 5115 json_t *denomkey_sig_array = json_array (); 5116 5117 GNUNET_assert (NULL != signkey_sig_array); 5118 GNUNET_assert (NULL != denomkey_sig_array); 5119 if ( (GNUNET_OK != 5120 sign_signkeys (&secmset.eddsa, 5121 signkeys, 5122 signkey_sig_array)) || 5123 (GNUNET_OK != 5124 sign_denomkeys (&secmset.rsa, 5125 &secmset.cs, 5126 denomkeys, 5127 denomkey_sig_array)) ) 5128 { 5129 global_ret = EXIT_FAILURE; 5130 GNUNET_SCHEDULER_shutdown (); 5131 json_decref (signkey_sig_array); 5132 json_decref (denomkey_sig_array); 5133 json_decref (keys); 5134 return; 5135 } 5136 5137 output_operation (OP_UPLOAD_SIGS, 5138 GNUNET_JSON_PACK ( 5139 GNUNET_JSON_pack_array_steal ("denom_sigs", 5140 denomkey_sig_array), 5141 GNUNET_JSON_pack_array_steal ("signkey_sigs", 5142 signkey_sig_array))); 5143 } 5144 json_decref (keys); 5145 next (args); 5146 } 5147 5148 5149 /** 5150 * Setup and output offline signing key. 5151 * 5152 * @param args the array of command-line arguments to process next 5153 */ 5154 static void 5155 do_setup (char *const *args) 5156 { 5157 if (GNUNET_OK != 5158 load_offline_key (GNUNET_YES)) 5159 { 5160 global_ret = EXIT_NOPERMISSION; 5161 return; 5162 } 5163 if (NULL != *args) 5164 { 5165 output_operation (OP_SETUP, 5166 GNUNET_JSON_PACK ( 5167 GNUNET_JSON_pack_data_auto ("exchange_offline_pub", 5168 &master_pub))); 5169 } 5170 5171 else 5172 { 5173 char *pub_s; 5174 5175 pub_s = GNUNET_STRINGS_data_to_string_alloc (&master_pub, 5176 sizeof (master_pub)); 5177 fprintf (stdout, 5178 "%s\n", 5179 pub_s); 5180 GNUNET_free (pub_s); 5181 } 5182 if ( (NULL != *args) && 5183 (0 == strcmp (*args, 5184 "-")) ) 5185 args++; 5186 next (args); 5187 } 5188 5189 5190 /** 5191 * Print the current extensions as configured 5192 * 5193 * @param args the array of command-line arguments to process next 5194 */ 5195 static void 5196 do_extensions_show (char *const *args) 5197 { 5198 const struct TALER_Extensions *it; 5199 json_t *exts = json_object (); 5200 json_t *obj; 5201 5202 GNUNET_assert (NULL != exts); 5203 for (it = TALER_extensions_get_head (); 5204 NULL != it && NULL != it->extension; 5205 it = it->next) 5206 { 5207 const struct TALER_Extension *extension = it->extension; 5208 int ret; 5209 5210 ret = json_object_set_new (exts, 5211 extension->name, 5212 extension->manifest (extension)); 5213 GNUNET_assert (-1 != ret); 5214 } 5215 5216 obj = GNUNET_JSON_PACK ( 5217 GNUNET_JSON_pack_object_steal ("extensions", 5218 exts)); 5219 GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE, 5220 "%s\n", 5221 json_dumps (obj, 5222 JSON_INDENT (2))); 5223 json_decref (obj); 5224 next (args); 5225 } 5226 5227 5228 /** 5229 * Sign the configurations of the enabled extensions 5230 */ 5231 static void 5232 do_extensions_sign (char *const *args) 5233 { 5234 json_t *extensions = json_object (); 5235 struct TALER_ExtensionManifestsHashP h_manifests; 5236 struct TALER_MasterSignatureP sig; 5237 const struct TALER_Extensions *it; 5238 bool found = false; 5239 json_t *obj; 5240 5241 GNUNET_assert (NULL != extensions); 5242 for (it = TALER_extensions_get_head (); 5243 NULL != it && NULL != it->extension; 5244 it = it->next) 5245 { 5246 const struct TALER_Extension *ext = it->extension; 5247 GNUNET_assert (ext); 5248 5249 found = true; 5250 5251 GNUNET_assert (0 == 5252 json_object_set_new (extensions, 5253 ext->name, 5254 ext->manifest (ext))); 5255 } 5256 5257 if (! found) 5258 return; 5259 5260 if (GNUNET_OK != 5261 TALER_JSON_extensions_manifests_hash (extensions, 5262 &h_manifests)) 5263 { 5264 json_decref (extensions); 5265 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 5266 "error while hashing manifest for extensions\n"); 5267 return; 5268 } 5269 5270 if (GNUNET_OK != 5271 load_offline_key (GNUNET_NO)) 5272 { 5273 json_decref (extensions); 5274 return; 5275 } 5276 5277 TALER_exchange_offline_extension_manifests_hash_sign (&h_manifests, 5278 &master_priv, 5279 &sig); 5280 obj = GNUNET_JSON_PACK ( 5281 GNUNET_JSON_pack_object_steal ("extensions", 5282 extensions), 5283 GNUNET_JSON_pack_data_auto ( 5284 "extensions_sig", 5285 &sig)); 5286 5287 output_operation (OP_EXTENSIONS, 5288 obj); 5289 next (args); 5290 } 5291 5292 5293 /** 5294 * Dispatch @a args in the @a cmds array. 5295 * 5296 * @param args arguments with subcommand to dispatch 5297 * @param cmds array of possible subcommands to call 5298 */ 5299 static void 5300 cmd_handler (char *const *args, 5301 const struct SubCommand *cmds) 5302 { 5303 nxt = NULL; 5304 for (unsigned int i = 0; NULL != cmds[i].name; i++) 5305 { 5306 if (0 == strcasecmp (cmds[i].name, 5307 args[0])) 5308 { 5309 cmds[i].cb (&args[1]); 5310 return; 5311 } 5312 } 5313 5314 if (0 != strcasecmp ("help", 5315 args[0])) 5316 { 5317 GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE, 5318 "Unexpected command `%s'\n", 5319 args[0]); 5320 global_ret = EXIT_INVALIDARGUMENT; 5321 } 5322 5323 GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE, 5324 "Supported subcommands:\n"); 5325 for (unsigned int i = 0; NULL != cmds[i].name; i++) 5326 { 5327 GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE, 5328 "- %s: %s\n", 5329 cmds[i].name, 5330 cmds[i].help); 5331 } 5332 json_decref (out); 5333 out = NULL; 5334 GNUNET_SCHEDULER_shutdown (); 5335 } 5336 5337 5338 static void 5339 do_work_extensions (char *const *args) 5340 { 5341 struct SubCommand cmds[] = { 5342 { 5343 .name = "show", 5344 .help = 5345 "show the extensions in the Taler-config and their configured parameters", 5346 .cb = &do_extensions_show 5347 }, 5348 { 5349 .name = "sign", 5350 .help = 5351 "sign the configuration of the extensions and publish it with the exchange", 5352 .cb = &do_extensions_sign 5353 }, 5354 { 5355 .name = NULL, 5356 } 5357 }; 5358 5359 if (NULL == args[0]) 5360 { 5361 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 5362 "You must provide a subcommand: `show` or `sign`.\n"); 5363 GNUNET_SCHEDULER_shutdown (); 5364 global_ret = EXIT_INVALIDARGUMENT; 5365 return; 5366 } 5367 5368 cmd_handler (args, cmds); 5369 } 5370 5371 5372 static void 5373 work (void *cls) 5374 { 5375 char *const *args = cls; 5376 struct SubCommand cmds[] = { 5377 { 5378 .name = "setup", 5379 .help = 5380 "initialize offline key signing material and display public offline key", 5381 .cb = &do_setup 5382 }, 5383 { 5384 .name = "download", 5385 .help = 5386 "obtain future public keys from exchange (to be performed online!)", 5387 .cb = &do_download 5388 }, 5389 { 5390 .name = "show", 5391 .help = 5392 "display future public keys from exchange for human review (pass '-' as argument to disable consuming input)", 5393 .cb = &do_show 5394 }, 5395 { 5396 .name = "sign", 5397 .help = "sign all future public keys from the input", 5398 .cb = &do_sign 5399 }, 5400 { 5401 .name = "revoke-denomination", 5402 .help = 5403 "revoke denomination key (hash of public key must be given as argument)", 5404 .cb = &do_revoke_denomination_key 5405 }, 5406 { 5407 .name = "revoke-signkey", 5408 .help = 5409 "revoke exchange online signing key (public key must be given as argument)", 5410 .cb = &do_revoke_signkey 5411 }, 5412 { 5413 .name = "enable-auditor", 5414 .help = 5415 "enable auditor for the exchange (auditor-public key, auditor-URI and auditor-name must be given as arguments)", 5416 .cb = &do_add_auditor 5417 }, 5418 { 5419 .name = "disable-auditor", 5420 .help = 5421 "disable auditor at the exchange (auditor-public key must be given as argument)", 5422 .cb = &do_del_auditor 5423 }, 5424 { 5425 .name = "enable-account", 5426 .help = 5427 "enable wire account of the exchange (payto-URI must be given as argument; for optional arguments see man page)", 5428 .cb = &do_add_wire 5429 }, 5430 { 5431 .name = "disable-account", 5432 .help = 5433 "disable wire account of the exchange (payto-URI must be given as argument)", 5434 .cb = &do_del_wire 5435 }, 5436 { 5437 .name = "wire-fee", 5438 .help = 5439 "sign wire fees for the given year (year, wire method, wire fee, and closing fee must be given as arguments)", 5440 .cb = &do_set_wire_fee 5441 }, 5442 { 5443 .name = "global-fee", 5444 .help = 5445 "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)", 5446 .cb = &do_set_global_fee 5447 }, 5448 { 5449 .name = "drain", 5450 .help = 5451 "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)", 5452 .cb = &do_drain 5453 }, 5454 { 5455 .name = "add-partner", 5456 .help = 5457 "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)", 5458 .cb = &do_add_partner 5459 }, 5460 { 5461 .name = "aml-enable", 5462 .help = 5463 "enable AML staff member (staff member public key, legal name and rw (read write) or ro (read only) must be given as arguments)", 5464 .cb = &enable_aml_staff 5465 }, 5466 { 5467 .name = "aml-disable", 5468 .help = 5469 "disable AML staff member (staff member public key and legal name must be given as arguments)", 5470 .cb = &disable_aml_staff 5471 }, 5472 { 5473 .name = "upload", 5474 .help = 5475 "upload operation result to exchange (to be performed online!)", 5476 .cb = &do_upload 5477 }, 5478 { 5479 .name = "extensions", 5480 .help = "subcommands for extension handling", 5481 .cb = &do_work_extensions 5482 }, 5483 /* list terminator */ 5484 { 5485 .name = NULL, 5486 } 5487 }; 5488 (void) cls; 5489 5490 cmd_handler (args, cmds); 5491 } 5492 5493 5494 /** 5495 * Main function that will be run. 5496 * 5497 * @param cls closure 5498 * @param args remaining command-line arguments 5499 * @param cfgfile name of the configuration file used (for saving, can be NULL!) 5500 * @param cfg configuration 5501 */ 5502 static void 5503 run (void *cls, 5504 char *const *args, 5505 const char *cfgfile, 5506 const struct GNUNET_CONFIGURATION_Handle *cfg) 5507 { 5508 (void) cls; 5509 (void) cfgfile; 5510 kcfg = cfg; 5511 5512 /* load extensions */ 5513 GNUNET_assert (GNUNET_OK == 5514 TALER_extensions_init (kcfg)); 5515 5516 /* setup age restriction, if applicable */ 5517 { 5518 const struct TALER_AgeRestrictionConfig *arc; 5519 5520 if (NULL != 5521 (arc = TALER_extensions_get_age_restriction_config ())) 5522 { 5523 ar_config = *arc; 5524 ar_enabled = true; 5525 } 5526 } 5527 5528 5529 if (GNUNET_OK != 5530 TALER_config_get_currency (kcfg, 5531 "exchange", 5532 ¤cy)) 5533 { 5534 global_ret = EXIT_NOTCONFIGURED; 5535 return; 5536 } 5537 5538 ctx = GNUNET_CURL_init (&GNUNET_CURL_gnunet_scheduler_reschedule, 5539 &rc); 5540 rc = GNUNET_CURL_gnunet_rc_create (ctx); 5541 GNUNET_SCHEDULER_add_shutdown (&do_shutdown, 5542 NULL); 5543 next (args); 5544 } 5545 5546 5547 /** 5548 * The main function of the taler-exchange-offline tool. This tool is used to 5549 * create the signing and denomination keys for the exchange. It uses the 5550 * long-term offline private key and generates signatures with it. It also 5551 * supports online operations with the exchange to download its input data and 5552 * to upload its results. Those online operations should be performed on 5553 * another machine in production! 5554 * 5555 * @param argc number of arguments from the command line 5556 * @param argv command line arguments 5557 * @return 0 ok, 1 on error 5558 */ 5559 int 5560 main (int argc, 5561 char *const *argv) 5562 { 5563 struct GNUNET_GETOPT_CommandLineOption options[] = { 5564 GNUNET_GETOPT_OPTION_END 5565 }; 5566 enum GNUNET_GenericReturnValue ret; 5567 5568 ret = GNUNET_PROGRAM_run ( 5569 TALER_EXCHANGE_project_data (), 5570 argc, argv, 5571 "taler-exchange-offline", 5572 gettext_noop ("Operations for offline signing for a Taler exchange"), 5573 options, 5574 &run, NULL); 5575 if (GNUNET_SYSERR == ret) 5576 return EXIT_INVALIDARGUMENT; 5577 if (GNUNET_NO == ret) 5578 return EXIT_SUCCESS; 5579 return global_ret; 5580 } 5581 5582 5583 /* end of taler-exchange-offline.c */