diff --git a/main.c b/main.c index e9c7b92..2c0786c 100644 --- a/main.c +++ b/main.c @@ -22,20 +22,19 @@ main(int argc, char **argv) while ((option = getopt(argc, argv, ":d:l:m:u:c:vi")) != -1) { switch (option) { case 'd': - estrlcpy(chroot_dir, optarg, sizeof(chroot_dir)); + esnprintf(chroot_dir, sizeof(chroot_dir), "%s", optarg); break; case 'l': - estrlcpy(lang, "lang=", sizeof(lang)); - estrlcat(lang, optarg, sizeof(lang)); + esnprintf(lang, sizeof(lang), "lang=%s", optarg); break; case 'm': - estrlcpy(default_mime, optarg, sizeof(default_mime)); + esnprintf(default_mime, sizeof(default_mime), "%s", optarg); break; case 'u': - estrlcpy(user, optarg, sizeof(user)); + esnprintf(user, sizeof(user), "%s", optarg); break; case 'c': - estrlcpy(cgi_dir, optarg, sizeof(cgi_dir)); + esnprintf(cgi_dir, sizeof(cgi_dir), "%s", optarg); break; case 'v': virtualhost = 1; @@ -59,6 +58,8 @@ main(int argc, char **argv) uridecode(query); uridecode(path); + remove_double_dot(path); + /* is it cgi ? */ if (*cgi_dir) if (do_cgi(chroot_dir, cgi_dir, path, hostname, query) == 0) diff --git a/utils.c b/utils.c index 89bc991..e4c1f64 100644 --- a/utils.c +++ b/utils.c @@ -45,29 +45,21 @@ epledge(const char *promises, const char *execpromises) #endif size_t -estrlcpy(char *dst, const char *src, size_t dstsize) +esnprintf(char *str, size_t size, const char *format, ...) { - size_t n = 0; + /* usage : esnprintf(str, sizeof(str), "%s ... %s", arg1, arg2); */ + va_list ap; + size_t ret = 0; - n = strlcpy(dst, src, dstsize); - if (n >= dstsize) { - status(41, "strlcpy failed, see logs"); - stop(EXIT_FAILURE, "strlcpy() failed for %s = %s", dst, src); + va_start(ap, format); + ret = vsnprintf(str, size, format, ap); + va_end(ap); + if (ret < 0 || ret >= size) { + status(41, "vnsprintf failed: Output trunkated"); + stop(EXIT_FAILURE, "vsnprintf: Output trunkated"); } - return n; -} - -size_t -estrlcat(char *dst, const char *src, size_t dstsize) -{ - size_t size; - if ((size = strlcat(dst, src, dstsize)) >= dstsize) { - status(41, "strlcat() failed, see logs"); - stop(EXIT_FAILURE, "strlcat on %s + %s", dst, src); - } - - return size; + return ret; } int diff --git a/utils.h b/utils.h index 72d64e8..0f8266b 100644 --- a/utils.h +++ b/utils.h @@ -1,9 +1,8 @@ void getsubexp(const char *, regmatch_t, char *); -void echdir (const char *); +void echdir(const char *); void epledge(const char *, const char *); void eunveil(const char *, const char *); int esetenv(const char *, const char *, int); -size_t estrlcat(char *, const char *, size_t); -size_t estrlcpy(char *, const char *, size_t); +size_t esnprintf(char *, size_t, const char *, ...); size_t print_file(FILE *fd); void set_errmsg(const char *, ...); diff --git a/vger.c b/vger.c index f4e0b97..79bf09e 100644 --- a/vger.c +++ b/vger.c @@ -159,7 +159,7 @@ drop_privileges(const char *user, const char *chroot_dir, const char *cgi_dir) /* promise permissions */ if (*cgi_dir) - epledge("stdio rpath exec", NULL); + epledge("stdio rpath exec proc", NULL); else epledge("stdio rpath", NULL); #endif @@ -222,10 +222,10 @@ do_cgi(const char *chroot_dir, const char *cgi_dir, const char *path, const char * cgi_dir + strlen(chrootdir) (skip chrootdir) */ - estrlcpy(cgirp, cgi_dir + strlen(chroot_dir), sizeof(cgirp)); + esnprintf(cgirp, sizeof(cgirp), "%s", cgi_dir + strlen(chroot_dir)); /* ensure there is no leading / if user didn't end chrootdir with */ while (*cgirp == '/') - estrlcpy(cgirp, cgirp+1, sizeof(cgirp)); + memmove(cgirp, cgirp+1, strlen(cgirp+1)+1); if (strncmp(cgirp, path, strlen(cgirp)) != 0) return 1; /* not in cgi_dir, go to display_file */ @@ -252,7 +252,7 @@ do_cgi(const char *chroot_dir, const char *cgi_dir, const char *path, const char */ /* cgi file to execute */ - estrlcpy(cgifp, path + strlen(cgirp) + 1, sizeof(cgifp)); + esnprintf(cgifp, sizeof(cgifp), "%s", path + strlen(cgirp) + 1); if (!(*cgifp)) /* problem with cgi file, abort */ return 1; @@ -313,25 +313,46 @@ autoindex(const char *path) void cgi(const char *cgicmd) { - /* TODO? cgi currently return the wrong data size unless we switch from execl to popen */ + int fildes[2] = {0}; + int retcode = 0; + pid_t pid = 0; + FILE *output = NULL; - /* run cgicmd replacing current process */ - _datasiz = -1; /* bytes sent by cgi are unknown */ - execl(cgicmd, cgicmd, NULL); + if (pipe(fildes) != 0) + goto cgierr; + + if ((pid = fork()) < 0) + goto cgierr; + + if (pid > 0) { /* parent */ + close(fildes[1]); /* make sure entry is closed to get EOF */ + if ((output = fdopen(fildes[0], "r")) == NULL) + goto cgierr; + + _datasiz += print_file(output); + close(fildes[0]); + fclose(output); + waitpid(pid, &retcode, 0); + stop(EXIT_SUCCESS, "cgi ran with exit code %d", status); + + } else { /* child */ + /* set pipe output equal to stdout & stderr */ + dup2(fildes[1], STDOUT_FILENO); + close(fildes[1]); /* no longer required */ + execl(cgicmd, cgicmd, NULL); + } + + cgierr: /* if execl is ok, this will never be reached */ + close(fildes[0]); + close(fildes[1]); status(42, "error when trying run cgi"); stop(EXIT_FAILURE, "error when trying to execl %s", cgicmd); - } char * read_request(char *request) { -/* -* read the request, check for errors and sanitize the input -*/ - char *pos = NULL; - /* read 1024 +1 chars from stdin to get the request (1024 + \0) */ if (fgets(request, GEMINI_REQUEST_MAX, stdin) == NULL) { @@ -357,31 +378,36 @@ read_request(char *request) request[strcspn(request, "\r\n")] = '\0'; /* save request for logs */ - estrlcpy(_request, request, sizeof(_request)); + esnprintf(_request, sizeof(_request), "%s", request); + + return request; +} + + +void +remove_double_dot(char *request) +{ + char *pos = NULL; /* remove all "/.." for safety reasons */ while ((pos = strstr(request, "/..")) != NULL) memmove(request, pos + 3, strlen(pos) + 1 - 3); /* "/.." = 3 */ - - return request; } char * set_path(char *path, size_t pathsiz, int virtualhost, const char *hostname) { - char tmp[GEMINI_REQUEST_MAX] = {'\0'}; - - if (strlen(path) == 0) /* this is root dir */ - estrlcpy(path, "./", pathsiz); - /* path is in a subdir named hostname */ if (virtualhost) { - estrlcpy(tmp, hostname, sizeof(tmp)); - estrlcat(tmp, "/", sizeof(tmp)); - estrlcat(tmp, path, sizeof(tmp)); - estrlcpy(path, tmp, pathsiz); + char tmp[GEMINI_REQUEST_MAX] = {'\0'}; + esnprintf(tmp, sizeof(tmp), "%s/%s", hostname, path); + esnprintf(path, pathsiz, "%s", tmp); } + if (strlen(path) == 0) /* this is root dir */ + esnprintf(path, pathsiz, "./"); + + return path; } @@ -406,21 +432,17 @@ check_path(char *path, size_t pathsiz, int virtualhost, size_t hstnm_o) /* check if dir path end with "/" */ if (path[strlen(path) - 1] != '/') { /* redirect to the dir with appropriate ending '/' */ - estrlcpy(tmp, "/", sizeof(tmp)); if (virtualhost) /* skip hostname */ - estrlcat(tmp, path+hstnm_o+1, sizeof(tmp)); + esnprintf(tmp, sizeof(tmp), "/%s/", path+hstnm_o+1); else - estrlcat(tmp, path, sizeof(tmp)); - estrlcat(tmp, "/", sizeof(tmp)); + esnprintf(tmp, sizeof(tmp), "/%s/", path); status(31, "%s", tmp); stop(EXIT_SUCCESS, NULL); } /* check if DEFAULT_INDEX exists in directory */ - estrlcpy(tmp, path, sizeof(tmp)); - estrlcat(tmp, "/", sizeof(tmp)); - estrlcat(tmp, DEFAULT_INDEX, sizeof(tmp)); + esnprintf(tmp, sizeof(tmp), "%s/%s", path, DEFAULT_INDEX); if (stat(tmp, &sb) == 0) - estrlcpy(path, tmp, pathsiz); + esnprintf(path, pathsiz, "%s", tmp); } } diff --git a/vger.h b/vger.h index 8246f1f..4a4c40c 100644 --- a/vger.h +++ b/vger.h @@ -46,6 +46,7 @@ void check_path(char *, size_t, int, size_t); ssize_t display_file(const char *); int do_cgi(const char *, const char *, const char *, const char *, const char *); void drop_privileges(const char *, const char *, const char *); +void remove_double_dot(char *); char * set_path(char *, size_t, int, const char *); void split_request(const char *, char *, char *, char *); void status(const int, const char *, ...);