diff options
-rw-r--r-- | README | 18 | ||||
-rw-r--r-- | auth_mellon.h | 8 | ||||
-rw-r--r-- | auth_mellon_config.c | 134 | ||||
-rw-r--r-- | auth_mellon_handler.c | 58 | ||||
-rw-r--r-- | configure.ac | 2 |
5 files changed, 176 insertions, 44 deletions
@@ -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 diff --git a/auth_mellon.h b/auth_mellon.h index f6bdede..69c19f1 100644 --- a/auth_mellon.h +++ b/auth_mellon.h @@ -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; diff --git a/auth_mellon_config.c b/auth_mellon_config.c index aff13f6..23db996 100644 --- a/auth_mellon_config.c +++ b/auth_mellon_config.c @@ -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,25 +222,85 @@ 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; + +#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; - const char **filename_slot; + GList *new_idp_ignore; + int i; + + if (argc < 1) + return apr_psprintf(cmd->pool, "%s takes at least one arguments", + cmd->cmd->name); - filename_slot = apr_array_push(cfg->idp_metadata_files); - *filename_slot = apr_pstrdup(pconf, arg); + 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 */ } @@ -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 : diff --git a/auth_mellon_handler.c b/auth_mellon_handler.c index b4401cf..5694f88 100644 --- a/auth_mellon_handler.c +++ b/auth_mellon_handler.c @@ -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 */ + + 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); + } + } - 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); + 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); diff --git a/configure.ac b/configure.ac index d37b811..394688d 100644 --- a/configure.ac +++ b/configure.ac @@ -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) |