CVE-2016-10156: Permissions, Privileges, and Access Controls (CWE-264)
value: A flaw in systemd v228 in /src/basic/fs-util.c caused world writable suid files to be created when using the systemd timers features, allowing local attackers to escalate their privileges to root. This is fixed in v229.
CWE description: Weaknesses in this category are related to the management of permissions, privileges, and other security features that are used to perform access control.
CVSS v3 base score: 7.8
Published: 2017-01-23T07:59Z
Repository: https://github.com/systemd/systemd
Language: C
Files changed: src/basic/fs-util.c, src/core/timer.c, src/test/test-conf-files.c
Commit date: 2015-11-11 22:54:56 +0100
Commit message: util-lib: use MODE_INVALID as invalid value for mode_t everywhere

Diff: diff --git a/src/basic/fs-util.c b/src/basic/fs-util.c
index 7aee404bf..cddd4232f 100644
--- a/src/basic/fs-util.c
+++ b/src/basic/fs-util.c
@@ -309,11 +309,11 @@ int touch_file(const char *path, bool parents, usec_t stamp, uid_t uid, gid_t gi
 
         fd = open(path, O_WRONLY|O_CREAT|O_CLOEXEC|O_NOCTTY, mode > 0 ? mode : 0644);
         if (fd < 0)
                 return -errno;
 
-        if (mode > 0) {
+        if (mode != MODE_INVALID) {
                 r = fchmod(fd, mode);
                 if (r < 0)
                         return -errno;
         }
 
@@ -336,11 +336,11 @@ int touch_file(const char *path, bool parents, usec_t stamp, uid_t uid, gid_t gi
 
         return 0;
 }
 
 int touch(const char *path) {
-        return touch_file(path, false, USEC_INFINITY, UID_INVALID, GID_INVALID, 0);
+        return touch_file(path, false, USEC_INFINITY, UID_INVALID, GID_INVALID, MODE_INVALID);
 }
 
 int symlink_idempotent(const char *from, const char *to) {
         _cleanup_free_ char *p = NULL;
         int r;
diff --git a/src/core/timer.c b/src/core/timer.c
index c50e891ae..06a603531 100644
--- a/src/core/timer.c
+++ b/src/core/timer.c
@@ -517,11 +517,11 @@ static void timer_enter_running(Timer *t) {
                 goto fail;
 
         dual_timestamp_get(&t->last_trigger);
 
         if (t->stamp_path)
-                touch_file(t->stamp_path, true, t->last_trigger.realtime, UID_INVALID, GID_INVALID, 0);
+                touch_file(t->stamp_path, true, t->last_trigger.realtime, UID_INVALID, GID_INVALID, MODE_INVALID);
 
         timer_set_state(t, TIMER_RUNNING);
         return;
 
 fail:
@@ -553,11 +553,11 @@ static int timer_start(Unit *u) {
                         t->last_trigger.realtime = timespec_load(&st.st_atim);
                 else if (errno == ENOENT)
                         /* The timer has never run before,
                          * make sure a stamp file exists.
                          */
-                        touch_file(t->stamp_path, true, USEC_INFINITY, UID_INVALID, GID_INVALID, 0);
+                        touch_file(t->stamp_path, true, USEC_INFINITY, UID_INVALID, GID_INVALID, MODE_INVALID);
         }
 
         t->result = TIMER_SUCCESS;
         timer_enter_waiting(t, true);
         return 1;
diff --git a/src/test/test-conf-files.c b/src/test/test-conf-files.c
index a69698d4e..86ac513d4 100644
--- a/src/test/test-conf-files.c
+++ b/src/test/test-conf-files.c
@@ -24,10 +24,11 @@
 
 #include "alloc-util.h"
 #include "conf-files.h"
 #include "fs-util.h"
 #include "macro.h"
+#include "parse-util.h"
 #include "rm-rf.h"
 #include "string-util.h"
 #include "strv.h"
 #include "user-util.h"
 #include "util.h"
@@ -38,11 +39,11 @@ static void setup_test_dir(char *tmp_dir, const char *files, ...) {
         assert_se(mkdtemp(tmp_dir) != NULL);
 
         va_start(ap, files);
         while (files != NULL) {
                 _cleanup_free_ char *path = strappend(tmp_dir, files);
-                assert_se(touch_file(path, true, USEC_INFINITY, UID_INVALID, GID_INVALID, 0) == 0);
+                assert_se(touch_file(path, true, USEC_INFINITY, UID_INVALID, GID_INVALID, MODE_INVALID) == 0);
                 files = va_arg(ap, const char *);
         }
         va_end(ap);
 }
 

Vulnerable code:         if (mode > 0) {
        return touch_file(path, false, USEC_INFINITY, UID_INVALID, GID_INVALID, 0);
                touch_file(t->stamp_path, true, t->last_trigger.realtime, UID_INVALID, GID_INVALID, 0);
                        touch_file(t->stamp_path, true, USEC_INFINITY, UID_INVALID, GID_INVALID, 0);
                assert_se(touch_file(path, true, USEC_INFINITY, UID_INVALID, GID_INVALID, 0) == 0);
Fixed code:         if (mode != MODE_INVALID) {
        return touch_file(path, false, USEC_INFINITY, UID_INVALID, GID_INVALID, MODE_INVALID);
                touch_file(t->stamp_path, true, t->last_trigger.realtime, UID_INVALID, GID_INVALID, MODE_INVALID);
                        touch_file(t->stamp_path, true, USEC_INFINITY, UID_INVALID, GID_INVALID, MODE_INVALID);
#include "parse-util.h"
                assert_se(touch_file(path, true, USEC_INFINITY, UID_INVALID, GID_INVALID, MODE_INVALID) == 0);