]> git.ipfire.org Git - thirdparty/squid.git/commitdiff
Finalized BinaryTokenizer context handling. Polished.
authorAlex Rousskov <rousskov@measurement-factory.com>
Mon, 2 May 2016 16:38:05 +0000 (10:38 -0600)
committerAlex Rousskov <rousskov@measurement-factory.com>
Mon, 2 May 2016 16:38:05 +0000 (10:38 -0600)
No more funny context fields inside TLS structures. Context is handled
by the parsing code without needlessly storing it long-term.

Hid TLS structures/parsers used exclusively by
Security::HandshakeParser inside security/Handshake.cc to simplify API.

Also skipped unused ServerHello.random (instead of storing it in
TlsDetails::clientRandom) and replaced SQUID_TLS_RANDOM_SIZE macro
with a regular C++ constant.

src/parser/BinaryTokenizer.cc
src/parser/BinaryTokenizer.h
src/security/Handshake.cc
src/security/Handshake.h

index 53c9e6eaac11d68c67c67466fce020a4fa111feb..94495d6a67a9e7e5ee197a715c4299b28c4010cc 100644 (file)
@@ -172,3 +172,34 @@ BinaryTokenizer::skip(uint64_t size, const char *description)
     skipped(size, description);
 }
 
+/*
+ * BinaryTokenizer::pstringN() implementations below reduce debugging noise by
+ * not parsing empty areas and not summarizing parsing context.success().
+ */
+
+SBuf
+BinaryTokenizer::pstring8(const char *description)
+{
+    BinaryTokenizerContext pstring(*this, description);
+    if (const uint8_t length = uint8(".length"))
+        return area(length, ".octets");
+    return SBuf();
+}
+
+SBuf
+BinaryTokenizer::pstring16(const char *description)
+{
+    BinaryTokenizerContext pstring(*this, description);
+    if (const uint16_t length = uint16(".length"))
+        return area(length, ".octets");
+    return SBuf();
+}
+
+SBuf
+BinaryTokenizer::pstring24(const char *description)
+{
+    BinaryTokenizerContext pstring(*this, description);
+    if (const uint32_t length = uint24(".length"))
+        return area(length, ".octets");
+    return SBuf();
+}
index 939e5e139a8aa86d5afb3a7eca464aad17831ecf..18c9429bf0eaf08eb35baad038b36378f94ceeb8 100644 (file)
@@ -78,6 +78,14 @@ public:
     /// parse size consecutive bytes as an opaque blob
     SBuf area(uint64_t size, const char *description);
 
+    /*
+     * Variable-length arrays (a.k.a. Pascal or prefix strings).
+     * pstringN() extracts and returns N-bit length followed by length bytes
+     */
+    SBuf pstring8(const char *description); ///< up to 255 byte-long p-string
+    SBuf pstring16(const char *description); ///< up to 64 KiB-long p-string
+    SBuf pstring24(const char *description); ///< up to 16 MiB-long p-string!
+
     /// ignore the next size bytes
     void skip(uint64_t size, const char *description);
 
index 3ebd5cd6ed090c2ad8e39010d210ab9a012c2c30..97439a48d10b34f4440f9704f217e8bb7984be26 100644 (file)
+/*
+ * Copyright (C) 1996-2016 The Squid Software Foundation and contributors
+ *
+ * Squid software is distributed under GPLv2+ license and includes
+ * contributions from numerous individuals and organizations.
+ * Please see the COPYING and CONTRIBUTORS files for details.
+ */
+
+/* DEBUG: section 83    SSL-Bump Server/Peer negotiation */
+
 #include "squid.h"
-#include "parser/BinaryTokenizer.h"
 #include "security/Handshake.h"
 #if USE_OPENSSL
 #include "ssl/support.h"
 #endif
 
