--- gvpe/src/vpn.C 2008/08/10 02:49:21 1.50 +++ gvpe/src/vpn.C 2015/07/15 23:04:06 1.68 @@ -1,6 +1,6 @@ /* vpn.C -- handle the protocol, encryption, handshaking etc. - Copyright (C) 2003-2008 Marc Lehmann + Copyright (C) 2003-2008,2010,2011,2013 Marc Lehmann This file is part of GVPE. @@ -40,6 +40,7 @@ #include #include #include +#include #include #include #include @@ -54,6 +55,8 @@ #include "util.h" #include "vpn.h" +using namespace std; + vpn network; // THE vpn (bad design...) ///////////////////////////////////////////////////////////////////////////// @@ -77,7 +80,7 @@ // the tricky part is rounding to the cipher key blocksize int mtu = conf.mtu - ETH_OVERHEAD - VPE_OVERHEAD - MAX_OVERHEAD; mtu += ETH_OVERHEAD - 6 - 6; // now we have the data portion - mtu -= mtu % EVP_CIPHER_block_size (CIPHER); // round + mtu -= mtu % BLOCK_SIZE (CIPHER); // round mtu -= ETH_OVERHEAD - 6 - 6; // and get interface mtu again char *env; @@ -112,31 +115,47 @@ { script_init_env (); - char *filename; - asprintf (&filename, - "%s/%s", - confbase, - ::conf.script_if_up ? ::conf.script_if_up : "if-up"); + return conf.config_filename (::conf.script_if_up, "if-up"); +} + +int +vpn::setup_socket (u8 prot, int family, int type, int proto) +{ + int fd = socket (family, type, proto); + + if (fd < 0) + { + slog (L_ERR, _("unable to create %s socket: %s."), strprotocol (prot), strerror (errno)); + return fd; + } + + fcntl (fd, F_SETFL, O_NONBLOCK); + fcntl (fd, F_SETFD, FD_CLOEXEC); - return filename; +#ifdef SO_MARK + if (::conf.nfmark) + if (setsockopt (fd, SOL_SOCKET, SO_MARK, &::conf.nfmark, sizeof ::conf.nfmark)) + slog (L_WARN, _("unable to set nfmark on %s socket: %s"), strprotocol (prot), strerror (errno)); +#endif + + return fd; } int vpn::setup () { + int success = 0; + ipv4_tos = -1; ipv4_fd = -1; if (THISNODE->protocols & PROT_IPv4 && ::conf.ip_proto) { - ipv4_fd = socket (PF_INET, SOCK_RAW, ::conf.ip_proto); + ipv4_fd = setup_socket (PROT_IPv4, PF_INET, SOCK_RAW, ::conf.ip_proto); if (ipv4_fd < 0) return -1; - fcntl (ipv4_fd, F_SETFL, O_NONBLOCK); - fcntl (ipv4_fd, F_SETFD, FD_CLOEXEC); - #if defined(SOL_IP) && defined(IP_MTU_DISCOVER) // this I really consider a linux bug. I am neither connected // nor do I fragment myself. Linux still sets DF and doesn't @@ -152,25 +171,25 @@ if (bind (ipv4_fd, si.sav4 (), si.salenv4 ())) { slog (L_ERR, _("can't bind ipv4 socket on %s: %s, exiting."), (const char *)si, strerror (errno)); - exit (EXIT_FAILURE); + return -1; } ipv4_ev_watcher.start (ipv4_fd, EV_READ); + ++success; } + else + THISNODE->protocols &= ~PROT_IPv4; udpv4_tos = -1; udpv4_fd = -1; if (THISNODE->protocols & PROT_UDPv4 && THISNODE->udp_port) { - udpv4_fd = socket (PF_INET, SOCK_DGRAM, IPPROTO_UDP); + udpv4_fd = setup_socket (PROT_UDPv4, PF_INET, SOCK_DGRAM, IPPROTO_UDP); if (udpv4_fd < 0) return -1; - fcntl (udpv4_fd, F_SETFL, O_NONBLOCK); - fcntl (udpv4_fd, F_SETFD, FD_CLOEXEC); - // standard daemon practise... { int oval = 1; @@ -192,11 +211,14 @@ if (bind (udpv4_fd, si.sav4 (), si.salenv4 ())) { slog (L_ERR, _("can't bind udpv4 on %s: %s, exiting."), (const char *)si, strerror (errno)); - exit (EXIT_FAILURE); + return -1; } udpv4_ev_watcher.start (udpv4_fd, EV_READ); + ++success; } + else + THISNODE->protocols &= ~PROT_UDPv4; icmpv4_tos = -1; icmpv4_fd = -1; @@ -204,14 +226,11 @@ #if ENABLE_ICMP if (THISNODE->protocols & PROT_ICMPv4) { - icmpv4_fd = socket (PF_INET, SOCK_RAW, IPPROTO_ICMP); + icmpv4_fd = setup_socket (PROT_ICMPv4, PF_INET, SOCK_RAW, IPPROTO_ICMP); if (icmpv4_fd < 0) return -1; - fcntl (icmpv4_fd, F_SETFL, O_NONBLOCK); - fcntl (icmpv4_fd, F_SETFD, FD_CLOEXEC); - #ifdef ICMP_FILTER { icmp_filter oval; @@ -238,10 +257,11 @@ if (bind (icmpv4_fd, si.sav4 (), si.salenv4 ())) { slog (L_ERR, _("can't bind icmpv4 on %s: %s, exiting."), (const char *)si, strerror (errno)); - exit (EXIT_FAILURE); + return -1; } icmpv4_ev_watcher.start (icmpv4_fd, EV_READ); + ++success; } #endif @@ -250,14 +270,11 @@ #if ENABLE_TCP if (THISNODE->protocols & PROT_TCPv4 && THISNODE->tcp_port) { - tcpv4_fd = socket (PF_INET, SOCK_STREAM, IPPROTO_TCP); + tcpv4_fd = setup_socket (PROT_TCPv4, PF_INET, SOCK_STREAM, IPPROTO_TCP); if (tcpv4_fd < 0) return -1; - fcntl (tcpv4_fd, F_SETFL, O_NONBLOCK); - fcntl (tcpv4_fd, F_SETFD, FD_CLOEXEC); - // standard daemon practise... { int oval = 1; @@ -269,17 +286,20 @@ if (bind (tcpv4_fd, si.sav4 (), si.salenv4 ())) { slog (L_ERR, _("can't bind tcpv4 on %s: %s, exiting."), (const char *)si, strerror (errno)); - exit (EXIT_FAILURE); + return -1; } if (listen (tcpv4_fd, 5)) { slog (L_ERR, _("can't listen tcpv4 on %s: %s, exiting."), (const char *)si, strerror (errno)); - exit (EXIT_FAILURE); + return -1; } tcpv4_ev_watcher.start (tcpv4_fd, EV_READ); + ++success; } + else + THISNODE->protocols &= ~PROT_TCPv4; #endif dnsv4_tos = -1; @@ -290,14 +310,11 @@ { dns_forwarder.set (::conf.dns_forw_host, ::conf.dns_forw_port, PROT_DNSv4); - dnsv4_fd = socket (PF_INET, SOCK_DGRAM, IPPROTO_UDP); + dnsv4_fd = setup_socket (PROT_DNSv4, PF_INET, SOCK_DGRAM, IPPROTO_UDP); if (dnsv4_fd < 0) return -1; - fcntl (dnsv4_fd, F_SETFL, O_NONBLOCK); - fcntl (dnsv4_fd, F_SETFD, FD_CLOEXEC); - # if defined(SOL_IP) && defined(IP_MTU_DISCOVER) // this I really consider a linux bug. I am neither connected // nor do I fragment myself. Linux still sets DF and doesn't @@ -321,15 +338,22 @@ if (bind (dnsv4_fd, si.sav4 (), si.salenv4 ())) { slog (L_ERR, _("can't bind dnsv4 on %s: %s, exiting."), (const char *)si, strerror (errno)); - exit (EXIT_FAILURE); + return -1; } dnsv4_ev_watcher.start (dnsv4_fd, EV_READ); + ++success; } #endif ///////////////////////////////////////////////////////////////////////////// + if (!success) + { + slog (L_ERR, _("no protocols enabled.")); + return -1; + } + reconnect_all (); ///////////////////////////////////////////////////////////////////////////// @@ -337,8 +361,8 @@ tap = new tap_device (); if (!tap) //D this, of course, never catches { - slog (L_ERR, _("cannot create network interface '%s', exiting."), conf.ifname); - exit (EXIT_FAILURE); + slog (L_ERR, _("cannot create network interface '%s'."), conf.ifname); + return -1; } fcntl (tap->fd, F_SETFD, FD_CLOEXEC); @@ -349,16 +373,16 @@ if (tap->if_up () && !run_script (cb, true)) { - slog (L_ERR, _("interface initialization command '%s' failed, exiting."), + slog (L_ERR, _("interface initialization command '%s' failed."), tap->if_up ()); - exit (EXIT_FAILURE); + return -1; } cb.set (this); if (!run_script (cb, true)) { - slog (L_ERR, _("if-up command execution failed, exiting.")); - exit (EXIT_FAILURE); + slog (L_ERR, _("if-up command execution failed.")); + return -1; } tap_ev_watcher.start (tap->fd, EV_READ); @@ -367,6 +391,74 @@ } bool +vpn::drop_privileges () +{ + if (::conf.change_root) + { + if (!strcmp (::conf.change_root, "/")) + { + char dir [L_tmpnam]; + if (!tmpnam (dir)) + { + slog (L_CRIT, _("unable to create anonymous root path.")); + return false; + } + + if (mkdir (dir, 0700)) + { + slog (L_CRIT, _("unable to create anonymous root directory.")); + return false; + } + + if (chdir (dir)) + { + slog (L_CRIT, _("unable to change to anonymous root directory.")); + return false; + } + + if (rmdir (dir)) + slog (L_ERR, _("unable to remove anonymous root directory, continuing.")); + } + else + { + if (chdir (::conf.change_root)) + { + slog (L_CRIT, _("%s: unable to change to specified root directory."), ::conf.change_root); + return false; + } + } + + if (chroot (".")) + { + slog (L_CRIT, _("unable to set new root directory.")); + return false; + } + + if (chdir ("/")) + { + slog (L_CRIT, _("unable to set cwd to new root directory.")); + return false; + } + } + + if (::conf.change_gid) + if (setgid (::conf.change_gid)) + { + slog (L_CRIT, _("unable to change group id to %d."), ::conf.change_gid); + return false; + } + + if (::conf.change_uid) + if (setuid (::conf.change_uid)) + { + slog (L_CRIT, _("unable to change user id to %d."), ::conf.change_uid); + return false; + } + + return true; +} + +bool vpn::send_ipv4_packet (vpn_packet *pkt, const sockinfo &si, int tos) { set_tos (ipv4_fd, ipv4_tos, tos); @@ -459,9 +551,6 @@ || pkt->typ () >= vpn_packet::PT_MAX) slog (L_WARN, _("(%s): received corrupted packet type %d (src %d, dst %d)."), (const char *)rsi, pkt->typ (), pkt->src (), pkt->dst ()); - else if (dst > conns.size ()) - slog (L_WARN, _("(%s): received corrupted packet type %d (src %d, dst %d)."), - (const char *)rsi, pkt->typ (), pkt->src (), pkt->dst ()); else { connection *c = conns[src - 1]; @@ -491,14 +580,14 @@ switch (si.prot) { case PROT_IPv4: - return send_ipv4_packet (pkt, si, tos); + return send_ipv4_packet (pkt, si, tos); case PROT_UDPv4: - return send_udpv4_packet (pkt, si, tos); + return send_udpv4_packet (pkt, si, tos); -#if ENABLE_TCP +#if ENABLE_TCP case PROT_TCPv4: - return send_tcpv4_packet (pkt, si, tos); + return send_tcpv4_packet (pkt, si, tos); #endif #if ENABLE_ICMP case PROT_ICMPv4: @@ -506,7 +595,7 @@ #endif #if ENABLE_DNS case PROT_DNSv4: - return send_dnsv4_packet (pkt, si, tos); + return send_dnsv4_packet (pkt, si, tos); #endif default: slog (L_CRIT, _("%s: FATAL: trying to send packet with unsupported protocol."), (const char *)si); @@ -534,7 +623,7 @@ pkt->len = len; // raw sockets deliver the ipv4 header, but don't expect it on sends - pkt->skip_hdr (IP_OVERHEAD); + pkt->skip_hdr (pkt->ipv4_hdr_len ()); recv_vpn_packet (pkt, si); } @@ -581,7 +670,7 @@ { // raw sockets deliver the ipv4, but don't expect it on sends // this is slow, but... - pkt->skip_hdr (ICMP_OVERHEAD); + pkt->skip_hdr (pkt->ipv4_hdr_len () + (ICMP_OVERHEAD - IP_OVERHEAD)); recv_vpn_packet (pkt, si); } @@ -733,7 +822,8 @@ (*c)->establish_connection (); } -bool vpn::can_direct (conf_node *src, conf_node *dst) const +bool +vpn::can_direct (conf_node *src, conf_node *dst) const { return src != dst && src->may_direct (dst) @@ -744,11 +834,13 @@ // only works for indirect and routed connections: find a router // from THISNODE to dst -connection *vpn::find_router_for (const connection *dst) +connection * +vpn::find_router_for (const connection *dst) { connection *router = 0; - // first try to find a router with a direct connection + // first try to find a router with a direct connection, route there + // regardless of any other considerations. { u32 prio = 1; @@ -758,16 +850,11 @@ if (c->conf->routerprio > prio && c->conf != THISNODE - && c != dst - && can_direct (c->conf, dst->conf)) + && can_direct (c->conf, dst->conf) + && c->ictx && c->octx) { - if (c->ictx && c->octx) - { - prio = c->conf->routerprio; - router = c; - } - else - c->establish_connection (); + prio = c->conf->routerprio; + router = c; } } } @@ -775,42 +862,39 @@ if (router) return router; - // second try find the router with the highest priority higher than ours + // second try find the router with the highest priority, higher than ours { - u32 prio = 1; + u32 prio = THISNODE->routerprio ? THISNODE->routerprio : 1; for (conns_vector::iterator i = conns.begin (); i != conns.end (); ++i) { connection *c = *i; if (c->conf->routerprio > prio - && c->conf->routerprio > THISNODE->routerprio && c != dst - && c->conf != THISNODE) + && c->conf != THISNODE + && c->ictx && c->octx) { - if (c->ictx && c->octx) - { - prio = c->conf->routerprio; - router = c; - } - else - c->establish_connection (); + prio = c->conf->routerprio; + router = c; } } } + return router; } -void vpn::connection_established (connection *c) +void +vpn::connection_established (connection *c) { for (conns_vector::iterator i = conns.begin (); i != conns.end (); ++i) { connection *o = *i; - if (!o->is_direct - && o->si.valid () + if (o->si.valid () && c->si != o->si - && c == find_router_for (o)) + && c == find_router_for (o) + && !can_direct (THISNODE, o->conf)) { slog (L_DEBUG, _("%s: can now route packets via %s, re-keying connection."), o->conf->nodename, c->conf->nodename); @@ -819,18 +903,19 @@ } } -void vpn::send_connect_request (connection *c) +void +vpn::send_connect_request (connection *c) { connection *r = find_router_for (c); if (r) { - slog (L_TRACE, _("%s: no way to connect, sending mediated connection request via %s."), + slog (L_TRACE, _("%s: no address known, sending mediated connection request via %s."), c->conf->nodename, r->conf->nodename); r->send_connect_request (c->conf->id); } else - slog (L_DEBUG, _("%s: no way to connect and no router found: unable to connect."), + slog (L_DEBUG, _("%s: no way to connect and no router found: unable to connect at this time."), c->conf->nodename); }