Fix traversal handling, add selector and traversal to CGI environment. - geomyidae - A small C-based gopherd. HTML git clone git://bitreich.org/geomyidae/ git://enlrupgkhuxnvlhsf6lc3fziv5h2hhfrinws65d7roiv6bfj7d652fid.onion/geomyidae/ DIR Log DIR Files DIR Refs DIR Tags DIR README DIR LICENSE --- DIR commit a291bc8c99309fcbfd1a7688988ba60a2e5bf042 DIR parent b12a77acd24fc170b1ad047986ffaf13592fb326 HTML 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(-) --- DIR 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' DIR 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; } } DIR 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 DIR 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); DIR 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); DIR 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");