]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/shared/clean-ipc.c
Merge pull request #2095 from evverx/fix-distcheck-for-disable-timesync
[thirdparty/systemd.git] / src / shared / clean-ipc.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4 This file is part of systemd.
5
6 Copyright 2014 Lennart Poettering
7
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
12
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
17
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 ***/
21
22 #include <dirent.h>
23 #include <errno.h>
24 #include <fcntl.h>
25 #include <limits.h>
26 #include <mqueue.h>
27 #include <stdbool.h>
28 #include <stdio.h>
29 #include <string.h>
30 #include <sys/ipc.h>
31 #include <sys/msg.h>
32 #include <sys/sem.h>
33 #include <sys/shm.h>
34 #include <sys/stat.h>
35 #include <unistd.h>
36
37 #include "clean-ipc.h"
38 #include "dirent-util.h"
39 #include "fd-util.h"
40 #include "fileio.h"
41 #include "formats-util.h"
42 #include "log.h"
43 #include "macro.h"
44 #include "string-util.h"
45 #include "strv.h"
46
47 static int clean_sysvipc_shm(uid_t delete_uid) {
48 _cleanup_fclose_ FILE *f = NULL;
49 char line[LINE_MAX];
50 bool first = true;
51 int ret = 0;
52
53 f = fopen("/proc/sysvipc/shm", "re");
54 if (!f) {
55 if (errno == ENOENT)
56 return 0;
57
58 return log_warning_errno(errno, "Failed to open /proc/sysvipc/shm: %m");
59 }
60
61 FOREACH_LINE(line, f, goto fail) {
62 unsigned n_attached;
63 pid_t cpid, lpid;
64 uid_t uid, cuid;
65 gid_t gid, cgid;
66 int shmid;
67
68 if (first) {
69 first = false;
70 continue;
71 }
72
73 truncate_nl(line);
74
75 if (sscanf(line, "%*i %i %*o %*u " PID_FMT " " PID_FMT " %u " UID_FMT " " GID_FMT " " UID_FMT " " GID_FMT,
76 &shmid, &cpid, &lpid, &n_attached, &uid, &gid, &cuid, &cgid) != 8)
77 continue;
78
79 if (n_attached > 0)
80 continue;
81
82 if (uid != delete_uid)
83 continue;
84
85 if (shmctl(shmid, IPC_RMID, NULL) < 0) {
86
87 /* Ignore entries that are already deleted */
88 if (errno == EIDRM || errno == EINVAL)
89 continue;
90
91 ret = log_warning_errno(errno,
92 "Failed to remove SysV shared memory segment %i: %m",
93 shmid);
94 }
95 }
96
97 return ret;
98
99 fail:
100 return log_warning_errno(errno, "Failed to read /proc/sysvipc/shm: %m");
101 }
102
103 static int clean_sysvipc_sem(uid_t delete_uid) {
104 _cleanup_fclose_ FILE *f = NULL;
105 char line[LINE_MAX];
106 bool first = true;
107 int ret = 0;
108
109 f = fopen("/proc/sysvipc/sem", "re");
110 if (!f) {
111 if (errno == ENOENT)
112 return 0;
113
114 return log_warning_errno(errno, "Failed to open /proc/sysvipc/sem: %m");
115 }
116
117 FOREACH_LINE(line, f, goto fail) {
118 uid_t uid, cuid;
119 gid_t gid, cgid;
120 int semid;
121
122 if (first) {
123 first = false;
124 continue;
125 }
126
127 truncate_nl(line);
128
129 if (sscanf(line, "%*i %i %*o %*u " UID_FMT " " GID_FMT " " UID_FMT " " GID_FMT,
130 &semid, &uid, &gid, &cuid, &cgid) != 5)
131 continue;
132
133 if (uid != delete_uid)
134 continue;
135
136 if (semctl(semid, 0, IPC_RMID) < 0) {
137
138 /* Ignore entries that are already deleted */
139 if (errno == EIDRM || errno == EINVAL)
140 continue;
141
142 ret = log_warning_errno(errno,
143 "Failed to remove SysV semaphores object %i: %m",
144 semid);
145 }
146 }
147
148 return ret;
149
150 fail:
151 return log_warning_errno(errno, "Failed to read /proc/sysvipc/sem: %m");
152 }
153
154 static int clean_sysvipc_msg(uid_t delete_uid) {
155 _cleanup_fclose_ FILE *f = NULL;
156 char line[LINE_MAX];
157 bool first = true;
158 int ret = 0;
159
160 f = fopen("/proc/sysvipc/msg", "re");
161 if (!f) {
162 if (errno == ENOENT)
163 return 0;
164
165 return log_warning_errno(errno, "Failed to open /proc/sysvipc/msg: %m");
166 }
167
168 FOREACH_LINE(line, f, goto fail) {
169 uid_t uid, cuid;
170 gid_t gid, cgid;
171 pid_t cpid, lpid;
172 int msgid;
173
174 if (first) {
175 first = false;
176 continue;
177 }
178
179 truncate_nl(line);
180
181 if (sscanf(line, "%*i %i %*o %*u %*u " PID_FMT " " PID_FMT " " UID_FMT " " GID_FMT " " UID_FMT " " GID_FMT,
182 &msgid, &cpid, &lpid, &uid, &gid, &cuid, &cgid) != 7)
183 continue;
184
185 if (uid != delete_uid)
186 continue;
187
188 if (msgctl(msgid, IPC_RMID, NULL) < 0) {
189
190 /* Ignore entries that are already deleted */
191 if (errno == EIDRM || errno == EINVAL)
192 continue;
193
194 ret = log_warning_errno(errno,
195 "Failed to remove SysV message queue %i: %m",
196 msgid);
197 }
198 }
199
200 return ret;
201
202 fail:
203 return log_warning_errno(errno, "Failed to read /proc/sysvipc/msg: %m");
204 }
205
206 static int clean_posix_shm_internal(DIR *dir, uid_t uid) {
207 struct dirent *de;
208 int ret = 0, r;
209
210 assert(dir);
211
212 FOREACH_DIRENT(de, dir, goto fail) {
213 struct stat st;
214
215 if (STR_IN_SET(de->d_name, "..", "."))
216 continue;
217
218 if (fstatat(dirfd(dir), de->d_name, &st, AT_SYMLINK_NOFOLLOW) < 0) {
219 if (errno == ENOENT)
220 continue;
221
222 log_warning_errno(errno, "Failed to stat() POSIX shared memory segment %s: %m", de->d_name);
223 ret = -errno;
224 continue;
225 }
226
227 if (st.st_uid != uid)
228 continue;
229
230 if (S_ISDIR(st.st_mode)) {
231 _cleanup_closedir_ DIR *kid;
232
233 kid = xopendirat(dirfd(dir), de->d_name, O_NOFOLLOW|O_NOATIME);
234 if (!kid) {
235 if (errno != ENOENT) {
236 log_warning_errno(errno, "Failed to enter shared memory directory %s: %m", de->d_name);
237 ret = -errno;
238 }
239 } else {
240 r = clean_posix_shm_internal(kid, uid);
241 if (r < 0)
242 ret = r;
243 }
244
245 if (unlinkat(dirfd(dir), de->d_name, AT_REMOVEDIR) < 0) {
246
247 if (errno == ENOENT)
248 continue;
249
250 log_warning_errno(errno, "Failed to remove POSIX shared memory directory %s: %m", de->d_name);
251 ret = -errno;
252 }
253 } else {
254
255 if (unlinkat(dirfd(dir), de->d_name, 0) < 0) {
256
257 if (errno == ENOENT)
258 continue;
259
260 log_warning_errno(errno, "Failed to remove POSIX shared memory segment %s: %m", de->d_name);
261 ret = -errno;
262 }
263 }
264 }
265
266 return ret;
267
268 fail:
269 log_warning_errno(errno, "Failed to read /dev/shm: %m");
270 return -errno;
271 }
272
273 static int clean_posix_shm(uid_t uid) {
274 _cleanup_closedir_ DIR *dir = NULL;
275
276 dir = opendir("/dev/shm");
277 if (!dir) {
278 if (errno == ENOENT)
279 return 0;
280
281 return log_warning_errno(errno, "Failed to open /dev/shm: %m");
282 }
283
284 return clean_posix_shm_internal(dir, uid);
285 }
286
287 static int clean_posix_mq(uid_t uid) {
288 _cleanup_closedir_ DIR *dir = NULL;
289 struct dirent *de;
290 int ret = 0;
291
292 dir = opendir("/dev/mqueue");
293 if (!dir) {
294 if (errno == ENOENT)
295 return 0;
296
297 return log_warning_errno(errno, "Failed to open /dev/mqueue: %m");
298 }
299
300 FOREACH_DIRENT(de, dir, goto fail) {
301 struct stat st;
302 char fn[1+strlen(de->d_name)+1];
303
304 if (STR_IN_SET(de->d_name, "..", "."))
305 continue;
306
307 if (fstatat(dirfd(dir), de->d_name, &st, AT_SYMLINK_NOFOLLOW) < 0) {
308 if (errno == ENOENT)
309 continue;
310
311 ret = log_warning_errno(errno,
312 "Failed to stat() MQ segment %s: %m",
313 de->d_name);
314 continue;
315 }
316
317 if (st.st_uid != uid)
318 continue;
319
320 fn[0] = '/';
321 strcpy(fn+1, de->d_name);
322
323 if (mq_unlink(fn) < 0) {
324 if (errno == ENOENT)
325 continue;
326
327 ret = log_warning_errno(errno,
328 "Failed to unlink POSIX message queue %s: %m",
329 fn);
330 }
331 }
332
333 return ret;
334
335 fail:
336 return log_warning_errno(errno, "Failed to read /dev/mqueue: %m");
337 }
338
339 int clean_ipc(uid_t uid) {
340 int ret = 0, r;
341
342 /* Refuse to clean IPC of the root and system users */
343 if (uid <= SYSTEM_UID_MAX)
344 return 0;
345
346 r = clean_sysvipc_shm(uid);
347 if (r < 0)
348 ret = r;
349
350 r = clean_sysvipc_sem(uid);
351 if (r < 0)
352 ret = r;
353
354 r = clean_sysvipc_msg(uid);
355 if (r < 0)
356 ret = r;
357
358 r = clean_posix_shm(uid);
359 if (r < 0)
360 ret = r;
361
362 r = clean_posix_mq(uid);
363 if (r < 0)
364 ret = r;
365
366 return ret;
367 }