commit - c3d3a336ffa1b625a5ddcff77bd6aaec86638279
commit + dcb89595705170326a44de80a246452329cfc0a1
blob - beabb467f8b0a218c66f84aa75b3a377276a55c2
blob + 30774393e8a5bd14695eeda37cf4105e6c11f907
--- gotwebd/fcgi.c
+++ gotwebd/fcgi.c
q++;
}
*q = '\0';
+
+ return NULL;
+}
+
+static const struct got_error *
+validate_path(const char *path)
+{
+ size_t len = strlen(path);
+
+ if (len >= 3 && strncmp(path, "../", 3) == 0)
+ return got_error(GOT_ERR_BAD_QUERYSTRING);
+
+ if (len >= 4 && strstr(path, "/../") != NULL)
+ return got_error(GOT_ERR_BAD_QUERYSTRING);
+
+ if (len >= 3 && strcmp(path + len - 3, "/..") == 0)
+ return got_error(GOT_ERR_BAD_QUERYSTRING);
return NULL;
+
}
static const struct got_error *
}
break;
case RFILE:
+ error = validate_path(value);
+ if (error)
+ goto done;
if (strlcpy(qs->file, value, sizeof(qs->file)) >=
sizeof(qs->file)) {
error = got_error_msg(GOT_ERR_NO_SPACE,
}
break;
case FOLDER:
+ error = validate_path(value);
+ if (error)
+ goto done;
if (strlcpy(qs->folder, value[0] ? value : "/",
sizeof(qs->folder)) >= sizeof(qs->folder)) {
error = got_error_msg(GOT_ERR_NO_SPACE,
}
break;
case HEADREF:
+ error = validate_path(value);
+ if (error)
+ goto done;
if (strlcpy(qs->headref, value, sizeof(qs->headref)) >=
sizeof(qs->headref)) {
error = got_error_msg(GOT_ERR_NO_SPACE,
}
break;
case PATH:
+ error = validate_path(value);
+ if (error)
+ goto done;
if (strlcpy(qs->path, value, sizeof(qs->path)) >=
sizeof(qs->path)) {
error = got_error_msg(GOT_ERR_NO_SPACE,
blob - cdd5b253e75c190fd7f3c710e9eadeefea473c8a
blob + d4eecc1c2d5aecc314dd3ab30ca5cf784947b3fc
--- gotwebd/gotweb.c
+++ gotwebd/gotweb.c
return gotweb_render_url(c, url);
}
+/*
+ * Ensure that a path sent in the request will not escape from the given
+ * parent directory. This matters for got-portable where we are not
+ * necessarily running in chroot and cannot be protected by unveil(2).
+ */
static const struct got_error *
+validate_path(const char *path, const char *parent_path,
+ const char *orig_path)
+{
+ const struct got_error *error = NULL;
+ char *abspath;
+
+ abspath = realpath(path, NULL);
+ if (abspath == NULL) {
+ /* Paths which cannot be resolved are safe. */
+ if (errno == ENOENT || errno == EACCES || errno == ENOTDIR)
+ return NULL;
+ return got_error_from_errno("realpath");
+ }
+
+ if (!got_path_is_child(abspath, parent_path, strlen(parent_path)))
+ error = got_error_path(orig_path, GOT_ERR_NOT_GIT_REPO);
+
+ free(abspath);
+ return error;
+}
+
+static const struct got_error *
gotweb_load_got_path(struct repo_dir **rp, const char *dir,
struct request *c)
{
GOTWEB_GIT_DIR) == -1)
return got_error_from_errno("asprintf");
+ error = validate_path(dir_test, srv->repos_path, dir);
+ if (error) {
+ free(dir_test);
+ return error;
+ }
+
dt = opendir(dir_test);
if (dt == NULL) {
free(dir_test);