Пример #1
0
 def send(self, dstip, data):
     debug2('UDP: sending to %r port %d\n' % dstip)
     try:
         self.sock.sendto(data, dstip)
     except socket.error as e:
         log('UDP send to %r port %d: %s\n' % (dstip[0], dstip[1], e))
         return
Пример #2
0
def _check_nmb(hostname, is_workgroup, is_master):
    return
    global _nmb_ok
    if not _nmb_ok:
        return
    argv = ['nmblookup'] + ['-M'] * is_master + ['--', hostname]
    debug2(' > n%d%d: %s\n' % (is_workgroup, is_master, hostname))
    try:
        p = ssubprocess.Popen(argv, stdout=ssubprocess.PIPE, stderr=null)
        lines = p.stdout.readlines()
        rv = p.wait()
    except OSError:
        _, e = sys.exc_info()[:2]
        log('%r failed: %r\n' % (argv, e))
        _nmb_ok = False
        return
    if rv:
        log('%r returned %d\n' % (argv, rv))
        return
    for line in lines:
        m = re.match(r'(\d+\.\d+\.\d+\.\d+) (\w+)<\w\w>\n', line)
        if m:
            g = m.groups()
            (ip, name) = (g[0], g[1].lower())
            debug3('<    %s -> %s\n' % (name, ip))
            if is_workgroup:
                _enqueue(_check_smb, ip)
            else:
                found_host(name, ip)
                check_host(name)
Пример #3
0
def connect_dst(family, ip, port):
    debug2('Connecting to %s:%d\n' % (ip, port))
    outsock = socket.socket(family)
    outsock.setsockopt(socket.SOL_IP, socket.IP_TTL, 42)
    return SockWrapper(outsock, outsock,
                       connect_to=(ip, port),
                       peername='%s:%d' % (ip, port))
Пример #4
0
    def try_send(self):
        if self.tries >= 3:
            return
        self.tries += 1

        family, peer = resolvconf_random_nameserver()

        sock = socket.socket(family, socket.SOCK_DGRAM)
        sock.setsockopt(socket.SOL_IP, socket.IP_TTL, 42)
        sock.connect((peer, 53))

        self.peers[sock] = peer

        debug2('DNS: sending to %r (try %d)\n' % (peer, self.tries))
        try:
            sock.send(self.request)
            self.socks.append(sock)
        except socket.error:
            _, e = sys.exc_info()[:2]
            if e.args[0] in ssnet.NET_ERRS:
                # might have been spurious; try again.
                # Note: these errors sometimes are reported by recv(),
                # and sometimes by send().  We have to catch both.
                debug2('DNS send to %r: %s\n' % (peer, e))
                self.try_send()
                return
            else:
                log('DNS send to %r: %s\n' % (peer, e))
                return
Пример #5
0
def runonce(handlers, mux):
    r = []
    w = []
    x = []
    to_remove = [s for s in handlers if not s.ok]
    for h in to_remove:
        handlers.remove(h)

    for s in handlers:
        s.pre_select(r, w, x)
    debug2('Waiting: %d r=%r w=%r x=%r (fullness=%d/%d)\n'
           % (len(handlers), _fds(r), _fds(w), _fds(x),
               mux.fullness, mux.too_full))
    (r, w, x) = select.select(r, w, x)
    debug2('  Ready: %d r=%r w=%r x=%r\n'
           % (len(handlers), _fds(r), _fds(w), _fds(x)))
    ready = r + w + x
    did = {}
    for h in handlers:
        for s in h.socks:
            if s in ready:
                h.callback(s)
                did[s] = 1
    for s in ready:
        if s not in did:
            raise Fatal('socket %r was not used by any handler' % s)
Пример #6
0
 def __init__(self, mux, channel):
     SockWrapper.__init__(self, mux.rsock, mux.wsock)
     self.mux = mux
     self.channel = channel
     self.mux.channels[channel] = self.got_packet
     self.socks = []
     debug2('new channel: %d\n' % channel)
Пример #7
0
 def nowrite(self):
     if not self.shut_write:
         debug2('%r: done writing\n' % self)
         self.shut_write = True
         try:
             self.wsock.shutdown(SHUT_WR)
         except socket.error as e:
             self.seterr('nowrite: %s' % e)
Пример #8
0
def _check_revdns(ip):
    debug2(' > rev: %s\n' % ip)
    try:
        r = socket.gethostbyaddr(ip)
        debug3('<    %s\n' % r[0])
        check_host(r[0])
        found_host(r[0], ip)
    except socket.herror:
        pass
Пример #9
0
 def send(self, channel, cmd, data):
     assert isinstance(data, bytes)
     assert len(data) <= 65535
     p = struct.pack('!ccHHH', b'S', b'S', channel, cmd, len(data)) + data
     self.outbuf.append(p)
     debug2(' > channel=%d cmd=%s len=%d (fullness=%d)\n'
            % (channel, cmd_to_name.get(cmd, hex(cmd)),
               len(data), self.fullness))
     self.fullness += len(data)
Пример #10
0
 def flush(self):
     self.wsock.setblocking(False)
     if self.outbuf and self.outbuf[0]:
         wrote = _nb_clean(os.write, self.wsock.fileno(), self.outbuf[0])
         debug2('mux wrote: %r/%d\n' % (wrote, len(self.outbuf[0])))
         if wrote:
             self.outbuf[0] = self.outbuf[0][wrote:]
     while self.outbuf and not self.outbuf[0]:
         self.outbuf[0:1] = []
Пример #11
0
 def print_listening(self, what):
     if self.v6:
         listenip = self.v6.getsockname()
         debug1('%s listening on %r.\n' % (what, listenip))
         debug2('%s listening with %r.\n' % (what, self.v6))
     if self.v4:
         listenip = self.v4.getsockname()
         debug1('%s listening on %r.\n' % (what, listenip))
         debug2('%s listening with %r.\n' % (what, self.v4))
Пример #12
0
 def callback(self, sock):
     try:
         data, peer = sock.recvfrom(4096)
     except socket.error as e:
         log('UDP recv from %r port %d: %s\n' % (peer[0], peer[1], e))
         return
     debug2('UDP response: %d bytes\n' % len(data))
     hdr = "%s,%r," % (peer[0], peer[1])
     self.mux.send(self.chan, ssnet.CMD_UDP_DATA, hdr + data)
Пример #13
0
def _check_dns(hostname):
    debug2(' > dns: %s\n' % hostname)
    try:
        ip = socket.gethostbyname(hostname)
        debug3('<    %s\n' % ip)
        check_host(ip)
        found_host(hostname, ip)
    except socket.gaierror:
        pass
Пример #14
0
def _check_smb(hostname):
    return
    global _smb_ok
    if not _smb_ok:
        return
    argv = ['smbclient', '-U', '%', '-L', hostname]
    debug2(' > smb: %s\n' % hostname)
    try:
        p = ssubprocess.Popen(argv, stdout=ssubprocess.PIPE, stderr=null)
        lines = p.stdout.readlines()
        p.wait()
    except OSError:
        _, e = sys.exc_info()[:2]
        log('%r failed: %r\n' % (argv, e))
        _smb_ok = False
        return

    lines.reverse()

    # junk at top
    while lines:
        line = lines.pop().strip()
        if re.match(r'Server\s+', line):
            break

    # server list section:
    #    Server   Comment
    #    ------   -------
    while lines:
        line = lines.pop().strip()
        if not line or re.match(r'-+\s+-+', line):
            continue
        if re.match(r'Workgroup\s+Master', line):
            break
        words = line.split()
        hostname = words[0].lower()
        debug3('<    %s\n' % hostname)
        check_host(hostname)

    # workgroup list section:
    #   Workgroup  Master
    #   ---------  ------
    while lines:
        line = lines.pop().strip()
        if re.match(r'-+\s+', line):
            continue
        if not line:
            break
        words = line.split()
        (workgroup, hostname) = (words[0].lower(), words[1].lower())
        debug3('<    group(%s) -> %s\n' % (workgroup, hostname))
        check_host(hostname)
        check_workgroup(workgroup)

    if lines:
        assert(0)
Пример #15
0
 def udp_open(channel, data):
     debug2('Incoming UDP open.\n')
     family = int(data)
     mux.channels[channel] = lambda cmd, data: udp_req(channel, cmd, data)
     if channel in udphandlers:
         raise Fatal('UDP connection channel %d already open' % channel)
     else:
         h = UdpProxy(mux, channel, family)
         handlers.append(h)
         udphandlers[channel] = h
Пример #16
0
def _check_smb(hostname):
    return
    global _smb_ok
    if not _smb_ok:
        return
    argv = ["smbclient", "-U", "%", "-L", hostname]
    debug2(" > smb: %s\n" % hostname)
    try:
        p = ssubprocess.Popen(argv, stdout=ssubprocess.PIPE, stderr=null)
        lines = p.stdout.readlines()
        p.wait()
    except OSError as e:
        log("%r failed: %r\n" % (argv, e))
        _smb_ok = False
        return

    lines.reverse()

    # junk at top
    while lines:
        line = lines.pop().strip()
        if re.match(r"Server\s+", line):
            break

    # server list section:
    #    Server   Comment
    #    ------   -------
    while lines:
        line = lines.pop().strip()
        if not line or re.match(r"-+\s+-+", line):
            continue
        if re.match(r"Workgroup\s+Master", line):
            break
        words = line.split()
        hostname = words[0].lower()
        debug3("<    %s\n" % hostname)
        check_host(hostname)

    # workgroup list section:
    #   Workgroup  Master
    #   ---------  ------
    while lines:
        line = lines.pop().strip()
        if re.match(r"-+\s+", line):
            continue
        if not line:
            break
        words = line.split()
        (workgroup, hostname) = (words[0].lower(), words[1].lower())
        debug3("<    group(%s) -> %s\n" % (workgroup, hostname))
        check_host(hostname)
        check_workgroup(workgroup)

    if lines:
        assert 0
Пример #17
0
def _check_etc_hosts():
    debug2(" > hosts\n")
    for line in open("/etc/hosts"):
        line = re.sub(r"#.*", "", line)
        words = line.strip().split()
        if not words:
            continue
        ip = words[0]
        names = words[1:]
        if _is_ip(ip):
            debug3("<    %s %r\n" % (ip, names))
            for n in names:
                check_host(n)
                found_host(n, ip)
Пример #18
0
def _check_netstat():
    debug2(" > netstat\n")
    argv = ["netstat", "-n"]
    try:
        p = ssubprocess.Popen(argv, stdout=ssubprocess.PIPE, stderr=null)
        content = p.stdout.read()
        p.wait()
    except OSError as e:
        log("%r failed: %r\n" % (argv, e))
        return

    for ip in re.findall(r"\d+\.\d+\.\d+\.\d+", content):
        debug3("<    %s\n" % ip)
        check_host(ip)
Пример #19
0
def _check_netstat():
    debug2(' > netstat\n')
    argv = ['netstat', '-n']
    try:
        p = ssubprocess.Popen(argv, stdout=ssubprocess.PIPE, stderr=null)
        content = p.stdout.read().decode("ASCII")
        p.wait()
    except OSError as e:
        log('%r failed: %r\n' % (argv, e))
        return

    for ip in re.findall(r'\d+\.\d+\.\d+\.\d+', content):
        debug3('<    %s\n' % ip)
        check_host(ip)
Пример #20
0
def _check_etc_hosts():
    debug2(' > hosts\n')
    for line in open('/etc/hosts'):
        line = re.sub(r'#.*', '', line)
        words = line.strip().split()
        if not words:
            continue
        ip = words[0]
        names = words[1:]
        if _is_ip(ip):
            debug3('<    %s %r\n' % (ip, names))
            for n in names:
                check_host(n)
                found_host(n, ip)
Пример #21
0
    def get_tcp_dstip(self, sock):
        pfile = self.firewall.pfile

        peer = sock.getpeername()
        proxy = sock.getsockname()

        argv = (sock.family, socket.IPPROTO_TCP, peer[0], peer[1], proxy[0], proxy[1])
        pfile.write("QUERY_PF_NAT %d,%d,%s,%d,%s,%d\n" % argv)
        pfile.flush()
        line = pfile.readline()
        debug2("QUERY_PF_NAT %d,%d,%s,%d,%s,%d" % argv + " > " + line)
        if line.startswith("QUERY_PF_NAT_SUCCESS "):
            (ip, port) = line[21:].split(",")
            return (ip, int(port))

        return sock.getsockname()
