CVE-2019-15901: Improper Privilege Management (CWE-269)
value: An issue was discovered in slicer69 doas before 6.2 on certain platforms other than OpenBSD. A setusercontext(3) call with flags to change the UID, primary GID, and secondary GIDs was replaced (on certain platforms: Linux and possibly NetBSD) with a single setuid(2) call. This resulted in neither changing the group id nor initializing secondary group ids.
CWE description: The product does not properly assign, modify, track, or check privileges for an actor, creating an unintended sphere of control for that actor.
CVSS v3 base score: 8.8
Published: 2019-10-18T16:15Z
Repository: https://github.com/slicer69/doas
Language: C
Files changed: doas.c
Commit date: 2019-09-03 02:45:20 +0200
Commit message: fix the setusercontext(3) workaround

Seeing this being used on even more system like Illumos with this ugly
and security critical bug open makes me cringe every time I check if it
was finally fixed.

I reported it directly to the maintainer in 2017. I reported it to
pkgsrc-security@netbsd.org without a response.

Diff: diff --git a/doas.c b/doas.c
index ca7a572..745b1c8 100644
--- a/doas.c
+++ b/doas.c
@@ -540,10 +540,27 @@ main(int argc, char **argv)
 #if defined(HAVE_LOGIN_CAP_H)
 	if (setusercontext(NULL, target_pw, target, LOGIN_SETGROUP |
 	    LOGIN_SETPRIORITY | LOGIN_SETRESOURCES | LOGIN_SETUMASK |
 	    LOGIN_SETUSER) != 0)
 		errx(1, "failed to set user context for target");
+#else
+	#if defined(__linux__) || defined(__FreeBSD__) || defined(__NetBSD__)
+	if (setresgid(target_pw->pw_gid, target_pw->pw_gid, target_pw->pw_gid) == -1)
+		err(1, "setresgid");
+	#else
+	if (setregid(target_pw->pw_gid, target_pw->pw_gid) == -1)
+		err(1, "setregid");
+	#endif
+	if (initgroups(target_pw->pw_name, target_pw->pw_gid) == -1)
+		err(1, "initgroups");
+	#if defined(__linux__) || defined(__FreeBSD__) || defined(__NetBSD__)
+	if (setresuid(target, target, target) == -1)
+		err(1, "setresuid");
+	#else
+	if (setreuid(target, target) == -1)
+		err(1, "setreuid");
+	#endif
 #endif
         /*
 	if (pledge("stdio rpath exec", NULL) == -1)
 		err(1, "pledge");
         */
@@ -555,18 +572,10 @@ main(int argc, char **argv)
 
 	/*
         if (pledge("stdio exec", NULL) == -1)
 		err(1, "pledge");
         */
-#ifndef HAVE_LOGIN_CAP_H
-        /* If we effectively are root, set the UID to actually be root to avoid
-           permission errors. */
-        if (target != 0)
-           setuid(target);
-        if ( geteuid() == ROOT_UID )
-           setuid(ROOT_UID);
-#endif
 
 	syslog(LOG_AUTHPRIV | LOG_INFO, "%s ran command %s as %s from %s",
 	    myname, cmdline, target_pw->pw_name, cwd);
 
 	envp = prepenv(rule, original_pw, target_pw);

Vulnerable code: #ifndef HAVE_LOGIN_CAP_H
        /* If we effectively are root, set the UID to actually be root to avoid
           permission errors. */
        if (target != 0)
           setuid(target);
        if ( geteuid() == ROOT_UID )
           setuid(ROOT_UID);
#endif
Fixed code: #else
	#if defined(__linux__) || defined(__FreeBSD__) || defined(__NetBSD__)
	if (setresgid(target_pw->pw_gid, target_pw->pw_gid, target_pw->pw_gid) == -1)
		err(1, "setresgid");
	#else
	if (setregid(target_pw->pw_gid, target_pw->pw_gid) == -1)
		err(1, "setregid");
	#endif
	if (initgroups(target_pw->pw_name, target_pw->pw_gid) == -1)
		err(1, "initgroups");
	#if defined(__linux__) || defined(__FreeBSD__) || defined(__NetBSD__)
	if (setresuid(target, target, target) == -1)
		err(1, "setresuid");
	#else
	if (setreuid(target, target) == -1)
		err(1, "setreuid");
	#endif