Title: [220583] trunk
Revision
220583
Author
carlo...@webkit.org
Date
2017-08-11 03:06:12 -0700 (Fri, 11 Aug 2017)

Log Message

[Soup] Cannot access HTTPS sites using a HTTP proxy that requires authentication
https://bugs.webkit.org/show_bug.cgi?id=175378

Reviewed by Sergio Villar Senin.

Source/WebCore:

Bring back part of the code removed in r206732, to keep a reference to the SoupMessage in the
AuthenticationChallenge since it can be different to the resource message.

* platform/network/soup/AuthenticationChallenge.h:
(WebCore::AuthenticationChallenge::AuthenticationChallenge): Deleted.
(WebCore::AuthenticationChallenge::authenticationClient const): Deleted.
(WebCore::AuthenticationChallenge::soupAuth const): Deleted.
(WebCore::AuthenticationChallenge::setProposedCredential): Deleted.
* platform/network/soup/AuthenticationChallengeSoup.cpp:
(WebCore::AuthenticationChallenge::AuthenticationChallenge):
(WebCore::AuthenticationChallenge::platformCompare):

Source/WebKit:

In case of HTTPS resource with a proxy, libsoup uses a tunnel internally, that uses its own SoupMessage during
the proxy authentication. We were ignoring authentication requests for other messages.

* NetworkProcess/soup/NetworkDataTaskSoup.cpp:
(WebKit::NetworkDataTaskSoup::authenticateCallback): Only return early if the message does't match and it's not
HTTPS resource over a proxy.
(WebKit::NetworkDataTaskSoup::authenticate): Use the soup message from the authentication challenge.
(WebKit::NetworkDataTaskSoup::continueAuthenticate): Ditto.

Tools:

Add two test cases to check proxy authentication.

* TestWebKitAPI/Tests/WebKitGLib/TestAuthentication.cpp:
(Tunnel::Tunnel):
(Tunnel::~Tunnel):
(Tunnel::connect):
(Tunnel::connected):
(serverCallback):
(ProxyAuthenticationTest::ProxyAuthenticationTest):
(ProxyAuthenticationTest::~ProxyAuthenticationTest):
(ProxyAuthenticationTest::proxyServerPortAsString):
(testWebViewAuthenticationProxy):
(testWebViewAuthenticationProxyHTTPS):
(beforeAll):

Modified Paths

Diff

Modified: trunk/Source/WebCore/ChangeLog (220582 => 220583)


--- trunk/Source/WebCore/ChangeLog	2017-08-11 08:15:40 UTC (rev 220582)
+++ trunk/Source/WebCore/ChangeLog	2017-08-11 10:06:12 UTC (rev 220583)
@@ -1,3 +1,22 @@
+2017-08-11  Carlos Garcia Campos  <cgar...@igalia.com>
+
+        [Soup] Cannot access HTTPS sites using a HTTP proxy that requires authentication
+        https://bugs.webkit.org/show_bug.cgi?id=175378
+
+        Reviewed by Sergio Villar Senin.
+
+        Bring back part of the code removed in r206732, to keep a reference to the SoupMessage in the
+        AuthenticationChallenge since it can be different to the resource message.
+
+        * platform/network/soup/AuthenticationChallenge.h:
+        (WebCore::AuthenticationChallenge::AuthenticationChallenge): Deleted.
+        (WebCore::AuthenticationChallenge::authenticationClient const): Deleted.
+        (WebCore::AuthenticationChallenge::soupAuth const): Deleted.
+        (WebCore::AuthenticationChallenge::setProposedCredential): Deleted.
+        * platform/network/soup/AuthenticationChallengeSoup.cpp:
+        (WebCore::AuthenticationChallenge::AuthenticationChallenge):
+        (WebCore::AuthenticationChallenge::platformCompare):
+
 2017-08-10  Dan Bernstein  <m...@apple.com>
 
         Fixed building for macOS 10.12 with the macOS 10.13 SDK.

Modified: trunk/Source/WebCore/platform/network/soup/AuthenticationChallenge.h (220582 => 220583)


--- trunk/Source/WebCore/platform/network/soup/AuthenticationChallenge.h	2017-08-11 08:15:40 UTC (rev 220582)
+++ trunk/Source/WebCore/platform/network/soup/AuthenticationChallenge.h	2017-08-11 10:06:12 UTC (rev 220583)
@@ -22,9 +22,9 @@
  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
-#ifndef AuthenticationChallenge_h
-#define AuthenticationChallenge_h
 
