2 * Copyright (c) 2021 Omar Polo <op@omarpolo.com>
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
27 send_string(int fd, const char *str)
36 if (write(fd, &len, sizeof(len)) != sizeof(len))
40 if (write(fd, str, len) != len)
47 recv_string(int fd, char **ret)
51 if (read(fd, &len, sizeof(len)) != sizeof(len))
59 if ((*ret = calloc(1, len+1)) == NULL)
62 if (read(fd, *ret, len) != len)
68 send_vhost(int fd, struct vhost *vhost)
72 if (vhost < hosts || vhost > hosts + HOSTSLEN)
76 return write(fd, &n, sizeof(n)) == sizeof(n);
80 recv_vhost(int fd, struct vhost **vhost)
84 if (read(fd, &n, sizeof(n)) != sizeof(n))
87 if (n < 0 || n > HOSTSLEN)
91 if ((*vhost)->domain == NULL)
96 /* send d though fd. see /usr/src/usr.sbin/syslogd/privsep_fdpass.c
99 send_fd(int fd, int d)
104 unsigned char buf[CMSG_SPACE(sizeof(int))];
106 struct cmsghdr *cmsg;
111 memset(&msg, 0, sizeof(msg));
114 msg.msg_control = &cmsgbuf.buf;
115 msg.msg_controllen = sizeof(cmsgbuf.buf);
116 cmsg = CMSG_FIRSTHDR(&msg);
117 cmsg->cmsg_len = CMSG_LEN(sizeof(int));
118 cmsg->cmsg_level = SOL_SOCKET;
119 cmsg->cmsg_type = SCM_RIGHTS;
120 *(int*)CMSG_DATA(cmsg) = d;
124 vec.iov_base = &result;
125 vec.iov_len = sizeof(int);
129 if ((n = sendmsg(fd, &msg, 0)) == -1 || n != sizeof(int)) {
130 fprintf(stderr, "sendmsg: got %zu but wanted %zu: (errno) %s",
131 n, sizeof(int), strerror(errno));
137 /* receive a descriptor via fd */
144 char buf[CMSG_SPACE(sizeof(int))];
146 struct cmsghdr *cmsg;
151 memset(&msg, 0, sizeof(msg));
152 vec.iov_base = &result;
153 vec.iov_len = sizeof(int);
156 msg.msg_control = &cmsgbuf.buf;
157 msg.msg_controllen = sizeof(cmsgbuf.buf);
159 if ((n = recvmsg(fd, &msg, 0)) != sizeof(int)) {
160 fprintf(stderr, "read %zu bytes bu wanted %zu\n", n, sizeof(int));
165 cmsg = CMSG_FIRSTHDR(&msg);
166 if (cmsg == NULL || cmsg->cmsg_type != SCM_RIGHTS)
168 return (*(int *)CMSG_DATA(cmsg));
174 safe_setenv(const char *name, const char *val)
178 setenv(name, val, 1);
181 /* fd or -1 on error */
183 launch_cgi(const char *spath, const char *relpath, const char *query,
184 const char *addr, const char *ruser, const char *cissuer, const char *chash,
187 int p[2]; /* read end, write end */
189 if (pipe2(p, O_NONBLOCK) == -1)
196 case 0: { /* child */
197 char *portno, *ex, *requri;
198 char *argv[] = { NULL, NULL, NULL };
201 if (dup2(p[1], 1) == -1)
204 if (asprintf(&portno, "%d", conf.port) == -1)
207 if (asprintf(&ex, "%s/%s", vhost->dir, spath) == -1)
210 if (asprintf(&requri, "%s%s%s", spath,
211 (relpath != NULL && *relpath == '\0') ? "" : "/",
212 (relpath != NULL ? relpath : "")) == -1)
215 argv[0] = argv[1] = ex;
217 safe_setenv("GATEWAY_INTERFACE", "CGI/1.1");
218 safe_setenv("SERVER_SOFTWARE", "gmid");
219 safe_setenv("SERVER_PORT", portno);
221 if (!strcmp(vhost->domain, "*"))
222 safe_setenv("SERVER_NAME", vhost->domain);
224 safe_setenv("SCRIPT_NAME", spath);
225 safe_setenv("SCRIPT_EXECUTABLE", ex);
226 safe_setenv("REQUEST_URI", requri);
227 safe_setenv("REQUEST_RELATIVE", relpath);
228 safe_setenv("QUERY_STRING", query);
229 safe_setenv("REMOTE_HOST", addr);
230 safe_setenv("REMOTE_ADDR", addr);
231 safe_setenv("DOCUMENT_ROOT", vhost->dir);
234 safe_setenv("AUTH_TYPE", "Certificate");
235 safe_setenv("REMOTE_USER", ruser);
236 safe_setenv("TLS_CLIENT_ISSUER", cissuer);
237 safe_setenv("TLS_CLIENT_HASH", chash);
250 dprintf(p[1], "%d internal server error\r\n", TEMP_FAILURE);
255 executor_main(int fd)
257 char *spath, *relpath, *query, *addr, *ruser, *cissuer, *chash;
262 pledge("stdio sendfd proc exec", NULL);
266 if (!recv_string(fd, &spath)
267 || !recv_string(fd, &relpath)
268 || !recv_string(fd, &query)
269 || !recv_string(fd, &addr)
270 || !recv_string(fd, &ruser)
271 || !recv_string(fd, &cissuer)
272 || !recv_string(fd, &chash)
273 || !recv_vhost(fd, &vhost))
276 d = launch_cgi(spath, relpath, query,
277 addr, ruser, cissuer, chash, vhost);
291 /* kill all process in my group. This means the listener and
292 * every pending CGI script. */