]> git.ipfire.org Git - thirdparty/squid.git/commitdiff
Handshake Error: ccs received early part2
authorChristos Tsantilas <chtsanti@users.sourceforge.net>
Wed, 4 May 2016 08:13:12 +0000 (11:13 +0300)
committerChristos Tsantilas <chtsanti@users.sourceforge.net>
Wed, 4 May 2016 08:13:12 +0000 (11:13 +0300)
Squid currently does not handle SSL server responses that start with
an SSL Alert Record. Squid fails to parse the Server Hello message and
also fails to detect and handle resuming sessions.

This is a Measurement Factory project.

src/ssl/bio.cc
src/ssl/bio.h

index bda00e00f8a55fb618e03956b09b61d447321a5c..512db7922b59af165174d32a3df964f6980a8510 100644 (file)
@@ -229,7 +229,7 @@ Ssl::ClientBio::read(char *buf, int size, BIO *table)
     }
 
     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);
@@ -246,7 +246,7 @@ Ssl::ClientBio::read(char *buf, int size, BIO *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;
         }
@@ -640,7 +640,7 @@ squid_ssl_info(const SSL *ssl, int where, int ret)
     }
 }
 
-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);
 }
@@ -765,39 +765,68 @@ Ssl::Bio::sslFeatures::parseMsgHead(const MemBuf &buf)
     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];
@@ -828,6 +857,18 @@ Ssl::Bio::sslFeatures::checkForCcsOrNst(const unsigned char *msg, size_t size)
     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)
 {
@@ -837,17 +878,17 @@ 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 {
@@ -860,7 +901,7 @@ Ssl::Bio::sslFeatures::get(const MemBuf &buf, bool record)
         // 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,
index b9b2b8478a81c6f59103f3600aff314841ff9529..f67f9a52c232adccfa8bf196514483f66921df24 100644 (file)
@@ -52,17 +52,21 @@ public:
         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
@@ -130,7 +134,7 @@ class ClientBio: public Bio
 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
@@ -157,7 +161,6 @@ private:
     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