summaryrefslogtreecommitdiff
path: root/www/ap2-auth-mellon
diff options
context:
space:
mode:
authormanu <manu@pkgsrc.org>2011-04-04 08:46:42 +0000
committermanu <manu@pkgsrc.org>2011-04-04 08:46:42 +0000
commit75fee338a6df8ae364866e846b71448eadcd479e (patch)
tree5e69d668a1c1957c823731aa32db7285a41edaf3 /www/ap2-auth-mellon
parentcf8b7a7ded4c14d6a7f47cea5861632079a5b4a5 (diff)
downloadpkgsrc-75fee338a6df8ae364866e846b71448eadcd479e.tar.gz
Patches from upcoming 0.3.1
Diffstat (limited to 'www/ap2-auth-mellon')
-rw-r--r--www/ap2-auth-mellon/patches/patch-ac515
-rw-r--r--www/ap2-auth-mellon/patches/patch-ae523
-rw-r--r--www/ap2-auth-mellon/patches/patch-af65
-rw-r--r--www/ap2-auth-mellon/patches/patch-ag149
4 files changed, 1252 insertions, 0 deletions
diff --git a/www/ap2-auth-mellon/patches/patch-ac b/www/ap2-auth-mellon/patches/patch-ac
new file mode 100644
index 00000000000..91a7d3d5b22
--- /dev/null
+++ b/www/ap2-auth-mellon/patches/patch-ac
@@ -0,0 +1,515 @@
+$NetBSD: patch-ac,v 1.1 2011/04/04 08:46:42 manu Exp $
+
+Add MellonCond directive (pulled up from upstream)
+
+Index: auth_mellon_util.c
+===================================================================
+--- auth_mellon_util.c (revision 113)
++++ auth_mellon_util.c (working copy)
+@@ -51,7 +51,7 @@
+
+
+ /* This function checks if the user has access according
+- * to the MellonRequire directives.
++ * to the MellonRequire and MellonCond directives.
+ *
+ * Parameters:
+ * request_rec *r The current request.
+@@ -63,51 +63,105 @@
+ int am_check_permissions(request_rec *r, am_cache_entry_t *session)
+ {
+ am_dir_cfg_rec *dir_cfg;
+- apr_hash_index_t *idx;
+- const char *key;
+- apr_array_header_t *rlist;
+ int i, j;
+- int rlist_ok;
+- const char **re;
++ int skip_or = 0;
+
+ dir_cfg = am_get_dir_cfg(r);
+
+- /* Iterate over all require-directives. */
+- for(idx = apr_hash_first(r->pool, dir_cfg->require);
+- idx != NULL;
+- idx = apr_hash_next(idx)) {
++ /* Iterate over all cond-directives */
++ for (i = 0; i < dir_cfg->cond->nelts; i++) {
++ am_cond_t *ce;
++ const char *value = NULL;
++ int match = 0;
+
+- /* Get current require directive. key will be the name
+- * of the attribute, and rlist is a list of all allowed values.
++ ce = &((am_cond_t *)(dir_cfg->cond->elts))[i];
++
++ /*
++ * Rule with ignore flog?
+ */
+- apr_hash_this(idx, (const void **)&key, NULL, (void **)&rlist);
++ if (ce->flags & AM_COND_FLAG_IGN)
++ continue;
+
+- /* Reset status to 0 before search. */
+- rlist_ok = 0;
++ /*
++ * We matched a [OR] rule, skip the next rules
++ * until we have one without [OR].
++ */
++ if (skip_or) {
++ if (!(ce->flags & AM_COND_FLAG_OR))
++ skip_or = 0;
+
+- re = (const char **)rlist->elts;
++ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
++ "Skip %s, [OR] rule matched previously",
++ ce->directive);
++ continue;
++ }
++
++ /*
++ * look for a match on each value for this attribute,
++ * stop on first match.
++ */
++ for (j = 0; (j < session->size) && !match; j++) {
++ const char *varname = NULL;
+
+- /* rlist is an array of all the valid values for this attribute. */
+- for(i = 0; i < rlist->nelts && !rlist_ok; i++) {
++ /*
++ * if MAP flag is set, check for remapped
++ * attribute name with mellonSetEnv
++ */
++ if (ce->flags & AM_COND_FLAG_MAP)
++ varname = apr_hash_get(dir_cfg->envattr,
++ session->env[j].varname,
++ APR_HASH_KEY_STRING);
+
+- /* Search for a matching attribute in the session data. */
+- for(j = 0; j < session->size && !rlist_ok; j++) {
+- if(strcmp(session->env[j].varname, key) == 0 &&
+- strcmp(session->env[j].value, re[i]) == 0) {
+- /* We found a attribute with the correct name
+- * and value.
+- */
+- rlist_ok = 1;
+- }
++ /*
++ * Otherwise or if not found, use the attribute name
++ * sent by the IdP.
++ */
++ if (varname == NULL)
++ varname = session->env[j].varname;
++
++ if (strcmp(varname, ce->varname) != 0)
++ continue;
++
++ value = session->env[j].value;
++
++ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
++ "Evaluate %s vs \"%s\"",
++ ce->directive, value);
++
++ if (value == NULL) {
++ match = 0; /* can not happen */
++ } else if (ce->flags & AM_COND_FLAG_REG) {
++ match = !ap_regexec(ce->regex, value, 0, NULL, 0);
++ } else if (ce->flags & AM_COND_FLAG_NC) {
++ match = !strcasecmp(ce->str, value);
++ } else {
++ match = !strcmp(ce->str, value);
+ }
+ }
+
+- if(!rlist_ok) {
+- ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
+- "Client failed to match required attribute \"%s\".",
+- key);
++ if (ce->flags & AM_COND_FLAG_NOT)
++ match = !match;
++
++ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
++ "%s: %smatch", ce->directive,
++ (match == 0) ? "no ": "");
++
++ /*
++ * If no match, we stop here, except if it is an [OR] condition
++ */
++ if (!match & !(ce->flags & AM_COND_FLAG_OR)) {
++ ap_log_rerror(APLOG_MARK, APLOG_NOTICE, 0, r,
++ "Client failed to match %s",
++ ce->directive);
+ return HTTP_FORBIDDEN;
+ }
++
++ /*
++ * Match on [OR] condition means we skip until a rule
++ * without [OR],
++ */
++ if (match && (ce->flags & AM_COND_FLAG_OR))
++ skip_or = 1;
+ }
+
+ return OK;
+Index: README
+===================================================================
+--- README (revision 113)
++++ README (working copy)
+@@ -152,9 +152,9 @@
+ # "auth": We will populate the environment with information about
+ # the user if he is authorized. If he is authenticated
+ # (logged in), but not authorized (according to the
+- # MellonRequire directives, then we will return a 403
+- # Forbidden error. If he isn't authenticated then we will
+- # redirect him to the login page of the IdP.
++ # MellonRequire and MellonCond directives, then we will
++ # return a 403 Forbidden error. If he isn't authenticated
++ # then we will redirect him to the login page of the IdP.
+ #
+ # Default: MellonEnable "off"
+ MellonEnable "auth"
+@@ -221,14 +221,45 @@
+ # Note that the attribute name is the name we received from the
+ # IdP.
+ #
+- # If you don't list any MellonRequire directives, then any user
+- # authenticated by the IdP will have access to this service. If
+- # you list several MellonRequire directives, then all of them
+- # will have to match.
++ # If you don't list any MellonRequire directives (and any
++ # MellonCond directives, see below), then any user authenticated
++ # by the IdP will have access to this service. If you list several
++ # MellonRequire directives, then all of them will have to match.
++ # If you use multiple MellonRequire directive on the same
++ # attribute, the last overrides the previous ones.
+ #
+ # Default: None set.
+ MellonRequire "eduPersonAffiliation" "student" "employee"
+
++ # MellonCond provides the same function as MellonRequire, with
++ # extra functionnality (MellonRequire is retained for backward
++ # compatibility). The syntax is
++ # 'MellonCond <attribute name> <value> [<options>]'
++ #
++ # <options> is an optional, comma-separated list of option
++ # encloseed with brackets. Here is an example: [NOT,NC]
++ # The valid options are:
++ # OR If this MellonCond evaluted to false, then the
++ # next one will be checked. If it evalutes to true,
++ # then the overall check succeeds.
++ # NOT This MellonCond evaluates to true if the attribute
++ # does not match the value.
++ # REG Value to check is a regular expression.
++ # NC Perform case insensitive match.
++ # MAP Attempt to search an attribute with name remapped by
++ # MellonSetEnv. Fallback to non remapped name if not
++ # found.
++ #
++ # It is allowed to have multiple MellonCond on the same
++ # attribute, and to mix MellonCond and MellonRequire.
++ # Note that this can create tricky situations, since the OR
++ # option has effect on a following MellonRequire directive.
++ #
++ # Default: none set
++ # MellonCond "mail" "@example\.net$" [OR,REG]
++ # MellonCond "mail" "@example\.com$" [OR,REG]
++ # MellonCond "uid" "superuser"
++
+ # MellonEndpointPath selects which directory mod_auth_mellon
+ # should assume contains the SAML 2.0 endpoints. Any request to
+ # this directory will be handled by mod_auth_mellon.
+Index: auth_mellon.h
+===================================================================
+--- auth_mellon.h (revision 113)
++++ auth_mellon.h (working copy)
+@@ -124,6 +124,30 @@
+ am_decoder_feide,
+ } am_decoder_t;
+
++typedef enum {
++ AM_COND_FLAG_NULL = 0x00, /* No flags */
++ AM_COND_FLAG_OR = 0x01, /* Or with next condition */
++ AM_COND_FLAG_NOT = 0x02, /* Negate this condition */
++ AM_COND_FLAG_REG = 0x04, /* Condition is regex */
++ AM_COND_FLAG_NC = 0x08, /* Case insensitive match */
++ AM_COND_FLAG_MAP = 0x10, /* Try to use attribute name from MellonSetEnv */
++ AM_COND_FLAG_IGN = 0x20, /* Condition is to be ignored */
++ AM_COND_FLAG_REQ = 0x40, /* Condition was configure using MellonRequire */
++} am_cond_flag_t;
++
++/* Not counting AM_COND_FLAG_NULL */
++#define AM_COND_FLAG_COUNT 7
++
++extern const char *am_cond_options[];
++
++typedef struct {
++ const char *varname;
++ int flags;
++ const char *str;
++ ap_regex_t *regex;
++ const char *directive;
++} am_cond_t;
++
+ typedef struct am_dir_cfg_rec {
+ /* enable_mellon is used to enable auth_mellon for a location.
+ */
+@@ -136,7 +160,7 @@
+
+ const char *varname;
+ int secure;
+- apr_hash_t *require;
++ apr_array_header_t *cond;
+ apr_hash_t *envattr;
+ const char *userattr;
+ const char *idpattr;
+Index: auth_mellon_config.c
+===================================================================
+--- auth_mellon_config.c (revision 113)
++++ auth_mellon_config.c (working copy)
+@@ -422,7 +422,136 @@
+ 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
++ */
++const char *am_cond_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 */
++ "IGN", /* AM_EXPIRE_FLAG_IGN */
++ "REQ", /* AM_EXPIRE_FLAG_REQ */
++};
+
++static int am_cond_flags(const char *arg)
++{
++ int flags = AM_COND_FLAG_NULL;
++
++ /* Skip inital [ */
++ if (arg[0] == '[')
++ arg++;
++ else
++ return -1;
++
++ do {
++ apr_size_t i;
++
++ for (i = 0; i < AM_COND_FLAG_COUNT; i++) {
++ apr_size_t optlen = strlen(am_cond_options[i]);
++
++ if (strncmp(arg, am_cond_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 == AM_COND_FLAG_COUNT)
++ return -1;
++
++ /* skip spaces, tabs and commas */
++ arg += strspn(arg, " \t,");
++
++ /* Garbage after ] is ignored */
++ if (*arg == ']')
++ return flags;
++ }
++ } 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;
++ am_cond_t *element;
++
++ if (*attribute == '\0' || *value == '\0')
++ return apr_pstrcat(cmd->pool, cmd->cmd->name,
++ " takes two or three arguments", NULL);
++
++ element = (am_cond_t *)apr_array_push(d->cond);
++ element->varname = attribute;
++ element->flags = AM_COND_FLAG_NULL;
++ element->str = NULL;
++ element->regex = NULL;
++ element->directive = apr_pstrcat(cmd->pool, cmd->directive->directive,
++ " ", cmd->directive->args, NULL);
++
++ /* Handle optional flags */
++ if (*options != '\0') {
++ int flags;
++
++ flags = am_cond_flags(options);
++ if (flags == -1)
++ return apr_psprintf(cmd->pool, "%s - invalid flags %s",
++ cmd->cmd->name, options);
++
++ element->flags = flags;
++ }
++
++ 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);
++ }
++
++ /*
++ * We keep the string also for regex, so that we can
++ * print it for debug purpose.
++ */
++ 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.
+@@ -440,10 +569,11 @@
+ void *struct_ptr,
+ const char *arg)
+ {
+- apr_array_header_t *r;
+ am_dir_cfg_rec *d = struct_ptr;
+ char *attribute, *value;
+- const char **element;
++ 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);
+@@ -453,20 +583,47 @@
+ " 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 {
+- r = (apr_array_header_t *)apr_hash_get(d->require, attribute,
+- APR_HASH_KEY_STRING);
++ 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;
+
+- if (r == NULL) {
+- r = apr_array_make(cmd->pool, 2, sizeof(const char *));
+- apr_hash_set(d->require, attribute, APR_HASH_KEY_STRING, r);
++ /*
++ * 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;
+ }
+
+- element = (const char **)apr_array_push(r);
+- *element = value;
+-
+ } while (*(value = ap_getword_conf(cmd->pool, &arg)) != '\0');
+
++ /*
++ * Remove OR flag on last element
++ */
++ element->flags &= ~AM_COND_FLAG_OR;
++
+ return NULL;
+ }
+
+@@ -650,6 +807,15 @@
+ " for the attribute. The syntax is:"
+ " MellonRequire <attribute> <value1> [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 <attribute> <value> [<options>]."
++ ),
+ AP_INIT_TAKE1(
+ "MellonSessionLength",
+ ap_set_int_slot,
+@@ -795,7 +961,7 @@
+
+ dir->varname = default_cookie_name;
+ dir->secure = default_secure_cookie;
+- dir->require = apr_hash_make(p);
++ dir->cond = apr_array_make(p, 0, sizeof(am_cond_t));
+ dir->envattr = apr_hash_make(p);
+ dir->userattr = default_user_attribute;
+ dir->idpattr = NULL;
+@@ -871,10 +1037,10 @@
+ base_cfg->secure);
+
+
+- new_cfg->require = apr_hash_copy(p,
+- (apr_hash_count(add_cfg->require) > 0) ?
+- add_cfg->require :
+- base_cfg->require);
++ 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) ?
diff --git a/www/ap2-auth-mellon/patches/patch-ae b/www/ap2-auth-mellon/patches/patch-ae
new file mode 100644
index 00000000000..a1f5ba5667f
--- /dev/null
+++ b/www/ap2-auth-mellon/patches/patch-ae
@@ -0,0 +1,523 @@
+$NetBSD: patch-ae,v 1.1 2011/04/04 08:46:42 manu Exp $
+
+Replace buildtin: diescovery URL by the discoProbe endpoint (pulled from
+upstream)
+
+Index: auth_mellon_handler.c
+===================================================================
+--- auth_mellon_handler.c (revision 112)
++++ auth_mellon_handler.c (working copy)
+@@ -226,34 +226,7 @@
+ return provider_id;
+ }
+
+-/* This returns built-in IdP discovery timeout
+- *
+- * Parameters:
+- * request_rec *r The request we received.
+- *
+- * Returns:
+- * the timeout, -1 if not enabled.
+- */
+-static long am_builtin_discovery_timeout(request_rec *r)
+-{
+- am_dir_cfg_rec *cfg = am_get_dir_cfg(r);
+- const char *builtin = "builtin:get-metadata";
+- const char *timeout = "?timeout=";
+- const char *cp;
+- const long default_timeout = 1L;
+
+- if ((cfg->discovery_url == NULL) ||
+- (strncmp(cfg->discovery_url, builtin, strlen(builtin)) != 0))
+- return -1;
+-
+- cp = cfg->discovery_url + strlen(builtin);
+- if (strncmp(cp, timeout, strlen(timeout)) != 0)
+- return default_timeout;
+-
+- cp += strlen(timeout);
+- return atoi(cp);
+-}
+-
+ /* This function selects an IdP and returns its provider_id
+ *
+ * Parameters:
+@@ -267,8 +240,6 @@
+ am_dir_cfg_rec *cfg = am_get_dir_cfg(r);
+ const char *idp_provider_id;
+ const char *idp_metadata_file;
+- apr_hash_index_t *index;
+- long timeout;
+
+ /*
+ * If we have a single IdP, return that one.
+@@ -308,47 +279,6 @@
+ return idp_provider_id;
+ }
+
+- /*
+- * If built-in IdP discovery is not configured, return default.
+- */
+- timeout = am_builtin_discovery_timeout(r);
+- if (timeout == -1)
+- return am_first_idp(r);
+-
+- /*
+- * Otherwise, proceed with built-in IdP discovery:
+- * send probes for all configures IdP to check availability.
+- * The first to answer is chosen. On error, use default.
+- */
+- for (index = apr_hash_first(r->pool, cfg->idp_metadata_files);
+- index;
+- index = apr_hash_next(index)) {
+- void *buffer;
+- apr_size_t len;
+- apr_ssize_t slen;
+- long status;
+-
+- apr_hash_this(index,
+- (const void **)&idp_provider_id,
+- &slen,
+- (void *)&idp_metadata_file);
+-
+- status = 0;
+- if (am_httpclient_get(r, idp_provider_id, &buffer, &len,
+- timeout, &status) != OK)
+- continue;
+-
+- if (status != HTTP_OK) {
+- ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
+- "Cannot probe %s: IdP returned HTTP %ld",
+- idp_provider_id, status);
+- continue;
+- }
+-
+- /* We got some succes */
+- return idp_provider_id;
+- }
+-
+ /*
+ * No IdP answered, use default
+ * Perhaps we should redirect to an error page instead.
+@@ -2506,7 +2436,6 @@
+
+ /* Check if IdP discovery is in use and no IdP was selected yet */
+ if ((cfg->discovery_url != NULL) &&
+- (am_builtin_discovery_timeout(r) == -1) && /* no built-in discovery */
+ (am_extract_query_parameter(r->pool, r->args, "IdP") == NULL)) {
+ char *discovery_url;
+ char *return_url;
+@@ -2536,8 +2465,7 @@
+ /* If IdP discovery is in use and we have an IdP selected,
+ * set the relay_state
+ */
+- if ((cfg->discovery_url != NULL) &&
+- (am_builtin_discovery_timeout(r) == -1)) { /* no built-in discovery */
++ if (cfg->discovery_url != NULL) {
+ char *return_url;
+
+ return_url = am_extract_query_parameter(r->pool, r->args, "ReturnTo");
+@@ -2615,7 +2543,151 @@
+ return am_send_authn_request(r, idp, return_to, is_passive);
+ }
+
++/* This function handles requests to the probe discovery handler
++ *
++ * Parameters:
++ * request_rec *r The request.
++ *
++ * Returns:
++ * OK on success, or an error if any of the steps fail.
++ */
++static int am_handle_probe_discovery(request_rec *r) {
++ am_dir_cfg_rec *cfg = am_get_dir_cfg(r);
++ const char *idp = NULL;
++ int timeout;
++ apr_hash_index_t *index;
++ char *return_to;
++ char *idp_param;
++ char *redirect_url;
++ int ret;
+
++ /*
++ * If built-in IdP discovery is not configured, return error.
++ * For now we only have the get-metadata metadata method, so this
++ * information is not saved in configuration nor it is checked here.
++ */
++ timeout = cfg->probe_discovery_timeout;
++ if (timeout == -1) {
++ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
++ "probe discovery handler invoked but not "
++ "configured. Plase set MellonProbeDiscoveryTimeout.");
++ return HTTP_INTERNAL_SERVER_ERROR;
++ }
++
++ /*
++ * Check for mandatory arguments early to avoid sending
++ * probles for nothing.
++ */
++ return_to = am_extract_query_parameter(r->pool, r->args, "return");
++ if(return_to == NULL) {
++ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
++ "Missing required return parameter.");
++ return HTTP_BAD_REQUEST;
++ }
++
++ ret = am_urldecode(return_to);
++ if (ret != OK) {
++ ap_log_rerror(APLOG_MARK, APLOG_ERR, ret, r,
++ "Could not urldecode return value.");
++ return HTTP_BAD_REQUEST;
++ }
++
++ idp_param = am_extract_query_parameter(r->pool, r->args, "returnIDParam");
++ if(idp_param == NULL) {
++ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
++ "Missing required returnIDParam parameter.");
++ return HTTP_BAD_REQUEST;
++ }
++
++ ret = am_urldecode(idp_param);
++ if (ret != OK) {
++ ap_log_rerror(APLOG_MARK, APLOG_ERR, ret, r,
++ "Could not urldecode returnIDParam value.");
++ return HTTP_BAD_REQUEST;
++ }
++
++ /*
++ * Proceed with built-in IdP discovery.
++ *
++ * Send probes for all configured IdP to check availability.
++ * The first to answer is chosen, but the list of usable
++ * IdP can be restricted in configuration.
++ */
++ for (index = apr_hash_first(r->pool, cfg->idp_metadata_files);
++ index;
++ index = apr_hash_next(index)) {
++ void *dontcare;
++ const char *ping_url;
++ apr_size_t len;
++ apr_ssize_t slen;
++ long status;
++
++ apr_hash_this(index, (const void **)&idp,
++ &slen, (void *)&dontcare);
++ ping_url = idp;
++
++ /*
++ * If a list of IdP was given for probe discovery,
++ * skip any IdP that does not match.
++ */
++ if (apr_hash_count(cfg->probe_discovery_idp) != 0) {
++ char *value = apr_hash_get(cfg->probe_discovery_idp,
++ idp, APR_HASH_KEY_STRING);
++
++ if (value == NULL) {
++ /* idp not in list, try the next one */
++ idp = NULL;
++ continue;
++ } else {
++ /* idp in list, use the value as the ping url */
++ ping_url = value;
++ }
++ }
++
++ status = 0;
++ if (am_httpclient_get(r, ping_url, &dontcare, &len,
++ timeout, &status) != OK)
++ continue;
++
++ if (status != HTTP_OK) {
++ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
++ "Cannot probe %s: \"%s\" returned HTTP %ld",
++ idp, ping_url, status);
++ continue;
++ }
++
++ /* We got some succes */
++ ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
++ "probeDiscovery using %s", idp);
++ break;
++ }
++
++ /*
++ * On failure, try default
++ */
++ if (idp == NULL) {
++ idp = am_first_idp(r);
++ if (idp == NULL) {
++ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
++ "probeDiscovery found no usable IdP.");
++ return HTTP_INTERNAL_SERVER_ERROR;
++ } else {
++ ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, "probeDiscovery "
++ "failed, trying default IdP %s", idp);
++ }
++ }
++
++ redirect_url = apr_psprintf(r->pool, "%s%s%s=%s", return_to,
++ strchr(return_to, '?') ? "&" : "?",
++ am_urlencode(r->pool, idp_param),
++ am_urlencode(r->pool, idp));
++
++ apr_table_setn(r->headers_out, "Location", redirect_url);
++
++ return HTTP_SEE_OTHER;
++}
++
++
+ /* This function takes a request for an endpoint and passes it on to the
+ * correct handler function.
+ *
+@@ -2656,6 +2728,8 @@
+ return am_handle_logout(r);
+ } else if(!strcmp(endpoint, "login")) {
+ return am_handle_login(r);
++ } else if(!strcmp(endpoint, "probeDisco")) {
++ return am_handle_probe_discovery(r);
+ } else {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
+ "Endpoint \"%s\" not handled by mod_auth_mellon.",
+Index: auth_mellon.h
+===================================================================
+--- auth_mellon.h (revision 112)
++++ auth_mellon.h (working copy)
+@@ -174,6 +174,8 @@
+
+ /* IdP discovery service */
+ const char *discovery_url;
++ int probe_discovery_timeout;
++ apr_hash_t *probe_discovery_idp;
+
+ /* Mutex to prevent us from creating several lasso server objects. */
+ apr_thread_mutex_t *server_mutex;
+Index: README
+===================================================================
+--- README (revision 112)
++++ README (working copy)
+@@ -321,8 +321,17 @@
+ # The IdP discovery must redirect the user to the return URL,
+ # with retueniDParam set to the selected IdP providerID.
+ #
+- # Alternatively, a simple built-in IdP discovery can be used,
+- # by specifying "builtin:get-metadata?timeout=1"
++ # The builtin:get-metadata discovery URL is not supported anymore
++ # starting with 0.3.1. See MellonProbeDiscoveryTimeout for
++ # a replacement.
++ #
++ # Default: None set.
++ MellonDiscoveryURL "http://www.example.net/idp-discovery"
++
++ # MellonProbeDiscoveryTimeout sets the timeout of the
++ # IdP probe discovery service, which is available on the
++ # probeDisco endoint.
++ #
+ # This will cause the SP to send HTTP GET requests on the
+ # configured IdP PorviderID URL. Theses URL should be used to
+ # publish metadata, though this is not mandatory. If the IdP
+@@ -330,9 +339,17 @@
+ # If the PorviderID URL requires SSL, MellonIdPCAFile is used
+ # as a trusted CA bundle.
+ #
+- # Default: None set.
+- MellonDiscoveryURL "http://www.example.net/idp-discovery"
++ # Default: unset, which means the feature is disabled
++ # MellonProbeDiscoveryTimeout 1
+
++ # MellonProbeDiscoveryIdP can be used to restrict the
++ # list of IdP queried by the IdP probe discovery service.
++ #
++ # Default unset, which means that all configured IdP are
++ # queried.
++ # MellonProbeDiscoveryIdP http://idp1.example.com/saml/metadata
++ # MellonProbeDiscoveryIdP http://idp2.example.net/saml/metadata
++
+ # This option will make the SAML authentication assertion
+ # available in the MELLON_SAML_RESPONSE environement
+ # variable. This assertion holds a verifiable signature
+@@ -476,7 +493,39 @@
+ This will return the user to "https://www.example.org/logged_out.html"
+ after the logout operation has completed.
+
++===========================================================================
++ Probe IdP discovery
++===========================================================================
+
++mod_auth_mellon has an IdP probe discovery service that sends HTTP GET
++to IdP and picks the first that answers. This can be used as a poor
++man's failover setup that redirects to your organisation internal IdP.
++Here is a sample configuration:
++
++ MellonEndpointPath "/saml"
++ (...)
++ MellonDiscoveryUrl "/saml/probeDisco"
++ MellonProbeDiscoveryTimeout 1
++
++The SP will sends HTTP GET to each configured IdP providerId URL until
++it gets an HTTP 200 response within the 1 second timeout. It will then
++proceed with that IdP.
++
++If you are in a federation, then your IdP login page will need to provide
++an IdP selection feature aimed at users from other institutions (after
++such a choice, the user would be redirected to the SP's /saml/login
++endpoint, with ReturnTo and IdP set appropriately). In such a setup,
++you will want to configure external IdP in mod_auth_mellon, but not
++use them for IdP probe discovery. The MellonProbeDiscoveryIdP
++directive can be used to limit the usable IdP for probe discovery:
++
++ MellonEndpointPath "/saml"
++ (...)
++ MellonDiscoveryUrl "/saml/probeDisco"
++ MellonProbeDiscoveryTimeout 1
++ MellonProbeDiscoveryIdP "https://idp1.example.net/saml/metadata"
++ MellonProbeDiscoveryIdP "https://idp2.example.net/saml/metadata"
++
+ ===========================================================================
+ Contributors
+ ===========================================================================
+Index: auth_mellon_config.c
+===================================================================
+--- auth_mellon_config.c (revision 112)
++++ auth_mellon_config.c (working copy)
+@@ -76,6 +76,47 @@
+ */
+ static const int post_count = 100;
+
++/* 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.
++ * This value isn't used by this function.
++ * 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 file
+ * slot in the module configuration. If lasso is recent enough, it
+ * attempts to read the file immediatly.
+@@ -133,10 +174,10 @@
+ * NULL on success or an error string on failure.
+ *
+ */
+-static const char *am_get_proovider_id(apr_pool_t *p,
+- server_rec *s,
+- const char *file,
+- const char **provider)
++static const char *am_get_provider_id(apr_pool_t *p,
++ server_rec *s,
++ const char *file,
++ const char **provider)
+ {
+ char *data;
+ apr_xml_parser *xp;
+@@ -195,7 +236,7 @@
+ * Returns:
+ * NULL on success or an error string on failure.
+ */
+-static const char *ap_set_idp_string_slot(cmd_parms *cmd,
++static const char *am_set_idp_string_slot(cmd_parms *cmd,
+ void *struct_ptr,
+ const char *arg)
+ {
+@@ -205,8 +246,8 @@
+ const char *error;
+ const char *provider_id;
+
+- if ((error = am_get_proovider_id(cmd->pool, s,
+- arg, &provider_id)) != NULL)
++ if ((error = am_get_provider_id(cmd->pool, s,
++ arg, &provider_id)) != NULL)
+ return apr_psprintf(cmd->pool, "%s - %s", cmd->cmd->name, error);
+
+ apr_hash_set(cfg->idp_metadata_files,
+@@ -649,8 +690,8 @@
+ ),
+ AP_INIT_TAKE1(
+ "MellonIdPMetadataFile",
+- ap_set_idp_string_slot,
+- NULL,
++ am_set_idp_string_slot,
++ NULL,
+ OR_AUTHCFG,
+ "Full path to xml metadata file for the IdP."
+ ),
+@@ -705,6 +746,21 @@
+ "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 of IdP probe discovery service. "
++ "Default is 1s"
++ ),
++ AP_INIT_TAKE12(
++ "MellonProbeDiscoveryIdP",
++ am_set_hash_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,
+@@ -760,6 +816,8 @@
+ dir->idp_ca_file = 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_hash_make(p);
+
+ dir->sp_org_name = apr_hash_make(p);
+ dir->sp_org_display_name = apr_hash_make(p);
+@@ -903,6 +961,16 @@
+ 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_hash_copy(p,
++ (apr_hash_count(add_cfg->probe_discovery_idp) > 0) ?
++ add_cfg->probe_discovery_idp :
++ base_cfg->probe_discovery_idp);
++
+ apr_thread_mutex_create(&new_cfg->server_mutex,
+ APR_THREAD_MUTEX_DEFAULT, p);
+ new_cfg->server = NULL;
diff --git a/www/ap2-auth-mellon/patches/patch-af b/www/ap2-auth-mellon/patches/patch-af
new file mode 100644
index 00000000000..59bbf9caefb
--- /dev/null
+++ b/www/ap2-auth-mellon/patches/patch-af
@@ -0,0 +1,65 @@
+$NetBSD: patch-af,v 1.1 2011/04/04 08:46:42 manu Exp $
+
+Make remapped attribute usable for MellonUser
+
+--- auth_mellon_cache.c.orig 2011-02-28 17:12:42.000000000 +0100
++++ auth_mellon_cache.c 2011-02-28 17:59:02.000000000 +0100
+@@ -367,21 +367,8 @@
+ }
+ }
+ }
+
+- if(t->user[0] != '\0') {
+- /* We have a user-"name". Set r->user and r->ap_auth_type. */
+- r->user = apr_pstrdup(r->pool, t->user);
+- r->ap_auth_type = apr_pstrdup(r->pool, "Mellon");
+- } else {
+- /* We don't have a user-"name". Log error. */
+- ap_log_rerror(APLOG_MARK, APLOG_NOTICE, 0, r,
+- "Didn't find the attribute \"%s\" in the attributes"
+- " which were received from the IdP. Cannot set a user"
+- " for this request without a valid user attribute.",
+- d->userattr);
+- }
+-
+ /* Allocate a set of counters for duplicate variables in the list. */
+ counters = apr_hash_make(r->pool);
+
+ /* Populate the subprocess environment with the attributes we
+@@ -398,8 +385,13 @@
+ }
+
+ value = t->env[i].value;
+
++ /*
++ * If we find a variable remapping to MellonUser, use it.
++ */
++ if ((t->user[0] == '\0') && (strcmp(varname, d->userattr) == 0))
++ strcpy(t->user, value);
+
+ /* Find the number of times this variable has been set. */
+ count = apr_hash_get(counters, varname, APR_HASH_KEY_STRING);
+ if(count == NULL) {
+@@ -425,8 +417,22 @@
+ /* Increase the count. */
+ ++(*count);
+ }
+
++ if(t->user[0] != '\0') {
++ /* We have a user-"name". Set r->user and r->ap_auth_type. */
++ r->user = apr_pstrdup(r->pool, t->user);
++ r->ap_auth_type = apr_pstrdup(r->pool, "Mellon");
++ } else {
++ /* We don't have a user-"name". Log error. */
++ ap_log_rerror(APLOG_MARK, APLOG_NOTICE, 0, r,
++ "Didn't find the attribute \"%s\" in the attributes"
++ " which were received from the IdP. Cannot set a user"
++ " for this request without a valid user attribute.",
++ d->userattr);
++ }
++
++
+ /* Populate with the session? */
+ if (d->dump_session) {
+ char *session;
+ int srclen, dstlen;
diff --git a/www/ap2-auth-mellon/patches/patch-ag b/www/ap2-auth-mellon/patches/patch-ag
new file mode 100644
index 00000000000..8790d1f82f8
--- /dev/null
+++ b/www/ap2-auth-mellon/patches/patch-ag
@@ -0,0 +1,149 @@
+$NetBSD: patch-ag,v 1.1 2011/04/04 08:46:42 manu Exp $
+
+Add the MellonIdPMetadataGlob directive (pulled from upstream)
+
+Index: auth_mellon_util.c
+===================================================================
+--- auth_mellon_util.c (revision 116)
++++ auth_mellon_util.c (working copy)
+@@ -838,6 +838,31 @@
+ return ret;
+ }
+
++/* This returns the directroy part of a path, a la dirname(3)
++ *
++ * Parameters:
++ * apr_pool_t p Pool to allocate memory from
++ * const char *path Path to extract directory from
++ *
++ * Returns:
++ * The directory part of path
++ */
++const char *am_filepath_dirname(apr_pool_t *p, const char *path)
++{
++ char *cp;
++
++ /*
++ * Try Unix and then Windows style. Borrowed from
++ * apr_match_glob(), it seems it cannot be made more
++ * portable.
++ */
++ if (((cp = strrchr(path, (int)'/')) == NULL) &&
++ ((cp = strrchr(path, (int)'\\')) == NULL))
++ return ".";
++
++ return apr_pstrndup(p, path, cp - path);
++}
++
+ /*
+ * malloc a buffer and fill it with a given file
+ *
+Index: auth_mellon.h
+===================================================================
+--- auth_mellon.h (revision 116)
++++ auth_mellon.h (working copy)
+@@ -52,6 +52,7 @@
+ #include "apr_file_io.h"
+ #include "apr_xml.h"
+ #include "apr_lib.h"
++#include "apr_fnmatch.h"
+
+ #include "ap_config.h"
+ #include "httpd.h"
+@@ -296,6 +297,7 @@
+ int am_postdir_cleanup(request_rec *s);
+ char *am_htmlencode(request_rec *r, const char *str);
+ int am_save_post(request_rec *r, const char **relay_state);
++const char *am_filepath_dirname(apr_pool_t *p, const char *path);
+ const char *am_strip_cr(request_rec *r, const char *str);
+ const char *am_add_cr(request_rec *r, const char *str);
+ const char *am_xstrtok(request_rec *r, const char *str,
+Index: README
+===================================================================
+--- README (revision 116)
++++ README (working copy)
+@@ -349,6 +349,11 @@
+ # Default: None set.
+ MellonIdPMetadataFile /etc/apache2/mellon/idp-metadata.xml
+
++ # MellonIdPMetadataGlob is a glob(3) pattern enabled alternative
++ # to MellonIdPMetadataFile.
++ # Default: None set.
++ #MellonIdPMetadataGlob /etc/apache2/mellon/*-metadata.xml
++
+ # MellonIdpPublicKeyFile is the full path of the public key of the
+ # IdP. This parameter is optional if the public key is embedded
+ # in the IdP's metadata file, or if a certificate authority is
+Index: auth_mellon_config.c
+===================================================================
+--- auth_mellon_config.c (revision 116)
++++ auth_mellon_config.c (working copy)
+@@ -222,6 +222,54 @@
+ return NULL;
+ }
+
++/* This function handles configuration directives which use
++ * a glob pattern
++ *
++ * 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_glob_fn(cmd_parms *cmd,
++ void *struct_ptr,
++ const char *arg)
++{
++ const char *(*take_argv)(cmd_parms *, void *, 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);
++
++ if (apr_match_glob(arg, &files, cmd->pool) != 0)
++ return take_argv(cmd, struct_ptr, arg);
++
++ 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);
++
++ if (error != NULL)
++ return error;
++ }
++
++ return NULL;
++}
++
+ /* This function handles configuration directives which set an
+ * idp related slot in the module configuration.
+ *
+@@ -872,6 +920,13 @@
+ "Full path to xml metadata file for the IdP."
+ ),
+ AP_INIT_TAKE1(
++ "MellonIdPMetadataGlob",
++ am_set_glob_fn,
++ am_set_idp_string_slot,
++ OR_AUTHCFG,
++ "Full path to xml metadata files for the IdP, with glob(3) patterns."
++ ),
++ AP_INIT_TAKE1(
+ "MellonIdPPublicKeyFile",
+ ap_set_string_slot,
+ (void *)APR_OFFSETOF(am_dir_cfg_rec, idp_public_key_file),