Add support for loading federation metadata files.

Patch originally created by Emmanuel Dreyfus, some changes by me.

git-svn-id: https://modmellon.googlecode.com/svn/trunk/mod_mellon2@129 a716ebb1-153a-0410-b759-cfb97c6a1b53
This commit is contained in:
olavmrk 2011-05-18 10:49:25 +00:00
parent 433634737d
commit b68c7c641d
5 changed files with 176 additions and 44 deletions

18
README
View File

@ -357,11 +357,20 @@ MellonPostCount 100
# metadata for the IdP you are authenticating against. This
# directive is required. Mutliple IdP metadata can be configured
# by using multiple MellonIdPMetadataFile directives.
#
# If your lasso library is recent enough (higher than 2.3.5),
# then MellonIdPMetadataFile will accept an XML file containing
# descriptors for multiple IdP. An optional validating chain can
# be supplied as a second argument to MellonIdPMetadataFile. If
# ommitted, no metadata validation will take place.
#
# Default: None set.
MellonIdPMetadataFile /etc/apache2/mellon/idp-metadata.xml
# MellonIdPMetadataGlob is a glob(3) pattern enabled alternative
# to MellonIdPMetadataFile.
# to MellonIdPMetadataFile. Like MellonIdPMetadataFile it will
# accept an optional validating chain if lasso is recent enough.
#
# Default: None set.
#MellonIdPMetadataGlob /etc/apache2/mellon/*-metadata.xml
@ -378,6 +387,13 @@ MellonPostCount 100
# Default: None set.
MellonIdPCAFile /etc/apache2/mellon/ca.pem
# MellonIdPIgnore lists IdP entityId that should not loaded
# from XML federation metadata files. This is usefull if an
# IdP cause bugs. Multiple entityId may be specified through
# single MellonIdPIgnore, and multiple MellonIdPIgnore are allowed.
# Default: None set.
#MellonIdPIgnore "https://bug.example.net/saml/idp"
# MellonDiscoveryURL is the URL for IdP discovery service.
# This is used for selecting among multiple configured IdP.
# On initiali user authentication, it is redirected to the

View File

