login
Header Space

 
 

Re: [RFH/PATCH] prefix_path(): disallow absolute paths

Score:
Previous message: [thread] [date] [author]
Next message: [thread] [date] [author]
To: Johannes Schindelin <Johannes.Schindelin@...>
Cc: Johannes Sixt <j.sixt@...>, Shawn Bohrer <shawn.bohrer@...>, <git@...>
Date: Monday, January 28, 2008 - 9:23 pm

Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:


If we are touching the prefix_path(), I think we should try to
make its "ambiguous path rejection" more complete.

Currently, we:

 - Remove "." path component (i.e. the directory leading part
   specified) from the input;

 - Remove ".." path component and strip one level of the prefix;

only from the beginning.  So if you give nonsense pathspec from
the command line, you can end up calling prefix_path() with things
like "/README", "/absolute/path/to//repository/tracked/file", and
"fo//o/../o".

And not passing such ambiguous path like "fo//o" to the core
level but sanitizing matters.  Then core level can always do
memcmp() with "fo/o" to see they are talking about the same
path.

I suspect that the right approach might be something like the
attached patch.  It introduces a version of prefix_path() that
sanitizes path (but not prefix part, which comes from git itself
and hopefully there should not be a need to sanitize it) while
doing the prefixing.  It also strips the leading absolute path
to the repository by comparing it with the value of work_tree.

A few things to note.

 * Your mv fix is rolled in.

 * This allows you to name a in-repository file as `pwd`/file,
   or `pwd`//file (iow, double-slash is also sanitized).  It may
   kill the bird in another thread nearby.

 * get_pathspec() drops paths outside of repository, so the
   caller may end up getting a smaller number of paths than it
   originally gave it.  If an existing caller expects the same
   number of paths to come back, it needs to be adjusted (I did
   not check).  We could alternatively die() but I couldn't
   decide which one is a better behaviour.

This is not to be applied (especially before auditing the
callers), but to be thought about.  Although it passes all the
tests...



 builtin-mv.c |    4 +-
 setup.c      |  152 ++++++++++++++++++++++++++++++++++++++++++----------------
 2 files changed, 112 insertions(+), 44 deletions(-)

diff --git a/builtin-mv.c b/builtin-mv.c
index 990e213..94f6dd2 100644
--- a/builtin-mv.c
+++ b/builtin-mv.c
@@ -164,7 +164,7 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
 				}
 
 				dst = add_slash(dst);
-				dst_len = strlen(dst) - 1;
+				dst_len = strlen(dst);
 
 				for (j = 0; j < last - first; j++) {
 					const char *path =
@@ -172,7 +172,7 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
 					source[argc + j] = path;
 					destination[argc + j] =
 						prefix_path(dst, dst_len,
-							path + length);
+							path + length + 1);
 					modes[argc + j] = INDEX;
 				}
 				argc += last - first;
diff --git a/setup.c b/setup.c
index adede16..fdc6459 100644
--- a/setup.c
+++ b/setup.c
@@ -4,51 +4,114 @@
 static int inside_git_dir = -1;
 static int inside_work_tree = -1;
 
