Replay POST requets after been sent to the IdP
git-svn-id: https://modmellon.googlecode.com/svn/trunk@67 a716ebb1-153a-0410-b759-cfb97c6a1b53
This commit is contained in:
parent
727a602582
commit
24d4e22219
5
NEWS
5
NEWS
|
@ -1,3 +1,8 @@
|
|||
Version 0.2.5
|
||||
---------------------------------------------------------------------------
|
||||
|
||||
* Replay POST requests after been sent to the IdP
|
||||
|
||||
Version 0.2.4
|
||||
---------------------------------------------------------------------------
|
||||
|
||||
|
|
19
README
19
README
|
@ -104,6 +104,25 @@ MellonCacheSize 100
|
|||
# Default: MellonLockFile "/tmp/mellonLock"
|
||||
MellonLockFile "/tmp/mellonLock"
|
||||
|
||||
# MellonPostDir is the full path of a directory where POST requests are
|
||||
# saved during authentication. This directory must be owned by the Apache
|
||||
# user and be mode 700. We will attempt to create it if it does not exist.
|
||||
# Default: MellonPostDir "/var/tmp/mellonpost"
|
||||
MellonPostDir "/var/tmp/mellonpost"
|
||||
|
||||
# MellonPostTTL is the delay in seconds before a saved POST request can
|
||||
# be flushed.
|
||||
# Default: MellonPostTTL 900 (15 mn)
|
||||
MellonPostTTL 900
|
||||
|
||||
# MellonPostSize is the maximum size for saved POST requests
|
||||
# Default: MellonPostSize 1073741824 (1 MB)
|
||||
MellonPostSize 1073741824
|
||||
|
||||
# MellonPostCount is the maxmimum amount of saved POST requests
|
||||
# Default: MellonPostCount 100
|
||||
MellonPostCount 100
|
||||
|
||||
###########################################################################
|
||||
# End of global configuration for mod_auth_mellon.
|
||||
###########################################################################
|
||||
|
|
|
@ -89,6 +89,10 @@
|
|||
typedef struct am_mod_cfg_rec {
|
||||
int cache_size;
|
||||
const char *lock_file;
|
||||
const char *post_dir;
|
||||
apr_time_t post_ttl;
|
||||
int post_count;
|
||||
apr_size_t post_size;
|
||||
|
||||
/* These variables can't be allowed to change after the session store
|
||||
* has been initialized. Therefore we copy them before initializing
|
||||
|
@ -257,11 +261,15 @@ char *am_urlencode(apr_pool_t *pool, const char *str);
|
|||
int am_urldecode(char *data);
|
||||
char *am_generate_session_id(request_rec *r);
|
||||
char *am_getfile(apr_pool_t *conf, server_rec *s, const char *file);
|
||||
char *am_get_endpoint_url(request_rec *r);
|
||||
int am_postdir_cleanup(request_rec *s);
|
||||
char *am_htmlencode(request_rec *r, const char *str);
|
||||
int am_save_post(request_rec *r, const char **relay_state);
|
||||
|
||||
|
||||
int am_auth_mellon_user(request_rec *r);
|
||||
int am_check_uid(request_rec *r);
|
||||
int am_handle_metadata(request_rec *r);
|
||||
int am_handler(request_rec *r);
|
||||
|
||||
|
||||
int am_httpclient_get(request_rec *r, const char *uri,
|
||||
|
|
|
@ -56,6 +56,25 @@ static const int default_dump_saml_response = 0;
|
|||
*/
|
||||
static const char *default_login_path = "/";
|
||||
|
||||
/* This is the directory for storing saved POST sessions
|
||||
* the MellonPostDirectory configuration directive if you change this.
|
||||
*/
|
||||
static const char *post_dir = "/var/tmp/mellonpost";
|
||||
|
||||
/* saved POST session time to live
|
||||
* the MellonPostTTL configuration directive if you change this.
|
||||
*/
|
||||
static const apr_time_t post_ttl = 15 * 60;
|
||||
|
||||
/* saved POST session maximum size
|
||||
* the MellonPostSize configuration directive if you change this.
|
||||
*/
|
||||
static const apr_size_t post_size = 1024 * 1024 * 1024;
|
||||
|
||||
/* maximum saved POST sessions
|
||||
* the MellonPostCount configuration directive if you change this.
|
||||
*/
|
||||
static const int post_count = 100;
|
||||
|
||||
/* This function handles configuration directives which set a file
|
||||
* slot in the module configuration. If lasso is recent enough, it
|
||||
|
@ -454,6 +473,38 @@ const command_rec auth_mellon_commands[] = {
|
|||
"The lock file for session synchronization."
|
||||
" Default value is \"/tmp/mellonLock\"."
|
||||
),
|
||||
AP_INIT_TAKE1(
|
||||
"MellonPostDirectory",
|
||||
am_set_module_config_string_slot,
|
||||
(void *)APR_OFFSETOF(am_mod_cfg_rec, post_dir),
|
||||
RSRC_CONF,
|
||||
"The directory for saving POST requests."
|
||||
" Default value is \"/var/tmp/mellonpost\"."
|
||||
),
|
||||
AP_INIT_TAKE1(
|
||||
"MellonPostTTL",
|
||||
am_set_module_config_int_slot,
|
||||
(void *)APR_OFFSETOF(am_mod_cfg_rec, post_ttl),
|
||||
RSRC_CONF,
|
||||
"The time to live for saved POST requests in seconds."
|
||||
" Default value is 15 mn."
|
||||
),
|
||||
AP_INIT_TAKE1(
|
||||
"MellonPostCount",
|
||||
am_set_module_config_int_slot,
|
||||
(void *)APR_OFFSETOF(am_mod_cfg_rec, post_count),
|
||||
RSRC_CONF,
|
||||
"The maximum saved POST sessions at once."
|
||||
" Default value is 100."
|
||||
),
|
||||
AP_INIT_TAKE1(
|
||||
"MellonPostSize",
|
||||
am_set_module_config_int_slot,
|
||||
(void *)APR_OFFSETOF(am_mod_cfg_rec, post_size),
|
||||
RSRC_CONF,
|
||||
"The maximum size of a saved POST, in bytes."
|
||||
" Default value is 1 MB."
|
||||
),
|
||||
|
||||
|
||||
/* Per-location configuration directives. */
|
||||
|
@ -870,6 +921,10 @@ void *auth_mellon_server_config(apr_pool_t *p, server_rec *s)
|
|||
|
||||
mod->cache_size = 100; /* ought to be enough for everybody */
|
||||
mod->lock_file = "/tmp/mellonLock";
|
||||
mod->post_dir = post_dir;
|
||||
mod->post_ttl = post_ttl;
|
||||
mod->post_count = post_count;
|
||||
mod->post_size = post_size;
|
||||
|
||||
mod->init_cache_size = 0;
|
||||
mod->init_lock_file = NULL;
|
||||
|
|
|
@ -30,43 +30,6 @@
|
|||
#endif /* HAVE_lasso_server_new_from_buffers */
|
||||
|
||||
|
||||
/* This function produces the endpoint URL
|
||||
*
|
||||
* Parameters:
|
||||
* request_rec *r The request we received.
|
||||
*
|
||||
* Returns:
|
||||
* the endpoint URL
|
||||
*/
|
||||
static char *am_get_endpoint_url(request_rec *r)
|
||||
{
|
||||
static APR_OPTIONAL_FN_TYPE(ssl_is_https) *am_is_https = NULL;
|
||||
am_dir_cfg_rec *cfg = am_get_dir_cfg(r);
|
||||
apr_pool_t *p = r->pool;
|
||||
server_rec *s = r->server;
|
||||
apr_port_t default_port;
|
||||
char *port;
|
||||
char *scheme;
|
||||
|
||||
am_is_https = APR_RETRIEVE_OPTIONAL_FN(ssl_is_https);
|
||||
|
||||
if (am_is_https && am_is_https(r->connection)) {
|
||||
scheme = "https://";
|
||||
default_port = DEFAULT_HTTPS_PORT;
|
||||
} else {
|
||||
scheme = "http://";
|
||||
default_port = DEFAULT_HTTP_PORT;
|
||||
}
|
||||
|
||||
if (s->addrs->host_port != default_port)
|
||||
port = apr_psprintf(p, ":%d", s->addrs->host_port);
|
||||
else
|
||||
port = "";
|
||||
|
||||
return apr_psprintf(p, "%s%s%s%s", scheme,
|
||||
s->server_hostname,
|
||||
port, cfg->endpoint_path);
|
||||
}
|
||||
|
||||
#ifdef HAVE_lasso_server_new_from_buffers
|
||||
/* This function generates optional metadata for a given element
|
||||
|
@ -1730,6 +1693,129 @@ static int am_handle_artifact_reply(request_rec *r)
|
|||
return am_handle_reply_common(r, login, relay_state, "");
|
||||
}
|
||||
|
||||
/* This function handles responses to repost request
|
||||
*
|
||||
* Parameters:
|
||||
* request_rec *r The request we received.
|
||||
*
|
||||
* Returns:
|
||||
* OK on success, or an error on failure.
|
||||
*/
|
||||
static int am_handle_repost(request_rec *r)
|
||||
{
|
||||
am_mod_cfg_rec *mod_cfg;
|
||||
const char *query;
|
||||
char *cp;
|
||||
char *psf_id;
|
||||
char *psf_filename;
|
||||
char *post_data;
|
||||
char *post_form;
|
||||
char *output;
|
||||
char *last;
|
||||
char *return_url;
|
||||
|
||||
mod_cfg = am_get_mod_cfg(r->server);
|
||||
query = r->parsed_uri.query;
|
||||
|
||||
psf_id = am_extract_query_parameter(r->pool, query, "id");
|
||||
if (psf_id == NULL) {
|
||||
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
|
||||
"Bad repost query: missing id");
|
||||
return HTTP_BAD_REQUEST;
|
||||
}
|
||||
|
||||
/* Check that Id is sane */
|
||||
for (cp = psf_id; *cp; cp++) {
|
||||
if (!apr_isalnum(*cp)) {
|
||||
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
|
||||
"Bad repost query: invalid id \"%s\"", psf_id);
|
||||
return HTTP_BAD_REQUEST;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return_url = am_extract_query_parameter(r->pool, query, "ReturnTo");
|
||||
if (return_url == NULL) {
|
||||
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
|
||||
"Invalid or missing query ReturnTo parameter.");
|
||||
return HTTP_BAD_REQUEST;
|
||||
}
|
||||
|
||||
if (am_urldecode(return_url) != OK) {
|
||||
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Bad repost query: return");
|
||||
return HTTP_BAD_REQUEST;
|
||||
}
|
||||
|
||||
psf_filename = apr_psprintf(r->pool, "%s/%s", mod_cfg->post_dir, psf_id);
|
||||
if ((post_data = am_getfile(r->pool, r->server, psf_filename)) == NULL) {
|
||||
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
|
||||
"Bad repost query: cannot find \"%s\"", psf_filename);
|
||||
return HTTP_BAD_REQUEST;
|
||||
}
|
||||
|
||||
post_form = "";
|
||||
for (cp = apr_strtok(post_data, "&", &last); cp;
|
||||
cp = apr_strtok(NULL, "&", &last)) {
|
||||
char *item;
|
||||
char *last2;
|
||||
char *name;
|
||||
char *value;
|
||||
char *input_item;
|
||||
|
||||
item = apr_pstrdup(r->pool, cp);
|
||||
|
||||
name = apr_strtok(item, "=", &last2);
|
||||
value = apr_strtok(NULL, "=", &last2);
|
||||
|
||||
if (name == NULL)
|
||||
continue;
|
||||
|
||||
if (value == NULL)
|
||||
value = (char *)"";
|
||||
|
||||
if (am_urldecode(name) != OK) {
|
||||
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
|
||||
"urldecode(\"%s\") failed", name);
|
||||
return HTTP_INTERNAL_SERVER_ERROR;
|
||||
}
|
||||
|
||||
if (am_urldecode(value) != OK) {
|
||||
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
|
||||
"urldecode(\"%s\") failed", value);
|
||||
return HTTP_INTERNAL_SERVER_ERROR;
|
||||
}
|
||||
|
||||
input_item = apr_psprintf(r->pool,
|
||||
" <input type=\"hidden\" name=\"%s\" value=\"%s\">\n",
|
||||
am_htmlencode(r, name), am_htmlencode(r, value));
|
||||
post_form = apr_pstrcat(r->pool, post_form, input_item, NULL);
|
||||
}
|
||||
|
||||
r->content_type = "text/html";
|
||||
output = apr_psprintf(r->pool,
|
||||
"<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">\n"
|
||||
"<html>\n"
|
||||
" <head>\n"
|
||||
" <title>SAML rePOST request</title>\n"
|
||||
" </head>\n"
|
||||
" <body onload=\"document.getElementById('form').submit();\">\n"
|
||||
" <noscript>\n"
|
||||
" Your browser does not support Javascript, \n"
|
||||
" you must click the button below to proceed.\n"
|
||||
" </noscript>\n"
|
||||
" <form id=\"form\" method=\"POST\" action=\"%s\">\n%s"
|
||||
" <noscript>\n"
|
||||
" <input type=\"submit\">\n"
|
||||
" </noscript>\n"
|
||||
" </form>\n"
|
||||
" </body>\n"
|
||||
"</html>\n",
|
||||
return_url, post_form);
|
||||
|
||||
ap_rputs(output, r);
|
||||
return OK;
|
||||
}
|
||||
|
||||
|
||||
/* This function handles responses to metadata request
|
||||
*
|
||||
|
@ -1739,14 +1825,46 @@ static int am_handle_artifact_reply(request_rec *r)
|
|||
* Returns:
|
||||
* OK on success, or an error on failure.
|
||||
*/
|
||||
int am_handle_metadata(request_rec *r)
|
||||
static int am_handle_metadata(request_rec *r)
|
||||
{
|
||||
am_dir_cfg_rec *cfg = am_get_dir_cfg(r);
|
||||
const char *endpoint;
|
||||
#ifdef HAVE_lasso_server_new_from_buffers
|
||||
LassoServer *server;
|
||||
const char *data;
|
||||
|
||||
server = am_get_lasso_server(r);
|
||||
if(server == NULL)
|
||||
return HTTP_INTERNAL_SERVER_ERROR;
|
||||
|
||||
data = cfg->sp_metadata_file;
|
||||
if (data == NULL)
|
||||
return HTTP_INTERNAL_SERVER_ERROR;
|
||||
|
||||
r->content_type = "application/samlmetadata+xml";
|
||||
|
||||
ap_rputs(data, r);
|
||||
|
||||
return OK;
|
||||
#else /* ! HAVE_lasso_server_new_from_buffers */
|
||||
|
||||
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
|
||||
"metadata publishing require lasso 2.2.2 or higher");
|
||||
return HTTP_NOT_FOUND;
|
||||
#endif
|
||||
}
|
||||
|
||||
/* This function handles responses to request on our endpoint
|
||||
*
|
||||
* Parameters:
|
||||
* request_rec *r The request we received.
|
||||
*
|
||||
* Returns:
|
||||
* OK on success, or an error on failure.
|
||||
*/
|
||||
int am_handler(request_rec *r)
|
||||
{
|
||||
am_dir_cfg_rec *cfg = am_get_dir_cfg(r);
|
||||
const char *endpoint;
|
||||
|
||||
/* Check if this is a request for one of our endpoints. We check if
|
||||
* the uri starts with the path set with the MellonEndpointPath
|
||||
|
@ -1755,11 +1873,6 @@ int am_handle_metadata(request_rec *r)
|
|||
if(strstr(r->uri, cfg->endpoint_path) != r->uri)
|
||||
return DECLINED;
|
||||
|
||||
endpoint = &r->uri[strlen(cfg->endpoint_path)];
|
||||
if (strcmp(endpoint, "metadata") != 0)
|
||||
return DECLINED;
|
||||
|
||||
#ifdef HAVE_lasso_server_new_from_buffers
|
||||
/* Make sure that this is a GET request. */
|
||||
if(r->method_number != M_GET) {
|
||||
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
|
||||
|
@ -1779,27 +1892,17 @@ int am_handle_metadata(request_rec *r)
|
|||
return DECLINED;
|
||||
}
|
||||
|
||||
server = am_get_lasso_server(r);
|
||||
if(server == NULL)
|
||||
return HTTP_INTERNAL_SERVER_ERROR;
|
||||
|
||||
data = cfg->sp_metadata_file;
|
||||
if (data == NULL)
|
||||
return HTTP_INTERNAL_SERVER_ERROR;
|
||||
|
||||
r->content_type = "application/samlmetadata+xml";
|
||||
|
||||
ap_rputs(data, r);
|
||||
|
||||
return OK;
|
||||
#else /* ! HAVE_lasso_server_new_from_buffers */
|
||||
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
|
||||
"metadata publishing require lasso 2.2.2 or higher");
|
||||
return HTTP_NOT_FOUND;
|
||||
#endif
|
||||
endpoint = &r->uri[strlen(cfg->endpoint_path)];
|
||||
if (strcmp(endpoint, "metadata") == 0)
|
||||
return am_handle_metadata(r);
|
||||
else if (strcmp(endpoint, "repost") == 0)
|
||||
return am_handle_repost(r);
|
||||
else
|
||||
return DECLINED;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static int am_auth_new_ticket(request_rec *r)
|
||||
{
|
||||
am_dir_cfg_rec *cfg = am_get_dir_cfg(r);
|
||||
|
@ -1812,6 +1915,12 @@ static int am_auth_new_ticket(request_rec *r)
|
|||
|
||||
relay_state = am_reconstruct_url(r);
|
||||
|
||||
/* If this is a POST request, attempt to save it */
|
||||
if (r->method_number == M_POST) {
|
||||
if (am_save_post(r, &relay_state) != OK)
|
||||
return HTTP_INTERNAL_SERVER_ERROR;
|
||||
}
|
||||
|
||||
/* Check if IdP discovery is in use and no IdP was selected yet */
|
||||
if ((cfg->discovery_url != NULL) &&
|
||||
(am_builtin_discovery_timeout(r) == -1) && /* no built-in discovery */
|
||||
|
@ -1965,6 +2074,8 @@ static int am_endpoint_handler(request_rec *r)
|
|||
return am_auth_new_ticket(r);
|
||||
} else if(!strcmp(endpoint, "metadata")) {
|
||||
return OK;
|
||||
} else if(!strcmp(endpoint, "repost")) {
|
||||
return OK;
|
||||
} else if(!strcmp(endpoint, "logout")
|
||||
|| !strcmp(endpoint, "logoutRequest")) {
|
||||
/* logoutRequest is included for backwards-compatibility
|
||||
|
|
|
@ -587,3 +587,355 @@ char *am_getfile(apr_pool_t *conf, server_rec *s, const char *file)
|
|||
|
||||
return data;
|
||||
}
|
||||
|
||||
/*
|
||||
* Create a directory for saved POST sessions, check for proper permissions
|
||||
*
|
||||
* Parameters:
|
||||
* request_rec *r The current request
|
||||
*
|
||||
* Returns:
|
||||
* OK on success, or HTTP_INTERNAL_SERVER on failure.
|
||||
*/
|
||||
static int am_postdir_mkdir(request_rec *r)
|
||||
{
|
||||
apr_int32_t wanted;
|
||||
apr_finfo_t afi;
|
||||
apr_status_t rv;
|
||||
char buffer[512];
|
||||
am_mod_cfg_rec *mod_cfg;
|
||||
apr_fileperms_t mode;
|
||||
apr_uid_t user;
|
||||
apr_uid_t group;
|
||||
apr_fileperms_t prot;
|
||||
|
||||
mod_cfg = am_get_mod_cfg(r->server);
|
||||
|
||||
mode = APR_FPROT_UREAD|APR_FPROT_UWRITE|APR_FPROT_UEXECUTE;
|
||||
if ((rv = apr_dir_make_recursive(mod_cfg->post_dir, mode, r->pool)) != OK) {
|
||||
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
|
||||
"cannot create POST directory \"%s\": %s",
|
||||
mod_cfg->post_dir,
|
||||
apr_strerror(rv, buffer, sizeof(buffer)));
|
||||
return HTTP_INTERNAL_SERVER_ERROR;
|
||||
}
|
||||
|
||||
/*
|
||||
* The directory may have already existed. Check we really own it
|
||||
*/
|
||||
wanted = APR_FINFO_USER|APR_FINFO_UPROT|APR_FINFO_GPROT|APR_FINFO_WPROT;
|
||||
if (apr_stat(&afi, mod_cfg->post_dir, wanted, r->pool) == OK) {
|
||||
if (apr_uid_current(&user, &group, r->pool) != OK) {
|
||||
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
|
||||
"apr_uid_current failed");
|
||||
return HTTP_INTERNAL_SERVER_ERROR;
|
||||
}
|
||||
|
||||
if (afi.user != user) {
|
||||
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
|
||||
"POST directory \"%s\" must be owned by the same "
|
||||
"user as the web server is running as.",
|
||||
mod_cfg->post_dir);
|
||||
return HTTP_INTERNAL_SERVER_ERROR;
|
||||
}
|
||||
|
||||
prot = APR_FPROT_UREAD|APR_FPROT_UWRITE|APR_FPROT_UEXECUTE;
|
||||
if (afi.protection != prot) {
|
||||
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
|
||||
"Premissions on POST directory \"%s\" must be 0700.",
|
||||
mod_cfg->post_dir);
|
||||
return HTTP_INTERNAL_SERVER_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* Purge outdated saved POST requests. If the MellonPostDir directory
|
||||
* does not exist, create it first.
|
||||
*
|
||||
* Parameters:
|
||||
* request_rec *r The current request
|
||||
*
|
||||
* Returns:
|
||||
* OK on success, or HTTP_INTERNAL_SERVER on failure.
|
||||
*/
|
||||
int am_postdir_cleanup(request_rec *r)
|
||||
{
|
||||
am_mod_cfg_rec *mod_cfg;
|
||||
apr_dir_t *postdir;
|
||||
apr_status_t rv;
|
||||
apr_finfo_t afi;
|
||||
char *fname;
|
||||
int count;
|
||||
|
||||
mod_cfg = am_get_mod_cfg(r->server);
|
||||
|
||||
/*
|
||||
* Open our POST directory or create it.
|
||||
*/
|
||||
if (apr_dir_open(&postdir, mod_cfg->post_dir, r->pool) != OK)
|
||||
return am_postdir_mkdir(r);
|
||||
|
||||
/*
|
||||
* Purge outdated items
|
||||
*/
|
||||
count = 0;
|
||||
do {
|
||||
rv = apr_dir_read(&afi, APR_FINFO_NAME|APR_FINFO_CTIME, postdir);
|
||||
if (rv != OK)
|
||||
break;
|
||||
|
||||
/* Skip dot_files */
|
||||
if (afi.name[0] == '.')
|
||||
continue;
|
||||
|
||||
if (afi.ctime + mod_cfg->post_ttl > apr_time_sec(apr_time_now())) {
|
||||
fname = apr_psprintf(r->pool, "%s/%s", mod_cfg->post_dir, afi.name);
|
||||
(void)apr_file_remove(fname , r->pool);
|
||||
} else {
|
||||
count++;
|
||||
}
|
||||
} while (1 /* CONSTCOND */);
|
||||
|
||||
(void)apr_dir_close(postdir);
|
||||
|
||||
if (count >= mod_cfg->post_count) {
|
||||
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
|
||||
"Too many saved POST sessions. "
|
||||
"Increase MellonPostCount directive.");
|
||||
return HTTP_INTERNAL_SERVER_ERROR;
|
||||
}
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* HTML-encode a string
|
||||
*
|
||||
* Parameters:
|
||||
* request_rec *r The current request
|
||||
* const char *str The string to encode
|
||||
*
|
||||
* Returns:
|
||||
* The encoded string
|
||||
*/
|
||||
char *am_htmlencode(request_rec *r, const char *str)
|
||||
{
|
||||
const char *cp;
|
||||
char *output;
|
||||
apr_size_t outputlen;
|
||||
int i;
|
||||
|
||||
outputlen = 0;
|
||||
for (cp = str; *cp; cp++) {
|
||||
switch (*cp) {
|
||||
case '&':
|
||||
outputlen += 5;
|
||||
break;
|
||||
case '"':
|
||||
outputlen += 6;
|
||||
break;
|
||||
default:
|
||||
outputlen += 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
i = 0;
|
||||
output = apr_palloc(r->pool, outputlen + 1);
|
||||
for (cp = str; *cp; cp++) {
|
||||
switch (*cp) {
|
||||
case '&':
|
||||
(void)strcpy(&output[i], "&");
|
||||
i += 5;
|
||||
break;
|
||||
case '"':
|
||||
(void)strcpy(&output[i], """);
|
||||
i += 6;
|
||||
break;
|
||||
default:
|
||||
output[i] = *cp;
|
||||
i += 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
output[i] = '\0';
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
/* This function produces the endpoint URL
|
||||
*
|
||||
* Parameters:
|
||||
* request_rec *r The request we received.
|
||||
*
|
||||
* Returns:
|
||||
* the endpoint URL
|
||||
*/
|
||||
char *am_get_endpoint_url(request_rec *r)
|
||||
{
|
||||
static APR_OPTIONAL_FN_TYPE(ssl_is_https) *am_is_https = NULL;
|
||||
am_dir_cfg_rec *cfg = am_get_dir_cfg(r);
|
||||
apr_pool_t *p = r->pool;
|
||||
server_rec *s = r->server;
|
||||
apr_port_t default_port;
|
||||
char *port;
|
||||
char *scheme;
|
||||
|
||||
am_is_https = APR_RETRIEVE_OPTIONAL_FN(ssl_is_https);
|
||||
|
||||
if (am_is_https && am_is_https(r->connection)) {
|
||||
scheme = "https://";
|
||||
default_port = DEFAULT_HTTPS_PORT;
|
||||
} else {
|
||||
scheme = "http://";
|
||||
default_port = DEFAULT_HTTP_PORT;
|
||||
}
|
||||
|
||||
if (s->addrs->host_port != default_port)
|
||||
port = apr_psprintf(p, ":%d", s->addrs->host_port);
|
||||
else
|
||||
port = "";
|
||||
|
||||
return apr_psprintf(p, "%s%s%s%s", scheme,
|
||||
s->server_hostname,
|
||||
port, cfg->endpoint_path);
|
||||
}
|
||||
|
||||
/*
|
||||
* The two functions below extract an HTTP header from the request.
|
||||
*
|
||||
* Parameters:
|
||||
* request_rec *r The current request.
|
||||
*
|
||||
* Returns:
|
||||
* 1 if multipart/form-data, 0 otherwise.
|
||||
*/
|
||||
struct am_get_header_state {
|
||||
request_rec *req;
|
||||
const char *header;
|
||||
const char *value;
|
||||
};
|
||||
|
||||
static int am_get_header_callback(void *s, const char *key, const char *val)
|
||||
{
|
||||
struct am_get_header_state *state;
|
||||
|
||||
state = (struct am_get_header_state *)s;
|
||||
|
||||
if (strcmp(key, state->header) != 0)
|
||||
return 1;
|
||||
|
||||
state->value = val;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const char *am_get_header(request_rec *r, const char *header)
|
||||
{
|
||||
struct am_get_header_state state;
|
||||
|
||||
state.req = r;
|
||||
state.header = header;
|
||||
state.value = NULL;
|
||||
|
||||
(void)apr_table_do(am_get_header_callback, &state, r->headers_in, NULL);
|
||||
|
||||
return state.value;
|
||||
}
|
||||
|
||||
/*
|
||||
* This function saves a POST request for later replay and updates
|
||||
* the return URL.
|
||||
*
|
||||
* Parameters:
|
||||
* request_rec *r The current request.
|
||||
* const char **relay_state The returl URL
|
||||
*
|
||||
* Returns:
|
||||
* OK on success, HTTP_INTERNAL_SERVER_ERROR otherwise
|
||||
*/
|
||||
int am_save_post(request_rec *r, const char **relay_state)
|
||||
{
|
||||
am_mod_cfg_rec *mod_cfg;
|
||||
const char *content_type;
|
||||
const char *psf_id;
|
||||
char *psf_name;
|
||||
char *post_data;
|
||||
apr_size_t post_data_len;
|
||||
apr_size_t written;
|
||||
apr_file_t *psf;
|
||||
|
||||
if (am_postdir_cleanup(r) != OK)
|
||||
return HTTP_INTERNAL_SERVER_ERROR;
|
||||
|
||||
/* Check Content-Type */
|
||||
content_type = am_get_header(r, "Content-Type");
|
||||
if ((content_type != NULL) &&
|
||||
(strcmp(content_type, "application/x-www-form-urlencoded") != 0)) {
|
||||
/*
|
||||
* This is probably "multipart/form-data; boundary=XXXXXXXXXX"
|
||||
* The POST request then contains MIME data. We are not yet
|
||||
* able to manage that, so issue an error 500.
|
||||
*/
|
||||
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
|
||||
"Unsupported POST Content-Type \"%s\"", content_type);
|
||||
return HTTP_INTERNAL_SERVER_ERROR;
|
||||
}
|
||||
|
||||
mod_cfg = am_get_mod_cfg(r->server);
|
||||
|
||||
if ((psf_id = am_generate_session_id(r)) == NULL) {
|
||||
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "cannot generate id");
|
||||
return HTTP_INTERNAL_SERVER_ERROR;
|
||||
}
|
||||
|
||||
psf_name = apr_psprintf(r->pool, "%s/%s", mod_cfg->post_dir, psf_id);
|
||||
|
||||
if (apr_file_open(&psf, psf_name,
|
||||
APR_WRITE|APR_CREATE|APR_BINARY,
|
||||
APR_FPROT_UREAD|APR_FPROT_UWRITE,
|
||||
r->pool) != OK) {
|
||||
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
|
||||
"cannot create POST session file");
|
||||
return HTTP_INTERNAL_SERVER_ERROR;
|
||||
}
|
||||
|
||||
if (am_read_post_data(r, &post_data, &post_data_len) != OK) {
|
||||
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "cannot read POST data");
|
||||
(void)apr_file_close(psf);
|
||||
return HTTP_INTERNAL_SERVER_ERROR;
|
||||
}
|
||||
|
||||
if (post_data_len > mod_cfg->post_size) {
|
||||
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
|
||||
"POST data size %" APR_SIZE_T_FMT
|
||||
" exceeds maximum %" APR_SIZE_T_FMT ". "
|
||||
"Increase MellonPostSize directive.",
|
||||
post_data_len, mod_cfg->post_size);
|
||||
(void)apr_file_close(psf);
|
||||
return HTTP_INTERNAL_SERVER_ERROR;
|
||||
}
|
||||
|
||||
written = post_data_len;
|
||||
if ((apr_file_write(psf, post_data, &written) != OK) ||
|
||||
(written != post_data_len)) {
|
||||
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
|
||||
"cannot write to POST session file");
|
||||
(void)apr_file_close(psf);
|
||||
return HTTP_INTERNAL_SERVER_ERROR;
|
||||
}
|
||||
|
||||
if (apr_file_close(psf) != OK) {
|
||||
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
|
||||
"cannot close POST session file");
|
||||
return HTTP_INTERNAL_SERVER_ERROR;
|
||||
}
|
||||
|
||||
*relay_state = apr_psprintf(r->pool, "%srepost?id=%s&ReturnTo=%s",
|
||||
am_get_endpoint_url(r), psf_id,
|
||||
am_urlencode(r->pool, *relay_state));
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
|
|
@ -94,7 +94,6 @@ static int am_global_init(apr_pool_t *conf, apr_pool_t *log,
|
|||
mod->init_cache_size = mod->cache_size;
|
||||
mod->init_lock_file = apr_pstrdup(conf, mod->lock_file);
|
||||
|
||||
|
||||
/* find out the memory size of the cache */
|
||||
mem_size = sizeof(am_cache_entry_t) * mod->init_cache_size;
|
||||
|
||||
|
@ -195,7 +194,7 @@ static void register_hooks(apr_pool_t *p)
|
|||
ap_hook_check_user_id(am_check_uid, NULL, NULL, APR_HOOK_MIDDLE);
|
||||
ap_hook_post_config(am_global_init, NULL, NULL, APR_HOOK_MIDDLE);
|
||||
ap_hook_child_init(am_child_init, NULL, NULL, APR_HOOK_MIDDLE);
|
||||
ap_hook_handler(am_handle_metadata, NULL, NULL, APR_HOOK_MIDDLE);
|
||||
ap_hook_handler(am_handler, NULL, NULL, APR_HOOK_MIDDLE);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue