]> git.ipfire.org Git - thirdparty/squid.git/commitdiff
Move BinaryTokenizer, Ssl::HandshakeParser and Ssl::Rfc5246::* classes to
authorChristos Tsantilas <chtsanti@users.sourceforge.net>
Sat, 19 Mar 2016 18:24:53 +0000 (20:24 +0200)
committerChristos Tsantilas <chtsanti@users.sourceforge.net>
Sat, 19 Mar 2016 18:24:53 +0000 (20:24 +0200)
their own *.cc,*.h files

-Move the BinaryTokenizer from ssl/bio.* files to parser/BinaryTokenizer.{cc,h}
-Move the Ssl::Handshake and related structures and declarations from ssl/bio.*
 to security/Handshake.{c,h}, and under the   Security namespace
 (Security::Handshake)
-Move the Ssl::Rfc5246::* classes from ssl/bio.* to security/Handshake.{c,h},
 under the Security namespace.

src/Makefile.am
src/parser/BinaryTokenizer.cc [new file with mode: 0644]
src/parser/BinaryTokenizer.h [new file with mode: 0644]
src/parser/Makefile.am
src/security/Makefile.am
src/ssl/PeerConnector.cc
src/ssl/bio.cc
src/ssl/bio.h

index 448a68e8151c73c61b4dfd36d3a93340a0ffad4f..c51b6e9aa900ba9c0291ebaef682699117152d7e 100644 (file)
@@ -541,7 +541,6 @@ squid_LDADD = \
        ftp/libftp.la \
        helper/libhelper.la \
        http/libhttp.la \
-       parser/libparser.la \
        dns/libdns.la \
        base/libbase.la \
        libsquid.la \
@@ -554,6 +553,7 @@ squid_LDADD = \
        anyp/libanyp.la \
        comm/libcomm.la \
        security/libsecurity.la \
+       parser/libparser.la \
        eui/libeui.la \
        icmp/libicmp.la \
        log/liblog.la \
diff --git a/src/parser/BinaryTokenizer.cc b/src/parser/BinaryTokenizer.cc
new file mode 100644 (file)
index 0000000..8ee0816
--- /dev/null
@@ -0,0 +1,157 @@
+/*
+ * Copyright (C) 1996-2015 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 24    SBuf */
+
+#include "squid.h"
+#include "BinaryTokenizer.h"
+
+BinaryTokenizer::BinaryTokenizer(): BinaryTokenizer(SBuf())
+{
+}
+
+BinaryTokenizer::BinaryTokenizer(const SBuf &data):
+    context(""),
+    data_(data),
+    parsed_(0),
+    syncPoint_(0)
+{
+}
+
+/// debugging helper that prints a "standard" debugs() trailer
+#define BinaryTokenizer_tail(size, start) \
+    " occupying " << (size) << " bytes @" << (start) << " in " << this;
+
+/// logs and throws if fewer than size octets remain; no other side effects
+void
+BinaryTokenizer::want(uint64_t size, const char *description) const
+{
+    if (parsed_ + size > data_.length()) {
+        debugs(24, 5, (parsed_ + size - data_.length()) << " more bytes for " <<
+               context << description << BinaryTokenizer_tail(size, parsed_));
+        throw InsufficientInput();
+    }
+}
+
+/// debugging helper for parsed number fields
+void
+BinaryTokenizer::got(uint32_t value, uint64_t size, const char *description) const
+{
+    debugs(24, 7, context << description << '=' << value <<
+           BinaryTokenizer_tail(size, parsed_ - size));
+}
+
+/// debugging helper for parsed areas/blobs
+void
+BinaryTokenizer::got(const SBuf &value, uint64_t size, const char *description) const
+{
+    debugs(24, 7, context << description << '=' <<
+           Raw(nullptr, value.rawContent(), value.length()).hex() <<
+           BinaryTokenizer_tail(size, parsed_ - size));
+
+}
+
+/// debugging helper for skipped fields
+void
+BinaryTokenizer::skipped(uint64_t size, const char *description) const
+{
+    debugs(24, 7, context << description << BinaryTokenizer_tail(size, parsed_ - size));
+
+}
+
+/// Returns the next ready-for-shift byte, adjusting the number of parsed bytes.
+/// The larger 32-bit return type helps callers shift/merge octets into numbers.
+/// This internal method does not perform out-of-bounds checks.
+uint32_t
+BinaryTokenizer::octet()
+{
+    // While char may be signed, we view data characters as unsigned,
+    // which helps to arrive at the right 32-bit return value.
+    return static_cast<uint8_t>(data_[parsed_++]);
+}
+
+void
+BinaryTokenizer::reset(const SBuf &data)
+{
+    *this = BinaryTokenizer(data);
+}
+
+void
+BinaryTokenizer::rollback()
+{
+    parsed_ = syncPoint_;
+}
+
+void
+BinaryTokenizer::commit()
+{
+    if (context && *context)
+        debugs(24, 6, context << BinaryTokenizer_tail(parsed_ - syncPoint_, syncPoint_));
+    syncPoint_ = parsed_;
+}
+
+bool
+BinaryTokenizer::atEnd() const
+{
+    return parsed_ >= data_.length();
+}
+
+uint8_t
+BinaryTokenizer::uint8(const char *description)
+{
+    want(1, description);
+    const uint8_t result = octet();
+    got(result, 1, description);
+    return result;
+}
+
+uint16_t
+BinaryTokenizer::uint16(const char *description)
+{
+    want(2, description);
+    const uint16_t result = (octet() << 8) | octet();
+    got(result, 2, description);
+    return result;
+}
+
+uint32_t
+BinaryTokenizer::uint24(const char *description)
+{
+    want(3, description);
+    const uint32_t result = (octet() << 16) | (octet() << 8) | octet();
+    got(result, 3, description);
+    return result;
+}
+
+uint32_t
+BinaryTokenizer::uint32(const char *description)
+{
+    want(4, description);
+    const uint32_t result = (octet() << 24) | (octet() << 16) | (octet() << 8) | octet();
+    got(result, 4, description);
+    return result;
+}
+
+SBuf
+BinaryTokenizer::area(uint64_t size, const char *description)
+{
+    want(size, description);
+    const SBuf result = data_.substr(parsed_, size);
+    parsed_ += size;
+    got(result, size, description);
+    return result;
+}
+
+void
+BinaryTokenizer::skip(uint64_t size, const char *description)
+{
+    want(size, description);
+    parsed_ += size;
+    skipped(size, description);
+}
+
diff --git a/src/parser/BinaryTokenizer.h b/src/parser/BinaryTokenizer.h
new file mode 100644 (file)
index 0000000..08af246
--- /dev/null
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 1996-2015 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.
+ */
+
+#ifndef SQUID_BINARY_TOKENIZER_H
+#define SQUID_BINARY_TOKENIZER_H
+
+#include "sbuf/SBuf.h"
+
+/// Safely extracts byte-oriented (i.e., non-textual) fields from raw input.
+/// Supports commit points for atomic incremental parsing of multi-part fields.
+/// Throws InsufficientInput when more input is needed to parse the next field.
+/// Throws on errors.
+class BinaryTokenizer
+{
+public:
+    class InsufficientInput {}; // thrown when a method runs out of data
+    typedef uint64_t size_type; // enough for the largest supported offset
+
+    BinaryTokenizer();
+    explicit BinaryTokenizer(const SBuf &data);
+
+    /// restart parsing from the very beginning
+    /// this method is for using one BinaryTokenizer to parse independent inputs
+    void reset(const SBuf &data);
+
+    /// change input without changing parsing state
+    /// this method avoids append overheads during incremental parsing
+    void reinput(const SBuf &data) { data_ = data; }
+
+    /// make progress: future parsing failures will not rollback beyond this point
+    void commit();
+
+    /// resume [incremental] parsing from the last commit point
+    void rollback();
+
+    /// no more bytes to parse or skip
+    bool atEnd() const;
+
+    /// parse a single-byte unsigned integer
+    uint8_t uint8(const char *description);
+
+    // parse a two-byte unsigned integer
+    uint16_t uint16(const char *description);
+
+    // parse a three-byte unsigned integer (returned as uint32_t)
+    uint32_t uint24(const char *description);
+
+    // parse a four-byte unsigned integer
+    uint32_t uint32(const char *description);
+
+    /// parse size consecutive bytes as an opaque blob
+    SBuf area(uint64_t size, const char *description);
+
+    /// ignore the next size bytes
+    void skip(uint64_t size, const char *description);
+
+    /// yet unparsed bytes
+    SBuf leftovers() const { return data_.substr(parsed_); }
+
+    const char *context; ///< simplifies debugging
+
+protected:
+    uint32_t octet();
+    void want(uint64_t size, const char *description) const;
+    void got(uint32_t value, uint64_t size, const char *description) const;
+    void got(const SBuf &value, uint64_t size, const char *description) const;
+    void skipped(uint64_t size, const char *description) const;
+
+private:
+    SBuf data_;
+    uint64_t parsed_; ///< number of data bytes parsed or skipped
+    uint64_t syncPoint_; ///< where to re-start the next parsing attempt
+};
+
+#endif // SQUID_BINARY_TOKENIZER_H
index 869f71be1fe05bc33549e68b5c32beb9c137aac8..bcf43322939f663db7197705204c9043fe095f38 100644 (file)
@@ -11,6 +11,8 @@ include $(top_srcdir)/src/TestHeaders.am
 noinst_LTLIBRARIES = libparser.la
 
 libparser_la_SOURCES = \
