]> git.ipfire.org Git - thirdparty/squid.git/blob - src/ssl/bio.h
merge from trunk r14590
[thirdparty/squid.git] / src / ssl / bio.h
1 /*
2 * Copyright (C) 1996-2016 The Squid Software Foundation and contributors
3 *
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.
7 */
8
9 #ifndef SQUID_SSL_BIO_H
10 #define SQUID_SSL_BIO_H
11
12 #include "fd.h"
13 #include "sbuf/SBuf.h"
14
15 #include <iosfwd>
16 #include <list>
17 #if HAVE_OPENSSL_BIO_H
18 #include <openssl/bio.h>
19 #endif
20 #include <string>
21 #include <type_traits>
22
23 // TODO: Move BinaryTokenizer to its own set of files outside of Ssl namespace.
24
25 /// Safely extracts byte-oriented (i.e., non-textual) fields from raw input.
26 /// Supports commit points for atomic incremental parsing of multi-part fields.
27 /// Throws InsufficientInput when more input is needed to parse the next field.
28 /// Throws on errors.
29 class BinaryTokenizer
30 {
31 public:
32 class InsufficientInput {}; // thrown when a method runs out of data
33 typedef uint64_t size_type; // enough for the largest supported offset
34
35 BinaryTokenizer();
36 explicit BinaryTokenizer(const SBuf &data);
37
38 /// restart parsing from the very beginning
39 /// this method is for using one BinaryTokenizer to parse independent inputs
40 void reset(const SBuf &data);
41
42 /// change input without changing parsing state
43 /// this method avoids append overheads during incremental parsing
44 void reinput(const SBuf &data) { data_ = data; }
45
46 /// make progress: future parsing failures will not rollback beyond this point
47 void commit();
48
49 /// resume [incremental] parsing from the last commit point
50 void rollback();
51
52 /// no more bytes to parse or skip
53 bool atEnd() const;
54
55 /// parse a single-byte unsigned integer
56 uint8_t uint8(const char *description);
57
58 // parse a two-byte unsigned integer
59 uint16_t uint16(const char *description);
60
61 // parse a three-byte unsigned integer (returned as uint32_t)
62 uint32_t uint24(const char *description);
63
64 // parse a four-byte unsigned integer
65 uint32_t uint32(const char *description);
66
67 /// parse size consecutive bytes as an opaque blob
68 SBuf area(uint64_t size, const char *description);
69
70 /// ignore the next size bytes
71 void skip(uint64_t size, const char *description);
72
73 /// yet unparsed bytes
74 SBuf leftovers() const { return data_.substr(parsed_); }
75
76 const char *context; ///< simplifies debugging
77
78 protected:
79 uint32_t octet();
80 void want(uint64_t size, const char *description) const;
81 void got(uint32_t value, uint64_t size, const char *description) const;
82 void got(const SBuf &value, uint64_t size, const char *description) const;
83 void skipped(uint64_t size, const char *description) const;
84
85 private:
86 SBuf data_;
87 uint64_t parsed_; ///< number of data bytes parsed or skipped
88 uint64_t syncPoint_; ///< where to re-start the next parsing attempt
89 };
90
91
92 namespace Ssl
93 {
94
95 // The Transport Layer Security (TLS) Protocol, Version 1.2
96
97 // TODO: Consider removing this namespace. The idea was to encapsulate various
98 // RFC 5246 types defined using the naming scheme from the RFC rather than
99 // following Squid naming conventions. However, using these names in other code
100 // may make that code inconsistent. Besides, we are running into some C++ naming
101 // limits.
102 namespace Rfc5246
103 {
104
105 /// Helper class to debug parsing of various TLS structures
106 class FieldGroup
107 {
108 public:
109 FieldGroup(BinaryTokenizer &tk, const char *description); ///< starts parsing
110
111 void commit(BinaryTokenizer &tk); ///< commits successful parsing results
112 };
113
114 /// TLS Record Layer's content types from RFC 5246 Section 6.2.1
115 enum ContentType {
116 ctChangeCipherSpec = 20,
117 ctAlert = 21,
118 ctHandshake = 22,
119 ctApplicationData = 23
120 };
121
122 /// TLS Record Layer's protocol version from RFC 5246 Section 6.2.1
123 struct ProtocolVersion
124 {
125 explicit ProtocolVersion(BinaryTokenizer &tk);
126
127 // the "v" prefix works around environments that #define major and minor
128 uint8_t vMajor;
129 uint8_t vMinor;
130 };
131
132 /// TLS Record Layer's frame from RFC 5246 Section 6.2.1.
133 struct TLSPlaintext: public FieldGroup
134 {
135 explicit TLSPlaintext(BinaryTokenizer &tk);
136
137 uint8_t type; ///< Rfc5246::ContentType
138 ProtocolVersion version;
139 uint16_t length;
140 SBuf fragment; ///< exactly length bytes
141 };
142
143 /// TLS Handshake protocol's handshake types from RFC 5246 Section 7.4
144 enum HandshakeType {
145 hskServerHello = 2,
146 hskCertificate = 11,
147 hskServerHelloDone = 14
148 };
149
150 /// TLS Handshake Protocol frame from RFC 5246 Section 7.4.
151 struct Handshake: public FieldGroup
152 {
153 explicit Handshake(BinaryTokenizer &tk);
154
155 uint32_t msg_type: 8; ///< HandshakeType
156 uint32_t length: 24;
157 SBuf body; ///< Handshake Protocol message, exactly length bytes
158 };
159
160 /// TLS Alert protocol frame from RFC 5246 Section 7.2.
161 struct Alert: public FieldGroup
162 {
163 explicit Alert(BinaryTokenizer &tk);
164 uint8_t level; ///< warning or fatal
165 uint8_t description; ///< close_notify, unexpected_message, etc.
166 };
167
168 /// Like a Pascal "length-first" string but with a 3-byte length field.
169 /// Used for (undocumented in RRC 5246?) Certificate and ASN1.Cert encodings.
170 struct P24String: public FieldGroup
171 {
172 explicit P24String(BinaryTokenizer &tk, const char *description);
173
174 uint32_t length; // bytes in body (stored using 3 bytes, not 4!)
175 SBuf body; ///< exactly length bytes
176 };
177
178 } // namespace Rfc5246
179
180
181 /// Incremental SSL Handshake parser.
182 class HandshakeParser {
183 public:
184 /// The parsing states
185 typedef enum {atHelloNone = 0, atHelloStarted, atHelloReceived, atCertificatesReceived, atHelloDoneReceived, atNstReceived, atCcsReceived, atFinishReceived} ParserState;
186
187 HandshakeParser(): state(atHelloNone), ressumingSession(false), parseDone(false), parseError(false), currentContentType(0), unParsedContent(0), parsingPos(0), currentMsg(0), currentMsgSize(0), certificatesMsgPos(0), certificatesMsgSize(0) {}
188
189 /// Parses the initial sequence of raw bytes sent by the SSL server.
190 /// Returns true upon successful completion (HelloDone or Finished received).
191 /// Otherwise, returns false (and sets parseError to true on errors).
192 bool parseServerHello(const SBuf &data);
193
194 Ssl::X509_STACK_Pointer serverCertificates; ///< parsed certificates chain
195
196 ParserState state; ///< current parsing state.
197
198 bool ressumingSession; ///< True if this is a resumming session
199
200 bool parseDone; ///< The parser finishes its job
201 bool parseError; ///< Set to tru by parse on parse error.
202
203 private:
204 unsigned int currentContentType; ///< The current SSL record content type
205 size_t unParsedContent; ///< The size of current SSL record, which is not parsed yet
206 size_t parsingPos; ///< The parsing position from the beginning of parsed data
207 size_t currentMsg; ///< The current handshake message possition from the beginning of parsed data
208 size_t currentMsgSize; ///< The current handshake message size.
209
210 size_t certificatesMsgPos; ///< The possition of certificates message from the beggining of parsed data
211 size_t certificatesMsgSize; ///< The size of certificates message
212
213 private:
214 void parseServerHelloTry();
215
216 void parseRecord();
217 void parseMessages();
218
219 void parseChangeCipherCpecMessage();
220 void parseAlertMessage();
221 void parseHandshakeMessage();
222 void parseApplicationDataMessage();
223 void skipMessage(const char *msgType);
224
225 void parseServerCertificates(const SBuf &raw);
226 static X509 *ParseCertificate(const SBuf &raw);
227
228 /// concatenated TLSPlaintext.fragments of TLSPlaintext.type
229 SBuf fragments;
230
231 BinaryTokenizer tkRecords; // TLS record layer (parsing uninterpreted data)
232 BinaryTokenizer tkMessages; // TLS message layer (parsing fragments)
233 };
234
235 /// BIO source and sink node, handling socket I/O and monitoring SSL state
236 class Bio
237 {
238 public:
239 enum Type {
240 BIO_TO_CLIENT = 6000,
241 BIO_TO_SERVER
242 };
243
244 /// Class to store SSL connection features
245 class sslFeatures
246 {
247 public:
248 sslFeatures();
249 bool get(const SSL *ssl); ///< Retrieves the features from SSL object
250 /// Retrieves features from raw SSL Hello message.
251 /// \param record whether to store Message to the helloMessage member
252 bool get(const SBuf &, bool record = true);
253 /// Parses a v3 ClientHello message
254 bool parseV3Hello(const unsigned char *hello, size_t helloSize);
255 /// Parses a v23 ClientHello message
256 bool parseV23Hello(const unsigned char *hello, size_t helloSize);
257 /// Parses a v3 ServerHello message.
258 bool parseV3ServerHello(const unsigned char *hello, size_t helloSize);
259 /// Prints to os stream a human readable form of sslFeatures object
260 std::ostream & print(std::ostream &os) const;
261 /// Converts to the internal squid SSL version form the sslVersion
262 int toSquidSSLVersion() const;
263 /// Configure the SSL object with the SSL features of the sslFeatures object
264 void applyToSSL(SSL *ssl, Ssl::BumpMode bumpMode) const;
265 /// Parses an SSL Message header. It returns the ssl Message size.
266 /// \retval >0 if the hello size is retrieved
267 /// \retval 0 if the contents of the buffer are not enough
268 /// \retval <0 if the contents of buf are not SSLv3 or TLS hello message
269 int parseMsgHead(const SBuf &);
270 public:
271 int sslHelloVersion; ///< The SSL hello message version
272 int sslVersion; ///< The requested/used SSL version
273 int compressMethod; ///< The requested/used compressed method
274 int helloMsgSize; ///< the hello message size
275 mutable SBuf serverName; ///< The SNI hostname, if any
276 std::string clientRequestedCiphers; ///< The client requested ciphers
277 bool unknownCiphers; ///< True if one or more ciphers are unknown
278 bool doHeartBeats;
279 bool tlsTicketsExtension; ///< whether TLS tickets extension is enabled
280 bool hasTlsTicket; ///< whether a TLS ticket is included
281 bool tlsStatusRequest; ///< whether the TLS status request extension is set
282 SBuf tlsAppLayerProtoNeg; ///< The value of the TLS application layer protocol extension if it is enabled
283 /// The client random number
284 unsigned char client_random[SSL3_RANDOM_SIZE];
285 SBuf sessionId;
286 std::list<int> extensions;
287 SBuf helloMessage;
288 bool initialized_;
289 };
290 explicit Bio(const int anFd);
291 virtual ~Bio();
292
293 /// Writes the given data to socket
294 virtual int write(const char *buf, int size, BIO *table);
295
296 /// Reads data from socket
297 virtual int read(char *buf, int size, BIO *table);
298
299 /// Flushes any buffered data to socket.
300 /// The Ssl::Bio does not buffer any data, so this method has nothing to do
301 virtual void flush(BIO *table) {}
302
303 int fd() const { return fd_; } ///< The SSL socket descriptor
304
305 /// Called by linked SSL connection whenever state changes, an alert
306 /// appears, or an error occurs. See SSL_set_info_callback().
307 virtual void stateChanged(const SSL *ssl, int where, int ret);
308
309 /// Creates a low-level BIO table, creates a high-level Ssl::Bio object
310 /// for a given socket, and then links the two together via BIO_C_SET_FD.
311 static BIO *Create(const int fd, Type type);
312 /// Tells ssl connection to use BIO and monitor state via stateChanged()
313 static void Link(SSL *ssl, BIO *bio);
314
315 /// Reads data from socket and record them to a buffer
316 int readAndBuffer(BIO *table, const char *description);
317
318 /// Return the TLS features requested by TLS client
319 const Bio::sslFeatures &receivedHelloFeatures() const {return receivedHelloFeatures_;}
320
321 const SBuf &rBufData() {return rbuf;}
322 protected:
323 const int fd_; ///< the SSL socket we are reading and writing
324 SBuf rbuf; ///< Used to buffer input data.
325 /// The features retrieved from client or Server TLS hello message
326 Bio::sslFeatures receivedHelloFeatures_;
327 };
328
329 /// BIO node to handle socket IO for squid client side
330 /// If bumping is enabled this Bio detects and analyses client hello message
331 /// to retrieve the SSL features supported by the client
332 class ClientBio: public Bio
333 {
334 public:
335 /// The ssl hello message read states
336 typedef enum {atHelloNone = 0, atHelloStarted, atHelloReceived} HelloReadState;
337 explicit ClientBio(const int anFd): Bio(anFd), holdRead_(false), holdWrite_(false), helloState(atHelloNone), helloSize(0), wrongProtocol(false) {}
338
339 /// The ClientBio version of the Ssl::Bio::stateChanged method
340 /// When the client hello message retrieved, fill the
341 /// "features" member with the client provided informations.
342 virtual void stateChanged(const SSL *ssl, int where, int ret);
343 /// The ClientBio version of the Ssl::Bio::write method
344 virtual int write(const char *buf, int size, BIO *table);
345 /// The ClientBio version of the Ssl::Bio::read method
346 /// If the holdRead flag is true then it does not write any data
347 /// to socket and sets the "read retry" flag of the BIO to true
348 virtual int read(char *buf, int size, BIO *table);
349 /// Return true if the client hello message received and analized
350 bool gotHello() { return (helloState == atHelloReceived); }
351 /// Prevents or allow writting on socket.
352 void hold(bool h) {holdRead_ = holdWrite_ = h;}
353 /// True if client does not looks like an SSL client
354 bool noSslClient() {return wrongProtocol;}
355 private:
356 /// True if the SSL state corresponds to a hello message
357 bool isClientHello(int state);
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
363 };
364
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
373 /// connection
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
381 {
382 public:
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);
399
400 /// Parses server Hello message if it is recorded and extracts
401 /// server-supported features.
402 void extractHelloFeatures();
403
404 bool resumingSession();
405
406 /// Reads Server hello message+certificates+ServerHelloDone message sent
407 /// by server and buffer it to rbuf member
408 int readAndBufferServerHelloMsg(BIO *table, const char *description);
409
410 /// The write hold state
411 bool holdWrite() const {return holdWrite_;}
412 /// Enables or disables the write hold state
413 void holdWrite(bool h) {holdWrite_ = h;}
414 /// The read hold state
415 bool holdRead() const {return holdRead_;}
416 /// Enables or disables the read hold state
417 void holdRead(bool h) {holdRead_ = h;}
418 /// Enables or disables the input data recording, for internal analysis.
419 void recordInput(bool r) {record_ = r;}
420 /// Whether we can splice or not the SSL stream
421 bool canSplice() {return allowSplice;}
422 /// Whether we can bump or not the SSL stream
423 bool canBump() {return allowBump;}
424 /// The bumping mode
425 void mode(Ssl::BumpMode m) {bumpMode_ = m;}
426 Ssl::BumpMode bumpMode() {return bumpMode_;} ///< return the bumping mode
427
428 /// Return true if the Server hello message received
429 bool gotHello() const { return (parser_.parseDone && !parser_.parseError); }
430
431 /// Return true if the Server Hello parsing failed
432 bool gotHelloFailed() const { return (parser_.parseDone && parser_.parseError); }
433
434 const Ssl::X509_STACK_Pointer &serverCertificates() { return parser_.serverCertificates; } /* XXX: may be nil */
435
436 private:
437 sslFeatures clientFeatures; ///< SSL client features extracted from ClientHello message or SSL object
438 SBuf helloMsg; ///< Used to buffer output data.
439 mb_size_t helloMsgSize;
440 bool helloBuild; ///< True if the client hello message sent to the server
441 bool allowSplice; ///< True if the SSL stream can be spliced
442 bool allowBump; ///< True if the SSL stream can be bumped
443 bool holdWrite_; ///< The write hold state of the bio.
444 bool holdRead_; ///< The read hold state of the bio.
445 bool record_; ///< If true the input data recorded to rbuf for internal use
446 Ssl::BumpMode bumpMode_;
447
448 ///< The size of data stored in rbuf which passed to the openSSL
449 size_t rbufConsumePos;
450 HandshakeParser parser_; ///< The SSL messages parser.
451 };
452
453 inline
454 std::ostream &operator <<(std::ostream &os, Ssl::Bio::sslFeatures const &f)
455 {
456 return f.print(os);
457 }
458
459 } // namespace Ssl
460
461 #endif /* SQUID_SSL_BIO_H */
462