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:
Benjamin Dauvergne 2012-01-02 23:27:24 +01:00
parent 9b10315fa3
commit 4c98626777
4 changed files with 99 additions and 42 deletions

18
README
View File

@ -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
===========================================================================

View File

@ -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,

View File

@ -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;
}

View File

@ -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;
}