All of lore.kernel.org
 help / color / mirror / Atom feed
* [sumo][PATCH 0/8] systemd: fix CVEs
@ 2019-02-25 16:37 George McCollister
  2019-02-25 16:37 ` [sumo][PATCH 1/8] systemd: fix CVE-2018-15686 George McCollister
                   ` (7 more replies)
  0 siblings, 8 replies; 9+ messages in thread
From: George McCollister @ 2019-02-25 16:37 UTC (permalink / raw)
  To: openembedded-core

Apply CVE patches to systemd. Backport changes from thud when possible
otherwise use patches from:
https://launchpad.net/ubuntu/+source/systemd/237-3ubuntu10.13

The following changes since commit 31f0c5e59c7fb0ae0915de584fbfcf3d95bbb061:

  testsdk: Improvements to the json logging (2018-12-07 10:56:32 +0000)

are available in the git repository at:

  git://github.com/gmccollister/openembedded-core sumo-cve-fixes
  https://github.com/gmccollister/openembedded-core/tree/sumo-cve-fixes

Chen Qi (2):
  systemd: fix CVE-2018-15686
  systemd: fix CVE-2018-15688

George McCollister (5):
  systemd: fix CVE-2018-15687
  systemd: Security fix CVE-2018-16864
  systemd: Security fix CVE-2018-16865
  systemd: fix CVE-2018-6954
  systemd: fix CVE-2019-6454

Marcus Cooper (1):
  systemd: Security fix CVE-2018-16866

 ...sive-let-s-rework-the-recursive-logic-to-.patch |  252 +++
 ...eserializing-state-always-use-read_line-L.patch |  250 +++
 ...sure-we-have-enough-space-for-the-DHCP6-o.patch |   39 +
 ...n-t-resolve-pathnames-when-traversing-rec.patch |  643 +++++++
 .../systemd/systemd/0002-Make-tmpfiles-safe.patch  | 1828 ++++++++++++++++++++
 ...-not-store-the-iovec-entry-for-process-co.patch |  193 +++
 ...ld-set-a-limit-on-the-number-of-fields-1k.patch |   60 +
 ...ote-set-a-limit-on-the-number-of-fields-i.patch |   79 +
 ...nal-fix-out-of-bounds-read-CVE-2018-16866.patch |   49 +
 .../systemd/systemd/CVE-2019-6454.patch            |  210 +++
 ...e-receive-an-invalid-dbus-message-ignore-.patch |   61 +
 meta/recipes-core/systemd/systemd_237.bb           |   11 +
 12 files changed, 3675 insertions(+)
 create mode 100644 meta/recipes-core/systemd/systemd/0001-chown-recursive-let-s-rework-the-recursive-logic-to-.patch
 create mode 100644 meta/recipes-core/systemd/systemd/0001-core-when-deserializing-state-always-use-read_line-L.patch
 create mode 100644 meta/recipes-core/systemd/systemd/0001-dhcp6-make-sure-we-have-enough-space-for-the-DHCP6-o.patch
 create mode 100644 meta/recipes-core/systemd/systemd/0001-tmpfiles-don-t-resolve-pathnames-when-traversing-rec.patch
 create mode 100644 meta/recipes-core/systemd/systemd/0002-Make-tmpfiles-safe.patch
 create mode 100644 meta/recipes-core/systemd/systemd/0024-journald-do-not-store-the-iovec-entry-for-process-co.patch
 create mode 100644 meta/recipes-core/systemd/systemd/0025-journald-set-a-limit-on-the-number-of-fields-1k.patch
 create mode 100644 meta/recipes-core/systemd/systemd/0026-journal-remote-set-a-limit-on-the-number-of-fields-i.patch
 create mode 100644 meta/recipes-core/systemd/systemd/0027-journal-fix-out-of-bounds-read-CVE-2018-16866.patch
 create mode 100644 meta/recipes-core/systemd/systemd/CVE-2019-6454.patch
 create mode 100644 meta/recipes-core/systemd/systemd/sd-bus-if-we-receive-an-invalid-dbus-message-ignore-.patch

-- 
2.11.0



^ permalink raw reply	[flat|nested] 9+ messages in thread

* [sumo][PATCH 1/8] systemd: fix CVE-2018-15686
  2019-02-25 16:37 [sumo][PATCH 0/8] systemd: fix CVEs George McCollister
@ 2019-02-25 16:37 ` George McCollister
  2019-02-25 16:37 ` [sumo][PATCH 2/8] systemd: fix CVE-2018-15687 George McCollister
                   ` (6 subsequent siblings)
  7 siblings, 0 replies; 9+ messages in thread
From: George McCollister @ 2019-02-25 16:37 UTC (permalink / raw)
  To: openembedded-core

From: Chen Qi <Qi.Chen@windriver.com>

Backport patch to fix the following CVE.

CVE: CVE-2018-15686

Signed-off-by: Chen Qi <Qi.Chen@windriver.com>
Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>

Cherry-picked from thud 0ef70603bc983315eb0e8a97958d995a31198c35

Signed-off-by: George McCollister <george.mccollister@gmail.com>
---
 ...eserializing-state-always-use-read_line-L.patch | 250 +++++++++++++++++++++
 meta/recipes-core/systemd/systemd_237.bb           |   1 +
 2 files changed, 251 insertions(+)
 create mode 100644 meta/recipes-core/systemd/systemd/0001-core-when-deserializing-state-always-use-read_line-L.patch

diff --git a/meta/recipes-core/systemd/systemd/0001-core-when-deserializing-state-always-use-read_line-L.patch b/meta/recipes-core/systemd/systemd/0001-core-when-deserializing-state-always-use-read_line-L.patch
new file mode 100644
index 0000000000..405300148a
--- /dev/null
+++ b/meta/recipes-core/systemd/systemd/0001-core-when-deserializing-state-always-use-read_line-L.patch
@@ -0,0 +1,250 @@
+From 56f77f7fcceea2fbb3b4efb8e307dd7784c63115 Mon Sep 17 00:00:00 2001
+From: Lennart Poettering <lennart@poettering.net>
+Date: Wed, 17 Oct 2018 18:36:24 +0200
+Subject: [PATCH] =?UTF-8?q?=3D=3FUTF-8=3Fq=3Fcore:=3D20when=3D20deserializ?=
+ =?UTF-8?q?ing=3D20state=3D20always=3F=3D?=
+
+ =?UTF-8?q?=20use=20read=5Fline(=E2=80=A6,=20LONG=5FLINE=5FMAX,=20?=
+ =?UTF-8?q?=E2=80=A6)?=
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+This should be much better than fgets(), as we can read substantially
+longer lines and overly long lines result in proper errors.
+
+Fixes a vulnerability discovered by Jann Horn at Google.
+
+CVE-2018-15686
+LP: #1796402
+https://bugzilla.redhat.com/show_bug.cgi?id=1639071
+
+(cherry picked from commit 8948b3415d762245ebf5e19d80b97d4d8cc208c1)
+
+CVE: CVE-2018-15686
+Upstream-Status: Backport
+
+Signed-off-by: Chen Qi <Qi.Chen@windriver.com>
+
+---
+ src/core/job.c     | 19 +++++++++++--------
+ src/core/manager.c | 44 ++++++++++++++++++++------------------------
+ src/core/unit.c    | 34 ++++++++++++++++++----------------
+ src/core/unit.h    |  2 +-
+ 4 files changed, 50 insertions(+), 49 deletions(-)
+
+diff --git a/src/core/job.c b/src/core/job.c
+index c6de8d27e..e0f9cee2f 100644
+--- a/src/core/job.c
++++ b/src/core/job.c
+@@ -28,6 +28,7 @@
+ #include "dbus-job.h"
+ #include "dbus.h"
+ #include "escape.h"
++#include "fileio.h"
+ #include "job.h"
+ #include "log.h"
+ #include "macro.h"
+@@ -1067,24 +1068,26 @@ int job_serialize(Job *j, FILE *f) {
+ }
+ 
+ int job_deserialize(Job *j, FILE *f) {
++        int r;
++
+         assert(j);
+         assert(f);
+ 
+         for (;;) {
+-                char line[LINE_MAX], *l, *v;
++                _cleanup_free_ char *line = NULL;
++                char *l, *v;
+                 size_t k;
+ 
+-                if (!fgets(line, sizeof(line), f)) {
+-                        if (feof(f))
+-                                return 0;
+-                        return -errno;
+-                }
++                r = read_line(f, LONG_LINE_MAX, &line);
++                if (r < 0)
++                        return log_error_errno(r, "Failed to read serialization line: %m");
++                if (r == 0)
++                        return 0;
+ 
+-                char_array_0(line);
+                 l = strstrip(line);
+ 
+                 /* End marker */
+-                if (l[0] == 0)
++                if (isempty(l))
+                         return 0;
+ 
+                 k = strcspn(l, "=");
+diff --git a/src/core/manager.c b/src/core/manager.c
+index e837a46f5..423f82c94 100644
+--- a/src/core/manager.c
++++ b/src/core/manager.c
+@@ -2841,22 +2841,19 @@ int manager_deserialize(Manager *m, FILE *f, FDSet *fds) {
+         m->n_reloading++;
+ 
+         for (;;) {
+-                char line[LINE_MAX];
++                _cleanup_free_ char *line = NULL;
+                 const char *val, *l;
+ 
+-                if (!fgets(line, sizeof(line), f)) {
+-                        if (feof(f))
+-                                r = 0;
+-                        else
+-                                r = -errno;
+-
++                r = read_line(f, LONG_LINE_MAX, &line);
++                if (r < 0) {
++                        log_error_errno(r, "Failed to read serialization line: %m");
+                         goto finish;
+                 }
++                if (r == 0)
++                        break;
+ 
+-                char_array_0(line);
+                 l = strstrip(line);
+-
+-                if (l[0] == 0)
++                if (isempty(l)) /* end marker */
+                         break;
+ 
+                 if ((val = startswith(l, "current-job-id="))) {
+@@ -3003,29 +3000,31 @@ int manager_deserialize(Manager *m, FILE *f, FDSet *fds) {
+         }
+ 
+         for (;;) {
+-                Unit *u;
+-                char name[UNIT_NAME_MAX+2];
++                _cleanup_free_ char *line = NULL;
+                 const char* unit_name;
++                Unit *u;
+ 
+                 /* Start marker */
+-                if (!fgets(name, sizeof(name), f)) {
+-                        if (feof(f))
+-                                r = 0;
+-                        else
+-                                r = -errno;
+-
++                r = read_line(f, LONG_LINE_MAX, &line);
++                if (r < 0) {
++                        log_error_errno(r, "Failed to read serialization line: %m");
+                         goto finish;
+                 }
++                if (r == 0)
++                        break;
+ 
+-                char_array_0(name);
+-                unit_name = strstrip(name);
++                unit_name = strstrip(line);
+ 
+                 r = manager_load_unit(m, unit_name, NULL, NULL, &u);
+                 if (r < 0) {
+                         log_notice_errno(r, "Failed to load unit \"%s\", skipping deserialization: %m", unit_name);
+                         if (r == -ENOMEM)
+                                 goto finish;
+-                        unit_deserialize_skip(f);
++
++                        r = unit_deserialize_skip(f);
++                        if (r < 0)
++                                goto finish;
++
+                         continue;
+                 }
+ 
+@@ -3038,9 +3037,6 @@ int manager_deserialize(Manager *m, FILE *f, FDSet *fds) {
+         }
+ 
+ finish:
+-        if (ferror(f))
+-                r = -EIO;
+-
+         assert(m->n_reloading > 0);
+         m->n_reloading--;
+ 
+diff --git a/src/core/unit.c b/src/core/unit.c
+index 932f05baa..4c3aa8e96 100644
+--- a/src/core/unit.c
++++ b/src/core/unit.c
+@@ -3346,21 +3346,19 @@ int unit_deserialize(Unit *u, FILE *f, FDSet *fds) {
+                 rt = (ExecRuntime**) ((uint8_t*) u + offset);
+ 
+         for (;;) {
+-                char line[LINE_MAX], *l, *v;
++                _cleanup_free_ char *line = NULL;
+                 CGroupIPAccountingMetric m;
++                char *l, *v;
+                 size_t k;
+ 
+-                if (!fgets(line, sizeof(line), f)) {
+-                        if (feof(f))
+-                                return 0;
+-                        return -errno;
+-                }
++                r = read_line(f, LONG_LINE_MAX, &line);
++                if (r < 0)
++                        return log_error_errno(r, "Failed to read serialization line: %m");
++                if (r == 0) /* eof */
++                        break;
+ 
+-                char_array_0(line);
+                 l = strstrip(line);
+-
+-                /* End marker */
+-                if (isempty(l))
++                if (isempty(l)) /* End marker */
+                         break;
+ 
+                 k = strcspn(l, "=");
+@@ -3637,23 +3635,27 @@ int unit_deserialize(Unit *u, FILE *f, FDSet *fds) {
+         return 0;
+ }
+ 
+-void unit_deserialize_skip(FILE *f) {
++int unit_deserialize_skip(FILE *f) {
++        int r;
+         assert(f);
+ 
+         /* Skip serialized data for this unit. We don't know what it is. */
+ 
+         for (;;) {
+-                char line[LINE_MAX], *l;
++                _cleanup_free_ char *line = NULL;
++                char *l;
+ 
+-                if (!fgets(line, sizeof line, f))
+-                        return;
++                r = read_line(f, LONG_LINE_MAX, &line);
++                if (r < 0)
++                        return log_error_errno(r, "Failed to read serialization line: %m");
++                if (r == 0)
++                        return 0;
+ 
+-                char_array_0(line);
+                 l = strstrip(line);
+ 
+                 /* End marker */
+                 if (isempty(l))
+-                        return;
++                        return 1;
+         }
+ }
+ 
+diff --git a/src/core/unit.h b/src/core/unit.h
+index 8c79d4ed2..08ad1c652 100644
+--- a/src/core/unit.h
++++ b/src/core/unit.h
+@@ -689,7 +689,7 @@ bool unit_can_serialize(Unit *u) _pure_;
+ 
+ int unit_serialize(Unit *u, FILE *f, FDSet *fds, bool serialize_jobs);
+ int unit_deserialize(Unit *u, FILE *f, FDSet *fds);
+-void unit_deserialize_skip(FILE *f);
++int unit_deserialize_skip(FILE *f);
+ 
+ int unit_serialize_item(Unit *u, FILE *f, const char *key, const char *value);
+ int unit_serialize_item_escaped(Unit *u, FILE *f, const char *key, const char *value);
diff --git a/meta/recipes-core/systemd/systemd_237.bb b/meta/recipes-core/systemd/systemd_237.bb
index a409b18297..28f3dafbb5 100644
--- a/meta/recipes-core/systemd/systemd_237.bb
+++ b/meta/recipes-core/systemd/systemd_237.bb
@@ -54,6 +54,7 @@ SRC_URI += "file://touchscreen.rules \
            file://libmount.patch \
            file://0034-Fix-format-truncation-compile-failure-by-typecasting.patch \
            file://0035-Define-glibc-compatible-basename-for-non-glibc-syste.patch \
+           file://0001-core-when-deserializing-state-always-use-read_line-L.patch \
            "
 SRC_URI_append_qemuall = " file://0001-core-device.c-Change-the-default-device-timeout-to-2.patch"
 
-- 
2.11.0



^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [sumo][PATCH 2/8] systemd: fix CVE-2018-15687
  2019-02-25 16:37 [sumo][PATCH 0/8] systemd: fix CVEs George McCollister
  2019-02-25 16:37 ` [sumo][PATCH 1/8] systemd: fix CVE-2018-15686 George McCollister
@ 2019-02-25 16:37 ` George McCollister
  2019-02-25 16:37 ` [sumo][PATCH 3/8] systemd: fix CVE-2018-15688 George McCollister
                   ` (5 subsequent siblings)
  7 siblings, 0 replies; 9+ messages in thread
From: George McCollister @ 2019-02-25 16:37 UTC (permalink / raw)
  To: openembedded-core

Backport patch to fix the following CVE.

CVE: CVE-2018-15687

Based on thud commit eeb621aa19f690971caf862290a172a115578ba1
The patch in the thud commit doesn't compile against 237. Use the
version of this patch,  CVE-2018-15687.patch from
systemd_237-3ubuntu10.13.debian.

Signed-off-by: George McCollister <george.mccollister@gmail.com>
---
 ...sive-let-s-rework-the-recursive-logic-to-.patch | 252 +++++++++++++++++++++
 meta/recipes-core/systemd/systemd_237.bb           |   1 +
 2 files changed, 253 insertions(+)
 create mode 100644 meta/recipes-core/systemd/systemd/0001-chown-recursive-let-s-rework-the-recursive-logic-to-.patch

diff --git a/meta/recipes-core/systemd/systemd/0001-chown-recursive-let-s-rework-the-recursive-logic-to-.patch b/meta/recipes-core/systemd/systemd/0001-chown-recursive-let-s-rework-the-recursive-logic-to-.patch
new file mode 100644
index 0000000000..9a0b55f97d
--- /dev/null
+++ b/meta/recipes-core/systemd/systemd/0001-chown-recursive-let-s-rework-the-recursive-logic-to-.patch
@@ -0,0 +1,252 @@
+From 2da8ba3f507345d0401ea9d7191fa16ffa560ebc Mon Sep 17 00:00:00 2001
+From: Lennart Poettering <lennart@poettering.net>
+Date: Fri, 19 Oct 2018 11:26:59 +0200
+Subject: [PATCH] chown-recursive: let's rework the recursive logic to use
+ O_PATH
+
+That way we can pin a specific inode and analyze it and manipulate it
+without it being swapped out beneath our hands.
+
+Fixes a vulnerability originally found by Jann Horn from Google.
+
+CVE-2018-15687
+LP: #1796692
+https://bugzilla.redhat.com/show_bug.cgi?id=1639076
+
+Origin: upstream, https://github.com/poettering/systemd/commit/5de6cce58b3e8b79239b6e83653459d91af6e57c
+Bug-Ubuntu: https://launchpad.net/bugs/1796692
+
+Patch for 237 from:
+systemd_237-3ubuntu10.13.debian CVE-2018-15687.patch
+
+CVE: CVE-2018-15687
+Upstream-Status: Backport
+
+---
+ src/core/chown-recursive.c | 146 ++++++++++++++++++++++-----------------------
+ 1 file changed, 70 insertions(+), 76 deletions(-)
+
+diff --git a/src/core/chown-recursive.c b/src/core/chown-recursive.c
+index c479450..27c6448 100644
+--- a/src/core/chown-recursive.c
++++ b/src/core/chown-recursive.c
+@@ -18,18 +18,20 @@
+   along with systemd; If not, see <http://www.gnu.org/licenses/>.
+ ***/
+
+-#include <sys/types.h>
+-#include <sys/stat.h>
+ #include <fcntl.h>
++#include <sys/stat.h>
++#include <sys/types.h>
+
+-#include "user-util.h"
+-#include "macro.h"
+-#include "fd-util.h"
+-#include "dirent-util.h"
+ #include "chown-recursive.h"
++#include "dirent-util.h"
++#include "fd-util.h"
++#include "macro.h"
++#include "stdio-util.h"
++#include "strv.h"
++#include "user-util.h"
+
+-static int chown_one(int fd, const char *name, const struct stat *st, uid_t uid, gid_t gid) {
+-        int r;
++static int chown_one(int fd, const struct stat *st, uid_t uid, gid_t gid) {
++        char procfs_path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int) + 1];
+
+         assert(fd >= 0);
+         assert(st);
+@@ -38,90 +40,82 @@
+             (!gid_is_valid(gid) || st->st_gid == gid))
+                 return 0;
+
+-        if (name)
+-                r = fchownat(fd, name, uid, gid, AT_SYMLINK_NOFOLLOW);
+-        else
+-                r = fchown(fd, uid, gid);
+-        if (r < 0)
+-                return -errno;
++        /* We change ownership through the /proc/self/fd/%i path, so that we have a stable reference that works with
++         * O_PATH. (Note: fchown() and fchmod() do not work with O_PATH, the kernel refuses that. */
++        xsprintf(procfs_path, "/proc/self/fd/%i", fd);
+
+-        /* The linux kernel alters the mode in some cases of chown(). Let's undo this. */
+-        if (name) {
+-                if (!S_ISLNK(st->st_mode))
+-                        r = fchmodat(fd, name, st->st_mode, 0);
+-                else /* There's currently no AT_SYMLINK_NOFOLLOW for fchmodat() */
+-                        r = 0;
+-        } else
+-                r = fchmod(fd, st->st_mode);
+-        if (r < 0)
++        if (chown(procfs_path, uid, gid) < 0)
+                 return -errno;
+
++        /* The linux kernel alters the mode in some cases of chown(). Let's undo this. We do this only for non-symlinks
++         * however. That's because for symlinks the access mode is ignored anyway and because on some kernels/file
++         * systems trying to change the access mode will succeed but has no effect while on others it actively
++         * fails. */
++        if (!S_ISLNK(st->st_mode))
++                if (chmod(procfs_path, st->st_mode & 07777) < 0)
++                        return -errno;
++
+         return 1;
+ }
+
+ static int chown_recursive_internal(int fd, const struct stat *st, uid_t uid, gid_t gid) {
++        _cleanup_closedir_ DIR *d = NULL;
+         bool changed = false;
++        struct dirent *de;
+         int r;
+
+         assert(fd >= 0);
+         assert(st);
+
+-        if (S_ISDIR(st->st_mode)) {
+-                _cleanup_closedir_ DIR *d = NULL;
+-                struct dirent *de;
+-
+-                d = fdopendir(fd);
+-                if (!d) {
+-                        r = -errno;
+-                        goto finish;
+-                }
+-                fd = -1;
+-
+-                FOREACH_DIRENT_ALL(de, d, r = -errno; goto finish) {
+-                        struct stat fst;
+-
+-                        if (dot_or_dot_dot(de->d_name))
+-                                continue;
++        d = fdopendir(fd);
++        if (!d) {
++                safe_close(fd);
++                return -errno;
++        }
+
+-                        if (fstatat(dirfd(d), de->d_name, &fst, AT_SYMLINK_NOFOLLOW) < 0) {
+-                                r = -errno;
+-                                goto finish;
+-                        }
+-
+-                        if (S_ISDIR(fst.st_mode)) {
+-                                int subdir_fd;
+-
+-                                subdir_fd = openat(dirfd(d), de->d_name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW|O_NOATIME);
+-                                if (subdir_fd < 0) {
+-                                        r = -errno;
+-                                        goto finish;
+-                                }
+-
+-                                r = chown_recursive_internal(subdir_fd, &fst, uid, gid);
+-                                if (r < 0)
+-                                        goto finish;
+-                                if (r > 0)
+-                                        changed = true;
+-                        } else {
+-                                r = chown_one(dirfd(d), de->d_name, &fst, uid, gid);
+-                                if (r < 0)
+-                                        goto finish;
+-                                if (r > 0)
+-                                        changed = true;
+-                        }
++        FOREACH_DIRENT_ALL(de, d, return -errno) {
++                _cleanup_close_ int path_fd = -1;
++                struct stat fst;
++
++                if (dot_or_dot_dot(de->d_name))
++                        continue;
++
++                /* Let's pin the child inode we want to fix now with an O_PATH fd, so that it cannot be swapped out
++                 * while we manipulate it. */
++                path_fd = openat(dirfd(d), de->d_name, O_PATH|O_CLOEXEC|O_NOFOLLOW);
++                if (path_fd < 0)
++                        return -errno;
++
++                if (fstat(path_fd, &fst) < 0)
++                        return -errno;
++
++                if (S_ISDIR(fst.st_mode)) {
++                        int subdir_fd;
++
++                        /* Convert it to a "real" (i.e. non-O_PATH) fd now */
++                        subdir_fd = fd_reopen(path_fd, O_RDONLY|O_CLOEXEC|O_NOATIME);
++                        if (subdir_fd < 0)
++                                return subdir_fd;
++
++                        r = chown_recursive_internal(subdir_fd, &fst, uid, gid); /* takes possession of subdir_fd even on failure */
++                        if (r < 0)
++                                return r;
++                        if (r > 0)
++                                changed = true;
++                } else {
++                        r = chown_one(path_fd, &fst, uid, gid);
++                        if (r < 0)
++                                return r;
++                        if (r > 0)
++                                changed = true;
+                 }
++        }
+
+-                r = chown_one(dirfd(d), NULL, st, uid, gid);
+-        } else
+-                r = chown_one(fd, NULL, st, uid, gid);
++        r = chown_one(dirfd(d), st, uid, gid);
+         if (r < 0)
+-                goto finish;
+-
+-        r = r > 0 || changed;
++                return r;
+
+-finish:
+-        safe_close(fd);
+-        return r;
++        return r > 0 || changed;
+ }
+
+ int path_chown_recursive(const char *path, uid_t uid, gid_t gid) {
+@@ -129,7 +123,7 @@
+         struct stat st;
+         int r;
+
+-        fd = open(path, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW|O_NOATIME);
++        fd = open(path, O_RDONLY|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW|O_NOATIME);
+         if (fd < 0)
+                 return -errno;
+
+--- a/src/basic/fd-util.c
++++ b/src/basic/fd-util.c
+@@ -578,3 +578,22 @@
+
+         return -EOPNOTSUPP;
+ }
++
++int fd_reopen(int fd, int flags) {
++        char procfs_path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)];
++        int new_fd;
++
++        /* Reopens the specified fd with new flags. This is useful for convert an O_PATH fd into a regular one, or to
++         * turn O_RDWR fds into O_RDONLY fds.
++         *
++         * This doesn't work on sockets (since they cannot be open()ed, ever).
++         *
++         * This implicitly resets the file read index to 0. */
++
++        xsprintf(procfs_path, "/proc/self/fd/%i", fd);
++        new_fd = open(procfs_path, flags);
++        if (new_fd < 0)
++                return -errno;
++
++        return new_fd;
++}
+--- a/src/basic/fd-util.h
++++ b/src/basic/fd-util.h
+@@ -91,3 +91,5 @@
+ /* Hint: ENETUNREACH happens if we try to connect to "non-existing" special IP addresses, such as ::5 */
+ #define ERRNO_IS_DISCONNECT(r) \
+         IN_SET(r, ENOTCONN, ECONNRESET, ECONNREFUSED, ECONNABORTED, EPIPE, ENETUNREACH)
++
++int fd_reopen(int fd, int flags);
diff --git a/meta/recipes-core/systemd/systemd_237.bb b/meta/recipes-core/systemd/systemd_237.bb
index 28f3dafbb5..cae9bccc60 100644
--- a/meta/recipes-core/systemd/systemd_237.bb
+++ b/meta/recipes-core/systemd/systemd_237.bb
@@ -55,6 +55,7 @@ SRC_URI += "file://touchscreen.rules \
            file://0034-Fix-format-truncation-compile-failure-by-typecasting.patch \
            file://0035-Define-glibc-compatible-basename-for-non-glibc-syste.patch \
            file://0001-core-when-deserializing-state-always-use-read_line-L.patch \
+           file://0001-chown-recursive-let-s-rework-the-recursive-logic-to-.patch \
            "
 SRC_URI_append_qemuall = " file://0001-core-device.c-Change-the-default-device-timeout-to-2.patch"
 
-- 
2.11.0



^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [sumo][PATCH 3/8] systemd: fix CVE-2018-15688
  2019-02-25 16:37 [sumo][PATCH 0/8] systemd: fix CVEs George McCollister
  2019-02-25 16:37 ` [sumo][PATCH 1/8] systemd: fix CVE-2018-15686 George McCollister
  2019-02-25 16:37 ` [sumo][PATCH 2/8] systemd: fix CVE-2018-15687 George McCollister
@ 2019-02-25 16:37 ` George McCollister
  2019-02-25 16:37 ` [sumo][PATCH 4/8] systemd: Security fix CVE-2018-16864 George McCollister
                   ` (4 subsequent siblings)
  7 siblings, 0 replies; 9+ messages in thread
