mirror of
https://github.com/systemd/systemd-stable.git
synced 2024-12-22 13:33:56 +03:00
ask-password: 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(). Use SIGTERM instead of SIGHUP to get unresponsive childs down. Port the collect_consoles() function forward to a pulbic and strv based function "get_kernel_consoles()" in terminal-util.c and use this in tty-ask-password-agent.c.
This commit is contained in:
parent
2099b3e993
commit
6af621248f
@ -50,6 +50,7 @@
|
||||
#include "socket-util.h"
|
||||
#include "stat-util.h"
|
||||
#include "string-util.h"
|
||||
#include "strv.h"
|
||||
#include "terminal-util.h"
|
||||
#include "time-util.h"
|
||||
#include "util.h"
|
||||
@ -708,6 +709,64 @@ char *resolve_dev_console(char **active) {
|
||||
return tty;
|
||||
}
|
||||
|
||||
int get_kernel_consoles(char ***consoles) {
|
||||
_cleanup_strv_free_ char **con = NULL;
|
||||
_cleanup_free_ char *line = NULL;
|
||||
const char *active;
|
||||
int r;
|
||||
|
||||
assert(consoles);
|
||||
|
||||
r = read_one_line_file("/sys/class/tty/console/active", &line);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
active = line;
|
||||
for (;;) {
|
||||
_cleanup_free_ char *tty = NULL;
|
||||
char *path;
|
||||
|
||||
r = extract_first_word(&active, &tty, NULL, 0);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0)
|
||||
break;
|
||||
|
||||
if (streq(tty, "tty0")) {
|
||||
tty = mfree(tty);
|
||||
r = read_one_line_file("/sys/class/tty/tty0/active", &tty);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
path = strappend("/dev/", tty);
|
||||
if (!path)
|
||||
return -ENOMEM;
|
||||
|
||||
if (access(path, F_OK) < 0) {
|
||||
log_debug_errno(errno, "Console device %s is not accessible, skipping: %m", path);
|
||||
free(path);
|
||||
continue;
|
||||
}
|
||||
|
||||
r = strv_consume(&con, path);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
if (strv_isempty(con)) {
|
||||
log_debug("No devices found for system console");
|
||||
|
||||
r = strv_extend(&con, "/dev/console");
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
*consoles = con;
|
||||
con = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool tty_is_vc_resolve(const char *tty) {
|
||||
_cleanup_free_ char *active = NULL;
|
||||
|
||||
|
@ -62,6 +62,7 @@ int ask_string(char **ret, const char *text, ...) _printf_(2, 3);
|
||||
int vt_disallocate(const char *name);
|
||||
|
||||
char *resolve_dev_console(char **active);
|
||||
int get_kernel_consoles(char ***consoles);
|
||||
bool tty_is_vc(const char *tty);
|
||||
bool tty_is_vc_resolve(const char *tty);
|
||||
bool tty_is_console(const char *tty) _pure_;
|
||||
|
@ -2,6 +2,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,12 +22,15 @@
|
||||
#include <fcntl.h>
|
||||
#include <getopt.h>
|
||||
#include <poll.h>
|
||||
#include <signal.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
#include <sys/inotify.h>
|
||||
#include <sys/prctl.h>
|
||||
#include <sys/signalfd.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/wait.h>
|
||||
#include <sys/un.h>
|
||||
#include <unistd.h>
|
||||
|
||||
@ -35,8 +39,12 @@
|
||||
#include "conf-parser.h"
|
||||
#include "def.h"
|
||||
#include "dirent-util.h"
|
||||
#include "exit-status.h"
|
||||
#include "fd-util.h"
|
||||
#include "fileio.h"
|
||||
#include "hashmap.h"
|
||||
#include "io-util.h"
|
||||
#include "macro.h"
|
||||
#include "mkdir.h"
|
||||
#include "path-util.h"
|
||||
#include "process-util.h"
|
||||
@ -57,6 +65,7 @@ static enum {
|
||||
|
||||
static bool arg_plymouth = false;
|
||||
static bool arg_console = false;
|
||||
static const char *arg_device = NULL;
|
||||
|
||||
static int ask_password_plymouth(
|
||||
const char *message,
|
||||
@ -354,7 +363,9 @@ static int parse_password(const char *filename, char **wall) {
|
||||
int tty_fd = -1;
|
||||
|
||||
if (arg_console) {
|
||||
tty_fd = acquire_terminal("/dev/console", false, false, false, USEC_INFINITY);
|
||||
const char *con = arg_device ? arg_device : "/dev/console";
|
||||
|
||||
tty_fd = acquire_terminal(con, false, false, false, USEC_INFINITY);
|
||||
if (tty_fd < 0)
|
||||
return log_error_errno(tty_fd, "Failed to acquire /dev/console: %m");
|
||||
|
||||
@ -586,14 +597,14 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
};
|
||||
|
||||
static const struct option options[] = {
|
||||
{ "help", no_argument, NULL, 'h' },
|
||||
{ "version", no_argument, NULL, ARG_VERSION },
|
||||
{ "list", no_argument, NULL, ARG_LIST },
|
||||
{ "query", no_argument, NULL, ARG_QUERY },
|
||||
{ "watch", no_argument, NULL, ARG_WATCH },
|
||||
{ "wall", no_argument, NULL, ARG_WALL },
|
||||
{ "plymouth", no_argument, NULL, ARG_PLYMOUTH },
|
||||
{ "console", no_argument, NULL, ARG_CONSOLE },
|
||||
{ "help", no_argument, NULL, 'h' },
|
||||
{ "version", no_argument, NULL, ARG_VERSION },
|
||||
{ "list", no_argument, NULL, ARG_LIST },
|
||||
{ "query", no_argument, NULL, ARG_QUERY },
|
||||
{ "watch", no_argument, NULL, ARG_WATCH },
|
||||
{ "wall", no_argument, NULL, ARG_WALL },
|
||||
{ "plymouth", no_argument, NULL, ARG_PLYMOUTH },
|
||||
{ "console", optional_argument, NULL, ARG_CONSOLE },
|
||||
{}
|
||||
};
|
||||
|
||||
@ -635,6 +646,15 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
|
||||
case ARG_CONSOLE:
|
||||
arg_console = true;
|
||||
if (optarg) {
|
||||
|
||||
if (isempty(optarg)) {
|
||||
log_error("Empty console device path is not allowed.");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
arg_device = optarg;
|
||||
}
|
||||
break;
|
||||
|
||||
case '?':
|
||||
@ -649,9 +669,171 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (arg_plymouth || arg_console) {
|
||||
|
||||
if (!IN_SET(arg_action, ACTION_QUERY, ACTION_WATCH)) {
|
||||
log_error("Options --query and --watch conflict.");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (arg_plymouth && arg_console) {
|
||||
log_error("Options --plymouth and --console conflict.");
|
||||
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 is 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_this_console(const char *tty, pid_t *pid, int argc, char *argv[]) {
|
||||
struct sigaction sig = {
|
||||
.sa_handler = nop_signal_handler,
|
||||
.sa_flags = SA_NOCLDSTOP | SA_RESTART,
|
||||
};
|
||||
|
||||
assert_se(sigprocmask_many(SIG_UNBLOCK, NULL, SIGHUP, SIGCHLD, -1) >= 0);
|
||||
|
||||
assert_se(sigemptyset(&sig.sa_mask) >= 0);
|
||||
assert_se(sigaction(SIGCHLD, &sig, NULL) >= 0);
|
||||
|
||||
sig.sa_handler = SIG_DFL;
|
||||
assert_se(sigaction(SIGHUP, &sig, NULL) >= 0);
|
||||
|
||||
*pid = fork();
|
||||
if (*pid < 0)
|
||||
return log_error_errno(errno, "Failed to fork process: %m");
|
||||
|
||||
if (*pid == 0) {
|
||||
int ac;
|
||||
|
||||
assert_se(prctl(PR_SET_PDEATHSIG, SIGHUP) >= 0);
|
||||
|
||||
reset_signal_mask();
|
||||
reset_all_signal_handlers();
|
||||
|
||||
for (ac = 0; ac < argc; ac++) {
|
||||
if (streq(argv[ac], "--console")) {
|
||||
argv[ac] = strjoina("--console=", tty, NULL);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
assert(ac < argc);
|
||||
|
||||
execv(SYSTEMD_TTY_ASK_PASSWORD_AGENT_BINARY_PATH, argv);
|
||||
_exit(EXIT_FAILURE);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void terminate_agents(Set *pids) {
|
||||
struct timespec ts;
|
||||
siginfo_t status = {};
|
||||
sigset_t set;
|
||||
Iterator i;
|
||||
void *p;
|
||||
int r, signum;
|
||||
|
||||
/*
|
||||
* Request termination of the remaining processes as those
|
||||
* are not required anymore.
|
||||
*/
|
||||
SET_FOREACH(p, pids, i)
|
||||
(void) kill(PTR_TO_PID(p), SIGTERM);
|
||||
|
||||
/*
|
||||
* Collect the processes which have go away.
|
||||
*/
|
||||
assert_se(sigemptyset(&set) >= 0);
|
||||
assert_se(sigaddset(&set, SIGCHLD) >= 0);
|
||||
timespec_store(&ts, 50 * USEC_PER_MSEC);
|
||||
|
||||
while (!set_isempty(pids)) {
|
||||
|
||||
zero(status);
|
||||
r = waitid(P_ALL, 0, &status, WEXITED|WNOHANG);
|
||||
if (r < 0 && errno == EINTR)
|
||||
continue;
|
||||
|
||||
if (r == 0 && status.si_pid > 0) {
|
||||
set_remove(pids, PID_TO_PTR(status.si_pid));
|
||||
continue;
|
||||
}
|
||||
|
||||
signum = sigtimedwait(&set, NULL, &ts);
|
||||
if (signum < 0) {
|
||||
if (errno != EAGAIN)
|
||||
log_error_errno(errno, "sigtimedwait() failed: %m");
|
||||
break;
|
||||
}
|
||||
assert(signum == SIGCHLD);
|
||||
}
|
||||
|
||||
/*
|
||||
* Kill hanging processes.
|
||||
*/
|
||||
SET_FOREACH(p, pids, i) {
|
||||
log_warning("Failed to terminate child %d, killing it", PTR_TO_PID(p));
|
||||
(void) kill(PTR_TO_PID(p), SIGKILL);
|
||||
}
|
||||
}
|
||||
|
||||
static int ask_on_consoles(int argc, char *argv[]) {
|
||||
_cleanup_set_free_ Set *pids = NULL;
|
||||
_cleanup_strv_free_ char **consoles = NULL;
|
||||
siginfo_t status = {};
|
||||
char **tty;
|
||||
pid_t pid;
|
||||
int r;
|
||||
|
||||
r = get_kernel_consoles(&consoles);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to determine devices of /dev/console: %m");
|
||||
|
||||
pids = set_new(NULL);
|
||||
if (!pids)
|
||||
return log_oom();
|
||||
|
||||
/* Start an agent on each console. */
|
||||
STRV_FOREACH(tty, consoles) {
|
||||
r = ask_on_this_console(*tty, &pid, argc, argv);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (set_put(pids, PID_TO_PTR(pid)) < 0)
|
||||
return log_oom();
|
||||
}
|
||||
|
||||
/* Wait for an agent to exit. */
|
||||
for (;;) {
|
||||
zero(status);
|
||||
|
||||
if (waitid(P_ALL, 0, &status, WEXITED) < 0) {
|
||||
if (errno == EINTR)
|
||||
continue;
|
||||
|
||||
return log_error_errno(errno, "waitid() failed: %m");
|
||||
}
|
||||
|
||||
set_remove(pids, PID_TO_PTR(status.si_pid));
|
||||
break;
|
||||
}
|
||||
|
||||
if (!is_clean_exit(status.si_code, status.si_status, NULL))
|
||||
log_error("Password agent failed with: %d", status.si_status);
|
||||
|
||||
terminate_agents(pids);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
int r;
|
||||
|
||||
@ -665,15 +847,28 @@ 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 separate process.
|
||||
*/
|
||||
r = ask_on_consoles(argc, argv);
|
||||
else {
|
||||
|
||||
if (IN_SET(arg_action, ACTION_WATCH, ACTION_WALL))
|
||||
r = watch_passwords();
|
||||
else
|
||||
r = show_passwords();
|
||||
if (arg_device) {
|
||||
/*
|
||||
* Later on, a controlling terminal 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();
|
||||
}
|
||||
|
||||
finish:
|
||||
return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
|
||||
|
Loading…
Reference in New Issue
Block a user