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