none
PowerShell to connect to an OpenLDAP server over SSL RRS feed

  • Question

  • Hi, scripting guys !

    I try to connect to an OpenLDAP server (Linux) using LDAPS (LDAP over SSL) from a Windows-Server 2012r2 using Powershell. It seems a simple thing, but I cannot make it work. I searched the Internet for clues, but the answers given are not working (most of the post are about connecting to an AD server with LDAPS). It would be just great if someone could help me out in here !

    My final goal is to connect using a user/password with LDAPS. But as a first step, I am trying to connect anonymously. Of course, I have tested with an LDAP browser that anonymous LDAPS connections are allowed on this server, just to be sure... Here is my code

    $ServerName = "xxx" $Port = 636 $dn = "$ServerName"+":"+"$Port"  $c = New-Object System.DirectoryServices.Protocols.LdapConnection $dn $c.SessionOptions.SecureSocketLayer = $True $c.SessionOptions.ProtocolVersion = 3 $c.AuthType = [System.DirectoryServices.Protocols.AuthType]::Anonymous 

    $c.Bind()

    It does not work. I have a message "LDAP server unavailable".

    If I use port 389 and set SecureSocketLayer to $False, the same code works (the server accepts both LDAP and LDAPS connection, for anonymous user).



    • Edited by Larhape Wednesday, August 14, 2019 4:52 PM
    Wednesday, August 14, 2019 4:51 PM

Answers

  • Ok, I found what the issue is. It took me some time, but now I understand. This was a certificate issue. The certificate on the OpenLDAP server is probably self-signed while the certificate on my AD server is a proper certificate issued by some registered CA. It explains the differentiated behavior. The error message sent by Powershell is not very explicit, but in the end, is due to the rejection of the self-signed certificate.

    I think one way to go is to add the certificate to the local certificate store on the server. But I wanted to be independant from the server, so I just said to powershell to accept whatever certificate is presented. Not very secure, but in my context this is good enough (in my case, LDAPS is only used to secure the data in transit, not to authenticate the server).

    I did that by setting "$c.SessionOptions.VerifyServerCertificate = { $true }"

    Here is the full code

    # Default LDAP and LDAPS ports
    $NoSSLPort = 389
    $SSLPort = 636
    
    # Parameters to be set
    $ServerName = "xxx"
    $basedn  = "yyy" 
    $UserName = "zzz"
    $Password = "ppp"
    
    # Start of Program
    $ErrorActionPreference = 'Stop'
    
    # Modify behavior here
    $ActivateSSL = $True
    $LDAP_Auth = $True
    
    if ($ActivateSSL) { $Port = $SSLPort } else { $Port = $NoSSLPort }
    
    $c = New-Object System.DirectoryServices.Protocols.LdapConnection ("$ServerName"+":"+"$Port") 
    
    # SSL Connection or not
     if ($ActivateSSL) {
        $c.SessionOptions.SecureSocketLayer = $True
        $c.SessionOptions.VerifyServerCertificate = { $true }
    } 
    else { $c.SessionOptions.SecureSocketLayer = $False }
    $c.SessionOptions.ProtocolVersion = 3
    
    # Authenticated Connection or not (Anonymous)
    if ($LDAP_Auth) {
        $c.AuthType = [System.DirectoryServices.Protocols.AuthType]::Basic
        $credentials = new-object "System.Net.NetworkCredential" -ArgumentList $UserName,$Password 
    }
    else {
        $c.AuthType = [System.DirectoryServices.Protocols.AuthType]::Anonymous
        $credentials = $null
    }
    
    # Try to connect
    Try { 
        if ($credentials -eq $null) { $c.Bind() }
        else { $c.Bind($credentials) } 
        Write-Host "Connection Successful"
    }
    Catch {
        Throw "Problem during connection - $($_.Exception.Message)"
    }
    
    # Try to request
    $scope = [System.DirectoryServices.Protocols.SearchScope]::Subtree
    $attrlist = $null
    $filter = "(objectClass=*)"
    $ModelQuery = New-Object System.DirectoryServices.Protocols.SearchRequest -ArgumentList $basedn,$filter,$scope,$attrlist
    
    Try {
        $ModelRequest = $c.SendRequest($ModelQuery)
    }
    Catch {
        $ModelRequest = $null
        Throw "Problem looking up model account - $($_.Exception.Message)"
    }
    
    # If request successful, display result
    $ModelRequest 
    

    • Marked as answer by Larhape Monday, August 19, 2019 1:56 PM
    Monday, August 19, 2019 1:55 PM