Пример #22
0
    def get_tcp_dstip(self, sock):
        pfile = self.firewall.pfile

        peer = sock.getpeername()
        proxy = sock.getsockname()

        argv = (sock.family, socket.IPPROTO_TCP, peer[0].encode("ASCII"), peer[1], proxy[0].encode("ASCII"), proxy[1])
        out_line = b"QUERY_PF_NAT %d,%d,%s,%d,%s,%d\n" % argv
        pfile.write(out_line)
        pfile.flush()
        in_line = pfile.readline()
        debug2(out_line.decode("ASCII") + " > " + in_line.decode("ASCII"))
        if in_line.startswith(b"QUERY_PF_NAT_SUCCESS "):
            (ip, port) = in_line[21:].split(b",")
            return (ip.decode("ASCII"), int(port))

        return sock.getsockname()
Пример #23
0
 def got_packet(self, channel, cmd, data):
     debug2('<  channel=%d cmd=%s len=%d\n'
            % (channel, cmd_to_name.get(cmd, hex(cmd)), len(data)))
     if cmd == CMD_PING:
         self.send(0, CMD_PONG, data)
     elif cmd == CMD_PONG:
         debug2('received PING response\n')
         self.too_full = False
         self.fullness = 0
     elif cmd == CMD_EXIT:
         self.ok = False
     elif cmd == CMD_TCP_CONNECT:
         assert(not self.channels.get(channel))
         if self.new_channel:
             self.new_channel(channel, data)
     elif cmd == CMD_DNS_REQ:
         assert(not self.channels.get(channel))
         if self.got_dns_req:
             self.got_dns_req(channel, data)
     elif cmd == CMD_UDP_OPEN:
         assert(not self.channels.get(channel))
         if self.got_udp_open:
             self.got_udp_open(channel, data)
     elif cmd == CMD_ROUTES:
         if self.got_routes:
             self.got_routes(data)
         else:
             raise Exception('got CMD_ROUTES without got_routes?')
     elif cmd == CMD_HOST_REQ:
         if self.got_host_req:
             self.got_host_req(data)
         else:
             raise Exception('got CMD_HOST_REQ without got_host_req?')
     elif cmd == CMD_HOST_LIST:
         if self.got_host_list:
             self.got_host_list(data)
         else:
             raise Exception('got CMD_HOST_LIST without got_host_list?')
     else:
         callback = self.channels.get(channel)
         if not callback:
             log('warning: closed channel %d got cmd=%s len=%d\n'
                 % (channel, cmd_to_name.get(cmd, hex(cmd)), len(data)))
         else:
             callback(cmd, data)
Пример #24
0
    def onroutes(routestr):
        if auto_nets:
            for line in routestr.strip().split(b'\n'):
                if not line:
                    continue
                (family, ip, width) = line.split(b',', 2)
                family = int(family)
                width = int(width)
                ip = ip.decode("ASCII")
                if family == socket.AF_INET6 and tcp_listener.v6 is None:
                    debug2("Ignored auto net %d/%s/%d\n" % (family, ip, width))
                if family == socket.AF_INET and tcp_listener.v4 is None:
                    debug2("Ignored auto net %d/%s/%d\n" % (family, ip, width))
                else:
                    debug2("Adding auto net %d/%s/%d\n" % (family, ip, width))
                    fw.auto_nets.append((family, ip, width, 0, 0))

        # we definitely want to do this *after* starting ssh, or we might end
        # up intercepting the ssh connection!
        #
        # Moreover, now that we have the --auto-nets option, we have to wait
        # for the server to send us that message anyway.  Even if we haven't
        # set --auto-nets, we might as well wait for the message first, then
        # ignore its contents.
        mux.got_routes = None
        fw.start()
Пример #25
0
    def callback(self, sock):
        peer = self.peers[sock]

        try:
            data = sock.recv(4096)
        except socket.error as e:
            self.socks.remove(sock)
            del self.peers[sock]

            if e.args[0] in ssnet.NET_ERRS:
                # might have been spurious; try again.
                # Note: these errors sometimes are reported by recv(),
                # and sometimes by send().  We have to catch both.
                debug2('DNS recv from %r: %s\n' % (peer, e))
                self.try_send()
                return
            else:
                log('DNS recv from %r: %s\n' % (peer, e))
                return
        debug2('DNS response: %d bytes\n' % len(data))
        self.mux.send(self.chan, ssnet.CMD_DNS_RESPONSE, data)
        self.ok = False
Пример #26
0
 def udp_req(channel, cmd, data):
     debug2('Incoming UDP request channel=%d, cmd=%d\n' % (channel, cmd))
     if cmd == ssnet.CMD_UDP_DATA:
         (dstip, dstport, data) = data.split(",", 2)
         dstport = int(dstport)
         debug2('is incoming UDP data. %r %d.\n' % (dstip, dstport))
         h = udphandlers[channel]
         h.send((dstip, dstport), data)
     elif cmd == ssnet.CMD_UDP_CLOSE:
         debug2('is incoming UDP close\n')
         h = udphandlers[channel]
         h.ok = False
         del mux.channels[channel]
Пример #27
0
    def onroutes(routestr):
        if auto_nets:
            for line in routestr.strip().split(b'\n'):
                (family, ip, width) = line.split(b',', 2)
                family = int(family)
                width = int(width)
                ip = ip.decode("ASCII")
                if family == socket.AF_INET6 and tcp_listener.v6 is None:
                    debug2("Ignored auto net %d/%s/%d\n" % (family, ip, width))
                if family == socket.AF_INET and tcp_listener.v4 is None:
                    debug2("Ignored auto net %d/%s/%d\n" % (family, ip, width))
                else:
                    debug2("Adding auto net %d/%s/%d\n" % (family, ip, width))
                    fw.auto_nets.append((family, ip, width))

        # we definitely want to do this *after* starting ssh, or we might end
        # up intercepting the ssh connection!
        #
        # Moreover, now that we have the --auto-nets option, we have to wait
        # for the server to send us that message anyway.  Even if we haven't
        # set --auto-nets, we might as well wait for the message first, then
        # ignore its contents.
        mux.got_routes = None
        fw.start()
Пример #28
0
 def is_supported(self):
     if which("iptables") and which("ip6tables"):
         return True
     debug2("tproxy method not supported because 'iptables' "
            "or 'ip6tables' commands are missing.\n")
     return False
Пример #29
0
def connect(ssh_cmd, rhostport, python, stderr, options):
    username, password, port, host = parse_hostport(rhostport)
    if username:
        rhost = "{}@{}".format(username, host)
    else:
        rhost = host

    z = zlib.compressobj(1)
    content = get_module_source('sshuttle.assembler')
    optdata = ''.join("%s=%r\n" % (k, v) for (k, v) in list(options.items()))
    optdata = optdata.encode("UTF8")
    content2 = (empackage(z, 'sshuttle') +
                empackage(z, 'sshuttle.cmdline_options', optdata) +
                empackage(z, 'sshuttle.helpers') +
                empackage(z, 'sshuttle.ssnet') +
                empackage(z, 'sshuttle.hostwatch') +
                empackage(z, 'sshuttle.server') + b"\n")

    # If the exec() program calls sys.exit(), it should exit python
    # and the sys.exit(98) call won't be reached (so we try to only
    # exit that way in the server). However, if the code that we
    # exec() simply returns from main, then we will return from
    # exec(). If the server's python process dies, it should stop
    # executing and also won't reach sys.exit(98).
    #
    # So, we shouldn't reach sys.exit(98) and we certainly shouldn't
    # reach it immediately after trying to start the server.
    pyscript = r"""
                import sys, os;
                verbosity=%d;
                sys.stdin = os.fdopen(0, "rb");
                exec(compile(sys.stdin.read(%d), "assembler.py", "exec"));
                sys.exit(98);
                """ % (helpers.verbose or 0, len(content))
    pyscript = re.sub(r'\s+', ' ', pyscript.strip())

    if not rhost:
        # ignore the --python argument when running locally; we already know
        # which python version works.
        argv = [sys.executable, '-c', pyscript]
    else:
        if ssh_cmd:
            sshl = shlex.split(ssh_cmd)
        else:
            sshl = ['ssh']
        if port is not None:
            portl = ["-p", str(port)]
        else:
            portl = []
        if python:
            pycmd = "'%s' -c '%s'" % (python, pyscript)
        else:
            # By default, we run the following code in a shell.
            # However, with restricted shells and other unusual
            # situations, there can be trouble. See the RESTRICTED
            # SHELL section in "man bash" for more information. The
            # code makes many assumptions:
            #
            # (1) That /bin/sh exists and that we can call it.
            # Restricted shells often do *not* allow you to run
            # programs specified with an absolute path like /bin/sh.
            # Either way, if there is trouble with this, it should
            # return error code 127.
            #
            # (2) python3 or python exists in the PATH and is
            # executable. If they aren't, then exec won't work (see (4)
            # below).
            #
            # (3) In /bin/sh, that we can redirect stderr in order to
            # hide the version that "python3 -V" might print (some
            # restricted shells don't allow redirection, see
            # RESTRICTED SHELL section in 'man bash'). However, if we
            # are in a restricted shell, we'd likely have trouble with
            # assumption (1) above.
            #
            # (4) The 'exec' command should work except if we failed
            # to exec python because it doesn't exist or isn't
            # executable OR if exec isn't allowed (some restricted
            # shells don't allow exec). If the exec succeeded, it will
            # not return and not get to the "exit 97" command. If exec
            # does return, we exit with code 97.
            #
            # Specifying the exact python program to run with --python
            # avoids many of the issues above. However, if
            # you have a restricted shell on remote, you may only be
            # able to run python if it is in your PATH (and you can't
            # run programs specified with an absolute path). In that
            # case, sshuttle might not work at all since it is not
            # possible to run python on the remote machine---even if
            # it is present.
            pycmd = ("P=python3; $P -V 2>%s || P=python; "
                     "exec \"$P\" -c %s; exit 97") % \
                     (os.devnull, quote(pyscript))
            pycmd = ("/bin/sh -c {}".format(quote(pycmd)))

        if password is not None:
            os.environ['SSHPASS'] = str(password)
            argv = (["sshpass", "-e"] + sshl + portl + [rhost, '--', pycmd])

        else:
            argv = (sshl + portl + [rhost, '--', pycmd])

    # Our which() function searches for programs in get_path()
    # directories (which include PATH). This step isn't strictly
    # necessary if ssh is already in the user's PATH, but it makes the
    # error message friendlier if the user incorrectly passes in a
    # custom ssh command that we cannot find.
    abs_path = which(argv[0])
    if abs_path is None:
        raise Fatal("Failed to find '%s' in path %s" % (argv[0], get_path()))
    argv[0] = abs_path

    (s1, s2) = socket.socketpair()

    def setup():
        # runs in the child process
        s2.close()

    s1a, s1b = os.dup(s1.fileno()), os.dup(s1.fileno())
    s1.close()

    debug2('executing: %r' % argv)
    p = ssubprocess.Popen(argv,
                          stdin=s1a,
                          stdout=s1b,
                          preexec_fn=setup,
                          close_fds=True,
                          stderr=stderr)
    os.close(s1a)
    os.close(s1b)
    s2.sendall(content)
    s2.sendall(content2)
    return p, s2
Пример #30
0
 def dns_req(channel, data):
     debug2('Incoming DNS request channel=%d.\n' % channel)
     h = DnsProxy(mux, channel, data)
     handlers.append(h)
     dnshandlers[channel] = h
Пример #31
0
 def is_supported(self):
     if which("iptables"):
         return True
     debug2("nat method not supported because 'iptables' command "
            "is missing.")
     return False
