ldap: retrieve tls info on ldap errors (#56666)

This commit is contained in:
Paul Marillonnet 2021-09-06 10:28:36 +02:00
parent 50f9c714fe
commit 1c7cc013ee
3 changed files with 184 additions and 0 deletions

View File

@ -36,6 +36,8 @@ import json
import logging
import os
import random
import socket
import ssl
import time
from django.conf import settings
@ -1771,6 +1773,26 @@ class LDAPBackend:
except ldap.TIMEOUT:
return False, 'timeout'
except ldap.SERVER_DOWN:
if block['use_tls']:
url = block['url']
if url and isinstance(url, (list, tuple)):
url = url[0]
hostname = url.split('://')[-1]
port = 636
if ':' in hostname:
hostname, port = hostname.split(':')
context = ssl.create_default_context()
try:
with socket.create_connection((hostname, port), timeout=2) as sock:
with context.wrap_socket(sock, server_hostname=hostname) as ssock:
pass
except (socket.herror, socket.gaierror) as e:
return False, 'socket address error on host %s: %s' % (hostname, e)
except socket.timeout:
return False, 'socket timeout error on host %s' % hostname
except (OSError, ssl.SSLError) as e:
return False, 'ssl error on host %s: %s' % (hostname, e)
return False, 'ldap is down yet no ssl error was detected on host %s' % hostname
return False, 'ldap is down'
@classmethod

View File

@ -68,6 +68,7 @@ EE_CITY = "PARIS"
base_dir = os.path.dirname(__file__)
key_file = os.path.join(base_dir, 'key.pem')
cert_file = os.path.join(base_dir, 'cert.pem')
wrong_cert_file = os.path.join(base_dir, 'wrongcert.pem')
@pytest.fixture
@ -1094,6 +1095,87 @@ def test_tls(db, tls_slapd, settings, client):
assert force_bytes('name="username"') not in result.content
def test_tls_connect_on_ldap_errors(db, tls_slapd, settings, client, caplog):
conn = tls_slapd.get_connection_admin()
conn.modify_s(
'cn=config',
[
# modifying ca cert to mock buggy ldap server
(ldap.MOD_ADD, 'olcTLSCACertificateFile', force_bytes(wrong_cert_file)),
(ldap.MOD_ADD, 'olcTLSVerifyClient', b'demand'),
],
)
settings.LDAP_AUTH_SETTINGS = [
{
'url': [tls_slapd.ldap_url],
'basedn': 'o=ôrga',
'use_tls': True,
'cacertfile': cert_file,
'certfile': cert_file,
'keyfile': key_file,
}
]
result = client.post(
'/login/', {'login-password-submit': '1', 'username': USERNAME, 'password': PASS}, follow=True
)
# ssl error on bind attempt
assert result.status_code == 200
assert force_bytes('Étienne Michu') not in result.content
assert force_bytes('name="username"') in result.content
assert 'ssl error on host localhost.entrouvert.org' in caplog.text
def test_connect_wrong_host(db, tls_slapd, settings, client, caplog):
conn = tls_slapd.get_connection_admin()
conn.modify_s(
'cn=config',
[
(ldap.MOD_ADD, 'olcTLSCACertificateFile', force_bytes(cert_file)),
(ldap.MOD_ADD, 'olcTLSVerifyClient', b'demand'),
],
)
settings.LDAP_AUTH_SETTINGS = [
{
'basedn': 'o=ôrga',
'use_tls': True,
'cacertfile': cert_file,
'certfile': cert_file,
'keyfile': key_file,
}
]
url = tls_slapd.ldap_url
uri, port = url.rsplit(':', 1)
wrong_port = str(int(port) + 1) # oops slapd not listening on this port
settings.LDAP_AUTH_SETTINGS[0]['url'] = ['%s:%s' % (uri, wrong_port)]
result = client.post(
'/login/', {'login-password-submit': '1', 'username': USERNAME, 'password': PASS}, follow=True
)
# earlier connect error when the port is wrong
assert result.status_code == 200
assert force_bytes('Étienne Michu') not in result.content
assert force_bytes('name="username"') in result.content
assert "ldap '%s:%s' is down" % (uri, wrong_port) in caplog.text
caplog.clear()
wrong_uri = 'ldap://localhost.nowhere.null'
settings.LDAP_AUTH_SETTINGS[0]['url'] = ['%s:%s' % (wrong_uri, port)]
result = client.post(
'/login/', {'login-password-submit': '1', 'username': USERNAME, 'password': PASS}, follow=True
)
# earlier connect error when the hostname is wrong
assert result.status_code == 200
assert force_bytes('Étienne Michu') not in result.content
assert force_bytes('name="username"') in result.content
assert "ldap '%s:%s' is down" % (wrong_uri, port) in caplog.text
def test_user_attributes(slapd, settings, client, db):
settings.LDAP_AUTH_SETTINGS = [
{

80
tests/wrongcert.pem Normal file
View File

@ -0,0 +1,80 @@
Certificate:
Data:
Version: 3 (0x2)
Serial Number:
20:7f:58:15:6b:4b:75:e4:49:d5:c0:97:5f:28:56:16:1c:6f:98:a9
Signature Algorithm: sha256WithRSAEncryption
Issuer: C=FR, ST=Some-State, L=Paris, O=Internet Widgits Pty Ltd, CN=localhost
Validity
Not Before: Sep 9 12:53:14 2021 GMT
Not After : Sep 7 12:53:14 2031 GMT
Subject: C=FR, ST=Some-State, L=Paris, O=Internet Widgits Pty Ltd, CN=localhost
Subject Public Key Info:
Public Key Algorithm: rsaEncryption
RSA Public-Key: (2048 bit)
Modulus:
00:ba:6a:06:93:a8:27:a2:fb:b9:72:81:56:01:96:
bf:dd:9c:68:28:b5:d1:c5:90:af:a1:5a:43:cd:52:
e0:be:87:ef:ab:06:a4:24:ac:2e:a5:aa:2c:7c:c0:
00:c6:1a:67:04:d0:36:ae:fa:6f:34:49:d2:4a:e3:
8e:62:75:a9:26:9c:c3:fa:33:ec:bc:25:83:b6:ce:
0f:3a:41:91:db:19:29:d3:3a:b6:3b:a8:b1:65:06:
13:6b:ea:7a:c9:eb:20:38:cd:40:09:07:d9:d2:93:
dd:3a:2a:04:40:e3:d5:0d:80:ec:bb:5e:5f:d1:b9:
05:59:c5:ff:10:cc:92:91:43:41:0e:d5:00:0b:e6:
e5:f6:1d:4e:89:4f:e9:e8:14:20:db:e5:0c:fa:b4:
40:70:85:71:45:50:bc:ab:25:5a:c6:79:a0:4d:51:
d1:9a:15:6f:15:7f:d7:51:71:0e:ff:30:b5:1d:e3:
bf:a3:81:02:2d:1a:a6:91:f6:ce:f7:8b:20:63:77:
0f:4b:86:0d:66:aa:10:73:07:fa:bd:d5:ad:17:d3:
0f:33:4f:9c:61:e0:7c:c3:3f:0b:b5:a4:a9:22:60:
63:0e:0e:3e:6c:2d:06:d5:2d:3a:12:13:d2:93:76:
54:2f:ef:5d:fe:aa:6a:ed:1c:81:2e:aa:a3:a7:81:
36:5d
Exponent: 65537 (0x10001)
X509v3 extensions:
X509v3 Subject Key Identifier:
D2:B9:76:2D:1F:ED:AF:41:05:C8:BE:89:27:63:32:4E:A9:C9:F8:C1
X509v3 Authority Key Identifier:
keyid:D2:B9:76:2D:1F:ED:AF:41:05:C8:BE:89:27:63:32:4E:A9:C9:F8:C1
X509v3 Basic Constraints: critical
CA:TRUE
Signature Algorithm: sha256WithRSAEncryption
0d:ac:42:71:3f:6b:3e:af:6a:ff:1d:d4:d3:b5:02:ff:c5:d6:
b8:24:c2:00:70:30:c1:4a:99:4c:f2:40:e3:35:ab:78:21:21:
04:0a:69:ec:5e:19:be:83:50:57:d2:f0:07:8b:16:7d:37:41:
23:fa:50:e0:fb:04:8c:08:4f:e1:c9:76:82:5b:66:59:46:b2:
42:5e:02:d3:4e:ee:d8:25:f6:61:f4:53:d9:7c:c3:5e:6a:02:
6a:cc:f2:f9:4a:7e:07:f1:18:55:d3:84:e4:be:a0:e6:3e:cc:
0b:0b:c0:91:f9:0d:71:7e:22:5b:96:1d:50:3d:17:a2:55:89:
92:dc:01:c0:5e:16:0b:42:ce:9c:94:32:44:18:6f:fb:f0:ff:
0d:f6:59:e1:90:fe:4a:13:ce:0f:a1:db:ca:8d:38:34:0d:6e:
e0:f2:ab:1c:a6:1d:21:c2:ea:fc:ad:30:6e:f4:05:35:a9:35:
9e:39:58:ec:46:36:76:15:3d:88:88:5b:ee:85:32:13:3e:ca:
01:cd:de:ea:ea:da:d2:1f:27:6d:fb:7c:ad:2b:ed:28:57:62:
85:71:e2:f4:51:92:78:fe:6c:31:de:86:2c:d5:26:e2:f2:3a:
73:40:b9:54:df:58:cd:4b:f6:10:8a:10:0d:55:73:7f:6b:df:
07:a9:ac:6a
-----BEGIN CERTIFICATE-----
MIIDszCCApugAwIBAgIUIH9YFWtLdeRJ1cCXXyhWFhxvmKkwDQYJKoZIhvcNAQEL
BQAwaTELMAkGA1UEBhMCRlIxEzARBgNVBAgMClNvbWUtU3RhdGUxDjAMBgNVBAcM
BVBhcmlzMSEwHwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQxEjAQBgNV
BAMMCWxvY2FsaG9zdDAeFw0yMTA5MDkxMjUzMTRaFw0zMTA5MDcxMjUzMTRaMGkx
CzAJBgNVBAYTAkZSMRMwEQYDVQQIDApTb21lLVN0YXRlMQ4wDAYDVQQHDAVQYXJp
czEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMRIwEAYDVQQDDAls
b2NhbGhvc3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC6agaTqCei
+7lygVYBlr/dnGgotdHFkK+hWkPNUuC+h++rBqQkrC6lqix8wADGGmcE0Dau+m80
SdJK445idakmnMP6M+y8JYO2zg86QZHbGSnTOrY7qLFlBhNr6nrJ6yA4zUAJB9nS
k906KgRA49UNgOy7Xl/RuQVZxf8QzJKRQ0EO1QAL5uX2HU6JT+noFCDb5Qz6tEBw
hXFFULyrJVrGeaBNUdGaFW8Vf9dRcQ7/MLUd47+jgQItGqaR9s73iyBjdw9Lhg1m
qhBzB/q91a0X0w8zT5xh4HzDPwu1pKkiYGMODj5sLQbVLToSE9KTdlQv713+qmrt
HIEuqqOngTZdAgMBAAGjUzBRMB0GA1UdDgQWBBTSuXYtH+2vQQXIvoknYzJOqcn4
wTAfBgNVHSMEGDAWgBTSuXYtH+2vQQXIvoknYzJOqcn4wTAPBgNVHRMBAf8EBTAD
AQH/MA0GCSqGSIb3DQEBCwUAA4IBAQANrEJxP2s+r2r/HdTTtQL/xda4JMIAcDDB
SplM8kDjNat4ISEECmnsXhm+g1BX0vAHixZ9N0Ej+lDg+wSMCE/hyXaCW2ZZRrJC
XgLTTu7YJfZh9FPZfMNeagJqzPL5Sn4H8RhV04TkvqDmPswLC8CR+Q1xfiJblh1Q
PReiVYmS3AHAXhYLQs6clDJEGG/78P8N9lnhkP5KE84PodvKjTg0DW7g8qscph0h
wur8rTBu9AU1qTWeOVjsRjZ2FT2IiFvuhTITPsoBzd7q6trSHydt+3ytK+0oV2KF
ceL0UZJ4/mwx3oYs1Sbi8jpzQLlU31jNS/YQihANVXN/a98Hqaxq
-----END CERTIFICATE-----