1 /*
2  * Copyright (C) 1996-2021 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_SRC_SECURITY_PEERCONNECTOR_H
10 #define SQUID_SRC_SECURITY_PEERCONNECTOR_H
11 
12 #include "acl/Acl.h"
13 #include "base/AsyncCbdataCalls.h"
14 #include "base/AsyncJob.h"
15 #include "CommCalls.h"
16 #include "http/forward.h"
17 #include "security/EncryptorAnswer.h"
18 #include "security/forward.h"
19 #if USE_OPENSSL
20 #include "ssl/support.h"
21 #endif
22 
23 #include <iosfwd>
24 #include <queue>
25 
26 class ErrorState;
27 class AccessLogEntry;
28 typedef RefCount<AccessLogEntry> AccessLogEntryPointer;
29 
30 namespace Security
31 {
32 
33 /**
34  * Initiates encryption on a connection to peers or servers.
35  * Despite its name does not perform any connect(2) operations.
36  *
37  * Contains common code and interfaces of various specialized PeerConnector's,
38  * including peer certificate validation code.
39  \par
40  * The caller receives a call back with Security::EncryptorAnswer. If answer.error
41  * is not nil, then there was an error and the encryption to the peer or server
42  * was not fully established. The error object is suitable for error response
43  * generation.
44  \par
45  * The caller must monitor the connection for closure because this
46  * job will not inform the caller about such events.
47  \par
48  * PeerConnector class currently supports a form of TLS negotiation timeout,
49  * which is accounted only when sets the read timeout from encrypted peers/servers.
50  * For a complete solution, the caller must monitor the overall connection
51  * establishment timeout and close the connection on timeouts. This is probably
52  * better than having dedicated (or none at all!) timeouts for peer selection,
53  * DNS lookup, TCP handshake, SSL handshake, etc. Some steps may have their
54  * own timeout, but not all steps should be forced to have theirs.
55  * XXX: tunnel.cc and probably other subsystems do not have an "overall
56  * connection establishment" timeout. We need to change their code so that they
57  * start monitoring earlier and close on timeouts. This change may need to be
58  * discussed on squid-dev.
59  \par
60  * This job never closes the connection, even on errors. If a 3rd-party
61  * closes the connection, this job simply quits without informing the caller.
62  */
63 class PeerConnector: virtual public AsyncJob
64 {
65     CBDATA_CLASS(PeerConnector);
66 
67 public:
68     typedef CbcPointer<PeerConnector> Pointer;
69 
70     /// Callback dialer API to allow PeerConnector to set the answer.
71     class CbDialer
72     {
73     public:
~CbDialer()74         virtual ~CbDialer() {}
75         /// gives PeerConnector access to the in-dialer answer
76         virtual Security::EncryptorAnswer &answer() = 0;
77     };
78 
79 public:
80     PeerConnector(const Comm::ConnectionPointer &aServerConn,
81                   AsyncCall::Pointer &aCallback,
82                   const AccessLogEntryPointer &alp,
83                   const time_t timeout = 0);
84     virtual ~PeerConnector();
85 
86 protected:
87     // AsyncJob API
88     virtual void start();
89     virtual bool doneAll() const;
90     virtual void swanSong();
91     virtual const char *status() const;
92 
93     /// The comm_close callback handler.
94     void commCloseHandler(const CommCloseCbParams &params);
95 
96     /// Inform us that the connection is closed. Does the required clean-up.
97     void connectionClosed(const char *reason);
98 
99     /// Sets up TCP socket-related notification callbacks if things go wrong.
100     /// If socket already closed return false, else install the comm_close
101     /// handler to monitor the socket.
102     bool prepareSocket();
103 
104     /// Sets the read timeout to avoid getting stuck while reading from a
105     /// silent server
106     void setReadTimeout();
107 
108     /// \returns true on successful TLS session initialization
109     virtual bool initialize(Security::SessionPointer &);
110 
111     /// Performs a single secure connection negotiation step.
112     /// It is called multiple times untill the negotiation finishes or aborts.
113     void negotiate();
114 
115     /// Called after negotiation has finished. Cleans up TLS/SSL state.
116     /// Returns false if we are now waiting for the certs validation job.
117     /// Otherwise, returns true, regardless of negotiation success/failure.
118     bool sslFinalized();
119 
120     /// Called when the negotiation step aborted because data needs to
121     /// be transferred to/from server or on error. In the first case
122     /// setups the appropriate Comm::SetSelect handler. In second case
123     /// fill an error and report to the PeerConnector caller.
124     void handleNegotiateError(const int result);
125 
126     /// Called when the openSSL SSL_connect fnction request more data from
127     /// the remote SSL server. Sets the read timeout and sets the
128     /// Squid COMM_SELECT_READ handler.
129     void noteWantRead();
130 
131 #if USE_OPENSSL
132     /// Run the certificates list sent by the SSL server and check if there
133     /// are missing certificates. Adds to the urlOfMissingCerts list the
134     /// URLS of missing certificates if this information provided by the
135     /// issued certificates with Authority Info Access extension.
136     bool checkForMissingCertificates();
137 
138     /// Start downloading procedure for the given URL.
139     void startCertDownloading(SBuf &url);
140 
141     /// Called by Downloader after a certificate object downloaded.
142     void certDownloadingDone(SBuf &object, int status);
143 #endif
144 
145     /// Called when the openSSL SSL_connect function needs to write data to
146     /// the remote SSL server. Sets the Squid COMM_SELECT_WRITE handler.
147     virtual void noteWantWrite();
148 
149     /// Called when the SSL_connect function aborts with an SSL negotiation error
150     /// \param result the SSL_connect return code
151     /// \param ssl_error the error code returned from the SSL_get_error function
152     /// \param ssl_lib_error the error returned from the ERR_Get_Error function
153     virtual void noteNegotiationError(const int result, const int ssl_error, const int ssl_lib_error);
154 
155     /// Called when the SSL negotiation to the server completed and the certificates
156     /// validated using the cert validator.
157     /// \param error if not NULL the SSL negotiation was aborted with an error
noteNegotiationDone(ErrorState * error)158     virtual void noteNegotiationDone(ErrorState *error) {}
159 
160     /// Must implemented by the kid classes to return the TLS context object to use
161     /// for building the encryption context objects.
162     virtual Security::ContextPointer getTlsContext() = 0;
163 
164     /// mimics FwdState to minimize changes to FwdState::initiate/negotiateSsl
serverConnection()165     Comm::ConnectionPointer const &serverConnection() const { return serverConn; }
166 
167     void bail(ErrorState *error); ///< Return an error to the PeerConnector caller
168 
169     /// Callback the caller class, and pass the ready to communicate secure
170     /// connection or an error if PeerConnector failed.
171     void callBack();
172 
173     /// If called the certificates validator will not used
bypassCertValidator()174     void bypassCertValidator() {useCertValidator_ = false;}
175 
176     /// Called after negotiation finishes to record connection details for
177     /// logging
178     void recordNegotiationDetails();
179 
180     HttpRequestPointer request; ///< peer connection trigger or cause
181     Comm::ConnectionPointer serverConn; ///< TCP connection to the peer
182     AccessLogEntryPointer al; ///< info for the future access.log entry
183     AsyncCall::Pointer callback; ///< we call this with the results
184 private:
185     PeerConnector(const PeerConnector &); // not implemented
186     PeerConnector &operator =(const PeerConnector &); // not implemented
187 
188 #if USE_OPENSSL
189     /// Process response from cert validator helper
190     void sslCrtvdHandleReply(Ssl::CertValidationResponsePointer);
191 
192     /// Check SSL errors returned from cert validator against sslproxy_cert_error access list
193     Security::CertErrors *sslCrtvdCheckForErrors(Ssl::CertValidationResponse const &, Ssl::ErrorDetail *&);
194 #endif
195 
196     static void NegotiateSsl(int fd, void *data);
197     void negotiateSsl();
198 
199     /// The maximum allowed missing certificates downloads.
200     static const unsigned int MaxCertsDownloads = 10;
201     /// The maximum allowed nested certificates downloads.
202     static const unsigned int MaxNestedDownloads = 3;
203 
204     AsyncCall::Pointer closeHandler; ///< we call this when the connection closed
205     time_t negotiationTimeout; ///< the SSL connection timeout to use
206     time_t startTime; ///< when the peer connector negotiation started
207     bool useCertValidator_; ///< whether the certificate validator should bypassed
208     /// The list of URLs where missing certificates should be downloaded.
209     std::queue<SBuf> urlsOfMissingCerts;
210     unsigned int certsDownloads; ///< the number of downloaded missing certificates
211 };
212 
213 } // namespace Security
214 
215 #endif /* SQUID_SRC_SECURITY_PEERCONNECTOR_H */
216 
217