-start delegation; cleanup
[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
106 /**
107  * Handle to a lookup operation from api
108  */
109 struct VerifyRequestHandle
110 {
111
112   /**
113    * We keep these in a DLL.
114    */
115   struct VerifyRequestHandle *next;
116
117   /**
118    * We keep these in a DLL.
119    */
120   struct VerifyRequestHandle *prev;
121
122   /**
123    * Handle to the requesting client
124    */
125   struct GNUNET_SERVICE_Client *client;
126
127   /**
128    * Handle to GNS lookup
129    */
130   struct GNUNET_GNS_LookupRequest *lookup_request;
131
132   /**
133    * Issuer public key
134    */
135   struct GNUNET_CRYPTO_EcdsaPublicKey issuer_key;
136   
137   /**
138    * Issuer attribute
139    */
140   char *issuer_attribute;
141
142   /**
143    * Subject public key
144    */
145   struct GNUNET_CRYPTO_EcdsaPublicKey subject_key;
146
147   /**
148    * Credential Chain
149    */
150   struct CredentialRecordEntry *cred_chain_head;
151
152   /**
153    * Credential Chain
154    */
155   struct CredentialRecordEntry *cred_chain_tail;
156
157   /**
158    * Number of chain entries
159    */
160   uint32_t cred_chain_entries;
161
162   /**
163    * Attribute Queue
164    */
165   struct AttributeQueueEntry *attr_queue_head;
166   
167   /**
168    * Attribute Queue
169    */
170   struct AttributeQueueEntry *attr_queue_tail;
171   
172   /**
173    * Current Attribute Pointer
174    */
175   struct AttributeQueueEntry *current_attribute;
176
177   /**
178    * The found credential
179    */
180   struct GNUNET_CREDENTIAL_CredentialRecordData *credential;
181
182   /**
183    * Length of the credential
184    */
185   uint32_t credential_size;
186
187   /**
188    * request id
189    */
190   uint32_t request_id;
191
192 };
193
194
195 /**
196  * Head of the DLL.
197  */
198 static struct VerifyRequestHandle *vrh_head;
199
200 /**
201  * Tail of the DLL.
202  */
203 static struct VerifyRequestHandle *vrh_tail;
204
205 /**
206  * Handle to the statistics service
207  */
208 static struct GNUNET_STATISTICS_Handle *statistics;
209
210
211
212 /**
213  * Handle to GNS service.
214  */
215 static struct GNUNET_GNS_Handle *gns;
216
217 /**
218  * Task run during shutdown.
219  *
220  * @param cls unused
221  * @param tc unused
222  */
223 static void
224 shutdown_task (void *cls)
225 {
226   struct VerifyRequestHandle *vrh;
227   
228   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
229               "Shutting down!\n");
230   while (NULL != (vrh = vrh_head))
231   {
232     //CREDENTIAL_resolver_lookup_cancel (clh->lookup);
233     GNUNET_CONTAINER_DLL_remove (vrh_head,
234                                  vrh_tail,
235                                  vrh);
236     GNUNET_free (vrh);
237   }
238
239   if (NULL != gns)
240   {
241     GNUNET_GNS_disconnect (gns);
242     gns = NULL;
243   }
244   if (NULL != statistics)
245   {
246     GNUNET_STATISTICS_destroy (statistics,
247                                GNUNET_NO);
248     statistics = NULL;
249   }
250   
251 }
252
253 /**
254  * Checks a #GNUNET_MESSAGE_TYPE_CREDENTIAL_VERIFY message
255  *
256  * @param cls client sending the message
257  * @param v_msg message of type `struct VerifyMessage`
258  * @return #GNUNET_OK if @a v_msg is well-formed
259  */
260 static int
261 check_verify (void *cls,
262                     const struct VerifyMessage *v_msg)
263 {
264   size_t msg_size;
265   const char* attrs;
266
267   msg_size = ntohs (v_msg->header.size);
268   if (msg_size < sizeof (struct VerifyMessage))
269   {
270     GNUNET_break (0);
271     return GNUNET_SYSERR;
272   }
273   if ((ntohs (v_msg->issuer_attribute_len) > GNUNET_CREDENTIAL_MAX_LENGTH) ||
274       (ntohs (v_msg->subject_attribute_len) > GNUNET_CREDENTIAL_MAX_LENGTH))
275   {
276     GNUNET_break (0);
277     return GNUNET_SYSERR;
278   }
279   attrs = (const char *) &v_msg[1];
280   
281   if ( ('\0' != attrs[ntohs(v_msg->header.size) - sizeof (struct VerifyMessage) - 1]) ||
282        (strlen (attrs) > GNUNET_CREDENTIAL_MAX_LENGTH * 2) )
283   {
284     GNUNET_break (0);
285     return GNUNET_SYSERR;
286   }
287   return GNUNET_OK;
288 }
289
290 /**
291  * Send.
292  *
293  * @param handle the handle to the request
294  */
295 static void
296 send_lookup_response (struct VerifyRequestHandle *vrh)
297 {
298   size_t len;
299   struct GNUNET_MQ_Envelope *env;
300   struct VerifyResultMessage *rmsg;
301
302   /**
303    * Get serialized record data size
304    */
305   len = vrh->credential_size; //TODO max length of attr
306
307   //TODO add attr chain
308   /**
309    * Prepare a lookup result response message for the client
310    */
311   env = GNUNET_MQ_msg_extra (rmsg,
312                              len,
313                              GNUNET_MESSAGE_TYPE_CREDENTIAL_VERIFY_RESULT);
314   //Assign id so that client can find associated request
315   rmsg->id = vrh->request_id;
316   rmsg->cd_count = htonl (vrh->cred_chain_entries);
317
318   /**
319    * Get serialized record data
320    * Append at the end of rmsg
321    */
322   rmsg->cred_found = htonl (GNUNET_NO);
323   if (NULL != vrh->credential)
324   {
325     memcpy (&rmsg[1],
326             vrh->credential,
327             vrh->credential_size);
328     rmsg->cred_found = htonl (GNUNET_YES);
329   }
330
331   /*char* tmp_entry = (char*)&rmsg[1];
332     for (cr_entry = vrh->cred_chain_head; NULL != cr_entry; cr_entry = cr_entry->next)
333     {
334     memcpy (tmp_entry,
335     &cr_entry->record_data,
336     cr_entry->record_data_size);
337     tmp_entry += cr_entry->record_data_size;
338     }*/
339   GNUNET_MQ_send (GNUNET_SERVICE_client_get_mq(vrh->client),
340                   env);
341
342   GNUNET_CONTAINER_DLL_remove (vrh_head, vrh_tail, vrh);
343
344   /**
345    * TODO:
346    * - Free DLL
347    * - Refactor into cleanup_handle() function for this
348    */
349   GNUNET_free (vrh);
350
351   GNUNET_STATISTICS_update (statistics,
352                             "Completed verifications", 1,
353                             GNUNET_NO);
354 }
355
356
357 static void
358 start_backward_resolution (void* cls,
359                            uint32_t rd_count,
360                            const struct GNUNET_GNSRECORD_Data *rd)
361 {
362   struct VerifyRequestHandle *vrh = cls;
363   struct GNUNET_CREDENTIAL_CredentialRecordData *cred;
364   const struct GNUNET_CREDENTIAL_AttributeRecordData *attr;
365   struct CredentialRecordEntry *cred_pointer;
366   struct AttributeQueueEntry *attr_entry;
367   int i;
368
369   for (i=0; i < rd_count; i++) 
370   {
371     if (GNUNET_GNSRECORD_TYPE_ATTRIBUTE != rd[i].record_type)
372       continue;
373     attr = rd[i].data;
374     for(cred_pointer = vrh->cred_chain_head; cred_pointer != NULL; 
375         cred_pointer = cred_pointer->next){
376       cred = cred_pointer->data;
377       
378       attr_entry = GNUNET_new (struct AttributeQueueEntry);
379
380       attr_entry->data = GNUNET_malloc (rd[i].data_size);
381       memcpy (attr_entry->data,
382               rd[i].data,
383               rd[i].data_size);
384       attr_entry->data_size = rd[i].data_size;
385
386       attr_entry->parent = vrh->current_attribute;
387
388       GNUNET_CONTAINER_DLL_insert (vrh->attr_queue_head,
389                                    vrh->attr_queue_tail,
390                                    attr_entry);
391
392       if(0 != memcmp (&attr->subject_key, 
393                       &cred_pointer->data->issuer_key,
394                       sizeof(struct GNUNET_CRYPTO_EcdsaPublicKey)))
395         continue;
396
397       if (0 != strcmp ((char*)&attr[1], (char*)&cred[1]))
398         continue;
399
400
401       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
402                   "Found issuer\n");
403       vrh->credential = GNUNET_malloc (rd[i].data_size);
404       memcpy (vrh->credential,
405               rd[i].data,
406               rd[i].data_size);
407       vrh->credential_size = rd[i].data_size;
408       //Found match 
409       send_lookup_response (vrh);
410       return;
411
412     }
413   }
414
415
416
417   //Start from next to head
418   vrh->current_attribute = vrh->attr_queue_head;
419
420   if(vrh->current_attribute != NULL)
421   {
422     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
423                 "We are all out of attributes...\n");
424     send_lookup_response (vrh);
425     return;
426   }
427
428   GNUNET_CONTAINER_DLL_remove (vrh->attr_queue_head,
429                                vrh->attr_queue_tail,
430                                vrh->current_attribute);
431
432
433
434   //Start with backward resolution
435   vrh->lookup_request = GNUNET_GNS_lookup (gns,
436                                            (char*)&vrh->current_attribute->data[1],
437                                            &vrh->current_attribute->data->subject_key, //issuer_key,
438                                            GNUNET_GNSRECORD_TYPE_ATTRIBUTE,
439                                            GNUNET_GNS_LO_DEFAULT,
440                                            NULL, //shorten_key, always NULL
441                                            &start_backward_resolution,
442                                            vrh);
443
444
445
446
447 /**
448  * Result from GNS lookup.
449  *
450  * @param cls the closure (our client lookup handle)
451  * @param rd_count the number of records in @a rd
452  * @param rd the record data
453  */
454 static void
455 handle_credential_query (void* cls,
456                          uint32_t rd_count,
457                          const struct GNUNET_GNSRECORD_Data *rd)
458 {
459   struct VerifyRequestHandle *vrh = cls;
460   int cred_record_count;
461   int i;
462   const struct GNUNET_CREDENTIAL_CredentialRecordData *crd;
463   struct CredentialRecordEntry *cr_entry;
464
465   cred_record_count = 0;
466   for (i=0; i < rd_count; i++)
467   {
468     if (GNUNET_GNSRECORD_TYPE_CREDENTIAL != rd[i].record_type)
469       continue;
470     cred_record_count++;
471     crd = rd[i].data;
472     /**
473      * TODO:
474      * Check if we have already found our credential here
475      * If so return success
476      * Else
477      *  Save all found attributes/issues and prepare forward
478      *  resolution of issuer attribute
479      */
480     if(GNUNET_OK != GNUNET_CRYPTO_ecdsa_verify(GNUNET_SIGNATURE_PURPOSE_CREDENTIAL, 
481                                                &crd->purpose,
482                                                &crd->sig,
483                                                &crd->issuer_key))
484     {
485       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
486                   "Invalid credential found\n");
487       continue;
488     }
489     cr_entry = GNUNET_new (struct CredentialRecordEntry);
490     cr_entry->data = GNUNET_malloc (rd[i].data_size);
491     memcpy (cr_entry->data,
492             crd,
493             rd[i].data_size);
494     cr_entry->data_size = rd[i].data_size;
495     GNUNET_CONTAINER_DLL_insert_tail (vrh->cred_chain_head,
496                                       vrh->cred_chain_tail,
497                                       cr_entry);
498
499     if (0 != memcmp (&crd->issuer_key,
500                      &vrh->issuer_key,
501                      sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey)))
502       continue;
503     if (0 != strcmp ((char*)&crd[1], vrh->issuer_attribute))
504       continue;
505     vrh->credential = GNUNET_malloc (rd[i].data_size);
506     memcpy (vrh->credential,
507             rd[i].data,
508             rd[i].data_size);
509     vrh->credential_size = rd[i].data_size;
510     //Found match prematurely
511     send_lookup_response (vrh);
512     return;
513
514   }
515
516   GNUNET_break (0); //TODO remove when implemented
517
518   /**
519    * Check for attributes from the issuer and follow the chain 
520    * till you get the required subject's attributes
521    */
522   char issuer_attribute_name[strlen (vrh->issuer_attribute)];
523   strcpy (issuer_attribute_name,
524           vrh->issuer_attribute);
525   strcpy (issuer_attribute_name + strlen (vrh->issuer_attribute),
526           ".gnu");
527   //Start with backward resolution
528   GNUNET_GNS_lookup (gns,
529                      issuer_attribute_name,
530                      &vrh->issuer_key, //issuer_key,
531                      GNUNET_GNSRECORD_TYPE_ATTRIBUTE,
532                      GNUNET_GNS_LO_DEFAULT,
533                      NULL, //shorten_key, always NULL
534                      &start_backward_resolution,
535                      vrh);
536 }
537
538
539 /**
540  * Handle Credential verification requests from client
541  *
542  * @param cls the closure
543  * @param client the client
544  * @param message the message
545  */
546 static void
547 handle_verify (void *cls,
548                const struct VerifyMessage *v_msg) 
549 {
550   char attrs[GNUNET_CREDENTIAL_MAX_LENGTH*2 + 1];
551   char issuer_attribute[GNUNET_CREDENTIAL_MAX_LENGTH + 1];
552   char subject_attribute[GNUNET_CREDENTIAL_MAX_LENGTH + 1 + 4];
553   struct VerifyRequestHandle *vrh;
554   struct GNUNET_SERVICE_Client *client = cls;
555   char *attrptr = attrs;
556   const char *utf_in;
557
558   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
559               "Received VERIFY message\n");
560
561   utf_in = (const char *) &v_msg[1];
562   GNUNET_STRINGS_utf8_tolower (utf_in, attrptr);
563
564   GNUNET_memcpy (issuer_attribute, attrs, ntohs (v_msg->issuer_attribute_len));
565   issuer_attribute[ntohs (v_msg->issuer_attribute_len)] = '\0';
566   GNUNET_memcpy (subject_attribute, attrs+strlen(issuer_attribute), ntohs (v_msg->subject_attribute_len));
567   strcpy (subject_attribute+ntohs (v_msg->subject_attribute_len),
568           ".gnu");
569   subject_attribute[ntohs (v_msg->subject_attribute_len)+4] = '\0';
570   vrh = GNUNET_new (struct VerifyRequestHandle);
571   GNUNET_CONTAINER_DLL_insert (vrh_head, vrh_tail, vrh);
572   vrh->client = client;
573   vrh->request_id = v_msg->id;
574   vrh->issuer_key = v_msg->issuer_key;
575   vrh->subject_key = v_msg->subject_key;
576   vrh->issuer_attribute = GNUNET_strdup (issuer_attribute);
577
578   if (NULL == subject_attribute)
579   {
580     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 
581                 "No subject attribute provided!\n");
582     send_lookup_response (vrh);
583     return;
584   }
585   if (NULL == issuer_attribute)
586   {
587     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 
588                 "No issuer attribute provided!\n");
589     send_lookup_response (vrh);
590     return;
591   }
592   /**
593    * First, get attribute from subject
594    */
595   vrh->lookup_request = GNUNET_GNS_lookup (gns,
596                                            subject_attribute,
597                                            &v_msg->subject_key, //subject_pkey,
598                                            GNUNET_GNSRECORD_TYPE_CREDENTIAL,
599                                            GNUNET_GNS_LO_DEFAULT,
600                                            NULL, //shorten_key, always NULL
601                                            &handle_credential_query,
602                                            vrh);
603 }
604
605
606 /**
607  * One of our clients disconnected, clean up after it.
608  *
609  * @param cls NULL
610  * @param client the client that disconnected
611  */
612 static void
613 client_disconnect_cb (void *cls,
614                       struct GNUNET_SERVICE_Client *client,
615                       void *app_ctx)
616 {
617   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
618               "Client %p disconnected\n",
619               client);
620 }
621
622 /**
623  * Add a client to our list of active clients.
624  *
625  * @param cls NULL
626  * @param client client to add
627  * @param mq message queue for @a client
628  * @return this client
629  */
630 static void *
631 client_connect_cb (void *cls,
632                    struct GNUNET_SERVICE_Client *client,
633                    struct GNUNET_MQ_Handle *mq)
634 {
635   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
636               "Client %p connected\n",
637               client);
638   return client;
639 }
640
641 /**
642  * Process Credential requests.
643  *
644  * @param cls closure
645  * @param server the initialized server
646  * @param c configuration to use
647  */
648 static void
649 run (void *cls,
650      const struct GNUNET_CONFIGURATION_Handle *c,
651      struct GNUNET_SERVICE_Handle *handle)
652 {
653
654   gns = GNUNET_GNS_connect (c);
655   if (NULL == gns)
656   {
657     fprintf (stderr,
658              _("Failed to connect to GNS\n"));
659   }
660
661   statistics = GNUNET_STATISTICS_create ("credential", c);
662   GNUNET_SCHEDULER_add_shutdown (&shutdown_task, NULL);
663 }
664
665
666 /**
667  * Define "main" method using service macro
668  */
669 GNUNET_SERVICE_MAIN
670 ("credential",
671  GNUNET_SERVICE_OPTION_NONE,
672  &run,
673  &client_connect_cb,
674  &client_disconnect_cb,
675  NULL,
676  GNUNET_MQ_hd_var_size (verify,
677                         GNUNET_MESSAGE_TYPE_CREDENTIAL_VERIFY,
678                         struct VerifyMessage,
679                         NULL),
680  GNUNET_MQ_handler_end());
681
682 /* end of gnunet-service-credential.c */