[PATCH 0/3] git-daemon hacking

DORMANTno replies

6 messages, 2 authors, 2016-06-15 · open the first message on its own page

[PATCH 0/3] git-daemon hacking

From: Mark Wooding <hidden>
Date: 2016-06-15 22:42:18

I've just been playing around a bit with the git-daemon.  My main
objective was to implement the feature in the third patch, namely to
allow users to publish their own repositories even though the daemon was
mainly locked down to serving from a particular central tree.  This is
the same idea as allowing a web server to serve files in a user's
~/public_html directory.

But I also fixed a couple of bugs I found on the way.

-- [mdw]

[PATCH 2/3] daemon: Set SO_REUSEADDR on listening sockets.

From: Mark Wooding <hidden>
Date: 2016-06-15 22:42:18

From: Mark Wooding <redacted>

Without this, you can silently lose the ability to receive IPv4
connections if you stop and restart the daemon.

Signed-off-by: Mark Wooding <redacted>
---

 daemon.c |   14 ++++++++++++++
 1 files changed, 14 insertions(+), 0 deletions(-)
diff --git a/daemon.c b/daemon.c
index 532bb0c..6b88c0c 100644
--- a/daemon.c
+++ b/daemon.c
@@ -454,6 +454,7 @@ static int socksetup(int port, int **soc
 	int socknum = 0, *socklist = NULL;
 	int maxfd = -1;
 	char pbuf[NI_MAXSERV];
+	int yes = 1;
 
 	struct addrinfo hints, *ai0, *ai;
 	int gai;
@@ -491,6 +492,12 @@ static int socksetup(int port, int **soc
 		}
 #endif
 
+		if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR,
+			       &yes, sizeof(yes))) {
+			close(sockfd);
+			return 0;	/* not fatal */
+		}
+
 		if (bind(sockfd, ai->ai_addr, ai->ai_addrlen) < 0) {
 			close(sockfd);
 			continue;	/* not fatal */
@@ -523,6 +530,7 @@ static int socksetup(int port, int **soc
 {
 	struct sockaddr_in sin;
 	int sockfd;
+	int yes = 1;
 
 	sockfd = socket(AF_INET, SOCK_STREAM, 0);
 	if (sockfd < 0)
@@ -533,6 +541,12 @@ static int socksetup(int port, int **soc
 	sin.sin_addr.s_addr = htonl(INADDR_ANY);
 	sin.sin_port = htons(port);
 
+	if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR,
+		       &yes, sizeof(yes))) {
+		close(sockfd);
+		return 0;
+	}
+
 	if ( bind(sockfd, (struct sockaddr *)&sin, sizeof sin) < 0 ) {
 		close(sockfd);
 		return 0;

[PATCH 3/3] daemon: Support a --user-path option.

From: Mark Wooding <hidden>
Date: 2016-06-15 22:42:18

From: Mark Wooding <redacted>

If we're invoked with --user-path=FOO option, then a URL of the form
git://~USER/PATH/... resolves to the path HOME/FOO/PATH/..., where HOME
is USER's home directory.  This is done instead of any transformation
due to --base-path, so you can use both at the same time.  This lets
users set up their own git repositories to be served by a central
daemon, without them all having to be in the same place, and without the
git-daemon being allowed to roam the entire filesystem freely, or
exposing details of filesystem layout on URLs.

Signed-off-by: Mark Wooding <redacted>
---

 Documentation/git-daemon.txt |   11 +++++++++--
 daemon.c                     |   36 +++++++++++++++++++++++++++++++++---
 2 files changed, 42 insertions(+), 5 deletions(-)
diff --git a/Documentation/git-daemon.txt b/Documentation/git-daemon.txt
index a20e053..2e48a10 100644
--- a/Documentation/git-daemon.txt
+++ b/Documentation/git-daemon.txt
@@ -10,7 +10,7 @@ SYNOPSIS
 [verse]
 'git-daemon' [--verbose] [--syslog] [--inetd | --port=n] [--export-all]
              [--timeout=n] [--init-timeout=n] [--strict-paths]
-             [--base-path=path] [directory...]
+             [--base-path=path] [--user-path=path] [directory...]
 
 DESCRIPTION
 -----------
@@ -43,7 +43,7 @@ OPTIONS
 	'--base-path=/srv/git' on example.com, then if you later try to pull
 	'git://example.com/hello.git', `git-daemon` will interpret the path
 	as '/srv/git/hello.git'. Home directories (the '~login' notation)
-	access is disabled.
+	access is disabled unless '--user-path' is also given.
 
 --export-all::
 	Allow pulling from all directories that look like GIT repositories
@@ -70,6 +70,13 @@ OPTIONS
 	Log to syslog instead of stderr. Note that this option does not imply
 	--verbose, thus by default only error conditions will be logged.
 
+--user-path::
+	Rewrite a request for "~user/something" to
+	"home/user-path/something".  Useful in conjunction with
+	'--base-path', if you want to restrict the daemon from roaming
+	the entire filesystem without preventing users from publishing
+	their own repositories.
+
 --verbose::
 	Log details about the incoming connections and requested files.
 
diff --git a/daemon.c b/daemon.c
index 6b88c0c..95b9c7e 100644
--- a/daemon.c
+++ b/daemon.c
@@ -6,6 +6,7 @@
 #include <netdb.h>
 #include <netinet/in.h>
 #include <arpa/inet.h>
+#include <pwd.h>
 #include <syslog.h>
 #include "pkt-line.h"
 #include "cache.h"
@@ -17,7 +18,7 @@ static int verbose;
 static const char daemon_usage[] =
 "git-daemon [--verbose] [--syslog] [--inetd | --port=n] [--export-all]\n"
 "           [--timeout=n] [--init-timeout=n] [--strict-paths]\n"
-"           [--base-path=path] [directory...]";
+"           [--base-path=path] [--user-path=path] [directory...]";
 
 /* List of acceptable pathname prefixes */
 static char **ok_paths = NULL;
@@ -28,6 +29,7 @@ static int export_all_trees = 0;
 
 /* Take all paths relative to this one if non-NULL */
 static char *base_path = NULL;
+static char *user_path = NULL;
 
 /* Timeout, and initial timeout */
 static unsigned int timeout = 0;
@@ -137,14 +139,34 @@ static int avoid_alias(char *p)
 static char *path_ok(char *dir)
 {
 	char *path;
+	static char rpath[PATH_MAX];
 
 	if (avoid_alias(dir)) {
 		logerror("'%s': aliased", dir);
 		return NULL;
 	}
 
-	if (base_path) {
-		static char rpath[PATH_MAX];
+	if (user_path && *dir == '~') {
+		struct passwd *pw;
+		char *u, *p;
+
+		u = dir + 1;
+		p = strchr(u, '/');
+		if (!p) {
+			logerror("'%s': Missing / after user name", dir);
+			return NULL;
+		}
+		*p = 0;
+		pw = getpwnam(u);
+		*p++ = '/';
+		if (!pw) {
+			logerror("'%s': User not found", u);
+			return NULL;
+		}
+		snprintf(rpath, PATH_MAX, "%s/%s/%s",
+			 pw->pw_dir, user_path, p);
+		dir = rpath;
+	} else if (base_path) {
 		if (*dir != '/') {
 			/* Forbid possible base-path evasion using ~paths. */
 			logerror("'%s': Non-absolute path denied (base-path active)", dir);
@@ -491,6 +513,10 @@ static int socksetup(int port, int **soc
 			/* Note: error is not fatal */
 		}
 #endif
+		if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes))) {
+			close(sockfd);
+			continue;	/* not fatal */
+		}
 
 		if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR,
 			       &yes, sizeof(yes))) {
@@ -673,6 +699,10 @@ int main(int argc, char **argv)
 			base_path = arg+12;
 			continue;
 		}
+		if (!strncmp(arg, "--user-path=", 12)) {
+			user_path = arg+12;
+			continue;
+		}
 		if (!strcmp(arg, "--")) {
 			ok_paths = &argv[i+1];
 			break;

[PATCH 1/3] daemon: Provide missing argument for logerror() call.

From: Mark Wooding <hidden>
Date: 2016-06-15 22:42:18

From: Mark Wooding <redacted>

Could cause a crash if --base-path set.  Unlikely to be a security the
concern: message doesn't go to the client, so we can't leak anything
(except by dumping core), and we've already forked, so it's not a denial
of service.

Signed-off-by: Mark Wooding <redacted>
---

 daemon.c |    2 +-
 1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/daemon.c b/daemon.c
index bb014fa..532bb0c 100644
--- a/daemon.c
+++ b/daemon.c
@@ -147,7 +147,7 @@ static char *path_ok(char *dir)
 		static char rpath[PATH_MAX];
 		if (*dir != '/') {
 			/* Forbid possible base-path evasion using ~paths. */
-			logerror("'%s': Non-absolute path denied (base-path active)");
+			logerror("'%s': Non-absolute path denied (base-path active)", dir);
 			return NULL;
 		}
 		snprintf(rpath, PATH_MAX, "%s%s", base_path, dir);

Re: [PATCH 2/3] daemon: Set SO_REUSEADDR on listening sockets.

From: Junio C Hamano <hidden>
Date: 2016-06-15 22:42:18

Mark Wooding [off-list ref] writes:
From: Mark Wooding <redacted>

Without this, you can silently lose the ability to receive IPv4
connections if you stop and restart the daemon.

Signed-off-by: Mark Wooding <redacted>
But with that, you expose yourself to the confusion TIME_WAIT
was designed to protect you from, so how about making it
optional like this?

Tested, of course ;-).

-- >8 --
From nobody Mon Sep 17 00:00:00 2001
From: Mark Wooding <redacted>
Date: Fri Feb 3 20:27:04 2006 +0000
Subject: [PATCH] daemon: Set SO_REUSEADDR on listening sockets.

Without this, you can silently lose the ability to receive IPv4
connections if you stop and restart the daemon.

[jc: tweaked code organization a bit and made this controllable
 from a command line option.]

Signed-off-by: Mark Wooding <redacted>
Signed-off-by: Junio C Hamano <redacted>

---

 daemon.c |   27 ++++++++++++++++++++++++++-
 1 files changed, 26 insertions(+), 1 deletions(-)

bb1527c884bbb9bf6a5d06c1dd409ea6c2045a91
diff --git a/daemon.c b/daemon.c
index 324bb04..dab8c2c 100644
--- a/daemon.c
+++ b/daemon.c
@@ -13,11 +13,12 @@
 
 static int log_syslog;
 static int verbose;
+static int reuseaddr;
 
 static const char daemon_usage[] =
 "git-daemon [--verbose] [--syslog] [--inetd | --port=n] [--export-all]\n"
 "           [--timeout=n] [--init-timeout=n] [--strict-paths]\n"
-"           [--base-path=path] [directory...]";
+"           [--base-path=path] [--reuseaddr] [directory...]";
 
 /* List of acceptable pathname prefixes */
 static char **ok_paths = NULL;
@@ -451,6 +452,16 @@ static void child_handler(int signo)
 	}
 }
 
