CVE-2021-42341: Other (NVD-CWE-Other)
value: checkpath in OpenRC before 0.44.7 uses the direct output of strlen() to allocate strings, which does not account for the '\0' byte at the end of the string. This results in memory corruption. CVE-2021-42341 was introduced in git commit 63db2d99e730547339d1bdd28e8437999c380cae, which was introduced as part of OpenRC 0.44.0 development.
CWE description: NVD is only using a subset of CWE for mapping instead of the entire CWE, and the weakness type is not covered by that subset.
CVSS v3 base score: 7.5
Published: 2021-10-14T05:15Z
Repository: https://github.com/OpenRC/openrc
Language: C
Files changed: src/rc/checkpath.c
Commit date: 2021-04-13 17:13:25 -0500
Commit message: checkpath: remove extra slashes from paths

This fixes #418.

Diff: diff --git a/src/rc/checkpath.c b/src/rc/checkpath.c
index f8eb0e81..b2d1dd23 100644
--- a/src/rc/checkpath.c
+++ b/src/rc/checkpath.c
@@ -91,17 +91,17 @@ static int get_dirfd(char *path, bool symlinks)
 		eerrorx("%s: empty or relative path", applet);
 	dirfd = openat(dirfd, "/", O_RDONLY);
 	if (dirfd == -1)
 		eerrorx("%s: unable to open the root directory: %s",
 				applet, strerror(errno));
-	path_dupe = xstrdup(path);
-	ch = path_dupe;
+	ch = path;
 	while (*ch) {
 		if (*ch == '/')
 			components++;
 		ch++;
 	}
+	path_dupe = xstrdup(path);
 	item = strtok(path_dupe, "/");
 #ifdef O_PATH
 	flags |= O_PATH;
 #endif
 	if (!symlinks)
@@ -134,22 +134,48 @@ static int get_dirfd(char *path, bool symlinks)
 		} else {
 			close(dirfd);
 			dirfd = new_dirfd;
 			free(linkpath);
 			linkpath = NULL;
-			item = strtok(NULL, "/");
-			components--;
 		}
+		item = strtok(NULL, "/");
+		components--;
 	}
 	free(path_dupe);
-	if (linkpath) {
-		free(linkpath);
-		linkpath = NULL;
-	}
+	free(linkpath);
 	return dirfd;
 }
 
+static char *clean_path(char *path)
+{
+	char *ch;
+	char *ch2;
+	char *str;
+	str = xmalloc(strlen(path));
+	ch = path;
+	ch2 = str;
+	while (true) {
+		*ch2 = *ch;
+		ch++;
+		ch2++;
+		if (!*(ch-1))
+			break;
+		while (*(ch - 1) == '/' && *ch == '/')
+			ch++;
+	}
+	/* get rid of trailing / characters */
+	while ((ch = strrchr(str, '/'))) {
+		if (ch == str)
+			break;
+		if (!*(ch+1))
+			*ch = 0;
+		else
+			break;
+	}
+	return str;
+}
+
 static int do_check(char *path, uid_t uid, gid_t gid, mode_t mode,
 	inode_t type, bool trunc, bool chowner, bool symlinks, bool selinux_on)
 {
 	struct stat st;
 	char *name = NULL;
@@ -343,10 +369,11 @@ int main(int argc, char **argv)
 	bool trunc = false;
 	bool chowner = false;
 	bool symlinks = false;
 	bool writable = false;
 	bool selinux_on = false;
+	char *path = NULL;
 
 	applet = basename_c(argv[0]);
 	while ((opt = getopt_long(argc, argv, getoptstring,
 		    longopts, (int *) 0)) != -1)
 	{
@@ -405,16 +432,18 @@ int main(int argc, char **argv)
 
 	if (selinux_util_open() == 1)
 		selinux_on = true;
 
 	while (optind < argc) {
+		path = clean_path(argv[optind]);
 		if (writable)
-			exit(!is_writable(argv[optind]));
-		if (do_check(argv[optind], uid, gid, mode, type, trunc, chowner,
+			exit(!is_writable(path));
+		if (do_check(path, uid, gid, mode, type, trunc, chowner,
 					symlinks, selinux_on))
 			retval = EXIT_FAILURE;
 		optind++;
+		free(path);
 	}
 
 	if (selinux_on)
 		selinux_util_close();
 

Vulnerable code: 	path_dupe = xstrdup(path);
	ch = path_dupe;
			item = strtok(NULL, "/");
			components--;
	if (linkpath) {
		free(linkpath);
		linkpath = NULL;
	}
			exit(!is_writable(argv[optind]));
		if (do_check(argv[optind], uid, gid, mode, type, trunc, chowner,
Fixed code: 	ch = path;
	path_dupe = xstrdup(path);
		item = strtok(NULL, "/");
		components--;
	free(linkpath);
static char *clean_path(char *path)
{
	char *ch;
	char *ch2;
	char *str;
	str = xmalloc(strlen(path));
	ch = path;
	ch2 = str;
	while (true) {
		*ch2 = *ch;
		ch++;
		ch2++;
		if (!*(ch-1))
			break;
		while (*(ch - 1) == '/' && *ch == '/')
			ch++;
	}
	/* get rid of trailing / characters */
	while ((ch = strrchr(str, '/'))) {
		if (ch == str)
			break;
		if (!*(ch+1))
			*ch = 0;
		else
			break;
	}
	return str;
}

	char *path = NULL;
		path = clean_path(argv[optind]);
			exit(!is_writable(path));
		if (do_check(path, uid, gid, mode, type, trunc, chowner,
		free(path);