]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
global: Create most files with O_NOFOLLOW flag for extra safety
authorTimo Sirainen <timo.sirainen@open-xchange.com>
Mon, 2 Mar 2026 08:21:24 +0000 (10:21 +0200)
committeraki.tuomi <aki.tuomi@open-xchange.com>
Thu, 5 Mar 2026 12:29:23 +0000 (12:29 +0000)
19 files changed:
src/auth/auth-token.c
src/lib-dict/dict-file.c
src/lib-index/mail-index.c
src/lib-index/mailbox-log.c
src/lib-index/test-mail-index-write.c
src/lib-sql/driver-cassandra.c
src/lib-storage/index/dbox-single/sdbox-file.c
src/lib-storage/index/maildir/maildir-save.c
src/lib-storage/index/maildir/maildir-storage.c
src/lib-storage/index/maildir/maildir-uidlist.c
src/lib-storage/mailbox-uidvalidity.c
src/lib/file-copy.c
src/lib/file-dotlock.c
src/lib/iostream-rawlog.c
src/lib/ostream-file.c
src/lib/safe-mkstemp.c
src/master/main.c
src/stats/event-exporter-transport-file.c
src/util/rawlog.c

index 48afcc68085bba891974ff7772e4f494781283ff..2e558c02e3d6dc1afc3a28395220704f2d9fed05 100644 (file)
@@ -105,7 +105,7 @@ auth_token_write_secret(const char *path,
        temp_path = t_strconcat(path, ".tmp", NULL);
 
        old_mask = umask(0);
-       fd = open(temp_path, O_WRONLY | O_CREAT | O_TRUNC, 0600);
+       fd = open(temp_path, O_WRONLY | O_CREAT | O_TRUNC | O_NOFOLLOW, 0600);
        umask(old_mask);
 
        if (fd == -1) {
index 59b751c88a877d411b47b18ca65b49b26d74a459..e8f146dc189b32ed015d2453c2113bd69d0040b3 100644 (file)
@@ -523,11 +523,11 @@ file_dict_lock(struct file_dict *dict, struct file_lock **lock_r,
 
        if (dict->fd == -1) {
                /* quota file doesn't exist yet, we need to create it */
-               dict->fd = open(dict->path, O_CREAT | O_RDWR, 0600);
+               dict->fd = open(dict->path, O_CREAT | O_RDWR | O_NOFOLLOW, 0600);
                if (dict->fd == -1 && errno == ENOENT) {
                        if (file_dict_mkdir(dict, error_r) < 0)
                                return -1;
-                       dict->fd = open(dict->path, O_CREAT | O_RDWR, 0600);
+                       dict->fd = open(dict->path, O_CREAT | O_RDWR | O_NOFOLLOW, 0600);
                }
                if (dict->fd == -1) {
                        if (errno == EACCES)
@@ -590,7 +590,7 @@ file_dict_write_changes(struct dict_transaction_memory_context *ctx,
                if (file_dict_lock(dict, &lock, error_r) < 0)
                        return -1;
                temp_path = t_strdup_printf("%s.tmp", dict->path);
-               fd = creat(temp_path, 0600);
+               fd = open(temp_path, O_WRONLY | O_CREAT | O_TRUNC | O_NOFOLLOW, 0600);
                if (fd == -1) {
                        *error_r = t_strdup_printf(
                                "dict-file: creat(%s) failed: %m", temp_path);
index f6b73f381fe3e7819a7d6465062444b4413473e7..b031f7a9b3689f201911651f96a978e8e0ca8266 100644 (file)
@@ -546,7 +546,7 @@ int mail_index_create_tmp_file(struct mail_index *index,
 
        path = *path_r = t_strconcat(path_prefix, ".tmp", NULL);
        old_mask = umask(0);
-       fd = open(path, O_RDWR|O_CREAT|O_EXCL, index->set.mode);
+       fd = open(path, O_RDWR|O_CREAT | O_EXCL | O_NOFOLLOW, index->set.mode);
        umask(old_mask);
        if (fd == -1 && errno == EEXIST) {
                /* stale temp file. unlink and recreate rather than overwriting,
@@ -554,7 +554,8 @@ int mail_index_create_tmp_file(struct mail_index *index,
                if (i_unlink(path) < 0)
                        return -1;
                old_mask = umask(0);
-               fd = open(path, O_RDWR|O_CREAT|O_EXCL, index->set.mode);
+               fd = open(path, O_RDWR | O_CREAT | O_EXCL | O_NOFOLLOW,
+                         index->set.mode);
                umask(old_mask);
        }
        if (fd == -1) {
index c158eb9c1ced83426dbcf430ace1bd455e948279..37b146be868f4c1986f0c5a1b7da9fe8a44eecd6 100644 (file)
@@ -95,13 +95,13 @@ static int mailbox_log_open(struct mailbox_log *log)
        i_assert(log->fd == -1);
 
        log->open_timestamp = ioloop_time;
-       log->fd = open(log->filepath, O_RDWR | O_APPEND);
+       log->fd = open(log->filepath, O_RDWR | O_APPEND | O_NOFOLLOW);
        if (log->fd != -1)
                return 0;
 
        /* try to create it */
        old_mode = umask(0666 ^ log->mode);
-       log->fd = open(log->filepath, O_RDWR | O_APPEND | O_CREAT, 0666);
+       log->fd = open(log->filepath, O_RDWR | O_APPEND | O_CREAT | O_NOFOLLOW, 0666);
        umask(old_mode);
 
        if (log->fd == -1) {
index 2ea105f5c00ffc97fb2f493778c9d96a69626228..0d0810a8774b6d49223128778c60bca0add03d53 100644 (file)
@@ -55,7 +55,7 @@ int mail_index_create_tmp_file(struct mail_index *index ATTR_UNUSED,
        test_assert(expect_index_rewrite);
 
        path = *path_r = t_strconcat(path_prefix, ".tmp", NULL);
-       fd = open(path, O_RDWR|O_CREAT, 0600);
+       fd = open(path, O_RDWR | O_CREAT | O_NOFOLLOW, 0600);
        if (fd == -1) {
                i_error("creat() failed: %m");
                return -1;
index 5e0497514e41f886bd2adbfe7b1c4d1fb8b982cc..d4e060b7e62f529af3c710286194f3d4929d6d60 100644 (file)
@@ -915,7 +915,8 @@ static void driver_cassandra_metrics_write(struct cassandra_db *db)
                return;
        }
 
-       fd = open(str_c(path), O_WRONLY | O_CREAT | O_TRUNC | O_NONBLOCK, 0600);
+       fd = open(str_c(path), O_WRONLY | O_CREAT | O_TRUNC |
+                 O_NONBLOCK | O_NOFOLLOW, 0600);
        if (fd == -1) {
                e_error(db->api.event, "creat(%s) failed: %m", str_c(path));
                return;
index b436b47b2a2a90dd658ee5c5d3f84be9dff24208..f1e8fbdd3f915a57291b27179e350b33ecd3c51e 100644 (file)
@@ -247,7 +247,7 @@ int sdbox_file_create_fd(struct dbox_file *file, const char *path, bool parents)
        int fd;
 
        old_mask = umask(0666 & ~perm->file_create_mode);
-       fd = open(path, O_RDWR | O_CREAT | O_TRUNC, 0666);
+       fd = open(path, O_RDWR | O_CREAT | O_TRUNC | O_NOFOLLOW, 0666);
        umask(old_mask);
        if (fd == -1 && errno == ENOENT && parents &&
            (p = strrchr(path, '/')) != NULL) {
@@ -262,7 +262,7 @@ int sdbox_file_create_fd(struct dbox_file *file, const char *path, bool parents)
                }
                /* try again */
                old_mask = umask(0666 & ~perm->file_create_mode);
-               fd = open(path, O_RDWR | O_CREAT | O_TRUNC, 0666);
+               fd = open(path, O_RDWR | O_CREAT | O_TRUNC | O_NOFOLLOW, 0666);
                umask(old_mask);
        }
        if (fd == -1) {
index 4e302bba32a7b0547809602e521c4268fe9f1ad6..cfb1ee739e406fa954d5b6e19e8300663544c1b4 100644 (file)
@@ -348,7 +348,7 @@ static int maildir_create_tmp(struct maildir_mailbox *mbox, const char *dir,
                   useless. */
                old_mask = umask(0777 & ~perm->file_create_mode);
                fd = open(str_c(path),
-                         O_WRONLY | O_CREAT | O_TRUNC | O_EXCL, 0777);
+                         O_WRONLY | O_CREAT | O_TRUNC | O_EXCL | O_NOFOLLOW, 0777);
                umask(old_mask);
        } while (fd == -1 && errno == EEXIST);
 
index 128fd679c0c10be64213ad4e43da0c629abf124d..b84cdd1d86d1568d7e9cfcd6423fe5838f3373a9 100644 (file)
@@ -447,7 +447,7 @@ static int maildir_create_shared(struct mailbox *box)
 
        old_mask = umask(0);
        path = t_strconcat(path, "/dovecot-shared", NULL);
-       fd = open(path, O_WRONLY | O_CREAT, perm->file_create_mode);
+       fd = open(path, O_WRONLY | O_CREAT | O_NOFOLLOW, perm->file_create_mode);
        umask(old_mask);
 
        if (fd == -1) {
@@ -523,7 +523,7 @@ static int maildir_create_maildirfolder_file(struct mailbox *box)
        path = t_strconcat(mailbox_get_path(box),
                           "/"MAILDIR_SUBFOLDER_FILENAME, NULL);
        old_mask = umask(0);
-       fd = open(path, O_CREAT | O_WRONLY, perm->file_create_mode);
+       fd = open(path, O_CREAT | O_WRONLY | O_NOFOLLOW, perm->file_create_mode);
        umask(old_mask);
        if (fd != -1) {
                /* ok */
index e9602f1f37f73c5cffb014fc2b3b918f4e938046..42fa7e1911656534cf41db2d17bf9bf0d5fe74ee 100644 (file)
@@ -1411,7 +1411,7 @@ static int maildir_uidlist_recreate(struct maildir_uidlist *uidlist)
 
        for (i = 0;; i++) {
                old_mask = umask(0777 & ~perm->file_create_mode);
-               fd = open(temp_path, O_RDWR | O_CREAT | O_TRUNC, 0777);
+               fd = open(temp_path, O_RDWR | O_CREAT | O_TRUNC | O_NOFOLLOW, 0777);
                umask(old_mask);
                if (fd != -1)
                        break;
index 287978db2f9f5a375909cb21a6356b841cd7ed08..39e9719c50f5f90c2679691fe497258763277a74 100644 (file)
@@ -46,7 +46,7 @@ static void mailbox_uidvalidity_write(struct mailbox_list *list,
        mailbox_list_get_root_permissions(list, &perm);
 
        old_mask = umask(0666 & ~perm.file_create_mode);
-       fd = open(path, O_RDWR | O_CREAT, 0666);
+       fd = open(path, O_RDWR | O_CREAT | O_NOFOLLOW, 0666);
        umask(old_mask);
        if (fd == -1) {
                e_error(user->event, "open(%s) failed: %m", path);
@@ -174,7 +174,7 @@ mailbox_uidvalidity_next_rescan(struct mailbox_list *list, const char *path)
                        tmp = t_strdup_printf("%s.%08x", path, cur_value);
                        /* the file is empty, don't bother with permissions */
                        old_mask = umask(0);
-                       fd = open(tmp, O_RDWR | O_CREAT | O_EXCL, 0444);
+                       fd = open(tmp, O_RDWR | O_CREAT | O_EXCL | O_NOFOLLOW, 0444);
                        umask(old_mask);
                        if (fd != -1 || errno != EEXIST)
                                break;
index c9b8e3e556538dab1b4b56f68f8cda6df1941976..aea71f9f8d9b92f93dd3b2ac21aee244347a24c7 100644 (file)
@@ -55,7 +55,7 @@ static int file_copy_to_tmp(const char *srcpath, const char *tmppath,
        }
 
        old_umask = umask(0);
-       fd_out = open(tmppath, O_WRONLY | O_CREAT | O_TRUNC, st.st_mode);
+       fd_out = open(tmppath, O_WRONLY | O_CREAT | O_TRUNC | O_NOFOLLOW, st.st_mode);
        umask(old_umask);
        if (fd_out == -1) {
                i_error("open(%s, O_CREAT) failed: %m", tmppath);
index 29901bf315627db89b1a9430b73b15ea9a717139..3d4bf9910a11a5607af219c045532ed2c2e74c4d 100644 (file)
@@ -400,7 +400,7 @@ static int try_create_lock_excl(struct lock_info *lock_info, bool write_pid)
 {
        int fd;
 
-       fd = open(lock_info->lock_path, O_RDWR | O_EXCL | O_CREAT, 0666);
+       fd = open(lock_info->lock_path, O_RDWR | O_EXCL | O_CREAT | O_NOFOLLOW, 0666);
        if (fd == -1) {
                if (errno == EEXIST)
                        return 0;
index 9b36939b7edebdafc85f8c77577c8c916e5e6022..bef12ad3752c5974d3afbac97cdffe31bcdaceb5 100644 (file)
@@ -241,7 +241,7 @@ int iostream_rawlog_create_prefix(const char *prefix, struct istream **input,
        int in_fd, out_fd;
 
        in_path = t_strdup_printf("%s.in", prefix);
-       in_fd = open(in_path, O_CREAT | O_APPEND | O_WRONLY, 0600);
+       in_fd = open(in_path, O_CREAT | O_APPEND | O_WRONLY | O_NOFOLLOW, 0600);
        if (in_fd == -1) {
                if (errno != ENOENT && !ENOACCESS(errno))
                        i_error("rawlog: creat(%s) failed: %m", in_path);
@@ -249,7 +249,7 @@ int iostream_rawlog_create_prefix(const char *prefix, struct istream **input,
        }
 
        out_path = t_strdup_printf("%s.out", prefix);
-       out_fd = open(out_path, O_CREAT | O_APPEND | O_WRONLY, 0600);
+       out_fd = open(out_path, O_CREAT | O_APPEND | O_WRONLY | O_NOFOLLOW, 0600);
        if (out_fd == -1) {
                if (errno != ENOENT && !ENOACCESS(errno))
                        i_error("rawlog: creat(%s) failed: %m", out_path);
@@ -279,7 +279,7 @@ int iostream_rawlog_create_path(const char *path, struct istream **input,
 
        if ((ret = iostream_rawlog_try_create_tcp(path, input, output)) != 0)
                return ret < 0 ? -1 : 0;
-       fd = open(path, O_CREAT | O_APPEND | O_WRONLY, 0600);
+       fd = open(path, O_CREAT | O_APPEND | O_WRONLY | O_NOFOLLOW, 0600);
        if (fd == -1) {
                if (errno != ENOENT && !ENOACCESS(errno))
                        i_error("rawlog: creat(%s) failed: %m", path);
index f6b5a1eefa1fabec33bde809fd5d848e82165650..df1ea273649a58f7d2840327c412f909e2b913dc 100644 (file)
@@ -1175,7 +1175,7 @@ struct ostream *o_stream_create_file(const char *path, uoff_t offset, mode_t mod
                                     enum ostream_create_file_flags flags)
 {
        int fd;
-       int open_flags = O_WRONLY|O_CREAT;
+       int open_flags = O_WRONLY | O_CREAT | O_NOFOLLOW;
        if (HAS_ANY_BITS(flags, OSTREAM_CREATE_FILE_FLAG_APPEND))
                open_flags |= O_APPEND;
        else
index cb834ff165e9860f0c4537cf462bf2861bf627ac..ab877450a99c5ecbaac11622ad0848f1ddd5ea3a 100644 (file)
@@ -55,7 +55,7 @@ safe_mkstemp_create_file(const char *path, mode_t mode)
        int fd;
 
        old_umask = umask(0666 ^ mode);
-       fd = open(path, O_RDWR | O_EXCL | O_CREAT, 0666);
+       fd = open(path, O_RDWR | O_EXCL | O_CREAT | O_NOFOLLOW, 0666);
        umask(old_umask);
 
        if (fd != -1)
index 9fbec8923991397a73993aad5062f4353cee0faf..2602b66973fdfab1bf6197500d8887cc519b6df0 100644 (file)
@@ -178,7 +178,7 @@ master_fatal_callback(const struct failure_context *ctx,
                /* write the error message to a file (we're chdired to
                   base dir) */
                path = t_strconcat(FATAL_FILENAME, NULL);
-               fd = open(path, O_CREAT | O_TRUNC | O_WRONLY, 0600);
+               fd = open(path, O_CREAT | O_TRUNC | O_WRONLY | O_NOFOLLOW, 0600);
                if (fd != -1) {
                        VA_COPY(args2, args);
                        str = t_strdup_vprintf(format, args2);
@@ -339,7 +339,7 @@ static void create_pid_file(const char *path)
 
        pid = t_strconcat(dec2str(getpid()), "\n", NULL);
 
-       fd = open(path, O_WRONLY|O_CREAT|O_TRUNC, 0644);
+       fd = open(path, O_WRONLY | O_CREAT | O_TRUNC | O_NOFOLLOW, 0644);
        if (fd == -1)
                i_fatal("open(%s) failed: %m", path);
        if (write_full(fd, pid, strlen(pid)) < 0)
index c3a83176dc740161c822363f033923c172954ae1..3bec1d99e99cb1ebcd593f50bc61abb2c8eb1bfc 100644 (file)
@@ -158,7 +158,8 @@ static bool exporter_file_open_unix(struct file_event_exporter *node)
 
 static bool exporter_file_open_plain(struct file_event_exporter *node)
 {
-       node->fd = open(node->fname, O_CREAT|O_APPEND|O_WRONLY, 0600);
+       node->fd = open(node->fname, O_CREAT | O_APPEND | O_WRONLY |
+                       O_NOFOLLOW, 0600);
        if (node->fd == -1) {
                if (ioloop_time - node->last_error > EXPORTER_LAST_ERROR_DELAY)
                        exporter_file_open_error(node, "open");
index 089e8f1dc5a39c766e66d0716ff723d4e7ea5e1f..11ba83454f82577da1da23077d1e1728f9ba5098 100644 (file)
@@ -246,7 +246,7 @@ static void proxy_open_logs(struct rawlog_proxy *proxy, const char *path,
 
        if ((proxy->flags & RAWLOG_FLAG_LOG_INPUT) != 0) {
                fname = t_strdup_printf("%s.in", str_c(path_prefix));
-               fd = open(fname, O_CREAT|O_EXCL|O_WRONLY, 0600);
+               fd = open(fname, O_CREAT|O_EXCL|O_WRONLY|O_NOFOLLOW, 0600);
                if (fd == -1) {
                        i_error("rawlog_open: creat(%s): %m", fname);
                        return;
@@ -257,7 +257,7 @@ static void proxy_open_logs(struct rawlog_proxy *proxy, const char *path,
 
        if ((proxy->flags & RAWLOG_FLAG_LOG_OUTPUT) != 0) {
                fname = t_strdup_printf("%s.out", str_c(path_prefix));
-               fd = open(fname, O_CREAT|O_EXCL|O_WRONLY, 0600);
+               fd = open(fname, O_CREAT|O_EXCL|O_WRONLY|O_NOFOLLOW, 0600);
                if (fd == -1) {
                        i_error("rawlog_open: creat(%s): %m", fname);
                        o_stream_destroy(&proxy->in_output);