Add support for holder-of-key validation of the SSL certificate
The check is added as a fixup phase handler, we lookup the SSL_CLIENT_CERT environment variable. So the directive SSLOptions +StdEnvVars +ExportCertData must be activated.
This commit is contained in:
parent
9b10315fa3
commit
4c98626777
18
README
18
README
|
@ -622,6 +622,24 @@ directive can be used to limit the usable IdP for probe discovery:
|
|||
MellonProbeDiscoveryIdP "https://idp1.example.net/saml/metadata"
|
||||
MellonProbeDiscoveryIdP "https://idp2.example.net/saml/metadata"
|
||||
|
||||
===========================================================================
|
||||
Subject Confirmation Method: holder of key
|
||||
===========================================================================
|
||||
|
||||
The holder of key subject confirmation method allows to check the client
|
||||
certificate of an SSL session against a certificate passed by the identity
|
||||
provider in the authenticating assertion. It removes the use of a PKI or
|
||||
of certificate attributes for access control of SSL authenticated user on
|
||||
service providers.
|
||||
|
||||
To enable the holder of key check you must add the following directive to your
|
||||
SSL virtualhost:
|
||||
|
||||
SSLOptions +StdEnvVars +ExportCertData
|
||||
|
||||
It's needed for mod_mellon to extract the client certificate value form the
|
||||
SSL_CLIENT_CERT environment variable.
|
||||
|
||||
===========================================================================
|
||||
Contributors
|
||||
===========================================================================
|
||||
|
|
|
@ -329,6 +329,7 @@ char *am_get_service_url(request_rec *r,
|
|||
int am_auth_mellon_user(request_rec *r);
|
||||
int am_check_uid(request_rec *r);
|
||||
int am_handler(request_rec *r);
|
||||
int am_fixup(request_rec *r);
|
||||
|
||||
|
||||
int am_httpclient_get(request_rec *r, const char *uri,
|
||||
|
|
|
@ -29,7 +29,9 @@
|
|||
# define SERVER_NEW lasso_server_new
|
||||
#endif /* HAVE_lasso_server_new_from_buffers */
|
||||
|
||||
|
||||
int error_helper(int i) {
|
||||
return i;
|
||||
}
|
||||
|
||||
#ifdef HAVE_lasso_server_new_from_buffers
|
||||
/* This function generates optional metadata for a given element
|
||||
|
@ -430,7 +432,7 @@ static const char *am_get_idp(request_rec *r)
|
|||
* LassoProfile *profile The profile object.
|
||||
*
|
||||
* Returns:
|
||||
* OK on success or HTTP_INTERNAL_SERVER_ERROR on failure.
|
||||
* OK on success or error_helper(HTTP_INTERNAL_SERVER_ERROR) on failure.
|
||||
*/
|
||||
static int am_save_lasso_profile_state(request_rec *r,
|
||||
LassoProfile *profile,
|
||||
|
@ -455,7 +457,7 @@ static int am_save_lasso_profile_state(request_rec *r,
|
|||
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
|
||||
"Could not create a identity dump from the"
|
||||
" LassoIdentity object.");
|
||||
return HTTP_INTERNAL_SERVER_ERROR;
|
||||
return error_helper(HTTP_INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -474,7 +476,7 @@ static int am_save_lasso_profile_state(request_rec *r,
|
|||
if(identity_dump != NULL) {
|
||||
g_free(identity_dump);
|
||||
}
|
||||
return HTTP_INTERNAL_SERVER_ERROR;
|
||||
return error_helper(HTTP_INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -484,7 +486,7 @@ static int am_save_lasso_profile_state(request_rec *r,
|
|||
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
|
||||
"Could not get auth_mellon session while attempting"
|
||||
" to store the lasso profile state.");
|
||||
return HTTP_INTERNAL_SERVER_ERROR;
|
||||
return error_helper(HTTP_INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
|
||||
/* Save the profile state. */
|
||||
|
@ -517,7 +519,7 @@ static int am_save_lasso_profile_state(request_rec *r,
|
|||
* LassoProfile *profile The profile object.
|
||||
*
|
||||
* Returns:
|
||||
* OK on success or HTTP_INTERNAL_SERVER_ERROR on failure.
|
||||
* OK on success or error_helper(HTTP_INTERNAL_SERVER_ERROR) on failure.
|
||||
*/
|
||||
static int am_restore_lasso_profile_state(request_rec *r,
|
||||
LassoProfile *profile)
|
||||
|
@ -533,7 +535,7 @@ static int am_restore_lasso_profile_state(request_rec *r,
|
|||
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
|
||||
"Could not get auth_mellon session while attempting"
|
||||
" to restore the lasso profile state.");
|
||||
return HTTP_INTERNAL_SERVER_ERROR;
|
||||
return error_helper(HTTP_INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
|
||||
identity_dump = am_cache_get_lasso_identity(am_session);
|
||||
|
@ -544,7 +546,7 @@ static int am_restore_lasso_profile_state(request_rec *r,
|
|||
"Could not restore identity from dump."
|
||||
" Lasso error: [%i] %s", rc, lasso_strerror(rc));
|
||||
am_release_request_session(r, am_session);
|
||||
return HTTP_INTERNAL_SERVER_ERROR;
|
||||
return error_helper(HTTP_INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -556,7 +558,7 @@ static int am_restore_lasso_profile_state(request_rec *r,
|
|||
"Could not restore session from dump."
|
||||
" Lasso error: [%i] %s", rc, lasso_strerror(rc));
|
||||
am_release_request_session(r, am_session);
|
||||
return HTTP_INTERNAL_SERVER_ERROR;
|
||||
return error_helper(HTTP_INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -633,7 +635,7 @@ static int am_handle_logout_request(request_rec *r,
|
|||
" Lasso error: [%i] %s", res, lasso_strerror(res));
|
||||
|
||||
lasso_logout_destroy(logout);
|
||||
return HTTP_INTERNAL_SERVER_ERROR;
|
||||
return error_helper(HTTP_INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
|
||||
|
||||
|
@ -778,7 +780,7 @@ static int am_init_logout_request(request_rec *r, LassoLogout *logout)
|
|||
" Lasso error: [%i] %s", res, lasso_strerror(res));
|
||||
|
||||
lasso_logout_destroy(logout);
|
||||
return HTTP_INTERNAL_SERVER_ERROR;
|
||||
return error_helper(HTTP_INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
|
||||
|
||||
|
@ -797,7 +799,7 @@ static int am_init_logout_request(request_rec *r, LassoLogout *logout)
|
|||
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
|
||||
"No assertions found for the current session.");
|
||||
lasso_logout_destroy(logout);
|
||||
return HTTP_INTERNAL_SERVER_ERROR;
|
||||
return error_helper(HTTP_INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
/* We currently only look at the first assertion in the list
|
||||
* lasso_session_get_assertions returns.
|
||||
|
@ -813,7 +815,7 @@ static int am_init_logout_request(request_rec *r, LassoLogout *logout)
|
|||
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
|
||||
"No AuthnStatement found in the current assertion.");
|
||||
lasso_logout_destroy(logout);
|
||||
return HTTP_INTERNAL_SERVER_ERROR;
|
||||
return error_helper(HTTP_INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
|
||||
if(authnStatement->SessionIndex) {
|
||||
|
@ -836,7 +838,7 @@ static int am_init_logout_request(request_rec *r, LassoLogout *logout)
|
|||
" Lasso error: [%i] %s", res, lasso_strerror(res));
|
||||
|
||||
lasso_logout_destroy(logout);
|
||||
return HTTP_INTERNAL_SERVER_ERROR;
|
||||
return error_helper(HTTP_INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
|
||||
/* Set the redirect url. */
|
||||
|
@ -881,14 +883,14 @@ static int am_handle_logout(request_rec *r)
|
|||
|
||||
server = am_get_lasso_server(r);
|
||||
if(server == NULL) {
|
||||
return HTTP_INTERNAL_SERVER_ERROR;
|
||||
return error_helper(HTTP_INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
|
||||
logout = lasso_logout_new(server);
|
||||
if(logout == NULL) {
|
||||
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
|
||||
"Error creating lasso logout object.");
|
||||
return HTTP_INTERNAL_SERVER_ERROR;
|
||||
return error_helper(HTTP_INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
|
||||
/* Restore lasso profile state. We ignore errors since we want to be able
|
||||
|
@ -915,7 +917,7 @@ static int am_handle_logout(request_rec *r)
|
|||
if (rc != OK) {
|
||||
ap_log_rerror(APLOG_MARK, APLOG_ERR, rc, r,
|
||||
"Error reading POST data.");
|
||||
return HTTP_INTERNAL_SERVER_ERROR;
|
||||
return error_helper(HTTP_INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
return am_handle_logout_request(r, logout, post_data);
|
||||
|
||||
|
@ -1122,12 +1124,14 @@ static int am_validate_subject(request_rec *r, LassoSaml2Assertion *assertion,
|
|||
}
|
||||
|
||||
sc = assertion->Subject->SubjectConfirmation;
|
||||
if (sc->Method == NULL ||
|
||||
strcmp(sc->Method, "urn:oasis:names:tc:SAML:2.0:cm:bearer")) {
|
||||
if (sc->Method == NULL) {
|
||||
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
|
||||
"Invalid Method in SubjectConfirmation.");
|
||||
"No Method in SubjectConfirmation.");
|
||||
return HTTP_BAD_REQUEST;
|
||||
}
|
||||
/* is it really necessary to make the variable name configurable ? */
|
||||
apr_table_set(r->subprocess_env, "MELLON_SUBJECT_CONFIRMATION_METHOD",
|
||||
sc->Method);
|
||||
|
||||
scd = sc->SubjectConfirmationData;
|
||||
if (scd == NULL) {
|
||||
|
@ -1768,7 +1772,7 @@ static int am_handle_reply_common(request_rec *r, LassoLogin *login,
|
|||
if(session == NULL) {
|
||||
ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL,
|
||||
"am_new_request_session() failed");
|
||||
return HTTP_INTERNAL_SERVER_ERROR;
|
||||
return error_helper(HTTP_INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
|
||||
rc = add_attributes(session, r, name_id, assertion);
|
||||
|
@ -1798,7 +1802,7 @@ static int am_handle_reply_common(request_rec *r, LassoLogin *login,
|
|||
"Unable to accept SSO message."
|
||||
" Lasso error: [%i] %s", rc, lasso_strerror(rc));
|
||||
lasso_login_destroy(login);
|
||||
return HTTP_INTERNAL_SERVER_ERROR;
|
||||
return error_helper(HTTP_INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
|
||||
|
||||
|
@ -1907,14 +1911,14 @@ static int am_handle_post_reply(request_rec *r)
|
|||
|
||||
server = am_get_lasso_server(r);
|
||||
if(server == NULL) {
|
||||
return HTTP_INTERNAL_SERVER_ERROR;
|
||||
return error_helper(HTTP_INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
|
||||
login = lasso_login_new(server);
|
||||
if (login == NULL) {
|
||||
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
|
||||
"Failed to initialize LassoLogin object.");
|
||||
return HTTP_INTERNAL_SERVER_ERROR;
|
||||
return error_helper(HTTP_INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
|
||||
/* Process login responce. */
|
||||
|
@ -1975,14 +1979,14 @@ static int am_handle_artifact_reply(request_rec *r)
|
|||
|
||||
server = am_get_lasso_server(r);
|
||||
if(server == NULL) {
|
||||
return HTTP_INTERNAL_SERVER_ERROR;
|
||||
return error_helper(HTTP_INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
|
||||
login = lasso_login_new(server);
|
||||
if (login == NULL) {
|
||||
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
|
||||
"Failed to initialize LassoLogin object.");
|
||||
return HTTP_INTERNAL_SERVER_ERROR;
|
||||
return error_helper(HTTP_INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
|
||||
/* Parse artifact url. */
|
||||
|
@ -2004,7 +2008,7 @@ static int am_handle_artifact_reply(request_rec *r)
|
|||
" resolution."
|
||||
" Lasso error: [%i] %s", rc, lasso_strerror(rc));
|
||||
lasso_login_destroy(login);
|
||||
return HTTP_INTERNAL_SERVER_ERROR;
|
||||
return error_helper(HTTP_INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
|
||||
/* Do the SOAP request. */
|
||||
|
@ -2018,7 +2022,7 @@ static int am_handle_artifact_reply(request_rec *r)
|
|||
);
|
||||
if(rc != OK) {
|
||||
lasso_login_destroy(login);
|
||||
return HTTP_INTERNAL_SERVER_ERROR;
|
||||
return error_helper(HTTP_INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
|
||||
rc = lasso_login_process_response_msg(login, response);
|
||||
|
@ -2027,7 +2031,7 @@ static int am_handle_artifact_reply(request_rec *r)
|
|||
"Failed to handle HTTP-Artifact response data."
|
||||
" Lasso error: [%i] %s", rc, lasso_strerror(rc));
|
||||
lasso_login_destroy(login);
|
||||
return HTTP_INTERNAL_SERVER_ERROR;
|
||||
return error_helper(HTTP_INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
|
||||
/* Extract the RelayState parameter. */
|
||||
|
@ -2274,7 +2278,7 @@ static int am_handle_repost(request_rec *r)
|
|||
|
||||
if ((post_form = (*post_mkform)(r, post_data)) == NULL) {
|
||||
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "am_post_mkform() failed");
|
||||
return HTTP_INTERNAL_SERVER_ERROR;
|
||||
return error_helper(HTTP_INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
|
||||
if (charset != NULL) {
|
||||
|
@ -2329,13 +2333,13 @@ static int am_handle_metadata(request_rec *r)
|
|||
|
||||
server = am_get_lasso_server(r);
|
||||
if(server == NULL)
|
||||
return HTTP_INTERNAL_SERVER_ERROR;
|
||||
return error_helper(HTTP_INTERNAL_SERVER_ERROR);
|
||||
|
||||
cfg = cfg->inherit_server_from;
|
||||
|
||||
data = cfg->sp_metadata_file;
|
||||
if (data == NULL)
|
||||
return HTTP_INTERNAL_SERVER_ERROR;
|
||||
return error_helper(HTTP_INTERNAL_SERVER_ERROR);
|
||||
|
||||
r->content_type = "application/samlmetadata+xml";
|
||||
|
||||
|
@ -2431,14 +2435,14 @@ static int am_send_authn_request(request_rec *r, const char *idp,
|
|||
|
||||
server = am_get_lasso_server(r);
|
||||
if(server == NULL) {
|
||||
return HTTP_INTERNAL_SERVER_ERROR;
|
||||
return error_helper(HTTP_INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
|
||||
login = lasso_login_new(server);
|
||||
if(login == NULL) {
|
||||
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
|
||||
"Error creating LassoLogin object from LassoServer.");
|
||||
return HTTP_INTERNAL_SERVER_ERROR;
|
||||
return error_helper(HTTP_INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
|
||||
ret = lasso_login_init_authn_request(login, idp,
|
||||
|
@ -2448,7 +2452,7 @@ static int am_send_authn_request(request_rec *r, const char *idp,
|
|||
"Error creating login request."
|
||||
" Lasso error: [%i] %s", ret, lasso_strerror(ret));
|
||||
lasso_login_destroy(login);
|
||||
return HTTP_INTERNAL_SERVER_ERROR;
|
||||
return error_helper(HTTP_INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
|
||||
request = LASSO_SAMLP2_AUTHN_REQUEST(LASSO_PROFILE(login)->request);
|
||||
|
@ -2457,7 +2461,7 @@ static int am_send_authn_request(request_rec *r, const char *idp,
|
|||
"Error creating login request. Please verify the "
|
||||
"MellonSPMetadataFile directive.");
|
||||
lasso_login_destroy(login);
|
||||
return HTTP_INTERNAL_SERVER_ERROR;
|
||||
return error_helper(HTTP_INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
|
||||
request->ForceAuthn = FALSE;
|
||||
|
@ -2509,8 +2513,8 @@ static int am_send_authn_request(request_rec *r, const char *idp,
|
|||
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
|
||||
"Error building login request."
|
||||
" Lasso error: [%i] %s", ret, lasso_strerror(ret));
|
||||
lasso_login_destroy(login);
|
||||
return HTTP_INTERNAL_SERVER_ERROR;
|
||||
lasso_login_destroy(login);
|
||||
return error_helper(HTTP_INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
|
||||
|
||||
|
@ -2549,7 +2553,7 @@ static int am_auth_new_ticket(request_rec *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;
|
||||
return error_helper(HTTP_INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
|
||||
/* Check if IdP discovery is in use and no IdP was selected yet */
|
||||
|
@ -2713,7 +2717,7 @@ static int am_handle_probe_discovery(request_rec *r) {
|
|||
|
||||
server = am_get_lasso_server(r);
|
||||
if(server == NULL) {
|
||||
return HTTP_INTERNAL_SERVER_ERROR;
|
||||
return error_helper(HTTP_INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -2726,7 +2730,7 @@ static int am_handle_probe_discovery(request_rec *r) {
|
|||
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
|
||||
"probe discovery handler invoked but not "
|
||||
"configured. Plase set MellonProbeDiscoveryTimeout.");
|
||||
return HTTP_INTERNAL_SERVER_ERROR;
|
||||
return error_helper(HTTP_INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -2813,7 +2817,7 @@ static int am_handle_probe_discovery(request_rec *r) {
|
|||
if (disco_idp == NULL) {
|
||||
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
|
||||
"probeDiscovery found no usable IdP.");
|
||||
return HTTP_INTERNAL_SERVER_ERROR;
|
||||
return error_helper(HTTP_INTERNAL_SERVER_ERROR);
|
||||
} else {
|
||||
ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, "probeDiscovery "
|
||||
"failed, trying default IdP %s", disco_idp);
|
||||
|
@ -3027,3 +3031,35 @@ int am_check_uid(request_rec *r)
|
|||
|
||||
return OK;
|
||||
}
|
||||
|
||||
int am_check_holder_of_key(request_rec *r)
|
||||
{
|
||||
const char *sc_key = apr_table_get(r->subprocess_env, "MELLON_SUBJECT_CONFIRMATION_KEY");
|
||||
const char *ssl_client_certificate = apr_table_get(r->subprocess_env, "SSL_CLIENT_CERT");
|
||||
|
||||
if (! sc_key || ! ssl_client_certificate) {
|
||||
return HTTP_FORBIDDEN;
|
||||
}
|
||||
/* XXX We should give this task to Lasso, which should be able to match a simple RSA key pair
|
||||
* against an X509 certificate, which is more robust. */
|
||||
if (strcmp(sc_key, ssl_client_certificate) == 0) {
|
||||
return DECLINED;
|
||||
}
|
||||
return HTTP_FORBIDDEN;
|
||||
}
|
||||
|
||||
/*
|
||||
* This method will enforce subject method confirmation in association with the SSL module, it looks
|
||||
* up the user certificate from the SSL_CLIENT_CERT variable.
|
||||
*/
|
||||
int am_fixup(request_rec *r)
|
||||
{
|
||||
const char *sc_method = apr_table_get(r->subprocess_env,
|
||||
"MELLON_SUBJECT_CONFIRMATION_METHOD");
|
||||
if (sc_method) {
|
||||
if (strcmp(sc_method, LASSO_SAML2_CONFIRMATION_METHOD_HOLDER_OF_KEY) == 0) {
|
||||
return am_check_holder_of_key(r);
|
||||
}
|
||||
}
|
||||
return DECLINED;
|
||||
}
|
||||
|
|
|
@ -190,11 +190,13 @@ static void am_child_init(apr_pool_t *p, server_rec *s)
|
|||
|
||||
static void register_hooks(apr_pool_t *p)
|
||||
{
|
||||
static char const * const hook_fixup_before[] = { "mod_ssl.c", NULL };
|
||||
ap_hook_access_checker(am_auth_mellon_user, NULL, NULL, APR_HOOK_MIDDLE);
|
||||
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_handler, NULL, NULL, APR_HOOK_MIDDLE);
|
||||
ap_hook_fixups(am_fixup, hook_fixup_before, NULL, APR_HOOK_MIDDLE);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue