#ifdef __TANDEM
# include <floss.h>
#endif
#include <config.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <stdio.h>
#include <stdlib.h>
#ifdef HAVE_STRING_H
# include <string.h>
#endif
#ifdef HAVE_STRINGS_H
# include <strings.h>
#endif
#include <unistd.h>
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <signal.h>
#include <grp.h>
#include <pwd.h>
#ifdef TIME_WITH_SYS_TIME
# include <time.h>
#endif
#ifdef HAVE_LOGIN_CAP_H
# include <login_cap.h>
# ifndef LOGIN_SETENV
# define LOGIN_SETENV 0
# endif
#endif
#ifdef HAVE_PROJECT_H
# include <project.h>
# include <sys/task.h>
#endif
#ifdef HAVE_SELINUX
# include <selinux/selinux.h>
#endif
#ifdef HAVE_SETAUTHDB
# include <usersec.h>
#endif
#if defined(HAVE_GETPRPWNAM) && defined(HAVE_SET_AUTH_PARAMETERS)
# ifdef __hpux
# undef MAXINT
# include <hpsecurity.h>
# else
# include <sys/security.h>
# endif
# include <prot.h>
#endif
#include <sudo_usage.h>
#include "sudo.h"
#include "sudo_plugin.h"
#include "sudo_plugin_int.h"
struct plugin_container policy_plugin;
struct plugin_container_list io_plugins = TAILQ_HEAD_INITIALIZER(io_plugins);
struct user_details user_details;
const char *list_user;
int sudo_debug_instance = SUDO_DEBUG_INSTANCE_INITIALIZER;
static struct command_details command_details;
static int sudo_mode;
struct sudo_gc_entry {
SLIST_ENTRY(sudo_gc_entry) entries;
enum sudo_gc_types {
GC_UNKNOWN,
GC_VECTOR,
GC_PTR
} type;
union {
char **vec;
void *ptr;
} u;
};
SLIST_HEAD(sudo_gc_list, sudo_gc_entry);
#ifdef NO_LEAKS
static struct sudo_gc_list sudo_gc_list = SLIST_HEAD_INITIALIZER(sudo_gc_list);
#endif
static void fix_fds(void);
static void disable_coredumps(void);
static void sudo_check_suid(const char *path);
static char **get_user_info(struct user_details *);
static void command_info_to_details(char * const info[],
struct command_details *details);
static bool gc_add(enum sudo_gc_types type, void *ptr);
static void gc_init(void);
static int policy_open(struct plugin_container *plugin,
struct sudo_settings *settings,
char * const user_info[], char * const user_env[]);
static void policy_close(struct plugin_container *plugin, int exit_status,
int error);
static int policy_show_version(struct plugin_container *plugin, int verbose);
static int policy_check(struct plugin_container *plugin, int argc,
char * const argv[], char *env_add[], char **command_info[],
char **argv_out[], char **user_env_out[]);
static int policy_list(struct plugin_container *plugin, int argc,
char * const argv[], int verbose, const char *list_user);
static int policy_validate(struct plugin_container *plugin);
static void policy_invalidate(struct plugin_container *plugin, int remove);
static int iolog_open(struct plugin_container *plugin,
struct sudo_settings *settings, char * const user_info[],
char * const command_details[], int argc, char * const argv[],
char * const user_env[]);
static void iolog_close(struct plugin_container *plugin, int exit_status,
int error);
static int iolog_show_version(struct plugin_container *plugin, int verbose);
static void iolog_unlink(struct plugin_container *plugin);
#ifdef RLIMIT_CORE
static struct rlimit corelimit;
#endif
#ifdef __linux__
static struct rlimit nproclimit;
#endif
__dso_public int main(int argc, char *argv[], char *envp[]);
int
main(int argc, char *argv[], char *envp[])
{
int nargc, ok, status = 0;
char **nargv, **env_add;
char **user_info, **command_info, **argv_out, **user_env_out;
struct sudo_settings *settings;
struct plugin_container *plugin, *next;
sigset_t mask;
debug_decl_vars(main, SUDO_DEBUG_MAIN)
fix_fds();
os_init(argc, argv, envp);
setlocale(LC_ALL, "");
bindtextdomain(PACKAGE_NAME, LOCALEDIR);
textdomain(PACKAGE_NAME);
(void) tzset();
#if defined(HAVE_GETPRPWNAM) && defined(HAVE_SET_AUTH_PARAMETERS)
(void) set_auth_parameters(argc, argv);
# ifdef HAVE_INITPRIVS
initprivs();
# endif
#endif
if (sudo_conf_read(NULL, SUDO_CONF_DEBUG) == -1)
exit(EXIT_FAILURE);
sudo_debug_instance = sudo_debug_register(getprogname(),
NULL, NULL, sudo_conf_debug_files(getprogname()));
sudo_check_suid(argc > 0 ? argv[0] : "sudo");
save_signals();
init_signals();
(void) sigemptyset(&mask);
(void) sigprocmask(SIG_SETMASK, &mask, NULL);
sudo_conf_read(NULL, SUDO_CONF_ALL & ~SUDO_CONF_DEBUG);
if ((user_info = get_user_info(&user_details)) == NULL)
sudo_fatalx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
disable_coredumps();
sudo_mode = parse_args(argc, argv, &nargc, &nargv, &settings, &env_add);
sudo_debug_printf(SUDO_DEBUG_DEBUG, "sudo_mode %d", sudo_mode);
if (ISSET(sudo_mode, MODE_VERSION)) {
printf(_("Sudo version %s\n"), PACKAGE_VERSION);
if (user_details.uid == ROOT_UID)
(void) printf(_("Configure options: %s\n"), CONFIGURE_ARGS);
}
sudo_warn_set_conversation(sudo_conversation);
if (!sudo_load_plugins(&policy_plugin, &io_plugins))
sudo_fatalx(U_("fatal error, unable to load plugins"));
ok = policy_open(&policy_plugin, settings, user_info, envp);
if (ok != 1) {
if (ok == -2)
usage(1);
else
sudo_fatalx(U_("unable to initialize policy plugin"));
}
switch (sudo_mode & MODE_MASK) {
case MODE_VERSION:
policy_show_version(&policy_plugin, !user_details.uid);
TAILQ_FOREACH(plugin, &io_plugins, entries) {
ok = iolog_open(plugin, settings, user_info, NULL,
nargc, nargv, envp);
if (ok != -1)
iolog_show_version(plugin, !user_details.uid);
}
break;
case MODE_VALIDATE:
case MODE_VALIDATE|MODE_INVALIDATE:
ok = policy_validate(&policy_plugin);
exit(ok != 1);
case MODE_KILL:
case MODE_INVALIDATE:
policy_invalidate(&policy_plugin, sudo_mode == MODE_KILL);
exit(0);
break;
case MODE_CHECK:
case MODE_CHECK|MODE_INVALIDATE:
case MODE_LIST:
case MODE_LIST|MODE_INVALIDATE:
ok = policy_list(&policy_plugin, nargc, nargv,
ISSET(sudo_mode, MODE_LONG_LIST), list_user);
exit(ok != 1);
case MODE_EDIT:
case MODE_RUN:
ok = policy_check(&policy_plugin, nargc, nargv, env_add,
&command_info, &argv_out, &user_env_out);
sudo_debug_printf(SUDO_DEBUG_INFO, "policy plugin returns %d", ok);
if (ok != 1) {
if (ok == -2)
usage(1);
exit(1);
}
for (nargv = argv_out, nargc = 0; nargv[nargc] != NULL; nargc++)
continue;
if (nargc == 0)
sudo_fatalx(U_("plugin did not return a command to execute"));
TAILQ_FOREACH_SAFE(plugin, &io_plugins, entries, next) {
ok = iolog_open(plugin, settings, user_info,
command_info, nargc, nargv, user_env_out);
switch (ok) {
case 1:
break;
case 0:
iolog_unlink(plugin);
break;
case -2:
usage(1);
break;
default:
sudo_fatalx(U_("error initializing I/O plugin %s"),
plugin->name);
}
}
command_info_to_details(command_info, &command_details);
command_details.argv = argv_out;
command_details.envp = user_env_out;
if (ISSET(sudo_mode, MODE_BACKGROUND))
SET(command_details.flags, CD_BACKGROUND);
if (setuid(ROOT_UID) == -1)
sudo_warn("setuid(%d)", ROOT_UID);
#ifdef RLIMIT_CORE
if (sudo_conf_disable_coredump())
(void) setrlimit(RLIMIT_CORE, &corelimit);
#endif
if (ISSET(command_details.flags, CD_SUDOEDIT)) {
status = sudo_edit(&command_details);
} else {
status = run_command(&command_details);
}
break;
default:
sudo_fatalx(U_("unexpected sudo mode 0x%x"), sudo_mode);
}
if (WIFSIGNALED(status)) {
sigaction_t sa;
memset(&sa, 0, sizeof(sa));
sigemptyset(&sa.sa_mask);
sa.sa_handler = SIG_DFL;
sigaction(SIGINT, &sa, NULL);
sudo_debug_exit_int(__func__, __FILE__, __LINE__, sudo_debug_subsys,
WTERMSIG(status) | 128);
kill(getpid(), WTERMSIG(status));
}
sudo_debug_exit_int(__func__, __FILE__, __LINE__, sudo_debug_subsys,
WEXITSTATUS(status));
exit(WEXITSTATUS(status));
}
int
os_init_common(int argc, char *argv[], char *envp[])
{
initprogname(argc > 0 ? argv[0] : "sudo");
#ifdef STATIC_SUDOERS_PLUGIN
preload_static_symbols();
#endif
gc_init();
return 0;
}
static void
fix_fds(void)
{
int miss[3], devnull = -1;
debug_decl(fix_fds, SUDO_DEBUG_UTIL)
miss[STDIN_FILENO] = fcntl(STDIN_FILENO, F_GETFL, 0) == -1;
miss[STDOUT_FILENO] = fcntl(STDOUT_FILENO, F_GETFL, 0) == -1;
miss[STDERR_FILENO] = fcntl(STDERR_FILENO, F_GETFL, 0) == -1;
if (miss[STDIN_FILENO] || miss[STDOUT_FILENO] || miss[STDERR_FILENO]) {
if ((devnull = open(_PATH_DEVNULL, O_RDWR, 0644)) == -1)
sudo_fatal(U_("unable to open %s"), _PATH_DEVNULL);
if (miss[STDIN_FILENO] && dup2(devnull, STDIN_FILENO) == -1)
sudo_fatal("dup2");
if (miss[STDOUT_FILENO] && dup2(devnull, STDOUT_FILENO) == -1)
sudo_fatal("dup2");
if (miss[STDERR_FILENO] && dup2(devnull, STDERR_FILENO) == -1)
sudo_fatal("dup2");
if (devnull > STDERR_FILENO)
close(devnull);
}
debug_return;
}
static int
fill_group_list(struct user_details *ud, int system_maxgroups)
{
int tries, rval = -1;
debug_decl(fill_group_list, SUDO_DEBUG_UTIL)
ud->ngroups = sudo_conf_max_groups();
if (ud->ngroups > 0) {
ud->groups = reallocarray(NULL, ud->ngroups, sizeof(GETGROUPS_T));
if (ud->groups == NULL) {
sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
goto done;
}
(void)getgrouplist(ud->username, ud->gid, ud->groups, &ud->ngroups);
rval = 0;
} else {
ud->groups = NULL;
ud->ngroups = system_maxgroups << 1;
for (tries = 0; tries < 10 && rval == -1; tries++) {
ud->ngroups <<= 1;
free(ud->groups);
ud->groups = reallocarray(NULL, ud->ngroups, sizeof(GETGROUPS_T));
if (ud->groups == NULL) {
sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
goto done;
}
rval = getgrouplist(ud->username, ud->gid, ud->groups, &ud->ngroups);
}
}
done:
debug_return_int(rval);
}
static char *
get_user_groups(struct user_details *ud)
{
char *cp, *gid_list = NULL;
size_t glsize;
int i, len, maxgroups, group_source;
debug_decl(get_user_groups, SUDO_DEBUG_UTIL)
maxgroups = (int)sysconf(_SC_NGROUPS_MAX);
if (maxgroups < 0)
maxgroups = NGROUPS_MAX;
ud->groups = NULL;
group_source = sudo_conf_group_source();
if (group_source != GROUP_SOURCE_DYNAMIC) {
if ((ud->ngroups = getgroups(0, NULL)) > 0) {
if (ud->ngroups < maxgroups || group_source == GROUP_SOURCE_STATIC) {
ud->groups = reallocarray(NULL, ud->ngroups, sizeof(GETGROUPS_T));
if (ud->groups == NULL)
goto oom;
if (getgroups(ud->ngroups, ud->groups) < 0) {
free(ud->groups);
ud->groups = NULL;
}
}
}
}
if (ud->groups == NULL) {
if (fill_group_list(ud, maxgroups) == -1)
sudo_fatal(U_("unable to get group vector"));
}
glsize = sizeof("groups=") - 1 + (ud->ngroups * (MAX_UID_T_LEN + 1));
if ((gid_list = malloc(glsize)) == NULL)
goto oom;
memcpy(gid_list, "groups=", sizeof("groups=") - 1);
cp = gid_list + sizeof("groups=") - 1;
for (i = 0; i < ud->ngroups; i++) {
len = snprintf(cp, glsize - (cp - gid_list), "%s%u",
i ? "," : "", (unsigned int)ud->groups[i]);
if (len <= 0 || (size_t)len >= glsize - (cp - gid_list))
sudo_fatalx(U_("internal error, %s overflow"), __func__);
cp += len;
}
debug_return_str(gid_list);
oom:
sudo_fatalx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
}
static char **
get_user_info(struct user_details *ud)
{
char *cp, **user_info, path[PATH_MAX];
unsigned int i = 0;
struct passwd *pw;
int fd;
debug_decl(get_user_info, SUDO_DEBUG_UTIL)
memset(ud, 0, sizeof(*ud));
user_info = reallocarray(NULL, 32, sizeof(char *));
if (user_info == NULL)
goto bad;
ud->pid = getpid();
ud->ppid = getppid();
ud->pgid = getpgid(0);
ud->tcpgid = -1;
fd = open(_PATH_TTY, O_RDWR);
if (fd != -1) {
ud->tcpgid = tcgetpgrp(fd);
close(fd);
}
ud->sid = getsid(0);
ud->uid = getuid();
ud->euid = geteuid();
ud->gid = getgid();
ud->egid = getegid();
pw = getpwuid(ud->uid);
if (pw == NULL)
sudo_fatalx(U_("unknown uid %u: who are you?"), (unsigned int)ud->uid);
user_info[i] = sudo_new_key_val("user", pw->pw_name);
if (user_info[i] == NULL)
goto bad;
ud->username = user_info[i] + sizeof("user=") - 1;
if ((ud->shell = getenv("SHELL")) == NULL || ud->shell[0] == '\0') {
ud->shell = pw->pw_shell[0] ? pw->pw_shell : _PATH_SUDO_BSHELL;
}
if ((ud->shell = strdup(ud->shell)) == NULL)
goto bad;
if (asprintf(&user_info[++i], "pid=%d", (int)ud->pid) == -1)
goto bad;
if (asprintf(&user_info[++i], "ppid=%d", (int)ud->ppid) == -1)
goto bad;
if (asprintf(&user_info[++i], "pgid=%d", (int)ud->pgid) == -1)
goto bad;
if (asprintf(&user_info[++i], "tcpgid=%d", (int)ud->tcpgid) == -1)
goto bad;
if (asprintf(&user_info[++i], "sid=%d", (int)ud->sid) == -1)
goto bad;
if (asprintf(&user_info[++i], "uid=%u", (unsigned int)ud->uid) == -1)
goto bad;
if (asprintf(&user_info[++i], "euid=%u", (unsigned int)ud->euid) == -1)
goto bad;
if (asprintf(&user_info[++i], "gid=%u", (unsigned int)ud->gid) == -1)
goto bad;
if (asprintf(&user_info[++i], "egid=%u", (unsigned int)ud->egid) == -1)
goto bad;
if ((cp = get_user_groups(ud)) != NULL)
user_info[++i] = cp;
if (getcwd(path, sizeof(path)) != NULL) {
user_info[++i] = sudo_new_key_val("cwd", path);
if (user_info[i] == NULL)
goto bad;
ud->cwd = user_info[i] + sizeof("cwd=") - 1;
}
if (get_process_ttyname(path, sizeof(path)) != NULL) {
user_info[++i] = sudo_new_key_val("tty", path);
if (user_info[i] == NULL)
goto bad;
ud->tty = user_info[i] + sizeof("tty=") - 1;
} else {
if (errno != ENOENT)
goto bad;
}
cp = sudo_gethostname();
user_info[++i] = sudo_new_key_val("host", cp ? cp : "localhost");
free(cp);
if (user_info[i] == NULL)
goto bad;
ud->host = user_info[i] + sizeof("host=") - 1;
sudo_get_ttysize(&ud->ts_lines, &ud->ts_cols);
if (asprintf(&user_info[++i], "lines=%d", ud->ts_lines) == -1)
goto bad;
if (asprintf(&user_info[++i], "cols=%d", ud->ts_cols) == -1)
goto bad;
user_info[++i] = NULL;
if (!gc_add(GC_VECTOR, user_info))
goto bad;
debug_return_ptr(user_info);
bad:
while (i--)
free(user_info[i]);
free(user_info);
debug_return_ptr(NULL);
}
static void
command_info_to_details(char * const info[], struct command_details *details)
{
int i;
id_t id;
char *cp;
const char *errstr;
debug_decl(command_info_to_details, SUDO_DEBUG_PCOMM)
memset(details, 0, sizeof(*details));
details->closefrom = -1;
details->execfd = -1;
details->flags = CD_SUDOEDIT_CHECKDIR | CD_SET_GROUPS;
TAILQ_INIT(&details->preserved_fds);
#define SET_STRING(s, n) \
if (strncmp(s, info[i], sizeof(s) - 1) == 0 && info[i][sizeof(s) - 1]) { \
details->n = info[i] + sizeof(s) - 1; \
break; \
}
#define SET_FLAG(s, n) \
if (strncmp(s, info[i], sizeof(s) - 1) == 0) { \
switch (sudo_strtobool(info[i] + sizeof(s) - 1)) { \
case true: \
SET(details->flags, n); \
break; \
case false: \
CLR(details->flags, n); \
break; \
default: \
sudo_debug_printf(SUDO_DEBUG_ERROR, \
"invalid boolean value for %s", info[i]); \
break; \
} \
break; \
}
sudo_debug_printf(SUDO_DEBUG_INFO, "command info from plugin:");
for (i = 0; info[i] != NULL; i++) {
sudo_debug_printf(SUDO_DEBUG_INFO, " %d: %s", i, info[i]);
switch (info[i][0]) {
case 'c':
SET_STRING("chroot=", chroot)
SET_STRING("command=", command)
SET_STRING("cwd=", cwd)
if (strncmp("closefrom=", info[i], sizeof("closefrom=") - 1) == 0) {
cp = info[i] + sizeof("closefrom=") - 1;
details->closefrom = strtonum(cp, 0, INT_MAX, &errstr);
if (errstr != NULL)
sudo_fatalx(U_("%s: %s"), info[i], U_(errstr));
break;
}
break;
case 'e':
SET_FLAG("exec_background=", CD_EXEC_BG)
if (strncmp("execfd=", info[i], sizeof("execfd=") - 1) == 0) {
cp = info[i] + sizeof("execfd=") - 1;
details->execfd = strtonum(cp, 0, INT_MAX, &errstr);
if (errstr != NULL)
sudo_fatalx(U_("%s: %s"), info[i], U_(errstr));
#ifdef HAVE_FEXECVE
add_preserved_fd(&details->preserved_fds, details->execfd);
#else
(void)fcntl(details->execfd, F_SETFD, FD_CLOEXEC);
details->execfd = -1;
#endif
break;
}
break;
case 'l':
SET_STRING("login_class=", login_class)
break;
case 'n':
if (strncmp("nice=", info[i], sizeof("nice=") - 1) == 0) {
cp = info[i] + sizeof("nice=") - 1;
details->priority = strtonum(cp, INT_MIN, INT_MAX, &errstr);
if (errstr != NULL)
sudo_fatalx(U_("%s: %s"), info[i], U_(errstr));
SET(details->flags, CD_SET_PRIORITY);
break;
}
SET_FLAG("noexec=", CD_NOEXEC)
break;
case 'p':
SET_FLAG("preserve_groups=", CD_PRESERVE_GROUPS)
if (strncmp("preserve_fds=", info[i], sizeof("preserve_fds=") - 1) == 0) {
parse_preserved_fds(&details->preserved_fds,
info[i] + sizeof("preserve_fds=") - 1);
break;
}
break;
case 'r':
if (strncmp("runas_egid=", info[i], sizeof("runas_egid=") - 1) == 0) {
cp = info[i] + sizeof("runas_egid=") - 1;
id = sudo_strtoid(cp, NULL, NULL, &errstr);
if (errstr != NULL)
sudo_fatalx(U_("%s: %s"), info[i], U_(errstr));
details->egid = (gid_t)id;
SET(details->flags, CD_SET_EGID);
break;
}
if (strncmp("runas_euid=", info[i], sizeof("runas_euid=") - 1) == 0) {
cp = info[i] + sizeof("runas_euid=") - 1;
id = sudo_strtoid(cp, NULL, NULL, &errstr);
if (errstr != NULL)
sudo_fatalx(U_("%s: %s"), info[i], U_(errstr));
details->euid = (uid_t)id;
SET(details->flags, CD_SET_EUID);
break;
}
if (strncmp("runas_gid=", info[i], sizeof("runas_gid=") - 1) == 0) {
cp = info[i] + sizeof("runas_gid=") - 1;
id = sudo_strtoid(cp, NULL, NULL, &errstr);
if (errstr != NULL)
sudo_fatalx(U_("%s: %s"), info[i], U_(errstr));
details->gid = (gid_t)id;
SET(details->flags, CD_SET_GID);
break;
}
if (strncmp("runas_groups=", info[i], sizeof("runas_groups=") - 1) == 0) {
cp = info[i] + sizeof("runas_groups=") - 1;
details->ngroups = sudo_parse_gids(cp, NULL, &details->groups);
if (details->ngroups == -1)
exit(1);
break;
}
if (strncmp("runas_uid=", info[i], sizeof("runas_uid=") - 1) == 0) {
cp = info[i] + sizeof("runas_uid=") - 1;
id = sudo_strtoid(cp, NULL, NULL, &errstr);
if (errstr != NULL)
sudo_fatalx(U_("%s: %s"), info[i], U_(errstr));
details->uid = (uid_t)id;
SET(details->flags, CD_SET_UID);
break;
}
#ifdef HAVE_PRIV_SET
if (strncmp("runas_privs=", info[i], sizeof("runas_privs=") - 1) == 0) {
const char *endp;
cp = info[i] + sizeof("runas_privs=") - 1;
if (*cp != '\0') {
details->privs = priv_str_to_set(cp, ",", &endp);
if (details->privs == NULL)
sudo_warn("invalid runas_privs %s", endp);
}
break;
}
if (strncmp("runas_limitprivs=", info[i], sizeof("runas_limitprivs=") - 1) == 0) {
const char *endp;
cp = info[i] + sizeof("runas_limitprivs=") - 1;
if (*cp != '\0') {
details->limitprivs = priv_str_to_set(cp, ",", &endp);
if (details->limitprivs == NULL)
sudo_warn("invalid runas_limitprivs %s", endp);
}
break;
}
#endif
break;
case 's':
SET_STRING("selinux_role=", selinux_role)
SET_STRING("selinux_type=", selinux_type)
SET_FLAG("set_utmp=", CD_SET_UTMP)
SET_FLAG("sudoedit=", CD_SUDOEDIT)
SET_FLAG("sudoedit_checkdir=", CD_SUDOEDIT_CHECKDIR)
SET_FLAG("sudoedit_follow=", CD_SUDOEDIT_FOLLOW)
break;
case 't':
if (strncmp("timeout=", info[i], sizeof("timeout=") - 1) == 0) {
cp = info[i] + sizeof("timeout=") - 1;
details->timeout = strtonum(cp, 0, INT_MAX, &errstr);
if (errstr != NULL)
sudo_fatalx(U_("%s: %s"), info[i], U_(errstr));
SET(details->flags, CD_SET_TIMEOUT);
break;
}
break;
case 'u':
if (strncmp("umask=", info[i], sizeof("umask=") - 1) == 0) {
cp = info[i] + sizeof("umask=") - 1;
details->umask = sudo_strtomode(cp, &errstr);
if (errstr != NULL)
sudo_fatalx(U_("%s: %s"), info[i], U_(errstr));
SET(details->flags, CD_SET_UMASK);
break;
}
SET_FLAG("use_pty=", CD_USE_PTY)
SET_STRING("utmp_user=", utmp_user)
break;
}
}
if (!ISSET(details->flags, CD_SET_EUID))
details->euid = details->uid;
if (!ISSET(details->flags, CD_SET_EGID))
details->egid = details->gid;
#ifdef HAVE_SETAUTHDB
aix_setauthdb(IDtouser(details->euid), NULL);
#endif
details->pw = getpwuid(details->euid);
if (details->pw != NULL && (details->pw = pw_dup(details->pw)) == NULL)
sudo_fatalx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
#ifdef HAVE_SETAUTHDB
aix_restoreauthdb();
#endif
#ifdef HAVE_SELINUX
if (details->selinux_role != NULL && is_selinux_enabled() > 0)
SET(details->flags, CD_RBAC_ENABLED);
#endif
debug_return;
}
static void
sudo_check_suid(const char *sudo)
{
char pathbuf[PATH_MAX];
struct stat sb;
bool qualified;
debug_decl(sudo_check_suid, SUDO_DEBUG_PCOMM)
if (geteuid() != 0) {
qualified = strchr(sudo, '/') != NULL;
if (!qualified) {
char *path = getenv_unhooked("PATH");
if (path != NULL) {
const char *cp, *ep;
const char *pathend = path + strlen(path);
for (cp = sudo_strsplit(path, pathend, ":", &ep); cp != NULL;
cp = sudo_strsplit(NULL, pathend, ":", &ep)) {
int len = snprintf(pathbuf, sizeof(pathbuf), "%.*s/%s",
(int)(ep - cp), cp, sudo);
if (len <= 0 || (size_t)len >= sizeof(pathbuf))
continue;
if (access(pathbuf, X_OK) == 0) {
sudo = pathbuf;
qualified = true;
break;
}
}
}
}
if (qualified && stat(sudo, &sb) == 0) {
if (sb.st_uid != ROOT_UID || !ISSET(sb.st_mode, S_ISUID)) {
sudo_fatalx(
U_("%s must be owned by uid %d and have the setuid bit set"),
sudo, ROOT_UID);
} else {
sudo_fatalx(U_("effective uid is not %d, is %s on a file system "
"with the 'nosuid' option set or an NFS file system without"
" root privileges?"), ROOT_UID, sudo);
}
} else {
sudo_fatalx(
U_("effective uid is not %d, is sudo installed setuid root?"),
ROOT_UID);
}
}
debug_return;
}
static void
disable_coredumps(void)
{
#if defined(RLIMIT_CORE)
struct rlimit rl;
debug_decl(disable_coredumps, SUDO_DEBUG_UTIL)
if (sudo_conf_disable_coredump()) {
(void) getrlimit(RLIMIT_CORE, &corelimit);
memcpy(&rl, &corelimit, sizeof(struct rlimit));
rl.rlim_cur = 0;
(void) setrlimit(RLIMIT_CORE, &rl);
}
debug_return;
#endif
}
static void
unlimit_nproc(void)
{
#ifdef __linux__
struct rlimit rl;
debug_decl(unlimit_nproc, SUDO_DEBUG_UTIL)
if (getrlimit(RLIMIT_NPROC, &nproclimit) != 0)
sudo_warn("getrlimit");
rl.rlim_cur = rl.rlim_max = RLIM_INFINITY;
if (setrlimit(RLIMIT_NPROC, &rl) != 0) {
rl.rlim_cur = rl.rlim_max = nproclimit.rlim_max;
if (setrlimit(RLIMIT_NPROC, &rl) != 0)
sudo_warn("setrlimit");
}
debug_return;
#endif
}
static void
restore_nproc(void)
{
#ifdef __linux__
debug_decl(restore_nproc, SUDO_DEBUG_UTIL)
if (setrlimit(RLIMIT_NPROC, &nproclimit) != 0)
sudo_warn("setrlimit");
debug_return;
#endif
}
static bool
set_user_groups(struct command_details *details)
{
bool rval = false;
debug_decl(set_user_groups, SUDO_DEBUG_EXEC)
if (!ISSET(details->flags, CD_PRESERVE_GROUPS)) {
if (details->ngroups >= 0) {
if (sudo_setgroups(details->ngroups, details->groups) < 0) {
sudo_warn(U_("unable to set supplementary group IDs"));
goto done;
}
}
}
#ifdef HAVE_SETEUID
if (ISSET(details->flags, CD_SET_EGID) && setegid(details->egid)) {
sudo_warn(U_("unable to set effective gid to runas gid %u"),
(unsigned int)details->egid);
goto done;
}
#endif
if (ISSET(details->flags, CD_SET_GID) && setgid(details->gid)) {
sudo_warn(U_("unable to set gid to runas gid %u"),
(unsigned int)details->gid);
goto done;
}
rval = true;
done:
CLR(details->flags, CD_SET_GROUPS);
debug_return_bool(rval);
}
bool
exec_setup(struct command_details *details, const char *ptyname, int ptyfd)
{
bool rval = false;
debug_decl(exec_setup, SUDO_DEBUG_EXEC)
#ifdef HAVE_SELINUX
if (ISSET(details->flags, CD_RBAC_ENABLED)) {
if (selinux_setup(details->selinux_role, details->selinux_type,
ptyname ? ptyname : user_details.tty, ptyfd) == -1)
goto done;
}
#endif
if (details->pw != NULL) {
#ifdef HAVE_PROJECT_H
set_project(details->pw);
#endif
#ifdef HAVE_PRIV_SET
if (details->privs != NULL) {
if (setppriv(PRIV_SET, PRIV_INHERITABLE, details->privs) != 0) {
sudo_warn("unable to set privileges");
goto done;
}
}
if (details->limitprivs != NULL) {
if (setppriv(PRIV_SET, PRIV_LIMIT, details->limitprivs) != 0) {
sudo_warn("unable to set limit privileges");
goto done;
}
} else if (details->privs != NULL) {
if (setppriv(PRIV_SET, PRIV_LIMIT, details->privs) != 0) {
sudo_warn("unable to set limit privileges");
goto done;
}
}
#endif
#ifdef HAVE_GETUSERATTR
if (aix_prep_user(details->pw->pw_name, ptyname ? ptyname : user_details.tty) != 0) {
goto done;
}
#endif
#ifdef HAVE_LOGIN_CAP_H
if (details->login_class) {
int flags;
login_cap_t *lc;
lc = login_getclass((char *)details->login_class);
if (!lc) {
sudo_warnx(U_("unknown login class %s"), details->login_class);
errno = ENOENT;
goto done;
}
if (ISSET(sudo_mode, MODE_LOGIN_SHELL)) {
flags = LOGIN_SETALL;
CLR(flags, LOGIN_SETGROUP|LOGIN_SETLOGIN|LOGIN_SETUSER|LOGIN_SETENV|LOGIN_SETPATH);
CLR(details->flags, CD_SET_UMASK);
} else {
flags = LOGIN_SETRESOURCES|LOGIN_SETPRIORITY;
}
if (setusercontext(lc, details->pw, details->pw->pw_uid, flags)) {
sudo_warn(U_("unable to set user context"));
if (details->pw->pw_uid != ROOT_UID)
goto done;
}
}
#endif
}
if (ISSET(details->flags, CD_SET_GROUPS)) {
if (!set_user_groups(details))
goto done;
}
if (ISSET(details->flags, CD_SET_PRIORITY)) {
if (setpriority(PRIO_PROCESS, 0, details->priority) != 0) {
sudo_warn(U_("unable to set process priority"));
goto done;
}
}
if (ISSET(details->flags, CD_SET_UMASK))
(void) umask(details->umask);
if (details->chroot) {
if (chroot(details->chroot) != 0 || chdir("/") != 0) {
sudo_warn(U_("unable to change root to %s"), details->chroot);
goto done;
}
}
unlimit_nproc();
#if defined(HAVE_SETRESUID)
if (setresuid(details->uid, details->euid, details->euid) != 0) {
sudo_warn(U_("unable to change to runas uid (%u, %u)"),
(unsigned int)details->uid, (unsigned int)details->euid);
goto done;
}
#elif defined(HAVE_SETREUID)
if (setreuid(details->uid, details->euid) != 0) {
sudo_warn(U_("unable to change to runas uid (%u, %u)"),
(unsigned int)details->uid, (unsigned int)details->euid);
goto done;
}
#else
if (setuid(details->euid) != 0) {
sudo_warn(U_("unable to change to runas uid (%u, %u)"),
(unsigned int)details->euid, (unsigned int)details->euid);
goto done;
}
#endif
restore_nproc();
if (details->cwd != NULL) {
if (details->chroot || user_details.cwd == NULL ||
strcmp(details->cwd, user_details.cwd) != 0) {
if (chdir(details->cwd) != 0) {
sudo_warn(U_("unable to change directory to %s"), details->cwd);
goto done;
}
}
}
rval = true;
done:
debug_return_bool(rval);
}
int
run_command(struct command_details *details)
{
struct plugin_container *plugin;
struct command_status cstat;
int status = 1;
debug_decl(run_command, SUDO_DEBUG_EXEC)
cstat.type = CMD_INVALID;
cstat.val = 0;
sudo_execute(details, &cstat);
switch (cstat.type) {
case CMD_ERRNO:
sudo_debug_printf(SUDO_DEBUG_DEBUG,
"calling policy close with errno %d", cstat.val);
policy_close(&policy_plugin, 0, cstat.val);
TAILQ_FOREACH(plugin, &io_plugins, entries) {
sudo_debug_printf(SUDO_DEBUG_DEBUG,
"calling I/O close with errno %d", cstat.val);
iolog_close(plugin, 0, cstat.val);
}
status = 1;
break;
case CMD_WSTATUS:
status = cstat.val;
#ifdef HAVE_SELINUX
if (ISSET(details->flags, CD_SUDOEDIT_COPY))
break;
#endif
sudo_debug_printf(SUDO_DEBUG_DEBUG,
"calling policy close with wait status %d", status);
policy_close(&policy_plugin, status, 0);
TAILQ_FOREACH(plugin, &io_plugins, entries) {
sudo_debug_printf(SUDO_DEBUG_DEBUG,
"calling I/O close with wait status %d", status);
iolog_close(plugin, status, 0);
}
break;
default:
sudo_warnx(U_("unexpected child termination condition: %d"), cstat.type);
break;
}
debug_return_int(status);
}
static char **
format_plugin_settings(struct plugin_container *plugin,
struct sudo_settings *sudo_settings)
{
size_t plugin_settings_size;
struct sudo_debug_file *debug_file;
struct sudo_settings *setting;
char **plugin_settings;
unsigned int i = 0;
debug_decl(format_plugin_settings, SUDO_DEBUG_PCOMM)
plugin_settings_size = 2;
for (setting = sudo_settings; setting->name != NULL; setting++)
plugin_settings_size++;
if (plugin->debug_files != NULL) {
TAILQ_FOREACH(debug_file, plugin->debug_files, entries)
plugin_settings_size++;
}
plugin_settings = reallocarray(NULL, plugin_settings_size, sizeof(char *));
if (plugin_settings == NULL)
goto bad;
plugin_settings[i] = sudo_new_key_val("plugin_path", plugin->path);
if (plugin_settings[i] == NULL)
goto bad;
for (setting = sudo_settings; setting->name != NULL; setting++) {
if (setting->value != NULL) {
sudo_debug_printf(SUDO_DEBUG_INFO, "settings: %s=%s",
setting->name, setting->value);
plugin_settings[++i] =
sudo_new_key_val(setting->name, setting->value);
if (plugin_settings[i] == NULL)
goto bad;
}
}
if (plugin->debug_files != NULL) {
TAILQ_FOREACH(debug_file, plugin->debug_files, entries) {
if (asprintf(&plugin_settings[++i], "debug_flags=%s %s",
debug_file->debug_file, debug_file->debug_flags) == -1)
goto bad;
}
}
plugin_settings[++i] = NULL;
if (!gc_add(GC_VECTOR, plugin_settings))
sudo_fatalx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
debug_return_ptr(plugin_settings);
bad:
while (i--)
free(plugin_settings[i]);
free(plugin_settings);
debug_return_ptr(NULL);
}
static int
policy_open(struct plugin_container *plugin, struct sudo_settings *settings,
char * const user_info[], char * const user_env[])
{
char **plugin_settings;
int rval;
debug_decl(policy_open, SUDO_DEBUG_PCOMM)
plugin_settings = format_plugin_settings(plugin, settings);
if (plugin_settings == NULL) {
sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
debug_return_int(-1);
}
sudo_debug_set_active_instance(SUDO_DEBUG_INSTANCE_INITIALIZER);
switch (plugin->u.generic->version) {
case SUDO_API_MKVERSION(1, 0):
case SUDO_API_MKVERSION(1, 1):
rval = plugin->u.policy_1_0->open(plugin->u.io_1_0->version,
sudo_conversation_1_7, sudo_conversation_printf, plugin_settings,
user_info, user_env);
break;
default:
rval = plugin->u.policy->open(SUDO_API_VERSION, sudo_conversation,
sudo_conversation_printf, plugin_settings, user_info, user_env,
plugin->options);
}
plugin->debug_instance = sudo_debug_get_active_instance();
sudo_debug_set_active_instance(sudo_debug_instance);
debug_return_int(rval);
}
static void
policy_close(struct plugin_container *plugin, int exit_status, int error_code)
{
debug_decl(policy_close, SUDO_DEBUG_PCOMM)
if (plugin->u.policy->close != NULL) {
sudo_debug_set_active_instance(plugin->debug_instance);
plugin->u.policy->close(exit_status, error_code);
sudo_debug_set_active_instance(sudo_debug_instance);
} else if (error_code) {
errno = error_code;
sudo_warn(U_("unable to execute %s"), command_details.command);
}
debug_return;
}
static int
policy_show_version(struct plugin_container *plugin, int verbose)
{
int rval;
debug_decl(policy_show_version, SUDO_DEBUG_PCOMM)
if (plugin->u.policy->show_version == NULL)
debug_return_int(true);
sudo_debug_set_active_instance(plugin->debug_instance);
rval = plugin->u.policy->show_version(verbose);
sudo_debug_set_active_instance(sudo_debug_instance);
debug_return_int(rval);
}
static int
policy_check(struct plugin_container *plugin, int argc, char * const argv[],
char *env_add[], char **command_info[], char **argv_out[],
char **user_env_out[])
{
int rval;
debug_decl(policy_check, SUDO_DEBUG_PCOMM)
if (plugin->u.policy->check_policy == NULL) {
sudo_fatalx(U_("policy plugin %s is missing the `check_policy' method"),
plugin->name);
}
sudo_debug_set_active_instance(plugin->debug_instance);
rval = plugin->u.policy->check_policy(argc, argv, env_add, command_info,
argv_out, user_env_out);
sudo_debug_set_active_instance(sudo_debug_instance);
debug_return_int(rval);
}
static int
policy_list(struct plugin_container *plugin, int argc, char * const argv[],
int verbose, const char *list_user)
{
int rval;
debug_decl(policy_list, SUDO_DEBUG_PCOMM)
if (plugin->u.policy->list == NULL) {
sudo_warnx(U_("policy plugin %s does not support listing privileges"),
plugin->name);
debug_return_int(false);
}
sudo_debug_set_active_instance(plugin->debug_instance);
rval = plugin->u.policy->list(argc, argv, verbose, list_user);
sudo_debug_set_active_instance(sudo_debug_instance);
debug_return_int(rval);
}
static int
policy_validate(struct plugin_container *plugin)
{
int rval;
debug_decl(policy_validate, SUDO_DEBUG_PCOMM)
if (plugin->u.policy->validate == NULL) {
sudo_warnx(U_("policy plugin %s does not support the -v option"),
plugin->name);
debug_return_int(false);
}
sudo_debug_set_active_instance(plugin->debug_instance);
rval = plugin->u.policy->validate();
sudo_debug_set_active_instance(sudo_debug_instance);
debug_return_int(rval);
}
static void
policy_invalidate(struct plugin_container *plugin, int remove)
{
debug_decl(policy_invalidate, SUDO_DEBUG_PCOMM)
if (plugin->u.policy->invalidate == NULL) {
sudo_fatalx(U_("policy plugin %s does not support the -k/-K options"),
plugin->name);
}
sudo_debug_set_active_instance(plugin->debug_instance);
plugin->u.policy->invalidate(remove);
sudo_debug_set_active_instance(sudo_debug_instance);
debug_return;
}
int
policy_init_session(struct command_details *details)
{
int rval = true;
debug_decl(policy_init_session, SUDO_DEBUG_PCOMM)
if (ISSET(details->flags, CD_SET_GROUPS)) {
if (!set_user_groups(details))
goto done;
}
if (policy_plugin.u.policy->init_session) {
sudo_debug_set_active_instance(policy_plugin.debug_instance);
switch (policy_plugin.u.generic->version) {
case SUDO_API_MKVERSION(1, 0):
case SUDO_API_MKVERSION(1, 1):
rval = policy_plugin.u.policy_1_0->init_session(details->pw);
break;
default:
rval = policy_plugin.u.policy->init_session(details->pw,
&details->envp);
}
sudo_debug_set_active_instance(sudo_debug_instance);
}
done:
debug_return_int(rval);
}
static int
iolog_open(struct plugin_container *plugin, struct sudo_settings *settings,
char * const user_info[], char * const command_info[],
int argc, char * const argv[], char * const user_env[])
{
char **plugin_settings;
int rval;
debug_decl(iolog_open, SUDO_DEBUG_PCOMM)
plugin_settings = format_plugin_settings(plugin, settings);
if (plugin_settings == NULL) {
sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
debug_return_int(-1);
}
sudo_debug_set_active_instance(plugin->debug_instance);
switch (plugin->u.generic->version) {
case SUDO_API_MKVERSION(1, 0):
rval = plugin->u.io_1_0->open(plugin->u.io_1_0->version,
sudo_conversation_1_7, sudo_conversation_printf, plugin_settings,
user_info, argc, argv, user_env);
break;
case SUDO_API_MKVERSION(1, 1):
rval = plugin->u.io_1_1->open(plugin->u.io_1_1->version,
sudo_conversation_1_7, sudo_conversation_printf, plugin_settings,
user_info, command_info, argc, argv, user_env);
break;
default:
rval = plugin->u.io->open(SUDO_API_VERSION, sudo_conversation,
sudo_conversation_printf, plugin_settings, user_info, command_info,
argc, argv, user_env, plugin->options);
}
sudo_debug_set_active_instance(sudo_debug_instance);
debug_return_int(rval);
}
static void
iolog_close(struct plugin_container *plugin, int exit_status, int error_code)
{
debug_decl(iolog_close, SUDO_DEBUG_PCOMM)
if (plugin->u.io->close != NULL) {
sudo_debug_set_active_instance(plugin->debug_instance);
plugin->u.io->close(exit_status, error_code);
sudo_debug_set_active_instance(sudo_debug_instance);
}
debug_return;
}
static int
iolog_show_version(struct plugin_container *plugin, int verbose)
{
int rval;
debug_decl(iolog_show_version, SUDO_DEBUG_PCOMM)
if (plugin->u.io->show_version == NULL)
debug_return_int(true);
sudo_debug_set_active_instance(plugin->debug_instance);
rval = plugin->u.io->show_version(verbose);
sudo_debug_set_active_instance(sudo_debug_instance);
debug_return_int(rval);
}
static void
iolog_unlink(struct plugin_container *plugin)
{
debug_decl(iolog_unlink, SUDO_DEBUG_PCOMM)
if (plugin->u.io->version >= SUDO_API_MKVERSION(1, 2)) {
if (plugin->u.io->deregister_hooks != NULL) {
sudo_debug_set_active_instance(plugin->debug_instance);
plugin->u.io->deregister_hooks(SUDO_HOOK_VERSION,
deregister_hook);
sudo_debug_set_active_instance(sudo_debug_instance);
}
}
TAILQ_REMOVE(&io_plugins, plugin, entries);
free(plugin->path);
free(plugin);
debug_return;
}
static bool
gc_add(enum sudo_gc_types type, void *v)
{
#ifdef NO_LEAKS
struct sudo_gc_entry *gc;
debug_decl(gc_add, SUDO_DEBUG_MAIN)
if (v == NULL)
debug_return_bool(false);
gc = calloc(1, sizeof(*gc));
if (gc == NULL) {
sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
debug_return_bool(false);
}
switch (type) {
case GC_PTR:
gc->u.ptr = v;
break;
case GC_VECTOR:
gc->u.vec = v;
break;
default:
free(gc);
sudo_warnx("unexpected garbage type %d", type);
debug_return_bool(false);
}
gc->type = type;
SLIST_INSERT_HEAD(&sudo_gc_list, gc, entries);
debug_return_bool(true);
#else
return true;
#endif
}
#ifdef NO_LEAKS
static void
gc_run(void)
{
struct plugin_container *plugin;
struct sudo_gc_entry *gc;
char **cur;
debug_decl(gc_run, SUDO_DEBUG_MAIN)
while ((gc = SLIST_FIRST(&sudo_gc_list))) {
SLIST_REMOVE_HEAD(&sudo_gc_list, entries);
switch (gc->type) {
case GC_PTR:
free(gc->u.ptr);
free(gc);
break;
case GC_VECTOR:
for (cur = gc->u.vec; *cur != NULL; cur++)
free(*cur);
free(gc->u.vec);
free(gc);
break;
default:
sudo_warnx("unexpected garbage type %d", gc->type);
}
}
free(policy_plugin.path);
while ((plugin = TAILQ_FIRST(&io_plugins))) {
TAILQ_REMOVE(&io_plugins, plugin, entries);
free(plugin->path);
free(plugin);
}
debug_return;
}
#endif
static void
gc_init(void)
{
#ifdef NO_LEAKS
atexit(gc_run);
#endif
}