fix sign api for to address #6164
[oweals/gnunet.git] / src / revocation / gnunet-service-revocation.c
1 /*
2    This file is part of GNUnet.
3    Copyright (C) 2013, 2014, 2016 GNUnet e.V.
4
5    GNUnet is free software: you can redistribute it and/or modify it
6    under the terms of the GNU Affero General Public License as published
7    by the Free Software Foundation, either version 3 of the License,
8    or (at your 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    Affero General Public License for more details.
14
15    You should have received a copy of the GNU Affero General Public License
16    along with this program.  If not, see <http://www.gnu.org/licenses/>.
17
18      SPDX-License-Identifier: AGPL3.0-or-later
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  * - optimization: avoid sending revocation back to peer that we got it from;
35  * - optimization: have randomized delay in sending revocations to other peers
36  *                 to make it rare to traverse each link twice (NSE-style)
37  */
38 #include "platform.h"
39 #include <math.h>
40 #include "gnunet_util_lib.h"
41 #include "gnunet_block_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    * Queue for sending messages to this peer.
60    */
61   struct GNUNET_MQ_Handle *mq;
62
63   /**
64    * What is the identity of the peer?
65    */
66   struct GNUNET_PeerIdentity id;
67
68   /**
69    * Tasked used to trigger the set union operation.
70    */
71   struct GNUNET_SCHEDULER_Task *transmit_task;
72
73   /**
74    * Handle to active set union operation (over revocation sets).
75    */
76   struct GNUNET_SET_OperationHandle *so;
77 };
78
79
80 /**
81  * Set from all revocations known to us.
82  */
83 static struct GNUNET_SET_Handle *revocation_set;
84
85 /**
86  * Hash map with all revoked keys, maps the hash of the public key
87  * to the respective `struct RevokeMessage`.
88  */
89 static struct GNUNET_CONTAINER_MultiHashMap *revocation_map;
90
91 /**
92  * Handle to our current configuration.
93  */
94 static const struct GNUNET_CONFIGURATION_Handle *cfg;
95
96 /**
97  * Handle to the statistics service.
98  */
99 static struct GNUNET_STATISTICS_Handle *stats;
100
101 /**
102  * Handle to the core service (for flooding)
103  */
104 static struct GNUNET_CORE_Handle *core_api;
105
106 /**
107  * Map of all connected peers.
108  */
109 static struct GNUNET_CONTAINER_MultiPeerMap *peers;
110
111 /**
112  * The peer identity of this peer.
113  */
114 static struct GNUNET_PeerIdentity my_identity;
115
116 /**
117  * File handle for the revocation database.
118  */
119 static struct GNUNET_DISK_FileHandle *revocation_db;
120
121 /**
122  * Handle for us listening to incoming revocation set union requests.
123  */
124 static struct GNUNET_SET_ListenHandle *revocation_union_listen_handle;
125
126 /**
127  * Amount of work required (W-bit collisions) for REVOCATION proofs, in collision-bits.
128  */
129 static unsigned long long revocation_work_required;
130
131 /**
132  * Our application ID for set union operations.  Must be the
133  * same for all (compatible) peers.
134  */
135 static struct GNUNET_HashCode revocation_set_union_app_id;
136
137
138 /**
139  * Create a new PeerEntry and add it to the peers multipeermap.
140  *
141  * @param peer the peer identity
142  * @return a pointer to the new PeerEntry
143  */
144 static struct PeerEntry *
145 new_peer_entry (const struct GNUNET_PeerIdentity *peer)
146 {
147   struct PeerEntry *peer_entry;
148
149   peer_entry = GNUNET_new (struct PeerEntry);
150   peer_entry->id = *peer;
151   GNUNET_assert (GNUNET_OK ==
152                  GNUNET_CONTAINER_multipeermap_put (peers,
153                                                     &peer_entry->id,
154                                                     peer_entry,
155                                                     GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
156   return peer_entry;
157 }
158
159
160 /**
161  * An revoke message has been received, check that it is well-formed.
162  *
163  * @param rm the message to verify
164  * @return #GNUNET_YES if the message is verified
165  *         #GNUNET_NO if the key/signature don't verify
166  */
167 static int
168 verify_revoke_message (const struct RevokeMessage *rm)
169 {
170   if (GNUNET_YES !=
171       GNUNET_REVOCATION_check_pow (&rm->public_key,
172                                    rm->proof_of_work,
173                                    (unsigned int) revocation_work_required))
174   {
175     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
176                 "Proof of work invalid!\n");
177     GNUNET_break_op (0);
178     return GNUNET_NO;
179   }
180   if (GNUNET_OK !=
181       GNUNET_CRYPTO_ecdsa_verify_ (GNUNET_SIGNATURE_PURPOSE_REVOCATION,
182                                    &rm->purpose,
183                                    &rm->signature,
184                                    &rm->public_key))
185   {
186     GNUNET_break_op (0);
187     return GNUNET_NO;
188   }
189   return GNUNET_YES;
190 }
191
192
193 /**
194  * Handle client connecting to the service.
195  *
196  * @param cls NULL
197  * @param client the new client
198  * @param mq the message queue of @a client
199  * @return @a client
200  */
201 static void *
202 client_connect_cb (void *cls,
203                    struct GNUNET_SERVICE_Client *client,
204                    struct GNUNET_MQ_Handle *mq)
205 {
206   return client;
207 }
208
209
210 /**
211  * Handle client connecting to the service.
212  *
213  * @param cls NULL
214  * @param client the new client
215  * @param app_cls must alias @a client
216  */
217 static void
218 client_disconnect_cb (void *cls,
219                       struct GNUNET_SERVICE_Client *client,
220                       void *app_cls)
221 {
222   GNUNET_assert (client == app_cls);
223 }
224
225
226 /**
227  * Handle QUERY message from client.
228  *
229  * @param cls client who sent the message
230  * @param qm the message received
231  */
232 static void
233 handle_query_message (void *cls,
234                       const struct QueryMessage *qm)
235 {
236   struct GNUNET_SERVICE_Client *client = cls;
237   struct GNUNET_MQ_Envelope *env;
238   struct QueryResponseMessage *qrm;
239   struct GNUNET_HashCode hc;
240   int res;
241
242   GNUNET_CRYPTO_hash (&qm->key,
243                       sizeof(struct GNUNET_CRYPTO_EcdsaPublicKey),
244                       &hc);
245   res = GNUNET_CONTAINER_multihashmap_contains (revocation_map,
246                                                 &hc);
247   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
248               (GNUNET_NO == res)
249               ? "Received revocation check for valid key `%s' from client\n"
250               : "Received revocation check for revoked key `%s' from client\n",
251               GNUNET_h2s (&hc));
252   env = GNUNET_MQ_msg (qrm,
253                        GNUNET_MESSAGE_TYPE_REVOCATION_QUERY_RESPONSE);
254   qrm->is_valid = htonl ((GNUNET_YES == res) ? GNUNET_NO : GNUNET_YES);
255   GNUNET_MQ_send (GNUNET_SERVICE_client_get_mq (client),
256                   env);
257   GNUNET_SERVICE_client_continue (client);
258 }
259
260
261 /**
262  * Flood the given revocation message to all neighbours.
263  *
264  * @param cls the `struct RevokeMessage` to flood
265  * @param target a neighbour
266  * @param value our `struct PeerEntry` for the neighbour
267  * @return #GNUNET_OK (continue to iterate)
268  */
269 static int
270 do_flood (void *cls,
271           const struct GNUNET_PeerIdentity *target,
272           void *value)
273 {
274   const struct RevokeMessage *rm = cls;
275   struct PeerEntry *pe = value;
276   struct GNUNET_MQ_Envelope *e;
277   struct RevokeMessage *cp;
278
279   if (NULL == pe->mq)
280     return GNUNET_OK; /* peer connected to us via SET,
281                          but we have no direct CORE
282                          connection for flooding */
283   e = GNUNET_MQ_msg (cp,
284                      GNUNET_MESSAGE_TYPE_REVOCATION_REVOKE);
285   *cp = *rm;
286   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
287               "Flooding revocation to `%s'\n",
288               GNUNET_i2s (target));
289   GNUNET_MQ_send (pe->mq,
290                   e);
291   return GNUNET_OK;
292 }
293
294
295 /**
296  * Publicize revocation message.   Stores the message locally in the
297  * database and passes it to all connected neighbours (and adds it to
298  * the set for future connections).
299  *
300  * @param rm message to publicize
301  * @return #GNUNET_OK on success, #GNUNET_NO if we encountered an error,
302  *         #GNUNET_SYSERR if the message was malformed
303  */
304 static int
305 publicize_rm (const struct RevokeMessage *rm)
306 {
307   struct RevokeMessage *cp;
308   struct GNUNET_HashCode hc;
309   struct GNUNET_SET_Element e;
310
311   GNUNET_CRYPTO_hash (&rm->public_key,
312                       sizeof(struct GNUNET_CRYPTO_EcdsaPublicKey),
313                       &hc);
314   if (GNUNET_YES ==
315       GNUNET_CONTAINER_multihashmap_contains (revocation_map,
316                                               &hc))
317   {
318     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
319                 "Duplicate revocation received from peer. Ignored.\n");
320     return GNUNET_OK;
321   }
322   if (GNUNET_OK !=
323       verify_revoke_message (rm))
324   {
325     GNUNET_break_op (0);
326     return GNUNET_SYSERR;
327   }
328   /* write to disk */
329   if (sizeof(struct RevokeMessage) !=
330       GNUNET_DISK_file_write (revocation_db,
331                               rm,
332                               sizeof(struct RevokeMessage)))
333   {
334     GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
335                          "write");
336     return GNUNET_NO;
337   }
338   if (GNUNET_OK !=
339       GNUNET_DISK_file_sync (revocation_db))
340   {
341     GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
342                          "sync");
343     return GNUNET_NO;
344   }
345   /* keep copy in memory */
346   cp = (struct RevokeMessage *) GNUNET_copy_message (&rm->header);
347   GNUNET_break (GNUNET_OK ==
348                 GNUNET_CONTAINER_multihashmap_put (revocation_map,
349                                                    &hc,
350                                                    cp,
351                                                    GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
352   /* add to set for future connections */
353   e.size = htons (rm->header.size);
354   e.element_type = GNUNET_BLOCK_TYPE_REVOCATION;
355   e.data = rm;
356   if (GNUNET_OK !=
357       GNUNET_SET_add_element (revocation_set,
358                               &e,
359                               NULL,
360                               NULL))
361   {
362     GNUNET_break (0);
363     return GNUNET_OK;
364   }
365   else
366   {
367     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
368                 "Added revocation info to SET\n");
369   }
370   /* flood to neighbours */
371   GNUNET_CONTAINER_multipeermap_iterate (peers,
372                                          &do_flood,
373                                          cp);
374   return GNUNET_OK;
375 }
376
377
378 /**
379  * Handle REVOKE message from client.
380  *
381  * @param cls client who sent the message
382  * @param rm the message received
383  */
384 static void
385 handle_revoke_message (void *cls,
386                        const struct RevokeMessage *rm)
387 {
388   struct GNUNET_SERVICE_Client *client = cls;
389   struct GNUNET_MQ_Envelope *env;
390   struct RevocationResponseMessage *rrm;
391   int ret;
392
393   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
394               "Received REVOKE message from client\n");
395   if (GNUNET_SYSERR == (ret = publicize_rm (rm)))
396   {
397     GNUNET_break_op (0);
398     GNUNET_SERVICE_client_drop (client);
399     return;
400   }
401   env = GNUNET_MQ_msg (rrm,
402                        GNUNET_MESSAGE_TYPE_REVOCATION_REVOKE_RESPONSE);
403   rrm->is_valid = htonl ((GNUNET_OK == ret) ? GNUNET_NO : GNUNET_YES);
404   GNUNET_MQ_send (GNUNET_SERVICE_client_get_mq (client),
405                   env);
406   GNUNET_SERVICE_client_continue (client);
407 }
408
409
410 /**
411  * Core handler for flooded revocation messages.
412  *
413  * @param cls closure unused
414  * @param rm revocation message
415  */
416 static void
417 handle_p2p_revoke (void *cls,
418                    const struct RevokeMessage *rm)
419 {
420   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
421               "Received REVOKE message\n");
422   GNUNET_break_op (GNUNET_SYSERR !=
423                    publicize_rm (rm));
424 }
425
426
427 /**
428  * Callback for set operation results. Called for each element in the
429  * result set.  Each element contains a revocation, which we should
430  * validate and then add to our revocation list (and set).
431  *
432  * @param cls closure
433  * @param element a result element, only valid if status is #GNUNET_SET_STATUS_OK
434  * @param current_size current set size
435  * @param status see `enum GNUNET_SET_Status`
436  */
437 static void
438 add_revocation (void *cls,
439                 const struct GNUNET_SET_Element *element,
440                 uint64_t current_size,
441                 enum GNUNET_SET_Status status)
442 {
443   struct PeerEntry *peer_entry = cls;
444   const struct RevokeMessage *rm;
445
446   switch (status)
447   {
448   case GNUNET_SET_STATUS_OK:
449     if (element->size != sizeof(struct RevokeMessage))
450     {
451       GNUNET_break_op (0);
452       return;
453     }
454     if (GNUNET_BLOCK_TYPE_REVOCATION != element->element_type)
455     {
456       GNUNET_STATISTICS_update (stats,
457                                 gettext_noop (
458                                   "# unsupported revocations received via set union"),
459                                 1,
460                                 GNUNET_NO);
461       return;
462     }
463     rm = element->data;
464     (void) handle_p2p_revoke (NULL,
465                               rm);
466     GNUNET_STATISTICS_update (stats,
467                               gettext_noop (
468                                 "# revocation messages received via set union"),
469                               1, GNUNET_NO);
470     break;
471
472   case GNUNET_SET_STATUS_FAILURE:
473     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
474                 _ ("Error computing revocation set union with %s\n"),
475                 GNUNET_i2s (&peer_entry->id));
476     peer_entry->so = NULL;
477     GNUNET_STATISTICS_update (stats,
478                               gettext_noop ("# revocation set unions failed"),
479                               1,
480                               GNUNET_NO);
481     break;
482
483   case GNUNET_SET_STATUS_HALF_DONE:
484     break;
485
486   case GNUNET_SET_STATUS_DONE:
487     peer_entry->so = NULL;
488     GNUNET_STATISTICS_update (stats,
489                               gettext_noop (
490                                 "# revocation set unions completed"),
491                               1,
492                               GNUNET_NO);
493     break;
494
495   default:
496     GNUNET_break (0);
497     break;
498   }
499 }
500
501
502 /**
503  * The timeout for performing the set union has expired,
504  * run the set operation on the revocation certificates.
505  *
506  * @param cls NULL
507  */
508 static void
509 transmit_task_cb (void *cls)
510 {
511   struct PeerEntry *peer_entry = cls;
512
513   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
514               "Starting set exchange with peer `%s'\n",
515               GNUNET_i2s (&peer_entry->id));
516   peer_entry->transmit_task = NULL;
517   GNUNET_assert (NULL == peer_entry->so);
518   peer_entry->so = GNUNET_SET_prepare (&peer_entry->id,
519                                        &revocation_set_union_app_id,
520                                        NULL,
521                                        GNUNET_SET_RESULT_ADDED,
522                                        (struct GNUNET_SET_Option[]) { { 0 } },
523                                        &add_revocation,
524                                        peer_entry);
525   if (GNUNET_OK !=
526       GNUNET_SET_commit (peer_entry->so,
527                          revocation_set))
528   {
529     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
530                 _ ("SET service crashed, terminating revocation service\n"));
531     GNUNET_SCHEDULER_shutdown ();
532     return;
533   }
534 }
535
536
537 /**
538  * Method called whenever a peer connects. Sets up the PeerEntry and
539  * schedules the initial revocation set exchange with this peer.
540  *
541  * @param cls closure
542  * @param peer peer identity this notification is about
543  */
544 static void *
545 handle_core_connect (void *cls,
546                      const struct GNUNET_PeerIdentity *peer,
547                      struct GNUNET_MQ_Handle *mq)
548 {
549   struct PeerEntry *peer_entry;
550   struct GNUNET_HashCode my_hash;
551   struct GNUNET_HashCode peer_hash;
552
553   if (0 == GNUNET_memcmp (peer,
554                           &my_identity))
555   {
556     return NULL;
557   }
558
559   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
560               "Peer `%s' connected to us\n",
561               GNUNET_i2s (peer));
562   GNUNET_STATISTICS_update (stats,
563                             "# peers connected",
564                             1,
565                             GNUNET_NO);
566   peer_entry = GNUNET_CONTAINER_multipeermap_get (peers,
567                                                   peer);
568   if (NULL != peer_entry)
569   {
570     /* This can happen if "core"'s notification is a tad late
571        and CADET+SET were faster and already produced a
572      #handle_revocation_union_request() for us to deal
573        with.  This should be rare, but isn't impossible. */
574     peer_entry->mq = mq;
575     return peer_entry;
576   }
577   peer_entry = new_peer_entry (peer);
578   peer_entry->mq = mq;
579   GNUNET_CRYPTO_hash (&my_identity,
580                       sizeof(my_identity),
581                       &my_hash);
582   GNUNET_CRYPTO_hash (peer,
583                       sizeof(*peer),
584                       &peer_hash);
585   if (0 < GNUNET_CRYPTO_hash_cmp (&my_hash,
586                                   &peer_hash))
587   {
588     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
589                 "Starting SET operation with peer `%s'\n",
590                 GNUNET_i2s (peer));
591     peer_entry->transmit_task =
592       GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS,
593                                     &transmit_task_cb,
594                                     peer_entry);
595   }
596   return peer_entry;
597 }
598
599
600 /**
601  * Method called whenever a peer disconnects. Deletes the PeerEntry and cancels
602  * any pending transmission requests to that peer.
603  *
604  * @param cls closure
605  * @param peer peer identity this notification is about
606  * @param internal_cls our `struct PeerEntry` for this peer
607  */
608 static void
609 handle_core_disconnect (void *cls,
610                         const struct GNUNET_PeerIdentity *peer,
611                         void *internal_cls)
612 {
613   struct PeerEntry *peer_entry = internal_cls;
614
615   if (0 == GNUNET_memcmp (peer,
616                           &my_identity))
617     return;
618   GNUNET_assert (NULL != peer_entry);
619   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
620               "Peer `%s' disconnected from us\n",
621               GNUNET_i2s (peer));
622   GNUNET_assert (GNUNET_YES ==
623                  GNUNET_CONTAINER_multipeermap_remove (peers,
624                                                        peer,
625                                                        peer_entry));
626   if (NULL != peer_entry->transmit_task)
627   {
628     GNUNET_SCHEDULER_cancel (peer_entry->transmit_task);
629     peer_entry->transmit_task = NULL;
630   }
631   if (NULL != peer_entry->so)
632   {
633     GNUNET_SET_operation_cancel (peer_entry->so);
634     peer_entry->so = NULL;
635   }
636   GNUNET_free (peer_entry);
637   GNUNET_STATISTICS_update (stats,
638                             "# peers connected",
639                             -1,
640                             GNUNET_NO);
641 }
642
643
644 /**
645  * Free all values in a hash map.
646  *
647  * @param cls NULL
648  * @param key the key
649  * @param value value to free
650  * @return #GNUNET_OK (continue to iterate)
651  */
652 static int
653 free_entry (void *cls,
654             const struct GNUNET_HashCode *key,
655             void *value)
656 {
657   GNUNET_free (value);
658   return GNUNET_OK;
659 }
660
661
662 /**
663  * Task run during shutdown.
664  *
665  * @param cls unused
666  */
667 static void
668 shutdown_task (void *cls)
669 {
670   if (NULL != revocation_set)
671   {
672     GNUNET_SET_destroy (revocation_set);
673     revocation_set = NULL;
674   }
675   if (NULL != revocation_union_listen_handle)
676   {
677     GNUNET_SET_listen_cancel (revocation_union_listen_handle);
678     revocation_union_listen_handle = NULL;
679   }
680   if (NULL != core_api)
681   {
682     GNUNET_CORE_disconnect (core_api);
683     core_api = NULL;
684   }
685   if (NULL != stats)
686   {
687     GNUNET_STATISTICS_destroy (stats, GNUNET_NO);
688     stats = NULL;
689   }
690   if (NULL != peers)
691   {
692     GNUNET_CONTAINER_multipeermap_destroy (peers);
693     peers = NULL;
694   }
695   if (NULL != revocation_db)
696   {
697     GNUNET_DISK_file_close (revocation_db);
698     revocation_db = NULL;
699   }
700   GNUNET_CONTAINER_multihashmap_iterate (revocation_map,
701                                          &free_entry,
702                                          NULL);
703   GNUNET_CONTAINER_multihashmap_destroy (revocation_map);
704 }
705
706
707 /**
708  * Called on core init/fail.
709  *
710  * @param cls service closure
711  * @param identity the public identity of this peer
712  */
713 static void
714 core_init (void *cls,
715            const struct GNUNET_PeerIdentity *identity)
716 {
717   if (NULL == identity)
718   {
719     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
720                 "Connection to core FAILED!\n");
721     GNUNET_SCHEDULER_shutdown ();
722     return;
723   }
724   my_identity = *identity;
725 }
726
727
728 /**
729  * Called when another peer wants to do a set operation with the
730  * local peer. If a listen error occurs, the 'request' is NULL.
731  *
732  * @param cls closure
733  * @param other_peer the other peer
734  * @param context_msg message with application specific information from
735  *        the other peer
736  * @param request request from the other peer (never NULL), use GNUNET_SET_accept()
737  *        to accept it, otherwise the request will be refused
738  *        Note that we can't just return value from the listen callback,
739  *        as it is also necessary to specify the set we want to do the
740  *        operation with, whith sometimes can be derived from the context
741  *        message. It's necessary to specify the timeout.
742  */
743 static void
744 handle_revocation_union_request (void *cls,
745                                  const struct GNUNET_PeerIdentity *other_peer,
746                                  const struct GNUNET_MessageHeader *context_msg,
747                                  struct GNUNET_SET_Request *request)
748 {
749   struct PeerEntry *peer_entry;
750
751   if (NULL == request)
752   {
753     GNUNET_break (0);
754     return;
755   }
756   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
757               "Received set exchange request from peer `%s'\n",
758               GNUNET_i2s (other_peer));
759   peer_entry = GNUNET_CONTAINER_multipeermap_get (peers,
760                                                   other_peer);
761   if (NULL == peer_entry)
762   {
763     peer_entry = new_peer_entry (other_peer);
764   }
765   if (NULL != peer_entry->so)
766   {
767     GNUNET_break_op (0);
768     return;
769   }
770   peer_entry->so = GNUNET_SET_accept (request,
771                                       GNUNET_SET_RESULT_ADDED,
772                                       (struct GNUNET_SET_Option[]) { { 0 } },
773                                       &add_revocation,
774                                       peer_entry);
775   if (GNUNET_OK !=
776       GNUNET_SET_commit (peer_entry->so,
777                          revocation_set))
778   {
779     GNUNET_break (0);
780     GNUNET_SCHEDULER_shutdown ();
781     return;
782   }
783 }
784
785
786 /**
787  * Handle network size estimate clients.
788  *
789  * @param cls closure
790  * @param server the initialized server
791  * @param c configuration to use
792  */
793 static void
794 run (void *cls,
795      const struct GNUNET_CONFIGURATION_Handle *c,
796      struct GNUNET_SERVICE_Handle *service)
797 {
798   struct GNUNET_MQ_MessageHandler core_handlers[] = {
799     GNUNET_MQ_hd_fixed_size (p2p_revoke,
800                              GNUNET_MESSAGE_TYPE_REVOCATION_REVOKE,
801                              struct RevokeMessage,
802                              NULL),
803     GNUNET_MQ_handler_end ()
804   };
805   char *fn;
806   uint64_t left;
807   struct RevokeMessage *rm;
808   struct GNUNET_HashCode hc;
809
810   GNUNET_CRYPTO_hash ("revocation-set-union-application-id",
811                       strlen ("revocation-set-union-application-id"),
812                       &revocation_set_union_app_id);
813   if (GNUNET_OK !=
814       GNUNET_CONFIGURATION_get_value_filename (c,
815                                                "REVOCATION",
816                                                "DATABASE",
817                                                &fn))
818   {
819     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
820                                "REVOCATION",
821                                "DATABASE");
822     GNUNET_SCHEDULER_shutdown ();
823     return;
824   }
825   cfg = c;
826   revocation_map = GNUNET_CONTAINER_multihashmap_create (16,
827                                                          GNUNET_NO);
828   if (GNUNET_OK !=
829       GNUNET_CONFIGURATION_get_value_number (cfg,
830                                              "REVOCATION",
831                                              "WORKBITS",
832                                              &revocation_work_required))
833   {
834     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
835                                "REVOCATION",
836                                "WORKBITS");
837     GNUNET_SCHEDULER_shutdown ();
838     GNUNET_free (fn);
839     return;
840   }
841   if (revocation_work_required >= sizeof(struct GNUNET_HashCode) * 8)
842   {
843     GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
844                                "REVOCATION",
845                                "WORKBITS",
846                                _ ("Value is too large.\n"));
847     GNUNET_SCHEDULER_shutdown ();
848     GNUNET_free (fn);
849     return;
850   }
851   revocation_set = GNUNET_SET_create (cfg,
852                                       GNUNET_SET_OPERATION_UNION);
853   revocation_union_listen_handle
854     = GNUNET_SET_listen (cfg,
855                          GNUNET_SET_OPERATION_UNION,
856                          &revocation_set_union_app_id,
857                          &handle_revocation_union_request,
858                          NULL);
859   revocation_db = GNUNET_DISK_file_open (fn,
860                                          GNUNET_DISK_OPEN_READWRITE
861                                          | GNUNET_DISK_OPEN_CREATE,
862                                          GNUNET_DISK_PERM_USER_READ
863                                          | GNUNET_DISK_PERM_USER_WRITE
864                                          | GNUNET_DISK_PERM_GROUP_READ
865                                          | GNUNET_DISK_PERM_OTHER_READ);
866   if (NULL == revocation_db)
867   {
868     GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
869                                "REVOCATION",
870                                "DATABASE",
871                                _ ("Could not open revocation database file!"));
872     GNUNET_SCHEDULER_shutdown ();
873     GNUNET_free (fn);
874     return;
875   }
876   if (GNUNET_OK !=
877       GNUNET_DISK_file_size (fn, &left, GNUNET_YES, GNUNET_YES))
878     left = 0;
879   while (left > sizeof(struct RevokeMessage))
880   {
881     rm = GNUNET_new (struct RevokeMessage);
882     if (sizeof(struct RevokeMessage) !=
883         GNUNET_DISK_file_read (revocation_db,
884                                rm,
885                                sizeof(struct RevokeMessage)))
886     {
887       GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
888                                 "read",
889                                 fn);
890       GNUNET_free (rm);
891       GNUNET_SCHEDULER_shutdown ();
892       GNUNET_free (fn);
893       return;
894     }
895     GNUNET_break (0 == ntohl (rm->reserved));
896     GNUNET_CRYPTO_hash (&rm->public_key,
897                         sizeof(struct GNUNET_CRYPTO_EcdsaPublicKey),
898                         &hc);
899     GNUNET_break (GNUNET_OK ==
900                   GNUNET_CONTAINER_multihashmap_put (revocation_map,
901                                                      &hc,
902                                                      rm,
903                                                      GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
904   }
905   GNUNET_free (fn);
906
907   GNUNET_SCHEDULER_add_shutdown (&shutdown_task,
908                                  NULL);
909   peers = GNUNET_CONTAINER_multipeermap_create (128,
910                                                 GNUNET_YES);
911   /* Connect to core service and register core handlers */
912   core_api = GNUNET_CORE_connect (cfg,    /* Main configuration */
913                                   NULL,       /* Closure passed to functions */
914                                   &core_init,    /* Call core_init once connected */
915                                   &handle_core_connect,  /* Handle connects */
916                                   &handle_core_disconnect,       /* Handle disconnects */
917                                   core_handlers);        /* Register these handlers */
918   if (NULL == core_api)
919   {
920     GNUNET_SCHEDULER_shutdown ();
921     return;
922   }
923   stats = GNUNET_STATISTICS_create ("revocation",
924                                     cfg);
925 }
926
927
928 /**
929  * Define "main" method using service macro.
930  */
931 GNUNET_SERVICE_MAIN
932   ("revocation",
933   GNUNET_SERVICE_OPTION_NONE,
934   &run,
935   &client_connect_cb,
936   &client_disconnect_cb,
937   NULL,
938   GNUNET_MQ_hd_fixed_size (query_message,
939                            GNUNET_MESSAGE_TYPE_REVOCATION_QUERY,
940                            struct QueryMessage,
941                            NULL),
942   GNUNET_MQ_hd_fixed_size (revoke_message,
943                            GNUNET_MESSAGE_TYPE_REVOCATION_REVOKE,
944                            struct RevokeMessage,
945                            NULL),
946   GNUNET_MQ_handler_end ());
947
948
949 #if defined(__linux__) && defined(__GLIBC__)
950 #include <malloc.h>
951
952 /**
953  * MINIMIZE heap size (way below 128k) since this process doesn't need much.
954  */
955 void __attribute__ ((constructor))
956 GNUNET_REVOCATION_memory_init ()
957 {
958   mallopt (M_TRIM_THRESHOLD, 4 * 1024);
959   mallopt (M_TOP_PAD, 1 * 1024);
960   malloc_trim (0);
961 }
962
963
964 #endif
965
966
967 /* end of gnunet-service-revocation.c */