commit a291bc8c99309fcbfd1a7688988ba60a2e5bf042
parent b12a77acd24fc170b1ad047986ffaf13592fb326
Author: Christoph Lohmann <20h@r-36.net>
Date: Sat, 22 Jul 2023 15:28:45 +0200
Fix traversal handling, add selector and traversal to CGI environment.
* Add raw selector to CGI scripts.
* Add traversal to CGI scripts.
* Add both to manpages.
Diffstat:
M | geomyidae.8 | | | 9 | ++++++--- |
M | handlr.c | | | 56 | ++++++++++++++++++++++++++++---------------------------- |
M | handlr.h | | | 18 | +++++++++++------- |
M | ind.c | | | 21 | +++++---------------- |
M | ind.h | | | 4 | ++-- |
M | main.c | | | 102 | +++++++++++++++++++++++++++++++++++++++---------------------------------------- |
6 files changed, 102 insertions(+), 108 deletions(-)
diff --git a/geomyidae.8 b/geomyidae.8
@@ -352,9 +352,11 @@ executable.[d]cgi $search $arguments $host $port
where
.Bd -literal -offset indent
search = query string (type 7) or "" (type 0)
-arguments = string after "?" in the path, the remaining path or ""
+arguments = string behind "?" in selector or ""
host = server's hostname ("localhost" by default)
port = server's port ("70" by default)
+traversal = remaining path from path traversal
+selector = raw selector
.Ed
.Pp
All terms are tab-separated (per gopher protocol) which can cause some
@@ -377,8 +379,9 @@ GATEWAY_INTERFACE = `CGI/1.1'
PATH_INFO = script which is executed
PATH_TRANSLATED = absolute path with script which is executed
QUERY_STRING = arguments (See above.)
-SELECTOR = arguments (For backwards compatibility.)
-REQUEST = arguments (For backwards compatibility.)
+SELECTOR = raw selector
+REQUEST = raw selector
+TRAVERSAL = traversel (See above.)
REMOTE_ADDR = IP of the client
REMOTE_HOST = REMOTE_ADDR
REQUEST_METHOD = `GET'
diff --git a/handlr.c b/handlr.c
@@ -24,7 +24,8 @@
void
handledir(int sock, char *path, char *port, char *base, char *args,
- char *sear, char *ohost, char *chost, char *bhost, int istls)
+ char *sear, char *ohost, char *chost, char *bhost, int istls,
+ char *sel, char *traverse)
{
char *pa, *file, *e, *par;
struct dirent **dirent;
@@ -35,12 +36,8 @@ handledir(int sock, char *path, char *port, char *base, char *args,
USED(args);
USED(sear);
USED(bhost);
-
- printf("handledir:\n");
- printf("sock = %d; path = %s; port = %s; base = %s; args = %s;\n",
- sock, path, port, base, args);
- printf("sear = %s; ohost = %s; chost = %s; bhost = %s; istls = %d;\n",
- sear, ohost, chost, bhost, istls);
+ USED(sel);
+ USED(traverse);
pa = xstrdup(path);
@@ -70,7 +67,6 @@ handledir(int sock, char *path, char *port, char *base, char *args,
pa,
pa[strlen(pa)-1] == '/'? "" : "/",
dirent[i]->d_name);
- printf("handledir: smprintf file = %s\n", file);
if (stat(file, &st) >= 0 && S_ISDIR(st.st_mode))
type = gettype("index.gph");
ret = dprintf(sock,
@@ -93,7 +89,8 @@ handledir(int sock, char *path, char *port, char *base, char *args,
void
handlegph(int sock, char *file, char *port, char *base, char *args,
- char *sear, char *ohost, char *chost, char *bhost, int istls)
+ char *sear, char *ohost, char *chost, char *bhost, int istls,
+ char *sel, char *traverse)
{
gphindex *act;
int i, ret = 0;
@@ -101,12 +98,8 @@ handlegph(int sock, char *file, char *port, char *base, char *args,
USED(args);
USED(sear);
USED(bhost);
-
- printf("handlegph:\n");
- printf("sock = %d; file = %s; port = %s; base = %s; args = %s;\n",
- sock, file, port, base, args);
- printf("sear = %s; ohost = %s; chost = %s; bhost = %s; istls = %d;\n",
- sear, ohost, chost, bhost, istls);
+ USED(sel);
+ USED(traverse);
act = gph_scanfile(file);
if (act != NULL) {
@@ -124,7 +117,8 @@ handlegph(int sock, char *file, char *port, char *base, char *args,
void
handlebin(int sock, char *file, char *port, char *base, char *args,
- char *sear, char *ohost, char *chost, char *bhost, int istls)
+ char *sear, char *ohost, char *chost, char *bhost, int istls,
+ char *sel, char *traverse)
{
int fd;
@@ -134,6 +128,8 @@ handlebin(int sock, char *file, char *port, char *base, char *args,
USED(sear);
USED(ohost);
USED(bhost);
+ USED(sel);
+ USED(traverse);
fd = open(file, O_RDONLY);
if (fd >= 0) {
@@ -145,9 +141,10 @@ handlebin(int sock, char *file, char *port, char *base, char *args,
void
handlecgi(int sock, char *file, char *port, char *base, char *args,
- char *sear, char *ohost, char *chost, char *bhost, int istls)
+ char *sear, char *ohost, char *chost, char *bhost, int istls,
+ char *sel, char *traverse)
{
- char *script, *path;
+ char *script, *path, *filec;
USED(base);
USED(port);
@@ -157,9 +154,10 @@ handlecgi(int sock, char *file, char *port, char *base, char *args,
sock, file, port, base, args);
printf("sear = %s; ohost = %s; chost = %s; bhost = %s; istls = %d;\n",
sear, ohost, chost, bhost, istls);
+ printf("sel = %s; traverse = %s;\n", sel, traverse);
- path = xstrdup(file);
- path = dirname(path);
+ filec = xstrdup(file);
+ path = dirname(filec);
script = path + strlen(path) + 1;
printf("path = %s\n", path);
printf("script = %s\n", script);
@@ -180,7 +178,7 @@ handlecgi(int sock, char *file, char *port, char *base, char *args,
}
setcgienviron(script, file, port, base, args, sear, ohost, chost,
- bhost, istls);
+ bhost, istls, sel, traverse);
if (execl(file, script, sear, args, ohost, port,
(char *)NULL) == -1) {
@@ -192,17 +190,18 @@ handlecgi(int sock, char *file, char *port, char *base, char *args,
break;
default:
wait(NULL);
- free(path);
+ free(filec);
break;
}
}
void
handledcgi(int sock, char *file, char *port, char *base, char *args,
- char *sear, char *ohost, char *chost, char *bhost, int istls)
+ char *sear, char *ohost, char *chost, char *bhost, int istls,
+ char *sel, char *traverse)
{
FILE *fp;
- char *script, *path, *ln = NULL;
+ char *script, *path, *filec, *ln = NULL;
size_t linesiz = 0;
ssize_t n;
int outsocks[2], ret = 0;
@@ -213,12 +212,13 @@ handledcgi(int sock, char *file, char *port, char *base, char *args,
sock, file, port, base, args);
printf("sear = %s; ohost = %s; chost = %s; bhost = %s; istls = %d;\n",
sear, ohost, chost, bhost, istls);
+ printf("sel = %s; traverse = %s;\n", sel, traverse);
if (socketpair(AF_LOCAL, SOCK_STREAM, 0, outsocks) < 0)
return;
- path = xstrdup(file);
- path = dirname(path);
+ filec = xstrdup(file);
+ path = dirname(filec);
script = path + strlen(path) + 1;
if (sear == NULL)
@@ -238,7 +238,7 @@ handledcgi(int sock, char *file, char *port, char *base, char *args,
}
setcgienviron(script, file, port, base, args, sear, ohost, chost,
- bhost, istls);
+ bhost, istls, sel, traverse);
if (execl(file, script, sear, args, ohost, port,
(char *)NULL) == -1) {
@@ -277,7 +277,7 @@ handledcgi(int sock, char *file, char *port, char *base, char *args,
free(ln);
fclose(fp);
wait(NULL);
- free(path);
+ free(filec);
break;
}
}
diff --git a/handlr.h b/handlr.h
@@ -23,7 +23,7 @@
* base .... base path of geomyidae, never ends in '/', so chroot is ''
* Sample: /var/gopher
* args .... Gives all variable input from the selector in some way.
- * Sample: /with/dirs////?key=value
+ * Sample: key=value
* sear .... search part of request
* Sample: search what?
* ohost ... host of geomyidae (See -h in geomyidae(8))
@@ -34,25 +34,29 @@
* Sample: 78.46.175.99
* istls ... set to 1, if TLS was used for thr request
* Sample: 1
+ * sel ..... Gives the raw selector after processing.
+ * Sample: /get/some/script/with/dirs////?key=value
+ * traversal ..... Gives the raw selector after processing.
+ * Sample: /with/dirs////
*/
void handledir(int sock, char *path, char *port, char *base, char *args,
char *sear, char *ohost, char *chost, char *bhost,
- int istls);
+ int istls, char *sel, char *traverse);
void handlegph(int sock, char *file, char *port, char *base, char *args,
char *sear, char *ohost, char *chost, char *bhost,
- int istls);
+ int istls, char *sel, char *traverse);
void handlebin(int sock, char *file, char *port, char *base, char *args,
char *sear, char *ohost, char *chost, char *bhost,
- int istls);
+ int istls, char *sel, char *traverse);
void handletxt(int sock, char *file, char *port, char *base, char *args,
char *sear, char *ohost, char *chost, char *bhost,
- int istls);
+ int istls, char *sel, char *traverse);
void handlecgi(int sock, char *file, char *port, char *base, char *args,
char *sear, char *ohost, char *chost, char *bhost,
- int istls);
+ int istls, char *sel, char *traverse);
void handledcgi(int sock, char *file, char *port, char *base, char *args,
char *sear, char *ohost, char *chost, char *bhost,
- int istls);
+ int istls, char *sel, char *traverse);
#endif
diff --git a/ind.c b/ind.c
@@ -535,7 +535,8 @@ reverselookup(char *host)
void
setcgienviron(char *file, char *path, char *port, char *base, char *args,
- char *sear, char *ohost, char *chost, char *bhost, int istls)
+ char *sear, char *ohost, char *chost, char *bhost, int istls,
+ char *sel, char *traverse)
{
/*
* TODO: Clean environment from possible unsafe environment variables.
@@ -547,26 +548,19 @@ setcgienviron(char *file, char *path, char *port, char *base, char *args,
setenv("GATEWAY_INTERFACE", "CGI/1.1", 1);
/* TODO: Separate, if run like rest.dcgi. */
setenv("PATH_INFO", file, 1);
- printf("PATH_INFO = %s\n", file);
setenv("PATH_TRANSLATED", path, 1);
- printf("PATH_TRANSLATED = %s\n", path);
setenv("QUERY_STRING", args, 1);
- printf("QUERY_STRING = %s\n", args);
- /* legacy compatibility */
- setenv("SELECTOR", args, 1);
- printf("SELECTOR = %s\n", args);
- setenv("REQUEST", args, 1);
- printf("REQUEST = %s\n", args);
+ setenv("SELECTOR", sel, 1);
+ setenv("REQUEST", sel, 1);
+ setenv("TRAVERSAL", traverse, 1);
setenv("REMOTE_ADDR", chost, 1);
- printf("REMOTE_ADDR = %s\n", chost);
/*
* Don't do a reverse lookup on every call. Only do when needed, in
* the script. The RFC allows us to set the IP to the value.
*/
setenv("REMOTE_HOST", chost, 1);
- printf("REMOTE_HOST = %s\n", chost);
/* Please do not implement identd here. */
unsetenv("REMOTE_IDENT");
unsetenv("REMOTE_USER");
@@ -578,12 +572,9 @@ setcgienviron(char *file, char *path, char *port, char *base, char *args,
*/
setenv("REQUEST_METHOD", "GET", 1);
setenv("SCRIPT_NAME", file, 1);
- printf("SCRIPT_NAME = %s\n", file);
setenv("SERVER_NAME", ohost, 1);
- printf("SERVER_PORT = %s\n", port);
setenv("SERVER_PORT", port, 1);
setenv("SERVER_LISTEN_NAME", bhost, 1);
- printf("SERVER_LISTEN_NAME = %s\n", bhost);
if (istls) {
setenv("SERVER_PROTOCOL", "gophers/1.0", 1);
} else {
@@ -592,10 +583,8 @@ setcgienviron(char *file, char *path, char *port, char *base, char *args,
setenv("SERVER_SOFTWARE", "geomyidae", 1);
setenv("X_GOPHER_SEARCH", sear, 1);
- printf("X_GOPHER_SEARCH = %s\n", sear);
/* legacy compatibility */
setenv("SEARCHREQUEST", sear, 1);
- printf("SEARCHREQUEST = %s\n", sear);
if (istls) {
setenv("GOPHERS", "on", 1);
diff --git a/ind.h b/ind.h
@@ -15,7 +15,7 @@ struct filetype {
char *end;
char *type;
void (* f)(int, char *, char *, char *, char *, char *, char *,
- char *, char *, int);
+ char *, char *, int, char *, char *);
};
filetype *gettype(char *filename);
@@ -51,7 +51,7 @@ char *smprintf(char *fmt, ...);
char *reverselookup(char *host);
void setcgienviron(char *file, char *path, char *port, char *base,
char *args, char *sear, char *ohost, char *chost,
- char *bhost, int istls);
+ char *bhost, int istls, char *sel, char *traverse);
char *humansize(off_t n);
char *humantime(const time_t *clock);
diff --git a/main.c b/main.c
@@ -137,8 +137,9 @@ handlerequest(int sock, char *req, int rlen, char *base, char *ohost,
char *serverp, int nocgi, int istls)
{
struct stat dir;
- char recvc[1025], recvb[1025], path[1025], args[1025], argsc[1025],
- *sear, *c, *sep, *recvbp;
+ char recvc[1025], recvb[1025], path[PATH_MAX+1], rpath[PATH_MAX+1], args[1025],
+ argsc[1025], traverse[1025], traversec[1025],
+ *sear, *sep, *recvbp, *c;
int len = 0, fd, i, maxrecv, pathfallthrough = 0;
filetype *type;
@@ -172,6 +173,7 @@ handlerequest(int sock, char *req, int rlen, char *base, char *ohost,
memset(recvc, 0, sizeof(recvc));
memset(args, 0, sizeof(args));
memset(argsc, 0, sizeof(argsc));
+ memset(traverse, 0, sizeof(argsc));
maxrecv = sizeof(recvb) - 1;
if (rlen > maxrecv || rlen < 0)
@@ -241,18 +243,19 @@ handlerequest(int sock, char *req, int rlen, char *base, char *ohost,
c = strchr(recvb, '?');
if (c != NULL) {
*c++ = '\0';
- snprintf(args, sizeof(args), "?%s", c);
+ snprintf(args, sizeof(args), "%s", c);
}
printf("args = %s\n", args);
printf("recvb = %s\n", recvb);
/* Strip '/' at the end of the request. */
for (c = recvb + strlen(recvb) - 1; c >= recvb && c[0] == '/'; c--) {
- /* Prepend to args. */
- snprintf(args, sizeof(args), "/%s", args);
+ memmove(traversec, traverse, strlen(traverse));
+ /* Prepend to traverse. */
+ snprintf(traverse, sizeof(traverse), "/%s", traversec);
c[0] = '\0';
}
- printf("args = %s\n", args);
+ printf("traverse = %s\n", traverse);
/* Do not allow requests including "..". */
if (strstr(recvb, "..")) {
@@ -284,8 +287,7 @@ handlerequest(int sock, char *req, int rlen, char *base, char *ohost,
* $args = $rest_of_path + "?" + $args
*/
if (stat(path, &dir) == -1) {
- printf("Not found. Try backtraversal.\n");
- memmove(argsc, args, strlen(args));
+ memmove(traversec, traverse, strlen(traverse));
snprintf(path, sizeof(path), "%s", base);
recvbp = recvb;
@@ -295,44 +297,42 @@ handlerequest(int sock, char *req, int rlen, char *base, char *ohost,
* etc.
*/
while (recvbp != NULL) {
- /* Traverse multiple / in selector. */
- for (sep = recvbp; sep != recvbp && sep != recvbp+1;
- sep = strsep(&recvbp, "/"));
- printf("traversal directory = %s\n", sep);
+ /* Traverse multiple empty / in selector. */
+ while(recvbp[0] == '/')
+ recvbp++;
+ sep = strchr(recvbp, '/');
+ if (sep != NULL)
+ *sep++ = '\0';
- /* Append found directory to path. */
snprintf(path+strlen(path), sizeof(path)-strlen(path),
- "/%s", sep);
+ "/%s", recvbp);
/* path is now always at least '/' */
- printf("full traversal path = %s\n", path);
-
if (stat(path, &dir) == -1) {
- /*
- * Current try was not found. Go back one
- * step and finish.
- */
- c = strrchr(path, '/');
- if (c != NULL) {
- *c++ = '\0';
- snprintf(args, sizeof(args),
- "/%s%s%s%s",
- c,
- (recvbp != NULL)? "/" : "",
- (recvbp != NULL)? recvbp : "",
- (argsc[0] != '\0')? argsc : ""
- );
- printf("args = %s\n", args);
- }
+ path[strlen(path)-strlen(recvbp)-1] = '\0';
+ snprintf(traverse, sizeof(traverse),
+ "/%s%s%s%s",
+ recvbp,
+ (sep != NULL)? "/" : "",
+ (sep != NULL)? sep : "",
+ (traversec[0] != '\0')? traversec : ""
+ );
/* path fallthrough */
pathfallthrough = 1;
printf("pathfallthrough = 1\n");
break;
}
+ /* Append found directory to path. */
+ recvbp = sep;
}
}
- printf("path = %s\n", path);
- if (stat(path, &dir) != -1) {
+ if (realpath(path, (char *)&rpath) == NULL) {
+ dprintf(sock, notfounderr, recvc);
+ if (loglvl & ERRORS)
+ logentry(clienth, clientp, recvc, "not found");
+ }
+ printf("rpath = %s\n", rpath);
+ if (stat(rpath, &dir) != -1) {
/*
* If sticky bit is set, only serve if this is encrypted.
*/
@@ -349,9 +349,9 @@ handlerequest(int sock, char *req, int rlen, char *base, char *ohost,
printf("S_ISDIR\n");
for (i = 0; i < sizeof(indexf)/sizeof(indexf[0]);
i++) {
- len = strlen(path);
- if (len + strlen(indexf[i]) + (path[len-1] == '/')? 0 : 1
- >= sizeof(path)) {
+ len = strlen(rpath);
+ if (len + strlen(indexf[i]) + (rpath[len-1] == '/')? 0 : 1
+ >= sizeof(rpath)) {
if (loglvl & ERRORS) {
logentry(clienth, clientp,
recvc,
@@ -359,21 +359,19 @@ handlerequest(int sock, char *req, int rlen, char *base, char *ohost,
}
return;
}
- sprintf(path, "%s%s%s",
- path,
- (path[len-1] == '/')? "" : "/",
- indexf[i]);
- printf("path index = %s\n", path);
- fd = open(path, O_RDONLY);
+ if (rpath[len-1] != '/')
+ strcat(rpath, "/");
+ strcat(rpath, indexf[i]);
+ printf("path index = %s\n", rpath);
+ fd = open(rpath, O_RDONLY);
if (fd >= 0)
break;
/* Not found. Clear path from indexf. */
- printf("len = %d\n", len);
- path[len] = '\0';
+ rpath[len] = '\0';
}
} else {
- fd = open(path, O_RDONLY);
+ fd = open(rpath, O_RDONLY);
if (fd < 0) {
dprintf(sock, notfounderr, recvc);
if (loglvl & ERRORS) {
@@ -389,9 +387,9 @@ handlerequest(int sock, char *req, int rlen, char *base, char *ohost,
if (fd >= 0) {
close(fd);
- c = strrchr(path, '/');
+ c = strrchr(rpath, '/');
if (c == NULL)
- c = path;
+ c = rpath;
type = gettype(c);
/*
@@ -416,8 +414,8 @@ handlerequest(int sock, char *req, int rlen, char *base, char *ohost,
if (loglvl & FILES)
logentry(clienth, clientp, recvc, "serving");
- type->f(sock, path, port, base, args, sear, ohost,
- clienth, serverh, istls);
+ type->f(sock, rpath, port, base, args, sear, ohost,
+ clienth, serverh, istls, recvc, traverse);
}
} else {
if (pathfallthrough && S_ISDIR(dir.st_mode)) {
@@ -429,8 +427,8 @@ handlerequest(int sock, char *req, int rlen, char *base, char *ohost,
}
if (!pathfallthrough && S_ISDIR(dir.st_mode)) {
- handledir(sock, path, port, base, args, sear, ohost,
- clienth, serverh, istls);
+ handledir(sock, rpath, port, base, args, sear, ohost,
+ clienth, serverh, istls, recvc, traverse);
if (loglvl & DIRS) {
logentry(clienth, clientp, recvc,
"dir listing");