Пример #32
0
def main(listenip_v6, listenip_v4, ssh_cmd, remotename, python,
         latency_control, dns, nslist, method_name, seed_hosts, auto_hosts,
         auto_nets, subnets_include, subnets_exclude, daemon, to_nameserver,
         pidfile, user, sudo_pythonpath):

    if not remotename:
        print("WARNING: You must specify -r/--remote to securely route "
              "traffic to a remote machine. Running without -r/--remote "
              "is only recommended for testing.")

    if daemon:
        try:
            check_daemon(pidfile)
        except Fatal as e:
            log("%s\n" % e)
            return 5
    debug1('Starting sshuttle proxy (version %s).\n' % __version__)
    helpers.logprefix = 'c : '

    fw = FirewallClient(method_name, sudo_pythonpath)

    # If --dns is used, store the IP addresses that the client
    # normally uses for DNS lookups in nslist. The firewall needs to
    # redirect packets outgoing to this server to the remote host
    # instead.
    if dns:
        nslist += resolvconf_nameservers(True)
        if to_nameserver is not None:
            to_nameserver = "%s@%s" % tuple(to_nameserver[1:])
    else:
        # option doesn't make sense if we aren't proxying dns
        if to_nameserver and len(to_nameserver) > 0:
            print("WARNING: --to-ns option is ignored because --dns was not "
                  "used.")
        to_nameserver = None

    # Get family specific subnet lists. Also, the user may not specify
    # any subnets if they use --auto-nets. In this case, our subnets
    # list will be empty and the forwarded subnets will be determined
    # later by the server.
    subnets_v4 = [i for i in subnets_include if i[0] == socket.AF_INET]
    subnets_v6 = [i for i in subnets_include if i[0] == socket.AF_INET6]
    nslist_v4 = [i for i in nslist if i[0] == socket.AF_INET]
    nslist_v6 = [i for i in nslist if i[0] == socket.AF_INET6]

    # Get available features from the firewall method
    avail = fw.method.get_supported_features()

    # A feature is "required" if the user supplies us parameters which
    # implies that the feature is needed.
    required = Features()

    # Select the default addresses to bind to / listen to.

    # Assume IPv4 is always available and should always be enabled. If
    # a method doesn't provide IPv4 support or if we wish to run
    # ipv6-only, changes to this code are required.
    assert avail.ipv4
    required.ipv4 = True

    # listenip_v4 contains user specified value or it is set to "auto".
    if listenip_v4 == "auto":
        listenip_v4 = ('127.0.0.1', 0)

    # listenip_v6 is...
    #    None when IPv6 is disabled.
    #    "auto" when listen address is unspecified.
    #    The user specified address if provided by user
    if listenip_v6 is None:
        debug1("IPv6 disabled by --disable-ipv6\n")
    if listenip_v6 == "auto":
        if avail.ipv6:
            debug1("IPv6 enabled: Using default IPv6 listen address ::1\n")
            listenip_v6 = ('::1', 0)
        else:
            debug1("IPv6 disabled since it isn't supported by method "
                   "%s.\n" % fw.method.name)
            listenip_v6 = None

    # Make final decision about enabling IPv6:
    required.ipv6 = False
    if listenip_v6:
        required.ipv6 = True

    # If we get here, it is possible that listenip_v6 was user
    # specified but not supported by the current method.
    if required.ipv6 and not avail.ipv6:
        raise Fatal("An IPv6 listen address was supplied, but IPv6 is "
                    "disabled at your request or is unsupported by the %s "
                    "method." % fw.method.name)

    if user is not None:
        if getpwnam is None:
            raise Fatal("Routing by user not available on this system.")
        try:
            user = getpwnam(user).pw_uid
        except KeyError:
            raise Fatal("User %s does not exist." % user)
    required.user = False if user is None else True

    if not required.ipv6 and len(subnets_v6) > 0:
        print("WARNING: IPv6 subnets were ignored because IPv6 is disabled "
              "in sshuttle.")
        subnets_v6 = []
        subnets_include = subnets_v4

    required.udp = avail.udp  # automatically enable UDP if it is available
    required.dns = len(nslist) > 0

    # Remove DNS servers using IPv6.
    if required.dns:
        if not required.ipv6 and len(nslist_v6) > 0:
            print("WARNING: Your system is configured to use an IPv6 DNS "
                  "server but sshuttle is not using IPv6. Therefore DNS "
                  "traffic your system sends to the IPv6 DNS server won't "
                  "be redirected via sshuttle to the remote machine.")
            nslist_v6 = []
            nslist = nslist_v4

        if len(nslist) == 0:
            raise Fatal("Can't redirect DNS traffic since IPv6 is not "
                        "enabled in sshuttle and all of the system DNS "
                        "servers are IPv6.")

    # If we aren't using IPv6, we can safely ignore excluded IPv6 subnets.
    if not required.ipv6:
        orig_len = len(subnets_exclude)
        subnets_exclude = [
            i for i in subnets_exclude if i[0] == socket.AF_INET
        ]
        if len(subnets_exclude) < orig_len:
            print("WARNING: Ignoring one or more excluded IPv6 subnets "
                  "because IPv6 is not enabled.")

    # This will print error messages if we required a feature that
    # isn't available by the current method.
    fw.method.assert_features(required)

    # display features enabled
    def feature_status(label, enabled, available):
        msg = label + ": "
        if enabled:
            msg += "on"
        else:
            msg += "off "
            if available:
                msg += "(available)"
            else:
                msg += "(not available with %s method)" % fw.method.name
        debug1(msg + "\n")

    debug1("Method: %s\n" % fw.method.name)
    feature_status("IPv4", required.ipv4, avail.ipv4)
    feature_status("IPv6", required.ipv6, avail.ipv6)
    feature_status("UDP ", required.udp, avail.udp)
    feature_status("DNS ", required.dns, avail.dns)
    feature_status("User", required.user, avail.user)

    # Exclude traffic destined to our listen addresses.
    if required.ipv4 and \
            not any(listenip_v4[0] == sex[1] for sex in subnets_v4):
        subnets_exclude.append((socket.AF_INET, listenip_v4[0], 32, 0, 0))

    if required.ipv6 and \
            not any(listenip_v6[0] == sex[1] for sex in subnets_v6):
        subnets_exclude.append((socket.AF_INET6, listenip_v6[0], 128, 0, 0))

    # We don't print the IP+port of where we are listening here
    # because we do that below when we have identified the ports to
    # listen on.
    debug1("Subnets to forward through remote host (type, IP, cidr mask "
           "width, startPort, endPort):\n")
    for i in subnets_include:
        debug1("  " + str(i) + "\n")
    if auto_nets:
        debug1("NOTE: Additional subnets to forward may be added below by "
               "--auto-nets.\n")
    debug1("Subnets to exclude from forwarding:\n")
    for i in subnets_exclude:
        debug1("  " + str(i) + "\n")
    if required.dns:
        debug1("DNS requests normally directed at these servers will be "
               "redirected to remote:\n")
        for i in nslist:
            debug1("  " + str(i) + "\n")

    if listenip_v6 and listenip_v6[1] and listenip_v4 and listenip_v4[1]:
        # if both ports given, no need to search for a spare port
        ports = [
            0,
        ]
    else:
        # if at least one port missing, we have to search
        ports = range(12300, 9000, -1)
        # keep track of failed bindings and used ports to avoid trying to
        # bind to the same socket address twice in different listeners
        used_ports = []

    # search for free ports and try to bind
    last_e = None
    redirectport_v6 = 0
    redirectport_v4 = 0
    bound = False
    for port in ports:
        debug2('Trying to bind redirector on port %d\n' % port)
        tcp_listener = MultiListener()

        if required.udp:
            udp_listener = MultiListener(socket.SOCK_DGRAM)
        else:
            udp_listener = None

        if listenip_v6 and listenip_v6[1]:
            lv6 = listenip_v6
            redirectport_v6 = lv6[1]
        elif listenip_v6:
            lv6 = (listenip_v6[0], port)
            redirectport_v6 = port
        else:
            lv6 = None
            redirectport_v6 = 0

        if listenip_v4 and listenip_v4[1]:
            lv4 = listenip_v4
            redirectport_v4 = lv4[1]
        elif listenip_v4:
            lv4 = (listenip_v4[0], port)
            redirectport_v4 = port
        else:
            lv4 = None
            redirectport_v4 = 0

        try:
            tcp_listener.bind(lv6, lv4)
            if udp_listener:
                udp_listener.bind(lv6, lv4)
            bound = True
            used_ports.append(port)
            break
        except socket.error as e:
            if e.errno == errno.EADDRINUSE:
                last_e = e
                used_ports.append(port)
            else:
                raise e

    if not bound:
        assert (last_e)
        raise last_e
    tcp_listener.listen(10)
    tcp_listener.print_listening("TCP redirector")
    if udp_listener:
        udp_listener.print_listening("UDP redirector")

    bound = False
    if required.dns:
        # search for spare port for DNS
        ports = range(12300, 9000, -1)
        for port in ports:
            debug2('Trying to bind DNS redirector on port %d\n' % port)
            if port in used_ports:
                continue

            dns_listener = MultiListener(socket.SOCK_DGRAM)

            if listenip_v6:
                lv6 = (listenip_v6[0], port)
                dnsport_v6 = port
            else:
                lv6 = None
                dnsport_v6 = 0

            if listenip_v4:
                lv4 = (listenip_v4[0], port)
                dnsport_v4 = port
            else:
                lv4 = None
                dnsport_v4 = 0

            try:
                dns_listener.bind(lv6, lv4)
                bound = True
                used_ports.append(port)
                break
            except socket.error as e:
                if e.errno == errno.EADDRINUSE:
                    last_e = e
                    used_ports.append(port)
                else:
                    raise e

        dns_listener.print_listening("DNS")
        if not bound:
            assert (last_e)
            raise last_e
    else:
        dnsport_v6 = 0
        dnsport_v4 = 0
        dns_listener = None

    # Last minute sanity checks.
    # These should never fail.
    # If these do fail, something is broken above.
    if subnets_v6:
        assert required.ipv6
        if redirectport_v6 == 0:
            raise Fatal("IPv6 subnets defined but not listening")

    if nslist_v6:
        assert required.dns
        assert required.ipv6
        if dnsport_v6 == 0:
            raise Fatal("IPv6 ns servers defined but not listening")

    if subnets_v4:
        if redirectport_v4 == 0:
            raise Fatal("IPv4 subnets defined but not listening")

    if nslist_v4:
        if dnsport_v4 == 0:
            raise Fatal("IPv4 ns servers defined but not listening")

    # setup method specific stuff on listeners
    fw.method.setup_tcp_listener(tcp_listener)
    if udp_listener:
        fw.method.setup_udp_listener(udp_listener)
    if dns_listener:
        fw.method.setup_udp_listener(dns_listener)

    # start the firewall
    fw.setup(subnets_include, subnets_exclude, nslist, redirectport_v6,
             redirectport_v4, dnsport_v6, dnsport_v4, required.udp, user)

    # start the client process
    try:
        return _main(tcp_listener, udp_listener, fw, ssh_cmd, remotename,
                     python, latency_control, dns_listener, seed_hosts,
                     auto_hosts, auto_nets, daemon, to_nameserver)
    finally:
        try:
            if daemon:
                # it's not our child anymore; can't waitpid
                fw.p.returncode = 0
            fw.done()
            sdnotify.send(sdnotify.stop())

        finally:
            if daemon:
                daemon_cleanup()
Пример #33
0
def connect(ssh_cmd, rhostport, python, stderr, options):
    username, password, port, host = parse_hostport(rhostport)
    if username:
        rhost = "{}@{}".format(username, host)
    else:
        rhost = host

    z = zlib.compressobj(1)
    content = get_module_source('sshuttle.assembler')
    optdata = ''.join("%s=%r\n" % (k, v) for (k, v) in list(options.items()))
    optdata = optdata.encode("UTF8")
    content2 = (empackage(z, 'sshuttle') +
                empackage(z, 'sshuttle.cmdline_options', optdata) +
                empackage(z, 'sshuttle.helpers') +
                empackage(z, 'sshuttle.ssnet') +
                empackage(z, 'sshuttle.hostwatch') +
                empackage(z, 'sshuttle.server') + b"\n")

    pyscript = r"""
                import sys, os;
                verbosity=%d;
                sys.stdin = os.fdopen(0, "rb");
                exec(compile(sys.stdin.read(%d), "assembler.py", "exec"))
                """ % (helpers.verbose or 0, len(content))
    pyscript = re.sub(r'\s+', ' ', pyscript.strip())

    if not rhost:
        # ignore the --python argument when running locally; we already know
        # which python version works.
        argv = [sys.executable, '-c', pyscript]
    else:
        if ssh_cmd:
            sshl = shlex.split(ssh_cmd)
        else:
            sshl = ['ssh']
        if port is not None:
            portl = ["-p", str(port)]
        else:
            portl = []
        if python:
            pycmd = "'%s' -c '%s'" % (python, pyscript)
        else:
            pycmd = ("P=python3; $P -V 2>%s || P=python; "
                     "exec \"$P\" -c %s") % (os.devnull, quote(pyscript))
            pycmd = ("/bin/sh -c {}".format(quote(pycmd)))

        if password is not None:
            os.environ['SSHPASS'] = str(password)
            argv = (["sshpass", "-e"] + sshl + portl + [rhost, '--', pycmd])

        else:
            argv = (sshl + portl + [rhost, '--', pycmd])

    # Our which() function searches for programs in get_path()
    # directories (which include PATH). This step isn't strictly
    # necessary if ssh is already in the user's PATH, but it makes the
    # error message friendlier if the user incorrectly passes in a
    # custom ssh command that we cannot find.
    abs_path = which(argv[0])
    if abs_path is None:
        raise Fatal("Failed to find '%s' in path %s" % (argv[0], get_path()))
    argv[0] = abs_path

    (s1, s2) = socket.socketpair()

    def setup():
        # runs in the child process
        s2.close()

    s1a, s1b = os.dup(s1.fileno()), os.dup(s1.fileno())
    s1.close()

    debug2('executing: %r\n' % argv)
    p = ssubprocess.Popen(argv,
                          stdin=s1a,
                          stdout=s1b,
                          preexec_fn=setup,
                          close_fds=True,
                          stderr=stderr)
    os.close(s1a)
    os.close(s1b)
    s2.sendall(content)
    s2.sendall(content2)
    return p, s2
