| Fix traversal handling, add selector and traversal to CGI environment. - geomyi… | |
| git clone git://bitreich.org/geomyidae/ git://enlrupgkhuxnvlhsf6lc3fziv5h2hhfri… | |
| Log | |
| Files | |
| Refs | |
| Tags | |
| README | |
| LICENSE | |
| --- | |
| commit a291bc8c99309fcbfd1a7688988ba60a2e5bf042 | |
| parent b12a77acd24fc170b1ad047986ffaf13592fb326 | |
| Author: Christoph Lohmann <[email protected]> | |
| 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… | |
| 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 … | |
| 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 … | |
| 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, cha… | |
| 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, cha… | |
| 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, cha… | |
| 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, ch… | |
| 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, ch… | |
| 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, cha… | |
| } | |
| setcgienviron(script, file, port, base, args, sear, ohost, cho… | |
| - 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, c… | |
| 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, … | |
| 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, ch… | |
| } | |
| setcgienviron(script, file, port, base, args, sear, ohost, cho… | |
| - 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, ch… | |
| 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 *b… | |
| 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 *ba… | |
| */ | |
| 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 *ba… | |
| 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, ch… | |
| 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], ar… | |
| + 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, ch… | |
| 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, … | |
| 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, ch… | |
| * $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, … | |
| * 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, ch… | |
| 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] == … | |
| - >= sizeof(path)) { | |
| + len = strlen(rpath); | |
| + if (len + strlen(indexf[i]) + (rpath[len-1] ==… | |
| + >= sizeof(rpath)) { | |
| if (loglvl & ERRORS) { | |
| logentry(clienth, clientp, | |
| recvc, | |
| @@ -359,21 +359,19 @@ handlerequest(int sock, char *req, int rlen, char *base, … | |
| } | |
| 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, ch… | |
| 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, ch… | |
| 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, ch… | |
| } | |
| 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"); |