-Security::ProtocolVersion::ProtocolVersion(BinaryTokenizer &tk):
-    context(tk, ".version"),
-    vMajor(tk.uint8(".major")),
-    vMinor(tk.uint8(".minor"))
+namespace Security {
+
+// TODO: Replace with Anyp::ProtocolVersion and use for TlsDetails::tls*Version.
+/// TLS Record Layer's protocol version from RFC 5246 Section 6.2.1
+class ProtocolVersion
 {
-    context.success();
+public:
+    ProtocolVersion() {}
+    explicit ProtocolVersion(BinaryTokenizer &tk);
+
+    /// XXX: TlsDetails use "int" to manipulate version information.
+    /// TODO: Use ProtocolVersion in TlsDetails and printTlsVersion().
+    int toNumberXXX() const { return (vMajor << 8) | vMinor; }
+
+    // the "v" prefix works around environments that #define major and minor
+    uint8_t vMajor = 0;
+    uint8_t vMinor = 0;
+};
+
+/* 
+ * The types below represent various SSL and TLS protocol elements. Most names
+ * are based on RFC 5264 and RFC 6066 terminology. Objects of these explicit
+ * types are stored or passed around. Other protocol elements are simply parsed
+ * in-place, without declaring a corresponding explicit class.
+ */
+
+/// TLS Record Layer's content types from RFC 5246 Section 6.2.1
+enum ContentType {
+    ctChangeCipherSpec = 20,
+    ctAlert = 21,
+    ctHandshake = 22,
+    ctApplicationData = 23
+};
+
+/// TLS Record Layer's frame from RFC 5246 Section 6.2.1.
+class TLSPlaintext
+{
+public:
+    explicit TLSPlaintext(BinaryTokenizer &tk);
+
+    uint8_t type; ///< see ContentType
+    int version; ///< Record Layer, not necessarily the negotiated TLS version; TODO: Replace with Anyp::ProtocolVersion
+    SBuf fragment; ///< possibly partial content
+};
+
+/// draft-hickman-netscape-ssl-00. Section 4.1. SSL Record Header Format
+class Sslv2Record
+{
+public:
+    explicit Sslv2Record(BinaryTokenizer &tk);
+
+    SBuf fragment;
+};
+
+/// TLS Handshake protocol's handshake types from RFC 5246 Section 7.4
+enum HandshakeType {
+    hskClientHello = 1,
+    hskServerHello = 2,
+    hskCertificate = 11,
+    hskServerHelloDone = 14
+};
+
+/// TLS Handshake Protocol frame from RFC 5246 Section 7.4.
+class Handshake
+{
+public:
+    explicit Handshake(BinaryTokenizer &tk);
+
+    uint8_t msg_type; ///< see HandshakeType
+    SBuf msg_body; ///< Handshake Protocol message
+};
+
+/// TLS Alert protocol frame from RFC 5246 Section 7.2.
+class Alert
+{
+public:
+    explicit Alert(BinaryTokenizer &tk);
+
+    bool fatal() const { return level == 2; }
+
+    uint8_t level; ///< warning or fatal
+    uint8_t description; ///< close_notify, unexpected_message, etc.
+};
+
+/// The size of the TLS Random structure from RFC 5246 Section 7.4.1.2.
+static const uint64_t HelloRandomSize = 32;
+
+/// TLS Hello Extension from RFC 5246 Section 7.4.1.4.
+class Extension
+{
+public:
+    explicit Extension(BinaryTokenizer &tk);
+
+    uint16_t type;
+    SBuf data;
+};
+
+} // namespace Security
+
+/// Convenience helper: We parse ProtocolVersion but store "int".
+static int
+ParseProtocolVersion(BinaryTokenizer &tk)
+{
+    const Security::ProtocolVersion version(tk);
+    return version.toNumberXXX();
 }
 
-Security::TLSPlaintext::TLSPlaintext(BinaryTokenizer &tk):
-    context(tk, "TLSPlaintext"),
-    type(tk.uint8(".type")),
-    version(tk),
-    length(tk.uint16(".length"))
+
+Security::ProtocolVersion::ProtocolVersion(BinaryTokenizer &tk)
+{
+    BinaryTokenizerContext context(tk, ".version");
+    vMajor = tk.uint8(".major");
+    vMinor = tk.uint8(".minor");
+    // do not summarize context.success() to reduce debugging noise
+}
+
+Security::TLSPlaintext::TLSPlaintext(BinaryTokenizer &tk)
 {
-    Must(version.vMajor == 3 && version.vMinor <= 3);
+    BinaryTokenizerContext context(tk, "TLSPlaintext");
+    type = tk.uint8(".type");
     Must(type >= ctChangeCipherSpec && type <= ctApplicationData);
-    fragment = tk.area(length, ".fragment");
+    version = ParseProtocolVersion(tk);
+    // TODO: Must(version.major == 3);
+    fragment = tk.pstring16(".fragment");
     context.success();
 }
 
