blob: f121df298e5f8d0166998893b1fcf6c338f5152b [file] [log] [blame]
/*
*
* 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.proton.engine.impl.ssl;
import org.apache.qpid.proton.engine.impl.TransportWrapper;
public class SslHandshakeSniffingTransportWrapper implements SslTransportWrapper
{
private final SslTransportWrapper _secureTransportWrapper;
private final TransportWrapper _plainTransportWrapper;
private boolean _determinationMade = false;
private boolean _isSecure;
public SslHandshakeSniffingTransportWrapper(
SslTransportWrapper secureTransportWrapper,
TransportWrapper plainTransportWrapper)
{
_secureTransportWrapper = secureTransportWrapper;
_plainTransportWrapper = plainTransportWrapper;
}
@Override
public int input(byte[] sourceBuffer, int offset, int size)
{
if (_determinationMade==false)
{
byte[] zeroBasedSrcBytes = new byte[size];
System.arraycopy(sourceBuffer, offset, zeroBasedSrcBytes, 0, size);
_isSecure = checkForSslHandshake(zeroBasedSrcBytes);
_determinationMade = true;
}
if (_isSecure)
{
return _secureTransportWrapper.input(sourceBuffer, offset, size);
}
else
{
return _plainTransportWrapper.input(sourceBuffer, offset, size);
}
}
@Override
public int output(byte[] destinationBuffer, int offset, int size)
{
if (_determinationMade == false)
{
_isSecure = false;
_determinationMade = true;
}
if (_isSecure)
{
return _secureTransportWrapper.output(destinationBuffer, offset, size);
}
else
{
return _plainTransportWrapper.output(destinationBuffer, offset, size);
}
}
@Override
public String getCipherName()
{
return _secureTransportWrapper.getCipherName();
}
@Override
public String getProtocolName()
{
return _secureTransportWrapper.getProtocolName();
}
// TODO perhaps the sniffer should save up the bytes from each
// input call until it has sufficient bytes to make the determination
// and only then pass them to the secure or plain wrapped transport?
private boolean checkForSslHandshake(byte[] buf)
{
if (buf.length >= 5)
{
/*
* SSLv2 Client Hello format
* http://www.mozilla.org/projects/security/pki/nss/ssl/draft02.html
*
* Bytes 0-1: RECORD-LENGTH Byte 2: MSG-CLIENT-HELLO (1) Byte 3:
* CLIENT-VERSION-MSB Byte 4: CLIENT-VERSION-LSB
*
* Allowed versions: 2.0 - SSLv2 3.0 - SSLv3 3.1 - TLS 1.0 3.2 - TLS
* 1.1 3.3 - TLS 1.2
*
* The version sent in the Client-Hello is the latest version
* supported by the client. NSS may send version 3.x in an SSLv2
* header for maximum compatibility.
*/
boolean isSSL2Handshake = buf[2] == 1 && // MSG-CLIENT-HELLO
((buf[3] == 3 && buf[4] <= 3) || // SSL 3.0 & TLS 1.0-1.2
// (v3.1-3.3)
(buf[3] == 2 && buf[4] == 0)); // SSL 2
/*
* SSLv3/TLS Client Hello format RFC 2246
*
* Byte 0: ContentType (handshake - 22) Bytes 1-2: ProtocolVersion
* {major, minor}
*
* Allowed versions: 3.0 - SSLv3 3.1 - TLS 1.0 3.2 - TLS 1.1 3.3 -
* TLS 1.2
*/
boolean isSSL3Handshake = buf[0] == 22 && // handshake
(buf[1] == 3 && buf[2] <= 3); // SSL 3.0 & TLS 1.0-1.2
// (v3.1-3.3)
return (isSSL2Handshake || isSSL3Handshake);
}
else
{
throw new IllegalArgumentException("Too few bytes (" + buf.length + ") to make SSL/plain determination.");
}
}
}