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