+       BinaryTokenizer.h \
+       BinaryTokenizer.cc \
        Tokenizer.h \
        Tokenizer.cc
 
index 7e8dc50d4ceddf2a9d2e672d7617db609af30231..debbb0740fa7950662ca5f1a7f2d49691eb00185 100644 (file)
@@ -16,6 +16,8 @@ libsecurity_la_SOURCES= \
        Context.h \
        EncryptorAnswer.cc \
        EncryptorAnswer.h \
+       Handshake.cc \
+       Handshake.h \
        forward.h \
        KeyData.h \
        LockingPointer.h \
index 5d633f921ee4eb05d4ed8b701e46a0e0b279e945..a77fbfe21e0c355e96e3437ba73cc782d0e24c83 100644 (file)
@@ -543,7 +543,7 @@ Ssl::PeerConnector::certDownloadingDone(SBuf &obj, int downloadStatus)
     if (X509 *cert = d2i_X509(NULL, &raw, obj.length())) {
         char buffer[1024];
         debugs(81, 5, "Retrieved certificate: " << X509_NAME_oneline(X509_get_subject_name(cert), buffer, 1024));
-        const Ssl::X509_STACK_Pointer &certsList = srvBio->serverCertificates();
+        const Ssl::X509_STACK_Pointer &certsList = srvBio->serverCertificatesIfAny();
         if (const char *issuerUri = Ssl::uriOfIssuerIfMissing(cert,  certsList)) {
             urlsOfMissingCerts.push(SBuf(issuerUri));
         }
index 91d5afd5dae0f54e0a5e6529cf0460a96f075d34..9c2b22ad795b415ae815b5b631bd7a0cb53ad992 100644 (file)
@@ -19,6 +19,7 @@
 #include "fde.h"
 #include "globals.h"
 #include "ip/Address.h"
+#include "parser/BinaryTokenizer.h"
 #include "ssl/bio.h"
 
 #if HAVE_OPENSSL_SSL_H
@@ -56,229 +57,8 @@ static BIO_METHOD SquidMethods = {
     NULL // squid_callback_ctrl not supported
 };
 
-
-/* BinaryTokenizer */
-
-BinaryTokenizer::BinaryTokenizer(): BinaryTokenizer(SBuf())
-{
-}
-
-BinaryTokenizer::BinaryTokenizer(const SBuf &data):
-    context(""),
-    data_(data),
-    parsed_(0),
-    syncPoint_(0)
-{
-}
-
-/// debugging helper that prints a "standard" debugs() trailer
-#define BinaryTokenizer_tail(size, start) \
-    " occupying " << (size) << " bytes @" << (start) << " in " << this;
-
-/// logs and throws if fewer than size octets remain; no other side effects
-void
-BinaryTokenizer::want(uint64_t size, const char *description) const
-{
-    if (parsed_ + size > data_.length()) {
-        debugs(83, 5, (parsed_ + size - data_.length()) << " more bytes for " <<
-               context << description << BinaryTokenizer_tail(size, parsed_));
-        throw InsufficientInput();
-    }
-}
-
-/// debugging helper for parsed number fields
-void
-BinaryTokenizer::got(uint32_t value, uint64_t size, const char *description) const
-{
-    debugs(83, 7, context << description << '=' << value <<
-           BinaryTokenizer_tail(size, parsed_ - size));
-}
-
-/// debugging helper for parsed areas/blobs
-void
-BinaryTokenizer::got(const SBuf &value, uint64_t size, const char *description) const
-{
-    debugs(83, 7, context << description << '=' <<
-           Raw(nullptr, value.rawContent(), value.length()).hex() <<
-           BinaryTokenizer_tail(size, parsed_ - size));
-
-}
-
-/// debugging helper for skipped fields
-void
-BinaryTokenizer::skipped(uint64_t size, const char *description) const
-{
-    debugs(83, 7, context << description << BinaryTokenizer_tail(size, parsed_ - size));
-
-}
-
-/// Returns the next ready-for-shift byte, adjusting the number of parsed bytes.
-/// The larger 32-bit return type helps callers shift/merge octets into numbers.
-/// This internal method does not perform out-of-bounds checks.
-uint32_t
-BinaryTokenizer::octet()
-{
-    // While char may be signed, we view data characters as unsigned,
-    // which helps to arrive at the right 32-bit return value.
-    return static_cast<uint8_t>(data_[parsed_++]);
-}
-
-void
-BinaryTokenizer::reset(const SBuf &data)
-{
-    *this = BinaryTokenizer(data);
-}
-
-void
-BinaryTokenizer::rollback()
-{
-    parsed_ = syncPoint_;
-}
-
-void
-BinaryTokenizer::commit()
-{
-    if (context && *context)
-        debugs(83, 6, context << BinaryTokenizer_tail(parsed_ - syncPoint_, syncPoint_));
-    syncPoint_ = parsed_;
-}
-
-bool
-BinaryTokenizer::atEnd() const
-{
-    return parsed_ >= data_.length();
-}
-
-uint8_t
-BinaryTokenizer::uint8(const char *description)
-{
-    want(1, description);
-    const uint8_t result = octet();
-    got(result, 1, description);
-    return result;
-}
-
-uint16_t
-BinaryTokenizer::uint16(const char *description)
-{
-    want(2, description);
-    const uint16_t result = (octet() << 8) | octet();
-    got(result, 2, description);
-    return result;
-}
-
-uint32_t
-BinaryTokenizer::uint24(const char *description)
-{
-    want(3, description);
-    const uint32_t result = (octet() << 16) | (octet() << 8) | octet();
-    got(result, 3, description);
-    return result;
-}
-
-uint32_t
-BinaryTokenizer::uint32(const char *description)
-{
-    want(4, description);
-    const uint32_t result = (octet() << 24) | (octet() << 16) | (octet() << 8) | octet();
-    got(result, 4, description);
-    return result;
-}
-
-SBuf
-BinaryTokenizer::area(uint64_t size, const char *description)
-{
-    want(size, description);
-    const SBuf result = data_.substr(parsed_, size);
-    parsed_ += size;
-    got(result, size, description);
-    return result;
-}
-
-void
-BinaryTokenizer::skip(uint64_t size, const char *description)
-{
-    want(size, description);
-    parsed_ += size;
-    skipped(size, description);
-}
-
-
-/* Ssl::Rfc5246 */
-
-Ssl::Rfc5246::FieldGroup::FieldGroup(BinaryTokenizer &tk, const char *description) {
-    tk.context = description;
-}
-
-void
-Ssl::Rfc5246::FieldGroup::commit(BinaryTokenizer &tk) {
-    tk.commit();
-    tk.context = "";
-}
-
-
-Ssl::Rfc5246::ProtocolVersion::ProtocolVersion(BinaryTokenizer &tk):
-    vMajor(tk.uint8(".vMajor")),
-    vMinor(tk.uint8(".vMinor"))
-{
-}
-
-Ssl::Rfc5246::TLSPlaintext::TLSPlaintext(BinaryTokenizer &tk):
-    FieldGroup(tk, "TLSPlaintext"),
-    type(tk.uint8(".type")),
-    version(tk),
-    length(tk.uint16(".length")),
-    fragment(tk.area(length, ".fragment"))
-{
-    commit(tk);
-}
-
-Ssl::Rfc5246::Handshake::Handshake(BinaryTokenizer &tk):
-    FieldGroup(tk, "Handshake"),
-    msg_type(tk.uint8(".msg_type")),
-    length(tk.uint24(".length")),
-    body(tk.area(length, ".body"))
-{
-    commit(tk);
-}
-
-Ssl::Rfc5246::Alert::Alert(BinaryTokenizer &tk):
-    FieldGroup(tk, "Alert"),
-    level(tk.uint8(".level")),
-    description(tk.uint8(".description"))
-{
-    commit(tk);
-}
-
-Ssl::Rfc5246::P24String::P24String(BinaryTokenizer &tk, const char *description):
-    FieldGroup(tk, description),
-    length(tk.uint24(".length")),
-    body(tk.area(length, ".body"))
-{
-    commit(tk);
-}
-
-
 /* Ssl:Bio */
 
-/// debugging helper to print various parsed records and messages
-class DebugFrame
-{
-public:
-    DebugFrame(const char *aName, uint64_t aType, uint64_t aSize):
-        name(aName), type(aType), size(aSize) {}
-
-    const char *name;
-    uint64_t type;
-    uint64_t size;
-};
-
-inline std::ostream &
-operator <<(std::ostream &os, const DebugFrame &frame)
-{
-    return os << frame.size << "-byte type-" << frame.type << ' ' << frame.name;
-}
-
 BIO *
 Ssl::Bio::Create(const int fd, Ssl::Bio::Type type)
 {
@@ -1356,176 +1136,5 @@ Ssl::Bio::sslFeatures::print(std::ostream &os) const
            " Random:" << Raw(nullptr, (char *)client_random, SSL3_RANDOM_SIZE).hex();
 }
 
-/// parses a single TLS Record Layer frame
-void
-Ssl::HandshakeParser::parseRecord()
-{
-    const Rfc5246::TLSPlaintext record(tkRecords);
-
-    Must(record.length <= (1 << 14)); // RFC 5246: length MUST NOT exceed 2^14
-
-    // RFC 5246: MUST NOT send zero-length [non-application] fragments
-    Must(record.length || record.type == Rfc5246::ContentType::ctApplicationData);
-
-    if (currentContentType != record.type) {
-        Must(tkMessages.atEnd()); // no currentContentType leftovers
-        fragments = record.fragment;
-        tkMessages.reset(fragments);
-        currentContentType = record.type;
-    } else {
-        fragments.append(record.fragment);
-        tkMessages.reinput(fragments);
-        tkMessages.rollback();
-    }
-    parseMessages();
-}
-
-/// parses one or more "higher-level protocol" frames of currentContentType
-void
-Ssl::HandshakeParser::parseMessages()
-{
-    debugs(83, 7, DebugFrame("fragments", currentContentType, fragments.length()));
-    while (!tkMessages.atEnd()) {
-        switch (currentContentType) {
-        case Rfc5246::ContentType::ctChangeCipherSpec:
-            parseChangeCipherCpecMessage();
-            continue;
-        case Rfc5246::ContentType::ctAlert:
-            parseAlertMessage();
-            continue;
-        case Rfc5246::ContentType::ctHandshake:
-            parseHandshakeMessage();
-            continue;
-        case Rfc5246::ContentType::ctApplicationData:
-            parseApplicationDataMessage();
-            continue;
-        }
-        skipMessage("unknown ContentType msg");
-    }
-}
-
-void
-Ssl::HandshakeParser::parseChangeCipherCpecMessage()
-{
-    Must(currentContentType == Rfc5246::ContentType::ctChangeCipherSpec);
-    // we are currently ignoring Change Cipher Spec Protocol messages
-    // Everything after this message may be is encrypted
-    // The continuing parsing is pointless, abort here and set parseDone
-    skipMessage("ChangeCipherCpec msg");
-    ressumingSession = true;
-    parseDone = true;
-}
-
-void
-Ssl::HandshakeParser::parseAlertMessage()
-{
-    Must(currentContentType == Rfc5246::ContentType::ctAlert);
-    const Rfc5246::Alert alert(tkMessages);
-    debugs(83, 3, "level " << alert.level << " description " << alert.description);
-    // we are currently ignoring Alert Protocol messages
-}
-
-void
-Ssl::HandshakeParser::parseHandshakeMessage()
-{
-    Must(currentContentType == Rfc5246::ContentType::ctHandshake);
-
-    const Rfc5246::Handshake message(tkMessages);
-
-    switch (message.msg_type) {
-        case Rfc5246::HandshakeType::hskServerHello:
-            Must(state < atHelloReceived);
-            // TODO: Parse ServerHello in message.body; extract version/session
-            // If the server is resuming a session, stop parsing w/o certificates
-            // because all subsequent [Finished] messages will be encrypted, right?
-            state = atHelloReceived;
-            return;
-        case Rfc5246::HandshakeType::hskCertificate:
-            Must(state < atCertificatesReceived);
-            parseServerCertificates(message.body);
-            state = atCertificatesReceived;
-            return;
-        case Rfc5246::HandshakeType::hskServerHelloDone:
-            Must(state < atHelloDoneReceived);
-            // zero-length
-            state = atHelloDoneReceived;
-            parseDone = true;
-            return;
-    }
-    debugs(83, 5, "ignoring " <<
-           DebugFrame("handshake msg", message.msg_type, message.length));
-}
-
-void
-Ssl::HandshakeParser::parseApplicationDataMessage()
-{
-    Must(currentContentType == Rfc5246::ContentType::ctApplicationData);
-    skipMessage("app data");
-}
-
-void
-Ssl::HandshakeParser::skipMessage(const char *description)
-{
-    // tkMessages/fragments can only contain messages of the same ContentType.
-    // To skip a message, we can and should skip everything we have [left]. If
-    // we have partial messages, debugging will mislead about their boundaries.
-    tkMessages.skip(tkMessages.leftovers().length(), description);
-    tkMessages.commit();
-}
-
-/// parseServerHelloTry() wrapper that maintains parseDone/parseError state
-bool
-Ssl::HandshakeParser::parseServerHello(const SBuf &data)
-{
-    try {
-        tkRecords.reinput(data); // data contains _everything_ read so far
-        tkRecords.rollback();
-        while (!tkRecords.atEnd() && !parseDone)
-            parseRecord();
-        debugs(83, 7, "success; done: " << parseDone);
-        return parseDone;
-    }
-    catch (const BinaryTokenizer::InsufficientInput &) {
-        debugs(83, 5, "need more data");
-        Must(!parseError);
-    }
-    catch (const std::exception &ex) {
-        debugs(83, 2, "parsing error: " << ex.what());
-        parseError = true;
-    }
-    return false;
-}
-
-X509 *
-Ssl::HandshakeParser::ParseCertificate(const SBuf &raw)
-{
-    typedef const unsigned char *x509Data;
-    const x509Data x509Start = reinterpret_cast<x509Data>(raw.rawContent());
-    x509Data x509Pos = x509Start;
-    X509 *x509 = d2i_X509(nullptr, &x509Pos, raw.length());
-    Must(x509); // successfully parsed
-    Must(x509Pos == x509Start + raw.length()); // no leftovers
-    return x509;
-}
-
-void
-Ssl::HandshakeParser::parseServerCertificates(const SBuf &raw)
-{
-    BinaryTokenizer tkList(raw);
-    const Rfc5246::P24String list(tkList, "CertificateList");
-    Must(tkList.atEnd()); // no leftovers after all certificates
-
-    BinaryTokenizer tkItems(list.body);
-    while (!tkItems.atEnd()) {
-        const Rfc5246::P24String item(tkItems, "Certificate");
-        X509 *cert = ParseCertificate(item.body);
-        if (!serverCertificates.get())
-            serverCertificates.reset(sk_X509_new_null());
-        sk_X509_push(serverCertificates.get(), cert);
-        debugs(83, 7, "parsed " << sk_X509_num(serverCertificates.get()) << " certificates so far");
-    }
-
-}
-
 #endif /* USE_SSL */
 
index 0268874a2e569727d606f6a5f50ef907f13485dd..1399f0908da492a22647e14209caf34076e7cf57 100644 (file)
@@ -11,6 +11,7 @@
 
 #include "fd.h"
 #include "sbuf/SBuf.h"
+#include "security/Handshake.h"
 
 #include <iosfwd>
 #include <list>
 #include <string>
 #include <type_traits>
 
-// TODO: Move BinaryTokenizer to its own set of files outside of Ssl namespace.
-
-/// Safely extracts byte-oriented (i.e., non-textual) fields from raw input.
-/// Supports commit points for atomic incremental parsing of multi-part fields.
-/// Throws InsufficientInput when more input is needed to parse the next field.
-/// Throws on errors.
-class BinaryTokenizer
-{
-public:
-    class InsufficientInput {}; // thrown when a method runs out of data
-    typedef uint64_t size_type; // enough for the largest supported offset
-
-    BinaryTokenizer();
-    explicit BinaryTokenizer(const SBuf &data);
-
-    /// restart parsing from the very beginning
-    /// this method is for using one BinaryTokenizer to parse independent inputs
-    void reset(const SBuf &data);
-
-    /// change input without changing parsing state
-    /// this method avoids append overheads during incremental parsing
-    void reinput(const SBuf &data) { data_ = data; }
-
-    /// make progress: future parsing failures will not rollback beyond this point
-    void commit();
-
-    /// resume [incremental] parsing from the last commit point
-    void rollback();
-
-    /// no more bytes to parse or skip
-    bool atEnd() const;
-
-    /// parse a single-byte unsigned integer
-    uint8_t uint8(const char *description);
-
-    // parse a two-byte unsigned integer
-    uint16_t uint16(const char *description);
-
-    // parse a three-byte unsigned integer (returned as uint32_t)
-    uint32_t uint24(const char *description);
-
-    // parse a four-byte unsigned integer
-    uint32_t uint32(const char *description);
-
-    /// parse size consecutive bytes as an opaque blob
-    SBuf area(uint64_t size, const char *description);
-
-    /// ignore the next size bytes
-    void skip(uint64_t size, const char *description);
-
-    /// yet unparsed bytes
-    SBuf leftovers() const { return data_.substr(parsed_); }
-
-    const char *context; ///< simplifies debugging
-
-protected:
-    uint32_t octet();
-    void want(uint64_t size, const char *description) const;
-    void got(uint32_t value, uint64_t size, const char *description) const;
-    void got(const SBuf &value, uint64_t size, const char *description) const;
-    void skipped(uint64_t size, const char *description) const;
-
-private:
-    SBuf data_;
-    uint64_t parsed_; ///< number of data bytes parsed or skipped
-    uint64_t syncPoint_; ///< where to re-start the next parsing attempt
-};
-
-
 namespace Ssl
 {
 
-// The Transport Layer Security (TLS) Protocol, Version 1.2
-
-// TODO: Consider removing this namespace. The idea was to encapsulate various
-// RFC 5246 types defined using the naming scheme from the RFC rather than
-// following Squid naming conventions. However, using these names in other code
-// may make that code inconsistent. Besides, we are running into some C++ naming
-// limits.
-namespace Rfc5246
-{
-
-/// Helper class to debug parsing of various TLS structures
-class FieldGroup
-{
-public:
-    FieldGroup(BinaryTokenizer &tk, const char *description); ///< starts parsing
-
-    void commit(BinaryTokenizer &tk); ///< commits successful parsing results
-};
-
-/// 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);
-
-    // 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: public FieldGroup
-{
-    explicit TLSPlaintext(BinaryTokenizer &tk);
-
-    uint8_t type; ///< Rfc5246::ContentType
-    ProtocolVersion version;
-    uint16_t length;
-    SBuf fragment; ///< exactly length bytes
-};
-
-/// TLS Handshake protocol's handshake types from RFC 5246 Section 7.4
-enum HandshakeType {
-    hskServerHello = 2,
-    hskCertificate = 11,
-    hskServerHelloDone = 14
-};
-
-/// TLS Handshake Protocol frame from RFC 5246 Section 7.4.
-struct Handshake: public FieldGroup
-{
-    explicit Handshake(BinaryTokenizer &tk);
-
-    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: public FieldGroup
-{
-    explicit Alert(BinaryTokenizer &tk);
-    uint8_t level; ///< warning or fatal
-    uint8_t description; ///< close_notify, unexpected_message, etc.
-};
-
-/// Like a Pascal "length-first" string but with a 3-byte length field.
-/// Used for (undocumented in RRC 5246?) Certificate and ASN1.Cert encodings.
-struct P24String: public FieldGroup
-{
-    explicit P24String(BinaryTokenizer &tk, const char *description);
-
-    uint32_t length;  // bytes in body (stored using 3 bytes, not 4!)
-    SBuf body; ///< exactly length bytes
-};
-
-} // namespace Rfc5246
-
-
-/// Incremental SSL Handshake parser.
-class HandshakeParser {
-public:
-    /// The parsing states
-    typedef enum {atHelloNone = 0, atHelloStarted, atHelloReceived, atCertificatesReceived, atHelloDoneReceived, atNstReceived, atCcsReceived, atFinishReceived} ParserState;
-
-    HandshakeParser(): state(atHelloNone), ressumingSession(false), parseDone(false), parseError(false), currentContentType(0), unParsedContent(0), parsingPos(0), currentMsg(0), currentMsgSize(0), certificatesMsgPos(0), certificatesMsgSize(0) {}
-
-    /// Parses the initial sequence of raw bytes sent by the SSL server.
-    /// Returns true upon successful completion (HelloDone or Finished received).
-    /// Otherwise, returns false (and sets parseError to true on errors).
-    bool parseServerHello(const SBuf &data);
-
-    Ssl::X509_STACK_Pointer serverCertificates; ///< parsed certificates chain
-
-    ParserState state; ///< current parsing state.
-
-    bool ressumingSession; ///< True if this is a resumming session
-
-    bool parseDone; ///< The parser finishes its job
-    bool parseError; ///< Set to tru by parse on parse error.
-
-private:
-    unsigned int currentContentType; ///< The current SSL record content type
-    size_t unParsedContent; ///< The size of current SSL record, which is not parsed yet
-    size_t parsingPos; ///< The parsing position from the beginning of parsed data
-    size_t currentMsg; ///< The current handshake message possition from the beginning of parsed data
-    size_t currentMsgSize; ///< The current handshake message size.
-
-    size_t certificatesMsgPos; ///< The possition of certificates message from the beggining of parsed data
-    size_t certificatesMsgSize; ///< The size of certificates message
-
-private:
-    void parseServerHelloTry();
-
-    void parseRecord();
-    void parseMessages();
-
-    void parseChangeCipherCpecMessage();
-    void parseAlertMessage();
-    void parseHandshakeMessage();
-    void parseApplicationDataMessage();
-    void skipMessage(const char *msgType);
-
-    void parseServerCertificates(const SBuf &raw);
-    static X509 *ParseCertificate(const SBuf &raw);
-
-    /// concatenated TLSPlaintext.fragments of TLSPlaintext.type
-    SBuf fragments;
-
-    BinaryTokenizer tkRecords; // TLS record layer (parsing uninterpreted data)
-    BinaryTokenizer tkMessages; // TLS message layer (parsing fragments)
-};
-
 /// BIO source and sink node, handling socket I/O and monitoring SSL state
 class Bio
 {
@@ -431,7 +223,7 @@ public:
     /// Return true if the Server Hello parsing failed
     bool gotHelloFailed() const { return (parser_.parseDone && parser_.parseError); }
 
-    const Ssl::X509_STACK_Pointer &serverCertificates() { return parser_.serverCertificates; } /* XXX: may be nil */
+    const Ssl::X509_STACK_Pointer &serverCertificatesIfAny() { return parser_.serverCertificates; } /* XXX: may be nil */
 
 private:
     sslFeatures clientFeatures; ///< SSL client features extracted from ClientHello message or SSL object
@@ -447,7 +239,7 @@ private:
 
     ///< The size of data stored in rbuf which passed to the openSSL
     size_t rbufConsumePos;
-    HandshakeParser parser_; ///< The SSL messages parser.
+    Security::HandshakeParser parser_; ///< The SSL messages parser.
 };
 
 inline