/* Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License.
*/
typedefstruct { constchar *name; /* provider name */ constchar *backend; /* backend address, as configured */ constchar *host;
apr_port_t port;
apr_sockaddr_t *backend_addrs; int is_authn; int is_authz;
} fcgi_provider_conf;
typedefstruct { constchar *name; /* provider name */ constchar *default_user; /* this is user if authorizer returns * success and a user expression yields * empty string
*/
ap_expr_info_t *user_expr; /* expr to evaluate to set r->user */ char authoritative; /* fail request if user is rejected? */ char require_basic_auth; /* fail if client didn't send credentials? */
} fcgi_dir_conf;
typedefstruct { /* If an "authnz" provider successfully authenticates, record * the provider name here for checking during authz.
*/ constchar *successful_authnz_provider;
} fcgi_request_notes;
/* * utility function to connect to a peer; generally useful, but * wait for AF_UNIX support in this mod before thinking about how * to make it available to other modules
*/ static apr_status_t connect_to_peer(apr_socket_t **newsock,
request_rec *r,
apr_sockaddr_t *backend_addrs, constchar *backend_name,
apr_interval_time_t timeout)
{
apr_status_t rv = APR_EINVAL; /* returned if no backend addr was provided
*/ int connected = 0;
apr_sockaddr_t *addr = backend_addrs;
if (!required_len) { if (next_elem < envarr->nelts) {
ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
APLOGNO(02499) "couldn't encode envvar '%s' in %"
APR_SIZE_T_FMT " bytes",
elts[next_elem].key, avail_len); /* skip this envvar and continue */
++next_elem; continue;
} /* only an unused element at the end of the array */ break;
}
ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
APLOGNO(02500) "required len for encoding envvars: %"
APR_SIZE_T_FMT ", %d/%d elems processed so far",
required_len, next_elem, envarr->nelts);
body = apr_palloc(temp_pool, required_len);
rv = ap_fcgi_encode_env(r, r->subprocess_env, body, required_len,
&starting_elem); /* we pre-compute, so we can't run out of space */
ap_assert(rv == APR_SUCCESS); /* compute and encode must be in sync */
ap_assert(starting_elem == next_elem);
/* * This header-state logic is from mod_proxy_fcgi.
*/ enum {
HDR_STATE_READING_HEADERS,
HDR_STATE_GOT_CR,
HDR_STATE_GOT_CRLF,
HDR_STATE_GOT_CRLFCR,
HDR_STATE_GOT_LF,
HDR_STATE_DONE_WITH_HEADERS
};
/* Try to find the end of the script headers in the response from the back * end fastcgi server. STATE holds the current header parsing state for this * request. * * Returns 0 if it can't find the end of the headers, and 1 if it found the
* end of the headers. */ staticint handle_headers(request_rec *r, int *state, constchar *readbuf, apr_size_t readlen)
{ constchar *itr = readbuf;
while (readlen--) { if (*itr == '\r') { switch (*state) { case HDR_STATE_GOT_CRLF:
*state = HDR_STATE_GOT_CRLFCR; break;
/* * Now get the actual content of the record.
*/ if (readbuflen != 0) {
rv = recv_data(conf, r, s, readbuf, &readbuflen); if (rv != APR_SUCCESS) { break;
}
readbuf[readbuflen] = '\0';
}
switch (type) { case AP_FCGI_STDOUT: /* Response headers and optional body */ if (clen != 0) {
b = apr_bucket_transient_create(readbuf,
readbuflen,
r->connection->bucket_alloc);
APR_BRIGADE_INSERT_TAIL(ob, b);
if (!seen_end_of_headers) { int st = handle_headers(r, &header_state,
readbuf, readbuflen);
/* FCGI has its own body framing mechanism which we don't * match against any provided Content-Length, so let the * core determine C-L vs T-E based on what's actually sent.
*/ if (!apr_table_get(r->subprocess_env, AP_TRUST_CGILIKE_CL_ENVVAR))
apr_table_unset(r->headers_out, "Content-Length");
apr_table_unset(r->headers_out, "Transfer-Encoding");
if (rspbuf) { /* caller wants to see response body, * if any
*/
apr_status_t tmprv;
if (rspbuflen) {
*rspbuflen = orspbuflen;
}
tmprv = apr_brigade_flatten(ob, rspbuf, rspbuflen); if (tmprv != APR_SUCCESS) { /* should not occur for these bucket types; * does not indicate overflow
*/
ap_log_rerror(APLOG_MARK, APLOG_ERR, tmprv, r,
APLOGNO(02505) "%s: error " "flattening response body",
fn);
}
}
if (status != OK) {
r->status = status;
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
APLOGNO(02506) "%s: Error parsing " "script headers from %s",
fn, conf->backend);
rv = APR_EINVAL; break;
}
apr_pool_clear(temp_pool);
} else { /* We're still looking for the end of the * headers, so this part of the data will need
* to persist. */
apr_bucket_setaside(b, temp_pool);
}
}
/* If we didn't read all the data go back and get the
* rest of it. */ if (clen > readbuflen) {
clen -= readbuflen; goto recv_again;
}
} break;
case AP_FCGI_STDERR: /* Text to log */ if (clen) {
ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
APLOGNO(02507) "%s: Logged from %s: '%s'",
fn, conf->backend, readbuf);
}
if (clen > readbuflen) {
clen -= readbuflen; goto recv_again; /* continue reading this record */
} break;
case AP_FCGI_END_REQUEST:
done = 1; break;
default:
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
APLOGNO(02508) "%s: Got bogus FastCGI record type " "%d", fn, type); break;
} /* Leave on above switch's inner error. */ if (rv != APR_SUCCESS) { break;
}
if (rv == APR_SUCCESS && !seen_end_of_headers) {
rv = APR_EINVAL;
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
APLOGNO(02510) "%s: Never reached end of script headers",
fn);
}
return rv;
}
/* almost from mod_fcgid */ staticint mod_fcgid_modify_auth_header(void *vars, constchar *key, constchar *val)
{ /* When the application gives a 200 response, the server ignores response headers whose names aren't prefixed with Variable- prefix, and ignores
any response content */ if (ap_cstr_casecmpn(key, "Variable-", 9) == 0)
apr_table_setn(vars, key, val); return 1;
}
/* The responder owns the request body, not the authorizer. * Don't even send an empty AP_FCGI_STDIN block. libfcgi doesn't care, * but it wasn't sent to authorizers by mod_fastcgi or mod_fcgi and * may be unhandled by the app. Additionally, the FastCGI spec does * not mention FCGI_STDIN in the Authorizer description, though it * does describe FCGI_STDIN elsewhere in more general terms than * simply a wrapper for the client's request body.
*/
if (rv != APR_SUCCESS) { /* some sort of mechanical problem */
r->status = HTTP_INTERNAL_SERVER_ERROR;
} else {
ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
APLOGNO(02515) "%s: Received HTTP status %d",
fn, r->status);
}
r->subprocess_env = saved_subprocess_env;
if (r->status == HTTP_OK) { /* An Authorizer application's 200 response may include headers * whose names are prefixed with Variable-, and they should be * available to subsequent phases via subprocess_env (and yanked * from the client response).
*/
apr_table_t *vars = apr_table_make(temp_pool, /* not used to allocate * any values that end up * in r->(anything)
*/
10);
apr_table_do(mod_fcgid_modify_auth_header, vars,
r->err_headers_out, NULL);
apr_table_do(fix_auth_header, r, vars, NULL);
}
if (r->status == HTTP_OK) { if (dconf->user_expr) { constchar *err; constchar *user = ap_expr_str_exec(r, dconf->user_expr,
&err); if (user && strlen(user)) {
r->user = apr_pstrdup(r->pool, user);
ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
APLOGNO(02519) "%s: Setting user to '%s'",
fn, r->user);
} elseif (user && dconf->default_user) {
r->user = apr_pstrdup(r->pool, dconf->default_user);
} elseif (user) {
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
APLOGNO(02520) "%s: Failure extracting user " "after calling authorizer: user expression " "yielded empty string (variable not set?)",
fn);
r->status = HTTP_INTERNAL_SERVER_ERROR;
} else { /* unexpected error, not even an empty string was returned */
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
APLOGNO(02521) "%s: Failure extracting user " "after calling authorizer: %s",
fn, err);
r->status = HTTP_INTERNAL_SERVER_ERROR;
}
} if (conf->is_authz) { /* combined authn/authz phase, so app won't be invoked for authz * * Remember that the request was successfully authorized by this * provider.
*/
fcgi_request_notes *rnotes = apr_palloc(r->pool, sizeof(*rnotes));
rnotes->successful_authnz_provider = conf->name;
ap_set_module_config(r->request_config, &authnz_fcgi_module,
rnotes);
}
} else { /* From the spec: * For Authorizer response status values other than "200" (OK), the * Web server denies access and sends the response status, headers, * and content back to the HTTP client. * But: * This only makes sense if this authorizer is authoritative.
*/ if (rspbuflen > 0 && !dconf->authoritative) {
ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
APLOGNO(02522) "%s: Ignoring response body from non-" "authoritative authorizer", fn);
} elseif (rspbuflen > 0) { if (rspbuflen == sizeof rspbuf - 1) { /* apr_brigade_flatten() interface :( */
ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
APLOGNO(02523) "%s: possible overflow handling " "response body", fn);
}
rspbuf[rspbuflen] = '\0'; /* we reserved an extra byte for '\0' */
ap_custom_response(r, r->status, rspbuf); /* API makes a copy */
}
}
/* currently we just have a single directive applicable to a * directory, so if it is set then grab all fields from fcgi_dir_conf
*/ if (over->name) {
memcpy(a, over, sizeof(*a));
} else {
memcpy(a, base, sizeof(*a));
}
return a;
}
AP_DECLARE_MODULE(authnz_fcgi) =
{
STANDARD20_MODULE_STUFF,
create_dir_conf, /* dir config creater */
merge_dir_conf, /* dir merger */
NULL, /* server config */
NULL, /* merge server config */
fcgi_cmds, /* command apr_table_t */
fcgi_register_hooks /* register hooks */
};
¤ Dauer der Verarbeitung: 0.22 Sekunden
(vorverarbeitet)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.