Skip to content

Commit

Permalink
ask-password: add support for caching passwords in the kernel keyring
Browse files Browse the repository at this point in the history
This adds support for caching harddisk passwords in the kernel keyring
if it is available, thus supporting caching without Plymouth being
around.

This is also useful for hooking up "gdm-auto-login" with the collected
boot-time harddisk password, in order to support gnome keyring
passphrase unlocking via the HDD password, if it is the same.

Any passwords added to the kernel keyring this way have a timeout of
2.5min at which time they are purged from the kernel.
  • Loading branch information
poettering committed Oct 7, 2015
1 parent 0084360 commit e287086
Show file tree
Hide file tree
Showing 16 changed files with 489 additions and 152 deletions.
2 changes: 1 addition & 1 deletion configure.ac
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down
70 changes: 57 additions & 13 deletions man/systemd-ask-password.xml
Original file line number Diff line number Diff line change
@@ -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">

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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>

Expand Down Expand Up @@ -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>
Expand Down Expand Up @@ -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>
Expand Down
85 changes: 40 additions & 45 deletions src/ask-password/ask-password.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

Expand All @@ -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[] = {
Expand All @@ -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 },
{}
};

Expand Down Expand Up @@ -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:
Expand All @@ -121,25 +123,31 @@ static int parse_argv(int argc, char *argv[]) {
arg_id = optarg;
break;

case ARG_KEYNAME:
arg_keyname = optarg;
break;

case '?':
return -EINVAL;

default:
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();
Expand All @@ -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;
}
45 changes: 45 additions & 0 deletions src/basic/missing.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Loading

0 comments on commit e287086

Please sign in to comment.