1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 This file is part of systemd.
5 Copyright 2010 Lennart Poettering
6 Copyright 2015 Werner Fink
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.
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.
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/>.
30 #include <sys/inotify.h>
31 #include <sys/prctl.h>
32 #include <sys/signalfd.h>
33 #include <sys/socket.h>
38 #include "alloc-util.h"
39 #include "ask-password-api.h"
40 #include "conf-parser.h"
42 #include "dirent-util.h"
43 #include "exit-status.h"
50 #include "path-util.h"
51 #include "process-util.h"
52 #include "signal-util.h"
53 #include "socket-util.h"
54 #include "string-util.h"
56 #include "terminal-util.h"
58 #include "utmp-wtmp.h"
65 } arg_action
= ACTION_QUERY
;
67 static bool arg_plymouth
= false;
68 static bool arg_console
= false;
69 static const char *arg_device
= NULL
;
71 static int ask_password_plymouth(
74 AskPasswordFlags flags
,
75 const char *flag_file
,
78 static const union sockaddr_union sa
= PLYMOUTH_SOCKET
;
79 _cleanup_close_
int fd
= -1, notify
= -1;
80 _cleanup_free_
char *packet
= NULL
;
83 struct pollfd pollfd
[2] = {};
84 char buffer
[LINE_MAX
];
94 notify
= inotify_init1(IN_CLOEXEC
|IN_NONBLOCK
);
98 r
= inotify_add_watch(notify
, flag_file
, IN_ATTRIB
); /* for the link count */
103 fd
= socket(AF_UNIX
, SOCK_STREAM
|SOCK_CLOEXEC
|SOCK_NONBLOCK
, 0);
107 r
= connect(fd
, &sa
.sa
, SOCKADDR_UN_LEN(sa
.un
));
111 if (flags
& ASK_PASSWORD_ACCEPT_CACHED
) {
112 packet
= strdup("c");
114 } else if (asprintf(&packet
, "*\002%c%s%n", (int) (strlen(message
) + 1), message
, &n
) < 0)
119 r
= loop_write(fd
, packet
, n
+ 1, true);
123 pollfd
[POLL_SOCKET
].fd
= fd
;
124 pollfd
[POLL_SOCKET
].events
= POLLIN
;
125 pollfd
[POLL_INOTIFY
].fd
= notify
;
126 pollfd
[POLL_INOTIFY
].events
= POLLIN
;
129 int sleep_for
= -1, j
;
134 y
= now(CLOCK_MONOTONIC
);
141 sleep_for
= (int) ((until
- y
) / USEC_PER_MSEC
);
144 if (flag_file
&& access(flag_file
, F_OK
) < 0) {
149 j
= poll(pollfd
, notify
>= 0 ? 2 : 1, sleep_for
);
161 if (notify
>= 0 && pollfd
[POLL_INOTIFY
].revents
!= 0)
164 if (pollfd
[POLL_SOCKET
].revents
== 0)
167 k
= read(fd
, buffer
+ p
, sizeof(buffer
) - p
);
169 if (IN_SET(errno
, EINTR
, EAGAIN
))
184 if (buffer
[0] == 5) {
186 if (flags
& ASK_PASSWORD_ACCEPT_CACHED
) {
187 /* Hmm, first try with cached
188 * passwords failed, so let's retry
189 * with a normal password request */
190 packet
= mfree(packet
);
192 if (asprintf(&packet
, "*\002%c%s%n", (int) (strlen(message
) + 1), message
, &n
) < 0) {
197 r
= loop_write(fd
, packet
, n
+1, true);
201 flags
&= ~ASK_PASSWORD_ACCEPT_CACHED
;
206 /* No password, because UI not shown */
210 } else if (IN_SET(buffer
[0], 2, 9)) {
214 /* One or more answers */
218 memcpy(&size
, buffer
+1, sizeof(size
));
219 size
= le32toh(size
);
220 if (size
+ 5 > sizeof(buffer
)) {
228 l
= strv_parse_nulstr(buffer
+ 5, size
);
247 explicit_bzero(buffer
, sizeof(buffer
));
251 static int send_passwords(const char *socket_name
, char **passwords
) {
252 _cleanup_free_
char *packet
= NULL
;
253 _cleanup_close_
int socket_fd
= -1;
254 union sockaddr_union sa
= { .un
.sun_family
= AF_UNIX
};
255 size_t packet_length
= 1;
261 STRV_FOREACH(p
, passwords
)
262 packet_length
+= strlen(*p
) + 1;
264 packet
= new(char, packet_length
);
271 STRV_FOREACH(p
, passwords
)
272 d
= stpcpy(d
, *p
) + 1;
274 socket_fd
= socket(AF_UNIX
, SOCK_DGRAM
|SOCK_CLOEXEC
, 0);
276 r
= log_debug_errno(errno
, "socket(): %m");
280 strncpy(sa
.un
.sun_path
, socket_name
, sizeof(sa
.un
.sun_path
));
282 r
= sendto(socket_fd
, packet
, packet_length
, MSG_NOSIGNAL
, &sa
.sa
, SOCKADDR_UN_LEN(sa
.un
));
284 r
= log_debug_errno(errno
, "sendto(): %m");
287 explicit_bzero(packet
, packet_length
);
291 static int parse_password(const char *filename
, char **wall
) {
292 _cleanup_free_
char *socket_name
= NULL
, *message
= NULL
;
293 bool accept_cached
= false, echo
= false;
294 uint64_t not_after
= 0;
297 const ConfigTableItem items
[] = {
298 { "Ask", "Socket", config_parse_string
, 0, &socket_name
},
299 { "Ask", "NotAfter", config_parse_uint64
, 0, ¬_after
},
300 { "Ask", "Message", config_parse_string
, 0, &message
},
301 { "Ask", "PID", config_parse_unsigned
, 0, &pid
},
302 { "Ask", "AcceptCached", config_parse_bool
, 0, &accept_cached
},
303 { "Ask", "Echo", config_parse_bool
, 0, &echo
},
311 r
= config_parse(NULL
, filename
, NULL
,
313 config_item_table_lookup
, items
,
314 CONFIG_PARSE_RELAXED
|CONFIG_PARSE_WARN
, NULL
);
319 log_error("Invalid password file %s", filename
);
323 if (not_after
> 0 && now(CLOCK_MONOTONIC
) > not_after
)
326 if (pid
> 0 && !pid_is_alive(pid
))
329 if (arg_action
== ACTION_LIST
)
330 printf("'%s' (PID %u)\n", message
, pid
);
332 else if (arg_action
== ACTION_WALL
) {
336 "%s%sPassword entry required for \'%s\' (PID %u).\r\n"
337 "Please enter password with the systemd-tty-ask-password-agent tool!",
339 *wall
? "\r\n\r\n" : "",
348 _cleanup_strv_free_erase_
char **passwords
= NULL
;
350 assert(IN_SET(arg_action
, ACTION_QUERY
, ACTION_WATCH
));
352 if (access(socket_name
, W_OK
) < 0) {
353 if (arg_action
== ACTION_QUERY
)
354 log_info("Not querying '%s' (PID %u), lacking privileges.", message
, pid
);
360 r
= ask_password_plymouth(message
, not_after
, accept_cached
? ASK_PASSWORD_ACCEPT_CACHED
: 0, filename
, &passwords
);
362 char *password
= NULL
;
366 const char *con
= arg_device
? arg_device
: "/dev/console";
368 tty_fd
= acquire_terminal(con
, false, false, false, USEC_INFINITY
);
370 return log_error_errno(tty_fd
, "Failed to acquire /dev/console: %m");
372 r
= reset_terminal_fd(tty_fd
, true);
374 log_warning_errno(r
, "Failed to reset terminal, ignoring: %m");
377 r
= ask_password_tty(message
, NULL
, not_after
, echo
? ASK_PASSWORD_ECHO
: 0, filename
, &password
);
380 tty_fd
= safe_close(tty_fd
);
385 r
= strv_push(&passwords
, password
);
388 string_free_erase(password
);
391 /* If the query went away, that's OK */
392 if (IN_SET(r
, -ETIME
, -ENOENT
))
396 return log_error_errno(r
, "Failed to query password: %m");
398 r
= send_passwords(socket_name
, passwords
);
400 return log_error_errno(r
, "Failed to send: %m");
406 static int wall_tty_block(void) {
407 _cleanup_free_
char *p
= NULL
;
411 r
= get_ctty_devnr(0, &devnr
);
412 if (r
== -ENXIO
) /* We have no controlling tty */
415 return log_error_errno(r
, "Failed to get controlling TTY: %m");
417 if (asprintf(&p
, "/run/systemd/ask-password-block/%u:%u", major(devnr
), minor(devnr
)) < 0)
420 mkdir_parents_label(p
, 0700);
423 fd
= open(p
, O_RDONLY
|O_CLOEXEC
|O_NONBLOCK
|O_NOCTTY
);
425 return log_debug_errno(errno
, "Failed to open %s: %m", p
);
430 static bool wall_tty_match(const char *path
, void *userdata
) {
431 _cleanup_free_
char *p
= NULL
;
432 _cleanup_close_
int fd
= -1;
435 if (!path_is_absolute(path
))
436 path
= strjoina("/dev/", path
);
438 if (lstat(path
, &st
) < 0) {
439 log_debug_errno(errno
, "Failed to stat %s: %m", path
);
443 if (!S_ISCHR(st
.st_mode
)) {
444 log_debug("%s is not a character device.", path
);
448 /* We use named pipes to ensure that wall messages suggesting
449 * password entry are not printed over password prompts
450 * already shown. We use the fact here that opening a pipe in
451 * non-blocking mode for write-only will succeed only if
452 * there's some writer behind it. Using pipes has the
453 * advantage that the block will automatically go away if the
456 if (asprintf(&p
, "/run/systemd/ask-password-block/%u:%u", major(st
.st_rdev
), minor(st
.st_rdev
)) < 0) {
461 fd
= open(p
, O_WRONLY
|O_CLOEXEC
|O_NONBLOCK
|O_NOCTTY
);
463 log_debug_errno(errno
, "Failed top open the wall pipe: %m");
467 /* What, we managed to open the pipe? Then this tty is filtered. */
471 static int show_passwords(void) {
472 _cleanup_closedir_
DIR *d
;
476 d
= opendir("/run/systemd/ask-password");
481 return log_error_errno(errno
, "Failed to open /run/systemd/ask-password: %m");
484 FOREACH_DIRENT_ALL(de
, d
, return log_error_errno(errno
, "Failed to read directory: %m")) {
485 _cleanup_free_
char *p
= NULL
, *wall
= NULL
;
488 /* We only support /dev on tmpfs, hence we can rely on
489 * d_type to be reliable */
491 if (de
->d_type
!= DT_REG
)
494 if (hidden_or_backup_file(de
->d_name
))
497 if (!startswith(de
->d_name
, "ask."))
500 p
= strappend("/run/systemd/ask-password/", de
->d_name
);
504 q
= parse_password(p
, &wall
);
509 (void) utmp_wall(wall
, NULL
, NULL
, wall_tty_match
, NULL
);
515 static int watch_passwords(void) {
522 _cleanup_close_
int notify
= -1, signal_fd
= -1, tty_block_fd
= -1;
523 struct pollfd pollfd
[_FD_MAX
] = {};
527 tty_block_fd
= wall_tty_block();
529 (void) mkdir_p_label("/run/systemd/ask-password", 0755);
531 notify
= inotify_init1(IN_CLOEXEC
);
533 return log_error_errno(errno
, "Failed to allocate directory watch: %m");
535 if (inotify_add_watch(notify
, "/run/systemd/ask-password", IN_CLOSE_WRITE
|IN_MOVED_TO
) < 0)
536 return log_error_errno(errno
, "Failed to add /run/systemd/ask-password to directory watch: %m");
538 assert_se(sigemptyset(&mask
) >= 0);
539 assert_se(sigset_add_many(&mask
, SIGINT
, SIGTERM
, -1) >= 0);
540 assert_se(sigprocmask(SIG_SETMASK
, &mask
, NULL
) >= 0);
542 signal_fd
= signalfd(-1, &mask
, SFD_NONBLOCK
|SFD_CLOEXEC
);
544 return log_error_errno(errno
, "Failed to allocate signal file descriptor: %m");
546 pollfd
[FD_INOTIFY
].fd
= notify
;
547 pollfd
[FD_INOTIFY
].events
= POLLIN
;
548 pollfd
[FD_SIGNAL
].fd
= signal_fd
;
549 pollfd
[FD_SIGNAL
].events
= POLLIN
;
552 r
= show_passwords();
554 log_error_errno(r
, "Failed to show password: %m");
556 if (poll(pollfd
, _FD_MAX
, -1) < 0) {
563 if (pollfd
[FD_INOTIFY
].revents
!= 0)
564 (void) flush_fd(notify
);
566 if (pollfd
[FD_SIGNAL
].revents
!= 0)
573 static void help(void) {
574 printf("%s [OPTIONS...]\n\n"
575 "Process system password requests.\n\n"
576 " -h --help Show this help\n"
577 " --version Show package version\n"
578 " --list Show pending password requests\n"
579 " --query Process pending password requests\n"
580 " --watch Continuously process password requests\n"
581 " --wall Continuously forward password requests to wall\n"
582 " --plymouth Ask question with Plymouth instead of on TTY\n"
583 " --console Ask question on /dev/console instead of current TTY\n",
584 program_invocation_short_name
);
587 static int parse_argv(int argc
, char *argv
[]) {
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
},
616 while ((c
= getopt_long(argc
, argv
, "h", options
, NULL
)) >= 0)
628 arg_action
= ACTION_LIST
;
632 arg_action
= ACTION_QUERY
;
636 arg_action
= ACTION_WATCH
;
640 arg_action
= ACTION_WALL
;
651 if (isempty(optarg
)) {
652 log_error("Empty console device path is not allowed.");
664 assert_not_reached("Unhandled option");
667 if (optind
!= argc
) {
668 log_error("%s takes no arguments.", program_invocation_short_name
);
672 if (arg_plymouth
|| arg_console
) {
674 if (!IN_SET(arg_action
, ACTION_QUERY
, ACTION_WATCH
)) {
675 log_error("Options --query and --watch conflict.");
679 if (arg_plymouth
&& arg_console
) {
680 log_error("Options --plymouth and --console conflict.");
689 * To be able to ask on all terminal devices of /dev/console
690 * the devices are collected. If more than one device is found,
691 * then on each of the terminals a inquiring task is forked.
692 * Every task has its own session and its own controlling terminal.
693 * If one of the tasks does handle a password, the remaining tasks
694 * will be terminated.
696 static int ask_on_this_console(const char *tty
, pid_t
*pid
, int argc
, char *argv
[]) {
697 struct sigaction sig
= {
698 .sa_handler
= nop_signal_handler
,
699 .sa_flags
= SA_NOCLDSTOP
| SA_RESTART
,
702 assert_se(sigprocmask_many(SIG_UNBLOCK
, NULL
, SIGHUP
, SIGCHLD
, -1) >= 0);
704 assert_se(sigemptyset(&sig
.sa_mask
) >= 0);
705 assert_se(sigaction(SIGCHLD
, &sig
, NULL
) >= 0);
707 sig
.sa_handler
= SIG_DFL
;
708 assert_se(sigaction(SIGHUP
, &sig
, NULL
) >= 0);
712 return log_error_errno(errno
, "Failed to fork process: %m");
717 assert_se(prctl(PR_SET_PDEATHSIG
, SIGHUP
) >= 0);
720 reset_all_signal_handlers();
722 for (ac
= 0; ac
< argc
; ac
++) {
723 if (streq(argv
[ac
], "--console")) {
724 argv
[ac
] = strjoina("--console=", tty
, NULL
);
731 execv(SYSTEMD_TTY_ASK_PASSWORD_AGENT_BINARY_PATH
, argv
);
737 static void terminate_agents(Set
*pids
) {
739 siginfo_t status
= {};
746 * Request termination of the remaining processes as those
747 * are not required anymore.
749 SET_FOREACH(p
, pids
, i
)
750 (void) kill(PTR_TO_PID(p
), SIGTERM
);
753 * Collect the processes which have go away.
755 assert_se(sigemptyset(&set
) >= 0);
756 assert_se(sigaddset(&set
, SIGCHLD
) >= 0);
757 timespec_store(&ts
, 50 * USEC_PER_MSEC
);
759 while (!set_isempty(pids
)) {
762 r
= waitid(P_ALL
, 0, &status
, WEXITED
|WNOHANG
);
763 if (r
< 0 && errno
== EINTR
)
766 if (r
== 0 && status
.si_pid
> 0) {
767 set_remove(pids
, PID_TO_PTR(status
.si_pid
));
771 signum
= sigtimedwait(&set
, NULL
, &ts
);
774 log_error_errno(errno
, "sigtimedwait() failed: %m");
777 assert(signum
== SIGCHLD
);
781 * Kill hanging processes.
783 SET_FOREACH(p
, pids
, i
) {
784 log_warning("Failed to terminate child %d, killing it", PTR_TO_PID(p
));
785 (void) kill(PTR_TO_PID(p
), SIGKILL
);
789 static int ask_on_consoles(int argc
, char *argv
[]) {
790 _cleanup_set_free_ Set
*pids
= NULL
;
791 _cleanup_strv_free_
char **consoles
= NULL
;
792 siginfo_t status
= {};
797 r
= get_kernel_consoles(&consoles
);
799 return log_error_errno(r
, "Failed to determine devices of /dev/console: %m");
801 pids
= set_new(NULL
);
805 /* Start an agent on each console. */
806 STRV_FOREACH(tty
, consoles
) {
807 r
= ask_on_this_console(*tty
, &pid
, argc
, argv
);
811 if (set_put(pids
, PID_TO_PTR(pid
)) < 0)
815 /* Wait for an agent to exit. */
819 if (waitid(P_ALL
, 0, &status
, WEXITED
) < 0) {
823 return log_error_errno(errno
, "waitid() failed: %m");
826 set_remove(pids
, PID_TO_PTR(status
.si_pid
));
830 if (!is_clean_exit(status
.si_code
, status
.si_status
, EXIT_CLEAN_DAEMON
, NULL
))
831 log_error("Password agent failed with: %d", status
.si_status
);
833 terminate_agents(pids
);
837 int main(int argc
, char *argv
[]) {
840 log_set_target(LOG_TARGET_AUTO
);
841 log_parse_environment();
846 r
= parse_argv(argc
, argv
);
850 if (arg_console
&& !arg_device
)
852 * Spawn for each console device a separate process.
854 r
= ask_on_consoles(argc
, argv
);
859 * Later on, a controlling terminal will be acquired,
860 * therefore the current process has to become a session
861 * leader and should not have a controlling terminal already.
864 (void) release_terminal();
867 if (IN_SET(arg_action
, ACTION_WATCH
, ACTION_WALL
))
868 r
= watch_passwords();
870 r
= show_passwords();
874 return r
< 0 ? EXIT_FAILURE
: EXIT_SUCCESS
;