/*
This file is part of GNUnet.
Copyright (C) 2012-2013 GNUnet e.V.
GNUnet is free software: you can redistribute it and/or modify it
under the terms of the GNU Affero General Public License as published
by the Free Software Foundation, either version 3 of the License,
or (at your option) any later version.
GNUnet is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see .
SPDX-License-Identifier: AGPL3.0-or-later
*/
/**
* @file gnunet-credential.c
* @brief command line tool to access command line Credential service
* @author Martin Schanzenbach
*/
#include "platform.h"
#include
#include
#include
#include
#include "credential_misc.h"
#include "credential_serialization.h"
/**
* Configuration we are using.
*/
static const struct GNUNET_CONFIGURATION_Handle *cfg;
/**
* Handle to the namestore.
*/
static struct GNUNET_NAMESTORE_Handle *ns;
/**
* Private key for the our zone.
*/
static struct GNUNET_CRYPTO_EcdsaPrivateKey zone_pkey;
/**
* EgoLookup
*/
static struct GNUNET_IDENTITY_EgoLookup *el;
/**
* Handle to Credential service.
*/
static struct GNUNET_CREDENTIAL_Handle *credential;
/**
* Desired timeout for the lookup (default is no timeout).
*/
static struct GNUNET_TIME_Relative timeout;
/**
* Handle to verify request
*/
static struct GNUNET_CREDENTIAL_Request *verify_request;
/**
* Handle to collect request
*/
static struct GNUNET_CREDENTIAL_Request *collect_request;
/**
* Task scheduled to handle timeout.
*/
static struct GNUNET_SCHEDULER_Task *tt;
/**
* Subject pubkey string
*/
static char *subject_key;
/**
* Subject credential string
*/
static char *subject_credential;
/**
* Credential TTL
*/
static char *expiration;
/**
* Subject key
*/
struct GNUNET_CRYPTO_EcdsaPublicKey subject_pkey;
/**
* Issuer key
*/
struct GNUNET_CRYPTO_EcdsaPublicKey issuer_pkey;
/**
* Issuer pubkey string
*/
static char *issuer_key;
/**
* ego
*/
static char *ego_name;
/**
* Issuer attribute
*/
static char *issuer_attr;
/**
* Verify mode
*/
static int verify;
/**
* Issue mode
*/
static int create_cred;
/**
* Collect mode
*/
static int collect;
/**
* Create mode
*/
static int create_is;
/**
* Create mode
*/
static int create_ss;
/**
* Create mode
*/
static int sign_ss;
/**
* Add mode
*/
static int add_iss;
/**
* Signed issue credentials
*/
static char *extension;
/**
* Queue entry for the 'add' operation.
*/
static struct GNUNET_NAMESTORE_QueueEntry *add_qe;
/**
* Value in binary format.
*/
static void *data;
/**
* Number of bytes in #data.
*/
static size_t data_size;
/**
* Type string converted to DNS type value.
*/
static uint32_t type;
/**
* Type of the record to add/remove, NULL to remove all.
*/
static char *typestring;
/**
* Expiration string converted to numeric value.
*/
static uint64_t etime;
/**
* Is expiration time relative or absolute time?
*/
static int etime_is_rel = GNUNET_SYSERR;
/**
* Task run on shutdown. Cleans up everything.
*
* @param cls unused
*/
static void
do_shutdown (void *cls)
{
if (NULL != verify_request)
{
GNUNET_CREDENTIAL_request_cancel (verify_request);
verify_request = NULL;
}
if (NULL != credential)
{
GNUNET_CREDENTIAL_disconnect (credential);
credential = NULL;
}
if (NULL != tt)
{
GNUNET_SCHEDULER_cancel (tt);
tt = NULL;
}
if (NULL != el)
{
GNUNET_IDENTITY_ego_lookup_cancel (el);
el = NULL;
}
if (NULL != add_qe)
{
GNUNET_NAMESTORE_cancel (add_qe);
add_qe = NULL;
}
if (NULL != ns)
{
GNUNET_NAMESTORE_disconnect (ns);
ns = NULL;
}
}
/**
* Task run on timeout. Triggers shutdown.
*
* @param cls unused
*/
static void
do_timeout (void *cls)
{
tt = NULL;
GNUNET_SCHEDULER_shutdown ();
}
static void
handle_collect_result (void *cls,
unsigned int d_count,
struct GNUNET_CREDENTIAL_Delegation *dc,
unsigned int c_count,
struct GNUNET_CREDENTIAL_Credential *cred)
{
int i;
char* line;
verify_request = NULL;
if (NULL != cred)
{
for (i=0;i DKCC5SMTBNV6W3VXDJ7A1N1YS6TRG7B3XC2S5N4HSXJEYYRFRCCG | D1NuT8hHEUbkCURo1lkcSPKhYiydhv4nMkV042kc9J4MgIhB2/fQKLgJUyuGlJKvYgXLf4jHXNRHJe+aCLG7jw== | 1561126006528100
//TODO: parse, wenn nicht als argument direkt geparsed werden kann
char cmd_para[100];
char para_str[1024];
char *token;
char *tmp_str;
int matches = 0;
tmp_str = GNUNET_strdup (extensionstring);
// use special strtok to match multiple characters
token = strtokm (tmp_str, "--");
while (NULL != token) {
// also fills the variables if "regex"-like match
fprintf(stderr, "TOKEN: %s\n", token);
// match everything till =, ignore = (%*c), match everything including whitespaces (required for the extension parameter)
matches = SSCANF (token, "%[^=]%*c%[^\n]", cmd_para, para_str);
// string not well formatted
if (0 == matches) {
GNUNET_log (GNUNET_ERROR_TYPE_ERROR, ("Failed to parse to extensionstring.\n"));
GNUNET_SCHEDULER_shutdown ();
GNUNET_free (tmp_str);
return GNUNET_SYSERR;
} else {
fprintf(stderr,"Found command and parameter: %s %s\n", cmd_para, para_str);
// assign values to variables, topntail to remove trailing/leading "
if(strcmp(cmd_para, "ego") == 0) {
fprintf(stderr,"ego found and parsed\n");
topntail(para_str);
ego_name = GNUNET_strdup(para_str);
} else if(strcmp(cmd_para, "attribute") == 0) {
fprintf(stderr,"issuer found and parsed\n");
topntail(para_str);
issuer_attr = GNUNET_strdup(para_str);
} else if(strcmp(cmd_para, "subject") == 0) {
fprintf(stderr,"subject found and parsed\n");
topntail(para_str);
subject_key = GNUNET_strdup(para_str);
} else if(strcmp(cmd_para, "ttl") == 0) {
fprintf(stderr,"ttl found and parsed\n");
expiration = GNUNET_strdup(para_str);
} else if(strcmp(cmd_para, "extension") == 0) {
fprintf(stderr,"extension found and parsed\n");
topntail(para_str);
extension = GNUNET_strdup(para_str);
}
}
token = strtokm (NULL, "--");
}
GNUNET_free (tmp_str);
//return GNUNET_SYSERR;
return GNUNET_OK;
}
/**
* Parse expiration time.
*
* @param expirationstring text to parse
* @param etime_is_rel[out] set to #GNUNET_YES if time is relative
* @param etime[out] set to expiration time (abs or rel)
* @return #GNUNET_OK on success
*/
static int
parse_expiration (const char *expirationstring,
int *etime_is_rel,
uint64_t *etime)
{
// TODO just copied from gnunet-namestore.c
struct GNUNET_TIME_Relative etime_rel;
struct GNUNET_TIME_Absolute etime_abs;
if (0 == strcmp (expirationstring,
"never"))
{
*etime = GNUNET_TIME_UNIT_FOREVER_ABS.abs_value_us;
*etime_is_rel = GNUNET_NO;
return GNUNET_OK;
}
if (GNUNET_OK ==
GNUNET_STRINGS_fancy_time_to_relative (expirationstring,
&etime_rel))
{
*etime_is_rel = GNUNET_YES;
*etime = etime_rel.rel_value_us;
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Storing record with relative expiration time of %s\n",
GNUNET_STRINGS_relative_time_to_string (etime_rel,
GNUNET_NO));
return GNUNET_OK;
}
if (GNUNET_OK ==
GNUNET_STRINGS_fancy_time_to_absolute (expirationstring,
&etime_abs))
{
*etime_is_rel = GNUNET_NO;
*etime = etime_abs.abs_value_us;
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Storing record with absolute expiration time of %s\n",
GNUNET_STRINGS_absolute_time_to_string (etime_abs));
return GNUNET_OK;
}
return GNUNET_SYSERR;
}
/**
* Function called if lookup fails.
*/
static void
error_cb (void *cls)
{
// TODO: Better
fprintf(stderr, "In add_error_cb\n");
GNUNET_SCHEDULER_shutdown ();
return;
}
static void
add_continuation (void *cls,
int32_t success,
const char *emsg)
{
fprintf(stderr, "Start: add_continuation\n");
struct GNUNET_NAMESTORE_QueueEntry **qe = cls;
*qe = NULL;
GNUNET_SCHEDULER_shutdown ();
}
static void
get_existing_record (void *cls,
const struct GNUNET_CRYPTO_EcdsaPrivateKey *zone_key,
const char *rec_name,
unsigned int rd_count,
const struct GNUNET_GNSRECORD_Data *rd)
{
struct GNUNET_GNSRECORD_Data rdn[rd_count + 1];
struct GNUNET_GNSRECORD_Data *rde;
fprintf(stderr, "Start: get_existing_record\n");
fprintf(stderr, "count: %d\n", rd_count);
memset (rdn, 0, sizeof (struct GNUNET_GNSRECORD_Data));
GNUNET_memcpy (&rdn[1],
rd,
rd_count * sizeof (struct GNUNET_GNSRECORD_Data));
rde = &rdn[0];
rde->data = data;
rde->data_size = data_size;
rde->record_type = type;
// TODO: flags
/*if (1 == is_shadow)
rde->flags |= GNUNET_GNSRECORD_RF_SHADOW_RECORD;
if (1 != is_public)
rde->flags |= GNUNET_GNSRECORD_RF_PRIVATE;*/
rde->expiration_time = etime;
if (GNUNET_YES == etime_is_rel)
rde->flags |= GNUNET_GNSRECORD_RF_RELATIVE_EXPIRATION;
else if (GNUNET_NO != etime_is_rel)
rde->expiration_time = GNUNET_TIME_UNIT_FOREVER_ABS.abs_value_us;
GNUNET_assert (NULL != rec_name);
add_qe = GNUNET_NAMESTORE_records_store (ns,
&zone_pkey,
rec_name,
rd_count + 1,
rde,
&add_continuation,
&add_qe);
return;
}
static void
store_cb (void *cls,
const struct GNUNET_IDENTITY_Ego *ego)
{
const struct GNUNET_CONFIGURATION_Handle *cfg = cls;
struct GNUNET_CRYPTO_EcdsaPublicKey pub;
fprintf(stderr, "Start: store_cb\n");
ns = GNUNET_NAMESTORE_connect (cfg);
if (NULL == ns)
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
_("Failed to connect to namestore\n"));
GNUNET_SCHEDULER_shutdown ();
return;
}
// Key handling
fprintf(stderr, "Connected to ns\n");
zone_pkey = *GNUNET_IDENTITY_ego_get_private_key (ego);
fprintf(stderr, "Got zone_pkey\n");
// TODO rename to zone_pub?
GNUNET_CRYPTO_ecdsa_key_get_public (&zone_pkey, &pub);
// Check relevant cmdline parameters
// name ⁼ issuer_attr
if (NULL == issuer_attr)
{
fprintf (stderr, "Missing option -attribute for operation 'create'.\n");
GNUNET_SCHEDULER_shutdown ();
return;
}
// TODO later, rename subject_key to subject
// value ⁼ subject_key
if (NULL == subject_key)
{
fprintf (stderr, "Missing option -subject for operation 'create'.'\n");
GNUNET_SCHEDULER_shutdown ();
return;
}
// String to value conversion for storage
if (GNUNET_OK != GNUNET_GNSRECORD_string_to_value (type,
subject_key,
&data,
&data_size))
{
fprintf (stderr, "Value `%s' invalid for record type `%s'\n",
subject_key,
typestring);
GNUNET_SCHEDULER_shutdown ();
return;
}
fprintf (stderr, "Data size: `%lu'\n", data_size);
// Take care of expiration
if (NULL == expiration)
{
fprintf (stderr, "Missing option -e for operation 'create'\n");
GNUNET_SCHEDULER_shutdown ();
return;
}
if (GNUNET_OK != parse_expiration (expiration,
&etime_is_rel,
&etime))
{
fprintf (stderr, "Invalid time format `%s'\n",
expiration);
GNUNET_SCHEDULER_shutdown ();
return;
}
// Start lookup
add_qe = GNUNET_NAMESTORE_records_lookup (ns,
&zone_pkey,
issuer_attr,
&error_cb,
NULL,
&get_existing_record,
NULL);
return;
}
static void
sign_cb (void *cls,
const struct GNUNET_IDENTITY_Ego *ego)
{
const struct GNUNET_CRYPTO_EcdsaPrivateKey *privkey;
struct GNUNET_CREDENTIAL_Credential *crd;
struct GNUNET_TIME_Absolute etime_abs;
struct GNUNET_TIME_Relative etime_rel;
char *res;
el = NULL;
// work on expiration time
if (NULL == expiration)
{
fprintf (stderr, "Please specify a TTL\n");
GNUNET_SCHEDULER_shutdown ();
return;
} else if (GNUNET_OK == GNUNET_STRINGS_fancy_time_to_relative (expiration, &etime_rel))
{
etime_abs = GNUNET_TIME_relative_to_absolute (etime_rel);
} else if (GNUNET_OK != GNUNET_STRINGS_fancy_time_to_absolute (expiration, &etime_abs))
{
fprintf (stderr, "%s is not a valid ttl!\n", expiration);
GNUNET_SCHEDULER_shutdown ();
return;
}
// if contains a space - split it by the first space only - assume first token entry is subject_key
fprintf (stderr, "Start splitting\n");
char *space;
int idx;
space = strchr(subject_key, ' ');
idx = (int)(space - subject_key);
// TODO rename subject_key to subject
char *subject_pubkey_str = GNUNET_malloc(idx+1);
GNUNET_memcpy(subject_pubkey_str, subject_key, idx);
subject_pubkey_str[idx] = '\0';
fprintf(stderr, "idx: %d, str: %s\n", idx, subject_pubkey_str);
// work on keys
privkey = GNUNET_IDENTITY_ego_get_private_key (ego);
if (GNUNET_OK != GNUNET_CRYPTO_ecdsa_public_key_from_string (subject_pubkey_str,
strlen (subject_pubkey_str),
&subject_pkey))
{
fprintf (stderr, "Subject public key `%s' is not well-formed\n", subject_pubkey_str);
GNUNET_SCHEDULER_shutdown ();
return;
}
// Sign credential / TODO not credential but delegate (new method), not only pass subject_pkey but also subject_attr
// gnunet-credential --issue --ego=registrarb --subject=$ALICE_KEY --attribute=$REG_STUD_ATTR --ttl=5m -c test_credential_lookup.conf
// gnunet-credential --create --ego=epub --attribute="a" --subject="B b" --where="ss" -E 60m
// TODO: only signs subject_pkey at the moment, also requires subject_attr (or both in subject_key)
crd = GNUNET_CREDENTIAL_credential_issue (privkey,
&subject_pkey,
issuer_attr,
&etime_abs);
res = GNUNET_CREDENTIAL_credential_to_string (crd);
fprintf(stderr,"Dele: %s\n", res);
GNUNET_free (crd);
printf ("--ego=\"%s\" --attribute=\"%s\" --subject=\"%s\" --ttl=%s --extension=\"%s\"\n", ego_name, issuer_attr, subject_key, expiration, res);
GNUNET_free_non_null (ego_name);
ego_name = NULL;
GNUNET_SCHEDULER_shutdown ();
}
/**
* Main function that will be run.
*
* @param cls closure
* @param args remaining command-line arguments
* @param cfgfile name of the configuration file used (for saving, can be NULL!)
* @param c configuration
*/
static void
run (void *cls,
char *const *args,
const char *cfgfile,
const struct GNUNET_CONFIGURATION_Handle *c)
{
cfg = c;
tt = GNUNET_SCHEDULER_add_delayed (timeout,
&do_timeout, NULL);
GNUNET_SCHEDULER_add_shutdown (&do_shutdown, NULL);
if (GNUNET_YES == create_is) {
fprintf(stderr, "Starting to create issuer side...\n");
if (NULL == ego_name) {
fprintf (stderr, "ego required\n");
GNUNET_SCHEDULER_shutdown ();
return;
}
type = GNUNET_GNSRECORD_TYPE_ATTRIBUTE;
//TODO: Store normally (at issuer, for backward search)
// stuff from gnunet-namestore.c of namestore folder
fprintf (stderr, "Start: Store issuer side\n");
el = GNUNET_IDENTITY_ego_lookup (cfg,
ego_name,
&store_cb,
(void *) cfg);
return;
}
if (GNUNET_YES == create_ss) {
fprintf(stderr, "Starting to create subject side...\n");
// check if "credential"/signed parameter filled
if (NULL == extension) {
fprintf (stderr, "'extension' required\n");
GNUNET_SCHEDULER_shutdown ();
return;
}
// parses all the passed parameters
parse_cmdl_param(extension);
fprintf (stderr,"List of parsed attributes:\n");
fprintf (stderr,"Ego: %s\n", ego_name);
fprintf (stderr,"Attribute: %s\n", issuer_attr);
fprintf (stderr,"Subject: %s\n", subject_key);
fprintf (stderr,"ttl: %s\n", expiration);
fprintf (stderr,"Extension: %s\n", extension);
//TODO: subject key does not have to be returned, extension replaces it
//TODO: use own delegation type, implement string_to_value and value_to_string methods of plugin
//type = GNUNET_GNSRECORD_TYPE_DELEGATE;
type = GNUNET_GNSRECORD_TYPE_CREDENTIAL;
subject_key = extension;
fprintf (stderr, "Start: Store subject side\n");
el = GNUNET_IDENTITY_ego_lookup (cfg,
ego_name,
&store_cb,
(void *) cfg);
return;
}
if (GNUNET_YES == sign_ss) {
fprintf(stderr, "Starting to sign subject side...\n");
if (NULL == ego_name) {
fprintf (stderr, "ego required\n");
GNUNET_SCHEDULER_shutdown ();
return;
}
if (NULL == subject_key)
{
fprintf (stderr, "Subject public key needed\n");
GNUNET_SCHEDULER_shutdown ();
return;
}
//TODO: Sign like credential and return to store subject side
//TODO: Return everything as an input for the add
//TODO: Idee: Gleich add machen, statt return und neues add
fprintf (stderr, "Start: Sign, return and subject side store\n");
el = GNUNET_IDENTITY_ego_lookup (cfg,
ego_name,
&sign_cb,
(void *) cfg);
return;
}
if (GNUNET_YES == collect) {
if (NULL == issuer_key)
{
fprintf (stderr,
_("Issuer public key not well-formed\n"));
GNUNET_SCHEDULER_shutdown ();
return;
}
credential = GNUNET_CREDENTIAL_connect (cfg);
if (NULL == credential)
{
fprintf (stderr,
_("Failed to connect to CREDENTIAL\n"));
GNUNET_SCHEDULER_shutdown ();
return;
}
if (NULL == issuer_attr)
{
fprintf (stderr,
_("You must provide issuer the attribute\n"));
GNUNET_SCHEDULER_shutdown ();
return;
}
if (NULL == ego_name) {
fprintf (stderr,
_("ego required\n"));
GNUNET_SCHEDULER_shutdown ();
return;
}
el = GNUNET_IDENTITY_ego_lookup (cfg,
ego_name,
&identity_cb,
(void *) cfg);
return;
}
if (NULL == subject_key)
{
fprintf (stderr,
_("Subject public key needed\n"));
GNUNET_SCHEDULER_shutdown ();
return;
}
if (GNUNET_OK !=
GNUNET_CRYPTO_ecdsa_public_key_from_string (subject_key,
strlen (subject_key),
&subject_pkey))
{
fprintf (stderr,
_("Subject public key `%s' is not well-formed\n"),
subject_key);
GNUNET_SCHEDULER_shutdown ();
return;
}
if (GNUNET_YES == verify) {
if (NULL == issuer_key)
{
fprintf (stderr,
_("Issuer public key not well-formed\n"));
GNUNET_SCHEDULER_shutdown ();
return;
}
if (GNUNET_OK !=
GNUNET_CRYPTO_ecdsa_public_key_from_string (issuer_key,
strlen (issuer_key),
&issuer_pkey))
{
fprintf (stderr,
_("Issuer public key `%s' is not well-formed\n"),
issuer_key);
GNUNET_SCHEDULER_shutdown ();
return;
}
credential = GNUNET_CREDENTIAL_connect (cfg);
if (NULL == credential)
{
fprintf (stderr,
_("Failed to connect to CREDENTIAL\n"));
GNUNET_SCHEDULER_shutdown ();
return;
}
if (NULL == issuer_attr || NULL == subject_credential)
{
fprintf (stderr,
_("You must provide issuer and subject attributes\n"));
GNUNET_SCHEDULER_shutdown ();
return;
}
//Subject credentials are comma separated
char *tmp = GNUNET_strdup (subject_credential);
char *tok = strtok (tmp, ",");
if (NULL == tok)
{
fprintf (stderr,
"Invalid subject credentials\n");
GNUNET_free (tmp);
GNUNET_SCHEDULER_shutdown ();
return;
}
int count = 1;
int i;
while (NULL != (tok = strtok(NULL, ",")))
count++;
struct GNUNET_CREDENTIAL_Credential credentials[count];
struct GNUNET_CREDENTIAL_Credential *cred;
GNUNET_free (tmp);
tmp = GNUNET_strdup (subject_credential);
tok = strtok (tmp, ",");
for (i=0;iissuer_attribute);
tok = strtok(NULL, ",");
GNUNET_free (cred);
}
verify_request = GNUNET_CREDENTIAL_verify(credential,
&issuer_pkey,
issuer_attr, //TODO argument
&subject_pkey,
count,
credentials,
&handle_verify_result,
NULL);
for (i=0;i