commit:     95e28045eb665da2392be8cc38738ccc66db97d8
Author:     Michał Górny <mgorny <AT> gentoo <DOT> org>
AuthorDate: Mon Dec  8 19:07:55 2025 +0000
Commit:     Michał Górny <mgorny <AT> gentoo <DOT> org>
CommitDate: Mon Dec  8 19:07:55 2025 +0000
URL:        https://gitweb.gentoo.org/proj/steve.git/commit/?id=95e28045

Support dropping privileges

Signed-off-by: Michał Górny <mgorny <AT> gentoo.org>

 steve.cxx | 35 +++++++++++++++++++++++++++++++++++
 1 file changed, 35 insertions(+)

diff --git a/steve.cxx b/steve.cxx
index cf0c063..3caf06f 100644
--- a/steve.cxx
+++ b/steve.cxx
@@ -27,6 +27,8 @@
 #include <variant>
 
 #include <getopt.h>
+#include <grp.h>
+#include <pwd.h>
 #include <sys/poll.h>
 #include <sys/syscall.h>
 #include <unistd.h>
@@ -667,6 +669,29 @@ static void steve_handle_recheck(evutil_socket_t, short, 
void *userdata) {
        steve_wake_waiters(state);
 }
 
+static bool steve_drop_privileges(const char *user) {
+       errno = 0;
+       struct passwd *pw = getpwnam(user);
+
+       if (!pw) {
+               if (errno != 0)
+                       perror("getpwnam() failed");
+               else
+                       std::print(stderr, "user {} not found\n", user);
+       } else if (chdir("/") == -1)
+               perror("chdir('/') failed");
+       else if (setgroups(0, nullptr) == -1)
+               perror("setgroups() failed");
+       else if (setgid(pw->pw_gid) == -1)
+               perror("setgid() failed");
+       else if (setuid(pw->pw_uid) == -1)
+               perror("setuid() failed");
+       else
+               return true;
+
+       return false;
+}
+
 static constexpr char steve_usage[] =
 "usage: {} [options]\n"
 "\n"
@@ -678,6 +703,8 @@ static constexpr char steve_usage[] =
 "                           do not serve tokens unless load is below 
LOAD_AVG\n"
 "    --min-jobs=MIN_JOBS, -m MIN_JOBS\n"
 "                           min. jobs to serve even if load average is 
exceeded\n"
+"    --user=USER, -u USER   drop superuser privileges and switch to USER\n"
+"                           (and its primary group)\n"
 "    --verbose, -v          enable verbose logging\n"
 "    --debug, -d            enable FUSE debug output\n";
 
@@ -687,6 +714,7 @@ static const struct option steve_long_opts[] = {
        { "jobs", required_argument, 0, 'j' },
        { "load-average", required_argument, 0, 'l' },
        { "min-jobs", required_argument, 0, 'm' },
+       { "user", required_argument, 0, 'u' },
        { "verbose", no_argument, 0, 'v' },
        { "debug", no_argument, 0, 'd' },
        {},
@@ -700,6 +728,7 @@ int main(int argc, char **argv)
 
        int opt;
        bool debug = false;
+       const char *user = nullptr;
        while ((opt = getopt_long(argc, argv, steve_short_opts, 
steve_long_opts, nullptr)) != -1) {
                switch (opt) {
                        case 'h':
@@ -730,6 +759,9 @@ int main(int argc, char **argv)
                                        return 1;
                                }
                                break;
+                       case 'u':
+                               user = optarg;
+                               break;
                        case 'v':
                                state.verbose = true;
                                break;
@@ -779,6 +811,9 @@ int main(int argc, char **argv)
        }
        fd_guard cuse_fd_guard{cuse_fd};
 
+       if (user && !steve_drop_privileges(user))
+               return 1;
+
        const char *dev_name = "DEVNAME=steve";
        const char *dev_info_argv[] = { dev_name };
        struct cuse_info ci{};

Reply via email to