]> git.ipfire.org Git - thirdparty/squid.git/commitdiff
Forward a StoreEntry holding the bumped secure connection establishment
authorChristos Tsantilas <chtsanti@users.sourceforge.net>
Tue, 20 Dec 2011 15:35:27 +0000 (17:35 +0200)
committerChristos Tsantilas <chtsanti@users.sourceforge.net>
Tue, 20 Dec 2011 15:35:27 +0000 (17:35 +0200)
error page from the server-side code (which generates the error) to the
client-side code (which delays the error until the first encrypted
request comes). This allows Squid to display the error page to the user
(using secure connection) when bumping intercepted SSL connections. The
code still needs more polishing, including generating errors with host
names and not IP addresses (when possible).

The peeked server certificate is now stored in
ConnStateData::bumpErrorEntry. This allows us to mimic the certificate
of dropped server connections.

Load signing certificate and key when initializing a tproxy-enabled
https_port.

Fixed debugging when opening a peeking connection.

src/cache_cf.cc
src/client_side.cc
src/client_side.h
src/client_side_reply.cc
src/client_side_reply.h
src/forward.cc
src/ssl/ErrorDetail.h
src/ssl/ServerPeeker.cc
src/ssl/ServerPeeker.h

index ac9a8a7394cc2325bafd7b3dde7a2c492d461590..add7c03dd2f54d21c905d56548649c93806fadba 100644 (file)
@@ -928,6 +928,9 @@ configDoConfigure(void)
                                        s->version, s->cipher, s->options, s->sslflags, s->clientca,
                                        s->cafile, s->capath, s->crlfile, s->dhfile,
                                        s->sslContextSessionId));
+
+            if (s->cert && s->key && s->sslBump)
+                Ssl::readCertChainAndPrivateKeyFromFiles(s->signingCert, s->signPkey, s->certsToChain, s->cert, s->key);
         }
     }
 
index d1577c94c67b5dccd4bbeab54008ec233e71be5d..4dec7a811302360b5ed59e1c2e9fb18796aa4597 100644 (file)
@@ -810,6 +810,11 @@ ConnStateData::~ConnStateData()
 
     if (bodyPipe != NULL)
         stopProducingFor(bodyPipe, false);
+   
+#if USE_SSL 
+    if (bumpErrorEntry)
+        bumpErrorEntry->unlock();
+#endif
 }
 
 /**
@@ -2639,6 +2644,25 @@ clientProcessRequest(ConnStateData *conn, HttpParser *hp, ClientSocketContext *c
         conn->flags.readMore = false;
     }
 
+#if USE_SSL
+    if (conn->switchedToHttps() && conn->bumpServerFirstErrorEntry() &&
+        !Comm::IsConnOpen(conn->pinning.serverConnection)) {
+        //Failed? Here we should get the error from conn and send it to client
+        // The error stored in ConnStateData::bumpFirstEntry, replace the 
+        // ClientHttpRequest store entry with this.
+        clientStreamNode *node = context->getClientReplyContext();
+        clientReplyContext *repContext = dynamic_cast<clientReplyContext *>(node->data.getRaw());
+        assert (repContext);
+        debugs(33, 5, "Connection first has failed for " << http->uri << ". Respond with an error");
+        StoreEntry *e = conn->bumpServerFirstErrorEntry();
+        Must(e && !e->isEmpty());
+        repContext->setReplyToStoreEntry(e);
+        context->pullData();
+        conn->flags.readMore = false;
+        goto finish;
+    }
+#endif
+
     /* Do we expect a request-body? */
     expectBody = chunked || request->content_length > 0;
     if (!context->mayUseConnection() && expectBody) {
@@ -3580,12 +3604,6 @@ ConnStateData::getSslContextStart()
             debugs(33, 5, HERE << "SSL certificate for " << host << " haven't found in cache");
         }
 
