| /* |
| * |
| * 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); |
| } |
| } |
| } |
| } |
| } |