From: George McCollister @ 2019-02-25 16:37 UTC (permalink / raw)
  To: openembedded-core

From: Chen Qi <Qi.Chen@windriver.com>

Backport patch to fix the following CVE.

CVE: CVE-2018-15688

Signed-off-by: Chen Qi <Qi.Chen@windriver.com>
Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>

Cherry-picked from thud 13591d7224393dc0ae529a03cdf74aceb3540ce9

Signed-off-by: George McCollister <george.mccollister@gmail.com>
---
 ...sure-we-have-enough-space-for-the-DHCP6-o.patch | 39 ++++++++++++++++++++++
 meta/recipes-core/systemd/systemd_237.bb           |  1 +
 2 files changed, 40 insertions(+)
 create mode 100644 meta/recipes-core/systemd/systemd/0001-dhcp6-make-sure-we-have-enough-space-for-the-DHCP6-o.patch

diff --git a/meta/recipes-core/systemd/systemd/0001-dhcp6-make-sure-we-have-enough-space-for-the-DHCP6-o.patch b/meta/recipes-core/systemd/systemd/0001-dhcp6-make-sure-we-have-enough-space-for-the-DHCP6-o.patch
new file mode 100644
index 0000000000..0c912f25df
--- /dev/null
+++ b/meta/recipes-core/systemd/systemd/0001-dhcp6-make-sure-we-have-enough-space-for-the-DHCP6-o.patch
@@ -0,0 +1,39 @@
+From a2622b8398ba026faf481f5eddeb53231d9de4a7 Mon Sep 17 00:00:00 2001
+From: Lennart Poettering <lennart@poettering.net>
+Date: Fri, 19 Oct 2018 12:12:33 +0200
+Subject: [PATCH] dhcp6: make sure we have enough space for the DHCP6 option
+ header
+
+Fixes a vulnerability originally discovered by Felix Wilhelm from
+Google.
+
+CVE-2018-15688
+LP: #1795921
+https://bugzilla.redhat.com/show_bug.cgi?id=1639067
+
+(cherry picked from commit 4dac5eaba4e419b29c97da38a8b1f82336c2c892)
+
+CVE: CVE-2018-15688
+Upstream-Status: Backport
+
+Signed-off-by: Chen Qi <Qi.Chen@windriver.com>
+---
+ src/libsystemd-network/dhcp6-option.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/src/libsystemd-network/dhcp6-option.c b/src/libsystemd-network/dhcp6-option.c
+index c4b402b..dcbaad0 100644
+--- a/src/libsystemd-network/dhcp6-option.c
++++ b/src/libsystemd-network/dhcp6-option.c
+@@ -103,7 +103,7 @@ int dhcp6_option_append_ia(uint8_t **buf, size_t *buflen, DHCP6IA *ia) {
+                 return -EINVAL;
+         }
+ 
+-        if (*buflen < len)
++        if (*buflen < offsetof(DHCP6Option, data) + len)
+                 return -ENOBUFS;
+ 
+         ia_hdr = *buf;
+-- 
+2.7.4
+
diff --git a/meta/recipes-core/systemd/systemd_237.bb b/meta/recipes-core/systemd/systemd_237.bb
index cae9bccc60..87793dd3af 100644
--- a/meta/recipes-core/systemd/systemd_237.bb
+++ b/meta/recipes-core/systemd/systemd_237.bb
@@ -56,6 +56,7 @@ SRC_URI += "file://touchscreen.rules \
            file://0035-Define-glibc-compatible-basename-for-non-glibc-syste.patch \
            file://0001-core-when-deserializing-state-always-use-read_line-L.patch \
            file://0001-chown-recursive-let-s-rework-the-recursive-logic-to-.patch \
+           file://0001-dhcp6-make-sure-we-have-enough-space-for-the-DHCP6-o.patch \
            "
 SRC_URI_append_qemuall = " file://0001-core-device.c-Change-the-default-device-timeout-to-2.patch"
 
-- 
2.11.0



^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [sumo][PATCH 4/8] systemd: Security fix CVE-2018-16864
  2019-02-25 16:37 [sumo][PATCH 0/8] systemd: fix CVEs George McCollister
                   ` (2 preceding siblings ...)
  2019-02-25 16:37 ` [sumo][PATCH 3/8] systemd: fix CVE-2018-15688 George McCollister
@ 2019-02-25 16:37 ` George McCollister
  2019-02-25 16:37 ` [sumo][PATCH 5/8] systemd: Security fix CVE-2018-16865 George McCollister
                   ` (3 subsequent siblings)
  7 siblings, 0 replies; 9+ messages in thread
From: George McCollister @ 2019-02-25 16:37 UTC (permalink / raw)
  To: openembedded-core

Affects < v240

Based on thud commit 403e74b07b6f3c4a2444e68c74a8434fb17aee49
The patch in the thud commit doesn't compile against 237. Use the
version of this patch, CVE-2018-16864.patch from
systemd_237-3ubuntu10.13.debian.

Signed-off-by: George McCollister <george.mccollister@gmail.com>
---
 ...-not-store-the-iovec-entry-for-process-co.patch | 193 +++++++++++++++++++++
 meta/recipes-core/systemd/systemd_237.bb           |   1 +
 2 files changed, 194 insertions(+)
 create mode 100644 meta/recipes-core/systemd/systemd/0024-journald-do-not-store-the-iovec-entry-for-process-co.patch

diff --git a/meta/recipes-core/systemd/systemd/0024-journald-do-not-store-the-iovec-entry-for-process-co.patch b/meta/recipes-core/systemd/systemd/0024-journald-do-not-store-the-iovec-entry-for-process-co.patch
new file mode 100644
index 0000000000..c0b0667d85
--- /dev/null
+++ b/meta/recipes-core/systemd/systemd/0024-journald-do-not-store-the-iovec-entry-for-process-co.patch
@@ -0,0 +1,193 @@
+From c29b44cb90e2cc521533e6169cf847553ebefd81 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= <zbyszek@in.waw.pl>
+Date: Wed, 5 Dec 2018 18:38:39 +0100
+Subject: [PATCH] journald: do not store the iovec entry for process
+ commandline on stack
+
+This fixes a crash where we would read the commandline, whose length is under
+control of the sending program, and then crash when trying to create a stack
+allocation for it.
+
+CVE-2018-16864
+https://bugzilla.redhat.com/show_bug.cgi?id=1653855
+
+The message actually doesn't get written to disk, because
+journal_file_append_entry() returns -E2BIG.
+
+Patch for 237 from:
+systemd_237-3ubuntu10.13.debian CVE-2018-16864.patch
+
+CVE: CVE-2018-16864
+Upstream-Status: Backport
+
+---
+ src/basic/io-util.c           | 10 ++++++++++
+ src/basic/io-util.h           |  2 ++
+ src/coredump/coredump.c       | 31 +++++++++++--------------------
+ src/journal/journald-server.c | 25 +++++++++++++++----------
+ 4 files changed, 38 insertions(+), 30 deletions(-)
+
+--- a/src/basic/io-util.c
++++ b/src/basic/io-util.c
+@@ -26,6 +26,7 @@
+ #include <unistd.h>
+ 
+ #include "io-util.h"
++#include "string-util.h"
+ #include "time-util.h"
+ 
+ int flush_fd(int fd) {
+@@ -270,3 +271,12 @@
+ 
+         return q - (const uint8_t*) p;
+ }
++
++char* set_iovec_string_field(struct iovec *iovec, size_t *n_iovec, const char *field, const char *value) {
++        char *x;
++
++        x = strappend(field, value);
++        if (x)
++                iovec[(*n_iovec)++] = IOVEC_MAKE_STRING(x);
++        return x;
++}
+--- a/src/basic/io-util.h
++++ b/src/basic/io-util.h
+@@ -91,3 +91,5 @@
+ #define IOVEC_MAKE(base, len) (struct iovec) IOVEC_INIT(base, len)
+ #define IOVEC_INIT_STRING(string) IOVEC_INIT((char*) string, strlen(string))
+ #define IOVEC_MAKE_STRING(string) (struct iovec) IOVEC_INIT_STRING(string)
++
++char* set_iovec_string_field(struct iovec *iovec, size_t *n_iovec, const char *field, const char *value);
+--- a/src/coredump/coredump.c
++++ b/src/coredump/coredump.c
+@@ -1067,19 +1067,10 @@
+         return 0;
+ }
+ 
+-static char* set_iovec_field(struct iovec iovec[27], size_t *n_iovec, const char *field, const char *value) {
+-        char *x;
+-
+-        x = strappend(field, value);
+-        if (x)
+-                iovec[(*n_iovec)++] = IOVEC_MAKE_STRING(x);
+-        return x;
+-}
+-
+ static char* set_iovec_field_free(struct iovec iovec[27], size_t *n_iovec, const char *field, char *value) {
+         char *x;
+ 
+-        x = set_iovec_field(iovec, n_iovec, field, value);
++        x = set_iovec_string_field(iovec, n_iovec, field, value);
+         free(value);
+         return x;
+ }
+@@ -1129,33 +1120,33 @@
+                         disable_coredumps();
+                 }
+ 
+-                set_iovec_field(iovec, n_iovec, "COREDUMP_UNIT=", context[CONTEXT_UNIT]);
++                set_iovec_string_field(iovec, n_iovec, "COREDUMP_UNIT=", context[CONTEXT_UNIT]);
+         }
+ 
+         if (cg_pid_get_user_unit(pid, &t) >= 0)
+                 set_iovec_field_free(iovec, n_iovec, "COREDUMP_USER_UNIT=", t);
+ 
+         /* The next few are mandatory */
+-        if (!set_iovec_field(iovec, n_iovec, "COREDUMP_PID=", context[CONTEXT_PID]))
++        if (!set_iovec_string_field(iovec, n_iovec, "COREDUMP_PID=", context[CONTEXT_PID]))
+                 return log_oom();
+ 
+-        if (!set_iovec_field(iovec, n_iovec, "COREDUMP_UID=", context[CONTEXT_UID]))
++        if (!set_iovec_string_field(iovec, n_iovec, "COREDUMP_UID=", context[CONTEXT_UID]))
+                 return log_oom();
+ 
+-        if (!set_iovec_field(iovec, n_iovec, "COREDUMP_GID=", context[CONTEXT_GID]))
++        if (!set_iovec_string_field(iovec, n_iovec, "COREDUMP_GID=", context[CONTEXT_GID]))
+                 return log_oom();
+ 
+-        if (!set_iovec_field(iovec, n_iovec, "COREDUMP_SIGNAL=", context[CONTEXT_SIGNAL]))
++        if (!set_iovec_string_field(iovec, n_iovec, "COREDUMP_SIGNAL=", context[CONTEXT_SIGNAL]))
+                 return log_oom();
+ 
+-        if (!set_iovec_field(iovec, n_iovec, "COREDUMP_RLIMIT=", context[CONTEXT_RLIMIT]))
++        if (!set_iovec_string_field(iovec, n_iovec, "COREDUMP_RLIMIT=", context[CONTEXT_RLIMIT]))
+                 return log_oom();
+ 
+-        if (!set_iovec_field(iovec, n_iovec, "COREDUMP_COMM=", context[CONTEXT_COMM]))
++        if (!set_iovec_string_field(iovec, n_iovec, "COREDUMP_COMM=", context[CONTEXT_COMM]))
+                 return log_oom();
+ 
+         if (context[CONTEXT_EXE] &&
+-            !set_iovec_field(iovec, n_iovec, "COREDUMP_EXE=", context[CONTEXT_EXE]))
++            !set_iovec_string_field(iovec, n_iovec, "COREDUMP_EXE=", context[CONTEXT_EXE]))
+                 return log_oom();
+ 
+         if (sd_pid_get_session(pid, &t) >= 0)
+@@ -1223,7 +1214,7 @@
+                 iovec[(*n_iovec)++] = IOVEC_MAKE_STRING(t);
+ 
+         if (safe_atoi(context[CONTEXT_SIGNAL], &signo) >= 0 && SIGNAL_VALID(signo))
+-                set_iovec_field(iovec, n_iovec, "COREDUMP_SIGNAL_NAME=SIG", signal_to_string(signo));
++                set_iovec_string_field(iovec, n_iovec, "COREDUMP_SIGNAL_NAME=SIG", signal_to_string(signo));
+ 
+         return 0; /* we successfully acquired all metadata */
+ }
+--- a/src/journal/journald-server.c
++++ b/src/journal/journald-server.c
+@@ -769,6 +769,7 @@
+                 pid_t object_pid) {
+ 
+         char source_time[sizeof("_SOURCE_REALTIME_TIMESTAMP=") + DECIMAL_STR_MAX(usec_t)];
++        _cleanup_free_ char *cmdline1 = NULL, *cmdline2 = NULL;
+         uid_t journal_uid;
+         ClientContext *o;
+ 
+@@ -785,20 +786,23 @@
+                 IOVEC_ADD_NUMERIC_FIELD(iovec, n, c->uid, uid_t, uid_is_valid, UID_FMT, "_UID");
+                 IOVEC_ADD_NUMERIC_FIELD(iovec, n, c->gid, gid_t, gid_is_valid, GID_FMT, "_GID");
+ 
+-                IOVEC_ADD_STRING_FIELD(iovec, n, c->comm, "_COMM");
+-                IOVEC_ADD_STRING_FIELD(iovec, n, c->exe, "_EXE");
+-                IOVEC_ADD_STRING_FIELD(iovec, n, c->cmdline, "_CMDLINE");
+-                IOVEC_ADD_STRING_FIELD(iovec, n, c->capeff, "_CAP_EFFECTIVE");
++                IOVEC_ADD_STRING_FIELD(iovec, n, c->comm, "_COMM"); /* At most TASK_COMM_LENGTH (16 bytes) */
++                IOVEC_ADD_STRING_FIELD(iovec, n, c->exe, "_EXE"); /* A path, so at most PATH_MAX (4096 bytes) */
+ 
+-                IOVEC_ADD_SIZED_FIELD(iovec, n, c->label, c->label_size, "_SELINUX_CONTEXT");
++                if (c->cmdline)
++                        /* At most _SC_ARG_MAX (2MB usually), which is too much to put on stack.
++                         * Let's use a heap allocation for this one. */
++                        cmdline1 = set_iovec_string_field(iovec, &n, "_CMDLINE=", c->cmdline);
+ 
++                IOVEC_ADD_STRING_FIELD(iovec, n, c->capeff, "_CAP_EFFECTIVE"); /* Read from /proc/.../status */
++                IOVEC_ADD_SIZED_FIELD(iovec, n, c->label, c->label_size, "_SELINUX_CONTEXT");
+                 IOVEC_ADD_NUMERIC_FIELD(iovec, n, c->auditid, uint32_t, audit_session_is_valid, "%" PRIu32, "_AUDIT_SESSION");
+                 IOVEC_ADD_NUMERIC_FIELD(iovec, n, c->loginuid, uid_t, uid_is_valid, UID_FMT, "_AUDIT_LOGINUID");
+ 
+-                IOVEC_ADD_STRING_FIELD(iovec, n, c->cgroup, "_SYSTEMD_CGROUP");
++                IOVEC_ADD_STRING_FIELD(iovec, n, c->cgroup, "_SYSTEMD_CGROUP"); /* A path */
+                 IOVEC_ADD_STRING_FIELD(iovec, n, c->session, "_SYSTEMD_SESSION");
+                 IOVEC_ADD_NUMERIC_FIELD(iovec, n, c->owner_uid, uid_t, uid_is_valid, UID_FMT, "_SYSTEMD_OWNER_UID");
+-                IOVEC_ADD_STRING_FIELD(iovec, n, c->unit, "_SYSTEMD_UNIT");
++                IOVEC_ADD_STRING_FIELD(iovec, n, c->unit, "_SYSTEMD_UNIT"); /* Unit names are bounded by UNIT_NAME_MAX */
+                 IOVEC_ADD_STRING_FIELD(iovec, n, c->user_unit, "_SYSTEMD_USER_UNIT");
+                 IOVEC_ADD_STRING_FIELD(iovec, n, c->slice, "_SYSTEMD_SLICE");
+                 IOVEC_ADD_STRING_FIELD(iovec, n, c->user_slice, "_SYSTEMD_USER_SLICE");
+@@ -819,13 +823,14 @@
+                 IOVEC_ADD_NUMERIC_FIELD(iovec, n, o->uid, uid_t, uid_is_valid, UID_FMT, "OBJECT_UID");
+                 IOVEC_ADD_NUMERIC_FIELD(iovec, n, o->gid, gid_t, gid_is_valid, GID_FMT, "OBJECT_GID");
+ 
++                /* See above for size limits, only ->cmdline may be large, so use a heap allocation for it. */
+                 IOVEC_ADD_STRING_FIELD(iovec, n, o->comm, "OBJECT_COMM");
+                 IOVEC_ADD_STRING_FIELD(iovec, n, o->exe, "OBJECT_EXE");
+-                IOVEC_ADD_STRING_FIELD(iovec, n, o->cmdline, "OBJECT_CMDLINE");
+-                IOVEC_ADD_STRING_FIELD(iovec, n, o->capeff, "OBJECT_CAP_EFFECTIVE");
++                if (o->cmdline)
++                        cmdline2 = set_iovec_string_field(iovec, &n, "OBJECT_CMDLINE=", o->cmdline);
+ 
++                IOVEC_ADD_STRING_FIELD(iovec, n, o->capeff, "OBJECT_CAP_EFFECTIVE");
+                 IOVEC_ADD_SIZED_FIELD(iovec, n, o->label, o->label_size, "OBJECT_SELINUX_CONTEXT");
+-
+                 IOVEC_ADD_NUMERIC_FIELD(iovec, n, o->auditid, uint32_t, audit_session_is_valid, "%" PRIu32, "OBJECT_AUDIT_SESSION");
+                 IOVEC_ADD_NUMERIC_FIELD(iovec, n, o->loginuid, uid_t, uid_is_valid, UID_FMT, "OBJECT_AUDIT_LOGINUID");
+ 
diff --git a/meta/recipes-core/systemd/systemd_237.bb b/meta/recipes-core/systemd/systemd_237.bb
index 87793dd3af..0cb84c002f 100644
--- a/meta/recipes-core/systemd/systemd_237.bb
+++ b/meta/recipes-core/systemd/systemd_237.bb
@@ -57,6 +57,7 @@ SRC_URI += "file://touchscreen.rules \
            file://0001-core-when-deserializing-state-always-use-read_line-L.patch \
            file://0001-chown-recursive-let-s-rework-the-recursive-logic-to-.patch \
            file://0001-dhcp6-make-sure-we-have-enough-space-for-the-DHCP6-o.patch \
+           file://0024-journald-do-not-store-the-iovec-entry-for-process-co.patch \
            "
 SRC_URI_append_qemuall = " file://0001-core-device.c-Change-the-default-device-timeout-to-2.patch"
 
-- 
2.11.0



^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [sumo][PATCH 5/8] systemd: Security fix CVE-2018-16865
  2019-02-25 16:37 [sumo][PATCH 0/8] systemd: fix CVEs George McCollister
                   ` (3 preceding siblings ...)
  2019-02-25 16:37 ` [sumo][PATCH 4/8] systemd: Security fix CVE-2018-16864 George McCollister
@ 2019-02-25 16:37 ` George McCollister
  2019-02-25 16:37 ` [sumo][PATCH 6/8] systemd: Security fix CVE-2018-16866 George McCollister
                   ` (2 subsequent siblings)
  7 siblings, 0 replies; 9+ messages in thread
