]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/shared/clean-ipc.c
shared: add formats-util.h
[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 log_warning_errno(errno, "Failed to remove SysV shared memory segment %i: %m", shmid);
82 ret = -errno;
83 }
84 }
85
86 return ret;
87
88 fail:
89 log_warning_errno(errno, "Failed to read /proc/sysvipc/shm: %m");
90 return -errno;
91 }
92
93 static int clean_sysvipc_sem(uid_t delete_uid) {
94 _cleanup_fclose_ FILE *f = NULL;
95 char line[LINE_MAX];
96 bool first = true;
97 int ret = 0;
98
99 f = fopen("/proc/sysvipc/sem", "re");
100 if (!f) {
101 if (errno == ENOENT)
102 return 0;
103
104 log_warning_errno(errno, "Failed to open /proc/sysvipc/sem: %m");
105 return -errno;
106 }
107
108 FOREACH_LINE(line, f, goto fail) {
109 uid_t uid, cuid;
110 gid_t gid, cgid;
111 int semid;
112
113 if (first) {
114 first = false;
115 continue;
116 }
117
118 truncate_nl(line);
119
120 if (sscanf(line, "%*i %i %*o %*u " UID_FMT " " GID_FMT " " UID_FMT " " GID_FMT,
121 &semid, &uid, &gid, &cuid, &cgid) != 5)
122 continue;
123
124 if (uid != delete_uid)
125 continue;
126
127 if (semctl(semid, 0, IPC_RMID) < 0) {
128
129 /* Ignore entries that are already deleted */
130 if (errno == EIDRM || errno == EINVAL)
131 continue;
132
133 log_warning_errno(errno, "Failed to remove SysV semaphores object %i: %m", semid);
134 ret = -errno;
135 }
136 }
137
138 return ret;
139
140 fail:
141 log_warning_errno(errno, "Failed to read /proc/sysvipc/sem: %m");
142 return -errno;
143 }
144
145 static int clean_sysvipc_msg(uid_t delete_uid) {
146 _cleanup_fclose_ FILE *f = NULL;
147 char line[LINE_MAX];
148 bool first = true;
149 int ret = 0;
150
151 f = fopen("/proc/sysvipc/msg", "re");
152 if (!f) {
153 if (errno == ENOENT)
154 return 0;
155
156 log_warning_errno(errno, "Failed to open /proc/sysvipc/msg: %m");
157 return -errno;
158 }
159
160 FOREACH_LINE(line, f, goto fail) {
161 uid_t uid, cuid;
162 gid_t gid, cgid;
163 pid_t cpid, lpid;
164 int msgid;
165
166 if (first) {
167 first = false;
168 continue;
169 }
170
171 truncate_nl(line);
172
173 if (sscanf(line, "%*i %i %*o %*u %*u " PID_FMT " " PID_FMT " " UID_FMT " " GID_FMT " " UID_FMT " " GID_FMT,
174 &msgid, &cpid, &lpid, &uid, &gid, &cuid, &cgid) != 7)
175 continue;
176
177 if (uid != delete_uid)
178 continue;
179
180 if (msgctl(msgid, IPC_RMID, NULL) < 0) {
181
182 /* Ignore entries that are already deleted */
183 if (errno == EIDRM || errno == EINVAL)
184 continue;
185
186 log_warning_errno(errno, "Failed to remove SysV message queue %i: %m", msgid);
187 ret = -errno;
188 }
189 }
190
191 return ret;
192
193 fail:
194 log_warning_errno(errno, "Failed to read /proc/sysvipc/msg: %m");
195 return -errno;
196 }
197
198 static int clean_posix_shm_internal(DIR *dir, uid_t uid) {
199 struct dirent *de;
200 int ret = 0, r;
201
202 assert(dir);
203
204 FOREACH_DIRENT(de, dir, goto fail) {
205 struct stat st;
206
207 if (STR_IN_SET(de->d_name, "..", "."))
208 continue;
209
210 if (fstatat(dirfd(dir), de->d_name, &st, AT_SYMLINK_NOFOLLOW) < 0) {
211 if (errno == ENOENT)
212 continue;
213
214 log_warning_errno(errno, "Failed to stat() POSIX shared memory segment %s: %m", de->d_name);
215 ret = -errno;
216 continue;
217 }
218
219 if (st.st_uid != uid)
220 continue;
221
222 if (S_ISDIR(st.st_mode)) {
223 _cleanup_closedir_ DIR *kid;
224
225 kid = xopendirat(dirfd(dir), de->d_name, O_NOFOLLOW|O_NOATIME);
226 if (!kid) {
227 if (errno != ENOENT) {
228 log_warning_errno(errno, "Failed to enter shared memory directory %s: %m", de->d_name);
229 ret = -errno;
230 }
231 } else {
232 r = clean_posix_shm_internal(kid, uid);
233 if (r < 0)
234 ret = r;
235 }
236
237 if (unlinkat(dirfd(dir), de->d_name, AT_REMOVEDIR) < 0) {
238
239 if (errno == ENOENT)
240 continue;
241
242 log_warning_errno(errno, "Failed to remove POSIX shared memory directory %s: %m", de->d_name);
243 ret = -errno;
244 }
245 } else {
246
247 if (unlinkat(dirfd(dir), de->d_name, 0) < 0) {
248
249 if (errno == ENOENT)
250 continue;
251
252 log_warning_errno(errno, "Failed to remove POSIX shared memory segment %s: %m", de->d_name);
253 ret = -errno;
254 }
255 }
256 }
257
258 return ret;
259
260 fail:
261 log_warning_errno(errno, "Failed to read /dev/shm: %m");
262 return -errno;
263 }
264
265 static int clean_posix_shm(uid_t uid) {
266 _cleanup_closedir_ DIR *dir = NULL;
267
268 dir = opendir("/dev/shm");
269 if (!dir) {
270 if (errno == ENOENT)
271 return 0;
272
273 log_warning_errno(errno, "Failed to open /dev/shm: %m");
274 return -errno;
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 log_warning_errno(errno, "Failed to open /dev/mqueue: %m");
291 return -errno;
292 }
293
294 FOREACH_DIRENT(de, dir, goto fail) {
295 struct stat st;
296 char fn[1+strlen(de->d_name)+1];
297
298 if (STR_IN_SET(de->d_name, "..", "."))
299 continue;
300
301 if (fstatat(dirfd(dir), de->d_name, &st, AT_SYMLINK_NOFOLLOW) < 0) {
302 if (errno == ENOENT)
303 continue;
304
305 log_warning_errno(errno, "Failed to stat() MQ segment %s: %m", de->d_name);
306 ret = -errno;
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 log_warning_errno(errno, "Failed to unlink POSIX message queue %s: %m", fn);
321 ret = -errno;
322 }
323 }
324
325 return ret;
326
327 fail:
328 log_warning_errno(errno, "Failed to read /dev/mqueue: %m");
329 return -errno;
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 }