Merge pull request #8184 from poettering/color-ask-pw

Trivial merge conflict resolved locally.
This commit is contained in:
Zbigniew Jędrzejewski-Szmek
2018-02-15 17:14:59 +01:00
13 changed files with 425 additions and 236 deletions

View File

@@ -67,6 +67,7 @@ BuildPackages=
m4
meson
pam-devel
pcre2-devel
pkgconfig
python3-devel
python3-lxml

12
TODO
View File

@@ -32,6 +32,18 @@ Features:
* teach tmpfiles.d q/Q logic something sensible in the context of XFS/ext4
project quota
* introduce DefaultSlice= or so in system.conf that allows changing where we
place our units by default, i.e. change system.slice to something
else. Similar, ManagerSlice= should exist so that PID1's own scope unit could
be moved somewhere else too. Finally machined and logind should get similar
options so that it is possible to move user session scopes and machines to a
different slice too by default. Usecase: people who want to put resources on
the entire system, with the exception of one specific service. See:
https://lists.freedesktop.org/archives/systemd-devel/2018-February/040369.html
* check what setting the login shell to /bin/false vs. /sbin/nologin means and
do the right thing in get_user_creds_clean() with it.
* maybe rework get_user_creds() to query the user database if $SHELL is used
for root, but only then.

View File

