commit 72f653b65247a296f1be344f3c6c1ad981b9fbcf from: Omar Polo date: Fri Nov 06 17:05:44 2020 UTC [cgi] execute cgi scripts only inside a specific directory change the meaning of the -x flag: now it takes a string and executes CGI scripts only if they are inside a directory with the given name, relatively to the document root. commit - f65ed01841f4603f861bed55224227501f56372b commit + 72f653b65247a296f1be344f3c6c1ad981b9fbcf blob - a5d43108fff381834af1f61b292ec12bbaa49819 blob + 133f513dd3f413b95795711b0a8ccf074973702c --- gmid.c +++ gmid.c @@ -107,12 +107,12 @@ struct etm { /* file extension to mime */ dprintf(logfd, "[%s] " fmt "\n", buf, __VA_ARGS__); \ } while (0) -const char *dir; +const char *dir, *cgi; int dirfd, logfd; -int cgi; int connected_clients; void siginfo_handler(int); +int starts_with(const char*, const char*); char *url_after_proto(char*); char *url_start_of_request(char*); @@ -147,13 +147,23 @@ siginfo_handler(int sig) (void)sig; } +int +starts_with(const char *str, const char *prefix) +{ + size_t i; + + for (i = 0; prefix[i] != '\0'; ++i) + if (str[i] != prefix[i]) + return 0; + return 1; +} + char * url_after_proto(char *url) { char *s; const char *proto = "gemini"; const char *marker = "://"; - size_t i; /* a relative URL */ if ((s = strstr(url, marker)) == NULL) @@ -162,9 +172,8 @@ url_after_proto(char *url) if (s - strlen(proto) != url) return NULL; - for (i = 0; proto[i] != '\0'; ++i) - if (url[i] != proto[i]) - return NULL; + if (!starts_with(url, proto)) + return NULL; /* a valid gemini:// URL */ return s + strlen(marker); @@ -353,7 +362,8 @@ open_file(char *path, char *query, struct pollfd *fds, return 0; } - if (cgi && (sb.st_mode & S_IXUSR)) { + /* +2 to skip the ./ */ + if ((sb.st_mode & S_IXUSR) && cgi != NULL && starts_with(fpath+2, cgi)) { start_cgi(fpath, query, fds, c); return 0; } @@ -868,7 +878,8 @@ void usage(const char *me) { fprintf(stderr, - "USAGE: %s [-h] [-c cert.pem] [-d docs] [-k key.pem]\n", + "USAGE: %s [-h] [-c cert.pem] [-d docs] [-k key.pem] " + "[-l logfile] [-x cgi-bin]\n", me); } @@ -892,9 +903,9 @@ main(int argc, char **argv) dir = "docs/"; logfd = 2; /* stderr */ - cgi = 0; + cgi = NULL; - while ((ch = getopt(argc, argv, "c:d:hk:l:x")) != -1) { + while ((ch = getopt(argc, argv, "c:d:hk:l:x:")) != -1) { switch (ch) { case 'c': cert = optarg; @@ -920,7 +931,7 @@ main(int argc, char **argv) break; case 'x': - cgi = 1; + cgi = optarg; break; default: @@ -953,12 +964,18 @@ main(int argc, char **argv) if ((dirfd = open(dir, O_RDONLY | O_DIRECTORY)) == -1) err(1, "open: %s", dir); - if (unveil(dir, cgi ? "rx" : "r") == -1) - err(1, "unveil"); + if (cgi != NULL) { + if (unveil(dir, "rx") == -1) + err(1, "unveil"); + if (pledge("stdio rpath inet proc exec", NULL) == -1) + err(1, "pledge"); + } else { + if (unveil(dir, "r") == -1) + err(1, "unveil"); + if (pledge("stdio rpath inet", NULL) == -1) + err(1, "pledge"); + } - if (pledge(cgi ? "stdio rpath inet proc exec" : "stdio rpath inet", NULL) == -1) - err(1, "pledge"); - loop(ctx, sock); close(sock);