2
0
mirror of https://github.com/sudo-project/sudo.git synced 2025-08-31 22:35:10 +00:00

sudoers_lookup() now returns a bitmap instead of an int. This makes it

possible to express things like "failed to validate because user not listed
for this host".  Some thigns that were previously VALIDATE_FOO are now
FLAG_FOO.  This may change later on.

Reorganized code in log_auth() and sudo.c to deal with above changes.

Safer versions of push/pushcp with in the do { ... } while (0) style

parse.yacc now saves info on the stack to allow parse.c to determine
if a user was listed, but not for the host he/she tried to run on.

Added --with-mail-if-no-host option
This commit is contained in:
Todd C. Miller
1999-08-19 16:30:09 +00:00
parent 804e168d90
commit 0598093e2c
9 changed files with 606 additions and 560 deletions

View File

@@ -227,6 +227,10 @@ Special features/options:
Normally, sudo will mail to the "alermail" user if the user invoking
sudo is not in the sudoers file. This option disables that behavior.
--with-mail-if-no-host
Send mail to the "alermail" user if the user exists in the sudoers
file, but is not allowed to run commands on the current host.
--with-mail-if-noperms
Send mail to the "alermail" user if the user is allowed to use sudo but
the command they are trying is not listed in their sudoers file entry.

View File

@@ -453,6 +453,9 @@
/* Define SEND_MAIL_WHEN_NO_USER to send mail when user not in sudoers file */
#undef SEND_MAIL_WHEN_NO_USER
/* Define SEND_MAIL_WHEN_NO_HOST to send mail when not allowed on this host */
#undef SEND_MAIL_WHEN_NO_HOST
/* Define SEND_MAIL_WHEN_NOT_OK to send mail when not allowed to run command */
#undef SEND_MAIL_WHEN_NOT_OK

767
configure vendored

File diff suppressed because it is too large Load Diff

View File