All replies

  • Does that same code work against a Windows domain controller?

    -- Bill Stewart [Bill_Stewart]

    Wednesday, August 14, 2019 6:25 PM
    Moderator
  • Either the port is wrong or unavailable. Try not using SSL.

    Check firewalls for the required port.  Be sure you have the correct protocols enabled in your system

    [Net.ServicePointManager]::SecurityProtocol


    \_(ツ)_/

    Wednesday, August 14, 2019 6:30 PM
  • Good question, I will try this, and send the results. Actually, I did not try because AD controllers can be accessed much more easily with cmdlet.
    Saturday, August 17, 2019 8:46 PM
  • It works without SSL, with the same server. But the thing is that authenticated connections are disallowed if SSL is not used. Only anonymous connections are allowed without SSL, that's why I need to connect with SSL. I don't think there is a firewall blocking anything on the way.
    Saturday, August 17, 2019 8:49 PM
  • Either the port is wrong or unavailable. Try not using SSL.

    Check firewalls for the required port.  Be sure you have the correct protocols enabled in your system

    [Net.ServicePointManager]::SecurityProtocol


    \_(ツ)_/

    Here is what I have with this command

    [Net.ServicePointManager]::SecurityProtocol
    Ssl3, Tls

    Saturday, August 17, 2019 8:52 PM
  • Does that same code work against a Windows domain controller?

    -- Bill Stewart [Bill_Stewart]

    No, it does not. Same error. Good point. I am not 100% sure that SSL is activated on my AD server, though (but I think it is). I will check this point tomorrow. But, yes, there may be something wrong with my code...

    But the same code works without SSL, just changing the port to 389 and the $c.SessionOptions.SecureSocketLayer to $False. As for the number of the LDAPS port, I am quite sure it is 636. I can connect to this port on my target server using a LDAP browser with SSL enabled. I did it with Apache Directory Studio, for example and it works fine.

    Saturday, August 17, 2019 9:05 PM
  • Does that same code work against a Windows domain controller?


    -- Bill Stewart [Bill_Stewart]

    No, it does not. Same error. Good point. I am not 100% sure that SSL is activated on my AD server, though (but I think it is). I will check this point tomorrow. But, yes, there may be something wrong with my code...

    But the same code works without SSL, just changing the port to 389 and the $c.SessionOptions.SecureSocketLayer to $False. As for the number of the LDAPS port, I am quite sure it is 636. I can connect to this port on my target server using a LDAP browser with SSL enabled. I did it with Apache Directory Studio, for example and it works fine.

    I did a few check and noticed two strange things. I checked the value of $c.SessionOptions and $c.Directory just after running my commands, just to see if everything is correct. Here are the results

    PS C:\Windows\system32> $c.SessionOptions
    
    ReferralChasing         : All
    SecureSocketLayer       : False
    ReferralHopLimit        : 32
    ProtocolVersion         : 3
    HostName                : 
    DomainName              : 
    LocatorFlag             : None
    HostReachable           : True
    PingKeepAliveTimeout    : 00:02:00
    PingLimit               : 4
    PingWaitTimeout         : 00:00:02
    AutoReconnect           : True
    SspiFlag                : 16386
    SslInformation          : 
    SecurityContext         : 
    Signing                 : False
    Sealing                 : False
    SaslMethod              : 
    RootDseCache            : True
    TcpKeepAlive            : False
    SendTimeout             : -00:00:01
    ReferralCallback        : System.DirectoryServices.Protocols.ReferralCallback
    QueryClientCertificate  : 
    VerifyServerCertificate : 
    
    PS C:\Windows\system32> $c.Directory | format-list
    
    
    Servers                   : {xxx:636}
    Connectionless            : False
    FullyQualifiedDnsHostName : False
    PortNumber                : 389 
    

    As you can see, the SecureSocketLayer parameter is $False even though I set it to $True. It is like it does not want to obey this. And the "Port Number" in the second check is 389 even though the "Servers" is correctly set with xxx:636 (xxx is the name of my server, not the real name of course, I just changed that part in the thread).

    Saturday, August 17, 2019 9:20 PM
  • Sorry, there is something wrong with this forum, I replied already, but everything has been deleted somehow. The good thing is that I can give several answers in one shot.

    No, the code is not working against a Windows DC although it should (SSL is enabled on the DC, TCP/636, proper certificate installed etc...). Very good point ! But I have no idea why the code is not working. Once again, if I change to non-SSL the same code works against the same server (that is : change #Port to 389 and $c.SessionOptions.SecureSocketLayer to $False)

    Monday, August 19, 2019 8:08 AM
  • Firewalls on the way are open on these ports. I can easily connect to LDAP over SSL on the server using a LDAP browser such as Apache Directory Studio.

    Here is what it gives when I look at my enabled protocols.

    PS> [Net.ServicePointManager]::SecurityProtocol
    Ssl3, Tls 
    

    Monday, August 19, 2019 8:15 AM
  • Because the code is not working against a DC, I did another check. I looked at the values of my variables, after the execution failed.

    PS> $c.SessionOptions
    
    ReferralChasing         : All
    SecureSocketLayer       : False
    ReferralHopLimit        : 32
    ProtocolVersion         : 3
    HostName                : 
    DomainName              : 
    LocatorFlag             : None
    HostReachable           : True
    PingKeepAliveTimeout    : 00:02:00
    PingLimit               : 4
    PingWaitTimeout         : 00:00:02
    AutoReconnect           : True
    SspiFlag                : 16386
    SslInformation          : 
    SecurityContext         : 
    Signing                 : False
    Sealing                 : False
    SaslMethod              : 
    RootDseCache            : True
    TcpKeepAlive            : False
    SendTimeout             : -00:00:01
    ReferralCallback        : System.DirectoryServices.Protocols.ReferralCallback
    QueryClientCertificate  : 
    VerifyServerCertificate : 
    
    
    PS> $c.Directory | format-list
    
    Servers                   : {xxx:636}
    Connectionless            : False
    FullyQualifiedDnsHostName : False
    PortNumber                : 389 

    There are two things that are strange (maybe the same cause) :

    1. In $c.SessionOptions, the value of SecureSocketLayer is $False even though I set it to $True. It is like this order is not working.
    2. The "PortNumber" in $c.Directory is 389 which is the default (non-SSL) port of LDAP. Note that the "Server" is correctly set with xxx:636 (xxx is the name of my server, that I changed when copy/paste the code).

    Monday, August 19, 2019 8:21 AM
  • It happened that, in the end the code is working on a DC server, probably this was a tipo or I don't know. But anyway, the code below is working against a Windows DC but not against the OpenLDAP server. I put more stuff in it in, but the core is the same.

    $NoSSLPort = 389
    $SSLPort = 636
    
    
    $ServerName = "xxx"
    $basedn  = "yyy" 
    $UserName = "zzz"
    $Password = "ppp"
    
    
    # Start of Program
    
    $ErrorActionPreference = 'Stop'
    
    # Modify behavior here
    $ActivateSSL = $True
    $LDAP_Auth = $False
    
    if ($ActivateSSL) { $Port = $SSLPort } else { $Port = $NoSSLPort }
    
    $c = New-Object System.DirectoryServices.Protocols.LdapConnection ("$ServerName"+":"+"$Port") 
    
    # SSL Connection or not
    if ($ActivateSSL) { $c.SessionOptions.SecureSocketLayer = $True }
    else { $c.SessionOptions.SecureSocketLayer = $False }
    $c.SessionOptions.ProtocolVersion = 3
    
    # Authenticated Connection or not (Anonymous)
    if ($LDAP_Auth) {
        $c.AuthType = [System.DirectoryServices.Protocols.AuthType]::Basic
        $credentials = new-object "System.Net.NetworkCredential" -ArgumentList $UserName,$Password 
    }
    else {
        $c.AuthType = [System.DirectoryServices.Protocols.AuthType]::Anonymous
        $credentials = $null
    }
    
    # Try to connect
    Try { 
        if ($credentials -eq $null) { $c.Bind() }
        else { $c.Bind($credentials) } 
        Write-Host "Connection Successful"
    }
    Catch {
        Throw "Problem during connection - $($_.Exception.Message)"
    }
    
    # Try to request
    $scope = [System.DirectoryServices.Protocols.SearchScope]::Subtree
    $attrlist = $null
    $filter = "(objectClass=*)"
    $ModelQuery = New-Object System.DirectoryServices.Protocols.SearchRequest -ArgumentList $basedn,$filter,$scope,$attrlist
    
    Try {
        $ModelRequest = $c.SendRequest($ModelQuery)
    }
    Catch {
        $ModelRequest = $null
        Throw "Problem looking up model account - $($_.Exception.Message)"
    }
    
    # If request successful, display result
    $ModelRequest 

    You change from SSL to non-SSL by changing $ActivateSSL ($True or $False) and you switch from anonymous to authenticate with $LDAP_Auth. Of course, the 4 variables in the beginning must be correct for it to work (including user and password for authenticated connection).

    This scripts works with a Windows DC controller whatever $ActivateSSL or $LDAP_Auth, but it works only with $ActivateSSL -eq $False with an openldap server.

    • Edited by Larhape Monday, August 19, 2019 1:20 PM
    Monday, August 19, 2019 1:15 PM
  • Ok, I found what the issue is. It took me some time, but now I understand. This was a certificate issue. The certificate on the OpenLDAP server is probably self-signed while the certificate on my AD server is a proper certificate issued by some registered CA. It explains the differentiated behavior. The error message sent by Powershell is not very explicit, but in the end, is due to the rejection of the self-signed certificate.

    I think one way to go is to add the certificate to the local certificate store on the server. But I wanted to be independant from the server, so I just said to powershell to accept whatever certificate is presented. Not very secure, but in my context this is good enough (in my case, LDAPS is only used to secure the data in transit, not to authenticate the server).

    I did that by setting "$c.SessionOptions.VerifyServerCertificate = { $true }"

    Here is the full code

    # Default LDAP and LDAPS ports
    $NoSSLPort = 389
    $SSLPort = 636
    
    # Parameters to be set
    $ServerName = "xxx"
    $basedn  = "yyy" 
    $UserName = "zzz"
    $Password = "ppp"
    
    # Start of Program
    $ErrorActionPreference = 'Stop'
    
    # Modify behavior here
    $ActivateSSL = $True
    $LDAP_Auth = $True
    
    if ($ActivateSSL) { $Port = $SSLPort } else { $Port = $NoSSLPort }
    
    $c = New-Object System.DirectoryServices.Protocols.LdapConnection ("$ServerName"+":"+"$Port") 
    
    # SSL Connection or not
     if ($ActivateSSL) {
        $c.SessionOptions.SecureSocketLayer = $True
        $c.SessionOptions.VerifyServerCertificate = { $true }
    } 
    else { $c.SessionOptions.SecureSocketLayer = $False }
    $c.SessionOptions.ProtocolVersion = 3
    
    # Authenticated Connection or not (Anonymous)
    if ($LDAP_Auth) {
        $c.AuthType = [System.DirectoryServices.Protocols.AuthType]::Basic
        $credentials = new-object "System.Net.NetworkCredential" -ArgumentList $UserName,$Password 
    }
    else {
        $c.AuthType = [System.DirectoryServices.Protocols.AuthType]::Anonymous
        $credentials = $null
    }
    
    # Try to connect
    Try { 
        if ($credentials -eq $null) { $c.Bind() }
        else { $c.Bind($credentials) } 
        Write-Host "Connection Successful"
    }
    Catch {
        Throw "Problem during connection - $($_.Exception.Message)"
    }
    
    # Try to request
    $scope = [System.DirectoryServices.Protocols.SearchScope]::Subtree
    $attrlist = $null
    $filter = "(objectClass=*)"
    $ModelQuery = New-Object System.DirectoryServices.Protocols.SearchRequest -ArgumentList $basedn,$filter,$scope,$attrlist
    
    Try {
        $ModelRequest = $c.SendRequest($ModelQuery)
    }
    Catch {
        $ModelRequest = $null
        Throw "Problem looking up model account - $($_.Exception.Message)"
    }
    
    # If request successful, display result
    $ModelRequest 
    

    • Marked as answer by Larhape Monday, August 19, 2019 1:56 PM
    Monday, August 19, 2019 1:55 PM