summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--configure.ac2
-rw-r--r--man/systemd-ask-password.xml70
-rw-r--r--src/ask-password/ask-password.c85
-rw-r--r--src/basic/missing.h45
-rw-r--r--src/basic/strv.c88
-rw-r--r--src/basic/strv.h3
-rw-r--r--src/core/execute.c2
-rw-r--r--src/core/main.c2
-rw-r--r--src/cryptsetup/cryptsetup.c14
-rw-r--r--src/firstboot/firstboot.c4
-rw-r--r--src/modules-load/modules-load.c2
-rw-r--r--src/shared/ask-password-api.c241
-rw-r--r--src/shared/ask-password-api.h16
-rw-r--r--src/shared/path-lookup.c6
-rw-r--r--src/test/test-strv.c27
-rw-r--r--src/tty-ask-password-agent/tty-ask-password-agent.c34
16 files changed, 489 insertions, 152 deletions
diff --git a/configure.ac b/configure.ac
index aabb5e4fe4..3a59a978d3 100644
--- a/configure.ac
+++ b/configure.ac
@@ -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>
diff --git a/man/systemd-ask-password.xml b/man/systemd-ask-password.xml
index 877c71af53..10bb529b81 100644
--- a/man/systemd-ask-password.xml
+++ b/man/systemd-ask-password.xml
@@ -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
@@ -112,6 +123,38 @@
</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>
<listitem><para>Specify the query timeout in seconds. Defaults
@@ -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>
diff --git a/src/ask-password/ask-password.c b/src/ask-password/ask-password.c
index bf3fa30f69..1a69d15908 100644
--- a/src/ask-password/ask-password.c
+++ b/src/ask-password/ask-password.c
@@ -32,24 +32,24 @@
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,34 +161,21 @@ int main(int argc, char *argv[]) {
else
timeout = 0;
- if (arg_use_tty && isatty(STDIN_FILENO)) {
- _cleanup_free_ char *password = NULL;
-
- r = ask_password_tty(arg_message, timeout, arg_echo, NULL, &password);
- if (r < 0) {
- log_error_errno(r, "Failed to ask for password on terminal: %m");
- goto finish;
- }
-
- puts(password);
- } else {
- _cleanup_free_ char **l = NULL;
- char **p;
-
- r = ask_password_agent(arg_message, arg_icon, arg_id, timeout, arg_echo, arg_accept_cached, &l);
- if (r < 0) {
- log_error_errno(r, "Failed to ask for password via agent: %m");
- goto finish;
- }
+ 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;
+ }
- STRV_FOREACH(p, l) {
- puts(*p);
+ STRV_FOREACH(p, l) {
+ puts(*p);
- if (!arg_multiple)
- break;
- }
+ if (!arg_multiple)
+ break;
}
finish:
+ free(arg_message);
+
return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
}
diff --git a/src/basic/missing.h b/src/basic/missing.h
index 1e3af283bb..59e835a466 100644
--- a/src/basic/missing.h
+++ b/src/basic/missing.h
@@ -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
diff --git a/src/basic/strv.c b/src/basic/strv.c
index d5169467da..27cb540895 100644
--- a/src/basic/strv.c
+++ b/src/basic/strv.c
@@ -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))
diff --git a/src/basic/strv.h b/src/basic/strv.h
index 7c1f80230a..e49f443835 100644
--- a/src/basic/strv.h
+++ b/src/basic/strv.h
@@ -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_;
diff --git a/src/core/execute.c b/src/core/execute.c
index 4664af873f..f5baad05f3 100644
--- a/src/core/execute.c
+++ b/src/core/execute.c
@@ -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;
diff --git a/src/core/main.c b/src/core/main.c
index 2406832694..2256fc5b33 100644
--- a/src/core/main.c
+++ b/src/core/main.c
@@ -601,7 +601,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();
diff --git a/src/cryptsetup/cryptsetup.c b/src/cryptsetup/cryptsetup.c
index 5d5872b7f4..cc03ad3ca8 100644
--- a/src/cryptsetup/cryptsetup.c
+++ b/src/cryptsetup/cryptsetup.c
@@ -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");
diff --git a/src/firstboot/firstboot.c b/src/firstboot/firstboot.c
index 8c0724a0e7..1562ccf0d7 100644
--- a/src/firstboot/firstboot.c
+++ b/src/firstboot/firstboot.c
@@ -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,7 +475,7 @@ 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) {
clear_string(a);
return log_error_errno(r, "Failed to query root password: %m");
diff --git a/src/modules-load/modules-load.c b/src/modules-load/modules-load.c
index 55bb35b9f7..b0a3add3e7 100644
--- a/src/modules-load/modules-load.c
+++ b/src/modules-load/modules-load.c
@@ -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;
diff --git a/src/shared/ask-password-api.c b/src/shared/ask-password-api.c
index 314f983b20..f8cf11b297 100644
--- a/src/shared/ask-password-api.c
+++ b/src/shared/ask-password-api.c
@@ -33,6 +33,7 @@
#include <unistd.h>
#include "formats-util.h"
+#include "missing.h"
#include "mkdir.h"
#include "random-util.h"
#include "signal-util.h"
@@ -42,6 +43,133 @@
#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)
@@ -56,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;
@@ -68,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);
@@ -147,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;
@@ -159,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)
@@ -180,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;
@@ -188,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. */
@@ -223,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;
}
@@ -236,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:
@@ -289,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,
@@ -300,17 +436,20 @@ 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);
@@ -355,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)
@@ -476,38 +615,38 @@ int ask_password_agent(
}
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);
+ l = strv_free(l);
log_debug("Invalid packet");
continue;
}
- *_passphrases = l;
+ break;
+ }
- } else if (passphrase[0] == '-') {
+ if (passphrase[0] == '-') {
r = -ECANCELED;
goto finish;
- } else {
- log_debug("Invalid packet");
- continue;
}
- break;
+ log_debug("Invalid packet");
}
+ if (keyname)
+ (void) add_to_keyring_and_log(keyname, flags, l);
+
+ *ret = l;
+ l = NULL;
r = 0;
finish:
@@ -520,7 +659,6 @@ finish:
(void) unlink(final);
assert_se(sigprocmask(SIG_SETMASK, &oldmask, NULL) == 0);
-
return r;
}
@@ -528,19 +666,25 @@ int ask_password_auto(
const char *message,
const char *icon,
const char *id,
+ const char *keyname,
usec_t until,
- bool accept_cached,
- char ***_passphrases) {
+ AskPasswordFlags flags,
+ char ***ret) {
int r;
- assert(message);
- assert(_passphrases);
+ assert(ret);
- if (isatty(STDIN_FILENO)) {
+ 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;
@@ -548,9 +692,12 @@ int ask_password_auto(
if (r < 0)
return -ENOMEM;
- *_passphrases = l;
+ *ret = l;
return 0;
}
- return ask_password_agent(message, icon, id, until, false, accept_cached, _passphrases);
+ if (!(flags & ASK_PASSWORD_NO_AGENT))
+ return ask_password_agent(message, icon, id, keyname, until, flags, ret);
+
+ return -EUNATCH;
}
diff --git a/src/shared/ask-password-api.h b/src/shared/ask-password-api.h
index aeb045fe19..913cad9f8a 100644
--- a/src/shared/ask-password-api.h
+++ b/src/shared/ask-password-api.h
@@ -25,6 +25,16 @@
#include "time-util.h"
-int ask_password_tty(const char *message, usec_t until, bool echo, const char *flag_file, char **_passphrase);
-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);
+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_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);
diff --git a/src/shared/path-lookup.c b/src/shared/path-lookup.c
index d803bbe07e..34eec959ef 100644
--- a/src/shared/path-lookup.c
+++ b/src/shared/path-lookup.c
@@ -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;
}
diff --git a/src/test/test-strv.c b/src/test/test-strv.c
index 64801f8e01..623c926521 100644
--- a/src/test/test-strv.c
+++ b/src/test/test-strv.c
@@ -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;
}
diff --git a/src/tty-ask-password-agent/tty-ask-password-agent.c b/src/tty-ask-password-agent/tty-ask-password-agent.c
index e1e2945c06..5dbc0a9bcc 100644
--- a/src/tty-ask-password-agent/tty-ask-password-agent.c
+++ b/src/tty-ask-password-agent/tty-ask-password-agent.c
@@ -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);
@@ -95,12 +95,11 @@ static int ask_password_plymouth(
if (r < 0)
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)
packet = NULL;
-
if (!packet)
return -ENOMEM;
@@ -130,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;
@@ -139,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;
@@ -156,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 */
@@ -169,7 +173,7 @@ static int ask_password_plymouth(
if (r < 0)
return r;
- accept_cached = false;
+ flags &= ~ASK_PASSWORD_ACCEPT_CACHED;
p = 0;
continue;
}
@@ -197,7 +201,7 @@ static int ask_password_plymouth(
if (!l)
return -ENOMEM;
- *_passphrases = l;
+ *ret = l;
break;
} else
@@ -282,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;
@@ -313,7 +317,7 @@ static int parse_password(const char *filename, char **wall) {
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) {
tty_fd = safe_close(tty_fd);
@@ -360,6 +364,8 @@ static int wall_tty_block(void) {
int fd, r;
r = get_ctty_devnr(0, &devnr);
+ if (r == -ENXIO) /* We have no controlling tty */
+ return -ENOTTY;
if (r < 0)
return log_error_errno(r, "Failed to get controlling TTY: %m");