-Security::Handshake::Handshake(BinaryTokenizer &tk):
-    context(tk, "Handshake"),
-    msg_type(tk.uint8(".msg_type")),
-    length(tk.uint24(".length")),
-    body(tk.area(length, ".body"))
+Security::Handshake::Handshake(BinaryTokenizer &tk)
 {
+    BinaryTokenizerContext context(tk, "Handshake");
+    msg_type = tk.uint8(".msg_type");
+    msg_body = tk.pstring24(".msg_body");
     context.success();
 }
 
-Security::Alert::Alert(BinaryTokenizer &tk):
-    context(tk, "Alert"),
-    level(tk.uint8(".level")),
-    description(tk.uint8(".description"))
+Security::Alert::Alert(BinaryTokenizer &tk)
 {
+    BinaryTokenizerContext context(tk, "Alert");
+    level = tk.uint8(".level");
+    description = tk.uint8(".description");
     context.success();
 }
 
-Security::Extension::Extension(BinaryTokenizer &tk):
-    context(tk, "Extension"),
-    type(tk.uint16(".type")),
-    length(tk.uint16(".length")),
-    body(tk.area(length, ".body"))
+Security::Extension::Extension(BinaryTokenizer &tk)
 {
+    BinaryTokenizerContext context(tk, "Extension");
+    type = tk.uint16(".type");
+    data = tk.pstring16(".data");
     context.success();
 }
 
-Security::Sslv2Record::Sslv2Record(BinaryTokenizer &tk):
-    context(tk, "Sslv2Record"),
-    length(0)
+Security::Sslv2Record::Sslv2Record(BinaryTokenizer &tk)
 {
+    BinaryTokenizerContext context(tk, "Sslv2Record");
     const uint16_t head = tk.uint16(".head");
-    length = head & 0x7FFF;
+    const uint16_t length = head & 0x7FFF;
     Must((head & 0x8000) && length); // SSLv2 message [without padding]
     fragment = tk.area(length, ".fragment");
     context.success();
@@ -143,12 +256,12 @@ Security::HandshakeParser::parseModernRecord()
     const TLSPlaintext record(tkRecords);
     tkRecords.commit();
 
-    Must(record.length <= (1 << 14)); // RFC 5246: length MUST NOT exceed 2^14
+    details->tlsVersion = record.version;
 
+    // RFC 5246: length MUST NOT exceed 2^14
+    Must(record.fragment.length() <= (1 << 14));
     // RFC 5246: MUST NOT send zero-length [non-application] fragments
-    Must(record.length || record.type == ContentType::ctApplicationData);
-
-    details->tlsVersion = record.version.toNumberXXX();
+    Must(record.fragment.length() || record.type == ContentType::ctApplicationData);
 
     if (currentContentType != record.type) {
         Must(tkMessages.atEnd()); // no currentContentType leftovers
@@ -222,18 +335,18 @@ Security::HandshakeParser::parseHandshakeMessage()
     switch (message.msg_type) {
         case HandshakeType::hskClientHello:
             Must(state < atHelloReceived);
-            Security::HandshakeParser::parseClientHelloHandshakeMessage(message.body);
+            Security::HandshakeParser::parseClientHelloHandshakeMessage(message.msg_body);
             state = atHelloReceived;
             done = "ClientHello";
             return;
         case HandshakeType::hskServerHello:
             Must(state < atHelloReceived);
-            parseServerHelloHandshakeMessage(message.body);
+            parseServerHelloHandshakeMessage(message.msg_body);
             state = atHelloReceived;
             return;
         case HandshakeType::hskCertificate:
             Must(state < atCertificatesReceived);
-            parseServerCertificates(message.body);
+            parseServerCertificates(message.msg_body);
             state = atCertificatesReceived;
             return;
         case HandshakeType::hskServerHelloDone:
@@ -244,7 +357,7 @@ Security::HandshakeParser::parseHandshakeMessage()
             return;
     }
     debugs(83, 5, "ignoring " <<
-           DebugFrame("handshake msg", message.msg_type, message.length));
+           DebugFrame("handshake msg", message.msg_type, message.msg_body.length()));
 }
 
 void
