Fix traversal handling, add selector and traversal to CGI environment. - geomyidae - A small C-based gopherd. (gopher://bitreich.org/1/scm/geomyidae)
 (HTM) git clone git://r-36.net/geomyidae
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) README
 (DIR) LICENSE
       ---
 (DIR) commit a291bc8c99309fcbfd1a7688988ba60a2e5bf042
 (DIR) parent b12a77acd24fc170b1ad047986ffaf13592fb326
 (HTM) 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");