bool init_redirector() { if ( !init_tun( TUN_INTERFACE ) ) { error( "Cannot create a tun interface." ); return false; } if ( host_db != NULL ) { error( "Host database is already created." ); return false; } host_db = create_hash( compare_ip_address, hash_ip_address ); set_fd_handler( fd, read_tun_fd, NULL, NULL, NULL ); set_readable( fd, true ); add_periodic_event_callback( HOST_DB_AGING_INTERVAL, age_host_db, NULL ); return true; }
/** * Open VPN tunnel interface. * * @param argc must be 6 * @param argv 0: binary name ("gnunet-helper-exit") * 1: tunnel interface name ("gnunet-exit") * 2: IPv4 "physical" interface name ("eth0"), or "%" to not do IPv4 NAT * 3: IPv6 address ("::1"), or "-" to skip IPv6 * 4: IPv6 netmask length in bits ("64") [ignored if #4 is "-"] * 5: IPv4 address ("1.2.3.4"), or "-" to skip IPv4 * 6: IPv4 netmask ("255.255.0.0") [ignored if #4 is "-"] */ int main (int argc, char **argv) { char dev[IFNAMSIZ]; int fd_tun; int global_ret; if (7 != argc) { fprintf (stderr, "Fatal: must supply 6 arguments!\n"); return 1; } if ( (0 == strcmp (argv[3], "-")) && (0 == strcmp (argv[5], "-")) ) { fprintf (stderr, "Fatal: disabling both IPv4 and IPv6 makes no sense.\n"); return 1; } if (0 == access ("/sbin/iptables", X_OK)) sbin_iptables = "/sbin/iptables"; else if (0 == access ("/usr/sbin/iptables", X_OK)) sbin_iptables = "/usr/sbin/iptables"; else { fprintf (stderr, "Fatal: executable iptables not found in approved directories: %s\n", strerror (errno)); return 1; } if (0 == access ("/sbin/sysctl", X_OK)) sbin_sysctl = "/sbin/sysctl"; else if (0 == access ("/usr/sbin/sysctl", X_OK)) sbin_sysctl = "/usr/sbin/sysctl"; else { fprintf (stderr, "Fatal: executable sysctl not found in approved directories: %s\n", strerror (errno)); return 1; } strncpy (dev, argv[1], IFNAMSIZ); dev[IFNAMSIZ - 1] = '\0'; if (-1 == (fd_tun = init_tun (dev))) { fprintf (stderr, "Fatal: could not initialize tun-interface `%s' with IPv6 %s/%s and IPv4 %s/%s\n", dev, argv[3], argv[4], argv[5], argv[6]); return 1; } if (0 != strcmp (argv[3], "-")) { { const char *address = argv[3]; long prefix_len = atol (argv[4]); if ((prefix_len < 1) || (prefix_len > 127)) { fprintf (stderr, "Fatal: prefix_len out of range\n"); return 1; } set_address6 (dev, address, prefix_len); } { char *const sysctl_args[] = { "sysctl", "-w", "net.ipv6.conf.all.forwarding=1", NULL }; if (0 != fork_and_exec (sbin_sysctl, sysctl_args)) { fprintf (stderr, "Failed to enable IPv6 forwarding. Will continue anyway.\n"); } } } if (0 != strcmp (argv[5], "-")) { { const char *address = argv[5]; const char *mask = argv[6]; set_address4 (dev, address, mask); } { char *const sysctl_args[] = { "sysctl", "-w", "net.ipv4.ip_forward=1", NULL }; if (0 != fork_and_exec (sbin_sysctl, sysctl_args)) { fprintf (stderr, "Failed to enable IPv4 forwarding. Will continue anyway.\n"); } } if (0 != strcmp (argv[2], "%")) { char *const iptables_args[] = { "iptables", "-t", "nat", "-A", "POSTROUTING", "-o", argv[2], "-j", "MASQUERADE", NULL }; if (0 != fork_and_exec (sbin_iptables, iptables_args)) { fprintf (stderr, "Failed to enable IPv4 masquerading (NAT). Will continue anyway.\n"); } } } uid_t uid = getuid (); #ifdef HAVE_SETRESUID if (0 != setresuid (uid, uid, uid)) { fprintf (stderr, "Failed to setresuid: %s\n", strerror (errno)); global_ret = 2; goto cleanup; } #else if (0 != (setuid (uid) | seteuid (uid))) { fprintf (stderr, "Failed to setuid: %s\n", strerror (errno)); global_ret = 2; goto cleanup; } #endif if (SIG_ERR == signal (SIGPIPE, SIG_IGN)) { fprintf (stderr, "Failed to protect against SIGPIPE: %s\n", strerror (errno)); /* no exit, we might as well die with SIGPIPE should it ever happen */ } run (fd_tun); global_ret = 0; cleanup: (void) close (fd_tun); return global_ret; }
/** * Main function of "gnunet-helper-dns", which opens a VPN tunnel interface, * redirects all outgoing DNS traffic (except from the specified port) to that * interface and then passes traffic from and to the interface via stdin/stdout. * * Once stdin/stdout close or have other errors, the tunnel is closed and the * DNS traffic redirection is stopped. * * @param argc number of arguments * @param argv 0: binary name (should be "gnunet-helper-vpn") * 1: tunnel interface name (typically "gnunet-dns") * 2: IPv6 address for the tunnel ("FE80::1") * 3: IPv6 netmask length in bits ("64") * 4: IPv4 address for the tunnel ("1.2.3.4") * 5: IPv4 netmask ("255.255.0.0") * 6: skip sysctl, routing and iptables setup ("0") * @return 0 on success, otherwise code indicating type of error: * 1 wrong number of arguments * 2 invalid arguments (i.e. port number / prefix length wrong) * 3 iptables not executable * 4 ip not executable * 5 failed to initialize tunnel interface * 6 failed to initialize control pipe * 8 failed to change routing table, cleanup successful * 9-23 failed to change routing table and failed to undo some changes to routing table * 24 failed to drop privs * 25-39 failed to drop privs and then failed to undo some changes to routing table * 40 failed to regain privs * 41-55 failed to regain prisv and then failed to undo some changes to routing table * 254 insufficient priviledges * 255 failed to handle kill signal properly */ int main (int argc, char *const*argv) { int r; char dev[IFNAMSIZ]; char mygid[32]; int fd_tun; uid_t uid; int nortsetup = 0; if (7 != argc) { fprintf (stderr, "Fatal: must supply 6 arguments!\n"); return 1; } /* assert privs so we can modify the firewall rules! */ uid = getuid (); #ifdef HAVE_SETRESUID if (0 != setresuid (uid, 0, 0)) { fprintf (stderr, "Failed to setresuid to root: %s\n", strerror (errno)); return 254; } #else if (0 != seteuid (0)) { fprintf (stderr, "Failed to seteuid back to root: %s\n", strerror (errno)); return 254; } #endif if (0 == strncmp (argv[6], "1", 2)) nortsetup = 1; if (0 == nortsetup) { /* verify that the binaries we care about are executable */ if (0 == access ("/sbin/iptables", X_OK)) sbin_iptables = "/sbin/iptables"; else if (0 == access ("/usr/sbin/iptables", X_OK)) sbin_iptables = "/usr/sbin/iptables"; else { fprintf (stderr, "Fatal: executable iptables not found in approved directories: %s\n", strerror (errno)); return 3; } if (0 == access ("/sbin/ip6tables", X_OK)) sbin_ip6tables = "/sbin/ip6tables"; else if (0 == access ("/usr/sbin/ip6tables", X_OK)) sbin_ip6tables = "/usr/sbin/ip6tables"; else { fprintf (stderr, "Fatal: executable ip6tables not found in approved directories: %s\n", strerror (errno)); return 3; } if (0 == access ("/sbin/ip", X_OK)) sbin_ip = "/sbin/ip"; else if (0 == access ("/usr/sbin/ip", X_OK)) sbin_ip = "/usr/sbin/ip"; else { fprintf (stderr, "Fatal: executable ip not found in approved directories: %s\n", strerror (errno)); return 4; } if (0 == access ("/sbin/sysctl", X_OK)) sbin_sysctl = "/sbin/sysctl"; else if (0 == access ("/usr/sbin/sysctl", X_OK)) sbin_sysctl = "/usr/sbin/sysctl"; else { fprintf (stderr, "Fatal: executable sysctl not found in approved directories: %s\n", strerror (errno)); return 5; } } /* setup 'mygid' string */ snprintf (mygid, sizeof (mygid), "%d", (int) getegid()); /* do not die on SIGPIPE */ if (SIG_ERR == signal (SIGPIPE, SIG_IGN)) { fprintf (stderr, "Failed to protect against SIGPIPE: %s\n", strerror (errno)); return 7; } /* setup pipe to shutdown nicely on SIGINT */ if (0 != pipe (cpipe)) { fprintf (stderr, "Fatal: could not setup control pipe: %s\n", strerror (errno)); return 6; } if (cpipe[0] >= FD_SETSIZE) { fprintf (stderr, "Pipe file descriptor to large: %d", cpipe[0]); (void) close (cpipe[0]); (void) close (cpipe[1]); return 6; } { /* make pipe non-blocking, as we theoretically could otherwise block in the signal handler */ int flags = fcntl (cpipe[1], F_GETFL); if (-1 == flags) { fprintf (stderr, "Failed to read flags for pipe: %s", strerror (errno)); (void) close (cpipe[0]); (void) close (cpipe[1]); return 6; } flags |= O_NONBLOCK; if (0 != fcntl (cpipe[1], F_SETFL, flags)) { fprintf (stderr, "Failed to make pipe non-blocking: %s", strerror (errno)); (void) close (cpipe[0]); (void) close (cpipe[1]); return 6; } } if ( (SIG_ERR == signal (SIGTERM, &signal_handler)) || #if (SIGTERM != GNUNET_TERM_SIG) (SIG_ERR == signal (GNUNET_TERM_SIG, &signal_handler)) || #endif (SIG_ERR == signal (SIGINT, &signal_handler)) || (SIG_ERR == signal (SIGHUP, &signal_handler)) ) { fprintf (stderr, "Fatal: could not initialize signal handler: %s\n", strerror (errno)); (void) close (cpipe[0]); (void) close (cpipe[1]); return 7; } /* get interface name */ strncpy (dev, argv[1], IFNAMSIZ); dev[IFNAMSIZ - 1] = '\0'; /* Disable rp filtering */ if (0 == nortsetup) { char *const sysctl_args[] = {"sysctl", "-w", "net.ipv4.conf.all.rp_filter=0", NULL}; char *const sysctl_args2[] = {"sysctl", "-w", "net.ipv4.conf.default.rp_filter=0", NULL}; if ((0 != fork_and_exec (sbin_sysctl, sysctl_args)) || (0 != fork_and_exec (sbin_sysctl, sysctl_args2))) { fprintf (stderr, "Failed to disable rp filtering.\n"); return 5; } } /* now open virtual interface (first part that requires root) */ if (-1 == (fd_tun = init_tun (dev))) { fprintf (stderr, "Fatal: could not initialize tun-interface\n"); (void) signal (SIGTERM, SIG_IGN); #if (SIGTERM != GNUNET_TERM_SIG) (void) signal (GNUNET_TERM_SIG, SIG_IGN); #endif (void) signal (SIGINT, SIG_IGN); (void) signal (SIGHUP, SIG_IGN); (void) close (cpipe[0]); (void) close (cpipe[1]); return 5; } /* now set interface addresses */ { const char *address = argv[2]; long prefix_len = atol (argv[3]); if ((prefix_len < 1) || (prefix_len > 127)) { fprintf (stderr, "Fatal: prefix_len out of range\n"); (void) signal (SIGTERM, SIG_IGN); #if (SIGTERM != GNUNET_TERM_SIG) (void) signal (GNUNET_TERM_SIG, SIG_IGN); #endif (void) signal (SIGINT, SIG_IGN); (void) signal (SIGHUP, SIG_IGN); (void) close (cpipe[0]); (void) close (cpipe[1]); return 2; } set_address6 (dev, address, prefix_len); } { const char *address = argv[4]; const char *mask = argv[5]; set_address4 (dev, address, mask); } /* update routing tables -- next part why we need SUID! */ /* Forward everything from our EGID (which should only be held by the 'gnunet-service-dns') and with destination to port 53 on UDP, without hijacking */ if (0 == nortsetup) { r = 8; /* failed to fully setup routing table */ { char *const mangle_args[] = { "iptables", "-m", "owner", "-t", "mangle", "-I", "OUTPUT", "1", "-p", "udp", "--gid-owner", mygid, "--dport", DNS_PORT, "-j", "ACCEPT", NULL }; if (0 != fork_and_exec (sbin_iptables, mangle_args)) goto cleanup_rest; } { char *const mangle_args[] = { "ip6tables", "-m", "owner", "-t", "mangle", "-I", "OUTPUT", "1", "-p", "udp", "--gid-owner", mygid, "--dport", DNS_PORT, "-j", "ACCEPT", NULL }; if (0 != fork_and_exec (sbin_ip6tables, mangle_args)) goto cleanup_mangle_1b; } /* Mark all of the other DNS traffic using our mark DNS_MARK, unless it is on a link-local IPv6 address, which we cannot support. */ { char *const mark_args[] = { "iptables", "-t", "mangle", "-I", "OUTPUT", "2", "-p", "udp", "--dport", DNS_PORT, "-j", "MARK", "--set-mark", DNS_MARK, NULL }; if (0 != fork_and_exec (sbin_iptables, mark_args)) goto cleanup_mangle_1; } { char *const mark_args[] = { "ip6tables", "-t", "mangle", "-I", "OUTPUT", "2", "-p", "udp", "--dport", DNS_PORT, "!", "-s", "fe80::/10", /* this line excludes link-local traffic */ "-j", "MARK", "--set-mark", DNS_MARK, NULL }; if (0 != fork_and_exec (sbin_ip6tables, mark_args)) goto cleanup_mark_2b; } /* Forward all marked DNS traffic to our DNS_TABLE */ { char *const forward_args[] = { "ip", "rule", "add", "fwmark", DNS_MARK, "table", DNS_TABLE, NULL }; if (0 != fork_and_exec (sbin_ip, forward_args)) goto cleanup_mark_2; } { char *const forward_args[] = { "ip", "-6", "rule", "add", "fwmark", DNS_MARK, "table", DNS_TABLE, NULL }; if (0 != fork_and_exec (sbin_ip, forward_args)) goto cleanup_forward_3b; } /* Finally, add rule in our forwarding table to pass to our virtual interface */ { char *const route_args[] = { "ip", "route", "add", "default", "dev", dev, "table", DNS_TABLE, NULL }; if (0 != fork_and_exec (sbin_ip, route_args)) goto cleanup_forward_3; } { char *const route_args[] = { "ip", "-6", "route", "add", "default", "dev", dev, "table", DNS_TABLE, NULL }; if (0 != fork_and_exec (sbin_ip, route_args)) goto cleanup_route_4b; } } /* drop privs *except* for the saved UID; this is not perfect, but better than doing nothing */ #ifdef HAVE_SETRESUID if (0 != setresuid (uid, uid, 0)) { fprintf (stderr, "Failed to setresuid: %s\n", strerror (errno)); r = 24; goto cleanup_route_4; } #else /* Note: no 'setuid' here as we must keep our saved UID as root */ if (0 != seteuid (uid)) { fprintf (stderr, "Failed to seteuid: %s\n", strerror (errno)); r = 24; goto cleanup_route_4; } #endif r = 0; /* did fully setup routing table (if nothing else happens, we were successful!) */ /* now forward until we hit a problem */ run (fd_tun); /* now need to regain privs so we can remove the firewall rules we added! */ #ifdef HAVE_SETRESUID if (0 != setresuid (uid, 0, 0)) { fprintf (stderr, "Failed to setresuid back to root: %s\n", strerror (errno)); r = 40; goto cleanup_route_4; } #else if (0 != seteuid (0)) { fprintf (stderr, "Failed to seteuid back to root: %s\n", strerror (errno)); r = 40; goto cleanup_route_4; } #endif /* update routing tables again -- this is why we could not fully drop privs */ /* now undo updating of routing tables; normal exit or clean-up-on-error case */ cleanup_route_4: if (0 == nortsetup) { char *const route_clean_args[] = { "ip", "-6", "route", "del", "default", "dev", dev, "table", DNS_TABLE, NULL }; if (0 != fork_and_exec (sbin_ip, route_clean_args)) r += 1; } cleanup_route_4b: if (0 == nortsetup) { char *const route_clean_args[] = { "ip", "route", "del", "default", "dev", dev, "table", DNS_TABLE, NULL }; if (0 != fork_and_exec (sbin_ip, route_clean_args)) r += 1; } cleanup_forward_3: if (0 == nortsetup) { char *const forward_clean_args[] = { "ip", "-6", "rule", "del", "fwmark", DNS_MARK, "table", DNS_TABLE, NULL }; if (0 != fork_and_exec (sbin_ip, forward_clean_args)) r += 2; } cleanup_forward_3b: if (0 == nortsetup) { char *const forward_clean_args[] = { "ip", "rule", "del", "fwmark", DNS_MARK, "table", DNS_TABLE, NULL }; if (0 != fork_and_exec (sbin_ip, forward_clean_args)) r += 2; } cleanup_mark_2: if (0 == nortsetup) { char *const mark_clean_args[] = { "ip6tables", "-t", "mangle", "-D", "OUTPUT", "-p", "udp", "--dport", DNS_PORT, "!", "-s", "fe80::/10", /* this line excludes link-local traffic */ "-j", "MARK", "--set-mark", DNS_MARK, NULL }; if (0 != fork_and_exec (sbin_ip6tables, mark_clean_args)) r += 4; } cleanup_mark_2b: if (0 == nortsetup) { char *const mark_clean_args[] = { "iptables", "-t", "mangle", "-D", "OUTPUT", "-p", "udp", "--dport", DNS_PORT, "-j", "MARK", "--set-mark", DNS_MARK, NULL }; if (0 != fork_and_exec (sbin_iptables, mark_clean_args)) r += 4; } cleanup_mangle_1: if (0 == nortsetup) { char *const mangle_clean_args[] = { "ip6tables", "-m", "owner", "-t", "mangle", "-D", "OUTPUT", "-p", "udp", "--gid-owner", mygid, "--dport", DNS_PORT, "-j", "ACCEPT", NULL }; if (0 != fork_and_exec (sbin_ip6tables, mangle_clean_args)) r += 8; } cleanup_mangle_1b: if (0 == nortsetup) { char *const mangle_clean_args[] = { "iptables", "-m", "owner", "-t", "mangle", "-D", "OUTPUT", "-p", "udp", "--gid-owner", mygid, "--dport", DNS_PORT, "-j", "ACCEPT", NULL }; if (0 != fork_and_exec (sbin_iptables, mangle_clean_args)) r += 8; } cleanup_rest: /* close virtual interface */ (void) close (fd_tun); /* remove signal handler so we can close the pipes */ (void) signal (SIGTERM, SIG_IGN); #if (SIGTERM != GNUNET_TERM_SIG) (void) signal (GNUNET_TERM_SIG, SIG_IGN); #endif (void) signal (SIGINT, SIG_IGN); (void) signal (SIGHUP, SIG_IGN); (void) close (cpipe[0]); (void) close (cpipe[1]); return r; }
int main(int argc, char **argv) { pingtun_t handle; handle.ret = -1; memset(&handle, 0, sizeof(handle)); DBG("parsing options"); parse_opts(&handle, argc, argv); if (handle.flags.is_server) { if (0 != set_ignore_echo(&handle)) { goto exit; } } DBG("initializing event base"); if (0 != init_base_ev(&handle)) { goto exit; } DBG("initializing ping socket"); if (handle.flags.is_server) { if (0 != init_sping(&handle)) { goto exit; } } if (handle.flags.is_client) { if (0 != init_cping(&handle)) { goto exit; } } if (handle.flags.is_server && !handle.flags.ignore_pings) { if (0 != init_reply(&handle)) { goto exit; } } DBG("initializing tun device"); if (0 != init_tun(&handle)) { goto exit; } if (0 != init_signals(&handle)) { goto exit; } switch (event_base_dispatch(handle.base_ev)) { case 0: break; case -1: ERR("dispatch event loop failed"); goto exit; case 1: ERR("no more active/pending events"); goto exit; } exit: if (handle.flags.is_server) { reset_ignore_echo(&handle); } //TODO: de-allocate all the shit. Do I really care? return handle.ret; }