{"thread":{"id":"3232","subject":"[PATCH 2/3] daemon: Set SO_REUSEADDR on listening sockets.","startedAt":"2006-02-03T20:23:30Z","lastAt":"2006-02-04T22:02:24Z","messageCount":13,"participants":["Mark Wooding","Junio C Hamano"],"isPatch":true,"patchVersion":1,"patchTotal":3},"messages":[{"id":"15530","messageId":"20060203202330.1895.60474.stgit@metalzone.distorted.org.uk","threadId":"3232","inReplyTo":null,"subject":"[PATCH 0/3] git-daemon hacking","fromName":"Mark Wooding","fromEmail":"mdw@distorted.org.uk","sentAt":"2006-02-03T20:23:30Z","receivedAt":"2006-02-03T20:23:30Z","isPatch":true,"sender":{"key":"mdw@distorted.org.uk","avatar":null},"body":"I've just been playing around a bit with the git-daemon.  My main\nobjective was to implement the feature in the third patch, namely to\nallow users to publish their own repositories even though the daemon was\nmainly locked down to serving from a particular central tree.  This is\nthe same idea as allowing a web server to serve files in a user's\n~/public_html directory.\n\nBut I also fixed a couple of bugs I found on the way.\n\n-- [mdw]\n"},{"id":"15531","messageId":"20060203202702.1895.98868.stgit@metalzone.distorted.org.uk","threadId":"3232","inReplyTo":"20060203202330.1895.60474.stgit@metalzone.distorted.org.uk","subject":"[PATCH 1/3] daemon: Provide missing argument for logerror() call.","fromName":"Mark Wooding","fromEmail":"mdw@distorted.org.uk","sentAt":"2006-02-03T20:27:02Z","receivedAt":"2006-02-03T20:27:02Z","isPatch":true,"sender":{"key":"mdw@distorted.org.uk","avatar":null},"body":"From: Mark Wooding <mdw@distorted.org.uk>\n\nCould cause a crash if --base-path set.  Unlikely to be a security the\nconcern: message doesn't go to the client, so we can't leak anything\n(except by dumping core), and we've already forked, so it's not a denial\nof service.\n\nSigned-off-by: Mark Wooding <mdw@distorted.org.uk>\n---\n\n daemon.c |    2 +-\n 1 files changed, 1 insertions(+), 1 deletions(-)\n\ndiff --git a/daemon.c b/daemon.c\nindex bb014fa..532bb0c 100644\n--- a/daemon.c\n+++ b/daemon.c\n@@ -147,7 +147,7 @@ static char *path_ok(char *dir)\n \t\tstatic char rpath[PATH_MAX];\n \t\tif (*dir != '/') {\n \t\t\t/* Forbid possible base-path evasion using ~paths. */\n-\t\t\tlogerror(\"'%s': Non-absolute path denied (base-path active)\");\n+\t\t\tlogerror(\"'%s': Non-absolute path denied (base-path active)\", dir);\n \t\t\treturn NULL;\n \t\t}\n \t\tsnprintf(rpath, PATH_MAX, \"%s%s\", base_path, dir);\n"},{"id":"15528","messageId":"20060203202704.1895.18383.stgit@metalzone.distorted.org.uk","threadId":"3232","inReplyTo":"20060203202330.1895.60474.stgit@metalzone.distorted.org.uk","subject":"[PATCH 2/3] daemon: Set SO_REUSEADDR on listening sockets.","fromName":"Mark Wooding","fromEmail":"mdw@distorted.org.uk","sentAt":"2006-02-03T20:27:04Z","receivedAt":"2006-02-03T20:27:04Z","isPatch":true,"sender":{"key":"mdw@distorted.org.uk","avatar":null},"body":"From: Mark Wooding <mdw@distorted.org.uk>\n\nWithout this, you can silently lose the ability to receive IPv4\nconnections if you stop and restart the daemon.\n\nSigned-off-by: Mark Wooding <mdw@distorted.org.uk>\n---\n\n daemon.c |   14 ++++++++++++++\n 1 files changed, 14 insertions(+), 0 deletions(-)\n\ndiff --git a/daemon.c b/daemon.c\nindex 532bb0c..6b88c0c 100644\n--- a/daemon.c\n+++ b/daemon.c\n@@ -454,6 +454,7 @@ static int socksetup(int port, int **soc\n \tint socknum = 0, *socklist = NULL;\n \tint maxfd = -1;\n \tchar pbuf[NI_MAXSERV];\n+\tint yes = 1;\n \n \tstruct addrinfo hints, *ai0, *ai;\n \tint gai;\n@@ -491,6 +492,12 @@ static int socksetup(int port, int **soc\n \t\t}\n #endif\n \n+\t\tif (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR,\n+\t\t\t       &yes, sizeof(yes))) {\n+\t\t\tclose(sockfd);\n+\t\t\treturn 0;\t/* not fatal */\n+\t\t}\n+\n \t\tif (bind(sockfd, ai->ai_addr, ai->ai_addrlen) < 0) {\n \t\t\tclose(sockfd);\n \t\t\tcontinue;\t/* not fatal */\n@@ -523,6 +530,7 @@ static int socksetup(int port, int **soc\n {\n \tstruct sockaddr_in sin;\n \tint sockfd;\n+\tint yes = 1;\n \n \tsockfd = socket(AF_INET, SOCK_STREAM, 0);\n \tif (sockfd < 0)\n@@ -533,6 +541,12 @@ static int socksetup(int port, int **soc\n \tsin.sin_addr.s_addr = htonl(INADDR_ANY);\n \tsin.sin_port = htons(port);\n \n+\tif (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR,\n+\t\t       &yes, sizeof(yes))) {\n+\t\tclose(sockfd);\n+\t\treturn 0;\n+\t}\n+\n \tif ( bind(sockfd, (struct sockaddr *)&sin, sizeof sin) < 0 ) {\n \t\tclose(sockfd);\n \t\treturn 0;\n"},{"id":"15529","messageId":"20060203202706.1895.70864.stgit@metalzone.distorted.org.uk","threadId":"3232","inReplyTo":"20060203202330.1895.60474.stgit@metalzone.distorted.org.uk","subject":"[PATCH 3/3] daemon: Support a --user-path option.","fromName":"Mark Wooding","fromEmail":"mdw@distorted.org.uk","sentAt":"2006-02-03T20:27:06Z","receivedAt":"2006-02-03T20:27:06Z","isPatch":true,"sender":{"key":"mdw@distorted.org.uk","avatar":null},"body":"From: Mark Wooding <mwooding@ponder.ncipher.com>\n\nIf we're invoked with --user-path=FOO option, then a URL of the form\ngit://~USER/PATH/... resolves to the path HOME/FOO/PATH/..., where HOME\nis USER's home directory.  This is done instead of any transformation\ndue to --base-path, so you can use both at the same time.  This lets\nusers set up their own git repositories to be served by a central\ndaemon, without them all having to be in the same place, and without the\ngit-daemon being allowed to roam the entire filesystem freely, or\nexposing details of filesystem layout on URLs.\n\nSigned-off-by: Mark Wooding <mdw@distorted.org.uk>\n---\n\n Documentation/git-daemon.txt |   11 +++++++++--\n daemon.c                     |   36 +++++++++++++++++++++++++++++++++---\n 2 files changed, 42 insertions(+), 5 deletions(-)\n\ndiff --git a/Documentation/git-daemon.txt b/Documentation/git-daemon.txt\nindex a20e053..2e48a10 100644\n--- a/Documentation/git-daemon.txt\n+++ b/Documentation/git-daemon.txt\n@@ -10,7 +10,7 @@ SYNOPSIS\n [verse]\n 'git-daemon' [--verbose] [--syslog] [--inetd | --port=n] [--export-all]\n              [--timeout=n] [--init-timeout=n] [--strict-paths]\n-             [--base-path=path] [directory...]\n+             [--base-path=path] [--user-path=path] [directory...]\n \n DESCRIPTION\n -----------\n@@ -43,7 +43,7 @@ OPTIONS\n \t'--base-path=/srv/git' on example.com, then if you later try to pull\n \t'git://example.com/hello.git', `git-daemon` will interpret the path\n \tas '/srv/git/hello.git'. Home directories (the '~login' notation)\n-\taccess is disabled.\n+\taccess is disabled unless '--user-path' is also given.\n \n --export-all::\n \tAllow pulling from all directories that look like GIT repositories\n@@ -70,6 +70,13 @@ OPTIONS\n \tLog to syslog instead of stderr. Note that this option does not imply\n \t--verbose, thus by default only error conditions will be logged.\n \n+--user-path::\n+\tRewrite a request for \"~user/something\" to\n+\t\"home/user-path/something\".  Useful in conjunction with\n+\t'--base-path', if you want to restrict the daemon from roaming\n+\tthe entire filesystem without preventing users from publishing\n+\ttheir own repositories.\n+\n --verbose::\n \tLog details about the incoming connections and requested files.\n \ndiff --git a/daemon.c b/daemon.c\nindex 6b88c0c..95b9c7e 100644\n--- a/daemon.c\n+++ b/daemon.c\n@@ -6,6 +6,7 @@\n #include <netdb.h>\n #include <netinet/in.h>\n #include <arpa/inet.h>\n+#include <pwd.h>\n #include <syslog.h>\n #include \"pkt-line.h\"\n #include \"cache.h\"\n@@ -17,7 +18,7 @@ static int verbose;\n static const char daemon_usage[] =\n \"git-daemon [--verbose] [--syslog] [--inetd | --port=n] [--export-all]\\n\"\n \"           [--timeout=n] [--init-timeout=n] [--strict-paths]\\n\"\n-\"           [--base-path=path] [directory...]\";\n+\"           [--base-path=path] [--user-path=path] [directory...]\";\n \n /* List of acceptable pathname prefixes */\n static char **ok_paths = NULL;\n@@ -28,6 +29,7 @@ static int export_all_trees = 0;\n \n /* Take all paths relative to this one if non-NULL */\n static char *base_path = NULL;\n+static char *user_path = NULL;\n \n /* Timeout, and initial timeout */\n static unsigned int timeout = 0;\n@@ -137,14 +139,34 @@ static int avoid_alias(char *p)\n static char *path_ok(char *dir)\n {\n \tchar *path;\n+\tstatic char rpath[PATH_MAX];\n \n \tif (avoid_alias(dir)) {\n \t\tlogerror(\"'%s': aliased\", dir);\n \t\treturn NULL;\n \t}\n \n-\tif (base_path) {\n-\t\tstatic char rpath[PATH_MAX];\n+\tif (user_path && *dir == '~') {\n+\t\tstruct passwd *pw;\n+\t\tchar *u, *p;\n+\n+\t\tu = dir + 1;\n+\t\tp = strchr(u, '/');\n+\t\tif (!p) {\n+\t\t\tlogerror(\"'%s': Missing / after user name\", dir);\n+\t\t\treturn NULL;\n+\t\t}\n+\t\t*p = 0;\n+\t\tpw = getpwnam(u);\n+\t\t*p++ = '/';\n+\t\tif (!pw) {\n+\t\t\tlogerror(\"'%s': User not found\", u);\n+\t\t\treturn NULL;\n+\t\t}\n+\t\tsnprintf(rpath, PATH_MAX, \"%s/%s/%s\",\n+\t\t\t pw->pw_dir, user_path, p);\n+\t\tdir = rpath;\n+\t} else if (base_path) {\n \t\tif (*dir != '/') {\n \t\t\t/* Forbid possible base-path evasion using ~paths. */\n \t\t\tlogerror(\"'%s': Non-absolute path denied (base-path active)\", dir);\n@@ -491,6 +513,10 @@ static int socksetup(int port, int **soc\n \t\t\t/* Note: error is not fatal */\n \t\t}\n #endif\n+\t\tif (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes))) {\n+\t\t\tclose(sockfd);\n+\t\t\tcontinue;\t/* not fatal */\n+\t\t}\n \n \t\tif (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR,\n \t\t\t       &yes, sizeof(yes))) {\n@@ -673,6 +699,10 @@ int main(int argc, char **argv)\n \t\t\tbase_path = arg+12;\n \t\t\tcontinue;\n \t\t}\n+\t\tif (!strncmp(arg, \"--user-path=\", 12)) {\n+\t\t\tuser_path = arg+12;\n+\t\t\tcontinue;\n+\t\t}\n \t\tif (!strcmp(arg, \"--\")) {\n \t\t\tok_paths = &argv[i+1];\n \t\t\tbreak;\n"},{"id":"15535","messageId":"7vr76kcggx.fsf@assigned-by-dhcp.cox.net","threadId":"3232","inReplyTo":"20060203202706.1895.70864.stgit@metalzone.distorted.org.uk","subject":"Re: [PATCH 3/3] daemon: Support a --user-path option.","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2006-02-03T20:52:46Z","receivedAt":"2006-02-03T20:52:46Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Mark Wooding <mdw@distorted.org.uk> writes:\n\n> If we're invoked with --user-path=FOO option, then a URL of the form\n> git://~USER/PATH/... resolves to the path HOME/FOO/PATH/..., where HOME\n> is USER's home directory.\n\nI am probably slow as usual but I do not see how this is useful.\n\nWouldn't loosening the \"request must be absolute if you use\n--base-path\" check in the area your first patch in the series\ntouches to also allow paths that start with a '~' be enough?\nThat way ~alice/foo would remain to be /home/alice/foo (with\n/home/alice being alice's $HOME) and ~becky/bar would be\n/home2/becky/bar (with /home2/becky being becky's $HOME).\n\nI suppose you are doing something similar to ~/public_html, but\nI think that is an independent feature.\n"},{"id":"15536","messageId":"7vmzh8cg9o.fsf@assigned-by-dhcp.cox.net","threadId":"3232","inReplyTo":"20060203202704.1895.18383.stgit@metalzone.distorted.org.uk","subject":"Re: [PATCH 2/3] daemon: Set SO_REUSEADDR on listening sockets.","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2006-02-03T20:57:07Z","receivedAt":"2006-02-03T20:57:07Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Mark Wooding <mdw@distorted.org.uk> writes:\n\n> From: Mark Wooding <mdw@distorted.org.uk>\n>\n> Without this, you can silently lose the ability to receive IPv4\n> connections if you stop and restart the daemon.\n>\n> Signed-off-by: Mark Wooding <mdw@distorted.org.uk>\n\nI've always wanted to ask HPA and Linus about this why we did\nnot do SO_REUSEADDR.  I've seen some non-git servers also not\nusing it, giving me an excuse to take a coffee break during work\n;-).  Is it because they predate REUSEADDR, or is there a valid\nreason to avoid using REUSEADDR under certain conditions?\n"},{"id":"15561","messageId":"7vfymz33v8.fsf@assigned-by-dhcp.cox.net","threadId":"3232","inReplyTo":"20060203202704.1895.18383.stgit@metalzone.distorted.org.uk","subject":"Re: [PATCH 2/3] daemon: Set SO_REUSEADDR on listening sockets.","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2006-02-04T08:49:47Z","receivedAt":"2006-02-04T08:49:47Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Mark Wooding <mdw@distorted.org.uk> writes:\n\n> From: Mark Wooding <mdw@distorted.org.uk>\n>\n> Without this, you can silently lose the ability to receive IPv4\n> connections if you stop and restart the daemon.\n>\n> Signed-off-by: Mark Wooding <mdw@distorted.org.uk>\n\nBut with that, you expose yourself to the confusion TIME_WAIT\nwas designed to protect you from, so how about making it\noptional like this?\n\nTested, of course ;-).\n\n-- >8 --\nFrom nobody Mon Sep 17 00:00:00 2001\nFrom: Mark Wooding <mdw@distorted.org.uk>\nDate: Fri Feb 3 20:27:04 2006 +0000\nSubject: [PATCH] daemon: Set SO_REUSEADDR on listening sockets.\n\nWithout this, you can silently lose the ability to receive IPv4\nconnections if you stop and restart the daemon.\n\n[jc: tweaked code organization a bit and made this controllable\n from a command line option.]\n\nSigned-off-by: Mark Wooding <mdw@distorted.org.uk>\nSigned-off-by: Junio C Hamano <junkio@cox.net>\n\n---\n\n daemon.c |   27 ++++++++++++++++++++++++++-\n 1 files changed, 26 insertions(+), 1 deletions(-)\n\nbb1527c884bbb9bf6a5d06c1dd409ea6c2045a91\ndiff --git a/daemon.c b/daemon.c\nindex 324bb04..dab8c2c 100644\n--- a/daemon.c\n+++ b/daemon.c\n@@ -13,11 +13,12 @@\n \n static int log_syslog;\n static int verbose;\n+static int reuseaddr;\n \n static const char daemon_usage[] =\n \"git-daemon [--verbose] [--syslog] [--inetd | --port=n] [--export-all]\\n\"\n \"           [--timeout=n] [--init-timeout=n] [--strict-paths]\\n\"\n-\"           [--base-path=path] [directory...]\";\n+\"           [--base-path=path] [--reuseaddr] [directory...]\";\n \n /* List of acceptable pathname prefixes */\n static char **ok_paths = NULL;\n@@ -451,6 +452,16 @@ static void child_handler(int signo)\n \t}\n }\n \n+static int set_reuse_addr(int sockfd)\n+{\n+\tint on = 1;\n+\n+\tif (!reuseaddr)\n+\t\treturn 0;\n+\treturn setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR,\n+\t\t\t  &on, sizeof(on));\n+}\n+\n #ifndef NO_IPV6\n \n static int socksetup(int port, int **socklist_p)\n@@ -495,6 +506,11 @@ static int socksetup(int port, int **soc\n \t\t}\n #endif\n \n+\t\tif (set_reuse_addr(sockfd)) {\n+\t\t\tclose(sockfd);\n+\t\t\treturn 0;\t/* not fatal */\n+\t\t}\n+\n \t\tif (bind(sockfd, ai->ai_addr, ai->ai_addrlen) < 0) {\n \t\t\tclose(sockfd);\n \t\t\tcontinue;\t/* not fatal */\n@@ -537,6 +553,11 @@ static int socksetup(int port, int **soc\n \tsin.sin_addr.s_addr = htonl(INADDR_ANY);\n \tsin.sin_port = htons(port);\n \n+\tif (set_reuse_addr(sockfd)) {\n+\t\tclose(sockfd);\n+\t\treturn 0;\n+\t}\n+\n \tif ( bind(sockfd, (struct sockaddr *)&sin, sizeof sin) < 0 ) {\n \t\tclose(sockfd);\n \t\treturn 0;\n@@ -663,6 +684,10 @@ int main(int argc, char **argv)\n \t\t\tbase_path = arg+12;\n \t\t\tcontinue;\n \t\t}\n+\t\tif (!strcmp(arg, \"--reuseaddr\")) {\n+\t\t\treuseaddr = 1;\n+\t\t\tcontinue;\n+\t\t}\n \t\tif (!strcmp(arg, \"--\")) {\n \t\t\tok_paths = &argv[i+1];\n \t\t\tbreak;\n-- \n1.1.6.gf7ef\n"},{"id":"15562","messageId":"7v8xsr33ur.fsf@assigned-by-dhcp.cox.net","threadId":"3232","inReplyTo":"7vr76kcggx.fsf@assigned-by-dhcp.cox.net","subject":"Re: [PATCH 3/3] daemon: Support a --user-path option.","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2006-02-04T08:50:04Z","receivedAt":"2006-02-04T08:50:04Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Junio C Hamano <junkio@cox.net> writes:\n\n> Wouldn't loosening the \"request must be absolute if you use\n> --base-path\" check in the area your first patch in the series\n> touches to also allow paths that start with a '~' be enough?\n\nThat is, something like this is what I mean.\n\nTested, of course ;-).\n\ndiff --git a/daemon.c b/daemon.c\nindex 532bb0c..324bb04 100644\n--- a/daemon.c\n+++ b/daemon.c\n@@ -145,13 +145,17 @@ static char *path_ok(char *dir)\n \n \tif (base_path) {\n \t\tstatic char rpath[PATH_MAX];\n-\t\tif (*dir != '/') {\n-\t\t\t/* Forbid possible base-path evasion using ~paths. */\n+\t\tif (!strict_paths && *dir == '~')\n+\t\t\t; /* allow user relative paths */\n+\t\telse if (*dir != '/') {\n+\t\t\t/* otherwise allow only absolute */\n \t\t\tlogerror(\"'%s': Non-absolute path denied (base-path active)\", dir);\n \t\t\treturn NULL;\n \t\t}\n-\t\tsnprintf(rpath, PATH_MAX, \"%s%s\", base_path, dir);\n-\t\tdir = rpath;\n+\t\telse {\n+\t\t\tsnprintf(rpath, PATH_MAX, \"%s%s\", base_path, dir);\n+\t\t\tdir = rpath;\n+\t\t}\n \t}\n \n \tpath = enter_repo(dir, strict_paths);\n"},{"id":"15564","messageId":"slrndu8utr.2i8.mdw@metalzone.distorted.org.uk","threadId":"3232","inReplyTo":"7vr76kcggx.fsf@assigned-by-dhcp.cox.net","subject":"Re: [PATCH 3/3] daemon: Support a --user-path option.","fromName":"Mark Wooding","fromEmail":"mdw@distorted.org.uk","sentAt":"2006-02-04T10:02:35Z","receivedAt":"2006-02-04T10:02:35Z","isPatch":true,"sender":{"key":"mdw@distorted.org.uk","avatar":null},"body":"Junio C Hamano <junkio@cox.net> wrote:\n\n> I am probably slow as usual but I do not see how this is useful.\n\nI don't want the git-daemon roaming all over the file system.  Partly,\nas a systems administrator, it makes me nervous about security (not for\nany particularly good reason, I admit), but mainly because I don't want\nto be exposing my local filesystem structure in my git://... namespace\n-- it just seems like a bad idea.  This is what --base-path is all about.\n\nI do still want users to be able to publish their repositories.  But I\nalso don't want git-daemon wandering all over their home directories --\nrestriction to sensible places is what --base-path is for, after all.\n\n> Wouldn't loosening the \"request must be absolute if you use\n> --base-path\" check in the area your first patch in the series\n> touches to also allow paths that start with a '~' be enough?\n> That way ~alice/foo would remain to be /home/alice/foo (with\n> /home/alice being alice's $HOME) and ~becky/bar would be\n> /home2/becky/bar (with /home2/becky being becky's $HOME).\n\nThat would still expose the structure of everyone's home directories in\ngit://~user URLs, which is rather unfortunate.  It's better than\nnothing, though.\n\n> I suppose you are doing something similar to ~/public_html, but\n> I think that is an independent feature.\n\nThis is what I'm after, yes.  The above can be achieved\nstraightforwardly with --user-path=. if that's what you actually wanted.\n(Indeed, --user-path= works too, but this is harder to explain.)\n\nI think I'd probably either run with --user-path=public-git or\n--user-path=public_html/git -- I've not made my mind up.\n\n-- [mdw]\n"},{"id":"15565","messageId":"slrndu8vn0.2i8.mdw@metalzone.distorted.org.uk","threadId":"3232","inReplyTo":"7vfymz33v8.fsf@assigned-by-dhcp.cox.net","subject":"Re: [PATCH 2/3] daemon: Set SO_REUSEADDR on listening sockets.","fromName":"Mark Wooding","fromEmail":"mdw@distorted.org.uk","sentAt":"2006-02-04T10:16:00Z","receivedAt":"2006-02-04T10:16:00Z","isPatch":true,"sender":{"key":"mdw@distorted.org.uk","avatar":null},"body":"Junio C Hamano <junkio@cox.net> wrote:\n\n> But with that, you expose yourself to the confusion TIME_WAIT\n> was designed to protect you from, so how about making it\n> optional like this?\n\nNo objections there.\n\n-- [mdw]\n"},{"id":"15574","messageId":"7v8xsrz49e.fsf@assigned-by-dhcp.cox.net","threadId":"3232","inReplyTo":"slrndu8utr.2i8.mdw@metalzone.distorted.org.uk","subject":"Re: [PATCH 3/3] daemon: Support a --user-path option.","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2006-02-04T12:40:13Z","receivedAt":"2006-02-04T12:40:13Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Mark Wooding <mdw@distorted.org.uk> writes:\n\n> This is what I'm after, yes.  The above can be achieved\n> straightforwardly with --user-path=. if that's what you actually wanted.\n> (Indeed, --user-path= works too, but this is harder to explain.)\n>\n> I think I'd probably either run with --user-path=public-git or\n> --user-path=public_html/git -- I've not made my mind up.\n\nI made that conditional to --strict, but come to think of it, an\nindependent option like --user-path makes more sense, whether\nthat option does the public_html-like path munging or not.\n\nI think for personal repositories public_html-like limiting may\nbe simpler to manage than the current approach of using\ngit-daemon-export-ok flag file (the latter is more flexible but\nmost people probably do not need that flexibility).\n\nIn my simplistic view, --base-path serves something like /pub\nhierarchy of an ftp server or /var/www of an http server.  It\ngoes hand-in-hand with the whitelist and everything under it are\nexported without having to mark individual directories with\ngit-daemon-export-ok (or having a name like public_html to mark\nit exportable).  For ~user/ based paths, it is natural to wish\nto limit the parts of home directories but there currently is\nnot a good way to do so.  We could probably extend the whitelist\nto take path glob patterns and say \"~*/public-git/\" or something\nsilly like that, but that still means the request must be in the\nform \"git://host/~alice/public-git/frotz.git/\" (which may not be\nsuch a bad thing); \"git://host/~alice/frotz.git/\" might look\nnicer.  Your path munging idea is one way to do so.  Another\nwould be for alice to have $HOME/frotz.git/git-daemon-export-ok.\nPersonally I do not think either would make too much of a\ndifference from usability point of view.\n\nSo I am not dismissing what you are trying to achieve here.\nHowever, I am not happy about having <pwd.h> there and majorly\nduplicating what enter_repo() does in that part of the code.\n"},{"id":"15579","messageId":"slrndu9v7h.2i8.mdw@metalzone.distorted.org.uk","threadId":"3232","inReplyTo":"7v8xsrz49e.fsf@assigned-by-dhcp.cox.net","subject":"Re: [PATCH 3/3] daemon: Support a --user-path option.","fromName":"Mark Wooding","fromEmail":"mdw@distorted.org.uk","sentAt":"2006-02-04T19:13:53Z","receivedAt":"2006-02-04T19:13:53Z","isPatch":true,"sender":{"key":"mdw@distorted.org.uk","avatar":null},"body":"Junio C Hamano <junkio@cox.net> wrote:\n\n> In my simplistic view, --base-path serves something like /pub\n> hierarchy of an ftp server or /var/www of an http server. \n\nYes, that's the way I think of it too.  I'll probably point it at\n/var/www/git or something when I finally get my tuits.\n\n> For ~user/ based paths, it is natural to wish to limit the parts of\n> home directories but there currently is not a good way to do so.  We\n> could probably extend the whitelist to take path glob patterns and say\n> \"~*/public-git/\" or something silly like that, but that still means\n> the request must be in the form\n> \"git://host/~alice/public-git/frotz.git/\" (which may not be such a bad\n> thing); \"git://host/~alice/frotz.git/\" might look nicer. \n\nPutting `public-git' in the URLs seems to exposing an unnecessary\ndetail.  Or it's saying something utterly obvious -- of course it's a\npublic GIT -- otherwise you wouldn't be asking for it.\n\n> Your path munging idea is one way to do so.  Another would be for\n> alice to have $HOME/frotz.git/git-daemon-export-ok.  Personally I do\n> not think either would make too much of a difference from usability\n> point of view.\n\nThe idea of filling my home directory with all my GIT repositories isn't\none I'm particularly keen on.  For one thing, I can just tell that I'm\ngoing to get confused between ~/src/foo/.git and ~/foo.git some day; and\nbesides, I like `ls ~' to be nice and short.\n\n> So I am not dismissing what you are trying to achieve here.\n> However, I am not happy about having <pwd.h> there and majorly\n> duplicating what enter_repo() does in that part of the code.\n\nOK, then: how about putting the user_path logic into enter_repo?  Like\nthis, perhaps:\n\n\n Documentation/git-daemon.txt |   11 +++++++++--\n cache.h                      |    2 +-\n daemon.c                     |   16 +++++++++++-----\n path.c                       |   34 ++++++++++++++++++++++++++++------\n receive-pack.c               |    2 +-\n upload-pack.c                |    2 +-\n 6 files changed, 51 insertions(+), 16 deletions(-)\n\ndiff --git a/Documentation/git-daemon.txt b/Documentation/git-daemon.txt\nindex a20e053..2e48a10 100644\n--- a/Documentation/git-daemon.txt\n+++ b/Documentation/git-daemon.txt\n@@ -10,7 +10,7 @@ SYNOPSIS\n [verse]\n 'git-daemon' [--verbose] [--syslog] [--inetd | --port=n] [--export-all]\n              [--timeout=n] [--init-timeout=n] [--strict-paths]\n-             [--base-path=path] [directory...]\n+             [--base-path=path] [--user-path=path] [directory...]\n \n DESCRIPTION\n -----------\n@@ -43,7 +43,7 @@ OPTIONS\n \t'--base-path=/srv/git' on example.com, then if you later try to pull\n \t'git://example.com/hello.git', `git-daemon` will interpret the path\n \tas '/srv/git/hello.git'. Home directories (the '~login' notation)\n-\taccess is disabled.\n+\taccess is disabled unless '--user-path' is also given.\n \n --export-all::\n \tAllow pulling from all directories that look like GIT repositories\n@@ -70,6 +70,13 @@ OPTIONS\n \tLog to syslog instead of stderr. Note that this option does not imply\n \t--verbose, thus by default only error conditions will be logged.\n \n+--user-path::\n+\tRewrite a request for \"~user/something\" to\n+\t\"home/user-path/something\".  Useful in conjunction with\n+\t'--base-path', if you want to restrict the daemon from roaming\n+\tthe entire filesystem without preventing users from publishing\n+\ttheir own repositories.\n+\n --verbose::\n \tLog details about the incoming connections and requested files.\n \ndiff --git a/cache.h b/cache.h\nindex bdbe2d6..b6c9cda 100644\n--- a/cache.h\n+++ b/cache.h\n@@ -191,7 +191,7 @@ int git_mkstemp(char *path, size_t n, co\n int adjust_shared_perm(const char *path);\n int safe_create_leading_directories(char *path);\n char *safe_strncpy(char *, const char *, size_t);\n-char *enter_repo(char *path, int strict);\n+char *enter_repo(char *path, const char *u_path, int strict);\n \n /* Read and unpack a sha1 file into memory, write memory to a sha1 file */\n extern int unpack_sha1_header(z_stream *stream, void *map, unsigned long mapsize, void *buffer, unsigned long size);\ndiff --git a/daemon.c b/daemon.c\nindex bb014fa..22d133d 100644\n--- a/daemon.c\n+++ b/daemon.c\n@@ -17,7 +17,7 @@ static int verbose;\n static const char daemon_usage[] =\n \"git-daemon [--verbose] [--syslog] [--inetd | --port=n] [--export-all]\\n\"\n \"           [--timeout=n] [--init-timeout=n] [--strict-paths]\\n\"\n-\"           [--base-path=path] [directory...]\";\n+\"           [--base-path=path] [--user-path=path] [directory...]\";\n \n /* List of acceptable pathname prefixes */\n static char **ok_paths = NULL;\n@@ -28,6 +28,7 @@ static int export_all_trees = 0;\n \n /* Take all paths relative to this one if non-NULL */\n static char *base_path = NULL;\n+static char *user_path = NULL;\n \n /* Timeout, and initial timeout */\n static unsigned int timeout = 0;\n@@ -137,14 +138,16 @@ static int avoid_alias(char *p)\n static char *path_ok(char *dir)\n {\n \tchar *path;\n+\tstatic char rpath[PATH_MAX];\n \n \tif (avoid_alias(dir)) {\n \t\tlogerror(\"'%s': aliased\", dir);\n \t\treturn NULL;\n \t}\n \n-\tif (base_path) {\n-\t\tstatic char rpath[PATH_MAX];\n+\tif (user_path && dir[0] == '~')\n+\t\t/* okay */;\n+\telse if (base_path) {\n \t\tif (*dir != '/') {\n \t\t\t/* Forbid possible base-path evasion using ~paths. */\n \t\t\tlogerror(\"'%s': Non-absolute path denied (base-path active)\");\n@@ -154,7 +157,7 @@ static char *path_ok(char *dir)\n \t\tdir = rpath;\n \t}\n \n-\tpath = enter_repo(dir, strict_paths);\n+\tpath = enter_repo(dir, user_path, strict_paths);\n \n \tif (!path) {\n \t\tlogerror(\"'%s': unable to chdir or not a git archive\", dir);\n@@ -490,7 +493,6 @@ static int socksetup(int port, int **soc\n \t\t\t/* Note: error is not fatal */\n \t\t}\n #endif\n-\n \t\tif (bind(sockfd, ai->ai_addr, ai->ai_addrlen) < 0) {\n \t\t\tclose(sockfd);\n \t\t\tcontinue;\t/* not fatal */\n@@ -659,6 +661,10 @@ int main(int argc, char **argv)\n \t\t\tbase_path = arg+12;\n \t\t\tcontinue;\n \t\t}\n+\t\tif (!strncmp(arg, \"--user-path=\", 12)) {\n+\t\t\tuser_path = arg+12;\n+\t\t\tcontinue;\n+\t\t}\n \t\tif (!strcmp(arg, \"--\")) {\n \t\t\tok_paths = &argv[i+1];\n \t\t\tbreak;\ndiff --git a/path.c b/path.c\nindex 334b2bd..596e37e 100644\n--- a/path.c\n+++ b/path.c\n@@ -131,7 +131,17 @@ int validate_symref(const char *path)\n \treturn -1;\n }\n \n-static char *user_path(char *buf, char *path, int sz)\n+static void kill_trailing_slashes(char *buf, int *lenp)\n+{\n+\tint len = *lenp;\n+\twhile ((1 < len) && (buf[len-1] == '/')) {\n+\t\tbuf[len-1] = 0;\n+\t\tlen--;\n+\t}\n+\t*lenp = len;\n+}\n+\n+static char *user_path(char *buf, char *path, const char *u_path, int sz)\n {\n \tstruct passwd *pw;\n \tchar *slash;\n@@ -157,9 +167,17 @@ static char *user_path(char *buf, char *\n \t\treturn NULL;\n \tbaselen = strlen(pw->pw_dir);\n \tmemcpy(buf, pw->pw_dir, baselen);\n-\twhile ((1 < baselen) && (buf[baselen-1] == '/')) {\n-\t\tbuf[baselen-1] = 0;\n-\t\tbaselen--;\n+\tkill_trailing_slashes(buf, &baselen);\n+\tif (u_path) {\n+\t\twhile (*u_path == '/')\n+\t\t\tu_path++;\n+\t\tlen = strlen(u_path);\n+\t\tif (sz <= baselen + len + 1)\n+\t\t\treturn NULL;\n+\t\tbuf[baselen++] = '/';\n+\t\tmemcpy(buf + baselen, u_path, len);\n+\t\tbaselen += len;\n+\t\tkill_trailing_slashes(buf, &baselen);\n \t}\n \tif (slash && slash[1]) {\n \t\tlen = strlen(slash);\n@@ -184,6 +202,10 @@ static char *user_path(char *buf, char *\n  * \"%s/.git\", \"%s.git\", \"%s\" in this order.  The first one that exists is\n  * what we try.\n  *\n+ * If \"u_path\" is given, and the path we're resolving has the form ~/path\n+ * or ~user/path, then we resolve to home/u_path/path (where home is the\n+ * appropriate user's home directory).\n+ *\n  * Second, we try chdir() to that.  Upon failure, we return NULL.\n  *\n  * Then, we try if the current directory is a valid git repository.\n@@ -194,7 +216,7 @@ static char *user_path(char *buf, char *\n  * links.  User relative paths are also returned as they are given,\n  * except DWIM suffixing.\n  */\n-char *enter_repo(char *path, int strict)\n+char *enter_repo(char *path, const char *u_path, int strict)\n {\n \tstatic char used_path[PATH_MAX];\n \tstatic char validated_path[PATH_MAX];\n@@ -215,7 +237,7 @@ char *enter_repo(char *path, int strict)\n \t\tif (PATH_MAX <= len)\n \t\t\treturn NULL;\n \t\tif (path[0] == '~') {\n-\t\t\tif (!user_path(used_path, path, PATH_MAX))\n+\t\t\tif (!user_path(used_path, path, u_path, PATH_MAX))\n \t\t\t\treturn NULL;\n \t\t\tstrcpy(validated_path, path);\n \t\t\tpath = used_path;\ndiff --git a/receive-pack.c b/receive-pack.c\nindex eae31e3..d411ab2 100644\n--- a/receive-pack.c\n+++ b/receive-pack.c\n@@ -317,7 +317,7 @@ int main(int argc, char **argv)\n \tif (!dir)\n \t\tusage(receive_pack_usage);\n \n-\tif(!enter_repo(dir, 0))\n+\tif(!enter_repo(dir, NULL, 0))\n \t\tdie(\"'%s': unable to chdir or not a git archive\", dir);\n \n \twrite_head_info();\ndiff --git a/upload-pack.c b/upload-pack.c\nindex d198055..3468de1 100644\n--- a/upload-pack.c\n+++ b/upload-pack.c\n@@ -276,7 +276,7 @@ int main(int argc, char **argv)\n \t\tusage(upload_pack_usage);\n \tdir = argv[i];\n \n-\tif (!enter_repo(dir, strict))\n+\tif (!enter_repo(dir, NULL, strict))\n \t\tdie(\"'%s': unable to chdir or not a git archive\", dir);\n \n \tupload_pack();\n\n\n-- [mdw]\n"},{"id":"15584","messageId":"7vacd6wznz.fsf@assigned-by-dhcp.cox.net","threadId":"3232","inReplyTo":"slrndu9v7h.2i8.mdw@metalzone.distorted.org.uk","subject":"Re: [PATCH 3/3] daemon: Support a --user-path option.","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2006-02-04T22:02:24Z","receivedAt":"2006-02-04T22:02:24Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Mark Wooding <mdw@distorted.org.uk> writes:\n\n> OK, then: how about putting the user_path logic into enter_repo?  Like\n> this, perhaps:\n\nI'd like to *REALLY* leave enter_repo alone unless necessary.\nThe last round to finalize enter_repo() was painful enough to\nthink its security implications through.  And this particular\n\"public_html\" insertion change can be done without touching it.\n\nI think the attached patch would essentially do the same thing.\nOnly lightly tested, but tested nevertheless ;-).  This comes on\ntop of what I have in 'pu'.\n\nOne thing I have not done yet but I consider bould be a good\nchange is to perhaps change the whitelist logic in path_ok()\nafter enter_repo() says what canonicalized path it decided to\nuse, so that it allows any user-relative paths without checking\nthe whitelist when user_path is in effect (and perhaps not\nempty, so giving --user-path=\"public_git\" would allow everybody\nwhile giving --user-path alone or --user-path=\"\" would not).\nOtherwise, as the code currently stands (regardless of any of\nthe patches in the discussion in this thread) I think you need\nto list all users on the whitelist line.  I think --user-path is\nabout giving all users ability to export their stuff.  What do\nyou think?\n\n-- >8 --\ndiff --git a/daemon.c b/daemon.c\nindex dab8c2c..a1ccda3 100644\n--- a/daemon.c\n+++ b/daemon.c\n@@ -18,7 +18,8 @@ static int reuseaddr;\n static const char daemon_usage[] =\n \"git-daemon [--verbose] [--syslog] [--inetd | --port=n] [--export-all]\\n\"\n \"           [--timeout=n] [--init-timeout=n] [--strict-paths]\\n\"\n-\"           [--base-path=path] [--reuseaddr] [directory...]\";\n+\"           [--base-path=path] [--user-path | --user-path=path]\\n\"\n+\"           [--reuseaddr] [directory...]\";\n \n /* List of acceptable pathname prefixes */\n static char **ok_paths = NULL;\n@@ -30,6 +31,12 @@ static int export_all_trees = 0;\n /* Take all paths relative to this one if non-NULL */\n static char *base_path = NULL;\n \n+/* If defined, ~user notation is allowed and the string is inserted\n+ * after ~user/.  E.g. a request to git://host/~alice/frotz would\n+ * go to /home/alice/pub_git/frotz with --user-path=pub_git.\n+ */\n+static char *user_path = NULL;\n+\n /* Timeout, and initial timeout */\n static unsigned int timeout = 0;\n static unsigned int init_timeout = 0;\n@@ -137,6 +144,7 @@ static int avoid_alias(char *p)\n \n static char *path_ok(char *dir)\n {\n+\tstatic char rpath[PATH_MAX];\n \tchar *path;\n \n \tif (avoid_alias(dir)) {\n@@ -144,12 +152,31 @@ static char *path_ok(char *dir)\n \t\treturn NULL;\n \t}\n \n-\tif (base_path) {\n-\t\tstatic char rpath[PATH_MAX];\n-\t\tif (!strict_paths && *dir == '~')\n-\t\t\t; /* allow user relative paths */\n-\t\telse if (*dir != '/') {\n-\t\t\t/* otherwise allow only absolute */\n+\tif (*dir == '~') {\n+\t\tif (!user_path) {\n+\t\t\tlogerror(\"'%s': User-path not allowed\", dir);\n+\t\t\treturn NULL;\n+\t\t}\n+\t\tif (*user_path) {\n+\t\t\t/* Got either \"~alice\" or \"~alice/foo\";\n+\t\t\t * rewrite them to \"~alice/%s\" or\n+\t\t\t * \"~alice/%s/foo\".\n+\t\t\t */\n+\t\t\tint namlen, restlen = strlen(dir);\n+\t\t\tchar *slash = strchr(dir, '/');\n+\t\t\tif (!slash)\n+\t\t\t\tslash = dir + restlen;\n+\t\t\tnamlen = slash - dir;\n+\t\t\trestlen -= namlen;\n+\t\t\tloginfo(\"userpath <%s>, request <%s>, namlen %d, restlen %d, slash <%s>\", user_path, dir, namlen, restlen, slash);\n+\t\t\tsnprintf(rpath, PATH_MAX, \"%.*s/%s%.*s\",\n+\t\t\t\t namlen, dir, user_path, restlen, slash);\n+\t\t\tdir = rpath;\n+\t\t}\n+\t}\n+\telse if (base_path) {\n+\t\tif (*dir != '/') {\n+\t\t\t/* Allow only absolute */\n \t\t\tlogerror(\"'%s': Non-absolute path denied (base-path active)\", dir);\n \t\t\treturn NULL;\n \t\t}\n@@ -688,6 +715,14 @@ int main(int argc, char **argv)\n \t\t\treuseaddr = 1;\n \t\t\tcontinue;\n \t\t}\n+\t\tif (!strcmp(arg, \"--user-path\")) {\n+\t\t\tuser_path = \"\";\n+\t\t\tcontinue;\n+\t\t}\n+\t\tif (!strncmp(arg, \"--user-path=\", 12)) {\n+\t\t\tuser_path = arg + 12;\n+\t\t\tcontinue;\n+\t\t}\n \t\tif (!strcmp(arg, \"--\")) {\n \t\t\tok_paths = &argv[i+1];\n \t\t\tbreak;\n"}]}