git/list[1] front-page[2] threads[3] people[4] search[5] about
 

Re: [PATCH 5/5] git-daemon support for user-relative paths.

From
Junio C Hamano <junkio@cox.net>
Date
Nov 18, 2005, 23:19 UTC
Message-ID
<7voe4hfssj.fsf@assigned-by-dhcp.cox.net>
In-Reply-To
<437DAA66.6070301@op5.se>
Andreas Ericsson <ae@op5.se> writes:
>> I think this list was added relatively recently as a usability
>> measure.  Maybe we would want an equivalent in enter_repo()?
>
> It's already there but in a different format.
I noticed that after I asked that question.  Thanks.
Show 7 quoted lines
>> Under strict-path, I think not doing any DWIM like this is fine,
>> but otherwise I suspect changing this would break existing
>> remotes/origin file people may have.  In addition enter_repo()
>> as posted does its own DWIM to chdir to ".git" unconditionally
>> as I pointed out...
>
> DWIM? That's an acronym I don't know.

I think you got HPA's message about it and its security implications.

>> Needs a bit more thought, but I think otherwise the basic idea
>> is right.
>
> Anything I should change before "take four" ?

I think it might make sense to inserting something like the attached untested patch in your series, between library and upload-pack. The validation done by path_ok() in git-daemon probabaly needs to lose alternate checks and validate only the path returned by enter_repo(). This would make writing whitelist by git-daemon administrator a bit more cumbersome, but I suspect at the same time would make auditing easier. So the series would become:

[1/6] Library code for user-relative paths, take three. [2/6] Do not DWIM in userpath library under strict mode. [3/6] Server-side support for user-relative paths. [4/6] Client side support for user-relative paths. [5/6] Documentation update for user-relative paths. [6/6] git-daemon support for user-relative paths.

-- >8 -- cut here -- >8 --
Subject: [PATCH] Do not DWIM in userpath library under strict mode.

This should force git-daemon administrator's job a bit harder because the exact paths need to be given in the whitelist, but at the same time makes the auditing easier.

This moves validate_symref() from refs.c to path.c, because we need to link git-daemon with path.c for its "enter_repo()", but we do not want to link the daemon with the rest of git libraries and its requirements.

Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 path.c |   77 ++++++++++++++++++++++++++++++++++++++++++++++++++--------------
 refs.c |   40 ---------------------------------
 2 files changed, 60 insertions(+), 57 deletions(-)

applies-to: 7223b28ede4511977d1e71f45fb2867780027235 bd8770050ee2c8d7aa5b2c3f138bc65d83a974e8

diff --git a/path.c b/path.c
index 5b61709..d635470 100644
--- a/path.c
+++ b/path.c
@@ -91,20 +91,55 @@ char *safe_strncpy(char *dest, const cha
 	return dest;
 }
 
+int validate_symref(const char *path)
+{
+	struct stat st;
+	char *buf, buffer[256];
+	int len, fd;
+
+	if (lstat(path, &st) < 0)
+		return -1;
+
+	/* Make sure it is a "refs/.." symlink */
+	if (S_ISLNK(st.st_mode)) {
+		len = readlink(path, buffer, sizeof(buffer)-1);
+		if (len >= 5 && !memcmp("refs/", buffer, 5))
+			return 0;
+		return -1;
+	}
+
+	/*
+	 * Anything else, just open it and try to see if it is a symbolic ref.
+	 */
+	fd = open(path, O_RDONLY);
+	if (fd < 0)
+		return -1;
+	len = read(fd, buffer, sizeof(buffer)-1);
+	close(fd);
+
+	/*
+	 * Is it a symbolic ref?
+	 */
+	if (len < 4 || memcmp("ref:", buffer, 4))
+		return -1;
+	buf = buffer + 4;
+	len -= 4;
+	while (len && isspace(*buf))
+		buf++, len--;
+	if (len >= 5 && !memcmp("refs/", buf, 5))
+		return 0;
+	return -1;
+}
+
 static char *current_dir()
 {
 	return getcwd(pathname, sizeof(pathname));
 }
 
