Auto sync2gitlab import of sudo-1.8.29-8.el8.src.rpm

This commit is contained in:
James Antill 2022-05-26 14:35:23 -04:00
parent ec397f87f6
commit 3f984eed16
29 changed files with 4275 additions and 1 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
/sudo-1.8.29.tar.gz

1
EMPTY
View File

@ -1 +0,0 @@

1
sources Normal file
View File

@ -0,0 +1 @@
SHA512 (sudo-1.8.29.tar.gz) = ea780922b2afb47df4df4b533fb355fd916cb18a6bfd13c7ca36a25b03ef585d805648c6fa85692bea363b1f83664ac3bc622f99bcd149b3a86f70522eb4d340

11
sudo-1.6.7p5-strip.patch Normal file
View File

@ -0,0 +1,11 @@
--- sudo-1.6.7p5/install-sh.strip 2005-07-21 14:28:25.000000000 +0200
+++ sudo-1.6.7p5/install-sh 2005-07-21 14:29:18.000000000 +0200
@@ -138,7 +138,7 @@
fi
;;
X-s)
- STRIPIT=true
+ #STRIPIT=true
;;
X--)
shift

View File

@ -0,0 +1,27 @@
From 44a602b49365969e56c63c9f12eda197e951302f Mon Sep 17 00:00:00 2001
From: Tomas Sykora <tosykora@redhat.com>
Date: Fri, 19 Aug 2016 14:07:35 +0200
Subject: [PATCH 02/10] Added "Enviroment debugging" message
rebased from:
Patch2: sudo-1.7.2p1-envdebug.patch
---
configure.ac | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/configure.ac b/configure.ac
index 9feddfd..39a2d86 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1390,7 +1390,7 @@ AC_ARG_ENABLE(env_debug,
[AS_HELP_STRING([--enable-env-debug], [Whether to enable environment debugging.])],
[ case "$enableval" in
yes) AC_MSG_RESULT(yes)
- AC_DEFINE(ENV_DEBUG)
+ AC_DEFINE(ENV_DEBUG, [], [Environment debugging.])
;;
no) AC_MSG_RESULT(no)
;;
--
2.7.4

View File