@@ -850,17 +850,33 @@ int kill_and_sigcont(pid_t pid, int sig) {
return r;
}
int getenv_for_pid(pid_t pid, const char *field, char **_value) {
int getenv_for_pid(pid_t pid, const char *field, char **ret) {
_cleanup_fclose_ FILE *f = NULL;
char *value = NULL;
int r;
bool done = false;
size_t l;
const char *path;
size_t l;
assert(pid >= 0);
assert(field);
assert(_value);
assert(ret);
if (pid == 0 || pid == getpid_cached()) {
const char *e;
e = getenv(field);
if (!e) {
*ret = NULL;
return 0;
}
value = strdup(e);
if (!value)
return -ENOMEM;
*ret = value;
return 1;
}
path = procfs_file_alloca(pid, "environ");
@@ -868,13 +884,13 @@ int getenv_for_pid(pid_t pid, const char *field, char **_value) {
if (!f) {
if (errno == ENOENT)
return -ESRCH;
return -errno;
}
(void) __fsetlocking(f, FSETLOCKING_BYCALLER);
l = strlen(field);
r = 0;
do {
char line[LINE_MAX];
@@ -899,14 +915,14 @@ int getenv_for_pid(pid_t pid, const char *field, char **_value) {
if (!value)
return -ENOMEM;
r = 1;
break;
*ret = value;
return 1;
}
} while (!done);
*_value = value;
return r;
*ret = NULL;
return 0;
}
bool pid_is_unwaited(pid_t pid) {

File diff suppressed because it is too large Load Diff

View File

@@ -52,7 +52,23 @@ int reset_terminal_fd(int fd, bool switch_to_text);
int reset_terminal(const char *name);
int open_terminal(const char *name, int mode);
int acquire_terminal(const char *name, bool fail, bool force, bool ignore_tiocstty_eperm, usec_t timeout);
/* Flags for tweaking the way we become the controlling process of a terminal. */
typedef enum AcquireTerminalFlags {
/* Try to become the controlling process of the TTY. If we can't return -EPERM. */
ACQUIRE_TERMINAL_TRY = 0,
/* Tell the kernel to forcibly make us the controlling process of the TTY. Returns -EPERM if the kernel doesn't allow that. */
ACQUIRE_TERMINAL_FORCE = 1,
/* If we can't become the controlling process of the TTY right-away, then wait until we can. */
ACQUIRE_TERMINAL_WAIT = 2,
/* Pick one of the above, and then OR this flag in, in order to request permissive behaviour, if we can't become controlling process then don't mind */
ACQUIRE_TERMINAL_PERMISSIVE = 4,
} AcquireTerminalFlags;
int acquire_terminal(const char *name, AcquireTerminalFlags flags, usec_t timeout);
int release_terminal(void);
int terminal_vhangup_fd(int fd);
@@ -66,8 +82,8 @@ 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);
int resolve_dev_console(char **ret);
int get_kernel_consoles(char ***ret);
bool tty_is_vc(const char *tty);
bool tty_is_vc_resolve(const char *tty);
bool tty_is_console(const char *tty) _pure_;
@@ -82,12 +98,15 @@ int fd_columns(int fd);
unsigned columns(void);
int fd_lines(int fd);
unsigned lines(void);
void columns_lines_cache_reset(int _unused_ signum);
void reset_terminal_feature_caches(void);
bool on_tty(void);
bool terminal_is_dumb(void);
bool colors_enabled(void);
bool underline_enabled(void);
bool dev_console_colors_enabled(void);
#define DEFINE_ANSI_FUNC(name, NAME) \
static inline const char *ansi_##name(void) { \

View File

@@ -408,3 +408,22 @@ int utf8_encoded_valid_unichar(const char *str) {
return len;
}
size_t utf8_n_codepoints(const char *str) {
size_t n = 0;
/* Returns the number of UTF-8 codepoints in this string, or (size_t) -1 if the string is not valid UTF-8. */
while (*str != 0) {
int k;
k = utf8_encoded_valid_unichar(str);
if (k < 0)
return (size_t) -1;
str += k;
n++;
}
return n;
}

View File

@@ -59,3 +59,5 @@ static inline bool utf16_is_trailing_surrogate(char16_t c) {
static inline char32_t utf16_surrogate_pair_to_unichar(char16_t lead, char16_t trail) {
return ((lead - 0xd800) << 10) + (trail - 0xdc00) + 0x10000;
}
size_t utf8_n_codepoints(const char *str);

View File

@@ -509,9 +509,9 @@ static int setup_input(
int fd;
fd = acquire_terminal(exec_context_tty_path(context),
i == EXEC_INPUT_TTY_FAIL,
i == EXEC_INPUT_TTY_FORCE,
false,
i == EXEC_INPUT_TTY_FAIL ? ACQUIRE_TERMINAL_TRY :
i == EXEC_INPUT_TTY_FORCE ? ACQUIRE_TERMINAL_FORCE :
ACQUIRE_TERMINAL_WAIT,
USEC_INFINITY);
if (fd < 0)
return fd;
@@ -753,7 +753,7 @@ static int setup_confirm_stdio(const char *vc, int *_saved_stdin, int *_saved_st
if (saved_stdout < 0)
return -errno;
fd = acquire_terminal(vc, false, false, false, DEFAULT_CONFIRM_USEC);
fd = acquire_terminal(vc, ACQUIRE_TERMINAL_WAIT, DEFAULT_CONFIRM_USEC);
if (fd < 0)
return fd;
@@ -3871,8 +3871,7 @@ static int exec_context_load_environment(const Unit *unit, const ExecContext *c,
}
static bool tty_may_match_dev_console(const char *tty) {
_cleanup_free_ char *active = NULL;
char *console;
_cleanup_free_ char *resolved = NULL;
if (!tty)
return true;
@@ -3883,13 +3882,11 @@ static bool tty_may_match_dev_console(const char *tty) {
if (streq(tty, "console"))
return true;
console = resolve_dev_console(&active);
/* if we could not resolve, assume it may */
if (!console)
return true;
if (resolve_dev_console(&resolved) < 0)
return true; /* if we could not resolve, assume it may */
/* "tty0" means the active VC, so it may be the same sometimes */
return streq(console, tty) || (streq(console, "tty0") && tty_is_vc(tty));
return streq(resolved, tty) || (streq(resolved, "tty0") && tty_is_vc(tty));
}
bool exec_context_may_touch_console(const ExecContext *ec) {

View File

@@ -558,7 +558,7 @@ static int prompt_root_password(void) {
for (;;) {
_cleanup_string_free_erase_ char *a = NULL, *b = NULL;
r = ask_password_tty(msg1, NULL, 0, 0, NULL, &a);
r = ask_password_tty(-1, msg1, NULL, 0, 0, NULL, &a);
if (r < 0)
return log_error_errno(r, "Failed to query root password: %m");
@@ -567,7 +567,7 @@ static int prompt_root_password(void) {
break;
}
r = ask_password_tty(msg2, NULL, 0, 0, NULL, &b);
r = ask_password_tty(-1, msg2, NULL, 0, 0, NULL, &b);
if (r < 0)
return log_error_errno(r, "Failed to query root password: %m");

View File

@@ -201,7 +201,29 @@ static void backspace_chars(int ttyfd, size_t p) {
}
}
static void backspace_string(int ttyfd, const char *str) {
size_t m;
assert(str);
if (ttyfd < 0)
return;
/* Backspaces back for enough characters to entirely undo printing of the specified string. */
m = utf8_n_codepoints(str);
if (m == (size_t) -1)
m = strlen(str); /* Not a valid UTF-8 string? If so, let's backspace the number of bytes output. Most
* likely this happened because we are not in an UTF-8 locale, and in that case that
* is the correct thing to do. And even if it's not, terminals tend to stop
* backspacing at the leftmost column, hence backspacing too much should be mostly
* OK. */
backspace_chars(ttyfd, m);
}
int ask_password_tty(
int ttyfd,
const char *message,
const char *keyname,
usec_t until,
@@ -209,19 +231,20 @@ int ask_password_tty(
const char *flag_file,
char **ret) {
struct termios old_termios, new_termios;
char passphrase[LINE_MAX + 1] = {}, *x;
size_t p = 0, codepoint = 0;
int r;
_cleanup_close_ int ttyfd = -1, notify = -1;
struct pollfd pollfd[2];
bool reset_tty = false;
bool dirty = false;
enum {
POLL_TTY,
POLL_INOTIFY
POLL_INOTIFY,
_POLL_MAX,
};
bool reset_tty = false, dirty = false, use_color = false;
_cleanup_close_ int cttyfd = -1, notify = -1;
struct termios old_termios, new_termios;
char passphrase[LINE_MAX + 1] = {}, *x;
struct pollfd pollfd[_POLL_MAX];
size_t p = 0, codepoint = 0;
int r;
assert(ret);
if (flags & ASK_PASSWORD_NO_TTY)
@@ -232,33 +255,34 @@ int ask_password_tty(
if (flag_file) {
notify = inotify_init1(IN_CLOEXEC|IN_NONBLOCK);
if (notify < 0) {
r = -errno;
goto finish;
}
if (notify < 0)
return -errno;
if (inotify_add_watch(notify, flag_file, IN_ATTRIB /* for the link count */) < 0) {
r = -errno;
goto finish;
}
if (inotify_add_watch(notify, flag_file, IN_ATTRIB /* for the link count */) < 0)
return -errno;
}
ttyfd = open("/dev/tty", O_RDWR|O_NOCTTY|O_CLOEXEC);
/* If the caller didn't specify a TTY, then use the controlling tty, if we can. */
if (ttyfd < 0)
ttyfd = cttyfd = open("/dev/tty", O_RDWR|O_NOCTTY|O_CLOEXEC);
if (ttyfd >= 0) {
if (tcgetattr(ttyfd, &old_termios) < 0)
return -errno;
if (tcgetattr(ttyfd, &old_termios) < 0) {
r = -errno;
goto finish;
}
if (flags & ASK_PASSWORD_CONSOLE_COLOR)
use_color = dev_console_colors_enabled();
else
use_color = colors_enabled();
if (colors_enabled())
loop_write(ttyfd, ANSI_HIGHLIGHT,
STRLEN(ANSI_HIGHLIGHT), false);
loop_write(ttyfd, message, strlen(message), false);
loop_write(ttyfd, " ", 1, false);
if (colors_enabled())
loop_write(ttyfd, ANSI_NORMAL, STRLEN(ANSI_NORMAL),
false);
if (use_color)
(void) loop_write(ttyfd, ANSI_HIGHLIGHT, STRLEN(ANSI_HIGHLIGHT), false);
(void) loop_write(ttyfd, message, strlen(message), false);
(void) loop_write(ttyfd, " ", 1, false);
if (use_color)
(void) loop_write(ttyfd, ANSI_NORMAL, STRLEN(ANSI_NORMAL), false);
new_termios = old_termios;
new_termios.c_lflag &= ~(ICANON|ECHO);
@@ -273,16 +297,19 @@ int ask_password_tty(
reset_tty = true;
}
zero(pollfd);
pollfd[POLL_TTY].fd = ttyfd >= 0 ? ttyfd : STDIN_FILENO;
pollfd[POLL_TTY].events = POLLIN;
pollfd[POLL_INOTIFY].fd = notify;
pollfd[POLL_INOTIFY].events = POLLIN;
pollfd[POLL_TTY] = (struct pollfd) {
.fd = ttyfd >= 0 ? ttyfd : STDIN_FILENO,
.events = POLLIN,
};
pollfd[POLL_INOTIFY] = (struct pollfd) {
.fd = notify,
.events = POLLIN,
};
for (;;) {
char c;
int sleep_for = -1, k;
ssize_t n;
char c;
if (until > 0) {
usec_t y;
@@ -294,7 +321,7 @@ int ask_password_tty(
goto finish;
}
sleep_for = (int) ((until - y) / USEC_PER_MSEC);
sleep_for = (int) DIV_ROUND_UP(until - y, USEC_PER_MSEC);
}
if (flag_file)
@@ -329,72 +356,99 @@ int ask_password_tty(
r = -errno;
goto finish;
} else if (n == 0)
}
/* We treat EOF, newline and NUL byte all as valid end markers */
if (n == 0 || c == '\n' || c == 0)
break;
if (c == '\n')
break;
else if (c == 21) { /* C-u */
if (c == 21) { /* C-u */
if (!(flags & ASK_PASSWORD_SILENT))
backspace_chars(ttyfd, p);
p = 0;
backspace_string(ttyfd, passphrase);
explicit_bzero(passphrase, sizeof(passphrase));
p = codepoint = 0;
} else if (IN_SET(c, '\b', 127)) {
if (p > 0) {
size_t q;
if (!(flags & ASK_PASSWORD_SILENT))
backspace_chars(ttyfd, 1);
p--;
/* Remove a full UTF-8 codepoint from the end. For that, figure out where the last one
* begins */
q = 0;
for (;;) {
size_t z;
z = utf8_encoded_valid_unichar(passphrase + q);
if (z == 0) {
q = (size_t) -1; /* Invalid UTF8! */
break;
}
if (q + z >= p) /* This one brings us over the edge */
break;
q += z;
}
p = codepoint = q == (size_t) -1 ? p - 1 : q;
explicit_bzero(passphrase + p, sizeof(passphrase) - p);
} else if (!dirty && !(flags & ASK_PASSWORD_SILENT)) {
flags |= ASK_PASSWORD_SILENT;
/* There are two ways to enter silent
* mode. Either by pressing backspace
* as first key (and only as first
* key), or ... */
/* There are two ways to enter silent mode. Either by pressing backspace as first key
* (and only as first key), or ... */
if (ttyfd >= 0)
loop_write(ttyfd, "(no echo) ", 10, false);
(void) loop_write(ttyfd, "(no echo) ", 10, false);
} else if (ttyfd >= 0)
loop_write(ttyfd, "\a", 1, false);
(void) loop_write(ttyfd, "\a", 1, false);
} else if (c == '\t' && !(flags & ASK_PASSWORD_SILENT)) {
backspace_chars(ttyfd, p);
backspace_string(ttyfd, passphrase);
flags |= ASK_PASSWORD_SILENT;
/* ... or by pressing TAB at any time. */
if (ttyfd >= 0)
loop_write(ttyfd, "(no echo) ", 10, false);
} else {
if (p >= sizeof(passphrase)-1) {
loop_write(ttyfd, "\a", 1, false);
continue;
}
(void) loop_write(ttyfd, "(no echo) ", 10, false);
} else if (p >= sizeof(passphrase)-1) {
/* Reached the size limit */
if (ttyfd >= 0)
(void) loop_write(ttyfd, "\a", 1, false);
} else {
passphrase[p++] = c;
if (!(flags & ASK_PASSWORD_SILENT) && ttyfd >= 0) {
/* Check if we got a complete UTF-8 character now. If so, let's output one '*'. */
n = utf8_encoded_valid_unichar(passphrase + codepoint);
if (n >= 0) {
codepoint = p;
loop_write(ttyfd, (flags & ASK_PASSWORD_ECHO) ? &c : "*", 1, false);
(void) loop_write(ttyfd, (flags & ASK_PASSWORD_ECHO) ? &c : "*", 1, false);
}
}
dirty = true;
}
/* Let's forget this char, just to not keep needlessly copies of key material around */
c = 'x';
}
x = strndup(passphrase, p);
explicit_bzero(passphrase, p);
explicit_bzero(passphrase, sizeof(passphrase));
if (!x) {
r = -ENOMEM;
goto finish;
@@ -408,8 +462,8 @@ int ask_password_tty(
finish:
if (ttyfd >= 0 && reset_tty) {
loop_write(ttyfd, "\n", 1, false);
tcsetattr(ttyfd, TCSADRAIN, &old_termios);
(void) loop_write(ttyfd, "\n", 1, false);
(void) tcsetattr(ttyfd, TCSADRAIN, &old_termios);
}
return r;
@@ -715,7 +769,7 @@ int ask_password_auto(
if (!(flags & ASK_PASSWORD_NO_TTY) && isatty(STDIN_FILENO)) {
char *s = NULL, **l = NULL;
r = ask_password_tty(message, keyname, until, flags, NULL, &s);
r = ask_password_tty(-1, message, keyname, until, flags, NULL, &s);
if (r < 0)
return r;

View File

@@ -25,15 +25,16 @@
#include "time-util.h"
typedef enum AskPasswordFlags {
ASK_PASSWORD_ACCEPT_CACHED = 1,
ASK_PASSWORD_PUSH_CACHE = 2,
ASK_PASSWORD_ECHO = 4, /* show the password literally while reading, instead of "*" */
ASK_PASSWORD_SILENT = 8, /* do no show any password at all while reading */
ASK_PASSWORD_NO_TTY = 16,
ASK_PASSWORD_NO_AGENT = 32,
ASK_PASSWORD_ACCEPT_CACHED = 1U << 0,
ASK_PASSWORD_PUSH_CACHE = 1U << 1,
ASK_PASSWORD_ECHO = 1U << 2, /* show the password literally while reading, instead of "*" */
ASK_PASSWORD_SILENT = 1U << 3, /* do no show any password at all while reading */
ASK_PASSWORD_NO_TTY = 1U << 4,
ASK_PASSWORD_NO_AGENT = 1U << 5,
ASK_PASSWORD_CONSOLE_COLOR = 1U << 6, /* Use color if /dev/console points to a console that supports color */
} AskPasswordFlags;
int ask_password_tty(const char *message, const char *keyname, usec_t until, AskPasswordFlags flags, const char *flag_file, char **ret);
int ask_password_tty(int tty_fd, const char *message, const char *keyname, usec_t until, AskPasswordFlags flags, const char *flag_file, char **ret);
int ask_password_agent(const char *message, const char *icon, const char *id, const char *keyname, usec_t until, AskPasswordFlags flag, char ***ret);
int ask_password_keyring(const char *keyname, AskPasswordFlags flags, char ***ret);
int ask_password_auto(const char *message, const char *icon, const char *id, const char *keyname, usec_t until, AskPasswordFlags flag, char ***ret);

View File

@@ -26,7 +26,7 @@ static void ask_password(void) {
int r;
_cleanup_free_ char *ret;
r = ask_password_tty("hello?", "da key", 0, 0, NULL, &ret);
r = ask_password_tty(-1, "hello?", "da key", 0, 0, NULL, &ret);
assert(r >= 0);
log_info("Got %s", ret);

View File

@@ -254,6 +254,7 @@ static int send_passwords(const char *socket_name, char **passwords) {
union sockaddr_union sa = { .un.sun_family = AF_UNIX };
size_t packet_length = 1;
char **p, *d;
ssize_t n;
int r;
assert(socket_name);
@@ -279,9 +280,13 @@ static int send_passwords(const char *socket_name, char **passwords) {
strncpy(sa.un.sun_path, socket_name, sizeof(sa.un.sun_path));
r = sendto(socket_fd, packet, packet_length, MSG_NOSIGNAL, &sa.sa, SOCKADDR_UN_LEN(sa.un));
if (r < 0)
n = sendto(socket_fd, packet, packet_length, MSG_NOSIGNAL, &sa.sa, SOCKADDR_UN_LEN(sa.un));
if (n < 0) {
r = log_debug_errno(errno, "sendto(): %m");
goto finish;
}
r = (int) n;
finish:
explicit_bzero(packet, packet_length);
@@ -363,18 +368,21 @@ static int parse_password(const char *filename, char **wall) {
int tty_fd = -1;
if (arg_console) {
const char *con = arg_device ? arg_device : "/dev/console";
const char *con = arg_device ?: "/dev/console";
tty_fd = acquire_terminal(con, false, false, false, USEC_INFINITY);
tty_fd = acquire_terminal(con, ACQUIRE_TERMINAL_WAIT, USEC_INFINITY);
if (tty_fd < 0)
return log_error_errno(tty_fd, "Failed to acquire /dev/console: %m");
return log_error_errno(tty_fd, "Failed to acquire %s: %m", con);
r = reset_terminal_fd(tty_fd, true);
if (r < 0)
log_warning_errno(r, "Failed to reset terminal, ignoring: %m");
}
r = ask_password_tty(message, NULL, not_after, echo ? ASK_PASSWORD_ECHO : 0, filename, &password);
r = ask_password_tty(tty_fd, message, NULL, not_after,
(echo ? ASK_PASSWORD_ECHO : 0) |
(arg_console ? ASK_PASSWORD_CONSOLE_COLOR : 0),
filename, &password);
if (arg_console) {
tty_fd = safe_close(tty_fd);