From 807e17ceedf2500aeca67b37f62463ee112d63c4 Mon Sep 17 00:00:00 2001
From: Todd C. Miller <Todd.Miller@sudo.ws>
Date: Mon, 09 Dec 2019 17:14:06 -0700
Subject: [PATCH] Fix CVE-2019-19234

Add a new flag "allow_unknown_runas_id" to control matching of unknown IDs.
Previous, sudo would always allow unknown user or group IDs if the
sudoers entry permitted it.  This included the "ALL" alias.
With this change, the admin must explicitly enable support for unknown IDs.

---
 doc/sudoers.man.in          | 17 +++++++++++++++++
 doc/sudoers.mdoc.in         | 16 ++++++++++++++++
 plugins/sudoers/def_data.c  |  4 ++++
 plugins/sudoers/def_data.h  |  2 ++
 plugins/sudoers/def_data.in |  3 +++
 plugins/sudoers/defaults.c  |  1 +
 plugins/sudoers/sudoers.c   | 28 ++++++++++++++++++++++++++--
 7 files changed, 69 insertions(+), 2 deletions(-)

diff --git a/doc/sudoers.man.in b/doc/sudoers.man.in
index e2470b5..45a600f 100644
--- a/doc/sudoers.man.in
+++ b/doc/sudoers.man.in
@@ -2869,6 +2869,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.30 or higher.
+Older versions of
+\fBsudo\fR
+always allowed matching of unknown user and group IDs.
+.TP 18n
 runaspw
 If set,
 \fBsudo\fR
diff --git a/doc/sudoers.mdoc.in b/doc/sudoers.mdoc.in
index 2053b5d..6a511d9 100644
--- a/doc/sudoers.mdoc.in
+++ b/doc/sudoers.mdoc.in
@@ -2698,6 +2698,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.30 or higher.
+Older versions of
+.Nm sudo
+always allowed matching of unknown user and group IDs.
 .It runaspw
 If set,
 .Nm sudo
diff --git a/plugins/sudoers/def_data.c b/plugins/sudoers/def_data.c
index 07e3433..97eaf79 100644
--- a/plugins/sudoers/def_data.c
+++ b/plugins/sudoers/def_data.c
@@ -494,6 +494,10 @@ struct sudo_defs_types sudo_defs_table[] = {
 	N_("Ignore case when matching group names"),
 	NULL,
     }, {
+	"runas_allow_unknown_id", T_FLAG,
+	N_("Allow the use of unknown runas user and/or group ID"),
+	NULL,
+    }, {
 	NULL, 0, NULL
     }
 };
diff --git a/plugins/sudoers/def_data.h b/plugins/sudoers/def_data.h
index 65f10c3..55878c9 100644
--- a/plugins/sudoers/def_data.h
+++ b/plugins/sudoers/def_data.h
@@ -226,6 +226,8 @@
 #define def_case_insensitive_user (sudo_defs_table[I_CASE_INSENSITIVE_USER].sd_un.flag)
 #define I_CASE_INSENSITIVE_GROUP 113
 #define def_case_insensitive_group (sudo_defs_table[I_CASE_INSENSITIVE_GROUP].sd_un.flag)
+#define I_RUNAS_ALLOW_UNKNOWN_ID 114
+#define def_runas_allow_unknown_id (sudo_defs_table[I_RUNAS_ALLOW_UNKNOWN_ID].sd_un.flag)
 
 enum def_tuple {
 	never,
diff --git a/plugins/sudoers/def_data.in b/plugins/sudoers/def_data.in
index 99d4360..cf0eead 100644
--- a/plugins/sudoers/def_data.in
+++ b/plugins/sudoers/def_data.in
@@ -357,3 +357,6 @@ case_insensitive_user
 case_insensitive_group
 	T_FLAG
 	"Ignore case when matching group names"
+runas_allow_unknown_id
+	T_FLAG
+	"Allow the use of unknown runas user and/or group ID"
diff --git a/plugins/sudoers/defaults.c b/plugins/sudoers/defaults.c
index 4c8c262..d5b1d9c 100644
--- a/plugins/sudoers/defaults.c
+++ b/plugins/sudoers/defaults.c
@@ -574,6 +574,7 @@ init_defaults(void)
     def_sudoedit_checkdir = true;
     def_iolog_mode = S_IRUSR|S_IWUSR;
     def_fdexec = digest_only;
+    def_runas_allow_unknown_id = false;
 
     /* Syslog options need special care since they both strings and ints */
 #if (LOGGING & SLOG_SYSLOG)
diff --git a/plugins/sudoers/sudoers.c b/plugins/sudoers/sudoers.c
index 1267949..b393d70 100644
--- a/plugins/sudoers/sudoers.c
+++ b/plugins/sudoers/sudoers.c
@@ -101,6 +101,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;
@@ -332,6 +334,22 @@ sudoers_policy_main(int argc, char * const argv[], int pwflag, char *env_add[],
 	}
     }
 
+    /* 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.
      */
@@ -1124,12 +1142,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, NULL, NULL, &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) {
@@ -1155,12 +1176,15 @@ set_runasgr(const char *group, bool quiet)
     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, NULL, NULL, &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) {
-- 
1.8.3.1

