diff options
Diffstat (limited to 'debian/patches/CVE-2020-16128.patch')
-rw-r--r-- | debian/patches/CVE-2020-16128.patch | 166 |
1 files changed, 166 insertions, 0 deletions
diff --git a/debian/patches/CVE-2020-16128.patch b/debian/patches/CVE-2020-16128.patch new file mode 100644 index 0000000..4e10f9f --- /dev/null +++ b/debian/patches/CVE-2020-16128.patch @@ -0,0 +1,166 @@ +Description: Fix file existence disclosures (CVE-2020-16128) + Move set_euid_egid into aptdaemon.utils and use it when setting + Terminal and DebConfSocket properties, to ensure our checks only + have access to the same objects the user has. + . + Rewrite the checks so that they avoid extra os.access lookups and + have correct error handling for os.open() in the terminal, and + os.stat() in the debconf case. +Author: Julian Andres Klode <juliank@ubuntu.com> +Bug-Ubuntu: https://bugs.launchpad.net/bugs/1899513 +--- a/aptdaemon/core.py ++++ b/aptdaemon/core.py +@@ -60,7 +60,7 @@ from . import enums + from defer import inline_callbacks, return_value, Deferred + from defer.utils import dbus_deferred_method + from . import policykit1 +-from .utils import split_package_id ++from .utils import split_package_id, set_euid_egid + from .worker import DummyWorker + from .worker.aptworker import (AptWorker, + trans_only_installs_pkgs_from_high_trust_repos) +@@ -1107,24 +1107,28 @@ class Transaction(DBusObject): + """ + if self.status != enums.STATUS_SETTING_UP: + raise errors.TransactionAlreadyRunning() +- if not os.access(ttyname, os.W_OK): +- raise errors.AptDaemonError("Pty device does not exist: " +- "%s" % ttyname) +- if not os.stat(ttyname)[4] == self.uid: +- raise errors.AptDaemonError("Pty device '%s' has to be owned by" +- "the owner of the transaction " +- "(uid %s) " % (ttyname, self.uid)) +- if os.path.dirname(ttyname) != "/dev/pts": +- raise errors.AptDaemonError("%s isn't a tty" % ttyname) +- try: +- slave_fd = os.open(ttyname, os.O_RDWR | os.O_NOCTTY) +- if os.isatty(slave_fd): +- self.terminal = dbus.String(ttyname) +- self.PropertyChanged("Terminal", self.terminal) +- else: ++ with set_euid_egid(self.uid, self.gid): ++ if os.path.dirname(ttyname) != "/dev/pts": + raise errors.AptDaemonError("%s isn't a tty" % ttyname) +- finally: +- os.close(slave_fd) ++ ++ slave_fd = None ++ try: ++ slave_fd = os.open(ttyname, os.O_RDWR | os.O_NOCTTY) ++ except Exception: ++ raise errors.AptDaemonError("Could not open %s" % ttyname) ++ else: ++ if os.fstat(slave_fd).st_uid != self.uid: ++ raise errors.AptDaemonError("Pty device '%s' has to be owned by" ++ "the owner of the transaction " ++ "(uid %s) " % (ttyname, self.uid)) ++ if os.isatty(slave_fd): ++ self.terminal = dbus.String(ttyname) ++ self.PropertyChanged("Terminal", self.terminal) ++ else: ++ raise errors.AptDaemonError("%s isn't a tty" % ttyname) ++ finally: ++ if slave_fd is not None: ++ os.close(slave_fd) + + def _set_debconf(self, debconf_socket): + """Set the socket of the debconf proxy. +@@ -1140,13 +1144,17 @@ class Transaction(DBusObject): + """ + if self.status != enums.STATUS_SETTING_UP: + raise errors.TransactionAlreadyRunning() +- if not os.access(debconf_socket, os.W_OK): +- raise errors.AptDaemonError("socket does not exist: " +- "%s" % debconf_socket) +- if not os.stat(debconf_socket)[4] == self.uid: +- raise errors.AptDaemonError("socket '%s' has to be owned by the " +- "owner of the " +- "transaction" % debconf_socket) ++ with set_euid_egid(self.uid, self.gid): ++ try: ++ stat = os.stat(debconf_socket) ++ except Exception: ++ raise errors.AptDaemonError("socket status could not be read: " ++ "%s" % debconf_socket) ++ else: ++ if stat.st_uid != self.uid: ++ raise errors.AptDaemonError("socket '%s' has to be owned by the " ++ "owner of the " ++ "transaction" % debconf_socket) + self.debconf = dbus.String(debconf_socket) + self.PropertyChanged("DebconfSocket", self.debconf) + +--- a/aptdaemon/worker/aptworker.py ++++ b/aptdaemon/worker/aptworker.py +@@ -58,6 +58,7 @@ from . import BaseWorker + from ..enums import * + from ..errors import * + from .. import lock ++from ..utils import set_euid_egid + from ..progress import ( + DaemonOpenProgress, + DaemonInstallProgress, +@@ -90,25 +91,6 @@ USE_HTTP="yes" + """ + + +-@contextlib.contextmanager +-def set_euid_egid(uid, gid): +- # no need to drop privs +- if os.getuid() != 0 and os.getgid() != 0: +- yield +- return +- # temporary drop privs +- os.setegid(gid) +- old_groups = os.getgroups() +- os.setgroups([gid]) +- os.seteuid(uid) +- try: +- yield +- finally: +- os.seteuid(os.getuid()) +- os.setegid(os.getgid()) +- os.setgroups(old_groups) +- +- + def trans_only_installs_pkgs_from_high_trust_repos(trans, + whitelist=set()): + """Return True if this transaction only touches packages in the +--- a/aptdaemon/utils.py ++++ b/aptdaemon/utils.py +@@ -24,7 +24,9 @@ __author__ = "Sebastian Heinlein <devel@ + + __all__ = ("deprecated", "IsoCodes") + ++import os + import sys ++import contextlib + import gettext + import functools + import warnings +@@ -58,6 +60,25 @@ def deprecated(func): + return new_func + + ++@contextlib.contextmanager ++def set_euid_egid(uid, gid): ++ # no need to drop privs ++ if os.getuid() != 0 and os.getgid() != 0: ++ yield ++ return ++ # temporary drop privs ++ os.setegid(gid) ++ old_groups = os.getgroups() ++ os.setgroups([gid]) ++ os.seteuid(uid) ++ try: ++ yield ++ finally: ++ os.seteuid(os.getuid()) ++ os.setegid(os.getgid()) ++ os.setgroups(old_groups) ++ ++ + class IsoCodes(object): + + """Provides access to the iso-codes language, script and country |