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