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 .Va PrivateKeyFile
specified in the configuration file. 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 .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, 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 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 @subsection Configuration of Darwin (MacOS/X) kernels
Tinc on Darwin relies on a tunnel driver for its data acquisition from the kernel. 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, which supports both tun and tap style devices,
and also the driver from from @uref{http://chrisp.de/en/projects/tunnel.html}. and also the driver from from @uref{http://chrisp.de/en/projects/tunnel.html}.
The former driver is recommended. The former driver is recommended.
@ -952,6 +952,11 @@ Note that there must be exactly one of PrivateKey
or PrivateKeyFile or PrivateKeyFile
specified in the configuration file. 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 @cindex TunnelServer
@item TunnelServer = <yes|no> (no) [experimental] @item TunnelServer = <yes|no> (no) [experimental]
When this option is enabled tinc will no longer forward information between other tinc daemons, 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. Disables encryption and authentication.
Only useful for debugging. 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 @item --help
Display a short reminder of these runtime options and terminate. Display a short reminder of these runtime options and terminate.

View file

@ -8,7 +8,7 @@
.Nd tinc VPN daemon .Nd tinc VPN daemon
.Sh SYNOPSIS .Sh SYNOPSIS
.Nm .Nm
.Op Fl cdDKnL .Op Fl cdDKnLRU
.Op Fl -config Ns = Ns Ar DIR .Op Fl -config Ns = Ns Ar DIR
.Op Fl -no-detach .Op Fl -no-detach
.Op Fl -debug Ns Op = Ns Ar LEVEL .Op Fl -debug Ns Op = Ns Ar LEVEL
@ -16,6 +16,8 @@
.Op Fl -mlock .Op Fl -mlock
.Op Fl -logfile Ns Op = Ns Ar FILE .Op Fl -logfile Ns Op = Ns Ar FILE
.Op Fl -bypass-security .Op Fl -bypass-security
.Op Fl -chroot
.Op Fl -user Ns = Ns Ar USER
.Op Fl -help .Op Fl -help
.Op Fl -version .Op Fl -version
.Sh DESCRIPTION .Sh DESCRIPTION
@ -70,6 +72,14 @@ is omitted, the default is
.It Fl -bypass-security .It Fl -bypass-security
Disables encryption and authentication of the meta protocol. Disables encryption and authentication of the meta protocol.
Only useful for debugging. 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 .It Fl -help
Display short list of options. Display short list of options.
.It Fl -version .It Fl -version

View file

@ -29,7 +29,7 @@ volatile char (*cp_file[]) = {"?", "?", "?", "?", "?", "?", "?", "?", "?", "?",
volatile int cp_index = 0; volatile int cp_index = 0;
#endif #endif
char *hexadecimals = "0123456789ABCDEF"; const char hexadecimals[] = "0123456789ABCDEF";
int charhex2bin(char c) { int charhex2bin(char c) {
if(isdigit(c)) if(isdigit(c))
@ -81,7 +81,7 @@ void cp_trace() {
#include <w32api/windows.h> #include <w32api/windows.h>
#endif #endif
char *winerror(int err) { const char *winerror(int err) {
static char buf[1024], *newline; static char buf[1024], *newline;
if (!FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, 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); extern void bin2hex(char *src, char *dst, int length);
#ifdef HAVE_MINGW #ifdef HAVE_MINGW
extern char *winerror(int); extern const char *winerror(int);
#define strerror(x) ((x)>0?strerror(x):winerror(GetLastError())) #define strerror(x) ((x)>0?strerror(x):winerror(GetLastError()))
#endif #endif

View file

@ -312,27 +312,8 @@ void sssp_bfs(void) {
e->to->via = indirect ? n->via : e->to; e->to->via = indirect ? n->via : e->to;
e->to->options = e->options; e->to->options = e->options;
if(sockaddrcmp(&e->to->address, &e->address)) { if(e->to->address.sa.sa_family == AF_UNSPEC && e->address.sa.sa_family != AF_UNKNOWN)
node = splay_unlink(node_udp_tree, e->to); update_node_udp(e->to, &e->address);
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);
}
}
list_insert_tail(todo_list, e->to); list_insert_tail(todo_list, e->to);
} }
@ -364,13 +345,13 @@ void check_reachability() {
if(n->status.reachable) { if(n->status.reachable) {
ifdebug(TRAFFIC) logger(LOG_DEBUG, _("Node %s (%s) became reachable"), ifdebug(TRAFFIC) logger(LOG_DEBUG, _("Node %s (%s) became reachable"),
n->name, n->hostname); n->name, n->hostname);
splay_insert(node_udp_tree, n);
} else { } else {
ifdebug(TRAFFIC) logger(LOG_DEBUG, _("Node %s (%s) became unreachable"), ifdebug(TRAFFIC) logger(LOG_DEBUG, _("Node %s (%s) became unreachable"),
n->name, n->hostname); 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.validkey = false;
n->status.waitingforkey = 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)) if(!get_config_string(lookup_config(config_tree, "Interface"), &iface))
#ifdef HAVE_LINUX_IF_TUN_H #ifdef HAVE_LINUX_IF_TUN_H
iface = xstrdup(netname); if (netname != NULL)
iface = xstrdup(netname);
#else #else
iface = xstrdup(rindex(device, '/') ? rindex(device, '/') + 1 : device); iface = xstrdup(rindex(device, '/') ? rindex(device, '/') + 1 : device);
#endif #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 send_packet(const struct node_t *, vpn_packet_t *);
extern void receive_tcppacket(struct connection_t *, char *, int); extern void receive_tcppacket(struct connection_t *, char *, int);
extern void broadcast_packet(const struct node_t *, vpn_packet_t *); 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 setup_outgoing_connection(struct outgoing_t *);
extern void try_outgoing_connections(void); extern void try_outgoing_connections(void);
extern void close_network_connections(void); extern void close_network_connections(void);

View file

@ -49,6 +49,7 @@
#endif #endif
int keylifetime = 0; 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 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 *); 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); 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); ifdebug(TRAFFIC) logger(LOG_INFO, _("Got MTU probe length %d from %s (%s)"), packet->len, n->name, n->hostname);
if(!packet->data[0]) { if(!packet->data[0]) {
packet->data[0] = 1; packet->data[0] = 1;
send_packet(n, packet); send_packet(n, packet);
} else { } else {
if(n->minmtu < packet->len) if(n->minmtu < len)
n->minmtu = packet->len; n->minmtu = len;
} }
} }
@ -160,7 +161,16 @@ static void receive_packet(node_t *n, vpn_packet_t *packet) {
route(n, 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 pkt1, pkt2;
vpn_packet_t *pkt[] = { &pkt1, &pkt2, &pkt1, &pkt2 }; vpn_packet_t *pkt[] = { &pkt1, &pkt2, &pkt1, &pkt2 };
int nextpkt = 0; int nextpkt = 0;
@ -170,9 +180,15 @@ static void receive_udppacket(node_t *n, vpn_packet_t *inpkt) {
cp(); 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 */ /* 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)"), ifdebug(TRAFFIC) logger(LOG_DEBUG, _("Got too short packet from %s (%s)"),
n->name, n->hostname); n->name, n->hostname);
return; return;
@ -180,18 +196,18 @@ static void receive_udppacket(node_t *n, vpn_packet_t *inpkt) {
/* Check the message authentication code */ /* 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); ifdebug(TRAFFIC) logger(LOG_DEBUG, _("Got unauthenticated packet from %s (%s)"), n->name, n->hostname);
return; return;
} }
/* Decrypt the packet */ /* Decrypt the packet */
if(cipher_active(&myself->cipher)) { if(cipher_active(&n->incipher)) {
outpkt = pkt[nextpkt++]; outpkt = pkt[nextpkt++];
outlen = MAXSIZE; 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); ifdebug(TRAFFIC) logger(LOG_DEBUG, _("Error decrypting packet from %s (%s)"), n->name, n->hostname);
return; return;
} }
@ -233,22 +249,26 @@ static void receive_udppacket(node_t *n, vpn_packet_t *inpkt) {
/* Decompress the packet */ /* Decompress the packet */
if(myself->compression) { length_t origlen = inpkt->len;
if(n->incompression) {
outpkt = pkt[nextpkt++]; 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)"), ifdebug(TRAFFIC) logger(LOG_ERR, _("Error while uncompressing packet from %s (%s)"),
n->name, n->hostname); n->name, n->hostname);
return; return;
} }
inpkt = outpkt; inpkt = outpkt;
origlen -= MTU/64 + 20;
} }
inpkt->priority = 0; inpkt->priority = 0;
if(!inpkt->data[12] && !inpkt->data[13]) if(!inpkt->data[12] && !inpkt->data[13])
mtu_probe_h(n, inpkt); mtu_probe_h(n, inpkt, origlen);
else else
receive_packet(n, inpkt); receive_packet(n, inpkt);
} }
@ -290,7 +310,7 @@ static void send_udppacket(node_t *n, vpn_packet_t *origpkt) {
n->name, n->hostname); n->name, n->hostname);
if(!n->status.waitingforkey) if(!n->status.waitingforkey)
send_req_key(n->nexthop->connection, myself, n); send_req_key(n);
n->status.waitingforkey = true; n->status.waitingforkey = true;
@ -299,12 +319,14 @@ static void send_udppacket(node_t *n, vpn_packet_t *origpkt) {
return; 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, ifdebug(TRAFFIC) logger(LOG_INFO,
_("No minimum MTU established yet for %s (%s), forwarding via TCP"), _("No minimum MTU established yet for %s (%s), forwarding via TCP"),
n->name, n->hostname); n->name, n->hostname);
send_tcppacket(n->nexthop->connection, origpkt); send_tcppacket(n->nexthop->connection, origpkt);
return;
} }
origlen = inpkt->len; origlen = inpkt->len;
@ -312,10 +334,10 @@ static void send_udppacket(node_t *n, vpn_packet_t *origpkt) {
/* Compress the packet */ /* Compress the packet */
if(n->compression) { if(n->outcompression) {
outpkt = pkt[nextpkt++]; 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)"), ifdebug(TRAFFIC) logger(LOG_ERR, _("Error while compressing packet to %s (%s)"),
n->name, n->hostname); n->name, n->hostname);
return; return;
@ -331,11 +353,11 @@ static void send_udppacket(node_t *n, vpn_packet_t *origpkt) {
/* Encrypt the packet */ /* Encrypt the packet */
if(cipher_active(&n->cipher)) { if(cipher_active(&n->outcipher)) {
outpkt = pkt[nextpkt++]; outpkt = pkt[nextpkt++];
outlen = MAXSIZE; 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); ifdebug(TRAFFIC) logger(LOG_ERR, _("Error while encrypting packet to %s (%s)"), n->name, n->hostname);
goto end; goto end;
} }
@ -346,9 +368,9 @@ static void send_udppacket(node_t *n, vpn_packet_t *origpkt) {
/* Add the message authentication code */ /* Add the message authentication code */
if(digest_active(&n->digest)) { if(digest_active(&n->outdigest)) {
digest_create(&n->digest, &inpkt->seqno, inpkt->len, &inpkt->seqno + inpkt->len); digest_create(&n->outdigest, &inpkt->seqno, inpkt->len, &inpkt->seqno + inpkt->len);
inpkt->len += digest_length(&n->digest); inpkt->len += digest_length(&n->outdigest);
} }
/* Determine which socket we have to use */ /* 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)"), ifdebug(TRAFFIC) logger(LOG_INFO, _("Broadcasting packet of %d bytes from %s (%s)"),
packet->len, from->name, from->hostname); packet->len, from->name, from->hostname);
if(from != myself) if(from != myself) {
send_packet(myself, packet); 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) { for(node = connection_tree->head; node; node = node->next) {
c = node->data; 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) void handle_incoming_vpn_data(int sock, short events, void *data)
{ {
vpn_packet_t pkt; vpn_packet_t pkt;
@ -467,11 +519,17 @@ void handle_incoming_vpn_data(int sock, short events, void *data)
n = lookup_node_udp(&from); n = lookup_node_udp(&from);
if(!n) { if(!n) {
hostname = sockaddr2hostname(&from); n = try_harder(&from, &pkt);
logger(LOG_WARNING, _("Received UDP packet from unknown source %s"), if(n)
hostname); update_node_udp(n, &from);
free(hostname); else ifdebug(PROTOCOL) {
return; hostname = sockaddr2hostname(&from);
logger(LOG_WARNING, _("Received UDP packet from unknown source %s"), hostname);
free(hostname);
return;
}
else
return;
} }
receive_udppacket(n, &pkt); receive_udppacket(n, &pkt);

View file

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

View file

@ -33,6 +33,8 @@
#include "utils.h" #include "utils.h"
#include "xalloc.h" #include "xalloc.h"
#include <assert.h>
#ifdef WSAEINPROGRESS #ifdef WSAEINPROGRESS
#define EINPROGRESS WSAEINPROGRESS #define EINPROGRESS WSAEINPROGRESS
#endif #endif
@ -80,7 +82,95 @@ static void configure_tcp(connection_t *c) {
#endif #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; int nfd;
char *addrstr; char *addrstr;
int option; int option;
@ -120,7 +210,7 @@ int setup_listen_socket(const sockaddr_t *sa) {
return -1; return -1;
} }
#else #else
logger(LOG_WARNING, _("BindToInterface not supported on this platform")); logger(LOG_WARNING, _("%s not supported on this platform"), "BindToInterface");
#endif #endif
} }
@ -202,24 +292,10 @@ int setup_vpn_in_socket(const sockaddr_t *sa) {
} }
#endif #endif
#if defined(SOL_SOCKET) && defined(SO_BINDTODEVICE) if (!bind_to_interface(nfd)) {
{ closesocket(nfd);
char *iface; return -1;
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;
}
}
} }
#endif
if(bind(nfd, &sa->sa, SALEN(sa->sa))) { if(bind(nfd, &sa->sa, SALEN(sa->sa))) {
closesocket(nfd); closesocket(nfd);
@ -231,7 +307,7 @@ int setup_vpn_in_socket(const sockaddr_t *sa) {
} }
return nfd; return nfd;
} } /* int setup_vpn_in_socket */
static void retry_outgoing_handler(int fd, short events, void *data) { static void retry_outgoing_handler(int fd, short events, void *data) {
setup_outgoing_connection(data); setup_outgoing_connection(data);
@ -329,6 +405,9 @@ begin:
setsockopt(c->socket, SOL_IPV6, IPV6_V6ONLY, &option, sizeof option); setsockopt(c->socket, SOL_IPV6, IPV6_V6ONLY, &option, sizeof option);
#endif #endif
bind_to_interface(c->socket);
bind_to_address(c);
/* Optimize TCP settings */ /* Optimize TCP settings */
configure_tcp(c); configure_tcp(c);

View file

@ -140,7 +140,41 @@ char *sockaddr2hostname(const sockaddr_t *sa) {
return str; 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; int result;
cp(); cp();

View file

@ -32,6 +32,7 @@ extern sockaddr_t str2sockaddr(const char *, const char *);
extern void sockaddr2str(const sockaddr_t *, char **, char **); extern void sockaddr2str(const sockaddr_t *, char **, char **);
extern char *sockaddr2hostname(const sockaddr_t *); extern char *sockaddr2hostname(const sockaddr_t *);
extern int sockaddrcmp(const sockaddr_t *, 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 sockaddrunmap(sockaddr_t *);
extern void sockaddrfree(sockaddr_t *); extern void sockaddrfree(sockaddr_t *);
extern void sockaddrcpy(sockaddr_t *, const 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); sockaddrfree(&n->address);
cipher_close(&n->cipher); cipher_close(&n->incipher);
digest_close(&n->digest); digest_close(&n->indigest);
cipher_close(&n->outcipher);
digest_close(&n->outdigest);
event_del(&n->mtuevent); event_del(&n->mtuevent);
@ -130,6 +132,7 @@ void node_del(node_t *n) {
} }
splay_delete(node_tree, n); splay_delete(node_tree, n);
splay_delete(node_udp_tree, n);
} }
node_t *lookup_node(char *name) { 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); 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) { int dump_nodes(struct evbuffer *out) {
splay_node_t *node; splay_node_t *node;
node_t *n; node_t *n;
@ -162,8 +184,8 @@ int dump_nodes(struct evbuffer *out) {
for(node = node_tree->head; node; node = node->next) { for(node = node_tree->head; node; node = node->next) {
n = node->data; 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"), 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), n->name, n->hostname, cipher_get_nid(&n->outcipher),
digest_get_nid(&n->digest), n->maclength, n->compression, digest_get_nid(&n->outdigest), n->outmaclength, n->outcompression,
n->options, *(uint32_t *)&n->status, n->nexthop ? n->nexthop->name : "-", 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) n->via ? n->via->name : "-", n->distance, n->mtu, n->minmtu, n->maxmtu) == -1)
return errno; return errno;

View file

@ -54,11 +54,16 @@ typedef struct node_t {
node_status_t status; node_status_t status;
cipher_t cipher; /* Cipher for UDP packets */ cipher_t incipher; /* Cipher for UDP packets */
digest_t digest; /* Digest for UDP packets */ digest_t indigest; /* Digest for UDP packets */
int maclength; /* Portion of digest to use */ 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; int distance;
struct node_t *nexthop; /* nearest node from us to him */ 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(char *);
extern node_t *lookup_node_udp(const sockaddr_t *); extern node_t *lookup_node_udp(const sockaddr_t *);
extern int dump_nodes(struct evbuffer *); extern int dump_nodes(struct evbuffer *);
extern void update_node_udp(node_t *, const sockaddr_t *);
#endif /* __TINC_NODE_H__ */ #endif /* __TINC_NODE_H__ */

View file

@ -52,7 +52,7 @@ bool digest_open_sha1(digest_t *digest) {
void digest_close(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; EVP_MD_CTX ctx;
if(EVP_DigestInit(&ctx, digest->digest) 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; 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); size_t len = EVP_MD_size(digest->digest);
char outdata[len]; 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_by_nid(struct digest *, int);
extern bool digest_open_sha1(struct digest *); extern bool digest_open_sha1(struct digest *);
extern void digest_close(struct digest *); extern void digest_close(struct digest *);
extern bool digest_create(struct digest *, void *indata, size_t inlen, void *outdata); extern bool digest_create(struct digest *, const void *indata, size_t inlen, void *outdata);
extern bool digest_verify(struct digest *, void *indata, size_t inlen, void *digestdata); extern bool digest_verify(struct digest *, const void *indata, size_t inlen, const void *digestdata);
extern int digest_get_nid(const struct digest *); extern int digest_get_nid(const struct digest *);
extern size_t digest_length(const struct digest *); extern size_t digest_length(const struct digest *);
extern bool digest_active(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_del_subnet(struct connection_t *, const struct subnet_t *);
extern bool send_add_edge(struct connection_t *, const struct edge_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_del_edge(struct connection_t *, const struct edge_t *);
extern bool send_key_changed(struct connection_t *, const struct node_t *); extern bool send_key_changed();
extern bool send_req_key(struct connection_t *, const struct node_t *, const struct node_t *); extern bool send_req_key(struct node_t *);
extern bool send_ans_key(struct connection_t *, const struct node_t *, const struct node_t *); extern bool send_ans_key(struct node_t *);
extern bool send_tcppacket(struct connection_t *, struct vpn_packet_t *); extern bool send_tcppacket(struct connection_t *, struct vpn_packet_t *);
/* Request handlers */ /* Request handlers */

View file

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

View file

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

View file

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

View file

@ -60,7 +60,7 @@ bool add_subnet_h(connection_t *c, char *request)
return false; return false;
} }
/* Check if owner name is a valid */ /* Check if owner name is valid */
if(!check_id(name)) { if(!check_id(name)) {
logger(LOG_ERR, _("Got bad %s from %s (%s): %s"), "ADD_SUBNET", c->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); 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; return false;
}
free_subnet(allowed); free_subnet(allowed);
} }
@ -176,7 +179,7 @@ bool del_subnet_h(connection_t *c, char *request)
return false; return false;
} }
/* Check if owner name is a valid */ /* Check if owner name is valid */
if(!check_id(name)) { if(!check_id(name)) {
logger(LOG_ERR, _("Got bad %s from %s (%s): %s"), "DEL_SUBNET", c->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; 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 */ /* Check if subnet string is valid */
if(!str2net(&s, subnetstr)) { if(!str2net(&s, subnetstr)) {
@ -208,6 +198,23 @@ bool del_subnet_h(connection_t *c, char *request)
if(seen_request(request)) if(seen_request(request))
return true; 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 */ /* If everything is correct, delete the subnet from the list of the owner */
s.owner = owner; s.owner = owner;

View file

@ -33,6 +33,12 @@
#include LZO1X_H #include LZO1X_H
#ifndef HAVE_MINGW
#include <pwd.h>
#include <grp.h>
#include <time.h>
#endif
#include <getopt.h> #include <getopt.h>
#include "conf.h" #include "conf.h"
@ -62,6 +68,12 @@ bool bypass_security = false;
/* If nonzero, disable swapping for this process. */ /* If nonzero, disable swapping for this process. */
bool do_mlock = false; 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. */ /* If nonzero, write log entries to a separate file. */
bool use_logfile = false; bool use_logfile = false;
@ -81,6 +93,8 @@ static struct option const long_options[] = {
{"debug", optional_argument, NULL, 'd'}, {"debug", optional_argument, NULL, 'd'},
{"bypass-security", no_argument, NULL, 3}, {"bypass-security", no_argument, NULL, 3},
{"mlock", no_argument, NULL, 'L'}, {"mlock", no_argument, NULL, 'L'},
{"chroot", no_argument, NULL, 'R'},
{"user", required_argument, NULL, 'U'},
{"logfile", optional_argument, NULL, 4}, {"logfile", optional_argument, NULL, 4},
{"controlsocket", required_argument, NULL, 5}, {"controlsocket", required_argument, NULL, 5},
{NULL, 0, NULL, 0} {NULL, 0, NULL, 0}
@ -105,7 +119,8 @@ static void usage(bool status)
" --logfile[=FILENAME] Write log entries to a logfile.\n" " --logfile[=FILENAME] Write log entries to a logfile.\n"
" --controlsocket=FILENAME Open control socket at FILENAME.\n" " --controlsocket=FILENAME Open control socket at FILENAME.\n"
" --bypass-security Disables meta protocol security, for debugging.\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")); " --version Output version information and exit.\n\n"));
printf(_("Report bugs to tinc@tinc-vpn.org.\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 r;
int option_index = 0; 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) { switch (r) {
case 0: /* long option */ case 0: /* long option */
break; break;
@ -130,8 +145,13 @@ static bool parse_options(int argc, char **argv)
break; break;
case 'L': /* no detach */ case 'L': /* no detach */
#ifndef HAVE_MLOCKALL
logger(LOG_ERR, _("%s not supported on this platform"), "mlockall()");
return false;
#else
do_mlock = true; do_mlock = true;
break; break;
#endif
case 'd': /* inc debug level */ case 'd': /* inc debug level */
if(optarg) if(optarg)
@ -144,6 +164,14 @@ static bool parse_options(int argc, char **argv)
netname = xstrdup(optarg); netname = xstrdup(optarg);
break; break;
case 'R': /* chroot to NETNAME dir */
do_chroot = true;
break;
case 'U': /* setuid to USER */
switchuser = optarg;
break;
case 1: /* show help */ case 1: /* show help */
show_help = true; show_help = true;
break; break;
@ -237,6 +265,63 @@ static void free_names() {
if (confbase) free(confbase); 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) int main(int argc, char **argv)
{ {
program_name = argv[0]; program_name = argv[0];
@ -277,20 +362,6 @@ int main(int argc, char **argv)
if(!init_control()) if(!init_control())
return 1; 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; g_argv = argv;
init_configuration(&config_tree); init_configuration(&config_tree);
@ -326,11 +397,46 @@ int main2(int argc, char **argv)
if(!detach()) if(!detach())
return 1; 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. */ /* 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; goto end;
/* Start main loop. It only exits when tinc is killed. */ /* Start main loop. It only exits when tinc is killed. */
@ -353,5 +459,8 @@ end:
crypto_exit(); crypto_exit();
exit_configuration(&config_tree);
free_names();
return status; return status;
} }