LISTSERV at Work

LISTSERV Tech Tip

Q: How can I troubleshoot LDAP connections for my LISTSERV installation?

In the past, we've covered the integration of LISTSERV with LDAP data sources like OpenLDAP and Active Directory. While assisting customers with technical support for LISTSERV's LDAP configuration, we've found that most problems arise from customers either not knowing what settings are required to connect to their LDAP server or not knowing how to translate those settings to LISTSERV site configuration settings. In this tech tip, we'll look at how to test your LDAP connection using standard Windows and Unix LDAP tools, and how to apply those settings to LISTSERV once you've made a successful connection.

LDP.EXE On Windows

Regardless of whether your LDAP server is Active Directory on Windows or OpenLDAP on Unix, if your LISTSERV installation is on Windows, you can use the standard Windows LDP.EXE tool to debug connections to your LDAP server. The screen captures below are of LDP.EXE on Windows Server 2008, but the tool works the same way on other Windows versions. To install LDP.EXE on Windows Server 2008, open the Server Manager, and under Roles, install Active Directory Lightweight Directory Services. This will include, among other tools, LDP.EXE:

For other Windows versions, the installation of LDP.EXE may be different, but the tool is available on all Windows platforms. Check the Microsoft TechNet for details for your Windows version.

You can launch LDP.EXE either from the Server Manager or from the Windows command line. From LDP.EXE, you'll first want to connect to the LDAP server. From the Connection menu, select Connect. The connection screen will prompt for the LDAP server hostname and port number:

The Server can be a fully qualified domain name (LDAP.EXAMPLE.ORG), a short hostname (LDAP), or an IP address (192.168.0.123). The default LDAP port is 389. The default port for LDAP over SSL is 636. If you have an Active Directory server and want to search the Global Catalog, you can use port 3268. Click OK, and verify that the connection succeeds. If it fails, check the hostname, port number, SSL certificate chain, etc., until the connection succeeds. Once you can successfully connect, you can input the hostname and port number into the LDAP_SERVER setting in LISTSERV (for example, LDAP.EXAMPLE.ORG:636).

The next step is to test authentication. From the Connection menu, select Bind:

For the Bind type, select Simple bind. This is the only Bind type that LISTSERV supports. Supply the user name and password for an account authorized to search the LDAP directory. For Active Directory, the user name may be in the form of a Windows domain login (DOMAIN\user). For other LDAP servers, it may expect the user name to be a Distinguished Name/DN (for example, CN=Foobar,CN=Users,DC=EXAMPLE,DC=EDU). Provide the password, and click OK. You should see a message like "Authenticated as: 'DOMAIN\user'". If not, then troubleshoot the user name and the password until you can authenticate successfully. Enter the username and password in LISTSERV's LDAP_UID and LDAP_AUTH settings respectively.

Once connected and authenticated, we should be able to search the directory. From the Browse menu, select Search:

This is where we'll supply a search base and filter. LISTSERV always does a Subtree search, so under Scope, select Subtree. For the Base DN, supply the search base. This will typically be something like CN=Users,DC=DOMAIN,DC=EXAMPLE,DC=EDU. The actual search base depends on your LDAP directory configuration – check with your directory administrator if you're not sure. For the Filter, you'll enter your search filter criteria. If you're configuring LISTSERV for LDAP authentication, the search Filter should be some variation on the email address – either the directory attribute containing the full email address, or the attribute containing the user name portion of the email address. For example, we might enter (mailNickName=user) to search the mailNickName field of the directory for 'user', or (mail=user@example.edu) to search the mail field of the directory for user@example.edu. In the Attributes box, enter the name of the directory attribute that contains the email address. For Active Directory, this is usually just 'mail'.

If you're configuring LISTSERV for LDAP authentication, the search Filter should return one (and only one) user record. Click Run, and make sure that the result is a single user:

If the search fails or returns multiple user records, modify the Base DN and/or the Filter until the result is a single user record with email address.

