ftp/libftp.la \
helper/libhelper.la \
http/libhttp.la \
- parser/libparser.la \
dns/libdns.la \
base/libbase.la \
libsquid.la \
anyp/libanyp.la \
comm/libcomm.la \
security/libsecurity.la \
+ parser/libparser.la \
eui/libeui.la \
icmp/libicmp.la \
log/liblog.la \
--- /dev/null
+/*
+ * 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);
+}
+
--- /dev/null
+/*
+ * 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
noinst_LTLIBRARIES = libparser.la
libparser_la_SOURCES = \
+ BinaryTokenizer.h \
+ BinaryTokenizer.cc \
Tokenizer.h \
Tokenizer.cc
Context.h \
EncryptorAnswer.cc \
EncryptorAnswer.h \
+ Handshake.cc \
+ Handshake.h \
forward.h \
KeyData.h \
LockingPointer.h \
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));
}
#include "fde.h"
#include "globals.h"
#include "ip/Address.h"
+#include "parser/BinaryTokenizer.h"
#include "ssl/bio.h"
#if HAVE_OPENSSL_SSL_H
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)
{
" 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 */
#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
{
/// 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
///< 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