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) {
84 log_error("Failed to add watch on %s: %m", flag_file);
90 if ((fd = socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0)) < 0) {
96 sa.sa.sa_family = AF_UNIX;
97 strncpy(sa.un.sun_path+1, "/org/freedesktop/plymouthd", sizeof(sa.un.sun_path)-1);
98 if (connect(fd, &sa.sa, offsetof(struct sockaddr_un, sun_path) + 1 + strlen(sa.un.sun_path+1)) < 0) {
99 log_error("Failed to connect to Plymouth: %m");
105 packet = strdup("c");
108 asprintf(&packet, "*\002%c%s%n", (int) (strlen(message) + 1), message, &n);
115 if ((k = loop_write(fd, packet, n+1, true)) != n+1) {
116 r = k < 0 ? (int) k : -EIO;
121 pollfd[POLL_SOCKET].fd = fd;
122 pollfd[POLL_SOCKET].events = POLLIN;
123 pollfd[POLL_INOTIFY].fd = notify;
124 pollfd[POLL_INOTIFY].events = POLLIN;
127 int sleep_for = -1, j;
132 y = now(CLOCK_MONOTONIC);
139 sleep_for = (int) ((until - y) / USEC_PER_MSEC);
143 if (access(flag_file, F_OK) < 0) {
148 if ((j = poll(pollfd, notify > 0 ? 2 : 1, sleep_for)) < 0) {
160 if (notify > 0 && pollfd[POLL_INOTIFY].revents != 0)
163 if (pollfd[POLL_SOCKET].revents == 0)
166 if ((k = read(fd, buffer + p, sizeof(buffer) - p)) <= 0) {
167 r = k < 0 ? -errno : -EIO;
176 if (buffer[0] == 5) {
179 /* Hmm, first try with cached
180 * passwords failed, so let's retry
181 * with a normal password request */
185 if (asprintf(&packet, "*\002%c%s%n", (int) (strlen(message) + 1), message, &n) < 0) {
190 if ((k = loop_write(fd, packet, n+1, true)) != n+1) {
191 r = k < 0 ? (int) k : -EIO;
195 accept_cached = false;
200 /* No password, because UI not shown */
204 } else if (buffer[0] == 2 || buffer[0] == 9) {
208 /* One ore more answers */
212 memcpy(&size, buffer+1, sizeof(size));
213 size = le32toh(size);
214 if (size+5 > sizeof(buffer)) {
222 if (!(l = strv_parse_nulstr(buffer + 5, size))) {
241 close_nointr_nofail(notify);
244 close_nointr_nofail(fd);
251 static int parse_password(const char *filename, char **wall) {
252 char *socket_name = NULL, *message = NULL, *packet = NULL;
253 uint64_t not_after = 0;
256 bool accept_cached = false;
258 const ConfigTableItem items[] = {
259 { "Ask", "Socket", config_parse_string, 0, &socket_name },
260 { "Ask", "NotAfter", config_parse_uint64, 0, ¬_after },
261 { "Ask", "Message", config_parse_string, 0, &message },
262 { "Ask", "PID", config_parse_unsigned, 0, &pid },
263 { "Ask", "AcceptCached", config_parse_bool, 0, &accept_cached },
264 { NULL, NULL, NULL, 0, NULL }
272 f = fopen(filename, "re");
277 log_error("open(%s): %m", filename);
281 r = config_parse(filename, f, NULL, config_item_table_lookup, (void*) items, true, NULL);
283 log_error("Failed to parse password file %s: %s", filename, strerror(-r));
288 log_error("Invalid password file %s", filename);
294 if (now(CLOCK_MONOTONIC) > not_after) {
307 if (arg_action == ACTION_LIST)
308 printf("'%s' (PID %u)\n", message, pid);
309 else if (arg_action == ACTION_WALL) {
313 "%s%sPassword entry required for \'%s\' (PID %u).\r\n"
314 "Please enter password with the systemd-tty-ask-password-agent tool!",
316 *wall ? "\r\n\r\n" : "",
328 struct sockaddr_un un;
330 size_t packet_length = 0;
332 assert(arg_action == ACTION_QUERY ||
333 arg_action == ACTION_WATCH);
335 if (access(socket_name, W_OK) < 0) {
337 if (arg_action == ACTION_QUERY)
338 log_info("Not querying '%s' (PID %u), lacking privileges.", message, pid);
345 char **passwords = NULL;
347 if ((r = ask_password_plymouth(message, not_after, filename, accept_cached, &passwords)) >= 0) {
351 STRV_FOREACH(p, passwords)
352 packet_length += strlen(*p) + 1;
354 if (!(packet = new(char, packet_length)))
362 STRV_FOREACH(p, passwords)
363 d = stpcpy(d, *p) + 1;
372 if ((tty_fd = acquire_terminal("/dev/console", false, false, false, (usec_t) -1)) < 0) {
377 r = ask_password_tty(message, not_after, filename, &password);
380 close_nointr_nofail(tty_fd);
385 packet_length = 1+strlen(password)+1;
386 if (!(packet = new(char, packet_length)))
390 strcpy(packet+1, password);
397 if (r == -ETIME || r == -ENOENT) {
398 /* If the query went away, that's OK */
404 log_error("Failed to query password: %s", strerror(-r));
408 if ((socket_fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0)) < 0) {
409 log_error("socket(): %m");
415 sa.un.sun_family = AF_UNIX;
416 strncpy(sa.un.sun_path, socket_name, sizeof(sa.un.sun_path));
418 if (sendto(socket_fd, packet, packet_length, MSG_NOSIGNAL, &sa.sa, offsetof(struct sockaddr_un, sun_path) + strlen(socket_name)) < 0) {
419 log_error("Failed to send: %m");
429 close_nointr_nofail(socket_fd);
438 static int wall_tty_block(void) {
443 r = get_ctty_devnr(0, &devnr);
447 if (asprintf(&p, "/run/systemd/ask-password-block/%u:%u", major(devnr), minor(devnr)) < 0)
450 mkdir_parents_label(p, 0700);
453 fd = open(p, O_RDONLY|O_CLOEXEC|O_NONBLOCK|O_NOCTTY);
462 static bool wall_tty_match(const char *path) {
467 if (path_is_absolute(path))
468 k = lstat(path, &st);
470 if (asprintf(&p, "/dev/%s", path) < 0)
480 if (!S_ISCHR(st.st_mode))
483 /* We use named pipes to ensure that wall messages suggesting
484 * password entry are not printed over password prompts
485 * already shown. We use the fact here that opening a pipe in
486 * non-blocking mode for write-only will succeed only if
487 * there's some writer behind it. Using pipes has the
488 * advantage that the block will automatically go away if the
491 if (asprintf(&p, "/run/systemd/ask-password-block/%u:%u", major(st.st_rdev), minor(st.st_rdev)) < 0)
494 fd = open(p, O_WRONLY|O_CLOEXEC|O_NONBLOCK|O_NOCTTY);
500 /* What, we managed to open the pipe? Then this tty is filtered. */
501 close_nointr_nofail(fd);
505 static int show_passwords(void) {
510 if (!(d = opendir("/run/systemd/ask-password"))) {
514 log_error("opendir(): %m");
518 while ((de = readdir(d))) {
523 /* We only support /dev on tmpfs, hence we can rely on
524 * d_type to be reliable */
526 if (de->d_type != DT_REG)
529 if (ignore_file(de->d_name))
532 if (!startswith(de->d_name, "ask."))
535 if (!(p = strappend("/run/systemd/ask-password/", de->d_name))) {
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) {
581 log_error("Failed to add watch on /run/systemd/ask-password: %m");
586 assert_se(sigemptyset(&mask) == 0);
587 sigset_add_many(&mask, SIGINT, SIGTERM, -1);
588 assert_se(sigprocmask(SIG_SETMASK, &mask, NULL) == 0);
590 if ((signal_fd = signalfd(-1, &mask, SFD_NONBLOCK|SFD_CLOEXEC)) < 0) {
591 log_error("signalfd(): %m");
597 pollfd[FD_INOTIFY].fd = notify;
598 pollfd[FD_INOTIFY].events = POLLIN;
599 pollfd[FD_SIGNAL].fd = signal_fd;
600 pollfd[FD_SIGNAL].events = POLLIN;
603 if ((r = show_passwords()) < 0)
604 log_error("Failed to show password: %s", strerror(-r));
606 if (poll(pollfd, _FD_MAX, -1) < 0) {
615 if (pollfd[FD_INOTIFY].revents != 0)
618 if (pollfd[FD_SIGNAL].revents != 0)
626 close_nointr_nofail(notify);
629 close_nointr_nofail(signal_fd);
631 if (tty_block_fd >= 0)
632 close_nointr_nofail(tty_block_fd);
637 static int help(void) {
639 printf("%s [OPTIONS...]\n\n"
640 "Process system password requests.\n\n"
641 " -h --help Show this help\n"
642 " --version Show package version\n"
643 " --list Show pending password requests\n"
644 " --query Process pending password requests\n"
645 " --watch Continuously process password requests\n"
646 " --wall Continuously forward password requests to wall\n"
647 " --plymouth Ask question with Plymouth instead of on TTY\n"
648 " --console Ask question on /dev/console instead of current TTY\n",
649 program_invocation_short_name);
654 static int parse_argv(int argc, char *argv[]) {
666 static const struct option options[] = {
667 { "help", no_argument, NULL, 'h' },
668 { "version", no_argument, NULL, ARG_VERSION },
669 { "list", no_argument, NULL, ARG_LIST },
670 { "query", no_argument, NULL, ARG_QUERY },
671 { "watch", no_argument, NULL, ARG_WATCH },
672 { "wall", no_argument, NULL, ARG_WALL },
673 { "plymouth", no_argument, NULL, ARG_PLYMOUTH },
674 { "console", no_argument, NULL, ARG_CONSOLE },
683 while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) {
692 puts(PACKAGE_STRING);
693 puts(SYSTEMD_FEATURES);
697 arg_action = ACTION_LIST;
701 arg_action = ACTION_QUERY;
705 arg_action = ACTION_WATCH;
709 arg_action = ACTION_WALL;
724 log_error("Unknown option code %c", c);
729 if (optind != argc) {
737 int main(int argc, char *argv[]) {
740 log_set_target(LOG_TARGET_AUTO);
741 log_parse_environment();
746 if ((r = parse_argv(argc, argv)) <= 0)
754 if (arg_action == ACTION_WATCH ||
755 arg_action == ACTION_WALL)
756 r = watch_passwords();
758 r = show_passwords();
761 log_error("Error: %s", strerror(-r));
764 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;