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