|
version 1.1.1.2, 2012/05/29 12:26:49
|
version 1.1.1.6, 2014/06/15 16:12:55
|
|
Line 1
|
Line 1
|
| /* |
/* |
| * Copyright (c) 2009-2012 Todd C. Miller <Todd.Miller@courtesan.com> | * Copyright (c) 2009-2013 Todd C. Miller <Todd.Miller@courtesan.com> |
| * |
* |
| * Permission to use, copy, modify, and distribute this software for any |
* Permission to use, copy, modify, and distribute this software for any |
| * purpose with or without fee is hereby granted, provided that the above |
* purpose with or without fee is hereby granted, provided that the above |
|
Line 21
|
Line 21
|
| #include <config.h> |
#include <config.h> |
| |
|
| #include <sys/types.h> |
#include <sys/types.h> |
| #include <sys/param.h> |
|
| #include <sys/stat.h> |
#include <sys/stat.h> |
| #include <sys/wait.h> |
#include <sys/wait.h> |
| #include <sys/socket.h> |
#include <sys/socket.h> |
|
Line 55
|
Line 54
|
| #include <signal.h> |
#include <signal.h> |
| #include <grp.h> |
#include <grp.h> |
| #include <pwd.h> |
#include <pwd.h> |
| #if TIME_WITH_SYS_TIME | #ifdef TIME_WITH_SYS_TIME |
| # include <time.h> |
# include <time.h> |
| #endif |
#endif |
| #ifdef HAVE_SETLOCALE |
|
| # include <locale.h> |
|
| #endif |
|
| #ifdef HAVE_LOGIN_CAP_H |
#ifdef HAVE_LOGIN_CAP_H |
| # include <login_cap.h> |
# include <login_cap.h> |
| # ifndef LOGIN_SETENV |
# ifndef LOGIN_SETENV |
|
Line 86
|
Line 82
|
| # endif /* __hpux */ |
# endif /* __hpux */ |
| # include <prot.h> |
# include <prot.h> |
| #endif /* HAVE_GETPRPWNAM && HAVE_SET_AUTH_PARAMETERS */ |
#endif /* HAVE_GETPRPWNAM && HAVE_SET_AUTH_PARAMETERS */ |
| #if defined(HAVE_STRUCT_KINFO_PROC_P_TDEV) || defined (HAVE_STRUCT_KINFO_PROC_KP_EPROC_E_TDEV) |
|
| # include <sys/sysctl.h> |
|
| #elif defined(HAVE_STRUCT_KINFO_PROC_KI_TDEV) |
|
| # include <sys/sysctl.h> |
|
| # include <sys/user.h> |
|
| #endif |
|
| |
|
| |
#include <sudo_usage.h> |
| #include "sudo.h" |
#include "sudo.h" |
| #include "sudo_plugin.h" |
#include "sudo_plugin.h" |
| #include "sudo_plugin_int.h" |
#include "sudo_plugin_int.h" |
| #include <sudo_usage.h> |
|
| |
|
| /* |
/* |
| * Local variables |
* Local variables |
| */ |
*/ |
| struct plugin_container policy_plugin; |
struct plugin_container policy_plugin; |
| struct plugin_container_list io_plugins; | struct plugin_container_list io_plugins = TAILQ_HEAD_INITIALIZER(io_plugins); |
| struct user_details user_details; |
struct user_details user_details; |
| const char *list_user, *runas_user, *runas_group; /* extern for parse_args.c */ | const char *list_user; /* extern for parse_args.c */ |
| | static struct command_details command_details; |
| static int sudo_mode; |
static int sudo_mode; |
| |
|
| /* |
/* |
|
Line 142 static void iolog_unlink(struct plugin_container *plug
|
Line 133 static void iolog_unlink(struct plugin_container *plug
|
| |
|
| #ifdef RLIMIT_CORE |
#ifdef RLIMIT_CORE |
| static struct rlimit corelimit; |
static struct rlimit corelimit; |
| #endif /* RLIMIT_CORE */ | #endif |
| #if defined(__linux__) | #ifdef __linux__ |
| static struct rlimit nproclimit; |
static struct rlimit nproclimit; |
| #endif |
#endif |
| |
|
| |
__dso_public int main(int argc, char *argv[], char *envp[]); |
| |
|
| int |
int |
| main(int argc, char *argv[], char *envp[]) |
main(int argc, char *argv[], char *envp[]) |
| { |
{ |
|
Line 154 main(int argc, char *argv[], char *envp[])
|
Line 147 main(int argc, char *argv[], char *envp[])
|
| char **nargv, **settings, **env_add; |
char **nargv, **settings, **env_add; |
| char **user_info, **command_info, **argv_out, **user_env_out; |
char **user_info, **command_info, **argv_out, **user_env_out; |
| struct plugin_container *plugin, *next; |
struct plugin_container *plugin, *next; |
| struct command_details command_details; |
|
| sigset_t mask; |
sigset_t mask; |
| debug_decl(main, SUDO_DEBUG_MAIN) |
debug_decl(main, SUDO_DEBUG_MAIN) |
| |
|
| #if defined(SUDO_DEVEL) && defined(__OpenBSD__) | os_init(argc, argv, envp); |
| { | |
| extern char *malloc_options; | |
| malloc_options = "AFGJPR"; | |
| } | |
| #endif | |
| |
|
| #if !defined(HAVE_GETPROGNAME) && !defined(HAVE___PROGNAME) |
|
| if (argc > 0) |
|
| setprogname(argv[0]); |
|
| #endif |
|
| |
|
| #ifdef HAVE_SETLOCALE |
|
| setlocale(LC_ALL, ""); |
setlocale(LC_ALL, ""); |
| #endif |
|
| bindtextdomain(PACKAGE_NAME, LOCALEDIR); |
bindtextdomain(PACKAGE_NAME, LOCALEDIR); |
| textdomain(PACKAGE_NAME); |
textdomain(PACKAGE_NAME); |
| |
|
| |
#ifdef HAVE_TZSET |
| |
(void) tzset(); |
| |
#endif /* HAVE_TZSET */ |
| |
|
| /* Must be done before we do any password lookups */ |
/* Must be done before we do any password lookups */ |
| #if defined(HAVE_GETPRPWNAM) && defined(HAVE_SET_AUTH_PARAMETERS) |
#if defined(HAVE_GETPRPWNAM) && defined(HAVE_SET_AUTH_PARAMETERS) |
| (void) set_auth_parameters(argc, argv); |
(void) set_auth_parameters(argc, argv); |
|
Line 187 main(int argc, char *argv[], char *envp[])
|
Line 171 main(int argc, char *argv[], char *envp[])
|
| /* Make sure we are setuid root. */ |
/* Make sure we are setuid root. */ |
| sudo_check_suid(argv[0]); |
sudo_check_suid(argv[0]); |
| |
|
| /* Reset signal mask and make sure fds 0-2 are open. */ | /* Reset signal mask, save signal state and make sure fds 0-2 are open. */ |
| (void) sigemptyset(&mask); |
(void) sigemptyset(&mask); |
| (void) sigprocmask(SIG_SETMASK, &mask, NULL); |
(void) sigprocmask(SIG_SETMASK, &mask, NULL); |
| |
save_signals(); |
| fix_fds(); |
fix_fds(); |
| |
|
| |
/* Read sudo.conf. */ |
| |
sudo_conf_read(NULL); |
| |
|
| /* Fill in user_info with user name, uid, cwd, etc. */ |
/* Fill in user_info with user name, uid, cwd, etc. */ |
| memset(&user_details, 0, sizeof(user_details)); |
memset(&user_details, 0, sizeof(user_details)); |
| user_info = get_user_info(&user_details); |
user_info = get_user_info(&user_details); |
| |
|
| /* Read sudo.conf. */ |
|
| sudo_conf_read(); |
|
| |
|
| /* Disable core dumps if not enabled in sudo.conf. */ |
/* Disable core dumps if not enabled in sudo.conf. */ |
| disable_coredumps(); |
disable_coredumps(); |
| |
|
|
Line 215 main(int argc, char *argv[], char *envp[])
|
Line 200 main(int argc, char *argv[], char *envp[])
|
| |
|
| /* Load plugins. */ |
/* Load plugins. */ |
| if (!sudo_load_plugins(&policy_plugin, &io_plugins)) |
if (!sudo_load_plugins(&policy_plugin, &io_plugins)) |
| errorx(1, _("fatal error, unable to load plugins")); | fatalx(U_("fatal error, unable to load plugins")); |
| |
|
| /* Open policy plugin. */ |
/* Open policy plugin. */ |
| ok = policy_open(&policy_plugin, settings, user_info, envp); |
ok = policy_open(&policy_plugin, settings, user_info, envp); |
|
Line 223 main(int argc, char *argv[], char *envp[])
|
Line 208 main(int argc, char *argv[], char *envp[])
|
| if (ok == -2) |
if (ok == -2) |
| usage(1); |
usage(1); |
| else |
else |
| errorx(1, _("unable to initialize policy plugin")); | fatalx(U_("unable to initialize policy plugin")); |
| } |
} |
| |
|
| |
init_signals(); |
| |
|
| switch (sudo_mode & MODE_MASK) { |
switch (sudo_mode & MODE_MASK) { |
| case MODE_VERSION: |
case MODE_VERSION: |
| policy_show_version(&policy_plugin, !user_details.uid); |
policy_show_version(&policy_plugin, !user_details.uid); |
| tq_foreach_fwd(&io_plugins, plugin) { | TAILQ_FOREACH(plugin, &io_plugins, entries) { |
| ok = iolog_open(plugin, settings, user_info, NULL, |
ok = iolog_open(plugin, settings, user_info, NULL, |
| nargc, nargv, envp); |
nargc, nargv, envp); |
| if (ok == 1) | if (ok != -1) |
| iolog_show_version(plugin, !user_details.uid); |
iolog_show_version(plugin, !user_details.uid); |
| } |
} |
| break; |
break; |
|
Line 263 main(int argc, char *argv[], char *envp[])
|
Line 250 main(int argc, char *argv[], char *envp[])
|
| exit(1); /* plugin printed error message */ |
exit(1); /* plugin printed error message */ |
| } |
} |
| /* Open I/O plugins once policy plugin succeeds. */ |
/* Open I/O plugins once policy plugin succeeds. */ |
| for (plugin = io_plugins.first; plugin != NULL; plugin = next) { | TAILQ_FOREACH_SAFE(plugin, &io_plugins, entries, next) { |
| next = plugin->next; | |
| ok = iolog_open(plugin, settings, user_info, |
ok = iolog_open(plugin, settings, user_info, |
| command_info, nargc, nargv, envp); |
command_info, nargc, nargv, envp); |
| switch (ok) { |
switch (ok) { |
|
Line 278 main(int argc, char *argv[], char *envp[])
|
Line 264 main(int argc, char *argv[], char *envp[])
|
| usage(1); |
usage(1); |
| break; |
break; |
| default: |
default: |
| errorx(1, _("error initializing I/O plugin %s"), | fatalx(U_("error initializing I/O plugin %s"), |
| plugin->name); |
plugin->name); |
| } |
} |
| } |
} |
|
Line 289 main(int argc, char *argv[], char *envp[])
|
Line 275 main(int argc, char *argv[], char *envp[])
|
| if (ISSET(sudo_mode, MODE_BACKGROUND)) |
if (ISSET(sudo_mode, MODE_BACKGROUND)) |
| SET(command_details.flags, CD_BACKGROUND); |
SET(command_details.flags, CD_BACKGROUND); |
| /* Become full root (not just setuid) so user cannot kill us. */ |
/* Become full root (not just setuid) so user cannot kill us. */ |
| (void) setuid(ROOT_UID); | if (setuid(ROOT_UID) == -1) |
| | warning("setuid(%d)", ROOT_UID); |
| /* Restore coredumpsize resource limit before running. */ |
/* Restore coredumpsize resource limit before running. */ |
| #ifdef RLIMIT_CORE |
#ifdef RLIMIT_CORE |
| if (sudo_conf_disable_coredump()) |
if (sudo_conf_disable_coredump()) |
|
Line 303 main(int argc, char *argv[], char *envp[])
|
Line 290 main(int argc, char *argv[], char *envp[])
|
| /* The close method was called by sudo_edit/run_command. */ |
/* The close method was called by sudo_edit/run_command. */ |
| break; |
break; |
| default: |
default: |
| errorx(1, _("unexpected sudo mode 0x%x"), sudo_mode); | fatalx(U_("unexpected sudo mode 0x%x"), sudo_mode); |
| } |
} |
| sudo_debug_exit_int(__func__, __FILE__, __LINE__, sudo_debug_subsys, exitcode); |
sudo_debug_exit_int(__func__, __FILE__, __LINE__, sudo_debug_subsys, exitcode); |
| exit(exitcode); |
exit(exitcode); |
| } |
} |
| |
|
| |
int |
| |
os_init_common(int argc, char *argv[], char *envp[]) |
| |
{ |
| |
initprogname(argc > 0 ? argv[0] : "sudo"); |
| |
#ifdef STATIC_SUDOERS_PLUGIN |
| |
preload_static_symbols(); |
| |
#endif |
| |
return 0; |
| |
} |
| |
|
| /* |
/* |
| * Ensure that stdin, stdout and stderr are open; set to /dev/null if not. |
* Ensure that stdin, stdout and stderr are open; set to /dev/null if not. |
| * Some operating systems do this automatically in the kernel or libc. |
* Some operating systems do this automatically in the kernel or libc. |
|
Line 328 fix_fds(void)
|
Line 325 fix_fds(void)
|
| miss[STDERR_FILENO] = fcntl(STDERR_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 (miss[STDIN_FILENO] || miss[STDOUT_FILENO] || miss[STDERR_FILENO]) { |
| if ((devnull = open(_PATH_DEVNULL, O_RDWR, 0644)) == -1) |
if ((devnull = open(_PATH_DEVNULL, O_RDWR, 0644)) == -1) |
| error(1, _("unable to open %s"), _PATH_DEVNULL); | fatal(U_("unable to open %s"), _PATH_DEVNULL); |
| if (miss[STDIN_FILENO] && dup2(devnull, STDIN_FILENO) == -1) |
if (miss[STDIN_FILENO] && dup2(devnull, STDIN_FILENO) == -1) |
| error(1, "dup2"); | fatal("dup2"); |
| if (miss[STDOUT_FILENO] && dup2(devnull, STDOUT_FILENO) == -1) |
if (miss[STDOUT_FILENO] && dup2(devnull, STDOUT_FILENO) == -1) |
| error(1, "dup2"); | fatal("dup2"); |
| if (miss[STDERR_FILENO] && dup2(devnull, STDERR_FILENO) == -1) |
if (miss[STDERR_FILENO] && dup2(devnull, STDERR_FILENO) == -1) |
| error(1, "dup2"); | fatal("dup2"); |
| if (devnull > STDERR_FILENO) |
if (devnull > STDERR_FILENO) |
| close(devnull); |
close(devnull); |
| } |
} |
|
Line 343 fix_fds(void)
|
Line 340 fix_fds(void)
|
| |
|
| /* |
/* |
| * Allocate space for groups and fill in using getgrouplist() |
* Allocate space for groups and fill in using getgrouplist() |
| * for when we cannot use getgroups(). | * for when we cannot (or don't want to) use getgroups(). |
| */ |
*/ |
| static int |
static int |
| fill_group_list(struct user_details *ud) | fill_group_list(struct user_details *ud, int system_maxgroups) |
| { |
{ |
| int maxgroups, tries, rval = -1; | int tries, rval = -1; |
| debug_decl(fill_group_list, SUDO_DEBUG_UTIL) |
debug_decl(fill_group_list, SUDO_DEBUG_UTIL) |
| |
|
| #if defined(HAVE_SYSCONF) && defined(_SC_NGROUPS_MAX) |
|
| maxgroups = (int)sysconf(_SC_NGROUPS_MAX); |
|
| if (maxgroups < 0) |
|
| #endif |
|
| maxgroups = NGROUPS_MAX; |
|
| |
|
| /* |
/* |
| * It is possible to belong to more groups in the group database | * If user specified a max number of groups, use it, otherwise keep |
| * than NGROUPS_MAX. We start off with NGROUPS_MAX * 2 entries | * trying getgrouplist() until we have enough room in the array. |
| * and double this as needed. | |
| */ |
*/ |
| ud->groups = NULL; | ud->ngroups = sudo_conf_max_groups(); |
| ud->ngroups = maxgroups; | if (ud->ngroups > 0) { |
| for (tries = 0; tries < 10 && rval == -1; tries++) { | |
| ud->ngroups *= 2; | |
| efree(ud->groups); | |
| ud->groups = emalloc2(ud->ngroups, sizeof(GETGROUPS_T)); |
ud->groups = emalloc2(ud->ngroups, sizeof(GETGROUPS_T)); |
| rval = getgrouplist(ud->username, ud->gid, ud->groups, &ud->ngroups); | /* No error on insufficient space if user specified max_groups. */ |
| | (void)getgrouplist(ud->username, ud->gid, ud->groups, &ud->ngroups); |
| | rval = 0; |
| | } else { |
| | /* |
| | * It is possible to belong to more groups in the group database |
| | * than NGROUPS_MAX. We start off with NGROUPS_MAX * 4 entries |
| | * and double this as needed. |
| | */ |
| | ud->groups = NULL; |
| | ud->ngroups = system_maxgroups << 1; |
| | for (tries = 0; tries < 10 && rval == -1; tries++) { |
| | ud->ngroups <<= 1; |
| | efree(ud->groups); |
| | ud->groups = emalloc2(ud->ngroups, sizeof(GETGROUPS_T)); |
| | rval = getgrouplist(ud->username, ud->gid, ud->groups, &ud->ngroups); |
| | } |
| } |
} |
| debug_return_int(rval); |
debug_return_int(rval); |
| } |
} |
|
Line 378 get_user_groups(struct user_details *ud)
|
Line 381 get_user_groups(struct user_details *ud)
|
| { |
{ |
| char *cp, *gid_list = NULL; |
char *cp, *gid_list = NULL; |
| size_t glsize; |
size_t glsize; |
| int i, len; | int i, len, maxgroups, group_source; |
| debug_decl(get_user_groups, SUDO_DEBUG_UTIL) |
debug_decl(get_user_groups, SUDO_DEBUG_UTIL) |
| |
|
| /* | #if defined(HAVE_SYSCONF) && defined(_SC_NGROUPS_MAX) |
| * Systems with mbr_check_membership() support more than NGROUPS_MAX | maxgroups = (int)sysconf(_SC_NGROUPS_MAX); |
| * groups so we cannot use getgroups(). | if (maxgroups < 0) |
| */ | #endif |
| | maxgroups = NGROUPS_MAX; |
| | |
| ud->groups = NULL; |
ud->groups = NULL; |
| #ifndef HAVE_MBR_CHECK_MEMBERSHIP | group_source = sudo_conf_group_source(); |
| if ((ud->ngroups = getgroups(0, NULL)) > 0) { | if (group_source != GROUP_SOURCE_DYNAMIC) { |
| ud->groups = emalloc2(ud->ngroups, sizeof(GETGROUPS_T)); | if ((ud->ngroups = getgroups(0, NULL)) > 0) { |
| if (getgroups(ud->ngroups, ud->groups) < 0) { | /* Use groups from kernel if not too many or source is static. */ |
| efree(ud->groups); | if (ud->ngroups < maxgroups || group_source == GROUP_SOURCE_STATIC) { |
| ud->groups = NULL; | ud->groups = emalloc2(ud->ngroups, sizeof(GETGROUPS_T)); |
| | if (getgroups(ud->ngroups, ud->groups) < 0) { |
| | efree(ud->groups); |
| | ud->groups = NULL; |
| | } |
| | } |
| } |
} |
| } |
} |
| #endif /* HAVE_MBR_CHECK_MEMBERSHIP */ |
|
| if (ud->groups == NULL) { |
if (ud->groups == NULL) { |
| if (fill_group_list(ud) == -1) | /* |
| error(1, _("unable to get group vector")); | * Query group database if kernel list is too small or disabled. |
| | * Typically, this is because NFS can only support up to 16 groups. |
| | */ |
| | if (fill_group_list(ud, maxgroups) == -1) |
| | fatal(U_("unable to get group vector")); |
| } |
} |
| |
|
| /* |
/* |
|
Line 423 get_user_groups(struct user_details *ud)
|
Line 436 get_user_groups(struct user_details *ud)
|
| static char ** |
static char ** |
| get_user_info(struct user_details *ud) |
get_user_info(struct user_details *ud) |
| { |
{ |
| char *cp, **user_info, cwd[PATH_MAX], host[MAXHOSTNAMELEN]; | char *cp, **user_info, cwd[PATH_MAX], host[HOST_NAME_MAX + 1]; |
| struct passwd *pw; |
struct passwd *pw; |
| int fd, i = 0; |
int fd, i = 0; |
| debug_decl(get_user_info, SUDO_DEBUG_UTIL) |
debug_decl(get_user_info, SUDO_DEBUG_UTIL) |
|
Line 449 get_user_info(struct user_details *ud)
|
Line 462 get_user_info(struct user_details *ud)
|
| |
|
| pw = getpwuid(ud->uid); |
pw = getpwuid(ud->uid); |
| if (pw == NULL) |
if (pw == NULL) |
| errorx(1, _("unknown uid %u: who are you?"), (unsigned int)ud->uid); | fatalx(U_("unknown uid %u: who are you?"), (unsigned int)ud->uid); |
| |
|
| user_info[i] = fmt_string("user", pw->pw_name); |
user_info[i] = fmt_string("user", pw->pw_name); |
| if (user_info[i] == NULL) |
if (user_info[i] == NULL) |
| errorx(1, _("unable to allocate memory")); | fatal(NULL); |
| ud->username = user_info[i] + sizeof("user=") - 1; |
ud->username = user_info[i] + sizeof("user=") - 1; |
| |
|
| /* Stash user's shell for use with the -s flag; don't pass to plugin. */ |
/* Stash user's shell for use with the -s flag; don't pass to plugin. */ |
|
Line 479 get_user_info(struct user_details *ud)
|
Line 492 get_user_info(struct user_details *ud)
|
| if (getcwd(cwd, sizeof(cwd)) != NULL) { |
if (getcwd(cwd, sizeof(cwd)) != NULL) { |
| user_info[++i] = fmt_string("cwd", cwd); |
user_info[++i] = fmt_string("cwd", cwd); |
| if (user_info[i] == NULL) |
if (user_info[i] == NULL) |
| errorx(1, _("unable to allocate memory")); | fatal(NULL); |
| ud->cwd = user_info[i] + sizeof("cwd=") - 1; |
ud->cwd = user_info[i] + sizeof("cwd=") - 1; |
| } |
} |
| |
|
| if ((cp = get_process_ttyname()) != NULL) { |
if ((cp = get_process_ttyname()) != NULL) { |
| user_info[++i] = fmt_string("tty", cp); |
user_info[++i] = fmt_string("tty", cp); |
| if (user_info[i] == NULL) |
if (user_info[i] == NULL) |
| errorx(1, _("unable to allocate memory")); | fatal(NULL); |
| ud->tty = user_info[i] + sizeof("tty=") - 1; |
ud->tty = user_info[i] + sizeof("tty=") - 1; |
| efree(cp); |
efree(cp); |
| } |
} |
|
Line 497 get_user_info(struct user_details *ud)
|
Line 510 get_user_info(struct user_details *ud)
|
| strlcpy(host, "localhost", sizeof(host)); |
strlcpy(host, "localhost", sizeof(host)); |
| user_info[++i] = fmt_string("host", host); |
user_info[++i] = fmt_string("host", host); |
| if (user_info[i] == NULL) |
if (user_info[i] == NULL) |
| errorx(1, _("unable to allocate memory")); | fatal(NULL); |
| ud->host = user_info[i] + sizeof("host=") - 1; |
ud->host = user_info[i] + sizeof("host=") - 1; |
| |
|
| get_ttysize(&ud->ts_lines, &ud->ts_cols); |
get_ttysize(&ud->ts_lines, &ud->ts_cols); |
|
Line 516 static void
|
Line 529 static void
|
| command_info_to_details(char * const info[], struct command_details *details) |
command_info_to_details(char * const info[], struct command_details *details) |
| { |
{ |
| int i; |
int i; |
| long lval; | id_t id; |
| unsigned long ulval; | char *cp; |
| char *cp, *ep; | const char *errstr; |
| debug_decl(command_info_to_details, SUDO_DEBUG_PCOMM) |
debug_decl(command_info_to_details, SUDO_DEBUG_PCOMM) |
| |
|
| memset(details, 0, sizeof(*details)); |
memset(details, 0, sizeof(*details)); |
| details->closefrom = -1; |
details->closefrom = -1; |
| |
TAILQ_INIT(&details->preserved_fds); |
| |
|
| #define SET_STRING(s, n) \ |
#define SET_STRING(s, n) \ |
| if (strncmp(s, info[i], sizeof(s) - 1) == 0 && info[i][sizeof(s) - 1]) { \ |
if (strncmp(s, info[i], sizeof(s) - 1) == 0 && info[i][sizeof(s) - 1]) { \ |
|
Line 540 command_info_to_details(char * const info[], struct co
|
Line 554 command_info_to_details(char * const info[], struct co
|
| SET_STRING("cwd=", cwd) |
SET_STRING("cwd=", cwd) |
| if (strncmp("closefrom=", info[i], sizeof("closefrom=") - 1) == 0) { |
if (strncmp("closefrom=", info[i], sizeof("closefrom=") - 1) == 0) { |
| cp = info[i] + sizeof("closefrom=") - 1; |
cp = info[i] + sizeof("closefrom=") - 1; |
| if (*cp == '\0') | details->closefrom = strtonum(cp, 0, INT_MAX, &errstr); |
| break; | if (errstr != NULL) |
| errno = 0; | fatalx(U_("%s: %s"), info[i], U_(errstr)); |
| lval = strtol(cp, &ep, 0); | |
| if (*cp != '\0' && *ep == '\0' && | |
| !(errno == ERANGE && | |
| (lval == LONG_MAX || lval == LONG_MIN)) && | |
| lval < INT_MAX && lval > INT_MIN) { | |
| details->closefrom = (int)lval; | |
| } | |
| break; |
break; |
| } |
} |
| break; |
break; |
| |
case 'e': |
| |
if (strncmp("exec_background=", info[i], sizeof("exec_background=") - 1) == 0) { |
| |
if (atobool(info[i] + sizeof("exec_background=") - 1) == true) |
| |
SET(details->flags, CD_EXEC_BG); |
| |
break; |
| |
} |
| |
break; |
| case 'l': |
case 'l': |
| SET_STRING("login_class=", login_class) |
SET_STRING("login_class=", login_class) |
| break; |
break; |
| case 'n': |
case 'n': |
| /* XXX - bounds check -NZERO to NZERO (inclusive). */ |
|
| if (strncmp("nice=", info[i], sizeof("nice=") - 1) == 0) { |
if (strncmp("nice=", info[i], sizeof("nice=") - 1) == 0) { |
| cp = info[i] + sizeof("nice=") - 1; |
cp = info[i] + sizeof("nice=") - 1; |
| if (*cp == '\0') | details->priority = strtonum(cp, INT_MIN, INT_MAX, &errstr); |
| break; | if (errstr != NULL) |
| errno = 0; | fatalx(U_("%s: %s"), info[i], U_(errstr)); |
| lval = strtol(cp, &ep, 0); | SET(details->flags, CD_SET_PRIORITY); |
| if (*cp != '\0' && *ep == '\0' && | |
| !(errno == ERANGE && | |
| (lval == LONG_MAX || lval == LONG_MIN)) && | |
| lval < INT_MAX && lval > INT_MIN) { | |
| details->priority = (int)lval; | |
| SET(details->flags, CD_SET_PRIORITY); | |
| } | |
| break; |
break; |
| } |
} |
| if (strncmp("noexec=", info[i], sizeof("noexec=") - 1) == 0) { |
if (strncmp("noexec=", info[i], sizeof("noexec=") - 1) == 0) { |
|
Line 585 command_info_to_details(char * const info[], struct co
|
Line 591 command_info_to_details(char * const info[], struct co
|
| SET(details->flags, CD_PRESERVE_GROUPS); |
SET(details->flags, CD_PRESERVE_GROUPS); |
| break; |
break; |
| } |
} |
| |
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; |
break; |
| case 'r': |
case 'r': |
| if (strncmp("runas_egid=", info[i], sizeof("runas_egid=") - 1) == 0) { |
if (strncmp("runas_egid=", info[i], sizeof("runas_egid=") - 1) == 0) { |
| cp = info[i] + sizeof("runas_egid=") - 1; |
cp = info[i] + sizeof("runas_egid=") - 1; |
| if (*cp == '\0') | id = atoid(cp, NULL, NULL, &errstr); |
| break; | if (errstr != NULL) |
| errno = 0; | fatalx(U_("%s: %s"), info[i], U_(errstr)); |
| ulval = strtoul(cp, &ep, 0); | details->egid = (gid_t)id; |
| if (*cp != '\0' && *ep == '\0' && | SET(details->flags, CD_SET_EGID); |
| (errno != ERANGE || ulval != ULONG_MAX)) { | |
| details->egid = (gid_t)ulval; | |
| SET(details->flags, CD_SET_EGID); | |
| } | |
| break; |
break; |
| } |
} |
| if (strncmp("runas_euid=", info[i], sizeof("runas_euid=") - 1) == 0) { |
if (strncmp("runas_euid=", info[i], sizeof("runas_euid=") - 1) == 0) { |
| cp = info[i] + sizeof("runas_euid=") - 1; |
cp = info[i] + sizeof("runas_euid=") - 1; |
| if (*cp == '\0') | id = atoid(cp, NULL, NULL, &errstr); |
| break; | if (errstr != NULL) |
| errno = 0; | fatalx(U_("%s: %s"), info[i], U_(errstr)); |
| ulval = strtoul(cp, &ep, 0); | details->euid = (uid_t)id; |
| if (*cp != '\0' && *ep == '\0' && | SET(details->flags, CD_SET_EUID); |
| (errno != ERANGE || ulval != ULONG_MAX)) { | |
| details->euid = (uid_t)ulval; | |
| SET(details->flags, CD_SET_EUID); | |
| } | |
| break; |
break; |
| } |
} |
| if (strncmp("runas_gid=", info[i], sizeof("runas_gid=") - 1) == 0) { |
if (strncmp("runas_gid=", info[i], sizeof("runas_gid=") - 1) == 0) { |
| cp = info[i] + sizeof("runas_gid=") - 1; |
cp = info[i] + sizeof("runas_gid=") - 1; |
| if (*cp == '\0') | id = atoid(cp, NULL, NULL, &errstr); |
| break; | if (errstr != NULL) |
| errno = 0; | fatalx(U_("%s: %s"), info[i], U_(errstr)); |
| ulval = strtoul(cp, &ep, 0); | details->gid = (gid_t)id; |
| if (*cp != '\0' && *ep == '\0' && | SET(details->flags, CD_SET_GID); |
| (errno != ERANGE || ulval != ULONG_MAX)) { | |
| details->gid = (gid_t)ulval; | |
| SET(details->flags, CD_SET_GID); | |
| } | |
| break; |
break; |
| } |
} |
| if (strncmp("runas_groups=", info[i], sizeof("runas_groups=") - 1) == 0) { |
if (strncmp("runas_groups=", info[i], sizeof("runas_groups=") - 1) == 0) { |
| int j; | /* parse_gid_list() will call fatalx() on error. */ |
| |
| /* count groups, alloc and fill in */ | |
| cp = info[i] + sizeof("runas_groups=") - 1; |
cp = info[i] + sizeof("runas_groups=") - 1; |
| if (*cp == '\0') | details->ngroups = parse_gid_list(cp, NULL, &details->groups); |
| break; | |
| for (;;) { | |
| details->ngroups++; | |
| if ((cp = strchr(cp, ',')) == NULL) | |
| break; | |
| cp++; | |
| } | |
| if (details->ngroups != 0) { | |
| details->groups = | |
| emalloc2(details->ngroups, sizeof(GETGROUPS_T)); | |
| cp = info[i] + sizeof("runas_groups=") - 1; | |
| for (j = 0; j < details->ngroups;) { | |
| errno = 0; | |
| ulval = strtoul(cp, &ep, 0); | |
| if (*cp == '\0' || (*ep != ',' && *ep != '\0') || | |
| (ulval == ULONG_MAX && errno == ERANGE)) { | |
| break; | |
| } | |
| details->groups[j++] = (gid_t)ulval; | |
| cp = ep + 1; | |
| } | |
| details->ngroups = j; | |
| } | |
| break; |
break; |
| } |
} |
| if (strncmp("runas_uid=", info[i], sizeof("runas_uid=") - 1) == 0) { |
if (strncmp("runas_uid=", info[i], sizeof("runas_uid=") - 1) == 0) { |
| cp = info[i] + sizeof("runas_uid=") - 1; |
cp = info[i] + sizeof("runas_uid=") - 1; |
| if (*cp == '\0') | id = atoid(cp, NULL, NULL, &errstr); |
| break; | if (errstr != NULL) |
| errno = 0; | fatalx(U_("%s: %s"), info[i], U_(errstr)); |
| ulval = strtoul(cp, &ep, 0); | details->uid = (uid_t)id; |
| if (*cp != '\0' && *ep == '\0' && | SET(details->flags, CD_SET_UID); |
| (errno != ERANGE || ulval != ULONG_MAX)) { | break; |
| details->uid = (uid_t)ulval; | } |
| SET(details->flags, CD_SET_UID); | #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) |
| | warning("invalid runas_privs %s", endp); |
| } |
} |
| break; |
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) |
| |
warning("invalid runas_limitprivs %s", endp); |
| |
} |
| |
break; |
| |
} |
| |
#endif /* HAVE_PRIV_SET */ |
| break; |
break; |
| case 's': |
case 's': |
| SET_STRING("selinux_role=", selinux_role) |
SET_STRING("selinux_role=", selinux_role) |
|
Line 688 command_info_to_details(char * const info[], struct co
|
Line 680 command_info_to_details(char * const info[], struct co
|
| case 't': |
case 't': |
| if (strncmp("timeout=", info[i], sizeof("timeout=") - 1) == 0) { |
if (strncmp("timeout=", info[i], sizeof("timeout=") - 1) == 0) { |
| cp = info[i] + sizeof("timeout=") - 1; |
cp = info[i] + sizeof("timeout=") - 1; |
| if (*cp == '\0') | details->timeout = strtonum(cp, 0, INT_MAX, &errstr); |
| break; | if (errstr != NULL) |
| errno = 0; | fatalx(U_("%s: %s"), info[i], U_(errstr)); |
| lval = strtol(cp, &ep, 0); | SET(details->flags, CD_SET_TIMEOUT); |
| if (*cp != '\0' && *ep == '\0' && | |
| !(errno == ERANGE && | |
| (lval == LONG_MAX || lval == LONG_MIN)) && | |
| lval <= INT_MAX && lval >= 0) { | |
| details->timeout = (int)lval; | |
| SET(details->flags, CD_SET_TIMEOUT); | |
| } | |
| break; |
break; |
| } |
} |
| break; |
break; |
| case 'u': |
case 'u': |
| if (strncmp("umask=", info[i], sizeof("umask=") - 1) == 0) { |
if (strncmp("umask=", info[i], sizeof("umask=") - 1) == 0) { |
| cp = info[i] + sizeof("umask=") - 1; |
cp = info[i] + sizeof("umask=") - 1; |
| if (*cp == '\0') | details->umask = atomode(cp, &errstr); |
| break; | if (errstr != NULL) |
| errno = 0; | fatalx(U_("%s: %s"), info[i], U_(errstr)); |
| ulval = strtoul(cp, &ep, 8); | SET(details->flags, CD_SET_UMASK); |
| if (*cp != '\0' && *ep == '\0' && | |
| (errno != ERANGE || ulval != ULONG_MAX)) { | |
| details->umask = (uid_t)ulval; | |
| SET(details->flags, CD_SET_UMASK); | |
| } | |
| break; |
break; |
| } |
} |
| if (strncmp("use_pty=", info[i], sizeof("use_pty=") - 1) == 0) { |
if (strncmp("use_pty=", info[i], sizeof("use_pty=") - 1) == 0) { |
|
Line 734 command_info_to_details(char * const info[], struct co
|
Line 714 command_info_to_details(char * const info[], struct co
|
| #endif |
#endif |
| details->pw = getpwuid(details->euid); |
details->pw = getpwuid(details->euid); |
| if (details->pw != NULL && (details->pw = pw_dup(details->pw)) == NULL) |
if (details->pw != NULL && (details->pw = pw_dup(details->pw)) == NULL) |
| errorx(1, _("unable to allocate memory")); | fatal(NULL); |
| #ifdef HAVE_SETAUTHDB |
#ifdef HAVE_SETAUTHDB |
| aix_restoreauthdb(); |
aix_restoreauthdb(); |
| #endif |
#endif |
|
Line 747 command_info_to_details(char * const info[], struct co
|
Line 727 command_info_to_details(char * const info[], struct co
|
| } |
} |
| |
|
| static void |
static void |
| sudo_check_suid(const char *path) | sudo_check_suid(const char *sudo) |
| { |
{ |
| |
char pathbuf[PATH_MAX]; |
| struct stat sb; |
struct stat sb; |
| |
bool qualified; |
| debug_decl(sudo_check_suid, SUDO_DEBUG_PCOMM) |
debug_decl(sudo_check_suid, SUDO_DEBUG_PCOMM) |
| |
|
| if (geteuid() != 0) { |
if (geteuid() != 0) { |
| if (strchr(path, '/') != NULL && stat(path, &sb) == 0) { | /* Search for sudo binary in PATH if not fully qualified. */ |
| | qualified = strchr(sudo, '/') != NULL; |
| | if (!qualified) { |
| | char *path = getenv_unhooked("PATH"); |
| | if (path != NULL) { |
| | int len; |
| | char *cp, *colon; |
| | |
| | cp = path = estrdup(path); |
| | do { |
| | if ((colon = strchr(cp, ':'))) |
| | *colon = '\0'; |
| | len = snprintf(pathbuf, sizeof(pathbuf), "%s/%s", cp, sudo); |
| | if (len <= 0 || (size_t)len >= sizeof(pathbuf)) |
| | continue; |
| | if (access(pathbuf, X_OK) == 0) { |
| | sudo = pathbuf; |
| | qualified = true; |
| | break; |
| | } |
| | cp = colon + 1; |
| | } while (colon); |
| | efree(path); |
| | } |
| | } |
| | |
| | if (qualified && stat(sudo, &sb) == 0) { |
| /* Try to determine why sudo was not running as root. */ |
/* Try to determine why sudo was not running as root. */ |
| if (sb.st_uid != ROOT_UID || !ISSET(sb.st_mode, S_ISUID)) { |
if (sb.st_uid != ROOT_UID || !ISSET(sb.st_mode, S_ISUID)) { |
| errorx(1, | fatalx( |
| _("%s must be owned by uid %d and have the setuid bit set"), | U_("%s must be owned by uid %d and have the setuid bit set"), |
| path, ROOT_UID); | sudo, ROOT_UID); |
| } else { |
} else { |
| errorx(1, _("effective uid is not %d, is %s on a file system " | fatalx(U_("effective uid is not %d, is %s on a file system " |
| "with the 'nosuid' option set or an NFS file system without" |
"with the 'nosuid' option set or an NFS file system without" |
| " root privileges?"), ROOT_UID, path); | " root privileges?"), ROOT_UID, sudo); |
| } |
} |
| } else { |
} else { |
| errorx(1, | fatalx( |
| _("effective uid is not %d, is sudo installed setuid root?"), | U_("effective uid is not %d, is sudo installed setuid root?"), |
| ROOT_UID); |
ROOT_UID); |
| } |
} |
| } |
} |
|
Line 781 sudo_check_suid(const char *path)
|
Line 789 sudo_check_suid(const char *path)
|
| static void |
static void |
| disable_coredumps(void) |
disable_coredumps(void) |
| { |
{ |
| #if defined(__linux__) || defined(RLIMIT_CORE) | #if defined(RLIMIT_CORE) |
| struct rlimit rl; |
struct rlimit rl; |
| #endif |
|
| debug_decl(disable_coredumps, SUDO_DEBUG_UTIL) |
debug_decl(disable_coredumps, SUDO_DEBUG_UTIL) |
| |
|
| #if defined(__linux__) |
|
| /* |
/* |
| * Unlimit the number of processes since Linux's setuid() will |
|
| * apply resource limits when changing uid and return EAGAIN if |
|
| * nproc would be violated by the uid switch. |
|
| */ |
|
| (void) getrlimit(RLIMIT_NPROC, &nproclimit); |
|
| rl.rlim_cur = rl.rlim_max = RLIM_INFINITY; |
|
| if (setrlimit(RLIMIT_NPROC, &rl)) { |
|
| memcpy(&rl, &nproclimit, sizeof(struct rlimit)); |
|
| rl.rlim_cur = rl.rlim_max; |
|
| (void)setrlimit(RLIMIT_NPROC, &rl); |
|
| } |
|
| #endif /* __linux__ */ |
|
| #ifdef RLIMIT_CORE |
|
| /* |
|
| * Turn off core dumps? |
* Turn off core dumps? |
| */ |
*/ |
| if (sudo_conf_disable_coredump()) { |
if (sudo_conf_disable_coredump()) { |
|
Line 810 disable_coredumps(void)
|
Line 802 disable_coredumps(void)
|
| rl.rlim_cur = 0; |
rl.rlim_cur = 0; |
| (void) setrlimit(RLIMIT_CORE, &rl); |
(void) setrlimit(RLIMIT_CORE, &rl); |
| } |
} |
| #endif /* RLIMIT_CORE */ |
|
| debug_return; |
debug_return; |
| |
#endif /* RLIMIT_CORE */ |
| } |
} |
| |
|
| #ifdef HAVE_PROJECT_H | /* |
| | * Unlimit the number of processes since Linux's setuid() will |
| | * apply resource limits when changing uid and return EAGAIN if |
| | * nproc would be exceeded by the uid switch. |
| | */ |
| static void |
static void |
| set_project(struct passwd *pw) | unlimit_nproc(void) |
| { |
{ |
| struct project proj; | #ifdef __linux__ |
| char buf[PROJECT_BUFSZ]; | struct rlimit rl; |
| int errval; | debug_decl(unlimit_nproc, SUDO_DEBUG_UTIL) |
| debug_decl(set_project, SUDO_DEBUG_UTIL) | |
| |
|
| /* | (void) getrlimit(RLIMIT_NPROC, &nproclimit); |
| * Collect the default project for the user and settaskid | rl.rlim_cur = rl.rlim_max = RLIM_INFINITY; |
| */ | if (setrlimit(RLIMIT_NPROC, &rl) != 0) { |
| setprojent(); | memcpy(&rl, &nproclimit, sizeof(struct rlimit)); |
| if (getdefaultproj(pw->pw_name, &proj, buf, sizeof(buf)) != NULL) { | rl.rlim_cur = rl.rlim_max; |
| errval = setproject(proj.pj_name, pw->pw_name, TASK_NORMAL); | (void)setrlimit(RLIMIT_NPROC, &rl); |
| switch(errval) { | |
| case 0: | |
| break; | |
| case SETPROJ_ERR_TASK: | |
| switch (errno) { | |
| case EAGAIN: | |
| warningx(_("resource control limit has been reached")); | |
| break; | |
| case ESRCH: | |
| warningx(_("user \"%s\" is not a member of project \"%s\""), | |
| pw->pw_name, proj.pj_name); | |
| break; | |
| case EACCES: | |
| warningx(_("the invoking task is final")); | |
| break; | |
| default: | |
| warningx(_("could not join project \"%s\""), proj.pj_name); | |
| } | |
| case SETPROJ_ERR_POOL: | |
| switch (errno) { | |
| case EACCES: | |
| warningx(_("no resource pool accepting default bindings " | |
| "exists for project \"%s\""), proj.pj_name); | |
| break; | |
| case ESRCH: | |
| warningx(_("specified resource pool does not exist for " | |
| "project \"%s\""), proj.pj_name); | |
| break; | |
| default: | |
| warningx(_("could not bind to default resource pool for " | |
| "project \"%s\""), proj.pj_name); | |
| } | |
| break; | |
| default: | |
| if (errval <= 0) { | |
| warningx(_("setproject failed for project \"%s\""), proj.pj_name); | |
| } else { | |
| warningx(_("warning, resource control assignment failed for " | |
| "project \"%s\""), proj.pj_name); | |
| } | |
| } | |
| } else { | |
| warning("getdefaultproj"); | |
| } |
} |
| endprojent(); |
|
| debug_return; |
debug_return; |
| |
#endif /* __linux__ */ |
| } |
} |
| #endif /* HAVE_PROJECT_H */ |
|
| |
|
| /* |
/* |
| |
* Restore saved value of RLIMIT_NPROC. |
| |
*/ |
| |
static void |
| |
restore_nproc(void) |
| |
{ |
| |
#ifdef __linux__ |
| |
debug_decl(restore_nproc, SUDO_DEBUG_UTIL) |
| |
|
| |
(void) setrlimit(RLIMIT_NPROC, &nproclimit); |
| |
|
| |
debug_return; |
| |
#endif /* __linux__ */ |
| |
} |
| |
|
| |
/* |
| * Setup the execution environment immediately prior to the call to execve() |
* Setup the execution environment immediately prior to the call to execve() |
| * Returns true on success and false on failure. |
* Returns true on success and false on failure. |
| */ |
*/ |
|
Line 900 exec_setup(struct command_details *details, const char
|
Line 866 exec_setup(struct command_details *details, const char
|
| #ifdef HAVE_PROJECT_H |
#ifdef HAVE_PROJECT_H |
| set_project(details->pw); |
set_project(details->pw); |
| #endif |
#endif |
| |
#ifdef HAVE_PRIV_SET |
| |
if (details->privs != NULL) { |
| |
if (setppriv(PRIV_SET, PRIV_INHERITABLE, details->privs) != 0) { |
| |
warning("unable to set privileges"); |
| |
goto done; |
| |
} |
| |
} |
| |
if (details->limitprivs != NULL) { |
| |
if (setppriv(PRIV_SET, PRIV_LIMIT, details->limitprivs) != 0) { |
| |
warning("unable to set limit privileges"); |
| |
goto done; |
| |
} |
| |
} else if (details->privs != NULL) { |
| |
if (setppriv(PRIV_SET, PRIV_LIMIT, details->privs) != 0) { |
| |
warning("unable to set limit privileges"); |
| |
goto done; |
| |
} |
| |
} |
| |
#endif /* HAVE_PRIV_SET */ |
| |
|
| #ifdef HAVE_GETUSERATTR |
#ifdef HAVE_GETUSERATTR |
| aix_prep_user(details->pw->pw_name, ptyname ? ptyname : user_details.tty); |
aix_prep_user(details->pw->pw_name, ptyname ? ptyname : user_details.tty); |
| #endif |
#endif |
|
Line 914 exec_setup(struct command_details *details, const char
|
Line 900 exec_setup(struct command_details *details, const char
|
| */ |
*/ |
| lc = login_getclass((char *)details->login_class); |
lc = login_getclass((char *)details->login_class); |
| if (!lc) { |
if (!lc) { |
| warningx(_("unknown login class %s"), details->login_class); | warningx(U_("unknown login class %s"), details->login_class); |
| errno = ENOENT; |
errno = ENOENT; |
| goto done; |
goto done; |
| } |
} |
|
Line 927 exec_setup(struct command_details *details, const char
|
Line 913 exec_setup(struct command_details *details, const char
|
| flags = LOGIN_SETRESOURCES|LOGIN_SETPRIORITY; |
flags = LOGIN_SETRESOURCES|LOGIN_SETPRIORITY; |
| } |
} |
| if (setusercontext(lc, details->pw, details->pw->pw_uid, flags)) { |
if (setusercontext(lc, details->pw, details->pw->pw_uid, flags)) { |
| if (details->pw->pw_uid != ROOT_UID) { | warning(U_("unable to set user context")); |
| warning(_("unable to set user context")); | if (details->pw->pw_uid != ROOT_UID) |
| goto done; |
goto done; |
| } else |
|
| warning(_("unable to set user context")); |
|
| } |
} |
| } |
} |
| #endif /* HAVE_LOGIN_CAP_H */ |
#endif /* HAVE_LOGIN_CAP_H */ |
|
Line 943 exec_setup(struct command_details *details, const char
|
Line 927 exec_setup(struct command_details *details, const char
|
| if (!ISSET(details->flags, CD_PRESERVE_GROUPS)) { |
if (!ISSET(details->flags, CD_PRESERVE_GROUPS)) { |
| if (details->ngroups >= 0) { |
if (details->ngroups >= 0) { |
| if (sudo_setgroups(details->ngroups, details->groups) < 0) { |
if (sudo_setgroups(details->ngroups, details->groups) < 0) { |
| warning(_("unable to set supplementary group IDs")); | warning(U_("unable to set supplementary group IDs")); |
| goto done; |
goto done; |
| } |
} |
| } |
} |
| } |
} |
| #ifdef HAVE_SETEUID |
#ifdef HAVE_SETEUID |
| if (ISSET(details->flags, CD_SET_EGID) && setegid(details->egid)) { |
if (ISSET(details->flags, CD_SET_EGID) && setegid(details->egid)) { |
| warning(_("unable to set effective gid to runas gid %u"), | warning(U_("unable to set effective gid to runas gid %u"), |
| (unsigned int)details->egid); |
(unsigned int)details->egid); |
| goto done; |
goto done; |
| } |
} |
| #endif |
#endif |
| if (ISSET(details->flags, CD_SET_GID) && setgid(details->gid)) { |
if (ISSET(details->flags, CD_SET_GID) && setgid(details->gid)) { |
| warning(_("unable to set gid to runas gid %u"), | warning(U_("unable to set gid to runas gid %u"), |
| (unsigned int)details->gid); |
(unsigned int)details->gid); |
| goto done; |
goto done; |
| } |
} |
| |
|
| if (ISSET(details->flags, CD_SET_PRIORITY)) { |
if (ISSET(details->flags, CD_SET_PRIORITY)) { |
| if (setpriority(PRIO_PROCESS, 0, details->priority) != 0) { |
if (setpriority(PRIO_PROCESS, 0, details->priority) != 0) { |
| warning(_("unable to set process priority")); | warning(U_("unable to set process priority")); |
| goto done; |
goto done; |
| } |
} |
| } |
} |
|
Line 971 exec_setup(struct command_details *details, const char
|
Line 955 exec_setup(struct command_details *details, const char
|
| (void) umask(details->umask); |
(void) umask(details->umask); |
| if (details->chroot) { |
if (details->chroot) { |
| if (chroot(details->chroot) != 0 || chdir("/") != 0) { |
if (chroot(details->chroot) != 0 || chdir("/") != 0) { |
| warning(_("unable to change root to %s"), details->chroot); | warning(U_("unable to change root to %s"), details->chroot); |
| goto done; |
goto done; |
| } |
} |
| } |
} |
| |
|
| |
/* |
| |
* Unlimit the number of processes since Linux's setuid() will |
| |
* return EAGAIN if RLIMIT_NPROC would be exceeded by the uid switch. |
| |
*/ |
| |
unlimit_nproc(); |
| |
|
| #ifdef HAVE_SETRESUID |
#ifdef HAVE_SETRESUID |
| if (setresuid(details->uid, details->euid, details->euid) != 0) { |
if (setresuid(details->uid, details->euid, details->euid) != 0) { |
| warning(_("unable to change to runas uid (%u, %u)"), details->uid, | warning(U_("unable to change to runas uid (%u, %u)"), details->uid, |
| details->euid); |
details->euid); |
| goto done; |
goto done; |
| } |
} |
| #elif HAVE_SETREUID | #elif defined(HAVE_SETREUID) |
| if (setreuid(details->uid, details->euid) != 0) { |
if (setreuid(details->uid, details->euid) != 0) { |
| warning(_("unable to change to runas uid (%u, %u)"), | warning(U_("unable to change to runas uid (%u, %u)"), |
| (unsigned int)details->uid, (unsigned int)details->euid); |
(unsigned int)details->uid, (unsigned int)details->euid); |
| goto done; |
goto done; |
| } |
} |
| #else |
#else |
| if (seteuid(details->euid) != 0 || setuid(details->euid) != 0) { |
if (seteuid(details->euid) != 0 || setuid(details->euid) != 0) { |
| warning(_("unable to change to runas uid (%u, %u)"), details->uid, | warning(U_("unable to change to runas uid (%u, %u)"), details->uid, |
| details->euid); |
details->euid); |
| goto done; |
goto done; |
| } |
} |
| #endif /* !HAVE_SETRESUID && !HAVE_SETREUID */ |
#endif /* !HAVE_SETRESUID && !HAVE_SETREUID */ |
| |
|
| |
/* Restore previous value of RLIMIT_NPROC. */ |
| |
restore_nproc(); |
| |
|
| /* |
/* |
| * Only change cwd if we have chroot()ed or the policy modules |
* Only change cwd if we have chroot()ed or the policy modules |
| * specifies a different cwd. Must be done after uid change. |
* specifies a different cwd. Must be done after uid change. |
|
Line 1004 exec_setup(struct command_details *details, const char
|
Line 997 exec_setup(struct command_details *details, const char
|
| if (details->chroot || strcmp(details->cwd, user_details.cwd) != 0) { |
if (details->chroot || strcmp(details->cwd, user_details.cwd) != 0) { |
| /* Note: cwd is relative to the new root, if any. */ |
/* Note: cwd is relative to the new root, if any. */ |
| if (chdir(details->cwd) != 0) { |
if (chdir(details->cwd) != 0) { |
| warning(_("unable to change directory to %s"), details->cwd); | warning(U_("unable to change directory to %s"), details->cwd); |
| goto done; |
goto done; |
| } |
} |
| } |
} |
| } |
} |
| |
|
| /* |
|
| * Restore nproc resource limit if pam_limits didn't do it for us. |
|
| * We must do this *after* the uid change to avoid potential EAGAIN |
|
| * from setuid(). |
|
| */ |
|
| #if defined(__linux__) |
|
| { |
|
| struct rlimit rl; |
|
| if (getrlimit(RLIMIT_NPROC, &rl) == 0) { |
|
| if (rl.rlim_cur == RLIM_INFINITY && rl.rlim_max == RLIM_INFINITY) |
|
| (void) setrlimit(RLIMIT_NPROC, &nproclimit); |
|
| } |
|
| } |
|
| #endif |
|
| |
|
| rval = true; |
rval = true; |
| |
|
| done: |
done: |
|
Line 1053 run_command(struct command_details *details)
|
Line 1031 run_command(struct command_details *details)
|
| sudo_debug_printf(SUDO_DEBUG_DEBUG, |
sudo_debug_printf(SUDO_DEBUG_DEBUG, |
| "calling policy close with errno %d", cstat.val); |
"calling policy close with errno %d", cstat.val); |
| policy_close(&policy_plugin, 0, cstat.val); |
policy_close(&policy_plugin, 0, cstat.val); |
| tq_foreach_fwd(&io_plugins, plugin) { | TAILQ_FOREACH(plugin, &io_plugins, entries) { |
| sudo_debug_printf(SUDO_DEBUG_DEBUG, |
sudo_debug_printf(SUDO_DEBUG_DEBUG, |
| "calling I/O close with errno %d", cstat.val); |
"calling I/O close with errno %d", cstat.val); |
| iolog_close(plugin, 0, cstat.val); |
iolog_close(plugin, 0, cstat.val); |
|
Line 1065 run_command(struct command_details *details)
|
Line 1043 run_command(struct command_details *details)
|
| sudo_debug_printf(SUDO_DEBUG_DEBUG, |
sudo_debug_printf(SUDO_DEBUG_DEBUG, |
| "calling policy close with wait status %d", cstat.val); |
"calling policy close with wait status %d", cstat.val); |
| policy_close(&policy_plugin, cstat.val, 0); |
policy_close(&policy_plugin, cstat.val, 0); |
| tq_foreach_fwd(&io_plugins, plugin) { | TAILQ_FOREACH(plugin, &io_plugins, entries) { |
| sudo_debug_printf(SUDO_DEBUG_DEBUG, |
sudo_debug_printf(SUDO_DEBUG_DEBUG, |
| "calling I/O close with wait status %d", cstat.val); |
"calling I/O close with wait status %d", cstat.val); |
| iolog_close(plugin, cstat.val, 0); |
iolog_close(plugin, cstat.val, 0); |
|
Line 1076 run_command(struct command_details *details)
|
Line 1054 run_command(struct command_details *details)
|
| exitcode = WTERMSIG(cstat.val) | 128; |
exitcode = WTERMSIG(cstat.val) | 128; |
| break; |
break; |
| default: |
default: |
| warningx(_("unexpected child termination condition: %d"), cstat.type); | warningx(U_("unexpected child termination condition: %d"), cstat.type); |
| break; |
break; |
| } |
} |
| debug_return_int(exitcode); |
debug_return_int(exitcode); |
|
Line 1110 static void
|
Line 1088 static void
|
| policy_close(struct plugin_container *plugin, int exit_status, int error) |
policy_close(struct plugin_container *plugin, int exit_status, int error) |
| { |
{ |
| debug_decl(policy_close, SUDO_DEBUG_PCOMM) |
debug_decl(policy_close, SUDO_DEBUG_PCOMM) |
| plugin->u.policy->close(exit_status, error); | if (plugin->u.policy->close != NULL) |
| | plugin->u.policy->close(exit_status, error); |
| | else |
| | warning(U_("unable to execute %s"), command_details.command); |
| debug_return; |
debug_return; |
| } |
} |
| |
|
|
Line 1118 static int
|
Line 1099 static int
|
| policy_show_version(struct plugin_container *plugin, int verbose) |
policy_show_version(struct plugin_container *plugin, int verbose) |
| { |
{ |
| debug_decl(policy_show_version, SUDO_DEBUG_PCOMM) |
debug_decl(policy_show_version, SUDO_DEBUG_PCOMM) |
| |
if (plugin->u.policy->show_version == NULL) |
| |
debug_return_bool(true); |
| debug_return_bool(plugin->u.policy->show_version(verbose)); |
debug_return_bool(plugin->u.policy->show_version(verbose)); |
| } |
} |
| |
|
|
Line 1127 policy_check(struct plugin_container *plugin, int argc
|
Line 1110 policy_check(struct plugin_container *plugin, int argc
|
| char **user_env_out[]) |
char **user_env_out[]) |
| { |
{ |
| debug_decl(policy_check, SUDO_DEBUG_PCOMM) |
debug_decl(policy_check, SUDO_DEBUG_PCOMM) |
| |
if (plugin->u.policy->check_policy == NULL) { |
| |
fatalx(U_("policy plugin %s is missing the `check_policy' method"), |
| |
plugin->name); |
| |
} |
| debug_return_bool(plugin->u.policy->check_policy(argc, argv, env_add, |
debug_return_bool(plugin->u.policy->check_policy(argc, argv, env_add, |
| command_info, argv_out, user_env_out)); |
command_info, argv_out, user_env_out)); |
| } |
} |
|
Line 1137 policy_list(struct plugin_container *plugin, int argc,
|
Line 1124 policy_list(struct plugin_container *plugin, int argc,
|
| { |
{ |
| debug_decl(policy_list, SUDO_DEBUG_PCOMM) |
debug_decl(policy_list, SUDO_DEBUG_PCOMM) |
| if (plugin->u.policy->list == NULL) { |
if (plugin->u.policy->list == NULL) { |
| warningx(_("policy plugin %s does not support listing privileges"), | warningx(U_("policy plugin %s does not support listing privileges"), |
| plugin->name); |
plugin->name); |
| debug_return_bool(false); |
debug_return_bool(false); |
| } |
} |
|
Line 1149 policy_validate(struct plugin_container *plugin)
|
Line 1136 policy_validate(struct plugin_container *plugin)
|
| { |
{ |
| debug_decl(policy_validate, SUDO_DEBUG_PCOMM) |
debug_decl(policy_validate, SUDO_DEBUG_PCOMM) |
| if (plugin->u.policy->validate == NULL) { |
if (plugin->u.policy->validate == NULL) { |
| warningx(_("policy plugin %s does not support the -v option"), | warningx(U_("policy plugin %s does not support the -v option"), |
| plugin->name); |
plugin->name); |
| debug_return_bool(false); |
debug_return_bool(false); |
| } |
} |
|
Line 1161 policy_invalidate(struct plugin_container *plugin, int
|
Line 1148 policy_invalidate(struct plugin_container *plugin, int
|
| { |
{ |
| debug_decl(policy_invalidate, SUDO_DEBUG_PCOMM) |
debug_decl(policy_invalidate, SUDO_DEBUG_PCOMM) |
| if (plugin->u.policy->invalidate == NULL) { |
if (plugin->u.policy->invalidate == NULL) { |
| errorx(1, _("policy plugin %s does not support the -k/-K options"), | fatalx(U_("policy plugin %s does not support the -k/-K options"), |
| plugin->name); |
plugin->name); |
| } |
} |
| plugin->u.policy->invalidate(remove); |
plugin->u.policy->invalidate(remove); |
|
Line 1225 static void
|
Line 1212 static void
|
| iolog_close(struct plugin_container *plugin, int exit_status, int error) |
iolog_close(struct plugin_container *plugin, int exit_status, int error) |
| { |
{ |
| debug_decl(iolog_close, SUDO_DEBUG_PCOMM) |
debug_decl(iolog_close, SUDO_DEBUG_PCOMM) |
| plugin->u.io->close(exit_status, error); | if (plugin->u.io->close != NULL) |
| | plugin->u.io->close(exit_status, error); |
| debug_return; |
debug_return; |
| } |
} |
| |
|
|
Line 1233 static int
|
Line 1221 static int
|
| iolog_show_version(struct plugin_container *plugin, int verbose) |
iolog_show_version(struct plugin_container *plugin, int verbose) |
| { |
{ |
| debug_decl(iolog_show_version, SUDO_DEBUG_PCOMM) |
debug_decl(iolog_show_version, SUDO_DEBUG_PCOMM) |
| |
if (plugin->u.io->show_version == NULL) |
| |
debug_return_bool(true); |
| debug_return_bool(plugin->u.io->show_version(verbose)); |
debug_return_bool(plugin->u.io->show_version(verbose)); |
| } |
} |
| |
|
|
Line 1252 iolog_unlink(struct plugin_container *plugin)
|
Line 1242 iolog_unlink(struct plugin_container *plugin)
|
| deregister_hook); |
deregister_hook); |
| } |
} |
| /* Remove from io_plugins list and free. */ |
/* Remove from io_plugins list and free. */ |
| tq_remove(&io_plugins, plugin); | TAILQ_REMOVE(&io_plugins, plugin, entries); |
| efree(plugin); |
efree(plugin); |
| |
|
| debug_return; |
debug_return; |