|
version 1.1, 2012/02/21 16:23:02
|
version 1.1.1.5, 2013/10/14 07:56:35
|
|
Line 1
|
Line 1
|
| /* |
/* |
| * Copyright (c) 2009-2011 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> |
| #ifdef HAVE_SYS_SELECT_H |
|
| # include <sys/select.h> |
|
| #endif /* HAVE_SYS_SELECT_H */ |
|
| #include <sys/time.h> |
#include <sys/time.h> |
| #include <sys/resource.h> |
#include <sys/resource.h> |
| #include <stdio.h> |
#include <stdio.h> |
|
Line 61
|
Line 57
|
| #if TIME_WITH_SYS_TIME |
#if 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 |
| |
# define LOGIN_SETENV 0 |
| |
# endif |
| #endif |
#endif |
| #ifdef HAVE_PROJECT_H |
#ifdef HAVE_PROJECT_H |
| # include <project.h> |
# include <project.h> |
|
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 */ |
| #ifdef HAVE_PRIV_SET |
|
| # include <priv.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 |
|
Line 101
|
Line 94
|
| struct plugin_container policy_plugin; |
struct plugin_container policy_plugin; |
| struct plugin_container_list io_plugins; |
struct plugin_container_list 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 */ |
| int debug_level; | static struct command_details command_details; |
| | static int sudo_mode; |
| |
|
| /* |
/* |
| * Local functions |
* Local functions |
| */ |
*/ |
| static void fix_fds(void); |
static void fix_fds(void); |
| static void disable_coredumps(void); |
static void disable_coredumps(void); |
| |
static void sudo_check_suid(const char *path); |
| static char **get_user_info(struct user_details *); |
static char **get_user_info(struct user_details *); |
| static void command_info_to_details(char * const info[], |
static void command_info_to_details(char * const info[], |
| struct command_details *details); |
struct command_details *details); |
| static int policy_open(struct plugin_container *plugin, char * const settings[], |
|
| char * const user_info[], char * const user_env[]); |
|
| static void policy_close(struct plugin_container *plugin, int exit_status, |
|
| int error); |
|
| static int iolog_open(struct plugin_container *plugin, char * const 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); |
|
| |
|
| /* Policy plugin convenience functions. */ |
/* Policy plugin convenience functions. */ |
| static int policy_open(struct plugin_container *plugin, char * const settings[], |
static int policy_open(struct plugin_container *plugin, char * const settings[], |
|
Line 135 static int policy_list(struct plugin_container *plugin
|
Line 121 static int policy_list(struct plugin_container *plugin
|
| char * const argv[], int verbose, const char *list_user); |
char * const argv[], int verbose, const char *list_user); |
| static int policy_validate(struct plugin_container *plugin); |
static int policy_validate(struct plugin_container *plugin); |
| static void policy_invalidate(struct plugin_container *plugin, int remove); |
static void policy_invalidate(struct plugin_container *plugin, int remove); |
| static int policy_init_session(struct plugin_container *plugin, |
|
| struct passwd *pwd); |
|
| |
|
| /* I/O log plugin convenience functions. */ |
/* I/O log plugin convenience functions. */ |
| static int iolog_open(struct plugin_container *plugin, char * const settings[], |
static int iolog_open(struct plugin_container *plugin, char * const settings[], |
|
Line 145 static int iolog_open(struct plugin_container *plugin,
|
Line 129 static int iolog_open(struct plugin_container *plugin,
|
| static void iolog_close(struct plugin_container *plugin, int exit_status, |
static void iolog_close(struct plugin_container *plugin, int exit_status, |
| int error); |
int error); |
| static int iolog_show_version(struct plugin_container *plugin, int verbose); |
static int iolog_show_version(struct plugin_container *plugin, int verbose); |
| |
static void iolog_unlink(struct plugin_container *plugin); |
| |
|
| #if defined(RLIMIT_CORE) && !defined(SUDO_DEVEL) | #ifdef RLIMIT_CORE |
| static struct rlimit corelimit; |
static struct rlimit corelimit; |
| #endif /* RLIMIT_CORE && !SUDO_DEVEL */ | #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[]) |
| { |
{ |
| int nargc, sudo_mode, exitcode = 0; | int nargc, ok, exitcode = 0; |
| 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; |
| int ok; | debug_decl(main, SUDO_DEBUG_MAIN) |
| #if defined(SUDO_DEVEL) && defined(__OpenBSD__) | |
| extern char *malloc_options; | |
| malloc_options = "AFGJPR"; | |
| #endif | |
| |
|
| #if !defined(HAVE_GETPROGNAME) && !defined(HAVE___PROGNAME) | os_init(argc, argv, envp); |
| 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 168 main(int argc, char *argv[], char *envp[])
|
| # endif |
# endif |
| #endif /* HAVE_GETPRPWNAM && HAVE_SET_AUTH_PARAMETERS */ |
#endif /* HAVE_GETPRPWNAM && HAVE_SET_AUTH_PARAMETERS */ |
| |
|
| if (geteuid() != 0) | /* Make sure we are setuid root. */ |
| errorx(1, _("must be setuid root")); | sudo_check_suid(argv[0]); |
| |
|
| /* Reset signal mask, disable core dumps 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); |
| disable_coredumps(); | 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); |
| |
|
| |
/* Disable core dumps if not enabled in sudo.conf. */ |
| |
disable_coredumps(); |
| |
|
| /* Parse command line arguments. */ |
/* Parse command line arguments. */ |
| sudo_mode = parse_args(argc, argv, &nargc, &nargv, &settings, &env_add); |
sudo_mode = parse_args(argc, argv, &nargc, &nargv, &settings, &env_add); |
| sudo_debug(9, "sudo_mode %d", sudo_mode); | sudo_debug_printf(SUDO_DEBUG_DEBUG, "sudo_mode %d", sudo_mode); |
| |
|
| /* Print sudo version early, in case of plugin init failure. */ |
/* Print sudo version early, in case of plugin init failure. */ |
| if (ISSET(sudo_mode, MODE_VERSION)) { |
if (ISSET(sudo_mode, MODE_VERSION)) { |
|
Line 211 main(int argc, char *argv[], char *envp[])
|
Line 198 main(int argc, char *argv[], char *envp[])
|
| (void) printf(_("Configure options: %s\n"), CONFIGURE_ARGS); |
(void) printf(_("Configure options: %s\n"), CONFIGURE_ARGS); |
| } |
} |
| |
|
| /* Read sudo.conf and load plugins. */ | /* Load plugins. */ |
| if (!sudo_load_plugins(_PATH_SUDO_CONF, &policy_plugin, &io_plugins)) | if (!sudo_load_plugins(&policy_plugin, &io_plugins)) |
| errorx(1, _("fatal error, unable to load plugins")); | fatalx(_("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); |
| if (ok != TRUE) { | if (ok != 1) { |
| if (ok == -2) |
if (ok == -2) |
| usage(1); |
usage(1); |
| else |
else |
| errorx(1, _("unable to initialize policy plugin")); | fatalx(_("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) { |
tq_foreach_fwd(&io_plugins, plugin) { |
| ok = iolog_open(plugin, settings, user_info, NULL, |
ok = iolog_open(plugin, settings, user_info, NULL, |
| nargc, nargv, envp); |
nargc, nargv, envp); |
| if (ok == TRUE) | if (ok != -1) |
| iolog_show_version(plugin, !user_details.uid); |
iolog_show_version(plugin, !user_details.uid); |
| } |
} |
| break; |
break; |
| case MODE_VALIDATE: |
case MODE_VALIDATE: |
| case MODE_VALIDATE|MODE_INVALIDATE: |
case MODE_VALIDATE|MODE_INVALIDATE: |
| ok = policy_validate(&policy_plugin); |
ok = policy_validate(&policy_plugin); |
| exit(ok != TRUE); | exit(ok != 1); |
| case MODE_KILL: |
case MODE_KILL: |
| case MODE_INVALIDATE: |
case MODE_INVALIDATE: |
| policy_invalidate(&policy_plugin, sudo_mode == MODE_KILL); |
policy_invalidate(&policy_plugin, sudo_mode == MODE_KILL); |
|
Line 249 main(int argc, char *argv[], char *envp[])
|
Line 238 main(int argc, char *argv[], char *envp[])
|
| case MODE_LIST|MODE_INVALIDATE: |
case MODE_LIST|MODE_INVALIDATE: |
| ok = policy_list(&policy_plugin, nargc, nargv, |
ok = policy_list(&policy_plugin, nargc, nargv, |
| ISSET(sudo_mode, MODE_LONG_LIST), list_user); |
ISSET(sudo_mode, MODE_LONG_LIST), list_user); |
| exit(ok != TRUE); | exit(ok != 1); |
| case MODE_EDIT: |
case MODE_EDIT: |
| case MODE_RUN: |
case MODE_RUN: |
| ok = policy_check(&policy_plugin, nargc, nargv, env_add, |
ok = policy_check(&policy_plugin, nargc, nargv, env_add, |
| &command_info, &argv_out, &user_env_out); |
&command_info, &argv_out, &user_env_out); |
| sudo_debug(8, "policy plugin returns %d", ok); | sudo_debug_printf(SUDO_DEBUG_INFO, "policy plugin returns %d", ok); |
| if (ok != TRUE) { | if (ok != 1) { |
| if (ok == -2) |
if (ok == -2) |
| usage(1); |
usage(1); |
| exit(1); /* plugin printed error message */ |
exit(1); /* plugin printed error message */ |
|
Line 266 main(int argc, char *argv[], char *envp[])
|
Line 255 main(int argc, char *argv[], char *envp[])
|
| 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) { |
| case TRUE: | case 1: |
| break; |
break; |
| case FALSE: | case 0: |
| /* I/O plugin asked to be disabled, remove from list. */ | /* I/O plugin asked to be disabled, remove and free. */ |
| tq_remove(&io_plugins, plugin); | iolog_unlink(plugin); |
| break; |
break; |
| case -2: |
case -2: |
| usage(1); |
usage(1); |
| break; |
break; |
| default: |
default: |
| errorx(1, _("error initializing I/O plugin %s"), | fatalx(_("error initializing I/O plugin %s"), |
| plugin->name); |
plugin->name); |
| } |
} |
| } |
} |
| |
/* Setup command details and run command/edit. */ |
| command_info_to_details(command_info, &command_details); |
command_info_to_details(command_info, &command_details); |
| command_details.argv = argv_out; |
command_details.argv = argv_out; |
| command_details.envp = user_env_out; |
command_details.envp = user_env_out; |
| 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. */ |
| |
if (setuid(ROOT_UID) == -1) |
| |
warning("setuid(%d)", ROOT_UID); |
| /* Restore coredumpsize resource limit before running. */ |
/* Restore coredumpsize resource limit before running. */ |
| #if defined(RLIMIT_CORE) && !defined(SUDO_DEVEL) | #ifdef RLIMIT_CORE |
| (void) setrlimit(RLIMIT_CORE, &corelimit); | if (sudo_conf_disable_coredump()) |
| #endif /* RLIMIT_CORE && !SUDO_DEVEL */ | (void) setrlimit(RLIMIT_CORE, &corelimit); |
| | #endif /* RLIMIT_CORE */ |
| if (ISSET(command_details.flags, CD_SUDOEDIT)) { |
if (ISSET(command_details.flags, CD_SUDOEDIT)) { |
| exitcode = sudo_edit(&command_details); |
exitcode = sudo_edit(&command_details); |
| } else { |
} else { |
|
Line 297 main(int argc, char *argv[], char *envp[])
|
Line 291 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(_("unexpected sudo mode 0x%x"), sudo_mode); |
| } |
} |
| |
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[]) |
| |
{ |
| |
#if !defined(HAVE_GETPROGNAME) && !defined(HAVE___PROGNAME) |
| |
if (argc > 0) |
| |
setprogname(argv[0]); |
| |
#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 310 static void
|
Line 315 static void
|
| fix_fds(void) |
fix_fds(void) |
| { |
{ |
| int miss[3], devnull = -1; |
int miss[3], devnull = -1; |
| |
debug_decl(fix_fds, SUDO_DEBUG_UTIL) |
| |
|
| /* |
/* |
| * stdin, stdout and stderr must be open; set them to /dev/null |
* stdin, stdout and stderr must be open; set them to /dev/null |
|
Line 320 fix_fds(void)
|
Line 326 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(_("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); |
| } |
} |
| |
debug_return; |
| } |
} |
| |
|
| /* |
/* |
| * 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) |
| |
|
| #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); |
| | } |
| } |
} |
| return rval; | debug_return_int(rval); |
| } |
} |
| |
|
| static char * |
static char * |
|
Line 368 get_user_groups(struct user_details *ud)
|
Line 382 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) |
| |
|
| /* | #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(_("unable to get group vector")); |
| } |
} |
| |
|
| /* |
/* |
|
Line 402 get_user_groups(struct user_details *ud)
|
Line 427 get_user_groups(struct user_details *ud)
|
| i ? "," : "", (unsigned int)ud->groups[i]); |
i ? "," : "", (unsigned int)ud->groups[i]); |
| cp += len; |
cp += len; |
| } |
} |
| return gid_list; | debug_return_str(gid_list); |
| } |
} |
| |
|
| /* |
/* |
|
Line 412 get_user_groups(struct user_details *ud)
|
Line 437 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 cwd[PATH_MAX]; | char *cp, **user_info, cwd[PATH_MAX], host[HOST_NAME_MAX + 1]; |
| char host[MAXHOSTNAMELEN]; | |
| char **user_info, *cp; | |
| struct passwd *pw; |
struct passwd *pw; |
| int i = 0; | int fd, i = 0; |
| | debug_decl(get_user_info, SUDO_DEBUG_UTIL) |
| |
|
| /* XXX - bound check number of entries */ |
/* XXX - bound check number of entries */ |
| user_info = emalloc2(32, sizeof(char *)); |
user_info = emalloc2(32, sizeof(char *)); |
| |
|
| |
ud->pid = getpid(); |
| |
ud->ppid = getppid(); |
| |
ud->pgid = getpgid(0); |
| |
ud->tcpgid = (pid_t)-1; |
| |
fd = open(_PATH_TTY, O_RDWR|O_NOCTTY|O_NONBLOCK, 0); |
| |
if (fd != -1) { |
| |
ud->tcpgid = tcgetpgrp(fd); |
| |
close(fd); |
| |
} |
| |
ud->sid = getsid(0); |
| |
|
| ud->uid = getuid(); |
ud->uid = getuid(); |
| ud->euid = geteuid(); |
ud->euid = geteuid(); |
| ud->gid = getgid(); |
ud->gid = getgid(); |
|
Line 428 get_user_info(struct user_details *ud)
|
Line 463 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(_("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 441 get_user_info(struct user_details *ud)
|
Line 476 get_user_info(struct user_details *ud)
|
| } |
} |
| ud->shell = estrdup(ud->shell); |
ud->shell = estrdup(ud->shell); |
| |
|
| |
easprintf(&user_info[++i], "pid=%d", (int)ud->pid); |
| |
easprintf(&user_info[++i], "ppid=%d", (int)ud->ppid); |
| |
easprintf(&user_info[++i], "pgid=%d", (int)ud->pgid); |
| |
easprintf(&user_info[++i], "tcpgid=%d", (int)ud->tcpgid); |
| |
easprintf(&user_info[++i], "sid=%d", (int)ud->sid); |
| |
|
| easprintf(&user_info[++i], "uid=%u", (unsigned int)ud->uid); |
easprintf(&user_info[++i], "uid=%u", (unsigned int)ud->uid); |
| easprintf(&user_info[++i], "euid=%u", (unsigned int)ud->euid); |
easprintf(&user_info[++i], "euid=%u", (unsigned int)ud->euid); |
| easprintf(&user_info[++i], "gid=%u", (unsigned int)ud->gid); |
easprintf(&user_info[++i], "gid=%u", (unsigned int)ud->gid); |
|
Line 452 get_user_info(struct user_details *ud)
|
Line 493 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 = ttyname(STDIN_FILENO)) || (cp = ttyname(STDOUT_FILENO)) || | if ((cp = get_process_ttyname()) != NULL) { |
| (cp = ttyname(STDERR_FILENO))) { | |
| 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); |
| } |
} |
| |
|
| if (gethostname(host, sizeof(host)) == 0) |
if (gethostname(host, sizeof(host)) == 0) |
|
Line 470 get_user_info(struct user_details *ud)
|
Line 511 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 479 get_user_info(struct user_details *ud)
|
Line 520 get_user_info(struct user_details *ud)
|
| |
|
| user_info[++i] = NULL; |
user_info[++i] = NULL; |
| |
|
| return user_info; | debug_return_ptr(user_info); |
| } |
} |
| |
|
| /* |
/* |
|
Line 489 static void
|
Line 530 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; |
| |
id_t id; |
| long lval; |
long lval; |
| unsigned long ulval; |
|
| char *cp, *ep; |
char *cp, *ep; |
| |
const char *errstr; |
| |
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; |
|
Line 502 command_info_to_details(char * const info[], struct co
|
Line 545 command_info_to_details(char * const info[], struct co
|
| break; \ |
break; \ |
| } |
} |
| |
|
| |
sudo_debug_printf(SUDO_DEBUG_INFO, "command info from plugin:"); |
| for (i = 0; info[i] != NULL; i++) { |
for (i = 0; info[i] != NULL; i++) { |
| sudo_debug(9, "command info: %s", info[i]); | sudo_debug_printf(SUDO_DEBUG_INFO, " %d: %s", i, info[i]); |
| switch (info[i][0]) { |
switch (info[i][0]) { |
| case 'c': |
case 'c': |
| SET_STRING("chroot=", chroot) |
SET_STRING("chroot=", chroot) |
| SET_STRING("command=", command) |
SET_STRING("command=", command) |
| 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; |
|
| if (*cp == '\0') |
|
| break; |
|
| errno = 0; |
errno = 0; |
| lval = strtol(cp, &ep, 0); | cp = info[i] + sizeof("closefrom=") - 1; |
| if (*cp != '\0' && *ep == '\0' && | lval = strtol(cp, &ep, 10); |
| !(errno == ERANGE && | if (*cp == '\0' || *ep != '\0') |
| (lval == LONG_MAX || lval == LONG_MIN)) && | fatalx(_("%s: %s"), info[i], _("invalid value")); |
| lval < INT_MAX && lval > INT_MIN) { | if ((errno == ERANGE && |
| details->closefrom = (int)lval; | (lval == LONG_MAX || lval == LONG_MIN)) || |
| } | (lval > INT_MAX || lval < 0)) |
| | fatalx(_("%s: %s"), info[i], _("value out of range")); |
| | 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; |
|
| if (*cp == '\0') |
|
| break; |
|
| errno = 0; |
errno = 0; |
| lval = strtol(cp, &ep, 0); | cp = info[i] + sizeof("nice=") - 1; |
| if (*cp != '\0' && *ep == '\0' && | lval = strtol(cp, &ep, 10); |
| !(errno == ERANGE && | if (*cp == '\0' || *ep != '\0') |
| (lval == LONG_MAX || lval == LONG_MIN)) && | fatalx(_("%s: %s"), info[i], _("invalid value")); |
| lval < INT_MAX && lval > INT_MIN) { | if ((errno == ERANGE && |
| details->priority = (int)lval; | (lval == LONG_MAX || lval == LONG_MIN)) || |
| SET(details->flags, CD_SET_PRIORITY); | (lval > INT_MAX || lval < INT_MIN)) |
| } | fatalx(_("%s: %s"), info[i], _("value out of range")); |
| | 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) { |
| if (atobool(info[i] + sizeof("noexec=") - 1) == TRUE) | if (atobool(info[i] + sizeof("noexec=") - 1) == true) |
| SET(details->flags, CD_NOEXEC); |
SET(details->flags, CD_NOEXEC); |
| break; |
break; |
| } |
} |
| #ifdef _PATH_SUDO_NOEXEC |
|
| /* XXX - deprecated */ |
|
| if (strncmp("noexec_file=", info[i], sizeof("noexec_file=") - 1) == 0) { |
|
| noexec_path = info[i] + sizeof("noexec_file=") - 1; |
|
| break; |
|
| } |
|
| #endif /* _PATH_SUDO_NOEXEC */ |
|
| break; |
break; |
| case 'p': |
case 'p': |
| if (strncmp("preserve_groups=", info[i], sizeof("preserve_groups=") - 1) == 0) { |
if (strncmp("preserve_groups=", info[i], sizeof("preserve_groups=") - 1) == 0) { |
| if (atobool(info[i] + sizeof("preserve_groups=") - 1) == TRUE) | if (atobool(info[i] + sizeof("preserve_groups=") - 1) == true) |
| SET(details->flags, CD_PRESERVE_GROUPS); |
SET(details->flags, CD_PRESERVE_GROUPS); |
| break; |
break; |
| } |
} |
|
Line 567 command_info_to_details(char * const info[], struct co
|
Line 608 command_info_to_details(char * const info[], struct co
|
| 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(_("%s: %s"), info[i], _(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(_("%s: %s"), info[i], _(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(_("%s: %s"), info[i], _(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(_("%s: %s"), info[i], _(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) |
| SET_STRING("selinux_type=", selinux_type) |
SET_STRING("selinux_type=", selinux_type) |
| if (strncmp("set_utmp=", info[i], sizeof("set_utmp=") - 1) == 0) { |
if (strncmp("set_utmp=", info[i], sizeof("set_utmp=") - 1) == 0) { |
| if (atobool(info[i] + sizeof("set_utmp=") - 1) == TRUE) | if (atobool(info[i] + sizeof("set_utmp=") - 1) == true) |
| SET(details->flags, CD_SET_UTMP); |
SET(details->flags, CD_SET_UTMP); |
| break; |
break; |
| } |
} |
| if (strncmp("sudoedit=", info[i], sizeof("sudoedit=") - 1) == 0) { |
if (strncmp("sudoedit=", info[i], sizeof("sudoedit=") - 1) == 0) { |
| if (atobool(info[i] + sizeof("sudoedit=") - 1) == TRUE) | if (atobool(info[i] + sizeof("sudoedit=") - 1) == true) |
| SET(details->flags, CD_SUDOEDIT); |
SET(details->flags, CD_SUDOEDIT); |
| break; |
break; |
| } |
} |
| break; |
break; |
| 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; |
|
| if (*cp == '\0') |
|
| break; |
|
| errno = 0; |
errno = 0; |
| lval = strtol(cp, &ep, 0); | cp = info[i] + sizeof("timeout=") - 1; |
| if (*cp != '\0' && *ep == '\0' && | lval = strtol(cp, &ep, 10); |
| !(errno == ERANGE && | if (*cp == '\0' || *ep != '\0') |
| (lval == LONG_MAX || lval == LONG_MIN)) && | fatalx(_("%s: %s"), info[i], _("invalid value")); |
| lval <= INT_MAX && lval >= 0) { | if ((errno == ERANGE && |
| details->timeout = (int)lval; | (lval == LONG_MAX || lval == LONG_MIN)) || |
| SET(details->flags, CD_SET_TIMEOUT); | (lval > INT_MAX || lval < 0)) |
| } | fatalx(_("%s: %s"), info[i], _("value out of range")); |
| | 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; |
|
| if (*cp == '\0') |
|
| break; |
|
| errno = 0; |
errno = 0; |
| ulval = strtoul(cp, &ep, 8); | cp = info[i] + sizeof("umask=") - 1; |
| if (*cp != '\0' && *ep == '\0' && | lval = strtol(cp, &ep, 8); |
| (errno != ERANGE || ulval != ULONG_MAX)) { | if (*cp == '\0' || *ep != '\0') |
| details->umask = (uid_t)ulval; | fatalx(_("%s: %s"), info[i], _("invalid value")); |
| SET(details->flags, CD_SET_UMASK); | if ((errno == ERANGE && |
| } | (lval == LONG_MAX || lval == LONG_MIN)) || |
| | (lval > 0777 || lval < 0)) |
| | fatalx(_("%s: %s"), info[i], _("value out of range")); |
| | details->umask = (mode_t)lval; |
| | 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) { |
| if (atobool(info[i] + sizeof("use_pty=") - 1) == TRUE) | if (atobool(info[i] + sizeof("use_pty=") - 1) == true) |
| SET(details->flags, CD_USE_PTY); |
SET(details->flags, CD_USE_PTY); |
| break; |
break; |
| } |
} |
|
Line 707 command_info_to_details(char * const info[], struct co
|
Line 729 command_info_to_details(char * const info[], struct co
|
| if (!ISSET(details->flags, CD_SET_EUID)) |
if (!ISSET(details->flags, CD_SET_EUID)) |
| details->euid = details->uid; |
details->euid = details->uid; |
| |
|
| |
#ifdef HAVE_SETAUTHDB |
| |
aix_setauthdb(IDtouser(details->euid)); |
| |
#endif |
| |
details->pw = getpwuid(details->euid); |
| |
if (details->pw != NULL && (details->pw = pw_dup(details->pw)) == NULL) |
| |
fatal(NULL); |
| |
#ifdef HAVE_SETAUTHDB |
| |
aix_restoreauthdb(); |
| |
#endif |
| |
|
| #ifdef HAVE_SELINUX |
#ifdef HAVE_SELINUX |
| if (details->selinux_role != NULL && is_selinux_enabled() > 0) |
if (details->selinux_role != NULL && is_selinux_enabled() > 0) |
| SET(details->flags, CD_RBAC_ENABLED); |
SET(details->flags, CD_RBAC_ENABLED); |
| #endif |
#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) { |
| |
/* 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 || 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. */ |
| |
if (sb.st_uid != ROOT_UID || !ISSET(sb.st_mode, S_ISUID)) { |
| |
fatalx( |
| |
_("%s must be owned by uid %d and have the setuid bit set"), |
| |
sudo, ROOT_UID); |
| |
} else { |
| |
fatalx(_("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 { |
| |
fatalx( |
| |
_("effective uid is not %d, is sudo installed setuid root?"), |
| |
ROOT_UID); |
| |
} |
| |
} |
| |
debug_return; |
| |
} |
| |
|
| /* |
/* |
| * Disable core dumps to avoid dropping a core with user password in it. |
* Disable core dumps to avoid dropping a core with user password in it. |
| * We will reset this limit before executing the command. |
* We will reset this limit before executing the command. |
|
Line 721 command_info_to_details(char * const info[], struct co
|
Line 809 command_info_to_details(char * const info[], struct co
|
| static void |
static void |
| disable_coredumps(void) |
disable_coredumps(void) |
| { |
{ |
| #if defined(__linux__) || (defined(RLIMIT_CORE) && !defined(SUDO_DEVEL)) | #if defined(RLIMIT_CORE) |
| struct rlimit rl; |
struct rlimit rl; |
| #endif | debug_decl(disable_coredumps, SUDO_DEBUG_UTIL) |
| |
|
| #if defined(__linux__) |
|
| /* |
/* |
| * Unlimit the number of processes since Linux's setuid() will | * Turn off core dumps? |
| * apply resource limits when changing uid and return EAGAIN if | |
| * nproc would be violated by the uid switch. | |
| */ |
*/ |
| |
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 /* RLIMIT_CORE */ |
| |
} |
| |
|
| |
/* |
| |
* 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 |
| |
unlimit_nproc(void) |
| |
{ |
| |
#ifdef __linux__ |
| |
struct rlimit rl; |
| |
debug_decl(unlimit_nproc, SUDO_DEBUG_UTIL) |
| |
|
| (void) getrlimit(RLIMIT_NPROC, &nproclimit); |
(void) getrlimit(RLIMIT_NPROC, &nproclimit); |
| rl.rlim_cur = rl.rlim_max = RLIM_INFINITY; |
rl.rlim_cur = rl.rlim_max = RLIM_INFINITY; |
| if (setrlimit(RLIMIT_NPROC, &rl)) { | if (setrlimit(RLIMIT_NPROC, &rl) != 0) { |
| memcpy(&rl, &nproclimit, sizeof(struct rlimit)); |
memcpy(&rl, &nproclimit, sizeof(struct rlimit)); |
| rl.rlim_cur = rl.rlim_max; |
rl.rlim_cur = rl.rlim_max; |
| (void)setrlimit(RLIMIT_NPROC, &rl); |
(void)setrlimit(RLIMIT_NPROC, &rl); |
| } |
} |
| |
debug_return; |
| #endif /* __linux__ */ |
#endif /* __linux__ */ |
| #if defined(RLIMIT_CORE) && !defined(SUDO_DEVEL) |
|
| /* |
|
| * Turn off core dumps. |
|
| */ |
|
| (void) getrlimit(RLIMIT_CORE, &corelimit); |
|
| memcpy(&rl, &corelimit, sizeof(struct rlimit)); |
|
| rl.rlim_cur = 0; |
|
| (void) setrlimit(RLIMIT_CORE, &rl); |
|
| #endif /* RLIMIT_CORE && !SUDO_DEVEL */ |
|
| } |
} |
| |
|
| #ifdef HAVE_PROJECT_H |
|
| static void |
|
| set_project(struct passwd *pw) |
|
| { |
|
| struct project proj; |
|
| char buf[PROJECT_BUFSZ]; |
|
| int errval; |
|
| |
|
| /* |
|
| * Collect the default project for the user and settaskid |
|
| */ |
|
| setprojent(); |
|
| if (getdefaultproj(pw->pw_name, &proj, buf, sizeof(buf)) != NULL) { |
|
| errval = setproject(proj.pj_name, pw->pw_name, TASK_NORMAL); |
|
| 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(); |
|
| } |
|
| #endif /* HAVE_PROJECT_H */ |
|
| |
|
| /* |
/* |
| * Disable execution of child processes in the command we are about | * Restore saved value of RLIMIT_NPROC. |
| * to run. On systems with privilege sets, we can remove the exec | |
| * privilege. On other systems we use LD_PRELOAD and the like. | |
| */ |
*/ |
| static void |
static void |
| disable_execute(struct command_details *details) | restore_nproc(void) |
| { |
{ |
| #ifdef _PATH_SUDO_NOEXEC | #ifdef __linux__ |
| char *cp, **ev, **nenvp; | debug_decl(restore_nproc, SUDO_DEBUG_UTIL) |
| int env_len = 0, env_size = 128; | |
| #endif /* _PATH_SUDO_NOEXEC */ | |
| |
|
| #ifdef HAVE_PRIV_SET | (void) setrlimit(RLIMIT_NPROC, &nproclimit); |
| /* Solaris privileges, remove PRIV_PROC_EXEC post-execve. */ | |
| if (priv_set(PRIV_OFF, PRIV_LIMIT, "PRIV_PROC_EXEC", NULL) == 0) | |
| return; | |
| warning(_("unable to remove PRIV_PROC_EXEC from PRIV_LIMIT")); | |
| #endif /* HAVE_PRIV_SET */ | |
| |
|
| #ifdef _PATH_SUDO_NOEXEC | debug_return; |
| nenvp = emalloc2(env_size, sizeof(char *)); | #endif /* __linux__ */ |
| for (ev = details->envp; *ev != NULL; ev++) { | |
| if (env_len + 2 > env_size) { | |
| env_size += 128; | |
| nenvp = erealloc3(nenvp, env_size, sizeof(char *)); | |
| } | |
| /* | |
| * Prune out existing preloaded libraries. | |
| * XXX - should save and append instead of replacing. | |
| */ | |
| # if defined(__darwin__) || defined(__APPLE__) | |
| if (strncmp(*ev, "DYLD_INSERT_LIBRARIES=", sizeof("DYLD_INSERT_LIBRARIES=") - 1) == 0) | |
| continue; | |
| if (strncmp(*ev, "DYLD_FORCE_FLAT_NAMESPACE=", sizeof("DYLD_INSERT_LIBRARIES=") - 1) == 0) | |
| continue; | |
| # elif defined(__osf__) || defined(__sgi) | |
| if (strncmp(*ev, "_RLD_LIST=", sizeof("_RLD_LIST=") - 1) == 0) | |
| continue; | |
| # elif defined(_AIX) | |
| if (strncmp(*ev, "LDR_PRELOAD=", sizeof("LDR_PRELOAD=") - 1) == 0) | |
| continue; | |
| # else | |
| if (strncmp(*ev, "LD_PRELOAD=", sizeof("LD_PRELOAD=") - 1) == 0) | |
| continue; | |
| # endif | |
| nenvp[env_len++] = *ev; | |
| } | |
| |
| /* | |
| * Preload a noexec file? For a list of LD_PRELOAD-alikes, see | |
| * http://www.fortran-2000.com/ArnaudRecipes/sharedlib.html | |
| * XXX - need to support 32-bit and 64-bit variants | |
| */ | |
| # if defined(__darwin__) || defined(__APPLE__) | |
| nenvp[env_len++] = "DYLD_FORCE_FLAT_NAMESPACE="; | |
| cp = fmt_string("DYLD_INSERT_LIBRARIES", noexec_path); | |
| # elif defined(__osf__) || defined(__sgi) | |
| easprintf(&cp, "_RLD_LIST=%s:DEFAULT", noexec_path); | |
| # elif defined(_AIX) | |
| cp = fmt_string("LDR_PRELOAD", noexec_path); | |
| # else | |
| cp = fmt_string("LD_PRELOAD", noexec_path); | |
| # endif | |
| if (cp == NULL) | |
| error(1, NULL); | |
| nenvp[env_len++] = cp; | |
| nenvp[env_len] = NULL; | |
| |
| details->envp = nenvp; | |
| #endif /* _PATH_SUDO_NOEXEC */ | |
| } |
} |
| |
|
| /* |
/* |
| * 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. |
| */ |
*/ |
| int | bool |
| exec_setup(struct command_details *details, const char *ptyname, int ptyfd) |
exec_setup(struct command_details *details, const char *ptyname, int ptyfd) |
| { |
{ |
| int rval = FALSE; | bool rval = false; |
| struct passwd *pw; | debug_decl(exec_setup, SUDO_DEBUG_EXEC) |
| |
|
| #ifdef HAVE_SETAUTHDB |
|
| aix_setauthdb(IDtouser(details->euid)); |
|
| #endif |
|
| pw = getpwuid(details->euid); |
|
| #ifdef HAVE_SETAUTHDB |
|
| aix_restoreauthdb(); |
|
| #endif |
|
| |
|
| /* |
|
| * Call policy plugin's session init before other setup occurs. |
|
| * The session init code is expected to print an error as needed. |
|
| */ |
|
| if (policy_init_session(&policy_plugin, pw) != TRUE) |
|
| goto done; |
|
| |
|
| #ifdef HAVE_SELINUX |
#ifdef HAVE_SELINUX |
| if (ISSET(details->flags, CD_RBAC_ENABLED)) { |
if (ISSET(details->flags, CD_RBAC_ENABLED)) { |
| if (selinux_setup(details->selinux_role, details->selinux_type, |
if (selinux_setup(details->selinux_role, details->selinux_type, |
|
Line 918 exec_setup(struct command_details *details, const char
|
Line 882 exec_setup(struct command_details *details, const char
|
| } |
} |
| #endif |
#endif |
| |
|
| if (pw != NULL) { | if (details->pw != NULL) { |
| #ifdef HAVE_PROJECT_H |
#ifdef HAVE_PROJECT_H |
| set_project(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(pw->pw_name, ptyname ? ptyname : user_details.tty); | aix_prep_user(details->pw->pw_name, ptyname ? ptyname : user_details.tty); |
| #endif |
#endif |
| #ifdef HAVE_LOGIN_CAP_H |
#ifdef HAVE_LOGIN_CAP_H |
| if (details->login_class) { |
if (details->login_class) { |
|
Line 931 exec_setup(struct command_details *details, const char
|
Line 915 exec_setup(struct command_details *details, const char
|
| login_cap_t *lc; |
login_cap_t *lc; |
| |
|
| /* |
/* |
| * We only use setusercontext() to set the nice value and rlimits. | * We only use setusercontext() to set the nice value and rlimits |
| | * unless this is a login shell (sudo -i). |
| */ |
*/ |
| lc = login_getclass((char *)details->login_class); |
lc = login_getclass((char *)details->login_class); |
| if (!lc) { |
if (!lc) { |
|
Line 939 exec_setup(struct command_details *details, const char
|
Line 924 exec_setup(struct command_details *details, const char
|
| errno = ENOENT; |
errno = ENOENT; |
| goto done; |
goto done; |
| } |
} |
| flags = LOGIN_SETRESOURCES|LOGIN_SETPRIORITY; | if (ISSET(sudo_mode, MODE_LOGIN_SHELL)) { |
| if (setusercontext(lc, pw, pw->pw_uid, flags)) { | /* Set everything except user, group and login name. */ |
| if (pw->pw_uid != ROOT_UID) { | flags = LOGIN_SETALL; |
| warning(_("unable to set user context")); | CLR(flags, LOGIN_SETGROUP|LOGIN_SETLOGIN|LOGIN_SETUSER|LOGIN_SETENV|LOGIN_SETPATH); |
| | CLR(details->flags, CD_SET_UMASK); /* LOGIN_UMASK instead */ |
| | } else { |
| | flags = LOGIN_SETRESOURCES|LOGIN_SETPRIORITY; |
| | } |
| | if (setusercontext(lc, details->pw, details->pw->pw_uid, flags)) { |
| | 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 954 exec_setup(struct command_details *details, const char
|
Line 944 exec_setup(struct command_details *details, const char
|
| /* |
/* |
| * Set groups, including supplementary group vector. |
* Set groups, including supplementary group vector. |
| */ |
*/ |
| |
if (!ISSET(details->flags, CD_PRESERVE_GROUPS)) { |
| |
if (details->ngroups >= 0) { |
| |
if (sudo_setgroups(details->ngroups, details->groups) < 0) { |
| |
warning(_("unable to set supplementary group IDs")); |
| |
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(_("unable to set effective gid to runas gid %u"), |
|
Line 967 exec_setup(struct command_details *details, const char
|
Line 965 exec_setup(struct command_details *details, const char
|
| goto done; |
goto done; |
| } |
} |
| |
|
| if (!ISSET(details->flags, CD_PRESERVE_GROUPS)) { |
|
| if (details->ngroups >= 0) { |
|
| if (sudo_setgroups(details->ngroups, details->groups) < 0) { |
|
| warning(_("unable to set supplementary group IDs")); |
|
| 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(_("unable to set process priority")); |
|
Line 991 exec_setup(struct command_details *details, const char
|
Line 980 exec_setup(struct command_details *details, const char
|
| } |
} |
| } |
} |
| |
|
| if (ISSET(details->flags, CD_NOEXEC)) | /* |
| disable_execute(details); | * 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) { |
|
Line 1014 exec_setup(struct command_details *details, const char
|
Line 1006 exec_setup(struct command_details *details, const char
|
| } |
} |
| #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 1028 exec_setup(struct command_details *details, const char
|
Line 1023 exec_setup(struct command_details *details, const char
|
| } |
} |
| } |
} |
| |
|
| /* | rval = true; |
| * 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; |
|
| |
|
| done: |
done: |
| return rval; | debug_return_bool(rval); |
| } |
} |
| |
|
| /* |
/* |
|
Line 1058 run_command(struct command_details *details)
|
Line 1038 run_command(struct command_details *details)
|
| struct plugin_container *plugin; |
struct plugin_container *plugin; |
| struct command_status cstat; |
struct command_status cstat; |
| int exitcode = 1; |
int exitcode = 1; |
| |
debug_decl(run_command, SUDO_DEBUG_EXEC) |
| |
|
| cstat.type = CMD_INVALID; |
cstat.type = CMD_INVALID; |
| cstat.val = 0; |
cstat.val = 0; |
| |
|
| sudo_execve(details, &cstat); | sudo_execute(details, &cstat); |
| |
|
| switch (cstat.type) { |
switch (cstat.type) { |
| case CMD_ERRNO: |
case CMD_ERRNO: |
| /* exec_setup() or execve() returned an error. */ |
/* exec_setup() or execve() returned an error. */ |
| sudo_debug(9, "calling policy close with errno"); | sudo_debug_printf(SUDO_DEBUG_DEBUG, |
| | "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) { |
tq_foreach_fwd(&io_plugins, plugin) { |
| sudo_debug(9, "calling I/O close with errno"); | sudo_debug_printf(SUDO_DEBUG_DEBUG, |
| | "calling I/O close with errno %d", cstat.val); |
| iolog_close(plugin, 0, cstat.val); |
iolog_close(plugin, 0, cstat.val); |
| } |
} |
| exitcode = 1; |
exitcode = 1; |
| break; |
break; |
| case CMD_WSTATUS: |
case CMD_WSTATUS: |
| /* Command ran, exited or was killed. */ |
/* Command ran, exited or was killed. */ |
| sudo_debug(9, "calling policy close with wait status"); | sudo_debug_printf(SUDO_DEBUG_DEBUG, |
| | "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) { |
tq_foreach_fwd(&io_plugins, plugin) { |
| sudo_debug(9, "calling I/O close with wait status"); | sudo_debug_printf(SUDO_DEBUG_DEBUG, |
| | "calling I/O close with wait status %d", cstat.val); |
| iolog_close(plugin, cstat.val, 0); |
iolog_close(plugin, cstat.val, 0); |
| } |
} |
| if (WIFEXITED(cstat.val)) |
if (WIFEXITED(cstat.val)) |
|
Line 1092 run_command(struct command_details *details)
|
Line 1077 run_command(struct command_details *details)
|
| warningx(_("unexpected child termination condition: %d"), cstat.type); |
warningx(_("unexpected child termination condition: %d"), cstat.type); |
| break; |
break; |
| } |
} |
| return exitcode; | debug_return_int(exitcode); |
| } |
} |
| |
|
| static int |
static int |
| policy_open(struct plugin_container *plugin, char * const settings[], |
policy_open(struct plugin_container *plugin, char * const settings[], |
| char * const user_info[], char * const user_env[]) |
char * const user_info[], char * const user_env[]) |
| { |
{ |
| return plugin->u.policy->open(SUDO_API_VERSION, sudo_conversation, | int rval; |
| _sudo_printf, settings, user_info, user_env); | debug_decl(policy_open, SUDO_DEBUG_PCOMM) |
| | |
| | /* |
| | * Backwards compatibility for older API versions |
| | */ |
| | 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, _sudo_printf, settings, user_info, user_env); |
| | break; |
| | default: |
| | rval = plugin->u.policy->open(SUDO_API_VERSION, sudo_conversation, |
| | _sudo_printf, settings, user_info, user_env, plugin->options); |
| | } |
| | |
| | debug_return_bool(rval); |
| } |
} |
| |
|
| static void |
static void |
| policy_close(struct plugin_container *plugin, int exit_status, int error) |
policy_close(struct plugin_container *plugin, int exit_status, int error) |
| { |
{ |
| plugin->u.policy->close(exit_status, error); | debug_decl(policy_close, SUDO_DEBUG_PCOMM) |
| | if (plugin->u.policy->close != NULL) |
| | plugin->u.policy->close(exit_status, error); |
| | else |
| | warning(_("unable to execute %s"), command_details.command); |
| | debug_return; |
| } |
} |
| |
|
| static int |
static int |
| policy_show_version(struct plugin_container *plugin, int verbose) |
policy_show_version(struct plugin_container *plugin, int verbose) |
| { |
{ |
| return plugin->u.policy->show_version(verbose); | 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)); |
| } |
} |
| |
|
| static int |
static int |
|
Line 1120 policy_check(struct plugin_container *plugin, int argc
|
Line 1129 policy_check(struct plugin_container *plugin, int argc
|
| char *env_add[], char **command_info[], char **argv_out[], |
char *env_add[], char **command_info[], char **argv_out[], |
| char **user_env_out[]) |
char **user_env_out[]) |
| { |
{ |
| return plugin->u.policy->check_policy(argc, argv, env_add, command_info, | debug_decl(policy_check, SUDO_DEBUG_PCOMM) |
| argv_out, user_env_out); | if (plugin->u.policy->check_policy == NULL) { |
| | fatalx(_("policy plugin %s is missing the `check_policy' method"), |
| | plugin->name); |
| | } |
| | debug_return_bool(plugin->u.policy->check_policy(argc, argv, env_add, |
| | command_info, argv_out, user_env_out)); |
| } |
} |
| |
|
| static int |
static int |
| policy_list(struct plugin_container *plugin, int argc, char * const argv[], |
policy_list(struct plugin_container *plugin, int argc, char * const argv[], |
| int verbose, const char *list_user) |
int verbose, const char *list_user) |
| { |
{ |
| |
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(_("policy plugin %s does not support listing privileges"), |
| plugin->name); |
plugin->name); |
| return FALSE; | debug_return_bool(false); |
| } |
} |
| return plugin->u.policy->list(argc, argv, verbose, list_user); | debug_return_bool(plugin->u.policy->list(argc, argv, verbose, list_user)); |
| } |
} |
| |
|
| static int |
static int |
| policy_validate(struct plugin_container *plugin) |
policy_validate(struct plugin_container *plugin) |
| { |
{ |
| |
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(_("policy plugin %s does not support the -v option"), |
| plugin->name); |
plugin->name); |
| return FALSE; | debug_return_bool(false); |
| } |
} |
| return plugin->u.policy->validate(); | debug_return_bool(plugin->u.policy->validate()); |
| } |
} |
| |
|
| static void |
static void |
| policy_invalidate(struct plugin_container *plugin, int remove) |
policy_invalidate(struct plugin_container *plugin, int remove) |
| { |
{ |
| |
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(_("policy plugin %s does not support the -k/-K options"), |
| plugin->name); |
plugin->name); |
| } |
} |
| plugin->u.policy->invalidate(remove); |
plugin->u.policy->invalidate(remove); |
| |
debug_return; |
| } |
} |
| |
|
| static int | int |
| policy_init_session(struct plugin_container *plugin, struct passwd *pwd) | policy_init_session(struct command_details *details) |
| { |
{ |
| if (plugin->u.policy->init_session) | int rval = true; |
| return plugin->u.policy->init_session(pwd); | debug_decl(policy_init_session, SUDO_DEBUG_PCOMM) |
| return TRUE; | |
| | if (policy_plugin.u.policy->init_session) { |
| | /* |
| | * Backwards compatibility for older API versions |
| | */ |
| | 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); |
| | } |
| | } |
| | debug_return_bool(rval); |
| } |
} |
| |
|
| static int |
static int |
|
Line 1171 iolog_open(struct plugin_container *plugin, char * con
|
Line 1204 iolog_open(struct plugin_container *plugin, char * con
|
| int argc, char * const argv[], char * const user_env[]) |
int argc, char * const argv[], char * const user_env[]) |
| { |
{ |
| int rval; |
int rval; |
| |
debug_decl(iolog_open, SUDO_DEBUG_PCOMM) |
| |
|
| /* |
/* |
| * Backwards compatibility for API major 1, minor 0 | * Backwards compatibility for older API versions |
| */ |
*/ |
| switch (plugin->u.generic->version) { |
switch (plugin->u.generic->version) { |
| case SUDO_API_MKVERSION(1, 0): |
case SUDO_API_MKVERSION(1, 0): |
|
Line 1181 iolog_open(struct plugin_container *plugin, char * con
|
Line 1215 iolog_open(struct plugin_container *plugin, char * con
|
| sudo_conversation, _sudo_printf, settings, user_info, argc, argv, |
sudo_conversation, _sudo_printf, settings, user_info, argc, argv, |
| user_env); |
user_env); |
| break; |
break; |
| |
case SUDO_API_MKVERSION(1, 1): |
| |
rval = plugin->u.io_1_1->open(plugin->u.io_1_1->version, |
| |
sudo_conversation, _sudo_printf, settings, user_info, |
| |
command_info, argc, argv, user_env); |
| |
break; |
| default: |
default: |
| rval = plugin->u.io->open(SUDO_API_VERSION, sudo_conversation, |
rval = plugin->u.io->open(SUDO_API_VERSION, sudo_conversation, |
| _sudo_printf, settings, user_info, command_info, argc, argv, | _sudo_printf, settings, user_info, command_info, |
| user_env); | argc, argv, user_env, plugin->options); |
| } |
} |
| return rval; | debug_return_bool(rval); |
| } |
} |
| |
|
| static void |
static void |
| iolog_close(struct plugin_container *plugin, int exit_status, int error) |
iolog_close(struct plugin_container *plugin, int exit_status, int error) |
| { |
{ |
| plugin->u.io->close(exit_status, error); | debug_decl(iolog_close, SUDO_DEBUG_PCOMM) |
| | if (plugin->u.io->close != NULL) |
| | plugin->u.io->close(exit_status, error); |
| | debug_return; |
| } |
} |
| |
|
| static int |
static int |
| iolog_show_version(struct plugin_container *plugin, int verbose) |
iolog_show_version(struct plugin_container *plugin, int verbose) |
| { |
{ |
| return plugin->u.io->show_version(verbose); | 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)); |
| } |
} |
| |
|
| /* |
/* |
| * Simple debugging/logging. | * Remove the specified I/O logging plugin from the io_plugins list. |
| | * Deregisters any hooks before unlinking, then frees the container. |
| */ |
*/ |
| void | static void |
| sudo_debug(int level, const char *fmt, ...) | iolog_unlink(struct plugin_container *plugin) |
| { |
{ |
| va_list ap; | debug_decl(iolog_unlink, SUDO_DEBUG_PCOMM) |
| char *buf; | |
| |
|
| if (level > debug_level) | /* Deregister hooks, if any. */ |
| return; | if (plugin->u.io->version >= SUDO_API_MKVERSION(1, 2)) { |
| | if (plugin->u.io->deregister_hooks != NULL) |
| | plugin->u.io->deregister_hooks(SUDO_HOOK_VERSION, |
| | deregister_hook); |
| | } |
| | /* Remove from io_plugins list and free. */ |
| | tq_remove(&io_plugins, plugin); |
| | efree(plugin); |
| |
|
| /* Bracket fmt with program name and a newline to make it a single write */ | debug_return; |
| va_start(ap, fmt); | |
| evasprintf(&buf, fmt, ap); | |
| va_end(ap); | |
| fprintf(stderr, "%s: %s\n", getprogname(), buf); | |
| efree(buf); | |
| } |
} |