@@ -260,7 +373,7 @@ Security::HandshakeParser::parseVersion2HandshakeMessage(const SBuf &raw)
     BinaryTokenizer tk(raw);
     BinaryTokenizerContext hello(tk, "V2ClientHello");
     Must(tk.uint8(".type") == hskClientHello); // Only client hello supported.
-    details->tlsSupportedVersion = parseProtocolVersion(tk);
+    details->tlsSupportedVersion = ParseProtocolVersion(tk);
     const uint16_t ciphersLen = tk.uint16(".cipher_specs.length");
     const uint16_t sessionIdLen = tk.uint16(".session_id.length");
     const uint16_t challengeLen = tk.uint16(".challenge.length");
@@ -275,13 +388,13 @@ Security::HandshakeParser::parseClientHelloHandshakeMessage(const SBuf &raw)
 {
     BinaryTokenizer tk(raw);
     BinaryTokenizerContext hello(tk, "ClientHello");
-    details->tlsSupportedVersion = parseProtocolVersion(tk);
-    details->clientRandom = tk.area(SQUID_TLS_RANDOM_SIZE, ".random");
-    details->sessionId = pstring8(tk, ".session_id");
-    parseCiphers(pstring16(tk, ".cipher_suites"));
-    details->compressMethod = pstring8(tk, ".compression_methods").length() > 0 ? 1 : 0; // Only deflate supported here.
+    details->tlsSupportedVersion = ParseProtocolVersion(tk);
+    details->clientRandom = tk.area(HelloRandomSize, ".random");
+    details->sessionId = tk.pstring8(".session_id");
+    parseCiphers(tk.pstring16(".cipher_suites"));
+    details->compressMethod = tk.pstring8(".compression_methods").length() > 0 ? 1 : 0; // Only deflate supported here.
     if (!tk.atEnd()) // extension-free message ends here
-        parseExtensions(pstring16(tk, ".extensions"));
+        parseExtensions(tk.pstring16(".extensions"));
     hello.success();
 }
 
