systemd/tty-ask-password-agent-on-console.patch

377 lines
13 KiB
Diff
Raw Permalink Normal View History

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/tty-ask-password-agent.c | 264 +++++++++++++++++++-
1 file changed, 254 insertions(+), 10 deletions(-)
Index: systemd-228/src/tty-ask-password-agent/tty-ask-password-agent.c
===================================================================
--- systemd-228.orig/src/tty-ask-password-agent/tty-ask-password-agent.c
+++ systemd-228/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
@@ -21,6 +22,9 @@
#include <errno.h>
#include <fcntl.h>
+#include <sys/prctl.h>
+#include <signal.h>
+#include <sys/wait.h>
#include <getopt.h>
#include <poll.h>
#include <stdbool.h>
@@ -49,6 +53,8 @@
#include "terminal-util.h"
#include "util.h"
#include "utmp-wtmp.h"
+#include "fileio.h"
+#include "macro.h"
static enum {
ACTION_LIST,
@@ -57,8 +63,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,
@@ -240,6 +259,80 @@ finish:
return r;
}
+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;
bool accept_cached = false, echo = false;
@@ -340,7 +433,7 @@ static int parse_password(const char *fi
int tty_fd = -1;
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 log_error_errno(tty_fd, "Failed to acquire /dev/console: %m");
@@ -601,7 +694,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 },
{}
};
@@ -643,6 +736,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 '?':
@@ -657,9 +754,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;
@@ -673,16 +904,29 @@ int main(int argc, char *argv[]) {
if (r <= 0)
goto finish;
- if (arg_console) {
- (void) setsid();
- (void) release_terminal();
+ if (arg_console && !arg_device)
+ /*
+ * Spawn 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();
-
finish:
return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
}