Add runas_check_shell flag to require a runas user to have a valid shell.
Not enabled by default.
This commit is contained in:
1
MANIFEST
1
MANIFEST
@@ -129,6 +129,7 @@ lib/util/getgrouplist.c
|
||||
lib/util/gethostname.c
|
||||
lib/util/getopt_long.c
|
||||
lib/util/gettime.c
|
||||
lib/util/getusershell.c
|
||||
lib/util/gidlist.c
|
||||
lib/util/glob.c
|
||||
lib/util/host_port.c
|
||||
|
@@ -343,6 +343,9 @@
|
||||
/* Define to 1 if you have the `getuserattr' function. */
|
||||
#undef HAVE_GETUSERATTR
|
||||
|
||||
/* Define to 1 if you have the `getusershell' function. */
|
||||
#undef HAVE_GETUSERSHELL
|
||||
|
||||
/* Define to 1 if you have the `getutid' function. */
|
||||
#undef HAVE_GETUTID
|
||||
|
||||
|
26
configure
vendored
26
configure
vendored
@@ -19397,6 +19397,32 @@ esac
|
||||
fi
|
||||
done
|
||||
|
||||
for ac_func in getusershell
|
||||
do :
|
||||
ac_fn_c_check_func "$LINENO" "getusershell" "ac_cv_func_getusershell"
|
||||
if test "x$ac_cv_func_getusershell" = xyes; then :
|
||||
cat >>confdefs.h <<_ACEOF
|
||||
#define HAVE_GETUSERSHELL 1
|
||||
_ACEOF
|
||||
|
||||
else
|
||||
|
||||
case " $LIBOBJS " in
|
||||
*" getusershell.$ac_objext "* ) ;;
|
||||
*) LIBOBJS="$LIBOBJS getusershell.$ac_objext"
|
||||
;;
|
||||
esac
|
||||
|
||||
|
||||
for _sym in sudo_getusershell; do
|
||||
COMPAT_EXP="${COMPAT_EXP}${_sym}
|
||||
"
|
||||
done
|
||||
|
||||
|
||||
fi
|
||||
done
|
||||
|
||||
for ac_func in reallocarray
|
||||
do :
|
||||
ac_fn_c_check_func "$LINENO" "reallocarray" "ac_cv_func_reallocarray"
|
||||
|
@@ -3,7 +3,7 @@ dnl Use the top-level autogen.sh script to generate configure and config.h.in
|
||||
dnl
|
||||
dnl SPDX-License-Identifier: ISC
|
||||
dnl
|
||||
dnl Copyright (c) 1994-1996, 1998-2018 Todd C. Miller <Todd.Miller@sudo.ws>
|
||||
dnl Copyright (c) 1994-1996, 1998-2019 Todd C. Miller <Todd.Miller@sudo.ws>
|
||||
dnl
|
||||
dnl Permission to use, copy, modify, and distribute this software for any
|
||||
dnl purpose with or without fee is hereby granted, provided that the above
|
||||
@@ -2565,6 +2565,10 @@ AC_CHECK_FUNCS([getdelim], [], [
|
||||
SUDO_APPEND_COMPAT_EXP(sudo_getdelim)
|
||||
COMPAT_TEST_PROGS="${COMPAT_TEST_PROGS}${COMPAT_TEST_PROGS+ }getdelim_test"
|
||||
])
|
||||
AC_CHECK_FUNCS([getusershell], [], [
|
||||
AC_LIBOBJ(getusershell)
|
||||
SUDO_APPEND_COMPAT_EXP(sudo_getusershell)
|
||||
])
|
||||
AC_CHECK_FUNCS([reallocarray], [], [
|
||||
AC_LIBOBJ(reallocarray)
|
||||
SUDO_APPEND_COMPAT_EXP(sudo_reallocarray)
|
||||
|
@@ -25,7 +25,7 @@
|
||||
.nr BA @BAMAN@
|
||||
.nr LC @LCMAN@
|
||||
.nr PS @PSMAN@
|
||||
.TH "SUDOERS" "@mansectform@" "December 8, 2019" "Sudo @PACKAGE_VERSION@" "File Formats Manual"
|
||||
.TH "SUDOERS" "@mansectform@" "December 9, 2019" "Sudo @PACKAGE_VERSION@" "File Formats Manual"
|
||||
.nh
|
||||
.if n .ad l
|
||||
.SH "NAME"
|
||||
@@ -2969,6 +2969,28 @@ Older versions of
|
||||
\fBsudo\fR
|
||||
always allowed matching of unknown user and group IDs.
|
||||
.TP 18n
|
||||
runas_check_shell
|
||||
.br
|
||||
If enabled,
|
||||
\fBsudo\fR
|
||||
will only run commands as a user whose shell appears in the
|
||||
\fI/etc/shells\fR
|
||||
file, even if the invoking user's
|
||||
\fRRunas_List\fR
|
||||
would otherwise permit it.
|
||||
If no
|
||||
\fI/etc/shells\fR
|
||||
file is present, a system-dependent list of built-in default shells is used.
|
||||
On many operating systems, system users such as
|
||||
\(lqbin\(rq,
|
||||
do not have a valid shell and this flag can be used to prevent
|
||||
commands from being run as those users.
|
||||
This flag is
|
||||
\fIoff\fR
|
||||
by default.
|
||||
.sp
|
||||
This setting is only supported by version 1.8.30 or higher.
|
||||
.TP 18n
|
||||
runaspw
|
||||
If set,
|
||||
\fBsudo\fR
|
||||
|
@@ -24,7 +24,7 @@
|
||||
.nr BA @BAMAN@
|
||||
.nr LC @LCMAN@
|
||||
.nr PS @PSMAN@
|
||||
.Dd December 8, 2019
|
||||
.Dd December 9, 2019
|
||||
.Dt SUDOERS @mansectform@
|
||||
.Os Sudo @PACKAGE_VERSION@
|
||||
.Sh NAME
|
||||
@@ -2794,6 +2794,26 @@ This setting is only supported by version 1.8.30 or higher.
|
||||
Older versions of
|
||||
.Nm sudo
|
||||
always allowed matching of unknown user and group IDs.
|
||||
.It runas_check_shell
|
||||
If enabled,
|
||||
.Nm sudo
|
||||
will only run commands as a user whose shell appears in the
|
||||
.Pa /etc/shells
|
||||
file, even if the invoking user's
|
||||
.Li Runas_List
|
||||
would otherwise permit it.
|
||||
If no
|
||||
.Pa /etc/shells
|
||||
file is present, a system-dependent list of built-in default shells is used.
|
||||
On many operating systems, system users such as
|
||||
.Dq bin ,
|
||||
do not have a valid shell and this flag can be used to prevent
|
||||
commands from being run as those users.
|
||||
This flag is
|
||||
.Em off
|
||||
by default.
|
||||
.Pp
|
||||
This setting is only supported by version 1.8.30 or higher.
|
||||
.It runaspw
|
||||
If set,
|
||||
.Nm sudo
|
||||
|
@@ -413,6 +413,17 @@ __dso_public ssize_t sudo_getdelim(char **bufp, size_t *bufsizep, int delim, FIL
|
||||
# undef getdelim
|
||||
# define getdelim(_a, _b, _c, _d) sudo_getdelim((_a), (_b), (_c), (_d))
|
||||
#endif /* HAVE_GETDELIM */
|
||||
#ifndef HAVE_GETUSERSHELL
|
||||
__dso_public char *sudo_getusershell(void);
|
||||
# undef getusershell
|
||||
# define getusershell() sudo_getusershell()
|
||||
__dso_public void sudo_setusershell(void);
|
||||
# undef setusershell
|
||||
# define setusershell() sudo_setusershell()
|
||||
__dso_public void sudo_endusershell(void);
|
||||
# undef endusershell
|
||||
# define endusershell() sudo_endusershell()
|
||||
#endif /* HAVE_GETUSERSHELL */
|
||||
#ifndef HAVE_UTIMENSAT
|
||||
__dso_public int sudo_utimensat(int fd, const char *file, const struct timespec *times, int flag);
|
||||
# undef utimensat
|
||||
|
@@ -706,6 +706,18 @@ gettime.i: $(srcdir)/gettime.c $(incdir)/compat/stdbool.h \
|
||||
$(CC) -E -o $@ $(CPPFLAGS) $<
|
||||
gettime.plog: gettime.i
|
||||
rm -f $@; pvs-studio --cfg $(PVS_CFG) --sourcetree-root $(top_srcdir) --skip-cl-exe yes --source-file $(srcdir)/gettime.c --i-file $< --output-file $@
|
||||
getusershell.lo: $(srcdir)/getusershell.c $(incdir)/compat/stdbool.h \
|
||||
$(incdir)/sudo_compat.h $(incdir)/sudo_debug.h \
|
||||
$(incdir)/sudo_gettext.h $(incdir)/sudo_queue.h \
|
||||
$(incdir)/sudo_util.h $(top_builddir)/config.h
|
||||
$(LIBTOOL) $(LTFLAGS) --mode=compile $(CC) -c -o $@ $(CPPFLAGS) $(CFLAGS) $(ASAN_CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(srcdir)/getusershell.c
|
||||
getusershell.i: $(srcdir)/getusershell.c $(incdir)/compat/stdbool.h \
|
||||
$(incdir)/sudo_compat.h $(incdir)/sudo_debug.h \
|
||||
$(incdir)/sudo_gettext.h $(incdir)/sudo_queue.h \
|
||||
$(incdir)/sudo_util.h $(top_builddir)/config.h
|
||||
$(CC) -E -o $@ $(CPPFLAGS) $<
|
||||
getusershell.plog: getusershell.i
|
||||
rm -f $@; pvs-studio --cfg $(PVS_CFG) --sourcetree-root $(top_srcdir) --skip-cl-exe yes --source-file $(srcdir)/getusershell.c --i-file $< --output-file $@
|
||||
gidlist.lo: $(srcdir)/gidlist.c $(incdir)/compat/stdbool.h \
|
||||
$(incdir)/sudo_compat.h $(incdir)/sudo_debug.h \
|
||||
$(incdir)/sudo_fatal.h $(incdir)/sudo_gettext.h \
|
||||
|
138
lib/util/getusershell.c
Normal file
138
lib/util/getusershell.c
Normal file
@@ -0,0 +1,138 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: ISC
|
||||
*
|
||||
* Copyright (c) 2019 Todd C. Miller <Todd.Miller@courtesan.com>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This is an open source non-commercial project. Dear PVS-Studio, please check it.
|
||||
* PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
|
||||
#define DEFAULT_TEXT_DOMAIN "sudo"
|
||||
#include "sudo_gettext.h" /* must be included before sudo_compat.h */
|
||||
|
||||
#include "sudo_compat.h"
|
||||
#include "sudo_debug.h"
|
||||
#include "sudo_util.h"
|
||||
|
||||
static char **allowed_shells, **current_shell;
|
||||
static char *default_shells[] = {
|
||||
"/bin/sh",
|
||||
"/bin/ksh",
|
||||
"/bin/ksh93",
|
||||
"/bin/bash",
|
||||
"/bin/dash",
|
||||
"/bin/zsh",
|
||||
"/bin/csh",
|
||||
"/bin/tcsh",
|
||||
NULL
|
||||
};
|
||||
|
||||
static char **
|
||||
read_shells(void)
|
||||
{
|
||||
size_t maxshells = 16, nshells = 0;
|
||||
size_t linesize = 0;
|
||||
char *line = NULL;
|
||||
FILE *fp;
|
||||
debug_decl(read_shells, SUDO_DEBUG_UTIL)
|
||||
|
||||
if ((fp = fopen("/etc/shells", "r")) == NULL)
|
||||
goto bad;
|
||||
|
||||
free(allowed_shells);
|
||||
allowed_shells = reallocarray(NULL, maxshells, sizeof(char *));
|
||||
if (allowed_shells == NULL)
|
||||
goto bad;
|
||||
|
||||
while (sudo_parseln(&line, &linesize, NULL, fp, PARSELN_CONT_IGN) != -1) {
|
||||
if (nshells + 1 >= maxshells) {
|
||||
char **new_shells;
|
||||
|
||||
new_shells = reallocarray(NULL, maxshells + 16, sizeof(char *));
|
||||
if (new_shells == NULL)
|
||||
goto bad;
|
||||
allowed_shells = new_shells;
|
||||
maxshells += 16;
|
||||
}
|
||||
if ((allowed_shells[nshells] = strdup(line)) == NULL)
|
||||
goto bad;
|
||||
nshells++;
|
||||
}
|
||||
allowed_shells[nshells] = NULL;
|
||||
|
||||
free(line);
|
||||
fclose(fp);
|
||||
debug_return_ptr(allowed_shells);
|
||||
bad:
|
||||
free(line);
|
||||
if (fp != NULL)
|
||||
fclose(fp);
|
||||
while (nshells != 0)
|
||||
free(allowed_shells[--nshells]);
|
||||
free(allowed_shells);
|
||||
allowed_shells = NULL;
|
||||
debug_return_ptr(default_shells);
|
||||
}
|
||||
|
||||
void
|
||||
sudo_setusershell(void)
|
||||
{
|
||||
debug_decl(setusershell, SUDO_DEBUG_UTIL)
|
||||
|
||||
current_shell = read_shells();
|
||||
|
||||
debug_return;
|
||||
}
|
||||
|
||||
void
|
||||
sudo_endusershell(void)
|
||||
{
|
||||
debug_decl(endusershell, SUDO_DEBUG_UTIL)
|
||||
|
||||
if (allowed_shells != NULL) {
|
||||
char **shell;
|
||||
|
||||
for (shell = allowed_shells; *shell != NULL; shell++)
|
||||
free(*shell);
|
||||
free(allowed_shells);
|
||||
allowed_shells = NULL;
|
||||
}
|
||||
current_shell = NULL;
|
||||
|
||||
debug_return;
|
||||
}
|
||||
|
||||
char *
|
||||
sudo_getusershell(void)
|
||||
{
|
||||
debug_decl(getusershell, SUDO_DEBUG_UTIL)
|
||||
|
||||
if (current_shell == NULL)
|
||||
current_shell = read_shells();
|
||||
|
||||
debug_return_str(*current_shell++);
|
||||
}
|
2
mkdep.pl
2
mkdep.pl
@@ -116,7 +116,7 @@ sub mkdep {
|
||||
# XXX - fill in AUTH_OBJS from contents of the auth dir instead
|
||||
$makefile =~ s:\@AUTH_OBJS\@:afs.lo aix_auth.lo bsdauth.lo dce.lo fwtk.lo getspwuid.lo kerb5.lo pam.lo passwd.lo rfc1938.lo secureware.lo securid5.lo sia.lo:;
|
||||
$makefile =~ s:\@DIGEST\@:digest.lo digest_openssl.lo digest_gcrypt.lo:;
|
||||
$makefile =~ s:\@LTLIBOBJS\@:arc4random.lo arc4random_uniform.lo closefrom.lo dup3.lo fchmodat.lo fstatat.lo fnmatch.lo getaddrinfo.lo getcwd.lo getentropy.lo getgrouplist.lo getdelim.lo getopt_long.lo glob.lo inet_ntop_lo inet_pton.lo isblank.lo memrchr.lo memset_s.lo mksiglist.lo mksigname.lo mktemp.lo nanosleep.lo openat.lo pipe2.lo pw_dup.lo reallocarray.lo sha2.lo sig2str.lo siglist.lo signame.lo snprintf.lo str2sig.lo strlcat.lo strlcpy.lo strndup.lo strnlen.lo strsignal.lo unlinkat.lo utimens.lo vsyslog.lo:;
|
||||
$makefile =~ s:\@LTLIBOBJS\@:arc4random.lo arc4random_uniform.lo closefrom.lo dup3.lo fchmodat.lo fstatat.lo fnmatch.lo getaddrinfo.lo getcwd.lo getentropy.lo getgrouplist.lo getdelim.lo getopt_long.lo getusershell.lo glob.lo inet_ntop_lo inet_pton.lo isblank.lo memrchr.lo memset_s.lo mksiglist.lo mksigname.lo mktemp.lo nanosleep.lo openat.lo pipe2.lo pw_dup.lo reallocarray.lo sha2.lo sig2str.lo siglist.lo signame.lo snprintf.lo str2sig.lo strlcat.lo strlcpy.lo strndup.lo strnlen.lo strsignal.lo unlinkat.lo utimens.lo vsyslog.lo:;
|
||||
|
||||
# Parse OBJS lines
|
||||
my %objs;
|
||||
|
@@ -328,3 +328,28 @@ get_authpw(int mode)
|
||||
|
||||
debug_return_ptr(pw);
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns true if the specified shell is allowed by /etc/shells, else false.
|
||||
*/
|
||||
bool
|
||||
check_user_shell(const struct passwd *pw)
|
||||
{
|
||||
const char *shell;
|
||||
debug_decl(check_user_shell, SUDOERS_DEBUG_AUTH)
|
||||
|
||||
if (!def_runas_check_shell)
|
||||
debug_return_bool(true);
|
||||
|
||||
sudo_debug_printf(SUDO_DEBUG_INFO,
|
||||
"%s: checking /etc/shells for %s", __func__, pw->pw_shell);
|
||||
|
||||
setusershell();
|
||||
while ((shell = getusershell()) != NULL) {
|
||||
if (strcmp(shell, pw->pw_shell) == 0)
|
||||
debug_return_bool(true);
|
||||
}
|
||||
endusershell();
|
||||
|
||||
debug_return_bool(false);
|
||||
}
|
||||
|
@@ -529,6 +529,10 @@ struct sudo_defs_types sudo_defs_table[] = {
|
||||
"runas_allow_unknown_id", T_FLAG,
|
||||
N_("Allow the use of unknown runas user and/or group ID"),
|
||||
NULL,
|
||||
}, {
|
||||
"runas_check_shell", T_FLAG,
|
||||
N_("Only permit running commands as a user with a valid shell"),
|
||||
NULL,
|
||||
}, {
|
||||
NULL, 0, NULL
|
||||
}
|
||||
|
@@ -244,6 +244,8 @@
|
||||
#define def_log_server_peer_key (sudo_defs_table[I_LOG_SERVER_PEER_KEY].sd_un.str)
|
||||
#define I_RUNAS_ALLOW_UNKNOWN_ID 122
|
||||
#define def_runas_allow_unknown_id (sudo_defs_table[I_RUNAS_ALLOW_UNKNOWN_ID].sd_un.flag)
|
||||
#define I_RUNAS_CHECK_SHELL 123
|
||||
#define def_runas_check_shell (sudo_defs_table[I_RUNAS_CHECK_SHELL].sd_un.flag)
|
||||
|
||||
enum def_tuple {
|
||||
never,
|
||||
|
@@ -384,3 +384,7 @@ log_server_peer_key
|
||||
runas_allow_unknown_id
|
||||
T_FLAG
|
||||
"Allow the use of unknown runas user and/or group ID"
|
||||
runas_check_shell
|
||||
T_FLAG
|
||||
"Only permit running commands as a user with a valid shell"
|
||||
|
||||
|
@@ -295,7 +295,7 @@ sudoers_policy_main(int argc, char * const argv[], int pwflag, char *env_add[],
|
||||
/* Not an audit event. */
|
||||
sudo_warnx(U_("sudoers specifies that root is not allowed to sudo"));
|
||||
goto bad;
|
||||
}
|
||||
}
|
||||
|
||||
if (!set_perms(PERM_INITIAL))
|
||||
goto bad;
|
||||
@@ -434,6 +434,13 @@ sudoers_policy_main(int argc, char * const argv[], int pwflag, char *env_add[],
|
||||
goto bad;
|
||||
}
|
||||
|
||||
/* Check runas user's shell. */
|
||||
if (!check_user_shell(runas_pw)) {
|
||||
log_warningx(SLOG_RAW_MSG, N_("invalid shell for user %s: %s"),
|
||||
runas_pw->pw_name, runas_pw->pw_shell);
|
||||
goto bad;
|
||||
}
|
||||
|
||||
/*
|
||||
* We don't reset the environment for sudoedit or if the user
|
||||
* specified the -E command line flag and they have setenv privs.
|
||||
|
@@ -256,6 +256,7 @@ int find_path(const char *infile, char **outfile, struct stat *sbp,
|
||||
|
||||
/* check.c */
|
||||
int check_user(int validate, int mode);
|
||||
bool check_user_shell(const struct passwd *pw);
|
||||
bool user_is_exempt(void);
|
||||
|
||||
/* prompt.c */
|
||||
|
Reference in New Issue
Block a user