From: George McCollister @ 2019-02-25 16:37 UTC (permalink / raw)
  To: openembedded-core

Affects < v240

Based on thud commit d5d2b821fc85b8cf39f683061ac2a45bddd2139f
The second patch in the thud commit doesn't apply against 237. Use the
version of the second patch CVE-2018-16865_2.patch from
systemd_237-3ubuntu10.13.debian.

Signed-off-by: George McCollister <george.mccollister@gmail.com>
---
 ...ld-set-a-limit-on-the-number-of-fields-1k.patch | 60 ++++++++++++++++
 ...ote-set-a-limit-on-the-number-of-fields-i.patch | 79 ++++++++++++++++++++++
 meta/recipes-core/systemd/systemd_237.bb           |  2 +
 3 files changed, 141 insertions(+)
 create mode 100644 meta/recipes-core/systemd/systemd/0025-journald-set-a-limit-on-the-number-of-fields-1k.patch
 create mode 100644 meta/recipes-core/systemd/systemd/0026-journal-remote-set-a-limit-on-the-number-of-fields-i.patch

diff --git a/meta/recipes-core/systemd/systemd/0025-journald-set-a-limit-on-the-number-of-fields-1k.patch b/meta/recipes-core/systemd/systemd/0025-journald-set-a-limit-on-the-number-of-fields-1k.patch
new file mode 100644
index 0000000000..e8a6f2b986
--- /dev/null
+++ b/meta/recipes-core/systemd/systemd/0025-journald-set-a-limit-on-the-number-of-fields-1k.patch
@@ -0,0 +1,60 @@
+From 4566aaf97f5b4143b930d75628f3abc905249dcd Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= <zbyszek@in.waw.pl>
+Date: Wed, 5 Dec 2018 22:45:02 +0100
+Subject: [PATCH] journald: set a limit on the number of fields (1k)
+
+We allocate a iovec entry for each field, so with many short entries,
+our memory usage and processing time can be large, even with a relatively
+small message size. Let's refuse overly long entries.
+
+CVE-2018-16865
+https://bugzilla.redhat.com/show_bug.cgi?id=1653861
+
+What from I can see, the problem is not from an alloca, despite what the CVE
+description says, but from the attack multiplication that comes from creating
+many very small iovecs: (void* + size_t) for each three bytes of input message.
+
+Patch backported from systemd master at
+052c57f132f04a3cf4148f87561618da1a6908b4.
+
+CVE: CVE-2018-16865
+Upstream-Status: Backport
+
+---
+ src/basic/journal-importer.h  | 3 +++
+ src/journal/journald-native.c | 5 +++++
+ 2 files changed, 8 insertions(+)
+
+diff --git a/src/basic/journal-importer.h b/src/basic/journal-importer.h
+index f49ce734a1..c4ae45d32d 100644
+--- a/src/basic/journal-importer.h
++++ b/src/basic/journal-importer.h
+@@ -16,6 +16,9 @@
+ #define DATA_SIZE_MAX (1024*1024*768u)
+ #define LINE_CHUNK 8*1024u
+ 
++/* The maximum number of fields in an entry */
++#define ENTRY_FIELD_COUNT_MAX 1024
++
+ struct iovec_wrapper {
+         struct iovec *iovec;
+         size_t size_bytes;
+diff --git a/src/journal/journald-native.c b/src/journal/journald-native.c
+index 5ff22a10af..951d092053 100644
+--- a/src/journal/journald-native.c
++++ b/src/journal/journald-native.c
+@@ -140,6 +140,11 @@ static int server_process_entry(
+                 }
+ 
+                 /* A property follows */
++                if (n > ENTRY_FIELD_COUNT_MAX) {
++                        log_debug("Received an entry that has more than " STRINGIFY(ENTRY_FIELD_COUNT_MAX) " fields, ignoring entry.");
++                        r = 1;
++                        goto finish;
++                }
+ 
+                 /* n existing properties, 1 new, +1 for _TRANSPORT */
+                 if (!GREEDY_REALLOC(iovec, m,
+-- 
+2.11.0
+
diff --git a/meta/recipes-core/systemd/systemd/0026-journal-remote-set-a-limit-on-the-number-of-fields-i.patch b/meta/recipes-core/systemd/systemd/0026-journal-remote-set-a-limit-on-the-number-of-fields-i.patch
new file mode 100644
index 0000000000..f297333e72
--- /dev/null
+++ b/meta/recipes-core/systemd/systemd/0026-journal-remote-set-a-limit-on-the-number-of-fields-i.patch
@@ -0,0 +1,79 @@
+From ce1475b4f69f0a4382c6190f55e080d91de84611 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= <zbyszek@in.waw.pl>
+Date: Fri, 7 Dec 2018 10:48:10 +0100
+Subject: [PATCH] journal-remote: set a limit on the number of fields in a
+ message
+
+Existing use of E2BIG is replaced with ENOBUFS (entry too long), and E2BIG is
+reused for the new error condition (too many fields).
+
+This matches the change done for systemd-journald, hence forming the second
+part of the fix for CVE-2018-16865
+(https://bugzilla.redhat.com/show_bug.cgi?id=1653861).
+
+Patch backported from systemd master at
+ef4d6abe7c7fab6cbff975b32e76b09feee56074.
+
+Patch for 237 from:
+systemd_237-3ubuntu10.13.debian CVE-2018-16865_2.patch
+
+CVE: CVE-2018-16865
+Upstream-Status: Backport
+
+---
+ src/journal-remote/journal-remote-main.c | 7 +++++--
+ src/journal-remote/journal-remote.c      | 3 +++
+ src/shared/journal-importer.c            | 5 ++++-
+ 3 files changed, 12 insertions(+), 3 deletions(-)
+
+--- a/src/basic/journal-importer.c
++++ b/src/basic/journal-importer.c
+@@ -38,6 +38,9 @@
+ };
+ 
+ static int iovw_put(struct iovec_wrapper *iovw, void* data, size_t len) {
++        if (iovw->count >= ENTRY_FIELD_COUNT_MAX)
++                return -E2BIG;
++
+         if (!GREEDY_REALLOC(iovw->iovec, iovw->size_bytes, iovw->count + 1))
+                 return log_oom();
+ 
+@@ -113,7 +116,7 @@
+                 imp->scanned = imp->filled;
+                 if (imp->scanned >= DATA_SIZE_MAX) {
+                         log_error("Entry is bigger than %u bytes.", DATA_SIZE_MAX);
+-                        return -E2BIG;
++                        return -ENOBUFS;
+                 }
+ 
+                 if (imp->passive_fd)
+--- a/src/journal-remote/journal-remote.c
++++ b/src/journal-remote/journal-remote.c
+@@ -517,10 +517,16 @@
+                         break;
+                 else if (r < 0) {
+                         log_warning("Failed to process data for connection %p", connection);
+-                        if (r == -E2BIG)
++                        if (r == -ENOBUFS)
+                                 return mhd_respondf(connection,
+                                                     r, MHD_HTTP_PAYLOAD_TOO_LARGE,
+                                                     "Entry is too large, maximum is " STRINGIFY(DATA_SIZE_MAX) " bytes.");
++
++                        else if (r == -E2BIG)
++                                return mhd_respondf(connection,
++                                                    r, MHD_HTTP_REQUEST_ENTITY_TOO_LARGE,
++                                                    "Entry with more fields than the maximum of " STRINGIFY(ENTRY_FIELD_COUNT_MAX) ".");
++
+                         else
+                                 return mhd_respondf(connection,
+                                                     r, MHD_HTTP_UNPROCESSABLE_ENTITY,
+@@ -1090,6 +1096,9 @@
+                 log_debug("%zu active sources remaining", s->active);
+                 return 0;
+         } else if (r == -E2BIG) {
++                log_notice("Entry with too many fields, skipped");
++                return 1;
++        } else if (r == -ENOBUFS) {
+                 log_notice_errno(E2BIG, "Entry too big, skipped");
+                 return 1;
+         } else if (r == -EAGAIN) {
diff --git a/meta/recipes-core/systemd/systemd_237.bb b/meta/recipes-core/systemd/systemd_237.bb
index 0cb84c002f..e6ef385f52 100644
--- a/meta/recipes-core/systemd/systemd_237.bb
+++ b/meta/recipes-core/systemd/systemd_237.bb
@@ -58,6 +58,8 @@ SRC_URI += "file://touchscreen.rules \
            file://0001-chown-recursive-let-s-rework-the-recursive-logic-to-.patch \
            file://0001-dhcp6-make-sure-we-have-enough-space-for-the-DHCP6-o.patch \
            file://0024-journald-do-not-store-the-iovec-entry-for-process-co.patch \
+           file://0025-journald-set-a-limit-on-the-number-of-fields-1k.patch \
+           file://0026-journal-remote-set-a-limit-on-the-number-of-fields-i.patch \
            "
 SRC_URI_append_qemuall = " file://0001-core-device.c-Change-the-default-device-timeout-to-2.patch"
 
-- 
2.11.0



^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [sumo][PATCH 6/8] systemd: Security fix CVE-2018-16866
  2019-02-25 16:37 [sumo][PATCH 0/8] systemd: fix CVEs George McCollister
                   ` (4 preceding siblings ...)
  2019-02-25 16:37 ` [sumo][PATCH 5/8] systemd: Security fix CVE-2018-16865 George McCollister
@ 2019-02-25 16:37 ` George McCollister
  2019-02-25 16:37 ` [sumo][PATCH 7/8] systemd: fix CVE-2018-6954 George McCollister
  2019-02-25 16:37 ` [sumo][PATCH 8/8] systemd: fix CVE-2019-6454 George McCollister
  7 siblings, 0 replies; 9+ messages in thread
From: George McCollister @ 2019-02-25 16:37 UTC (permalink / raw)
  To: openembedded-core

From: Marcus Cooper <marcus.cooper@axis.com>

Affects < v240

Signed-off-by: Marcus Cooper <marcusc@axis.com>

From v2 patch on openembedded-core@lists.openembedded.org
Incresed file name number from 0026 to 0027.

Signed-off-by: George McCollister <george.mccollister@gmail.com>
---
 ...nal-fix-out-of-bounds-read-CVE-2018-16866.patch | 49 ++++++++++++++++++++++
 meta/recipes-core/systemd/systemd_237.bb           |  1 +
 2 files changed, 50 insertions(+)
 create mode 100644 meta/recipes-core/systemd/systemd/0027-journal-fix-out-of-bounds-read-CVE-2018-16866.patch

diff --git a/meta/recipes-core/systemd/systemd/0027-journal-fix-out-of-bounds-read-CVE-2018-16866.patch b/meta/recipes-core/systemd/systemd/0027-journal-fix-out-of-bounds-read-CVE-2018-16866.patch
new file mode 100644
index 0000000000..3925a4abbb
--- /dev/null
+++ b/meta/recipes-core/systemd/systemd/0027-journal-fix-out-of-bounds-read-CVE-2018-16866.patch
@@ -0,0 +1,49 @@
+From ebd06c37d4311db9851f4d3fdd023de3dd590de0 Mon Sep 17 00:00:00 2001
+From: Filipe Brandenburger <filbranden@google.com>
+Date: Thu, 10 Jan 2019 14:53:33 -0800
+Subject: [PATCH] journal: fix out-of-bounds read CVE-2018-16866
+
+The original code didn't account for the fact that strchr() would match on the
+'\0' character, making it read past the end of the buffer if no non-whitespace
+character was present.
+
+This bug was introduced in commit ec5ff4445cca6a which was first released in
+systemd v221 and later fixed in commit 8595102d3ddde6 which was released in
+v240, so versions in the range [v221, v240) are affected.
+
+Patch backported from systemd-stable at f005e73d3723d62a39be661931fcb6347119b52b
+also includes a change from systemd master which removes a heap buffer overflow
+a6aadf4ae0bae185dc4c414d492a4a781c80ffe5.
+
+CVE: CVE-2018-16866
+Upstream-Status: Backport
+Signed-off-by: Marcus Cooper <marcusc@axis.com>
+---
+ src/journal/journald-syslog.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/src/journal/journald-syslog.c b/src/journal/journald-syslog.c
+index 9dea116722..809b318c06 100644
+--- a/src/journal/journald-syslog.c
++++ b/src/journal/journald-syslog.c
+@@ -194,7 +194,7 @@ size_t syslog_parse_identifier(const char **buf, char **identifier, char **pid)
+         e = l;
+         l--;
+ 
+-        if (p[l-1] == ']') {
++        if (l > 0 && p[l-1] == ']') {
+                 size_t k = l-1;
+ 
+                 for (;;) {
+@@ -219,7 +219,7 @@ size_t syslog_parse_identifier(const char **buf, char **identifier, char **pid)
+         if (t)
+                 *identifier = t;
+ 
+-        if (strchr(WHITESPACE, p[e]))
++        if (p[e] != '\0' && strchr(WHITESPACE, p[e]))
+                 e++;
+         *buf = p + e;
+         return e;
+-- 
+2.11.0
+
diff --git a/meta/recipes-core/systemd/systemd_237.bb b/meta/recipes-core/systemd/systemd_237.bb
index e6ef385f52..b53221896f 100644
--- a/meta/recipes-core/systemd/systemd_237.bb
+++ b/meta/recipes-core/systemd/systemd_237.bb
@@ -60,6 +60,7 @@ SRC_URI += "file://touchscreen.rules \
            file://0024-journald-do-not-store-the-iovec-entry-for-process-co.patch \
            file://0025-journald-set-a-limit-on-the-number-of-fields-1k.patch \
            file://0026-journal-remote-set-a-limit-on-the-number-of-fields-i.patch \
+           file://0027-journal-fix-out-of-bounds-read-CVE-2018-16866.patch \
            "
 SRC_URI_append_qemuall = " file://0001-core-device.c-Change-the-default-device-timeout-to-2.patch"
 
-- 
2.11.0



^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [sumo][PATCH 7/8] systemd: fix CVE-2018-6954
  2019-02-25 16:37 [sumo][PATCH 0/8] systemd: fix CVEs George McCollister
                   ` (5 preceding siblings ...)
  2019-02-25 16:37 ` [sumo][PATCH 6/8] systemd: Security fix CVE-2018-16866 George McCollister
@ 2019-02-25 16:37 ` George McCollister
  2019-02-25 16:37 ` [sumo][PATCH 8/8] systemd: fix CVE-2019-6454 George McCollister
  7 siblings, 0 replies; 9+ messages in thread
From: George McCollister @ 2019-02-25 16:37 UTC (permalink / raw)
  To: openembedded-core

Apply patches to fix CVE-2018-6954

NVD description from https://nvd.nist.gov/vuln/detail/CVE-2018-6954

systemd-tmpfiles in systemd through 237 mishandles symlinks present in
non-terminal path components, which allows local users to obtain
ownership of arbitrary files via vectors involving creation of a
directory and a file under that directory, and later replacing that
directory with a symlink. This occurs even if the fs.protected_symlinks
sysctl is turned on.

Patches from systemd_237-3ubuntu10.13.debian.

These patches shouldn't be required on newer OE releases since they use
systemd v239 or higher.

Signed-off-by: George McCollister <george.mccollister@gmail.com>
---
 ...n-t-resolve-pathnames-when-traversing-rec.patch |  643 +++++++
 .../systemd/systemd/0002-Make-tmpfiles-safe.patch  | 1828 ++++++++++++++++++++
 meta/recipes-core/systemd/systemd_237.bb           |    2 +
 3 files changed, 2473 insertions(+)
 create mode 100644 meta/recipes-core/systemd/systemd/0001-tmpfiles-don-t-resolve-pathnames-when-traversing-rec.patch
 create mode 100644 meta/recipes-core/systemd/systemd/0002-Make-tmpfiles-safe.patch

diff --git a/meta/recipes-core/systemd/systemd/0001-tmpfiles-don-t-resolve-pathnames-when-traversing-rec.patch b/meta/recipes-core/systemd/systemd/0001-tmpfiles-don-t-resolve-pathnames-when-traversing-rec.patch
new file mode 100644
index 0000000000..108e4ad8b8
--- /dev/null
+++ b/meta/recipes-core/systemd/systemd/0001-tmpfiles-don-t-resolve-pathnames-when-traversing-rec.patch
@@ -0,0 +1,643 @@
+From 33dc9a280f952f503e5493ee29f6815bef29d551 Mon Sep 17 00:00:00 2001
+From: Franck Bui <fbui@suse.com>
+Date: Fri, 2 Mar 2018 17:19:32 +0100
+Subject: [PATCH] tmpfiles: don't resolve pathnames when traversing recursively
+ through directory trees
+
+Otherwise we can be fooled if one path component is replaced underneath us.
+
+The patch achieves that by always operating at file descriptor level (by using
+*at() helpers) and by making sure we do not any path resolution when traversing
+direcotry trees.
+
+However this is not always possible, for instance when listing the content of a
+directory or some operations don't provide the *at() helpers or others (such as
+fchmodat()) don't have the AT_EMPTY_PATH flag. In such cases we operate on
+/proc/self/fd/%i pseudo-symlink instead, which works the same for all kinds of
+objects and requires no checking of type beforehand.
+
+Also O_PATH flag is used when opening file objects in order to prevent
+undesired behaviors: device nodes from reacting, automounts from
+triggering, etc...
+
+Fixes: CVE-2018-6954
+
+Origin: upstream, https://github.com/systemd/systemd/commit/936f6bdb803c432578e2cdcc5f93f3bfff93aff0
+Bug: https://github.com/systemd/systemd/issues/7986
+
+Patch from:
+systemd_237-3ubuntu10.13.debian CVE-2018-6954.patch
+
+https://usn.ubuntu.com/3816-1/ states that CVE-2018-6954 doesn't
+affect Ubuntu 18.10 which uses the same version of systemd as thud
+(239).
+
+CVE: CVE-2018-6954
+Upstream-Status: Backport
+
+Signed-off-by: George McCollister <george.mccollister@gmail.com>
+---
+ src/tmpfiles/tmpfiles.c | 363 +++++++++++++++++++++++++++++++-----------------
+ 1 file changed, 239 insertions(+), 124 deletions(-)
+
+diff --git a/src/tmpfiles/tmpfiles.c b/src/tmpfiles/tmpfiles.c
+index 88cc543f09..613d418eb3 100644
+--- a/src/tmpfiles/tmpfiles.c
++++ b/src/tmpfiles/tmpfiles.c
+@@ -792,94 +792,105 @@ static bool hardlink_vulnerable(struct stat *st) {
+         return !S_ISDIR(st->st_mode) && st->st_nlink > 1 && dangerous_hardlinks();
+ }
+ 
+-static int path_set_perms(Item *i, const char *path) {
+-        char fn[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)];
+-        _cleanup_close_ int fd = -1;
+-        struct stat st;
++static int fd_set_perms(Item *i, int fd, const struct stat *st) {
++        _cleanup_free_ char *path = NULL;
++        int r;
+ 
+         assert(i);
+-        assert(path);
+-
+-        if (!i->mode_set && !i->uid_set && !i->gid_set)
+-                goto shortcut;
+-
+-        /* We open the file with O_PATH here, to make the operation
+-         * somewhat atomic. Also there's unfortunately no fchmodat()
+-         * with AT_SYMLINK_NOFOLLOW, hence we emulate it here via
+-         * O_PATH. */
+-
+-        fd = open(path, O_NOFOLLOW|O_CLOEXEC|O_PATH);
+-        if (fd < 0) {
+-                int level = LOG_ERR, r = -errno;
++        assert(fd);
+ 
+-                /* Option "e" operates only on existing objects. Do not
+-                 * print errors about non-existent files or directories */
+-                if (i->type == EMPTY_DIRECTORY && errno == ENOENT) {
+-                        level = LOG_DEBUG;
+-                        r = 0;
+-                }
+-
+-                log_full_errno(level, errno, "Adjusting owner and mode for %s failed: %m", path);
++        r = fd_get_path(fd, &path);
++        if (r < 0)
+                 return r;
+-        }
+ 
+-        if (fstatat(fd, "", &st, AT_EMPTY_PATH) < 0)
+-                return log_error_errno(errno, "Failed to fstat() file %s: %m", path);
++        if (!i->mode_set && !i->uid_set && !i->gid_set)
++                goto shortcut;
+ 
+-        if (hardlink_vulnerable(&st)) {
++        if (hardlink_vulnerable(st)) {
+                 log_error("Refusing to set permissions on hardlinked file %s while the fs.protected_hardlinks sysctl is turned off.", path);
+                 return -EPERM;
+         }
+ 
+-        xsprintf(fn, "/proc/self/fd/%i", fd);
+-
+         if (i->mode_set) {
+-                if (S_ISLNK(st.st_mode))
++                if (S_ISLNK(st->st_mode))
+                         log_debug("Skipping mode fix for symlink %s.", path);
+                 else {
+                         mode_t m = i->mode;
+ 
+                         if (i->mask_perms) {
+-                                if (!(st.st_mode & 0111))
++                                if (!(st->st_mode & 0111))
+                                         m &= ~0111;
+-                                if (!(st.st_mode & 0222))
++                                if (!(st->st_mode & 0222))
+                                         m &= ~0222;
+-                                if (!(st.st_mode & 0444))
++                                if (!(st->st_mode & 0444))
+                                         m &= ~0444;
+-                                if (!S_ISDIR(st.st_mode))
++                                if (!S_ISDIR(st->st_mode))
+                                         m &= ~07000; /* remove sticky/sgid/suid bit, unless directory */
+                         }
+ 
+-                        if (m == (st.st_mode & 07777))
+-                                log_debug("\"%s\" has correct mode %o already.", path, st.st_mode);
++                        if (m == (st->st_mode & 07777))
++                                log_debug("\"%s\" has correct mode %o already.", path, st->st_mode);
+                         else {
++                                char procfs_path[strlen("/proc/self/fd/") + DECIMAL_STR_MAX(int)];
++
+                                 log_debug("Changing \"%s\" to mode %o.", path, m);
+ 
+-                                if (chmod(fn, m) < 0)
+-                                        return log_error_errno(errno, "chmod() of %s via %s failed: %m", path, fn);
++                                /* fchmodat() still doesn't have AT_EMPTY_PATH flag. */
++                                xsprintf(procfs_path, "/proc/self/fd/%i", fd);
++
++                                if (chmod(procfs_path, m) < 0)
++                                        return log_error_errno(errno, "chmod() of %s via %s failed: %m", path, procfs_path);
+                         }
+                 }
+         }
+ 
+-        if ((i->uid_set && i->uid != st.st_uid) ||
+-            (i->gid_set && i->gid != st.st_gid)) {
++        if ((i->uid_set && i->uid != st->st_uid) ||
++            (i->gid_set && i->gid != st->st_gid)) {
+                 log_debug("Changing \"%s\" to owner "UID_FMT":"GID_FMT,
+                           path,
+                           i->uid_set ? i->uid : UID_INVALID,
+                           i->gid_set ? i->gid : GID_INVALID);
+ 
+-                if (chown(fn,
+-                          i->uid_set ? i->uid : UID_INVALID,
+-                          i->gid_set ? i->gid : GID_INVALID) < 0)
+-                        return log_error_errno(errno, "chown() of %s via %s failed: %m", path, fn);
++                if (fchownat(fd,
++                             "",
++                             i->uid_set ? i->uid : UID_INVALID,
++                             i->gid_set ? i->gid : GID_INVALID,
++                             AT_EMPTY_PATH) < 0)
++                        return log_error_errno(errno, "fchownat() of %s failed: %m", path);
+         }
+ 
+-        fd = safe_close(fd);
+-
+ shortcut:
+         return label_fix(path, false, false);
+ }
+ 
++static int path_set_perms(Item *i, const char *path) {
++        _cleanup_close_ int fd = -1;
++        struct stat st;
++
++        assert(i);
++        assert(path);
++
++        fd = open(path, O_NOFOLLOW|O_CLOEXEC|O_PATH);
++        if (fd < 0) {
++                int level = LOG_ERR, r = -errno;
++
++                /* Option "e" operates only on existing objects. Do not
++                 * print errors about non-existent files or directories */
++                if (i->type == EMPTY_DIRECTORY && errno == ENOENT) {
++                        level = LOG_DEBUG;
++                        r = 0;
++                }
++
++                log_full_errno(level, errno, "Adjusting owner and mode for %s failed: %m", path);
++                return r;
++        }
++
++        if (fstat(fd, &st) < 0)
++                return log_error_errno(errno, "Failed to fstat() file %s: %m", path);
++
++        return fd_set_perms(i, fd, &st);
++}
++
+ static int parse_xattrs_from_arg(Item *i) {
+         const char *p;
+         int r;
+@@ -918,21 +929,43 @@ static int parse_xattrs_from_arg(Item *i) {
+         return 0;
+ }
+ 
+-static int path_set_xattrs(Item *i, const char *path) {
++static int fd_set_xattrs(Item *i, int fd, const struct stat *st) {
++        char procfs_path[strlen("/proc/self/fd/") + DECIMAL_STR_MAX(int)];
++        _cleanup_free_ char *path = NULL;
+         char **name, **value;
++        int r;
+ 
+         assert(i);
+-        assert(path);
++        assert(fd);
++
++        r = fd_get_path(fd, &path);
++        if (r < 0)
++                return r;
++
++        xsprintf(procfs_path, "/proc/self/fd/%i", fd);
+ 
+         STRV_FOREACH_PAIR(name, value, i->xattrs) {
+                 log_debug("Setting extended attribute '%s=%s' on %s.", *name, *value, path);
+-                if (lsetxattr(path, *name, *value, strlen(*value), 0) < 0)
++                if (setxattr(procfs_path, *name, *value, strlen(*value), 0) < 0)
+                         return log_error_errno(errno, "Setting extended attribute %s=%s on %s failed: %m",
+                                                *name, *value, path);
+         }
+         return 0;
+ }
+ 
++static int path_set_xattrs(Item *i, const char *path) {
++        _cleanup_close_ int fd = -1;
++
++        assert(i);
++        assert(path);
++
++        fd = open(path, O_CLOEXEC|O_NOFOLLOW|O_PATH);
++        if (fd < 0)
++                return log_error_errno(errno, "Cannot open '%s': %m", path);
++
++        return fd_set_xattrs(i, fd, NULL);
++}
++
+ static int parse_acls_from_arg(Item *item) {
+ #if HAVE_ACL
+         int r;
+@@ -998,52 +1031,71 @@ static int path_set_acl(const char *path, const char *pretty, acl_type_t type, a
+ }
+ #endif
+ 
+-static int path_set_acls(Item *item, const char *path) {
++static int fd_set_acls(Item *item, int fd, const struct stat *st) {
+         int r = 0;
+ #if HAVE_ACL
+-        char fn[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)];
+-        _cleanup_close_ int fd = -1;
+-        struct stat st;
++        char procfs_path[strlen("/proc/self/fd/") + DECIMAL_STR_MAX(int)];
++        _cleanup_free_ char *path = NULL;
+ 
+         assert(item);
+-        assert(path);
+-
+-        fd = open(path, O_NOFOLLOW|O_CLOEXEC|O_PATH);
+-        if (fd < 0)
+-                return log_error_errno(errno, "Adjusting ACL of %s failed: %m", path);
++        assert(fd);
++        assert(st);
+ 
+-        if (fstatat(fd, "", &st, AT_EMPTY_PATH) < 0)
+-                return log_error_errno(errno, "Failed to fstat() file %s: %m", path);
++        r = fd_get_path(fd, &path);
++        if (r < 0)
++                return r;
+ 
+-        if (hardlink_vulnerable(&st)) {
++        if (hardlink_vulnerable(st)) {
+                 log_error("Refusing to set ACLs on hardlinked file %s while the fs.protected_hardlinks sysctl is turned off.", path);
+                 return -EPERM;
+         }
+ 
+-        if (S_ISLNK(st.st_mode)) {
++        if (S_ISLNK(st->st_mode)) {
+                 log_debug("Skipping ACL fix for symlink %s.", path);
+                 return 0;
+         }
+ 
+-        xsprintf(fn, "/proc/self/fd/%i", fd);
++        xsprintf(procfs_path, "/proc/self/fd/%i", fd);
+ 
+         if (item->acl_access)
+-                r = path_set_acl(fn, path, ACL_TYPE_ACCESS, item->acl_access, item->force);
++                r = path_set_acl(procfs_path, path, ACL_TYPE_ACCESS, item->acl_access, item->force);
+ 
+         if (r == 0 && item->acl_default)
+-                r = path_set_acl(fn, path, ACL_TYPE_DEFAULT, item->acl_default, item->force);
++                r = path_set_acl(procfs_path, path, ACL_TYPE_DEFAULT, item->acl_default, item->force);
+ 
+         if (r > 0)
+                 return -r; /* already warned */
+-        else if (r == -EOPNOTSUPP) {
++        if (r == -EOPNOTSUPP) {
+                 log_debug_errno(r, "ACLs not supported by file system at %s", path);
+                 return 0;
+-        } else if (r < 0)
+-                log_error_errno(r, "ACL operation on \"%s\" failed: %m", path);
++        }
++        if (r < 0)
++                return log_error_errno(r, "ACL operation on \"%s\" failed: %m", path);
+ #endif
+         return r;
+ }
+ 
++static int path_set_acls(Item *item, const char *path) {
++        int r = 0;
++#ifdef HAVE_ACL
++        _cleanup_close_ int fd = -1;
++        struct stat st;
++
++        assert(item);
++        assert(path);
++
++        fd = open(path, O_NOFOLLOW|O_CLOEXEC|O_PATH);
++        if (fd < 0)
++                return log_error_errno(errno, "Adjusting ACL of %s failed: %m", path);
++
++        if (fstat(fd, &st) < 0)
++                return log_error_errno(errno, "Failed to fstat() file %s: %m", path);
++
++        r = fd_set_acls(item, fd, &st);
++ #endif
++         return r;
++ }
++
+ #define ATTRIBUTES_ALL                          \
+         (FS_NOATIME_FL      |                   \
+          FS_SYNC_FL         |                   \
+@@ -1143,30 +1195,24 @@ static int parse_attribute_from_arg(Item *item) {
+         return 0;
+ }
+ 
+-static int path_set_attribute(Item *item, const char *path) {
+-        _cleanup_close_ int fd = -1;
+-        struct stat st;
++static int fd_set_attribute(Item *item, int fd, const struct stat *st) {
++        char procfs_path[strlen("/proc/self/fd/") + DECIMAL_STR_MAX(int)];
++        _cleanup_close_ int procfs_fd = -1;
++        _cleanup_free_ char *path = NULL;
+         unsigned f;
+         int r;
+ 
+         if (!item->attribute_set || item->attribute_mask == 0)
+                 return 0;
+ 
+-        fd = open(path, O_RDONLY|O_NONBLOCK|O_CLOEXEC|O_NOATIME|O_NOFOLLOW);
+-        if (fd < 0) {
+-                if (errno == ELOOP)
+-                        return log_error_errno(errno, "Skipping file attributes adjustment on symlink %s.", path);
+-
+-                return log_error_errno(errno, "Cannot open '%s': %m", path);
+-        }
+-
+-        if (fstat(fd, &st) < 0)
+-                return log_error_errno(errno, "Cannot stat '%s': %m", path);
++        r = fd_get_path(fd, &path);
++        if (r < 0)
++                return r;
+ 
+         /* Issuing the file attribute ioctls on device nodes is not
+          * safe, as that will be delivered to the drivers, not the
+          * file system containing the device node. */
+-        if (!S_ISREG(st.st_mode) && !S_ISDIR(st.st_mode)) {
++        if (!S_ISREG(st->st_mode) && !S_ISDIR(st->st_mode)) {
+                 log_error("Setting file flags is only supported on regular files and directories, cannot set on '%s'.", path);
+                 return -EINVAL;
+         }
+@@ -1174,10 +1220,16 @@ static int path_set_attribute(Item *item, const char *path) {
+         f = item->attribute_value & item->attribute_mask;
+ 
+         /* Mask away directory-specific flags */
+-        if (!S_ISDIR(st.st_mode))
++        if (!S_ISDIR(st->st_mode))
+                 f &= ~FS_DIRSYNC_FL;
+ 
+-        r = chattr_fd(fd, f, item->attribute_mask);
++        xsprintf(procfs_path, "/proc/self/fd/%i", fd);
++
++        procfs_fd = open(procfs_path, O_RDONLY|O_CLOEXEC|O_NOATIME);
++        if (procfs_fd < 0)
++                return -errno;
++
++        r = chattr_fd(procfs_fd, f, item->attribute_mask);
+         if (r < 0)
+                 log_full_errno(IN_SET(r, -ENOTTY, -EOPNOTSUPP) ? LOG_DEBUG : LOG_WARNING,
+                                r,
+@@ -1187,6 +1239,23 @@ static int path_set_attribute(Item *item, const char *path) {
+         return 0;
+ }
+ 
++static int path_set_attribute(Item *item, const char *path) {
++        _cleanup_close_ int fd = -1;
++        struct stat st;
++
++        if (!item->attribute_set || item->attribute_mask == 0)
++                return 0;
++
++        fd = open(path, O_CLOEXEC|O_NOFOLLOW|O_PATH);
++        if (fd < 0)
++                return log_error_errno(errno, "Cannot open '%s': %m", path);
++
++        if (fstat(fd, &st) < 0)
++                return log_error_errno(errno, "Cannot stat '%s': %m", path);
++
++        return fd_set_attribute(item, fd, &st);
++}
++
+ static int write_one_file(Item *i, const char *path) {
+         _cleanup_close_ int fd = -1;
+         int flags, r = 0;
+@@ -1251,48 +1320,58 @@ done:
+ }
+ 
+ typedef int (*action_t)(Item *, const char *);
++typedef int (*fdaction_t)(Item *, int fd, const struct stat *st);
+ 
+-static int item_do_children(Item *i, const char *path, action_t action) {
+-        _cleanup_closedir_ DIR *d;
+-        struct dirent *de;
+-        int r = 0;
++static int item_do(Item *i, int fd, const struct stat *st, fdaction_t action) {
++        int r = 0, q;
+ 
+         assert(i);
+-        assert(path);
++        assert(fd >= 0);
++        assert(st);
+ 
+         /* This returns the first error we run into, but nevertheless
+          * tries to go on */
++        r = action(i, fd, st);
+ 
+-        d = opendir_nomod(path);
+-        if (!d)
+-                return IN_SET(errno, ENOENT, ENOTDIR, ELOOP) ? 0 : -errno;
++        if (S_ISDIR(st->st_mode)) {
++                char procfs_path[strlen("/proc/self/fd/") + DECIMAL_STR_MAX(int)];
++                _cleanup_closedir_ DIR *d = NULL;
++                struct dirent *de;
+ 
+-        FOREACH_DIRENT_ALL(de, d, r = -errno) {
+-                _cleanup_free_ char *p = NULL;
+-                int q;
++                /* The passed 'fd' was opened with O_PATH. We need to convert
++                 * it into a 'regular' fd before reading the directory content. */
++                xsprintf(procfs_path, "/proc/self/fd/%i", fd);
+ 
+-                if (dot_or_dot_dot(de->d_name))
+-                        continue;
++                d = opendir(procfs_path);
++                if (!d) {
++                        r = r ?: -errno;
++                        goto finish;
++                }
+ 
+-                p = strjoin(path, "/", de->d_name);
+-                if (!p)
+-                        return -ENOMEM;
++                FOREACH_DIRENT_ALL(de, d, q = -errno; goto finish) {
++                        struct stat de_st;
++                        int de_fd;
++
++                        if (dot_or_dot_dot(de->d_name))
++                                continue;
+ 
+-                q = action(i, p);
+-                if (q < 0 && q != -ENOENT && r == 0)
+-                        r = q;
++                        de_fd = openat(fd, de->d_name, O_NOFOLLOW|O_CLOEXEC|O_PATH);
++                        if (de_fd >= 0 && fstat(de_fd, &de_st) >= 0)
++                                /* pass ownership of dirent fd over  */
++                                q = item_do(i, de_fd, &de_st, action);
++                        else
++                                q = -errno;
+ 
+-                if (IN_SET(de->d_type, DT_UNKNOWN, DT_DIR)) {
+-                        q = item_do_children(i, p, action);
+                         if (q < 0 && r == 0)
+                                 r = q;
+                 }
+         }
+-
++finish:
++        safe_close(fd);
+         return r;
+ }
+ 
+-static int glob_item(Item *i, action_t action, bool recursive) {
++static int glob_item(Item *i, action_t action) {
+         _cleanup_globfree_ glob_t g = {
+ #ifdef GLOB_ALTDIRFUNC
+                 .gl_opendir = (void *(*)(const char *)) opendir_nomod,
+@@ -1309,12 +1388,48 @@ static int glob_item(Item *i, action_t action, bool recursive) {
+                 k = action(i, *fn);
+                 if (k < 0 && r == 0)
+                         r = k;
++        }
+ 
+-                if (recursive) {
+-                        k = item_do_children(i, *fn, action);
+-                        if (k < 0 && r == 0)
+-                                r = k;
++        return r;
++}
++
++static int glob_item_recursively(Item *i, fdaction_t action) {
++        _cleanup_globfree_ glob_t g = {
++                .gl_opendir = (void *(*)(const char *)) opendir_nomod,
++        };
++        int r = 0, k;
++        char **fn;
++
++        k = safe_glob(i->path, GLOB_NOSORT|GLOB_BRACE, &g);
++        if (k < 0 && k != -ENOENT)
++                return log_error_errno(k, "glob(%s) failed: %m", i->path);
++
++        STRV_FOREACH(fn, g.gl_pathv) {
++                _cleanup_close_ int fd = -1;
++                struct stat st;
++
++                /* Make sure we won't trigger/follow file object (such as
++                 * device nodes, automounts, ...) pointed out by 'fn' with
++                 * O_PATH. Note, when O_PATH is used, flags other than
++                 * O_CLOEXEC, O_DIRECTORY, and O_NOFOLLOW are ignored. */
++
++                fd = open(*fn, O_CLOEXEC|O_NOFOLLOW|O_PATH);
++                if (fd < 0) {
++                        r = r ?: -errno;
++                        continue;
++                }
++
++                if (fstat(fd, &st) < 0) {
++                        r = r ?: -errno;
++                        continue;
+                 }
++
++                k = item_do(i, fd, &st, action);
++                if (k < 0 && r == 0)
++                        r = k;
++
++                /* we passed fd ownership to the previous call */
++                fd = -1;
+         }
+ 
+         return r;
+@@ -1403,7 +1518,7 @@ static int create_item(Item *i) {
+                 break;
+ 
+         case WRITE_FILE:
+-                r = glob_item(i, write_one_file, false);
++                r = glob_item(i, write_one_file);
+                 if (r < 0)
+                         return r;
+ 
+@@ -1662,49 +1777,49 @@ static int create_item(Item *i) {
+ 
+         case ADJUST_MODE:
+         case RELABEL_PATH:
+-                r = glob_item(i, path_set_perms, false);
++                r = glob_item(i, path_set_perms);
+                 if (r < 0)
+                         return r;
+                 break;
+ 
+         case RECURSIVE_RELABEL_PATH:
+-                r = glob_item(i, path_set_perms, true);
++                r = glob_item_recursively(i, fd_set_perms);
+                 if (r < 0)
+                         return r;
+                 break;
+ 
+         case SET_XATTR:
+-                r = glob_item(i, path_set_xattrs, false);
++                r = glob_item(i, path_set_xattrs);
+                 if (r < 0)
+                         return r;
+                 break;
+ 
+         case RECURSIVE_SET_XATTR:
+-                r = glob_item(i, path_set_xattrs, true);
++                r = glob_item_recursively(i, fd_set_xattrs);
+                 if (r < 0)
+                         return r;
+                 break;
+ 
+         case SET_ACL:
+-                r = glob_item(i, path_set_acls, false);
++                r = glob_item(i, path_set_acls);
+                 if (r < 0)
+                         return r;
+                 break;
+ 
+         case RECURSIVE_SET_ACL:
+-                r = glob_item(i, path_set_acls, true);
++                r = glob_item_recursively(i, fd_set_acls);
+                 if (r < 0)
+                         return r;
+                 break;
+ 
+         case SET_ATTRIBUTE:
+-                r = glob_item(i, path_set_attribute, false);
++                r = glob_item(i, path_set_attribute);
+                 if (r < 0)
+                         return r;
+                 break;
+ 
+         case RECURSIVE_SET_ATTRIBUTE:
+-                r = glob_item(i, path_set_attribute, true);
++                r = glob_item_recursively(i, fd_set_attribute);
+                 if (r < 0)
+                         return r;
+                 break;
+@@ -1754,7 +1869,7 @@ static int remove_item(Item *i) {
+         case REMOVE_PATH:
+         case TRUNCATE_DIRECTORY:
+         case RECURSIVE_REMOVE_PATH:
+-                return glob_item(i, remove_item_instance, false);
++                return glob_item(i, remove_item_instance);
+ 
+         default:
+                 return 0;
+@@ -1828,7 +1943,7 @@ static int clean_item(Item *i) {
+                 return 0;
+         case EMPTY_DIRECTORY:
+         case IGNORE_DIRECTORY_PATH:
+-                return glob_item(i, clean_item_instance, false);
++                return glob_item(i, clean_item_instance);
+         default:
+                 return 0;
+         }
+-- 
+2.11.0
+
diff --git a/meta/recipes-core/systemd/systemd/0002-Make-tmpfiles-safe.patch b/meta/recipes-core/systemd/systemd/0002-Make-tmpfiles-safe.patch
new file mode 100644
index 0000000000..80d27c141b
--- /dev/null
+++ b/meta/recipes-core/systemd/systemd/0002-Make-tmpfiles-safe.patch
@@ -0,0 +1,1828 @@
+From fb95c890cf5116e698347c6a7bb3daeeb2d28cf9 Mon Sep 17 00:00:00 2001
+From: George McCollister <george.mccollister@gmail.com>
+Date: Thu, 21 Feb 2019 18:04:37 -0600
+Subject: [PATCH] Make tmpfiles safe
+
+In addition to backporting the changesets in #8822, this also backports
+e04fc13 (test: add tests for systemd-tmpfiles), as well as empty_to_root()
+from v239.
+
+Origin: upstream, https://github.com/systemd/systemd/pull/8822/commits
+Bug: https://github.com/systemd/systemd/issues/7986
+
+Patch from:
+systemd_237-3ubuntu10.13.debian CVE-2018-6954_2.patch
+
+https://usn.ubuntu.com/3816-1/ states that CVE-2018-6954 doesn't
+affect Ubuntu 18.10 which uses the same version of systemd as thud
+(239).
+
+CVE: CVE-2018-6954
+Upstream-Status: Backport
+
+Signed-off-by: George McCollister <george.mccollister@gmail.com>
+---
+ src/basic/btrfs-util.c   |  26 +-
+ src/basic/btrfs-util.h   |   1 +
+ src/basic/fileio.c       |   5 +-
+ src/basic/fs-util.c      |  27 +-
+ src/basic/fs-util.h      |   2 +
+ src/basic/label.h        |   1 +
+ src/basic/mkdir-label.c  |  17 +
+ src/basic/mkdir.c        |   6 +
+ src/basic/mkdir.h        |   1 +
+ src/basic/path-util.c    |   5 +-
+ src/basic/path-util.h    |   4 +
+ src/basic/selinux-util.c |  84 +++--
+ src/basic/selinux-util.h |   1 +
+ src/basic/smack-util.c   | 119 +++++--
+ src/basic/smack-util.h   |   1 +
+ src/basic/stat-util.c    |  11 +
+ src/basic/stat-util.h    |   1 +
+ src/test/test-fs-util.c  |  25 ++
+ src/tmpfiles/tmpfiles.c  | 902 ++++++++++++++++++++++++++++++++---------------
+ 19 files changed, 882 insertions(+), 357 deletions(-)
+
+diff --git a/src/basic/btrfs-util.c b/src/basic/btrfs-util.c
+index 19d385ab7c..26b088f52b 100644
+--- a/src/basic/btrfs-util.c
++++ b/src/basic/btrfs-util.c
+@@ -150,8 +150,25 @@ int btrfs_is_subvol(const char *path) {
+         return btrfs_is_subvol_fd(fd);
+ }
+ 
+-int btrfs_subvol_make(const char *path) {
++int btrfs_subvol_make_fd(int fd, const char *subvolume) {
+         struct btrfs_ioctl_vol_args args = {};
++        int r;
++
++        assert(subvolume);
++
++        r = validate_subvolume_name(subvolume);
++        if (r < 0)
++                return r;
++
++        strncpy(args.name, subvolume, sizeof(args.name)-1);
++
++        if (ioctl(fd, BTRFS_IOC_SUBVOL_CREATE, &args) < 0)
++                return -errno;
++
++        return 0;
++}
++
++int btrfs_subvol_make(const char *path) {
+         _cleanup_close_ int fd = -1;
+         const char *subvolume;
+         int r;
+@@ -166,12 +183,7 @@ int btrfs_subvol_make(const char *path) {
+         if (fd < 0)
+                 return fd;
+ 
+-        strncpy(args.name, subvolume, sizeof(args.name)-1);
+-
+-        if (ioctl(fd, BTRFS_IOC_SUBVOL_CREATE, &args) < 0)
+-                return -errno;
+-
+-        return 0;
++        return btrfs_subvol_make_fd(fd, subvolume);
+ }
+ 
+ int btrfs_subvol_set_read_only_fd(int fd, bool b) {
+diff --git a/src/basic/btrfs-util.h b/src/basic/btrfs-util.h
+index 952b3c26da..e92687bc57 100644
+--- a/src/basic/btrfs-util.h
++++ b/src/basic/btrfs-util.h
+@@ -84,6 +84,7 @@ int btrfs_resize_loopback_fd(int fd, uint64_t size, bool grow_only);
+ int btrfs_resize_loopback(const char *path, uint64_t size, bool grow_only);
+ 
+ int btrfs_subvol_make(const char *path);
++int btrfs_subvol_make_fd(int fd, const char *subvolume);
+ 
+ int btrfs_subvol_snapshot_fd(int old_fd, const char *new_path, BtrfsSnapshotFlags flags);
+ int btrfs_subvol_snapshot(const char *old_path, const char *new_path, BtrfsSnapshotFlags flags);
+diff --git a/src/basic/fileio.c b/src/basic/fileio.c
+index 26d6174664..1c7e23332f 100644
+--- a/src/basic/fileio.c
++++ b/src/basic/fileio.c
+@@ -1304,7 +1304,10 @@ int tempfn_random_child(const char *p, const char *extra, char **ret) {
+         if (!t)
+                 return -ENOMEM;
+ 
+-        x = stpcpy(stpcpy(stpcpy(t, p), "/.#"), extra);
++        if (isempty(p))
++                x = stpcpy(stpcpy(t, ".#"), extra);
++        else
++                x = stpcpy(stpcpy(stpcpy(t, p), "/.#"), extra);
+ 
+         u = random_u64();
+         for (i = 0; i < 16; i++) {
+diff --git a/src/basic/fs-util.c b/src/basic/fs-util.c
+index a8e50d4c78..c96a8813ea 100644
+--- a/src/basic/fs-util.c
++++ b/src/basic/fs-util.c
+@@ -465,6 +465,31 @@ int mkfifo_atomic(const char *path, mode_t mode) {
+         return 0;
+ }
+ 
++int mkfifoat_atomic(int dirfd, const char *path, mode_t mode) {
++        _cleanup_free_ char *t = NULL;
++        int r;
++
++        assert(path);
++
++        if (path_is_absolute(path))
++                return mkfifo_atomic(path, mode);
++
++        /* We're only interested in the (random) filename.  */
++        r = tempfn_random_child("", NULL, &t);
++        if (r < 0)
++                return r;
++
++        if (mkfifoat(dirfd, t, mode) < 0)
++                return -errno;
++
++        if (renameat(dirfd, t, dirfd, path) < 0) {
++                unlink_noerrno(t);
++                return -errno;
++        }
++
++        return 0;
++}
++
+ int get_files_in_directory(const char *path, char ***list) {
+         _cleanup_closedir_ DIR *d = NULL;
+         struct dirent *de;
+@@ -808,7 +833,7 @@ int chase_symlinks(const char *path, const char *original_root, unsigned flags,
+                     fd_is_fs_type(child, AUTOFS_SUPER_MAGIC) > 0)
+                         return -EREMOTE;
+ 
+-                if (S_ISLNK(st.st_mode)) {
++                if (S_ISLNK(st.st_mode) && !((flags & CHASE_NOFOLLOW) && isempty(todo))) {
+                         char *joined;
+ 
+                         _cleanup_free_ char *destination = NULL;
+diff --git a/src/basic/fs-util.h b/src/basic/fs-util.h
+index 9c4b02eccd..121345e74d 100644
+--- a/src/basic/fs-util.h
++++ b/src/basic/fs-util.h
+@@ -80,6 +80,7 @@ int symlink_idempotent(const char *from, const char *to);
+ int symlink_atomic(const char *from, const char *to);
+ int mknod_atomic(const char *path, mode_t mode, dev_t dev);
+ int mkfifo_atomic(const char *path, mode_t mode);
++int mkfifoat_atomic(int dir_fd, const char *path, mode_t mode);
+ 
+ int get_files_in_directory(const char *path, char ***list);
+ 
+@@ -106,6 +107,7 @@ enum {
+         CHASE_NO_AUTOFS   = 1U << 2,   /* If set, return -EREMOTE if autofs mount point found */
+         CHASE_SAFE        = 1U << 3,   /* If set, return EPERM if we ever traverse from unprivileged to privileged files or directories */
+         CHASE_OPEN        = 1U << 4,   /* If set, return an O_PATH object to the final component */
++        CHASE_NOFOLLOW    = 1U << 7, /* Only valid with CHASE_OPEN: when the path's right-most component refers to symlink return O_PATH fd of the symlink, rather than following it. */
+ };
+ 
+ int chase_symlinks(const char *path_with_prefix, const char *root, unsigned flags, char **ret);
+diff --git a/src/basic/label.h b/src/basic/label.h
+index d73dacec4f..3ecfed72c6 100644
+--- a/src/basic/label.h
++++ b/src/basic/label.h
+@@ -26,6 +26,7 @@
+ int label_fix(const char *path, bool ignore_enoent, bool ignore_erofs);
+ 
+ int mkdir_label(const char *path, mode_t mode);
++int mkdirat_label(int dirfd, const char *path, mode_t mode);
+ int symlink_label(const char *old_path, const char *new_path);
+ 
+ int btrfs_subvol_make_label(const char *path);
+diff --git a/src/basic/mkdir-label.c b/src/basic/mkdir-label.c
+index 6f3a46f467..3c1a227bfa 100644
+--- a/src/basic/mkdir-label.c
++++ b/src/basic/mkdir-label.c
+@@ -47,6 +47,23 @@ int mkdir_label(const char *path, mode_t mode) {
+         return mac_smack_fix(path, false, false);
+ }
+ 
++int mkdirat_label(int dirfd, const char *path, mode_t mode) {
++        int r;
++
++        assert(path);
++
++        r = mac_selinux_create_file_prepare_at(dirfd, path, S_IFDIR);
++        if (r < 0)
++                return r;
++
++        r = mkdirat_errno_wrapper(dirfd, path, mode);
++        mac_selinux_create_file_clear();
++        if (r < 0)
++                return r;
++
++        return mac_smack_fix_at(dirfd, path, false, false);
++}
++
+ int mkdir_safe_label(const char *path, mode_t mode, uid_t uid, gid_t gid, bool follow_symlink) {
+         return mkdir_safe_internal(path, mode, uid, gid, follow_symlink, mkdir_label);
+ }
+diff --git a/src/basic/mkdir.c b/src/basic/mkdir.c
+index d51518a5a7..418945ad4a 100644
+--- a/src/basic/mkdir.c
++++ b/src/basic/mkdir.c
+@@ -77,6 +77,12 @@ int mkdir_errno_wrapper(const char *pathname, mode_t mode) {
+         return 0;
+ }
+ 
++int mkdirat_errno_wrapper(int dirfd, const char *pathname, mode_t mode) {
++        if (mkdirat(dirfd, pathname, mode) < 0)
++                return -errno;
++        return 0;
++}
++
+ int mkdir_safe(const char *path, mode_t mode, uid_t uid, gid_t gid, bool follow_symlink) {
+         return mkdir_safe_internal(path, mode, uid, gid, follow_symlink, mkdir_errno_wrapper);
+ }
+diff --git a/src/basic/mkdir.h b/src/basic/mkdir.h
+index d6c2d579a3..3ec6f3ed2d 100644
+--- a/src/basic/mkdir.h
++++ b/src/basic/mkdir.h
+@@ -24,6 +24,7 @@
+ #include <sys/types.h>
+ 
+ int mkdir_errno_wrapper(const char *pathname, mode_t mode);
++int mkdirat_errno_wrapper(int dirfd, const char *pathname, mode_t mode);
+ int mkdir_safe(const char *path, mode_t mode, uid_t uid, gid_t gid, bool follow_symlink);
+ int mkdir_parents(const char *path, mode_t mode);
+ int mkdir_p(const char *path, mode_t mode);
+diff --git a/src/basic/path-util.c b/src/basic/path-util.c
+index df94629385..84404f7ee1 100644
+--- a/src/basic/path-util.c
++++ b/src/basic/path-util.c
+@@ -127,10 +127,7 @@ int path_make_absolute_cwd(const char *p, char **ret) {
+                 if (r < 0)
+                         return r;
+ 
+-                if (endswith(cwd, "/"))
+-                        c = strjoin(cwd, p);
+-                else
+-                        c = strjoin(cwd, "/", p);
++                c = path_join(NULL, cwd, p);
+         }
+         if (!c)
+                 return -ENOMEM;
+diff --git a/src/basic/path-util.h b/src/basic/path-util.h
+index 89c285e076..1094baca12 100644
+--- a/src/basic/path-util.h
++++ b/src/basic/path-util.h
+@@ -156,3 +156,7 @@ static inline const char *skip_dev_prefix(const char *p) {
+ 
+         return e ?: p;
+ }
++static inline const char *empty_to_root(const char *path) {
++        return isempty(path) ? "/" : path;
++}
++
+diff --git a/src/basic/selinux-util.c b/src/basic/selinux-util.c
+index 0c6e99b1d7..bdef7d148b 100644
+--- a/src/basic/selinux-util.c
++++ b/src/basic/selinux-util.c
+@@ -34,6 +34,7 @@
+ #endif
+ 
+ #include "alloc-util.h"
++#include "fd-util.h"
+ #include "log.h"
+ #include "macro.h"
+ #include "path-util.h"
+@@ -311,48 +312,89 @@ char* mac_selinux_free(char *label) {
+         return NULL;
+ }
+ 
+-int mac_selinux_create_file_prepare(const char *path, mode_t mode) {
+-
+ #if HAVE_SELINUX
++static int selinux_create_file_prepare_abspath(const char *abspath, mode_t mode) {
+         _cleanup_freecon_ char *filecon = NULL;
++        _cleanup_free_ char *path = NULL;
+         int r;
+ 
+-        assert(path);
+-
+-        if (!label_hnd)
+-                return 0;
+-
+-        if (path_is_absolute(path))
+-                r = selabel_lookup_raw(label_hnd, &filecon, path, mode);
+-        else {
+-                _cleanup_free_ char *newpath = NULL;
+-
+-                r = path_make_absolute_cwd(path, &newpath);
+-                if (r < 0)
+-                        return r;
+-
+-                r = selabel_lookup_raw(label_hnd, &filecon, newpath, mode);
+-        }
++        assert(abspath);
++        assert(path_is_absolute(abspath));
+ 
++        r = selabel_lookup_raw(label_hnd, &filecon, abspath, mode);
+         if (r < 0) {
+                 /* No context specified by the policy? Proceed without setting it. */
+                 if (errno == ENOENT)
+                         return 0;
+ 
+-                log_enforcing("Failed to determine SELinux security context for %s: %m", path);
++                log_enforcing("Failed to determine SELinux security context for %s: %m", abspath);
+         } else {
+                 if (setfscreatecon_raw(filecon) >= 0)
+                         return 0; /* Success! */
+ 
+-                log_enforcing("Failed to set SELinux security context %s for %s: %m", filecon, path);
++                log_enforcing("Failed to set SELinux security context %s for %s: %m", filecon, abspath);
+         }
+ 
+         if (security_getenforce() > 0)
+                 return -errno;
+ 
+-#endif
+         return 0;
+ }
++#endif
++
++int mac_selinux_create_file_prepare_at(int dirfd, const char *path, mode_t mode) {
++        int r = 0;
++
++#if HAVE_SELINUX
++        _cleanup_free_ char *abspath = NULL;
++        _cleanup_close_ int fd = -1;
++
++        assert(path);
++
++        if (!label_hnd)
++                return 0;
++
++        if (!path_is_absolute(path)) {
++                _cleanup_free_ char *p = NULL;
++
++                if (dirfd == AT_FDCWD)
++                        r = safe_getcwd(&p);
++                else
++                        r = fd_get_path(dirfd, &p);
++                if (r < 0)
++                        return r;
++
++                abspath = path_join(NULL, p, path);
++                if (!abspath)
++                        return -ENOMEM;
++
++                path = abspath;
++        }
++
++        r = selinux_create_file_prepare_abspath(path, mode);
++#endif
++        return r;
++}
++
++int mac_selinux_create_file_prepare(const char *path, mode_t mode) {
++        int r = 0;
++
++#if HAVE_SELINUX
++        _cleanup_free_ char *abspath = NULL;
++
++        assert(path);
++
++        if (!label_hnd)
++                return 0;
++
++        r = path_make_absolute_cwd(path, &abspath);
++        if (r < 0)
++                return r;
++
++        r = selinux_create_file_prepare_abspath(abspath, mode);
++#endif
++        return r;
++}
+ 
+ void mac_selinux_create_file_clear(void) {
+ 
+diff --git a/src/basic/selinux-util.h b/src/basic/selinux-util.h
+index 9780dca81e..84a8bf9729 100644
+--- a/src/basic/selinux-util.h
++++ b/src/basic/selinux-util.h
+@@ -41,6 +41,7 @@ int mac_selinux_get_child_mls_label(int socket_fd, const char *exe, const char *
+ char* mac_selinux_free(char *label);
+ 
+ int mac_selinux_create_file_prepare(const char *path, mode_t mode);
++int mac_selinux_create_file_prepare_at(int dirfd, const char *path, mode_t mode);
+ void mac_selinux_create_file_clear(void);
+ 
+ int mac_selinux_create_socket_prepare(const char *label);
+diff --git a/src/basic/smack-util.c b/src/basic/smack-util.c
+index f0018f013f..ea0af3e45f 100644
+--- a/src/basic/smack-util.c
++++ b/src/basic/smack-util.c
+@@ -21,18 +21,21 @@
+ ***/
+ 
+ #include <errno.h>
++#include <fcntl.h>
+ #include <string.h>
+ #include <sys/stat.h>
+ #include <sys/xattr.h>
+ #include <unistd.h>
+ 
+ #include "alloc-util.h"
++#include "fd-util.h"
+ #include "fileio.h"
+ #include "log.h"
+ #include "macro.h"
+ #include "path-util.h"
+ #include "process-util.h"
+ #include "smack-util.h"
++#include "stdio-util.h"
+ #include "string-table.h"
+ #include "xattr-util.h"
+ 
+@@ -134,59 +137,111 @@ int mac_smack_apply_pid(pid_t pid, const char *label) {
+         return r;
+ }
+ 
+-int mac_smack_fix(const char *path, bool ignore_enoent, bool ignore_erofs) {
++static int smack_fix_fd(int fd , const char *abspath, bool ignore_erofs) {
++        char procfs_path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)];
++        const char *label;
+         struct stat st;
+         int r;
+ 
+-        assert(path);
++        /* The caller should have done the sanity checks. */
++        assert(abspath);
++        assert(path_is_absolute(abspath));
+ 
+-        if (!mac_smack_use())
++        /* Path must be in /dev. */
++        if (!path_startswith(abspath, "/dev"))
+                 return 0;
+ 
++        if (fstat(fd, &st) < 0)
++                return -errno;
++
+         /*
+-         * Path must be in /dev and must exist
++         * Label directories and character devices "*".
++         * Label symlinks "_".
++         * Don't change anything else.
+          */
+-        if (!path_startswith(path, "/dev"))
++
++        if (S_ISDIR(st.st_mode))
++                label = SMACK_STAR_LABEL;
++        else if (S_ISLNK(st.st_mode))
++                label = SMACK_FLOOR_LABEL;
++        else if (S_ISCHR(st.st_mode))
++                label = SMACK_STAR_LABEL;
++        else
+                 return 0;
+ 
+-        r = lstat(path, &st);
+-        if (r >= 0) {
+-                const char *label;
+-
+-                /*
+-                 * Label directories and character devices "*".
+-                 * Label symlinks "_".
+-                 * Don't change anything else.
+-                 */
+-
+-                if (S_ISDIR(st.st_mode))
+-                        label = SMACK_STAR_LABEL;
+-                else if (S_ISLNK(st.st_mode))
+-                        label = SMACK_FLOOR_LABEL;
+-                else if (S_ISCHR(st.st_mode))
+-                        label = SMACK_STAR_LABEL;
+-                else
+-                        return 0;
++        xsprintf(procfs_path, "/proc/self/fd/%i", fd);
++        if (setxattr(procfs_path, "security.SMACK64", label, strlen(label), 0) < 0) {
++                _cleanup_free_ char *old_label = NULL;
+ 
+-                r = lsetxattr(path, "security.SMACK64", label, strlen(label), 0);
++                r = -errno;
+ 
+                 /* If the FS doesn't support labels, then exit without warning */
+-                if (r < 0 && errno == EOPNOTSUPP)
++                if (r == -EOPNOTSUPP)
++                        return 0;
++
++                /* It the FS is read-only and we were told to ignore failures caused by that, suppress error */
++                if (r == -EROFS && ignore_erofs)
++                        return 0;
++
++                /* If the old label is identical to the new one, suppress any kind of error */
++                if (getxattr_malloc(procfs_path, "security.SMACK64", &old_label, false) >= 0 &&
++                    streq(old_label, label))
+                         return 0;
++
++                return log_debug_errno(r, "Unable to fix SMACK label of %s: %m", abspath);
+         }
+ 
+-        if (r < 0) {
+-                /* Ignore ENOENT in some cases */
++        return r;
++}
++
++int mac_smack_fix_at(int dirfd, const char *path, bool ignore_enoent, bool ignore_erofs) {
++        _cleanup_free_ char *p = NULL;
++        _cleanup_close_ int fd = -1;
++        int r;
++
++        assert(path);
++
++        if (!mac_smack_use())
++                return 0;
++
++        fd = openat(dirfd, path, O_NOFOLLOW|O_CLOEXEC|O_PATH);
++        if (fd < 0) {
+                 if (ignore_enoent && errno == ENOENT)
+                         return 0;
+ 
+-                if (ignore_erofs && errno == EROFS)
++                return -errno;
++        }
++
++        r = fd_get_path(fd, &p);
++        if (r < 0)
++                return r;
++
++        return smack_fix_fd(fd, p, ignore_erofs);
++}
++
++int mac_smack_fix(const char *path, bool ignore_enoent, bool ignore_erofs) {
++        _cleanup_free_ char *abspath = NULL;
++        _cleanup_close_ int fd = -1;
++        int r;
++
++        assert(path);
++
++        if (!mac_smack_use())
++                return 0;
++
++        r = path_make_absolute_cwd(path, &abspath);
++        if (r < 0)
++                return r;
++
++        fd = open(abspath, O_NOFOLLOW|O_CLOEXEC|O_PATH);
++        if (fd < 0) {
++                if (ignore_enoent && errno == ENOENT)
+                         return 0;
+ 
+-                r = log_debug_errno(errno, "Unable to fix SMACK label of %s: %m", path);
++                return -errno;
+         }
+ 
+-        return r;
++        return smack_fix_fd(fd, abspath, ignore_erofs);
+ }
+ 
+ int mac_smack_copy(const char *dest, const char *src) {
+@@ -236,6 +291,10 @@ int mac_smack_fix(const char *path, bool ignore_enoent, bool ignore_erofs) {
+         return 0;
+ }
+ 
++int mac_smack_fix_at(int dirfd, const char *path, bool ignore_enoent, bool ignore_erofs) {
++        return 0;
++}
++
+ int mac_smack_copy(const char *dest, const char *src) {
+         return 0;
+ }
+diff --git a/src/basic/smack-util.h b/src/basic/smack-util.h
+index e4d46d7736..0c214bbbc0 100644
+--- a/src/basic/smack-util.h
++++ b/src/basic/smack-util.h
+@@ -44,6 +44,7 @@ typedef enum SmackAttr {
+ bool mac_smack_use(void);
+ 
+ int mac_smack_fix(const char *path, bool ignore_enoent, bool ignore_erofs);
++int mac_smack_fix_at(int dirfd, const char *path, bool ignore_enoent, bool ignore_erofs);
+ 
+ const char* smack_attr_to_string(SmackAttr i) _const_;
+ SmackAttr smack_attr_from_string(const char *s) _pure_;
+diff --git a/src/basic/stat-util.c b/src/basic/stat-util.c
+index 3a54103f1b..801889ae5b 100644
+--- a/src/basic/stat-util.c
++++ b/src/basic/stat-util.c
+@@ -63,6 +63,17 @@ int is_dir(const char* path, bool follow) {
+         return !!S_ISDIR(st.st_mode);
+ }
+ 
++int is_dir_fd(int fd) {
++        struct stat st;
++        int r;
++
++        r = fstat(fd, &st);
++        if (r < 0)
++                return -errno;
++
++        return !!S_ISDIR(st.st_mode);
++}
++
+ int is_device_node(const char *path) {
+         struct stat info;
+ 
+diff --git a/src/basic/stat-util.h b/src/basic/stat-util.h
+index d8d3c20496..7ea68abfa3 100644
+--- a/src/basic/stat-util.h
++++ b/src/basic/stat-util.h
+@@ -31,6 +31,7 @@
+ 
+ int is_symlink(const char *path);
+ int is_dir(const char *path, bool follow);
++int is_dir_fd(int fd);
+ int is_device_node(const char *path);
+ 
+ int dir_is_empty(const char *path);
+diff --git a/src/test/test-fs-util.c b/src/test/test-fs-util.c
+index 9f3a500080..a76d6d0f8b 100644
+--- a/src/test/test-fs-util.c
++++ b/src/test/test-fs-util.c
+@@ -40,6 +40,7 @@ static void test_chase_symlinks(void) {
+         _cleanup_free_ char *result = NULL;
+         char temp[] = "/tmp/test-chase.XXXXXX";
+         const char *top, *p, *pslash, *q, *qslash;
++        struct stat st;
+         int r, pfd;
+ 
+         assert_se(mkdtemp(temp));
+@@ -288,6 +289,30 @@ static void test_chase_symlinks(void) {
+                 assert_se(sd_id128_equal(a, b));
+         }
+ 
++        /* Test CHASE_NOFOLLOW */
++
++        p = strjoina(temp, "/target");
++        q = strjoina(temp, "/symlink");
++        assert_se(symlink(p, q) >= 0);
++        pfd = chase_symlinks(q, NULL, CHASE_OPEN|CHASE_NOFOLLOW, &result);
++        assert_se(pfd > 0);
++        assert_se(path_equal(result, q));
++        assert_se(fstat(pfd, &st) >= 0);
++        assert_se(S_ISLNK(st.st_mode));
++        result = mfree(result);
++
++        /* s1 -> s2 -> nonexistent */
++        q = strjoina(temp, "/s1");
++        assert_se(symlink("s2", q) >= 0);
++        p = strjoina(temp, "/s2");
++        assert_se(symlink("nonexistent", p) >= 0);
++        pfd = chase_symlinks(q, NULL, CHASE_OPEN|CHASE_NOFOLLOW, &result);
++        assert_se(pfd > 0);
++        assert_se(path_equal(result, q));
++        assert_se(fstat(pfd, &st) >= 0);
++        assert_se(S_ISLNK(st.st_mode));
++        result = mfree(result);
++
+         assert_se(rm_rf(temp, REMOVE_ROOT|REMOVE_PHYSICAL) >= 0);
+ }
+ 
+diff --git a/src/tmpfiles/tmpfiles.c b/src/tmpfiles/tmpfiles.c
+index 613d418eb3..d59ccbaa39 100644
+--- a/src/tmpfiles/tmpfiles.c
++++ b/src/tmpfiles/tmpfiles.c
+@@ -794,6 +794,7 @@ static bool hardlink_vulnerable(struct stat *st) {
+ 
+ static int fd_set_perms(Item *i, int fd, const struct stat *st) {
+         _cleanup_free_ char *path = NULL;
++        struct stat stbuf;
+         int r;
+ 
+         assert(i);
+@@ -806,6 +807,12 @@ static int fd_set_perms(Item *i, int fd, const struct stat *st) {
+         if (!i->mode_set && !i->uid_set && !i->gid_set)
+                 goto shortcut;
+ 
++        if (!st) {
++                if (fstat(fd, &stbuf) < 0)
++                        return log_error_errno(errno, "fstat(%s) failed: %m", path);
++                st = &stbuf;
++        }
++
+         if (hardlink_vulnerable(st)) {
+                 log_error("Refusing to set permissions on hardlinked file %s while the fs.protected_hardlinks sysctl is turned off.", path);
+                 return -EPERM;
+@@ -863,32 +870,62 @@ shortcut:
+         return label_fix(path, false, false);
+ }
+ 
+-static int path_set_perms(Item *i, const char *path) {
+-        _cleanup_close_ int fd = -1;
+-        struct stat st;
++static int path_open_parent_safe(const char *path) {
++        _cleanup_free_ char *dn = NULL;
++        int fd;
+ 
+-        assert(i);
+-        assert(path);
++        if (path_equal(path, "/") || !path_is_normalized(path)) {
++                log_error("Failed to open parent of '%s': invalid path.", path);
++                return -EINVAL;
++        }
+ 
+-        fd = open(path, O_NOFOLLOW|O_CLOEXEC|O_PATH);
+-        if (fd < 0) {
+-                int level = LOG_ERR, r = -errno;
++        dn = dirname_malloc(path);
++        if (!dn)
++                return log_oom();
+ 
+-                /* Option "e" operates only on existing objects. Do not
+-                 * print errors about non-existent files or directories */
+-                if (i->type == EMPTY_DIRECTORY && errno == ENOENT) {
+-                        level = LOG_DEBUG;
+-                        r = 0;
+-                }
++        fd = chase_symlinks(dn, NULL, CHASE_OPEN|CHASE_SAFE, NULL);
++        if (fd == -EPERM)
++                return log_error_errno(fd, "Unsafe symlinks encountered in %s, refusing.", path);
++        if (fd < 0)
++                return log_error_errno(fd, "Failed to validate path %s: %m", path);
+ 
+-                log_full_errno(level, errno, "Adjusting owner and mode for %s failed: %m", path);
+-                return r;
++        return fd;
++}
++
++static int path_open_safe(const char *path) {
++        int fd;
++
++        /* path_open_safe() returns a file descriptor opened with O_PATH after
++         * verifying that the path doesn't contain unsafe transitions, except
++         * for its final component as the function does not follow symlink. */
++
++        assert(path);
++
++        if (!path_is_normalized(path)) {
++                log_error("Failed to open invalid path '%s'.", path);
++                return -EINVAL;
+         }
+ 
+-        if (fstat(fd, &st) < 0)
+-                return log_error_errno(errno, "Failed to fstat() file %s: %m", path);
++        fd = chase_symlinks(path, NULL, CHASE_OPEN|CHASE_SAFE|CHASE_NOFOLLOW, NULL);
++        if (fd == -EPERM)
++                return log_error_errno(fd, "Unsafe symlinks encountered in %s, refusing.", path);
++        if (fd < 0)
++                return log_error_errno(fd, "Failed to validate path %s: %m", path);
++
++        return fd;
++}
++
++static int path_set_perms(Item *i, const char *path) {
++        _cleanup_close_ int fd = -1;
++
++        assert(i);
++        assert(path);
+ 
+-        return fd_set_perms(i, fd, &st);
++        fd = path_open_safe(path);
++        if (fd < 0)
++                return fd;
++
++        return fd_set_perms(i, fd, NULL);
+ }
+ 
+ static int parse_xattrs_from_arg(Item *i) {
+@@ -959,9 +996,9 @@ static int path_set_xattrs(Item *i, const char *path) {
+         assert(i);
+         assert(path);
+ 
+-        fd = open(path, O_CLOEXEC|O_NOFOLLOW|O_PATH);
++        fd = path_open_safe(path);
+         if (fd < 0)
+-                return log_error_errno(errno, "Cannot open '%s': %m", path);
++                return fd;
+ 
+         return fd_set_xattrs(i, fd, NULL);
+ }
+@@ -1036,15 +1073,21 @@ static int fd_set_acls(Item *item, int fd, const struct stat *st) {
+ #if HAVE_ACL
+         char procfs_path[strlen("/proc/self/fd/") + DECIMAL_STR_MAX(int)];
+         _cleanup_free_ char *path = NULL;
++        struct stat stbuf;
+ 
+         assert(item);
+         assert(fd);
+-        assert(st);
+ 
+         r = fd_get_path(fd, &path);
+         if (r < 0)
+                 return r;
+ 
++        if (!st) {
++                if (fstat(fd, &stbuf) < 0)
++                        return log_error_errno(errno, "fstat(%s) failed: %m", path);
++                st = &stbuf;
++        }
++
+         if (hardlink_vulnerable(st)) {
+                 log_error("Refusing to set ACLs on hardlinked file %s while the fs.protected_hardlinks sysctl is turned off.", path);
+                 return -EPERM;
+@@ -1079,19 +1122,15 @@ static int path_set_acls(Item *item, const char *path) {
+         int r = 0;
+ #ifdef HAVE_ACL
+         _cleanup_close_ int fd = -1;
+-        struct stat st;
+ 
+         assert(item);
+         assert(path);
+ 
+-        fd = open(path, O_NOFOLLOW|O_CLOEXEC|O_PATH);
++        fd = path_open_safe(path);
+         if (fd < 0)
+-                return log_error_errno(errno, "Adjusting ACL of %s failed: %m", path);
++                return fd;
+ 
+-        if (fstat(fd, &st) < 0)
+-                return log_error_errno(errno, "Failed to fstat() file %s: %m", path);
+-
+-        r = fd_set_acls(item, fd, &st);
++        r = fd_set_acls(item, fd, NULL);
+  #endif
+          return r;
+  }
+@@ -1199,6 +1238,7 @@ static int fd_set_attribute(Item *item, int fd, const struct stat *st) {
+         char procfs_path[strlen("/proc/self/fd/") + DECIMAL_STR_MAX(int)];
+         _cleanup_close_ int procfs_fd = -1;
+         _cleanup_free_ char *path = NULL;
++        struct stat stbuf;
+         unsigned f;
+         int r;
+ 
+@@ -1209,6 +1249,12 @@ static int fd_set_attribute(Item *item, int fd, const struct stat *st) {
+         if (r < 0)
+                 return r;
+ 
++        if (!st) {
++                if (fstat(fd, &stbuf) < 0)
++                        return log_error_errno(errno, "fstat(%s) failed: %m", path);
++                st = &stbuf;
++        }
++
+         /* Issuing the file attribute ioctls on device nodes is not
+          * safe, as that will be delivered to the drivers, not the
+          * file system containing the device node. */
+@@ -1241,99 +1287,558 @@ static int fd_set_attribute(Item *item, int fd, const struct stat *st) {
+ 
+ static int path_set_attribute(Item *item, const char *path) {
+         _cleanup_close_ int fd = -1;
+-        struct stat st;
+ 
+         if (!item->attribute_set || item->attribute_mask == 0)
+                 return 0;
+ 
+-        fd = open(path, O_CLOEXEC|O_NOFOLLOW|O_PATH);
++        fd = path_open_safe(path);
+         if (fd < 0)
+-                return log_error_errno(errno, "Cannot open '%s': %m", path);
+-
+-        if (fstat(fd, &st) < 0)
+-                return log_error_errno(errno, "Cannot stat '%s': %m", path);
++                return fd;
+ 
+-        return fd_set_attribute(item, fd, &st);
++        return fd_set_attribute(item, fd, NULL);
+ }
+ 
+ static int write_one_file(Item *i, const char *path) {
+-        _cleanup_close_ int fd = -1;
+-        int flags, r = 0;
+-        struct stat st;
++        _cleanup_close_ int fd = -1, dir_fd = -1;
++        char *bn;
++        int r;
++
++        assert(i);
++        assert(path);
++        assert(i->argument);
++        assert(i->type == WRITE_FILE);
++
++        /* Validate the path and keep the fd on the directory for opening the
++         * file so we're sure that it can't be changed behind our back. */
++        dir_fd = path_open_parent_safe(path);
++        if (dir_fd < 0)
++                return dir_fd;
++
++        bn = basename(path);
++
++        /* Follows symlinks */
++        fd = openat(dir_fd, bn, O_NONBLOCK|O_CLOEXEC|O_WRONLY|O_NOCTTY, i->mode);
++        if (fd < 0) {
++                if (errno == ENOENT) {
++                        log_debug_errno(errno, "Not writing missing file \"%s\": %m", path);
++                        return 0;
++                }
++                return log_error_errno(errno, "Failed to open file \"%s\": %m", path);
++        }
++
++        /* 'w' is allowed to write into any kind of files. */
++        log_debug("Writing to \"%s\".", path);
++
++        r = loop_write(fd, i->argument, strlen(i->argument), false);
++        if (r < 0)
++                return log_error_errno(r, "Failed to write file \"%s\": %m", path);
++
++        return fd_set_perms(i, fd, NULL);
++}
++
++static int create_file(Item *i, const char *path) {
++        _cleanup_close_ int fd = -1, dir_fd = -1;
++        struct stat stbuf, *st = NULL;
++        int r = 0;
++        char *bn;
+ 
+         assert(i);
+         assert(path);
++        assert(i->type == CREATE_FILE);
++
++        /* 'f' operates on regular files exclusively. */
+ 
+-        flags = i->type == CREATE_FILE ? O_CREAT|O_EXCL|O_NOFOLLOW :
+-                i->type == TRUNCATE_FILE ? O_CREAT|O_TRUNC|O_NOFOLLOW : 0;
++        /* Validate the path and keep the fd on the directory for opening the
++         * file so we're sure that it can't be changed behind our back. */
++        dir_fd = path_open_parent_safe(path);
++        if (dir_fd < 0)
++                return dir_fd;
++
++        bn = basename(path);
+ 
+         RUN_WITH_UMASK(0000) {
+                 mac_selinux_create_file_prepare(path, S_IFREG);
+-                fd = open(path, flags|O_NDELAY|O_CLOEXEC|O_WRONLY|O_NOCTTY, i->mode);
++                fd = openat(dir_fd, bn, O_CREAT|O_EXCL|O_NOFOLLOW|O_NONBLOCK|O_CLOEXEC|O_WRONLY|O_NOCTTY, i->mode);
+                 mac_selinux_create_file_clear();
+         }
+ 
+         if (fd < 0) {
+-                if (i->type == WRITE_FILE && errno == ENOENT) {
+-                        log_debug_errno(errno, "Not writing missing file \"%s\": %m", path);
+-                        return 0;
++                /* Even on a read-only filesystem, open(2) returns EEXIST if the
++                 * file already exists. It returns EROFS only if it needs to
++                 * create the file. */
++                if (errno != EEXIST)
++                        return log_error_errno(errno, "Failed to create file %s: %m", path);
++
++                /* Re-open the file. At that point it must exist since open(2)
++                 * failed with EEXIST. We still need to check if the perms/mode
++                 * need to be changed. For read-only filesystems, we let
++                 * fd_set_perms() report the error if the perms need to be
++                 * modified. */
++                fd = openat(dir_fd, bn, O_NOFOLLOW|O_CLOEXEC|O_PATH, i->mode);
++                if (fd < 0)
++                        return log_error_errno(errno, "Failed to re-open file %s: %m", path);
++
++                if (fstat(fd, &stbuf) < 0)
++                        return log_error_errno(errno, "stat(%s) failed: %m", path);
++
++                if (!S_ISREG(stbuf.st_mode)) {
++                        log_error("%s exists and is not a regular file.", path);
++                        return -EEXIST;
+                 }
+-                if (i->type == CREATE_FILE && errno == EEXIST) {
+-                        log_debug_errno(errno, "Not writing to pre-existing file \"%s\": %m", path);
+-                        goto done;
++
++                st = &stbuf;
++        } else {
++
++                log_debug("\"%s\" has been created.", path);
++
++                if (i->argument) {
++                        log_debug("Writing to \"%s\".", path);
++
++                        r = loop_write(fd, i->argument, strlen(i->argument), false);
++                        if (r < 0)
++                                return log_error_errno(r, "Failed to write file \"%s\": %m", path);
+                 }
++        }
+ 
+-                r = -errno;
+-                if (!i->argument && errno == EROFS && stat(path, &st) == 0 &&
+-                    (i->type == CREATE_FILE || st.st_size == 0))
+-                        goto check_mode;
++        return fd_set_perms(i, fd, st);
++}
+ 
+-                return log_error_errno(r, "Failed to create file %s: %m", path);
++static int truncate_file(Item *i, const char *path) {
++        _cleanup_close_ int fd = -1, dir_fd = -1;
++        struct stat stbuf, *st = NULL;
++        bool erofs = false;
++        int r = 0;
++        char *bn;
++
++        assert(i);
++        assert(path);
++        assert(i->type == TRUNCATE_FILE);
++
++        /* We want to operate on regular file exclusively especially since
++         * O_TRUNC is unspecified if the file is neither a regular file nor a
++         * fifo nor a terminal device. Therefore we first open the file and make
++         * sure it's a regular one before truncating it. */
++
++        /* Validate the path and keep the fd on the directory for opening the
++         * file so we're sure that it can't be changed behind our back. */
++        dir_fd = path_open_parent_safe(path);
++        if (dir_fd < 0)
++                return dir_fd;
++
++        bn = basename(path);
++
++        RUN_WITH_UMASK(0000) {
++                mac_selinux_create_file_prepare(path, S_IFREG);
++                fd = openat(dir_fd, bn, O_CREAT|O_NOFOLLOW|O_NONBLOCK|O_CLOEXEC|O_WRONLY|O_NOCTTY, i->mode);
++                mac_selinux_create_file_clear();
+         }
+ 
+-        if (i->argument) {
+-                log_debug("%s to \"%s\".", i->type == CREATE_FILE ? "Appending" : "Writing", path);
++        if (fd < 0) {
++                if (errno != EROFS)
++                        return log_error_errno(errno, "Failed to open/create file %s: %m", path);
+ 
+-                r = loop_write(fd, i->argument, strlen(i->argument), false);
+-                if (r < 0)
+-                        return log_error_errno(r, "Failed to write file \"%s\": %m", path);
+-        } else
+-                log_debug("\"%s\" has been created.", path);
++                /* On a read-only filesystem, we don't want to fail if the
++                 * target is already empty and the perms are set. So we still
++                 * proceed with the sanity checks and let the remaining
++                 * operations fail with EROFS if they try to modify the target
++                 * file. */
++
++                fd = openat(dir_fd, bn, O_NOFOLLOW|O_CLOEXEC|O_PATH, i->mode);
++                if (fd < 0) {
++                        if (errno == ENOENT) {
++                                log_error("Cannot create file %s on a read-only file system.", path);
++                                return -EROFS;
++                        }
+ 
+-        fd = safe_close(fd);
++                        return log_error_errno(errno, "Failed to re-open file %s: %m", path);
++                }
+ 
+-done:
+-        if (stat(path, &st) < 0)
++                erofs = true;
++        }
++
++        if (fstat(fd, &stbuf) < 0)
+                 return log_error_errno(errno, "stat(%s) failed: %m", path);
+ 
+- check_mode:
+-        if (!S_ISREG(st.st_mode)) {
+-                log_error("%s is not a file.", path);
++        if (!S_ISREG(stbuf.st_mode)) {
++                log_error("%s exists and is not a regular file.", path);
+                 return -EEXIST;
+         }
+ 
+-        r = path_set_perms(i, path);
++        if (stbuf.st_size > 0) {
++                if (ftruncate(fd, 0) < 0) {
++                        r = erofs ? -EROFS : -errno;
++                        return log_error_errno(r, "Failed to truncate file %s: %m", path);
++                }
++        } else
++                st = &stbuf;
++
++        log_debug("\"%s\" has been created.", path);
++
++        if (i->argument) {
++                log_debug("Writing to \"%s\".", path);
++
++                r = loop_write(fd, i->argument, strlen(i->argument), false);
++                if (r < 0) {
++                        r = erofs ? -EROFS : r;
++                        return log_error_errno(r, "Failed to write file %s: %m", path);
++                }
++        }
++
++        return fd_set_perms(i, fd, st);
++}
++
++static int copy_files(Item *i) {
++        _cleanup_close_ int dfd = -1, fd = -1;
++        char *bn;
++        int r;
++
++        log_debug("Copying tree \"%s\" to \"%s\".", i->argument, i->path);
++
++        bn = basename(i->path);
++
++        /* Validate the path and use the returned directory fd for copying the
++         * target so we're sure that the path can't be changed behind our
++         * back. */
++        dfd = path_open_parent_safe(i->path);
++        if (dfd < 0)
++                return dfd;
++
++        r = copy_tree_at(AT_FDCWD, i->argument,
++                         dfd, bn,
++                         i->uid_set ? i->uid : UID_INVALID,
++                         i->gid_set ? i->gid : GID_INVALID,
++                         COPY_REFLINK);
++        if (r < 0) {
++                struct stat a, b;
++
++                /* If the target already exists on read-only filesystems, trying
++                 * to create the target will not fail with EEXIST but with
++                 * EROFS. */
++                if (r == -EROFS && faccessat(dfd, bn, F_OK, AT_SYMLINK_NOFOLLOW) == 0)
++                        r = -EEXIST;
++
++                if (r != -EEXIST)
++                        return log_error_errno(r, "Failed to copy files to %s: %m", i->path);
++
++                if (stat(i->argument, &a) < 0)
++                        return log_error_errno(errno, "stat(%s) failed: %m", i->argument);
++
++                if (fstatat(dfd, bn, &b, AT_SYMLINK_NOFOLLOW) < 0)
++                        return log_error_errno(errno, "stat(%s) failed: %m", i->path);
++
++                if ((a.st_mode ^ b.st_mode) & S_IFMT) {
++                        log_debug("Can't copy to %s, file exists already and is of different type", i->path);
++                        return 0;
++                }
++        }
++
++        fd = openat(dfd, bn, O_NOFOLLOW|O_CLOEXEC|O_PATH);
++        if (fd < 0)
++                return log_error_errno(errno, "Failed to openat(%s): %m", i->path);
++
++        return fd_set_perms(i, fd, NULL);
++}
++
++typedef enum {
++        CREATION_NORMAL,
++        CREATION_EXISTING,
++        CREATION_FORCE,
++        _CREATION_MODE_MAX,
++        _CREATION_MODE_INVALID = -1
++} CreationMode;
++
++static const char *creation_mode_verb_table[_CREATION_MODE_MAX] = {
++        [CREATION_NORMAL] = "Created",
++        [CREATION_EXISTING] = "Found existing",
++        [CREATION_FORCE] = "Created replacement",
++};
++
++DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(creation_mode_verb, CreationMode);
++
++static int create_directory_or_subvolume(const char *path, mode_t mode, bool subvol) {
++        _cleanup_close_ int pfd = -1;
++        CreationMode creation;
++        int r;
++
++        assert(path);
++
++        pfd = path_open_parent_safe(path);
++        if (pfd < 0)
++                return pfd;
++
++        if (subvol) {
++                if (btrfs_is_subvol(empty_to_root(arg_root)) <= 0)
++
++                        /* Don't create a subvolume unless the root directory is
++                         * one, too. We do this under the assumption that if the
++                         * root directory is just a plain directory (i.e. very
++                         * light-weight), we shouldn't try to split it up into
++                         * subvolumes (i.e. more heavy-weight). Thus, chroot()
++                         * environments and suchlike will get a full brtfs
++                         * subvolume set up below their tree only if they
++                         * specifically set up a btrfs subvolume for the root
++                         * dir too. */
++
++                        subvol = false;
++                else {
++                        RUN_WITH_UMASK((~mode) & 0777)
++                                r = btrfs_subvol_make_fd(pfd, basename(path));
++                }
++        } else
++                r = 0;
++
++        if (!subvol || r == -ENOTTY)
++                RUN_WITH_UMASK(0000)
++                        r = mkdirat_label(pfd, basename(path), mode);
++
++        if (r < 0) {
++                int k;
++
++                if (!IN_SET(r, -EEXIST, -EROFS))
++                        return log_error_errno(r, "Failed to create directory or subvolume \"%s\": %m", path);
++
++                k = is_dir_fd(pfd);
++                if (k == -ENOENT && r == -EROFS)
++                        return log_error_errno(r, "%s does not exist and cannot be created as the file system is read-only.", path);
++                if (k < 0)
++                        return log_error_errno(k, "Failed to check if %s exists: %m", path);
++                if (!k) {
++                        log_warning("\"%s\" already exists and is not a directory.", path);
++                        return -EEXIST;
++                }
++
++                creation = CREATION_EXISTING;
++        } else
++                creation = CREATION_NORMAL;
++
++        log_debug("%s directory \"%s\".", creation_mode_verb_to_string(creation), path);
++
++        r = openat(pfd, basename(path), O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
+         if (r < 0)
+-                return r;
++                return -errno;
++        return r;
++}
+ 
+-        return 0;
++static int create_directory(Item *i, const char *path) {
++        _cleanup_close_ int fd = -1;
++
++        assert(i);
++        assert(IN_SET(i->type, CREATE_DIRECTORY, TRUNCATE_DIRECTORY));
++
++        fd = create_directory_or_subvolume(path, i->mode, false);
++        if (fd == -EEXIST)
++                return 0;
++        if (fd < 0)
++                return fd;
++
++        return fd_set_perms(i, fd, NULL);
++}
++
++static int create_subvolume(Item *i, const char *path) {
++        _cleanup_close_ int fd = -1;
++        int r, q = 0;
++
++        assert(i);
++        assert(IN_SET(i->type, CREATE_SUBVOLUME, CREATE_SUBVOLUME_NEW_QUOTA, CREATE_SUBVOLUME_INHERIT_QUOTA));
++
++        fd = create_directory_or_subvolume(path, i->mode, true);
++        if (fd == -EEXIST)
++                return 0;
++        if (fd < 0)
++                return fd;
++
++        if (IN_SET(i->type, CREATE_SUBVOLUME_NEW_QUOTA, CREATE_SUBVOLUME_INHERIT_QUOTA)) {
++                r = btrfs_subvol_auto_qgroup_fd(fd, 0, i->type == CREATE_SUBVOLUME_NEW_QUOTA);
++                if (r == -ENOTTY)
++                        log_debug_errno(r, "Couldn't adjust quota for subvolume \"%s\" (unsupported fs or dir not a subvolume): %m", i->path);
++                else if (r == -EROFS)
++                        log_debug_errno(r, "Couldn't adjust quota for subvolume \"%s\" (fs is read-only).", i->path);
++                else if (r == -ENOPROTOOPT)
++                        log_debug_errno(r, "Couldn't adjust quota for subvolume \"%s\" (quota support is disabled).", i->path);
++                else if (r < 0)
++                        q = log_error_errno(r, "Failed to adjust quota for subvolume \"%s\": %m", i->path);
++                else if (r > 0)
++                        log_debug("Adjusted quota for subvolume \"%s\".", i->path);
++                else if (r == 0)
++                        log_debug("Quota for subvolume \"%s\" already in place, no change made.", i->path);
++        }
++
++        r = fd_set_perms(i, fd, NULL);
++        if (q < 0)
++                return q;
++
++        return r;
++}
++
++static int empty_directory(Item *i, const char *path) {
++        int r;
++
++        assert(i);
++        assert(i->type == EMPTY_DIRECTORY);
++
++        r = is_dir(path, false);
++        if (r == -ENOENT) {
++                /* Option "e" operates only on existing objects. Do not
++                 * print errors about non-existent files or directories */
++                log_debug("Skipping missing directory: %s", path);
++                return 0;
++        }
++        if (r < 0)
++                return log_error_errno(r, "is_dir() failed on path %s: %m", path);
++
++        return path_set_perms(i, path);
++}
++
++static int create_device(Item *i, mode_t file_type) {
++        _cleanup_close_ int dfd = -1, fd = -1;
++        CreationMode creation;
++        char *bn;
++        int r;
++
++        assert(i);
++        assert(IN_SET(file_type, S_IFBLK, S_IFCHR));
++
++        bn = basename(i->path);
++
++        /* Validate the path and use the returned directory fd for copying the
++         * target so we're sure that the path can't be changed behind our
++         * back. */
++        dfd = path_open_parent_safe(i->path);
++        if (dfd < 0)
++                return dfd;
++
++        RUN_WITH_UMASK(0000) {
++                mac_selinux_create_file_prepare(i->path, file_type);
++                r = mknodat(dfd, bn, i->mode | file_type, i->major_minor);
++                mac_selinux_create_file_clear();
++        }
++
++        if (r < 0) {
++                struct stat st;
++
++                if (errno == EPERM) {
++                        log_debug("We lack permissions, possibly because of cgroup configuration; "
++                                  "skipping creation of device node %s.", i->path);
++                        return 0;
++                }
++
++                if (errno != EEXIST)
++                        return log_error_errno(errno, "Failed to create device node %s: %m", i->path);
++
++                if (fstatat(dfd, bn, &st, 0) < 0)
++                        return log_error_errno(errno, "stat(%s) failed: %m", i->path);
++
++                if ((st.st_mode & S_IFMT) != file_type) {
++
++                        if (i->force) {
++
++                                RUN_WITH_UMASK(0000) {
++                                        mac_selinux_create_file_prepare(i->path, file_type);
++                                        /* FIXME: need to introduce mknodat_atomic() */
++                                        r = mknod_atomic(i->path, i->mode | file_type, i->major_minor);
++                                        mac_selinux_create_file_clear();
++                                }
++
++                                if (r < 0)
++                                        return log_error_errno(r, "Failed to create device node \"%s\": %m", i->path);
++                                creation = CREATION_FORCE;
++                        } else {
++                                log_debug("%s is not a device node.", i->path);
++                                return 0;
++                        }
++                } else
++                        creation = CREATION_EXISTING;
++        } else
++                creation = CREATION_NORMAL;
++
++        log_debug("%s %s device node \"%s\" %u:%u.",
++                  creation_mode_verb_to_string(creation),
++                  i->type == CREATE_BLOCK_DEVICE ? "block" : "char",
++                  i->path, major(i->mode), minor(i->mode));
++
++        fd = openat(dfd, bn, O_NOFOLLOW|O_CLOEXEC|O_PATH);
++        if (fd < 0)
++                return log_error_errno(errno, "Failed to openat(%s): %m", i->path);
++
++        return fd_set_perms(i, fd, NULL);
++}
++
++static int create_fifo(Item *i, const char *path) {
++        _cleanup_close_ int pfd = -1, fd = -1;
++        CreationMode creation;
++        struct stat st;
++        char *bn;
++        int r;
++
++        pfd = path_open_parent_safe(path);
++        if (pfd < 0)
++                return pfd;
++
++        bn = basename(path);
++
++        RUN_WITH_UMASK(0000) {
++                mac_selinux_create_file_prepare(path, S_IFIFO);
++                r = mkfifoat(pfd, bn, i->mode);
++                mac_selinux_create_file_clear();
++        }
++
++        if (r < 0) {
++                if (errno != EEXIST)
++                        return log_error_errno(errno, "Failed to create fifo %s: %m", path);
++
++                if (fstatat(pfd, bn, &st, AT_SYMLINK_NOFOLLOW) < 0)
++                        return log_error_errno(errno, "stat(%s) failed: %m", path);
++
++                if (!S_ISFIFO(st.st_mode)) {
++
++                        if (i->force) {
++                                RUN_WITH_UMASK(0000) {
++                                        mac_selinux_create_file_prepare(path, S_IFIFO);
++                                        r = mkfifoat_atomic(pfd, bn, i->mode);
++                                        mac_selinux_create_file_clear();
++                                }
++
++                                if (r < 0)
++                                        return log_error_errno(r, "Failed to create fifo %s: %m", path);
++                                creation = CREATION_FORCE;
++                        } else {
++                                log_warning("\"%s\" already exists and is not a fifo.", path);
++                                return 0;
++                        }
++                } else
++                        creation = CREATION_EXISTING;
++        } else
++                creation = CREATION_NORMAL;
++
++        log_debug("%s fifo \"%s\".", creation_mode_verb_to_string(creation), path);
++
++        fd = openat(pfd, bn, O_NOFOLLOW|O_CLOEXEC|O_PATH);
++        if (fd < 0)
++                return log_error_errno(fd, "Failed to openat(%s): %m", path);
++
++        return fd_set_perms(i, fd, NULL);
+ }
+ 
+ typedef int (*action_t)(Item *, const char *);
+ typedef int (*fdaction_t)(Item *, int fd, const struct stat *st);
+ 
+-static int item_do(Item *i, int fd, const struct stat *st, fdaction_t action) {
++static int item_do(Item *i, int fd, fdaction_t action) {
++        struct stat st;
+         int r = 0, q;
+ 
+         assert(i);
+         assert(fd >= 0);
+-        assert(st);
++
++        if (fstat(fd, &st) < 0) {
++                r = -errno;
++                goto finish;
++        }
+ 
+         /* This returns the first error we run into, but nevertheless
+          * tries to go on */
+-        r = action(i, fd, st);
++        r = action(i, fd, &st);
+ 
+-        if (S_ISDIR(st->st_mode)) {
++        if (S_ISDIR(st.st_mode)) {
+                 char procfs_path[strlen("/proc/self/fd/") + DECIMAL_STR_MAX(int)];
+                 _cleanup_closedir_ DIR *d = NULL;
+                 struct dirent *de;
+@@ -1349,16 +1854,15 @@ static int item_do(Item *i, int fd, const struct stat *st, fdaction_t action) {
+                 }
+ 
+                 FOREACH_DIRENT_ALL(de, d, q = -errno; goto finish) {
+-                        struct stat de_st;
+                         int de_fd;
+ 
+                         if (dot_or_dot_dot(de->d_name))
+                                 continue;
+ 
+                         de_fd = openat(fd, de->d_name, O_NOFOLLOW|O_CLOEXEC|O_PATH);
+-                        if (de_fd >= 0 && fstat(de_fd, &de_st) >= 0)
++                        if (de_fd >= 0)
+                                 /* pass ownership of dirent fd over  */
+-                                q = item_do(i, de_fd, &de_st, action);
++                                q = item_do(i, de_fd, action);
+                         else
+                                 q = -errno;
+ 
+@@ -1406,7 +1910,6 @@ static int glob_item_recursively(Item *i, fdaction_t action) {
+ 
+         STRV_FOREACH(fn, g.gl_pathv) {
+                 _cleanup_close_ int fd = -1;
+-                struct stat st;
+ 
+                 /* Make sure we won't trigger/follow file object (such as
+                  * device nodes, automounts, ...) pointed out by 'fn' with
+@@ -1419,12 +1922,7 @@ static int glob_item_recursively(Item *i, fdaction_t action) {
+                         continue;
+                 }
+ 
+-                if (fstat(fd, &st) < 0) {
+-                        r = r ?: -errno;
+-                        continue;
+-                }
+-
+-                k = item_do(i, fd, &st, action);
++                k = item_do(i, fd, action);
+                 if (k < 0 && r == 0)
+                         r = k;
+ 
+@@ -1435,27 +1933,9 @@ static int glob_item_recursively(Item *i, fdaction_t action) {
+         return r;
+ }
+ 
+-typedef enum {
+-        CREATION_NORMAL,
+-        CREATION_EXISTING,
+-        CREATION_FORCE,
+-        _CREATION_MODE_MAX,
+-        _CREATION_MODE_INVALID = -1
+-} CreationMode;
+-
+-static const char *creation_mode_verb_table[_CREATION_MODE_MAX] = {
+-        [CREATION_NORMAL] = "Created",
+-        [CREATION_EXISTING] = "Found existing",
+-        [CREATION_FORCE] = "Created replacement",
+-};
+-
+-DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(creation_mode_verb, CreationMode);
+-
+ static int create_item(Item *i) {
+-        struct stat st;
+-        int r = 0;
+-        int q = 0;
+         CreationMode creation;
++        int r = 0;
+ 
+         assert(i);
+ 
+@@ -1470,51 +1950,31 @@ static int create_item(Item *i) {
+                 return 0;
+ 
+         case CREATE_FILE:
+-        case TRUNCATE_FILE:
+                 RUN_WITH_UMASK(0000)
+                         (void) mkdir_parents_label(i->path, 0755);
+ 
+-                r = write_one_file(i, i->path);
++                r = create_file(i, i->path);
+                 if (r < 0)
+                         return r;
+                 break;
+ 
+-        case COPY_FILES: {
+-
++        case TRUNCATE_FILE:
+                 RUN_WITH_UMASK(0000)
+                         (void) mkdir_parents_label(i->path, 0755);
+ 
+-                log_debug("Copying tree \"%s\" to \"%s\".", i->argument, i->path);
+-                r = copy_tree(i->argument, i->path,
+-                              i->uid_set ? i->uid : UID_INVALID,
+-                              i->gid_set ? i->gid : GID_INVALID,
+-                              COPY_REFLINK);
+-
+-                if (r == -EROFS && stat(i->path, &st) == 0)
+-                        r = -EEXIST;
+-
+-                if (r < 0) {
+-                        struct stat a, b;
+-
+-                        if (r != -EEXIST)
+-                                return log_error_errno(r, "Failed to copy files to %s: %m", i->path);
+-
+-                        if (stat(i->argument, &a) < 0)
+-                                return log_error_errno(errno, "stat(%s) failed: %m", i->argument);
++                r = truncate_file(i, i->path);
++                if (r < 0)
++                        return r;
++                break;
+ 
+-                        if (stat(i->path, &b) < 0)
+-                                return log_error_errno(errno, "stat(%s) failed: %m", i->path);
++        case COPY_FILES: {
+ 
+-                        if ((a.st_mode ^ b.st_mode) & S_IFMT) {
+-                                log_debug("Can't copy to %s, file exists already and is of different type", i->path);
+-                                return 0;
+-                        }
+-                }
++                RUN_WITH_UMASK(0000)
++                        (void) mkdir_parents_label(i->path, 0755);
+ 
+-                r = path_set_perms(i, i->path);
++                r = copy_files(i);
+                 if (r < 0)
+                         return r;
+-
+                 break;
+ 
+         case WRITE_FILE:
+@@ -1526,132 +1986,39 @@ static int create_item(Item *i) {
+ 
+         case CREATE_DIRECTORY:
+         case TRUNCATE_DIRECTORY:
++                RUN_WITH_UMASK(0000)
++                        (void) mkdir_parents_label(i->path, 0755);
++
++                r = create_directory(i, i->path);
++                if (r < 0)
++                        return r;
++                break;
++
+         case CREATE_SUBVOLUME:
+         case CREATE_SUBVOLUME_INHERIT_QUOTA:
+         case CREATE_SUBVOLUME_NEW_QUOTA:
+                 RUN_WITH_UMASK(0000)
+                         (void) mkdir_parents_label(i->path, 0755);
+ 
+-                if (IN_SET(i->type, CREATE_SUBVOLUME, CREATE_SUBVOLUME_INHERIT_QUOTA, CREATE_SUBVOLUME_NEW_QUOTA)) {
+-
+-                        if (btrfs_is_subvol(isempty(arg_root) ? "/" : arg_root) <= 0)
+-
+-                                /* Don't create a subvolume unless the
+-                                 * root directory is one, too. We do
+-                                 * this under the assumption that if
+-                                 * the root directory is just a plain
+-                                 * directory (i.e. very light-weight),
+-                                 * we shouldn't try to split it up
+-                                 * into subvolumes (i.e. more
+-                                 * heavy-weight). Thus, chroot()
+-                                 * environments and suchlike will get
+-                                 * a full brtfs subvolume set up below
+-                                 * their tree only if they
+-                                 * specifically set up a btrfs
+-                                 * subvolume for the root dir too. */
+-
+-                                r = -ENOTTY;
+-                        else {
+-                                RUN_WITH_UMASK((~i->mode) & 0777)
+-                                        r = btrfs_subvol_make(i->path);
+-                        }
+-                } else
+-                        r = 0;
+-
+-                if (IN_SET(i->type, CREATE_DIRECTORY, TRUNCATE_DIRECTORY) || r == -ENOTTY)
+-                        RUN_WITH_UMASK(0000)
+-                                r = mkdir_label(i->path, i->mode);
+-
+-                if (r < 0) {
+-                        int k;
+-
+-                        if (!IN_SET(r, -EEXIST, -EROFS))
+-                                return log_error_errno(r, "Failed to create directory or subvolume \"%s\": %m", i->path);
+-
+-                        k = is_dir(i->path, false);
+-                        if (k == -ENOENT && r == -EROFS)
+-                                return log_error_errno(r, "%s does not exist and cannot be created as the file system is read-only.", i->path);
+-                        if (k < 0)
+-                                return log_error_errno(k, "Failed to check if %s exists: %m", i->path);
+-                        if (!k) {
+-                                log_warning("\"%s\" already exists and is not a directory.", i->path);
+-                                return 0;
+-                        }
+-
+-                        creation = CREATION_EXISTING;
+-                } else
+-                        creation = CREATION_NORMAL;
+-
+-                log_debug("%s directory \"%s\".", creation_mode_verb_to_string(creation), i->path);
+-
+-                if (IN_SET(i->type, CREATE_SUBVOLUME_NEW_QUOTA, CREATE_SUBVOLUME_INHERIT_QUOTA)) {
+-                        r = btrfs_subvol_auto_qgroup(i->path, 0, i->type == CREATE_SUBVOLUME_NEW_QUOTA);
+-                        if (r == -ENOTTY)
+-                                log_debug_errno(r, "Couldn't adjust quota for subvolume \"%s\" (unsupported fs or dir not a subvolume): %m", i->path);
+-                        else if (r == -EROFS)
+-                                log_debug_errno(r, "Couldn't adjust quota for subvolume \"%s\" (fs is read-only).", i->path);
+-                        else if (r == -ENOPROTOOPT)
+-                                log_debug_errno(r, "Couldn't adjust quota for subvolume \"%s\" (quota support is disabled).", i->path);
+-                        else if (r < 0)
+-                                q = log_error_errno(r, "Failed to adjust quota for subvolume \"%s\": %m", i->path);
+-                        else if (r > 0)
+-                                log_debug("Adjusted quota for subvolume \"%s\".", i->path);
+-                        else if (r == 0)
+-                                log_debug("Quota for subvolume \"%s\" already in place, no change made.", i->path);
+-                }
++                r = create_subvolume(i, i->path);
++                if (r < 0)
++                        return r;
++                break;
+ 
+-                _fallthrough_;
+         case EMPTY_DIRECTORY:
+-                r = path_set_perms(i, i->path);
+-                if (q < 0)
+-                        return q;
++                r = empty_directory(i, i->path);
+                 if (r < 0)
+                         return r;
+ 
+                 break;
+ 
+         case CREATE_FIFO:
+-                RUN_WITH_UMASK(0000) {
++                RUN_WITH_UMASK(0000)
+                         (void) mkdir_parents_label(i->path, 0755);
+ 
+-                        mac_selinux_create_file_prepare(i->path, S_IFIFO);
+-                        r = mkfifo(i->path, i->mode);
+-                        mac_selinux_create_file_clear();
+-                }
+-
+-                if (r < 0) {
+-                        if (errno != EEXIST)
+-                                return log_error_errno(errno, "Failed to create fifo %s: %m", i->path);
+-
+-                        if (lstat(i->path, &st) < 0)
+-                                return log_error_errno(errno, "stat(%s) failed: %m", i->path);
+-
+-                        if (!S_ISFIFO(st.st_mode)) {
+-
+-                                if (i->force) {
+-                                        RUN_WITH_UMASK(0000) {
+-                                                mac_selinux_create_file_prepare(i->path, S_IFIFO);
+-                                                r = mkfifo_atomic(i->path, i->mode);
+-                                                mac_selinux_create_file_clear();
+-                                        }
+-
+-                                        if (r < 0)
+-                                                return log_error_errno(r, "Failed to create fifo %s: %m", i->path);
+-                                        creation = CREATION_FORCE;
+-                                } else {
+-                                        log_warning("\"%s\" already exists and is not a fifo.", i->path);
+-                                        return 0;
+-                                }
+-                        } else
+-                                creation = CREATION_EXISTING;
+-                } else
+-                        creation = CREATION_NORMAL;
+-                log_debug("%s fifo \"%s\".", creation_mode_verb_to_string(creation), i->path);
+-
+-                r = path_set_perms(i, i->path);
++                r = create_fifo(i, i->path);
+                 if (r < 0)
+                         return r;
+-
+                 break;
+         }
+ 
+@@ -1704,9 +2071,7 @@ static int create_item(Item *i) {
+         }
+ 
+         case CREATE_BLOCK_DEVICE:
+-        case CREATE_CHAR_DEVICE: {
+-                mode_t file_type;
+-
++        case CREATE_CHAR_DEVICE:
+                 if (have_effective_cap(CAP_MKNOD) == 0) {
+                         /* In a container we lack CAP_MKNOD. We
+                         shouldn't attempt to create the device node in
+@@ -1720,60 +2085,11 @@ static int create_item(Item *i) {
+                 RUN_WITH_UMASK(0000)
+                         (void) mkdir_parents_label(i->path, 0755);
+ 
+-                file_type = i->type == CREATE_BLOCK_DEVICE ? S_IFBLK : S_IFCHR;
+-
+-                RUN_WITH_UMASK(0000) {
+-                        mac_selinux_create_file_prepare(i->path, file_type);
+-                        r = mknod(i->path, i->mode | file_type, i->major_minor);
+-                        mac_selinux_create_file_clear();
+-                }
+-
+-                if (r < 0) {
+-                        if (errno == EPERM) {
+-                                log_debug("We lack permissions, possibly because of cgroup configuration; "
+-                                          "skipping creation of device node %s.", i->path);
+-                                return 0;
+-                        }
+-
+-                        if (errno != EEXIST)
+-                                return log_error_errno(errno, "Failed to create device node %s: %m", i->path);
+-
+-                        if (lstat(i->path, &st) < 0)
+-                                return log_error_errno(errno, "stat(%s) failed: %m", i->path);
+-
+-                        if ((st.st_mode & S_IFMT) != file_type) {
+-
+-                                if (i->force) {
+-
+-                                        RUN_WITH_UMASK(0000) {
+-                                                mac_selinux_create_file_prepare(i->path, file_type);
+-                                                r = mknod_atomic(i->path, i->mode | file_type, i->major_minor);
+-                                                mac_selinux_create_file_clear();
+-                                        }
+-
+-                                        if (r < 0)
+-                                                return log_error_errno(r, "Failed to create device node \"%s\": %m", i->path);
+-                                        creation = CREATION_FORCE;
+-                                } else {
+-                                        log_debug("%s is not a device node.", i->path);
+-                                        return 0;
+-                                }
+-                        } else
+-                                creation = CREATION_EXISTING;
+-                } else
+-                        creation = CREATION_NORMAL;
+-
+-                log_debug("%s %s device node \"%s\" %u:%u.",
+-                          creation_mode_verb_to_string(creation),
+-                          i->type == CREATE_BLOCK_DEVICE ? "block" : "char",
+-                          i->path, major(i->mode), minor(i->mode));
+-
+-                r = path_set_perms(i, i->path);
++                r = create_device(i, i->type == CREATE_BLOCK_DEVICE ? S_IFBLK : S_IFCHR);
+                 if (r < 0)
+                         return r;
+ 
+                 break;
+-        }
+ 
+         case ADJUST_MODE:
+         case RELABEL_PATH:
+-- 
+2.11.0
+
diff --git a/meta/recipes-core/systemd/systemd_237.bb b/meta/recipes-core/systemd/systemd_237.bb
index b53221896f..1d9fcf0bd7 100644
--- a/meta/recipes-core/systemd/systemd_237.bb
+++ b/meta/recipes-core/systemd/systemd_237.bb
@@ -61,6 +61,8 @@ SRC_URI += "file://touchscreen.rules \
            file://0025-journald-set-a-limit-on-the-number-of-fields-1k.patch \
            file://0026-journal-remote-set-a-limit-on-the-number-of-fields-i.patch \
            file://0027-journal-fix-out-of-bounds-read-CVE-2018-16866.patch \
+           file://0001-tmpfiles-don-t-resolve-pathnames-when-traversing-rec.patch \
+           file://0002-Make-tmpfiles-safe.patch \
            "
 SRC_URI_append_qemuall = " file://0001-core-device.c-Change-the-default-device-timeout-to-2.patch"
 
-- 
2.11.0



^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [sumo][PATCH 8/8] systemd: fix CVE-2019-6454
  2019-02-25 16:37 [sumo][PATCH 0/8] systemd: fix CVEs George McCollister
                   ` (6 preceding siblings ...)
  2019-02-25 16:37 ` [sumo][PATCH 7/8] systemd: fix CVE-2018-6954 George McCollister
@ 2019-02-25 16:37 ` George McCollister
  7 siblings, 0 replies; 9+ messages in thread
From: George McCollister @ 2019-02-25 16:37 UTC (permalink / raw)
  To: openembedded-core

Apply patches from systemd_237-3ubuntu10.13 to fix CVE-2019-6454.
CVE-2019-6454 is an issue in which systemd (PID1) can be crashed
with a specially formed D-Bus message.

For information see:
https://usn.ubuntu.com/3891-1/
https://git.launchpad.net/ubuntu/+source/systemd/commit/?h=applied/ubuntu/bionic-updates&id=d7584b894afcaa8a4a1abb69db2a9c81a6276e80

Signed-off-by: George McCollister <george.mccollister@gmail.com>
---
 .../systemd/systemd/CVE-2019-6454.patch            | 210 +++++++++++++++++++++
 ...e-receive-an-invalid-dbus-message-ignore-.patch |  61 ++++++
 meta/recipes-core/systemd/systemd_237.bb           |   2 +
 3 files changed, 273 insertions(+)
 create mode 100644 meta/recipes-core/systemd/systemd/CVE-2019-6454.patch
 create mode 100644 meta/recipes-core/systemd/systemd/sd-bus-if-we-receive-an-invalid-dbus-message-ignore-.patch

diff --git a/meta/recipes-core/systemd/systemd/CVE-2019-6454.patch b/meta/recipes-core/systemd/systemd/CVE-2019-6454.patch
new file mode 100644
index 0000000000..e3c67c166c
--- /dev/null
+++ b/meta/recipes-core/systemd/systemd/CVE-2019-6454.patch
@@ -0,0 +1,210 @@
+Description: sd-bus: enforce a size limit for dbus paths, and don't allocate
+ them on the stacka
+Forwarded: no
+
+Patch from: systemd_237-3ubuntu10.13
+
+For information see:
+https://usn.ubuntu.com/3891-1/
+https://git.launchpad.net/ubuntu/+source/systemd/commit/?h=applied/ubuntu/bionic-updates&id=d7584b894afcaa8a4a1abb69db2a9c81a6276e80
+
+CVE: CVE-2019-6454
+Upstream-Status: Backport
+
+Signed-off-by: George McCollister <george.mccollister@gmail.com>
+
+--- a/src/libsystemd/sd-bus/bus-internal.c
++++ b/src/libsystemd/sd-bus/bus-internal.c
+@@ -61,7 +61,7 @@
+         if (slash)
+                 return false;
+ 
+-        return true;
++        return (q - p) <= BUS_PATH_SIZE_MAX;
+ }
+ 
+ char* object_path_startswith(const char *a, const char *b) {
+--- a/src/libsystemd/sd-bus/bus-internal.h
++++ b/src/libsystemd/sd-bus/bus-internal.h
+@@ -339,6 +339,10 @@
+ 
+ #define BUS_MESSAGE_SIZE_MAX (64*1024*1024)
+ #define BUS_AUTH_SIZE_MAX (64*1024)
++/* Note that the D-Bus specification states that bus paths shall have no size limit. We enforce here one
++ * anyway, since truly unbounded strings are a security problem. The limit we pick is relatively large however,
++ * to not clash unnecessarily with real-life applications. */
++#define BUS_PATH_SIZE_MAX (64*1024)
+ 
+ #define BUS_CONTAINER_DEPTH 128
+ 
+--- a/src/libsystemd/sd-bus/bus-objects.c
++++ b/src/libsystemd/sd-bus/bus-objects.c
+@@ -1150,7 +1150,8 @@
+                 const char *path,
+                 sd_bus_error *error) {
+ 
+-        char *prefix;
++        _cleanup_free_ char *prefix = NULL;
++        size_t pl;
+         int r;
+ 
+         assert(bus);
+@@ -1166,7 +1167,12 @@
+                 return 0;
+ 
+         /* Second, add fallback vtables registered for any of the prefixes */
+-        prefix = alloca(strlen(path) + 1);
++        pl = strlen(path);
++        assert(pl <= BUS_PATH_SIZE_MAX);
++        prefix = new(char, pl + 1);
++        if (!prefix)
++                return -ENOMEM;
++
+         OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
+                 r = object_manager_serialize_path(bus, reply, prefix, path, true, error);
+                 if (r < 0)
+@@ -1362,6 +1368,7 @@
+ }
+ 
+ int bus_process_object(sd_bus *bus, sd_bus_message *m) {
++        _cleanup_free_ char *prefix = NULL;
+         int r;
+         size_t pl;
+         bool found_object = false;
+@@ -1386,9 +1393,12 @@
+         assert(m->member);
+ 
+         pl = strlen(m->path);
+-        do {
+-                char prefix[pl+1];
++        assert(pl <= BUS_PATH_SIZE_MAX);
++        prefix = new(char, pl + 1);
++        if (!prefix)
++                return -ENOMEM;
+ 
++        do {
+                 bus->nodes_modified = false;
+ 
+                 r = object_find_and_run(bus, m, m->path, false, &found_object);
+@@ -1516,9 +1526,15 @@
+ 
+         n = hashmap_get(bus->nodes, path);
+         if (!n) {
+-                char *prefix;
++                _cleanup_free_ char *prefix = NULL;
++                size_t pl;
++
++                pl = strlen(path);
++                assert(pl <= BUS_PATH_SIZE_MAX);
++                prefix = new(char, pl + 1);
++                if (!prefix)
++                        return -ENOMEM;
+ 
+-                prefix = alloca(strlen(path) + 1);
+                 OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
+                         n = hashmap_get(bus->nodes, prefix);
+                         if (n)
+@@ -2108,8 +2124,9 @@
+                 char **names) {
+ 
+         BUS_DONT_DESTROY(bus);
++        _cleanup_free_ char *prefix = NULL;
+         bool found_interface = false;
+-        char *prefix;
++        size_t pl;
+         int r;
+ 
+         assert_return(bus, -EINVAL);
+@@ -2128,6 +2145,12 @@
+         if (names && names[0] == NULL)
+                 return 0;
+ 
++        pl = strlen(path);
++        assert(pl <= BUS_PATH_SIZE_MAX);
++        prefix = new(char, pl + 1);
++        if (!prefix)
++                return -ENOMEM;
++
+         do {
+                 bus->nodes_modified = false;
+ 
+@@ -2137,7 +2160,6 @@
+                 if (bus->nodes_modified)
+                         continue;
+ 
+-                prefix = alloca(strlen(path) + 1);
+                 OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
+                         r = emit_properties_changed_on_interface(bus, prefix, path, interface, true, &found_interface, names);
+                         if (r != 0)
+@@ -2269,7 +2291,8 @@
+ 
+ static int object_added_append_all(sd_bus *bus, sd_bus_message *m, const char *path) {
+         _cleanup_set_free_ Set *s = NULL;
+-        char *prefix;
++        _cleanup_free_ char *prefix = NULL;
++        size_t pl;
+         int r;
+ 
+         assert(bus);
+@@ -2314,7 +2337,12 @@
+         if (bus->nodes_modified)
+                 return 0;
+ 
+-        prefix = alloca(strlen(path) + 1);
++        pl = strlen(path);
++        assert(pl <= BUS_PATH_SIZE_MAX);
++        prefix = new(char, pl + 1);
++        if (!prefix)
++                return -ENOMEM;
++
+         OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
+                 r = object_added_append_all_prefix(bus, m, s, prefix, path, true);
+                 if (r < 0)
+@@ -2453,7 +2481,8 @@
+ 
+ static int object_removed_append_all(sd_bus *bus, sd_bus_message *m, const char *path) {
+         _cleanup_set_free_ Set *s = NULL;
+-        char *prefix;
++        _cleanup_free_ char *prefix = NULL;
++        size_t pl;
+         int r;
+ 
+         assert(bus);
+@@ -2485,7 +2514,12 @@
+         if (bus->nodes_modified)
+                 return 0;
+ 
+-        prefix = alloca(strlen(path) + 1);
++        pl = strlen(path);
++        assert(pl <= BUS_PATH_SIZE_MAX);
++        prefix = new(char, pl + 1);
++        if (!prefix)
++                return -ENOMEM;
++
+         OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
+                 r = object_removed_append_all_prefix(bus, m, s, prefix, path, true);
+                 if (r < 0)
+@@ -2635,7 +2669,8 @@
+                 const char *path,
+                 const char *interface) {
+ 
+-        char *prefix;
++        _cleanup_free_ char *prefix = NULL;
++        size_t pl;
+         int r;
+ 
+         assert(bus);
+@@ -2649,7 +2684,12 @@
+         if (bus->nodes_modified)
+                 return 0;
+ 
+-        prefix = alloca(strlen(path) + 1);
++        pl = strlen(path);
++        assert(pl <= BUS_PATH_SIZE_MAX);
++        prefix = new(char, pl + 1);
++        if (!prefix)
++                return -ENOMEM;
++
+         OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
+                 r = interfaces_added_append_one_prefix(bus, m, prefix, path, interface, true);
+                 if (r != 0)
diff --git a/meta/recipes-core/systemd/systemd/sd-bus-if-we-receive-an-invalid-dbus-message-ignore-.patch b/meta/recipes-core/systemd/systemd/sd-bus-if-we-receive-an-invalid-dbus-message-ignore-.patch
new file mode 100644
index 0000000000..4ffa739145
--- /dev/null
+++ b/meta/recipes-core/systemd/systemd/sd-bus-if-we-receive-an-invalid-dbus-message-ignore-.patch
@@ -0,0 +1,61 @@
+Description: sd-bus: if we receive an invalid dbus message, ignore and
+ proceeed
+ .
+ dbus-daemon might have a slightly different idea of what a valid msg is
+ than us (for example regarding valid msg and field sizes). Let's hence
+ try to proceed if we can and thus drop messages rather than fail the
+ connection if we fail to validate a message.
+ .
+ Hopefully the differences in what is considered valid are not visible
+ for real-life usecases, but are specific to exploit attempts only.
+Author: Lennart Poettering <lennart@poettering.net>
+Forwarded: other,https://github.com/systemd/systemd/pull/11708/
+
+Patch from: systemd_237-3ubuntu10.13
+
+For information see:
+https://usn.ubuntu.com/3891-1/
+https://git.launchpad.net/ubuntu/+source/systemd/commit/?h=applied/ubuntu/bionic-updates&id=d7584b894afcaa8a4a1abb69db2a9c81a6276e80
+
+CVE: CVE-2019-6454
+Upstream-Status: Backport
+
+Signed-off-by: George McCollister <george.mccollister@gmail.com>
+
+diff --git a/src/libsystemd/sd-bus/bus-socket.c b/src/libsystemd/sd-bus/bus-socket.c
+index 30d6455b6f..441b4a816f 100644
+--- a/src/libsystemd/sd-bus/bus-socket.c
++++ b/src/libsystemd/sd-bus/bus-socket.c
+@@ -1072,7 +1072,7 @@ static int bus_socket_read_message_need(sd_bus *bus, size_t *need) {
+ }
+
+ static int bus_socket_make_message(sd_bus *bus, size_t size) {
+-        sd_bus_message *t;
++        sd_bus_message *t = NULL;
+         void *b;
+         int r;
+
+@@ -1097,7 +1097,9 @@ static int bus_socket_make_message(sd_bus *bus, size_t size) {
+                                     bus->fds, bus->n_fds,
+                                     NULL,
+                                     &t);
+-        if (r < 0) {
++        if (r == -EBADMSG)
++                log_debug_errno(r, "Received invalid message from connection %s, dropping.", strna(bus->description));
++        else if (r < 0) {
+                 free(b);
+                 return r;
+         }
+@@ -1108,7 +1110,8 @@ static int bus_socket_make_message(sd_bus *bus, size_t size) {
+         bus->fds = NULL;
+         bus->n_fds = 0;
+
+-        bus->rqueue[bus->rqueue_size++] = t;
++        if (t)
++                bus->rqueue[bus->rqueue_size++] = t;
+
+         return 1;
+ }
+--
+2.17.1
+
diff --git a/meta/recipes-core/systemd/systemd_237.bb b/meta/recipes-core/systemd/systemd_237.bb
index 1d9fcf0bd7..3b9c4026ce 100644
--- a/meta/recipes-core/systemd/systemd_237.bb
+++ b/meta/recipes-core/systemd/systemd_237.bb
@@ -63,6 +63,8 @@ SRC_URI += "file://touchscreen.rules \
            file://0027-journal-fix-out-of-bounds-read-CVE-2018-16866.patch \
            file://0001-tmpfiles-don-t-resolve-pathnames-when-traversing-rec.patch \
            file://0002-Make-tmpfiles-safe.patch \
+           file://CVE-2019-6454.patch \
+           file://sd-bus-if-we-receive-an-invalid-dbus-message-ignore-.patch \
            "
 SRC_URI_append_qemuall = " file://0001-core-device.c-Change-the-default-device-timeout-to-2.patch"
 
-- 
2.11.0



^ permalink raw reply related	[flat|nested] 9+ messages in thread

end of thread, other threads:[~2019-02-25 16:38 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-02-25 16:37 [sumo][PATCH 0/8] systemd: fix CVEs George McCollister
2019-02-25 16:37 ` [sumo][PATCH 1/8] systemd: fix CVE-2018-15686 George McCollister
2019-02-25 16:37 ` [sumo][PATCH 2/8] systemd: fix CVE-2018-15687 George McCollister
2019-02-25 16:37 ` [sumo][PATCH 3/8] systemd: fix CVE-2018-15688 George McCollister
2019-02-25 16:37 ` [sumo][PATCH 4/8] systemd: Security fix CVE-2018-16864 George McCollister
2019-02-25 16:37 ` [sumo][PATCH 5/8] systemd: Security fix CVE-2018-16865 George McCollister
2019-02-25 16:37 ` [sumo][PATCH 6/8] systemd: Security fix CVE-2018-16866 George McCollister
2019-02-25 16:37 ` [sumo][PATCH 7/8] systemd: fix CVE-2018-6954 George McCollister
2019-02-25 16:37 ` [sumo][PATCH 8/8] systemd: fix CVE-2019-6454 George McCollister

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.