2 This file is part of GNUnet.
3 Copyright (C) 2012-2013 GNUnet e.V.
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
18 SPDX-License-Identifier: AGPL3.0-or-later
22 * @brief command line tool to access command line Credential service
23 * @author Martin Schanzenbach
26 #include <gnunet_util_lib.h>
27 #include <gnunet_abd_service.h>
28 #include <gnunet_gnsrecord_lib.h>
29 #include <gnunet_namestore_service.h>
30 #include "delegate_misc.h"
31 #include "abd_serialization.h"
34 * Configuration we are using.
36 static const struct GNUNET_CONFIGURATION_Handle *cfg;
39 * Handle to the namestore.
41 static struct GNUNET_NAMESTORE_Handle *ns;
44 * Private key for the our zone.
46 static struct GNUNET_CRYPTO_EcdsaPrivateKey zone_pkey;
51 static struct GNUNET_IDENTITY_EgoLookup *el;
54 * Handle to Credential service.
56 static struct GNUNET_ABD_Handle *abd;
59 * Desired timeout for the lookup (default is no timeout).
61 static struct GNUNET_TIME_Relative timeout;
64 * Handle to verify request
66 static struct GNUNET_ABD_Request *verify_request;
69 * Handle to collect request
71 static struct GNUNET_ABD_Request *collect_request;
74 * Task scheduled to handle timeout.
76 static struct GNUNET_SCHEDULER_Task *tt;
79 * Return value of the commandline.
84 * Subject pubkey string
89 * Subject delegate string
91 static char *subject_delegate;
96 static char *expiration;
101 struct GNUNET_CRYPTO_EcdsaPublicKey subject_pkey;
106 struct GNUNET_CRYPTO_EcdsaPublicKey issuer_pkey;
110 * Issuer pubkey string
112 static char *issuer_key;
117 static char *ego_name;
122 static char *issuer_attr;
137 static int create_is;
142 static int create_ss;
150 * Signed issue credentials
157 static int is_private;
160 * Search direction: forward
165 * Search direction: backward
170 * API enum, filled and passed for collect/verify
172 enum GNUNET_ABD_AlgoDirectionFlags direction = 0;
175 * Queue entry for the 'add' operation.
177 static struct GNUNET_NAMESTORE_QueueEntry *add_qe;
180 * Value in binary format.
185 * Number of bytes in #data.
187 static size_t data_size;
190 * Type string converted to DNS type value.
192 static uint32_t type;
195 * Type of the record to add/remove, NULL to remove all.
197 static char *typestring;
199 * Expiration string converted to numeric value.
201 static uint64_t etime;
204 * Is expiration time relative or absolute time?
206 static int etime_is_rel = GNUNET_SYSERR;
209 * Fixed size of the public/private keys
211 static const int key_length = 52;
214 * Record label for storing delegations
216 static char *record_label;
219 * Task run on shutdown. Cleans up everything.
224 do_shutdown (void *cls)
226 if (NULL != verify_request)
228 GNUNET_ABD_request_cancel (verify_request);
229 verify_request = NULL;
233 GNUNET_ABD_disconnect (abd);
238 GNUNET_SCHEDULER_cancel (tt);
243 GNUNET_IDENTITY_ego_lookup_cancel (el);
248 GNUNET_NAMESTORE_cancel (add_qe);
253 GNUNET_NAMESTORE_disconnect (ns);
260 * Task run on timeout. Triggers shutdown.
265 do_timeout (void *cls)
268 GNUNET_SCHEDULER_shutdown ();
273 handle_intermediate_result (void *cls,
274 struct GNUNET_ABD_Delegation *dd,
279 prefix = "Backward -";
281 prefix = "Forward -";
283 printf ("%s Intermediate result: %s.%s <- %s.%s\n",
285 GNUNET_CRYPTO_ecdsa_public_key_to_string (&dd->issuer_key),
286 dd->issuer_attribute,
287 GNUNET_CRYPTO_ecdsa_public_key_to_string (&dd->subject_key),
288 dd->subject_attribute);
293 handle_collect_result (void *cls,
294 unsigned int d_count,
295 struct GNUNET_ABD_Delegation *dc,
296 unsigned int c_count,
297 struct GNUNET_ABD_Delegate *dele)
302 verify_request = NULL;
305 for (i = 0; i < c_count; i++)
307 line = GNUNET_ABD_delegate_to_string (&dele[i]);
308 printf ("%s\n", line);
314 printf ("Received NULL\n");
317 GNUNET_SCHEDULER_shutdown ();
322 handle_verify_result (void *cls,
323 unsigned int d_count,
324 struct GNUNET_ABD_Delegation *dc,
325 unsigned int c_count,
326 struct GNUNET_ABD_Delegate *dele)
332 verify_request = NULL;
337 printf ("Delegation Chain:\n");
338 for (i = 0; i < d_count; i++)
340 iss_key = GNUNET_CRYPTO_ecdsa_public_key_to_string (&dc[i].issuer_key);
341 sub_key = GNUNET_CRYPTO_ecdsa_public_key_to_string (&dc[i].subject_key);
343 if (0 != dc[i].subject_attribute_len)
345 printf ("(%d) %s.%s <- %s.%s\n",
348 dc[i].issuer_attribute,
350 dc[i].subject_attribute);
354 printf ("(%d) %s.%s <- %s\n",
357 dc[i].issuer_attribute,
360 GNUNET_free (iss_key);
361 GNUNET_free (sub_key);
363 printf ("\nDelegate(s):\n");
364 for (i = 0; i < c_count; i++)
366 iss_key = GNUNET_CRYPTO_ecdsa_public_key_to_string (&dele[i].issuer_key);
367 sub_key = GNUNET_CRYPTO_ecdsa_public_key_to_string (&dele[i].subject_key);
368 printf ("%s.%s <- %s\n", iss_key, dele[i].issuer_attribute, sub_key);
369 GNUNET_free (iss_key);
370 GNUNET_free (sub_key);
372 printf ("Successful.\n");
375 GNUNET_SCHEDULER_shutdown ();
380 * Callback invoked from identity service with ego information.
381 * An @a ego of NULL means the ego was not found.
383 * @param cls closure with the configuration
384 * @param ego an ego known to identity service, or NULL
387 identity_cb (void *cls, const struct GNUNET_IDENTITY_Ego *ego)
389 const struct GNUNET_CRYPTO_EcdsaPrivateKey *privkey;
394 if (NULL != ego_name)
397 _ ("Ego `%s' not known to identity service\n"),
400 GNUNET_SCHEDULER_shutdown ();
404 if (GNUNET_YES == collect)
408 GNUNET_CRYPTO_ecdsa_public_key_from_string (issuer_key,
413 _ ("Issuer public key `%s' is not well-formed\n"),
415 GNUNET_SCHEDULER_shutdown ();
417 privkey = GNUNET_IDENTITY_ego_get_private_key (ego);
419 collect_request = GNUNET_ABD_collect (abd,
424 &handle_collect_result,
426 &handle_intermediate_result,
430 GNUNET_SCHEDULER_shutdown ();
435 * Parse expiration time.
437 * @param expirationstring text to parse
438 * @param etime_is_rel[out] set to #GNUNET_YES if time is relative
439 * @param etime[out] set to expiration time (abs or rel)
440 * @return #GNUNET_OK on success
443 parse_expiration (const char *expirationstring,
447 // copied from namestore/gnunet-namestore.c
448 struct GNUNET_TIME_Relative etime_rel;
449 struct GNUNET_TIME_Absolute etime_abs;
451 if (0 == strcmp (expirationstring, "never"))
453 *etime = GNUNET_TIME_UNIT_FOREVER_ABS.abs_value_us;
454 *etime_is_rel = GNUNET_NO;
458 GNUNET_STRINGS_fancy_time_to_relative (expirationstring, &etime_rel))
460 *etime_is_rel = GNUNET_YES;
461 *etime = etime_rel.rel_value_us;
462 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
463 "Storing record with relative expiration time of %s\n",
464 GNUNET_STRINGS_relative_time_to_string (etime_rel, GNUNET_NO));
468 GNUNET_STRINGS_fancy_time_to_absolute (expirationstring, &etime_abs))
470 *etime_is_rel = GNUNET_NO;
471 *etime = etime_abs.abs_value_us;
472 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
473 "Storing record with absolute expiration time of %s\n",
474 GNUNET_STRINGS_absolute_time_to_string (etime_abs));
477 return GNUNET_SYSERR;
482 * Function called if lookup fails.
487 fprintf (stderr, "Error occured during lookup, shutting down.\n");
488 GNUNET_SCHEDULER_shutdown ();
494 add_continuation (void *cls, int32_t success, const char *emsg)
496 struct GNUNET_NAMESTORE_QueueEntry **qe = cls;
499 if (GNUNET_OK == success)
500 printf ("Adding successful.\n");
502 fprintf (stderr, "Error occured during adding, shutting down.\n");
504 GNUNET_SCHEDULER_shutdown ();
509 get_existing_record (void *cls,
510 const struct GNUNET_CRYPTO_EcdsaPrivateKey *zone_key,
511 const char *rec_name,
512 unsigned int rd_count,
513 const struct GNUNET_GNSRECORD_Data *rd)
515 struct GNUNET_GNSRECORD_Data rdn[rd_count + 1];
516 struct GNUNET_GNSRECORD_Data *rde;
518 memset (rdn, 0, sizeof (struct GNUNET_GNSRECORD_Data));
519 GNUNET_memcpy (&rdn[1], rd, rd_count * sizeof (struct GNUNET_GNSRECORD_Data));
522 rde->data_size = data_size;
523 rde->record_type = type;
526 if (GNUNET_YES == is_private)
527 rde->flags |= GNUNET_GNSRECORD_RF_PRIVATE;
528 rde->expiration_time = etime;
529 if (GNUNET_YES == etime_is_rel)
530 rde->flags |= GNUNET_GNSRECORD_RF_RELATIVE_EXPIRATION;
531 else if (GNUNET_NO != etime_is_rel)
532 rde->expiration_time = GNUNET_TIME_UNIT_FOREVER_ABS.abs_value_us;
534 GNUNET_assert (NULL != rec_name);
535 add_qe = GNUNET_NAMESTORE_records_store (ns,
548 store_cb (void *cls, const struct GNUNET_IDENTITY_Ego *ego)
550 const struct GNUNET_CONFIGURATION_Handle *cfg = cls;
554 ns = GNUNET_NAMESTORE_connect (cfg);
557 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
558 _ ("Failed to connect to namestore\n"));
559 GNUNET_SCHEDULER_shutdown ();
564 zone_pkey = *GNUNET_IDENTITY_ego_get_private_key (ego);
566 if (GNUNET_GNSRECORD_TYPE_DELEGATE == type)
569 struct GNUNET_ABD_Delegate *cred;
570 cred = GNUNET_ABD_delegate_from_string (import);
572 // Get import subject public key string
573 char *subject_pubkey_str =
574 GNUNET_CRYPTO_ecdsa_public_key_to_string (&cred->subject_key);
576 // Get zone public key string
577 struct GNUNET_CRYPTO_EcdsaPublicKey zone_pubkey;
578 GNUNET_IDENTITY_ego_get_public_key (ego, &zone_pubkey);
579 char *zone_pubkey_str =
580 GNUNET_CRYPTO_ecdsa_public_key_to_string (&zone_pubkey);
582 // Check if the subject key in the signed import matches the zone's key it is issued to
583 if (strcmp (zone_pubkey_str, subject_pubkey_str) != 0)
586 "Import signed delegate does not match this ego's public key.\n");
587 GNUNET_SCHEDULER_shutdown ();
592 etime = cred->expiration.abs_value_us;
593 etime_is_rel = GNUNET_NO;
595 // Prepare the data to be store in the record
596 data_size = GNUNET_ABD_delegate_serialize (cred, (char **) &data);
601 // For all other types e.g. GNUNET_GNSRECORD_TYPE_ATTRIBUTE
603 GNUNET_GNSRECORD_string_to_value (type, subject, &data, &data_size))
606 "Value `%s' invalid for record type `%s'\n",
609 GNUNET_SCHEDULER_shutdown ();
613 // Take care of expiration
614 if (NULL == expiration)
616 fprintf (stderr, "Missing option -e for operation 'create'\n");
617 GNUNET_SCHEDULER_shutdown ();
620 if (GNUNET_OK != parse_expiration (expiration, &etime_is_rel, &etime))
622 fprintf (stderr, "Invalid time format `%s'\n", expiration);
623 GNUNET_SCHEDULER_shutdown ();
629 add_qe = GNUNET_NAMESTORE_records_lookup (ns,
634 &get_existing_record,
641 sign_cb (void *cls, const struct GNUNET_IDENTITY_Ego *ego)
643 const struct GNUNET_CRYPTO_EcdsaPrivateKey *privkey;
644 struct GNUNET_ABD_Delegate *dele;
645 struct GNUNET_TIME_Absolute etime_abs;
650 // work on expiration time
651 if (NULL == expiration)
653 fprintf (stderr, "Please specify a TTL\n");
654 GNUNET_SCHEDULER_shutdown ();
657 else if (GNUNET_OK !=
658 GNUNET_STRINGS_fancy_time_to_absolute (expiration, &etime_abs))
661 "%s is not a valid ttl! Only absolute times are accepted!\n",
663 GNUNET_SCHEDULER_shutdown ();
667 // If contains a space - split it by the first space only - assume first entry is subject followed by attribute(s)
668 char *subject_pubkey_str;
669 char *subject_attr = NULL;
672 // Subject Public Key
673 token = strtok (subject, " ");
674 if (key_length == strlen (token))
676 subject_pubkey_str = token;
680 fprintf (stderr, "Key error, wrong length: %ld!\n", strlen (token));
681 GNUNET_SCHEDULER_shutdown ();
684 // Subject Attribute(s)
685 token = strtok (NULL, " ");
688 subject_attr = token;
692 privkey = GNUNET_IDENTITY_ego_get_private_key (ego);
695 GNUNET_CRYPTO_ecdsa_public_key_from_string (subject_pubkey_str,
696 strlen (subject_pubkey_str),
700 "Subject public key `%s' is not well-formed\n",
702 GNUNET_SCHEDULER_shutdown ();
707 dele = GNUNET_ABD_delegate_issue (privkey,
712 res = GNUNET_ABD_delegate_to_string (dele);
714 printf ("%s\n", res);
716 GNUNET_free_non_null (ego_name);
719 GNUNET_SCHEDULER_shutdown ();
724 * Main function that will be run.
727 * @param args remaining command-line arguments
728 * @param cfgfile name of the configuration file used (for saving, can be NULL!)
729 * @param c configuration
735 const struct GNUNET_CONFIGURATION_Handle *c)
739 tt = GNUNET_SCHEDULER_add_delayed (timeout, &do_timeout, NULL);
740 GNUNET_SCHEDULER_add_shutdown (&do_shutdown, NULL);
742 // Check relevant cmdline parameters
743 if (GNUNET_YES == create_is)
745 if (NULL == ego_name)
747 fprintf (stderr, "Missing option '-ego'\n");
748 GNUNET_SCHEDULER_shutdown ();
751 if (NULL == issuer_attr)
753 fprintf (stderr, "Missing option '-attribute' for issuer attribute\n");
754 GNUNET_SCHEDULER_shutdown ();
759 fprintf (stderr, "Missing option -subject for operation 'create'.'\n");
760 GNUNET_SCHEDULER_shutdown ();
764 // Lookup ego, on success call store_cb and store as ATTRIBUTE type
765 type = GNUNET_GNSRECORD_TYPE_ATTRIBUTE;
766 record_label = issuer_attr;
767 el = GNUNET_IDENTITY_ego_lookup (cfg, ego_name, &store_cb, (void *) cfg);
771 if (GNUNET_YES == create_ss)
774 // check if signed parameter has been passed in cmd line call
777 fprintf (stderr, "'import' required\n");
778 GNUNET_SCHEDULER_shutdown ();
782 type = GNUNET_GNSRECORD_TYPE_DELEGATE;
783 record_label = GNUNET_GNS_EMPTY_LABEL_AT;
784 // Store subject side
785 el = GNUNET_IDENTITY_ego_lookup (cfg, ego_name, &store_cb, (void *) cfg);
790 if (GNUNET_YES == sign_ss)
792 if (NULL == ego_name)
794 fprintf (stderr, "ego required\n");
795 GNUNET_SCHEDULER_shutdown ();
800 fprintf (stderr, "Subject public key needed\n");
801 GNUNET_SCHEDULER_shutdown ();
805 // lookup ego and call function sign_cb on success
806 el = GNUNET_IDENTITY_ego_lookup (cfg, ego_name, &sign_cb, (void *) cfg);
810 if ((GNUNET_NO == forward) && (GNUNET_NO == backward))
812 // set default: bidirectional
813 forward = GNUNET_YES;
814 backward = GNUNET_YES;
816 if (GNUNET_YES == forward)
817 direction |= GNUNET_ABD_FLAG_FORWARD;
818 if (GNUNET_YES == backward)
819 direction |= GNUNET_ABD_FLAG_BACKWARD;
821 if (GNUNET_YES == collect)
823 if (NULL == issuer_key)
825 fprintf (stderr, _ ("Issuer public key not well-formed\n"));
826 GNUNET_SCHEDULER_shutdown ();
830 abd = GNUNET_ABD_connect (cfg);
834 fprintf (stderr, _ ("Failed to connect to ABD\n"));
835 GNUNET_SCHEDULER_shutdown ();
838 if (NULL == issuer_attr)
840 fprintf (stderr, _ ("You must provide issuer the attribute\n"));
841 GNUNET_SCHEDULER_shutdown ();
845 if (NULL == ego_name)
847 fprintf (stderr, _ ("ego required\n"));
848 GNUNET_SCHEDULER_shutdown ();
851 el = GNUNET_IDENTITY_ego_lookup (cfg, ego_name, &identity_cb, (void *) cfg);
857 fprintf (stderr, _ ("Subject public key needed\n"));
858 GNUNET_SCHEDULER_shutdown ();
861 if (GNUNET_OK != GNUNET_CRYPTO_ecdsa_public_key_from_string (subject,
866 _ ("Subject public key `%s' is not well-formed\n"),
868 GNUNET_SCHEDULER_shutdown ();
872 if (GNUNET_YES == verify)
874 if (NULL == issuer_key)
876 fprintf (stderr, _ ("Issuer public key not well-formed\n"));
877 GNUNET_SCHEDULER_shutdown ();
881 GNUNET_CRYPTO_ecdsa_public_key_from_string (issuer_key,
886 _ ("Issuer public key `%s' is not well-formed\n"),
888 GNUNET_SCHEDULER_shutdown ();
891 abd = GNUNET_ABD_connect (cfg);
895 fprintf (stderr, _ ("Failed to connect to ABD\n"));
896 GNUNET_SCHEDULER_shutdown ();
899 if ((NULL == issuer_attr) || (NULL == subject_delegate))
901 fprintf (stderr, _ ("You must provide issuer and subject attributes\n"));
902 GNUNET_SCHEDULER_shutdown ();
906 // Subject credentials are comma separated
907 char *tmp = GNUNET_strdup (subject_delegate);
908 char *tok = strtok (tmp, ",");
911 fprintf (stderr, "Invalid subject credentials\n");
913 GNUNET_SCHEDULER_shutdown ();
918 while (NULL != (tok = strtok (NULL, ",")))
920 struct GNUNET_ABD_Delegate delegates[count];
921 struct GNUNET_ABD_Delegate *dele;
923 tmp = GNUNET_strdup (subject_delegate);
924 tok = strtok (tmp, ",");
925 for (i = 0; i < count; i++)
927 dele = GNUNET_ABD_delegate_from_string (tok);
928 GNUNET_memcpy (&delegates[i],
930 sizeof (struct GNUNET_ABD_Delegate));
931 delegates[i].issuer_attribute = GNUNET_strdup (dele->issuer_attribute);
932 tok = strtok (NULL, ",");
936 verify_request = GNUNET_ABD_verify (abd,
943 &handle_verify_result,
945 &handle_intermediate_result,
947 for (i = 0; i < count; i++)
949 GNUNET_free ((char *) delegates[i].issuer_attribute);
957 "Please specify name to lookup, subject key and issuer key!\n"));
958 GNUNET_SCHEDULER_shutdown ();
965 * The main function for gnunet-gns.
967 * @param argc number of arguments from the command line
968 * @param argv command line arguments
969 * @return 0 ok, 1 on error
972 main (int argc, char *const *argv)
974 struct GNUNET_GETOPT_CommandLineOption options[] =
975 {GNUNET_GETOPT_option_flag ('V',
978 "verify credential against attribute"),
980 GNUNET_GETOPT_option_string (
985 "The public key of the subject to lookup the"
986 "credential for, or for issuer side storage: subject and its attributes"),
988 GNUNET_GETOPT_option_string (
992 gettext_noop ("The private, signed delegate presented by the subject"),
994 GNUNET_GETOPT_option_string (
999 "The public key of the authority to verify the credential against"),
1001 GNUNET_GETOPT_option_string ('e',
1004 gettext_noop ("The ego/zone name to use"),
1006 GNUNET_GETOPT_option_string (
1010 gettext_noop ("The issuer attribute to verify against or to issue"),
1012 GNUNET_GETOPT_option_string ('T',
1016 "The time to live for the credential."
1017 "e.g. 5m, 6h, \"1990-12-30 12:00:00\""),
1019 GNUNET_GETOPT_option_flag ('g',
1021 gettext_noop ("collect credentials"),
1023 GNUNET_GETOPT_option_flag ('U',
1026 "Create and issue a credential issuer side."),
1028 GNUNET_GETOPT_option_flag ('C',
1029 "createSubjectSide",
1031 "Issue a credential subject side."),
1033 GNUNET_GETOPT_option_flag (
1036 gettext_noop ("Create, sign and return a credential subject side."),
1038 GNUNET_GETOPT_option_string (
1043 "Import signed credentials that should be issued to a zone/ego"),
1045 GNUNET_GETOPT_option_flag ('P',
1047 gettext_noop ("Create private record entry."),
1049 GNUNET_GETOPT_option_flag (
1053 "Indicates that the collect/verify process is done via forward search."),
1055 GNUNET_GETOPT_option_flag (
1059 "Indicates that the collect/verify process is done via forward search."),
1061 GNUNET_GETOPT_OPTION_END};
1064 timeout = GNUNET_TIME_UNIT_FOREVER_REL;
1065 if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
1068 GNUNET_log_setup ("gnunet-abd", "WARNING", NULL);
1069 if (GNUNET_OK != GNUNET_PROGRAM_run (argc,
1072 _ ("GNUnet abd resolver tool"),
1077 GNUNET_free ((void *) argv);
1082 /* end of gnunet-abd.c */