-/* Take a raw path from is_git_repo() and canonicalize it using Linus'
- * idea of a blind chdir() and getcwd(). */
-static const char *canonical_path(char *path, int strict)
+static int user_chdir(char *path)
 {
 	char *dir = path;
 
-	if(strict && *dir != '/')
-		return NULL;
-
 	if(*dir == '~') {		/* user-relative path */
 		struct passwd *pw;
 		char *slash = strchr(dir, '/');
@@ -125,19 +160,19 @@ static const char *canonical_path(char *
 
 		/* make sure we got something back that we can chdir() to */
 		if(!pw || chdir(pw->pw_dir) < 0)
-			return NULL;
+			return -1;
 
 		if(!slash || !slash[1]) /* no path following username */
-			return current_dir();
+			return 0;
 
 		dir = slash + 1;
 	}
 
 	/* ~foo/path/to/repo is now path/to/repo and we're in foo's homedir */
 	if(chdir(dir) < 0)
-		return NULL;
+		return -1;
 
-	return current_dir();
+	return 0;
 }
 
 char *enter_repo(char *path, int strict)
@@ -145,16 +180,24 @@ char *enter_repo(char *path, int strict)
 	if(!path)
 		return NULL;
 
-	if(!canonical_path(path, strict)) {
-		if(strict || !canonical_path(mkpath("%s.git", path), strict))
+	if (strict) {
+		if((path[0] != '/') || chdir(path) < 0)
 			return NULL;
 	}
+	else {
+		if (!*path)
+			; /* happy -- no chdir */
+		else if (!user_chdir(path))
+			; /* happy -- as given */
+		else if (!user_chdir(mkpath("%s.git", path)))
+			; /* happy -- uemacs --> uemacs.git */
+		else
+			return NULL;
+		(void)chdir(".git");
+	}
 
-	/* This is perfectly safe, and people tend to think of the directory
-	 * where they ran git-init-db as their repository, so humour them. */
-	(void)chdir(".git");
-
-	if(access("objects", X_OK) == 0 && access("refs", X_OK) == 0) {
+	if(access("objects", X_OK) == 0 && access("refs", X_OK) == 0 &&
+	   validate_symref("HEAD") == 0) {
 		putenv("GIT_DIR=.");
 		return current_dir();
 	}
diff --git a/refs.c b/refs.c
index f324be5..ac26198 100644
--- a/refs.c
+++ b/refs.c
@@ -10,46 +10,6 @@
 #define USE_SYMLINK_HEAD 1
 #endif
 
-int validate_symref(const char *path)
-{
-	struct stat st;
-	char *buf, buffer[256];
-	int len, fd;
-
-	if (lstat(path, &st) < 0)
-		return -1;
-
-	/* Make sure it is a "refs/.." symlink */
-	if (S_ISLNK(st.st_mode)) {
-		len = readlink(path, buffer, sizeof(buffer)-1);
-		if (len >= 5 && !memcmp("refs/", buffer, 5))
-			return 0;
-		return -1;
-	}
-
-	/*
-	 * Anything else, just open it and try to see if it is a symbolic ref.
-	 */
-	fd = open(path, O_RDONLY);
-	if (fd < 0)
-		return -1;
-	len = read(fd, buffer, sizeof(buffer)-1);
-	close(fd);
-
-	/*
-	 * Is it a symbolic ref?
-	 */
-	if (len < 4 || memcmp("ref:", buffer, 4))
-		return -1;
-	buf = buffer + 4;
-	len -= 4;
-	while (len && isspace(*buf))
-		buf++, len--;
-	if (len >= 5 && !memcmp("refs/", buf, 5))
-		return 0;
-	return -1;
-}
-
 const char *resolve_ref(const char *path, unsigned char *sha1, int reading)
 {
 	int depth = MAXDEPTH, len;
---
0.99.9.GIT
Previous: Junio C HamanoNext: Andreas Ericsson
Message 7 of 12 in “git-daemon support for user-relative paths.”
  1. 5/5 git-daemon support for user-relative paths.Andreas Ericsson, Nov 17, 2005
  2. Junio C HamanoNov 18, 2005
  3. Andreas EricssonNov 18, 2005
  4. Matthias UrlichsNov 18, 2005
  5. H. Peter AnvinNov 18, 2005
  6. Junio C HamanoNov 18, 2005
  7. Junio C HamanoNov 18, 2005
  8. Andreas EricssonNov 18, 2005
  9. Junio C HamanoNov 21, 2005
  10. Junio C HamanoNov 21, 2005
  11. Andreas EricssonNov 21, 2005
  12. Junio C HamanoNov 21, 2005

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.