diff options
| author | Robert Gemmell <robbie@apache.org> | 2013-04-12 16:16:09 +0000 |
|---|---|---|
| committer | Robert Gemmell <robbie@apache.org> | 2013-04-12 16:16:09 +0000 |
| commit | 249369d22526b77b3ffa4c456854b55c287cfd7b (patch) | |
| tree | d3706c9c525d196e824d1fdd51873ec275295eae /qpid/java/broker/src/main | |
| parent | 332410c66c62d5e075e9f9077d29fc4669e11db0 (diff) | |
| download | qpid-python-249369d22526b77b3ffa4c456854b55c287cfd7b.tar.gz | |
QPID-4739: complete support for defining multiple key/trust stores and assigning them on a port-specific basis
git-svn-id: https://svn.apache.org/repos/asf/qpid/trunk@1467334 13f79535-47bb-0310-9956-ffa450edef68
Diffstat (limited to 'qpid/java/broker/src/main')
14 files changed, 652 insertions, 324 deletions
diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/configuration/startup/BrokerRecoverer.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/configuration/startup/BrokerRecoverer.java index 0d7be75a0b..4fc0a37c3e 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/configuration/startup/BrokerRecoverer.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/configuration/startup/BrokerRecoverer.java @@ -1,23 +1,28 @@ package org.apache.qpid.server.configuration.startup; +import java.util.ArrayList; import java.util.Collection; +import java.util.HashMap; +import java.util.List; import java.util.Map; import org.apache.qpid.server.configuration.ConfigurationEntry; import org.apache.qpid.server.configuration.ConfiguredObjectRecoverer; import org.apache.qpid.server.configuration.IllegalConfigurationException; import org.apache.qpid.server.configuration.RecovererProvider; +import org.apache.qpid.server.configuration.store.StoreConfigurationChangeListener; +import org.apache.qpid.server.configuration.updater.TaskExecutor; import org.apache.qpid.server.logging.LogRecorder; import org.apache.qpid.server.logging.RootMessageLogger; import org.apache.qpid.server.model.AuthenticationProvider; import org.apache.qpid.server.model.Broker; import org.apache.qpid.server.model.ConfiguredObject; +import org.apache.qpid.server.model.KeyStore; import org.apache.qpid.server.model.Port; +import org.apache.qpid.server.model.TrustStore; import org.apache.qpid.server.model.adapter.AuthenticationProviderFactory; import org.apache.qpid.server.model.adapter.BrokerAdapter; import org.apache.qpid.server.model.adapter.PortFactory; -import org.apache.qpid.server.configuration.store.StoreConfigurationChangeListener; -import org.apache.qpid.server.configuration.updater.TaskExecutor; import org.apache.qpid.server.stats.StatisticsGatherer; import org.apache.qpid.server.virtualhost.VirtualHostRegistry; @@ -52,31 +57,62 @@ public class BrokerRecoverer implements ConfiguredObjectRecoverer<Broker> _logRecorder, _rootMessageLogger, _authenticationProviderFactory, _portFactory, _taskExecutor, entry.getStore()); broker.addChangeListener(storeChangeListener); - Map<String, Collection<ConfigurationEntry>> childEntries = entry.getChildren(); - for (String type : childEntries.keySet()) + + //Recover the SSL keystores / truststores first, then others that depend on them + Map<String, Collection<ConfigurationEntry>> childEntries = new HashMap<String, Collection<ConfigurationEntry>>(entry.getChildren()); + Map<String, Collection<ConfigurationEntry>> sslChildEntries = new HashMap<String, Collection<ConfigurationEntry>>(childEntries); + List<String> types = new ArrayList<String>(childEntries.keySet()); + + for(String type : types) { - ConfiguredObjectRecoverer<?> recoverer = recovererProvider.getRecoverer(type); - if (recoverer == null) + if(KeyStore.class.getSimpleName().equals(type) || TrustStore.class.getSimpleName().equals(type)) { - throw new IllegalConfigurationException("Cannot recover entry for the type '" + type + "' from broker"); + childEntries.remove(type); } - Collection<ConfigurationEntry> entries = childEntries.get(type); - for (ConfigurationEntry childEntry : entries) + else { - ConfiguredObject object = recoverer.create(recovererProvider, childEntry, broker); - if (object == null) - { - throw new IllegalConfigurationException("Cannot create configured object for the entry " + childEntry); - } - broker.recoverChild(object); - object.addChangeListener(storeChangeListener); + sslChildEntries.remove(type); } } + + for (String type : sslChildEntries.keySet()) + { + recoverType(recovererProvider, storeChangeListener, broker, sslChildEntries, type); + } + for (String type : childEntries.keySet()) + { + recoverType(recovererProvider, storeChangeListener, broker, childEntries, type); + } + wireUpConfiguredObjects(broker, entry.getAttributes()); return broker; } + private void recoverType(RecovererProvider recovererProvider, + StoreConfigurationChangeListener storeChangeListener, + BrokerAdapter broker, + Map<String, Collection<ConfigurationEntry>> childEntries, + String type) + { + ConfiguredObjectRecoverer<?> recoverer = recovererProvider.getRecoverer(type); + if (recoverer == null) + { + throw new IllegalConfigurationException("Cannot recover entry for the type '" + type + "' from broker"); + } + Collection<ConfigurationEntry> entries = childEntries.get(type); + for (ConfigurationEntry childEntry : entries) + { + ConfiguredObject object = recoverer.create(recovererProvider, childEntry, broker); + if (object == null) + { + throw new IllegalConfigurationException("Cannot create configured object for the entry " + childEntry); + } + broker.recoverChild(object); + object.addChangeListener(storeChangeListener); + } + } + private void wireUpConfiguredObjects(BrokerAdapter broker, Map<String, Object> brokerAttributes) { AuthenticationProvider defaultAuthenticationProvider = null; diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/model/Broker.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/model/Broker.java index ad0e68cee6..0e230bd552 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/model/Broker.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/model/Broker.java @@ -88,18 +88,6 @@ public interface Broker extends ConfiguredObject String ACL_FILE = "aclFile"; /* - * A temporary attributes to set the broker default key/trust stores. - * TODO: Remove them after adding a full support to configure KeyStore/TrustStore via management layers. - */ - String KEY_STORE_PATH = "keyStorePath"; - String KEY_STORE_PASSWORD = "keyStorePassword"; - String KEY_STORE_CERT_ALIAS = "keyStoreCertAlias"; - String TRUST_STORE_PATH = "trustStorePath"; - String TRUST_STORE_PASSWORD = "trustStorePassword"; - String PEER_STORE_PATH = "peerStorePath"; - String PEER_STORE_PASSWORD = "peerStorePassword"; - - /* * A temporary attributes to set the broker group file. * TODO: Remove them after adding a full support to configure authorization providers via management layers. */ @@ -148,16 +136,8 @@ public interface Broker extends ConfiguredObject VIRTUALHOST_STORE_TRANSACTION_IDLE_TIMEOUT_WARN, VIRTUALHOST_STORE_TRANSACTION_OPEN_TIMEOUT_CLOSE, VIRTUALHOST_STORE_TRANSACTION_OPEN_TIMEOUT_WARN, - - ACL_FILE, - KEY_STORE_PATH, - KEY_STORE_PASSWORD, - KEY_STORE_CERT_ALIAS, - TRUST_STORE_PATH, - TRUST_STORE_PASSWORD, - PEER_STORE_PATH, - PEER_STORE_PASSWORD, - GROUP_FILE + GROUP_FILE, + ACL_FILE )); //children @@ -194,6 +174,10 @@ public interface Broker extends ConfiguredObject VirtualHost findVirtualHostByName(String name); + KeyStore findKeyStoreByName(String name); + + TrustStore findTrustStoreByName(String name); + /** * Get the SubjectCreator for the given socket address. * TODO: move the authentication related functionality into host aliases and AuthenticationProviders @@ -211,10 +195,6 @@ public interface Broker extends ConfiguredObject */ VirtualHostRegistry getVirtualHostRegistry(); - KeyStore getDefaultKeyStore(); - - TrustStore getDefaultTrustStore(); - TaskExecutor getTaskExecutor(); boolean isManagementMode(); diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/model/KeyStore.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/model/KeyStore.java index 959714656b..74a7469ffb 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/model/KeyStore.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/model/KeyStore.java @@ -24,10 +24,23 @@ import java.util.Arrays; import java.util.Collection; import java.util.Collections; -public interface KeyStore extends TrustStore +public interface KeyStore extends ConfiguredObject { + String ID = "id"; + String NAME = "name"; + String DURABLE = "durable"; + String LIFETIME_POLICY = "lifetimePolicy"; + String STATE = "state"; + String TIME_TO_LIVE = "timeToLive"; + String CREATED = "created"; + String UPDATED = "updated"; + String DESCRIPTION = "description"; + String PATH = "path"; + String PASSWORD = "password"; + String TYPE = "type"; String CERTIFICATE_ALIAS = "certificateAlias"; + String KEY_MANAGER_FACTORY_ALGORITHM = "keyManagerFactoryAlgorithm"; public static final Collection<String> AVAILABLE_ATTRIBUTES = Collections.unmodifiableList( @@ -44,8 +57,11 @@ public interface KeyStore extends TrustStore PATH, PASSWORD, TYPE, - KEY_MANAGER_FACTORY_ALGORITHM, - CERTIFICATE_ALIAS + CERTIFICATE_ALIAS, + KEY_MANAGER_FACTORY_ALGORITHM )); + public String getPassword(); + + public void setPassword(String password); } diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/model/Port.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/model/Port.java index 33ba34767d..7ff5f04ee7 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/model/Port.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/model/Port.java @@ -45,6 +45,8 @@ public interface Port extends ConfiguredObject String NEED_CLIENT_AUTH = "needClientAuth"; String WANT_CLIENT_AUTH = "wantClientAuth"; String AUTHENTICATION_PROVIDER = "authenticationProvider"; + String KEY_STORE = "keyStore"; + String TRUST_STORES = "trustStores"; // Attributes public static final Collection<String> AVAILABLE_ATTRIBUTES = @@ -67,7 +69,9 @@ public interface Port extends ConfiguredObject RECEIVE_BUFFER_SIZE, NEED_CLIENT_AUTH, WANT_CLIENT_AUTH, - AUTHENTICATION_PROVIDER + AUTHENTICATION_PROVIDER, + KEY_STORE, + TRUST_STORES )); @@ -75,6 +79,10 @@ public interface Port extends ConfiguredObject int getPort(); + KeyStore getKeyStore(); + + Collection<TrustStore> getTrustStores(); + Collection<Transport> getTransports(); void addTransport(Transport transport) throws IllegalStateException, diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/model/TrustStore.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/model/TrustStore.java index 53498ab431..c686e7bd50 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/model/TrustStore.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/model/TrustStore.java @@ -38,9 +38,9 @@ public interface TrustStore extends ConfiguredObject String PATH = "path"; String PASSWORD = "password"; - String PEERS_ONLY = "peersOnly"; String TYPE = "type"; - String KEY_MANAGER_FACTORY_ALGORITHM = "keyManagerFactoryAlgorithm"; + String PEERS_ONLY = "peersOnly"; + String TRUST_MANAGER_FACTORY_ALGORITHM = "trustManagerFactoryAlgorithm"; public static final Collection<String> AVAILABLE_ATTRIBUTES = Collections.unmodifiableList( @@ -56,9 +56,9 @@ public interface TrustStore extends ConfiguredObject DESCRIPTION, PATH, PASSWORD, - PEERS_ONLY, TYPE, - KEY_MANAGER_FACTORY_ALGORITHM + PEERS_ONLY, + TRUST_MANAGER_FACTORY_ALGORITHM )); public String getPassword(); diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/AbstractAdapter.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/AbstractAdapter.java index 20929a337a..51fd3a5c78 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/AbstractAdapter.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/AbstractAdapter.java @@ -63,6 +63,7 @@ abstract class AbstractAdapter implements ConfiguredObject { if (attributes.containsKey(name)) { + //TODO: dont put nulls _attributes.put(name, attributes.get(name)); } } @@ -254,6 +255,7 @@ abstract class AbstractAdapter implements ConfiguredObject if((currentValue == null && expected == null) || (currentValue != null && currentValue.equals(expected))) { + //TODO: dont put nulls _attributes.put(name, desired); return true; } @@ -397,4 +399,51 @@ abstract class AbstractAdapter implements ConfiguredObject { return _defaultAttributes; } + + /** + * Returns a map of effective attribute values that would result + * if applying the supplied changes. Does not apply the changes. + */ + protected Map<String, Object> generateEffectiveAttributes(Map<String,Object> changedValues) + { + //Build a new set of effective attributes that would be + //the result of applying the attribute changes, so we + //can validate the configuration that would result + + Map<String, Object> defaultValues = getDefaultAttributes(); + Map<String, Object> existingActualValues = getActualAttributes(); + + //create a new merged map, starting with the defaults + Map<String, Object> merged = new HashMap<String, Object>(defaultValues); + + for(String name : getAttributeNames()) + { + if(changedValues.containsKey(name)) + { + Object changedValue = changedValues.get(name); + if(changedValue != null) + { + //use the new non-null value for the merged values + merged.put(name, changedValue); + } + else + { + //we just use the default (if there was one) since the changed + //value is null and effectively clears any existing actual value + } + } + else if(existingActualValues.get(name) != null) + { + //Use existing non-null actual value for the merge + merged.put(name, existingActualValues.get(name)); + } + else + { + //There was neither a change or an existing non-null actual + //value, so just use the default value (if there was one). + } + } + + return merged; + } } diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/AbstractKeyStoreAdapter.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/AbstractKeyStoreAdapter.java index 80196c395e..707cf8076d 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/AbstractKeyStoreAdapter.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/AbstractKeyStoreAdapter.java @@ -37,20 +37,21 @@ import org.apache.qpid.server.util.MapValueConverter; public abstract class AbstractKeyStoreAdapter extends AbstractAdapter { + public static final String DUMMY_PASSWORD_MASK = "********"; + public static final String DEFAULT_KEYSTORE_TYPE = java.security.KeyStore.getDefaultType(); + private String _name; private String _password; - protected AbstractKeyStoreAdapter(UUID id, Broker broker, Map<String, Object> attributes) + protected AbstractKeyStoreAdapter(UUID id, Broker broker, Map<String, Object> defaults, + Map<String, Object> attributes) { - super(id, broker.getTaskExecutor()); + super(id, defaults, attributes, broker.getTaskExecutor()); addParent(Broker.class, broker); + _name = MapValueConverter.getStringAttribute(TrustStore.NAME, attributes); _password = MapValueConverter.getStringAttribute(TrustStore.PASSWORD, attributes); - setMandatoryAttribute(TrustStore.PATH, attributes); - setOptionalAttribute(TrustStore.PEERS_ONLY, attributes); - setOptionalAttribute(TrustStore.TYPE, attributes); - setOptionalAttribute(TrustStore.KEY_MANAGER_FACTORY_ALGORITHM, attributes); - setOptionalAttribute(TrustStore.DESCRIPTION, attributes); + MapValueConverter.assertMandatoryAttribute(KeyStore.PATH, attributes); } @Override @@ -163,15 +164,16 @@ public abstract class AbstractKeyStoreAdapter extends AbstractAdapter } else if(KeyStore.PASSWORD.equals(name)) { - return null; // for security reasons we don't expose the password + // For security reasons we don't expose the password + if (getPassword() != null) + { + return DUMMY_PASSWORD_MASK; + } + + return null; } - return super.getAttribute(name); - } - @Override - protected boolean setState(State currentState, State desiredState) - { - return false; + return super.getAttribute(name); } public String getPassword() @@ -183,25 +185,4 @@ public abstract class AbstractKeyStoreAdapter extends AbstractAdapter { _password = password; } - - private void setMandatoryAttribute(String name, Map<String, Object> attributeValues) - { - changeAttribute(name, null, MapValueConverter.getStringAttribute(name, attributeValues)); - } - - private void setOptionalAttribute(String name, Map<String, Object> attributeValues) - { - Object attrValue = attributeValues.get(name); - if (attrValue != null) - { - if (attrValue instanceof Boolean) - { - changeAttribute(name, null, MapValueConverter.getBooleanAttribute(name, attributeValues)); - } - else - { - changeAttribute(name, null, MapValueConverter.getStringAttribute(name, attributeValues)); - } - } - } } diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/AmqpPortAdapter.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/AmqpPortAdapter.java index e7057f89d3..71f5397d2b 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/AmqpPortAdapter.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/AmqpPortAdapter.java @@ -133,15 +133,11 @@ public class AmqpPortAdapter extends PortAdapter private SSLContext createSslContext() { - KeyStore keyStore = _broker.getDefaultKeyStore(); - if (keyStore == null) - { - throw new IllegalConfigurationException("SSL was requested on AMQP port '" - + this.getName() + "' but no key store defined"); - } + KeyStore keyStore = getKeyStore(); - Collection<TrustStore> trustStores = _broker.getTrustStores(); - if (((Boolean)getAttribute(NEED_CLIENT_AUTH) || (Boolean)getAttribute(WANT_CLIENT_AUTH)) && trustStores.isEmpty()) + Collection<TrustStore> trustStores = getTrustStores(); + boolean needClientCert = (Boolean)getAttribute(NEED_CLIENT_AUTH) || (Boolean)getAttribute(WANT_CLIENT_AUTH); + if (needClientCert && trustStores.isEmpty()) { throw new IllegalConfigurationException("Client certificate authentication is enabled on AMQP port '" + this.getName() + "' but no trust store defined"); @@ -165,7 +161,7 @@ public class AmqpPortAdapter extends PortAdapter trustStore.getPassword(), (String)trustStore.getAttribute(TrustStore.TYPE), (Boolean) trustStore.getAttribute(TrustStore.PEERS_ONLY), - (String)trustStore.getAttribute(TrustStore.KEY_MANAGER_FACTORY_ALGORITHM))); + (String)trustStore.getAttribute(TrustStore.TRUST_MANAGER_FACTORY_ALGORITHM))); } sslContext = SSLContextFactory.buildClientContext(trstWrappers, keystorePath, keystorePassword, keystoreType, diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/BrokerAdapter.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/BrokerAdapter.java index e968d91e79..ae30f70877 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/BrokerAdapter.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/BrokerAdapter.java @@ -24,7 +24,6 @@ import java.lang.reflect.Type; import java.net.InetSocketAddress; import java.net.SocketAddress; import java.security.AccessControlException; -import java.security.KeyStoreException; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -32,13 +31,12 @@ import java.util.HashMap; import java.util.Map; import java.util.UUID; -import javax.net.ssl.KeyManagerFactory; -import java.security.cert.Certificate; - import org.apache.log4j.Logger; import org.apache.qpid.common.QpidProperties; import org.apache.qpid.server.configuration.ConfigurationEntryStore; import org.apache.qpid.server.configuration.IllegalConfigurationException; +import org.apache.qpid.server.configuration.store.ManagementModeStoreHandler; +import org.apache.qpid.server.configuration.updater.TaskExecutor; import org.apache.qpid.server.logging.LogRecorder; import org.apache.qpid.server.logging.RootMessageLogger; import org.apache.qpid.server.logging.actors.BrokerActor; @@ -58,22 +56,18 @@ import org.apache.qpid.server.model.Statistics; import org.apache.qpid.server.model.TrustStore; import org.apache.qpid.server.model.UUIDGenerator; import org.apache.qpid.server.model.VirtualHost; -import org.apache.qpid.server.configuration.store.ManagementModeStoreHandler; -import org.apache.qpid.server.configuration.updater.TaskExecutor; +import org.apache.qpid.server.security.SecurityManager; +import org.apache.qpid.server.security.SubjectCreator; import org.apache.qpid.server.security.access.Operation; import org.apache.qpid.server.security.group.FileGroupManager; import org.apache.qpid.server.security.group.GroupManager; -import org.apache.qpid.server.security.SecurityManager; -import org.apache.qpid.server.security.SubjectCreator; import org.apache.qpid.server.stats.StatisticsGatherer; import org.apache.qpid.server.store.MessageStoreCreator; import org.apache.qpid.server.util.MapValueConverter; import org.apache.qpid.server.virtualhost.VirtualHostRegistry; -import org.apache.qpid.transport.network.security.ssl.SSLUtil; public class BrokerAdapter extends AbstractAdapter implements Broker, ConfigurationChangeListener { - private static final Logger LOGGER = Logger.getLogger(BrokerAdapter.class); @SuppressWarnings("serial") @@ -100,13 +94,6 @@ public class BrokerAdapter extends AbstractAdapter implements Broker, Configurat put(DEFAULT_VIRTUAL_HOST, String.class); put(DEFAULT_AUTHENTICATION_PROVIDER, String.class); - put(KEY_STORE_PATH, String.class); - put(KEY_STORE_PASSWORD, String.class); - put(KEY_STORE_CERT_ALIAS, String.class); - put(TRUST_STORE_PATH, String.class); - put(TRUST_STORE_PASSWORD, String.class); - put(PEER_STORE_PATH, String.class); - put(PEER_STORE_PASSWORD, String.class); put(GROUP_FILE, String.class); put(VIRTUALHOST_STORE_TRANSACTION_IDLE_TIMEOUT_CLOSE, Long.class); put(VIRTUALHOST_STORE_TRANSACTION_IDLE_TIMEOUT_WARN, Long.class); @@ -133,12 +120,7 @@ public class BrokerAdapter extends AbstractAdapter implements Broker, Configurat public static final long DEFAULT_STORE_TRANSACTION_IDLE_TIMEOUT_WARN = 0l; public static final long DEFAULT_STORE_TRANSACTION_OPEN_TIMEOUT_CLOSE = 0l; public static final long DEFAULT_STORE_TRANSACTION_OPEN_TIMEOUT_WARN = 0l; - private static final String DEFAULT_KEY_STORE_NAME = "defaultKeyStore"; - private static final String DEFAULT_TRUST_STORE_NAME = "defaultTrustStore"; private static final String DEFAULT_GROUP_PROVIDER_NAME = "defaultGroupProvider"; - private static final String DEFAULT_PEER_STORE_NAME = "defaultPeerStore"; - - private static final String DUMMY_PASSWORD_MASK = "********"; @SuppressWarnings("serial") private static final Map<String, Object> DEFAULTS = Collections.unmodifiableMap(new HashMap<String, Object>(){{ @@ -182,16 +164,14 @@ public class BrokerAdapter extends AbstractAdapter implements Broker, Configurat private final Map<UUID, AuthenticationProvider> _authenticationProviders = new HashMap<UUID, AuthenticationProvider>(); private final Map<String, GroupProvider> _groupProviders = new HashMap<String, GroupProvider>(); private final Map<UUID, ConfiguredObject> _plugins = new HashMap<UUID, ConfiguredObject>(); - private final Map<UUID, KeyStore> _keyStores = new HashMap<UUID, KeyStore>(); - private final Map<UUID, TrustStore> _trustStores = new HashMap<UUID, TrustStore>(); + private final Map<String, KeyStore> _keyStores = new HashMap<String, KeyStore>(); + private final Map<String, TrustStore> _trustStores = new HashMap<String, TrustStore>(); private final AuthenticationProviderFactory _authenticationProviderFactory; private AuthenticationProvider _defaultAuthenticationProvider; private final PortFactory _portFactory; private final SecurityManager _securityManager; - private final UUID _defaultKeyStoreId; - private final UUID _defaultTrustStoreId; private final Collection<String> _supportedStoreTypes; private final ConfigurationEntryStore _brokerStore; @@ -212,8 +192,6 @@ public class BrokerAdapter extends AbstractAdapter implements Broker, Configurat _portFactory = portFactory; _securityManager = new SecurityManager((String)getAttribute(ACL_FILE)); addChangeListener(_securityManager); - _defaultKeyStoreId = UUIDGenerator.generateBrokerChildUUID(KeyStore.class.getSimpleName(), DEFAULT_KEY_STORE_NAME); - _defaultTrustStoreId = UUIDGenerator.generateBrokerChildUUID(TrustStore.class.getSimpleName(), DEFAULT_TRUST_STORE_NAME); createBrokerChildrenFromAttributes(); _supportedStoreTypes = new MessageStoreCreator().getStoreTypes(); _brokerStore = brokerStore; @@ -226,9 +204,6 @@ public class BrokerAdapter extends AbstractAdapter implements Broker, Configurat private void createBrokerChildrenFromAttributes() { createGroupProvider(); - createKeyStore(); - createTrustStore(); - createPeerStore(); } private void createGroupProvider() @@ -248,80 +223,12 @@ public class BrokerAdapter extends AbstractAdapter implements Broker, Configurat } } - private void createKeyStore() - { - Map<String, Object> actualAttributes = getActualAttributes(); - String keyStorePath = (String) getAttribute(KEY_STORE_PATH); - if (keyStorePath != null) - { - Map<String, Object> keyStoreAttributes = new HashMap<String, Object>(); - keyStoreAttributes.put(KeyStore.NAME, DEFAULT_KEY_STORE_NAME); - keyStoreAttributes.put(KeyStore.PATH, keyStorePath); - keyStoreAttributes.put(KeyStore.PASSWORD, (String) actualAttributes.get(KEY_STORE_PASSWORD)); - keyStoreAttributes.put(KeyStore.TYPE, java.security.KeyStore.getDefaultType()); - keyStoreAttributes.put(KeyStore.CERTIFICATE_ALIAS, getAttribute(KEY_STORE_CERT_ALIAS)); - keyStoreAttributes.put(KeyStore.KEY_MANAGER_FACTORY_ALGORITHM, KeyManagerFactory.getDefaultAlgorithm()); - KeyStoreAdapter keyStoreAdapter = new KeyStoreAdapter(_defaultKeyStoreId, this, keyStoreAttributes); - _keyStores.put(keyStoreAdapter.getId(), keyStoreAdapter); - } - else - { - _keyStores.remove(_defaultKeyStoreId); - } - } - - private void createTrustStore() - { - Map<String, Object> actualAttributes = getActualAttributes(); - String trustStorePath = (String) getAttribute(TRUST_STORE_PATH); - if (trustStorePath != null) - { - Map<String, Object> trsustStoreAttributes = new HashMap<String, Object>(); - trsustStoreAttributes.put(TrustStore.NAME, DEFAULT_TRUST_STORE_NAME); - trsustStoreAttributes.put(TrustStore.PATH, trustStorePath); - trsustStoreAttributes.put(TrustStore.PEERS_ONLY, Boolean.FALSE); - trsustStoreAttributes.put(TrustStore.PASSWORD, (String) actualAttributes.get(TRUST_STORE_PASSWORD)); - trsustStoreAttributes.put(TrustStore.TYPE, java.security.KeyStore.getDefaultType()); - trsustStoreAttributes.put(TrustStore.KEY_MANAGER_FACTORY_ALGORITHM, KeyManagerFactory.getDefaultAlgorithm()); - TrustStoreAdapter trustStore = new TrustStoreAdapter(_defaultTrustStoreId, this, trsustStoreAttributes); - _trustStores.put(trustStore.getId(), trustStore); - } - else - { - _trustStores.remove(_defaultTrustStoreId); - } - } - - private void createPeerStore() - { - Map<String, Object> actualAttributes = getActualAttributes(); - String peerStorePath = (String) getAttribute(PEER_STORE_PATH); - UUID peerStoreId = UUIDGenerator.generateBrokerChildUUID(TrustStore.class.getSimpleName(), DEFAULT_PEER_STORE_NAME); - if (peerStorePath != null) - { - Map<String, Object> peerStoreAttributes = new HashMap<String, Object>(); - peerStoreAttributes.put(TrustStore.NAME, peerStoreId.toString()); - peerStoreAttributes.put(TrustStore.PATH, peerStorePath); - peerStoreAttributes.put(TrustStore.PEERS_ONLY, Boolean.TRUE); - peerStoreAttributes.put(TrustStore.PASSWORD, (String) actualAttributes.get(PEER_STORE_PASSWORD)); - peerStoreAttributes.put(TrustStore.TYPE, java.security.KeyStore.getDefaultType()); - peerStoreAttributes.put(TrustStore.KEY_MANAGER_FACTORY_ALGORITHM, KeyManagerFactory.getDefaultAlgorithm()); - TrustStoreAdapter trustStore = new TrustStoreAdapter(peerStoreId, this, peerStoreAttributes); - _trustStores.put(trustStore.getId(), trustStore); - } - else - { - _trustStores.remove(peerStoreId); - } - } - public Collection<VirtualHost> getVirtualHosts() { synchronized(_vhostAdapters) { return new ArrayList<VirtualHost>(_vhostAdapters.values()); } - } public Collection<Port> getPorts() @@ -354,6 +261,22 @@ public class BrokerAdapter extends AbstractAdapter implements Broker, Configurat return null; } + public KeyStore findKeyStoreByName(String keyStoreName) + { + synchronized(_keyStores) + { + return _keyStores.get(keyStoreName); + } + } + + public TrustStore findTrustStoreByName(String trustStoreName) + { + synchronized(_trustStores) + { + return _trustStores.get(trustStoreName); + } + } + @Override public AuthenticationProvider getDefaultAuthenticationProvider() { @@ -396,14 +319,14 @@ public class BrokerAdapter extends AbstractAdapter implements Broker, Configurat // permission has already been granted to create the virtual host // disable further access check on other operations, e.g. create exchange - _securityManager.setAccessChecksDisabled(true); + SecurityManager.setAccessChecksDisabled(true); try { virtualHostAdapter.setDesiredState(State.INITIALISING, State.ACTIVE); } finally { - _securityManager.setAccessChecksDisabled(false); + SecurityManager.setAccessChecksDisabled(false); } return virtualHostAdapter; } @@ -526,6 +449,14 @@ public class BrokerAdapter extends AbstractAdapter implements Broker, Configurat { return (C) createAuthenticationProvider(attributes); } + else if(childClass == KeyStore.class) + { + return (C) createKeyStore(attributes); + } + else if(childClass == TrustStore.class) + { + return (C) createTrustStore(attributes); + } else { throw new IllegalArgumentException("Cannot create child of class " + childClass.getSimpleName()); @@ -618,40 +549,76 @@ public class BrokerAdapter extends AbstractAdapter implements Broker, Configurat throw new UnsupportedOperationException("Not implemented yet!"); } + private KeyStore createKeyStore(Map<String, Object> attributes) + { + KeyStore keyStore = new KeyStoreAdapter(UUIDGenerator.generateRandomUUID(), this, attributes); + addKeyStore(keyStore); + + return keyStore; + } + + private TrustStore createTrustStore(Map<String, Object> attributes) + { + TrustStore trustStore = new TrustStoreAdapter(UUIDGenerator.generateRandomUUID(), this, attributes); + addTrustStore(trustStore); + + return trustStore; + } + private void addKeyStore(KeyStore keyStore) { synchronized (_keyStores) { - if(_keyStores.containsKey(keyStore.getId())) + if(_keyStores.containsKey(keyStore.getName())) { - throw new IllegalConfigurationException("Cannot add KeyStore because one with id " + keyStore.getId() + " already exists"); + throw new IllegalConfigurationException("Can't add KeyStore because one with name " + keyStore.getName() + " already exists"); } - _keyStores.put(keyStore.getId(), keyStore); + _keyStores.put(keyStore.getName(), keyStore); } keyStore.addChangeListener(this); } private boolean deleteKeyStore(KeyStore object) { - throw new UnsupportedOperationException("Not implemented yet!"); + synchronized(_keyStores) + { + String name = object.getName(); + KeyStore removedKeyStore = _keyStores.remove(name); + if(removedKeyStore != null) + { + removedKeyStore.removeChangeListener(this); + } + + return removedKeyStore != null; + } } private void addTrustStore(TrustStore trustStore) { synchronized (_trustStores) { - if(_trustStores.containsKey(trustStore.getId())) + if(_trustStores.containsKey(trustStore.getName())) { - throw new IllegalConfigurationException("Cannot add TrustStore because one with id " + trustStore.getId() + " already exists"); + throw new IllegalConfigurationException("Can't add TrustStore because one with name " + trustStore.getName() + " already exists"); } - _trustStores.put(trustStore.getId(), trustStore); + _trustStores.put(trustStore.getName(), trustStore); } trustStore.addChangeListener(this); } private boolean deleteTrustStore(TrustStore object) { - throw new UnsupportedOperationException("Not implemented yet!"); + synchronized(_trustStores) + { + String name = object.getName(); + TrustStore removedTrustStore = _trustStores.remove(name); + if(removedTrustStore != null) + { + removedTrustStore.removeChangeListener(this); + } + + return removedTrustStore != null; + } } @Override @@ -726,14 +693,6 @@ public class BrokerAdapter extends AbstractAdapter implements Broker, Configurat { return _authenticationProviderFactory.getSupportedAuthenticationProviders(); } - else if (KEY_STORE_PASSWORD.equals(name) || TRUST_STORE_PASSWORD.equals(name) || PEER_STORE_PASSWORD.equals(name)) - { - if (getActualAttributes().get(name) != null) - { - return DUMMY_PASSWORD_MASK; - } - return null; - } else if (MODEL_VERSION.equals(name)) { return Model.MODEL_MAJOR_VERSION + "." + Model.MODEL_MINOR_VERSION; @@ -885,6 +844,7 @@ public class BrokerAdapter extends AbstractAdapter implements Broker, Configurat { childDeleted = deleteTrustStore((TrustStore)object); } + if(childDeleted) { childRemoved(object); @@ -1034,18 +994,6 @@ public class BrokerAdapter extends AbstractAdapter implements Broker, Configurat } @Override - public KeyStore getDefaultKeyStore() - { - return _keyStores.get(_defaultKeyStoreId); - } - - @Override - public TrustStore getDefaultTrustStore() - { - return _trustStores.get(_defaultTrustStoreId); - } - - @Override public TaskExecutor getTaskExecutor() { return super.getTaskExecutor(); @@ -1058,9 +1006,6 @@ public class BrokerAdapter extends AbstractAdapter implements Broker, Configurat Map<String, Object> convertedAttributes = MapValueConverter.convert(attributes, ATTRIBUTE_TYPES); validateAttributes(convertedAttributes); - boolean keyStoreChanged = false; - boolean trustStoreChanged = false; - boolean peerStoreChanged = false; Collection<String> names = AVAILABLE_ATTRIBUTES; for (String name : names) { @@ -1081,34 +1026,11 @@ public class BrokerAdapter extends AbstractAdapter implements Broker, Configurat _defaultAuthenticationProvider = findAuthenticationProviderByName((String)desired); } } - else if (KEY_STORE_PATH.equals(name) || KEY_STORE_PASSWORD.equals(name) || KEY_STORE_CERT_ALIAS.equals(name)) - { - keyStoreChanged = true; - } - else if (TRUST_STORE_PATH.equals(name) || TRUST_STORE_PASSWORD.equals(name)) - { - trustStoreChanged = true; - } - else if (PEER_STORE_PATH.equals(name) || PEER_STORE_PASSWORD.equals(name)) - { - peerStoreChanged = true; - } + attributeSet(name, expected, desired); } } } - if (keyStoreChanged) - { - createKeyStore(); - } - if (trustStoreChanged) - { - createTrustStore(); - } - if (peerStoreChanged) - { - createPeerStore(); - } } private void validateAttributes(Map<String, Object> convertedAttributes) @@ -1125,9 +1047,7 @@ public class BrokerAdapter extends AbstractAdapter implements Broker, Configurat // create a group manager to validate the groups specified in file new FileGroupManager(groupFile); } - validateKeyStoreAttributes(convertedAttributes, "key store", KEY_STORE_PATH, KEY_STORE_PASSWORD, KEY_STORE_CERT_ALIAS); - validateKeyStoreAttributes(convertedAttributes, "trust store", TRUST_STORE_PATH, TRUST_STORE_PASSWORD, null); - validateKeyStoreAttributes(convertedAttributes, "peer store", PEER_STORE_PATH, PEER_STORE_PASSWORD, null); + String defaultAuthenticationProvider = (String) convertedAttributes.get(DEFAULT_AUTHENTICATION_PROVIDER); if (defaultAuthenticationProvider != null) { @@ -1176,59 +1096,6 @@ public class BrokerAdapter extends AbstractAdapter implements Broker, Configurat } } - private void validateKeyStoreAttributes(Map<String, Object> convertedAttributes, String type, String pathAttribute, - String passwordAttribute, String aliasAttribute) - { - String keyStoreFile = (String) convertedAttributes.get(pathAttribute); - String password = (String) convertedAttributes.get(passwordAttribute); - String alias = aliasAttribute!= null? (String) convertedAttributes.get(aliasAttribute) : null; - if (keyStoreFile != null || password != null || alias != null) - { - if (keyStoreFile == null) - { - keyStoreFile = (String) getActualAttributes().get(pathAttribute); - } - if (password == null) - { - password = (String) getActualAttributes().get(passwordAttribute); - } - java.security.KeyStore keyStore = null; - try - { - keyStore = SSLUtil.getInitializedKeyStore(keyStoreFile, password, java.security.KeyStore.getDefaultType()); - } - catch (Exception e) - { - throw new IllegalConfigurationException("Cannot instantiate " + type + " at " + keyStoreFile, e); - } - if (aliasAttribute != null) - { - if (alias == null) - { - alias = (String) getActualAttributes().get(aliasAttribute); - } - if (alias != null) - { - Certificate cert = null; - try - { - cert = keyStore.getCertificate(alias); - } - catch (KeyStoreException e) - { - // key store should be initialized above - throw new RuntimeException("Key store has not been initialized", e); - } - if (cert == null) - { - throw new IllegalConfigurationException("Cannot find a certificate with alias " + alias + "in " + type - + " : " + keyStoreFile); - } - } - } - } - } - @Override protected void authoriseSetAttribute(String name, Object expected, Object desired) throws AccessControlException { diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/KeyStoreAdapter.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/KeyStoreAdapter.java index 113d895e62..4d4d3bb31d 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/KeyStoreAdapter.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/KeyStoreAdapter.java @@ -20,23 +20,63 @@ */ package org.apache.qpid.server.model.adapter; +import java.lang.reflect.Type; +import java.security.AccessControlException; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.cert.Certificate; +import java.util.ArrayList; import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; import java.util.Map; import java.util.UUID; +import javax.net.ssl.KeyManagerFactory; + +import org.apache.qpid.server.configuration.IllegalConfigurationException; import org.apache.qpid.server.model.Broker; +import org.apache.qpid.server.model.IntegrityViolationException; import org.apache.qpid.server.model.KeyStore; +import org.apache.qpid.server.model.Port; +import org.apache.qpid.server.model.State; +import org.apache.qpid.server.security.access.Operation; +import org.apache.qpid.server.util.MapValueConverter; +import org.apache.qpid.transport.network.security.ssl.SSLUtil; public class KeyStoreAdapter extends AbstractKeyStoreAdapter implements KeyStore { + @SuppressWarnings("serial") + public static final Map<String, Type> ATTRIBUTE_TYPES = Collections.unmodifiableMap(new HashMap<String, Type>(){{ + put(NAME, String.class); + put(PATH, String.class); + put(PASSWORD, String.class); + put(TYPE, String.class); + put(CERTIFICATE_ALIAS, String.class); + put(KEY_MANAGER_FACTORY_ALGORITHM, String.class); + }}); + + @SuppressWarnings("serial") + public static final Map<String, Object> DEFAULTS = Collections.unmodifiableMap(new HashMap<String, Object>(){{ + put(KeyStore.TYPE, DEFAULT_KEYSTORE_TYPE); + put(KeyStore.KEY_MANAGER_FACTORY_ALGORITHM, KeyManagerFactory.getDefaultAlgorithm()); + }}); + + private Broker _broker; public KeyStoreAdapter(UUID id, Broker broker, Map<String, Object> attributes) { - super(id, broker, attributes); - if (attributes.get(CERTIFICATE_ALIAS) != null) - { - changeAttribute(CERTIFICATE_ALIAS, null, attributes.get(CERTIFICATE_ALIAS)); - } + super(id, broker, DEFAULTS, MapValueConverter.convert(attributes, ATTRIBUTE_TYPES)); + _broker = broker; + + String keyStorePath = (String)getAttribute(KeyStore.PATH); + String keyStorePassword = getPassword(); + String keyStoreType = (String)getAttribute(KeyStore.TYPE); + String keyManagerFactoryAlgorithm = (String)getAttribute(KeyStore.KEY_MANAGER_FACTORY_ALGORITHM); + String certAlias = (String)getAttribute(KeyStore.CERTIFICATE_ALIAS); + + validateKeyStoreAttributes(keyStoreType, keyStorePath, keyStorePassword, + certAlias, keyManagerFactoryAlgorithm); } @Override @@ -45,4 +85,129 @@ public class KeyStoreAdapter extends AbstractKeyStoreAdapter implements KeyStore return AVAILABLE_ATTRIBUTES; } + @Override + protected boolean setState(State currentState, State desiredState) + { + if(desiredState == State.DELETED) + { + // verify that it is not in use + String storeName = getName(); + + Collection<Port> ports = new ArrayList<Port>(_broker.getPorts()); + for (Port port : ports) + { + if (storeName.equals(port.getAttribute(Port.KEY_STORE))) + { + throw new IntegrityViolationException("Key store '" + storeName + "' can't be deleted as it is in use by a port:" + port.getName()); + } + } + + return true; + } + + return false; + } + + @Override + protected void authoriseSetDesiredState(State currentState, State desiredState) throws AccessControlException + { + if(desiredState == State.DELETED) + { + if (!_broker.getSecurityManager().authoriseConfiguringBroker(getName(), KeyStore.class, Operation.DELETE)) + { + throw new AccessControlException("Deletion of key store is denied"); + } + } + } + + @Override + protected void authoriseSetAttribute(String name, Object expected, Object desired) throws AccessControlException + { + authoriseSetAttribute(); + } + + @Override + protected void authoriseSetAttributes(Map<String, Object> attributes) throws AccessControlException + { + authoriseSetAttribute(); + } + + private void authoriseSetAttribute() + { + if (!_broker.getSecurityManager().authoriseConfiguringBroker(getName(), KeyStore.class, Operation.UPDATE)) + { + throw new AccessControlException("Setting key store attributes is denied"); + } + } + + @Override + protected void changeAttributes(Map<String, Object> attributes) + { + Map<String, Object> changedValues = MapValueConverter.convert(attributes, ATTRIBUTE_TYPES); + if(changedValues.containsKey(KeyStore.NAME)) + { + String newName = (String) changedValues.get(KeyStore.NAME); + if(!getName().equals(newName)) + { + throw new IllegalConfigurationException("Changing the key store name is not allowed"); + } + } + + Map<String, Object> merged = generateEffectiveAttributes(changedValues); + + String keyStorePath = (String)merged.get(KeyStore.PATH); + String keyStorePassword = (String) merged.get(KeyStore.PASSWORD); + String keyStoreType = (String)merged.get(KeyStore.TYPE); + String keyManagerFactoryAlgorithm = (String)merged.get(KeyStore.KEY_MANAGER_FACTORY_ALGORITHM); + String certAlias = (String)merged.get(KeyStore.CERTIFICATE_ALIAS); + + validateKeyStoreAttributes(keyStoreType, keyStorePath, keyStorePassword, + certAlias, keyManagerFactoryAlgorithm); + + super.changeAttributes(changedValues); + } + + private void validateKeyStoreAttributes(String type, String keyStorePath, + String keyStorePassword, String alias, + String keyManagerFactoryAlgorithm) + { + java.security.KeyStore keyStore = null; + try + { + keyStore = SSLUtil.getInitializedKeyStore(keyStorePath, keyStorePassword, type); + } + catch (Exception e) + { + throw new IllegalConfigurationException("Cannot instantiate key store at " + keyStorePath, e); + } + + if (alias != null) + { + Certificate cert = null; + try + { + cert = keyStore.getCertificate(alias); + } + catch (KeyStoreException e) + { + // key store should be initialized above + throw new RuntimeException("Key store has not been initialized", e); + } + if (cert == null) + { + throw new IllegalConfigurationException("Cannot find a certificate with alias " + alias + + "in key store : " + keyStorePath); + } + } + + try + { + KeyManagerFactory.getInstance(keyManagerFactoryAlgorithm); + } + catch (NoSuchAlgorithmException e) + { + throw new IllegalConfigurationException("Unknown keyManagerFactoryAlgorithm: " + + keyManagerFactoryAlgorithm); + } + } } diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/PortAdapter.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/PortAdapter.java index 4250de17a7..0c1249d20e 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/PortAdapter.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/PortAdapter.java @@ -37,12 +37,14 @@ import org.apache.qpid.server.model.AuthenticationProvider; import org.apache.qpid.server.model.Broker; import org.apache.qpid.server.model.ConfiguredObject; import org.apache.qpid.server.model.Connection; +import org.apache.qpid.server.model.KeyStore; import org.apache.qpid.server.model.LifetimePolicy; import org.apache.qpid.server.model.Port; import org.apache.qpid.server.model.Protocol; import org.apache.qpid.server.model.State; import org.apache.qpid.server.model.Statistics; import org.apache.qpid.server.model.Transport; +import org.apache.qpid.server.model.TrustStore; import org.apache.qpid.server.model.VirtualHost; import org.apache.qpid.server.model.VirtualHostAlias; import org.apache.qpid.server.security.access.Operation; @@ -58,6 +60,8 @@ public class PortAdapter extends AbstractAdapter implements Port put(NAME, String.class); put(PROTOCOLS, new ParameterizedTypeImpl(Set.class, Protocol.class)); put(TRANSPORTS, new ParameterizedTypeImpl(Set.class, Transport.class)); + put(TRUST_STORES, new ParameterizedTypeImpl(Set.class, String.class)); + put(KEY_STORE, String.class); put(PORT, Integer.class); put(TCP_NO_DELAY, Boolean.class); put(RECEIVE_BUFFER_SIZE, Integer.class); @@ -373,16 +377,40 @@ public class PortAdapter extends AbstractAdapter implements Port boolean requiresCertificate = (needClientCertificate != null && needClientCertificate.booleanValue()) || (wantClientCertificate != null && wantClientCertificate.booleanValue()); - if (transports != null && transports.contains(Transport.SSL)) + String keyStoreName = (String) merged.get(KEY_STORE); + boolean hasKeyStore = keyStoreName != null; + if(keyStoreName != null) { - if (_broker.getKeyStores().isEmpty()) + if (_broker.findKeyStoreByName(keyStoreName) == null) { - throw new IllegalConfigurationException("Can't create port which requires SSL as the broker has no keystore configured."); + throw new IllegalConfigurationException("Can't find key store with name '" + keyStoreName + "' for port " + getName()); + } + } + + Set<String> trustStoreNames = (Set<String>) merged.get(TRUST_STORES); + boolean hasTrustStore = trustStoreNames != null && !trustStoreNames.isEmpty(); + if(hasTrustStore) + { + for (String trustStoreName : trustStoreNames) + { + if (_broker.findTrustStoreByName(trustStoreName) == null) + { + throw new IllegalConfigurationException("Cannot find trust store with name '" + trustStoreName + "'"); + } + } + } + + boolean usesSsl = transports != null && transports.contains(Transport.SSL); + if (usesSsl) + { + if (keyStoreName == null) + { + throw new IllegalConfigurationException("Can't create port which requires SSL but has no key store configured."); } - if (_broker.getTrustStores().isEmpty() && requiresCertificate) + if (!hasTrustStore && requiresCertificate) { - throw new IllegalConfigurationException("Can't create port which requests SSL client certificates as the broker has no trust/peer stores configured."); + throw new IllegalConfigurationException("Can't create port which requests SSL client certificates but has no trust store configured."); } } else @@ -393,9 +421,14 @@ public class PortAdapter extends AbstractAdapter implements Port } } - if (protocols != null && protocols.contains(Protocol.HTTPS) && _broker.getKeyStores().isEmpty()) + if (protocols != null && protocols.contains(Protocol.HTTPS) && !hasKeyStore) { - throw new IllegalConfigurationException("Can't create port which requires SSL as the broker has no keystore configured."); + throw new IllegalConfigurationException("Can't create port which requires SSL but has no key store configured."); + } + + if (protocols != null && protocols.contains(Protocol.RMI) && usesSsl) + { + throw new IllegalConfigurationException("Can't create RMI Registry port which requires SSL."); } String authenticationProviderName = (String)merged.get(AUTHENTICATION_PROVIDER); @@ -450,4 +483,42 @@ public class PortAdapter extends AbstractAdapter implements Port throw new AccessControlException("Setting of port attributes is denied"); } } + + @Override + public KeyStore getKeyStore() + { + String keyStoreName = (String)getAttribute(Port.KEY_STORE); + KeyStore keyStore = _broker.findKeyStoreByName(keyStoreName); + + if (keyStoreName != null && keyStore == null) + { + throw new IllegalConfigurationException("Can't find key store with name '" + keyStoreName + "' for port " + getName()); + } + + return keyStore; + } + + @Override + public Collection<TrustStore> getTrustStores() + { + Set<String> trustStoreNames = (Set<String>) getAttribute(TRUST_STORES); + boolean hasTrustStoreName = trustStoreNames != null && !trustStoreNames.isEmpty(); + + final Collection<TrustStore> trustStores = new ArrayList<TrustStore>(); + if(hasTrustStoreName) + { + for (String name : trustStoreNames) + { + TrustStore trustStore = _broker.findTrustStoreByName(name); + if (trustStore == null) + { + throw new IllegalConfigurationException("Can't find trust store with name '" + name + "' for port " + getName()); + } + + trustStores.add(trustStore); + } + } + + return trustStores; + } } diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/PortFactory.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/PortFactory.java index ffbd24997a..2efe189d73 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/PortFactory.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/PortFactory.java @@ -106,9 +106,9 @@ public class PortFactory port = new AmqpPortAdapter(id, broker, attributes, defaults, broker.getTaskExecutor()); boolean useClientAuth = (Boolean) port.getAttribute(Port.NEED_CLIENT_AUTH) || (Boolean) port.getAttribute(Port.WANT_CLIENT_AUTH); - if(useClientAuth && broker.getTrustStores().isEmpty()) + if(useClientAuth && port.getTrustStores().isEmpty()) { - throw new IllegalConfigurationException("Can't create port which requests SSL client certificates as the broker has no trust/peer stores configured."); + throw new IllegalConfigurationException("Can't create port which requests SSL client certificates but has no trust stores configured."); } if(useClientAuth && !port.getTransports().contains(Transport.SSL)) @@ -142,13 +142,19 @@ public class PortFactory defaults.put(Port.NAME, portValue + "-" + protocol.name()); port = new PortAdapter(id, broker, attributes, defaults, broker.getTaskExecutor()); + + boolean rmiPort = port.getProtocols().contains(Protocol.RMI); + if (rmiPort && port.getTransports().contains(Transport.SSL)) + { + throw new IllegalConfigurationException("Can't create RMI registry port which requires SSL"); + } } if(port.getTransports().contains(Transport.SSL) || port.getProtocols().contains(Protocol.HTTPS)) { - if(broker.getKeyStores().isEmpty()) + if(port.getKeyStore() == null) { - throw new IllegalConfigurationException("Can't create port which requires SSL as the broker has no keystore configured."); + throw new IllegalConfigurationException("Can't create port which requires SSL but has no key store configured."); } } diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/TrustStoreAdapter.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/TrustStoreAdapter.java index bdffe605ec..06089e43c6 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/TrustStoreAdapter.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/TrustStoreAdapter.java @@ -20,18 +20,61 @@ */ package org.apache.qpid.server.model.adapter; +import java.lang.reflect.Type; +import java.security.AccessControlException; +import java.security.NoSuchAlgorithmException; +import java.util.ArrayList; import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; import java.util.Map; import java.util.UUID; +import javax.net.ssl.TrustManagerFactory; + +import org.apache.qpid.server.configuration.IllegalConfigurationException; import org.apache.qpid.server.model.Broker; +import org.apache.qpid.server.model.IntegrityViolationException; +import org.apache.qpid.server.model.Port; +import org.apache.qpid.server.model.State; import org.apache.qpid.server.model.TrustStore; +import org.apache.qpid.server.security.access.Operation; +import org.apache.qpid.server.util.MapValueConverter; +import org.apache.qpid.transport.network.security.ssl.SSLUtil; public class TrustStoreAdapter extends AbstractKeyStoreAdapter implements TrustStore { + @SuppressWarnings("serial") + public static final Map<String, Type> ATTRIBUTE_TYPES = Collections.unmodifiableMap(new HashMap<String, Type>(){{ + put(NAME, String.class); + put(PATH, String.class); + put(PASSWORD, String.class); + put(TYPE, String.class); + put(PEERS_ONLY, Boolean.class); + put(TRUST_MANAGER_FACTORY_ALGORITHM, String.class); + }}); + + @SuppressWarnings("serial") + public static final Map<String, Object> DEFAULTS = Collections.unmodifiableMap(new HashMap<String, Object>(){{ + put(TrustStore.TYPE, DEFAULT_KEYSTORE_TYPE); + put(TrustStore.PEERS_ONLY, Boolean.FALSE); + put(TrustStore.TRUST_MANAGER_FACTORY_ALGORITHM, TrustManagerFactory.getDefaultAlgorithm()); + }}); + + private Broker _broker; + public TrustStoreAdapter(UUID id, Broker broker, Map<String, Object> attributes) { - super(id, broker, attributes); + super(id, broker, DEFAULTS, MapValueConverter.convert(attributes, ATTRIBUTE_TYPES)); + _broker = broker; + + String trustStorePath = (String) getAttribute(TrustStore.PATH); + String trustStorePassword = getPassword(); + String trustStoreType = (String) getAttribute(TrustStore.TYPE); + String trustManagerFactoryAlgorithm = (String) getAttribute(TrustStore.TRUST_MANAGER_FACTORY_ALGORITHM); + + validateTrustStoreAttributes(trustStoreType, trustStorePath, + trustStorePassword, trustManagerFactoryAlgorithm); } @Override @@ -40,4 +83,110 @@ public class TrustStoreAdapter extends AbstractKeyStoreAdapter implements TrustS return AVAILABLE_ATTRIBUTES; } + @Override + protected boolean setState(State currentState, State desiredState) + { + if(desiredState == State.DELETED) + { + // verify that it is not in use + String storeName = getName(); + + Collection<Port> ports = new ArrayList<Port>(_broker.getPorts()); + for (Port port : ports) + { + Collection<TrustStore> trustStores = port.getTrustStores(); + for(TrustStore store : trustStores) + { + if (storeName.equals(store.getAttribute(TrustStore.NAME))) + { + throw new IntegrityViolationException("Trust store '" + storeName + "' can't be deleted as it is in use by a port: " + port.getName()); + } + } + } + + return true; + } + + return false; + } + + @Override + protected void authoriseSetDesiredState(State currentState, State desiredState) throws AccessControlException + { + if(desiredState == State.DELETED) + { + if (!_broker.getSecurityManager().authoriseConfiguringBroker(getName(), TrustStore.class, Operation.DELETE)) + { + throw new AccessControlException("Deletion of key store is denied"); + } + } + } + + @Override + protected void authoriseSetAttribute(String name, Object expected, Object desired) throws AccessControlException + { + authoriseSetAttribute(); + } + + @Override + protected void authoriseSetAttributes(Map<String, Object> attributes) throws AccessControlException + { + authoriseSetAttribute(); + } + + private void authoriseSetAttribute() + { + if (!_broker.getSecurityManager().authoriseConfiguringBroker(getName(), TrustStore.class, Operation.UPDATE)) + { + throw new AccessControlException("Setting key store attributes is denied"); + } + } + + @Override + protected void changeAttributes(Map<String, Object> attributes) + { + Map<String, Object> changedValues = MapValueConverter.convert(attributes, ATTRIBUTE_TYPES); + if(changedValues.containsKey(TrustStore.NAME)) + { + String newName = (String) changedValues.get(TrustStore.NAME); + if(!getName().equals(newName)) + { + throw new IllegalConfigurationException("Changing the trust store name is not allowed"); + } + } + + Map<String, Object> merged = generateEffectiveAttributes(changedValues); + + String trustStorePath = (String)merged.get(TrustStore.PATH); + String trustStorePassword = (String) merged.get(TrustStore.PASSWORD); + String trustStoreType = (String)merged.get(TrustStore.TYPE); + String trustManagerFactoryAlgorithm = (String)merged.get(TrustStore.TRUST_MANAGER_FACTORY_ALGORITHM); + + validateTrustStoreAttributes(trustStoreType, trustStorePath, + trustStorePassword, trustManagerFactoryAlgorithm); + + super.changeAttributes(changedValues); + } + + private void validateTrustStoreAttributes(String type, String trustStorePath, + String password, String trustManagerFactoryAlgorithm) + { + try + { + SSLUtil.getInitializedKeyStore(trustStorePath, password, type); + } + catch (Exception e) + { + throw new IllegalConfigurationException("Cannot instantiate trust store at " + trustStorePath, e); + } + + try + { + TrustManagerFactory.getInstance(trustManagerFactoryAlgorithm); + } + catch (NoSuchAlgorithmException e) + { + throw new IllegalConfigurationException("Unknown trustManagerFactoryAlgorithm: " + trustManagerFactoryAlgorithm); + } + } } diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/util/MapValueConverter.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/util/MapValueConverter.java index 8c57d04348..16e717a9c7 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/util/MapValueConverter.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/util/MapValueConverter.java @@ -62,7 +62,7 @@ public class MapValueConverter return getStringAttribute(name, attributes, null); } - private static void assertMandatoryAttribute(String name, Map<String, Object> attributes) + public static void assertMandatoryAttribute(String name, Map<String, Object> attributes) { if (!attributes.containsKey(name)) { @@ -326,6 +326,10 @@ public class MapValueConverter public static <T> Set<T> toSet(Object rawValue, Class<T> setItemClass, String attributeName) { + if (rawValue == null) + { + return null; + } HashSet<T> set = new HashSet<T>(); if (rawValue instanceof Iterable) { |
