]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
Merge pull request #30739 from poettering/pam-util-many
authorYu Watanabe <watanabe.yu+github@gmail.com>
Thu, 4 Jan 2024 14:28:34 +0000 (23:28 +0900)
committerGitHub <noreply@github.com>
Thu, 4 Jan 2024 14:28:34 +0000 (23:28 +0900)
pam-util: add pam_get_item_many() to shorten some code

59 files changed:
man/loginctl.xml
src/basic/missing_socket.h
src/basic/missing_wait.h [new file with mode: 0644]
src/basic/pidref.c
src/basic/pidref.h
src/basic/process-util.c
src/basic/process-util.h
src/basic/socket-util.c
src/basic/socket-util.h
src/basic/string-util.c
src/basic/string-util.h
src/basic/strv.c
src/basic/strv.h
src/core/dbus.c
src/core/exec-invoke.c
src/creds/creds.c
src/creds/io.systemd.credentials.policy [new file with mode: 0644]
src/creds/meson.build
src/fundamental/string-util-fundamental.c
src/fundamental/string-util-fundamental.h
src/home/homed-home.c
src/home/homed-manager-bus.c
src/home/homed-manager.c
src/home/org.freedesktop.home1.conf
src/hostname/hostnamed.c
src/import/importd.c
src/locale/localed-util.c
src/login/logind-session-dbus.c
src/login/logind-session.c
src/login/logind-session.h
src/login/logind-user.c
src/login/logind.c
src/login/pam_systemd.c
src/machine/machined.c
src/network/netdev/netdev.c
src/network/networkd-manager.c
src/network/networkd-route.c
src/oom/oomd-manager.c
src/pcrlock/pcrlock.c
src/portable/portabled.c
src/resolve/resolved-manager.c
src/shared/bus-polkit.c
src/shared/bus-polkit.h
src/shared/conf-parser.c
src/shared/discover-image.c
src/shared/json.c
src/shared/json.h
src/shared/varlink.c
src/shared/varlink.h
src/test/test-json.c
src/test/test-process-util.c
src/test/test-strv.c
src/timedate/timedated.c
src/timesync/timesyncd-manager.c
src/tpm2-setup/tpm2-generator.c
test/units/testsuite-35.sh
tools/update-man-rules.py
units/systemd-creds.socket
units/systemd-logind.service.in

index c8abf0972a6dfc4f44db82c44562c18542a6200b..8a093c6e2982d71dd4d474973242904a8adeced6 100644 (file)
       <varlistentry>
         <term><command>show-session</command> <optional><replaceable>ID</replaceable>…</optional></term>
 
-        <listitem><para>Show properties of one or more sessions or the
-        manager itself. If no argument is specified, properties of the
-        manager will be shown. If a session ID is specified,
-        properties of the session are shown. By default, empty
-        properties are suppressed. Use <option>--all</option> to show
-        those too. To select specific properties to show, use
-        <option>--property=</option>. This command is intended to be
-        used whenever computer-parsable output is required. Use
-        <command>session-status</command> if you are looking for
-        formatted human-readable output.</para>
+        <listitem><para>Show properties of one or more sessions or the manager itself. If no argument is
+        specified, properties of the manager will be shown. If a session ID is specified, properties of
+        the session are shown. Specially, if the given ID is <literal>self</literal>, the session to which
+        the <command>loginctl</command> process belongs is used. If <literal>auto</literal>, the current
+        session is used as with <literal>self</literal> if exists, and falls back to the current user's
+        graphical session. By default, empty properties are suppressed. Use <option>--all</option> to show
+        those too. To select specific properties to show, use <option>--property=</option>. This command
+        is intended to be used whenever computer-parsable output is required. Use <command>session-status</command>
+        if you are looking for formatted human-readable output.</para>
 
         <xi:include href="version-info.xml" xpointer="v233"/></listitem>
       </varlistentry>
       <varlistentry>
         <term><option>--kill-whom=</option></term>
 
-        <listitem><para>When used with
-        <command>kill-session</command>, choose which processes to
-        kill. Must be one of <option>leader</option>, or
-        <option>all</option> to select whether to kill only the leader
-        process of the session or all processes of the session. If
-        omitted, defaults to <option>all</option>.</para>
+        <listitem><para>When used with <command>kill-session</command>, choose which processes to kill.
+        Takes one of <literal>leader</literal> or <literal>all</literal>, to select whether to kill only
+        the leader process of the session or all processes of the session. If omitted, defaults to
+        <option>all</option>.</para>
 
         <xi:include href="version-info.xml" xpointer="v252"/></listitem>
       </varlistentry>