@@ -295,7 +408,7 @@ Security::HandshakeParser::parseExtensions(const SBuf &raw)
 
         switch(extension.type) {
         case 0: // The SNI extension; RFC 6066, Section 3
-            details->serverName = parseSniExtension(extension.body);
+            details->serverName = parseSniExtension(extension.data);
             break;
         case 5: // Certificate Status Request; RFC 6066, Section 8
             details->tlsStatusRequest = true;
@@ -304,14 +417,13 @@ Security::HandshakeParser::parseExtensions(const SBuf &raw)
             details->doHeartBeats = true;
             break;
         case 16: { // Application-Layer Protocol Negotiation Extension, RFC 7301
-            BinaryTokenizer tkAPN(extension.body);
-            details->tlsAppLayerProtoNeg = pstring16(tkAPN, "APN extension");
+            BinaryTokenizer tkAPN(extension.data);
+            details->tlsAppLayerProtoNeg = tkAPN.pstring16("APN");
             break;
         }
         case 35: // SessionTicket TLS Extension; RFC 5077
             details->tlsTicketsExtension = true;
-            if (extension.length)
-                details->hasTlsTicket = true;
+            details->hasTlsTicket = !extension.data.isEmpty();
         case 13172: // Next Protocol Negotiation Extension (expired draft?)
         default:
             break;
@@ -350,15 +462,15 @@ void
 Security::HandshakeParser::parseServerHelloHandshakeMessage(const SBuf &raw)
 {
     BinaryTokenizer tk(raw);
-    BinaryTokenizerContext serverHello(tk, "ServerHello");
-    details->tlsSupportedVersion = parseProtocolVersion(tk);
-    details->clientRandom = tk.area(SQUID_TLS_RANDOM_SIZE, ".random");
-    details->sessionId = pstring8(tk, ".session_id");
+    BinaryTokenizerContext hello(tk, "ServerHello");
+    details->tlsSupportedVersion = ParseProtocolVersion(tk);
+    tk.skip(HelloRandomSize, ".random");
+    details->sessionId = tk.pstring8(".session_id");
     details->ciphers.push_back(tk.uint16(".cipher_suite"));
     details->compressMethod = tk.uint8(".compression_method") != 0; // not null
     if (!tk.atEnd()) // extensions present
-        parseExtensions(pstring16(tk, ".extensions"));
-    serverHello.success();
+        parseExtensions(tk.pstring16(".extensions"));
+    hello.success();
 }
 
 // RFC 6066 Section 3: ServerNameList (may be sent by both clients and servers)
@@ -372,11 +484,11 @@ Security::HandshakeParser::parseSniExtension(const SBuf &extensionData) const
     // SNI MUST NOT contain more than one name of the same name_type but
     // we ignore violations and simply return the first host name found.
     BinaryTokenizer tkList(extensionData);
-    BinaryTokenizer tkNames(pstring16(tkList, "ServerNameList"));
+    BinaryTokenizer tkNames(tkList.pstring16("ServerNameList"));
     while (!tkNames.atEnd()) {
         BinaryTokenizerContext serverName(tkNames, "ServerName");
         const uint8_t nameType = tkNames.uint8(".name_type");
-        const SBuf name = pstring16(tkNames, ".name");
+        const SBuf name = tkNames.pstring16(".name");
         serverName.success();
 
         if (nameType == 0) {
@@ -423,44 +535,6 @@ Security::HandshakeParser::parseHello(const SBuf &data)
     return false; // unreached
 }
 
-SBuf
-Security::HandshakeParser::pstring8(BinaryTokenizer &tk, const char *description) const
-{
-    BinaryTokenizerContext pstring(tk, description);
-    const uint8_t length = tk.uint8(".length");
-    const SBuf body = tk.area(length, ".body");
-    pstring.success();
-    return body;
-}
-
-SBuf
-Security::HandshakeParser::pstring16(BinaryTokenizer &tk, const char *description) const
-{
-    BinaryTokenizerContext pstring(tk, description);
-    const uint16_t length = tk.uint16(".length");
-    const SBuf body = tk.area(length, ".body");
-    pstring.success();
-    return body;
-}
-
-SBuf
-Security::HandshakeParser::pstring24(BinaryTokenizer &tk, const char *description) const
-{
-    BinaryTokenizerContext pstring(tk, description);
-    const uint32_t length = tk.uint24(".length");
-    const SBuf body = tk.area(length, ".body");
-    pstring.success();
-    return body;
-}
-
-/// Convenience helper: We parse ProtocolVersion but store "int".
-int
-Security::HandshakeParser::parseProtocolVersion(BinaryTokenizer &tk) const
-{
-    const ProtocolVersion version(tk);
-    return version.toNumberXXX();
-}
-
 #if USE_OPENSSL
 X509 *
 Security::HandshakeParser::ParseCertificate(const SBuf &raw)
@@ -478,12 +552,12 @@ void
 Security::HandshakeParser::parseServerCertificates(const SBuf &raw)
 {
     BinaryTokenizer tkList(raw);
-    const SBuf clist = pstring24(tkList, "CertificateList");
+    const SBuf clist = tkList.pstring24("CertificateList");
     Must(tkList.atEnd()); // no leftovers after all certificates
 
     BinaryTokenizer tkItems(clist);
     while (!tkItems.atEnd()) {
-        X509 *cert = ParseCertificate(pstring24(tkItems, "Certificate"));
+        X509 *cert = ParseCertificate(tkItems.pstring24("Certificate"));
         if (!serverCertificates.get())
             serverCertificates.reset(sk_X509_new_null());
         sk_X509_push(serverCertificates.get(), cert);
index aa8ab0b0e58471981eded370e848d14a79f00c57..26bd40f0c72012c22e6bf787b03c7441e8595412 100644 (file)
@@ -10,7 +10,6 @@
 #define SQUID_SECURITY_HANDSHAKE_H
 
 #include "base/RefCount.h"
-#include "fd.h"
 #include "parser/BinaryTokenizer.h"
 #include "sbuf/SBuf.h"
 #if USE_OPENSSL
 namespace Security
 {
 
-// The Transport Layer Security (TLS) Protocol, Version 1.2
-
-/// TLS Record Layer's content types from RFC 5246 Section 6.2.1
-enum ContentType {
-    ctChangeCipherSpec = 20,
-    ctAlert = 21,
-    ctHandshake = 22,
-    ctApplicationData = 23
-};
-
-/// TLS Record Layer's protocol version from RFC 5246 Section 6.2.1
-struct ProtocolVersion
-{
-    explicit ProtocolVersion(BinaryTokenizer &tk);
-
-    /// XXX: TlsDetails use "int" to manipulate version information.
-    /// TODO: Use ProtocolVersion in TlsDetails and printTlsVersion().
-    int toNumberXXX() const { return (vMajor << 8) | vMinor; }
-
-    BinaryTokenizerContext context; ///< parsing context for debugging
-
-    // the "v" prefix works around environments that #define major and minor
-    uint8_t vMajor;
-    uint8_t vMinor;
-};
-
-/// TLS Record Layer's frame from RFC 5246 Section 6.2.1.
-struct TLSPlaintext
-{
-    explicit TLSPlaintext(BinaryTokenizer &tk);
-
-    BinaryTokenizerContext context; ///< parsing context for debugging
-
-    uint8_t type; ///< Rfc5246::ContentType
-    ProtocolVersion version;
-    uint16_t length;
-    SBuf fragment; ///< exactly length bytes
-};
-
-/// draft-hickman-netscape-ssl-00. Section 4.1. SSL Record Header Format
-struct Sslv2Record
-{
-    explicit Sslv2Record(BinaryTokenizer &tk);
-
-    BinaryTokenizerContext context; ///< parsing context for debugging
-
-    uint16_t length;
-    SBuf fragment;
-};
-
-/// TLS Handshake protocol's handshake types from RFC 5246 Section 7.4
-enum HandshakeType {
-    hskClientHello = 1,
-    hskServerHello = 2,
-    hskCertificate = 11,
-    hskServerHelloDone = 14
-};
-
-/// TLS Handshake Protocol frame from RFC 5246 Section 7.4.
-struct Handshake
-{
-    explicit Handshake(BinaryTokenizer &tk);
-
-    BinaryTokenizerContext context; ///< parsing context for debugging
-
-    uint32_t msg_type: 8; ///< HandshakeType
-    uint32_t length: 24;
-    SBuf body; ///< Handshake Protocol message, exactly length bytes
-};
-
-/// TLS Alert protocol frame from RFC 5246 Section 7.2.
-struct Alert
-{
-    explicit Alert(BinaryTokenizer &tk);
-
-
-    bool fatal() const { return level == 2; }
-
-    BinaryTokenizerContext context; ///< parsing context for debugging
-
-    uint8_t level; ///< warning or fatal
-    uint8_t description; ///< close_notify, unexpected_message, etc.
-};
-
-/// TLS Hello Extension from RFC 5246 Section 7.4.1.4.
-struct Extension
-{
-    explicit Extension(BinaryTokenizer &tk);
-
-    BinaryTokenizerContext context; ///< parsing context for debugging
-
-    uint16_t type;
-    uint16_t length; // XXX just use SBuf!
-    SBuf body;
-};
-
-#define SQUID_TLS_RANDOM_SIZE 32
-
 class TlsDetails: public RefCountable
 {
 public:
@@ -204,16 +105,6 @@ private:
     static X509 *ParseCertificate(const SBuf &raw);
 #endif
 
-    /* 
-     * RFC 5246 Section 4.3: Variable-length vectors (a.k.a. prefix strings).
-     * vectorN() returns raw post-length "contents" of vector<0..2^N-1>
-     */
-    SBuf pstring8(BinaryTokenizer &tk, const char *description) const;
-    SBuf pstring16(BinaryTokenizer &tk, const char *description) const;
-    SBuf pstring24(BinaryTokenizer &tk, const char *description) const;
-
-    int parseProtocolVersion(BinaryTokenizer &tk) const;
-
     unsigned int currentContentType; ///< The current SSL record content type
 
     const char *done; ///< not nil iff we got what we were looking for