1b077aed3SPierre Pronchery /*
2b077aed3SPierre Pronchery * Copyright 1995-2022 The OpenSSL Project Authors. All Rights Reserved.
3b077aed3SPierre Pronchery *
4b077aed3SPierre Pronchery * Licensed under the Apache License 2.0 (the "License"). You may not use
5b077aed3SPierre Pronchery * this file except in compliance with the License. You can obtain a copy
6b077aed3SPierre Pronchery * in the file LICENSE in the source distribution or at
7b077aed3SPierre Pronchery * https://www.openssl.org/source/license.html
8b077aed3SPierre Pronchery */
9b077aed3SPierre Pronchery
10b077aed3SPierre Pronchery /* Very basic HTTP server */
11b077aed3SPierre Pronchery
12b077aed3SPierre Pronchery #if !defined(_POSIX_C_SOURCE) && defined(OPENSSL_SYS_VMS)
13b077aed3SPierre Pronchery /*
14b077aed3SPierre Pronchery * On VMS, you need to define this to get the declaration of fileno(). The
15b077aed3SPierre Pronchery * value 2 is to make sure no function defined in POSIX-2 is left undefined.
16b077aed3SPierre Pronchery */
17b077aed3SPierre Pronchery # define _POSIX_C_SOURCE 2
18b077aed3SPierre Pronchery #endif
19b077aed3SPierre Pronchery
20b077aed3SPierre Pronchery #include <string.h>
21b077aed3SPierre Pronchery #include <ctype.h>
22b077aed3SPierre Pronchery #include "http_server.h"
23b077aed3SPierre Pronchery #include "internal/sockets.h"
24b077aed3SPierre Pronchery #include <openssl/err.h>
25b077aed3SPierre Pronchery #include <openssl/rand.h>
26b077aed3SPierre Pronchery #include "s_apps.h"
27b077aed3SPierre Pronchery
28b077aed3SPierre Pronchery #if defined(__TANDEM)
29b077aed3SPierre Pronchery # if defined(OPENSSL_TANDEM_FLOSS)
30b077aed3SPierre Pronchery # include <floss.h(floss_fork)>
31b077aed3SPierre Pronchery # endif
32b077aed3SPierre Pronchery #endif
33b077aed3SPierre Pronchery
34b077aed3SPierre Pronchery static int verbosity = LOG_INFO;
35b077aed3SPierre Pronchery
36b077aed3SPierre Pronchery #define HTTP_PREFIX "HTTP/"
37b077aed3SPierre Pronchery #define HTTP_VERSION_PATT "1." /* allow 1.x */
38b077aed3SPierre Pronchery #define HTTP_PREFIX_VERSION HTTP_PREFIX""HTTP_VERSION_PATT
39b077aed3SPierre Pronchery #define HTTP_1_0 HTTP_PREFIX_VERSION"0" /* "HTTP/1.0" */
40b077aed3SPierre Pronchery
41b077aed3SPierre Pronchery #ifdef HTTP_DAEMON
42b077aed3SPierre Pronchery
43b077aed3SPierre Pronchery int multi = 0; /* run multiple responder processes */
44b077aed3SPierre Pronchery int acfd = (int) INVALID_SOCKET;
45b077aed3SPierre Pronchery
print_syslog(const char * str,size_t len,void * levPtr)46b077aed3SPierre Pronchery static int print_syslog(const char *str, size_t len, void *levPtr)
47b077aed3SPierre Pronchery {
48b077aed3SPierre Pronchery int level = *(int *)levPtr;
49b077aed3SPierre Pronchery int ilen = len > MAXERRLEN ? MAXERRLEN : len;
50b077aed3SPierre Pronchery
51b077aed3SPierre Pronchery syslog(level, "%.*s", ilen, str);
52b077aed3SPierre Pronchery
53b077aed3SPierre Pronchery return ilen;
54b077aed3SPierre Pronchery }
55b077aed3SPierre Pronchery #endif
56b077aed3SPierre Pronchery
log_message(const char * prog,int level,const char * fmt,...)57b077aed3SPierre Pronchery void log_message(const char *prog, int level, const char *fmt, ...)
58b077aed3SPierre Pronchery {
59b077aed3SPierre Pronchery va_list ap;
60b077aed3SPierre Pronchery
61b077aed3SPierre Pronchery if (verbosity < level)
62b077aed3SPierre Pronchery return;
63b077aed3SPierre Pronchery
64b077aed3SPierre Pronchery va_start(ap, fmt);
65b077aed3SPierre Pronchery #ifdef HTTP_DAEMON
66b077aed3SPierre Pronchery if (multi) {
67b077aed3SPierre Pronchery char buf[1024];
68b077aed3SPierre Pronchery
69b077aed3SPierre Pronchery if (vsnprintf(buf, sizeof(buf), fmt, ap) > 0)
70b077aed3SPierre Pronchery syslog(level, "%s", buf);
71b077aed3SPierre Pronchery if (level <= LOG_ERR)
72b077aed3SPierre Pronchery ERR_print_errors_cb(print_syslog, &level);
73b077aed3SPierre Pronchery } else
74b077aed3SPierre Pronchery #endif
75b077aed3SPierre Pronchery {
76b077aed3SPierre Pronchery BIO_printf(bio_err, "%s: ", prog);
77b077aed3SPierre Pronchery BIO_vprintf(bio_err, fmt, ap);
78b077aed3SPierre Pronchery BIO_printf(bio_err, "\n");
79b077aed3SPierre Pronchery (void)BIO_flush(bio_err);
80b077aed3SPierre Pronchery }
81b077aed3SPierre Pronchery va_end(ap);
82b077aed3SPierre Pronchery }
83b077aed3SPierre Pronchery
84b077aed3SPierre Pronchery #ifdef HTTP_DAEMON
socket_timeout(int signum)85b077aed3SPierre Pronchery void socket_timeout(int signum)
86b077aed3SPierre Pronchery {
87b077aed3SPierre Pronchery if (acfd != (int)INVALID_SOCKET)
88b077aed3SPierre Pronchery (void)shutdown(acfd, SHUT_RD);
89b077aed3SPierre Pronchery }
90b077aed3SPierre Pronchery
killall(int ret,pid_t * kidpids)91b077aed3SPierre Pronchery static void killall(int ret, pid_t *kidpids)
92b077aed3SPierre Pronchery {
93b077aed3SPierre Pronchery int i;
94b077aed3SPierre Pronchery
95b077aed3SPierre Pronchery for (i = 0; i < multi; ++i)
96b077aed3SPierre Pronchery if (kidpids[i] != 0)
97b077aed3SPierre Pronchery (void)kill(kidpids[i], SIGTERM);
98b077aed3SPierre Pronchery OPENSSL_free(kidpids);
99b077aed3SPierre Pronchery ossl_sleep(1000);
100b077aed3SPierre Pronchery exit(ret);
101b077aed3SPierre Pronchery }
102b077aed3SPierre Pronchery
103b077aed3SPierre Pronchery static int termsig = 0;
104b077aed3SPierre Pronchery
noteterm(int sig)105b077aed3SPierre Pronchery static void noteterm(int sig)
106b077aed3SPierre Pronchery {
107b077aed3SPierre Pronchery termsig = sig;
108b077aed3SPierre Pronchery }
109b077aed3SPierre Pronchery
110b077aed3SPierre Pronchery /*
111b077aed3SPierre Pronchery * Loop spawning up to `multi` child processes, only child processes return
112b077aed3SPierre Pronchery * from this function. The parent process loops until receiving a termination
113b077aed3SPierre Pronchery * signal, kills extant children and exits without returning.
114b077aed3SPierre Pronchery */
spawn_loop(const char * prog)115b077aed3SPierre Pronchery void spawn_loop(const char *prog)
116b077aed3SPierre Pronchery {
117b077aed3SPierre Pronchery pid_t *kidpids = NULL;
118b077aed3SPierre Pronchery int status;
119b077aed3SPierre Pronchery int procs = 0;
120b077aed3SPierre Pronchery int i;
121b077aed3SPierre Pronchery
122b077aed3SPierre Pronchery openlog(prog, LOG_PID, LOG_DAEMON);
123b077aed3SPierre Pronchery
124b077aed3SPierre Pronchery if (setpgid(0, 0)) {
125b077aed3SPierre Pronchery syslog(LOG_ERR, "fatal: error detaching from parent process group: %s",
126b077aed3SPierre Pronchery strerror(errno));
127b077aed3SPierre Pronchery exit(1);
128b077aed3SPierre Pronchery }
129b077aed3SPierre Pronchery kidpids = app_malloc(multi * sizeof(*kidpids), "child PID array");
130b077aed3SPierre Pronchery for (i = 0; i < multi; ++i)
131b077aed3SPierre Pronchery kidpids[i] = 0;
132b077aed3SPierre Pronchery
133b077aed3SPierre Pronchery signal(SIGINT, noteterm);
134b077aed3SPierre Pronchery signal(SIGTERM, noteterm);
135b077aed3SPierre Pronchery
136b077aed3SPierre Pronchery while (termsig == 0) {
137b077aed3SPierre Pronchery pid_t fpid;
138b077aed3SPierre Pronchery
139b077aed3SPierre Pronchery /*
140b077aed3SPierre Pronchery * Wait for a child to replace when we're at the limit.
141b077aed3SPierre Pronchery * Slow down if a child exited abnormally or waitpid() < 0
142b077aed3SPierre Pronchery */
143b077aed3SPierre Pronchery while (termsig == 0 && procs >= multi) {
144b077aed3SPierre Pronchery if ((fpid = waitpid(-1, &status, 0)) > 0) {
145b077aed3SPierre Pronchery for (i = 0; i < procs; ++i) {
146b077aed3SPierre Pronchery if (kidpids[i] == fpid) {
147b077aed3SPierre Pronchery kidpids[i] = 0;
148b077aed3SPierre Pronchery --procs;
149b077aed3SPierre Pronchery break;
150b077aed3SPierre Pronchery }
151b077aed3SPierre Pronchery }
152b077aed3SPierre Pronchery if (i >= multi) {
153b077aed3SPierre Pronchery syslog(LOG_ERR, "fatal: internal error: "
154b077aed3SPierre Pronchery "no matching child slot for pid: %ld",
155b077aed3SPierre Pronchery (long) fpid);
156b077aed3SPierre Pronchery killall(1, kidpids);
157b077aed3SPierre Pronchery }
158b077aed3SPierre Pronchery if (status != 0) {
159b077aed3SPierre Pronchery if (WIFEXITED(status))
160b077aed3SPierre Pronchery syslog(LOG_WARNING, "child process: %ld, exit status: %d",
161b077aed3SPierre Pronchery (long)fpid, WEXITSTATUS(status));
162b077aed3SPierre Pronchery else if (WIFSIGNALED(status))
163b077aed3SPierre Pronchery syslog(LOG_WARNING, "child process: %ld, term signal %d%s",
164b077aed3SPierre Pronchery (long)fpid, WTERMSIG(status),
165b077aed3SPierre Pronchery # ifdef WCOREDUMP
166b077aed3SPierre Pronchery WCOREDUMP(status) ? " (core dumped)" :
167b077aed3SPierre Pronchery # endif
168b077aed3SPierre Pronchery "");
169b077aed3SPierre Pronchery ossl_sleep(1000);
170b077aed3SPierre Pronchery }
171b077aed3SPierre Pronchery break;
172b077aed3SPierre Pronchery } else if (errno != EINTR) {
173b077aed3SPierre Pronchery syslog(LOG_ERR, "fatal: waitpid(): %s", strerror(errno));
174b077aed3SPierre Pronchery killall(1, kidpids);
175b077aed3SPierre Pronchery }
176b077aed3SPierre Pronchery }
177b077aed3SPierre Pronchery if (termsig)
178b077aed3SPierre Pronchery break;
179b077aed3SPierre Pronchery
180b077aed3SPierre Pronchery switch (fpid = fork()) {
181b077aed3SPierre Pronchery case -1: /* error */
182b077aed3SPierre Pronchery /* System critically low on memory, pause and try again later */
183b077aed3SPierre Pronchery ossl_sleep(30000);
184b077aed3SPierre Pronchery break;
185b077aed3SPierre Pronchery case 0: /* child */
186b077aed3SPierre Pronchery OPENSSL_free(kidpids);
187b077aed3SPierre Pronchery signal(SIGINT, SIG_DFL);
188b077aed3SPierre Pronchery signal(SIGTERM, SIG_DFL);
189b077aed3SPierre Pronchery if (termsig)
190b077aed3SPierre Pronchery _exit(0);
191b077aed3SPierre Pronchery if (RAND_poll() <= 0) {
192b077aed3SPierre Pronchery syslog(LOG_ERR, "fatal: RAND_poll() failed");
193b077aed3SPierre Pronchery _exit(1);
194b077aed3SPierre Pronchery }
195b077aed3SPierre Pronchery return;
196b077aed3SPierre Pronchery default: /* parent */
197b077aed3SPierre Pronchery for (i = 0; i < multi; ++i) {
198b077aed3SPierre Pronchery if (kidpids[i] == 0) {
199b077aed3SPierre Pronchery kidpids[i] = fpid;
200b077aed3SPierre Pronchery procs++;
201b077aed3SPierre Pronchery break;
202b077aed3SPierre Pronchery }
203b077aed3SPierre Pronchery }
204b077aed3SPierre Pronchery if (i >= multi) {
205b077aed3SPierre Pronchery syslog(LOG_ERR, "fatal: internal error: no free child slots");
206b077aed3SPierre Pronchery killall(1, kidpids);
207b077aed3SPierre Pronchery }
208b077aed3SPierre Pronchery break;
209b077aed3SPierre Pronchery }
210b077aed3SPierre Pronchery }
211b077aed3SPierre Pronchery
212b077aed3SPierre Pronchery /* The loop above can only break on termsig */
213b077aed3SPierre Pronchery syslog(LOG_INFO, "terminating on signal: %d", termsig);
214b077aed3SPierre Pronchery killall(0, kidpids);
215b077aed3SPierre Pronchery }
216b077aed3SPierre Pronchery #endif
217b077aed3SPierre Pronchery
218b077aed3SPierre Pronchery #ifndef OPENSSL_NO_SOCK
http_server_init_bio(const char * prog,const char * port)219b077aed3SPierre Pronchery BIO *http_server_init_bio(const char *prog, const char *port)
220b077aed3SPierre Pronchery {
221b077aed3SPierre Pronchery BIO *acbio = NULL, *bufbio;
222b077aed3SPierre Pronchery int asock;
223*0d0c8621SEnji Cooper char name[40];
224b077aed3SPierre Pronchery
225*0d0c8621SEnji Cooper snprintf(name, sizeof(name), "[::]:%s", port); /* port may be "0" */
226b077aed3SPierre Pronchery bufbio = BIO_new(BIO_f_buffer());
227b077aed3SPierre Pronchery if (bufbio == NULL)
228b077aed3SPierre Pronchery goto err;
229b077aed3SPierre Pronchery acbio = BIO_new(BIO_s_accept());
230b077aed3SPierre Pronchery if (acbio == NULL
231*0d0c8621SEnji Cooper || BIO_set_accept_ip_family(acbio, BIO_FAMILY_IPANY) <= 0 /* IPv4/6 */
232*0d0c8621SEnji Cooper || BIO_set_bind_mode(acbio, BIO_BIND_REUSEADDR) <= 0
233*0d0c8621SEnji Cooper || BIO_set_accept_name(acbio, name) <= 0) {
234b077aed3SPierre Pronchery log_message(prog, LOG_ERR, "Error setting up accept BIO");
235b077aed3SPierre Pronchery goto err;
236b077aed3SPierre Pronchery }
237b077aed3SPierre Pronchery
238b077aed3SPierre Pronchery BIO_set_accept_bios(acbio, bufbio);
239b077aed3SPierre Pronchery bufbio = NULL;
240b077aed3SPierre Pronchery if (BIO_do_accept(acbio) <= 0) {
241b077aed3SPierre Pronchery log_message(prog, LOG_ERR, "Error starting accept");
242b077aed3SPierre Pronchery goto err;
243b077aed3SPierre Pronchery }
244b077aed3SPierre Pronchery
245b077aed3SPierre Pronchery /* Report back what address and port are used */
246b077aed3SPierre Pronchery BIO_get_fd(acbio, &asock);
247b077aed3SPierre Pronchery if (!report_server_accept(bio_out, asock, 1, 1)) {
248b077aed3SPierre Pronchery log_message(prog, LOG_ERR, "Error printing ACCEPT string");
249b077aed3SPierre Pronchery goto err;
250b077aed3SPierre Pronchery }
251b077aed3SPierre Pronchery
252b077aed3SPierre Pronchery return acbio;
253b077aed3SPierre Pronchery
254b077aed3SPierre Pronchery err:
255b077aed3SPierre Pronchery BIO_free_all(acbio);
256b077aed3SPierre Pronchery BIO_free(bufbio);
257b077aed3SPierre Pronchery return NULL;
258b077aed3SPierre Pronchery }
259b077aed3SPierre Pronchery
260b077aed3SPierre Pronchery /*
261b077aed3SPierre Pronchery * Decode %xx URL-decoding in-place. Ignores malformed sequences.
262b077aed3SPierre Pronchery */
urldecode(char * p)263b077aed3SPierre Pronchery static int urldecode(char *p)
264b077aed3SPierre Pronchery {
265b077aed3SPierre Pronchery unsigned char *out = (unsigned char *)p;
266b077aed3SPierre Pronchery unsigned char *save = out;
267b077aed3SPierre Pronchery
268b077aed3SPierre Pronchery for (; *p; p++) {
269b077aed3SPierre Pronchery if (*p != '%') {
270b077aed3SPierre Pronchery *out++ = *p;
271b077aed3SPierre Pronchery } else if (isxdigit(_UC(p[1])) && isxdigit(_UC(p[2]))) {
272b077aed3SPierre Pronchery /* Don't check, can't fail because of ixdigit() call. */
273b077aed3SPierre Pronchery *out++ = (OPENSSL_hexchar2int(p[1]) << 4)
274b077aed3SPierre Pronchery | OPENSSL_hexchar2int(p[2]);
275b077aed3SPierre Pronchery p += 2;
276b077aed3SPierre Pronchery } else {
277b077aed3SPierre Pronchery return -1;
278b077aed3SPierre Pronchery }
279b077aed3SPierre Pronchery }
280b077aed3SPierre Pronchery *out = '\0';
281b077aed3SPierre Pronchery return (int)(out - save);
282b077aed3SPierre Pronchery }
283b077aed3SPierre Pronchery
284b077aed3SPierre Pronchery /* if *pcbio != NULL, continue given connected session, else accept new */
285b077aed3SPierre Pronchery /* if found_keep_alive != NULL, return this way connection persistence state */
http_server_get_asn1_req(const ASN1_ITEM * it,ASN1_VALUE ** preq,char ** ppath,BIO ** pcbio,BIO * acbio,int * found_keep_alive,const char * prog,const char * port,int accept_get,int timeout)286b077aed3SPierre Pronchery int http_server_get_asn1_req(const ASN1_ITEM *it, ASN1_VALUE **preq,
287b077aed3SPierre Pronchery char **ppath, BIO **pcbio, BIO *acbio,
288b077aed3SPierre Pronchery int *found_keep_alive,
289b077aed3SPierre Pronchery const char *prog, const char *port,
290b077aed3SPierre Pronchery int accept_get, int timeout)
291b077aed3SPierre Pronchery {
292b077aed3SPierre Pronchery BIO *cbio = *pcbio, *getbio = NULL, *b64 = NULL;
293b077aed3SPierre Pronchery int len;
294b077aed3SPierre Pronchery char reqbuf[2048], inbuf[2048];
295b077aed3SPierre Pronchery char *meth, *url, *end;
296b077aed3SPierre Pronchery ASN1_VALUE *req;
297b077aed3SPierre Pronchery int ret = 0;
298b077aed3SPierre Pronchery
299b077aed3SPierre Pronchery *preq = NULL;
300b077aed3SPierre Pronchery if (ppath != NULL)
301b077aed3SPierre Pronchery *ppath = NULL;
302b077aed3SPierre Pronchery
303b077aed3SPierre Pronchery if (cbio == NULL) {
304b077aed3SPierre Pronchery log_message(prog, LOG_DEBUG,
305b077aed3SPierre Pronchery "Awaiting new connection on port %s...", port);
306b077aed3SPierre Pronchery if (BIO_do_accept(acbio) <= 0)
307b077aed3SPierre Pronchery /* Connection loss before accept() is routine, ignore silently */
308b077aed3SPierre Pronchery return ret;
309b077aed3SPierre Pronchery
310b077aed3SPierre Pronchery *pcbio = cbio = BIO_pop(acbio);
311b077aed3SPierre Pronchery } else {
312b077aed3SPierre Pronchery log_message(prog, LOG_DEBUG, "Awaiting next request...");
313b077aed3SPierre Pronchery }
314b077aed3SPierre Pronchery if (cbio == NULL) {
315b077aed3SPierre Pronchery /* Cannot call http_server_send_status(cbio, ...) */
316b077aed3SPierre Pronchery ret = -1;
317b077aed3SPierre Pronchery goto out;
318b077aed3SPierre Pronchery }
319b077aed3SPierre Pronchery
320b077aed3SPierre Pronchery # ifdef HTTP_DAEMON
321b077aed3SPierre Pronchery if (timeout > 0) {
322b077aed3SPierre Pronchery (void)BIO_get_fd(cbio, &acfd);
323b077aed3SPierre Pronchery alarm(timeout);
324b077aed3SPierre Pronchery }
325b077aed3SPierre Pronchery # endif
326b077aed3SPierre Pronchery
327b077aed3SPierre Pronchery /* Read the request line. */
328b077aed3SPierre Pronchery len = BIO_gets(cbio, reqbuf, sizeof(reqbuf));
329b077aed3SPierre Pronchery if (len == 0)
330b077aed3SPierre Pronchery return ret;
331b077aed3SPierre Pronchery ret = 1;
332b077aed3SPierre Pronchery if (len < 0) {
333b077aed3SPierre Pronchery log_message(prog, LOG_WARNING, "Request line read error");
334b077aed3SPierre Pronchery (void)http_server_send_status(cbio, 400, "Bad Request");
335b077aed3SPierre Pronchery goto out;
336b077aed3SPierre Pronchery }
337b077aed3SPierre Pronchery if ((end = strchr(reqbuf, '\r')) != NULL
338b077aed3SPierre Pronchery || (end = strchr(reqbuf, '\n')) != NULL)
339b077aed3SPierre Pronchery *end = '\0';
340b077aed3SPierre Pronchery log_message(prog, LOG_INFO, "Received request, 1st line: %s", reqbuf);
341b077aed3SPierre Pronchery
342b077aed3SPierre Pronchery meth = reqbuf;
343b077aed3SPierre Pronchery url = meth + 3;
344b077aed3SPierre Pronchery if ((accept_get && strncmp(meth, "GET ", 4) == 0)
345b077aed3SPierre Pronchery || (url++, strncmp(meth, "POST ", 5) == 0)) {
346b077aed3SPierre Pronchery static const char http_version_str[] = " "HTTP_PREFIX_VERSION;
347b077aed3SPierre Pronchery static const size_t http_version_str_len = sizeof(http_version_str) - 1;
348b077aed3SPierre Pronchery
349b077aed3SPierre Pronchery /* Expecting (GET|POST) {sp} /URL {sp} HTTP/1.x */
350b077aed3SPierre Pronchery *(url++) = '\0';
351b077aed3SPierre Pronchery while (*url == ' ')
352b077aed3SPierre Pronchery url++;
353b077aed3SPierre Pronchery if (*url != '/') {
354b077aed3SPierre Pronchery log_message(prog, LOG_WARNING,
355b077aed3SPierre Pronchery "Invalid %s -- URL does not begin with '/': %s",
356b077aed3SPierre Pronchery meth, url);
357b077aed3SPierre Pronchery (void)http_server_send_status(cbio, 400, "Bad Request");
358b077aed3SPierre Pronchery goto out;
359b077aed3SPierre Pronchery }
360b077aed3SPierre Pronchery url++;
361b077aed3SPierre Pronchery
362b077aed3SPierre Pronchery /* Splice off the HTTP version identifier. */
363b077aed3SPierre Pronchery for (end = url; *end != '\0'; end++)
364b077aed3SPierre Pronchery if (*end == ' ')
365b077aed3SPierre Pronchery break;
366b077aed3SPierre Pronchery if (strncmp(end, http_version_str, http_version_str_len) != 0) {
367b077aed3SPierre Pronchery log_message(prog, LOG_WARNING,
368b077aed3SPierre Pronchery "Invalid %s -- bad HTTP/version string: %s",
369b077aed3SPierre Pronchery meth, end + 1);
370b077aed3SPierre Pronchery (void)http_server_send_status(cbio, 400, "Bad Request");
371b077aed3SPierre Pronchery goto out;
372b077aed3SPierre Pronchery }
373b077aed3SPierre Pronchery *end = '\0';
374b077aed3SPierre Pronchery /* above HTTP 1.0, connection persistence is the default */
375b077aed3SPierre Pronchery if (found_keep_alive != NULL)
376b077aed3SPierre Pronchery *found_keep_alive = end[http_version_str_len] > '0';
377b077aed3SPierre Pronchery
378b077aed3SPierre Pronchery /*-
379b077aed3SPierre Pronchery * Skip "GET / HTTP..." requests often used by load-balancers.
380b077aed3SPierre Pronchery * 'url' was incremented above to point to the first byte *after*
381b077aed3SPierre Pronchery * the leading slash, so in case 'GET / ' it is now an empty string.
382b077aed3SPierre Pronchery */
383b077aed3SPierre Pronchery if (strlen(meth) == 3 && url[0] == '\0') {
384b077aed3SPierre Pronchery (void)http_server_send_status(cbio, 200, "OK");
385b077aed3SPierre Pronchery goto out;
386b077aed3SPierre Pronchery }
387b077aed3SPierre Pronchery
388b077aed3SPierre Pronchery len = urldecode(url);
389b077aed3SPierre Pronchery if (len < 0) {
390b077aed3SPierre Pronchery log_message(prog, LOG_WARNING,
391b077aed3SPierre Pronchery "Invalid %s request -- bad URL encoding: %s",
392b077aed3SPierre Pronchery meth, url);
393b077aed3SPierre Pronchery (void)http_server_send_status(cbio, 400, "Bad Request");
394b077aed3SPierre Pronchery goto out;
395b077aed3SPierre Pronchery }
396b077aed3SPierre Pronchery if (strlen(meth) == 3) { /* GET */
397b077aed3SPierre Pronchery if ((getbio = BIO_new_mem_buf(url, len)) == NULL
398b077aed3SPierre Pronchery || (b64 = BIO_new(BIO_f_base64())) == NULL) {
399b077aed3SPierre Pronchery log_message(prog, LOG_ERR,
400b077aed3SPierre Pronchery "Could not allocate base64 bio with size = %d",
401b077aed3SPierre Pronchery len);
402b077aed3SPierre Pronchery goto fatal;
403b077aed3SPierre Pronchery }
404b077aed3SPierre Pronchery BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL);
405b077aed3SPierre Pronchery getbio = BIO_push(b64, getbio);
406b077aed3SPierre Pronchery }
407b077aed3SPierre Pronchery } else {
408b077aed3SPierre Pronchery log_message(prog, LOG_WARNING,
409b077aed3SPierre Pronchery "HTTP request does not begin with %sPOST: %s",
410b077aed3SPierre Pronchery accept_get ? "GET or " : "", reqbuf);
411b077aed3SPierre Pronchery (void)http_server_send_status(cbio, 400, "Bad Request");
412b077aed3SPierre Pronchery goto out;
413b077aed3SPierre Pronchery }
414b077aed3SPierre Pronchery
415b077aed3SPierre Pronchery /* chop any further/duplicate leading or trailing '/' */
416b077aed3SPierre Pronchery while (*url == '/')
417b077aed3SPierre Pronchery url++;
418b077aed3SPierre Pronchery while (end >= url + 2 && end[-2] == '/' && end[-1] == '/')
419b077aed3SPierre Pronchery end--;
420b077aed3SPierre Pronchery *end = '\0';
421b077aed3SPierre Pronchery
422b077aed3SPierre Pronchery /* Read and skip past the headers. */
423b077aed3SPierre Pronchery for (;;) {
424b077aed3SPierre Pronchery char *key, *value, *line_end = NULL;
425b077aed3SPierre Pronchery
426b077aed3SPierre Pronchery len = BIO_gets(cbio, inbuf, sizeof(inbuf));
427b077aed3SPierre Pronchery if (len <= 0) {
428b077aed3SPierre Pronchery log_message(prog, LOG_WARNING, "Error reading HTTP header");
429b077aed3SPierre Pronchery (void)http_server_send_status(cbio, 400, "Bad Request");
430b077aed3SPierre Pronchery goto out;
431b077aed3SPierre Pronchery }
432b077aed3SPierre Pronchery
433b077aed3SPierre Pronchery if (inbuf[0] == '\r' || inbuf[0] == '\n')
434b077aed3SPierre Pronchery break;
435b077aed3SPierre Pronchery
436b077aed3SPierre Pronchery key = inbuf;
437b077aed3SPierre Pronchery value = strchr(key, ':');
438b077aed3SPierre Pronchery if (value == NULL) {
439b077aed3SPierre Pronchery log_message(prog, LOG_WARNING,
440b077aed3SPierre Pronchery "Error parsing HTTP header: missing ':'");
441b077aed3SPierre Pronchery (void)http_server_send_status(cbio, 400, "Bad Request");
442b077aed3SPierre Pronchery goto out;
443b077aed3SPierre Pronchery }
444b077aed3SPierre Pronchery *(value++) = '\0';
445b077aed3SPierre Pronchery while (*value == ' ')
446b077aed3SPierre Pronchery value++;
447b077aed3SPierre Pronchery line_end = strchr(value, '\r');
448b077aed3SPierre Pronchery if (line_end == NULL) {
449b077aed3SPierre Pronchery line_end = strchr(value, '\n');
450b077aed3SPierre Pronchery if (line_end == NULL) {
451b077aed3SPierre Pronchery log_message(prog, LOG_WARNING,
452b077aed3SPierre Pronchery "Error parsing HTTP header: missing end of line");
453b077aed3SPierre Pronchery (void)http_server_send_status(cbio, 400, "Bad Request");
454b077aed3SPierre Pronchery goto out;
455b077aed3SPierre Pronchery }
456b077aed3SPierre Pronchery }
457b077aed3SPierre Pronchery *line_end = '\0';
458b077aed3SPierre Pronchery /* https://tools.ietf.org/html/rfc7230#section-6.3 Persistence */
459b077aed3SPierre Pronchery if (found_keep_alive != NULL
460b077aed3SPierre Pronchery && OPENSSL_strcasecmp(key, "Connection") == 0) {
461b077aed3SPierre Pronchery if (OPENSSL_strcasecmp(value, "keep-alive") == 0)
462b077aed3SPierre Pronchery *found_keep_alive = 1;
463b077aed3SPierre Pronchery else if (OPENSSL_strcasecmp(value, "close") == 0)
464b077aed3SPierre Pronchery *found_keep_alive = 0;
465b077aed3SPierre Pronchery }
466b077aed3SPierre Pronchery }
467b077aed3SPierre Pronchery
468b077aed3SPierre Pronchery # ifdef HTTP_DAEMON
469b077aed3SPierre Pronchery /* Clear alarm before we close the client socket */
470b077aed3SPierre Pronchery alarm(0);
471b077aed3SPierre Pronchery timeout = 0;
472b077aed3SPierre Pronchery # endif
473b077aed3SPierre Pronchery
474b077aed3SPierre Pronchery /* Try to read and parse request */
475b077aed3SPierre Pronchery req = ASN1_item_d2i_bio(it, getbio != NULL ? getbio : cbio, NULL);
476b077aed3SPierre Pronchery if (req == NULL) {
477b077aed3SPierre Pronchery log_message(prog, LOG_WARNING,
478b077aed3SPierre Pronchery "Error parsing DER-encoded request content");
479b077aed3SPierre Pronchery (void)http_server_send_status(cbio, 400, "Bad Request");
480b077aed3SPierre Pronchery } else if (ppath != NULL && (*ppath = OPENSSL_strdup(url)) == NULL) {
481b077aed3SPierre Pronchery log_message(prog, LOG_ERR,
482b077aed3SPierre Pronchery "Out of memory allocating %zu bytes", strlen(url) + 1);
483b077aed3SPierre Pronchery ASN1_item_free(req, it);
484b077aed3SPierre Pronchery goto fatal;
485b077aed3SPierre Pronchery }
486b077aed3SPierre Pronchery
487b077aed3SPierre Pronchery *preq = req;
488b077aed3SPierre Pronchery
489b077aed3SPierre Pronchery out:
490b077aed3SPierre Pronchery BIO_free_all(getbio);
491b077aed3SPierre Pronchery # ifdef HTTP_DAEMON
492b077aed3SPierre Pronchery if (timeout > 0)
493b077aed3SPierre Pronchery alarm(0);
494b077aed3SPierre Pronchery acfd = (int)INVALID_SOCKET;
495b077aed3SPierre Pronchery # endif
496b077aed3SPierre Pronchery return ret;
497b077aed3SPierre Pronchery
498b077aed3SPierre Pronchery fatal:
499b077aed3SPierre Pronchery (void)http_server_send_status(cbio, 500, "Internal Server Error");
500b077aed3SPierre Pronchery if (ppath != NULL) {
501b077aed3SPierre Pronchery OPENSSL_free(*ppath);
502b077aed3SPierre Pronchery *ppath = NULL;
503b077aed3SPierre Pronchery }
504b077aed3SPierre Pronchery BIO_free_all(cbio);
505b077aed3SPierre Pronchery *pcbio = NULL;
506b077aed3SPierre Pronchery ret = -1;
507b077aed3SPierre Pronchery goto out;
508b077aed3SPierre Pronchery }
509b077aed3SPierre Pronchery
510b077aed3SPierre Pronchery /* assumes that cbio does not do an encoding that changes the output length */
http_server_send_asn1_resp(BIO * cbio,int keep_alive,const char * content_type,const ASN1_ITEM * it,const ASN1_VALUE * resp)511b077aed3SPierre Pronchery int http_server_send_asn1_resp(BIO *cbio, int keep_alive,
512b077aed3SPierre Pronchery const char *content_type,
513b077aed3SPierre Pronchery const ASN1_ITEM *it, const ASN1_VALUE *resp)
514b077aed3SPierre Pronchery {
515b077aed3SPierre Pronchery int ret = BIO_printf(cbio, HTTP_1_0" 200 OK\r\n%s"
516b077aed3SPierre Pronchery "Content-type: %s\r\n"
517b077aed3SPierre Pronchery "Content-Length: %d\r\n\r\n",
518b077aed3SPierre Pronchery keep_alive ? "Connection: keep-alive\r\n" : "",
519b077aed3SPierre Pronchery content_type,
520b077aed3SPierre Pronchery ASN1_item_i2d(resp, NULL, it)) > 0
521b077aed3SPierre Pronchery && ASN1_item_i2d_bio(it, cbio, resp) > 0;
522b077aed3SPierre Pronchery
523b077aed3SPierre Pronchery (void)BIO_flush(cbio);
524b077aed3SPierre Pronchery return ret;
525b077aed3SPierre Pronchery }
526b077aed3SPierre Pronchery
http_server_send_status(BIO * cbio,int status,const char * reason)527b077aed3SPierre Pronchery int http_server_send_status(BIO *cbio, int status, const char *reason)
528b077aed3SPierre Pronchery {
529b077aed3SPierre Pronchery int ret = BIO_printf(cbio, HTTP_1_0" %d %s\r\n\r\n",
530b077aed3SPierre Pronchery /* This implicitly cancels keep-alive */
531b077aed3SPierre Pronchery status, reason) > 0;
532b077aed3SPierre Pronchery
533b077aed3SPierre Pronchery (void)BIO_flush(cbio);
534b077aed3SPierre Pronchery return ret;
535b077aed3SPierre Pronchery }
536b077aed3SPierre Pronchery #endif
537