]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/login/logind-inhibit.c
login: fix use after free
[thirdparty/systemd.git] / src / login / logind-inhibit.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
2
3 #include <errno.h>
4 #include <fcntl.h>
5 #include <string.h>
6 #include <sys/stat.h>
7 #include <sys/types.h>
8 #include <unistd.h>
9
10 #include "alloc-util.h"
11 #include "env-file.h"
12 #include "escape.h"
13 #include "fd-util.h"
14 #include "fileio.h"
15 #include "format-util.h"
16 #include "io-util.h"
17 #include "logind-dbus.h"
18 #include "logind-inhibit.h"
19 #include "mkdir.h"
20 #include "parse-util.h"
21 #include "path-util.h"
22 #include "string-table.h"
23 #include "string-util.h"
24 #include "tmpfile-util.h"
25 #include "user-util.h"
26 #include "util.h"
27
28 static void inhibitor_remove_fifo(Inhibitor *i);
29
30 int inhibitor_new(Inhibitor **ret, Manager *m, const char* id) {
31 _cleanup_(inhibitor_freep) Inhibitor *i = NULL;
32 int r;
33
34 assert(ret);
35 assert(m);
36 assert(id);
37
38 i = new(Inhibitor, 1);
39 if (!i)
40 return -ENOMEM;
41
42 *i = (Inhibitor) {
43 .manager = m,
44 .what = _INHIBIT_WHAT_INVALID,
45 .mode = _INHIBIT_MODE_INVALID,
46 .uid = UID_INVALID,
47 .fifo_fd = -1,
48 };
49
50 i->state_file = path_join("/run/systemd/inhibit", id);
51 if (!i->state_file)
52 return -ENOMEM;
53
54 i->id = basename(i->state_file);
55
56 r = hashmap_put(m->inhibitors, i->id, i);
57 if (r < 0)
58 return r;
59
60 *ret = TAKE_PTR(i);
61 return 0;
62 }
63
64 Inhibitor* inhibitor_free(Inhibitor *i) {
65
66 if (!i)
67 return NULL;
68
69 free(i->who);
70 free(i->why);
71
72 sd_event_source_unref(i->event_source);
73 safe_close(i->fifo_fd);
74
75 hashmap_remove(i->manager->inhibitors, i->id);
76
77 /* Note that we don't remove neither the state file nor the fifo path here, since we want both to
78 * survive daemon restarts */
79 free(i->state_file);
80 free(i->fifo_path);
81
82 return mfree(i);
83 }
84
85 static int inhibitor_save(Inhibitor *i) {
86 _cleanup_free_ char *temp_path = NULL;
87 _cleanup_fclose_ FILE *f = NULL;
88 int r;
89
90 assert(i);
91
92 r = mkdir_safe_label("/run/systemd/inhibit", 0755, 0, 0, MKDIR_WARN_MODE);
93 if (r < 0)
94 goto fail;
95
96 r = fopen_temporary(i->state_file, &f, &temp_path);
97 if (r < 0)
98 goto fail;
99
100 (void) fchmod(fileno(f), 0644);
101
102 fprintf(f,
103 "# This is private data. Do not parse.\n"
104 "WHAT=%s\n"
105 "MODE=%s\n"
106 "UID="UID_FMT"\n"
107 "PID="PID_FMT"\n",
108 inhibit_what_to_string(i->what),
109 inhibit_mode_to_string(i->mode),
110 i->uid,
111 i->pid);
112
113 if (i->who) {
114 _cleanup_free_ char *cc = NULL;
115
116 cc = cescape(i->who);
117 if (!cc) {
118 r = -ENOMEM;
119 goto fail;
120 }
121
122 fprintf(f, "WHO=%s\n", cc);
123 }
124
125 if (i->why) {
126 _cleanup_free_ char *cc = NULL;
127
128 cc = cescape(i->why);
129 if (!cc) {
130 r = -ENOMEM;
131 goto fail;
132 }
133
134 fprintf(f, "WHY=%s\n", cc);
135 }
136
137 if (i->fifo_path)
138 fprintf(f, "FIFO=%s\n", i->fifo_path);
139
140 r = fflush_and_check(f);
141 if (r < 0)
142 goto fail;
143
144 if (rename(temp_path, i->state_file) < 0) {
145 r = -errno;
146 goto fail;
147 }
148
149 return 0;
150
151 fail:
152 (void) unlink(i->state_file);
153
154 if (temp_path)
155 (void) unlink(temp_path);
156
157 return log_error_errno(r, "Failed to save inhibit data %s: %m", i->state_file);
158 }
159
160 static int bus_manager_send_inhibited_change(Inhibitor *i) {
161 const char *property;
162
163 assert(i);
164
165 property = i->mode == INHIBIT_BLOCK ? "BlockInhibited" : "DelayInhibited";
166
167 return manager_send_changed(i->manager, property, NULL);
168 }
169
170 int inhibitor_start(Inhibitor *i) {
171 assert(i);
172
173 if (i->started)
174 return 0;
175
176 dual_timestamp_get(&i->since);
177
178 log_debug("Inhibitor %s (%s) pid="PID_FMT" uid="UID_FMT" mode=%s started.",
179 strna(i->who), strna(i->why),
180 i->pid, i->uid,
181 inhibit_mode_to_string(i->mode));
182
183 i->started = true;
184
185 inhibitor_save(i);
186
187 bus_manager_send_inhibited_change(i);
188
189 return 0;
190 }
191
192 void inhibitor_stop(Inhibitor *i) {
193 assert(i);
194
195 if (i->started)
196 log_debug("Inhibitor %s (%s) pid="PID_FMT" uid="UID_FMT" mode=%s stopped.",
197 strna(i->who), strna(i->why),
198 i->pid, i->uid,
199 inhibit_mode_to_string(i->mode));
200
201 inhibitor_remove_fifo(i);
202
203 if (i->state_file)
204 (void) unlink(i->state_file);
205
206 i->started = false;
207
208 bus_manager_send_inhibited_change(i);
209 }
210
211 int inhibitor_load(Inhibitor *i) {
212
213 _cleanup_free_ char
214 *what = NULL,
215 *uid = NULL,
216 *pid = NULL,
217 *who = NULL,
218 *why = NULL,
219 *mode = NULL;
220
221 InhibitWhat w;
222 InhibitMode mm;
223 char *cc;
224 int r;
225
226 r = parse_env_file(NULL, i->state_file,
227 "WHAT", &what,
228 "UID", &uid,
229 "PID", &pid,
230 "WHO", &who,
231 "WHY", &why,
232 "MODE", &mode,
233 "FIFO", &i->fifo_path);
234 if (r < 0)
235 return log_error_errno(r, "Failed to read %s: %m", i->state_file);
236
237 w = what ? inhibit_what_from_string(what) : 0;
238 if (w >= 0)
239 i->what = w;
240
241 mm = mode ? inhibit_mode_from_string(mode) : INHIBIT_BLOCK;
242 if (mm >= 0)
243 i->mode = mm;
244
245 if (uid) {
246 r = parse_uid(uid, &i->uid);
247 if (r < 0)
248 log_debug_errno(r, "Failed to parse UID of inhibitor: %s", uid);
249 }
250
251 if (pid) {
252 r = parse_pid(pid, &i->pid);
253 if (r < 0)
254 log_debug_errno(r, "Failed to parse PID of inhibitor: %s", pid);
255 }
256
257 if (who) {
258 r = cunescape(who, 0, &cc);
259 if (r < 0)
260 return log_oom();
261
262 free_and_replace(i->who, cc);
263 }
264
265 if (why) {
266 r = cunescape(why, 0, &cc);
267 if (r < 0)
268 return log_oom();
269
270 free_and_replace(i->why, cc);
271 }
272
273 if (i->fifo_path) {
274 _cleanup_close_ int fd = -1;
275
276 /* Let's re-open the FIFO on both sides, and close the writing side right away */
277 fd = inhibitor_create_fifo(i);
278 if (fd < 0)
279 return log_error_errno(fd, "Failed to reopen FIFO: %m");
280 }
281
282 return 0;
283 }
284
285 static int inhibitor_dispatch_fifo(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
286 Inhibitor *i = userdata;
287
288 assert(s);
289 assert(fd == i->fifo_fd);
290 assert(i);
291
292 inhibitor_stop(i);
293 inhibitor_free(i);
294
295 return 0;
296 }
297
298 int inhibitor_create_fifo(Inhibitor *i) {
299 int r;
300
301 assert(i);
302
303 /* Create FIFO */
304 if (!i->fifo_path) {
305 r = mkdir_safe_label("/run/systemd/inhibit", 0755, 0, 0, MKDIR_WARN_MODE);
306 if (r < 0)
307 return r;
308
309 i->fifo_path = strjoin("/run/systemd/inhibit/", i->id, ".ref");
310 if (!i->fifo_path)
311 return -ENOMEM;
312
313 if (mkfifo(i->fifo_path, 0600) < 0 && errno != EEXIST)
314 return -errno;
315 }
316
317 /* Open reading side */
318 if (i->fifo_fd < 0) {
319 i->fifo_fd = open(i->fifo_path, O_RDONLY|O_CLOEXEC|O_NONBLOCK);
320 if (i->fifo_fd < 0)
321 return -errno;
322 }
323
324 if (!i->event_source) {
325 r = sd_event_add_io(i->manager->event, &i->event_source, i->fifo_fd, 0, inhibitor_dispatch_fifo, i);
326 if (r < 0)
327 return r;
328
329 r = sd_event_source_set_priority(i->event_source, SD_EVENT_PRIORITY_IDLE-10);
330 if (r < 0)
331 return r;
332
333 (void) sd_event_source_set_description(i->event_source, "inhibitor-ref");
334 }
335
336 /* Open writing side */
337 r = open(i->fifo_path, O_WRONLY|O_CLOEXEC|O_NONBLOCK);
338 if (r < 0)
339 return -errno;
340
341 return r;
342 }
343
344 static void inhibitor_remove_fifo(Inhibitor *i) {
345 assert(i);
346
347 i->event_source = sd_event_source_unref(i->event_source);
348 i->fifo_fd = safe_close(i->fifo_fd);
349
350 if (i->fifo_path) {
351 (void) unlink(i->fifo_path);
352 i->fifo_path = mfree(i->fifo_path);
353 }
354 }
355
356 bool inhibitor_is_orphan(Inhibitor *i) {
357 assert(i);
358
359 if (!i->started)
360 return true;
361
362 if (!i->fifo_path)
363 return true;
364
365 if (i->fifo_fd < 0)
366 return true;
367
368 if (pipe_eof(i->fifo_fd) != 0)
369 return true;
370
371 return false;
372 }
373
374 InhibitWhat manager_inhibit_what(Manager *m, InhibitMode mm) {
375 Inhibitor *i;
376 Iterator j;
377 InhibitWhat what = 0;
378
379 assert(m);
380
381 HASHMAP_FOREACH(i, m->inhibitors, j)
382 if (i->mode == mm && i->started)
383 what |= i->what;
384
385 return what;
386 }
387
388 static int pid_is_active(Manager *m, pid_t pid) {
389 Session *s;
390 int r;
391
392 /* Get client session. This is not what you are looking for these days.
393 * FIXME #6852 */
394 r = manager_get_session_by_pid(m, pid, &s);
395 if (r < 0)
396 return r;
397
398 /* If there's no session assigned to it, then it's globally
399 * active on all ttys */
400 if (r == 0)
401 return 1;
402
403 return session_is_active(s);
404 }
405
406 bool manager_is_inhibited(
407 Manager *m,
408 InhibitWhat w,
409 InhibitMode mm,
410 dual_timestamp *since,
411 bool ignore_inactive,
412 bool ignore_uid,
413 uid_t uid,
414 Inhibitor **offending) {
415
416 Inhibitor *i;
417 Iterator j;
418 struct dual_timestamp ts = DUAL_TIMESTAMP_NULL;
419 bool inhibited = false;
420
421 assert(m);
422 assert(w > 0 && w < _INHIBIT_WHAT_MAX);
423
424 HASHMAP_FOREACH(i, m->inhibitors, j) {
425 if (!i->started)
426 continue;
427
428 if (!(i->what & w))
429 continue;
430
431 if (i->mode != mm)
432 continue;
433
434 if (ignore_inactive && pid_is_active(m, i->pid) <= 0)
435 continue;
436
437 if (ignore_uid && i->uid == uid)
438 continue;
439
440 if (!inhibited ||
441 i->since.monotonic < ts.monotonic)
442 ts = i->since;
443
444 inhibited = true;
445
446 if (offending)
447 *offending = i;
448 }
449
450 if (since)
451 *since = ts;
452
453 return inhibited;
454 }
455
456 const char *inhibit_what_to_string(InhibitWhat w) {
457 static thread_local char buffer[97];
458 char *p;
459
460 if (w < 0 || w >= _INHIBIT_WHAT_MAX)
461 return NULL;
462
463 p = buffer;
464 if (w & INHIBIT_SHUTDOWN)
465 p = stpcpy(p, "shutdown:");
466 if (w & INHIBIT_SLEEP)
467 p = stpcpy(p, "sleep:");
468 if (w & INHIBIT_IDLE)
469 p = stpcpy(p, "idle:");
470 if (w & INHIBIT_HANDLE_POWER_KEY)
471 p = stpcpy(p, "handle-power-key:");
472 if (w & INHIBIT_HANDLE_SUSPEND_KEY)
473 p = stpcpy(p, "handle-suspend-key:");
474 if (w & INHIBIT_HANDLE_HIBERNATE_KEY)
475 p = stpcpy(p, "handle-hibernate-key:");
476 if (w & INHIBIT_HANDLE_LID_SWITCH)
477 p = stpcpy(p, "handle-lid-switch:");
478
479 if (p > buffer)
480 *(p-1) = 0;
481 else
482 *p = 0;
483
484 return buffer;
485 }
486
487 InhibitWhat inhibit_what_from_string(const char *s) {
488 InhibitWhat what = 0;
489 const char *word, *state;
490 size_t l;
491
492 FOREACH_WORD_SEPARATOR(word, l, s, ":", state) {
493 if (l == 8 && strneq(word, "shutdown", l))
494 what |= INHIBIT_SHUTDOWN;
495 else if (l == 5 && strneq(word, "sleep", l))
496 what |= INHIBIT_SLEEP;
497 else if (l == 4 && strneq(word, "idle", l))
498 what |= INHIBIT_IDLE;
499 else if (l == 16 && strneq(word, "handle-power-key", l))
500 what |= INHIBIT_HANDLE_POWER_KEY;
501 else if (l == 18 && strneq(word, "handle-suspend-key", l))
502 what |= INHIBIT_HANDLE_SUSPEND_KEY;
503 else if (l == 20 && strneq(word, "handle-hibernate-key", l))
504 what |= INHIBIT_HANDLE_HIBERNATE_KEY;
505 else if (l == 17 && strneq(word, "handle-lid-switch", l))
506 what |= INHIBIT_HANDLE_LID_SWITCH;
507 else
508 return _INHIBIT_WHAT_INVALID;
509 }
510
511 return what;
512 }
513
514 static const char* const inhibit_mode_table[_INHIBIT_MODE_MAX] = {
515 [INHIBIT_BLOCK] = "block",
516 [INHIBIT_DELAY] = "delay"
517 };
518
519 DEFINE_STRING_TABLE_LOOKUP(inhibit_mode, InhibitMode);