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 "conf-parser.h"
38 #include "utmp-wtmp.h"
39 #include "socket-util.h"
40 #include "ask-password-api.h"
48 } arg_action = ACTION_QUERY;
50 static bool arg_plymouth = false;
51 static bool arg_console = false;
53 static int ask_password_plymouth(
56 const char *flag_file,
58 char ***_passphrases) {
60 int fd = -1, notify = -1;
61 union sockaddr_union sa;
65 struct pollfd pollfd[2];
66 char buffer[LINE_MAX];
76 if ((notify = inotify_init1(IN_CLOEXEC|IN_NONBLOCK)) < 0) {
81 if (inotify_add_watch(notify, flag_file, IN_ATTRIB /* for the link count */) < 0) {
87 if ((fd = socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0)) < 0) {
93 sa.sa.sa_family = AF_UNIX;
94 strncpy(sa.un.sun_path+1, "/org/freedesktop/plymouthd", sizeof(sa.un.sun_path)-1);
95 if (connect(fd, &sa.sa, offsetof(struct sockaddr_un, sun_path) + 1 + strlen(sa.un.sun_path+1)) < 0) {
96 log_error("Failed to connect to Plymouth: %m");
102 packet = strdup("c");
105 asprintf(&packet, "*\002%c%s%n", (int) (strlen(message) + 1), message, &n);
112 if ((k = loop_write(fd, packet, n+1, true)) != n+1) {
113 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))) {
238 close_nointr_nofail(notify);
241 close_nointr_nofail(fd);
248 static int parse_password(const char *filename, char **wall) {
249 char *socket_name = NULL, *message = NULL, *packet = NULL;
250 uint64_t not_after = 0;
253 bool accept_cached = false;
255 const ConfigTableItem items[] = {
256 { "Ask", "Socket", config_parse_string, 0, &socket_name },
257 { "Ask", "NotAfter", config_parse_uint64, 0, ¬_after },
258 { "Ask", "Message", config_parse_string, 0, &message },
259 { "Ask", "PID", config_parse_unsigned, 0, &pid },
260 { "Ask", "AcceptCached", config_parse_bool, 0, &accept_cached },
261 { NULL, NULL, NULL, 0, NULL }
269 f = fopen(filename, "re");
274 log_error("open(%s): %m", filename);
278 r = config_parse(filename, f, NULL, config_item_table_lookup, (void*) items, true, NULL);
280 log_error("Failed to parse password file %s: %s", filename, strerror(-r));
285 log_error("Invalid password file %s", filename);
291 if (now(CLOCK_MONOTONIC) > not_after) {
304 if (arg_action == ACTION_LIST)
305 printf("'%s' (PID %u)\n", message, pid);
306 else if (arg_action == ACTION_WALL) {
310 "%s%sPassword entry required for \'%s\' (PID %u).\r\n"
311 "Please enter password with the systemd-tty-ask-password-agent tool!",
313 *wall ? "\r\n\r\n" : "",
316 log_error("Out of memory");
326 struct sockaddr_un un;
328 size_t packet_length = 0;
330 assert(arg_action == ACTION_QUERY ||
331 arg_action == ACTION_WATCH);
333 if (access(socket_name, W_OK) < 0) {
335 if (arg_action == ACTION_QUERY)
336 log_info("Not querying '%s' (PID %u), lacking privileges.", message, pid);
343 char **passwords = NULL;
345 if ((r = ask_password_plymouth(message, not_after, filename, accept_cached, &passwords)) >= 0) {
349 STRV_FOREACH(p, passwords)
350 packet_length += strlen(*p) + 1;
352 if (!(packet = new(char, packet_length)))
360 STRV_FOREACH(p, passwords)
361 d = stpcpy(d, *p) + 1;
370 if ((tty_fd = acquire_terminal("/dev/console", false, false, false)) < 0) {
375 r = ask_password_tty(message, not_after, filename, &password);
378 close_nointr_nofail(tty_fd);
383 packet_length = 1+strlen(password)+1;
384 if (!(packet = new(char, packet_length)))
388 strcpy(packet+1, password);
395 if (r == -ETIME || r == -ENOENT) {
396 /* If the query went away, that's OK */
402 log_error("Failed to query password: %s", strerror(-r));
406 if ((socket_fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0)) < 0) {
407 log_error("socket(): %m");
413 sa.un.sun_family = AF_UNIX;
414 strncpy(sa.un.sun_path, socket_name, sizeof(sa.un.sun_path));
416 if (sendto(socket_fd, packet, packet_length, MSG_NOSIGNAL, &sa.sa, offsetof(struct sockaddr_un, sun_path) + strlen(socket_name)) < 0) {
417 log_error("Failed to send: %m");
427 close_nointr_nofail(socket_fd);
436 static int wall_tty_block(void) {
441 r = get_ctty_devnr(0, &devnr);
445 if (asprintf(&p, "/run/systemd/ask-password-block/%u:%u", major(devnr), minor(devnr)) < 0)
448 mkdir_parents(p, 0700);
451 fd = open(p, O_RDONLY|O_CLOEXEC|O_NONBLOCK|O_NOCTTY);
460 static bool wall_tty_match(const char *path) {
465 if (path_is_absolute(path))
466 k = lstat(path, &st);
468 if (asprintf(&p, "/dev/%s", path) < 0)
478 if (!S_ISCHR(st.st_mode))
481 /* We use named pipes to ensure that wall messages suggesting
482 * password entry are not printed over password prompts
483 * already shown. We use the fact here that opening a pipe in
484 * non-blocking mode for write-only will succeed only if
485 * there's some writer behind it. Using pipes has the
486 * advantage that the block will automatically go away if the
489 if (asprintf(&p, "/run/systemd/ask-password-block/%u:%u", major(st.st_rdev), minor(st.st_rdev)) < 0)
492 fd = open(p, O_WRONLY|O_CLOEXEC|O_NONBLOCK|O_NOCTTY);
498 /* What, we managed to open the pipe? Then this tty is filtered. */
499 close_nointr_nofail(fd);
503 static int show_passwords(void) {
508 if (!(d = opendir("/run/systemd/ask-password"))) {
512 log_error("opendir(): %m");
516 while ((de = readdir(d))) {
521 /* We only support /dev on tmpfs, hence we can rely on
522 * d_type to be reliable */
524 if (de->d_type != DT_REG)
527 if (ignore_file(de->d_name))
530 if (!startswith(de->d_name, "ask."))
533 if (!(p = strappend("/run/systemd/ask-password/", de->d_name))) {
534 log_error("Out of memory");
540 if ((q = parse_password(p, &wall)) < 0)
546 utmp_wall(wall, wall_tty_match);
558 static int watch_passwords(void) {
565 int notify = -1, signal_fd = -1, tty_block_fd = -1;
566 struct pollfd pollfd[_FD_MAX];
570 tty_block_fd = wall_tty_block();
572 mkdir_p("/run/systemd/ask-password", 0755);
574 if ((notify = inotify_init1(IN_CLOEXEC)) < 0) {
579 if (inotify_add_watch(notify, "/run/systemd/ask-password", IN_CLOSE_WRITE|IN_MOVED_TO) < 0) {
584 assert_se(sigemptyset(&mask) == 0);
585 sigset_add_many(&mask, SIGINT, SIGTERM, -1);
586 assert_se(sigprocmask(SIG_SETMASK, &mask, NULL) == 0);
588 if ((signal_fd = signalfd(-1, &mask, SFD_NONBLOCK|SFD_CLOEXEC)) < 0) {
589 log_error("signalfd(): %m");
595 pollfd[FD_INOTIFY].fd = notify;
596 pollfd[FD_INOTIFY].events = POLLIN;
597 pollfd[FD_SIGNAL].fd = signal_fd;
598 pollfd[FD_SIGNAL].events = POLLIN;
601 if ((r = show_passwords()) < 0)
602 log_error("Failed to show password: %s", strerror(-r));
604 if (poll(pollfd, _FD_MAX, -1) < 0) {
613 if (pollfd[FD_INOTIFY].revents != 0)
616 if (pollfd[FD_SIGNAL].revents != 0)
624 close_nointr_nofail(notify);
627 close_nointr_nofail(signal_fd);
629 if (tty_block_fd >= 0)
630 close_nointr_nofail(tty_block_fd);
635 static int help(void) {
637 printf("%s [OPTIONS...]\n\n"
638 "Process system password requests.\n\n"
639 " -h --help Show this help\n"
640 " --list Show pending password requests\n"
641 " --query Process pending password requests\n"
642 " --watch Continuously process password requests\n"
643 " --wall Continuously forward password requests to wall\n"
644 " --plymouth Ask question with Plymouth instead of on TTY\n"
645 " --console Ask question on /dev/console instead of current TTY\n",
646 program_invocation_short_name);
651 static int parse_argv(int argc, char *argv[]) {
662 static const struct option options[] = {
663 { "help", no_argument, NULL, 'h' },
664 { "list", no_argument, NULL, ARG_LIST },
665 { "query", no_argument, NULL, ARG_QUERY },
666 { "watch", no_argument, NULL, ARG_WATCH },
667 { "wall", no_argument, NULL, ARG_WALL },
668 { "plymouth", no_argument, NULL, ARG_PLYMOUTH },
669 { "console", no_argument, NULL, ARG_CONSOLE },
678 while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) {
687 arg_action = ACTION_LIST;
691 arg_action = ACTION_QUERY;
695 arg_action = ACTION_WATCH;
699 arg_action = ACTION_WALL;
714 log_error("Unknown option code %c", c);
719 if (optind != argc) {
727 int main(int argc, char *argv[]) {
730 log_parse_environment();
735 if ((r = parse_argv(argc, argv)) <= 0)
743 if (arg_action == ACTION_WATCH ||
744 arg_action == ACTION_WALL)
745 r = watch_passwords();
747 r = show_passwords();
750 log_error("Error: %s", strerror(-r));
753 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;