#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;
/* 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;
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);
}
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 {
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;
}
return 0;
}
+ if (listen(sockfd, 5) < 0) {
+ close(sockfd);
+ return 0;
+ }
+
*socklist_p = xmalloc(sizeof(int));
**socklist_p = sockfd;
+ return 1;
}
#endif
strict_paths = 1;
continue;
}
+ if (!strncmp(arg, "--base-path=", 12)) {
+ base_path = arg+12;
+ continue;
+ }
if (!strcmp(arg, "--")) {
ok_paths = &argv[i+1];
break;