summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaniel Mack <github@zonque.org>2015-10-07 15:32:42 +0200
committerDaniel Mack <github@zonque.org>2015-10-07 15:32:42 +0200
commit79bec997c911be7c903db9f7e5d07ab2cd303ed7 (patch)
tree1e6e62138a8920a3210b8063157612b484efbea6
parentf74431288aec78ffdd05be9a519eab3dbe1c4f81 (diff)
parente287086b8aa2558356af225a12d9bfea8e7d61ca (diff)
Merge pull request #1484 from poettering/ask-pass-kernel-keyring
cache harddisk passwords in the kernel keyring
-rw-r--r--CODING_STYLE6
-rw-r--r--configure.ac2
-rw-r--r--man/systemd-ask-password.xml70
-rw-r--r--src/ask-password/ask-password.c91
-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.c7
-rw-r--r--src/modules-load/modules-load.c2
-rw-r--r--src/shared/ask-password-api.c364
-rw-r--r--src/shared/ask-password-api.h20
-rw-r--r--src/shared/path-lookup.c6
-rw-r--r--src/shared/spawn-ask-password-agent.c12
-rw-r--r--src/test/test-strv.c27
-rw-r--r--src/tty-ask-password-agent/tty-ask-password-agent.c109
18 files changed, 597 insertions, 273 deletions
diff --git a/CODING_STYLE b/CODING_STYLE
index cf86de5f62..7fd4af8b87 100644
--- a/CODING_STYLE
+++ b/CODING_STYLE
@@ -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.
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 abfd545c79..1a69d15908 100644
--- a/src/ask-password/ask-password.c
+++ b/src/ask-password/ask-password.c
@@ -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_tty(arg_message, timeout, arg_echo, NULL,
- &password);
- if (r >= 0) {
- puts(password);
- free(password);
- }
-
- } 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);
+ 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;
+ }
- if (!arg_multiple)
- break;
- }
+ STRV_FOREACH(p, l) {
+ puts(*p);
- strv_free(l);
- }
+ 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 9fe3b5dfff..b66c176487 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 2454c8b962..87b3af92bc 100644
--- a/src/core/main.c
+++ b/src/core/main.c
@@ -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();
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 4f0c669ca1..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,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)) {
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 b02cdf9a17..f8cf11b297 100644
--- a/src/shared/ask-password-api.c
+++ b/src/shared/ask-password-api.c
@@ -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 (r < 0) {
- r = -errno;
- log_error_errno(errno, "bind(%s) failed: %m", sa.un.sun_path);
- goto fail;
+ if (bind(fd, &sa.sa, offsetof(struct sockaddr_un, sun_path) + strlen(sa.un.sun_path)) < 0)
+ return -errno;
}
- 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;
+ break;
+ }
- } else if (passphrase[0] == '-') {
+ if (passphrase[0] == '-') {
r = -ECANCELED;
goto finish;
- } else {
- log_error("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:
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;
}
diff --git a/src/shared/ask-password-api.h b/src/shared/ask-password-api.h
index ccb3be0fca..913cad9f8a 100644
--- a/src/shared/ask-password-api.h
+++ b/src/shared/ask-password-api.h
@@ -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);
-
-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/shared/spawn-ask-password-agent.c b/src/shared/spawn-ask-password-agent.c
index 70466d17e5..29db855c67 100644
--- a/src/shared/spawn-ask-password-agent.c
+++ b/src/shared/spawn-ask-password-agent.c
@@ -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;
}
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 4630eb94f1..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);
@@ -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;