+#pragma once
+
 #include "AuthenticationChallengeBase.h"
 #include "AuthenticationClient.h"
 
@@ -33,7 +33,7 @@
 
 namespace WebCore {
 
-class AuthenticationChallenge : public AuthenticationChallengeBase {
+class AuthenticationChallenge final : public AuthenticationChallengeBase {
 public:
     AuthenticationChallenge()
     {
@@ -46,6 +46,7 @@
 
     AuthenticationChallenge(SoupMessage*, SoupAuth*, bool retrying, AuthenticationClient* = nullptr);
     AuthenticationClient* authenticationClient() const { return m_authenticationClient.get(); }
+    SoupMessage* soupMessage() const { return m_soupMessage.get(); }
     SoupAuth* soupAuth() const { return m_soupAuth.get(); }
     void setProposedCredential(const Credential& credential) { m_proposedCredential = credential; }
 
@@ -53,10 +54,10 @@
     friend class AuthenticationChallengeBase;
     static bool platformCompare(const AuthenticationChallenge&, const AuthenticationChallenge&);
 
+    GRefPtr<SoupMessage> m_soupMessage;
     GRefPtr<SoupAuth> m_soupAuth;
     RefPtr<AuthenticationClient> m_authenticationClient;
 };
 
-}
+} // namespace WebCore
 
-#endif

Modified: trunk/Source/WebCore/platform/network/soup/AuthenticationChallengeSoup.cpp (220582 => 220583)


--- trunk/Source/WebCore/platform/network/soup/AuthenticationChallengeSoup.cpp	2017-08-11 08:15:40 UTC (rev 220582)
+++ trunk/Source/WebCore/platform/network/soup/AuthenticationChallengeSoup.cpp	2017-08-11 10:06:12 UTC (rev 220583)
@@ -72,6 +72,7 @@
         retrying ? 1 : 0, // previousFailureCount
         soupMessage, // failureResponse
         ResourceError::authenticationError(soupMessage))
+    , m_soupMessage(soupMessage)
     , m_soupAuth(soupAuth)
     , m_authenticationClient(client)
 {
@@ -79,7 +80,7 @@
 
 bool AuthenticationChallenge::platformCompare(const AuthenticationChallenge& a, const AuthenticationChallenge& b)
 {
-    return a.soupAuth() == b.soupAuth();
+    return a.soupMessage() == b.soupMessage() && a.soupAuth() == b.soupAuth();
 }
 
 } // namespace WebCore

Modified: trunk/Source/WebKit/ChangeLog (220582 => 220583)


--- trunk/Source/WebKit/ChangeLog	2017-08-11 08:15:40 UTC (rev 220582)
+++ trunk/Source/WebKit/ChangeLog	2017-08-11 10:06:12 UTC (rev 220583)
@@ -1,3 +1,19 @@
+2017-08-11  Carlos Garcia Campos  <cgar...@igalia.com>
+
+        [Soup] Cannot access HTTPS sites using a HTTP proxy that requires authentication
+        https://bugs.webkit.org/show_bug.cgi?id=175378
+
+        Reviewed by Sergio Villar Senin.
+
+        In case of HTTPS resource with a proxy, libsoup uses a tunnel internally, that uses its own SoupMessage during
+        the proxy authentication. We were ignoring authentication requests for other messages.
+
+        * NetworkProcess/soup/NetworkDataTaskSoup.cpp:
+        (WebKit::NetworkDataTaskSoup::authenticateCallback): Only return early if the message does't match and it's not
+        HTTPS resource over a proxy.
+        (WebKit::NetworkDataTaskSoup::authenticate): Use the soup message from the authentication challenge.
+        (WebKit::NetworkDataTaskSoup::continueAuthenticate): Ditto.
+
 2017-08-10  Carlos Garcia Campos  <cgar...@igalia.com>
 
         [Soup] Do not spawn the network process to setup cookie persistent storage

Modified: trunk/Source/WebKit/NetworkProcess/soup/NetworkDataTaskSoup.cpp (220582 => 220583)


