Browse Source

[mod_cgi] handle local redirect response (fixes #2108)

RFC3875 CGI 1.1 specification section 6.2.2 Local Redirect Response
http://www.ietf.org/rfc/rfc3875

x-ref:
  "CGI local redirect not implemented correctly"
  https://redmine.lighttpd.net/issues/2108
personal/stbuehler/mod-csrf-old
Glenn Strauss 5 years ago
parent
commit
8861c2bb54
  1. 17
      src/connections-glue.c
  2. 1
      src/connections.h
  3. 49
      src/mod_cgi.c
  4. 5
      tests/docroot/www/cgi.pl
  5. 9
      tests/mod-cgi.t

17
src/connections-glue.c

@ -379,3 +379,20 @@ handler_t connection_handle_read_post_state(server *srv, connection *con) {
: HANDLER_WAIT_FOR_EVENT;
}
}
void connection_response_reset(server *srv, connection *con) {
UNUSED(srv);
con->http_status = 0;
con->is_writable = 1;
con->file_finished = 0;
con->file_started = 0;
con->got_response = 0;
con->parsed_response = 0;
con->response.keep_alive = 0;
con->response.content_length = -1;
con->response.transfer_encoding = 0;
buffer_reset(con->physical.path);
array_reset(con->response.headers);
chunkqueue_reset(con->write_queue);
}

1
src/connections.h

@ -18,5 +18,6 @@ const char * connection_get_short_state(connection_state_t state);
int connection_state_machine(server *srv, connection *con);
int connection_handle_read(server *srv, connection *con);
handler_t connection_handle_read_post_state(server *srv, connection *con);
void connection_response_reset(server *srv, connection *con);
#endif

49
src/mod_cgi.c

@ -104,7 +104,7 @@ static void cgi_handler_ctx_free(handler_ctx *hctx) {
free(hctx);
}
enum {FDEVENT_HANDLED_UNSET, FDEVENT_HANDLED_FINISHED, FDEVENT_HANDLED_NOT_FINISHED, FDEVENT_HANDLED_ERROR};
enum {FDEVENT_HANDLED_UNSET, FDEVENT_HANDLED_FINISHED, FDEVENT_HANDLED_NOT_FINISHED, FDEVENT_HANDLED_COMEBACK, FDEVENT_HANDLED_ERROR};
INIT_FUNC(mod_cgi_init) {
plugin_data *p;
@ -516,6 +516,48 @@ static int cgi_demux_response(server *srv, handler_ctx *hctx) {
/* parse the response header */
cgi_response_parse(srv, con, p, hctx->response_header);
if (con->http_status >= 300 && con->http_status < 400) {
/*(con->parsed_response & HTTP_LOCATION)*/
data_string *ds;
if (NULL != (ds = (data_string *) array_get_element(con->response.headers, "Location"))
&& ds->value->ptr[0] == '/') {
if (++con->loops_per_request > 5) {
log_error_write(srv, __FILE__, __LINE__, "sb", "too many internal loops while processing request:", con->request.orig_uri);
con->http_status = 500; /* Internal Server Error */
con->mode = DIRECT;
return FDEVENT_HANDLED_FINISHED;
}
if (!buffer_is_equal(con->request.uri, con->request.orig_uri)
&& !array_get_element(con->environment, "REDIRECT_URI")) {
array_set_key_value(con->environment,
CONST_STR_LEN("REDIRECT_URI"),
CONST_BUF_LEN(con->request.orig_uri));
}
buffer_copy_buffer(con->request.uri, ds->value);
if (con->request.content_length) {
if ((off_t)con->request.content_length != chunkqueue_length(con->request_content_queue)) {
con->keep_alive = 0;
}
con->request.content_length = 0;
chunkqueue_reset(con->request_content_queue);
}
if (con->http_status != 307 && con->http_status != 308) {
/* Note: request body (if any) sent to initial dynamic handler
* and is not available to the internal redirect */
con->request.http_method = HTTP_METHOD_GET;
}
connection_response_reset(srv, con); /*(includes con->http_status = 0)*/
con->mode = DIRECT;
return FDEVENT_HANDLED_COMEBACK;
}
}
if (p->conf.xsendfile_allow) {
data_string *ds;
if (NULL != (ds = (data_string *) array_get_element(con->response.headers, "X-Sendfile"))) {
@ -735,6 +777,9 @@ static int cgi_recv_response(server *srv, handler_ctx *hctx) {
/* if we get a IN|HUP and have read everything don't exec the close twice */
return HANDLER_FINISHED;
case FDEVENT_HANDLED_COMEBACK:
cgi_connection_close(srv, hctx);
return HANDLER_COMEBACK;
case FDEVENT_HANDLED_ERROR:
log_error_write(srv, __FILE__, __LINE__, "s", "demuxer failed: ");
@ -769,7 +814,7 @@ static handler_t cgi_handle_fdevent(server *srv, void *ctx, int revents) {
do {
rc = cgi_recv_response(srv,hctx);/*(might invalidate hctx)*/
} while (rc == HANDLER_GO_ON); /*(unless HANDLER_GO_ON)*/
return rc; /* HANDLER_FINISHED or HANDLER_ERROR */
return rc; /* HANDLER_FINISHED or HANDLER_COMEBACK or HANDLER_ERROR */
} else if (!buffer_string_is_empty(hctx->response_header)) {
/* unfinished header package which is a body in reality */
con->file_started = 1;

5
tests/docroot/www/cgi.pl

@ -1,5 +1,10 @@
#!/usr/bin/env perl
if ($ENV{"QUERY_STRING"} eq "internal-redir") {
print "Location: /cgi.pl\r\n\r\n";
exit 0;
}
print "Content-Type: text/html\r\n\r\n";
print $ENV{"SCRIPT_NAME"};

9
tests/mod-cgi.t

@ -8,7 +8,7 @@ BEGIN {
use strict;
use IO::Socket;
use Test::More tests => 15;
use Test::More tests => 16;
use LightyTest;
my $tf = LightyTest->new();
@ -32,6 +32,13 @@ EOF
$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 200, 'HTTP-Content' => '/cgi.pl' } ];
ok($tf->handle_http($t) == 0, 'perl via cgi + pathinfo');
$t->{REQUEST} = ( <<EOF
GET /cgi.pl?internal-redir HTTP/1.0
EOF
);
$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 200 } ];
ok($tf->handle_http($t) == 0, 'perl via cgi and internal redirect from CGI');
$t->{REQUEST} = ( <<EOF
GET /cgi-pathinfo.pl/foo HTTP/1.0
EOF

Loading…
Cancel
Save