head 1.2; access; symbols pkgsrc-2013Q2:1.2.0.6 pkgsrc-2013Q2-base:1.2 pkgsrc-2012Q4:1.2.0.4 pkgsrc-2012Q4-base:1.2 pkgsrc-2011Q4:1.2.0.2 pkgsrc-2011Q4-base:1.2 pkgsrc-2011Q3:1.1.0.4 pkgsrc-2011Q3-base:1.1 pkgsrc-2011Q2:1.1.0.2 pkgsrc-2011Q2-base:1.1; locks; strict; comment @# @; 1.2 date 2011.12.06.09.58.01; author manu; state dead; branches; next 1.1; 1.1 date 2011.04.04.08.46.42; author manu; state Exp; branches; next ; desc @@ 1.2 log @Update to mod_auth_mellon 0.4.0 plus upstream patch: * Honour MellonProbeDiscoveryIdP order when sending probes * Allow MellonUser variable to be translated through MellonSetEnv * A /mellon/probeDisco endpoint replaces the builtin:get-metadata IdP dicovery URL scheme * New MellonCond directive to enable attribute filtering beyond MellonRequire functionalities. * New MellonIdPMetadataGlob directive to load mulitple IdP metadata using a glob(3) pattern. * Support for running behind reverse proxy. * MellonCookieDomain and MellonCookiePath options to configure cookie settings. * Support for loading federation metadata files. * Several bugfixes. @ text @$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 []' + # + # 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 [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, @@@@ -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) ? @ 1.1 log @Patches from upcoming 0.3.1 @ text @d1 1 a1 1 $NetBSD$ @