Bug 1520364

Summary: perl-LDAP not to default to TLS 1.0 in an explicit STARTTLS LDAP protocol upgrade
Product: Red Hat Enterprise Linux 7 Reporter: Petr Pisar <ppisar>
Component: perl-LDAPAssignee: Petr Pisar <ppisar>
Status: CLOSED ERRATA QA Contact: Branislav Náter <bnater>
Severity: urgent Docs Contact: Lenka Špačková <lkuprova>
Priority: unspecified    
Version: 7.4CC: amitkuma, bgollahe, bnater, jorton, mlstarling31, perl-maint-list, ppisar, psabata, qe-baseos-security
Target Milestone: rcKeywords: FutureFeature, Patch
Target Release: ---   
Hardware: x86_64   
OS: Unspecified   
Whiteboard:
Fixed In Version: perl-LDAP-0.56-6.el7 Doc Type: Release Note
Doc Text:
The *Net::LDAP* Perl module no longer defaults to TLS 1.0 Previously, when the *Net::LDAP* Perl module module was used for upgrading an unsecured LDAP connection to a TLS-protected one, the module used the TLS protocol version 1.0, which is currently considered insecure. With this update, the default TLS version has been removed from *Net::LDAP*, and both implicit (LDAPS schema) and explicit (LDAP schema) TLS protocols rely on the default TLS version selected in the *IO::Socket::SSL* Perl module. As a result, it is no longer necessary to override the TLS version in the *Net::LDAP* clients by passing the `sslversion` argument to the `start_tls()` method to preserve security.
Story Points: ---
Clone Of: 1519080 Environment:
Last Closed: 2018-10-30 07:54:46 UTC Type: Bug
Regression: --- Mount Type: ---
Documentation: --- CRM:
Verified Versions: Category: ---
oVirt Team: --- RHEL 7.3 requirements from Atomic Host:
Cloudforms Team: --- Target Upstream Version:
Embargoed:
Bug Depends On:    
Bug Blocks: 1549616, 1551025, 1562205    
Attachments:
Description Flags
1st upstream fix
none
2nd upstream fix ported to 0.56
none
2nd upstream fix
none
1st upstream fix ported to 0.56
none
2nd fix ported to 0.56 none

Description Petr Pisar 2017-12-04 11:11:59 UTC
+++ This bug was initially created as a clone of Bug #1519080 +++
[...]
# vim slapd.conf
TLSProtocolMin = 3.2 or 3.3 //TLS1.2  1.3.    While 3.1(TLS1.0) works fine
# smbldap-passwd user-name
Cannot start TLS on LDAP connection: ldap://<>:389: SSL connect attempt failed with unknown error error:1408F10B:SSL routines:SSL3_GET_RECORD:wrong version number 

[...]

Steps to Reproduce:
1. Configure TLSProtocolMin 3.2 or 3.3 in slapd.conf
2. Run smbldap-passwd user-name
3. See the Error: Cannot start TLS on LDAP connection: ldap://<>:389: SSL connect attempt failed with unknown error error:1408F10B:SSL routines:SSL3_GET_RECORD:wrong version number

Actual results:
TLS1.1, 1.2 cannot complete SSL handshake and connection establishment.

Expected results:
TLS1.1, 1.2 should work fine

[...]

--- Additional comment from Michael Starling on 2017-12-01 14:57:22 GMT ---

I am using startTLS over 389 not LDAPS. I do not have a problem with openssl or client connections. The only failure I'm seeing is with Perl.

With TLSProtocolMin 3.2 set in the directory I receive the following fatal error when connecting with Perl. When I set TLSProtocolMin 3.1 thinsg works as expected.

use strict; use warnings; use diagnostics;
use Net::LDAP;

my $ldap_master = Net::LDAP->new("ldap1.example.com",
                                port => '389',
                                version => 3);
