From eba78415e60b2d597c5ee03afd5245f53129a15a Mon Sep 17 00:00:00 2001 From: ruckc Date: Fri, 3 Nov 2017 13:03:47 -0400 Subject: [PATCH 1/6] add CASRedirectAfterValidation parameter to mimic java-cas-client's redirectAfterValidation parameter --- README | 5 ++++ src/mod_auth_cas.c | 61 ++++++++++++++++++++++++++++++---------------- src/mod_auth_cas.h | 4 ++- 3 files changed, 48 insertions(+), 22 deletions(-) diff --git a/README b/README index f669391..9e5c498 100644 --- a/README +++ b/README @@ -208,6 +208,11 @@ Default: NULL Description: The URL to use when performing a proxy validation. This is currently an unimplemented feature, so setting this will have no effect. +Directive: CASDisableRedirectAfterValidation +Default: On +Description: Whether to redirect to the same URL after ticket validation, but without the + ticket in the parameter. Defaults to on + Directive: CASRootProxiedAs Default: NULL Description: This URL represents the URL that end users may see in the event that diff --git a/src/mod_auth_cas.c b/src/mod_auth_cas.c index 4a55d95..e1fa30a 100644 --- a/src/mod_auth_cas.c +++ b/src/mod_auth_cas.c @@ -194,6 +194,7 @@ void *cas_create_dir_config(apr_pool_t *pool, char *path) c->CASGatewayCookie = CAS_DEFAULT_GATEWAY_COOKIE; c->CASAuthNHeader = CAS_DEFAULT_AUTHN_HEADER; c->CASScrubRequestHeaders = CAS_DEFAULT_SCRUB_REQUEST_HEADERS; + c->CASDisableRedirectAfterValidation = CAS_DEFAULT_REDIRECT_AFTER_VALIDATION; return(c); } @@ -237,6 +238,12 @@ void *cas_merge_dir_config(apr_pool_t *pool, void *BASE, void *ADD) if(add->CASScrubRequestHeaders != NULL && apr_strnatcasecmp(add->CASScrubRequestHeaders, "Off") == 0) c->CASScrubRequestHeaders = NULL; + c->CASDisableRedirectAfterValidation = (add->CASDisableRedirectAfterValidation != CAS_DEFAULT_REDIRECT_AFTER_VALIDATION ? + add->CASDisableRedirectAfterValidation : + base->CASDisableRedirectAfterValidation); + if(add->CASDisableRedirectAfterValidation != NULL && apr_strnatcasecmp(add->CASDisableRedirectAfterValidation, "Off") == 0) + c->CASDisableRedirectAfterValidation = NULL; + return(c); } @@ -1664,6 +1671,7 @@ apr_byte_t isValidCASTicket(request_rec *r, cas_cfg *c, char *ticket, char **use apr_byte_t isValidCASCookie(request_rec *r, cas_cfg *c, char *cookie, char **user, cas_saml_attr **attrs) { + char *path; cas_cache_entry cache; cas_dir_cfg *d = ap_get_module_config(r->per_dir_config, &auth_cas_module); @@ -1677,6 +1685,8 @@ apr_byte_t isValidCASCookie(request_rec *r, cas_cfg *c, char *cookie, char **use return FALSE; } + path = apr_psprintf(r->pool, "%s%s", c->CASCookiePath, cookie); + /* * mitigate session hijacking by not allowing cookies transmitted in the clear to be submitted * for HTTPS URLs and by voiding HTTPS cookies sent in the clear @@ -1836,6 +1846,8 @@ char *getResponseFromServer (request_rec *r, cas_cfg *c, char *ticket) curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L); memcpy(&validateURL, &c->CASValidateURL, sizeof(apr_uri_t)); + if(c->CASDebug) + ap_log_rerror(APLOG_MARK,APLOG_DEBUG,0,r,"MOD_AUTH_CAS: validateUrl: %s", apr_uri_unparse(r->pool, &validateURL, 0)); if(c->CASValidateSAML == FALSE) validateURL.query = apr_psprintf(r->pool, "service=%s&ticket=%s%s", getCASService(r, c), ticket, getCASRenew(r)); else @@ -1844,8 +1856,10 @@ char *getResponseFromServer (request_rec *r, cas_cfg *c, char *ticket) curl_easy_setopt(curl, CURLOPT_URL, apr_uri_unparse(r->pool, &validateURL, 0)); if(curl_easy_perform(curl) != CURLE_OK) { - if(c->CASDebug) + if(c->CASDebug) { + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "MOD_AUTH_CAS: query: %s", validateURL.query); ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "MOD_AUTH_CAS: curl_easy_perform() failed (%s)", curlError); + } goto out; } @@ -2124,31 +2138,35 @@ int cas_authenticate(request_rec *r) if(d->CASAuthNHeader != NULL) apr_table_set(r->headers_in, d->CASAuthNHeader, remoteUser); - if(parametersRemoved == TRUE) { - if(ssl == TRUE && port != 443) - printPort = TRUE; - else if(port != 80) - printPort = TRUE; - - if(c->CASRootProxiedAs.is_initialized) { + if(d->CASDisableRedirectAfterValidation == NULL) { + if(parametersRemoved == TRUE) { + if(ssl == TRUE && port != 443) + printPort = TRUE; + else if(port != 80) + printPort = TRUE; + + if(c->CASRootProxiedAs.is_initialized) { newLocation = apr_psprintf(r->pool, "%s%s%s%s", apr_uri_unparse(r->pool, &c->CASRootProxiedAs, 0), r->uri, ((r->args != NULL) ? "?" : ""), ((r->args != NULL) ? r->args : "")); - } else { + } else { #ifdef APACHE2_0 - if(printPort == TRUE) - newLocation = apr_psprintf(r->pool, "%s://%s:%u%s%s%s", ap_http_method(r), r->server->server_hostname, r->connection->local_addr->port, r->uri, ((r->args != NULL) ? "?" : ""), ((r->args != NULL) ? r->args : "")); - else - newLocation = apr_psprintf(r->pool, "%s://%s%s%s%s", ap_http_method(r), r->server->server_hostname, r->uri, ((r->args != NULL) ? "?" : ""), ((r->args != NULL) ? r->args : "")); + if(printPort == TRUE) + newLocation = apr_psprintf(r->pool, "%s://%s:%u%s%s%s", ap_http_method(r), r->server->server_hostname, r->connection->local_addr->port, r->uri, ((r->args != NULL) ? "?" : ""), ((r->args != NULL) ? r->args : "")); + else + newLocation = apr_psprintf(r->pool, "%s://%s%s%s%s", ap_http_method(r), r->server->server_hostname, r->uri, ((r->args != NULL) ? "?" : ""), ((r->args != NULL) ? r->args : "")); #else - if(printPort == TRUE) - newLocation = apr_psprintf(r->pool, "%s://%s:%u%s%s%s", ap_http_scheme(r), r->server->server_hostname, r->connection->local_addr->port, r->uri, ((r->args != NULL) ? "?" : ""), ((r->args != NULL) ? r->args : "")); - else - newLocation = apr_psprintf(r->pool, "%s://%s%s%s%s", ap_http_scheme(r), r->server->server_hostname, r->uri, ((r->args != NULL) ? "?" : ""), ((r->args != NULL) ? r->args : "")); + if(printPort == TRUE) + newLocation = apr_psprintf(r->pool, "%s://%s:%u%s%s%s", ap_http_scheme(r), r->server->server_hostname, r->connection->local_addr->port, r->uri, ((r->args != NULL) ? "?" : ""), ((r->args != NULL) ? r->args : "")); + else + newLocation = apr_psprintf(r->pool, "%s://%s%s%s%s", ap_http_scheme(r), r->server->server_hostname, r->uri, ((r->args != NULL) ? "?" : ""), ((r->args != NULL) ? r->args : "")); #endif + } + if(c->CASDebug) + ap_log_rerror(APLOG_MARK,APLOG_DEBUG,0,r,"Sending 302 redirect (%s)", newLocation); + apr_table_add(r->headers_out, "Location", newLocation); + return HTTP_MOVED_TEMPORARILY; + } else { + return OK; } - apr_table_add(r->headers_out, "Location", newLocation); - return HTTP_MOVED_TEMPORARILY; - } else { - return OK; } } else { /* sometimes, pages that automatically refresh will re-send the ticket parameter, so let's check any cookies presented or return an error if none */ @@ -2827,6 +2845,7 @@ const command_rec cas_cmds [] = { AP_INIT_TAKE1("CASTimeout", cfg_readCASParameter, (void *) cmd_session_timeout, RSRC_CONF, "Maximum time (in seconds) a session cookie is valid for, regardless of idle time. Set to 0 to allow non-idle sessions to never expire"), AP_INIT_TAKE1("CASIdleTimeout", cfg_readCASParameter, (void *) cmd_idle_timeout, RSRC_CONF, "Maximum time (in seconds) a session can be idle for"), AP_INIT_TAKE1("CASCacheCleanInterval", cfg_readCASParameter, (void *) cmd_cache_interval, RSRC_CONF, "Amount of time (in seconds) between cache cleanups. This value is checked when a new local ticket is issued or when a ticket expires."), + AP_INIT_TAKE1("CASDisableRedirectAfterValidation", ap_set_string_slot, (void *) APR_OFFSETOF(cas_dir_cfg, CASDisableRedirectAfterValidation), ACCESS_CONF, "Set 'Off' to not send a 302 redirect to remove the ticket parameter after ticket validation"), AP_INIT_TAKE1("CASRootProxiedAs", cfg_readCASParameter, (void *) cmd_root_proxied_as, RSRC_CONF, "URL used to access the root of the virtual server (only needed when the server is proxied)"), AP_INIT_TAKE1("CASScrubRequestHeaders", ap_set_string_slot, (void *) APR_OFFSETOF(cas_dir_cfg, CASScrubRequestHeaders), ACCESS_CONF, "Scrub CAS user name and SAML attribute headers from the user's request."), /* authorization options */ diff --git a/src/mod_auth_cas.h b/src/mod_auth_cas.h index e2c59d6..fa54a3f 100644 --- a/src/mod_auth_cas.h +++ b/src/mod_auth_cas.h @@ -83,6 +83,7 @@ #define CAS_DEFAULT_VALIDATE_V2_URL NULL #define CAS_DEFAULT_VALIDATE_URL CAS_DEFAULT_VALIDATE_V2_URL #define CAS_DEFAULT_PROXY_VALIDATE_URL NULL +#define CAS_DEFAULT_REDIRECT_AFTER_VALIDATION NULL #define CAS_DEFAULT_ROOT_PROXIED_AS_URL NULL #define CAS_DEFAULT_COOKIE_ENTROPY 32 #define CAS_DEFAULT_COOKIE_DOMAIN NULL @@ -144,6 +145,7 @@ typedef struct cas_dir_cfg { char *CASGatewayCookie; char *CASAuthNHeader; char *CASScrubRequestHeaders; + char *CASDisableRedirectAfterValidation; } cas_dir_cfg; typedef struct cas_cache_entry { @@ -167,7 +169,7 @@ typedef enum { cmd_loginurl, cmd_validateurl, cmd_proxyurl, cmd_cookie_entropy, cmd_session_timeout, cmd_idle_timeout, cmd_cache_interval, cmd_cookie_domain, cmd_cookie_httponly, cmd_sso, cmd_validate_saml, cmd_attribute_delimiter, cmd_attribute_prefix, - cmd_root_proxied_as, cmd_authoritative + cmd_root_proxied_as, cmd_authoritative, cmd_validate_after_redirect } valid_cmds; module AP_MODULE_DECLARE_DATA auth_cas_module; From 512f34eff831fa14928273d538df9639291b9bb4 Mon Sep 17 00:00:00 2001 From: ruckc Date: Tue, 28 Nov 2017 17:50:36 -0500 Subject: [PATCH 2/6] adjust based on PR-136 comments --- src/mod_auth_cas.c | 14 ++++++-------- src/mod_auth_cas.h | 6 +++--- 2 files changed, 9 insertions(+), 11 deletions(-) diff --git a/src/mod_auth_cas.c b/src/mod_auth_cas.c index e1fa30a..41fe2e5 100644 --- a/src/mod_auth_cas.c +++ b/src/mod_auth_cas.c @@ -194,7 +194,7 @@ void *cas_create_dir_config(apr_pool_t *pool, char *path) c->CASGatewayCookie = CAS_DEFAULT_GATEWAY_COOKIE; c->CASAuthNHeader = CAS_DEFAULT_AUTHN_HEADER; c->CASScrubRequestHeaders = CAS_DEFAULT_SCRUB_REQUEST_HEADERS; - c->CASDisableRedirectAfterValidation = CAS_DEFAULT_REDIRECT_AFTER_VALIDATION; + c->CASDisableRedirectAfterValidation = CAS_DEFAULT_DISABLE_REDIRECT_AFTER_VALIDATION; return(c); } @@ -238,7 +238,7 @@ void *cas_merge_dir_config(apr_pool_t *pool, void *BASE, void *ADD) if(add->CASScrubRequestHeaders != NULL && apr_strnatcasecmp(add->CASScrubRequestHeaders, "Off") == 0) c->CASScrubRequestHeaders = NULL; - c->CASDisableRedirectAfterValidation = (add->CASDisableRedirectAfterValidation != CAS_DEFAULT_REDIRECT_AFTER_VALIDATION ? + c->CASDisableRedirectAfterValidation = (add->CASDisableRedirectAfterValidation != CAS_DEFAULT_DISABLE_REDIRECT_AFTER_VALIDATION ? add->CASDisableRedirectAfterValidation : base->CASDisableRedirectAfterValidation); if(add->CASDisableRedirectAfterValidation != NULL && apr_strnatcasecmp(add->CASDisableRedirectAfterValidation, "Off") == 0) @@ -1671,7 +1671,6 @@ apr_byte_t isValidCASTicket(request_rec *r, cas_cfg *c, char *ticket, char **use apr_byte_t isValidCASCookie(request_rec *r, cas_cfg *c, char *cookie, char **user, cas_saml_attr **attrs) { - char *path; cas_cache_entry cache; cas_dir_cfg *d = ap_get_module_config(r->per_dir_config, &auth_cas_module); @@ -1685,8 +1684,6 @@ apr_byte_t isValidCASCookie(request_rec *r, cas_cfg *c, char *cookie, char **use return FALSE; } - path = apr_psprintf(r->pool, "%s%s", c->CASCookiePath, cookie); - /* * mitigate session hijacking by not allowing cookies transmitted in the clear to be submitted * for HTTPS URLs and by voiding HTTPS cookies sent in the clear @@ -1846,13 +1843,14 @@ char *getResponseFromServer (request_rec *r, cas_cfg *c, char *ticket) curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L); memcpy(&validateURL, &c->CASValidateURL, sizeof(apr_uri_t)); - if(c->CASDebug) - ap_log_rerror(APLOG_MARK,APLOG_DEBUG,0,r,"MOD_AUTH_CAS: validateUrl: %s", apr_uri_unparse(r->pool, &validateURL, 0)); if(c->CASValidateSAML == FALSE) validateURL.query = apr_psprintf(r->pool, "service=%s&ticket=%s%s", getCASService(r, c), ticket, getCASRenew(r)); else validateURL.query = apr_psprintf(r->pool, "TARGET=%s%s", getCASService(r, c), getCASRenew(r)); + if(c->CASDebug) + ap_log_rerror(APLOG_MARK,APLOG_DEBUG,0,r,"MOD_AUTH_CAS: validateUrl: %s", apr_uri_unparse(r->pool, &validateURL, 0)); + curl_easy_setopt(curl, CURLOPT_URL, apr_uri_unparse(r->pool, &validateURL, 0)); if(curl_easy_perform(curl) != CURLE_OK) { @@ -2845,7 +2843,7 @@ const command_rec cas_cmds [] = { AP_INIT_TAKE1("CASTimeout", cfg_readCASParameter, (void *) cmd_session_timeout, RSRC_CONF, "Maximum time (in seconds) a session cookie is valid for, regardless of idle time. Set to 0 to allow non-idle sessions to never expire"), AP_INIT_TAKE1("CASIdleTimeout", cfg_readCASParameter, (void *) cmd_idle_timeout, RSRC_CONF, "Maximum time (in seconds) a session can be idle for"), AP_INIT_TAKE1("CASCacheCleanInterval", cfg_readCASParameter, (void *) cmd_cache_interval, RSRC_CONF, "Amount of time (in seconds) between cache cleanups. This value is checked when a new local ticket is issued or when a ticket expires."), - AP_INIT_TAKE1("CASDisableRedirectAfterValidation", ap_set_string_slot, (void *) APR_OFFSETOF(cas_dir_cfg, CASDisableRedirectAfterValidation), ACCESS_CONF, "Set 'Off' to not send a 302 redirect to remove the ticket parameter after ticket validation"), + AP_INIT_TAKE1("CASDisableRedirectAfterValidation", ap_set_string_slot, (void *) APR_OFFSETOF(cas_dir_cfg, CASDisableRedirectAfterValidation), ACCESS_CONF, "Set 'On' to not send a 302 redirect to remove the ticket parameter after ticket validation"), AP_INIT_TAKE1("CASRootProxiedAs", cfg_readCASParameter, (void *) cmd_root_proxied_as, RSRC_CONF, "URL used to access the root of the virtual server (only needed when the server is proxied)"), AP_INIT_TAKE1("CASScrubRequestHeaders", ap_set_string_slot, (void *) APR_OFFSETOF(cas_dir_cfg, CASScrubRequestHeaders), ACCESS_CONF, "Scrub CAS user name and SAML attribute headers from the user's request."), /* authorization options */ diff --git a/src/mod_auth_cas.h b/src/mod_auth_cas.h index fa54a3f..c45d675 100644 --- a/src/mod_auth_cas.h +++ b/src/mod_auth_cas.h @@ -83,7 +83,7 @@ #define CAS_DEFAULT_VALIDATE_V2_URL NULL #define CAS_DEFAULT_VALIDATE_URL CAS_DEFAULT_VALIDATE_V2_URL #define CAS_DEFAULT_PROXY_VALIDATE_URL NULL -#define CAS_DEFAULT_REDIRECT_AFTER_VALIDATION NULL +#define CAS_DEFAULT_DISABLE_REDIRECT_AFTER_VALIDATION NULL #define CAS_DEFAULT_ROOT_PROXIED_AS_URL NULL #define CAS_DEFAULT_COOKIE_ENTROPY 32 #define CAS_DEFAULT_COOKIE_DOMAIN NULL @@ -99,7 +99,7 @@ #define CAS_DEFAULT_SSO_ENABLED FALSE #define CAS_DEFAULT_AUTHORITATIVE FALSE -#define CAS_MAX_RESPONSE_SIZE 65536 +#define CAS_MAX_RESPONSE_SIZE 524288 #define CAS_MAX_ERROR_SIZE 1024 #define CAS_MAX_XML_SIZE 1024 @@ -169,7 +169,7 @@ typedef enum { cmd_loginurl, cmd_validateurl, cmd_proxyurl, cmd_cookie_entropy, cmd_session_timeout, cmd_idle_timeout, cmd_cache_interval, cmd_cookie_domain, cmd_cookie_httponly, cmd_sso, cmd_validate_saml, cmd_attribute_delimiter, cmd_attribute_prefix, - cmd_root_proxied_as, cmd_authoritative, cmd_validate_after_redirect + cmd_root_proxied_as, cmd_authoritative } valid_cmds; module AP_MODULE_DECLARE_DATA auth_cas_module; From 2fda60886144282a0e09d5c5eaa3674e504b56a7 Mon Sep 17 00:00:00 2001 From: ruckc Date: Tue, 28 Nov 2017 18:00:04 -0500 Subject: [PATCH 3/6] adjust README verbiage for CASDisableRedirectAfterValidation --- README | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/README b/README index 9e5c498..699984d 100644 --- a/README +++ b/README @@ -209,9 +209,12 @@ Description: The URL to use when performing a proxy validation. This is current an unimplemented feature, so setting this will have no effect. Directive: CASDisableRedirectAfterValidation -Default: On -Description: Whether to redirect to the same URL after ticket validation, but without the - ticket in the parameter. Defaults to on +Default: Off +Description: Normal requests that contain a ticket parameter have that removed by sending + a 302 redirect with ticket removed in the URL. Turning this parameter On + disables the automatic/forced 302 redirects. Turning this on is helpful when + clients can't automatically follow the redirect, or this is a POST request with + a POST body already submitted. Directive: CASRootProxiedAs Default: NULL From 70b6112e0151eb447ee9cd36414b9b077bbb22b9 Mon Sep 17 00:00:00 2001 From: "Curtis W. Ruck" Date: Wed, 29 Nov 2017 07:54:10 -0500 Subject: [PATCH 4/6] add additional return OK --- src/mod_auth_cas.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/mod_auth_cas.c b/src/mod_auth_cas.c index e1fa30a..80a1b67 100644 --- a/src/mod_auth_cas.c +++ b/src/mod_auth_cas.c @@ -2167,6 +2167,8 @@ int cas_authenticate(request_rec *r) } else { return OK; } + } else { + return OK; } } else { /* sometimes, pages that automatically refresh will re-send the ticket parameter, so let's check any cookies presented or return an error if none */ From c2e865e6649b72baab5cf1538c226f9dbc39a5d6 Mon Sep 17 00:00:00 2001 From: "Curtis W. Ruck" Date: Thu, 14 Dec 2017 16:28:35 -0500 Subject: [PATCH 5/6] try fixing mod_auth_cas too many redirects --- src/mod_auth_cas.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mod_auth_cas.c b/src/mod_auth_cas.c index e8469be..40705fb 100644 --- a/src/mod_auth_cas.c +++ b/src/mod_auth_cas.c @@ -2114,7 +2114,7 @@ int cas_authenticate(request_rec *r) } /* now, handle when a ticket is present (this will also catch gateway users since ticket != NULL on their trip back) */ - if(ticket != NULL) { + if(ticket != NULL && r->user == NULL) { if(isValidCASTicket(r, c, ticket, &remoteUser, &attrs)) { /* if we could not find remote user at this point, we have bigger problems */ From 71905b4863da4c213698216c0d5bd8ed0e3b03fb Mon Sep 17 00:00:00 2001 From: "Curtis W. Ruck" Date: Thu, 14 Dec 2017 17:09:28 -0500 Subject: [PATCH 6/6] revert last commit --- src/mod_auth_cas.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mod_auth_cas.c b/src/mod_auth_cas.c index 40705fb..e8469be 100644 --- a/src/mod_auth_cas.c +++ b/src/mod_auth_cas.c @@ -2114,7 +2114,7 @@ int cas_authenticate(request_rec *r) } /* now, handle when a ticket is present (this will also catch gateway users since ticket != NULL on their trip back) */ - if(ticket != NULL && r->user == NULL) { + if(ticket != NULL) { if(isValidCASTicket(r, c, ticket, &remoteUser, &attrs)) { /* if we could not find remote user at this point, we have bigger problems */