-const char *prefix_path(const char *prefix, int len, const char *path)
+static int sanitary_path_copy(char *dst, const char *src)
 {
-	const char *orig = path;
+	char *dst0 = dst;
+
+	if (*src == '/') {
+		*dst++ = '/';
+		while (*src == '/')
+			src++;
+	}
+
 	for (;;) {
-		char c;
-		if (*path != '.')
-			break;
-		c = path[1];
-		/* "." */
-		if (!c) {
-			path++;
-			break;
+		char c = *src;
+
+		/*
+		 * A path component that begins with . could be
+		 * special:
+		 * (1) "." and ends   -- ignore and terminate.
+		 * (2) "./"           -- ignore them, eat slash and continue.
+		 * (3) ".." and ends  -- strip one and terminate.
+		 * (4) "../"          -- strip one, eat slash and continue.
+		 */
+		if (c == '.') {
+			switch (src[1]) {
+			case '\0':
+				/* (1) */
+				src++;
+				break;
+			case '/':
+				/* (2) */
+				src += 2;
+				while (*src == '/')
+					src++;
+				continue;
+			case '.':
+				switch (src[2]) {
+				case '\0':
+					/* (3) */
+					src += 2;
+					goto up_one;
+				case '/':
+					/* (4) */
+					src += 3;
+					while (*src == '/')
+						src++;
+					goto up_one;
+				}
+			}
 		}
-		/* "./" */
+
+		/* copy up to the next '/', and eat all '/' */
+		while ((c = *src++) != '\0' && c != '/')
+			*dst++ = c;
 		if (c == '/') {
-			path += 2;
-			continue;
-		}
-		if (c != '.')
+			*dst++ = c;
+			while (c == '/')
+				c = *src++;
+			src--;
+		} else if (!c)
 			break;
-		c = path[2];
-		if (!c)
-			path += 2;
-		else if (c == '/')
-			path += 3;
-		else
-			break;
-		/* ".." and "../" */
-		/* Remove last component of the prefix */
-		do {
-			if (!len)
-				die("'%s' is outside repository", orig);
-			len--;
-		} while (len && prefix[len-1] != '/');
 		continue;
+
+	up_one:
+		/*
+		 * dst0..dst is prefix portion, and dst[-1] is '/';
+		 * go up one level.
+		 */
+		dst -= 2; /* go past trailing '/' if any */
+		if (dst < dst0)
+			return -1;
+		while (1) {
+			if (dst <= dst0)
+				break;
+			c = *dst--;
+			if (c == '/') {
+				dst += 2;
+				break;
+			}
+		}
 	}
-	if (len) {
-		int speclen = strlen(path);
-		char *n = xmalloc(speclen + len + 1);
+	*dst = '\0';
+	return 0;
+}
 
-		memcpy(n, prefix, len);
-		memcpy(n + len, path, speclen+1);
-		path = n;
+const char *prefix_path(const char *prefix, int len, const char *path)
+{
+	const char *orig = path;
+	char *sanitized = xmalloc(len + strlen(path) + 1);
+	if (*orig == '/')
+		strcpy(sanitized, path);
+	else {
+		if (len)
+			memcpy(sanitized, prefix, len);
+		strcpy(sanitized + len, path);		
 	}
-	return path;
+	if (sanitary_path_copy(sanitized, sanitized))
+		goto error_out;
+	if (*orig == '/') {
+		const char *work_tree = get_git_work_tree();
+		size_t len = strlen(work_tree);
+		if (strncmp(sanitized, work_tree, len) ||
+		    (sanitized[len] != '\0' && sanitized[len] != '/')) {
+		error_out:
+			error("'%s' is outside repository", orig);
+			free(sanitized);
+			return NULL;
+		}
+	}
+	return sanitized;
 }
 
 /*
@@ -114,7 +177,7 @@ void verify_non_filename(const char *prefix, const char *arg)
 const char **get_pathspec(const char *prefix, const char **pathspec)
 {
 	const char *entry = *pathspec;
-	const char **p;
+	const char **src, **dst;
 	int prefixlen;
 
 	if (!prefix && !entry)
@@ -128,12 +191,17 @@ const char **get_pathspec(const char *prefix, const char **pathspec)
 	}
 
 	/* Otherwise we have to re-write the entries.. */
-	p = pathspec;
+	src = pathspec;
+	dst = pathspec;
 	prefixlen = prefix ? strlen(prefix) : 0;
-	do {
-		*p = prefix_path(prefix, prefixlen, entry);
-	} while ((entry = *++p) != NULL);
-	return (const char **) pathspec;
+	while (*src) {
+		const char *p = prefix_path(prefix, prefixlen, *src);
+		if (p)
+			*(dst++) = p;
+		src++;
+	}
+	*dst = NULL;
+	return pathspec;
 }
 
 /*
-
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Previous message: [thread] [date] [author]
Next message: [thread] [date] [author]

Messages in current thread:
git-clean buglet, Johannes Sixt, (Wed Jan 23, 11:14 am)
Re: git-clean buglet, Johannes Schindelin, (Wed Jan 23, 11:29 am)
Re: git-clean buglet, Johannes Sixt, (Wed Jan 23, 11:40 am)
[PATCH] Fix off by one error in prep_exclude., Shawn Bohrer, (Sun Jan 27, 3:55 pm)
Re: [PATCH] Fix off by one error in prep_exclude., Johannes Schindelin, (Sun Jan 27, 4:44 pm)
Re: [PATCH] Fix off by one error in prep_exclude., Junio C Hamano, (Sun Jan 27, 6:34 pm)
Re: [PATCH] Fix off by one error in prep_exclude., Shawn Bohrer, (Sun Jan 27, 8:34 pm)
Re: [PATCH] Fix off by one error in prep_exclude., Junio C Hamano, (Sun Jan 27, 10:52 pm)
Re: [PATCH] Fix off by one error in prep_exclude., Johannes Sixt, (Mon Jan 28, 3:12 am)
Re: [PATCH] Fix off by one error in prep_exclude., Junio C Hamano, (Mon Jan 28, 4:46 am)
Re: [PATCH] Fix off by one error in prep_exclude., Johannes Sixt, (Mon Jan 28, 5:05 am)
[RFH/PATCH] prefix_path(): disallow absolute paths, Johannes Schindelin, (Mon Jan 28, 8:33 am)
Re: [RFH/PATCH] prefix_path(): disallow absolute paths, Junio C Hamano, (Mon Jan 28, 9:23 pm)
Re: [RFH/PATCH] prefix_path(): disallow absolute paths, しらいしななこ, (Tue Jan 29, 5:53 pm)
Re: [RFH/PATCH] prefix_path(): disallow absolute paths, Junio C Hamano, (Tue Jan 29, 8:43 pm)
Re: [RFH/PATCH] prefix_path(): disallow absolute paths, Johannes Sixt, (Tue Jan 29, 3:20 am)
Re: [RFH/PATCH] prefix_path(): disallow absolute paths, Junio C Hamano, (Tue Jan 29, 3:28 am)
Re: [RFH/PATCH] prefix_path(): disallow absolute paths, Johannes Sixt, (Tue Jan 29, 3:43 am)
Re: [RFH/PATCH] prefix_path(): disallow absolute paths, Junio C Hamano, (Tue Jan 29, 4:31 am)
Re: [RFH/PATCH] prefix_path(): disallow absolute paths, Johannes Schindelin, (Mon Jan 28, 10:37 pm)
Re: [RFH/PATCH] prefix_path(): disallow absolute paths, Junio C Hamano, (Mon Jan 28, 10:45 pm)
Re: [RFH/PATCH] prefix_path(): disallow absolute paths, Johannes Schindelin, (Mon Jan 28, 10:59 pm)
Re: [RFH/PATCH] prefix_path(): disallow absolute paths, Junio C Hamano, (Mon Jan 28, 10:03 pm)
Re: [RFH/PATCH] prefix_path(): disallow absolute paths, Junio C Hamano, (Tue Jan 29, 3:02 am)
[PATCH] More test cases for sanitized path names, Robin Rosenberg, (Fri Feb 1, 12:34 am)
Re: [PATCH] More test cases for sanitized path names, Junio C Hamano, (Fri Feb 1, 3:17 am)
[PATCH for post 1.5.4] Sane use of test_expect_failure, Junio C Hamano, (Fri Feb 1, 5:50 am)
Re: [PATCH] Sane use of test_expect_failure, Junio C Hamano, (Sat Feb 2, 6:06 am)
Re: [PATCH] More test cases for sanitized path names, Robin Rosenberg, (Fri Feb 1, 5:10 am)
Re: [PATCH] More test cases for sanitized path names, Junio C Hamano, (Fri Feb 1, 6:22 am)
Re: [PATCH] More test cases for sanitized path names, Robin Rosenberg, (Fri Feb 1, 10:17 am)
Re: [PATCH] More test cases for sanitized path names, Junio C Hamano, (Fri Feb 1, 1:45 pm)
Re: [PATCH] More test cases for sanitized path names, Junio C Hamano, (Fri Feb 1, 6:51 am)
Re: [PATCH] More test cases for sanitized path names, Junio C Hamano, (Fri Feb 1, 7:10 am)
[PATCH] Make blame accept absolute paths, Robin Rosenberg, (Fri Feb 1, 12:07 am)
Re: [RFH/PATCH] prefix_path(): disallow absolute paths, Junio C Hamano, (Mon Jan 28, 10:03 pm)
[PATCH] prefix_path(): disallow absolute paths, Johannes Schindelin, (Mon Jan 28, 11:05 am)
Re: [PATCH] Fix off by one error in prep_exclude., Junio C Hamano, (Mon Jan 28, 5:22 am)
[PATCH] Fix off by one error in prep_exclude., Shawn Bohrer, (Sun Jan 27, 8:37 pm)
Re: [PATCH] Fix off by one error in prep_exclude., Johannes Schindelin, (Mon Jan 28, 7:59 am)
Re: [PATCH] Fix off by one error in prep_exclude., Junio C Hamano, (Mon Jan 28, 8:04 am)
Re: [PATCH] Fix off by one error in prep_exclude., Shawn Bohrer, (Sun Jan 27, 5:15 pm)
Re: git-clean buglet, Johannes Sixt, (Wed Jan 23, 11:24 am)
speck-geostationary