Пример #34
0
def restore_etc_hosts(hostmap, port):
    # Only restore if we added hosts to /etc/hosts previously.
    if len(hostmap) > 0:
        debug2('undoing /etc/hosts changes.')
        rewrite_etc_hosts({}, port)
Пример #35
0
def main(listenip_v6, listenip_v4,
         ssh_cmd, remotename, python, latency_control, dns, nslist,
         method_name, seed_hosts, auto_nets,
         subnets_include, subnets_exclude, daemon, pidfile):

    if daemon:
        try:
            check_daemon(pidfile)
        except Fatal as e:
            log("%s\n" % e)
            return 5
    debug1('Starting sshuttle proxy.\n')

    fw = FirewallClient(method_name)

    # Get family specific subnet lists
    if dns:
        nslist += resolvconf_nameservers()

    subnets = subnets_include + subnets_exclude  # we don't care here
    subnets_v6 = [i for i in subnets if i[0] == socket.AF_INET6]
    nslist_v6 = [i for i in nslist if i[0] == socket.AF_INET6]
    subnets_v4 = [i for i in subnets if i[0] == socket.AF_INET]
    nslist_v4 = [i for i in nslist if i[0] == socket.AF_INET]

    # Check features available
    avail = fw.method.get_supported_features()
    required = Features()

    if listenip_v6 == "auto":
        if avail.ipv6:
            listenip_v6 = ('::1', 0)
        else:
            listenip_v6 = None

    required.ipv6 = len(subnets_v6) > 0 or len(nslist_v6) > 0 \
        or listenip_v6 is not None
    required.udp = avail.udp
    required.dns = len(nslist) > 0

    fw.method.assert_features(required)

    if required.ipv6 and listenip_v6 is None:
        raise Fatal("IPv6 required but not listening.")

    # display features enabled
    debug1("IPv6 enabled: %r\n" % required.ipv6)
    debug1("UDP enabled: %r\n" % required.udp)
    debug1("DNS enabled: %r\n" % required.dns)

    # bind to required ports
    if listenip_v4 == "auto":
        listenip_v4 = ('127.0.0.1', 0)

    if listenip_v6 and listenip_v6[1] and listenip_v4 and listenip_v4[1]:
        # if both ports given, no need to search for a spare port
        ports = [0, ]
    else:
        # if at least one port missing, we have to search
        ports = range(12300, 9000, -1)

    # search for free ports and try to bind
    last_e = None
    redirectport_v6 = 0
    redirectport_v4 = 0
    bound = False
    debug2('Binding redirector:')
    for port in ports:
        debug2(' %d' % port)
        tcp_listener = MultiListener()

        if required.udp:
            udp_listener = MultiListener(socket.SOCK_DGRAM)
        else:
            udp_listener = None

        if listenip_v6 and listenip_v6[1]:
            lv6 = listenip_v6
            redirectport_v6 = lv6[1]
        elif listenip_v6:
            lv6 = (listenip_v6[0], port)
            redirectport_v6 = port
        else:
            lv6 = None
            redirectport_v6 = 0

        if listenip_v4 and listenip_v4[1]:
            lv4 = listenip_v4
            redirectport_v4 = lv4[1]
        elif listenip_v4:
            lv4 = (listenip_v4[0], port)
            redirectport_v4 = port
        else:
            lv4 = None
            redirectport_v4 = 0

        try:
            tcp_listener.bind(lv6, lv4)
            if udp_listener:
                udp_listener.bind(lv6, lv4)
            bound = True
            break
        except socket.error as e:
            if e.errno == errno.EADDRINUSE:
                last_e = e
            else:
                raise e

    debug2('\n')
    if not bound:
        assert(last_e)
        raise last_e
    tcp_listener.listen(10)
    tcp_listener.print_listening("TCP redirector")
    if udp_listener:
        udp_listener.print_listening("UDP redirector")

    bound = False
    if required.dns:
        # search for spare port for DNS
        debug2('Binding DNS:')
        ports = range(12300, 9000, -1)
        for port in ports:
            debug2(' %d' % port)
            dns_listener = MultiListener(socket.SOCK_DGRAM)

            if listenip_v6:
                lv6 = (listenip_v6[0], port)
                dnsport_v6 = port
            else:
                lv6 = None
                dnsport_v6 = 0

            if listenip_v4:
                lv4 = (listenip_v4[0], port)
                dnsport_v4 = port
            else:
                lv4 = None
                dnsport_v4 = 0

            try:
                dns_listener.bind(lv6, lv4)
                bound = True
                break
            except socket.error as e:
                if e.errno == errno.EADDRINUSE:
                    last_e = e
                else:
                    raise e
        debug2('\n')
        dns_listener.print_listening("DNS")
        if not bound:
            assert(last_e)
            raise last_e
    else:
        dnsport_v6 = 0
        dnsport_v4 = 0
        dns_listener = None

    # Last minute sanity checks.
    # These should never fail.
    # If these do fail, something is broken above.
    if len(subnets_v6) > 0:
        assert required.ipv6
        if redirectport_v6 == 0:
            raise Fatal("IPv6 subnets defined but not listening")

    if len(nslist_v6) > 0:
        assert required.dns
        assert required.ipv6
        if dnsport_v6 == 0:
            raise Fatal("IPv6 ns servers defined but not listening")

    if len(subnets_v4) > 0:
        if redirectport_v4 == 0:
            raise Fatal("IPv4 subnets defined but not listening")

    if len(nslist_v4) > 0:
        if dnsport_v4 == 0:
            raise Fatal("IPv4 ns servers defined but not listening")

    # setup method specific stuff on listeners
    fw.method.setup_tcp_listener(tcp_listener)
    if udp_listener:
        fw.method.setup_udp_listener(udp_listener)
    if dns_listener:
        fw.method.setup_udp_listener(dns_listener)

    # start the firewall
    fw.setup(subnets_include, subnets_exclude, nslist,
             redirectport_v6, redirectport_v4, dnsport_v6, dnsport_v4,
             required.udp)

    # start the client process
    try:
        return _main(tcp_listener, udp_listener, fw, ssh_cmd, remotename,
                     python, latency_control, dns_listener,
                     seed_hosts, auto_nets, daemon)
    finally:
        try:
            if daemon:
                # it's not our child anymore; can't waitpid
                fw.p.returncode = 0
            fw.done()
        finally:
            if daemon:
                daemon_cleanup()
Пример #36
0
def _check_smb(hostname):
    return
    global _smb_ok
    if not _smb_ok:
        return
    debug2(' > smb: %s\n' % hostname)
    env = {
        'PATH': os.environ['PATH'],
        'LC_ALL': "C",
    }
    argv = ['smbclient', '-U', '%', '-L', hostname]
    try:
        p = ssubprocess.Popen(argv,
                              stdout=ssubprocess.PIPE,
                              stderr=null,
                              env=env)
        lines = p.stdout.readlines()
        p.wait()
    except OSError:
        _, e = sys.exc_info()[:2]
        log('%r failed: %r\n' % (argv, e))
        _smb_ok = False
        return

    lines.reverse()

    # junk at top
    while lines:
        line = lines.pop().strip()
        if re.match(r'Server\s+', line):
            break

    # server list section:
    #    Server   Comment
    #    ------   -------
    while lines:
        line = lines.pop().strip()
        if not line or re.match(r'-+\s+-+', line):
            continue
        if re.match(r'Workgroup\s+Master', line):
            break
        words = line.split()
        hostname = words[0].lower()
        debug3('<    %s\n' % hostname)
        check_host(hostname)

    # workgroup list section:
    #   Workgroup  Master
    #   ---------  ------
    while lines:
        line = lines.pop().strip()
        if re.match(r'-+\s+', line):
            continue
        if not line:
            break
        words = line.split()
        (workgroup, hostname) = (words[0].lower(), words[1].lower())
        debug3('<    group(%s) -> %s\n' % (workgroup, hostname))
        check_host(hostname)
        check_workgroup(workgroup)

    if lines:
        assert (0)
Пример #37
0
 def setnoread(self):
     if not self.shut_read:
         debug2('%r: done reading\n' % self)
         self.shut_read = True
         self.maybe_close()
Пример #38
0
def connect(ssh_cmd, rhostport, python, stderr, options):
    portl = []

    if re.sub(r'.*@', '', rhostport or '').count(':') > 1:
        if rhostport.count(']') or rhostport.count('['):
            result = rhostport.split(']')
            rhost = result[0].strip('[')
            if len(result) > 1:
                result[1] = result[1].strip(':')
                if result[1] is not '':
                    portl = ['-p', str(int(result[1]))]
        # can't disambiguate IPv6 colons and a port number. pass the hostname
        # through.
        else:
            rhost = rhostport
    else:  # IPv4
        l = (rhostport or '').rsplit(':', 1)
        rhost = l[0]
        if len(l) > 1:
            portl = ['-p', str(int(l[1]))]

    if rhost == '-':
        rhost = None

    z = zlib.compressobj(1)
    content = readfile('sshuttle.assembler')
    optdata = ''.join("%s=%r\n" % (k, v) for (k, v) in list(options.items()))
    optdata = optdata.encode("UTF8")
    content2 = (empackage(z, 'sshuttle') +
                empackage(z, 'sshuttle.cmdline_options', optdata) +
                empackage(z, 'sshuttle.helpers') +
                empackage(z, 'sshuttle.ssnet') +
                empackage(z, 'sshuttle.hostwatch') +
                empackage(z, 'sshuttle.server') + b"\n")

    pyscript = r"""
                import sys, os;
                verbosity=%d;
                sys.stdin = os.fdopen(0, "rb");
                exec(compile(sys.stdin.read(%d), "assembler.py", "exec"))
                """ % (helpers.verbose or 0, len(content))
    pyscript = re.sub(r'\s+', ' ', pyscript.strip())

    if not rhost:
        # ignore the --python argument when running locally; we already know
        # which python version works.
        argv = [sys.executable, '-c', pyscript]
    else:
        if ssh_cmd:
            sshl = shlex.split(ssh_cmd)
        else:
            sshl = ['ssh']
        if python:
            pycmd = "'%s' -c '%s'" % (python, pyscript)
        else:
            pycmd = ("P=python3; $P -V 2>/dev/null || P=python; "
                     "exec \"$P\" -c %s") % quote(pyscript)
            pycmd = ("exec /bin/sh -c %s" % quote(pycmd))
        argv = (sshl + portl + [rhost, '--', pycmd])
    (s1, s2) = socket.socketpair()

    def setup():
        # runs in the child process
        s2.close()

    s1a, s1b = os.dup(s1.fileno()), os.dup(s1.fileno())
    s1.close()
    debug2('executing: %r\n' % argv)
    p = ssubprocess.Popen(argv,
                          stdin=s1a,
                          stdout=s1b,
                          preexec_fn=setup,
                          close_fds=True,
                          stderr=stderr)
    os.close(s1a)
    os.close(s1b)
    s2.sendall(content)
    s2.sendall(content2)
    return p, s2