$ldap_master->start_tls();
my $msg = $ldap_master->search(base => "dc=example,dc=com",
                        filter => "(&(objectclass=posixaccount)(uid=*))",
                        scope => "sub");
foreach my $entry ($msg->all_entries) {
          my $dn= $entry->dn;
          print($dn . "\n");
}



New TCP connection #1: ldap1(42174) <-> ldap1(389)
1 1  0.0215 (0.0215)  C>S  Handshake
      ClientHello
        Version 3.1
        cipher suites
        TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA
        TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA
        TLS_DHE_RSA_WITH_AES_256_CBC_SHA
        TLS_DHE_DSS_WITH_AES_256_CBC_SHA
        TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA
        TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA
        TLS_ECDH_anon_WITH_AES_256_CBC_SHA
        TLS_DH_anon_WITH_AES_256_CBC_SHA
        TLS_DH_anon_WITH_CAMELLIA_256_CBC_SHA
        TLS_ECDH_RSA_WITH_AES_256_CBC_SHA
        TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA
        TLS_RSA_WITH_AES_256_CBC_SHA
        TLS_RSA_WITH_CAMELLIA_256_CBC_SHA
        TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA
        TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA
        TLS_DHE_RSA_WITH_AES_128_CBC_SHA
        TLS_DHE_DSS_WITH_AES_128_CBC_SHA
        TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA
        TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA
        TLS_DHE_RSA_WITH_SEED_CBC_SHA
        TLS_DHE_DSS_WITH_SEED_CBC_SHA
        TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA
        TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA
        TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA
        TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA
        TLS_ECDH_anon_WITH_AES_128_CBC_SHA
        TLS_DH_anon_WITH_AES_128_CBC_SHA
        TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA
        TLS_DH_anon_WITH_SEED_CBC_SHA
        TLS_DH_anon_WITH_CAMELLIA_128_CBC_SHA
        TLS_DH_anon_WITH_3DES_EDE_CBC_SHA
        TLS_ECDH_RSA_WITH_AES_128_CBC_SHA
        TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA
        TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA
        TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA
        TLS_RSA_WITH_AES_128_CBC_SHA
        TLS_RSA_WITH_SEED_CBC_SHA
        TLS_RSA_WITH_CAMELLIA_128_CBC_SHA
        TLS_RSA_WITH_3DES_EDE_CBC_SHA
        TLS_RSA_WITH_IDEA_CBC_SHA
        TLS_ECDHE_RSA_WITH_RC4_128_SHA
        TLS_ECDHE_ECDSA_WITH_RC4_128_SHA
        TLS_ECDH_anon_WITH_RC4_128_SHA
        TLS_DH_anon_WITH_RC4_128_MD5
        TLS_ECDH_RSA_WITH_RC4_128_SHA
        TLS_ECDH_ECDSA_WITH_RC4_128_SHA
        TLS_RSA_WITH_RC4_128_SHA
        TLS_RSA_WITH_RC4_128_MD5
        TLS_DHE_RSA_WITH_DES_CBC_SHA
        TLS_DHE_DSS_WITH_DES_CBC_SHA
        TLS_DH_anon_WITH_DES_CBC_SHA
        TLS_RSA_WITH_DES_CBC_SHA
        TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA
        TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA
        TLS_DH_anon_EXPORT_WITH_DES40_CBC_SHA
        TLS_RSA_EXPORT_WITH_DES40_CBC_SHA
        TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5
        TLS_DH_anon_EXPORT_WITH_RC4_40_MD5
        TLS_RSA_EXPORT_WITH_RC4_40_MD5
        TLS_EMPTY_RENEGOTIATION_INFO_SCSV
        compression methods
                  NULL
1 2  0.0217 (0.0002)  S>C  Alert
    level           fatal
    value           protocol_version
1 3  0.0218 (0.0000)  C>S  Alert
    level           fatal
    value           protocol_version
1    0.0218 (0.0000)  S>C  TCP FIN
1    0.0219 (0.0000)  C>S  TCP RST



