summaryrefslogtreecommitdiffstats
path: root/debian/patches/create-new-file-when-writing-pidfile.patch
blob: 96defbf43335b29e4697adbbc431b0cdb0bdc033 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
From f00fed20092b6a42283f29c6ee1f58244d74b545 Mon Sep 17 00:00:00 2001
From: Miroslav Lichvar <mlichvar@redhat.com>
Date: Thu, 6 Aug 2020 09:31:11 +0200
Subject: main: create new file when writing pidfile

When writing the pidfile, open the file with the O_CREAT|O_EXCL flags
to avoid following a symlink and writing the PID to an unexpected file,
when chronyd still has the root privileges.

The Linux open(2) man page warns about O_EXCL not working as expected on
NFS versions before 3 and Linux versions before 2.6. Saving pidfiles on
a distributed filesystem like NFS is not generally expected, but if
there is a reason to do that, these old kernel and NFS versions are not
considered to be supported for saving files by chronyd.

This is a minimal backport specific to this issue of the following
commits:
- commit 2fc8edacb810 ("use PATH_MAX")
- commit f4c6a00b2a11 ("logging: call exit() in LOG_Message()")
- commit 7a4c396bba8f ("util: add functions for common file operations")
- commit e18903a6b563 ("switch to new util file functions")

Reported-by: Matthias Gerstner <mgerstner@suse.de>

--- a/logging.c
+++ b/logging.c
@@ -171,6 +171,7 @@ void LOG_Message(LOG_Severity severity,
         system_log = 0;
         log_message(1, severity, buf);
       }
+      exit(1);
       break;
     default:
       assert(0);
--- a/main.c
+++ b/main.c
@@ -281,13 +281,9 @@ write_pidfile(void)
   if (!pidfile[0])
     return;
 
-  out = fopen(pidfile, "w");
-  if (!out) {
-    LOG_FATAL("Could not open %s : %s", pidfile, strerror(errno));
-  } else {
-    fprintf(out, "%d\n", (int)getpid());
-    fclose(out);
-  }
+  out = UTI_OpenFile(NULL, pidfile, NULL, 'W', 0644);
+  fprintf(out, "%d\n", (int)getpid());
+  fclose(out);
 }
 
 /* ================================================== */
--- a/sysincl.h
+++ b/sysincl.h
@@ -37,6 +37,7 @@
 #include <glob.h>
 #include <grp.h>
 #include <inttypes.h>
+#include <limits.h>
 #include <math.h>
 #include <netdb.h>
 #include <netinet/in.h>
--- a/util.c
+++ b/util.c
@@ -1179,6 +1179,101 @@ UTI_CheckDirPermissions(const char *path
 
 /* ================================================== */
 
+static int
+join_path(const char *basedir, const char *name, const char *suffix,
+          char *buffer, size_t length, LOG_Severity severity)
+{
+  const char *sep;
+
+  if (!basedir) {
+    basedir = "";
+    sep = "";
+  } else {
+    sep = "/";
+  }
+
+  if (!suffix)
+    suffix = "";
+
+  if (snprintf(buffer, length, "%s%s%s%s", basedir, sep, name, suffix) >= length) {
+    LOG(severity, "File path %s%s%s%s too long", basedir, sep, name, suffix);
+    return 0;
+  }
+
+  return 1;
+}
+
+/* ================================================== */
+
+FILE *
+UTI_OpenFile(const char *basedir, const char *name, const char *suffix,
+             char mode, mode_t perm)
+{
+  const char *file_mode;
+  char path[PATH_MAX];
+  LOG_Severity severity;
+  int fd, flags;
+  FILE *file;
+
+  severity = mode >= 'A' && mode <= 'Z' ? LOGS_FATAL : LOGS_ERR;
+
+  if (!join_path(basedir, name, suffix, path, sizeof (path), severity))
+    return NULL;
+
+  switch (mode) {
+    case 'r':
+    case 'R':
+      flags = O_RDONLY;
+      file_mode = "r";
+      if (severity != LOGS_FATAL)
+        severity = LOGS_DEBUG;
+      break;
+    case 'w':
+    case 'W':
+      flags = O_WRONLY | O_CREAT | O_EXCL;
+      file_mode = "w";
+      break;
+    case 'a':
+    case 'A':
+      flags = O_WRONLY | O_CREAT | O_APPEND;
+      file_mode = "a";
+      break;
+    default:
+      assert(0);
+      return NULL;
+  }
+
+try_again:
+  fd = open(path, flags, perm);
+  if (fd < 0) {
+    if (errno == EEXIST) {
+      if (unlink(path) < 0) {
+        LOG(severity, "Could not remove %s : %s", path, strerror(errno));
+        return NULL;
+      }
+      DEBUG_LOG("Removed %s", path);
+      goto try_again;
+    }
+    LOG(severity, "Could not open %s : %s", path, strerror(errno));
+    return NULL;
+  }
+
+  UTI_FdSetCloexec(fd);
+
+  file = fdopen(fd, file_mode);
+  if (!file) {
+    LOG(severity, "Could not open %s : %s", path, strerror(errno));
+    close(fd);
+    return NULL;
+  }
+
+  DEBUG_LOG("Opened %s fd=%d mode=%c", path, fd, mode);
+
+  return file;
+}
+
+/* ================================================== */
+
 void
 UTI_DropRoot(uid_t uid, gid_t gid)
 {
--- a/util.h
+++ b/util.h
@@ -176,6 +176,17 @@ extern int UTI_CreateDirAndParents(const
    permissions and its uid/gid must match the specified values. */
 extern int UTI_CheckDirPermissions(const char *path, mode_t perm, uid_t uid, gid_t gid);
 
+/* Open a file.  The full path of the file is constructed from the basedir
+   (may be NULL), '/' (if basedir is not NULL), name, and suffix (may be NULL).
+   Created files have specified permissions (umasked).  Returns NULL on error.
+   The following modes are supported (if the mode is an uppercase character,
+   errors are fatal):
+   r/R - open an existing file for reading
+   w/W - open a new file for writing (remove existing file)
+   a/A - open an existing file for appending (create if does not exist) */
+extern FILE *UTI_OpenFile(const char *basedir, const char *name, const char *suffix,
+                          char mode, mode_t perm);
+
 /* Set process user/group IDs and drop supplementary groups */
 extern void UTI_DropRoot(uid_t uid, gid_t gid);