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:
4
INSTALL
4
INSTALL
@@ -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.
|
||||
|
@@ -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
|
||||
|
||||
|
15
configure.in
15
configure.in
@@ -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
118
logging.c
@@ -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
38
parse.c
@@ -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);
|
||||
}
|
||||
|
||||
/*
|
||||
|
26
parse.yacc
26
parse.yacc
@@ -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
42
sudo.c
@@ -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
17
sudo.h
@@ -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
|
||||
|
Reference in New Issue
Block a user