Tighten refspec processing
[gitweb.git] / daemon.c
index ad8492873ea1db63d56c24bec6174c4682105433..2b4a6f145cc0f37c95da31df23dfde3ba7fa63a7 100644 (file)
--- a/daemon.c
+++ b/daemon.c
@@ -1,24 +1,18 @@
-#include <signal.h>
-#include <sys/wait.h>
-#include <sys/socket.h>
-#include <sys/time.h>
-#include <sys/poll.h>
-#include <netdb.h>
-#include <netinet/in.h>
-#include <arpa/inet.h>
-#include <syslog.h>
-#include <pwd.h>
-#include <grp.h>
-#include <limits.h>
-#include "pkt-line.h"
 #include "cache.h"
+#include "pkt-line.h"
 #include "exec_cmd.h"
 #include "interpolate.h"
 
+#include <syslog.h>
+
 #ifndef HOST_NAME_MAX
 #define HOST_NAME_MAX 256
 #endif
 
+#ifndef NI_MAXSERV
+#define NI_MAXSERV 32
+#endif
+
 static int log_syslog;
 static int verbose;
 static int reuseaddr;
@@ -26,7 +20,8 @@ static int reuseaddr;
 static const char daemon_usage[] =
 "git-daemon [--verbose] [--syslog] [--export-all]\n"
 "           [--timeout=n] [--init-timeout=n] [--strict-paths]\n"
-"           [--base-path=path] [--user-path | --user-path=path]\n"
+"           [--base-path=path] [--base-path-relaxed]\n"
+"           [--user-path | --user-path=path]\n"
 "           [--interpolated-path=path]\n"
 "           [--reuseaddr] [--detach] [--pid-file=file]\n"
 "           [--[enable|disable|allow-override|forbid-override]=service]\n"
@@ -44,6 +39,7 @@ static int export_all_trees;
 /* Take all paths relative to this one if non-NULL */
 static char *base_path;
 static char *interpolated_path;
+static int base_path_relaxed;
 
 /* Flag indicating client sent extra args. */
 static int saw_extended_args;
@@ -112,7 +108,7 @@ static void logreport(int priority, const char *err, va_list params)
        buf[buflen++] = '\n';
        buf[buflen] = '\0';
 
-       write(2, buf, buflen);
+       write_in_full(2, buf, buflen);
 }
 
 static void logerror(const char *err, ...)
@@ -143,7 +139,7 @@ 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.
@@ -190,6 +186,7 @@ static char *path_ok(struct interp *itable)
 {
        static char rpath[PATH_MAX];
        static char interp_path[PATH_MAX];
+       int retried_path = 0;
        char *path;
        char *dir;
 
@@ -245,7 +242,22 @@ static char *path_ok(struct interp *itable)
                dir = rpath;
        }
 
-       path = enter_repo(dir, strict_paths);
+       do {
+               path = enter_repo(dir, strict_paths);
+               if (path)
+                       break;
+
+               /*
+                * if we fail and base_path_relaxed is enabled, try without
+                * prefixing the base path
+                */
+               if (base_path && base_path_relaxed && !retried_path) {
+                       dir = itable[INTERP_SLOT_DIR].value;
+                       retried_path = 1;
+                       continue;
+               }
+               break;
+       } while (1);
 
        if (!path) {
                logerror("'%s': unable to chdir or not a git archive", dir);
@@ -257,7 +269,7 @@ static char *path_ok(struct interp *itable)
                int pathlen = strlen(path);
 
                /* The validation is done on the paths after enter_repo
-                * appends optional {.git,.git/.git} and friends, but 
+                * 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.
@@ -296,7 +308,7 @@ static int service_enabled;
 
 static int git_daemon_config(const char *var, const char *value)
 {
-       if (!strncmp(var, "daemon.", 7) &&
+       if (!prefixcmp(var, "daemon.") &&
            !strcmp(var + 7, service_looking_at->config_name)) {
                service_enabled = git_config_bool(var, value);
                return 0;
@@ -382,12 +394,20 @@ static int upload_archive(void)
        return -1;
 }
 
+static int receive_pack(void)
+{
+       execl_git_cmd("receive-pack", ".", NULL);
+       return -1;
+}
+
 static struct daemon_service daemon_service[] = {
        { "upload-archive", "uploadarch", upload_archive, 0, 1 },
        { "upload-pack", "uploadpack", upload_pack, 1, 1 },
+       { "receive-pack", "receivepack", receive_pack, 0, 1 },
 };
 
-static void enable_service(const char *name, int ena) {
+static void enable_service(const char *name, int ena)
+{
        int i;
        for (i = 0; i < ARRAY_SIZE(daemon_service); i++) {
                if (!strcmp(daemon_service[i].name, name)) {
@@ -398,7 +418,8 @@ static void enable_service(const char *name, int ena) {
        die("No such service %s", name);
 }
 
-static void make_service_overridable(const char *name, int ena) {
+static void make_service_overridable(const char *name, int ena)
+{
        int i;
        for (i = 0; i < ARRAY_SIZE(daemon_service); i++) {
                if (!strcmp(daemon_service[i].name, name)) {
@@ -411,7 +432,7 @@ static void make_service_overridable(const char *name, int ena) {
 
 /*
  * Separate the "extra args" information as supplied by the client connection.
- * Any resulting data is squirrelled away in the given interpolation table.
+ * Any resulting data is squirreled away in the given interpolation table.
  */
 static void parse_extra_args(struct interp *table, char *extra_args, int buflen)
 {
@@ -442,7 +463,7 @@ static void parse_extra_args(struct interp *table, char *extra_args, int buflen)
        }
 }
 
