diff --git a/libseccomp.py b/libseccomp.py new file mode 100644 index 0000000..dd71639 --- /dev/null +++ b/libseccomp.py @@ -0,0 +1,136 @@ +""" +Applies a seccomp filter on Linux via libseccomp's Python bindings. +May have some security benefits, but since Python and Qt use many calls +and are pretty high-level, things are very prone to breaking. + +Libseccomp's Python bindings aren't available on the pypi, check your distro's +package manager for python-libseccomp (Arch) or python3-seccomp (Debian). +""" +import os +import logging +import threading + +try: + import seccomp +except ImportError: + seccomp = None + +pesterchum_log = logging.getLogger("pchumLogger") + + +def activate_seccomp(): + """Sets the process into seccomp filter mode.""" + if seccomp is None: + pesterchum_log.error("Failed to import seccomp.") + return + # Violation gives "Operation not permitted". + sec_filter = seccomp.SyscallFilter(defaction=seccomp.ERRNO(1)) + # Full access calls + for call in PCHUM_SYSTEM_CALLS: + sec_filter.add_rule(seccomp.ALLOW, call) + + # Allow only UNIX and INET sockets, see the linux manual and source on socket for reference. + # Arg(0, seccomp.EQ, 1) means argument 0 must be equal to 1, 1 being the value of AF_UNIX. + sec_filter.add_rule(seccomp.ALLOW, "socket", seccomp.Arg(0, seccomp.EQ, 1)) # AF_UNIX + sec_filter.add_rule(seccomp.ALLOW, "socket", seccomp.Arg(0, seccomp.EQ, 2)) # AF_INET + + # We can kill ourselves in case of skill issues but not others. + sec_filter.add_rule(seccomp.ALLOW, "kill", seccomp.Arg(0, seccomp.EQ, os.getpid())) + sec_filter.add_rule(seccomp.ALLOW, "tgkill", seccomp.Arg(1, seccomp.EQ, threading.get_native_id())) + + sec_filter.load() + +def restrict_open(): + # Allow only opening for reading/writing + sec_filter.add_rule(seccomp.ALLOW, "openat", seccomp.Arg(0, seccomp.EQ, os.getpid())) + +# Required for Pesterchum to run. +PCHUM_SYSTEM_CALLS = [ + "access", + "bind", + "brk", + "clone3", + "close", + "connect", + "eventfd2", + "exit", + "exit_group", + "faccessat", + "faccessat2", + "fallocate", + "fcntl", + "fsync", + "ftruncate", + "futex", + "getcwd", + "getdents64", + "getegid", + "geteuid", + "getgid", + "getpeername", + "getpid", + "getrandom", + "getresgid", + "getresuid", + "getsockname", + "getsockopt", + "gettid", + "getuid", + "ioctl", + "lseek", + "memfd_create", + "mkdir", + "mmap", + "mprotect", + "munmap", + "newfstatat", + "openat", + "pipe2", + "poll", + "prctl", + "pselect6", + "pwrite64", + "read", + "recv", + "recvfrom", + "recvfrom", + "recvmmsg", + "recvmsg", + "restart_syscall", + "rseq", + "rt_sigaction", + "rt_sigprocmask", + "rt_sigreturn", + "sched_getaffinity", + "sched_getattr", + "sched_setattr", + "select", + "sendmmsg", + "sendmsg", + "sendto", + "setsockopt", + "shutdown", + "statx", + "umask", + "uname", + "write", +] + +""" +# Optional +EXTRA_CALLS = [ + "mlock", + "munlock", + "socketcall", + "socketpair", + "readlink", + "getsockname", + "getpeername", + "writev", + "open", + "time", + "listen", + "wait4", # for links? + "clone", # for links? +] +""" diff --git a/pesterchum.py b/pesterchum.py index 99f34b1..1b22cd5 100755 --- a/pesterchum.py +++ b/pesterchum.py @@ -17,6 +17,8 @@ if os.path.dirname(sys.argv[0]): import ostools import nickservmsgs import pytwmn +if ostools.isLinux(): + import libseccomp # import console from pnc.dep.attrdict import AttrDict @@ -105,6 +107,17 @@ parser.add_argument( parser.add_argument( "--nohonk", action="store_true", help="Disables the honk soundeffect 🤡📣" ) +if ostools.isLinux(): + parser.add_argument( + "--seccomp", + action="store_true", + help=( + "Restrict the system calls Pesterchum is allowed to make via seccomp." + " May have some security benefits, but since Python and Qt use many calls" + " and are pretty high-level, things are very prone to breaking." + " (Requires Linux and libseccomp's Python bindings, not available in frozen builds.)" + ), + ) # Set logging config section, log level is in oppts. # Logger @@ -1388,6 +1401,10 @@ class PesterWindow(MovingWindow): self.honk = options["honk"] else: self.honk = True + # Activate seccomp if enabled + if "seccomp" in options: + if options["seccomp"]: + libseccomp.activate_seccomp() self.modes = "" self.sound_type = None @@ -4548,6 +4565,10 @@ class MainProgram(QtCore.QObject): # Disable honks if args.nohonk: options["honk"] = False + if ostools.isLinux(): + if args.seccomp: + options["seccomp"] = True + except Exception as e: print(e) return options