2 * Copyright (C) 1996-2015 The Squid Software Foundation and contributors
4 * Squid software is distributed under GPLv2+ license and includes
5 * contributions from numerous individuals and organizations.
6 * Please see the COPYING and CONTRIBUTORS files for details.
9 #ifndef SQUID_SSL_BIO_H
10 #define SQUID_SSL_BIO_H
16 #if HAVE_OPENSSL_BIO_H
17 #include <openssl/bio.h>
20 #include <type_traits>
22 // TODO: Move BinaryTokenizer to its own set of files outside of Ssl namespace.
24 /// Safely extracts byte-oriented (i.e., non-textual) fields from raw input.
25 /// Supports commit points for atomic incremental parsing of multi-part fields.
26 /// Throws InsufficientInput when more input is needed to parse the next field.
31 class InsufficientInput
{}; // thrown when a method runs out of data
32 typedef uint64_t size_type
; // enough for the largest supported offset
35 explicit BinaryTokenizer(const SBuf
&data
);
37 /// restart parsing from the very beginning
38 /// this method is for using one BinaryTokenizer to parse independent inputs
39 void reset(const SBuf
&data
);
41 /// change input without changing parsing state
42 /// this method avoids append overheads during incremental parsing
43 void reinput(const SBuf
&data
) { data_
= data
; }
45 /// make progress: future parsing failures will not rollback beyond this point
48 /// resume [incremental] parsing from the last commit point
51 /// no more bytes to parse or skip
54 /// parse a single-byte unsigned integer
55 uint8_t uint8(const char *description
);
57 // parse a two-byte unsigned integer
58 uint16_t uint16(const char *description
);
60 // parse a three-byte unsigned integer (returned as uint32_t)
61 uint32_t uint24(const char *description
);
63 // parse a four-byte unsigned integer
64 uint32_t uint32(const char *description
);
66 /// parse size consecutive bytes as an opaque blob
67 SBuf
area(uint64_t size
, const char *description
);
69 /// ignore the next size bytes
70 void skip(uint64_t size
, const char *description
);
72 /// yet unparsed bytes
73 SBuf
leftovers() const { return data_
.substr(parsed_
); }
75 const char *context
; ///< simplifies debugging
79 void want(uint64_t size
, const char *description
) const;
80 void got(uint32_t value
, uint64_t size
, const char *description
) const;
81 void got(const SBuf
&value
, uint64_t size
, const char *description
) const;
82 void skipped(uint64_t size
, const char *description
) const;
86 uint64_t parsed_
; ///< number of data bytes parsed or skipped
87 uint64_t syncPoint_
; ///< where to re-start the next parsing attempt
94 // The Transport Layer Security (TLS) Protocol, Version 1.2
96 // TODO: Consider removing this namespace. The idea was to encapsulate various
97 // RFC 5246 types defined using the naming scheme from the RFC rather than
98 // following Squid naming conventions. However, using these names in other code
99 // may make that code inconsistent. Besides, we are running into some C++ naming
104 /// Helper class to debug parsing of various TLS structures
108 FieldGroup(BinaryTokenizer
&tk
, const char *description
); ///< starts parsing
110 void commit(BinaryTokenizer
&tk
); ///< commits successful parsing results
113 /// TLS Record Layer's content types from RFC 5246 Section 6.2.1
115 ctChangeCipherSpec
= 20,
118 ctApplicationData
= 23
121 /// TLS Record Layer's protocol version from RFC 5246 Section 6.2.1
122 struct ProtocolVersion
124 explicit ProtocolVersion(BinaryTokenizer
&tk
);
126 // the "v" prefix works around environments that #define major and minor
131 /// TLS Record Layer's frame from RFC 5246 Section 6.2.1.
132 struct TLSPlaintext
: public FieldGroup
134 explicit TLSPlaintext(BinaryTokenizer
&tk
);
136 uint8_t type
; ///< Rfc5246::ContentType
137 ProtocolVersion version
;
139 SBuf fragment
; ///< exactly length bytes
142 /// TLS Handshake protocol's handshake types from RFC 5246 Section 7.4
146 hskServerHelloDone
= 14
149 /// TLS Handshake Protocol frame from RFC 5246 Section 7.4.
150 struct Handshake
: public FieldGroup
152 explicit Handshake(BinaryTokenizer
&tk
);
154 uint32_t msg_type
: 8; ///< HandshakeType
156 SBuf body
; ///< Handshake Protocol message, exactly length bytes
159 /// TLS Alert protocol frame from RFC 5246 Section 7.2.
160 struct Alert
: public FieldGroup
162 explicit Alert(BinaryTokenizer
&tk
);
163 uint8_t level
; ///< warning or fatal
164 uint8_t description
; ///< close_notify, unexpected_message, etc.
167 /// Like a Pascal "length-first" string but with a 3-byte length field.
168 /// Used for (undocumented in RRC 5246?) Certificate and ASN1.Cert encodings.
169 struct P24String
: public FieldGroup
171 explicit P24String(BinaryTokenizer
&tk
, const char *description
);
173 uint32_t length
; // bytes in body (stored using 3 bytes, not 4!)
174 SBuf body
; ///< exactly length bytes
177 } // namespace Rfc5246
180 /// Incremental SSL Handshake parser.
181 class HandshakeParser
{
183 /// The parsing states
184 typedef enum {atHelloNone
= 0, atHelloStarted
, atHelloReceived
, atCertificatesReceived
, atHelloDoneReceived
, atNstReceived
, atCcsReceived
, atFinishReceived
} ParserState
;
186 HandshakeParser(): state(atHelloNone
), ressumingSession(false), parseDone(false), parseError(false), currentContentType(0), unParsedContent(0), parsingPos(0), currentMsg(0), currentMsgSize(0), certificatesMsgPos(0), certificatesMsgSize(0) {}
188 /// Parses the initial sequence of raw bytes sent by the SSL server.
189 /// Returns true upon successful completion (HelloDone or Finished received).
190 /// Otherwise, returns false (and sets parseError to true on errors).
191 bool parseServerHello(const SBuf
&data
);
193 Ssl::X509_STACK_Pointer serverCertificates
; ///< parsed certificates chain
195 ParserState state
; ///< current parsing state.
197 bool ressumingSession
; ///< True if this is a resumming session
199 bool parseDone
; ///< The parser finishes its job
200 bool parseError
; ///< Set to tru by parse on parse error.
203 unsigned int currentContentType
; ///< The current SSL record content type
204 size_t unParsedContent
; ///< The size of current SSL record, which is not parsed yet
205 size_t parsingPos
; ///< The parsing position from the beginning of parsed data
206 size_t currentMsg
; ///< The current handshake message possition from the beginning of parsed data
207 size_t currentMsgSize
; ///< The current handshake message size.
209 size_t certificatesMsgPos
; ///< The possition of certificates message from the beggining of parsed data
210 size_t certificatesMsgSize
; ///< The size of certificates message
213 void parseServerHelloTry();
216 void parseMessages();
218 void parseChangeCipherCpecMessage();
219 void parseAlertMessage();
220 void parseHandshakeMessage();
221 void parseApplicationDataMessage();
222 void skipMessage(const char *msgType
);
224 void parseServerCertificates(const SBuf
&raw
);
225 static X509
*ParseCertificate(const SBuf
&raw
);
227 /// concatenated TLSPlaintext.fragments of TLSPlaintext.type
230 BinaryTokenizer tkRecords
; // TLS record layer (parsing uninterpreted data)
231 BinaryTokenizer tkMessages
; // TLS message layer (parsing fragments)
234 /// BIO source and sink node, handling socket I/O and monitoring SSL state
239 BIO_TO_CLIENT
= 6000,
243 /// Class to store SSL connection features
248 bool get(const SSL
*ssl
); ///< Retrieves the features from SSL object
249 /// Retrieves features from raw SSL Hello message.
250 /// \param record whether to store Message to the helloMessage member
251 bool get(const MemBuf
&, bool record
= true);
252 /// Parses a v3 ClientHello message
253 bool parseV3Hello(const unsigned char *hello
, size_t helloSize
);
254 /// Parses a v23 ClientHello message
255 bool parseV23Hello(const unsigned char *hello
, size_t helloSize
);
256 /// Parses a v3 ServerHello message.
257 bool parseV3ServerHello(const unsigned char *hello
, size_t helloSize
);
258 /// Prints to os stream a human readable form of sslFeatures object
259 std::ostream
& print(std::ostream
&os
) const;
260 /// Converts to the internal squid SSL version form the sslVersion
261 int toSquidSSLVersion() const;
262 /// Configure the SSL object with the SSL features of the sslFeatures object
263 void applyToSSL(SSL
*ssl
, Ssl::BumpMode bumpMode
) const;
264 /// Parses an SSL Message header. It returns the ssl Message size.
265 /// \retval >0 if the hello size is retrieved
266 /// \retval 0 if the contents of the buffer are not enough
267 /// \retval <0 if the contents of buf are not SSLv3 or TLS hello message
268 int parseMsgHead(const MemBuf
&);
270 int sslVersion
; ///< The requested/used SSL version
271 int compressMethod
; ///< The requested/used compressed method
272 int helloMsgSize
; ///< the hello message size
273 mutable SBuf serverName
; ///< The SNI hostname, if any
274 std::string clientRequestedCiphers
; ///< The client requested ciphers
275 bool unknownCiphers
; ///< True if one or more ciphers are unknown
277 bool tlsTicketsExtension
; ///< whether TLS tickets extension is enabled
278 bool hasTlsTicket
; ///< whether a TLS ticket is included
279 bool tlsStatusRequest
; ///< whether the TLS status request extension is set
280 SBuf tlsAppLayerProtoNeg
; ///< The value of the TLS application layer protocol extension if it is enabled
281 /// The client random number
282 unsigned char client_random
[SSL3_RANDOM_SIZE
];
284 std::list
<int> extensions
;
288 explicit Bio(const int anFd
);
291 /// Writes the given data to socket
292 virtual int write(const char *buf
, int size
, BIO
*table
);
294 /// Reads data from socket
295 virtual int read(char *buf
, int size
, BIO
*table
);
297 /// Flushes any buffered data to socket.
298 /// The Ssl::Bio does not buffer any data, so this method has nothing to do
299 virtual void flush(BIO
*table
) {}
301 int fd() const { return fd_
; } ///< The SSL socket descriptor
303 /// Called by linked SSL connection whenever state changes, an alert
304 /// appears, or an error occurs. See SSL_set_info_callback().
305 virtual void stateChanged(const SSL
*ssl
, int where
, int ret
);
307 /// Creates a low-level BIO table, creates a high-level Ssl::Bio object
308 /// for a given socket, and then links the two together via BIO_C_SET_FD.
309 static BIO
*Create(const int fd
, Type type
);
310 /// Tells ssl connection to use BIO and monitor state via stateChanged()
311 static void Link(SSL
*ssl
, BIO
*bio
);
313 /// Prepare the rbuf buffer to accept hello data
316 /// Reads data from socket and record them to a buffer
317 int readAndBuffer(BIO
*table
, const char *description
);
319 const MemBuf
&rBufData() {return rbuf
;}
321 const int fd_
; ///< the SSL socket we are reading and writing
322 MemBuf rbuf
; ///< Used to buffer input data.
325 /// BIO node to handle socket IO for squid client side
326 /// If bumping is enabled this Bio detects and analyses client hello message
327 /// to retrieve the SSL features supported by the client
328 class ClientBio
: public Bio
331 /// The ssl hello message read states
332 typedef enum {atHelloNone
= 0, atHelloStarted
, atHelloReceived
} HelloReadState
;
333 explicit ClientBio(const int anFd
): Bio(anFd
), holdRead_(false), holdWrite_(false), helloState(atHelloNone
), helloSize(0), wrongProtocol(false) {}
335 /// The ClientBio version of the Ssl::Bio::stateChanged method
336 /// When the client hello message retrieved, fill the
337 /// "features" member with the client provided informations.
338 virtual void stateChanged(const SSL
*ssl
, int where
, int ret
);
339 /// The ClientBio version of the Ssl::Bio::write method
340 virtual int write(const char *buf
, int size
, BIO
*table
);
341 /// The ClientBio version of the Ssl::Bio::read method
342 /// If the holdRead flag is true then it does not write any data
343 /// to socket and sets the "read retry" flag of the BIO to true
344 virtual int read(char *buf
, int size
, BIO
*table
);
345 /// Return true if the client hello message received and analized
346 bool gotHello() { return (helloState
== atHelloReceived
); }
347 /// Return the SSL features requested by SSL client
348 const Bio::sslFeatures
&getFeatures() const {return features
;}
349 /// Prevents or allow writting on socket.
350 void hold(bool h
) {holdRead_
= holdWrite_
= h
;}
351 /// True if client does not looks like an SSL client
352 bool noSslClient() {return wrongProtocol
;}
354 /// True if the SSL state corresponds to a hello message
355 bool isClientHello(int state
);
356 /// The futures retrieved from client SSL hello message
357 Bio::sslFeatures features
;
358 bool holdRead_
; ///< The read hold state of the bio.
359 bool holdWrite_
; ///< The write hold state of the bio.
360 HelloReadState helloState
; ///< The SSL hello read state
361 int helloSize
; ///< The SSL hello message sent by client size
362 bool wrongProtocol
; ///< true if client SSL hello parsing failed
365 /// BIO node to handle socket IO for squid server side
366 /// If bumping is enabled, analyses the SSL hello message sent by squid OpenSSL
367 /// subsystem (step3 bumping step) against bumping mode:
368 /// * Peek mode: Send client hello message instead of the openSSL generated
369 /// hello message and normaly denies bumping and allow only
370 /// splice or terminate the SSL connection
371 /// * Stare mode: Sends the openSSL generated hello message and normaly
372 /// denies splicing and allow bump or terminate the SSL
374 /// If SQUID_USE_OPENSSL_HELLO_OVERWRITE_HACK is enabled also checks if the
375 /// openSSL library features are compatible with the features reported in
376 /// web client SSL hello message and if it is, overwrites the openSSL SSL
377 /// object members to replace hello message with web client hello message.
378 /// This is may allow bumping in peek mode and splicing in stare mode after
379 /// the server hello message received.
380 class ServerBio
: public Bio
383 explicit ServerBio(const int anFd
): Bio(anFd
), helloMsgSize(0), helloBuild(false), allowSplice(false), allowBump(false), holdWrite_(false), holdRead_(true), record_(false), bumpMode_(bumpNone
), rbufConsumePos(0) {}
384 /// The ServerBio version of the Ssl::Bio::stateChanged method
385 virtual void stateChanged(const SSL
*ssl
, int where
, int ret
);
386 /// The ServerBio version of the Ssl::Bio::write method
387 /// If a clientRandom number is set then rewrites the raw hello message
388 /// "client random" field with the provided random number.
389 /// It may buffer the output packets.
390 virtual int write(const char *buf
, int size
, BIO
*table
);
391 /// The ServerBio version of the Ssl::Bio::read method
392 /// If the record flag is set then append the data to the rbuf member
393 virtual int read(char *buf
, int size
, BIO
*table
);
394 /// The ServerBio version of the Ssl::Bio::flush method.
395 /// Flushes any buffered data
396 virtual void flush(BIO
*table
);
397 /// Sets the random number to use in client SSL HELLO message
398 void setClientFeatures(const sslFeatures
&features
);
400 bool resumingSession();
402 /// Reads Server hello message+certificates+ServerHelloDone message sent
403 /// by server and buffer it to rbuf member
404 int readAndBufferServerHelloMsg(BIO
*table
, const char *description
);
406 /// The write hold state
407 bool holdWrite() const {return holdWrite_
;}
408 /// Enables or disables the write hold state
409 void holdWrite(bool h
) {holdWrite_
= h
;}
410 /// The read hold state
411 bool holdRead() const {return holdRead_
;}
412 /// Enables or disables the read hold state
413 void holdRead(bool h
) {holdRead_
= h
;}
414 /// Enables or disables the input data recording, for internal analysis.
415 void recordInput(bool r
) {record_
= r
;}
416 /// Whether we can splice or not the SSL stream
417 bool canSplice() {return allowSplice
;}
418 /// Whether we can bump or not the SSL stream
419 bool canBump() {return allowBump
;}
421 void mode(Ssl::BumpMode m
) {bumpMode_
= m
;}
422 Ssl::BumpMode
bumpMode() {return bumpMode_
;} ///< return the bumping mode
424 /// Return true if the Server hello message received
425 bool gotHello() const { return (parser_
.parseDone
&& !parser_
.parseError
); }
427 /// Return true if the Server Hello parsing failed
428 bool gotHelloFailed() const { return (parser_
.parseDone
&& parser_
.parseError
); }
430 const Ssl::X509_STACK_Pointer
&serverCertificates() { return parser_
.serverCertificates
; } /* XXX: may be nil */
433 sslFeatures clientFeatures
; ///< SSL client features extracted from ClientHello message or SSL object
434 SBuf helloMsg
; ///< Used to buffer output data.
435 mb_size_t helloMsgSize
;
436 bool helloBuild
; ///< True if the client hello message sent to the server
437 bool allowSplice
; ///< True if the SSL stream can be spliced
438 bool allowBump
; ///< True if the SSL stream can be bumped
439 bool holdWrite_
; ///< The write hold state of the bio.
440 bool holdRead_
; ///< The read hold state of the bio.
441 bool record_
; ///< If true the input data recorded to rbuf for internal use
442 Ssl::BumpMode bumpMode_
;
444 ///< The size of data stored in rbuf which passed to the openSSL
445 size_t rbufConsumePos
;
446 HandshakeParser parser_
; ///< The SSL messages parser.
450 std::ostream
&operator <<(std::ostream
&os
, Ssl::Bio::sslFeatures
const &f
)
457 #endif /* SQUID_SSL_BIO_H */