Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,15 @@ public class TrustStoreBackend extends LocalBackend<TrustStoreBackendCfg>

/** The certificate manager for the trust store. */
private CertificateManager certificateManager;
/**
* Held over every access to the trust store file and to the certificate manager. A change
* reads the file, changes that copy and writes the whole file back over the old one, and the
* certificate manager keeps the copy it read last. Replication replays the instance keys of
* cn=admin data on parallel threads, each of which changes the file here, and an SSL context
* is built from the file for every replication connection: unserialized, one of two changes
* is lost, and a reader finds the file half written (#1188).
*/
private final Object trustStoreLock = new Object();
/** The server context. */
private ServerContext serverContext;

Expand Down Expand Up @@ -183,10 +192,13 @@ public void openBackend() throws ConfigException, InitializationException

trustStorePIN = getTrustStorePIN(configuration, true);
final String keyStorePath = getFileForPath(trustStoreFile).getPath();
certificateManager = new CertificateManager(keyStorePath, trustStoreType, trustStorePIN);
synchronized (trustStoreLock)
{
certificateManager = new CertificateManager(keyStorePath, trustStoreType, trustStorePIN);

// Generate a self-signed certificate, if there is none.
generateInstanceCertificateIfAbsent();
// Generate a self-signed certificate, if there is none.
generateInstanceCertificateIfAbsent();
}

// Construct the trust store base entry.
LinkedHashMap<ObjectClass,String> objectClasses = new LinkedHashMap<>(2);
Expand Down Expand Up @@ -327,7 +339,11 @@ public long getEntryCount()

