mirror of
https://github.com/sshuttle/sshuttle.git
synced 2024-11-21 23:43:18 +01:00
commit
4e43af758d
@ -197,55 +197,101 @@ class FirewallClient:
|
|||||||
if ssyslog._p:
|
if ssyslog._p:
|
||||||
argvbase += ['--syslog']
|
argvbase += ['--syslog']
|
||||||
|
|
||||||
# Determine how to prefix the command in order to elevate privileges.
|
# A list of commands that we can try to run to start the firewall.
|
||||||
if platform.platform().startswith('OpenBSD'):
|
argv_tries = []
|
||||||
elev_prefix = ['doas'] # OpenBSD uses built in `doas`
|
|
||||||
|
if os.getuid() == 0: # No need to elevate privileges
|
||||||
|
argv_tries.append(argvbase)
|
||||||
else:
|
else:
|
||||||
elev_prefix = ['sudo', '-p', '[local sudo] Password: ']
|
# Linux typically uses sudo; OpenBSD uses doas. However, some
|
||||||
|
# Linux distributions are starting to use doas.
|
||||||
|
sudo_cmd = ['sudo', '-p', '[local sudo] Password: ']+argvbase
|
||||||
|
doas_cmd = ['doas']+argvbase
|
||||||
|
|
||||||
# Look for binary and switch to absolute path if we can find
|
# For clarity, try to replace executable name with the
|
||||||
# it.
|
# full path.
|
||||||
path = which(elev_prefix[0])
|
doas_path = which("doas")
|
||||||
if path:
|
if doas_path:
|
||||||
elev_prefix[0] = path
|
doas_cmd[0] = doas_path
|
||||||
|
sudo_path = which("sudo")
|
||||||
|
if sudo_path:
|
||||||
|
sudo_cmd[0] = sudo_path
|
||||||
|
|
||||||
if sudo_pythonpath:
|
# sudo_pythonpath indicates if we should set the
|
||||||
elev_prefix += ['/usr/bin/env',
|
# PYTHONPATH environment variable when elevating
|
||||||
'PYTHONPATH=%s' %
|
# privileges. This can be adjusted with the
|
||||||
os.path.dirname(os.path.dirname(__file__))]
|
# --no-sudo-pythonpath option.
|
||||||
argv_tries = [elev_prefix + argvbase, argvbase]
|
if sudo_pythonpath:
|
||||||
|
pp_prefix = ['/usr/bin/env',
|
||||||
|
'PYTHONPATH=%s' %
|
||||||
|
os.path.dirname(os.path.dirname(__file__))]
|
||||||
|
sudo_cmd = pp_prefix + sudo_cmd
|
||||||
|
doas_cmd = pp_prefix + doas_cmd
|
||||||
|
|
||||||
# we can't use stdin/stdout=subprocess.PIPE here, as we normally would,
|
# If we can find doas and not sudo or if we are on
|
||||||
# because stupid Linux 'su' requires that stdin be attached to a tty.
|
# OpenBSD, try using doas first.
|
||||||
# Instead, attach a *bidirectional* socket to its stdout, and use
|
if (doas_path and not sudo_path) or \
|
||||||
# that for talking in both directions.
|
platform.platform().startswith('OpenBSD'):
|
||||||
(s1, s2) = socket.socketpair()
|
argv_tries = [doas_cmd, sudo_cmd, argvbase]
|
||||||
|
else:
|
||||||
|
argv_tries = [sudo_cmd, doas_cmd, argvbase]
|
||||||
|
|
||||||
def setup():
|
# Try all commands in argv_tries in order. If a command
|
||||||
# run in the child process
|
# produces an error, try the next one. If command is
|
||||||
s2.close()
|
# successful, set 'success' variable and break.
|
||||||
if os.getuid() == 0:
|
success = False
|
||||||
argv_tries = argv_tries[-1:] # last entry only
|
|
||||||
for argv in argv_tries:
|
for argv in argv_tries:
|
||||||
|
# we can't use stdin/stdout=subprocess.PIPE here, as we
|
||||||
|
# normally would, because stupid Linux 'su' requires that
|
||||||
|
# stdin be attached to a tty. Instead, attach a
|
||||||
|
# *bidirectional* socket to its stdout, and use that for
|
||||||
|
# talking in both directions.
|
||||||
|
(s1, s2) = socket.socketpair()
|
||||||
|
|
||||||
|
def setup():
|
||||||
|
# run in the child process
|
||||||
|
s2.close()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
if argv[0] == 'su':
|
debug1("Starting firewall manager with command: %r" % argv)
|
||||||
sys.stderr.write('[local su] ')
|
|
||||||
self.p = ssubprocess.Popen(argv, stdout=s1, preexec_fn=setup)
|
self.p = ssubprocess.Popen(argv, stdout=s1, preexec_fn=setup)
|
||||||
# No env: Talking to `FirewallClient.start`, which has no i18n.
|
# No env: Talking to `FirewallClient.start`, which has no i18n.
|
||||||
break
|
|
||||||
except OSError as e:
|
except OSError as e:
|
||||||
log('Spawning firewall manager: %r' % argv)
|
# This exception will occur if the program isn't
|
||||||
raise Fatal(e)
|
# present or isn't executable.
|
||||||
self.argv = argv
|
debug1('Unable to start firewall manager. Popen failed. '
|
||||||
s1.close()
|
'Command=%r Exception=%s' % (argv, e))
|
||||||
self.pfile = s2.makefile('rwb')
|
continue
|
||||||
line = self.pfile.readline()
|
|
||||||
self.check()
|
self.argv = argv
|
||||||
if line[0:5] != b'READY':
|
s1.close()
|
||||||
raise Fatal('%r expected READY, got %r' % (self.argv, line))
|
self.pfile = s2.makefile('rwb')
|
||||||
method_name = line[6:-1]
|
line = self.pfile.readline()
|
||||||
self.method = get_method(method_name.decode("ASCII"))
|
|
||||||
self.method.set_firewall(self)
|
rv = self.p.poll() # Check if process is still running
|
||||||
|
if rv:
|
||||||
|
# We might get here if program runs and exits before
|
||||||
|
# outputting anything. For example, someone might have
|
||||||
|
# entered the wrong password to elevate privileges.
|
||||||
|
debug1('Unable to start firewall manager. '
|
||||||
|
'Process exited too early. '
|
||||||
|
'%r returned %d' % (self.argv, rv))
|
||||||
|
continue
|
||||||
|
|
||||||
|
if line[0:5] != b'READY':
|
||||||
|
debug1('Unable to start firewall manager. '
|
||||||
|
'Expected READY, got %r. '
|
||||||
|
'Command=%r' % (line, self.argv))
|
||||||
|
continue
|
||||||
|
|
||||||
|
method_name = line[6:-1]
|
||||||
|
self.method = get_method(method_name.decode("ASCII"))
|
||||||
|
self.method.set_firewall(self)
|
||||||
|
success = True
|
||||||
|
break
|
||||||
|
|
||||||
|
if not success:
|
||||||
|
raise Fatal("All attempts to elevate privileges failed.")
|
||||||
|
|
||||||
def setup(self, subnets_include, subnets_exclude, nslist,
|
def setup(self, subnets_include, subnets_exclude, nslist,
|
||||||
redirectport_v6, redirectport_v4, dnsport_v6, dnsport_v4, udp,
|
redirectport_v6, redirectport_v4, dnsport_v6, dnsport_v4, udp,
|
||||||
|
Loading…
Reference in New Issue
Block a user