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)
162 (void) flush_fd(notify
);
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;
262 STRV_FOREACH(p
, passwords
)
263 packet_length
+= strlen(*p
) + 1;
265 packet
= new(char, packet_length
);
272 STRV_FOREACH(p
, passwords
)
273 d
= stpcpy(d
, *p
) + 1;
275 socket_fd
= socket(AF_UNIX
, SOCK_DGRAM
|SOCK_CLOEXEC
, 0);
277 r
= log_debug_errno(errno
, "socket(): %m");
281 strncpy(sa
.un
.sun_path
, socket_name
, sizeof(sa
.un
.sun_path
));
283 n
= sendto(socket_fd
, packet
, packet_length
, MSG_NOSIGNAL
, &sa
.sa
, SOCKADDR_UN_LEN(sa
.un
));
285 r
= log_debug_errno(errno
, "sendto(): %m");
292 explicit_bzero(packet
, packet_length
);
296 static int parse_password(const char *filename
, char **wall
) {
297 _cleanup_free_
char *socket_name
= NULL
, *message
= NULL
;
298 bool accept_cached
= false, echo
= false;
299 uint64_t not_after
= 0;
302 const ConfigTableItem items
[] = {
303 { "Ask", "Socket", config_parse_string
, 0, &socket_name
},
304 { "Ask", "NotAfter", config_parse_uint64
, 0, ¬_after
},
305 { "Ask", "Message", config_parse_string
, 0, &message
},
306 { "Ask", "PID", config_parse_unsigned
, 0, &pid
},
307 { "Ask", "AcceptCached", config_parse_bool
, 0, &accept_cached
},
308 { "Ask", "Echo", config_parse_bool
, 0, &echo
},
316 r
= config_parse(NULL
, filename
, NULL
,
318 config_item_table_lookup
, items
,
319 CONFIG_PARSE_RELAXED
|CONFIG_PARSE_WARN
, NULL
);
324 log_error("Invalid password file %s", filename
);
328 if (not_after
> 0 && now(CLOCK_MONOTONIC
) > not_after
)
331 if (pid
> 0 && !pid_is_alive(pid
))
334 if (arg_action
== ACTION_LIST
)
335 printf("'%s' (PID %u)\n", message
, pid
);
337 else if (arg_action
== ACTION_WALL
) {
341 "%s%sPassword entry required for \'%s\' (PID %u).\r\n"
342 "Please enter password with the systemd-tty-ask-password-agent tool!",
344 *wall
? "\r\n\r\n" : "",
353 _cleanup_strv_free_erase_
char **passwords
= NULL
;
355 assert(IN_SET(arg_action
, ACTION_QUERY
, ACTION_WATCH
));
357 if (access(socket_name
, W_OK
) < 0) {
358 if (arg_action
== ACTION_QUERY
)
359 log_info("Not querying '%s' (PID %u), lacking privileges.", message
, pid
);
365 r
= ask_password_plymouth(message
, not_after
, accept_cached
? ASK_PASSWORD_ACCEPT_CACHED
: 0, filename
, &passwords
);
367 char *password
= NULL
;
371 const char *con
= arg_device
?: "/dev/console";
373 tty_fd
= acquire_terminal(con
, ACQUIRE_TERMINAL_WAIT
, USEC_INFINITY
);
375 return log_error_errno(tty_fd
, "Failed to acquire %s: %m", con
);
377 r
= reset_terminal_fd(tty_fd
, true);
379 log_warning_errno(r
, "Failed to reset terminal, ignoring: %m");
382 r
= ask_password_tty(tty_fd
, message
, NULL
, not_after
,
383 (echo
? ASK_PASSWORD_ECHO
: 0) |
384 (arg_console
? ASK_PASSWORD_CONSOLE_COLOR
: 0),
385 filename
, &password
);
388 tty_fd
= safe_close(tty_fd
);
393 r
= strv_push(&passwords
, password
);
396 string_free_erase(password
);
399 /* If the query went away, that's OK */
400 if (IN_SET(r
, -ETIME
, -ENOENT
))
404 return log_error_errno(r
, "Failed to query password: %m");
406 r
= send_passwords(socket_name
, passwords
);
408 return log_error_errno(r
, "Failed to send: %m");
414 static int wall_tty_block(void) {
415 _cleanup_free_
char *p
= NULL
;
419 r
= get_ctty_devnr(0, &devnr
);
420 if (r
== -ENXIO
) /* We have no controlling tty */
423 return log_error_errno(r
, "Failed to get controlling TTY: %m");
425 if (asprintf(&p
, "/run/systemd/ask-password-block/%u:%u", major(devnr
), minor(devnr
)) < 0)
428 (void) mkdir_parents_label(p
, 0700);
429 (void) mkfifo(p
, 0600);
431 fd
= open(p
, O_RDONLY
|O_CLOEXEC
|O_NONBLOCK
|O_NOCTTY
);
433 return log_debug_errno(errno
, "Failed to open %s: %m", p
);
438 static bool wall_tty_match(const char *path
, void *userdata
) {
439 _cleanup_free_
char *p
= NULL
;
440 _cleanup_close_
int fd
= -1;
443 if (!path_is_absolute(path
))
444 path
= strjoina("/dev/", path
);
446 if (lstat(path
, &st
) < 0) {
447 log_debug_errno(errno
, "Failed to stat %s: %m", path
);
451 if (!S_ISCHR(st
.st_mode
)) {
452 log_debug("%s is not a character device.", path
);
456 /* We use named pipes to ensure that wall messages suggesting
457 * password entry are not printed over password prompts
458 * already shown. We use the fact here that opening a pipe in
459 * non-blocking mode for write-only will succeed only if
460 * there's some writer behind it. Using pipes has the
461 * advantage that the block will automatically go away if the
464 if (asprintf(&p
, "/run/systemd/ask-password-block/%u:%u", major(st
.st_rdev
), minor(st
.st_rdev
)) < 0) {
469 fd
= open(p
, O_WRONLY
|O_CLOEXEC
|O_NONBLOCK
|O_NOCTTY
);
471 log_debug_errno(errno
, "Failed to open the wall pipe: %m");
475 /* What, we managed to open the pipe? Then this tty is filtered. */
479 static int show_passwords(void) {
480 _cleanup_closedir_
DIR *d
;
484 d
= opendir("/run/systemd/ask-password");
489 return log_error_errno(errno
, "Failed to open /run/systemd/ask-password: %m");
492 FOREACH_DIRENT_ALL(de
, d
, return log_error_errno(errno
, "Failed to read directory: %m")) {
493 _cleanup_free_
char *p
= NULL
, *wall
= NULL
;
496 /* We only support /dev on tmpfs, hence we can rely on
497 * d_type to be reliable */
499 if (de
->d_type
!= DT_REG
)
502 if (hidden_or_backup_file(de
->d_name
))
505 if (!startswith(de
->d_name
, "ask."))
508 p
= strappend("/run/systemd/ask-password/", de
->d_name
);
512 q
= parse_password(p
, &wall
);
517 (void) utmp_wall(wall
, NULL
, NULL
, wall_tty_match
, NULL
);
523 static int watch_passwords(void) {
530 _cleanup_close_
int notify
= -1, signal_fd
= -1, tty_block_fd
= -1;
531 struct pollfd pollfd
[_FD_MAX
] = {};
535 tty_block_fd
= wall_tty_block();
537 (void) mkdir_p_label("/run/systemd/ask-password", 0755);
539 notify
= inotify_init1(IN_CLOEXEC
);
541 return log_error_errno(errno
, "Failed to allocate directory watch: %m");
543 if (inotify_add_watch(notify
, "/run/systemd/ask-password", IN_CLOSE_WRITE
|IN_MOVED_TO
) < 0)
544 return log_error_errno(errno
, "Failed to add /run/systemd/ask-password to directory watch: %m");
546 assert_se(sigemptyset(&mask
) >= 0);
547 assert_se(sigset_add_many(&mask
, SIGINT
, SIGTERM
, -1) >= 0);
548 assert_se(sigprocmask(SIG_SETMASK
, &mask
, NULL
) >= 0);
550 signal_fd
= signalfd(-1, &mask
, SFD_NONBLOCK
|SFD_CLOEXEC
);
552 return log_error_errno(errno
, "Failed to allocate signal file descriptor: %m");
554 pollfd
[FD_INOTIFY
].fd
= notify
;
555 pollfd
[FD_INOTIFY
].events
= POLLIN
;
556 pollfd
[FD_SIGNAL
].fd
= signal_fd
;
557 pollfd
[FD_SIGNAL
].events
= POLLIN
;
560 r
= show_passwords();
562 log_error_errno(r
, "Failed to show password: %m");
564 if (poll(pollfd
, _FD_MAX
, -1) < 0) {
571 if (pollfd
[FD_INOTIFY
].revents
!= 0)
572 (void) flush_fd(notify
);
574 if (pollfd
[FD_SIGNAL
].revents
!= 0)
581 static void help(void) {
582 printf("%s [OPTIONS...]\n\n"
583 "Process system password requests.\n\n"
584 " -h --help Show this help\n"
585 " --version Show package version\n"
586 " --list Show pending password requests\n"
587 " --query Process pending password requests\n"
588 " --watch Continuously process password requests\n"
589 " --wall Continuously forward password requests to wall\n"
590 " --plymouth Ask question with Plymouth instead of on TTY\n"
591 " --console Ask question on /dev/console instead of current TTY\n",
592 program_invocation_short_name
);
595 static int parse_argv(int argc
, char *argv
[]) {
607 static const struct option options
[] = {
608 { "help", no_argument
, NULL
, 'h' },
609 { "version", no_argument
, NULL
, ARG_VERSION
},
610 { "list", no_argument
, NULL
, ARG_LIST
},
611 { "query", no_argument
, NULL
, ARG_QUERY
},
612 { "watch", no_argument
, NULL
, ARG_WATCH
},
613 { "wall", no_argument
, NULL
, ARG_WALL
},
614 { "plymouth", no_argument
, NULL
, ARG_PLYMOUTH
},
615 { "console", optional_argument
, NULL
, ARG_CONSOLE
},
624 while ((c
= getopt_long(argc
, argv
, "h", options
, NULL
)) >= 0)
636 arg_action
= ACTION_LIST
;
640 arg_action
= ACTION_QUERY
;
644 arg_action
= ACTION_WATCH
;
648 arg_action
= ACTION_WALL
;
659 if (isempty(optarg
)) {
660 log_error("Empty console device path is not allowed.");
672 assert_not_reached("Unhandled option");
675 if (optind
!= argc
) {
676 log_error("%s takes no arguments.", program_invocation_short_name
);
680 if (arg_plymouth
|| arg_console
) {
682 if (!IN_SET(arg_action
, ACTION_QUERY
, ACTION_WATCH
)) {
683 log_error("Options --query and --watch conflict.");
687 if (arg_plymouth
&& arg_console
) {
688 log_error("Options --plymouth and --console conflict.");
697 * To be able to ask on all terminal devices of /dev/console
698 * the devices are collected. If more than one device is found,
699 * then on each of the terminals a inquiring task is forked.
700 * Every task has its own session and its own controlling terminal.
701 * If one of the tasks does handle a password, the remaining tasks
702 * will be terminated.
704 static int ask_on_this_console(const char *tty
, pid_t
*ret_pid
, int argc
, char *argv
[]) {
705 struct sigaction sig
= {
706 .sa_handler
= nop_signal_handler
,
707 .sa_flags
= SA_NOCLDSTOP
| SA_RESTART
,
712 assert_se(sigprocmask_many(SIG_UNBLOCK
, NULL
, SIGHUP
, SIGCHLD
, -1) >= 0);
714 assert_se(sigemptyset(&sig
.sa_mask
) >= 0);
715 assert_se(sigaction(SIGCHLD
, &sig
, NULL
) >= 0);
717 sig
.sa_handler
= SIG_DFL
;
718 assert_se(sigaction(SIGHUP
, &sig
, NULL
) >= 0);
720 r
= safe_fork("(sd-passwd)", FORK_RESET_SIGNALS
|FORK_LOG
, &pid
);
726 assert_se(prctl(PR_SET_PDEATHSIG
, SIGHUP
) >= 0);
728 for (ac
= 0; ac
< argc
; ac
++) {
729 if (streq(argv
[ac
], "--console")) {
730 argv
[ac
] = strjoina("--console=", tty
);
737 execv(SYSTEMD_TTY_ASK_PASSWORD_AGENT_BINARY_PATH
, argv
);
745 static void terminate_agents(Set
*pids
) {
747 siginfo_t status
= {};
754 * Request termination of the remaining processes as those
755 * are not required anymore.
757 SET_FOREACH(p
, pids
, i
)
758 (void) kill(PTR_TO_PID(p
), SIGTERM
);
761 * Collect the processes which have go away.
763 assert_se(sigemptyset(&set
) >= 0);
764 assert_se(sigaddset(&set
, SIGCHLD
) >= 0);
765 timespec_store(&ts
, 50 * USEC_PER_MSEC
);
767 while (!set_isempty(pids
)) {
770 r
= waitid(P_ALL
, 0, &status
, WEXITED
|WNOHANG
);
771 if (r
< 0 && errno
== EINTR
)
774 if (r
== 0 && status
.si_pid
> 0) {
775 set_remove(pids
, PID_TO_PTR(status
.si_pid
));
779 signum
= sigtimedwait(&set
, NULL
, &ts
);
782 log_error_errno(errno
, "sigtimedwait() failed: %m");
785 assert(signum
== SIGCHLD
);
789 * Kill hanging processes.
791 SET_FOREACH(p
, pids
, i
) {
792 log_warning("Failed to terminate child %d, killing it", PTR_TO_PID(p
));
793 (void) kill(PTR_TO_PID(p
), SIGKILL
);
797 static int ask_on_consoles(int argc
, char *argv
[]) {
798 _cleanup_set_free_ Set
*pids
= NULL
;
799 _cleanup_strv_free_
char **consoles
= NULL
;
800 siginfo_t status
= {};
805 r
= get_kernel_consoles(&consoles
);
807 return log_error_errno(r
, "Failed to determine devices of /dev/console: %m");
809 pids
= set_new(NULL
);
813 /* Start an agent on each console. */
814 STRV_FOREACH(tty
, consoles
) {
815 r
= ask_on_this_console(*tty
, &pid
, argc
, argv
);
819 if (set_put(pids
, PID_TO_PTR(pid
)) < 0)
823 /* Wait for an agent to exit. */
827 if (waitid(P_ALL
, 0, &status
, WEXITED
) < 0) {
831 return log_error_errno(errno
, "waitid() failed: %m");
834 set_remove(pids
, PID_TO_PTR(status
.si_pid
));
838 if (!is_clean_exit(status
.si_code
, status
.si_status
, EXIT_CLEAN_DAEMON
, NULL
))
839 log_error("Password agent failed with: %d", status
.si_status
);
841 terminate_agents(pids
);
845 int main(int argc
, char *argv
[]) {
848 log_set_target(LOG_TARGET_AUTO
);
849 log_parse_environment();
854 r
= parse_argv(argc
, argv
);
858 if (arg_console
&& !arg_device
)
860 * Spawn for each console device a separate process.
862 r
= ask_on_consoles(argc
, argv
);
867 * Later on, a controlling terminal will be acquired,
868 * therefore the current process has to become a session
869 * leader and should not have a controlling terminal already.
872 (void) release_terminal();
875 if (IN_SET(arg_action
, ACTION_WATCH
, ACTION_WALL
))
876 r
= watch_passwords();
878 r
= show_passwords();
882 return r
< 0 ? EXIT_FAILURE
: EXIT_SUCCESS
;