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