/* * * auth_mellon_config.c: an authentication apache module * Copyright © 2003-2007 UNINETT (http://www.uninett.no/) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #include "auth_mellon.h" #ifdef APLOG_USE_MODULE APLOG_USE_MODULE(auth_mellon); #endif /* This is the default endpoint path. Remember to update the description of * the MellonEndpointPath configuration directive if you change this. */ static const char *default_endpoint_path = "/mellon/"; /* This is the default name of the attribute we use as a username. Remember * to update the description of the MellonUser configuration directive if * you change this. */ static const char *default_user_attribute = "NAME_ID"; /* This is the default prefix to use for attributes received from the * server. Customizable using the MellonEnvPrefix option */ static const char *default_env_prefix = "MELLON_"; /* This is the default name of the cookie which mod_auth_mellon will set. * If you change this, then you should also update the description of the * MellonVar configuration directive. */ static const char *default_cookie_name = "cookie"; /* The default setting for cookie is to not enforce secure flag */ static const int default_secure_cookie = 0; /* The default setting for cookie is to not enforce HttpOnly flag */ static const int default_http_only_cookie = 0; /* The default setting for setting MELLON_SESSION */ static const int default_dump_session = 0; /* The default setting for setting MELLON_SAML_RESPONSE */ static const int default_dump_saml_response = 0; /* This is the default IdP initiated login location * the MellonDefaultLoginPath configuration directive if you change this. */ static const char *default_login_path = "/"; /* saved POST session time to live * the MellonPostTTL configuration directive if you change this. */ static const apr_time_t post_ttl = 15 * 60; /* saved POST session maximum size * the MellonPostSize configuration directive if you change this. */ static const apr_size_t post_size = 1024 * 1024; /* maximum saved POST sessions * the MellonPostCount configuration directive if you change this. */ static const int post_count = 100; #ifdef ENABLE_DIAGNOSTICS /* Default filename for mellon diagnostics log file. * Relative pathname is relative to server root. */ static const char *default_diag_filename = "logs/mellon_diagnostics"; /* Default state for diagnostics is off */ static am_diag_flags_t default_diag_flags = AM_DIAG_FLAG_DISABLE; #endif /* whether to merge env. vars or not * the MellonMergeEnvVars configuration directive if you change this. */ static const char *default_merge_env_vars = NULL; /* for env. vars with multiple values, the index start * the MellonEnvVarsIndexStart configuration directive if you change this. */ static const int default_env_vars_index_start = -1; /* whether to also populate env. var _N with number of values * the MellonEnvVarsSetCount configuration directive if you change this. */ static const int default_env_vars_count_in_n = -1; /* The default list of trusted redirect domains. */ static const char * const default_redirect_domains[] = { "[self]", NULL }; /* This function handles configuration directives which set a * multivalued string slot in the module configuration (the destination * strucure is a hash). * * 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 *key The string argument following this configuration * directive in the configuraion file. * const char *value Optional value to be stored in the hash. * * Returns: * NULL on success or an error string on failure. */ static const char *am_set_hash_string_slot(cmd_parms *cmd, void *struct_ptr, const char *key, const char *value) { server_rec *s = cmd->server; apr_pool_t *pconf = s->process->pconf; am_dir_cfg_rec *cfg = (am_dir_cfg_rec *)struct_ptr; int offset; apr_hash_t **hash; /* * If no value is given, we just store the key in the hash. */ if (value == NULL || *value == '\0') value = key; offset = (int)(long)cmd->info; hash = (apr_hash_t **)((char *)cfg + offset); apr_hash_set(*hash, apr_pstrdup(pconf, key), APR_HASH_KEY_STRING, value); return NULL; } /* This function handles configuration directives which set a * multivalued string slot in the module configuration (the destination * strucure is a table). * * 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 *key The string argument following this configuration * directive in the configuraion file. * const char *value Optional value to be stored in the hash. * * Returns: * NULL on success or an error string on failure. */ static const char *am_set_table_string_slot(cmd_parms *cmd, void *struct_ptr, const char *key, const char *value) { server_rec *s = cmd->server; apr_pool_t *pconf = s->process->pconf; am_dir_cfg_rec *cfg = (am_dir_cfg_rec *)struct_ptr; int offset; apr_table_t **table; /* * If no value is given, we just store the key in the hash. */ if (value == NULL || *value == '\0') value = key; offset = (int)(long)cmd->info; table = (apr_table_t **)((char *)cfg + offset); apr_table_set(*table, apr_pstrdup(pconf, key), value); return NULL; } /* This function handles configuration directives which set a file slot * in the module configuration. The file contents are immediately read. * * 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. * This value isn't used by this function. * const char *arg The string argument following this configuration * directive in the configuraion file. * * Returns: * NULL on success or an error string on failure. */ static const char *am_set_file_contents_slot(cmd_parms *cmd, void *struct_ptr, const char *arg) { const char *path; apr_status_t rv; am_dir_cfg_rec *cfg = (am_dir_cfg_rec *)struct_ptr; int offset; am_file_data_t **p_file_data, *file_data; path = ap_server_root_relative(cmd->pool, arg); if (!path) { return apr_pstrcat(cmd->pool, cmd->cmd->name, ": Invalid file path ", arg, NULL); } offset = (int)(long)cmd->info; p_file_data = (am_file_data_t **)((char *)cfg + offset); *p_file_data = am_file_data_new(cmd->pool, path); file_data = *p_file_data; rv = am_file_read(file_data); if (rv != APR_SUCCESS) { return file_data->strerror; } return NULL; } /* This function handles configuration directives which set a file * pathname in the module configuration. The file is checked for * existence. * * 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. * This value isn't used by this function. * const char *arg The string argument following this configuration * directive in the configuraion file. * * Returns: * NULL on success or an error string on failure. */ static const char *am_set_file_pathname_slot(cmd_parms *cmd, void *struct_ptr, const char *arg) { const char *path; apr_status_t rv; am_dir_cfg_rec *cfg = (am_dir_cfg_rec *)struct_ptr; int offset; am_file_data_t **p_file_data, *file_data; path = ap_server_root_relative(cmd->pool, arg); if (!path) { return apr_pstrcat(cmd->pool, cmd->cmd->name, ": Invalid file_data path ", arg, NULL); } offset = (int)(long)cmd->info; p_file_data = (am_file_data_t **)((char *)cfg + offset); *p_file_data = am_file_data_new(cmd->pool, path); file_data = *p_file_data; rv = am_file_stat(file_data); if (rv != APR_SUCCESS) { return file_data->strerror; } if (file_data->finfo.filetype != APR_REG) { return apr_psprintf(cmd->pool, "file \"%s\" is not a regular file", file_data->path); } return NULL; } /* This function handles configuration directives which use * 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 *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_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 *); apr_array_header_t *files; const char *error; const char *directory; int i; take_argv = cmd->info; directory = am_filepath_dirname(cmd->pool, glob_pat); 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; path = apr_pstrcat(cmd->pool, directory, "/", ((const char **)(files->elts))[i], NULL); error = take_argv(cmd, struct_ptr, path, option); if (error != NULL) return error; } return NULL; } /* This function handles configuration directives which set an * idp related 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. * 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 *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; am_file_data_t *idp_file_data = NULL; am_file_data_t *chain_file_data = NULL; #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 */ idp_file_data = am_file_data_new(pconf, metadata); if (am_file_stat(idp_file_data) != APR_SUCCESS) { return idp_file_data->strerror; } if (chain) { chain_file_data = am_file_data_new(pconf, chain); if (am_file_stat(chain_file_data) != APR_SUCCESS) { return chain_file_data->strerror; } } else { chain_file_data = NULL; } am_metadata_t *idp_metadata = apr_array_push(cfg->idp_metadata); idp_metadata->metadata = idp_file_data; idp_metadata->chain = chain_file_data; 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 file path * 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. * This value isn't used by this function. * const char *arg The string argument following this configuration * directive in the configuraion file. * * Returns: * NULL on success or an error string on failure. */ static const char *am_set_module_config_file_slot(cmd_parms *cmd, void *struct_ptr, const char *arg) { return ap_set_file_slot(cmd, am_get_mod_cfg(cmd->server), arg); } /* This function handles configuration directives which set an int * 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. * This value isn't used by this function. * const char *arg The string argument following this configuration * directive in the configuraion file. * * Returns: * NULL on success or an error string on failure. */ static const char *am_set_module_config_int_slot(cmd_parms *cmd, void *struct_ptr, const char *arg) { return ap_set_int_slot(cmd, am_get_mod_cfg(cmd->server), arg); } /* This function handles the MellonDiagnosticsFile configuration directive. * It emits as warning in the log file if Mellon is not built with * diagnostics enabled. * * 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. * * Returns: * NULL on success or an error string on failure. */ static const char *am_set_module_diag_file_slot(cmd_parms *cmd, void *struct_ptr, const char *arg) { #ifdef ENABLE_DIAGNOSTICS return ap_set_file_slot(cmd, am_get_diag_cfg(cmd->server), arg); #else ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, cmd->server, "%s has no effect because Mellon was not compiled with" " diagnostics enabled, use ./configure --enable-diagnostics" " at build time to turn this feature on.", cmd->directive->directive); return NULL; #endif } /* This function handles configuration directives which sets the * diagnostics flags 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. * const char *arg The string argument following this configuration * directive in the configuraion file. * * Returns: * NULL on success or an error string on failure. */ static const char *am_set_module_diag_flags_slot(cmd_parms *cmd, void *struct_ptr, const char *arg) { #ifdef ENABLE_DIAGNOSTICS am_diag_cfg_rec *diag_cfg = am_get_diag_cfg(cmd->server); if (strcasecmp(arg, "on") == 0) { diag_cfg->flags = AM_DIAG_FLAG_ENABLE_ALL; } else if (strcasecmp(arg, "off") == 0) { diag_cfg->flags = AM_DIAG_FLAG_DISABLE; } else { return apr_psprintf(cmd->pool, "%s: must be one of: 'on', 'off'", cmd->cmd->name); } return NULL; #else ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, cmd->server, "%s has no effect because Mellon was not compiled with" " diagnostics enabled, use ./configure --enable-diagnostics" " at build time to turn this feature on.", cmd->directive->directive); return NULL; #endif } /* This function handles the MellonCookieSameSite configuration directive. * This directive can be set to "lax" or "strict" * * Parameters: * cmd_parms *cmd The command structure for this configuration * directive. * void *struct_ptr Pointer to the current directory configuration. * const char *arg The string argument following this configuration * directive in the configuraion file. * * Returns: * NULL on success or an error string if the argument is wrong. */ static const char *am_set_samesite_slot(cmd_parms *cmd, void *struct_ptr, const char *arg) { am_dir_cfg_rec *d = (am_dir_cfg_rec *)struct_ptr; if(!strcasecmp(arg, "lax")) { d->cookie_samesite = am_samesite_lax; } else if(!strcasecmp(arg, "strict")) { d->cookie_samesite = am_samesite_strict; } else { return "The MellonCookieSameSite parameter must be 'lax' or 'strict'"; } return NULL; } /* This function handles the MellonEnable configuration directive. * This directive can be set to "off", "info" or "auth". * * Parameters: * cmd_parms *cmd The command structure for this configuration * directive. * void *struct_ptr Pointer to the current directory configuration. * const char *arg The string argument following this configuration * directive in the configuraion file. * * Returns: * NULL on success or an error string if the argument is wrong. */ static const char *am_set_enable_slot(cmd_parms *cmd, void *struct_ptr, const char *arg) { am_dir_cfg_rec *d = (am_dir_cfg_rec *)struct_ptr; if(!strcasecmp(arg, "auth")) { d->enable_mellon = am_enable_auth; } else if(!strcasecmp(arg, "info")) { d->enable_mellon = am_enable_info; } else if(!strcasecmp(arg, "off")) { d->enable_mellon = am_enable_off; } else { return "parameter must be 'off', 'info' or 'auth'"; } return NULL; } /* This function handles the MellonSecureCookie configuration directive. * This directive can be set to "on", "off", "secure" or "httponly". * * Parameters: * cmd_parms *cmd The command structure for this configuration * directive. * void *struct_ptr Pointer to the current directory configuration. * const char *arg The string argument following this configuration * directive in the configuraion file. * * Returns: * NULL on success or an error string if the argument is wrong. */ static const char *am_set_secure_slots(cmd_parms *cmd, void *struct_ptr, const char *arg) { am_dir_cfg_rec *d = (am_dir_cfg_rec *)struct_ptr; if(!strcasecmp(arg, "on")) { d->secure = 1; d->http_only = 1; } else if(!strcasecmp(arg, "secure")) { d->secure = 1; } else if(!strcasecmp(arg, "httponly")) { d->http_only = 1; } else if(strcasecmp(arg, "off")) { return "parameter must be 'on', 'off', 'secure' or 'httponly'"; } return NULL; } /* This function handles the obsolete MellonDecoder configuration directive. * It is a no-op. * * Parameters: * cmd_parms *cmd The command structure for this configuration * directive. * void *struct_ptr Pointer to the current directory configuration. * const char *arg The string argument following this configuration * directive in the configuraion file. * * Returns: * NULL */ static const char *am_set_decoder_slot(cmd_parms *cmd, void *struct_ptr, const char *arg) { return NULL; } /* This function handles the MellonEndpointPath configuration directive. * If the path doesn't end with a '/', then we will append one. * * Parameters: * cmd_parms *cmd The command structure for the MellonEndpointPath * 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 containing the path of the * endpoint directory. * * Returns: * This function will always return NULL. */ static const char *am_set_endpoint_path(cmd_parms *cmd, void *struct_ptr, const char *arg) { am_dir_cfg_rec *d = (am_dir_cfg_rec *)struct_ptr; /* Make sure that the path ends with '/'. */ if(strlen(arg) == 0 || arg[strlen(arg) - 1] != '/') { d->endpoint_path = apr_pstrcat(cmd->pool, arg, "/", NULL); } else { d->endpoint_path = arg; } return NULL; } /* This function handles the MellonSetEnv configuration directive. * This directive allows the user to change the name of attributes. * * Parameters: * cmd_parms *cmd The command structure for the MellonSetEnv * configuration directive. * void *struct_ptr Pointer to the current directory configuration. * const char *newName The new name of the attribute. * const char *oldName The old name of the attribute. * * Returns: * This function will always return NULL. */ static const char *am_set_setenv_slot(cmd_parms *cmd, void *struct_ptr, const char *newName, const char *oldName) { am_dir_cfg_rec *d = (am_dir_cfg_rec *)struct_ptr; /* Configure as prefixed attribute name */ am_envattr_conf_t *envattr_conf = (am_envattr_conf_t *)apr_palloc(cmd->pool, sizeof(am_envattr_conf_t)); envattr_conf->name = newName; envattr_conf->prefixed = 1; apr_hash_set(d->envattr, oldName, APR_HASH_KEY_STRING, envattr_conf); return NULL; } /* This function handles the MellonSetEnvNoPrefix configuration directive. * This directive allows the user to change the name of attributes without prefixing them with MELLON_. * * Parameters: * cmd_parms *cmd The command structure for the MellonSetEnv * configuration directive. * void *struct_ptr Pointer to the current directory configuration. * const char *newName The new name of the attribute. * const char *oldName The old name of the attribute. * * Returns: * This function will always return NULL. */ static const char *am_set_setenv_no_prefix_slot(cmd_parms *cmd, void *struct_ptr, const char *newName, const char *oldName) { am_dir_cfg_rec *d = (am_dir_cfg_rec *)struct_ptr; /* Configure as not prefixed attribute name */ am_envattr_conf_t *envattr_conf = (am_envattr_conf_t *)apr_palloc(cmd->pool, sizeof(am_envattr_conf_t)); envattr_conf->name = newName; envattr_conf->prefixed = 0; apr_hash_set(d->envattr, oldName, APR_HASH_KEY_STRING, envattr_conf); return NULL; } /* This function handles the MellonAuthnContextComparisonType option. * It could be set to "exact", "minimum", "maximum" or "better" * * Parameters: * cmd_parms *cmd The command structure for this configuration * directive. * void *struct_ptr Pointer to the current directory configuration. * const char *arg The string argument following this configuration * directive in the configuraion file. * * Returns: * NULL on success or an error string if the argument is wrong. */ static const char *am_set_authn_context_comparison_type_slot(cmd_parms *cmd, void *struct_ptr, const char *arg) { am_dir_cfg_rec *d = (am_dir_cfg_rec *)struct_ptr; if (!strcasecmp(arg, LASSO_LIB_AUTHN_CONTEXT_COMPARISON_EXACT)) { d->authn_context_comparison_type = LASSO_LIB_AUTHN_CONTEXT_COMPARISON_EXACT; } else if (!strcasecmp(arg, LASSO_LIB_AUTHN_CONTEXT_COMPARISON_MINIMUM)) { d->authn_context_comparison_type = LASSO_LIB_AUTHN_CONTEXT_COMPARISON_MINIMUM; } else if (!strcasecmp(arg, LASSO_LIB_AUTHN_CONTEXT_COMPARISON_MAXIMUM)) { d->authn_context_comparison_type = LASSO_LIB_AUTHN_CONTEXT_COMPARISON_MAXIMUM; } else if (!strcasecmp(arg, LASSO_LIB_AUTHN_CONTEXT_COMPARISON_BETTER)) { d->authn_context_comparison_type = LASSO_LIB_AUTHN_CONTEXT_COMPARISON_BETTER; } else { return "parameter must be 'exact', 'minimum', 'maximum' or 'better'"; } return NULL; } /* This function decodes MellonCond flags, such as [NOT,REG] * * Parameters: * const char *arg Pointer to the flags string * * Returns: * flags, or -1 on error */ static int am_cond_flags(const char *arg) { int flags = AM_COND_FLAG_NULL; static const char * const options[] = { "OR", /* AM_EXPIRE_FLAG_OR */ "NOT", /* AM_EXPIRE_FLAG_NOT */ "REG", /* AM_EXPIRE_FLAG_REG */ "NC", /* AM_EXPIRE_FLAG_NC */ "MAP", /* AM_EXPIRE_FLAG_MAP */ "REF", /* AM_EXPIRE_FLAG_REF */ "SUB", /* AM_EXPIRE_FLAG_SUB */ /* The other options (IGN, REQ, FSTR, ...) are only internally used */ }; apr_size_t options_count = sizeof(options) / sizeof(*options); /* Skip inital [ */ if (arg[0] == '[') arg++; else return -1; do { apr_size_t i; for (i = 0; i < options_count; i++) { apr_size_t optlen = strlen(options[i]); if (strncmp(arg, options[i], optlen) == 0) { /* Make sure we have a separator next */ if (arg[optlen] && !strchr("]\t ,", (int)arg[optlen])) return -1; flags |= (1 << i); arg += optlen; break; } /* no match */ if (i == options_count) return -1; /* skip spaces, tabs and commas */ arg += strspn(arg, " \t,"); /* * End of option, but we fire an error if * there is trailing garbage */ if (*arg == ']') { arg++; return (*arg == '\0') ? flags : -1; } } } while (*arg); /* Missing trailing ] */ return -1; } /* This function handles the MellonCond configuration directive, which * allows the user to restrict access based on attributes received from * the IdP. * * Parameters: * cmd_parms *cmd The command structure for the MellonCond * configuration directive. * void *struct_ptr Pointer to the current directory configuration. * const char *attribute Pointer to the attribute name * const char *value Pointer to the attribute value or regex * const char *options Pointer to options * * Returns: * NULL on success or an error string on failure. */ static const char *am_set_cond_slot(cmd_parms *cmd, void *struct_ptr, const char *attribute, const char *value, const char *options) { am_dir_cfg_rec *d = struct_ptr; int flags = AM_COND_FLAG_NULL; am_cond_t *element; if (attribute == NULL || *attribute == '\0' || value == NULL || *value == '\0') return apr_pstrcat(cmd->pool, cmd->cmd->name, " takes at least two arguments", NULL); if (options != NULL && *options != '\0') flags = am_cond_flags(options); if (flags == -1) return apr_psprintf(cmd->pool, "%s - invalid flags %s", cmd->cmd->name, options); element = (am_cond_t *)apr_array_push(d->cond); element->varname = attribute; element->flags = flags; element->str = NULL; element->regex = NULL; element->directive = apr_pstrcat(cmd->pool, cmd->directive->directive, " ", cmd->directive->args, NULL); if (element->flags & AM_COND_FLAG_REG) { int regex_flags = AP_REG_EXTENDED|AP_REG_NOSUB; if (element->flags & AM_COND_FLAG_NC) regex_flags |= AP_REG_ICASE; element->regex = ap_pregcomp(cmd->pool, value, regex_flags); if (element->regex == NULL) return apr_psprintf(cmd->pool, "%s - invalid regex %s", cmd->cmd->name, value); } /* * Flag values containing format strings to that we do * not have to process the others at runtime. */ if (strchr(value, '%') != NULL) element->flags |= AM_COND_FLAG_FSTR; /* * We keep the string also for regex, so that we can * print it for debug purpose and perform substitutions on it. */ element->str = value; return NULL; } /* This function handles the MellonRequire configuration directive, which * allows the user to restrict access based on attributes received from * the IdP. * * Parameters: * cmd_parms *cmd The command structure for the MellonRequire * configuration directive. * void *struct_ptr Pointer to the current directory configuration. * const char *arg Pointer to the configuration string. * * Returns: * NULL on success or an error string on failure. */ static const char *am_set_require_slot(cmd_parms *cmd, void *struct_ptr, const char *arg) { am_dir_cfg_rec *d = struct_ptr; char *attribute, *value; int i; am_cond_t *element; am_cond_t *first_element; attribute = ap_getword_conf(cmd->pool, &arg); value = ap_getword_conf(cmd->pool, &arg); if (*attribute == '\0' || *value == '\0') { return apr_pstrcat(cmd->pool, cmd->cmd->name, " takes at least two arguments", NULL); } /* * MellonRequire overwrites previous conditions on this attribute * We just tag the am_cond_t with the ignore flag, as it is * easier (and probably faster) than to really remove it. */ for (i = 0; i < d->cond->nelts; i++) { am_cond_t *ce = &((am_cond_t *)(d->cond->elts))[i]; if ((strcmp(ce->varname, attribute) == 0) && (ce->flags & AM_COND_FLAG_REQ)) ce->flags |= AM_COND_FLAG_IGN; } first_element = NULL; do { element = (am_cond_t *)apr_array_push(d->cond); element->varname = attribute; element->flags = AM_COND_FLAG_OR|AM_COND_FLAG_REQ; element->str = value; element->regex = NULL; /* * When multiple values are given, we track the first one * in order to retreive the directive */ if (first_element == NULL) { element->directive = apr_pstrcat(cmd->pool, cmd->directive->directive, " ", cmd->directive->args, NULL); first_element = element; } else { element->directive = first_element->directive; } } while (*(value = ap_getword_conf(cmd->pool, &arg)) != '\0'); /* * Remove OR flag on last element */ element->flags &= ~AM_COND_FLAG_OR; return NULL; } /* This function handles the MellonOrganization* directives, which * which specify language-qualified strings * * Parameters: * cmd_parms *cmd The command structure for the MellonOrganization* * configuration directive. * void *struct_ptr Pointer to the current directory configuration. * const char *lang Pointer to the language string (optional) * const char *value Pointer to the data * * Returns: * NULL on success or an error string on failure. */ static const char *am_set_langstring_slot(cmd_parms *cmd, void *struct_ptr, const char *lang, const char *value) { apr_hash_t *h = *(apr_hash_t **)(struct_ptr + (apr_size_t)cmd->info); if (value == NULL || *value == '\0') { value = lang; lang = ""; } apr_hash_set(h, lang, APR_HASH_KEY_STRING, apr_pstrdup(cmd->server->process->pconf, value)); return NULL; } /* This function handles the MellonAuthnContextClassRef directive. * * Parameters: * cmd_parms *cmd The command structure for the MellonAuthnContextClassRef * configuration directive. * void *struct_ptr Pointer to the current directory configuration. * NULL if we are not in a directory configuration. * const char *arg An URI for an SAMLv2 AuthnContextClassRef * * Returns: * This function will always return NULL. */ static const char *am_set_authn_context_class_ref(cmd_parms *cmd, void *struct_ptr, const char *arg) { am_dir_cfg_rec *d = (am_dir_cfg_rec *)struct_ptr; apr_pool_t *p= cmd->pool; char **context_class_ref_p; if(strlen(arg) == 0) { return NULL; } context_class_ref_p = apr_array_push(d->authn_context_class_ref); *context_class_ref_p = apr_pstrdup(p, arg); return NULL; } /* This function handles the MellonDoNotVerifyLogoutSignature configuration directive, * it is identical to the am_set_hash_string_slot function. You can refer to it. * * 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 *key The string argument following this configuration * directive in the configuraion file. * * Returns: * NULL on success or an error string on failure. */ static const char *am_set_do_not_verify_logout_signature(cmd_parms *cmd, void *struct_ptr, const char *key) { #ifdef HAVE_lasso_profile_set_signature_verify_hint return am_set_hash_string_slot(cmd, struct_ptr, key, NULL); #else return apr_pstrcat(cmd->pool, cmd->cmd->name, " is not usable as modmellon was compiled against " "a version of the lasso library which miss the " "function lasso_profile_set_signature_verify_hint.", NULL); #endif } /* This function handles the MellonMergeEnvVars configuration directive, * it sets merge_env_vars to nonempty separator (default semicolon), * or empty string to denote no merging. * * 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 *flag On/Off flag * const char *sep Optional separator, should be only present with On * * Returns: * NULL on success or an error string on failure. */ static const char *am_set_merge_env_vars(cmd_parms *cmd, void *struct_ptr, const char *flag, const char *sep) { am_dir_cfg_rec *d = (am_dir_cfg_rec *)struct_ptr; apr_pool_t *p= cmd->pool; if (strcasecmp(flag, "on") == 0) { if (sep && *sep) { /* * TAKE12 will not give us the second argument if it is * empty string so we cannot complain about it, we will just * silently use semicolon */ d->merge_env_vars = apr_pstrdup(p, sep); } else { d->merge_env_vars = ";"; } } else if (strcasecmp(flag, "off") == 0) { if (sep) { return apr_pstrcat(cmd->pool, cmd->cmd->name, " separator should not be used with Off", NULL); } d->merge_env_vars = ""; } else { return apr_pstrcat(cmd->pool, cmd->cmd->name, " first parameer must be On or Off", NULL); } return NULL; } /* Handle MellonRedirectDomains option. * * 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 redirect domains. * char *const argv[] List of redirect domains. * * Returns: * NULL on success, or errror string on failure. */ static const char *am_set_redirect_domains(cmd_parms *cmd, void *struct_ptr, int argc, char *const argv[]) { am_dir_cfg_rec *cfg = (am_dir_cfg_rec *)struct_ptr; const char **redirect_domains; int i; if (argc < 1) return apr_psprintf(cmd->pool, "%s takes at least one arguments", cmd->cmd->name); redirect_domains = apr_palloc(cmd->pool, sizeof(const char *) * (argc + 1)); for (i = 0; i < argc; i++) { redirect_domains[i] = argv[i]; } redirect_domains[argc] = NULL; cfg->redirect_domains = redirect_domains; return NULL; } /* This function handles the MellonSignatureMethod configuration directive. * This directive can be set to one of: * * Parameters: * cmd_parms *cmd The command structure for this configuration * directive. * void *struct_ptr Pointer to the current directory configuration. * const char *arg The string argument following this configuration * directive in the configuraion file. * * Returns: * NULL on success or an error string if the argument is wrong. */ static const char *am_set_signature_method_slot(cmd_parms *cmd, void *struct_ptr, const char *arg) { am_dir_cfg_rec *d = (am_dir_cfg_rec *)struct_ptr; char *valid_methods = "rsa-sha1" #if HAVE_DECL_LASSO_SIGNATURE_METHOD_RSA_SHA256 " rsa-sha256" #endif #if HAVE_DECL_LASSO_SIGNATURE_METHOD_RSA_SHA384 " rsa-sha384" #endif #if HAVE_DECL_LASSO_SIGNATURE_METHOD_RSA_SHA512 " rsa-sha512" #endif ; if (!strcasecmp(arg, "rsa-sha1")) { d->signature_method = LASSO_SIGNATURE_METHOD_RSA_SHA1; } #if HAVE_DECL_LASSO_SIGNATURE_METHOD_RSA_SHA256 else if (!strcasecmp(arg, "rsa-sha256")) { d->signature_method = LASSO_SIGNATURE_METHOD_RSA_SHA256; } #endif #if HAVE_DECL_LASSO_SIGNATURE_METHOD_RSA_SHA384 else if (!strcasecmp(arg, "rsa-sha384")) { d->signature_method = LASSO_SIGNATURE_METHOD_RSA_SHA384; } #endif #if HAVE_DECL_LASSO_SIGNATURE_METHOD_RSA_SHA512 else if (!strcasecmp(arg, "rsa-sha512")) { d->signature_method = LASSO_SIGNATURE_METHOD_RSA_SHA512; } #endif else { return apr_psprintf(cmd->pool, "%s: Invalid method \"%s\", must be one of: %s", cmd->cmd->name, arg, valid_methods); } return NULL; } /* This array contains all the configuration directive which are handled * by auth_mellon. */ const command_rec auth_mellon_commands[] = { /* Global configuration directives. */ AP_INIT_TAKE1( "MellonCacheSize", am_set_module_config_int_slot, (void *)APR_OFFSETOF(am_mod_cfg_rec, cache_size), RSRC_CONF, "The number of sessions we can keep track of at once. You must" " restart the server before any changes to this directive will" " take effect. The default value is 100." ), AP_INIT_TAKE1( "MellonCacheEntrySize", am_set_module_config_int_slot, (void *)APR_OFFSETOF(am_mod_cfg_rec, entry_size), RSRC_CONF, "The maximum size for a single session entry. You must" " restart the server before any changes to this directive will" " take effect. The default value is 192KiB." ), AP_INIT_TAKE1( "MellonLockFile", am_set_module_config_file_slot, (void *)APR_OFFSETOF(am_mod_cfg_rec, lock_file), RSRC_CONF, "The lock file for session synchronization." " Default value is \"/var/run/mod_auth_mellon.lock\"." ), AP_INIT_TAKE1( "MellonPostDirectory", am_set_module_config_file_slot, (void *)APR_OFFSETOF(am_mod_cfg_rec, post_dir), RSRC_CONF, "The directory for saving POST requests." " Not set by default." ), AP_INIT_TAKE1( "MellonPostTTL", am_set_module_config_int_slot, (void *)APR_OFFSETOF(am_mod_cfg_rec, post_ttl), RSRC_CONF, "The time to live for saved POST requests in seconds." " Default value is 900 (15 minutes)." ), AP_INIT_TAKE1( "MellonPostCount", am_set_module_config_int_slot, (void *)APR_OFFSETOF(am_mod_cfg_rec, post_count), RSRC_CONF, "The maximum saved POST sessions at once." " Default value is 100." ), AP_INIT_TAKE1( "MellonPostSize", am_set_module_config_int_slot, (void *)APR_OFFSETOF(am_mod_cfg_rec, post_size), RSRC_CONF, "The maximum size of a saved POST, in bytes." " Default value is 1048576 (1 MB)." ), AP_INIT_TAKE1( "MellonDiagnosticsFile", am_set_module_diag_file_slot, #ifdef ENABLE_DIAGNOSTICS (void *)APR_OFFSETOF(am_diag_cfg_rec, filename), #else NULL, #endif RSRC_CONF, "Diagnostics log file. [file|pipe] " "If file then file is a filename, relative to the ServerRoot." "If pipe then the filename is a pipe character \"|\", " "followed by the path to a program to receive the log information " "on its standard input. " " Default value is \"logs/mellon_diagnostics\"." ), AP_INIT_ITERATE( "MellonDiagnosticsEnable", am_set_module_diag_flags_slot, NULL, RSRC_CONF, "Diagnostics flags. [on|off] " " Default value is \"off\"." ), /* Per-location configuration directives. */ AP_INIT_TAKE1( "MellonEnable", am_set_enable_slot, NULL, OR_AUTHCFG, "Enable auth_mellon on a location. This can be set to 'off', 'info'" " and 'auth'. 'off' disables auth_mellon for a location, 'info'" " will only populate the environment with attributes if the user" " has logged in already. 'auth' will redirect the user to the IdP" " if he hasn't logged in yet, but otherwise behaves like 'info'." ), AP_INIT_TAKE1( "MellonDecoder", am_set_decoder_slot, NULL, OR_AUTHCFG, "Obsolete option, now a no-op for backwards compatibility." ), AP_INIT_TAKE1( "MellonVariable", ap_set_string_slot, (void *)APR_OFFSETOF(am_dir_cfg_rec, varname), OR_AUTHCFG, "The name of the cookie which auth_mellon will set. Defaults to" " 'cookie'. This string is appended to 'mellon-' to create the" " cookie name, and the default name of the cookie will therefore" " be 'mellon-cookie'." ), AP_INIT_TAKE1( "MellonSecureCookie", am_set_secure_slots, NULL, OR_AUTHCFG, "Whether the cookie set by auth_mellon should have HttpOnly and" " secure flags set. Default is 'off'. Once 'on' - both flags will" " be set. Values 'httponly' or 'secure' will respectively set only" " one flag." ), AP_INIT_TAKE1( "MellonCookieDomain", ap_set_string_slot, (void *)APR_OFFSETOF(am_dir_cfg_rec, cookie_domain), OR_AUTHCFG, "The domain of the cookie which auth_mellon will set. Defaults to" " the domain of the current request." ), AP_INIT_TAKE1( "MellonCookiePath", ap_set_string_slot, (void *)APR_OFFSETOF(am_dir_cfg_rec, cookie_path), OR_AUTHCFG, "The path of the cookie which auth_mellon will set. Defaults to" " '/'." ), AP_INIT_TAKE1( "MellonCookieSameSite", am_set_samesite_slot, NULL, OR_AUTHCFG, "The SameSite value for the auth_mellon cookie. Defaults to" " having no SameSite value. Accepts values of Lax or Strict." ), AP_INIT_TAKE1( "MellonUser", ap_set_string_slot, (void *)APR_OFFSETOF(am_dir_cfg_rec, userattr), OR_AUTHCFG, "Attribute to set as r->user. Defaults to NAME_ID, which is the" " attribute we set to the identifier we receive from the IdP." ), AP_INIT_TAKE1( "MellonIdP", ap_set_string_slot, (void *)APR_OFFSETOF(am_dir_cfg_rec, idpattr), OR_AUTHCFG, "Attribute we set to the IdP ProviderId." ), AP_INIT_TAKE2( "MellonSetEnv", am_set_setenv_slot, NULL, OR_AUTHCFG, "Renames attributes received from the server while retaining the" " prefix. The prefix defaults to MELLON_ but can be changed with" " MellonEnvPrefix." " The format is MellonSetEnv ." ), AP_INIT_TAKE2( "MellonSetEnvNoPrefix", am_set_setenv_no_prefix_slot, NULL, OR_AUTHCFG, "Renames attributes received from the server without adding prefix. The format is" " MellonSetEnvNoPrefix ." ), AP_INIT_TAKE1( "MellonEnvPrefix", ap_set_string_slot, (void *)APR_OFFSETOF(am_dir_cfg_rec, env_prefix), OR_AUTHCFG, "The prefix to use for attributes received from the server." ), AP_INIT_FLAG( "MellonSessionDump", ap_set_flag_slot, (void *)APR_OFFSETOF(am_dir_cfg_rec, dump_session), OR_AUTHCFG, "Dump session in environment. Default is off" ), AP_INIT_FLAG( "MellonSamlResponseDump", ap_set_flag_slot, (void *)APR_OFFSETOF(am_dir_cfg_rec, dump_saml_response), OR_AUTHCFG, "Dump SAML authentication response in environment. Default is off" ), AP_INIT_RAW_ARGS( "MellonRequire", am_set_require_slot, NULL, OR_AUTHCFG, "Attribute requirements for authorization. Allows you to restrict" " access based on attributes received from the IdP. If you list" " several MellonRequire configuration directives, then all of them" " must match. Every MellonRequire can list several allowed values" " for the attribute. The syntax is:" " MellonRequire [value2....]." ), AP_INIT_TAKE23( "MellonCond", am_set_cond_slot, NULL, OR_AUTHCFG, "Attribute requirements for authorization. Allows you to restrict" " access based on attributes received from the IdP. The syntax is:" " MellonRequire []." ), AP_INIT_TAKE1( "MellonSessionLength", ap_set_int_slot, (void *)APR_OFFSETOF(am_dir_cfg_rec, session_length), OR_AUTHCFG, "Maximum number of seconds a session will be valid for. Defaults" " to 86400 seconds (1 day)." ), AP_INIT_TAKE1( "MellonNoCookieErrorPage", ap_set_string_slot, (void *)APR_OFFSETOF(am_dir_cfg_rec, no_cookie_error_page), OR_AUTHCFG, "Web page to display if the user has disabled cookies. We will" " return a 400 Bad Request error if this is unset and the user" " ha disabled cookies." ), AP_INIT_TAKE1( "MellonNoSuccessErrorPage", ap_set_string_slot, (void *)APR_OFFSETOF(am_dir_cfg_rec, no_success_error_page), OR_AUTHCFG, "Web page to display if the idp posts with a failed" " authentication error. We will return a 401 Unauthorized error" " if this is unset and the idp posts such assertion." ), AP_INIT_TAKE1( "MellonSPMetadataFile", am_set_file_contents_slot, (void *)APR_OFFSETOF(am_dir_cfg_rec, sp_metadata_file), OR_AUTHCFG, "Full path to xml file with metadata for the SP." ), AP_INIT_TAKE1( "MellonSPPrivateKeyFile", am_set_file_contents_slot, (void *)APR_OFFSETOF(am_dir_cfg_rec, sp_private_key_file), OR_AUTHCFG, "Full path to pem file with the private key for the SP." ), AP_INIT_TAKE1( "MellonSPCertFile", am_set_file_contents_slot, (void *)APR_OFFSETOF(am_dir_cfg_rec, sp_cert_file), OR_AUTHCFG, "Full path to pem file with certificate for the SP." ), AP_INIT_TAKE12( "MellonIdPMetadataFile", am_set_idp_string_slot, NULL, OR_AUTHCFG, "Full path to xml metadata file for IdP, " "with optional validating chain." ), AP_INIT_TAKE12( "MellonIdPMetadataGlob", am_set_glob_fn12, am_set_idp_string_slot, OR_AUTHCFG, "Full path to xml metadata files for IdP, with glob(3) patterns. " "An optional validating chain can be supplied." ), AP_INIT_TAKE1( "MellonIdPPublicKeyFile", am_set_file_pathname_slot, (void *)APR_OFFSETOF(am_dir_cfg_rec, idp_public_key_file), OR_AUTHCFG, "Full path to pem file with the public key for the IdP." ), AP_INIT_TAKE1( "MellonIdPCAFile", am_set_file_pathname_slot, (void *)APR_OFFSETOF(am_dir_cfg_rec, idp_ca_file), 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_TAKE1( "MellonSPentityId", ap_set_string_slot, (void *)APR_OFFSETOF(am_dir_cfg_rec, sp_entity_id), OR_AUTHCFG, "SP entity Id to be used for metadata auto generation." ), AP_INIT_TAKE12( "MellonOrganizationName", am_set_langstring_slot, (void *)APR_OFFSETOF(am_dir_cfg_rec, sp_org_name), OR_AUTHCFG, "Language-qualified oranization name." ), AP_INIT_TAKE12( "MellonOrganizationDisplayName", am_set_langstring_slot, (void *)APR_OFFSETOF(am_dir_cfg_rec, sp_org_display_name), OR_AUTHCFG, "Language-qualified oranization name, human redable." ), AP_INIT_TAKE12( "MellonOrganizationURL", am_set_langstring_slot, (void *)APR_OFFSETOF(am_dir_cfg_rec, sp_org_url), OR_AUTHCFG, "Language-qualified oranization URL." ), AP_INIT_TAKE1( "MellonDefaultLoginPath", ap_set_string_slot, (void *)APR_OFFSETOF(am_dir_cfg_rec, login_path), OR_AUTHCFG, "The location where to redirect after IdP initiated login." " Default value is \"/\"." ), AP_INIT_TAKE1( "MellonDiscoveryURL", ap_set_string_slot, (void *)APR_OFFSETOF(am_dir_cfg_rec, discovery_url), OR_AUTHCFG, "The URL of IdP discovery service. Default is unset." ), AP_INIT_TAKE1( "MellonProbeDiscoveryTimeout", ap_set_int_slot, (void *)APR_OFFSETOF(am_dir_cfg_rec, probe_discovery_timeout), OR_AUTHCFG, "The timeout in seconds of IdP probe discovery service. " "The default is unset, which means that this feature is disabled." ), AP_INIT_TAKE12( "MellonProbeDiscoveryIdP", am_set_table_string_slot, (void *)APR_OFFSETOF(am_dir_cfg_rec, probe_discovery_idp), OR_AUTHCFG, "An IdP that can be used for IdP probe discovery." ), AP_INIT_TAKE1( "MellonEndpointPath", am_set_endpoint_path, NULL, OR_AUTHCFG, "The root directory of the SAML2 endpoints, relative to the root" " of the web server. Default value is \"/mellon/\", which will" " make mod_mellon to the handler for every request to" " \"http:///mellon/*\". The path you specify must" " be contained within the current Location directive." ), AP_INIT_TAKE1( "MellonAuthnContextClassRef", am_set_authn_context_class_ref, NULL, OR_AUTHCFG, "A list of AuthnContextClassRef to request in the AuthnRequest and " "to validate upon reception of an Assertion" ), AP_INIT_TAKE1( "MellonAuthnContextComparisonType", am_set_authn_context_comparison_type_slot, NULL, OR_AUTHCFG, "An AuthnContextComparisonType attribute as part of the AuthnRequest." ), AP_INIT_FLAG( "MellonSubjectConfirmationDataAddressCheck", ap_set_flag_slot, (void *)APR_OFFSETOF(am_dir_cfg_rec, subject_confirmation_data_address_check), OR_AUTHCFG, "Check address given in SubjectConfirmationData Address attribute. Default is on." ), AP_INIT_FLAG( "MellonSendCacheControlHeader", ap_set_flag_slot, (void *)APR_OFFSETOF(am_dir_cfg_rec, send_cache_control_header), OR_AUTHCFG, "Send the cache-control header on responses. Default is on." ), AP_INIT_TAKE1( "MellonDoNotVerifyLogoutSignature", am_set_do_not_verify_logout_signature, (void *)APR_OFFSETOF(am_dir_cfg_rec, do_not_verify_logout_signature), OR_AUTHCFG, "A list of entity of IdP whose logout requests signatures will not " "be valided" ), AP_INIT_FLAG( "MellonPostReplay", ap_set_flag_slot, (void *)APR_OFFSETOF(am_dir_cfg_rec, post_replay), OR_AUTHCFG, "Whether we should replay POST requests that trigger authentication. Default is off." ), AP_INIT_TAKE12( "MellonMergeEnvVars", am_set_merge_env_vars, NULL, OR_AUTHCFG, "Whether to merge environment variables multi-values or not. Default is off." "When first parameter is on, optional second parameter is the separator, " "defaulting to semicolon." ), AP_INIT_TAKE1( "MellonEnvVarsIndexStart", ap_set_int_slot, (void *)APR_OFFSETOF(am_dir_cfg_rec, env_vars_index_start), OR_AUTHCFG, "Start indexing environment variables for multivalues with 0 or 1. Default is 0." ), AP_INIT_FLAG( "MellonEnvVarsSetCount", ap_set_flag_slot, (void *)APR_OFFSETOF(am_dir_cfg_rec, env_vars_count_in_n), OR_AUTHCFG, "Whether to also populate environment variable suffixed _N with number of values. Default is off." ), AP_INIT_FLAG( "MellonECPSendIDPList", ap_set_flag_slot, (void *)APR_OFFSETOF(am_dir_cfg_rec, ecp_send_idplist), OR_AUTHCFG, "Whether to send an ECP client a list of IdP's. Default is off." ), AP_INIT_TAKE_ARGV( "MellonRedirectDomains", am_set_redirect_domains, NULL, OR_AUTHCFG, "List of domains we can redirect to." ), AP_INIT_TAKE1( "MellonSignatureMethod", am_set_signature_method_slot, NULL, OR_AUTHCFG, "Signature method used to sign SAML messages sent by Mellon" ), {NULL} }; const am_error_map_t auth_mellon_errormap[] = { { LASSO_PROFILE_ERROR_STATUS_NOT_SUCCESS, HTTP_UNAUTHORIZED }, #ifdef LASSO_PROFILE_ERROR_REQUEST_DENIED { LASSO_PROFILE_ERROR_REQUEST_DENIED, HTTP_UNAUTHORIZED }, #endif { 0, 0 } }; /* Release a lasso_server object associated with this configuration. * * Parameters: * void *data The pointer to the configuration data. * * Returns: * Always APR_SUCCESS. */ static apr_status_t auth_mellon_free_server(void *data) { am_dir_cfg_rec *dir = data; if (dir->server != NULL) { lasso_server_destroy(dir->server); dir->server = NULL; } return APR_SUCCESS; } /* This function creates and initializes a directory configuration * object for auth_mellon. * * Parameters: * apr_pool_t *p The pool we should allocate memory from. * char *d Unused, always NULL. * * Returns: * The new directory configuration object. */ void *auth_mellon_dir_config(apr_pool_t *p, char *d) { am_dir_cfg_rec *dir = apr_palloc(p, sizeof(*dir)); apr_pool_cleanup_register(p, dir, auth_mellon_free_server, auth_mellon_free_server); dir->enable_mellon = am_enable_default; dir->varname = default_cookie_name; dir->secure = default_secure_cookie; dir->http_only = default_http_only_cookie; dir->merge_env_vars = default_merge_env_vars; dir->env_vars_index_start = default_env_vars_index_start; dir->env_vars_count_in_n = default_env_vars_count_in_n; dir->cond = apr_array_make(p, 0, sizeof(am_cond_t)); dir->cookie_domain = NULL; dir->cookie_path = NULL; dir->cookie_samesite = am_samesite_default; dir->envattr = apr_hash_make(p); dir->env_prefix = default_env_prefix; dir->userattr = default_user_attribute; dir->idpattr = NULL; dir->signature_method = inherit_signature_method; dir->dump_session = default_dump_session; dir->dump_saml_response = default_dump_saml_response; dir->endpoint_path = default_endpoint_path; dir->session_length = -1; /* -1 means use default. */ dir->no_cookie_error_page = NULL; dir->no_success_error_page = NULL; dir->sp_metadata_file = NULL; dir->sp_private_key_file = NULL; dir->sp_cert_file = NULL; 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 */ dir->probe_discovery_idp = apr_table_make(p, 0); dir->sp_entity_id = NULL; dir->sp_org_name = apr_hash_make(p); dir->sp_org_display_name = apr_hash_make(p); dir->sp_org_url = apr_hash_make(p); apr_thread_mutex_create(&dir->server_mutex, APR_THREAD_MUTEX_DEFAULT, p); dir->inherit_server_from = dir; dir->server = NULL; dir->authn_context_class_ref = apr_array_make(p, 0, sizeof(char *)); dir->authn_context_comparison_type = NULL; dir->subject_confirmation_data_address_check = inherit_subject_confirmation_data_address_check; dir->send_cache_control_header = inherit_send_cache_control_header; dir->do_not_verify_logout_signature = apr_hash_make(p); dir->post_replay = inherit_post_replay; dir->redirect_domains = default_redirect_domains; dir->ecp_send_idplist = inherit_ecp_send_idplist; return dir; } /* Determine whether this configuration changes anything relevant to the * lasso_server configuration. * * Parameters: * am_dir_cfg_rec *add_cfg The new configuration. * * Returns: * true if we can inherit the lasso_server object, false if not. */ static bool cfg_can_inherit_lasso_server(const am_dir_cfg_rec *add_cfg) { if (add_cfg->endpoint_path != default_endpoint_path) return false; if (add_cfg->sp_metadata_file != NULL || add_cfg->sp_private_key_file != NULL || add_cfg->sp_cert_file != NULL) return false; if (add_cfg->idp_metadata->nelts > 0 || add_cfg->idp_public_key_file != NULL || add_cfg->idp_ca_file != NULL || add_cfg->idp_ignore != NULL) return false; if (apr_hash_count(add_cfg->sp_org_name) > 0 || apr_hash_count(add_cfg->sp_org_display_name) > 0 || apr_hash_count(add_cfg->sp_org_url) > 0) return false; return true; } /* This function merges two am_dir_cfg_rec structures. * It will try to inherit from the base where possible. * * Parameters: * apr_pool_t *p The pool we should allocate memory from. * void *base The original structure. * void *add The structure we should add to base. * * Returns: * The merged structure. */ void *auth_mellon_dir_merge(apr_pool_t *p, void *base, void *add) { am_dir_cfg_rec *base_cfg = (am_dir_cfg_rec *)base; am_dir_cfg_rec *add_cfg = (am_dir_cfg_rec *)add; am_dir_cfg_rec *new_cfg; new_cfg = (am_dir_cfg_rec *)apr_palloc(p, sizeof(*new_cfg)); apr_pool_cleanup_register(p, new_cfg, auth_mellon_free_server, auth_mellon_free_server); new_cfg->enable_mellon = (add_cfg->enable_mellon != am_enable_default ? add_cfg->enable_mellon : base_cfg->enable_mellon); new_cfg->varname = (add_cfg->varname != default_cookie_name ? add_cfg->varname : base_cfg->varname); new_cfg->secure = (add_cfg->secure != default_secure_cookie ? add_cfg->secure : base_cfg->secure); new_cfg->http_only = (add_cfg->http_only != default_http_only_cookie ? add_cfg->http_only : base_cfg->http_only); new_cfg->merge_env_vars = (add_cfg->merge_env_vars != default_merge_env_vars ? add_cfg->merge_env_vars : base_cfg->merge_env_vars); new_cfg->env_vars_index_start = (add_cfg->env_vars_index_start != default_env_vars_index_start ? add_cfg->env_vars_index_start : base_cfg->env_vars_index_start); new_cfg->env_vars_count_in_n = (add_cfg->env_vars_count_in_n != default_env_vars_count_in_n ? add_cfg->env_vars_count_in_n : base_cfg->env_vars_count_in_n); new_cfg->cookie_domain = (add_cfg->cookie_domain != NULL ? add_cfg->cookie_domain : base_cfg->cookie_domain); new_cfg->cookie_path = (add_cfg->cookie_path != NULL ? add_cfg->cookie_path : base_cfg->cookie_path); new_cfg->cookie_samesite = (add_cfg->cookie_samesite != am_samesite_default ? add_cfg->cookie_samesite : base_cfg->cookie_samesite); new_cfg->cond = apr_array_copy(p, (!apr_is_empty_array(add_cfg->cond)) ? add_cfg->cond : base_cfg->cond); new_cfg->envattr = apr_hash_copy(p, (apr_hash_count(add_cfg->envattr) > 0) ? add_cfg->envattr : base_cfg->envattr); new_cfg->env_prefix = (add_cfg->env_prefix != default_env_prefix ? add_cfg->env_prefix : base_cfg->env_prefix); new_cfg->userattr = (add_cfg->userattr != default_user_attribute ? add_cfg->userattr : base_cfg->userattr); new_cfg->idpattr = (add_cfg->idpattr != NULL ? add_cfg->idpattr : base_cfg->idpattr); new_cfg->signature_method = CFG_MERGE(add_cfg, base_cfg, signature_method); new_cfg->dump_session = (add_cfg->dump_session != default_dump_session ? add_cfg->dump_session : base_cfg->dump_session); new_cfg->dump_saml_response = (add_cfg->dump_saml_response != default_dump_saml_response ? add_cfg->dump_saml_response : base_cfg->dump_saml_response); new_cfg->endpoint_path = ( add_cfg->endpoint_path != default_endpoint_path ? add_cfg->endpoint_path : base_cfg->endpoint_path ); new_cfg->session_length = (add_cfg->session_length != -1 ? add_cfg->session_length : base_cfg->session_length); new_cfg->no_cookie_error_page = (add_cfg->no_cookie_error_page != NULL ? add_cfg->no_cookie_error_page : base_cfg->no_cookie_error_page); new_cfg->no_success_error_page = (add_cfg->no_success_error_page != NULL ? add_cfg->no_success_error_page : base_cfg->no_success_error_page); new_cfg->sp_metadata_file = (add_cfg->sp_metadata_file ? add_cfg->sp_metadata_file : base_cfg->sp_metadata_file); new_cfg->sp_private_key_file = (add_cfg->sp_private_key_file ? add_cfg->sp_private_key_file : base_cfg->sp_private_key_file); new_cfg->sp_cert_file = (add_cfg->sp_cert_file ? add_cfg->sp_cert_file : base_cfg->sp_cert_file); 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 : base_cfg->idp_public_key_file); new_cfg->idp_ca_file = (add_cfg->idp_ca_file ? 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_entity_id = (add_cfg->sp_entity_id ? add_cfg->sp_entity_id : base_cfg->sp_entity_id); new_cfg->sp_org_name = apr_hash_copy(p, (apr_hash_count(add_cfg->sp_org_name) > 0) ? add_cfg->sp_org_name : base_cfg->sp_org_name); new_cfg->sp_org_display_name = apr_hash_copy(p, (apr_hash_count(add_cfg->sp_org_display_name) > 0) ? add_cfg->sp_org_display_name : base_cfg->sp_org_display_name); new_cfg->sp_org_url = apr_hash_copy(p, (apr_hash_count(add_cfg->sp_org_url) > 0) ? add_cfg->sp_org_url : base_cfg->sp_org_url); new_cfg->login_path = (add_cfg->login_path != default_login_path ? add_cfg->login_path : base_cfg->login_path); new_cfg->discovery_url = (add_cfg->discovery_url ? add_cfg->discovery_url : base_cfg->discovery_url); new_cfg->probe_discovery_timeout = (add_cfg->probe_discovery_timeout != -1 ? add_cfg->probe_discovery_timeout : base_cfg->probe_discovery_timeout); new_cfg->probe_discovery_idp = apr_table_copy(p, (!apr_is_empty_table(add_cfg->probe_discovery_idp)) ? add_cfg->probe_discovery_idp : base_cfg->probe_discovery_idp); if (cfg_can_inherit_lasso_server(add_cfg)) { new_cfg->inherit_server_from = base_cfg->inherit_server_from; } else { apr_thread_mutex_create(&new_cfg->server_mutex, APR_THREAD_MUTEX_DEFAULT, p); new_cfg->inherit_server_from = new_cfg; } new_cfg->server = NULL; new_cfg->authn_context_class_ref = (add_cfg->authn_context_class_ref->nelts ? add_cfg->authn_context_class_ref : base_cfg->authn_context_class_ref); new_cfg->authn_context_comparison_type = (add_cfg->authn_context_comparison_type != NULL ? add_cfg->authn_context_comparison_type : base_cfg->authn_context_comparison_type); new_cfg->do_not_verify_logout_signature = apr_hash_copy(p, (apr_hash_count(add_cfg->do_not_verify_logout_signature) > 0) ? add_cfg->do_not_verify_logout_signature : base_cfg->do_not_verify_logout_signature); new_cfg->subject_confirmation_data_address_check = CFG_MERGE(add_cfg, base_cfg, subject_confirmation_data_address_check); new_cfg->send_cache_control_header = CFG_MERGE(add_cfg, base_cfg, send_cache_control_header); new_cfg->post_replay = CFG_MERGE(add_cfg, base_cfg, post_replay); new_cfg->ecp_send_idplist = CFG_MERGE(add_cfg, base_cfg, ecp_send_idplist); new_cfg->redirect_domains = (add_cfg->redirect_domains != default_redirect_domains ? add_cfg->redirect_domains : base_cfg->redirect_domains); return new_cfg; } /* This function creates a new per-server configuration. * auth_mellon uses the server configuration to store a pointer * to the global module configuration. * * Parameters: * apr_pool_t *p The pool we should allocate memory from. * server_rec *s The server we should add our configuration to. * * Returns: * The new per-server configuration. */ void *auth_mellon_server_config(apr_pool_t *p, server_rec *s) { am_srv_cfg_rec *srv; am_mod_cfg_rec *mod; const char key[] = "auth_mellon_server_config"; srv = apr_palloc(p, sizeof(*srv)); #ifdef ENABLE_DIAGNOSTICS srv->diag_cfg.filename = default_diag_filename; srv->diag_cfg.fd = NULL; srv->diag_cfg.flags = default_diag_flags; srv->diag_cfg.dir_cfg_emitted = apr_table_make(p, 0); #endif /* we want to keeep our global configuration of shared memory and * mutexes, so we try to find it in the userdata before doing anything * else */ apr_pool_userdata_get((void **)&mod, key, p); if (mod) { srv->mc = mod; return srv; } /* the module has not been initiated at all */ mod = apr_palloc(p, sizeof(*mod)); mod->cache_size = 100; /* ought to be enough for everybody */ mod->lock_file = "/var/run/mod_auth_mellon.lock"; mod->post_dir = NULL; mod->post_ttl = post_ttl; mod->post_count = post_count; mod->post_size = post_size; mod->entry_size = AM_CACHE_DEFAULT_ENTRY_SIZE; mod->init_cache_size = 0; mod->init_lock_file = NULL; mod->init_entry_size = 0; mod->cache = NULL; mod->lock = NULL; apr_pool_userdata_set(mod, key, apr_pool_cleanup_null, p); srv->mc = mod; return srv; } /* This function merges two am_srv_cfg_rec structures. * It will try to inherit from the base where possible. * * Parameters: * apr_pool_t *p The pool we should allocate memory from. * void *base The original structure. * void *add The structure we should add to base. * * Returns: * The merged structure. */ void *auth_mellon_srv_merge(apr_pool_t *p, void *base, void *add) { am_srv_cfg_rec *base_cfg = (am_srv_cfg_rec *)base; am_srv_cfg_rec *new_cfg; new_cfg = (am_srv_cfg_rec *)apr_palloc(p, sizeof(*new_cfg)); new_cfg->mc = base_cfg->mc; #ifdef ENABLE_DIAGNOSTICS am_srv_cfg_rec *add_cfg = (am_srv_cfg_rec *)add; new_cfg->diag_cfg.filename = (add_cfg->diag_cfg.filename != default_diag_filename ? add_cfg->diag_cfg.filename : base_cfg->diag_cfg.filename); new_cfg->diag_cfg.fd = NULL; new_cfg->diag_cfg.flags = (add_cfg->diag_cfg.flags != default_diag_flags ? add_cfg->diag_cfg.flags : base_cfg->diag_cfg.flags); new_cfg->diag_cfg.dir_cfg_emitted = apr_table_make(p, 0); #endif return new_cfg; }