1 #include "first.h" 2 3 #include "base.h" 4 #include "stat_cache.h" 5 #include "http_kv.h" 6 #include "log.h" 7 #include "response.h" 8 #include "http_cgi.h" 9 #include "http_chunk.h" 10 #include "http_header.h" 11 12 #include "plugin.h" 13 14 #include <sys/types.h> 15 #include "sys-socket.h" 16 #ifdef HAVE_SYS_WAIT_H 17 #include <sys/wait.h> 18 #endif 19 20 #include <unistd.h> 21 #include <errno.h> 22 #include <stdlib.h> 23 #include <string.h> 24 #include <fdevent.h> 25 26 #include <fcntl.h> 27 #include <signal.h> 28 29 static int pipe_cloexec(int pipefd[2]) { 30 #ifdef HAVE_PIPE2 31 if (0 == pipe2(pipefd, O_CLOEXEC)) return 0; 32 #endif 33 return 0 == pipe(pipefd) 34 #ifdef FD_CLOEXEC 35 && 0 == fcntl(pipefd[0], F_SETFD, FD_CLOEXEC) 36 && 0 == fcntl(pipefd[1], F_SETFD, FD_CLOEXEC) 37 #endif 38 ? 0 39 : -1; 40 } 41 42 typedef struct { 43 uintptr_t *offsets; 44 size_t osize; 45 size_t oused; 46 buffer *b; 47 buffer *boffsets; 48 buffer *ld_preload; 49 buffer *ld_library_path; 50 #ifdef __CYGWIN__ 51 buffer *systemroot; 52 #endif 53 } env_accum; 54 55 typedef struct { 56 unix_time64_t read_timeout; 57 unix_time64_t write_timeout; 58 } cgi_limits; 59 60 typedef struct { 61 const array *cgi; 62 const cgi_limits *limits; 63 unsigned short execute_x_only; 64 unsigned short local_redir; 65 unsigned short xsendfile_allow; 66 unsigned short upgrade; 67 const array *xsendfile_docroot; 68 } plugin_config; 69 70 struct cgi_pid_t; 71 72 typedef struct { 73 PLUGIN_DATA; 74 plugin_config defaults; 75 plugin_config conf; 76 int tempfile_accum; 77 struct cgi_pid_t *cgi_pid; 78 env_accum env; 79 } plugin_data; 80 81 typedef struct { 82 struct cgi_pid_t *cgi_pid; 83 int fd; 84 int fdtocgi; 85 fdnode *fdn; 86 fdnode *fdntocgi; 87 88 request_st *r; 89 struct fdevents *ev; /* dumb pointer */ 90 plugin_data *plugin_data; /* dumb pointer */ 91 92 buffer *response; 93 unix_time64_t read_ts; 94 unix_time64_t write_ts; 95 buffer *cgi_handler; /* dumb pointer */ 96 http_response_opts opts; 97 plugin_config conf; 98 } handler_ctx; 99 100 typedef struct cgi_pid_t { 101 pid_t pid; 102 handler_ctx *hctx; 103 struct cgi_pid_t *next; 104 struct cgi_pid_t *prev; 105 } cgi_pid_t; 106 107 static handler_ctx * cgi_handler_ctx_init(void) { 108 handler_ctx *hctx = calloc(1, sizeof(*hctx)); 109 110 force_assert(hctx); 111 112 hctx->response = chunk_buffer_acquire(); 113 hctx->fd = -1; 114 hctx->fdtocgi = -1; 115 116 return hctx; 117 } 118 119 static void cgi_handler_ctx_free(handler_ctx *hctx) { 120 chunk_buffer_release(hctx->response); 121 free(hctx); 122 } 123 124 INIT_FUNC(mod_cgi_init) { 125 plugin_data *p; 126 const char *s; 127 128 p = calloc(1, sizeof(*p)); 129 130 force_assert(p); 131 132 /* for valgrind */ 133 s = getenv("LD_PRELOAD"); 134 if (s) p->env.ld_preload = buffer_init_string(s); 135 s = getenv("LD_LIBRARY_PATH"); 136 if (s) p->env.ld_library_path = buffer_init_string(s); 137 #ifdef __CYGWIN__ 138 /* CYGWIN needs SYSTEMROOT */ 139 s = getenv("SYSTEMROOT"); 140 if (s) p->env.systemroot = buffer_init_string(s); 141 #endif 142 143 return p; 144 } 145 146 147 FREE_FUNC(mod_cgi_free) { 148 plugin_data *p = p_d; 149 buffer_free(p->env.ld_preload); 150 buffer_free(p->env.ld_library_path); 151 #ifdef __CYGWIN__ 152 buffer_free(p->env.systemroot); 153 #endif 154 155 for (cgi_pid_t *cgi_pid = p->cgi_pid, *next; cgi_pid; cgi_pid = next) { 156 next = cgi_pid->next; 157 free(cgi_pid); 158 } 159 160 if (NULL == p->cvlist) return; 161 /* (init i to 0 if global context; to 1 to skip empty global context) */ 162 for (int i = !p->cvlist[0].v.u2[1], used = p->nconfig; i < used; ++i) { 163 config_plugin_value_t *cpv = p->cvlist + p->cvlist[i].v.u2[0]; 164 for (; -1 != cpv->k_id; ++cpv) { 165 if (cpv->vtype != T_CONFIG_LOCAL || NULL == cpv->v.v) continue; 166 switch (cpv->k_id) { 167 case 6: /* cgi.limits */ 168 free(cpv->v.v); 169 break; 170 default: 171 break; 172 } 173 } 174 } 175 } 176 177 static void mod_cgi_merge_config_cpv(plugin_config * const pconf, const config_plugin_value_t * const cpv) { 178 switch (cpv->k_id) { /* index into static config_plugin_keys_t cpk[] */ 179 case 0: /* cgi.assign */ 180 pconf->cgi = cpv->v.a; 181 break; 182 case 1: /* cgi.execute-x-only */ 183 pconf->execute_x_only = (unsigned short)cpv->v.u; 184 break; 185 case 2: /* cgi.x-sendfile */ 186 pconf->xsendfile_allow = (unsigned short)cpv->v.u; 187 break; 188 case 3: /* cgi.x-sendfile-docroot */ 189 pconf->xsendfile_docroot = cpv->v.a; 190 break; 191 case 4: /* cgi.local-redir */ 192 pconf->local_redir = (unsigned short)cpv->v.u; 193 break; 194 case 5: /* cgi.upgrade */ 195 pconf->upgrade = (unsigned short)cpv->v.u; 196 break; 197 case 6: /* cgi.limits */ 198 if (cpv->vtype != T_CONFIG_LOCAL) break; 199 pconf->limits = cpv->v.v; 200 break; 201 default:/* should not happen */ 202 return; 203 } 204 } 205 206 static void mod_cgi_merge_config(plugin_config * const pconf, const config_plugin_value_t *cpv) { 207 do { 208 mod_cgi_merge_config_cpv(pconf, cpv); 209 } while ((++cpv)->k_id != -1); 210 } 211 212 static void mod_cgi_patch_config(request_st * const r, plugin_data * const p) { 213 p->conf = p->defaults; /* copy small struct instead of memcpy() */ 214 /*memcpy(&p->conf, &p->defaults, sizeof(plugin_config));*/ 215 for (int i = 1, used = p->nconfig; i < used; ++i) { 216 if (config_check_cond(r, (uint32_t)p->cvlist[i].k_id)) 217 mod_cgi_merge_config(&p->conf, p->cvlist + p->cvlist[i].v.u2[0]); 218 } 219 } 220 221 static cgi_limits * mod_cgi_parse_limits(const array * const a, log_error_st * const errh) { 222 cgi_limits * const limits = calloc(1, sizeof(cgi_limits)); 223 force_assert(limits); 224 for (uint32_t i = 0; i < a->used; ++i) { 225 const data_unset * const du = a->data[i]; 226 int32_t v = config_plugin_value_to_int32(du, -1); 227 if (buffer_eq_icase_slen(&du->key, CONST_STR_LEN("read-timeout"))) { 228 limits->read_timeout = (unix_time64_t)v; 229 continue; 230 } 231 if (buffer_eq_icase_slen(&du->key, CONST_STR_LEN("write-timeout"))) { 232 limits->write_timeout = (unix_time64_t)v; 233 continue; 234 } 235 log_error(errh, __FILE__, __LINE__, 236 "unrecognized cgi.limits param: %s", du->key.ptr); 237 } 238 return limits; 239 } 240 241 SETDEFAULTS_FUNC(mod_cgi_set_defaults) { 242 static const config_plugin_keys_t cpk[] = { 243 { CONST_STR_LEN("cgi.assign"), 244 T_CONFIG_ARRAY_KVSTRING, 245 T_CONFIG_SCOPE_CONNECTION } 246 ,{ CONST_STR_LEN("cgi.execute-x-only"), 247 T_CONFIG_BOOL, 248 T_CONFIG_SCOPE_CONNECTION } 249 ,{ CONST_STR_LEN("cgi.x-sendfile"), 250 T_CONFIG_BOOL, 251 T_CONFIG_SCOPE_CONNECTION } 252 ,{ CONST_STR_LEN("cgi.x-sendfile-docroot"), 253 T_CONFIG_ARRAY_VLIST, 254 T_CONFIG_SCOPE_CONNECTION } 255 ,{ CONST_STR_LEN("cgi.local-redir"), 256 T_CONFIG_BOOL, 257 T_CONFIG_SCOPE_CONNECTION } 258 ,{ CONST_STR_LEN("cgi.upgrade"), 259 T_CONFIG_BOOL, 260 T_CONFIG_SCOPE_CONNECTION } 261 ,{ CONST_STR_LEN("cgi.limits"), 262 T_CONFIG_ARRAY_KVANY, 263 T_CONFIG_SCOPE_CONNECTION } 264 ,{ NULL, 0, 265 T_CONFIG_UNSET, 266 T_CONFIG_SCOPE_UNSET } 267 }; 268 269 plugin_data * const p = p_d; 270 if (!config_plugin_values_init(srv, p, cpk, "mod_cgi")) 271 return HANDLER_ERROR; 272 273 /* process and validate config directives 274 * (init i to 0 if global context; to 1 to skip empty global context) */ 275 for (int i = !p->cvlist[0].v.u2[1]; i < p->nconfig; ++i) { 276 config_plugin_value_t *cpv = p->cvlist + p->cvlist[i].v.u2[0]; 277 for (; -1 != cpv->k_id; ++cpv) { 278 switch (cpv->k_id) { 279 case 0: /* cgi.assign */ 280 case 1: /* cgi.execute-x-only */ 281 case 2: /* cgi.x-sendfile */ 282 break; 283 case 3: /* cgi.x-sendfile-docroot */ 284 for (uint32_t j = 0; j < cpv->v.a->used; ++j) { 285 data_string *ds = (data_string *)cpv->v.a->data[j]; 286 if (ds->value.ptr[0] != '/') { 287 log_error(srv->errh, __FILE__, __LINE__, 288 "%s paths must begin with '/'; invalid: \"%s\"", 289 cpk[cpv->k_id].k, ds->value.ptr); 290 return HANDLER_ERROR; 291 } 292 buffer_path_simplify(&ds->value); 293 buffer_append_slash(&ds->value); 294 } 295 break; 296 case 4: /* cgi.local-redir */ 297 case 5: /* cgi.upgrade */ 298 break; 299 case 6: /* cgi.limits */ 300 cpv->v.v = mod_cgi_parse_limits(cpv->v.a, srv->errh); 301 if (NULL == cpv->v.v) return HANDLER_ERROR; 302 cpv->vtype = T_CONFIG_LOCAL; 303 break; 304 default:/* should not happen */ 305 break; 306 } 307 } 308 } 309 310 /* initialize p->defaults from global config context */ 311 if (p->nconfig > 0 && p->cvlist->v.u2[1]) { 312 const config_plugin_value_t *cpv = p->cvlist + p->cvlist->v.u2[0]; 313 if (-1 != cpv->k_id) 314 mod_cgi_merge_config(&p->defaults, cpv); 315 } 316 317 p->tempfile_accum = 318 !srv->srvconf.feature_flags 319 || config_plugin_value_tobool( 320 array_get_element_klen(srv->srvconf.feature_flags, 321 CONST_STR_LEN("cgi.tempfile-accum")), 1); 322 323 return HANDLER_GO_ON; 324 } 325 326 327 static cgi_pid_t * cgi_pid_add(plugin_data *p, pid_t pid, handler_ctx *hctx) { 328 cgi_pid_t *cgi_pid = malloc(sizeof(cgi_pid_t)); 329 force_assert(cgi_pid); 330 cgi_pid->pid = pid; 331 cgi_pid->hctx = hctx; 332 cgi_pid->prev = NULL; 333 cgi_pid->next = p->cgi_pid; 334 p->cgi_pid = cgi_pid; 335 return cgi_pid; 336 } 337 338 static void cgi_pid_kill(cgi_pid_t *cgi_pid) { 339 cgi_pid->hctx = NULL; 340 kill(cgi_pid->pid, SIGTERM); 341 } 342 343 static void cgi_pid_del(plugin_data *p, cgi_pid_t *cgi_pid) { 344 if (cgi_pid->prev) 345 cgi_pid->prev->next = cgi_pid->next; 346 else 347 p->cgi_pid = cgi_pid->next; 348 349 if (cgi_pid->next) 350 cgi_pid->next->prev = cgi_pid->prev; 351 352 free(cgi_pid); 353 } 354 355 356 static void cgi_connection_close_fdtocgi(handler_ctx *hctx) { 357 /*(closes only hctx->fdtocgi)*/ 358 struct fdevents * const ev = hctx->ev; 359 fdevent_fdnode_event_del(ev, hctx->fdntocgi); 360 /*fdevent_unregister(ev, hctx->fdtocgi);*//*(handled below)*/ 361 fdevent_sched_close(ev, hctx->fdtocgi, 0); 362 hctx->fdntocgi = NULL; 363 hctx->fdtocgi = -1; 364 } 365 366 static void cgi_connection_close(handler_ctx *hctx) { 367 /* the connection to the browser went away, but we still have a connection 368 * to the CGI script 369 * 370 * close cgi-connection 371 */ 372 373 if (hctx->fd != -1) { 374 struct fdevents * const ev = hctx->ev; 375 /* close connection to the cgi-script */ 376 fdevent_fdnode_event_del(ev, hctx->fdn); 377 /*fdevent_unregister(ev, hctx->fd);*//*(handled below)*/ 378 fdevent_sched_close(ev, hctx->fd, 0); 379 hctx->fdn = NULL; 380 } 381 382 if (hctx->fdtocgi != -1) { 383 cgi_connection_close_fdtocgi(hctx); /*(closes only hctx->fdtocgi)*/ 384 } 385 386 plugin_data * const p = hctx->plugin_data; 387 request_st * const r = hctx->r; 388 r->plugin_ctx[p->id] = NULL; 389 390 if (hctx->cgi_pid) 391 cgi_pid_kill(hctx->cgi_pid); 392 cgi_handler_ctx_free(hctx); 393 394 /* finish response (if not already r->resp_body_started, r->resp_body_finished) */ 395 if (r->handler_module == p->self) { 396 http_response_backend_done(r); 397 } 398 } 399 400 static handler_t cgi_connection_close_callback(request_st * const r, void *p_d) { 401 handler_ctx *hctx = r->plugin_ctx[((plugin_data *)p_d)->id]; 402 if (hctx) { 403 chunkqueue_set_tempdirs(&r->reqbody_queue, /* reset sz */ 404 r->reqbody_queue.tempdirs, 0); 405 cgi_connection_close(hctx); 406 } 407 return HANDLER_GO_ON; 408 } 409 410 411 static int cgi_write_request(handler_ctx *hctx, int fd); 412 413 414 static handler_t cgi_handle_fdevent_send (void *ctx, int revents) { 415 handler_ctx *hctx = ctx; 416 request_st * const r = hctx->r; 417 418 /*(joblist only actually necessary here in mod_cgi fdevent send if returning HANDLER_ERROR)*/ 419 joblist_append(r->con); 420 421 if (revents & FDEVENT_OUT) { 422 if (0 != cgi_write_request(hctx, hctx->fdtocgi)) { 423 cgi_connection_close(hctx); 424 return HANDLER_ERROR; 425 } 426 /* more request body to be sent to CGI */ 427 } 428 429 if (revents & FDEVENT_HUP) { 430 /* skip sending remaining data to CGI */ 431 if (r->reqbody_length) { 432 chunkqueue *cq = &r->reqbody_queue; 433 chunkqueue_mark_written(cq, chunkqueue_length(cq)); 434 if (cq->bytes_in != (off_t)r->reqbody_length) { 435 r->keep_alive = 0; 436 } 437 } 438 439 cgi_connection_close_fdtocgi(hctx); /*(closes only hctx->fdtocgi)*/ 440 } else if (revents & FDEVENT_ERR) { 441 /* kill all connections to the cgi process */ 442 #if 1 443 log_error(r->conf.errh, __FILE__, __LINE__, "cgi-FDEVENT_ERR"); 444 #endif 445 cgi_connection_close(hctx); 446 return HANDLER_ERROR; 447 } 448 449 return HANDLER_FINISHED; 450 } 451 452 453 static handler_t cgi_response_headers(request_st * const r, struct http_response_opts_t *opts) { 454 /* response headers just completed */ 455 handler_ctx *hctx = (handler_ctx *)opts->pdata; 456 457 if (light_btst(r->resp_htags, HTTP_HEADER_UPGRADE)) { 458 if (hctx->conf.upgrade && r->http_status == 101) { 459 /* 101 Switching Protocols; transition to transparent proxy */ 460 http_response_upgrade_read_body_unknown(r); 461 } 462 else { 463 light_bclr(r->resp_htags, HTTP_HEADER_UPGRADE); 464 #if 0 465 /* preserve prior questionable behavior; likely broken behavior 466 * anyway if backend thinks connection is being upgraded but client 467 * does not receive Connection: upgrade */ 468 http_header_response_unset(r, HTTP_HEADER_UPGRADE, 469 CONST_STR_LEN("Upgrade")); 470 #endif 471 } 472 } 473 474 if (hctx->conf.upgrade 475 && !light_btst(r->resp_htags, HTTP_HEADER_UPGRADE)) { 476 chunkqueue *cq = &r->reqbody_queue; 477 hctx->conf.upgrade = 0; 478 if (cq->bytes_out == (off_t)r->reqbody_length) { 479 cgi_connection_close_fdtocgi(hctx); /*(closes hctx->fdtocgi)*/ 480 } 481 } 482 483 return HANDLER_GO_ON; 484 } 485 486 487 static int cgi_recv_response(request_st * const r, handler_ctx * const hctx) { 488 const off_t bytes_in = r->write_queue.bytes_in; 489 switch (http_response_read(r, &hctx->opts, 490 hctx->response, hctx->fdn)) { 491 default: 492 if (r->write_queue.bytes_in > bytes_in) 493 hctx->read_ts = log_monotonic_secs; 494 return HANDLER_GO_ON; 495 case HANDLER_ERROR: 496 http_response_backend_error(r); 497 __attribute_fallthrough__ 498 case HANDLER_FINISHED: 499 cgi_connection_close(hctx); 500 return HANDLER_FINISHED; 501 case HANDLER_COMEBACK: 502 /* flag for mod_cgi_handle_subrequest() */ 503 hctx->conf.local_redir = 2; 504 buffer_clear(hctx->response); 505 return HANDLER_COMEBACK; 506 } 507 } 508 509 510 static handler_t cgi_handle_fdevent(void *ctx, int revents) { 511 handler_ctx *hctx = ctx; 512 request_st * const r = hctx->r; 513 514 joblist_append(r->con); 515 516 if (revents & FDEVENT_IN) { 517 handler_t rc = cgi_recv_response(r, hctx); /*(might invalidate hctx)*/ 518 if (rc != HANDLER_GO_ON) return rc; /*(unless HANDLER_GO_ON)*/ 519 } 520 521 /* perhaps this issue is already handled */ 522 if (revents & (FDEVENT_HUP|FDEVENT_RDHUP)) { 523 if (r->resp_body_started) { 524 /* drain any remaining data from kernel pipe buffers 525 * even if (r->conf.stream_response_body 526 * & FDEVENT_STREAM_RESPONSE_BUFMIN) 527 * since event loop will spin on fd FDEVENT_HUP event 528 * until unregistered. */ 529 handler_t rc; 530 const unsigned short flags = r->conf.stream_response_body; 531 r->conf.stream_response_body &= ~FDEVENT_STREAM_RESPONSE_BUFMIN; 532 r->conf.stream_response_body |= FDEVENT_STREAM_RESPONSE_POLLRDHUP; 533 do { 534 rc = cgi_recv_response(r,hctx); /*(might invalidate hctx)*/ 535 } while (rc == HANDLER_GO_ON); /*(unless HANDLER_GO_ON)*/ 536 r->conf.stream_response_body = flags; 537 return rc; /* HANDLER_FINISHED or HANDLER_COMEBACK or HANDLER_ERROR */ 538 } else if (!buffer_is_blank(hctx->response)) { 539 /* unfinished header package which is a body in reality */ 540 r->resp_body_started = 1; 541 if (0 != http_chunk_append_buffer(r, hctx->response)) { 542 cgi_connection_close(hctx); 543 return HANDLER_ERROR; 544 } 545 if (0 == r->http_status) r->http_status = 200; /* OK */ 546 } 547 cgi_connection_close(hctx); 548 } else if (revents & FDEVENT_ERR) { 549 /* kill all connections to the cgi process */ 550 cgi_connection_close(hctx); 551 return HANDLER_ERROR; 552 } 553 554 return HANDLER_FINISHED; 555 } 556 557 558 __attribute_cold__ 559 __attribute_noinline__ 560 static void cgi_env_offset_resize(env_accum *env) { 561 chunk_buffer_prepare_append(env->boffsets, env->boffsets->size*2); 562 env->offsets = (uintptr_t *)(void *)env->boffsets->ptr; 563 env->osize = env->boffsets->size/sizeof(*env->offsets); 564 } 565 566 static int cgi_env_add(void *venv, const char *key, size_t key_len, const char *val, size_t val_len) { 567 env_accum *env = venv; 568 569 if (!key || (!val && val_len)) return -1; 570 571 if (__builtin_expect( (env->osize == env->oused), 0)) 572 cgi_env_offset_resize(env); 573 env->offsets[env->oused++] = env->b->used-1; 574 575 char * const dst = buffer_extend(env->b, key_len + val_len + 2); 576 memcpy(dst, key, key_len); 577 dst[key_len] = '='; 578 if (val_len) memcpy(dst + key_len + 1, val, val_len); 579 dst[key_len + 1 + val_len] = '\0'; 580 581 return 0; 582 } 583 584 static int cgi_write_request(handler_ctx *hctx, int fd) { 585 request_st * const r = hctx->r; 586 chunkqueue *cq = &r->reqbody_queue; 587 chunk *c; 588 589 chunkqueue_remove_finished_chunks(cq); /* unnecessary? */ 590 591 /* old comment: windows doesn't support select() on pipes - wouldn't be easy to fix for all platforms. 592 * solution: if this is still a problem on windows, then substitute 593 * socketpair() for pipe() and closesocket() for close() on windows. 594 */ 595 596 for (c = cq->first; c; c = cq->first) { 597 ssize_t wr = chunkqueue_write_chunk_to_pipe(fd, cq, r->conf.errh); 598 if (wr > 0) { 599 hctx->write_ts = log_monotonic_secs; 600 chunkqueue_mark_written(cq, wr); 601 /* continue if wrote whole chunk or wrote 16k block 602 * (see chunkqueue_write_chunk_file_intermed()) */ 603 if (c != cq->first || wr == 16384) 604 continue; 605 /*(else partial write)*/ 606 } 607 else if (wr < 0) { 608 switch(errno) { 609 case EAGAIN: 610 case EINTR: 611 /* ignore and try again later */ 612 break; 613 case EPIPE: 614 case ECONNRESET: 615 /* connection closed */ 616 log_error(r->conf.errh, __FILE__, __LINE__, 617 "failed to send post data to cgi, connection closed by CGI"); 618 /* skip all remaining data */ 619 chunkqueue_mark_written(cq, chunkqueue_length(cq)); 620 break; 621 default: 622 /* fatal error */ 623 log_perror(r->conf.errh, __FILE__, __LINE__, "write() failed"); 624 return -1; 625 } 626 } 627 /*if (0 == wr) break;*/ /*(might block)*/ 628 break; 629 } 630 631 if (cq->bytes_out == (off_t)r->reqbody_length && !hctx->conf.upgrade) { 632 /* sent all request body input */ 633 /* close connection to the cgi-script */ 634 if (-1 == hctx->fdtocgi) { /*(received request body sent in initial send to pipe buffer)*/ 635 --r->con->srv->cur_fds; 636 if (close(fd)) { 637 log_perror(r->conf.errh, __FILE__, __LINE__, "cgi stdin close %d failed", fd); 638 } 639 } else { 640 cgi_connection_close_fdtocgi(hctx); /*(closes only hctx->fdtocgi)*/ 641 } 642 } else { 643 off_t cqlen = chunkqueue_length(cq); 644 if (cq->bytes_in != r->reqbody_length && cqlen < 65536 - 16384) { 645 /*(r->conf.stream_request_body & FDEVENT_STREAM_REQUEST)*/ 646 if (!(r->conf.stream_request_body & FDEVENT_STREAM_REQUEST_POLLIN)) { 647 r->conf.stream_request_body |= FDEVENT_STREAM_REQUEST_POLLIN; 648 r->con->is_readable = 1; /* trigger optimistic read from client */ 649 } 650 } 651 struct fdevents * const ev = hctx->ev; 652 if (-1 == hctx->fdtocgi) { /*(not registered yet)*/ 653 hctx->fdtocgi = fd; 654 hctx->fdntocgi = fdevent_register(ev, hctx->fdtocgi, cgi_handle_fdevent_send, hctx); 655 } 656 if (0 == cqlen) { /*(chunkqueue_is_empty(cq))*/ 657 if ((fdevent_fdnode_interest(hctx->fdntocgi) & FDEVENT_OUT)) { 658 fdevent_fdnode_event_set(ev, hctx->fdntocgi, 0); 659 } 660 } else { 661 /* more request body remains to be sent to CGI so register for fdevents */ 662 hctx->write_ts = log_monotonic_secs; 663 fdevent_fdnode_event_set(ev, hctx->fdntocgi, FDEVENT_OUT); 664 } 665 } 666 667 return 0; 668 } 669 670 static int cgi_create_env(request_st * const r, plugin_data * const p, handler_ctx * const hctx, buffer * const cgi_handler) { 671 char *args[3]; 672 int to_cgi_fds[2]; 673 int from_cgi_fds[2]; 674 UNUSED(p); 675 676 if (!buffer_is_blank(cgi_handler)) { 677 if (NULL == stat_cache_path_stat(cgi_handler)) { 678 log_perror(r->conf.errh, __FILE__, __LINE__, 679 "stat for cgi-handler %s", cgi_handler->ptr); 680 return -1; 681 } 682 } 683 684 to_cgi_fds[0] = -1; 685 #ifndef __CYGWIN__ 686 if (0 == r->reqbody_length) { 687 /* future: might keep fd open in p->devnull for reuse 688 * and dup() here, or do not close() (later in this func) */ 689 to_cgi_fds[0] = fdevent_open_devnull(); 690 if (-1 == to_cgi_fds[0]) { 691 log_perror(r->conf.errh, __FILE__, __LINE__, "open /dev/null"); 692 return -1; 693 } 694 } 695 else if (!(r->conf.stream_request_body /*(if not streaming request body)*/ 696 & (FDEVENT_STREAM_REQUEST|FDEVENT_STREAM_REQUEST_BUFMIN))) { 697 chunkqueue * const cq = &r->reqbody_queue; 698 chunk * const c = cq->first; 699 if (c && c == cq->last && c->type == FILE_CHUNK && c->file.is_temp) { 700 /* request body in single tempfile if not streaming req body */ 701 if (-1 == c->file.fd 702 && 0 != chunkqueue_open_file_chunk(cq, r->conf.errh)) 703 return -1; 704 if (-1 == lseek(c->file.fd, 0, SEEK_SET)) { 705 log_perror(r->conf.errh, __FILE__, __LINE__, 706 "lseek %s", c->mem->ptr); 707 return -1; 708 } 709 to_cgi_fds[0] = c->file.fd; 710 to_cgi_fds[1] = -1; 711 } 712 } 713 #endif 714 715 if (-1 == to_cgi_fds[0] && pipe_cloexec(to_cgi_fds)) { 716 log_perror(r->conf.errh, __FILE__, __LINE__, "pipe failed"); 717 return -1; 718 } 719 if (pipe_cloexec(from_cgi_fds)) { 720 if (0 == r->reqbody_length) { 721 close(to_cgi_fds[0]); 722 } 723 else if (-1 != to_cgi_fds[1]) { 724 close(to_cgi_fds[0]); 725 close(to_cgi_fds[1]); 726 } 727 log_perror(r->conf.errh, __FILE__, __LINE__, "pipe failed"); 728 return -1; 729 } 730 731 env_accum * const env = &p->env; 732 env->b = chunk_buffer_acquire(); 733 env->boffsets = chunk_buffer_acquire(); 734 buffer_truncate(env->b, 0); 735 char **envp; 736 { 737 size_t i = 0; 738 http_cgi_opts opts = { 0, 0, NULL, NULL }; 739 env->offsets = (uintptr_t *)(void *)env->boffsets->ptr; 740 env->osize = env->boffsets->size/sizeof(*env->offsets); 741 env->oused = 0; 742 743 /* create environment */ 744 745 http_cgi_headers(r, &opts, cgi_env_add, env); 746 747 /* for valgrind */ 748 if (p->env.ld_preload) { 749 cgi_env_add(env, CONST_STR_LEN("LD_PRELOAD"), BUF_PTR_LEN(p->env.ld_preload)); 750 } 751 if (p->env.ld_library_path) { 752 cgi_env_add(env, CONST_STR_LEN("LD_LIBRARY_PATH"), BUF_PTR_LEN(p->env.ld_library_path)); 753 } 754 #ifdef __CYGWIN__ 755 /* CYGWIN needs SYSTEMROOT */ 756 if (p->env.systemroot) { 757 cgi_env_add(env, CONST_STR_LEN("SYSTEMROOT"), BUF_PTR_LEN(p->env.systemroot)); 758 } 759 #endif 760 761 /* adjust (uintptr_t) offsets to (char *) ptr 762 * (stored as offsets while accumulating in buffer, 763 * in case buffer is reallocated during env creation) */ 764 if (__builtin_expect( (env->osize == env->oused), 0)) 765 cgi_env_offset_resize(env); 766 envp = (char **)env->offsets; 767 envp[env->oused] = NULL; 768 const uintptr_t baseptr = (uintptr_t)env->b->ptr; 769 for (i = 0; i < env->oused; ++i) 770 envp[i] += baseptr; 771 772 /* set up args */ 773 i = 0; 774 775 if (!buffer_is_blank(cgi_handler)) { 776 args[i++] = cgi_handler->ptr; 777 } 778 args[i++] = r->physical.path.ptr; 779 args[i ] = NULL; 780 } 781 782 int dfd = fdevent_open_dirname(r->physical.path.ptr,r->conf.follow_symlink); 783 if (-1 == dfd) { 784 log_perror(r->conf.errh, __FILE__, __LINE__, "open dirname %s failed", r->physical.path.ptr); 785 } 786 787 int serrh_fd = r->conf.serrh ? r->conf.serrh->errorlog_fd : -1; 788 pid_t pid = (dfd >= 0) 789 ? fdevent_fork_execve(args[0], args, envp, 790 to_cgi_fds[0], from_cgi_fds[1], serrh_fd, dfd) 791 : -1; 792 793 chunk_buffer_release(env->boffsets); 794 chunk_buffer_release(env->b); 795 env->boffsets = NULL; 796 env->b = NULL; 797 798 if (-1 == pid) { 799 /* log error with errno prior to calling close() (might change errno) */ 800 log_perror(r->conf.errh, __FILE__, __LINE__, "fork failed"); 801 if (-1 != dfd) close(dfd); 802 close(from_cgi_fds[0]); 803 close(from_cgi_fds[1]); 804 if (0 == r->reqbody_length) { 805 close(to_cgi_fds[0]); 806 } 807 else if (-1 != to_cgi_fds[1]) { 808 close(to_cgi_fds[0]); 809 close(to_cgi_fds[1]); 810 } 811 return -1; 812 } else { 813 if (-1 != dfd) close(dfd); 814 close(from_cgi_fds[1]); 815 816 hctx->fd = from_cgi_fds[0]; 817 hctx->cgi_pid = cgi_pid_add(p, pid, hctx); 818 819 if (0 == r->reqbody_length) { 820 close(to_cgi_fds[0]); 821 } 822 else if (-1 == to_cgi_fds[1]) { 823 chunkqueue * const cq = &r->reqbody_queue; 824 chunkqueue_mark_written(cq, chunkqueue_length(cq)); 825 } 826 else if (0 == fdevent_fcntl_set_nb(to_cgi_fds[1]) 827 && 0 == cgi_write_request(hctx, to_cgi_fds[1])) { 828 close(to_cgi_fds[0]); 829 ++r->con->srv->cur_fds; 830 } 831 else { 832 close(to_cgi_fds[0]); 833 close(to_cgi_fds[1]); 834 /*(hctx->fd not yet registered with fdevent, so manually 835 * cleanup here; see fdevent_register() further below)*/ 836 close(hctx->fd); 837 hctx->fd = -1; 838 cgi_connection_close(hctx); 839 return -1; 840 } 841 842 ++r->con->srv->cur_fds; 843 844 struct fdevents * const ev = hctx->ev; 845 hctx->fdn = fdevent_register(ev, hctx->fd, cgi_handle_fdevent, hctx); 846 if (-1 == fdevent_fcntl_set_nb(hctx->fd)) { 847 log_perror(r->conf.errh, __FILE__, __LINE__, "fcntl failed"); 848 cgi_connection_close(hctx); 849 return -1; 850 } 851 hctx->read_ts = log_monotonic_secs; 852 fdevent_fdnode_event_set(ev, hctx->fdn, FDEVENT_IN | FDEVENT_RDHUP); 853 854 return 0; 855 } 856 } 857 858 URIHANDLER_FUNC(cgi_is_handled) { 859 plugin_data *p = p_d; 860 const stat_cache_st *st; 861 data_string *ds; 862 863 if (NULL != r->handler_module) return HANDLER_GO_ON; 864 /* r->physical.path is non-empty for handle_subrequest_start */ 865 /*if (buffer_is_blank(&r->physical.path)) return HANDLER_GO_ON;*/ 866 867 mod_cgi_patch_config(r, p); 868 if (NULL == p->conf.cgi) return HANDLER_GO_ON; 869 870 ds = (data_string *)array_match_key_suffix(p->conf.cgi, &r->physical.path); 871 if (NULL == ds) return HANDLER_GO_ON; 872 873 /* r->tmp_sce is set in http_response_physical_path_check() and is valid 874 * in handle_subrequest_start callback -- handle_subrequest_start callbacks 875 * should not change r->physical.path (or should invalidate r->tmp_sce) */ 876 st = r->tmp_sce && buffer_is_equal(&r->tmp_sce->name, &r->physical.path) 877 ? &r->tmp_sce->st 878 : stat_cache_path_stat(&r->physical.path); 879 if (NULL == st) return HANDLER_GO_ON; 880 881 /* (aside: CGI might be executable even if it is not readable) */ 882 if (!S_ISREG(st->st_mode)) return HANDLER_GO_ON; 883 if (p->conf.execute_x_only == 1 && (st->st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)) == 0) return HANDLER_GO_ON; 884 885 if (r->reqbody_length 886 && p->tempfile_accum 887 && !(r->conf.stream_request_body /*(if not streaming request body)*/ 888 & (FDEVENT_STREAM_REQUEST|FDEVENT_STREAM_REQUEST_BUFMIN))) { 889 /* store request body in single tempfile if not streaming request body*/ 890 r->reqbody_queue.upload_temp_file_size = INTMAX_MAX; 891 } 892 893 { 894 handler_ctx *hctx = cgi_handler_ctx_init(); 895 hctx->ev = r->con->srv->ev; 896 hctx->r = r; 897 hctx->plugin_data = p; 898 hctx->cgi_handler = &ds->value; 899 memcpy(&hctx->conf, &p->conf, sizeof(plugin_config)); 900 hctx->conf.upgrade = 901 hctx->conf.upgrade 902 && r->http_version == HTTP_VERSION_1_1 903 && light_btst(r->rqst_htags, HTTP_HEADER_UPGRADE); 904 hctx->opts.fdfmt = S_IFIFO; 905 hctx->opts.backend = BACKEND_CGI; 906 hctx->opts.authorizer = 0; 907 hctx->opts.local_redir = hctx->conf.local_redir; 908 hctx->opts.xsendfile_allow = hctx->conf.xsendfile_allow; 909 hctx->opts.xsendfile_docroot = hctx->conf.xsendfile_docroot; 910 hctx->opts.pdata = hctx; 911 hctx->opts.headers = cgi_response_headers; 912 r->plugin_ctx[p->id] = hctx; 913 r->handler_module = p->self; 914 } 915 916 return HANDLER_GO_ON; 917 } 918 919 __attribute_cold__ 920 __attribute_noinline__ 921 static handler_t mod_cgi_local_redir(request_st * const r) { 922 /* must be called from mod_cgi_handle_subrequest() so that HANDLER_COMEBACK 923 * return value propagates back through connection_state_machine() */ 924 http_response_reset(r); /*(includes r->http_status = 0)*/ 925 plugins_call_handle_request_reset(r); 926 /*cgi_connection_close(hctx);*//*(already cleaned up and hctx is now invalid)*/ 927 return HANDLER_COMEBACK; 928 } 929 930 /* 931 * - HANDLER_GO_ON : not our job 932 * - HANDLER_FINISHED: got response 933 * - HANDLER_WAIT_FOR_EVENT: waiting for response 934 */ 935 SUBREQUEST_FUNC(mod_cgi_handle_subrequest) { 936 plugin_data * const p = p_d; 937 handler_ctx * const hctx = r->plugin_ctx[p->id]; 938 if (NULL == hctx) return HANDLER_GO_ON; 939 940 if (2 == hctx->conf.local_redir) return mod_cgi_local_redir(r); 941 942 if ((r->conf.stream_response_body & FDEVENT_STREAM_RESPONSE_BUFMIN) 943 && r->resp_body_started) { 944 if (chunkqueue_length(&r->write_queue) > 65536 - 4096) { 945 fdevent_fdnode_event_clr(hctx->ev, hctx->fdn, FDEVENT_IN); 946 } else if (!(fdevent_fdnode_interest(hctx->fdn) & FDEVENT_IN)) { 947 /* optimistic read from backend */ 948 handler_t rc = cgi_recv_response(r, hctx); /*(might invalidate hctx)*/ 949 if (rc == HANDLER_COMEBACK) mod_cgi_local_redir(r); 950 if (rc != HANDLER_GO_ON) return rc; /*(unless HANDLER_GO_ON)*/ 951 hctx->read_ts = log_monotonic_secs; 952 fdevent_fdnode_event_add(hctx->ev, hctx->fdn, FDEVENT_IN); 953 } 954 } 955 956 chunkqueue * const cq = &r->reqbody_queue; 957 958 if (cq->bytes_in != (off_t)r->reqbody_length) { 959 /*(64k - 4k to attempt to avoid temporary files 960 * in conjunction with FDEVENT_STREAM_REQUEST_BUFMIN)*/ 961 if (chunkqueue_length(cq) > 65536 - 4096 962 && (r->conf.stream_request_body & FDEVENT_STREAM_REQUEST_BUFMIN)){ 963 r->conf.stream_request_body &= ~FDEVENT_STREAM_REQUEST_POLLIN; 964 if (-1 != hctx->fd) return HANDLER_WAIT_FOR_EVENT; 965 } else { 966 handler_t rc = r->con->reqbody_read(r); 967 if (!chunkqueue_is_empty(cq)) { 968 if (fdevent_fdnode_interest(hctx->fdntocgi) & FDEVENT_OUT) { 969 return (rc == HANDLER_GO_ON) ? HANDLER_WAIT_FOR_EVENT : rc; 970 } 971 } 972 if (rc != HANDLER_GO_ON) return rc; 973 974 /* CGI environment requires that Content-Length be set. 975 * Send 411 Length Required if Content-Length missing. 976 * (occurs here if client sends Transfer-Encoding: chunked 977 * and module is flagged to stream request body to backend) */ 978 if (-1 == r->reqbody_length) { 979 return (r->conf.stream_request_body & FDEVENT_STREAM_REQUEST) 980 ? http_response_reqbody_read_error(r, 411) 981 : HANDLER_WAIT_FOR_EVENT; 982 } 983 } 984 } 985 986 if (-1 == hctx->fd) { 987 if (cgi_create_env(r, p, hctx, hctx->cgi_handler)) { 988 r->http_status = 500; 989 r->handler_module = NULL; 990 991 return HANDLER_FINISHED; 992 } 993 } else if (!chunkqueue_is_empty(cq)) { 994 if (0 != cgi_write_request(hctx, hctx->fdtocgi)) { 995 cgi_connection_close(hctx); 996 return HANDLER_ERROR; 997 } 998 } 999 1000 /* if not done, wait for CGI to close stdout, so we read EOF on pipe */ 1001 return HANDLER_WAIT_FOR_EVENT; 1002 } 1003 1004 1005 __attribute_cold__ 1006 __attribute_noinline__ 1007 static void cgi_trigger_hctx_timeout(handler_ctx * const hctx, const char * const msg) { 1008 request_st * const r = hctx->r; 1009 joblist_append(r->con); 1010 1011 log_error(r->conf.errh, __FILE__, __LINE__, 1012 "%s timeout on CGI: %s (pid: %lld)", 1013 msg, r->physical.path.ptr, (long long)hctx->cgi_pid->pid); 1014 1015 if (*msg == 'w') { /* "write" */ 1016 /* theoretically, response might be waiting on hctx->fdn pipe 1017 * if it arrived since we last checked for event, and if CGI 1018 * timeout out while reading (or did not read) request body */ 1019 handler_t rc = cgi_recv_response(r, hctx); /*(might invalidate hctx)*/ 1020 if (rc != HANDLER_GO_ON) return; /*(unless HANDLER_GO_ON)*/ 1021 } 1022 1023 if (0 == r->http_status) r->http_status = 504; /* Gateway Timeout */ 1024 cgi_connection_close(hctx); 1025 } 1026 1027 1028 static handler_t cgi_trigger_cb(server *srv, void *p_d) { 1029 UNUSED(srv); 1030 const unix_time64_t mono = log_monotonic_secs; 1031 plugin_data * const p = p_d; 1032 for (cgi_pid_t *cgi_pid = p->cgi_pid; cgi_pid; cgi_pid = cgi_pid->next) { 1033 /*(hctx stays in cgi_pid list until process pid is reaped, 1034 * so p->cgi_pid[] is not modified during this loop)*/ 1035 handler_ctx * const hctx = cgi_pid->hctx; 1036 if (!hctx) continue; /*(already called cgi_pid_kill())*/ 1037 const cgi_limits * const limits = hctx->conf.limits; 1038 if (NULL == limits) continue; 1039 if (limits->read_timeout && hctx->fdn 1040 && (fdevent_fdnode_interest(hctx->fdn) & FDEVENT_IN) 1041 && mono - hctx->read_ts > limits->read_timeout) { 1042 cgi_trigger_hctx_timeout(hctx, "read"); 1043 continue; 1044 } 1045 if (limits->write_timeout && hctx->fdntocgi 1046 && (fdevent_fdnode_interest(hctx->fdntocgi) & FDEVENT_OUT) 1047 && mono - hctx->write_ts > limits->write_timeout) { 1048 cgi_trigger_hctx_timeout(hctx, "write"); 1049 continue; 1050 } 1051 } 1052 return HANDLER_GO_ON; 1053 } 1054 1055 1056 static handler_t cgi_waitpid_cb(server *srv, void *p_d, pid_t pid, int status) { 1057 /*(XXX: if supporting a large number of CGI, might use a different algorithm 1058 * instead of linked list, e.g. splaytree indexed with pid)*/ 1059 plugin_data *p = (plugin_data *)p_d; 1060 for (cgi_pid_t *cgi_pid = p->cgi_pid; cgi_pid; cgi_pid = cgi_pid->next) { 1061 if (pid != cgi_pid->pid) continue; 1062 1063 handler_ctx * const hctx = cgi_pid->hctx; 1064 if (hctx) hctx->cgi_pid = NULL; 1065 cgi_pid_del(p, cgi_pid); 1066 1067 if (WIFEXITED(status)) { 1068 /* (skip logging (non-zero) CGI exit; might be very noisy) */ 1069 } 1070 else if (WIFSIGNALED(status)) { 1071 /* ignore SIGTERM if sent by cgi_connection_close() (NULL == hctx)*/ 1072 if (WTERMSIG(status) != SIGTERM || NULL != hctx) { 1073 log_error_st *errh = hctx ? hctx->r->conf.errh : srv->errh; 1074 log_error(errh, __FILE__, __LINE__, 1075 "CGI pid %d died with signal %d", pid, WTERMSIG(status)); 1076 } 1077 } 1078 else { 1079 log_error_st *errh = hctx ? hctx->r->conf.errh : srv->errh; 1080 log_error(errh, __FILE__, __LINE__, 1081 "CGI pid %d ended unexpectedly", pid); 1082 } 1083 1084 return HANDLER_FINISHED; 1085 } 1086 1087 return HANDLER_GO_ON; 1088 } 1089 1090 1091 int mod_cgi_plugin_init(plugin *p); 1092 int mod_cgi_plugin_init(plugin *p) { 1093 p->version = LIGHTTPD_VERSION_ID; 1094 p->name = "cgi"; 1095 1096 p->handle_request_reset = cgi_connection_close_callback; 1097 p->handle_subrequest_start = cgi_is_handled; 1098 p->handle_subrequest = mod_cgi_handle_subrequest; 1099 p->handle_trigger = cgi_trigger_cb; 1100 p->handle_waitpid = cgi_waitpid_cb; 1101 p->init = mod_cgi_init; 1102 p->cleanup = mod_cgi_free; 1103 p->set_defaults = mod_cgi_set_defaults; 1104 1105 return 0; 1106 } 1107