]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/shared/clean-ipc.c
Merge pull request #1691 from poettering/util-lib-3
[thirdparty/systemd.git] / src / shared / clean-ipc.c
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 <dirent.h>
23 #include <fcntl.h>
24 #include <mqueue.h>
25 #include <sys/ipc.h>
26 #include <sys/msg.h>
27 #include <sys/sem.h>
28 #include <sys/shm.h>
29 #include <sys/stat.h>
30
31 #include "clean-ipc.h"
32 #include "fd-util.h"
33 #include "fileio.h"
34 #include "formats-util.h"
35 #include "string-util.h"
36 #include "strv.h"
37 #include "util.h"
38 #include "dirent-util.h"
39
40 static 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
51 log_warning_errno(errno, "Failed to open /proc/sysvipc/shm: %m");
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
85 ret = log_warning_errno(errno,
86 "Failed to remove SysV shared memory segment %i: %m",
87 shmid);
88 }
89 }
90
91 return ret;
92
93 fail:
94 log_warning_errno(errno, "Failed to read /proc/sysvipc/shm: %m");
95 return -errno;
96 }
97
98 static 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
109 log_warning_errno(errno, "Failed to open /proc/sysvipc/sem: %m");
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
138 ret = log_warning_errno(errno,
139 "Failed to remove SysV semaphores object %i: %m",
140 semid);
141 }
142 }
143
144 return ret;
145
146 fail:
147 log_warning_errno(errno, "Failed to read /proc/sysvipc/sem: %m");
148 return -errno;
149 }
150
151 static 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
162 log_warning_errno(errno, "Failed to open /proc/sysvipc/msg: %m");
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
192 ret = log_warning_errno(errno,
193 "Failed to remove SysV message queue %i: %m",
194 msgid);
195 }
196 }
197
198 return ret;
199
200 fail:
201 log_warning_errno(errno, "Failed to read /proc/sysvipc/msg: %m");
202 return -errno;
203 }
204
205 static 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
221 log_warning_errno(errno, "Failed to stat() POSIX shared memory segment %s: %m", de->d_name);
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) {
235 log_warning_errno(errno, "Failed to enter shared memory directory %s: %m", de->d_name);
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
249 log_warning_errno(errno, "Failed to remove POSIX shared memory directory %s: %m", de->d_name);
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
259 log_warning_errno(errno, "Failed to remove POSIX shared memory segment %s: %m", de->d_name);
260 ret = -errno;
261 }
262 }
263 }
264
265 return ret;
266
267 fail:
268 log_warning_errno(errno, "Failed to read /dev/shm: %m");
269 return -errno;
270 }
271
272 static 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
280 log_warning_errno(errno, "Failed to open /dev/shm: %m");
281 return -errno;
282 }
283
284 return clean_posix_shm_internal(dir, uid);
285 }
286
287 static 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
297 log_warning_errno(errno, "Failed to open /dev/mqueue: %m");
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
312 ret = log_warning_errno(errno,
313 "Failed to stat() MQ segment %s: %m",
314 de->d_name);
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
328 ret = log_warning_errno(errno,
329 "Failed to unlink POSIX message queue %s: %m",
330 fn);
331 }
332 }
333
334 return ret;
335
336 fail:
337 log_warning_errno(errno, "Failed to read /dev/mqueue: %m");
338 return -errno;
339 }
340
341 int clean_ipc(uid_t uid) {
342 int ret = 0, r;
343
344 /* Refuse to clean IPC of the root and system users */
345 if (uid <= SYSTEM_UID_MAX)
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 }