Perforce Public Knowledge Base - Authenticating with LDAP
Perforce Software logo
Reset Search
 

 

Article

Authenticating with LDAP

« Go Back

Information

 
Problem

How can I authenticate Perforce users against an external authentication source such as LDAP or Active Directory?

Solution

External Authentication with Helix 2014.2 and beyond

Configure Active Directory using as below.  Alternatively, you can still use external authentication triggers as seen in the KB article Authenticating with LDAP on Perforce 2014.1 and earlier.
 

Configuring LDAP

  • LDAP authentication requires security=3.  Confirm your security value using:
p4 configure show security
monitor=1 (configure)

Set it using:

p4 configure set security=3
For server 'any', configuration variable 'security' set to '3'
  • Users must log in with a strong password even if AuthMethod: is set to perforce.
  • Perforce names must match Active Directory names
  • Spaces are not allowed in Perforce names.
  • Warning: By enabling LDAP authentication, any existing auth-check trigger will become inactive. Thus when upgrading to LDAP authentication, test on a separate test instance prior to deploying in a production environment.
  1. Create an LDAP template

    Choose SASL, Simple, or Search method.  This example uses SASL.  Simple and Search methods are described further below.

    p4 ldap saslconfig
    Name: saslconfig
    Host: 10.20.30.40
    Port: 389
    Encryption: none
    BindMethod: sasl
    SearchScope: subtree
    GroupSearchScope: subtree
    

    Where 10.20.30.40 is the IP address of your Active Directory server.

  2. Test that authentication works before implementing in production:

    p4 ldap -t username saslconfig

    The username may or may not be the Active Directory full name, and the name may need to be surrounded in quotes. Confirm what works for your environment.

  3. Set configurables:

    p4 configure set auth.default.method=ldap
    p4 configure set auth.ldap.order.1=saslconfig
    

    You can set multiple profiles so if one LDAP profile does not login for a user, the next will be tried. For example:

    p4 configure set auth.ldap.order.2=searchconfig
    
  4. Ensure you have at least one super user using Perforce authentication in case the LDAP server is unreachable

    p4 user -fsuperuser
    
    AuthMethod: perforce
    
  5. Restart the Perforce server:

    p4 admin restart
  6. Verify that Active Directory is in use. Log into Perforce with a different Active Directory user and deliberately type in an incorrect password:

    p4 -u ActiveDirectoryUser login
    Enter password:
    Authentication failed.
    

    Note the error "Authentication failed" instead of "Password invalid".
    external authentication
    Next, log in as the user using the correct Active Directory password:

    p4 -u ActiveDirectoryUser login
    Enter password: 
    User ActiveDirectoryUser logged in.
    	

SASL

The easiest way to set up external authentication is by using SASL. See the example above.
Note that reverse DNS must work from the IP addresses listed in the Perforce LDAP specifications.  On Unix, you can workaround this by placing a .ldaprc in the $HOME directory with "SASL_NOCANON on" and use the full hostname in the Perforce LDAP specifications.  If you use SASL_NOCANON:

SASL_NOCANON on = P4 ldap configs must use hostname not IP address
SASL_NOCANON off = P4 ldap configs can use IP address only on LDAP servers where reverse DNS is working
 

Simple

The simple method requires knowledge of the distinguished name. Enter the command:

p4 ldap mysimple

Change the template specification:

Name:   mysimple
Host:   10.20.30.40
Port:   389
Encryption:     none
BindMethod:     simple
SimplePattern:  CN=%user%,CN=Users,DC=ad,DC=foo,DC=com
SearchScope:    subtree
GroupSearchScope:       subtree

Where "10.20.30.40" is replaced with the IP address of the Active Directory server. Use a tool like Sysinternals Active Directory Explorer and select one user on the left pane. This tool shows the distinguishedName in the right panel. Substitute the variable %user% in place of the name.

kA0F0000000Cq3qKAC_en_US_4_0 

Another example using Simple:

Name:    simpleconfig
Host:    gabriel.foo.com
Port:    389
Encryption:    tls
BindMethod:    simple
Options:    nodowncase nogetattrs norealminusername
SimplePattern:    FOO\%user%
SearchBaseDN:    cn=users,dc=das,dc=perforce,dc=com
SearchScope:    subtree
GroupSearchScope:    subtree

where FOO is the domain.
 

Search

The search method logs into Active Directory as a particular user and associated password and uses a standard LDAP query to filter results down to one user to log in.  A look through Active Directory Explorer shows that there are multiple ways to filter for the user.  In this example, we are using the username bruno and bruno's password to gain access to Active Directory, and then we'll search the sAMAccountName fields seen in the right pane of Active Directory Explorer.

Name:   mysearch
Host:   10.20.30.40
Port:   389
Encryption:     none
BindMethod:     search
SearchBaseDN:   CN=Users,DC=ad,DC=foo,DC=com
SearchFilter:   (sAMAccountName=%user%)
SearchScope:    subtree
SearchBindDN:   bruno@ad.perforce.com
SearchPasswd:   stdpasswd
GroupSearchScope:       subtree


