Merge branch 'jk/daemon-path-ok-check-truncation' into maint
authorJunio C Hamano <gitster@pobox.com>
Tue, 29 Nov 2016 21:27:55 +0000 (13:27 -0800)
committerJunio C Hamano <gitster@pobox.com>
Tue, 29 Nov 2016 21:27:56 +0000 (13:27 -0800)
"git daemon" used fixed-length buffers to turn URL to the
repository the client asked for into the server side directory
path, using snprintf() to avoid overflowing these buffers, but
allowed possibly truncated paths to the directory. This has been
tightened to reject such a request that causes overlong path to be
required to serve.

* jk/daemon-path-ok-check-truncation:
daemon: detect and reject too-long paths

1  2 
daemon.c
diff --combined daemon.c
index 425aad0507f48ca07b11faa05828ea841bdd302f,72f6ad3c0d5faccfb845f7181331e621454376da..ff0fa583b0497d886c54ff93d97cc45fd909e7a5
+++ b/daemon.c
@@@ -1,5 -1,6 +1,5 @@@
  #include "cache.h"
  #include "pkt-line.h"
 -#include "exec_cmd.h"
  #include "run-command.h"
  #include "strbuf.h"
  #include "string-list.h"
@@@ -31,7 -32,7 +31,7 @@@ static const char daemon_usage[] 
  "           [<directory>...]";
  
  /* List of acceptable pathname prefixes */
 -static char **ok_paths;
 +static const char **ok_paths;
  static int strict_paths;
  
  /* If this is set, git-daemon-export-ok is not required */
@@@ -160,6 -161,7 +160,7 @@@ static const char *path_ok(const char *
  {
        static char rpath[PATH_MAX];
        static char interp_path[PATH_MAX];
+       size_t rlen;
        const char *path;
        const char *dir;
  
                        namlen = slash - dir;
                        restlen -= namlen;
                        loginfo("userpath <%s>, request <%s>, namlen %d, restlen %d, slash <%s>", user_path, dir, namlen, restlen, slash);
-                       snprintf(rpath, PATH_MAX, "%.*s/%s%.*s",
-                                namlen, dir, user_path, restlen, slash);
+                       rlen = snprintf(rpath, sizeof(rpath), "%.*s/%s%.*s",
+                                       namlen, dir, user_path, restlen, slash);
+                       if (rlen >= sizeof(rpath)) {
+                               logerror("user-path too large: %s", rpath);
+                               return NULL;
+                       }
                        dir = rpath;
                }
        }
  
                strbuf_expand(&expanded_path, interpolated_path,
                              expand_path, &context);
-               strlcpy(interp_path, expanded_path.buf, PATH_MAX);
+               rlen = strlcpy(interp_path, expanded_path.buf,
+                              sizeof(interp_path));
+               if (rlen >= sizeof(interp_path)) {
+                       logerror("interpolated path too large: %s",
+                                interp_path);
+                       return NULL;
+               }
                strbuf_release(&expanded_path);
                loginfo("Interpolated dir '%s'", interp_path);
  
                        logerror("'%s': Non-absolute path denied (base-path active)", dir);
                        return NULL;
                }
-               snprintf(rpath, PATH_MAX, "%s%s", base_path, dir);
+               rlen = snprintf(rpath, sizeof(rpath), "%s%s", base_path, dir);
+               if (rlen >= sizeof(rpath)) {
+                       logerror("base-path too large: %s", rpath);
+                       return NULL;
+               }
                dir = rpath;
        }
  
        }
  
        if ( ok_paths && *ok_paths ) {
 -              char **pp;
 +              const char **pp;
                int pathlen = strlen(path);
  
                /* The validation is done on the paths after enter_repo
@@@ -668,17 -686,6 +685,17 @@@ static void hostinfo_clear(struct hosti
        strbuf_release(&hi->tcp_port);
  }
  
 +static void set_keep_alive(int sockfd)
 +{
 +      int ka = 1;
 +
 +      if (setsockopt(sockfd, SOL_SOCKET, SO_KEEPALIVE, &ka, sizeof(ka)) < 0) {
 +              if (errno != ENOTSOCK)
 +                      logerror("unable to set SO_KEEPALIVE on socket: %s",
 +                              strerror(errno));
 +      }
 +}
 +
  static int execute(void)
  {
        char *line = packet_buffer;
        if (addr)
                loginfo("Connection from %s:%s", addr, port);
  
 +      set_keep_alive(0);
        alarm(init_timeout ? init_timeout : timeout);
        pktlen = packet_read(0, NULL, NULL, packet_buffer, sizeof(packet_buffer), 0);
        alarm(0);
@@@ -962,8 -968,6 +979,8 @@@ static int setup_named_sock(char *liste
                        continue;
                }
  
 +              set_keep_alive(sockfd);
 +
                if (bind(sockfd, ai->ai_addr, ai->ai_addrlen) < 0) {
                        logerror("Could not bind to %s: %s",
                                 ip2str(ai->ai_family, ai->ai_addr, ai->ai_addrlen),
@@@ -1023,8 -1027,6 +1040,8 @@@ static int setup_named_sock(char *liste
                return 0;
        }
  
 +      set_keep_alive(sockfd);
 +
        if ( bind(sockfd, (struct sockaddr *)&sin, sizeof sin) < 0 ) {
                logerror("Could not bind to %s: %s",
                         ip2str(AF_INET, (struct sockaddr *)&sin, sizeof(sin)),
@@@ -1193,7 -1195,7 +1210,7 @@@ static int serve(struct string_list *li
        return service_loop(&socklist);
  }
  
 -int main(int argc, char **argv)
 +int cmd_main(int argc, const char **argv)
  {
        int listen_port = 0;
        struct string_list listen_addr = STRING_LIST_INIT_NODUP;
        struct credentials *cred = NULL;
        int i;
  
 -      git_setup_gettext();
 -
 -      git_extract_argv0_path(argv[0]);
 -
        for (i = 1; i < argc; i++) {
 -              char *arg = argv[i];
 +              const char *arg = argv[i];
                const char *v;
  
                if (skip_prefix(arg, "--listen=", &v)) {
        if (detach) {
                if (daemonize())
                        die("--detach not supported on this platform");
 -      } else
 -              sanitize_stdfds();
 +      }
  
        if (pid_file)
                write_file(pid_file, "%"PRIuMAX, (uintmax_t) getpid());