Пример #39
0
def main(listenip_v6, listenip_v4, ssh_cmd, remotename, python,
         latency_control, dns, nslist, method_name, seed_hosts, auto_hosts,
         auto_nets, subnets_include, subnets_exclude, daemon, to_nameserver,
         pidfile, user, sudo_pythonpath):

    if daemon:
        try:
            check_daemon(pidfile)
        except Fatal as e:
            log("%s\n" % e)
            return 5
    debug1('Starting sshuttle proxy.\n')

    fw = FirewallClient(method_name, sudo_pythonpath)

    # Get family specific subnet lists
    if dns:
        nslist += resolvconf_nameservers()
        if to_nameserver is not None:
            to_nameserver = "%s@%s" % tuple(to_nameserver[1:])
    else:
        # option doesn't make sense if we aren't proxying dns
        to_nameserver = None

    subnets = subnets_include + subnets_exclude  # we don't care here
    subnets_v6 = [i for i in subnets if i[0] == socket.AF_INET6]
    nslist_v6 = [i for i in nslist if i[0] == socket.AF_INET6]
    subnets_v4 = [i for i in subnets if i[0] == socket.AF_INET]
    nslist_v4 = [i for i in nslist if i[0] == socket.AF_INET]

    # Check features available
    avail = fw.method.get_supported_features()
    required = Features()

    if listenip_v6 == "auto":
        if avail.ipv6:
            listenip_v6 = ('::1', 0)
        else:
            listenip_v6 = None

    if user is not None:
        if getpwnam is None:
            raise Fatal("Routing by user not available on this system.")
        try:
            user = getpwnam(user).pw_uid
        except KeyError:
            raise Fatal("User %s does not exist." % user)

    required.ipv6 = len(subnets_v6) > 0 or listenip_v6 is not None
    required.ipv4 = len(subnets_v4) > 0 or listenip_v4 is not None
    required.udp = avail.udp
    required.dns = len(nslist) > 0
    required.user = False if user is None else True

    # if IPv6 not supported, ignore IPv6 DNS servers
    if not required.ipv6:
        nslist_v6 = []
        nslist = nslist_v4

    fw.method.assert_features(required)

    if required.ipv6 and listenip_v6 is None:
        raise Fatal("IPv6 required but not listening.")

    # display features enabled
    debug1("IPv6 enabled: %r\n" % required.ipv6)
    debug1("UDP enabled: %r\n" % required.udp)
    debug1("DNS enabled: %r\n" % required.dns)
    debug1("User enabled: %r\n" % required.user)

    # bind to required ports
    if listenip_v4 == "auto":
        listenip_v4 = ('127.0.0.1', 0)

    if required.ipv4 and \
            not any(listenip_v4[0] == sex[1] for sex in subnets_v4):
        subnets_exclude.append((socket.AF_INET, listenip_v4[0], 32, 0, 0))

    if required.ipv6 and \
            not any(listenip_v6[0] == sex[1] for sex in subnets_v6):
        subnets_exclude.append((socket.AF_INET6, listenip_v6[0], 128, 0, 0))

    if listenip_v6 and listenip_v6[1] and listenip_v4 and listenip_v4[1]:
        # if both ports given, no need to search for a spare port
        ports = [
            0,
        ]
    else:
        # if at least one port missing, we have to search
        ports = range(12300, 9000, -1)
        # keep track of failed bindings and used ports to avoid trying to
        # bind to the same socket address twice in different listeners
        used_ports = []

    # search for free ports and try to bind
    last_e = None
    redirectport_v6 = 0
    redirectport_v4 = 0
    bound = False
    debug2('Binding redirector:')
    for port in ports:
        debug2(' %d' % port)
        tcp_listener = MultiListener()

        if required.udp:
            udp_listener = MultiListener(socket.SOCK_DGRAM)
        else:
            udp_listener = None

        if listenip_v6 and listenip_v6[1]:
            lv6 = listenip_v6
            redirectport_v6 = lv6[1]
        elif listenip_v6:
            lv6 = (listenip_v6[0], port)
            redirectport_v6 = port
        else:
            lv6 = None
            redirectport_v6 = 0

        if listenip_v4 and listenip_v4[1]:
            lv4 = listenip_v4
            redirectport_v4 = lv4[1]
        elif listenip_v4:
            lv4 = (listenip_v4[0], port)
            redirectport_v4 = port
        else:
            lv4 = None
            redirectport_v4 = 0

        try:
            tcp_listener.bind(lv6, lv4)
            if udp_listener:
                udp_listener.bind(lv6, lv4)
            bound = True
            used_ports.append(port)
            break
        except socket.error as e:
            if e.errno == errno.EADDRINUSE:
                last_e = e
                used_ports.append(port)
            else:
                raise e

    debug2('\n')
    if not bound:
        assert (last_e)
        raise last_e
    tcp_listener.listen(10)
    tcp_listener.print_listening("TCP redirector")
    if udp_listener:
        udp_listener.print_listening("UDP redirector")

    bound = False
    if required.dns:
        # search for spare port for DNS
        debug2('Binding DNS:')
        ports = range(12300, 9000, -1)
        for port in ports:
            debug2(' %d' % port)
            if port in used_ports: continue

            dns_listener = MultiListener(socket.SOCK_DGRAM)

            if listenip_v6:
                lv6 = (listenip_v6[0], port)
                dnsport_v6 = port
            else:
                lv6 = None
                dnsport_v6 = 0

            if listenip_v4:
                lv4 = (listenip_v4[0], port)
                dnsport_v4 = port
            else:
                lv4 = None
                dnsport_v4 = 0

            try:
                dns_listener.bind(lv6, lv4)
                bound = True
                used_ports.append(port)
                break
            except socket.error as e:
                if e.errno == errno.EADDRINUSE:
                    last_e = e
                    used_ports.append(port)
                else:
                    raise e
        debug2('\n')
        dns_listener.print_listening("DNS")
        if not bound:
            assert (last_e)
            raise last_e
    else:
        dnsport_v6 = 0
        dnsport_v4 = 0
        dns_listener = None

    # Last minute sanity checks.
    # These should never fail.
    # If these do fail, something is broken above.
    if subnets_v6:
        assert required.ipv6
        if redirectport_v6 == 0:
            raise Fatal("IPv6 subnets defined but not listening")

    if nslist_v6:
        assert required.dns
        assert required.ipv6
        if dnsport_v6 == 0:
            raise Fatal("IPv6 ns servers defined but not listening")

    if subnets_v4:
        if redirectport_v4 == 0:
            raise Fatal("IPv4 subnets defined but not listening")

    if nslist_v4:
        if dnsport_v4 == 0:
            raise Fatal("IPv4 ns servers defined but not listening")

    # setup method specific stuff on listeners
    fw.method.setup_tcp_listener(tcp_listener)
    if udp_listener:
        fw.method.setup_udp_listener(udp_listener)
    if dns_listener:
        fw.method.setup_udp_listener(dns_listener)

    # start the firewall
    fw.setup(subnets_include, subnets_exclude, nslist, redirectport_v6,
             redirectport_v4, dnsport_v6, dnsport_v4, required.udp, user)

    # start the client process
    try:
        return _main(tcp_listener, udp_listener, fw, ssh_cmd, remotename,
                     python, latency_control, dns_listener, seed_hosts,
                     auto_hosts, auto_nets, daemon, to_nameserver)
    finally:
        try:
            if daemon:
                # it's not our child anymore; can't waitpid
                fw.p.returncode = 0
            fw.done()
        finally:
            if daemon:
                daemon_cleanup()
Пример #40
0
def main(method_name, syslog):
    stdin, stdout = setup_daemon()
    hostmap = {}

    debug1('firewall manager: Starting firewall with Python version %s\n' %
           platform.python_version())

    if method_name == "auto":
        method = get_auto_method()
    else:
        method = get_method(method_name)

    if syslog:
        ssyslog.start_syslog()
        ssyslog.stderr_to_syslog()

    debug1('firewall manager: ready method name %s.\n' % method.name)
    stdout.write('READY %s\n' % method.name)
    stdout.flush()

    # we wait until we get some input before creating the rules.  That way,
    # sshuttle can launch us as early as possible (and get sudo password
    # authentication as early in the startup process as possible).
    line = stdin.readline(128)
    if not line:
        return  # parent died; nothing to do

    subnets = []
    if line != 'ROUTES\n':
        raise Fatal('firewall: expected ROUTES but got %r' % line)
    while 1:
        line = stdin.readline(128)
        if not line:
            raise Fatal('firewall: expected route but got %r' % line)
        elif line.startswith("NSLIST\n"):
            break
        try:
            (family, width, exclude, ip, fport, lport) = \
                    line.strip().split(',', 5)
        except:
            raise Fatal('firewall: expected route or NSLIST but got %r' % line)
        subnets.append((int(family), int(width), bool(int(exclude)), ip,
                        int(fport), int(lport)))
    debug2('firewall manager: Got subnets: %r\n' % subnets)

    nslist = []
    if line != 'NSLIST\n':
        raise Fatal('firewall: expected NSLIST but got %r' % line)
    while 1:
        line = stdin.readline(128)
        if not line:
            raise Fatal('firewall: expected nslist but got %r' % line)
        elif line.startswith("PORTS "):
            break
        try:
            (family, ip) = line.strip().split(',', 1)
        except:
            raise Fatal('firewall: expected nslist or PORTS but got %r' % line)
        nslist.append((int(family), ip))
        debug2('firewall manager: Got partial nslist: %r\n' % nslist)
    debug2('firewall manager: Got nslist: %r\n' % nslist)

    if not line.startswith('PORTS '):
        raise Fatal('firewall: expected PORTS but got %r' % line)
    _, _, ports = line.partition(" ")
    ports = ports.split(",")
    if len(ports) != 4:
        raise Fatal('firewall: expected 4 ports but got %d' % len(ports))
    port_v6 = int(ports[0])
    port_v4 = int(ports[1])
    dnsport_v6 = int(ports[2])
    dnsport_v4 = int(ports[3])

    assert (port_v6 >= 0)
    assert (port_v6 <= 65535)
    assert (port_v4 >= 0)
    assert (port_v4 <= 65535)
    assert (dnsport_v6 >= 0)
    assert (dnsport_v6 <= 65535)
    assert (dnsport_v4 >= 0)
    assert (dnsport_v4 <= 65535)

    debug2('firewall manager: Got ports: %d,%d,%d,%d\n' %
           (port_v6, port_v4, dnsport_v6, dnsport_v4))

    line = stdin.readline(128)
    if not line:
        raise Fatal('firewall: expected GO but got %r' % line)
    elif not line.startswith("GO "):
        raise Fatal('firewall: expected GO but got %r' % line)

    _, _, args = line.partition(" ")
    udp, user = args.strip().split(" ", 1)
    udp = bool(int(udp))
    if user == '-':
        user = None
    debug2('firewall manager: Got udp: %r, user: %r\n' % (udp, user))

    subnets_v6 = [i for i in subnets if i[0] == socket.AF_INET6]
    nslist_v6 = [i for i in nslist if i[0] == socket.AF_INET6]
    subnets_v4 = [i for i in subnets if i[0] == socket.AF_INET]
    nslist_v4 = [i for i in nslist if i[0] == socket.AF_INET]

    try:
        debug1('firewall manager: setting up.\n')

        if subnets_v6 or nslist_v6:
            debug2('firewall manager: setting up IPv6.\n')
            method.setup_firewall(port_v6, dnsport_v6, nslist_v6,
                                  socket.AF_INET6, subnets_v6, udp, user)

        if subnets_v4 or nslist_v4:
            debug2('firewall manager: setting up IPv4.\n')
            method.setup_firewall(port_v4, dnsport_v4, nslist_v4,
                                  socket.AF_INET, subnets_v4, udp, user)

        stdout.write('STARTED\n')
        sdnotify.send(sdnotify.ready(), sdnotify.status('Connected'))

        try:
            stdout.flush()
        except IOError:
            # the parent process died for some reason; he's surely been loud
            # enough, so no reason to report another error
            return

        # Now we wait until EOF or any other kind of exception.  We need
        # to stay running so that we don't need a *second* password
        # authentication at shutdown time - that cleanup is important!
        while 1:
            line = stdin.readline(128)
            if line.startswith('HOST '):
                (name, ip) = line[5:].strip().split(',', 1)
                hostmap[name] = ip
                debug2('firewall manager: setting up /etc/hosts.\n')
                rewrite_etc_hosts(hostmap, port_v6 or port_v4)
            elif line:
                if not method.firewall_command(line):
                    raise Fatal('firewall: expected command, got %r' % line)
            else:
                break
    finally:
        try:
            sdnotify.send(sdnotify.stop())
            debug1('firewall manager: undoing changes.\n')
        except:
            pass

        try:
            if subnets_v6 or nslist_v6:
                debug2('firewall manager: undoing IPv6 changes.\n')
                method.restore_firewall(port_v6, socket.AF_INET6, udp, user)
        except:
            try:
                debug1("firewall manager: "
                       "Error trying to undo IPv6 firewall.\n")
                for line in traceback.format_exc().splitlines():
                    debug1("---> %s\n" % line)
            except:
                pass

        try:
            if subnets_v4 or nslist_v4:
                debug2('firewall manager: undoing IPv4 changes.\n')
                method.restore_firewall(port_v4, socket.AF_INET, udp, user)
        except:
            try:
                debug1("firewall manager: "
                       "Error trying to undo IPv4 firewall.\n")
                for line in traceback.format_exc().splitlines():
                    debug1("firewall manager: ---> %s\n" % line)
            except:
                pass

        try:
            debug2('firewall manager: undoing /etc/hosts changes.\n')
            restore_etc_hosts(port_v6 or port_v4)
        except:
            try:
                debug1("firewall manager: "
                       "Error trying to undo /etc/hosts changes.\n")
                for line in traceback.format_exc().splitlines():
                    debug1("firewall manager: ---> %s\n" % line)
            except:
                pass