-        Ssl::X509_Pointer serverCert;
-        if (Comm::IsConnOpen(pinning.serverConnection)) {
-            SSL *ssl = fd_table[pinning.serverConnection->fd].ssl;
-            serverCert.reset(SSL_get_peer_certificate(ssl));
-        }
-
 #if USE_SSL_CRTD
         debugs(33, 5, HERE << "Generating SSL certificate for " << host << " using ssl_crtd.");
         Ssl::CrtdMessage request_message;
@@ -3594,8 +3612,8 @@ ConnStateData::getSslContextStart()
         map.insert(std::make_pair(Ssl::CrtdMessage::param_host, host));
         std::string bufferToWrite;
         Ssl::writeCertAndPrivateKeyToMemory(port->signingCert, port->signPkey, bufferToWrite);
-        if (serverCert.get()) {
-            Ssl::appendCertToMemory(serverCert, bufferToWrite);
+        if (bumpServerCert.get()) {
+            Ssl::appendCertToMemory(bumpServerCert, bufferToWrite);
             debugs(33, 5, HERE << "Append Mimic Certificate to body request: " << bufferToWrite);
         }
         request_message.composeBody(map, bufferToWrite);
@@ -3603,7 +3621,7 @@ ConnStateData::getSslContextStart()
         return;
 #else
         debugs(33, 5, HERE << "Generating SSL certificate for " << host);
-        dynCtx = Ssl::generateSslContext(host, serverCert, port->signingCert, port->signPkey);
+        dynCtx = Ssl::generateSslContext(host, bumpServerCert, port->signingCert, port->signPkey);
         getSslContextDone(dynCtx, true);
         return;
 #endif //USE_SSL_CRTD
@@ -3675,9 +3693,12 @@ ConnStateData::switchToHttps(const char *host, const int port)
     const bool alwaysBumpServerFirst = true;
     if (alwaysBumpServerFirst) {
         Must(!httpsPeeker.set());
-        httpsPeeker = AsyncJob::Start(new Ssl::ServerPeeker(
-                                      this, sslHostName.termedBuf(), port));
+        httpsPeeker = new Ssl::ServerPeeker(this, sslHostName.termedBuf(), port);
+        bumpErrorEntry = httpsPeeker->storeEntry();
+        Must(bumpErrorEntry);
+        bumpErrorEntry->lock();
         // will call httpsPeeked() with certificate and connection, eventually
+        AsyncJob::Start(httpsPeeker.raw());
         return;
     }
 
@@ -3690,26 +3711,30 @@ ConnStateData::httpsPeeked(Comm::ConnectionPointer serverConnection)
 {
     Must(httpsPeeker.set());
 
-    /* XXX: handle httpsPeeker errors instead of asserting there are none */
-    assert(Comm::IsConnOpen(serverConnection));
-    SSL *ssl = fd_table[serverConnection->fd].ssl;
-    assert(ssl);
-    Ssl::X509_Pointer serverCert(SSL_get_peer_certificate(ssl));
-    assert(serverCert.get() != NULL);
-
-    char name[256] = ""; // stores common name (CN)
-    // TODO: What if CN is a UTF8String? See X509_NAME_get_index_by_NID(3ssl).
-    const int nameLen = X509_NAME_get_text_by_NID(
-        X509_get_subject_name(serverCert.get()),
-                              NID_commonName,  name, sizeof(name));
-    assert(0 < nameLen && nameLen < static_cast<int>(sizeof(name)));
-    debugs(33, 5, HERE << "found HTTPS server " << name << " at bumped " <<
-           *serverConnection);
-    sslHostName = name;
-
-    pinConnection(serverConnection, NULL, NULL, false);
-
-    debugs(33, 5, HERE << "bumped HTTPS server: " << sslHostName);
+    if (Comm::IsConnOpen(serverConnection)) {
+        SSL *ssl = fd_table[serverConnection->fd].ssl;
+        assert(ssl);
+        Ssl::X509_Pointer serverCert(SSL_get_peer_certificate(ssl));
+        assert(serverCert.get() != NULL);
+
+        char name[256] = ""; // stores common name (CN)
+        // TODO: What if CN is a UTF8String? See X509_NAME_get_index_by_NID(3ssl).
+        const int nameLen = X509_NAME_get_text_by_NID(
+            X509_get_subject_name(serverCert.get()),
+            NID_commonName,  name, sizeof(name));
+        assert(0 < nameLen && nameLen < static_cast<int>(sizeof(name)));
+        debugs(33, 5, HERE << "found HTTPS server " << name << " at bumped " <<
+               *serverConnection);
+        sslHostName = name;
+
+        pinConnection(serverConnection, NULL, NULL, false);
+
+        debugs(33, 5, HERE << "bumped HTTPS server: " << sslHostName);
+    } else
+        debugs(33, 5, HERE << "Error while bumped HTTPS server: " << sslHostName);
+
+    if (httpsPeeker.valid())
+        httpsPeeker->noteHttpsPeeked(serverConnection);
     httpsPeeker.clear();
     getSslContextStart();
 }
@@ -4004,8 +4029,10 @@ CBDATA_CLASS_INIT(ConnStateData);
 
 ConnStateData::ConnStateData() :
         AsyncJob("ConnStateData"),
-        closing_(false),
-        switchedToHttps_(false)
+        closing_(false)
+#if USE_SSL
+        ,switchedToHttps_(false)
+#endif
 {
     pinning.pinned = false;
     pinning.auth = false;
index 295bc4c9f8b374aa3158c685e315fd5dd3e70867..72c6207908fad723ed9a262344da6daf5ad82e94 100644 (file)
@@ -44,6 +44,9 @@
 #include "HttpParser.h"
 #include "RefCount.h"
 #include "StoreIOBuffer.h"
+#if USE_SSL
+#include "ssl/support.h"
+#endif
 
 class ConnStateData;
 class ClientHttpRequest;
@@ -160,7 +163,11 @@ private:
 
 
 class ConnectionDetail;
-
+#if USE_SSL
+namespace Ssl {
+    class ServerPeeker;
+}
+#endif
 /**
  * Manages a connection to a client.
  *
@@ -324,6 +331,9 @@ public:
 
     void switchToHttps(const char *host, const int port);
     bool switchedToHttps() const { return switchedToHttps_; }
+    /// Holds the squid error reply in the case of bump server first error 
+    StoreEntry *bumpServerFirstErrorEntry() const {return bumpErrorEntry;}
+    void setBumpServerCert(X509 *serverCert) {bumpServerCert.reset(serverCert);}
 #else
     bool switchedToHttps() const { return false; }
 #endif
@@ -351,7 +361,9 @@ private:
     String sslHostName; ///< Host name for SSL certificate generation
 
     /// a job that connects to the HTTPS server to get its SSL certificate
-    AsyncJob::Pointer httpsPeeker;
+    CbcPointer<Ssl::ServerPeeker> httpsPeeker;
+    StoreEntry *bumpErrorEntry;
+    Ssl::X509_Pointer bumpServerCert;
 #endif
 
     AsyncCall::Pointer reader; ///< set when we are reading
index 0014fa07cdcf06e6520338166b3d2cb7774b4749..0e3ff46bb9c8c17f4688767495d16d8a5553b437 100644 (file)
@@ -128,6 +128,18 @@ clientReplyContext::setReplyToError(
     /* Now the caller reads to get this */
 }
 
+void clientReplyContext::setReplyToStoreEntry(StoreEntry *entry)
+{
+    sc = storeClientListAdd(entry, this);
+#if USE_DELAY_POOLS
+    sc->setDelayId(DelayId::DelayClient(http));
+#endif
+    reqofs = 0;
+    reqsize = 0;
+    flags.storelogiccomplete = 1;
+    http->storeEntry(entry);
+}
+
 void
 clientReplyContext::removeStoreReference(store_client ** scp,
         StoreEntry ** ep)
index c7e2b34be2d1685a2f84cf10fedaceecb48035d8..e92f41f1832228c9da27d4ffc6e6e1b5bc8a9374 100644 (file)
@@ -71,6 +71,8 @@ public:
     void identifyFoundObject(StoreEntry *entry);
     int storeOKTransferDone() const;
     int storeNotOKTransferDone() const;
+    /// Replaces the store entry with the given and awaiting the client side to read it
+    void setReplyToStoreEntry(StoreEntry *entry);
     void setReplyToError(err_type, http_status, const HttpRequestMethod&, char const *, Ip::Address &, HttpRequest *, const char *,
 #if USE_AUTH
                          AuthUserRequest::Pointer);
index 449124a10e61df9b6b9e63c33b1753a0959224f2..23a21d6cc5af9d750eb0524330a656affdbcaaff 100644 (file)
@@ -59,6 +59,7 @@
 #if USE_SSL
 #include "ssl/support.h"
 #include "ssl/ErrorDetail.h"
+#include "ssl/ServerPeeker.h"
 #endif
 
 static PSC fwdPeerSelectionCompleteWrapper;
@@ -147,11 +148,11 @@ FwdState::selectPeerForIntercepted()
         p->peerType = PINNED;
         entry->ping_status = PING_DONE;     /* Skip ICP */
     } else {
-        debugs(17, 3, HERE << "opening a new conn: " << *p);
         p = new Comm::Connection();
         p->peerType = ORIGINAL_DST;
         p->remote = clientConn->local;
         getOutgoingAddress(request, p);
+        debugs(17, 3, HERE << "opening a new conn: " << *p);
     }
 
     serverDestinations.push_back(p);
