This patch adds "user" and "group" config options to the "program" section so the configured command can be run as a different user.
I re-used the setuid/setgid code from "haproxy.c" for this so I'm hoping there are not terrible bugs I've introduced :) Regards, Andrew Heberle >From 571715863738524e3f01fa842f8816f181777b89 Mon Sep 17 00:00:00 2001 From: Andrew Heberle <andrew.hebe...@gmail.com> Date: Thu, 11 Jul 2019 10:57:19 +0800 Subject: [PATCH] MEDIUM: config: Add user/group options to program section --- doc/configuration.txt | 8 ++++++ include/types/global.h | 2 ++ src/mworker-prog.c | 70 ++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 80 insertions(+) diff --git a/doc/configuration.txt b/doc/configuration.txt index a46384bf..98940a0e 100644 --- a/doc/configuration.txt +++ b/doc/configuration.txt @@ -2232,6 +2232,14 @@ command <command> [arguments*] mandatory option of the program section. Arguments containing spaces must be enclosed in quotes or double quotes or be prefixed by a backslash. +user <user name> + Changes the executed command user ID to the <user name> from /etc/passwd. + See also "group". + +group <group name> + Changes the executed command group ID to the <group name> from /etc/group. + See also "user". + option start-on-reload no option start-on-reload Start (or not) a new instance of the program upon a reload of the master. diff --git a/include/types/global.h b/include/types/global.h index df0111c7..b6ba6737 100644 --- a/include/types/global.h +++ b/include/types/global.h @@ -215,6 +215,8 @@ struct mworker_proc { int timestamp; struct server *srv; /* the server entry in the master proxy */ struct list list; + int uid; + int gid; }; extern struct global global; diff --git a/src/mworker-prog.c b/src/mworker-prog.c index ba52406e..1d401a3c 100644 --- a/src/mworker-prog.c +++ b/src/mworker-prog.c @@ -15,6 +15,7 @@ #include <sys/types.h> #include <errno.h> #include <grp.h> +#include <pwd.h> #include <stdio.h> #include <string.h> #include <unistd.h> @@ -91,6 +92,23 @@ int mworker_ext_launch_all() mworker_cleanlisteners(); mworker_cleantasks(); + /* setgid / setuid */ + if (child->gid != -1) { + if (getgroups(0, NULL) > 0 && setgroups(0, NULL) == -1) + ha_warning("[%s.main()] Failed to drop supplementary groups. Using 'gid'/'group'" + " without 'uid'/'user' is generally useless.\n", child->command[0]); + + if (setgid(child->gid) == -1) { + ha_alert("[%s.main()] Cannot set gid %d.\n", child->command[0], child->gid); + exit(1); + } + } + + if (child->uid != -1 && setuid(child->uid) == -1) { + ha_alert("[%s.main()] Cannot set uid %d.\n", child->command[0], child->gid); + exit(1); + } + execvp(child->command[0], child->command); ha_alert("Cannot execute %s: %s\n", child->command[0], strerror(errno)); @@ -143,6 +161,8 @@ int cfg_parse_program(const char *file, int linenum, char **args, int kwm) ext_child->ipc_fd[0] = -1; ext_child->ipc_fd[1] = -1; ext_child->options |= PROC_O_START_RELOAD; /* restart the programs by default */ + ext_child->uid = -1; + ext_child->gid = -1; LIST_INIT(&ext_child->list); list_for_each_entry(child, &proc_list, list) { @@ -219,6 +239,56 @@ int cfg_parse_program(const char *file, int linenum, char **args, int kwm) err_code |= ERR_ALERT | ERR_FATAL; goto error; } + } else if (!strcmp(args[0], "user")) { + struct passwd *ext_child_user; + if (*(args[1]) == '\0') { + ha_alert("parsing [%s:%d]: '%s' expects a user name.\n", + file, linenum, args[0]); + err_code |= ERR_ALERT | ERR_FATAL; + goto error; + } + + if (alertif_too_many_args(1, file, linenum, args, &err_code)) + goto error; + + if (ext_child->uid != -1) { + ha_alert("parsing [%s:%d] : user/uid already specified. Continuing.\n", file, linenum); + err_code |= ERR_ALERT; + goto out; + } + + ext_child_user = getpwnam(args[1]); + if (ext_child_user != NULL) { + ext_child->uid = (int)ext_child_user->pw_uid; + } else { + ha_alert("parsing [%s:%d] : cannot find user id for '%s' (%d:%s)\n", file, linenum, args[1], errno, strerror(errno)); + err_code |= ERR_ALERT | ERR_FATAL; + } + } else if (!strcmp(args[0], "group")) { + struct group *ext_child_group; + if (*(args[1]) == '\0') { + ha_alert("parsing [%s:%d]: '%s' expects a group name.\n", + file, linenum, args[0]); + err_code |= ERR_ALERT | ERR_FATAL; + goto error; + } + + if (alertif_too_many_args(1, file, linenum, args, &err_code)) + goto error; + + if (ext_child->gid != -1) { + ha_alert("parsing [%s:%d] : group/gid already specified. Continuing.\n", file, linenum); + err_code |= ERR_ALERT; + goto out; + } + + ext_child_group = getgrnam(args[1]); + if (ext_child_group != NULL) { + ext_child->gid = (int)ext_child_group->gr_gid; + } else { + ha_alert("parsing [%s:%d] : cannot find group id for '%s' (%d:%s)\n", file, linenum, args[1], errno, strerror(errno)); + err_code |= ERR_ALERT | ERR_FATAL; + } } else { ha_alert("parsing [%s:%d] : unknown keyword '%s' in '%s' section\n", file, linenum, args[0], "program"); err_code |= ERR_ALERT | ERR_FATAL; -- 2.15.0.windows.1