diff options
Diffstat (limited to 'src/revocation/gnunet-service-revocation.c')
-rw-r--r-- | src/revocation/gnunet-service-revocation.c | 1032 |
1 files changed, 0 insertions, 1032 deletions
diff --git a/src/revocation/gnunet-service-revocation.c b/src/revocation/gnunet-service-revocation.c deleted file mode 100644 index 4494ade83..000000000 --- a/src/revocation/gnunet-service-revocation.c +++ /dev/null | |||
@@ -1,1032 +0,0 @@ | |||
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_setu_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_SETU_OperationHandle *so; | ||
77 | }; | ||
78 | |||
79 | |||
80 | /** | ||
81 | * Set from all revocations known to us. | ||
82 | */ | ||
83 | static struct GNUNET_SETU_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_SETU_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 | * Length of an expiration expoch | ||
133 | */ | ||
134 | static struct GNUNET_TIME_Relative epoch_duration; | ||
135 | |||
136 | /** | ||
137 | * Our application ID for set union operations. Must be the | ||
138 | * same for all (compatible) peers. | ||
139 | */ | ||
140 | static struct GNUNET_HashCode revocation_set_union_app_id; | ||
141 | |||
142 | |||
143 | /** | ||
144 | * Create a new PeerEntry and add it to the peers multipeermap. | ||
145 | * | ||
146 | * @param peer the peer identity | ||
147 | * @return a pointer to the new PeerEntry | ||
148 | */ | ||
149 | static struct PeerEntry * | ||
150 | new_peer_entry (const struct GNUNET_PeerIdentity *peer) | ||
151 | { | ||
152 | struct PeerEntry *peer_entry; | ||
153 | |||
154 | peer_entry = GNUNET_new (struct PeerEntry); | ||
155 | peer_entry->id = *peer; | ||
156 | GNUNET_assert (GNUNET_OK == | ||
157 | GNUNET_CONTAINER_multipeermap_put (peers, | ||
158 | &peer_entry->id, | ||
159 | peer_entry, | ||
160 | GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)); | ||
161 | return peer_entry; | ||
162 | } | ||
163 | |||
164 | |||
165 | /** | ||
166 | * An revoke message has been received, check that it is well-formed. | ||
167 | * | ||
168 | * @param rm the message to verify | ||
169 | * @return #GNUNET_YES if the message is verified | ||
170 | * #GNUNET_NO if the key/signature don't verify | ||
171 | */ | ||
172 | static enum GNUNET_GenericReturnValue | ||
173 | verify_revoke_message (const struct RevokeMessage *rm) | ||
174 | { | ||
175 | const struct GNUNET_REVOCATION_PowP *pow | ||
176 | = (const struct GNUNET_REVOCATION_PowP *) &rm[1]; | ||
177 | |||
178 | if (GNUNET_YES != | ||
179 | GNUNET_REVOCATION_check_pow (pow, | ||
180 | (unsigned int) revocation_work_required, | ||
181 | epoch_duration)) | ||
182 | { | ||
183 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
184 | "Proof of work invalid!\n"); | ||
185 | GNUNET_break_op (0); | ||
186 | return GNUNET_NO; | ||
187 | } | ||
188 | return GNUNET_YES; | ||
189 | } | ||
190 | |||
191 | |||
192 | /** | ||
193 | * Handle client connecting to the service. | ||
194 | * | ||
195 | * @param cls NULL | ||
196 | * @param client the new client | ||
197 | * @param mq the message queue of @a client | ||
198 | * @return @a client | ||
199 | */ | ||
200 | static void * | ||
201 | client_connect_cb (void *cls, | ||
202 | struct GNUNET_SERVICE_Client *client, | ||
203 | struct GNUNET_MQ_Handle *mq) | ||
204 | { | ||
205 | return client; | ||
206 | } | ||
207 | |||
208 | |||
209 | /** | ||
210 | * Handle client connecting to the service. | ||
211 | * | ||
212 | * @param cls NULL | ||
213 | * @param client the new client | ||
214 | * @param app_cls must alias @a client | ||
215 | */ | ||
216 | static void | ||
217 | client_disconnect_cb (void *cls, | ||
218 | struct GNUNET_SERVICE_Client *client, | ||
219 | void *app_cls) | ||
220 | { | ||
221 | GNUNET_assert (client == app_cls); | ||
222 | } | ||
223 | |||
224 | |||
225 | /** | ||
226 | * Handle QUERY message from client. | ||
227 | * | ||
228 | * @param cls client who sent the message | ||
229 | * @param qm the message received | ||
230 | */ | ||
231 | static void | ||
232 | handle_query_message (void *cls, | ||
233 | const struct QueryMessage *qm) | ||
234 | { | ||
235 | struct GNUNET_SERVICE_Client *client = cls; | ||
236 | struct GNUNET_MQ_Envelope *env; | ||
237 | struct QueryResponseMessage *qrm; | ||
238 | struct GNUNET_HashCode hc; | ||
239 | int res; | ||
240 | |||
241 | GNUNET_CRYPTO_hash (&qm->key, | ||
242 | sizeof(struct GNUNET_IDENTITY_PublicKey), | ||
243 | &hc); | ||
244 | res = GNUNET_CONTAINER_multihashmap_contains (revocation_map, | ||
245 | &hc); | ||
246 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
247 | (GNUNET_NO == res) | ||
248 | ? "Received revocation check for valid key `%s' from client\n" | ||
249 | : "Received revocation check for revoked key `%s' from client\n", | ||
250 | GNUNET_h2s (&hc)); | ||
251 | env = GNUNET_MQ_msg (qrm, | ||
252 | GNUNET_MESSAGE_TYPE_REVOCATION_QUERY_RESPONSE); | ||
253 | qrm->is_valid = htonl ((GNUNET_YES == res) ? GNUNET_NO : GNUNET_YES); | ||
254 | GNUNET_MQ_send (GNUNET_SERVICE_client_get_mq (client), | ||
255 | env); | ||
256 | GNUNET_SERVICE_client_continue (client); | ||
257 | } | ||
258 | |||
259 | |||
260 | /** | ||
261 | * Flood the given revocation message to all neighbours. | ||
262 | * | ||
263 | * @param cls the `struct RevokeMessage` to flood | ||
264 | * @param target a neighbour | ||
265 | * @param value our `struct PeerEntry` for the neighbour | ||
266 | * @return #GNUNET_OK (continue to iterate) | ||
267 | */ | ||
268 | static enum GNUNET_GenericReturnValue | ||
269 | do_flood (void *cls, | ||
270 | const struct GNUNET_PeerIdentity *target, | ||
271 | void *value) | ||
272 | { | ||
273 | const struct RevokeMessage *rm = cls; | ||
274 | struct PeerEntry *pe = value; | ||
275 | struct GNUNET_MQ_Envelope *e; | ||
276 | struct RevokeMessage *cp; | ||
277 | |||
278 | if (NULL == pe->mq) | ||
279 | return GNUNET_OK; /* peer connected to us via SET, | ||
280 | but we have no direct CORE | ||
281 | connection for flooding */ | ||
282 | e = GNUNET_MQ_msg_extra (cp, | ||
283 | htonl (rm->pow_size), | ||
284 | GNUNET_MESSAGE_TYPE_REVOCATION_REVOKE); | ||
285 | *cp = *rm; | ||
286 | memcpy (&cp[1], | ||
287 | &rm[1], | ||
288 | htonl (rm->pow_size)); | ||
289 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
290 | "Flooding revocation to `%s'\n", | ||
291 | GNUNET_i2s (target)); | ||
292 | GNUNET_MQ_send (pe->mq, | ||
293 | e); | ||
294 | return GNUNET_OK; | ||
295 | } | ||
296 | |||
297 | |||
298 | /** | ||
299 | * Publicize revocation message. Stores the message locally in the | ||
300 | * database and passes it to all connected neighbours (and adds it to | ||
301 | * the set for future connections). | ||
302 | * | ||
303 | * @param rm message to publicize | ||
304 | * @return #GNUNET_OK on success, #GNUNET_NO if we encountered an error, | ||
305 | * #GNUNET_SYSERR if the message was malformed | ||
306 | */ | ||
307 | static enum GNUNET_GenericReturnValue | ||
308 | publicize_rm (const struct RevokeMessage *rm) | ||
309 | { | ||
310 | struct RevokeMessage *cp; | ||
311 | struct GNUNET_HashCode hc; | ||
312 | struct GNUNET_SETU_Element e; | ||
313 | ssize_t pklen; | ||
314 | const struct GNUNET_REVOCATION_PowP *pow | ||
315 | = (const struct GNUNET_REVOCATION_PowP *) &rm[1]; | ||
316 | const struct GNUNET_IDENTITY_PublicKey *pk | ||
317 | = (const struct GNUNET_IDENTITY_PublicKey *) &pow[1]; | ||
318 | |||
319 | pklen = GNUNET_IDENTITY_key_get_length (pk); | ||
320 | if (0 > pklen) | ||
321 | { | ||
322 | GNUNET_break_op (0); | ||
323 | return GNUNET_SYSERR; | ||
324 | } | ||
325 | GNUNET_CRYPTO_hash (pk, | ||
326 | pklen, | ||
327 | &hc); | ||
328 | if (GNUNET_YES == | ||
329 | GNUNET_CONTAINER_multihashmap_contains (revocation_map, | ||
330 | &hc)) | ||
331 | { | ||
332 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
333 | "Duplicate revocation received from peer. Ignored.\n"); | ||
334 | return GNUNET_OK; | ||
335 | } | ||
336 | if (GNUNET_OK != | ||
337 | verify_revoke_message (rm)) | ||
338 | { | ||
339 | GNUNET_break_op (0); | ||
340 | return GNUNET_SYSERR; | ||
341 | } | ||
342 | /* write to disk */ | ||
343 | if (sizeof(struct RevokeMessage) != | ||
344 | GNUNET_DISK_file_write (revocation_db, | ||
345 | rm, | ||
346 | sizeof(struct RevokeMessage))) | ||
347 | { | ||
348 | GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, | ||
349 | "write"); | ||
350 | return GNUNET_NO; | ||
351 | } | ||
352 | if (GNUNET_OK != | ||
353 | GNUNET_DISK_file_sync (revocation_db)) | ||
354 | { | ||
355 | GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, | ||
356 | "sync"); | ||
357 | return GNUNET_NO; | ||
358 | } | ||
359 | /* keep copy in memory */ | ||
360 | cp = (struct RevokeMessage *) GNUNET_copy_message (&rm->header); | ||
361 | GNUNET_break (GNUNET_OK == | ||
362 | GNUNET_CONTAINER_multihashmap_put (revocation_map, | ||
363 | &hc, | ||
364 | cp, | ||
365 | GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)); | ||
366 | /* add to set for future connections */ | ||
367 | e.size = htons (rm->header.size); | ||
368 | e.element_type = GNUNET_BLOCK_TYPE_REVOCATION; | ||
369 | e.data = rm; | ||
370 | if (GNUNET_OK != | ||
371 | GNUNET_SETU_add_element (revocation_set, | ||
372 | &e, | ||
373 | NULL, | ||
374 | NULL)) | ||
375 | { | ||
376 | GNUNET_break (0); | ||
377 | return GNUNET_OK; | ||
378 | } | ||
379 | else | ||
380 | { | ||
381 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
382 | "Added revocation info to SET\n"); | ||
383 | } | ||
384 | /* flood to neighbours */ | ||
385 | GNUNET_CONTAINER_multipeermap_iterate (peers, | ||
386 | &do_flood, | ||
387 | cp); | ||
388 | return GNUNET_OK; | ||
389 | } | ||
390 | |||
391 | |||
392 | static int | ||
393 | check_revoke_message (void *cls, | ||
394 | const struct RevokeMessage *rm) | ||
395 | { | ||
396 | uint16_t size; | ||
397 | |||
398 | size = ntohs (rm->header.size); | ||
399 | if (size <= sizeof(struct RevokeMessage) || | ||
400 | (size > UINT16_MAX)) | ||
401 | { | ||
402 | GNUNET_break (0); | ||
403 | return GNUNET_SYSERR; | ||
404 | } | ||
405 | return GNUNET_OK; | ||
406 | |||
407 | } | ||
408 | |||
409 | |||
410 | /** | ||
411 | * Handle REVOKE message from client. | ||
412 | * | ||
413 | * @param cls client who sent the message | ||
414 | * @param rm the message received | ||
415 | */ | ||
416 | static void | ||
417 | handle_revoke_message (void *cls, | ||
418 | const struct RevokeMessage *rm) | ||
419 | { | ||
420 | struct GNUNET_SERVICE_Client *client = cls; | ||
421 | struct GNUNET_MQ_Envelope *env; | ||
422 | struct RevocationResponseMessage *rrm; | ||
423 | int ret; | ||
424 | |||
425 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
426 | "Received REVOKE message from client\n"); | ||
427 | if (GNUNET_SYSERR == (ret = publicize_rm (rm))) | ||
428 | { | ||
429 | GNUNET_break_op (0); | ||
430 | GNUNET_SERVICE_client_drop (client); | ||
431 | return; | ||
432 | } | ||
433 | env = GNUNET_MQ_msg (rrm, | ||
434 | GNUNET_MESSAGE_TYPE_REVOCATION_REVOKE_RESPONSE); | ||
435 | rrm->is_valid = htonl ((GNUNET_OK == ret) ? GNUNET_NO : GNUNET_YES); | ||
436 | GNUNET_MQ_send (GNUNET_SERVICE_client_get_mq (client), | ||
437 | env); | ||
438 | GNUNET_SERVICE_client_continue (client); | ||
439 | } | ||
440 | |||
441 | |||
442 | static int | ||
443 | check_p2p_revoke (void *cls, | ||
444 | const struct RevokeMessage *rm) | ||
445 | { | ||
446 | uint16_t size; | ||
447 | |||
448 | size = ntohs (rm->header.size); | ||
449 | if (size <= sizeof(struct RevokeMessage)) | ||
450 | { | ||
451 | GNUNET_break (0); | ||
452 | return GNUNET_SYSERR; | ||
453 | } | ||
454 | return GNUNET_OK; | ||
455 | |||
456 | } | ||
457 | |||
458 | |||
459 | /** | ||
460 | * Core handler for flooded revocation messages. | ||
461 | * | ||
462 | * @param cls closure unused | ||
463 | * @param rm revocation message | ||
464 | */ | ||
465 | static void | ||
466 | handle_p2p_revoke (void *cls, | ||
467 | const struct RevokeMessage *rm) | ||
468 | { | ||
469 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
470 | "Received REVOKE message\n"); | ||
471 | GNUNET_break_op (GNUNET_SYSERR != | ||
472 | publicize_rm (rm)); | ||
473 | } | ||
474 | |||
475 | |||
476 | /** | ||
477 | * Callback for set operation results. Called for each element in the | ||
478 | * result set. Each element contains a revocation, which we should | ||
479 | * validate and then add to our revocation list (and set). | ||
480 | * | ||
481 | * @param cls closure | ||
482 | * @param element a result element, only valid if status is #GNUNET_SETU_STATUS_OK | ||
483 | * @param current_size current set size | ||
484 | * @param status see `enum GNUNET_SETU_Status` | ||
485 | */ | ||
486 | static void | ||
487 | add_revocation (void *cls, | ||
488 | const struct GNUNET_SETU_Element *element, | ||
489 | uint64_t current_size, | ||
490 | enum GNUNET_SETU_Status status) | ||
491 | { | ||
492 | struct PeerEntry *peer_entry = cls; | ||
493 | const struct RevokeMessage *rm; | ||
494 | |||
495 | switch (status) | ||
496 | { | ||
497 | case GNUNET_SETU_STATUS_ADD_LOCAL: | ||
498 | if (element->size != sizeof(struct RevokeMessage)) | ||
499 | { | ||
500 | GNUNET_break_op (0); | ||
501 | return; | ||
502 | } | ||
503 | if (GNUNET_BLOCK_TYPE_REVOCATION != element->element_type) | ||
504 | { | ||
505 | GNUNET_STATISTICS_update (stats, | ||
506 | gettext_noop ( | ||
507 | "# unsupported revocations received via set union"), | ||
508 | 1, | ||
509 | GNUNET_NO); | ||
510 | return; | ||
511 | } | ||
512 | rm = element->data; | ||
513 | (void) handle_p2p_revoke (NULL, | ||
514 | rm); | ||
515 | GNUNET_STATISTICS_update (stats, | ||
516 | gettext_noop ( | ||
517 | "# revocation messages received via set union"), | ||
518 | 1, GNUNET_NO); | ||
519 | break; | ||
520 | case GNUNET_SETU_STATUS_FAILURE: | ||
521 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, | ||
522 | _ ("Error computing revocation set union with %s\n"), | ||
523 | GNUNET_i2s (&peer_entry->id)); | ||
524 | peer_entry->so = NULL; | ||
525 | GNUNET_STATISTICS_update (stats, | ||
526 | gettext_noop ("# revocation set unions failed"), | ||
527 | 1, | ||
528 | GNUNET_NO); | ||
529 | break; | ||
530 | case GNUNET_SETU_STATUS_DONE: | ||
531 | peer_entry->so = NULL; | ||
532 | GNUNET_STATISTICS_update (stats, | ||
533 | gettext_noop ( | ||
534 | "# revocation set unions completed"), | ||
535 | 1, | ||
536 | GNUNET_NO); | ||
537 | break; | ||
538 | default: | ||
539 | GNUNET_break (0); | ||
540 | break; | ||
541 | } | ||
542 | } | ||
543 | |||
544 | |||
545 | /** | ||
546 | * The timeout for performing the set union has expired, | ||
547 | * run the set operation on the revocation certificates. | ||
548 | * | ||
549 | * @param cls NULL | ||
550 | */ | ||
551 | static void | ||
552 | transmit_task_cb (void *cls) | ||
553 | { | ||
554 | struct PeerEntry *peer_entry = cls; | ||
555 | |||
556 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
557 | "Starting set exchange with peer `%s'\n", | ||
558 | GNUNET_i2s (&peer_entry->id)); | ||
559 | peer_entry->transmit_task = NULL; | ||
560 | GNUNET_assert (NULL == peer_entry->so); | ||
561 | peer_entry->so = GNUNET_SETU_prepare (&peer_entry->id, | ||
562 | &revocation_set_union_app_id, | ||
563 | NULL, | ||
564 | (struct GNUNET_SETU_Option[]) { { 0 } }, | ||
565 | &add_revocation, | ||
566 | peer_entry); | ||
567 | if (GNUNET_OK != | ||
568 | GNUNET_SETU_commit (peer_entry->so, | ||
569 | revocation_set)) | ||
570 | { | ||
571 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, | ||
572 | _ ("SET service crashed, terminating revocation service\n")); | ||
573 | GNUNET_SCHEDULER_shutdown (); | ||
574 | return; | ||
575 | } | ||
576 | } | ||
577 | |||
578 | |||
579 | /** | ||
580 | * Method called whenever a peer connects. Sets up the PeerEntry and | ||
581 | * schedules the initial revocation set exchange with this peer. | ||
582 | * | ||
583 | * @param cls closure | ||
584 | * @param peer peer identity this notification is about | ||
585 | */ | ||
586 | static void * | ||
587 | handle_core_connect (void *cls, | ||
588 | const struct GNUNET_PeerIdentity *peer, | ||
589 | struct GNUNET_MQ_Handle *mq) | ||
590 | { | ||
591 | struct PeerEntry *peer_entry; | ||
592 | struct GNUNET_HashCode my_hash; | ||
593 | struct GNUNET_HashCode peer_hash; | ||
594 | |||
595 | if (0 == GNUNET_memcmp (peer, | ||
596 | &my_identity)) | ||
597 | { | ||
598 | return NULL; | ||
599 | } | ||
600 | |||
601 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
602 | "Peer `%s' connected to us\n", | ||
603 | GNUNET_i2s (peer)); | ||
604 | GNUNET_STATISTICS_update (stats, | ||
605 | "# peers connected", | ||
606 | 1, | ||
607 | GNUNET_NO); | ||
608 | peer_entry = GNUNET_CONTAINER_multipeermap_get (peers, | ||
609 | peer); | ||
610 | if (NULL != peer_entry) | ||
611 | { | ||
612 | /* This can happen if "core"'s notification is a tad late | ||
613 | and CADET+SET were faster and already produced a | ||
614 | #handle_revocation_union_request() for us to deal | ||
615 | with. This should be rare, but isn't impossible. */ | ||
616 | peer_entry->mq = mq; | ||
617 | return peer_entry; | ||
618 | } | ||
619 | peer_entry = new_peer_entry (peer); | ||
620 | peer_entry->mq = mq; | ||
621 | GNUNET_CRYPTO_hash (&my_identity, | ||
622 | sizeof(my_identity), | ||
623 | &my_hash); | ||
624 | GNUNET_CRYPTO_hash (peer, | ||
625 | sizeof(*peer), | ||
626 | &peer_hash); | ||
627 | if (0 < GNUNET_CRYPTO_hash_cmp (&my_hash, | ||
628 | &peer_hash)) | ||
629 | { | ||
630 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
631 | "Starting SET operation with peer `%s'\n", | ||
632 | GNUNET_i2s (peer)); | ||
633 | peer_entry->transmit_task = | ||
634 | GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS, | ||
635 | &transmit_task_cb, | ||
636 | peer_entry); | ||
637 | } | ||
638 | return peer_entry; | ||
639 | } | ||
640 | |||
641 | |||
642 | /** | ||
643 | * Method called whenever a peer disconnects. Deletes the PeerEntry and cancels | ||
644 | * any pending transmission requests to that peer. | ||
645 | * | ||
646 | * @param cls closure | ||
647 | * @param peer peer identity this notification is about | ||
648 | * @param internal_cls our `struct PeerEntry` for this peer | ||
649 | */ | ||
650 | static void | ||
651 | handle_core_disconnect (void *cls, | ||
652 | const struct GNUNET_PeerIdentity *peer, | ||
653 | void *internal_cls) | ||
654 | { | ||
655 | struct PeerEntry *peer_entry = internal_cls; | ||
656 | |||
657 | if (0 == GNUNET_memcmp (peer, | ||
658 | &my_identity)) | ||
659 | return; | ||
660 | GNUNET_assert (NULL != peer_entry); | ||
661 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
662 | "Peer `%s' disconnected from us\n", | ||
663 | GNUNET_i2s (peer)); | ||
664 | GNUNET_assert (GNUNET_YES == | ||
665 | GNUNET_CONTAINER_multipeermap_remove (peers, | ||
666 | peer, | ||
667 | peer_entry)); | ||
668 | if (NULL != peer_entry->transmit_task) | ||
669 | { | ||
670 | GNUNET_SCHEDULER_cancel (peer_entry->transmit_task); | ||
671 | peer_entry->transmit_task = NULL; | ||
672 | } | ||
673 | if (NULL != peer_entry->so) | ||
674 | { | ||
675 | GNUNET_SETU_operation_cancel (peer_entry->so); | ||
676 | peer_entry->so = NULL; | ||
677 | } | ||
678 | GNUNET_free (peer_entry); | ||
679 | GNUNET_STATISTICS_update (stats, | ||
680 | "# peers connected", | ||
681 | -1, | ||
682 | GNUNET_NO); | ||
683 | } | ||
684 | |||
685 | |||
686 | /** | ||
687 | * Free all values in a hash map. | ||
688 | * | ||
689 | * @param cls NULL | ||
690 | * @param key the key | ||
691 | * @param value value to free | ||
692 | * @return #GNUNET_OK (continue to iterate) | ||
693 | */ | ||
694 | static int | ||
695 | free_entry (void *cls, | ||
696 | const struct GNUNET_HashCode *key, | ||
697 | void *value) | ||
698 | { | ||
699 | GNUNET_free (value); | ||
700 | return GNUNET_OK; | ||
701 | } | ||
702 | |||
703 | |||
704 | /** | ||
705 | * Task run during shutdown. | ||
706 | * | ||
707 | * @param cls unused | ||
708 | */ | ||
709 | static void | ||
710 | shutdown_task (void *cls) | ||
711 | { | ||
712 | if (NULL != revocation_set) | ||
713 | { | ||
714 | GNUNET_SETU_destroy (revocation_set); | ||
715 | revocation_set = NULL; | ||
716 | } | ||
717 | if (NULL != revocation_union_listen_handle) | ||
718 | { | ||
719 | GNUNET_SETU_listen_cancel (revocation_union_listen_handle); | ||
720 | revocation_union_listen_handle = NULL; | ||
721 | } | ||
722 | if (NULL != core_api) | ||
723 | { | ||
724 | GNUNET_CORE_disconnect (core_api); | ||
725 | core_api = NULL; | ||
726 | } | ||
727 | if (NULL != stats) | ||
728 | { | ||
729 | GNUNET_STATISTICS_destroy (stats, GNUNET_NO); | ||
730 | stats = NULL; | ||
731 | } | ||
732 | if (NULL != peers) | ||
733 | { | ||
734 | GNUNET_CONTAINER_multipeermap_destroy (peers); | ||
735 | peers = NULL; | ||
736 | } | ||
737 | if (NULL != revocation_db) | ||
738 | { | ||
739 | GNUNET_DISK_file_close (revocation_db); | ||
740 | revocation_db = NULL; | ||
741 | } | ||
742 | GNUNET_CONTAINER_multihashmap_iterate (revocation_map, | ||
743 | &free_entry, | ||
744 | NULL); | ||
745 | GNUNET_CONTAINER_multihashmap_destroy (revocation_map); | ||
746 | } | ||
747 | |||
748 | |||
749 | /** | ||
750 | * Called on core init/fail. | ||
751 | * | ||
752 | * @param cls service closure | ||
753 | * @param identity the public identity of this peer | ||
754 | */ | ||
755 | static void | ||
756 | core_init (void *cls, | ||
757 | const struct GNUNET_PeerIdentity *identity) | ||
758 | { | ||
759 | if (NULL == identity) | ||
760 | { | ||
761 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
762 | "Connection to core FAILED!\n"); | ||
763 | GNUNET_SCHEDULER_shutdown (); | ||
764 | return; | ||
765 | } | ||
766 | my_identity = *identity; | ||
767 | } | ||
768 | |||
769 | |||
770 | /** | ||
771 | * Called when another peer wants to do a set operation with the | ||
772 | * local peer. If a listen error occurs, the 'request' is NULL. | ||
773 | * | ||
774 | * @param cls closure | ||
775 | * @param other_peer the other peer | ||
776 | * @param context_msg message with application specific information from | ||
777 | * the other peer | ||
778 | * @param request request from the other peer (never NULL), use GNUNET_SETU_accept() | ||
779 | * to accept it, otherwise the request will be refused | ||
780 | * Note that we can't just return value from the listen callback, | ||
781 | * as it is also necessary to specify the set we want to do the | ||
782 | * operation with, which sometimes can be derived from the context | ||
783 | * message. It's necessary to specify the timeout. | ||
784 | */ | ||
785 | static void | ||
786 | handle_revocation_union_request (void *cls, | ||
787 | const struct GNUNET_PeerIdentity *other_peer, | ||
788 | const struct GNUNET_MessageHeader *context_msg, | ||
789 | struct GNUNET_SETU_Request *request) | ||
790 | { | ||
791 | struct PeerEntry *peer_entry; | ||
792 | |||
793 | if (NULL == request) | ||
794 | { | ||
795 | GNUNET_break (0); | ||
796 | return; | ||
797 | } | ||
798 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
799 | "Received set exchange request from peer `%s'\n", | ||
800 | GNUNET_i2s (other_peer)); | ||
801 | peer_entry = GNUNET_CONTAINER_multipeermap_get (peers, | ||
802 | other_peer); | ||
803 | if (NULL == peer_entry) | ||
804 | { | ||
805 | peer_entry = new_peer_entry (other_peer); | ||
806 | } | ||
807 | if (NULL != peer_entry->so) | ||
808 | { | ||
809 | GNUNET_break_op (0); | ||
810 | return; | ||
811 | } | ||
812 | peer_entry->so = GNUNET_SETU_accept (request, | ||
813 | (struct GNUNET_SETU_Option[]) { { 0 } }, | ||
814 | &add_revocation, | ||
815 | peer_entry); | ||
816 | if (GNUNET_OK != | ||
817 | GNUNET_SETU_commit (peer_entry->so, | ||
818 | revocation_set)) | ||
819 | { | ||
820 | GNUNET_break (0); | ||
821 | GNUNET_SCHEDULER_shutdown (); | ||
822 | return; | ||
823 | } | ||
824 | } | ||
825 | |||
826 | |||
827 | /** | ||
828 | * Handle network size estimate clients. | ||
829 | * | ||
830 | * @param cls closure | ||
831 | * @param server the initialized server | ||
832 | * @param c configuration to use | ||
833 | */ | ||
834 | static void | ||
835 | run (void *cls, | ||
836 | const struct GNUNET_CONFIGURATION_Handle *c, | ||
837 | struct GNUNET_SERVICE_Handle *service) | ||
838 | { | ||
839 | struct GNUNET_MQ_MessageHandler core_handlers[] = { | ||
840 | GNUNET_MQ_hd_var_size (p2p_revoke, | ||
841 | GNUNET_MESSAGE_TYPE_REVOCATION_REVOKE, | ||
842 | struct RevokeMessage, | ||
843 | NULL), | ||
844 | GNUNET_MQ_handler_end () | ||
845 | }; | ||
846 | char *fn; | ||
847 | uint64_t left; | ||
848 | struct RevokeMessage *rm; | ||
849 | struct GNUNET_HashCode hc; | ||
850 | const struct GNUNET_IDENTITY_PublicKey *pk; | ||
851 | |||
852 | GNUNET_CRYPTO_hash ("revocation-set-union-application-id", | ||
853 | strlen ("revocation-set-union-application-id"), | ||
854 | &revocation_set_union_app_id); | ||
855 | if (GNUNET_OK != | ||
856 | GNUNET_CONFIGURATION_get_value_filename (c, | ||
857 | "REVOCATION", | ||
858 | "DATABASE", | ||
859 | &fn)) | ||
860 | { | ||
861 | GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, | ||
862 | "REVOCATION", | ||
863 | "DATABASE"); | ||
864 | GNUNET_SCHEDULER_shutdown (); | ||
865 | return; | ||
866 | } | ||
867 | cfg = c; | ||
868 | revocation_map = GNUNET_CONTAINER_multihashmap_create (16, | ||
869 | GNUNET_NO); | ||
870 | if (GNUNET_OK != | ||
871 | GNUNET_CONFIGURATION_get_value_number (cfg, | ||
872 | "REVOCATION", | ||
873 | "WORKBITS", | ||
874 | &revocation_work_required)) | ||
875 | { | ||
876 | GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, | ||
877 | "REVOCATION", | ||
878 | "WORKBITS"); | ||
879 | GNUNET_SCHEDULER_shutdown (); | ||
880 | GNUNET_free (fn); | ||
881 | return; | ||
882 | } | ||
883 | if (revocation_work_required >= sizeof(struct GNUNET_HashCode) * 8) | ||
884 | { | ||
885 | GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, | ||
886 | "REVOCATION", | ||
887 | "WORKBITS", | ||
888 | _ ("Value is too large.\n")); | ||
889 | GNUNET_SCHEDULER_shutdown (); | ||
890 | GNUNET_free (fn); | ||
891 | return; | ||
892 | } | ||
893 | if (GNUNET_OK != | ||
894 | GNUNET_CONFIGURATION_get_value_time (cfg, | ||
895 | "REVOCATION", | ||
896 | "EPOCH_DURATION", | ||
897 | &epoch_duration)) | ||
898 | { | ||
899 | GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, | ||
900 | "REVOCATION", | ||
901 | "EPOCH_DURATION"); | ||
902 | GNUNET_SCHEDULER_shutdown (); | ||
903 | GNUNET_free (fn); | ||
904 | return; | ||
905 | } | ||
906 | |||
907 | revocation_set = GNUNET_SETU_create (cfg); | ||
908 | revocation_union_listen_handle | ||
909 | = GNUNET_SETU_listen (cfg, | ||
910 | &revocation_set_union_app_id, | ||
911 | &handle_revocation_union_request, | ||
912 | NULL); | ||
913 | revocation_db = GNUNET_DISK_file_open (fn, | ||
914 | GNUNET_DISK_OPEN_READWRITE | ||
915 | | GNUNET_DISK_OPEN_CREATE, | ||
916 | GNUNET_DISK_PERM_USER_READ | ||
917 | | GNUNET_DISK_PERM_USER_WRITE | ||
918 | | GNUNET_DISK_PERM_GROUP_READ | ||
919 | | GNUNET_DISK_PERM_OTHER_READ); | ||
920 | if (NULL == revocation_db) | ||
921 | { | ||
922 | GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, | ||
923 | "REVOCATION", | ||
924 | "DATABASE", | ||
925 | _ ("Could not open revocation database file!")); | ||
926 | GNUNET_SCHEDULER_shutdown (); | ||
927 | GNUNET_free (fn); | ||
928 | return; | ||
929 | } | ||
930 | if (GNUNET_OK != | ||
931 | GNUNET_DISK_file_size (fn, &left, GNUNET_YES, GNUNET_YES)) | ||
932 | left = 0; | ||
933 | while (left > sizeof(struct RevokeMessage)) | ||
934 | { | ||
935 | rm = GNUNET_new (struct RevokeMessage); | ||
936 | if (sizeof(struct RevokeMessage) != | ||
937 | GNUNET_DISK_file_read (revocation_db, | ||
938 | rm, | ||
939 | sizeof(struct RevokeMessage))) | ||
940 | { | ||
941 | GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, | ||
942 | "read", | ||
943 | fn); | ||
944 | GNUNET_free (rm); | ||
945 | GNUNET_SCHEDULER_shutdown (); | ||
946 | GNUNET_free (fn); | ||
947 | return; | ||
948 | } | ||
949 | struct GNUNET_REVOCATION_PowP *pow = (struct | ||
950 | GNUNET_REVOCATION_PowP *) &rm[1]; | ||
951 | ssize_t ksize; | ||
952 | pk = (const struct GNUNET_IDENTITY_PublicKey *) &pow[1]; | ||
953 | ksize = GNUNET_IDENTITY_key_get_length (pk); | ||
954 | if (0 > ksize) | ||
955 | { | ||
956 | GNUNET_break_op (0); | ||
957 | GNUNET_free (rm); | ||
958 | GNUNET_free (fn); | ||
959 | return; | ||
960 | } | ||
961 | GNUNET_CRYPTO_hash (pk, | ||
962 | ksize, | ||
963 | &hc); | ||
964 | GNUNET_break (GNUNET_OK == | ||
965 | GNUNET_CONTAINER_multihashmap_put (revocation_map, | ||
966 | &hc, | ||
967 | rm, | ||
968 | GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)); | ||
969 | } | ||
970 | GNUNET_free (fn); | ||
971 | |||
972 | GNUNET_SCHEDULER_add_shutdown (&shutdown_task, | ||
973 | NULL); | ||
974 | peers = GNUNET_CONTAINER_multipeermap_create (128, | ||
975 | GNUNET_YES); | ||
976 | /* Connect to core service and register core handlers */ | ||
977 | core_api = GNUNET_CORE_connect (cfg, /* Main configuration */ | ||
978 | NULL, /* Closure passed to functions */ | ||
979 | &core_init, /* Call core_init once connected */ | ||
980 | &handle_core_connect, /* Handle connects */ | ||
981 | &handle_core_disconnect, /* Handle disconnects */ | ||
982 | core_handlers); /* Register these handlers */ | ||
983 | if (NULL == core_api) | ||
984 | { | ||
985 | GNUNET_SCHEDULER_shutdown (); | ||
986 | return; | ||
987 | } | ||
988 | stats = GNUNET_STATISTICS_create ("revocation", | ||
989 | cfg); | ||
990 | } | ||
991 | |||
992 | |||
993 | /** | ||
994 | * Define "main" method using service macro. | ||
995 | */ | ||
996 | GNUNET_SERVICE_MAIN | ||
997 | ("revocation", | ||
998 | GNUNET_SERVICE_OPTION_NONE, | ||
999 | &run, | ||
1000 | &client_connect_cb, | ||
1001 | &client_disconnect_cb, | ||
1002 | NULL, | ||
1003 | GNUNET_MQ_hd_fixed_size (query_message, | ||
1004 | GNUNET_MESSAGE_TYPE_REVOCATION_QUERY, | ||
1005 | struct QueryMessage, | ||
1006 | NULL), | ||
1007 | GNUNET_MQ_hd_var_size (revoke_message, | ||
1008 | GNUNET_MESSAGE_TYPE_REVOCATION_REVOKE, | ||
1009 | struct RevokeMessage, | ||
1010 | NULL), | ||
1011 | GNUNET_MQ_handler_end ()); | ||
1012 | |||
1013 | |||
1014 | #if defined(__linux__) && defined(__GLIBC__) | ||
1015 | #include <malloc.h> | ||
1016 | |||
1017 | /** | ||
1018 | * MINIMIZE heap size (way below 128k) since this process doesn't need much. | ||
1019 | */ | ||
1020 | void __attribute__ ((constructor)) | ||
1021 | GNUNET_REVOCATION_memory_init () | ||
1022 | { | ||
1023 | mallopt (M_TRIM_THRESHOLD, 4 * 1024); | ||
1024 | mallopt (M_TOP_PAD, 1 * 1024); | ||
1025 | malloc_trim (0); | ||
1026 | } | ||
1027 | |||
1028 | |||
1029 | #endif | ||
1030 | |||
1031 | |||
1032 | /* end of gnunet-service-revocation.c */ | ||