#include "ssl/support.h"
#endif
-Security::FieldGroup::FieldGroup(BinaryTokenizer &tk, const char *description) {
- tk.context = description;
-}
-
-void
-Security::FieldGroup::commit(BinaryTokenizer &tk) {
- tk.commit();
- tk.context = "";
-}
-
Security::ProtocolVersion::ProtocolVersion(BinaryTokenizer &tk):
- vMajor(tk.uint8(".vMajor")),
- vMinor(tk.uint8(".vMinor"))
+ context(tk, ".version"),
+ vMajor(tk.uint8(".major")),
+ vMinor(tk.uint8(".minor"))
{
+ context.success();
}
Security::TLSPlaintext::TLSPlaintext(BinaryTokenizer &tk):
- FieldGroup(tk, "TLSPlaintext"),
+ context(tk, "TLSPlaintext"),
type(tk.uint8(".type")),
version(tk),
length(tk.uint16(".length"))
Must(version.vMajor == 3 && version.vMinor <= 3);
Must(type >= ctChangeCipherSpec && type <= ctApplicationData);
fragment = tk.area(length, ".fragment");
- commit(tk);
+ context.success();
}
Security::Handshake::Handshake(BinaryTokenizer &tk):
- FieldGroup(tk, "Handshake"),
+ context(tk, "Handshake"),
msg_type(tk.uint8(".msg_type")),
length(tk.uint24(".length")),
body(tk.area(length, ".body"))
{
- commit(tk);
+ context.success();
}
Security::Alert::Alert(BinaryTokenizer &tk):
- FieldGroup(tk, "Alert"),
+ context(tk, "Alert"),
level(tk.uint8(".level")),
description(tk.uint8(".description"))
{
- commit(tk);
+ context.success();
}
Security::Extension::Extension(BinaryTokenizer &tk):
- FieldGroup(tk, "Extension"),
+ context(tk, "Extension"),
type(tk.uint16(".type")),
length(tk.uint16(".length")),
body(tk.area(length, ".body"))
{
- commit(tk);
+ context.success();
}
Security::Sslv2Record::Sslv2Record(BinaryTokenizer &tk):
- FieldGroup(tk, "Sslv2Record")
+ context(tk, "Sslv2Record"),
+ length(0)
{
- const uint16_t head = tk.uint16(".head(Record+Length)");
+ const uint16_t head = tk.uint16(".head");
length = head & 0x7FFF;
Must((head & 0x8000) && length); // SSLv2 message [without padding]
- version = 0x02;
- // The remained message has length of length-sizeof(type)=(length-1);
fragment = tk.area(length, ".fragment");
- commit(tk);
+ context.success();
}
Security::TlsDetails::TlsDetails():
Security::HandshakeParser::parseVersion2Record()
{
const Sslv2Record record(tkRecords);
+ tkRecords.commit();
Must(details);
- details->tlsVersion = record.version;
+ details->tlsVersion = 0x002;
parseVersion2HandshakeMessage(record.fragment);
state = atHelloReceived;
- done = "SSL v2 Hello";
+ done = "SSLv2";
}
/// RFC 5246. Appendix E.2. Compatibility with SSL 2.0
-/// And draft-hickman-netscape-ssl-00. Section 4.1 SSL Record Header Format
+/// And draft-hickman-netscape-ssl-00. Section 4.1. SSL Record Header Format
bool
Security::HandshakeParser::isSslv2Record(const SBuf &raw) const
{
BinaryTokenizer tk(raw, true);
- const uint16_t head = tk.uint16("V2Hello.msg_length+");
- const uint8_t type = tk.uint8("V2Hello.msg_type");
+ const uint16_t head = tk.uint16("?v2Hello.msg_head");
+ const uint8_t type = tk.uint8("?v2Hello.msg_type");
const uint16_t length = head & 0x7FFF;
return (head & 0x8000) && length && type == 1;
}
void
Security::HandshakeParser::parseRecord()
{
+ Must(details);
if (expectingModernRecords)
parseModernRecord();
else
Security::HandshakeParser::parseModernRecord()
{
const TLSPlaintext record(tkRecords);
+ tkRecords.commit();
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 == ContentType::ctApplicationData);
- details->tlsVersion = ((record.version.vMajor & 0xFF) << 8) | (record.version.vMinor & 0xFF);
+ details->tlsVersion = record.version.toNumberXXX();
if (currentContentType != record.type) {
Must(tkMessages.atEnd()); // no currentContentType leftovers
void
Security::HandshakeParser::parseMessages()
{
- debugs(83, 7, DebugFrame("fragments", currentContentType, fragments.length()));
- while (!tkMessages.atEnd()) {
+ for (; !tkMessages.atEnd(); tkMessages.commit()) {
switch (currentContentType) {
case ContentType::ctChangeCipherSpec:
parseChangeCipherCpecMessage();
{
Must(currentContentType == ContentType::ctAlert);
const Alert alert(tkMessages);
- debugs(83, 3, "level " << alert.level << " description " << alert.description);
+ debugs(83, (alert.fatal() ? 2:3),
+ "level " << static_cast<int>(alert.level) <<
+ " description " << static_cast<int>(alert.description));
// we are currently ignoring Alert Protocol messages
}
void
Security::HandshakeParser::parseVersion2HandshakeMessage(const SBuf &raw)
{
- BinaryTokenizer tkHsk(raw);
- Must(details);
-
- uint8_t type = tkHsk.uint8("type");
- Must(type == hskClientHello); // Only client hello supported.
-
- details->tlsSupportedVersion = tkHsk.uint16("tlsSupportedVersion");
- uint16_t ciphersLen = tkHsk.uint16(".cipherSpecLength");
- uint16_t sessionIdLen = tkHsk.uint16(".sessionIdLength");
- tkHsk.skip(sizeof(uint16_t), ".challengeLength");
-
- SBuf ciphers = tkHsk.area(ciphersLen, "Ciphers list");
- parseV23Ciphers(ciphers);
- details->sessionId = tkHsk.area(sessionIdLen, "Session Id");
-
- // tkHsk.skip(challengeLen, "Challenge");
+ BinaryTokenizer tk(raw);
+ BinaryTokenizerContext hello(tk, "V2ClientHello");
+ Must(tk.uint8(".type") == hskClientHello); // Only client hello supported.
+ 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");
+ parseV23Ciphers(tk.area(ciphersLen, ".cipher_specs.body"));
+ details->sessionId = tk.area(sessionIdLen, ".session_id.body");
+ tk.skip(challengeLen, ".challenge.body");
+ hello.success();
}
void
Security::HandshakeParser::parseClientHelloHandshakeMessage(const SBuf &raw)
{
- BinaryTokenizer tkHsk(raw);
- Must(details);
- details->tlsSupportedVersion = tkHsk.uint16("tlsSupportedVersion");
- details->clientRandom = tkHsk.area(SQUID_TLS_RANDOM_SIZE, "Client Random");
- details->sessionId = pstring8(tkHsk, "Session ID");
- parseCiphers(pstring16(tkHsk, "Ciphers list"));
- details->compressMethod = pstring8(tkHsk, "Compression methods").length() > 0 ? 1 : 0; // Only deflate supported here.
- if (!tkHsk.atEnd()) // extension-free message ends here
- parseExtensions(pstring16(tkHsk, "Extensions List"));
+ 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.
+ if (!tk.atEnd()) // extension-free message ends here
+ parseExtensions(pstring16(tk, ".extensions"));
+ hello.success();
}
void
Security::HandshakeParser::parseExtensions(const SBuf &raw)
{
- Must(details);
BinaryTokenizer tk(raw);
while (!tk.atEnd()) {
Extension extension(tk);
void
Security::HandshakeParser::parseCiphers(const SBuf &raw)
{
- Must(details);
BinaryTokenizer tk(raw);
while (!tk.atEnd()) {
const uint16_t cipher = tk.uint16("cipher");
void
Security::HandshakeParser::parseV23Ciphers(const SBuf &raw)
{
- Must(details);
BinaryTokenizer tk(raw);
while (!tk.atEnd()) {
// The v2 hello messages cipher has 3 bytes.
// The v2 cipher has the first byte not null.
// We support v3 messages only so we are ignoring v2 ciphers.
+ // XXX: The above line sounds wrong -- we support v2 hello messages.
const uint8_t prefix = tk.uint8("prefix");
const uint16_t cipher = tk.uint16("cipher");
- if (prefix == 0)
+ if (prefix == 0) // TODO: return immediately if prefix is positive?
details->ciphers.push_back(cipher);
}
}
+/// RFC 5246 Section 7.4.1.3. Server Hello
void
Security::HandshakeParser::parseServerHelloHandshakeMessage(const SBuf &raw)
{
- BinaryTokenizer tkHsk(raw);
- Must(details);
- details->tlsSupportedVersion = tkHsk.uint16("tlsSupportedVersion");
- details->clientRandom = tkHsk.area(SQUID_TLS_RANDOM_SIZE, "Client Random");
- details->sessionId = pstring8(tkHsk, "Session ID");
- const uint16_t cipher = tkHsk.uint16("cipher");
- details->ciphers.push_back(cipher);
- const uint8_t compressionMethod = tkHsk.uint8("Compression method");
- details->compressMethod = compressionMethod > 0 ? 1 : 0; // Only deflate supported here.
- if (!tkHsk.atEnd()) // extensions present
- parseExtensions(pstring16(tkHsk, "Extensions List"));
+ 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");
+ 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();
}
// RFC 6066 Section 3: ServerNameList (may be sent by both clients and servers)
BinaryTokenizer tkList(extensionData);
BinaryTokenizer tkNames(pstring16(tkList, "ServerNameList"));
while (!tkNames.atEnd()) {
- const uint8_t nameType = tkNames.uint8("ServerName.name_type");
- const SBuf name = pstring16(tkNames, "ServerName.name");
+ BinaryTokenizerContext serverName(tkNames, "ServerName");
+ const uint8_t nameType = tkNames.uint8(".name_type");
+ const SBuf name = pstring16(tkNames, ".name");
+ serverName.success();
+
if (nameType == 0) {
debugs(83, 3, "host_name=" << name);
return name; // it may be empty
// else we just parsed a new/unsupported NameType which,
// according to RFC 6066, MUST begin with a 16-bit length field
}
- return SBuf(); // SNI present but contains no names
+ return SBuf(); // SNI extension lacks host_name
}
void
// 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();
}
bool
SBuf
Security::HandshakeParser::pstring8(BinaryTokenizer &tk, const char *description) const
{
- tk.context = description;
+ BinaryTokenizerContext pstring(tk, description);
const uint8_t length = tk.uint8(".length");
const SBuf body = tk.area(length, ".body");
- tk.commit();
- tk.context = "";
+ pstring.success();
return body;
}
SBuf
Security::HandshakeParser::pstring16(BinaryTokenizer &tk, const char *description) const
{
- tk.context = description;
+ BinaryTokenizerContext pstring(tk, description);
const uint16_t length = tk.uint16(".length");
const SBuf body = tk.area(length, ".body");
- tk.commit();
- tk.context = "";
+ pstring.success();
return body;
}
SBuf
Security::HandshakeParser::pstring24(BinaryTokenizer &tk, const char *description) const
{
- tk.context = description;
+ BinaryTokenizerContext pstring(tk, description);
const uint32_t length = tk.uint24(".length");
const SBuf body = tk.area(length, ".body");
- tk.commit();
- tk.context = "";
+ 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)
// The Transport Layer Security (TLS) Protocol, Version 1.2
-/// 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,
{
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: public FieldGroup
+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
};
-struct Sslv2Record: public FieldGroup
+/// draft-hickman-netscape-ssl-00. Section 4.1. SSL Record Header Format
+struct Sslv2Record
{
explicit Sslv2Record(BinaryTokenizer &tk);
- uint16_t version;
+
+ BinaryTokenizerContext context; ///< parsing context for debugging
+
uint16_t length;
SBuf fragment;
};
};
/// TLS Handshake Protocol frame from RFC 5246 Section 7.4.
-struct Handshake: public FieldGroup
+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: public FieldGroup
+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.
};
-struct Extension: public FieldGroup
+/// 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;
+ uint16_t length; // XXX just use SBuf!
SBuf body;
};
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