@@ -474,7 +474,7 @@ AC_ARG_WITH(mailsubject, [ --with-mailsubject subject of sudo mail],
esac], AC_DEFINE(MAILSUBJECT, "*** SECURITY information for %h ***"))
AC_MSG_CHECKING(whether to send mail when a user is not in sudoers)
AC_ARG_WITH(mail-if-no-user, [ --without-mail-if-no-user Do not send mail if user not in sudoers],
AC_ARG_WITH(mail-if-no-user, [ --without-mail-if-no-user do not send mail if user not in sudoers],
[case $with_mail_if_no_user in
yes) AC_DEFINE(SEND_MAIL_WHEN_NO_USER)
AC_MSG_RESULT(yes)
@@ -486,6 +486,19 @@ AC_ARG_WITH(mail-if-no-user, [ --without-mail-if-no-user Do not send mail if us
;;
esac], [AC_DEFINE(SEND_MAIL_WHEN_NO_USER) AC_MSG_RESULT(yes)])
AC_MSG_CHECKING(whether to send mail when user listed but not for this host)
AC_ARG_WITH(mail-if-no-host, [ --with-mail-if-no-host send mail if user in sudoers but not for this host],
[case $with_mail_if_no_host in
yes) AC_DEFINE(SEND_MAIL_WHEN_NO_HOST)
AC_MSG_RESULT(yes)
;;
no) AC_MSG_RESULT(no)
;;
*) echo "Unknown argument to --with-mail-if-no-host: $with_mail_if_no_host"
exit 1
;;
esac], AC_MSG_RESULT(no))
AC_MSG_CHECKING(whether to send mail when a user tries a disallowed command)
AC_ARG_WITH(mail-if-noperms, [ --with-mail-if-noperms send mail if user not allowed to run command],
[case $with_mail_if_noperms in

118
logging.c
View File

@@ -69,6 +69,7 @@ static void do_syslog __P((int, char *));
static void do_logfile __P((char *));
#endif
static void send_mail __P((char *));
static void mail_auth __P((int, char *));
#if (LOGGING & SLOG_SYSLOG)
# ifdef BROKEN_SYSLOG
@@ -271,80 +272,51 @@ log_auth(status, inform_user)
char *message;
char *logline;
#if (LOGGING & SLOG_SYSLOG)
int pri = PRI_FAILURE;
int pri;
if (status & VALIDATE_OK)
pri = PRI_SUCCESS;
else
pri = PRI_FAILURE;
#endif /* LOGGING & SLOG_SYSLOG */
/* Set error message, if any. */
switch (status) {
case VALIDATE_OK:
case VALIDATE_OK_NOPASS:
if (status & VALIDATE_OK)
message = "";
break;
case VALIDATE_NO_USER:
else if (status & FLAG_NO_USER)
message = "user NOT in sudoers ; ";
break;
case VALIDATE_NOT_OK:
case VALIDATE_NOT_OK_NOPASS:
else if (status & FLAG_NO_HOST)
message = "user NOT authorized on host ; ";
else if (status & VALIDATE_NOT_OK)
message = "command not allowed ; ";
break;
default:
message = "unknown error ; ";
}
if (user_args)
easprintf(&logline, "%sTTY=%s ; PWD=%s ; USER=%s ; COMMAND=%s %s",
message, user_tty, user_cwd, user_runas, user_cmnd, user_args);
else
easprintf(&logline, "%sTTY=%s ; PWD=%s ; USER=%s ; COMMAND=%s",
message, user_tty, user_cwd, user_runas, user_cmnd);
message = "unknown error ; ";
/*
* Inform the user if they failed to authenticate and send a
* copy of the error via mail if compiled with the appropriate option.
*/
switch (status) {
case VALIDATE_OK:
case VALIDATE_OK_NOPASS:
#if (LOGGING & SLOG_SYSLOG)
pri = PRI_SUCCESS;
#endif /* LOGGING & SLOG_SYSLOG */
#ifdef SEND_MAIL_WHEN_OK
send_mail(logline);
#endif
break;
case VALIDATE_NO_USER:
#ifdef SEND_MAIL_WHEN_NO_USER
send_mail(logline);
#endif
if (inform_user)
easprintf(&logline, "%sTTY=%s ; PWD=%s ; USER=%s ; COMMAND=%s%s%s",
message, user_tty, user_cwd, user_runas, user_cmnd,
user_args ? " " : "", user_args ? user_args : "");
mail_auth(status, logline); /* send mail based on status */
/* Inform the user if they failed to authenticate. */
if (inform_user) {
if (status & FLAG_NO_USER)
(void) fprintf(stderr, "%s is not in the sudoers file. %s",
user_name, "This incident will be reported.\n");
break;
case VALIDATE_NOT_OK:
case VALIDATE_NOT_OK_NOPASS:
#ifdef SEND_MAIL_WHEN_NOT_OK
send_mail(logline);
#endif
if (inform_user) {
else if (status & FLAG_NO_HOST)
(void) fprintf(stderr, "%s is not allowed to run sudo on %s. %s",
user_name, user_shost, "This incident will be reported.\n");
else if (status & VALIDATE_NOT_OK)
(void) fprintf(stderr,
"Sorry, user %s is not allowed to execute '%s",
user_name, user_cmnd);
if (user_args) {
fputc(' ', stderr);
fputs(user_args, stderr);
}
(void) fprintf(stderr, "' as %s on %s.\n", user_runas, user_host);
}
break;
default:
send_mail(logline);
if (inform_user)
"Sorry, user %s is not allowed to execute '%s%s%s' as %s on %s.\n",
user_name, user_cmnd, user_args ? " " : "",
user_args ? user_args : "", user_runas, user_host);
else
(void) fprintf(stderr, "An unknown error has occurred.\n");
break;
}
/*
* Log to syslog and/or a file.
* Log via syslog and/or a file.
*/
#if (LOGGING & SLOG_SYSLOG)
do_syslog(pri, logline);
@@ -550,6 +522,34 @@ send_mail(line)
}
#endif
/*
* Send mail based on the value of "status" and compile-time options.
*/
static void
mail_auth(status, line)
int status;
char *line;
{
int mail_mask;
mail_mask = VALIDATE_ERROR;
#ifdef SEND_MAIL_WHEN_OK
mail_mask |= VALIDATE_OK;
#endif
#ifdef SEND_MAIL_WHEN_NO_USER
mail_mask |= FLAG_NO_USER;
#endif
#ifdef SEND_MAIL_WHEN_NO_HOST
mail_mask |= FLAG_NO_HOST;
#endif
#ifdef SEND_MAIL_WHEN_NOT_OK
mail_mask |= VALIDATE_NOT_OK;
#endif
if ((status & mail_mask) != 0)
send_mail(line);
}
/*
* SIGCHLD sig handler--wait for children as they die.
*/

38
parse.c
View File

@@ -114,7 +114,7 @@ int
sudoers_lookup(check_cmnd)
int check_cmnd;
{
int return_code;
int error;
/* Become sudoers file owner */
set_perms(PERM_SUDOERS, 0);
@@ -133,7 +133,7 @@ sudoers_lookup(check_cmnd)
* Need to be root while stat'ing things in the parser.
*/
set_perms(PERM_ROOT, 0);
return_code = yyparse();
error = yyparse();
/*
* Don't need to keep this open...
@@ -144,18 +144,18 @@ sudoers_lookup(check_cmnd)
/* relinquish extra privs */
set_perms(PERM_USER, 0);
if (return_code || parse_error)
if (error || parse_error)
return(VALIDATE_ERROR);
/*
* Nothing on the top of the stack => user doesn't appear in sudoers.
* Allow anyone to try the psuedo commands "list" and "validate".
* Assume the worst. If the stack is empty the user was
* not mentioned at all.
*/
if (top == 0) {
if (check_cmnd == TRUE)
return(VALIDATE_NO_USER);
else
return(VALIDATE_NOT_OK);
error = VALIDATE_NOT_OK;
if (check_cmnd == TRUE) {
error |= FLAG_NO_HOST;
if (!top)
error |= FLAG_NO_USER;
}
/*
@@ -167,9 +167,9 @@ sudoers_lookup(check_cmnd)
if (check_cmnd == FALSE)
while (top) {
if (host_matches == TRUE) {
/* user may always do validate or list on allowed hosts */
/* User may always validate or list on allowed hosts */
if (no_passwd == TRUE)
return(VALIDATE_OK_NOPASS);
return(VALIDATE_OK | FLAG_NOPASS);
else
return(VALIDATE_OK);
}
@@ -178,6 +178,7 @@ sudoers_lookup(check_cmnd)
else
while (top) {
if (host_matches == TRUE) {
error &= ~FLAG_NO_HOST;
if (runas_matches == TRUE) {
if (cmnd_matches == TRUE) {
/*
@@ -185,13 +186,15 @@ sudoers_lookup(check_cmnd)
* If no passwd required return as such.
*/
if (no_passwd == TRUE)
return(VALIDATE_OK_NOPASS);
return(VALIDATE_OK | FLAG_NOPASS);
else
return(VALIDATE_OK);
} else if (cmnd_matches == FALSE) {
/* User was explicitly denied acces to cmnd on host. */
/*
* User was explicitly denied acces to cmnd on host.
*/
if (no_passwd == TRUE)
return(VALIDATE_NOT_OK_NOPASS);
return(VALIDATE_NOT_OK | FLAG_NOPASS);
else
return(VALIDATE_NOT_OK);
}
@@ -201,10 +204,9 @@ sudoers_lookup(check_cmnd)
}
/*
* We popped everything off the stack and the user was mentioned, but
* not explicitly granted nor denied access.
* The user was not explicitly granted nor denied access.
*/
return(VALIDATE_NOT_OK);
return(error);
}
/*

View File

@@ -112,7 +112,7 @@ struct matchstack *match;
int top = 0, stacksize = 0;
#define push \
{ \
do { \
if (top >= stacksize) { \
while ((stacksize += STACKINCREMENT) < top); \
match = (struct matchstack *) erealloc(match, sizeof(struct matchstack) * stacksize); \
@@ -123,10 +123,10 @@ int top = 0, stacksize = 0;
match[top].runas = -1; \
match[top].nopass = pwdef; \
top++; \
}
} while (0)
#define pushcp \
{ \
do { \
if (top >= stacksize) { \
while ((stacksize += STACKINCREMENT) < top); \
match = (struct matchstack *) erealloc(match, sizeof(struct matchstack) * stacksize); \
@@ -137,7 +137,7 @@ int top = 0, stacksize = 0;
match[top].runas = match[top-1].runas; \
match[top].nopass = match[top-1].nopass; \
top++; \
}
} while (0)
#define pop \
{ \
@@ -358,12 +358,20 @@ cmndspeclist : cmndspec
cmndspec : runasspec nopasswd opcmnd {
/*
* Push the entry onto the stack if it is worth
* saving (or if nothing else is on the stack)
* and clear match status.
* saving and clear cmnd_matches for next cmnd.
*
* We need to save at least one entry on
* the stack so sudoers_lookup() can tell that
* the user was listed in sudoers. Also, we
* need to be able to tell whether or not a
* user was listed for this specific host.
*/
if (user_matches == TRUE && host_matches == TRUE &&
((cmnd_matches != -1 && runas_matches != -1) ||
top == 1))
if (user_matches != -1 && host_matches != -1 &&
cmnd_matches != -1 && runas_matches != -1)
pushcp;
else if (user_matches != -1 && (top == 1 ||
(top == 2 && host_matches != -1 &&
match[0].host == -1)))
pushcp;
cmnd_matches = -1;
}

42
sudo.c
View File

@@ -265,14 +265,17 @@ main(argc, argv)
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));
validated =
sudoers_lookup((sudo_mode != MODE_VALIDATE && sudo_mode != MODE_LIST));
switch (validated) {
case VALIDATE_OK:
/* Require a password unless the NOPASS tag was set. */
if (!(validated & FLAG_NOPASS))
check_user();
/* fallthrough */
case VALIDATE_OK_NOPASS:
if (validated & VALIDATE_ERROR)
log_error(0, "parse error in %s near line %d", _PATH_SUDOERS,
errorlineno);
else 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);
@@ -331,18 +334,11 @@ main(argc, argv)
(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();
} else if ((validated & FLAG_NO_USER) || (validated & FLAG_NO_HOST)) {
log_auth(validated, 1);
exit(1);
break;
case VALIDATE_NOT_OK:
check_user();
case VALIDATE_NOT_OK_NOPASS:
} else if (validated & VALIDATE_NOT_OK) {
#ifndef DONT_LEAK_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
@@ -350,7 +346,6 @@ main(argc, argv)
* is just "no foo in path" since the user can trivially set
* their path to just contain a single dir.
*/
#ifndef DONT_LEAK_PATH_INFO
log_auth(validated,
!(cmnd_status == NOT_FOUND_DOT || cmnd_status == NOT_FOUND));
if (cmnd_status == NOT_FOUND)
@@ -358,19 +353,14 @@ main(argc, argv)
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;
#else
log_auth(validated, 1);
#endif /* DONT_LEAK_PATH_INFO */
case VALIDATE_ERROR:
log_error(0, "parse error in %s around line %d", _PATH_SUDOERS,
errorlineno);
break;
default:
exit(1);
} else {
/* should never get here */
log_auth(validated, 1);
exit(1);
break;
}
exit(0); /* not reached */
}

17
sudo.h
View File

@@ -58,15 +58,16 @@ struct sudo_user {
};
/*
* Return values for sudoers_lookup()
* Also arguments for log_auth()
* Return values for sudoers_lookup(), also used as arguments for log_auth()
* Note: cannot use '0' as a value here.
*/
#define VALIDATE_OK 0x00
#define VALIDATE_OK_NOPASS 0x01
#define VALIDATE_NO_USER 0x02
#define VALIDATE_NOT_OK 0x03
#define VALIDATE_NOT_OK_NOPASS 0x04
#define VALIDATE_ERROR -1
/* XXX - VALIDATE_SUCCESS and VALIDATE_FAILURE instead? */
#define VALIDATE_ERROR 0x01
#define VALIDATE_OK 0x02
#define VALIDATE_NOT_OK 0x04
#define FLAG_NOPASS 0x10
#define FLAG_NO_USER 0x20
#define FLAG_NO_HOST 0x40
/*
* Boolean values