/* 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-service-reclaim.c * @brief reclaim Service * */ #include "platform.h" #include "gnunet_util_lib.h" #include "gnunet_constants.h" #include "gnunet_protocols.h" #include "gnunet_identity_service.h" #include "gnunet_gnsrecord_lib.h" #include "gnunet_namestore_service.h" #include "gnunet_abe_lib.h" #include "gnunet_credential_service.h" #include "gnunet_statistics_service.h" #include "gnunet_gns_service.h" #include "gnunet_reclaim_plugin.h" #include "gnunet_reclaim_attribute_lib.h" #include "gnunet_signatures.h" #include "reclaim.h" /** * First pass state */ #define STATE_INIT 0 /** * Normal operation state */ #define STATE_POST_INIT 1 /** * Minimum interval between updates */ #define MIN_WAIT_TIME GNUNET_TIME_UNIT_MINUTES /** * Standard token expiration time */ #define DEFAULT_TOKEN_EXPIRATION_INTERVAL GNUNET_TIME_UNIT_HOURS /** * Identity handle */ static struct GNUNET_IDENTITY_Handle *identity_handle; /** * Database handle */ static struct GNUNET_RECLAIM_PluginFunctions *TKT_database; /** * Name of DB plugin */ static char *db_lib_name; /** * Token expiration interval */ static struct GNUNET_TIME_Relative token_expiration_interval; /** * Namestore handle */ static struct GNUNET_NAMESTORE_Handle *ns_handle; /** * GNS handle */ static struct GNUNET_GNS_Handle *gns_handle; /** * Credential handle */ static struct GNUNET_CREDENTIAL_Handle *credential_handle; /** * Namestore qe */ static struct GNUNET_NAMESTORE_QueueEntry *ns_qe; /** * Namestore iterator */ static struct GNUNET_NAMESTORE_ZoneIterator *ns_it; /** * Timeout task */ static struct GNUNET_SCHEDULER_Task *timeout_task; /** * Update task */ static struct GNUNET_SCHEDULER_Task *update_task; /** * Currently processed token */ static struct IdentityToken *token; /** * Label for currently processed token */ static char* label; /** * Scopes for processed token */ static char* scopes; /** * Handle to the statistics service. */ static struct GNUNET_STATISTICS_Handle *stats; /** * Our configuration. */ static const struct GNUNET_CONFIGURATION_Handle *cfg; /** * An idp client */ struct IdpClient; /** * A ticket iteration operation. */ struct TicketIteration { /** * DLL */ struct TicketIteration *next; /** * DLL */ struct TicketIteration *prev; /** * Client which intiated this zone iteration */ struct IdpClient *client; /** * Key of the identity we are iterating over. */ struct GNUNET_CRYPTO_EcdsaPublicKey identity; /** * Identity is audience */ uint32_t is_audience; /** * The operation id fot the iteration in the response for the client */ uint32_t r_id; /** * Offset of the iteration used to address next result of the * iteration in the store * * Initialy set to 0 in handle_iteration_start * Incremented with by every call to handle_iteration_next */ uint32_t offset; }; /** * Callback after an ABE bootstrap * * @param cls closure * @param abe_key the ABE key that exists or was created */ typedef void (*AbeBootstrapResult) (void *cls, struct GNUNET_ABE_AbeMasterKey *abe_key); struct AbeBootstrapHandle { /** * Function to call when finished */ AbeBootstrapResult proc; /** * Callback closure */ char *proc_cls; /** * Key of the zone we are iterating over. */ struct GNUNET_CRYPTO_EcdsaPrivateKey identity; /** * Namestore Queue Entry */ struct GNUNET_NAMESTORE_QueueEntry *ns_qe; /** * The issuer egos ABE master key */ struct GNUNET_ABE_AbeMasterKey *abe_key; /** * Recreate master keys */ int recreate; }; /** * An attribute iteration operation. */ struct AttributeIterator { /** * Next element in the DLL */ struct AttributeIterator *next; /** * Previous element in the DLL */ struct AttributeIterator *prev; /** * IDP client which intiated this zone iteration */ struct IdpClient *client; /** * Key of the zone we are iterating over. */ struct GNUNET_CRYPTO_EcdsaPrivateKey identity; /** * The issuer egos ABE master key */ struct GNUNET_ABE_AbeMasterKey *abe_key; /** * Namestore iterator */ struct GNUNET_NAMESTORE_ZoneIterator *ns_it; /** * The operation id fot the zone iteration in the response for the client */ uint32_t request_id; }; /** * An idp client */ struct IdpClient { /** * The client */ struct GNUNET_SERVICE_Client *client; /** * Message queue for transmission to @e client */ struct GNUNET_MQ_Handle *mq; /** * Head of the DLL of * Attribute iteration operations in * progress initiated by this client */ struct AttributeIterator *attr_iter_head; /** * Tail of the DLL of * Attribute iteration operations * in progress initiated by this client */ struct AttributeIterator *attr_iter_tail; /** * Head of DLL of ticket iteration ops */ struct TicketIteration *ticket_iter_head; /** * Tail of DLL of ticket iteration ops */ struct TicketIteration *ticket_iter_tail; /** * Head of DLL of ticket revocation ops */ struct TicketRevocationHandle *revoke_op_head; /** * Tail of DLL of ticket revocation ops */ struct TicketRevocationHandle *revoke_op_tail; /** * Head of DLL of ticket issue ops */ struct TicketIssueHandle *issue_op_head; /** * Tail of DLL of ticket issue ops */ struct TicketIssueHandle *issue_op_tail; /** * Head of DLL of ticket consume ops */ struct ConsumeTicketHandle *consume_op_head; /** * Tail of DLL of ticket consume ops */ struct ConsumeTicketHandle *consume_op_tail; /** * Head of DLL of attribute store ops */ struct AttributeStoreHandle *store_op_head; /** * Tail of DLL of attribute store ops */ struct AttributeStoreHandle *store_op_tail; }; struct AttributeStoreHandle { /** * DLL */ struct AttributeStoreHandle *next; /** * DLL */ struct AttributeStoreHandle *prev; /** * Client connection */ struct IdpClient *client; /** * Identity */ struct GNUNET_CRYPTO_EcdsaPrivateKey identity; /** * Identity pubkey */ struct GNUNET_CRYPTO_EcdsaPublicKey identity_pkey; /** * The issuer egos ABE master key */ struct GNUNET_ABE_AbeMasterKey *abe_key; /** * QueueEntry */ struct GNUNET_NAMESTORE_QueueEntry *ns_qe; /** * The attribute to store */ struct GNUNET_RECLAIM_ATTRIBUTE_Claim *claim; /** * The attribute expiration interval */ struct GNUNET_TIME_Relative exp; /** * request id */ uint32_t r_id; }; /* Prototype */ struct ParallelLookup; struct ConsumeTicketHandle { /** * DLL */ struct ConsumeTicketHandle *next; /** * DLL */ struct ConsumeTicketHandle *prev; /** * Client connection */ struct IdpClient *client; /** * Ticket */ struct GNUNET_RECLAIM_Ticket ticket; /** * LookupRequest */ struct GNUNET_GNS_LookupRequest *lookup_request; /** * Audience Key */ struct GNUNET_CRYPTO_EcdsaPrivateKey identity; /** * Audience Key */ struct GNUNET_CRYPTO_EcdsaPublicKey identity_pub; /** * Lookup DLL */ struct ParallelLookup *parallel_lookups_head; /** * Lookup DLL */ struct ParallelLookup *parallel_lookups_tail; /** * Kill task */ struct GNUNET_SCHEDULER_Task *kill_task; /** * The ABE key */ struct GNUNET_ABE_AbeKey *key; /** * Attributes */ struct GNUNET_RECLAIM_ATTRIBUTE_ClaimList *attrs; /** * Lookup time */ struct GNUNET_TIME_Absolute lookup_start_time; /** * request id */ uint32_t r_id; }; /** * Handle for a parallel GNS lookup job */ struct ParallelLookup { /* DLL */ struct ParallelLookup *next; /* DLL */ struct ParallelLookup *prev; /* The GNS request */ struct GNUNET_GNS_LookupRequest *lookup_request; /* The handle the return to */ struct ConsumeTicketHandle *handle; /** * Lookup time */ struct GNUNET_TIME_Absolute lookup_start_time; /* The label to look up */ char *label; }; /** * Ticket revocation request handle */ struct TicketRevocationHandle { /** * DLL */ struct TicketRevocationHandle *prev; /** * DLL */ struct TicketRevocationHandle *next; /** * Client connection */ struct IdpClient *client; /** * Attributes to reissue */ struct GNUNET_RECLAIM_ATTRIBUTE_ClaimList *attrs; /** * Attributes to revoke */ struct GNUNET_RECLAIM_ATTRIBUTE_ClaimList *rvk_attrs; /** * Issuer Key */ struct GNUNET_CRYPTO_EcdsaPrivateKey identity; /** * Ticket to issue */ struct GNUNET_RECLAIM_Ticket ticket; /** * QueueEntry */ struct GNUNET_NAMESTORE_QueueEntry *ns_qe; /** * Namestore iterator */ struct GNUNET_NAMESTORE_ZoneIterator *ns_it; /** * The ABE master key */ struct GNUNET_ABE_AbeMasterKey *abe_key; /** * Offset */ uint32_t offset; /** * request id */ uint32_t r_id; }; /** * Ticket issue request handle */ struct TicketIssueHandle { /** * DLL */ struct TicketIssueHandle *prev; /** * DLL */ struct TicketIssueHandle *next; /** * Client connection */ struct IdpClient *client; /** * Attributes to issue */ struct GNUNET_RECLAIM_ATTRIBUTE_ClaimList *attrs; /** * Issuer Key */ struct GNUNET_CRYPTO_EcdsaPrivateKey identity; /** * Ticket to issue */ struct GNUNET_RECLAIM_Ticket ticket; /** * QueueEntry */ struct GNUNET_NAMESTORE_QueueEntry *ns_qe; /** * request id */ uint32_t r_id; }; /** * DLL for ego handles to egos containing the ID_ATTRS in a map in json_t format * */ struct EgoEntry { /** * DLL */ struct EgoEntry *next; /** * DLL */ struct EgoEntry *prev; /** * Ego handle */ struct GNUNET_IDENTITY_Ego *ego; /** * Attribute map. Contains the attributes as json_t */ struct GNUNET_CONTAINER_MultiHashMap *attr_map; }; /** * Cleanup task */ static void cleanup() { GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Cleaning up\n"); if (NULL != stats) { GNUNET_STATISTICS_destroy (stats, GNUNET_NO); stats = NULL; } GNUNET_break (NULL == GNUNET_PLUGIN_unload (db_lib_name, TKT_database)); GNUNET_free (db_lib_name); db_lib_name = NULL; if (NULL != timeout_task) GNUNET_SCHEDULER_cancel (timeout_task); if (NULL != update_task) GNUNET_SCHEDULER_cancel (update_task); if (NULL != identity_handle) GNUNET_IDENTITY_disconnect (identity_handle); if (NULL != gns_handle) GNUNET_GNS_disconnect (gns_handle); if (NULL != credential_handle) GNUNET_CREDENTIAL_disconnect (credential_handle); if (NULL != ns_it) GNUNET_NAMESTORE_zone_iteration_stop (ns_it); if (NULL != ns_qe) GNUNET_NAMESTORE_cancel (ns_qe); if (NULL != ns_handle) GNUNET_NAMESTORE_disconnect (ns_handle); GNUNET_free_non_null (token); GNUNET_free_non_null (label); } /** * Shutdown task * * @param cls NULL */ static void do_shutdown (void *cls) { GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Shutting down...\n"); cleanup(); } /** * Finished storing newly bootstrapped ABE key */ static void bootstrap_store_cont (void *cls, int32_t success, const char *emsg) { struct AbeBootstrapHandle *abh = cls; if (GNUNET_SYSERR == success) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to bootstrap ABE master %s\n", emsg); abh->proc (abh->proc_cls, NULL); GNUNET_free (abh->abe_key); GNUNET_free (abh); return; } abh->proc (abh->proc_cls, abh->abe_key); GNUNET_free (abh); } /** * Error checking for ABE master */ static void bootstrap_abe_error (void *cls) { struct AbeBootstrapHandle *abh = cls; abh->proc (abh->proc_cls, NULL); GNUNET_free (abh); } /** * Handle ABE lookup in namestore */ static void bootstrap_abe_result (void *cls, const struct GNUNET_CRYPTO_EcdsaPrivateKey *zone, const char *label, unsigned int rd_count, const struct GNUNET_GNSRECORD_Data *rd) { struct AbeBootstrapHandle *abh = cls; struct GNUNET_ABE_AbeMasterKey *abe_key; for (uint32_t i=0;irecreate) continue; abe_key = GNUNET_ABE_cpabe_deserialize_master_key (rd[i].data, rd[i].data_size); abh->proc (abh->proc_cls, abe_key); GNUNET_free (abh); return; } //No ABE master found, bootstrapping... abh->abe_key = GNUNET_ABE_cpabe_create_master_key (); { struct GNUNET_GNSRECORD_Data rdn[rd_count+1]; char *key; unsigned int rd_count_new = rd_count + 1; for (uint32_t i=0;irecreate) && (GNUNET_GNSRECORD_TYPE_ABE_MASTER == rd[i].record_type)) { rdn[i].data_size = GNUNET_ABE_cpabe_serialize_master_key (abh->abe_key, (void**)&key); rdn[i].data = key; rdn[i].record_type = GNUNET_GNSRECORD_TYPE_ABE_MASTER; rdn[i].flags = GNUNET_GNSRECORD_RF_RELATIVE_EXPIRATION | GNUNET_GNSRECORD_RF_PRIVATE; rdn[i].expiration_time = GNUNET_TIME_UNIT_HOURS.rel_value_us; //TODO sane? rd_count_new = rd_count; } else { GNUNET_memcpy (&rdn[i], &rd[i], sizeof (struct GNUNET_GNSRECORD_Data)); } } if (rd_count < rd_count_new) { rdn[rd_count].data_size = GNUNET_ABE_cpabe_serialize_master_key (abh->abe_key, (void**)&key); rdn[rd_count].data = key; rdn[rd_count].record_type = GNUNET_GNSRECORD_TYPE_ABE_MASTER; rdn[rd_count].flags = GNUNET_GNSRECORD_RF_RELATIVE_EXPIRATION | GNUNET_GNSRECORD_RF_PRIVATE; rdn[rd_count].expiration_time = GNUNET_TIME_UNIT_HOURS.rel_value_us; //TODO sane? } abh->ns_qe = GNUNET_NAMESTORE_records_store (ns_handle, &abh->identity, GNUNET_GNS_EMPTY_LABEL_AT, rd_count_new, rdn, &bootstrap_store_cont, abh); GNUNET_free (key); } } /** * Bootstrap ABE master if it does not yet exists. * Will call the AbeBootstrapResult processor when done. * will always recreate the ABE key of GNUNET_YES == recreate */ static void bootstrap_abe (const struct GNUNET_CRYPTO_EcdsaPrivateKey *identity, AbeBootstrapResult proc, void* cls, int recreate) { struct AbeBootstrapHandle *abh; abh = GNUNET_new (struct AbeBootstrapHandle); abh->proc = proc; abh->proc_cls = cls; abh->identity = *identity; if (GNUNET_YES == recreate) { abh->abe_key = GNUNET_ABE_cpabe_create_master_key (); abh->recreate = GNUNET_YES; } else { abh->recreate = GNUNET_NO; } abh->ns_qe = GNUNET_NAMESTORE_records_lookup (ns_handle, identity, GNUNET_GNS_EMPTY_LABEL_AT, &bootstrap_abe_error, abh, &bootstrap_abe_result, abh); } static int create_sym_key_from_ecdh(const struct GNUNET_HashCode *new_key_hash, struct GNUNET_CRYPTO_SymmetricSessionKey *skey, struct GNUNET_CRYPTO_SymmetricInitializationVector *iv) { struct GNUNET_CRYPTO_HashAsciiEncoded new_key_hash_str; GNUNET_CRYPTO_hash_to_enc (new_key_hash, &new_key_hash_str); GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Creating symmetric rsa key from %s\n", (char*)&new_key_hash_str); static const char ctx_key[] = "gnuid-aes-ctx-key"; GNUNET_CRYPTO_kdf (skey, sizeof (struct GNUNET_CRYPTO_SymmetricSessionKey), new_key_hash, sizeof (struct GNUNET_HashCode), ctx_key, strlen (ctx_key), NULL, 0); static const char ctx_iv[] = "gnuid-aes-ctx-iv"; GNUNET_CRYPTO_kdf (iv, sizeof (struct GNUNET_CRYPTO_SymmetricInitializationVector), new_key_hash, sizeof (struct GNUNET_HashCode), ctx_iv, strlen (ctx_iv), NULL, 0); return GNUNET_OK; } /** * Cleanup ticket consume handle * @param handle the handle to clean up */ static void cleanup_ticket_issue_handle (struct TicketIssueHandle *handle) { if (NULL != handle->attrs) GNUNET_RECLAIM_ATTRIBUTE_list_destroy (handle->attrs); if (NULL != handle->ns_qe) GNUNET_NAMESTORE_cancel (handle->ns_qe); GNUNET_free (handle); } static void send_ticket_result (struct IdpClient *client, uint32_t r_id, const struct GNUNET_RECLAIM_Ticket *ticket, const struct GNUNET_RECLAIM_ATTRIBUTE_ClaimList *attrs) { struct TicketResultMessage *irm; struct GNUNET_MQ_Envelope *env; struct GNUNET_RECLAIM_Ticket *ticket_buf; /* store ticket in DB */ if (GNUNET_OK != TKT_database->store_ticket (TKT_database->cls, ticket, attrs)) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Unable to store ticket after issue\n"); GNUNET_break (0); } env = GNUNET_MQ_msg_extra (irm, sizeof (struct GNUNET_RECLAIM_Ticket), GNUNET_MESSAGE_TYPE_RECLAIM_TICKET_RESULT); ticket_buf = (struct GNUNET_RECLAIM_Ticket *)&irm[1]; *ticket_buf = *ticket; irm->id = htonl (r_id); GNUNET_MQ_send (client->mq, env); } static void store_ticket_issue_cont (void *cls, int32_t success, const char *emsg) { struct TicketIssueHandle *handle = cls; handle->ns_qe = NULL; GNUNET_CONTAINER_DLL_remove (handle->client->issue_op_head, handle->client->issue_op_tail, handle); if (GNUNET_SYSERR == success) { cleanup_ticket_issue_handle (handle); GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "%s\n", "Unknown Error\n"); GNUNET_SCHEDULER_add_now (&do_shutdown, NULL); return; } send_ticket_result (handle->client, handle->r_id, &handle->ticket, handle->attrs); cleanup_ticket_issue_handle (handle); } int serialize_abe_keyinfo2 (const struct GNUNET_RECLAIM_Ticket *ticket, const struct GNUNET_RECLAIM_ATTRIBUTE_ClaimList *attrs, const struct GNUNET_ABE_AbeKey *rp_key, struct GNUNET_CRYPTO_EcdhePrivateKey **ecdh_privkey, char **result) { struct GNUNET_CRYPTO_EcdhePublicKey ecdh_pubkey; struct GNUNET_RECLAIM_ATTRIBUTE_ClaimListEntry *le; char *enc_keyinfo; char *serialized_key; char *buf; char *write_ptr; char attrs_str_len; ssize_t size; struct GNUNET_CRYPTO_SymmetricSessionKey skey; struct GNUNET_CRYPTO_SymmetricInitializationVector iv; struct GNUNET_HashCode new_key_hash; ssize_t enc_size; size = GNUNET_ABE_cpabe_serialize_key (rp_key, (void**)&serialized_key); attrs_str_len = 0; for (le = attrs->list_head; NULL != le; le = le->next) { attrs_str_len += strlen (le->claim->name) + 1; } buf = GNUNET_malloc (attrs_str_len + size); write_ptr = buf; GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Writing attributes\n"); for (le = attrs->list_head; NULL != le; le = le->next) { GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "%s\n", le->claim->name); GNUNET_memcpy (write_ptr, le->claim->name, strlen (le->claim->name)); write_ptr[strlen (le->claim->name)] = ','; write_ptr += strlen (le->claim->name) + 1; } write_ptr--; write_ptr[0] = '\0'; //replace last , with a 0-terminator write_ptr++; GNUNET_memcpy (write_ptr, serialized_key, size); GNUNET_free (serialized_key); // ECDH keypair E = eG *ecdh_privkey = GNUNET_CRYPTO_ecdhe_key_create(); GNUNET_CRYPTO_ecdhe_key_get_public (*ecdh_privkey, &ecdh_pubkey); enc_keyinfo = GNUNET_malloc (size + attrs_str_len); // Derived key K = H(eB) GNUNET_assert (GNUNET_OK == GNUNET_CRYPTO_ecdh_ecdsa (*ecdh_privkey, &ticket->audience, &new_key_hash)); create_sym_key_from_ecdh(&new_key_hash, &skey, &iv); enc_size = GNUNET_CRYPTO_symmetric_encrypt (buf, size + attrs_str_len, &skey, &iv, enc_keyinfo); *result = GNUNET_malloc (sizeof (struct GNUNET_CRYPTO_EcdhePublicKey)+ enc_size); GNUNET_memcpy (*result, &ecdh_pubkey, sizeof (struct GNUNET_CRYPTO_EcdhePublicKey)); GNUNET_memcpy (*result + sizeof (struct GNUNET_CRYPTO_EcdhePublicKey), enc_keyinfo, enc_size); GNUNET_free (enc_keyinfo); GNUNET_free (buf); return sizeof (struct GNUNET_CRYPTO_EcdhePublicKey)+enc_size; } static void issue_ticket_after_abe_bootstrap (void *cls, struct GNUNET_ABE_AbeMasterKey *abe_key) { struct TicketIssueHandle *ih = cls; struct GNUNET_RECLAIM_ATTRIBUTE_ClaimListEntry *le; struct GNUNET_CRYPTO_EcdhePrivateKey *ecdhe_privkey; struct GNUNET_GNSRECORD_Data code_record[1]; struct GNUNET_ABE_AbeKey *rp_key; char *code_record_data; char **attrs; char *label; char *policy; int attrs_len; uint32_t i; size_t code_record_len; //Create new ABE key for RP attrs_len = 0; for (le = ih->attrs->list_head; NULL != le; le = le->next) attrs_len++; attrs = GNUNET_malloc ((attrs_len + 1)*sizeof (char*)); i = 0; for (le = ih->attrs->list_head; NULL != le; le = le->next) { GNUNET_asprintf (&policy, "%s_%lu", le->claim->name, le->claim->version); GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Adding attribute to key: %s\n", policy); attrs[i] = policy; i++; } attrs[i] = NULL; rp_key = GNUNET_ABE_cpabe_create_key (abe_key, attrs); //TODO review this wireformat code_record_len = serialize_abe_keyinfo2 (&ih->ticket, ih->attrs, rp_key, &ecdhe_privkey, &code_record_data); code_record[0].data = code_record_data; code_record[0].data_size = code_record_len; code_record[0].expiration_time = GNUNET_TIME_UNIT_DAYS.rel_value_us; code_record[0].record_type = GNUNET_GNSRECORD_TYPE_ABE_KEY; code_record[0].flags = GNUNET_GNSRECORD_RF_RELATIVE_EXPIRATION; label = GNUNET_STRINGS_data_to_string_alloc (&ih->ticket.rnd, sizeof (uint64_t)); //Publish record ih->ns_qe = GNUNET_NAMESTORE_records_store (ns_handle, &ih->identity, label, 1, code_record, &store_ticket_issue_cont, ih); //for (; i > 0; i--) // GNUNET_free (attrs[i-1]); GNUNET_free (ecdhe_privkey); GNUNET_free (label); GNUNET_free (attrs); GNUNET_free (code_record_data); GNUNET_ABE_cpabe_delete_key (rp_key, GNUNET_YES); GNUNET_ABE_cpabe_delete_master_key (abe_key); } static int check_issue_ticket_message(void *cls, const struct IssueTicketMessage *im) { uint16_t size; size = ntohs (im->header.size); if (size <= sizeof (struct IssueTicketMessage)) { GNUNET_break (0); return GNUNET_SYSERR; } return GNUNET_OK; } static void handle_issue_ticket_message (void *cls, const struct IssueTicketMessage *im) { struct TicketIssueHandle *ih; struct IdpClient *idp = cls; size_t attrs_len; ih = GNUNET_new (struct TicketIssueHandle); attrs_len = ntohs (im->attr_len); ih->attrs = GNUNET_RECLAIM_ATTRIBUTE_list_deserialize ((char*)&im[1], attrs_len); ih->r_id = ntohl (im->id); ih->client = idp; ih->identity = im->identity; GNUNET_CRYPTO_ecdsa_key_get_public (&ih->identity, &ih->ticket.identity); ih->ticket.audience = im->rp; ih->ticket.rnd = GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_STRONG, UINT64_MAX); GNUNET_CONTAINER_DLL_insert (idp->issue_op_head, idp->issue_op_tail, ih); bootstrap_abe (&ih->identity, &issue_ticket_after_abe_bootstrap, ih, GNUNET_NO); GNUNET_SERVICE_client_continue (idp->client); } /********************************************************** * Revocation **********************************************************/ /** * Cleanup revoke handle * * @param rh the ticket revocation handle */ static void cleanup_revoke_ticket_handle (struct TicketRevocationHandle *rh) { if (NULL != rh->attrs) GNUNET_RECLAIM_ATTRIBUTE_list_destroy (rh->attrs); if (NULL != rh->rvk_attrs) GNUNET_RECLAIM_ATTRIBUTE_list_destroy (rh->rvk_attrs); if (NULL != rh->abe_key) GNUNET_ABE_cpabe_delete_master_key (rh->abe_key); if (NULL != rh->ns_qe) GNUNET_NAMESTORE_cancel (rh->ns_qe); if (NULL != rh->ns_it) GNUNET_NAMESTORE_zone_iteration_stop (rh->ns_it); GNUNET_free (rh); } /** * Send revocation result * * @param rh ticket revocation handle * @param success GNUNET_OK if successful result */ static void send_revocation_finished (struct TicketRevocationHandle *rh, uint32_t success) { struct GNUNET_MQ_Envelope *env; struct RevokeTicketResultMessage *trm; GNUNET_break(TKT_database->delete_ticket (TKT_database->cls, &rh->ticket)); env = GNUNET_MQ_msg (trm, GNUNET_MESSAGE_TYPE_RECLAIM_REVOKE_TICKET_RESULT); trm->id = htonl (rh->r_id); trm->success = htonl (success); GNUNET_MQ_send (rh->client->mq, env); GNUNET_CONTAINER_DLL_remove (rh->client->revoke_op_head, rh->client->revoke_op_tail, rh); } /** * Process ticket from database * * @param cls struct TicketIterationProcResult * @param ticket the ticket * @param attrs the attributes */ static void ticket_reissue_proc (void *cls, const struct GNUNET_RECLAIM_Ticket *ticket, const struct GNUNET_RECLAIM_ATTRIBUTE_ClaimList *attrs); static void revocation_reissue_tickets (struct TicketRevocationHandle *rh); static void reissue_next (void *cls) { struct TicketRevocationHandle *rh = cls; revocation_reissue_tickets (rh); } static void reissue_ticket_cont (void *cls, int32_t success, const char *emsg) { struct TicketRevocationHandle *rh = cls; rh->ns_qe = NULL; if (GNUNET_SYSERR == success) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "%s\n", "Unknown Error\n"); send_revocation_finished (rh, GNUNET_SYSERR); cleanup_revoke_ticket_handle (rh); return; } rh->offset++; GNUNET_SCHEDULER_add_now (&reissue_next, rh); } /** * Process ticket from database * * @param cls struct TicketIterationProcResult * @param ticket the ticket * @param attrs the attributes */ static void ticket_reissue_proc (void *cls, const struct GNUNET_RECLAIM_Ticket *ticket, const struct GNUNET_RECLAIM_ATTRIBUTE_ClaimList *attrs) { struct TicketRevocationHandle *rh = cls; struct GNUNET_RECLAIM_ATTRIBUTE_ClaimListEntry *le; struct GNUNET_RECLAIM_ATTRIBUTE_ClaimListEntry *le_rollover; struct GNUNET_CRYPTO_EcdhePrivateKey *ecdhe_privkey; struct GNUNET_GNSRECORD_Data code_record[1]; struct GNUNET_ABE_AbeKey *rp_key; char *code_record_data; char **attr_arr; char *label; char *policy; int attrs_len; uint32_t i; int reissue_ticket; size_t code_record_len; if (NULL == ticket) { GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Iteration done\n"); return; } if (0 == memcmp (&ticket->audience, &rh->ticket.audience, sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey))) { GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Do not reissue for this identity.!\n"); label = GNUNET_STRINGS_data_to_string_alloc (&rh->ticket.rnd, sizeof (uint64_t)); //Delete record rh->ns_qe = GNUNET_NAMESTORE_records_store (ns_handle, &rh->identity, label, 0, NULL, &reissue_ticket_cont, rh); GNUNET_free (label); return; } /* * Check if any attribute of this ticket intersects with a rollover attribute */ reissue_ticket = GNUNET_NO; for (le = attrs->list_head; NULL != le; le = le->next) { for (le_rollover = rh->rvk_attrs->list_head; NULL != le_rollover; le_rollover = le_rollover->next) { if (0 == strcmp (le_rollover->claim->name, le->claim->name)) { reissue_ticket = GNUNET_YES; le->claim->version = le_rollover->claim->version; } } } if (GNUNET_NO == reissue_ticket) { GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Skipping ticket.\n"); rh->offset++; GNUNET_SCHEDULER_add_now (&reissue_next, rh); return; } //Create new ABE key for RP attrs_len = 0; /* If this is the RP we want to revoke attributes of, the do so */ for (le = attrs->list_head; NULL != le; le = le->next) attrs_len++; attr_arr = GNUNET_malloc ((attrs_len + 1)*sizeof (char*)); i = 0; for (le = attrs->list_head; NULL != le; le = le->next) { GNUNET_asprintf (&policy, "%s_%lu", le->claim->name, le->claim->version); GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Recreating key with %s\n", policy); attr_arr[i] = policy; i++; } attr_arr[i] = NULL; rp_key = GNUNET_ABE_cpabe_create_key (rh->abe_key, attr_arr); //TODO review this wireformat code_record_len = serialize_abe_keyinfo2 (ticket, attrs, rp_key, &ecdhe_privkey, &code_record_data); code_record[0].data = code_record_data; code_record[0].data_size = code_record_len; code_record[0].expiration_time = GNUNET_TIME_UNIT_DAYS.rel_value_us; code_record[0].record_type = GNUNET_GNSRECORD_TYPE_ABE_KEY; code_record[0].flags = GNUNET_GNSRECORD_RF_RELATIVE_EXPIRATION; label = GNUNET_STRINGS_data_to_string_alloc (&ticket->rnd, sizeof (uint64_t)); //Publish record rh->ns_qe = GNUNET_NAMESTORE_records_store (ns_handle, &rh->identity, label, 1, code_record, &reissue_ticket_cont, rh); //for (; i > 0; i--) // GNUNET_free (attr_arr[i-1]); GNUNET_free (ecdhe_privkey); GNUNET_free (label); GNUNET_free (attr_arr); GNUNET_free (code_record_data); GNUNET_ABE_cpabe_delete_key (rp_key, GNUNET_YES); } /* Prototype for below function */ static void attr_reenc_cont (void *cls, int32_t success, const char *emsg); static void revocation_reissue_tickets (struct TicketRevocationHandle *rh) { int ret; /* Done, issue new keys */ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Revocation Phase III: Reissuing Tickets\n"); if (GNUNET_SYSERR == (ret = TKT_database->iterate_tickets (TKT_database->cls, &rh->ticket.identity, GNUNET_NO, rh->offset, &ticket_reissue_proc, rh))) { GNUNET_break (0); } if (GNUNET_NO == ret) { send_revocation_finished (rh, GNUNET_OK); cleanup_revoke_ticket_handle (rh); return; } } /** * Failed to check for attribute */ static void check_attr_error (void *cls) { struct TicketRevocationHandle *rh = cls; GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Unable to check for existing attribute\n"); rh->ns_qe = NULL; send_revocation_finished (rh, GNUNET_SYSERR); cleanup_revoke_ticket_handle (rh); } /** * Revoke next attribte by reencryption with * new ABE master */ static void reenc_next_attribute (void *cls); /** * Check for existing attribute and overwrite */ static void check_attr_cb (void *cls, const struct GNUNET_CRYPTO_EcdsaPrivateKey *zone, const char *label, unsigned int rd_count, const struct GNUNET_GNSRECORD_Data *rd_old) { struct TicketRevocationHandle *rh = cls; struct GNUNET_GNSRECORD_Data rd[1]; char* buf; char* enc_buf; size_t enc_size; char* rd_buf; size_t buf_size; char* policy; uint32_t attr_ver; rh->ns_qe = NULL; if (1 != rd_count) { GNUNET_SCHEDULER_add_now (&reenc_next_attribute, rh); return; } buf_size = GNUNET_RECLAIM_ATTRIBUTE_serialize_get_size (rh->attrs->list_head->claim); buf = GNUNET_malloc (buf_size); rh->attrs->list_head->claim->version++; GNUNET_RECLAIM_ATTRIBUTE_serialize (rh->attrs->list_head->claim, buf); GNUNET_asprintf (&policy, "%s_%lu", rh->attrs->list_head->claim->name, rh->attrs->list_head->claim->version); GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Encrypting with policy %s\n", policy); /** * Encrypt the attribute value and store in namestore */ enc_size = GNUNET_ABE_cpabe_encrypt (buf, buf_size, policy, //Policy rh->abe_key, (void**)&enc_buf); GNUNET_free (buf); if (GNUNET_SYSERR == enc_size) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Unable to re-encrypt with policy %s\n", policy); GNUNET_free (policy); send_revocation_finished (rh, GNUNET_SYSERR); cleanup_revoke_ticket_handle (rh); return; } GNUNET_free (policy); rd[0].data_size = enc_size + sizeof (uint32_t); rd_buf = GNUNET_malloc (rd[0].data_size); attr_ver = htonl (rh->attrs->list_head->claim->version); GNUNET_memcpy (rd_buf, &attr_ver, sizeof (uint32_t)); GNUNET_memcpy (rd_buf+sizeof (uint32_t), enc_buf, enc_size); rd[0].data = rd_buf; rd[0].record_type = GNUNET_GNSRECORD_TYPE_ID_ATTR; rd[0].flags = GNUNET_GNSRECORD_RF_RELATIVE_EXPIRATION; rd[0].expiration_time = rd_old[0].expiration_time; rh->ns_qe = GNUNET_NAMESTORE_records_store (ns_handle, &rh->identity, rh->attrs->list_head->claim->name, 1, rd, &attr_reenc_cont, rh); GNUNET_free (enc_buf); GNUNET_free (rd_buf); } /** * Revoke next attribte by reencryption with * new ABE master */ static void reenc_next_attribute (void *cls) { struct TicketRevocationHandle *rh = cls; if (NULL == rh->attrs->list_head) { revocation_reissue_tickets (rh); return; } /* First check if attribute still exists */ rh->ns_qe = GNUNET_NAMESTORE_records_lookup (ns_handle, &rh->identity, rh->attrs->list_head->claim->name, &check_attr_error, rh, &check_attr_cb, rh); } /** * Namestore callback after revoked attribute * is stored */ static void attr_reenc_cont (void *cls, int32_t success, const char *emsg) { struct TicketRevocationHandle *rh = cls; struct GNUNET_RECLAIM_ATTRIBUTE_ClaimListEntry *le; rh->ns_qe = NULL; if (GNUNET_SYSERR == success) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to reencrypt attribute %s\n", emsg); GNUNET_SCHEDULER_add_now (&do_shutdown, NULL); return; } if (NULL == rh->attrs->list_head) { revocation_reissue_tickets (rh); return; } le = rh->attrs->list_head; GNUNET_CONTAINER_DLL_remove (rh->attrs->list_head, rh->attrs->list_tail, le); GNUNET_assert (NULL != rh->rvk_attrs); GNUNET_CONTAINER_DLL_insert (rh->rvk_attrs->list_head, rh->rvk_attrs->list_tail, le); GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Re-encrypting next attribute\n"); reenc_next_attribute (rh); } static void process_attributes_to_update (void *cls, const struct GNUNET_RECLAIM_Ticket *ticket, const struct GNUNET_RECLAIM_ATTRIBUTE_ClaimList *attrs) { struct TicketRevocationHandle *rh = cls; rh->attrs = GNUNET_RECLAIM_ATTRIBUTE_list_dup (attrs); GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Revocation Phase I: Collecting attributes\n"); /* Reencrypt all attributes with new key */ if (NULL == rh->attrs->list_head) { /* No attributes to reencrypt */ send_revocation_finished (rh, GNUNET_OK); cleanup_revoke_ticket_handle (rh); return; } else { GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Revocation Phase II: Re-encrypting attributes\n"); reenc_next_attribute (rh); } } static void get_ticket_after_abe_bootstrap (void *cls, struct GNUNET_ABE_AbeMasterKey *abe_key) { GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Finished ABE bootstrap\n"); struct TicketRevocationHandle *rh = cls; rh->abe_key = abe_key; TKT_database->get_ticket_attributes (TKT_database->cls, &rh->ticket, &process_attributes_to_update, rh); } static int check_revoke_ticket_message(void *cls, const struct RevokeTicketMessage *im) { uint16_t size; size = ntohs (im->header.size); if (size <= sizeof (struct RevokeTicketMessage)) { GNUNET_break (0); return GNUNET_SYSERR; } return GNUNET_OK; } static void handle_revoke_ticket_message (void *cls, const struct RevokeTicketMessage *rm) { struct TicketRevocationHandle *rh; struct IdpClient *idp = cls; struct GNUNET_RECLAIM_Ticket *ticket; rh = GNUNET_new (struct TicketRevocationHandle); ticket = (struct GNUNET_RECLAIM_Ticket*)&rm[1]; rh->rvk_attrs = GNUNET_new (struct GNUNET_RECLAIM_ATTRIBUTE_ClaimList); rh->ticket = *ticket; rh->r_id = ntohl (rm->id); rh->client = idp; rh->identity = rm->identity; GNUNET_CRYPTO_ecdsa_key_get_public (&rh->identity, &rh->ticket.identity); GNUNET_CONTAINER_DLL_insert (idp->revoke_op_head, idp->revoke_op_tail, rh); bootstrap_abe (&rh->identity, &get_ticket_after_abe_bootstrap, rh, GNUNET_NO); GNUNET_SERVICE_client_continue (idp->client); } /** * Cleanup ticket consume handle * @param handle the handle to clean up */ static void cleanup_consume_ticket_handle (struct ConsumeTicketHandle *handle) { struct ParallelLookup *lu; struct ParallelLookup *tmp; if (NULL != handle->lookup_request) GNUNET_GNS_lookup_cancel (handle->lookup_request); for (lu = handle->parallel_lookups_head; NULL != lu;) { GNUNET_GNS_lookup_cancel (lu->lookup_request); GNUNET_free (lu->label); tmp = lu->next; GNUNET_CONTAINER_DLL_remove (handle->parallel_lookups_head, handle->parallel_lookups_tail, lu); GNUNET_free (lu); lu = tmp; } if (NULL != handle->key) GNUNET_ABE_cpabe_delete_key (handle->key, GNUNET_YES); if (NULL != handle->attrs) GNUNET_RECLAIM_ATTRIBUTE_list_destroy (handle->attrs); GNUNET_free (handle); } static int check_consume_ticket_message(void *cls, const struct ConsumeTicketMessage *cm) { uint16_t size; size = ntohs (cm->header.size); if (size <= sizeof (struct ConsumeTicketMessage)) { GNUNET_break (0); return GNUNET_SYSERR; } return GNUNET_OK; } static void process_parallel_lookup2 (void *cls, uint32_t rd_count, const struct GNUNET_GNSRECORD_Data *rd) { GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Parallel lookup finished (count=%u)\n", rd_count); struct ParallelLookup *parallel_lookup = cls; struct ConsumeTicketHandle *handle = parallel_lookup->handle; struct ConsumeTicketResultMessage *crm; struct GNUNET_MQ_Envelope *env; struct GNUNET_RECLAIM_ATTRIBUTE_ClaimListEntry *attr_le; struct GNUNET_TIME_Absolute decrypt_duration; char *data; char *data_tmp; ssize_t attr_len; size_t attrs_len; GNUNET_CONTAINER_DLL_remove (handle->parallel_lookups_head, handle->parallel_lookups_tail, parallel_lookup); GNUNET_free (parallel_lookup->label); GNUNET_STATISTICS_update (stats, "attribute_lookup_time_total", GNUNET_TIME_absolute_get_duration (parallel_lookup->lookup_start_time).rel_value_us, GNUNET_YES); GNUNET_STATISTICS_update (stats, "attribute_lookups_count", 1, GNUNET_YES); GNUNET_free (parallel_lookup); if (1 != rd_count) GNUNET_break(0);//TODO if (rd->record_type == GNUNET_GNSRECORD_TYPE_ID_ATTR) { decrypt_duration = GNUNET_TIME_absolute_get (); attr_len = GNUNET_ABE_cpabe_decrypt (rd->data + sizeof (uint32_t), rd->data_size - sizeof (uint32_t), handle->key, (void**)&data); if (GNUNET_SYSERR != attr_len) { GNUNET_STATISTICS_update (stats, "abe_decrypt_time_total", GNUNET_TIME_absolute_get_duration (decrypt_duration).rel_value_us, GNUNET_YES); GNUNET_STATISTICS_update (stats, "abe_decrypt_count", 1, GNUNET_YES); attr_le = GNUNET_new (struct GNUNET_RECLAIM_ATTRIBUTE_ClaimListEntry); attr_le->claim = GNUNET_RECLAIM_ATTRIBUTE_deserialize (data, attr_len); attr_le->claim->version = ntohl(*(uint32_t*)rd->data); GNUNET_CONTAINER_DLL_insert (handle->attrs->list_head, handle->attrs->list_tail, attr_le); GNUNET_free (data); } } if (NULL != handle->parallel_lookups_head) return; //Wait for more /* Else we are done */ /* Store ticket in DB */ if (GNUNET_OK != TKT_database->store_ticket (TKT_database->cls, &handle->ticket, handle->attrs)) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Unable to store ticket after consume\n"); GNUNET_break (0); } GNUNET_SCHEDULER_cancel (handle->kill_task); attrs_len = GNUNET_RECLAIM_ATTRIBUTE_list_serialize_get_size (handle->attrs); env = GNUNET_MQ_msg_extra (crm, attrs_len, GNUNET_MESSAGE_TYPE_RECLAIM_CONSUME_TICKET_RESULT); crm->id = htonl (handle->r_id); crm->attrs_len = htons (attrs_len); crm->identity = handle->ticket.identity; data_tmp = (char *) &crm[1]; GNUNET_RECLAIM_ATTRIBUTE_list_serialize (handle->attrs, data_tmp); GNUNET_MQ_send (handle->client->mq, env); GNUNET_CONTAINER_DLL_remove (handle->client->consume_op_head, handle->client->consume_op_tail, handle); cleanup_consume_ticket_handle (handle); } void abort_parallel_lookups2 (void *cls) { struct ConsumeTicketHandle *handle = cls; struct ParallelLookup *lu; struct ParallelLookup *tmp; struct AttributeResultMessage *arm; struct GNUNET_MQ_Envelope *env; handle->kill_task = NULL; for (lu = handle->parallel_lookups_head; NULL != lu;) { GNUNET_GNS_lookup_cancel (lu->lookup_request); GNUNET_free (lu->label); tmp = lu->next; GNUNET_CONTAINER_DLL_remove (handle->parallel_lookups_head, handle->parallel_lookups_tail, lu); GNUNET_free (lu); lu = tmp; } env = GNUNET_MQ_msg (arm, GNUNET_MESSAGE_TYPE_RECLAIM_ATTRIBUTE_RESULT); arm->id = htonl (handle->r_id); arm->attr_len = htons (0); GNUNET_MQ_send (handle->client->mq, env); } static void process_consume_abe_key (void *cls, uint32_t rd_count, const struct GNUNET_GNSRECORD_Data *rd) { struct ConsumeTicketHandle *handle = cls; struct GNUNET_HashCode new_key_hash; struct GNUNET_CRYPTO_SymmetricSessionKey enc_key; struct GNUNET_CRYPTO_SymmetricInitializationVector enc_iv; struct GNUNET_CRYPTO_EcdhePublicKey *ecdh_key; struct ParallelLookup *parallel_lookup; size_t size; char *buf; char *scope; handle->lookup_request = NULL; if (1 != rd_count) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Number of keys %d != 1.", rd_count); cleanup_consume_ticket_handle (handle); GNUNET_CONTAINER_DLL_remove (handle->client->consume_op_head, handle->client->consume_op_tail, handle); GNUNET_SCHEDULER_add_now (&do_shutdown, NULL); return; } //Decrypt ecdh_key = (struct GNUNET_CRYPTO_EcdhePublicKey *)rd->data; buf = GNUNET_malloc (rd->data_size - sizeof (struct GNUNET_CRYPTO_EcdhePublicKey)); //Calculate symmetric key from ecdh parameters GNUNET_assert (GNUNET_OK == GNUNET_CRYPTO_ecdsa_ecdh (&handle->identity, ecdh_key, &new_key_hash)); create_sym_key_from_ecdh (&new_key_hash, &enc_key, &enc_iv); size = GNUNET_CRYPTO_symmetric_decrypt (rd->data + sizeof (struct GNUNET_CRYPTO_EcdhePublicKey), rd->data_size - sizeof (struct GNUNET_CRYPTO_EcdhePublicKey), &enc_key, &enc_iv, buf); GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Decrypted bytes: %zd Expected bytes: %zd\n", size, rd->data_size - sizeof (struct GNUNET_CRYPTO_EcdhePublicKey)); GNUNET_STATISTICS_update (stats, "abe_key_lookup_time_total", GNUNET_TIME_absolute_get_duration (handle->lookup_start_time).rel_value_us, GNUNET_YES); GNUNET_STATISTICS_update (stats, "abe_key_lookups_count", 1, GNUNET_YES); scopes = GNUNET_strdup (buf); GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Scopes %s\n", scopes); handle->key = GNUNET_ABE_cpabe_deserialize_key ((void*)(buf + strlen (scopes) + 1), rd->data_size - sizeof (struct GNUNET_CRYPTO_EcdhePublicKey) - strlen (scopes) - 1); for (scope = strtok (scopes, ","); NULL != scope; scope = strtok (NULL, ",")) { GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Looking up %s\n", scope); parallel_lookup = GNUNET_new (struct ParallelLookup); parallel_lookup->handle = handle; parallel_lookup->label = GNUNET_strdup (scope); parallel_lookup->lookup_start_time = GNUNET_TIME_absolute_get(); parallel_lookup->lookup_request = GNUNET_GNS_lookup (gns_handle, scope, &handle->ticket.identity, GNUNET_GNSRECORD_TYPE_ID_ATTR, GNUNET_GNS_LO_DEFAULT, &process_parallel_lookup2, parallel_lookup); GNUNET_CONTAINER_DLL_insert (handle->parallel_lookups_head, handle->parallel_lookups_tail, parallel_lookup); } GNUNET_free (scopes); GNUNET_free (buf); handle->kill_task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_MINUTES,3), &abort_parallel_lookups2, handle); } static void handle_consume_ticket_message (void *cls, const struct ConsumeTicketMessage *cm) { struct ConsumeTicketHandle *ch; struct IdpClient *idp = cls; char* rnd_label; ch = GNUNET_new (struct ConsumeTicketHandle); ch->r_id = ntohl (cm->id); ch->client = idp; ch->identity = cm->identity; ch->attrs = GNUNET_new (struct GNUNET_RECLAIM_ATTRIBUTE_ClaimList); GNUNET_CRYPTO_ecdsa_key_get_public (&ch->identity, &ch->identity_pub); ch->ticket = *((struct GNUNET_RECLAIM_Ticket*)&cm[1]); rnd_label = GNUNET_STRINGS_data_to_string_alloc (&ch->ticket.rnd, sizeof (uint64_t)); GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Looking for ABE key under %s\n", rnd_label); ch->lookup_start_time = GNUNET_TIME_absolute_get (); ch->lookup_request = GNUNET_GNS_lookup (gns_handle, rnd_label, &ch->ticket.identity, GNUNET_GNSRECORD_TYPE_ABE_KEY, GNUNET_GNS_LO_DEFAULT, &process_consume_abe_key, ch); GNUNET_CONTAINER_DLL_insert (idp->consume_op_head, idp->consume_op_tail, ch); GNUNET_free (rnd_label); GNUNET_SERVICE_client_continue (idp->client); } /** * Cleanup attribute store handle * * @param handle handle to clean up */ static void cleanup_as_handle (struct AttributeStoreHandle *handle) { if (NULL != handle->ns_qe) GNUNET_NAMESTORE_cancel (handle->ns_qe); if (NULL != handle->claim) GNUNET_free (handle->claim); if (NULL != handle->abe_key) GNUNET_ABE_cpabe_delete_master_key (handle->abe_key); GNUNET_free (handle); } static void attr_store_cont (void *cls, int32_t success, const char *emsg) { struct AttributeStoreHandle *as_handle = cls; struct GNUNET_MQ_Envelope *env; struct AttributeStoreResultMessage *acr_msg; as_handle->ns_qe = NULL; GNUNET_CONTAINER_DLL_remove (as_handle->client->store_op_head, as_handle->client->store_op_tail, as_handle); if (GNUNET_SYSERR == success) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to store attribute %s\n", emsg); cleanup_as_handle (as_handle); GNUNET_SCHEDULER_add_now (&do_shutdown, NULL); return; } GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Sending ATTRIBUTE_STORE_RESPONSE message\n"); env = GNUNET_MQ_msg (acr_msg, GNUNET_MESSAGE_TYPE_RECLAIM_ATTRIBUTE_STORE_RESPONSE); acr_msg->id = htonl (as_handle->r_id); acr_msg->op_result = htonl (GNUNET_OK); GNUNET_MQ_send (as_handle->client->mq, env); cleanup_as_handle (as_handle); } static void attr_store_task (void *cls) { struct AttributeStoreHandle *as_handle = cls; struct GNUNET_GNSRECORD_Data rd[1]; char* buf; char* policy; char* enc_buf; char* rd_buf; size_t enc_size; size_t buf_size; uint32_t attr_ver; GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Storing attribute\n"); buf_size = GNUNET_RECLAIM_ATTRIBUTE_serialize_get_size (as_handle->claim); buf = GNUNET_malloc (buf_size); GNUNET_RECLAIM_ATTRIBUTE_serialize (as_handle->claim, buf); GNUNET_asprintf (&policy, "%s_%lu", as_handle->claim->name, as_handle->claim->version); GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Encrypting with policy %s\n", policy); /** * Encrypt the attribute value and store in namestore */ enc_size = GNUNET_ABE_cpabe_encrypt (buf, buf_size, policy, //Policy as_handle->abe_key, (void**)&enc_buf); if (GNUNET_SYSERR == enc_size) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to encrypt with policy %s\n", policy); GNUNET_CONTAINER_DLL_remove (as_handle->client->store_op_head, as_handle->client->store_op_tail, as_handle); cleanup_as_handle (as_handle); GNUNET_free (buf); GNUNET_free (policy); GNUNET_SCHEDULER_add_now (&do_shutdown, NULL); return; } GNUNET_free (buf); GNUNET_free (policy); rd[0].data_size = enc_size + sizeof (uint32_t); rd_buf = GNUNET_malloc (rd[0].data_size); attr_ver = htonl (as_handle->claim->version); GNUNET_memcpy (rd_buf, &attr_ver, sizeof (uint32_t)); GNUNET_memcpy (rd_buf+sizeof (uint32_t), enc_buf, enc_size); rd[0].data = rd_buf; rd[0].record_type = GNUNET_GNSRECORD_TYPE_ID_ATTR; rd[0].flags = GNUNET_GNSRECORD_RF_RELATIVE_EXPIRATION; rd[0].expiration_time = as_handle->exp.rel_value_us; as_handle->ns_qe = GNUNET_NAMESTORE_records_store (ns_handle, &as_handle->identity, as_handle->claim->name, 1, rd, &attr_store_cont, as_handle); GNUNET_free (enc_buf); GNUNET_free (rd_buf); } static void store_after_abe_bootstrap (void *cls, struct GNUNET_ABE_AbeMasterKey *abe_key) { GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Finished ABE bootstrap\n"); struct AttributeStoreHandle *ash = cls; ash->abe_key = abe_key; GNUNET_SCHEDULER_add_now (&attr_store_task, ash); } static int check_attribute_store_message(void *cls, const struct AttributeStoreMessage *sam) { uint16_t size; size = ntohs (sam->header.size); if (size <= sizeof (struct AttributeStoreMessage)) { GNUNET_break (0); return GNUNET_SYSERR; } return GNUNET_OK; } static void handle_attribute_store_message (void *cls, const struct AttributeStoreMessage *sam) { struct AttributeStoreHandle *as_handle; struct IdpClient *idp = cls; size_t data_len; GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Received ATTRIBUTE_STORE message\n"); data_len = ntohs (sam->attr_len); as_handle = GNUNET_new (struct AttributeStoreHandle); as_handle->claim = GNUNET_RECLAIM_ATTRIBUTE_deserialize ((char*)&sam[1], data_len); as_handle->r_id = ntohl (sam->id); as_handle->identity = sam->identity; as_handle->exp.rel_value_us = GNUNET_ntohll (sam->exp); GNUNET_CRYPTO_ecdsa_key_get_public (&sam->identity, &as_handle->identity_pkey); GNUNET_SERVICE_client_continue (idp->client); as_handle->client = idp; GNUNET_CONTAINER_DLL_insert (idp->store_op_head, idp->store_op_tail, as_handle); bootstrap_abe (&as_handle->identity, &store_after_abe_bootstrap, as_handle, GNUNET_NO); } static void cleanup_attribute_iter_handle (struct AttributeIterator *ai) { if (NULL != ai->abe_key) GNUNET_ABE_cpabe_delete_master_key (ai->abe_key); GNUNET_free (ai); } static void attr_iter_error (void *cls) { struct AttributeIterator *ai = cls; //TODO GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to iterate over attributes\n"); GNUNET_CONTAINER_DLL_remove (ai->client->attr_iter_head, ai->client->attr_iter_tail, ai); cleanup_attribute_iter_handle (ai); GNUNET_SCHEDULER_add_now (&do_shutdown, NULL); } static void attr_iter_finished (void *cls) { struct AttributeIterator *ai = cls; struct GNUNET_MQ_Envelope *env; struct AttributeResultMessage *arm; env = GNUNET_MQ_msg (arm, GNUNET_MESSAGE_TYPE_RECLAIM_ATTRIBUTE_RESULT); arm->id = htonl (ai->request_id); arm->attr_len = htons (0); GNUNET_MQ_send (ai->client->mq, env); GNUNET_CONTAINER_DLL_remove (ai->client->attr_iter_head, ai->client->attr_iter_tail, ai); cleanup_attribute_iter_handle (ai); } static void attr_iter_cb (void *cls, const struct GNUNET_CRYPTO_EcdsaPrivateKey *zone, const char *label, unsigned int rd_count, const struct GNUNET_GNSRECORD_Data *rd) { struct AttributeIterator *ai = cls; struct AttributeResultMessage *arm; struct GNUNET_ABE_AbeKey *key; struct GNUNET_MQ_Envelope *env; ssize_t msg_extra_len; char* attr_ser; char* attrs[2]; char* data_tmp; char* policy; uint32_t attr_ver; if (rd_count != 1) { GNUNET_NAMESTORE_zone_iterator_next (ai->ns_it, 1); return; } if (GNUNET_GNSRECORD_TYPE_ID_ATTR != rd->record_type) { GNUNET_NAMESTORE_zone_iterator_next (ai->ns_it, 1); return; } attr_ver = ntohl(*((uint32_t*)rd->data)); GNUNET_asprintf (&policy, "%s_%lu", label, attr_ver); attrs[0] = policy; attrs[1] = 0; key = GNUNET_ABE_cpabe_create_key (ai->abe_key, attrs); msg_extra_len = GNUNET_ABE_cpabe_decrypt (rd->data+sizeof (uint32_t), rd->data_size-sizeof (uint32_t), key, (void**)&attr_ser); if (GNUNET_SYSERR == msg_extra_len) { GNUNET_NAMESTORE_zone_iterator_next (ai->ns_it, 1); return; } GNUNET_ABE_cpabe_delete_key (key, GNUNET_YES); //GNUNET_free (policy); GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Found attribute: %s\n", label); env = GNUNET_MQ_msg_extra (arm, msg_extra_len, GNUNET_MESSAGE_TYPE_RECLAIM_ATTRIBUTE_RESULT); arm->id = htonl (ai->request_id); arm->attr_len = htons (msg_extra_len); GNUNET_CRYPTO_ecdsa_key_get_public (zone, &arm->identity); data_tmp = (char *) &arm[1]; GNUNET_memcpy (data_tmp, attr_ser, msg_extra_len); GNUNET_MQ_send (ai->client->mq, env); GNUNET_free (attr_ser); GNUNET_ABE_cpabe_delete_master_key (ai->abe_key); ai->abe_key = NULL; } void iterate_after_abe_bootstrap (void *cls, struct GNUNET_ABE_AbeMasterKey *abe_key) { struct AttributeIterator *ai = cls; ai->abe_key = abe_key; ai->ns_it = GNUNET_NAMESTORE_zone_iteration_start (ns_handle, &ai->identity, &attr_iter_error, ai, &attr_iter_cb, ai, &attr_iter_finished, ai); } static void iterate_next_after_abe_bootstrap (void *cls, struct GNUNET_ABE_AbeMasterKey *abe_key) { struct AttributeIterator *ai = cls; ai->abe_key = abe_key; GNUNET_NAMESTORE_zone_iterator_next (ai->ns_it, 1); } static void handle_iteration_start (void *cls, const struct AttributeIterationStartMessage *ais_msg) { struct IdpClient *idp = cls; struct AttributeIterator *ai; GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Received ATTRIBUTE_ITERATION_START message\n"); ai = GNUNET_new (struct AttributeIterator); ai->request_id = ntohl (ais_msg->id); ai->client = idp; ai->identity = ais_msg->identity; GNUNET_CONTAINER_DLL_insert (idp->attr_iter_head, idp->attr_iter_tail, ai); bootstrap_abe (&ai->identity, &iterate_after_abe_bootstrap, ai, GNUNET_NO); GNUNET_SERVICE_client_continue (idp->client); } static void handle_iteration_stop (void *cls, const struct AttributeIterationStopMessage *ais_msg) { struct IdpClient *idp = cls; struct AttributeIterator *ai; uint32_t rid; GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Received `%s' message\n", "ATTRIBUTE_ITERATION_STOP"); rid = ntohl (ais_msg->id); for (ai = idp->attr_iter_head; NULL != ai; ai = ai->next) if (ai->request_id == rid) break; if (NULL == ai) { GNUNET_break (0); GNUNET_SERVICE_client_drop (idp->client); return; } GNUNET_CONTAINER_DLL_remove (idp->attr_iter_head, idp->attr_iter_tail, ai); GNUNET_free (ai); GNUNET_SERVICE_client_continue (idp->client); } static void handle_iteration_next (void *cls, const struct AttributeIterationNextMessage *ais_msg) { struct IdpClient *idp = cls; struct AttributeIterator *ai; uint32_t rid; GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Received ATTRIBUTE_ITERATION_NEXT message\n"); rid = ntohl (ais_msg->id); for (ai = idp->attr_iter_head; NULL != ai; ai = ai->next) if (ai->request_id == rid) break; if (NULL == ai) { GNUNET_break (0); GNUNET_SERVICE_client_drop (idp->client); return; } bootstrap_abe (&ai->identity, &iterate_next_after_abe_bootstrap, ai, GNUNET_NO); GNUNET_SERVICE_client_continue (idp->client); } /** * Ticket iteration processor result */ enum ZoneIterationResult { /** * Iteration start. */ IT_START = 0, /** * Found tickets, * Continue to iterate with next iteration_next call */ IT_SUCCESS_MORE_AVAILABLE = 1, /** * Iteration complete */ IT_SUCCESS_NOT_MORE_RESULTS_AVAILABLE = 2 }; /** * Context for ticket iteration */ struct TicketIterationProcResult { /** * The ticket iteration handle */ struct TicketIteration *ti; /** * Iteration result: iteration done? * #IT_SUCCESS_MORE_AVAILABLE: if there may be more results overall but * we got one for now and have sent it to the client * #IT_SUCCESS_NOT_MORE_RESULTS_AVAILABLE: if there are no further results, * #IT_START: if we are still trying to find a result. */ int res_iteration_finished; }; static void cleanup_ticket_iter_handle (struct TicketIteration *ti) { GNUNET_free (ti); } /** * Process ticket from database * * @param cls struct TicketIterationProcResult * @param ticket the ticket * @param attrs the attributes */ static void ticket_iterate_proc (void *cls, const struct GNUNET_RECLAIM_Ticket *ticket, const struct GNUNET_RECLAIM_ATTRIBUTE_ClaimList *attrs) { struct TicketIterationProcResult *proc = cls; if (NULL == ticket) { GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Iteration done\n"); proc->res_iteration_finished = IT_SUCCESS_NOT_MORE_RESULTS_AVAILABLE; return; } proc->res_iteration_finished = IT_SUCCESS_MORE_AVAILABLE; send_ticket_result (proc->ti->client, proc->ti->r_id, ticket, attrs); } /** * Perform ticket iteration step * * @param ti ticket iterator to process */ static void run_ticket_iteration_round (struct TicketIteration *ti) { struct TicketIterationProcResult proc; struct GNUNET_MQ_Envelope *env; struct TicketResultMessage *trm; int ret; memset (&proc, 0, sizeof (proc)); proc.ti = ti; proc.res_iteration_finished = IT_START; while (IT_START == proc.res_iteration_finished) { if (GNUNET_SYSERR == (ret = TKT_database->iterate_tickets (TKT_database->cls, &ti->identity, ti->is_audience, ti->offset, &ticket_iterate_proc, &proc))) { GNUNET_break (0); break; } if (GNUNET_NO == ret) proc.res_iteration_finished = IT_SUCCESS_NOT_MORE_RESULTS_AVAILABLE; ti->offset++; } if (IT_SUCCESS_MORE_AVAILABLE == proc.res_iteration_finished) { GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "More results available\n"); return; /* more later */ } /* send empty response to indicate end of list */ env = GNUNET_MQ_msg (trm, GNUNET_MESSAGE_TYPE_RECLAIM_TICKET_RESULT); trm->id = htonl (ti->r_id); GNUNET_MQ_send (ti->client->mq, env); GNUNET_CONTAINER_DLL_remove (ti->client->ticket_iter_head, ti->client->ticket_iter_tail, ti); cleanup_ticket_iter_handle (ti); } static void handle_ticket_iteration_start (void *cls, const struct TicketIterationStartMessage *tis_msg) { struct IdpClient *client = cls; struct TicketIteration *ti; GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Received TICKET_ITERATION_START message\n"); ti = GNUNET_new (struct TicketIteration); ti->r_id = ntohl (tis_msg->id); ti->offset = 0; ti->client = client; ti->identity = tis_msg->identity; ti->is_audience = ntohl (tis_msg->is_audience); GNUNET_CONTAINER_DLL_insert (client->ticket_iter_head, client->ticket_iter_tail, ti); run_ticket_iteration_round (ti); GNUNET_SERVICE_client_continue (client->client); } static void handle_ticket_iteration_stop (void *cls, const struct TicketIterationStopMessage *tis_msg) { struct IdpClient *client = cls; struct TicketIteration *ti; uint32_t rid; GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Received `%s' message\n", "TICKET_ITERATION_STOP"); rid = ntohl (tis_msg->id); for (ti = client->ticket_iter_head; NULL != ti; ti = ti->next) if (ti->r_id == rid) break; if (NULL == ti) { GNUNET_break (0); GNUNET_SERVICE_client_drop (client->client); return; } GNUNET_CONTAINER_DLL_remove (client->ticket_iter_head, client->ticket_iter_tail, ti); cleanup_ticket_iter_handle (ti); GNUNET_SERVICE_client_continue (client->client); } static void handle_ticket_iteration_next (void *cls, const struct TicketIterationNextMessage *tis_msg) { struct IdpClient *client = cls; struct TicketIteration *ti; uint32_t rid; GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Received TICKET_ITERATION_NEXT message\n"); rid = ntohl (tis_msg->id); for (ti = client->ticket_iter_head; NULL != ti; ti = ti->next) if (ti->r_id == rid) break; if (NULL == ti) { GNUNET_break (0); GNUNET_SERVICE_client_drop (client->client); return; } run_ticket_iteration_round (ti); GNUNET_SERVICE_client_continue (client->client); } /** * Main function that will be run * * @param cls closure * @param c the configuration used * @param server the service handle */ static void run (void *cls, const struct GNUNET_CONFIGURATION_Handle *c, struct GNUNET_SERVICE_Handle *server) { char *database; cfg = c; stats = GNUNET_STATISTICS_create ("reclaim", cfg); //Connect to identity and namestore services ns_handle = GNUNET_NAMESTORE_connect (cfg); if (NULL == ns_handle) { GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "error connecting to namestore"); } gns_handle = GNUNET_GNS_connect (cfg); if (NULL == gns_handle) { GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "error connecting to gns"); } credential_handle = GNUNET_CREDENTIAL_connect (cfg); if (NULL == credential_handle) { GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "error connecting to credential"); } identity_handle = GNUNET_IDENTITY_connect (cfg, NULL, NULL); /* Loading DB plugin */ if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (cfg, "reclaim", "database", &database)) GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "No database backend configured\n"); GNUNET_asprintf (&db_lib_name, "libgnunet_plugin_reclaim_%s", database); TKT_database = GNUNET_PLUGIN_load (db_lib_name, (void *) cfg); GNUNET_free (database); if (NULL == TKT_database) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Could not load database backend `%s'\n", db_lib_name); GNUNET_SCHEDULER_shutdown (); return; } if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_time (cfg, "reclaim", "TOKEN_EXPIRATION_INTERVAL", &token_expiration_interval)) { GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Time window for zone iteration: %s\n", GNUNET_STRINGS_relative_time_to_string (token_expiration_interval, GNUNET_YES)); } else { token_expiration_interval = DEFAULT_TOKEN_EXPIRATION_INTERVAL; } GNUNET_SCHEDULER_add_shutdown (&do_shutdown, NULL); } /** * Called whenever a client is disconnected. * * @param cls closure * @param client identification of the client * @param app_ctx @a client */ static void client_disconnect_cb (void *cls, struct GNUNET_SERVICE_Client *client, void *app_ctx) { struct IdpClient *idp = app_ctx; struct AttributeIterator *ai; struct TicketIteration *ti; struct TicketRevocationHandle *rh; struct TicketIssueHandle *iss; struct ConsumeTicketHandle *ct; struct AttributeStoreHandle *as; //TODO other operations GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Client %p disconnected\n", client); while (NULL != (iss = idp->issue_op_head)) { GNUNET_CONTAINER_DLL_remove (idp->issue_op_head, idp->issue_op_tail, iss); cleanup_ticket_issue_handle (iss); } while (NULL != (ct = idp->consume_op_head)) { GNUNET_CONTAINER_DLL_remove (idp->consume_op_head, idp->consume_op_tail, ct); cleanup_consume_ticket_handle (ct); } while (NULL != (as = idp->store_op_head)) { GNUNET_CONTAINER_DLL_remove (idp->store_op_head, idp->store_op_tail, as); cleanup_as_handle (as); } while (NULL != (ai = idp->attr_iter_head)) { GNUNET_CONTAINER_DLL_remove (idp->attr_iter_head, idp->attr_iter_tail, ai); cleanup_attribute_iter_handle (ai); } while (NULL != (rh = idp->revoke_op_head)) { GNUNET_CONTAINER_DLL_remove (idp->revoke_op_head, idp->revoke_op_tail, rh); cleanup_revoke_ticket_handle (rh); } while (NULL != (ti = idp->ticket_iter_head)) { GNUNET_CONTAINER_DLL_remove (idp->ticket_iter_head, idp->ticket_iter_tail, ti); cleanup_ticket_iter_handle (ti); } GNUNET_free (idp); } /** * Add a client to our list of active clients. * * @param cls NULL * @param client client to add * @param mq message queue for @a client * @return internal namestore client structure for this client */ static void * client_connect_cb (void *cls, struct GNUNET_SERVICE_Client *client, struct GNUNET_MQ_Handle *mq) { struct IdpClient *idp; GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Client %p connected\n", client); idp = GNUNET_new (struct IdpClient); idp->client = client; idp->mq = mq; return idp; } /** * Define "main" method using service macro. */ GNUNET_SERVICE_MAIN ("reclaim", GNUNET_SERVICE_OPTION_NONE, &run, &client_connect_cb, &client_disconnect_cb, NULL, GNUNET_MQ_hd_var_size (attribute_store_message, GNUNET_MESSAGE_TYPE_RECLAIM_ATTRIBUTE_STORE, struct AttributeStoreMessage, NULL), GNUNET_MQ_hd_fixed_size (iteration_start, GNUNET_MESSAGE_TYPE_RECLAIM_ATTRIBUTE_ITERATION_START, struct AttributeIterationStartMessage, NULL), GNUNET_MQ_hd_fixed_size (iteration_next, GNUNET_MESSAGE_TYPE_RECLAIM_ATTRIBUTE_ITERATION_NEXT, struct AttributeIterationNextMessage, NULL), GNUNET_MQ_hd_fixed_size (iteration_stop, GNUNET_MESSAGE_TYPE_RECLAIM_ATTRIBUTE_ITERATION_STOP, struct AttributeIterationStopMessage, NULL), GNUNET_MQ_hd_var_size (issue_ticket_message, GNUNET_MESSAGE_TYPE_RECLAIM_ISSUE_TICKET, struct IssueTicketMessage, NULL), GNUNET_MQ_hd_var_size (consume_ticket_message, GNUNET_MESSAGE_TYPE_RECLAIM_CONSUME_TICKET, struct ConsumeTicketMessage, NULL), GNUNET_MQ_hd_fixed_size (ticket_iteration_start, GNUNET_MESSAGE_TYPE_RECLAIM_TICKET_ITERATION_START, struct TicketIterationStartMessage, NULL), GNUNET_MQ_hd_fixed_size (ticket_iteration_next, GNUNET_MESSAGE_TYPE_RECLAIM_TICKET_ITERATION_NEXT, struct TicketIterationNextMessage, NULL), GNUNET_MQ_hd_fixed_size (ticket_iteration_stop, GNUNET_MESSAGE_TYPE_RECLAIM_TICKET_ITERATION_STOP, struct TicketIterationStopMessage, NULL), GNUNET_MQ_hd_var_size (revoke_ticket_message, GNUNET_MESSAGE_TYPE_RECLAIM_REVOKE_TICKET, struct RevokeTicketMessage, NULL), GNUNET_MQ_handler_end()); /* end of gnunet-service-reclaim.c */