From 84d099197761b548978a5f9113792457ba4c7ff8 Mon Sep 17 00:00:00 2001 From: Logan oos Even <46396513+Logan007@users.noreply.github.com> Date: Tue, 20 Jul 2021 23:07:38 +0200 Subject: [PATCH] fortified user/pw auth scheme (#731) --- include/n2n_define.h | 3 +++ src/edge_utils.c | 48 +++++++++++++++++++++++++++++-------- src/sn_utils.c | 57 +++++++++++++++++++++++++++++++++++++------- 3 files changed, 89 insertions(+), 19 deletions(-) diff --git a/include/n2n_define.h b/include/n2n_define.h index 7f985c4..ace173b 100644 --- a/include/n2n_define.h +++ b/include/n2n_define.h @@ -105,6 +105,9 @@ enum sn_purge{SN_PURGEABLE = 0, SN_UNPURGEABLE = 1}; #define HEADER_ENCRYPTION_NONE 1 #define HEADER_ENCRYPTION_ENABLED 2 +/* REGISTER_SUPER_ACK packet hash length with user/pw auth, up to 16 bytes */ +#define N2N_REG_SUP_HASH_CHECK_LEN 16 + #define DEFAULT_MTU 1290 #define HASH_ADD_PEER(head,add) \ diff --git a/src/edge_utils.c b/src/edge_utils.c index 4e95817..938b4e7 100644 --- a/src/edge_utils.c +++ b/src/edge_utils.c @@ -1141,6 +1141,7 @@ void send_query_peer (n2n_edge_t * eee, void send_register_super (n2n_edge_t *eee) { uint8_t pktbuf[N2N_PKT_BUF_SIZE] = {0}; + uint8_t hash_buf[16] = {0}; size_t idx; /* ssize_t sent; */ n2n_common_t cmn; @@ -1172,11 +1173,18 @@ void send_register_super (n2n_edge_t *eee) { traceEvent(TRACE_DEBUG, "send REGISTER_SUPER to %s", sock_to_cstr(sockbuf, &(eee->curr_sn->sock))); - if(eee->conf.header_encryption == HEADER_ENCRYPTION_ENABLED) + if(eee->conf.header_encryption == HEADER_ENCRYPTION_ENABLED) { packet_header_encrypt(pktbuf, idx, idx, eee->conf.header_encryption_ctx_static, eee->conf.header_iv_ctx_static, time_stamp()); + if(eee->conf.shared_secret) { + pearson_hash_128(hash_buf, pktbuf, idx); + speck_128_encrypt(hash_buf, (speck_context_t*)eee->conf.shared_secret_ctx); + encode_buf(pktbuf, &idx, hash_buf, N2N_REG_SUP_HASH_CHECK_LEN); + } + } + /* sent = */ sendto_sock(eee, pktbuf, idx, &(eee->curr_sn->sock)); } @@ -2259,6 +2267,7 @@ void process_udp (n2n_edge_t *eee, const struct sockaddr_in *sender_sock, const n2n_sock_str_t sockbuf2; /* don't clobber sockbuf1 if writing two addresses to trace */ macstr_t mac_buf1; macstr_t mac_buf2; + uint8_t hash_buf[16]; size_t rem; size_t idx; size_t msg_type; @@ -2295,16 +2304,24 @@ void process_udp (n2n_edge_t *eee, const struct sockaddr_in *sender_sock, const if(eee->conf.header_encryption == HEADER_ENCRYPTION_ENABLED) { // match with static (1) or dynamic (2) ctx? - header_enc = packet_header_decrypt(udp_buf, udp_size, - (char *)eee->conf.community_name, - eee->conf.header_encryption_ctx_static, eee->conf.header_iv_ctx_static, - &stamp); - if(!header_enc) - if(packet_header_decrypt(udp_buf, udp_size, + // check dynamic first as it is identical to static in normal header encryption mode + if(packet_header_decrypt(udp_buf, udp_size, (char *)eee->conf.community_name, eee->conf.header_encryption_ctx_dynamic, eee->conf.header_iv_ctx_dynamic, &stamp)) { - header_enc = 2; + header_enc = 2; /* not accurate with normal header encryption but does not matter */ + } + if(!header_enc) { + // check static now (very likely to be REGISTER_SUPER_ACK, REGISTER_SUPER_NAK or invalid) + if(eee->conf.shared_secret) { + // hash the still encrypted packet to eventually be able to check it later (required for REGISTER_SUPER_ACK with user/pw auth) + pearson_hash_128(hash_buf, udp_buf, max(0, (int)udp_size - (int)N2N_REG_SUP_HASH_CHECK_LEN)); + } + header_enc = packet_header_decrypt(udp_buf, max(0, (int)udp_size - (int)N2N_REG_SUP_HASH_CHECK_LEN), + (char *)eee->conf.community_name, + eee->conf.header_encryption_ctx_static, eee->conf.header_iv_ctx_static, + &stamp); + header_enc = 1; } if(!header_enc) { traceEvent(TRACE_DEBUG, "readFromIPSocket failed to decrypt header."); @@ -2500,6 +2517,15 @@ void process_udp (n2n_edge_t *eee, const struct sockaddr_in *sender_sock, const } } + // hash check (user/pw auth only) + if(eee->conf.shared_secret) { + speck_128_encrypt(hash_buf, (speck_context_t*)eee->conf.shared_secret_ctx); + if(memcmp(hash_buf, udp_buf + udp_size - N2N_REG_SUP_HASH_CHECK_LEN /* length is has already been checked */, N2N_REG_SUP_HASH_CHECK_LEN)) { + traceEvent(TRACE_INFO, "Rx REGISTER_SUPER_ACK with wrong hash."); + return; + } + } + if(memcmp(ra.cookie, eee->curr_sn->last_cookie, N2N_COOKIE_SIZE)) { traceEvent(TRACE_INFO, "Rx REGISTER_SUPER_ACK with wrong or old cookie."); return; @@ -2624,7 +2650,9 @@ void process_udp (n2n_edge_t *eee, const struct sockaddr_in *sender_sock, const } else { traceEvent(TRACE_ERROR, "Authentication error. MAC or IP address already in use or not released yet by supernode."); } - exit(1); + // REVISIT: the following portion is too harsh, repeated error warning should be sufficient until it eventually is resolved, + // preventing de-auth attacks + /* exit(1); this is too harsh, repeated error warning should be sufficient until it eventually is resolved, preventing de-auth attacks } else { HASH_FIND_PEER(eee->known_peers, nak.srcMac, peer); if(peer != NULL) { @@ -2633,7 +2661,7 @@ void process_udp (n2n_edge_t *eee, const struct sockaddr_in *sender_sock, const HASH_FIND_PEER(eee->pending_peers, nak.srcMac, scan); if(scan != NULL) { HASH_DEL(eee->pending_peers, scan); - } + } */ } break; } diff --git a/src/sn_utils.c b/src/sn_utils.c index c2d73ef..0698a63 100644 --- a/src/sn_utils.c +++ b/src/sn_utils.c @@ -1240,6 +1240,8 @@ static int process_udp (n2n_sn_t * sss, macstr_t mac_buf2; n2n_sock_str_t sockbuf; char buf[32]; + uint8_t hash_buf[16] = {0}; /* always size of 16 (max) despite the actual value of N2N_REG_SUP_HASH_CHECK_LEN (<= 16) */ + struct sn_community *comm, *tmp; uint32_t header_enc = 0; /* 1 == encrypted by static key, 2 == encrypted by dynamic key */ uint64_t stamp; @@ -1289,17 +1291,20 @@ static int process_udp (n2n_sn_t * sss, if(comm->header_encryption == HEADER_ENCRYPTION_NONE) { continue; } + // match with static (1) or dynamic (2) ctx? - header_enc = packet_header_decrypt(udp_buf, udp_size, - comm->community, - comm->header_encryption_ctx_static, comm->header_iv_ctx_static, - &stamp); - if(!header_enc) - if(packet_header_decrypt(udp_buf, udp_size, - comm->community, - comm->header_encryption_ctx_dynamic, comm->header_iv_ctx_dynamic, - &stamp)) + // check dynamic first as it is identical to static in normal header encryption mode + if(packet_header_decrypt(udp_buf, udp_size, + comm->community, + comm->header_encryption_ctx_dynamic, comm->header_iv_ctx_dynamic, + &stamp)) { header_enc = 2; + } + if(!header_enc) { + pearson_hash_128(hash_buf, udp_buf, max(0, (int)udp_size - (int)N2N_REG_SUP_HASH_CHECK_LEN)); + header_enc = packet_header_decrypt(udp_buf, max(0, (int)udp_size - (int)N2N_REG_SUP_HASH_CHECK_LEN), comm->community, + comm->header_encryption_ctx_static, comm->header_iv_ctx_static, &stamp); + } if(header_enc) { // time stamp verification follows in the packet specific section as it requires to determine the @@ -1338,6 +1343,7 @@ static int process_udp (n2n_sn_t * sss, rem = udp_size; /* Counts down bytes of packet to protect against buffer overruns. */ idx = 0; /* marches through packet header as parts are decoded. */ + if(decode_common(&cmn, udp_buf, &rem, &idx) < 0) { traceEvent(TRACE_ERROR, "Failed to decode common section"); return -1; /* failed to decode packet */ @@ -1563,6 +1569,7 @@ static int process_udp (n2n_sn_t * sss, int num = 0; int skip; int ret_value; + sn_user_t *user = NULL; memset(&ack, 0, sizeof(n2n_REGISTER_SUPER_ACK_t)); memset(&nak, 0, sizeof(n2n_REGISTER_SUPER_NAK_t)); @@ -1631,6 +1638,22 @@ static int process_udp (n2n_sn_t * sss, return -1; } + // hash check (user/pw auth only) + if(comm->allowed_users) { + // check if submitted public key is in list of allowed users + HASH_FIND(hh, comm->allowed_users, ®.auth.token, sizeof(n2n_private_public_key_t), user); + if(user) { + speck_128_encrypt(hash_buf, (speck_context_t*)user->shared_secret_ctx); + if(memcmp(hash_buf, udp_buf + udp_size - N2N_REG_SUP_HASH_CHECK_LEN /* length has already been checked */, N2N_REG_SUP_HASH_CHECK_LEN)) { + traceEvent(TRACE_INFO, "Rx REGISTER_SUPER with wrong hash."); + return -1; + } + } else { + traceEvent(TRACE_INFO, "Rx REGISTER_SUPER from unknown user."); + // continue and let auth check do the rest (otherwise, no NAK is sent) + } + } + cmn2.ttl = N2N_DEFAULT_TTL; cmn2.pc = n2n_register_super_ack; cmn2.flags = N2N_FLAGS_SOCKET | N2N_FLAGS_FROM_SUPERNODE; @@ -1715,6 +1738,10 @@ static int process_udp (n2n_sn_t * sss, packet_header_encrypt(ackbuf, encx, encx, comm->header_encryption_ctx_static, comm->header_iv_ctx_static, time_stamp()); + // if user-password-auth + if(comm->allowed_users) { + encode_buf(ackbuf, &encx, hash_buf /* no matter what content */, N2N_REG_SUP_HASH_CHECK_LEN); + } } sendto_sock(sss, socket_fd, (struct sockaddr *)sender_sock, ackbuf, encx); @@ -1753,6 +1780,14 @@ static int process_udp (n2n_sn_t * sss, packet_header_encrypt(ackbuf, encx, encx, comm->header_encryption_ctx_static, comm->header_iv_ctx_static, time_stamp()); + // if user-password-auth + if(comm->allowed_users) { + // append an encrypted packet hash + pearson_hash_128(hash_buf, ackbuf, encx); + // same 'user' as above + speck_128_encrypt(hash_buf, (speck_context_t*)user->shared_secret_ctx); + encode_buf(ackbuf, &encx, hash_buf, N2N_REG_SUP_HASH_CHECK_LEN); + } } sendto_sock(sss, socket_fd, (struct sockaddr *)sender_sock, ackbuf, encx); @@ -1953,6 +1988,10 @@ static int process_udp (n2n_sn_t * sss, packet_header_encrypt(nakbuf, encx, encx, comm->header_encryption_ctx_static, comm->header_iv_ctx_static, time_stamp()); + // if user-password-auth + if(comm->allowed_users) { + encode_buf(nakbuf, &encx, hash_buf /* no matter what content */, N2N_REG_SUP_HASH_CHECK_LEN); + } } sendto_peer(sss, peer, nakbuf, encx);