From 252ce9779ae9a2203feb740ddbab5990b368718f Mon Sep 17 00:00:00 2001 From: Logan007 Date: Tue, 21 Jul 2020 19:11:31 +0545 Subject: [PATCH 1/6] moved windows-gettimeofday() from benchmark.c to n2n.c --- src/n2n.c | 22 ++++++++++++++++++++++ tools/benchmark.c | 20 -------------------- 2 files changed, 22 insertions(+), 20 deletions(-) diff --git a/src/n2n.c b/src/n2n.c index 7d57cf6..2390a37 100644 --- a/src/n2n.c +++ b/src/n2n.c @@ -377,3 +377,25 @@ int sock_equal(const n2n_sock_t * a, return(1); } +/* *********************************************** */ + +#if defined(WIN32) && !defined(__GNUC__) +static int gettimeofday(struct timeval *tp, void *tzp) +{ + time_t clock; + struct tm tm; + SYSTEMTIME wtm; + GetLocalTime(&wtm); + tm.tm_year = wtm.wYear - 1900; + tm.tm_mon = wtm.wMonth - 1; + tm.tm_mday = wtm.wDay; + tm.tm_hour = wtm.wHour; + tm.tm_min = wtm.wMinute; + tm.tm_sec = wtm.wSecond; + tm.tm_isdst = -1; + clock = mktime(&tm); + tp->tv_sec = clock; + tp->tv_usec = wtm.wMilliseconds * 1000; + return (0); +} +#endif diff --git a/tools/benchmark.c b/tools/benchmark.c index 671b639..e1d3644 100644 --- a/tools/benchmark.c +++ b/tools/benchmark.c @@ -18,26 +18,6 @@ #include "n2n.h" -#if defined(WIN32) && !defined(__GNUC__) -static int gettimeofday(struct timeval *tp, void *tzp) -{ - time_t clock; - struct tm tm; - SYSTEMTIME wtm; - GetLocalTime(&wtm); - tm.tm_year = wtm.wYear - 1900; - tm.tm_mon = wtm.wMonth - 1; - tm.tm_mday = wtm.wDay; - tm.tm_hour = wtm.wHour; - tm.tm_min = wtm.wMinute; - tm.tm_sec = wtm.wSecond; - tm.tm_isdst = -1; - clock = mktime(&tm); - tp->tv_sec = clock; - tp->tv_usec = wtm.wMilliseconds * 1000; - return (0); -} -#endif uint8_t PKT_CONTENT[]={ 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15, 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15, From 36eb807fca267f5f00bbd74113fd312418ddb737 Mon Sep 17 00:00:00 2001 From: Logan007 Date: Thu, 23 Jul 2020 01:55:02 +0545 Subject: [PATCH 2/6] added time stamp handling functions --- src/n2n.c | 67 +++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 65 insertions(+), 2 deletions(-) diff --git a/src/n2n.c b/src/n2n.c index 2390a37..b100e1b 100644 --- a/src/n2n.c +++ b/src/n2n.c @@ -25,6 +25,9 @@ #define PURGE_REGISTRATION_FREQUENCY 30 #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 */ + 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 */ static const uint8_t ipv6_multicast_addr[6] = { 0x33, 0x33, 0x00, 0x00, 0x00, 0x00 }; /* First 2 bytes are meaningful */ @@ -380,8 +383,8 @@ int sock_equal(const n2n_sock_t * a, /* *********************************************** */ #if defined(WIN32) && !defined(__GNUC__) -static int gettimeofday(struct timeval *tp, void *tzp) -{ +// REVISIT during the years 2035...2038 +int gettimeofday(struct timeval *tp, void *tzp) { time_t clock; struct tm tm; SYSTEMTIME wtm; @@ -399,3 +402,63 @@ static int gettimeofday(struct timeval *tp, void *tzp) return (0); } #endif + + +// returns a time stamp for use with replay protection +// REVISIT during the years 2035...2038 +uint64_t time_stamp (void) { + + struct timeval tod; + uint64_t micro_seconds; + + gettimeofday (&tod, NULL); + /* We will (roughly) calculate the microseconds since 1970 leftbound into the return value. + The leading 32 bits are used for tv_sec. The following 20 bits (sufficent as microseconds + fraction never exceeds 1,000,000,) encode the value tv_usec. The remaining lowest 12 bits + are kept random for use in IV */ + micro_seconds = n2n_rand(); + micro_seconds = ( (((uint64_t)(tod.tv_sec) << 32) + (tod.tv_usec << 12)) + | (micro_seconds >> 52) ); + // more exact but more costly due to the multiplication: + // micro_seconds = (tod.tv_sec * 1000000 + tod.tv_usec) << 12) | ... + + return (micro_seconds); +} + + +// checks if a provided time stamp is consistent with current time and previously valid time stamps +// returns the time stamp to store as "last valid time stamp" or zero in case of invalid time stamp +// uses branchless sign extensio tricks inspired by https://www.chessprogramming.org/Avoiding_Branches +// and also https://hbfs.wordpress.com/2008/08/05/branchless-equivalents-of-simple-functions +// REVISIT during the years 2035...2038 +uint64_t time_stamp_verify (uint64_t stamp, uint64_t previous_stamp) { + + int64_t diff; // do not change this to unsigned for keeping the following code work + + // 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(); + // branchless abs() + diff = (diff + (diff >> 63)) ^ (diff >> 63); + + if(diff < TIME_STAMP_FRAME) { + + // for not allowing to exploit the allowed TIME_STAMP_JITTER to "turn the clock backwards", + // return the higher value making use of branchless max() + diff = stamp - previous_stamp; + diff = stamp - (diff & (diff >> 63)); + return ((uint64_t)diff); + + } else { + traceEvent(TRACE_DEBUG, "time_stamp_verify found a timestamp out of allowed frame."); + return (0); + } + + } else { + traceEvent(TRACE_DEBUG, "time_stamp_verify found a timestamp too old / older than previous."); + return (0); + } +} From d3e823af98ebd8362dbba698b7846c487d381d38 Mon Sep 17 00:00:00 2001 From: Logan007 Date: Thu, 23 Jul 2020 02:17:08 +0545 Subject: [PATCH 3/6] removed branchless code --- src/n2n.c | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/src/n2n.c b/src/n2n.c index b100e1b..85a1db2 100644 --- a/src/n2n.c +++ b/src/n2n.c @@ -428,12 +428,10 @@ uint64_t time_stamp (void) { // checks if a provided time stamp is consistent with current time and previously valid time stamps // returns the time stamp to store as "last valid time stamp" or zero in case of invalid time stamp -// uses branchless sign extensio tricks inspired by https://www.chessprogramming.org/Avoiding_Branches -// and also https://hbfs.wordpress.com/2008/08/05/branchless-equivalents-of-simple-functions // REVISIT during the years 2035...2038 uint64_t time_stamp_verify (uint64_t stamp, uint64_t previous_stamp) { - int64_t diff; // do not change this to unsigned for keeping the following code work + int64_t diff; // do not change to unsigned // is it higher than previous time stamp (including allowed deviation of TIME_STAMP_JITTER)? diff = stamp - previous_stamp + TIME_STAMP_JITTER; @@ -441,16 +439,14 @@ uint64_t time_stamp_verify (uint64_t stamp, uint64_t previous_stamp) { // is it around current time (+/- allowed deviation TIME_STAMP_FRAME)? diff = stamp - time_stamp(); - // branchless abs() - diff = (diff + (diff >> 63)) ^ (diff >> 63); + // abs() + diff = (diff < 0 ? -diff : diff); if(diff < TIME_STAMP_FRAME) { // for not allowing to exploit the allowed TIME_STAMP_JITTER to "turn the clock backwards", - // return the higher value making use of branchless max() - diff = stamp - previous_stamp; - diff = stamp - (diff & (diff >> 63)); - return ((uint64_t)diff); + // return the higher value, max() + return (stamp > previous_stamp ? stamp : previous_stamp); } else { traceEvent(TRACE_DEBUG, "time_stamp_verify found a timestamp out of allowed frame."); From 895bbc284401ac056c3a3cbed0f1eb16053833fc Mon Sep 17 00:00:00 2001 From: Logan007 Date: Thu, 23 Jul 2020 16:06:54 +0545 Subject: [PATCH 4/6] added time stamp blending and sending --- include/header_encryption.h | 6 ++++-- include/n2n.h | 4 ++++ src/edge_utils.c | 34 +++++++++++++++++++++++++++------- src/header_encryption.c | 9 +++++---- src/n2n.c | 15 ++++++++------- src/sn_utils.c | 20 +++++++++++++------- 6 files changed, 61 insertions(+), 27 deletions(-) diff --git a/include/header_encryption.h b/include/header_encryption.h index 0e8a708..9ed7eaf 100644 --- a/include/header_encryption.h +++ b/include/header_encryption.h @@ -19,10 +19,12 @@ uint32_t packet_header_decrypt (uint8_t packet[], uint16_t packet_len, char * community_name, he_context_t * ctx, - he_context_t * ctx_iv, uint16_t * checksum); + he_context_t * ctx_iv, + uint64_t * stamp, uint16_t * checksum); int32_t packet_header_encrypt (uint8_t packet[], uint8_t header_len, he_context_t * ctx, - he_context_t * ctx_iv, uint16_t checksum); + he_context_t * ctx_iv, + uint64_t stamp, uint16_t checksum); void packet_header_setup_key (const char * community_name, he_context_t ** ctx, diff --git a/include/n2n.h b/include/n2n.h index e4b3b4a..547c585 100644 --- a/include/n2n.h +++ b/include/n2n.h @@ -428,6 +428,10 @@ SOCKET open_socket(int local_port, int bind_any); int sock_equal( const n2n_sock_t * a, const n2n_sock_t * b ); +/* Header encryption */ +uint64_t time_stamp(void); +int time_stamp_verify (uint64_t stamp, uint64_t * previous_stamp); + /* Operations on peer_info lists. */ size_t purge_peer_list( struct peer_info ** peer_list, time_t purge_before ); diff --git a/src/edge_utils.c b/src/edge_utils.c index 1389ab8..ca97d34 100644 --- a/src/edge_utils.c +++ b/src/edge_utils.c @@ -17,7 +17,6 @@ */ #include "n2n.h" -#include "header_encryption.h" #include "edge_utils_win32.h" /* heap allocation for compression as per lzo example doc */ @@ -731,7 +730,8 @@ static void send_register_super(n2n_edge_t * eee, if(eee->conf.header_encryption == HEADER_ENCRYPTION_ENABLED) packet_header_encrypt (pktbuf, idx, eee->conf.header_encryption_ctx, - eee->conf.header_iv_ctx, pearson_hash_16 (pktbuf, idx)); + eee->conf.header_iv_ctx, + time_stamp (), pearson_hash_16 (pktbuf, idx)); /* sent = */ sendto_sock(eee->udp_sock, pktbuf, idx, supernode); } @@ -763,7 +763,8 @@ static void send_query_peer( n2n_edge_t * eee, if(eee->conf.header_encryption == HEADER_ENCRYPTION_ENABLED){ packet_header_encrypt (pktbuf, idx, eee->conf.header_encryption_ctx, - eee->conf.header_iv_ctx, pearson_hash_16 (pktbuf, idx)); + eee->conf.header_iv_ctx, + time_stamp (), pearson_hash_16 (pktbuf, idx)); } sendto_sock( eee->udp_sock, pktbuf, idx, &(eee->supernode) ); } @@ -810,7 +811,8 @@ static void send_register(n2n_edge_t * eee, if(eee->conf.header_encryption == HEADER_ENCRYPTION_ENABLED) packet_header_encrypt (pktbuf, idx, eee->conf.header_encryption_ctx, - eee->conf.header_iv_ctx, pearson_hash_16 (pktbuf, idx)); + eee->conf.header_iv_ctx, + time_stamp (), pearson_hash_16 (pktbuf, idx)); /* sent = */ sendto_sock(eee->udp_sock, pktbuf, idx, remote_peer); } @@ -853,7 +855,8 @@ static void send_register_ack(n2n_edge_t * eee, if(eee->conf.header_encryption == HEADER_ENCRYPTION_ENABLED) packet_header_encrypt (pktbuf, idx, eee->conf.header_encryption_ctx, - eee->conf.header_iv_ctx, pearson_hash_16 (pktbuf, idx)); + eee->conf.header_iv_ctx, + time_stamp (), pearson_hash_16 (pktbuf, idx)); /* sent = */ sendto_sock(eee->udp_sock, pktbuf, idx, remote_peer); } @@ -1471,7 +1474,8 @@ void edge_send_packet2net(n2n_edge_t * eee, if(eee->conf.header_encryption == HEADER_ENCRYPTION_ENABLED) packet_header_encrypt (pktbuf, headerIdx, eee->conf.header_encryption_ctx, - eee->conf.header_iv_ctx, pearson_hash_16 (pktbuf, idx)); + eee->conf.header_iv_ctx, + time_stamp (), pearson_hash_16 (pktbuf, idx)); #ifdef MTU_ASSERT_VALUE { @@ -1564,6 +1568,7 @@ static void readFromIPSocket(n2n_edge_t * eee, int in_sock) { n2n_sock_t sender; n2n_sock_t * orig_sender=NULL; time_t now=0; + uint64_t stamp = 0; size_t i; @@ -1601,10 +1606,16 @@ static void readFromIPSocket(n2n_edge_t * eee, int in_sock) { if(eee->conf.header_encryption == HEADER_ENCRYPTION_ENABLED) { uint16_t checksum = 0; if( packet_header_decrypt (udp_buf, recvlen, (char *)eee->conf.community_name, eee->conf.header_encryption_ctx, - eee->conf.header_iv_ctx, &checksum) == 0) { + eee->conf.header_iv_ctx, + &stamp, &checksum) == 0) { traceEvent(TRACE_DEBUG, "readFromIPSocket failed to decrypt header."); return; } + + // time stamp verification follows in the packet specific section as it requires to determine the + // sender from the hash list by its MAC, or the packet might be from the supernode, this all depends + // on packet type, path taken (via supernode) and packet structure (MAC is not always in the same place) + if (checksum != pearson_hash_16 (udp_buf, recvlen)) { traceEvent(TRACE_DEBUG, "readFromIPSocket dropped packet due to checksum error."); return; @@ -1635,6 +1646,15 @@ static 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 ( !time_stamp_verify (stamp, &... !!!) ) { + traceEvent(TRACE_DEBUG, "readFromIPSocket dropped packet due to time stamp error."); + return; + } + } +*/ if(is_valid_peer_sock(&pkt.sock)) orig_sender = &(pkt.sock); diff --git a/src/header_encryption.c b/src/header_encryption.c index 32b6654..c526726 100644 --- a/src/header_encryption.c +++ b/src/header_encryption.c @@ -25,7 +25,7 @@ uint32_t packet_header_decrypt (uint8_t packet[], uint16_t packet_len, char * community_name, he_context_t * ctx, - he_context_t * ctx_iv, uint16_t * checksum) { + he_context_t * ctx_iv, uint64_t * stamp, uint16_t * checksum) { // assemble IV // the last four are ASCII "n2n!" and do not get overwritten @@ -35,9 +35,10 @@ uint32_t packet_header_decrypt (uint8_t packet[], uint16_t packet_len, // to full 128 bit IV memcpy (iv, packet, 12); - // extract checksum (last 16 bit) blended in IV + // extract time stamp (first 64 bit) and checksum (last 16 bit) blended in IV speck_he_iv_decrypt (iv, (speck_context_t*)ctx_iv); *checksum = be16toh (((uint16_t*)iv)[5]); + *stamp = be64toh (((uint64_t*)iv)[0]); memcpy (iv, packet, 12); @@ -64,7 +65,7 @@ uint32_t packet_header_decrypt (uint8_t packet[], uint16_t packet_len, /* ********************************************************************** */ int32_t packet_header_encrypt (uint8_t packet[], uint8_t header_len, he_context_t * ctx, - he_context_t * ctx_iv, uint16_t checksum) { + he_context_t * ctx_iv, uint64_t stamp, uint16_t checksum) { uint8_t iv[16]; uint16_t *iv16 = (uint16_t*)&iv; @@ -79,7 +80,7 @@ int32_t packet_header_encrypt (uint8_t packet[], uint8_t header_len, he_context_ memcpy (&packet[16], &packet[00], 4); - iv64[0] = n2n_rand (); + iv64[0] = htobe64 (stamp); iv16[4] = n2n_rand (); iv16[5] = htobe16 (checksum); iv32[3] = htobe32 (magic); diff --git a/src/n2n.c b/src/n2n.c index 85a1db2..59f27db 100644 --- a/src/n2n.c +++ b/src/n2n.c @@ -427,14 +427,14 @@ uint64_t time_stamp (void) { // checks if a provided time stamp is consistent with current time and previously valid time stamps -// returns the time stamp to store as "last valid time stamp" or zero in case of invalid time stamp +// and, in case of validity, replaces the "last valid time stamp" // REVISIT during the years 2035...2038 -uint64_t time_stamp_verify (uint64_t stamp, uint64_t previous_stamp) { +int time_stamp_verify (uint64_t stamp, uint64_t * previous_stamp) { int64_t diff; // do not change to unsigned // is it higher than previous time stamp (including allowed deviation of TIME_STAMP_JITTER)? - diff = stamp - previous_stamp + TIME_STAMP_JITTER; + diff = stamp - *previous_stamp + TIME_STAMP_JITTER; if(diff > 0) { // is it around current time (+/- allowed deviation TIME_STAMP_FRAME)? @@ -445,16 +445,17 @@ uint64_t time_stamp_verify (uint64_t stamp, uint64_t previous_stamp) { if(diff < TIME_STAMP_FRAME) { // for not allowing to exploit the allowed TIME_STAMP_JITTER to "turn the clock backwards", - // return the higher value, max() - return (stamp > previous_stamp ? stamp : previous_stamp); + // set the higher of the values + *previous_stamp = (stamp > *previous_stamp ? stamp : *previous_stamp); + return (1); // success } else { traceEvent(TRACE_DEBUG, "time_stamp_verify found a timestamp out of allowed frame."); - return (0); + return (0); // failure } } else { traceEvent(TRACE_DEBUG, "time_stamp_verify found a timestamp too old / older than previous."); - return (0); + return (0); // failure } } diff --git a/src/sn_utils.c b/src/sn_utils.c index 4ea34f6..8cf5821 100644 --- a/src/sn_utils.c +++ b/src/sn_utils.c @@ -1,5 +1,4 @@ #include "n2n.h" -#include "header_encryption.h" #define HASH_FIND_COMMUNITY(head, name, out) HASH_FIND_STR(head, name, out) @@ -391,6 +390,7 @@ static int process_udp(n2n_sn_t * sss, n2n_sock_str_t sockbuf; char buf[32]; struct sn_community *comm, *tmp; + uint64_t stamp; traceEvent(TRACE_DEBUG, "Processing incoming UDP packet [len: %lu][sender: %s:%u]", udp_size, intoa(ntohl(sender_sock->sin_addr.s_addr), buf, sizeof(buf)), @@ -437,7 +437,8 @@ static int process_udp(n2n_sn_t * sss, continue; uint16_t checksum = 0; if ( (ret = packet_header_decrypt (udp_buf, udp_size, comm->community, comm->header_encryption_ctx, - comm->header_iv_ctx, &checksum)) ) { + comm->header_iv_ctx, + &stamp, &checksum)) ) { if (checksum != pearson_hash_16 (udp_buf, udp_size)) { traceEvent(TRACE_DEBUG, "process_udp dropped packet due to checksum error."); return -1; @@ -537,7 +538,8 @@ static int process_udp(n2n_sn_t * sss, if (comm->header_encryption == HEADER_ENCRYPTION_ENABLED) packet_header_encrypt (rec_buf, oldEncx, comm->header_encryption_ctx, - comm->header_iv_ctx, pearson_hash_16 (rec_buf, encx)); + comm->header_iv_ctx, + time_stamp (), pearson_hash_16 (rec_buf, encx)); } else { /* Already from a supernode. Nothing to modify, just pass to @@ -550,7 +552,8 @@ static int process_udp(n2n_sn_t * sss, if (comm->header_encryption == HEADER_ENCRYPTION_ENABLED) packet_header_encrypt (rec_buf, idx, comm->header_encryption_ctx, - comm->header_iv_ctx, pearson_hash_16 (rec_buf, udp_size)); + comm->header_iv_ctx, + time_stamp (), pearson_hash_16 (rec_buf, udp_size)); } /* Common section to forward the final product. */ @@ -611,7 +614,8 @@ static int process_udp(n2n_sn_t * sss, if (comm->header_encryption == HEADER_ENCRYPTION_ENABLED) packet_header_encrypt (rec_buf, encx, comm->header_encryption_ctx, - comm->header_iv_ctx, pearson_hash_16 (rec_buf, encx)); + comm->header_iv_ctx, + time_stamp (), pearson_hash_16 (rec_buf, encx)); try_forward(sss, comm, &cmn, reg.dstMac, rec_buf, encx); /* unicast only */ } else @@ -683,7 +687,8 @@ static int process_udp(n2n_sn_t * sss, if (comm->header_encryption == HEADER_ENCRYPTION_ENABLED) packet_header_encrypt (ackbuf, encx, comm->header_encryption_ctx, - comm->header_iv_ctx, pearson_hash_16 (ackbuf, encx)); + comm->header_iv_ctx, + time_stamp (), pearson_hash_16 (ackbuf, encx)); sendto(sss->sock, ackbuf, encx, 0, (struct sockaddr *)sender_sock, sizeof(struct sockaddr_in)); @@ -731,7 +736,8 @@ static int process_udp(n2n_sn_t * sss, if (comm->header_encryption == HEADER_ENCRYPTION_ENABLED) packet_header_encrypt (encbuf, encx, comm->header_encryption_ctx, - comm->header_iv_ctx, pearson_hash_16 (encbuf, encx)); + comm->header_iv_ctx, + time_stamp (), pearson_hash_16 (encbuf, encx)); sendto( sss->sock, encbuf, encx, 0, (struct sockaddr *)sender_sock, sizeof(struct sockaddr_in) ); From b976379125210d1798649b04108d5c7041b87456 Mon Sep 17 00:00:00 2001 From: Logan007 Date: Thu, 23 Jul 2020 20:34:54 +0545 Subject: [PATCH 5/6] added data structures for replay protection --- include/n2n.h | 6 ++++-- src/edge_utils.c | 16 +++++++++------- src/sn_utils.c | 3 +++ 3 files changed, 16 insertions(+), 9 deletions(-) diff --git a/include/n2n.h b/include/n2n.h index 547c585..6251ef7 100644 --- a/include/n2n.h +++ b/include/n2n.h @@ -197,6 +197,7 @@ struct peer_info { time_t last_seen; time_t last_p2p; time_t last_sent_query; + uint64_t last_valid_time_stamp; UT_hash_handle hh; /* makes this structure hashable */ }; @@ -304,8 +305,9 @@ struct n2n_edge { n2n_trans_op_t transop; /**< The transop to use when encoding */ n2n_cookie_t last_cookie; /**< Cookie sent in last REGISTER_SUPER. */ n2n_route_t *sn_route_to_clean; /**< Supernode route to clean */ - n2n_edge_callbacks_t cb; /**< API callbacks */ - void *user_data; /**< Can hold user data */ + n2n_edge_callbacks_t cb; /**< API callbacks */ + void *user_data; /**< Can hold user data */ + uint64_t sn_last_valid_time_stamp;/*< last valid time stamp from supernode */ /* Sockets */ n2n_sock_t supernode; diff --git a/src/edge_utils.c b/src/edge_utils.c index ca97d34..7f78d50 100644 --- a/src/edge_utils.c +++ b/src/edge_utils.c @@ -1646,17 +1646,19 @@ static 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(is_valid_peer_sock(&pkt.sock)) + orig_sender = &(pkt.sock); - if ( !time_stamp_verify (stamp, &... !!!) ) { - traceEvent(TRACE_DEBUG, "readFromIPSocket dropped packet due to time stamp error."); - return; +/* // 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."); + 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 diff --git a/src/sn_utils.c b/src/sn_utils.c index 8cf5821..8a9896d 100644 --- a/src/sn_utils.c +++ b/src/sn_utils.c @@ -439,6 +439,9 @@ static int process_udp(n2n_sn_t * sss, if ( (ret = packet_header_decrypt (udp_buf, udp_size, comm->community, comm->header_encryption_ctx, comm->header_iv_ctx, &stamp, &checksum)) ) { + // time stamp verification follows in the packet specific section as it requires to determine the + // sender from the hash list by its MAC, this all depends on packet type and packet structure + // (MAC is not always in the same place) if (checksum != pearson_hash_16 (udp_buf, udp_size)) { traceEvent(TRACE_DEBUG, "process_udp dropped packet due to checksum error."); return -1; From 0776e069124728b5f62c01b6b6482660a1b38c2d Mon Sep 17 00:00:00 2001 From: Logan007 Date: Sat, 25 Jul 2020 18:13:20 +0545 Subject: [PATCH 6/6] 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 ) );