xref: /freebsd/crypto/openssl/apps/lib/http_server.c (revision 0d0c8621fd181e507f0fb50ffcca606faf66a8c2)
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