summaryrefslogtreecommitdiffstats
path: root/config/chroot_local-includes/usr/local/lib/tails-autotest-remote-shell
diff options
context:
space:
mode:
Diffstat (limited to 'config/chroot_local-includes/usr/local/lib/tails-autotest-remote-shell')
-rwxr-xr-xconfig/chroot_local-includes/usr/local/lib/tails-autotest-remote-shell149
1 files changed, 89 insertions, 60 deletions
diff --git a/config/chroot_local-includes/usr/local/lib/tails-autotest-remote-shell b/config/chroot_local-includes/usr/local/lib/tails-autotest-remote-shell
index 3dd2384..3ebca14 100755
--- a/config/chroot_local-includes/usr/local/lib/tails-autotest-remote-shell
+++ b/config/chroot_local-includes/usr/local/lib/tails-autotest-remote-shell
@@ -4,76 +4,105 @@
# adversary with access to you *physical* serial port, which means
# that you are screwed any way.
-from subprocess import Popen, PIPE
-from sys import argv
-from json import dumps, loads
-from pwd import getpwnam
-from os import setgid, setuid, environ
+import base64
+import fcntl
+import json
+import os
+import pwd
import serial
-from systemd.daemon import notify as sd_notify
+import signal
+import subprocess
+import sys
+import systemd.daemon
+import traceback
+
+REMOTE_SHELL_DEV = '/dev/ttyS0'
+
def mk_switch_user_fn(uid, gid):
def switch_user():
- setgid(gid)
- setuid(uid)
+ os.setgid(gid)
+ os.setuid(uid)
return switch_user
+
def run_cmd_as_user(cmd, user):
- pwd_user = getpwnam(user)
- switch_user_fn = mk_switch_user_fn(pwd_user.pw_uid,
- pwd_user.pw_gid)
- # We try to create an environment identical to what's expected
- # inside Tails for the user by logging in (via `su`) as the user,
- # setting up the GNOME shell environment, and extracting the
- # environment via `env`; not that we will run `env` unconditionally
- # since the former command could fail, e.g. if GNOME is not running.
- env_cmd = '. /usr/local/lib/tails-shell-library/gnome.sh && ' + \
- 'export_gnome_env ; ' + \
- 'env'
- wrapped_env_cmd = "su -c '{}' {}".format(env_cmd, user)
- pipe = Popen(wrapped_env_cmd, stdout=PIPE, shell=True)
- env_data = pipe.communicate()[0].decode('utf-8')
- env = dict((line.split('=', 1) for line in env_data.splitlines()))
- cwd = env['HOME']
- return Popen(cmd, stdout=PIPE, stderr=PIPE, shell=True, env=env, cwd=cwd,
- preexec_fn=switch_user_fn)
+ pwd_user = pwd.getpwnam(user)
+ switch_user_fn = mk_switch_user_fn(pwd_user.pw_uid,
+ pwd_user.pw_gid)
+ # We try to create an environment identical to what's expected
+ # inside Tails for the user by logging in (via `su`) as the user,
+ # setting up the GNOME shell environment, and extracting the
+ # environment via `env`; not that we will run `env` unconditionally
+ # since the former command could fail, e.g. if GNOME is not running.
+ env_cmd = '. /usr/local/lib/tails-shell-library/gnome.sh && ' + \
+ 'export_gnome_env ; ' + \
+ 'env'
+ wrapped_env_cmd = "su -c '{}' {}".format(env_cmd, user)
+ pipe = subprocess.Popen(wrapped_env_cmd, stdout=subprocess.PIPE, shell=True)
+ env_data = pipe.communicate()[0].decode('utf-8')
+ env = dict((line.split('=', 1) for line in env_data.splitlines()))
+ cwd = env['HOME']
+ return subprocess.Popen(
+ cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE,
+ shell=True, env=env, cwd=cwd, preexec_fn=switch_user_fn
+ )
+
def main():
- dev = argv[1]
- port = serial.Serial(port = dev, baudrate = 4000000)
- if not port.isOpen():
- port.open()
+ port = serial.Serial(port = REMOTE_SHELL_DEV, baudrate = 4000000)
- # Notify systemd that we're ready
- sd_notify('READY=1')
- sd_notify('STATUS=Processing requests...\n')
+ # Notify systemd that we're ready
+ systemd.daemon.notify('READY=1')
+ systemd.daemon.notify('STATUS=Processing requests...\n')
- while True:
- try:
- line = port.readline().decode('utf-8')
- except Exception as e:
- # port must be opened wrong, so we restart everything and pray
- # that it works.
- print(str(e))
- port.close()
- return main()
- try:
- id, cmd_type, user, cmd = loads(line)
- except Exception as e:
- # We had a parse/pack error, so we just send a \0 as an ACK,
- # releasing the client from blocking.
- print(str(e))
- port.write(b"\0")
- continue
- p = run_cmd_as_user(cmd, user)
- if cmd_type == "spawn":
- returncode, stdout, stderr = 0, "", ""
- else:
- stdout_b, stderr_b = p.communicate()
- stdout = stdout_b.decode('utf-8')
- stderr = stderr_b.decode('utf-8')
- returncode = p.returncode
- port.write(dumps([id, returncode, stdout, stderr]).encode('utf-8') + b"\0")
+ while True:
+ line = port.readline().decode('utf-8')
+ try:
+ id, cmd_type, *rest = json.loads(line)
+ ret = ""
+ if cmd_type in ['call', 'spawn']:
+ user, cmd = rest
+ p = run_cmd_as_user(cmd, user)
+ if cmd_type == "spawn":
+ returncode, stdout, stderr = 0, "", ""
+ else:
+ stdout_b, stderr_b = p.communicate()
+ stdout = stdout_b.decode('utf-8')
+ stderr = stderr_b.decode('utf-8')
+ returncode = p.returncode
+ ret = json.dumps([id, 'success', returncode, stdout, stderr])
+ elif cmd_type in ['read', 'write', 'append']:
+ path, *rest = rest
+ open_mode = cmd_type[0] + 'b'
+ with open(path, open_mode) as f:
+ if cmd_type == 'read':
+ assert(rest == [])
+ ret = str(base64.b64encode(f.read()), 'utf-8')
+ elif cmd_type in ['write', 'append']:
+ assert(len(rest) == 1)
+ data = base64.b64decode(rest[0])
+ ret = f.write(data)
+ if ret != len(data):
+ raise IOError("we only wrote {} bytes out of {}"
+ .format(ret, len(data)))
+ ret = json.dumps([id, 'success'] + [ret])
+ else:
+ raise ValueError("unknown command type")
+ response = (ret + "\n").encode('utf-8')
+ port.write(response)
+ port.flush()
+ except Exception as e:
+ print("Error caught while processing line:", file=sys.stderr)
+ print(" " + line, file=sys.stderr)
+ print("The error was:", file=sys.stderr)
+ traceback.print_exc(file=sys.stdout)
+ print("-----", file=sys.stderr)
+ sys.stderr.flush()
+ exc_str = '{}: {}'.format(type(e).__name__, str(e))
+ port.write(json.dumps([id, 'error', exc_str]).encode('utf-8') + b"\n")
+ port.flush()
+ continue
if __name__ == "__main__":
- main()
+ main()