Allow subject of CMP -oldcert as sender unless protection cert is given
[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                              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 *url, *end;
265     ASN1_VALUE *req;
266     int ret = 1;
267
268     *preq = NULL;
269     *pcbio = NULL;
270
271     /* Connection loss before accept() is routine, ignore silently */
272     if (BIO_do_accept(acbio) <= 0)
273         return 0;
274
275     cbio = BIO_pop(acbio);
276     *pcbio = cbio;
277     if (cbio == NULL) {
278         ret = -1;
279         goto out;
280     }
281
282 # ifdef HTTP_DAEMON
283     if (timeout > 0) {
284         (void)BIO_get_fd(cbio, &acfd);
285         alarm(timeout);
286     }
287 # endif
288
289     /* Read the request line. */
290     len = BIO_gets(cbio, reqbuf, sizeof(reqbuf));
291     if (len <= 0)
292         goto out;
293
294     if (accept_get && strncmp(reqbuf, "GET ", 4) == 0) {
295         /* Expecting GET {sp} /URL {sp} HTTP/1.x */
296         for (url = reqbuf + 4; *url == ' '; ++url)
297             continue;
298         if (*url != '/') {
299             log_message(prog, LOG_INFO,
300                         "Invalid GET -- URL does not begin with '/': %s", url);
301             goto out;
302         }
303         url++;
304
305         /* Splice off the HTTP version identifier. */
306         for (end = url; *end != '\0'; end++)
307             if (*end == ' ')
308                 break;
309         if (strncmp(end, " HTTP/1.", 7) != 0) {
310             log_message(prog, LOG_INFO,
311                         "Invalid GET -- bad HTTP/version string: %s", end + 1);
312             goto out;
313         }
314         *end = '\0';
315
316         /*-
317          * Skip "GET / HTTP..." requests often used by load-balancers.
318          * 'url' was incremented above to point to the first byte *after*
319          * the leading slash, so in case 'GET / ' it is now an empty string.
320          */
321         if (url[0] == '\0')
322             goto out;
323
324         len = urldecode(url);
325         if (len <= 0) {
326             log_message(prog, LOG_INFO,
327                         "Invalid GET request -- bad URL encoding: %s", url);
328             goto out;
329         }
330         if ((getbio = BIO_new_mem_buf(url, len)) == NULL
331             || (b64 = BIO_new(BIO_f_base64())) == NULL) {
332             log_message(prog, LOG_ERR,
333                         "Could not allocate base64 bio with size = %d", len);
334             BIO_free_all(cbio);
335             *pcbio = NULL;
336             ret = -1;
337             goto out;
338         }
339         BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL);
340         getbio = BIO_push(b64, getbio);
341     } else if (strncmp(reqbuf, "POST ", 5) != 0) {
342         log_message(prog, LOG_INFO,
343                     "HTTP request does not start with GET/POST: %s", reqbuf);
344         /* TODO provide better diagnosis in case client tries TLS */
345         goto out;
346     }
347
348     /* Read and skip past the headers. */
349     for (;;) {
350         len = BIO_gets(cbio, inbuf, sizeof(inbuf));
351         if (len <= 0) {
352             log_message(prog, LOG_ERR,
353                         "Error skipping remaining HTTP headers");
354             goto out;
355         }
356         if ((inbuf[0] == '\r') || (inbuf[0] == '\n'))
357             break;
358     }
359
360 # ifdef HTTP_DAEMON
361     /* Clear alarm before we close the client socket */
362     alarm(0);
363     timeout = 0;
364 # endif
365
366     /* Try to read and parse request */
367     req = ASN1_item_d2i_bio(it, getbio != NULL ? getbio : cbio, NULL);
368     if (req == NULL)
369         log_message(prog, LOG_ERR, "Error parsing request");
370
371     *preq = req;
372
373  out:
374     BIO_free_all(getbio);
375 # ifdef HTTP_DAEMON
376     if (timeout > 0)
377         alarm(0);
378     acfd = (int)INVALID_SOCKET;
379 # endif
380     return ret;
381 }
382
383 /* assumes that cbio does not do an encoding that changes the output length */
384 int http_server_send_asn1_resp(BIO *cbio, const char *content_type,
385                                const ASN1_ITEM *it, const ASN1_VALUE *resp)
386 {
387     int ret = BIO_printf(cbio, "HTTP/1.0 200 OK\r\nContent-type: %s\r\n"
388                          "Content-Length: %d\r\n\r\n", content_type,
389                          ASN1_item_i2d(resp, NULL, it)) > 0
390             && ASN1_item_i2d_bio(it, cbio, resp) > 0;
391
392     (void)BIO_flush(cbio);
393     return ret;
394 }
395 #endif