Line data Source code
1 : /* SPDX-License-Identifier: LGPL-2.1+ */
2 : /***
3 : Copyright © 2015 Werner Fink
4 : ***/
5 :
6 : #include <errno.h>
7 : #include <fcntl.h>
8 : #include <getopt.h>
9 : #include <poll.h>
10 : #include <signal.h>
11 : #include <stdbool.h>
12 : #include <stddef.h>
13 : #include <string.h>
14 : #include <sys/inotify.h>
15 : #include <sys/prctl.h>
16 : #include <sys/signalfd.h>
17 : #include <sys/socket.h>
18 : #include <sys/stat.h>
19 : #include <sys/types.h>
20 : #include <sys/un.h>
21 : #include <sys/wait.h>
22 : #include <unistd.h>
23 :
24 : #include "alloc-util.h"
25 : #include "ask-password-api.h"
26 : #include "conf-parser.h"
27 : #include "def.h"
28 : #include "dirent-util.h"
29 : #include "exit-status.h"
30 : #include "fd-util.h"
31 : #include "fileio.h"
32 : #include "hashmap.h"
33 : #include "io-util.h"
34 : #include "macro.h"
35 : #include "main-func.h"
36 : #include "memory-util.h"
37 : #include "mkdir.h"
38 : #include "path-util.h"
39 : #include "plymouth-util.h"
40 : #include "pretty-print.h"
41 : #include "process-util.h"
42 : #include "set.h"
43 : #include "signal-util.h"
44 : #include "socket-util.h"
45 : #include "string-util.h"
46 : #include "strv.h"
47 : #include "terminal-util.h"
48 : #include "utmp-wtmp.h"
49 :
50 : static enum {
51 : ACTION_LIST,
52 : ACTION_QUERY,
53 : ACTION_WATCH,
54 : ACTION_WALL
55 : } arg_action = ACTION_QUERY;
56 :
57 : static bool arg_plymouth = false;
58 : static bool arg_console = false;
59 : static const char *arg_device = NULL;
60 :
61 0 : static int ask_password_plymouth(
62 : const char *message,
63 : usec_t until,
64 : AskPasswordFlags flags,
65 : const char *flag_file,
66 : char ***ret) {
67 :
68 : static const union sockaddr_union sa = PLYMOUTH_SOCKET;
69 0 : _cleanup_close_ int fd = -1, notify = -1;
70 0 : _cleanup_free_ char *packet = NULL;
71 : ssize_t k;
72 : int r, n;
73 0 : struct pollfd pollfd[2] = {};
74 : char buffer[LINE_MAX];
75 0 : size_t p = 0;
76 : enum {
77 : POLL_SOCKET,
78 : POLL_INOTIFY
79 : };
80 :
81 0 : assert(ret);
82 :
83 0 : if (flag_file) {
84 0 : notify = inotify_init1(IN_CLOEXEC|IN_NONBLOCK);
85 0 : if (notify < 0)
86 0 : return -errno;
87 :
88 0 : r = inotify_add_watch(notify, flag_file, IN_ATTRIB); /* for the link count */
89 0 : if (r < 0)
90 0 : return -errno;
91 : }
92 :
93 0 : fd = socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
94 0 : if (fd < 0)
95 0 : return -errno;
96 :
97 0 : r = connect(fd, &sa.sa, SOCKADDR_UN_LEN(sa.un));
98 0 : if (r < 0)
99 0 : return -errno;
100 :
101 0 : if (flags & ASK_PASSWORD_ACCEPT_CACHED) {
102 0 : packet = strdup("c");
103 0 : n = 1;
104 0 : } else if (asprintf(&packet, "*\002%c%s%n", (int) (strlen(message) + 1), message, &n) < 0)
105 0 : packet = NULL;
106 0 : if (!packet)
107 0 : return -ENOMEM;
108 :
109 0 : r = loop_write(fd, packet, n + 1, true);
110 0 : if (r < 0)
111 0 : return r;
112 :
113 0 : pollfd[POLL_SOCKET].fd = fd;
114 0 : pollfd[POLL_SOCKET].events = POLLIN;
115 0 : pollfd[POLL_INOTIFY].fd = notify;
116 0 : pollfd[POLL_INOTIFY].events = POLLIN;
117 :
118 0 : for (;;) {
119 0 : int sleep_for = -1, j;
120 :
121 0 : if (until > 0) {
122 : usec_t y;
123 :
124 0 : y = now(CLOCK_MONOTONIC);
125 :
126 0 : if (y > until) {
127 0 : r = -ETIME;
128 0 : goto finish;
129 : }
130 :
131 0 : sleep_for = (int) ((until - y) / USEC_PER_MSEC);
132 : }
133 :
134 0 : if (flag_file && access(flag_file, F_OK) < 0) {
135 0 : r = -errno;
136 0 : goto finish;
137 : }
138 :
139 0 : j = poll(pollfd, notify >= 0 ? 2 : 1, sleep_for);
140 0 : if (j < 0) {
141 0 : if (errno == EINTR)
142 0 : continue;
143 :
144 0 : r = -errno;
145 0 : goto finish;
146 0 : } else if (j == 0) {
147 0 : r = -ETIME;
148 0 : goto finish;
149 : }
150 :
151 0 : if (notify >= 0 && pollfd[POLL_INOTIFY].revents != 0)
152 0 : (void) flush_fd(notify);
153 :
154 0 : if (pollfd[POLL_SOCKET].revents == 0)
155 0 : continue;
156 :
157 0 : k = read(fd, buffer + p, sizeof(buffer) - p);
158 0 : if (k < 0) {
159 0 : if (IN_SET(errno, EINTR, EAGAIN))
160 0 : continue;
161 :
162 0 : r = -errno;
163 0 : goto finish;
164 0 : } else if (k == 0) {
165 0 : r = -EIO;
166 0 : goto finish;
167 : }
168 :
169 0 : p += k;
170 :
171 0 : if (p < 1)
172 0 : continue;
173 :
174 0 : if (buffer[0] == 5) {
175 :
176 0 : if (flags & ASK_PASSWORD_ACCEPT_CACHED) {
177 : /* Hmm, first try with cached
178 : * passwords failed, so let's retry
179 : * with a normal password request */
180 0 : packet = mfree(packet);
181 :
182 0 : if (asprintf(&packet, "*\002%c%s%n", (int) (strlen(message) + 1), message, &n) < 0) {
183 0 : r = -ENOMEM;
184 0 : goto finish;
185 : }
186 :
187 0 : r = loop_write(fd, packet, n+1, true);
188 0 : if (r < 0)
189 0 : goto finish;
190 :
191 0 : flags &= ~ASK_PASSWORD_ACCEPT_CACHED;
192 0 : p = 0;
193 0 : continue;
194 : }
195 :
196 : /* No password, because UI not shown */
197 0 : r = -ENOENT;
198 0 : goto finish;
199 :
200 0 : } else if (IN_SET(buffer[0], 2, 9)) {
201 : uint32_t size;
202 : char **l;
203 :
204 : /* One or more answers */
205 0 : if (p < 5)
206 0 : continue;
207 :
208 0 : memcpy(&size, buffer+1, sizeof(size));
209 0 : size = le32toh(size);
210 0 : if (size + 5 > sizeof(buffer)) {
211 0 : r = -EIO;
212 0 : goto finish;
213 : }
214 :
215 0 : if (p-5 < size)
216 0 : continue;
217 :
218 0 : l = strv_parse_nulstr(buffer + 5, size);
219 0 : if (!l) {
220 0 : r = -ENOMEM;
221 0 : goto finish;
222 : }
223 :
224 0 : *ret = l;
225 0 : break;
226 :
227 : } else {
228 : /* Unknown packet */
229 0 : r = -EIO;
230 0 : goto finish;
231 : }
232 : }
233 :
234 0 : r = 0;
235 :
236 0 : finish:
237 0 : explicit_bzero_safe(buffer, sizeof(buffer));
238 0 : return r;
239 : }
240 :
241 0 : static int send_passwords(const char *socket_name, char **passwords) {
242 0 : _cleanup_(erase_and_freep) char *packet = NULL;
243 0 : _cleanup_close_ int socket_fd = -1;
244 0 : union sockaddr_union sa = {};
245 0 : size_t packet_length = 1;
246 : char **p, *d;
247 : ssize_t n;
248 : int salen;
249 :
250 0 : assert(socket_name);
251 :
252 0 : salen = sockaddr_un_set_path(&sa.un, socket_name);
253 0 : if (salen < 0)
254 0 : return salen;
255 :
256 0 : STRV_FOREACH(p, passwords)
257 0 : packet_length += strlen(*p) + 1;
258 :
259 0 : packet = new(char, packet_length);
260 0 : if (!packet)
261 0 : return -ENOMEM;
262 :
263 0 : packet[0] = '+';
264 :
265 0 : d = packet + 1;
266 0 : STRV_FOREACH(p, passwords)
267 0 : d = stpcpy(d, *p) + 1;
268 :
269 0 : socket_fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0);
270 0 : if (socket_fd < 0)
271 0 : return log_debug_errno(errno, "socket(): %m");
272 :
273 0 : n = sendto(socket_fd, packet, packet_length, MSG_NOSIGNAL, &sa.sa, salen);
274 0 : if (n < 0)
275 0 : return log_debug_errno(errno, "sendto(): %m");
276 :
277 0 : return (int) n;
278 : }
279 :
280 0 : static int parse_password(const char *filename, char **wall) {
281 0 : _cleanup_free_ char *socket_name = NULL, *message = NULL;
282 0 : bool accept_cached = false, echo = false;
283 0 : uint64_t not_after = 0;
284 0 : unsigned pid = 0;
285 :
286 0 : const ConfigTableItem items[] = {
287 : { "Ask", "Socket", config_parse_string, 0, &socket_name },
288 : { "Ask", "NotAfter", config_parse_uint64, 0, ¬_after },
289 : { "Ask", "Message", config_parse_string, 0, &message },
290 : { "Ask", "PID", config_parse_unsigned, 0, &pid },
291 : { "Ask", "AcceptCached", config_parse_bool, 0, &accept_cached },
292 : { "Ask", "Echo", config_parse_bool, 0, &echo },
293 : {}
294 : };
295 :
296 : int r;
297 :
298 0 : assert(filename);
299 :
300 0 : r = config_parse(NULL, filename, NULL,
301 : NULL,
302 : config_item_table_lookup, items,
303 : CONFIG_PARSE_RELAXED|CONFIG_PARSE_WARN, NULL);
304 0 : if (r < 0)
305 0 : return r;
306 :
307 0 : if (!socket_name)
308 0 : return log_error_errno(SYNTHETIC_ERRNO(EBADMSG),
309 : "Invalid password file %s", filename);
310 :
311 0 : if (not_after > 0 && now(CLOCK_MONOTONIC) > not_after)
312 0 : return 0;
313 :
314 0 : if (pid > 0 && !pid_is_alive(pid))
315 0 : return 0;
316 :
317 0 : if (arg_action == ACTION_LIST)
318 0 : printf("'%s' (PID %u)\n", message, pid);
319 :
320 0 : else if (arg_action == ACTION_WALL) {
321 : char *_wall;
322 :
323 0 : if (asprintf(&_wall,
324 : "%s%sPassword entry required for \'%s\' (PID %u).\r\n"
325 : "Please enter password with the systemd-tty-ask-password-agent tool:",
326 : strempty(*wall),
327 0 : *wall ? "\r\n\r\n" : "",
328 : message,
329 : pid) < 0)
330 0 : return log_oom();
331 :
332 0 : free(*wall);
333 0 : *wall = _wall;
334 :
335 : } else {
336 0 : _cleanup_strv_free_erase_ char **passwords = NULL;
337 :
338 0 : assert(IN_SET(arg_action, ACTION_QUERY, ACTION_WATCH));
339 :
340 0 : if (access(socket_name, W_OK) < 0) {
341 0 : if (arg_action == ACTION_QUERY)
342 0 : log_info("Not querying '%s' (PID %u), lacking privileges.", message, pid);
343 :
344 0 : return 0;
345 : }
346 :
347 0 : if (arg_plymouth)
348 0 : r = ask_password_plymouth(message, not_after, accept_cached ? ASK_PASSWORD_ACCEPT_CACHED : 0, filename, &passwords);
349 : else {
350 0 : int tty_fd = -1;
351 :
352 0 : if (arg_console) {
353 0 : const char *con = arg_device ?: "/dev/console";
354 :
355 0 : tty_fd = acquire_terminal(con, ACQUIRE_TERMINAL_WAIT, USEC_INFINITY);
356 0 : if (tty_fd < 0)
357 0 : return log_error_errno(tty_fd, "Failed to acquire %s: %m", con);
358 :
359 0 : r = reset_terminal_fd(tty_fd, true);
360 0 : if (r < 0)
361 0 : log_warning_errno(r, "Failed to reset terminal, ignoring: %m");
362 : }
363 :
364 0 : r = ask_password_tty(tty_fd, message, NULL, not_after,
365 0 : (echo ? ASK_PASSWORD_ECHO : 0) |
366 0 : (arg_console ? ASK_PASSWORD_CONSOLE_COLOR : 0),
367 : filename, &passwords);
368 :
369 0 : if (arg_console) {
370 0 : tty_fd = safe_close(tty_fd);
371 0 : release_terminal();
372 : }
373 : }
374 :
375 : /* If the query went away, that's OK */
376 0 : if (IN_SET(r, -ETIME, -ENOENT))
377 0 : return 0;
378 :
379 0 : if (r < 0)
380 0 : return log_error_errno(r, "Failed to query password: %m");
381 :
382 0 : r = send_passwords(socket_name, passwords);
383 0 : if (r < 0)
384 0 : return log_error_errno(r, "Failed to send: %m");
385 : }
386 :
387 0 : return 0;
388 : }
389 :
390 0 : static int wall_tty_block(void) {
391 0 : _cleanup_free_ char *p = NULL;
392 : dev_t devnr;
393 : int fd, r;
394 :
395 0 : r = get_ctty_devnr(0, &devnr);
396 0 : if (r == -ENXIO) /* We have no controlling tty */
397 0 : return -ENOTTY;
398 0 : if (r < 0)
399 0 : return log_error_errno(r, "Failed to get controlling TTY: %m");
400 :
401 0 : if (asprintf(&p, "/run/systemd/ask-password-block/%u:%u", major(devnr), minor(devnr)) < 0)
402 0 : return log_oom();
403 :
404 0 : (void) mkdir_parents_label(p, 0700);
405 0 : (void) mkfifo(p, 0600);
406 :
407 0 : fd = open(p, O_RDONLY|O_CLOEXEC|O_NONBLOCK|O_NOCTTY);
408 0 : if (fd < 0)
409 0 : return log_debug_errno(errno, "Failed to open %s: %m", p);
410 :
411 0 : return fd;
412 : }
413 :
414 0 : static bool wall_tty_match(const char *path, void *userdata) {
415 0 : _cleanup_free_ char *p = NULL;
416 0 : _cleanup_close_ int fd = -1;
417 : struct stat st;
418 :
419 0 : if (!path_is_absolute(path))
420 0 : path = strjoina("/dev/", path);
421 :
422 0 : if (lstat(path, &st) < 0) {
423 0 : log_debug_errno(errno, "Failed to stat %s: %m", path);
424 0 : return true;
425 : }
426 :
427 0 : if (!S_ISCHR(st.st_mode)) {
428 0 : log_debug("%s is not a character device.", path);
429 0 : return true;
430 : }
431 :
432 : /* We use named pipes to ensure that wall messages suggesting
433 : * password entry are not printed over password prompts
434 : * already shown. We use the fact here that opening a pipe in
435 : * non-blocking mode for write-only will succeed only if
436 : * there's some writer behind it. Using pipes has the
437 : * advantage that the block will automatically go away if the
438 : * process dies. */
439 :
440 0 : if (asprintf(&p, "/run/systemd/ask-password-block/%u:%u", major(st.st_rdev), minor(st.st_rdev)) < 0) {
441 0 : log_oom();
442 0 : return true;
443 : }
444 :
445 0 : fd = open(p, O_WRONLY|O_CLOEXEC|O_NONBLOCK|O_NOCTTY);
446 0 : if (fd < 0) {
447 0 : log_debug_errno(errno, "Failed to open the wall pipe: %m");
448 0 : return 1;
449 : }
450 :
451 : /* What, we managed to open the pipe? Then this tty is filtered. */
452 0 : return 0;
453 : }
454 :
455 0 : static int show_passwords(void) {
456 0 : _cleanup_closedir_ DIR *d;
457 : struct dirent *de;
458 0 : int r = 0;
459 :
460 0 : d = opendir("/run/systemd/ask-password");
461 0 : if (!d) {
462 0 : if (errno == ENOENT)
463 0 : return 0;
464 :
465 0 : return log_error_errno(errno, "Failed to open /run/systemd/ask-password: %m");
466 : }
467 :
468 0 : FOREACH_DIRENT_ALL(de, d, return log_error_errno(errno, "Failed to read directory: %m")) {
469 0 : _cleanup_free_ char *p = NULL, *wall = NULL;
470 : int q;
471 :
472 : /* We only support /dev on tmpfs, hence we can rely on
473 : * d_type to be reliable */
474 :
475 0 : if (de->d_type != DT_REG)
476 0 : continue;
477 :
478 0 : if (hidden_or_backup_file(de->d_name))
479 0 : continue;
480 :
481 0 : if (!startswith(de->d_name, "ask."))
482 0 : continue;
483 :
484 0 : p = path_join("/run/systemd/ask-password", de->d_name);
485 0 : if (!p)
486 0 : return log_oom();
487 :
488 0 : q = parse_password(p, &wall);
489 0 : if (q < 0 && r == 0)
490 0 : r = q;
491 :
492 0 : if (wall)
493 0 : (void) utmp_wall(wall, NULL, NULL, wall_tty_match, NULL);
494 : }
495 :
496 0 : return r;
497 : }
498 :
499 0 : static int watch_passwords(void) {
500 : enum {
501 : FD_INOTIFY,
502 : FD_SIGNAL,
503 : _FD_MAX
504 : };
505 :
506 0 : _cleanup_close_ int notify = -1, signal_fd = -1, tty_block_fd = -1;
507 0 : struct pollfd pollfd[_FD_MAX] = {};
508 : sigset_t mask;
509 : int r;
510 :
511 0 : tty_block_fd = wall_tty_block();
512 :
513 0 : (void) mkdir_p_label("/run/systemd/ask-password", 0755);
514 :
515 0 : notify = inotify_init1(IN_CLOEXEC);
516 0 : if (notify < 0)
517 0 : return log_error_errno(errno, "Failed to allocate directory watch: %m");
518 :
519 0 : if (inotify_add_watch(notify, "/run/systemd/ask-password", IN_CLOSE_WRITE|IN_MOVED_TO) < 0) {
520 0 : if (errno == ENOSPC)
521 0 : return log_error_errno(errno, "Failed to add /run/systemd/ask-password to directory watch: inotify watch limit reached");
522 : else
523 0 : return log_error_errno(errno, "Failed to add /run/systemd/ask-password to directory watch: %m");
524 : }
525 :
526 0 : assert_se(sigemptyset(&mask) >= 0);
527 0 : assert_se(sigset_add_many(&mask, SIGINT, SIGTERM, -1) >= 0);
528 0 : assert_se(sigprocmask(SIG_SETMASK, &mask, NULL) >= 0);
529 :
530 0 : signal_fd = signalfd(-1, &mask, SFD_NONBLOCK|SFD_CLOEXEC);
531 0 : if (signal_fd < 0)
532 0 : return log_error_errno(errno, "Failed to allocate signal file descriptor: %m");
533 :
534 0 : pollfd[FD_INOTIFY].fd = notify;
535 0 : pollfd[FD_INOTIFY].events = POLLIN;
536 0 : pollfd[FD_SIGNAL].fd = signal_fd;
537 0 : pollfd[FD_SIGNAL].events = POLLIN;
538 :
539 : for (;;) {
540 0 : r = show_passwords();
541 0 : if (r < 0)
542 0 : log_error_errno(r, "Failed to show password: %m");
543 :
544 0 : if (poll(pollfd, _FD_MAX, -1) < 0) {
545 0 : if (errno == EINTR)
546 0 : continue;
547 :
548 0 : return -errno;
549 : }
550 :
551 0 : if (pollfd[FD_INOTIFY].revents != 0)
552 0 : (void) flush_fd(notify);
553 :
554 0 : if (pollfd[FD_SIGNAL].revents != 0)
555 0 : break;
556 : }
557 :
558 0 : return 0;
559 : }
560 :
561 3 : static int help(void) {
562 3 : _cleanup_free_ char *link = NULL;
563 : int r;
564 :
565 3 : r = terminal_urlify_man("systemd-tty-ask-password-agent", "1", &link);
566 3 : if (r < 0)
567 0 : return log_oom();
568 :
569 3 : printf("%s [OPTIONS...]\n\n"
570 : "Process system password requests.\n\n"
571 : " -h --help Show this help\n"
572 : " --version Show package version\n"
573 : " --list Show pending password requests\n"
574 : " --query Process pending password requests\n"
575 : " --watch Continuously process password requests\n"
576 : " --wall Continuously forward password requests to wall\n"
577 : " --plymouth Ask question with Plymouth instead of on TTY\n"
578 : " --console Ask question on /dev/console instead of current TTY\n"
579 : "\nSee the %s for details.\n"
580 : , program_invocation_short_name
581 : , link
582 : );
583 :
584 3 : return 0;
585 : }
586 :
587 4 : static int parse_argv(int argc, char *argv[]) {
588 :
589 : enum {
590 : ARG_LIST = 0x100,
591 : ARG_QUERY,
592 : ARG_WATCH,
593 : ARG_WALL,
594 : ARG_PLYMOUTH,
595 : ARG_CONSOLE,
596 : ARG_VERSION
597 : };
598 :
599 : static const struct option options[] = {
600 : { "help", no_argument, NULL, 'h' },
601 : { "version", no_argument, NULL, ARG_VERSION },
602 : { "list", no_argument, NULL, ARG_LIST },
603 : { "query", no_argument, NULL, ARG_QUERY },
604 : { "watch", no_argument, NULL, ARG_WATCH },
605 : { "wall", no_argument, NULL, ARG_WALL },
606 : { "plymouth", no_argument, NULL, ARG_PLYMOUTH },
607 : { "console", optional_argument, NULL, ARG_CONSOLE },
608 : {}
609 : };
610 :
611 : int c;
612 :
613 4 : assert(argc >= 0);
614 4 : assert(argv);
615 :
616 4 : while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0)
617 :
618 4 : switch (c) {
619 :
620 3 : case 'h':
621 3 : return help();
622 :
623 0 : case ARG_VERSION:
624 0 : return version();
625 :
626 0 : case ARG_LIST:
627 0 : arg_action = ACTION_LIST;
628 0 : break;
629 :
630 0 : case ARG_QUERY:
631 0 : arg_action = ACTION_QUERY;
632 0 : break;
633 :
634 0 : case ARG_WATCH:
635 0 : arg_action = ACTION_WATCH;
636 0 : break;
637 :
638 0 : case ARG_WALL:
639 0 : arg_action = ACTION_WALL;
640 0 : break;
641 :
642 0 : case ARG_PLYMOUTH:
643 0 : arg_plymouth = true;
644 0 : break;
645 :
646 0 : case ARG_CONSOLE:
647 0 : arg_console = true;
648 0 : if (optarg) {
649 :
650 0 : if (isempty(optarg))
651 0 : return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
652 : "Empty console device path is not allowed.");
653 :
654 0 : arg_device = optarg;
655 : }
656 0 : break;
657 :
658 1 : case '?':
659 1 : return -EINVAL;
660 :
661 0 : default:
662 0 : assert_not_reached("Unhandled option");
663 : }
664 :
665 0 : if (optind != argc)
666 0 : return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
667 : "%s takes no arguments.", program_invocation_short_name);
668 :
669 0 : if (arg_plymouth || arg_console) {
670 :
671 0 : if (!IN_SET(arg_action, ACTION_QUERY, ACTION_WATCH))
672 0 : return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
673 : "Options --query and --watch conflict.");
674 :
675 0 : if (arg_plymouth && arg_console)
676 0 : return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
677 : "Options --plymouth and --console conflict.");
678 : }
679 :
680 0 : return 1;
681 : }
682 :
683 : /*
684 : * To be able to ask on all terminal devices of /dev/console the devices are collected. If more than one
685 : * device is found, then on each of the terminals a inquiring task is forked. Every task has its own session
686 : * and its own controlling terminal. If one of the tasks does handle a password, the remaining tasks will be
687 : * terminated.
688 : */
689 0 : static int ask_on_this_console(const char *tty, pid_t *ret_pid, char **arguments) {
690 : static const struct sigaction sigchld = {
691 : .sa_handler = nop_signal_handler,
692 : .sa_flags = SA_NOCLDSTOP | SA_RESTART,
693 : };
694 : static const struct sigaction sighup = {
695 : .sa_handler = SIG_DFL,
696 : .sa_flags = SA_RESTART,
697 : };
698 : int r;
699 :
700 0 : assert_se(sigaction(SIGCHLD, &sigchld, NULL) >= 0);
701 0 : assert_se(sigaction(SIGHUP, &sighup, NULL) >= 0);
702 0 : assert_se(sigprocmask_many(SIG_UNBLOCK, NULL, SIGHUP, SIGCHLD, -1) >= 0);
703 :
704 0 : r = safe_fork("(sd-passwd)", FORK_RESET_SIGNALS|FORK_LOG, ret_pid);
705 0 : if (r < 0)
706 0 : return r;
707 0 : if (r == 0) {
708 : char **i;
709 :
710 0 : assert_se(prctl(PR_SET_PDEATHSIG, SIGHUP) >= 0);
711 :
712 0 : STRV_FOREACH(i, arguments) {
713 : char *k;
714 :
715 0 : if (!streq(*i, "--console"))
716 0 : continue;
717 :
718 0 : k = strjoin("--console=", tty);
719 0 : if (!k) {
720 0 : log_oom();
721 0 : _exit(EXIT_FAILURE);
722 : }
723 :
724 0 : free_and_replace(*i, k);
725 : }
726 :
727 0 : execv(SYSTEMD_TTY_ASK_PASSWORD_AGENT_BINARY_PATH, arguments);
728 0 : _exit(EXIT_FAILURE);
729 : }
730 :
731 0 : return 0;
732 : }
733 :
734 0 : static void terminate_agents(Set *pids) {
735 : struct timespec ts;
736 0 : siginfo_t status = {};
737 : sigset_t set;
738 : Iterator i;
739 : void *p;
740 : int r, signum;
741 :
742 : /*
743 : * Request termination of the remaining processes as those
744 : * are not required anymore.
745 : */
746 0 : SET_FOREACH(p, pids, i)
747 0 : (void) kill(PTR_TO_PID(p), SIGTERM);
748 :
749 : /*
750 : * Collect the processes which have go away.
751 : */
752 0 : assert_se(sigemptyset(&set) >= 0);
753 0 : assert_se(sigaddset(&set, SIGCHLD) >= 0);
754 0 : timespec_store(&ts, 50 * USEC_PER_MSEC);
755 :
756 0 : while (!set_isempty(pids)) {
757 :
758 0 : zero(status);
759 0 : r = waitid(P_ALL, 0, &status, WEXITED|WNOHANG);
760 0 : if (r < 0 && errno == EINTR)
761 0 : continue;
762 :
763 0 : if (r == 0 && status.si_pid > 0) {
764 0 : set_remove(pids, PID_TO_PTR(status.si_pid));
765 0 : continue;
766 : }
767 :
768 0 : signum = sigtimedwait(&set, NULL, &ts);
769 0 : if (signum < 0) {
770 0 : if (errno != EAGAIN)
771 0 : log_error_errno(errno, "sigtimedwait() failed: %m");
772 0 : break;
773 : }
774 0 : assert(signum == SIGCHLD);
775 : }
776 :
777 : /*
778 : * Kill hanging processes.
779 : */
780 0 : SET_FOREACH(p, pids, i) {
781 0 : log_warning("Failed to terminate child %d, killing it", PTR_TO_PID(p));
782 0 : (void) kill(PTR_TO_PID(p), SIGKILL);
783 : }
784 0 : }
785 :
786 0 : static int ask_on_consoles(char *argv[]) {
787 0 : _cleanup_set_free_ Set *pids = NULL;
788 0 : _cleanup_strv_free_ char **consoles = NULL, **arguments = NULL;
789 0 : siginfo_t status = {};
790 : char **tty;
791 : pid_t pid;
792 : int r;
793 :
794 0 : r = get_kernel_consoles(&consoles);
795 0 : if (r < 0)
796 0 : return log_error_errno(r, "Failed to determine devices of /dev/console: %m");
797 :
798 0 : pids = set_new(NULL);
799 0 : if (!pids)
800 0 : return log_oom();
801 :
802 0 : arguments = strv_copy(argv);
803 0 : if (!arguments)
804 0 : return log_oom();
805 :
806 : /* Start an agent on each console. */
807 0 : STRV_FOREACH(tty, consoles) {
808 0 : r = ask_on_this_console(*tty, &pid, arguments);
809 0 : if (r < 0)
810 0 : return r;
811 :
812 0 : if (set_put(pids, PID_TO_PTR(pid)) < 0)
813 0 : return log_oom();
814 : }
815 :
816 : /* Wait for an agent to exit. */
817 : for (;;) {
818 0 : zero(status);
819 :
820 0 : if (waitid(P_ALL, 0, &status, WEXITED) < 0) {
821 0 : if (errno == EINTR)
822 0 : continue;
823 :
824 0 : return log_error_errno(errno, "waitid() failed: %m");
825 : }
826 :
827 0 : set_remove(pids, PID_TO_PTR(status.si_pid));
828 0 : break;
829 : }
830 :
831 0 : if (!is_clean_exit(status.si_code, status.si_status, EXIT_CLEAN_DAEMON, NULL))
832 0 : log_error("Password agent failed with: %d", status.si_status);
833 :
834 0 : terminate_agents(pids);
835 0 : return 0;
836 : }
837 :
838 4 : static int run(int argc, char *argv[]) {
839 : int r;
840 :
841 4 : log_setup_service();
842 :
843 4 : umask(0022);
844 :
845 4 : r = parse_argv(argc, argv);
846 4 : if (r <= 0)
847 4 : return r;
848 :
849 0 : if (arg_console && !arg_device)
850 : /*
851 : * Spawn a separate process for each console device.
852 : */
853 0 : return ask_on_consoles(argv);
854 :
855 0 : if (arg_device) {
856 : /*
857 : * Later on, a controlling terminal will be acquired,
858 : * therefore the current process has to become a session
859 : * leader and should not have a controlling terminal already.
860 : */
861 0 : (void) setsid();
862 0 : (void) release_terminal();
863 : }
864 :
865 0 : if (IN_SET(arg_action, ACTION_WATCH, ACTION_WALL))
866 0 : return watch_passwords();
867 : else
868 0 : return show_passwords();
869 : }
870 :
871 4 : DEFINE_MAIN_FUNCTION(run);
|