diff -Nru networkd-dispatcher-1.7/debian/changelog networkd-dispatcher-1.7/debian/changelog --- networkd-dispatcher-1.7/debian/changelog 2018-10-15 12:12:24.000000000 +0000 +++ networkd-dispatcher-1.7/debian/changelog 2022-04-28 10:43:22.000000000 +0000 @@ -1,3 +1,21 @@ +networkd-dispatcher (1.7-0ubuntu3.4) bionic-security; urgency=medium + + * SECURITY UPDATE: Directory traversal + - debian/patches/CVE-2022-29799.patch: Add allowed admin and + operational states in networkd-dispatcher and throw exceptions in + handle_state function if the current state is not one of those. + - CVE-2022-29799 + * SECURITY UPDATE: Time-of-check-time-of-use race condition + - debian/patches/CVE-2022-29800-1.patch: Add check_perms function that + will be invoked in scripts_in_path function before appending a file + path to the script_list in networkd-dispatcher. + - debian/patches/CVE-2022-29800-2.patch: Passes os.path.dirname(path) + when checking for permissions in scripts_in_path function in + networkd-dispatcher. + - CVE-2022-29800 + + -- Rodrigo Figueiredo Zaiden Thu, 28 Apr 2022 07:43:22 -0300 + networkd-dispatcher (1.7-0ubuntu3.3) bionic; urgency=medium * Allow overriding /usr/lib scripts in /etc/networkd-dispatcher. diff -Nru networkd-dispatcher-1.7/debian/patches/CVE-2022-29799.patch networkd-dispatcher-1.7/debian/patches/CVE-2022-29799.patch --- networkd-dispatcher-1.7/debian/patches/CVE-2022-29799.patch 1970-01-01 00:00:00.000000000 +0000 +++ networkd-dispatcher-1.7/debian/patches/CVE-2022-29799.patch 2022-04-28 10:42:36.000000000 +0000 @@ -0,0 +1,94 @@ +[Ubuntu note: Backport of the following patch, with adjustments to match +the current code baseline: removed the patch on the test file +tests/test_networkd-dispatcher.py and changed the logger.exception +message. +--Rodrigo Figueiredo Zaiden] + + +From 074ff68f08d64a963a13e3cfc4fb3e3fb9006dfe Mon Sep 17 00:00:00 2001 +From: Clayton Craft +Date: Fri, 1 Apr 2022 18:42:04 -0700 +Subject: [PATCH] don't allow unknown operational/admin states + +--- + networkd-dispatcher | 40 ++++++++++++++++++++++++------- + tests/test_networkd-dispatcher.py | 16 +++++++++++++ + 2 files changed, 47 insertions(+), 9 deletions(-) + +--- networkd-dispatcher-1.7.orig/networkd-dispatcher ++++ networkd-dispatcher-1.7/networkd-dispatcher +@@ -51,6 +51,11 @@ LOG_FORMAT = '%(levelname)s:%(message)s' + STATE_IGN = {'carrier', 'degraded', None} + SINGLETONS = {'Type', 'ESSID', 'OperationalState'} + ++# taken from https://www.freedesktop.org/software/systemd/man/networkctl.html ++ADMIN_STATES = ['configured', 'configuring', 'failed', 'pending', 'unmanaged', ++ 'linger'] ++OPER_STATES = ['carrier', 'degraded', 'degraded-carrier', 'dormant', ++ 'enslaved', 'missing', 'no-carrier', 'off', 'routable'] + + AddressList = collections.namedtuple('AddressList', ['ipv4', 'ipv6']) + NetworkctlListState = collections.namedtuple('NetworkctlListState', +@@ -58,6 +63,10 @@ NetworkctlListState = collections.namedt + 'operational', 'administrative']) + + ++class UnknownState(Exception): ++ pass ++ ++ + def unquote(buf, char='\\'): + """Remove escape characters from iwconfig ESSID output""" + idx = 0 +@@ -252,10 +261,12 @@ class Dispatcher(object): + administrative_state=iface.administrative, + operational_state=iface.operational, + force=True) +- # pylint: disable=broad-except +- except Exception: +- logger.exception('Error handling initial for interface %r', +- iface) ++ except UnknownState as e: ++ logger.exception("Unknown state for interface %s: %s", ++ iface, str(e)) ++ except Exception: # pylint: disable=broad-except ++ logger.exception("Error handling initial state for " ++ "interface %r", iface) + + def get_scripts_list(self, state): + """Return scripts for the given state""" +@@ -296,6 +307,13 @@ class Dispatcher(object): + + def handle_state(self, iface_name, administrative_state=None, + operational_state=None, force=False): ++ if (administrative_state and ++ administrative_state.lower() not in ADMIN_STATES): ++ raise UnknownState(administrative_state) ++ if (operational_state and ++ operational_state.lower() not in OPER_STATES): ++ raise UnknownState(operational_state) ++ + self._handle_one_state(iface_name, administrative_state, + 'administrative', force=force) + self._handle_one_state(iface_name, operational_state, 'operational', +@@ -373,11 +391,15 @@ class Dispatcher(object): + + if ((operational_state is not None) or + (administrative_state is not None)): +- self.handle_state(iface_name, +- administrative_state=str(administrative_state) +- if administrative_state else None, +- operational_state=str(operational_state) +- if operational_state else None,) ++ try: ++ self.handle_state(iface_name, ++ administrative_state=str(administrative_state) # noqa ++ if administrative_state else None, ++ operational_state=str(operational_state) ++ if operational_state else None,) ++ except UnknownState as e: ++ logger.exception("Unknown state for interface %s: %s", ++ iface_name, str(e)) + + # Handle interfaces that have been removed + if administrative_state == 'linger': diff -Nru networkd-dispatcher-1.7/debian/patches/CVE-2022-29800-1.patch networkd-dispatcher-1.7/debian/patches/CVE-2022-29800-1.patch --- networkd-dispatcher-1.7/debian/patches/CVE-2022-29800-1.patch 1970-01-01 00:00:00.000000000 +0000 +++ networkd-dispatcher-1.7/debian/patches/CVE-2022-29800-1.patch 2022-04-28 10:42:49.000000000 +0000 @@ -0,0 +1,93 @@ +[Ubuntu note: Backport of the following patch, with adjustments to match +the current code baseline: removed the patch on the test file +tests/test_networkd-dispatcher.py, removed one line on the import section +and changed the logger.error format. +--Rodrigo Figueiredo Zaiden] + +From 2e226ee027bdc8022f0e10470318f89f25dc6133 Mon Sep 17 00:00:00 2001 +From: Clayton Craft +Date: Fri, 1 Apr 2022 23:45:29 -0700 +Subject: [PATCH] make sure scripts are not writeable by non-root users + +In addition to the previous checks that ensure the scripts are owned by +root, this makes sure that other users can't write to them. +--- + networkd-dispatcher | 49 +++++++++++++++++++++++-------- + tests/test_networkd-dispatcher.py | 28 ++++++++++-------- + 2 files changed, 52 insertions(+), 25 deletions(-) + +--- networkd-dispatcher-1.7.orig/networkd-dispatcher ++++ networkd-dispatcher-1.7/networkd-dispatcher +@@ -15,8 +15,8 @@ import errno + import json + import logging + import os ++import pathlib + import socket +-import stat + import subprocess + import sys + +@@ -150,6 +150,24 @@ def iwconfig_get_ssid(iface_name): + return unquote(essid) + + ++def check_perms(path, mode=0o755, uid=0, gid=0): ++ """ Check that the given file or dir @ path has the given mode set, and is ++ owned by the given uid/gid. Symlinks are *not* followed. Raises ++ FileNotFoundError if path doesn't exist.""" ++ ++ if not os.path.exists(path): ++ raise FileNotFoundError ++ st = os.stat(path, follow_symlinks=False) ++ st_mode = st.st_mode & 0x00FFF ++ if st.st_uid == uid and st.st_gid == gid and st_mode == mode: ++ return True ++ ++ logger.error("invalid permissions on %s. expected mode=%s, uid=%d, " ++ "gid=%d; got mode=%s, uid=%d, gid=%d", path, oct(mode), uid, ++ gid, oct(st_mode), st.st_uid, st.st_gid) ++ return False ++ ++ + def scripts_in_path(path, subdir): + """Given directory names in PATH notation (separated by :), and a + subdirectory name, return a sorted list of executables +@@ -168,20 +186,25 @@ def scripts_in_path(path, subdir): + for filename in sorted(base_filenames): + for one_path in path.split(":"): + pathname = os.path.join(one_path, subdir, filename) +- logger.debug("Checking if %s exists as %s", filename, pathname) + + if os.path.isfile(pathname): +- entry = os.stat(pathname) +- # Make sure script can be executed +- if not stat.S_IXUSR & entry.st_mode: +- logger.error("Unable to execute script, check file mode: %s", +- pathname) +- # Make sure script is owned by root +- elif entry.st_uid != 0 or entry.st_gid != 0: +- logger.error("Unable to execute script, check file perms: %s", +- pathname) +- else: +- script_list.append(pathname) ++ try: ++ realpath = pathlib.Path(pathname).resolve() ++ ++ # Make sure that the file's parent dir has the correct ++ # perms, without following any symlinks ++ if not check_perms(os.path.basename(pathname), ++ 0o755, 0, 0): ++ continue ++ ++ # Make sure file has correct perms, after following any ++ # symlink(s) ++ if not check_perms(realpath, 0o755, 0, 0): ++ continue ++ except FileNotFoundError: ++ continue ++ ++ script_list.append(pathname) + break + + return script_list diff -Nru networkd-dispatcher-1.7/debian/patches/CVE-2022-29800-2.patch networkd-dispatcher-1.7/debian/patches/CVE-2022-29800-2.patch --- networkd-dispatcher-1.7/debian/patches/CVE-2022-29800-2.patch 1970-01-01 00:00:00.000000000 +0000 +++ networkd-dispatcher-1.7/debian/patches/CVE-2022-29800-2.patch 2022-04-28 10:43:04.000000000 +0000 @@ -0,0 +1,29 @@ +From 41e1d0f123c1113de24bafa3f23359e647945173 Mon Sep 17 00:00:00 2001 +From: primalmotion +Date: Tue, 26 Apr 2022 11:41:39 -0700 +Subject: [PATCH] fixed: use os.path.dirname instead of os.path.basename + +scripts_in_path is verifying if the containing folder of a candidate +script has the correct permissions invoking check_perms. However, it +passes the script's basename to check_perm (so '/path/to/script' becomes +'script'). check_perms then checks for the existence of this 'script' in +the current working directory, which fatally ends up with a FileNotFound +and a skip of the script. + +This patch fixes that by passing os.path.dirname(path) when checking +for the permissions of the parent folder. +--- + networkd-dispatcher | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- networkd-dispatcher-1.7.orig/networkd-dispatcher ++++ networkd-dispatcher-1.7/networkd-dispatcher +@@ -193,7 +193,7 @@ def scripts_in_path(path, subdir): + + # Make sure that the file's parent dir has the correct + # perms, without following any symlinks +- if not check_perms(os.path.basename(pathname), ++ if not check_perms(os.path.dirname(pathname), + 0o755, 0, 0): + continue + diff -Nru networkd-dispatcher-1.7/debian/patches/series networkd-dispatcher-1.7/debian/patches/series --- networkd-dispatcher-1.7/debian/patches/series 2018-10-15 12:12:24.000000000 +0000 +++ networkd-dispatcher-1.7/debian/patches/series 2022-04-28 10:43:01.000000000 +0000 @@ -1,3 +1,6 @@ 0001-Patch-etc-conf.d-p.conf-to-etc-default-p.patch 0002-networkd-dispatcher.conf-Run-startup-triggers-by-def.patch 0003-Allow-usr-lib-networkd-dispatcher-scripts-with-etc-o.patch +CVE-2022-29799.patch +CVE-2022-29800-1.patch +CVE-2022-29800-2.patch