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