2 This file is part of GNUnet.
3 Copyright (C) 2011-2013 GNUnet e.V.
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 3, or (at your
8 option) any later version.
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18 Boston, MA 02110-1301, USA.
21 * @file gns/gnunet-service-credential.c
22 * @brief GNU Credential Service (main service)
23 * @author Adnan Husain
26 #include "gnunet_util_lib.h"
27 #include "gnunet_credential_service.h"
28 #include "gnunet_statistics_service.h"
29 #include "credential.h"
30 #include "gnunet_protocols.h"
31 #include "gnunet_signatures.h"
33 // For Looking up GNS request
34 #include <gnunet_dnsparser_lib.h>
35 #include <gnunet_identity_service.h>
36 #include <gnunet_gnsrecord_lib.h>
37 #include <gnunet_namestore_service.h>
38 #include <gnunet_gns_service.h>
39 #include "gnunet_gns_service.h"
44 #define GNUNET_CREDENTIAL_MAX_LENGTH 255
49 struct CredentialRecordEntry
54 struct CredentialRecordEntry *next;
59 struct CredentialRecordEntry *prev;
65 struct GNUNET_CREDENTIAL_CredentialRecordData *data;
74 * DLL for attributes - Used as a queue
75 * Insert tail - Pop head
77 struct AttributeQueueEntry
82 struct AttributeQueueEntry *next;
87 struct AttributeQueueEntry *prev;
92 struct GNUNET_CREDENTIAL_AttributeRecordData *data;
100 * Parent attribute delegation
102 struct AttributeQueueEntry *parent;
105 * Trailing attribute context
112 * Handle to a lookup operation from api
114 struct VerifyRequestHandle
118 * We keep these in a DLL.
120 struct VerifyRequestHandle *next;
123 * We keep these in a DLL.
125 struct VerifyRequestHandle *prev;
128 * Handle to the requesting client
130 struct GNUNET_SERVICE_Client *client;
133 * Handle to GNS lookup
135 struct GNUNET_GNS_LookupRequest *lookup_request;
140 struct GNUNET_CRYPTO_EcdsaPublicKey issuer_key;
145 char *issuer_attribute;
150 struct GNUNET_CRYPTO_EcdsaPublicKey subject_key;
155 struct CredentialRecordEntry *cred_chain_head;
160 struct CredentialRecordEntry *cred_chain_tail;
163 * Number of chain entries
165 uint32_t cred_chain_entries;
170 struct AttributeQueueEntry *attr_queue_head;
175 struct AttributeQueueEntry *attr_queue_tail;
178 * Current Attribute Pointer
180 struct AttributeQueueEntry *current_attribute;
183 * The found credential
185 struct GNUNET_CREDENTIAL_CredentialRecordData *credential;
188 * Length of the credential
190 uint32_t credential_size;
203 static struct VerifyRequestHandle *vrh_head;
208 static struct VerifyRequestHandle *vrh_tail;
211 * Handle to the statistics service
213 static struct GNUNET_STATISTICS_Handle *statistics;
218 * Handle to GNS service.
220 static struct GNUNET_GNS_Handle *gns;
223 * Task run during shutdown.
229 shutdown_task (void *cls)
231 struct VerifyRequestHandle *vrh;
233 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
235 while (NULL != (vrh = vrh_head))
237 //CREDENTIAL_resolver_lookup_cancel (clh->lookup);
238 GNUNET_CONTAINER_DLL_remove (vrh_head,
246 GNUNET_GNS_disconnect (gns);
249 if (NULL != statistics)
251 GNUNET_STATISTICS_destroy (statistics,
259 * Checks a #GNUNET_MESSAGE_TYPE_CREDENTIAL_VERIFY message
261 * @param cls client sending the message
262 * @param v_msg message of type `struct VerifyMessage`
263 * @return #GNUNET_OK if @a v_msg is well-formed
266 check_verify (void *cls,
267 const struct VerifyMessage *v_msg)
272 msg_size = ntohs (v_msg->header.size);
273 if (msg_size < sizeof (struct VerifyMessage))
276 return GNUNET_SYSERR;
278 if ((ntohs (v_msg->issuer_attribute_len) > GNUNET_CREDENTIAL_MAX_LENGTH) ||
279 (ntohs (v_msg->subject_attribute_len) > GNUNET_CREDENTIAL_MAX_LENGTH))
282 return GNUNET_SYSERR;
284 attrs = (const char *) &v_msg[1];
286 if ( ('\0' != attrs[ntohs(v_msg->header.size) - sizeof (struct VerifyMessage) - 1]) ||
287 (strlen (attrs) > GNUNET_CREDENTIAL_MAX_LENGTH * 2) )
290 return GNUNET_SYSERR;
298 * @param handle the handle to the request
301 send_lookup_response (struct VerifyRequestHandle *vrh)
304 struct GNUNET_MQ_Envelope *env;
305 struct VerifyResultMessage *rmsg;
308 * Get serialized record data size
310 len = vrh->credential_size; //TODO max length of attr
312 //TODO add attr chain
314 * Prepare a lookup result response message for the client
316 env = GNUNET_MQ_msg_extra (rmsg,
318 GNUNET_MESSAGE_TYPE_CREDENTIAL_VERIFY_RESULT);
319 //Assign id so that client can find associated request
320 rmsg->id = vrh->request_id;
321 rmsg->cd_count = htonl (vrh->cred_chain_entries);
324 * Get serialized record data
325 * Append at the end of rmsg
327 rmsg->cred_found = htonl (GNUNET_NO);
328 if (NULL != vrh->credential)
332 vrh->credential_size);
333 rmsg->cred_found = htonl (GNUNET_YES);
336 /*char* tmp_entry = (char*)&rmsg[1];
337 for (cr_entry = vrh->cred_chain_head; NULL != cr_entry; cr_entry = cr_entry->next)
340 &cr_entry->record_data,
341 cr_entry->record_data_size);
342 tmp_entry += cr_entry->record_data_size;
344 GNUNET_MQ_send (GNUNET_SERVICE_client_get_mq(vrh->client),
347 GNUNET_CONTAINER_DLL_remove (vrh_head, vrh_tail, vrh);
352 * - Refactor into cleanup_handle() function for this
356 GNUNET_STATISTICS_update (statistics,
357 "Completed verifications", 1,
363 start_backward_resolution (void* cls,
365 const struct GNUNET_GNSRECORD_Data *rd)
367 struct VerifyRequestHandle *vrh = cls;
368 struct GNUNET_CREDENTIAL_CredentialRecordData *cred;
369 const struct GNUNET_CREDENTIAL_AttributeRecordData *attr;
370 struct CredentialRecordEntry *cred_pointer;
371 struct AttributeQueueEntry *attr_entry;
376 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
377 "Got %d attrs\n", rd_count);
379 for (i=0; i < rd_count; i++)
381 if (GNUNET_GNSRECORD_TYPE_ATTRIBUTE != rd[i].record_type)
385 attr_entry = GNUNET_new (struct AttributeQueueEntry);
386 attr_entry->data_size = rd[i].data_size;
387 if (NULL != vrh->current_attribute &&
388 NULL != vrh->current_attribute->attr_trailer)
390 if (rd[i].data_size == sizeof (struct GNUNET_CREDENTIAL_AttributeRecordData))
392 GNUNET_asprintf (&expanded_attr,
394 vrh->current_attribute->attr_trailer);
397 GNUNET_asprintf (&expanded_attr,
400 vrh->current_attribute->attr_trailer);
402 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
403 "Expanded to %s\n", expanded_attr);
404 attr_entry->data_size += strlen (vrh->current_attribute->attr_trailer) + 1;
406 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
407 "Not Expanding %s\n", (char*)&attr[1]);
409 attr_entry->data = GNUNET_malloc (attr_entry->data_size);
410 memcpy (attr_entry->data,
413 if (NULL != vrh->current_attribute && NULL != vrh->current_attribute->attr_trailer)
415 memcpy ((char*)&attr_entry->data[1],
417 strlen (expanded_attr));
419 check_attr = (char*)&attr_entry->data[1];
420 check_attr[attr_entry->data_size] = '\0';
421 attr_entry->parent = vrh->current_attribute;
423 GNUNET_CONTAINER_DLL_insert (vrh->attr_queue_head,
424 vrh->attr_queue_tail,
426 for(cred_pointer = vrh->cred_chain_head; cred_pointer != NULL;
427 cred_pointer = cred_pointer->next){
428 cred = cred_pointer->data;
429 if(0 != memcmp (&attr->subject_key,
430 &cred_pointer->data->issuer_key,
431 sizeof(struct GNUNET_CRYPTO_EcdsaPublicKey)))
433 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
434 "Checking if %s matches %s\n",
435 (char*)&attr_entry->data[1], (char*)&cred[1]);
437 if (0 != strcmp ((char*)&attr_entry->data[1], (char*)&cred[1]))
440 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
442 vrh->credential = GNUNET_malloc (rd[i].data_size);
443 memcpy (vrh->credential,
446 vrh->credential_size = rd[i].data_size;
448 send_lookup_response (vrh);
456 //Start from next to head
457 vrh->current_attribute = vrh->attr_queue_head;
459 if(NULL == vrh->current_attribute)
461 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
462 "We are all out of attributes...\n");
463 send_lookup_response (vrh);
467 GNUNET_CONTAINER_DLL_remove (vrh->attr_queue_head,
468 vrh->attr_queue_tail,
469 vrh->current_attribute);
473 //Start with backward resolution
474 char issuer_attribute_name[strlen ((char*)&vrh->current_attribute->data[1])];
476 strcpy (issuer_attribute_name,
477 (char*)&vrh->current_attribute->data[1]);
478 char *next_attr = strtok (issuer_attribute_name, ".");
479 GNUNET_asprintf (&lookup_attr,
482 next_attr += strlen (next_attr) + 1;
483 vrh->current_attribute->attr_trailer = GNUNET_strdup (next_attr);
485 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
486 "Looking up %s\n", lookup_attr);
487 if (NULL != vrh->current_attribute->attr_trailer)
488 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
489 "%s still to go...\n", vrh->current_attribute->attr_trailer);
491 vrh->lookup_request = GNUNET_GNS_lookup (gns,
493 &vrh->current_attribute->data->subject_key, //issuer_key,
494 GNUNET_GNSRECORD_TYPE_ATTRIBUTE,
495 GNUNET_GNS_LO_DEFAULT,
496 NULL, //shorten_key, always NULL
497 &start_backward_resolution,
499 GNUNET_free (lookup_attr);
505 * Result from GNS lookup.
507 * @param cls the closure (our client lookup handle)
508 * @param rd_count the number of records in @a rd
509 * @param rd the record data
512 handle_credential_query (void* cls,
514 const struct GNUNET_GNSRECORD_Data *rd)
516 struct VerifyRequestHandle *vrh = cls;
517 int cred_record_count;
519 const struct GNUNET_CREDENTIAL_CredentialRecordData *crd;
520 struct CredentialRecordEntry *cr_entry;
522 cred_record_count = 0;
523 for (i=0; i < rd_count; i++)
525 if (GNUNET_GNSRECORD_TYPE_CREDENTIAL != rd[i].record_type)
529 if(GNUNET_OK != GNUNET_CRYPTO_ecdsa_verify(GNUNET_SIGNATURE_PURPOSE_CREDENTIAL,
534 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
535 "Invalid credential found\n");
538 cr_entry = GNUNET_new (struct CredentialRecordEntry);
539 cr_entry->data = GNUNET_malloc (rd[i].data_size);
540 memcpy (cr_entry->data,
543 cr_entry->data_size = rd[i].data_size;
544 GNUNET_CONTAINER_DLL_insert_tail (vrh->cred_chain_head,
545 vrh->cred_chain_tail,
548 if (0 != memcmp (&crd->issuer_key,
550 sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey)))
552 if (0 != strcmp ((char*)&crd[1], vrh->issuer_attribute))
554 vrh->credential = GNUNET_malloc (rd[i].data_size);
555 memcpy (vrh->credential,
558 vrh->credential_size = rd[i].data_size;
559 //Found match prematurely
560 send_lookup_response (vrh);
566 * Check for attributes from the issuer and follow the chain
567 * till you get the required subject's attributes
569 char issuer_attribute_name[strlen (vrh->issuer_attribute)];
570 strcpy (issuer_attribute_name,
571 vrh->issuer_attribute);
572 strcpy (issuer_attribute_name + strlen (vrh->issuer_attribute),
574 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
575 "Looking up %s\n", issuer_attribute_name);
577 //Start with backward resolution
578 GNUNET_GNS_lookup (gns,
579 issuer_attribute_name,
580 &vrh->issuer_key, //issuer_key,
581 GNUNET_GNSRECORD_TYPE_ATTRIBUTE,
582 GNUNET_GNS_LO_DEFAULT,
583 NULL, //shorten_key, always NULL
584 &start_backward_resolution,
590 * Handle Credential verification requests from client
592 * @param cls the closure
593 * @param client the client
594 * @param message the message
597 handle_verify (void *cls,
598 const struct VerifyMessage *v_msg)
600 char attrs[GNUNET_CREDENTIAL_MAX_LENGTH*2 + 1];
601 char issuer_attribute[GNUNET_CREDENTIAL_MAX_LENGTH + 1];
602 char subject_attribute[GNUNET_CREDENTIAL_MAX_LENGTH + 1 + 4];
603 struct VerifyRequestHandle *vrh;
604 struct GNUNET_SERVICE_Client *client = cls;
605 char *attrptr = attrs;
608 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
609 "Received VERIFY message\n");
611 utf_in = (const char *) &v_msg[1];
612 GNUNET_STRINGS_utf8_tolower (utf_in, attrptr);
614 GNUNET_memcpy (issuer_attribute, attrs, ntohs (v_msg->issuer_attribute_len));
615 issuer_attribute[ntohs (v_msg->issuer_attribute_len)] = '\0';
616 GNUNET_memcpy (subject_attribute, attrs+strlen(issuer_attribute), ntohs (v_msg->subject_attribute_len));
617 strcpy (subject_attribute+ntohs (v_msg->subject_attribute_len),
619 subject_attribute[ntohs (v_msg->subject_attribute_len)+4] = '\0';
620 vrh = GNUNET_new (struct VerifyRequestHandle);
621 GNUNET_CONTAINER_DLL_insert (vrh_head, vrh_tail, vrh);
622 vrh->client = client;
623 vrh->request_id = v_msg->id;
624 vrh->issuer_key = v_msg->issuer_key;
625 vrh->subject_key = v_msg->subject_key;
626 vrh->issuer_attribute = GNUNET_strdup (issuer_attribute);
628 if (NULL == subject_attribute)
630 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
631 "No subject attribute provided!\n");
632 send_lookup_response (vrh);
635 if (NULL == issuer_attribute)
637 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
638 "No issuer attribute provided!\n");
639 send_lookup_response (vrh);
643 * First, get attribute from subject
645 vrh->lookup_request = GNUNET_GNS_lookup (gns,
647 &v_msg->subject_key, //subject_pkey,
648 GNUNET_GNSRECORD_TYPE_CREDENTIAL,
649 GNUNET_GNS_LO_DEFAULT,
650 NULL, //shorten_key, always NULL
651 &handle_credential_query,
657 * One of our clients disconnected, clean up after it.
660 * @param client the client that disconnected
663 client_disconnect_cb (void *cls,
664 struct GNUNET_SERVICE_Client *client,
667 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
668 "Client %p disconnected\n",
673 * Add a client to our list of active clients.
676 * @param client client to add
677 * @param mq message queue for @a client
678 * @return this client
681 client_connect_cb (void *cls,
682 struct GNUNET_SERVICE_Client *client,
683 struct GNUNET_MQ_Handle *mq)
685 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
686 "Client %p connected\n",
692 * Process Credential requests.
695 * @param server the initialized server
696 * @param c configuration to use
700 const struct GNUNET_CONFIGURATION_Handle *c,
701 struct GNUNET_SERVICE_Handle *handle)
704 gns = GNUNET_GNS_connect (c);
708 _("Failed to connect to GNS\n"));
711 statistics = GNUNET_STATISTICS_create ("credential", c);
712 GNUNET_SCHEDULER_add_shutdown (&shutdown_task, NULL);
717 * Define "main" method using service macro
721 GNUNET_SERVICE_OPTION_NONE,
724 &client_disconnect_cb,
726 GNUNET_MQ_hd_var_size (verify,
727 GNUNET_MESSAGE_TYPE_CREDENTIAL_VERIFY,
728 struct VerifyMessage,
730 GNUNET_MQ_handler_end());
732 /* end of gnunet-service-credential.c */