Commit af7102af authored by Bradley Nicholes's avatar Bradley Nicholes
Browse files

Add the directive AuthLDAPAllowDNAuth to allow a user to authenticate against...

Add the directive AuthLDAPAllowDNAuth to allow a user to authenticate against an LDAP directory using a full user DN.  This directive allows a user to authenticate against a subcontext that may contain non-unique user IDs.

git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@168016 13f79535-47bb-0310-9956-ffa450edef68
parent f4e1a8a7
Loading
Loading
Loading
Loading
+40 −5
Original line number Diff line number Diff line
@@ -819,6 +819,38 @@ environment variable</description>
    the username that was passed by the client. It is turned off by
    default.</p>
</usage>
<seealso><directive module="mod_authnz_ldap">AuthLDAPAllowDNAuth</directive></seealso>
</directivesynopsis>

<directivesynopsis>
<name>AuthLDAPAllowDNAuth</name>
<description>Allow the user to authenticate by passing a fully distinguished
user name.</description>
<syntax>AuthLDAPAllowDNAuth on|off</syntax>
<default>AuthLDAPAllowDNAuth off</default>
<contextlist><context>directory</context><context>.htaccess</context>
</contextlist>
<override>AuthConfig</override>

<usage>
    <p>If this directive is set to ON, users are allowed to pass a fully
    distinguished user name as the user ID.  Regardless of this setting,
    Auth_LDAP will still allow a contextless login. This directive is 
    turned off by default.</p>

    <note><title>Note</title>
      <p>If a full user DN is allowed for authentication and the value of
      <directive module="mod_authnz_ldap">AuthLDAPRemoteUserIsDN</directive> 
      is set to OFF, the value of the REMOTE_USER environment variable
      will contain the actual user name value passed in the request.  If 
      this directive is set to ON, the REMOTE_USER environment variable 
      will always be set to the user DN retrieved from the LDAP directory. 
      If a contextless user ID is required in all cases instead of a 
      full DN, it is possible to retrieve the desired attribute value 
      from the user object by specifying an attribute list in the 
      <directive module="mod_authnz_ldap">AuthLDAPUrl</directive> directive.</p>
    </note>
</usage>
</directivesynopsis>

<directivesynopsis>
@@ -874,13 +906,16 @@ environment variable</description>

<dt>attribute</dt>

        <dd>The attribute to search for.
        <dd>The attribute to search for as well as additional attribute
        values to extract from the authenticated user object.
        Although RFC 2255 allows a comma-separated list of
        attributes, only the first attribute will be used, no
        matter how many are provided. If no attributes are
        provided, the default is to use <code>uid</code>. It's a good
        idea to choose an attribute that will be unique across all
        entries in the subtree you will be using.</dd>
        matter how many are provided. The values of all other listed 
        attributes will be extracted from the user object and assigned
        to environment variables (AUTHENTICATE_&lt;Attribute&gt;=value). 
        If no attributes are provided, the default is to use <code>uid</code>. 
        It's a good idea to choose an attribute that will be unique across 
        all entries in the subtree you will be searching.</dd>

<dt>scope</dt>

+102 −53
Original line number Diff line number Diff line
@@ -73,6 +73,7 @@ typedef struct {
                                        it's the exact string passed by the HTTP client */

    int secure;                     /* True if SSL connections are requested */
    int allow_fdn_auth;             /* Set to True if user is allowed to authenticate using an FDN */
} authn_ldap_config_t;

typedef struct {
@@ -150,6 +151,61 @@ static apr_xlate_t* get_conv_set (request_rec *r)
    return NULL;
}

#define FILTER_LENGTH MAX_STRING_LEN
static char* cat_escape_dn_element(char *filtbuf, char *user)
{
    char *p, *q, *filtbuf_end;

    /* 
     * Now add the client-supplied username to the filter, ensuring that any
     * LDAP filter metachars are escaped.
     */
    filtbuf_end = filtbuf + FILTER_LENGTH - 1;
#if APR_HAS_MICROSOFT_LDAPSDK
    for (p = user, q=filtbuf + strlen(filtbuf);
         *p && q < filtbuf_end; ) {
        if (strchr("*()\\", *p) != NULL) {
            if ( q + 3 >= filtbuf_end)
              break;  /* Don't write part of escape sequence if we can't write all of it */
            *q++ = '\\';
            switch ( *p++ )
            {
              case '*':
                *q++ = '2';
                *q++ = 'a';
                break;
              case '(':
                *q++ = '2';
                *q++ = '8';
                break;
              case ')':
                *q++ = '2';
                *q++ = '9';
                break;
              case '\\':
                *q++ = '5';
                *q++ = 'c';
                break;
		        }
        }
        else
            *q++ = *p++;
    }
#else
    for (p = user, q=filtbuf + strlen(filtbuf);
         *p && q < filtbuf_end; *q++ = *p++) {
        if (strchr("*()\\", *p) != NULL) {
            *q++ = '\\';
            if (q >= filtbuf_end) {
              break;
            }
        }
    }
#endif
    *q = '\0';

    return filtbuf;
}