@ -151,6 +151,11 @@ typedef struct {
const char *directive;
} am_cond_t;
typedef struct am_metadata {
const char *file; /* Metadata file with one or many IdP */
const char *chain; /* Validating chain */
} am_metadata_t;
typedef struct am_dir_cfg_rec {
/* enable_mellon is used to enable auth_mellon for a location.
*/
@ -183,9 +188,10 @@ typedef struct am_dir_cfg_rec {
const char *sp_metadata_file;
const char *sp_private_key_file;
const char *sp_cert_file;
apr_array_header_t *idp_metadata_files;
apr_array_header_t *idp_metadata;
const char *idp_public_key_file;
const char *idp_ca_file;
GList *idp_ignore;
/* metadata autogeneration helper */
apr_hash_t *sp_org_name;

View File

@ -163,37 +163,41 @@ static const char *am_set_filestring_slot(cmd_parms *cmd,
/* This function handles configuration directives which use
* a glob pattern
* a glob pattern, with a second optional argument
*
* 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.
* const char *glob_pat glob(3) pattern
* const char *option Optional argument
*
* Returns:
* NULL on success or an error string on failure.
*/
static const char *am_set_glob_fn(cmd_parms *cmd,
void *struct_ptr,
const char *arg)
static const char *am_set_glob_fn12(cmd_parms *cmd,
void *struct_ptr,
const char *glob_pat,
const char *option)
{
const char *(*take_argv)(cmd_parms *, void *, const char *);
const char *(*take_argv)(cmd_parms *, void *, const char *, const char *);
apr_array_header_t *files;
const char *error;
const char *directory;
int i;
take_argv = cmd->info;
directory = am_filepath_dirname(cmd->pool, arg);
if (arg == NULL || *arg == '\0')
return apr_psprintf(cmd->pool, "%s takes one argument", cmd->cmd->name);
directory = am_filepath_dirname(cmd->pool, glob_pat);
if (apr_match_glob(arg, &files, cmd->pool) != 0)
return take_argv(cmd, struct_ptr, arg);
if (glob_pat == NULL || *glob_pat == '\0')
return apr_psprintf(cmd->pool,
"%s takes one or two arguments",
cmd->cmd->name);
if (apr_match_glob(glob_pat, &files, cmd->pool) != 0)
return take_argv(cmd, struct_ptr, glob_pat, option);
for (i = 0; i < files->nelts; i++) {
const char *path;
@ -201,7 +205,7 @@ static const char *am_set_glob_fn(cmd_parms *cmd,
path = apr_pstrcat(cmd->pool, directory, "/",
((const char **)(files->elts))[i], NULL);
error = take_argv(cmd, struct_ptr, path);
error = take_argv(cmd, struct_ptr, path, option);
if (error != NULL)
return error;
@ -218,28 +222,88 @@ static const char *am_set_glob_fn(cmd_parms *cmd,
* 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.
* const char *metadata Path to metadata file for one or multiple IdP
* const char *chain Optional path to validating chain
*
* Returns:
* NULL on success or an error string on failure.
*/
static const char *am_set_idp_string_slot(cmd_parms *cmd,
void *struct_ptr,
const char *arg)
const char *metadata,
const char *chain)
{
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 **filename_slot;
filename_slot = apr_array_push(cfg->idp_metadata_files);
*filename_slot = apr_pstrdup(pconf, arg);
#ifndef HAVE_lasso_server_load_metadata
if (chain != NULL)
return apr_psprintf(cmd->pool, "Cannot specify validating chain "
"for %s since lasso library lacks "
"lasso_server_load_metadata()", cmd->cmd->name);
#endif /* HAVE_lasso_server_load_metadata */
am_metadata_t *idp_metadata = apr_array_push(cfg->idp_metadata);
idp_metadata->file = apr_pstrdup(pconf, metadata);
idp_metadata->chain = apr_pstrdup(pconf, chain);
return NULL;
}
/* This function handles configuration directives which set an
* idp federation blacklist 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.
* int argc Number of blacklisted providerId.
* char *const argv[] List of blacklisted providerId.
*
* Returns:
* NULL on success, or errror string
*/
static const char *am_set_idp_ignore_slot(cmd_parms *cmd,
void *struct_ptr,
int argc,
char *const argv[])
{
#ifdef HAVE_lasso_server_load_metadata
server_rec *s = cmd->server;
apr_pool_t *pconf = s->process->pconf;
am_dir_cfg_rec *cfg = (am_dir_cfg_rec *)struct_ptr;
GList *new_idp_ignore;
int i;
if (argc < 1)
return apr_psprintf(cmd->pool, "%s takes at least one arguments",
cmd->cmd->name);
for (i = 0; i < argc; i++) {
new_idp_ignore = apr_palloc(pconf, sizeof(GList));
new_idp_ignore->data = apr_pstrdup(pconf, argv[i]);
/* Prepend it to the list. */
new_idp_ignore->next = cfg->idp_ignore;
if (cfg->idp_ignore != NULL)
cfg->idp_ignore->prev = new_idp_ignore;
cfg->idp_ignore = new_idp_ignore;
}
return NULL;
#else /* HAVE_lasso_server_load_metadata */
return apr_psprintf(cmd->pool, "Cannot use %s since lasso library lacks "
"lasso_server_load_metadata()", cmd->cmd->name);
#endif /* HAVE_lasso_server_load_metadata */
}
/* This function handles configuration directives which set a string
* slot in the module configuration.
*
@ -861,19 +925,21 @@ const command_rec auth_mellon_commands[] = {
OR_AUTHCFG,
"Full path to pem file with certificate for the SP."
),
AP_INIT_TAKE1(
AP_INIT_TAKE12(
"MellonIdPMetadataFile",
am_set_idp_string_slot,
NULL,
OR_AUTHCFG,
"Full path to xml metadata file for the IdP."
"Full path to xml metadata file for IdP, "
"with optional validating chain."
),
AP_INIT_TAKE1(
AP_INIT_TAKE12(
"MellonIdPMetadataGlob",
am_set_glob_fn,
am_set_glob_fn12,
am_set_idp_string_slot,
OR_AUTHCFG,
"Full path to xml metadata files for the IdP, with glob(3) patterns."
"Full path to xml metadata files for IdP, with glob(3) patterns. "
"An optional validating chain can be supplied."
),
AP_INIT_TAKE1(
"MellonIdPPublicKeyFile",
@ -889,6 +955,13 @@ const command_rec auth_mellon_commands[] = {
OR_AUTHCFG,
"Full path to pem file with CA chain for the IdP."
),
AP_INIT_TAKE_ARGV(
"MellonIdPIgnore",
am_set_idp_ignore_slot,
NULL,
OR_AUTHCFG,
"List of IdP entityId to ignore."
),
AP_INIT_TAKE12(
"MellonOrganizationName",
am_set_langstring_slot,
@ -1017,9 +1090,10 @@ 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_files = apr_array_make(p, 0, sizeof(const char *));
dir->idp_metadata = apr_array_make(p, 0, sizeof(am_metadata_t));
dir->idp_public_key_file = NULL;
dir->idp_ca_file = NULL;
dir->idp_ignore = NULL;
dir->login_path = default_login_path;
dir->discovery_url = NULL;
dir->probe_discovery_timeout = -1; /* -1 means no probe discovery */
@ -1141,9 +1215,9 @@ 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_files = (add_cfg->idp_metadata_files->nelts > 0 ?
add_cfg->idp_metadata_files :
base_cfg->idp_metadata_files);
new_cfg->idp_metadata = (add_cfg->idp_metadata->nelts ?
add_cfg->idp_metadata :
base_cfg->idp_metadata);
new_cfg->idp_public_key_file = (add_cfg->idp_public_key_file ?
add_cfg->idp_public_key_file :
@ -1153,6 +1227,10 @@ void *auth_mellon_dir_merge(apr_pool_t *p, void *base, void *add)
add_cfg->idp_ca_file :
base_cfg->idp_ca_file);
new_cfg->idp_ignore = add_cfg->idp_ignore != NULL ?
add_cfg->idp_ignore :
base_cfg->idp_ignore;
new_cfg->sp_org_name = apr_hash_copy(p,
(apr_hash_count(add_cfg->sp_org_name) > 0) ?
add_cfg->sp_org_name :

View File

@ -214,34 +214,64 @@ static char *am_generate_metadata(apr_pool_t *p, request_rec *r)
static guint 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_size_t index;
if (cfg->idp_metadata_files->nelts == 1)
if (cfg->idp_metadata->nelts == 1)
idp_public_key_file = cfg->idp_public_key_file;
else
idp_public_key_file = NULL;
for (index = 0; index < cfg->idp_metadata_files->nelts; index++) {
int ret;
idp_metadata_file = APR_ARRAY_IDX(cfg->idp_metadata_files, index,
const char *);
for (index = 0; index < cfg->idp_metadata->nelts; index++) {
const am_metadata_t *idp_metadata;
int error;
#ifdef HAVE_lasso_server_load_metadata
GList *loaded_idp = NULL;
#endif /* HAVE_lasso_server_load_metadata */
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 from \"%s\" to lasso server object.",
idp_metadata_file);
idp_metadata = &APR_ARRAY_IDX(cfg->idp_metadata, index, const am_metadata_t);
#ifdef HAVE_lasso_server_load_metadata
error = lasso_server_load_metadata(cfg->server,
LASSO_PROVIDER_ROLE_IDP,
idp_metadata->file,
idp_metadata->chain,
cfg->idp_ignore,
&loaded_idp,
LASSO_SERVER_LOAD_METADATA_FLAG_DEFAULT);
if (error == 0) {
GList *idx;
for (idx = loaded_idp; idx != NULL; idx = idx->next) {
ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
"loaded IdP \"%s\" from \"%s\".",
(char *)idx->data, idp_metadata->file);
}
}
if (loaded_idp != NULL)
g_free(loaded_idp);
#else /* HAVE_lasso_server_load_metadata */
error = lasso_server_add_provider(cfg->server,
LASSO_PROVIDER_ROLE_IDP,
idp_metadata->file,
idp_public_key_file,
cfg->idp_ca_file);
#endif /* HAVE_lasso_server_load_metadata */
if (error != 0) {
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
"Error adding metadata \"%s\" to "
"lasso server objects: %s.",
idp_metadata->file, lasso_strerror(error));
}
}
return g_hash_table_size(cfg->server->providers);
}
static LassoServer *am_get_lasso_server(request_rec *r)
{
am_dir_cfg_rec *cfg = am_get_dir_cfg(r);

View File

@ -46,6 +46,8 @@ PKG_CHECK_MODULES(LASSO, lasso)
saved_LIBS=$LIBS; LIBS="$LIBS $LASSO_LIBS";
AC_CHECK_LIB(lasso, lasso_server_new_from_buffers,
LASSO_CFLAGS="$LASSO_CFLAGS -DHAVE_lasso_server_new_from_buffers")
AC_CHECK_LIB(lasso, lasso_server_load_metadata,
LASSO_CFLAGS="$LASSO_CFLAGS -DHAVE_lasso_server_load_metadata")
LIBS=$saved_LIBS;
AC_SUBST(LASSO_CFLAGS)
AC_SUBST(LASSO_LIBS)