From 0776e069124728b5f62c01b6b6482660a1b38c2d Mon Sep 17 00:00:00 2001 From: Logan007 Date: Sat, 25 Jul 2020 18:13:20 +0545 Subject: [PATCH] added replay protection --- include/n2n.h | 3 +- src/edge_utils.c | 85 +++++++++++++++++++++++++++++++++++++++++------- src/n2n.c | 59 ++++++++++++++++++--------------- src/sn_utils.c | 62 +++++++++++++++++++++++++++++++++++ 4 files changed, 171 insertions(+), 38 deletions(-) diff --git a/include/n2n.h b/include/n2n.h index 6251ef7..bd0d4f6 100644 --- a/include/n2n.h +++ b/include/n2n.h @@ -432,7 +432,8 @@ int sock_equal( const n2n_sock_t * a, /* Header encryption */ uint64_t time_stamp(void); -int time_stamp_verify (uint64_t stamp, uint64_t * previous_stamp); +uint64_t initial_time_stamp (void); +int time_stamp_verify_and_update (uint64_t stamp, uint64_t * previous_stamp); /* Operations on peer_info lists. */ size_t purge_peer_list( struct peer_info ** peer_list, diff --git a/src/edge_utils.c b/src/edge_utils.c index 7f78d50..c49a0c5 100644 --- a/src/edge_utils.c +++ b/src/edge_utils.c @@ -193,6 +193,7 @@ n2n_edge_t* edge_init(const tuntap_dev *dev, const n2n_edge_conf_t *conf, int *r eee->known_peers = NULL; eee->pending_peers = NULL; eee->sup_attempts = N2N_EDGE_SUP_ATTEMPTS; + eee->sn_last_valid_time_stamp = initial_time_stamp (); pearson_hash_init(); @@ -380,6 +381,42 @@ static int supernode2addr(n2n_sock_t * sn, const n2n_sn_name_t addrIn) { /* ************************************** */ +static const int definitely_from_supernode = 1; + +/*** + * + * For a given packet, find the apporopriate internal last valid time stamp for lookup + * and verify it (and also update, if applicable). + */ +static int find_peer_time_stamp_and_verify (n2n_edge_t * eee, + int from_supernode, n2n_mac_t mac, + uint64_t stamp) { + + uint64_t * previous_stamp = NULL; + + if(from_supernode) { + // from supernode + previous_stamp = &(eee->sn_last_valid_time_stamp); + } else { + // from (peer) edge + struct peer_info *peer; + HASH_FIND_PEER(eee->pending_peers, mac, peer); + if(!peer) { + HASH_FIND_PEER(eee->known_peers, mac, peer); + } + if(peer) { + // time_stamp_verify_and_update allows the pointer a previous stamp to be NULL + // if it is a (so far) unknown peer + previous_stamp = &(peer->last_valid_time_stamp); + } + } + + // failure --> 0; success --> 1 + return ( time_stamp_verify_and_update (stamp, previous_stamp) ); +} + +/* ************************************** */ + /*** * * Register over multicast in case there is a peer on the same network listening @@ -431,6 +468,7 @@ static void register_with_new_peer(n2n_edge_t * eee, scan->sock = *peer; scan->timeout = REGISTER_SUPER_INTERVAL_DFL; /* TODO: should correspond to the peer supernode registration timeout */ scan->last_seen = time(NULL); /* Don't change this it marks the pending peer for removal. */ + scan->last_valid_time_stamp = initial_time_stamp (); HASH_ADD_PEER(eee->pending_peers, scan); @@ -1245,6 +1283,7 @@ static int check_query_peer_info(n2n_edge_t *eee, time_t now, n2n_mac_t mac) { memcpy(scan->mac_addr, mac, N2N_MAC_SIZE); scan->timeout = REGISTER_SUPER_INTERVAL_DFL; /* TODO: should correspond to the peer supernode registration timeout */ scan->last_seen = now; /* Don't change this it marks the pending peer for removal. */ + scan->last_valid_time_stamp = initial_time_stamp (); HASH_ADD_PEER(eee->pending_peers, scan); } @@ -1646,19 +1685,15 @@ static void readFromIPSocket(n2n_edge_t * eee, int in_sock) { decode_PACKET(&pkt, &cmn, udp_buf, &rem, &idx); - if(is_valid_peer_sock(&pkt.sock)) - orig_sender = &(pkt.sock); - -/* // sketch for time stamp verification -- to be implemented !!! - if(eee->conf.header_encryption == HEADER_ENCRYPTION_ENABLED) { - // find edge and its specific last time stamp or supernode's one !!! - if ( !time_stamp_verify (stamp, &found_time_stamp !!!) ) { - traceEvent(TRACE_DEBUG, "readFromIPSocket dropped packet due to time stamp error."); + if(!find_peer_time_stamp_and_verify (eee, from_supernode, pkt.srcMac, stamp)) { + traceEvent(TRACE_DEBUG, "readFromIPSocket dropped PACKET due to time stamp error."); return; - } - } -*/ + } + } + + if(is_valid_peer_sock(&pkt.sock)) + orig_sender = &(pkt.sock); if(!from_supernode) { /* This is a P2P packet from the peer. We purge a pending @@ -1686,6 +1721,13 @@ static void readFromIPSocket(n2n_edge_t * eee, int in_sock) { decode_REGISTER(®, &cmn, udp_buf, &rem, &idx); + if(eee->conf.header_encryption == HEADER_ENCRYPTION_ENABLED) { + if(!find_peer_time_stamp_and_verify (eee, from_supernode, reg.srcMac, stamp)) { + traceEvent(TRACE_DEBUG, "readFromIPSocket dropped REGISTER due to time stamp error."); + return; + } + } + if(is_valid_peer_sock(®.sock)) orig_sender = &(reg.sock); @@ -1730,6 +1772,13 @@ static void readFromIPSocket(n2n_edge_t * eee, int in_sock) { decode_REGISTER_ACK(&ra, &cmn, udp_buf, &rem, &idx); + if(eee->conf.header_encryption == HEADER_ENCRYPTION_ENABLED) { + if(!find_peer_time_stamp_and_verify (eee, !definitely_from_supernode, ra.srcMac, stamp)) { + traceEvent(TRACE_DEBUG, "readFromIPSocket dropped REGISTER_ACK due to time stamp error."); + return; + } + } + if(is_valid_peer_sock(&ra.sock)) orig_sender = &(ra.sock); @@ -1750,6 +1799,13 @@ static void readFromIPSocket(n2n_edge_t * eee, int in_sock) { { decode_REGISTER_SUPER_ACK(&ra, &cmn, udp_buf, &rem, &idx); + if(eee->conf.header_encryption == HEADER_ENCRYPTION_ENABLED) { + if(!find_peer_time_stamp_and_verify (eee, definitely_from_supernode, null_mac, stamp)) { + traceEvent(TRACE_DEBUG, "readFromIPSocket dropped REGISTER_SUPER_ACK due to time stamp error."); + return; + } + } + if(is_valid_peer_sock(&ra.sock)) orig_sender = &(ra.sock); @@ -1793,6 +1849,13 @@ static void readFromIPSocket(n2n_edge_t * eee, int in_sock) { struct peer_info * scan; decode_PEER_INFO( &pi, &cmn, udp_buf, &rem, &idx ); + if(eee->conf.header_encryption == HEADER_ENCRYPTION_ENABLED) { + if(!find_peer_time_stamp_and_verify (eee, definitely_from_supernode, null_mac, stamp)) { + traceEvent(TRACE_DEBUG, "readFromIPSocket dropped PEER_INFO due to time stamp error."); + return; + } + } + if(!is_valid_peer_sock(&pi.sock)) { traceEvent(TRACE_DEBUG, "Skip invalid PEER_INFO %s [%s]", sock_to_cstr(sockbuf1, &pi.sock), diff --git a/src/n2n.c b/src/n2n.c index 59f27db..203b58c 100644 --- a/src/n2n.c +++ b/src/n2n.c @@ -26,7 +26,8 @@ #define REGISTRATION_TIMEOUT 60 #define TIME_STAMP_FRAME 0x0000001000000000LL /* clocks of different computers are allowed +/- 16 seconds to be off */ -#define TIME_STAMP_JITTER 0x0000000027100000LL /* we allow a packet to arrive 160 ms (== 0x27100 us) before another */ +#define TIME_STAMP_JITTER 0x0000000027100000LL /* we allow a packet to arrive 160 ms (== 0x27100 us) before another + * set to 0x0000000000000000LL if increasing (or equal) time stamps allowed only */ static const uint8_t broadcast_addr[6] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; static const uint8_t multicast_addr[6] = { 0x01, 0x00, 0x5E, 0x00, 0x00, 0x00 }; /* First 3 bytes are meaningful */ @@ -383,7 +384,6 @@ int sock_equal(const n2n_sock_t * a, /* *********************************************** */ #if defined(WIN32) && !defined(__GNUC__) -// REVISIT during the years 2035...2038 int gettimeofday(struct timeval *tp, void *tzp) { time_t clock; struct tm tm; @@ -405,7 +405,6 @@ int gettimeofday(struct timeval *tp, void *tzp) { // returns a time stamp for use with replay protection -// REVISIT during the years 2035...2038 uint64_t time_stamp (void) { struct timeval tod; @@ -426,36 +425,44 @@ uint64_t time_stamp (void) { } -// checks if a provided time stamp is consistent with current time and previously valid time stamps -// and, in case of validity, replaces the "last valid time stamp" -// REVISIT during the years 2035...2038 -int time_stamp_verify (uint64_t stamp, uint64_t * previous_stamp) { +// returns an initial time stamp for use with replay protection +uint64_t initial_time_stamp (void) { - int64_t diff; // do not change to unsigned + return ( time_stamp() - TIME_STAMP_FRAME ); +} - // is it higher than previous time stamp (including allowed deviation of TIME_STAMP_JITTER)? - diff = stamp - *previous_stamp + TIME_STAMP_JITTER; - if(diff > 0) { - // is it around current time (+/- allowed deviation TIME_STAMP_FRAME)? - diff = stamp - time_stamp(); - // abs() - diff = (diff < 0 ? -diff : diff); +// checks if a provided time stamp is consistent with current time and previously valid time stamps +// and, in case of validity, updates the "last valid time stamp" +int time_stamp_verify_and_update (uint64_t stamp, uint64_t * previous_stamp) { - if(diff < TIME_STAMP_FRAME) { + int64_t diff; // do not change to unsigned - // for not allowing to exploit the allowed TIME_STAMP_JITTER to "turn the clock backwards", - // set the higher of the values - *previous_stamp = (stamp > *previous_stamp ? stamp : *previous_stamp); - return (1); // success + // is it around current time (+/- allowed deviation TIME_STAMP_FRAME)? + diff = stamp - time_stamp(); + // abs() + diff = (diff < 0 ? -diff : diff); + if(diff >= TIME_STAMP_FRAME) { + traceEvent(TRACE_DEBUG, "time_stamp_verify_and_update found a timestamp out of allowed frame."); + return (0); // failure + } - } else { - traceEvent(TRACE_DEBUG, "time_stamp_verify found a timestamp out of allowed frame."); + // if applicable: is it higher than previous time stamp (including allowed deviation of TIME_STAMP_JITTER)? + if(NULL != previous_stamp) { + // if no jitter allowed, reset lowest three (random) nybbles; the codnition shoudl already be evaluated by the compiler + if(TIME_STAMP_JITTER == 0) { + stamp = (stamp >> 12) << 12; + *previous_stamp = (*previous_stamp >> 12) << 12; + } + diff = stamp - *previous_stamp + TIME_STAMP_JITTER; + if(diff <= 0) { + traceEvent(TRACE_DEBUG, "time_stamp_verify_and_update found a timestamp too old compared to previous."); return (0); // failure } - - } else { - traceEvent(TRACE_DEBUG, "time_stamp_verify found a timestamp too old / older than previous."); - return (0); // failure + // for not allowing to exploit the allowed TIME_STAMP_JITTER to "turn the clock backwards", + // set the higher of the values + *previous_stamp = (stamp > *previous_stamp ? stamp : *previous_stamp); } + + return (1); // success } diff --git a/src/sn_utils.c b/src/sn_utils.c index 8a9896d..85fea28 100644 --- a/src/sn_utils.c +++ b/src/sn_utils.c @@ -41,6 +41,8 @@ static int process_udp(n2n_sn_t *sss, size_t udp_size, time_t now); +/* ************************************** */ + static int try_forward(n2n_sn_t * sss, const struct sn_community *comm, const n2n_common_t * cmn, @@ -255,6 +257,7 @@ static int update_edge(n2n_sn_t *sss, memcpy(&(scan->mac_addr), edgeMac, sizeof(n2n_mac_t)); memcpy(&(scan->sock), sender_sock, sizeof(n2n_sock_t)); + scan->last_valid_time_stamp = initial_time_stamp (); HASH_ADD_PEER(comm->edges, scan); @@ -285,6 +288,32 @@ static int update_edge(n2n_sn_t *sss, return 0; } +/*** + * + * For a given packet, find the apporopriate internal last valid time stamp for lookup + * and verify it (and also update, if applicable). + */ +static int find_edge_time_stamp_and_verify (struct peer_info * edges, + int from_supernode, n2n_mac_t mac, + uint64_t stamp) { + + uint64_t * previous_stamp = NULL; + + if(!from_supernode) { + struct peer_info *edge; + HASH_FIND_PEER(edges, mac, edge); + if(edge) { + // time_stamp_verify_and_update allows the pointer a previous stamp to be NULL + // if it is a (so far) unknown edge + previous_stamp = &(edge->last_valid_time_stamp); + } + } + + // failure --> 0; success --> 1 + return ( time_stamp_verify_and_update (stamp, previous_stamp) ); +} + + static int process_mgmt(n2n_sn_t *sss, const struct sockaddr_in *sender_sock, const uint8_t *mgmt_buf, @@ -512,6 +541,14 @@ static int process_udp(n2n_sn_t * sss, sss->stats.last_fwd=now; decode_PACKET(&pkt, &cmn, udp_buf, &rem, &idx); + // already checked for valid comm + if(comm->header_encryption == HEADER_ENCRYPTION_ENABLED) { + if(!find_edge_time_stamp_and_verify (comm->edges, from_supernode, pkt.srcMac, stamp)) { + traceEvent(TRACE_DEBUG, "process_udp dropped PACKET due to time stamp error."); + return -1; + } + } + unicast = (0 == is_multi_broadcast(pkt.dstMac)); traceEvent(TRACE_DEBUG, "RX PACKET (%s) %s -> %s %s", @@ -585,6 +622,14 @@ static int process_udp(n2n_sn_t * sss, sss->stats.last_fwd=now; decode_REGISTER(®, &cmn, udp_buf, &rem, &idx); + // already checked for valid comm + if(comm->header_encryption == HEADER_ENCRYPTION_ENABLED) { + if(!find_edge_time_stamp_and_verify (comm->edges, from_supernode, reg.srcMac, stamp)) { + traceEvent(TRACE_DEBUG, "process_udp dropped REGISTER due to time stamp error."); + return -1; + } + } + unicast = (0 == is_multi_broadcast(reg.dstMac)); if(unicast) { @@ -641,6 +686,15 @@ static int process_udp(n2n_sn_t * sss, ++(sss->stats.reg_super); decode_REGISTER_SUPER(®, &cmn, udp_buf, &rem, &idx); + if (comm) { + if(comm->header_encryption == HEADER_ENCRYPTION_ENABLED) { + if(!find_edge_time_stamp_and_verify (comm->edges, from_supernode, reg.edgeMac, stamp)) { + traceEvent(TRACE_DEBUG, "process_udp dropped REGISTER_SUPER due to time stamp error."); + return -1; + } + } + } + /* Before we move any further, we need to check if the requested community is allowed by the supernode. In case it is not we do @@ -718,6 +772,14 @@ static int process_udp(n2n_sn_t * sss, decode_QUERY_PEER( &query, &cmn, udp_buf, &rem, &idx ); + // already checked for valid comm + if(comm->header_encryption == HEADER_ENCRYPTION_ENABLED) { + if(!find_edge_time_stamp_and_verify (comm->edges, from_supernode, query.srcMac, stamp)) { + traceEvent(TRACE_DEBUG, "process_udp dropped QUERY_PEER due to time stamp error."); + return -1; + } + } + traceEvent( TRACE_DEBUG, "Rx QUERY_PEER from %s for %s", macaddr_str( mac_buf, query.srcMac ), macaddr_str( mac_buf2, query.targetMac ) );