Author: rgodfrey Date: Thu Apr 13 09:22:55 2017 New Revision: 1791223 URL: http://svn.apache.org/viewvc?rev=1791223&view=rev Log: QPID-7742 : Take hostname from SNI and SASL when available
Modified: qpid/java/trunk/broker-core/src/main/java/org/apache/qpid/server/transport/NonBlockingConnection.java qpid/java/trunk/broker-core/src/main/java/org/apache/qpid/server/transport/NonBlockingConnectionTLSDelegate.java qpid/java/trunk/broker-core/src/main/java/org/apache/qpid/server/transport/ServerNetworkConnection.java qpid/java/trunk/broker-core/src/main/java/org/apache/qpid/server/transport/network/security/ssl/SSLUtil.java qpid/java/trunk/broker-plugins/amqp-1-0-protocol/src/main/java/org/apache/qpid/server/protocol/v1_0/AMQPConnection_1_0Impl.java Modified: qpid/java/trunk/broker-core/src/main/java/org/apache/qpid/server/transport/NonBlockingConnection.java URL: http://svn.apache.org/viewvc/qpid/java/trunk/broker-core/src/main/java/org/apache/qpid/server/transport/NonBlockingConnection.java?rev=1791223&r1=1791222&r2=1791223&view=diff ============================================================================== --- qpid/java/trunk/broker-core/src/main/java/org/apache/qpid/server/transport/NonBlockingConnection.java (original) +++ qpid/java/trunk/broker-core/src/main/java/org/apache/qpid/server/transport/NonBlockingConnection.java Thu Apr 13 09:22:55 2017 @@ -76,6 +76,7 @@ public class NonBlockingConnection imple private final List<SchedulingDelayNotificationListener> _schedulingDelayNotificationListeners = new CopyOnWriteArrayList<>(); private final AtomicBoolean _hasShutdown = new AtomicBoolean(); private volatile long _bufferedSize; + private String _selectedHost; public NonBlockingConnection(SocketChannel socketChannel, ProtocolEngine protocolEngine, @@ -658,4 +659,15 @@ public class NonBlockingConnection imple { _selectionTask = selectionTask; } + + public void setSelectedHost(final String selectedHost) + { + _selectedHost = selectedHost; + } + + @Override + public String getSelectedHost() + { + return _selectedHost; + } } Modified: qpid/java/trunk/broker-core/src/main/java/org/apache/qpid/server/transport/NonBlockingConnectionTLSDelegate.java URL: http://svn.apache.org/viewvc/qpid/java/trunk/broker-core/src/main/java/org/apache/qpid/server/transport/NonBlockingConnectionTLSDelegate.java?rev=1791223&r1=1791222&r2=1791223&view=diff ============================================================================== --- qpid/java/trunk/broker-core/src/main/java/org/apache/qpid/server/transport/NonBlockingConnectionTLSDelegate.java (original) +++ qpid/java/trunk/broker-core/src/main/java/org/apache/qpid/server/transport/NonBlockingConnectionTLSDelegate.java Thu Apr 13 09:22:55 2017 @@ -55,6 +55,7 @@ public class NonBlockingConnectionTLSDel private Principal _principal; private Certificate _peerCertificate; private boolean _principalChecked; + private volatile boolean _hostChecked; private QpidByteBuffer _netInputBuffer; private QpidByteBuffer _netOutputBuffer; private QpidByteBuffer _applicationBuffer; @@ -87,6 +88,31 @@ public class NonBlockingConnectionTLSDel @Override public boolean processData() throws IOException { + if(!_hostChecked) + { + QpidByteBuffer buffer = _netInputBuffer.duplicate(); + try + { + buffer.flip(); + if (SSLUtil.isSufficientToDetermineClientSNIHost(buffer)) + { + String hostName = SSLUtil.getServerNameFromTLSClientHello(buffer); + if (hostName != null) + { + _parent.setSelectedHost(hostName); + } + _hostChecked = true; + } + else + { + return false; + } + } + finally + { + buffer.dispose();; + } + } _netInputBuffer.flip(); boolean readData = false; boolean tasksRun; Modified: qpid/java/trunk/broker-core/src/main/java/org/apache/qpid/server/transport/ServerNetworkConnection.java URL: http://svn.apache.org/viewvc/qpid/java/trunk/broker-core/src/main/java/org/apache/qpid/server/transport/ServerNetworkConnection.java?rev=1791223&r1=1791222&r2=1791223&view=diff ============================================================================== --- qpid/java/trunk/broker-core/src/main/java/org/apache/qpid/server/transport/ServerNetworkConnection.java (original) +++ qpid/java/trunk/broker-core/src/main/java/org/apache/qpid/server/transport/ServerNetworkConnection.java Thu Apr 13 09:22:55 2017 @@ -30,4 +30,6 @@ public interface ServerNetworkConnection void addSchedulingDelayNotificationListeners(SchedulingDelayNotificationListener listener); void removeSchedulingDelayNotificationListeners(SchedulingDelayNotificationListener listener); + + String getSelectedHost(); } Modified: qpid/java/trunk/broker-core/src/main/java/org/apache/qpid/server/transport/network/security/ssl/SSLUtil.java URL: http://svn.apache.org/viewvc/qpid/java/trunk/broker-core/src/main/java/org/apache/qpid/server/transport/network/security/ssl/SSLUtil.java?rev=1791223&r1=1791222&r2=1791223&view=diff ============================================================================== --- qpid/java/trunk/broker-core/src/main/java/org/apache/qpid/server/transport/network/security/ssl/SSLUtil.java (original) +++ qpid/java/trunk/broker-core/src/main/java/org/apache/qpid/server/transport/network/security/ssl/SSLUtil.java Thu Apr 13 09:22:55 2017 @@ -56,15 +56,17 @@ import java.util.TreeSet; import javax.naming.InvalidNameException; import javax.naming.ldap.LdapName; import javax.naming.ldap.Rdn; +import javax.net.ssl.SNIHostName; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLEngine; -import javax.net.ssl.SSLParameters; import javax.net.ssl.SSLPeerUnverifiedException; import javax.net.ssl.SSLSocket; +import javax.net.ssl.StandardConstants; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.apache.qpid.server.bytebuffer.QpidByteBuffer; import org.apache.qpid.server.transport.TransportException; import org.apache.qpid.server.util.Strings; @@ -612,4 +614,142 @@ public class SSLUtil throw new NoSuchAlgorithmException(String.format("Could not create SSLContext with one of the requested protocols: %s", Arrays.toString(protocols))); } + + public static boolean isSufficientToDetermineClientSNIHost(QpidByteBuffer buffer) + { + if(buffer.remaining() < 6) + { + return false; + } + else if(looksLikeSSLv3ClientHello(buffer)) + { + final int position = buffer.position(); + final int recordSize = 5 + (((buffer.get(position + 3) & 0xFF) << 8) | (buffer.get(position + 4) & 0xFF)); + return buffer.remaining() >= recordSize; + } + else + { + return true; + } + } + + private static boolean looksLikeSSLv3ClientHello(QpidByteBuffer buffer) + { + int first = buffer.get(buffer.position()+0); + int second = buffer.get(buffer.position()+1); + int third = buffer.get(buffer.position()+2); + int sixth = buffer.get(buffer.position()+5); + + return first == 22 && // SSL Handshake + (second == 3 && // SSL 3.0 / TLS 1.x + (third == 0 || // SSL 3.0 + third == 1 || // TLS 1.0 + third == 2 || // TLS 1.1 + third == 3)) && // TLS1.2 + (sixth == 1); // client_hello + } + + public final static String getServerNameFromTLSClientHello(QpidByteBuffer source) + { + + QpidByteBuffer input = source.duplicate(); + try + { + + // Do we have a complete header? + if (input.remaining() < 5) + { + return null; + } + + byte first = input.get(); + byte second = input.get(); + byte third = input.get(); + if (first == 22 && third != 0x01) + { + + int recordLength = input.getUnsignedShort(); + if (recordLength > input.remaining()) + { + return null; + } + + if (input.get() == 0x01) + { + // 24-bit length field + int length = ((input.get() & 0xFF) << 16) | ((input.get() & 0xFF) << 8) | (input.get() & 0xFF); + + input.limit(length + input.position()); + + input.position(input.position() + 34); // hello minor/major version + random + int skip = (int) input.get(); // session-id + input.position(input.position() + skip); + skip = input.getUnsignedShort(); // cipher suites + input.position(input.position() + skip); + skip = (int) input.get(); // compression methods + input.position(input.position() + skip); + + if (input.hasRemaining()) + { + + int remaining = input.getUnsignedShort(); + input.limit(input.position()+remaining); + while (input.hasRemaining()) + { + int extensionType = input.getUnsignedShort(); + + int extensionLength = input.getUnsignedShort(); + + if (extensionType == 0x00) + { + + int extensionDataRemaining = extensionLength; + if (extensionDataRemaining >= 2) + { + int listLength = input.getUnsignedShort(); // length of server_name_list + if (listLength + 2 != extensionDataRemaining) + { + // invalid format + return null; + } + + extensionDataRemaining -= 2; + while (extensionDataRemaining > 0) + { + int code = input.get(); + int serverNameLength = input.getUnsignedShort(); + if (serverNameLength > extensionDataRemaining) + { + // invalid format; + return null; + } + byte[] encoded = new byte[serverNameLength]; + input.get(encoded); + + if (code == StandardConstants.SNI_HOST_NAME) + { + + return new SNIHostName(encoded).getAsciiName(); + } + extensionDataRemaining -= serverNameLength + 3; + } + } + return null; + } + else + { + input.position(input.position() + extensionLength); + } + } + } + } + } + return null; + + } + finally + { + input.dispose(); + } + } } Modified: qpid/java/trunk/broker-plugins/amqp-1-0-protocol/src/main/java/org/apache/qpid/server/protocol/v1_0/AMQPConnection_1_0Impl.java URL: http://svn.apache.org/viewvc/qpid/java/trunk/broker-plugins/amqp-1-0-protocol/src/main/java/org/apache/qpid/server/protocol/v1_0/AMQPConnection_1_0Impl.java?rev=1791223&r1=1791222&r2=1791223&view=diff ============================================================================== --- qpid/java/trunk/broker-plugins/amqp-1-0-protocol/src/main/java/org/apache/qpid/server/protocol/v1_0/AMQPConnection_1_0Impl.java (original) +++ qpid/java/trunk/broker-plugins/amqp-1-0-protocol/src/main/java/org/apache/qpid/server/protocol/v1_0/AMQPConnection_1_0Impl.java Thu Apr 13 09:22:55 2017 @@ -732,7 +732,16 @@ public class AMQPConnection_1_0Impl exte ? getBroker().getNetworkBufferSize() : Math.min(open.getMaxFrameSize().intValue(), getBroker().getNetworkBufferSize()); _remoteContainerId = open.getContainerId(); - _localHostname = open.getHostname(); + + if(open.getHostname() != null && !"".equals(open.getHostname().trim())) + { + _localHostname = open.getHostname(); + } + + if(_localHostname == null || "".equals(_localHostname.trim()) && getNetwork().getSelectedHost() != null) + { + _localHostname = getNetwork().getSelectedHost(); + } if (open.getIdleTimeOut() != null) { _idleTimeout = open.getIdleTimeOut().longValue(); @@ -932,6 +941,14 @@ public class AMQPConnection_1_0Impl exte public void receiveSaslInit(final SaslInit saslInit) { assertState(FrameReceivingState.SASL_INIT_ONLY); + if(saslInit.getHostname() != null && !"".equals(saslInit.getHostname().trim())) + { + _localHostname = saslInit.getHostname(); + } + else if(getNetwork().getSelectedHost() != null) + { + _localHostname = getNetwork().getSelectedHost(); + } String mechanism = saslInit.getMechanism() == null ? null : saslInit.getMechanism().toString(); final Binary initialResponse = saslInit.getInitialResponse(); byte[] response = initialResponse == null ? new byte[0] : initialResponse.getArray(); @@ -940,6 +957,12 @@ public class AMQPConnection_1_0Impl exte processSaslResponse(response); } + @Override + public String getLocalFQDN() + { + return _localHostname != null ? _localHostname : super.getLocalFQDN(); + } + private void processSaslResponse(final byte[] response) { byte[] challenge = null; --------------------------------------------------------------------- To unsubscribe, e-mail: commits-unsubscr...@qpid.apache.org For additional commands, e-mail: commits-h...@qpid.apache.org