commit - adbe6a6493c0e91fcfc918db8f4b5839a2867b1c
commit + fdea6aa0bca24f6f947e2126ce101fd59caa7a31
blob - fd9c39132abca70abee5a57a670e913e6b831021
blob + f6c4288ba7c6a77e42712f451cc92f9dc06a02dd
--- ChangeLog
+++ ChangeLog
+2021-04-30 Omar Polo <op@omarpolo.com>
+
+ * gmid.c (load_vhosts): allow ``root'' rule to be specified per-location block
+
2021-04-29 Omar Polo <op@omarpolo.com>
* parse.y (servopt): added ``alias'' option to define hostname aliases for a server
blob - 6437a4233765bbddba92e87c77363406bc53bd58
blob + 6a5effe3c8d324d6ae4d3ef0af562482f5c53a95
--- ex.c
+++ ex.c
/* fd or -1 on error */
static int
-launch_cgi(struct iri *iri, struct cgireq *req, struct vhost *vhost)
+launch_cgi(struct iri *iri, struct cgireq *req, struct vhost *vhost,
+ struct location *loc)
{
int p[2]; /* read end, write end */
if (dup2(p[1], 1) == -1)
goto childerr;
- ex = xasprintf("%s/%s", vhost->dir, req->spath);
+ ex = xasprintf("%s/%s", loc->dir, req->spath);
serialize_iri(iri, iribuf, sizeof(iribuf));
safe_setenv("GATEWAY_INTERFACE", "CGI/1.1");
- safe_setenv("GEMINI_DOCUMENT_ROOT", vhost->dir);
+ safe_setenv("GEMINI_DOCUMENT_ROOT", loc->dir);
safe_setenv("GEMINI_SCRIPT_FILENAME",
- xasprintf("%s/%s", vhost->dir, req->spath));
+ xasprintf("%s/%s", loc->dir, req->spath));
safe_setenv("GEMINI_URL", iribuf);
strlcpy(path, "/", sizeof(path));
strlcat(path, req->relpath, sizeof(path));
safe_setenv("PATH_INFO", path);
- strlcpy(path, vhost->dir, sizeof(path));
+ strlcpy(path, loc->dir, sizeof(path));
strlcat(path, "/", sizeof(path));
strlcat(path, req->relpath, sizeof(path));
safe_setenv("PATH_TRANSLATED", path);
return NULL;
}
+static struct location *
+loc_nth(struct vhost *vhost, size_t n)
+{
+ struct location *loc;
+
+ TAILQ_FOREACH(loc, &vhost->locations, locations) {
+ if (n == 0)
+ return loc;
+ n--;
+ }
+
+ return NULL;
+}
+
static void
handle_imsg_cgi_req(struct imsgbuf *ibuf, struct imsg *imsg, size_t datalen)
{
struct vhost *h;
+ struct location *l;
struct cgireq req;
struct iri iri;
int fd;
if ((h = host_nth(req.host_off)) == NULL)
abort();
- fd = launch_cgi(&iri, &req, h);
+ if ((l = loc_nth(h, req.host_off)) == NULL)
+ abort();
+
+ fd = launch_cgi(&iri, &req, h, l);
imsg_compose(ibuf, IMSG_CGI_RES, imsg->hdr.peerid, 0, fd, NULL, 0);
imsg_flush(ibuf);
}
blob - d28bae1cef3eb44cd32e6ebe6c7f5ad4d770efc4
blob + 852ee90c2bea8fd0efa3eca8a7faf0dc9b7befa7
--- gmid.1
+++ gmid.1
.Ic location
section may include most of the server configuration rules
except
-.Ic alias , Ic cert , Ic env , Ic key , Ic root , Ic location ,
+.Ic alias , Ic cert , Ic env , Ic key , Ic location ,
.Ic entrypoint No and Ic cgi .
.It Ic root Pa directory
Specify the root directory for this server.
blob - 0af53f3f5eb37e5466caec6042d9b2127bd9e760
blob + dda03832b0589c754d1d907b11520e5e6167eae1
--- gmid.c
+++ gmid.c
void
load_vhosts(void)
{
- struct vhost *h;
+ struct vhost *h;
+ struct location *l;
TAILQ_FOREACH(h, &hosts, vhosts) {
- if ((h->dirfd = open(h->dir, O_RDONLY | O_DIRECTORY)) == -1)
- fatal("open %s for domain %s", h->dir, h->domain);
+ TAILQ_FOREACH(l, &h->locations, locations) {
+ if (l->dir == NULL)
+ continue;
+ if ((l->dirfd = open(l->dir, O_RDONLY | O_DIRECTORY)) == -1)
+ fatal("open %s for domain %s", l->dir, h->domain);
+ }
}
}
free((char*)l->default_mime);
free((char*)l->index);
free((char*)l->block_fmt);
+ free((char*)l->dir);
+
+ if (l->dirfd != -1)
+ close(l->dirfd);
+
free(l);
}
free(a);
}
+ free((char*)h->domain);
+ free((char*)h->cert);
+ free((char*)h->key);
+ free((char*)h->cgi);
+ free((char*)h->entrypoint);
+
TAILQ_REMOVE(&hosts, h, vhosts);
free(h);
}
switch (argc) {
case 0:
- h->dir = getcwd(path, sizeof(path));
+ l->dir = getcwd(path, sizeof(path));
break;
case 1:
- h->dir = absolutify_path(argv[0]);
+ l->dir = absolutify_path(argv[0]);
break;
default:
usage(getprogname());
return 1;
}
- log_notice(NULL, "serving %s on port %d", h->dir, conf.port);
+ log_notice(NULL, "serving %s on port %d", l->dir, conf.port);
}
/* setup tls before dropping privileges: we don't want user
blob - 5eec8af079d3151b5a57d86bb012bcc24acafeed
blob + 818f8d76d76bb58230bf2f5747cface67fd28203
--- gmid.h
+++ gmid.h
int strip;
X509_STORE *reqca;
int disable_log;
+
+ const char *dir;
+ int dirfd;
TAILQ_ENTRY(location) locations;
};
const char *domain;
const char *cert;
const char *key;
- const char *dir;
const char *cgi;
const char *entrypoint;
- int dirfd;
TAILQ_ENTRY(vhost) vhosts;
time_t notafter;
size_t host_off;
+ size_t loc_off;
};
enum {
blob - 59e1a6c42461c4968a2ab87b961ae7926c89839b
blob + c93e471d26dda69c44b91a928567c124bd8b0541
--- parse.y
+++ parse.y
}
} '{' servopts locations '}' {
- if (host->cert == NULL || host->key == NULL ||
- host->dir == NULL)
+ if (host->cert == NULL || host->key == NULL)
yyerror("invalid vhost definition: %s", $2);
}
| error '}' { yyerror("error in server directive"); }
TAILQ_INSERT_TAIL(&host->env, e, envs);
}
| TKEY TSTRING { host->key = ensure_absolute_path($2); }
- | TROOT TSTRING { host->dir = ensure_absolute_path($2); }
| locopt
;
if ((loc->reqca = load_ca($4)) == NULL)
yyerror("couldn't load ca cert: %s", $4);
free($4);
+ }
+ | TROOT TSTRING {
+ if (loc->dir != NULL)
+ yyerror("`root' specified more than once");
+
+ loc->dir = ensure_absolute_path($2);
}
| TSTRIP TNUM { loc->strip = check_strip_no($2); }
;
static struct location *
new_location(void)
{
- return xcalloc(1, sizeof(struct location));
+ struct location *l;
+
+ l = xcalloc(1, sizeof(*l));
+ l->dirfd = -1;
+ return l;
}
void
blob - d24ac08e71e386d883506b38dcb1d58d510c0a4f
blob + 53f154981a51d0c6f424563b4272a62d4410f025
--- regress/runtime
+++ regress/runtime
echo OK GET / with invalid client certificate
ggflags=''
+
+
+# test with root inside a location
+
+config '' 'location "/foo/*" { root "'$PWD'/testdata" strip 1 }'
+checkconf
+restart
+
+# XXX: this fails, because /foo isn't matched by /foo/*, but it would
+# be nice if it worked.
+#eq "$(head /foo)" "30 /foo/" "Unexpected head for /foo"
+
+eq "$(head /foo/)" "20 text/gemini" "Unexpected head for /foo/"
blob - b689a27ba8a2ae4af33d3c87b4955370e27940c8
blob + 4e107392d2ac379331c4a93820e852aa076e3d64
--- sandbox.c
+++ sandbox.c
void
sandbox_server_process(void)
{
- struct vhost *h;
+ struct vhost *h;
+ struct location *l;
TAILQ_FOREACH(h, &hosts, vhosts) {
- if (unveil(h->dir, "r") == -1)
- fatal("unveil %s for domain %s", h->dir, h->domain);
+ TAILQ_FOREACH(l, &h->locations, locations) {
+ if (l->dir == NULL)
+ continue;
+
+ if (unveil(l->dir, "r") == -1)
+ fatal("unveil %s for domain %s",
+ l->dir,
+ h->domain);
+ }
}
if (pledge("stdio recvfd rpath inet", NULL) == -1)
sandbox_executor_process(void)
{
struct vhost *h;
+ struct location *l;
TAILQ_FOREACH(h, &hosts, vhosts) {
- /* r so we can chdir into the correct directory */
- if (unveil(h->dir, "rx") == -1)
- err(1, "unveil %s for domain %s",
- h->dir, h->domain);
+ TAILQ_FOREACH(l, &h->locations, locations) {
+ if (l->dir == NULL)
+ continue;
+
+ /* r so we can chdir into the correct directory */
+ if (unveil(l->dir, "rx") == -1)
+ fatal("unveil %s for domain %s",
+ l->dir, h->domain);
+ }
}
/* rpath to chdir into the correct directory */
blob - 86c8d0886707548054ba07f1a8004df3328bcee7
blob + 60d1da89e8af270710e78b6adaf856ed25f9a56f
--- server.c
+++ server.c
}
int
+vhost_dirfd(struct vhost *v, const char *path)
+{
+ struct location *loc;
+
+ if (v == NULL || path == NULL)
+ return -1;
+
+ loc = TAILQ_FIRST(&v->locations);
+ while ((loc = TAILQ_NEXT(loc, locations)) != NULL) {
+ if (loc->dirfd != -1)
+ if (matches(loc->match, path))
+ return loc->dirfd;
+ }
+
+ loc = TAILQ_FIRST(&v->locations);
+ return loc->dirfd;
+}
+
+int
vhost_strip(struct vhost *v, const char *path)
{
struct location *loc;
{
struct stat sb;
const char *p;
- int flags;
+ int flags, dirfd, strip;
assert(path != NULL);
- if (*path == '\0')
+ /*
+ * in send_dir we add an initial / (to be redirect-friendly),
+ * but here we want to skip it
+ */
+ if (*path == '/')
+ path++;
+
+ strip = vhost_strip(c->host, path);
+ p = strip_path(path, strip);
+
+ if (*p == '/')
+ p = p+1;
+ if (*p == '\0')
p = ".";
- else if (*path == '/')
- /* in send_dir we add an initial / (to be
- * redirect-friendly), but here we want to skip it */
- p = path+1;
- else
- p = path;
+ dirfd = vhost_dirfd(c->host, path);
+ log_debug(c, "check_path: strip=%d path=%s original=%s",
+ strip, p, path);
flags = O_RDONLY | O_NOFOLLOW;
-
- if (*fd == -1 && (*fd = openat(c->host->dirfd, p, flags)) == -1)
+ if (*fd == -1 && (*fd = openat(dirfd, p, flags)) == -1)
return FILE_MISSING;
if (fstat(*fd, &sb) == -1) {