Пример #41
0
def connect(ssh_cmd, rhostport, python, stderr, options):
    username, password, port, host = parse_hostport(rhostport)
    if username:
        rhost = "{}@{}".format(username, host)
    else:
        rhost = host

    z = zlib.compressobj(1)
    content = readfile('sshuttle.assembler')
    optdata = ''.join("%s=%r\n" % (k, v) for (k, v) in list(options.items()))
    optdata = optdata.encode("UTF8")
    content2 = (empackage(z, 'sshuttle') +
                empackage(z, 'sshuttle.cmdline_options', optdata) +
                empackage(z, 'sshuttle.helpers') +
                empackage(z, 'sshuttle.ssnet') +
                empackage(z, 'sshuttle.hostwatch') +
                empackage(z, 'sshuttle.server') + b"\n")

    pyscript = r"""
                import sys, os;
                verbosity=%d;
                sys.stdin = os.fdopen(0, "rb");
                exec(compile(sys.stdin.read(%d), "assembler.py", "exec"))
                """ % (helpers.verbose or 0, len(content))
    pyscript = re.sub(r'\s+', ' ', pyscript.strip())

    if not rhost:
        # ignore the --python argument when running locally; we already know
        # which python version works.
        argv = [sys.executable, '-c', pyscript]
    else:
        if ssh_cmd:
            sshl = shlex.split(ssh_cmd)
        else:
            sshl = ['ssh']
        if python:
            pycmd = "'%s' -c '%s'" % (python, pyscript)
        else:
            pycmd = ("P=python3; $P -V 2>%s || P=python; "
                     "exec \"$P\" -c %s") % (os.devnull, quote(pyscript))
            pycmd = ("/bin/sh -c {}".format(quote(pycmd)))

        if password is not None:
            os.environ['SSHPASS'] = str(password)
            argv = (["sshpass", "-e"] + sshl + ["-p", str(port)] +
                    [rhost, '--', pycmd])

        else:
            argv = (sshl + ["-p", str(port)] + [rhost, '--', pycmd])
    (s1, s2) = socket.socketpair()

    def setup():
        # runs in the child process
        s2.close()

    s1a, s1b = os.dup(s1.fileno()), os.dup(s1.fileno())
    s1.close()
    debug2('executing: %r\n' % argv)
    p = ssubprocess.Popen(argv,
                          stdin=s1a,
                          stdout=s1b,
                          preexec_fn=setup,
                          close_fds=True,
                          stderr=stderr)
    os.close(s1a)
    os.close(s1b)
    s2.sendall(content)
    s2.sendall(content2)
    return p, s2
Пример #42
0
def main(method_name, syslog):
    stdin, stdout = setup_daemon()
    hostmap = {}

    debug1('firewall manager: Starting firewall with Python version %s\n'
           % platform.python_version())

    if method_name == "auto":
        method = get_auto_method()
    else:
        method = get_method(method_name)

    if syslog:
        ssyslog.start_syslog()
        ssyslog.stderr_to_syslog()

    debug1('firewall manager: ready method name %s.\n' % method.name)
    stdout.write('READY %s\n' % method.name)
    stdout.flush()

    # we wait until we get some input before creating the rules.  That way,
    # sshuttle can launch us as early as possible (and get sudo password
    # authentication as early in the startup process as possible).
    line = stdin.readline(128)
    if not line:
        return  # parent died; nothing to do

    subnets = []
    if line != 'ROUTES\n':
        raise Fatal('firewall: expected ROUTES but got %r' % line)
    while 1:
        line = stdin.readline(128)
        if not line:
            raise Fatal('firewall: expected route but got %r' % line)
        elif line.startswith("NSLIST\n"):
            break
        try:
            (family, width, exclude, ip) = line.strip().split(',', 3)
        except:
            raise Fatal('firewall: expected route or NSLIST but got %r' % line)
        subnets.append((int(family), int(width), bool(int(exclude)), ip))
    debug2('firewall manager: Got subnets: %r\n' % subnets)

    nslist = []
    if line != 'NSLIST\n':
        raise Fatal('firewall: expected NSLIST but got %r' % line)
    while 1:
        line = stdin.readline(128)
        if not line:
            raise Fatal('firewall: expected nslist but got %r' % line)
        elif line.startswith("PORTS "):
            break
        try:
            (family, ip) = line.strip().split(',', 1)
        except:
            raise Fatal('firewall: expected nslist or PORTS but got %r' % line)
        nslist.append((int(family), ip))
        debug2('firewall manager: Got partial nslist: %r\n' % nslist)
    debug2('firewall manager: Got nslist: %r\n' % nslist)

    if not line.startswith('PORTS '):
        raise Fatal('firewall: expected PORTS but got %r' % line)
    _, _, ports = line.partition(" ")
    ports = ports.split(",")
    if len(ports) != 4:
        raise Fatal('firewall: expected 4 ports but got %n' % len(ports))
    port_v6 = int(ports[0])
    port_v4 = int(ports[1])
    dnsport_v6 = int(ports[2])
    dnsport_v4 = int(ports[3])

    assert(port_v6 >= 0)
    assert(port_v6 <= 65535)
    assert(port_v4 >= 0)
    assert(port_v4 <= 65535)
    assert(dnsport_v6 >= 0)
    assert(dnsport_v6 <= 65535)
    assert(dnsport_v4 >= 0)
    assert(dnsport_v4 <= 65535)

    debug2('firewall manager: Got ports: %d,%d,%d,%d\n'
           % (port_v6, port_v4, dnsport_v6, dnsport_v4))

    line = stdin.readline(128)
    if not line:
        raise Fatal('firewall: expected GO but got %r' % line)
    elif not line.startswith("GO "):
        raise Fatal('firewall: expected GO but got %r' % line)

    _, _, udp = line.partition(" ")
    udp = bool(int(udp))
    debug2('firewall manager: Got udp: %r\n' % udp)

    subnets_v6 = [i for i in subnets if i[0] == socket.AF_INET6]
    nslist_v6 = [i for i in nslist if i[0] == socket.AF_INET6]
    subnets_v4 = [i for i in subnets if i[0] == socket.AF_INET]
    nslist_v4 = [i for i in nslist if i[0] == socket.AF_INET]

    try:
        debug1('firewall manager: setting up.\n')

        if len(subnets_v6) > 0 or len(nslist_v6) > 0:
            debug2('firewall manager: setting up IPv6.\n')
            method.setup_firewall(
                port_v6, dnsport_v6, nslist_v6,
                socket.AF_INET6, subnets_v6, udp)

        if len(subnets_v4) > 0 or len(nslist_v4) > 0:
            debug2('firewall manager: setting up IPv4.\n')
            method.setup_firewall(
                port_v4, dnsport_v4, nslist_v4,
                socket.AF_INET, subnets_v4, udp)

        stdout.write('STARTED\n')

        try:
            stdout.flush()
        except IOError:
            # the parent process died for some reason; he's surely been loud
            # enough, so no reason to report another error
            return

        # Now we wait until EOF or any other kind of exception.  We need
        # to stay running so that we don't need a *second* password
        # authentication at shutdown time - that cleanup is important!
        while 1:
            line = stdin.readline(128)
            if line.startswith('HOST '):
                (name, ip) = line[5:].strip().split(',', 1)
                hostmap[name] = ip
                debug2('firewall manager: setting up /etc/hosts.\n')
                rewrite_etc_hosts(hostmap, port_v6 or port_v4)
            elif line:
                if not method.firewall_command(line):
                    raise Fatal('firewall: expected command, got %r' % line)
            else:
                break
    finally:
        try:
            debug1('firewall manager: undoing changes.\n')
        except:
            pass

        try:
            if len(subnets_v6) > 0 or len(nslist_v6) > 0:
                debug2('firewall manager: undoing IPv6 changes.\n')
                method.restore_firewall(port_v6, socket.AF_INET6, udp)
        except:
            try:
                debug1("firewall manager: "
                       "Error trying to undo IPv6 firewall.\n")
                for line in traceback.format_exc().splitlines():
                    debug1("---> %s\n" % line)
            except:
                pass

        try:
            if len(subnets_v4) > 0 or len(nslist_v4) > 0:
                debug2('firewall manager: undoing IPv4 changes.\n')
                method.restore_firewall(port_v4, socket.AF_INET, udp)
        except:
            try:
                debug1("firewall manager: "
                       "Error trying to undo IPv4 firewall.\n")
                for line in traceback.format_exc().splitlines():
                    debug1("firewall manager: ---> %s\n" % line)
            except:
                pass

        try:
            debug2('firewall manager: undoing /etc/hosts changes.\n')
            restore_etc_hosts(port_v6 or port_v4)
        except:
            try:
                debug1("firewall manager: "
                       "Error trying to undo /etc/hosts changes.\n")
                for line in traceback.format_exc().splitlines():
                    debug1("firewall manager: ---> %s\n" % line)
            except:
                pass
Пример #43
0
def main(method_name, syslog):
    stdin, stdout = setup_daemon()

    if method_name == "auto":
        method = get_auto_method()
    else:
        method = get_method(method_name)

    if syslog:
        ssyslog.start_syslog()
        ssyslog.stderr_to_syslog()

    debug1('firewall manager ready method name %s.\n' % method.name)
    stdout.write('READY %s\n' % method.name)
    stdout.flush()

    # we wait until we get some input before creating the rules.  That way,
    # sshuttle can launch us as early as possible (and get sudo password
    # authentication as early in the startup process as possible).
    line = stdin.readline(128)
    if not line:
        return  # parent died; nothing to do

    subnets = []
    if line != 'ROUTES\n':
        raise Fatal('firewall: expected ROUTES but got %r' % line)
    while 1:
        line = stdin.readline(128)
        if not line:
            raise Fatal('firewall: expected route but got %r' % line)
        elif line.startswith("NSLIST\n"):
            break
        try:
            (family, width, exclude, ip) = line.strip().split(',', 3)
        except:
            raise Fatal('firewall: expected route or NSLIST but got %r' % line)
        subnets.append((int(family), int(width), bool(int(exclude)), ip))
    debug2('Got subnets: %r\n' % subnets)

    nslist = []
    if line != 'NSLIST\n':
        raise Fatal('firewall: expected NSLIST but got %r' % line)
    while 1:
        line = stdin.readline(128)
        if not line:
            raise Fatal('firewall: expected nslist but got %r' % line)
        elif line.startswith("PORTS "):
            break
        try:
            (family, ip) = line.strip().split(',', 1)
        except:
            raise Fatal('firewall: expected nslist or PORTS but got %r' % line)
        nslist.append((int(family), ip))
        debug2('Got partial nslist: %r\n' % nslist)
    debug2('Got nslist: %r\n' % nslist)

    if not line.startswith('PORTS '):
        raise Fatal('firewall: expected PORTS but got %r' % line)
    _, _, ports = line.partition(" ")
    ports = ports.split(",")
    if len(ports) != 4:
        raise Fatal('firewall: expected 4 ports but got %n' % len(ports))
    port_v6 = int(ports[0])
    port_v4 = int(ports[1])
    dnsport_v6 = int(ports[2])
    dnsport_v4 = int(ports[3])

    assert (port_v6 >= 0)
    assert (port_v6 <= 65535)
    assert (port_v4 >= 0)
    assert (port_v4 <= 65535)
    assert (dnsport_v6 >= 0)
    assert (dnsport_v6 <= 65535)
    assert (dnsport_v4 >= 0)
    assert (dnsport_v4 <= 65535)

    debug2('Got ports: %d,%d,%d,%d\n' %
           (port_v6, port_v4, dnsport_v6, dnsport_v4))

    line = stdin.readline(128)
    if not line:
        raise Fatal('firewall: expected GO but got %r' % line)
    elif not line.startswith("GO "):
        raise Fatal('firewall: expected GO but got %r' % line)

    _, _, udp = line.partition(" ")
    udp = bool(int(udp))
    debug2('Got udp: %r\n' % udp)

    try:
        do_wait = None
        debug1('firewall manager: starting transproxy.\n')

        nslist_v6 = [i for i in nslist if i[0] == socket.AF_INET6]
        subnets_v6 = [i for i in subnets if i[0] == socket.AF_INET6]
        if port_v6 > 0:
            do_wait = method.setup_firewall(port_v6, dnsport_v6, nslist_v6,
                                            socket.AF_INET6, subnets_v6, udp)
        elif len(subnets_v6) > 0:
            debug1("IPv6 subnets defined but IPv6 disabled\n")

        nslist_v4 = [i for i in nslist if i[0] == socket.AF_INET]
        subnets_v4 = [i for i in subnets if i[0] == socket.AF_INET]
        if port_v4 > 0:
            do_wait = method.setup_firewall(port_v4, dnsport_v4, nslist_v4,
                                            socket.AF_INET, subnets_v4, udp)
        elif len(subnets_v4) > 0:
            debug1('IPv4 subnets defined but IPv4 disabled\n')

        stdout.write('STARTED\n')

        try:
            stdout.flush()
        except IOError:
            # the parent process died for some reason; he's surely been loud
            # enough, so no reason to report another error
            return

        # Now we wait until EOF or any other kind of exception.  We need
        # to stay running so that we don't need a *second* password
        # authentication at shutdown time - that cleanup is important!
        while 1:
            if do_wait is not None:
                do_wait()
            line = stdin.readline(128)
            if line.startswith('HOST '):
                (name, ip) = line[5:].strip().split(',', 1)
                hostmap[name] = ip
                rewrite_etc_hosts(port_v6 or port_v4)
            elif line:
                if not method.firewall_command(line):
                    raise Fatal('expected EOF, got %r' % line)
            else:
                break
    finally:
        try:
            debug1('firewall manager: undoing changes.\n')
        except:
            pass
        if port_v6:
            method.setup_firewall(port_v6, 0, [], socket.AF_INET6, [], udp)
        if port_v4:
            method.setup_firewall(port_v4, 0, [], socket.AF_INET, [], udp)
        restore_etc_hosts(port_v6 or port_v4)