--- trunk/Source/WebKit/NetworkProcess/soup/NetworkDataTaskSoup.cpp	2017-08-11 08:15:40 UTC (rev 220582)
+++ trunk/Source/WebKit/NetworkProcess/soup/NetworkDataTaskSoup.cpp	2017-08-11 10:06:12 UTC (rev 220583)
@@ -445,7 +445,12 @@
 void NetworkDataTaskSoup::authenticateCallback(SoupSession* session, SoupMessage* soupMessage, SoupAuth* soupAuth, gboolean retrying, NetworkDataTaskSoup* task)
 {
     ASSERT(session == static_cast<NetworkSessionSoup&>(task->m_session.get()).soupSession());
-    if (soupMessage != task->m_soupMessage.get())
+
+    // We don't return early here in case the given soupMessage is different to m_soupMessage when
+    // it's proxy authentication and the request URL is HTTPS, because in that case libsoup uses a
+    // tunnel internally and the SoupMessage used for the authentication is the tunneling one.
+    // See https://bugs.webkit.org/show_bug.cgi?id=175378.
+    if (soupMessage != task->m_soupMessage.get() && (soupMessage->status_code != SOUP_STATUS_PROXY_AUTHENTICATION_REQUIRED || !task->m_currentRequest.url().protocolIs("https")))
         return;
 
     if (task->state() == State::Canceling || task->state() == State::Completed || !task->m_client) {
@@ -487,7 +492,7 @@
         }
     }
 
-    soup_session_pause_message(static_cast<NetworkSessionSoup&>(m_session.get()).soupSession(), m_soupMessage.get());
+    soup_session_pause_message(static_cast<NetworkSessionSoup&>(m_session.get()).soupSession(), challenge.soupMessage());
 
     // We could also do this before we even start the request, but that would be at the expense
     // of all request latency, versus a one-time latency for the small subset of requests that
@@ -541,7 +546,7 @@
             soup_auth_authenticate(challenge.soupAuth(), credential.user().utf8().data(), credential.password().utf8().data());
         }
 
-        soup_session_unpause_message(static_cast<NetworkSessionSoup&>(m_session.get()).soupSession(), m_soupMessage.get());
+        soup_session_unpause_message(static_cast<NetworkSessionSoup&>(m_session.get()).soupSession(), challenge.soupMessage());
     });
 }
 

Modified: trunk/Tools/ChangeLog (220582 => 220583)


--- trunk/Tools/ChangeLog	2017-08-11 08:15:40 UTC (rev 220582)
+++ trunk/Tools/ChangeLog	2017-08-11 10:06:12 UTC (rev 220583)
@@ -1,3 +1,25 @@
+2017-08-11  Carlos Garcia Campos  <cgar...@igalia.com>
+
+        [Soup] Cannot access HTTPS sites using a HTTP proxy that requires authentication
+        https://bugs.webkit.org/show_bug.cgi?id=175378
+
+        Reviewed by Sergio Villar Senin.
+
+        Add two test cases to check proxy authentication.
+
+        * TestWebKitAPI/Tests/WebKitGLib/TestAuthentication.cpp:
+        (Tunnel::Tunnel):
+        (Tunnel::~Tunnel):
+        (Tunnel::connect):
+        (Tunnel::connected):
+        (serverCallback):
+        (ProxyAuthenticationTest::ProxyAuthenticationTest):
+        (ProxyAuthenticationTest::~ProxyAuthenticationTest):
+        (ProxyAuthenticationTest::proxyServerPortAsString):
+        (testWebViewAuthenticationProxy):
+        (testWebViewAuthenticationProxyHTTPS):
+        (beforeAll):
+
 2017-08-11  Xabier Rodriguez Calvar  <calva...@igalia.com>
 
         [GStreamer][GTK][WPE] Unify GStreamer JHBuild moduleset for both GTK+ and WPE

Modified: trunk/Tools/TestWebKitAPI/Tests/WebKitGLib/TestAuthentication.cpp (220582 => 220583)


--- trunk/Tools/TestWebKitAPI/Tests/WebKitGLib/TestAuthentication.cpp	2017-08-11 08:15:40 UTC (rev 220582)
+++ trunk/Tools/TestWebKitAPI/Tests/WebKitGLib/TestAuthentication.cpp	2017-08-11 10:06:12 UTC (rev 220583)
@@ -252,14 +252,74 @@
     g_assert_cmpstr(webkit_web_view_get_title(test->m_webView), ==, authExpectedSuccessTitle);
 }
 