index 30ac297e171716519b748de8f9a752a967785223..3333cf18e7a6e88115bf7c8db898061774f5483e 100644 (file)
@@ -32,6 +32,10 @@ struct sockaddr_vm {
 #define SO_PEERGROUPS 59
 #endif
 
+#ifndef SO_PEERPIDFD
+#define SO_PEERPIDFD 77
+#endif
+
 #ifndef SO_BINDTOIFINDEX
 #define SO_BINDTOIFINDEX 62
 #endif
diff --git a/src/basic/missing_wait.h b/src/basic/missing_wait.h
new file mode 100644 (file)
index 0000000..a24779d
--- /dev/null
@@ -0,0 +1,8 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include <sys/wait.h>
+
+#ifndef P_PIDFD
+#define P_PIDFD 3
+#endif
index cf1c165b605b5ffba9943d61c4a9f6e9b9f47793..972853bbd6b99c7ed6cd76c8ccf50ac49e5ee0d4 100644 (file)
@@ -3,6 +3,7 @@
 #include "errno-util.h"
 #include "fd-util.h"
 #include "missing_syscall.h"
+#include "missing_wait.h"
 #include "parse-util.h"
 #include "pidref.h"
 #include "process-util.h"
@@ -302,6 +303,44 @@ bool pidref_is_self(const PidRef *pidref) {
         return pidref->pid == getpid_cached();
 }
 
+int pidref_wait(const PidRef *pidref, siginfo_t *ret, int options) {
+        int r;
+
+        if (!pidref_is_set(pidref))
+                return -ESRCH;
+
+        if (pidref->pid == 1 || pidref->pid == getpid_cached())
+                return -ECHILD;
+
+        siginfo_t si = {};
+
+        if (pidref->fd >= 0) {
+                r = RET_NERRNO(waitid(P_PIDFD, pidref->fd, &si, options));
+                if (r >= 0) {
+                        if (ret)
+                                *ret = si;
+                        return r;
+                }
+                if (r != -EINVAL) /* P_PIDFD was added in kernel 5.4 only */
+                        return r;
+        }
+
+        r = RET_NERRNO(waitid(P_PID, pidref->pid, &si, options));
+        if (r >= 0 && ret)
+                *ret = si;
+        return r;
+}
+
+int pidref_wait_for_terminate(const PidRef *pidref, siginfo_t *ret) {
+        int r;
+
+        for (;;) {
+                r = pidref_wait(pidref, ret, WEXITED);
+                if (r != -EINTR)
+                        return r;
+        }
+}
+
 static void pidref_hash_func(const PidRef *pidref, struct siphash *state) {
         siphash24_compress_typesafe(pidref->pid, state);
 }
index a01d4cc85ba078b8f1200895051d9fbd1bccb4fc..0fbffb332064fb4debf95df57e7bef8ec67ec57c 100644 (file)
@@ -55,7 +55,19 @@ int pidref_new_from_pid(pid_t pid, PidRef **ret);
 
 int pidref_kill(const PidRef *pidref, int sig);
 int pidref_kill_and_sigcont(const PidRef *pidref, int sig);
-int pidref_sigqueue(const PidRef *pidfref, int sig, int value);
+int pidref_sigqueue(const PidRef *pidref, int sig, int value);
+
+int pidref_wait(const PidRef *pidref, siginfo_t *siginfo, int options);
+int pidref_wait_for_terminate(const PidRef *pidref, siginfo_t *ret);
+
+static inline void pidref_done_sigkill_wait(PidRef *pidref) {
+        if (!pidref_is_set(pidref))
+                return;
+
+        (void) pidref_kill(pidref, SIGKILL);
+        (void) pidref_wait_for_terminate(pidref, NULL);
+        pidref_done(pidref);
+}
 
 int pidref_verify(const PidRef *pidref);
 
index 201c5596ae9f969ee771395189b036c52a59d4e8..4d5c01d2cffec0815a8553037f9157b110bf7977 100644 (file)
@@ -730,6 +730,82 @@ int get_process_ppid(pid_t pid, pid_t *ret) {
         return 0;
 }
 
+int pid_get_start_time(pid_t pid, uint64_t *ret) {
+        _cleanup_free_ char *line = NULL;
+        const char *p;
+        int r;
+
+        assert(pid >= 0);
+
+        p = procfs_file_alloca(pid, "stat");
+        r = read_one_line_file(p, &line);
+        if (r == -ENOENT)
+                return -ESRCH;
+        if (r < 0)
+                return r;
+
+        /* Let's skip the pid and comm fields. The latter is enclosed in () but does not escape any () in its
+         * value, so let's skip over it manually */
+
+        p = strrchr(line, ')');
+        if (!p)
+                return -EIO;
+
+        p++;
+
+        unsigned long llu;
+
+        if (sscanf(p, " "
+                   "%*c "  /* state */
+                   "%*u " /* ppid */
+                   "%*u " /* pgrp */
+                   "%*u " /* session */
+                   "%*u " /* tty_nr */
+                   "%*u " /* tpgid */
+                   "%*u " /* flags */
+                   "%*u " /* minflt */
+                   "%*u " /* cminflt */
+                   "%*u " /* majflt */
+                   "%*u " /* cmajflt */
+                   "%*u " /* utime */
+                   "%*u " /* stime */
+                   "%*u " /* cutime */
+                   "%*u " /* cstime */
+                   "%*i " /* priority */
+                   "%*i " /* nice */
+                   "%*u " /* num_threads */
+                   "%*u " /* itrealvalue */
+                   "%lu ", /* starttime */
+                   &llu) != 1)
+                return -EIO;
+
+        if (ret)
+                *ret = llu;
+
+        return 0;
+}
+
+int pidref_get_start_time(const PidRef *pid, uint64_t *ret) {
+        uint64_t t;
+        int r;
+
+        if (!pidref_is_set(pid))
+                return -ESRCH;
+
+        r = pid_get_start_time(pid->pid, ret ? &t : NULL);
+        if (r < 0)
+                return r;
+
+        r = pidref_verify(pid);
+        if (r < 0)
+                return r;
+
+        if (ret)
+                *ret = t;
+
+        return 0;
+}
+
 int get_process_umask(pid_t pid, mode_t *ret) {
         _cleanup_free_ char *m = NULL;
         const char *p;
@@ -1112,8 +1188,10 @@ int pidref_is_alive(const PidRef *pidref) {
                 return -ESRCH;
 
         result = pid_is_alive(pidref->pid);
-        if (result < 0)
+        if (result < 0) {
+                assert(result != -ESRCH);
                 return result;
+        }
 
         r = pidref_verify(pidref);
         if (r == -ESRCH)
@@ -1650,6 +1728,30 @@ int safe_fork_full(
         return 0;
 }
 
+int pidref_safe_fork_full(
+                const char *name,
+                const int stdio_fds[3],
+                const int except_fds[],
+                size_t n_except_fds,
+                ForkFlags flags,
+                PidRef *ret_pid) {
+
+        pid_t pid;
+        int r, q;
+
+        assert(!FLAGS_SET(flags, FORK_WAIT));
+
+        r = safe_fork_full(name, stdio_fds, except_fds, n_except_fds, flags, &pid);
+        if (r < 0)
+                return r;
+
+        q = pidref_set_pid(ret_pid, pid);
+        if (q < 0) /* Let's not fail for this, no matter what, the process exists after all, and that's key */
+                *ret_pid = PIDREF_MAKE_FROM_PID(pid);
+
+        return r;
+}
+
 int namespace_fork(
                 const char *outer_name,
                 const char *inner_name,
index af6cba13ebdd3f6e09422fe71b3ad475b57acf3f..de6a2bd2038cfff6897375c48dfbbb64a1c9aa2f 100644 (file)
@@ -54,6 +54,8 @@ int get_process_cwd(pid_t pid, char **ret);
 int get_process_root(pid_t pid, char **ret);
 int get_process_environ(pid_t pid, char **ret);
 int get_process_ppid(pid_t pid, pid_t *ret);
+int pid_get_start_time(pid_t pid, uint64_t *ret);
+int pidref_get_start_time(const PidRef* pid, uint64_t *ret);
 int get_process_umask(pid_t pid, mode_t *ret);
 
 int container_get_leader(const char *machine, pid_t *pid);
@@ -191,6 +193,18 @@ static inline int safe_fork(const char *name, ForkFlags flags, pid_t *ret_pid) {
         return safe_fork_full(name, NULL, NULL, 0, flags, ret_pid);
 }
 
+int pidref_safe_fork_full(
+                const char *name,
+                const int stdio_fds[3],
+                const int except_fds[],
+                size_t n_except_fds,
+                ForkFlags flags,
+                PidRef *ret_pid);
+
+static inline int pidref_safe_fork(const char *name, ForkFlags flags, PidRef *ret_pid) {
+        return pidref_safe_fork_full(name, NULL, NULL, 0, flags, ret_pid);
+}
+
 int namespace_fork(const char *outer_name, const char *inner_name, const int except_fds[], size_t n_except_fds, ForkFlags flags, int pidns_fd, int mntns_fd, int netns_fd, int userns_fd, int root_fd, pid_t *ret_pid);
 
 int set_oom_score_adjust(int value);
index 86472c88474f6de43ab1e9037b3991bbca6d8a68..98133a2ecda7686e851dbeab76ced51bf75656e5 100644 (file)
@@ -956,6 +956,21 @@ int getpeergroups(int fd, gid_t **ret) {
         return (int) n;
 }
 
+int getpeerpidfd(int fd) {
+        socklen_t n = sizeof(int);
+        int pidfd = -EBADF;
+
+        assert(fd >= 0);
+
+        if (getsockopt(fd, SOL_SOCKET, SO_PEERPIDFD, &pidfd, &n) < 0)
+                return -errno;
+
+        if (n != sizeof(int))
+                return -EIO;
+
+        return pidfd;
+}
+
 ssize_t send_many_fds_iov_sa(
                 int transport_fd,
                 int *fds_array, size_t n_fds_array,
index 9a11df834d113ebd26a76a88458dc62d52eef185..032d73857ed7f43d790d3df9d070cac4148c7926 100644 (file)
@@ -152,6 +152,7 @@ bool address_label_valid(const char *p);
 int getpeercred(int fd, struct ucred *ucred);
 int getpeersec(int fd, char **ret);
 int getpeergroups(int fd, gid_t **ret);
+int getpeerpidfd(int fd);
 
 ssize_t send_many_fds_iov_sa(
                 int transport_fd,
index f6453990bd9e9b73edd02b2e639dcef3c7be2b04..8b039ebd9843dd258ea8454c3d10d56c28a5d1f0 100644 (file)
@@ -1420,18 +1420,6 @@ char *find_line_startswith(const char *haystack, const char *needle) {
         return p + strlen(needle);
 }
 
-char *startswith_strv(const char *string, char **strv) {
-        char *found = NULL;
-
-        STRV_FOREACH(i, strv) {
-                found = startswith(string, *i);
-                if (found)
-                        break;
-        }
-
-        return found;
-}
-
 bool version_is_valid(const char *s) {
         if (isempty(s))
                 return false;
@@ -1537,7 +1525,7 @@ char *strrstr(const char *haystack, const char *needle) {
                 return strchr(haystack, 0);
 
         for (const char *p = haystack; *p; p++)
-                if (strncmp(p, needle, l) == 0)
+                if (strneq(p, needle, l))
                         f = p;
 
         return (char*) f;
index bf427cd7f7a67144c5272504e93739c3ddf8acb0..e162765aa7174a8113952e2b606b5c22b09cb482 100644 (file)
@@ -291,11 +291,6 @@ char *strdupcspn(const char *a, const char *reject);
 
 char *find_line_startswith(const char *haystack, const char *needle);
 
-char *startswith_strv(const char *string, char **strv);
-
-#define STARTSWITH_SET(p, ...)                                  \
-        startswith_strv(p, STRV_MAKE(__VA_ARGS__))
-
 bool version_is_valid(const char *s);
 
 bool version_is_valid_versionspec(const char *s);
index 43a4f569bd25e492fd3f897058268b8f02972c14..908e9e251398a15e5e95b01622dd003c7d4b0f6c 100644 (file)
@@ -706,6 +706,26 @@ int strv_extendf(char ***l, const char *format, ...) {
         return strv_consume(l, x);
 }
 
+char* startswith_strv(const char *s, char * const *l) {
+        STRV_FOREACH(i, l) {
+                char *found = startswith(s, *i);
+                if (found)
+                        return found;
+        }
+
+        return NULL;
+}
+
+char* endswith_strv(const char *s, char * const *l) {
+        STRV_FOREACH(i, l) {
+                char *found = endswith(s, *i);
+                if (found)
+                        return found;
+        }
+
+        return NULL;
+}
+
 char** strv_reverse(char **l) {
         size_t n;
 
@@ -905,13 +925,3 @@ int _string_strv_ordered_hashmap_put(OrderedHashmap **h, const char *key, const
 }
 
 DEFINE_HASH_OPS_FULL(string_strv_hash_ops, char, string_hash_func, string_compare_func, free, char*, strv_free);
-
-char* strv_endswith(const char *s, char **l) {
-        STRV_FOREACH(i, l) {
-                char *e = endswith(s, *i);
-                if (e)
-                        return (char*) e;
-        }
-
-        return NULL;
-}
index 18df0f23f246fe3ff0c02d588da5843b1534abd0..f1a8bc49109540bfbbecf94d4ce9f52e997937b4 100644 (file)
@@ -159,6 +159,16 @@ static inline void strv_print(char * const *l) {
         strv_print_full(l, NULL);
 }
 
+char* startswith_strv(const char *s, char * const *l);
+
+#define STARTSWITH_SET(p, ...)                                  \
+        startswith_strv(p, STRV_MAKE(__VA_ARGS__))
+
+char* endswith_strv(const char *s, char * const *l);
+
+#define ENDSWITH_SET(p, ...)                                    \
+        endswith_strv(p, STRV_MAKE(__VA_ARGS__))
+
 #define strv_from_stdarg_alloca(first)                          \
         ({                                                      \
                 char **_l;                                      \
@@ -202,18 +212,6 @@ static inline void strv_print(char * const *l) {
                 _x && strv_contains_case(STRV_MAKE(__VA_ARGS__), _x); \
         })
 
-#define ENDSWITH_SET(p, ...)                                    \
-        ({                                                      \
-                const char *_p = (p);                           \
-                char *_found = NULL;                            \
-                STRV_FOREACH(_i, STRV_MAKE(__VA_ARGS__)) {      \
-                        _found = endswith(_p, *_i);             \
-                        if (_found)                             \
-                                break;                          \
-                }                                               \
-                _found;                                         \
-        })
-
 #define _FOREACH_STRING(uniq, x, y, ...)                                \
         for (const char *x, * const*UNIQ_T(l, uniq) = STRV_MAKE_CONST(({ x = y; }), ##__VA_ARGS__); \
              x;                                                         \
@@ -252,5 +250,3 @@ int _string_strv_hashmap_put(Hashmap **h, const char *key, const char *value  HA
 int _string_strv_ordered_hashmap_put(OrderedHashmap **h, const char *key, const char *value  HASHMAP_DEBUG_PARAMS);
 #define string_strv_hashmap_put(h, k, v) _string_strv_hashmap_put(h, k, v  HASHMAP_DEBUG_SRC_ARGS)
 #define string_strv_ordered_hashmap_put(h, k, v) _string_strv_ordered_hashmap_put(h, k, v  HASHMAP_DEBUG_SRC_ARGS)
-
-char* strv_endswith(const char *s, char **l);
index f7d4a97096215dd4a8a4d27e836b7a4e0b73845a..e24c5bbc53394140e9a5276b01d3642b9c8ad1e8 100644 (file)
@@ -1073,7 +1073,7 @@ void bus_done(Manager *m) {
         assert(!m->subscribed);
 
         m->deserialized_subscribed = strv_free(m->deserialized_subscribed);
-        bus_verify_polkit_async_registry_free(m->polkit_registry);
+        m->polkit_registry = hashmap_free(m->polkit_registry);
 }
 
 int bus_fdset_add_all(Manager *m, FDSet *fds) {
index 3b3250f32ed045017454993e46277ca47ff81f37..f39b53280067157b1db4cee58458fd9e0b7af040 100644 (file)
@@ -1107,7 +1107,8 @@ static int setup_pam(
                 gid_t gid,
                 const char *tty,
                 char ***env, /* updated on success */
-                const int fds[], size_t n_fds) {
+                const int fds[], size_t n_fds,
+                int exec_fd) {
 
 #if HAVE_PAM
 
@@ -1210,6 +1211,11 @@ static int setup_pam(
                  * those fds are open here that have been opened by PAM. */
                 (void) close_many(fds, n_fds);
 
+                /* Also close the 'exec_fd' in the child, since the service manager waits for the EOF induced
+                 * by the execve() to wait for completion, and if we'd keep the fd open here in the child
+                 * we'd never signal completion. */
+                exec_fd = safe_close(exec_fd);
+
                 /* Drop privileges - we don't need any to pam_close_session and this will make
                  * PR_SET_PDEATHSIG work in most cases.  If this fails, ignore the error - but expect sd-pam
                  * threads to fail to exit normally */
@@ -4631,7 +4637,7 @@ int exec_invoke(
                  * wins here. (See above.) */
 
                 /* All fds passed in the fds array will be closed in the pam child process. */
-                r = setup_pam(context->pam_name, username, uid, gid, context->tty_path, &accum_env, params->fds, n_fds);
+                r = setup_pam(context->pam_name, username, uid, gid, context->tty_path, &accum_env, params->fds, n_fds, params->exec_fd);
                 if (r < 0) {
                         *exit_status = EXIT_PAM;
                         return log_exec_error_errno(context, params, r, "Failed to set up PAM session: %m");
index ed39ffe51e138f46f99cec4e507bef7b50431e2b..5586baff9a49c882318fa668828e6194fb0462c8 100644 (file)
@@ -4,6 +4,7 @@
 #include <unistd.h>
 
 #include "build.h"
+#include "bus-polkit.h"
 #include "creds-util.h"
 #include "dirent-util.h"
 #include "escape.h"
@@ -983,6 +984,7 @@ static int vl_method_encrypt(Varlink *link, JsonVariant *parameters, VarlinkMeth
                 { "data",      JSON_VARIANT_STRING,        json_dispatch_unbase64_iovec, offsetof(MethodEncryptParameters, data),      0 },
                 { "timestamp", _JSON_VARIANT_TYPE_INVALID, json_dispatch_uint64,         offsetof(MethodEncryptParameters, timestamp), 0 },
                 { "notAfter",  _JSON_VARIANT_TYPE_INVALID, json_dispatch_uint64,         offsetof(MethodEncryptParameters, not_after), 0 },
+                VARLINK_DISPATCH_POLKIT_FIELD,
                 {}
         };
         _cleanup_(method_encrypt_parameters_done) MethodEncryptParameters p = {
@@ -990,6 +992,7 @@ static int vl_method_encrypt(Varlink *link, JsonVariant *parameters, VarlinkMeth
                 .not_after = UINT64_MAX,
         };
         _cleanup_(iovec_done) struct iovec output = {};
+        Hashmap **polkit_registry = ASSERT_PTR(userdata);
         int r;
 
         assert(link);
@@ -1010,6 +1013,16 @@ static int vl_method_encrypt(Varlink *link, JsonVariant *parameters, VarlinkMeth
         if (p.not_after != UINT64_MAX && p.not_after < p.timestamp)
                 return varlink_error_invalid_parameter_name(link, "notAfter");
 
+        r = varlink_verify_polkit_async(
+                        link,
+                        /* bus= */ NULL,
+                        "io.systemd.credentials.encrypt",
+                        /* details= */ NULL,
+                        /* good_user= */ UID_INVALID,
+                        polkit_registry);
+        if (r <= 0)
+                return r;
+
         r = encrypt_credential_and_warn(
                         arg_with_key,
                         p.name,
@@ -1051,15 +1064,17 @@ static void method_decrypt_parameters_done(MethodDecryptParameters *p) {
 static int vl_method_decrypt(Varlink *link, JsonVariant *parameters, VarlinkMethodFlags flags, void *userdata) {
 
         static const JsonDispatch dispatch_table[] = {
-                { "name",      JSON_VARIANT_STRING,        json_dispatch_const_string,   offsetof(MethodDecryptParameters, name),      0 },
-                { "blob",      JSON_VARIANT_STRING,        json_dispatch_unbase64_iovec, offsetof(MethodDecryptParameters, blob),      0 },
-                { "timestamp", _JSON_VARIANT_TYPE_INVALID, json_dispatch_uint64,         offsetof(MethodDecryptParameters, timestamp), 0 },
+                { "name",      JSON_VARIANT_STRING,        json_dispatch_const_string,   offsetof(MethodDecryptParameters, name),      0              },
+                { "blob",      JSON_VARIANT_STRING,        json_dispatch_unbase64_iovec, offsetof(MethodDecryptParameters, blob),      JSON_MANDATORY },
+                { "timestamp", _JSON_VARIANT_TYPE_INVALID, json_dispatch_uint64,         offsetof(MethodDecryptParameters, timestamp), 0              },
+                VARLINK_DISPATCH_POLKIT_FIELD,
                 {}
         };
         _cleanup_(method_decrypt_parameters_done) MethodDecryptParameters p = {
                 .timestamp = UINT64_MAX,
         };
         _cleanup_(iovec_done_erase) struct iovec output = {};
+        Hashmap **polkit_registry = ASSERT_PTR(userdata);
         int r;
 
         assert(link);
@@ -1073,11 +1088,19 @@ static int vl_method_decrypt(Varlink *link, JsonVariant *parameters, VarlinkMeth
 
         if (p.name && !credential_name_valid(p.name))
                 return varlink_error_invalid_parameter_name(link, "name");
-        if (!p.blob.iov_base)
-                return varlink_error_invalid_parameter_name(link, "blob");
         if (p.timestamp == UINT64_MAX)
                 p.timestamp = now(CLOCK_REALTIME);
 
+        r = varlink_verify_polkit_async(
+                        link,
+                        /* bus= */ NULL,
+                        "io.systemd.credentials.decrypt",
+                        /* details= */ NULL,
+                        /* good_user= */ UID_INVALID,
+                        polkit_registry);
+        if (r <= 0)
+                return r;
+
         r = decrypt_credential_and_warn(
                         p.name,
                         p.timestamp,
@@ -1116,10 +1139,11 @@ static int run(int argc, char *argv[]) {
 
         if (arg_varlink) {
                 _cleanup_(varlink_server_unrefp) VarlinkServer *varlink_server = NULL;
+                _cleanup_(hashmap_freep) Hashmap *polkit_registry = NULL;
 
                 /* Invocation as Varlink service */
 
-                r = varlink_server_new(&varlink_server, VARLINK_SERVER_ROOT_ONLY|VARLINK_SERVER_INHERIT_USERDATA);
+                r = varlink_server_new(&varlink_server, VARLINK_SERVER_ACCOUNT_UID|VARLINK_SERVER_INHERIT_USERDATA);
                 if (r < 0)
                         return log_error_errno(r, "Failed to allocate Varlink server: %m");
 
@@ -1134,6 +1158,8 @@ static int run(int argc, char *argv[]) {
                 if (r < 0)
                         return log_error_errno(r, "Failed to bind Varlink methods: %m");
 
+                varlink_server_set_userdata(varlink_server, &polkit_registry);
+
                 r = varlink_server_loop_auto(varlink_server);
                 if (r < 0)
                         return log_error_errno(r, "Failed to run Varlink event loop: %m");
diff --git a/src/creds/io.systemd.credentials.policy b/src/creds/io.systemd.credentials.policy
new file mode 100644 (file)
index 0000000..f94571b
--- /dev/null
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="UTF-8"?> <!--*-nxml-*-->
+<!DOCTYPE policyconfig PUBLIC "-//freedesktop//DTD PolicyKit Policy Configuration 1.0//EN"
+        "https://www.freedesktop.org/standards/PolicyKit/1/policyconfig.dtd">
+
+<!--
+  SPDX-License-Identifier: LGPL-2.1-or-later
+
+  This file is part of systemd.
+
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU Lesser General Public License as published by
+  the Free Software Foundation; either version 2.1 of the License, or
+  (at your option) any later version.
+-->
+
+<policyconfig>
+
+        <vendor>The systemd Project</vendor>
+        <vendor_url>https://systemd.io</vendor_url>
+
+        <action id="io.systemd.credentials.encrypt">
+                <description gettext-domain="systemd">Allow encryption and signing of system credentials.</description>
+                <message gettext-domain="systemd">Authentication is required for an application to encrypt and sign a system credential.</message>
+                <defaults>
+                        <allow_any>auth_admin_keep</allow_any>
+                        <allow_inactive>auth_admin_keep</allow_inactive>
+                        <allow_active>auth_admin_keep</allow_active>
+                </defaults>
+        </action>
+
+        <action id="io.systemd.credentials.decrypt">
+                <description gettext-domain="systemd">Allow decryption of system credentials.</description>
+                <message gettext-domain="systemd">Authentication is required for an application to decrypto a system credential.</message>
+                <defaults>
+                        <allow_any>auth_admin_keep</allow_any>
+                        <allow_inactive>auth_admin_keep</allow_inactive>
+                        <allow_active>auth_admin_keep</allow_active>
+                </defaults>
+        </action>
+</policyconfig>
index 85572568f6c0b85c91676b569962fc9dac0e613c..24833110d532239c7df39fc0d46eade8bdc05c65 100644 (file)
@@ -23,3 +23,6 @@ if install_sysconfdir
         install_emptydir(sysconfdir / 'credstore.encrypted',
                          install_mode : 'rwx------')
 endif
+
+install_data('io.systemd.credentials.policy',
+             install_dir : polkitpolicydir)
index a5bafc63f4769e3202532e352f9bd3e3e8746b48..a18b2bc4c9d1494f6ba9c30422e46d36675a053f 100644 (file)
@@ -33,14 +33,14 @@ sd_char *startswith_no_case(const sd_char *s, const sd_char *prefix) {
         return (sd_char*) s + l;
 }
 
-sd_char* endswith(const sd_char *s, const sd_char *postfix) {
+sd_char* endswith(const sd_char *s, const sd_char *suffix) {
         size_t sl, pl;
 
         assert(s);
-        assert(postfix);
+        assert(suffix);
 
         sl = strlen(s);
-        pl = strlen(postfix);
+        pl = strlen(suffix);
 
         if (pl == 0)
                 return (sd_char*) s + sl;
@@ -48,20 +48,20 @@ sd_char* endswith(const sd_char *s, const sd_char *postfix) {
         if (sl < pl)
                 return NULL;
 
-        if (strcmp(s + sl - pl, postfix) != 0)
+        if (!streq(s + sl - pl, suffix))
                 return NULL;
 
         return (sd_char*) s + sl - pl;
 }
 
-sd_char* endswith_no_case(const sd_char *s, const sd_char *postfix) {
+sd_char* endswith_no_case(const sd_char *s, const sd_char *suffix) {
         size_t sl, pl;
 
         assert(s);
-        assert(postfix);
+        assert(suffix);
 
         sl = strlen(s);
-        pl = strlen(postfix);
+        pl = strlen(suffix);
 
         if (pl == 0)
                 return (sd_char*) s + sl;
@@ -69,7 +69,7 @@ sd_char* endswith_no_case(const sd_char *s, const sd_char *postfix) {
         if (sl < pl)
                 return NULL;
 
-        if (strcasecmp(s + sl - pl, postfix) != 0)
+        if (!strcaseeq(s + sl - pl, suffix))
                 return NULL;
 
         return (sd_char*) s + sl - pl;
index b537b2e31c738d559c0ebfcc4e5c39e729a0dcbf..419f1cc3da43e9e9bd036fffb24d4133c9b73a9a 100644 (file)
@@ -59,8 +59,8 @@ static inline size_t strlen_ptr(const sd_char *s) {
 
 sd_char *startswith(const sd_char *s, const sd_char *prefix) _pure_;
 sd_char *startswith_no_case(const sd_char *s, const sd_char *prefix) _pure_;
-sd_char *endswith(const sd_char *s, const sd_char *postfix) _pure_;
-sd_char *endswith_no_case(const sd_char *s, const sd_char *postfix) _pure_;
+sd_char *endswith(const sd_char *s, const sd_char *suffix) _pure_;
+sd_char *endswith_no_case(const sd_char *s, const sd_char *suffix) _pure_;
 
 static inline bool isempty(const sd_char *a) {
         return !a || a[0] == '\0';
index 7673e5043576bc830e9d0272f10a04cbfc0b3a16..787dc773ac9f9563016eb7f6e9243acd0a1a7b01 100644 (file)
@@ -650,11 +650,17 @@ static int convert_worker_errno(Home *h, int e, sd_bus_error *error) {
         return 0;
 }
 
-static void home_count_bad_authentication(Home *h, bool save) {
+static void home_count_bad_authentication(Home *h, int error, bool save) {
         int r;
 
         assert(h);
 
+        if (!IN_SET(error,
+                    -ENOKEY,       /* Password incorrect */
+                    -EBADSLT,      /* Password incorrect and no token */
+                    -EREMOTEIO))   /* Recovery key incorrect */
+                return;
+
         r = user_record_bad_authentication(h->record);
         if (r < 0) {
                 log_warning_errno(r, "Failed to increase bad authentication counter, ignoring: %m");
@@ -680,8 +686,7 @@ static void home_fixate_finish(Home *h, int ret, UserRecord *hr) {
         secret = TAKE_PTR(h->secret); /* Take possession */
 
         if (ret < 0) {
-                if (ret == -ENOKEY)
-                        (void) home_count_bad_authentication(h, false);
+                (void) home_count_bad_authentication(h, ret, /* save= */ false);
 
                 (void) convert_worker_errno(h, ret, &error);
                 r = log_error_errno(ret, "Fixation failed: %m");
@@ -743,6 +748,27 @@ fail:
         home_set_state(h, HOME_UNFIXATED);
 }
 
+static bool error_is_bad_password(int ret) {
+        /* Tests for the various cases of bad passwords. We generally don't want to log so loudly about
+         * these, since everyone types in a bad password now and then. Moreover we usually try to start out
+         * with an empty set of passwords, so the first authentication will frequently fail, if not token is
+         * inserted. */
+
+        return IN_SET(ret,
+                      -ENOKEY,       /* Bad password, or insufficient */
+                      -EBADSLT,      /* Bad password, and no token */
+                      -EREMOTEIO,    /* Bad recovery key */
+                      -ENOANO,       /* PIN for security token needed */
+                      -ERFKILL,      /* "Protected Authentication Path" for token needed */
+                      -EMEDIUMTYPE,  /* Presence confirmation on token needed */
+                      -ENOCSI,       /* User verification on token needed */
+                      -ENOSTR,       /* Token action timeout */
+                      -EOWNERDEAD,   /* PIN locked of security token */
+                      -ENOLCK,       /* Bad PIN of security token */
+                      -ETOOMANYREFS, /* Bad PIN and few tries left */
+                      -EUCLEAN);     /* Bad PIN and one try left */
+}
+
 static void home_activate_finish(Home *h, int ret, UserRecord *hr) {
         _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
         int r;
@@ -751,11 +777,11 @@ static void home_activate_finish(Home *h, int ret, UserRecord *hr) {
         assert(IN_SET(h->state, HOME_ACTIVATING, HOME_ACTIVATING_FOR_ACQUIRE));
 
         if (ret < 0) {
-                if (ret == -ENOKEY)
-                        home_count_bad_authentication(h, true);
+                (void) home_count_bad_authentication(h, ret, /* save= */ true);
 
                 (void) convert_worker_errno(h, ret, &error);
-                r = log_error_errno(ret, "Activation failed: %m");
+                r = log_full_errno(error_is_bad_password(ret) ? LOG_NOTICE : LOG_ERR,
+                                   ret, "Activation failed: %s", bus_error_message(&error, ret));
                 goto finish;
         }
 
@@ -912,11 +938,11 @@ static void home_change_finish(Home *h, int ret, UserRecord *hr) {
         assert(h);
 
         if (ret < 0) {
-                if (ret == -ENOKEY)
-                        (void) home_count_bad_authentication(h, true);
+                (void) home_count_bad_authentication(h, ret, /* save= */ true);
 
                 (void) convert_worker_errno(h, ret, &error);
-                r = log_error_errno(ret, "Change operation failed: %m");
+                r = log_full_errno(error_is_bad_password(ret) ? LOG_NOTICE : LOG_ERR,
+                                   ret, "Change operation failed: %s", bus_error_message(&error, ret));
                 goto finish;
         }
 
@@ -982,11 +1008,11 @@ static void home_unlocking_finish(Home *h, int ret, UserRecord *hr) {
         assert(IN_SET(h->state, HOME_UNLOCKING, HOME_UNLOCKING_FOR_ACQUIRE));
 
         if (ret < 0) {
-                if (ret == -ENOKEY)
-                        (void) home_count_bad_authentication(h, true);
+                (void) home_count_bad_authentication(h, ret, /* save= */ true);
 
                 (void) convert_worker_errno(h, ret, &error);
-                r = log_error_errno(ret, "Unlocking operation failed: %m");
+                r = log_full_errno(error_is_bad_password(ret) ? LOG_NOTICE : LOG_ERR,
+                                   ret, "Unlocking operation failed: %s", bus_error_message(&error, ret));
 
                 /* Revert to locked state */
                 home_set_state(h, HOME_LOCKED);
@@ -1018,11 +1044,11 @@ static void home_authenticating_finish(Home *h, int ret, UserRecord *hr) {
         assert(IN_SET(h->state, HOME_AUTHENTICATING, HOME_AUTHENTICATING_WHILE_ACTIVE, HOME_AUTHENTICATING_FOR_ACQUIRE));
 
         if (ret < 0) {
-                if (ret == -ENOKEY)
-                        (void) home_count_bad_authentication(h, true);
+                (void) home_count_bad_authentication(h, ret, /* save= */ true);
 
                 (void) convert_worker_errno(h, ret, &error);
-                r = log_error_errno(ret, "Authentication failed: %m");
+                r = log_full_errno(error_is_bad_password(ret) ? LOG_NOTICE : LOG_ERR,
+                                   ret, "Authentication failed: %s", bus_error_message(&error, ret));
                 goto finish;
         }
 
index b5dffb2c695af826743c5892f6b8101ee9490e98..cc32a0e852e59be3726b00fa16eb913b39007cf3 100644 (file)
@@ -61,6 +61,53 @@ static int property_get_auto_login(
         return sd_bus_message_close_container(reply);
 }
 
+static int lookup_user_name(
+                Manager *m,
+                sd_bus_message *message,
+                const char *user_name,
+                sd_bus_error *error,
+                Home **ret) {
+
+        Home *h;
+        int r;
+
+        assert(m);
+        assert(message);
+        assert(user_name);
+        assert(ret);
+
+        if (isempty(user_name)) {
+                _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
+                uid_t uid;
+
+                /* If an empty user name is specified, then identify caller's EUID and find home by that. */
+
+                r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_EUID, &creds);
+                if (r < 0)
+                        return r;
+
+                r = sd_bus_creds_get_euid(creds, &uid);
+                if (r < 0)
+                        return r;
+
+                h = hashmap_get(m->homes_by_uid, UID_TO_PTR(uid));
+                if (!h)
+                        return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_HOME, "Client's UID " UID_FMT " not managed.", uid);
+
+        } else {
+
+                if (!valid_user_group_name(user_name, 0))
+                        return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "User name %s is not valid", user_name);
+
+                h = hashmap_get(m->homes_by_name, user_name);
+                if (!h)
+                        return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_HOME, "No home for user %s known", user_name);
+        }
+
+        *ret = h;
+        return 0;
+}
+
 static int method_get_home_by_name(
                 sd_bus_message *message,
                 void *userdata,
@@ -77,12 +124,10 @@ static int method_get_home_by_name(
         r = sd_bus_message_read(message, "s", &user_name);
         if (r < 0)
                 return r;
-        if (!valid_user_group_name(user_name, 0))
-                return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "User name %s is not valid", user_name);
 
-        h = hashmap_get(m->homes_by_name, user_name);
-        if (!h)
-                return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_HOME, "No home for user %s known", user_name);
+        r = lookup_user_name(m, message, user_name, error, &h);
+        if (r < 0)
+                return r;
 
         r = bus_home_path(h, &path);
         if (r < 0)
@@ -204,12 +249,10 @@ static int method_get_user_record_by_name(
         r = sd_bus_message_read(message, "s", &user_name);
         if (r < 0)
                 return r;
-        if (!valid_user_group_name(user_name, 0))
-                return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "User name %s is not valid", user_name);
 
-        h = hashmap_get(m->homes_by_name, user_name);
-        if (!h)
-                return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_HOME, "No home for user %s known", user_name);
+        r = lookup_user_name(m, message, user_name, error, &h);
+        if (r < 0)
+                return r;
 
         r = bus_home_get_record_json(h, message, &json, &incomplete);
         if (r < 0)
@@ -278,12 +321,9 @@ static int generic_home_method(
         if (r < 0)
                 return r;
 
-        if (!valid_user_group_name(user_name, 0))
-                return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "User name %s is not valid", user_name);
-
-        h = hashmap_get(m->homes_by_name, user_name);
-        if (!h)
-                return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_HOME, "No home for user %s known", user_name);
+        r = lookup_user_name(m, message, user_name, error, &h);
+        if (r < 0)
+                return r;
 
         return handler(message, h, error);
 }
index c4525310fc20ff7944a644d6634edbf5d96a0bed..94b2ea5181a16b25e3872b8836728801142f209d 100644 (file)
@@ -268,7 +268,7 @@ Manager* manager_free(Manager *m) {
                 (void) home_wait_for_worker(h);
 
         m->bus = sd_bus_flush_close_unref(m->bus);
-        m->polkit_registry = bus_verify_polkit_async_registry_free(m->polkit_registry);
+        m->polkit_registry = hashmap_free(m->polkit_registry);
 
         m->device_monitor = sd_device_monitor_unref(m->device_monitor);
 
index de1fb93cc0d81cd386683417047e722f6fe1c758..5af1a686074433915c84f416df1ed2647e5af08d 100644 (file)
                        send_interface="org.freedesktop.home1.Manager"
                        send_member="LockAllHomes"/>
 
+                <allow send_destination="org.freedesktop.home1"
+                       send_interface="org.freedesktop.home1.Manager"
+                       send_member="DeactivateAllHomes"/>
+
                 <allow send_destination="org.freedesktop.home1"
                        send_interface="org.freedesktop.home1.Manager"
                        send_member="Rebalance"/>
index 893eb4cc0f15be02265cdc8f037419fdea805d62..f0e643822a3bc6c2e080eea9bd8c48ad15171385 100644 (file)
@@ -91,7 +91,7 @@ static void context_destroy(Context *c) {
         assert(c);
 
         context_reset(c, UINT64_MAX);
-        bus_verify_polkit_async_registry_free(c->polkit_registry);
+        hashmap_free(c->polkit_registry);
 }
 
 static void context_read_etc_hostname(Context *c) {
index e9bbbb628dae84697b66a2bfc72d7287a3d07558..8bc8a328666765c40fd31630669ec2f133cce42d 100644 (file)
@@ -527,7 +527,7 @@ static Manager *manager_unref(Manager *m) {
 
         hashmap_free(m->transfers);
 
-        bus_verify_polkit_async_registry_free(m->polkit_registry);
+        hashmap_free(m->polkit_registry);
 
         m->bus = sd_bus_flush_close_unref(m->bus);
         sd_event_unref(m->event);
index e4e57a0f4a613250d52508fdac700ffe84cdc90d..0489df573b593077547bf1ca3df6324fc4d3e53a 100644 (file)
@@ -304,7 +304,7 @@ void context_clear(Context *c) {
         c->x11_cache = sd_bus_message_unref(c->x11_cache);
         c->vc_cache = sd_bus_message_unref(c->vc_cache);
 
-        c->polkit_registry = bus_verify_polkit_async_registry_free(c->polkit_registry);
+        c->polkit_registry = hashmap_free(c->polkit_registry);
 };
 
 X11Context *context_get_x11_context(Context *c) {
index 9348ccc4dd0e3f7a65306c12fa6a9febbf66f3ca..15f04c4a66aaeedd08511cc9bd732e9f5579d0a9 100644 (file)
@@ -807,7 +807,6 @@ int session_send_create_reply(Session *s, sd_bus_error *error) {
         _cleanup_(sd_bus_message_unrefp) sd_bus_message *c = NULL;
         _cleanup_close_ int fifo_fd = -EBADF;
         _cleanup_free_ char *p = NULL;
-        int r;
 
         assert(s);
 
@@ -828,10 +827,6 @@ int session_send_create_reply(Session *s, sd_bus_error *error) {
         if (fifo_fd < 0)
                 return fifo_fd;
 
-        r = session_watch_pidfd(s);
-        if (r < 0)
-                return r;
-
         /* Update the session state file before we notify the client about the result. */
         session_save(s);
 
index b4277c33f1489d1f2c826bb4909bfc59baadcc53..cdd299e0064933c1d171ca36c3fa2f16aa684cc7 100644 (file)
@@ -15,6 +15,7 @@
 #include "audit-util.h"
 #include "bus-error.h"
 #include "bus-util.h"
+#include "daemon-util.h"
 #include "devnum-util.h"
 #include "env-file.h"
 #include "escape.h"
@@ -87,13 +88,54 @@ int session_new(Session **ret, Manager *m, const char *id) {
         return 0;
 }
 
-static void session_reset_leader(Session *s) {
+static int session_dispatch_leader_pidfd(sd_event_source *es, int fd, uint32_t revents, void *userdata) {
+        Session *s = ASSERT_PTR(userdata);
+
+        assert(s->leader.fd == fd);
+        session_stop(s, /* force= */ false);
+
+        return 1;
+}
+
+static int session_watch_pidfd(Session *s) {
+        int r;
+
         assert(s);
+        assert(s->manager);
+        assert(pidref_is_set(&s->leader));
+
+        if (s->leader.fd < 0)
+                return 0;
+
+        r = sd_event_add_io(s->manager->event, &s->leader_pidfd_event_source, s->leader.fd, EPOLLIN, session_dispatch_leader_pidfd, s);
+        if (r < 0)
+                return r;
+
+        r = sd_event_source_set_priority(s->leader_pidfd_event_source, SD_EVENT_PRIORITY_IMPORTANT);
+        if (r < 0)
+                return r;
+
+        (void) sd_event_source_set_description(s->leader_pidfd_event_source, "session-pidfd");
+
+        return 0;
+}
+
+static void session_reset_leader(Session *s, bool keep_fdstore) {
+        assert(s);
+
+        if (!keep_fdstore) {
+                /* Clear fdstore if we're asked to, no matter if s->leader is set or not, so that when
+                 * initially deserializing leader fd we clear the old fd too. */
+                (void) notify_remove_fd_warnf("session-%s-leader-fd", s->id);
+                s->leader_fd_saved = false;
+        }
 
         if (!pidref_is_set(&s->leader))
                 return;
 
-        assert_se(hashmap_remove_value(s->manager->sessions_by_leader, &s->leader, s));
+        s->leader_pidfd_event_source = sd_event_source_disable_unref(s->leader_pidfd_event_source);
+
+        (void) hashmap_remove_value(s->manager->sessions_by_leader, &s->leader, s);
 
         return pidref_done(&s->leader);
 }
@@ -142,7 +184,7 @@ Session* session_free(Session *s) {
 
         free(s->scope_job);
 
-        session_reset_leader(s);
+        session_reset_leader(s, /* keep_fdstore = */ true);
 
         sd_bus_message_unref(s->create_message);
 
@@ -188,15 +230,27 @@ int session_set_leader_consume(Session *s, PidRef _leader) {
         if (pidref_equal(&s->leader, &pidref))
                 return 0;
 
-        session_reset_leader(s);
+        session_reset_leader(s, /* keep_fdstore = */ false);
 
         s->leader = TAKE_PIDREF(pidref);
 
+        r = session_watch_pidfd(s);
+        if (r < 0)
+                return log_error_errno(r, "Failed to watch leader pidfd for session '%s': %m", s->id);
+
         r = hashmap_ensure_put(&s->manager->sessions_by_leader, &pidref_hash_ops, &s->leader, s);
         if (r < 0)
                 return r;
         assert(r > 0);
 
+        if (s->leader.fd >= 0) {
+                r = notify_push_fdf(s->leader.fd, "session-%s-leader-fd", s->id);
+                if (r < 0)
+                        log_warning_errno(r, "Failed to push leader pidfd for session '%s', ignoring: %m", s->id);
+                else
+                        s->leader_fd_saved = true;
+        }
+
         (void) audit_session_from_pid(s->leader.pid, &s->audit_id);
 
         return 1;
@@ -240,16 +294,18 @@ int session_save(Session *s) {
                 "# This is private data. Do not parse.\n"
                 "UID="UID_FMT"\n"
                 "USER=%s\n"
-                "ACTIVE=%i\n"
-                "IS_DISPLAY=%i\n"
+                "ACTIVE=%s\n"
+                "IS_DISPLAY=%s\n"
                 "STATE=%s\n"
-                "REMOTE=%i\n",
+                "REMOTE=%s\n"
+                "LEADER_FD_SAVED=%s\n",
                 s->user->user_record->uid,
                 s->user->user_record->user_name,
-                session_is_active(s),
-                s->user->display == s,
+                one_zero(session_is_active(s)),
+                one_zero(s->user->display == s),
                 session_state_to_string(session_get_state(s)),
-                s->remote);
+                one_zero(s->remote),
+                one_zero(s->leader_fd_saved));
 
         if (s->type >= 0)
                 fprintf(f, "TYPE=%s\n", session_type_to_string(s->type));
@@ -408,7 +464,8 @@ int session_load(Session *s) {
                 *vtnr = NULL,
                 *state = NULL,
                 *position = NULL,
-                *leader = NULL,
+                *leader_pid = NULL,
+                *leader_fd_saved = NULL,
                 *type = NULL,
                 *original_type = NULL,
                 *class = NULL,
@@ -425,32 +482,33 @@ int session_load(Session *s) {
         assert(s);
 
         r = parse_env_file(NULL, s->state_file,
-                           "REMOTE",         &remote,
-                           "SCOPE",          &s->scope,
-                           "SCOPE_JOB",      &s->scope_job,
-                           "FIFO",           &s->fifo_path,
-                           "SEAT",           &seat,
-                           "TTY",            &s->tty,
-                           "TTY_VALIDITY",   &tty_validity,
-                           "DISPLAY",        &s->display,
-                           "REMOTE_HOST",    &s->remote_host,
-                           "REMOTE_USER",    &s->remote_user,
-                           "SERVICE",        &s->service,
-                           "DESKTOP",        &s->desktop,
-                           "VTNR",           &vtnr,
-                           "STATE",          &state,
-                           "POSITION",       &position,
-                           "LEADER",         &leader,
-                           "TYPE",           &type,
-                           "ORIGINAL_TYPE",  &original_type,
-                           "CLASS",          &class,
-                           "UID",            &uid,
-                           "REALTIME",       &realtime,
-                           "MONOTONIC",      &monotonic,
-                           "CONTROLLER",     &controller,
-                           "ACTIVE",         &active,
-                           "DEVICES",        &devices,
-                           "IS_DISPLAY",     &is_display);
+                           "REMOTE",          &remote,
+                           "SCOPE",           &s->scope,
+                           "SCOPE_JOB",       &s->scope_job,
+                           "FIFO",            &s->fifo_path,
+                           "SEAT",            &seat,
+                           "TTY",             &s->tty,
+                           "TTY_VALIDITY",    &tty_validity,
+                           "DISPLAY",         &s->display,
+                           "REMOTE_HOST",     &s->remote_host,
+                           "REMOTE_USER",     &s->remote_user,
+                           "SERVICE",         &s->service,
+                           "DESKTOP",         &s->desktop,
+                           "VTNR",            &vtnr,
+                           "STATE",           &state,
+                           "POSITION",        &position,
+                           "LEADER",          &leader_pid,
+                           "LEADER_FD_SAVED", &leader_fd_saved,
+                           "TYPE",            &type,
+                           "ORIGINAL_TYPE",   &original_type,
+                           "CLASS",           &class,
+                           "UID",             &uid,
+                           "REALTIME",        &realtime,
+                           "MONOTONIC",       &monotonic,
+                           "CONTROLLER",      &controller,
+                           "ACTIVE",          &active,
+                           "DEVICES",         &devices,
+                           "IS_DISPLAY",      &is_display);
         if (r < 0)
                 return log_error_errno(r, "Failed to read %s: %m", s->state_file);
 
@@ -517,19 +575,6 @@ int session_load(Session *s) {
                         s->tty_validity = v;
         }
 
-        if (leader) {
-                _cleanup_(pidref_done) PidRef p = PIDREF_NULL;
-
-                r = pidref_set_pidstr(&p, leader);
-                if (r < 0)
-                        log_debug_errno(r, "Failed to parse leader PID of session: %s", leader);
-                else {
-                        r = session_set_leader_consume(s, TAKE_PIDREF(p));
-                        if (r < 0)
-                                log_warning_errno(r, "Failed to set session leader PID, ignoring: %m");
-                }
-        }
-
         if (type) {
                 SessionType t;
 
@@ -604,6 +649,33 @@ int session_load(Session *s) {
                         session_restore_vt(s);
         }
 
+        if (leader_pid) {
+                assert(!pidref_is_set(&s->leader));
+
+                r = parse_pid(leader_pid, &s->deserialized_pid);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to parse LEADER=%s: %m", leader_pid);
+
+                if (leader_fd_saved) {
+                        r = parse_boolean(leader_fd_saved);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to parse LEADER_FD_SAVED=%s: %m", leader_fd_saved);
+                        s->leader_fd_saved = r > 0;
+
+                        if (s->leader_fd_saved)
+                                /* The leader fd will be acquired from fdstore later */
+                                return 0;
+                }
+
+                _cleanup_(pidref_done) PidRef p = PIDREF_NULL;
+
+                r = pidref_set_pid(&p, s->deserialized_pid);
+                if (r >= 0)
+                        r = session_set_leader_consume(s, TAKE_PIDREF(p));
+                if (r < 0)
+                        log_warning_errno(r, "Failed to set leader PID for session '%s': %m", s->id);
+        }
+
         return r;
 }
 
@@ -917,7 +989,6 @@ int session_finalize(Session *s) {
                            LOG_MESSAGE("Removed session %s.", s->id));
 
         s->timer_event_source = sd_event_source_unref(s->timer_event_source);
-        s->leader_pidfd_event_source = sd_event_source_unref(s->leader_pidfd_event_source);
 
         if (s->seat)
                 seat_evict_position(s->seat, s);
@@ -942,7 +1013,7 @@ int session_finalize(Session *s) {
                 seat_save(s->seat);
         }
 
-        session_reset_leader(s);
+        session_reset_leader(s, /* keep_fdstore = */ false);
 
         user_save(s->user);
         user_send_changed(s->user, "Display", NULL);
@@ -1225,36 +1296,6 @@ static void session_remove_fifo(Session *s) {
         }
 }
 
-static int session_dispatch_leader_pidfd(sd_event_source *es, int fd, uint32_t revents, void *userdata) {
-        Session *s = ASSERT_PTR(userdata);
-
-        assert(s->leader.fd == fd);
-        session_stop(s, /* force= */ false);
-
-        return 1;
-}
-
-int session_watch_pidfd(Session *s) {
-        int r;
-
-        assert(s);
-
-        if (s->leader.fd < 0)
-                return 0;
-
-        r = sd_event_add_io(s->manager->event, &s->leader_pidfd_event_source, s->leader.fd, EPOLLIN, session_dispatch_leader_pidfd, s);
-        if (r < 0)
-                return r;
-
-        r = sd_event_source_set_priority(s->leader_pidfd_event_source, SD_EVENT_PRIORITY_IMPORTANT);
-        if (r < 0)
-                return r;
-
-        (void) sd_event_source_set_description(s->leader_pidfd_event_source, "session-pidfd");
-
-        return 0;
-}
-
 bool session_may_gc(Session *s, bool drop_not_started) {
         int r;
 
@@ -1267,15 +1308,17 @@ bool session_may_gc(Session *s, bool drop_not_started) {
                 return true;
 
         r = pidref_is_alive(&s->leader);
+        if (r == -ESRCH)
+                /* Session has no leader. This is probably because the leader vanished before deserializing
+                 * pidfd from FD store. */
+                return true;
         if (r < 0)
-                log_debug_errno(r, "Unable to determine if leader PID " PID_FMT " is still alive, assuming not.", s->leader.pid);
+                log_debug_errno(r, "Unable to determine if leader PID " PID_FMT " is still alive, assuming not: %m", s->leader.pid);
         if (r > 0)
                 return false;
 
-        if (s->fifo_fd >= 0) {
-                if (pipe_eof(s->fifo_fd) <= 0)
-                        return false;
-        }
+        if (s->fifo_fd >= 0 && pipe_eof(s->fifo_fd) <= 0)
+                return false;
 
         if (s->scope_job) {
                 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
index 8b63843ed8127211750bca0e7b052a305fa53794..95d49bb224188b17d7b19fe8b232b5b6108cc819 100644 (file)
@@ -89,6 +89,8 @@ struct Session {
         int vtfd;
 
         PidRef leader;
+        bool leader_fd_saved; /* pidfd of leader uploaded to fdstore */
+        pid_t deserialized_pid; /* PID deserialized from state file (for verification when pidfd is used) */
         uint32_t audit_id;
 
         int fifo_fd;
@@ -144,7 +146,6 @@ void session_set_type(Session *s, SessionType t);
 int session_set_display(Session *s, const char *display);
 int session_set_tty(Session *s, const char *tty);
 int session_create_fifo(Session *s);
-int session_watch_pidfd(Session *s);
 int session_start(Session *s, sd_bus_message *properties, sd_bus_error *error);
 int session_stop(Session *s, bool force);
 int session_finalize(Session *s);
index c6133077f0c174c5390de449ef91b0fcd8a63a3d..4e6b20eb11e85f0522c06c6a9244f616fdfc4479 100644 (file)
@@ -412,12 +412,14 @@ static int user_update_slice(User *u) {
                 { "IOWeight",   u->user_record->io_weight   },
         };
 
-        for (size_t i = 0; i < ELEMENTSOF(settings); i++)
-                if (settings[i].value != UINT64_MAX) {
-                        r = sd_bus_message_append(m, "(sv)", settings[i].name, "t", settings[i].value);
-                        if (r < 0)
-                                return bus_log_create_error(r);
-                }
+        FOREACH_ARRAY(st, settings, ELEMENTSOF(settings)) {
+                if (st->value == UINT64_MAX)
+                        continue;
+
+                r = sd_bus_message_append(m, "(sv)", st->name, "t", st->value);
+                if (r < 0)
+                        return bus_log_create_error(r);
+        }
 
         r = sd_bus_message_close_container(m);
         if (r < 0)
index 965e2a4aefe9cfcb251b3fbdf5204f74a878dd01..4123e6bb1922165f9af2602bef37d66af539536b 100644 (file)
@@ -18,6 +18,7 @@
 #include "constants.h"
 #include "daemon-util.h"
 #include "device-util.h"
+#include "devnum-util.h"
 #include "dirent-util.h"
 #include "escape.h"
 #include "fd-util.h"
@@ -154,7 +155,7 @@ static Manager* manager_free(Manager *m) {
         if (m->unlink_nologin)
                 (void) unlink_or_warn("/run/nologin");
 
-        bus_verify_polkit_async_registry_free(m->polkit_registry);
+        hashmap_free(m->polkit_registry);
 
         sd_bus_flush_close_unref(m->bus);
         sd_event_unref(m->event);
@@ -191,13 +192,10 @@ static int manager_enumerate_devices(Manager *m) {
         if (r < 0)
                 return r;
 
-        FOREACH_DEVICE(e, d) {
-                int k;
+        r = 0;
 
-                k = manager_process_seat_device(m, d);
-                if (k < 0)
-                        r = k;
-        }
+        FOREACH_DEVICE(e, d)
+                RET_GATHER(r, manager_process_seat_device(m, d));
 
         return r;
 }
@@ -225,13 +223,10 @@ static int manager_enumerate_buttons(Manager *m) {
         if (r < 0)
                 return r;
 
-        FOREACH_DEVICE(e, d) {
-                int k;
+        r = 0;
 
-                k = manager_process_button_device(m, d);
-                if (k < 0)
-                        r = k;
-        }
+        FOREACH_DEVICE(e, d)
+                RET_GATHER(r, manager_process_button_device(m, d));
 
         return r;
 }
@@ -251,12 +246,11 @@ static int manager_enumerate_seats(Manager *m) {
                 if (errno == ENOENT)
                         return 0;
 
-                return log_error_errno(errno, "Failed to open /run/systemd/seats: %m");
+                return log_error_errno(errno, "Failed to open /run/systemd/seats/: %m");
         }
 
         FOREACH_DIRENT(de, d, return -errno) {
                 Seat *s;
-                int k;
 
                 if (!dirent_is_file(de))
                         continue;
@@ -269,9 +263,7 @@ static int manager_enumerate_seats(Manager *m) {
                         continue;
                 }
 
-                k = seat_load(s);
-                if (k < 0)
-                        r = k;
+                RET_GATHER(r, seat_load(s));
         }
 
         return r;
@@ -292,19 +284,21 @@ static int manager_enumerate_linger_users(Manager *m) {
         }
 
         FOREACH_DIRENT(de, d, return -errno) {
-                int k;
                 _cleanup_free_ char *n = NULL;
+                int k;
 
                 if (!dirent_is_file(de))
                         continue;
+
                 k = cunescape(de->d_name, 0, &n);
                 if (k < 0) {
-                        r = log_warning_errno(k, "Failed to unescape username '%s', ignoring: %m", de->d_name);
+                        RET_GATHER(r, log_warning_errno(k, "Failed to unescape username '%s', ignoring: %m", de->d_name));
                         continue;
                 }
+
                 k = manager_add_user_by_name(m, n, NULL);
                 if (k < 0)
-                        r = log_warning_errno(k, "Couldn't add lingering user %s, ignoring: %m", de->d_name);
+                        RET_GATHER(r, log_warning_errno(k, "Couldn't add lingering user %s, ignoring: %m", de->d_name));
         }
 
         return r;
@@ -312,7 +306,7 @@ static int manager_enumerate_linger_users(Manager *m) {
 
 static int manager_enumerate_users(Manager *m) {
         _cleanup_closedir_ DIR *d = NULL;
-        int r, k;
+        int r;
 
         assert(m);
 
@@ -325,148 +319,198 @@ static int manager_enumerate_users(Manager *m) {
                 if (errno == ENOENT)
                         return 0;
 
-                return log_error_errno(errno, "Failed to open /run/systemd/users: %m");
+                return log_error_errno(errno, "Failed to open /run/systemd/users/: %m");
         }
 
         FOREACH_DIRENT(de, d, return -errno) {
                 User *u;
                 uid_t uid;
+                int k;
 
                 if (!dirent_is_file(de))
                         continue;
 
                 k = parse_uid(de->d_name, &uid);
                 if (k < 0) {
-                        r = log_warning_errno(k, "Failed to parse filename /run/systemd/users/%s as UID.", de->d_name);
+                        RET_GATHER(r, log_warning_errno(k, "Failed to parse filename /run/systemd/users/%s as UID, ignoring: %m", de->d_name));
                         continue;
                 }
 
                 k = manager_add_user_by_uid(m, uid, &u);
                 if (k < 0) {
-                        r = log_warning_errno(k, "Failed to add user by file name %s, ignoring: %m", de->d_name);
+                        RET_GATHER(r, log_warning_errno(k, "Failed to add user by filename %s, ignoring: %m", de->d_name));
                         continue;
                 }
 
                 user_add_to_gc_queue(u);
 
-                k = user_load(u);
-                if (k < 0)
-                        r = k;
+                RET_GATHER(r, user_load(u));
         }
 
         return r;
 }
 
-static int parse_fdname(const char *fdname, char **session_id, dev_t *dev) {
+static int parse_fdname(const char *fdname, char **ret_session_id, dev_t *ret_devno) {
         _cleanup_strv_free_ char **parts = NULL;
         _cleanup_free_ char *id = NULL;
-        unsigned major, minor;
         int r;
 
+        assert(ret_session_id);
+        assert(ret_devno);
+
         parts = strv_split(fdname, "-");
         if (!parts)
                 return -ENOMEM;
-        if (strv_length(parts) != 5)
-                return -EINVAL;
 
-        if (!streq(parts[0], "session"))
+        if (_unlikely_(!streq(parts[0], "session")))
                 return -EINVAL;
 
         id = strdup(parts[1]);
         if (!id)
                 return -ENOMEM;
 
-        if (!streq(parts[2], "device"))
+        if (streq(parts[2], "leader")) {
+                *ret_session_id = TAKE_PTR(id);
+                *ret_devno = 0;
+
+                return 0;
+        }
+
+        if (_unlikely_(!streq(parts[2], "device")))
                 return -EINVAL;
 
+        unsigned major, minor;
+
         r = safe_atou(parts[3], &major);
         if (r < 0)
                 return r;
+
         r = safe_atou(parts[4], &minor);
         if (r < 0)
                 return r;
 
-        *dev = makedev(major, minor);
-        *session_id = TAKE_PTR(id);
+        *ret_session_id = TAKE_PTR(id);
+        *ret_devno = makedev(major, minor);
 
         return 0;
 }
 
-static int deliver_fd(Manager *m, const char *fdname, int fd) {
-        _cleanup_free_ char *id = NULL;
+static int deliver_session_device_fd(Session *s, const char *fdname, int fd, dev_t devno) {
         SessionDevice *sd;
         struct stat st;
-        Session *s;
-        dev_t dev;
-        int r;
 
-        assert(m);
+        assert(s);
+        assert(fdname);
         assert(fd >= 0);
-
-        r = parse_fdname(fdname, &id, &dev);
-        if (r < 0)
-                return log_debug_errno(r, "Failed to parse fd name %s: %m", fdname);
-
-        s = hashmap_get(m->sessions, id);
-        if (!s)
-                /* If the session doesn't exist anymore, the associated session device attached to this fd
-                 * doesn't either. Let's simply close this fd. */
-                return log_debug_errno(SYNTHETIC_ERRNO(ENXIO), "Failed to attach fd for unknown session: %s", id);
+        assert(devno > 0);
 
         if (fstat(fd, &st) < 0)
                 /* The device is allowed to go away at a random point, in which case fstat() failing is
                  * expected. */
-                return log_debug_errno(errno, "Failed to stat device fd for session %s: %m", id);
+                return log_debug_errno(errno, "Failed to stat device fd '%s' for session '%s': %m",
+                                       fdname, s->id);
 
-        if (!S_ISCHR(st.st_mode) || st.st_rdev != dev)
-                return log_debug_errno(SYNTHETIC_ERRNO(ENODEV), "Device fd doesn't point to the expected character device node");
+        if (!S_ISCHR(st.st_mode) || st.st_rdev != devno)
+                return log_debug_errno(SYNTHETIC_ERRNO(ENODEV),
+                                       "Device fd '%s' doesn't point to the expected character device node.",
+                                       fdname);
 
-        sd = hashmap_get(s->devices, &dev);
+        sd = hashmap_get(s->devices, &devno);
         if (!sd)
                 /* Weird, we got an fd for a session device which wasn't recorded in the session state
                  * file... */
-                return log_warning_errno(SYNTHETIC_ERRNO(ENODEV), "Got fd for missing session device [%u:%u] in session %s",
-                                         major(dev), minor(dev), s->id);
+                return log_warning_errno(SYNTHETIC_ERRNO(ENODEV),
+                                         "Got session device fd '%s' [" DEVNUM_FORMAT_STR "], but not present in session state.",
+                                         fdname, DEVNUM_FORMAT_VAL(devno));
 
-        log_debug("Attaching fd to session device [%u:%u] for session %s",
-                  major(dev), minor(dev), s->id);
+        log_debug("Attaching session device fd '%s' [" DEVNUM_FORMAT_STR "] to session '%s'.",
+                  fdname, DEVNUM_FORMAT_VAL(devno), s->id);
 
         session_device_attach_fd(sd, fd, s->was_active);
         return 0;
 }
 
-static int manager_attach_fds(Manager *m) {
-        _cleanup_strv_free_ char **fdnames = NULL;
-        int n;
+static int deliver_session_leader_fd_consume(Session *s, const char *fdname, int fd) {
+        _cleanup_(pidref_done) PidRef leader_fdstore = PIDREF_NULL;
+        int r;
 
-        /* Upon restart, PID1 will send us back all fds of session devices that we previously opened. Each
-         * file descriptor is associated with a given session. The session ids are passed through FDNAMES. */
+        assert(s);
+        assert(fdname);
+        assert(fd >= 0);
 
-        assert(m);
+        if (!s->leader_fd_saved)
+                log_warning("Got leader pidfd for session '%s', but not recorded in session state, proceeding anyway.",
+                            s->id);
+        else
+                assert(!pidref_is_set(&s->leader));
 
-        n = sd_listen_fds_with_names(true, &fdnames);
-        if (n < 0)
-                return log_warning_errno(n, "Failed to acquire passed fd list: %m");
-        if (n == 0)
-                return 0;
+        r = pidref_set_pidfd_take(&leader_fdstore, fd);
+        if (r < 0) {
+                if (r == -ESRCH)
+                        log_debug_errno(r, "Leader of session '%s' is gone while deserializing.", s->id);
+                else
+                        log_warning_errno(r, "Failed to create reference to leader of session '%s': %m", s->id);
 
-        for (int i = 0; i < n; i++) {
-                int fd = SD_LISTEN_FDS_START + i;
+                close_and_notify_warn(fd, fdname);
+                return r;
+        }
 
-                if (deliver_fd(m, fdnames[i], fd) >= 0)
-                        continue;
+        assert(pid_is_valid(s->deserialized_pid));
 
-                /* Hmm, we couldn't deliver the fd to any session device object? If so, let's close the fd
-                 * and remove it from fdstore. */
-                close_and_notify_warn(fd, fdnames[i]);
-        }
+        if (leader_fdstore.pid != s->deserialized_pid)
+                log_warning("Leader from pidfd (" PID_FMT ") doesn't match with LEADER=" PID_FMT " for session '%s', proceeding anyway.",
+                            leader_fdstore.pid, s->deserialized_pid, s->id);
+
+        r = session_set_leader_consume(s, TAKE_PIDREF(leader_fdstore));
+        if (r < 0)
+                return log_warning_errno(r, "Failed to attach leader pidfd for session '%s': %m", s->id);
 
         return 0;
 }
 
+static int manager_attach_session_fd_one_consume(Manager *m, const char *fdname, int fd) {
+        _cleanup_free_ char *id = NULL;
+        dev_t devno;
+        Session *s;
+        int r;
+
+        assert(m);
+        assert(fdname);
+        assert(fd >= 0);
+
+        r = parse_fdname(fdname, &id, &devno);
+        if (r < 0) {
+                log_warning_errno(r, "Failed to parse stored fd name '%s': %m", fdname);
+                goto fail_close;
+        }
+
+        s = hashmap_get(m->sessions, id);
+        if (!s) {
+                /* If the session doesn't exist anymore, let's simply close this fd. */
+                r = log_debug_errno(SYNTHETIC_ERRNO(ENXIO),
+                                    "Cannot attach fd '%s' to unknown session '%s', ignoring.", fdname, id);
+                goto fail_close;
+        }
+
+        if (devno > 0) {
+                r = deliver_session_device_fd(s, fdname, fd, devno);
+                if (r < 0)
+                        goto fail_close;
+                return 0;
+        }
+
+        /* Takes ownership of fd on both success and failure */
+        return deliver_session_leader_fd_consume(s, fdname, fd);
+
+fail_close:
+        close_and_notify_warn(fd, fdname);
+        return r;
+}
+
 static int manager_enumerate_sessions(Manager *m) {
+        _cleanup_strv_free_ char **fdnames = NULL;
         _cleanup_closedir_ DIR *d = NULL;
-        int r = 0, k;
+        int r = 0, n;
 
         assert(m);
 
@@ -476,18 +520,19 @@ static int manager_enumerate_sessions(Manager *m) {
                 if (errno == ENOENT)
                         return 0;
 
-                return log_error_errno(errno, "Failed to open /run/systemd/sessions: %m");
+                return log_error_errno(errno, "Failed to open /run/systemd/sessions/: %m");
         }
 
         FOREACH_DIRENT(de, d, return -errno) {
-                struct Session *s;
+                Session *s;
+                int k;
 
                 if (!dirent_is_file(de))
                         continue;
 
                 k = manager_add_session(m, de->d_name, &s);
                 if (k < 0) {
-                        r = log_warning_errno(k, "Failed to add session by file name %s, ignoring: %m", de->d_name);
+                        RET_GATHER(r, log_warning_errno(k, "Failed to add session by filename %s, ignoring: %m", de->d_name));
                         continue;
                 }
 
@@ -495,12 +540,18 @@ static int manager_enumerate_sessions(Manager *m) {
 
                 k = session_load(s);
                 if (k < 0)
-                        r = k;
+                        RET_GATHER(r, log_warning_errno(k, "Failed to deserialize session '%s', ignoring: %m", s->id));
         }
 
-        /* We might be restarted and PID1 could have sent us back the session device fds we previously
-         * saved. */
-        (void) manager_attach_fds(m);
+        n = sd_listen_fds_with_names(/* unset_environment = */ true, &fdnames);
+        if (n < 0)
+                return log_error_errno(n, "Failed to acquire passed fd list: %m");
+
+        for (int i = 0; i < n; i++) {
+                int fd = SD_LISTEN_FDS_START + i;
+
+                RET_GATHER(r, manager_attach_session_fd_one_consume(m, fdnames[i], fd));
+        }
 
         return r;
 }
@@ -516,25 +567,23 @@ static int manager_enumerate_inhibitors(Manager *m) {
                 if (errno == ENOENT)
                         return 0;
 
-                return log_error_errno(errno, "Failed to open /run/systemd/inhibit: %m");
+                return log_error_errno(errno, "Failed to open /run/systemd/inhibit/: %m");
         }
 
         FOREACH_DIRENT(de, d, return -errno) {
-                int k;
                 Inhibitor *i;
+                int k;
 
                 if (!dirent_is_file(de))
                         continue;
 
                 k = manager_add_inhibitor(m, de->d_name, &i);
                 if (k < 0) {
-                        r = log_warning_errno(k, "Couldn't add inhibitor %s, ignoring: %m", de->d_name);
+                        RET_GATHER(r, log_warning_errno(k, "Couldn't add inhibitor %s, ignoring: %m", de->d_name));
                         continue;
                 }
 
-                k = inhibitor_load(i);
-                if (k < 0)
-                        r = k;
+                RET_GATHER(r, inhibitor_load(i));
         }
 
         return r;
index 135a47c0dc21a8734f2ccf4e214edf52f05e62dc..20ec5530d9a0ffabcbd6028fdc859009f8a912b1 100644 (file)
@@ -959,16 +959,14 @@ _public_ PAM_EXTERN int pam_sm_open_session(
         class = getenv_harder(handle, "XDG_SESSION_CLASS", class_pam);
         desktop = getenv_harder(handle, "XDG_SESSION_DESKTOP", desktop_pam);
 
-        tty = strempty(tty);
-
-        if (strchr(tty, ':')) {
+        if (tty && strchr(tty, ':')) {
                 /* A tty with a colon is usually an X11 display, placed there to show up in utmp. We rearrange things
                  * and don't pretend that an X display was a tty. */
                 if (isempty(display))
                         display = tty;
                 tty = NULL;
 
-        } else if (streq(tty, "cron")) {
+        } else if (streq_ptr(tty, "cron")) {
                 /* cron is setting PAM_TTY to "cron" for some reason (the commit carries no information why, but
                  * probably because it wants to set it to something as pam_time/pam_access/… require PAM_TTY to be set
                  * (as they otherwise even try to update it!) — but cron doesn't actually allocate a TTY for its forked
@@ -977,10 +975,10 @@ _public_ PAM_EXTERN int pam_sm_open_session(
                 class = "background";
                 tty = NULL;
 
-        } else if (streq(tty, "ssh")) {
+        } else if (streq_ptr(tty, "ssh")) {
                 /* ssh has been setting PAM_TTY to "ssh" (for the same reason as cron does this, see above. For further
                  * details look for "PAM_TTY_KLUDGE" in the openssh sources). */
-                type ="tty";
+                type = "tty";
                 class = "user";
                 tty = NULL; /* This one is particularly sad, as this means that ssh sessions — even though usually
                              * associated with a pty — won't be tracked by their tty in logind. This is because ssh
@@ -988,7 +986,7 @@ _public_ PAM_EXTERN int pam_sm_open_session(
                              * much later (this is because it doesn't know yet if it needs one at all, as whether to
                              * register a pty or not is negotiated much later in the protocol). */
 
-        } else
+        } else if (tty)
                 /* Chop off leading /dev prefix that some clients specify, but others do not. */
                 tty = skip_dev_prefix(tty);
 
index 58a407d4513f0d5f215ea50009c718828806ac10..4e830a4bc23220c65301c91dffde8907e5256c91 100644 (file)
@@ -96,7 +96,7 @@ static Manager* manager_unref(Manager *m) {
         sd_event_source_unref(m->nscd_cache_flush_event);
 #endif
 
-        bus_verify_polkit_async_registry_free(m->polkit_registry);
+        hashmap_free(m->polkit_registry);
 
         manager_varlink_done(m);
 
index 57127a861abad48aa330f81655e8069105c3d4bd..894cec44cbb574923d9d4a3cf64041ec4604b280 100644 (file)
@@ -198,14 +198,6 @@ static void netdev_detach_from_manager(NetDev *netdev) {
 static NetDev *netdev_free(NetDev *netdev) {
         assert(netdev);
 
-        netdev_detach_from_manager(netdev);
-
-        free(netdev->filename);
-
-        free(netdev->description);
-        free(netdev->ifname);
-        condition_free_list(netdev->conditions);
-
         /* Invoke the per-kind done() destructor, but only if the state field is initialized. We conditionalize that
          * because we parse .netdev files twice: once to determine the kind (with a short, minimal NetDev structure
          * allocation, with no room for per-kind fields), and once to read the kind's properties (with a full,
@@ -218,6 +210,13 @@ static NetDev *netdev_free(NetDev *netdev) {
             NETDEV_VTABLE(netdev)->done)
                 NETDEV_VTABLE(netdev)->done(netdev);
 
+        netdev_detach_from_manager(netdev);
+
+        condition_free_list(netdev->conditions);
+        free(netdev->filename);
+        free(netdev->description);
+        free(netdev->ifname);
+
         return mfree(netdev);
 }
 
index e81905ad98a99deedde2a34a08e8199290b6c943..8933fc49776d6053b37c412ab6f9d90d293d3daf 100644 (file)
@@ -638,8 +638,7 @@ Manager* manager_free(Manager *m) {
         sd_device_monitor_unref(m->device_monitor);
 
         manager_varlink_done(m);
-
-        bus_verify_polkit_async_registry_free(m->polkit_registry);
+        hashmap_free(m->polkit_registry);
         sd_bus_flush_close_unref(m->bus);
 
         free(m->dynamic_timezone);
index e7cc252243b7f76bcbb61e99c6acb49f062cfa28..72dbf9ed0675179315cf2add7e1fa2eaf2ae04a5 100644 (file)
@@ -2814,7 +2814,7 @@ int config_parse_route_mtu(
         }
 
         r = config_parse_mtu(unit, filename, line, section, section_line, lvalue, ltype, rvalue, &n->mtu, userdata);
-        if (r < 0)
+        if (r <= 0)
                 return r;
 
         TAKE_PTR(n);
index 6081254b3dc6c24e6257f8e2d85a076dc6109e5a..23c3ae64abca2d49d2a2be5a64f3eaff73413f9d 100644 (file)
@@ -642,7 +642,7 @@ Manager* manager_free(Manager *m) {
         sd_event_source_unref(m->mem_pressure_context_event_source);
         sd_event_unref(m->event);
 
-        bus_verify_polkit_async_registry_free(m->polkit_registry);
+        hashmap_free(m->polkit_registry);
         sd_bus_flush_close_unref(m->bus);
 
         hashmap_free(m->monitored_swap_cgroup_contexts);
index d1bab5f59f867045e4346fc30ee08a03a7ad9fa4..8841f4e76ce1ba03421a3c83d4b7b4f7429137d3 100644 (file)
@@ -4174,7 +4174,7 @@ static int verb_make_policy(int argc, char *argv[], void *userdata) {
          * policies).
          *
          * Whenever we want to lock an encrypted object (for example FDE) against this policy, we'll use a
-         * PolicyAuthorizeNV epxression that pins the NV index in the policy, and permits access to any
+         * PolicyAuthorizeNV expression that pins the NV index in the policy, and permits access to any
          * policies matching the current NV index contents.
          *
          * We grant world-readable read access to the NV index. Write access is controlled by a PIN (which we
index 136c5fa19184a084add11f7f77117d0b6525deec..7c2ddd90ef77e14b5f11340736e12461b51108c6 100644 (file)
@@ -65,7 +65,7 @@ static Manager* manager_unref(Manager *m) {
 
         sd_event_source_unref(m->image_cache_defer_event);
 
-        bus_verify_polkit_async_registry_free(m->polkit_registry);
+        hashmap_free(m->polkit_registry);
 
         sd_bus_flush_close_unref(m->bus);
         sd_event_unref(m->event);
index 0295662b5b5eae4f886adc940e4c7ce6acaa2f5a..a0251b4b97b37f511294074ad34f05dfd3aeaf9d 100644 (file)
@@ -731,7 +731,7 @@ Manager *manager_free(Manager *m) {
 
         ordered_set_free(m->dns_extra_stub_listeners);
 
-        bus_verify_polkit_async_registry_free(m->polkit_registry);
+        hashmap_free(m->polkit_registry);
 
         sd_bus_flush_close_unref(m->bus);
 
index 9f923372a4b3e3ecdddf523a1c8c57cb5ae89821..fe9363aad4466312f3c602aee2d9e0cacee818c7 100644 (file)
@@ -4,10 +4,11 @@
 #include "bus-message.h"
 #include "bus-polkit.h"
 #include "bus-util.h"
+#include "process-util.h"
 #include "strv.h"
 #include "user-util.h"
 
-static int check_good_user(sd_bus_message *m, uid_t good_user) {
+static int bus_message_check_good_user(sd_bus_message *m, uid_t good_user) {
         _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
         uid_t sender_uid;
         int r;
@@ -15,7 +16,7 @@ static int check_good_user(sd_bus_message *m, uid_t good_user) {
         assert(m);
 
         if (good_user == UID_INVALID)
-                return 0;
+                return false;
 
         r = sd_bus_query_sender_creds(m, SD_BUS_CREDS_EUID, &creds);
         if (r < 0)
@@ -54,7 +55,7 @@ static int bus_message_append_strv_key_value(sd_bus_message *m, const char **l)
         return r;
 }
 
-static int bus_message_new_polkit_auth_call(
+static int bus_message_new_polkit_auth_call_for_bus(
                 sd_bus_message *m,
                 const char *action,
                 const char **details,
@@ -115,7 +116,7 @@ int bus_test_polkit(
 
         /* Tests non-interactively! */
 
-        r = check_good_user(call, good_user);
+        r = bus_message_check_good_user(call, good_user);
         if (r != 0)
                 return r;
 
@@ -129,7 +130,7 @@ int bus_test_polkit(
         _cleanup_(sd_bus_message_unrefp) sd_bus_message *request = NULL, *reply = NULL;
         int authorized = false, challenge = false;
 
-        r = bus_message_new_polkit_auth_call(call, action, details, /* interactive = */ false, &request);
+        r = bus_message_new_polkit_auth_call_for_bus(call, action, details, /* interactive = */ false, &request);
         if (r < 0)
                 return r;
 
@@ -190,8 +191,10 @@ typedef struct AsyncPolkitQuery {
 
         AsyncPolkitQueryAction *action;
 
+        sd_bus *bus;
         sd_bus_message *request;
         sd_bus_slot *slot;
+        Varlink *link;
 
         Hashmap *registry;
         sd_event_source *defer_event_source;
@@ -213,6 +216,9 @@ static AsyncPolkitQuery *async_polkit_query_free(AsyncPolkitQuery *q) {
 
         sd_bus_message_unref(q->request);
 
+        sd_bus_unref(q->bus);
+        varlink_unref(q->link);
+
         async_polkit_query_action_free(q->action);
 
         sd_event_source_disable_unref(q->defer_event_source);
@@ -230,6 +236,14 @@ static AsyncPolkitQuery *async_polkit_query_free(AsyncPolkitQuery *q) {
 DEFINE_PRIVATE_TRIVIAL_REF_UNREF_FUNC(AsyncPolkitQuery, async_polkit_query, async_polkit_query_free);
 DEFINE_TRIVIAL_CLEANUP_FUNC(AsyncPolkitQuery*, async_polkit_query_unref);
 
+DEFINE_HASH_OPS_WITH_VALUE_DESTRUCTOR(
+                async_polkit_query_hash_ops,
+                void,
+                trivial_hash_func,
+                trivial_compare_func,
+                AsyncPolkitQuery,
+                async_polkit_query_unref);
+
 static int async_polkit_defer(sd_event_source *s, void *userdata) {
         AsyncPolkitQuery *q = ASSERT_PTR(userdata);
 
@@ -316,7 +330,7 @@ static int async_polkit_process_reply(sd_bus_message *reply, AsyncPolkitQuery *q
 
         if (!q->defer_event_source) {
                 r = sd_event_add_defer(
-                                sd_bus_get_event(sd_bus_message_get_bus(reply)),
+                                sd_bus_get_event(q->bus),
                                 &q->defer_event_source,
                                 async_polkit_defer,
                                 q);
@@ -332,13 +346,21 @@ static int async_polkit_process_reply(sd_bus_message *reply, AsyncPolkitQuery *q
         if (r < 0)
                 return r;
 
-        r = sd_bus_message_rewind(q->request, true);
-        if (r < 0)
-                return r;
+        if (q->request) {
+                r = sd_bus_message_rewind(q->request, true);
+                if (r < 0)
+                        return r;
 
-        r = sd_bus_enqueue_for_read(sd_bus_message_get_bus(q->request), q->request);
-        if (r < 0)
-                return r;
+                r = sd_bus_enqueue_for_read(q->bus, q->request);
+                if (r < 0)
+                        return r;
+        }
+
+        if (q->link) {
+                r = varlink_dispatch_again(q->link);
+                if (r < 0)
+                        return r;
+        }
 
         return 1;
 }
@@ -352,7 +374,10 @@ static int async_polkit_callback(sd_bus_message *reply, void *userdata, sd_bus_e
         r = async_polkit_process_reply(reply, q);
         if (r < 0) {
                 log_debug_errno(r, "Processing asynchronous PolicyKit reply failed, ignoring: %m");
-                (void) sd_bus_reply_method_errno(q->request, r, NULL);
+                if (q->request)
+                        (void) sd_bus_reply_method_errno(q->request, r, NULL);
+                if (q->link)
+                        varlink_error_errno(q->link, r);
                 async_polkit_query_unref(q);
         }
         return r;
@@ -366,11 +391,10 @@ static int async_polkit_query_check_action(
 
         assert(q);
         assert(action);
-        assert(ret_error);
 
         LIST_FOREACH(authorized, a, q->authorized_actions)
                 if (streq(a->action, action) && strv_equal(a->details, (char**) details))
-                        return 1;
+                        return 1; /* Allow! */
 
         if (q->error_action && streq(q->error_action->action, action))
                 return sd_bus_error_copy(ret_error, &q->error);
@@ -480,7 +504,7 @@ int bus_verify_polkit_async_full(
         assert(registry);
         assert(ret_error);
 
-        r = check_good_user(call, good_user);
+        r = bus_message_check_good_user(call, good_user);
         if (r != 0)
                 return r;
 
@@ -512,11 +536,7 @@ int bus_verify_polkit_async_full(
         if (c > 0)
                 interactive = true;
 
-        r = hashmap_ensure_allocated(registry, NULL);
-        if (r < 0)
-                return r;
-
-        r = bus_message_new_polkit_auth_call(call, action, details, interactive, &pk);
+        r = bus_message_new_polkit_auth_call_for_bus(call, action, details, interactive, &pk);
         if (r < 0)
                 return r;
 
@@ -528,6 +548,7 @@ int bus_verify_polkit_async_full(
                 *q = (AsyncPolkitQuery) {
                         .n_ref = 1,
                         .request = sd_bus_message_ref(call),
+                        .bus = sd_bus_ref(sd_bus_message_get_bus(call)),
                 };
         }
 
@@ -544,7 +565,7 @@ int bus_verify_polkit_async_full(
                 return -ENOMEM;
 
         if (!q->registry) {
-                r = hashmap_put(*registry, call, q);
+                r = hashmap_ensure_put(registry, &async_polkit_query_hash_ops, call, q);
                 if (r < 0)
                         return r;
 
@@ -563,11 +584,231 @@ int bus_verify_polkit_async_full(
         return -EACCES;
 }
 
-Hashmap *bus_verify_polkit_async_registry_free(Hashmap *registry) {
+static int varlink_check_good_user(Varlink *link, uid_t good_user) {
+        int r;
+
+        assert(link);
+
+        if (good_user == UID_INVALID)
+                return false;
+
+        uid_t peer_uid;
+        r = varlink_get_peer_uid(link, &peer_uid);
+        if (r < 0)
+                return r;
+
+        return good_user == peer_uid;
+}
+
+static int varlink_check_peer_privilege(Varlink *link) {
+        int r;
+
+        assert(link);
+
+        uid_t peer_uid;
+        r = varlink_get_peer_uid(link, &peer_uid);
+        if (r < 0)
+                return r;
+
+        uid_t our_uid = getuid();
+        return peer_uid == our_uid ||
+                (our_uid != 0 && peer_uid == 0);
+}
+
+#if ENABLE_POLKIT
+static int bus_message_new_polkit_auth_call_for_varlink(
+                sd_bus *bus,
+                Varlink *link,
+                const char *action,
+                const char **details,
+                bool interactive,
+                sd_bus_message **ret) {
+
+        _cleanup_(sd_bus_message_unrefp) sd_bus_message *c = NULL;
+        _cleanup_(pidref_done) PidRef pidref = PIDREF_NULL;
+        int r;
+
+        assert(bus);
+        assert(link);
+        assert(action);
+        assert(ret);
+
+        r = varlink_get_peer_pidref(link, &pidref);
+        if (r < 0)
+                return r;
+        if (r == 0) /* if we couldn't get a pidfd this returns == 0 */
+                return log_debug_errno(SYNTHETIC_ERRNO(EPERM), "Failed to get peer pidfd, cannot securely authenticate.");
+
+        uid_t uid;
+        r = varlink_get_peer_uid(link, &uid);
+        if (r < 0)
+                return r;
+
+        r = sd_bus_message_new_method_call(
+                        bus,
+                        &c,
+                        "org.freedesktop.PolicyKit1",
+                        "/org/freedesktop/PolicyKit1/Authority",
+                        "org.freedesktop.PolicyKit1.Authority",
+                        "CheckAuthorization");
+        if (r < 0)
+                return r;
+
+        r = sd_bus_message_append(
+                        c,
+                        "(sa{sv})s",
+                        "unix-process", 2,
+                        "pidfd", "h", (uint32_t) pidref.fd,
+                        "uid", "i", (int32_t) uid,
+                        action);
+        if (r < 0)
+                return r;
+
+        r = bus_message_append_strv_key_value(c, details);
+        if (r < 0)
+                return r;
+
+        r = sd_bus_message_append(c, "us", interactive, NULL);
+        if (r < 0)
+                return r;
+
+        *ret = TAKE_PTR(c);
+        return 0;
+}
+
+static bool varlink_allow_interactive_authentication(Varlink *link) {
+        _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
+        int r;
+
+        assert(link);
+
+        /* We look for the allowInteractiveAuthentication field in the message currently being dispatched,
+         * always under the same name. */
+
+        r = varlink_get_current_parameters(link, &v);
+        if (r < 0)
+                return r;
+
+        JsonVariant *b;
+        b = json_variant_by_key(v, "allowInteractiveAuthentication");
+        if (b) {
+                if (json_variant_is_boolean(b))
+                        return json_variant_boolean(b);
+
+                log_debug("Incoming 'allowInteractiveAuthentication' field is not a boolean, ignoring.");
+        }
+
+        return false;
+}
+#endif
+
+int varlink_verify_polkit_async(
+                Varlink *link,
+                sd_bus *bus,
+                const char *action,
+                const char **details,
+                uid_t good_user,
+                Hashmap **registry) {
+
+        int r;
+
+        assert(link);
+        assert(registry);
+
+        /* This is the same as bus_verify_polkit_async_full(), but authenticates the peer of a varlink
+         * connection rather than the sender of a bus message. */
+
+        r = varlink_check_good_user(link, good_user);
+        if (r != 0)
+                return r;
+
+        r = varlink_check_peer_privilege(link);
+        if (r != 0)
+                return r;
+
 #if ENABLE_POLKIT
-        return hashmap_free_with_destructor(registry, async_polkit_query_unref);
-#else
-        assert(hashmap_isempty(registry));
-        return hashmap_free(registry);
+        _cleanup_(async_polkit_query_unrefp) AsyncPolkitQuery *q = NULL;
+
+        q = async_polkit_query_ref(hashmap_get(*registry, link));
+        /* This is a repeated invocation of this function, hence let's check if we've already got
+         * a response from polkit for this action */
+        if (q) {
+                _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+                r = async_polkit_query_check_action(q, action, details, &error);
+                if (r < 0) {
+                        /* Reply with a nice error */
+                        if (sd_bus_error_has_name(&error, SD_BUS_ERROR_INTERACTIVE_AUTHORIZATION_REQUIRED))
+                                return varlink_error(link, VARLINK_ERROR_INTERACTIVE_AUTHENTICATION_REQUIRED, NULL);
+
+                        if (ERRNO_IS_NEG_PRIVILEGE(r))
+                                return varlink_error(link, VARLINK_ERROR_PERMISSION_DENIED, NULL);
+
+                        return r;
+                }
+                if (r > 0)
+                        return r;
+        }
+
+        _cleanup_(sd_bus_unrefp) sd_bus *mybus = NULL;
+        if (!bus) {
+                r = sd_bus_open_system(&mybus);
+                if (r < 0)
+                        return r;
+
+                r = sd_bus_attach_event(mybus, varlink_get_event(link), 0);
+                if (r < 0)
+                        return r;
+
+                bus = mybus;
+        }
+
+        bool interactive = varlink_allow_interactive_authentication(link);
+
+        _cleanup_(sd_bus_message_unrefp) sd_bus_message *pk = NULL;
+        r = bus_message_new_polkit_auth_call_for_varlink(bus, link, action, details, interactive, &pk);
+        if (r < 0)
+                return r;
+
+        if (!q) {
+                q = new(AsyncPolkitQuery, 1);
+                if (!q)
+                        return -ENOMEM;
+
+                *q = (AsyncPolkitQuery) {
+                        .n_ref = 1,
+                        .link = varlink_ref(link),
+                        .bus = sd_bus_ref(bus),
+                };
+        }
+
+        assert(!q->action);
+        q->action = new(AsyncPolkitQueryAction, 1);
+        if (!q->action)
+                return -ENOMEM;
+
+        *q->action = (AsyncPolkitQueryAction) {
+                .action = strdup(action),
+                .details = strv_copy((char**) details),
+        };
+        if (!q->action->action || !q->action->details)
+                return -ENOMEM;
+
+        if (!q->registry) {
+                r = hashmap_ensure_put(registry, &async_polkit_query_hash_ops, link, q);
+                if (r < 0)
+                        return r;
+
+                q->registry = *registry;
+        }
+
+        r = sd_bus_call_async(bus, &q->slot, pk, async_polkit_callback, q, 0);
+        if (r < 0)
+                return r;
+
+        TAKE_PTR(q);
+
+        return 0;
 #endif
+
+        return -EACCES;
 }
index d82ac4679c617ee70cb024aadb9b0c1d77b74871..0fe3a4ca0ed91d0ab0bdc0c72c0a89d64ad6104e 100644 (file)
@@ -5,6 +5,7 @@
 
 #include "hashmap.h"
 #include "user-util.h"
+#include "varlink.h"
 
 int bus_test_polkit(sd_bus_message *call, const char *action, const char **details, uid_t good_user, bool *_challenge, sd_bus_error *e);
 
@@ -13,4 +14,12 @@ static inline int bus_verify_polkit_async(sd_bus_message *call, const char *acti
         return bus_verify_polkit_async_full(call, action, details, false, UID_INVALID, registry, ret_error);
 }
 
-Hashmap *bus_verify_polkit_async_registry_free(Hashmap *registry);
+int varlink_verify_polkit_async(Varlink *link, sd_bus *bus, const char *action, const char **details, uid_t good_user, Hashmap **registry);
+
+/* A JsonDispatch initializer that makes sure the allowInteractiveAuthentication boolean field we want for
+ * polkit support in Varlink calls is ignored while regular dispatching (and does not result in errors
+ * regarding unexpected fields) */
+#define VARLINK_DISPATCH_POLKIT_FIELD {                          \
+                .name = "allowInteractiveAuthentication",        \
+                .type = JSON_VARIANT_BOOLEAN,                    \
+        }
index 5d4500cb05bf320a572d0b9940a2c482afaef976..1d201528ace4bc50a4256bbb5f27c652a983d219 100644 (file)
@@ -1592,7 +1592,7 @@ int config_parse_mtu(
                 return 0;
         }
 
-        return 0;
+        return 1;
 }
 
 int config_parse_rlimit(
index e7e3a4d71c68c83754f8d885410ee5691c6e1649..ed89580d822b8909c1f7c5cc1113f23722355d06 100644 (file)
@@ -233,7 +233,7 @@ static int extract_image_basename(
                 return r;
 
         if (format_suffixes) {
-                char *e = strv_endswith(name, format_suffixes);
+                char *e = endswith_strv(name, format_suffixes);
                 if (!e) /* Format suffix is required */
                         return -EINVAL;
 
index af2fc9aa70c10dc2f0d72e2d50488f47cc454ed7..15675a7046ea84b19658ece1b68c1ad61e4f4384 100644 (file)
@@ -1771,6 +1771,28 @@ static int json_format(FILE *f, JsonVariant *v, JsonFormatFlags flags, const cha
         return 0;
 }
 
+static bool json_variant_is_sensitive_recursive(JsonVariant *v) {
+        if (!v)
+                return false;
+        if (json_variant_is_sensitive(v))
+                return true;
+        if (v == JSON_VARIANT_MAGIC_EMPTY_ARRAY ||
+            v == JSON_VARIANT_MAGIC_EMPTY_OBJECT)
+                return false;
+        if (!json_variant_is_regular(v))
+                return false;
+        if (!IN_SET(v->type, JSON_VARIANT_ARRAY, JSON_VARIANT_OBJECT))
+                return false;
+        if (v->is_reference)
+                return json_variant_is_sensitive_recursive(v->reference);
+
+        for (size_t i = 0; i < json_variant_elements(v); i++)
+                if (json_variant_is_sensitive_recursive(json_variant_by_index(v, i)))
+                        return true;
+
+        return false;
+}
+
 int json_variant_format(JsonVariant *v, JsonFormatFlags flags, char **ret) {
         _cleanup_(memstream_done) MemStream m = {};
         size_t sz;
@@ -1786,6 +1808,10 @@ int json_variant_format(JsonVariant *v, JsonFormatFlags flags, char **ret) {
         if (flags & JSON_FORMAT_OFF)
                 return -ENOEXEC;
 
+        if ((flags & JSON_FORMAT_REFUSE_SENSITIVE))
+                if (json_variant_is_sensitive_recursive(v))
+                        return -EPERM;
+
         f = memstream_init(&m);
         if (!f)
                 return -ENOMEM;
index c40c23487ab97fd5899e284d42e35835cc04585a..975cf562a9640421bc5baa7fddb0d8e658138fd3 100644 (file)
@@ -185,17 +185,18 @@ struct json_variant_foreach_state {
 int json_variant_get_source(JsonVariant *v, const char **ret_source, unsigned *ret_line, unsigned *ret_column);
 
 typedef enum JsonFormatFlags {
-        JSON_FORMAT_NEWLINE     = 1 << 0, /* suffix with newline */
-        JSON_FORMAT_PRETTY      = 1 << 1, /* add internal whitespace to appeal to human readers */
-        JSON_FORMAT_PRETTY_AUTO = 1 << 2, /* same, but only if connected to a tty (and JSON_FORMAT_NEWLINE otherwise) */
-        JSON_FORMAT_COLOR       = 1 << 3, /* insert ANSI color sequences */
-        JSON_FORMAT_COLOR_AUTO  = 1 << 4, /* insert ANSI color sequences if colors_enabled() says so */
-        JSON_FORMAT_SOURCE      = 1 << 5, /* prefix with source filename/line/column */
-        JSON_FORMAT_SSE         = 1 << 6, /* prefix/suffix with W3C server-sent events */
-        JSON_FORMAT_SEQ         = 1 << 7, /* prefix/suffix with RFC 7464 application/json-seq */
-        JSON_FORMAT_FLUSH       = 1 << 8, /* call fflush() after dumping JSON */
-        JSON_FORMAT_EMPTY_ARRAY = 1 << 9, /* output "[]" for empty input */
-        JSON_FORMAT_OFF         = 1 << 10, /* make json_variant_format() fail with -ENOEXEC */
+        JSON_FORMAT_NEWLINE          = 1 << 0, /* suffix with newline */
+        JSON_FORMAT_PRETTY           = 1 << 1, /* add internal whitespace to appeal to human readers */
+        JSON_FORMAT_PRETTY_AUTO      = 1 << 2, /* same, but only if connected to a tty (and JSON_FORMAT_NEWLINE otherwise) */
+        JSON_FORMAT_COLOR            = 1 << 3, /* insert ANSI color sequences */
+        JSON_FORMAT_COLOR_AUTO       = 1 << 4, /* insert ANSI color sequences if colors_enabled() says so */
+        JSON_FORMAT_SOURCE           = 1 << 5, /* prefix with source filename/line/column */
+        JSON_FORMAT_SSE              = 1 << 6, /* prefix/suffix with W3C server-sent events */
+        JSON_FORMAT_SEQ              = 1 << 7, /* prefix/suffix with RFC 7464 application/json-seq */
+        JSON_FORMAT_FLUSH            = 1 << 8, /* call fflush() after dumping JSON */
+        JSON_FORMAT_EMPTY_ARRAY      = 1 << 9, /* output "[]" for empty input */
+        JSON_FORMAT_OFF              = 1 << 10, /* make json_variant_format() fail with -ENOEXEC */
+        JSON_FORMAT_REFUSE_SENSITIVE = 1 << 11, /* return EPERM if any node in the tree is marked as senstitive */
 } JsonFormatFlags;
 
 int json_variant_format(JsonVariant *v, JsonFormatFlags flags, char **ret);
index 12a27fcb8dfc43061af25d12cc50e24dd1cbf7e9..2b40c7f3bd1fa17fe5bdff20f93078fa8539a9f1 100644 (file)
@@ -171,6 +171,7 @@ struct Varlink {
         JsonVariant *current;
         VarlinkSymbol *current_method;
 
+        int peer_pidfd;
         struct ucred ucred;
         bool ucred_acquired:1;
 
@@ -361,6 +362,8 @@ static int varlink_new(Varlink **ret) {
                 .timeout = VARLINK_DEFAULT_TIMEOUT_USEC,
 
                 .af = -1,
+
+                .peer_pidfd = -EBADF,
         };
 
         *ret = v;
@@ -638,6 +641,8 @@ static void varlink_clear(Varlink *v) {
                 sigterm_wait(v->exec_pid);
                 v->exec_pid = 0;
         }
+
+        v->peer_pidfd = safe_close(v->peer_pidfd);
 }
 
 static Varlink* varlink_destroy(Varlink *v) {
@@ -956,10 +961,6 @@ static int varlink_parse_message(Varlink *v) {
 
         sz = e - begin + 1;
 
-        varlink_log(v, "New incoming message: %s", begin); /* FIXME: should we output the whole message here before validation?
-                                                            * This may produce a non-printable journal entry if the message
-                                                            * is invalid. We may also expose privileged information. */
-
         r = json_parse(begin, 0, &v->current, NULL, NULL);
         if (r < 0) {
                 /* If we encounter a parse failure flush all data. We cannot possibly recover from this,
@@ -1477,6 +1478,48 @@ finish:
         return r;
 }
 
+int varlink_dispatch_again(Varlink *v) {
+        int r;
+
+        assert_return(v, -EINVAL);
+
+        /* If a method call handler could not process the method call just yet (for example because it needed
+         * some Polkit authentication first), then it can leave the call unanswered, do its thing, and then
+         * ask to be dispatched a second time, via this call. It will then be called again, for the same
+         * message */
+
+        if (v->state == VARLINK_DISCONNECTED)
+                return varlink_log_errno(v, SYNTHETIC_ERRNO(ENOTCONN), "Not connected.");
+        if (v->state != VARLINK_PENDING_METHOD)
+                return varlink_log_errno(v, SYNTHETIC_ERRNO(EBUSY), "Connection has no pending method.");
+
+        varlink_set_state(v, VARLINK_IDLE_SERVER);
+
+        r = sd_event_source_set_enabled(v->defer_event_source, SD_EVENT_ON);
+        if (r < 0)
+                return varlink_log_errno(v, r, "Failed to enable deferred event source: %m");
+
+        return 0;
+}
+
+int varlink_get_current_parameters(Varlink *v, JsonVariant **ret) {
+        JsonVariant *p;
+
+        assert_return(v, -EINVAL);
+
+        if (!v->current)
+                return -ENODATA;
+
+        p = json_variant_by_key(v->current, "parameters");
+        if (!p)
+                return -ENODATA;
+
+        if (ret)
+                *ret = json_variant_ref(p);
+
+        return 0;
+}
+
 static void handle_revents(Varlink *v, int revents) {
         assert(v);
 
@@ -1721,12 +1764,17 @@ Varlink* varlink_flush_close_unref(Varlink *v) {
 
 static int varlink_format_json(Varlink *v, JsonVariant *m) {
         _cleanup_(erase_and_freep) char *text = NULL;
+        bool sensitive = false;
         int r;
 
         assert(v);
         assert(m);
 
-        r = json_variant_format(m, 0, &text);
+        r = json_variant_format(m, JSON_FORMAT_REFUSE_SENSITIVE, &text);
+        if (r == -EPERM) {
+                sensitive = true;
+                r = json_variant_format(m, /* flags= */ 0, &text);
+        }
         if (r < 0)
                 return r;
         assert(text[r] == '\0');
@@ -1734,7 +1782,7 @@ static int varlink_format_json(Varlink *v, JsonVariant *m) {
         if (v->output_buffer_size + r + 1 > VARLINK_BUFFER_MAX)
                 return -ENOBUFS;
 
-        varlink_log(v, "Sending message: %s", text);
+        varlink_log(v, "Sending message: %s", sensitive ? "<sensitive data>" : text);
 
         if (v->output_buffer_size == 0) {
 
@@ -1765,7 +1813,7 @@ static int varlink_format_json(Varlink *v, JsonVariant *m) {
                 v->output_buffer_index = 0;
         }
 
-        if (json_variant_is_sensitive(m))
+        if (sensitive)
                 v->output_buffer_sensitive = true; /* Propagate sensitive flag */
         else
                 text = mfree(text); /* No point in the erase_and_free() destructor declared above */
@@ -2591,6 +2639,54 @@ int varlink_get_peer_pid(Varlink *v, pid_t *ret) {
         return 0;
 }
 
+static int varlink_acquire_pidfd(Varlink *v) {
+        assert(v);
+
+        if (v->peer_pidfd >= 0)
+                return 0;
+
+        v->peer_pidfd = getpeerpidfd(v->fd);
+        if (v->peer_pidfd < 0)
+                return v->peer_pidfd;
+
+        return 0;
+}
+
+int varlink_get_peer_pidref(Varlink *v, PidRef *ret) {
+        int r;
+
+        assert_return(v, -EINVAL);
+        assert_return(ret, -EINVAL);
+
+        /* Returns r > 0 if we acquired the pidref via SO_PEERPIDFD (i.e. if we can use it for
+         * authentication). Returns == 0 if we didn't, and the pidref should not be used for
+         * authentication. */
+
+        r = varlink_acquire_pidfd(v);
+        if (r < 0)
+                return r;
+
+        if (v->peer_pidfd < 0) {
+                pid_t pid;
+
+                r = varlink_get_peer_pid(v, &pid);
+                if (r < 0)
+                        return r;
+
+                r = pidref_set_pid(ret, pid);
+                if (r < 0)
+                        return r;
+
+                return 0; /* didn't get pidfd securely */
+        }
+
+        r = pidref_set_pidfd(ret, v->peer_pidfd);
+        if (r < 0)
+                return r;
+
+        return 1; /* got pidfd securely */
+}
+
 int varlink_set_relative_timeout(Varlink *v, usec_t timeout) {
         assert_return(v, -EINVAL);
         assert_return(timeout > 0, -EINVAL);
index c60f695be78ec77acd062b76fefa985355680a9c..a971762a511c65ed9b8af5c4f80e4e3804886180 100644 (file)
@@ -4,6 +4,7 @@
 #include "sd-event.h"
 
 #include "json.h"
+#include "pidref.h"
 #include "time-util.h"
 #include "varlink-idl.h"
 
@@ -116,6 +117,12 @@ int varlink_error_errno(Varlink *v, int error);
 int varlink_notify(Varlink *v, JsonVariant *parameters);
 int varlink_notifyb(Varlink *v, ...);
 
+/* Ask for the current message to be dispatched again */
+int varlink_dispatch_again(Varlink *v);
+
+/* Get the currently processed incoming message */
+int varlink_get_current_parameters(Varlink *v, JsonVariant **ret);
+
 /* Parsing incoming data via json_dispatch() and generate a nice error on parse errors */
 int varlink_dispatch(Varlink *v, JsonVariant *parameters, const JsonDispatch table[], void *userdata);
 
@@ -139,6 +146,7 @@ void* varlink_get_userdata(Varlink *v);
 
 int varlink_get_peer_uid(Varlink *v, uid_t *ret);
 int varlink_get_peer_pid(Varlink *v, pid_t *ret);
+int varlink_get_peer_pidref(Varlink *v, PidRef *ret);
 
 int varlink_set_relative_timeout(Varlink *v, usec_t usec);
 
@@ -214,6 +222,9 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(VarlinkServer *, varlink_server_unref);
 /* This one we invented, and use for generically propagating system errors (errno) to clients */
 #define VARLINK_ERROR_SYSTEM "io.systemd.System"
 
+/* This one we invented and is a weaker version of "org.varlink.service.PermissionDenied", and indicates that if user would allow interactive auth, we might allow access */
+#define VARLINK_ERROR_INTERACTIVE_AUTHENTICATION_REQUIRED "io.systemd.InteractiveAuthenticationRequired"
+
 /* These are errors defined in the Varlink spec */
 #define VARLINK_ERROR_INTERFACE_NOT_FOUND "org.varlink.service.InterfaceNotFound"
 #define VARLINK_ERROR_METHOD_NOT_FOUND "org.varlink.service.MethodNotFound"
index c120a702c66a634f2d30e4beaa07e718bbe39623..4ceb084c0c81fdcfa822aaa8e46d7a0309ea8713 100644 (file)
@@ -104,6 +104,17 @@ static void test_variant_one(const char *data, Test test) {
         assert_se(json_variant_has_type(w, json_variant_type(v)));
         assert_se(json_variant_equal(v, w));
 
+        s = mfree(s);
+        r = json_variant_format(w, JSON_FORMAT_REFUSE_SENSITIVE, &s);
+        assert_se(r == -EPERM);
+        assert_se(!s);
+
+        s = mfree(s);
+        r = json_variant_format(w, JSON_FORMAT_PRETTY, &s);
+        assert_se(r >= 0);
+        assert_se(s);
+        assert_se((size_t) r == strlen(s));
+
         s = mfree(s);
         w = json_variant_unref(w);
 
@@ -813,4 +824,98 @@ TEST(json_dispatch) {
         assert_se(foobar.l == INT16_MIN);
 }
 
+TEST(json_sensitive) {
+        _cleanup_(json_variant_unrefp) JsonVariant *a = NULL, *b = NULL, *v = NULL;
+        _cleanup_free_ char *s = NULL;
+        int r;
+
+        assert_se(json_build(&a, JSON_BUILD_STRV(STRV_MAKE("foo", "bar", "baz", "bar", "baz", "foo", "qux", "baz"))) >= 0);
+        assert_se(json_build(&b, JSON_BUILD_STRV(STRV_MAKE("foo", "bar", "baz", "qux"))) >= 0);
+
+        json_variant_sensitive(a);
+
+        assert_se(json_variant_format(a, JSON_FORMAT_REFUSE_SENSITIVE, &s) == -EPERM);
+        assert_se(!s);
+
+        r = json_variant_format(b, JSON_FORMAT_REFUSE_SENSITIVE, &s);
+        assert_se(r >= 0);
+        assert_se(s);
+        assert_se((size_t) r == strlen(s));
+        s = mfree(s);
+
+        assert_se(json_build(&v, JSON_BUILD_OBJECT(
+                                             JSON_BUILD_PAIR("c", JSON_BUILD_INTEGER(INT64_MIN)),
+                                             JSON_BUILD_PAIR("d", JSON_BUILD_STRING("-9223372036854775808")),
+                                             JSON_BUILD_PAIR("e", JSON_BUILD_EMPTY_OBJECT))) >= 0);
+        json_variant_dump(v, JSON_FORMAT_COLOR|JSON_FORMAT_PRETTY, NULL, NULL);
+
+        r = json_variant_format(v, JSON_FORMAT_REFUSE_SENSITIVE, &s);
+        assert_se(r >= 0);
+        assert_se(s);
+        assert_se((size_t) r == strlen(s));
+        s = mfree(s);
+        v = json_variant_unref(v);
+
+        assert_se(json_build(&v, JSON_BUILD_OBJECT(
+                                             JSON_BUILD_PAIR_VARIANT("b", b),
+                                             JSON_BUILD_PAIR("c", JSON_BUILD_INTEGER(INT64_MIN)),
+                                             JSON_BUILD_PAIR("d", JSON_BUILD_STRING("-9223372036854775808")),
+                                             JSON_BUILD_PAIR("e", JSON_BUILD_EMPTY_OBJECT))) >= 0);
+        json_variant_dump(v, JSON_FORMAT_COLOR|JSON_FORMAT_PRETTY, NULL, NULL);
+
+        r = json_variant_format(v, JSON_FORMAT_REFUSE_SENSITIVE, &s);
+        assert_se(r >= 0);
+        assert_se(s);
+        assert_se((size_t) r == strlen(s));
+        s = mfree(s);
+        v = json_variant_unref(v);
+
+        assert_se(json_build(&v, JSON_BUILD_OBJECT(
+                                             JSON_BUILD_PAIR_VARIANT("b", b),
+                                             JSON_BUILD_PAIR_VARIANT("a", a),
+                                             JSON_BUILD_PAIR("c", JSON_BUILD_INTEGER(INT64_MIN)),
+                                             JSON_BUILD_PAIR("d", JSON_BUILD_STRING("-9223372036854775808")),
+                                             JSON_BUILD_PAIR("e", JSON_BUILD_EMPTY_OBJECT))) >= 0);
+        json_variant_dump(v, JSON_FORMAT_COLOR|JSON_FORMAT_PRETTY, NULL, NULL);
+
+        assert_se(json_variant_format(v, JSON_FORMAT_REFUSE_SENSITIVE, &s) == -EPERM);
+        assert_se(!s);
+        v = json_variant_unref(v);
+
+        assert_se(json_build(&v, JSON_BUILD_OBJECT(
+                                             JSON_BUILD_PAIR_VARIANT("b", b),
+                                             JSON_BUILD_PAIR("c", JSON_BUILD_INTEGER(INT64_MIN)),
+                                             JSON_BUILD_PAIR_VARIANT("a", a),
+                                             JSON_BUILD_PAIR("d", JSON_BUILD_STRING("-9223372036854775808")),
+                                             JSON_BUILD_PAIR("e", JSON_BUILD_EMPTY_OBJECT))) >= 0);
+        json_variant_dump(v, JSON_FORMAT_COLOR|JSON_FORMAT_PRETTY, NULL, NULL);
+
+        assert_se(json_variant_format(v, JSON_FORMAT_REFUSE_SENSITIVE, &s) == -EPERM);
+        assert_se(!s);
+        v = json_variant_unref(v);
+
+        assert_se(json_build(&v, JSON_BUILD_OBJECT(
+                                             JSON_BUILD_PAIR_VARIANT("b", b),
+                                             JSON_BUILD_PAIR("c", JSON_BUILD_INTEGER(INT64_MIN)),
+                                             JSON_BUILD_PAIR("d", JSON_BUILD_STRING("-9223372036854775808")),
+                                             JSON_BUILD_PAIR_VARIANT("a", a),
+                                             JSON_BUILD_PAIR("e", JSON_BUILD_EMPTY_OBJECT))) >= 0);
+        json_variant_dump(v, JSON_FORMAT_COLOR|JSON_FORMAT_PRETTY, NULL, NULL);
+
+        assert_se(json_variant_format(v, JSON_FORMAT_REFUSE_SENSITIVE, &s) == -EPERM);
+        assert_se(!s);
+        v = json_variant_unref(v);
+
+        assert_se(json_build(&v, JSON_BUILD_OBJECT(
+                                             JSON_BUILD_PAIR_VARIANT("b", b),
+                                             JSON_BUILD_PAIR("c", JSON_BUILD_INTEGER(INT64_MIN)),
+                                             JSON_BUILD_PAIR("d", JSON_BUILD_STRING("-9223372036854775808")),
+                                             JSON_BUILD_PAIR("e", JSON_BUILD_EMPTY_OBJECT),
+                                             JSON_BUILD_PAIR_VARIANT("a", a))) >= 0);
+        json_variant_dump(v, JSON_FORMAT_COLOR|JSON_FORMAT_PRETTY, NULL, NULL);
+
+        assert_se(json_variant_format(v, JSON_FORMAT_REFUSE_SENSITIVE, &s) == -EPERM);
+        assert_se(!s);
+}
+
 DEFINE_TEST_MAIN(LOG_DEBUG);
index 957e2141ef2a7b5c5620027d9fccf020e98a6e00..027b2a401eb1d2a3dda7662938818effbfb29d09 100644 (file)
@@ -946,6 +946,27 @@ TEST(is_reaper_process) {
         }
 }
 
+TEST(pid_get_start_time) {
+        _cleanup_(pidref_done) PidRef pidref = PIDREF_NULL;
+
+        assert_se(pidref_set_self(&pidref) >= 0);
+
+        uint64_t start_time;
+        assert_se(pidref_get_start_time(&pidref, &start_time) >= 0);
+        log_info("our starttime: %" PRIu64, start_time);
+
+        _cleanup_(pidref_done_sigkill_wait) PidRef child = PIDREF_NULL;
+
+        assert_se(pidref_safe_fork("(stub)", FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS, &child) >= 0);
+
+        uint64_t start_time2;
+        assert_se(pidref_get_start_time(&child, &start_time2) >= 0);
+
+        log_info("child starttime: %" PRIu64, start_time2);
+
+        assert_se(start_time2 >= start_time);
+}
+
 static int intro(void) {
         log_show_color(true);
         return EXIT_SUCCESS;
index f4a45703d0d433e68d137dc3f07f64662961b8e6..f70e2aa862d3d1f607b8fc91975a7831aab320cb 100644 (file)
@@ -1006,12 +1006,12 @@ TEST(strv_find_first_field) {
         assert_se(streq_ptr(strv_find_first_field(STRV_MAKE("i", "k", "l", "m", "d", "c", "a", "b"), haystack), "j"));
 }
 
-TEST(strv_endswith) {
-        assert_se(streq_ptr(strv_endswith("waldo", STRV_MAKE("xxx", "yyy", "ldo", "zzz")), "ldo"));
-        assert_se(streq_ptr(strv_endswith("waldo", STRV_MAKE("xxx", "yyy", "zzz")), NULL));
-        assert_se(streq_ptr(strv_endswith("waldo", STRV_MAKE("waldo")), "waldo"));
-        assert_se(streq_ptr(strv_endswith("waldo", STRV_MAKE("w", "o", "ldo")), "o"));
-        assert_se(streq_ptr(strv_endswith("waldo", STRV_MAKE("knurz", "", "waldo")), ""));
+TEST(endswith_strv) {
+        assert_se(streq_ptr(endswith_strv("waldo", STRV_MAKE("xxx", "yyy", "ldo", "zzz")), "ldo"));
+        assert_se(streq_ptr(endswith_strv("waldo", STRV_MAKE("xxx", "yyy", "zzz")), NULL));
+        assert_se(streq_ptr(endswith_strv("waldo", STRV_MAKE("waldo")), "waldo"));
+        assert_se(streq_ptr(endswith_strv("waldo", STRV_MAKE("w", "o", "ldo")), "o"));
+        assert_se(streq_ptr(endswith_strv("waldo", STRV_MAKE("knurz", "", "waldo")), ""));
 }
 
 DEFINE_TEST_MAIN(LOG_INFO);
index 53c2a6fb710cd4afb1f1c19f51e9087d58537166..b4a58fc6923961d00129bc6d6303ddd60f521e02 100644 (file)
@@ -119,7 +119,7 @@ static void context_clear(Context *c) {
         assert(c);
 
         free(c->zone);
-        bus_verify_polkit_async_registry_free(c->polkit_registry);
+        hashmap_free(c->polkit_registry);
         sd_bus_message_unref(c->cache);
 
         sd_bus_slot_unref(c->slot_job_removed);
index 1317bc0f76421176cff647c1e5b901290da58d13..6ed15aa402570c6365d60bb0f3aecce24b87c058 100644 (file)
@@ -962,7 +962,7 @@ Manager* manager_free(Manager *m) {
 
         sd_bus_flush_close_unref(m->bus);
 
-        bus_verify_polkit_async_registry_free(m->polkit_registry);
+        hashmap_free(m->polkit_registry);
 
         return mfree(m);
 }
index 1ba8a7fc93bc89a91bdcbf0a1af03a59a9c02bbe..723b31898a923e066e6089527ab5ffc5434b999e 100644 (file)
@@ -1,10 +1,10 @@
 /* SPDX-License-Identifier: LGPL-2.1-or-later */
 
 #include "generator.h"
+#include "parse-util.h"
 #include "proc-cmdline.h"
 #include "special.h"
 #include "tpm2-util.h"
-#include "parse-util.h"
 
 /* A small generator that enqueues tpm2.target as synchronization point if the TPM2 device hasn't shown up
  * yet, but the firmware reports it to exist. This is supposed to deal with systems where the TPM2 driver
index b250a5c18fb1527f1e793247f79783291044b091..8ea801ee96ee44069b0eb3e4219f3065b59fa947 100755 (executable)
@@ -25,14 +25,22 @@ setup_test_user() {
     trap cleanup_test_user EXIT
 }
 
-test_enable_debug() {
-    mkdir -p /run/systemd/system/systemd-logind.service.d
-    cat >/run/systemd/system/systemd-logind.service.d/debug.conf <<EOF
+test_write_dropin() {
+    systemctl edit --runtime --stdin systemd-logind.service --drop-in=debug.conf <<EOF
 [Service]
 Environment=SYSTEMD_LOG_LEVEL=debug
 EOF
-    systemctl daemon-reload
-    systemctl stop systemd-logind.service
+
+    # We test "coldplug" (completely stop and start logind) here. So we need to preserve
+    # the fdstore, which might contain session leader pidfds. This is extremely rare use case
+    # and shall not be considered fully supported.
+    # See also: https://github.com/systemd/systemd/pull/30610#discussion_r1440507850
+    systemctl edit --runtime --stdin systemd-logind.service --drop-in=fdstore-preserve.conf <<EOF
+[Service]
+FileDescriptorStorePreserve=yes
+EOF
+
+    systemctl restart systemd-logind.service
 }
 
 testcase_properties() {
@@ -673,7 +681,7 @@ EOF
 }
 
 setup_test_user
-test_enable_debug
+test_write_dropin
 run_testcases
 
 touch /testok
index 1c2c9a8f65924effdbf8bc9b39df1add131eb8fb..3f10a29c473897cffa087d2cf98467ce275f590a 100755 (executable)
@@ -85,6 +85,7 @@ def main():
     pages = glob.glob(source_glob)
     pages = (p for p in pages
              if Path(p).name not in {
+                     'standard-conf.xml',
                      'systemd.directives.xml',
                      'systemd.index.xml',
                      'directives-template.xml'})
index 794fac19a848b0afef6c005c78991f3fb25eda9a..ac72ccc82f1d25d254e3740fa5f05214b3bd1fff 100644 (file)
@@ -16,7 +16,7 @@ Before=sockets.target
 [Socket]
 ListenStream=/run/systemd/io.systemd.Credentials
 FileDescriptorName=varlink
-SocketMode=0600
+SocketMode=0666
 Accept=yes
 
 [Install]
index 39dc0c2241964a2fac250cc3f7ea3411ed45c874..cc1b6be429c9632a30df88c4bad276dab5ec233f 100644 (file)
@@ -31,7 +31,7 @@ DeviceAllow=char-input rw
 DeviceAllow=char-tty rw
 DeviceAllow=char-vcs rw
 ExecStart={{LIBEXECDIR}}/systemd-logind
-FileDescriptorStoreMax=512
+FileDescriptorStoreMax=768
 IPAddressDeny=any
 LockPersonality=yes
 MemoryDenyWriteExecute=yes