diff options
Diffstat (limited to 'java/broker-core/src/main/java/org/apache/qpid/server/security/auth/manager/MD5AuthenticationProvider.java')
-rw-r--r-- | java/broker-core/src/main/java/org/apache/qpid/server/security/auth/manager/MD5AuthenticationProvider.java | 227 |
1 files changed, 227 insertions, 0 deletions
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); + } + } + } + } +} |