Alternatively, you can use your LDAP query to filter using the CN field seen in the right pane of
Active Directory Explorer.

Name:   mysearch2
Host:   10.0.10.8
Port:   389
Encryption:     none
BindMethod:     search
SearchBaseDN:  CN=Users,DC=ad,DC=perforce,DC=com
SearchFilter:   (CN=%user%)
SearchScope:    subtree
SearchBindDN:   bruno@ad.foo.com
SearchPasswd:   stdpasswd
GroupSearchScope:       subtree

If you have multiple LDAP servers and GroupSearchFilter, note that Perforce will check the user credentials first. Only if the credentials are valid on the first LDAP server is the group filter checked. Only if the credentials are not valid will the next LDAP server be checked.

 

Troubleshooting LDAP queries

 

Using ldifde to check LDAP on Windows

Use ldifde to check whether the LDAP query to place into SearchFilter is correct. 

For example, we use distinguishedName and (CN=*) to search for all the CN entries.

c:\> ldifde -d "CN=Users,DC=ad,DC=foo,DC=com" -
f output.txt -r "(CN=*)"
Connecting to "ts-vm.ad.foo.com"
Logging in as current user using SSPI
Exporting directory to file output.txt
Searching for entries...
Writing out entries...............................
31 entries exported

The command has completed successfully

Here we run the query with a particular user and password, useful for entering the username and password when choosing the search method.

c:\> ldifde -a perforce stdpasswd -d "CN=Users,DC=ad,DC=foo,DC=com" -f output.txt -r "(CN=*)"
Connecting to "ts-vm.ad.foo.com"
Logging in as "bruno" using simple bind
Exporting directory to file output.txt
Searching for entries...
Writing out entries...............................
31 entries exported

The command has completed successfully

The LDAP query will use a filter to narrow LDAP to find one particular user.  This user will become %user% in the proper LDAP query syntax.  In this example, our filter in LDAP query syntax is "(CN=smith)" to filter to this one name.
  
c:\> ldifde -d "CN=Users,DC=ad,DC=foo,DC=com" -f output.txt -r "(CN=smith)"
Connecting to "ts--vm.ad.foo.com"
Logging in as current user using SSPI
Exporting directory to file output.txt
Searching for entries...
Writing out entries.
1 entries exported
The command has completed successfully


If this works, we can replace "smith" with the variable "%user%" in the p4 ldap form.  If commands like ldifde do not work properly outside of Perforce, it is unlikely that Perforce ldap authentication will work either. 
More information is documented in Authenticating against LDAP servers
 

Using ldapsearch to test simple and search on Linux

Use ldapsearch to choose a user with privileges to query the LDAP server.  This information will help fill in the SearchBaseDN, the SearchFilter, and the SearchBindDN when using the Search binding for simple and search binds.
For example, if the username is bruno, the output may look something like:

$ ldapsearch -x -D "bruno@gabriel.foo.com" -b "CN=Users,DC=ad,DC=foo,DC=com" -h 10.5.10.80 -w <password here> "(sAMAccountName=*)"

# extended LDIF
# filter: (sAMAccountName=*)
# requesting: ALL
#

# Administrator, Users, gabriel.foo.com
dn: CN=Administrator,CN=Users,DC=ad,DC=foo,DC=com
objectClass: top
objectClass: person
objectClass: organizationalPerson
objectClass: user
cn: Administrator
description: Built-in account for administering the computer/domain
distinguishedName: CN=Administrator,CN=Users,DC=ad,DC=foo,DC=com
instanceType: 4
whenCreated: 20161010173928.0Z
whenChanged: 20161010175539.0Z
uSNCreated: 8196
memberOf: CN=Group Policy Creator Owners,CN=Users,DC=ad,DC=foo,DC=com
memberOf: CN=Domain Admins,CN=Users,DC=ad,DC=foo,DC=com
memberOf: CN=Enterprise Admins,CN=Users,DC=ad,DC=foo,DC=com
memberOf: CN=Schema Admins,CN=Users,DC=ad,DC=foo,DC=com
memberOf: CN=Administrators,CN=Builtin,DC=ad,DC=foo,DC=com
uSNChanged: 12540
name: Administrator
objectGUID:: JlNMZyAfm0qHYP9ErBod4g==
userAccountControl: 66048
badPwdCount: 0
codePage: 0
countryCode: 0
badPasswordTime: 131205931176691073
lastLogoff: 0
lastLogon: 131205979688411863
logonHours:: ////////////////////////////
pwdLastSet: 131205957394203190
primaryGroupID: 513
objectSid:: AQUAAAAAAAUVAAAA38Yq7eUZ1nzjLef09AEAAA==
adminCount: 1
accountExpires: 0
logonCount: 31
sAMAccountName: Administrator
sAMAccountType: 805306368
objectCategory: CN=Person,CN=Schema,CN=Configuration,DC=ad,DC=foo,DC=com
isCriticalSystemObject: TRUE
dSCorePropagationData: 20161010175531.0Z
dSCorePropagationData: 20161010175531.0Z
dSCorePropagationData: 20161010174020.0Z
dSCorePropagationData: 16010101181216.0Z
lastLogonTimestamp: 131205956950608765