-void fill_in_extra_table_entries(struct interp *itable)
+static void fill_in_extra_table_entries(struct interp *itable)
 {
        char *hp;
 
@@ -450,6 +471,8 @@ void fill_in_extra_table_entries(struct interp *itable)
         * Replace literal host with lowercase-ized hostname.
         */
        hp = interp_table[INTERP_SLOT_HOST].value;
+       if (!hp)
+               return;
        for ( ; *hp; hp++)
                *hp = tolower(*hp);
 
@@ -519,7 +542,7 @@ static int execute(struct sockaddr *addr)
                if (addr->sa_family == AF_INET) {
                        struct sockaddr_in *sin_addr = (void *) addr;
                        inet_ntop(addr->sa_family, &sin_addr->sin_addr, addrbuf, sizeof(addrbuf));
-                       port = sin_addr->sin_port;
+                       port = ntohs(sin_addr->sin_port);
 #ifndef NO_IPV6
                } else if (addr && addr->sa_family == AF_INET6) {
                        struct sockaddr_in6 *sin6_addr = (void *) addr;
@@ -529,7 +552,7 @@ static int execute(struct sockaddr *addr)
                        inet_ntop(AF_INET6, &sin6_addr->sin6_addr, buf, sizeof(addrbuf) - 1);
                        strcat(buf, "]");
 
-                       port = sin6_addr->sin6_port;
+                       port = ntohs(sin6_addr->sin6_port);
 #endif
                }
                loginfo("Connection from %s:%d", addrbuf, port);
@@ -544,8 +567,10 @@ static int execute(struct sockaddr *addr)
                loginfo("Extended attributes (%d bytes) exist <%.*s>",
                        (int) pktlen - len,
                        (int) pktlen - len, line + len + 1);
-       if (len && line[len-1] == '\n')
+       if (len && line[len-1] == '\n') {
                line[--len] = 0;
+               pktlen--;
+       }
 
        /*
         * Initialize the path interpolation table for this connection.
@@ -561,7 +586,7 @@ static int execute(struct sockaddr *addr)
        for (i = 0; i < ARRAY_SIZE(daemon_service); i++) {
                struct daemon_service *s = &(daemon_service[i]);
                int namelen = strlen(s->name);
-               if (!strncmp("git-", line, 4) &&
+               if (!prefixcmp(line, "git-") &&
                    !strncmp(s->name, line + 4, namelen) &&
                    line[namelen + 4] == ' ') {
                        /*
@@ -772,6 +797,7 @@ static int socksetup(char *listen_addr, int listen_port, int **socklist_p)
        char pbuf[NI_MAXSERV];
        struct addrinfo hints, *ai0, *ai;
        int gai;
+       long flags;
 
        sprintf(pbuf, "%d", listen_port);
        memset(&hints, 0, sizeof(hints));
@@ -819,6 +845,10 @@ static int socksetup(char *listen_addr, int listen_port, int **socklist_p)
                        continue;       /* not fatal */
                }
 
+               flags = fcntl(sockfd, F_GETFD, 0);
+               if (flags >= 0)
+                       fcntl(sockfd, F_SETFD, flags | FD_CLOEXEC);
+
                socklist = xrealloc(socklist, sizeof(int) * (socknum + 1));
                socklist[socknum++] = sockfd;
 
@@ -838,6 +868,7 @@ static int socksetup(char *listen_addr, int listen_port, int **socklist_p)
 {
        struct sockaddr_in sin;
        int sockfd;
+       long flags;
 
        memset(&sin, 0, sizeof sin);
        sin.sin_family = AF_INET;
@@ -870,6 +901,10 @@ static int socksetup(char *listen_addr, int listen_port, int **socklist_p)
                return 0;
        }
 
+       flags = fcntl(sockfd, F_GETFD, 0);
+       if (flags >= 0)
+               fcntl(sockfd, F_SETFD, flags | FD_CLOEXEC);
+
        *socklist_p = xmalloc(sizeof(int));
        **socklist_p = sockfd;
        return 1;
@@ -959,8 +994,8 @@ static void store_pid(const char *path)
        FILE *f = fopen(path, "w");
        if (!f)
                die("cannot open pid file %s: %s", path, strerror(errno));
