1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227
|
#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:
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 {
End();
return false;
}
// 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) {
ClientHello hello;
// >= 5 + frame size bytes for frame parsing
if (body_offset_ + frame_len_ > avail)
return;
// Check hello protocol version. Protocol tuples that we know about:
//
// (3,1) TLS v1.0
// (3,2) TLS v1.1
// (3,3) TLS v1.2
//
if (data[body_offset_ + 4] != 0x03 ||
data[body_offset_ + 5] < 0x01 ||
data[body_offset_ + 5] > 0x03) {
goto fail;
}
if (data[body_offset_] == kClientHello) {
if (state_ == kTLSHeader) {
if (!ParseTLSClientHello(data, avail))
goto fail;
} else {
// We couldn't get here, but whatever
goto fail;
}
// Check if we overflowed (do not reply with any private data)
if (session_id_ == nullptr ||
session_size_ > 32 ||
session_id_ + session_size_ > data + avail) {
goto fail;
}
}
state_ = kPaused;
hello.session_id_ = session_id_;
hello.session_size_ = session_size_;
hello.has_ticket_ = tls_ticket_ != nullptr && tls_ticket_size_ != 0;
hello.ocsp_request_ = ocsp_request_;
hello.servername_ = servername_;
hello.servername_size_ = static_cast<uint8_t>(servername_size_);
onhello_cb_(cb_arg_, hello);
return;
fail:
return End();
}
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 kServerName:
{
if (len < 2)
return;
uint32_t server_names_len = (data[0] << 8) + data[1];
if (server_names_len + 2 > len)
return;
for (size_t offset = 2; offset < 2 + server_names_len; ) {
if (offset + 3 > len)
return;
uint8_t name_type = data[offset];
if (name_type != kServernameHostname)
return;
uint16_t name_len = (data[offset + 1] << 8) + data[offset + 2];
offset += 3;
if (offset + name_len > len)
return;
servername_ = data + offset;
servername_size_ = name_len;
offset += name_len;
}
}
break;
case kStatusRequest:
// We are ignoring any data, just indicating the presence of extension
if (len < kMinStatusRequestSize)
return;
// Unknown type, ignore it
if (data[0] != kStatusRequestOCSP)
break;
// Ignore extensions, they won't work with caching on backend anyway
ocsp_request_ = 1;
break;
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;
}
} // namespace node
|