-remove trailing whitespace
[oweals/gnunet.git] / src / revocation / gnunet-service-revocation.c
1 /*
2   This file is part of GNUnet.
3   (C) 2013 Christian Grothoff (and other contributing authors)
4
5   GNUnet is free software; you can redistribute it and/or modify
6   it under the terms of the GNU General Public Licerevocation 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 Licerevocation for more details.
14
15   You should have received a copy of the GNU General Public Licerevocation
16   along with GNUnet; see the file COPYING.  If not, write to the
17   Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18   Boston, MA 02111-1307, USA.
19  */
20
21 /**
22  * @file revocation/gnunet-service-revocation.c
23  * @brief key revocation service
24  * @author Christian Grothoff
25  *
26  * The purpose of this service is to allow users to permanently revoke
27  * (compromised) keys.  This is done by flooding the network with the
28  * revocation requests.  To reduce the attack potential offered by such
29  * flooding, revocations must include a proof of work.  We use the
30  * set service for efficiently computing the union of revocations of
31  * peers that connect.
32  *
33  * TODO:
34  * - broadcast p2p revocations
35  * - handle p2p connect (trigger SET union)
36  * - optimization: avoid sending revocation back to peer that we got it from;
37  * - optimization: have randomized delay in sending revocations to other peers
38  *                 to make it rare to traverse each link twice (NSE-style)
39  */
40 #include "platform.h"
41 #include <math.h>
42 #include "gnunet_util_lib.h"
43 #include "gnunet_constants.h"
44 #include "gnunet_protocols.h"
45 #include "gnunet_signatures.h"
46 #include "gnunet_statistics_service.h"
47 #include "gnunet_core_service.h"
48 #include "gnunet_revocation_service.h"
49 #include "gnunet_set_service.h"
50 #include "revocation.h"
51 #include <gcrypt.h>
52
53
54 /**
55  * Per-peer information.
56  */
57 struct PeerEntry
58 {
59
60   /**
61    * Core handle for sending messages to this peer.
62    */
63   struct GNUNET_CORE_TransmitHandle *th;
64
65   /**
66    * What is the identity of the peer?
67    */
68   struct GNUNET_PeerIdentity id;
69
70   /**
71    * Task scheduled to send message to this peer.
72    */
73   GNUNET_SCHEDULER_TaskIdentifier transmit_task;
74
75 };
76
77
78 /**
79  * Set from all revocations known to us.
80  */
81 static struct GNUNET_SET_Handle *revocation_set;
82
83 /**
84  * Hash map with all revoked keys, maps the hash of the public key
85  * to the respective `struct RevokeMessage`.
86  */
87 static struct GNUNET_CONTAINER_MultiHashMap *revocation_map;
88
89 /**
90  * Handle to our current configuration.
91  */
92 static const struct GNUNET_CONFIGURATION_Handle *cfg;
93
94 /**
95  * Handle to the statistics service.
96  */
97 static struct GNUNET_STATISTICS_Handle *stats;
98
99 /**
100  * Handle to the core service (for flooding)
101  */
102 static struct GNUNET_CORE_Handle *coreAPI;
103
104 /**
105  * Map of all connected peers.
106  */
107 static struct GNUNET_CONTAINER_MultiPeerMap *peers;
108
109 /**
110  * The peer identity of this peer.
111  */
112 static struct GNUNET_PeerIdentity my_identity;
113
114 /**
115  * Handle to this serivce's server.
116  */
117 static struct GNUNET_SERVER_Handle *srv;
118
119 /**
120  * Notification context for convenient sending of replies to the clients.
121  */
122 static struct GNUNET_SERVER_NotificationContext *nc;
123
124 /**
125  * File handle for the revocation database.
126  */
127 static struct GNUNET_DISK_FileHandle *revocation_db;
128
129 /**
130  * Amount of work required (W-bit collisions) for REVOCATION proofs, in collision-bits.
131  */
132 static unsigned long long revocation_work_required;
133
134
135 /**
136  * An revoke message has been received, check that it is well-formed.
137  *
138  * @param rm the message to verify
139  * @return #GNUNET_YES if the message is verified
140  *         #GNUNET_NO if the key/signature don't verify
141  */
142 static int
143 verify_revoke_message (const struct RevokeMessage *rm)
144 {
145   if (GNUNET_YES !=
146       GNUNET_REVOCATION_check_pow (&rm->public_key,
147                                    rm->proof_of_work,
148                                    (unsigned int) revocation_work_required))
149   {
150     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
151                 "Proof of work invalid: %llu!\n",
152                 (unsigned long long)
153                 GNUNET_ntohll (rm->proof_of_work));
154     GNUNET_break_op (0);
155     return GNUNET_NO;
156   }
157   if (GNUNET_OK !=
158       GNUNET_CRYPTO_ecc_verify (GNUNET_SIGNATURE_PURPOSE_REVOCATION,
159                                 &rm->purpose,
160                                 &rm->signature,
161                                 &rm->public_key))
162   {
163     GNUNET_break_op (0);
164     return GNUNET_NO;
165   }
166   return GNUNET_YES;
167 }
168
169
170 /**
171  * Handle QUERY message from client.
172  *
173  * @param cls unused
174  * @param client who sent the message
175  * @param message the message received
176  */
177 static void
178 handle_query_message (void *cls,
179                       struct GNUNET_SERVER_Client *client,
180                       const struct GNUNET_MessageHeader *message)
181 {
182   const struct QueryMessage *qm = (const struct QueryMessage *) message;
183   struct QueryResponseMessage qrm;
184   struct GNUNET_HashCode hc;
185   int res;
186
187   GNUNET_CRYPTO_hash (&qm->key,
188                       sizeof (struct GNUNET_CRYPTO_EccPublicSignKey),
189                       &hc);
190   res = GNUNET_CONTAINER_multihashmap_contains (revocation_map, &hc);
191   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
192               (GNUNET_NO == res)
193               ? "Received revocation check for valid key `%s' from client\n"
194               : "Received revocation check for revoked key `%s' from client\n",
195               GNUNET_h2s (&hc));
196   qrm.header.size = htons (sizeof (struct QueryResponseMessage));
197   qrm.header.type = htons (GNUNET_MESSAGE_TYPE_REVOCATION_QUERY_RESPONSE);
198   qrm.is_valid = htons ((GNUNET_YES == res) ? GNUNET_NO : GNUNET_YES);
199   GNUNET_SERVER_notification_context_add (nc,
200                                           client);
201   GNUNET_SERVER_notification_context_unicast (nc,
202                                               client,
203                                               &qrm.header,
204                                               GNUNET_NO);
205   GNUNET_SERVER_receive_done (client, GNUNET_OK);
206 }
207
208
209 /**
210  * Flood the given revocation message to all neighbours.
211  *
212  * @param cls the `struct RevokeMessage` to flood
213  * @param target a neighbour
214  * @param value our `struct PeerEntry` for the neighbour
215  * @return #GNUNET_OK (continue to iterate)
216  */
217 static int
218 do_flood (void *cls,
219           const struct GNUNET_PeerIdentity *target,
220           void *value)
221 {
222   GNUNET_break (0); // FIXME: not implemented
223   return GNUNET_OK;
224 }
225
226
227 /**
228  * Publicize revocation message.   Stores the message locally in the
229  * database and passes it to all connected neighbours (and adds it to
230  * the set for future connections).
231  *
232  * @param rm message to publicize
233  * @return #GNUNET_OK on success, #GNUNET_NO if we encountered an error,
234  *         #GNUNET_SYSERR if the message was malformed
235  */
236 static int
237 publicize_rm (const struct RevokeMessage *rm)
238 {
239   struct RevokeMessage *cp;
240   struct GNUNET_HashCode hc;
241   struct GNUNET_SET_Element e;
242
243   GNUNET_CRYPTO_hash (&rm->public_key,
244                       sizeof (struct GNUNET_CRYPTO_EccPublicSignKey),
245                       &hc);
246   if (GNUNET_YES ==
247       GNUNET_CONTAINER_multihashmap_contains (revocation_map,
248                                               &hc))
249   {
250     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
251                 _("Duplicate revocation received from peer. Ignored.\n"));
252     return GNUNET_OK;
253   }
254   if (GNUNET_OK !=
255       verify_revoke_message (rm))
256   {
257     GNUNET_break_op (0);
258     return GNUNET_SYSERR;
259   }
260   /* write to disk */
261   if (sizeof (struct RevokeMessage) !=
262       GNUNET_DISK_file_write (revocation_db,
263                               rm,
264                               sizeof (struct RevokeMessage)))
265   {
266     GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
267                          "write");
268     return GNUNET_NO;
269   }
270   if (GNUNET_OK !=
271       GNUNET_DISK_file_sync (revocation_db))
272   {
273     GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
274                          "sync");
275     return GNUNET_NO;
276   }
277   /* keep copy in memory */
278   cp = (struct RevokeMessage *) GNUNET_copy_message (&rm->header);
279   GNUNET_break (GNUNET_OK ==
280                 GNUNET_CONTAINER_multihashmap_put (revocation_map,
281                                                    &hc,
282                                                    cp,
283                                                    GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
284   /* add to set for future connections */
285   e.size = htons (rm->header.size);
286   e.type = 0;
287   e.data = rm;
288   if (GNUNET_OK !=
289       GNUNET_SET_add_element (revocation_set,
290                               &e,
291                               NULL, NULL))
292   {
293     GNUNET_break (0);
294     return GNUNET_OK;
295   }
296   /* flood to neighbours */
297   GNUNET_CONTAINER_multipeermap_iterate (peers,
298                                          &do_flood,
299                                          cp);
300   return GNUNET_OK;
301 }
302
303
304 /**
305  * Handle REVOKE message from client.
306  *
307  * @param cls unused
308  * @param client who sent the message
309  * @param message the message received
310  */
311 static void
312 handle_revoke_message (void *cls,
313                        struct GNUNET_SERVER_Client *client,
314                        const struct GNUNET_MessageHeader *message)
315 {
316   const struct RevokeMessage *rm;
317   struct RevocationResponseMessage rrm;
318   int ret;
319
320   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
321               "Received REVOKE message from client\n");
322   rm = (const struct RevokeMessage *) message;
323   if (GNUNET_SYSERR == (ret = publicize_rm (rm)))
324   {
325     GNUNET_break_op (0);
326     GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
327     return;
328   }
329   rrm.header.size = htons (sizeof (struct RevocationResponseMessage));
330   rrm.header.type = htons (GNUNET_MESSAGE_TYPE_REVOCATION_REVOKE_RESPONSE);
331   rrm.is_valid = htons ((GNUNET_OK == ret) ? GNUNET_NO : GNUNET_YES);
332   GNUNET_SERVER_notification_context_add (nc,
333                                           client);
334   GNUNET_SERVER_notification_context_unicast (nc,
335                                               client,
336                                               &rrm.header,
337                                               GNUNET_NO);
338   GNUNET_SERVER_receive_done (client, GNUNET_OK);
339 }
340
341
342 /**
343  * Core handler for flooded revocation messages.
344  *
345  * @param cls closure unused
346  * @param message message
347  * @param peer peer identity this message is from (ignored)
348  */
349 static int
350 handle_p2p_revoke_message (void *cls,
351                            const struct GNUNET_PeerIdentity *peer,
352                            const struct GNUNET_MessageHeader *message)
353 {
354   const struct RevokeMessage *rm;
355
356   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
357               "Received REVOKE message from peer\n");
358   rm = (const struct RevokeMessage *) message;
359   GNUNET_break_op (GNUNET_SYSERR != publicize_rm (rm));
360   return GNUNET_OK;
361 }
362
363
364 /**
365  * Method called whenever a peer connects. Sets up the PeerEntry and
366  * schedules the initial revocation set exchange with this peer.
367  *
368  * @param cls closure
369  * @param peer peer identity this notification is about
370  */
371 static void
372 handle_core_connect (void *cls,
373                      const struct GNUNET_PeerIdentity *peer)
374 {
375   struct PeerEntry *peer_entry;
376
377   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Peer `%s' connected to us\n",
378               GNUNET_i2s (peer));
379   peer_entry = GNUNET_new (struct PeerEntry);
380   peer_entry->id = *peer;
381   GNUNET_assert (GNUNET_OK ==
382                  GNUNET_CONTAINER_multipeermap_put (peers, peer,
383                                                     peer_entry,
384                                                     GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
385   GNUNET_break (0); // FIXME: implement revocation set union on connect!
386 #if 0
387   peer_entry->transmit_task =
388       GNUNET_SCHEDULER_add_delayed (get_transmit_delay (-1), &transmit_task_cb,
389                                     peer_entry);
390 #endif
391   GNUNET_STATISTICS_update (stats, "# peers connected", 1, GNUNET_NO);
392 }
393
394
395 /**
396  * Method called whenever a peer disconnects. Deletes the PeerEntry and cancels
397  * any pending transmission requests to that peer.
398  *
399  * @param cls closure
400  * @param peer peer identity this notification is about
401  */
402 static void
403 handle_core_disconnect (void *cls,
404                         const struct GNUNET_PeerIdentity *peer)
405 {
406   struct PeerEntry *pos;
407
408   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
409               "Peer `%s' disconnected from us\n",
410               GNUNET_i2s (peer));
411   pos = GNUNET_CONTAINER_multipeermap_get (peers, peer);
412   if (NULL == pos)
413   {
414     GNUNET_break (0);
415     return;
416   }
417   GNUNET_assert (GNUNET_YES ==
418                  GNUNET_CONTAINER_multipeermap_remove (peers, peer,
419                                                        pos));
420 #if 0
421   if (pos->transmit_task != GNUNET_SCHEDULER_NO_TASK)
422   {
423     GNUNET_SCHEDULER_cancel (pos->transmit_task);
424     pos->transmit_task = GNUNET_SCHEDULER_NO_TASK;
425   }
426   if (NULL != pos->th)
427   {
428     GNUNET_CORE_notify_transmit_ready_cancel (pos->th);
429     pos->th = NULL;
430   }
431 #endif
432   GNUNET_free (pos);
433   GNUNET_STATISTICS_update (stats, "# peers connected", -1, GNUNET_NO);
434 }
435
436
437 /**
438  * Free all values in a hash map.
439  *
440  * @param cls NULL
441  * @param key the key
442  * @param value value to free
443  * @return #GNUNET_OK (continue to iterate)
444  */
445 static int
446 free_entry (void *cls,
447             const struct GNUNET_HashCode *key,
448             void *value)
449 {
450   GNUNET_free (value);
451   return GNUNET_OK;
452 }
453
454
455 /**
456  * Task run during shutdown.
457  *
458  * @param cls unused
459  * @param tc unused
460  */
461 static void
462 shutdown_task (void *cls,
463                const struct GNUNET_SCHEDULER_TaskContext *tc)
464 {
465   if (NULL != revocation_set)
466   {
467     GNUNET_SET_destroy (revocation_set);
468     revocation_set = NULL;
469   }
470   if (NULL != coreAPI)
471   {
472     GNUNET_CORE_disconnect (coreAPI);
473     coreAPI = NULL;
474   }
475   if (NULL != stats)
476   {
477     GNUNET_STATISTICS_destroy (stats, GNUNET_NO);
478     stats = NULL;
479   }
480   if (NULL != peers)
481   {
482     GNUNET_CONTAINER_multipeermap_destroy (peers);
483     peers = NULL;
484   }
485   if (NULL != nc)
486   {
487     GNUNET_SERVER_notification_context_destroy (nc);
488     nc = NULL;
489   }
490   if (NULL != revocation_db)
491   {
492     GNUNET_DISK_file_close (revocation_db);
493     revocation_db = NULL;
494   }
495   GNUNET_CONTAINER_multihashmap_iterate (revocation_map,
496                                          &free_entry,
497                                          NULL);
498   GNUNET_CONTAINER_multihashmap_destroy (revocation_map);
499 }
500
501
502 /**
503  * Called on core init/fail.
504  *
505  * @param cls service closure
506  * @param identity the public identity of this peer
507  */
508 static void
509 core_init (void *cls,
510            const struct GNUNET_PeerIdentity *identity)
511 {
512   if (NULL == identity)
513   {
514     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
515                 "Connection to core FAILED!\n");
516     GNUNET_SCHEDULER_shutdown ();
517     return;
518   }
519   my_identity = *identity;
520 }
521
522
523 /**
524  * Handle network size estimate clients.
525  *
526  * @param cls closure
527  * @param server the initialized server
528  * @param c configuration to use
529  */
530 static void
531 run (void *cls,
532      struct GNUNET_SERVER_Handle *server,
533      const struct GNUNET_CONFIGURATION_Handle *c)
534 {
535   static const struct GNUNET_SERVER_MessageHandler handlers[] = {
536     {&handle_query_message, NULL, GNUNET_MESSAGE_TYPE_REVOCATION_QUERY,
537      sizeof (struct QueryMessage)},
538     {&handle_revoke_message, NULL, GNUNET_MESSAGE_TYPE_REVOCATION_REVOKE,
539      sizeof (struct RevokeMessage)},
540     {NULL, NULL, 0, 0}
541   };
542   static const struct GNUNET_CORE_MessageHandler core_handlers[] = {
543     {&handle_p2p_revoke_message, GNUNET_MESSAGE_TYPE_REVOCATION_REVOKE,
544      sizeof (struct RevokeMessage)},
545     {NULL, 0, 0}
546   };
547   char *fn;
548   uint64_t left;
549   struct RevokeMessage *rm;
550   struct GNUNET_HashCode hc;
551
552   if (GNUNET_OK !=
553       GNUNET_CONFIGURATION_get_value_filename (c,
554                                                "REVOCATION",
555                                                "DATABASE",
556                                                &fn))
557   {
558     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
559                                "REVOCATION",
560                                "DATABASE");
561     GNUNET_SCHEDULER_shutdown ();
562     return;
563   }
564   cfg = c;
565   srv = server;
566   revocation_map = GNUNET_CONTAINER_multihashmap_create (16, GNUNET_NO);
567   nc = GNUNET_SERVER_notification_context_create (server, 1);
568   if (GNUNET_OK !=
569       GNUNET_CONFIGURATION_get_value_number (cfg, "REVOCATION", "WORKBITS",
570                                              &revocation_work_required))
571   {
572     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
573                                "REVOCATION",
574                                "WORKBITS");
575     GNUNET_SCHEDULER_shutdown ();
576     GNUNET_free (fn);
577     return;
578   }
579   if (revocation_work_required >= sizeof (struct GNUNET_HashCode) * 8)
580   {
581     GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
582                                "REVOCATION",
583                                "WORKBITS",
584                                _("Value is too large.\n"));
585     GNUNET_SCHEDULER_shutdown ();
586     GNUNET_free (fn);
587     return;
588   }
589   revocation_set = GNUNET_SET_create (cfg,
590                                       GNUNET_SET_OPERATION_UNION);
591
592   revocation_db = GNUNET_DISK_file_open (fn,
593                                          GNUNET_DISK_OPEN_READWRITE |
594                                          GNUNET_DISK_OPEN_CREATE,
595                                          GNUNET_DISK_PERM_USER_READ | GNUNET_DISK_PERM_USER_WRITE |
596                                          GNUNET_DISK_PERM_GROUP_READ |
597                                          GNUNET_DISK_PERM_OTHER_READ);
598   if (NULL == revocation_db)
599   {
600     GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
601                                "REVOCATION",
602                                "DATABASE",
603                                _("Could not open revocation database file!"));
604     GNUNET_SCHEDULER_shutdown ();
605     GNUNET_free (fn);
606     return;
607   }
608   if (GNUNET_OK !=
609       GNUNET_DISK_file_size (fn, &left, GNUNET_YES, GNUNET_YES))
610     left = 0;
611   while (left > sizeof (struct RevokeMessage))
612   {
613     rm = GNUNET_new (struct RevokeMessage);
614     if (sizeof (struct RevokeMessage) !=
615         GNUNET_DISK_file_read (revocation_db,
616                                rm,
617                                sizeof (struct RevokeMessage)))
618     {
619       GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
620                                 "read",
621                                 fn);
622       GNUNET_free (rm);
623       GNUNET_SCHEDULER_shutdown ();
624       GNUNET_free (fn);
625       return;
626     }
627     GNUNET_break (0 == ntohl (rm->reserved));
628     GNUNET_CRYPTO_hash (&rm->public_key,
629                         sizeof (struct GNUNET_CRYPTO_EccPublicSignKey),
630                         &hc);
631     GNUNET_break (GNUNET_OK ==
632                   GNUNET_CONTAINER_multihashmap_put (revocation_map,
633                                                      &hc,
634                                                      rm,
635                                                      GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
636   }
637   GNUNET_free (fn);
638
639   GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL, &shutdown_task,
640                                 NULL);
641   peers = GNUNET_CONTAINER_multipeermap_create (128, GNUNET_NO);
642   GNUNET_SERVER_add_handlers (srv, handlers);
643    /* Connect to core service and register core handlers */
644   coreAPI = GNUNET_CORE_connect (cfg,   /* Main configuration */
645                                  NULL,       /* Closure passed to functions */
646                                  &core_init,    /* Call core_init once connected */
647                                  &handle_core_connect,  /* Handle connects */
648                                  &handle_core_disconnect,       /* Handle disconnects */
649                                  NULL,  /* Don't want notified about all incoming messages */
650                                  GNUNET_NO,     /* For header only inbound notification */
651                                  NULL,  /* Don't want notified about all outbound messages */
652                                  GNUNET_NO,     /* For header only outbound notification */
653                                  core_handlers);        /* Register these handlers */
654   if (NULL == coreAPI)
655   {
656     GNUNET_SCHEDULER_shutdown ();
657     return;
658   }
659   stats = GNUNET_STATISTICS_create ("revocation", cfg);
660 }
661
662
663 /**
664  * The main function for the network size estimation service.
665  *
666  * @param argc number of arguments from the command line
667  * @param argv command line arguments
668  * @return 0 ok, 1 on error
669  */
670 int
671 main (int argc,
672       char *const *argv)
673 {
674   return (GNUNET_OK ==
675           GNUNET_SERVICE_run (argc, argv, "revocation", GNUNET_SERVICE_OPTION_NONE,
676                               &run, NULL)) ? 0 : 1;
677 }
678
679
680 #ifdef LINUX
681 #include <malloc.h>
682
683 /**
684  * MINIMIZE heap size (way below 128k) since this process doesn't need much.
685  */
686 void __attribute__ ((constructor))
687 GNUNET_ARM_memory_init ()
688 {
689   mallopt (M_TRIM_THRESHOLD, 4 * 1024);
690   mallopt (M_TOP_PAD, 1 * 1024);
691   malloc_trim (0);
692 }
693 #endif
694
695
696
697 /* end of gnunet-service-revocation.c */