aboutsummaryrefslogtreecommitdiff
path: root/debian/patches/CVE-2020-16128.patch
diff options
context:
space:
mode:
Diffstat (limited to 'debian/patches/CVE-2020-16128.patch')
-rw-r--r--debian/patches/CVE-2020-16128.patch166
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