Commit 21bc923a authored by Lennart Poettering's avatar Lennart Poettering

ask-password: supported plymouth cached passwords

parent 80758717
......@@ -27,7 +27,7 @@ AUTHOR:
Lennart Poettering with major support from Kay Sievers
REQUIREMENTS:
Linux kernel >= 2.6.30 (with autofs4, devtmpfs, cgroups, ipv6)
Linux kernel >= 2.6.30 (with devtmpfs, cgroups; optional but strongly recommended: autofs4, ipv6)
libudev >= 163
dbus >= 1.4.0
libcap
......@@ -47,10 +47,27 @@ REQUIREMENTS:
automake
autoconf
libtool
make, gcc, and similar tools
During runtime you need the following dependencies:
util-linux > v2.18 (requires fsck -l, agetty -s)
sulogin (from sysvinit-tools)
sulogin (from sysvinit-tools, optional but recommended)
plymouth (optional)
dracut (optional)
WARNINGS:
systemd will warn you during boot if /etc/mtab is not a
symlink to /proc/mounts. Please ensure that /etc/mtab is a
proper symlink.
systemd will warn you during boot if /usr is on a different
file system than /. While in systemd itself very little will
break if /usr is on a seperate partition many of its
dependencies very likely will break sooner or later in one
form or another. For example udev rules tend to refer to
binaries in /usr, binaries that link to libraries in /usr or
binaries that refer to data files in /usr. Since these
breakages are not always directly visible systemd will warn
about this, since this kind of file system setup is not really
supported anymore by the basic set of Linux OS components.
......@@ -4,17 +4,11 @@ F15:
* isolate multi-user.target doesn't start a getty@tty1 if we run it from graphical.target
* when plymouth is disabled the console password entry stuff seems to be borked
https://bugzilla.redhat.com/show_bug.cgi?id=655538
* increase password timeout
https://bugzilla.redhat.com/show_bug.cgi?id=677962
* finish syslog socket stuff
* support caching password questions in plymouth and on the console
https://bugzilla.redhat.com/show_bug.cgi?id=677438
* load EnvironmentFile= when starting services, not when reloading configuration
https://bugzilla.redhat.com/show_bug.cgi?id=661282
......
......@@ -32,6 +32,7 @@
#include <sys/signalfd.h>
#include "util.h"
#include "strv.h"
#include "ask-password-api.h"
......@@ -251,12 +252,12 @@ fail:
return r;
}
int ask_password_agent(
const char *message,
const char *icon,
usec_t until,
char **_passphrase) {
bool accept_cached,
char ***_passphrases) {
enum {
FD_SOCKET,
......@@ -273,6 +274,8 @@ int ask_password_agent(
sigset_t mask;
struct pollfd pollfd[_FD_MAX];
assert(_passphrases);
mkdir_p("/dev/.systemd/ask-password", 0755);
if ((fd = mkostemp(temp, O_CLOEXEC|O_CREAT|O_WRONLY)) < 0) {
......@@ -310,9 +313,11 @@ int ask_password_agent(
"[Ask]\n"
"PID=%lu\n"
"Socket=%s\n"
"AcceptCached=%i\n"
"NotAfter=%llu\n",
(unsigned long) getpid(),
socket_name,
accept_cached ? 1 : 0,
(unsigned long long) until);
if (message)
......@@ -384,8 +389,10 @@ int ask_password_agent(
goto finish;
}
if (pollfd[FD_SIGNAL].revents & POLLIN)
break;
if (pollfd[FD_SIGNAL].revents & POLLIN) {
r = -EINTR;
goto finish;
}
if (pollfd[FD_SOCKET].revents != POLLIN) {
log_error("Unexpected poll() event.");
......@@ -395,7 +402,7 @@ int ask_password_agent(
zero(iovec);
iovec.iov_base = passphrase;
iovec.iov_len = sizeof(passphrase)-1;
iovec.iov_len = sizeof(passphrase);
zero(control);
zero(msghdr);
......@@ -435,13 +442,21 @@ int ask_password_agent(
}
if (passphrase[0] == '+') {
passphrase[n] = 0;
char **l;
if (!(*_passphrase = strdup(passphrase+1))) {
if (!(l = strv_parse_nulstr(passphrase+1, n-1))) {
r = -ENOMEM;
goto finish;
}
if (strv_length(l) <= 0) {
strv_free(l);
log_error("Invalid packet");
continue;
}
*_passphrases = l;
} else if (passphrase[0] == '-') {
r = -ECANCELED;
goto finish;
......@@ -481,12 +496,26 @@ finish:
return r;
}
int ask_password_auto(const char *message, const char *icon, usec_t until, char **_passphrase) {
int ask_password_auto(const char *message, const char *icon, usec_t until, bool accept_cached, char ***_passphrases) {
assert(message);
assert(_passphrase);
assert(_passphrases);
if (isatty(STDIN_FILENO)) {
int r;
char *s = NULL, **l = NULL;
if ((r = ask_password_tty(message, until, NULL, &s)) < 0)
return r;
l = strv_new(s, NULL);
free(s);
if (!l)
return -ENOMEM;
*_passphrases = l;
return r;
if (isatty(STDIN_FILENO))
return ask_password_tty(message, until, NULL, _passphrase);
else
return ask_password_agent(message, icon, until, _passphrase);
} else
return ask_password_agent(message, icon, until, accept_cached, _passphrases);
}
......@@ -26,8 +26,8 @@
int ask_password_tty(const char *message, usec_t until, const char *flag_file, char **_passphrase);
int ask_password_agent(const char *message, const char *icon, usec_t until, char **_passphrase);
int ask_password_agent(const char *message, const char *icon, usec_t until, bool accept_cached, char ***_passphrases);
int ask_password_auto(const char *message, const char *icon, usec_t until, char **_passphrase);
int ask_password_auto(const char *message, const char *icon, usec_t until, bool accept_cached, char ***_passphrases);
#endif
......@@ -38,21 +38,26 @@
#include "log.h"
#include "macro.h"
#include "util.h"
#include "strv.h"
#include "ask-password-api.h"
static const char *arg_icon = NULL;
static const char *arg_message = NULL;
static bool arg_use_tty = true;
static usec_t arg_timeout = 60 * USEC_PER_SEC;
static bool arg_accept_cached = false;
static bool arg_multiple = false;
static int 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"
" --no-tty Ask question via agent even on TTY\n",
" -h --help Show this help\n"
" --icon=NAME Icon name\n"
" --timeout=SEC Timeout in sec\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);
return 0;
......@@ -63,15 +68,19 @@ static int parse_argv(int argc, char *argv[]) {
enum {
ARG_ICON = 0x100,
ARG_TIMEOUT,
ARG_NO_TTY
ARG_NO_TTY,
ARG_ACCEPT_CACHED,
ARG_MULTIPLE
};
static const struct option options[] = {
{ "help", no_argument, NULL, 'h' },
{ "icon", required_argument, NULL, ARG_ICON },
{ "timeout", required_argument, NULL, ARG_TIMEOUT },
{ "no-tty", no_argument, NULL, ARG_NO_TTY },
{ NULL, 0, NULL, 0 }
{ "help", no_argument, NULL, 'h' },
{ "icon", required_argument, NULL, ARG_ICON },
{ "timeout", required_argument, NULL, ARG_TIMEOUT },
{ "no-tty", no_argument, NULL, ARG_NO_TTY },
{ "accept-cached", no_argument, NULL, ARG_ACCEPT_CACHED },
{ "multiple", no_argument, NULL, ARG_MULTIPLE },
{ NULL, 0, NULL, 0 }
};
int c;
......@@ -102,6 +111,14 @@ static int parse_argv(int argc, char *argv[]) {
arg_use_tty = false;
break;
case ARG_ACCEPT_CACHED:
arg_accept_cached = true;
break;
case ARG_MULTIPLE:
arg_multiple = true;
break;
case '?':
return -EINVAL;
......@@ -122,7 +139,6 @@ static int parse_argv(int argc, char *argv[]) {
int main(int argc, char *argv[]) {
int r;
char *password = NULL;
log_parse_environment();
log_open();
......@@ -130,18 +146,32 @@ int main(int argc, char *argv[]) {
if ((r = parse_argv(argc, argv)) <= 0)
goto finish;
if (arg_use_tty && isatty(STDIN_FILENO))
r = ask_password_tty(arg_message, now(CLOCK_MONOTONIC) + arg_timeout, NULL, &password);
else
r = ask_password_agent(arg_message, arg_icon, now(CLOCK_MONOTONIC) + arg_timeout, &password);
if (arg_use_tty && isatty(STDIN_FILENO)) {
char *password = NULL;
if ((r = ask_password_tty(arg_message, now(CLOCK_MONOTONIC) + arg_timeout, NULL, &password)) >= 0) {
puts(password);
free(password);
}
} else {
char **l;
if ((r = ask_password_agent(arg_message, arg_icon, now(CLOCK_MONOTONIC) + arg_timeout, arg_accept_cached, &l)) >= 0) {
char **p;
STRV_FOREACH(p, l) {
puts(*p);
if (r >= 0) {
fputs(password, stdout);
fflush(stdout);
if (!arg_multiple)
break;
}
strv_free(l);
}
}
finish:
free(password);
return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
}
......@@ -28,6 +28,7 @@
#include "log.h"
#include "util.h"
#include "strv.h"
#include "ask-password-api.h"
static const char *opt_type = NULL; /* LUKS1 or PLAIN */
......@@ -179,7 +180,7 @@ finish:
int main(int argc, char *argv[]) {
int r = EXIT_FAILURE;
struct crypt_device *cd = NULL;
char *password = NULL, *truncated_cipher = NULL;
char **passwords = NULL, *truncated_cipher = NULL;
const char *cipher = NULL, *cipher_mode = NULL, *hash = NULL, *name = NULL;
char *description = NULL;
......@@ -268,18 +269,19 @@ int main(int argc, char *argv[]) {
for (try = 0; try < opt_tries; try++) {
bool pass_volume_key = false;
free(password);
password = NULL;
strv_free(passwords);
passwords = NULL;
if (!key_file) {
char *text;
char **p;
if (asprintf(&text, "Please enter passphrase for disk %s!", name) < 0) {
log_error("Out of memory");
goto finish;
}
k = ask_password_auto(text, "drive-harddisk", until, &password);
k = ask_password_auto(text, "drive-harddisk", until, try == 0 && !opt_verify, &passwords);
free(text);
if (k < 0) {
......@@ -288,14 +290,16 @@ int main(int argc, char *argv[]) {
}
if (opt_verify) {
char *password2 = NULL;
char **passwords2 = NULL;
assert(strv_length(passwords) == 1);
if (asprintf(&text, "Please enter passphrase for disk %s! (verification)", name) < 0) {
log_error("Out of memory");
goto finish;
}
k = ask_password_auto(text, "drive-harddisk", until, &password2);
k = ask_password_auto(text, "drive-harddisk", until, false, &passwords2);
free(text);
if (k < 0) {
......@@ -303,28 +307,32 @@ int main(int argc, char *argv[]) {
goto finish;
}
if (!streq(password, password2)) {
assert(strv_length(passwords2) == 1);
if (!streq(passwords[0], passwords2[0])) {
log_warning("Passwords did not match, retrying.");
free(password2);
strv_free(passwords2);
continue;
}
free(password2);
strv_free(passwords2);
}
if (strlen(password)+1 < opt_key_size) {
STRV_FOREACH(p, passwords) {
char *c;
/* Pad password if necessary */
if (strlen(*p)+1 >= opt_key_size)
continue;
/* Pad password if necessary */
if (!(c = new(char, opt_key_size))) {
log_error("Out of memory.");
goto finish;
}
strncpy(c, password, opt_key_size);
free(password);
password = c;
strncpy(c, *p, opt_key_size);
free(*p);
*p = c;
}
}
......@@ -367,10 +375,20 @@ int main(int argc, char *argv[]) {
if (key_file)
k = crypt_activate_by_keyfile(cd, argv[2], CRYPT_ANY_SLOT, key_file, opt_key_size, flags);
else if (pass_volume_key)
k = crypt_activate_by_volume_key(cd, argv[2], password, opt_key_size, flags);
else
k = crypt_activate_by_passphrase(cd, argv[2], CRYPT_ANY_SLOT, password, strlen(password), flags);
else {
char **p;
STRV_FOREACH(p, passwords) {
if (pass_volume_key)
k = crypt_activate_by_volume_key(cd, argv[2], *p, opt_key_size, flags);
else
k = crypt_activate_by_passphrase(cd, argv[2], CRYPT_ANY_SLOT, *p, strlen(*p), flags);
if (k >= 0)
break;
}
}
if (k >= 0)
break;
......@@ -421,7 +439,7 @@ finish:
free(truncated_cipher);
free(password);
strv_free(passwords);
free(description);
......
......@@ -577,3 +577,45 @@ char **strv_env_clean(char **l) {
return ret;
}
char **strv_parse_nulstr(const char *s, size_t l) {
const char *p;
unsigned c = 0, i = 0;
char **v;
assert(s || l <= 0);
if (l <= 0)
return strv_new(NULL, NULL);
for (p = s; p < s + l; p++)
if (*p == 0)
c++;
if (s[l-1] != 0)
c++;
if (!(v = new0(char*, c+1)))
return NULL;
p = s;
while (p < s + l) {
const char *e;
e = memchr(p, 0, s + l - p);
if (!(v[i++] = strndup(p, e ? e - p : s + l - p))) {
strv_free(v);
return NULL;
}
if (!e)
break;
p = e + 1;
}
assert(i == c);
return v;
}
......@@ -65,6 +65,8 @@ char *strv_env_get(char **x, const char *n);
char **strv_env_clean(char **l);
char **strv_parse_nulstr(const char *s, size_t l);
#define STRV_FOREACH(s, l) \
for ((s) = (l); (s) && *(s); (s)++)
......
......@@ -48,6 +48,14 @@ int main(int argc, char *argv[]) {
};
char **i, **r, *t, **a, **b;
const char nulstr[] = "fuck\0fuck2\0fuck3\0\0fuck5\0\0xxx";
a = strv_parse_nulstr(nulstr, sizeof(nulstr)-1);
STRV_FOREACH(i, a)
printf("nulstr--%s\n", *i);
strv_free(a);
r = replace_env_argv((char**) line, (char**) env);
......
......@@ -37,6 +37,7 @@
#include "utmp-wtmp.h"
#include "socket-util.h"
#include "ask-password-api.h"
#include "strv.h"
static enum {
ACTION_LIST,
......@@ -48,7 +49,13 @@ static enum {
static bool arg_plymouth = false;
static bool arg_console = false;
static int ask_password_plymouth(const char *message, usec_t until, const char *flag_file, char **_passphrase) {
static int ask_password_plymouth(
const char *message,
usec_t until,
const char *flag_file,
bool accept_cached,
char ***_passphrases) {
int fd = -1, notify = -1;
union sockaddr_union sa;
char *packet = NULL;
......@@ -62,6 +69,8 @@ static int ask_password_plymouth(const char *message, usec_t until, const char *
POLL_INOTIFY
};
assert(_passphrases);
if (flag_file) {
if ((notify = inotify_init1(IN_CLOEXEC|IN_NONBLOCK)) < 0) {
r = -errno;
......@@ -88,7 +97,13 @@ static int ask_password_plymouth(const char *message, usec_t until, const char *
goto finish;
}
if (asprintf(&packet, "*\002%c%s%n", (int) (strlen(message) + 1), message, &n) < 0) {
if (accept_cached) {
packet = strdup("c");
n = 1;
} else
asprintf(&packet, "*\002%c%s%n", (int) (strlen(message) + 1), message, &n);
if (!packet) {
r = -ENOMEM;
goto finish;
}
......@@ -155,15 +170,38 @@ static int ask_password_plymouth(const char *message, usec_t until, const char *
continue;
if (buffer[0] == 5) {
if (accept_cached) {
/* Hmm, first try with cached
* passwords failed, so let's retry
* with a normal password request */
free(packet);
packet = NULL;
if (asprintf(&packet, "*\002%c%s%n", (int) (strlen(message) + 1), message, &n) < 0) {
r = -ENOMEM;
goto finish;
}
if ((k = loop_write(fd, packet, n+1, true)) != n+1) {
r = k < 0 ? (int) k : -EIO;
goto finish;
}
accept_cached = false;
p = 0;
continue;
}
/* No password, because UI not shown */
r = -ENOENT;
goto finish;
} else if (buffer[0] == 2) {
} else if (buffer[0] == 2 || buffer[0] == 9) {
uint32_t size;
char *s;
char **l;
/* One answer */
/* One ore more answers */
if (p < 5)
continue;
......@@ -176,13 +214,14 @@ static int ask_password_plymouth(const char *message, usec_t until, const char *
if (p-5 < size)
continue;
if (!(s = strndup(buffer + 5, size))) {
if (!(l = strv_parse_nulstr(buffer + 5, size))) {
r = -ENOMEM;
goto finish;
}
*_passphrase = s;
*_passphrases = l;
break;
} else {
/* Unknown packet */
r = -EIO;
......@@ -209,12 +248,14 @@ static int parse_password(const char *filename, char **wall) {
uint64_t not_after = 0;
unsigned pid = 0;
int socket_fd = -1;
bool accept_cached = false;
const ConfigItem items[] = {
{ "Socket", config_parse_string, &socket_name, "Ask" },
{ "NotAfter", config_parse_uint64, &not_after, "Ask" },
{ "Message", config_parse_string, &message, "Ask" },
{ "PID", config_parse_unsigned, &pid, "Ask" },
{ "Socket", config_parse_string, &socket_name, "Ask" },
{ "NotAfter", config_parse_uint64, &not_after, "Ask" },
{ "Message", config_parse_string, &message, "Ask" },
{ "PID", config_parse_unsigned, &pid, "Ask" },
{ "AcceptCached", config_parse_bool, &