Dr. Werner Fink 2015-10-06 15:13:51 +00:00 committed by Git OBS Bridge
parent f65bc873a7
commit 173cbeb878
3 changed files with 247 additions and 128 deletions

View File

@ -1,3 +1,9 @@
-------------------------------------------------------------------
Tue Oct 6 15:13:04 UTC 2015 - werner@suse.de
- Modify patch tty-ask-password-agent-on-console.patch to reflect
the changes done for pull request 1432
-------------------------------------------------------------------
Thu Oct 1 15:58:32 UTC 2015 - jengelh@inai.de

View File

@ -1,3 +1,9 @@
-------------------------------------------------------------------
Tue Oct 6 15:13:04 UTC 2015 - werner@suse.de
- Modify patch tty-ask-password-agent-on-console.patch to reflect
the changes done for pull request 1432
-------------------------------------------------------------------
Thu Oct 1 15:58:32 UTC 2015 - jengelh@inai.de

View File

@ -1,6 +1,6 @@
From 633a5904c1c4e363a7147f47e2d9fdb1925f7b9f Mon Sep 17 00:00:00 2001
From 907bc2aa36f58c6050cd4b7b290e0992a4373e49 Mon Sep 17 00:00:00 2001
From: Werner Fink <werner@suse.de>
Date: Fri, 25 Sep 2015 14:28:58 +0200
Date: Wed, 30 Sep 2015 15:00:41 +0200
Subject: [PATCH] Ask for passphrases not only on the first console of
/dev/console
@ -9,22 +9,45 @@ 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 | 191 ++++++++++++++++++++-
1 file changed, 186 insertions(+), 5 deletions(-)
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 82cbf95..928a5e8 100644
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
@@ -31,6 +31,10 @@
@@ -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 <sys/mman.h>
#include "util.h"
#include "mkdir.h"
@ -37,7 +60,7 @@ index 82cbf95..928a5e8 100644
static enum {
ACTION_LIST,
@@ -53,6 +59,19 @@ static enum {
@@ -53,8 +59,21 @@ static enum {
ACTION_WALL
} arg_action = ACTION_QUERY;
@ -46,88 +69,101 @@ index 82cbf95..928a5e8 100644
+ char *tty;
+};
+
+static volatile unsigned long *usemask;
+static volatile sig_atomic_t sigchild;
+static void chld_handler(int sig)
+{
+ (void)sig;
+
+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";
@@ -210,6 +229,69 @@ static int ask_password_plymouth(
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, const unsigned int num) {
+static void free_consoles(struct console *con, unsigned int num) {
+ unsigned int n;
+ if (!con || !num)
+
+ if (!con || num == 0)
+ return;
+
+ for (n = 0; n < num; n++)
+ free(con[n].tty);
+
+ free(con);
+}
+
+static const char *current_dev = "/dev/console";
+static struct console* collect_consoles(unsigned int * num) {
+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(*num == 0);
+ assert(consoles);
+
+ ret = read_one_line_file("/sys/class/tty/console/active", &active);
+ if (ret < 0)
+ return con;
+ 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 (strneq(word, "tty0", len) &&
+ read_one_line_file("/sys/class/tty/tty0/active", &tty) >= 0) {
+ 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((void**)&con, &con_len, 1+(*num), sizeof(struct console));
+ if (con == NULL) {
+ log_oom();
+ return NULL;
+
+ 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();
+ }
+ if (asprintf(&con[*num].tty, "/dev/%.*s", (int)len, word) < 0) {
+ free_consoles(con, *num);
+ log_oom();
+ *num = 0;
+ return NULL;
+ }
+ con[*num].pid = 0;
+ (*num)++;
+
+ con[count].pid = 0;
+ count++;
+ }
+ if (con == NULL) {
+ con = greedy_realloc((void**)&con, &con_len, 1, sizeof(struct console));
+ if (con == NULL) {
+ log_oom();
+ return NULL;
+ }
+
+ if (!con) {
+ con = GREEDY_REALLOC(con, con_len, 1);
+ if (!con)
+ return log_oom();
+
+ con[0].tty = strdup(current_dev);
+ if (con[0].tty == NULL) {
+ if (!con[0].tty) {
+ free_consoles(con, 1);
+ log_oom();
+ return NULL;
+ return log_oom();
+ }
+
+ con[0].pid = 0;
+ (*num)++;
+ count++;
+ }
+ return con;
+
+ *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;
@@ -310,7 +392,7 @@ static int parse_password(const char *filename, char **wall) {
@@ -311,7 +404,7 @@ static int parse_password(const char *fi
_cleanup_free_ char *password = NULL;
if (arg_console) {
@ -136,137 +172,208 @@ index 82cbf95..928a5e8 100644
if (tty_fd < 0)
return tty_fd;
}
@@ -614,8 +696,90 @@ static int parse_argv(int argc, char *argv[]) {
@@ -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;
}
+static unsigned int wfa_child(const struct console * con, const unsigned int id)
+{
+ setsid();
+ release_terminal();
+ *usemask |= 1 << id; /* shared memory area */
+ current_dev = con[id].tty;
+ return id;
+}
+
+static unsigned int wait_for_answer(void)
+{
+ struct console *consoles;
+/*
+ * 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 set, oldset;
+ sigset_t oldset;
+ unsigned int num = 0, id;
+ int status = 0, ret;
+ pid_t job;
+ siginfo_t status = {};
+ int ret;
+
+ consoles = collect_consoles(&num);
+ if (!consoles) {
+ log_error("Failed to query password: %m");
+ exit(EXIT_FAILURE);
+ }
+ if (num < 2)
+ return wfa_child(consoles, 0);
+ 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);
+
+ assert_se(sigemptyset(&set) == 0);
+ assert_se(sigaddset(&set, SIGHUP) == 0);
+ assert_se(sigaddset(&set, SIGCHLD) == 0);
+ assert_se(sigemptyset(&sig.sa_mask) == 0);
+ assert_se(sigprocmask(SIG_UNBLOCK, &set, &oldset) == 0);
+ assert_se(sigaction(SIGCHLD, &sig, &oldsig) == 0);
+ sig.sa_handler = SIG_DFL;
+ assert_se(sigaction(SIGHUP, &sig, NULL) == 0);
+ assert_se(sigaction(SIGHUP, &sig, NULL) >= 0);
+
+ for (id = 0; id < num; id++) {
+ consoles[id].pid = fork();
+
+ if (consoles[id].pid < 0) {
+ log_error("Failed to query password: %m");
+ exit(EXIT_FAILURE);
+ }
+ if (consoles[id].pid < 0)
+ return log_error_errno(errno, "Failed to query password: %m");
+
+ if (consoles[id].pid == 0) {
+ if (prctl(PR_SET_PDEATHSIG, SIGHUP) < 0)
+ _exit(EXIT_FAILURE);
+ 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);
+ return wfa_child(consoles, id);
+ 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 ((job = wait(&status)) != 0) {
+ if (job < 0) {
+ if (errno != EINTR)
+ 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++) {
+ if (consoles[id].pid == job || kill(consoles[id].pid, 0) < 0) {
+ *usemask &= ~(1 << id); /* shared memory area */
+ continue;
+ }
+ if (*usemask & (1 << id)) /* shared memory area */
+ 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);
+ usleep(50000);
+
+ 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) && ret == 0)
+ ret = WEXITSTATUS(status);
+
+ if (WIFEXITED(status.si_status) && ret == 0)
+ ret = WEXITSTATUS(status.si_status);
+ }
+
+ free_consoles(consoles, num);
+ exit(ret != 0 ? EXIT_FAILURE : EXIT_SUCCESS); /* parent */
+
+ return ret;
+}
+
int main(int argc, char *argv[]) {
- int r;
+ int r, id = 0;
int r;
log_set_target(LOG_TARGET_AUTO);
log_parse_environment();
@@ -627,11 +791,27 @@ int main(int argc, char *argv[]) {
@@ -628,15 +859,28 @@ int main(int argc, char *argv[]) {
if (r <= 0)
goto finish;
+ /*
+ * Use this shared memory area to be able to synchronize the
+ * workers asking for password with the main process.
+ * This allows to continue if one of the consoles had been
+ * used as afterwards the remaining asking processes will
+ * be terminated. The wait_for_terminate() does not help
+ * for this use case.
+ */
+ usemask = mmap(NULL, sizeof(*usemask), PROT_READ | PROT_WRITE,
+ MAP_ANONYMOUS | MAP_SHARED, -1, 0);
+ assert_se(usemask != NULL);
+
if (arg_console) {
- if (arg_console) {
- setsid();
- release_terminal();
+ if (!arg_plymouth &&
+ !IN_SET(arg_action, ACTION_WALL, ACTION_LIST)) {
+ id = wait_for_answer();
+ } else {
+ 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
@@ -640,6 +820,7 @@ int main(int argc, char *argv[]) {
- 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");
+ *usemask &= ~(1 << id); /* shared memory area */
finish:
return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
}
--
2.2.0