@ -0,0 +1,89 @@
diff -up ./plugins/sudoers/cvtsudoers.c.legacy-processing ./plugins/sudoers/cvtsudoers.c
--- ./plugins/sudoers/cvtsudoers.c.legacy-processing 2019-10-28 13:28:52.000000000 +0100
+++ ./plugins/sudoers/cvtsudoers.c 2019-10-30 13:32:43.309480623 +0100
@@ -347,6 +347,15 @@ main(int argc, char *argv[])
sudo_fatalx("error: unhandled input %d", input_format);
}
+ /*
+ * cvtsudoers group filtering doesn't work if def_match_group_by_gid
+ * is set to true by default (at compile-time). It cannot be set to false
+ * because cvtsudoers doesn't apply the parsed Defaults.
+ *
+ * Related: sudo-1.8.23-legacy-group-processing.patch
+ */
+ def_match_group_by_gid = def_legacy_group_processing = false;
+
/* Apply filters. */
filter_userspecs(&parsed_policy, conf);
filter_defaults(&parsed_policy, conf);
diff -up ./plugins/sudoers/defaults.c.legacy-processing ./plugins/sudoers/defaults.c
--- ./plugins/sudoers/defaults.c.legacy-processing 2019-10-28 13:28:52.000000000 +0100
+++ ./plugins/sudoers/defaults.c 2019-10-30 13:32:43.309480623 +0100
@@ -93,6 +93,7 @@ static struct early_default early_defaul
{ I_FQDN },
#endif
{ I_MATCH_GROUP_BY_GID },
+ { I_LEGACY_GROUP_PROCESSING },
{ I_GROUP_PLUGIN },
{ I_RUNAS_DEFAULT },
{ I_SUDOERS_LOCALE },
@@ -494,6 +495,8 @@ init_defaults(void)
}
/* First initialize the flags. */
+ def_legacy_group_processing = true;
+ def_match_group_by_gid = true;
#ifdef LONG_OTP_PROMPT
def_long_otp_prompt = true;
#endif
diff -up ./plugins/sudoers/def_data.c.legacy-processing ./plugins/sudoers/def_data.c
--- ./plugins/sudoers/def_data.c.legacy-processing 2019-10-30 13:32:43.309480623 +0100
+++ ./plugins/sudoers/def_data.c 2019-10-30 13:37:25.914602825 +0100
@@ -506,6 +506,10 @@ struct sudo_defs_types sudo_defs_table[]
N_("Log when a command is denied by sudoers"),
NULL,
}, {
+ "legacy_group_processing", T_FLAG,
+ N_("Don't pre-resolve all group names"),
+ NULL,
+ }, {
NULL, 0, NULL
}
};
diff -up ./plugins/sudoers/def_data.h.legacy-processing ./plugins/sudoers/def_data.h
--- ./plugins/sudoers/def_data.h.legacy-processing 2019-10-30 13:32:43.310480638 +0100
+++ ./plugins/sudoers/def_data.h 2019-10-30 13:40:59.651713757 +0100
@@ -232,6 +232,8 @@
#define def_log_allowed (sudo_defs_table[I_LOG_ALLOWED].sd_un.flag)
#define I_LOG_DENIED 116
#define def_log_denied (sudo_defs_table[I_LOG_DENIED].sd_un.flag)
+#define I_LEGACY_GROUP_PROCESSING 117
+#define def_legacy_group_processing (sudo_defs_table[I_LEGACY_GROUP_PROCESSING].sd_un.flag)
enum def_tuple {
never,
diff -up ./plugins/sudoers/def_data.in.legacy-processing ./plugins/sudoers/def_data.in
--- ./plugins/sudoers/def_data.in.legacy-processing 2019-10-30 13:32:43.310480638 +0100
+++ ./plugins/sudoers/def_data.in 2019-10-30 13:42:20.915896239 +0100
@@ -366,3 +366,6 @@ log_allowed
log_denied
T_FLAG
"Log when a command is denied by sudoers"
+legacy_group_processing
+ T_FLAG
+ "Don't pre-resolve all group names"
diff -up ./plugins/sudoers/sudoers.c.legacy-processing ./plugins/sudoers/sudoers.c
--- ./plugins/sudoers/sudoers.c.legacy-processing 2019-10-28 13:28:53.000000000 +0100
+++ ./plugins/sudoers/sudoers.c 2019-10-30 13:32:43.310480638 +0100
@@ -221,6 +221,10 @@ sudoers_policy_init(void *info, char * c
if (set_loginclass(runas_pw ? runas_pw : sudo_user.pw))
ret = true;
+ if (!def_match_group_by_gid || !def_legacy_group_processing) {
+ def_match_group_by_gid = false;
+ def_legacy_group_processing = false;
+ }
cleanup:
if (!restore_perms())
ret = -1;

View File

@ -0,0 +1,60 @@
diff -up ./plugins/sudoers/def_data.c.nowait ./plugins/sudoers/def_data.c
--- ./plugins/sudoers/def_data.c.nowait 2019-10-30 13:43:48.376168944 +0100
+++ ./plugins/sudoers/def_data.c 2019-10-30 13:43:48.378168973 +0100
@@ -510,6 +510,10 @@ struct sudo_defs_types sudo_defs_table[]
N_("Don't pre-resolve all group names"),
NULL,
}, {
+ "cmnd_no_wait", T_FLAG,
+ N_("Don't fork and wait for the command to finish, just exec it"),
+ NULL,
+ }, {
NULL, 0, NULL
}
};
diff -up ./plugins/sudoers/def_data.h.nowait ./plugins/sudoers/def_data.h
--- ./plugins/sudoers/def_data.h.nowait 2019-10-30 13:43:48.378168973 +0100
+++ ./plugins/sudoers/def_data.h 2019-10-30 13:45:38.425770365 +0100
@@ -234,6 +234,8 @@
#define def_log_denied (sudo_defs_table[I_LOG_DENIED].sd_un.flag)
#define I_LEGACY_GROUP_PROCESSING 117
#define def_legacy_group_processing (sudo_defs_table[I_LEGACY_GROUP_PROCESSING].sd_un.flag)
+#define I_CMND_NO_WAIT 118
+#define def_cmnd_no_wait (sudo_defs_table[I_CMND_NO_WAIT].sd_un.flag)
enum def_tuple {
never,
diff -up ./plugins/sudoers/def_data.in.nowait ./plugins/sudoers/def_data.in
--- ./plugins/sudoers/def_data.in.nowait 2019-10-30 13:43:48.376168944 +0100
+++ ./plugins/sudoers/def_data.in 2019-10-30 13:43:48.379168987 +0100
@@ -369,3 +369,6 @@ log_denied
legacy_group_processing
T_FLAG
"Don't pre-resolve all group names"
+cmnd_no_wait
+ T_FLAG
+ "Don't fork and wait for the command to finish, just exec it"
diff -up ./plugins/sudoers/sudoers.c.nowait ./plugins/sudoers/sudoers.c
--- ./plugins/sudoers/sudoers.c.nowait 2019-10-30 13:43:48.376168944 +0100
+++ ./plugins/sudoers/sudoers.c 2019-10-30 13:43:48.379168987 +0100
@@ -225,6 +225,20 @@ sudoers_policy_init(void *info, char * c
def_match_group_by_gid = false;
def_legacy_group_processing = false;
}
+
+ /*
+ * Emulate cmnd_no_wait option by disabling PAM session, PTY allocation
+ * and I/O logging. This will cause sudo to execute the given command
+ * directly instead of forking a separate process for it.
+ */
+ if (def_cmnd_no_wait) {
+ def_pam_setcred = false;
+ def_pam_session = false;
+ def_use_pty = false;
+ def_log_input = false;
+ def_log_output = false;
+ }
+
cleanup:
if (!restore_perms())
ret = -1;

View File

@ -0,0 +1,32 @@
diff -up sudo-1.8.23/doc/Makefile.in.sudoldapconfman sudo-1.8.23/doc/Makefile.in
--- sudo-1.8.23/doc/Makefile.in.sudoldapconfman 2018-04-29 21:59:31.000000000 +0200
+++ sudo-1.8.23/doc/Makefile.in 2018-05-17 13:56:24.693651178 +0200
@@ -345,10 +345,16 @@ install-doc: install-dirs
rm -f $(DESTDIR)$(mandirsu)/sudoedit.$(mansectsu)$(MANCOMPRESSEXT); \
echo ln -s sudo.$(mansectsu)$(MANCOMPRESSEXT) $(DESTDIR)$(mandirsu)/sudoedit.$(mansectsu)$(MANCOMPRESSEXT); \
ln -s sudo.$(mansectsu)$(MANCOMPRESSEXT) $(DESTDIR)$(mandirsu)/sudoedit.$(mansectsu)$(MANCOMPRESSEXT); \
+ rm -f $(DESTDIR)$(mandirform)/sudo-ldap.conf.$(mansectform)$(MANCOMPRESSEXT); \
+ echo ln -s sudoers.ldap.$(mansectform)$(MANCOMPRESSEXT) $(DESTDIR)$(mandirform)/sudo-ldap.conf.$(mansectform)$(MANCOMPRESSEXT); \
+ ln -s sudoers.ldap.$(mansectform)$(MANCOMPRESSEXT) $(DESTDIR)$(mandirform)/sudo-ldap.conf.$(mansectform)$(MANCOMPRESSEXT); \
else \
rm -f $(DESTDIR)$(mandirsu)/sudoedit.$(mansectsu); \
echo ln -s sudo.$(mansectsu) $(DESTDIR)$(mandirsu)/sudoedit.$(mansectsu); \
ln -s sudo.$(mansectsu) $(DESTDIR)$(mandirsu)/sudoedit.$(mansectsu); \
+ rm -f $(DESTDIR)$(mandirform)/sudo-ldap.conf.$(mansectform); \
+ echo ln -s sudoers.ldap.$(mansectform) $(DESTDIR)$(mandirform)/sudo-ldap.conf.$(mansectform); \
+ ln -s sudoers.ldap.$(mansectform) $(DESTDIR)$(mandirform)/sudo-ldap.conf.$(mansectform); \
fi
install-plugin:
@@ -363,8 +369,9 @@ uninstall:
$(DESTDIR)$(mandirsu)/visudo.$(mansectsu) \
$(DESTDIR)$(mandirform)/sudo.conf.$(mansectform) \
$(DESTDIR)$(mandirform)/sudoers.$(mansectform) \
- $(DESTDIR)$(mandirform)/sudoers_timestamp.$(mansectform)
- $(DESTDIR)$(mandirform)/sudoers.ldap.$(mansectform)
+ $(DESTDIR)$(mandirform)/sudoers_timestamp.$(mansectform) \
+ $(DESTDIR)$(mandirform)/sudoers.ldap.$(mansectform) \
+ $(DESTDIR)$(mandirform)/sudo-ldap.conf.$(mansectform)
splint:

View File

@ -0,0 +1,77 @@
diff -up ./src/tgetpass.c.CVE-2019-18634 ./src/tgetpass.c
--- ./src/tgetpass.c.CVE-2019-18634 2019-10-28 13:27:39.000000000 +0100
+++ ./src/tgetpass.c 2020-02-05 14:08:27.516101197 +0100
@@ -61,7 +61,7 @@ enum tgetpass_errval {
static volatile sig_atomic_t signo[NSIG];
static void tgetpass_handler(int);
-static char *getln(int, char *, size_t, int, enum tgetpass_errval *);
+static char *getln(int, char *, size_t, bool, enum tgetpass_errval *);
static char *sudo_askpass(const char *, const char *);
static int
@@ -125,6 +125,7 @@ tgetpass(const char *prompt, int timeout
static char buf[SUDO_CONV_REPL_MAX + 1];
int i, input, output, save_errno, ttyfd;
bool need_restart, neednl = false;
+ bool feedback = ISSET(flags, TGP_MASK);
enum tgetpass_errval errval;
debug_decl(tgetpass, SUDO_DEBUG_CONV)
@@ -180,7 +181,7 @@ restart:
*/
if (!ISSET(flags, TGP_ECHO)) {
for (;;) {
- if (ISSET(flags, TGP_MASK))
+ if (feedback)
neednl = sudo_term_cbreak(input);
else
neednl = sudo_term_noecho(input);
@@ -194,6 +195,9 @@ restart:
}
}
}
+ /* Only use feedback mode when we can disable echo. */
+ if (!neednl)
+ feedback = false;
/*
* Catch signals that would otherwise cause the user to end
@@ -224,7 +228,7 @@ restart:
if (timeout > 0)
alarm(timeout);
- pass = getln(input, buf, sizeof(buf), ISSET(flags, TGP_MASK), &errval);
+ pass = getln(input, buf, sizeof(buf), feedback, &errval);
alarm(0);
save_errno = errno;
@@ -360,7 +364,7 @@ sudo_askpass(const char *askpass, const
extern int sudo_term_eof, sudo_term_erase, sudo_term_kill;
static char *
-getln(int fd, char *buf, size_t bufsiz, int feedback,
+getln(int fd, char *buf, size_t bufsiz, bool feedback,
enum tgetpass_errval *errval)
{
size_t left = bufsiz;
@@ -389,15 +393,15 @@ getln(int fd, char *buf, size_t bufsiz,
while (cp > buf) {
if (write(fd, "\b \b", 3) == -1)
break;
- --cp;
+ cp--;
}
+ cp = buf;
left = bufsiz;
continue;
} else if (c == sudo_term_erase) {
if (cp > buf) {
- if (write(fd, "\b \b", 3) == -1)
- break;
- --cp;
+ ignore_result(write(fd, "\b \b", 3));
+ cp--;
left++;
}
continue;

View File

@ -0,0 +1,169 @@
diff -up ./doc/sudoers.man.in.CVE-2019-19232 ./doc/sudoers.man.in
--- ./doc/sudoers.man.in.CVE-2019-19232 2019-10-28 13:28:52.000000000 +0100
+++ ./doc/sudoers.man.in 2020-01-14 15:34:46.908027286 +0100
@@ -2942,6 +2942,23 @@ This flag is
\fIoff\fR
by default.
.TP 18n
+runas_allow_unknown_id
+If enabled, allow matching of runas user and group IDs that are
+not present in the password or group databases.
+In addition to explicitly matching unknown user or group IDs in a
+\fRRunas_List\fR,
+this option also allows the
+\fBALL\fR
+alias to match unknown IDs.
+This flag is
+\fIoff\fR
+by default.
+.sp
+This setting is only supported by version 1.8.29 or higher.
+Older versions of
+\fBsudo\fR
+always allowed matching of unknown user and group IDs.
+.TP 18n
runaspw
If set,
\fBsudo\fR
diff -up ./doc/sudoers.mdoc.in.CVE-2019-19232 ./doc/sudoers.mdoc.in
--- ./doc/sudoers.mdoc.in.CVE-2019-19232 2019-10-28 13:28:52.000000000 +0100
+++ ./doc/sudoers.mdoc.in 2020-01-14 15:34:46.908027286 +0100
@@ -2768,6 +2768,22 @@ when running a command or editing a file
This flag is
.Em off
by default.
+.It runas_allow_unknown_id
+If enabled, allow matching of runas user and group IDs that are
+not present in the password or group databases.
+In addition to explicitly matching unknown user or group IDs in a
+.Li Runas_List ,
+this option also allows the
+.Sy ALL
+alias to match unknown IDs.
+This flag is
+.Em off
+by default.
+.Pp
+This setting is only supported by version 1.8.29 or higher.
+Older versions of
+.Nm sudo
+always allowed matching of unknown user and group IDs.
.It runaspw
If set,
.Nm sudo
diff -up ./plugins/sudoers/defaults.c.CVE-2019-19232 ./plugins/sudoers/defaults.c
--- ./plugins/sudoers/defaults.c.CVE-2019-19232 2020-01-14 15:34:46.902027246 +0100
+++ ./plugins/sudoers/defaults.c 2020-01-14 15:34:46.909027293 +0100
@@ -581,6 +581,7 @@ init_defaults(void)
def_fdexec = digest_only;
def_log_allowed = true;
def_log_denied = true;
+ def_runas_allow_unknown_id = false;
/* Syslog options need special care since they both strings and ints */
#if (LOGGING & SLOG_SYSLOG)
diff -up ./plugins/sudoers/def_data.c.CVE-2019-19232 ./plugins/sudoers/def_data.c
--- ./plugins/sudoers/def_data.c.CVE-2019-19232 2020-01-14 15:34:46.908027286 +0100
+++ ./plugins/sudoers/def_data.c 2020-01-14 15:40:19.441555509 +0100
@@ -514,6 +514,10 @@ struct sudo_defs_types sudo_defs_table[]
N_("Don't fork and wait for the command to finish, just exec it"),
NULL,
}, {
+ "runas_allow_unknown_id", T_FLAG,
+ N_("Allow the use of unknown runas user and/or group ID"),
+ NULL,
+ }, {
NULL, 0, NULL
}
};
diff -up ./plugins/sudoers/def_data.h.CVE-2019-19232 ./plugins/sudoers/def_data.h
--- ./plugins/sudoers/def_data.h.CVE-2019-19232 2020-01-14 15:34:46.909027293 +0100
+++ ./plugins/sudoers/def_data.h 2020-01-14 15:41:33.658012401 +0100
@@ -236,6 +236,8 @@
#define def_legacy_group_processing (sudo_defs_table[I_LEGACY_GROUP_PROCESSING].sd_un.flag)
#define I_CMND_NO_WAIT 118
#define def_cmnd_no_wait (sudo_defs_table[I_CMND_NO_WAIT].sd_un.flag)
+#define I_RUNAS_ALLOW_UNKNOWN_ID 119
+#define def_runas_allow_unknown_id (sudo_defs_table[I_RUNAS_ALLOW_UNKNOWN_ID].sd_un.flag)
enum def_tuple {
never,
diff -up ./plugins/sudoers/def_data.in.CVE-2019-19232 ./plugins/sudoers/def_data.in
--- ./plugins/sudoers/def_data.in.CVE-2019-19232 2020-01-14 15:34:46.909027293 +0100
+++ ./plugins/sudoers/def_data.in 2020-01-14 15:42:42.176481484 +0100
@@ -372,3 +372,6 @@ legacy_group_processing
cmnd_no_wait
T_FLAG
"Don't fork and wait for the command to finish, just exec it"
+runas_allow_unknown_id
+ T_FLAG
+ "Allow the use of unknown runas user and/or group ID"
diff -up ./plugins/sudoers/sudoers.c.CVE-2019-19232 ./plugins/sudoers/sudoers.c
--- ./plugins/sudoers/sudoers.c.CVE-2019-19232 2020-01-14 15:34:46.905027266 +0100
+++ ./plugins/sudoers/sudoers.c 2020-01-14 15:34:46.910027299 +0100
@@ -105,6 +105,8 @@ static char *prev_user;
static char *runas_user;
static char *runas_group;
static struct sudo_nss_list *snl;
+static bool unknown_runas_uid;
+static bool unknown_runas_gid;
#ifdef __linux__
static struct rlimit nproclimit;
@@ -354,6 +356,22 @@ sudoers_policy_main(int argc, char * con
}
}
+ /* Defer uid/gid checks until after defaults have been updated. */
+ if (unknown_runas_uid && !def_runas_allow_unknown_id) {
+ audit_failure(NewArgc, NewArgv, N_("unknown user: %s"),
+ runas_pw->pw_name);
+ sudo_warnx(U_("unknown user: %s"), runas_pw->pw_name);
+ goto done;
+ }
+ if (runas_gr != NULL) {
+ if (unknown_runas_gid && !def_runas_allow_unknown_id) {
+ audit_failure(NewArgc, NewArgv, N_("unknown group: %s"),
+ runas_gr->gr_name);
+ sudo_warnx(U_("unknown group: %s"), runas_gr->gr_name);
+ goto done;
+ }
+ }
+
/*
* Look up the timestamp dir owner if one is specified.
*/
@@ -1167,12 +1185,15 @@ set_runaspw(const char *user, bool quiet
struct passwd *pw = NULL;
debug_decl(set_runaspw, SUDOERS_DEBUG_PLUGIN)
+ unknown_runas_uid = false;
if (*user == '#') {
const char *errstr;
uid_t uid = sudo_strtoid(user + 1, &errstr);
if (errstr == NULL) {
- if ((pw = sudo_getpwuid(uid)) == NULL)
+ if ((pw = sudo_getpwuid(uid)) == NULL) {
+ unknown_runas_uid = true;
pw = sudo_fakepwnam(user, user_gid);
+ }
}
}
if (pw == NULL) {
@@ -1198,12 +1219,15 @@ set_runasgr(const char *group, bool quie
struct group *gr = NULL;
debug_decl(set_runasgr, SUDOERS_DEBUG_PLUGIN)
+ unknown_runas_gid = false;
if (*group == '#') {
const char *errstr;
gid_t gid = sudo_strtoid(group + 1, &errstr);
if (errstr == NULL) {
- if ((gr = sudo_getgrgid(gid)) == NULL)
+ if ((gr = sudo_getgrgid(gid)) == NULL) {
+ unknown_runas_gid = true;
gr = sudo_fakegrnam(group);
+ }
}
}
if (gr == NULL) {

View File

@ -0,0 +1,439 @@
diff -up ./config.h.in.CVE-2019-19234 ./config.h.in
--- ./config.h.in.CVE-2019-19234 2019-10-28 13:28:52.000000000 +0100
+++ ./config.h.in 2020-01-14 15:53:40.506988064 +0100
@@ -334,6 +334,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
diff -up ./configure.ac.CVE-2019-19234 ./configure.ac
--- ./configure.ac.CVE-2019-19234 2020-01-14 15:53:40.496987995 +0100
+++ ./configure.ac 2020-01-14 15:53:40.509988084 +0100
@@ -2562,6 +2562,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)
diff -up ./configure.CVE-2019-19234 ./configure
--- ./configure.CVE-2019-19234 2019-10-28 13:29:14.000000000 +0100
+++ ./configure 2020-01-14 15:53:40.509988084 +0100
@@ -19395,6 +19395,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"
diff -up ./doc/sudoers.man.in.CVE-2019-19234 ./doc/sudoers.man.in
--- ./doc/sudoers.man.in.CVE-2019-19234 2020-01-14 15:53:40.503988043 +0100
+++ ./doc/sudoers.man.in 2020-01-14 15:53:40.510988091 +0100
@@ -2959,6 +2959,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.29 or higher.
+.TP 18n
runaspw
If set,
\fBsudo\fR
diff -up ./doc/sudoers.mdoc.in.CVE-2019-19234 ./doc/sudoers.mdoc.in
--- ./doc/sudoers.mdoc.in.CVE-2019-19234 2020-01-14 15:53:40.504988050 +0100
+++ ./doc/sudoers.mdoc.in 2020-01-14 15:53:40.510988091 +0100
@@ -2784,6 +2784,26 @@ This setting is only supported by versio
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.29 or higher.
.It runaspw
If set,
.Nm sudo
diff -up ./include/sudo_compat.h.CVE-2019-19234 ./include/sudo_compat.h
--- ./include/sudo_compat.h.CVE-2019-19234 2019-10-28 13:28:52.000000000 +0100
+++ ./include/sudo_compat.h 2020-01-14 15:53:40.511988098 +0100
@@ -407,6 +407,17 @@ __dso_public ssize_t sudo_getdelim(char
# 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
diff -up ./lib/util/getusershell.c.CVE-2019-19234 ./lib/util/getusershell.c
--- ./lib/util/getusershell.c.CVE-2019-19234 2020-01-14 15:53:40.511988098 +0100
+++ ./lib/util/getusershell.c 2020-01-14 15:53:40.511988098 +0100
@@ -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++);
+}
diff -up ./lib/util/Makefile.in.CVE-2019-19234 ./lib/util/Makefile.in
--- ./lib/util/Makefile.in.CVE-2019-19234 2019-10-28 13:28:53.000000000 +0100
+++ ./lib/util/Makefile.in 2020-01-14 15:53:40.511988098 +0100
@@ -678,6 +678,18 @@ gettime.i: $(srcdir)/gettime.c $(incdir)
$(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 \
diff -up ./MANIFEST.CVE-2019-19234 ./MANIFEST
--- ./MANIFEST.CVE-2019-19234 2019-10-28 13:28:52.000000000 +0100
+++ ./MANIFEST 2020-01-14 15:53:40.506988064 +0100
@@ -103,6 +103,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/inet_ntop.c
diff -up ./mkdep.pl.CVE-2019-19234 ./mkdep.pl
--- ./mkdep.pl.CVE-2019-19234 2019-10-28 13:28:52.000000000 +0100
+++ ./mkdep.pl 2020-01-14 15:53:40.511988098 +0100
@@ -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 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 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 utimens.lo vsyslog.lo pipe2.lo:;
+ $makefile =~ s:\@LTLIBOBJS\@:arc4random.lo arc4random_uniform.lo closefrom.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 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 utimens.lo vsyslog.lo pipe2.lo:;
# Parse OBJS lines
my %objs;
diff -up ./plugins/sudoers/check.c.CVE-2019-19234 ./plugins/sudoers/check.c
--- ./plugins/sudoers/check.c.CVE-2019-19234 2019-10-28 13:27:45.000000000 +0100
+++ ./plugins/sudoers/check.c 2020-01-14 15:53:40.511988098 +0100
@@ -333,3 +333,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);
+}
diff -up ./plugins/sudoers/def_data.c.CVE-2019-19234 ./plugins/sudoers/def_data.c
--- ./plugins/sudoers/def_data.c.CVE-2019-19234 2020-01-14 15:53:40.504988050 +0100
+++ ./plugins/sudoers/def_data.c 2020-01-14 15:53:40.511988098 +0100
@@ -518,6 +518,10 @@ struct sudo_defs_types sudo_defs_table[]
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
}
};
diff -up ./plugins/sudoers/def_data.h.CVE-2019-19234 ./plugins/sudoers/def_data.h
--- ./plugins/sudoers/def_data.h.CVE-2019-19234 2020-01-14 15:53:40.512988105 +0100
+++ ./plugins/sudoers/def_data.h 2020-01-14 15:58:06.927808982 +0100
@@ -238,6 +238,8 @@
#define def_cmnd_no_wait (sudo_defs_table[I_CMND_NO_WAIT].sd_un.flag)
#define I_RUNAS_ALLOW_UNKNOWN_ID 119
#define def_runas_allow_unknown_id (sudo_defs_table[I_RUNAS_ALLOW_UNKNOWN_ID].sd_un.flag)
+#define I_RUNAS_CHECK_SHELL 120
+#define def_runas_check_shell (sudo_defs_table[I_RUNAS_CHECK_SHELL].sd_un.flag)
enum def_tuple {
never,
diff -up ./plugins/sudoers/def_data.in.CVE-2019-19234 ./plugins/sudoers/def_data.in
--- ./plugins/sudoers/def_data.in.CVE-2019-19234 2020-01-14 15:53:40.505988057 +0100
+++ ./plugins/sudoers/def_data.in 2020-01-14 15:53:40.512988105 +0100
@@ -375,3 +375,7 @@ cmnd_no_wait
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"
+
diff -up ./plugins/sudoers/sudoers.c.CVE-2019-19234 ./plugins/sudoers/sudoers.c
--- ./plugins/sudoers/sudoers.c.CVE-2019-19234 2020-01-14 15:53:40.505988057 +0100
+++ ./plugins/sudoers/sudoers.c 2020-01-14 15:53:40.512988105 +0100
@@ -273,7 +273,7 @@ sudoers_policy_main(int argc, char * con
/* 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;
@@ -412,6 +412,13 @@ sudoers_policy_main(int argc, char * con
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.
diff -up ./plugins/sudoers/sudoers.h.CVE-2019-19234 ./plugins/sudoers/sudoers.h
--- ./plugins/sudoers/sudoers.h.CVE-2019-19234 2020-01-14 15:53:40.502988036 +0100
+++ ./plugins/sudoers/sudoers.h 2020-01-14 15:53:40.512988105 +0100
@@ -264,6 +264,7 @@ int find_path(const char *infile, char *
/* check.c */
int check_user(int validate, int mode);
+bool check_user_shell(const struct passwd *pw);
bool user_is_exempt(void);
/* prompt.c */

View File

@ -0,0 +1,154 @@
From 4b6de608c25a6ffbdb507be958e12f814b43077c Mon Sep 17 00:00:00 2001
From: "Todd C. Miller" <Todd.Miller@sudo.ws>
Date: Wed, 4 Dec 2019 12:38:22 -0700
Subject: [PATCH] Only update the time stamp entry after the approval function
has succeeded. Bug #910
---
plugins/sudoers/check.c | 59 +++++++++++++++++++----------------------
1 file changed, 27 insertions(+), 32 deletions(-)
diff --git a/plugins/sudoers/check.c b/plugins/sudoers/check.c
index db8e05161..ea1d89085 100644
--- a/plugins/sudoers/check.c
+++ b/plugins/sudoers/check.c
@@ -51,6 +51,7 @@ static bool display_lecture(int);
static struct passwd *get_authpw(int);
struct getpass_closure {
+ int tstat;
void *cookie;
struct passwd *auth_pw;
};
@@ -89,27 +90,20 @@ getpass_resume(int signo, void *vclosure)
* or -1 on fatal error.
*/
static int
-check_user_interactive(int validated, int mode, struct passwd *auth_pw)
+check_user_interactive(int validated, int mode, struct getpass_closure *closure)
{
struct sudo_conv_callback cb, *callback = NULL;
- struct getpass_closure closure;
- int status = TS_ERROR;
int ret = -1;
char *prompt;
bool lectured;
debug_decl(check_user_interactive, SUDOERS_DEBUG_AUTH)
- /* Setup closure for getpass_{suspend,resume} */
- closure.auth_pw = auth_pw;
- closure.cookie = NULL;
- sudo_pw_addref(closure.auth_pw);
-
/* Open, lock and read time stamp file if we are using it. */
if (!ISSET(mode, MODE_IGNORE_TICKET)) {
/* Open time stamp file and check its status. */
- closure.cookie = timestamp_open(user_name, user_sid);
- if (timestamp_lock(closure.cookie, closure.auth_pw))
- status = timestamp_status(closure.cookie, closure.auth_pw);
+ closure->cookie = timestamp_open(user_name, user_sid);
+ if (timestamp_lock(closure->cookie, closure->auth_pw))
+ closure->tstat = timestamp_status(closure->cookie, closure->auth_pw);
/* Construct callback for getpass function. */
memset(&cb, 0, sizeof(cb));
@@ -120,7 +114,7 @@ check_user_interactive(int validated, int mode, struct passwd *auth_pw)
callback = &cb;
}
- switch (status) {
+ switch (closure->tstat) {
case TS_FATAL:
/* Fatal error (usually setuid failure), unsafe to proceed. */
goto done;
@@ -144,32 +138,22 @@ check_user_interactive(int validated, int mode, struct passwd *auth_pw)
}
/* XXX - should not lecture if askpass helper is being used. */
- lectured = display_lecture(status);
+ lectured = display_lecture(closure->tstat);
/* Expand any escapes in the prompt. */
prompt = expand_prompt(user_prompt ? user_prompt : def_passprompt,
- closure.auth_pw->pw_name);
+ closure->auth_pw->pw_name);
if (prompt == NULL)
goto done;
- ret = verify_user(closure.auth_pw, prompt, validated, callback);
+ ret = verify_user(closure->auth_pw, prompt, validated, callback);
if (ret == true && lectured)
(void)set_lectured(); /* lecture error not fatal */
free(prompt);
break;
}
- /*
- * Only update time stamp if user was validated.
- * Failure to update the time stamp is not a fatal error.
- */
- if (ret == true && ISSET(validated, VALIDATE_SUCCESS) && status != TS_ERROR)
- (void)timestamp_update(closure.cookie, closure.auth_pw);
done:
- if (closure.cookie != NULL)
- timestamp_close(closure.cookie);
- sudo_pw_delref(closure.auth_pw);
-
debug_return_int(ret);
}
@@ -180,7 +164,7 @@ done:
int
check_user(int validated, int mode)
{
- struct passwd *auth_pw;
+ struct getpass_closure closure = { TS_ERROR };
int ret = -1;
bool exempt = false;
debug_decl(check_user, SUDOERS_DEBUG_AUTH)
@@ -189,9 +173,9 @@ check_user(int validated, int mode)
* Init authentication system regardless of whether we need a password.
* Required for proper PAM session support.
*/
- if ((auth_pw = get_authpw(mode)) == NULL)
+ if ((closure.auth_pw = get_authpw(mode)) == NULL)
goto done;
- if (sudo_auth_init(auth_pw) == -1)
+ if (sudo_auth_init(closure.auth_pw) == -1)
goto done;
/*
@@ -222,15 +206,26 @@ check_user(int validated, int mode)
}
}
- ret = check_user_interactive(validated, mode, auth_pw);
+ ret = check_user_interactive(validated, mode, &closure);
done:
if (ret == true) {
/* The approval function may disallow a user post-authentication. */
- ret = sudo_auth_approval(auth_pw, validated, exempt);
+ ret = sudo_auth_approval(closure.auth_pw, validated, exempt);
+
+ /*
+ * Only update time stamp if user validated and was approved.
+ * Failure to update the time stamp is not a fatal error.
+ */
+ if (ret == true && closure.tstat != TS_ERROR) {
+ if (ISSET(validated, VALIDATE_SUCCESS))
+ (void)timestamp_update(closure.cookie, closure.auth_pw);
+ }
}
- sudo_auth_cleanup(auth_pw);
- sudo_pw_delref(auth_pw);
+ timestamp_close(closure.cookie);
+ sudo_auth_cleanup(closure.auth_pw);
+ if (closure.auth_pw != NULL)
+ sudo_pw_delref(closure.auth_pw);
debug_return_int(ret);
}
--
2.25.1

