diff --git a/doc/Crypto.md b/doc/Crypto.md index 3288a05..f7f4fc6 100644 --- a/doc/Crypto.md +++ b/doc/Crypto.md @@ -191,6 +191,8 @@ Upon receival, the time stamp as well as the checksum can be extracted from the - The (remote) time stamp is checked against the local clock. It may not deviate more than plus/minus 16 seconds. So, edges and supernode need to keep a somewhat current time. This limit can be adjusted by changing the `TIME_STAMP_FRAME` definition. It is time-zone indifferent as UTC is used. +- However, the systemic packets such as REGISTER_SUPER are not allowed any time stamp jitter because n2n relies on the actual sender's socket. A replay from another IP within any allowed jitter time frame would deviate the traffic which shall be prevented (even if it remains undecryptable). Under absolutely rare (!) circumstances, this might cause a re-registration requirement which happens automatically but might cause a small delay – security (including network availability) first! + - Valid (remote) time stamps get stored as "last valid time stamp" seen from each node (supernode and edges). So, a newly arriving packet's time stamp can be compared to the last valid one. It should be equal or higher. However, as UDP packets may overtake each other just by taking another path through the internet, they are allowed to be 160 millisecond earlier than the last valid one. This limit can be adjusted by changing the `TIME_STAMP_JITTER` definition. The way the IV is used for replay protection and for checksumming makes enabled header encryption a prerequisite for these features. diff --git a/include/n2n.h b/include/n2n.h index 3513b6a..ee48306 100644 --- a/include/n2n.h +++ b/include/n2n.h @@ -459,7 +459,7 @@ int sock_equal( const n2n_sock_t * a, /* Header encryption */ uint64_t time_stamp(void); uint64_t initial_time_stamp (void); -int time_stamp_verify_and_update (uint64_t stamp, uint64_t * previous_stamp); +int time_stamp_verify_and_update (uint64_t stamp, uint64_t * previous_stamp, int allow_jitter); /* Operations on peer_info lists. */ size_t purge_peer_list( struct peer_info ** peer_list, diff --git a/include/n2n_define.h b/include/n2n_define.h index 19bbfea..96dd01a 100644 --- a/include/n2n_define.h +++ b/include/n2n_define.h @@ -50,6 +50,8 @@ #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 * set to 0x0000000000000000LL if increasing (or equal) time stamps allowed only */ +#define TIME_STAMP_ALLOW_JITTER 1 /* constant for allowing or... */ +#define TIME_STAMP_NO_JITTER 0 /* not allowing jitter to be considered */ /* N2N compression indicators. */ /* Compression is disabled by default for outgoing packets if no cli diff --git a/src/edge_utils.c b/src/edge_utils.c index 205f155..a2cb8b0 100644 --- a/src/edge_utils.c +++ b/src/edge_utils.c @@ -394,7 +394,7 @@ static const int definitely_from_supernode = 1; */ static int find_peer_time_stamp_and_verify (n2n_edge_t * eee, int from_supernode, const n2n_mac_t mac, - uint64_t stamp) { + uint64_t stamp, int allow_jitter) { uint64_t * previous_stamp = NULL; @@ -416,7 +416,7 @@ static int find_peer_time_stamp_and_verify (n2n_edge_t * eee, } // failure --> 0; success --> 1 - return ( time_stamp_verify_and_update (stamp, previous_stamp) ); + return ( time_stamp_verify_and_update (stamp, previous_stamp, allow_jitter) ); } /* ************************************** */ @@ -1787,7 +1787,7 @@ void readFromIPSocket(n2n_edge_t * eee, int in_sock) { decode_PACKET(&pkt, &cmn, udp_buf, &rem, &idx); if(eee->conf.header_encryption == HEADER_ENCRYPTION_ENABLED) { - if(!find_peer_time_stamp_and_verify (eee, from_supernode, pkt.srcMac, stamp)) { + if(!find_peer_time_stamp_and_verify (eee, from_supernode, pkt.srcMac, stamp, TIME_STAMP_ALLOW_JITTER)) { traceEvent(TRACE_DEBUG, "readFromIPSocket dropped PACKET due to time stamp error."); return; } @@ -1827,7 +1827,7 @@ 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)) { + if(!find_peer_time_stamp_and_verify (eee, from_supernode, reg.srcMac, stamp, TIME_STAMP_NO_JITTER)) { traceEvent(TRACE_DEBUG, "readFromIPSocket dropped REGISTER due to time stamp error."); return; } @@ -1878,7 +1878,7 @@ 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)) { + if(!find_peer_time_stamp_and_verify (eee, !definitely_from_supernode, ra.srcMac, stamp, TIME_STAMP_NO_JITTER)) { traceEvent(TRACE_DEBUG, "readFromIPSocket dropped REGISTER_ACK due to time stamp error."); return; } @@ -1918,7 +1918,7 @@ 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)) { + if(!find_peer_time_stamp_and_verify (eee, definitely_from_supernode, null_mac, stamp, TIME_STAMP_NO_JITTER)) { traceEvent(TRACE_DEBUG, "readFromIPSocket dropped REGISTER_SUPER_ACK due to time stamp error."); return; } @@ -1988,7 +1988,7 @@ void readFromIPSocket(n2n_edge_t * eee, int in_sock) { 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)) { + if(!find_peer_time_stamp_and_verify (eee, definitely_from_supernode, null_mac, stamp, TIME_STAMP_ALLOW_JITTER)) { traceEvent(TRACE_DEBUG, "readFromIPSocket dropped PEER_INFO due to time stamp error."); return; } diff --git a/src/n2n.c b/src/n2n.c index 4c877e6..554fae2 100644 --- a/src/n2n.c +++ b/src/n2n.c @@ -468,7 +468,7 @@ uint64_t initial_time_stamp (void) { // 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) { +int time_stamp_verify_and_update (uint64_t stamp, uint64_t * previous_stamp, int allow_jitter) { int64_t diff; // do not change to unsigned @@ -483,12 +483,14 @@ int time_stamp_verify_and_update (uint64_t stamp, uint64_t * previous_stamp) { // 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; + // always reset lowest three (random) nybbles -- important in case of no jitter, do not if() to avoid jumping + stamp = (stamp >> 12) << 12; + *previous_stamp = (*previous_stamp >> 12) << 12; + + diff = stamp - *previous_stamp; + if (allow_jitter) + diff += 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 diff --git a/src/sn_utils.c b/src/sn_utils.c index 86d7e11..b2cc1c8 100644 --- a/src/sn_utils.c +++ b/src/sn_utils.c @@ -456,7 +456,7 @@ int assign_one_ip_subnet(n2n_sn_t *sss, */ static int find_edge_time_stamp_and_verify (struct peer_info * edges, int from_supernode, n2n_mac_t mac, - uint64_t stamp) { + uint64_t stamp, int allow_jitter) { uint64_t * previous_stamp = NULL; @@ -471,7 +471,7 @@ static int find_edge_time_stamp_and_verify (struct peer_info * edges, } // failure --> 0; success --> 1 - return ( time_stamp_verify_and_update (stamp, previous_stamp) ); + return ( time_stamp_verify_and_update (stamp, previous_stamp, allow_jitter) ); } static int purge_expired_communities(n2n_sn_t *sss, @@ -784,7 +784,7 @@ static int process_udp(n2n_sn_t * sss, // 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)) { + if(!find_edge_time_stamp_and_verify (comm->edges, from_supernode, pkt.srcMac, stamp, TIME_STAMP_ALLOW_JITTER)) { traceEvent(TRACE_DEBUG, "process_udp dropped PACKET due to time stamp error."); return -1; } @@ -865,7 +865,7 @@ static int process_udp(n2n_sn_t * sss, // 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)) { + if(!find_edge_time_stamp_and_verify (comm->edges, from_supernode, reg.srcMac, stamp, TIME_STAMP_NO_JITTER)) { traceEvent(TRACE_DEBUG, "process_udp dropped REGISTER due to time stamp error."); return -1; } @@ -936,7 +936,7 @@ static int process_udp(n2n_sn_t * sss, if (comm) { if(comm->header_encryption == HEADER_ENCRYPTION_ENABLED) { - if(!find_edge_time_stamp_and_verify (comm->edges, from_supernode, reg.edgeMac, stamp)) { + if(!find_edge_time_stamp_and_verify (comm->edges, from_supernode, reg.edgeMac, stamp, TIME_STAMP_NO_JITTER)) { traceEvent(TRACE_DEBUG, "process_udp dropped REGISTER_SUPER due to time stamp error."); return -1; } @@ -1054,7 +1054,7 @@ static int process_udp(n2n_sn_t * sss, // 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)) { + if(!find_edge_time_stamp_and_verify (comm->edges, from_supernode, query.srcMac, stamp, TIME_STAMP_ALLOW_JITTER)) { traceEvent(TRACE_DEBUG, "process_udp dropped QUERY_PEER due to time stamp error."); return -1; }