diff --git a/Makefile b/Makefile index 790998a..8a665b0 100644 --- a/Makefile +++ b/Makefile @@ -10,7 +10,10 @@ CFLAGS += -pedantic -Wall -Wextra -Wmissing-prototypes \ all: vger clean: - rm -f vger *.core *.o + find . -name vger -o \ + -name \*\.o -o \ + -name \*\.core \ + -delete vger: main.o mimes.o utils.o opts.h ${CC} ${CFLAGS} -o $@ main.o mimes.o utils.o diff --git a/main.c b/main.c index 1a7c44f..ead8d72 100644 --- a/main.c +++ b/main.c @@ -76,13 +76,16 @@ drop_privileges(const char *user, const char *path) } else { eunveil(path, "r"); } + /* permission to execute what's inside cgipath */ if (strlen(cgibin) > 0) { + /* first, build the full path of cgi (not in chroot) */ char cgifullpath[PATH_MAX] = {'\0'}; estrlcpy(cgifullpath, path, sizeof(cgifullpath)); estrlcat(cgifullpath, cgibin, sizeof(cgifullpath)); + eunveil(cgifullpath, "rx"); } - /* no more unveil later */ + /* forbid more unveil */ eunveil(NULL, NULL); /* @@ -90,6 +93,7 @@ drop_privileges(const char *user, const char *path) * write to stdio */ if (strlen(cgibin) > 0) { + /* cgi need execlp() (exec) and fork() (proc) */ epledge("stdio rpath exec proc", NULL); } else { epledge("stdio rpath", NULL); @@ -121,6 +125,7 @@ display_file(const char *uri) char *buffer[BUFSIZ]; char target[FILENAME_MAX] = {'\0'}; char fp[PATH_MAX] = {'\0'}; + char tmp[PATH_MAX] = {'\0'}; /* used to build temporary path */ /* build file path inside chroot */ estrlcpy(fp, chroot_dir, sizeof(fp)); @@ -141,21 +146,19 @@ display_file(const char *uri) if (S_ISDIR(sb.st_mode) != 0) { if (fp[strlen(fp) -1 ] != '/') { /* no ending "/", redirect to "path/" */ - char new_uri[PATH_MAX] = {'\0'}; - estrlcpy(new_uri, uri, sizeof(new_uri)); - estrlcat(new_uri, "/", sizeof(new_uri)); - status_redirect(31, new_uri); + estrlcpy(tmp, uri, sizeof(tmp)); + estrlcat(tmp, "/", sizeof(tmp)); + status_redirect(31, tmp); return; } else { /* there is a leading "/", display index.gmi */ - char index_path[PATH_MAX] = {'\0'}; - estrlcpy(index_path, fp, sizeof(index_path)); - estrlcat(index_path, "index.gmi", sizeof(index_path)); + estrlcpy(tmp, fp, sizeof(tmp)); + estrlcat(tmp, "index.gmi", sizeof(tmp)); /* check if index.gmi exists or show autoindex */ - if (stat(index_path, &sb) == 0) { - estrlcpy(fp, index_path, sizeof(fp)); + if (stat(tmp, &sb) == 0) { + estrlcpy(fp, tmp, sizeof(fp)); } else if (doautoidx != 0) { autoindex(fp); return; @@ -172,7 +175,7 @@ display_file(const char *uri) status(20, file_mime); - /* read the file and write it to stdout */ + /* read the file byte after byte in buffer and write it to stdout */ while ((nread = fread(buffer, 1, sizeof(buffer), fd)) != 0) fwrite(buffer, 1, nread, stdout); goto closefd; @@ -205,22 +208,41 @@ void autoindex(const char *path) { int n = 0; - struct dirent **namelist; + char *pos = NULL; + struct dirent **namelist; /* this must be freed at last */ syslog(LOG_DAEMON, "autoindex: %s", path); status(20, "text/gemini"); + /* display link to parent */ + char parent[PATH_MAX] = {'\0'}; + /* parent is "path" without chroot_dir */ + estrlcpy(parent, path+strlen(chroot_dir), sizeof(parent)); + /* remove ending '/' */ + while (parent[strlen(parent)-1] == '/') { + parent[strlen(parent)-1] = '\0'; + } + /* remove last part after '/' */ + pos = strrchr(parent, '/'); + if (pos != NULL) { + pos[1] = '\0'; /* at worse, parent is now "/" */ + } + printf("=> %s ../\n", parent); + + /* use alphasort to always have the same order on every system */ if ((n = scandir(path, &namelist, NULL, alphasort)) < 0) { status(51, "text/gemini"); errlog("Can't scan %s", path); } else { for(int j = 0; j < n; j++) { + /* skip self and parent */ if ((strcmp(namelist[j]->d_name, ".") == 0) || (strcmp(namelist[j]->d_name, "..") == 0)) { continue; } + /* add "/" at the end of a directory path */ if (namelist[j]->d_type == DT_DIR) { printf("=> ./%s/ %s/\n", namelist[j]->d_name, namelist[j]->d_name); } else { @@ -239,6 +261,7 @@ cgi(const char *cgicmd) int pipedes[2] = {0}; pid_t pid; + /* get a pipe to get stdout */ if (pipe(pipedes) != 0) { status(42, "text/gemini"); err(1, "pipe failed"); @@ -414,6 +437,7 @@ main(int argc, char **argv) if ((strlen(cgibin) > 0) && (strncmp(uri, cgibin, strlen(cgibin)) == 0)) { + /* cgipath with chroot_dir at the beginning */ char cgipath[PATH_MAX] = {'\0'}; estrlcpy(cgipath, chroot_dir, sizeof(cgipath)); estrlcat(cgipath, uri, sizeof(cgipath)); diff --git a/tests/test.sh b/tests/test.sh index bad3c1a..f3727d1 100644 --- a/tests/test.sh +++ b/tests/test.sh @@ -72,7 +72,7 @@ if ! [ $OUT = "874f5e1af67eff6b93bedf8ac8033066" ] ; then echo "error" ; exit 1 # auto index in directory OUT=$(printf "gemini://host.name/autoidx/\r\n" | ../vger -d var/gemini/ -i | tee /dev/stderr | $MD5) -if ! [ $OUT = "988160d1908a9dcd0cadc21262de4490" ] ; then echo "error" ; exit 1 ; fi +if ! [ $OUT = "515bcb4ba5f8869360f53afe2841e044" ] ; then echo "error" ; exit 1 ; fi # cgi simple script OUT=$(printf "gemini://host.name/cgi-bin/test.cgi\r\n" | ../vger -d var/gemini/ -c /cgi-bin | tee /dev/stderr | $MD5) diff --git a/utils.c b/utils.c index 413d30b..ad6c84d 100644 --- a/utils.c +++ b/utils.c @@ -34,7 +34,7 @@ estrlcpy(char *dst, const char *src, size_t dstsize) n = strlcpy(dst, src, dstsize); if (n >= dstsize) { - err(1, "strlcyp failed for %s = %s", dst, src); + err(1, "strlcpy failed for %s = %s", dst, src); } return n;