mirror of
https://github.com/systemd/systemd.git
synced 2024-12-23 21:35:11 +03:00
Merge pull request #3247 from fbuihuu/ask-passowrd-on-all-consoles
ask-password: ask for passphrases not only on the first console
This commit is contained in:
commit
bbed3b8efd
@ -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