Merge branch 'fix'
[gitweb.git] / daemon.c
index ac4c94bc709350471d66180d427a7e5df91c6235..bb014fa9c2cb8c02a3952d0fc521358222662da1 100644 (file)
--- a/daemon.c
+++ b/daemon.c
@@ -9,13 +9,15 @@
 #include <syslog.h>
 #include "pkt-line.h"
 #include "cache.h"
+#include "exec_cmd.h"
 
 static int log_syslog;
 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] [directory...]";
+"           [--timeout=n] [--init-timeout=n] [--strict-paths]\n"
+"           [--base-path=path] [directory...]";
 
 /* List of acceptable pathname prefixes */
 static char **ok_paths = NULL;
@@ -24,6 +26,9 @@ static int strict_paths = 0;
 /* If this is set, git-daemon-export-ok is not required */
 static int export_all_trees = 0;
 
+/* Take all paths relative to this one if non-NULL */
+static char *base_path = NULL;
+
 /* Timeout, and initial timeout */
 static unsigned int timeout = 0;
 static unsigned int init_timeout = 0;
@@ -82,9 +87,74 @@ static void loginfo(const char *err, ...)
        va_end(params);
 }
 
+static int avoid_alias(char *p)
+{
+       int sl, ndot;
+
+       /* 
+        * This resurrects the belts and suspenders paranoia check by HPA
+        * done in <435560F7.4080006@zytor.com> thread, now enter_repo()
+        * does not do getcwd() based path canonicalizations.
+        *
+        * sl becomes true immediately after seeing '/' and continues to
+        * be true as long as dots continue after that without intervening
+        * non-dot character.
+        */
+       if (!p || (*p != '/' && *p != '~'))
+               return -1;
+       sl = 1; ndot = 0;
+       p++;
+
+       while (1) {
+               char ch = *p++;
+               if (sl) {
+                       if (ch == '.')
+                               ndot++;
+                       else if (ch == '/') {
+                               if (ndot < 3)
+                                       /* reject //, /./ and /../ */
+                                       return -1;
+                               ndot = 0;
+                       }
+                       else if (ch == 0) {
+                               if (0 < ndot && ndot < 3)
+                                       /* reject /.$ and /..$ */
+                                       return -1;
+                               return 0;
+                       }
+                       else
+                               sl = ndot = 0;
+               }
+               else if (ch == 0)
+                       return 0;
+               else if (ch == '/') {
+                       sl = 1;
+                       ndot = 0;
+               }
+       }
+}
+
 static char *path_ok(char *dir)
 {
-       char *path = enter_repo(dir, strict_paths);
+       char *path;
+
+       if (avoid_alias(dir)) {
+               logerror("'%s': aliased", dir);
+               return NULL;
+       }
+
+       if (base_path) {
+               static char rpath[PATH_MAX];
+               if (*dir != '/') {
+                       /* Forbid possible base-path evasion using ~paths. */
+                       logerror("'%s': Non-absolute path denied (base-path active)");
+                       return NULL;
+               }
+               snprintf(rpath, PATH_MAX, "%s%s", base_path, dir);
+               dir = rpath;
+       }
+
+       path = enter_repo(dir, strict_paths);
 
        if (!path) {
                logerror("'%s': unable to chdir or not a git archive", dir);
@@ -92,25 +162,23 @@ static char *path_ok(char *dir)
        }
 
        if ( ok_paths && *ok_paths ) {
-               char **pp = NULL;
-               int dirlen = strlen(dir);
+               char **pp;
                int pathlen = strlen(path);
 
+               /* The validation is done on the paths after enter_repo
+                * appends optional {.git,.git/.git} and friends, but 
+                * it does not use getcwd().  So if your /pub is
+                * a symlink to /mnt/pub, you can whitelist /pub and
+                * do not have to say /mnt/pub.
+                * Do not say /pub/.
+                */
                for ( pp = ok_paths ; *pp ; pp++ ) {
                        int len = strlen(*pp);
-                       /* because of symlinks we must match both what the
-                        * user passed and the canonicalized path, otherwise
-                        * the user can send a string matching either a whitelist
-                        * entry or an actual directory exactly and still not
-                        * get through */
-                       if (len <= pathlen && !memcmp(*pp, path, len)) {
-                               if (path[len] == '\0' || (!strict_paths && path[len] == '/'))
-                                       return path;
-                       }
-                       if (len <= dirlen && !memcmp(*pp, dir, len)) {
-                               if (dir[len] == '\0' || (!strict_paths && dir[len] == '/'))
-                                       return path;
-                       }
+                       if (len <= pathlen &&
+                           !memcmp(*pp, path, len) &&
+                           (path[len] == '\0' ||
+                            (!strict_paths && path[len] == '/')))
+                               return path;
                }
        }
        else {
@@ -160,7 +228,7 @@ static int upload(char *dir)
        snprintf(timeout_buf, sizeof timeout_buf, "--timeout=%u", timeout);
 
        /* git-upload-pack only ever reads stuff, so this is safe */
-       execlp("git-upload-pack", "git-upload-pack", "--strict", timeout_buf, path, NULL);
+       execl_git_cmd("upload-pack", "--strict", timeout_buf, ".", NULL);
        return -1;
 }
 
@@ -470,8 +538,14 @@ static int socksetup(int port, int **socklist_p)
                return 0;
        }
 
+       if (listen(sockfd, 5) < 0) {
+               close(sockfd);
+               return 0;
+       }
+
        *socklist_p = xmalloc(sizeof(int));
        **socklist_p = sockfd;
+       return 1;
 }
 
 #endif
@@ -581,6 +655,10 @@ int main(int argc, char **argv)
                        strict_paths = 1;
                        continue;
                }
+               if (!strncmp(arg, "--base-path=", 12)) {
+                       base_path = arg+12;
+                       continue;
+               }
                if (!strcmp(arg, "--")) {
                        ok_paths = &argv[i+1];
                        break;