diff options
-rw-r--r-- | CODING_STYLE | 6 | ||||
-rw-r--r-- | configure.ac | 2 | ||||
-rw-r--r-- | man/systemd-ask-password.xml | 70 | ||||
-rw-r--r-- | src/ask-password/ask-password.c | 91 | ||||
-rw-r--r-- | src/basic/missing.h | 45 | ||||
-rw-r--r-- | src/basic/strv.c | 88 | ||||
-rw-r--r-- | src/basic/strv.h | 3 | ||||
-rw-r--r-- | src/core/execute.c | 2 | ||||
-rw-r--r-- | src/core/main.c | 2 | ||||
-rw-r--r-- | src/cryptsetup/cryptsetup.c | 14 | ||||
-rw-r--r-- | src/firstboot/firstboot.c | 7 | ||||
-rw-r--r-- | src/modules-load/modules-load.c | 2 | ||||
-rw-r--r-- | src/shared/ask-password-api.c | 364 | ||||
-rw-r--r-- | src/shared/ask-password-api.h | 20 | ||||
-rw-r--r-- | src/shared/path-lookup.c | 6 | ||||
-rw-r--r-- | src/shared/spawn-ask-password-agent.c | 12 | ||||
-rw-r--r-- | src/test/test-strv.c | 27 | ||||
-rw-r--r-- | src/tty-ask-password-agent/tty-ask-password-agent.c | 109 |
18 files changed, 597 insertions, 273 deletions
diff --git a/CODING_STYLE b/CODING_STYLE index cf86de5f62..7fd4af8b87 100644 --- a/CODING_STYLE +++ b/CODING_STYLE @@ -332,9 +332,13 @@ - Avoid leaving long-running child processes around, i.e. fork()s that are not followed quickly by an execv() in the child. Resource management is unclear in this case, and memory CoW will result in - penalties in the parent much much later on. + unexpected penalties in the parent much much later on. - Don't block execution for arbitrary amounts of time using usleep() or a similar call, unless you really know what you do. Just "giving something some time", or so is a lazy excuse. Always wait for the proper event, instead of doing time-based poll loops. + +- To determine the length of a constant string "foo", don't bother + with sizeof("foo")-1, please use strlen("foo") directly. gcc knows + strlen() anyway and turns it into a constant expression if possible. diff --git a/configure.ac b/configure.ac index aabb5e4fe4..3a59a978d3 100644 --- a/configure.ac +++ b/configure.ac @@ -298,7 +298,7 @@ AC_SUBST(CAP_LIBS) AC_CHECK_FUNCS([memfd_create]) AC_CHECK_FUNCS([__secure_getenv secure_getenv]) -AC_CHECK_DECLS([gettid, pivot_root, name_to_handle_at, setns, getrandom, renameat2, kcmp, LO_FLAGS_PARTSCAN], +AC_CHECK_DECLS([gettid, pivot_root, name_to_handle_at, setns, getrandom, renameat2, kcmp, keyctl, key_serial_t, LO_FLAGS_PARTSCAN], [], [], [[ #include <sys/types.h> #include <unistd.h> diff --git a/man/systemd-ask-password.xml b/man/systemd-ask-password.xml index 877c71af53..10bb529b81 100644 --- a/man/systemd-ask-password.xml +++ b/man/systemd-ask-password.xml @@ -1,4 +1,4 @@ -<?xml version='1.0'?> <!--*-nxml-*--> +<?xml version='1.0'?> <!--*- Mode: nxml; nxml-child-indent: 2; indent-tabs-mode: nil -*--> <!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN" "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd"> @@ -72,17 +72,28 @@ plugged in or at boot, entering an SSL certificate passphrase for web and VPN servers.</para> - <para>Existing agents are: a boot-time password agent asking the - user for passwords using Plymouth; a boot-time password agent - querying the user directly on the console; an agent requesting - password input via a - <citerefentry project='man-pages'><refentrytitle>wall</refentrytitle><manvolnum>1</manvolnum></citerefentry> - message; an agent suitable for running in a GNOME session; a - command line agent which can be started temporarily to process - queued password requests; a TTY agent that is temporarily spawned - during - <citerefentry><refentrytitle>systemctl</refentrytitle><manvolnum>1</manvolnum></citerefentry> - invocations.</para> + <para>Existing agents are: + <itemizedlist> + + <listitem><para>A boot-time password agent asking the user for + passwords using Plymouth</para></listitem> + + <listitem><para>A boot-time password agent querying the user + directly on the console</para></listitem> + + <listitem><para>An agent requesting password input via a + <citerefentry + project='man-pages'><refentrytitle>wall</refentrytitle><manvolnum>1</manvolnum></citerefentry> + message</para></listitem> + + <listitem><para>A command line agent which can be started + temporarily to process queued password + requests</para></listitem> + + <listitem><para>A TTY agent that is temporarily spawned during + <citerefentry><refentrytitle>systemctl</refentrytitle><manvolnum>1</manvolnum></citerefentry> + invocations</para></listitem> + </itemizedlist></para> <para>Additional password agents may be implemented according to the <ulink @@ -112,6 +123,38 @@ </varlistentry> <varlistentry> + <term><option>--id=</option></term> + <listitem><para>Specify an identifier for this password + query. This identifier is freely choosable and allows + recognition of queries by involved agents. It should include + the subsystem doing the query and the specific object the + query is done for. Example: + <literal>--id=cryptsetup:/dev/sda5</literal>.</para></listitem> + </varlistentry> + + <varlistentry> + <term><option>--keyname=</option></term> + <listitem><para>Configure a kernel keyring key name to use as + cache for the password. If set, then the tool will try to push + any collected passwords into the kernel keyring of the root + user, as a key of the specified name. If combined with + <option>--accept-cached</option> it will also try to retrieve + the such cached passwords from the key in the kernel keyring + instead of querying the user right-away. By using this option + the kernel keyring may be used as effective cache to avoid + repeatedly asking users for passwords, if there are multiple + objects that may be unlocked with the same password. The + cached key will have a timeout of 2.5min set, after which it + will be purged from the kernel keyring. Note that it is + possible to cache multiple passwords under the same keyname, + in which case they will be stored as NUL-separated list of + passwords. Use + <citerefentry><refentrytitle>keyctl</refentrytitle><manvolnum>1</manvolnum></citerefentry> + to access the cached key via the kernel keyring + directly. Example: <literal>--keyname=cryptsetup</literal></para></listitem> + </varlistentry> + + <varlistentry> <term><option>--timeout=</option></term> <listitem><para>Specify the query timeout in seconds. Defaults @@ -138,7 +181,7 @@ <term><option>--accept-cached</option></term> <listitem><para>If passed, accept cached passwords, i.e. - passwords previously typed in.</para></listitem> + passwords previously typed in. </para></listitem> </varlistentry> <varlistentry> @@ -166,6 +209,7 @@ <para> <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>, <citerefentry><refentrytitle>systemctl</refentrytitle><manvolnum>1</manvolnum></citerefentry>, + <citerefentry><refentrytitle>keyctl</refentrytitle><manvolnum>1</manvolnum></citerefentry>, <citerefentry project='die-net'><refentrytitle>plymouth</refentrytitle><manvolnum>8</manvolnum></citerefentry>, <citerefentry project='man-pages'><refentrytitle>wall</refentrytitle><manvolnum>1</manvolnum></citerefentry> </para> diff --git a/src/ask-password/ask-password.c b/src/ask-password/ask-password.c index abfd545c79..1a69d15908 100644 --- a/src/ask-password/ask-password.c +++ b/src/ask-password/ask-password.c @@ -20,36 +20,36 @@ ***/ #include <errno.h> -#include <unistd.h> #include <getopt.h> #include <stddef.h> +#include <unistd.h> +#include "ask-password-api.h" +#include "def.h" #include "log.h" #include "macro.h" #include "strv.h" -#include "ask-password-api.h" -#include "def.h" static const char *arg_icon = NULL; static const char *arg_id = NULL; -static const char *arg_message = NULL; -static bool arg_echo = false; -static bool arg_use_tty = true; +static const char *arg_keyname = NULL; +static char *arg_message = NULL; static usec_t arg_timeout = DEFAULT_TIMEOUT_USEC; -static bool arg_accept_cached = false; static bool arg_multiple = false; +static AskPasswordFlags arg_flags = ASK_PASSWORD_PUSH_CACHE; static void help(void) { printf("%s [OPTIONS...] MESSAGE\n\n" "Query the user for a system passphrase, via the TTY or an UI agent.\n\n" - " -h --help Show this help\n" - " --icon=NAME Icon name\n" - " --timeout=SEC Timeout in sec\n" - " --echo Do not mask input (useful for usernames)\n" - " --no-tty Ask question via agent even on TTY\n" - " --accept-cached Accept cached passwords\n" - " --multiple List multiple passwords if available\n" - " --id=ID Query identifier (e.g. cryptsetup:/dev/sda5)\n" + " -h --help Show this help\n" + " --icon=NAME Icon name\n" + " --id=ID Query identifier (e.g. \"cryptsetup:/dev/sda5\")\n" + " --keyname=NAME Kernel key name for caching passwords (e.g. \"cryptsetup\")\n" + " --timeout=SEC Timeout in seconds\n" + " --echo Do not mask input (useful for usernames)\n" + " --no-tty Ask question via agent even on TTY\n" + " --accept-cached Accept cached passwords\n" + " --multiple List multiple passwords if available\n" , program_invocation_short_name); } @@ -62,7 +62,8 @@ static int parse_argv(int argc, char *argv[]) { ARG_NO_TTY, ARG_ACCEPT_CACHED, ARG_MULTIPLE, - ARG_ID + ARG_ID, + ARG_KEYNAME, }; static const struct option options[] = { @@ -74,6 +75,7 @@ static int parse_argv(int argc, char *argv[]) { { "accept-cached", no_argument, NULL, ARG_ACCEPT_CACHED }, { "multiple", no_argument, NULL, ARG_MULTIPLE }, { "id", required_argument, NULL, ARG_ID }, + { "keyname", required_argument, NULL, ARG_KEYNAME }, {} }; @@ -102,15 +104,15 @@ static int parse_argv(int argc, char *argv[]) { break; case ARG_ECHO: - arg_echo = true; + arg_flags |= ASK_PASSWORD_ECHO; break; case ARG_NO_TTY: - arg_use_tty = false; + arg_flags |= ASK_PASSWORD_NO_TTY; break; case ARG_ACCEPT_CACHED: - arg_accept_cached = true; + arg_flags |= ASK_PASSWORD_ACCEPT_CACHED; break; case ARG_MULTIPLE: @@ -121,6 +123,10 @@ static int parse_argv(int argc, char *argv[]) { arg_id = optarg; break; + case ARG_KEYNAME: + arg_keyname = optarg; + break; + case '?': return -EINVAL; @@ -128,18 +134,20 @@ static int parse_argv(int argc, char *argv[]) { assert_not_reached("Unhandled option"); } - if (optind != argc - 1) { - log_error("%s: required argument missing.", program_invocation_short_name); - return -EINVAL; + if (argc > optind) { + arg_message = strv_join(argv + optind, " "); + if (!arg_message) + return log_oom(); } - arg_message = argv[optind]; return 1; } int main(int argc, char *argv[]) { - int r; + _cleanup_strv_free_ char **l = NULL; usec_t timeout; + char **p; + int r; log_parse_environment(); log_open(); @@ -153,36 +161,21 @@ int main(int argc, char *argv[]) { else timeout = 0; - if (arg_use_tty && isatty(STDIN_FILENO)) { - char *password = NULL; - - r = ask_password_tty(arg_message, timeout, arg_echo, NULL, - &password); - if (r >= 0) { - puts(password); - free(password); - } - - } else { - char **l; - - r = ask_password_agent(arg_message, arg_icon, arg_id, timeout, - arg_echo, arg_accept_cached, &l); - if (r >= 0) { - char **p; - - STRV_FOREACH(p, l) { - puts(*p); + r = ask_password_auto(arg_message, arg_icon, arg_id, arg_keyname, timeout, arg_flags, &l); + if (r < 0) { + log_error_errno(r, "Failed to query password: %m"); + goto finish; + } - if (!arg_multiple) - break; - } + STRV_FOREACH(p, l) { + puts(*p); - strv_free(l); - } + if (!arg_multiple) + break; } finish: + free(arg_message); return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; } diff --git a/src/basic/missing.h b/src/basic/missing.h index 1e3af283bb..59e835a466 100644 --- a/src/basic/missing.h +++ b/src/basic/missing.h @@ -1063,3 +1063,48 @@ static inline int kcmp(pid_t pid1, pid_t pid2, int type, unsigned long idx1, uns #ifndef INPUT_PROP_ACCELEROMETER #define INPUT_PROP_ACCELEROMETER 0x06 #endif + +#if !HAVE_DECL_KEY_SERIAL_T +typedef int32_t key_serial_t; +#endif + +#if !HAVE_DECL_KEYCTL +static inline long keyctl(int cmd, unsigned long arg2, unsigned long arg3, unsigned long arg4,unsigned long arg5) { +#if defined(__NR_keyctl) + return syscall(__NR_keyctl, cmd, arg2, arg3, arg4, arg5); +#else + errno = ENOSYS; + return -1; +#endif +} + +static inline key_serial_t add_key(const char *type, const char *description, const void *payload, size_t plen, key_serial_t ringid) { +#if defined (__NR_add_key) + return syscall(__NR_add_key, type, description, payload, plen, ringid); +#else + errno = ENOSYS; + return -1; +#endif +} + +static inline key_serial_t request_key(const char *type, const char *description, const char * callout_info, key_serial_t destringid) { +#if defined (__NR_request_key) + return syscall(__NR_request_key, type, description, callout_info, destringid); +#else + errno = ENOSYS; + return -1; +#endif +} +#endif + +#ifndef KEYCTL_READ +#define KEYCTL_READ 11 +#endif + +#ifndef KEYCTL_SET_TIMEOUT +#define KEYCTL_SET_TIMEOUT 15 +#endif + +#ifndef KEY_SPEC_USER_KEYRING +#define KEY_SPEC_USER_KEYRING -4 +#endif diff --git a/src/basic/strv.c b/src/basic/strv.c index 9fe3b5dfff..b66c176487 100644 --- a/src/basic/strv.c +++ b/src/basic/strv.c @@ -188,17 +188,48 @@ char **strv_new(const char *x, ...) { return r; } -int strv_extend_strv(char ***a, char **b) { - int r; - char **s; +int strv_extend_strv(char ***a, char **b, bool filter_duplicates) { + char **s, **t; + size_t p, q, i = 0, j; + + assert(a); + + if (strv_isempty(b)) + return 0; + + p = strv_length(*a); + q = strv_length(b); + + t = realloc(*a, sizeof(char*) * (p + q + 1)); + if (!t) + return -ENOMEM; + + t[p] = NULL; + *a = t; STRV_FOREACH(s, b) { - r = strv_extend(a, *s); - if (r < 0) - return r; + + if (filter_duplicates && strv_contains(t, *s)) + continue; + + t[p+i] = strdup(*s); + if (!t[p+i]) + goto rollback; + + i++; + t[p+i] = NULL; } - return 0; + assert(i <= q); + + return (int) i; + +rollback: + for (j = 0; j < i; j++) + free(t[p + j]); + + t[p] = NULL; + return -ENOMEM; } int strv_extend_strv_concat(char ***a, char **b, const char *suffix) { @@ -618,6 +649,41 @@ char **strv_split_nulstr(const char *s) { return r; } +int strv_make_nulstr(char **l, char **p, size_t *q) { + size_t n_allocated = 0, n = 0; + _cleanup_free_ char *m = NULL; + char **i; + + assert(p); + assert(q); + + STRV_FOREACH(i, l) { + size_t z; + + z = strlen(*i); + + if (!GREEDY_REALLOC(m, n_allocated, n + z + 1)) + return -ENOMEM; + + memcpy(m + n, *i, z + 1); + n += z + 1; + } + + if (!m) { + m = new0(char, 1); + if (!m) + return -ENOMEM; + n = 0; + } + + *p = m; + *q = n; + + m = NULL; + + return 0; +} + bool strv_overlap(char **a, char **b) { char **i; @@ -644,8 +710,12 @@ char **strv_sort(char **l) { } bool strv_equal(char **a, char **b) { - if (!a || !b) - return a == b; + + if (strv_isempty(a)) + return strv_isempty(b); + + if (strv_isempty(b)) + return false; for ( ; *a || *b; ++a, ++b) if (!streq_ptr(*a, *b)) diff --git a/src/basic/strv.h b/src/basic/strv.h index 7c1f80230a..e49f443835 100644 --- a/src/basic/strv.h +++ b/src/basic/strv.h @@ -40,7 +40,7 @@ void strv_clear(char **l); char **strv_copy(char * const *l); unsigned strv_length(char * const *l) _pure_; -int strv_extend_strv(char ***a, char **b); +int strv_extend_strv(char ***a, char **b, bool filter_duplicates); int strv_extend_strv_concat(char ***a, char **b, const char *suffix); int strv_extend(char ***l, const char *value); int strv_extendf(char ***l, const char *format, ...) _printf_(2,0); @@ -80,6 +80,7 @@ char *strv_join_quoted(char **l); char **strv_parse_nulstr(const char *s, size_t l); char **strv_split_nulstr(const char *s); +int strv_make_nulstr(char **l, char **p, size_t *n); bool strv_overlap(char **a, char **b) _pure_; diff --git a/src/core/execute.c b/src/core/execute.c index 4664af873f..f5baad05f3 100644 --- a/src/core/execute.c +++ b/src/core/execute.c @@ -2725,7 +2725,7 @@ int exec_command_append(ExecCommand *c, const char *path, ...) { if (!l) return -ENOMEM; - r = strv_extend_strv(&c->argv, l); + r = strv_extend_strv(&c->argv, l, false); if (r < 0) return r; diff --git a/src/core/main.c b/src/core/main.c index 2454c8b962..87b3af92bc 100644 --- a/src/core/main.c +++ b/src/core/main.c @@ -599,7 +599,7 @@ static int config_parse_join_controllers(const char *unit, for (a = arg_join_controllers; *a; a++) { if (strv_overlap(*a, l)) { - if (strv_extend_strv(&l, *a) < 0) { + if (strv_extend_strv(&l, *a, false) < 0) { strv_free(l); strv_free_free(t); return log_oom(); diff --git a/src/cryptsetup/cryptsetup.c b/src/cryptsetup/cryptsetup.c index 5d5872b7f4..cc03ad3ca8 100644 --- a/src/cryptsetup/cryptsetup.c +++ b/src/cryptsetup/cryptsetup.c @@ -313,14 +313,10 @@ static char *disk_mount_point(const char *label) { } static int get_password(const char *vol, const char *src, usec_t until, bool accept_cached, char ***passwords) { - int r = 0; - char **p; - _cleanup_free_ char *text = NULL; - _cleanup_free_ char *escaped_name = NULL; - char *id; + _cleanup_free_ char *description = NULL, *name_buffer = NULL, *mount_point = NULL, *maj_min = NULL, *text = NULL, *escaped_name = NULL; const char *name = NULL; - _cleanup_free_ char *description = NULL, *name_buffer = NULL, - *mount_point = NULL, *maj_min = NULL; + char **p, *id; + int r = 0; assert(vol); assert(src); @@ -364,7 +360,7 @@ static int get_password(const char *vol, const char *src, usec_t until, bool acc id = strjoina("cryptsetup:", escaped_name); - r = ask_password_auto(text, "drive-harddisk", id, until, accept_cached, passwords); + r = ask_password_auto(text, "drive-harddisk", id, "cryptsetup", until, ASK_PASSWORD_PUSH_CACHE|(accept_cached ? ASK_PASSWORD_ACCEPT_CACHED : 0), passwords); if (r < 0) return log_error_errno(r, "Failed to query password: %m"); @@ -378,7 +374,7 @@ static int get_password(const char *vol, const char *src, usec_t until, bool acc id = strjoina("cryptsetup-verification:", escaped_name); - r = ask_password_auto(text, "drive-harddisk", id, until, false, &passwords2); + r = ask_password_auto(text, "drive-harddisk", id, "cryptsetup", until, ASK_PASSWORD_PUSH_CACHE, &passwords2); if (r < 0) return log_error_errno(r, "Failed to query verification password: %m"); diff --git a/src/firstboot/firstboot.c b/src/firstboot/firstboot.c index 4f0c669ca1..1562ccf0d7 100644 --- a/src/firstboot/firstboot.c +++ b/src/firstboot/firstboot.c @@ -466,7 +466,7 @@ static int prompt_root_password(void) { for (;;) { _cleanup_free_ char *a = NULL, *b = NULL; - r = ask_password_tty(msg1, 0, false, NULL, &a); + r = ask_password_tty(msg1, NULL, 0, 0, NULL, &a); if (r < 0) return log_error_errno(r, "Failed to query root password: %m"); @@ -475,11 +475,10 @@ static int prompt_root_password(void) { break; } - r = ask_password_tty(msg2, 0, false, NULL, &b); + r = ask_password_tty(msg2, NULL, 0, 0, NULL, &b); if (r < 0) { - log_error_errno(r, "Failed to query root password: %m"); clear_string(a); - return r; + return log_error_errno(r, "Failed to query root password: %m"); } if (!streq(a, b)) { diff --git a/src/modules-load/modules-load.c b/src/modules-load/modules-load.c index 55bb35b9f7..b0a3add3e7 100644 --- a/src/modules-load/modules-load.c +++ b/src/modules-load/modules-load.c @@ -50,7 +50,7 @@ static int add_modules(const char *p) { if (!k) return log_oom(); - if (strv_extend_strv(&arg_proc_cmdline_modules, k) < 0) + if (strv_extend_strv(&arg_proc_cmdline_modules, k, true) < 0) return log_oom(); return 0; diff --git a/src/shared/ask-password-api.c b/src/shared/ask-password-api.c index b02cdf9a17..f8cf11b297 100644 --- a/src/shared/ask-password-api.c +++ b/src/shared/ask-password-api.c @@ -18,28 +18,158 @@ You should have received a copy of the GNU Lesser General Public License along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include <stdbool.h> -#include <termios.h> -#include <unistd.h> -#include <poll.h> -#include <sys/inotify.h> + #include <errno.h> #include <fcntl.h> -#include <sys/socket.h> -#include <string.h> -#include <sys/un.h> +#include <poll.h> +#include <stdbool.h> #include <stddef.h> +#include <string.h> +#include <sys/inotify.h> #include <sys/signalfd.h> +#include <sys/socket.h> +#include <sys/un.h> +#include <termios.h> +#include <unistd.h> -#include "util.h" #include "formats-util.h" +#include "missing.h" #include "mkdir.h" -#include "strv.h" #include "random-util.h" -#include "terminal-util.h" #include "signal-util.h" +#include "socket-util.h" +#include "strv.h" +#include "terminal-util.h" +#include "util.h" #include "ask-password-api.h" +#define KEYRING_TIMEOUT_USEC ((5 * USEC_PER_MINUTE) / 2) + +static int lookup_key(const char *keyname, key_serial_t *ret) { + key_serial_t serial; + + assert(keyname); + assert(ret); + + serial = request_key("user", keyname, NULL, 0); + if (serial == -1) + return -errno; + + *ret = serial; + return 0; +} + +static int retrieve_key(key_serial_t serial, char ***ret) { + _cleanup_free_ char *p = NULL; + long m = 100, n; + char **l; + + assert(ret); + + for (;;) { + p = new(char, m); + if (!p) + return -ENOMEM; + + n = keyctl(KEYCTL_READ, (unsigned long) serial, (unsigned long) p, (unsigned long) m, 0); + if (n < 0) + return -errno; + + if (n < m) + break; + + free(p); + m *= 2; + } + + l = strv_parse_nulstr(p, n); + if (!l) + return -ENOMEM; + + *ret = l; + return 0; +} + +static int add_to_keyring(const char *keyname, AskPasswordFlags flags, char **passwords) { + _cleanup_strv_free_ char **l = NULL; + _cleanup_free_ char *p = NULL; + key_serial_t serial; + size_t n; + int r; + + assert(keyname); + assert(passwords); + + if (!(flags & ASK_PASSWORD_PUSH_CACHE)) + return 0; + + r = lookup_key(keyname, &serial); + if (r >= 0) { + r = retrieve_key(serial, &l); + if (r < 0) + return r; + } else if (r != -ENOKEY) + return r; + + r = strv_extend_strv(&l, passwords, true); + if (r <= 0) + return r; + + r = strv_make_nulstr(l, &p, &n); + if (r < 0) + return r; + + /* Truncate trailing NUL */ + assert(n > 0); + assert(p[n-1] == 0); + + serial = add_key("user", keyname, p, n-1, KEY_SPEC_USER_KEYRING); + if (serial == -1) + return -errno; + + if (keyctl(KEYCTL_SET_TIMEOUT, + (unsigned long) serial, + (unsigned long) DIV_ROUND_UP(KEYRING_TIMEOUT_USEC, USEC_PER_SEC), 0, 0) < 0) + log_debug_errno(errno, "Failed to adjust timeout: %m"); + + log_debug("Added key to keyring as %" PRIi32 ".", serial); + + return 1; +} + +static int add_to_keyring_and_log(const char *keyname, AskPasswordFlags flags, char **passwords) { + int r; + + assert(keyname); + assert(passwords); + + r = add_to_keyring(keyname, flags, passwords); + if (r < 0) + return log_debug_errno(r, "Failed to add password to keyring: %m"); + + return 0; +} + +int ask_password_keyring(const char *keyname, AskPasswordFlags flags, char ***ret) { + + key_serial_t serial; + int r; + + assert(keyname); + assert(ret); + + if (!(flags & ASK_PASSWORD_ACCEPT_CACHED)) + return -EUNATCH; + + r = lookup_key(keyname, &serial); + if (r == -ENOSYS) /* when retrieving the distinction doesn't matter */ + return -ENOKEY; + if (r < 0) + return r; + + return retrieve_key(serial, ret); +} + static void backspace_chars(int ttyfd, size_t p) { if (ttyfd < 0) @@ -54,10 +184,11 @@ static void backspace_chars(int ttyfd, size_t p) { int ask_password_tty( const char *message, + const char *keyname, usec_t until, - bool echo, + AskPasswordFlags flags, const char *flag_file, - char **_passphrase) { + char **ret) { struct termios old_termios, new_termios; char passphrase[LINE_MAX], *x; @@ -66,15 +197,19 @@ int ask_password_tty( _cleanup_close_ int ttyfd = -1, notify = -1; struct pollfd pollfd[2]; bool reset_tty = false; - bool silent_mode = false; bool dirty = false; enum { POLL_TTY, POLL_INOTIFY }; - assert(message); - assert(_passphrase); + assert(ret); + + if (flags & ASK_PASSWORD_NO_TTY) + return -EUNATCH; + + if (!message) + message = "Password:"; if (flag_file) { notify = inotify_init1(IN_CLOEXEC|IN_NONBLOCK); @@ -97,10 +232,10 @@ int ask_password_tty( goto finish; } - loop_write(ttyfd, ANSI_HIGHLIGHT, sizeof(ANSI_HIGHLIGHT)-1, false); + loop_write(ttyfd, ANSI_HIGHLIGHT, strlen(ANSI_HIGHLIGHT), false); loop_write(ttyfd, message, strlen(message), false); loop_write(ttyfd, " ", 1, false); - loop_write(ttyfd, ANSI_NORMAL, sizeof(ANSI_NORMAL)-1, false); + loop_write(ttyfd, ANSI_NORMAL, strlen(ANSI_NORMAL), false); new_termios = old_termios; new_termios.c_lflag &= ~(ICANON|ECHO); @@ -145,7 +280,7 @@ int ask_password_tty( goto finish; } - k = poll(pollfd, notify > 0 ? 2 : 1, sleep_for); + k = poll(pollfd, notify >= 0 ? 2 : 1, sleep_for); if (k < 0) { if (errno == EINTR) continue; @@ -157,7 +292,7 @@ int ask_password_tty( goto finish; } - if (notify > 0 && pollfd[POLL_INOTIFY].revents != 0) + if (notify >= 0 && pollfd[POLL_INOTIFY].revents != 0) flush_fd(notify); if (pollfd[POLL_TTY].revents == 0) @@ -178,7 +313,7 @@ int ask_password_tty( break; else if (c == 21) { /* C-u */ - if (!silent_mode) + if (!(flags & ASK_PASSWORD_SILENT)) backspace_chars(ttyfd, p); p = 0; @@ -186,28 +321,28 @@ int ask_password_tty( if (p > 0) { - if (!silent_mode) + if (!(flags & ASK_PASSWORD_SILENT)) backspace_chars(ttyfd, 1); p--; - } else if (!dirty && !silent_mode) { + } else if (!dirty && !(flags & ASK_PASSWORD_SILENT)) { - silent_mode = true; + flags |= ASK_PASSWORD_SILENT; /* There are two ways to enter silent * mode. Either by pressing backspace - * as first key (and only as first key), - * or ... */ + * as first key (and only as first + * key), or ... */ if (ttyfd >= 0) loop_write(ttyfd, "(no echo) ", 10, false); } else if (ttyfd >= 0) loop_write(ttyfd, "\a", 1, false); - } else if (c == '\t' && !silent_mode) { + } else if (c == '\t' && !(flags & ASK_PASSWORD_SILENT)) { backspace_chars(ttyfd, p); - silent_mode = true; + flags |= ASK_PASSWORD_SILENT; /* ... or by pressing TAB at any time. */ @@ -221,8 +356,8 @@ int ask_password_tty( passphrase[p++] = c; - if (!silent_mode && ttyfd >= 0) - loop_write(ttyfd, echo ? &c : "*", 1, false); + if (!(flags & ASK_PASSWORD_SILENT) && ttyfd >= 0) + loop_write(ttyfd, (flags & ASK_PASSWORD_ECHO) ? &c : "*", 1, false); dirty = true; } @@ -234,7 +369,10 @@ int ask_password_tty( goto finish; } - *_passphrase = x; + if (keyname) + (void) add_to_keyring_and_log(keyname, flags, STRV_MAKE(x)); + + *ret = x; r = 0; finish: @@ -247,52 +385,38 @@ finish: } static int create_socket(char **name) { - int fd; - union { - struct sockaddr sa; - struct sockaddr_un un; - } sa = { + union sockaddr_union sa = { .un.sun_family = AF_UNIX, }; - int one = 1; - int r = 0; + _cleanup_close_ int fd = -1; + static const int one = 1; char *c; + int r; assert(name); fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0); if (fd < 0) - return log_error_errno(errno, "socket() failed: %m"); + return -errno; snprintf(sa.un.sun_path, sizeof(sa.un.sun_path)-1, "/run/systemd/ask-password/sck.%" PRIx64, random_u64()); RUN_WITH_UMASK(0177) { - r = bind(fd, &sa.sa, offsetof(struct sockaddr_un, sun_path) + strlen(sa.un.sun_path)); - } - - if (r < 0) { - r = -errno; - log_error_errno(errno, "bind(%s) failed: %m", sa.un.sun_path); - goto fail; + if (bind(fd, &sa.sa, offsetof(struct sockaddr_un, sun_path) + strlen(sa.un.sun_path)) < 0) + return -errno; } - if (setsockopt(fd, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one)) < 0) { - r = -errno; - log_error_errno(errno, "SO_PASSCRED failed: %m"); - goto fail; - } + if (setsockopt(fd, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one)) < 0) + return -errno; c = strdup(sa.un.sun_path); - if (!c) { - r = log_oom(); - goto fail; - } + if (!c) + return -ENOMEM; *name = c; - return fd; -fail: - safe_close(fd); + r = fd; + fd = -1; return r; } @@ -301,10 +425,10 @@ int ask_password_agent( const char *message, const char *icon, const char *id, + const char *keyname, usec_t until, - bool echo, - bool accept_cached, - char ***_passphrases) { + AskPasswordFlags flags, + char ***ret) { enum { FD_SOCKET, @@ -312,35 +436,38 @@ int ask_password_agent( _FD_MAX }; + _cleanup_close_ int socket_fd = -1, signal_fd = -1, fd = -1; char temp[] = "/run/systemd/ask-password/tmp.XXXXXX"; char final[sizeof(temp)] = ""; - _cleanup_fclose_ FILE *f = NULL; _cleanup_free_ char *socket_name = NULL; - _cleanup_close_ int socket_fd = -1, signal_fd = -1, fd = -1; - sigset_t mask, oldmask; + _cleanup_strv_free_ char **l = NULL; + _cleanup_fclose_ FILE *f = NULL; struct pollfd pollfd[_FD_MAX]; + sigset_t mask, oldmask; int r; - assert(_passphrases); + assert(ret); + + if (flags & ASK_PASSWORD_NO_AGENT) + return -EUNATCH; assert_se(sigemptyset(&mask) >= 0); assert_se(sigset_add_many(&mask, SIGINT, SIGTERM, -1) >= 0); assert_se(sigprocmask(SIG_BLOCK, &mask, &oldmask) >= 0); - mkdir_p_label("/run/systemd/ask-password", 0755); + (void) mkdir_p_label("/run/systemd/ask-password", 0755); fd = mkostemp_safe(temp, O_WRONLY|O_CLOEXEC); if (fd < 0) { - r = log_error_errno(errno, - "Failed to create password file: %m"); + r = -errno; goto finish; } - fchmod(fd, 0644); + (void) fchmod(fd, 0644); f = fdopen(fd, "w"); if (!f) { - r = log_error_errno(errno, "Failed to allocate FILE: %m"); + r = -errno; goto finish; } @@ -348,7 +475,7 @@ int ask_password_agent( signal_fd = signalfd(-1, &mask, SFD_NONBLOCK|SFD_CLOEXEC); if (signal_fd < 0) { - r = log_error_errno(errno, "signalfd(): %m"); + r = -errno; goto finish; } @@ -367,8 +494,8 @@ int ask_password_agent( "NotAfter="USEC_FMT"\n", getpid(), socket_name, - accept_cached ? 1 : 0, - echo ? 1 : 0, + (flags & ASK_PASSWORD_ACCEPT_CACHED) ? 1 : 0, + (flags & ASK_PASSWORD_ECHO) ? 1 : 0, until); if (message) @@ -381,10 +508,8 @@ int ask_password_agent( fprintf(f, "Id=%s\n", id); r = fflush_and_check(f); - if (r < 0) { - log_error_errno(r, "Failed to write query file: %m"); + if (r < 0) goto finish; - } memcpy(final, temp, sizeof(temp)); @@ -393,7 +518,7 @@ int ask_password_agent( final[sizeof(final)-9] = 'k'; if (rename(temp, final) < 0) { - r = log_error_errno(errno, "Failed to rename query file: %m"); + r = -errno; goto finish; } @@ -419,7 +544,6 @@ int ask_password_agent( t = now(CLOCK_MONOTONIC); if (until > 0 && until <= t) { - log_notice("Timed out"); r = -ETIME; goto finish; } @@ -429,12 +553,11 @@ int ask_password_agent( if (errno == EINTR) continue; - r = log_error_errno(errno, "poll() failed: %m"); + r = -errno; goto finish; } if (k <= 0) { - log_notice("Timed out"); r = -ETIME; goto finish; } @@ -445,7 +568,6 @@ int ask_password_agent( } if (pollfd[FD_SOCKET].revents != POLLIN) { - log_error("Unexpected poll() event."); r = -EIO; goto finish; } @@ -467,14 +589,14 @@ int ask_password_agent( errno == EINTR) continue; - r = log_error_errno(errno, "recvmsg() failed: %m"); + r = -errno; goto finish; } cmsg_close_all(&msghdr); if (n <= 0) { - log_error("Message too short"); + log_debug("Message too short"); continue; } @@ -482,84 +604,100 @@ int ask_password_agent( control.cmsghdr.cmsg_level != SOL_SOCKET || control.cmsghdr.cmsg_type != SCM_CREDENTIALS || control.cmsghdr.cmsg_len != CMSG_LEN(sizeof(struct ucred))) { - log_warning("Received message without credentials. Ignoring."); + log_debug("Received message without credentials. Ignoring."); continue; } ucred = (struct ucred*) CMSG_DATA(&control.cmsghdr); if (ucred->uid != 0) { - log_warning("Got request from unprivileged user. Ignoring."); + log_debug("Got request from unprivileged user. Ignoring."); continue; } if (passphrase[0] == '+') { - char **l; - + /* An empty message refers to the empty password */ if (n == 1) l = strv_new("", NULL); else l = strv_parse_nulstr(passphrase+1, n-1); - /* An empty message refers to the empty password */ - if (!l) { r = -ENOMEM; goto finish; } if (strv_length(l) <= 0) { - strv_free(l); - log_error("Invalid packet"); + l = strv_free(l); + log_debug("Invalid packet"); continue; } - *_passphrases = l; + break; + } - } else if (passphrase[0] == '-') { + if (passphrase[0] == '-') { r = -ECANCELED; goto finish; - } else { - log_error("Invalid packet"); - continue; } - break; + log_debug("Invalid packet"); } + if (keyname) + (void) add_to_keyring_and_log(keyname, flags, l); + + *ret = l; + l = NULL; r = 0; finish: if (socket_name) - unlink(socket_name); + (void) unlink(socket_name); - unlink(temp); + (void) unlink(temp); if (final[0]) - unlink(final); + (void) unlink(final); assert_se(sigprocmask(SIG_SETMASK, &oldmask, NULL) == 0); - return r; } -int ask_password_auto(const char *message, const char *icon, const char *id, - usec_t until, bool accept_cached, char ***_passphrases) { - assert(message); - assert(_passphrases); +int ask_password_auto( + const char *message, + const char *icon, + const char *id, + const char *keyname, + usec_t until, + AskPasswordFlags flags, + char ***ret) { - if (isatty(STDIN_FILENO)) { - int r; + int r; + + assert(ret); + + if ((flags & ASK_PASSWORD_ACCEPT_CACHED) && keyname) { + r = ask_password_keyring(keyname, flags, ret); + if (r != -ENOKEY) + return r; + } + + if (!(flags & ASK_PASSWORD_NO_TTY) && isatty(STDIN_FILENO)) { char *s = NULL, **l = NULL; - r = ask_password_tty(message, until, false, NULL, &s); + r = ask_password_tty(message, keyname, until, flags, NULL, &s); if (r < 0) return r; r = strv_consume(&l, s); if (r < 0) - return r; + return -ENOMEM; - *_passphrases = l; - return r; - } else - return ask_password_agent(message, icon, id, until, false, accept_cached, _passphrases); + *ret = l; + return 0; + } + + if (!(flags & ASK_PASSWORD_NO_AGENT)) + return ask_password_agent(message, icon, id, keyname, until, flags, ret); + + return -EUNATCH; } diff --git a/src/shared/ask-password-api.h b/src/shared/ask-password-api.h index ccb3be0fca..913cad9f8a 100644 --- a/src/shared/ask-password-api.h +++ b/src/shared/ask-password-api.h @@ -25,10 +25,16 @@ #include "time-util.h" -int ask_password_tty(const char *message, usec_t until, bool echo, const char *flag_file, char **_passphrase); - -int ask_password_agent(const char *message, const char *icon, const char *id, - usec_t until, bool echo, bool accept_cached, char ***_passphrases); - -int ask_password_auto(const char *message, const char *icon, const char *id, - usec_t until, bool accept_cached, char ***_passphrases); +typedef enum AskPasswordFlags { + ASK_PASSWORD_ACCEPT_CACHED = 1, + ASK_PASSWORD_PUSH_CACHE = 2, + ASK_PASSWORD_ECHO = 4, /* show the password literally while reading, instead of "*" */ + ASK_PASSWORD_SILENT = 8, /* do no show any password at all while reading */ + ASK_PASSWORD_NO_TTY = 16, + ASK_PASSWORD_NO_AGENT = 32, +} AskPasswordFlags; + +int ask_password_tty(const char *message, const char *keyname, usec_t until, AskPasswordFlags flags, const char *flag_file, char **ret); +int ask_password_agent(const char *message, const char *icon, const char *id, const char *keyname, usec_t until, AskPasswordFlags flag, char ***ret); +int ask_password_keyring(const char *keyname, AskPasswordFlags flags, char ***ret); +int ask_password_auto(const char *message, const char *icon, const char *id, const char *keyname, usec_t until, AskPasswordFlags flag, char ***ret); diff --git a/src/shared/path-lookup.c b/src/shared/path-lookup.c index d803bbe07e..34eec959ef 100644 --- a/src/shared/path-lookup.c +++ b/src/shared/path-lookup.c @@ -181,7 +181,7 @@ static char** user_dirs( if (strv_extend_strv_concat(&res, config_dirs, "/systemd/user") < 0) return NULL; - if (strv_extend_strv(&res, (char**) config_unit_paths) < 0) + if (strv_extend_strv(&res, (char**) config_unit_paths, false) < 0) return NULL; if (runtime_dir) @@ -203,7 +203,7 @@ static char** user_dirs( if (strv_extend_strv_concat(&res, data_dirs, "/systemd/user") < 0) return NULL; - if (strv_extend_strv(&res, (char**) data_unit_paths) < 0) + if (strv_extend_strv(&res, (char**) data_unit_paths, false) < 0) return NULL; if (generator_late) @@ -318,7 +318,7 @@ int lookup_paths_init( if (!unit_path) return -ENOMEM; - r = strv_extend_strv(&p->unit_path, unit_path); + r = strv_extend_strv(&p->unit_path, unit_path, false); if (r < 0) return r; } diff --git a/src/shared/spawn-ask-password-agent.c b/src/shared/spawn-ask-password-agent.c index 70466d17e5..29db855c67 100644 --- a/src/shared/spawn-ask-password-agent.c +++ b/src/shared/spawn-ask-password-agent.c @@ -19,13 +19,13 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ +#include <signal.h> #include <stdlib.h> #include <unistd.h> -#include <signal.h> #include "log.h" -#include "util.h" #include "process-util.h" +#include "util.h" #include "spawn-ask-password-agent.h" static pid_t agent_pid = 0; @@ -46,9 +46,9 @@ int ask_password_agent_open(void) { SYSTEMD_TTY_ASK_PASSWORD_AGENT_BINARY_PATH, SYSTEMD_TTY_ASK_PASSWORD_AGENT_BINARY_PATH, "--watch", NULL); if (r < 0) - log_error_errno(r, "Failed to fork TTY ask password agent: %m"); + return log_error_errno(r, "Failed to fork TTY ask password agent: %m"); - return r; + return 1; } void ask_password_agent_close(void) { @@ -57,8 +57,8 @@ void ask_password_agent_close(void) { return; /* Inform agent that we are done */ - kill(agent_pid, SIGTERM); - kill(agent_pid, SIGCONT); + (void) kill(agent_pid, SIGTERM); + (void) kill(agent_pid, SIGCONT); (void) wait_for_terminate(agent_pid, NULL); agent_pid = 0; } diff --git a/src/test/test-strv.c b/src/test/test-strv.c index 64801f8e01..623c926521 100644 --- a/src/test/test-strv.c +++ b/src/test/test-strv.c @@ -341,11 +341,11 @@ static void test_strv_extend_strv(void) { _cleanup_strv_free_ char **a = NULL, **b = NULL; a = strv_new("abc", "def", "ghi", NULL); - b = strv_new("jkl", "mno", "pqr", NULL); + b = strv_new("jkl", "mno", "abc", "pqr", NULL); assert_se(a); assert_se(b); - assert_se(strv_extend_strv(&a, b) >= 0); + assert_se(strv_extend_strv(&a, b, true) == 3); assert_se(streq(a[0], "abc")); assert_se(streq(a[1], "def")); @@ -618,6 +618,28 @@ static void test_strv_extend_n(void) { assert_se(v[1] == NULL); } +static void test_strv_make_nulstr_one(char **l) { + _cleanup_free_ char *b = NULL, *c = NULL; + _cleanup_strv_free_ char **q = NULL; + size_t n, m; + + assert_se(strv_make_nulstr(l, &b, &n) >= 0); + assert_se(q = strv_parse_nulstr(b, n)); + assert_se(strv_equal(l, q)); + + assert_se(strv_make_nulstr(q, &c, &m) >= 0); + assert_se(m == n); + assert_se(memcmp(b, c, m) == 0); +} + +static void test_strv_make_nulstr(void) { + test_strv_make_nulstr_one(NULL); + test_strv_make_nulstr_one(STRV_MAKE(NULL)); + test_strv_make_nulstr_one(STRV_MAKE("foo")); + test_strv_make_nulstr_one(STRV_MAKE("foo", "bar")); + test_strv_make_nulstr_one(STRV_MAKE("foo", "bar", "quuux")); +} + int main(int argc, char *argv[]) { test_specifier_printf(); test_strv_foreach(); @@ -678,6 +700,7 @@ int main(int argc, char *argv[]) { test_strv_shell_escape(); test_strv_skip(); test_strv_extend_n(); + test_strv_make_nulstr(); return 0; } diff --git a/src/tty-ask-password-agent/tty-ask-password-agent.c b/src/tty-ask-password-agent/tty-ask-password-agent.c index 4630eb94f1..5dbc0a9bcc 100644 --- a/src/tty-ask-password-agent/tty-ask-password-agent.c +++ b/src/tty-ask-password-agent/tty-ask-password-agent.c @@ -58,9 +58,9 @@ static bool arg_console = false; static int ask_password_plymouth( const char *message, usec_t until, + AskPasswordFlags flags, const char *flag_file, - bool accept_cached, - char ***_passphrases) { + char ***ret) { _cleanup_close_ int fd = -1, notify = -1; union sockaddr_union sa = PLYMOUTH_SOCKET; @@ -75,7 +75,7 @@ static int ask_password_plymouth( POLL_INOTIFY }; - assert(_passphrases); + assert(ret); if (flag_file) { notify = inotify_init1(IN_CLOEXEC|IN_NONBLOCK); @@ -93,17 +93,15 @@ static int ask_password_plymouth( r = connect(fd, &sa.sa, offsetof(struct sockaddr_un, sun_path) + 1 + strlen(sa.un.sun_path+1)); if (r < 0) - return log_error_errno(errno, "Failed to connect to Plymouth: %m"); + return -errno; - if (accept_cached) { + if (flags & ASK_PASSWORD_ACCEPT_CACHED) { packet = strdup("c"); n = 1; - } else if (asprintf(&packet, "*\002%c%s%n", (int) (strlen(message) + 1), - message, &n) < 0) + } else if (asprintf(&packet, "*\002%c%s%n", (int) (strlen(message) + 1), message, &n) < 0) packet = NULL; - if (!packet) - return log_oom(); + return -ENOMEM; r = loop_write(fd, packet, n + 1, true); if (r < 0) @@ -131,7 +129,7 @@ static int ask_password_plymouth( if (flag_file && access(flag_file, F_OK) < 0) return -errno; - j = poll(pollfd, notify > 0 ? 2 : 1, sleep_for); + j = poll(pollfd, notify >= 0 ? 2 : 1, sleep_for); if (j < 0) { if (errno == EINTR) continue; @@ -140,15 +138,20 @@ static int ask_password_plymouth( } else if (j == 0) return -ETIME; - if (notify > 0 && pollfd[POLL_INOTIFY].revents != 0) + if (notify >= 0 && pollfd[POLL_INOTIFY].revents != 0) flush_fd(notify); if (pollfd[POLL_SOCKET].revents == 0) continue; k = read(fd, buffer + p, sizeof(buffer) - p); - if (k <= 0) - return r = k < 0 ? -errno : -EIO; + if (k < 0) { + if (errno == EINTR || errno == EAGAIN) + continue; + + return -errno; + } else if (k == 0) + return -EIO; p += k; @@ -157,7 +160,7 @@ static int ask_password_plymouth( if (buffer[0] == 5) { - if (accept_cached) { + if (flags & ASK_PASSWORD_ACCEPT_CACHED) { /* Hmm, first try with cached * passwords failed, so let's retry * with a normal password request */ @@ -170,7 +173,7 @@ static int ask_password_plymouth( if (r < 0) return r; - accept_cached = false; + flags &= ~ASK_PASSWORD_ACCEPT_CACHED; p = 0; continue; } @@ -198,7 +201,7 @@ static int ask_password_plymouth( if (!l) return -ENOMEM; - *_passphrases = l; + *ret = l; break; } else @@ -283,7 +286,7 @@ static int parse_password(const char *filename, char **wall) { if (arg_plymouth) { _cleanup_strv_free_ char **passwords = NULL; - r = ask_password_plymouth(message, not_after, filename, accept_cached, &passwords); + r = ask_password_plymouth(message, not_after, accept_cached ? ASK_PASSWORD_ACCEPT_CACHED : 0, filename, &passwords); if (r >= 0) { char **p; @@ -305,19 +308,19 @@ static int parse_password(const char *filename, char **wall) { } } else { - int tty_fd = -1; _cleanup_free_ char *password = NULL; + int tty_fd = -1; if (arg_console) { tty_fd = acquire_terminal("/dev/console", false, false, false, USEC_INFINITY); if (tty_fd < 0) - return tty_fd; + return log_error_errno(tty_fd, "Failed to acquire /dev/console: %m"); } - r = ask_password_tty(message, not_after, echo, filename, &password); + r = ask_password_tty(message, NULL, not_after, echo ? ASK_PASSWORD_ECHO : 0, filename, &password); if (arg_console) { - safe_close(tty_fd); + tty_fd = safe_close(tty_fd); release_terminal(); } @@ -347,12 +350,9 @@ static int parse_password(const char *filename, char **wall) { sa.un.sun_family = AF_UNIX; strncpy(sa.un.sun_path, socket_name, sizeof(sa.un.sun_path)); - r = sendto(socket_fd, packet, packet_length, MSG_NOSIGNAL, &sa.sa, - offsetof(struct sockaddr_un, sun_path) + strlen(socket_name)); - if (r < 0) { - log_error_errno(errno, "Failed to send: %m"); - return r; - } + r = sendto(socket_fd, packet, packet_length, MSG_NOSIGNAL, &sa.sa, offsetof(struct sockaddr_un, sun_path) + strlen(socket_name)); + if (r < 0) + return log_error_errno(errno, "Failed to send: %m"); } return 0; @@ -360,40 +360,45 @@ static int parse_password(const char *filename, char **wall) { static int wall_tty_block(void) { _cleanup_free_ char *p = NULL; - int fd, r; dev_t devnr; + int fd, r; r = get_ctty_devnr(0, &devnr); + if (r == -ENXIO) /* We have no controlling tty */ + return -ENOTTY; if (r < 0) - return r; + return log_error_errno(r, "Failed to get controlling TTY: %m"); if (asprintf(&p, "/run/systemd/ask-password-block/%u:%u", major(devnr), minor(devnr)) < 0) - return -ENOMEM; + return log_oom(); mkdir_parents_label(p, 0700); mkfifo(p, 0600); fd = open(p, O_RDONLY|O_CLOEXEC|O_NONBLOCK|O_NOCTTY); if (fd < 0) - return -errno; + return log_error_errno(errno, "Failed to open %s: %m", p); return fd; } static bool wall_tty_match(const char *path, void *userdata) { - int fd, r; - struct stat st; _cleanup_free_ char *p = NULL; + _cleanup_close_ int fd = -1; + struct stat st; if (!path_is_absolute(path)) path = strjoina("/dev/", path); - r = lstat(path, &st); - if (r < 0) + if (lstat(path, &st) < 0) { + log_debug_errno(errno, "Failed to stat %s: %m", path); return true; + } - if (!S_ISCHR(st.st_mode)) + if (!S_ISCHR(st.st_mode)) { + log_debug("%s is not a character device.", path); return true; + } /* We use named pipes to ensure that wall messages suggesting * password entry are not printed over password prompts @@ -403,16 +408,19 @@ static bool wall_tty_match(const char *path, void *userdata) { * advantage that the block will automatically go away if the * process dies. */ - if (asprintf(&p, "/run/systemd/ask-password-block/%u:%u", major(st.st_rdev), minor(st.st_rdev)) < 0) + if (asprintf(&p, "/run/systemd/ask-password-block/%u:%u", major(st.st_rdev), minor(st.st_rdev)) < 0) { + log_oom(); return true; + } fd = open(p, O_WRONLY|O_CLOEXEC|O_NONBLOCK|O_NOCTTY); - if (fd < 0) - return true; + if (fd < 0) { + log_debug_errno(errno, "Failed top open the wall pipe: %m"); + return 1; + } /* What, we managed to open the pipe? Then this tty is filtered. */ - safe_close(fd); - return false; + return 0; } static int show_passwords(void) { @@ -425,11 +433,10 @@ static int show_passwords(void) { if (errno == ENOENT) return 0; - log_error_errno(errno, "opendir(/run/systemd/ask-password): %m"); - return -errno; + return log_error_errno(errno, "Failed top open /run/systemd/ask-password: %m"); } - while ((de = readdir(d))) { + FOREACH_DIRENT_ALL(de, d, return log_error_errno(errno, "Failed to read directory: %m")) { _cleanup_free_ char *p = NULL, *wall = NULL; int q; @@ -454,7 +461,7 @@ static int show_passwords(void) { r = q; if (wall) - utmp_wall(wall, NULL, NULL, wall_tty_match, NULL); + (void) utmp_wall(wall, NULL, NULL, wall_tty_match, NULL); } return r; @@ -474,14 +481,14 @@ static int watch_passwords(void) { tty_block_fd = wall_tty_block(); - mkdir_p_label("/run/systemd/ask-password", 0755); + (void) mkdir_p_label("/run/systemd/ask-password", 0755); notify = inotify_init1(IN_CLOEXEC); if (notify < 0) - return -errno; + return log_error_errno(errno, "Failed to allocate directory watch: %m"); if (inotify_add_watch(notify, "/run/systemd/ask-password", IN_CLOSE_WRITE|IN_MOVED_TO) < 0) - return -errno; + return log_error_errno(errno, "Failed to add /run/systemd/ask-password to directory watch: %m"); assert_se(sigemptyset(&mask) >= 0); assert_se(sigset_add_many(&mask, SIGINT, SIGTERM, -1) >= 0); @@ -489,7 +496,7 @@ static int watch_passwords(void) { signal_fd = signalfd(-1, &mask, SFD_NONBLOCK|SFD_CLOEXEC); if (signal_fd < 0) - return -errno; + return log_error_errno(errno, "Failed to allocate signal file descriptor: %m"); pollfd[FD_INOTIFY].fd = notify; pollfd[FD_INOTIFY].events = POLLIN; @@ -509,7 +516,7 @@ static int watch_passwords(void) { } if (pollfd[FD_INOTIFY].revents != 0) - flush_fd(notify); + (void) flush_fd(notify); if (pollfd[FD_SIGNAL].revents != 0) break; @@ -633,8 +640,6 @@ int main(int argc, char *argv[]) { r = watch_passwords(); else r = show_passwords(); - if (r < 0) - log_error_errno(r, "Error: %m"); finish: return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; |