mirror of https://github.com/lukechilds/node.git
Browse Source
Share ClientHelloParser code between `tls_wrap.cc` and `node_crypto.cc`. fix #5959v0.11.5-release
Fedor Indutny
12 years ago
8 changed files with 536 additions and 414 deletions
@ -0,0 +1,74 @@ |
|||||
|
// Copyright Joyent, Inc. and other Node contributors.
|
||||
|
//
|
||||
|
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
|
// copy of this software and associated documentation files (the
|
||||
|
// "Software"), to deal in the Software without restriction, including
|
||||
|
// without limitation the rights to use, copy, modify, merge, publish,
|
||||
|
// distribute, sublicense, and/or sell copies of the Software, and to permit
|
||||
|
// persons to whom the Software is furnished to do so, subject to the
|
||||
|
// following conditions:
|
||||
|
//
|
||||
|
// The above copyright notice and this permission notice shall be included
|
||||
|
// in all copies or substantial portions of the Software.
|
||||
|
//
|
||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
|
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
|
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
|
||||
|
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||
|
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
|
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
||||
|
// USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
|
||||
|
#ifndef SRC_NODE_CRYPTO_CLIENTHELLO_INL_H_ |
||||
|
#define SRC_NODE_CRYPTO_CLIENTHELLO_INL_H_ |
||||
|
|
||||
|
#include <assert.h> |
||||
|
|
||||
|
namespace node { |
||||
|
|
||||
|
inline void ClientHelloParser::Reset() { |
||||
|
frame_len_ = 0; |
||||
|
body_offset_ = 0; |
||||
|
extension_offset_ = 0; |
||||
|
session_size_ = 0; |
||||
|
session_id_ = NULL; |
||||
|
tls_ticket_size_ = -1; |
||||
|
tls_ticket_ = NULL; |
||||
|
} |
||||
|
|
||||
|
inline void ClientHelloParser::Start(ClientHelloParser::OnHelloCb onhello_cb, |
||||
|
ClientHelloParser::OnEndCb onend_cb, |
||||
|
void* onend_arg) { |
||||
|
if (!IsEnded()) |
||||
|
return; |
||||
|
Reset(); |
||||
|
|
||||
|
assert(onhello_cb != NULL); |
||||
|
|
||||
|
state_ = kWaiting; |
||||
|
onhello_cb_ = onhello_cb; |
||||
|
onend_cb_ = onend_cb; |
||||
|
cb_arg_ = onend_arg; |
||||
|
} |
||||
|
|
||||
|
inline void ClientHelloParser::End() { |
||||
|
if (state_ == kEnded) |
||||
|
return; |
||||
|
state_ = kEnded; |
||||
|
if (onend_cb_ != NULL) { |
||||
|
onend_cb_(cb_arg_); |
||||
|
onend_cb_ = NULL; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
inline bool ClientHelloParser::IsEnded() const { |
||||
|
return state_ == kEnded; |
||||
|
} |
||||
|
|
||||
|
inline bool ClientHelloParser::IsPaused() const { |
||||
|
return state_ == kPaused; |
||||
|
} |
||||
|
|
||||
|
} // namespace node
|
||||
|
|
||||
|
#endif // SRC_NODE_CRYPTO_CLIENTHELLO_INL_H_
|
@ -0,0 +1,240 @@ |
|||||
|
// Copyright Joyent, Inc. and other Node contributors.
|
||||
|
//
|
||||
|
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
|
// copy of this software and associated documentation files (the
|
||||
|
// "Software"), to deal in the Software without restriction, including
|
||||
|
// without limitation the rights to use, copy, modify, merge, publish,
|
||||
|
// distribute, sublicense, and/or sell copies of the Software, and to permit
|
||||
|
// persons to whom the Software is furnished to do so, subject to the
|
||||
|
// following conditions:
|
||||
|
//
|
||||
|
// The above copyright notice and this permission notice shall be included
|
||||
|
// in all copies or substantial portions of the Software.
|
||||
|
//
|
||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
|
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
|
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
|
||||
|
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||
|
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
|
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
||||
|
// USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
|
||||
|
#include "node_crypto_clienthello.h" |
||||
|
#include "node_crypto_clienthello-inl.h" |
||||
|
#include "node_buffer.h" // Buffer |
||||
|
|
||||
|
namespace node { |
||||
|
|
||||
|
void ClientHelloParser::Parse(const uint8_t* data, size_t avail) { |
||||
|
switch (state_) { |
||||
|
case kWaiting: |
||||
|
if (!ParseRecordHeader(data, avail)) |
||||
|
break; |
||||
|
// Fall through
|
||||
|
case kTLSHeader: |
||||
|
case kSSL2Header: |
||||
|
ParseHeader(data, avail); |
||||
|
break; |
||||
|
case kPaused: |
||||
|
// Just nop
|
||||
|
case kEnded: |
||||
|
// Already ended, just ignore it
|
||||
|
break; |
||||
|
default: |
||||
|
break; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
|
||||
|
bool ClientHelloParser::ParseRecordHeader(const uint8_t* data, size_t avail) { |
||||
|
// >= 5 bytes for header parsing
|
||||
|
if (avail < 5) |
||||
|
return false; |
||||
|
|
||||
|
if (data[0] == kChangeCipherSpec || |
||||
|
data[0] == kAlert || |
||||
|
data[0] == kHandshake || |
||||
|
data[0] == kApplicationData) { |
||||
|
frame_len_ = (data[3] << 8) + data[4]; |
||||
|
state_ = kTLSHeader; |
||||
|
body_offset_ = 5; |
||||
|
} else { |
||||
|
#ifdef OPENSSL_NO_SSL2 |
||||
|
frame_len_ = ((data[0] << 8) & kSSL2HeaderMask) + data[1]; |
||||
|
state_ = kSSL2Header; |
||||
|
if (data[0] & kSSL2TwoByteHeaderBit) { |
||||
|
// header without padding
|
||||
|
body_offset_ = 2; |
||||
|
} else { |
||||
|
// header with padding
|
||||
|
body_offset_ = 3; |
||||
|
} |
||||
|
#else |
||||
|
End(); |
||||
|
return false; |
||||
|
#endif // OPENSSL_NO_SSL2
|
||||
|
} |
||||
|
|
||||
|
// Sanity check (too big frame, or too small)
|
||||
|
// Let OpenSSL handle it
|
||||
|
if (frame_len_ >= kMaxTLSFrameLen) { |
||||
|
End(); |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
void ClientHelloParser::ParseHeader(const uint8_t* data, size_t avail) { |
||||
|
// >= 5 + frame size bytes for frame parsing
|
||||
|
if (body_offset_ + frame_len_ > avail) |
||||
|
return; |
||||
|
|
||||
|
// Skip unsupported frames and gather some data from frame
|
||||
|
|
||||
|
// TODO(indutny): Check hello protocol version
|
||||
|
if (data[body_offset_] == kClientHello) { |
||||
|
if (state_ == kTLSHeader) { |
||||
|
if (!ParseTLSClientHello(data, avail)) |
||||
|
return End(); |
||||
|
} else if (state_ == kSSL2Header) { |
||||
|
#ifdef OPENSSL_NO_SSL2 |
||||
|
if (!ParseSSL2ClientHello(data, avail)) |
||||
|
return End(); |
||||
|
#else |
||||
|
abort(); // Unreachable
|
||||
|
#endif // OPENSSL_NO_SSL2
|
||||
|
} else { |
||||
|
// We couldn't get here, but whatever
|
||||
|
return End(); |
||||
|
} |
||||
|
|
||||
|
// Check if we overflowed (do not reply with any private data)
|
||||
|
if (session_id_ == NULL || |
||||
|
session_size_ > 32 || |
||||
|
session_id_ + session_size_ > data + avail) { |
||||
|
return End(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
state_ = kPaused; |
||||
|
ClientHello hello; |
||||
|
hello.session_id_ = session_id_; |
||||
|
hello.session_size_ = session_size_; |
||||
|
hello.has_ticket_ = tls_ticket_ != NULL && tls_ticket_size_ != 0; |
||||
|
onhello_cb_(cb_arg_, hello); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
void ClientHelloParser::ParseExtension(ClientHelloParser::ExtensionType type, |
||||
|
const uint8_t* data, |
||||
|
size_t len) { |
||||
|
// NOTE: In case of anything we're just returning back, ignoring the problem.
|
||||
|
// That's because we're heavily relying on OpenSSL to solve any problem with
|
||||
|
// incoming data.
|
||||
|
switch (type) { |
||||
|
case kTLSSessionTicket: |
||||
|
tls_ticket_size_ = len; |
||||
|
tls_ticket_ = data + len; |
||||
|
break; |
||||
|
default: |
||||
|
// Ignore
|
||||
|
break; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
|
||||
|
bool ClientHelloParser::ParseTLSClientHello(const uint8_t* data, size_t avail) { |
||||
|
const uint8_t* body; |
||||
|
|
||||
|
// Skip frame header, hello header, protocol version and random data
|
||||
|
size_t session_offset = body_offset_ + 4 + 2 + 32; |
||||
|
|
||||
|
if (session_offset + 1 >= avail) |
||||
|
return false; |
||||
|
|
||||
|
body = data + session_offset; |
||||
|
session_size_ = *body; |
||||
|
session_id_ = body + 1; |
||||
|
|
||||
|
size_t cipher_offset = session_offset + 1 + session_size_; |
||||
|
|
||||
|
// Session OOB failure
|
||||
|
if (cipher_offset + 1 >= avail) |
||||
|
return false; |
||||
|
|
||||
|
uint16_t cipher_len = |
||||
|
(data[cipher_offset] << 8) + data[cipher_offset + 1]; |
||||
|
size_t comp_offset = cipher_offset + 2 + cipher_len; |
||||
|
|
||||
|
// Cipher OOB failure
|
||||
|
if (comp_offset >= avail) |
||||
|
return false; |
||||
|
|
||||
|
uint8_t comp_len = data[comp_offset]; |
||||
|
size_t extension_offset = comp_offset + 1 + comp_len; |
||||
|
|
||||
|
// Compression OOB failure
|
||||
|
if (extension_offset > avail) |
||||
|
return false; |
||||
|
|
||||
|
// No extensions present
|
||||
|
if (extension_offset == avail) |
||||
|
return true; |
||||
|
|
||||
|
size_t ext_off = extension_offset + 2; |
||||
|
|
||||
|
// Parse known extensions
|
||||
|
while (ext_off < avail) { |
||||
|
// Extension OOB
|
||||
|
if (ext_off + 4 > avail) |
||||
|
return false; |
||||
|
|
||||
|
uint16_t ext_type = (data[ext_off] << 8) + data[ext_off + 1]; |
||||
|
uint16_t ext_len = (data[ext_off + 2] << 8) + data[ext_off + 3]; |
||||
|
ext_off += 4; |
||||
|
|
||||
|
// Extension OOB
|
||||
|
if (ext_off + ext_len > avail) |
||||
|
return false; |
||||
|
|
||||
|
ParseExtension(static_cast<ExtensionType>(ext_type), |
||||
|
data + ext_off, |
||||
|
ext_len); |
||||
|
|
||||
|
ext_off += ext_len; |
||||
|
} |
||||
|
|
||||
|
// Extensions OOB failure
|
||||
|
if (ext_off > avail) |
||||
|
return false; |
||||
|
|
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
#ifdef OPENSSL_NO_SSL2 |
||||
|
bool ClientHelloParser::ParseSSL2ClientHello(const uint8_t* data, |
||||
|
size_t avail) { |
||||
|
const uint8_t* body; |
||||
|
|
||||
|
// Skip header, version
|
||||
|
size_t session_offset = body_offset_ + 3; |
||||
|
|
||||
|
if (session_offset + 4 < avail) { |
||||
|
body = data + session_offset; |
||||
|
|
||||
|
uint16_t ciphers_size = (body[0] << 8) + body[1]; |
||||
|
|
||||
|
if (body + 4 + ciphers_size < data + avail) { |
||||
|
session_size_ = (body[2] << 8) + body[3]; |
||||
|
session_id_ = body + 4 + ciphers_size; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return true; |
||||
|
} |
||||
|
#endif // OPENSSL_NO_SSL2
|
||||
|
|
||||
|
} // namespace node
|
@ -0,0 +1,124 @@ |
|||||
|
// Copyright Joyent, Inc. and other Node contributors.
|
||||
|
//
|
||||
|
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
|
// copy of this software and associated documentation files (the
|
||||
|
// "Software"), to deal in the Software without restriction, including
|
||||
|
// without limitation the rights to use, copy, modify, merge, publish,
|
||||
|
// distribute, sublicense, and/or sell copies of the Software, and to permit
|
||||
|
// persons to whom the Software is furnished to do so, subject to the
|
||||
|
// following conditions:
|
||||
|
//
|
||||
|
// The above copyright notice and this permission notice shall be included
|
||||
|
// in all copies or substantial portions of the Software.
|
||||
|
//
|
||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
|
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
|
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
|
||||
|
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||
|
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
|
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
||||
|
// USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
|
||||
|
#ifndef SRC_NODE_CRYPTO_CLIENTHELLO_H_ |
||||
|
#define SRC_NODE_CRYPTO_CLIENTHELLO_H_ |
||||
|
|
||||
|
#include "node.h" |
||||
|
|
||||
|
#include <stddef.h> // size_t |
||||
|
#include <stdlib.h> // NULL |
||||
|
|
||||
|
namespace node { |
||||
|
|
||||
|
class ClientHelloParser { |
||||
|
public: |
||||
|
ClientHelloParser() : state_(kEnded), |
||||
|
onhello_cb_(NULL), |
||||
|
onend_cb_(NULL), |
||||
|
cb_arg_(NULL) { |
||||
|
Reset(); |
||||
|
} |
||||
|
|
||||
|
class ClientHello { |
||||
|
public: |
||||
|
ClientHello() { |
||||
|
} |
||||
|
|
||||
|
inline uint8_t session_size() const { return session_size_; } |
||||
|
inline const uint8_t* session_id() const { return session_id_; } |
||||
|
inline bool has_ticket() const { return has_ticket_; } |
||||
|
|
||||
|
private: |
||||
|
uint8_t session_size_; |
||||
|
const uint8_t* session_id_; |
||||
|
bool has_ticket_; |
||||
|
|
||||
|
friend class ClientHelloParser; |
||||
|
}; |
||||
|
|
||||
|
typedef void (*OnHelloCb)(void* arg, const ClientHello& hello); |
||||
|
typedef void (*OnEndCb)(void* arg); |
||||
|
|
||||
|
void Parse(const uint8_t* data, size_t avail); |
||||
|
|
||||
|
inline void Reset(); |
||||
|
inline void Start(OnHelloCb onhello_cb, OnEndCb onend_cb, void* onend_arg); |
||||
|
inline void End(); |
||||
|
inline bool IsPaused() const; |
||||
|
inline bool IsEnded() const; |
||||
|
|
||||
|
private: |
||||
|
static const uint8_t kSSL2TwoByteHeaderBit = 0x80; |
||||
|
static const uint8_t kSSL2HeaderMask = 0x3f; |
||||
|
static const size_t kMaxTLSFrameLen = 16 * 1024 + 5; |
||||
|
static const size_t kMaxSSLExFrameLen = 32 * 1024; |
||||
|
|
||||
|
enum ParseState { |
||||
|
kWaiting, |
||||
|
kTLSHeader, |
||||
|
kSSL2Header, |
||||
|
kPaused, |
||||
|
kEnded |
||||
|
}; |
||||
|
|
||||
|
enum FrameType { |
||||
|
kChangeCipherSpec = 20, |
||||
|
kAlert = 21, |
||||
|
kHandshake = 22, |
||||
|
kApplicationData = 23, |
||||
|
kOther = 255 |
||||
|
}; |
||||
|
|
||||
|
enum HandshakeType { |
||||
|
kClientHello = 1 |
||||
|
}; |
||||
|
|
||||
|
enum ExtensionType { |
||||
|
kTLSSessionTicket = 35 |
||||
|
}; |
||||
|
|
||||
|
bool ParseRecordHeader(const uint8_t* data, size_t avail); |
||||
|
void ParseHeader(const uint8_t* data, size_t avail); |
||||
|
void ParseExtension(ExtensionType type, |
||||
|
const uint8_t* data, |
||||
|
size_t len); |
||||
|
bool ParseTLSClientHello(const uint8_t* data, size_t avail); |
||||
|
#ifdef OPENSSL_NO_SSL2 |
||||
|
bool ParseSSL2ClientHello(const uint8_t* data, size_t avail); |
||||
|
#endif // OPENSSL_NO_SSL2
|
||||
|
|
||||
|
ParseState state_; |
||||
|
OnHelloCb onhello_cb_; |
||||
|
OnEndCb onend_cb_; |
||||
|
void* cb_arg_; |
||||
|
size_t frame_len_; |
||||
|
size_t body_offset_; |
||||
|
size_t extension_offset_; |
||||
|
uint8_t session_size_; |
||||
|
const uint8_t* session_id_; |
||||
|
uint16_t tls_ticket_size_; |
||||
|
const uint8_t* tls_ticket_; |
||||
|
}; |
||||
|
|
||||
|
} // namespace node
|
||||
|
|
||||
|
#endif // SRC_NODE_CRYPTO_CLIENTHELLO_H_
|
Loading…
Reference in new issue