]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
localed: avoid TOCTOU in loading config
authorYu Watanabe <watanabe.yu+github@gmail.com>
Tue, 13 Dec 2022 08:30:12 +0000 (17:30 +0900)
committerYu Watanabe <watanabe.yu+github@gmail.com>
Wed, 14 Dec 2022 11:18:58 +0000 (20:18 +0900)
src/locale/localed-util.c
src/locale/localed-util.h
src/locale/localed.c

index 8fb65b96997d1ac202e8712c4ca4b76ec974fbd9..cace820bddb874070f1a8d5753cf8fb9c7c44278 100644 (file)
@@ -20,6 +20,7 @@
 #include "mkdir-label.h"
 #include "nulstr-util.h"
 #include "process-util.h"
+#include "stat-util.h"
 #include "string-util.h"
 #include "strv.h"
 #include "tmpfile-util.h"
@@ -92,8 +93,8 @@ int locale_read_data(Context *c, sd_bus_message *m) {
 }
 
 int vconsole_read_data(Context *c, sd_bus_message *m) {
+        _cleanup_close_ int fd = -EBADFD;
         struct stat st;
-        usec_t t;
 
         /* Do not try to re-read the file within single bus operation. */
         if (m) {
@@ -104,33 +105,35 @@ int vconsole_read_data(Context *c, sd_bus_message *m) {
                 c->vc_cache = sd_bus_message_ref(m);
         }
 
-        if (stat("/etc/vconsole.conf", &st) < 0) {
-                if (errno != ENOENT)
-                        return -errno;
-
-                c->vc_mtime = USEC_INFINITY;
+        fd = RET_NERRNO(open("/etc/vconsole.conf", O_CLOEXEC | O_PATH));
+        if (fd == -ENOENT) {
+                c->vc_stat = (struct stat) {};
                 context_free_vconsole(c);
                 return 0;
         }
+        if (fd < 0)
+                return fd;
+
+        if (fstat(fd, &st) < 0)
+                return -errno;
 
-        /* If mtime is not changed, then we do not need to re-read */
-        t = timespec_load(&st.st_mtim);
-        if (c->vc_mtime != USEC_INFINITY && t == c->vc_mtime)
+        /* If the file is not changed, then we do not need to re-read */
+        if (stat_inode_unmodified(&c->vc_stat, &st))
                 return 0;
 
-        c->vc_mtime = t;
+        c->vc_stat = st;
         context_free_vconsole(c);
 
-        return parse_env_file(NULL, "/etc/vconsole.conf",
-                              "KEYMAP",        &c->vc_keymap,
-                              "KEYMAP_TOGGLE", &c->vc_keymap_toggle);
+        return parse_env_file_fd(fd, "/etc/vconsole.conf",
+                                 "KEYMAP",        &c->vc_keymap,
+                                 "KEYMAP_TOGGLE", &c->vc_keymap_toggle);
 }
 
 int x11_read_data(Context *c, sd_bus_message *m) {
+        _cleanup_close_ int fd = -EBADFD, fd_ro = -EBADFD;
         _cleanup_fclose_ FILE *f = NULL;
         bool in_section = false;
         struct stat st;
-        usec_t t;
         int r;
 
         /* Do not try to re-read the file within single bus operation. */
@@ -142,27 +145,35 @@ int x11_read_data(Context *c, sd_bus_message *m) {
                 c->x11_cache = sd_bus_message_ref(m);
         }
 
-        if (stat("/etc/X11/xorg.conf.d/00-keyboard.conf", &st) < 0) {
-                if (errno != ENOENT)
-                        return -errno;
-
-                c->x11_mtime = USEC_INFINITY;
+        fd = RET_NERRNO(open("/etc/X11/xorg.conf.d/00-keyboard.conf", O_CLOEXEC | O_PATH));
+        if (fd == -ENOENT) {
+                c->x11_stat = (struct stat) {};
                 context_free_x11(c);
                 return 0;
         }
+        if (fd < 0)
+                return fd;
 
-        /* If mtime is not changed, then we do not need to re-read */
-        t = timespec_load(&st.st_mtim);
-        if (c->x11_mtime != USEC_INFINITY && t == c->x11_mtime)
+        if (fstat(fd, &st) < 0)
+                return -errno;
+
+        /* If the file is not changed, then we do not need to re-read */
+        if (stat_inode_unmodified(&c->x11_stat, &st))
                 return 0;
 
-        c->x11_mtime = t;
+        c->x11_stat = st;
         context_free_x11(c);
 
-        f = fopen("/etc/X11/xorg.conf.d/00-keyboard.conf", "re");
+        fd_ro = fd_reopen(fd, O_CLOEXEC | O_RDONLY);
+        if (fd_ro < 0)
+                return fd_ro;
+
+        f = fdopen(fd_ro, "re");
         if (!f)
                 return -errno;
 
+        TAKE_FD(fd_ro);
+
         for (;;) {
                 _cleanup_free_ char *line = NULL;
                 char *l;
@@ -219,7 +230,6 @@ int x11_read_data(Context *c, sd_bus_message *m) {
 
 int vconsole_write_data(Context *c) {
         _cleanup_strv_free_ char **l = NULL;
-        struct stat st;
         int r;
 
         r = load_env_file(NULL, "/etc/vconsole.conf", &l);
@@ -238,7 +248,7 @@ int vconsole_write_data(Context *c) {
                 if (unlink("/etc/vconsole.conf") < 0)
                         return errno == ENOENT ? 0 : -errno;
 
-                c->vc_mtime = USEC_INFINITY;
+                c->vc_stat = (struct stat) {};
                 return 0;
         }
 
@@ -246,8 +256,8 @@ int vconsole_write_data(Context *c) {
         if (r < 0)
                 return r;
 
-        if (stat("/etc/vconsole.conf", &st) >= 0)
-                c->vc_mtime = timespec_load(&st.st_mtim);
+        if (stat("/etc/vconsole.conf", &c->vc_stat) < 0)
+                return -errno;
 
         return 0;
 }
@@ -255,7 +265,6 @@ int vconsole_write_data(Context *c) {
 int x11_write_data(Context *c) {
         _cleanup_fclose_ FILE *f = NULL;
         _cleanup_(unlink_and_freep) char *temp_path = NULL;
-        struct stat st;
         int r;
 
         if (isempty(c->x11_layout) &&
@@ -266,7 +275,7 @@ int x11_write_data(Context *c) {
                 if (unlink("/etc/X11/xorg.conf.d/00-keyboard.conf") < 0)
                         return errno == ENOENT ? 0 : -errno;
 
-                c->vc_mtime = USEC_INFINITY;
+                c->x11_stat = (struct stat) {};
                 return 0;
         }
 
@@ -305,8 +314,8 @@ int x11_write_data(Context *c) {
         if (rename(temp_path, "/etc/X11/xorg.conf.d/00-keyboard.conf") < 0)
                 return -errno;
 
-        if (stat("/etc/X11/xorg.conf.d/00-keyboard.conf", &st) >= 0)
-                c->x11_mtime = timespec_load(&st.st_mtim);
+        if (stat("/etc/X11/xorg.conf.d/00-keyboard.conf", &c->x11_stat) < 0)
+                return -errno;
 
         return 0;
 }
index 5470d1bb9b63264a3cc8e09da35ad5b3c40a9056..b09ed80b83f87382f42873263880060905c64128 100644 (file)
@@ -1,25 +1,26 @@
 /* SPDX-License-Identifier: LGPL-2.1-or-later */
 #pragma once
 
+#include <sys/stat.h>
+
 #include "sd-bus.h"
 
 #include "hashmap.h"
 #include "locale-setup.h"
-#include "time-util.h"
 
 typedef struct Context {
         sd_bus_message *locale_cache;
         LocaleContext locale_context;
 
         sd_bus_message *x11_cache;
-        usec_t x11_mtime;
+        struct stat x11_stat;
         char *x11_layout;
         char *x11_model;
         char *x11_variant;
         char *x11_options;
 
         sd_bus_message *vc_cache;
-        usec_t vc_mtime;
+        struct stat vc_stat;
         char *vc_keymap;
         char *vc_keymap_toggle;
 
index a2014e3da0fbabb6575adb587749ece0f7f96490..ebce509b3063cd35d27bf75ca5fa884c3aad673e 100644 (file)
@@ -766,8 +766,6 @@ static int connect_bus(Context *c, sd_event *event, sd_bus **_bus) {
 static int run(int argc, char *argv[]) {
         _cleanup_(context_clear) Context context = {
                 .locale_context.mtime = USEC_INFINITY,
-                .vc_mtime = USEC_INFINITY,
-                .x11_mtime = USEC_INFINITY,
         };
         _cleanup_(sd_event_unrefp) sd_event *event = NULL;
         _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;