]> git.meshlink.io Git - meshlink/commitdiff
Merge branch 'master' into 1.1
authorGuus Sliepen <guus@tinc-vpn.org>
Fri, 5 Jun 2009 21:03:28 +0000 (23:03 +0200)
committerGuus Sliepen <guus@tinc-vpn.org>
Fri, 5 Jun 2009 21:14:13 +0000 (23:14 +0200)
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

23 files changed:
doc/tinc.conf.5.in
doc/tinc.texi
doc/tincd.8.in
lib/utils.c
lib/utils.h
src/graph.c
src/linux/device.c
src/net.h
src/net_packet.c
src/net_setup.c
src/net_socket.c
src/netutl.c
src/netutl.h
src/node.c
src/node.h
src/openssl/digest.c
src/openssl/digest.h
src/protocol.h
src/protocol_auth.c
src/protocol_edge.c
src/protocol_key.c
src/protocol_subnet.c
src/tincd.c

index b3b94f8f668eea70bfbb94bebe4ab44483433410..223005fb612648578392987ffc60a2bb3a531a46 100644 (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
index f1d438fe456d82d0cbdc30e67c160fb296954273..13784335f37ea36d0efd6bde215c91c4fdbb5428 100644 (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.
 
index e4bbaeb7bfd04b32f94284a82e9fa03eb2d8d0f1..966cc77ee0981e85b9ea66dba63828385b3a273e 100644 (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
index b00a73bc7c5e7adc2d3f4305f77f1bb654844cb5..beabfeafadf08571b1cc6354c3a9dbaf0d57ee32 100644 (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,
index 6ff20829bb3aca7ee7e93aaa45d8c7cab4526d50..04c4ddbd3aeb6c8f94087594a99baf9ba7240fc1 100644 (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
 
index 8e801b3d55e567c36b3eb8fc80d79d951a8b8e62..4e060b7ffa9c647cd4cc3103085e3e542ddf3ef7 100644 (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;
 
index b02e88e721ff3d06bf51f2bec0d136d5243cdbfd..e692ca9ff8c5082b8195c24b7de2fe3b8bf7bf84 100644 (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
index d320c15af6e537644e130a04b9fe144cc0d69166..a28f47858cbb1327b06e64c0cf0602f1189ea77d 100644 (file)
--- a/src/net.h
+++ b/src/net.h
@@ -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);
index 7d640cb6c804f90a799dbd7e0e93d69e30581655..062e0be0a4302a5a68dd2ac91694d2c92e89f192 100644 (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);
index 43adbc8434fa59703bd3fab75958a6d9fdf00207..17eaec2b7475be8ec88b5ccae689cdecec68de6b 100644 (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;
 }
 
index 43b8ada2924bc4f3f18f41c1646d621608c7d3ff..2dfaa2f0b1831a2f14f137a297761c9a63be1c43 100644 (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);
index 116d858d41f194ac53599d9803fbbc26b6229d80..39cd79074e889af0eaff7c9951baf528e0ae0308 100644 (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();
index bd642b1b1905a96e48e883715fc4411c4657b41c..aa2a173b412f0653488f55abe6f200baee1754ea 100644 (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 *);
index 80d28d64112c8cc49e1ad3616b4fb06cb432c463..5df8b667467ff4aee7f43ca8815c10d940e8758b 100644 (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;
index f4fc88bbc5fc887a7f0740c37810ef12578226c1..9b0d136f2bc5cc5e34366c50107d1a38b3fd6da1 100644 (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__ */
index 7c4dfc238da8f9726408b95ea519af0b8876e57f..1e2557d9490fa84097d643af4df3f1c5f59c9251 100644 (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];
 
index f10e67fbc048c0d227d9c9d3d06ffc324663d0df..deba7d3a085db4bbda8de15decbabbeaa745d01d 100644 (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 *);
index cecc580b8365e7b1ef383a286b862421f685b221..8f53072105e80909cf8a2c70a372cd1fd9fb28b8 100644 (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  */
index 2109cd4d7328d8af9fe0ef638141c7a622c714ca..93fe23ea1692053309204c6f3dab93a31ae02166 100644 (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 */
 
index f515eeeddbb5307a9756986b976ea5c8313578f3..35776116ee929c0cf33fcffd8bd29910c9b28d2d 100644 (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);
index 3cf4ab11a0bd121fb976e2e25dfade630e966f40..06ce733df77c21129c473d100db354bf5a7c450a 100644 (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"
 
 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;
index 00cdde67a994014fa95fca8ce63ba26e705e384d..e5927213e379f04096fc26d1c4806c0e63d543d8 100644 (file)
@@ -60,7 +60,7 @@ bool add_subnet_h(connection_t *c, char *request)
                return false;
        }
 
-       /* Check if owner name is 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 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;
index ac0cbc6c39cbe2683249b7119158d7cd80e74766..76053a00825e27c348739775abd1e19ad849220e 100644 (file)
 
 #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;
 }