Пример #44
0
 def is_supported(self):
     if which("nft"):
         return True
     debug2("nft method not supported because 'nft' command is missing.")
     return False
Пример #45
0
 def maybe_close(self):
     if self.shut_read and self.shut_write:
         debug2('%r: closing connection\n' % self)
         # remove the mux's reference to us.  The python garbage collector
         # will then be able to reap our object.
         self.mux.channels[self.channel] = None
Пример #46
0
def main(method_name, syslog, ttl):
    helpers.logprefix = 'fw: '
    stdin, stdout = setup_daemon()
    hostmap = {}
    debug1('Starting firewall with Python version %s'
           % platform.python_version())

    if method_name == "auto":
        method = get_auto_method()
    else:
        method = get_method(method_name)

    if syslog:
        ssyslog.start_syslog()
        ssyslog.stderr_to_syslog()

    if not method.is_supported():
        raise Fatal("The %s method is not supported on this machine. "
                    "Check that the appropriate programs are in your "
                    "PATH." % method_name)

    debug1('ready method name %s.' % method.name)
    stdout.write('READY %s\n' % method.name)
    stdout.flush()

    # we wait until we get some input before creating the rules.  That way,
    # sshuttle can launch us as early as possible (and get sudo password
    # authentication as early in the startup process as possible).
    line = stdin.readline(128)
    if not line:
        return  # parent died; nothing to do

    subnets = []
    if line != 'ROUTES\n':
        raise Fatal('expected ROUTES but got %r' % line)
    while 1:
        line = stdin.readline(128)
        if not line:
            raise Fatal('expected route but got %r' % line)
        elif line.startswith("NSLIST\n"):
            break
        try:
            (family, width, exclude, ip, fport, lport) = \
                    line.strip().split(',', 5)
        except BaseException:
            raise Fatal('expected route or NSLIST but got %r' % line)
        subnets.append((
            int(family),
            int(width),
            bool(int(exclude)),
            ip,
            int(fport),
            int(lport)))
    debug2('Got subnets: %r' % subnets)

    nslist = []
    if line != 'NSLIST\n':
        raise Fatal('expected NSLIST but got %r' % line)
    while 1:
        line = stdin.readline(128)
        if not line:
            raise Fatal('expected nslist but got %r' % line)
        elif line.startswith("PORTS "):
            break
        try:
            (family, ip) = line.strip().split(',', 1)
        except BaseException:
            raise Fatal('expected nslist or PORTS but got %r' % line)
        nslist.append((int(family), ip))
        debug2('Got partial nslist: %r' % nslist)
    debug2('Got nslist: %r' % nslist)

    if not line.startswith('PORTS '):
        raise Fatal('expected PORTS but got %r' % line)
    _, _, ports = line.partition(" ")
    ports = ports.split(",")
    if len(ports) != 4:
        raise Fatal('expected 4 ports but got %d' % len(ports))
    port_v6 = int(ports[0])
    port_v4 = int(ports[1])
    dnsport_v6 = int(ports[2])
    dnsport_v4 = int(ports[3])

    assert(port_v6 >= 0)
    assert(port_v6 <= 65535)
    assert(port_v4 >= 0)
    assert(port_v4 <= 65535)
    assert(dnsport_v6 >= 0)
    assert(dnsport_v6 <= 65535)
    assert(dnsport_v4 >= 0)
    assert(dnsport_v4 <= 65535)

    debug2('Got ports: %d,%d,%d,%d'
           % (port_v6, port_v4, dnsport_v6, dnsport_v4))

    line = stdin.readline(128)
    if not line:
        raise Fatal('expected GO but got %r' % line)
    elif not line.startswith("GO "):
        raise Fatal('expected GO but got %r' % line)

    _, _, args = line.partition(" ")
    udp, user, ttl, tmark = args.strip().split(" ", 3)
    udp = bool(int(udp))
    if user == '-':
        user = None
    ttl = int(ttl)
    debug2('Got udp: %r, user: %r, ttl: %s, tmark: %s' %
           (udp, user, ttl, tmark))

    subnets_v6 = [i for i in subnets if i[0] == socket.AF_INET6]
    nslist_v6 = [i for i in nslist if i[0] == socket.AF_INET6]
    subnets_v4 = [i for i in subnets if i[0] == socket.AF_INET]
    nslist_v4 = [i for i in nslist if i[0] == socket.AF_INET]

    try:
        debug1('setting up.')

        if subnets_v6 or nslist_v6:
            debug2('setting up IPv6.')
            method.setup_firewall(
                port_v6, dnsport_v6, nslist_v6,
                socket.AF_INET6, subnets_v6, udp,
                user, ttl, tmark)

        if subnets_v4 or nslist_v4:
            debug2('setting up IPv4.')
            method.setup_firewall(
                port_v4, dnsport_v4, nslist_v4,
                socket.AF_INET, subnets_v4, udp,
                user, ttl, tmark)

        flush_systemd_dns_cache()
        stdout.write('STARTED\n')

        try:
            stdout.flush()
        except IOError:
            # the parent process died for some reason; he's surely been loud
            # enough, so no reason to report another error
            return

        # Now we wait until EOF or any other kind of exception.  We need
        # to stay running so that we don't need a *second* password
        # authentication at shutdown time - that cleanup is important!
        while 1:
            line = stdin.readline(128)
            if line.startswith('HOST '):
                (name, ip) = line[5:].strip().split(',', 1)
                hostmap[name] = ip
                debug2('setting up /etc/hosts.')
                rewrite_etc_hosts(hostmap, port_v6 or port_v4)
            elif line:
                if not method.firewall_command(line):
                    raise Fatal('expected command, got %r' % line)
            else:
                break
    finally:
        try:
            debug1('undoing changes.')
        except BaseException:
            debug2('An error occurred, ignoring it.')

        try:
            if subnets_v6 or nslist_v6:
                debug2('undoing IPv6 changes.')
                method.restore_firewall(port_v6, socket.AF_INET6, udp, user)
        except BaseException:
            try:
                debug1("Error trying to undo IPv6 firewall.")
                debug1(traceback.format_exc())
            except BaseException:
                debug2('An error occurred, ignoring it.')

        try:
            if subnets_v4 or nslist_v4:
                debug2('undoing IPv4 changes.')
                method.restore_firewall(port_v4, socket.AF_INET, udp, user)
        except BaseException:
            try:
                debug1("Error trying to undo IPv4 firewall.")
                debug1(traceback.format_exc())
            except BaseException:
                debug2('An error occurred, ignoring it.')

        try:
            # debug2() message printed in restore_etc_hosts() function.
            restore_etc_hosts(hostmap, port_v6 or port_v4)
        except BaseException:
            try:
                debug1("Error trying to undo /etc/hosts changes.")
                debug1(traceback.format_exc())
            except BaseException:
                debug2('An error occurred, ignoring it.')

        try:
            flush_systemd_dns_cache()
        except BaseException:
            try:
                debug1("Error trying to flush systemd dns cache.")
                debug1(traceback.format_exc())
            except BaseException:
                debug2("An error occurred, ignoring it.")
Пример #47
0
def main(listenip_v6, listenip_v4,
         ssh_cmd, remotename, python, latency_control, dns, nslist,
         method_name, seed_hosts, auto_nets,
         subnets_include, subnets_exclude, syslog, daemon, pidfile):

    if syslog:
        ssyslog.start_syslog()
    if daemon:
        try:
            check_daemon(pidfile)
        except Fatal as e:
            log("%s\n" % e)
            return 5
    debug1('Starting sshuttle proxy.\n')

    fw = FirewallClient(method_name)

    features = fw.method.get_supported_features()
    if listenip_v6 == "auto":
        if features.ipv6:
            listenip_v6 = ('::1', 0)
        else:
            listenip_v6 = None

    if listenip_v4 == "auto":
        listenip_v4 = ('127.0.0.1', 0)

    udp = features.udp
    debug1("UDP enabled: %r\n" % udp)

    if listenip_v6 and listenip_v6[1] and listenip_v4 and listenip_v4[1]:
        # if both ports given, no need to search for a spare port
        ports = [0, ]
    else:
        # if at least one port missing, we have to search
        ports = range(12300, 9000, -1)

    # search for free ports and try to bind
    last_e = None
    redirectport_v6 = 0
    redirectport_v4 = 0
    bound = False
    debug2('Binding redirector:')
    for port in ports:
        debug2(' %d' % port)
        tcp_listener = MultiListener()
        tcp_listener.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

        if udp:
            udp_listener = MultiListener(socket.SOCK_DGRAM)
            udp_listener.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        else:
            udp_listener = None

        if listenip_v6 and listenip_v6[1]:
            lv6 = listenip_v6
            redirectport_v6 = lv6[1]
        elif listenip_v6:
            lv6 = (listenip_v6[0], port)
            redirectport_v6 = port
        else:
            lv6 = None
            redirectport_v6 = 0

        if listenip_v4 and listenip_v4[1]:
            lv4 = listenip_v4
            redirectport_v4 = lv4[1]
        elif listenip_v4:
            lv4 = (listenip_v4[0], port)
            redirectport_v4 = port
        else:
            lv4 = None
            redirectport_v4 = 0

        try:
            tcp_listener.bind(lv6, lv4)
            if udp_listener:
                udp_listener.bind(lv6, lv4)
            bound = True
            break
        except socket.error as e:
            if e.errno == errno.EADDRINUSE:
                last_e = e
            else:
                raise e

    debug2('\n')
    if not bound:
        assert(last_e)
        raise last_e
    tcp_listener.listen(10)
    tcp_listener.print_listening("TCP redirector")
    if udp_listener:
        udp_listener.print_listening("UDP redirector")

    bound = False
    if dns or nslist:
        if dns:
            nslist += resolvconf_nameservers()
        dns = True
        # search for spare port for DNS
        debug2('Binding DNS:')
        ports = range(12300, 9000, -1)
        for port in ports:
            debug2(' %d' % port)
            dns_listener = MultiListener(socket.SOCK_DGRAM)

            if listenip_v6:
                lv6 = (listenip_v6[0], port)
                dnsport_v6 = port
            else:
                lv6 = None
                dnsport_v6 = 0

            if listenip_v4:
                lv4 = (listenip_v4[0], port)
                dnsport_v4 = port
            else:
                lv4 = None
                dnsport_v4 = 0

            try:
                dns_listener.bind(lv6, lv4)
                bound = True
                break
            except socket.error as e:
                if e.errno == errno.EADDRINUSE:
                    last_e = e
                else:
                    raise e
        debug2('\n')
        dns_listener.print_listening("DNS")
        if not bound:
            assert(last_e)
            raise last_e
    else:
        dnsport_v6 = 0
        dnsport_v4 = 0
        dns_listener = None

    fw.method.check_settings(udp, dns)
    fw.method.setup_tcp_listener(tcp_listener)
    if udp_listener:
        fw.method.setup_udp_listener(udp_listener)
    if dns_listener:
        fw.method.setup_udp_listener(dns_listener)

    fw.setup(subnets_include, subnets_exclude, nslist,
             redirectport_v6, redirectport_v4, dnsport_v6, dnsport_v4,
             udp)

    try:
        return _main(tcp_listener, udp_listener, fw, ssh_cmd, remotename,
                     python, latency_control, dns_listener,
                     seed_hosts, auto_nets, syslog,
                     daemon)
    finally:
        try:
            if daemon:
                # it's not our child anymore; can't waitpid
                fw.p.returncode = 0
            fw.done()
        finally:
            if daemon:
                daemon_cleanup()
