bring the CGI implementation in par with GLV-1.12556

This commit is contained in:
Omar Polo 2021-02-01 11:11:43 +00:00
parent e17642a7bb
commit 2fafa2d23e
4 changed files with 162 additions and 48 deletions

153
ex.c
View File

@ -18,7 +18,10 @@
#include <errno.h>
#include <fcntl.h>
#include <libgen.h>
#include <limits.h>
#include <signal.h>
#include <stdarg.h>
#include <string.h>
#include "gmid.h"
@ -64,6 +67,41 @@ recv_string(int fd, char **ret)
return 1;
}
int
send_iri(int fd, struct iri *i)
{
return send_string(fd, i->schema)
&& send_string(fd, i->host)
&& send_string(fd, i->port)
&& send_string(fd, i->path)
&& send_string(fd, i->query);
}
int
recv_iri(int fd, struct iri *i)
{
memset(i, 0, sizeof(*i));
if (!recv_string(fd, &i->schema)
|| !recv_string(fd, &i->host)
|| !recv_string(fd, &i->port)
|| !recv_string(fd, &i->path)
|| !recv_string(fd, &i->query))
return 0;
return 1;
}
void
free_recvd_iri(struct iri *i)
{
free(i->schema);
free(i->host);
free(i->port);
free(i->path);
free(i->query);
}
int
send_vhost(int fd, struct vhost *vhost)
{
@ -178,11 +216,25 @@ safe_setenv(const char *name, const char *val)
setenv(name, val, 1);
}
static char *
xasprintf(const char *fmt, ...)
{
va_list ap;
char *s;
va_start(ap, fmt);
if (vasprintf(&s, fmt, ap) == -1)
s = NULL;
va_end(ap);
return s;
}
/* fd or -1 on error */
static int
launch_cgi(const char *spath, const char *relpath, const char *query,
const char *addr, const char *ruser, const char *cissuer, const char *chash,
struct vhost *vhost)
launch_cgi(struct iri *iri, const char *spath, char *relpath,
const char *addr, const char *ruser, const char *cissuer,
const char *chash, struct vhost *vhost)
{
int p[2]; /* read end, write end */
@ -194,42 +246,58 @@ launch_cgi(const char *spath, const char *relpath, const char *query,
return -1;
case 0: { /* child */
char *portno, *ex, *requri;
char *argv[] = { NULL, NULL, NULL };
char *argv[] = {NULL, NULL, NULL};
char *ex, *pwd;
char iribuf[GEMINI_URL_LEN];
char path[PATH_MAX];
close(p[0]);
if (dup2(p[1], 1) == -1)
goto childerr;
if (asprintf(&portno, "%d", conf.port) == -1)
goto childerr;
ex = xasprintf("%s/%s", vhost->dir, spath);
argv[0] = ex;
argv[1] = iri->query;
if (asprintf(&ex, "%s/%s", vhost->dir, spath) == -1)
goto childerr;
if (asprintf(&requri, "%s%s%s", spath,
(relpath != NULL && *relpath == '\0') ? "" : "/",
(relpath != NULL ? relpath : "")) == -1)
goto childerr;
argv[0] = argv[1] = ex;
serialize_iri(iri, iribuf, sizeof(iribuf));
safe_setenv("GATEWAY_INTERFACE", "CGI/1.1");
safe_setenv("SERVER_PROTOCOL", "GEMINI");
safe_setenv("SERVER_SOFTWARE", "gmid");
safe_setenv("SERVER_PORT", portno);
safe_setenv("GEMINI_DOCUMENT_ROOT", vhost->dir);
safe_setenv("GEMINI_SCRIPT_FILENAME",
xasprintf("%s/%s", vhost->dir, spath));
safe_setenv("GEMINI_URL", iribuf);
if (!strcmp(vhost->domain, "*"))
safe_setenv("SERVER_NAME", vhost->domain);
strlcpy(path, "/", sizeof(path));
strlcat(path, spath, sizeof(path));
safe_setenv("GEMINI_URL_PATH", path);
safe_setenv("SCRIPT_NAME", spath);
safe_setenv("SCRIPT_EXECUTABLE", ex);
safe_setenv("REQUEST_URI", requri);
safe_setenv("REQUEST_RELATIVE", relpath);
safe_setenv("QUERY_STRING", query);
safe_setenv("REMOTE_HOST", addr);
if (relpath != NULL) {
strlcpy(path, "/", sizeof(path));
strlcat(path, relpath, sizeof(path));
safe_setenv("PATH_INFO", path);
strlcpy(path, vhost->dir, sizeof(path));
strlcat(path, "/", sizeof(path));
strlcat(path, relpath, sizeof(path));
safe_setenv("PATH_TRANSLATED", path);
}
safe_setenv("QUERY_STRING", iri->query);
safe_setenv("REMOTE_ADDR", addr);
safe_setenv("DOCUMENT_ROOT", vhost->dir);
safe_setenv("REMOTE_HOST", addr);
safe_setenv("REQUEST_METHOD", "");
strlcpy(path, "/", sizeof(path));
strlcat(path, spath, sizeof(path));
safe_setenv("SCRIPT_NAME", path);
safe_setenv("SERVER_NAME", iri->host);
snprintf(path, sizeof(path), "%d", conf.port);
safe_setenv("SERVER_PORT", path);
safe_setenv("SERVER_PROTOCOL", "GEMINI");
safe_setenv("SERVER_SOFTWARE", "gmid/1.5");
if (ruser != NULL) {
safe_setenv("AUTH_TYPE", "Certificate");
@ -238,9 +306,15 @@ launch_cgi(const char *spath, const char *relpath, const char *query,
safe_setenv("TLS_CLIENT_HASH", chash);
}
fchdir(vhost->dirfd);
strlcpy(path, argv[0], sizeof(path));
pwd = dirname(path);
if (chdir(pwd)) {
warn("chdir");
goto childerr;
}
execvp(ex, argv);
execvp(argv[0], argv);
warn("execvp: %s", argv[0]);
goto childerr;
}
@ -257,25 +331,28 @@ childerr:
int
executor_main(int fd)
{
char *spath, *relpath, *query, *addr, *ruser, *cissuer, *chash;
char *spath, *relpath, *addr, *ruser, *cissuer, *chash;
struct vhost *vhost;
struct iri iri;
int d;
#ifdef __OpenBSD__
for (vhost = hosts; vhost->domain != NULL; ++vhost) {
if (unveil(vhost->dir, "x") == -1)
/* r so we can chdir into the correct directory */
if (unveil(vhost->dir, "rx") == -1)
err(1, "unveil %s for domain %s",
vhost->dir, vhost->domain);
}
if (pledge("stdio sendfd proc exec", NULL))
/* rpath to chdir into the correct directory */
if (pledge("stdio rpath sendfd proc exec", NULL))
err(1, "pledge");
#endif
for (;;) {
if (!recv_string(fd, &spath)
if (!recv_iri(fd, &iri)
|| !recv_string(fd, &spath)
|| !recv_string(fd, &relpath)
|| !recv_string(fd, &query)
|| !recv_string(fd, &addr)
|| !recv_string(fd, &ruser)
|| !recv_string(fd, &cissuer)
@ -283,15 +360,15 @@ executor_main(int fd)
|| !recv_vhost(fd, &vhost))
break;
d = launch_cgi(spath, relpath, query,
addr, ruser, cissuer, chash, vhost);
d = launch_cgi(&iri, spath, relpath, addr, ruser, cissuer, chash,
vhost);
if (!send_fd(fd, d))
break;
close(d);
free_recvd_iri(&iri);
free(spath);
free(relpath);
free(query);
free(addr);
free(ruser);
free(cissuer);

9
gmid.h
View File

@ -204,12 +204,12 @@ int vhost_auto_index(struct vhost*, const char*);
int check_path(struct client*, const char*, int*);
void open_file(struct pollfd*, struct client*);
void load_file(struct pollfd*, struct client*);
void check_for_cgi(char *, char*, struct pollfd*, struct client*);
void check_for_cgi(struct pollfd*, struct client*);
void mark_nonblock(int);
void handle_handshake(struct pollfd*, struct client*);
void handle_open_conn(struct pollfd*, struct client*);
void start_reply(struct pollfd*, struct client*, int, const char*);
void start_cgi(const char*, const char*, const char*, struct pollfd*, struct client*);
void start_cgi(const char*, const char*, struct pollfd*, struct client*);
void send_file(struct pollfd*, struct client*);
void open_dir(struct pollfd*, struct client*);
void redirect_canonical_dir(struct pollfd*, struct client*);
@ -226,6 +226,9 @@ void loop(struct tls*, int, int);
/* ex.c */
int send_string(int, const char*);
int recv_string(int, char**);
int send_iri(int, struct iri*);
int recv_iri(int, struct iri*);
void free_recvd_iri(struct iri*);
int send_vhost(int, struct vhost*);
int recv_vhost(int, struct vhost**);
int send_fd(int, int);
@ -242,6 +245,7 @@ char *utf8_nth(char*, size_t);
/* iri.c */
int parse_iri(char*, struct iri*, const char**);
int trim_req_iri(char*, const char **);
int serialize_iri(struct iri*, char*, size_t);
/* puny.c */
int puny_decode(const char*, char*, size_t, const char**);
@ -250,5 +254,6 @@ int puny_decode(const char*, char*, size_t, const char**);
int starts_with(const char*, const char*);
int ends_with(const char*, const char*);
ssize_t filesize(int);
char *absolutify_path(const char*);
#endif

28
iri.c
View File

@ -377,3 +377,31 @@ trim_req_iri(char *iri, const char **err)
*i = '\0';
return 1;
}
int
serialize_iri(struct iri *i, char *buf, size_t len)
{
size_t l;
/* in ex.c we receive empty "" strings as NULL */
if (i->schema == NULL || i->host == NULL) {
memset(buf, 0, len);
return 0;
}
strlcpy(buf, i->schema, len);
strlcat(buf, "://", len);
strlcat(buf, i->host, len);
strlcat(buf, "/", len);
if (i->path != NULL)
l = strlcat(buf, i->path, len);
if (i->query != NULL && *i->query != '\0') {
strlcat(buf, "?", len);
l = strlcat(buf, i->query, len);
}
return l < len;
}

View File

@ -23,6 +23,7 @@
#include <errno.h>
#include <fcntl.h>
#include <fnmatch.h>
#include <limits.h>
#include <string.h>
#include "gmid.h"
@ -150,7 +151,7 @@ open_file(struct pollfd *fds, struct client *c)
switch (check_path(c, c->iri.path, &c->fd)) {
case FILE_EXECUTABLE:
if (starts_with(c->iri.path, c->host->cgi)) {
start_cgi(c->iri.path, "", c->iri.query, fds, c);
start_cgi(c->iri.path, "", fds, c);
return;
}
@ -166,7 +167,7 @@ open_file(struct pollfd *fds, struct client *c)
case FILE_MISSING:
if (c->host->cgi != NULL && starts_with(c->iri.path, c->host->cgi)) {
check_for_cgi(c->iri.path, c->iri.query, fds, c);
check_for_cgi(fds, c);
return;
}
start_reply(fds, c, NOT_FOUND, "not found");
@ -206,9 +207,12 @@ load_file(struct pollfd *fds, struct client *c)
* executable is found or we emptied the path.
*/
void
check_for_cgi(char *path, char *query, struct pollfd *fds, struct client *c)
check_for_cgi(struct pollfd *fds, struct client *c)
{
char path[PATH_MAX];
char *end;
strlcpy(path, c->iri.path, sizeof(path));
end = strchr(path, '\0');
/* NB: assume CGI is enabled and path matches cgi */
@ -223,7 +227,7 @@ check_for_cgi(char *path, char *query, struct pollfd *fds, struct client *c)
switch (check_path(c, path, &c->fd)) {
case FILE_EXECUTABLE:
start_cgi(path, end+1, query, fds,c);
start_cgi(path, end+1, fds, c);
return;
case FILE_MISSING:
break;
@ -396,7 +400,7 @@ start_reply(struct pollfd *pfd, struct client *c, int code, const char *meta)
}
void
start_cgi(const char *spath, const char *relpath, const char *query,
start_cgi(const char *spath, const char *relpath,
struct pollfd *fds, struct client *c)
{
char addr[NI_MAXHOST];
@ -420,9 +424,9 @@ start_cgi(const char *spath, const char *relpath, const char *query,
chash = NULL;
}
if (!send_string(exfd, spath)
if (!send_iri(exfd, &c->iri)
|| !send_string(exfd, spath)
|| !send_string(exfd, relpath)
|| !send_string(exfd, query)
|| !send_string(exfd, addr)
|| !send_string(exfd, ruser)
|| !send_string(exfd, cissuer)
@ -515,7 +519,7 @@ open_dir(struct pollfd *fds, struct client *c)
switch (check_path(c, c->iri.path, &c->fd)) {
case FILE_EXECUTABLE:
if (starts_with(c->iri.path, c->host->cgi)) {
start_cgi(c->iri.path, "", c->iri.query, fds, c);
start_cgi(c->iri.path, "", fds, c);
break;
}