From c7ddad5148de6e41445f62a80fb6846dce1a6856 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 6 Oct 2015 15:52:03 +0200 Subject: update CODING_STYLE --- CODING_STYLE | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) 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. -- cgit v1.2.3-54-g00ecf From 0084360296429b2068c1a4d4d4263083a8963b02 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 6 Oct 2015 16:27:24 +0200 Subject: ask-password: various modernizations Primarily clean-up error logging: log either all or no error messages in the various functions. Mostly this means the actual password querying calls no longer will log on their own, but the callers have to do so. Contains various other fixes too, for example ports some code over to use the clean-up macro. Should contain no functional changes. --- src/ask-password/ask-password.c | 42 ++++--- src/firstboot/firstboot.c | 3 +- src/shared/ask-password-api.c | 131 ++++++++++----------- src/shared/ask-password-api.h | 8 +- src/shared/spawn-ask-password-agent.c | 12 +- .../tty-ask-password-agent.c | 75 ++++++------ 6 files changed, 127 insertions(+), 144 deletions(-) diff --git a/src/ask-password/ask-password.c b/src/ask-password/ask-password.c index abfd545c79..bf3fa30f69 100644 --- a/src/ask-password/ask-password.c +++ b/src/ask-password/ask-password.c @@ -20,15 +20,15 @@ ***/ #include -#include #include #include +#include +#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; @@ -154,35 +154,33 @@ int main(int argc, char *argv[]) { timeout = 0; if (arg_use_tty && isatty(STDIN_FILENO)) { - char *password = NULL; + _cleanup_free_ char *password = NULL; - r = ask_password_tty(arg_message, timeout, arg_echo, NULL, - &password); - if (r >= 0) { - puts(password); - free(password); + 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 { - char **l; + _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) { - 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; + } - STRV_FOREACH(p, l) { - puts(*p); + STRV_FOREACH(p, l) { + puts(*p); - if (!arg_multiple) - break; - } - - strv_free(l); + if (!arg_multiple) + break; } } finish: - return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; } diff --git a/src/firstboot/firstboot.c b/src/firstboot/firstboot.c index 4f0c669ca1..8c0724a0e7 100644 --- a/src/firstboot/firstboot.c +++ b/src/firstboot/firstboot.c @@ -477,9 +477,8 @@ static int prompt_root_password(void) { r = ask_password_tty(msg2, 0, false, 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/shared/ask-password-api.c b/src/shared/ask-password-api.c index b02cdf9a17..314f983b20 100644 --- a/src/shared/ask-password-api.c +++ b/src/shared/ask-password-api.c @@ -18,26 +18,28 @@ You should have received a copy of the GNU Lesser General Public License along with systemd; If not, see . ***/ -#include -#include -#include -#include -#include + #include #include -#include -#include -#include +#include +#include #include +#include +#include #include +#include +#include +#include +#include -#include "util.h" #include "formats-util.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" static void backspace_chars(int ttyfd, size_t p) { @@ -97,10 +99,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); @@ -247,52 +249,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 (bind(fd, &sa.sa, offsetof(struct sockaddr_un, sun_path) + strlen(sa.un.sun_path)) < 0) + return -errno; } - if (r < 0) { - r = -errno; - log_error_errno(errno, "bind(%s) failed: %m", sa.un.sun_path); - goto fail; - } - - 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; } @@ -323,24 +311,24 @@ int ask_password_agent( assert(_passphrases); + 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 +336,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; } @@ -381,10 +369,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 +379,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 +405,6 @@ int ask_password_agent( t = now(CLOCK_MONOTONIC); if (until > 0 && until <= t) { - log_notice("Timed out"); r = -ETIME; goto finish; } @@ -429,12 +414,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 +429,6 @@ int ask_password_agent( } if (pollfd[FD_SOCKET].revents != POLLIN) { - log_error("Unexpected poll() event."); r = -EIO; goto finish; } @@ -467,14 +450,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,13 +465,13 @@ 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; } @@ -508,7 +491,7 @@ int ask_password_agent( if (strv_length(l) <= 0) { strv_free(l); - log_error("Invalid packet"); + log_debug("Invalid packet"); continue; } @@ -518,7 +501,7 @@ int ask_password_agent( r = -ECANCELED; goto finish; } else { - log_error("Invalid packet"); + log_debug("Invalid packet"); continue; } @@ -529,25 +512,32 @@ int ask_password_agent( 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) { +int ask_password_auto( + const char *message, + const char *icon, + const char *id, + usec_t until, + bool accept_cached, + char ***_passphrases) { + + int r; + assert(message); assert(_passphrases); if (isatty(STDIN_FILENO)) { - int r; char *s = NULL, **l = NULL; r = ask_password_tty(message, until, false, NULL, &s); @@ -556,10 +546,11 @@ int ask_password_auto(const char *message, const char *icon, const char *id, 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); + return 0; + } + + return ask_password_agent(message, icon, id, until, false, accept_cached, _passphrases); } diff --git a/src/shared/ask-password-api.h b/src/shared/ask-password-api.h index ccb3be0fca..aeb045fe19 100644 --- a/src/shared/ask-password-api.h +++ b/src/shared/ask-password-api.h @@ -26,9 +26,5 @@ #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); +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); 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 . ***/ +#include #include #include -#include #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/tty-ask-password-agent/tty-ask-password-agent.c b/src/tty-ask-password-agent/tty-ask-password-agent.c index 4630eb94f1..e1e2945c06 100644 --- a/src/tty-ask-password-agent/tty-ask-password-agent.c +++ b/src/tty-ask-password-agent/tty-ask-password-agent.c @@ -93,17 +93,16 @@ 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) { 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) @@ -305,19 +304,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); if (arg_console) { - safe_close(tty_fd); + tty_fd = safe_close(tty_fd); release_terminal(); } @@ -347,12 +346,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 +356,43 @@ 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 < 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 +402,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 +427,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 +455,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 +475,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 +490,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 +510,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 +634,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; -- cgit v1.2.3-54-g00ecf From e287086b8aa2558356af225a12d9bfea8e7d61ca Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 7 Oct 2015 11:26:10 +0200 Subject: ask-password: add support for caching passwords in the kernel keyring 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. --- configure.ac | 2 +- man/systemd-ask-password.xml | 70 ++++-- src/ask-password/ask-password.c | 85 ++++---- src/basic/missing.h | 45 ++++ src/basic/strv.c | 88 +++++++- src/basic/strv.h | 3 +- src/core/execute.c | 2 +- src/core/main.c | 2 +- src/cryptsetup/cryptsetup.c | 14 +- src/firstboot/firstboot.c | 4 +- src/modules-load/modules-load.c | 2 +- src/shared/ask-password-api.c | 241 +++++++++++++++++---- src/shared/ask-password-api.h | 16 +- src/shared/path-lookup.c | 6 +- src/test/test-strv.c | 27 ++- .../tty-ask-password-agent.c | 34 +-- 16 files changed, 489 insertions(+), 152 deletions(-) diff --git a/configure.ac b/configure.ac index aabb5e4fe4..3a59a978d3 100644 --- a/configure.ac +++ b/configure.ac @@ -298,7 +298,7 @@ AC_SUBST(CAP_LIBS) AC_CHECK_FUNCS([memfd_create]) AC_CHECK_FUNCS([__secure_getenv secure_getenv]) -AC_CHECK_DECLS([gettid, pivot_root, name_to_handle_at, setns, getrandom, renameat2, kcmp, LO_FLAGS_PARTSCAN], +AC_CHECK_DECLS([gettid, pivot_root, name_to_handle_at, setns, getrandom, renameat2, kcmp, keyctl, key_serial_t, LO_FLAGS_PARTSCAN], [], [], [[ #include #include 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 @@ - + @@ -72,17 +72,28 @@ plugged in or at boot, entering an SSL certificate passphrase for web and VPN servers. - 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 - wall1 - 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 - systemctl1 - invocations. + 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 + wall1 + message + + A command line agent which can be started + temporarily to process queued password + requests + + A TTY agent that is temporarily spawned during + systemctl1 + invocations + Additional password agents may be implemented according to the . + + + 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: + --id=cryptsetup:/dev/sda5. + + + + + 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 + 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 + keyctl1 + to access the cached key via the kernel keyring + directly. Example: --keyname=cryptsetup + + @@ -138,7 +181,7 @@ If passed, accept cached passwords, i.e. - passwords previously typed in. + passwords previously typed in. @@ -166,6 +209,7 @@ systemd1, systemctl1, + keyctl1, plymouth8, wall1 diff --git a/src/ask-password/ask-password.c b/src/ask-password/ask-password.c index bf3fa30f69..1a69d15908 100644 --- a/src/ask-password/ask-password.c +++ b/src/ask-password/ask-password.c @@ -32,24 +32,24 @@ static const char *arg_icon = NULL; static const char *arg_id = NULL; -static const char *arg_message = NULL; -static bool arg_echo = false; -static bool arg_use_tty = true; +static const char *arg_keyname = NULL; +static char *arg_message = NULL; static usec_t arg_timeout = DEFAULT_TIMEOUT_USEC; -static bool arg_accept_cached = false; static bool arg_multiple = false; +static AskPasswordFlags arg_flags = ASK_PASSWORD_PUSH_CACHE; static void help(void) { printf("%s [OPTIONS...] MESSAGE\n\n" "Query the user for a system passphrase, via the TTY or an UI agent.\n\n" - " -h --help Show this help\n" - " --icon=NAME Icon name\n" - " --timeout=SEC Timeout in sec\n" - " --echo Do not mask input (useful for usernames)\n" - " --no-tty Ask question via agent even on TTY\n" - " --accept-cached Accept cached passwords\n" - " --multiple List multiple passwords if available\n" - " --id=ID Query identifier (e.g. cryptsetup:/dev/sda5)\n" + " -h --help Show this help\n" + " --icon=NAME Icon name\n" + " --id=ID Query identifier (e.g. \"cryptsetup:/dev/sda5\")\n" + " --keyname=NAME Kernel key name for caching passwords (e.g. \"cryptsetup\")\n" + " --timeout=SEC Timeout in seconds\n" + " --echo Do not mask input (useful for usernames)\n" + " --no-tty Ask question via agent even on TTY\n" + " --accept-cached Accept cached passwords\n" + " --multiple List multiple passwords if available\n" , program_invocation_short_name); } @@ -62,7 +62,8 @@ static int parse_argv(int argc, char *argv[]) { ARG_NO_TTY, ARG_ACCEPT_CACHED, ARG_MULTIPLE, - ARG_ID + ARG_ID, + ARG_KEYNAME, }; static const struct option options[] = { @@ -74,6 +75,7 @@ static int parse_argv(int argc, char *argv[]) { { "accept-cached", no_argument, NULL, ARG_ACCEPT_CACHED }, { "multiple", no_argument, NULL, ARG_MULTIPLE }, { "id", required_argument, NULL, ARG_ID }, + { "keyname", required_argument, NULL, ARG_KEYNAME }, {} }; @@ -102,15 +104,15 @@ static int parse_argv(int argc, char *argv[]) { break; case ARG_ECHO: - arg_echo = true; + arg_flags |= ASK_PASSWORD_ECHO; break; case ARG_NO_TTY: - arg_use_tty = false; + arg_flags |= ASK_PASSWORD_NO_TTY; break; case ARG_ACCEPT_CACHED: - arg_accept_cached = true; + arg_flags |= ASK_PASSWORD_ACCEPT_CACHED; break; case ARG_MULTIPLE: @@ -121,6 +123,10 @@ static int parse_argv(int argc, char *argv[]) { arg_id = optarg; break; + case ARG_KEYNAME: + arg_keyname = optarg; + break; + case '?': return -EINVAL; @@ -128,18 +134,20 @@ static int parse_argv(int argc, char *argv[]) { assert_not_reached("Unhandled option"); } - if (optind != argc - 1) { - log_error("%s: required argument missing.", program_invocation_short_name); - return -EINVAL; + if (argc > optind) { + arg_message = strv_join(argv + optind, " "); + if (!arg_message) + return log_oom(); } - arg_message = argv[optind]; return 1; } int main(int argc, char *argv[]) { - int r; + _cleanup_strv_free_ char **l = NULL; usec_t timeout; + char **p; + int r; log_parse_environment(); log_open(); @@ -153,34 +161,21 @@ int main(int argc, char *argv[]) { else timeout = 0; - if (arg_use_tty && isatty(STDIN_FILENO)) { - _cleanup_free_ char *password = NULL; - - r = ask_password_tty(arg_message, timeout, arg_echo, NULL, &password); - if (r < 0) { - log_error_errno(r, "Failed to ask for password on terminal: %m"); - goto finish; - } - - puts(password); - } else { - _cleanup_free_ char **l = NULL; - char **p; - - r = ask_password_agent(arg_message, arg_icon, arg_id, timeout, arg_echo, arg_accept_cached, &l); - if (r < 0) { - log_error_errno(r, "Failed to ask for password via agent: %m"); - goto finish; - } + r = ask_password_auto(arg_message, arg_icon, arg_id, arg_keyname, timeout, arg_flags, &l); + if (r < 0) { + log_error_errno(r, "Failed to query password: %m"); + goto finish; + } - STRV_FOREACH(p, l) { - puts(*p); + STRV_FOREACH(p, l) { + puts(*p); - if (!arg_multiple) - break; - } + if (!arg_multiple) + break; } finish: + free(arg_message); + return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; } diff --git a/src/basic/missing.h b/src/basic/missing.h index 1e3af283bb..59e835a466 100644 --- a/src/basic/missing.h +++ b/src/basic/missing.h @@ -1063,3 +1063,48 @@ static inline int kcmp(pid_t pid1, pid_t pid2, int type, unsigned long idx1, uns #ifndef INPUT_PROP_ACCELEROMETER #define INPUT_PROP_ACCELEROMETER 0x06 #endif + +#if !HAVE_DECL_KEY_SERIAL_T +typedef int32_t key_serial_t; +#endif + +#if !HAVE_DECL_KEYCTL +static inline long keyctl(int cmd, unsigned long arg2, unsigned long arg3, unsigned long arg4,unsigned long arg5) { +#if defined(__NR_keyctl) + return syscall(__NR_keyctl, cmd, arg2, arg3, arg4, arg5); +#else + errno = ENOSYS; + return -1; +#endif +} + +static inline key_serial_t add_key(const char *type, const char *description, const void *payload, size_t plen, key_serial_t ringid) { +#if defined (__NR_add_key) + return syscall(__NR_add_key, type, description, payload, plen, ringid); +#else + errno = ENOSYS; + return -1; +#endif +} + +static inline key_serial_t request_key(const char *type, const char *description, const char * callout_info, key_serial_t destringid) { +#if defined (__NR_request_key) + return syscall(__NR_request_key, type, description, callout_info, destringid); +#else + errno = ENOSYS; + return -1; +#endif +} +#endif + +#ifndef KEYCTL_READ +#define KEYCTL_READ 11 +#endif + +#ifndef KEYCTL_SET_TIMEOUT +#define KEYCTL_SET_TIMEOUT 15 +#endif + +#ifndef KEY_SPEC_USER_KEYRING +#define KEY_SPEC_USER_KEYRING -4 +#endif diff --git a/src/basic/strv.c b/src/basic/strv.c index d5169467da..27cb540895 100644 --- a/src/basic/strv.c +++ b/src/basic/strv.c @@ -188,17 +188,48 @@ char **strv_new(const char *x, ...) { return r; } -int strv_extend_strv(char ***a, char **b) { - int r; - char **s; +int strv_extend_strv(char ***a, char **b, bool filter_duplicates) { + char **s, **t; + size_t p, q, i = 0, j; + + assert(a); + + if (strv_isempty(b)) + return 0; + + p = strv_length(*a); + q = strv_length(b); + + t = realloc(*a, sizeof(char*) * (p + q + 1)); + if (!t) + return -ENOMEM; + + t[p] = NULL; + *a = t; STRV_FOREACH(s, b) { - r = strv_extend(a, *s); - if (r < 0) - return r; + + if (filter_duplicates && strv_contains(t, *s)) + continue; + + t[p+i] = strdup(*s); + if (!t[p+i]) + goto rollback; + + i++; + t[p+i] = NULL; } - return 0; + assert(i <= q); + + return (int) i; + +rollback: + for (j = 0; j < i; j++) + free(t[p + j]); + + t[p] = NULL; + return -ENOMEM; } int strv_extend_strv_concat(char ***a, char **b, const char *suffix) { @@ -618,6 +649,41 @@ char **strv_split_nulstr(const char *s) { return r; } +int strv_make_nulstr(char **l, char **p, size_t *q) { + size_t n_allocated = 0, n = 0; + _cleanup_free_ char *m = NULL; + char **i; + + assert(p); + assert(q); + + STRV_FOREACH(i, l) { + size_t z; + + z = strlen(*i); + + if (!GREEDY_REALLOC(m, n_allocated, n + z + 1)) + return -ENOMEM; + + memcpy(m + n, *i, z + 1); + n += z + 1; + } + + if (!m) { + m = new0(char, 1); + if (!m) + return -ENOMEM; + n = 0; + } + + *p = m; + *q = n; + + m = NULL; + + return 0; +} + bool strv_overlap(char **a, char **b) { char **i; @@ -644,8 +710,12 @@ char **strv_sort(char **l) { } bool strv_equal(char **a, char **b) { - if (!a || !b) - return a == b; + + if (strv_isempty(a)) + return strv_isempty(b); + + if (strv_isempty(b)) + return false; for ( ; *a || *b; ++a, ++b) if (!streq_ptr(*a, *b)) diff --git a/src/basic/strv.h b/src/basic/strv.h index 7c1f80230a..e49f443835 100644 --- a/src/basic/strv.h +++ b/src/basic/strv.h @@ -40,7 +40,7 @@ void strv_clear(char **l); char **strv_copy(char * const *l); unsigned strv_length(char * const *l) _pure_; -int strv_extend_strv(char ***a, char **b); +int strv_extend_strv(char ***a, char **b, bool filter_duplicates); int strv_extend_strv_concat(char ***a, char **b, const char *suffix); int strv_extend(char ***l, const char *value); int strv_extendf(char ***l, const char *format, ...) _printf_(2,0); @@ -80,6 +80,7 @@ char *strv_join_quoted(char **l); char **strv_parse_nulstr(const char *s, size_t l); char **strv_split_nulstr(const char *s); +int strv_make_nulstr(char **l, char **p, size_t *n); bool strv_overlap(char **a, char **b) _pure_; diff --git a/src/core/execute.c b/src/core/execute.c index 4664af873f..f5baad05f3 100644 --- a/src/core/execute.c +++ b/src/core/execute.c @@ -2725,7 +2725,7 @@ int exec_command_append(ExecCommand *c, const char *path, ...) { if (!l) return -ENOMEM; - r = strv_extend_strv(&c->argv, l); + r = strv_extend_strv(&c->argv, l, false); if (r < 0) return r; diff --git a/src/core/main.c b/src/core/main.c index 2406832694..2256fc5b33 100644 --- a/src/core/main.c +++ b/src/core/main.c @@ -601,7 +601,7 @@ static int config_parse_join_controllers(const char *unit, for (a = arg_join_controllers; *a; a++) { if (strv_overlap(*a, l)) { - if (strv_extend_strv(&l, *a) < 0) { + if (strv_extend_strv(&l, *a, false) < 0) { strv_free(l); strv_free_free(t); return log_oom(); diff --git a/src/cryptsetup/cryptsetup.c b/src/cryptsetup/cryptsetup.c index 5d5872b7f4..cc03ad3ca8 100644 --- a/src/cryptsetup/cryptsetup.c +++ b/src/cryptsetup/cryptsetup.c @@ -313,14 +313,10 @@ static char *disk_mount_point(const char *label) { } static int get_password(const char *vol, const char *src, usec_t until, bool accept_cached, char ***passwords) { - int r = 0; - char **p; - _cleanup_free_ char *text = NULL; - _cleanup_free_ char *escaped_name = NULL; - char *id; + _cleanup_free_ char *description = NULL, *name_buffer = NULL, *mount_point = NULL, *maj_min = NULL, *text = NULL, *escaped_name = NULL; const char *name = NULL; - _cleanup_free_ char *description = NULL, *name_buffer = NULL, - *mount_point = NULL, *maj_min = NULL; + char **p, *id; + int r = 0; assert(vol); assert(src); @@ -364,7 +360,7 @@ static int get_password(const char *vol, const char *src, usec_t until, bool acc id = strjoina("cryptsetup:", escaped_name); - r = ask_password_auto(text, "drive-harddisk", id, until, accept_cached, passwords); + r = ask_password_auto(text, "drive-harddisk", id, "cryptsetup", until, ASK_PASSWORD_PUSH_CACHE|(accept_cached ? ASK_PASSWORD_ACCEPT_CACHED : 0), passwords); if (r < 0) return log_error_errno(r, "Failed to query password: %m"); @@ -378,7 +374,7 @@ static int get_password(const char *vol, const char *src, usec_t until, bool acc id = strjoina("cryptsetup-verification:", escaped_name); - r = ask_password_auto(text, "drive-harddisk", id, until, false, &passwords2); + r = ask_password_auto(text, "drive-harddisk", id, "cryptsetup", until, ASK_PASSWORD_PUSH_CACHE, &passwords2); if (r < 0) return log_error_errno(r, "Failed to query verification password: %m"); diff --git a/src/firstboot/firstboot.c b/src/firstboot/firstboot.c index 8c0724a0e7..1562ccf0d7 100644 --- a/src/firstboot/firstboot.c +++ b/src/firstboot/firstboot.c @@ -466,7 +466,7 @@ static int prompt_root_password(void) { for (;;) { _cleanup_free_ char *a = NULL, *b = NULL; - r = ask_password_tty(msg1, 0, false, NULL, &a); + r = ask_password_tty(msg1, NULL, 0, 0, NULL, &a); if (r < 0) return log_error_errno(r, "Failed to query root password: %m"); @@ -475,7 +475,7 @@ static int prompt_root_password(void) { break; } - r = ask_password_tty(msg2, 0, false, NULL, &b); + r = ask_password_tty(msg2, NULL, 0, 0, NULL, &b); if (r < 0) { clear_string(a); return log_error_errno(r, "Failed to query root password: %m"); diff --git a/src/modules-load/modules-load.c b/src/modules-load/modules-load.c index 55bb35b9f7..b0a3add3e7 100644 --- a/src/modules-load/modules-load.c +++ b/src/modules-load/modules-load.c @@ -50,7 +50,7 @@ static int add_modules(const char *p) { if (!k) return log_oom(); - if (strv_extend_strv(&arg_proc_cmdline_modules, k) < 0) + if (strv_extend_strv(&arg_proc_cmdline_modules, k, true) < 0) return log_oom(); return 0; diff --git a/src/shared/ask-password-api.c b/src/shared/ask-password-api.c index 314f983b20..f8cf11b297 100644 --- a/src/shared/ask-password-api.c +++ b/src/shared/ask-password-api.c @@ -33,6 +33,7 @@ #include #include "formats-util.h" +#include "missing.h" #include "mkdir.h" #include "random-util.h" #include "signal-util.h" @@ -42,6 +43,133 @@ #include "util.h" #include "ask-password-api.h" +#define KEYRING_TIMEOUT_USEC ((5 * USEC_PER_MINUTE) / 2) + +static int lookup_key(const char *keyname, key_serial_t *ret) { + key_serial_t serial; + + assert(keyname); + assert(ret); + + serial = request_key("user", keyname, NULL, 0); + if (serial == -1) + return -errno; + + *ret = serial; + return 0; +} + +static int retrieve_key(key_serial_t serial, char ***ret) { + _cleanup_free_ char *p = NULL; + long m = 100, n; + char **l; + + assert(ret); + + for (;;) { + p = new(char, m); + if (!p) + return -ENOMEM; + + n = keyctl(KEYCTL_READ, (unsigned long) serial, (unsigned long) p, (unsigned long) m, 0); + if (n < 0) + return -errno; + + if (n < m) + break; + + free(p); + m *= 2; + } + + l = strv_parse_nulstr(p, n); + if (!l) + return -ENOMEM; + + *ret = l; + return 0; +} + +static int add_to_keyring(const char *keyname, AskPasswordFlags flags, char **passwords) { + _cleanup_strv_free_ char **l = NULL; + _cleanup_free_ char *p = NULL; + key_serial_t serial; + size_t n; + int r; + + assert(keyname); + assert(passwords); + + if (!(flags & ASK_PASSWORD_PUSH_CACHE)) + return 0; + + r = lookup_key(keyname, &serial); + if (r >= 0) { + r = retrieve_key(serial, &l); + if (r < 0) + return r; + } else if (r != -ENOKEY) + return r; + + r = strv_extend_strv(&l, passwords, true); + if (r <= 0) + return r; + + r = strv_make_nulstr(l, &p, &n); + if (r < 0) + return r; + + /* Truncate trailing NUL */ + assert(n > 0); + assert(p[n-1] == 0); + + serial = add_key("user", keyname, p, n-1, KEY_SPEC_USER_KEYRING); + if (serial == -1) + return -errno; + + if (keyctl(KEYCTL_SET_TIMEOUT, + (unsigned long) serial, + (unsigned long) DIV_ROUND_UP(KEYRING_TIMEOUT_USEC, USEC_PER_SEC), 0, 0) < 0) + log_debug_errno(errno, "Failed to adjust timeout: %m"); + + log_debug("Added key to keyring as %" PRIi32 ".", serial); + + return 1; +} + +static int add_to_keyring_and_log(const char *keyname, AskPasswordFlags flags, char **passwords) { + int r; + + assert(keyname); + assert(passwords); + + r = add_to_keyring(keyname, flags, passwords); + if (r < 0) + return log_debug_errno(r, "Failed to add password to keyring: %m"); + + return 0; +} + +int ask_password_keyring(const char *keyname, AskPasswordFlags flags, char ***ret) { + + key_serial_t serial; + int r; + + assert(keyname); + assert(ret); + + if (!(flags & ASK_PASSWORD_ACCEPT_CACHED)) + return -EUNATCH; + + r = lookup_key(keyname, &serial); + if (r == -ENOSYS) /* when retrieving the distinction doesn't matter */ + return -ENOKEY; + if (r < 0) + return r; + + return retrieve_key(serial, ret); +} + static void backspace_chars(int ttyfd, size_t p) { if (ttyfd < 0) @@ -56,10 +184,11 @@ static void backspace_chars(int ttyfd, size_t p) { int ask_password_tty( const char *message, + const char *keyname, usec_t until, - bool echo, + AskPasswordFlags flags, const char *flag_file, - char **_passphrase) { + char **ret) { struct termios old_termios, new_termios; char passphrase[LINE_MAX], *x; @@ -68,15 +197,19 @@ int ask_password_tty( _cleanup_close_ int ttyfd = -1, notify = -1; struct pollfd pollfd[2]; bool reset_tty = false; - bool silent_mode = false; bool dirty = false; enum { POLL_TTY, POLL_INOTIFY }; - assert(message); - assert(_passphrase); + assert(ret); + + if (flags & ASK_PASSWORD_NO_TTY) + return -EUNATCH; + + if (!message) + message = "Password:"; if (flag_file) { notify = inotify_init1(IN_CLOEXEC|IN_NONBLOCK); @@ -147,7 +280,7 @@ int ask_password_tty( goto finish; } - k = poll(pollfd, notify > 0 ? 2 : 1, sleep_for); + k = poll(pollfd, notify >= 0 ? 2 : 1, sleep_for); if (k < 0) { if (errno == EINTR) continue; @@ -159,7 +292,7 @@ int ask_password_tty( goto finish; } - if (notify > 0 && pollfd[POLL_INOTIFY].revents != 0) + if (notify >= 0 && pollfd[POLL_INOTIFY].revents != 0) flush_fd(notify); if (pollfd[POLL_TTY].revents == 0) @@ -180,7 +313,7 @@ int ask_password_tty( break; else if (c == 21) { /* C-u */ - if (!silent_mode) + if (!(flags & ASK_PASSWORD_SILENT)) backspace_chars(ttyfd, p); p = 0; @@ -188,28 +321,28 @@ int ask_password_tty( if (p > 0) { - if (!silent_mode) + if (!(flags & ASK_PASSWORD_SILENT)) backspace_chars(ttyfd, 1); p--; - } else if (!dirty && !silent_mode) { + } else if (!dirty && !(flags & ASK_PASSWORD_SILENT)) { - silent_mode = true; + flags |= ASK_PASSWORD_SILENT; /* There are two ways to enter silent * mode. Either by pressing backspace - * as first key (and only as first key), - * or ... */ + * as first key (and only as first + * key), or ... */ if (ttyfd >= 0) loop_write(ttyfd, "(no echo) ", 10, false); } else if (ttyfd >= 0) loop_write(ttyfd, "\a", 1, false); - } else if (c == '\t' && !silent_mode) { + } else if (c == '\t' && !(flags & ASK_PASSWORD_SILENT)) { backspace_chars(ttyfd, p); - silent_mode = true; + flags |= ASK_PASSWORD_SILENT; /* ... or by pressing TAB at any time. */ @@ -223,8 +356,8 @@ int ask_password_tty( passphrase[p++] = c; - if (!silent_mode && ttyfd >= 0) - loop_write(ttyfd, echo ? &c : "*", 1, false); + if (!(flags & ASK_PASSWORD_SILENT) && ttyfd >= 0) + loop_write(ttyfd, (flags & ASK_PASSWORD_ECHO) ? &c : "*", 1, false); dirty = true; } @@ -236,7 +369,10 @@ int ask_password_tty( goto finish; } - *_passphrase = x; + if (keyname) + (void) add_to_keyring_and_log(keyname, flags, STRV_MAKE(x)); + + *ret = x; r = 0; finish: @@ -289,10 +425,10 @@ int ask_password_agent( const char *message, const char *icon, const char *id, + const char *keyname, usec_t until, - bool echo, - bool accept_cached, - char ***_passphrases) { + AskPasswordFlags flags, + char ***ret) { enum { FD_SOCKET, @@ -300,17 +436,20 @@ int ask_password_agent( _FD_MAX }; + _cleanup_close_ int socket_fd = -1, signal_fd = -1, fd = -1; char temp[] = "/run/systemd/ask-password/tmp.XXXXXX"; char final[sizeof(temp)] = ""; - _cleanup_fclose_ FILE *f = NULL; _cleanup_free_ char *socket_name = NULL; - _cleanup_close_ int socket_fd = -1, signal_fd = -1, fd = -1; - sigset_t mask, oldmask; + _cleanup_strv_free_ char **l = NULL; + _cleanup_fclose_ FILE *f = NULL; struct pollfd pollfd[_FD_MAX]; + sigset_t mask, oldmask; int r; - assert(_passphrases); + assert(ret); + if (flags & ASK_PASSWORD_NO_AGENT) + return -EUNATCH; assert_se(sigemptyset(&mask) >= 0); assert_se(sigset_add_many(&mask, SIGINT, SIGTERM, -1) >= 0); @@ -355,8 +494,8 @@ int ask_password_agent( "NotAfter="USEC_FMT"\n", getpid(), socket_name, - accept_cached ? 1 : 0, - echo ? 1 : 0, + (flags & ASK_PASSWORD_ACCEPT_CACHED) ? 1 : 0, + (flags & ASK_PASSWORD_ECHO) ? 1 : 0, until); if (message) @@ -476,38 +615,38 @@ int ask_password_agent( } if (passphrase[0] == '+') { - char **l; - + /* An empty message refers to the empty password */ if (n == 1) l = strv_new("", NULL); else l = strv_parse_nulstr(passphrase+1, n-1); - /* An empty message refers to the empty password */ - if (!l) { r = -ENOMEM; goto finish; } if (strv_length(l) <= 0) { - strv_free(l); + l = strv_free(l); log_debug("Invalid packet"); continue; } - *_passphrases = l; + break; + } - } else if (passphrase[0] == '-') { + if (passphrase[0] == '-') { r = -ECANCELED; goto finish; - } else { - log_debug("Invalid packet"); - continue; } - break; + log_debug("Invalid packet"); } + if (keyname) + (void) add_to_keyring_and_log(keyname, flags, l); + + *ret = l; + l = NULL; r = 0; finish: @@ -520,7 +659,6 @@ finish: (void) unlink(final); assert_se(sigprocmask(SIG_SETMASK, &oldmask, NULL) == 0); - return r; } @@ -528,19 +666,25 @@ int ask_password_auto( const char *message, const char *icon, const char *id, + const char *keyname, usec_t until, - bool accept_cached, - char ***_passphrases) { + AskPasswordFlags flags, + char ***ret) { int r; - assert(message); - assert(_passphrases); + assert(ret); - if (isatty(STDIN_FILENO)) { + if ((flags & ASK_PASSWORD_ACCEPT_CACHED) && keyname) { + r = ask_password_keyring(keyname, flags, ret); + if (r != -ENOKEY) + return r; + } + + if (!(flags & ASK_PASSWORD_NO_TTY) && isatty(STDIN_FILENO)) { char *s = NULL, **l = NULL; - r = ask_password_tty(message, until, false, NULL, &s); + r = ask_password_tty(message, keyname, until, flags, NULL, &s); if (r < 0) return r; @@ -548,9 +692,12 @@ int ask_password_auto( if (r < 0) return -ENOMEM; - *_passphrases = l; + *ret = l; return 0; } - return ask_password_agent(message, icon, id, until, false, accept_cached, _passphrases); + if (!(flags & ASK_PASSWORD_NO_AGENT)) + return ask_password_agent(message, icon, id, keyname, until, flags, ret); + + return -EUNATCH; } diff --git a/src/shared/ask-password-api.h b/src/shared/ask-password-api.h index aeb045fe19..913cad9f8a 100644 --- a/src/shared/ask-password-api.h +++ b/src/shared/ask-password-api.h @@ -25,6 +25,16 @@ #include "time-util.h" -int ask_password_tty(const char *message, usec_t until, bool echo, const char *flag_file, char **_passphrase); -int ask_password_agent(const char *message, const char *icon, const char *id, usec_t until, bool echo, bool accept_cached, char ***_passphrases); -int ask_password_auto(const char *message, const char *icon, const char *id, usec_t until, bool accept_cached, char ***_passphrases); +typedef enum AskPasswordFlags { + ASK_PASSWORD_ACCEPT_CACHED = 1, + ASK_PASSWORD_PUSH_CACHE = 2, + ASK_PASSWORD_ECHO = 4, /* show the password literally while reading, instead of "*" */ + ASK_PASSWORD_SILENT = 8, /* do no show any password at all while reading */ + ASK_PASSWORD_NO_TTY = 16, + ASK_PASSWORD_NO_AGENT = 32, +} AskPasswordFlags; + +int ask_password_tty(const char *message, const char *keyname, usec_t until, AskPasswordFlags flags, const char *flag_file, char **ret); +int ask_password_agent(const char *message, const char *icon, const char *id, const char *keyname, usec_t until, AskPasswordFlags flag, char ***ret); +int ask_password_keyring(const char *keyname, AskPasswordFlags flags, char ***ret); +int ask_password_auto(const char *message, const char *icon, const char *id, const char *keyname, usec_t until, AskPasswordFlags flag, char ***ret); diff --git a/src/shared/path-lookup.c b/src/shared/path-lookup.c index d803bbe07e..34eec959ef 100644 --- a/src/shared/path-lookup.c +++ b/src/shared/path-lookup.c @@ -181,7 +181,7 @@ static char** user_dirs( if (strv_extend_strv_concat(&res, config_dirs, "/systemd/user") < 0) return NULL; - if (strv_extend_strv(&res, (char**) config_unit_paths) < 0) + if (strv_extend_strv(&res, (char**) config_unit_paths, false) < 0) return NULL; if (runtime_dir) @@ -203,7 +203,7 @@ static char** user_dirs( if (strv_extend_strv_concat(&res, data_dirs, "/systemd/user") < 0) return NULL; - if (strv_extend_strv(&res, (char**) data_unit_paths) < 0) + if (strv_extend_strv(&res, (char**) data_unit_paths, false) < 0) return NULL; if (generator_late) @@ -318,7 +318,7 @@ int lookup_paths_init( if (!unit_path) return -ENOMEM; - r = strv_extend_strv(&p->unit_path, unit_path); + r = strv_extend_strv(&p->unit_path, unit_path, false); if (r < 0) return r; } diff --git a/src/test/test-strv.c b/src/test/test-strv.c index 64801f8e01..623c926521 100644 --- a/src/test/test-strv.c +++ b/src/test/test-strv.c @@ -341,11 +341,11 @@ static void test_strv_extend_strv(void) { _cleanup_strv_free_ char **a = NULL, **b = NULL; a = strv_new("abc", "def", "ghi", NULL); - b = strv_new("jkl", "mno", "pqr", NULL); + b = strv_new("jkl", "mno", "abc", "pqr", NULL); assert_se(a); assert_se(b); - assert_se(strv_extend_strv(&a, b) >= 0); + assert_se(strv_extend_strv(&a, b, true) == 3); assert_se(streq(a[0], "abc")); assert_se(streq(a[1], "def")); @@ -618,6 +618,28 @@ static void test_strv_extend_n(void) { assert_se(v[1] == NULL); } +static void test_strv_make_nulstr_one(char **l) { + _cleanup_free_ char *b = NULL, *c = NULL; + _cleanup_strv_free_ char **q = NULL; + size_t n, m; + + assert_se(strv_make_nulstr(l, &b, &n) >= 0); + assert_se(q = strv_parse_nulstr(b, n)); + assert_se(strv_equal(l, q)); + + assert_se(strv_make_nulstr(q, &c, &m) >= 0); + assert_se(m == n); + assert_se(memcmp(b, c, m) == 0); +} + +static void test_strv_make_nulstr(void) { + test_strv_make_nulstr_one(NULL); + test_strv_make_nulstr_one(STRV_MAKE(NULL)); + test_strv_make_nulstr_one(STRV_MAKE("foo")); + test_strv_make_nulstr_one(STRV_MAKE("foo", "bar")); + test_strv_make_nulstr_one(STRV_MAKE("foo", "bar", "quuux")); +} + int main(int argc, char *argv[]) { test_specifier_printf(); test_strv_foreach(); @@ -678,6 +700,7 @@ int main(int argc, char *argv[]) { test_strv_shell_escape(); test_strv_skip(); test_strv_extend_n(); + test_strv_make_nulstr(); return 0; } diff --git a/src/tty-ask-password-agent/tty-ask-password-agent.c b/src/tty-ask-password-agent/tty-ask-password-agent.c index e1e2945c06..5dbc0a9bcc 100644 --- a/src/tty-ask-password-agent/tty-ask-password-agent.c +++ b/src/tty-ask-password-agent/tty-ask-password-agent.c @@ -58,9 +58,9 @@ static bool arg_console = false; static int ask_password_plymouth( const char *message, usec_t until, + AskPasswordFlags flags, const char *flag_file, - bool accept_cached, - char ***_passphrases) { + char ***ret) { _cleanup_close_ int fd = -1, notify = -1; union sockaddr_union sa = PLYMOUTH_SOCKET; @@ -75,7 +75,7 @@ static int ask_password_plymouth( POLL_INOTIFY }; - assert(_passphrases); + assert(ret); if (flag_file) { notify = inotify_init1(IN_CLOEXEC|IN_NONBLOCK); @@ -95,12 +95,11 @@ static int ask_password_plymouth( if (r < 0) return -errno; - if (accept_cached) { + if (flags & ASK_PASSWORD_ACCEPT_CACHED) { packet = strdup("c"); n = 1; } else if (asprintf(&packet, "*\002%c%s%n", (int) (strlen(message) + 1), message, &n) < 0) packet = NULL; - if (!packet) return -ENOMEM; @@ -130,7 +129,7 @@ static int ask_password_plymouth( if (flag_file && access(flag_file, F_OK) < 0) return -errno; - j = poll(pollfd, notify > 0 ? 2 : 1, sleep_for); + j = poll(pollfd, notify >= 0 ? 2 : 1, sleep_for); if (j < 0) { if (errno == EINTR) continue; @@ -139,15 +138,20 @@ static int ask_password_plymouth( } else if (j == 0) return -ETIME; - if (notify > 0 && pollfd[POLL_INOTIFY].revents != 0) + if (notify >= 0 && pollfd[POLL_INOTIFY].revents != 0) flush_fd(notify); if (pollfd[POLL_SOCKET].revents == 0) continue; k = read(fd, buffer + p, sizeof(buffer) - p); - if (k <= 0) - return r = k < 0 ? -errno : -EIO; + if (k < 0) { + if (errno == EINTR || errno == EAGAIN) + continue; + + return -errno; + } else if (k == 0) + return -EIO; p += k; @@ -156,7 +160,7 @@ static int ask_password_plymouth( if (buffer[0] == 5) { - if (accept_cached) { + if (flags & ASK_PASSWORD_ACCEPT_CACHED) { /* Hmm, first try with cached * passwords failed, so let's retry * with a normal password request */ @@ -169,7 +173,7 @@ static int ask_password_plymouth( if (r < 0) return r; - accept_cached = false; + flags &= ~ASK_PASSWORD_ACCEPT_CACHED; p = 0; continue; } @@ -197,7 +201,7 @@ static int ask_password_plymouth( if (!l) return -ENOMEM; - *_passphrases = l; + *ret = l; break; } else @@ -282,7 +286,7 @@ static int parse_password(const char *filename, char **wall) { if (arg_plymouth) { _cleanup_strv_free_ char **passwords = NULL; - r = ask_password_plymouth(message, not_after, filename, accept_cached, &passwords); + r = ask_password_plymouth(message, not_after, accept_cached ? ASK_PASSWORD_ACCEPT_CACHED : 0, filename, &passwords); if (r >= 0) { char **p; @@ -313,7 +317,7 @@ static int parse_password(const char *filename, char **wall) { return log_error_errno(tty_fd, "Failed to acquire /dev/console: %m"); } - r = ask_password_tty(message, not_after, echo, filename, &password); + r = ask_password_tty(message, NULL, not_after, echo ? ASK_PASSWORD_ECHO : 0, filename, &password); if (arg_console) { tty_fd = safe_close(tty_fd); @@ -360,6 +364,8 @@ static int wall_tty_block(void) { int fd, r; r = get_ctty_devnr(0, &devnr); + if (r == -ENXIO) /* We have no controlling tty */ + return -ENOTTY; if (r < 0) return log_error_errno(r, "Failed to get controlling TTY: %m"); -- cgit v1.2.3-54-g00ecf