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