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