summaryrefslogtreecommitdiffstats
path: root/src/shared/ask-password-api.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/shared/ask-password-api.c')
-rw-r--r--src/shared/ask-password-api.c260
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;
+}