-static void serverCallback(SoupServer*, SoupMessage* message, const char* path, GHashTable*, SoupClientContext*, void*)
+class Tunnel {
+public:
+    Tunnel(SoupServer* server, SoupMessage* message)
+        : m_server(server)
+        , m_message(message)
+    {
+        soup_server_pause_message(m_server.get(), m_message.get());
+    }
+
+    ~Tunnel()
+    {
+        soup_server_unpause_message(m_server.get(), m_message.get());
+    }
+
+    void connect(Function<void (const char*)>&& completionHandler)
+    {
+        m_completionHandler = WTFMove(completionHandler);
+        GRefPtr<GSocketClient> client = adoptGRef(g_socket_client_new());
+        auto* uri = soup_message_get_uri(m_message.get());
+        g_socket_client_connect_to_host_async(client.get(), uri->host, uri->port, nullptr, [](GObject* source, GAsyncResult* result, gpointer userData) {
+            auto* tunnel = static_cast<Tunnel*>(userData);
+            GUniqueOutPtr<GError> error;
+            GRefPtr<GSocketConnection> connection = adoptGRef(g_socket_client_connect_to_host_finish(G_SOCKET_CLIENT(source), result, &error.outPtr()));
+            tunnel->connected(!connection ? error->message : nullptr);
+        }, this);
+    }
+
+    void connected(const char* errorMessage)
+    {
+        auto completionHandler = std::exchange(m_completionHandler, nullptr);
+        completionHandler(errorMessage);
+    }
+
+    GRefPtr<SoupServer> m_server;
+    GRefPtr<SoupMessage> m_message;
+    Function<void (const char*)> m_completionHandler;
+};
+
+unsigned gProxyServerPort;
+
+static void serverCallback(SoupServer* server, SoupMessage* message, const char* path, GHashTable*, SoupClientContext* context, void*)
 {
+    if (message->method == SOUP_METHOD_CONNECT) {
+        g_assert_cmpuint(soup_server_get_port(server), ==, gProxyServerPort);
+        auto tunnel = std::make_unique<Tunnel>(server, message);
+        auto* tunnelPtr = tunnel.get();
+        tunnelPtr->connect([tunnel = WTFMove(tunnel)](const char* errorMessage) {
+            if (errorMessage) {
+                soup_message_set_status(tunnel->m_message.get(), SOUP_STATUS_BAD_GATEWAY);
+                soup_message_set_response(tunnel->m_message.get(), "text/plain", SOUP_MEMORY_COPY, errorMessage, strlen(errorMessage));
+            } else {
+                soup_message_headers_append(tunnel->m_message->response_headers, "Proxy-Authenticate", "Basic realm=\"Proxy realm\"");
+                soup_message_set_status(tunnel->m_message.get(), SOUP_STATUS_PROXY_AUTHENTICATION_REQUIRED);
+            }
+        });
+        return;
+    }
+
     if (message->method != SOUP_METHOD_GET) {
         soup_message_set_status(message, SOUP_STATUS_NOT_IMPLEMENTED);
         return;
     }
 
-    if (!strcmp(path, "/auth-test.html") || !strcmp(path, "/empty-realm.html")) {
+    if (g_str_has_suffix(path, "/auth-test.html") || g_str_has_suffix(path, "/empty-realm.html")) {
+        bool isProxy = g_str_has_prefix(path, "/proxy");
+        if (isProxy)
+            g_assert_cmpuint(soup_server_get_port(server), ==, gProxyServerPort);
+
         const char* authorization = soup_message_headers_get_one(message->request_headers, "Authorization");
         // Require authentication.
         if (!g_strcmp0(authorization, authExpectedAuthorization)) {
@@ -269,11 +329,11 @@
             AuthenticationTest::authenticationRetries = 0;
         } else if (++AuthenticationTest::authenticationRetries < 3) {
             // No or invalid authorization header provided by the client, request authentication twice then fail.
-            soup_message_set_status(message, SOUP_STATUS_UNAUTHORIZED);
+            soup_message_set_status(message, isProxy ? SOUP_STATUS_PROXY_AUTHENTICATION_REQUIRED : SOUP_STATUS_UNAUTHORIZED);
             if (!strcmp(path, "/empty-realm.html"))
                 soup_message_headers_append(message->response_headers, "WWW-Authenticate", "Basic");
             else
-                soup_message_headers_append(message->response_headers, "WWW-Authenticate", "Basic realm=\"my realm\"");
+                soup_message_headers_append(message->response_headers, isProxy ? "Proxy-Authenticate" : "WWW-Authenticate", isProxy ? "Basic realm=\"Proxy realm\"" : "Basic realm=\"my realm\"");
             // Include a failure message in case the user attempts to proceed without authentication.
             soup_message_body_append(message->response_body, SOUP_MEMORY_STATIC, authFailureHTMLString, strlen(authFailureHTMLString));
         } else {
@@ -287,6 +347,64 @@
     soup_message_body_complete(message->response_body);
 }
 
+class ProxyAuthenticationTest : public AuthenticationTest {
+public:
+    MAKE_GLIB_TEST_FIXTURE(ProxyAuthenticationTest);
+
+    ProxyAuthenticationTest()
+    {
+        m_proxyServer.run(serverCallback);
+        g_assert(m_proxyServer.baseURI());
+        gProxyServerPort = soup_uri_get_port(m_proxyServer.baseURI());
+        GUniquePtr<char> proxyURI(soup_uri_to_string(m_proxyServer.baseURI(), FALSE));
+        WebKitNetworkProxySettings* settings = webkit_network_proxy_settings_new(proxyURI.get(), nullptr);
+        webkit_web_context_set_network_proxy_settings(m_webContext.get(), WEBKIT_NETWORK_PROXY_MODE_CUSTOM, settings);
+        webkit_network_proxy_settings_free(settings);
+    }
+
+    ~ProxyAuthenticationTest()
+    {
+        gProxyServerPort = 0;
+    }
+
+    GUniquePtr<char> proxyServerPortAsString()
+    {
+        GUniquePtr<char> port(g_strdup_printf("%u", soup_uri_get_port(m_proxyServer.baseURI())));
+        return port;
+    }
+
+    WebKitTestServer m_proxyServer;
+};
+
+static void testWebViewAuthenticationProxy(ProxyAuthenticationTest* test, gconstpointer)
+{
+    test->loadURI(kServer->getURIForPath("/proxy/auth-test.html").data());
+    WebKitAuthenticationRequest* request = test->waitForAuthenticationRequest();
+    // FIXME: the uri and host should the proxy ones, not the requested ones.
+    g_assert_cmpstr(webkit_authentication_request_get_host(request), ==, soup_uri_get_host(kServer->baseURI()));
+    g_assert_cmpuint(webkit_authentication_request_get_port(request), ==, soup_uri_get_port(kServer->baseURI()));
+    g_assert_cmpstr(webkit_authentication_request_get_realm(request), ==, "Proxy realm");
+    g_assert(webkit_authentication_request_get_scheme(request) == WEBKIT_AUTHENTICATION_SCHEME_HTTP_BASIC);
+    g_assert(webkit_authentication_request_is_for_proxy(request));
+    g_assert(!webkit_authentication_request_is_retry(request));
+}
+
+static void testWebViewAuthenticationProxyHTTPS(ProxyAuthenticationTest* test, gconstpointer)
+{
+    auto httpsServer = std::make_unique<WebKitTestServer>(WebKitTestServer::ServerHTTPS);
+    httpsServer->run(serverCallback);
+
+    test->loadURI(httpsServer->getURIForPath("/proxy/auth-test.html").data());
+    WebKitAuthenticationRequest* request = test->waitForAuthenticationRequest();
+    // FIXME: the uri and host should the proxy ones, not the requested ones.
+    g_assert_cmpstr(webkit_authentication_request_get_host(request), ==, soup_uri_get_host(httpsServer->baseURI()));
+    g_assert_cmpuint(webkit_authentication_request_get_port(request), ==, soup_uri_get_port(httpsServer->baseURI()));
+    g_assert_cmpstr(webkit_authentication_request_get_realm(request), ==, "Proxy realm");
+    g_assert(webkit_authentication_request_get_scheme(request) == WEBKIT_AUTHENTICATION_SCHEME_HTTP_BASIC);
+    g_assert(webkit_authentication_request_is_for_proxy(request));
+    g_assert(!webkit_authentication_request_is_retry(request));
+}
+
 void beforeAll()
 {
     kServer = new WebKitTestServer();
@@ -300,6 +418,8 @@
     AuthenticationTest::add("WebKitWebView", "authentication-no-credential", testWebViewAuthenticationNoCredential);
     AuthenticationTest::add("WebKitWebView", "authentication-storage", testWebViewAuthenticationStorage);
     AuthenticationTest::add("WebKitWebView", "authentication-empty-realm", testWebViewAuthenticationEmptyRealm);
+    ProxyAuthenticationTest::add("WebKitWebView", "authentication-proxy", testWebViewAuthenticationProxy);
+    ProxyAuthenticationTest::add("WebKitWebView", "authentication-proxy-https", testWebViewAuthenticationProxyHTTPS);
 }
 
 void afterAll()
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to