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