/*
This file is part of GNUnet.
Copyright (C) 2012-2015 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
*/
/**
* @author Martin Schanzenbach
* @file src/reclaim/gnunet-reclaim.c
* @brief Identity Provider utility
*
*/
#include "platform.h"
#include
#include "gnunet_util_lib.h"
#include "gnunet_identity_service.h"
#include "gnunet_namestore_service.h"
#include "gnunet_reclaim_service.h"
#include "gnunet_signatures.h"
/**
* return value
*/
static int ret;
/**
* List attribute flag
*/
static int list;
/**
* List credentials flag
*/
static int list_credentials;
/**
* Credential ID string
*/
static char *credential_id;
/**
* Credential ID
*/
static struct GNUNET_RECLAIM_Identifier credential;
/**
* Credential name
*/
static char *credential_name;
/**
* Credential type
*/
static char *credential_type;
/**
* Credential exists
*/
static int credential_exists;
/**
* Relying party
*/
static char *rp;
/**
* The attribute
*/
static char *attr_name;
/**
* Attribute value
*/
static char *attr_value;
/**
* Attributes to issue
*/
static char *issue_attrs;
/**
* Ticket to consume
*/
static char *consume_ticket;
/**
* Attribute type
*/
static char *type_str;
/**
* Ticket to revoke
*/
static char *revoke_ticket;
/**
* Ticket listing
*/
static int list_tickets;
/**
* Ego name
*/
static char *ego_name;
/**
* Identity handle
*/
static struct GNUNET_IDENTITY_Handle *identity_handle;
/**
* reclaim handle
*/
static struct GNUNET_RECLAIM_Handle *reclaim_handle;
/**
* reclaim operation
*/
static struct GNUNET_RECLAIM_Operation *reclaim_op;
/**
* Attribute iterator
*/
static struct GNUNET_RECLAIM_AttributeIterator *attr_iterator;
/**
* Credential iterator
*/
static struct GNUNET_RECLAIM_CredentialIterator *cred_iterator;
/**
* Ticket iterator
*/
static struct GNUNET_RECLAIM_TicketIterator *ticket_iterator;
/**
* ego private key
*/
static const struct GNUNET_IDENTITY_PrivateKey *pkey;
/**
* rp public key
*/
static struct GNUNET_IDENTITY_PublicKey rp_key;
/**
* Ticket to consume
*/
static struct GNUNET_RECLAIM_Ticket ticket;
/**
* Attribute list
*/
static struct GNUNET_RECLAIM_AttributeList *attr_list;
/**
* Attribute expiration interval
*/
static struct GNUNET_TIME_Relative exp_interval;
/**
* Timeout task
*/
static struct GNUNET_SCHEDULER_Task *timeout;
/**
* Cleanup task
*/
static struct GNUNET_SCHEDULER_Task *cleanup_task;
/**
* Claim to store
*/
struct GNUNET_RECLAIM_Attribute *claim;
/**
* Claim to delete
*/
static char *attr_delete;
/**
* Claim object to delete
*/
static struct GNUNET_RECLAIM_Attribute *attr_to_delete;
static void
do_cleanup (void *cls)
{
cleanup_task = NULL;
if (NULL != timeout)
GNUNET_SCHEDULER_cancel (timeout);
if (NULL != reclaim_op)
GNUNET_RECLAIM_cancel (reclaim_op);
if (NULL != attr_iterator)
GNUNET_RECLAIM_get_attributes_stop (attr_iterator);
if (NULL != cred_iterator)
GNUNET_RECLAIM_get_credentials_stop (cred_iterator);
if (NULL != ticket_iterator)
GNUNET_RECLAIM_ticket_iteration_stop (ticket_iterator);
if (NULL != reclaim_handle)
GNUNET_RECLAIM_disconnect (reclaim_handle);
if (NULL != identity_handle)
GNUNET_IDENTITY_disconnect (identity_handle);
if (NULL != attr_list)
GNUNET_free (attr_list);
if (NULL != attr_to_delete)
GNUNET_free (attr_to_delete);
if (NULL == credential_type)
GNUNET_free (credential_type);
}
static void
ticket_issue_cb (void *cls,
const struct GNUNET_RECLAIM_Ticket *ticket,
const struct GNUNET_RECLAIM_PresentationList *presentations)
{
char *ticket_str;
reclaim_op = NULL;
if (NULL != ticket)
{
ticket_str =
GNUNET_STRINGS_data_to_string_alloc (ticket,
sizeof(
struct GNUNET_RECLAIM_Ticket));
printf ("%s\n", ticket_str);
GNUNET_free (ticket_str);
}
cleanup_task = GNUNET_SCHEDULER_add_now (&do_cleanup, NULL);
}
static void
store_cont (void *cls, int32_t success, const char *emsg)
{
reclaim_op = NULL;
if (GNUNET_SYSERR == success)
{
fprintf (stderr, "%s\n", emsg);
}
cleanup_task = GNUNET_SCHEDULER_add_now (&do_cleanup, NULL);
}
static void
process_attrs (void *cls,
const struct GNUNET_IDENTITY_PublicKey *identity,
const struct GNUNET_RECLAIM_Attribute *attr,
const struct GNUNET_RECLAIM_Presentation *presentation)
{
char *value_str;
char *id;
const char *attr_type;
if (NULL == identity)
{
reclaim_op = NULL;
cleanup_task = GNUNET_SCHEDULER_add_now (&do_cleanup, NULL);
return;
}
if (NULL == attr)
{
ret = 1;
return;
}
attr_type = GNUNET_RECLAIM_attribute_number_to_typename (attr->type);
id = GNUNET_STRINGS_data_to_string_alloc (&attr->id, sizeof(attr->id));
value_str = NULL;
if (NULL == presentation)
{
value_str = GNUNET_RECLAIM_attribute_value_to_string (attr->type,
attr->data,
attr->data_size);
}
else
{
struct GNUNET_RECLAIM_AttributeListEntry *ale;
struct GNUNET_RECLAIM_AttributeList *al
= GNUNET_RECLAIM_presentation_get_attributes (presentation);
for (ale = al->list_head; NULL != ale; ale = ale->next)
{
if (0 != strncmp (attr->data, ale->attribute->name, attr->data_size))
continue;
value_str
= GNUNET_RECLAIM_attribute_value_to_string (ale->attribute->type,
ale->attribute->data,
ale->attribute->data_size);
break;
}
}
fprintf (stdout,
"Name: %s; Value: %s (%s); Flag %u; ID: %s %s\n",
attr->name,
(NULL != value_str) ? value_str : "???",
attr_type,
attr->flag,
id,
(NULL == presentation) ? "" : "(ATTESTED)");
GNUNET_free (value_str);
GNUNET_free (id);
}
static void
ticket_iter_err (void *cls)
{
ticket_iterator = NULL;
fprintf (stderr, "Failed to iterate over tickets\n");
cleanup_task = GNUNET_SCHEDULER_add_now (&do_cleanup, NULL);
}
static void
ticket_iter_fin (void *cls)
{
ticket_iterator = NULL;
cleanup_task = GNUNET_SCHEDULER_add_now (&do_cleanup, NULL);
}
static void
ticket_iter (void *cls, const struct GNUNET_RECLAIM_Ticket *ticket)
{
char *aud;
char *ref;
char *tkt;
aud =
GNUNET_STRINGS_data_to_string_alloc (&ticket->audience,
sizeof(struct
GNUNET_IDENTITY_PublicKey));
ref = GNUNET_STRINGS_data_to_string_alloc (&ticket->rnd, sizeof(ticket->rnd));
tkt =
GNUNET_STRINGS_data_to_string_alloc (ticket,
sizeof(struct GNUNET_RECLAIM_Ticket));
fprintf (stdout, "Ticket: %s | ID: %s | Audience: %s\n", tkt, ref, aud);
GNUNET_free (aud);
GNUNET_free (ref);
GNUNET_free (tkt);
GNUNET_RECLAIM_ticket_iteration_next (ticket_iterator);
}
static void
iter_error (void *cls)
{
attr_iterator = NULL;
cred_iterator = NULL;
fprintf (stderr, "Failed\n");
cleanup_task = GNUNET_SCHEDULER_add_now (&do_cleanup, NULL);
}
static void
timeout_task (void *cls)
{
timeout = NULL;
ret = 1;
fprintf (stderr, "Timeout\n");
if (NULL == cleanup_task)
cleanup_task = GNUNET_SCHEDULER_add_now (&do_cleanup, NULL);
}
static void
process_rvk (void *cls, int success, const char *msg)
{
reclaim_op = NULL;
if (GNUNET_OK != success)
{
fprintf (stderr, "Revocation failed.\n");
ret = 1;
}
cleanup_task = GNUNET_SCHEDULER_add_now (&do_cleanup, NULL);
}
static void
process_delete (void *cls, int success, const char *msg)
{
reclaim_op = NULL;
if (GNUNET_OK != success)
{
fprintf (stderr, "Deletion failed.\n");
ret = 1;
}
cleanup_task = GNUNET_SCHEDULER_add_now (&do_cleanup, NULL);
}
static void
iter_finished (void *cls)
{
char *data;
size_t data_size;
int type;
attr_iterator = NULL;
if (list)
{
cleanup_task = GNUNET_SCHEDULER_add_now (&do_cleanup, NULL);
return;
}
if (issue_attrs)
{
reclaim_op = GNUNET_RECLAIM_ticket_issue (reclaim_handle,
pkey,
&rp_key,
attr_list,
&ticket_issue_cb,
NULL);
return;
}
if (consume_ticket)
{
reclaim_op = GNUNET_RECLAIM_ticket_consume (reclaim_handle,
pkey,
&ticket,
&process_attrs,
NULL);
timeout = GNUNET_SCHEDULER_add_delayed (
GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 10),
&timeout_task,
NULL);
return;
}
if (revoke_ticket)
{
reclaim_op = GNUNET_RECLAIM_ticket_revoke (reclaim_handle,
pkey,
&ticket,
&process_rvk,
NULL);
return;
}
if (attr_delete)
{
if (NULL == attr_to_delete)
{
fprintf (stdout, "No such attribute ``%s''\n", attr_delete);
GNUNET_SCHEDULER_add_now (&do_cleanup, NULL);
return;
}
reclaim_op = GNUNET_RECLAIM_attribute_delete (reclaim_handle,
pkey,
attr_to_delete,
&process_delete,
NULL);
return;
}
if (attr_name)
{
if (NULL == type_str)
type = GNUNET_RECLAIM_ATTRIBUTE_TYPE_STRING;
else
type = GNUNET_RECLAIM_attribute_typename_to_number (type_str);
GNUNET_assert (GNUNET_SYSERR !=
GNUNET_RECLAIM_attribute_string_to_value (type,
attr_value,
(void **) &data,
&data_size));
if (NULL != claim)
{
claim->type = type;
claim->data = data;
claim->data_size = data_size;
}
else
{
claim =
GNUNET_RECLAIM_attribute_new (attr_name, NULL, type, data, data_size);
}
if (NULL != credential_id)
{
claim->credential = credential;
}
reclaim_op = GNUNET_RECLAIM_attribute_store (reclaim_handle,
pkey,
claim,
&exp_interval,
&store_cont,
NULL);
GNUNET_free (data);
GNUNET_free (claim);
return;
}
cleanup_task = GNUNET_SCHEDULER_add_now (&do_cleanup, NULL);
}
static void
iter_cb (void *cls,
const struct GNUNET_IDENTITY_PublicKey *identity,
const struct GNUNET_RECLAIM_Attribute *attr)
{
struct GNUNET_RECLAIM_AttributeListEntry *le;
char *attrs_tmp;
char *attr_str;
char *label;
char *id;
const char *attr_type;
if ((NULL != attr_name) && (NULL == claim))
{
if (0 == strcasecmp (attr_name, attr->name))
{
claim = GNUNET_RECLAIM_attribute_new (attr->name,
&attr->credential,
attr->type,
attr->data,
attr->data_size);
claim->id = attr->id;
}
}
else if (issue_attrs)
{
attrs_tmp = GNUNET_strdup (issue_attrs);
attr_str = strtok (attrs_tmp, ",");
while (NULL != attr_str)
{
if (0 != strcasecmp (attr_str, attr->name))
{
attr_str = strtok (NULL, ",");
continue;
}
le = GNUNET_new (struct GNUNET_RECLAIM_AttributeListEntry);
le->attribute = GNUNET_RECLAIM_attribute_new (attr->name,
&attr->credential,
attr->type,
attr->data,
attr->data_size);
le->attribute->flag = attr->flag;
le->attribute->id = attr->id;
GNUNET_CONTAINER_DLL_insert (attr_list->list_head,
attr_list->list_tail,
le);
break;
}
GNUNET_free (attrs_tmp);
}
else if (attr_delete && (NULL == attr_to_delete))
{
label = GNUNET_STRINGS_data_to_string_alloc (&attr->id, sizeof(attr->id));
if (0 == strcasecmp (attr_delete, label))
{
attr_to_delete = GNUNET_RECLAIM_attribute_new (attr->name,
&attr->credential,
attr->type,
attr->data,
attr->data_size);
attr_to_delete->id = attr->id;
}
GNUNET_free (label);
}
else if (list)
{
attr_str = GNUNET_RECLAIM_attribute_value_to_string (attr->type,
attr->data,
attr->data_size);
attr_type = GNUNET_RECLAIM_attribute_number_to_typename (attr->type);
id = GNUNET_STRINGS_data_to_string_alloc (&attr->id, sizeof(attr->id));
if (GNUNET_YES == GNUNET_RECLAIM_id_is_zero (&attr->credential))
{
fprintf (stdout,
"%s: ``%s'' (%s); ID: %s\n",
attr->name,
attr_str,
attr_type,
id);
}
else
{
char *cred_id =
GNUNET_STRINGS_data_to_string_alloc (&attr->credential,
sizeof(attr->credential));
fprintf (stdout,
"%s: ``%s'' in credential presentation `%s' (%s); ID: %s\n",
attr->name,
attr_str,
cred_id,
attr_type,
id);
GNUNET_free (cred_id);
}
GNUNET_free (id);
}
GNUNET_RECLAIM_get_attributes_next (attr_iterator);
}
static void
cred_iter_finished (void *cls)
{
cred_iterator = NULL;
// Add new credential
if ((NULL != credential_name) &&
(NULL != attr_value))
{
enum GNUNET_RECLAIM_CredentialType ctype =
GNUNET_RECLAIM_credential_typename_to_number (credential_type);
struct GNUNET_RECLAIM_Credential *credential =
GNUNET_RECLAIM_credential_new (credential_name,
ctype,
attr_value,
strlen (attr_value));
reclaim_op = GNUNET_RECLAIM_credential_store (reclaim_handle,
pkey,
credential,
&exp_interval,
store_cont,
NULL);
return;
}
if (list_credentials)
{
cleanup_task = GNUNET_SCHEDULER_add_now (&do_cleanup, NULL);
return;
}
attr_iterator = GNUNET_RECLAIM_get_attributes_start (reclaim_handle,
pkey,
&iter_error,
NULL,
&iter_cb,
NULL,
&iter_finished,
NULL);
}
static void
cred_iter_cb (void *cls,
const struct GNUNET_IDENTITY_PublicKey *identity,
const struct GNUNET_RECLAIM_Credential *cred)
{
char *cred_str;
char *attr_str;
char *id;
const char *cred_type;
struct GNUNET_RECLAIM_AttributeListEntry *ale;
if (GNUNET_YES == GNUNET_RECLAIM_id_is_equal (&credential,
&cred->id))
credential_exists = GNUNET_YES;
if (list_credentials)
{
cred_str = GNUNET_RECLAIM_credential_value_to_string (cred->type,
cred->data,
cred->data_size);
cred_type = GNUNET_RECLAIM_credential_number_to_typename (cred->type);
id = GNUNET_STRINGS_data_to_string_alloc (&cred->id, sizeof(cred->id));
fprintf (stdout,
"%s: ``%s'' (%s); ID: %s\n",
cred->name,
cred_str,
cred_type,
id);
struct GNUNET_RECLAIM_AttributeList *attrs =
GNUNET_RECLAIM_credential_get_attributes (cred);
if (NULL != attrs)
{
fprintf (stdout,
"\t Attributes:\n");
for (ale = attrs->list_head; NULL != ale; ale = ale->next)
{
attr_str = GNUNET_RECLAIM_attribute_value_to_string (
ale->attribute->type,
ale->attribute->data,
ale->attribute->data_size);
fprintf (stdout,
"\t %s: %s\n", ale->attribute->name, attr_str);
GNUNET_free (attr_str);
}
GNUNET_RECLAIM_attribute_list_destroy (attrs);
}
GNUNET_free (id);
}
GNUNET_RECLAIM_get_credentials_next (cred_iterator);
}
static void
start_process ()
{
if (NULL == pkey)
{
fprintf (stderr, "Ego %s not found\n", ego_name);
cleanup_task = GNUNET_SCHEDULER_add_now (&do_cleanup, NULL);
return;
}
if (NULL == credential_type)
credential_type = GNUNET_strdup ("JWT");
credential = GNUNET_RECLAIM_ID_ZERO;
if (NULL != credential_id)
GNUNET_STRINGS_string_to_data (credential_id,
strlen (credential_id),
&credential, sizeof(credential));
credential_exists = GNUNET_NO;
if (list_tickets)
{
ticket_iterator = GNUNET_RECLAIM_ticket_iteration_start (reclaim_handle,
pkey,
&ticket_iter_err,
NULL,
&ticket_iter,
NULL,
&ticket_iter_fin,
NULL);
return;
}
if ((NULL != rp) &&
(GNUNET_OK !=
GNUNET_IDENTITY_public_key_from_string (rp, &rp_key)) )
{
fprintf (stderr, "%s is not a public key!\n", rp);
cleanup_task = GNUNET_SCHEDULER_add_now (&do_cleanup, NULL);
return;
}
if (NULL != consume_ticket)
GNUNET_STRINGS_string_to_data (consume_ticket,
strlen (consume_ticket),
&ticket,
sizeof(struct GNUNET_RECLAIM_Ticket));
if (NULL != revoke_ticket)
GNUNET_STRINGS_string_to_data (revoke_ticket,
strlen (revoke_ticket),
&ticket,
sizeof(struct GNUNET_RECLAIM_Ticket));
attr_list = GNUNET_new (struct GNUNET_RECLAIM_AttributeList);
claim = NULL;
cred_iterator = GNUNET_RECLAIM_get_credentials_start (reclaim_handle,
pkey,
&iter_error,
NULL,
&cred_iter_cb,
NULL,
&cred_iter_finished,
NULL);
}
static int init = GNUNET_YES;
static void
ego_cb (void *cls,
struct GNUNET_IDENTITY_Ego *ego,
void **ctx,
const char *name)
{
if (NULL == name)
{
if (GNUNET_YES == init)
{
init = GNUNET_NO;
start_process ();
}
return;
}
if (0 != strcmp (name, ego_name))
return;
pkey = GNUNET_IDENTITY_ego_get_private_key (ego);
}
static void
run (void *cls,
char *const *args,
const char *cfgfile,
const struct GNUNET_CONFIGURATION_Handle *c)
{
ret = 0;
if (NULL == ego_name)
{
ret = 1;
fprintf (stderr, _ ("Ego is required\n"));
return;
}
if ((NULL == attr_value) && (NULL != attr_name))
{
ret = 1;
fprintf (stderr, _ ("Attribute value missing!\n"));
return;
}
if ((NULL == rp) && (NULL != issue_attrs))
{
ret = 1;
fprintf (stderr, _ ("Requesting party key is required!\n"));
return;
}
reclaim_handle = GNUNET_RECLAIM_connect (c);
// Get Ego
identity_handle = GNUNET_IDENTITY_connect (c, &ego_cb, NULL);
}
int
main (int argc, char *const argv[])
{
exp_interval = GNUNET_TIME_UNIT_HOURS;
struct GNUNET_GETOPT_CommandLineOption options[] = {
GNUNET_GETOPT_option_string ('a',
"add",
"NAME",
gettext_noop ("Add or update an attribute NAME"),
&attr_name),
GNUNET_GETOPT_option_string ('d',
"delete",
"ID",
gettext_noop ("Delete the attribute with ID"),
&attr_delete),
GNUNET_GETOPT_option_string ('V',
"value",
"VALUE",
gettext_noop ("The attribute VALUE"),
&attr_value),
GNUNET_GETOPT_option_string ('e',
"ego",
"EGO",
gettext_noop ("The EGO to use"),
&ego_name),
GNUNET_GETOPT_option_string ('r',
"rp",
"RP",
gettext_noop (
"Specify the relying party for issue"),
&rp),
GNUNET_GETOPT_option_flag ('D',
"dump",
gettext_noop ("List attributes for EGO"),
&list),
GNUNET_GETOPT_option_flag ('A',
"credentials",
gettext_noop ("List credentials for EGO"),
&list_credentials),
GNUNET_GETOPT_option_string ('I',
"credential-id",
"CREDENTIAL_ID",
gettext_noop (
"Credential to use for attribute"),
&credential_id),
GNUNET_GETOPT_option_string ('N',
"credential-name",
"NAME",
gettext_noop ("Credential name"),
&credential_name),
GNUNET_GETOPT_option_string ('i',
"issue",
"A1,A2,...",
gettext_noop (
"Issue a ticket for a set of attributes separated by comma"),
&issue_attrs),
GNUNET_GETOPT_option_string ('C',
"consume",
"TICKET",
gettext_noop ("Consume a ticket"),
&consume_ticket),
GNUNET_GETOPT_option_string ('R',
"revoke",
"TICKET",
gettext_noop ("Revoke a ticket"),
&revoke_ticket),
GNUNET_GETOPT_option_string ('t',
"type",
"TYPE",
gettext_noop ("Type of attribute"),
&type_str),
GNUNET_GETOPT_option_string ('u',
"credential-type",
"TYPE",
gettext_noop ("Type of credential"),
&credential_type),
GNUNET_GETOPT_option_flag ('T',
"tickets",
gettext_noop ("List tickets of ego"),
&list_tickets),
GNUNET_GETOPT_option_relative_time ('E',
"expiration",
"INTERVAL",
gettext_noop (
"Expiration interval of the attribute"),
&exp_interval),
GNUNET_GETOPT_OPTION_END
};
if (GNUNET_OK != GNUNET_PROGRAM_run (argc,
argv,
"gnunet-reclaim",
_ ("re:claimID command line tool"),
options,
&run,
NULL))
return 1;
else
return ret;
}