From 7fe99a8caab4f570b4c163f200b920f187f08922 Mon Sep 17 00:00:00 2001 From: "Todd C. Miller" Date: Thu, 22 Jul 1999 12:55:17 +0000 Subject: [PATCH] o no longer contains sudo 1.1/1.2 code o BSD copyright o use new logging routines o simplified flow of control o BIND resolver additions to badenv_table --- sudo.c | 906 +++++++++++++++++++++++++-------------------------------- 1 file changed, 403 insertions(+), 503 deletions(-) diff --git a/sudo.c b/sudo.c index a342d02e1..8683ff7b9 100644 --- a/sudo.c +++ b/sudo.c @@ -1,50 +1,34 @@ /* - * CU sudo version 1.6 -- allows users to execute commands as root and others - * Copyright (c) 1991 The Root Group, Inc. - * Copyright (c) 1994,1996,1998,1999 Todd C. Miller + * Copyright (c) 1994,1996,1998,1999 Todd C. Miller + * All rights reserved. * - * Please send bugs, changes, problems to sudo-bugs@courtesan.com + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 1, or (at your option) - * any later version. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - * - ************************************************************************** - * - * sudo.c - * - * This is the main() routine for sudo - * - * sudo is a program to allow users to execute commands - * as root. The commands are defined in a global network- - * wide file and can be distributed. - * - * sudo has been hacked far and wide. Too many people to - * know about. It's about time to come up with a secure - * version that will work well in a network. - * - * This most recent version is done by: - * - * Jeff Nieusma - * Dave Hieb - * - * However, due to the fact that both of the above are no longer - * working at Root Group, I am maintaining the "CU version" of - * sudo. - * Todd Miller + * For a brief history of sudo, please see the HISTORY file included + * with this distribution. */ -#define MAIN +#define _SUDO_SUDO_C #include "config.h" @@ -64,18 +48,13 @@ #include #include #include +#include #include #include #include #include #include #include -#ifdef HAVE_DCE -#include -#endif /* HAVE_DCE */ -#ifdef HAVE_KERB5 -#include -#endif /* HAVE_KERB5 */ #include "sudo.h" #include "interfaces.h" @@ -89,7 +68,6 @@ extern char *getenv __P((char *)); static const char rcsid[] = "$Sudo$"; #endif /* lint */ - /* * Local type declarations */ @@ -98,16 +76,14 @@ struct env_table { int len; }; - /* * Prototypes */ static int parse_args __P((void)); static void usage __P((int)); static void usage_excl __P((int)); -static void load_globals __P((int)); -static int check_sudoers __P((void)); -static int load_cmnd __P((int)); +static void check_sudoers __P((void)); +static int init_vars __P((int)); static void add_env __P((int)); static void clean_env __P((char **, struct env_table *)); extern int user_is_exempt __P((void)); @@ -121,33 +97,21 @@ int Argc; char **Argv; int NewArgc = 0; char **NewArgv = NULL; -struct passwd *user_pw_ent; -char *runas_user = RUNAS_DEFAULT; -char *cmnd = NULL; -char *cmnd_safe = NULL; -char *cmnd_args = NULL; -char *tty = "unknown"; -char *prompt; -char host[MAXHOSTNAMELEN]; -char *shost; -char cwd[MAXPATHLEN]; +struct sudo_user sudo_user; FILE *sudoers_fp = NULL; -static char *runas_homedir = NULL; +static char *runas_homedir = NULL; /* XXX */ struct interface *interfaces; int num_interfaces; -extern int printmatches; -int arg_prompt = 0; /* was -p used? */ -#ifdef HAVE_KERB5 -krb5_context sudo_context = NULL; -char *realm = NULL; -int xrealm = 0; -#endif /* HAVE_KERB5 */ +extern int errorlineno; /* * Table of "bad" envariables to remove and len for strncmp() */ -struct env_table badenv_table[] = { +static struct env_table badenv_table[] = { { "IFS=", 4 }, + { "LOCALDOMAIN=", 12 }, + { "RES_OPTIONS=", 12 }, + { "HOSTALIASES=", 12 }, { "LD_", 3 }, { "_RLD", 4 }, #ifdef __hpux @@ -168,28 +132,29 @@ struct env_table badenv_table[] = { }; -/******************************************************************** - * - * main() - * - * the driving force behind sudo... - */ - int main(argc, argv) int argc; char **argv; { - int rtn, serrno; - int cmnd_status = FOUND; - int sudo_mode = MODE_RUN; - extern char ** environ; + int validated; + int fd; + int cmnd_status; + int sudo_mode; +#ifdef POSIX_SIGNALS + sigset_t set, oset; +#else + int omask; +#endif /* POSIX_SIGNALS */ + extern char **environ; + extern int printmatches; + /* Must be done as the first thing... */ #if defined(HAVE_GETPRPWNAM) && defined(HAVE_SET_AUTH_PARAMETERS) (void) set_auth_parameters(argc, argv); -# ifdef HAVE_INITPRIVS +# ifdef HAVE_INITPRIVS initprivs(); -# endif +# endif #endif /* HAVE_GETPRPWNAM && HAVE_SET_AUTH_PARAMETERS */ Argv = argv; @@ -200,27 +165,50 @@ main(argc, argv) exit(1); } + /* Initialize syslog(3) if we are using it. */ +#if (LOGGING & SLOG_SYSLOG) +# ifdef Syslog_facility + openlog(Syslog_ident, Syslog_options, Syslog_facility); +# else + openlog(Syslog_ident, Syslog_options); +# endif /* Syslog_facility */ +#endif /* LOGGING & SLOG_SYSLOG */ + /* - * Close all file descriptors to make sure we have a nice - * clean slate from which to work. + * Block signals so the user cannot kill us at some point and + * avoid the logging. + * XXX - this list is not complete! */ -#ifdef HAVE_SYSCONF - for (rtn = sysconf(_SC_OPEN_MAX) - 1; rtn > 2; rtn--) - (void) close(rtn); +#ifdef POSIX_SIGNALS + (void) sigemptyset(&set); + (void) sigaddset(&set, SIGHUP); + (void) sigaddset(&set, SIGINT); + (void) sigaddset(&set, SIGQUIT); + (void) sigaddset(&set, SIGILL); + (void) sigaddset(&set, SIGTSTP); + (void) sigprocmask(SIG_BLOCK, &set, &oset); #else - for (rtn = getdtablesize() - 1; rtn > 2; rtn--) - (void) close(rtn); -#endif /* HAVE_SYSCONF */ + omask = sigblock(sigmask(SIGHUP)|sigmask(SIGINT)|sigmask(SIGQUIT)|sigmask(SIGILL)|sigmask(SIGTSTP)); +#endif /* POSIX_SIGNALS */ /* - * set the prompt based on $SUDO_PROMPT (can be overridden by `-p') + * Close any open fd's other than stdin, stdout and stderr. */ - if ((prompt = getenv("SUDO_PROMPT")) == NULL) - prompt = PASSPROMPT; +#ifdef HAVE_SYSCONF + for (fd = sysconf(_SC_OPEN_MAX) - 1; fd > 2; fd--) + (void) close(fd); +#else + for (fd = getdtablesize() - 1; fd > 2; fd--) + (void) close(fd); +#endif /* HAVE_SYSCONF */ /* - * parse our arguments + * Set the prompt based on $SUDO_PROMPT (can be overridden by `-p') */ + if ((user_prompt = getenv("SUDO_PROMPT")) == NULL) + user_prompt = PASSPROMPT; + + /* Parse our arguments. */ sudo_mode = parse_args(); switch (sudo_mode) { @@ -233,95 +221,60 @@ main(argc, argv) usage(0); break; case MODE_VALIDATE: - cmnd = "validate"; + user_cmnd = "validate"; break; case MODE_KILL: - cmnd = "kill"; + case MODE_INVALIDATE: + user_cmnd = "kill"; + break; + case MODE_SHELL: + user_cmnd = "shell"; break; case MODE_LIST: - cmnd = "list"; + user_cmnd = "list"; printmatches = 1; break; } - /* must have a command to run unless got -s */ - if (cmnd == NULL && NewArgc == 0 && !(sudo_mode & MODE_SHELL)) + /* Must have a command to run... */ + if (user_cmnd == NULL && NewArgc == 0) usage(1); clean_env(environ, badenv_table); - load_globals(sudo_mode); /* load global variables used throughout sudo */ + cmnd_status = init_vars(sudo_mode); - /* - * If we got the '-s' option (run shell) we need to redo NewArgv - * and NewArgc. This can only be done after load_globals(). - */ - if ((sudo_mode & MODE_SHELL)) { - char **dst, **src = NewArgv; - - NewArgv = (char **) emalloc (sizeof(char *) * (++NewArgc + 1)); - - /* add the shell as argv[0] */ - if (user_shell && *user_shell) { - NewArgv[0] = user_shell; - } else { - (void) fprintf(stderr, "%s: Unable to determine shell.", Argv[0]); - exit(1); - } + set_perms(PERM_USER, sudo_mode); - /* copy the args from Argv */ - for (dst = NewArgv + 1; (*dst = *src) != NULL; ++src, ++dst) - ; - } + check_sudoers(); /* check mode/owner on _PATH_SUDO_SUDOERS */ - rtn = check_sudoers(); /* check mode/owner on _PATH_SUDO_SUDOERS */ - if (rtn != ALL_SYSTEMS_GO) { - serrno = errno; - log_error(rtn); - set_perms(PERM_FULL_USER, sudo_mode); - errno = serrno; - inform_user(rtn); - exit(1); - } - -#ifdef SECURE_PATH - /* replace the PATH envariable with a secure one */ - if (!user_is_exempt() && sudo_setenv("PATH", SECURE_PATH)) { - (void) fprintf(stderr, "%s: cannot allocate memory!\n", Argv[0]); - exit(1); - } -#endif /* SECURE_PATH */ - - if ((sudo_mode & MODE_RUN)) { - cmnd_status = load_cmnd(sudo_mode); /* load the cmnd global variable */ - } else if (sudo_mode == MODE_KILL) { - remove_timestamp(); /* remove the timestamp ticket file */ + if (sudo_mode == MODE_KILL || sudo_mode == MODE_INVALIDATE) { + remove_timestamp((sudo_mode == MODE_KILL)); exit(0); } add_env(!(sudo_mode & MODE_SHELL)); /* add in SUDO_* envariables */ - /* validate the user but don't search for pseudo-commands */ - rtn = validate((sudo_mode != MODE_VALIDATE && sudo_mode != MODE_LIST)); - - switch (rtn) { + /* Validate the user but don't search for pseudo-commands. */ + validated = validate((sudo_mode != MODE_VALIDATE && sudo_mode != MODE_LIST)); + switch (validated) { case VALIDATE_OK: check_user(); /* fallthrough */ case VALIDATE_OK_NOPASS: - /* finally tell the user if the command did not exist */ + /* Finally tell the user if the command did not exist. */ if (cmnd_status == NOT_FOUND_DOT) { - (void) fprintf(stderr, "%s: ignoring `%s' found in '.'\nUse `sudo ./%s' if this is the `%s' you wish to run.\n", Argv[0], cmnd, cmnd, cmnd); + (void) fprintf(stderr, "%s: ignoring `%s' found in '.'\nUse `sudo ./%s' if this is the `%s' you wish to run.\n", Argv[0], user_cmnd, user_cmnd, user_cmnd); exit(1); } else if (cmnd_status == NOT_FOUND) { (void) fprintf(stderr, "%s: %s: command not found\n", Argv[0], - cmnd); + user_cmnd); exit(1); } - log_error(ALL_SYSTEMS_GO); + log_auth(validated, 1); if (sudo_mode == MODE_VALIDATE) exit(0); else if (sudo_mode == MODE_LIST) { @@ -329,34 +282,54 @@ main(argc, argv) exit(0); } - /* become specified user or root */ + /* Become specified user or root. */ set_perms(PERM_RUNAS, sudo_mode); - /* set $HOME for `sudo -H' */ + /* Set $HOME for `sudo -H' */ if ((sudo_mode & MODE_RESET_HOME) && runas_homedir) (void) sudo_setenv("HOME", runas_homedir); - /* this *must* have been set if we got a match but... */ - if (cmnd_safe == NULL) { - inform_user(NO_CMND_SAFE); - exit(1); + /* This *must* have been set if we got a match but... */ + if (safe_cmnd == NULL) { + log_error(MSG_ONLY, + "internal error, cmnd_safe never got set for %s; %s", + user_cmnd, + "please report this error to sudo-bugs@courtesan.com"); } + +#if (LOGGING & SLOG_SYSLOG) + closelog(); +#endif + + /* Reset signal mask. */ +#ifdef POSIX_SIGNALS + (void) sigprocmask(SIG_SETMASK, &oset, NULL); +#else + (void) sigsetmask(omask); +#endif /* POSIX_SIGNALS */ + #ifndef PROFILING if ((sudo_mode & MODE_BACKGROUND) && fork() > 0) exit(0); else - EXEC(cmnd_safe, NewArgv); /* run the command */ + EXEC(safe_cmnd, NewArgv); /* run the command */ #else exit(0); #endif /* PROFILING */ /* * If we got here then the exec() failed... */ - (void) fprintf(stderr, "%s: ", Argv[0]); - perror(cmnd); + (void) fprintf(stderr, "%s: unable to exec %s: %s\n", + Argv[0], safe_cmnd, strerror(errno)); exit(-1); break; + case VALIDATE_NO_USER: + check_user(); + log_auth(validated, 1); + exit(1); + break; + case VALIDATE_NOT_OK: check_user(); @@ -369,207 +342,200 @@ main(argc, argv) * their path to just contain a single dir. */ #ifndef DONT_LEAK_PATH_INFO - log_error(rtn); - if (cmnd_status == NOT_FOUND_DOT) - (void) fprintf(stderr, "%s: ignoring `%s' found in '.'\nUse `sudo ./%s' if this is the `%s' you wish to run.\n", Argv[0], cmnd, cmnd, cmnd); - else if (cmnd_status == NOT_FOUND) + log_auth(validated, + !(cmnd_status == NOT_FOUND_DOT || cmnd_status == NOT_FOUND)); + if (cmnd_status == NOT_FOUND) (void) fprintf(stderr, "%s: %s: command not found\n", Argv[0], - cmnd); - else - inform_user(rtn); + user_cmnd); + else if (cmnd_status == NOT_FOUND_DOT) + (void) fprintf(stderr, "%s: ignoring `%s' found in '.'\nUse `sudo ./%s' if this is the `%s' you wish to run.\n", Argv[0], user_cmnd, user_cmnd, user_cmnd); exit(1); break; #endif /* DONT_LEAK_PATH_INFO */ + case VALIDATE_ERROR: + log_error(0, "parse error in %s around line %d", _PATH_SUDO_SUDOERS, + errorlineno); + break; + default: - log_error(rtn); - inform_user(rtn); + log_auth(validated, 1); exit(1); break; } + exit(0); /* not reached */ } - - -/********************************************************************** - * - * load_globals() - * - * This function primes these important global variables: - * user_pw_ent, host, cwd, interfaces. +/* + * Initialize timezone, set umask, fill in ``sudo_user'' struct and + * load the ``interfaces'' array. */ - -static void -load_globals(sudo_mode) +static int +init_vars(sudo_mode) int sudo_mode; { - char *p; + char *p, thost[MAXHOSTNAMELEN]; #ifdef FQDN - struct hostent *h_ent; + struct hostent *hp; #endif /* FQDN */ -#ifdef HAVE_KERB5 - krb5_error_code retval; - char *lrealm; -#endif /* HAVE_KERB5 */ - -#ifdef HOST_IN_LOG - /* - * Logging routines may use shost so set to a dummy value for now. - */ - shost = strcpy(host, "localhost"); -#endif - /* - * Get a local copy of the user's struct passwd with the shadow password - * if necessary. It is assumed that euid is 0 at this point so we - * can read the shadow passwd file if necessary. - */ - if ((user_pw_ent = sudo_getpwuid(getuid())) == NULL) { - /* need to make a fake user_pw_ent */ - struct passwd pw; - char pw_name[MAX_UID_T_LEN + 1]; - - /* fill in uid and name fields with the uid */ - pw.pw_uid = getuid(); - (void) sprintf(pw_name, "%ld", (long) pw.pw_uid); - pw.pw_name = pw_name; - user_pw_ent = &pw; - - /* complain, log, and die */ - log_error(GLOBAL_NO_PW_ENT); - inform_user(GLOBAL_NO_PW_ENT); - exit(1); - } - -#ifdef HAVE_KERB5 - if (retval = krb5_init_context(&sudo_context)) { - log_error(GLOBAL_KRB5_INIT_ERR); - inform_user(GLOBAL_KRB5_INIT_ERR); +#ifdef NO_ROOT_SUDO + if (getuid() == 0) { + (void) fputs("You are already root, you don't need to use sudo.\n", + stderr); exit(1); } - krb5_init_ets(sudo_context); +#endif - if (retval = krb5_get_default_realm(sudo_context, &lrealm)) { - log_error(GLOBAL_KRB5_INIT_ERR); - inform_user(GLOBAL_KRB5_INIT_ERR); + /* Sanity check command from user. */ + if (user_cmnd == NULL && strlen(NewArgv[0]) >= MAXPATHLEN) { + (void) fprintf(stderr, "%s: %s: Pathname too long\n", Argv[0], + NewArgv[0]); exit(1); } - if (realm) { - if (strcmp(realm, lrealm) != 0) - xrealm = 1; /* User supplied realm is not the system default */ - free(lrealm); - } else - realm = lrealm; - - if (!arg_prompt) { - p = emalloc(strlen(user_name) + strlen(realm) + 17); - sprintf(p, "Password for %s@%s: ", user_name, realm); - prompt = p; - } -#endif /* HAVE_KERB5 */ - - /* Set euid == user and ruid == root */ - set_perms(PERM_ROOT, sudo_mode); - set_perms(PERM_USER, sudo_mode); - #ifdef HAVE_TZSET (void) tzset(); /* set the timezone if applicable */ #endif /* HAVE_TZSET */ - /* - * Need to get tty early since it's used for logging - */ - if ((p = (char *) ttyname(0)) || (p = (char *) ttyname(1))) { - if (strncmp(p, _PATH_DEV, sizeof(_PATH_DEV) - 1) == 0) - p += sizeof(_PATH_DEV) - 1; - tty = estrdup(p); +#ifdef SECURE_PATH + /* Replace the PATH envariable with a secure one. */ + if (!user_is_exempt() && sudo_setenv("PATH", SECURE_PATH)) { + (void) fprintf(stderr, "%s: cannot allocate memory!\n", Argv[0]); + exit(1); } +#endif /* SECURE_PATH */ #ifdef SUDO_UMASK (void) umask((mode_t)SUDO_UMASK); #endif /* SUDO_UMASK */ -#ifdef NO_ROOT_SUDO - if (user_uid == 0) { - (void) fprintf(stderr, - "You are already root, you don't need to use sudo.\n"); - exit(1); - } -#endif + /* Default values for runas and cmnd, overridden later. */ + user_runas = RUNAS_DEFAULT; + if (user_cmnd == NULL) + user_cmnd = NewArgv[0]; + (void) strcpy(user_cwd, "unknown"); /* - * so we know where we are... (do as user) + * We avoid gethostbyname() if possible since we don't want + * sudo to block if DNS or NIS is hosed. + * "host" is the (possibly fully-qualified) hostname and + * "shost" is the unqualified form of the hostname. */ - if (!getcwd(cwd, sizeof(cwd))) { - /* try as root... */ - set_perms(PERM_ROOT, sudo_mode); - if (!getcwd(cwd, sizeof(cwd))) { - (void) fprintf(stderr, "%s: Can't get working directory!\n", - Argv[0]); - (void) strcpy(cwd, "unknown"); - } - set_perms(PERM_USER, sudo_mode); + if ((gethostname(thost, sizeof(thost)))) { + user_host = "localhost"; + log_error(USE_ERRNO|MSG_ONLY, "can't get hostname"); + } else + user_host = estrdup(thost); +#ifdef FQDN + if (!(hp = gethostbyname(user_host))) + log_error(USE_ERRNO|MSG_ONLY|NO_EXIT, + "unable to lookup %s via gethostbyname()", user_host); + else + user_host = estrdup(hp->h_name); +#endif /* FQDN */ + if ((p = strchr(user_host, '.'))) { + *p = '\0'; + user_shost = estrdup(user_host); + *p = '.'; + } else { + user_shost = user_host; } + if ((p = ttyname(STDIN_FILENO)) || (p = ttyname(STDOUT_FILENO))) { + if (strncmp(p, _PATH_DEV, sizeof(_PATH_DEV) - 1) == 0) + p += sizeof(_PATH_DEV) - 1; + user_tty = estrdup(p); + } else + user_tty = "unknown"; + /* - * load the host global variable from gethostname() and use - * gethostbyname() if we want to be sure it is fully qualified. + * Get a local copy of the user's struct passwd with the shadow password + * if necessary. It is assumed that euid is 0 at this point so we + * can read the shadow passwd file if necessary. */ - if ((gethostname(host, sizeof(host)))) { - strcpy(host, "localhost"); - log_error(GLOBAL_NO_HOSTNAME); - inform_user(GLOBAL_NO_HOSTNAME); - exit(2); + if ((sudo_user.pw = sudo_getpwuid(getuid())) == NULL) { + /* Need to make a fake struct passwd for logging to work. */ + struct passwd pw; + char pw_name[MAX_UID_T_LEN + 1]; + + pw.pw_uid = getuid(); + (void) sprintf(pw_name, "%ld", (long) pw.pw_uid); + pw.pw_name = pw_name; + sudo_user.pw = &pw; + + log_error(0, "uid %ld does not exist in the passwd file!", + (long) pw.pw_uid); } -#ifdef FQDN - if ((h_ent = gethostbyname(host)) == NULL) - log_error(GLOBAL_HOST_UNREGISTERED); - else - strcpy(host, h_ent -> h_name); -#endif /* FQDN */ + + /* It is now safe to use log_error() and set_perms() */ /* - * "host" is the (possibly fully-qualified) hostname and - * "shost" is the unqualified form of the hostname. + * Get current working directory. Try as user, fall back to root. */ - if ((p = strchr(host, '.'))) { - *p = '\0'; - shost = estrdup(host); - *p = '.'; - } else { - shost = &host[0]; - } + set_perms(PERM_USER, sudo_mode); + if (!getcwd(user_cwd, sizeof(user_cwd))) { + set_perms(PERM_ROOT, sudo_mode); + if (!getcwd(user_cwd, sizeof(user_cwd))) { + (void) fprintf(stderr, "%s: Can't get working directory!\n", + Argv[0]); + (void) strcpy(user_cwd, "unknown"); + } + } else + set_perms(PERM_ROOT, sudo_mode); /* - * load a list of ip addresses and netmasks into + * Load the list of local ip addresses and netmasks into * the interfaces array. */ load_interfaces(); -} + /* + * If we were given the '-s' option (run shell) we need to redo + * NewArgv and NewArgc. + */ + if ((sudo_mode & MODE_SHELL)) { + char **dst, **src = NewArgv; + NewArgv = (char **) emalloc (sizeof(char *) * (++NewArgc + 1)); + if (user_shell && *user_shell) { + NewArgv[0] = user_shell; + } else { + (void) fprintf(stderr, "%s: Unable to determine shell.", Argv[0]); + exit(1); + } -/********************************************************************** - * - * parse_args() - * - * this function parses the arguments to sudo - */ + /* copy the args from Argv */ + for (dst = NewArgv + 1; (*dst = *src) != NULL; ++src, ++dst) + ; + } + /* Resolve the path and return. */ + if ((sudo_mode & MODE_RUN)) + return(find_path(NewArgv[0], &user_cmnd)); + else + return(FOUND); +} + +/* + * Command line argument parsing, can't use getopt(3). + */ static int parse_args() { - int ret = MODE_RUN; /* what mode is suod to be run in? */ + int rval = MODE_RUN; /* what mode is suod to be run in? */ int excl = 0; /* exclusive arg, no others allowed */ +#ifdef HAVE_KERB5 + extern char *realm; /* kerb5 realm (may be user-specified */ +#endif /* HAVE_KERB5 */ NewArgv = Argv + 1; NewArgc = Argc - 1; #ifdef SHELL_IF_NO_ARGS if (Argc < 2) { /* no options and no command */ - ret |= MODE_SHELL; - return(ret); + rval |= MODE_SHELL; + return(rval); } #else if (Argc < 2) /* no options and no command */ @@ -586,90 +552,95 @@ parse_args() switch (NewArgv[0][1]) { #ifdef HAVE_KERB5 case 'r': - /* must have an associated realm */ + /* Must have an associated realm. */ if (NewArgv[1] == NULL) usage(1); realm = NewArgv[1]; - /* shift Argv over and adjust Argc */ + /* Shift Argv over and adjust Argc. */ NewArgc--; NewArgv++; break; #endif /* HAVE_KERB5 */ case 'p': - /* must have an associated prompt */ + /* Must have an associated prompt. */ if (NewArgv[1] == NULL) usage(1); - prompt = NewArgv[1]; - arg_prompt = 1; + user_prompt = NewArgv[1]; - /* shift Argv over and adjust Argc */ + /* Shift Argv over and adjust Argc. */ NewArgc--; NewArgv++; break; case 'u': - /* must have an associated runas user */ + /* Must have an associated runas user. */ if (NewArgv[1] == NULL) usage(1); - runas_user = NewArgv[1]; + user_runas = NewArgv[1]; - /* shift Argv over and adjust Argc */ + /* Shift Argv over and adjust Argc. */ NewArgc--; NewArgv++; break; case 'b': - ret |= MODE_BACKGROUND; + rval |= MODE_BACKGROUND; break; case 'v': - ret = MODE_VALIDATE; + rval = MODE_VALIDATE; if (excl && excl != 'v') usage_excl(1); excl = 'v'; break; case 'k': - ret = MODE_KILL; + rval = MODE_INVALIDATE; if (excl && excl != 'k') usage_excl(1); excl = 'k'; break; + case 'K': + rval = MODE_KILL; + if (excl && excl != 'K') + usage_excl(1); + excl = 'K'; + break; case 'l': - ret = MODE_LIST; + rval = MODE_LIST; if (excl && excl != 'l') usage_excl(1); excl = 'l'; break; case 'V': - ret = MODE_VERSION; + rval = MODE_VERSION; if (excl && excl != 'V') usage_excl(1); excl = 'V'; break; case 'h': - ret = MODE_HELP; + rval = MODE_HELP; if (excl && excl != 'h') usage_excl(1); excl = 'h'; break; case 's': - ret |= MODE_SHELL; + rval |= MODE_SHELL; #ifdef SHELL_SETS_HOME - ret |= MODE_RESET_HOME; + rval |= MODE_RESET_HOME; #endif /* SHELL_SETS_HOME */ break; case 'H': - ret |= MODE_RESET_HOME; + rval |= MODE_RESET_HOME; break; case '-': NewArgc--; NewArgv++; #ifdef SHELL_IF_NO_ARGS - if (ret == MODE_RUN) - ret |= MODE_SHELL; + if (rval == MODE_RUN) + rval |= MODE_SHELL; #endif /* SHELL_IF_NO_ARGS */ - return(ret); + return(rval); case '\0': (void) fprintf(stderr, "%s: '-' requires an argument\n", Argv[0]); @@ -683,60 +654,16 @@ parse_args() NewArgv++; } - if (NewArgc > 0 && (ret == MODE_VALIDATE || ret == MODE_KILL || - ret == MODE_LIST)) + if (NewArgc > 0 && !(rval & MODE_RUN)) usage(1); - return(ret); + return(rval); } - - -/********************************************************************** - * - * usage_excl() - * - * Tell which options are mutually exclusive and exit - */ - -static void -usage_excl(exit_val) - int exit_val; -{ - (void) fprintf(stderr, "Only one of the -v, -k, -l, -V and -h options may be used\n"); - usage(exit_val); -} - -/********************************************************************** - * - * usage() - * - * this function just gives you instructions and exits - */ - -static void -usage(exit_val) - int exit_val; -{ - (void) fprintf(stderr, - "usage: %s -V | -h | -l | -v | -k | -H | [-b] [-p prompt] ", - Argv[0]); -#ifdef HAVE_KERB5 - (void) fprintf(stderr, "[-r realm] "); -#endif /* HAVE_KERB5 */ - (void) fprintf(stderr, "[-u username/#uid] -s | \n"); - exit(exit_val); -} - - - -/********************************************************************** - * - * add_env() - * - * this function adds sudo-specific variables into the environment +/* + * Add sudo-specific variables into the environment. + * Sets ``cmnd_args'' as a side effect. */ - static void add_env(contiguous) int contiguous; @@ -745,8 +672,8 @@ add_env(contiguous) size_t size; char *buf; - /* add the SUDO_COMMAND envariable (cmnd + args) */ - size = strlen(cmnd) + 1; + /* Add the SUDO_COMMAND envariable (cmnd + args). */ + size = strlen(user_cmnd) + 1; if (NewArgc > 1) { char *to, **from; @@ -761,17 +688,17 @@ add_env(contiguous) buf = (char *) emalloc(size); /* - * Copy the command and it's arguments info buf + * Copy the command and it's arguments info buf. */ - (void) strcpy(buf, cmnd); - to = buf + strlen(cmnd); + (void) strcpy(buf, user_cmnd); + to = buf + strlen(user_cmnd); for (from = &NewArgv[1]; *from; from++) { *to++ = ' '; (void) strcpy(to, *from); to += strlen(*from); } } else { - buf = cmnd; + buf = user_cmnd; } if (sudo_setenv("SUDO_COMMAND", buf)) { (void) fprintf(stderr, "%s: cannot allocate memory!\n", Argv[0]); @@ -780,35 +707,35 @@ add_env(contiguous) if (NewArgc > 1) free(buf); - /* grab a pointer to the flat arg string from the environment */ - if (NewArgc > 1 && (cmnd_args = getenv("SUDO_COMMAND"))) { - if ((cmnd_args = strchr(cmnd_args, ' '))) - cmnd_args++; + /* Grab a pointer to the flat arg string from the environment. */ + if (NewArgc > 1 && (user_args = getenv("SUDO_COMMAND"))) { + if ((user_args = strchr(user_args, ' '))) + user_args++; else - cmnd_args = NULL; + user_args = NULL; } - /* add the SUDO_USER envariable */ + /* Add the SUDO_USER environment variable. */ if (sudo_setenv("SUDO_USER", user_name)) { (void) fprintf(stderr, "%s: cannot allocate memory!\n", Argv[0]); exit(1); } - /* add the SUDO_UID envariable */ + /* Add the SUDO_UID environment variable. */ (void) sprintf(idstr, "%ld", (long) user_uid); if (sudo_setenv("SUDO_UID", idstr)) { (void) fprintf(stderr, "%s: cannot allocate memory!\n", Argv[0]); exit(1); } - /* add the SUDO_GID envariable */ + /* Add the SUDO_GID environment variable. */ (void) sprintf(idstr, "%ld", (long) user_gid); if (sudo_setenv("SUDO_GID", idstr)) { (void) fprintf(stderr, "%s: cannot allocate memory!\n", Argv[0]); exit(1); } - /* set PS1 if SUDO_PS1 is set */ + /* Set PS1 if SUDO_PS1 is set. */ if ((buf = getenv("SUDO_PS1"))) if (sudo_setenv("PS1", buf)) { (void) fprintf(stderr, "%s: cannot allocate memory!\n", Argv[0]); @@ -816,54 +743,16 @@ add_env(contiguous) } } - - -/********************************************************************** - * - * load_cmnd() - * - * This function sets the cmnd global variable - * Returns 1 on success, 0 on failure. - */ - -static int -load_cmnd(sudo_mode) - int sudo_mode; -{ - int retval; - - if (strlen(NewArgv[0]) >= MAXPATHLEN) { - errno = ENAMETOOLONG; - (void) fprintf(stderr, "%s: %s: Pathname too long\n", Argv[0], - NewArgv[0]); - exit(1); - } - - /* - * Resolve the path - */ - if ((retval = find_path(NewArgv[0], &cmnd)) != FOUND) - cmnd = NewArgv[0]; - return(retval); -} - - - -/********************************************************************** - * - * check_sudoers() - * - * This function check to see that the sudoers file is owned by - * uid SUDOERS_UID, gid SUDOERS_GID and is mode SUDOERS_MODE. +/* + * Sanity check sudoers mode/owner/type. + * Leaves a file pointer to the sudoers file open in ``fp''. */ - -static int +static void check_sudoers() { struct stat statbuf; int rootstat, i; char c; - int rtn = ALL_SYSTEMS_GO; /* * Fix the mode and group on sudoers file from old default. @@ -883,15 +772,13 @@ check_sudoers() Argv[0], _PATH_SUDO_SUDOERS); statbuf.st_gid = SUDOERS_GID; } else { - (void) fprintf(stderr,"%s: Unable to set group on %s: ", - Argv[0], _PATH_SUDO_SUDOERS); - perror(""); + (void) fprintf(stderr,"%s: Unable to set group on %s: %s\n", + Argv[0], _PATH_SUDO_SUDOERS, strerror(errno)); } } } else { - (void) fprintf(stderr, "%s: Unable to fix mode on %s: ", - Argv[0], _PATH_SUDO_SUDOERS); - perror(""); + (void) fprintf(stderr, "%s: Unable to fix mode on %s: %s\n", + Argv[0], _PATH_SUDO_SUDOERS, strerror(errno)); } } @@ -903,13 +790,18 @@ check_sudoers() set_perms(PERM_SUDOERS, 0); if (rootstat != 0 && lstat(_PATH_SUDO_SUDOERS, &statbuf) != 0) - rtn = NO_SUDOERS_FILE; + log_error(USE_ERRNO, "can't stat %s", _PATH_SUDO_SUDOERS); else if (!S_ISREG(statbuf.st_mode)) - rtn = SUDOERS_NOT_FILE; - else if ((statbuf.st_mode & 0007777) != SUDOERS_MODE) - rtn = SUDOERS_WRONG_MODE; - else if (statbuf.st_uid != SUDOERS_UID || statbuf.st_gid != SUDOERS_GID) - rtn = SUDOERS_WRONG_OWNER; + log_error(0, "%s is not a regular file", _PATH_SUDO_SUDOERS); + else if ((statbuf.st_mode & 07777) != SUDOERS_MODE) + log_error(0, "%s is mode 0%o, should be 0%o", _PATH_SUDO_SUDOERS, + (statbuf.st_mode & 07777), SUDOERS_MODE); + else if (statbuf.st_uid != SUDOERS_UID) + log_error(0, "%s is owned by uid %ld, should be %d", _PATH_SUDO_SUDOERS, + (long) statbuf.st_uid, SUDOERS_UID); + else if (statbuf.st_gid != SUDOERS_GID) + log_error(0, "%s is owned by gid %ld, should be %d", _PATH_SUDO_SUDOERS, + (long) statbuf.st_gid, SUDOERS_GID); else { /* Solaris sometimes returns EAGAIN so try 10 times */ for (i = 0; i < 10 ; i++) { @@ -923,28 +815,48 @@ check_sudoers() break; sleep(1); } - if (sudoers_fp == NULL) { - fprintf(stderr, "%s: cannot open %s: ", Argv[0], _PATH_SUDO_SUDOERS); - perror(""); - rtn = NO_SUDOERS_FILE; - } + if (sudoers_fp == NULL) + log_error(USE_ERRNO, "can't open %s", _PATH_SUDO_SUDOERS); } set_perms(PERM_ROOT, 0); set_perms(PERM_USER, 0); - - return(rtn); } +/* + * Remove environment variables that match the entries in badenv_table. + */ +static void +clean_env(envp, badenv_table) + char **envp; + struct env_table *badenv_table; +{ + struct env_table *bad; + char **cur; + + /* + * Remove any envars that match entries in badenv_table. + */ + for (cur = envp; *cur; cur++) { + for (bad = badenv_table; bad->name; bad++) { + if (strncmp(*cur, bad->name, bad->len) == 0) { + /* Got a match so remove it. */ + char **move; + for (move = cur; *move; move++) + *move = *(move + 1); -/********************************************************************** - * - * set_perms() - * - * this function sets real and effective uids and gids based on perm. - */ + cur--; + break; + } + } + } +} + +/* + * Set real and effective uids and gids based on perm. + */ void set_perms(perm, sudo_mode) int perm; @@ -963,7 +875,7 @@ set_perms(perm, sudo_mode) case PERM_USER: (void) setgid(user_gid); - if (seteuid(user_uid)) { + if (geteuid() != user_uid && seteuid(user_uid)) { perror("seteuid(user_uid)"); exit(1); } @@ -990,19 +902,18 @@ set_perms(perm, sudo_mode) } /* XXX - add group/gid support */ - if (*runas_user == '#') { - if (setuid(atoi(runas_user + 1))) { + if (*user_runas == '#') { + if (setuid(atoi(user_runas + 1))) { (void) fprintf(stderr, - "%s: cannot set uid to %s: ", - Argv[0], runas_user); - perror(""); + "%s: cannot set uid to %s: %s\n", + Argv[0], user_runas, strerror(errno)); exit(1); } } else { - if (!(pw = getpwnam(runas_user))) { + if (!(pw = getpwnam(user_runas))) { (void) fprintf(stderr, "%s: no passwd entry for %s!\n", - Argv[0], runas_user); + Argv[0], user_runas); exit(1); } @@ -1022,9 +933,9 @@ set_perms(perm, sudo_mode) if (setgid(pw->pw_gid)) { (void) fprintf(stderr, - "%s: cannot set gid to %ld: ", - Argv[0], (long) pw->pw_gid); - perror(""); + "%s: cannot set gid to %ld: %s\n", + Argv[0], (long) pw->pw_gid, + strerror(errno)); exit(1); } @@ -1032,21 +943,20 @@ set_perms(perm, sudo_mode) * Initialize group vector only if are * going to run as a non-root user. */ - if (strcmp(runas_user, "root") != 0 && - initgroups(runas_user, pw->pw_gid) + if (strcmp(user_runas, "root") != 0 && + initgroups(user_runas, pw->pw_gid) == -1) { (void) fprintf(stderr, - "%s: cannot set group vector ", - Argv[0]); - perror(""); + "%s: cannot set group vector: %s\n", + Argv[0], strerror(errno)); exit(1); } if (setuid(pw->pw_uid)) { (void) fprintf(stderr, - "%s: cannot set uid to %ld: ", - Argv[0], (long) pw->pw_uid); - perror(""); + "%s: cannot set uid to %ld: %s\n", + Argv[0], (long) pw->pw_uid, + strerror(errno)); exit(1); } if (sudo_mode & MODE_RESET_HOME) @@ -1088,41 +998,31 @@ set_perms(perm, sudo_mode) } } - - -/********************************************************************** - * - * clean_env() - * - * This function removes things from the environment that match the - * entries in badenv_table. It would be nice to add in the SUDO_* - * variables here as well but cmnd has not been defined at this point. +/* + * Tell which options are mutually exclusive and exit. */ - static void -clean_env(envp, badenv_table) - char **envp; - struct env_table *badenv_table; +usage_excl(exit_val) + int exit_val; { - struct env_table *bad; - char **cur; - - /* - * Remove any envars that match entries in badenv_table - */ - for (cur = envp; *cur; cur++) { - for (bad = badenv_table; bad -> name; bad++) { - if (strncmp(*cur, bad -> name, bad -> len) == 0) { - /* got a match so remove it */ - char **move; - - for (move = cur; *move; move++) - *move = *(move + 1); - - cur--; + (void) fprintf(stderr, + "Only one of the -v, -k, -K, -l, -V and -h options may be used\n"); + usage(exit_val); +} - break; - } - } - } +/* + * Give usage message and exit. + */ +static void +usage(exit_val) + int exit_val; +{ + (void) fprintf(stderr, + "usage: %s -V | -h | -l | -v | -k | -K | -H | [-b] [-p prompt]\n%*s", + Argv[0], strlen(Argv[0]) + 8, " "); +#ifdef HAVE_KERB5 + (void) fprintf(stderr, "[-r realm] "); +#endif /* HAVE_KERB5 */ + (void) fprintf(stderr, "[-u username/#uid] -s | \n"); + exit(exit_val); } -- 2.40.0