--- Additional comment from Petr Pisar on 2017-12-04 10:58:52 GMT ---

Your problem lies in the perl-LDAP, not in the perl-IO-Socket-SSL.

Net::LDAP documentation at start_tls() method reads:

   sslversion => ’sslv2’ | ’sslv3’ | ’sslv2/3’ | ’tlsv1’
     This defines the version of the SSL/TLS protocol to use. Defaults to tlsv1’.

And IO::Socket::SSL explains: "’TLSv1’ [...] restrict handshake and protocol to the specified version." Thus your Net::LDAP client forces TLS 1.0 while your server forces TLS 1.1. Therefore the TLS handshake fails.

If add some failure checks into your client:

#!/usr/bin/perl
use strict; use warnings; use diagnostics;
use Net::LDAP;

my $ldap_master = Net::LDAP->new('localhost',
                                port => '389',
                                version => 3,
);
$ldap_master->start_tls()
        or die "start_tls() failed: $@\n";
my $msg = $ldap_master->search(base => "dc=example,dc=com",
                        filter => "(&(objectclass=posixaccount)(uid=*))",
                        scope => "sub")
        or die "search() failed: $@\n";

You can see client reports:

$ /tmp/test
syswrite() on closed filehandle GEN0 at
        /usr/share/perl5/vendor_perl/Net/LDAP.pm line 806, <DATA> line 522 (#1)
    (W closed) The filehandle you're writing to got itself closed sometime
    before now.  Check your control flow.

and the server reports:

5a252608 connection_get(12)
TLS: error: accept - force handshake failure: errno 11 - moznss error -12279
TLS: can't accept: TLS error -12279:Peer using unsupported version of security protocol..
5a252608 connection_closing: readying conn=1003 sd=12 for close

Passing 'sslversion' => 'TLSv1_1' start_tls() helps:

ldap_master->start_tls(sslversion => 'TLSv1_1')
        or die "start_tls() failed: $@\n";

If you want your client to support TLS 1.1 or 1.2, you can use 'TLSv1_1:TLSv1_2'. Or you can disable SSL and keep TLS enabled with 'SSLv23:!SSLv2:!SSLv3'.
---------

RHEL-7 (perl-LDAP-0.56-5.el7) is affected.

This is a feature request to remove hard-coded default SSL protocol form Net::LDAP Perl module.

This was implemented in the upstream in 0.65 version:

* LDAP.pm: do not set default sslversion for start_tls() & LDAPS
  Instead of second-guessing a default sslversion for start_tls() and LDAPS,
  rely on the - known to be sane - default value used by IO::Socket::SSL.

Comment 1 Petr Pisar 2017-12-04 12:35:48 UTC
Also implicit TLS defaults to SSLv23. This does not causes any issues now, but for consistency, it's good to remove the perl-Net-LDAP specific override too.

Comment 2 Petr Pisar 2017-12-04 12:36:36 UTC
Created attachment 1362657 [details]
1st upstream fix

Comment 3 Petr Pisar 2017-12-04 12:37:06 UTC
Created attachment 1362658 [details]
2nd upstream fix ported to 0.56

Comment 4 Petr Pisar 2017-12-04 12:46:10 UTC
How to test:

(1) Configure openldap-servers' slapd daemon to use TLS 1.2 only. E.g. add into /etc/openldap/slapd.conf:

TLSCACertificatePath /etc/openldap/certs
TLSCertificateFile localhost
TLSCertificateKeyFile /etc/openldap/certs/password
TLSProtocolMin 3.3

and import the certificate and key into slapd's NSS database (it must be readable by ldap group) and start the slapd:

# /usr/sbin/slapd -h  'ldap:/// ldaps:/// ldapi:///' -u ldap -d Args,Conns,Config -f /etc/openldap/slapd.conf -F /etc/openldap/slapd.d

(2) Run a Net::LDAPS client:

$ perl -Ilib -MNet::LDAPS -e 'my $c=Net::LDAPS->new(q{localhost}, version=>3, port =>636) or die qq{$@\n}'

Before: It should connect.
After: It should still connect.

(3) Run a Net::LDAP client with explicit STARTTLS:

$ cat /tmp/test
#!/usr/bin/perl
use strict; use warnings; use diagnostics;
use Net::LDAP;
my $ldap_master = Net::LDAP->new('localhost',
                                port => '389',
                                version => 3,
);
$ldap_master->start_tls()
        or die "start_tls() failed: $@\n";
my $msg = $ldap_master->search(base => "dc=example,dc=com",
                        filter => "(&(objectclass=posixaccount)(uid=*))",
                        scope => "sub")
        or die "search() failed: $@\n";

Before: It fails:

$ perl /tmp/test
syswrite() on closed filehandle GEN0 at
        /usr/share/perl5/vendor_perl/Net/LDAP.pm line 806, <DATA> line 522 (#1)
    (W closed) The filehandle you're writing to got itself closed sometime
    before now.  Check your control flow.

and the server logs:

TLS: error: accept - force handshake failure: errno 11 - moznss error -12279
TLS: can't accept: TLS error -12279:Peer using unsupported version of security protocol..

After: It connects.

Comment 5 amitkuma 2017-12-05 15:30:05 UTC
Hello,

//Business Justification from Customer//
1. Why does the customer need this? (List the business requirements here).
We have a very large RHEL6 infrastructure and TLSv 1.0 is no longer acceptable for PCI compliance.

2. Is there already an existing RFE upstream or in Red Hat Bugzilla?
NO

3. Does the customer have any specific timeline dependencies?
ASAP

4. Is the sales team involved in this request and do they have any additional input?
NO
5. List any affected packages or components?
As far as I can see through testing it appears perl-LDAP and possibly some versions of openLDAP are both affected.

6. Would the customer be able to assist in testing this functionality if implemented?
Absolutely.

Thanks
Amit

Comment 6 Petr Pisar 2017-12-06 09:50:45 UTC
Created attachment 1363575 [details]
2nd upstream fix

Comment 7 Petr Pisar 2017-12-06 10:12:25 UTC
Created attachment 1363581 [details]
1st upstream fix ported to 0.56

Comment 8 Petr Pisar 2017-12-06 10:12:57 UTC
Created attachment 1363582 [details]
2nd fix ported to 0.56

Comment 16 Petr Pisar 2018-09-06 10:49:10 UTC
The test with Net::LDAP client with explicit TLS does not check errors properly. This one should be better:

#!/usr/bin/perl
use strict;
use warnings;
use Net::LDAP qw(LDAP_NO_SUCH_OBJECT);
my $ldap_master = Net::LDAP->new('localhost',
                                port => '389',
                                version => 3,
);
my $retval = $ldap_master->start_tls();
$retval->code and die "start_tls() failed: " . $retval->error;
$retval = $ldap_master->search(base => "dc=example,dc=com",
                        filter => "(&(objectclass=posixaccount)(uid=*))",
                        scope => "sub");
$retval->code and $retval->code != LDAP_NO_SUCH_OBJECT
        and die "search() failed: " . $retval->error;

The unpatched build should report the error like this:
$ perl test.pl; echo $?
start_tls() failed: SSL connect attempt failed because of handshake problems SSL wants a read first at test.pl line 10, <DATA> line 747.
255

Patched build should be silent:

$ perl test.pl; echo $?
0

Comment 19 errata-xmlrpc 2018-10-30 07:54:46 UTC
Since the problem described in this bug report should be
resolved in a recent advisory, it has been closed with a
resolution of ERRATA.

For information on the advisory, and where to find the updated
files, follow the link below.

If the solution does not work for you, open a new bug report.

https://access.redhat.com/errata/RHEA-2018:3038