View File

@ -0,0 +1,29 @@
From 5472b1751645f750e42a0ba6daac667983b1a56c Mon Sep 17 00:00:00 2001
From: "Todd C. Miller" <Todd.Miller@sudo.ws>
Date: Fri, 24 Jan 2020 11:13:55 -0700
Subject: [PATCH] Fix crash in sudo 1.8.30 when suspending sudo at the password
prompt. The closure pointer in sudo_conv_callback was being filled in with a
struct getpass_closure ** instead of a struct getpass_closure *. The bug was
introduced in the fix for Bug #910; previously the closure variable was a
struct getpass_closure, not a pointer. Fix from Michael Norton; Bug #914.
---
plugins/sudoers/check.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/plugins/sudoers/check.c b/plugins/sudoers/check.c
index 72e87eef6..9b03c7a05 100644
--- a/plugins/sudoers/check.c
+++ b/plugins/sudoers/check.c
@@ -108,7 +108,7 @@ check_user_interactive(int validated, int mode, struct getpass_closure *closure)
/* Construct callback for getpass function. */
memset(&cb, 0, sizeof(cb));
cb.version = SUDO_CONV_CALLBACK_VERSION;
- cb.closure = &closure;
+ cb.closure = closure;
cb.on_suspend = getpass_suspend;
cb.on_resume = getpass_resume;
callback = &cb;
--
2.25.1

View File

