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"
49 } arg_action = ACTION_QUERY;
51 static bool arg_plymouth = false;
52 static bool arg_console = false;
54 static int ask_password_plymouth(
57 const char *flag_file,
59 char ***_passphrases) {
61 int fd = -1, notify = -1;
62 union sockaddr_union sa;
66 struct pollfd pollfd[2];
67 char buffer[LINE_MAX];
77 if ((notify = inotify_init1(IN_CLOEXEC|IN_NONBLOCK)) < 0) {
82 if (inotify_add_watch(notify, flag_file, IN_ATTRIB /* for the link count */) < 0) {
88 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;
119 pollfd[POLL_SOCKET].fd = fd;
120 pollfd[POLL_SOCKET].events = POLLIN;
121 pollfd[POLL_INOTIFY].fd = notify;
122 pollfd[POLL_INOTIFY].events = POLLIN;
125 int sleep_for = -1, j;
130 y = now(CLOCK_MONOTONIC);
137 sleep_for = (int) ((until - y) / USEC_PER_MSEC);
141 if (access(flag_file, F_OK) < 0) {
146 if ((j = poll(pollfd, notify > 0 ? 2 : 1, sleep_for)) < 0) {
158 if (notify > 0 && pollfd[POLL_INOTIFY].revents != 0)
161 if (pollfd[POLL_SOCKET].revents == 0)
164 if ((k = read(fd, buffer + p, sizeof(buffer) - p)) <= 0) {
165 r = k < 0 ? -errno : -EIO;
174 if (buffer[0] == 5) {
177 /* Hmm, first try with cached
178 * passwords failed, so let's retry
179 * with a normal password request */
183 if (asprintf(&packet, "*\002%c%s%n", (int) (strlen(message) + 1), message, &n) < 0) {
188 if ((k = loop_write(fd, packet, n+1, true)) != n+1) {
189 r = k < 0 ? (int) k : -EIO;
193 accept_cached = false;
198 /* No password, because UI not shown */
202 } else if (buffer[0] == 2 || buffer[0] == 9) {
206 /* One ore more answers */
210 memcpy(&size, buffer+1, sizeof(size));
211 size = le32toh(size);
212 if (size+5 > sizeof(buffer)) {
220 if (!(l = strv_parse_nulstr(buffer + 5, size))) {
239 close_nointr_nofail(notify);
242 close_nointr_nofail(fd);
249 static int parse_password(const char *filename, char **wall) {
250 char *socket_name = NULL, *message = NULL, *packet = NULL;
251 uint64_t not_after = 0;
254 bool accept_cached = false;
256 const ConfigTableItem items[] = {
257 { "Ask", "Socket", config_parse_string, 0, &socket_name },
258 { "Ask", "NotAfter", config_parse_uint64, 0, ¬_after },
259 { "Ask", "Message", config_parse_string, 0, &message },
260 { "Ask", "PID", config_parse_unsigned, 0, &pid },
261 { "Ask", "AcceptCached", config_parse_bool, 0, &accept_cached },
262 { NULL, NULL, NULL, 0, NULL }
270 f = fopen(filename, "re");
275 log_error("open(%s): %m", filename);
279 r = config_parse(filename, f, NULL, config_item_table_lookup, (void*) items, true, NULL);
281 log_error("Failed to parse password file %s: %s", filename, strerror(-r));
286 log_error("Invalid password file %s", filename);
292 if (now(CLOCK_MONOTONIC) > not_after) {
305 if (arg_action == ACTION_LIST)
306 printf("'%s' (PID %u)\n", message, pid);
307 else if (arg_action == ACTION_WALL) {
311 "%s%sPassword entry required for \'%s\' (PID %u).\r\n"
312 "Please enter password with the systemd-tty-ask-password-agent tool!",
314 *wall ? "\r\n\r\n" : "",
317 log_error("Out of memory");
327 struct sockaddr_un un;
329 size_t packet_length = 0;
331 assert(arg_action == ACTION_QUERY ||
332 arg_action == ACTION_WATCH);
334 if (access(socket_name, W_OK) < 0) {
336 if (arg_action == ACTION_QUERY)
337 log_info("Not querying '%s' (PID %u), lacking privileges.", message, pid);
344 char **passwords = NULL;
346 if ((r = ask_password_plymouth(message, not_after, filename, accept_cached, &passwords)) >= 0) {
350 STRV_FOREACH(p, passwords)
351 packet_length += strlen(*p) + 1;
353 if (!(packet = new(char, packet_length)))
361 STRV_FOREACH(p, passwords)
362 d = stpcpy(d, *p) + 1;
371 if ((tty_fd = acquire_terminal("/dev/console", false, false, false)) < 0) {
376 r = ask_password_tty(message, not_after, filename, &password);
379 close_nointr_nofail(tty_fd);
384 packet_length = 1+strlen(password)+1;
385 if (!(packet = new(char, packet_length)))
389 strcpy(packet+1, password);
396 if (r == -ETIME || r == -ENOENT) {
397 /* If the query went away, that's OK */
403 log_error("Failed to query password: %s", strerror(-r));
407 if ((socket_fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0)) < 0) {
408 log_error("socket(): %m");
414 sa.un.sun_family = AF_UNIX;
415 strncpy(sa.un.sun_path, socket_name, sizeof(sa.un.sun_path));
417 if (sendto(socket_fd, packet, packet_length, MSG_NOSIGNAL, &sa.sa, offsetof(struct sockaddr_un, sun_path) + strlen(socket_name)) < 0) {
418 log_error("Failed to send: %m");
428 close_nointr_nofail(socket_fd);
437 static int wall_tty_block(void) {
442 r = get_ctty_devnr(0, &devnr);
446 if (asprintf(&p, "/run/systemd/ask-password-block/%u:%u", major(devnr), minor(devnr)) < 0)
449 mkdir_parents_label(p, 0700);
452 fd = open(p, O_RDONLY|O_CLOEXEC|O_NONBLOCK|O_NOCTTY);
461 static bool wall_tty_match(const char *path) {
466 if (path_is_absolute(path))
467 k = lstat(path, &st);
469 if (asprintf(&p, "/dev/%s", path) < 0)
479 if (!S_ISCHR(st.st_mode))
482 /* We use named pipes to ensure that wall messages suggesting
483 * password entry are not printed over password prompts
484 * already shown. We use the fact here that opening a pipe in
485 * non-blocking mode for write-only will succeed only if
486 * there's some writer behind it. Using pipes has the
487 * advantage that the block will automatically go away if the
490 if (asprintf(&p, "/run/systemd/ask-password-block/%u:%u", major(st.st_rdev), minor(st.st_rdev)) < 0)
493 fd = open(p, O_WRONLY|O_CLOEXEC|O_NONBLOCK|O_NOCTTY);
499 /* What, we managed to open the pipe? Then this tty is filtered. */
500 close_nointr_nofail(fd);
504 static int show_passwords(void) {
509 if (!(d = opendir("/run/systemd/ask-password"))) {
513 log_error("opendir(): %m");
517 while ((de = readdir(d))) {
522 /* We only support /dev on tmpfs, hence we can rely on
523 * d_type to be reliable */
525 if (de->d_type != DT_REG)
528 if (ignore_file(de->d_name))
531 if (!startswith(de->d_name, "ask."))
534 if (!(p = strappend("/run/systemd/ask-password/", de->d_name))) {
535 log_error("Out of memory");
541 if ((q = parse_password(p, &wall)) < 0)
547 utmp_wall(wall, wall_tty_match);
559 static int watch_passwords(void) {
566 int notify = -1, signal_fd = -1, tty_block_fd = -1;
567 struct pollfd pollfd[_FD_MAX];
571 tty_block_fd = wall_tty_block();
573 mkdir_p_label("/run/systemd/ask-password", 0755);
575 if ((notify = inotify_init1(IN_CLOEXEC)) < 0) {
580 if (inotify_add_watch(notify, "/run/systemd/ask-password", IN_CLOSE_WRITE|IN_MOVED_TO) < 0) {
585 assert_se(sigemptyset(&mask) == 0);
586 sigset_add_many(&mask, SIGINT, SIGTERM, -1);
587 assert_se(sigprocmask(SIG_SETMASK, &mask, NULL) == 0);
589 if ((signal_fd = signalfd(-1, &mask, SFD_NONBLOCK|SFD_CLOEXEC)) < 0) {
590 log_error("signalfd(): %m");
596 pollfd[FD_INOTIFY].fd = notify;
597 pollfd[FD_INOTIFY].events = POLLIN;
598 pollfd[FD_SIGNAL].fd = signal_fd;
599 pollfd[FD_SIGNAL].events = POLLIN;
602 if ((r = show_passwords()) < 0)
603 log_error("Failed to show password: %s", strerror(-r));
605 if (poll(pollfd, _FD_MAX, -1) < 0) {
614 if (pollfd[FD_INOTIFY].revents != 0)
617 if (pollfd[FD_SIGNAL].revents != 0)
625 close_nointr_nofail(notify);
628 close_nointr_nofail(signal_fd);
630 if (tty_block_fd >= 0)
631 close_nointr_nofail(tty_block_fd);
636 static int help(void) {
638 printf("%s [OPTIONS...]\n\n"
639 "Process system password requests.\n\n"
640 " -h --help Show this help\n"
641 " --list Show pending password requests\n"
642 " --query Process pending password requests\n"
643 " --watch Continuously process password requests\n"
644 " --wall Continuously forward password requests to wall\n"
645 " --plymouth Ask question with Plymouth instead of on TTY\n"
646 " --console Ask question on /dev/console instead of current TTY\n",
647 program_invocation_short_name);
652 static int parse_argv(int argc, char *argv[]) {
663 static const struct option options[] = {
664 { "help", no_argument, NULL, 'h' },
665 { "list", no_argument, NULL, ARG_LIST },
666 { "query", no_argument, NULL, ARG_QUERY },
667 { "watch", no_argument, NULL, ARG_WATCH },
668 { "wall", no_argument, NULL, ARG_WALL },
669 { "plymouth", no_argument, NULL, ARG_PLYMOUTH },
670 { "console", no_argument, NULL, ARG_CONSOLE },
679 while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) {
688 arg_action = ACTION_LIST;
692 arg_action = ACTION_QUERY;
696 arg_action = ACTION_WATCH;
700 arg_action = ACTION_WALL;
715 log_error("Unknown option code %c", c);
720 if (optind != argc) {
728 int main(int argc, char *argv[]) {
731 log_parse_environment();
736 if ((r = parse_argv(argc, argv)) <= 0)
744 if (arg_action == ACTION_WATCH ||
745 arg_action == ACTION_WALL)
746 r = watch_passwords();
748 r = show_passwords();
751 log_error("Error: %s", strerror(-r));
754 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;