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 /* DEBUG: section 33    Client-side Routines */
10 
11 #ifndef SQUID_CLIENTSIDE_H
12 #define SQUID_CLIENTSIDE_H
13 
14 #include "base/RunnersRegistry.h"
15 #include "clientStreamForward.h"
16 #include "comm.h"
17 #include "helper/forward.h"
18 #include "http/forward.h"
19 #include "HttpControlMsg.h"
20 #include "ipc/FdNotes.h"
21 #include "sbuf/SBuf.h"
22 #include "servers/Server.h"
23 #if USE_AUTH
24 #include "auth/UserRequest.h"
25 #endif
26 #if USE_OPENSSL
27 #include "security/Handshake.h"
28 #include "ssl/support.h"
29 #endif
30 
31 #include <iosfwd>
32 
33 class ClientHttpRequest;
34 class HttpHdrRangeSpec;
35 
36 class MasterXaction;
37 typedef RefCount<MasterXaction> MasterXactionPointer;
38 
39 #if USE_OPENSSL
40 namespace Ssl
41 {
42 class ServerBump;
43 }
44 #endif
45 
46 /**
47  * Legacy Server code managing a connection to a client.
48  *
49  * NP: presents AsyncJob API but does not operate autonomously as a Job.
50  *     So Must() is not safe to use.
51  *
52  * Multiple requests (up to pipeline_prefetch) can be pipelined.
53  * This object is responsible for managing which one is currently being
54  * fulfilled and what happens to the queue if the current one causes the client
55  * connection to be closed early.
56  *
57  * Act as a manager for the client connection and passes data in buffer to a
58  * Parser relevant to the state (message headers vs body) that is being
59  * processed.
60  *
61  * Performs HTTP message processing to kick off the actual HTTP request
62  * handling objects (Http::Stream, ClientHttpRequest, HttpRequest).
63  *
64  * Performs SSL-Bump processing for switching between HTTP and HTTPS protocols.
65  *
66  * To terminate a ConnStateData close() the client Comm::Connection it is
67  * managing, or for graceful half-close use the stopReceiving() or
68  * stopSending() methods.
69  */
70 class ConnStateData : public Server, public HttpControlMsgSink, private IndependentRunner
71 {
72 
73 public:
74     explicit ConnStateData(const MasterXactionPointer &xact);
75     virtual ~ConnStateData();
76 
77     /* ::Server API */
78     virtual void receivedFirstByte();
79     virtual bool handleReadData();
80     virtual void afterClientRead();
81     virtual void afterClientWrite(size_t);
82 
83     /* HttpControlMsgSink API */
84     virtual void sendControlMsg(HttpControlMsg);
85     virtual void doneWithControlMsg();
86 
87     /// Traffic parsing
88     bool clientParseRequests();
89     void readNextRequest();
90 
91     /// try to make progress on a transaction or read more I/O
92     void kick();
93 
94     bool isOpen() const;
95 
96     Http1::TeChunkedParser *bodyParser; ///< parses HTTP/1.1 chunked request body
97 
98     /** number of body bytes we need to comm_read for the "current" request
99      *
100      * \retval 0         We do not need to read any [more] body bytes
101      * \retval negative  May need more but do not know how many; could be zero!
102      * \retval positive  Need to read exactly that many more body bytes
103      */
104     int64_t mayNeedToReadMoreBody() const;
105 
106 #if USE_AUTH
107     /**
108      * Fetch the user details for connection based authentication
109      * NOTE: this is ONLY connection based because NTLM and Negotiate is against HTTP spec.
110      */
getAuth()111     const Auth::UserRequest::Pointer &getAuth() const { return auth_; }
112 
113     /**
114      * Set the user details for connection-based authentication to use from now until connection closure.
115      *
116      * Any change to existing credentials shows that something invalid has happened. Such as:
117      * - NTLM/Negotiate auth was violated by the per-request headers missing a revalidation token
118      * - NTLM/Negotiate auth was violated by the per-request headers being for another user
119      * - SSL-Bump CONNECT tunnel with persistent credentials has ended
120      */
121     void setAuth(const Auth::UserRequest::Pointer &aur, const char *cause);
122 #endif
123 
124     Ip::Address log_addr;
125 
126     struct {
127         bool readMore; ///< needs comm_read (for this request or new requests)
128         bool swanSang; // XXX: temporary flag to check proper cleanup
129     } flags;
130     struct {
131         Comm::ConnectionPointer serverConnection; /* pinned server side connection */
132         char *host;             /* host name of pinned connection */
133         int port;               /* port of pinned connection */
134         bool pinned;             /* this connection was pinned */
135         bool auth;               /* pinned for www authentication */
136         bool reading;   ///< we are monitoring for peer connection closure
137         bool zeroReply; ///< server closed w/o response (ERR_ZERO_SIZE_OBJECT)
138         CachePeer *peer;             /* CachePeer the connection goes via */
139         AsyncCall::Pointer readHandler; ///< detects serverConnection closure
140         AsyncCall::Pointer closeHandler; /*The close handler for pinned server side connection*/
141     } pinning;
142 
143     bool transparent() const;
144 
145     /// true if we stopped receiving the request
stoppedReceiving()146     const char *stoppedReceiving() const { return stoppedReceiving_; }
147     /// true if we stopped sending the response
stoppedSending()148     const char *stoppedSending() const { return stoppedSending_; }
149     /// note request receiving error and close as soon as we write the response
150     void stopReceiving(const char *error);
151     /// note response sending error and close as soon as we read the request
152     void stopSending(const char *error);
153 
154     void expectNoForwarding(); ///< cleans up virgin request [body] forwarding state
155 
156     /* BodyPipe API */
157     BodyPipe::Pointer expectRequestBody(int64_t size);
158     virtual void noteMoreBodySpaceAvailable(BodyPipe::Pointer) = 0;
159     virtual void noteBodyConsumerAborted(BodyPipe::Pointer) = 0;
160 
161     bool handleRequestBodyData();
162 
163     /// parameters for the async notePinnedConnectionBecameIdle() call
164     class PinnedIdleContext
165     {
166     public:
PinnedIdleContext(const Comm::ConnectionPointer & conn,const HttpRequest::Pointer & req)167         PinnedIdleContext(const Comm::ConnectionPointer &conn, const HttpRequest::Pointer &req): connection(conn), request(req) {}
168 
169         Comm::ConnectionPointer connection; ///< to-server connection to be pinned
170         HttpRequest::Pointer request; ///< to-server request that initiated serverConnection
171     };
172 
173     /// Called when a pinned connection becomes available for forwarding the next request.
174     void notePinnedConnectionBecameIdle(PinnedIdleContext pic);
175     /// Forward future client requests using the given to-server connection.
176     /// The connection is still being used by the current client request.
177     void pinBusyConnection(const Comm::ConnectionPointer &pinServerConn, const HttpRequest::Pointer &request);
178     /// Undo pinConnection() and, optionally, close the pinned connection.
179     void unpinConnection(const bool andClose);
180     /// Returns validated pinnned server connection (and stops its monitoring).
181     Comm::ConnectionPointer borrowPinnedConnection(HttpRequest *request, const CachePeer *aPeer);
182     /**
183      * Checks if there is pinning info if it is valid. It can close the server side connection
184      * if pinned info is not valid.
185      \param request   if it is not NULL also checks if the pinning info refers to the request client side HttpRequest
186      \param CachePeer      if it is not NULL also check if the CachePeer is the pinning CachePeer
187      \return          The details of the server side connection (may be closed if failures were present).
188      */
189     const Comm::ConnectionPointer validatePinnedConnection(HttpRequest *request, const CachePeer *peer);
190     /**
191      * returts the pinned CachePeer if exists, NULL otherwise
192      */
pinnedPeer()193     CachePeer *pinnedPeer() const {return pinning.peer;}
pinnedAuth()194     bool pinnedAuth() const {return pinning.auth;}
195 
196     /// called just before a FwdState-dispatched job starts using connection
notePeerConnection(Comm::ConnectionPointer)197     virtual void notePeerConnection(Comm::ConnectionPointer) {}
198 
199     // pining related comm callbacks
200     virtual void clientPinnedConnectionClosed(const CommCloseCbParams &io);
201 
202     // comm callbacks
203     void clientReadFtpData(const CommIoCbParams &io);
204     void connStateClosed(const CommCloseCbParams &io);
205     void requestTimeout(const CommTimeoutCbParams &params);
206 
207     // AsyncJob API
208     virtual void start();
doneAll()209     virtual bool doneAll() const { return BodyProducer::doneAll() && false;}
210     virtual void swanSong();
211 
212     /// Changes state so that we close the connection and quit after serving
213     /// the client-side-detected error response instead of getting stuck.
214     void quitAfterError(HttpRequest *request); // meant to be private
215 
216     /// The caller assumes responsibility for connection closure detection.
217     void stopPinnedConnectionMonitoring();
218 
219     /// the second part of old httpsAccept, waiting for future HttpsServer home
220     void postHttpsAccept();
221 
222 #if USE_OPENSSL
223     /// Initializes and starts a peek-and-splice negotiation with the SSL client
224     void startPeekAndSplice();
225 
226     /// Called when a peek-and-splice step finished. For example after
227     /// server SSL certificates received and fake server SSL certificates
228     /// generated
229     void doPeekAndSpliceStep();
230     /// called by FwdState when it is done bumping the server
231     void httpsPeeked(PinnedIdleContext pic);
232 
233     /// Splice a bumped client connection on peek-and-splice mode
234     bool splice();
235 
236     /// Start to create dynamic Security::ContextPointer for host or uses static port SSL context.
237     void getSslContextStart();
238 
239     /// finish configuring the newly created SSL context"
240     void getSslContextDone(Security::ContextPointer &);
241 
242     /// Callback function. It is called when squid receive message from ssl_crtd.
243     static void sslCrtdHandleReplyWrapper(void *data, const Helper::Reply &reply);
244     /// Proccess response from ssl_crtd.
245     void sslCrtdHandleReply(const Helper::Reply &reply);
246 
247     void switchToHttps(HttpRequest *request, Ssl::BumpMode bumpServerMode);
248     void parseTlsHandshake();
switchedToHttps()249     bool switchedToHttps() const { return switchedToHttps_; }
serverBump()250     Ssl::ServerBump *serverBump() {return sslServerBump;}
setServerBump(Ssl::ServerBump * srvBump)251     inline void setServerBump(Ssl::ServerBump *srvBump) {
252         if (!sslServerBump)
253             sslServerBump = srvBump;
254         else
255             assert(sslServerBump == srvBump);
256     }
sslCommonName()257     const SBuf &sslCommonName() const {return sslCommonName_;}
resetSslCommonName(const char * name)258     void resetSslCommonName(const char *name) {sslCommonName_ = name;}
tlsClientSni()259     const SBuf &tlsClientSni() const { return tlsClientSni_; }
260     /// Fill the certAdaptParams with the required data for certificate adaptation
261     /// and create the key for storing/retrieve the certificate to/from the cache
262     void buildSslCertGenerationParams(Ssl::CertificateProperties &certProperties);
263     /// Called when the client sends the first request on a bumped connection.
264     /// Returns false if no [delayed] error should be written to the client.
265     /// Otherwise, writes the error to the client and returns true. Also checks
266     /// for SQUID_X509_V_ERR_DOMAIN_MISMATCH on bumped requests.
267     bool serveDelayedError(Http::Stream *);
268 
269     Ssl::BumpMode sslBumpMode; ///< ssl_bump decision (Ssl::bumpEnd if n/a).
270 
271     /// Tls parser to use for client HELLO messages parsing on bumped
272     /// connections.
273     Security::HandshakeParser tlsParser;
274 #else
switchedToHttps()275     bool switchedToHttps() const { return false; }
276 #endif
277     char *prepareTlsSwitchingURL(const Http1::RequestParserPointer &hp);
278 
279     /* clt_conn_tag=tag annotation access */
connectionTag()280     const SBuf &connectionTag() const { return connectionTag_; }
connectionTag(const char * aTag)281     void connectionTag(const char *aTag) { connectionTag_ = aTag; }
282 
283     /// handle a control message received by context from a peer and call back
284     virtual bool writeControlMsgAndCall(HttpReply *rep, AsyncCall::Pointer &call) = 0;
285 
286     /// ClientStream calls this to supply response header (once) and data
287     /// for the current Http::Stream.
288     virtual void handleReply(HttpReply *header, StoreIOBuffer receivedData) = 0;
289 
290     /// remove no longer needed leading bytes from the input buffer
291     void consumeInput(const size_t byteCount);
292 
293     /* TODO: Make the methods below (at least) non-public when possible. */
294 
295     /// stop parsing the request and create context for relaying error info
296     Http::Stream *abortRequestParsing(const char *const errUri);
297 
298     /// generate a fake CONNECT request with the given payload
299     /// at the beginning of the client I/O buffer
300     bool fakeAConnectRequest(const char *reason, const SBuf &payload);
301 
302     /// generates and sends to tunnel.cc a fake request with a given payload
303     bool initiateTunneledRequest(HttpRequest::Pointer const &cause, Http::MethodType const method, const char *reason, const SBuf &payload);
304 
305     /// whether we should start saving inBuf client bytes in anticipation of
306     /// tunneling them to the server later (on_unsupported_protocol)
307     bool shouldPreserveClientData() const;
308 
309     // TODO: move to the protected section when removing clientTunnelOnError()
310     bool tunnelOnError(const HttpRequestMethod &, const err_type);
311 
312     /// build a fake http request
313     ClientHttpRequest *buildFakeRequest(Http::MethodType const method, SBuf &useHost, unsigned short usePort, const SBuf &payload);
314 
315     /// From-client handshake bytes (including bytes at the beginning of a
316     /// CONNECT tunnel) which we may need to forward as-is if their syntax does
317     /// not match the expected TLS or HTTP protocol (on_unsupported_protocol).
318     SBuf preservedClientData;
319 
320     /* Registered Runner API */
321     virtual void startShutdown();
322     virtual void endingShutdown();
323 
324 protected:
325     void startDechunkingRequest();
326     void finishDechunkingRequest(bool withSuccess);
327     void abortChunkedRequestBody(const err_type error);
328     err_type handleChunkedRequestBody();
329 
330     void startPinnedConnectionMonitoring();
331     void clientPinnedConnectionRead(const CommIoCbParams &io);
332 #if USE_OPENSSL
333     /// Handles a ready-for-reading TLS squid-to-server connection that
334     /// we thought was idle.
335     /// \return false if and only if the connection should be closed.
336     bool handleIdleClientPinnedTlsRead();
337 #endif
338 
339     /// Parse an HTTP request
340     /// \note Sets result->flags.parsed_ok to 0 if failed to parse the request,
341     ///       to 1 if the request was correctly parsed
342     /// \param[in] hp an Http1::RequestParser
343     /// \return NULL on incomplete requests,
344     ///         a Http::Stream on success or failure.
345     /// TODO: Move to HttpServer. Warning: Move requires large code nonchanges!
346     Http::Stream *parseHttpRequest(const Http1::RequestParserPointer &);
347 
348     /// parse input buffer prefix into a single transfer protocol request
349     /// return NULL to request more header bytes (after checking any limits)
350     /// use abortRequestParsing() to handle parsing errors w/o creating request
351     virtual Http::Stream *parseOneRequest() = 0;
352 
353     /// start processing a freshly parsed request
354     virtual void processParsedRequest(Http::StreamPointer &) = 0;
355 
356     /// returning N allows a pipeline of 1+N requests (see pipeline_prefetch)
357     virtual int pipelinePrefetchMax() const;
358 
359     /// timeout to use when waiting for the next request
360     virtual time_t idleTimeout() const = 0;
361 
362     /// Perform client data lookups that depend on client src-IP.
363     /// The PROXY protocol may require some data input first.
364     void whenClientIpKnown();
365 
366     BodyPipe::Pointer bodyPipe; ///< set when we are reading request body
367 
368     /// whether preservedClientData is valid and should be kept up to date
369     bool preservingClientData_;
370 
371 private:
372     /* ::Server API */
373     virtual bool connFinishedWithConn(int size);
374     virtual void checkLogging();
375 
376     void clientAfterReadingRequests();
377     bool concurrentRequestQueueFilled() const;
378 
379     void pinConnection(const Comm::ConnectionPointer &pinServerConn, const HttpRequest &request);
380 
381     /* PROXY protocol functionality */
382     bool proxyProtocolValidateClient();
383     bool parseProxyProtocolHeader();
384     bool parseProxy1p0();
385     bool parseProxy2p0();
386     bool proxyProtocolError(const char *reason);
387 
388 #if USE_OPENSSL
389     /// \returns a pointer to the matching cached TLS context or nil
390     Security::ContextPointer getTlsContextFromCache(const SBuf &cacheKey, const Ssl::CertificateProperties &certProperties);
391 
392     /// Attempts to add a given TLS context to the cache, replacing the old
393     /// same-key context, if any
394     void storeTlsContextToCache(const SBuf &cacheKey, Security::ContextPointer &ctx);
395 #endif
396 
397     /// whether PROXY protocol header is still expected
398     bool needProxyProtocolHeader_;
399 
400 #if USE_AUTH
401     /// some user details that can be used to perform authentication on this connection
402     Auth::UserRequest::Pointer auth_;
403 #endif
404 
405 #if USE_OPENSSL
406     bool switchedToHttps_;
407     bool parsingTlsHandshake; ///< whether we are getting/parsing TLS Hello bytes
408     /// The number of parsed HTTP requests headers on a bumped client connection
409     uint64_t parsedBumpedRequestCount;
410 
411     /// The TLS server host name appears in CONNECT request or the server ip address for the intercepted requests
412     SBuf tlsConnectHostOrIp; ///< The TLS server host name as passed in the CONNECT request
413     unsigned short tlsConnectPort; ///< The TLS server port number as passed in the CONNECT request
414     SBuf sslCommonName_; ///< CN name for SSL certificate generation
415 
416     /// TLS client delivered SNI value. Empty string if none has been received.
417     SBuf tlsClientSni_;
418     SBuf sslBumpCertKey; ///< Key to use to store/retrieve generated certificate
419 
420     /// HTTPS server cert. fetching state for bump-ssl-server-first
421     Ssl::ServerBump *sslServerBump;
422     Ssl::CertSignAlgorithm signAlgorithm; ///< The signing algorithm to use
423 #endif
424 
425     /// the reason why we no longer write the response or nil
426     const char *stoppedSending_;
427     /// the reason why we no longer read the request or nil
428     const char *stoppedReceiving_;
429 
430     SBuf connectionTag_; ///< clt_conn_tag=Tag annotation for client connection
431 };
432 
433 const char *findTrailingHTTPVersion(const char *uriAndHTTPVersion, const char *end = NULL);
434 
435 int varyEvaluateMatch(StoreEntry * entry, HttpRequest * req);
436 
437 /// accept requests to a given port and inform subCall about them
438 void clientStartListeningOn(AnyP::PortCfgPointer &port, const RefCount< CommCbFunPtrCallT<CommAcceptCbPtrFun> > &subCall, const Ipc::FdNoteId noteId);
439 
440 void clientOpenListenSockets(void);
441 void clientConnectionsClose(void);
442 void httpRequestFree(void *);
443 
444 /// decide whether to expect multiple requests on the corresponding connection
445 void clientSetKeepaliveFlag(ClientHttpRequest *http);
446 
447 /// append a "part" HTTP header (as in a multi-part/range reply) to the buffer
448 void clientPackRangeHdr(const HttpReply *, const HttpHdrRangeSpec *, String boundary, MemBuf *);
449 
450 /// put terminating boundary for multiparts to the buffer
451 void clientPackTermBound(String boundary, MemBuf *);
452 
453 /* misplaced declaratrions of Stream callbacks provided/used by client side */
454 SQUIDCEXTERN CSR clientGetMoreData;
455 SQUIDCEXTERN CSS clientReplyStatus;
456 SQUIDCEXTERN CSD clientReplyDetach;
457 CSCB clientSocketRecipient;
458 CSD clientSocketDetach;
459 
460 void clientProcessRequest(ConnStateData *, const Http1::RequestParserPointer &, Http::Stream *);
461 void clientPostHttpsAccept(ConnStateData *);
462 
463 std::ostream &operator <<(std::ostream &os, const ConnStateData::PinnedIdleContext &pic);
464 
465 #endif /* SQUID_CLIENTSIDE_H */
466 
467