Once the search succeeds, if you want to configure LISTSERV for LDAP authentication, you can enter the Base DN in LISTSERV's LDAP_PW_BASE setting, and the Filter in LISTSERV's LDAP_PW_FILTER setting. In LDAP_PW_FILTER, you'll enter a placeholder that LISTSERV will populate when it runs the query to authenticate the user. Use %s for the user's full email address, %u for the user name portion of the email address, and/or %h for the hostname portion of the email address. So our sample filter becomes (mailNickName=%u), such that if the user logs in to LISTSERV as user@example.edu, the filter will search the mailNickName field of the directory for 'user'. It is not typically necessary to set LDAP_PW_BIND – the default setting is %n, which binds with the user's full DN, and works with most LDAP directory servers, including Active Directory.

If the tests above fail, alter your Base DN and/or Filter until you get the expected results. At that point, you're ready to enter settings into LISTSERV's site configuration.

If you're configuring LISTSERV for LDAP-based Dynamic Query Lists (DQL), you should use LDP.EXE to test using a Filter like (Department=Sales) to return a set of users that will correspond to your list membership. You'll also need to know which directory attribute contains the email address (and optionally full name) for the users. In our example above, our email attribute is called 'mail', and our full name attribute is called 'displayName'. Make sure that the result set from the query matches the list of recipients to which you expect to deliver mail. Then in your LISTSERV configuration, you could create a Dynamic Query something like:

DYN_QUERY_SAMPLE= BASE=CN=Users,DC=EXAMPLE,DC=EDU FILTER= '(Department=Sales)' EMAIL=mail NAME=displayName

Ldapsearch on Unix

If you're running LISTSERV on Unix and have OpenLDAP installed, the best tool for testing LDAP connectivity is the ldapsearch command. A sample ldapsearch command (as of OpenLDAP 2.3) might look as follows; other OpenLDAP versions may require different syntax:

ldapsearch -x -H ldaps://ldap.example.edu -D 'cn=admin,cn=Users,dc=domain,dc=example,dc=edu' -w 'password' -b dc=domain,dc=example,dc=edu -s sub 'mailnickname=user'

In the sample above, -H is the URI of the LDAP server. It may begin with ldap:// or ldaps:// and may optionally have a port number appended to it. The URI will go into LISTSERV's LDAP_SERVER setting.

The D flag is the DN of the account with which we're authenticating ('cn=admin,cn=Users,dc=domain,dc=example,dc=edu'). Note that if the DN contains spaces, you may need to pass escape characters to your shell for the command to succeed. This will become the LDAP_UID setting in LISTSERV.

The w flag is the password (which also may need escape characters if it contains spaces or characters that are otherwise interpreted by your shell). This will become the LDAP_AUTH setting in LISTSERV.

The b flag is the Base to use for the search (dc=domain,dc=example,dc=edu). If you're configuring LISTSERV for authentication, this will become the LDAP_PW_BASE setting. If you're configuring LISTSERV for dynamic queries, it will be used as the BASE in the dynamic query.

The s flag should always be 'sub' for a subtree search. This is the only type of search used by LISTSERV.

The final argument is the search filter ('mailnickname=user'). To search for a single account for authentication purposes, the filter should return a single user record. This will become the LDAP_PW_FILTER setting in LISTSERV.

For testing dynamic query lists (DQL), the filter should return all of those email addresses that should be list recipients. It will become the FILTER part of the dynamic query:

DYN_QUERY_SAMPLE= BASE=CN=Users,DC=EXAMPLE,DC=EDU FILTER= '(Department=Sales)' EMAIL=mail NAME=displayName

If the ldapsearch command fails, pay attention to the output and modify the LDAP search parameters as needed. Once the ldapsearch command works, only then should you apply the settings to LISTSERV.

LDAP debugging in LISTSERV

If you were able to successfully test LDAP authentication and/or dynamic queries with LDP.EXE or ldapsearch but the LDAP functions still aren't working in LISTSERV, you can enable LDAP debugging in your LISTSERV log file. Send the following command to LISTSERV (either via email or the web interface) to enable tracing of LDAP calls:

DEBUG FLAGS +TRACE_LDAP_CALLS

The response should look something like this:

