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