]>
Commit | Line | Data |
---|---|---|
53e1b683 | 1 | /* SPDX-License-Identifier: LGPL-2.1+ */ |
66cdd0f2 | 2 | |
07630cea | 3 | #include <dirent.h> |
a8fbdf54 | 4 | #include <errno.h> |
07630cea | 5 | #include <fcntl.h> |
a8fbdf54 | 6 | #include <limits.h> |
07630cea | 7 | #include <mqueue.h> |
a8fbdf54 TA |
8 | #include <stdbool.h> |
9 | #include <stdio.h> | |
10 | #include <string.h> | |
66cdd0f2 | 11 | #include <sys/ipc.h> |
66cdd0f2 | 12 | #include <sys/msg.h> |
07630cea LP |
13 | #include <sys/sem.h> |
14 | #include <sys/shm.h> | |
66cdd0f2 | 15 | #include <sys/stat.h> |
a8fbdf54 | 16 | #include <unistd.h> |
66cdd0f2 | 17 | |
3ffd4af2 | 18 | #include "clean-ipc.h" |
cf0fbc49 | 19 | #include "dirent-util.h" |
3ffd4af2 | 20 | #include "fd-util.h" |
0d39fa9c | 21 | #include "fileio.h" |
f97b34a6 | 22 | #include "format-util.h" |
a8fbdf54 TA |
23 | #include "log.h" |
24 | #include "macro.h" | |
07630cea | 25 | #include "string-util.h" |
66cdd0f2 | 26 | #include "strv.h" |
00d9ef85 | 27 | #include "user-util.h" |
66cdd0f2 | 28 | |
00d9ef85 LP |
29 | static bool match_uid_gid(uid_t subject_uid, gid_t subject_gid, uid_t delete_uid, gid_t delete_gid) { |
30 | ||
31 | if (uid_is_valid(delete_uid) && subject_uid == delete_uid) | |
32 | return true; | |
33 | ||
34 | if (gid_is_valid(delete_gid) && subject_gid == delete_gid) | |
35 | return true; | |
36 | ||
37 | return false; | |
38 | } | |
39 | ||
98e4fcec | 40 | static int clean_sysvipc_shm(uid_t delete_uid, gid_t delete_gid, bool rm) { |
66cdd0f2 | 41 | _cleanup_fclose_ FILE *f = NULL; |
66cdd0f2 | 42 | bool first = true; |
d5b3c07d | 43 | int ret = 0, r; |
66cdd0f2 LP |
44 | |
45 | f = fopen("/proc/sysvipc/shm", "re"); | |
46 | if (!f) { | |
47 | if (errno == ENOENT) | |
48 | return 0; | |
49 | ||
e1427b13 | 50 | return log_warning_errno(errno, "Failed to open /proc/sysvipc/shm: %m"); |
66cdd0f2 LP |
51 | } |
52 | ||
d5b3c07d LP |
53 | for (;;) { |
54 | _cleanup_free_ char *line = NULL; | |
66cdd0f2 LP |
55 | unsigned n_attached; |
56 | pid_t cpid, lpid; | |
57 | uid_t uid, cuid; | |
58 | gid_t gid, cgid; | |
59 | int shmid; | |
60 | ||
d5b3c07d LP |
61 | r = read_line(f, LONG_LINE_MAX, &line); |
62 | if (r < 0) | |
63 | return log_warning_errno(errno, "Failed to read /proc/sysvipc/shm: %m"); | |
64 | if (r == 0) | |
65 | break; | |
66 | ||
66cdd0f2 LP |
67 | if (first) { |
68 | first = false; | |
69 | continue; | |
70 | } | |
71 | ||
66cdd0f2 LP |
72 | if (sscanf(line, "%*i %i %*o %*u " PID_FMT " " PID_FMT " %u " UID_FMT " " GID_FMT " " UID_FMT " " GID_FMT, |
73 | &shmid, &cpid, &lpid, &n_attached, &uid, &gid, &cuid, &cgid) != 8) | |
74 | continue; | |
75 | ||
76 | if (n_attached > 0) | |
77 | continue; | |
78 | ||
00d9ef85 | 79 | if (!match_uid_gid(uid, gid, delete_uid, delete_gid)) |
66cdd0f2 LP |
80 | continue; |
81 | ||
98e4fcec LP |
82 | if (!rm) |
83 | return 1; | |
84 | ||
66cdd0f2 LP |
85 | if (shmctl(shmid, IPC_RMID, NULL) < 0) { |
86 | ||
87 | /* Ignore entries that are already deleted */ | |
3742095b | 88 | if (IN_SET(errno, EIDRM, EINVAL)) |
66cdd0f2 LP |
89 | continue; |
90 | ||
94c156cd LP |
91 | ret = log_warning_errno(errno, |
92 | "Failed to remove SysV shared memory segment %i: %m", | |
93 | shmid); | |
98e4fcec | 94 | } else { |
8a384842 | 95 | log_debug("Removed SysV shared memory segment %i.", shmid); |
98e4fcec LP |
96 | if (ret == 0) |
97 | ret = 1; | |
98 | } | |
66cdd0f2 LP |
99 | } |
100 | ||
101 | return ret; | |
66cdd0f2 LP |
102 | } |
103 | ||
98e4fcec | 104 | static int clean_sysvipc_sem(uid_t delete_uid, gid_t delete_gid, bool rm) { |
66cdd0f2 | 105 | _cleanup_fclose_ FILE *f = NULL; |
66cdd0f2 | 106 | bool first = true; |
d5b3c07d | 107 | int ret = 0, r; |
66cdd0f2 LP |
108 | |
109 | f = fopen("/proc/sysvipc/sem", "re"); | |
110 | if (!f) { | |
111 | if (errno == ENOENT) | |
112 | return 0; | |
113 | ||
e1427b13 | 114 | return log_warning_errno(errno, "Failed to open /proc/sysvipc/sem: %m"); |
66cdd0f2 LP |
115 | } |
116 | ||
d5b3c07d LP |
117 | for (;;) { |
118 | _cleanup_free_ char *line = NULL; | |
66cdd0f2 LP |
119 | uid_t uid, cuid; |
120 | gid_t gid, cgid; | |
121 | int semid; | |
122 | ||
d5b3c07d LP |
123 | r = read_line(f, LONG_LINE_MAX, &line); |
124 | if (r < 0) | |
125 | return log_warning_errno(r, "Failed to read /proc/sysvipc/sem: %m"); | |
126 | if (r == 0) | |
127 | break; | |
128 | ||
66cdd0f2 LP |
129 | if (first) { |
130 | first = false; | |
131 | continue; | |
132 | } | |
133 | ||
66cdd0f2 LP |
134 | if (sscanf(line, "%*i %i %*o %*u " UID_FMT " " GID_FMT " " UID_FMT " " GID_FMT, |
135 | &semid, &uid, &gid, &cuid, &cgid) != 5) | |
136 | continue; | |
137 | ||
00d9ef85 | 138 | if (!match_uid_gid(uid, gid, delete_uid, delete_gid)) |
66cdd0f2 LP |
139 | continue; |
140 | ||
98e4fcec LP |
141 | if (!rm) |
142 | return 1; | |
143 | ||
66cdd0f2 LP |
144 | if (semctl(semid, 0, IPC_RMID) < 0) { |
145 | ||
146 | /* Ignore entries that are already deleted */ | |
3742095b | 147 | if (IN_SET(errno, EIDRM, EINVAL)) |
66cdd0f2 LP |
148 | continue; |
149 | ||
94c156cd LP |
150 | ret = log_warning_errno(errno, |
151 | "Failed to remove SysV semaphores object %i: %m", | |
152 | semid); | |
98e4fcec | 153 | } else { |
8a384842 | 154 | log_debug("Removed SysV semaphore %i.", semid); |
98e4fcec LP |
155 | if (ret == 0) |
156 | ret = 1; | |
157 | } | |
66cdd0f2 LP |
158 | } |
159 | ||
160 | return ret; | |
66cdd0f2 LP |
161 | } |
162 | ||
98e4fcec | 163 | static int clean_sysvipc_msg(uid_t delete_uid, gid_t delete_gid, bool rm) { |
66cdd0f2 | 164 | _cleanup_fclose_ FILE *f = NULL; |
66cdd0f2 | 165 | bool first = true; |
d5b3c07d | 166 | int ret = 0, r; |
66cdd0f2 LP |
167 | |
168 | f = fopen("/proc/sysvipc/msg", "re"); | |
169 | if (!f) { | |
170 | if (errno == ENOENT) | |
171 | return 0; | |
172 | ||
e1427b13 | 173 | return log_warning_errno(errno, "Failed to open /proc/sysvipc/msg: %m"); |
66cdd0f2 LP |
174 | } |
175 | ||
d5b3c07d LP |
176 | for (;;) { |
177 | _cleanup_free_ char *line = NULL; | |
66cdd0f2 LP |
178 | uid_t uid, cuid; |
179 | gid_t gid, cgid; | |
180 | pid_t cpid, lpid; | |
181 | int msgid; | |
182 | ||
d5b3c07d LP |
183 | r = read_line(f, LONG_LINE_MAX, &line); |
184 | if (r < 0) | |
185 | return log_warning_errno(r, "Failed to read /proc/sysvipc/msg: %m"); | |
186 | if (r == 0) | |
187 | break; | |
188 | ||
66cdd0f2 LP |
189 | if (first) { |
190 | first = false; | |
191 | continue; | |
192 | } | |
193 | ||
66cdd0f2 LP |
194 | if (sscanf(line, "%*i %i %*o %*u %*u " PID_FMT " " PID_FMT " " UID_FMT " " GID_FMT " " UID_FMT " " GID_FMT, |
195 | &msgid, &cpid, &lpid, &uid, &gid, &cuid, &cgid) != 7) | |
196 | continue; | |
197 | ||
00d9ef85 | 198 | if (!match_uid_gid(uid, gid, delete_uid, delete_gid)) |
66cdd0f2 LP |
199 | continue; |
200 | ||
98e4fcec LP |
201 | if (!rm) |
202 | return 1; | |
203 | ||
66cdd0f2 LP |
204 | if (msgctl(msgid, IPC_RMID, NULL) < 0) { |
205 | ||
206 | /* Ignore entries that are already deleted */ | |
3742095b | 207 | if (IN_SET(errno, EIDRM, EINVAL)) |
66cdd0f2 LP |
208 | continue; |
209 | ||
94c156cd LP |
210 | ret = log_warning_errno(errno, |
211 | "Failed to remove SysV message queue %i: %m", | |
212 | msgid); | |
98e4fcec | 213 | } else { |
8a384842 | 214 | log_debug("Removed SysV message queue %i.", msgid); |
98e4fcec LP |
215 | if (ret == 0) |
216 | ret = 1; | |
217 | } | |
66cdd0f2 LP |
218 | } |
219 | ||
220 | return ret; | |
66cdd0f2 LP |
221 | } |
222 | ||
98e4fcec | 223 | static int clean_posix_shm_internal(DIR *dir, uid_t uid, gid_t gid, bool rm) { |
66cdd0f2 LP |
224 | struct dirent *de; |
225 | int ret = 0, r; | |
226 | ||
227 | assert(dir); | |
228 | ||
91f2048c | 229 | FOREACH_DIRENT_ALL(de, dir, goto fail) { |
66cdd0f2 LP |
230 | struct stat st; |
231 | ||
49bfc877 | 232 | if (dot_or_dot_dot(de->d_name)) |
66cdd0f2 LP |
233 | continue; |
234 | ||
235 | if (fstatat(dirfd(dir), de->d_name, &st, AT_SYMLINK_NOFOLLOW) < 0) { | |
236 | if (errno == ENOENT) | |
237 | continue; | |
238 | ||
3db99289 | 239 | ret = log_warning_errno(errno, "Failed to stat() POSIX shared memory segment %s: %m", de->d_name); |
66cdd0f2 LP |
240 | continue; |
241 | } | |
242 | ||
66cdd0f2 LP |
243 | if (S_ISDIR(st.st_mode)) { |
244 | _cleanup_closedir_ DIR *kid; | |
245 | ||
246 | kid = xopendirat(dirfd(dir), de->d_name, O_NOFOLLOW|O_NOATIME); | |
247 | if (!kid) { | |
3db99289 LP |
248 | if (errno != ENOENT) |
249 | ret = log_warning_errno(errno, "Failed to enter shared memory directory %s: %m", de->d_name); | |
66cdd0f2 | 250 | } else { |
98e4fcec | 251 | r = clean_posix_shm_internal(kid, uid, gid, rm); |
66cdd0f2 LP |
252 | if (r < 0) |
253 | ret = r; | |
254 | } | |
255 | ||
98e4fcec LP |
256 | if (!match_uid_gid(st.st_uid, st.st_gid, uid, gid)) |
257 | continue; | |
258 | ||
259 | if (!rm) | |
260 | return 1; | |
261 | ||
66cdd0f2 LP |
262 | if (unlinkat(dirfd(dir), de->d_name, AT_REMOVEDIR) < 0) { |
263 | ||
264 | if (errno == ENOENT) | |
265 | continue; | |
266 | ||
3db99289 | 267 | ret = log_warning_errno(errno, "Failed to remove POSIX shared memory directory %s: %m", de->d_name); |
98e4fcec | 268 | } else { |
8a384842 | 269 | log_debug("Removed POSIX shared memory directory %s", de->d_name); |
98e4fcec LP |
270 | if (ret == 0) |
271 | ret = 1; | |
272 | } | |
66cdd0f2 LP |
273 | } else { |
274 | ||
98e4fcec LP |
275 | if (!match_uid_gid(st.st_uid, st.st_gid, uid, gid)) |
276 | continue; | |
277 | ||
278 | if (!rm) | |
279 | return 1; | |
280 | ||
66cdd0f2 LP |
281 | if (unlinkat(dirfd(dir), de->d_name, 0) < 0) { |
282 | ||
283 | if (errno == ENOENT) | |
284 | continue; | |
285 | ||
3db99289 | 286 | ret = log_warning_errno(errno, "Failed to remove POSIX shared memory segment %s: %m", de->d_name); |
98e4fcec | 287 | } else { |
8a384842 | 288 | log_debug("Removed POSIX shared memory segment %s", de->d_name); |
98e4fcec LP |
289 | if (ret == 0) |
290 | ret = 1; | |
291 | } | |
66cdd0f2 LP |
292 | } |
293 | } | |
294 | ||
295 | return ret; | |
296 | ||
297 | fail: | |
3db99289 | 298 | return log_warning_errno(errno, "Failed to read /dev/shm: %m"); |
66cdd0f2 LP |
299 | } |
300 | ||
98e4fcec | 301 | static int clean_posix_shm(uid_t uid, gid_t gid, bool rm) { |
66cdd0f2 LP |
302 | _cleanup_closedir_ DIR *dir = NULL; |
303 | ||
304 | dir = opendir("/dev/shm"); | |
305 | if (!dir) { | |
306 | if (errno == ENOENT) | |
307 | return 0; | |
308 | ||
e1427b13 | 309 | return log_warning_errno(errno, "Failed to open /dev/shm: %m"); |
66cdd0f2 LP |
310 | } |
311 | ||
98e4fcec | 312 | return clean_posix_shm_internal(dir, uid, gid, rm); |
66cdd0f2 LP |
313 | } |
314 | ||
98e4fcec | 315 | static int clean_posix_mq(uid_t uid, gid_t gid, bool rm) { |
66cdd0f2 LP |
316 | _cleanup_closedir_ DIR *dir = NULL; |
317 | struct dirent *de; | |
318 | int ret = 0; | |
319 | ||
320 | dir = opendir("/dev/mqueue"); | |
321 | if (!dir) { | |
322 | if (errno == ENOENT) | |
323 | return 0; | |
324 | ||
e1427b13 | 325 | return log_warning_errno(errno, "Failed to open /dev/mqueue: %m"); |
66cdd0f2 LP |
326 | } |
327 | ||
91f2048c | 328 | FOREACH_DIRENT_ALL(de, dir, goto fail) { |
66cdd0f2 LP |
329 | struct stat st; |
330 | char fn[1+strlen(de->d_name)+1]; | |
331 | ||
49bfc877 | 332 | if (dot_or_dot_dot(de->d_name)) |
66cdd0f2 LP |
333 | continue; |
334 | ||
335 | if (fstatat(dirfd(dir), de->d_name, &st, AT_SYMLINK_NOFOLLOW) < 0) { | |
336 | if (errno == ENOENT) | |
337 | continue; | |
338 | ||
94c156cd LP |
339 | ret = log_warning_errno(errno, |
340 | "Failed to stat() MQ segment %s: %m", | |
341 | de->d_name); | |
66cdd0f2 LP |
342 | continue; |
343 | } | |
344 | ||
00d9ef85 | 345 | if (!match_uid_gid(st.st_uid, st.st_gid, uid, gid)) |
66cdd0f2 LP |
346 | continue; |
347 | ||
98e4fcec LP |
348 | if (!rm) |
349 | return 1; | |
350 | ||
66cdd0f2 LP |
351 | fn[0] = '/'; |
352 | strcpy(fn+1, de->d_name); | |
353 | ||
354 | if (mq_unlink(fn) < 0) { | |
355 | if (errno == ENOENT) | |
356 | continue; | |
357 | ||
94c156cd LP |
358 | ret = log_warning_errno(errno, |
359 | "Failed to unlink POSIX message queue %s: %m", | |
360 | fn); | |
98e4fcec | 361 | } else { |
8a384842 | 362 | log_debug("Removed POSIX message queue %s", fn); |
98e4fcec LP |
363 | if (ret == 0) |
364 | ret = 1; | |
365 | } | |
66cdd0f2 LP |
366 | } |
367 | ||
368 | return ret; | |
369 | ||
370 | fail: | |
e1427b13 | 371 | return log_warning_errno(errno, "Failed to read /dev/mqueue: %m"); |
66cdd0f2 LP |
372 | } |
373 | ||
98e4fcec | 374 | int clean_ipc_internal(uid_t uid, gid_t gid, bool rm) { |
66cdd0f2 LP |
375 | int ret = 0, r; |
376 | ||
98e4fcec LP |
377 | /* If 'rm' is true, clean all IPC objects owned by either the specified UID or the specified GID. Return the |
378 | * last error encountered or == 0 if no matching IPC objects have been found or > 0 if matching IPC objects | |
379 | * have been found and have been removed. | |
380 | * | |
381 | * If 'rm' is false, just search for IPC objects owned by either the specified UID or the specified GID. In | |
382 | * this case we return < 0 on error, > 0 if we found a matching object, == 0 if we didn't. | |
383 | * | |
384 | * As special rule: if UID/GID is specified as root we'll silently not clean up things, and always claim that | |
385 | * there are IPC objects for it. */ | |
386 | ||
387 | if (uid == 0) { | |
388 | if (!rm) | |
389 | return 1; | |
390 | ||
391 | uid = UID_INVALID; | |
392 | } | |
393 | if (gid == 0) { | |
394 | if (!rm) | |
395 | return 1; | |
396 | ||
397 | gid = GID_INVALID; | |
398 | } | |
399 | ||
00d9ef85 LP |
400 | /* Anything to do? */ |
401 | if (!uid_is_valid(uid) && !gid_is_valid(gid)) | |
66cdd0f2 LP |
402 | return 0; |
403 | ||
98e4fcec LP |
404 | r = clean_sysvipc_shm(uid, gid, rm); |
405 | if (r != 0) { | |
406 | if (!rm) | |
407 | return r; | |
408 | if (ret == 0) | |
409 | ret = r; | |
410 | } | |
66cdd0f2 | 411 | |
98e4fcec LP |
412 | r = clean_sysvipc_sem(uid, gid, rm); |
413 | if (r != 0) { | |
414 | if (!rm) | |
415 | return r; | |
416 | if (ret == 0) | |
417 | ret = r; | |
418 | } | |
66cdd0f2 | 419 | |
98e4fcec LP |
420 | r = clean_sysvipc_msg(uid, gid, rm); |
421 | if (r != 0) { | |
422 | if (!rm) | |
423 | return r; | |
424 | if (ret == 0) | |
425 | ret = r; | |
426 | } | |
66cdd0f2 | 427 | |
98e4fcec LP |
428 | r = clean_posix_shm(uid, gid, rm); |
429 | if (r != 0) { | |
430 | if (!rm) | |
431 | return r; | |
432 | if (ret == 0) | |
433 | ret = r; | |
434 | } | |
66cdd0f2 | 435 | |
98e4fcec LP |
436 | r = clean_posix_mq(uid, gid, rm); |
437 | if (r != 0) { | |
438 | if (!rm) | |
439 | return r; | |
440 | if (ret == 0) | |
441 | ret = r; | |
442 | } | |
66cdd0f2 LP |
443 | |
444 | return ret; | |
445 | } | |
00d9ef85 LP |
446 | |
447 | int clean_ipc_by_uid(uid_t uid) { | |
98e4fcec | 448 | return clean_ipc_internal(uid, GID_INVALID, true); |
00d9ef85 LP |
449 | } |
450 | ||
451 | int clean_ipc_by_gid(gid_t gid) { | |
98e4fcec | 452 | return clean_ipc_internal(UID_INVALID, gid, true); |
00d9ef85 | 453 | } |