- fix test
[oweals/gnunet.git] / src / credential / gnunet-service-credential.c
1 /*
2      This file is part of GNUnet.
3      Copyright (C) 2011-2013 GNUnet e.V.
4
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.
9
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.
14
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.
19 */
20 /**
21  * @file gns/gnunet-service-credential.c
22  * @brief GNU Credential Service (main service)
23  * @author Adnan Husain 
24  */
25 #include "platform.h"
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"
32
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"
40
41
42
43
44 #define GNUNET_CREDENTIAL_MAX_LENGTH 255
45
46 /**
47  * DLL for record
48  */
49 struct CredentialRecordEntry
50 {
51   /**
52    * DLL
53    */
54   struct CredentialRecordEntry *next;
55
56   /**
57    * DLL
58    */
59   struct CredentialRecordEntry *prev;
60
61
62   /**
63    * Payload
64    */
65   struct GNUNET_CREDENTIAL_CredentialRecordData *data;
66
67   /**
68    * Size
69    */
70   uint64_t data_size;
71 };
72
73 /**
74  * DLL for attributes - Used as a queue
75  * Insert tail - Pop head
76  */
77 struct AttributeQueueEntry
78 {
79   /**
80    * DLL
81    */
82   struct AttributeQueueEntry *next;
83
84   /**
85    * DLL
86    */
87   struct AttributeQueueEntry *prev;
88
89   /**
90    * Payload
91    */
92   struct GNUNET_CREDENTIAL_AttributeRecordData *data;
93
94   /**
95    * Size
96    */
97   uint64_t data_size;
98
99   /**
100    * Parent attribute delegation
101    */
102   struct AttributeQueueEntry *parent;
103
104   /**
105    * Trailing attribute context
106    */
107   char *attr_trailer;
108 };
109
110
111 /**
112  * Handle to a lookup operation from api
113  */
114 struct VerifyRequestHandle
115 {
116
117   /**
118    * We keep these in a DLL.
119    */
120   struct VerifyRequestHandle *next;
121
122   /**
123    * We keep these in a DLL.
124    */
125   struct VerifyRequestHandle *prev;
126
127   /**
128    * Handle to the requesting client
129    */
130   struct GNUNET_SERVICE_Client *client;
131
132   /**
133    * Handle to GNS lookup
134    */
135   struct GNUNET_GNS_LookupRequest *lookup_request;
136
137   /**
138    * Issuer public key
139    */
140   struct GNUNET_CRYPTO_EcdsaPublicKey issuer_key;
141   
142   /**
143    * Issuer attribute
144    */
145   char *issuer_attribute;
146
147   /**
148    * Subject public key
149    */
150   struct GNUNET_CRYPTO_EcdsaPublicKey subject_key;
151
152   /**
153    * Credential Chain
154    */
155   struct CredentialRecordEntry *cred_chain_head;
156
157   /**
158    * Credential Chain
159    */
160   struct CredentialRecordEntry *cred_chain_tail;
161
162   /**
163    * Number of chain entries
164    */
165   uint32_t cred_chain_entries;
166
167   /**
168    * Attribute Queue
169    */
170   struct AttributeQueueEntry *attr_queue_head;
171   
172   /**
173    * Attribute Queue
174    */
175   struct AttributeQueueEntry *attr_queue_tail;
176   
177   /**
178    * Current Attribute Pointer
179    */
180   struct AttributeQueueEntry *current_attribute;
181
182   /**
183    * The found credential
184    */
185   struct GNUNET_CREDENTIAL_CredentialRecordData *credential;
186
187   /**
188    * Length of the credential
189    */
190   uint32_t credential_size;
191
192   /**
193    * request id
194    */
195   uint32_t request_id;
196
197 };
198
199
200 /**
201  * Head of the DLL.
202  */
203 static struct VerifyRequestHandle *vrh_head;
204
205 /**
206  * Tail of the DLL.
207  */
208 static struct VerifyRequestHandle *vrh_tail;
209
210 /**
211  * Handle to the statistics service
212  */
213 static struct GNUNET_STATISTICS_Handle *statistics;
214
215
216
217 /**
218  * Handle to GNS service.
219  */
220 static struct GNUNET_GNS_Handle *gns;
221
222 /**
223  * Task run during shutdown.
224  *
225  * @param cls unused
226  * @param tc unused
227  */
228 static void
229 shutdown_task (void *cls)
230 {
231   struct VerifyRequestHandle *vrh;
232   
233   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
234               "Shutting down!\n");
235   while (NULL != (vrh = vrh_head))
236   {
237     //CREDENTIAL_resolver_lookup_cancel (clh->lookup);
238     GNUNET_CONTAINER_DLL_remove (vrh_head,
239                                  vrh_tail,
240                                  vrh);
241     GNUNET_free (vrh);
242   }
243
244   if (NULL != gns)
245   {
246     GNUNET_GNS_disconnect (gns);
247     gns = NULL;
248   }
249   if (NULL != statistics)
250   {
251     GNUNET_STATISTICS_destroy (statistics,
252                                GNUNET_NO);
253     statistics = NULL;
254   }
255   
256 }
257
258 /**
259  * Checks a #GNUNET_MESSAGE_TYPE_CREDENTIAL_VERIFY message
260  *
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
264  */
265 static int
266 check_verify (void *cls,
267                     const struct VerifyMessage *v_msg)
268 {
269   size_t msg_size;
270   const char* attrs;
271
272   msg_size = ntohs (v_msg->header.size);
273   if (msg_size < sizeof (struct VerifyMessage))
274   {
275     GNUNET_break (0);
276     return GNUNET_SYSERR;
277   }
278   if ((ntohs (v_msg->issuer_attribute_len) > GNUNET_CREDENTIAL_MAX_LENGTH) ||
279       (ntohs (v_msg->subject_attribute_len) > GNUNET_CREDENTIAL_MAX_LENGTH))
280   {
281     GNUNET_break (0);
282     return GNUNET_SYSERR;
283   }
284   attrs = (const char *) &v_msg[1];
285   
286   if ( ('\0' != attrs[ntohs(v_msg->header.size) - sizeof (struct VerifyMessage) - 1]) ||
287        (strlen (attrs) > GNUNET_CREDENTIAL_MAX_LENGTH * 2) )
288   {
289     GNUNET_break (0);
290     return GNUNET_SYSERR;
291   }
292   return GNUNET_OK;
293 }
294
295 /**
296  * Send.
297  *
298  * @param handle the handle to the request
299  */
300 static void
301 send_lookup_response (struct VerifyRequestHandle *vrh)
302 {
303   size_t len;
304   struct GNUNET_MQ_Envelope *env;
305   struct VerifyResultMessage *rmsg;
306
307   /**
308    * Get serialized record data size
309    */
310   len = vrh->credential_size; //TODO max length of attr
311
312   //TODO add attr chain
313   /**
314    * Prepare a lookup result response message for the client
315    */
316   env = GNUNET_MQ_msg_extra (rmsg,
317                              len,
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);
322
323   /**
324    * Get serialized record data
325    * Append at the end of rmsg
326    */
327   rmsg->cred_found = htonl (GNUNET_NO);
328   if (NULL != vrh->credential)
329   {
330     memcpy (&rmsg[1],
331             vrh->credential,
332             vrh->credential_size);
333     rmsg->cred_found = htonl (GNUNET_YES);
334   }
335
336   /*char* tmp_entry = (char*)&rmsg[1];
337     for (cr_entry = vrh->cred_chain_head; NULL != cr_entry; cr_entry = cr_entry->next)
338     {
339     memcpy (tmp_entry,
340     &cr_entry->record_data,
341     cr_entry->record_data_size);
342     tmp_entry += cr_entry->record_data_size;
343     }*/
344   GNUNET_MQ_send (GNUNET_SERVICE_client_get_mq(vrh->client),
345                   env);
346
347   GNUNET_CONTAINER_DLL_remove (vrh_head, vrh_tail, vrh);
348
349   /**
350    * TODO:
351    * - Free DLL
352    * - Refactor into cleanup_handle() function for this
353    */
354   GNUNET_free (vrh);
355
356   GNUNET_STATISTICS_update (statistics,
357                             "Completed verifications", 1,
358                             GNUNET_NO);
359 }
360
361
362 static void
363 start_backward_resolution (void* cls,
364                            uint32_t rd_count,
365                            const struct GNUNET_GNSRECORD_Data *rd)
366 {
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;
372   char *expanded_attr;
373   char *check_attr;
374   int i;
375   
376   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
377               "Got %d attrs\n", rd_count);
378
379   for (i=0; i < rd_count; i++) 
380   {
381     if (GNUNET_GNSRECORD_TYPE_ATTRIBUTE != rd[i].record_type)
382       continue;
383
384     attr = rd[i].data;
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)
389     {
390       if (rd[i].data_size == sizeof (struct GNUNET_CREDENTIAL_AttributeRecordData))
391       {
392         GNUNET_asprintf (&expanded_attr,
393                          "%s",
394                          vrh->current_attribute->attr_trailer);
395
396       } else {
397         GNUNET_asprintf (&expanded_attr,
398                          "%s.%s",
399                          (char*)&attr[1],
400                          vrh->current_attribute->attr_trailer);
401       }
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;
405     } else {
406       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
407                   "Not Expanding %s\n", (char*)&attr[1]);
408     }
409     attr_entry->data = GNUNET_malloc (attr_entry->data_size);
410     memcpy (attr_entry->data,
411             rd[i].data,
412             rd[i].data_size);
413     if (NULL != vrh->current_attribute && NULL != vrh->current_attribute->attr_trailer)
414     {
415       memcpy ((char*)&attr_entry->data[1],
416               expanded_attr,
417               strlen (expanded_attr));
418     } 
419     check_attr = (char*)&attr_entry->data[1];
420     check_attr[attr_entry->data_size] = '\0';
421     attr_entry->parent = vrh->current_attribute;
422
423     GNUNET_CONTAINER_DLL_insert (vrh->attr_queue_head,
424                                  vrh->attr_queue_tail,
425                                  attr_entry);
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)))
432         continue;
433       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
434                   "Checking if %s matches %s\n",
435                   (char*)&attr_entry->data[1], (char*)&cred[1]);
436
437       if (0 != strcmp ((char*)&attr_entry->data[1], (char*)&cred[1]))
438         continue;
439
440       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
441                   "Found issuer\n");
442       vrh->credential = GNUNET_malloc (rd[i].data_size);
443       memcpy (vrh->credential,
444               rd[i].data,
445               rd[i].data_size);
446       vrh->credential_size = rd[i].data_size;
447       //Found match 
448       send_lookup_response (vrh);
449       return;
450
451     }
452   }
453
454
455
456   //Start from next to head
457   vrh->current_attribute = vrh->attr_queue_head;
458
459   if(NULL == vrh->current_attribute)
460   {
461     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
462                 "We are all out of attributes...\n");
463     send_lookup_response (vrh);
464     return;
465   }
466
467   GNUNET_CONTAINER_DLL_remove (vrh->attr_queue_head,
468                                vrh->attr_queue_tail,
469                                vrh->current_attribute);
470
471
472
473   //Start with backward resolution
474   char issuer_attribute_name[strlen ((char*)&vrh->current_attribute->data[1])];
475   char *lookup_attr;
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,
480                    "%s.gnu",
481                    next_attr);
482   next_attr += strlen (next_attr) + 1;
483   vrh->current_attribute->attr_trailer = GNUNET_strdup (next_attr);
484
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);
490
491   vrh->lookup_request = GNUNET_GNS_lookup (gns,
492                                            lookup_attr,
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,
498                                            vrh);
499   GNUNET_free (lookup_attr);
500
501
502
503
504 /**
505  * Result from GNS lookup.
506  *
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
510  */
511 static void
512 handle_credential_query (void* cls,
513                          uint32_t rd_count,
514                          const struct GNUNET_GNSRECORD_Data *rd)
515 {
516   struct VerifyRequestHandle *vrh = cls;
517   int cred_record_count;
518   int i;
519   const struct GNUNET_CREDENTIAL_CredentialRecordData *crd;
520   struct CredentialRecordEntry *cr_entry;
521
522   cred_record_count = 0;
523   for (i=0; i < rd_count; i++)
524   {
525     if (GNUNET_GNSRECORD_TYPE_CREDENTIAL != rd[i].record_type)
526       continue;
527     cred_record_count++;
528     crd = rd[i].data;
529     if(GNUNET_OK != GNUNET_CRYPTO_ecdsa_verify(GNUNET_SIGNATURE_PURPOSE_CREDENTIAL, 
530                                                &crd->purpose,
531                                                &crd->sig,
532                                                &crd->issuer_key))
533     {
534       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
535                   "Invalid credential found\n");
536       continue;
537     }
538     cr_entry = GNUNET_new (struct CredentialRecordEntry);
539     cr_entry->data = GNUNET_malloc (rd[i].data_size);
540     memcpy (cr_entry->data,
541             crd,
542             rd[i].data_size);
543     cr_entry->data_size = rd[i].data_size;
544     GNUNET_CONTAINER_DLL_insert_tail (vrh->cred_chain_head,
545                                       vrh->cred_chain_tail,
546                                       cr_entry);
547
548     if (0 != memcmp (&crd->issuer_key,
549                      &vrh->issuer_key,
550                      sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey)))
551       continue;
552     if (0 != strcmp ((char*)&crd[1], vrh->issuer_attribute))
553       continue;
554     vrh->credential = GNUNET_malloc (rd[i].data_size);
555     memcpy (vrh->credential,
556             rd[i].data,
557             rd[i].data_size);
558     vrh->credential_size = rd[i].data_size;
559     //Found match prematurely
560     send_lookup_response (vrh);
561     return;
562
563   }
564
565   /**
566    * Check for attributes from the issuer and follow the chain 
567    * till you get the required subject's attributes
568    */
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),
573           ".gnu");
574   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
575               "Looking up %s\n", issuer_attribute_name);
576
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,
585                      vrh);
586 }
587
588
589 /**
590  * Handle Credential verification requests from client
591  *
592  * @param cls the closure
593  * @param client the client
594  * @param message the message
595  */
596 static void
597 handle_verify (void *cls,
598                const struct VerifyMessage *v_msg) 
599 {
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;
606   const char *utf_in;
607
608   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
609               "Received VERIFY message\n");
610
611   utf_in = (const char *) &v_msg[1];
612   GNUNET_STRINGS_utf8_tolower (utf_in, attrptr);
613
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),
618           ".gnu");
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);
627
628   if (NULL == subject_attribute)
629   {
630     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 
631                 "No subject attribute provided!\n");
632     send_lookup_response (vrh);
633     return;
634   }
635   if (NULL == issuer_attribute)
636   {
637     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 
638                 "No issuer attribute provided!\n");
639     send_lookup_response (vrh);
640     return;
641   }
642   /**
643    * First, get attribute from subject
644    */
645   vrh->lookup_request = GNUNET_GNS_lookup (gns,
646                                            subject_attribute,
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,
652                                            vrh);
653 }
654
655
656 /**
657  * One of our clients disconnected, clean up after it.
658  *
659  * @param cls NULL
660  * @param client the client that disconnected
661  */
662 static void
663 client_disconnect_cb (void *cls,
664                       struct GNUNET_SERVICE_Client *client,
665                       void *app_ctx)
666 {
667   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
668               "Client %p disconnected\n",
669               client);
670 }
671
672 /**
673  * Add a client to our list of active clients.
674  *
675  * @param cls NULL
676  * @param client client to add
677  * @param mq message queue for @a client
678  * @return this client
679  */
680 static void *
681 client_connect_cb (void *cls,
682                    struct GNUNET_SERVICE_Client *client,
683                    struct GNUNET_MQ_Handle *mq)
684 {
685   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
686               "Client %p connected\n",
687               client);
688   return client;
689 }
690
691 /**
692  * Process Credential requests.
693  *
694  * @param cls closure
695  * @param server the initialized server
696  * @param c configuration to use
697  */
698 static void
699 run (void *cls,
700      const struct GNUNET_CONFIGURATION_Handle *c,
701      struct GNUNET_SERVICE_Handle *handle)
702 {
703
704   gns = GNUNET_GNS_connect (c);
705   if (NULL == gns)
706   {
707     fprintf (stderr,
708              _("Failed to connect to GNS\n"));
709   }
710
711   statistics = GNUNET_STATISTICS_create ("credential", c);
712   GNUNET_SCHEDULER_add_shutdown (&shutdown_task, NULL);
713 }
714
715
716 /**
717  * Define "main" method using service macro
718  */
719 GNUNET_SERVICE_MAIN
720 ("credential",
721  GNUNET_SERVICE_OPTION_NONE,
722  &run,
723  &client_connect_cb,
724  &client_disconnect_cb,
725  NULL,
726  GNUNET_MQ_hd_var_size (verify,
727                         GNUNET_MESSAGE_TYPE_CREDENTIAL_VERIFY,
728                         struct VerifyMessage,
729                         NULL),
730  GNUNET_MQ_handler_end());
731
732 /* end of gnunet-service-credential.c */