diff --git a/symmetric-core/src/main/java/org/jumpmind/symmetric/AbstractSymmetricEngine.java b/symmetric-core/src/main/java/org/jumpmind/symmetric/AbstractSymmetricEngine.java index d4240c3f43..c422c2eeb1 100644 --- a/symmetric-core/src/main/java/org/jumpmind/symmetric/AbstractSymmetricEngine.java +++ b/symmetric-core/src/main/java/org/jumpmind/symmetric/AbstractSymmetricEngine.java @@ -28,6 +28,7 @@ import java.lang.reflect.Method; import java.net.MalformedURLException; import java.net.URL; +import java.security.UnrecoverableKeyException; import java.sql.SQLException; import java.util.ArrayList; import java.util.Date; @@ -56,6 +57,7 @@ import org.jumpmind.symmetric.common.Constants; import org.jumpmind.symmetric.common.ParameterConstants; import org.jumpmind.symmetric.common.TableConstants; +import org.jumpmind.symmetric.config.INodeIdCreator; import org.jumpmind.symmetric.db.ISoftwareUpgradeListener; import org.jumpmind.symmetric.db.ISymmetricDialect; import org.jumpmind.symmetric.ext.ISymmetricEngineLifecycle; @@ -70,6 +72,7 @@ import org.jumpmind.symmetric.model.ProcessInfo; import org.jumpmind.symmetric.model.ProcessInfo.ProcessStatus; import org.jumpmind.symmetric.model.RemoteNodeStatuses; +import org.jumpmind.symmetric.security.INodePasswordFilter; import org.jumpmind.symmetric.service.IAcknowledgeService; import org.jumpmind.symmetric.service.IBandwidthService; import org.jumpmind.symmetric.service.IClusterService; @@ -141,6 +144,7 @@ import org.jumpmind.symmetric.transport.TransportManagerFactory; import org.jumpmind.symmetric.util.PropertiesUtil; import org.jumpmind.util.AppUtils; +import org.jumpmind.util.ExceptionUtils; import org.jumpmind.util.FormatUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -613,7 +617,7 @@ public synchronized boolean start(boolean startJobs) { setup(); if (isConfigured()) { Node node = nodeService.findIdentity(); - checkSystemIntegrity(node); + node = checkSystemIntegrity(node); isInitialized = true; if (node != null) { log.info( @@ -682,7 +686,7 @@ public synchronized boolean start(boolean startJobs) { return started; } - protected void checkSystemIntegrity(Node node) { + protected Node checkSystemIntegrity(Node node) { if (node != null && (!node.getExternalId().equals(getParameterService().getExternalId()) || !node.getNodeGroupId().equals(getParameterService().getNodeGroupId()))) { if (parameterService.is(ParameterConstants.NODE_COPY_MODE_ENABLED, false)) { @@ -705,6 +709,53 @@ protected void checkSystemIntegrity(Node node) { node != null ? node.getNodeId() : "null", ParameterConstants.INITIAL_LOAD_USE_EXTRACT_JOB, useExtractJob, ParameterConstants.STREAM_TO_FILE_ENABLED, streamToFile)); } + INodePasswordFilter filter = extensionService.getExtensionPoint(INodePasswordFilter.class); + if (filter != null) { + log.info("Testing keystore integrity"); + try { + securityService.encrypt(ParameterConstants.EXTERNAL_ID); + } catch (Exception e) { + if (ExceptionUtils.is(e, UnrecoverableKeyException.class)) { + throw new SymmetricException("Failed to open keystore because keystore password is wrong. " + + "Check javax.net.ssl.keyStorePassword in conf/sym_service.conf and bin/setenv.", e); + } + throw e; + } + log.info("Testing node security integrity"); + Map nodeSecurities = nodeService.findAllNodeSecurity(false); + List badNodeSecurities = new ArrayList(); + for (NodeSecurity nodeSecurity : nodeSecurities.values()) { + if (StringUtils.isBlank(nodeSecurity.getNodePassword())) { + badNodeSecurities.add(nodeSecurity); + } + } + if (badNodeSecurities.size() > 0) { + if (parameterService.is(ParameterConstants.CLUSTER_LOCKING_ENABLED)) { + throw new IllegalStateException("Unable to decrypt " + badNodeSecurities.size() + + " node security rows. Copy the security/keystore file from a working node in the cluster."); + } else if (parameterService.isRegistrationServer()) { + log.error("Found {} bad node securities. Attempting to re-open registration to fix them.", badNodeSecurities.size()); + String myNodeId = nodeService.findIdentityNodeId(); + for (NodeSecurity nodeSecurity : badNodeSecurities) { + if (nodeSecurity.getNodeId().equals(myNodeId)) { + log.info("Re-generating my node password"); + String password = extensionService.getExtensionPoint(INodeIdCreator.class).generatePassword(node); + nodeSecurity.setNodePassword(password); + nodeService.updateNodeSecurity(nodeSecurity); + } else { + registrationService.reOpenRegistration(nodeSecurity.getNodeId()); + } + } + } else { + log.error( + "Found {} bad node securities. Removing identity and attempting re-registration to fix them. You may need to approve the registration request.", + badNodeSecurities.size()); + nodeService.deleteIdentity(); + node = null; + } + } + } + return node; } public String getEngineDescription(String msg) { diff --git a/symmetric-core/src/main/java/org/jumpmind/symmetric/service/impl/MailService.java b/symmetric-core/src/main/java/org/jumpmind/symmetric/service/impl/MailService.java index 1fff5971ce..7393fa2554 100644 --- a/symmetric-core/src/main/java/org/jumpmind/symmetric/service/impl/MailService.java +++ b/symmetric-core/src/main/java/org/jumpmind/symmetric/service/impl/MailService.java @@ -233,7 +233,12 @@ protected Properties getJavaMailProperties(TypedProperties typedProp) { protected String decryptPassword(String password) { if (password != null && password.startsWith(SecurityConstants.PREFIX_ENC)) { - return securityService.decrypt(password.substring(SecurityConstants.PREFIX_ENC.length())); + try { + return securityService.decrypt(password.substring(SecurityConstants.PREFIX_ENC.length())); + } catch (Exception e) { + throw new IllegalStateException( + "Failed to decrypt the mail server password. Please re-enter the password on the Configure -> Mail Server screen", e); + } } return password; } diff --git a/symmetric-jdbc/src/main/java/org/jumpmind/db/util/BasicDataSourceFactory.java b/symmetric-jdbc/src/main/java/org/jumpmind/db/util/BasicDataSourceFactory.java index 648d7b24c5..2c0a97be1e 100644 --- a/symmetric-jdbc/src/main/java/org/jumpmind/db/util/BasicDataSourceFactory.java +++ b/symmetric-jdbc/src/main/java/org/jumpmind/db/util/BasicDataSourceFactory.java @@ -91,7 +91,12 @@ public static ResettableBasicDataSource create(TypedProperties properties, dataSource.setUrl(properties.get(BasicDataSourcePropertyConstants.DB_POOL_URL, null)); String user = properties.get(BasicDataSourcePropertyConstants.DB_POOL_USER, ""); if (user != null && user.startsWith(SecurityConstants.PREFIX_ENC)) { - user = securityService.decrypt(user.substring(SecurityConstants.PREFIX_ENC.length())); + try { + user = securityService.decrypt(user.substring(SecurityConstants.PREFIX_ENC.length())); + } catch (Exception ex) { + throw new IllegalStateException("Failed to decrypt the database user from your engine properties file stored under the " + + BasicDataSourcePropertyConstants.DB_POOL_USER + " property. Please re-encrypt your user", ex); + } } dataSource.setUsername(user); String password = properties.get(BasicDataSourcePropertyConstants.DB_POOL_PASSWORD, ""); diff --git a/symmetric-util/src/main/java/org/jumpmind/util/ExceptionUtils.java b/symmetric-util/src/main/java/org/jumpmind/util/ExceptionUtils.java index 85ebbabdf3..c527b485af 100644 --- a/symmetric-util/src/main/java/org/jumpmind/util/ExceptionUtils.java +++ b/symmetric-util/src/main/java/org/jumpmind/util/ExceptionUtils.java @@ -49,4 +49,16 @@ public static Throwable getRootCause(Throwable ex) { } return cause; } + + public static boolean is(Exception e, Class... exceptions) { + if (e != null) { + Throwable cause = getRootCause(e); + for (Class ex : exceptions) { + if (ex.isInstance(e) || ex.isInstance(cause)) { + return true; + } + } + } + return false; + } }