+static int set_reuse_addr(int sockfd)
+{
+	int on = 1;
+
+	if (!reuseaddr)
+		return 0;
+	return setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR,
+			  &on, sizeof(on));
+}
+
 #ifndef NO_IPV6
 
 static int socksetup(int port, int **socklist_p)
@@ -495,6 +506,11 @@ static int socksetup(int port, int **soc
 		}
 #endif
 
+		if (set_reuse_addr(sockfd)) {
+			close(sockfd);
+			return 0;	/* not fatal */
+		}
+
 		if (bind(sockfd, ai->ai_addr, ai->ai_addrlen) < 0) {
 			close(sockfd);
 			continue;	/* not fatal */
@@ -537,6 +553,11 @@ static int socksetup(int port, int **soc
 	sin.sin_addr.s_addr = htonl(INADDR_ANY);
 	sin.sin_port = htons(port);
 
+	if (set_reuse_addr(sockfd)) {
+		close(sockfd);
+		return 0;
+	}
+
 	if ( bind(sockfd, (struct sockaddr *)&sin, sizeof sin) < 0 ) {
 		close(sockfd);
 		return 0;
@@ -663,6 +684,10 @@ int main(int argc, char **argv)
 			base_path = arg+12;
 			continue;
 		}
+		if (!strcmp(arg, "--reuseaddr")) {
+			reuseaddr = 1;
+			continue;
+		}
 		if (!strcmp(arg, "--")) {
 			ok_paths = &argv[i+1];
 			break;
-- 
1.1.6.gf7ef

Re: [PATCH 2/3] daemon: Set SO_REUSEADDR on listening sockets.

From: Mark Wooding <hidden>
Date: 2016-06-15 22:42:18

Junio C Hamano [off-list ref] wrote:
But with that, you expose yourself to the confusion TIME_WAIT
was designed to protect you from, so how about making it
optional like this?
No objections there.

-- [mdw]
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help