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 char *socket_name = NULL, *message = NULL, *packet = NULL;
247 uint64_t not_after = 0;
250 bool accept_cached = false;
252 const ConfigTableItem items[] = {
253 { "Ask", "Socket", config_parse_string, 0, &socket_name },
254 { "Ask", "NotAfter", config_parse_uint64, 0, ¬_after },
255 { "Ask", "Message", config_parse_string, 0, &message },
256 { "Ask", "PID", config_parse_unsigned, 0, &pid },
257 { "Ask", "AcceptCached", config_parse_bool, 0, &accept_cached },
258 { NULL, NULL, NULL, 0, NULL }
265 r = config_parse(NULL, filename, NULL,
267 config_item_table_lookup, items,
268 true, false, true, NULL);
273 log_error("Invalid password file %s", filename);
279 if (now(CLOCK_MONOTONIC) > not_after) {
285 if (pid > 0 && !pid_is_alive(pid)) {
290 if (arg_action == ACTION_LIST)
291 printf("'%s' (PID %u)\n", message, pid);
292 else if (arg_action == ACTION_WALL) {
296 "%s%sPassword entry required for \'%s\' (PID %u).\r\n"
297 "Please enter password with the systemd-tty-ask-password-agent tool!",
299 *wall ? "\r\n\r\n" : "",
311 struct sockaddr_un un;
313 size_t packet_length = 0;
315 assert(arg_action == ACTION_QUERY ||
316 arg_action == ACTION_WATCH);
318 if (access(socket_name, W_OK) < 0) {
320 if (arg_action == ACTION_QUERY)
321 log_info("Not querying '%s' (PID %u), lacking privileges.", message, pid);
328 _cleanup_strv_free_ char **passwords = NULL;
330 if ((r = ask_password_plymouth(message, not_after, filename, accept_cached, &passwords)) >= 0) {
334 STRV_FOREACH(p, passwords)
335 packet_length += strlen(*p) + 1;
337 if (!(packet = new(char, packet_length)))
345 STRV_FOREACH(p, passwords)
346 d = stpcpy(d, *p) + 1;
352 char *password = NULL;
355 if ((tty_fd = acquire_terminal("/dev/console", false, false, false, (usec_t) -1)) < 0) {
360 r = ask_password_tty(message, not_after, filename, &password);
368 packet_length = 1+strlen(password)+1;
369 if (!(packet = new(char, packet_length)))
373 strcpy(packet+1, password);
380 if (r == -ETIME || r == -ENOENT) {
381 /* If the query went away, that's OK */
387 log_error("Failed to query password: %s", strerror(-r));
391 if ((socket_fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0)) < 0) {
392 log_error("socket(): %m");
397 sa.un.sun_family = AF_UNIX;
398 strncpy(sa.un.sun_path, socket_name, sizeof(sa.un.sun_path));
400 if (sendto(socket_fd, packet, packet_length, MSG_NOSIGNAL, &sa.sa, offsetof(struct sockaddr_un, sun_path) + strlen(socket_name)) < 0) {
401 log_error("Failed to send: %m");
408 safe_close(socket_fd);
417 static int wall_tty_block(void) {
422 r = get_ctty_devnr(0, &devnr);
426 if (asprintf(&p, "/run/systemd/ask-password-block/%u:%u", major(devnr), minor(devnr)) < 0)
429 mkdir_parents_label(p, 0700);
432 fd = open(p, O_RDONLY|O_CLOEXEC|O_NONBLOCK|O_NOCTTY);
441 static bool wall_tty_match(const char *path) {
446 if (path_is_absolute(path))
447 k = lstat(path, &st);
449 if (asprintf(&p, "/dev/%s", path) < 0)
459 if (!S_ISCHR(st.st_mode))
462 /* We use named pipes to ensure that wall messages suggesting
463 * password entry are not printed over password prompts
464 * already shown. We use the fact here that opening a pipe in
465 * non-blocking mode for write-only will succeed only if
466 * there's some writer behind it. Using pipes has the
467 * advantage that the block will automatically go away if the
470 if (asprintf(&p, "/run/systemd/ask-password-block/%u:%u", major(st.st_rdev), minor(st.st_rdev)) < 0)
473 fd = open(p, O_WRONLY|O_CLOEXEC|O_NONBLOCK|O_NOCTTY);
479 /* What, we managed to open the pipe? Then this tty is filtered. */
484 static int show_passwords(void) {
489 if (!(d = opendir("/run/systemd/ask-password"))) {
493 log_error("opendir(/run/systemd/ask-password): %m");
497 while ((de = readdir(d))) {
502 /* We only support /dev on tmpfs, hence we can rely on
503 * d_type to be reliable */
505 if (de->d_type != DT_REG)
508 if (ignore_file(de->d_name))
511 if (!startswith(de->d_name, "ask."))
514 if (!(p = strappend("/run/systemd/ask-password/", de->d_name))) {
520 if ((q = parse_password(p, &wall)) < 0)
526 utmp_wall(wall, NULL, wall_tty_match);
538 static int watch_passwords(void) {
545 int notify = -1, signal_fd = -1, tty_block_fd = -1;
546 struct pollfd pollfd[_FD_MAX] = {};
550 tty_block_fd = wall_tty_block();
552 mkdir_p_label("/run/systemd/ask-password", 0755);
554 if ((notify = inotify_init1(IN_CLOEXEC)) < 0) {
559 if (inotify_add_watch(notify, "/run/systemd/ask-password", IN_CLOSE_WRITE|IN_MOVED_TO) < 0) {
564 assert_se(sigemptyset(&mask) == 0);
565 sigset_add_many(&mask, SIGINT, SIGTERM, -1);
566 assert_se(sigprocmask(SIG_SETMASK, &mask, NULL) == 0);
568 if ((signal_fd = signalfd(-1, &mask, SFD_NONBLOCK|SFD_CLOEXEC)) < 0) {
569 log_error("signalfd(): %m");
574 pollfd[FD_INOTIFY].fd = notify;
575 pollfd[FD_INOTIFY].events = POLLIN;
576 pollfd[FD_SIGNAL].fd = signal_fd;
577 pollfd[FD_SIGNAL].events = POLLIN;
580 if ((r = show_passwords()) < 0)
581 log_error("Failed to show password: %s", strerror(-r));
583 if (poll(pollfd, _FD_MAX, -1) < 0) {
592 if (pollfd[FD_INOTIFY].revents != 0)
595 if (pollfd[FD_SIGNAL].revents != 0)
603 safe_close(signal_fd);
604 safe_close(tty_block_fd);
609 static int help(void) {
611 printf("%s [OPTIONS...]\n\n"
612 "Process system password requests.\n\n"
613 " -h --help Show this help\n"
614 " --version Show package version\n"
615 " --list Show pending password requests\n"
616 " --query Process pending password requests\n"
617 " --watch Continuously process password requests\n"
618 " --wall Continuously forward password requests to wall\n"
619 " --plymouth Ask question with Plymouth instead of on TTY\n"
620 " --console Ask question on /dev/console instead of current TTY\n",
621 program_invocation_short_name);
626 static int parse_argv(int argc, char *argv[]) {
638 static const struct option options[] = {
639 { "help", no_argument, NULL, 'h' },
640 { "version", no_argument, NULL, ARG_VERSION },
641 { "list", no_argument, NULL, ARG_LIST },
642 { "query", no_argument, NULL, ARG_QUERY },
643 { "watch", no_argument, NULL, ARG_WATCH },
644 { "wall", no_argument, NULL, ARG_WALL },
645 { "plymouth", no_argument, NULL, ARG_PLYMOUTH },
646 { "console", no_argument, NULL, ARG_CONSOLE },
655 while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) {
663 puts(PACKAGE_STRING);
664 puts(SYSTEMD_FEATURES);
668 arg_action = ACTION_LIST;
672 arg_action = ACTION_QUERY;
676 arg_action = ACTION_WATCH;
680 arg_action = ACTION_WALL;
695 assert_not_reached("Unhandled option");
699 if (optind != argc) {
707 int main(int argc, char *argv[]) {
710 log_set_target(LOG_TARGET_AUTO);
711 log_parse_environment();
716 if ((r = parse_argv(argc, argv)) <= 0)
724 if (arg_action == ACTION_WATCH ||
725 arg_action == ACTION_WALL)
726 r = watch_passwords();
728 r = show_passwords();
731 log_error("Error: %s", strerror(-r));
734 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;