version 1.16, 2002/04/25 15:49:03 |
version 1.17, 2003/03/15 21:23:54 |
|
|
/* |
/* |
* Copyright (c) 1993-1996,1998-2002 Todd C. Miller <Todd.Miller@courtesan.com> |
* Copyright (c) 1993-1996,1998-2003 Todd C. Miller <Todd.Miller@courtesan.com> |
* All rights reserved. |
* All rights reserved. |
* |
* |
* Redistribution and use in source and binary forms, with or without |
* Redistribution and use in source and binary forms, with or without |
|
|
#include "version.h" |
#include "version.h" |
|
|
#ifndef lint |
#ifndef lint |
static const char rcsid[] = "$Sudo: sudo.c,v 1.318 2002/01/15 23:43:59 millert Exp $"; |
static const char rcsid[] = "$Sudo: sudo.c,v 1.333 2003/03/15 20:31:01 millert Exp $"; |
#endif /* lint */ |
#endif /* lint */ |
|
|
/* |
/* |
|
|
struct interface *interfaces; |
struct interface *interfaces; |
int num_interfaces; |
int num_interfaces; |
int tgetpass_flags; |
int tgetpass_flags; |
|
uid_t timestamp_uid; |
extern int errorlineno; |
extern int errorlineno; |
#if defined(RLIMIT_CORE) && !defined(SUDO_DEVEL) |
#if defined(RLIMIT_CORE) && !defined(SUDO_DEVEL) |
static struct rlimit corelimit; |
static struct rlimit corelimit; |
|
|
#ifdef HAVE_BSD_AUTH_H |
#ifdef HAVE_BSD_AUTH_H |
char *login_style; |
char *login_style; |
#endif /* HAVE_BSD_AUTH_H */ |
#endif /* HAVE_BSD_AUTH_H */ |
void (*set_perms) __P((int, int)); |
void (*set_perms) __P((int)); |
|
|
|
|
int |
int |
|
|
int sudo_mode; |
int sudo_mode; |
int pwflag; |
int pwflag; |
char **new_environ; |
char **new_environ; |
sigaction_t sa; |
sigaction_t sa, saved_sa_int, saved_sa_quit, saved_sa_tstp, saved_sa_chld; |
extern int printmatches; |
extern int printmatches; |
extern char **environ; |
extern char **environ; |
|
|
|
|
} |
} |
|
|
/* |
/* |
* Ignore keyboard-generated signals so the user cannot interrupt |
* Signal setup: |
* us at some point and avoid the logging. |
* Ignore keyboard-generated signals so the user cannot interrupt |
|
* us at some point and avoid the logging. |
|
* Install handler to wait for children when they exit. |
*/ |
*/ |
sigemptyset(&sa.sa_mask); |
sigemptyset(&sa.sa_mask); |
sa.sa_flags = SA_RESTART; |
sa.sa_flags = SA_RESTART; |
sa.sa_handler = SIG_IGN; |
sa.sa_handler = SIG_IGN; |
(void) sigaction(SIGINT, &sa, NULL); |
(void) sigaction(SIGINT, &sa, &saved_sa_int); |
(void) sigaction(SIGQUIT, &sa, NULL); |
(void) sigaction(SIGQUIT, &sa, &saved_sa_quit); |
(void) sigaction(SIGTSTP, &sa, NULL); |
(void) sigaction(SIGTSTP, &sa, &saved_sa_tstp); |
|
sa.sa_handler = reapchild; |
|
(void) sigaction(SIGCHLD, &sa, &saved_sa_chld); |
|
|
/* |
/* |
* Setup signal handlers, turn off core dumps, and close open files. |
* Turn off core dumps, close open files and setup set_perms(). |
*/ |
*/ |
initial_setup(); |
initial_setup(); |
setpwent(); |
setpwent(); |
|
|
dump_auth_methods(); |
dump_auth_methods(); |
dump_defaults(); |
dump_defaults(); |
dump_interfaces(); |
dump_interfaces(); |
dump_badenv(); |
|
} |
} |
exit(0); |
exit(0); |
break; |
break; |
|
|
validated = sudoers_lookup(pwflag); |
validated = sudoers_lookup(pwflag); |
|
|
/* |
/* |
* If we have POSIX saved uids and the stay_setuid flag was not set, |
* If we are using set_perms_posix() and the stay_setuid flag was not set, |
* set the real, effective and saved uids to 0 and use set_perms_fallback() |
* set the real, effective and saved uids to 0 and use set_perms_nosuid() |
* instead of set_perms_posix(). |
* instead of set_perms_posix(). |
*/ |
*/ |
#if !defined(NO_SAVED_IDS) && defined(_SC_SAVED_IDS) && defined(_SC_VERSION) |
#if !defined(HAVE_SETRESUID) && !defined(HAVE_SETREUID) && \ |
|
!defined(NO_SAVED_IDS) && defined(_SC_SAVED_IDS) && defined(_SC_VERSION) |
if (!def_flag(I_STAY_SETUID) && set_perms == set_perms_posix) { |
if (!def_flag(I_STAY_SETUID) && set_perms == set_perms_posix) { |
if (setuid(0)) { |
if (setuid(0)) { |
perror("setuid(0)"); |
perror("setuid(0)"); |
exit(1); |
exit(1); |
} |
} |
set_perms = set_perms_fallback; |
set_perms = set_perms_nosuid; |
} |
} |
#endif |
#endif |
|
|
|
|
log_error(NO_MAIL|MSG_ONLY, "no passwd entry for %s!", *user_runas); |
log_error(NO_MAIL|MSG_ONLY, "no passwd entry for %s!", *user_runas); |
} |
} |
|
|
|
/* |
|
* Look up the timestamp dir owner if one is specified. |
|
*/ |
|
if (def_str(I_TIMESTAMPOWNER)) { |
|
struct passwd *pw; |
|
|
|
if (*def_str(I_TIMESTAMPOWNER) == '#') |
|
pw = getpwuid(atoi(def_str(I_TIMESTAMPOWNER) + 1)); |
|
else |
|
pw = getpwnam(def_str(I_TIMESTAMPOWNER)); |
|
if (!pw) |
|
log_error(0, "timestamp owner (%s): No such user", |
|
def_str(I_TIMESTAMPOWNER)); |
|
timestamp_uid = pw->pw_uid; |
|
} |
|
|
/* This goes after the sudoers parse since we honor sudoers options. */ |
/* This goes after the sudoers parse since we honor sudoers options. */ |
if (sudo_mode == MODE_KILL || sudo_mode == MODE_INVALIDATE) { |
if (sudo_mode == MODE_KILL || sudo_mode == MODE_INVALIDATE) { |
remove_timestamp((sudo_mode == MODE_KILL)); |
remove_timestamp((sudo_mode == MODE_KILL)); |
|
|
|
|
/* Is root even allowed to run sudo? */ |
/* Is root even allowed to run sudo? */ |
if (user_uid == 0 && !def_flag(I_ROOT_SUDO)) { |
if (user_uid == 0 && !def_flag(I_ROOT_SUDO)) { |
(void) fputs("You are already root, you don't need to use sudo.\n", |
(void) fprintf(stderr, |
stderr); |
"Sorry, %s has been configured to not allow root to run it.\n", |
|
Argv[0]); |
exit(1); |
exit(1); |
} |
} |
|
|
|
|
"please report this error at http://courtesan.com/sudo/bugs/"); |
"please report this error at http://courtesan.com/sudo/bugs/"); |
} |
} |
|
|
/* Reset signal handlers before we exec. */ |
|
sigemptyset(&sa.sa_mask); |
|
sa.sa_flags = SA_RESTART; |
|
sa.sa_handler = SIG_DFL; |
|
(void) sigaction(SIGINT, &sa, NULL); |
|
(void) sigaction(SIGQUIT, &sa, NULL); |
|
(void) sigaction(SIGTSTP, &sa, NULL); |
|
|
|
/* Override user's umask if configured to do so. */ |
/* Override user's umask if configured to do so. */ |
if (def_ival(I_UMASK) != 0777) |
if (def_ival(I_UMASK) != 0777) |
(void) umask(def_mode(I_UMASK)); |
(void) umask(def_mode(I_UMASK)); |
|
|
#endif /* RLIMIT_CORE */ |
#endif /* RLIMIT_CORE */ |
|
|
/* Become specified user or root. */ |
/* Become specified user or root. */ |
set_perms(PERM_RUNAS, sudo_mode); |
set_perms(PERM_RUNAS); |
|
|
/* Close the password and group files */ |
/* Close the password and group files */ |
endpwent(); |
endpwent(); |
|
|
/* Install the new environment. */ |
/* Install the new environment. */ |
environ = new_environ; |
environ = new_environ; |
|
|
|
/* Restore signal handlers before we exec. */ |
|
(void) sigaction(SIGINT, &saved_sa_int, NULL); |
|
(void) sigaction(SIGQUIT, &saved_sa_quit, NULL); |
|
(void) sigaction(SIGTSTP, &saved_sa_tstp, NULL); |
|
(void) sigaction(SIGCHLD, &saved_sa_chld, NULL); |
|
|
#ifndef PROFILING |
#ifndef PROFILING |
if ((sudo_mode & MODE_BACKGROUND) && fork() > 0) |
if ((sudo_mode & MODE_BACKGROUND) && fork() > 0) |
exit(0); |
exit(0); |
|
|
/* Default value for cmnd and cwd, overridden later. */ |
/* Default value for cmnd and cwd, overridden later. */ |
if (user_cmnd == NULL) |
if (user_cmnd == NULL) |
user_cmnd = NewArgv[0]; |
user_cmnd = NewArgv[0]; |
(void) strcpy(user_cwd, "unknown"); |
(void) strlcpy(user_cwd, "unknown", sizeof(user_cwd)); |
|
|
/* |
/* |
* We avoid gethostbyname() if possible since we don't want |
* We avoid gethostbyname() if possible since we don't want |
|
|
char pw_name[MAX_UID_T_LEN + 1]; |
char pw_name[MAX_UID_T_LEN + 1]; |
|
|
pw.pw_uid = getuid(); |
pw.pw_uid = getuid(); |
(void) sprintf(pw_name, "%ld", (long) pw.pw_uid); |
(void) snprintf(pw_name, sizeof(pw_name), "%lu", |
|
(unsigned long) pw.pw_uid); |
pw.pw_name = pw_name; |
pw.pw_name = pw_name; |
sudo_user.pw = &pw; |
sudo_user.pw = &pw; |
|
|
log_error(0, "uid %ld does not exist in the passwd file!", |
log_error(0, "uid %lu does not exist in the passwd file!", |
(long) pw.pw_uid); |
(unsigned long) pw.pw_uid); |
} |
} |
if (user_shell == NULL || *user_shell == '\0') |
if (user_shell == NULL || *user_shell == '\0') |
user_shell = sudo_user.pw->pw_shell; |
user_shell = sudo_user.pw->pw_shell; |
|
|
/* |
/* |
* Get current working directory. Try as user, fall back to root. |
* Get current working directory. Try as user, fall back to root. |
*/ |
*/ |
set_perms(PERM_USER, sudo_mode); |
set_perms(PERM_USER); |
if (!getcwd(user_cwd, sizeof(user_cwd))) { |
if (!getcwd(user_cwd, sizeof(user_cwd))) { |
set_perms(PERM_ROOT, sudo_mode); |
set_perms(PERM_ROOT); |
if (!getcwd(user_cwd, sizeof(user_cwd))) { |
if (!getcwd(user_cwd, sizeof(user_cwd))) { |
(void) fprintf(stderr, "%s: Can't get working directory!\n", |
(void) fprintf(stderr, "%s: Can't get working directory!\n", |
Argv[0]); |
Argv[0]); |
(void) strcpy(user_cwd, "unknown"); |
(void) strlcpy(user_cwd, "unknown", sizeof(user_cwd)); |
} |
} |
} else |
} else |
set_perms(PERM_ROOT, sudo_mode); |
set_perms(PERM_ROOT); |
|
|
/* |
/* |
* If we were given the '-s' option (run shell) we need to redo |
* If we were given the '-s' option (run shell) we need to redo |
|
|
if ((sudo_mode & MODE_SHELL)) { |
if ((sudo_mode & MODE_SHELL)) { |
char **dst, **src = NewArgv; |
char **dst, **src = NewArgv; |
|
|
NewArgv = (char **) emalloc (sizeof(char *) * (++NewArgc + 1)); |
NewArgv = (char **) emalloc2((++NewArgc + 1), sizeof(char *)); |
if (user_shell && *user_shell) { |
if (user_shell && *user_shell) { |
NewArgv[0] = user_shell; |
NewArgv[0] = user_shell; |
} else { |
} else { |
|
|
rval = find_path(NewArgv[0], &user_cmnd, user_path); |
rval = find_path(NewArgv[0], &user_cmnd, user_path); |
if (rval != FOUND) { |
if (rval != FOUND) { |
/* Failed as root, try as invoking user. */ |
/* Failed as root, try as invoking user. */ |
set_perms(PERM_USER, sudo_mode); |
set_perms(PERM_USER); |
rval = find_path(NewArgv[0], &user_cmnd, user_path); |
rval = find_path(NewArgv[0], &user_cmnd, user_path); |
set_perms(PERM_ROOT, sudo_mode); |
set_perms(PERM_ROOT); |
} |
} |
|
|
/* set user_args */ |
/* set user_args */ |
if (NewArgc > 1) { |
if (NewArgc > 1) { |
char *to, **from; |
char *to, **from; |
size_t size; |
size_t size, n; |
|
|
/* If MODE_SHELL not set then NewArgv is contiguous so just count */ |
/* If MODE_SHELL not set then NewArgv is contiguous so just count */ |
if (!(sudo_mode & MODE_SHELL)) { |
if (!(sudo_mode & MODE_SHELL)) { |
|
|
} |
} |
|
|
/* alloc and copy. */ |
/* alloc and copy. */ |
to = user_args = (char *) emalloc(size); |
user_args = (char *) emalloc(size); |
for (from = NewArgv + 1; *from; from++) { |
for (to = user_args, from = NewArgv + 1; *from; from++) { |
(void) strcpy(to, *from); |
n = strlcpy(to, *from, size - (to - user_args)); |
to += strlen(*from); |
if (n >= size) { |
|
(void) fprintf(stderr, |
|
"%s: internal error, init_vars() overflow\n", Argv[0]); |
|
exit(1); |
|
} |
|
to += n; |
*to++ = ' '; |
*to++ = ' '; |
} |
} |
*--to = '\0'; |
*--to = '\0'; |
|
|
static int |
static int |
parse_args() |
parse_args() |
{ |
{ |
int rval = MODE_RUN; /* what mode is suod to be run in? */ |
int rval = MODE_RUN; /* what mode is sudo to be run in? */ |
int excl = 0; /* exclusive arg, no others allowed */ |
int excl = 0; /* exclusive arg, no others allowed */ |
|
|
NewArgv = Argv + 1; |
NewArgv = Argv + 1; |
|
|
* Fix the mode and group on sudoers file from old default. |
* Fix the mode and group on sudoers file from old default. |
* Only works if filesystem is readable/writable by root. |
* Only works if filesystem is readable/writable by root. |
*/ |
*/ |
if ((rootstat = lstat(_PATH_SUDOERS, &statbuf)) == 0 && |
if ((rootstat = stat_sudoers(_PATH_SUDOERS, &statbuf)) == 0 && |
SUDOERS_UID == statbuf.st_uid && SUDOERS_MODE != 0400 && |
SUDOERS_UID == statbuf.st_uid && SUDOERS_MODE != 0400 && |
(statbuf.st_mode & 0007777) == 0400) { |
(statbuf.st_mode & 0007777) == 0400) { |
|
|
|
|
* file owner. We already did a stat as root, so use that |
* file owner. We already did a stat as root, so use that |
* data if we can't stat as sudoers file owner. |
* data if we can't stat as sudoers file owner. |
*/ |
*/ |
set_perms(PERM_SUDOERS, 0); |
set_perms(PERM_SUDOERS); |
|
|
if (rootstat != 0 && lstat(_PATH_SUDOERS, &statbuf) != 0) |
if (rootstat != 0 && stat_sudoers(_PATH_SUDOERS, &statbuf) != 0) |
log_error(USE_ERRNO, "can't stat %s", _PATH_SUDOERS); |
log_error(USE_ERRNO, "can't stat %s", _PATH_SUDOERS); |
else if (!S_ISREG(statbuf.st_mode)) |
else if (!S_ISREG(statbuf.st_mode)) |
log_error(0, "%s is not a regular file", _PATH_SUDOERS); |
log_error(0, "%s is not a regular file", _PATH_SUDOERS); |
|
|
log_error(0, "%s is mode 0%o, should be 0%o", _PATH_SUDOERS, |
log_error(0, "%s is mode 0%o, should be 0%o", _PATH_SUDOERS, |
(statbuf.st_mode & 07777), SUDOERS_MODE); |
(statbuf.st_mode & 07777), SUDOERS_MODE); |
else if (statbuf.st_uid != SUDOERS_UID) |
else if (statbuf.st_uid != SUDOERS_UID) |
log_error(0, "%s is owned by uid %ld, should be %d", _PATH_SUDOERS, |
log_error(0, "%s is owned by uid %lu, should be %lu", _PATH_SUDOERS, |
(long) statbuf.st_uid, SUDOERS_UID); |
(unsigned long) statbuf.st_uid, SUDOERS_UID); |
else if (statbuf.st_gid != SUDOERS_GID) |
else if (statbuf.st_gid != SUDOERS_GID) |
log_error(0, "%s is owned by gid %ld, should be %d", _PATH_SUDOERS, |
log_error(0, "%s is owned by gid %lu, should be %lu", _PATH_SUDOERS, |
(long) statbuf.st_gid, SUDOERS_GID); |
(unsigned long) statbuf.st_gid, SUDOERS_GID); |
else { |
else { |
/* Solaris sometimes returns EAGAIN so try 10 times */ |
/* Solaris sometimes returns EAGAIN so try 10 times */ |
for (i = 0; i < 10 ; i++) { |
for (i = 0; i < 10 ; i++) { |
|
|
log_error(USE_ERRNO, "can't open %s", _PATH_SUDOERS); |
log_error(USE_ERRNO, "can't open %s", _PATH_SUDOERS); |
} |
} |
|
|
set_perms(PERM_ROOT, 0); /* change back to root */ |
set_perms(PERM_ROOT); /* change back to root */ |
} |
} |
|
|
/* |
/* |
|
|
#ifdef HAVE_SETRLIMIT |
#ifdef HAVE_SETRLIMIT |
struct rlimit rl; |
struct rlimit rl; |
#endif |
#endif |
sigaction_t sa; |
|
|
|
#if defined(RLIMIT_CORE) && !defined(SUDO_DEVEL) |
#if defined(RLIMIT_CORE) && !defined(SUDO_DEVEL) |
/* |
/* |
|
|
for (fd = maxfd; fd > STDERR_FILENO; fd--) |
for (fd = maxfd; fd > STDERR_FILENO; fd--) |
(void) close(fd); |
(void) close(fd); |
|
|
/* Catch children as they die... */ |
/* |
sigemptyset(&sa.sa_mask); |
* Make set_perms point to the correct function. |
sa.sa_flags = SA_RESTART; |
* If we are using setresuid() or setreuid() we only need to set this |
sa.sa_handler = reapchild; |
* once. If we are using POSIX saved uids we will switch to |
(void) sigaction(SIGCHLD, &sa, NULL); |
* set_perms_nosuid after sudoers has been parsed if the "stay_suid" |
|
* option is not set. |
/* Set set_perms pointer to the correct function */ |
*/ |
#if !defined(NO_SAVED_IDS) && defined(_SC_SAVED_IDS) && defined(_SC_VERSION) |
#if defined(HAVE_SETRESUID) || defined(HAVE_SETREUID) |
|
set_perms = set_perms_suid; |
|
#else |
|
# if !defined(NO_SAVED_IDS) && defined(_SC_SAVED_IDS) && defined(_SC_VERSION) |
if (sysconf(_SC_SAVED_IDS) == 1 && sysconf(_SC_VERSION) >= 199009) |
if (sysconf(_SC_SAVED_IDS) == 1 && sysconf(_SC_VERSION) >= 199009) |
set_perms = set_perms_posix; |
set_perms = set_perms_posix; |
else |
else |
#endif |
# endif |
set_perms = set_perms_fallback; |
set_perms = set_perms_nosuid; |
|
#endif /* HAVE_SETRESUID || HAVE_SETREUID */ |
} |
} |
|
|
#ifdef HAVE_LOGIN_CAP_H |
#ifdef HAVE_LOGIN_CAP_H |