mirror of
https://github.com/systemd/systemd.git
synced 2024-11-23 18:23:32 +08:00
Merge pull request #1484 from poettering/ask-pass-kernel-keyring
cache harddisk passwords in the kernel keyring
This commit is contained in:
commit
79bec997c9
@ -332,9 +332,13 @@
|
||||
- Avoid leaving long-running child processes around, i.e. fork()s that
|
||||
are not followed quickly by an execv() in the child. Resource
|
||||
management is unclear in this case, and memory CoW will result in
|
||||
penalties in the parent much much later on.
|
||||
unexpected penalties in the parent much much later on.
|
||||
|
||||
- Don't block execution for arbitrary amounts of time using usleep()
|
||||
or a similar call, unless you really know what you do. Just "giving
|
||||
something some time", or so is a lazy excuse. Always wait for the
|
||||
proper event, instead of doing time-based poll loops.
|
||||
|
||||
- To determine the length of a constant string "foo", don't bother
|
||||
with sizeof("foo")-1, please use strlen("foo") directly. gcc knows
|
||||
strlen() anyway and turns it into a constant expression if possible.
|
||||
|
@ -298,7 +298,7 @@ AC_SUBST(CAP_LIBS)
|
||||
|
||||
AC_CHECK_FUNCS([memfd_create])
|
||||
AC_CHECK_FUNCS([__secure_getenv secure_getenv])
|
||||
AC_CHECK_DECLS([gettid, pivot_root, name_to_handle_at, setns, getrandom, renameat2, kcmp, LO_FLAGS_PARTSCAN],
|
||||
AC_CHECK_DECLS([gettid, pivot_root, name_to_handle_at, setns, getrandom, renameat2, kcmp, keyctl, key_serial_t, LO_FLAGS_PARTSCAN],
|
||||
[], [], [[
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
@ -1,4 +1,4 @@
|
||||
<?xml version='1.0'?> <!--*-nxml-*-->
|
||||
<?xml version='1.0'?> <!--*- Mode: nxml; nxml-child-indent: 2; indent-tabs-mode: nil -*-->
|
||||
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
|
||||
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
|
||||
|
||||
@ -72,17 +72,28 @@
|
||||
plugged in or at boot, entering an SSL certificate passphrase for
|
||||
web and VPN servers.</para>
|
||||
|
||||
<para>Existing agents are: a boot-time password agent asking the
|
||||
user for passwords using Plymouth; a boot-time password agent
|
||||
querying the user directly on the console; an agent requesting
|
||||
password input via a
|
||||
<citerefentry project='man-pages'><refentrytitle>wall</refentrytitle><manvolnum>1</manvolnum></citerefentry>
|
||||
message; an agent suitable for running in a GNOME session; a
|
||||
command line agent which can be started temporarily to process
|
||||
queued password requests; a TTY agent that is temporarily spawned
|
||||
during
|
||||
<citerefentry><refentrytitle>systemctl</refentrytitle><manvolnum>1</manvolnum></citerefentry>
|
||||
invocations.</para>
|
||||
<para>Existing agents are:
|
||||
<itemizedlist>
|
||||
|
||||
<listitem><para>A boot-time password agent asking the user for
|
||||
passwords using Plymouth</para></listitem>
|
||||
|
||||
<listitem><para>A boot-time password agent querying the user
|
||||
directly on the console</para></listitem>
|
||||
|
||||
<listitem><para>An agent requesting password input via a
|
||||
<citerefentry
|
||||
project='man-pages'><refentrytitle>wall</refentrytitle><manvolnum>1</manvolnum></citerefentry>
|
||||
message</para></listitem>
|
||||
|
||||
<listitem><para>A command line agent which can be started
|
||||
temporarily to process queued password
|
||||
requests</para></listitem>
|
||||
|
||||
<listitem><para>A TTY agent that is temporarily spawned during
|
||||
<citerefentry><refentrytitle>systemctl</refentrytitle><manvolnum>1</manvolnum></citerefentry>
|
||||
invocations</para></listitem>
|
||||
</itemizedlist></para>
|
||||
|
||||
<para>Additional password agents may be implemented according to
|
||||
the <ulink
|
||||
@ -111,6 +122,38 @@
|
||||
Icon Naming Specification</ulink>.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>--id=</option></term>
|
||||
<listitem><para>Specify an identifier for this password
|
||||
query. This identifier is freely choosable and allows
|
||||
recognition of queries by involved agents. It should include
|
||||
the subsystem doing the query and the specific object the
|
||||
query is done for. Example:
|
||||
<literal>--id=cryptsetup:/dev/sda5</literal>.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>--keyname=</option></term>
|
||||
<listitem><para>Configure a kernel keyring key name to use as
|
||||
cache for the password. If set, then the tool will try to push
|
||||
any collected passwords into the kernel keyring of the root
|
||||
user, as a key of the specified name. If combined with
|
||||
<option>--accept-cached</option> it will also try to retrieve
|
||||
the such cached passwords from the key in the kernel keyring
|
||||
instead of querying the user right-away. By using this option
|
||||
the kernel keyring may be used as effective cache to avoid
|
||||
repeatedly asking users for passwords, if there are multiple
|
||||
objects that may be unlocked with the same password. The
|
||||
cached key will have a timeout of 2.5min set, after which it
|
||||
will be purged from the kernel keyring. Note that it is
|
||||
possible to cache multiple passwords under the same keyname,
|
||||
in which case they will be stored as NUL-separated list of
|
||||
passwords. Use
|
||||
<citerefentry><refentrytitle>keyctl</refentrytitle><manvolnum>1</manvolnum></citerefentry>
|
||||
to access the cached key via the kernel keyring
|
||||
directly. Example: <literal>--keyname=cryptsetup</literal></para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>--timeout=</option></term>
|
||||
|
||||
@ -138,7 +181,7 @@
|
||||
<term><option>--accept-cached</option></term>
|
||||
|
||||
<listitem><para>If passed, accept cached passwords, i.e.
|
||||
passwords previously typed in.</para></listitem>
|
||||
passwords previously typed in. </para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
@ -166,6 +209,7 @@
|
||||
<para>
|
||||
<citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
|
||||
<citerefentry><refentrytitle>systemctl</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
|
||||
<citerefentry><refentrytitle>keyctl</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
|
||||
<citerefentry project='die-net'><refentrytitle>plymouth</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
|
||||
<citerefentry project='man-pages'><refentrytitle>wall</refentrytitle><manvolnum>1</manvolnum></citerefentry>
|
||||
</para>
|
||||
|
@ -20,36 +20,36 @@
|
||||
***/
|
||||
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <getopt.h>
|
||||
#include <stddef.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "ask-password-api.h"
|
||||
#include "def.h"
|
||||
#include "log.h"
|
||||
#include "macro.h"
|
||||
#include "strv.h"
|
||||
#include "ask-password-api.h"
|
||||
#include "def.h"
|
||||
|
||||
static const char *arg_icon = NULL;
|
||||
static const char *arg_id = NULL;
|
||||
static const char *arg_message = NULL;
|
||||
static bool arg_echo = false;
|
||||
static bool arg_use_tty = true;
|
||||
static const char *arg_keyname = NULL;
|
||||
static char *arg_message = NULL;
|
||||
static usec_t arg_timeout = DEFAULT_TIMEOUT_USEC;
|
||||
static bool arg_accept_cached = false;
|
||||
static bool arg_multiple = false;
|
||||
static AskPasswordFlags arg_flags = ASK_PASSWORD_PUSH_CACHE;
|
||||
|
||||
static void help(void) {
|
||||
printf("%s [OPTIONS...] MESSAGE\n\n"
|
||||
"Query the user for a system passphrase, via the TTY or an UI agent.\n\n"
|
||||
" -h --help Show this help\n"
|
||||
" --icon=NAME Icon name\n"
|
||||
" --timeout=SEC Timeout in sec\n"
|
||||
" --echo Do not mask input (useful for usernames)\n"
|
||||
" --no-tty Ask question via agent even on TTY\n"
|
||||
" --accept-cached Accept cached passwords\n"
|
||||
" --multiple List multiple passwords if available\n"
|
||||
" --id=ID Query identifier (e.g. cryptsetup:/dev/sda5)\n"
|
||||
" -h --help Show this help\n"
|
||||
" --icon=NAME Icon name\n"
|
||||
" --id=ID Query identifier (e.g. \"cryptsetup:/dev/sda5\")\n"
|
||||
" --keyname=NAME Kernel key name for caching passwords (e.g. \"cryptsetup\")\n"
|
||||
" --timeout=SEC Timeout in seconds\n"
|
||||
" --echo Do not mask input (useful for usernames)\n"
|
||||
" --no-tty Ask question via agent even on TTY\n"
|
||||
" --accept-cached Accept cached passwords\n"
|
||||
" --multiple List multiple passwords if available\n"
|
||||
, program_invocation_short_name);
|
||||
}
|
||||
|
||||
@ -62,7 +62,8 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
ARG_NO_TTY,
|
||||
ARG_ACCEPT_CACHED,
|
||||
ARG_MULTIPLE,
|
||||
ARG_ID
|
||||
ARG_ID,
|
||||
ARG_KEYNAME,
|
||||
};
|
||||
|
||||
static const struct option options[] = {
|
||||
@ -74,6 +75,7 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
{ "accept-cached", no_argument, NULL, ARG_ACCEPT_CACHED },
|
||||
{ "multiple", no_argument, NULL, ARG_MULTIPLE },
|
||||
{ "id", required_argument, NULL, ARG_ID },
|
||||
{ "keyname", required_argument, NULL, ARG_KEYNAME },
|
||||
{}
|
||||
};
|
||||
|
||||
@ -102,15 +104,15 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
break;
|
||||
|
||||
case ARG_ECHO:
|
||||
arg_echo = true;
|
||||
arg_flags |= ASK_PASSWORD_ECHO;
|
||||
break;
|
||||
|
||||
case ARG_NO_TTY:
|
||||
arg_use_tty = false;
|
||||
arg_flags |= ASK_PASSWORD_NO_TTY;
|
||||
break;
|
||||
|
||||
case ARG_ACCEPT_CACHED:
|
||||
arg_accept_cached = true;
|
||||
arg_flags |= ASK_PASSWORD_ACCEPT_CACHED;
|
||||
break;
|
||||
|
||||
case ARG_MULTIPLE:
|
||||
@ -121,6 +123,10 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
arg_id = optarg;
|
||||
break;
|
||||
|
||||
case ARG_KEYNAME:
|
||||
arg_keyname = optarg;
|
||||
break;
|
||||
|
||||
case '?':
|
||||
return -EINVAL;
|
||||
|
||||
@ -128,18 +134,20 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
assert_not_reached("Unhandled option");
|
||||
}
|
||||
|
||||
if (optind != argc - 1) {
|
||||
log_error("%s: required argument missing.", program_invocation_short_name);
|
||||
return -EINVAL;
|
||||
if (argc > optind) {
|
||||
arg_message = strv_join(argv + optind, " ");
|
||||
if (!arg_message)
|
||||
return log_oom();
|
||||
}
|
||||
|
||||
arg_message = argv[optind];
|
||||
return 1;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
int r;
|
||||
_cleanup_strv_free_ char **l = NULL;
|
||||
usec_t timeout;
|
||||
char **p;
|
||||
int r;
|
||||
|
||||
log_parse_environment();
|
||||
log_open();
|
||||
@ -153,36 +161,21 @@ int main(int argc, char *argv[]) {
|
||||
else
|
||||
timeout = 0;
|
||||
|
||||
if (arg_use_tty && isatty(STDIN_FILENO)) {
|
||||
char *password = NULL;
|
||||
r = ask_password_auto(arg_message, arg_icon, arg_id, arg_keyname, timeout, arg_flags, &l);
|
||||
if (r < 0) {
|
||||
log_error_errno(r, "Failed to query password: %m");
|
||||
goto finish;
|
||||
}
|
||||
|
||||
r = ask_password_tty(arg_message, timeout, arg_echo, NULL,
|
||||
&password);
|
||||
if (r >= 0) {
|
||||
puts(password);
|
||||
free(password);
|
||||
}
|
||||
STRV_FOREACH(p, l) {
|
||||
puts(*p);
|
||||
|
||||
} else {
|
||||
char **l;
|
||||
|
||||
r = ask_password_agent(arg_message, arg_icon, arg_id, timeout,
|
||||
arg_echo, arg_accept_cached, &l);
|
||||
if (r >= 0) {
|
||||
char **p;
|
||||
|
||||
STRV_FOREACH(p, l) {
|
||||
puts(*p);
|
||||
|
||||
if (!arg_multiple)
|
||||
break;
|
||||
}
|
||||
|
||||
strv_free(l);
|
||||
}
|
||||
if (!arg_multiple)
|
||||
break;
|
||||
}
|
||||
|
||||
finish:
|
||||
free(arg_message);
|
||||
|
||||
return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
|
||||
}
|
||||
|
@ -1063,3 +1063,48 @@ static inline int kcmp(pid_t pid1, pid_t pid2, int type, unsigned long idx1, uns
|
||||
#ifndef INPUT_PROP_ACCELEROMETER
|
||||
#define INPUT_PROP_ACCELEROMETER 0x06
|
||||
#endif
|
||||
|
||||
#if !HAVE_DECL_KEY_SERIAL_T
|
||||
typedef int32_t key_serial_t;
|
||||
#endif
|
||||
|
||||
#if !HAVE_DECL_KEYCTL
|
||||
static inline long keyctl(int cmd, unsigned long arg2, unsigned long arg3, unsigned long arg4,unsigned long arg5) {
|
||||
#if defined(__NR_keyctl)
|
||||
return syscall(__NR_keyctl, cmd, arg2, arg3, arg4, arg5);
|
||||
#else
|
||||
errno = ENOSYS;
|
||||
return -1;
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline key_serial_t add_key(const char *type, const char *description, const void *payload, size_t plen, key_serial_t ringid) {
|
||||
#if defined (__NR_add_key)
|
||||
return syscall(__NR_add_key, type, description, payload, plen, ringid);
|
||||
#else
|
||||
errno = ENOSYS;
|
||||
return -1;
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline key_serial_t request_key(const char *type, const char *description, const char * callout_info, key_serial_t destringid) {
|
||||
#if defined (__NR_request_key)
|
||||
return syscall(__NR_request_key, type, description, callout_info, destringid);
|
||||
#else
|
||||
errno = ENOSYS;
|
||||
return -1;
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef KEYCTL_READ
|
||||
#define KEYCTL_READ 11
|
||||
#endif
|
||||
|
||||
#ifndef KEYCTL_SET_TIMEOUT
|
||||
#define KEYCTL_SET_TIMEOUT 15
|
||||
#endif
|
||||
|
||||
#ifndef KEY_SPEC_USER_KEYRING
|
||||
#define KEY_SPEC_USER_KEYRING -4
|
||||
#endif
|
||||
|
@ -188,17 +188,48 @@ char **strv_new(const char *x, ...) {
|
||||
return r;
|
||||
}
|
||||
|
||||
int strv_extend_strv(char ***a, char **b) {
|
||||
int r;
|
||||
char **s;
|
||||
int strv_extend_strv(char ***a, char **b, bool filter_duplicates) {
|
||||
char **s, **t;
|
||||
size_t p, q, i = 0, j;
|
||||
|
||||
assert(a);
|
||||
|
||||
if (strv_isempty(b))
|
||||
return 0;
|
||||
|
||||
p = strv_length(*a);
|
||||
q = strv_length(b);
|
||||
|
||||
t = realloc(*a, sizeof(char*) * (p + q + 1));
|
||||
if (!t)
|
||||
return -ENOMEM;
|
||||
|
||||
t[p] = NULL;
|
||||
*a = t;
|
||||
|
||||
STRV_FOREACH(s, b) {
|
||||
r = strv_extend(a, *s);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (filter_duplicates && strv_contains(t, *s))
|
||||
continue;
|
||||
|
||||
t[p+i] = strdup(*s);
|
||||
if (!t[p+i])
|
||||
goto rollback;
|
||||
|
||||
i++;
|
||||
t[p+i] = NULL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
assert(i <= q);
|
||||
|
||||
return (int) i;
|
||||
|
||||
rollback:
|
||||
for (j = 0; j < i; j++)
|
||||
free(t[p + j]);
|
||||
|
||||
t[p] = NULL;
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
int strv_extend_strv_concat(char ***a, char **b, const char *suffix) {
|
||||
@ -618,6 +649,41 @@ char **strv_split_nulstr(const char *s) {
|
||||
return r;
|
||||
}
|
||||
|
||||
int strv_make_nulstr(char **l, char **p, size_t *q) {
|
||||
size_t n_allocated = 0, n = 0;
|
||||
_cleanup_free_ char *m = NULL;
|
||||
char **i;
|
||||
|
||||
assert(p);
|
||||
assert(q);
|
||||
|
||||
STRV_FOREACH(i, l) {
|
||||
size_t z;
|
||||
|
||||
z = strlen(*i);
|
||||
|
||||
if (!GREEDY_REALLOC(m, n_allocated, n + z + 1))
|
||||
return -ENOMEM;
|
||||
|
||||
memcpy(m + n, *i, z + 1);
|
||||
n += z + 1;
|
||||
}
|
||||
|
||||
if (!m) {
|
||||
m = new0(char, 1);
|
||||
if (!m)
|
||||
return -ENOMEM;
|
||||
n = 0;
|
||||
}
|
||||
|
||||
*p = m;
|
||||
*q = n;
|
||||
|
||||
m = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool strv_overlap(char **a, char **b) {
|
||||
char **i;
|
||||
|
||||
@ -644,8 +710,12 @@ char **strv_sort(char **l) {
|
||||
}
|
||||
|
||||
bool strv_equal(char **a, char **b) {
|
||||
if (!a || !b)
|
||||
return a == b;
|
||||
|
||||
if (strv_isempty(a))
|
||||
return strv_isempty(b);
|
||||
|
||||
if (strv_isempty(b))
|
||||
return false;
|
||||
|
||||
for ( ; *a || *b; ++a, ++b)
|
||||
if (!streq_ptr(*a, *b))
|
||||
|
@ -40,7 +40,7 @@ void strv_clear(char **l);
|
||||
char **strv_copy(char * const *l);
|
||||
unsigned strv_length(char * const *l) _pure_;
|
||||
|
||||
int strv_extend_strv(char ***a, char **b);
|
||||
int strv_extend_strv(char ***a, char **b, bool filter_duplicates);
|
||||
int strv_extend_strv_concat(char ***a, char **b, const char *suffix);
|
||||
int strv_extend(char ***l, const char *value);
|
||||
int strv_extendf(char ***l, const char *format, ...) _printf_(2,0);
|
||||
@ -80,6 +80,7 @@ char *strv_join_quoted(char **l);
|
||||
|
||||
char **strv_parse_nulstr(const char *s, size_t l);
|
||||
char **strv_split_nulstr(const char *s);
|
||||
int strv_make_nulstr(char **l, char **p, size_t *n);
|
||||
|
||||
bool strv_overlap(char **a, char **b) _pure_;
|
||||
|
||||
|
@ -2725,7 +2725,7 @@ int exec_command_append(ExecCommand *c, const char *path, ...) {
|
||||
if (!l)
|
||||
return -ENOMEM;
|
||||
|
||||
r = strv_extend_strv(&c->argv, l);
|
||||
r = strv_extend_strv(&c->argv, l, false);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
|
@ -599,7 +599,7 @@ static int config_parse_join_controllers(const char *unit,
|
||||
for (a = arg_join_controllers; *a; a++) {
|
||||
|
||||
if (strv_overlap(*a, l)) {
|
||||
if (strv_extend_strv(&l, *a) < 0) {
|
||||
if (strv_extend_strv(&l, *a, false) < 0) {
|
||||
strv_free(l);
|
||||
strv_free_free(t);
|
||||
return log_oom();
|
||||
|
@ -313,14 +313,10 @@ static char *disk_mount_point(const char *label) {
|
||||
}
|
||||
|
||||
static int get_password(const char *vol, const char *src, usec_t until, bool accept_cached, char ***passwords) {
|
||||
int r = 0;
|
||||
char **p;
|
||||
_cleanup_free_ char *text = NULL;
|
||||
_cleanup_free_ char *escaped_name = NULL;
|
||||
char *id;
|
||||
_cleanup_free_ char *description = NULL, *name_buffer = NULL, *mount_point = NULL, *maj_min = NULL, *text = NULL, *escaped_name = NULL;
|
||||
const char *name = NULL;
|
||||
_cleanup_free_ char *description = NULL, *name_buffer = NULL,
|
||||
*mount_point = NULL, *maj_min = NULL;
|
||||
char **p, *id;
|
||||
int r = 0;
|
||||
|
||||
assert(vol);
|
||||
assert(src);
|
||||
@ -364,7 +360,7 @@ static int get_password(const char *vol, const char *src, usec_t until, bool acc
|
||||
|
||||
id = strjoina("cryptsetup:", escaped_name);
|
||||
|
||||
r = ask_password_auto(text, "drive-harddisk", id, until, accept_cached, passwords);
|
||||
r = ask_password_auto(text, "drive-harddisk", id, "cryptsetup", until, ASK_PASSWORD_PUSH_CACHE|(accept_cached ? ASK_PASSWORD_ACCEPT_CACHED : 0), passwords);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to query password: %m");
|
||||
|
||||
@ -378,7 +374,7 @@ static int get_password(const char *vol, const char *src, usec_t until, bool acc
|
||||
|
||||
id = strjoina("cryptsetup-verification:", escaped_name);
|
||||
|
||||
r = ask_password_auto(text, "drive-harddisk", id, until, false, &passwords2);
|
||||
r = ask_password_auto(text, "drive-harddisk", id, "cryptsetup", until, ASK_PASSWORD_PUSH_CACHE, &passwords2);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to query verification password: %m");
|
||||
|
||||
|
@ -466,7 +466,7 @@ static int prompt_root_password(void) {
|
||||
for (;;) {
|
||||
_cleanup_free_ char *a = NULL, *b = NULL;
|
||||
|
||||
r = ask_password_tty(msg1, 0, false, NULL, &a);
|
||||
r = ask_password_tty(msg1, NULL, 0, 0, NULL, &a);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to query root password: %m");
|
||||
|
||||
@ -475,11 +475,10 @@ static int prompt_root_password(void) {
|
||||
break;
|
||||
}
|
||||
|
||||
r = ask_password_tty(msg2, 0, false, NULL, &b);
|
||||
r = ask_password_tty(msg2, NULL, 0, 0, NULL, &b);
|
||||
if (r < 0) {
|
||||
log_error_errno(r, "Failed to query root password: %m");
|
||||
clear_string(a);
|
||||
return r;
|
||||
return log_error_errno(r, "Failed to query root password: %m");
|
||||
}
|
||||
|
||||
if (!streq(a, b)) {
|
||||
|
@ -50,7 +50,7 @@ static int add_modules(const char *p) {
|
||||
if (!k)
|
||||
return log_oom();
|
||||
|
||||
if (strv_extend_strv(&arg_proc_cmdline_modules, k) < 0)
|
||||
if (strv_extend_strv(&arg_proc_cmdline_modules, k, true) < 0)
|
||||
return log_oom();
|
||||
|
||||
return 0;
|
||||
|
@ -18,28 +18,158 @@
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with systemd; If not, see <http://www.gnu.org/licenses/>.
|
||||
***/
|
||||
#include <stdbool.h>
|
||||
#include <termios.h>
|
||||
#include <unistd.h>
|
||||
#include <poll.h>
|
||||
#include <sys/inotify.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/socket.h>
|
||||
#include <string.h>
|
||||
#include <sys/un.h>
|
||||
#include <poll.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
#include <sys/inotify.h>
|
||||
#include <sys/signalfd.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/un.h>
|
||||
#include <termios.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "util.h"
|
||||
#include "formats-util.h"
|
||||
#include "missing.h"
|
||||
#include "mkdir.h"
|
||||
#include "strv.h"
|
||||
#include "random-util.h"
|
||||
#include "terminal-util.h"
|
||||
#include "signal-util.h"
|
||||
#include "socket-util.h"
|
||||
#include "strv.h"
|
||||
#include "terminal-util.h"
|
||||
#include "util.h"
|
||||
#include "ask-password-api.h"
|
||||
|
||||
#define KEYRING_TIMEOUT_USEC ((5 * USEC_PER_MINUTE) / 2)
|
||||
|
||||
static int lookup_key(const char *keyname, key_serial_t *ret) {
|
||||
key_serial_t serial;
|
||||
|
||||
assert(keyname);
|
||||
assert(ret);
|
||||
|
||||
serial = request_key("user", keyname, NULL, 0);
|
||||
if (serial == -1)
|
||||
return -errno;
|
||||
|
||||
*ret = serial;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int retrieve_key(key_serial_t serial, char ***ret) {
|
||||
_cleanup_free_ char *p = NULL;
|
||||
long m = 100, n;
|
||||
char **l;
|
||||
|
||||
assert(ret);
|
||||
|
||||
for (;;) {
|
||||
p = new(char, m);
|
||||
if (!p)
|
||||
return -ENOMEM;
|
||||
|
||||
n = keyctl(KEYCTL_READ, (unsigned long) serial, (unsigned long) p, (unsigned long) m, 0);
|
||||
if (n < 0)
|
||||
return -errno;
|
||||
|
||||
if (n < m)
|
||||
break;
|
||||
|
||||
free(p);
|
||||
m *= 2;
|
||||
}
|
||||
|
||||
l = strv_parse_nulstr(p, n);
|
||||
if (!l)
|
||||
return -ENOMEM;
|
||||
|
||||
*ret = l;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int add_to_keyring(const char *keyname, AskPasswordFlags flags, char **passwords) {
|
||||
_cleanup_strv_free_ char **l = NULL;
|
||||
_cleanup_free_ char *p = NULL;
|
||||
key_serial_t serial;
|
||||
size_t n;
|
||||
int r;
|
||||
|
||||
assert(keyname);
|
||||
assert(passwords);
|
||||
|
||||
if (!(flags & ASK_PASSWORD_PUSH_CACHE))
|
||||
return 0;
|
||||
|
||||
r = lookup_key(keyname, &serial);
|
||||
if (r >= 0) {
|
||||
r = retrieve_key(serial, &l);
|
||||
if (r < 0)
|
||||
return r;
|
||||
} else if (r != -ENOKEY)
|
||||
return r;
|
||||
|
||||
r = strv_extend_strv(&l, passwords, true);
|
||||
if (r <= 0)
|
||||
return r;
|
||||
|
||||
r = strv_make_nulstr(l, &p, &n);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
/* Truncate trailing NUL */
|
||||
assert(n > 0);
|
||||
assert(p[n-1] == 0);
|
||||
|
||||
serial = add_key("user", keyname, p, n-1, KEY_SPEC_USER_KEYRING);
|
||||
if (serial == -1)
|
||||
return -errno;
|
||||
|
||||
if (keyctl(KEYCTL_SET_TIMEOUT,
|
||||
(unsigned long) serial,
|
||||
(unsigned long) DIV_ROUND_UP(KEYRING_TIMEOUT_USEC, USEC_PER_SEC), 0, 0) < 0)
|
||||
log_debug_errno(errno, "Failed to adjust timeout: %m");
|
||||
|
||||
log_debug("Added key to keyring as %" PRIi32 ".", serial);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int add_to_keyring_and_log(const char *keyname, AskPasswordFlags flags, char **passwords) {
|
||||
int r;
|
||||
|
||||
assert(keyname);
|
||||
assert(passwords);
|
||||
|
||||
r = add_to_keyring(keyname, flags, passwords);
|
||||
if (r < 0)
|
||||
return log_debug_errno(r, "Failed to add password to keyring: %m");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ask_password_keyring(const char *keyname, AskPasswordFlags flags, char ***ret) {
|
||||
|
||||
key_serial_t serial;
|
||||
int r;
|
||||
|
||||
assert(keyname);
|
||||
assert(ret);
|
||||
|
||||
if (!(flags & ASK_PASSWORD_ACCEPT_CACHED))
|
||||
return -EUNATCH;
|
||||
|
||||
r = lookup_key(keyname, &serial);
|
||||
if (r == -ENOSYS) /* when retrieving the distinction doesn't matter */
|
||||
return -ENOKEY;
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return retrieve_key(serial, ret);
|
||||
}
|
||||
|
||||
static void backspace_chars(int ttyfd, size_t p) {
|
||||
|
||||
if (ttyfd < 0)
|
||||
@ -54,10 +184,11 @@ static void backspace_chars(int ttyfd, size_t p) {
|
||||
|
||||
int ask_password_tty(
|
||||
const char *message,
|
||||
const char *keyname,
|
||||
usec_t until,
|
||||
bool echo,
|
||||
AskPasswordFlags flags,
|
||||
const char *flag_file,
|
||||
char **_passphrase) {
|
||||
char **ret) {
|
||||
|
||||
struct termios old_termios, new_termios;
|
||||
char passphrase[LINE_MAX], *x;
|
||||
@ -66,15 +197,19 @@ int ask_password_tty(
|
||||
_cleanup_close_ int ttyfd = -1, notify = -1;
|
||||
struct pollfd pollfd[2];
|
||||
bool reset_tty = false;
|
||||
bool silent_mode = false;
|
||||
bool dirty = false;
|
||||
enum {
|
||||
POLL_TTY,
|
||||
POLL_INOTIFY
|
||||
};
|
||||
|
||||
assert(message);
|
||||
assert(_passphrase);
|
||||
assert(ret);
|
||||
|
||||
if (flags & ASK_PASSWORD_NO_TTY)
|
||||
return -EUNATCH;
|
||||
|
||||
if (!message)
|
||||
message = "Password:";
|
||||
|
||||
if (flag_file) {
|
||||
notify = inotify_init1(IN_CLOEXEC|IN_NONBLOCK);
|
||||
@ -97,10 +232,10 @@ int ask_password_tty(
|
||||
goto finish;
|
||||
}
|
||||
|
||||
loop_write(ttyfd, ANSI_HIGHLIGHT, sizeof(ANSI_HIGHLIGHT)-1, false);
|
||||
loop_write(ttyfd, ANSI_HIGHLIGHT, strlen(ANSI_HIGHLIGHT), false);
|
||||
loop_write(ttyfd, message, strlen(message), false);
|
||||
loop_write(ttyfd, " ", 1, false);
|
||||
loop_write(ttyfd, ANSI_NORMAL, sizeof(ANSI_NORMAL)-1, false);
|
||||
loop_write(ttyfd, ANSI_NORMAL, strlen(ANSI_NORMAL), false);
|
||||
|
||||
new_termios = old_termios;
|
||||
new_termios.c_lflag &= ~(ICANON|ECHO);
|
||||
@ -145,7 +280,7 @@ int ask_password_tty(
|
||||
goto finish;
|
||||
}
|
||||
|
||||
k = poll(pollfd, notify > 0 ? 2 : 1, sleep_for);
|
||||
k = poll(pollfd, notify >= 0 ? 2 : 1, sleep_for);
|
||||
if (k < 0) {
|
||||
if (errno == EINTR)
|
||||
continue;
|
||||
@ -157,7 +292,7 @@ int ask_password_tty(
|
||||
goto finish;
|
||||
}
|
||||
|
||||
if (notify > 0 && pollfd[POLL_INOTIFY].revents != 0)
|
||||
if (notify >= 0 && pollfd[POLL_INOTIFY].revents != 0)
|
||||
flush_fd(notify);
|
||||
|
||||
if (pollfd[POLL_TTY].revents == 0)
|
||||
@ -178,7 +313,7 @@ int ask_password_tty(
|
||||
break;
|
||||
else if (c == 21) { /* C-u */
|
||||
|
||||
if (!silent_mode)
|
||||
if (!(flags & ASK_PASSWORD_SILENT))
|
||||
backspace_chars(ttyfd, p);
|
||||
p = 0;
|
||||
|
||||
@ -186,28 +321,28 @@ int ask_password_tty(
|
||||
|
||||
if (p > 0) {
|
||||
|
||||
if (!silent_mode)
|
||||
if (!(flags & ASK_PASSWORD_SILENT))
|
||||
backspace_chars(ttyfd, 1);
|
||||
|
||||
p--;
|
||||
} else if (!dirty && !silent_mode) {
|
||||
} else if (!dirty && !(flags & ASK_PASSWORD_SILENT)) {
|
||||
|
||||
silent_mode = true;
|
||||
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 ... */
|
||||
* as first key (and only as first
|
||||
* key), or ... */
|
||||
if (ttyfd >= 0)
|
||||
loop_write(ttyfd, "(no echo) ", 10, false);
|
||||
|
||||
} else if (ttyfd >= 0)
|
||||
loop_write(ttyfd, "\a", 1, false);
|
||||
|
||||
} else if (c == '\t' && !silent_mode) {
|
||||
} else if (c == '\t' && !(flags & ASK_PASSWORD_SILENT)) {
|
||||
|
||||
backspace_chars(ttyfd, p);
|
||||
silent_mode = true;
|
||||
flags |= ASK_PASSWORD_SILENT;
|
||||
|
||||
/* ... or by pressing TAB at any time. */
|
||||
|
||||
@ -221,8 +356,8 @@ int ask_password_tty(
|
||||
|
||||
passphrase[p++] = c;
|
||||
|
||||
if (!silent_mode && ttyfd >= 0)
|
||||
loop_write(ttyfd, echo ? &c : "*", 1, false);
|
||||
if (!(flags & ASK_PASSWORD_SILENT) && ttyfd >= 0)
|
||||
loop_write(ttyfd, (flags & ASK_PASSWORD_ECHO) ? &c : "*", 1, false);
|
||||
|
||||
dirty = true;
|
||||
}
|
||||
@ -234,7 +369,10 @@ int ask_password_tty(
|
||||
goto finish;
|
||||
}
|
||||
|
||||
*_passphrase = x;
|
||||
if (keyname)
|
||||
(void) add_to_keyring_and_log(keyname, flags, STRV_MAKE(x));
|
||||
|
||||
*ret = x;
|
||||
r = 0;
|
||||
|
||||
finish:
|
||||
@ -247,52 +385,38 @@ finish:
|
||||
}
|
||||
|
||||
static int create_socket(char **name) {
|
||||
int fd;
|
||||
union {
|
||||
struct sockaddr sa;
|
||||
struct sockaddr_un un;
|
||||
} sa = {
|
||||
union sockaddr_union sa = {
|
||||
.un.sun_family = AF_UNIX,
|
||||
};
|
||||
int one = 1;
|
||||
int r = 0;
|
||||
_cleanup_close_ int fd = -1;
|
||||
static const int one = 1;
|
||||
char *c;
|
||||
int r;
|
||||
|
||||
assert(name);
|
||||
|
||||
fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
|
||||
if (fd < 0)
|
||||
return log_error_errno(errno, "socket() failed: %m");
|
||||
return -errno;
|
||||
|
||||
snprintf(sa.un.sun_path, sizeof(sa.un.sun_path)-1, "/run/systemd/ask-password/sck.%" PRIx64, random_u64());
|
||||
|
||||
RUN_WITH_UMASK(0177) {
|
||||
r = bind(fd, &sa.sa, offsetof(struct sockaddr_un, sun_path) + strlen(sa.un.sun_path));
|
||||
if (bind(fd, &sa.sa, offsetof(struct sockaddr_un, sun_path) + strlen(sa.un.sun_path)) < 0)
|
||||
return -errno;
|
||||
}
|
||||
|
||||
if (r < 0) {
|
||||
r = -errno;
|
||||
log_error_errno(errno, "bind(%s) failed: %m", sa.un.sun_path);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (setsockopt(fd, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one)) < 0) {
|
||||
r = -errno;
|
||||
log_error_errno(errno, "SO_PASSCRED failed: %m");
|
||||
goto fail;
|
||||
}
|
||||
if (setsockopt(fd, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one)) < 0)
|
||||
return -errno;
|
||||
|
||||
c = strdup(sa.un.sun_path);
|
||||
if (!c) {
|
||||
r = log_oom();
|
||||
goto fail;
|
||||
}
|
||||
if (!c)
|
||||
return -ENOMEM;
|
||||
|
||||
*name = c;
|
||||
return fd;
|
||||
|
||||
fail:
|
||||
safe_close(fd);
|
||||
r = fd;
|
||||
fd = -1;
|
||||
|
||||
return r;
|
||||
}
|
||||
@ -301,10 +425,10 @@ int ask_password_agent(
|
||||
const char *message,
|
||||
const char *icon,
|
||||
const char *id,
|
||||
const char *keyname,
|
||||
usec_t until,
|
||||
bool echo,
|
||||
bool accept_cached,
|
||||
char ***_passphrases) {
|
||||
AskPasswordFlags flags,
|
||||
char ***ret) {
|
||||
|
||||
enum {
|
||||
FD_SOCKET,
|
||||
@ -312,35 +436,38 @@ int ask_password_agent(
|
||||
_FD_MAX
|
||||
};
|
||||
|
||||
_cleanup_close_ int socket_fd = -1, signal_fd = -1, fd = -1;
|
||||
char temp[] = "/run/systemd/ask-password/tmp.XXXXXX";
|
||||
char final[sizeof(temp)] = "";
|
||||
_cleanup_fclose_ FILE *f = NULL;
|
||||
_cleanup_free_ char *socket_name = NULL;
|
||||
_cleanup_close_ int socket_fd = -1, signal_fd = -1, fd = -1;
|
||||
sigset_t mask, oldmask;
|
||||
_cleanup_strv_free_ char **l = NULL;
|
||||
_cleanup_fclose_ FILE *f = NULL;
|
||||
struct pollfd pollfd[_FD_MAX];
|
||||
sigset_t mask, oldmask;
|
||||
int r;
|
||||
|
||||
assert(_passphrases);
|
||||
assert(ret);
|
||||
|
||||
if (flags & ASK_PASSWORD_NO_AGENT)
|
||||
return -EUNATCH;
|
||||
|
||||
assert_se(sigemptyset(&mask) >= 0);
|
||||
assert_se(sigset_add_many(&mask, SIGINT, SIGTERM, -1) >= 0);
|
||||
assert_se(sigprocmask(SIG_BLOCK, &mask, &oldmask) >= 0);
|
||||
|
||||
mkdir_p_label("/run/systemd/ask-password", 0755);
|
||||
(void) mkdir_p_label("/run/systemd/ask-password", 0755);
|
||||
|
||||
fd = mkostemp_safe(temp, O_WRONLY|O_CLOEXEC);
|
||||
if (fd < 0) {
|
||||
r = log_error_errno(errno,
|
||||
"Failed to create password file: %m");
|
||||
r = -errno;
|
||||
goto finish;
|
||||
}
|
||||
|
||||
fchmod(fd, 0644);
|
||||
(void) fchmod(fd, 0644);
|
||||
|
||||
f = fdopen(fd, "w");
|
||||
if (!f) {
|
||||
r = log_error_errno(errno, "Failed to allocate FILE: %m");
|
||||
r = -errno;
|
||||
goto finish;
|
||||
}
|
||||
|
||||
@ -348,7 +475,7 @@ int ask_password_agent(
|
||||
|
||||
signal_fd = signalfd(-1, &mask, SFD_NONBLOCK|SFD_CLOEXEC);
|
||||
if (signal_fd < 0) {
|
||||
r = log_error_errno(errno, "signalfd(): %m");
|
||||
r = -errno;
|
||||
goto finish;
|
||||
}
|
||||
|
||||
@ -367,8 +494,8 @@ int ask_password_agent(
|
||||
"NotAfter="USEC_FMT"\n",
|
||||
getpid(),
|
||||
socket_name,
|
||||
accept_cached ? 1 : 0,
|
||||
echo ? 1 : 0,
|
||||
(flags & ASK_PASSWORD_ACCEPT_CACHED) ? 1 : 0,
|
||||
(flags & ASK_PASSWORD_ECHO) ? 1 : 0,
|
||||
until);
|
||||
|
||||
if (message)
|
||||
@ -381,10 +508,8 @@ int ask_password_agent(
|
||||
fprintf(f, "Id=%s\n", id);
|
||||
|
||||
r = fflush_and_check(f);
|
||||
if (r < 0) {
|
||||
log_error_errno(r, "Failed to write query file: %m");
|
||||
if (r < 0)
|
||||
goto finish;
|
||||
}
|
||||
|
||||
memcpy(final, temp, sizeof(temp));
|
||||
|
||||
@ -393,7 +518,7 @@ int ask_password_agent(
|
||||
final[sizeof(final)-9] = 'k';
|
||||
|
||||
if (rename(temp, final) < 0) {
|
||||
r = log_error_errno(errno, "Failed to rename query file: %m");
|
||||
r = -errno;
|
||||
goto finish;
|
||||
}
|
||||
|
||||
@ -419,7 +544,6 @@ int ask_password_agent(
|
||||
t = now(CLOCK_MONOTONIC);
|
||||
|
||||
if (until > 0 && until <= t) {
|
||||
log_notice("Timed out");
|
||||
r = -ETIME;
|
||||
goto finish;
|
||||
}
|
||||
@ -429,12 +553,11 @@ int ask_password_agent(
|
||||
if (errno == EINTR)
|
||||
continue;
|
||||
|
||||
r = log_error_errno(errno, "poll() failed: %m");
|
||||
r = -errno;
|
||||
goto finish;
|
||||
}
|
||||
|
||||
if (k <= 0) {
|
||||
log_notice("Timed out");
|
||||
r = -ETIME;
|
||||
goto finish;
|
||||
}
|
||||
@ -445,7 +568,6 @@ int ask_password_agent(
|
||||
}
|
||||
|
||||
if (pollfd[FD_SOCKET].revents != POLLIN) {
|
||||
log_error("Unexpected poll() event.");
|
||||
r = -EIO;
|
||||
goto finish;
|
||||
}
|
||||
@ -467,14 +589,14 @@ int ask_password_agent(
|
||||
errno == EINTR)
|
||||
continue;
|
||||
|
||||
r = log_error_errno(errno, "recvmsg() failed: %m");
|
||||
r = -errno;
|
||||
goto finish;
|
||||
}
|
||||
|
||||
cmsg_close_all(&msghdr);
|
||||
|
||||
if (n <= 0) {
|
||||
log_error("Message too short");
|
||||
log_debug("Message too short");
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -482,84 +604,100 @@ int ask_password_agent(
|
||||
control.cmsghdr.cmsg_level != SOL_SOCKET ||
|
||||
control.cmsghdr.cmsg_type != SCM_CREDENTIALS ||
|
||||
control.cmsghdr.cmsg_len != CMSG_LEN(sizeof(struct ucred))) {
|
||||
log_warning("Received message without credentials. Ignoring.");
|
||||
log_debug("Received message without credentials. Ignoring.");
|
||||
continue;
|
||||
}
|
||||
|
||||
ucred = (struct ucred*) CMSG_DATA(&control.cmsghdr);
|
||||
if (ucred->uid != 0) {
|
||||
log_warning("Got request from unprivileged user. Ignoring.");
|
||||
log_debug("Got request from unprivileged user. Ignoring.");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (passphrase[0] == '+') {
|
||||
char **l;
|
||||
|
||||
/* An empty message refers to the empty password */
|
||||
if (n == 1)
|
||||
l = strv_new("", NULL);
|
||||
else
|
||||
l = strv_parse_nulstr(passphrase+1, n-1);
|
||||
/* An empty message refers to the empty password */
|
||||
|
||||
if (!l) {
|
||||
r = -ENOMEM;
|
||||
goto finish;
|
||||
}
|
||||
|
||||
if (strv_length(l) <= 0) {
|
||||
strv_free(l);
|
||||
log_error("Invalid packet");
|
||||
l = strv_free(l);
|
||||
log_debug("Invalid packet");
|
||||
continue;
|
||||
}
|
||||
|
||||
*_passphrases = l;
|
||||
|
||||
} else if (passphrase[0] == '-') {
|
||||
r = -ECANCELED;
|
||||
goto finish;
|
||||
} else {
|
||||
log_error("Invalid packet");
|
||||
continue;
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
if (passphrase[0] == '-') {
|
||||
r = -ECANCELED;
|
||||
goto finish;
|
||||
}
|
||||
|
||||
log_debug("Invalid packet");
|
||||
}
|
||||
|
||||
if (keyname)
|
||||
(void) add_to_keyring_and_log(keyname, flags, l);
|
||||
|
||||
*ret = l;
|
||||
l = NULL;
|
||||
r = 0;
|
||||
|
||||
finish:
|
||||
if (socket_name)
|
||||
unlink(socket_name);
|
||||
(void) unlink(socket_name);
|
||||
|
||||
unlink(temp);
|
||||
(void) unlink(temp);
|
||||
|
||||
if (final[0])
|
||||
unlink(final);
|
||||
(void) unlink(final);
|
||||
|
||||
assert_se(sigprocmask(SIG_SETMASK, &oldmask, NULL) == 0);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
int ask_password_auto(const char *message, const char *icon, const char *id,
|
||||
usec_t until, bool accept_cached, char ***_passphrases) {
|
||||
assert(message);
|
||||
assert(_passphrases);
|
||||
int ask_password_auto(
|
||||
const char *message,
|
||||
const char *icon,
|
||||
const char *id,
|
||||
const char *keyname,
|
||||
usec_t until,
|
||||
AskPasswordFlags flags,
|
||||
char ***ret) {
|
||||
|
||||
if (isatty(STDIN_FILENO)) {
|
||||
int r;
|
||||
int r;
|
||||
|
||||
assert(ret);
|
||||
|
||||
if ((flags & ASK_PASSWORD_ACCEPT_CACHED) && keyname) {
|
||||
r = ask_password_keyring(keyname, flags, ret);
|
||||
if (r != -ENOKEY)
|
||||
return r;
|
||||
}
|
||||
|
||||
if (!(flags & ASK_PASSWORD_NO_TTY) && isatty(STDIN_FILENO)) {
|
||||
char *s = NULL, **l = NULL;
|
||||
|
||||
r = ask_password_tty(message, until, false, NULL, &s);
|
||||
r = ask_password_tty(message, keyname, until, flags, NULL, &s);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = strv_consume(&l, s);
|
||||
if (r < 0)
|
||||
return r;
|
||||
return -ENOMEM;
|
||||
|
||||
*_passphrases = l;
|
||||
return r;
|
||||
} else
|
||||
return ask_password_agent(message, icon, id, until, false, accept_cached, _passphrases);
|
||||
*ret = l;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!(flags & ASK_PASSWORD_NO_AGENT))
|
||||
return ask_password_agent(message, icon, id, keyname, until, flags, ret);
|
||||
|
||||
return -EUNATCH;
|
||||
}
|
||||
|
@ -25,10 +25,16 @@
|
||||
|
||||
#include "time-util.h"
|
||||
|
||||
int ask_password_tty(const char *message, usec_t until, bool echo, const char *flag_file, char **_passphrase);
|
||||
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,
|
||||
} AskPasswordFlags;
|
||||
|
||||
int ask_password_agent(const char *message, const char *icon, const char *id,
|
||||
usec_t until, bool echo, bool accept_cached, char ***_passphrases);
|
||||
|
||||
int ask_password_auto(const char *message, const char *icon, const char *id,
|
||||
usec_t until, bool accept_cached, char ***_passphrases);
|
||||
int ask_password_tty(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);
|
||||
|
@ -181,7 +181,7 @@ static char** user_dirs(
|
||||
if (strv_extend_strv_concat(&res, config_dirs, "/systemd/user") < 0)
|
||||
return NULL;
|
||||
|
||||
if (strv_extend_strv(&res, (char**) config_unit_paths) < 0)
|
||||
if (strv_extend_strv(&res, (char**) config_unit_paths, false) < 0)
|
||||
return NULL;
|
||||
|
||||
if (runtime_dir)
|
||||
@ -203,7 +203,7 @@ static char** user_dirs(
|
||||
if (strv_extend_strv_concat(&res, data_dirs, "/systemd/user") < 0)
|
||||
return NULL;
|
||||
|
||||
if (strv_extend_strv(&res, (char**) data_unit_paths) < 0)
|
||||
if (strv_extend_strv(&res, (char**) data_unit_paths, false) < 0)
|
||||
return NULL;
|
||||
|
||||
if (generator_late)
|
||||
@ -318,7 +318,7 @@ int lookup_paths_init(
|
||||
if (!unit_path)
|
||||
return -ENOMEM;
|
||||
|
||||
r = strv_extend_strv(&p->unit_path, unit_path);
|
||||
r = strv_extend_strv(&p->unit_path, unit_path, false);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
@ -19,13 +19,13 @@
|
||||
along with systemd; If not, see <http://www.gnu.org/licenses/>.
|
||||
***/
|
||||
|
||||
#include <signal.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <signal.h>
|
||||
|
||||
#include "log.h"
|
||||
#include "util.h"
|
||||
#include "process-util.h"
|
||||
#include "util.h"
|
||||
#include "spawn-ask-password-agent.h"
|
||||
|
||||
static pid_t agent_pid = 0;
|
||||
@ -46,9 +46,9 @@ int ask_password_agent_open(void) {
|
||||
SYSTEMD_TTY_ASK_PASSWORD_AGENT_BINARY_PATH,
|
||||
SYSTEMD_TTY_ASK_PASSWORD_AGENT_BINARY_PATH, "--watch", NULL);
|
||||
if (r < 0)
|
||||
log_error_errno(r, "Failed to fork TTY ask password agent: %m");
|
||||
return log_error_errno(r, "Failed to fork TTY ask password agent: %m");
|
||||
|
||||
return r;
|
||||
return 1;
|
||||
}
|
||||
|
||||
void ask_password_agent_close(void) {
|
||||
@ -57,8 +57,8 @@ void ask_password_agent_close(void) {
|
||||
return;
|
||||
|
||||
/* Inform agent that we are done */
|
||||
kill(agent_pid, SIGTERM);
|
||||
kill(agent_pid, SIGCONT);
|
||||
(void) kill(agent_pid, SIGTERM);
|
||||
(void) kill(agent_pid, SIGCONT);
|
||||
(void) wait_for_terminate(agent_pid, NULL);
|
||||
agent_pid = 0;
|
||||
}
|
||||
|
@ -341,11 +341,11 @@ static void test_strv_extend_strv(void) {
|
||||
_cleanup_strv_free_ char **a = NULL, **b = NULL;
|
||||
|
||||
a = strv_new("abc", "def", "ghi", NULL);
|
||||
b = strv_new("jkl", "mno", "pqr", NULL);
|
||||
b = strv_new("jkl", "mno", "abc", "pqr", NULL);
|
||||
assert_se(a);
|
||||
assert_se(b);
|
||||
|
||||
assert_se(strv_extend_strv(&a, b) >= 0);
|
||||
assert_se(strv_extend_strv(&a, b, true) == 3);
|
||||
|
||||
assert_se(streq(a[0], "abc"));
|
||||
assert_se(streq(a[1], "def"));
|
||||
@ -618,6 +618,28 @@ static void test_strv_extend_n(void) {
|
||||
assert_se(v[1] == NULL);
|
||||
}
|
||||
|
||||
static void test_strv_make_nulstr_one(char **l) {
|
||||
_cleanup_free_ char *b = NULL, *c = NULL;
|
||||
_cleanup_strv_free_ char **q = NULL;
|
||||
size_t n, m;
|
||||
|
||||
assert_se(strv_make_nulstr(l, &b, &n) >= 0);
|
||||
assert_se(q = strv_parse_nulstr(b, n));
|
||||
assert_se(strv_equal(l, q));
|
||||
|
||||
assert_se(strv_make_nulstr(q, &c, &m) >= 0);
|
||||
assert_se(m == n);
|
||||
assert_se(memcmp(b, c, m) == 0);
|
||||
}
|
||||
|
||||
static void test_strv_make_nulstr(void) {
|
||||
test_strv_make_nulstr_one(NULL);
|
||||
test_strv_make_nulstr_one(STRV_MAKE(NULL));
|
||||
test_strv_make_nulstr_one(STRV_MAKE("foo"));
|
||||
test_strv_make_nulstr_one(STRV_MAKE("foo", "bar"));
|
||||
test_strv_make_nulstr_one(STRV_MAKE("foo", "bar", "quuux"));
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
test_specifier_printf();
|
||||
test_strv_foreach();
|
||||
@ -678,6 +700,7 @@ int main(int argc, char *argv[]) {
|
||||
test_strv_shell_escape();
|
||||
test_strv_skip();
|
||||
test_strv_extend_n();
|
||||
test_strv_make_nulstr();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -58,9 +58,9 @@ static bool arg_console = false;
|
||||
static int ask_password_plymouth(
|
||||
const char *message,
|
||||
usec_t until,
|
||||
AskPasswordFlags flags,
|
||||
const char *flag_file,
|
||||
bool accept_cached,
|
||||
char ***_passphrases) {
|
||||
char ***ret) {
|
||||
|
||||
_cleanup_close_ int fd = -1, notify = -1;
|
||||
union sockaddr_union sa = PLYMOUTH_SOCKET;
|
||||
@ -75,7 +75,7 @@ static int ask_password_plymouth(
|
||||
POLL_INOTIFY
|
||||
};
|
||||
|
||||
assert(_passphrases);
|
||||
assert(ret);
|
||||
|
||||
if (flag_file) {
|
||||
notify = inotify_init1(IN_CLOEXEC|IN_NONBLOCK);
|
||||
@ -93,17 +93,15 @@ static int ask_password_plymouth(
|
||||
|
||||
r = connect(fd, &sa.sa, offsetof(struct sockaddr_un, sun_path) + 1 + strlen(sa.un.sun_path+1));
|
||||
if (r < 0)
|
||||
return log_error_errno(errno, "Failed to connect to Plymouth: %m");
|
||||
return -errno;
|
||||
|
||||
if (accept_cached) {
|
||||
if (flags & ASK_PASSWORD_ACCEPT_CACHED) {
|
||||
packet = strdup("c");
|
||||
n = 1;
|
||||
} else if (asprintf(&packet, "*\002%c%s%n", (int) (strlen(message) + 1),
|
||||
message, &n) < 0)
|
||||
} else if (asprintf(&packet, "*\002%c%s%n", (int) (strlen(message) + 1), message, &n) < 0)
|
||||
packet = NULL;
|
||||
|
||||
if (!packet)
|
||||
return log_oom();
|
||||
return -ENOMEM;
|
||||
|
||||
r = loop_write(fd, packet, n + 1, true);
|
||||
if (r < 0)
|
||||
@ -131,7 +129,7 @@ static int ask_password_plymouth(
|
||||
if (flag_file && access(flag_file, F_OK) < 0)
|
||||
return -errno;
|
||||
|
||||
j = poll(pollfd, notify > 0 ? 2 : 1, sleep_for);
|
||||
j = poll(pollfd, notify >= 0 ? 2 : 1, sleep_for);
|
||||
if (j < 0) {
|
||||
if (errno == EINTR)
|
||||
continue;
|
||||
@ -140,15 +138,20 @@ static int ask_password_plymouth(
|
||||
} else if (j == 0)
|
||||
return -ETIME;
|
||||
|
||||
if (notify > 0 && pollfd[POLL_INOTIFY].revents != 0)
|
||||
if (notify >= 0 && pollfd[POLL_INOTIFY].revents != 0)
|
||||
flush_fd(notify);
|
||||
|
||||
if (pollfd[POLL_SOCKET].revents == 0)
|
||||
continue;
|
||||
|
||||
k = read(fd, buffer + p, sizeof(buffer) - p);
|
||||
if (k <= 0)
|
||||
return r = k < 0 ? -errno : -EIO;
|
||||
if (k < 0) {
|
||||
if (errno == EINTR || errno == EAGAIN)
|
||||
continue;
|
||||
|
||||
return -errno;
|
||||
} else if (k == 0)
|
||||
return -EIO;
|
||||
|
||||
p += k;
|
||||
|
||||
@ -157,7 +160,7 @@ static int ask_password_plymouth(
|
||||
|
||||
if (buffer[0] == 5) {
|
||||
|
||||
if (accept_cached) {
|
||||
if (flags & ASK_PASSWORD_ACCEPT_CACHED) {
|
||||
/* Hmm, first try with cached
|
||||
* passwords failed, so let's retry
|
||||
* with a normal password request */
|
||||
@ -170,7 +173,7 @@ static int ask_password_plymouth(
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
accept_cached = false;
|
||||
flags &= ~ASK_PASSWORD_ACCEPT_CACHED;
|
||||
p = 0;
|
||||
continue;
|
||||
}
|
||||
@ -198,7 +201,7 @@ static int ask_password_plymouth(
|
||||
if (!l)
|
||||
return -ENOMEM;
|
||||
|
||||
*_passphrases = l;
|
||||
*ret = l;
|
||||
break;
|
||||
|
||||
} else
|
||||
@ -283,7 +286,7 @@ static int parse_password(const char *filename, char **wall) {
|
||||
if (arg_plymouth) {
|
||||
_cleanup_strv_free_ char **passwords = NULL;
|
||||
|
||||
r = ask_password_plymouth(message, not_after, filename, accept_cached, &passwords);
|
||||
r = ask_password_plymouth(message, not_after, accept_cached ? ASK_PASSWORD_ACCEPT_CACHED : 0, filename, &passwords);
|
||||
if (r >= 0) {
|
||||
char **p;
|
||||
|
||||
@ -305,19 +308,19 @@ static int parse_password(const char *filename, char **wall) {
|
||||
}
|
||||
|
||||
} else {
|
||||
int tty_fd = -1;
|
||||
_cleanup_free_ char *password = NULL;
|
||||
int tty_fd = -1;
|
||||
|
||||
if (arg_console) {
|
||||
tty_fd = acquire_terminal("/dev/console", false, false, false, USEC_INFINITY);
|
||||
if (tty_fd < 0)
|
||||
return tty_fd;
|
||||
return log_error_errno(tty_fd, "Failed to acquire /dev/console: %m");
|
||||
}
|
||||
|
||||
r = ask_password_tty(message, not_after, echo, filename, &password);
|
||||
r = ask_password_tty(message, NULL, not_after, echo ? ASK_PASSWORD_ECHO : 0, filename, &password);
|
||||
|
||||
if (arg_console) {
|
||||
safe_close(tty_fd);
|
||||
tty_fd = safe_close(tty_fd);
|
||||
release_terminal();
|
||||
}
|
||||
|
||||
@ -347,12 +350,9 @@ static int parse_password(const char *filename, char **wall) {
|
||||
sa.un.sun_family = AF_UNIX;
|
||||
strncpy(sa.un.sun_path, socket_name, sizeof(sa.un.sun_path));
|
||||
|
||||
r = sendto(socket_fd, packet, packet_length, MSG_NOSIGNAL, &sa.sa,
|
||||
offsetof(struct sockaddr_un, sun_path) + strlen(socket_name));
|
||||
if (r < 0) {
|
||||
log_error_errno(errno, "Failed to send: %m");
|
||||
return r;
|
||||
}
|
||||
r = sendto(socket_fd, packet, packet_length, MSG_NOSIGNAL, &sa.sa, offsetof(struct sockaddr_un, sun_path) + strlen(socket_name));
|
||||
if (r < 0)
|
||||
return log_error_errno(errno, "Failed to send: %m");
|
||||
}
|
||||
|
||||
return 0;
|
||||
@ -360,40 +360,45 @@ static int parse_password(const char *filename, char **wall) {
|
||||
|
||||
static int wall_tty_block(void) {
|
||||
_cleanup_free_ char *p = NULL;
|
||||
int fd, r;
|
||||
dev_t devnr;
|
||||
int fd, r;
|
||||
|
||||
r = get_ctty_devnr(0, &devnr);
|
||||
if (r == -ENXIO) /* We have no controlling tty */
|
||||
return -ENOTTY;
|
||||
if (r < 0)
|
||||
return r;
|
||||
return log_error_errno(r, "Failed to get controlling TTY: %m");
|
||||
|
||||
if (asprintf(&p, "/run/systemd/ask-password-block/%u:%u", major(devnr), minor(devnr)) < 0)
|
||||
return -ENOMEM;
|
||||
return log_oom();
|
||||
|
||||
mkdir_parents_label(p, 0700);
|
||||
mkfifo(p, 0600);
|
||||
|
||||
fd = open(p, O_RDONLY|O_CLOEXEC|O_NONBLOCK|O_NOCTTY);
|
||||
if (fd < 0)
|
||||
return -errno;
|
||||
return log_error_errno(errno, "Failed to open %s: %m", p);
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
static bool wall_tty_match(const char *path, void *userdata) {
|
||||
int fd, r;
|
||||
struct stat st;
|
||||
_cleanup_free_ char *p = NULL;
|
||||
_cleanup_close_ int fd = -1;
|
||||
struct stat st;
|
||||
|
||||
if (!path_is_absolute(path))
|
||||
path = strjoina("/dev/", path);
|
||||
|
||||
r = lstat(path, &st);
|
||||
if (r < 0)
|
||||
if (lstat(path, &st) < 0) {
|
||||
log_debug_errno(errno, "Failed to stat %s: %m", path);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!S_ISCHR(st.st_mode))
|
||||
if (!S_ISCHR(st.st_mode)) {
|
||||
log_debug("%s is not a character device.", path);
|
||||
return true;
|
||||
}
|
||||
|
||||
/* We use named pipes to ensure that wall messages suggesting
|
||||
* password entry are not printed over password prompts
|
||||
@ -403,16 +408,19 @@ static bool wall_tty_match(const char *path, void *userdata) {
|
||||
* advantage that the block will automatically go away if the
|
||||
* process dies. */
|
||||
|
||||
if (asprintf(&p, "/run/systemd/ask-password-block/%u:%u", major(st.st_rdev), minor(st.st_rdev)) < 0)
|
||||
if (asprintf(&p, "/run/systemd/ask-password-block/%u:%u", major(st.st_rdev), minor(st.st_rdev)) < 0) {
|
||||
log_oom();
|
||||
return true;
|
||||
}
|
||||
|
||||
fd = open(p, O_WRONLY|O_CLOEXEC|O_NONBLOCK|O_NOCTTY);
|
||||
if (fd < 0)
|
||||
return true;
|
||||
if (fd < 0) {
|
||||
log_debug_errno(errno, "Failed top open the wall pipe: %m");
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* What, we managed to open the pipe? Then this tty is filtered. */
|
||||
safe_close(fd);
|
||||
return false;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int show_passwords(void) {
|
||||
@ -425,11 +433,10 @@ static int show_passwords(void) {
|
||||
if (errno == ENOENT)
|
||||
return 0;
|
||||
|
||||
log_error_errno(errno, "opendir(/run/systemd/ask-password): %m");
|
||||
return -errno;
|
||||
return log_error_errno(errno, "Failed top open /run/systemd/ask-password: %m");
|
||||
}
|
||||
|
||||
while ((de = readdir(d))) {
|
||||
FOREACH_DIRENT_ALL(de, d, return log_error_errno(errno, "Failed to read directory: %m")) {
|
||||
_cleanup_free_ char *p = NULL, *wall = NULL;
|
||||
int q;
|
||||
|
||||
@ -454,7 +461,7 @@ static int show_passwords(void) {
|
||||
r = q;
|
||||
|
||||
if (wall)
|
||||
utmp_wall(wall, NULL, NULL, wall_tty_match, NULL);
|
||||
(void) utmp_wall(wall, NULL, NULL, wall_tty_match, NULL);
|
||||
}
|
||||
|
||||
return r;
|
||||
@ -474,14 +481,14 @@ static int watch_passwords(void) {
|
||||
|
||||
tty_block_fd = wall_tty_block();
|
||||
|
||||
mkdir_p_label("/run/systemd/ask-password", 0755);
|
||||
(void) mkdir_p_label("/run/systemd/ask-password", 0755);
|
||||
|
||||
notify = inotify_init1(IN_CLOEXEC);
|
||||
if (notify < 0)
|
||||
return -errno;
|
||||
return log_error_errno(errno, "Failed to allocate directory watch: %m");
|
||||
|
||||
if (inotify_add_watch(notify, "/run/systemd/ask-password", IN_CLOSE_WRITE|IN_MOVED_TO) < 0)
|
||||
return -errno;
|
||||
return log_error_errno(errno, "Failed to add /run/systemd/ask-password to directory watch: %m");
|
||||
|
||||
assert_se(sigemptyset(&mask) >= 0);
|
||||
assert_se(sigset_add_many(&mask, SIGINT, SIGTERM, -1) >= 0);
|
||||
@ -489,7 +496,7 @@ static int watch_passwords(void) {
|
||||
|
||||
signal_fd = signalfd(-1, &mask, SFD_NONBLOCK|SFD_CLOEXEC);
|
||||
if (signal_fd < 0)
|
||||
return -errno;
|
||||
return log_error_errno(errno, "Failed to allocate signal file descriptor: %m");
|
||||
|
||||
pollfd[FD_INOTIFY].fd = notify;
|
||||
pollfd[FD_INOTIFY].events = POLLIN;
|
||||
@ -509,7 +516,7 @@ static int watch_passwords(void) {
|
||||
}
|
||||
|
||||
if (pollfd[FD_INOTIFY].revents != 0)
|
||||
flush_fd(notify);
|
||||
(void) flush_fd(notify);
|
||||
|
||||
if (pollfd[FD_SIGNAL].revents != 0)
|
||||
break;
|
||||
@ -633,8 +640,6 @@ int main(int argc, char *argv[]) {
|
||||
r = watch_passwords();
|
||||
else
|
||||
r = show_passwords();
|
||||
if (r < 0)
|
||||
log_error_errno(r, "Error: %m");
|
||||
|
||||
finish:
|
||||
return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
|
||||
|
Loading…
Reference in New Issue
Block a user