Merge branch 'master' into 1.1

Conflicts:
	doc/tincd.8.in
	lib/pidfile.c
	src/graph.c
	src/net.c
	src/net.h
	src/net_packet.c
	src/net_setup.c
	src/net_socket.c
	src/netutl.c
	src/node.c
	src/node.h
	src/protocol_auth.c
	src/protocol_key.c
	src/tincd.c
This commit is contained in:
Guus Sliepen 2009-06-05 23:03:28 +02:00
commit 5a132550de
23 changed files with 534 additions and 183 deletions

View file

@ -304,6 +304,10 @@ or
.Va PrivateKeyFile
specified in the configuration file.
.It Va ProcessPriority Li = low | normal | high
When this option is used the priority of the tincd process will be adjusted.
Increasing the priority may help to reduce latency and packet loss on the VPN.
.It Va TunnelServer Li = yes | no Po no Pc Bq experimental
When this option is enabled tinc will no longer forward information between other tinc daemons,
and will only allow nodes and subnets on the VPN which are present in the

View file

@ -308,7 +308,7 @@ If the @file{net/if_tun.h} header file is missing, install it from the source pa
@subsection Configuration of Darwin (MacOS/X) kernels
Tinc on Darwin relies on a tunnel driver for its data acquisition from the kernel.
Tinc supports either the driver from @uref{http://www-user.rhrk.uni-kl.de/~nissler/tuntap/},
Tinc supports either the driver from @uref{http://tuntaposx.sourceforge.net/},
which supports both tun and tap style devices,
and also the driver from from @uref{http://chrisp.de/en/projects/tunnel.html}.
The former driver is recommended.
@ -952,6 +952,11 @@ Note that there must be exactly one of PrivateKey
or PrivateKeyFile
specified in the configuration file.
@cindex ProcessPriority
@item ProcessPriority = <low|normal|high>
When this option is used the priority of the tincd process will be adjusted.
Increasing the priority may help to reduce latency and packet loss on the VPN.
@cindex TunnelServer
@item TunnelServer = <yes|no> (no) [experimental]
When this option is enabled tinc will no longer forward information between other tinc daemons,
@ -1523,6 +1528,23 @@ If @var{file} is omitted, the default is @file{@value{localstatedir}/log/tinc.@v
Disables encryption and authentication.
Only useful for debugging.
@item -R, --chroot
Change process root directory to the directory where the config file is
located (@file{@value{sysconfdir}/tinc/@var{netname}/} as determined by
-n/--net option or as given by -c/--config option), for added security.
The chroot is performed after all the initialization is done, after
writing pid files and opening network sockets.
Note that this option alone does not do any good without -U/--user, below.
Note also that tinc can't run scripts anymore (such as tinc-down or host-up),
unless it's setup to be runnable inside chroot environment.
@item -U, --user=@var{user}
Switch to the given @var{user} after initialization, at the same time as
chroot is performed (see --chroot above). With this option tinc drops
privileges, for added security.
@item --help
Display a short reminder of these runtime options and terminate.

View file

@ -8,7 +8,7 @@
.Nd tinc VPN daemon
.Sh SYNOPSIS
.Nm
.Op Fl cdDKnL
.Op Fl cdDKnLRU
.Op Fl -config Ns = Ns Ar DIR
.Op Fl -no-detach
.Op Fl -debug Ns Op = Ns Ar LEVEL
@ -16,6 +16,8 @@
.Op Fl -mlock
.Op Fl -logfile Ns Op = Ns Ar FILE
.Op Fl -bypass-security
.Op Fl -chroot
.Op Fl -user Ns = Ns Ar USER
.Op Fl -help
.Op Fl -version
.Sh DESCRIPTION
@ -70,6 +72,14 @@ is omitted, the default is
.It Fl -bypass-security
Disables encryption and authentication of the meta protocol.
Only useful for debugging.
.It Fl -chroot
With this option tinc chroots into the directory where network
config is located (@sysconfdir@/tinc/NETNAME if -n option is used,
or to the directory specified with -c option) after initialization.
.It Fl -user Ns = Ns Ar USER
setuid to the specified
.Ar USER
after initialization.
.It Fl -help
Display short list of options.
.It Fl -version

View file

@ -29,7 +29,7 @@ volatile char (*cp_file[]) = {"?", "?", "?", "?", "?", "?", "?", "?", "?", "?",
volatile int cp_index = 0;
#endif
char *hexadecimals = "0123456789ABCDEF";
const char hexadecimals[] = "0123456789ABCDEF";
int charhex2bin(char c) {
if(isdigit(c))
@ -81,7 +81,7 @@ void cp_trace() {
#include <w32api/windows.h>
#endif
char *winerror(int err) {
const char *winerror(int err) {
static char buf[1024], *newline;
if (!FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,

View file

@ -39,7 +39,7 @@ extern void hex2bin(char *src, char *dst, int length);
extern void bin2hex(char *src, char *dst, int length);
#ifdef HAVE_MINGW
extern char *winerror(int);
extern const char *winerror(int);
#define strerror(x) ((x)>0?strerror(x):winerror(GetLastError()))
#endif

View file

@ -312,27 +312,8 @@ void sssp_bfs(void) {
e->to->via = indirect ? n->via : e->to;
e->to->options = e->options;
if(sockaddrcmp(&e->to->address, &e->address)) {
node = splay_unlink(node_udp_tree, e->to);
sockaddrfree(&e->to->address);
sockaddrcpy(&e->to->address, &e->address);
if(e->to->hostname)
free(e->to->hostname);
e->to->hostname = sockaddr2hostname(&e->to->address);
if(node)
splay_insert_node(node_udp_tree, node);
if(e->to->options & OPTION_PMTU_DISCOVERY) {
e->to->mtuprobes = 0;
e->to->minmtu = 0;
e->to->maxmtu = MTU;
if(e->to->status.validkey)
send_mtu_probe(e->to);
}
}
if(e->to->address.sa.sa_family == AF_UNSPEC && e->address.sa.sa_family != AF_UNKNOWN)
update_node_udp(e->to, &e->address);
list_insert_tail(todo_list, e->to);
}
@ -364,13 +345,13 @@ void check_reachability() {
if(n->status.reachable) {
ifdebug(TRAFFIC) logger(LOG_DEBUG, _("Node %s (%s) became reachable"),
n->name, n->hostname);
splay_insert(node_udp_tree, n);
} else {
ifdebug(TRAFFIC) logger(LOG_DEBUG, _("Node %s (%s) became unreachable"),
n->name, n->hostname);
splay_delete(node_udp_tree, n);
}
/* TODO: only clear status.validkey if node is unreachable? */
n->status.validkey = false;
n->status.waitingforkey = false;

View file

@ -62,7 +62,8 @@ bool setup_device(void) {
if(!get_config_string(lookup_config(config_tree, "Interface"), &iface))
#ifdef HAVE_LINUX_IF_TUN_H
iface = xstrdup(netname);
if (netname != NULL)
iface = xstrdup(netname);
#else
iface = xstrdup(rindex(device, '/') ? rindex(device, '/') + 1 : device);
#endif

View file

@ -131,7 +131,7 @@ extern int setup_vpn_in_socket(const sockaddr_t *);
extern void send_packet(const struct node_t *, vpn_packet_t *);
extern void receive_tcppacket(struct connection_t *, char *, int);
extern void broadcast_packet(const struct node_t *, vpn_packet_t *);
extern bool setup_network_connections(void);
extern bool setup_network(void);
extern void setup_outgoing_connection(struct outgoing_t *);
extern void try_outgoing_connections(void);
extern void close_network_connections(void);

View file

@ -49,6 +49,7 @@
#endif
int keylifetime = 0;
int keyexpires = 0;
static char lzo_wrkmem[LZO1X_999_MEM_COMPRESS > LZO1X_1_MEM_COMPRESS ? LZO1X_999_MEM_COMPRESS : LZO1X_1_MEM_COMPRESS];
static void send_udppacket(node_t *, vpn_packet_t *);
@ -99,15 +100,15 @@ void send_mtu_probe(node_t *n) {
send_mtu_probe_handler(0, 0, n);
}
void mtu_probe_h(node_t *n, vpn_packet_t *packet) {
void mtu_probe_h(node_t *n, vpn_packet_t *packet, length_t len) {
ifdebug(TRAFFIC) logger(LOG_INFO, _("Got MTU probe length %d from %s (%s)"), packet->len, n->name, n->hostname);
if(!packet->data[0]) {
packet->data[0] = 1;
send_packet(n, packet);
} else {
if(n->minmtu < packet->len)
n->minmtu = packet->len;
if(n->minmtu < len)
n->minmtu = len;
}
}
@ -160,7 +161,16 @@ static void receive_packet(node_t *n, vpn_packet_t *packet) {
route(n, packet);
}
static void receive_udppacket(node_t *n, vpn_packet_t *inpkt) {
static bool try_mac(node_t *n, const vpn_packet_t *inpkt)
{
if(!digest_active(&n->indigest) || !n->inmaclength || inpkt->len < sizeof inpkt->seqno + n->inmaclength)
return false;
return digest_verify(&n->indigest, &inpkt->seqno, inpkt->len, &inpkt->seqno + inpkt->len);
}
static void receive_udppacket(node_t *n, vpn_packet_t *inpkt)
{
vpn_packet_t pkt1, pkt2;
vpn_packet_t *pkt[] = { &pkt1, &pkt2, &pkt1, &pkt2 };
int nextpkt = 0;
@ -170,9 +180,15 @@ static void receive_udppacket(node_t *n, vpn_packet_t *inpkt) {
cp();
if(!cipher_active(&n->incipher)) {
ifdebug(TRAFFIC) logger(LOG_DEBUG, _("Got packet from %s (%s) but he hasn't got our key yet"),
n->name, n->hostname);
return;
}
/* Check packet length */
if(inpkt->len < sizeof inpkt->seqno + digest_length(&myself->digest)) {
if(inpkt->len < sizeof inpkt->seqno + digest_length(&n->indigest)) {
ifdebug(TRAFFIC) logger(LOG_DEBUG, _("Got too short packet from %s (%s)"),
n->name, n->hostname);
return;
@ -180,18 +196,18 @@ static void receive_udppacket(node_t *n, vpn_packet_t *inpkt) {
/* Check the message authentication code */
if(digest_active(&myself->digest) && !digest_verify(&myself->digest, &inpkt->seqno, inpkt->len, &inpkt->seqno + inpkt->len)) {
if(digest_active(&n->indigest) && !digest_verify(&n->indigest, &inpkt->seqno, inpkt->len, &inpkt->seqno + inpkt->len)) {
ifdebug(TRAFFIC) logger(LOG_DEBUG, _("Got unauthenticated packet from %s (%s)"), n->name, n->hostname);
return;
}
/* Decrypt the packet */
if(cipher_active(&myself->cipher)) {
if(cipher_active(&n->incipher)) {
outpkt = pkt[nextpkt++];
outlen = MAXSIZE;
if(!cipher_decrypt(&myself->cipher, &inpkt->seqno, inpkt->len, &outpkt->seqno, &outlen, true)) {
if(!cipher_decrypt(&n->incipher, &inpkt->seqno, inpkt->len, &outpkt->seqno, &outlen, true)) {
ifdebug(TRAFFIC) logger(LOG_DEBUG, _("Error decrypting packet from %s (%s)"), n->name, n->hostname);
return;
}
@ -233,22 +249,26 @@ static void receive_udppacket(node_t *n, vpn_packet_t *inpkt) {
/* Decompress the packet */
if(myself->compression) {
length_t origlen = inpkt->len;
if(n->incompression) {
outpkt = pkt[nextpkt++];
if((outpkt->len = uncompress_packet(outpkt->data, inpkt->data, inpkt->len, myself->compression)) < 0) {
if((outpkt->len = uncompress_packet(outpkt->data, inpkt->data, inpkt->len, n->incompression)) < 0) {
ifdebug(TRAFFIC) logger(LOG_ERR, _("Error while uncompressing packet from %s (%s)"),
n->name, n->hostname);
return;
}
inpkt = outpkt;
origlen -= MTU/64 + 20;
}
inpkt->priority = 0;
if(!inpkt->data[12] && !inpkt->data[13])
mtu_probe_h(n, inpkt);
mtu_probe_h(n, inpkt, origlen);
else
receive_packet(n, inpkt);
}
@ -290,7 +310,7 @@ static void send_udppacket(node_t *n, vpn_packet_t *origpkt) {
n->name, n->hostname);
if(!n->status.waitingforkey)
send_req_key(n->nexthop->connection, myself, n);
send_req_key(n);
n->status.waitingforkey = true;
@ -299,12 +319,14 @@ static void send_udppacket(node_t *n, vpn_packet_t *origpkt) {
return;
}
if(!n->minmtu && (inpkt->data[12] | inpkt->data[13])) {
if(n->options & OPTION_PMTU_DISCOVERY && !n->minmtu && (inpkt->data[12] | inpkt->data[13])) {
ifdebug(TRAFFIC) logger(LOG_INFO,
_("No minimum MTU established yet for %s (%s), forwarding via TCP"),
n->name, n->hostname);
send_tcppacket(n->nexthop->connection, origpkt);
return;
}
origlen = inpkt->len;
@ -312,10 +334,10 @@ static void send_udppacket(node_t *n, vpn_packet_t *origpkt) {
/* Compress the packet */
if(n->compression) {
if(n->outcompression) {
outpkt = pkt[nextpkt++];
if((outpkt->len = compress_packet(outpkt->data, inpkt->data, inpkt->len, n->compression)) < 0) {
if((outpkt->len = compress_packet(outpkt->data, inpkt->data, inpkt->len, n->outcompression)) < 0) {
ifdebug(TRAFFIC) logger(LOG_ERR, _("Error while compressing packet to %s (%s)"),
n->name, n->hostname);
return;
@ -331,11 +353,11 @@ static void send_udppacket(node_t *n, vpn_packet_t *origpkt) {
/* Encrypt the packet */
if(cipher_active(&n->cipher)) {
if(cipher_active(&n->outcipher)) {
outpkt = pkt[nextpkt++];
outlen = MAXSIZE;
if(!cipher_encrypt(&n->cipher, &inpkt->seqno, inpkt->len, &outpkt->seqno, &outlen, true)) {
if(!cipher_encrypt(&n->outcipher, &inpkt->seqno, inpkt->len, &outpkt->seqno, &outlen, true)) {
ifdebug(TRAFFIC) logger(LOG_ERR, _("Error while encrypting packet to %s (%s)"), n->name, n->hostname);
goto end;
}
@ -346,9 +368,9 @@ static void send_udppacket(node_t *n, vpn_packet_t *origpkt) {
/* Add the message authentication code */
if(digest_active(&n->digest)) {
digest_create(&n->digest, &inpkt->seqno, inpkt->len, &inpkt->seqno + inpkt->len);
inpkt->len += digest_length(&n->digest);
if(digest_active(&n->outdigest)) {
digest_create(&n->outdigest, &inpkt->seqno, inpkt->len, &inpkt->seqno + inpkt->len);
inpkt->len += digest_length(&n->outdigest);
}
/* Determine which socket we have to use */
@ -434,9 +456,15 @@ void broadcast_packet(const node_t *from, vpn_packet_t *packet) {
ifdebug(TRAFFIC) logger(LOG_INFO, _("Broadcasting packet of %d bytes from %s (%s)"),
packet->len, from->name, from->hostname);
if(from != myself)
if(from != myself) {
send_packet(myself, packet);
// In TunnelServer mode, do not forward broadcast packets.
// The MST might not be valid and create loops.
if(tunnelserver)
return;
}
for(node = connection_tree->head; node; node = node->next) {
c = node->data;
@ -445,6 +473,30 @@ void broadcast_packet(const node_t *from, vpn_packet_t *packet) {
}
}
static node_t *try_harder(const sockaddr_t *from, const vpn_packet_t *pkt) {
splay_node_t *node;
edge_t *e;
node_t *n = NULL;
for(node = edge_weight_tree->head; node; node = node->next) {
e = node->data;
if(sockaddrcmp_noport(from, &e->address))
continue;
if(!n)
n = e->to;
if(!try_mac(e->to, pkt))
continue;
n = e->to;
break;
}
return n;
}
void handle_incoming_vpn_data(int sock, short events, void *data)
{
vpn_packet_t pkt;
@ -467,11 +519,17 @@ void handle_incoming_vpn_data(int sock, short events, void *data)
n = lookup_node_udp(&from);
if(!n) {
hostname = sockaddr2hostname(&from);
logger(LOG_WARNING, _("Received UDP packet from unknown source %s"),
hostname);
free(hostname);
return;
n = try_harder(&from, &pkt);
if(n)
update_node_udp(n, &from);
else ifdebug(PROTOCOL) {
hostname = sockaddr2hostname(&from);
logger(LOG_WARNING, _("Received UDP packet from unknown source %s"), hostname);
free(hostname);
return;
}
else
return;
}
receive_udppacket(n, &pkt);

View file

@ -148,14 +148,8 @@ static void keyexpire_handler(int fd, short events, void *data) {
}
void regenerate_key() {
ifdebug(STATUS) logger(LOG_INFO, _("Regenerating symmetric key"));
if(!cipher_regenerate_key(&myself->cipher, true)) {
logger(LOG_ERR, _("Error regenerating key!"));
abort();
}
if(timeout_initialized(&keyexpire_event)) {
ifdebug(STATUS) logger(LOG_INFO, _("Expiring symmetric keys"));
event_del(&keyexpire_event);
send_key_changed(broadcast, myself);
} else {
@ -270,7 +264,7 @@ bool setup_myself(void) {
#if !defined(SOL_IP) || !defined(IP_TOS)
if(priorityinheritance)
logger(LOG_WARNING, _("PriorityInheritance not supported on this platform"));
logger(LOG_WARNING, _("%s not supported on this platform"), "PriorityInheritance");
#endif
if(!get_config_int(lookup_config(config_tree, "MACExpire"), &macexpire))
@ -305,7 +299,7 @@ bool setup_myself(void) {
if(!get_config_string(lookup_config(myself->connection->config_tree, "Cipher"), &cipher))
cipher = xstrdup("blowfish");
if(!cipher_open_by_name(&myself->cipher, cipher)) {
if(!cipher_open_by_name(&myself->incipher, cipher)) {
logger(LOG_ERR, _("Unrecognized cipher type!"));
return false;
}
@ -320,18 +314,18 @@ bool setup_myself(void) {
if(!get_config_string(lookup_config(myself->connection->config_tree, "Digest"), &digest))
digest = xstrdup("sha1");
if(!digest_open_by_name(&myself->digest, digest)) {
if(!digest_open_by_name(&myself->indigest, digest)) {
logger(LOG_ERR, _("Unrecognized digest type!"));
return false;
}
if(!get_config_int(lookup_config(myself->connection->config_tree, "MACLength"), &myself->maclength))
if(!get_config_int(lookup_config(myself->connection->config_tree, "MACLength"), &myself->inmaclength))
if(digest_active(&myself->digest)) {
if(myself->maclength > digest_length(&myself->digest)) {
if(digest_active(&myself->indigest)) {
if(myself->inmaclength > digest_length(&myself->indigest)) {
logger(LOG_ERR, _("MAC length exceeds size of digest!"));
return false;
} else if(myself->maclength < 0) {
} else if(myself->inmaclength < 0) {
logger(LOG_ERR, _("Bogus MAC length!"));
return false;
}
@ -339,13 +333,13 @@ bool setup_myself(void) {
/* Compression */
if(get_config_int(lookup_config(myself->connection->config_tree, "Compression"), &myself->compression)) {
if(myself->compression < 0 || myself->compression > 11) {
if(get_config_int(lookup_config(myself->connection->config_tree, "Compression"), &myself->incompression)) {
if(myself->incompression < 0 || myself->incompression > 11) {
logger(LOG_ERR, _("Bogus compression level!"));
return false;
}
} else
myself->compression = 0;
myself->incompression = 0;
myself->connection->outcompression = 0;
@ -467,9 +461,10 @@ bool setup_myself(void) {
}
/*
setup all initial network connections
initialize network
*/
bool setup_network_connections(void) {
bool setup_network(void)
{
cp();
init_connections();
@ -496,8 +491,6 @@ bool setup_network_connections(void) {
if(!setup_myself())
return false;
try_outgoing_connections();
return true;
}

View file

@ -33,6 +33,8 @@
#include "utils.h"
#include "xalloc.h"
#include <assert.h>
#ifdef WSAEINPROGRESS
#define EINPROGRESS WSAEINPROGRESS
#endif
@ -80,7 +82,95 @@ static void configure_tcp(connection_t *c) {
#endif
}
int setup_listen_socket(const sockaddr_t *sa) {
static bool bind_to_interface(int sd) { /* {{{ */
char *iface;
#if defined(SOL_SOCKET) && defined(SO_BINDTODEVICE)
struct ifreq ifr;
int status;
#endif /* defined(SOL_SOCKET) && defined(SO_BINDTODEVICE) */
if(!get_config_string (lookup_config (config_tree, "BindToInterface"), &iface))
return true;
#if defined(SOL_SOCKET) && defined(SO_BINDTODEVICE)
memset(&ifr, 0, sizeof(ifr));
strncpy(ifr.ifr_ifrn.ifrn_name, iface, IFNAMSIZ);
ifr.ifr_ifrn.ifrn_name[IFNAMSIZ - 1] = 0;
status = setsockopt(sd, SOL_SOCKET, SO_BINDTODEVICE, &ifr, sizeof(ifr));
if(status) {
logger(LOG_ERR, _("Can't bind to interface %s: %s"), iface,
strerror(errno));
return false;
}
#else /* if !defined(SOL_SOCKET) || !defined(SO_BINDTODEVICE) */
logger(LOG_WARNING, _("%s not supported on this platform"), "BindToInterface");
#endif
return true;
} /* }}} bool bind_to_interface */
static bool bind_to_address(connection_t *c) { /* {{{ */
char *node;
struct addrinfo *ai_list;
struct addrinfo *ai_ptr;
struct addrinfo ai_hints;
int status;
assert(c != NULL);
assert(c->socket >= 0);
node = NULL;
if(!get_config_string(lookup_config(config_tree, "BindToAddress"),
&node))
return true;
assert(node != NULL);
memset(&ai_hints, 0, sizeof(ai_hints));
ai_hints.ai_family = c->address.sa.sa_family;
/* We're called from `do_outgoing_connection' only. */
ai_hints.ai_socktype = SOCK_STREAM;
ai_hints.ai_protocol = IPPROTO_TCP;
ai_list = NULL;
status = getaddrinfo(node, /* service = */ NULL,
&ai_hints, &ai_list);
if(status) {
free(node);
logger(LOG_WARNING, _("Error looking up %s port %s: %s"),
node, _("any"), gai_strerror(status));
return false;
}
assert(ai_list != NULL);
status = -1;
for(ai_ptr = ai_list; ai_ptr != NULL; ai_ptr = ai_ptr->ai_next) {
status = bind(c->socket,
ai_list->ai_addr, ai_list->ai_addrlen);
if(!status)
break;
}
if(status) {
logger(LOG_ERR, _("Can't bind to %s/tcp: %s"), node,
strerror(errno));
} else ifdebug(CONNECTIONS) {
logger(LOG_DEBUG, "Successfully bound outgoing "
"TCP socket to %s", node);
}
free(node);
freeaddrinfo(ai_list);
return status ? false : true;
} /* }}} bool bind_to_address */
int setup_listen_socket(const sockaddr_t *sa)
{
int nfd;
char *addrstr;
int option;
@ -120,7 +210,7 @@ int setup_listen_socket(const sockaddr_t *sa) {
return -1;
}
#else
logger(LOG_WARNING, _("BindToInterface not supported on this platform"));
logger(LOG_WARNING, _("%s not supported on this platform"), "BindToInterface");
#endif
}
@ -202,24 +292,10 @@ int setup_vpn_in_socket(const sockaddr_t *sa) {
}
#endif
#if defined(SOL_SOCKET) && defined(SO_BINDTODEVICE)
{
char *iface;
struct ifreq ifr;
if(get_config_string(lookup_config(config_tree, "BindToInterface"), &iface)) {
memset(&ifr, 0, sizeof ifr);
strncpy(ifr.ifr_ifrn.ifrn_name, iface, IFNAMSIZ);
if(setsockopt(nfd, SOL_SOCKET, SO_BINDTODEVICE, &ifr, sizeof ifr)) {
closesocket(nfd);
logger(LOG_ERR, _("Can't bind to interface %s: %s"), iface,
strerror(errno));
return -1;
}
}
if (!bind_to_interface(nfd)) {
closesocket(nfd);
return -1;
}
#endif
if(bind(nfd, &sa->sa, SALEN(sa->sa))) {
closesocket(nfd);
@ -231,7 +307,7 @@ int setup_vpn_in_socket(const sockaddr_t *sa) {
}
return nfd;
}
} /* int setup_vpn_in_socket */
static void retry_outgoing_handler(int fd, short events, void *data) {
setup_outgoing_connection(data);
@ -329,6 +405,9 @@ begin:
setsockopt(c->socket, SOL_IPV6, IPV6_V6ONLY, &option, sizeof option);
#endif
bind_to_interface(c->socket);
bind_to_address(c);
/* Optimize TCP settings */
configure_tcp(c);

View file

@ -140,7 +140,41 @@ char *sockaddr2hostname(const sockaddr_t *sa) {
return str;
}
int sockaddrcmp(const sockaddr_t *a, const sockaddr_t *b) {
int sockaddrcmp_noport(const sockaddr_t *a, const sockaddr_t *b)
{
int result;
cp();
result = a->sa.sa_family - b->sa.sa_family;
if(result)
return result;
switch (a->sa.sa_family) {
case AF_UNSPEC:
return 0;
case AF_UNKNOWN:
return strcmp(a->unknown.address, b->unknown.address);
case AF_INET:
return memcmp(&a->in.sin_addr, &b->in.sin_addr, sizeof(a->in.sin_addr));
case AF_INET6:
return memcmp(&a->in6.sin6_addr, &b->in6.sin6_addr, sizeof(a->in6.sin6_addr));
default:
logger(LOG_ERR, _("sockaddrcmp() was called with unknown address family %d, exitting!"),
a->sa.sa_family);
cp_trace();
raise(SIGFPE);
exit(0);
}
}
int sockaddrcmp(const sockaddr_t *a, const sockaddr_t *b)
{
int result;
cp();

View file

@ -32,6 +32,7 @@ extern sockaddr_t str2sockaddr(const char *, const char *);
extern void sockaddr2str(const sockaddr_t *, char **, char **);
extern char *sockaddr2hostname(const sockaddr_t *);
extern int sockaddrcmp(const sockaddr_t *, const sockaddr_t *);
extern int sockaddrcmp_noport(const sockaddr_t *, const sockaddr_t *);
extern void sockaddrunmap(sockaddr_t *);
extern void sockaddrfree(sockaddr_t *);
extern void sockaddrcpy(sockaddr_t *, const sockaddr_t *);

View file

@ -90,8 +90,10 @@ void free_node(node_t *n) {
sockaddrfree(&n->address);
cipher_close(&n->cipher);
digest_close(&n->digest);
cipher_close(&n->incipher);
digest_close(&n->indigest);
cipher_close(&n->outcipher);
digest_close(&n->outdigest);
event_del(&n->mtuevent);
@ -130,6 +132,7 @@ void node_del(node_t *n) {
}
splay_delete(node_tree, n);
splay_delete(node_udp_tree, n);
}
node_t *lookup_node(char *name) {
@ -153,6 +156,25 @@ node_t *lookup_node_udp(const sockaddr_t *sa) {
return splay_search(node_udp_tree, &n);
}
void update_node_udp(node_t *n, const sockaddr_t *sa)
{
splay_delete(node_udp_tree, n);
if(n->hostname)
free(n->hostname);
if(sa) {
n->address = *sa;
n->hostname = sockaddr2hostname(&n->address);
splay_delete(node_udp_tree, n);
splay_insert(node_udp_tree, n);
logger(LOG_DEBUG, "UDP address of %s set to %s", n->name, n->hostname);
} else {
memset(&n->address, 0, sizeof n->address);
logger(LOG_DEBUG, "UDP address of %s cleared", n->name);
}
}
int dump_nodes(struct evbuffer *out) {
splay_node_t *node;
node_t *n;
@ -162,8 +184,8 @@ int dump_nodes(struct evbuffer *out) {
for(node = node_tree->head; node; node = node->next) {
n = node->data;
if(evbuffer_add_printf(out, _(" %s at %s cipher %d digest %d maclength %d compression %d options %lx status %04x nexthop %s via %s distance %d pmtu %d (min %d max %d)\n"),
n->name, n->hostname, cipher_get_nid(&n->cipher),
digest_get_nid(&n->digest), n->maclength, n->compression,
n->name, n->hostname, cipher_get_nid(&n->outcipher),
digest_get_nid(&n->outdigest), n->outmaclength, n->outcompression,
n->options, *(uint32_t *)&n->status, n->nexthop ? n->nexthop->name : "-",
n->via ? n->via->name : "-", n->distance, n->mtu, n->minmtu, n->maxmtu) == -1)
return errno;

View file

@ -54,11 +54,16 @@ typedef struct node_t {
node_status_t status;
cipher_t cipher; /* Cipher for UDP packets */
digest_t digest; /* Digest for UDP packets */
int maclength; /* Portion of digest to use */
cipher_t incipher; /* Cipher for UDP packets */
digest_t indigest; /* Digest for UDP packets */
int inmaclength; /* Portion of digest to use */
int compression; /* Compressionlevel, 0 = no compression */
cipher_t outcipher; /* Cipher for UDP packets */
digest_t outdigest; /* Digest for UDP packets */
int outmaclength; /* Portion of digest to use */
int incompression; /* Compressionlevel, 0 = no compression */
int outcompression; /* Compressionlevel, 0 = no compression */
int distance;
struct node_t *nexthop; /* nearest node from us to him */
@ -94,5 +99,6 @@ extern void node_del(node_t *);
extern node_t *lookup_node(char *);
extern node_t *lookup_node_udp(const sockaddr_t *);
extern int dump_nodes(struct evbuffer *);
extern void update_node_udp(node_t *, const sockaddr_t *);
#endif /* __TINC_NODE_H__ */

View file

@ -52,7 +52,7 @@ bool digest_open_sha1(digest_t *digest) {
void digest_close(digest_t *digest) {
}
bool digest_create(digest_t *digest, void *indata, size_t inlen, void *outdata) {
bool digest_create(digest_t *digest, const void *indata, size_t inlen, void *outdata) {
EVP_MD_CTX ctx;
if(EVP_DigestInit(&ctx, digest->digest)
@ -64,7 +64,7 @@ bool digest_create(digest_t *digest, void *indata, size_t inlen, void *outdata)
return false;
}
bool digest_verify(digest_t *digest, void *indata, size_t inlen, void *cmpdata) {
bool digest_verify(digest_t *digest, const void *indata, size_t inlen, const void *cmpdata) {
size_t len = EVP_MD_size(digest->digest);
char outdata[len];

View file

@ -34,8 +34,8 @@ extern bool digest_open_by_name(struct digest *, const char *);
extern bool digest_open_by_nid(struct digest *, int);
extern bool digest_open_sha1(struct digest *);
extern void digest_close(struct digest *);
extern bool digest_create(struct digest *, void *indata, size_t inlen, void *outdata);
extern bool digest_verify(struct digest *, void *indata, size_t inlen, void *digestdata);
extern bool digest_create(struct digest *, const void *indata, size_t inlen, void *outdata);
extern bool digest_verify(struct digest *, const void *indata, size_t inlen, const void *digestdata);
extern int digest_get_nid(const struct digest *);
extern size_t digest_length(const struct digest *);
extern bool digest_active(const struct digest *);

View file

@ -96,9 +96,9 @@ extern bool send_add_subnet(struct connection_t *, const struct subnet_t *);
extern bool send_del_subnet(struct connection_t *, const struct subnet_t *);
extern bool send_add_edge(struct connection_t *, const struct edge_t *);
extern bool send_del_edge(struct connection_t *, const struct edge_t *);
extern bool send_key_changed(struct connection_t *, const struct node_t *);
extern bool send_req_key(struct connection_t *, const struct node_t *, const struct node_t *);
extern bool send_ans_key(struct connection_t *, const struct node_t *, const struct node_t *);
extern bool send_key_changed();
extern bool send_req_key(struct node_t *);
extern bool send_ans_key(struct node_t *);
extern bool send_tcppacket(struct connection_t *, struct vpn_packet_t *);
/* Request handlers */

View file

@ -243,7 +243,7 @@ bool send_challenge(connection_t *c) {
cp();
if(!c->hischallenge)
c->hischallenge = xmalloc(len);
c->hischallenge = xrealloc(c->hischallenge, len);
/* Copy random data to the buffer */

View file

@ -93,6 +93,17 @@ bool add_edge_h(connection_t *c, char *request) {
/* Lookup nodes */
from = lookup_node(from_name);
to = lookup_node(to_name);
if(tunnelserver &&
from != myself && from != c->node &&
to != myself && to != c->node) {
/* ignore indirect edge registrations for tunnelserver */
ifdebug(PROTOCOL) logger(LOG_WARNING,
_("Ignoring indirect %s from %s (%s)"),
"ADD_EDGE", c->name, c->hostname);
return true;
}
if(!from) {
from = new_node();
@ -100,16 +111,12 @@ bool add_edge_h(connection_t *c, char *request) {
node_add(from);
}
to = lookup_node(to_name);
if(!to) {
to = new_node();
to->name = xstrdup(to_name);
node_add(to);
}
if(tunnelserver && from != myself && from != c->node && to != myself && to != c->node)
return false;
/* Convert addresses */
@ -206,6 +213,17 @@ bool del_edge_h(connection_t *c, char *request) {
/* Lookup nodes */
from = lookup_node(from_name);
to = lookup_node(to_name);
if(tunnelserver &&
from != myself && from != c->node &&
to != myself && to != c->node) {
/* ignore indirect edge registrations for tunnelserver */
ifdebug(PROTOCOL) logger(LOG_WARNING,
_("Ignoring indirect %s from %s (%s)"),
"DEL_EDGE", c->name, c->hostname);
return true;
}
if(!from) {
ifdebug(PROTOCOL) logger(LOG_ERR, _("Got %s from %s (%s) which does not appear in the edge tree"),
@ -213,17 +231,12 @@ bool del_edge_h(connection_t *c, char *request) {
return true;
}
to = lookup_node(to_name);
if(!to) {
ifdebug(PROTOCOL) logger(LOG_ERR, _("Got %s from %s (%s) which does not appear in the edge tree"),
"DEL_EDGE", c->name, c->hostname);
return true;
}
if(tunnelserver && from != myself && from != c->node && to != myself && to != c->node)
return false;
/* Check if edge exists */
e = lookup_edge(from, to);

View file

@ -25,6 +25,7 @@
#include "splay_tree.h"
#include "cipher.h"
#include "connection.h"
#include "crypto.h"
#include "logger.h"
#include "net.h"
#include "netutl.h"
@ -35,17 +36,17 @@
static bool mykeyused = false;
bool send_key_changed(connection_t *c, const node_t *n) {
bool send_key_changed() {
cp();
/* Only send this message if some other daemon requested our key previously.
This reduces unnecessary key_changed broadcasts.
*/
if(n == myself && !mykeyused)
if(!mykeyused)
return true;
return send_request(c, "%d %lx %s", KEY_CHANGED, random(), n->name);
return send_request(broadcast, "%d %lx %s", KEY_CHANGED, random(), myself->name);
}
bool key_changed_h(connection_t *c, char *request) {
@ -82,10 +83,10 @@ bool key_changed_h(connection_t *c, char *request) {
return true;
}
bool send_req_key(connection_t *c, const node_t *from, const node_t *to) {
bool send_req_key(node_t *to) {
cp();
return send_request(c, "%d %s %s", REQ_KEY, from->name, to->name);
return send_request(to->nexthop->connection, "%d %s %s", REQ_KEY, myself->name, to->name);
}
bool req_key_h(connection_t *c, char *request) {
@ -120,10 +121,8 @@ bool req_key_h(connection_t *c, char *request) {
/* Check if this key request is for us */
if(to == myself) { /* Yes, send our own key back */
mykeyused = true;
from->received_seqno = 0;
memset(from->late, 0, sizeof from->late);
send_ans_key(c, myself, from);
send_ans_key(from);
} else {
if(tunnelserver)
return false;
@ -134,27 +133,39 @@ bool req_key_h(connection_t *c, char *request) {
return true;
}
send_req_key(to->nexthop->connection, from, to);
send_request(to->nexthop->connection, "%s", request);
}
return true;
}
bool send_ans_key(connection_t *c, const node_t *from, const node_t *to) {
size_t keylen = cipher_keylength(&from->cipher);
bool send_ans_key(node_t *to) {
size_t keylen = cipher_keylength(&myself->incipher);
char key[keylen * 2 + 1];
cp();
cipher_get_key(&from->cipher, key);
cipher_open_by_nid(&to->incipher, cipher_get_nid(&myself->incipher));
digest_open_by_nid(&to->indigest, digest_get_nid(&myself->indigest));
to->inmaclength = myself->inmaclength;
to->incompression = myself->incompression;
randomize(key, keylen);
cipher_set_key(&to->incipher, key, true);
bin2hex(key, key, keylen);
key[keylen * 2] = '\0';
return send_request(c, "%d %s %s %s %d %d %d %d", ANS_KEY,
from->name, to->name, key,
cipher_get_nid(&from->cipher),
digest_get_nid(&from->digest), from->maclength,
from->compression);
// Reset sequence number and late packet window
mykeyused = true;
to->received_seqno = 0;
memset(to->late, 0, sizeof(to->late));
return send_request(to->nexthop->connection, "%d %s %s %s %d %d %d %d", ANS_KEY,
myself->name, to->name, key,
cipher_get_nid(&to->incipher),
digest_get_nid(&to->indigest), to->inmaclength,
to->incompression);
}
bool ans_key_h(connection_t *c, char *request) {
@ -207,24 +218,24 @@ bool ans_key_h(connection_t *c, char *request) {
/* Check and lookup cipher and digest algorithms */
if(!cipher_open_by_nid(&from->cipher, cipher)) {
if(!cipher_open_by_nid(&from->outcipher, cipher)) {
logger(LOG_ERR, _("Node %s (%s) uses unknown cipher!"), from->name, from->hostname);
return false;
}
if(strlen(key) / 2 != cipher_keylength(&from->cipher)) {
if(strlen(key) / 2 != cipher_keylength(&from->outcipher)) {
logger(LOG_ERR, _("Node %s (%s) uses wrong keylength!"), from->name, from->hostname);
return false;
}
from->maclength = maclength;
from->outmaclength = maclength;
if(!digest_open_by_nid(&from->digest, digest)) {
if(!digest_open_by_nid(&from->outdigest, digest)) {
logger(LOG_ERR, _("Node %s (%s) uses unknown digest!"), from->name, from->hostname);
return false;
}
if(from->maclength > digest_length(&from->digest) || from->maclength < 0) {
if(from->outmaclength > digest_length(&from->outdigest) || from->outmaclength < 0) {
logger(LOG_ERR, _("Node %s (%s) uses bogus MAC length!"), from->name, from->hostname);
return false;
}
@ -234,12 +245,12 @@ bool ans_key_h(connection_t *c, char *request) {
return false;
}
from->compression = compression;
from->outcompression = compression;
/* Update our copy of the origin's packet key */
hex2bin(key, key, cipher_keylength(&from->cipher));
cipher_set_key(&from->cipher, key, false);
hex2bin(key, key, cipher_keylength(&from->outcipher));
cipher_set_key(&from->outcipher, key, false);
from->status.validkey = true;
from->status.waitingforkey = false;

View file

@ -60,7 +60,7 @@ bool add_subnet_h(connection_t *c, char *request)
return false;
}
/* Check if owner name is a valid */
/* Check if owner name is valid */
if(!check_id(name)) {
logger(LOG_ERR, _("Got bad %s from %s (%s): %s"), "ADD_SUBNET", c->name,
@ -127,8 +127,11 @@ bool add_subnet_h(connection_t *c, char *request)
free_subnet(allowed);
}
if(!cfg)
if(!cfg) {
logger(LOG_WARNING, _("Unauthorized %s from %s (%s) for %s"),
"ADD_SUBNET", c->name, c->hostname, subnetstr);
return false;
}
free_subnet(allowed);
}
@ -176,7 +179,7 @@ bool del_subnet_h(connection_t *c, char *request)
return false;
}
/* Check if owner name is a valid */
/* Check if owner name is valid */
if(!check_id(name)) {
logger(LOG_ERR, _("Got bad %s from %s (%s): %s"), "DEL_SUBNET", c->name,
@ -184,19 +187,6 @@ bool del_subnet_h(connection_t *c, char *request)
return false;
}
/* Check if the owner of the new subnet is in the connection list */
owner = lookup_node(name);
if(!owner) {
ifdebug(PROTOCOL) logger(LOG_WARNING, _("Got %s from %s (%s) for %s which is not in our node tree"),
"DEL_SUBNET", c->name, c->hostname, name);
return true;
}
if(tunnelserver && owner != myself && owner != c->node)
return false;
/* Check if subnet string is valid */
if(!str2net(&s, subnetstr)) {
@ -208,6 +198,23 @@ bool del_subnet_h(connection_t *c, char *request)
if(seen_request(request))
return true;
/* Check if the owner of the subnet being deleted is in the connection list */
owner = lookup_node(name);
if(tunnelserver && owner != myself && owner != c->node) {
/* in case of tunnelserver, ignore indirect subnet deletion */
ifdebug(PROTOCOL) logger(LOG_WARNING, _("Ignoring indirect %s from %s (%s) for %s"),
"DEL_SUBNET", c->name, c->hostname, subnetstr);
return true;
}
if(!owner) {
ifdebug(PROTOCOL) logger(LOG_WARNING, _("Got %s from %s (%s) for %s which is not in our node tree"),
"DEL_SUBNET", c->name, c->hostname, name);
return true;
}
/* If everything is correct, delete the subnet from the list of the owner */
s.owner = owner;

View file

@ -33,6 +33,12 @@
#include LZO1X_H
#ifndef HAVE_MINGW
#include <pwd.h>
#include <grp.h>
#include <time.h>
#endif
#include <getopt.h>
#include "conf.h"
@ -62,6 +68,12 @@ bool bypass_security = false;
/* If nonzero, disable swapping for this process. */
bool do_mlock = false;
/* If nonzero, chroot to netdir after startup. */
static bool do_chroot = false;
/* If !NULL, do setuid to given user after startup */
static const char *switchuser = NULL;
/* If nonzero, write log entries to a separate file. */
bool use_logfile = false;
@ -81,6 +93,8 @@ static struct option const long_options[] = {
{"debug", optional_argument, NULL, 'd'},
{"bypass-security", no_argument, NULL, 3},
{"mlock", no_argument, NULL, 'L'},
{"chroot", no_argument, NULL, 'R'},
{"user", required_argument, NULL, 'U'},
{"logfile", optional_argument, NULL, 4},
{"controlsocket", required_argument, NULL, 5},
{NULL, 0, NULL, 0}
@ -105,7 +119,8 @@ static void usage(bool status)
" --logfile[=FILENAME] Write log entries to a logfile.\n"
" --controlsocket=FILENAME Open control socket at FILENAME.\n"
" --bypass-security Disables meta protocol security, for debugging.\n"
" --help Display this help and exit.\n"
" -R, --chroot chroot to NET dir at startup.\n"
" -U, --user=USER setuid to given USER at startup.\n" " --help Display this help and exit.\n"
" --version Output version information and exit.\n\n"));
printf(_("Report bugs to tinc@tinc-vpn.org.\n"));
}
@ -116,7 +131,7 @@ static bool parse_options(int argc, char **argv)
int r;
int option_index = 0;
while((r = getopt_long(argc, argv, "c:DLd::n:", long_options, &option_index)) != EOF) {
while((r = getopt_long(argc, argv, "c:DLd::n:RU:", long_options, &option_index)) != EOF) {
switch (r) {
case 0: /* long option */
break;
@ -130,8 +145,13 @@ static bool parse_options(int argc, char **argv)
break;
case 'L': /* no detach */
#ifndef HAVE_MLOCKALL
logger(LOG_ERR, _("%s not supported on this platform"), "mlockall()");
return false;
#else
do_mlock = true;
break;
#endif
case 'd': /* inc debug level */
if(optarg)
@ -144,6 +164,14 @@ static bool parse_options(int argc, char **argv)
netname = xstrdup(optarg);
break;
case 'R': /* chroot to NETNAME dir */
do_chroot = true;
break;
case 'U': /* setuid to USER */
switchuser = optarg;
break;
case 1: /* show help */
show_help = true;
break;
@ -237,6 +265,63 @@ static void free_names() {
if (confbase) free(confbase);
}
static bool drop_privs() {
#ifdef HAVE_MINGW
if (switchuser) {
logger(LOG_ERR, _("%s not supported on this platform"), "-U");
return false;
}
if (do_chroot) {
logger(LOG_ERR, _("%s not supported on this platform"), "-R");
return false;
}
#else
uid_t uid = 0;
if (switchuser) {
struct passwd *pw = getpwnam(switchuser);
if (!pw) {
logger(LOG_ERR, _("unknown user `%s'"), switchuser);
return false;
}
uid = pw->pw_uid;
if (initgroups(switchuser, pw->pw_gid) != 0 ||
setgid(pw->pw_gid) != 0) {
logger(LOG_ERR, _("System call `%s' failed: %s"),
"initgroups", strerror(errno));
return false;
}
endgrent();
endpwent();
}
if (do_chroot) {
tzset(); /* for proper timestamps in logs */
if (chroot(confbase) != 0 || chdir("/") != 0) {
logger(LOG_ERR, _("System call `%s' failed: %s"),
"chroot", strerror(errno));
return false;
}
free(confbase);
confbase = xstrdup("");
}
if (switchuser)
if (setuid(uid) != 0) {
logger(LOG_ERR, _("System call `%s' failed: %s"),
"setuid", strerror(errno));
return false;
}
#endif
return true;
}
#ifdef HAVE_MINGW
# define setpriority(level) SetPriorityClass(GetCurrentProcess(), level);
#else
# define NORMAL_PRIORITY_CLASS 0
# define BELOW_NORMAL_PRIORITY_CLASS 10
# define HIGH_PRIORITY_CLASS -10
# define setpriority(level) nice(level)
#endif
int main(int argc, char **argv)
{
program_name = argv[0];
@ -277,20 +362,6 @@ int main(int argc, char **argv)
if(!init_control())
return 1;
/* Lock all pages into memory if requested */
if(do_mlock)
#ifdef HAVE_MLOCKALL
if(mlockall(MCL_CURRENT | MCL_FUTURE)) {
logger(LOG_ERR, _("System call `%s' failed: %s"), "mlockall",
strerror(errno));
#else
{
logger(LOG_ERR, _("mlockall() not supported on this platform!"));
#endif
return -1;
}
g_argv = argv;
init_configuration(&config_tree);
@ -326,11 +397,46 @@ int main2(int argc, char **argv)
if(!detach())
return 1;
#ifdef HAVE_MLOCKALL
/* Lock all pages into memory if requested.
* This has to be done after daemon()/fork() so it works for child.
* No need to do that in parent as it's very short-lived. */
if(do_mlock && mlockall(MCL_CURRENT | MCL_FUTURE) != 0) {
logger(LOG_ERR, _("System call `%s' failed: %s"), "mlockall",
strerror(errno));
return 1;
}
#endif
/* Setup sockets and open device. */
if(!setup_network_connections())
if(!setup_network())
goto end;
/* Initiate all outgoing connections. */
try_outgoing_connections();
/* Change process priority */
char *priority = 0;
if(get_config_string(lookup_config(config_tree, "ProcessPriority"), &priority)) {
if(!strcasecmp(priority, "Normal"))
setpriority(NORMAL_PRIORITY_CLASS);
else if(!strcasecmp(priority, "Low"))
setpriority(BELOW_NORMAL_PRIORITY_CLASS);
else if(!strcasecmp(priority, "High"))
setpriority(HIGH_PRIORITY_CLASS);
else {
logger(LOG_ERR, _("Invalid priority `%s`!"), priority);
goto end;
}
}
/* drop privileges */
if (!drop_privs())
goto end;
/* Start main loop. It only exits when tinc is killed. */
@ -353,5 +459,8 @@ end:
crypto_exit();
exit_configuration(&config_tree);
free_names();
return status;
}