Debug flags for this session: 00000080

00000001 TRACE_DIST       [Trace DISTRIBUTE processing - OFF]
00000002 TRACE_MIME       [Trace MIME parser - OFF]
00000004 TRACE_LISTS      [Trace (a few) list-related functions - OFF]
00000008 TRACE_SPAM       [Trace spam filter calls - OFF]
00000010 TRACE_EMM        [Trace Embedded Mail-Merge processor - OFF]
00000020 TRACE_DEV        [Temporary ad hoc tracing for development use - OFF]
00000040 TRACE_FSAV       [Trace FSAV calls - OFF]
00000080 TRACE_LDAP_CALLS  Trace LDAP library calls
00000100 TRACE_LDAP_DATA  [Trace data obtained from LDAP - OFF]
08000000 HOLD_DISTBG      [Do not process background DISTRIBUTE jobs - OFF]
10000000 HOLD_XB64        [Hold X-B64 jobs instead of processing them - OFF]
20000000 KEEP_JOBFILES    [Keep successfully processed job files - OFF]
40000000 TRACE_TCPGUI     [Additional TCPGUI tracing - OFF]

This will enable debugging of LDAP calls in the LISTSERV log. The debug log might look like this:

13 May 2014 12:32:38 From [ANONYMOUS]: X-LOGIN user@example.edu fe80:0:0:0:2c58:48f0:2a73:1d52%11 PW=[redacted]
LDAP> ldap_init("ldap.example.edu", 389) -> SUCCESS
LDAP> ldap_set_option(ld, 17, =3) -> SUCCESS
LDAP> ldap_bind_s(ld, "cn=admin,cn=Users,dc=domain,dc=example,dc=edu", "XXXXXXXX", 128) -> SUCCESS
LDAP> ldap_search_s(ld, "dc=domain,dc=example,dc=edu", 2, "mailNickName=user", attrs, 0, &result) -> SUCCESS
LDAP> + attrs[0] = "1.1"
LDAP> ldap_first_entry(ld, 0x8BA918) -> SUCCESS
LDAP> ldap_get_dn(ld, 0x10CE6B0) -> SUCCESS
LDAP> : DN="CN=Sample User,CN=Users,DC=EXAMPLE,DC=EDU"
LDAP> ldap_next_entry(ld, 0x8BA928) -> EOF
LDAP> ldap_unbind(ld) -> SUCCESS
LDAP> ldap_init("ldap.example.edu", 389) -> SUCCESS
LDAP> ldap_set_option(ld, 17, =3) -> SUCCESS
LDAP> ldap_bind_s(ld, "CN=Sample User,CN=Users,DC=EXAMPLE,DC=EDU", "XXXXXXXX", 128) -> status=49
LDAP> ldap_unbind(ld) -> SUCCESS
13 May 2014 12:32:38 To [ANONYMOUS]: ***BADPW***

In the debug output above, we see user@example.edu try to log in to LISTSERV. LISTSERV successfully connects to LDAP.EXAMPLE.EDU and binds with the "cn=admin,cn=Users,dc=domain,dc=example,dc=edu" account. It runs the search with the LDAP_PW_BASE of dc=domain,dc=example,dc=edu and the filter mailNickName=user. The directory returns the user's DN (CN=Sample User,CN=Users,DC=EXAMPLE,DC=EDU). LISTSERV then connects to LDAP.EXAMPLE.EDU a second time and tries to bind with the user's DN and the password the user provided. In this case the authentication fails: (status=49), so LISTSERV rejects the login with a ***BADPW*** error, meaning that the user was found in the directory but the password didn't match.

If you are testing using LISTSERV for dynamic queries, it is also possible to enable debugging to log the user records returned by the LDAP server. Send the following command to LISTSERV (either via email or the web interface) to enable tracing of LDAP data:

DEBUG FLAGS +TRACE_LDAP_DATA

The additional debug lines in the LISTSERV log file will show all of the user records returned by any LDAP dynamic queries run by LISTSERV.

References


Subscribe to LISTSERV at Work.

© L-Soft 2014. All Rights Reserved.