From 21bc923aa35d455cdef1607eb7022608c705c9f3 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 23 Feb 2011 01:12:07 +0100 Subject: [PATCH] ask-password: supported plymouth cached passwords --- README | 21 ++++++- TODO | 6 -- src/ask-password-api.c | 55 ++++++++++++++----- src/ask-password-api.h | 4 +- src/ask-password.c | 68 ++++++++++++++++------- src/cryptsetup.c | 56 ++++++++++++------- src/strv.c | 42 ++++++++++++++ src/strv.h | 2 + src/test-env-replace.c | 8 +++ src/tty-ask-password-agent.c | 103 ++++++++++++++++++++++++++++------- 10 files changed, 285 insertions(+), 80 deletions(-) diff --git a/README b/README index b2a6dc3e..a183cb36 100644 --- a/README +++ b/README @@ -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. diff --git a/TODO b/TODO index 415eb762..64f7ad7d 100644 --- a/TODO +++ b/TODO @@ -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 diff --git a/src/ask-password-api.c b/src/ask-password-api.c index 9f7023e3..cd663ae0 100644 --- a/src/ask-password-api.c +++ b/src/ask-password-api.c @@ -32,6 +32,7 @@ #include #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); } diff --git a/src/ask-password-api.h b/src/ask-password-api.h index ec858bac..fec8625a 100644 --- a/src/ask-password-api.h +++ b/src/ask-password-api.h @@ -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 diff --git a/src/ask-password.c b/src/ask-password.c index 596c8e08..c7737648 100644 --- a/src/ask-password.c +++ b/src/ask-password.c @@ -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; } diff --git a/src/cryptsetup.c b/src/cryptsetup.c index c80572ae..f72a14ab 100644 --- a/src/cryptsetup.c +++ b/src/cryptsetup.c @@ -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); diff --git a/src/strv.c b/src/strv.c index d1c7b2c3..b1643b35 100644 --- a/src/strv.c +++ b/src/strv.c @@ -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; +} diff --git a/src/strv.h b/src/strv.h index 5af84ee4..064576ce 100644 --- a/src/strv.h +++ b/src/strv.h @@ -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)++) diff --git a/src/test-env-replace.c b/src/test-env-replace.c index 4188c67d..05dbacd7 100644 --- a/src/test-env-replace.c +++ b/src/test-env-replace.c @@ -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); diff --git a/src/tty-ask-password-agent.c b/src/tty-ask-password-agent.c index 655bfb9f..14b01486 100644 --- a/src/tty-ask-password-agent.c +++ b/src/tty-ask-password-agent.c @@ -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, ¬_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, ¬_after, "Ask" }, + { "Message", config_parse_string, &message, "Ask" }, + { "PID", config_parse_unsigned, &pid, "Ask" }, + { "AcceptCached", config_parse_bool, &accept_cached, "Ask" }, { NULL, NULL, NULL, NULL } }; @@ -274,7 +315,7 @@ static int parse_password(const char *filename, char **wall) { struct sockaddr sa; struct sockaddr_un un; } sa; - char *password; + size_t packet_length; assert(arg_action == ACTION_QUERY || arg_action == ACTION_WATCH); @@ -288,10 +329,32 @@ static int parse_password(const char *filename, char **wall) { goto finish; } - if (arg_plymouth) - r = ask_password_plymouth(message, not_after, filename, &password); - else { + if (arg_plymouth) { + char **passwords; + + if ((r = ask_password_plymouth(message, not_after, filename, accept_cached, &passwords)) >= 0) { + char **p; + + packet_length = 1; + STRV_FOREACH(p, passwords) + packet_length += strlen(*p) + 1; + + if (!(packet = new(char, packet_length))) + r = -ENOMEM; + else { + char *d; + + packet[0] = '+'; + d = packet+1; + + STRV_FOREACH(p, passwords) + d = stpcpy(d, *p) + 1; + } + } + + } else { int tty_fd = -1; + char *password; if (arg_console) if ((tty_fd = acquire_terminal("/dev/console", false, false, false)) < 0) { @@ -305,6 +368,11 @@ static int parse_password(const char *filename, char **wall) { close_nointr_nofail(tty_fd); release_terminal(); } + + asprintf(&packet, "+%s", password); + free(password); + + packet_length = strlen(packet); } if (r < 0) { @@ -312,9 +380,6 @@ static int parse_password(const char *filename, char **wall) { goto finish; } - asprintf(&packet, "+%s", password); - free(password); - if (!packet) { log_error("Out of memory"); r = -ENOMEM; @@ -331,7 +396,7 @@ 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)); - if (sendto(socket_fd, packet, strlen(packet), MSG_NOSIGNAL, &sa.sa, offsetof(struct sockaddr_un, sun_path) + strlen(socket_name)) < 0) { + if (sendto(socket_fd, packet, packet_length, MSG_NOSIGNAL, &sa.sa, offsetof(struct sockaddr_un, sun_path) + strlen(socket_name)) < 0) { log_error("Failed to send: %m"); r = -errno; goto finish; -- 2.39.5