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