/* Licensed to the Apache Software Foundation (ASF) under one or more *contributorlicenseagreements.SeetheNOTICEfiledistributedwith *thisworkforadditionalinformationregardingcopyrightownership. *TheASFlicensesthisfiletoYouundertheApacheLicense,Version2.0 *(the"License");youmaynotusethisfileexceptincompliancewith *theLicense.YoumayobtainacopyoftheLicenseat * *http://www.apache.org/licenses/LICENSE-2.0 * *Unlessrequiredbyapplicablelaworagreedtoinwriting,software *distributedundertheLicenseisdistributedonan"ASIS"BASIS, *WITHOUTWARRANTIESORCONDITIONSOFANYKIND,eitherexpressorimplied. *SeetheLicenseforthespecificlanguagegoverningpermissionsand *limitationsundertheLicense.
*/
typedefstruct {
apr_pool_t *pool; /* Pool that this config is allocated from */ #if APR_HAS_THREADS
apr_thread_mutex_t *lock; /* Lock for this config */ #endif
/* These parameters are all derived from the AuthLDAPURL directive */ char *url; /* String representation of the URL */
char *host; /* Name of the LDAP server (or space separated list) */ int port; /* Port of the LDAP server */ char *basedn; /* Base DN to do all searches from */ char *attribute; /* Attribute to search for */ char **attributes; /* Array of all the attributes to return */ int scope; /* Scope of the search */ char *filter; /* Filter to further limit the search */
deref_options deref; /* how to handle alias dereferening */ char *binddn; /* DN to bind to server (can be NULL) */ char *bindpw; /* Password to bind to server (can be NULL) */ int bind_authoritative; /* If true, will return errors when bind fails */
int user_is_dn; /* If true, r->user is replaced by DN during authn */ char *remote_user_attribute; /* If set, r->user is replaced by this attribute during authn */ int compare_dn_on_server; /* If true, will use server to do DN compare */
int have_ldap_url; /* Set if we have found an LDAP url */
apr_array_header_t *groupattr; /* List of Group attributes identifying user members. Default:"member uniqueMember" */ int group_attrib_is_dn; /* If true, the group attribute is the DN, otherwise,
it's the exact string passed by the HTTP client */ char **sgAttributes; /* Array of strings constructed (post-config) from subgroupattrs. Last entry is NULL. */
apr_array_header_t *subgroupclasses; /* List of object classes of sub-groups. Default:"groupOfNames groupOfUniqueNames" */ int maxNestingDepth; /* Maximum recursive nesting depth permitted during subgroup processing. Default: 10 */
int secure; /* True if SSL connections are requested */ char *authz_prefix; /* Prefix for environment variables added during authz */ int initial_bind_as_user; /* true if we should try to bind (to lookup DN) directly with the basic auth username */
ap_regex_t *bind_regex; /* basic auth -> bind'able username regex */ constchar *bind_subst; /* basic auth -> bind'able username substitution */ int search_as_user; /* true if authz searches should be done with the users credentials (when we did authn) */ int compare_as_user; /* true if authz compares should be done with the users credentials (when we did authn) */
} authn_ldap_config_t;
typedefstruct { constchar *dn; /* The saved dn from a successful search */ constchar *user; /* The username provided by the client */ constchar **vals; /* The additional values pulled during the DN search*/ constchar *password; /* if this module successfully authenticates, the basic auth password, else null */
} authn_ldap_request_t;
/* Convert the user name to UTF-8. This is only valid for LDAP v3 */ if (apr_xlate_conv_buffer(convset, user, &inbytes, outbuf, &outbytes) == APR_SUCCESS) {
user = outbuf;
}
}
/* *BasicsanitychecksbeforeanyLDAPoperationsevenhappen.
*/ if (!sec->have_ldap_url) {
ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, APLOGNO(02558) "no AuthLDAPURL");
return AUTH_GENERAL_ERROR;
}
/* Get the password that the client sent */ if (password == NULL) {
ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01692) "auth_ldap authenticate: no password specified"); return AUTH_GENERAL_ERROR;
}
if (user == NULL) {
ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01693) "auth_ldap authenticate: no user specified"); return AUTH_GENERAL_ERROR;
}
/* There is a good AuthLDAPURL, right? */ if (sec->host) { constchar *binddn = sec->binddn; constchar *bindpw = sec->bindpw; if (sec->initial_bind_as_user) {
bindpw = password;
binddn = ldap_determine_binddn(r, user);
}
/* build the username filter */ if (APR_SUCCESS != authn_ldap_build_filter(filtbuf, r, user, NULL, sec)) {
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(02622) "auth_ldap authenticate: ldap filter too long (>%d): %s",
FILTER_LENGTH, filtbuf);
util_ldap_connection_close(ldc); return AUTH_GENERAL_ERROR;
}
ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, "auth_ldap authenticate: final authn filter is %s", filtbuf);
/* convert password to utf-8 */
utfpassword = authn_ldap_xlate_password(r, password);
/* do the user search */
result = util_ldap_cache_checkuserid(r, ldc, sec->url, sec->basedn, sec->scope,
sec->attributes, filtbuf, utfpassword,
&dn, &(req->vals));
util_ldap_connection_close(ldc);
/* talking to a primitive LDAP server (like RACF-over-LDAP) that doesn't return specific errors */ if (!strcasecmp(sec->filter, "none") && LDAP_OTHER == result) { return AUTH_USER_NOT_FOUND;
}
return (LDAP_NO_SUCH_OBJECT == result) ? AUTH_USER_NOT_FOUND #ifdef LDAP_SECURITY_ERROR
: (LDAP_SECURITY_ERROR(result)) ? AUTH_DENIED #else
: (LDAP_INAPPROPRIATE_AUTH == result) ? AUTH_DENIED
: (LDAP_INVALID_CREDENTIALS == result) ? AUTH_DENIED #ifdef LDAP_INSUFFICIENT_ACCESS
: (LDAP_INSUFFICIENT_ACCESS == result) ? AUTH_DENIED #endif #ifdef LDAP_INSUFFICIENT_RIGHTS
: (LDAP_INSUFFICIENT_RIGHTS == result) ? AUTH_DENIED #endif #endif #ifdef LDAP_CONSTRAINT_VIOLATION /* At least Sun Directory Server sends this if a user is *locked.ThisisnotcoveredbyLDAP_SECURITY_ERROR.
*/
: (LDAP_CONSTRAINT_VIOLATION == result) ? AUTH_DENIED #endif
: AUTH_GENERAL_ERROR;
}
/* mark the user and DN */
req->dn = dn;
req->user = user;
req->password = password; if (sec->user_is_dn) {
r->user = (char *)req->dn;
}
/* sanity check */ if (sec->remote_user_attribute && !remote_user_attribute_set) {
ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, APLOGNO(01696) "auth_ldap authenticate: " "REMOTE_USER was to be set with attribute '%s', " "but this attribute was not requested for in the " "LDAP query for the user. REMOTE_USER will fall " "back to username or DN as appropriate.",
sec->remote_user_attribute);
}
/* Build the username filter */ if (APR_SUCCESS != authn_ldap_build_filter(filtbuf, r, r->user, NULL, sec)) {
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(02623) "auth_ldap authorize: ldap filter too long (>%d): %s",
FILTER_LENGTH, filtbuf); return AUTHZ_DENIED;
}
/* Search for the user DN */
result = util_ldap_cache_getuserdn(r, ldc, sec->url, sec->basedn,
sec->scope, sec->attributes, filtbuf, &dn, &(req->vals));
/* Search failed, log error and return failure */ if(result != LDAP_SUCCESS) {
ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01701) "auth_ldap authorise: User DN not found, %s", ldc->reason); return AUTHZ_DENIED;
}
req = (authn_ldap_request_t *)apr_pcalloc(r->pool, sizeof(authn_ldap_request_t)); /* Build the username filter */ if (APR_SUCCESS != authn_ldap_build_filter(filtbuf, r, r->user, NULL, sec)) {
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(02624) "auth_ldap authorize: ldap filter too long (>%d): %s",
FILTER_LENGTH, filtbuf); return AUTHZ_DENIED;
}
/* Search for the user DN */
result = util_ldap_cache_getuserdn(r, ldc, sec->url, sec->basedn,
sec->scope, sec->attributes, filtbuf, &dn, &(req->vals));
/* Search failed, log error and return failure */ if(result != LDAP_SUCCESS) {
ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01711) "auth_ldap authorise: User DN not found, %s", ldc->reason); return AUTHZ_DENIED;
}
ent = (struct mod_auth_ldap_groupattr_entry_t *) sec->groupattr->elts;
if (sec->group_attrib_is_dn) { if (req->dn == NULL || strlen(req->dn) == 0) {
ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01712) "auth_ldap authorize: require group: user's DN has " "not been defined; failing authorization for user %s",
r->user); return AUTHZ_DENIED;
}
} else { if (req->user == NULL || strlen(req->user) == 0) { /* We weren't called in the authentication phase, so we didn't have a
* chance to set the user field. Do so now. */
req->user = r->user;
}
}
for (i = 0; i < sec->groupattr->nelts; i++) { /* nested groups need searches and compares, so grab a new handle */
authnz_ldap_cleanup_connection_close(ldc);
apr_pool_cleanup_kill(r->pool, ldc,authnz_ldap_cleanup_connection_close);
req = (authn_ldap_request_t *)apr_pcalloc(r->pool, sizeof(authn_ldap_request_t)); /* Build the username filter */ if (APR_SUCCESS != authn_ldap_build_filter(filtbuf, r, r->user, NULL, sec)) {
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(02625) "auth_ldap authorize: ldap filter too long (>%d): %s",
FILTER_LENGTH, filtbuf); return AUTHZ_DENIED;
}
/* Search for the user DN */
result = util_ldap_cache_getuserdn(r, ldc, sec->url, sec->basedn,
sec->scope, sec->attributes, filtbuf, &dn, &(req->vals));
/* Search failed, log error and return failure */ if(result != LDAP_SUCCESS) {
ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01724) "auth_ldap authorise: User DN not found with filter %s: %s", filtbuf, ldc->reason); return AUTHZ_DENIED;
}
req = (authn_ldap_request_t *)apr_pcalloc(r->pool, sizeof(authn_ldap_request_t)); /* Build the username filter */ if (APR_SUCCESS != authn_ldap_build_filter(filtbuf, r, r->user, NULL, sec)) {
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(02626) "auth_ldap authorize: ldap filter too long (>%d): %s",
FILTER_LENGTH, filtbuf); return AUTHZ_DENIED;
}
/* Search for the user DN */
result = util_ldap_cache_getuserdn(r, ldc, sec->url, sec->basedn,
sec->scope, sec->attributes, filtbuf, &dn, &(req->vals));
/* Search failed, log error and return failure */ if(result != LDAP_SUCCESS) {
ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01732) "auth_ldap authorise: User DN not found with filter %s: %s", filtbuf, ldc->reason); return AUTHZ_DENIED;
}
req = (authn_ldap_request_t *)apr_pcalloc(r->pool, sizeof(authn_ldap_request_t)); /* Build the username filter */ if (APR_SUCCESS != authn_ldap_build_filter(filtbuf, r, r->user, NULL, sec)) {
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(02627) "auth_ldap authorize: ldap filter too long (>%d): %s",
FILTER_LENGTH, filtbuf); return AUTHZ_DENIED;
}
/* Search for the user DN */
result = util_ldap_cache_getuserdn(r, ldc, sec->url, sec->basedn,
sec->scope, sec->attributes, filtbuf, &dn, &(req->vals));
/* Search failed, log error and return failure */ if(result != LDAP_SUCCESS) {
ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01741) "auth_ldap authorise: User DN not found with filter %s: %s", filtbuf, ldc->reason); return AUTHZ_DENIED;
}
/* Build the username filter */ if (APR_SUCCESS != authn_ldap_build_filter(filtbuf, r, req->user, t, sec)) {
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(02628) "auth_ldap authorize: ldap filter too long (>%d): %s",
FILTER_LENGTH, filtbuf); return AUTHZ_DENIED;
}
/* Search for the user DN */
result = util_ldap_cache_getuserdn(r, ldc, sec->url, sec->basedn,
sec->scope, sec->attributes, filtbuf, &dn, &(req->vals));
/* Make sure that the filtered search returned the correct user dn */ if (result == LDAP_SUCCESS) {
ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01744) "auth_ldap authorize: checking dn match %s", dn); if (sec->compare_as_user) { /* ldap-filter is the only authz that requires a search and a compare */
apr_pool_cleanup_kill(r->pool, ldc, authnz_ldap_cleanup_connection_close);
authnz_ldap_cleanup_connection_close(ldc);
ldc = get_connection_for_authz(r, LDAP_COMPARE);
}
result = util_ldap_cache_comparedn(r, ldc, sec->url, req->dn, dn,
sec->compare_dn_on_server);
}
if ((arglen > 5) && strncmp(arg, "exec:", 5) == 0) { if (apr_tokenize_to_argv(arg+5, &argv, cmd->temp_pool) != APR_SUCCESS) { return apr_pstrcat(cmd->pool, "Unable to parse exec arguments from ",
arg+5, NULL);
}
argv[0] = ap_server_root_relative(cmd->temp_pool, argv[0]);
if (!argv[0]) { return apr_pstrcat(cmd->pool, "Invalid AuthLDAPBindPassword exec location:",
arg+5, NULL);
}
result = ap_get_exec_line(cmd->pool,
(constchar*)argv[0], (constchar * const *)argv);
if (!result) { return apr_pstrcat(cmd->pool, "Unable to get bind password from exec of ",
arg+5, NULL);
}
sec->bindpw = result;
} else {
sec->bindpw = (char *)arg;
}
if (!(*sec->bindpw)) { return"Empty passwords are invalid for AuthLDAPBindPassword";
}
return NULL;
}
staticconst command_rec authnz_ldap_cmds[] =
{
AP_INIT_TAKE12("AuthLDAPURL", mod_auth_ldap_parse_url, NULL, OR_AUTHCFG, "URL to define LDAP connection. This should be an RFC 2255 compliant\n" "URL of the form ldap://host[:port]/basedn[?attrib[?scope[?filter]]].\n" "<ul>\n" "<li>Host is the name of the LDAP server. Use a space separated list of hosts \n" "to specify redundant servers.\n" "<li>Port is optional, and specifies the port to connect to.\n" "<li>basedn specifies the base DN to start searches from\n" "<li>Attrib specifies what attribute to search for in the directory. If not " "provided, it defaults to <b>uid</b>.\n" "<li>Scope is the scope of the search, and can be either <b>sub</b> or " "<b>one</b>. If not provided, the default is <b>sub</b>.\n" "<li>Filter is a filter to use in the search. If not provided, " "defaults to <b>(objectClass=*)</b>.\n" "</ul>\n" "Searches are performed using the attribute and the filter combined. " "For example, assume that the\n" "LDAP URL is <b>ldap://ldap.airius.com/ou=People, o=Airius?uid?sub?(posixid=*)</b>. " "Searches will\n" "be done using the filter <b>(&((posixid=*))(uid=<i>username</i>))</b>, " "where <i>username</i>\n" "is the user name passed by the HTTP client. The search will be a subtree " "search on the branch <b>ou=People, o=Airius</b>."),
AP_INIT_TAKE1("AuthLDAPBindDN", ap_set_string_slot,
(void *)APR_OFFSETOF(authn_ldap_config_t, binddn), OR_AUTHCFG, "DN to use to bind to LDAP server. If not provided, will do an anonymous bind."),
AP_INIT_TAKE1("AuthLDAPBindPassword", set_bind_password, NULL, OR_AUTHCFG, "Password to use to bind to LDAP server. If not provided, will do an anonymous bind."),
AP_INIT_FLAG("AuthLDAPBindAuthoritative", ap_set_flag_slot,
(void *)APR_OFFSETOF(authn_ldap_config_t, bind_authoritative), OR_AUTHCFG, "Set to 'on' to return failures when user-specific bind fails - defaults to on."),
AP_INIT_FLAG("AuthLDAPRemoteUserIsDN", ap_set_flag_slot,
(void *)APR_OFFSETOF(authn_ldap_config_t, user_is_dn), OR_AUTHCFG, "Set to 'on' to set the REMOTE_USER environment variable to be the full " "DN of the remote user. By default, this is set to off, meaning that " "the REMOTE_USER variable will contain whatever value the remote user sent."),
AP_INIT_TAKE1("AuthLDAPRemoteUserAttribute", ap_set_string_slot,
(void *)APR_OFFSETOF(authn_ldap_config_t, remote_user_attribute), OR_AUTHCFG, "Override the user supplied username and place the " "contents of this attribute in the REMOTE_USER " "environment variable."),
AP_INIT_FLAG("AuthLDAPCompareDNOnServer", ap_set_flag_slot,
(void *)APR_OFFSETOF(authn_ldap_config_t, compare_dn_on_server), OR_AUTHCFG, "Set to 'on' to force auth_ldap to do DN compares (for the \"require dn\" " "directive) using the server, and set it 'off' to do the compares locally " "(at the expense of possible false matches). See the documentation for " "a complete description of this option."),
AP_INIT_ITERATE("AuthLDAPSubGroupAttribute", mod_auth_ldap_add_subgroup_attribute, NULL, OR_AUTHCFG, "Attribute labels used to define sub-group (or nested group) membership in groups - " "defaults to member and uniqueMember"),
AP_INIT_ITERATE("AuthLDAPSubGroupClass", mod_auth_ldap_add_subgroup_class, NULL, OR_AUTHCFG, "LDAP objectClass values used to identify sub-group instances - " "defaults to groupOfNames and groupOfUniqueNames"),
AP_INIT_TAKE1("AuthLDAPMaxSubGroupDepth", mod_auth_ldap_set_subgroup_maxdepth, NULL, OR_AUTHCFG, "Maximum subgroup nesting depth to be evaluated - defaults to 10 (top-level group = 0)"),
AP_INIT_ITERATE("AuthLDAPGroupAttribute", mod_auth_ldap_add_group_attribute, NULL, OR_AUTHCFG, "A list of attribute labels used to identify the user members of groups - defaults to " "member and uniquemember"),
AP_INIT_FLAG("AuthLDAPGroupAttributeIsDN", ap_set_flag_slot,
(void *)APR_OFFSETOF(authn_ldap_config_t, group_attrib_is_dn), OR_AUTHCFG, "If set to 'on', auth_ldap uses the DN that is retrieved from the server for " "subsequent group comparisons. If set to 'off', auth_ldap uses the string " "provided by the client directly. Defaults to 'on'."),
AP_INIT_TAKE1("AuthLDAPDereferenceAliases", mod_auth_ldap_set_deref, NULL, OR_AUTHCFG, "Determines how aliases are handled during a search. Can be one of the " "values \"never\", \"searching\", \"finding\", or \"always\". " "Defaults to always."),
AP_INIT_TAKE1("AuthLDAPCharsetConfig", set_charset_config, NULL, RSRC_CONF, "Character set conversion configuration file. If omitted, character set " "conversion is disabled."),
AP_INIT_TAKE1("AuthLDAPAuthorizePrefix", ap_set_string_slot,
(void *)APR_OFFSETOF(authn_ldap_config_t, authz_prefix), OR_AUTHCFG, "The prefix to add to environment variables set during " "successful authorization, default '" AUTHZ_PREFIX "'"),
AP_INIT_FLAG("AuthLDAPInitialBindAsUser", ap_set_flag_slot,
(void *)APR_OFFSETOF(authn_ldap_config_t, initial_bind_as_user), OR_AUTHCFG, "Set to 'on' to perform the initial DN lookup with the basic auth credentials " "instead of anonymous or hard-coded credentials"),
AP_INIT_TAKE2("AuthLDAPInitialBindPattern", set_bind_pattern, NULL, OR_AUTHCFG, "The regex and substitution to determine a username that can bind based on an HTTP basic auth username"),
AP_INIT_FLAG("AuthLDAPSearchAsUser", ap_set_flag_slot,
(void *)APR_OFFSETOF(authn_ldap_config_t, search_as_user), OR_AUTHCFG, "Set to 'on' to perform authorization-based searches with the users credentials, when this module " "has also performed authentication. Does not affect nested groups lookup."),
AP_INIT_FLAG("AuthLDAPCompareAsUser", ap_set_flag_slot,
(void *)APR_OFFSETOF(authn_ldap_config_t, compare_as_user), OR_AUTHCFG, "Set to 'on' to perform authorization-based compares with the users credentials, when this module " "has also performed authentication. Does not affect nested groups lookups."),
{NULL}
};
if(sec->secure) { if(!util_ldap_ssl_supported(s)) { ap_log_error(APLOG_MARK,APLOG_CRIT,0,s,APLOGNO(03159) "LDAP:SSLconnections(ldaps://) not supported by utilLDAP"); return(!OK); } }
*/
/* make sure that mod_ldap (util_ldap) is loaded */ if (ap_find_linked_module("util_ldap.c") == NULL) {
ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, APLOGNO(01749) "Module mod_ldap missing. Mod_ldap (aka. util_ldap) " "must be loaded in order for mod_authnz_ldap to function properly"); return HTTP_INTERNAL_SERVER_ERROR;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.