diff options
Diffstat (limited to 'src/tty-ask-password-agent/tty-ask-password-agent.c')
-rw-r--r-- | src/tty-ask-password-agent/tty-ask-password-agent.c | 247 |
1 files changed, 145 insertions, 102 deletions
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 82cbf95f1e..8cfe10330d 100644 --- a/src/tty-ask-password-agent/tty-ask-password-agent.c +++ b/src/tty-ask-password-agent/tty-ask-password-agent.c @@ -19,32 +19,36 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include <stdbool.h> #include <errno.h> +#include <fcntl.h> +#include <getopt.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 <stddef.h> -#include <poll.h> -#include <sys/inotify.h> #include <unistd.h> -#include <getopt.h> -#include <sys/signalfd.h> -#include <fcntl.h> -#include "util.h" +#include "alloc-util.h" +#include "ask-password-api.h" +#include "conf-parser.h" +#include "def.h" +#include "dirent-util.h" +#include "fd-util.h" +#include "io-util.h" #include "mkdir.h" #include "path-util.h" -#include "conf-parser.h" -#include "utmp-wtmp.h" +#include "process-util.h" +#include "signal-util.h" #include "socket-util.h" -#include "ask-password-api.h" +#include "string-util.h" #include "strv.h" -#include "build.h" -#include "def.h" -#include "process-util.h" #include "terminal-util.h" -#include "signal-util.h" +#include "util.h" +#include "utmp-wtmp.h" static enum { ACTION_LIST, @@ -59,9 +63,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; @@ -76,7 +80,7 @@ static int ask_password_plymouth( POLL_INOTIFY }; - assert(_passphrases); + assert(ret); if (flag_file) { notify = inotify_init1(IN_CLOEXEC|IN_NONBLOCK); @@ -94,17 +98,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) @@ -123,33 +125,48 @@ static int ask_password_plymouth( y = now(CLOCK_MONOTONIC); - if (y > until) - return -ETIME; + if (y > until) { + r = -ETIME; + goto finish; + } sleep_for = (int) ((until - y) / USEC_PER_MSEC); } - if (flag_file && access(flag_file, F_OK) < 0) - return -errno; + if (flag_file && access(flag_file, F_OK) < 0) { + r = -errno; + goto finish; + } - 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; - return -errno; - } else if (j == 0) - return -ETIME; + r = -errno; + goto finish; + } else if (j == 0) { + r = -ETIME; + goto finish; + } - 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; + + r = -errno; + goto finish; + } else if (k == 0) { + r = -EIO; + goto finish; + } p += k; @@ -158,26 +175,29 @@ 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 */ packet = mfree(packet); - if (asprintf(&packet, "*\002%c%s%n", (int) (strlen(message) + 1), message, &n) < 0) - return -ENOMEM; + if (asprintf(&packet, "*\002%c%s%n", (int) (strlen(message) + 1), message, &n) < 0) { + r = -ENOMEM; + goto finish; + } r = loop_write(fd, packet, n+1, true); if (r < 0) - return r; + goto finish; - accept_cached = false; + flags &= ~ASK_PASSWORD_ACCEPT_CACHED; p = 0; continue; } /* No password, because UI not shown */ - return -ENOENT; + r = -ENOENT; + goto finish; } else if (buffer[0] == 2 || buffer[0] == 9) { uint32_t size; @@ -189,32 +209,43 @@ static int ask_password_plymouth( memcpy(&size, buffer+1, sizeof(size)); size = le32toh(size); - if (size + 5 > sizeof(buffer)) - return -EIO; + if (size + 5 > sizeof(buffer)) { + r = -EIO; + goto finish; + } if (p-5 < size) continue; l = strv_parse_nulstr(buffer + 5, size); - if (!l) - return -ENOMEM; + if (!l) { + r = -ENOMEM; + goto finish; + } - *_passphrases = l; + *ret = l; break; - } else + } else { /* Unknown packet */ - return -EIO; + r = -EIO; + goto finish; + } } - return 0; + r = 0; + +finish: + memory_erase(buffer, sizeof(buffer)); + return r; } static int parse_password(const char *filename, char **wall) { _cleanup_free_ char *socket_name = NULL, *message = NULL, *packet = NULL; + bool accept_cached = false, echo = false; + size_t packet_length = 0; uint64_t not_after = 0; unsigned pid = 0; - bool accept_cached = false, echo = false; const ConfigTableItem items[] = { { "Ask", "Socket", config_parse_string, 0, &socket_name }, @@ -257,7 +288,7 @@ static int parse_password(const char *filename, char **wall) { if (asprintf(&_wall, "%s%sPassword entry required for \'%s\' (PID %u).\r\n" "Please enter password with the systemd-tty-ask-password-agent tool!", - *wall ? *wall : "", + strempty(*wall), *wall ? "\r\n\r\n" : "", message, pid) < 0) @@ -268,7 +299,6 @@ static int parse_password(const char *filename, char **wall) { } else { union sockaddr_union sa = {}; - size_t packet_length = 0; _cleanup_close_ int socket_fd = -1; assert(arg_action == ACTION_QUERY || @@ -282,9 +312,9 @@ static int parse_password(const char *filename, char **wall) { } if (arg_plymouth) { - _cleanup_strv_free_ char **passwords = NULL; + _cleanup_strv_free_erase_ 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; @@ -306,19 +336,23 @@ static int parse_password(const char *filename, char **wall) { } } else { + _cleanup_string_free_erase_ char *password = NULL; int tty_fd = -1; - _cleanup_free_ char *password = NULL; 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 = reset_terminal_fd(tty_fd, true); + if (r < 0) + log_warning_errno(r, "Failed to reset terminal, ignoring: %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(); } @@ -334,67 +368,79 @@ static int parse_password(const char *filename, char **wall) { } } - if (IN_SET(r, -ETIME, -ENOENT)) + if (IN_SET(r, -ETIME, -ENOENT)) { /* If the query went away, that's OK */ - return 0; - - if (r < 0) - return log_error_errno(r, "Failed to query password: %m"); + r = 0; + goto finish; + } + if (r < 0) { + log_error_errno(r, "Failed to query password: %m"); + goto finish; + } socket_fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0); - if (socket_fd < 0) - return log_error_errno(errno, "socket(): %m"); + if (socket_fd < 0) { + r = log_error_errno(errno, "socket(): %m"); + goto finish; + } 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)); + memory_erase(packet, packet_length); + if (r < 0) + return log_error_errno(errno, "Failed to send: %m"); } return 0; + +finish: + memory_erase(packet, packet_length); + return r; } 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_debug_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 @@ -404,16 +450,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) { @@ -426,11 +475,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 to 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; @@ -455,7 +503,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; @@ -475,14 +523,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); @@ -490,7 +538,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; @@ -510,7 +558,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; @@ -571,9 +619,7 @@ static int parse_argv(int argc, char *argv[]) { return 0; case ARG_VERSION: - puts(PACKAGE_STRING); - puts(SYSTEMD_FEATURES); - return 0; + return version(); case ARG_LIST: arg_action = ACTION_LIST; @@ -628,8 +674,8 @@ int main(int argc, char *argv[]) { goto finish; if (arg_console) { - setsid(); - release_terminal(); + (void) setsid(); + (void) release_terminal(); } if (IN_SET(arg_action, ACTION_WATCH, ACTION_WALL)) @@ -637,9 +683,6 @@ int main(int argc, char *argv[]) { else r = show_passwords(); - if (r < 0) - log_error_errno(r, "Error: %m"); - finish: return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; } |