diff options
Diffstat (limited to 'java/broker-core/src/main/java/org/apache/qpid/server/security')
-rw-r--r-- | java/broker-core/src/main/java/org/apache/qpid/server/security/SubjectCreator.java | 4 | ||||
-rw-r--r-- | java/broker-core/src/main/java/org/apache/qpid/server/security/auth/manager/AbstractAuthenticationManager.java | 14 | ||||
-rw-r--r-- | java/broker-core/src/main/java/org/apache/qpid/server/security/auth/manager/AbstractScramAuthenticationManager.java | 210 | ||||
-rw-r--r-- | java/broker-core/src/main/java/org/apache/qpid/server/security/auth/manager/ConfigModelPasswordManagingAuthenticationProvider.java | 229 | ||||
-rw-r--r-- | java/broker-core/src/main/java/org/apache/qpid/server/security/auth/manager/MD5AuthenticationProvider.java | 227 | ||||
-rw-r--r-- | java/broker-core/src/main/java/org/apache/qpid/server/security/auth/manager/ManagedUser.java (renamed from java/broker-core/src/main/java/org/apache/qpid/server/security/auth/manager/ScramAuthUser.java) | 48 | ||||
-rw-r--r-- | java/broker-core/src/main/java/org/apache/qpid/server/security/auth/manager/PlainAuthenticationProvider.java | 176 |
7 files changed, 671 insertions, 237 deletions
diff --git a/java/broker-core/src/main/java/org/apache/qpid/server/security/SubjectCreator.java b/java/broker-core/src/main/java/org/apache/qpid/server/security/SubjectCreator.java index ac8d002577..bbec239d74 100644 --- a/java/broker-core/src/main/java/org/apache/qpid/server/security/SubjectCreator.java +++ b/java/broker-core/src/main/java/org/apache/qpid/server/security/SubjectCreator.java @@ -53,10 +53,10 @@ public class SubjectCreator { private final boolean _secure; private AuthenticationProvider<?> _authenticationProvider; - private Collection<GroupProvider> _groupProviders; + private Collection<GroupProvider<?>> _groupProviders; public SubjectCreator(AuthenticationProvider<?> authenticationProvider, - Collection<GroupProvider> groupProviders, + Collection<GroupProvider<?>> groupProviders, final boolean secure) { _authenticationProvider = authenticationProvider; diff --git a/java/broker-core/src/main/java/org/apache/qpid/server/security/auth/manager/AbstractAuthenticationManager.java b/java/broker-core/src/main/java/org/apache/qpid/server/security/auth/manager/AbstractAuthenticationManager.java index b2cf1739ab..7f98468726 100644 --- a/java/broker-core/src/main/java/org/apache/qpid/server/security/auth/manager/AbstractAuthenticationManager.java +++ b/java/broker-core/src/main/java/org/apache/qpid/server/security/auth/manager/AbstractAuthenticationManager.java @@ -53,13 +53,13 @@ public abstract class AbstractAuthenticationManager<T extends AbstractAuthentica { private static final Logger LOGGER = Logger.getLogger(AbstractAuthenticationManager.class); - private final Broker _broker; - private PreferencesProvider _preferencesProvider; + private final Broker<?> _broker; + private PreferencesProvider<?> _preferencesProvider; @ManagedAttributeField private List<String> _secureOnlyMechanisms; - protected AbstractAuthenticationManager(final Map<String, Object> attributes, final Broker broker) + protected AbstractAuthenticationManager(final Map<String, Object> attributes, final Broker<?> broker) { super(parentsMap(broker), attributes); _broker = broker; @@ -120,13 +120,13 @@ public abstract class AbstractAuthenticationManager<T extends AbstractAuthentica } @Override - public PreferencesProvider getPreferencesProvider() + public PreferencesProvider<?> getPreferencesProvider() { return _preferencesProvider; } @Override - public void setPreferencesProvider(final PreferencesProvider preferencesProvider) + public void setPreferencesProvider(final PreferencesProvider<?> preferencesProvider) { _preferencesProvider = preferencesProvider; } @@ -143,8 +143,8 @@ public abstract class AbstractAuthenticationManager<T extends AbstractAuthentica { if(childClass == PreferencesProvider.class) { - attributes = new HashMap<String, Object>(attributes); - PreferencesProvider pp = getObjectFactory().create(PreferencesProvider.class, attributes, this); + attributes = new HashMap<>(attributes); + PreferencesProvider<?> pp = getObjectFactory().create(PreferencesProvider.class, attributes, this); _preferencesProvider = pp; return (C)pp; diff --git a/java/broker-core/src/main/java/org/apache/qpid/server/security/auth/manager/AbstractScramAuthenticationManager.java b/java/broker-core/src/main/java/org/apache/qpid/server/security/auth/manager/AbstractScramAuthenticationManager.java index 152a9086ec..6887cb99d4 100644 --- a/java/broker-core/src/main/java/org/apache/qpid/server/security/auth/manager/AbstractScramAuthenticationManager.java +++ b/java/broker-core/src/main/java/org/apache/qpid/server/security/auth/manager/AbstractScramAuthenticationManager.java @@ -20,52 +20,38 @@ */ package org.apache.qpid.server.security.auth.manager; -import java.io.IOException; -import java.nio.charset.Charset; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.security.Principal; import java.security.SecureRandom; import java.util.Arrays; import java.util.Collections; -import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.UUID; -import java.util.concurrent.ConcurrentHashMap; import javax.crypto.Mac; import javax.crypto.spec.SecretKeySpec; -import javax.security.auth.login.AccountNotFoundException; import javax.security.sasl.SaslException; import javax.security.sasl.SaslServer; import javax.xml.bind.DatatypeConverter; -import org.apache.qpid.server.configuration.updater.Task; -import org.apache.qpid.server.configuration.updater.VoidTaskWithException; import org.apache.qpid.server.model.Broker; -import org.apache.qpid.server.model.ConfiguredObject; import org.apache.qpid.server.model.PasswordCredentialManagingAuthenticationProvider; -import org.apache.qpid.server.model.User; -import org.apache.qpid.server.security.access.Operation; import org.apache.qpid.server.security.auth.AuthenticationResult; import org.apache.qpid.server.security.auth.UsernamePrincipal; import org.apache.qpid.server.security.auth.sasl.plain.PlainAdapterSaslServer; import org.apache.qpid.server.security.auth.sasl.scram.ScramSaslServer; public abstract class AbstractScramAuthenticationManager<X extends AbstractScramAuthenticationManager<X>> - extends AbstractAuthenticationManager<X> + extends ConfigModelPasswordManagingAuthenticationProvider<X> implements PasswordCredentialManagingAuthenticationProvider<X> { - static final Charset ASCII = Charset.forName("ASCII"); public static final String PLAIN = "PLAIN"; private final SecureRandom _random = new SecureRandom(); private int _iterationCount = 4096; - private Map<String, ScramAuthUser> _users = new ConcurrentHashMap<String, ScramAuthUser>(); - protected AbstractScramAuthenticationManager(final Map<String, Object> attributes, final Broker broker) { @@ -103,33 +89,9 @@ public abstract class AbstractScramAuthenticationManager<X extends AbstractScram protected abstract String getDigestName(); @Override - public AuthenticationResult authenticate(final SaslServer server, final byte[] response) - { - try - { - // Process response from the client - byte[] challenge = server.evaluateResponse(response != null ? response : new byte[0]); - - if (server.isComplete() && (challenge == null || challenge.length == 0)) - { - final String userId = server.getAuthorizationID(); - return new AuthenticationResult(new UsernamePrincipal(userId)); - } - else - { - return new AuthenticationResult(challenge, AuthenticationResult.AuthenticationStatus.CONTINUE); - } - } - catch (SaslException e) - { - return new AuthenticationResult(AuthenticationResult.AuthenticationStatus.ERROR, e); - } - } - - @Override public AuthenticationResult authenticate(final String username, final String password) { - ScramAuthUser user = getUser(username); + ManagedUser user = getUser(username); if(user != null) { final String[] usernamePassword = user.getPassword().split(","); @@ -142,7 +104,7 @@ public abstract class AbstractScramAuthenticationManager<X extends AbstractScram return new AuthenticationResult(new UsernamePrincipal(username)); } } - catch (SaslException e) + catch (IllegalArgumentException e) { return new AuthenticationResult(AuthenticationResult.AuthenticationStatus.ERROR,e); } @@ -162,7 +124,7 @@ public abstract class AbstractScramAuthenticationManager<X extends AbstractScram public byte[] getSalt(final String username) { - ScramAuthUser user = getUser(username); + ManagedUser user = getUser(username); if(user == null) { @@ -183,7 +145,7 @@ public abstract class AbstractScramAuthenticationManager<X extends AbstractScram public byte[] getSaltedPassword(final String username) throws SaslException { - ScramAuthUser user = getUser(username); + ManagedUser user = getUser(username); if(user == null) { throw new SaslException("Authentication Failed"); @@ -194,14 +156,9 @@ public abstract class AbstractScramAuthenticationManager<X extends AbstractScram } } - private ScramAuthUser getUser(final String username) - { - return _users.get(username); - } - - private byte[] createSaltedPassword(byte[] salt, String password) throws SaslException + private byte[] createSaltedPassword(byte[] salt, String password) { - Mac mac = createSha1Hmac(password.getBytes(ASCII)); + Mac mac = createShaHmac(password.getBytes(ASCII)); mac.update(salt); mac.update(INT_1); @@ -222,8 +179,7 @@ public abstract class AbstractScramAuthenticationManager<X extends AbstractScram } - private Mac createSha1Hmac(final byte[] keyBytes) - throws SaslException + private Mac createShaHmac(final byte[] keyBytes) { try { @@ -232,132 +188,16 @@ public abstract class AbstractScramAuthenticationManager<X extends AbstractScram mac.init(key); return mac; } - catch (NoSuchAlgorithmException e) - { - throw new SaslException(e.getMessage(), e); - } - catch (InvalidKeyException e) + catch (NoSuchAlgorithmException | InvalidKeyException e) { - throw new SaslException(e.getMessage(), e); + throw new IllegalArgumentException(e.getMessage(), e); } } protected abstract String getHmacName(); @Override - public boolean createUser(final String username, final String password, final Map<String, String> attributes) - { - return runTask(new Task<Boolean>() - { - @Override - public Boolean execute() - { - getSecurityManager().authoriseUserOperation(Operation.CREATE, username); - if (_users.containsKey(username)) - { - throw new IllegalArgumentException("User '" + username + "' already exists"); - } - try - { - Map<String, Object> userAttrs = new HashMap<String, Object>(); - userAttrs.put(User.ID, UUID.randomUUID()); - userAttrs.put(User.NAME, username); - userAttrs.put(User.PASSWORD, createStoredPassword(password)); - userAttrs.put(User.TYPE, ScramAuthUser.SCRAM_USER_TYPE); - ScramAuthUser user = new ScramAuthUser(userAttrs, AbstractScramAuthenticationManager.this); - user.create(); - - return true; - } - catch (SaslException e) - { - throw new IllegalArgumentException(e); - } - } - }); - } - - org.apache.qpid.server.security.SecurityManager getSecurityManager() - { - return getBroker().getSecurityManager(); - } - - @Override - public void deleteUser(final String user) throws AccountNotFoundException - { - runTask(new VoidTaskWithException<AccountNotFoundException>() - { - @Override - public void execute() throws AccountNotFoundException - { - final ScramAuthUser authUser = getUser(user); - if(authUser != null) - { - authUser.delete(); - } - else - { - throw new AccountNotFoundException("No such user: '" + user + "'"); - } - } - }); - } - - @Override - public void setPassword(final String username, final String password) throws AccountNotFoundException - { - runTask(new VoidTaskWithException<AccountNotFoundException>() - { - @Override - public void execute() throws AccountNotFoundException - { - - final ScramAuthUser authUser = getUser(username); - if (authUser != null) - { - authUser.setPassword(password); - } - else - { - throw new AccountNotFoundException("No such user: '" + username + "'"); - } - } - }); - - } - - @Override - public Map<String, Map<String, String>> getUsers() - { - return runTask(new Task<Map<String, Map<String, String>>>() - { - @Override - public Map<String, Map<String, String>> execute() - { - - Map<String, Map<String, String>> users = new HashMap<String, Map<String, String>>(); - for (String user : _users.keySet()) - { - users.put(user, Collections.<String, String>emptyMap()); - } - return users; - } - }); - } - - @Override - public void reload() throws IOException - { - - } - - @Override - public void recoverUser(final User user) - { - _users.put(user.getName(), (ScramAuthUser) user); - } - - protected String createStoredPassword(final String password) throws SaslException + protected String createStoredPassword(final String password) { byte[] salt = new byte[32]; _random.nextBytes(salt); @@ -366,33 +206,11 @@ public abstract class AbstractScramAuthenticationManager<X extends AbstractScram } @Override - public <C extends ConfiguredObject> C addChild(final Class<C> childClass, - final Map<String, Object> attributes, - final ConfiguredObject... otherParents) + void validateUser(final ManagedUser managedUser) { - if(childClass == User.class) + if(!ASCII.newEncoder().canEncode(managedUser.getName())) { - String username = (String) attributes.get("name"); - String password = (String) attributes.get("password"); - - if(createUser(username, password,null)) - { - @SuppressWarnings("unchecked") - C user = (C) _users.get(username); - return user; - } - else - { - return null; - - } + throw new IllegalArgumentException("User names are restricted to characters in the ASCII charset"); } - return super.addChild(childClass, attributes, otherParents); } - - Map<String, ScramAuthUser> getUserMap() - { - return _users; - } - } diff --git a/java/broker-core/src/main/java/org/apache/qpid/server/security/auth/manager/ConfigModelPasswordManagingAuthenticationProvider.java b/java/broker-core/src/main/java/org/apache/qpid/server/security/auth/manager/ConfigModelPasswordManagingAuthenticationProvider.java new file mode 100644 index 0000000000..5126e6978d --- /dev/null +++ b/java/broker-core/src/main/java/org/apache/qpid/server/security/auth/manager/ConfigModelPasswordManagingAuthenticationProvider.java @@ -0,0 +1,229 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.server.security.auth.manager; + +import java.io.IOException; +import java.nio.charset.Charset; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; + +import javax.security.auth.login.AccountNotFoundException; +import javax.security.sasl.SaslException; +import javax.security.sasl.SaslServer; + +import org.apache.qpid.server.configuration.updater.Task; +import org.apache.qpid.server.configuration.updater.VoidTaskWithException; +import org.apache.qpid.server.model.Broker; +import org.apache.qpid.server.model.ConfiguredObject; +import org.apache.qpid.server.model.PasswordCredentialManagingAuthenticationProvider; +import org.apache.qpid.server.model.User; +import org.apache.qpid.server.security.SecurityManager; +import org.apache.qpid.server.security.access.Operation; +import org.apache.qpid.server.security.auth.AuthenticationResult; +import org.apache.qpid.server.security.auth.UsernamePrincipal; + +public abstract class ConfigModelPasswordManagingAuthenticationProvider<X extends ConfigModelPasswordManagingAuthenticationProvider<X>> + extends AbstractAuthenticationManager<X> + implements PasswordCredentialManagingAuthenticationProvider<X> +{ + static final Charset ASCII = Charset.forName("ASCII"); + protected Map<String, ManagedUser> _users = new ConcurrentHashMap<>(); + + protected ConfigModelPasswordManagingAuthenticationProvider(final Map<String, Object> attributes, + final Broker broker) + { + super(attributes, broker); + } + + ManagedUser getUser(final String username) + { + return _users.get(username); + } + + @Override + public boolean createUser(final String username, final String password, final Map<String, String> attributes) + { + return runTask(new Task<Boolean>() + { + @Override + public Boolean execute() + { + getSecurityManager().authoriseUserOperation(Operation.CREATE, username); + if (_users.containsKey(username)) + { + throw new IllegalArgumentException("User '" + username + "' already exists"); + } + + Map<String, Object> userAttrs = new HashMap<>(); + userAttrs.put(User.ID, UUID.randomUUID()); + userAttrs.put(User.NAME, username); + userAttrs.put(User.PASSWORD, createStoredPassword(password)); + userAttrs.put(User.TYPE, ManagedUser.MANAGED_USER_TYPE); + ManagedUser user = new ManagedUser(userAttrs, ConfigModelPasswordManagingAuthenticationProvider.this); + user.create(); + + return true; + + } + }); + } + + SecurityManager getSecurityManager() + { + return getBroker().getSecurityManager(); + } + + @Override + public void deleteUser(final String user) throws AccountNotFoundException + { + runTask(new VoidTaskWithException<AccountNotFoundException>() + { + @Override + public void execute() throws AccountNotFoundException + { + final ManagedUser authUser = getUser(user); + if(authUser != null) + { + authUser.delete(); + } + else + { + throw new AccountNotFoundException("No such user: '" + user + "'"); + } + } + }); + } + + @Override + public Map<String, Map<String, String>> getUsers() + { + return runTask(new Task<Map<String, Map<String, String>>>() + { + @Override + public Map<String, Map<String, String>> execute() + { + + Map<String, Map<String, String>> users = new HashMap<>(); + for (String user : _users.keySet()) + { + users.put(user, Collections.<String, String>emptyMap()); + } + return users; + } + }); + } + + @Override + public void reload() throws IOException + { + + } + + @Override + public void recoverUser(final User user) + { + _users.put(user.getName(), (ManagedUser) user); + } + + @Override + public void setPassword(final String username, final String password) throws AccountNotFoundException + { + runTask(new VoidTaskWithException<AccountNotFoundException>() + { + @Override + public void execute() throws AccountNotFoundException + { + + final ManagedUser authUser = getUser(username); + if (authUser != null) + { + authUser.setPassword(password); + } + else + { + throw new AccountNotFoundException("No such user: '" + username + "'"); + } + } + }); + + } + + @Override + public AuthenticationResult authenticate(final SaslServer server, final byte[] response) + { + try + { + // Process response from the client + byte[] challenge = server.evaluateResponse(response != null ? response : new byte[0]); + + if (server.isComplete() && (challenge == null || challenge.length == 0)) + { + final String userId = server.getAuthorizationID(); + return new AuthenticationResult(new UsernamePrincipal(userId)); + } + else + { + return new AuthenticationResult(challenge, AuthenticationResult.AuthenticationStatus.CONTINUE); + } + } + catch (SaslException e) + { + return new AuthenticationResult(AuthenticationResult.AuthenticationStatus.ERROR, e); + } + } + + protected abstract String createStoredPassword(String password); + + Map<String, ManagedUser> getUserMap() + { + return _users; + } + + @Override + public <C extends ConfiguredObject> C addChild(final Class<C> childClass, + final Map<String, Object> attributes, + final ConfiguredObject... otherParents) + { + if(childClass == User.class) + { + String username = (String) attributes.get("name"); + String password = (String) attributes.get("password"); + + if(createUser(username, password,null)) + { + @SuppressWarnings("unchecked") + C user = (C) getUser(username); + return user; + } + else + { + return null; + + } + } + return super.addChild(childClass, attributes, otherParents); + } + + abstract void validateUser(final ManagedUser managedUser); +} diff --git a/java/broker-core/src/main/java/org/apache/qpid/server/security/auth/manager/MD5AuthenticationProvider.java b/java/broker-core/src/main/java/org/apache/qpid/server/security/auth/manager/MD5AuthenticationProvider.java new file mode 100644 index 0000000000..cdb2f3dcc7 --- /dev/null +++ b/java/broker-core/src/main/java/org/apache/qpid/server/security/auth/manager/MD5AuthenticationProvider.java @@ -0,0 +1,227 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.server.security.auth.manager; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.security.Principal; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import javax.security.auth.callback.Callback; +import javax.security.auth.callback.CallbackHandler; +import javax.security.auth.callback.NameCallback; +import javax.security.auth.callback.PasswordCallback; +import javax.security.auth.callback.UnsupportedCallbackException; +import javax.security.sasl.AuthorizeCallback; +import javax.security.sasl.SaslException; +import javax.security.sasl.SaslServer; +import javax.xml.bind.DatatypeConverter; + +import org.apache.qpid.server.model.Broker; +import org.apache.qpid.server.model.ManagedObject; +import org.apache.qpid.server.model.ManagedObjectFactoryConstructor; +import org.apache.qpid.server.security.auth.AuthenticationResult; +import org.apache.qpid.server.security.auth.UsernamePrincipal; +import org.apache.qpid.server.security.auth.sasl.crammd5.CRAMMD5HashedSaslServer; +import org.apache.qpid.server.security.auth.sasl.crammd5.CRAMMD5HexSaslServer; +import org.apache.qpid.server.security.auth.sasl.plain.PlainAdapterSaslServer; +import org.apache.qpid.server.security.auth.sasl.plain.PlainSaslServer; +import org.apache.qpid.server.util.ServerScopedRuntimeException; + +@ManagedObject( category = false, type = "MD5" ) +public class MD5AuthenticationProvider + extends ConfigModelPasswordManagingAuthenticationProvider<MD5AuthenticationProvider> +{ + private final List<String> _mechanisms = Collections.unmodifiableList(Arrays.asList(PlainSaslServer.MECHANISM, + CRAMMD5HashedSaslServer.MECHANISM, + CRAMMD5HexSaslServer.MECHANISM)); + + + @ManagedObjectFactoryConstructor + protected MD5AuthenticationProvider(final Map<String, Object> attributes, final Broker broker) + { + super(attributes, broker); + } + + @Override + protected String createStoredPassword(final String password) + { + byte[] data = password.getBytes(StandardCharsets.UTF_8); + MessageDigest md = null; + try + { + md = MessageDigest.getInstance("MD5"); + } + catch (NoSuchAlgorithmException e) + { + throw new ServerScopedRuntimeException("MD5 not supported although Java compliance requires it"); + } + + md.update(data); + return DatatypeConverter.printBase64Binary(md.digest()); + } + + @Override + void validateUser(final ManagedUser managedUser) + { + } + + @Override + public List<String> getMechanisms() + { + return _mechanisms; + } + + @Override + public SaslServer createSaslServer(final String mechanism, + final String localFQDN, + final Principal externalPrincipal) + throws SaslException + { + if(PlainSaslServer.MECHANISM.equals(mechanism)) + { + return new PlainAdapterSaslServer(this); + } + else if(CRAMMD5HashedSaslServer.MECHANISM.equals(mechanism)) + { + //simply delegate to the built in CRAM-MD5 SaslServer + return new CRAMMD5HashedSaslServer(mechanism, "AMQP", localFQDN, null, new MD5Callbackhandler(false)); + } + else if(CRAMMD5HexSaslServer.MECHANISM.equals(mechanism)) + { + //simply delegate to the built in CRAM-MD5 SaslServer + return new CRAMMD5HashedSaslServer(mechanism, "AMQP", localFQDN, null, new MD5Callbackhandler(true)); + } + else + { + throw new SaslException("Unsupported mechanism: " + mechanism); + } + } + + @Override + public AuthenticationResult authenticate(final String username, final String password) + { + ManagedUser user = getUser(username); + AuthenticationResult result; + if(user != null && user.getPassword().equals(createStoredPassword(password))) + { + result = new AuthenticationResult(new UsernamePrincipal(username)); + } + else + { + result = new AuthenticationResult(AuthenticationResult.AuthenticationStatus.ERROR); + } + return result; + } + private static final char[] HEX_CHARACTERS = + {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; + + private class MD5Callbackhandler implements CallbackHandler + { + private final boolean _hexify; + private String _username; + + public MD5Callbackhandler(final boolean hexify) + { + _hexify = hexify; + } + + public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException + { + List<Callback> callbackList = new ArrayList<>(Arrays.asList(callbacks)); + Iterator<Callback> iter = callbackList.iterator(); + while(iter.hasNext()) + { + Callback callback = iter.next(); + if (callback instanceof NameCallback) + { + _username = ((NameCallback) callback).getDefaultName(); + iter.remove(); + break; + } + } + + if(_username != null) + { + iter = callbackList.iterator(); + while (iter.hasNext()) + { + Callback callback = iter.next(); + if (callback instanceof PasswordCallback) + { + iter.remove(); + ManagedUser user = getUser(_username); + if(user != null) + { + String passwordData = user.getPassword(); + byte[] passwordBytes = DatatypeConverter.parseBase64Binary(passwordData); + char[] password; + if(_hexify) + { + password = new char[passwordBytes.length]; + + for(int i = 0; i < passwordBytes.length; i--) + { + password[2*i] = HEX_CHARACTERS[(((int)passwordBytes[i]) & 0xf0)>>4]; + password[(2*i)+1] = HEX_CHARACTERS[(((int)passwordBytes[i]) & 0x0f)]; + } + } + else + { + password = new char[passwordBytes.length]; + for(int i = 0; i < passwordBytes.length; i++) + { + password[i] = (char) passwordBytes[i]; + } + } + ((PasswordCallback) callback).setPassword(password); + } + else + { + ((PasswordCallback) callback).setPassword(null); + } + break; + } + } + } + + for (Callback callback : callbackList) + { + + if (callback instanceof AuthorizeCallback) + { + ((AuthorizeCallback) callback).setAuthorized(true); + } + else + { + throw new UnsupportedCallbackException(callback); + } + } + } + } +} diff --git a/java/broker-core/src/main/java/org/apache/qpid/server/security/auth/manager/ScramAuthUser.java b/java/broker-core/src/main/java/org/apache/qpid/server/security/auth/manager/ManagedUser.java index b3de1d1f17..c8884e15a8 100644 --- a/java/broker-core/src/main/java/org/apache/qpid/server/security/auth/manager/ScramAuthUser.java +++ b/java/broker-core/src/main/java/org/apache/qpid/server/security/auth/manager/ManagedUser.java @@ -27,8 +27,6 @@ import java.util.HashMap; import java.util.Map; import java.util.Set; -import javax.security.sasl.SaslException; - import org.apache.qpid.server.configuration.updater.VoidTask; import org.apache.qpid.server.model.AbstractConfiguredObject; import org.apache.qpid.server.model.ConfiguredObject; @@ -41,24 +39,21 @@ import org.apache.qpid.server.model.StateTransition; import org.apache.qpid.server.model.User; import org.apache.qpid.server.security.access.Operation; -@ManagedObject( category = false, type = ScramAuthUser.SCRAM_USER_TYPE) -class ScramAuthUser extends AbstractConfiguredObject<ScramAuthUser> implements User<ScramAuthUser> +@ManagedObject( category = false, type = ManagedUser.MANAGED_USER_TYPE) +class ManagedUser extends AbstractConfiguredObject<ManagedUser> implements User<ManagedUser> { - public static final String SCRAM_USER_TYPE = "scram"; + public static final String MANAGED_USER_TYPE = "managed"; - private AbstractScramAuthenticationManager _authenticationManager; + private ConfigModelPasswordManagingAuthenticationProvider<?> _authenticationManager; @ManagedAttributeField private String _password; @ManagedObjectFactoryConstructor - ScramAuthUser(final Map<String, Object> attributes, AbstractScramAuthenticationManager parent) + ManagedUser(final Map<String, Object> attributes, ConfigModelPasswordManagingAuthenticationProvider<?> parent) { super(parentsMap(parent), attributes); _authenticationManager = parent; - if(!ScramSHA1AuthenticationManager.ASCII.newEncoder().canEncode(getName())) - { - throw new IllegalArgumentException("Scram SHA1 user names are restricted to characters in the ASCII charset"); - } + setState(State.ACTIVE); } @@ -73,6 +68,7 @@ class ScramAuthUser extends AbstractConfiguredObject<ScramAuthUser> implements U public void onValidate() { super.onValidate(); + _authenticationManager.validateUser(this); if(!isDurable()) { throw new IllegalArgumentException(getClass().getSimpleName() + " must be durable"); @@ -122,17 +118,11 @@ class ScramAuthUser extends AbstractConfiguredObject<ScramAuthUser> implements U if (attributes.containsKey(User.PASSWORD) && !newPassword.equals(getActualAttributes().get(User.PASSWORD))) { - try - { - modifiedAttributes.put(User.PASSWORD, - _authenticationManager.createStoredPassword(newPassword)); - } - catch (SaslException e) - { - throw new IllegalArgumentException(e); - } + modifiedAttributes.put(User.PASSWORD, + _authenticationManager.createStoredPassword(newPassword)); + } - ScramAuthUser.super.setAttributes(modifiedAttributes); + ManagedUser.super.setAttributes(modifiedAttributes); } }); @@ -150,15 +140,9 @@ class ScramAuthUser extends AbstractConfiguredObject<ScramAuthUser> implements U { _authenticationManager.getSecurityManager().authoriseUserOperation(Operation.UPDATE, getName()); - try - { - changeAttribute(User.PASSWORD, getAttribute(User.PASSWORD), _authenticationManager.createStoredPassword( - password)); - } - catch (SaslException e) - { - throw new IllegalArgumentException(e); - } + changeAttribute(User.PASSWORD, getAttribute(User.PASSWORD), + _authenticationManager.createStoredPassword(password)); + } @Override @@ -170,7 +154,7 @@ class ScramAuthUser extends AbstractConfiguredObject<ScramAuthUser> implements U @Override public Map<String, Object> getPreferences() { - PreferencesProvider preferencesProvider = _authenticationManager.getPreferencesProvider(); + PreferencesProvider<?> preferencesProvider = _authenticationManager.getPreferencesProvider(); if (preferencesProvider == null) { return null; @@ -192,7 +176,7 @@ class ScramAuthUser extends AbstractConfiguredObject<ScramAuthUser> implements U @Override public Map<String, Object> setPreferences(Map<String, Object> preferences) { - PreferencesProvider preferencesProvider = _authenticationManager.getPreferencesProvider(); + PreferencesProvider<?> preferencesProvider = _authenticationManager.getPreferencesProvider(); if (preferencesProvider == null) { return null; diff --git a/java/broker-core/src/main/java/org/apache/qpid/server/security/auth/manager/PlainAuthenticationProvider.java b/java/broker-core/src/main/java/org/apache/qpid/server/security/auth/manager/PlainAuthenticationProvider.java new file mode 100644 index 0000000000..5c79253ebb --- /dev/null +++ b/java/broker-core/src/main/java/org/apache/qpid/server/security/auth/manager/PlainAuthenticationProvider.java @@ -0,0 +1,176 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.server.security.auth.manager; + +import java.io.IOException; +import java.security.Principal; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import javax.security.auth.callback.Callback; +import javax.security.auth.callback.CallbackHandler; +import javax.security.auth.callback.NameCallback; +import javax.security.auth.callback.PasswordCallback; +import javax.security.auth.callback.UnsupportedCallbackException; +import javax.security.sasl.AuthorizeCallback; +import javax.security.sasl.Sasl; +import javax.security.sasl.SaslException; +import javax.security.sasl.SaslServer; + +import org.apache.qpid.server.model.Broker; +import org.apache.qpid.server.model.ManagedObject; +import org.apache.qpid.server.model.ManagedObjectFactoryConstructor; +import org.apache.qpid.server.security.auth.AuthenticationResult; +import org.apache.qpid.server.security.auth.UsernamePrincipal; +import org.apache.qpid.server.security.auth.sasl.crammd5.CRAMMD5Initialiser; +import org.apache.qpid.server.security.auth.sasl.plain.PlainAdapterSaslServer; +import org.apache.qpid.server.security.auth.sasl.plain.PlainSaslServer; + +@ManagedObject( category = false, type = "Plain" ) +public class PlainAuthenticationProvider + extends ConfigModelPasswordManagingAuthenticationProvider<PlainAuthenticationProvider> +{ + private final List<String> _mechanisms = Collections.unmodifiableList(Arrays.asList(PlainSaslServer.MECHANISM, + CRAMMD5Initialiser.MECHANISM)); + + + @ManagedObjectFactoryConstructor + protected PlainAuthenticationProvider(final Map<String, Object> attributes, final Broker broker) + { + super(attributes, broker); + } + + @Override + protected String createStoredPassword(final String password) + { + return password; + } + + @Override + void validateUser(final ManagedUser managedUser) + { + // NOOP + } + + @Override + public List<String> getMechanisms() + { + return _mechanisms; + } + + @Override + public SaslServer createSaslServer(final String mechanism, + final String localFQDN, + final Principal externalPrincipal) + throws SaslException + { + if(PlainSaslServer.MECHANISM.equals(mechanism)) + { + return new PlainAdapterSaslServer(this); + } + else if(CRAMMD5Initialiser.MECHANISM.equals(mechanism)) + { + //simply delegate to the built in CRAM-MD5 SaslServer + return Sasl.createSaslServer(mechanism, "AMQP", localFQDN, null, new ServerCallbackHandler()); + } + else + { + throw new SaslException("Unsupported mechanism: " + mechanism); + } + } + + @Override + public AuthenticationResult authenticate(final String username, final String password) + { + ManagedUser user = getUser(username); + AuthenticationResult result; + if(user != null && user.getPassword().equals(password)) + { + result = new AuthenticationResult(new UsernamePrincipal(username)); + } + else + { + result = new AuthenticationResult(AuthenticationResult.AuthenticationStatus.ERROR); + } + return result; + } + + private class ServerCallbackHandler implements CallbackHandler + { + String _username; + + public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException + { + List<Callback> callbackList = new ArrayList<>(Arrays.asList(callbacks)); + Iterator<Callback> iter = callbackList.iterator(); + while(iter.hasNext()) + { + Callback callback = iter.next(); + if (callback instanceof NameCallback) + { + _username = ((NameCallback) callback).getDefaultName(); + iter.remove(); + break; + } + } + + if(_username != null) + { + iter = callbackList.iterator(); + while (iter.hasNext()) + { + Callback callback = iter.next(); + if (callback instanceof PasswordCallback) + { + iter.remove(); + ManagedUser user = getUser(_username); + if(user != null) + { + ((PasswordCallback) callback).setPassword(user.getPassword().toCharArray()); + } + else + { + ((PasswordCallback) callback).setPassword(null); + } + break; + } + } + } + + for (Callback callback : callbackList) + { + + if (callback instanceof AuthorizeCallback) + { + ((AuthorizeCallback) callback).setAuthorized(true); + } + else + { + throw new UnsupportedCallbackException(callback); + } + } + } + } +} |