]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/shared/clean-ipc.c
util: split out escaping code into escape.[ch]
[thirdparty/systemd.git] / src / shared / clean-ipc.c
CommitLineData
66cdd0f2
LP
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>
66cdd0f2
LP
27#include <fcntl.h>
28#include <dirent.h>
29#include <mqueue.h>
30
31#include "util.h"
6482f626 32#include "formats-util.h"
66cdd0f2
LP
33#include "strv.h"
34#include "clean-ipc.h"
35
36static 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
56f64d95 47 log_warning_errno(errno, "Failed to open /proc/sysvipc/shm: %m");
66cdd0f2
LP
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
94c156cd
LP
81 ret = log_warning_errno(errno,
82 "Failed to remove SysV shared memory segment %i: %m",
83 shmid);
66cdd0f2
LP
84 }
85 }
86
87 return ret;
88
89fail:
56f64d95 90 log_warning_errno(errno, "Failed to read /proc/sysvipc/shm: %m");
66cdd0f2
LP
91 return -errno;
92}
93
94static 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
56f64d95 105 log_warning_errno(errno, "Failed to open /proc/sysvipc/sem: %m");
66cdd0f2
LP
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
94c156cd
LP
134 ret = log_warning_errno(errno,
135 "Failed to remove SysV semaphores object %i: %m",
136 semid);
66cdd0f2
LP
137 }
138 }
139
140 return ret;
141
142fail:
56f64d95 143 log_warning_errno(errno, "Failed to read /proc/sysvipc/sem: %m");
66cdd0f2
LP
144 return -errno;
145}
146
147static 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
56f64d95 158 log_warning_errno(errno, "Failed to open /proc/sysvipc/msg: %m");
66cdd0f2
LP
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
94c156cd
LP
188 ret = log_warning_errno(errno,
189 "Failed to remove SysV message queue %i: %m",
190 msgid);
66cdd0f2
LP
191 }
192 }
193
194 return ret;
195
196fail:
56f64d95 197 log_warning_errno(errno, "Failed to read /proc/sysvipc/msg: %m");
66cdd0f2
LP
198 return -errno;
199}
200
201static 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
56f64d95 217 log_warning_errno(errno, "Failed to stat() POSIX shared memory segment %s: %m", de->d_name);
66cdd0f2
LP
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) {
56f64d95 231 log_warning_errno(errno, "Failed to enter shared memory directory %s: %m", de->d_name);
66cdd0f2
LP
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
56f64d95 245 log_warning_errno(errno, "Failed to remove POSIX shared memory directory %s: %m", de->d_name);
66cdd0f2
LP
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
56f64d95 255 log_warning_errno(errno, "Failed to remove POSIX shared memory segment %s: %m", de->d_name);
66cdd0f2
LP
256 ret = -errno;
257 }
258 }
259 }
260
261 return ret;
262
263fail:
56f64d95 264 log_warning_errno(errno, "Failed to read /dev/shm: %m");
66cdd0f2
LP
265 return -errno;
266}
267
268static 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
56f64d95 276 log_warning_errno(errno, "Failed to open /dev/shm: %m");
66cdd0f2
LP
277 return -errno;
278 }
279
280 return clean_posix_shm_internal(dir, uid);
281}
282
283static 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
56f64d95 293 log_warning_errno(errno, "Failed to open /dev/mqueue: %m");
66cdd0f2
LP
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
94c156cd
LP
308 ret = log_warning_errno(errno,
309 "Failed to stat() MQ segment %s: %m",
310 de->d_name);
66cdd0f2
LP
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
94c156cd
LP
324 ret = log_warning_errno(errno,
325 "Failed to unlink POSIX message queue %s: %m",
326 fn);
66cdd0f2
LP
327 }
328 }
329
330 return ret;
331
332fail:
56f64d95 333 log_warning_errno(errno, "Failed to read /dev/mqueue: %m");
66cdd0f2
LP
334 return -errno;
335}
336
337int clean_ipc(uid_t uid) {
338 int ret = 0, r;
339
f7dc3ab9
LP
340 /* Refuse to clean IPC of the root and system users */
341 if (uid <= SYSTEM_UID_MAX)
66cdd0f2
LP
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}