try
{
String[] aliases = certificateManager.getCertificateAliases();
String[] aliases;
synchronized (trustStoreLock)
{
aliases = certificateManager.getCertificateAliases();
}
if (aliases != null)
{
numEntries += aliases.length;
Expand Down Expand Up @@ -409,7 +425,11 @@ private Entry getCertEntry(DN entryDN)
ByteString certValue;
try
{
Certificate cert = certificateManager.getCertificate(certAlias);
Certificate cert;
synchronized (trustStoreLock)
{
cert = certificateManager.getCertificate(certAlias);
}
if (cert == null)
{
LocalizableMessage message = ERR_TRUSTSTORE_CERTIFICATE_NOT_FOUND.get(entryDN, certAlias);
Expand Down Expand Up @@ -468,7 +488,10 @@ public void addEntry(Entry entry, AddOperation addOperation)

if (parentDN.equals(getBaseDN()))
{
addCertificate(entry);
synchronized (trustStoreLock)
{
addCertificate(entry);
}
}
else
{
Expand All @@ -494,7 +517,10 @@ public void deleteEntry(DN entryDN, DeleteOperation deleteOperation)
throw new DirectoryException(ResultCode.NO_SUCH_OBJECT, message);
}

deleteCertificate(entryDN);
synchronized (trustStoreLock)
{
deleteCertificate(entryDN);
}
}

@Override
Expand Down Expand Up @@ -538,7 +564,10 @@ public void search(SearchOperation searchOperation)
String[] aliases = null;
try
{
aliases = certificateManager.getCertificateAliases();
synchronized (trustStoreLock)
{
aliases = certificateManager.getCertificateAliases();
}
}
catch (KeyStoreException e)
{
Expand Down Expand Up @@ -772,12 +801,15 @@ public ConfigChangeResult applyConfigurationChange(TrustStoreBackendCfg cfg)

if (ccr.getResultCode() == ResultCode.SUCCESS)
{
trustStoreFile = newTrustStoreFile;
trustStoreType = newTrustStoreType;
trustStorePIN = newPIN;
configuration = cfg;
final String keyStorePath = getFileForPath(trustStoreFile).getPath();
certificateManager = new CertificateManager(keyStorePath, trustStoreType, trustStorePIN);
synchronized (trustStoreLock)
{
trustStoreFile = newTrustStoreFile;
trustStoreType = newTrustStoreType;
trustStorePIN = newPIN;
configuration = cfg;
final String keyStorePath = getFileForPath(trustStoreFile).getPath();
certificateManager = new CertificateManager(keyStorePath, trustStoreType, trustStorePIN);
}
}

return ccr;
Expand All @@ -802,24 +834,28 @@ private static DN makeChildDN(DN parentDN, AttributeType rdnAttrType, String rdn
public KeyManager[] getKeyManagers()
throws DirectoryException
{
final KeyStore keyStore = loadKeyStore();

try
{
String keyManagerAlgorithm = KeyManagerFactory.getDefaultAlgorithm();
KeyManagerFactory keyManagerFactory =
KeyManagerFactory.getInstance(keyManagerAlgorithm);
keyManagerFactory.init(keyStore, trustStorePIN);
return keyManagerFactory.getKeyManagers();
}
catch (Exception e)
// the keys are opened with the PIN of the file they were read from, not that of a new one
synchronized (trustStoreLock)
{
logger.traceException(e);
final KeyStore keyStore = loadKeyStore();

LocalizableMessage message = ERR_TRUSTSTORE_CANNOT_CREATE_FACTORY.get(
trustStoreFile, getExceptionMessage(e));
throw new DirectoryException(DirectoryServer.getCoreConfigManager().getServerErrorResultCode(),
message, e);
try
{
String keyManagerAlgorithm = KeyManagerFactory.getDefaultAlgorithm();
KeyManagerFactory keyManagerFactory =
KeyManagerFactory.getInstance(keyManagerAlgorithm);
keyManagerFactory.init(keyStore, trustStorePIN);
return keyManagerFactory.getKeyManagers();
}
catch (Exception e)
{
logger.traceException(e);

LocalizableMessage message = ERR_TRUSTSTORE_CANNOT_CREATE_FACTORY.get(
trustStoreFile, getExceptionMessage(e));
throw new DirectoryException(DirectoryServer.getCoreConfigManager().getServerErrorResultCode(),
message, e);
}
}
}

Expand Down Expand Up @@ -864,16 +900,19 @@ public boolean containsKeyWithAlias(String alias) throws DirectoryException

private KeyStore loadKeyStore() throws DirectoryException
{
try (FileInputStream inputStream = new FileInputStream(getFileForPath(trustStoreFile)))
synchronized (trustStoreLock)
{
final KeyStore keyStore = KeyStore.getInstance(trustStoreType);
keyStore.load(inputStream, trustStorePIN);
return keyStore;
}
catch (Exception e)
{
LocalizableMessage message = ERR_TRUSTSTORE_CANNOT_LOAD.get(trustStoreFile, getExceptionMessage(e));
throw new DirectoryException(DirectoryServer.getCoreConfigManager().getServerErrorResultCode(), message, e);
try (FileInputStream inputStream = new FileInputStream(getFileForPath(trustStoreFile)))
{
final KeyStore keyStore = KeyStore.getInstance(trustStoreType);
keyStore.load(inputStream, trustStorePIN);
return keyStore;
}
catch (Exception e)
{
LocalizableMessage message = ERR_TRUSTSTORE_CANNOT_LOAD.get(trustStoreFile, getExceptionMessage(e));
throw new DirectoryException(DirectoryServer.getCoreConfigManager().getServerErrorResultCode(), message, e);
}
}
}

Expand Down Expand Up @@ -925,20 +964,24 @@ public TrustManager[] getTrustManagers()
public Key getKey(String alias)
throws DirectoryException
{
KeyStore trustStore = loadKeyStore();

try
// the key is opened with the PIN of the file it was read from, not that of a new one
synchronized (trustStoreLock)
{
return trustStore.getKey(alias, trustStorePIN);
}
catch (Exception e)
{
logger.traceException(e);
KeyStore trustStore = loadKeyStore();

LocalizableMessage message = ERR_TRUSTSTORE_ERROR_READING_KEY.get(
alias, trustStoreFile, getExceptionMessage(e));
throw new DirectoryException(DirectoryServer.getCoreConfigManager().getServerErrorResultCode(),
message, e);
try
{
return trustStore.getKey(alias, trustStorePIN);
}
catch (Exception e)
{
logger.traceException(e);

LocalizableMessage message = ERR_TRUSTSTORE_ERROR_READING_KEY.get(
alias, trustStoreFile, getExceptionMessage(e));
throw new DirectoryException(DirectoryServer.getCoreConfigManager().getServerErrorResultCode(),
message, e);
}
}
}

Expand Down Expand Up @@ -1047,6 +1090,12 @@ private void addCertificate(Entry entry)
}
}
}
catch (DirectoryException e)
{
// a refusal made above keeps its result code: an alias in use is an entry which exists,
// not a failure of the server
throw e;
}
catch (Exception e)
{
LocalizableMessage message = ERR_TRUSTSTORE_CANNOT_ADD_CERT.get(
Expand Down Expand Up @@ -1079,6 +1128,11 @@ private void deleteCertificate(DN entryDN)

certificateManager.removeCertificate(certAlias);
}
catch (DirectoryException e)
{
// an alias not in use is an entry which does not exist, not a failure of the server
throw e;
}
catch (Exception e)
{
LocalizableMessage message = ERR_TRUSTSTORE_CANNOT_DELETE_CERT.get(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
*
* Copyright 2008-2010 Sun Microsystems, Inc.
* Portions Copyright 2014-2016 ForgeRock AS.
* Portions Copyright 2026 3A Systems, LLC.
*/
package org.opends.server.crypto;

Expand Down Expand Up @@ -359,9 +360,15 @@ private void modifyEntry(Entry srcEntry, Entry dstEntry)
private static void deleteEntry(DN dstDN)
{
DeleteOperation delOperation = getRootConnection().processDelete(dstDN);
if (delOperation.getResultCode() != ResultCode.SUCCESS)
if (delOperation.getResultCode() == ResultCode.NO_SUCH_OBJECT)
{
logger.debug(INFO_TRUSTSTORESYNC_DELETE_FAILED, dstDN, delOperation.getErrorMessage());
// The certificate has left the trust store already, as it does when its key is marked compromised.
logger.debug(ERR_TRUSTSTORESYNC_DELETE_FAILED, dstDN, delOperation.getErrorMessage());
}
else if (delOperation.getResultCode() != ResultCode.SUCCESS)
{
// While the certificate is there, this server goes on trusting whoever holds the key.
logger.error(ERR_TRUSTSTORESYNC_DELETE_FAILED, dstDN, delOperation.getErrorMessage());
}
}

Expand All @@ -383,9 +390,16 @@ private void addEntry(Entry srcEntry, DN dstDN)

Entry addEntry = new Entry(dstDN, ocMap, userAttrs, null);
AddOperation addOperation = getRootConnection().processAdd(addEntry);
if (addOperation.getResultCode() != ResultCode.SUCCESS)
if (addOperation.getResultCode() == ResultCode.ENTRY_ALREADY_EXISTS)
{
// dsreplication seeds the trust store of the servers it enables: the certificate is there already.
logger.debug(ERR_TRUSTSTORESYNC_ADD_FAILED, dstDN, addOperation.getErrorMessage());
}
else if (addOperation.getResultCode() != ResultCode.SUCCESS)
{
logger.debug(INFO_TRUSTSTORESYNC_ADD_FAILED, dstDN, addOperation.getErrorMessage());
// Without the certificate this server does not trust the server the key belongs to, and
// every replication connection between the two fails its handshake (#1188).
logger.error(ERR_TRUSTSTORESYNC_ADD_FAILED, dstDN, addOperation.getErrorMessage());
}
}

Expand Down
Loading
Loading