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 ¶ms); 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