From c847f8c507d9d5af0c9c1398386f131563c3ae3e Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Thu, 25 Apr 2019 13:44:49 +0200 Subject: [PATCH] implement DV learn monotime handling --- src/include/gnunet_peerstore_service.h | 8 ++ src/transport/gnunet-service-tng.c | 172 +++++++++++++++++++++++-- 2 files changed, 166 insertions(+), 14 deletions(-) diff --git a/src/include/gnunet_peerstore_service.h b/src/include/gnunet_peerstore_service.h index b20c1f3c7..ad80a3fa3 100644 --- a/src/include/gnunet_peerstore_service.h +++ b/src/include/gnunet_peerstore_service.h @@ -60,6 +60,14 @@ extern "C" { #define GNUNET_PEERSTORE_TRANSPORT_BACKCHANNEL_MONOTIME \ "transport-backchannel-monotonic-time" +/** + * Key used to store sender's monotonic time from DV learn + * messages. + */ +#define GNUNET_PEERSTORE_TRANSPORT_DVLEARN_MONOTIME \ + "transport-dv-learn-monotonic-time" + + /** * Options for storing values in PEERSTORE */ diff --git a/src/transport/gnunet-service-tng.c b/src/transport/gnunet-service-tng.c index baf443f03..9fb8141bf 100644 --- a/src/transport/gnunet-service-tng.c +++ b/src/transport/gnunet-service-tng.c @@ -24,12 +24,6 @@ * * TODO: * Implement next: - * - FIXME: handle_client_send(): pick DVH path, and box - * message accordingly (if applicable, see FIXMEs) - * - proper use/initialization of timestamps in messages exchanged - * during DV learning - * - persistence of monotonic time from DVInit to prevent - * replay attacks using DVInit messages * - dv hop-by-hop signature verification (at least at initiator) * - persistence of monotonic time obtained from other peers * in PEERSTORE (by message type) -- done for backchannel, needed elsewhere? @@ -719,6 +713,20 @@ struct TransportDVLearnMessage */ struct GNUNET_TIME_RelativeNBO non_network_delay; + /** + * Time at the initiator when generating the signature. + * + * Note that the receiver MUST IGNORE the absolute time, and only interpret + * the value as a mononic time and reject "older" values than the last one + * observed. This is necessary as we do not want to require synchronized + * clocks and may not have a bidirectional communication channel. + * + * Even with this, there is no real guarantee against replay achieved here, + * unless the latest timestamp is persisted. Persistence should be + * provided via PEERSTORE if possible. + */ + struct GNUNET_TIME_AbsoluteNBO monotonic_time; + /** * Signature of this hop over the path, of purpose * #GNUNET_SIGNATURE_PURPOSE_TRANSPORT_DV_INITIATOR @@ -1663,6 +1671,18 @@ struct Neighbour */ struct GNUNET_SCHEDULER_Task *timeout_task; + /** + * Handle for an operation to fetch @e last_dv_learn_monotime information from + * the PEERSTORE, or NULL. + */ + struct GNUNET_PEERSTORE_IterateContext *get; + + /** + * Handle to a PEERSTORE store operation to store this @e pid's @e + * @e last_dv_learn_monotime. NULL if no PEERSTORE operation is pending. + */ + struct GNUNET_PEERSTORE_StoreContext *sc; + /** * Quota at which CORE is allowed to transmit to this peer * (note that the value CORE should actually be told is this @@ -1677,6 +1697,12 @@ struct Neighbour */ struct GNUNET_BANDWIDTH_Value32NBO quota_out; + /** + * Latest DVLearn monotonic time seen from this peer. Initialized only + * if @e dl_monotime_available is #GNUNET_YES. + */ + struct GNUNET_TIME_Absolute last_dv_learn_monotime; + /** * What is the earliest timeout of any message in @e pending_msg_tail? */ @@ -1687,6 +1713,12 @@ struct Neighbour * CORE? */ int core_visible; + + /** + * Do we have the lastest value for @e last_dv_learn_monotime from + * PEERSTORE yet, or are we still waiting for a reply of PEERSTORE? + */ + int dv_monotime_available; }; @@ -2886,7 +2918,20 @@ free_neighbour (struct Neighbour *neighbour) free_dv_route (dv); } if (NULL != neighbour->reassembly_timeout_task) + { GNUNET_SCHEDULER_cancel (neighbour->reassembly_timeout_task); + neighbour->reassembly_timeout_task = NULL; + } + if (NULL != neighbour->get) + { + GNUNET_PEERSTORE_iterate_cancel (neighbour->get); + neighbour->get = NULL; + } + if (NULL != neighbour->sc) + { + GNUNET_PEERSTORE_store_cancel (neighbour->sc); + neighbour->sc = NULL; + } GNUNET_free (neighbour); } @@ -6004,6 +6049,7 @@ forward_dv_learn (const struct GNUNET_PeerIdentity *next_hop, /** * Check signature of type #GNUNET_SIGNATURE_PURPOSE_TRANSPORT_DV_INITIATOR * + * @param sender_monotonic_time monotonic time of the initiator * @param init the signer * @param challenge the challenge that was signed * @param init_sig signature presumably by @a init @@ -6011,6 +6057,7 @@ forward_dv_learn (const struct GNUNET_PeerIdentity *next_hop, */ static int validate_dv_initiator_signature ( + struct GNUNET_TIME_AbsoluteNBO sender_monotonic_time, const struct GNUNET_PeerIdentity *init, const struct ChallengeNonceP *challenge, const struct GNUNET_CRYPTO_EddsaSignature *init_sig) @@ -6018,6 +6065,7 @@ validate_dv_initiator_signature ( struct DvInitPS ip = {.purpose.purpose = htonl ( GNUNET_SIGNATURE_PURPOSE_TRANSPORT_DV_INITIATOR), .purpose.size = htonl (sizeof (ip)), + .monotonic_time = sender_monotonic_time, .challenge = *challenge}; if ( @@ -6223,6 +6271,24 @@ calculate_fork_degree (unsigned int hops_taken, } +/** + * Function called when peerstore is done storing a DV monotonic time. + * + * @param cls a `struct Neighbour` + * @param success #GNUNET_YES if peerstore was successful + */ +static void +neighbour_store_dvmono_cb (void *cls, int success) +{ + struct Neighbour *n = cls; + + n->sc = NULL; + if (GNUNET_YES != success) + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Failed to store other peer's monotonic time in peerstore!\n"); +} + + /** * Communicator gave us a DV learn message. Process the request. * @@ -6242,6 +6308,7 @@ handle_dv_learn (void *cls, const struct TransportDVLearnMessage *dvl) int do_fwd; int did_initiator; struct GNUNET_TIME_Absolute in_time; + struct Neighbour *n; nhops = ntohs (dvl->bidirectional); /* 0 = sender is initiator */ bi_history = ntohs (dvl->bidirectional); @@ -6276,15 +6343,44 @@ handle_dv_learn (void *cls, const struct TransportDVLearnMessage *dvl) /* continue communicator here, everything else can happen asynchronous! */ finish_cmc_handling (cmc); - /* OPTIMIZE-FIXME: Technically, we only need to bother checking - the initiator signature if we send the message back to the initiator... - */ - if (GNUNET_OK != validate_dv_initiator_signature (&dvl->initiator, - &dvl->challenge, - &dvl->init_sig)) + n = lookup_neighbour (&dvl->initiator); + if (NULL != n) { - GNUNET_break_op (0); - return; + if ((n->dv_monotime_available == GNUNET_YES) && + (GNUNET_TIME_absolute_ntoh (dvl->monotonic_time).abs_value_us < + n->last_dv_learn_monotime.abs_value_us)) + { + GNUNET_STATISTICS_update (GST_stats, + "# DV learn discarded due to time travel", + 1, + GNUNET_NO); + return; + } + if (GNUNET_OK != validate_dv_initiator_signature (dvl->monotonic_time, + &dvl->initiator, + &dvl->challenge, + &dvl->init_sig)) + { + GNUNET_break_op (0); + return; + } + n->last_dv_learn_monotime = GNUNET_TIME_absolute_ntoh (dvl->monotonic_time); + if (GNUNET_YES == n->dv_monotime_available) + { + if (NULL != n->sc) + GNUNET_PEERSTORE_store_cancel (n->sc); + n->sc = + GNUNET_PEERSTORE_store (peerstore, + "transport", + &dvl->initiator, + GNUNET_PEERSTORE_TRANSPORT_DVLEARN_MONOTIME, + &dvl->monotonic_time, + sizeof (dvl->monotonic_time), + GNUNET_TIME_UNIT_FOREVER_ABS, + GNUNET_PEERSTORE_STOREOPTION_REPLACE, + &neighbour_store_dvmono_cb, + n); + } } // FIXME: asynchronously (!) verify hop-by-hop signatures! // => if signature verification load too high, implement random drop @@ -8057,10 +8153,13 @@ start_dv_learn (void *cls) dvl.num_hops = htons (0); dvl.bidirectional = htons (0); dvl.non_network_delay = GNUNET_TIME_relative_hton (GNUNET_TIME_UNIT_ZERO); + dvl.monotonic_time = + GNUNET_TIME_absolute_hton (GNUNET_TIME_absolute_get_monotonic (GST_cfg)); { struct DvInitPS dvip = {.purpose.purpose = htonl ( GNUNET_SIGNATURE_PURPOSE_TRANSPORT_DV_INITIATOR), .purpose.size = htonl (sizeof (dvip)), + .monotonic_time = dvl.monotonic_time, .challenge = lle->challenge}; GNUNET_assert (GNUNET_OK == GNUNET_CRYPTO_eddsa_sign (GST_my_private_key, @@ -8121,6 +8220,44 @@ check_validation_request_pending (void *cls, } +/** + * Function called with the monotonic time of a DV initiator + * by PEERSTORE. Updates the time. + * + * @param cls a `struct Neighbour` + * @param record the information found, NULL for the last call + * @param emsg error message + */ +static void +neighbour_dv_monotime_cb (void *cls, + const struct GNUNET_PEERSTORE_Record *record, + const char *emsg) +{ + struct Neighbour *n = cls; + struct GNUNET_TIME_AbsoluteNBO *mtbe; + struct GNUNET_TIME_Absolute mt; + + (void) emsg; + if (NULL == record) + { + /* we're done with #neighbour_dv_monotime_cb() invocations, + continue normal processing */ + n->get = NULL; + n->dv_monotime_available = GNUNET_YES; + return; + } + if (sizeof (*mtbe) != record->value_size) + { + GNUNET_break (0); + return; + } + mtbe = record->value; + n->last_dv_learn_monotime = + GNUNET_TIME_absolute_max (n->last_dv_learn_monotime, + GNUNET_TIME_absolute_ntoh (*mtbe)); +} + + /** * New queue became available. Process the request. * @@ -8157,6 +8294,13 @@ handle_add_queue_message (void *cls, &neighbour->pid, neighbour, GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)); + neighbour->get = + GNUNET_PEERSTORE_iterate (peerstore, + "transport", + &neighbour->pid, + GNUNET_PEERSTORE_TRANSPORT_DVLEARN_MONOTIME, + &neighbour_dv_monotime_cb, + neighbour); } addr_len = ntohs (aqm->header.size) - sizeof (*aqm); addr = (const char *) &aqm[1]; -- 2.25.1