-       fprintf(f, "%d\n", getpid());
-       fclose(f);
+       if (fprintf(f, "%d\n", getpid()) < 0 || fclose(f) != 0)
+               die("failed to write pid file %s: %s", path, strerror(errno));
 }
 
 static int serve(char *listen_addr, int listen_port, struct passwd *pass, gid_t gid)
@@ -1000,7 +1035,7 @@ int main(int argc, char **argv)
        for (i = 1; i < argc; i++) {
                char *arg = argv[i];
 
-               if (!strncmp(arg, "--listen=", 9)) {
+               if (!prefixcmp(arg, "--listen=")) {
                    char *p = arg + 9;
                    char *ph = listen_addr = xmalloc(strlen(arg + 9) + 1);
                    while (*p)
@@ -1008,7 +1043,7 @@ int main(int argc, char **argv)
                    *ph = 0;
                    continue;
                }
-               if (!strncmp(arg, "--port=", 7)) {
+               if (!prefixcmp(arg, "--port=")) {
                        char *end;
                        unsigned long n;
                        n = strtoul(arg+7, &end, 0);
@@ -1034,11 +1069,11 @@ int main(int argc, char **argv)
                        export_all_trees = 1;
                        continue;
                }
-               if (!strncmp(arg, "--timeout=", 10)) {
+               if (!prefixcmp(arg, "--timeout=")) {
                        timeout = atoi(arg+10);
                        continue;
                }
-               if (!strncmp(arg, "--init-timeout=", 15)) {
+               if (!prefixcmp(arg, "--init-timeout=")) {
                        init_timeout = atoi(arg+15);
                        continue;
                }
@@ -1046,11 +1081,15 @@ int main(int argc, char **argv)
                        strict_paths = 1;
                        continue;
                }
-               if (!strncmp(arg, "--base-path=", 12)) {
+               if (!prefixcmp(arg, "--base-path=")) {
                        base_path = arg+12;
                        continue;
                }
-               if (!strncmp(arg, "--interpolated-path=", 20)) {
+               if (!strcmp(arg, "--base-path-relaxed")) {
+                       base_path_relaxed = 1;
+                       continue;
+               }
+               if (!prefixcmp(arg, "--interpolated-path=")) {
                        interpolated_path = arg+20;
                        continue;
                }
@@ -1062,11 +1101,11 @@ int main(int argc, char **argv)
                        user_path = "";
                        continue;
                }
-               if (!strncmp(arg, "--user-path=", 12)) {
+               if (!prefixcmp(arg, "--user-path=")) {
                        user_path = arg + 12;
                        continue;
                }
-               if (!strncmp(arg, "--pid-file=", 11)) {
+               if (!prefixcmp(arg, "--pid-file=")) {
                        pid_file = arg + 11;
                        continue;
                }
@@ -1075,27 +1114,27 @@ int main(int argc, char **argv)
                        log_syslog = 1;
                        continue;
                }
-               if (!strncmp(arg, "--user=", 7)) {
+               if (!prefixcmp(arg, "--user=")) {
                        user_name = arg + 7;
                        continue;
                }
-               if (!strncmp(arg, "--group=", 8)) {
+               if (!prefixcmp(arg, "--group=")) {
                        group_name = arg + 8;
                        continue;
                }
-               if (!strncmp(arg, "--enable=", 9)) {
+               if (!prefixcmp(arg, "--enable=")) {
                        enable_service(arg + 9, 1);
                        continue;
                }
-               if (!strncmp(arg, "--disable=", 10)) {
+               if (!prefixcmp(arg, "--disable=")) {
                        enable_service(arg + 10, 0);
                        continue;
                }
-               if (!strncmp(arg, "--allow-override=", 17)) {
+               if (!prefixcmp(arg, "--allow-override=")) {
                        make_service_overridable(arg + 17, 1);
                        continue;
                }
-               if (!strncmp(arg, "--forbid-override=", 18)) {
+               if (!prefixcmp(arg, "--forbid-override=")) {
                        make_service_overridable(arg + 18, 0);
                        continue;
                }
@@ -1110,6 +1149,11 @@ int main(int argc, char **argv)
                usage(daemon_usage);
        }
 
+       if (log_syslog) {
+               openlog("git-daemon", 0, LOG_DAEMON);
+               set_die_routine(daemon_die);
+       }
+
        if (inetd_mode && (group_name || user_name))
                die("--user and --group are incompatible with --inetd");
 
@@ -1137,14 +1181,17 @@ int main(int argc, char **argv)
                }
        }
 
-       if (log_syslog) {
-               openlog("git-daemon", 0, LOG_DAEMON);
-               set_die_routine(daemon_die);
-       }
-
        if (strict_paths && (!ok_paths || !*ok_paths))
                die("option --strict-paths requires a whitelist");
 
+       if (base_path) {
+               struct stat st;
+
+               if (stat(base_path, &st) || !S_ISDIR(st.st_mode))
+                       die("base-path '%s' does not exist or "
+                           "is not a directory", base_path);
+       }
+
        if (inetd_mode) {
                struct sockaddr_storage ss;
                struct sockaddr *peer = (struct sockaddr *)&ss;