@@ -336,6 +337,14 @@ FwdState::startConnectionOrFail()
             anErr->xerrno = errno;
             fail(anErr);
         } // else use actual error from last connection attempt
+#if USE_SSL
+        if (request->protocol == AnyP::PROTO_SSL_PEEK && request->clientConnectionManager.valid()) {
+            errorAppendEntry(entry, err); // will free err
+            err = NULL;
+            CallJobHere1(17, 4, request->clientConnectionManager, ConnStateData,
+                         ConnStateData::httpsPeeked, Comm::ConnectionPointer(NULL));
+        }
+#endif
         self = NULL;       // refcounted
     }
 }
@@ -646,16 +655,22 @@ FwdState::negotiateSSL(int fd)
                 // Copy errFromFailure to a new Ssl::ErrorDetail object
                 anErr->detail = new Ssl::ErrorDetail(*errFromFailure);
             } else {
-                // clientCert can be be NULL
-                X509 *client_cert = SSL_get_peer_certificate(ssl);
-                anErr->detail = new Ssl::ErrorDetail(SQUID_ERR_SSL_HANDSHAKE, client_cert);
-                if (client_cert)
-                    X509_free(client_cert);
+                // server_cert can be be NULL
+                X509 *server_cert = SSL_get_peer_certificate(ssl);
+                anErr->detail = new Ssl::ErrorDetail(SQUID_ERR_SSL_HANDSHAKE, server_cert);
+                X509_free(server_cert);
             }
 
             if (ssl_lib_error != SSL_ERROR_NONE)
                 anErr->detail->setLibError(ssl_lib_error);
 
