Browse Source

added replay protection

pull/327/head
Logan007 4 years ago
parent
commit
0776e06912
  1. 3
      include/n2n.h
  2. 85
      src/edge_utils.c
  3. 59
      src/n2n.c
  4. 62
      src/sn_utils.c

3
include/n2n.h

@ -432,7 +432,8 @@ int sock_equal( const n2n_sock_t * a,
/* Header encryption */ /* Header encryption */
uint64_t time_stamp(void); 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. */ /* Operations on peer_info lists. */
size_t purge_peer_list( struct peer_info ** peer_list, size_t purge_peer_list( struct peer_info ** peer_list,

85
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->known_peers = NULL;
eee->pending_peers = NULL; eee->pending_peers = NULL;
eee->sup_attempts = N2N_EDGE_SUP_ATTEMPTS; eee->sup_attempts = N2N_EDGE_SUP_ATTEMPTS;
eee->sn_last_valid_time_stamp = initial_time_stamp ();
pearson_hash_init(); 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 * 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->sock = *peer;
scan->timeout = REGISTER_SUPER_INTERVAL_DFL; /* TODO: should correspond to the peer supernode registration timeout */ 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_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); 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); memcpy(scan->mac_addr, mac, N2N_MAC_SIZE);
scan->timeout = REGISTER_SUPER_INTERVAL_DFL; /* TODO: should correspond to the peer supernode registration timeout */ 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_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); 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); 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) { if(eee->conf.header_encryption == HEADER_ENCRYPTION_ENABLED) {
// find edge and its specific last time stamp or supernode's one !!! if(!find_peer_time_stamp_and_verify (eee, from_supernode, pkt.srcMac, stamp)) {
if ( !time_stamp_verify (stamp, &found_time_stamp !!!) ) { traceEvent(TRACE_DEBUG, "readFromIPSocket dropped PACKET due to time stamp error.");
traceEvent(TRACE_DEBUG, "readFromIPSocket dropped packet due to time stamp error.");
return; return;
} }
} }
*/
if(is_valid_peer_sock(&pkt.sock))
orig_sender = &(pkt.sock);
if(!from_supernode) { if(!from_supernode) {
/* This is a P2P packet from the peer. We purge a pending /* 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(&reg, &cmn, udp_buf, &rem, &idx); decode_REGISTER(&reg, &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(&reg.sock)) if(is_valid_peer_sock(&reg.sock))
orig_sender = &(reg.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); 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)) if(is_valid_peer_sock(&ra.sock))
orig_sender = &(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); 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)) if(is_valid_peer_sock(&ra.sock))
orig_sender = &(ra.sock); orig_sender = &(ra.sock);
@ -1793,6 +1849,13 @@ static void readFromIPSocket(n2n_edge_t * eee, int in_sock) {
struct peer_info * scan; struct peer_info * scan;
decode_PEER_INFO( &pi, &cmn, udp_buf, &rem, &idx ); 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)) { if(!is_valid_peer_sock(&pi.sock)) {
traceEvent(TRACE_DEBUG, "Skip invalid PEER_INFO %s [%s]", traceEvent(TRACE_DEBUG, "Skip invalid PEER_INFO %s [%s]",
sock_to_cstr(sockbuf1, &pi.sock), sock_to_cstr(sockbuf1, &pi.sock),

59
src/n2n.c

@ -26,7 +26,8 @@
#define REGISTRATION_TIMEOUT 60 #define REGISTRATION_TIMEOUT 60
#define TIME_STAMP_FRAME 0x0000001000000000LL /* clocks of different computers are allowed +/- 16 seconds to be off */ #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 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 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__) #if defined(WIN32) && !defined(__GNUC__)
// REVISIT during the years 2035...2038
int gettimeofday(struct timeval *tp, void *tzp) { int gettimeofday(struct timeval *tp, void *tzp) {
time_t clock; time_t clock;
struct tm tm; struct tm tm;
@ -405,7 +405,6 @@ int gettimeofday(struct timeval *tp, void *tzp) {
// returns a time stamp for use with replay protection // returns a time stamp for use with replay protection
// REVISIT during the years 2035...2038
uint64_t time_stamp (void) { uint64_t time_stamp (void) {
struct timeval tod; 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 // returns an initial time stamp for use with replay protection
// and, in case of validity, replaces the "last valid time stamp" uint64_t initial_time_stamp (void) {
// REVISIT during the years 2035...2038
int time_stamp_verify (uint64_t stamp, uint64_t * previous_stamp) {
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)? // checks if a provided time stamp is consistent with current time and previously valid time stamps
diff = stamp - time_stamp(); // and, in case of validity, updates the "last valid time stamp"
// abs() int time_stamp_verify_and_update (uint64_t stamp, uint64_t * previous_stamp) {
diff = (diff < 0 ? -diff : diff);
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", // is it around current time (+/- allowed deviation TIME_STAMP_FRAME)?
// set the higher of the values diff = stamp - time_stamp();
*previous_stamp = (stamp > *previous_stamp ? stamp : *previous_stamp); // abs()
return (1); // success 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 { // if applicable: is it higher than previous time stamp (including allowed deviation of TIME_STAMP_JITTER)?
traceEvent(TRACE_DEBUG, "time_stamp_verify found a timestamp out of allowed frame."); 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 return (0); // failure
} }
// for not allowing to exploit the allowed TIME_STAMP_JITTER to "turn the clock backwards",
} else { // set the higher of the values
traceEvent(TRACE_DEBUG, "time_stamp_verify found a timestamp too old / older than previous."); *previous_stamp = (stamp > *previous_stamp ? stamp : *previous_stamp);
return (0); // failure
} }
return (1); // success
} }

62
src/sn_utils.c

@ -41,6 +41,8 @@ static int process_udp(n2n_sn_t *sss,
size_t udp_size, size_t udp_size,
time_t now); time_t now);
/* ************************************** */
static int try_forward(n2n_sn_t * sss, static int try_forward(n2n_sn_t * sss,
const struct sn_community *comm, const struct sn_community *comm,
const n2n_common_t * cmn, 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->mac_addr), edgeMac, sizeof(n2n_mac_t));
memcpy(&(scan->sock), sender_sock, sizeof(n2n_sock_t)); memcpy(&(scan->sock), sender_sock, sizeof(n2n_sock_t));
scan->last_valid_time_stamp = initial_time_stamp ();
HASH_ADD_PEER(comm->edges, scan); HASH_ADD_PEER(comm->edges, scan);
@ -285,6 +288,32 @@ static int update_edge(n2n_sn_t *sss,
return 0; 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, static int process_mgmt(n2n_sn_t *sss,
const struct sockaddr_in *sender_sock, const struct sockaddr_in *sender_sock,
const uint8_t *mgmt_buf, const uint8_t *mgmt_buf,
@ -512,6 +541,14 @@ static int process_udp(n2n_sn_t * sss,
sss->stats.last_fwd=now; sss->stats.last_fwd=now;
decode_PACKET(&pkt, &cmn, udp_buf, &rem, &idx); 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)); unicast = (0 == is_multi_broadcast(pkt.dstMac));
traceEvent(TRACE_DEBUG, "RX PACKET (%s) %s -> %s %s", 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; sss->stats.last_fwd=now;
decode_REGISTER(&reg, &cmn, udp_buf, &rem, &idx); decode_REGISTER(&reg, &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)); unicast = (0 == is_multi_broadcast(reg.dstMac));
if(unicast) { if(unicast) {
@ -641,6 +686,15 @@ static int process_udp(n2n_sn_t * sss,
++(sss->stats.reg_super); ++(sss->stats.reg_super);
decode_REGISTER_SUPER(&reg, &cmn, udp_buf, &rem, &idx); decode_REGISTER_SUPER(&reg, &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 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 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 ); 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", traceEvent( TRACE_DEBUG, "Rx QUERY_PEER from %s for %s",
macaddr_str( mac_buf, query.srcMac ), macaddr_str( mac_buf, query.srcMac ),
macaddr_str( mac_buf2, query.targetMac ) ); macaddr_str( mac_buf2, query.targetMac ) );

Loading…
Cancel
Save