Skip to content

Commit

Permalink
Issue 6324 - Provide more information in the error message during set…
Browse files Browse the repository at this point in the history
…up_ol_tls_conn() (#6325)

Description: When there's a problem with creating a new TLS context, we just fail with -1 error code.
We can improve it by providing more information that can be extracted from LDAP_OPT_DIAGNOSTIC_MESSAGE or LDAP_OPT_ERROR_STRING.

Use slapi_ldap_get_lderrno to get more information from the ld structure.
Add a test case.

Fixes: #6324

Reviewed by: @progier389 (Thanks!)
  • Loading branch information
droideck committed Sep 18, 2024
1 parent 8c86704 commit 868b6d6
Show file tree
Hide file tree
Showing 2 changed files with 180 additions and 16 deletions.
140 changes: 140 additions & 0 deletions dirsrvtests/tests/suites/tls/tls_repl_clientauth_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
# --- BEGIN COPYRIGHT BLOCK ---
# Copyright (C) 2024 Red Hat, Inc.
# All rights reserved.
#
# License: GPL (version 3 or any later version).
# See LICENSE for details.
# --- END COPYRIGHT BLOCK ---
#
import os
import pytest
import logging
from lib389.replica import Replicas, ReplicationManager
from lib389._constants import DEFAULT_SUFFIX
from lib389.config import CertmapLegacy
from lib389.idm.services import ServiceAccounts
from lib389.topologies import topology_m2 as topo

pytestmark = pytest.mark.tier1

DEBUGGING = os.getenv("DEBUGGING", default=False)
if DEBUGGING:
logging.getLogger(__name__).setLevel(logging.DEBUG)
else:
logging.getLogger(__name__).setLevel(logging.INFO)
log = logging.getLogger(__name__)


@pytest.fixture(scope="module")
def topo_tls_ldapi(topo):
"""Enable TLS on both suppliers and reconfigure both agreements
to use TLS Client auth. Also, setup ldapi and export DB
"""

m1, m2 = topo.ms["supplier1"], topo.ms["supplier2"]

# Create and configure certmaps
cm_m1, cm_m2 = CertmapLegacy(m1), CertmapLegacy(m2)
certmaps = cm_m1.list()
certmaps['default'].update({'DNComps': None, 'CmapLdapAttr': 'nsCertSubjectDN'})
cm_m1.set(certmaps)
cm_m2.set(certmaps)

# Enable TLS on both instances
for instance in topo:
instance.enable_tls()

# Create replication DNs
services = ServiceAccounts(m1, DEFAULT_SUFFIX)
for instance in (m1, m2):
repl = services.get(f'{instance.host}:{instance.sslport}')
repl.set('nsCertSubjectDN', instance.get_server_tls_subject())

# Check the replication is "done".
repl = ReplicationManager(DEFAULT_SUFFIX)
repl.wait_for_replication(m1, m2)

# Now change the auth type
for instance, other in [(m1, m2), (m2, m1)]:
replica = Replicas(instance).get(DEFAULT_SUFFIX)
agmt = replica.get_agreements().list()[0]
agmt.replace_many(
('nsDS5ReplicaBindMethod', 'SSLCLIENTAUTH'),
('nsDS5ReplicaTransportInfo', 'SSL'),
('nsDS5ReplicaPort', str(other.sslport)),
)
agmt.remove_all('nsDS5ReplicaBindDN')

# Set up LDAPI
for instance in topo:
instance.config.set('nsslapd-ldapilisten', 'on')
instance.config.set('nsslapd-ldapifilepath', f'/var/run/slapd-{instance.serverid}.socket')
instance.restart()

repl.test_replication(m1, m2)
repl.test_replication(m2, m1)

return topo


def find_ca_files():
ca_files = []
for root, dirs, files in os.walk('/tmp'):
if 'Self-Signed-CA.pem' in files and 'dirsrv@' in root:
ca_files.append(os.path.join(root, 'Self-Signed-CA.pem'))
return ca_files


def test_new_tls_context_error(topo_tls_ldapi):
"""Test TLS context error when CA certificate is removed
:id: 88d4c841-9f91-499b-ba30-f834225effd8
:setup: Two supplier replication with SSLCLIENTAUTH agmts
:steps:
1. Remove tmp's Self-Signed-CA.pem dirsrv file
2. Reinit agreement
3. Check errors log and make sure the detailed error is there
:expectedresults:
1. Self-Signed-CA.pem file is removed
2. Replication reinitialization fails
3. Error log contains the expected SSL certificate verify failed message
"""

m1 = topo_tls_ldapi.ms["supplier1"]

log.info('Find and remove Self-Signed-CA.pem dirsrv files')
ca_files = find_ca_files()
if not ca_files:
pytest.skip("No Self-Signed-CA.pem files found. Skipping test.")

log.info(f'Found {len(ca_files)} Self-Signed-CA.pem files')
log.info(f'CA files are: {", ".join(ca_files)}')

for ca_file in ca_files:
try:
os.remove(ca_file)
log.info(f"Removed file: {ca_file}")
except OSError as e:
log.info(f"Error removing file {ca_file}: {e}")

log.info('Reinit agreement')
replica_m1 = Replicas(m1).get(DEFAULT_SUFFIX)
agmt_m1 = replica_m1.get_agreements().list()[0]
agmt_m1.begin_reinit()
agmt_m1.wait_reinit()

log.info('Restart the server to flush logs')
m1.restart()

log.info('Check errors log for certificate verify failed message')
error_msg = "error:80000002:system library::No such file or directory"
assert m1.searchErrorsLog(error_msg), f"Expected error message not found: {error_msg}"

log.info('Test completed successfully')


if __name__ == '__main__':
# Run isolated
# -s for DEBUG mode
CURRENT_FILE = os.path.realpath(__file__)
pytest.main(["-s", CURRENT_FILE])
56 changes: 40 additions & 16 deletions ldap/servers/slapd/ldaputil.c
Original file line number Diff line number Diff line change
Expand Up @@ -508,6 +508,7 @@ setup_ol_tls_conn(LDAP *ld, int clientauth)
int ssl_strength = 0;
int rc = 0;
const char *cacert = NULL;
char *errmsg = NULL;

/* certdir is used to setup outgoing secure connection (openldap)
* It refers to the place where PEM files have been extracted
Expand All @@ -526,19 +527,25 @@ setup_ol_tls_conn(LDAP *ld, int clientauth)
ssl_strength = LDAP_OPT_X_TLS_NEVER;
}

if (ldap_set_option(ld, LDAP_OPT_X_TLS_REQUIRE_CERT, &ssl_strength)) {
rc = ldap_set_option(ld, LDAP_OPT_X_TLS_REQUIRE_CERT, &ssl_strength);
if (rc != LDAP_SUCCESS) {
rc = slapi_ldap_get_lderrno(ld, NULL, &errmsg);
slapi_log_err(SLAPI_LOG_ERR, "setup_ol_tls_conn",
"failed: unable to set REQUIRE_CERT option to %d\n", ssl_strength);
"failed: unable to set REQUIRE_CERT option to %d: %d (%s) %s\n",
ssl_strength, rc, ldap_err2string(rc), errmsg ? errmsg : "");
slapi_ch_free_string(&errmsg);
}
if (slapi_client_uses_non_nss(ld) && config_get_extract_pem()) {
cacert = slapi_get_cacertfile();
if (cacert) {
/* CA Cert PEM file exists. Set the path to openldap option. */
rc = ldap_set_option(ld, LDAP_OPT_X_TLS_CACERTFILE, cacert);
if (rc) {
if (rc != LDAP_SUCCESS) {
rc = slapi_ldap_get_lderrno(ld, NULL, &errmsg);
slapi_log_err(SLAPI_LOG_ERR, "setup_ol_tls_conn",
"Could not set CA cert path [%s]: %d:%s\n",
cacert, rc, ldap_err2string(rc));
"Could not set CA cert path [%s]: %d (%s) %s\n",
cacert, rc, ldap_err2string(rc), errmsg ? errmsg : "");
slapi_ch_free_string(&errmsg);
}
}
}
Expand All @@ -552,43 +559,60 @@ setup_ol_tls_conn(LDAP *ld, int clientauth)
}
/* Sets the CRL evaluation strategy. */
rc = ldap_set_option(ld, LDAP_OPT_X_TLS_CRLCHECK, &crlcheck);
if (rc) {
if (rc != LDAP_SUCCESS) {
rc = slapi_ldap_get_lderrno(ld, NULL, &errmsg);
slapi_log_err(SLAPI_LOG_ERR, "setup_ol_tls_conn",
"Could not set CRLCHECK [%d]: %d:%s\n",
crlcheck, rc, ldap_err2string(rc));
"Could not set CRLCHECK [%d]: %d (%s) %s\n",
crlcheck, rc, ldap_err2string(rc), errmsg ? errmsg : "");
slapi_ch_free_string(&errmsg);
}
}
/* tell it where our cert db/file is */
if (ldap_set_option(ld, LDAP_OPT_X_TLS_CACERTDIR, certdir)) {
rc = ldap_set_option(ld, LDAP_OPT_X_TLS_CACERTDIR, certdir);
if (rc != LDAP_SUCCESS) {
rc = slapi_ldap_get_lderrno(ld, NULL, &errmsg);
slapi_log_err(SLAPI_LOG_ERR, "setup_ol_tls_conn",
"failed: unable to set CACERTDIR option to %s\n", certdir);
"failed: unable to set CACERTDIR option to %s: %d (%s) %s\n",
certdir, rc, ldap_err2string(rc), errmsg ? errmsg : "");
slapi_ch_free_string(&errmsg);
}
slapi_ch_free_string(&certdir);
#if defined(LDAP_OPT_X_TLS_PROTOCOL_MIN)
getSSLVersionRangeOL(&optval, NULL);
if (ldap_set_option(ld, LDAP_OPT_X_TLS_PROTOCOL_MIN, &optval)) {
rc = ldap_set_option(ld, LDAP_OPT_X_TLS_PROTOCOL_MIN, &optval);
if (rc != LDAP_SUCCESS) {
char *minstr = NULL;
(void)getSSLVersionRange(&minstr, NULL);
rc = slapi_ldap_get_lderrno(ld, NULL, &errmsg);
slapi_log_err(SLAPI_LOG_ERR, "setup_ol_tls_conn",
"failed: unable to set minimum TLS protocol level to %s\n", minstr);
"failed: unable to set minimum TLS protocol level to %s: %d (%s) %s\n",
minstr, rc, ldap_err2string(rc), errmsg ? errmsg : "");
slapi_ch_free_string(&minstr);
slapi_ch_free_string(&errmsg);
}
#endif /* LDAP_OPT_X_TLS_PROTOCOL_MIN */
if (clientauth) {
rc = slapd_SSL_client_auth(ld);
if (rc) {
if (rc != LDAP_SUCCESS) {
rc = slapi_ldap_get_lderrno(ld, NULL, &errmsg);
slapi_log_err(SLAPI_LOG_ERR, "setup_ol_tls_conn",
"failed: unable to setup connection for TLS/SSL EXTERNAL client cert authentication - %d\n", rc);
"failed: unable to setup connection for TLS/SSL EXTERNAL client cert authentication: %d (%s) %s\n",
rc, ldap_err2string(rc), errmsg ? errmsg : "");
slapi_ch_free_string(&errmsg);
}
}

/* have to do this last - this creates the new TLS handle and sets/copies
all of the parameters set above into that TLS handle context - note
that optval is zero, meaning create a context for a client */
optval = 0;
if ((rc = ldap_set_option(ld, LDAP_OPT_X_TLS_NEWCTX, &optval))) {
rc = ldap_set_option(ld, LDAP_OPT_X_TLS_NEWCTX, &optval);
if (rc != LDAP_SUCCESS) {
rc = slapi_ldap_get_lderrno(ld, NULL, &errmsg);
slapi_log_err(SLAPI_LOG_ERR, "setup_ol_tls_conn",
"failed: unable to create new TLS context - %d\n", rc);
"failed: unable to create new TLS context: %d (%s) %s\n",
rc, ldap_err2string(rc), errmsg ? errmsg : "");
slapi_ch_free_string(&errmsg);
}

return rc;
Expand Down

0 comments on commit 868b6d6

Please sign in to comment.