frosix-cli.c (15874B)
1 /* 2 This file is part of Frosix 3 Copyright (C) 2020,2021,2022 Anastasis SARL 4 5 Frosix is free software; you can redistribute it and/or modify it under the 6 terms of the GNU Lesser General Public License as published by the Free Software 7 Foundation; either version 3, or (at your option) any later version. 8 9 Frosix 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 Affero General Public License for more details. 12 13 You should have received a copy of the GNU Affero General Public License along with 14 Frosix; see the file COPYING.GPL. If not, see <http://www.gnu.org/licenses/> 15 */ 16 /** 17 * @file cli/frosix-cli.c 18 * @brief command line tool for Frosix client 19 * @author Christian Grothoff 20 * @author Dennis Neufeld 21 * @author Dominik Meister 22 * @author Joel Urech 23 */ 24 25 #include "platform.h" 26 #include <gnunet/gnunet_util_lib.h> 27 #include <gnunet/gnunet_curl_lib.h> 28 #include "frosix.h" 29 #include <taler/taler_util.h> 30 #include <taler/taler_error_codes.h> 31 #include <taler/taler_json_lib.h> 32 #include "frosix_util_lib.h" 33 34 /** 35 * Closure for #GNUNET_CURL_gnunet_scheduler_reschedule(). 36 */ 37 static struct GNUNET_CURL_RescheduleContext *rc; 38 39 /** 40 * Curl context for communication with Frosix backend 41 */ 42 static struct GNUNET_CURL_Context *ctx; 43 44 /** 45 * Input to -a option given. 46 */ 47 static char *input = NULL; 48 49 /** 50 * Output filename, if given. 51 */ 52 static char *output_filename = NULL; 53 54 /** 55 * Message we want to sign, -m option. 56 */ 57 static char *message = NULL; 58 59 /** 60 * JSON containing additional arguments 61 */ 62 static json_t *arguments; 63 64 /** 65 * JSON containing data from stdin 66 */ 67 static json_t *std_input; 68 69 /** 70 * Handle to an ongoing action. 71 */ 72 static struct FROSIX_ReduxAction *ra; 73 74 /** 75 * Return value from main. 76 */ 77 static int global_ret; 78 79 80 /** 81 * Persist a json state, report errors. 82 * 83 * @param state to persist 84 * @param filename where to write the state to, NULL for stdout 85 */ 86 static void 87 persist_new_state (json_t *state, 88 const char *filename) 89 { 90 if (NULL != filename) 91 { 92 if (0 != 93 json_dump_file (state, 94 filename, 95 JSON_INDENT (1))) 96 { 97 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 98 "Could not dump state to `%s'\n", 99 filename); 100 return; 101 } 102 return; 103 } 104 { 105 char *state_str = json_dumps (state, 106 JSON_INDENT (1)); 107 if (-1 >= 108 fprintf (stdout, 109 "%s", 110 state_str)) 111 { 112 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 113 "Could not dump state to stdout\n"); 114 GNUNET_free (state_str); 115 return; 116 } 117 GNUNET_free (state_str); 118 } 119 } 120 121 122 /** 123 * @brief Shutdown the application. 124 * 125 * @param cls closure 126 */ 127 static void 128 shutdown_task (void *cls) 129 { 130 (void) cls; 131 132 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 133 "Shutdown initiated\n"); 134 if (NULL != ra) 135 { 136 FROSIX_redux_action_cancel (ra); 137 } 138 if (NULL != ctx) 139 { 140 GNUNET_CURL_fini (ctx); 141 ctx = NULL; 142 } 143 if (NULL != rc) 144 { 145 GNUNET_CURL_gnunet_rc_destroy (rc); 146 rc = NULL; 147 } 148 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 149 "Shutdown complete\n"); 150 } 151 152 153 /** 154 * Function called with the results of #FROSIX_redux_action(). 155 * 156 * @param cls closure 157 * @param error_code Error code 158 * @param result_state new state as result 159 */ 160 static void 161 action_cb (void *cls, 162 enum TALER_ErrorCode error_code, 163 json_t *result_state) 164 { 165 (void) cls; 166 // ra = NULL; 167 if (NULL != result_state) 168 169 persist_new_state (result_state, 170 output_filename); 171 172 if (TALER_EC_NONE != error_code) 173 { 174 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 175 "Redux failed with error %d: %s\n", 176 error_code, 177 TALER_ErrorCode_get_hint (error_code)); 178 json_dumpf (result_state, 179 stderr, 180 JSON_INDENT (2)); 181 fprintf (stderr, "action_cb - error\n"); 182 } 183 184 /* free json argument and result */ 185 json_decref (result_state); 186 187 GNUNET_SCHEDULER_shutdown (); 188 global_ret = (TALER_EC_NONE != error_code) ? 1 : 0; 189 } 190 191 192 /** 193 * @brief Start the application 194 * 195 * @param cls closure 196 * @param args arguments left 197 * @param cfgfile config file name 198 * @param cfg handle for the configuration file 199 */ 200 static void 201 run (void *cls, 202 char *const *args, 203 const char *cfgfile, 204 const struct GNUNET_CONFIGURATION_Handle *cfg) 205 { 206 (void) cls; 207 json_error_t error; 208 209 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 210 "Starting frosix-reducer\n"); 211 GNUNET_SCHEDULER_add_shutdown (&shutdown_task, 212 NULL); 213 214 /* action processing */ 215 { 216 const char *action = args[0]; 217 218 if (NULL == action) 219 { 220 GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE, 221 "You must specify an action as the first argument (or `-k' or `-s')\n"); 222 GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE, 223 "Example: frosix-reducer --keygen -a 'json-formatted-argument'\n"); 224 GNUNET_SCHEDULER_shutdown (); 225 global_ret = 1; 226 return; 227 } 228 229 /*** KEYGEN ***/ 230 if (0 == strcasecmp (action, 231 "keygen")) 232 { 233 /* load json file from std input */ 234 std_input = json_loadf (stdin, 235 JSON_DECODE_ANY, 236 &error); 237 if (NULL == std_input) 238 { 239 GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE, 240 "Failed to parse arguments on line %u:%u: %s!\n", 241 error.line, 242 error.column, 243 error.text); 244 GNUNET_SCHEDULER_shutdown (); 245 global_ret = 1; 246 return; 247 } 248 249 /* initialize HTTP client event loop */ 250 ctx = GNUNET_CURL_init (&GNUNET_CURL_gnunet_scheduler_reschedule, 251 &rc); 252 rc = GNUNET_CURL_gnunet_rc_create (ctx); 253 254 FROSIX_redux_init (ctx); 255 256 ra = FROSIX_redux_keygen_start (std_input, 257 &action_cb, 258 cls); 259 } 260 261 /*** REQUEST-CHALLENGE ***/ 262 if (0 == strcasecmp (action, 263 "request-challenge")) 264 { 265 /* check if -a and -m flag, abort if not found */ 266 if (NULL != input && NULL != message) 267 { 268 std_input = json_loadf (stdin, 269 JSON_DECODE_ANY, 270 &error); 271 if (NULL == std_input) 272 { 273 GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE, 274 "Failed to parse arguments on line %u:%u: %s!\n", 275 error.line, 276 error.column, 277 error.text); 278 GNUNET_SCHEDULER_shutdown (); 279 global_ret = 1; 280 return; 281 } 282 283 arguments = json_loads (input, 284 JSON_DECODE_ANY, 285 &error); 286 if (NULL == arguments) 287 { 288 GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE, 289 "Failed to parse arguments on line %u:%u: %s!\n", 290 error.line, 291 error.column, 292 error.text); 293 GNUNET_SCHEDULER_shutdown (); 294 global_ret = 1; 295 return; 296 } 297 } 298 else { 299 GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE, 300 "Failed to parse arguments on line %u:%u: %s!\n", 301 error.line, 302 error.column, 303 error.text); 304 GNUNET_SCHEDULER_shutdown (); 305 global_ret = 1; 306 return; 307 } 308 309 /* initialize HTTP client event loop */ 310 ctx = GNUNET_CURL_init (&GNUNET_CURL_gnunet_scheduler_reschedule, 311 &rc); 312 rc = GNUNET_CURL_gnunet_rc_create (ctx); 313 314 FROSIX_redux_init (ctx); 315 316 ra = FROSIX_redux_challenge_request_start (std_input, 317 arguments, 318 message, 319 &action_cb, 320 cls); 321 } 322 323 /*** SIGN ***/ 324 if (0 == strcasecmp (action, 325 "sign")) 326 { 327 /* check if -a and -m flag, abort if not found */ 328 if (NULL != input && NULL != message) 329 { 330 std_input = json_loadf (stdin, 331 JSON_DECODE_ANY, 332 &error); 333 if (NULL == std_input) 334 { 335 GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE, 336 "Failed to parse arguments on line %u:%u: %s!\n", 337 error.line, 338 error.column, 339 error.text); 340 GNUNET_SCHEDULER_shutdown (); 341 global_ret = 1; 342 return; 343 } 344 345 arguments = json_loads (input, 346 JSON_DECODE_ANY, 347 &error); 348 if (NULL == arguments) 349 { 350 GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE, 351 "Failed to parse arguments on line %u:%u: %s!\n", 352 error.line, 353 error.column, 354 error.text); 355 GNUNET_SCHEDULER_shutdown (); 356 global_ret = 1; 357 return; 358 } 359 } 360 else { 361 GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE, 362 "Failed to parse arguments on line %u:%u: %s!\n", 363 error.line, 364 error.column, 365 error.text); 366 GNUNET_SCHEDULER_shutdown (); 367 global_ret = 1; 368 return; 369 } 370 371 /* initialize HTTP client event loop */ 372 ctx = GNUNET_CURL_init (&GNUNET_CURL_gnunet_scheduler_reschedule, 373 &rc); 374 rc = GNUNET_CURL_gnunet_rc_create (ctx); 375 376 FROSIX_redux_init (ctx); 377 378 ra = FROSIX_redux_sign_start (std_input, 379 arguments, 380 message, 381 &action_cb, 382 cls); 383 } 384 385 /*** VERIFY ***/ 386 if (0 == strcasecmp (action, 387 "verify")) 388 { 389 /* check if -m flag, abort if not found */ 390 if (NULL != message) 391 { 392 std_input = json_loadf (stdin, 393 JSON_DECODE_ANY, 394 &error); 395 if (NULL == std_input) 396 { 397 GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE, 398 "Failed to parse arguments on line %u:%u: %s!\n", 399 error.line, 400 error.column, 401 error.text); 402 GNUNET_SCHEDULER_shutdown (); 403 global_ret = 1; 404 return; 405 } 406 } 407 else { 408 GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE, 409 "Failed to parse arguments on line %u:%u: %s!\n", 410 error.line, 411 error.column, 412 error.text); 413 GNUNET_SCHEDULER_shutdown (); 414 global_ret = 1; 415 return; 416 } 417 418 /* verify signature and return OK */ 419 if (GNUNET_OK != FROSIX_verify_signature (message, 420 std_input)) 421 { 422 GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE, 423 "Failed to verify signature!\n"); 424 global_ret = 1; 425 } 426 else { 427 GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE, 428 "Signature verified!\n"); 429 } 430 } 431 432 /*** EXPORT-PK ***/ 433 if (0 == strcasecmp (action, 434 "export-pk")) 435 { 436 std_input = json_loadf (stdin, 437 JSON_DECODE_ANY, 438 &error); 439 if (NULL == std_input) 440 { 441 GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE, 442 "Failed to parse arguments on line %u:%u: %s!\n", 443 error.line, 444 error.column, 445 error.text); 446 GNUNET_SCHEDULER_shutdown (); 447 global_ret = 1; 448 return; 449 } 450 451 FROSIX_export_public_key (std_input, 452 &action_cb, 453 cls); 454 } 455 456 /*** VERIFY-PK ***/ 457 if (0 == strcasecmp (action, 458 "verify-pk")) 459 { 460 std_input = json_loadf (stdin, 461 JSON_DECODE_ANY, 462 &error); 463 if (NULL == std_input) 464 { 465 GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE, 466 "Failed to parse arguments on line %u:%u: %s!\n", 467 error.line, 468 error.column, 469 error.text); 470 GNUNET_SCHEDULER_shutdown (); 471 global_ret = 1; 472 return; 473 } 474 475 /* verify signature and return OK */ 476 if (GNUNET_OK != FROSIX_verify_public_key (std_input)) 477 { 478 GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE, 479 "Failed to verify public key!\n"); 480 global_ret = 1; 481 } 482 else { 483 GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE, 484 "Public key verified!\n"); 485 } 486 } 487 488 /*** DELETE-KEY ***/ 489 if (0 == strcasecmp (action, 490 "delete-key")) 491 { 492 /* load json file from std input */ 493 std_input = json_loadf (stdin, 494 JSON_DECODE_ANY, 495 &error); 496 if (NULL == std_input) 497 { 498 GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE, 499 "Failed to parse arguments on line %u:%u: %s!\n", 500 error.line, 501 error.column, 502 error.text); 503 GNUNET_SCHEDULER_shutdown (); 504 global_ret = 1; 505 return; 506 } 507 508 /* initialize HTTP client event loop */ 509 ctx = GNUNET_CURL_init (&GNUNET_CURL_gnunet_scheduler_reschedule, 510 &rc); 511 rc = GNUNET_CURL_gnunet_rc_create (ctx); 512 513 FROSIX_redux_init (ctx); 514 515 ra = FROSIX_redux_key_delete_start (std_input, 516 &action_cb, 517 cls); 518 } 519 } 520 } 521 522 523 int 524 main (int argc, 525 char *const *argv) 526 { 527 /* the available command line options */ 528 struct GNUNET_GETOPT_CommandLineOption options[] = { 529 GNUNET_GETOPT_option_string ('a', 530 "arguments", 531 "JSON", 532 "pass a JSON string containing arguments to cli", 533 &input), 534 GNUNET_GETOPT_option_string ('o', 535 "output", 536 "Filename", 537 "define output filename", 538 &output_filename), 539 GNUNET_GETOPT_option_string ('m', 540 "message", 541 "Message to sign", 542 "The message to sign", 543 &message), 544 GNUNET_GETOPT_OPTION_END 545 }; 546 enum GNUNET_GenericReturnValue ret; 547 548 /* FIRST get the libtalerutil initialization out 549 of the way. Then throw that one away, and force 550 the SYNC defaults to be used! */ 551 (void) TALER_project_data_default (); 552 GNUNET_OS_init (FROSIX_project_data_default ()); 553 ret = GNUNET_PROGRAM_run (argc, 554 argv, 555 "frosix-cli", 556 "This is an application for using Frosix.\n", 557 options, 558 &run, 559 NULL); 560 if (GNUNET_SYSERR == ret) 561 return 3; 562 if (GNUNET_NO == ret) 563 return 0; 564 return global_ret; 565 } 566 567 568 /* end of frosix-cli-redux.c */