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