diff options
author | manu <manu@pkgsrc.org> | 2011-04-04 08:46:42 +0000 |
---|---|---|
committer | manu <manu@pkgsrc.org> | 2011-04-04 08:46:42 +0000 |
commit | 75fee338a6df8ae364866e846b71448eadcd479e (patch) | |
tree | 5e69d668a1c1957c823731aa32db7285a41edaf3 /www/ap2-auth-mellon | |
parent | cf8b7a7ded4c14d6a7f47cea5861632079a5b4a5 (diff) | |
download | pkgsrc-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-ac | 515 | ||||
-rw-r--r-- | www/ap2-auth-mellon/patches/patch-ae | 523 | ||||
-rw-r--r-- | www/ap2-auth-mellon/patches/patch-af | 65 | ||||
-rw-r--r-- | www/ap2-auth-mellon/patches/patch-ag | 149 |
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), |