2002-02-11 10:05:58 +00:00
|
|
|
/*
|
|
|
|
protocol_key.c -- handle the meta-protocol, key exchange
|
2006-04-26 13:52:58 +00:00
|
|
|
Copyright (C) 1999-2005 Ivo Timmermans,
|
2014-12-24 21:15:40 +00:00
|
|
|
2000-2014 Guus Sliepen <guus@tinc-vpn.org>
|
2002-02-11 10:05:58 +00:00
|
|
|
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
|
|
it under the terms of the GNU General Public License as published by
|
|
|
|
the Free Software Foundation; either version 2 of the License, or
|
|
|
|
(at your option) any later version.
|
|
|
|
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
GNU General Public License for more details.
|
|
|
|
|
2009-09-24 22:01:00 +00:00
|
|
|
You should have received a copy of the GNU General Public License along
|
|
|
|
with this program; if not, write to the Free Software Foundation, Inc.,
|
|
|
|
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
2002-02-11 10:05:58 +00:00
|
|
|
*/
|
|
|
|
|
2003-07-17 15:06:27 +00:00
|
|
|
#include "system.h"
|
2002-02-11 10:05:58 +00:00
|
|
|
|
2008-12-11 14:44:44 +00:00
|
|
|
#include "cipher.h"
|
2003-07-17 15:06:27 +00:00
|
|
|
#include "connection.h"
|
2009-06-05 21:03:28 +00:00
|
|
|
#include "crypto.h"
|
2003-07-17 15:06:27 +00:00
|
|
|
#include "logger.h"
|
2002-02-11 10:05:58 +00:00
|
|
|
#include "net.h"
|
|
|
|
#include "netutl.h"
|
|
|
|
#include "node.h"
|
2011-07-03 13:59:49 +00:00
|
|
|
#include "prf.h"
|
2003-07-17 15:06:27 +00:00
|
|
|
#include "protocol.h"
|
2012-07-30 16:36:59 +00:00
|
|
|
#include "sptps.h"
|
2003-07-17 15:06:27 +00:00
|
|
|
#include "utils.h"
|
|
|
|
#include "xalloc.h"
|
2002-02-11 10:05:58 +00:00
|
|
|
|
2008-12-11 14:44:44 +00:00
|
|
|
static bool mykeyused = false;
|
2002-02-11 10:05:58 +00:00
|
|
|
|
2011-05-28 01:57:20 +00:00
|
|
|
void send_key_changed(void) {
|
2012-02-20 16:12:48 +00:00
|
|
|
send_request(everyone, "%d %x %s", KEY_CHANGED, rand(), myself->name);
|
2002-09-09 21:25:28 +00:00
|
|
|
|
2010-02-03 10:18:46 +00:00
|
|
|
/* Immediately send new keys to directly connected nodes to keep UDP mappings alive */
|
2002-09-09 21:25:28 +00:00
|
|
|
|
2012-10-07 22:35:38 +00:00
|
|
|
for list_each(connection_t, c, connection_list)
|
2014-07-12 10:57:03 +00:00
|
|
|
if(c->edge && c->node && c->node->status.reachable && !c->node->status.sptps)
|
2012-10-07 22:35:38 +00:00
|
|
|
send_ans_key(c->node);
|
2012-07-30 16:36:59 +00:00
|
|
|
|
|
|
|
/* Force key exchange for connections using SPTPS */
|
|
|
|
|
|
|
|
if(experimental) {
|
2012-10-07 22:35:38 +00:00
|
|
|
for splay_each(node_t, n, node_tree)
|
2012-07-31 19:43:49 +00:00
|
|
|
if(n->status.reachable && n->status.validkey && n->status.sptps)
|
2012-07-30 16:36:59 +00:00
|
|
|
sptps_force_kex(&n->sptps);
|
2010-02-03 10:18:46 +00:00
|
|
|
}
|
2002-02-11 10:05:58 +00:00
|
|
|
}
|
|
|
|
|
2012-05-08 14:44:15 +00:00
|
|
|
bool key_changed_h(connection_t *c, const char *request) {
|
2002-09-09 21:25:28 +00:00
|
|
|
char name[MAX_STRING_SIZE];
|
|
|
|
node_t *n;
|
|
|
|
|
2007-05-19 22:23:02 +00:00
|
|
|
if(sscanf(request, "%*d %*x " MAX_STRING, name) != 1) {
|
2012-02-26 17:37:36 +00:00
|
|
|
logger(DEBUG_ALWAYS, LOG_ERR, "Got bad %s from %s (%s)", "KEY_CHANGED",
|
2002-09-09 21:25:28 +00:00
|
|
|
c->name, c->hostname);
|
2003-07-22 20:55:21 +00:00
|
|
|
return false;
|
2002-09-09 21:25:28 +00:00
|
|
|
}
|
|
|
|
|
2007-05-19 22:23:02 +00:00
|
|
|
if(seen_request(request))
|
2003-07-22 20:55:21 +00:00
|
|
|
return true;
|
2002-09-09 21:25:28 +00:00
|
|
|
|
|
|
|
n = lookup_node(name);
|
|
|
|
|
|
|
|
if(!n) {
|
2012-02-26 17:37:36 +00:00
|
|
|
logger(DEBUG_ALWAYS, LOG_ERR, "Got %s from %s (%s) origin %s which does not exist",
|
2002-09-09 21:25:28 +00:00
|
|
|
"KEY_CHANGED", c->name, c->hostname, name);
|
2010-01-23 17:48:01 +00:00
|
|
|
return true;
|
2002-09-09 21:25:28 +00:00
|
|
|
}
|
|
|
|
|
2012-07-31 19:43:49 +00:00
|
|
|
if(!n->status.sptps) {
|
2012-07-30 16:36:59 +00:00
|
|
|
n->status.validkey = false;
|
|
|
|
n->last_req_key = 0;
|
|
|
|
}
|
2002-09-09 21:25:28 +00:00
|
|
|
|
|
|
|
/* Tell the others */
|
|
|
|
|
2003-11-17 15:30:18 +00:00
|
|
|
if(!tunnelserver)
|
2007-05-19 22:23:02 +00:00
|
|
|
forward_request(c, request);
|
2002-09-09 21:25:28 +00:00
|
|
|
|
2003-07-22 20:55:21 +00:00
|
|
|
return true;
|
2002-02-11 10:05:58 +00:00
|
|
|
}
|
|
|
|
|
2014-12-24 21:15:40 +00:00
|
|
|
static bool send_initial_sptps_data(void *handle, uint8_t type, const void *data, size_t len) {
|
2012-07-30 16:36:59 +00:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2009-06-05 21:03:28 +00:00
|
|
|
bool send_req_key(node_t *to) {
|
2012-07-31 19:43:49 +00:00
|
|
|
if(to->status.sptps) {
|
2012-07-30 16:36:59 +00:00
|
|
|
if(!node_read_ecdsa_public_key(to)) {
|
2014-05-18 18:47:04 +00:00
|
|
|
logger(DEBUG_PROTOCOL, LOG_DEBUG, "No Ed25519 key known for %s (%s)", to->name, to->hostname);
|
2012-07-19 23:02:51 +00:00
|
|
|
send_request(to->nexthop->connection, "%d %s %s %d", REQ_KEY, myself->name, to->name, REQ_PUBKEY);
|
2012-07-30 16:36:59 +00:00
|
|
|
return true;
|
|
|
|
}
|
2012-10-14 12:33:54 +00:00
|
|
|
|
|
|
|
if(to->sptps.label)
|
|
|
|
logger(DEBUG_ALWAYS, LOG_DEBUG, "send_req_key(%s) called while sptps->label != NULL!", to->name);
|
|
|
|
|
2012-10-10 15:17:49 +00:00
|
|
|
char label[25 + strlen(myself->name) + strlen(to->name)];
|
2012-07-30 16:36:59 +00:00
|
|
|
snprintf(label, sizeof label, "tinc UDP key expansion %s %s", myself->name, to->name);
|
2012-08-02 15:23:51 +00:00
|
|
|
sptps_stop(&to->sptps);
|
|
|
|
to->status.validkey = false;
|
2012-10-07 11:31:19 +00:00
|
|
|
to->status.waitingforkey = true;
|
2013-03-08 13:11:15 +00:00
|
|
|
to->last_req_key = now.tv_sec;
|
2012-08-02 15:44:59 +00:00
|
|
|
to->incompression = myself->incompression;
|
2012-10-10 15:17:49 +00:00
|
|
|
return sptps_start(&to->sptps, to, true, true, myself->connection->ecdsa, to->ecdsa, label, sizeof label, send_initial_sptps_data, receive_sptps_record);
|
2012-07-19 23:02:51 +00:00
|
|
|
}
|
2012-07-30 16:36:59 +00:00
|
|
|
|
2012-07-19 23:02:51 +00:00
|
|
|
return send_request(to->nexthop->connection, "%d %s %s", REQ_KEY, myself->name, to->name);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* REQ_KEY is overloaded to allow arbitrary requests to be routed between two nodes. */
|
|
|
|
|
|
|
|
static bool req_key_ext_h(connection_t *c, const char *request, node_t *from, int reqno) {
|
|
|
|
switch(reqno) {
|
|
|
|
case REQ_PUBKEY: {
|
2014-09-21 09:38:41 +00:00
|
|
|
if(!node_read_ecdsa_public_key(from)) {
|
|
|
|
/* Request their key *before* we send our key back. Otherwise the first SPTPS packet from them will get dropped. */
|
|
|
|
logger(DEBUG_PROTOCOL, LOG_DEBUG, "Preemptively requesting Ed25519 key for %s (%s)", from->name, from->hostname);
|
|
|
|
send_request(from->nexthop->connection, "%d %s %s %d", REQ_KEY, myself->name, from->name, REQ_PUBKEY);
|
|
|
|
}
|
2013-05-01 15:17:22 +00:00
|
|
|
char *pubkey = ecdsa_get_base64_public_key(myself->connection->ecdsa);
|
2012-07-19 23:02:51 +00:00
|
|
|
send_request(from->nexthop->connection, "%d %s %s %d %s", REQ_KEY, myself->name, from->name, ANS_PUBKEY, pubkey);
|
|
|
|
free(pubkey);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
case ANS_PUBKEY: {
|
|
|
|
if(node_read_ecdsa_public_key(from)) {
|
2012-09-28 15:51:48 +00:00
|
|
|
logger(DEBUG_PROTOCOL, LOG_WARNING, "Got ANS_PUBKEY from %s (%s) even though we already have his pubkey", from->name, from->hostname);
|
2012-07-19 23:02:51 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2012-07-30 16:36:59 +00:00
|
|
|
char pubkey[MAX_STRING_SIZE];
|
2013-05-01 15:17:22 +00:00
|
|
|
if(sscanf(request, "%*d %*s %*s %*d " MAX_STRING, pubkey) != 1 || !(from->ecdsa = ecdsa_set_base64_public_key(pubkey))) {
|
2012-07-19 23:02:51 +00:00
|
|
|
logger(DEBUG_ALWAYS, LOG_ERR, "Got bad %s from %s (%s): %s", "ANS_PUBKEY", from->name, from->hostname, "invalid pubkey");
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2014-05-18 18:47:04 +00:00
|
|
|
logger(DEBUG_PROTOCOL, LOG_INFO, "Learned Ed25519 public key from %s (%s)", from->name, from->hostname);
|
|
|
|
append_config_file(from->name, "Ed25519PublicKey", pubkey);
|
2012-07-19 23:02:51 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2012-07-30 16:36:59 +00:00
|
|
|
case REQ_KEY: {
|
|
|
|
if(!node_read_ecdsa_public_key(from)) {
|
2014-05-18 18:47:04 +00:00
|
|
|
logger(DEBUG_PROTOCOL, LOG_DEBUG, "No Ed25519 key known for %s (%s)", from->name, from->hostname);
|
2012-07-30 16:36:59 +00:00
|
|
|
send_request(from->nexthop->connection, "%d %s %s %d", REQ_KEY, myself->name, from->name, REQ_PUBKEY);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2012-10-14 12:33:54 +00:00
|
|
|
if(from->sptps.label)
|
|
|
|
logger(DEBUG_ALWAYS, LOG_DEBUG, "Got REQ_KEY from %s while we already started a SPTPS session!", from->name);
|
|
|
|
|
2012-07-30 16:36:59 +00:00
|
|
|
char buf[MAX_STRING_SIZE];
|
2013-05-28 11:39:15 +00:00
|
|
|
int len;
|
|
|
|
|
|
|
|
if(sscanf(request, "%*d %*s %*s %*d " MAX_STRING, buf) != 1 || !(len = b64decode(buf, buf, strlen(buf)))) {
|
2012-10-14 12:33:54 +00:00
|
|
|
logger(DEBUG_ALWAYS, LOG_ERR, "Got bad %s from %s (%s): %s", "REQ_SPTPS_START", from->name, from->hostname, "invalid SPTPS data");
|
2012-07-30 16:36:59 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
char label[25 + strlen(from->name) + strlen(myself->name)];
|
|
|
|
snprintf(label, sizeof label, "tinc UDP key expansion %s %s", from->name, myself->name);
|
2012-08-02 15:23:51 +00:00
|
|
|
sptps_stop(&from->sptps);
|
|
|
|
from->status.validkey = false;
|
2012-10-07 11:31:19 +00:00
|
|
|
from->status.waitingforkey = true;
|
2013-03-08 13:11:15 +00:00
|
|
|
from->last_req_key = now.tv_sec;
|
2012-10-10 15:17:49 +00:00
|
|
|
sptps_start(&from->sptps, from, false, true, myself->connection->ecdsa, from->ecdsa, label, sizeof label, send_sptps_data, receive_sptps_record);
|
2012-07-30 16:36:59 +00:00
|
|
|
sptps_receive_data(&from->sptps, buf, len);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2012-10-14 12:33:54 +00:00
|
|
|
case REQ_SPTPS: {
|
2012-10-07 12:03:50 +00:00
|
|
|
if(!from->status.validkey) {
|
2012-10-14 12:33:54 +00:00
|
|
|
logger(DEBUG_PROTOCOL, LOG_ERR, "Got REQ_SPTPS from %s (%s) but we don't have a valid key yet", from->name, from->hostname);
|
2012-10-07 12:03:50 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
char buf[MAX_STRING_SIZE];
|
2013-05-28 11:39:15 +00:00
|
|
|
int len;
|
|
|
|
if(sscanf(request, "%*d %*s %*s %*d " MAX_STRING, buf) != 1 || !(len = b64decode(buf, buf, strlen(buf)))) {
|
2012-10-14 12:33:54 +00:00
|
|
|
logger(DEBUG_ALWAYS, LOG_ERR, "Got bad %s from %s (%s): %s", "REQ_SPTPS", from->name, from->hostname, "invalid SPTPS data");
|
2012-10-07 12:03:50 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
sptps_receive_data(&from->sptps, buf, len);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2012-07-19 23:02:51 +00:00
|
|
|
default:
|
|
|
|
logger(DEBUG_ALWAYS, LOG_ERR, "Unknown extended REQ_KEY request from %s (%s): %s", from->name, from->hostname, request);
|
|
|
|
return true;
|
|
|
|
}
|
2002-02-11 10:05:58 +00:00
|
|
|
}
|
|
|
|
|
2012-05-08 14:44:15 +00:00
|
|
|
bool req_key_h(connection_t *c, const char *request) {
|
2002-09-09 21:25:28 +00:00
|
|
|
char from_name[MAX_STRING_SIZE];
|
|
|
|
char to_name[MAX_STRING_SIZE];
|
|
|
|
node_t *from, *to;
|
2012-07-19 23:02:51 +00:00
|
|
|
int reqno = 0;
|
2002-09-09 21:25:28 +00:00
|
|
|
|
2012-07-19 23:02:51 +00:00
|
|
|
if(sscanf(request, "%*d " MAX_STRING " " MAX_STRING " %d", from_name, to_name, &reqno) < 2) {
|
2012-02-26 17:37:36 +00:00
|
|
|
logger(DEBUG_ALWAYS, LOG_ERR, "Got bad %s from %s (%s)", "REQ_KEY", c->name,
|
2002-09-09 21:25:28 +00:00
|
|
|
c->hostname);
|
2003-07-22 20:55:21 +00:00
|
|
|
return false;
|
2002-09-09 21:25:28 +00:00
|
|
|
}
|
|
|
|
|
2010-01-23 17:48:01 +00:00
|
|
|
if(!check_id(from_name) || !check_id(to_name)) {
|
2012-02-26 17:37:36 +00:00
|
|
|
logger(DEBUG_ALWAYS, LOG_ERR, "Got bad %s from %s (%s): %s", "REQ_KEY", c->name, c->hostname, "invalid name");
|
2010-01-23 17:48:01 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2002-09-09 21:25:28 +00:00
|
|
|
from = lookup_node(from_name);
|
|
|
|
|
|
|
|
if(!from) {
|
2012-02-26 17:37:36 +00:00
|
|
|
logger(DEBUG_ALWAYS, LOG_ERR, "Got %s from %s (%s) origin %s which does not exist in our connection list",
|
2002-09-09 21:25:28 +00:00
|
|
|
"REQ_KEY", c->name, c->hostname, from_name);
|
2010-01-23 17:48:01 +00:00
|
|
|
return true;
|
2002-09-09 21:25:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
to = lookup_node(to_name);
|
|
|
|
|
|
|
|
if(!to) {
|
2012-02-26 17:37:36 +00:00
|
|
|
logger(DEBUG_ALWAYS, LOG_ERR, "Got %s from %s (%s) destination %s which does not exist in our connection list",
|
2002-09-09 21:25:28 +00:00
|
|
|
"REQ_KEY", c->name, c->hostname, to_name);
|
2010-01-23 17:48:01 +00:00
|
|
|
return true;
|
2002-09-09 21:25:28 +00:00
|
|
|
}
|
|
|
|
|
Add UDP_INFO protocol message.
In this commit, nodes use UDP_INFO messages to provide UDP address
information. The basic principle is that the node that receives packets
sends UDP_INFO messages to the node that's sending the packets. The
message originally contains no address information, and is (hopefully)
updated with relevant address information as it gets relayed through the
metagraph - specifically, each intermediate node will update the message
with its best guess as to what the address is while forwarding it.
When a node receives an UDP_INFO message, and it doesn't have a
confirmed UDP tunnel with the originator node, it will update its
records with the new address for that node, so that it always has the
best possible guess as to how to reach that node. This applies to the
destination node of course, but also to any intermediate nodes, because
there's no reason they should pass on the free intel, and because it
results in nice behavior in the presence of relay chains (multiple nodes
in a path all trying to reach the same destination).
If, on the other hand, the node does have a confirmed UDP tunnel, it
will ignore the address information contained in the message.
In all cases, if the node that receives the message is not the
destination node specified in the message, it will forward the message
but not before overriding the address information with the one from its
own records. If the node has a confirmed UDP tunnel, that means the
message is updated with the address of the confirmed tunnel; if not,
the message simply reflects the records of the intermediate node, which
just happen to be the contents of the UDP_INFO message it just got, so
it's simply forwarded with no modification.
This is similar to the way ANS_KEY messages are currently
overloaded to provide UDP address information, with two differences:
- UDP_INFO messages are sent way more often than ANS_KEY messages,
thereby keeping the address information fresh. Previously, if the UDP
situation were to change after the ANS_KEY message was sent, the
sender would virtually never get the updated information.
- Once a node puts address information in an ANS_KEY message, it is
never changed again as the message travels through the metagraph; in
contrast, UDP_INFO messages behave the opposite way, as they get
rewritten every time they travel through a node with a confirmed UDP
tunnel. The latter behavior seems more appropriate because UDP tunnel
information becomes more relevant as it moves closer to the
destination node. The ANS_KEY behavior is not satisfactory in some
cases such as multi-layered graphs where the first hop is located
before a NAT.
Ultimately, the rationale behind this whole process is to improve UDP
hole punching capabilities when port translation is in effect, and more
generally, to make tinc more reliable in (very) hostile network
conditions (such as multi-layered NAT).
2015-01-03 17:46:33 +00:00
|
|
|
/* If this is a SPTPS packet, see if sending UDP info helps.
|
|
|
|
Note that we only do this if we're the destination or the static relay;
|
|
|
|
otherwise every hop would initiate its own UDP info message, resulting in elevated chatter. */
|
|
|
|
|
|
|
|
if(experimental && (reqno == REQ_KEY || reqno == REQ_SPTPS) && to->via == myself)
|
|
|
|
send_udp_info(myself, from);
|
|
|
|
|
2002-09-09 21:25:28 +00:00
|
|
|
/* Check if this key request is for us */
|
|
|
|
|
2012-10-10 15:17:49 +00:00
|
|
|
if(to == myself) { /* Yes */
|
2012-07-31 19:43:49 +00:00
|
|
|
/* Is this an extended REQ_KEY message? */
|
2012-07-19 23:02:51 +00:00
|
|
|
if(experimental && reqno)
|
|
|
|
return req_key_ext_h(c, request, from, reqno);
|
|
|
|
|
2012-07-31 19:43:49 +00:00
|
|
|
/* No, just send our key back */
|
2009-04-02 23:05:23 +00:00
|
|
|
send_ans_key(from);
|
2002-09-09 21:25:28 +00:00
|
|
|
} else {
|
2003-11-17 15:30:18 +00:00
|
|
|
if(tunnelserver)
|
2010-01-23 17:48:01 +00:00
|
|
|
return true;
|
2003-11-17 15:30:18 +00:00
|
|
|
|
2008-12-11 15:21:40 +00:00
|
|
|
if(!to->status.reachable) {
|
2012-09-28 15:51:48 +00:00
|
|
|
logger(DEBUG_PROTOCOL, LOG_WARNING, "Got %s from %s (%s) destination %s which is not reachable",
|
2008-12-11 15:21:40 +00:00
|
|
|
"REQ_KEY", c->name, c->hostname, to_name);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
Add UDP datagram relay support to SPTPS.
This commit changes the layout of UDP datagrams to include a 6-byte
destination node ID at the very beginning of the datagram (i.e. before
the source node ID and the seqno). Note that this only applies to SPTPS.
Thanks to this new field, it is now possible to send SPTPS datagrams to
nodes that are not the final recipient of the packets, thereby using
these nodes as relay nodes. Previously SPTPS was unable to relay packets
using UDP, and required a fallback to TCP if the final recipient could
not be contacted directly using UDP. In that sense it fixes a regression
that SPTPS introduced with regard to the legacy protocol.
This change also updates tinc's low-level routing logic (i.e.
send_sptps_data()) to automatically use this relaying facility if at all
possible. Specifically, it will relay packets if we don't have a
confirmed UDP link to the final recipient (but we have one with the next
hop node), or if IndirectData is specified. This is similar to how the
legacy protocol forwards packets.
When sending packets directly without any relaying, the sender node uses
a special value for the destination node ID: instead of setting the
field to the ID of the recipient node, it writes a zero ID instead. This
allows the recipient node to distinguish between a relayed packet and a
direct packet, which is important when determining the UDP address of
the sending node.
On the relay side, relay nodes will happily relay packets that have a
destination ID which is non-zero *and* is different from their own,
provided that the source IP address of the packet is known. This is to
prevent abuse by random strangers, since a node can't authenticate the
packets that are being relayed through it.
This change keeps the protocol number from the previous datagram format
change (source IDs), 17.4. Compatibility is still preserved with 1.0 and
with pre-1.1 releases. Note, however, that nodes running this code won't
understand datagrams sent from nodes that only use source IDs and
vice-versa (not that we really care).
There is one caveat: in the current state, there is no way for the
original sender to know what the PMTU is beyond the first hop, and
contrary to the legacy protocol, relay nodes can't apply MSS clamping
because they can't decrypt the relayed packets. This leads to
inefficient scenarios where a reduced PMTU over some link that's part of
the relay path will result in relays falling back to TCP to send packets
to their final destinations.
Another caveat is that once a packet gets sent over TCP, it will use
TCP over the entire path, even if it is technically possible to use UDP
beyond the TCP-only link(s).
Arguably, these two caveats can be fixed by improving the
metaconnection protocol, but that's out of scope for this change. TODOs
are added instead. In any case, this is no worse than before.
In addition, this change increases SPTPS datagram overhead by another
6 bytes for the destination ID, on top of the existing 6-byte overhead
from the source ID.
2014-09-28 11:38:06 +00:00
|
|
|
/* TODO: forwarding SPTPS packets in this way is inefficient because we send them over TCP without checking for UDP connectivity */
|
2009-06-05 21:03:28 +00:00
|
|
|
send_request(to->nexthop->connection, "%s", request);
|
2002-09-09 21:25:28 +00:00
|
|
|
}
|
2002-02-11 10:05:58 +00:00
|
|
|
|
2003-07-22 20:55:21 +00:00
|
|
|
return true;
|
2002-02-11 10:05:58 +00:00
|
|
|
}
|
|
|
|
|
2009-06-05 21:03:28 +00:00
|
|
|
bool send_ans_key(node_t *to) {
|
2012-07-31 19:43:49 +00:00
|
|
|
if(to->status.sptps)
|
2012-07-30 16:36:59 +00:00
|
|
|
abort();
|
2011-07-03 11:17:28 +00:00
|
|
|
|
2014-12-29 21:57:18 +00:00
|
|
|
#ifdef DISABLE_LEGACY
|
|
|
|
return false;
|
|
|
|
#else
|
2014-05-18 19:51:42 +00:00
|
|
|
size_t keylen = myself->incipher ? cipher_keylength(myself->incipher) : 1;
|
2008-12-11 14:44:44 +00:00
|
|
|
char key[keylen * 2 + 1];
|
2002-09-09 21:25:28 +00:00
|
|
|
|
2014-05-18 19:51:42 +00:00
|
|
|
randomize(key, keylen);
|
|
|
|
|
2013-05-01 15:17:22 +00:00
|
|
|
cipher_close(to->incipher);
|
|
|
|
digest_close(to->indigest);
|
2012-10-09 14:27:28 +00:00
|
|
|
|
2014-05-18 19:51:42 +00:00
|
|
|
if(myself->incipher) {
|
|
|
|
to->incipher = cipher_open_by_nid(cipher_get_nid(myself->incipher));
|
|
|
|
if(!to->incipher)
|
|
|
|
abort();
|
|
|
|
if(!cipher_set_key(to->incipher, key, false))
|
|
|
|
abort();
|
|
|
|
}
|
2002-09-09 21:25:28 +00:00
|
|
|
|
2014-05-18 19:51:42 +00:00
|
|
|
if(myself->indigest) {
|
|
|
|
to->indigest = digest_open_by_nid(digest_get_nid(myself->indigest), digest_length(myself->indigest));
|
|
|
|
if(!to->indigest)
|
|
|
|
abort();
|
|
|
|
if(!digest_set_key(to->indigest, key, keylen))
|
|
|
|
abort();
|
|
|
|
}
|
2013-05-01 15:17:22 +00:00
|
|
|
|
2014-05-18 19:51:42 +00:00
|
|
|
to->incompression = myself->incompression;
|
2009-05-24 17:31:31 +00:00
|
|
|
|
2008-12-11 14:44:44 +00:00
|
|
|
bin2hex(key, key, keylen);
|
2002-09-09 21:25:28 +00:00
|
|
|
|
2009-05-24 17:31:31 +00:00
|
|
|
// Reset sequence number and late packet window
|
|
|
|
mykeyused = true;
|
|
|
|
to->received_seqno = 0;
|
2013-01-15 12:33:16 +00:00
|
|
|
to->received = 0;
|
2010-11-13 18:05:50 +00:00
|
|
|
if(replaywin) memset(to->late, 0, replaywin);
|
2009-04-02 23:05:23 +00:00
|
|
|
|
2015-01-10 21:26:33 +00:00
|
|
|
to->status.validkey_in = true;
|
|
|
|
|
2012-07-21 10:51:53 +00:00
|
|
|
return send_request(to->nexthop->connection, "%d %s %s %s %d %d %d %d", ANS_KEY,
|
2009-06-05 21:03:28 +00:00
|
|
|
myself->name, to->name, key,
|
2013-05-01 15:17:22 +00:00
|
|
|
cipher_get_nid(to->incipher),
|
|
|
|
digest_get_nid(to->indigest),
|
|
|
|
(int)digest_length(to->indigest),
|
2009-06-05 21:03:28 +00:00
|
|
|
to->incompression);
|
2014-12-29 21:57:18 +00:00
|
|
|
#endif
|
2002-02-11 10:05:58 +00:00
|
|
|
}
|
|
|
|
|
2012-05-08 14:44:15 +00:00
|
|
|
bool ans_key_h(connection_t *c, const char *request) {
|
2002-09-09 21:25:28 +00:00
|
|
|
char from_name[MAX_STRING_SIZE];
|
|
|
|
char to_name[MAX_STRING_SIZE];
|
|
|
|
char key[MAX_STRING_SIZE];
|
2012-10-10 15:17:49 +00:00
|
|
|
char address[MAX_STRING_SIZE] = "";
|
|
|
|
char port[MAX_STRING_SIZE] = "";
|
2009-12-18 00:15:25 +00:00
|
|
|
int cipher, digest, maclength, compression, keylen;
|
2002-09-09 21:25:28 +00:00
|
|
|
node_t *from, *to;
|
|
|
|
|
2011-07-13 20:52:52 +00:00
|
|
|
if(sscanf(request, "%*d "MAX_STRING" "MAX_STRING" "MAX_STRING" %d %d %d %d "MAX_STRING" "MAX_STRING,
|
2002-09-09 21:25:28 +00:00
|
|
|
from_name, to_name, key, &cipher, &digest, &maclength,
|
2010-02-01 23:51:44 +00:00
|
|
|
&compression, address, port) < 7) {
|
2012-02-26 17:37:36 +00:00
|
|
|
logger(DEBUG_ALWAYS, LOG_ERR, "Got bad %s from %s (%s)", "ANS_KEY", c->name,
|
2002-09-09 21:25:28 +00:00
|
|
|
c->hostname);
|
2003-07-22 20:55:21 +00:00
|
|
|
return false;
|
2002-02-11 10:05:58 +00:00
|
|
|
}
|
2002-09-09 21:25:28 +00:00
|
|
|
|
2010-01-23 17:48:01 +00:00
|
|
|
if(!check_id(from_name) || !check_id(to_name)) {
|
2012-02-26 17:37:36 +00:00
|
|
|
logger(DEBUG_ALWAYS, LOG_ERR, "Got bad %s from %s (%s): %s", "ANS_KEY", c->name, c->hostname, "invalid name");
|
2010-01-23 17:48:01 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2002-09-09 21:25:28 +00:00
|
|
|
from = lookup_node(from_name);
|
|
|
|
|
|
|
|
if(!from) {
|
2012-02-26 17:37:36 +00:00
|
|
|
logger(DEBUG_ALWAYS, LOG_ERR, "Got %s from %s (%s) origin %s which does not exist in our connection list",
|
2002-09-09 21:25:28 +00:00
|
|
|
"ANS_KEY", c->name, c->hostname, from_name);
|
2010-01-23 17:48:01 +00:00
|
|
|
return true;
|
2002-09-09 21:25:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
to = lookup_node(to_name);
|
|
|
|
|
|
|
|
if(!to) {
|
2012-02-26 17:37:36 +00:00
|
|
|
logger(DEBUG_ALWAYS, LOG_ERR, "Got %s from %s (%s) destination %s which does not exist in our connection list",
|
2002-09-09 21:25:28 +00:00
|
|
|
"ANS_KEY", c->name, c->hostname, to_name);
|
2010-01-23 17:48:01 +00:00
|
|
|
return true;
|
2002-02-11 10:05:58 +00:00
|
|
|
}
|
2002-09-09 21:25:28 +00:00
|
|
|
|
|
|
|
/* Forward it if necessary */
|
|
|
|
|
|
|
|
if(to != myself) {
|
2003-11-17 15:30:18 +00:00
|
|
|
if(tunnelserver)
|
2010-01-23 17:48:01 +00:00
|
|
|
return true;
|
2003-11-17 15:30:18 +00:00
|
|
|
|
2008-12-11 15:21:40 +00:00
|
|
|
if(!to->status.reachable) {
|
2012-02-26 17:37:36 +00:00
|
|
|
logger(DEBUG_ALWAYS, LOG_WARNING, "Got %s from %s (%s) destination %s which is not reachable",
|
2008-12-11 15:21:40 +00:00
|
|
|
"ANS_KEY", c->name, c->hostname, to_name);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2010-06-04 14:03:19 +00:00
|
|
|
if(!*address && from->address.sa.sa_family != AF_UNSPEC) {
|
2010-04-17 10:33:15 +00:00
|
|
|
char *address, *port;
|
2012-02-26 17:37:36 +00:00
|
|
|
logger(DEBUG_PROTOCOL, LOG_DEBUG, "Appending reflexive UDP address to ANS_KEY from %s to %s", from->name, to->name);
|
2010-04-17 10:33:15 +00:00
|
|
|
sockaddr2str(&from->address, &address, &port);
|
|
|
|
send_request(to->nexthop->connection, "%s %s %s", request, address, port);
|
|
|
|
free(address);
|
|
|
|
free(port);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2007-05-19 22:23:02 +00:00
|
|
|
return send_request(to->nexthop->connection, "%s", request);
|
2002-02-11 10:05:58 +00:00
|
|
|
}
|
2002-09-09 21:25:28 +00:00
|
|
|
|
2014-12-29 21:57:18 +00:00
|
|
|
#ifndef DISABLE_LEGACY
|
2012-09-30 11:45:47 +00:00
|
|
|
/* Don't use key material until every check has passed. */
|
2013-05-01 15:17:22 +00:00
|
|
|
cipher_close(from->outcipher);
|
|
|
|
digest_close(from->outdigest);
|
2014-12-29 21:57:18 +00:00
|
|
|
#endif
|
2012-09-30 11:45:47 +00:00
|
|
|
from->status.validkey = false;
|
|
|
|
|
2012-08-02 15:44:59 +00:00
|
|
|
if(compression < 0 || compression > 11) {
|
|
|
|
logger(DEBUG_ALWAYS, LOG_ERR, "Node %s (%s) uses bogus compression level!", from->name, from->hostname);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
from->outcompression = compression;
|
|
|
|
|
2012-07-30 16:36:59 +00:00
|
|
|
/* SPTPS or old-style key exchange? */
|
|
|
|
|
2012-07-31 19:43:49 +00:00
|
|
|
if(from->status.sptps) {
|
2012-07-30 16:36:59 +00:00
|
|
|
char buf[strlen(key)];
|
|
|
|
int len = b64decode(key, buf, strlen(key));
|
|
|
|
|
2013-05-28 11:39:15 +00:00
|
|
|
if(!len || !sptps_receive_data(&from->sptps, buf, len))
|
2012-07-30 16:36:59 +00:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2014-12-29 21:57:18 +00:00
|
|
|
#ifdef DISABLE_LEGACY
|
2014-12-30 09:56:30 +00:00
|
|
|
logger(DEBUG_ALWAYS, LOG_ERR, "Node %s (%s) uses legacy protocol!", from->name, from->hostname);
|
2014-12-29 21:57:18 +00:00
|
|
|
return false;
|
|
|
|
#else
|
2002-09-09 21:25:28 +00:00
|
|
|
/* Check and lookup cipher and digest algorithms */
|
|
|
|
|
2013-11-28 13:19:55 +00:00
|
|
|
if(cipher) {
|
|
|
|
if(!(from->outcipher = cipher_open_by_nid(cipher))) {
|
|
|
|
logger(DEBUG_ALWAYS, LOG_ERR, "Node %s (%s) uses unknown cipher!", from->name, from->hostname);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
from->outcipher = NULL;
|
2008-12-11 14:44:44 +00:00
|
|
|
}
|
2007-11-07 02:47:05 +00:00
|
|
|
|
2013-11-28 13:19:55 +00:00
|
|
|
if(digest) {
|
|
|
|
if(!(from->outdigest = digest_open_by_nid(digest, maclength))) {
|
|
|
|
logger(DEBUG_ALWAYS, LOG_ERR, "Node %s (%s) uses unknown digest!", from->name, from->hostname);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
from->outdigest = NULL;
|
2008-12-11 14:44:44 +00:00
|
|
|
}
|
2007-11-07 02:47:05 +00:00
|
|
|
|
2013-05-01 15:17:22 +00:00
|
|
|
if(maclength != digest_length(from->outdigest)) {
|
2012-02-26 17:37:36 +00:00
|
|
|
logger(DEBUG_ALWAYS, LOG_ERR, "Node %s (%s) uses bogus MAC length!", from->name, from->hostname);
|
2008-12-11 14:44:44 +00:00
|
|
|
return false;
|
2002-09-09 21:25:28 +00:00
|
|
|
}
|
|
|
|
|
2012-07-30 16:36:59 +00:00
|
|
|
/* Process key */
|
2011-07-16 18:21:44 +00:00
|
|
|
|
2012-07-30 16:36:59 +00:00
|
|
|
keylen = hex2bin(key, key, sizeof key);
|
2011-07-03 11:17:28 +00:00
|
|
|
|
2014-05-18 19:51:42 +00:00
|
|
|
if(keylen != (from->outcipher ? cipher_keylength(from->outcipher) : 1)) {
|
2012-07-30 16:36:59 +00:00
|
|
|
logger(DEBUG_ALWAYS, LOG_ERR, "Node %s (%s) uses wrong keylength!", from->name, from->hostname);
|
|
|
|
return true;
|
|
|
|
}
|
2008-12-11 14:44:44 +00:00
|
|
|
|
2012-07-30 16:36:59 +00:00
|
|
|
/* Update our copy of the origin's packet key */
|
2011-07-03 11:17:28 +00:00
|
|
|
|
2014-05-18 19:51:42 +00:00
|
|
|
if(from->outcipher && !cipher_set_key(from->outcipher, key, true))
|
2013-05-10 18:30:47 +00:00
|
|
|
return false;
|
2014-05-18 19:51:42 +00:00
|
|
|
if(from->outdigest && !digest_set_key(from->outdigest, key, keylen))
|
2013-05-10 18:30:47 +00:00
|
|
|
return false;
|
2008-12-11 14:44:44 +00:00
|
|
|
|
|
|
|
from->status.validkey = true;
|
|
|
|
from->sent_seqno = 0;
|
2003-10-11 12:16:13 +00:00
|
|
|
|
2010-02-01 23:51:44 +00:00
|
|
|
if(*address && *port) {
|
2012-02-26 17:37:36 +00:00
|
|
|
logger(DEBUG_PROTOCOL, LOG_DEBUG, "Using reflexive UDP address from %s: %s port %s", from->name, address, port);
|
2010-02-01 23:51:44 +00:00
|
|
|
sockaddr_t sa = str2sockaddr(address, port);
|
|
|
|
update_node_udp(from, &sa);
|
|
|
|
}
|
|
|
|
|
2003-07-22 20:55:21 +00:00
|
|
|
return true;
|
2014-12-29 21:57:18 +00:00
|
|
|
#endif
|
2002-02-11 10:05:58 +00:00
|
|
|
}
|