# Guest, Users, gabriel.foo.com
dn: CN=Guest,CN=Users,DC=ad,DC=foo,DC=com
objectClass: top
objectClass: person
objectClass: organizationalPerson
objectClass: user
cn: Guest
description: Built-in account for guest access to the computer/domain
distinguishedName: CN=Guest,CN=Users,DC=ad,DC=foo,DC=com
instanceType: 4
whenCreated: 20161010173928.0Z
whenChanged: 20161010173928.0Z
uSNCreated: 8197
memberOf: CN=Guests,CN=Builtin,DC=ad,DC=foo,DC=com
uSNChanged: 8197
name: Guest
objectGUID:: JN1wah0I+0avvG6uL+vymQ==
userAccountControl: 66082
badPwdCount: 0
codePage: 0
countryCode: 0
badPasswordTime: 0
lastLogoff: 0
lastLogon: 0
pwdLastSet: 0
primaryGroupID: 514
objectSid:: AQUAAAAAAAUVAAAA38Yq7eUZ1nzjLef09QEAAA==
accountExpires: 9223372036854775807
logonCount: 0
sAMAccountName: Guest
sAMAccountType: 805306368
objectCategory: CN=Person,CN=Schema,CN=Configuration,DC=ad,DC=foo,DC=com
isCriticalSystemObject: TRUE
dSCorePropagationData: 20161010174020.0Z
dSCorePropagationData: 16010101000001.0Z

# WinRMRemoteWMIUsers__, Users, gabriel.foo.com
dn: CN=WinRMRemoteWMIUsers__,CN=Users,DC=ad,DC=foo,DC=com
objectClass: top
objectClass: group
cn: WinRMRemoteWMIUsers__
description: Members of this group can access WMI resources over management pr
otocols (such as WS-Management via the Windows Remote Management service). Th
is applies only to WMI namespaces that grant access to the user.
distinguishedName: CN=WinRMRemoteWMIUsers__,CN=Users,DC=ad,DC=foo,DC=com
instanceType: 4
whenCreated: 20161010173928.0Z
whenChanged: 20161010173928.0Z
uSNCreated: 8198
uSNChanged: 8198
name: WinRMRemoteWMIUsers__
objectGUID:: 8/kOy8af7kG0viWVt6QTMQ==
objectSid:: AQUAAAAAAAUVAAAA38Yq7eUZ1nzjLef06AMAAA==
sAMAccountName: WinRMRemoteWMIUsers__
sAMAccountType: 536870912
groupType: -2147483644
objectCategory: CN=Group,CN=Schema,CN=Configuration,DC=ad,DC=foo,DC=com
dSCorePropagationData: 20161010174020.0Z
dSCorePropagationData: 16010101000001.0Z

 

This translates into an LDAP simple bind spec similar to
 
Name:   mysimple
Host:   10.20.30.40
Port:   389
Encryption:     none
BindMethod:     simple
Options:        nodowncase getattrs norealminusername
SimplePattern:  CN=%user%,CN=Users,DC=ad,DC=foo,DC=com
SearchScope:    subtree
GroupSearchScope:       subtree
AttributeUid:   sAMAccountName
AttributeName:  displayName
AttributeEmail: userPrincipalName

This also translates into an LDAP search bind spec similar to

Name:   mysearch
Host:   10.20.30.40
Port:   389
Encryption:     none
BindMethod:     search
Options:        nodowncase getattrs norealminusername
SearchBaseDN:   CN=Users,DC=ad,DC=foo,DC=com
SearchFilter:   (&(objectClass=user)(sAMAccountName=%user%))
SearchScope:    subtree
SearchBindDN:   bruno@ad.foo.com
SearchPasswd:   stdpassword
GroupSearchScope:       subtree
AttributeUid:   sAMAccountName
AttributeName:  displayName
AttributeEmail: userPrincipalName

Here the LDAP query states that objectClass must equal "user" and the field sAMAccountName must have the user name. The %user% specifies the LDAP user.  Authentication to the LDAP server is in this case through user bruno@ad.foo.com and password "stdpassword". 

Then test it:

$ p4 ldap -t bruno mysearch
Enter password:
Authentication successful.

 

Using ldapsearch to test SASL on Linux

You can also use ldapsearch to test SASL (with or without the -I flag).
For example:

$ ldapsearch -h 10.25.10.80 -p 389 -b "CN=Users,DC=ad,DC=foo,DC=com" -U perforce "(objectclass=*)" -Y DIGEST-MD5

SASL/DIGEST-MD5 authentication started
Please enter your password:
SASL username: perforce
SASL SSF: 128
SASL data security layer installed.
# extended LDIF
#
# LDAPv3
# base <CN=Users,DC=ad,DC=perforce,DC=com> with scope subtree
# filter: (objectclass=*)
# requesting: ALL
#

<snip>

 

Related Links

Feedback

 

Was this article helpful?


   

Feedback

Please tell us how we can make this article more useful.

Characters Remaining: 255