diff options
Diffstat (limited to 'src/shared/ask-password-api.c')
-rw-r--r-- | src/shared/ask-password-api.c | 260 |
1 files changed, 161 insertions, 99 deletions
diff --git a/src/shared/ask-password-api.c b/src/shared/ask-password-api.c index 0bb6611c64..7b9cdadc54 100644 --- a/src/shared/ask-password-api.c +++ b/src/shared/ask-password-api.c @@ -27,6 +27,7 @@ #include "format-util.h" #include "fs-util.h" #include "glyph-util.h" +#include "inotify-util.h" #include "io-util.h" #include "iovec-util.h" #include "keyring-util.h" @@ -36,6 +37,7 @@ #include "missing_syscall.h" #include "mkdir-label.h" #include "nulstr-util.h" +#include "path-lookup.h" #include "plymouth-util.h" #include "process-util.h" #include "random-util.h" @@ -57,7 +59,7 @@ static int lookup_key(const char *keyname, key_serial_t *ret) { assert(keyname); assert(ret); - serial = request_key("user", keyname, NULL, 0); + serial = request_key("user", keyname, /* callout_info= */ NULL, /* dest_keyring= */ 0); if (serial == -1) return negative_errno(); @@ -85,6 +87,32 @@ static int retrieve_key(key_serial_t serial, char ***ret) { return 0; } +static int get_ask_password_directory_for_flags(AskPasswordFlags flags, char **ret) { + if (FLAGS_SET(flags, ASK_PASSWORD_USER)) + return acquire_user_ask_password_directory(ret); + + return strdup_to_full(ret, "/run/systemd/ask-password/"); /* Returns 1, indicating there's a suitable directory */ +} + +static int touch_ask_password_directory(AskPasswordFlags flags) { + int r; + + _cleanup_free_ char *p = NULL; + r = get_ask_password_directory_for_flags(flags, &p); + if (r <= 0) + return r; + + _cleanup_close_ int fd = open_mkdir(p, O_CLOEXEC, 0755); + if (fd < 0) + return fd; + + r = touch_fd(fd, USEC_INFINITY); + if (r < 0) + return r; + + return 1; /* did something */ +} + static int add_to_keyring(const char *keyname, AskPasswordFlags flags, char **passwords) { _cleanup_strv_free_erase_ char **l = NULL; _cleanup_(erase_and_freep) char *p = NULL; @@ -107,7 +135,7 @@ static int add_to_keyring(const char *keyname, AskPasswordFlags flags, char **pa } else if (r != -ENOKEY) return r; - r = strv_extend_strv(&l, passwords, true); + r = strv_extend_strv(&l, passwords, /* filter_duplicates= */ true); if (r <= 0) return r; @@ -129,7 +157,7 @@ static int add_to_keyring(const char *keyname, AskPasswordFlags flags, char **pa log_debug_errno(errno, "Failed to adjust kernel keyring key timeout: %m"); /* Tell everyone to check the keyring */ - (void) touch("/run/systemd/ask-password"); + (void) touch_ask_password_directory(flags); log_debug("Added key to kernel keyring as %" PRIi32 ".", serial); @@ -220,17 +248,12 @@ int ask_password_plymouth( const char *flag_file, char ***ret) { - _cleanup_close_ int fd = -EBADF, notify = -EBADF; + _cleanup_close_ int fd = -EBADF, inotify_fd = -EBADF; _cleanup_free_ char *packet = NULL; ssize_t k; int r, n; - struct pollfd pollfd[2] = {}; char buffer[LINE_MAX]; size_t p = 0; - enum { - POLL_SOCKET, - POLL_INOTIFY - }; assert(ret); @@ -240,11 +263,11 @@ int ask_password_plymouth( const char *message = req && req->message ? req->message : "Password:"; if (flag_file) { - notify = inotify_init1(IN_CLOEXEC|IN_NONBLOCK); - if (notify < 0) + inotify_fd = inotify_init1(IN_CLOEXEC|IN_NONBLOCK); + if (inotify_fd < 0) return -errno; - if (inotify_add_watch(notify, flag_file, IN_ATTRIB) < 0) /* for the link count */ + if (inotify_add_watch(inotify_fd, flag_file, IN_ATTRIB) < 0) /* for the link count */ return -errno; } @@ -266,10 +289,17 @@ int ask_password_plymouth( CLEANUP_ERASE(buffer); - pollfd[POLL_SOCKET].fd = fd; - pollfd[POLL_SOCKET].events = POLLIN; - pollfd[POLL_INOTIFY].fd = notify; - pollfd[POLL_INOTIFY].events = POLLIN; + enum { + POLL_SOCKET, + POLL_INOTIFY, /* Must be last, because optional */ + _POLL_MAX, + }; + + struct pollfd pollfd[_POLL_MAX] = { + [POLL_SOCKET] = { .fd = fd, .events = POLLIN }, + [POLL_INOTIFY] = { .fd = inotify_fd, .events = POLLIN }, + }; + size_t n_pollfd = inotify_fd >= 0 ? _POLL_MAX : _POLL_MAX-1; for (;;) { usec_t timeout; @@ -282,7 +312,7 @@ int ask_password_plymouth( if (flag_file && access(flag_file, F_OK) < 0) return -errno; - r = ppoll_usec(pollfd, notify >= 0 ? 2 : 1, timeout); + r = ppoll_usec(pollfd, n_pollfd, timeout); if (r == -EINTR) continue; if (r < 0) @@ -290,8 +320,8 @@ int ask_password_plymouth( if (r == 0) return -ETIME; - if (notify >= 0 && pollfd[POLL_INOTIFY].revents != 0) - (void) flush_fd(notify); + if (inotify_fd >= 0 && pollfd[POLL_INOTIFY].revents != 0) + (void) flush_fd(inotify_fd); if (pollfd[POLL_SOCKET].revents == 0) continue; @@ -375,18 +405,11 @@ int ask_password_tty( const char *flag_file, char ***ret) { - enum { - POLL_TTY, - POLL_INOTIFY, - _POLL_MAX, - }; - bool reset_tty = false, dirty = false, use_color = false, press_tab_visible = false; - _cleanup_close_ int cttyfd = -EBADF, notify = -EBADF; + _cleanup_close_ int cttyfd = -EBADF, inotify_fd = -EBADF; struct termios old_termios, new_termios; char passphrase[LINE_MAX + 1] = {}, *x; _cleanup_strv_free_erase_ char **l = NULL; - struct pollfd pollfd[_POLL_MAX]; size_t p = 0, codepoint = 0; int r; @@ -405,23 +428,36 @@ int ask_password_tty( message = strjoina(special_glyph(SPECIAL_GLYPH_LOCK_AND_KEY), " ", message); if (flag_file || (FLAGS_SET(flags, ASK_PASSWORD_ACCEPT_CACHED) && keyring)) { - notify = inotify_init1(IN_CLOEXEC|IN_NONBLOCK); - if (notify < 0) + inotify_fd = inotify_init1(IN_CLOEXEC|IN_NONBLOCK); + if (inotify_fd < 0) return -errno; } if (flag_file) { - if (inotify_add_watch(notify, flag_file, IN_ATTRIB /* for the link count */) < 0) + if (inotify_add_watch(inotify_fd, flag_file, IN_ATTRIB /* for the link count */) < 0) return -errno; } if (FLAGS_SET(flags, ASK_PASSWORD_ACCEPT_CACHED) && req && keyring) { r = ask_password_keyring(req, flags, ret); if (r >= 0) return 0; - else if (r != -ENOKEY) + if (r != -ENOKEY) return r; - if (inotify_add_watch(notify, "/run/systemd/ask-password", IN_ATTRIB /* for mtime */) < 0) - return -errno; + /* Let's watch the askpw directory for mtime changes, which we issue above whenever the + * keyring changes */ + _cleanup_free_ char *watch_path = NULL; + r = get_ask_password_directory_for_flags(flags, &watch_path); + if (r < 0) + return r; + if (r > 0) { + _cleanup_close_ int watch_fd = open_mkdir(watch_path, O_CLOEXEC|O_RDONLY, 0755); + if (watch_fd < 0) + return watch_fd; + + r = inotify_add_watch_fd(inotify_fd, watch_fd, IN_ONLYDIR|IN_ATTRIB /* for mtime */); + if (r < 0) + return r; + } } CLEANUP_ERASE(passphrase); @@ -466,14 +502,17 @@ int ask_password_tty( reset_tty = true; } - pollfd[POLL_TTY] = (struct pollfd) { - .fd = ttyfd >= 0 ? ttyfd : STDIN_FILENO, - .events = POLLIN, + enum { + POLL_TTY, + POLL_INOTIFY, /* Must be last, because optional */ + _POLL_MAX, }; - pollfd[POLL_INOTIFY] = (struct pollfd) { - .fd = notify, - .events = POLLIN, + + struct pollfd pollfd[_POLL_MAX] = { + [POLL_TTY] = { .fd = ttyfd >= 0 ? ttyfd : STDIN_FILENO, .events = POLLIN }, + [POLL_INOTIFY] = { .fd = inotify_fd, .events = POLLIN }, }; + size_t n_pollfd = inotify_fd >= 0 ? _POLL_MAX : _POLL_MAX-1; for (;;) { _cleanup_(erase_char) char c; @@ -491,7 +530,7 @@ int ask_password_tty( goto finish; } - r = ppoll_usec(pollfd, notify >= 0 ? 2 : 1, timeout); + r = ppoll_usec(pollfd, n_pollfd, timeout); if (r == -EINTR) continue; if (r < 0) @@ -501,8 +540,8 @@ int ask_password_tty( goto finish; } - if (notify >= 0 && pollfd[POLL_INOTIFY].revents != 0 && keyring) { - (void) flush_fd(notify); + if (inotify_fd >= 0 && pollfd[POLL_INOTIFY].revents != 0 && keyring) { + (void) flush_fd(inotify_fd); r = ask_password_keyring(req, flags, ret); if (r >= 0) { @@ -659,20 +698,21 @@ finish: return r; } -static int create_socket(char **ret) { +static int create_socket(const char *askpwdir, char **ret) { _cleanup_free_ char *path = NULL; union sockaddr_union sa; socklen_t sa_len; _cleanup_close_ int fd = -EBADF; int r; + assert(askpwdir); assert(ret); fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0); if (fd < 0) return -errno; - if (asprintf(&path, "/run/systemd/ask-password/sck.%" PRIx64, random_u64()) < 0) + if (asprintf(&path, "%s/sck.%" PRIx64, askpwdir, random_u64()) < 0) return -ENOMEM; r = sockaddr_un_set_path(&sa.un, path); @@ -698,20 +738,11 @@ int ask_password_agent( AskPasswordFlags flags, char ***ret) { - enum { - FD_SOCKET, - FD_SIGNAL, - FD_INOTIFY, - _FD_MAX - }; - - _cleanup_close_ int socket_fd = -EBADF, signal_fd = -EBADF, notify = -EBADF, fd = -EBADF; - char temp[] = "/run/systemd/ask-password/tmp.XXXXXX"; - char final[sizeof(temp)] = ""; - _cleanup_free_ char *socket_name = NULL; + _cleanup_close_ int socket_fd = -EBADF, signal_fd = -EBADF, inotify_fd = -EBADF, dfd = -EBADF; + _cleanup_(unlink_and_freep) char *socket_name = NULL; + _cleanup_free_ char *temp = NULL, *final = NULL; _cleanup_strv_free_erase_ char **l = NULL; _cleanup_fclose_ FILE *f = NULL; - struct pollfd pollfd[_FD_MAX]; sigset_t mask, oldmask; int r; @@ -727,7 +758,20 @@ int ask_password_agent( assert_se(sigset_add_many(&mask, SIGINT, SIGTERM) >= 0); assert_se(sigprocmask(SIG_BLOCK, &mask, &oldmask) >= 0); - (void) mkdir_p_label("/run/systemd/ask-password", 0755); + _cleanup_free_ char *askpwdir = NULL; + r = get_ask_password_directory_for_flags(flags, &askpwdir); + if (r < 0) + goto finish; + if (r == 0) { + r = -ENXIO; + goto finish; + } + + dfd = open_mkdir(askpwdir, O_RDONLY|O_CLOEXEC, 0755); + if (dfd < 0) { + r = log_debug_errno(dfd, "Failed to open directory '%s': %m", askpwdir); + goto finish; + } if (FLAGS_SET(flags, ASK_PASSWORD_ACCEPT_CACHED) && req && req->keyring) { r = ask_password_keyring(req, flags, ret); @@ -737,30 +781,25 @@ int ask_password_agent( } else if (r != -ENOKEY) goto finish; - notify = inotify_init1(IN_CLOEXEC | IN_NONBLOCK); - if (notify < 0) { + inotify_fd = inotify_init1(IN_CLOEXEC | IN_NONBLOCK); + if (inotify_fd < 0) { r = -errno; goto finish; } - r = RET_NERRNO(inotify_add_watch(notify, "/run/systemd/ask-password", IN_ATTRIB /* for mtime */)); + r = inotify_add_watch_fd(inotify_fd, dfd, IN_ONLYDIR|IN_ATTRIB /* for mtime */); if (r < 0) goto finish; } - fd = mkostemp_safe(temp); - if (fd < 0) { - r = fd; + if (asprintf(&final, "ask.%" PRIu64, random_u64()) < 0) { + r = -ENOMEM; goto finish; } - (void) fchmod(fd, 0644); - - f = take_fdopen(&fd, "w"); - if (!f) { - r = -errno; + r = fopen_temporary_at(dfd, final, &f, &temp); + if (r < 0) goto finish; - } signal_fd = signalfd(-1, &mask, SFD_NONBLOCK|SFD_CLOEXEC); if (signal_fd < 0) { @@ -768,7 +807,7 @@ int ask_password_agent( goto finish; } - socket_fd = create_socket(&socket_name); + socket_fd = create_socket(askpwdir, &socket_name); if (socket_fd < 0) { r = socket_fd; goto finish; @@ -800,27 +839,35 @@ int ask_password_agent( fprintf(f, "Id=%s\n", req->id); } + if (fchmod(fileno(f), 0644) < 0) { + r = -errno; + goto finish; + } + r = fflush_and_check(f); if (r < 0) goto finish; - memcpy(final, temp, sizeof(temp)); + if (renameat(dfd, temp, dfd, final) < 0) { + r = -errno; + goto finish; + } - final[sizeof(final)-11] = 'a'; - final[sizeof(final)-10] = 's'; - final[sizeof(final)-9] = 'k'; + temp = mfree(temp); - r = RET_NERRNO(rename(temp, final)); - if (r < 0) - goto finish; + enum { + POLL_SOCKET, + POLL_SIGNAL, + POLL_INOTIFY, /* Must be last, because optional */ + _POLL_MAX + }; - zero(pollfd); - pollfd[FD_SOCKET].fd = socket_fd; - pollfd[FD_SOCKET].events = POLLIN; - pollfd[FD_SIGNAL].fd = signal_fd; - pollfd[FD_SIGNAL].events = POLLIN; - pollfd[FD_INOTIFY].fd = notify; - pollfd[FD_INOTIFY].events = POLLIN; + struct pollfd pollfd[_POLL_MAX] = { + [POLL_SOCKET] = { .fd = socket_fd, .events = POLLIN }, + [POLL_SIGNAL] = { .fd = signal_fd, .events = POLLIN }, + [POLL_INOTIFY] = { .fd = inotify_fd, .events = POLLIN }, + }; + size_t n_pollfd = inotify_fd >= 0 ? _POLL_MAX : _POLL_MAX - 1; for (;;) { CMSG_BUFFER_TYPE(CMSG_SPACE(sizeof(struct ucred))) control; @@ -835,7 +882,7 @@ int ask_password_agent( else timeout = USEC_INFINITY; - r = ppoll_usec(pollfd, notify >= 0 ? _FD_MAX : _FD_MAX - 1, timeout); + r = ppoll_usec(pollfd, n_pollfd, timeout); if (r == -EINTR) continue; if (r < 0) @@ -845,13 +892,13 @@ int ask_password_agent( goto finish; } - if (pollfd[FD_SIGNAL].revents & POLLIN) { + if (pollfd[POLL_SIGNAL].revents & POLLIN) { r = -EINTR; goto finish; } - if (notify >= 0 && pollfd[FD_INOTIFY].revents != 0) { - (void) flush_fd(notify); + if (inotify_fd >= 0 && pollfd[POLL_INOTIFY].revents != 0) { + (void) flush_fd(inotify_fd); if (req && req->keyring) { r = ask_password_keyring(req, flags, ret); @@ -863,10 +910,10 @@ int ask_password_agent( } } - if (pollfd[FD_SOCKET].revents == 0) + if (pollfd[POLL_SOCKET].revents == 0) continue; - if (pollfd[FD_SOCKET].revents != POLLIN) { + if (pollfd[POLL_SOCKET].revents != POLLIN) { r = -EIO; goto finish; } @@ -911,8 +958,8 @@ int ask_password_agent( continue; } - if (ucred->uid != 0) { - log_debug("Got request from unprivileged user. Ignoring."); + if (ucred->uid != getuid() && ucred->uid != 0) { + log_debug("Got response from bad user. Ignoring."); continue; } @@ -951,13 +998,13 @@ int ask_password_agent( r = 0; finish: - if (socket_name) - (void) unlink(socket_name); - - (void) unlink(temp); - - if (final[0]) - (void) unlink(final); + if (temp) { + assert(dfd >= 0); + (void) unlinkat(dfd, temp, 0); + } else if (final) { + assert(dfd >= 0); + (void) unlinkat(dfd, final, 0); + } assert_se(sigprocmask(SIG_SETMASK, &oldmask, NULL) == 0); return r; @@ -1021,3 +1068,18 @@ int ask_password_auto( return -EUNATCH; } + +int acquire_user_ask_password_directory(char **ret) { + int r; + + r = xdg_user_runtime_dir("systemd/ask-password", ret); + if (r == -ENXIO) { + if (ret) + *ret = NULL; + return 0; + } + if (r < 0) + return r; + + return 1; +} |