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