/*
 * Build the search filter, or at least as much of the search filter that
@@ -168,20 +224,23 @@ static apr_xlate_t* get_conv_set (request_rec *r)
 *
 * Further, assume that the userid passed by the client was `userj'.  The
 * search filter will be (&(posixid=*)(uid=userj)).
 *
 * If a full DN is allowed for authentication, the a userid of 
 * cn=userj,o=dev will result in the following search filter:
 *  (&(objectclass=*)(&(cn:dn:=userj)(o:dn:=dev)))
 */
#define FILTER_LENGTH MAX_STRING_LEN
static void authn_ldap_build_filter(char *filtbuf, 
                             request_rec *r,
                             const char* sent_user,
                             const char* sent_filter,
                             authn_ldap_config_t *sec)
{
    char *p, *q, *filtbuf_end;
    char *user, *filter;
    apr_xlate_t *convset = NULL;
    apr_size_t inbytes;
    apr_size_t outbytes;
    char *outbuf;
	char **dnbuf = NULL;

    if (sent_user != NULL) {
        user = apr_pstrdup (r->pool, sent_user);
@@ -210,66 +269,50 @@ static void authn_ldap_build_filter(char *filtbuf,
        }
    }

    /* 
     * Create the first part of the filter, which consists of the 
     * config-supplied portions.
     */
    apr_snprintf(filtbuf, FILTER_LENGTH, "(&(%s)(%s=", filter, sec->attribute);
    apr_snprintf(filtbuf, FILTER_LENGTH, "(&(%s)(&", filter);

    /* 
     * Now add the client-supplied username to the filter, ensuring that any
     * LDAP filter metachars are escaped.
     */
    filtbuf_end = filtbuf + FILTER_LENGTH - 1;
#if APR_HAS_MICROSOFT_LDAPSDK
    for (p = user, q=filtbuf + strlen(filtbuf);
         *p && q < filtbuf_end; ) {
        if (strchr("*()\\", *p) != NULL) {
            if ( q + 3 >= filtbuf_end)
              break;  /* Don't write part of escape sequence if we can't write all of it */
            *q++ = '\\';
            switch ( *p++ )
            {
              case '*':
                *q++ = '2';
                *q++ = 'a';
                break;
              case '(':
                *q++ = '2';
                *q++ = '8';
                break;
              case ')':
                *q++ = '2';
                *q++ = '9';
                break;
              case '\\':
                *q++ = '5';
                *q++ = 'c';
                break;
    /* If FDN authentication is not allowed then don't attempt to explode
        the user ID.  The result is that dnbuf well be NULL which will
        bypass building a FDN unique filter. */
    if (sec->allow_fdn_auth) {
        dnbuf = ldap_explode_dn (user, 0); 
    }

	if (dnbuf && (strchr(*dnbuf, '='))) {
		char **buf;
        char *dnfilter = "&";
		for (buf = dnbuf; *buf; buf++) {
            char *eq = strchr (*buf, '=');
            char *newdn = *buf;

            if (eq) {
                newdn = apr_pstrcat (r->pool, apr_pstrndup (r->pool, *buf, eq-(*buf)),
                                     ":dn:", eq, NULL);
            }
        else
            *q++ = *p++;
    }
#else
    for (p = user, q=filtbuf + strlen(filtbuf);
         *p && q < filtbuf_end; *q++ = *p++) {
        if (strchr("*()\\", *p) != NULL) {
            *q++ = '\\';
            if (q >= filtbuf_end) {
              break;

            apr_snprintf(filtbuf, FILTER_LENGTH, "%s(", filtbuf);
            cat_escape_dn_element(filtbuf, newdn);
            apr_snprintf(filtbuf, FILTER_LENGTH, "%s)", filtbuf);
		}
	}
    else {
        /* 
         * Create the first part of the filter, which consists of the 
         * config-supplied portions.
         */
        apr_snprintf(filtbuf, FILTER_LENGTH, "(&(%s)(%s=", filter, sec->attribute);
        cat_escape_dn_element(filtbuf, user);
    }
#endif
    *q = '\0';
    ldap_value_free (dnbuf); 

    /* 
     * Append the closing parens of the filter, unless doing so would 
     * overrun the buffer.
     */
    if (q + 2 <= filtbuf_end)
        strcat(filtbuf, "))");
    apr_snprintf(filtbuf, FILTER_LENGTH, "%s))", filtbuf);

    ap_log_rerror(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, r,
                  "[%d] auth_ldap authenticate: using filter %s", getpid(), filtbuf);
}

static void *create_authnz_ldap_dir_config(apr_pool_t *p, char *d)
@@ -295,6 +338,7 @@ static void *create_authnz_ldap_dir_config(apr_pool_t *p, char *d)
    sec->deref = always;
    sec->group_attrib_is_dn = 1;
    sec->auth_authoritative = 1;
    sec->allow_fdn_auth = 0;

/*
    sec->frontpage_hack = 0;
@@ -1053,6 +1097,11 @@ static const command_rec authnz_ldap_cmds[] =
                  "Character set conversion configuration file. If omitted, character set"
                  "conversion is disabled."),

    AP_INIT_FLAG("AuthLDAPAllowDNAuth", ap_set_flag_slot,
                 (void *)APR_OFFSETOF(authn_ldap_config_t, allow_fdn_auth), OR_AUTHCFG,
                 "If set to 'on', auth_ldap allows the user to authenicate using a fully" 
                 "distinguished user name. Defaults to 'off'."),

    {NULL}
};