]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
ask-password-api: add support for querying pws from unpriv agents
authorLennart Poettering <lennart@poettering.net>
Fri, 13 Sep 2024 11:53:35 +0000 (13:53 +0200)
committerLennart Poettering <lennart@poettering.net>
Mon, 21 Oct 2024 12:14:05 +0000 (14:14 +0200)
src/shared/ask-password-api.c
src/shared/ask-password-api.h

index cd68c5c90f22c442db65c0096397e0874f5c2560..1c4f357fbe4f9252bbe7a0941acda4caf247ad16 100644 (file)
@@ -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"
@@ -86,6 +87,28 @@ 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;
+
+        r = touch(p);
+        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;
@@ -130,7 +153,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);
 
@@ -416,8 +439,21 @@ int ask_password_tty(
                 if (r != -ENOKEY)
                         return r;
 
-                if (inotify_add_watch(inotify_fd, "/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);
@@ -658,20 +694,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);
@@ -697,10 +734,9 @@ int ask_password_agent(
                 AskPasswordFlags flags,
                 char ***ret) {
 
-        _cleanup_close_ int socket_fd = -EBADF, signal_fd = -EBADF, inotify_fd = -EBADF, fd = -EBADF;
-        char temp[] = "/run/systemd/ask-password/tmp.XXXXXX";
-        char final[sizeof(temp)] = "";
+        _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;
         sigset_t mask, oldmask;
@@ -718,7 +754,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);
@@ -734,24 +783,19 @@ int ask_password_agent(
                         goto finish;
                 }
 
-                r = RET_NERRNO(inotify_add_watch(inotify_fd, "/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) {
@@ -759,7 +803,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;
@@ -791,19 +835,21 @@ 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));
-
-        final[sizeof(final)-11] = 'a';
-        final[sizeof(final)-10] = 's';
-        final[sizeof(final)-9] = 'k';
-
-        r = RET_NERRNO(rename(temp, final));
-        if (r < 0)
+        if (renameat(dfd, temp, dfd, final) < 0) {
+                r = -errno;
                 goto finish;
+        }
+
+        temp = mfree(temp);
 
         enum {
                 POLL_SOCKET,
@@ -908,8 +954,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;
                 }
 
@@ -948,10 +994,13 @@ int ask_password_agent(
         r = 0;
 
 finish:
-        (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;
index 9b197230e963526f70e407d43b573e597746f6e3..b3cb407e51c7de3000668b155306bb5aaeb4c3a0 100644 (file)
@@ -6,16 +6,17 @@
 #include "time-util.h"
 
 typedef enum AskPasswordFlags {
-        ASK_PASSWORD_ACCEPT_CACHED = 1 << 0, /* read from kernel keyring */
-        ASK_PASSWORD_PUSH_CACHE    = 1 << 1, /* write to kernel keyring after getting password from elsewhere */
-        ASK_PASSWORD_ECHO          = 1 << 2, /* show the password literally while reading, instead of "*" */
-        ASK_PASSWORD_SILENT        = 1 << 3, /* do no show any password at all while reading */
-        ASK_PASSWORD_NO_TTY        = 1 << 4, /* never ask for password on tty */
-        ASK_PASSWORD_NO_AGENT      = 1 << 5, /* never ask for password via agent */
-        ASK_PASSWORD_CONSOLE_COLOR = 1 << 6, /* Use color if /dev/console points to a console that supports color */
-        ASK_PASSWORD_NO_CREDENTIAL = 1 << 7, /* never use $CREDENTIALS_DIRECTORY data */
-        ASK_PASSWORD_HIDE_EMOJI    = 1 << 8, /* hide the lock and key emoji */
-        ASK_PASSWORD_HEADLESS      = 1 << 9, /* headless mode: never query interactively */
+        ASK_PASSWORD_ACCEPT_CACHED = 1 << 0,  /* read from kernel keyring */
+        ASK_PASSWORD_PUSH_CACHE    = 1 << 1,  /* write to kernel keyring after getting password from elsewhere */
+        ASK_PASSWORD_ECHO          = 1 << 2,  /* show the password literally while reading, instead of "*" */
+        ASK_PASSWORD_SILENT        = 1 << 3,  /* do no show any password at all while reading */
+        ASK_PASSWORD_NO_TTY        = 1 << 4,  /* never ask for password on tty */
+        ASK_PASSWORD_NO_AGENT      = 1 << 5,  /* never ask for password via agent */
+        ASK_PASSWORD_CONSOLE_COLOR = 1 << 6,  /* Use color if /dev/console points to a console that supports color */
+        ASK_PASSWORD_NO_CREDENTIAL = 1 << 7,  /* never use $CREDENTIALS_DIRECTORY data */
+        ASK_PASSWORD_HIDE_EMOJI    = 1 << 8,  /* hide the lock and key emoji */
+        ASK_PASSWORD_HEADLESS      = 1 << 9,  /* headless mode: never query interactively */
+        ASK_PASSWORD_USER          = 1 << 10, /* query only our own agents, not any system password agents */
 } AskPasswordFlags;
 
 /* Encapsulates the mostly static fields of a password query */