]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/shared/clean-ipc.c
Merge pull request #1790 from endocode/kayrus/fix_man_kernel_cl
[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 return log_warning_errno(errno, "Failed to open /proc/sysvipc/shm: %m");
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 return log_warning_errno(errno, "Failed to read /proc/sysvipc/shm: %m");
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 return log_warning_errno(errno, "Failed to open /proc/sysvipc/sem: %m");
108 }
109
110 FOREACH_LINE(line, f, goto fail) {
111 uid_t uid, cuid;
112 gid_t gid, cgid;
113 int semid;
114
115 if (first) {
116 first = false;
117 continue;
118 }
119
120 truncate_nl(line);
121
122 if (sscanf(line, "%*i %i %*o %*u " UID_FMT " " GID_FMT " " UID_FMT " " GID_FMT,
123 &semid, &uid, &gid, &cuid, &cgid) != 5)
124 continue;
125
126 if (uid != delete_uid)
127 continue;
128
129 if (semctl(semid, 0, IPC_RMID) < 0) {
130
131 /* Ignore entries that are already deleted */
132 if (errno == EIDRM || errno == EINVAL)
133 continue;
134
135 ret = log_warning_errno(errno,
136 "Failed to remove SysV semaphores object %i: %m",
137 semid);
138 }
139 }
140
141 return ret;
142
143 fail:
144 return log_warning_errno(errno, "Failed to read /proc/sysvipc/sem: %m");
145 }
146
147 static int clean_sysvipc_msg(uid_t delete_uid) {
148 _cleanup_fclose_ FILE *f = NULL;
149 char line[LINE_MAX];
150 bool first = true;
151 int ret = 0;
152
153 f = fopen("/proc/sysvipc/msg", "re");
154 if (!f) {
155 if (errno == ENOENT)
156 return 0;
157
158 return log_warning_errno(errno, "Failed to open /proc/sysvipc/msg: %m");
159 }
160
161 FOREACH_LINE(line, f, goto fail) {
162 uid_t uid, cuid;
163 gid_t gid, cgid;
164 pid_t cpid, lpid;
165 int msgid;
166
167 if (first) {
168 first = false;
169 continue;
170 }
171
172 truncate_nl(line);
173
174 if (sscanf(line, "%*i %i %*o %*u %*u " PID_FMT " " PID_FMT " " UID_FMT " " GID_FMT " " UID_FMT " " GID_FMT,
175 &msgid, &cpid, &lpid, &uid, &gid, &cuid, &cgid) != 7)
176 continue;
177
178 if (uid != delete_uid)
179 continue;
180
181 if (msgctl(msgid, IPC_RMID, NULL) < 0) {
182
183 /* Ignore entries that are already deleted */
184 if (errno == EIDRM || errno == EINVAL)
185 continue;
186
187 ret = log_warning_errno(errno,
188 "Failed to remove SysV message queue %i: %m",
189 msgid);
190 }
191 }
192
193 return ret;
194
195 fail:
196 return log_warning_errno(errno, "Failed to read /proc/sysvipc/msg: %m");
197 }
198
199 static int clean_posix_shm_internal(DIR *dir, uid_t uid) {
200 struct dirent *de;
201 int ret = 0, r;
202
203 assert(dir);
204
205 FOREACH_DIRENT(de, dir, goto fail) {
206 struct stat st;
207
208 if (STR_IN_SET(de->d_name, "..", "."))
209 continue;
210
211 if (fstatat(dirfd(dir), de->d_name, &st, AT_SYMLINK_NOFOLLOW) < 0) {
212 if (errno == ENOENT)
213 continue;
214
215 log_warning_errno(errno, "Failed to stat() POSIX shared memory segment %s: %m", de->d_name);
216 ret = -errno;
217 continue;
218 }
219
220 if (st.st_uid != uid)
221 continue;
222
223 if (S_ISDIR(st.st_mode)) {
224 _cleanup_closedir_ DIR *kid;
225
226 kid = xopendirat(dirfd(dir), de->d_name, O_NOFOLLOW|O_NOATIME);
227 if (!kid) {
228 if (errno != ENOENT) {
229 log_warning_errno(errno, "Failed to enter shared memory directory %s: %m", de->d_name);
230 ret = -errno;
231 }
232 } else {
233 r = clean_posix_shm_internal(kid, uid);
234 if (r < 0)
235 ret = r;
236 }
237
238 if (unlinkat(dirfd(dir), de->d_name, AT_REMOVEDIR) < 0) {
239
240 if (errno == ENOENT)
241 continue;
242
243 log_warning_errno(errno, "Failed to remove POSIX shared memory directory %s: %m", de->d_name);
244 ret = -errno;
245 }
246 } else {
247
248 if (unlinkat(dirfd(dir), de->d_name, 0) < 0) {
249
250 if (errno == ENOENT)
251 continue;
252
253 log_warning_errno(errno, "Failed to remove POSIX shared memory segment %s: %m", de->d_name);
254 ret = -errno;
255 }
256 }
257 }
258
259 return ret;
260
261 fail:
262 log_warning_errno(errno, "Failed to read /dev/shm: %m");
263 return -errno;
264 }
265
266 static int clean_posix_shm(uid_t uid) {
267 _cleanup_closedir_ DIR *dir = NULL;
268
269 dir = opendir("/dev/shm");
270 if (!dir) {
271 if (errno == ENOENT)
272 return 0;
273
274 return log_warning_errno(errno, "Failed to open /dev/shm: %m");
275 }
276
277 return clean_posix_shm_internal(dir, uid);
278 }
279
280 static int clean_posix_mq(uid_t uid) {
281 _cleanup_closedir_ DIR *dir = NULL;
282 struct dirent *de;
283 int ret = 0;
284
285 dir = opendir("/dev/mqueue");
286 if (!dir) {
287 if (errno == ENOENT)
288 return 0;
289
290 return log_warning_errno(errno, "Failed to open /dev/mqueue: %m");
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 ret = log_warning_errno(errno,
305 "Failed to stat() MQ segment %s: %m",
306 de->d_name);
307 continue;
308 }
309
310 if (st.st_uid != uid)
311 continue;
312
313 fn[0] = '/';
314 strcpy(fn+1, de->d_name);
315
316 if (mq_unlink(fn) < 0) {
317 if (errno == ENOENT)
318 continue;
319
320 ret = log_warning_errno(errno,
321 "Failed to unlink POSIX message queue %s: %m",
322 fn);
323 }
324 }
325
326 return ret;
327
328 fail:
329 return log_warning_errno(errno, "Failed to read /dev/mqueue: %m");
330 }
331
332 int clean_ipc(uid_t uid) {
333 int ret = 0, r;
334
335 /* Refuse to clean IPC of the root and system users */
336 if (uid <= SYSTEM_UID_MAX)
337 return 0;
338
339 r = clean_sysvipc_shm(uid);
340 if (r < 0)
341 ret = r;
342
343 r = clean_sysvipc_sem(uid);
344 if (r < 0)
345 ret = r;
346
347 r = clean_sysvipc_msg(uid);
348 if (r < 0)
349 ret = r;
350
351 r = clean_posix_shm(uid);
352 if (r < 0)
353 ret = r;
354
355 r = clean_posix_mq(uid);
356 if (r < 0)
357 ret = r;
358
359 return ret;
360 }