1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2010 Lennart Poettering
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/>.
25 #include <sys/socket.h>
29 #include <sys/inotify.h>
32 #include <sys/signalfd.h>
37 #include "path-util.h"
38 #include "conf-parser.h"
39 #include "utmp-wtmp.h"
40 #include "socket-util.h"
41 #include "ask-password-api.h"
50 } arg_action = ACTION_QUERY;
52 static bool arg_plymouth = false;
53 static bool arg_console = false;
55 static int ask_password_plymouth(
58 const char *flag_file,
60 char ***_passphrases) {
62 int fd = -1, notify = -1;
63 union sockaddr_union sa = {};
67 struct pollfd pollfd[2] = {};
68 char buffer[LINE_MAX];
78 if ((notify = inotify_init1(IN_CLOEXEC|IN_NONBLOCK)) < 0) {
83 if (inotify_add_watch(notify, flag_file, IN_ATTRIB /* for the link count */) < 0) {
89 if ((fd = socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0)) < 0) {
94 sa.sa.sa_family = AF_UNIX;
95 strncpy(sa.un.sun_path+1, "/org/freedesktop/plymouthd", sizeof(sa.un.sun_path)-1);
96 if (connect(fd, &sa.sa, offsetof(struct sockaddr_un, sun_path) + 1 + strlen(sa.un.sun_path+1)) < 0) {
97 log_error("Failed to connect to Plymouth: %m");
103 packet = strdup("c");
106 asprintf(&packet, "*\002%c%s%n", (int) (strlen(message) + 1), message, &n);
113 if ((k = loop_write(fd, packet, n+1, true)) != n+1) {
114 r = k < 0 ? (int) k : -EIO;
118 pollfd[POLL_SOCKET].fd = fd;
119 pollfd[POLL_SOCKET].events = POLLIN;
120 pollfd[POLL_INOTIFY].fd = notify;
121 pollfd[POLL_INOTIFY].events = POLLIN;
124 int sleep_for = -1, j;
129 y = now(CLOCK_MONOTONIC);
136 sleep_for = (int) ((until - y) / USEC_PER_MSEC);
140 if (access(flag_file, F_OK) < 0) {
145 if ((j = poll(pollfd, notify > 0 ? 2 : 1, sleep_for)) < 0) {
157 if (notify > 0 && pollfd[POLL_INOTIFY].revents != 0)
160 if (pollfd[POLL_SOCKET].revents == 0)
163 if ((k = read(fd, buffer + p, sizeof(buffer) - p)) <= 0) {
164 r = k < 0 ? -errno : -EIO;
173 if (buffer[0] == 5) {
176 /* Hmm, first try with cached
177 * passwords failed, so let's retry
178 * with a normal password request */
182 if (asprintf(&packet, "*\002%c%s%n", (int) (strlen(message) + 1), message, &n) < 0) {
187 if ((k = loop_write(fd, packet, n+1, true)) != n+1) {
188 r = k < 0 ? (int) k : -EIO;
192 accept_cached = false;
197 /* No password, because UI not shown */
201 } else if (buffer[0] == 2 || buffer[0] == 9) {
205 /* One ore more answers */
209 memcpy(&size, buffer+1, sizeof(size));
210 size = le32toh(size);
211 if (size+5 > sizeof(buffer)) {
219 if (!(l = strv_parse_nulstr(buffer + 5, size))) {
245 static int parse_password(const char *filename, char **wall) {
246 _cleanup_free_ char *socket_name = NULL, *message = NULL, *packet = NULL;
247 uint64_t not_after = 0;
249 bool accept_cached = false;
251 const ConfigTableItem items[] = {
252 { "Ask", "Socket", config_parse_string, 0, &socket_name },
253 { "Ask", "NotAfter", config_parse_uint64, 0, ¬_after },
254 { "Ask", "Message", config_parse_string, 0, &message },
255 { "Ask", "PID", config_parse_unsigned, 0, &pid },
256 { "Ask", "AcceptCached", config_parse_bool, 0, &accept_cached },
257 { NULL, NULL, NULL, 0, NULL }
264 r = config_parse(NULL, filename, NULL,
266 config_item_table_lookup, items,
267 true, false, true, NULL);
272 log_error("Invalid password file %s", filename);
276 if (not_after > 0 && now(CLOCK_MONOTONIC) > not_after)
279 if (pid > 0 && !pid_is_alive(pid))
282 if (arg_action == ACTION_LIST)
283 printf("'%s' (PID %u)\n", message, pid);
285 else if (arg_action == ACTION_WALL) {
289 "%s%sPassword entry required for \'%s\' (PID %u).\r\n"
290 "Please enter password with the systemd-tty-ask-password-agent tool!",
292 *wall ? "\r\n\r\n" : "",
301 union sockaddr_union sa = {};
302 size_t packet_length = 0;
303 _cleanup_close_ int socket_fd = -1;
305 assert(arg_action == ACTION_QUERY ||
306 arg_action == ACTION_WATCH);
308 if (access(socket_name, W_OK) < 0) {
309 if (arg_action == ACTION_QUERY)
310 log_info("Not querying '%s' (PID %u), lacking privileges.", message, pid);
316 _cleanup_strv_free_ char **passwords = NULL;
318 r = ask_password_plymouth(message, not_after, filename, accept_cached, &passwords);
323 STRV_FOREACH(p, passwords)
324 packet_length += strlen(*p) + 1;
326 packet = new(char, packet_length);
335 STRV_FOREACH(p, passwords)
336 d = stpcpy(d, *p) + 1;
342 _cleanup_free_ char *password = NULL;
345 tty_fd = acquire_terminal("/dev/console", false, false, false, (usec_t) -1);
350 r = ask_password_tty(message, not_after, filename, &password);
358 packet_length = 1 + strlen(password) + 1;
359 packet = new(char, packet_length);
364 strcpy(packet + 1, password);
369 if (r == -ETIME || r == -ENOENT)
370 /* If the query went away, that's OK */
374 log_error("Failed to query password: %s", strerror(-r));
378 socket_fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0);
380 log_error("socket(): %m");
384 sa.un.sun_family = AF_UNIX;
385 strncpy(sa.un.sun_path, socket_name, sizeof(sa.un.sun_path));
387 r = sendto(socket_fd, packet, packet_length, MSG_NOSIGNAL, &sa.sa,
388 offsetof(struct sockaddr_un, sun_path) + strlen(socket_name));
390 log_error("Failed to send: %m");
398 static int wall_tty_block(void) {
403 r = get_ctty_devnr(0, &devnr);
407 if (asprintf(&p, "/run/systemd/ask-password-block/%u:%u", major(devnr), minor(devnr)) < 0)
410 mkdir_parents_label(p, 0700);
413 fd = open(p, O_RDONLY|O_CLOEXEC|O_NONBLOCK|O_NOCTTY);
422 static bool wall_tty_match(const char *path) {
427 if (path_is_absolute(path))
428 k = lstat(path, &st);
430 if (asprintf(&p, "/dev/%s", path) < 0)
440 if (!S_ISCHR(st.st_mode))
443 /* We use named pipes to ensure that wall messages suggesting
444 * password entry are not printed over password prompts
445 * already shown. We use the fact here that opening a pipe in
446 * non-blocking mode for write-only will succeed only if
447 * there's some writer behind it. Using pipes has the
448 * advantage that the block will automatically go away if the
451 if (asprintf(&p, "/run/systemd/ask-password-block/%u:%u", major(st.st_rdev), minor(st.st_rdev)) < 0)
454 fd = open(p, O_WRONLY|O_CLOEXEC|O_NONBLOCK|O_NOCTTY);
460 /* What, we managed to open the pipe? Then this tty is filtered. */
465 static int show_passwords(void) {
470 if (!(d = opendir("/run/systemd/ask-password"))) {
474 log_error("opendir(/run/systemd/ask-password): %m");
478 while ((de = readdir(d))) {
483 /* We only support /dev on tmpfs, hence we can rely on
484 * d_type to be reliable */
486 if (de->d_type != DT_REG)
489 if (ignore_file(de->d_name))
492 if (!startswith(de->d_name, "ask."))
495 if (!(p = strappend("/run/systemd/ask-password/", de->d_name))) {
501 if ((q = parse_password(p, &wall)) < 0)
507 utmp_wall(wall, NULL, wall_tty_match);
519 static int watch_passwords(void) {
526 int notify = -1, signal_fd = -1, tty_block_fd = -1;
527 struct pollfd pollfd[_FD_MAX] = {};
531 tty_block_fd = wall_tty_block();
533 mkdir_p_label("/run/systemd/ask-password", 0755);
535 if ((notify = inotify_init1(IN_CLOEXEC)) < 0) {
540 if (inotify_add_watch(notify, "/run/systemd/ask-password", IN_CLOSE_WRITE|IN_MOVED_TO) < 0) {
545 assert_se(sigemptyset(&mask) == 0);
546 sigset_add_many(&mask, SIGINT, SIGTERM, -1);
547 assert_se(sigprocmask(SIG_SETMASK, &mask, NULL) == 0);
549 if ((signal_fd = signalfd(-1, &mask, SFD_NONBLOCK|SFD_CLOEXEC)) < 0) {
550 log_error("signalfd(): %m");
555 pollfd[FD_INOTIFY].fd = notify;
556 pollfd[FD_INOTIFY].events = POLLIN;
557 pollfd[FD_SIGNAL].fd = signal_fd;
558 pollfd[FD_SIGNAL].events = POLLIN;
561 if ((r = show_passwords()) < 0)
562 log_error("Failed to show password: %s", strerror(-r));
564 if (poll(pollfd, _FD_MAX, -1) < 0) {
573 if (pollfd[FD_INOTIFY].revents != 0)
576 if (pollfd[FD_SIGNAL].revents != 0)
584 safe_close(signal_fd);
585 safe_close(tty_block_fd);
590 static int help(void) {
592 printf("%s [OPTIONS...]\n\n"
593 "Process system password requests.\n\n"
594 " -h --help Show this help\n"
595 " --version Show package version\n"
596 " --list Show pending password requests\n"
597 " --query Process pending password requests\n"
598 " --watch Continuously process password requests\n"
599 " --wall Continuously forward password requests to wall\n"
600 " --plymouth Ask question with Plymouth instead of on TTY\n"
601 " --console Ask question on /dev/console instead of current TTY\n",
602 program_invocation_short_name);
607 static int parse_argv(int argc, char *argv[]) {
619 static const struct option options[] = {
620 { "help", no_argument, NULL, 'h' },
621 { "version", no_argument, NULL, ARG_VERSION },
622 { "list", no_argument, NULL, ARG_LIST },
623 { "query", no_argument, NULL, ARG_QUERY },
624 { "watch", no_argument, NULL, ARG_WATCH },
625 { "wall", no_argument, NULL, ARG_WALL },
626 { "plymouth", no_argument, NULL, ARG_PLYMOUTH },
627 { "console", no_argument, NULL, ARG_CONSOLE },
636 while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) {
644 puts(PACKAGE_STRING);
645 puts(SYSTEMD_FEATURES);
649 arg_action = ACTION_LIST;
653 arg_action = ACTION_QUERY;
657 arg_action = ACTION_WATCH;
661 arg_action = ACTION_WALL;
676 assert_not_reached("Unhandled option");
680 if (optind != argc) {
688 int main(int argc, char *argv[]) {
691 log_set_target(LOG_TARGET_AUTO);
692 log_parse_environment();
697 if ((r = parse_argv(argc, argv)) <= 0)
705 if (arg_action == ACTION_WATCH ||
706 arg_action == ACTION_WALL)
707 r = watch_passwords();
709 r = show_passwords();
712 log_error("Error: %s", strerror(-r));
715 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;