}
if (helloState == atHelloNone) {
- helloSize = features.parseMsgHead(rbuf);
+ const int helloSize = features.parseMsgHead(rbuf);
if (helloSize == 0) {
// Not enough bytes to get hello message size
BIO_set_retry_read(table);
const char *s = objToString(head, rbuf.contentSize());
debugs(83, 7, "SSL Header: " << s);
- if (helloSize > rbuf.contentSize()) {
+ if (!features.helloRecord(rbuf)) {
BIO_set_retry_read(table);
return -1;
}
}
}
-Ssl::Bio::sslFeatures::sslFeatures(): sslVersion(-1), compressMethod(-1), helloMsgSize(0), unknownCiphers(false), doHeartBeats(true), tlsTicketsExtension(false), hasTlsTicket(false), tlsStatusRequest(false), hasCcsOrNst(false), initialized_(false)
+Ssl::Bio::sslFeatures::sslFeatures(): sslVersion(-1), compressMethod(-1), helloRecordStart(0), helloMsgSize(0), unknownCiphers(false), doHeartBeats(true), tlsTicketsExtension(false), hasTlsTicket(false), tlsStatusRequest(false), hasCcsOrNst(false), initialized_(false)
{
memset(client_random, 0, SSL3_RANDOM_SIZE);
}
if (helloMsgSize > 0)
return helloMsgSize;
- // Check for SSLPlaintext/TLSPlaintext record
- // RFC6101 section 5.2.1
- // RFC5246 section 6.2.1
- if (head[0] == 0x16) {
- debugs(83, 7, "SSL version 3 handshake message");
- // The SSL version exist in the 2nd and 3rd bytes
- sslVersion = (head[1] << 8) | head[2];
- debugs(83, 7, "SSL Version :" << std::hex << std::setw(8) << std::setfill('0') << sslVersion);
- // The hello message size exist in 4th and 5th bytes
- helloMsgSize = (head[3] << 8) + head[4];
- debugs(83, 7, "SSL Header Size: " << helloMsgSize);
- helloMsgSize +=5;
- } else if ((head[0] & 0x80) && head[2] == 0x01 && head[3] == 0x03) {
+ if ((head[0] & 0x80) && head[2] == 0x01 && head[3] == 0x03) {
debugs(83, 7, "SSL version 2 handshake message with v3 support");
sslVersion = (head[3] << 8) | head[4];
+ helloRecordStart = 0;
debugs(83, 7, "SSL Version :" << std::hex << std::setw(8) << std::setfill('0') << sslVersion);
// The hello message size exist in 2nd byte
helloMsgSize = head[1];
helloMsgSize +=2;
- } else {
- debugs(83, 7, "Not an SSL acceptable handshake message (SSLv2 message?)");
- return (helloMsgSize = -1);
+ initialized_ = true;
+ return helloMsgSize;
}
- // Set object as initialized. Even if we did not full parsing yet
- // The basic features, like the SSL version is set
- initialized_ = true;
- return helloMsgSize;
+ const int headSize = buf.contentSize();
+ int currentPos = 0;
+ do {
+ const unsigned char *currentRecord = head + currentPos;
+ // Check for Alert Protocol records before hello message. RFC5246 section-7.2
+ if (currentRecord[0] == 0x15) {
+ int recordSize = (currentRecord[3] << 8) + currentRecord[4];
+ // We need at least 5 bytes for each record.
+ if ((currentPos + recordSize + 5) > headSize)
+ return 0; // Not enough bytes;
+ // Check for fatal Alert and abort if found
+ if (currentRecord[5] > 1)
+ return -1;
+ currentPos += recordSize + 5;
+ } else if (currentRecord[0] == 0x16) {
+ // SSLPlaintext/TLSPlaintext record
+ // RFC6101 section 5.2.1, RFC5246 section 6.2.1
+ debugs(83, 7, "SSL version 3 handshake message");
+ // The SSL version exist in the 2nd and 3rd bytes
+ sslVersion = (currentRecord[1] << 8) | currentRecord[2];
+ debugs(83, 7, "SSL Version :" << std::hex << std::setw(8) << std::setfill('0') << sslVersion);
+ // The hello message size exist in 4th and 5th bytes
+ helloMsgSize = (currentRecord[3] << 8) + currentRecord[4];
+ debugs(83, 7, "SSL Header Size: " << helloMsgSize);
+ helloMsgSize +=5;
+ helloRecordStart = currentPos;
+
+ // Set object as initialized. Even if we did not full parsing yet
+ // The basic features, like the SSL version is set
+ initialized_ = true;
+ return helloMsgSize;
+ } else {
+ debugs(83, 7, "Not an SSL acceptable handshake message (SSLv2 message?)");
+ return (helloMsgSize = -1);
+ }
+ } while (currentPos + 5 <= headSize);
+
+ return 0;
}
bool
-Ssl::Bio::sslFeatures::checkForCcsOrNst(const unsigned char *msg, size_t size)
+Ssl::Bio::sslFeatures::checkForCcsOrNst(const MemBuf &buf)
{
+ if (helloMsgSize <= 0) //unparsed content?
+ return false;
+
+ // Check the records after the Hello record.
+ const int afterHello = helloRecordStart + helloMsgSize;
+ const unsigned char *msg = reinterpret_cast<const unsigned char *>(buf.content()) + afterHello;
+ size_t size = (buf.contentSize() > afterHello) ? (size_t)(buf.contentSize() - afterHello) : 0;
while (size > 5) {
const int msgType = msg[0];
const int msgSslVersion = (msg[1] << 8) | msg[2];
return false;
}
+const unsigned char *
+Ssl::Bio::sslFeatures::helloRecord(const MemBuf &buf)
+{
+ if (helloMsgSize <= 0)
+ return NULL;
+
+ if (helloRecordStart + helloMsgSize <= buf.contentSize())
+ return reinterpret_cast<const unsigned char *>(buf.content()) + helloRecordStart;
+
+ return NULL;
+}
+
bool
Ssl::Bio::sslFeatures::get(const MemBuf &buf, bool record)
{
return false;
}
- if (msgSize > buf.contentSize()) {
- debugs(83, 2, "Partial SSL handshake message, can not parse!");
- return false;
- }
-
if (record) {
helloMessage.clear();
helloMessage.append(buf.content(), buf.contentSize());
}
- const unsigned char *msg = (const unsigned char *)buf.content();
+ const unsigned char *msg = helloRecord(buf);
+ if (!msg) {
+ debugs(83, 2, "Partial SSL handshake message, can not parse!");
+ return false;
+ }
+
if (msg[0] & 0x80)
return parseV23Hello(msg, (size_t)msgSize);
else {
// RFC5246 section 7.4
if (msg[5] == 0x2) { // ServerHello message
if (parseV3ServerHello(msg, (size_t)msgSize)) {
- hasCcsOrNst = checkForCcsOrNst(msg + msgSize, buf.contentSize() - msgSize);
+ hasCcsOrNst = checkForCcsOrNst(buf);
return true;
}
} else if (msg[5] == 0x1) // ClientHello message,
int toSquidSSLVersion() const;
/// Configure the SSL object with the SSL features of the sslFeatures object
void applyToSSL(SSL *ssl, Ssl::BumpMode bumpMode) const;
- /// Parses an SSL Message header. It returns the ssl Message size.
+ /// Parses an SSL Message header. It returns the Hello ssl Message size.
/// \retval >0 if the hello size is retrieved
/// \retval 0 if the contents of the buffer are not enough
/// \retval <0 if the contents of buf are not SSLv3 or TLS hello message
int parseMsgHead(const MemBuf &);
- /// Parses msg buffer and return true if one of the Change Cipher Spec
+ /// Return a pointer to the SSL Record include the hello message
+ /// or NULL if this is not available
+ const unsigned char *helloRecord(const MemBuf &);
+ /// Parses buf buffer and return true if one of the Change Cipher Spec
/// or New Session Ticket messages found
- bool checkForCcsOrNst(const unsigned char *msg, size_t size);
+ bool checkForCcsOrNst(const MemBuf &buf);
public:
int sslVersion; ///< The requested/used SSL version
int compressMethod; ///< The requested/used compressed method
+ int helloRecordStart; ///< The SSL hello position in SSL
int helloMsgSize; ///< the hello message size
mutable SBuf serverName; ///< The SNI hostname, if any
std::string clientRequestedCiphers; ///< The client requested ciphers
public:
/// The ssl hello message read states
typedef enum {atHelloNone = 0, atHelloStarted, atHelloReceived} HelloReadState;
- explicit ClientBio(const int anFd): Bio(anFd), holdRead_(false), holdWrite_(false), helloState(atHelloNone), helloSize(0) {}
+ explicit ClientBio(const int anFd): Bio(anFd), holdRead_(false), holdWrite_(false), helloState(atHelloNone) {}
/// The ClientBio version of the Ssl::Bio::stateChanged method
/// When the client hello message retrieved, fill the
bool holdRead_; ///< The read hold state of the bio.
bool holdWrite_; ///< The write hold state of the bio.
HelloReadState helloState; ///< The SSL hello read state
- int helloSize; ///< The SSL hello message sent by client size
};
/// BIO node to handle socket IO for squid server side