- add better test for verification
[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 record_data;
66 };
67
68 /**
69  * DLL for attributes - Used as a queue
70  * Insert tail - Pop head
71  */
72 struct AttributeRecordEntry
73 {
74   /**
75    * DLL
76    */
77   struct AttributeRecordEntry *next;
78
79   /**
80    * DLL
81    */
82   struct AttributeRecordEntry *prev;
83
84   /**
85    *
86    */
87   struct GNUNET_CREDENTIAL_AttributeRecordData record_data;
88 };
89
90 /**
91  * Handle to a lookup operation from api
92  */
93 struct VerifyRequestHandle
94 {
95
96   /**
97    * We keep these in a DLL.
98    */
99   struct VerifyRequestHandle *next;
100
101   /**
102    * We keep these in a DLL.
103    */
104   struct VerifyRequestHandle *prev;
105
106   /**
107    * Handle to the requesting client
108    */
109   struct GNUNET_SERVICE_Client *client;
110
111   /**
112    * Handle to GNS lookup
113    */
114   struct GNUNET_GNS_LookupRequest *lookup_request;
115
116   /**
117    * Issuer public key
118    */
119   struct GNUNET_CRYPTO_EcdsaPublicKey issuer_key;
120
121   /**
122    * Subject public key
123    */
124   struct GNUNET_CRYPTO_EcdsaPublicKey subject_key;
125
126   /**
127    * Credential Chain
128    */
129   struct CredentialRecordEntry *cred_chain_head;
130
131   /**
132    * Credential Chain
133    */
134   struct CredentialRecordEntry *cred_chain_tail;
135
136   /**
137    * Attribute Queue
138    */
139   struct AttributeRecordEntry *attr_queue_head;
140   
141   /**
142    * Attribute Queue
143    */
144   struct AttributeRecordEntry *attr_queue_tail;
145
146   /**
147    * request id
148    */
149   uint32_t request_id;
150
151 };
152
153
154 /**
155  * Head of the DLL.
156  */
157 static struct VerifyRequestHandle *vrh_head;
158
159 /**
160  * Tail of the DLL.
161  */
162 static struct VerifyRequestHandle *vrh_tail;
163
164 /**
165  * Handle to the statistics service
166  */
167 static struct GNUNET_STATISTICS_Handle *statistics;
168
169
170
171 /**
172  * Handle to GNS service.
173  */
174 static struct GNUNET_GNS_Handle *gns;
175
176 /**
177  * Task run during shutdown.
178  *
179  * @param cls unused
180  * @param tc unused
181  */
182 static void
183 shutdown_task (void *cls)
184 {
185   struct VerifyRequestHandle *vrh;
186   
187   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
188               "Shutting down!\n");
189   while (NULL != (vrh = vrh_head))
190   {
191     //CREDENTIAL_resolver_lookup_cancel (clh->lookup);
192     GNUNET_CONTAINER_DLL_remove (vrh_head,
193                                  vrh_tail,
194                                  vrh);
195     GNUNET_free (vrh);
196   }
197
198   
199   if (NULL != statistics)
200   {
201     GNUNET_STATISTICS_destroy (statistics,
202                                GNUNET_NO);
203     statistics = NULL;
204   }
205   
206 }
207
208 /**
209  * Checks a #GNUNET_MESSAGE_TYPE_CREDENTIAL_VERIFY message
210  *
211  * @param cls client sending the message
212  * @param v_msg message of type `struct VerifyMessage`
213  * @return #GNUNET_OK if @a v_msg is well-formed
214  */
215 static int
216 check_verify (void *cls,
217                     const struct VerifyMessage *v_msg)
218 {
219   size_t msg_size;
220   const char* attrs;
221
222   msg_size = ntohs (v_msg->header.size);
223   if (msg_size < sizeof (struct VerifyMessage))
224   {
225     GNUNET_break (0);
226     return GNUNET_SYSERR;
227   }
228   if ((ntohs (v_msg->issuer_attribute_len) > GNUNET_CREDENTIAL_MAX_LENGTH) ||
229       (ntohs (v_msg->subject_attribute_len) > GNUNET_CREDENTIAL_MAX_LENGTH))
230   {
231     GNUNET_break (0);
232     return GNUNET_SYSERR;
233   }
234   attrs = (const char *) &v_msg[1];
235   
236   if ( ('\0' != attrs[ntohs(v_msg->header.size) - sizeof (struct VerifyMessage) - 1]) ||
237        (strlen (attrs) > GNUNET_CREDENTIAL_MAX_LENGTH * 2) )
238   {
239     GNUNET_break (0);
240     return GNUNET_SYSERR;
241   }
242   return GNUNET_OK;
243 }
244
245
246 /**
247  * Result from GNS lookup.
248  *
249  * @param cls the closure (our client lookup handle)
250  * @param rd_count the number of records in @a rd
251  * @param rd the record data
252  */
253 static void
254 send_lookup_response (void* cls,
255                       uint32_t rd_count,
256                       const struct GNUNET_GNSRECORD_Data *rd)
257 {
258   struct VerifyRequestHandle *vrh = cls;
259   size_t len;
260   int i;
261   int cred_record_count;
262   struct GNUNET_MQ_Envelope *env;
263   struct VerifyResultMessage *rmsg;
264   const struct GNUNET_CREDENTIAL_CredentialRecordData *crd;
265   struct GNUNET_CRYPTO_EccSignaturePurpose *purp;
266   struct CredentialRecordEntry *cr_entry;
267
268   cred_record_count = 0;
269   for (i=0; i < rd_count; i++)
270   {
271     if (GNUNET_GNSRECORD_TYPE_CREDENTIAL != rd[i].record_type)
272       continue;
273     cred_record_count++;
274     crd = rd[i].data;
275     /**
276      * TODO:
277      * Check if we have already found our credential here
278      * If so return success
279      * Else
280      *  Save all found attributes/issues and prepare forward
281      *  resolution of issuer attribute
282      */
283     cr_entry = GNUNET_new (struct CredentialRecordEntry);
284     cr_entry->record_data = *crd;
285     GNUNET_CONTAINER_DLL_insert_tail (vrh->cred_chain_head,
286                                       vrh->cred_chain_tail,
287                                       cr_entry);
288     purp = GNUNET_malloc (sizeof (struct GNUNET_CRYPTO_EccSignaturePurpose) +
289                           sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey) +
290                           strlen ((char*)&crd[1]) +1 );
291     purp->size = htonl (sizeof (struct GNUNET_CRYPTO_EccSignaturePurpose) +
292                         sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey) +
293                         strlen ((char*)&crd[1]) +1 );
294
295     purp->purpose = htonl (GNUNET_SIGNATURE_PURPOSE_CREDENTIAL);
296     if(GNUNET_OK == GNUNET_CRYPTO_ecdsa_verify(GNUNET_SIGNATURE_PURPOSE_CREDENTIAL, 
297                                                purp,
298                                                &crd->sig,
299                                                &crd->issuer_key))
300     {
301       GNUNET_free (purp);
302       break;
303     }
304     GNUNET_free (purp);
305
306   }
307
308
309
310   /**
311    * TODO
312    * Start resolution of Attribute delegations from issuer
313    *
314    * - Build adequate data structures for attribute(s) to lookup
315    * - Use GNUNET_GNSRECORD_TYPE_XXX
316    * - recursively try to find match(es) with results found top
317    * - return one found credential chain
318    *
319    */
320
321   /**
322    * Get serialized record data size
323    */
324   len = cred_record_count * sizeof (struct GNUNET_CREDENTIAL_CredentialRecordData);
325
326   /**
327    * Prepare a lookup result response message for the client
328    */
329   env = GNUNET_MQ_msg_extra (rmsg,
330                              len,
331                              GNUNET_MESSAGE_TYPE_CREDENTIAL_VERIFY_RESULT);
332   //Assign id so that client can find associated request
333   rmsg->id = vrh->request_id;
334   rmsg->ad_count = htonl (cred_record_count);
335
336   /**
337    * Get serialized record data
338    * Append at the end of rmsg
339    */
340   i = 0;
341   struct GNUNET_CREDENTIAL_CredentialRecordData *tmp_record = (struct GNUNET_CREDENTIAL_CredentialRecordData*) &rmsg[1];
342   for (cr_entry = vrh->cred_chain_head; NULL != cr_entry; cr_entry = cr_entry->next)
343   {
344     memcpy (tmp_record,
345             &cr_entry->record_data,
346             sizeof (struct GNUNET_CREDENTIAL_CredentialRecordData));
347     tmp_record++;
348   }
349   GNUNET_MQ_send (GNUNET_SERVICE_client_get_mq(vrh->client),
350                   env);
351
352   GNUNET_CONTAINER_DLL_remove (vrh_head, vrh_tail, vrh);
353
354   /**
355    * TODO:
356    * - Free DLL
357    * - Refactor into cleanup_handle() function for this
358    */
359   GNUNET_free (vrh);
360
361   GNUNET_STATISTICS_update (statistics,
362                             "Completed verifications", 1,
363                             GNUNET_NO);
364   GNUNET_STATISTICS_update (statistics,
365                             "Credentials resolved",
366                             rd_count,
367                             GNUNET_NO);
368 }
369
370 /**
371  * Handle Credential verification requests from client
372  *
373  * @param cls the closure
374  * @param client the client
375  * @param message the message
376  */
377 static void
378 handle_verify (void *cls,
379                const struct VerifyMessage *v_msg) 
380 {
381   char attrs[GNUNET_CREDENTIAL_MAX_LENGTH*2 + 1];
382   char issuer_attribute[GNUNET_CREDENTIAL_MAX_LENGTH + 1];
383   char subject_attribute[GNUNET_CREDENTIAL_MAX_LENGTH + 1];
384   struct VerifyRequestHandle *vrh;
385   struct GNUNET_SERVICE_Client *client = cls;
386   char *attrptr = attrs;
387   const char *utf_in;
388
389   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
390               "Received VERIFY message\n");
391
392   utf_in = (const char *) &v_msg[1];
393   GNUNET_STRINGS_utf8_tolower (utf_in, attrptr);
394
395   GNUNET_memcpy (issuer_attribute, attrs, ntohs (v_msg->issuer_attribute_len));
396   issuer_attribute[ntohs (v_msg->issuer_attribute_len)] = '\0';
397   GNUNET_memcpy (subject_attribute, attrs+strlen(issuer_attribute), ntohs (v_msg->subject_attribute_len));
398   subject_attribute[ntohs (v_msg->subject_attribute_len)] = '\0';
399   vrh = GNUNET_new (struct VerifyRequestHandle);
400   GNUNET_CONTAINER_DLL_insert (vrh_head, vrh_tail, vrh);
401   vrh->client = client;
402   vrh->request_id = v_msg->id;
403   vrh->issuer_key = v_msg->issuer_key;
404   vrh->subject_key = v_msg->subject_key;
405
406   if (NULL == subject_attribute)
407   {
408     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 
409                 "No subject attribute provided!\n");
410     send_lookup_response (vrh, 0, NULL);
411     return;
412   }
413   if (NULL == issuer_attribute)
414   {
415     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 
416                 "No issuer attribute provided!\n");
417     send_lookup_response (vrh, 0, NULL);
418     return;
419   }
420   /**
421    * First, get attribute from subject
422    */
423   vrh->lookup_request = GNUNET_GNS_lookup (gns,
424                                            subject_attribute,
425                                            &v_msg->subject_key, //subject_pkey,
426                                            GNUNET_GNSRECORD_TYPE_CREDENTIAL,
427                                            GNUNET_GNS_LO_DEFAULT,
428                                            NULL, //shorten_key, always NULL
429                                            &send_lookup_response,
430                                            vrh);
431 }
432
433
434 /**
435  * One of our clients disconnected, clean up after it.
436  *
437  * @param cls NULL
438  * @param client the client that disconnected
439  */
440 static void
441 client_disconnect_cb (void *cls,
442                       struct GNUNET_SERVICE_Client *client,
443                       void *app_ctx)
444 {
445   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
446               "Client %p disconnected\n",
447               client);
448 }
449
450 /**
451  * Add a client to our list of active clients.
452  *
453  * @param cls NULL
454  * @param client client to add
455  * @param mq message queue for @a client
456  * @return this client
457  */
458 static void *
459 client_connect_cb (void *cls,
460                    struct GNUNET_SERVICE_Client *client,
461                    struct GNUNET_MQ_Handle *mq)
462 {
463   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
464               "Client %p connected\n",
465               client);
466   return client;
467 }
468
469 /**
470  * Process Credential requests.
471  *
472  * @param cls closure
473  * @param server the initialized server
474  * @param c configuration to use
475  */
476 static void
477 run (void *cls,
478      const struct GNUNET_CONFIGURATION_Handle *c,
479      struct GNUNET_SERVICE_Handle *handle)
480 {
481
482   gns = GNUNET_GNS_connect (c);
483   if (NULL == gns)
484   {
485     fprintf (stderr,
486              _("Failed to connect to GNS\n"));
487   }
488
489   statistics = GNUNET_STATISTICS_create ("credential", c);
490   GNUNET_SCHEDULER_add_shutdown (&shutdown_task, NULL);
491 }
492
493
494 /**
495  * Define "main" method using service macro
496  */
497 GNUNET_SERVICE_MAIN
498 ("credential",
499  GNUNET_SERVICE_OPTION_NONE,
500  &run,
501  &client_connect_cb,
502  &client_disconnect_cb,
503  NULL,
504  GNUNET_MQ_hd_var_size (verify,
505                         GNUNET_MESSAGE_TYPE_CREDENTIAL_VERIFY,
506                         struct VerifyMessage,
507                         NULL),
508  GNUNET_MQ_handler_end());
509
510 /* end of gnunet-service-credential.c */