]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/shared/clean-ipc.c
util-lib: introduce dirent-util.[ch] for directory entry calls
[thirdparty/systemd.git] / src / shared / clean-ipc.c
CommitLineData
66cdd0f2
LP
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
07630cea
LP
22#include <dirent.h>
23#include <fcntl.h>
24#include <mqueue.h>
66cdd0f2 25#include <sys/ipc.h>
66cdd0f2 26#include <sys/msg.h>
07630cea
LP
27#include <sys/sem.h>
28#include <sys/shm.h>
66cdd0f2 29#include <sys/stat.h>
66cdd0f2 30
3ffd4af2
LP
31#include "clean-ipc.h"
32#include "fd-util.h"
0d39fa9c 33#include "fileio.h"
6482f626 34#include "formats-util.h"
07630cea 35#include "string-util.h"
66cdd0f2 36#include "strv.h"
07630cea 37#include "util.h"
a0956174 38#include "dirent-util.h"
66cdd0f2
LP
39
40static int clean_sysvipc_shm(uid_t delete_uid) {
41 _cleanup_fclose_ FILE *f = NULL;
42 char line[LINE_MAX];
43 bool first = true;
44 int ret = 0;
45
46 f = fopen("/proc/sysvipc/shm", "re");
47 if (!f) {
48 if (errno == ENOENT)
49 return 0;
50
56f64d95 51 log_warning_errno(errno, "Failed to open /proc/sysvipc/shm: %m");
66cdd0f2
LP
52 return -errno;
53 }
54
55 FOREACH_LINE(line, f, goto fail) {
56 unsigned n_attached;
57 pid_t cpid, lpid;
58 uid_t uid, cuid;
59 gid_t gid, cgid;
60 int shmid;
61
62 if (first) {
63 first = false;
64 continue;
65 }
66
67 truncate_nl(line);
68
69 if (sscanf(line, "%*i %i %*o %*u " PID_FMT " " PID_FMT " %u " UID_FMT " " GID_FMT " " UID_FMT " " GID_FMT,
70 &shmid, &cpid, &lpid, &n_attached, &uid, &gid, &cuid, &cgid) != 8)
71 continue;
72
73 if (n_attached > 0)
74 continue;
75
76 if (uid != delete_uid)
77 continue;
78
79 if (shmctl(shmid, IPC_RMID, NULL) < 0) {
80
81 /* Ignore entries that are already deleted */
82 if (errno == EIDRM || errno == EINVAL)
83 continue;
84
94c156cd
LP
85 ret = log_warning_errno(errno,
86 "Failed to remove SysV shared memory segment %i: %m",
87 shmid);
66cdd0f2
LP
88 }
89 }
90
91 return ret;
92
93fail:
56f64d95 94 log_warning_errno(errno, "Failed to read /proc/sysvipc/shm: %m");
66cdd0f2
LP
95 return -errno;
96}
97
98static int clean_sysvipc_sem(uid_t delete_uid) {
99 _cleanup_fclose_ FILE *f = NULL;
100 char line[LINE_MAX];
101 bool first = true;
102 int ret = 0;
103
104 f = fopen("/proc/sysvipc/sem", "re");
105 if (!f) {
106 if (errno == ENOENT)
107 return 0;
108
56f64d95 109 log_warning_errno(errno, "Failed to open /proc/sysvipc/sem: %m");
66cdd0f2
LP
110 return -errno;
111 }
112
113 FOREACH_LINE(line, f, goto fail) {
114 uid_t uid, cuid;
115 gid_t gid, cgid;
116 int semid;
117
118 if (first) {
119 first = false;
120 continue;
121 }
122
123 truncate_nl(line);
124
125 if (sscanf(line, "%*i %i %*o %*u " UID_FMT " " GID_FMT " " UID_FMT " " GID_FMT,
126 &semid, &uid, &gid, &cuid, &cgid) != 5)
127 continue;
128
129 if (uid != delete_uid)
130 continue;
131
132 if (semctl(semid, 0, IPC_RMID) < 0) {
133
134 /* Ignore entries that are already deleted */
135 if (errno == EIDRM || errno == EINVAL)
136 continue;
137
94c156cd
LP
138 ret = log_warning_errno(errno,
139 "Failed to remove SysV semaphores object %i: %m",
140 semid);
66cdd0f2
LP
141 }
142 }
143
144 return ret;
145
146fail:
56f64d95 147 log_warning_errno(errno, "Failed to read /proc/sysvipc/sem: %m");
66cdd0f2
LP
148 return -errno;
149}
150
151static int clean_sysvipc_msg(uid_t delete_uid) {
152 _cleanup_fclose_ FILE *f = NULL;
153 char line[LINE_MAX];
154 bool first = true;
155 int ret = 0;
156
157 f = fopen("/proc/sysvipc/msg", "re");
158 if (!f) {
159 if (errno == ENOENT)
160 return 0;
161
56f64d95 162 log_warning_errno(errno, "Failed to open /proc/sysvipc/msg: %m");
66cdd0f2
LP
163 return -errno;
164 }
165
166 FOREACH_LINE(line, f, goto fail) {
167 uid_t uid, cuid;
168 gid_t gid, cgid;
169 pid_t cpid, lpid;
170 int msgid;
171
172 if (first) {
173 first = false;
174 continue;
175 }
176
177 truncate_nl(line);
178
179 if (sscanf(line, "%*i %i %*o %*u %*u " PID_FMT " " PID_FMT " " UID_FMT " " GID_FMT " " UID_FMT " " GID_FMT,
180 &msgid, &cpid, &lpid, &uid, &gid, &cuid, &cgid) != 7)
181 continue;
182
183 if (uid != delete_uid)
184 continue;
185
186 if (msgctl(msgid, IPC_RMID, NULL) < 0) {
187
188 /* Ignore entries that are already deleted */
189 if (errno == EIDRM || errno == EINVAL)
190 continue;
191
94c156cd
LP
192 ret = log_warning_errno(errno,
193 "Failed to remove SysV message queue %i: %m",
194 msgid);
66cdd0f2
LP
195 }
196 }
197
198 return ret;
199
200fail:
56f64d95 201 log_warning_errno(errno, "Failed to read /proc/sysvipc/msg: %m");
66cdd0f2
LP
202 return -errno;
203}
204
205static int clean_posix_shm_internal(DIR *dir, uid_t uid) {
206 struct dirent *de;
207 int ret = 0, r;
208
209 assert(dir);
210
211 FOREACH_DIRENT(de, dir, goto fail) {
212 struct stat st;
213
214 if (STR_IN_SET(de->d_name, "..", "."))
215 continue;
216
217 if (fstatat(dirfd(dir), de->d_name, &st, AT_SYMLINK_NOFOLLOW) < 0) {
218 if (errno == ENOENT)
219 continue;
220
56f64d95 221 log_warning_errno(errno, "Failed to stat() POSIX shared memory segment %s: %m", de->d_name);
66cdd0f2
LP
222 ret = -errno;
223 continue;
224 }
225
226 if (st.st_uid != uid)
227 continue;
228
229 if (S_ISDIR(st.st_mode)) {
230 _cleanup_closedir_ DIR *kid;
231
232 kid = xopendirat(dirfd(dir), de->d_name, O_NOFOLLOW|O_NOATIME);
233 if (!kid) {
234 if (errno != ENOENT) {
56f64d95 235 log_warning_errno(errno, "Failed to enter shared memory directory %s: %m", de->d_name);
66cdd0f2
LP
236 ret = -errno;
237 }
238 } else {
239 r = clean_posix_shm_internal(kid, uid);
240 if (r < 0)
241 ret = r;
242 }
243
244 if (unlinkat(dirfd(dir), de->d_name, AT_REMOVEDIR) < 0) {
245
246 if (errno == ENOENT)
247 continue;
248
56f64d95 249 log_warning_errno(errno, "Failed to remove POSIX shared memory directory %s: %m", de->d_name);
66cdd0f2
LP
250 ret = -errno;
251 }
252 } else {
253
254 if (unlinkat(dirfd(dir), de->d_name, 0) < 0) {
255
256 if (errno == ENOENT)
257 continue;
258
56f64d95 259 log_warning_errno(errno, "Failed to remove POSIX shared memory segment %s: %m", de->d_name);
66cdd0f2
LP
260 ret = -errno;
261 }
262 }
263 }
264
265 return ret;
266
267fail:
56f64d95 268 log_warning_errno(errno, "Failed to read /dev/shm: %m");
66cdd0f2
LP
269 return -errno;
270}
271
272static int clean_posix_shm(uid_t uid) {
273 _cleanup_closedir_ DIR *dir = NULL;
274
275 dir = opendir("/dev/shm");
276 if (!dir) {
277 if (errno == ENOENT)
278 return 0;
279
56f64d95 280 log_warning_errno(errno, "Failed to open /dev/shm: %m");
66cdd0f2
LP
281 return -errno;
282 }
283
284 return clean_posix_shm_internal(dir, uid);
285}
286
287static 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
56f64d95 297 log_warning_errno(errno, "Failed to open /dev/mqueue: %m");
66cdd0f2
LP
298 return -errno;
299 }
300
301 FOREACH_DIRENT(de, dir, goto fail) {
302 struct stat st;
303 char fn[1+strlen(de->d_name)+1];
304
305 if (STR_IN_SET(de->d_name, "..", "."))
306 continue;
307
308 if (fstatat(dirfd(dir), de->d_name, &st, AT_SYMLINK_NOFOLLOW) < 0) {
309 if (errno == ENOENT)
310 continue;
311
94c156cd
LP
312 ret = log_warning_errno(errno,
313 "Failed to stat() MQ segment %s: %m",
314 de->d_name);
66cdd0f2
LP
315 continue;
316 }
317
318 if (st.st_uid != uid)
319 continue;
320
321 fn[0] = '/';
322 strcpy(fn+1, de->d_name);
323
324 if (mq_unlink(fn) < 0) {
325 if (errno == ENOENT)
326 continue;
327
94c156cd
LP
328 ret = log_warning_errno(errno,
329 "Failed to unlink POSIX message queue %s: %m",
330 fn);
66cdd0f2
LP
331 }
332 }
333
334 return ret;
335
336fail:
56f64d95 337 log_warning_errno(errno, "Failed to read /dev/mqueue: %m");
66cdd0f2
LP
338 return -errno;
339}
340
341int clean_ipc(uid_t uid) {
342 int ret = 0, r;
343
f7dc3ab9
LP
344 /* Refuse to clean IPC of the root and system users */
345 if (uid <= SYSTEM_UID_MAX)
66cdd0f2
LP
346 return 0;
347
348 r = clean_sysvipc_shm(uid);
349 if (r < 0)
350 ret = r;
351
352 r = clean_sysvipc_sem(uid);
353 if (r < 0)
354 ret = r;
355
356 r = clean_sysvipc_msg(uid);
357 if (r < 0)
358 ret = r;
359
360 r = clean_posix_shm(uid);
361 if (r < 0)
362 ret = r;
363
364 r = clean_posix_mq(uid);
365 if (r < 0)
366 ret = r;
367
368 return ret;
369}