[mod_fastcgi] Authorizer support with Responder (fixes #321, fixes #322)

import Variable-* from FastCGI authorizer response into con->environment
restart request after FastCGI authorizer if no fastcgi.server docroot

(thx Christoph Kreutzer for initial patch attempt)

x-ref:
  "mod_fastcgi authorizers cannot protect fastcgi responders"
  http://redmine.lighttpd.net/issues/321

x-ref:
  "FastCGI Authorizer support for Variable-name variable passing"
  http://redmine.lighttpd.net/issues/322

github: closes #70
This commit is contained in:
Glenn Strauss 2016-09-18 20:23:59 -04:00
parent 5dfe21acc9
commit 2dcfe1733e
1 changed files with 134 additions and 74 deletions

View File

@ -333,6 +333,7 @@ typedef struct {
fcgi_proc *proc;
fcgi_extension_host *host;
fcgi_extension *ext;
fcgi_extension *ext_auth;
fcgi_connection_state_t state;
time_t state_timestamp;
@ -501,11 +502,8 @@ static handler_ctx * handler_ctx_init(void) {
return hctx;
}
static void handler_ctx_free(server *srv, handler_ctx *hctx) {
if (hctx->host) {
fcgi_host_reset(srv, hctx);
}
static void handler_ctx_free(handler_ctx *hctx) {
/* caller MUST have called fcgi_backend_close(srv, hctx) if necessary */
buffer_free(hctx->response_header);
chunkqueue_free(hctx->rb);
@ -514,6 +512,37 @@ static void handler_ctx_free(server *srv, handler_ctx *hctx) {
free(hctx);
}
static void handler_ctx_clear(handler_ctx *hctx) {
/* caller MUST have called fcgi_backend_close(srv, hctx) if necessary */
hctx->proc = NULL;
hctx->host = NULL;
hctx->ext = NULL;
/*hctx->ext_auth is intentionally preserved to flag that auth occurred*/
hctx->state = FCGI_STATE_INIT;
/*hctx->state_timestamp = 0;*//*(unused; left as-is)*/
chunkqueue_reset(hctx->rb);
chunkqueue_reset(hctx->wb);
hctx->wb_reqlen = 0;
buffer_reset(hctx->response_header);
hctx->fd = -1;
hctx->fde_ndx = -1;
/*hctx->pid = -1;*//*(unused; left as-is)*/
hctx->got_proc = 0;
hctx->reconnects = 0;
hctx->request_id = 0;
hctx->send_content_body = 1;
/*plugin_config conf;*//*(no need to reset for same request)*/
/*hctx->remote_conn = NULL;*//*(no need to reset for same request)*/
/*hctx->plugin_data = NULL;*//*(no need to reset for same request)*/
}
static fcgi_proc *fastcgi_process_init(void) {
fcgi_proc *f;
@ -1511,11 +1540,6 @@ SETDEFAULTS_FUNC(mod_fastcgi_set_defaults) {
host->mode = FCGI_RESPONDER;
} else if (strcmp(fcgi_mode->ptr, "authorizer") == 0) {
host->mode = FCGI_AUTHORIZER;
if (buffer_string_is_empty(host->docroot)) {
log_error_write(srv, __FILE__, __LINE__, "s",
"ERROR: docroot is required for authorizer mode.");
goto error;
}
} else {
log_error_write(srv, __FILE__, __LINE__, "sbs",
"WARNING: unknown fastcgi mode:",
@ -1567,25 +1591,20 @@ static int fcgi_set_state(server *srv, handler_ctx *hctx, fcgi_connection_state_
}
static void fcgi_connection_close(server *srv, handler_ctx *hctx) {
plugin_data *p;
connection *con;
p = hctx->plugin_data;
con = hctx->remote_conn;
static void fcgi_backend_close(server *srv, handler_ctx *hctx) {
if (hctx->fd != -1) {
fdevent_event_del(srv->ev, &(hctx->fde_ndx), hctx->fd);
fdevent_unregister(srv->ev, hctx->fd);
fdevent_sched_close(srv->ev, hctx->fd, 1);
hctx->fd = -1;
}
if (hctx->host && hctx->proc) {
if (hctx->got_proc) {
if (hctx->host) {
if (hctx->proc && hctx->got_proc) {
/* after the connect the process gets a load */
fcgi_proc_load_dec(srv, hctx);
if (p->conf.debug) {
if (hctx->plugin_data->conf.debug) {
log_error_write(srv, __FILE__, __LINE__, "ssdsbsd",
"released proc:",
"pid:", hctx->proc->pid,
@ -1593,10 +1612,20 @@ static void fcgi_connection_close(server *srv, handler_ctx *hctx) {
"load:", hctx->proc->load);
}
}
fcgi_host_reset(srv, hctx);
}
}
static void fcgi_connection_close(server *srv, handler_ctx *hctx) {
plugin_data *p;
connection *con;
handler_ctx_free(srv, hctx);
p = hctx->plugin_data;
con = hctx->remote_conn;
fcgi_backend_close(srv, hctx);
handler_ctx_free(hctx);
con->plugin_ctx[p->id] = NULL;
/* finish response (if not already con->file_started, con->file_finished) */
@ -2244,6 +2273,19 @@ static int fcgi_response_parse(server *srv, connection *con, plugin_data *p, buf
}
}
if (host->mode == FCGI_AUTHORIZER &&
key_len > 9 &&
0 == strncasecmp(key, CONST_STR_LEN("Variable-"))) {
data_string *ds;
if (NULL == (ds = (data_string *)array_get_unused_element(con->environment, TYPE_STRING))) {
ds = data_response_init();
}
buffer_copy_string_len(ds->key, key + 9, key_len - 9);
buffer_copy_string(ds->value, value);
array_insert_unique(con->environment, (data_unset *)ds);
}
switch(key_len) {
case 4:
if (0 == strncasecmp(key, "Date", key_len)) {
@ -3233,9 +3275,16 @@ SUBREQUEST_FUNC(mod_fastcgi_handle_subrequest) {
}
}
if (0 == hctx->wb->bytes_in
? con->state == CON_STATE_READ_POST
: hctx->wb->bytes_in < hctx->wb_reqlen) {
/* (do not receive request body before FCGI_AUTHORIZER has run or else
* the request body is discarded with fcgi_connection_close() of the
* FastCGI Authorizer)
* (hctx->host might not be assigned yet, so check first host in list
* of potential hosts (hctx->ext->hosts[0]->mode != FCGI_AUTHORIZER))*/
if (hctx->ext->hosts[0]->mode != FCGI_AUTHORIZER
&& (0 == hctx->wb->bytes_in
? con->state == CON_STATE_READ_POST
: hctx->wb->bytes_in < hctx->wb_reqlen)) {
/*(64k - 4k to attempt to avoid temporary files
* in conjunction with FDEVENT_STREAM_REQUEST_BUFMIN)*/
if (hctx->wb->bytes_in - hctx->wb->bytes_out > 65536 - 4096
@ -3282,17 +3331,39 @@ static handler_t fcgi_recv_response(server *srv, handler_ctx *hctx) {
* was processed already, and status 200 has been returned. We need
* now to handle authorized request.
*/
buffer *physpath = NULL;
buffer_copy_buffer(con->physical.doc_root, host->docroot);
buffer_copy_buffer(con->physical.basedir, host->docroot);
if (!buffer_string_is_empty(host->docroot)) {
buffer_copy_buffer(con->physical.doc_root, host->docroot);
buffer_copy_buffer(con->physical.basedir, host->docroot);
buffer_copy_buffer(con->physical.path, host->docroot);
buffer_append_string_buffer(con->physical.path, con->uri.path);
buffer_copy_buffer(con->physical.path, host->docroot);
buffer_append_string_buffer(con->physical.path, con->uri.path);
physpath = con->physical.path;
}
con->mode = DIRECT;/*(avoid changing con->state, con->http_status)*/
fcgi_connection_close(srv, hctx);
con->http_status = 0;
con->file_started = 1; /* fcgi_extension won't touch the request afterwards */
fcgi_backend_close(srv, hctx);
handler_ctx_clear(hctx);
/* don't do more than 6 loops here, that normally shouldn't happen */
if (++con->loops_per_request > 5) {
log_error_write(srv, __FILE__, __LINE__, "sb", "too many loops while processing request:", con->request.orig_uri);
con->http_status = 500; /* Internal Server Error */
con->mode = DIRECT;
return HANDLER_FINISHED;
}
/* restart the request so other handlers can process it */
if (physpath) con->physical.path = NULL;
connection_response_reset(srv, con); /*(includes con->http_status = 0)*/
if (physpath) con->physical.path = physpath; /* preserve con->physical.path with modified docroot */
/*(FYI: if multiple FastCGI authorizers were to be supported,
* next one could be started here instead of restarting request)*/
con->mode = DIRECT;
return HANDLER_COMEBACK;
} else {
/* we are done */
fcgi_connection_close(srv, hctx);
@ -3486,12 +3557,11 @@ static handler_t fcgi_check_extension(server *srv, connection *con, void *p_d, i
buffer *fn;
fcgi_extension *extension = NULL;
fcgi_extension_host *host = NULL;
handler_ctx *hctx;
fcgi_extension *ext_auth;
if (con->mode != DIRECT) return HANDLER_GO_ON;
/* Possibly, we processed already this request */
if (con->file_started == 1) return HANDLER_GO_ON;
fn = uri_path_handler ? con->uri.path : con->physical.path;
if (buffer_string_is_empty(fn)) return HANDLER_GO_ON;
@ -3500,6 +3570,9 @@ static handler_t fcgi_check_extension(server *srv, connection *con, void *p_d, i
fcgi_patch_connection(srv, con, p);
hctx = con->plugin_ctx[p->id]; /*(not NULL if FCGI_AUTHORIZER ran)*/
ext_auth = hctx ? hctx->ext_auth : NULL;
/* fastcgi.map-extensions maps extensions to existing fastcgi.server entries
*
* fastcgi.map-extensions = ( ".php3" => ".php" )
@ -3528,7 +3601,8 @@ static handler_t fcgi_check_extension(server *srv, connection *con, void *p_d, i
extension = p->conf.exts->exts[k];
if (buffer_is_equal(ds->value, extension->key)) {
break;
/* do not reuse same authorizer; skip if ext matches ext_auth */
if (extension != ext_auth) break;
}
}
@ -3550,6 +3624,9 @@ static handler_t fcgi_check_extension(server *srv, connection *con, void *p_d, i
if (buffer_is_empty(ext->key)) continue;
/* do not reuse same authorizer; skip if ext matches ext_auth */
if (ext == ext_auth) continue;
ct_len = buffer_string_length(ext->key);
/* check _url_ in the form "/fcgi_pattern" */
@ -3613,30 +3690,9 @@ static handler_t fcgi_check_extension(server *srv, connection *con, void *p_d, i
/* init handler-context */
if (uri_path_handler) {
if (host->check_local == 0) {
handler_ctx *hctx;
char *pathinfo;
hctx = handler_ctx_init();
hctx->remote_conn = con;
hctx->plugin_data = p;
hctx->proc = NULL;
hctx->ext = extension;
hctx->conf.exts = p->conf.exts;
hctx->conf.debug = p->conf.debug;
con->plugin_ctx[p->id] = hctx;
con->mode = p->id;
if (con->conf.log_request_handling) {
log_error_write(srv, __FILE__, __LINE__, "s",
"handling it in mod_fastcgi");
}
if (host->check_local != 0) {
return HANDLER_GO_ON;
} else {
/* do not split path info for authorizer */
if (host->mode != FCGI_AUTHORIZER) {
/* the prefix is the SCRIPT_NAME,
@ -3665,6 +3721,7 @@ static handler_t fcgi_check_extension(server *srv, connection *con, void *p_d, i
* PATH_INFO = /bar
*
*/
char *pathinfo;
/* the rewrite is only done for /prefix/? matches */
if (host->fix_root_path_name && extension->key->ptr[0] == '/' && extension->key->ptr[1] == '\0') {
@ -3680,25 +3737,28 @@ static handler_t fcgi_check_extension(server *srv, connection *con, void *p_d, i
}
}
}
} else {
handler_ctx *hctx;
hctx = handler_ctx_init();
}
hctx->remote_conn = con;
hctx->plugin_data = p;
hctx->proc = NULL;
hctx->ext = extension;
if (!hctx) hctx = handler_ctx_init();
hctx->conf.exts = p->conf.exts;
hctx->conf.debug = p->conf.debug;
hctx->remote_conn = con;
hctx->plugin_data = p;
hctx->proc = NULL;
hctx->ext = extension;
con->plugin_ctx[p->id] = hctx;
hctx->conf.exts = p->conf.exts;
hctx->conf.debug = p->conf.debug;
con->mode = p->id;
if (host->mode == FCGI_AUTHORIZER) {
hctx->ext_auth = hctx->ext; /* save pointer to authorizer ext */
}
if (con->conf.log_request_handling) {
log_error_write(srv, __FILE__, __LINE__, "s", "handling it in mod_fastcgi");
}
con->plugin_ctx[p->id] = hctx;
con->mode = p->id;
if (con->conf.log_request_handling) {
log_error_write(srv, __FILE__, __LINE__, "s", "handling it in mod_fastcgi");
}
return HANDLER_GO_ON;