diff options
author | 1999-11-18 16:29:01 +0000 | |
---|---|---|
committer | 1999-11-18 16:29:01 +0000 | |
commit | ee4a81dac7260aeb8b39c7b51d451b215a8821f1 (patch) | |
tree | e7ce36a1f0161cd43c76d6b6cc71a3721ed0b8cc /usr.bin/sudo/sudo.c | |
parent | more %d vs. %s in fmt-strings (diff) | |
download | wireguard-openbsd-ee4a81dac7260aeb8b39c7b51d451b215a8821f1.tar.xz wireguard-openbsd-ee4a81dac7260aeb8b39c7b51d451b215a8821f1.zip |
sudo 1.6, now with a BSD license
Diffstat (limited to 'usr.bin/sudo/sudo.c')
-rw-r--r-- | usr.bin/sudo/sudo.c | 1062 |
1 files changed, 1062 insertions, 0 deletions
diff --git a/usr.bin/sudo/sudo.c b/usr.bin/sudo/sudo.c new file mode 100644 index 00000000000..0a4c908f6ee --- /dev/null +++ b/usr.bin/sudo/sudo.c @@ -0,0 +1,1062 @@ +/* + * Copyright (c) 1994-1996,1998-1999 Todd C. Miller <Todd.Miller@courtesan.com> + * All rights reserved. + * + * 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. + * + * 4. Products derived from this software may not be called "Sudo" nor + * may "Sudo" appear in their names without specific prior written + * permission from the author. + * + * 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. + * + * For a brief history of sudo, please see the HISTORY file included + * with this distribution. + */ + +#define _SUDO_SUDO_C + +#include "config.h" + +#include <stdio.h> +#ifdef STDC_HEADERS +#include <stdlib.h> +#endif /* STDC_HEADERS */ +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif /* HAVE_UNISTD_H */ +#ifdef HAVE_STRING_H +#include <string.h> +#endif /* HAVE_STRING_H */ +#ifdef HAVE_STRINGS_H +#include <strings.h> +#endif /* HAVE_STRINGS_H */ +#include <pwd.h> +#include <errno.h> +#include <fcntl.h> +#include <signal.h> +#include <grp.h> +#include <time.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/param.h> +#include <netinet/in.h> +#include <netdb.h> +#ifdef HAVE_SETRLIMIT +#include <sys/time.h> +#include <sys/resource.h> +#endif +#if defined(HAVE_GETPRPWNAM) && defined(HAVE_SET_AUTH_PARAMETERS) +# ifdef __hpux +# undef MAXINT +# include <hpsecurity.h> +# else +# include <sys/security.h> +# endif /* __hpux */ +# include <prot.h> +#endif /* HAVE_GETPRPWNAM && HAVE_SET_AUTH_PARAMETERS */ + +#include "sudo.h" +#include "interfaces.h" +#include "version.h" + +#ifndef STDC_HEADERS +extern char *getenv __P((char *)); +#endif /* STDC_HEADERS */ + +#ifndef lint +static const char rcsid[] = "$Sudo: sudo.c,v 1.258 1999/11/16 06:09:23 millert Exp $"; +#endif /* lint */ + +/* + * Local type declarations + */ +struct env_table { + char *name; + int len; +}; + +/* + * Prototypes + */ +static int parse_args __P((void)); +static void usage __P((int)); +static void usage_excl __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 *)); +static void initial_setup __P((void)); +extern int user_is_exempt __P((void)); +extern struct passwd *sudo_getpwuid __P((uid_t)); +extern void list_matches __P((void)); + +/* + * Globals + */ +int Argc; +char **Argv; +int NewArgc = 0; +char **NewArgv = NULL; +struct sudo_user sudo_user; +FILE *sudoers_fp = NULL; +static char *runas_homedir = NULL; /* XXX */ +struct interface *interfaces; +int num_interfaces; +extern int errorlineno; + +/* + * Table of "bad" envariables to remove and len for strncmp() + */ +static struct env_table badenv_table[] = { + { "IFS=", 4 }, + { "LOCALDOMAIN=", 12 }, + { "RES_OPTIONS=", 12 }, + { "HOSTALIASES=", 12 }, + { "LD_", 3 }, + { "_RLD", 4 }, +#ifdef __hpux + { "SHLIB_PATH=", 11 }, +#endif /* __hpux */ +#ifdef _AIX + { "LIBPATH=", 8 }, +#endif /* _AIX */ +#ifdef HAVE_KERB4 + { "KRB_CONF", 8 }, +#endif /* HAVE_KERB4 */ +#ifdef HAVE_KERB5 + { "KRB5_CONFIG", 11 }, +#endif /* HAVE_KERB5 */ + { "ENV=", 4 }, + { "BASH_ENV=", 9 }, + { (char *) NULL, 0 } +}; + + +int +main(argc, argv) + int argc; + char **argv; +{ + 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 + initprivs(); +# endif +#endif /* HAVE_GETPRPWNAM && HAVE_SET_AUTH_PARAMETERS */ + + Argv = argv; + Argc = argc; + + if (geteuid() != 0) { + (void) fprintf(stderr, "Sorry, %s must be setuid root.\n", Argv[0]); + exit(1); + } + + /* + * Block signals so the user cannot interrupt us at some point and + * avoid the logging. + */ +#ifdef POSIX_SIGNALS + (void) sigemptyset(&set); + (void) sigaddset(&set, SIGINT); + (void) sigaddset(&set, SIGQUIT); + (void) sigaddset(&set, SIGTSTP); + (void) sigprocmask(SIG_BLOCK, &set, &oset); +#else + omask = sigblock(sigmask(SIGINT)|sigmask(SIGQUIT)|sigmask(SIGTSTP)); +#endif /* POSIX_SIGNALS */ + + /* + * Setup signal handlers, turn off core dumps, and close open files. + */ + initial_setup(); + + /* + * Set the prompt based on $SUDO_PROMPT (can be overridden by `-p') + */ + user_prompt = getenv("SUDO_PROMPT"); + + /* Parse our arguments. */ + sudo_mode = parse_args(); + + /* Setup defaults data structures. */ + init_defaults(); + + /* Initialize syslog(3) if we are using it. */ + if (def_str(I_LOGFACSTR)) { +#ifdef LOG_NFACILITIES + openlog("sudo", 0, def_ival(I_LOGFAC)); +#else + openlog("sudo", 0); +#endif /* LOG_NFACILITIES */ + } + + if (sudo_mode & MODE_SHELL) + user_cmnd = "shell"; + else + switch (sudo_mode) { + case MODE_VERSION: + (void) printf("Sudo version %s\n", version); + if (getuid() == 0) { + putchar('\n'); + dump_auth_methods(); + dump_defaults(); + } + exit(0); + break; + case MODE_HELP: + usage(0); + break; + case MODE_VALIDATE: + user_cmnd = "validate"; + break; + case MODE_KILL: + case MODE_INVALIDATE: + user_cmnd = "kill"; + break; + case MODE_LISTDEFS: + list_options(); + exit(0); + break; + case MODE_LIST: + user_cmnd = "list"; + printmatches = 1; + break; + } + + /* Must have a command to run... */ + if (user_cmnd == NULL && NewArgc == 0) + usage(1); + + clean_env(environ, badenv_table); + + cmnd_status = init_vars(sudo_mode); + + /* At this point, ruid == euid == 0 */ + + check_sudoers(); /* check mode/owner on _PATH_SUDOERS */ + + 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. */ + validated = + sudoers_lookup((sudo_mode != MODE_VALIDATE && sudo_mode != MODE_LIST)); + + if (validated & VALIDATE_ERROR) + log_error(0, "parse error in %s near line %d", _PATH_SUDOERS, + errorlineno); + + /* Is root even allowed to run sudo? */ + if (user_uid == 0 && !def_flag(I_ROOT_SUDO)) { + (void) fputs("You are already root, you don't need to use sudo.\n", + stderr); + exit(1); + } + + /* Bail if a tty is required and we don't have one. */ + if (def_flag(I_REQUIRETTY)) { + if ((fd = open(_PATH_TTY, O_RDWR|O_NOCTTY)) == -1) + log_error(NO_MAIL, "sorry, you must have a tty to run sudo"); + else + (void) close(fd); + } + + /* Require a password unless the NOPASS tag was set. */ + if (!(validated & FLAG_NOPASS)) + check_user(); + + if (validated & VALIDATE_OK) { + /* 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], 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], + user_cmnd); + exit(1); + } + + log_auth(validated, 1); + if (sudo_mode == MODE_VALIDATE) + exit(0); + else if (sudo_mode == MODE_LIST) { + list_matches(); + exit(0); + } + + /* Become specified user or root. */ + set_perms(PERM_RUNAS, sudo_mode); + + /* 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 (safe_cmnd == NULL) { + log_error(MSG_ONLY, + "internal error, cmnd_safe never got set for %s; %s", + user_cmnd, + "please report this error at http://courtesan.com/sudo/bugs/"); + } + + if (def_ival(I_LOGFACSTR)) + closelog(); + + /* Reset signal mask before we exec. */ +#ifdef POSIX_SIGNALS + (void) sigprocmask(SIG_SETMASK, &oset, NULL); +#else + (void) sigsetmask(omask); +#endif /* POSIX_SIGNALS */ + + /* Override user's umask if configured to do so. */ + if (def_ival(I_UMASK) != 0777) + (void) umask(def_mode(I_UMASK)); + + /* Replace the PATH envariable with a secure one. */ + if (def_str(I_SECURE_PATH) && !user_is_exempt()) + if (sudo_setenv("PATH", def_str(I_SECURE_PATH))) { + (void) fprintf(stderr, "%s: cannot allocate memory!\n", + Argv[0]); + exit(1); + } + +#ifndef PROFILING + if ((sudo_mode & MODE_BACKGROUND) && fork() > 0) + exit(0); + else + EXEC(safe_cmnd, NewArgv); /* run the command */ +#else + exit(0); +#endif /* PROFILING */ + /* + * If we got here then the exec() failed... + */ + (void) fprintf(stderr, "%s: unable to exec %s: %s\n", + Argv[0], safe_cmnd, strerror(errno)); + exit(-1); + } else if ((validated & FLAG_NO_USER) || (validated & FLAG_NO_HOST)) { + log_auth(validated, 1); + exit(1); + } else if (validated & VALIDATE_NOT_OK) { + if (def_flag(I_PATH_INFO)) { + /* + * We'd like to not leak path info at all here, but that can + * *really* confuse the users. To really close the leak we'd + * have to say "not allowed to run foo" even when the problem + * is just "no foo in path" since the user can trivially set + * their path to just contain a single dir. + */ + 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], + 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); + } else { + /* Just tell the user they are not allowed to run foo. */ + log_auth(validated, 1); + } + exit(1); + } else { + /* should never get here */ + log_auth(validated, 1); + exit(1); + } + exit(0); /* not reached */ +} + +/* + * Initialize timezone, set umask, fill in ``sudo_user'' struct and + * load the ``interfaces'' array. + */ +static int +init_vars(sudo_mode) + int sudo_mode; +{ + char *p, thost[MAXHOSTNAMELEN]; + struct hostent *hp; + + /* 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); + } + +#ifdef HAVE_TZSET + (void) tzset(); /* set the timezone if applicable */ +#endif /* HAVE_TZSET */ + + /* Default value for cmnd and cwd, overridden later. */ + if (user_cmnd == NULL) + user_cmnd = NewArgv[0]; + (void) strcpy(user_cwd, "unknown"); + + /* + * 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 ((gethostname(thost, sizeof(thost)))) { + user_host = "localhost"; + log_error(USE_ERRNO|MSG_ONLY, "can't get hostname"); + } else + user_host = estrdup(thost); + if (def_flag(I_FQDN)) { + if (!(hp = gethostbyname(user_host))) { + log_error(USE_ERRNO|MSG_ONLY|NO_EXIT, + "unable to lookup %s via gethostbyname()", user_host); + } else { + free(user_host); + user_host = estrdup(hp->h_name); + } + } + 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"; + + /* + * 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 ((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); + } + + /* It is now safe to use log_error() and set_perms() */ + + /* + * Get current working directory. Try as user, fall back to root. + */ + 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 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); + } + + /* 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 rval = MODE_RUN; /* what mode is suod to be run in? */ + int excl = 0; /* exclusive arg, no others allowed */ + + NewArgv = Argv + 1; + NewArgc = Argc - 1; + + if (Argc < 2) { /* no options and no command */ + if (!def_flag(I_SHELL_NOARGS)) + usage(1); + rval |= MODE_SHELL; + return(rval); + } + + while (NewArgc > 0 && NewArgv[0][0] == '-') { + if (NewArgv[0][1] != '\0' && NewArgv[0][2] != '\0') { + (void) fprintf(stderr, "%s: Please use single character options\n", + Argv[0]); + usage(1); + } + + switch (NewArgv[0][1]) { + case 'p': + /* Must have an associated prompt. */ + if (NewArgv[1] == NULL) + usage(1); + + user_prompt = NewArgv[1]; + + /* Shift Argv over and adjust Argc. */ + NewArgc--; + NewArgv++; + break; + case 'u': + /* Must have an associated runas user. */ + if (NewArgv[1] == NULL) + usage(1); + + user_runas = &NewArgv[1]; + + /* Shift Argv over and adjust Argc. */ + NewArgc--; + NewArgv++; + break; + case 'b': + rval |= MODE_BACKGROUND; + break; + case 'v': + rval = MODE_VALIDATE; + if (excl && excl != 'v') + usage_excl(1); + excl = 'v'; + break; + case 'k': + 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': + rval = MODE_LISTDEFS; + if (excl && excl != 'L') + usage_excl(1); + excl = 'L'; + break; + case 'l': + rval = MODE_LIST; + if (excl && excl != 'l') + usage_excl(1); + excl = 'l'; + break; + case 'V': + rval = MODE_VERSION; + if (excl && excl != 'V') + usage_excl(1); + excl = 'V'; + break; + case 'h': + rval = MODE_HELP; + if (excl && excl != 'h') + usage_excl(1); + excl = 'h'; + break; + case 's': + rval |= MODE_SHELL; + if (def_flag(I_SET_HOME)) + rval |= MODE_RESET_HOME; + break; + case 'H': + rval |= MODE_RESET_HOME; + break; + case '-': + NewArgc--; + NewArgv++; + if (def_flag(I_SHELL_NOARGS) && rval == MODE_RUN) + rval |= MODE_SHELL; + return(rval); + case '\0': + (void) fprintf(stderr, "%s: '-' requires an argument\n", + Argv[0]); + usage(1); + default: + (void) fprintf(stderr, "%s: Illegal option %s\n", Argv[0], + NewArgv[0]); + usage(1); + } + NewArgc--; + NewArgv++; + } + + if (NewArgc > 0 && !(rval & MODE_RUN)) + usage(1); + + return(rval); +} + +/* + * Add sudo-specific variables into the environment. + * Sets ``cmnd_args'' as a side effect. + */ +static void +add_env(contiguous) + int contiguous; +{ + char idstr[MAX_UID_T_LEN + 1]; + size_t size; + char *buf; + + /* Add the SUDO_COMMAND envariable (cmnd + args). */ + size = strlen(user_cmnd) + 1; + if (NewArgc > 1) { + char *to, **from; + + if (contiguous) { + size += (size_t) (NewArgv[NewArgc-1] - NewArgv[1]) + + strlen(NewArgv[NewArgc-1]) + 1; + } else { + for (from = &NewArgv[1]; *from; from++) + size += strlen(*from) + 1; + } + + buf = (char *) emalloc(size); + + /* + * Copy the command and it's arguments info buf. + */ + (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 = user_cmnd; + } + if (sudo_setenv("SUDO_COMMAND", buf)) { + (void) fprintf(stderr, "%s: cannot allocate memory!\n", Argv[0]); + exit(1); + } + if (NewArgc > 1) + free(buf); + + /* 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 + user_args = NULL; + } + + /* 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 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 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. */ + if ((buf = getenv("SUDO_PS1"))) + if (sudo_setenv("PS1", buf)) { + (void) fprintf(stderr, "%s: cannot allocate memory!\n", Argv[0]); + exit(1); + } +} + +/* + * Sanity check sudoers mode/owner/type. + * Leaves a file pointer to the sudoers file open in ``fp''. + */ +static void +check_sudoers() +{ + struct stat statbuf; + int rootstat, i; + char c; + + /* + * Fix the mode and group on sudoers file from old default. + * Only works if filesystem is readable/writable by root. + */ + if ((rootstat = lstat(_PATH_SUDOERS, &statbuf)) == 0 && + SUDOERS_UID == statbuf.st_uid && SUDOERS_MODE != 0400 && + (statbuf.st_mode & 0007777) == 0400) { + + if (chmod(_PATH_SUDOERS, SUDOERS_MODE) == 0) { + (void) fprintf(stderr, "%s: fixed mode on %s\n", + Argv[0], _PATH_SUDOERS); + if (statbuf.st_gid != SUDOERS_GID) { + if (!chown(_PATH_SUDOERS,(uid_t) -1,SUDOERS_GID)) { + (void) fprintf(stderr, "%s: set group on %s\n", + Argv[0], _PATH_SUDOERS); + statbuf.st_gid = SUDOERS_GID; + } else { + (void) fprintf(stderr,"%s: Unable to set group on %s: %s\n", + Argv[0], _PATH_SUDOERS, strerror(errno)); + } + } + } else { + (void) fprintf(stderr, "%s: Unable to fix mode on %s: %s\n", + Argv[0], _PATH_SUDOERS, strerror(errno)); + } + } + + /* + * Sanity checks on sudoers file. Must be done as sudoers + * file owner. We already did a stat as root, so use that + * data if we can't stat as sudoers file owner. + */ + set_perms(PERM_SUDOERS, 0); + + if (rootstat != 0 && lstat(_PATH_SUDOERS, &statbuf) != 0) + log_error(USE_ERRNO, "can't stat %s", _PATH_SUDOERS); + else if (!S_ISREG(statbuf.st_mode)) + log_error(0, "%s is not a regular file", _PATH_SUDOERS); + else if ((statbuf.st_mode & 07777) != SUDOERS_MODE) + log_error(0, "%s is mode 0%o, should be 0%o", _PATH_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_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_SUDOERS, + (long) statbuf.st_gid, SUDOERS_GID); + else { + /* Solaris sometimes returns EAGAIN so try 10 times */ + for (i = 0; i < 10 ; i++) { + errno = 0; + if ((sudoers_fp = fopen(_PATH_SUDOERS, "r")) == NULL || + fread(&c, sizeof(c), 1, sudoers_fp) != 1) { + sudoers_fp = NULL; + if (errno != EAGAIN && errno != EWOULDBLOCK) + break; + } else + break; + sleep(1); + } + if (sudoers_fp == NULL) + log_error(USE_ERRNO, "can't open %s", _PATH_SUDOERS); + } + + set_perms(PERM_ROOT, 0); /* change back to root */ +} + +/* + * 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); + + cur--; + + break; + } + } + } +} + +/* + * Set real and effective uids and gids based on perm. + */ +void +set_perms(perm, sudo_mode) + int perm; + int sudo_mode; +{ + struct passwd *pw; + + /* + * First, set real & effective uids to root. + * If perm is PERM_ROOT then we don't need to do anything else. + */ + if (setuid(0)) { + perror("setuid(0)"); + exit(1); + } + + switch (perm) { + case PERM_USER: + (void) setgid(user_gid); + + if (seteuid(user_uid)) { + perror("seteuid(user_uid)"); + exit(1); + } + break; + + case PERM_FULL_USER: + (void) setgid(user_gid); + + if (setuid(user_uid)) { + perror("setuid(user_uid)"); + exit(1); + } + break; + + case PERM_RUNAS: + /* XXX - add group/gid support */ + if (**user_runas == '#') { + if (setuid(atoi(*user_runas + 1))) { + (void) fprintf(stderr, + "%s: cannot set uid to %s: %s\n", + Argv[0], *user_runas, strerror(errno)); + exit(1); + } + } else { + if (!(pw = getpwnam(*user_runas))) { + (void) fprintf(stderr, + "%s: no passwd entry for %s!\n", + Argv[0], *user_runas); + exit(1); + } + + /* Set $USER and $LOGNAME to target user */ + if (sudo_setenv("USER", pw->pw_name)) { + (void) fprintf(stderr, + "%s: cannot allocate memory!\n", + Argv[0]); + exit(1); + } + if (sudo_setenv("LOGNAME", pw->pw_name)) { + (void) fprintf(stderr, + "%s: cannot allocate memory!\n", + Argv[0]); + exit(1); + } + + if (setgid(pw->pw_gid)) { + (void) fprintf(stderr, + "%s: cannot set gid to %ld: %s\n", + Argv[0], (long) pw->pw_gid, + strerror(errno)); + exit(1); + } + + /* + * Initialize group vector only if are + * going to run as a non-root user. + */ + if (strcmp(*user_runas, "root") != 0 && + initgroups(*user_runas, pw->pw_gid) + == -1) { + (void) fprintf(stderr, + "%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: %s\n", + Argv[0], (long) pw->pw_uid, + strerror(errno)); + exit(1); + } + if (sudo_mode & MODE_RESET_HOME) + runas_homedir = pw->pw_dir; + } + break; + + case PERM_SUDOERS: + if (setgid(SUDOERS_GID)) { + perror("setgid(SUDOERS_GID)"); + exit(1); + } + + /* + * If SUDOERS_UID == 0 and SUDOERS_MODE + * is group readable we use a non-zero + * uid in order to avoid NFS lossage. + * Using uid 1 is a bit bogus but should + * work on all OS's. + */ + if (SUDOERS_UID == 0) { + if ((SUDOERS_MODE & 040) && seteuid(1)) { + perror("seteuid(1)"); + exit(1); + } + } else { + if (seteuid(SUDOERS_UID)) { + perror("seteuid(SUDOERS_UID)"); + exit(1); + } + } + break; + } +} + +/* + * Close all open files (except std*) and turn off core dumps. + */ +static void +initial_setup() +{ + int fd, maxfd; +#ifdef HAVE_SETRLIMIT + struct rlimit rl; +#endif +#ifdef POSIX_SIGNALS + struct sigaction sa; +#endif + +#if defined(RLIMIT_CORE) && !defined(SUDO_DEVEL) + /* + * Turn off core dumps. + */ + rl.rlim_cur = rl.rlim_max = 0; + (void) setrlimit(RLIMIT_CORE, &rl); +#endif /* RLIMIT_CORE */ + + /* + * Close any open fd's other than stdin, stdout and stderr. + */ +#ifdef RLIMIT_NOFILE + if (getrlimit(RLIMIT_NOFILE, &rl) == 0) + maxfd = rl.rlim_max - 1; + else +#endif /* RLIMIT_NOFILE */ +#ifdef HAVE_SYSCONF + maxfd = sysconf(_SC_OPEN_MAX) - 1; +#else + maxfd = getdtablesize() - 1; +#endif /* HAVE_SYSCONF */ + + for (fd = maxfd; fd > STDERR_FILENO; fd--) + (void) close(fd); + + /* Catch children as they die... */ +#ifdef POSIX_SIGNALS + (void) memset((VOID *)&sa, 0, sizeof(sa)); + sa.sa_handler = reapchild; + (void) sigaction(SIGCHLD, &sa, NULL); +#else + (void) signal(SIGCHLD, reapchild); +#endif /* POSIX_SIGNALS */ +} + +/* + * 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, -K, -l, -V and -h options may be used\n"); + usage(exit_val); +} + +/* + * Give usage message and exit. + */ +static void +usage(exit_val) + int exit_val; +{ + (void) fprintf(stderr, + "usage: %s -V | -h | -L | -l | -v | -k | -K | -H | [-b] [-p prompt]\n%*s", + Argv[0], (int) strlen(Argv[0]) + 8, " "); + (void) fprintf(stderr, "[-u username/#uid] -s | <command>\n"); + exit(exit_val); +} |