Use datagram SPTPS for packet exchange between nodes.
When two nodes which support SPTPS want to send packets to each other, they now always use SPTPS. The node initiating the SPTPS session send the first SPTPS packet via an extended REQ_KEY messages. All other handshake messages are sent using ANS_KEY messages. This ensures that intermediate nodes using an older version of tinc can still help with NAT traversal. After the authentication phase is over, SPTPS packets are sent via UDP, or are encapsulated in extended REQ_KEY messages instead of PACKET messages.
This commit is contained in:
parent
248d300f1b
commit
153abaa4d9
12 changed files with 213 additions and 134 deletions
10
src/graph.c
10
src/graph.c
|
@ -263,13 +263,19 @@ static void check_reachability(void) {
|
||||||
|
|
||||||
subnet_update(n, NULL, n->status.reachable);
|
subnet_update(n, NULL, n->status.reachable);
|
||||||
|
|
||||||
if(!n->status.reachable)
|
if(!n->status.reachable) {
|
||||||
update_node_udp(n, NULL);
|
update_node_udp(n, NULL);
|
||||||
else if(n->connection)
|
} else if(n->connection) {
|
||||||
|
if(experimental && OPTION_VERSION(n->options) >= 2) {
|
||||||
|
if(n->connection->outgoing)
|
||||||
|
send_req_key(n);
|
||||||
|
} else {
|
||||||
send_ans_key(n);
|
send_ans_key(n);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void graph(void) {
|
void graph(void) {
|
||||||
subnet_cache_flush();
|
subnet_cache_flush();
|
||||||
|
|
|
@ -31,7 +31,7 @@
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
#include "xalloc.h"
|
#include "xalloc.h"
|
||||||
|
|
||||||
bool send_meta_sptps(void *handle, const char *buffer, size_t length) {
|
bool send_meta_sptps(void *handle, uint8_t type, const char *buffer, size_t length) {
|
||||||
connection_t *c = handle;
|
connection_t *c = handle;
|
||||||
|
|
||||||
if(!c) {
|
if(!c) {
|
||||||
|
|
|
@ -24,7 +24,7 @@
|
||||||
#include "connection.h"
|
#include "connection.h"
|
||||||
|
|
||||||
extern bool send_meta(struct connection_t *, const char *, int);
|
extern bool send_meta(struct connection_t *, const char *, int);
|
||||||
extern bool send_meta_sptps(void *, const char *, size_t);
|
extern bool send_meta_sptps(void *, uint8_t, const char *, size_t);
|
||||||
extern bool receive_meta_sptps(void *, uint8_t, const char *, uint16_t);
|
extern bool receive_meta_sptps(void *, uint8_t, const char *, uint16_t);
|
||||||
extern void broadcast_meta(struct connection_t *, const char *, int);
|
extern void broadcast_meta(struct connection_t *, const char *, int);
|
||||||
extern bool receive_meta(struct connection_t *);
|
extern bool receive_meta(struct connection_t *);
|
||||||
|
|
14
src/net.h
14
src/net.h
|
@ -83,6 +83,18 @@ typedef struct vpn_packet_t {
|
||||||
uint8_t data[MAXSIZE];
|
uint8_t data[MAXSIZE];
|
||||||
} vpn_packet_t;
|
} vpn_packet_t;
|
||||||
|
|
||||||
|
/* Packet types when using SPTPS */
|
||||||
|
|
||||||
|
#define PKT_COMPRESSED 1
|
||||||
|
#define PKT_MAC 2
|
||||||
|
#define PKT_PROBE 4
|
||||||
|
|
||||||
|
typedef enum packet_type_t {
|
||||||
|
PACKET_NORMAL,
|
||||||
|
PACKET_COMPRESSED,
|
||||||
|
PACKET_PROBE
|
||||||
|
} packet_type_t;
|
||||||
|
|
||||||
typedef struct listen_socket_t {
|
typedef struct listen_socket_t {
|
||||||
struct event ev_tcp;
|
struct event ev_tcp;
|
||||||
struct event ev_udp;
|
struct event ev_udp;
|
||||||
|
@ -146,6 +158,8 @@ extern bool do_outgoing_connection(struct connection_t *);
|
||||||
extern void handle_new_meta_connection(int, short, void *);
|
extern void handle_new_meta_connection(int, short, void *);
|
||||||
extern int setup_listen_socket(const sockaddr_t *);
|
extern int setup_listen_socket(const sockaddr_t *);
|
||||||
extern int setup_vpn_in_socket(const sockaddr_t *);
|
extern int setup_vpn_in_socket(const sockaddr_t *);
|
||||||
|
extern bool send_sptps_data(void *handle, uint8_t type, const char *data, size_t len);
|
||||||
|
extern bool receive_sptps_record(void *handle, uint8_t type, const char *data, uint16_t len);
|
||||||
extern void send_packet(struct node_t *, vpn_packet_t *);
|
extern void send_packet(struct node_t *, vpn_packet_t *);
|
||||||
extern void receive_tcppacket(struct connection_t *, const char *, int);
|
extern void receive_tcppacket(struct connection_t *, const char *, int);
|
||||||
extern void broadcast_packet(const struct node_t *, vpn_packet_t *);
|
extern void broadcast_packet(const struct node_t *, vpn_packet_t *);
|
||||||
|
|
|
@ -265,6 +265,11 @@ static void receive_udppacket(node_t *n, vpn_packet_t *inpkt) {
|
||||||
vpn_packet_t *outpkt = pkt[0];
|
vpn_packet_t *outpkt = pkt[0];
|
||||||
size_t outlen;
|
size_t outlen;
|
||||||
|
|
||||||
|
if(experimental && OPTION_VERSION(n->options) >= 2) {
|
||||||
|
sptps_receive_data(&n->sptps, (char *)inpkt->data - 4, inpkt->len);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if(!cipher_active(&n->incipher)) {
|
if(!cipher_active(&n->incipher)) {
|
||||||
logger(DEBUG_TRAFFIC, LOG_DEBUG, "Got packet from %s (%s) but he hasn't got our key yet",
|
logger(DEBUG_TRAFFIC, LOG_DEBUG, "Got packet from %s (%s) but he hasn't got our key yet",
|
||||||
n->name, n->hostname);
|
n->name, n->hostname);
|
||||||
|
@ -430,6 +435,14 @@ static void send_udppacket(node_t *n, vpn_packet_t *origpkt) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(experimental && OPTION_VERSION(n->options) >= 2) {
|
||||||
|
uint8_t type = 0;
|
||||||
|
if(!(inpkt->data[12] | inpkt->data[13]))
|
||||||
|
type = PKT_PROBE;
|
||||||
|
sptps_send_record(&n->sptps, type, (char *)inpkt->data, inpkt->len);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
/* Compress the packet */
|
/* Compress the packet */
|
||||||
|
|
||||||
if(n->outcompression) {
|
if(n->outcompression) {
|
||||||
|
@ -531,6 +544,75 @@ end:
|
||||||
origpkt->len = origlen;
|
origpkt->len = origlen;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool send_sptps_data(void *handle, uint8_t type, const char *data, size_t len) {
|
||||||
|
node_t *to = handle;
|
||||||
|
|
||||||
|
if(type >= SPTPS_HANDSHAKE) {
|
||||||
|
char buf[len * 4 / 3 + 5];
|
||||||
|
b64encode(data, buf, len);
|
||||||
|
if(!to->status.validkey)
|
||||||
|
return send_request(to->nexthop->connection, "%d %s %s %s -1 -1 -1 -1", ANS_KEY, myself->name, to->name, buf);
|
||||||
|
else
|
||||||
|
return send_request(to->nexthop->connection, "%d %s %s %d %s", REQ_KEY, myself->name, to->name, REQ_SPTPS, buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Send the packet */
|
||||||
|
|
||||||
|
struct sockaddr *sa;
|
||||||
|
socklen_t sl;
|
||||||
|
int sock;
|
||||||
|
|
||||||
|
sa = &(to->address.sa);
|
||||||
|
sl = SALEN(to->address.sa);
|
||||||
|
sock = to->sock;
|
||||||
|
|
||||||
|
if(sendto(listen_socket[sock].udp, data, len, 0, sa, sl) < 0 && !sockwouldblock(sockerrno)) {
|
||||||
|
if(sockmsgsize(sockerrno)) {
|
||||||
|
if(to->maxmtu >= len)
|
||||||
|
to->maxmtu = len - 1;
|
||||||
|
if(to->mtu >= len)
|
||||||
|
to->mtu = len - 1;
|
||||||
|
} else {
|
||||||
|
logger(DEBUG_TRAFFIC, LOG_WARNING, "Error sending UDP SPTPS packet to %s (%s): %s", to->name, to->hostname, sockstrerror(sockerrno));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool receive_sptps_record(void *handle, uint8_t type, const char *data, uint16_t len) {
|
||||||
|
node_t *from = handle;
|
||||||
|
|
||||||
|
if(type == SPTPS_HANDSHAKE) {
|
||||||
|
from->status.validkey = true;
|
||||||
|
logger(DEBUG_META, LOG_INFO, "SPTPS key exchange with %s (%s) succesful", from->name, from->hostname);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(len > MTU) {
|
||||||
|
logger(DEBUG_ALWAYS, LOG_ERR, "Packet from %s (%s) larger than maximum supported size (%d > %d)", from->name, from->hostname, len, MTU);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
vpn_packet_t inpkt;
|
||||||
|
inpkt.len = len;
|
||||||
|
memcpy(inpkt.data, data, len);
|
||||||
|
|
||||||
|
if(type == PKT_PROBE) {
|
||||||
|
mtu_probe_h(from, &inpkt, len);
|
||||||
|
return true;
|
||||||
|
|
||||||
|
}
|
||||||
|
if(type != 0) {
|
||||||
|
logger(DEBUG_ALWAYS, LOG_ERR, "Unexpected SPTPS record type %d len %d from %s (%s)", type, len, from->name, from->hostname);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
receive_packet(from, &inpkt);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
send a packet to the given vpn ip.
|
send a packet to the given vpn ip.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -85,8 +85,8 @@ void free_node(node_t *n) {
|
||||||
cipher_close(&n->outcipher);
|
cipher_close(&n->outcipher);
|
||||||
digest_close(&n->outdigest);
|
digest_close(&n->outdigest);
|
||||||
|
|
||||||
ecdh_free(&n->ecdh);
|
|
||||||
ecdsa_free(&n->ecdsa);
|
ecdsa_free(&n->ecdsa);
|
||||||
|
sptps_stop(&n->sptps);
|
||||||
|
|
||||||
if(timeout_initialized(&n->mtuevent))
|
if(timeout_initialized(&n->mtuevent))
|
||||||
event_del(&n->mtuevent);
|
event_del(&n->mtuevent);
|
||||||
|
|
|
@ -25,7 +25,6 @@
|
||||||
#include "cipher.h"
|
#include "cipher.h"
|
||||||
#include "connection.h"
|
#include "connection.h"
|
||||||
#include "digest.h"
|
#include "digest.h"
|
||||||
#include "ecdh.h"
|
|
||||||
#include "subnet.h"
|
#include "subnet.h"
|
||||||
|
|
||||||
typedef struct node_status_t {
|
typedef struct node_status_t {
|
||||||
|
@ -50,7 +49,7 @@ typedef struct node_t {
|
||||||
time_t last_req_key;
|
time_t last_req_key;
|
||||||
|
|
||||||
ecdsa_t ecdsa; /* His public ECDSA key */
|
ecdsa_t ecdsa; /* His public ECDSA key */
|
||||||
ecdh_t ecdh; /* State for ECDH key exchange */
|
sptps_t sptps;
|
||||||
|
|
||||||
cipher_t incipher; /* Cipher for UDP packets */
|
cipher_t incipher; /* Cipher for UDP packets */
|
||||||
digest_t indigest; /* Digest for UDP packets */
|
digest_t indigest; /* Digest for UDP packets */
|
||||||
|
|
|
@ -46,6 +46,7 @@ typedef enum request_t {
|
||||||
/* Tinc 1.1 requests */
|
/* Tinc 1.1 requests */
|
||||||
CONTROL,
|
CONTROL,
|
||||||
REQ_PUBKEY, ANS_PUBKEY,
|
REQ_PUBKEY, ANS_PUBKEY,
|
||||||
|
REQ_SPTPS,
|
||||||
LAST /* Guardian for the highest request number */
|
LAST /* Guardian for the highest request number */
|
||||||
} request_t;
|
} request_t;
|
||||||
|
|
||||||
|
|
|
@ -24,13 +24,13 @@
|
||||||
#include "cipher.h"
|
#include "cipher.h"
|
||||||
#include "connection.h"
|
#include "connection.h"
|
||||||
#include "crypto.h"
|
#include "crypto.h"
|
||||||
#include "ecdh.h"
|
|
||||||
#include "logger.h"
|
#include "logger.h"
|
||||||
#include "net.h"
|
#include "net.h"
|
||||||
#include "netutl.h"
|
#include "netutl.h"
|
||||||
#include "node.h"
|
#include "node.h"
|
||||||
#include "prf.h"
|
#include "prf.h"
|
||||||
#include "protocol.h"
|
#include "protocol.h"
|
||||||
|
#include "sptps.h"
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
#include "xalloc.h"
|
#include "xalloc.h"
|
||||||
|
|
||||||
|
@ -46,11 +46,23 @@ void send_key_changed(void) {
|
||||||
|
|
||||||
for(node = connection_tree->head; node; node = node->next) {
|
for(node = connection_tree->head; node; node = node->next) {
|
||||||
c = node->data;
|
c = node->data;
|
||||||
if(c->status.active && c->node && c->node->status.reachable)
|
if(c->status.active && c->node && c->node->status.reachable) {
|
||||||
|
if(!experimental || OPTION_VERSION(c->node->options) < 2)
|
||||||
send_ans_key(c->node);
|
send_ans_key(c->node);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Force key exchange for connections using SPTPS */
|
||||||
|
|
||||||
|
if(experimental) {
|
||||||
|
for(node = node_tree->head; node; node = node->next) {
|
||||||
|
node_t *n = node->data;
|
||||||
|
if(n->status.reachable && n->status.validkey && OPTION_VERSION(n->options) >= 2)
|
||||||
|
sptps_force_kex(&n->sptps);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool key_changed_h(connection_t *c, const char *request) {
|
bool key_changed_h(connection_t *c, const char *request) {
|
||||||
char name[MAX_STRING_SIZE];
|
char name[MAX_STRING_SIZE];
|
||||||
node_t *n;
|
node_t *n;
|
||||||
|
@ -72,8 +84,10 @@ bool key_changed_h(connection_t *c, const char *request) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(OPTION_VERSION(n->options) < 2) {
|
||||||
n->status.validkey = false;
|
n->status.validkey = false;
|
||||||
n->last_req_key = 0;
|
n->last_req_key = 0;
|
||||||
|
}
|
||||||
|
|
||||||
/* Tell the others */
|
/* Tell the others */
|
||||||
|
|
||||||
|
@ -83,11 +97,26 @@ bool key_changed_h(connection_t *c, const char *request) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool send_initial_sptps_data(void *handle, uint8_t type, const char *data, size_t len) {
|
||||||
|
node_t *to = handle;
|
||||||
|
to->sptps.send_data = send_sptps_data;
|
||||||
|
char buf[len * 4 / 3 + 5];
|
||||||
|
b64encode(data, buf, len);
|
||||||
|
return send_request(to->nexthop->connection, "%d %s %s %d %s", REQ_KEY, myself->name, to->name, REQ_KEY, buf);
|
||||||
|
}
|
||||||
|
|
||||||
bool send_req_key(node_t *to) {
|
bool send_req_key(node_t *to) {
|
||||||
if(experimental && OPTION_VERSION(to->options) >= 2) {
|
if(experimental && OPTION_VERSION(to->options) >= 2) {
|
||||||
if(!node_read_ecdsa_public_key(to))
|
if(!node_read_ecdsa_public_key(to)) {
|
||||||
|
logger(DEBUG_ALWAYS, LOG_DEBUG, "No ECDSA key known for %s (%s)", to->name, to->hostname);
|
||||||
send_request(to->nexthop->connection, "%d %s %s %d", REQ_KEY, myself->name, to->name, REQ_PUBKEY);
|
send_request(to->nexthop->connection, "%d %s %s %d", REQ_KEY, myself->name, to->name, REQ_PUBKEY);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
char label[25 + strlen(myself->name) + strlen(to->name)];
|
||||||
|
snprintf(label, sizeof label, "tinc UDP key expansion %s %s", myself->name, to->name);
|
||||||
|
return sptps_start(&to->sptps, to, true, true, myself->connection->ecdsa, to->ecdsa, label, sizeof label, send_initial_sptps_data, receive_sptps_record);
|
||||||
|
}
|
||||||
|
|
||||||
return send_request(to->nexthop->connection, "%d %s %s", REQ_KEY, myself->name, to->name);
|
return send_request(to->nexthop->connection, "%d %s %s", REQ_KEY, myself->name, to->name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -108,7 +137,7 @@ static bool req_key_ext_h(connection_t *c, const char *request, node_t *from, in
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
char pubkey[4096];
|
char pubkey[MAX_STRING_SIZE];
|
||||||
if(sscanf(request, "%*d %*s %*s %*d " MAX_STRING, pubkey) != 1 || !ecdsa_set_base64_public_key(&from->ecdsa, pubkey)) {
|
if(sscanf(request, "%*d %*s %*s %*d " MAX_STRING, pubkey) != 1 || !ecdsa_set_base64_public_key(&from->ecdsa, pubkey)) {
|
||||||
logger(DEBUG_ALWAYS, LOG_ERR, "Got bad %s from %s (%s): %s", "ANS_PUBKEY", from->name, from->hostname, "invalid pubkey");
|
logger(DEBUG_ALWAYS, LOG_ERR, "Got bad %s from %s (%s): %s", "ANS_PUBKEY", from->name, from->hostname, "invalid pubkey");
|
||||||
return true;
|
return true;
|
||||||
|
@ -119,6 +148,30 @@ static bool req_key_ext_h(connection_t *c, const char *request, node_t *from, in
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case REQ_KEY: {
|
||||||
|
if(!node_read_ecdsa_public_key(from)) {
|
||||||
|
logger(DEBUG_ALWAYS, LOG_DEBUG, "No ECDSA key known for %s (%s)", from->name, from->hostname);
|
||||||
|
send_request(from->nexthop->connection, "%d %s %s %d", REQ_KEY, myself->name, from->name, REQ_PUBKEY);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
case REQ_SPTPS: {
|
||||||
|
char buf[MAX_STRING_SIZE];
|
||||||
|
if(sscanf(request, "%*d %*s %*s %*d " MAX_STRING, buf) != 1) {
|
||||||
|
logger(DEBUG_ALWAYS, LOG_ERR, "Got bad %s from %s (%s): %s", "REQ_KEY", from->name, from->hostname, "invalid SPTPS data");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
int len = b64decode(buf, buf, strlen(buf));
|
||||||
|
|
||||||
|
|
||||||
|
char label[25 + strlen(from->name) + strlen(myself->name)];
|
||||||
|
snprintf(label, sizeof label, "tinc UDP key expansion %s %s", from->name, myself->name);
|
||||||
|
sptps_start(&from->sptps, from, false, true, myself->connection->ecdsa, from->ecdsa, label, sizeof label, send_sptps_data, receive_sptps_record);
|
||||||
|
sptps_receive_data(&from->sptps, buf, len);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
logger(DEBUG_ALWAYS, LOG_ERR, "Unknown extended REQ_KEY request from %s (%s): %s", from->name, from->hostname, request);
|
logger(DEBUG_ALWAYS, LOG_ERR, "Unknown extended REQ_KEY request from %s (%s): %s", from->name, from->hostname, request);
|
||||||
return true;
|
return true;
|
||||||
|
@ -181,31 +234,9 @@ bool req_key_h(connection_t *c, const char *request) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool send_ans_key_ecdh(node_t *to) {
|
|
||||||
int siglen = ecdsa_size(&myself->connection->ecdsa);
|
|
||||||
char key[(ECDH_SIZE + siglen) * 2 + 1];
|
|
||||||
|
|
||||||
if(!ecdh_generate_public(&to->ecdh, key))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if(!ecdsa_sign(&myself->connection->ecdsa, key, ECDH_SIZE, key + ECDH_SIZE))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
b64encode(key, key, ECDH_SIZE + siglen);
|
|
||||||
|
|
||||||
int result = send_request(to->nexthop->connection, "%d %s %s %s %d %d %d %d", ANS_KEY,
|
|
||||||
myself->name, to->name, key,
|
|
||||||
cipher_get_nid(&myself->incipher),
|
|
||||||
digest_get_nid(&myself->indigest),
|
|
||||||
(int)digest_length(&myself->indigest),
|
|
||||||
myself->incompression);
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool send_ans_key(node_t *to) {
|
bool send_ans_key(node_t *to) {
|
||||||
if(experimental && OPTION_VERSION(to->options) >= 2)
|
if(experimental && OPTION_VERSION(to->options) >= 2)
|
||||||
return send_ans_key_ecdh(to);
|
abort();
|
||||||
|
|
||||||
size_t keylen = cipher_keylength(&myself->incipher);
|
size_t keylen = cipher_keylength(&myself->incipher);
|
||||||
char key[keylen * 2 + 1];
|
char key[keylen * 2 + 1];
|
||||||
|
@ -296,6 +327,29 @@ bool ans_key_h(connection_t *c, const char *request) {
|
||||||
return send_request(to->nexthop->connection, "%s", request);
|
return send_request(to->nexthop->connection, "%s", request);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* SPTPS or old-style key exchange? */
|
||||||
|
|
||||||
|
if(experimental && OPTION_VERSION(from->options) >= 2) {
|
||||||
|
char buf[strlen(key)];
|
||||||
|
int len = b64decode(key, buf, strlen(key));
|
||||||
|
|
||||||
|
if(!sptps_receive_data(&from->sptps, buf, len))
|
||||||
|
logger(DEBUG_ALWAYS, LOG_ERR, "Error processing SPTPS data from %s (%s)", from->name, from->hostname);
|
||||||
|
|
||||||
|
if(from->status.validkey) {
|
||||||
|
if(*address && *port) {
|
||||||
|
logger(DEBUG_PROTOCOL, LOG_DEBUG, "Using reflexive UDP address from %s: %s port %s", from->name, address, port);
|
||||||
|
sockaddr_t sa = str2sockaddr(address, port);
|
||||||
|
update_node_udp(from, &sa);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(from->options & OPTION_PMTU_DISCOVERY)
|
||||||
|
send_mtu_probe(from);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
/* Check and lookup cipher and digest algorithms */
|
/* Check and lookup cipher and digest algorithms */
|
||||||
|
|
||||||
if(!cipher_open_by_nid(&from->outcipher, cipher)) {
|
if(!cipher_open_by_nid(&from->outcipher, cipher)) {
|
||||||
|
@ -320,87 +374,8 @@ bool ans_key_h(connection_t *c, const char *request) {
|
||||||
|
|
||||||
from->outcompression = compression;
|
from->outcompression = compression;
|
||||||
|
|
||||||
/* ECDH or old-style key exchange? */
|
/* Process key */
|
||||||
|
|
||||||
if(experimental && OPTION_VERSION(from->options) >= 2) {
|
|
||||||
/* Check if we already have an ECDSA public key for this node. */
|
|
||||||
|
|
||||||
if(!node_read_ecdsa_public_key(from)) {
|
|
||||||
logger(DEBUG_ALWAYS, LOG_ERR, "No ECDSA public key known for %s (%s), cannot verify ECDH key exchange!", from->name, from->hostname);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
int siglen = ecdsa_size(&from->ecdsa);
|
|
||||||
int keylen = b64decode(key, key, sizeof key);
|
|
||||||
|
|
||||||
if(keylen != ECDH_SIZE + siglen) {
|
|
||||||
logger(DEBUG_ALWAYS, LOG_ERR, "Node %s (%s) uses wrong keylength! %d != %d", from->name, from->hostname, keylen, ECDH_SIZE + siglen);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(ECDH_SHARED_SIZE < cipher_keylength(&from->outcipher)) {
|
|
||||||
logger(DEBUG_ALWAYS, LOG_ERR, "ECDH key too short for cipher of %s!", from->name);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!ecdsa_verify(&from->ecdsa, key, ECDH_SIZE, key + ECDH_SIZE)) {
|
|
||||||
logger(DEBUG_ALWAYS, LOG_ERR, "Possible intruder %s (%s): %s", from->name, from->hostname, "invalid ECDSA signature");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!from->ecdh) {
|
|
||||||
if(!send_ans_key_ecdh(from))
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
char shared[ECDH_SHARED_SIZE * 2 + 1];
|
|
||||||
|
|
||||||
if(!ecdh_compute_shared(&from->ecdh, key, shared))
|
|
||||||
return true;
|
|
||||||
|
|
||||||
/* Update our crypto end */
|
|
||||||
|
|
||||||
size_t mykeylen = cipher_keylength(&myself->incipher);
|
|
||||||
size_t hiskeylen = cipher_keylength(&from->outcipher);
|
|
||||||
|
|
||||||
char *mykey;
|
|
||||||
char *hiskey;
|
|
||||||
char *seed;
|
|
||||||
|
|
||||||
if(strcmp(myself->name, from->name) < 0) {
|
|
||||||
mykey = key;
|
|
||||||
hiskey = key + mykeylen * 2;
|
|
||||||
xasprintf(&seed, "tinc UDP key expansion %s %s", myself->name, from->name);
|
|
||||||
} else {
|
|
||||||
mykey = key + hiskeylen * 2;
|
|
||||||
hiskey = key;
|
|
||||||
xasprintf(&seed, "tinc UDP key expansion %s %s", from->name, myself->name);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!prf(shared, ECDH_SHARED_SIZE, seed, strlen(seed), key, hiskeylen * 2 + mykeylen * 2))
|
|
||||||
return true;
|
|
||||||
|
|
||||||
free(seed);
|
|
||||||
|
|
||||||
cipher_open_by_nid(&from->incipher, cipher_get_nid(&myself->incipher));
|
|
||||||
digest_open_by_nid(&from->indigest, digest_get_nid(&myself->indigest), digest_length(&myself->indigest));
|
|
||||||
from->incompression = myself->incompression;
|
|
||||||
|
|
||||||
cipher_set_key(&from->incipher, mykey, false);
|
|
||||||
digest_set_key(&from->indigest, mykey + mykeylen, mykeylen);
|
|
||||||
|
|
||||||
cipher_set_key(&from->outcipher, hiskey, true);
|
|
||||||
digest_set_key(&from->outdigest, hiskey + hiskeylen, hiskeylen);
|
|
||||||
|
|
||||||
// Reset sequence number and late packet window
|
|
||||||
mykeyused = true;
|
|
||||||
from->received_seqno = 0;
|
|
||||||
if(replaywin)
|
|
||||||
memset(from->late, 0, replaywin);
|
|
||||||
|
|
||||||
if(strcmp(myself->name, from->name) < 0)
|
|
||||||
memmove(key, key + mykeylen * 2, hiskeylen * 2);
|
|
||||||
} else {
|
|
||||||
keylen = hex2bin(key, key, sizeof key);
|
keylen = hex2bin(key, key, sizeof key);
|
||||||
|
|
||||||
if(keylen != cipher_keylength(&from->outcipher)) {
|
if(keylen != cipher_keylength(&from->outcipher)) {
|
||||||
|
@ -412,7 +387,6 @@ bool ans_key_h(connection_t *c, const char *request) {
|
||||||
|
|
||||||
cipher_set_key(&from->outcipher, key, true);
|
cipher_set_key(&from->outcipher, key, true);
|
||||||
digest_set_key(&from->outdigest, key, keylen);
|
digest_set_key(&from->outdigest, key, keylen);
|
||||||
}
|
|
||||||
|
|
||||||
from->status.validkey = true;
|
from->status.validkey = true;
|
||||||
from->sent_seqno = 0;
|
from->sent_seqno = 0;
|
||||||
|
|
11
src/sptps.c
11
src/sptps.c
|
@ -78,10 +78,10 @@ static bool send_record_priv_datagram(sptps_t *s, uint8_t type, const char *data
|
||||||
if(!digest_create(&s->outdigest, buffer, len + 7UL, buffer + 7UL + len))
|
if(!digest_create(&s->outdigest, buffer, len + 7UL, buffer + 7UL + len))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
return s->send_data(s->handle, buffer + 2, len + 21UL);
|
return s->send_data(s->handle, type, buffer + 2, len + 21UL);
|
||||||
} else {
|
} else {
|
||||||
// Otherwise send as plaintext
|
// Otherwise send as plaintext
|
||||||
return s->send_data(s->handle, buffer + 2, len + 5UL);
|
return s->send_data(s->handle, type, buffer + 2, len + 5UL);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Send a record (private version, accepts all record types, handles encryption and authentication).
|
// Send a record (private version, accepts all record types, handles encryption and authentication).
|
||||||
|
@ -110,10 +110,10 @@ static bool send_record_priv(sptps_t *s, uint8_t type, const char *data, uint16_
|
||||||
if(!digest_create(&s->outdigest, buffer, len + 7UL, buffer + 7UL + len))
|
if(!digest_create(&s->outdigest, buffer, len + 7UL, buffer + 7UL + len))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
return s->send_data(s->handle, buffer + 4, len + 19UL);
|
return s->send_data(s->handle, type, buffer + 4, len + 19UL);
|
||||||
} else {
|
} else {
|
||||||
// Otherwise send as plaintext
|
// Otherwise send as plaintext
|
||||||
return s->send_data(s->handle, buffer + 4, len + 3UL);
|
return s->send_data(s->handle, type, buffer + 4, len + 3UL);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -438,6 +438,9 @@ static bool sptps_receive_data_datagram(sptps_t *s, const char *data, size_t len
|
||||||
return error(s, EIO, "Application record received before handshake finished");
|
return error(s, EIO, "Application record received before handshake finished");
|
||||||
if(!s->receive_record(s->handle, type, buffer + 7, len - 21))
|
if(!s->receive_record(s->handle, type, buffer + 7, len - 21))
|
||||||
return false;
|
return false;
|
||||||
|
} else if(type == SPTPS_HANDSHAKE) {
|
||||||
|
if(!receive_handshake(s, buffer + 7, len - 21))
|
||||||
|
return false;
|
||||||
} else {
|
} else {
|
||||||
return error(s, EIO, "Invalid record type");
|
return error(s, EIO, "Invalid record type");
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,7 +40,7 @@
|
||||||
#define SPTPS_SIG 2 // Waiting for a SIGnature record
|
#define SPTPS_SIG 2 // Waiting for a SIGnature record
|
||||||
#define SPTPS_ACK 3 // Waiting for an ACKnowledgement record
|
#define SPTPS_ACK 3 // Waiting for an ACKnowledgement record
|
||||||
|
|
||||||
typedef bool (*send_data_t)(void *handle, const char *data, size_t len);
|
typedef bool (*send_data_t)(void *handle, uint8_t type, const char *data, size_t len);
|
||||||
typedef bool (*receive_record_t)(void *handle, uint8_t type, const char *data, uint16_t len);
|
typedef bool (*receive_record_t)(void *handle, uint8_t type, const char *data, uint16_t len);
|
||||||
|
|
||||||
typedef struct sptps {
|
typedef struct sptps {
|
||||||
|
|
|
@ -32,7 +32,7 @@ char *send_meta;
|
||||||
|
|
||||||
ecdsa_t mykey, hiskey;
|
ecdsa_t mykey, hiskey;
|
||||||
|
|
||||||
static bool send_data(void *handle, const char *data, size_t len) {
|
static bool send_data(void *handle, uint8_t type, const char *data, size_t len) {
|
||||||
char hex[len * 2 + 1];
|
char hex[len * 2 + 1];
|
||||||
bin2hex(data, hex, len);
|
bin2hex(data, hex, len);
|
||||||
fprintf(stderr, "Sending %d bytes of data:\n%s\n", (int)len, hex);
|
fprintf(stderr, "Sending %d bytes of data:\n%s\n", (int)len, hex);
|
||||||
|
|
Loading…
Reference in a new issue