+            if (request->clientConnectionManager.valid()) {
+                // Get the server certificate from ErrorDetail object and store it 
+                // to connection manager
+                X509 *x509 = anErr->detail->peerCert();
+                request->clientConnectionManager->setBumpServerCert(X509_dup(x509));
+            }
+
             fail(anErr);
 
             if (serverConnection()->getPeer()) {
@@ -666,6 +681,9 @@ FwdState::negotiateSSL(int fd)
             return;
         }
     }
+    
+    if (request->clientConnectionManager.valid())
+        request->clientConnectionManager->setBumpServerCert(SSL_get_peer_certificate(ssl));
 
     if (serverConnection()->getPeer() && !SSL_session_reused(ssl)) {
         if (serverConnection()->getPeer()->sslSession)
@@ -984,11 +1002,11 @@ FwdState::dispatch()
 #if USE_SSL
     if (request->protocol == AnyP::PROTO_SSL_PEEK) {
         CallJobHere1(17, 4, request->clientConnectionManager, ConnStateData,
-                    ConnStateData::httpsPeeked, serverConnection());
+                     ConnStateData::httpsPeeked, serverConnection());
         unregister(serverConn); // async call owns it now
         complete(); // destroys us
         return;
-       }
+    }
 #endif
 
     if (serverConnection()->getPeer() != NULL) {
index 2e2896263648368eb579dc49cdc9e1751cd31bae..8938551d80a800eb776eda9172c4d2b6f6a79bed 100644 (file)
@@ -56,7 +56,8 @@ public:
     ssl_error_t errorNo() const {return error_no;}
     ///Sets the low-level error returned by OpenSSL ERR_get_error()
     void setLibError(unsigned long lib_err_no) {lib_error_no = lib_err_no;}
-
+    ///The peer certificate
+    X509 *peerCert() { return peer_cert.get(); }
 private:
     typedef const char * (ErrorDetail::*fmt_action_t)() const;
     /**
index 21c04c2d361e47e8f261cf2babcfaa64437749ed..70036544059e627ca88e7a1bb788ce8be53d0b8b 100644 (file)
@@ -21,8 +21,7 @@ Ssl::ServerPeeker::ServerPeeker(ConnStateData *anInitiator,
     AsyncJob("Ssl::ServerPeeker"),
     initiator(anInitiator),
     clientConnection(anInitiator->clientConnection),
-    request(new HttpRequest),
-    entry(NULL)
+    request(new HttpRequest)
 {
     debugs(33, 4, HERE << "will peek at " << host << ':' << port);
 
@@ -30,20 +29,25 @@ Ssl::ServerPeeker::ServerPeeker(ConnStateData *anInitiator,
     request->port = port;
     request->protocol = AnyP::PROTO_SSL_PEEK;
     request->clientConnectionManager = initiator;
+    const char *uri = urlCanonical(request);
+    entry = storeCreateEntry(uri, uri, request->flags, request->method);
+}
+
+Ssl::ServerPeeker::~ServerPeeker()
+{
+    if (entry)
+        entry->unlock();
 }
 
 void
 Ssl::ServerPeeker::start()
 {
-    const char *uri = urlCanonical(request);
-    entry = storeCreateEntry(uri, uri, request->flags, request->method);
-
     FwdState::fwdStart(clientConnection, entry, request);
+}
 
-    // XXX: wait for FwdState to tell us the connection is ready
-
-    // TODO: send our answer to the initiator
-    // CallJobHere(33, 4, initiator, ConnStateData, ConnStateData::httpsPeeked);
+void Ssl::ServerPeeker::noteHttpsPeeked(Comm::ConnectionPointer &serverConnection)
+{
+    assert(initiator.raw());
     initiator.clear(); // will trigger the end of the job
 }
 
index e0e4620f9d27e3b7c1f09a329dd39d23eee5e501..1e2849972ae0eab32285e9f95a2254884ad3260f 100644 (file)
@@ -32,10 +32,12 @@ public:
     explicit ServerPeeker(ConnStateData *anInitiator, const char *host, const int port);
 
     /* AsyncJob API */
-    //virtual ~ServerPeeker();
+    virtual ~ServerPeeker();
     virtual void start();
     virtual bool doneAll() const;
     virtual void swanSong();
+    StoreEntry *storeEntry() {return entry;}
+    void noteHttpsPeeked(Comm::ConnectionPointer &serverConnection);
 
 private:
     /// connection manager waiting for peeked server info