@ -0,0 +1,206 @@
diff -up ./plugins/sudoers/policy.c.heap-buffer ./plugins/sudoers/policy.c
--- ./plugins/sudoers/policy.c.heap-buffer 2019-10-28 13:28:52.000000000 +0100
+++ ./plugins/sudoers/policy.c 2021-01-20 11:38:06.481807015 +0100
@@ -100,10 +100,11 @@ parse_bool(const char *line, int varlen,
int
sudoers_policy_deserialize_info(void *v, char **runas_user, char **runas_group)
{
+ const int edit_mask = MODE_EDIT|MODE_IGNORE_TICKET|MODE_NONINTERACTIVE;
struct sudoers_policy_open_info *info = v;
- char * const *cur;
const char *p, *errstr, *groups = NULL;
const char *remhost = NULL;
+ char * const *cur;
int flags = 0;
debug_decl(sudoers_policy_deserialize_info, SUDOERS_DEBUG_PLUGIN)
@@ -332,6 +333,12 @@ sudoers_policy_deserialize_info(void *v,
#endif
}
+ /* Sudo front-end should restrict mode flags for sudoedit. */
+ if (ISSET(flags, MODE_EDIT) && (flags & edit_mask) != flags) {
+ sudo_warnx(U_("invalid mode flags from sudo front end: 0x%x"), flags);
+ goto bad;
+ }
+
user_gid = (gid_t)-1;
user_sid = (pid_t)-1;
user_uid = (gid_t)-1;
diff -up ./plugins/sudoers/sudoers.c.heap-buffer ./plugins/sudoers/sudoers.c
--- ./plugins/sudoers/sudoers.c.heap-buffer 2021-01-20 11:34:57.523317977 +0100
+++ ./plugins/sudoers/sudoers.c 2021-01-20 12:08:01.331553520 +0100
@@ -451,7 +451,7 @@ sudoers_policy_main(int argc, char * con
/* If run as root with SUDO_USER set, set sudo_user.pw to that user. */
/* XXX - causes confusion when root is not listed in sudoers */
- if (sudo_mode & (MODE_RUN | MODE_EDIT) && prev_user != NULL) {
+ if (ISSET(sudo_mode, MODE_RUN|MODE_EDIT) && prev_user != NULL) {
if (user_uid == 0 && strcmp(prev_user, "root") != 0) {
struct passwd *pw;
@@ -834,8 +834,8 @@ set_cmnd(void)
if (user_cmnd == NULL)
user_cmnd = NewArgv[0];
- if (sudo_mode & (MODE_RUN | MODE_EDIT | MODE_CHECK)) {
- if (ISSET(sudo_mode, MODE_RUN | MODE_CHECK)) {
+ if (ISSET(sudo_mode, MODE_RUN|MODE_EDIT|MODE_CHECK)) {
+ if (!ISSET(sudo_mode, MODE_EDIT)) {
if (def_secure_path && !user_is_exempt())
path = def_secure_path;
if (!set_perms(PERM_RUNAS))
@@ -873,7 +873,8 @@ set_cmnd(void)
sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
debug_return_int(-1);
}
- if (ISSET(sudo_mode, MODE_SHELL|MODE_LOGIN_SHELL)) {
+ if (ISSET(sudo_mode, MODE_SHELL|MODE_LOGIN_SHELL) &&
+ ISSET(sudo_mode, MODE_RUN)) {
/*
* When running a command via a shell, the sudo front-end
* escapes potential meta chars. We unescape non-spaces
@@ -881,10 +882,22 @@ set_cmnd(void)
*/
for (to = user_args, av = NewArgv + 1; (from = *av); av++) {
while (*from) {
- if (from[0] == '\\' && !isspace((unsigned char)from[1]))
+ if (from[0] == '\\' && from[1] != '\0' &&
+ !isspace((unsigned char)from[1])) {
from++;
+ }
+ if (size - (to - user_args) < 1) {
+ sudo_warnx(U_("internal error, %s overflow"),
+ __func__);
+ debug_return_int(NOT_FOUND_ERROR);
+ }
*to++ = *from++;
}
+ if (size - (to - user_args) < 1) {
+ sudo_warnx(U_("internal error, %s overflow"),
+ __func__);
+ debug_return_int(NOT_FOUND_ERROR);
+ }
*to++ = ' ';
}
*--to = '\0';
diff -up ./plugins/sudoers/timestamp.c.heap-buffer ./plugins/sudoers/timestamp.c
--- ./plugins/sudoers/timestamp.c.heap-buffer 2021-01-20 12:11:28.218774128 +0100
+++ ./plugins/sudoers/timestamp.c 2021-01-20 12:20:41.772324808 +0100
@@ -652,8 +652,8 @@ timestamp_lock(void *vcookie, struct pas
} else if (entry.type != TS_LOCKEXCL) {
/* Old sudo record, convert it to TS_LOCKEXCL. */
entry.type = TS_LOCKEXCL;
- memset((char *)&entry + offsetof(struct timestamp_entry, type), 0,
- nread - offsetof(struct timestamp_entry, type));
+ memset((char *)&entry + offsetof(struct timestamp_entry, flags), 0,
+ nread - offsetof(struct timestamp_entry, flags));
if (ts_write(cookie->fd, cookie->fname, &entry, 0) == -1)
debug_return_bool(false);
}
diff -up ./src/parse_args.c.heap-buffer ./src/parse_args.c
--- ./src/parse_args.c.heap-buffer 2021-01-20 11:35:18.231043445 +0100
+++ ./src/parse_args.c 2021-01-20 12:26:31.290591673 +0100
@@ -124,7 +124,10 @@ struct environment {
/*
* Default flags allowed when running a command.
*/
-#define DEFAULT_VALID_FLAGS (MODE_BACKGROUND|MODE_PRESERVE_ENV|MODE_RESET_HOME|MODE_LOGIN_SHELL|MODE_NONINTERACTIVE|MODE_SHELL)
+#define DEFAULT_VALID_FLAGS (MODE_BACKGROUND|MODE_PRESERVE_ENV|MODE_RESET_HOME|MODE_LOGIN_SHELL|MODE_NONINTERACTIVE|MODE_PRESERVE_GROUPS|MODE_SHELL)
+#define EDIT_VALID_FLAGS MODE_NONINTERACTIVE
+#define LIST_VALID_FLAGS (MODE_NONINTERACTIVE|MODE_LONG_LIST)
+#define VALIDATE_VALID_FLAGS MODE_NONINTERACTIVE
/* Option number for the --host long option due to ambiguity of the -h flag. */
#define OPT_HOSTNAME 256
@@ -269,6 +272,7 @@ parse_args(int argc, char **argv, int *n
progname = "sudoedit";
mode = MODE_EDIT;
sudo_settings[ARG_SUDOEDIT].value = "true";
+ valid_flags = EDIT_VALID_FLAGS;
}
/* Load local IP addresses and masks. */
@@ -360,7 +364,7 @@ parse_args(int argc, char **argv, int *n
usage_excl(1);
mode = MODE_EDIT;
sudo_settings[ARG_SUDOEDIT].value = "true";
- valid_flags = MODE_NONINTERACTIVE;
+ valid_flags = EDIT_VALID_FLAGS;
break;
case 'g':
assert(optarg != NULL);
@@ -371,6 +375,7 @@ parse_args(int argc, char **argv, int *n
break;
case 'H':
sudo_settings[ARG_SET_HOME].value = "true";
+ SET(flags, MODE_RESET_HOME);
break;
case 'h':
if (optarg == NULL) {
@@ -421,7 +426,7 @@ parse_args(int argc, char **argv, int *n
usage_excl(1);
}
mode = MODE_LIST;
- valid_flags = MODE_NONINTERACTIVE|MODE_LONG_LIST;
+ valid_flags = LIST_VALID_FLAGS;
break;
case 'n':
SET(flags, MODE_NONINTERACTIVE);
@@ -429,6 +434,7 @@ parse_args(int argc, char **argv, int *n
break;
case 'P':
sudo_settings[ARG_PRESERVE_GROUPS].value = "true";
+ SET(flags, MODE_PRESERVE_GROUPS);
break;
case 'p':
/* An empty prompt is allowed. */
@@ -478,7 +484,7 @@ parse_args(int argc, char **argv, int *n
if (mode && mode != MODE_VALIDATE)
usage_excl(1);
mode = MODE_VALIDATE;
- valid_flags = MODE_NONINTERACTIVE;
+ valid_flags = VALIDATE_VALID_FLAGS;
break;
case 'V':
if (mode && mode != MODE_VERSION)
@@ -505,7 +511,7 @@ parse_args(int argc, char **argv, int *n
if (!mode) {
/* Defer -k mode setting until we know whether it is a flag or not */
if (sudo_settings[ARG_IGNORE_TICKET].value != NULL) {
- if (argc == 0 && !(flags & (MODE_SHELL|MODE_LOGIN_SHELL))) {
+ if (argc == 0 && !ISSET(flags, MODE_SHELL|MODE_LOGIN_SHELL)) {
mode = MODE_INVALIDATE; /* -k by itself */
sudo_settings[ARG_IGNORE_TICKET].value = NULL;
valid_flags = 0;
@@ -568,23 +574,24 @@ parse_args(int argc, char **argv, int *n
/*
* For shell mode we need to rewrite argv
*/
- if (ISSET(mode, MODE_RUN) && ISSET(flags, MODE_SHELL)) {
+ if (ISSET(flags, MODE_SHELL|MODE_LOGIN_SHELL) && ISSET(mode, MODE_RUN)) {
char **av, *cmnd = NULL;
int ac = 1;
if (argc != 0) {
/* shell -c "command" */
char *src, *dst;
- size_t cmnd_size = (size_t) (argv[argc - 1] - argv[0]) +
- strlen(argv[argc - 1]) + 1;
+ size_t size = 0;
- cmnd = dst = reallocarray(NULL, cmnd_size, 2);
- if (cmnd == NULL)
+ for (av = argv; *av != NULL; av++)
+ size += strlen(*av) + 1;
+
+ if (size == 0 || (cmnd = reallocarray(NULL, size, 2)) == NULL)
sudo_fatalx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
if (!gc_add(GC_PTR, cmnd))
exit(1);
- for (av = argv; *av != NULL; av++) {
+ for (dst = cmnd, av = argv; *av != NULL; av++) {
for (src = *av; *src != '\0'; src++) {
/* quote potential meta characters */
if (!isalnum((unsigned char)*src) && *src != '_' && *src != '-' && *src != '$')

View File

@ -0,0 +1,90 @@
From 06b46ae226fecd4188af372ac0ccd7aa582e21c8 Mon Sep 17 00:00:00 2001
From: Tomas Sykora <tosykora@redhat.com>
Date: Wed, 17 Aug 2016 10:12:11 +0200
Subject: [PATCH] Sudo logs username root instead of realuser
RHEL7 sudo logs username root instead of realuser in /var/log/secure
Rebased from:
Patch50: sudo-1.8.6p7-logsudouser.patch
Resolves:
rhbz#1312486
---
plugins/sudoers/logging.c | 14 +++++++-------
plugins/sudoers/sudoers.h | 1 +
2 files changed, 8 insertions(+), 7 deletions(-)
diff --git a/plugins/sudoers/logging.c b/plugins/sudoers/logging.c
index 45cae67..74b2220 100644
--- a/plugins/sudoers/logging.c
+++ b/plugins/sudoers/logging.c
@@ -104,7 +104,7 @@ do_syslog(int pri, char *msg)
* Log the full line, breaking into multiple syslog(3) calls if necessary
*/
fmt = _("%8s : %s");
- maxlen = def_syslog_maxlen - (strlen(fmt) - 5 + strlen(user_name));
+ maxlen = def_syslog_maxlen - (strlen(fmt) - 5 + strlen(sudo_user_name));
for (p = msg; *p != '\0'; ) {
len = strlen(p);
if (len > maxlen) {
@@ -120,7 +120,7 @@ do_syslog(int pri, char *msg)
save = *tmp;
*tmp = '\0';
- mysyslog(pri, fmt, user_name, p);
+ mysyslog(pri, fmt, sudo_user_name, p);
*tmp = save; /* restore saved character */
@@ -128,11 +128,11 @@ do_syslog(int pri, char *msg)
for (p = tmp; *p == ' '; p++)
continue;
} else {
- mysyslog(pri, fmt, user_name, p);
+ mysyslog(pri, fmt, sudo_user_name, p);
p += len;
}
fmt = _("%8s : (command continued) %s");
- maxlen = def_syslog_maxlen - (strlen(fmt) - 5 + strlen(user_name));
+ maxlen = def_syslog_maxlen - (strlen(fmt) - 5 + strlen(sudo_user_name));
}
sudoers_setlocale(oldlocale, NULL);
@@ -179,10 +179,10 @@ do_logfile(const char *msg)
timestr = "invalid date";
if (def_log_host) {
len = asprintf(&full_line, "%s : %s : HOST=%s : %s",
- timestr, user_name, user_srunhost, msg);
+ timestr, sudo_user_name, user_srunhost, msg);
} else {
len = asprintf(&full_line, "%s : %s : %s",
- timestr, user_name, msg);
+ timestr, sudo_user_name, msg);
}
if (len == -1) {
sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
@@ -746,7 +746,7 @@ send_mail(const char *fmt, ...)
if ((timestr = get_timestr(time(NULL), def_log_year)) == NULL)
timestr = "invalid date";
- (void) fprintf(mail, "\n\n%s : %s : %s : ", user_host, timestr, user_name);
+ (void) fprintf(mail, "\n\n%s : %s : %s : ", user_host, timestr, sudo_user_name);
va_start(ap, fmt);
(void) vfprintf(mail, fmt, ap);
va_end(ap);
diff --git a/plugins/sudoers/sudoers.h b/plugins/sudoers/sudoers.h
index cfd5abb..c69a043 100644
--- a/plugins/sudoers/sudoers.h
+++ b/plugins/sudoers/sudoers.h
@@ -180,6 +180,7 @@ struct sudo_user {
/*
* Shortcuts for sudo_user contents.
*/
+#define sudo_user_name (sudo_user.pw->pw_name)
#define user_name (sudo_user.name)
#define user_uid (sudo_user.uid)
#define user_gid (sudo_user.gid)
--
2.7.4

View File

@ -0,0 +1,61 @@
From db1f27c0350e9e437c93780ffe88648ae1984467 Mon Sep 17 00:00:00 2001
From: "Todd C. Miller" <Todd.Miller@sudo.ws>
Date: Wed, 6 Jan 2021 10:16:00 -0700
Subject: [PATCH] Fix potential directory existing info leak in sudoedit. When
creating a new file, sudoedit checks to make sure the parent directory exists
so it can provide the user with a sensible error message. However, this
could be used to test for the existence of directories not normally
accessible to the user by pointing to them with a symbolic link when the
parent directory is controlled by the user. Problem reported by Matthias
Gerstner of SUSE.
---
src/sudo_edit.c | 29 ++++++++++++++++++++++++-----
1 file changed, 24 insertions(+), 5 deletions(-)
diff --git a/src/sudo_edit.c b/src/sudo_edit.c
index 82e04a71b..5502b7bd9 100644
--- a/src/sudo_edit.c
+++ b/src/sudo_edit.c
@@ -541,14 +541,33 @@ sudo_edit_create_tfiles(struct command_details *command_details,
S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH, command_details);
if (ofd != -1 || errno == ENOENT) {
if (ofd == -1) {
- /* New file, verify parent dir exists unless in cwd. */
+ /*
+ * New file, verify parent dir exists unless in cwd.
+ * This fails early so the user knows ahead of time if the
+ * edit won't succeed. Additional checks are performed
+ * when copying the temporary file back to the origin.
+ */
char *slash = strrchr(files[i], '/');
if (slash != NULL && slash != files[i]) {
- int serrno = errno;
+ const int sflags = command_details->flags;
+ const int serrno = errno;
+ int dfd;
+
+ /*
+ * The parent directory is allowed to be a symbolic
+ * link as long as *its* parent is not writable.
+ */
*slash = '\0';
- if (stat(files[i], &sb) == 0 && S_ISDIR(sb.st_mode)) {
- memset(&sb, 0, sizeof(sb));
- rc = 0;
+ SET(command_details->flags, CD_SUDOEDIT_FOLLOW);
+ dfd = sudo_edit_open(files[i], DIR_OPEN_FLAGS,
+ S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH, command_details);
+ command_details->flags = sflags;
+ if (dfd != -1) {
+ if (fstat(dfd, &sb) == 0 && S_ISDIR(sb.st_mode)) {
+ memset(&sb, 0, sizeof(sb));
+ rc = 0;
+ }
+ close(dfd);
}
*slash = '/';
errno = serrno;
--
2.26.2

View File

@ -0,0 +1,158 @@
From adb4360c40df99238c17c3ecedcb1d32d76e2b2e Mon Sep 17 00:00:00 2001
From: "Todd C. Miller" <Todd.Miller@sudo.ws>
Date: Fri, 17 Apr 2020 19:08:56 -0600
Subject: [PATCH] Extend the original file before to the new size before
updating it. Instead of opening the original file for writing w/ tuncation,
we first extend the file with zeroes (by writing, not seeking), then
overwrite it. This should allow sudo to fail early if the disk is out of
space before it overwrites the original file.
---
src/sudo_edit.c | 93 ++++++++++++++++++++++++++++++++++++++++---------
1 file changed, 77 insertions(+), 16 deletions(-)
diff --git a/src/sudo_edit.c b/src/sudo_edit.c
index 28f6c6100..d99a5658a 100644
--- a/src/sudo_edit.c
+++ b/src/sudo_edit.c
@@ -1,7 +1,7 @@
/*
* SPDX-License-Identifier: ISC
*
- * Copyright (c) 2004-2008, 2010-2018 Todd C. Miller <Todd.Miller@sudo.ws>
+ * Copyright (c) 2004-2008, 2010-2020 Todd C. Miller <Todd.Miller@sudo.ws>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -650,6 +650,51 @@ sudo_edit_create_tfiles(struct command_details *command_details,
debug_return_int(j);
}
+/*
+ * Extend the given fd to the specified size in bytes.
+ * We do this to allocate disk space up-front before overwriting
+ * the original file with the temporary. Otherwise, we could
+ * we run out of disk space after truncating the original file.
+ */
+static int
+sudo_edit_extend_file(int fd, off_t new_size)
+{
+ off_t old_size, size;
+ ssize_t nwritten;
+ char zeroes[1024] = { '\0' };
+ debug_decl(sudo_edit_extend_file, SUDO_DEBUG_EDIT);
+
+ if ((old_size = lseek(fd, 0, SEEK_END)) == -1) {
+ sudo_warn("lseek");
+ debug_return_int(-1);
+ }
+ sudo_debug_printf(SUDO_DEBUG_INFO, "%s: extending file from %lld to %lld",
+ __func__, (long long)old_size, (long long)new_size);
+
+ for (size = old_size; size < new_size; size += nwritten) {
+ size_t len = new_size - size;
+ if (len > sizeof(zeroes))
+ len = sizeof(zeroes);
+ nwritten = write(fd, zeroes, len);
+ if (nwritten == -1) {
+ int serrno = errno;
+ if (ftruncate(fd, old_size) == -1) {
+ sudo_debug_printf(
+ SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO|SUDO_DEBUG_ERRNO,
+ "unable to truncate to %lld", (long long)old_size);
+ }
+ errno = serrno;
+ debug_return_int(-1);
+ }
+ }
+ if (lseek(fd, 0, SEEK_SET) == -1) {
+ sudo_warn("lseek");
+ debug_return_int(-1);
+ }
+
+ debug_return_int(0);
+}
+
/*
* Copy the temporary files specified in tf to the originals.
* Returns the number of copy errors or 0 if completely successful.
@@ -708,38 +753,53 @@ sudo_edit_copy_tfiles(struct command_details *command_details,
switch_user(command_details->euid, command_details->egid,
command_details->ngroups, command_details->groups);
oldmask = umask(command_details->umask);
- ofd = sudo_edit_open(tf[i].ofile, O_WRONLY|O_TRUNC|O_CREAT,
+ ofd = sudo_edit_open(tf[i].ofile, O_WRONLY|O_CREAT,
S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH, command_details);
umask(oldmask);
switch_user(ROOT_UID, user_details.egid,
user_details.ngroups, user_details.groups);
- if (ofd == -1) {
- sudo_warn(U_("unable to write to %s"), tf[i].ofile);
- sudo_warnx(U_("contents of edit session left in %s"), tf[i].tfile);
- close(tfd);
- errors++;
- continue;
+ if (ofd == -1)
+ goto write_error;
+ /* Extend the file to the new size if larger before copying. */
+ if (tf[i].osize > 0 && sb.st_size > tf[i].osize) {
+ if (sudo_edit_extend_file(ofd, sb.st_size) == -1)
+ goto write_error;
}
+ /* Overwrite the old file with the new contents. */
while ((nread = read(tfd, buf, sizeof(buf))) > 0) {
- if ((nwritten = write(ofd, buf, nread)) != nread) {
+ ssize_t off = 0;
+ do {
+ nwritten = write(ofd, buf + off, nread - off);
if (nwritten == -1)
- sudo_warn("%s", tf[i].ofile);
- else
- sudo_warnx(U_("%s: short write"), tf[i].ofile);
- break;
- }
+ goto write_error;
+ off += nwritten;
+ } while (nread > off);
}
if (nread == 0) {
- /* success, got EOF */
+ /* success, read to EOF */
+ if (tf[i].osize > 0 && sb.st_size < tf[i].osize) {
+ /* We don't open with O_TRUNC so must truncate manually. */
+ if (ftruncate(ofd, sb.st_size) == -1) {
+ sudo_debug_printf(
+ SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO|SUDO_DEBUG_ERRNO,
+ "unable to truncate %s to %lld", tf[i].ofile,
+ (long long)sb.st_size);
+ goto write_error;
+ }
+ }
unlink(tf[i].tfile);
} else if (nread < 0) {
sudo_warn(U_("unable to read temporary file"));
sudo_warnx(U_("contents of edit session left in %s"), tf[i].tfile);
+ errors++;
} else {
+write_error:
sudo_warn(U_("unable to write to %s"), tf[i].ofile);
sudo_warnx(U_("contents of edit session left in %s"), tf[i].tfile);
+ errors++;
}
- close(ofd);
+ if (ofd != -1)
+ close(ofd);
close(tfd);
}
debug_return_int(errors);
@@ -1065,6 +1125,7 @@ cleanup:
for (i = 0; i < nfiles; i++) {
if (tf[i].tfile != NULL)
unlink(tf[i].tfile);
+ free(tf[i].tfile);
}
}
free(tf);
--
2.26.2

View File

@ -0,0 +1,431 @@
diff -up ./src/copy_file.c.symbolic-link-attack-2 ./src/copy_file.c
--- ./src/copy_file.c.symbolic-link-attack-2 2021-02-02 15:31:20.555340446 +0100
+++ ./src/copy_file.c 2021-02-02 15:31:20.555340446 +0100
@@ -0,0 +1,128 @@
+/*
+ * SPDX-License-Identifier: ISC
+ *
+ * Copyright (c) 2020 Todd C. Miller <Todd.Miller@sudo.ws>
+ *
+ * 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 <unistd.h>
+#include <errno.h>
+
+#include "sudo.h"
+
+/*
+ * Extend the given fd to the specified size in bytes.
+ * We do this to allocate disk space up-front before overwriting
+ * the original file with the temporary. Otherwise, we could
+ * we run out of disk space after truncating the original file.
+ */
+static int
+sudo_extend_file(int fd, const char *name, off_t new_size)
+{
+ off_t old_size, size;
+ ssize_t nwritten;
+ char zeroes[BUFSIZ] = { '\0' };
+ debug_decl(sudo_extend_file, SUDO_DEBUG_UTIL);
+
+ if ((old_size = lseek(fd, 0, SEEK_END)) == -1) {
+ sudo_warn("lseek");
+ debug_return_int(-1);
+ }
+ sudo_debug_printf(SUDO_DEBUG_INFO, "%s: extending %s from %lld to %lld",
+ __func__, name, (long long)old_size, (long long)new_size);
+
+ for (size = old_size; size < new_size; size += nwritten) {
+ size_t len = new_size - size;
+ if (len > sizeof(zeroes))
+ len = sizeof(zeroes);
+ nwritten = write(fd, zeroes, len);
+ if (nwritten == -1) {
+ int serrno = errno;
+ if (ftruncate(fd, old_size) == -1) {
+ sudo_debug_printf(
+ SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO|SUDO_DEBUG_ERRNO,
+ "unable to truncate %s to %lld", name, (long long)old_size);
+ }
+ errno = serrno;
+ debug_return_int(-1);
+ }
+ }
+ if (lseek(fd, 0, SEEK_SET) == -1) {
+ sudo_warn("lseek");
+ debug_return_int(-1);
+ }
+
+ debug_return_int(0);
+}
+
+/*
+ * Copy the contents of src_fd into dst_fd.
+ * Returns 0 on success or -1 on error.
+ */
+int
+sudo_copy_file(const char *src, int src_fd, off_t src_len, const char *dst,
+ int dst_fd, off_t dst_len)
+{
+ char buf[BUFSIZ];
+ ssize_t nwritten, nread;
+ debug_decl(sudo_copy_file, SUDO_DEBUG_UTIL);
+
+ /* Extend the file to the new size if larger before copying. */
+ if (dst_len > 0 && src_len > dst_len) {
+ if (sudo_extend_file(dst_fd, dst, src_len) == -1)
+ goto write_error;
+ }
+
+ /* Overwrite the old file with the new contents. */
+ while ((nread = read(src_fd, buf, sizeof(buf))) > 0) {
+ ssize_t off = 0;
+ do {
+ nwritten = write(dst_fd, buf + off, nread - off);
+ if (nwritten == -1)
+ goto write_error;
+ off += nwritten;
+ } while (nread > off);
+ }
+ if (nread == 0) {
+ /* success, read to EOF */
+ if (src_len < dst_len) {
+ /* We don't open with O_TRUNC so must truncate manually. */
+ if (ftruncate(dst_fd, src_len) == -1) {
+ sudo_debug_printf(
+ SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO|SUDO_DEBUG_ERRNO,
+ "unable to truncate %s to %lld", dst, (long long)src_len);
+ goto write_error;
+ }
+ }
+ debug_return_int(0);
+ } else if (nread < 0) {
+ sudo_warn(U_("unable to read from %s"), src);
+ debug_return_int(-1);
+ } else {
+write_error:
+ sudo_warn(U_("unable to write to %s"), dst);
+ debug_return_int(-1);
+ }
+}
diff -up ./src/Makefile.in.symbolic-link-attack-2 ./src/Makefile.in
--- ./src/Makefile.in.symbolic-link-attack-2 2019-10-28 13:28:54.000000000 +0100
+++ ./src/Makefile.in 2021-02-02 15:31:20.555340446 +0100
@@ -120,16 +120,17 @@ SHELL = @SHELL@
PROGS = @PROGS@
-OBJS = conversation.o env_hooks.o exec.o exec_common.o exec_monitor.o \
- exec_nopty.o exec_pty.o get_pty.o hooks.o limits.o load_plugins.o \
- net_ifs.o parse_args.o preserve_fds.o signal.o sudo.o sudo_edit.o \
- tcsetpgrp_nobg.o tgetpass.o ttyname.o utmp.o @SUDO_OBJS@
+OBJS = conversation.o copy_file.o env_hooks.o exec.o exec_common.o \
+ exec_monitor.o exec_nopty.o exec_pty.o get_pty.o hooks.o \
+ limits.o load_plugins.o net_ifs.o parse_args.o preserve_fds.o \
+ signal.o sudo.o sudo_edit.o tcsetpgrp_nobg.o tgetpass.o \
+ ttyname.o utmp.o @SUDO_OBJS@
IOBJS = $(OBJS:.o=.i) sesh.i
POBJS = $(IOBJS:.i=.plog)
-SESH_OBJS = sesh.o exec_common.o
+SESH_OBJS = copy_file.o exec_common.o sesh.o
CHECK_NOEXEC_OBJS = check_noexec.o exec_common.o
@@ -335,6 +336,22 @@ conversation.i: $(srcdir)/conversation.c
$(CC) -E -o $@ $(CPPFLAGS) $<
conversation.plog: conversation.i
rm -f $@; pvs-studio --cfg $(PVS_CFG) --sourcetree-root $(top_srcdir) --skip-cl-exe yes --source-file $(srcdir)/conversation.c --i-file $< --output-file $@
+copy_file.o: $(srcdir)/copy_file.c $(incdir)/compat/stdbool.h \
+ $(incdir)/sudo_compat.h $(incdir)/sudo_conf.h \
+ $(incdir)/sudo_debug.h $(incdir)/sudo_event.h \
+ $(incdir)/sudo_fatal.h $(incdir)/sudo_gettext.h \
+ $(incdir)/sudo_queue.h $(incdir)/sudo_util.h $(srcdir)/sudo.h \
+ $(top_builddir)/config.h $(top_builddir)/pathnames.h
+ $(CC) -c $(CPPFLAGS) $(CFLAGS) $(ASAN_CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(srcdir)/copy_file.c
+copy_file.i: $(srcdir)/copy_file.c $(incdir)/compat/stdbool.h \
+ $(incdir)/sudo_compat.h $(incdir)/sudo_conf.h \
+ $(incdir)/sudo_debug.h $(incdir)/sudo_event.h \
+ $(incdir)/sudo_fatal.h $(incdir)/sudo_gettext.h \
+ $(incdir)/sudo_queue.h $(incdir)/sudo_util.h $(srcdir)/sudo.h \
+ $(top_builddir)/config.h $(top_builddir)/pathnames.h
+ $(CC) -E -o $@ $(CPPFLAGS) $<
+copy_file.plog: copy_file.i
+ rm -f $@; pvs-studio --cfg $(PVS_CFG) --sourcetree-root $(top_srcdir) --skip-cl-exe yes --source-file $(srcdir)/copy_file.c --i-file $< --output-file $@
env_hooks.o: $(srcdir)/env_hooks.c $(incdir)/compat/stdbool.h \
$(incdir)/sudo_compat.h $(incdir)/sudo_conf.h \
$(incdir)/sudo_debug.h $(incdir)/sudo_dso.h \
diff -up ./src/sesh.c.symbolic-link-attack-2 ./src/sesh.c
--- ./src/sesh.c.symbolic-link-attack-2 2019-10-28 13:28:52.000000000 +0100
+++ ./src/sesh.c 2021-02-02 15:31:20.555340446 +0100
@@ -1,7 +1,7 @@
/*
* SPDX-License-Identifier: ISC
*
- * Copyright (c) 2008, 2010-2018 Todd C. Miller <Todd.Miller@sudo.ws>
+ * Copyright (c) 2008, 2010-2018, 2020 Todd C. Miller <Todd.Miller@sudo.ws>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -182,7 +182,7 @@ sesh_sudoedit(int argc, char *argv[])
* so that it's ensured that the temporary files are
* created by us and that we are not opening any symlinks.
*/
- oflags_dst = O_WRONLY|O_TRUNC|O_CREAT|(post ? follow : O_EXCL);
+ oflags_dst = O_WRONLY|O_CREAT|(post ? follow : O_EXCL);
for (i = 0; i < argc - 1; i += 2) {
const char *path_src = argv[i];
const char *path_dst = argv[i + 1];
@@ -214,14 +214,29 @@ sesh_sudoedit(int argc, char *argv[])
}
if (fd_src != -1) {
- while ((nread = read(fd_src, buf, sizeof(buf))) > 0) {
- if ((nwritten = write(fd_dst, buf, nread)) != nread) {
- sudo_warn("%s", path_src);
- if (post) {
- ret = SESH_ERR_SOME_FILES;
- goto nocleanup;
- } else
- goto cleanup_0;
+ off_t len_src = -1;
+ off_t len_dst = -1;
+
+ if (post) {
+ if (fstat(fd_src, &sb) != 0) {
+ ret = SESH_ERR_SOME_FILES;
+ goto nocleanup;
+ }
+ len_src = sb.st_size;
+ if (fstat(fd_dst, &sb) != 0) {
+ ret = SESH_ERR_SOME_FILES;
+ goto nocleanup;
+ }
+ len_dst = sb.st_size;
+ }
+
+ if (sudo_copy_file(path_src, fd_src, len_src, path_dst, fd_dst,
+ len_dst) == -1) {
+ if (post) {
+ ret = SESH_ERR_SOME_FILES;
+ goto nocleanup;
+ } else {
+ goto cleanup_0;
}
}
}
diff -up ./src/sudo_edit.c.symbolic-link-attack-2 ./src/sudo_edit.c
--- ./src/sudo_edit.c.symbolic-link-attack-2 2021-02-02 15:31:20.554340459 +0100
+++ ./src/sudo_edit.c 2021-02-02 15:31:54.355884326 +0100
@@ -42,7 +42,6 @@
#include <grp.h>
#include <pwd.h>
#include <signal.h>
-#include <errno.h>
#include <fcntl.h>
#include "sudo.h"
@@ -551,8 +550,6 @@ sudo_edit_create_tfiles(struct command_d
struct tempfile *tf, char *files[], int nfiles)
{
int i, j, tfd, ofd, rc;
- char buf[BUFSIZ];
- ssize_t nwritten, nread;
struct timespec times[2];
struct stat sb;
debug_decl(sudo_edit_create_tfiles, SUDO_DEBUG_EDIT)
@@ -648,18 +645,7 @@ sudo_edit_create_tfiles(struct command_d
debug_return_int(-1);
}
if (ofd != -1) {
- while ((nread = read(ofd, buf, sizeof(buf))) > 0) {
- if ((nwritten = write(tfd, buf, nread)) != nread) {
- if (nwritten == -1)
- sudo_warn("%s", tf[j].tfile);
- else
- sudo_warnx(U_("%s: short write"), tf[j].tfile);
- break;
- }
- }
- if (nread != 0) {
- if (nread < 0)
- sudo_warn("%s", files[i]);
+ if (sudo_copy_file(tf[j].ofile, ofd, tf[j].osize, tf[j].tfile, tfd, -1) == -1) {
close(ofd);
close(tfd);
debug_return_int(-1);
@@ -689,51 +675,6 @@ sudo_edit_create_tfiles(struct command_d
}
/*
- * Extend the given fd to the specified size in bytes.
- * We do this to allocate disk space up-front before overwriting
- * the original file with the temporary. Otherwise, we could
- * we run out of disk space after truncating the original file.
- */
-static int
-sudo_edit_extend_file(int fd, off_t new_size)
-{
- off_t old_size, size;
- ssize_t nwritten;
- char zeroes[1024] = { '\0' };
- debug_decl(sudo_edit_extend_file, SUDO_DEBUG_EDIT);
-
- if ((old_size = lseek(fd, 0, SEEK_END)) == -1) {
- sudo_warn("lseek");
- debug_return_int(-1);
- }
- sudo_debug_printf(SUDO_DEBUG_INFO, "%s: extending file from %lld to %lld",
- __func__, (long long)old_size, (long long)new_size);
-
- for (size = old_size; size < new_size; size += nwritten) {
- size_t len = new_size - size;
- if (len > sizeof(zeroes))
- len = sizeof(zeroes);
- nwritten = write(fd, zeroes, len);
- if (nwritten == -1) {
- int serrno = errno;
- if (ftruncate(fd, old_size) == -1) {
- sudo_debug_printf(
- SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO|SUDO_DEBUG_ERRNO,
- "unable to truncate to %lld", (long long)old_size);
- }
- errno = serrno;
- debug_return_int(-1);
- }
- }
- if (lseek(fd, 0, SEEK_SET) == -1) {
- sudo_warn("lseek");
- debug_return_int(-1);
- }
-
- debug_return_int(0);
-}
-
-/*
* Copy the temporary files specified in tf to the originals.
* Returns the number of copy errors or 0 if completely successful.
*/
@@ -741,9 +682,7 @@ static int
sudo_edit_copy_tfiles(struct command_details *command_details,
struct tempfile *tf, int nfiles, struct timespec *times)
{
- int i, tfd, ofd, rc, errors = 0;
- char buf[BUFSIZ];
- ssize_t nwritten, nread;
+ int i, tfd, ofd, errors = 0;
struct timespec ts;
struct stat sb;
mode_t oldmask;
@@ -751,7 +690,7 @@ sudo_edit_copy_tfiles(struct command_det
/* Copy contents of temp files to real ones. */
for (i = 0; i < nfiles; i++) {
- rc = -1;
+ int rc = -1;
sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO,
"seteuid(%u)", (unsigned int)user_details.uid);
if (seteuid(user_details.uid) != 0)
@@ -764,8 +703,8 @@ sudo_edit_copy_tfiles(struct command_det
"seteuid(%u)", ROOT_UID);
if (seteuid(ROOT_UID) != 0)
sudo_fatal("seteuid(ROOT_UID)");
- if (rc || !S_ISREG(sb.st_mode)) {
- if (rc)
+ if (rc == -1 || !S_ISREG(sb.st_mode)) {
+ if (rc == -1)
sudo_warn("%s", tf[i].tfile);
else
sudo_warnx(U_("%s: not a regular file"), tf[i].tfile);
@@ -796,46 +735,19 @@ sudo_edit_copy_tfiles(struct command_det
umask(oldmask);
switch_user(ROOT_UID, user_details.egid,
user_details.ngroups, user_details.groups);
- if (ofd == -1)
- goto write_error;
- /* Extend the file to the new size if larger before copying. */
- if (tf[i].osize > 0 && sb.st_size > tf[i].osize) {
- if (sudo_edit_extend_file(ofd, sb.st_size) == -1)
- goto write_error;
+ if (ofd == -1) {
+ sudo_warn(U_("unable to write to %s"), tf[i].ofile);
+ goto bad;
}
+
/* Overwrite the old file with the new contents. */
- while ((nread = read(tfd, buf, sizeof(buf))) > 0) {
- ssize_t off = 0;
- do {
- nwritten = write(ofd, buf + off, nread - off);
- if (nwritten == -1)
- goto write_error;
- off += nwritten;
- } while (nread > off);
- }
- if (nread == 0) {
- /* success, read to EOF */
- if (tf[i].osize > 0 && sb.st_size < tf[i].osize) {
- /* We don't open with O_TRUNC so must truncate manually. */
- if (ftruncate(ofd, sb.st_size) == -1) {
- sudo_debug_printf(
- SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO|SUDO_DEBUG_ERRNO,
- "unable to truncate %s to %lld", tf[i].ofile,
- (long long)sb.st_size);
- goto write_error;
- }
- }
- unlink(tf[i].tfile);
- } else if (nread < 0) {
- sudo_warn(U_("unable to read temporary file"));
- sudo_warnx(U_("contents of edit session left in %s"), tf[i].tfile);
- errors++;
- } else {
-write_error:
- sudo_warn(U_("unable to write to %s"), tf[i].ofile);
+ if (sudo_copy_file(tf[i].tfile, tfd, sb.st_size, tf[i].ofile, ofd,
+ tf[i].osize) == -1) {
+bad:
sudo_warnx(U_("contents of edit session left in %s"), tf[i].tfile);
errors++;
}
+
if (ofd != -1)
close(ofd);
close(tfd);
diff -up ./src/sudo_exec.h.symbolic-link-attack-2 ./src/sudo_exec.h
--- ./src/sudo_exec.h.symbolic-link-attack-2 2019-10-28 13:27:39.000000000 +0100
+++ ./src/sudo_exec.h 2021-02-02 15:31:20.556340432 +0100
@@ -84,6 +84,9 @@
struct command_details;
struct command_status;
+/* copy_file.c */
+int sudo_copy_file(const char *src, int src_fd, off_t src_len, const char *dst, int dst_fd, off_t dst_len);
+
/* exec.c */
void exec_cmnd(struct command_details *details, int errfd);
void terminate_command(pid_t pid, bool use_pgrp);

View File

@ -0,0 +1,345 @@
diff -up ./src/exec_monitor.c.symbolic-link-attack-3 ./src/exec_monitor.c
--- ./src/exec_monitor.c.symbolic-link-attack-3 2019-10-28 13:27:39.000000000 +0100
+++ ./src/exec_monitor.c 2021-02-02 17:11:32.382020407 +0100
@@ -613,7 +613,7 @@ exec_monitor(struct command_details *det
#ifdef HAVE_SELINUX
if (ISSET(details->flags, CD_RBAC_ENABLED)) {
if (selinux_setup(details->selinux_role, details->selinux_type,
- details->tty, io_fds[SFD_SLAVE]) == -1)
+ details->tty, io_fds[SFD_SLAVE], true) == -1)
goto bad;
}
#endif
diff -up ./src/exec_nopty.c.symbolic-link-attack-3 ./src/exec_nopty.c
--- ./src/exec_nopty.c.symbolic-link-attack-3 2019-10-28 13:27:39.000000000 +0100
+++ ./src/exec_nopty.c 2021-02-02 17:11:32.382020407 +0100
@@ -381,7 +381,7 @@ exec_nopty(struct command_details *detai
#ifdef HAVE_SELINUX
if (ISSET(details->flags, CD_RBAC_ENABLED)) {
if (selinux_setup(details->selinux_role, details->selinux_type,
- details->tty, -1) == -1) {
+ details->tty, -1, true) == -1) {
cstat->type = CMD_ERRNO;
cstat->val = errno;
debug_return;
diff -up ./src/selinux.c.symbolic-link-attack-3 ./src/selinux.c
--- ./src/selinux.c.symbolic-link-attack-3 2019-10-28 13:27:39.000000000 +0100
+++ ./src/selinux.c 2021-02-02 17:11:32.382020407 +0100
@@ -363,7 +363,7 @@ bad:
*/
int
selinux_setup(const char *role, const char *type, const char *ttyn,
- int ptyfd)
+ int ptyfd, bool label_tty)
{
int ret = -1;
debug_decl(selinux_setup, SUDO_DEBUG_SELINUX)
@@ -392,7 +392,7 @@ selinux_setup(const char *role, const ch
sudo_debug_printf(SUDO_DEBUG_INFO, "%s: new context %s", __func__,
se_state.new_context);
- if (relabel_tty(ttyn, ptyfd) == -1) {
+ if (label_tty && relabel_tty(ttyn, ptyfd) == -1) {
sudo_warn(U_("unable to set tty context to %s"), se_state.new_context);
goto done;
}
@@ -408,6 +408,28 @@ done:
debug_return_int(ret);
}
+int
+selinux_setcon(void)
+{
+ debug_decl(selinux_setcon, SUDO_DEBUG_SELINUX);
+
+ if (setexeccon(se_state.new_context)) {
+ sudo_warn(U_("unable to set exec context to %s"), se_state.new_context);
+ if (se_state.enforcing)
+ debug_return_int(-1);
+ }
+
+#ifdef HAVE_SETKEYCREATECON
+ if (setkeycreatecon(se_state.new_context)) {
+ sudo_warn(U_("unable to set key creation context to %s"), se_state.new_context);
+ if (se_state.enforcing)
+ debug_return_int(-1);
+ }
+#endif /* HAVE_SETKEYCREATECON */
+
+ debug_return_int(0);
+}
+
void
selinux_execve(int fd, const char *path, char *const argv[], char *envp[],
bool noexec)
@@ -424,19 +446,9 @@ selinux_execve(int fd, const char *path,
debug_return;
}
- if (setexeccon(se_state.new_context)) {
- sudo_warn(U_("unable to set exec context to %s"), se_state.new_context);
- if (se_state.enforcing)
- debug_return;
- }
-
-#ifdef HAVE_SETKEYCREATECON
- if (setkeycreatecon(se_state.new_context)) {
- sudo_warn(U_("unable to set key creation context to %s"), se_state.new_context);
- if (se_state.enforcing)
- debug_return;
- }
-#endif /* HAVE_SETKEYCREATECON */
+ /* Set SELinux exec and keycreate contexts. */
+ if (selinux_setcon() == -1)
+ debug_return;
/*
* Build new argv with sesh as argv[0].
diff -up ./src/sudo.c.symbolic-link-attack-3 ./src/sudo.c
--- ./src/sudo.c.symbolic-link-attack-3 2021-02-02 17:12:32.773182386 +0100
+++ ./src/sudo.c 2021-02-02 17:12:48.510964009 +0100
@@ -971,10 +971,6 @@ run_command(struct command_details *deta
case CMD_WSTATUS:
/* Command ran, exited or was killed. */
status = cstat.val;
-#ifdef HAVE_SELINUX
- if (ISSET(details->flags, CD_SUDOEDIT_COPY))
- break;
-#endif
sudo_debug_printf(SUDO_DEBUG_DEBUG,
"calling policy close with wait status %d", status);
policy_close(&policy_plugin, status, 0);
diff -up ./src/sudo_edit.c.symbolic-link-attack-3 ./src/sudo_edit.c
--- ./src/sudo_edit.c.symbolic-link-attack-3 2021-02-02 17:11:32.380020435 +0100
+++ ./src/sudo_edit.c 2021-02-02 17:11:32.382020407 +0100
@@ -757,28 +757,54 @@ bad:
#ifdef HAVE_SELINUX
static int
+selinux_run_helper(char *argv[], char *envp[])
+{
+ int status, ret = SESH_ERR_FAILURE;
+ const char *sesh;
+ pid_t child, pid;
+ debug_decl(selinux_run_helper, SUDO_DEBUG_EDIT);
+
+ sesh = sudo_conf_sesh_path();
+ if (sesh == NULL) {
+ sudo_warnx("internal error: sesh path not set");
+ debug_return_int(-1);
+ }
+
+ child = sudo_debug_fork();
+ switch (child) {
+ case -1:
+ sudo_warn(U_("unable to fork"));
+ break;
+ case 0:
+ /* child runs sesh in new context */
+ if (selinux_setcon() == 0)
+ execve(sesh, argv, envp);
+ _exit(SESH_ERR_FAILURE);
+ default:
+ /* parent waits */
+ do {
+ pid = waitpid(child, &status, 0);
+ } while (pid == -1 && errno == EINTR);
+
+ ret = WIFSIGNALED(status) ? SESH_ERR_KILLED : WEXITSTATUS(status);
+ }
+
+ debug_return_int(ret);
+}
+
+static int
selinux_edit_create_tfiles(struct command_details *command_details,
struct tempfile *tf, char *files[], int nfiles)
{
char **sesh_args, **sesh_ap;
int i, rc, sesh_nargs;
struct stat sb;
- struct command_details saved_command_details;
debug_decl(selinux_edit_create_tfiles, SUDO_DEBUG_EDIT)
-
- /* Prepare selinux stuff (setexeccon) */
- if (selinux_setup(command_details->selinux_role,
- command_details->selinux_type, NULL, -1) != 0)
- debug_return_int(-1);
if (nfiles < 1)
debug_return_int(0);
/* Construct common args for sesh */
- memcpy(&saved_command_details, command_details, sizeof(struct command_details));
- command_details->command = _PATH_SUDO_SESH;
- command_details->flags |= CD_SUDOEDIT_COPY;
-
sesh_nargs = 4 + (nfiles * 2) + 1;
sesh_args = sesh_ap = reallocarray(NULL, sesh_nargs, sizeof(char *));
if (sesh_args == NULL) {
@@ -791,6 +817,7 @@ selinux_edit_create_tfiles(struct comman
*sesh_ap++ = "-h";
*sesh_ap++ = "0";
+ /* XXX - temp files should be created with user's context */
for (i = 0; i < nfiles; i++) {
char *tfile, *ofile = files[i];
int tfd;
@@ -820,8 +847,7 @@ selinux_edit_create_tfiles(struct comman
*sesh_ap = NULL;
/* Run sesh -e [-h] 0 <o1> <t1> ... <on> <tn> */
- command_details->argv = sesh_args;
- rc = run_command(command_details);
+ rc = selinux_run_helper(sesh_args, command_details->envp);
switch (rc) {
case SESH_SUCCESS:
break;
@@ -829,15 +855,12 @@ selinux_edit_create_tfiles(struct comman
sudo_fatalx(U_("sesh: internal error: odd number of paths"));
case SESH_ERR_NO_FILES:
sudo_fatalx(U_("sesh: unable to create temporary files"));
+ case SESH_ERR_KILLED:
+ sudo_fatalx(U_("sesh: killed by a signal"));
default:
sudo_fatalx(U_("sesh: unknown error %d"), rc);
}
- /* Restore saved command_details. */
- command_details->command = saved_command_details.command;
- command_details->flags = saved_command_details.flags;
- command_details->argv = saved_command_details.argv;
-
/* Chown to user's UID so they can edit the temporary files. */
for (i = 0; i < nfiles; i++) {
if (chown(tf[i].tfile, user_details.uid, user_details.gid) != 0) {
@@ -858,24 +881,14 @@ selinux_edit_copy_tfiles(struct command_
{
char **sesh_args, **sesh_ap;
int i, rc, sesh_nargs, ret = 1;
- struct command_details saved_command_details;
struct timespec ts;
struct stat sb;
debug_decl(selinux_edit_copy_tfiles, SUDO_DEBUG_EDIT)
-
- /* Prepare selinux stuff (setexeccon) */
- if (selinux_setup(command_details->selinux_role,
- command_details->selinux_type, NULL, -1) != 0)
- debug_return_int(1);
if (nfiles < 1)
debug_return_int(0);
/* Construct common args for sesh */
- memcpy(&saved_command_details, command_details, sizeof(struct command_details));
- command_details->command = _PATH_SUDO_SESH;
- command_details->flags |= CD_SUDOEDIT_COPY;
-
sesh_nargs = 3 + (nfiles * 2) + 1;
sesh_args = sesh_ap = reallocarray(NULL, sesh_nargs, sizeof(char *));
if (sesh_args == NULL) {
@@ -913,32 +926,29 @@ selinux_edit_copy_tfiles(struct command_
if (sesh_ap - sesh_args > 3) {
/* Run sesh -e 1 <t1> <o1> ... <tn> <on> */
- command_details->argv = sesh_args;
- rc = run_command(command_details);
+ rc = selinux_run_helper(sesh_args, command_details->envp);
switch (rc) {
case SESH_SUCCESS:
ret = 0;
break;
case SESH_ERR_NO_FILES:
sudo_warnx(U_("unable to copy temporary files back to their original location"));
- sudo_warnx(U_("contents of edit session left in %s"), edit_tmpdir);
break;
case SESH_ERR_SOME_FILES:
sudo_warnx(U_("unable to copy some of the temporary files back to their original location"));
- sudo_warnx(U_("contents of edit session left in %s"), edit_tmpdir);
+ break;
+ case SESH_ERR_KILLED:
+ sudo_warnx(U_("sesh: killed by a signal"));
break;
default:
sudo_warnx(U_("sesh: unknown error %d"), rc);
break;
}
+ if (ret != 0)
+ sudo_warnx(U_("contents of edit session left in %s"), edit_tmpdir);
}
free(sesh_args);
- /* Restore saved command_details. */
- command_details->command = saved_command_details.command;
- command_details->flags = saved_command_details.flags;
- command_details->argv = saved_command_details.argv;
-
debug_return_int(ret);
}
#endif /* HAVE_SELINUX */
@@ -990,6 +1000,15 @@ sudo_edit(struct command_details *comman
goto cleanup;
}
+#ifdef HAVE_SELINUX
+ /* Compute new SELinux security context. */
+ if (ISSET(command_details->flags, CD_RBAC_ENABLED)) {
+ if (selinux_setup(command_details->selinux_role,
+ command_details->selinux_type, NULL, -1, false) != 0)
+ goto cleanup;
+ }
+#endif
+
/* Copy editor files to temporaries. */
tf = calloc(nfiles, sizeof(*tf));
if (tf == NULL) {
@@ -1025,6 +1044,7 @@ sudo_edit(struct command_details *comman
/*
* Run the editor with the invoking user's creds,
* keeping track of the time spent in the editor.
+ * XXX - should run editor with user's context
*/
if (sudo_gettime_real(&times[0]) == -1) {
sudo_warn(U_("unable to read the clock"));
diff -up ./src/sudo_exec.h.symbolic-link-attack-3 ./src/sudo_exec.h
--- ./src/sudo_exec.h.symbolic-link-attack-3 2021-02-02 17:11:32.380020435 +0100
+++ ./src/sudo_exec.h 2021-02-02 17:11:32.382020407 +0100
@@ -73,6 +73,7 @@
*/
#define SESH_SUCCESS 0 /* successful operation */
#define SESH_ERR_FAILURE 1 /* unspecified error */
+#define SESH_ERR_KILLED 2 /* killed by a signal */
#define SESH_ERR_INVALID 30 /* invalid -e arg value */
#define SESH_ERR_BAD_PATHS 31 /* odd number of paths */
#define SESH_ERR_NO_FILES 32 /* copy error, no files copied */
diff -up ./src/sudo.h.symbolic-link-attack-3 ./src/sudo.h
--- ./src/sudo.h.symbolic-link-attack-3 2019-10-28 13:28:52.000000000 +0100
+++ ./src/sudo.h 2021-02-02 17:11:32.382020407 +0100
@@ -135,12 +135,11 @@ struct user_details {
#define CD_USE_PTY 0x001000
#define CD_SET_UTMP 0x002000
#define CD_EXEC_BG 0x004000
-#define CD_SUDOEDIT_COPY 0x008000
-#define CD_SUDOEDIT_FOLLOW 0x010000
-#define CD_SUDOEDIT_CHECKDIR 0x020000
-#define CD_SET_GROUPS 0x040000
-#define CD_LOGIN_SHELL 0x080000
-#define CD_OVERRIDE_UMASK 0x100000
+#define CD_SUDOEDIT_FOLLOW 0x008000
+#define CD_SUDOEDIT_CHECKDIR 0x010000
+#define CD_SET_GROUPS 0x020000
+#define CD_LOGIN_SHELL 0x040000
+#define CD_OVERRIDE_UMASK 0x080000
struct preserved_fd {
TAILQ_ENTRY(preserved_fd) entries;
@@ -240,7 +239,8 @@ int os_init_openbsd(int argc, char *argv
/* selinux.c */
int selinux_restore_tty(void);
int selinux_setup(const char *role, const char *type, const char *ttyn,
- int ttyfd);
+ int ttyfd, bool label_tty);
+int selinux_setcon(void);
void selinux_execve(int fd, const char *path, char *const argv[],
char *envp[], bool noexec);

View File

@ -0,0 +1,380 @@
diff -up ./src/copy_file.c.symbolic-link-attack-4 ./src/copy_file.c
--- ./src/copy_file.c.symbolic-link-attack-4 2021-02-02 16:35:18.453036846 +0100
+++ ./src/copy_file.c 2021-02-02 16:38:09.430731749 +0100
@@ -23,6 +23,7 @@
#include <config.h>
+#include <sys/stat.h>
#include <sys/types.h>
#include <stdio.h>
@@ -126,3 +127,35 @@ write_error:
debug_return_int(-1);
}
}
+
+#ifdef HAVE_SELINUX
+bool
+sudo_check_temp_file(int tfd, const char *tfile, uid_t uid, struct stat *sb)
+{
+ struct stat sbuf;
+ debug_decl(sudo_check_temp_file, SUDO_DEBUG_UTIL);
+
+ if (sb == NULL)
+ sb = &sbuf;
+
+ if (fstat(tfd, sb) == -1) {
+ sudo_warn(U_("unable to stat %s"), tfile);
+ debug_return_bool(false);
+ }
+ if (!S_ISREG(sb->st_mode)) {
+ sudo_warnx(U_("%s: not a regular file"), tfile);
+ debug_return_bool(false);
+ }
+ if ((sb->st_mode & ALLPERMS) != (S_IRUSR|S_IWUSR)) {
+ sudo_warnx(U_("%s: bad file mode: 0%o"), tfile,
+ (unsigned int)(sb->st_mode & ALLPERMS));
+ debug_return_bool(false);
+ }
+ if (sb->st_uid != uid) {
+ sudo_warnx(U_("%s is owned by uid %u, should be %u"),
+ tfile, (unsigned int)sb->st_uid, (unsigned int)uid);
+ debug_return_bool(false);
+ }
+ debug_return_bool(true);
+}
+#endif /* SELINUX */
diff -up ./src/sesh.c.symbolic-link-attack-4 ./src/sesh.c
--- ./src/sesh.c.symbolic-link-attack-4 2021-02-02 16:35:18.450036887 +0100
+++ ./src/sesh.c 2021-02-02 16:38:52.907146897 +0100
@@ -134,7 +134,7 @@ main(int argc, char *argv[], char *envp[
static int
sesh_sudoedit(int argc, char *argv[])
{
- int i, oflags_dst, post, ret = SESH_ERR_FAILURE;
+ int i, oflags_src, oflags_dst, post, ret = SESH_ERR_FAILURE;
int fd_src = -1, fd_dst = -1, follow = 0;
ssize_t nread, nwritten;
struct stat sb;
@@ -178,10 +178,12 @@ sesh_sudoedit(int argc, char *argv[])
debug_return_int(SESH_ERR_BAD_PATHS);
/*
- * Use O_EXCL if we are not in the post editing stage
- * so that it's ensured that the temporary files are
- * created by us and that we are not opening any symlinks.
+ * In the pre-editing stage, use O_EXCL to ensure that the temporary
+ * files are created by us and that we are not opening any symlinks.
+ * In the post-editing stage, use O_NOFOLLOW so we don't follow symlinks
+ * when opening the temporary files.
*/
+ oflags_src = O_RDONLY|(post ? O_NONBLOCK|O_NOFOLLOW : follow);
oflags_dst = O_WRONLY|O_CREAT|(post ? follow : O_EXCL);
for (i = 0; i < argc - 1; i += 2) {
const char *path_src = argv[i];
@@ -191,7 +193,7 @@ sesh_sudoedit(int argc, char *argv[])
* doesn't exist, that's OK, we'll create an empty
* destination file.
*/
- if ((fd_src = open(path_src, O_RDONLY|follow, S_IRUSR|S_IWUSR)) < 0) {
+ if ((fd_src = open(path_src, oflags_src, S_IRUSR|S_IWUSR)) < 0) {
if (errno != ENOENT) {
sudo_warn("%s", path_src);
if (post) {
@@ -201,6 +203,14 @@ sesh_sudoedit(int argc, char *argv[])
goto cleanup_0;
}
}
+ if (post) {
+ /* Make sure the temporary file is safe and has the proper owner. */
+ if (!sudo_check_temp_file(fd_src, path_src, geteuid(), &sb)) {
+ ret = SESH_ERR_SOME_FILES;
+ goto nocleanup;
+ }
+ fcntl(fd_src, F_SETFL, fcntl(fd_src, F_GETFL, 0) & ~O_NONBLOCK);
+ }
if ((fd_dst = open(path_dst, oflags_dst, post ?
(S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH) : (S_IRUSR|S_IWUSR))) < 0) {
@@ -218,10 +228,7 @@ sesh_sudoedit(int argc, char *argv[])
off_t len_dst = -1;
if (post) {
- if (fstat(fd_src, &sb) != 0) {
- ret = SESH_ERR_SOME_FILES;
- goto nocleanup;
- }
+ /* sudo_check_temp_file() filled in sb for us. */
len_src = sb.st_size;
if (fstat(fd_dst, &sb) != 0) {
ret = SESH_ERR_SOME_FILES;
diff -up ./src/sudo_edit.c.symbolic-link-attack-4 ./src/sudo_edit.c
--- ./src/sudo_edit.c.symbolic-link-attack-4 2021-02-02 16:35:18.452036860 +0100
+++ ./src/sudo_edit.c 2021-02-02 16:54:25.943429580 +0100
@@ -253,8 +253,10 @@ sudo_edit_mktemp(const char *ofile, char
} else {
len = asprintf(tfile, "%s/%s.XXXXXXXX", edit_tmpdir, cp);
}
- if (len == -1)
- sudo_fatalx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
+ if (len == -1) {
+ sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
+ debug_return_int(-1);
+ }
tfd = mkstemps(*tfile, suff ? strlen(suff) : 0);
sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO,
"%s -> %s, fd %d", ofile, *tfile, tfd);
@@ -757,7 +759,8 @@ bad:
#ifdef HAVE_SELINUX
static int
-selinux_run_helper(char *argv[], char *envp[])
+selinux_run_helper(uid_t uid, gid_t gid, int ngroups, GETGROUPS_T *groups,
+ char *const argv[], char *const envp[])
{
int status, ret = SESH_ERR_FAILURE;
const char *sesh;
@@ -777,8 +780,10 @@ selinux_run_helper(char *argv[], char *e
break;
case 0:
/* child runs sesh in new context */
- if (selinux_setcon() == 0)
+ if (selinux_setcon() == 0) {
+ switch_user(uid, gid, ngroups, groups);
execve(sesh, argv, envp);
+ }
_exit(SESH_ERR_FAILURE);
default:
/* parent waits */
@@ -797,7 +802,7 @@ selinux_edit_create_tfiles(struct comman
struct tempfile *tf, char *files[], int nfiles)
{
char **sesh_args, **sesh_ap;
- int i, rc, sesh_nargs;
+ int i, rc, error, sesh_nargs, ret = -1;
struct stat sb;
debug_decl(selinux_edit_create_tfiles, SUDO_DEBUG_EDIT)
@@ -809,7 +814,7 @@ selinux_edit_create_tfiles(struct comman
sesh_args = sesh_ap = reallocarray(NULL, sesh_nargs, sizeof(char *));
if (sesh_args == NULL) {
sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
- debug_return_int(-1);
+ goto done;
}
*sesh_ap++ = "sesh";
*sesh_ap++ = "-e";
@@ -817,7 +822,6 @@ selinux_edit_create_tfiles(struct comman
*sesh_ap++ = "-h";
*sesh_ap++ = "0";
- /* XXX - temp files should be created with user's context */
for (i = 0; i < nfiles; i++) {
char *tfile, *ofile = files[i];
int tfd;
@@ -835,8 +839,7 @@ selinux_edit_create_tfiles(struct comman
if (tfd == -1) {
sudo_warn("mkstemps");
free(tfile);
- free(sesh_args);
- debug_return_int(-1);
+ goto done;
}
/* Helper will re-create temp file with proper security context. */
close(tfd);
@@ -847,8 +850,10 @@ selinux_edit_create_tfiles(struct comman
*sesh_ap = NULL;
/* Run sesh -e [-h] 0 <o1> <t1> ... <on> <tn> */
- rc = selinux_run_helper(sesh_args, command_details->envp);
- switch (rc) {
+ error = selinux_run_helper(command_details->uid, command_details->gid,
+ command_details->ngroups, command_details->groups, sesh_args,
+ command_details->envp);
+ switch (error) {
case SESH_SUCCESS:
break;
case SESH_ERR_BAD_PATHS:
@@ -858,21 +863,34 @@ selinux_edit_create_tfiles(struct comman
case SESH_ERR_KILLED:
sudo_fatalx(U_("sesh: killed by a signal"));
default:
- sudo_fatalx(U_("sesh: unknown error %d"), rc);
+ sudo_fatalx(U_("sesh: unknown error %d"), error);
+ goto done;
}
- /* Chown to user's UID so they can edit the temporary files. */
for (i = 0; i < nfiles; i++) {
- if (chown(tf[i].tfile, user_details.uid, user_details.gid) != 0) {
- sudo_warn("unable to chown(%s) to %d:%d for editing",
- tf[i].tfile, user_details.uid, user_details.gid);
- }
+ int tfd = open(tf[i].tfile, O_RDONLY|O_NONBLOCK|O_NOFOLLOW);
+ if (tfd == -1) {
+ sudo_warn(U_("unable to open %s"), tf[i].tfile);
+ goto done;
+ }
+ if (!sudo_check_temp_file(tfd, tf[i].tfile, command_details->uid, NULL)) {
+ close(tfd);
+ goto done;
+ }
+ if (fchown(tfd, user_details.uid, user_details.gid) != 0) {
+ sudo_warn("unable to chown(%s) to %d:%d for editing",
+ tf[i].tfile, user_details.uid, user_details.gid);
+ close(tfd);
+ goto done;
+ }
+ close(tfd);
}
+done:
/* Contents of tf will be freed by caller. */
free(sesh_args);
- return (nfiles);
+ debug_return_int(ret);
}
static int
@@ -880,7 +898,8 @@ selinux_edit_copy_tfiles(struct command_
struct tempfile *tf, int nfiles, struct timespec *times)
{
char **sesh_args, **sesh_ap;
- int i, rc, sesh_nargs, ret = 1;
+ int i, rc, error, sesh_nargs, ret = 1;
+ int tfd = -1;
struct timespec ts;
struct stat sb;
debug_decl(selinux_edit_copy_tfiles, SUDO_DEBUG_EDIT)
@@ -901,33 +920,43 @@ selinux_edit_copy_tfiles(struct command_
/* Construct args for sesh -e 1 */
for (i = 0; i < nfiles; i++) {
- if (stat(tf[i].tfile, &sb) == 0) {
- mtim_get(&sb, ts);
- if (tf[i].osize == sb.st_size && sudo_timespeccmp(&tf[i].omtim, &ts, ==)) {
- /*
- * If mtime and size match but the user spent no measurable
- * time in the editor we can't tell if the file was changed.
- */
- if (sudo_timespeccmp(&times[0], &times[1], !=)) {
- sudo_warnx(U_("%s unchanged"), tf[i].ofile);
- unlink(tf[i].tfile);
- continue;
- }
+ if (tfd != -1)
+ close(tfd);
+ if ((tfd = open(tf[i].tfile, O_RDONLY|O_NONBLOCK|O_NOFOLLOW)) == -1) {
+ sudo_warn(U_("unable to open %s"), tf[i].tfile);
+ continue;
+ }
+ if (!sudo_check_temp_file(tfd, tf[i].tfile, user_details.uid, &sb))
+ continue;
+ mtim_get(&sb, ts);
+ if (tf[i].osize == sb.st_size && sudo_timespeccmp(&tf[i].omtim, &ts, ==)) {
+ /*
+ * If mtime and size match but the user spent no measurable
+ * time in the editor we can't tell if the file was changed.
+ */
+ if (sudo_timespeccmp(&times[0], &times[1], !=)) {
+ sudo_warnx(U_("%s unchanged"), tf[i].ofile);
+ unlink(tf[i].tfile);
+ continue;
}
}
*sesh_ap++ = tf[i].tfile;
*sesh_ap++ = tf[i].ofile;
- if (chown(tf[i].tfile, command_details->uid, command_details->gid) != 0) {
+ if (fchown(tfd, command_details->uid, command_details->gid) != 0) {
sudo_warn("unable to chown(%s) back to %d:%d", tf[i].tfile,
command_details->uid, command_details->gid);
}
}
*sesh_ap = NULL;
+ if (tfd != -1)
+ close(tfd);
if (sesh_ap - sesh_args > 3) {
/* Run sesh -e 1 <t1> <o1> ... <tn> <on> */
- rc = selinux_run_helper(sesh_args, command_details->envp);
- switch (rc) {
+ error = selinux_run_helper(command_details->uid, command_details->gid,
+ command_details->ngroups, command_details->groups, sesh_args,
+ command_details->envp);
+ switch (error) {
case SESH_SUCCESS:
ret = 0;
break;
@@ -941,7 +970,7 @@ selinux_edit_copy_tfiles(struct command_
sudo_warnx(U_("sesh: killed by a signal"));
break;
default:
- sudo_warnx(U_("sesh: unknown error %d"), rc);
+ sudo_warnx(U_("sesh: unknown error %d"), error);
break;
}
if (ret != 0)
@@ -963,7 +992,7 @@ sudo_edit(struct command_details *comman
{
struct command_details saved_command_details;
char **nargv = NULL, **ap, **files = NULL;
- int errors, i, ac, nargc, rc;
+ int errors, i, ac, nargc, ret;
int editor_argc = 0, nfiles = 0;
struct timespec times[2];
struct tempfile *tf = NULL;
@@ -1058,7 +1087,7 @@ sudo_edit(struct command_details *comman
command_details->ngroups = user_details.ngroups;
command_details->groups = user_details.groups;
command_details->argv = nargv;
- rc = run_command(command_details);
+ ret = run_command(command_details);
if (sudo_gettime_real(&times[1]) == -1) {
sudo_warn(U_("unable to read the clock"));
goto cleanup;
@@ -1080,14 +1109,16 @@ sudo_edit(struct command_details *comman
else
#endif
errors = sudo_edit_copy_tfiles(command_details, tf, nfiles, times);
- if (errors)
- goto cleanup;
+ if (errors) {
+ /* Preserve the edited temporary files. */
+ ret = W_EXITCODE(1, 0);
+ }
for (i = 0; i < nfiles; i++)
free(tf[i].tfile);
free(tf);
free(nargv);
- debug_return_int(rc);
+ debug_return_int(ret);
cleanup:
/* Clean up temp files and return. */
diff -up ./src/sudo_exec.h.symbolic-link-attack-4 ./src/sudo_exec.h
--- ./src/sudo_exec.h.symbolic-link-attack-4 2021-02-02 16:35:18.452036860 +0100
+++ ./src/sudo_exec.h 2021-02-02 16:35:18.454036833 +0100
@@ -1,7 +1,7 @@
/*
* SPDX-License-Identifier: ISC
*
- * Copyright (c) 2010-2016 Todd C. Miller <Todd.Miller@sudo.ws>
+ * Copyright (c) 2010-2017, 2020-2021 Todd C. Miller <Todd.Miller@sudo.ws>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -84,9 +84,11 @@
*/
struct command_details;
struct command_status;
+struct stat;
/* copy_file.c */
int sudo_copy_file(const char *src, int src_fd, off_t src_len, const char *dst, int dst_fd, off_t dst_len);
+bool sudo_check_temp_file(int tfd, const char *tname, uid_t uid, struct stat *sb);
/* exec.c */
void exec_cmnd(struct command_details *details, int errfd);

View File

@ -0,0 +1,47 @@
diff -up ./src/copy_file.c.symbolic-link-attack-5 ./src/copy_file.c
--- ./src/copy_file.c.symbolic-link-attack-5 2021-02-02 17:18:05.355567274 +0100
+++ ./src/copy_file.c 2021-02-02 17:19:09.904671563 +0100
@@ -128,7 +128,6 @@ write_error:
}
}
-#ifdef HAVE_SELINUX
bool
sudo_check_temp_file(int tfd, const char *tfile, uid_t uid, struct stat *sb)
{
@@ -158,4 +157,3 @@ sudo_check_temp_file(int tfd, const char
}
debug_return_bool(true);
}
-#endif /* SELINUX */
diff -up ./src/sudo_edit.c.symbolic-link-attack-5 ./src/sudo_edit.c
--- ./src/sudo_edit.c.symbolic-link-attack-5 2021-02-02 17:18:05.355567274 +0100
+++ ./src/sudo_edit.c 2021-02-02 17:18:05.356567260 +0100
@@ -692,24 +692,17 @@ sudo_edit_copy_tfiles(struct command_det
/* Copy contents of temp files to real ones. */
for (i = 0; i < nfiles; i++) {
- int rc = -1;
sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO,
"seteuid(%u)", (unsigned int)user_details.uid);
if (seteuid(user_details.uid) != 0)
sudo_fatal("seteuid(%u)", (unsigned int)user_details.uid);
tfd = sudo_edit_open(tf[i].tfile, O_RDONLY,
S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH, NULL);
- if (tfd != -1)
- rc = fstat(tfd, &sb);
- sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO,
- "seteuid(%u)", ROOT_UID);
if (seteuid(ROOT_UID) != 0)
sudo_fatal("seteuid(ROOT_UID)");
- if (rc == -1 || !S_ISREG(sb.st_mode)) {
- if (rc == -1)
- sudo_warn("%s", tf[i].tfile);
- else
- sudo_warnx(U_("%s: not a regular file"), tf[i].tfile);
+ sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO,
+ "seteuid(%u)", ROOT_UID);
+ if (tfd == -1 || !sudo_check_temp_file(tfd, tf[i].tfile, user_details.uid, &sb)) {
sudo_warnx(U_("%s left unmodified"), tf[i].ofile);
if (tfd != -1)
close(tfd);

View File

@ -0,0 +1,11 @@
diff -up ./src/sudo_edit.c.rest ./src/sudo_edit.c
--- ./src/sudo_edit.c.rest 2021-11-21 18:41:09.420657680 +0100
+++ ./src/sudo_edit.c 2021-11-21 18:42:23.214272777 +0100
@@ -878,6 +878,7 @@ selinux_edit_create_tfiles(struct comman
}
close(tfd);
}
+ ret = nfiles;
done:
/* Contents of tf will be freed by caller. */

View File

@ -0,0 +1,54 @@
diff -up ./plugins/sudoers/auth/pam.c.krb5ccname ./plugins/sudoers/auth/pam.c
--- ./plugins/sudoers/auth/pam.c.krb5ccname 2019-10-28 13:27:38.000000000 +0100
+++ ./plugins/sudoers/auth/pam.c 2021-12-06 11:14:15.580226222 +0100
@@ -119,10 +119,10 @@ conv_filter_init(void)
/*
* Messages from PAM account management when trusted mode is enabled:
- * 1 Last successful login for %s: %s
- * 2 Last successful login for %s: %s on %s
- * 3 Last unsuccessful login for %s: %s
- * 4 Last unsuccessful login for %s: %s on %s
+ * 1 Last successful login for %s: %s
+ * 2 Last successful login for %s: %s on %s
+ * 3 Last unsuccessful login for %s: %s
+ * 4 Last unsuccessful login for %s: %s on %s
*/
if ((catd = catopen("pam_comsec", NL_CAT_LOCALE)) != -1) {
maxfilters += 4;
@@ -290,6 +290,7 @@ sudo_pam_init_quiet(struct passwd *pw, s
int
sudo_pam_verify(struct passwd *pw, char *prompt, sudo_auth *auth, struct sudo_conv_callback *callback)
{
+ const char *envccname;
const char *s;
int *pam_status = (int *) auth->data;
debug_decl(sudo_pam_verify, SUDOERS_DEBUG_AUTH)
@@ -298,8 +299,27 @@ sudo_pam_verify(struct passwd *pw, char
getpass_error = false; /* set by converse if user presses ^C */
conv_callback = callback; /* passed to conversation function */
+ /* Set KRB5CCNAME from the user environment if not set to propagate this
+ * information to PAM modules that may use it to authentication. */
+ envccname = sudo_getenv("KRB5CCNAME");
+ if (envccname == NULL && user_ccname != NULL) {
+ if (sudo_setenv("KRB5CCNAME", user_ccname, true) != 0) {
+ sudo_debug_printf(SUDO_DEBUG_WARN|SUDO_DEBUG_LINENO,
+ "unable to set KRB5CCNAME");
+ debug_return_int(AUTH_FAILURE);
+ }
+ }
+
/* PAM_SILENT prevents the authentication service from generating output. */
*pam_status = pam_authenticate(pamh, PAM_SILENT);
+
+ /* Restore KRB5CCNAME to its original value. */
+ if (envccname == NULL && sudo_unsetenv("KRB5CCNAME") != 0) {
+ sudo_debug_printf(SUDO_DEBUG_WARN|SUDO_DEBUG_LINENO,
+ "unable to restore KRB5CCNAME");
+ debug_return_int(AUTH_FAILURE);
+ }
+
if (getpass_error) {
/* error or ^C from tgetpass() */
debug_return_int(AUTH_INTR);

35
sudo-1.9.7-sigchild.patch Normal file
View File

@ -0,0 +1,35 @@
From 727056e0c9519d8eecde801e950b35f2f69c72e2 Mon Sep 17 00:00:00 2001
From: "Todd C. Miller" <Todd.Miller@sudo.ws>
Date: Fri, 23 Apr 2021 07:41:27 -0600
Subject: [PATCH] Make sure SIGCHLD is not ignored when sudo is executed. If
SIGCHLD is ignored there is a race condition between when the process is
executed and when the SIGCHLD handler is installed. This fixes the bug
described by GitHub PR #98
---
src/signal.c | 12 ++++++++++++
1 file changed, 12 insertions(+)
diff --git a/src/signal.c b/src/signal.c
index 7f90d707b..866b64790 100644
--- a/src/signal.c
+++ b/src/signal.c
@@ -133,6 +133,18 @@ init_signals(void)
case SIGTTOU:
/* Don't install these until exec time. */
break;
+ case SIGCHLD:
+ /* Sudo needs to be able to catch SIGCHLD. */
+ if (ss->sa.sa_handler == SIG_IGN) {
+ sudo_debug_printf(SUDO_DEBUG_INFO,
+ "will restore signal %d on exec", SIGCHLD);
+ ss->restore = true;
+ }
+ if (sigaction(SIGCHLD, &sa, NULL) != 0) {
+ sudo_warn(U_("unable to set handler for signal %d"),
+ SIGCHLD);
+ }
+ break;
default:
if (ss->sa.sa_handler != SIG_IGN) {
if (sigaction(ss->signo, &sa, NULL) != 0) {

View File

@ -0,0 +1,22 @@
From 3fc3a07a03ef74fde99db40ce9ef43ccab336205 Mon Sep 17 00:00:00 2001
From: MertsA <andrewmerts@gmail.com>
Date: Fri, 23 Jul 2021 03:36:05 -0700
Subject: [PATCH] Rewind utmp file pointer after searching for entry
getutline() advances the file pointer until it matches or reaches EOF. pututline() starts from the current position in utmp. This rewinds the file pointer to the beginning to avoid allocating additional spurious utmp entries.
---
src/utmp.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/src/utmp.c b/src/utmp.c
index 544a37519..952bf3043 100644
--- a/src/utmp.c
+++ b/src/utmp.c
@@ -210,6 +210,7 @@ utmp_login(const char *from_line, const char *to_line, int ttyfd,
memset(&utbuf, 0, sizeof(utbuf));
strncpy(utbuf.ut_line, from_line, sizeof(utbuf.ut_line));
ut_old = sudo_getutline(&utbuf);
+ sudo_setutent();
}
utmp_fill(to_line, user, ut_old, &utbuf);
if (sudo_pututline(&utbuf) != NULL)

86
sudo-ldap.conf Normal file
View File

@ -0,0 +1,86 @@
## BINDDN DN
## The BINDDN parameter specifies the identity, in the form of a Dis
## tinguished Name (DN), to use when performing LDAP operations. If
## not specified, LDAP operations are performed with an anonymous
## identity. By default, most LDAP servers will allow anonymous
## access.
##
#binddn uid=sudo,cn=sysaccounts,cn=etc,dc=example,dc=com
## BINDPW secret
## The BINDPW parameter specifies the password to use when performing
## LDAP operations. This is typically used in conjunction with the
## BINDDN parameter.
##
#bindpw secret
## SSL start_tls
## If the SSL parameter is set to start_tls, the LDAP server connec
## tion is initiated normally and TLS encryption is begun before the
## bind credentials are sent. This has the advantage of not requiring
## a dedicated port for encrypted communications. This parameter is
## only supported by LDAP servers that honor the start_tls extension,
## such as the OpenLDAP and Tivoli Directory servers.
##
#ssl start_tls
## TLS_CACERTFILE file name
## The path to a certificate authority bundle which contains the cer
## tificates for all the Certificate Authorities the client knows to
## be valid, e.g. /etc/ssl/ca-bundle.pem. This option is only sup
## ported by the OpenLDAP libraries. Netscape-derived LDAP libraries
## use the same certificate database for CA and client certificates
## (see TLS_CERT).
##
#tls_cacertfile /path/to/CA.crt
## TLS_CHECKPEER on/true/yes/off/false/no
## If enabled, TLS_CHECKPEER will cause the LDAP server's TLS certifi
## cated to be verified. If the server's TLS certificate cannot be
## verified (usually because it is signed by an unknown certificate
## authority), sudo will be unable to connect to it. If TLS_CHECKPEER
## is disabled, no check is made. Note that disabling the check cre
## ates an opportunity for man-in-the-middle attacks since the
## server's identity will not be authenticated. If possible, the CA's
## certificate should be installed locally so it can be verified.
## This option is not supported by the Tivoli Directory Server LDAP
## libraries.
#tls_checkpeer yes
##
## URI ldap[s]://[hostname[:port]] ...
## Specifies a whitespace-delimited list of one or more
## URIs describing the LDAP server(s) to connect to.
##
#uri ldap://ldapserver
##
## SUDOERS_BASE base
## The base DN to use when performing sudo LDAP queries.
## Multiple SUDOERS_BASE lines may be specified, in which
## case they are queried in the order specified.
##
#sudoers_base ou=SUDOers,dc=example,dc=com
##
## BIND_TIMELIMIT seconds
## The BIND_TIMELIMIT parameter specifies the amount of
## time to wait while trying to connect to an LDAP server.
##
#bind_timelimit 30
##
## TIMELIMIT seconds
## The TIMELIMIT parameter specifies the amount of time
## to wait for a response to an LDAP query.
##
#timelimit 30
##
## SUDOERS_DEBUG debug_level
## This sets the debug level for sudo LDAP queries. Debugging
## information is printed to the standard error. A value of 1
## results in a moderate amount of debugging information.
## A value of 2 shows the results of the matches themselves.
##
#sudoers_debug 1

57
sudo.conf Normal file
View File

@ -0,0 +1,57 @@
#
# Default /etc/sudo.conf file
#
# Format:
# Plugin plugin_name plugin_path plugin_options ...
# Path askpass /path/to/askpass
# Path noexec /path/to/sudo_noexec.so
# Debug sudo /var/log/sudo_debug all@warn
# Set disable_coredump true
#
# Sudo plugins:
#
# The plugin_path is relative to ${prefix}/libexec unless fully qualified.
# The plugin_name corresponds to a global symbol in the plugin
# that contains the plugin interface structure.
# The plugin_options are optional.
#
# The sudoers plugin is used by default if no Plugin lines are present.
Plugin sudoers_policy sudoers.so
Plugin sudoers_io sudoers.so
#
# Sudo askpass:
#
# An askpass helper program may be specified to provide a graphical
# password prompt for "sudo -A" support. Sudo does not ship with its
# own passpass program but can use the OpenSSH askpass.
#
# Use the OpenSSH askpass
#Path askpass /usr/X11R6/bin/ssh-askpass
#
# Use the Gnome OpenSSH askpass
#Path askpass /usr/libexec/openssh/gnome-ssh-askpass
#
# Sudo noexec:
#
# Path to a shared library containing dummy versions of the execv(),
# execve() and fexecve() library functions that just return an error.
# This is used to implement the "noexec" functionality on systems that
# support C<LD_PRELOAD> or its equivalent.
# The compiled-in value is usually sufficient and should only be changed
# if you rename or move the sudo_noexec.so file.
#
#Path noexec /usr/libexec/sudo_noexec.so
#
# Core dumps:
#
# By default, sudo disables core dumps while it is executing (they
# are re-enabled for the command that is run).
# To aid in debugging sudo problems, you may wish to enable core
# dumps by setting "disable_coredump" to false.
#
# Set to false here so as not to interfere with /proc/sys/fs/suid_dumpable
#
Set disable_coredump false

1083
sudo.spec Normal file

File diff suppressed because it is too large Load Diff

120
sudoers Normal file
View File

@ -0,0 +1,120 @@
## Sudoers allows particular users to run various commands as
## the root user, without needing the root password.
##
## Examples are provided at the bottom of the file for collections
## of related commands, which can then be delegated out to particular
## users or groups.
##
## This file must be edited with the 'visudo' command.
## Host Aliases
## Groups of machines. You may prefer to use hostnames (perhaps using
## wildcards for entire domains) or IP addresses instead.
# Host_Alias FILESERVERS = fs1, fs2
# Host_Alias MAILSERVERS = smtp, smtp2
## User Aliases
## These aren't often necessary, as you can use regular groups
## (ie, from files, LDAP, NIS, etc) in this file - just use %groupname
## rather than USERALIAS
# User_Alias ADMINS = jsmith, mikem
## Command Aliases
## These are groups of related commands...
## Networking
# Cmnd_Alias NETWORKING = /sbin/route, /sbin/ifconfig, /bin/ping, /sbin/dhclient, /usr/bin/net, /sbin/iptables, /usr/bin/rfcomm, /usr/bin/wvdial, /sbin/iwconfig, /sbin/mii-tool
## Installation and management of software
# Cmnd_Alias SOFTWARE = /bin/rpm, /usr/bin/up2date, /usr/bin/yum
## Services
# Cmnd_Alias SERVICES = /sbin/service, /sbin/chkconfig, /usr/bin/systemctl start, /usr/bin/systemctl stop, /usr/bin/systemctl reload, /usr/bin/systemctl restart, /usr/bin/systemctl status, /usr/bin/systemctl enable, /usr/bin/systemctl disable
## Updating the locate database
# Cmnd_Alias LOCATE = /usr/bin/updatedb
## Storage
# Cmnd_Alias STORAGE = /sbin/fdisk, /sbin/sfdisk, /sbin/parted, /sbin/partprobe, /bin/mount, /bin/umount
## Delegating permissions
# Cmnd_Alias DELEGATING = /usr/sbin/visudo, /bin/chown, /bin/chmod, /bin/chgrp
## Processes
# Cmnd_Alias PROCESSES = /bin/nice, /bin/kill, /usr/bin/kill, /usr/bin/killall
## Drivers
# Cmnd_Alias DRIVERS = /sbin/modprobe
# Defaults specification
#
# Refuse to run if unable to disable echo on the tty.
#
Defaults !visiblepw
#
# Preserving HOME has security implications since many programs
# use it when searching for configuration files. Note that HOME
# is already set when the the env_reset option is enabled, so
# this option is only effective for configurations where either
# env_reset is disabled or HOME is present in the env_keep list.
#
Defaults always_set_home
Defaults match_group_by_gid
# Prior to version 1.8.15, groups listed in sudoers that were not
# found in the system group database were passed to the group
# plugin, if any. Starting with 1.8.15, only groups of the form
# %:group are resolved via the group plugin by default.
# We enable always_query_group_plugin to restore old behavior.
# Disable this option for new behavior.
Defaults always_query_group_plugin
Defaults env_reset
Defaults env_keep = "COLORS DISPLAY HOSTNAME HISTSIZE KDEDIR LS_COLORS"
Defaults env_keep += "MAIL PS1 PS2 QTDIR USERNAME LANG LC_ADDRESS LC_CTYPE"
Defaults env_keep += "LC_COLLATE LC_IDENTIFICATION LC_MEASUREMENT LC_MESSAGES"
Defaults env_keep += "LC_MONETARY LC_NAME LC_NUMERIC LC_PAPER LC_TELEPHONE"
Defaults env_keep += "LC_TIME LC_ALL LANGUAGE LINGUAS _XKB_CHARSET XAUTHORITY"
#
# Adding HOME to env_keep may enable a user to run unrestricted
# commands via sudo.
#
# Defaults env_keep += "HOME"
Defaults secure_path = /sbin:/bin:/usr/sbin:/usr/bin
## Next comes the main part: which users can run what software on
## which machines (the sudoers file can be shared between multiple
## systems).
## Syntax:
##
## user MACHINE=COMMANDS
##
## The COMMANDS section may have other options added to it.
##
## Allow root to run any commands anywhere
root ALL=(ALL) ALL
## Allows members of the 'sys' group to run networking, software,
## service management apps and more.
# %sys ALL = NETWORKING, SOFTWARE, SERVICES, STORAGE, DELEGATING, PROCESSES, LOCATE, DRIVERS
## Allows people in group wheel to run all commands
%wheel ALL=(ALL) ALL
## Same thing without a password
# %wheel ALL=(ALL) NOPASSWD: ALL
## Allows members of the users group to mount and unmount the
## cdrom as root
# %users ALL=/sbin/mount /mnt/cdrom, /sbin/umount /mnt/cdrom
## Allows members of the users group to shutdown this system
# %users localhost=/sbin/shutdown -h now
## Read drop-in files from /etc/sudoers.d (the # here does not mean a comment)
#includedir /etc/sudoers.d