Пример #48
0
 def noread(self):
     if not self.shut_read:
         debug2('%r: done reading\n' % self)
         self.shut_read = True
Пример #49
0
 def is_supported(self):
     if which("pfctl"):
         return True
     debug2("pf method not supported because 'pfctl' command is missing.")
     return False
Пример #50
0
 def setnowrite(self):
     if not self.shut_write:
         debug2('%r: done writing\n' % self)
         self.shut_write = True
         self.maybe_close()
Пример #51
0
def main(listenip_v6, listenip_v4,
         ssh_cmd, remotename, python, latency_control, dns, nslist,
         method_name, seed_hosts, auto_nets,
         subnets_include, subnets_exclude, daemon, pidfile):

    if daemon:
        try:
            check_daemon(pidfile)
        except Fatal as e:
            log("%s\n" % e)
            return 5
    debug1('Starting sshuttle proxy.\n')

    fw = FirewallClient(method_name)

    # Get family specific subnet lists
    if dns:
        nslist += resolvconf_nameservers()

    subnets = subnets_include + subnets_exclude  # we don't care here
    subnets_v6 = [i for i in subnets if i[0] == socket.AF_INET6]
    nslist_v6 = [i for i in nslist if i[0] == socket.AF_INET6]
    subnets_v4 = [i for i in subnets if i[0] == socket.AF_INET]
    nslist_v4 = [i for i in nslist if i[0] == socket.AF_INET]

    # Check features available
    avail = fw.method.get_supported_features()
    required = Features()

    if listenip_v6 == "auto":
        if avail.ipv6:
            listenip_v6 = ('::1', 0)
        else:
            listenip_v6 = None

    required.ipv6 = len(subnets_v6) > 0 or listenip_v6 is not None
    required.udp = avail.udp
    required.dns = len(nslist) > 0

    # if IPv6 not supported, ignore IPv6 DNS servers
    if not required.ipv6:
        nslist_v6 = []
        nslist = nslist_v4

    fw.method.assert_features(required)

    if required.ipv6 and listenip_v6 is None:
        raise Fatal("IPv6 required but not listening.")

    # display features enabled
    debug1("IPv6 enabled: %r\n" % required.ipv6)
    debug1("UDP enabled: %r\n" % required.udp)
    debug1("DNS enabled: %r\n" % required.dns)

    # bind to required ports
    if listenip_v4 == "auto":
        listenip_v4 = ('127.0.0.1', 0)

    if listenip_v6 and listenip_v6[1] and listenip_v4 and listenip_v4[1]:
        # if both ports given, no need to search for a spare port
        ports = [0, ]
    else:
        # if at least one port missing, we have to search
        ports = range(12300, 9000, -1)

    # search for free ports and try to bind
    last_e = None
    redirectport_v6 = 0
    redirectport_v4 = 0
    bound = False
    debug2('Binding redirector:')
    for port in ports:
        debug2(' %d' % port)
        tcp_listener = MultiListener()

        if required.udp:
            udp_listener = MultiListener(socket.SOCK_DGRAM)
        else:
            udp_listener = None

        if listenip_v6 and listenip_v6[1]:
            lv6 = listenip_v6
            redirectport_v6 = lv6[1]
        elif listenip_v6:
            lv6 = (listenip_v6[0], port)
            redirectport_v6 = port
        else:
            lv6 = None
            redirectport_v6 = 0

        if listenip_v4 and listenip_v4[1]:
            lv4 = listenip_v4
            redirectport_v4 = lv4[1]
        elif listenip_v4:
            lv4 = (listenip_v4[0], port)
            redirectport_v4 = port
        else:
            lv4 = None
            redirectport_v4 = 0

        try:
            tcp_listener.bind(lv6, lv4)
            if udp_listener:
                udp_listener.bind(lv6, lv4)
            bound = True
            break
        except socket.error as e:
            if e.errno == errno.EADDRINUSE:
                last_e = e
            else:
                raise e

    debug2('\n')
    if not bound:
        assert(last_e)
        raise last_e
    tcp_listener.listen(10)
    tcp_listener.print_listening("TCP redirector")
    if udp_listener:
        udp_listener.print_listening("UDP redirector")

    bound = False
    if required.dns:
        # search for spare port for DNS
        debug2('Binding DNS:')
        ports = range(12300, 9000, -1)
        for port in ports:
            debug2(' %d' % port)
            dns_listener = MultiListener(socket.SOCK_DGRAM)

            if listenip_v6:
                lv6 = (listenip_v6[0], port)
                dnsport_v6 = port
            else:
                lv6 = None
                dnsport_v6 = 0

            if listenip_v4:
                lv4 = (listenip_v4[0], port)
                dnsport_v4 = port
            else:
                lv4 = None
                dnsport_v4 = 0

            try:
                dns_listener.bind(lv6, lv4)
                bound = True
                break
            except socket.error as e:
                if e.errno == errno.EADDRINUSE:
                    last_e = e
                else:
                    raise e
        debug2('\n')
        dns_listener.print_listening("DNS")
        if not bound:
            assert(last_e)
            raise last_e
    else:
        dnsport_v6 = 0
        dnsport_v4 = 0
        dns_listener = None

    # Last minute sanity checks.
    # These should never fail.
    # If these do fail, something is broken above.
    if len(subnets_v6) > 0:
        assert required.ipv6
        if redirectport_v6 == 0:
            raise Fatal("IPv6 subnets defined but not listening")

    if len(nslist_v6) > 0:
        assert required.dns
        assert required.ipv6
        if dnsport_v6 == 0:
            raise Fatal("IPv6 ns servers defined but not listening")

    if len(subnets_v4) > 0:
        if redirectport_v4 == 0:
            raise Fatal("IPv4 subnets defined but not listening")

    if len(nslist_v4) > 0:
        if dnsport_v4 == 0:
            raise Fatal("IPv4 ns servers defined but not listening")

    # setup method specific stuff on listeners
    fw.method.setup_tcp_listener(tcp_listener)
    if udp_listener:
        fw.method.setup_udp_listener(udp_listener)
    if dns_listener:
        fw.method.setup_udp_listener(dns_listener)

    # start the firewall
    fw.setup(subnets_include, subnets_exclude, nslist,
             redirectport_v6, redirectport_v4, dnsport_v6, dnsport_v4,
             required.udp)

    # start the client process
    try:
        return _main(tcp_listener, udp_listener, fw, ssh_cmd, remotename,
                     python, latency_control, dns_listener,
                     seed_hosts, auto_nets, daemon)
    finally:
        try:
            if daemon:
                # it's not our child anymore; can't waitpid
                fw.p.returncode = 0
            fw.done()
        finally:
            if daemon:
                daemon_cleanup()
Пример #52
0
 def is_supported(self):
     if which("ipfw"):
         return True
     debug2("ipfw method not supported because 'ipfw' command is "
            "missing.")
     return False
Пример #53
0
 def noread(self):
     if not self.shut_read:
         debug2('%r: done reading\n' % self)
         self.shut_read = True
         self.mux.send(self.channel, CMD_TCP_STOP_SENDING, b'')
         self.maybe_close()
Пример #54
0
 def onhostlist(hostlist):
     debug2('got host list: %r\n' % hostlist)
     for line in hostlist.strip().split():
         if line:
             name, ip = line.split(b',', 1)
             fw.sethostip(name, ip)
Пример #55
0
 def nowrite(self):
     if not self.shut_write:
         debug2('%r: done writing\n' % self)
         self.shut_write = True
         self.mux.send(self.channel, CMD_TCP_EOF, b'')
         self.maybe_close()
Пример #56
0
def connect(ssh_cmd, rhostport, python, stderr, options):
    portl = []

    if (rhostport or '').count(':') > 1:
        if rhostport.count(']') or rhostport.count('['):
            result = rhostport.split(']')
            rhost = result[0].strip('[')
            if len(result) > 1:
                result[1] = result[1].strip(':')
                if result[1] is not '':
                    portl = ['-p', str(int(result[1]))]
        # can't disambiguate IPv6 colons and a port number. pass the hostname
        # through.
        else:
            rhost = rhostport
    else:  # IPv4
        l = (rhostport or '').split(':', 1)
        rhost = l[0]
        if len(l) > 1:
            portl = ['-p', str(int(l[1]))]

    if rhost == '-':
        rhost = None

    z = zlib.compressobj(1)
    content = readfile('sshuttle.assembler')
    optdata = ''.join("%s=%r\n" % (k, v) for (k, v) in list(options.items()))
    optdata = optdata.encode("UTF8")
    content2 = (empackage(z, 'sshuttle') +
                empackage(z, 'sshuttle.cmdline_options', optdata) +
                empackage(z, 'sshuttle.helpers') +
                empackage(z, 'sshuttle.ssnet') +
                empackage(z, 'sshuttle.hostwatch') +
                empackage(z, 'sshuttle.server') +
                b"\n")

    pyscript = r"""
                import sys, os;
                verbosity=%d;
                sys.stdin = os.fdopen(0, "rb");
                exec(compile(sys.stdin.read(%d), "assembler.py", "exec"))
                """ % (helpers.verbose or 0, len(content))
    pyscript = re.sub(r'\s+', ' ', pyscript.strip())

    if not rhost:
        # ignore the --python argument when running locally; we already know
        # which python version works.
        argv = [sys.executable, '-c', pyscript]
    else:
        if ssh_cmd:
            sshl = shlex.split(ssh_cmd)
        else:
            sshl = ['ssh']
        if python:
            pycmd = "'%s' -c '%s'" % (python, pyscript)
        else:
            pycmd = ("P=python3.5; $P -V 2>/dev/null || P=python; "
                     "exec \"$P\" -c %s") % quote(pyscript)
            pycmd = ("exec /bin/sh -c %s" % quote(pycmd))
        argv = (sshl +
                portl +
                [rhost, '--', pycmd])
    (s1, s2) = socket.socketpair()

    def setup():
        # runs in the child process
        s2.close()
    s1a, s1b = os.dup(s1.fileno()), os.dup(s1.fileno())
    s1.close()
    debug2('executing: %r\n' % argv)
    p = ssubprocess.Popen(argv, stdin=s1a, stdout=s1b, preexec_fn=setup,
                          close_fds=True, stderr=stderr)
    os.close(s1a)
    os.close(s1b)
    s2.sendall(content)
    s2.sendall(content2)
    return p, s2
Пример #57
0
 def maybe_close(self):
     if self.shut_read and self.shut_write:
         debug2('%r: closing connection\n' % self)
         # remove the mux's reference to us.
         del self.mux.channels[self.channel]
Пример #58
0
 def dns_req(channel, data):
     debug2('Incoming DNS request channel=%d.\n' % channel)
     h = DnsProxy(mux, channel, data)
     handlers.append(h)
     dnshandlers[channel] = h
Пример #59
0
def connect_dst(family, ip, port):
    debug2('Connecting to %s:%d\n' % (ip, port))
    outsock = socket.socket(family)
    return SockWrapper(outsock, outsock,
                       connect_to=(ip, port),
                       peername='%s:%d' % (ip, port))