forked from pool/systemd
380 lines
13 KiB
Diff
380 lines
13 KiB
Diff
From 907bc2aa36f58c6050cd4b7b290e0992a4373e49 Mon Sep 17 00:00:00 2001
|
|
From: Werner Fink <werner@suse.de>
|
|
Date: Wed, 30 Sep 2015 15:00:41 +0200
|
|
Subject: [PATCH] Ask for passphrases not only on the first console of
|
|
/dev/console
|
|
|
|
but also on all other consoles. This does help on e.g. mainframes
|
|
where often a serial console together with other consoles are
|
|
used. Even rack based servers attachted to both a serial console
|
|
as well as having a virtual console do sometimes miss a connected
|
|
monitor.
|
|
|
|
To be able to ask on all terminal devices of /dev/console the devices
|
|
are collected. If more than one device are found, then on each of the
|
|
terminals a inquiring task for passphrase is forked and do not return
|
|
to the caller.
|
|
|
|
Every task has its own session and its own controlling terminal.
|
|
If one of the tasks does handle a password, the remaining tasks
|
|
will be terminated.
|
|
|
|
Also let contradictory options on the command of
|
|
systemd-tty-ask-password-agent fail.
|
|
|
|
Spwan for each device of the system console /dev/console a own process.
|
|
|
|
Replace the system call wait() with with system call waitid().
|
|
---
|
|
src/tty-ask-password-agent.c | 264 ++++++++++++++++++++-
|
|
1 file changed, 255 insertions(+), 9 deletions(-)
|
|
|
|
diff --git src/tty-ask-password-agent/tty-ask-password-agent.c src/tty-ask-password-agent/tty-ask-password-agent.c
|
|
index 4630eb9..df4bada 100644
|
|
--- a/src/tty-ask-password-agent/tty-ask-password-agent.c
|
|
+++ b/src/tty-ask-password-agent/tty-ask-password-agent.c
|
|
@@ -4,6 +4,7 @@
|
|
This file is part of systemd.
|
|
|
|
Copyright 2010 Lennart Poettering
|
|
+ Copyright 2015 Werner Fink
|
|
|
|
systemd is free software; you can redistribute it and/or modify it
|
|
under the terms of the GNU Lesser General Public License as published by
|
|
@@ -31,6 +32,9 @@
|
|
#include <getopt.h>
|
|
#include <sys/signalfd.h>
|
|
#include <fcntl.h>
|
|
+#include <sys/prctl.h>
|
|
+#include <signal.h>
|
|
+#include <sys/wait.h>
|
|
|
|
#include "util.h"
|
|
#include "mkdir.h"
|
|
@@ -45,6 +49,8 @@
|
|
#include "process-util.h"
|
|
#include "terminal-util.h"
|
|
#include "signal-util.h"
|
|
+#include "fileio.h"
|
|
+#include "macro.h"
|
|
|
|
static enum {
|
|
ACTION_LIST,
|
|
@@ -53,8 +59,21 @@ static enum {
|
|
ACTION_WALL
|
|
} arg_action = ACTION_QUERY;
|
|
|
|
+struct console {
|
|
+ pid_t pid;
|
|
+ char *tty;
|
|
+};
|
|
+
|
|
+static volatile sig_atomic_t sigchild;
|
|
+
|
|
+static void chld_handler(int sig) {
|
|
+ ++sigchild;
|
|
+}
|
|
+
|
|
static bool arg_plymouth = false;
|
|
static bool arg_console = false;
|
|
+static bool arg_device = false;
|
|
+static const char *current_dev = "/dev/console";
|
|
|
|
static int ask_password_plymouth(
|
|
const char *message,
|
|
@@ -211,6 +230,80 @@ static int ask_password_plymouth(
|
|
return 0;
|
|
}
|
|
|
|
+static void free_consoles(struct console *con, unsigned int num) {
|
|
+ unsigned int n;
|
|
+
|
|
+ if (!con || num == 0)
|
|
+ return;
|
|
+
|
|
+ for (n = 0; n < num; n++)
|
|
+ free(con[n].tty);
|
|
+
|
|
+ free(con);
|
|
+}
|
|
+
|
|
+static int collect_consoles(struct console **consoles, unsigned int *num) {
|
|
+ _cleanup_free_ char *active = NULL;
|
|
+ const char *word, *state;
|
|
+ struct console *con = NULL;
|
|
+ size_t con_len = 0, len;
|
|
+ unsigned int count = 0;
|
|
+ int ret;
|
|
+
|
|
+ assert(num);
|
|
+ assert(consoles);
|
|
+
|
|
+ ret = read_one_line_file("/sys/class/tty/console/active", &active);
|
|
+ if (ret < 0)
|
|
+ return log_error_errno(ret, "Failed to read /sys/class/tty/console/active: %m");
|
|
+
|
|
+ FOREACH_WORD(word, len, active, state) {
|
|
+ _cleanup_free_ char *tty = NULL;
|
|
+
|
|
+ if (len == 4 && strneq(word, "tty0", 4)) {
|
|
+
|
|
+ ret = read_one_line_file("/sys/class/tty/tty0/active", &tty);
|
|
+ if (ret < 0)
|
|
+ return log_error_errno(ret, "Failed to read /sys/class/tty/tty0/active: %m");
|
|
+
|
|
+ word = tty;
|
|
+ len = strlen(tty);
|
|
+ }
|
|
+
|
|
+ con = GREEDY_REALLOC(con, con_len, 1+count);
|
|
+ if (!con)
|
|
+ return log_oom();
|
|
+
|
|
+ if (asprintf(&con[count].tty, "/dev/%.*s", (int)len, word) < 0) {
|
|
+ free_consoles(con, count);
|
|
+ return log_oom();
|
|
+ }
|
|
+
|
|
+ con[count].pid = 0;
|
|
+ count++;
|
|
+ }
|
|
+
|
|
+ if (!con) {
|
|
+ con = GREEDY_REALLOC(con, con_len, 1);
|
|
+ if (!con)
|
|
+ return log_oom();
|
|
+
|
|
+ con[0].tty = strdup(current_dev);
|
|
+ if (!con[0].tty) {
|
|
+ free_consoles(con, 1);
|
|
+ return log_oom();
|
|
+ }
|
|
+
|
|
+ con[0].pid = 0;
|
|
+ count++;
|
|
+ }
|
|
+
|
|
+ *num = count;
|
|
+ *consoles = con;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
static int parse_password(const char *filename, char **wall) {
|
|
_cleanup_free_ char *socket_name = NULL, *message = NULL, *packet = NULL;
|
|
uint64_t not_after = 0;
|
|
@@ -311,7 +404,7 @@ static int parse_password(const char *fi
|
|
_cleanup_free_ char *password = NULL;
|
|
|
|
if (arg_console) {
|
|
- tty_fd = acquire_terminal("/dev/console", false, false, false, USEC_INFINITY);
|
|
+ tty_fd = acquire_terminal(current_dev, false, false, false, USEC_INFINITY);
|
|
if (tty_fd < 0)
|
|
return tty_fd;
|
|
}
|
|
@@ -554,7 +647,7 @@ static int parse_argv(int argc, char *ar
|
|
{ "watch", no_argument, NULL, ARG_WATCH },
|
|
{ "wall", no_argument, NULL, ARG_WALL },
|
|
{ "plymouth", no_argument, NULL, ARG_PLYMOUTH },
|
|
- { "console", no_argument, NULL, ARG_CONSOLE },
|
|
+ { "console", optional_argument, NULL, ARG_CONSOLE },
|
|
{}
|
|
};
|
|
|
|
@@ -598,6 +691,10 @@ static int parse_argv(int argc, char *ar
|
|
|
|
case ARG_CONSOLE:
|
|
arg_console = true;
|
|
+ if (optarg && *optarg) {
|
|
+ current_dev = optarg;
|
|
+ arg_device = true;
|
|
+ }
|
|
break;
|
|
|
|
case '?':
|
|
@@ -612,9 +709,143 @@ static int parse_argv(int argc, char *ar
|
|
return -EINVAL;
|
|
}
|
|
|
|
+ if (arg_plymouth || arg_console) {
|
|
+
|
|
+ if (!IN_SET(arg_action, ACTION_QUERY, ACTION_WATCH)) {
|
|
+ log_error("%s conflicting options --query and --watch.", program_invocation_short_name);
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ if (arg_plymouth && arg_console) {
|
|
+ log_error("%s conflicting options --plymouth and --console.", program_invocation_short_name);
|
|
+ return -EINVAL;
|
|
+ }
|
|
+ }
|
|
+
|
|
return 1;
|
|
}
|
|
|
|
+/*
|
|
+ * To be able to ask on all terminal devices of /dev/console
|
|
+ * the devices are collected. If more than one device are found,
|
|
+ * then on each of the terminals a inquiring task is forked.
|
|
+ * Every task has its own session and its own controlling terminal.
|
|
+ * If one of the tasks does handle a password, the remaining tasks
|
|
+ * will be terminated.
|
|
+ */
|
|
+static int ask_on_consoles(int argc, char *argv[]) {
|
|
+ struct console *consoles = NULL;
|
|
+ struct sigaction sig = {
|
|
+ .sa_handler = chld_handler,
|
|
+ .sa_flags = SA_NOCLDSTOP | SA_RESTART,
|
|
+ };
|
|
+ struct sigaction oldsig;
|
|
+ sigset_t oldset;
|
|
+ unsigned int num = 0, id;
|
|
+ siginfo_t status = {};
|
|
+ int ret;
|
|
+
|
|
+ ret = collect_consoles(&consoles, &num);
|
|
+ if (ret < 0)
|
|
+ return log_error_errno(ret, "Failed to query password: %m");
|
|
+
|
|
+ assert_se(sigprocmask_many(SIG_UNBLOCK, &oldset, SIGHUP, SIGCHLD, -1) >= 0);
|
|
+
|
|
+ assert_se(sigemptyset(&sig.sa_mask) >= 0);
|
|
+ assert_se(sigaction(SIGCHLD, &sig, &oldsig) >= 0);
|
|
+
|
|
+ sig.sa_handler = SIG_DFL;
|
|
+ assert_se(sigaction(SIGHUP, &sig, NULL) >= 0);
|
|
+
|
|
+ for (id = 0; id < num; id++) {
|
|
+ consoles[id].pid = fork();
|
|
+
|
|
+ if (consoles[id].pid < 0)
|
|
+ return log_error_errno(errno, "Failed to query password: %m");
|
|
+
|
|
+ if (consoles[id].pid == 0) {
|
|
+ char *conarg;
|
|
+ int ac;
|
|
+
|
|
+ conarg = strjoina("--console=", consoles[id].tty);
|
|
+ if (!conarg)
|
|
+ return log_oom();
|
|
+
|
|
+ free_consoles(consoles, num); /* not used anymore */
|
|
+
|
|
+ assert_se(prctl(PR_SET_PDEATHSIG, SIGHUP) >= 0);
|
|
+
|
|
+ zero(sig);
|
|
+ assert_se(sigprocmask(SIG_UNBLOCK, &oldset, NULL) >= 0);
|
|
+ assert_se(sigaction(SIGCHLD, &oldsig, NULL) >= 0);
|
|
+
|
|
+ for (ac = 0; ac < argc; ac++) {
|
|
+ if (streq(argv[ac], "--console")) {
|
|
+ argv[ac] = conarg;
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ execv(SYSTEMD_TTY_ASK_PASSWORD_AGENT_BINARY_PATH, argv);
|
|
+
|
|
+ return log_error_errno(errno, "Failed to execute %s: %m", program_invocation_name);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ ret = 0;
|
|
+ while (true) {
|
|
+
|
|
+ if ((ret = waitid(P_ALL, 0, &status, WEXITED)) < 0) {
|
|
+
|
|
+ if (errno != EINTR) {
|
|
+ ret = -errno;
|
|
+ if (errno == ECHILD)
|
|
+ ret = EXIT_SUCCESS;
|
|
+ break;
|
|
+ }
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ for (id = 0; id < num; id++) {
|
|
+ struct timespec timeout;
|
|
+ sigset_t set;
|
|
+ int signum;
|
|
+
|
|
+ if (consoles[id].pid == status.si_pid || kill(consoles[id].pid, 0) < 0)
|
|
+ consoles[id].pid = -1;
|
|
+
|
|
+ if (consoles[id].pid < 0)
|
|
+ continue;
|
|
+
|
|
+ kill(consoles[id].pid, SIGHUP);
|
|
+
|
|
+ assert_se(sigemptyset(&set) >= 0);
|
|
+ assert_se(sigaddset(&set, SIGCHLD) >= 0);
|
|
+
|
|
+ timespec_store(&timeout, 50 * USEC_PER_MSEC);
|
|
+ signum = sigtimedwait(&set, NULL, &timeout);
|
|
+
|
|
+ if (signum != SIGCHLD) {
|
|
+
|
|
+ if (signum < 0 && errno != EAGAIN)
|
|
+ return log_error_errno(errno, "sigtimedwait() failed: %m");
|
|
+
|
|
+ if (signum >= 0)
|
|
+ log_warning("sigtimedwait() returned unexpected signal.");
|
|
+ }
|
|
+
|
|
+ kill(consoles[id].pid, SIGKILL);
|
|
+ }
|
|
+
|
|
+ if (WIFEXITED(status.si_status) && ret == 0)
|
|
+ ret = WEXITSTATUS(status.si_status);
|
|
+ }
|
|
+
|
|
+ free_consoles(consoles, num);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
int main(int argc, char *argv[]) {
|
|
int r;
|
|
|
|
@@ -628,15 +859,28 @@ int main(int argc, char *argv[]) {
|
|
if (r <= 0)
|
|
goto finish;
|
|
|
|
- if (arg_console) {
|
|
- setsid();
|
|
- release_terminal();
|
|
+ if (arg_console && !arg_device)
|
|
+ /*
|
|
+ * Spwan for each console device a own process
|
|
+ */
|
|
+ r = ask_on_consoles(argc, argv);
|
|
+ else {
|
|
+
|
|
+ if (arg_device) {
|
|
+ /*
|
|
+ * Later on a controlling terminal will be will be acquired,
|
|
+ * therefore the current process has to become a session
|
|
+ * leader and should not have a controlling terminal already.
|
|
+ */
|
|
+ (void) setsid();
|
|
+ (void) release_terminal();
|
|
+ }
|
|
+
|
|
+ if (IN_SET(arg_action, ACTION_WATCH, ACTION_WALL))
|
|
+ r = watch_passwords();
|
|
+ else
|
|
+ r = show_passwords();
|
|
}
|
|
-
|
|
- if (IN_SET(arg_action, ACTION_WATCH, ACTION_WALL))
|
|
- r = watch_passwords();
|
|
- else
|
|
- r = show_passwords();
|
|
|
|
if (r < 0)
|
|
log_error_errno(r, "Error: %m");
|
|
|
|
--
|
|
2.2.0
|
|
|