the following organizations have provided non-financial support for
the Squid Project:
+@Squid-3.3:
+Netbox Blue Pty (http://netboxblue.com/)
+
+ Netbox Blue Pty. contributed development resources towards
+ testing and stabilizing of authentication systems in Squid-3.2
+ and Squid-3.3.
+
@Squid-3.2:
iiNet Ltd - http://www.iinet.net.au/
cache_cf.h \
YesNoNone.h \
tests/stub_cache_cf.cc \
+ tests/stub_client_side.cc \
tests/stub_debug.cc \
tests/stub_DelayId.cc \
tests/stub_DiskIOModule.cc \
return ACCESS_DENIED;
} else if (request->flags.sslBumped) {
debugs(28, 5, "SslBumped request: It is an encapsulated request do not authenticate");
- checklist->auth_user_request = checklist->conn() != NULL ? checklist->conn()->auth_user_request : request->auth_user_request;
+ checklist->auth_user_request = checklist->conn() != NULL ? checklist->conn()->getAuth() : request->auth_user_request;
if (checklist->auth_user_request != NULL)
return ACCESS_ALLOWED;
else
checklist->auth_user_request = NULL;
if (checklist->conn() != NULL) {
- checklist->conn()->auth_user_request = NULL;
+ checklist->conn()->setAuth(NULL, "proxy_auth ACL failure");
}
}
{}
void
-Auth::UserRequest::onConnectionClose(ConnStateData *)
+Auth::UserRequest::releaseAuthServer()
{}
const char *
else if (request != NULL && request->auth_user_request != NULL)
return request->auth_user_request;
else if (conn != NULL)
- return conn->auth_user_request;
+ return conn->getAuth();
else
return NULL;
}
/* connection auth we must reset on auth errors */
if (conn != NULL) {
- conn->auth_user_request = NULL;
+ conn->setAuth(NULL, "HTTP request missing credentials");
}
*auth_user_request = NULL;
* No check for function required in the if: its compulsory for conn based
* auth modules
*/
- if (proxy_auth && conn != NULL && conn->auth_user_request != NULL &&
- authenticateUserAuthenticated(conn->auth_user_request) &&
- conn->auth_user_request->connLastHeader() != NULL &&
- strcmp(proxy_auth, conn->auth_user_request->connLastHeader())) {
+ if (proxy_auth && conn != NULL && conn->getAuth() != NULL &&
+ authenticateUserAuthenticated(conn->getAuth()) &&
+ conn->getAuth()->connLastHeader() != NULL &&
+ strcmp(proxy_auth, conn->getAuth()->connLastHeader())) {
debugs(29, 2, "WARNING: DUPLICATE AUTH - authentication header on already authenticated connection!. AU " <<
- conn->auth_user_request << ", Current user '" <<
- conn->auth_user_request->username() << "' proxy_auth " <<
+ conn->getAuth() << ", Current user '" <<
+ conn->getAuth()->username() << "' proxy_auth " <<
proxy_auth);
/* remove this request struct - the link is already authed and it can't be to reauth. */
* authenticateAuthenticate
*/
assert(*auth_user_request == NULL);
- conn->auth_user_request = NULL;
+ conn->setAuth(NULL, "changed credentials token");
}
/* we have a proxy auth header and as far as we know this connection has
debugs(29, 9, HERE << "This is a new checklist test on:" << conn->clientConnection);
}
- if (proxy_auth && request->auth_user_request == NULL && conn != NULL && conn->auth_user_request != NULL) {
+ if (proxy_auth && request->auth_user_request == NULL && conn != NULL && conn->getAuth() != NULL) {
Auth::Config * scheme = Auth::Config::Find(proxy_auth);
- if (conn->auth_user_request->user() == NULL || conn->auth_user_request->user()->config != scheme) {
+ if (conn->getAuth()->user() == NULL || conn->getAuth()->user()->config != scheme) {
debugs(29, DBG_IMPORTANT, "WARNING: Unexpected change of authentication scheme from '" <<
- conn->auth_user_request->user()->config->type() <<
+ (conn->getAuth()->user()!=NULL?conn->getAuth()->user()->config->type():"[no user]") <<
"' to '" << proxy_auth << "' (client " <<
src_addr << ")");
- conn->auth_user_request = NULL;
+ conn->setAuth(NULL, "changed auth scheme");
}
}
- if (request->auth_user_request == NULL && (conn == NULL || conn->auth_user_request == NULL)) {
+ if (request->auth_user_request == NULL && (conn == NULL || conn->getAuth() == NULL)) {
/* beginning of a new request check */
debugs(29, 4, HERE << "No connection authentication type");
*auth_user_request = request->auth_user_request;
} else {
assert (conn != NULL);
- if (conn->auth_user_request != NULL) {
- *auth_user_request = conn->auth_user_request;
+ if (conn->getAuth() != NULL) {
+ *auth_user_request = conn->getAuth();
} else {
/* failed connection based authentication */
- debugs(29, 4, HERE << "Auth user request " <<
- *auth_user_request << " conn-auth user request " <<
- conn->auth_user_request << " conn type " <<
- conn->auth_user_request->user()->auth_type << " authentication failed.");
-
+ debugs(29, 4, HERE << "Auth user request " << *auth_user_request << " conn-auth missing and failed to authenticate.");
*auth_user_request = NULL;
return AUTH_ACL_CHALLENGE;
}
/* add the [Proxy-]Authentication-Info trailer */
virtual void addAuthenticationInfoTrailer(HttpReply * rep, int accel);
- virtual void onConnectionClose(ConnStateData *);
+ virtual void releaseAuthServer();
/**
* Called when squid is ready to put the request on hold and wait for a callback from the auth module
debugs(29, 6, HERE << "No Negotiate auth server to release.");
}
-/* clear any connection related authentication details */
-void
-Auth::Negotiate::UserRequest::onConnectionClose(ConnStateData *conn)
-{
- assert(conn != NULL);
-
- debugs(29, 8, HERE << "closing connection '" << conn << "' (this is '" << this << "')");
-
- if (conn->auth_user_request == NULL) {
- debugs(29, 8, HERE << "no auth_user_request");
- return;
- }
-
- releaseAuthServer();
-
- /* unlock the connection based lock */
- debugs(29, 9, HERE << "Unlocking auth_user from the connection '" << conn << "'.");
-
- conn->auth_user_request = NULL;
-}
-
void
Auth::Negotiate::UserRequest::authenticate(HttpRequest * aRequest, ConnStateData * conn, http_hdr_type type)
{
user()->credentials(Auth::Pending);
safe_free(client_blob);
client_blob=xstrdup(blob);
- assert(conn->auth_user_request == NULL);
- conn->auth_user_request = this;
+ assert(conn->getAuth() == NULL);
+ conn->setAuth(this, "new Negotiate handshake request");
request = aRequest;
HTTPMSGLOCK(request);
break;
virtual int authenticated() const;
virtual void authenticate(HttpRequest * request, ConnStateData * conn, http_hdr_type type);
virtual Direction module_direction();
- virtual void onConnectionClose(ConnStateData *);
virtual void module_start(AUTHCB *, void *);
virtual void addAuthenticationInfoHeader(HttpReply * rep, int accel);
debugs(29, 6, HERE << "No NTLM auth server to release.");
}
-void
-Auth::Ntlm::UserRequest::onConnectionClose(ConnStateData *conn)
-{
- assert(conn != NULL);
-
- debugs(29, 8, HERE << "closing connection '" << conn << "' (this is '" << this << "')");
-
- if (conn->auth_user_request == NULL) {
- debugs(29, 8, HERE << "no auth_user_request");
- return;
- }
-
- releaseAuthServer();
-
- /* unlock the connection based lock */
- debugs(29, 9, HERE << "Unlocking auth_user from the connection '" << conn << "'.");
-
- conn->auth_user_request = NULL;
-}
-
void
Auth::Ntlm::UserRequest::authenticate(HttpRequest * aRequest, ConnStateData * conn, http_hdr_type type)
{
user()->credentials(Auth::Pending);
safe_free(client_blob);
client_blob=xstrdup(blob);
- assert(conn->auth_user_request == NULL);
- conn->auth_user_request = this;
+ assert(conn->getAuth() == NULL);
+ conn->setAuth(this, "new NTLM handshake request");
request = aRequest;
HTTPMSGLOCK(request);
break;
virtual int authenticated() const;
virtual void authenticate(HttpRequest * request, ConnStateData * conn, http_hdr_type type);
virtual Auth::Direction module_direction();
- virtual void onConnectionClose(ConnStateData *);
virtual void module_start(AUTHCB *, void *);
virtual const char * connLastHeader();
/* we need to store the helper server between requests */
helper_stateful_server *authserver;
- void releaseAuthServer(void); ///< Release authserver NTLM helpers properly when finished or abandoning.
+ virtual void releaseAuthServer(); ///< Release authserver NTLM helpers properly when finished or abandoning.
/* our current blob to pass to the client */
char *server_blob;
deleteThis("ConnStateData::connStateClosed");
}
+#if USE_AUTH
+void
+ConnStateData::setAuth(const Auth::UserRequest::Pointer &aur, const char *by)
+{
+ if (auth_ == NULL) {
+ if (aur != NULL) {
+ debugs(33, 2, "Adding connection-auth to " << clientConnection << " from " << by);
+ auth_ = aur;
+ }
+ return;
+ }
+
+ // clobered with self-pointer
+ // NP: something nasty is going on in Squid, but harmless.
+ if (aur == auth_) {
+ debugs(33, 2, "WARNING: Ignoring duplicate connection-auth for " << clientConnection << " from " << by);
+ return;
+ }
+
+ /*
+ * Connection-auth relies on a single set of credentials being preserved
+ * for all requests on a connection once they have been setup.
+ * There are several things which need to happen to preserve security
+ * when connection-auth credentials change unexpectedly or are unset.
+ *
+ * 1) auth helper released from any active state
+ *
+ * They can only be reserved by a handshake process which this
+ * connection can now never complete.
+ * This prevents helpers hanging when their connections close.
+ *
+ * 2) pinning is expected to be removed and server conn closed
+ *
+ * The upstream link is authenticated with the same credentials.
+ * Expecting the same level of consistency we should have received.
+ * This prevents upstream being faced with multiple or missing
+ * credentials after authentication.
+ * NP: un-pin is left to the cleanup in ConnStateData::swanSong()
+ * we just trigger that cleanup here via comm_reset_close() or
+ * ConnStateData::stopReceiving()
+ *
+ * 3) the connection needs to close.
+ *
+ * This prevents attackers injecting requests into a connection,
+ * or gateways wrongly multiplexing users into a single connection.
+ *
+ * When credentials are missing closure needs to follow an auth
+ * challenge for best recovery by the client.
+ *
+ * When credentials change there is nothing we can do but abort as
+ * fast as possible. Sending TCP RST instead of an HTTP response
+ * is the best-case action.
+ */
+
+ // clobbered with nul-pointer
+ if (aur == NULL) {
+ debugs(33, 2, "WARNING: Graceful closure on " << clientConnection << " due to connection-auth erase from " << by);
+ auth_->releaseAuthServer();
+ auth_ = NULL;
+ // XXX: need to test whether the connection re-auth challenge is sent. If not, how to trigger it from here.
+ // NP: the current situation seems to fix challenge loops in Safari without visible issues in others.
+ // we stop receiving more traffic but can leave the Job running to terminate after the error or challenge is delivered.
+ stopReceiving("connection-auth removed");
+ return;
+ }
+
+ // clobbered with alternative credentials
+ if (aur != auth_) {
+ debugs(33, 2, "ERROR: Closing " << clientConnection << " due to change of connection-auth from " << by);
+ auth_->releaseAuthServer();
+ auth_ = NULL;
+ // this is a fatal type of problem.
+ // Close the connection immediately with TCP RST to abort all traffic flow
+ comm_reset_close(clientConnection);
+ return;
+ }
+
+ /* NOT REACHABLE */
+}
+#endif
+
// cleans up before destructor is called
void
ConnStateData::swanSong()
clientdbEstablished(clientConnection->remote, -1); /* decrement */
assert(areAllContextsForThisConnection());
freeAllContexts();
-#if USE_AUTH
- if (auth_user_request != NULL) {
- debugs(33, 4, "ConnStateData::swanSong: freeing auth_user_request '" << auth_user_request << "' (this is '" << this << "')");
- auth_user_request->onConnectionClose(this);
- }
-#endif
unpinConnection();
clientConnection->close();
clientConnection = NULL;
+#if USE_AUTH
+ // NP: do this bit after closing the connections to avoid side effects from unwanted TCP RST
+ setAuth(NULL, "ConnStateData::SwanSong cleanup");
+#endif
+
BodyProducer::swanSong();
flags.swanSang = true;
}
!conn->port->allow_direct : 0;
#if USE_AUTH
if (request->flags.sslBumped) {
- if (conn->auth_user_request != NULL)
- request->auth_user_request = conn->auth_user_request;
+ if (conn->getAuth() != NULL)
+ request->auth_user_request = conn->getAuth();
}
#endif
#if USE_AUTH
/**
- * note this is ONLY connection based because NTLM and Negotiate is against HTTP spec.
- * the user details for connection based authentication
+ * Fetch the user details for connection based authentication
+ * NOTE: this is ONLY connection based because NTLM and Negotiate is against HTTP spec.
*/
- Auth::UserRequest::Pointer auth_user_request;
+ const Auth::UserRequest::Pointer &getAuth() const { return auth_; }
+
+ /**
+ * Set the user details for connection-based authentication to use from now until connection closure.
+ *
+ * Any change to existing credentials shows that something invalid has happened. Such as:
+ * - NTLM/Negotiate auth was violated by the per-request headers missing a revalidation token
+ * - NTLM/Negotiate auth was violated by the per-request headers being for another user
+ * - SSL-Bump CONNECT tunnel with persistent credentials has ended
+ */
+ void setAuth(const Auth::UserRequest::Pointer &aur, const char *cause);
#endif
/**
int connFinishedWithConn(int size);
void clientAfterReadingRequests();
-private:
+#if USE_AUTH
+ /// some user details that can be used to perform authentication on this connection
+ Auth::UserRequest::Pointer auth_;
+#endif
+
HttpParser parser_;
// XXX: CBDATA plays with public/private and leaves the following 'private' fields all public... :(
http->request,
NULL,
#if USE_AUTH
- http->getConn() != NULL && http->getConn()->auth_user_request != NULL ?
- http->getConn()->auth_user_request : http->request->auth_user_request);
+ http->getConn() != NULL && http->getConn()->getAuth() != NULL ?
+ http->getConn()->getAuth() : http->request->auth_user_request);
#else
NULL);
#endif
#if USE_AUTH
char const *proxy_auth_msg = "<null>";
- if (http->getConn() != NULL && http->getConn()->auth_user_request != NULL)
- proxy_auth_msg = http->getConn()->auth_user_request->denyMessage("<null>");
+ if (http->getConn() != NULL && http->getConn()->getAuth() != NULL)
+ proxy_auth_msg = http->getConn()->getAuth()->denyMessage("<null>");
else if (http->request->auth_user_request != NULL)
proxy_auth_msg = http->request->auth_user_request->denyMessage("<null>");
#endif
#if USE_AUTH
error->auth_user_request =
- http->getConn() != NULL && http->getConn()->auth_user_request != NULL ?
- http->getConn()->auth_user_request : http->request->auth_user_request;
+ http->getConn() != NULL && http->getConn()->getAuth() != NULL ?
+ http->getConn()->getAuth() : http->request->auth_user_request;
#endif
readNextRequest = true;
#if USE_AUTH
// Preserve authentication info for the ssl-bumped request
if (request->auth_user_request != NULL)
- getConn()->auth_user_request = request->auth_user_request;
+ getConn()->setAuth(request->auth_user_request, "SSL-bumped CONNECT");
#endif
assert(sslBumpNeeded());
);
#if USE_AUTH
calloutContext->error->auth_user_request =
- c != NULL && c->auth_user_request != NULL ? c->auth_user_request : request->auth_user_request;
+ c != NULL && c->getAuth() != NULL ? c->getAuth() : request->auth_user_request;
#endif
calloutContext->error->detailError(errDetail);
calloutContext->readNextRequest = true;
http->request,
NULL,
#if USE_AUTH
- http->getConn() != NULL && http->getConn()->auth_user_request != NULL ?
- http->getConn()->auth_user_request : http->request->auth_user_request);
+ http->getConn() != NULL && http->getConn()->getAuth() != NULL ?
+ http->getConn()->getAuth() : http->request->auth_user_request);
#else
NULL);
#endif
tests/stub_cache_manager.cc \
tests/stub_cbdata.cc \
tests/stub_client_db.cc \
+ tests/stub_client_side.cc \
tests/stub_client_side_request.cc \
tests/stub_comm.cc \
tests/stub_debug.cc \
--- /dev/null
+#include "squid.h"
+#include "client_side.h"
+
+#define STUB_API "client_side.cc"
+#include "tests/STUB.h"
+
+ClientSocketContext::ClientSocketContext() STUB
+ClientSocketContext::~ClientSocketContext() STUB
+bool ClientSocketContext::startOfOutput() const STUB_RETVAL(false)
+void ClientSocketContext::writeComplete(const Comm::ConnectionPointer &conn, char *bufnotused, size_t size, comm_err_t errflag) STUB
+void ClientSocketContext::keepaliveNextRequest() STUB
+void ClientSocketContext::pullData() STUB
+int64_t ClientSocketContext::getNextRangeOffset() const STUB_RETVAL(0)
+bool ClientSocketContext::canPackMoreRanges() const STUB_RETVAL(false)
+clientStream_status_t ClientSocketContext::socketState() STUB_RETVAL(STREAM_NONE)
+void ClientSocketContext::sendBody(HttpReply * rep, StoreIOBuffer bodyData) STUB
+void ClientSocketContext::sendStartOfMessage(HttpReply * rep, StoreIOBuffer bodyData) STUB
+size_t ClientSocketContext::lengthToSend(Range<int64_t> const &available) STUB_RETVAL(0)
+void ClientSocketContext::noteSentBodyBytes(size_t) STUB
+void ClientSocketContext::buildRangeHeader(HttpReply * rep) STUB
+clientStreamNode * ClientSocketContext::getTail() const STUB_RETVAL(NULL)
+clientStreamNode * ClientSocketContext::getClientReplyContext() const STUB_RETVAL(NULL)
+void ClientSocketContext::connIsFinished() STUB
+void ClientSocketContext::removeFromConnectionList(ConnStateData * conn) STUB
+void ClientSocketContext::deferRecipientForLater(clientStreamNode * node, HttpReply * rep, StoreIOBuffer receivedData) STUB
+bool ClientSocketContext::multipartRangeRequest() const STUB_RETVAL(false)
+void ClientSocketContext::registerWithConn() STUB
+void ClientSocketContext::noteIoError(const int xerrno) STUB
+void ClientSocketContext::writeControlMsg(HttpControlMsg &msg) STUB
+
+void ConnStateData::readSomeData() STUB
+int ConnStateData::getAvailableBufferLength() const STUB_RETVAL(0)
+bool ConnStateData::areAllContextsForThisConnection() const STUB_RETVAL(false)
+void ConnStateData::freeAllContexts() STUB
+void ConnStateData::notifyAllContexts(const int xerrno) STUB
+bool ConnStateData::clientParseRequests() STUB_RETVAL(false)
+void ConnStateData::readNextRequest() STUB
+bool ConnStateData::maybeMakeSpaceAvailable() STUB_RETVAL(false)
+void ConnStateData::addContextToQueue(ClientSocketContext * context) STUB
+int ConnStateData::getConcurrentRequestCount() const STUB_RETVAL(0)
+bool ConnStateData::isOpen() const STUB_RETVAL(false)
+void ConnStateData::checkHeaderLimits() STUB
+void ConnStateData::sendControlMsg(HttpControlMsg msg) STUB
+char *ConnStateData::In::addressToReadInto() const STUB_RETVAL(NULL)
+int64_t ConnStateData::mayNeedToReadMoreBody() const STUB_RETVAL(0)
+#if USE_AUTH
+void ConnStateData::setAuth(const Auth::UserRequest::Pointer &aur, const char *cause) STUB
+#endif
+bool ConnStateData::transparent() const STUB_RETVAL(false)
+bool ConnStateData::reading() const STUB_RETVAL(false)
+void ConnStateData::stopReading() STUB
+void ConnStateData::stopReceiving(const char *error) STUB
+void ConnStateData::stopSending(const char *error) STUB
+void ConnStateData::expectNoForwarding() STUB
+void ConnStateData::noteMoreBodySpaceAvailable(BodyPipe::Pointer) STUB
+void ConnStateData::noteBodyConsumerAborted(BodyPipe::Pointer) STUB
+bool ConnStateData::handleReadData(char *buf, size_t size) STUB_RETVAL(false)
+bool ConnStateData::handleRequestBodyData() STUB_RETVAL(false)
+void ConnStateData::pinConnection(const Comm::ConnectionPointer &pinServerConn, HttpRequest *request, CachePeer *peer, bool auth) STUB
+void ConnStateData::unpinConnection() STUB
+const Comm::ConnectionPointer ConnStateData::validatePinnedConnection(HttpRequest *request, const CachePeer *peer) STUB_RETVAL(NULL)
+void ConnStateData::clientPinnedConnectionClosed(const CommCloseCbParams &io) STUB
+void ConnStateData::clientReadRequest(const CommIoCbParams &io) STUB
+void ConnStateData::connStateClosed(const CommCloseCbParams &io) STUB
+void ConnStateData::requestTimeout(const CommTimeoutCbParams ¶ms) STUB
+void ConnStateData::swanSong() STUB
+void ConnStateData::quitAfterError(HttpRequest *request) STUB
+#if USE_SSL
+void ConnStateData::httpsPeeked(Comm::ConnectionPointer serverConnection) STUB
+void ConnStateData::getSslContextStart() STUB
+void ConnStateData::getSslContextDone(SSL_CTX * sslContext, bool isNew) STUB
+void ConnStateData::sslCrtdHandleReplyWrapper(void *data, const HelperReply &reply) STUB
+void ConnStateData::sslCrtdHandleReply(const HelperReply &reply) STUB
+void ConnStateData::switchToHttps(HttpRequest *request, Ssl::BumpMode bumpServerMode) STUB
+void ConnStateData::buildSslCertGenerationParams(Ssl::CertificateProperties &certProperties) STUB
+bool ConnStateData::serveDelayedError(ClientSocketContext *context) STUB_RETVAL(false)
+#endif
+
+void setLogUri(ClientHttpRequest * http, char const *uri, bool cleanUrl) STUB
+const char *findTrailingHTTPVersion(const char *uriAndHTTPVersion, const char *end) STUB_RETVAL(NULL)
+int varyEvaluateMatch(StoreEntry * entry, HttpRequest * req) STUB_RETVAL(0)
+void clientOpenListenSockets(void) STUB
+void clientHttpConnectionsClose(void) STUB
+void httpRequestFree(void *) STUB