]> git.ipfire.org Git - thirdparty/squid.git/blob - src/ssl/bio.h
Fetch missing certificates.
[thirdparty/squid.git] / src / ssl / bio.h
1 /*
2 * Copyright (C) 1996-2015 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.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
22 namespace Ssl
23 {
24 class HandshakeParser {
25 public:
26 /// The parsing states
27 typedef enum {atHelloNone = 0, atHelloStarted, atHelloReceived, atCertificatesReceived, atHelloDoneReceived, atNstReceived, atCcsReceived, atFinishReceived} ParserState;
28
29 /// TLS record protocol, content types, RFC5246 section 6.2.1
30 typedef enum {ctNone = 0, ctChangeCipherSpec = 20, ctAlert = 21, ctHandshake = 22, ctApplicationData} ContentType;
31 /// TLS Handshake protocol, handshake types, RFC5246 section 7.4
32 typedef enum {hskNone = 0, hskServerHello = 2, shkNewSessionTicket = 4, hskCertificate = 11, hskServerHelloDone = 14, hskFinished = 20} HandshakeType;
33
34 HandshakeParser(): state(atHelloNone), currentContentType(ctNone), unParsedContent(0), parsingPos(0), currentMsg(0), currentMsgSize(0), certificatesMsgPos(0), certificatesMsgSize(0), ressumingSession(false), parseDone(false), parseError(false) {}
35
36 /// Parses the SSL Server Hello records stored in data.
37 /// Return false if the hello messages are not complete (HelloDone
38 /// or Finished handshake messages are not received)
39 /// On parse error, return false and sets the parseError member to true.
40 bool parseServerHello(const unsigned char *data, size_t dataSize);
41
42 /// Parse server certificates message and store the certificate to serverCertificates list
43 bool parseServerCertificates(Ssl::X509_STACK_Pointer &serverCertificates, const unsigned char *msg, size_t size);
44
45 ParserState state; ///< current parsing state.
46
47 ContentType currentContentType; ///< The current SSL record content type
48 size_t unParsedContent; ///< The size of current SSL record, which is not parsed yet
49 size_t parsingPos; ///< The parsing position from the beginning of parsed data
50 size_t currentMsg; ///< The current handshake message possition from the beginning of parsed data
51 size_t currentMsgSize; ///< The current handshake message size.
52
53 size_t certificatesMsgPos; ///< The possition of certificates message from the beggining of parsed data
54 size_t certificatesMsgSize; ///< The size of certificates message
55 bool ressumingSession; ///< True if this is a resumming session
56
57 bool parseDone; ///< The parser finishes its job
58 bool parseError; ///< Set to tru by parse on parse error.
59
60 private:
61 /// Do nothing if there are unparsed data from existing SSL record
62 /// else parses the next SSL record.
63 /// Return false if the next SSL record is not complete.
64 bool parseNextContentRecord(const unsigned char *msg, size_t size);
65 /// Consumes the current SSL record and set the parsingPos to the next
66 bool skipContentDataRecord(const unsigned char *msg, size_t size);
67 /// Parses the next handshake message in current SSL record
68 HandshakeType parseNextHandshakeMessage(const unsigned char *msg, size_t size);
69 };
70
71 /// BIO source and sink node, handling socket I/O and monitoring SSL state
72 class Bio
73 {
74 public:
75 enum Type {
76 BIO_TO_CLIENT = 6000,
77 BIO_TO_SERVER
78 };
79
80 /// Class to store SSL connection features
81 class sslFeatures
82 {
83 public:
84 sslFeatures();
85 bool get(const SSL *ssl); ///< Retrieves the features from SSL object
86 /// Retrieves features from raw SSL Hello message.
87 /// \param record whether to store Message to the helloMessage member
88 bool get(const MemBuf &, bool record = true);
89 /// Parses a v3 ClientHello message
90 bool parseV3Hello(const unsigned char *hello, size_t helloSize);
91 /// Parses a v23 ClientHello message
92 bool parseV23Hello(const unsigned char *hello, size_t helloSize);
93 /// Parses a v3 ServerHello message.
94 bool parseV3ServerHello(const unsigned char *hello, size_t helloSize);
95 /// Prints to os stream a human readable form of sslFeatures object
96 std::ostream & print(std::ostream &os) const;
97 /// Converts to the internal squid SSL version form the sslVersion
98 int toSquidSSLVersion() const;
99 /// Configure the SSL object with the SSL features of the sslFeatures object
100 void applyToSSL(SSL *ssl, Ssl::BumpMode bumpMode) const;
101 /// Parses an SSL Message header. It returns the ssl Message size.
102 /// \retval >0 if the hello size is retrieved
103 /// \retval 0 if the contents of the buffer are not enough
104 /// \retval <0 if the contents of buf are not SSLv3 or TLS hello message
105 int parseMsgHead(const MemBuf &);
106 public:
107 int sslVersion; ///< The requested/used SSL version
108 int compressMethod; ///< The requested/used compressed method
109 int helloMsgSize; ///< the hello message size
110 mutable SBuf serverName; ///< The SNI hostname, if any
111 std::string clientRequestedCiphers; ///< The client requested ciphers
112 bool unknownCiphers; ///< True if one or more ciphers are unknown
113 std::string ecPointFormatList;///< tlsExtension ecPointFormatList
114 std::string ellipticCurves; ///< tlsExtension ellipticCurveList
115 std::string opaquePrf; ///< tlsExtension opaquePrf
116 bool doHeartBeats;
117 bool tlsTicketsExtension; ///< whether TLS tickets extension is enabled
118 bool hasTlsTicket; ///< whether a TLS ticket is included
119 bool tlsStatusRequest; ///< whether the TLS status request extension is set
120 SBuf tlsAppLayerProtoNeg; ///< The value of the TLS application layer protocol extension if it is enabled
121 /// The client random number
122 unsigned char client_random[SSL3_RANDOM_SIZE];
123 SBuf sessionId;
124 std::list<int> extensions;
125 SBuf helloMessage;
126 bool initialized_;
127 };
128 explicit Bio(const int anFd);
129 virtual ~Bio();
130
131 /// Writes the given data to socket
132 virtual int write(const char *buf, int size, BIO *table);
133
134 /// Reads data from socket
135 virtual int read(char *buf, int size, BIO *table);
136
137 /// Flushes any buffered data to socket.
138 /// The Ssl::Bio does not buffer any data, so this method has nothing to do
139 virtual void flush(BIO *table) {}
140
141 int fd() const { return fd_; } ///< The SSL socket descriptor
142
143 /// Called by linked SSL connection whenever state changes, an alert
144 /// appears, or an error occurs. See SSL_set_info_callback().
145 virtual void stateChanged(const SSL *ssl, int where, int ret);
146
147 /// Creates a low-level BIO table, creates a high-level Ssl::Bio object
148 /// for a given socket, and then links the two together via BIO_C_SET_FD.
149 static BIO *Create(const int fd, Type type);
150 /// Tells ssl connection to use BIO and monitor state via stateChanged()
151 static void Link(SSL *ssl, BIO *bio);
152
153 /// Prepare the rbuf buffer to accept hello data
154 void prepReadBuf();
155
156 /// Reads data from socket and record them to a buffer
157 int readAndBuffer(BIO *table, const char *description);
158
159 const MemBuf &rBufData() {return rbuf;}
160 protected:
161 const int fd_; ///< the SSL socket we are reading and writing
162 MemBuf rbuf; ///< Used to buffer input data.
163 };
164
165 /// BIO node to handle socket IO for squid client side
166 /// If bumping is enabled this Bio detects and analyses client hello message
167 /// to retrieve the SSL features supported by the client
168 class ClientBio: public Bio
169 {
170 public:
171 /// The ssl hello message read states
172 typedef enum {atHelloNone = 0, atHelloStarted, atHelloReceived} HelloReadState;
173 explicit ClientBio(const int anFd): Bio(anFd), holdRead_(false), holdWrite_(false), helloState(atHelloNone), helloSize(0), wrongProtocol(false) {}
174
175 /// The ClientBio version of the Ssl::Bio::stateChanged method
176 /// When the client hello message retrieved, fill the
177 /// "features" member with the client provided informations.
178 virtual void stateChanged(const SSL *ssl, int where, int ret);
179 /// The ClientBio version of the Ssl::Bio::write method
180 virtual int write(const char *buf, int size, BIO *table);
181 /// The ClientBio version of the Ssl::Bio::read method
182 /// If the holdRead flag is true then it does not write any data
183 /// to socket and sets the "read retry" flag of the BIO to true
184 virtual int read(char *buf, int size, BIO *table);
185 /// Return true if the client hello message received and analized
186 bool gotHello() { return (helloState == atHelloReceived); }
187 /// Return the SSL features requested by SSL client
188 const Bio::sslFeatures &getFeatures() const {return features;}
189 /// Prevents or allow writting on socket.
190 void hold(bool h) {holdRead_ = holdWrite_ = h;}
191 /// True if client does not looks like an SSL client
192 bool noSslClient() {return wrongProtocol;}
193 private:
194 /// True if the SSL state corresponds to a hello message
195 bool isClientHello(int state);
196 /// The futures retrieved from client SSL hello message
197 Bio::sslFeatures features;
198 bool holdRead_; ///< The read hold state of the bio.
199 bool holdWrite_; ///< The write hold state of the bio.
200 HelloReadState helloState; ///< The SSL hello read state
201 int helloSize; ///< The SSL hello message sent by client size
202 bool wrongProtocol; ///< true if client SSL hello parsing failed
203 };
204
205 /// BIO node to handle socket IO for squid server side
206 /// If bumping is enabled, analyses the SSL hello message sent by squid OpenSSL
207 /// subsystem (step3 bumping step) against bumping mode:
208 /// * Peek mode: Send client hello message instead of the openSSL generated
209 /// hello message and normaly denies bumping and allow only
210 /// splice or terminate the SSL connection
211 /// * Stare mode: Sends the openSSL generated hello message and normaly
212 /// denies splicing and allow bump or terminate the SSL
213 /// connection
214 /// If SQUID_USE_OPENSSL_HELLO_OVERWRITE_HACK is enabled also checks if the
215 /// openSSL library features are compatible with the features reported in
216 /// web client SSL hello message and if it is, overwrites the openSSL SSL
217 /// object members to replace hello message with web client hello message.
218 /// This is may allow bumping in peek mode and splicing in stare mode after
219 /// the server hello message received.
220 class ServerBio: public Bio
221 {
222 public:
223 explicit ServerBio(const int anFd): Bio(anFd), helloMsgSize(0), helloBuild(false), allowSplice(false), allowBump(false), holdWrite_(false), holdRead_(true), bumpMode_(bumpNone), rbufConsumePos(0) {}
224 /// The ServerBio version of the Ssl::Bio::stateChanged method
225 virtual void stateChanged(const SSL *ssl, int where, int ret);
226 /// The ServerBio version of the Ssl::Bio::write method
227 /// If a clientRandom number is set then rewrites the raw hello message
228 /// "client random" field with the provided random number.
229 /// It may buffer the output packets.
230 virtual int write(const char *buf, int size, BIO *table);
231 /// The ServerBio version of the Ssl::Bio::read method
232 /// If the record flag is set then append the data to the rbuf member
233 virtual int read(char *buf, int size, BIO *table);
234 /// The ServerBio version of the Ssl::Bio::flush method.
235 /// Flushes any buffered data
236 virtual void flush(BIO *table);
237 /// Sets the random number to use in client SSL HELLO message
238 void setClientFeatures(const sslFeatures &features);
239
240 bool resumingSession();
241
242 /// Reads Server hello message+certificates+ServerHelloDone message sent
243 /// by server and buffer it to rbuf member
244 int readAndBufferServerHelloMsg(BIO *table, const char *description);
245
246 /// The write hold state
247 bool holdWrite() const {return holdWrite_;}
248 /// Enables or disables the write hold state
249 void holdWrite(bool h) {holdWrite_ = h;}
250 /// The read hold state
251 bool holdRead() const {return holdRead_;}
252 /// Enables or disables the read hold state
253 void holdRead(bool h) {holdRead_ = h;}
254 /// Whether we can splice or not the SSL stream
255 bool canSplice() {return allowSplice;}
256 /// Whether we can bump or not the SSL stream
257 bool canBump() {return allowBump;}
258 /// The bumping mode
259 void mode(Ssl::BumpMode m) {bumpMode_ = m;}
260 Ssl::BumpMode bumpMode() {return bumpMode_;} ///< return the bumping mode
261
262 /// Return true if the Server hello message received
263 bool gotHello() const { return (parser_.parseDone && !parser_.parseError); }
264
265 /// Return true if the Server Hello parsing failed
266 bool gotHelloFailed() const { return (parser_.parseDone && parser_.parseError); }
267
268 const Ssl::X509_STACK_Pointer &serverCertificates();
269 private:
270 sslFeatures clientFeatures; ///< SSL client features extracted from ClientHello message or SSL object
271 SBuf helloMsg; ///< Used to buffer output data.
272 mb_size_t helloMsgSize;
273 bool helloBuild; ///< True if the client hello message sent to the server
274 bool allowSplice; ///< True if the SSL stream can be spliced
275 bool allowBump; ///< True if the SSL stream can be bumped
276 bool holdWrite_; ///< The write hold state of the bio.
277 bool holdRead_; ///< The read hold state of the bio.
278 Ssl::BumpMode bumpMode_;
279
280 ///< The size of data stored in rbuf which passed to the openSSL
281 size_t rbufConsumePos;
282 HandshakeParser parser_; ///< The SSL messages parser.
283 Ssl::X509_STACK_Pointer serverCertificates_; ///< The certificates chain sent by the SSL server
284 };
285
286 inline
287 std::ostream &operator <<(std::ostream &os, Ssl::Bio::sslFeatures const &f)
288 {
289 return f.print(os);
290 }
291
292 } // namespace Ssl
293
294 #endif /* SQUID_SSL_BIO_H */
295