summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--auth_mellon.h12
-rw-r--r--auth_mellon_config.c123
-rw-r--r--auth_mellon_handler.c359
-rw-r--r--auth_mellon_httpclient.c51
-rw-r--r--mod_auth_mellon.c1
5 files changed, 473 insertions, 73 deletions
diff --git a/auth_mellon.h b/auth_mellon.h
index b1880eb..57e5ffb 100644
--- a/auth_mellon.h
+++ b/auth_mellon.h
@@ -49,6 +49,7 @@
#include "apr_md5.h"
#include "apr_file_info.h"
#include "apr_file_io.h"
+#include "apr_xml.h"
#include "ap_config.h"
#include "httpd.h"
@@ -116,7 +117,6 @@ typedef enum {
am_decoder_feide,
} am_decoder_t;
-
typedef struct am_dir_cfg_rec {
/* enable_mellon is used to enable auth_mellon for a location.
*/
@@ -144,7 +144,7 @@ typedef struct am_dir_cfg_rec {
const char *sp_metadata_file;
const char *sp_private_key_file;
const char *sp_cert_file;
- const char *idp_metadata_file;
+ apr_hash_t *idp_metadata_files;
const char *idp_public_key_file;
const char *idp_ca_file;
@@ -157,6 +157,9 @@ typedef struct am_dir_cfg_rec {
/* Login path for IdP initiated logins */
const char *login_path;
+ /* IdP discovery service */
+ const char *discovery_url;
+
/* Mutex to prevent us from creating several lasso server objects. */
apr_thread_mutex_t *server_mutex;
/* Cached lasso server object. */
@@ -242,8 +245,9 @@ int am_check_uid(request_rec *r);
int am_handle_metadata(request_rec *r);
-int am_httpclient_get(request_rec *r, const char *uri,
- void **buffer, apr_size_t *size);
+int am_httpclient_get(request_rec *r, const char *uri,
+ void **buffer, apr_size_t *size,
+ apr_time_t timeout, long *status);
int am_httpclient_post(request_rec *r, const char *uri,
const void *post_data, apr_size_t post_length,
const char *content_type,
diff --git a/auth_mellon_config.c b/auth_mellon_config.c
index 767663f..6e805e1 100644
--- a/auth_mellon_config.c
+++ b/auth_mellon_config.c
@@ -83,6 +83,103 @@ static const char *am_set_filestring_slot(cmd_parms *cmd,
}
+/* This function extracts an IdP ProviderID from metadata
+ *
+ * Parameters:
+ * apr_pool_t *p Pool to allocate temporary items from.
+ * server_rec *s The server.
+ * const char *file File containing metadata.
+ * const char **provider The providerID
+ *
+ * Returns:
+ * NULL on success or an error string on failure.
+ *
+ */
+static const char *am_get_proovider_id(apr_pool_t *p,
+ server_rec *s,
+ const char *file,
+ const char **provider)
+{
+ char *data;
+ apr_xml_parser *xp;
+ apr_xml_doc *xd;
+ apr_xml_attr *xa;
+ char error[1024];
+
+ *provider = NULL;
+
+ /*
+ * Get the data
+ */
+ if ((data = am_getfile(p, s, file)) == NULL)
+ return apr_psprintf(p, "Cannot read file %s", file);
+
+ /*
+ * Parse
+ */
+ xp = apr_xml_parser_create(p);
+ if (apr_xml_parser_feed(xp, data, strlen(data)) != 0)
+ return apr_psprintf(p, "Cannot parse %s: %s", file,
+ apr_xml_parser_geterror(xp, error, sizeof(error)));
+
+ if (apr_xml_parser_done(xp, &xd) != 0)
+ return apr_psprintf(p, "Parse error %s: %s", file,
+ apr_xml_parser_geterror(xp, error, sizeof(error)));
+
+ /*
+ * Extract /EntityDescriptor@EntityID
+ */
+ if (strcasecmp(xd->root->name, "EntityDescriptor") != 0)
+ return apr_psprintf(p, "<EntityDescriptor> is not root in %s", file);
+
+ for (xa = xd->root->attr; xa; xa = xa->next)
+ if (strcasecmp(xa->name, "entityID") == 0)
+ break;
+
+ if (xa == NULL)
+ return apr_psprintf(p, "entityID not found in %s", file);
+
+ *provider = xa->value;
+ return NULL;
+}
+
+/* This function handles configuration directives which set an
+ * idp related slot in the module configuration.
+ *
+ * Parameters:
+ * cmd_parms *cmd The command structure for this configuration
+ * directive.
+ * void *struct_ptr Pointer to the current directory configuration.
+ * NULL if we are not in a directory configuration.
+ * const char *arg The string argument following this configuration
+ * directive in the configuraion file.
+ *
+ * Returns:
+ * NULL on success or an error string on failure.
+ */
+static const char *ap_set_idp_string_slot(cmd_parms *cmd,
+ void *struct_ptr,
+ const char *arg)
+{
+ server_rec *s = cmd->server;
+ apr_pool_t *pconf = s->process->pconf;
+ am_dir_cfg_rec *cfg = (am_dir_cfg_rec *)struct_ptr;
+ const char *error;
+ const char *provider_id;
+
+ if ((error = am_get_proovider_id(cmd->pool, s,
+ arg, &provider_id)) != NULL)
+ return apr_psprintf(cmd->pool, "%s - %s", cmd->cmd->name, error);
+
+ apr_hash_set(cfg->idp_metadata_files,
+ apr_pstrdup(pconf, provider_id),
+ APR_HASH_KEY_STRING,
+ apr_pstrdup(pconf, arg));
+
+ return NULL;
+}
+
+
/* This function handles configuration directives which set a string
* slot in the module configuration.
*
@@ -431,8 +528,8 @@ const command_rec auth_mellon_commands[] = {
),
AP_INIT_TAKE1(
"MellonIdPMetadataFile",
- ap_set_string_slot,
- (void *)APR_OFFSETOF(am_dir_cfg_rec, idp_metadata_file),
+ ap_set_idp_string_slot,
+ NULL,
OR_AUTHCFG,
"Full path to xml metadata file for the IdP."
),
@@ -459,6 +556,13 @@ const command_rec auth_mellon_commands[] = {
" Default value is \"/\"."
),
AP_INIT_TAKE1(
+ "MellonDiscoveryURL",
+ ap_set_string_slot,
+ (void *)APR_OFFSETOF(am_dir_cfg_rec, discovery_url),
+ OR_AUTHCFG,
+ "The URL of IdP discovery service. Default is unset."
+ ),
+ AP_INIT_TAKE1(
"MellonEndpointPath",
am_set_endpoint_path,
NULL,
@@ -506,11 +610,11 @@ void *auth_mellon_dir_config(apr_pool_t *p, char *d)
dir->sp_metadata_file = NULL;
dir->sp_private_key_file = NULL;
dir->sp_cert_file = NULL;
- dir->idp_metadata_file = NULL;
+ dir->idp_metadata_files = apr_hash_make(p);
dir->idp_public_key_file = NULL;
dir->idp_ca_file = NULL;
dir->login_path = default_login_path;
-
+ dir->discovery_url = NULL;
apr_thread_mutex_create(&dir->server_mutex, APR_THREAD_MUTEX_DEFAULT, p);
@@ -602,9 +706,10 @@ void *auth_mellon_dir_merge(apr_pool_t *p, void *base, void *add)
add_cfg->sp_cert_file :
base_cfg->sp_cert_file);
- new_cfg->idp_metadata_file = (add_cfg->idp_metadata_file ?
- add_cfg->idp_metadata_file :
- base_cfg->idp_metadata_file);
+ new_cfg->idp_metadata_files = apr_hash_copy(p,
+ (apr_hash_count(add_cfg->idp_metadata_files) > 0) ?
+ add_cfg->idp_metadata_files :
+ base_cfg->idp_metadata_files);
new_cfg->idp_public_key_file = (add_cfg->idp_public_key_file ?
add_cfg->idp_public_key_file :
@@ -618,6 +723,10 @@ void *auth_mellon_dir_merge(apr_pool_t *p, void *base, void *add)
add_cfg->login_path :
base_cfg->login_path);
+ new_cfg->discovery_url = (add_cfg->discovery_url ?
+ add_cfg->discovery_url :
+ base_cfg->discovery_url);
+
apr_thread_mutex_create(&new_cfg->server_mutex,
APR_THREAD_MUTEX_DEFAULT, p);
new_cfg->server = NULL;
diff --git a/auth_mellon_handler.c b/auth_mellon_handler.c
index da0fc61..ad76027 100644
--- a/auth_mellon_handler.c
+++ b/auth_mellon_handler.c
@@ -30,7 +30,6 @@
#endif /* HAVE_lasso_server_new_from_buffers */
-#ifdef HAVE_lasso_server_new_from_buffers
/* This function produces the endpoint URL
*
* Parameters:
@@ -69,6 +68,7 @@ static char *am_get_endpoint_url(request_rec *r)
port, cfg->endpoint_path);
}
+#ifdef HAVE_lasso_server_new_from_buffers
/* This function generates metadata
*
* Parameters:
@@ -132,12 +132,213 @@ static char *am_generate_metadata(apr_pool_t *p, request_rec *r)
}
#endif /* HAVE_lasso_server_new_from_buffers */
-static LassoServer *am_get_lasso_server(request_rec *r)
+/* This function returns the first configured IdP
+ *
+ * Parameters:
+ * request_rec *r The request we received.
+ *
+ * Returns:
+ * the providerID, or NULL if an error occured
+ */
+static const char *am_first_idp(request_rec *r)
{
- am_dir_cfg_rec *cfg;
- gint ret;
+ am_dir_cfg_rec *cfg = am_get_dir_cfg(r);
+ apr_hash_index_t *index;
+ const char *provider_id;
+ apr_ssize_t len;
+ void *idp_metadata_file;
+
+ index = apr_hash_first(r->pool, cfg->idp_metadata_files);
+ if (index == NULL)
+ return NULL;
+
+ apr_hash_this(index, (const void **)&provider_id,
+ &len, &idp_metadata_file);
+ return provider_id;
+}
+
+/* This returns built-in IdP discovery timeout
+ *
+ * Parameters:
+ * request_rec *r The request we received.
+ *
+ * Returns:
+ * the timeout, -1 if not enabled.
+ */
+static long am_builtin_discovery_timeout(request_rec *r)
+{
+ am_dir_cfg_rec *cfg = am_get_dir_cfg(r);
+ const char *builtin = "builtin:get-metadata";
+ const char *timeout = "?timeout=";
+ const char *cp;
+ const long default_timeout = 1L;
+
+ if ((cfg->discovery_url == NULL) ||
+ (strncmp(cfg->discovery_url, builtin, strlen(builtin)) != 0))
+ return -1;
+
+ cp = cfg->discovery_url + strlen(builtin);
+ if (strncmp(cp, timeout, strlen(timeout)) != 0)
+ return default_timeout;
+
+ cp += strlen(timeout);
+ return atoi(cp);
+}
+
+/* This function selects an IdP and returns its provider_id
+ *
+ * Parameters:
+ * request_rec *r The request we received.
+ *
+ * Returns:
+ * the provider_id, or NULL if an error occured
+ */
+static const char *am_get_idp(request_rec *r)
+{
+ am_dir_cfg_rec *cfg = am_get_dir_cfg(r);
+ const char *idp_provider_id;
+ const char *idp_metadata_file;
+ apr_hash_index_t *index;
+ long timeout;
+
+ /*
+ * If we have a single IdP, return that one.
+ */
+ if (apr_hash_count(cfg->idp_metadata_files) == 1)
+ return am_first_idp(r);
+
+ /*
+ * If IdP discovery handed us an IdP, try to use it.
+ */
+ idp_provider_id = am_extract_query_parameter(r->pool, r->args, "IdP");
+ if (idp_provider_id != NULL) {
+ int rc;
+
+ rc = am_urldecode((char *)idp_provider_id);
+ if (rc != OK) {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, rc, r,
+ "Could not urldecode IdP discovery value.");
+ idp_provider_id = NULL;
+ } else {
+ idp_metadata_file = apr_hash_get(cfg->idp_metadata_files,
+ idp_provider_id,
+ APR_HASH_KEY_STRING);
+ if (idp_metadata_file == NULL)
+ idp_provider_id = NULL;
+ }
+
+ /*
+ * If we do not know about it, fall back to default.
+ */
+ if (idp_provider_id == NULL) {
+ ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
+ "IdP discovery returned unknown or inexistant IdP");
+ idp_provider_id = am_first_idp(r);
+ }
+
+ return idp_provider_id;
+ }
+
+ /*
+ * If built-in IdP discovery is not configured, return default.
+ */
+ timeout = am_builtin_discovery_timeout(r);
+ if (timeout == -1)
+ return am_first_idp(r);
+
+ /*
+ * Otherwise, proceed with built-in IdP discovery:
+ * send probes for all configures IdP to check availability.
+ * The first to answer is chosen. On error, use default.
+ */
+ for (index = apr_hash_first(r->pool, cfg->idp_metadata_files);
+ index;
+ index = apr_hash_next(index)) {
+ void *buffer;
+ apr_size_t len;
+ apr_ssize_t slen;
+ long status;
+
+ apr_hash_this(index,
+ (const void **)&idp_provider_id,
+ &slen,
+ (void *)&idp_metadata_file);
+
+ status = 0;
+ if (am_httpclient_get(r, idp_provider_id, &buffer, &len,
+ timeout, &status) != OK)
+ continue;
+
+ if (status != HTTP_OK) {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
+ "Cannot probe %s: IdP returned HTTP %ld",
+ idp_provider_id, status);
+ continue;
+ }
+
+ /* We got some succes */
+ return idp_provider_id;
+ }
+
+ /*
+ * No IdP answered, use default
+ * Perhaps we should redirect to an error page instead.
+ */
+ return am_first_idp(r);
+}
+
+/*
+ * This function loads all IdP metadata in a lasso server
+ *
+ * Parameters:
+ * request_rec *r The request we received.
+ *
+ * Returns:
+ * number of loaded providers
+ */
+static int am_server_add_providers(request_rec *r)
+{
+ am_dir_cfg_rec *cfg = am_get_dir_cfg(r);
+ const char *idp_metadata_file;
+ const char *idp_public_key_file;
+ apr_hash_index_t *index;
+ int count = 0;
+
+ if (apr_hash_count(cfg->idp_metadata_files) == 1)
+ idp_public_key_file = cfg->idp_public_key_file;
+ else
+ idp_public_key_file = NULL;
+
+ for (index = apr_hash_first(r->pool, cfg->idp_metadata_files);
+ index;
+ index = apr_hash_next(index)) {
+ const char *idp_provider_id;
+ apr_ssize_t len;
+ int ret;
+
+ apr_hash_this(index, (const void **)&idp_provider_id,
+ &len, (void *)&idp_metadata_file);
+
+
+ ret = lasso_server_add_provider(cfg->server, LASSO_PROVIDER_ROLE_IDP,
+ idp_metadata_file,
+ idp_public_key_file,
+ cfg->idp_ca_file);
+ if (ret != 0) {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
+ "Error adding IdP \"%s\" to lasso server object.",
+ idp_provider_id);
+ } else {
+ count++;
+ }
+ }
+
+ return count;
+}
- cfg = am_get_dir_cfg(r);
+static LassoServer *am_get_lasso_server(request_rec *r)
+{
+ am_dir_cfg_rec *cfg = am_get_dir_cfg(r);
apr_thread_mutex_lock(cfg->server_mutex);
if(cfg->server == NULL) {
@@ -166,12 +367,7 @@ static LassoServer *am_get_lasso_server(request_rec *r)
return NULL;
}
-
- ret = lasso_server_add_provider(cfg->server, LASSO_PROVIDER_ROLE_IDP,
- cfg->idp_metadata_file,
- cfg->idp_public_key_file,
- cfg->idp_ca_file);
- if(ret != 0) {
+ if (am_server_add_providers(r) == 0) {
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
"Error adding IdP to lasso server object. Please"
" verify the following configuration directives:"
@@ -1452,59 +1648,58 @@ int am_handle_metadata(request_rec *r)
}
-/* This function takes a request for an endpoint and passes it on to the
- * correct handler function.
- *
- * Parameters:
- * request_rec *r The request we are currently handling.
- *
- * Returns:
- * The return value of the endpoint handler function,
- * or HTTP_NOT_FOUND if we don't have a handler for the requested
- * endpoint.
- */
-static int am_endpoint_handler(request_rec *r)
-{
- const char *endpoint;
- am_dir_cfg_rec *dir = am_get_dir_cfg(r);
-
- /* r->uri starts with cfg->endpoint_path, so we can find the endpoint
- * by extracting the string following chf->endpoint_path.
- */
- endpoint = &r->uri[strlen(dir->endpoint_path)];
-
-
- if(!strcmp(endpoint, "postResponse")) {
- return am_handle_post_reply(r);
- } else if(!strcmp(endpoint, "artifactResponse")) {
- return am_handle_artifact_reply(r);
- } else if(!strcmp(endpoint, "metadata")) {
- return OK;
- } else if(!strcmp(endpoint, "logout")
- || !strcmp(endpoint, "logoutRequest")) {
- /* logoutRequest is included for backwards-compatibility
- * with version 0.0.6 and older.
- */
- return am_handle_logout(r);
- } else {
- ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
- "Endpoint \"%s\" not handled by mod_auth_mellon.",
- endpoint);
-
- return HTTP_NOT_FOUND;
- }
-
-}
-
-
static int am_auth_new_ticket(request_rec *r)
{
+ am_dir_cfg_rec *cfg = am_get_dir_cfg(r);
LassoServer *server;
LassoLogin *login;
LassoSamlp2AuthnRequest *request;
gint ret;
char *redirect_to;
+ const char *relay_state;
+ relay_state = am_reconstruct_url(r);
+
+ /* 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 */
+ (am_extract_query_parameter(r->pool, r->args, "IdP") == NULL)) {
+ char *discovery_url;
+ char *return_url;
+ char *endpoint = am_get_endpoint_url(r);
+ char *sep;
+
+ /* If discovery URL already has a ? we append a & */
+ sep = (strchr(cfg->discovery_url, '?')) ? "&" : "?";
+
+ return_url = apr_psprintf(r->pool, "%sauth?ReturnTo=%s",
+ endpoint,
+ am_urlencode(r->pool, relay_state));
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
+ "return_url = %s", return_url);
+ discovery_url = apr_psprintf(r->pool, "%s%sentityID=%smetadata&"
+ "return=%s&returnIDParam=IdP",
+ cfg->discovery_url, sep,
+ am_urlencode(r->pool, endpoint),
+ am_urlencode(r->pool, return_url));
+
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
+ "discovery_url = %s", discovery_url);
+ apr_table_setn(r->headers_out, "Location", discovery_url);
+ return HTTP_SEE_OTHER;
+ }
+
+ /* If IdP discovery is in use and we have an IdP selected,
+ * set the relay_state
+ */
+ if ((cfg->discovery_url != NULL) &&
+ (am_builtin_discovery_timeout(r) == -1)) { /* no built-in discovery */
+ char *return_url;
+
+ return_url = am_extract_query_parameter(r->pool, r->args, "ReturnTo");
+ if ((return_url != NULL) && am_urldecode((char *)return_url) == 0)
+ relay_state = return_url;
+ }
/* Add cookie for cookie test. We know that we should have
* a valid cookie when we return from the IdP after SP-initiated
@@ -1525,7 +1720,7 @@ static int am_auth_new_ticket(request_rec *r)
return HTTP_INTERNAL_SERVER_ERROR;
}
- ret = lasso_login_init_authn_request(login, NULL,
+ ret = lasso_login_init_authn_request(login, am_get_idp(r),
LASSO_HTTP_METHOD_REDIRECT);
if(ret != 0) {
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
@@ -1552,7 +1747,7 @@ static int am_auth_new_ticket(request_rec *r)
LASSO_SAMLP2_REQUEST_ABSTRACT(request)->Consent
= g_strdup(LASSO_SAML2_CONSENT_IMPLICIT);
- LASSO_PROFILE(login)->msg_relayState = g_strdup(am_reconstruct_url(r));
+ LASSO_PROFILE(login)->msg_relayState = g_strdup(relay_state);
ret = lasso_login_build_authn_request_msg(login);
if(ret != 0) {
@@ -1588,6 +1783,53 @@ static int am_auth_new_ticket(request_rec *r)
return HTTP_SEE_OTHER;
}
+/* This function takes a request for an endpoint and passes it on to the
+ * correct handler function.
+ *
+ * Parameters:
+ * request_rec *r The request we are currently handling.
+ *
+ * Returns:
+ * The return value of the endpoint handler function,
+ * or HTTP_NOT_FOUND if we don't have a handler for the requested
+ * endpoint.
+ */
+static int am_endpoint_handler(request_rec *r)
+{
+ const char *endpoint;
+ am_dir_cfg_rec *dir = am_get_dir_cfg(r);
+
+ /* r->uri starts with cfg->endpoint_path, so we can find the endpoint
+ * by extracting the string following chf->endpoint_path.
+ */
+ endpoint = &r->uri[strlen(dir->endpoint_path)];
+
+
+ if(!strcmp(endpoint, "postResponse")) {
+ return am_handle_post_reply(r);
+ } else if(!strcmp(endpoint, "artifactResponse")) {
+ return am_handle_artifact_reply(r);
+ } else if(!strcmp(endpoint, "auth")) {
+ return am_auth_new_ticket(r);
+ } else if(!strcmp(endpoint, "metadata")) {
+ return OK;
+ } else if(!strcmp(endpoint, "logout")
+ || !strcmp(endpoint, "logoutRequest")) {
+ /* logoutRequest is included for backwards-compatibility
+ * with version 0.0.6 and older.
+ */
+ return am_handle_logout(r);
+ } else {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
+ "Endpoint \"%s\" not handled by mod_auth_mellon.",
+ endpoint);
+
+ return HTTP_NOT_FOUND;
+ }
+
+}
+
+
int am_auth_mellon_user(request_rec *r)
{
@@ -1606,7 +1848,6 @@ int am_auth_mellon_user(request_rec *r)
return DECLINED;
}
-
/* Disable all caching within this location. */
am_set_nocache(r);
diff --git a/auth_mellon_httpclient.c b/auth_mellon_httpclient.c
index 6f2f04b..5d274ba 100644
--- a/auth_mellon_httpclient.c
+++ b/auth_mellon_httpclient.c
@@ -246,6 +246,7 @@ static CURL *am_httpclient_init_curl(request_rec *r, const char *uri,
am_hc_block_header_t *bh,
char *curl_error)
{
+ am_dir_cfg_rec *cfg = am_get_dir_cfg(r);
CURL *curl;
CURLcode res;
@@ -311,6 +312,17 @@ static CURL *am_httpclient_init_curl(request_rec *r, const char *uri,
goto cleanup_fail;
}
+ /* If we have a CA configured, try to use it */
+ if (cfg->idp_ca_file != NULL) {
+ res = curl_easy_setopt(curl, CURLOPT_CAINFO, cfg->idp_ca_file);
+ if(res != CURLE_OK) {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
+ "Failed to set SSL CA info %s:"
+ " [%u] %s", cfg->idp_ca_file, res, curl_error);
+ goto cleanup_fail;
+ }
+ }
+
/* Enable fail on http error. */
res = curl_easy_setopt(curl, CURLOPT_FAILONERROR, 1L);
if(res != CURLE_OK) {
@@ -359,7 +371,7 @@ static CURL *am_httpclient_init_curl(request_rec *r, const char *uri,
}
-/* This function downloads data from a specified URI.
+/* This function downloads data from a specified URI, with specified timeout
*
* Parameters:
* request_rec *r The apache request this download is associated
@@ -371,13 +383,16 @@ static CURL *am_httpclient_init_curl(request_rec *r, const char *uri,
* apr_size_t *size This is a pointer to where we will store the length
* of the downloaded data, not including the
* null-terminator we add. This parameter can be NULL.
+ * apr_time_t timeout Timeout in seconds, 0 for no timeout.
+ * long *status Pointer to HTTP status code.
*
* Returns:
* OK on success, or HTTP_INTERNAL_SERVER_ERROR on failure. On failure we
* will write a log message describing the error.
*/
int am_httpclient_get(request_rec *r, const char *uri,
- void **buffer, apr_size_t *size)
+ void **buffer, apr_size_t *size,
+ apr_time_t timeout, long *status)
{
am_hc_block_header_t bh;
CURL *curl;
@@ -393,15 +408,45 @@ int am_httpclient_get(request_rec *r, const char *uri,
return HTTP_INTERNAL_SERVER_ERROR;
}
+ res = curl_easy_setopt(curl, CURLOPT_TIMEOUT, timeout);
+ if(res != CURLE_OK) {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
+ "Failed to download data from the uri \"%s\", "
+ "cannot set timeout to %ld: [%u] %s",
+ uri, (long)timeout, res, curl_error);
+ goto cleanup_fail;
+ }
+
+ res = curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, timeout);
+ if(res != CURLE_OK) {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
+ "Failed to download data from the uri \"%s\", "
+ "cannot set connect timeout to %ld: [%u] %s",
+ uri, (long)timeout, res, curl_error);
+ goto cleanup_fail;
+ }
+
/* Do the download. */
res = curl_easy_perform(curl);
if(res != CURLE_OK) {
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
- "Failed to download data from the uri \"%s\": [%u] %s",
+ "Failed to download data from the uri \"%s\", "
+ "transaction aborted: [%u] %s",
uri, res, curl_error);
goto cleanup_fail;
}
+ if (status != NULL) {
+ res = curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, status);
+ if(res != CURLE_OK) {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
+ "Failed to download data from the uri \"%s\", "
+ "no status report: [%u] %s",
+ uri, res, curl_error);
+ goto cleanup_fail;
+ }
+ }
+
/* Free the curl object. */
curl_easy_cleanup(curl);
diff --git a/mod_auth_mellon.c b/mod_auth_mellon.c
index 1697a7b..98ecfce 100644
--- a/mod_auth_mellon.c
+++ b/mod_auth_mellon.c
@@ -47,6 +47,7 @@ static apr_status_t am_global_kill(void *p)
server_rec *s = (server_rec *) p;
am_mod_cfg_rec *m = am_get_mod_cfg(s);
+
if (m->cache) {
/* Destroy the shared memory for session data. */
apr_shm_destroy(m->cache);