Changeset 220583 in webkit


Ignore:
Timestamp:
Aug 11, 2017 3:06:12 AM (7 years ago)
Author:
Carlos Garcia Campos
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):

Location:
trunk
Files:
7 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/WebCore/ChangeLog

    r220577 r220583  
     12017-08-11  Carlos Garcia Campos  <cgarcia@igalia.com>
     2
     3        [Soup] Cannot access HTTPS sites using a HTTP proxy that requires authentication
     4        https://bugs.webkit.org/show_bug.cgi?id=175378
     5
     6        Reviewed by Sergio Villar Senin.
     7
     8        Bring back part of the code removed in r206732, to keep a reference to the SoupMessage in the
     9        AuthenticationChallenge since it can be different to the resource message.
     10
     11        * platform/network/soup/AuthenticationChallenge.h:
     12        (WebCore::AuthenticationChallenge::AuthenticationChallenge): Deleted.
     13        (WebCore::AuthenticationChallenge::authenticationClient const): Deleted.
     14        (WebCore::AuthenticationChallenge::soupAuth const): Deleted.
     15        (WebCore::AuthenticationChallenge::setProposedCredential): Deleted.
     16        * platform/network/soup/AuthenticationChallengeSoup.cpp:
     17        (WebCore::AuthenticationChallenge::AuthenticationChallenge):
     18        (WebCore::AuthenticationChallenge::platformCompare):
     19
    1202017-08-10  Dan Bernstein  <mitz@apple.com>
    221
  • trunk/Source/WebCore/platform/network/soup/AuthenticationChallenge.h

    r206732 r220583  
    2323 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
    2424 */
    25 #ifndef AuthenticationChallenge_h
    26 #define AuthenticationChallenge_h
     25
     26#pragma once
    2727
    2828#include "AuthenticationChallengeBase.h"
     
    3434namespace WebCore {
    3535
    36 class AuthenticationChallenge : public AuthenticationChallengeBase {
     36class AuthenticationChallenge final : public AuthenticationChallengeBase {
    3737public:
    3838    AuthenticationChallenge()
     
    4747    AuthenticationChallenge(SoupMessage*, SoupAuth*, bool retrying, AuthenticationClient* = nullptr);
    4848    AuthenticationClient* authenticationClient() const { return m_authenticationClient.get(); }
     49    SoupMessage* soupMessage() const { return m_soupMessage.get(); }
    4950    SoupAuth* soupAuth() const { return m_soupAuth.get(); }
    5051    void setProposedCredential(const Credential& credential) { m_proposedCredential = credential; }
     
    5455    static bool platformCompare(const AuthenticationChallenge&, const AuthenticationChallenge&);
    5556
     57    GRefPtr<SoupMessage> m_soupMessage;
    5658    GRefPtr<SoupAuth> m_soupAuth;
    5759    RefPtr<AuthenticationClient> m_authenticationClient;
    5860};
    5961
    60 }
     62} // namespace WebCore
    6163
    62 #endif
  • trunk/Source/WebCore/platform/network/soup/AuthenticationChallengeSoup.cpp

    r206732 r220583  
    7373        soupMessage, // failureResponse
    7474        ResourceError::authenticationError(soupMessage))
     75    , m_soupMessage(soupMessage)
    7576    , m_soupAuth(soupAuth)
    7677    , m_authenticationClient(client)
     
    8081bool AuthenticationChallenge::platformCompare(const AuthenticationChallenge& a, const AuthenticationChallenge& b)
    8182{
    82     return a.soupAuth() == b.soupAuth();
     83    return a.soupMessage() == b.soupMessage() && a.soupAuth() == b.soupAuth();
    8384}
    8485
  • trunk/Source/WebKit/ChangeLog

    r220581 r220583  
     12017-08-11  Carlos Garcia Campos  <cgarcia@igalia.com>
     2
     3        [Soup] Cannot access HTTPS sites using a HTTP proxy that requires authentication
     4        https://bugs.webkit.org/show_bug.cgi?id=175378
     5
     6        Reviewed by Sergio Villar Senin.
     7
     8        In case of HTTPS resource with a proxy, libsoup uses a tunnel internally, that uses its own SoupMessage during
     9        the proxy authentication. We were ignoring authentication requests for other messages.
     10
     11        * NetworkProcess/soup/NetworkDataTaskSoup.cpp:
     12        (WebKit::NetworkDataTaskSoup::authenticateCallback): Only return early if the message does't match and it's not
     13        HTTPS resource over a proxy.
     14        (WebKit::NetworkDataTaskSoup::authenticate): Use the soup message from the authentication challenge.
     15        (WebKit::NetworkDataTaskSoup::continueAuthenticate): Ditto.
     16
    1172017-08-10  Carlos Garcia Campos  <cgarcia@igalia.com>
    218
  • trunk/Source/WebKit/NetworkProcess/soup/NetworkDataTaskSoup.cpp

    r219954 r220583  
    446446{
    447447    ASSERT(session == static_cast<NetworkSessionSoup&>(task->m_session.get()).soupSession());
    448     if (soupMessage != task->m_soupMessage.get())
     448
     449    // We don't return early here in case the given soupMessage is different to m_soupMessage when
     450    // it's proxy authentication and the request URL is HTTPS, because in that case libsoup uses a
     451    // tunnel internally and the SoupMessage used for the authentication is the tunneling one.
     452    // See https://bugs.webkit.org/show_bug.cgi?id=175378.
     453    if (soupMessage != task->m_soupMessage.get() && (soupMessage->status_code != SOUP_STATUS_PROXY_AUTHENTICATION_REQUIRED || !task->m_currentRequest.url().protocolIs("https")))
    449454        return;
    450455
     
    488493    }
    489494
    490     soup_session_pause_message(static_cast<NetworkSessionSoup&>(m_session.get()).soupSession(), m_soupMessage.get());
     495    soup_session_pause_message(static_cast<NetworkSessionSoup&>(m_session.get()).soupSession(), challenge.soupMessage());
    491496
    492497    // We could also do this before we even start the request, but that would be at the expense
     
    542547        }
    543548
    544         soup_session_unpause_message(static_cast<NetworkSessionSoup&>(m_session.get()).soupSession(), m_soupMessage.get());
     549        soup_session_unpause_message(static_cast<NetworkSessionSoup&>(m_session.get()).soupSession(), challenge.soupMessage());
    545550    });
    546551}
  • trunk/Tools/ChangeLog

    r220582 r220583  
     12017-08-11  Carlos Garcia Campos  <cgarcia@igalia.com>
     2
     3        [Soup] Cannot access HTTPS sites using a HTTP proxy that requires authentication
     4        https://bugs.webkit.org/show_bug.cgi?id=175378
     5
     6        Reviewed by Sergio Villar Senin.
     7
     8        Add two test cases to check proxy authentication.
     9
     10        * TestWebKitAPI/Tests/WebKitGLib/TestAuthentication.cpp:
     11        (Tunnel::Tunnel):
     12        (Tunnel::~Tunnel):
     13        (Tunnel::connect):
     14        (Tunnel::connected):
     15        (serverCallback):
     16        (ProxyAuthenticationTest::ProxyAuthenticationTest):
     17        (ProxyAuthenticationTest::~ProxyAuthenticationTest):
     18        (ProxyAuthenticationTest::proxyServerPortAsString):
     19        (testWebViewAuthenticationProxy):
     20        (testWebViewAuthenticationProxyHTTPS):
     21        (beforeAll):
     22
    1232017-08-11  Xabier Rodriguez Calvar  <calvaris@igalia.com>
    224
  • trunk/Tools/TestWebKitAPI/Tests/WebKitGLib/TestAuthentication.cpp

    r218685 r220583  
    253253}
    254254
    255 static void serverCallback(SoupServer*, SoupMessage* message, const char* path, GHashTable*, SoupClientContext*, void*)
    256 {
     255class Tunnel {
     256public:
     257    Tunnel(SoupServer* server, SoupMessage* message)
     258        : m_server(server)
     259        , m_message(message)
     260    {
     261        soup_server_pause_message(m_server.get(), m_message.get());
     262    }
     263
     264    ~Tunnel()
     265    {
     266        soup_server_unpause_message(m_server.get(), m_message.get());
     267    }
     268
     269    void connect(Function<void (const char*)>&& completionHandler)
     270    {
     271        m_completionHandler = WTFMove(completionHandler);
     272        GRefPtr<GSocketClient> client = adoptGRef(g_socket_client_new());
     273        auto* uri = soup_message_get_uri(m_message.get());
     274        g_socket_client_connect_to_host_async(client.get(), uri->host, uri->port, nullptr, [](GObject* source, GAsyncResult* result, gpointer userData) {
     275            auto* tunnel = static_cast<Tunnel*>(userData);
     276            GUniqueOutPtr<GError> error;
     277            GRefPtr<GSocketConnection> connection = adoptGRef(g_socket_client_connect_to_host_finish(G_SOCKET_CLIENT(source), result, &error.outPtr()));
     278            tunnel->connected(!connection ? error->message : nullptr);
     279        }, this);
     280    }
     281
     282    void connected(const char* errorMessage)
     283    {
     284        auto completionHandler = std::exchange(m_completionHandler, nullptr);
     285        completionHandler(errorMessage);
     286    }
     287
     288    GRefPtr<SoupServer> m_server;
     289    GRefPtr<SoupMessage> m_message;
     290    Function<void (const char*)> m_completionHandler;
     291};
     292
     293unsigned gProxyServerPort;
     294
     295static void serverCallback(SoupServer* server, SoupMessage* message, const char* path, GHashTable*, SoupClientContext* context, void*)
     296{
     297    if (message->method == SOUP_METHOD_CONNECT) {
     298        g_assert_cmpuint(soup_server_get_port(server), ==, gProxyServerPort);
     299        auto tunnel = std::make_unique<Tunnel>(server, message);
     300        auto* tunnelPtr = tunnel.get();
     301        tunnelPtr->connect([tunnel = WTFMove(tunnel)](const char* errorMessage) {
     302            if (errorMessage) {
     303                soup_message_set_status(tunnel->m_message.get(), SOUP_STATUS_BAD_GATEWAY);
     304                soup_message_set_response(tunnel->m_message.get(), "text/plain", SOUP_MEMORY_COPY, errorMessage, strlen(errorMessage));
     305            } else {
     306                soup_message_headers_append(tunnel->m_message->response_headers, "Proxy-Authenticate", "Basic realm=\"Proxy realm\"");
     307                soup_message_set_status(tunnel->m_message.get(), SOUP_STATUS_PROXY_AUTHENTICATION_REQUIRED);
     308            }
     309        });
     310        return;
     311    }
     312
    257313    if (message->method != SOUP_METHOD_GET) {
    258314        soup_message_set_status(message, SOUP_STATUS_NOT_IMPLEMENTED);
     
    260316    }
    261317
    262     if (!strcmp(path, "/auth-test.html") || !strcmp(path, "/empty-realm.html")) {
     318    if (g_str_has_suffix(path, "/auth-test.html") || g_str_has_suffix(path, "/empty-realm.html")) {
     319        bool isProxy = g_str_has_prefix(path, "/proxy");
     320        if (isProxy)
     321            g_assert_cmpuint(soup_server_get_port(server), ==, gProxyServerPort);
     322
    263323        const char* authorization = soup_message_headers_get_one(message->request_headers, "Authorization");
    264324        // Require authentication.
     
    270330        } else if (++AuthenticationTest::authenticationRetries < 3) {
    271331            // No or invalid authorization header provided by the client, request authentication twice then fail.
    272             soup_message_set_status(message, SOUP_STATUS_UNAUTHORIZED);
     332            soup_message_set_status(message, isProxy ? SOUP_STATUS_PROXY_AUTHENTICATION_REQUIRED : SOUP_STATUS_UNAUTHORIZED);
    273333            if (!strcmp(path, "/empty-realm.html"))
    274334                soup_message_headers_append(message->response_headers, "WWW-Authenticate", "Basic");
    275335            else
    276                 soup_message_headers_append(message->response_headers, "WWW-Authenticate", "Basic realm=\"my realm\"");
     336                soup_message_headers_append(message->response_headers, isProxy ? "Proxy-Authenticate" : "WWW-Authenticate", isProxy ? "Basic realm=\"Proxy realm\"" : "Basic realm=\"my realm\"");
    277337            // Include a failure message in case the user attempts to proceed without authentication.
    278338            soup_message_body_append(message->response_body, SOUP_MEMORY_STATIC, authFailureHTMLString, strlen(authFailureHTMLString));
     
    288348}
    289349
     350class ProxyAuthenticationTest : public AuthenticationTest {
     351public:
     352    MAKE_GLIB_TEST_FIXTURE(ProxyAuthenticationTest);
     353
     354    ProxyAuthenticationTest()
     355    {
     356        m_proxyServer.run(serverCallback);
     357        g_assert(m_proxyServer.baseURI());
     358        gProxyServerPort = soup_uri_get_port(m_proxyServer.baseURI());
     359        GUniquePtr<char> proxyURI(soup_uri_to_string(m_proxyServer.baseURI(), FALSE));
     360        WebKitNetworkProxySettings* settings = webkit_network_proxy_settings_new(proxyURI.get(), nullptr);
     361        webkit_web_context_set_network_proxy_settings(m_webContext.get(), WEBKIT_NETWORK_PROXY_MODE_CUSTOM, settings);
     362        webkit_network_proxy_settings_free(settings);
     363    }
     364
     365    ~ProxyAuthenticationTest()
     366    {
     367        gProxyServerPort = 0;
     368    }
     369
     370    GUniquePtr<char> proxyServerPortAsString()
     371    {
     372        GUniquePtr<char> port(g_strdup_printf("%u", soup_uri_get_port(m_proxyServer.baseURI())));
     373        return port;
     374    }
     375
     376    WebKitTestServer m_proxyServer;
     377};
     378
     379static void testWebViewAuthenticationProxy(ProxyAuthenticationTest* test, gconstpointer)
     380{
     381    test->loadURI(kServer->getURIForPath("/proxy/auth-test.html").data());
     382    WebKitAuthenticationRequest* request = test->waitForAuthenticationRequest();
     383    // FIXME: the uri and host should the proxy ones, not the requested ones.
     384    g_assert_cmpstr(webkit_authentication_request_get_host(request), ==, soup_uri_get_host(kServer->baseURI()));
     385    g_assert_cmpuint(webkit_authentication_request_get_port(request), ==, soup_uri_get_port(kServer->baseURI()));
     386    g_assert_cmpstr(webkit_authentication_request_get_realm(request), ==, "Proxy realm");
     387    g_assert(webkit_authentication_request_get_scheme(request) == WEBKIT_AUTHENTICATION_SCHEME_HTTP_BASIC);
     388    g_assert(webkit_authentication_request_is_for_proxy(request));
     389    g_assert(!webkit_authentication_request_is_retry(request));
     390}
     391
     392static void testWebViewAuthenticationProxyHTTPS(ProxyAuthenticationTest* test, gconstpointer)
     393{
     394    auto httpsServer = std::make_unique<WebKitTestServer>(WebKitTestServer::ServerHTTPS);
     395    httpsServer->run(serverCallback);
     396
     397    test->loadURI(httpsServer->getURIForPath("/proxy/auth-test.html").data());
     398    WebKitAuthenticationRequest* request = test->waitForAuthenticationRequest();
     399    // FIXME: the uri and host should the proxy ones, not the requested ones.
     400    g_assert_cmpstr(webkit_authentication_request_get_host(request), ==, soup_uri_get_host(httpsServer->baseURI()));
     401    g_assert_cmpuint(webkit_authentication_request_get_port(request), ==, soup_uri_get_port(httpsServer->baseURI()));
     402    g_assert_cmpstr(webkit_authentication_request_get_realm(request), ==, "Proxy realm");
     403    g_assert(webkit_authentication_request_get_scheme(request) == WEBKIT_AUTHENTICATION_SCHEME_HTTP_BASIC);
     404    g_assert(webkit_authentication_request_is_for_proxy(request));
     405    g_assert(!webkit_authentication_request_is_retry(request));
     406}
     407
    290408void beforeAll()
    291409{
     
    301419    AuthenticationTest::add("WebKitWebView", "authentication-storage", testWebViewAuthenticationStorage);
    302420    AuthenticationTest::add("WebKitWebView", "authentication-empty-realm", testWebViewAuthenticationEmptyRealm);
     421    ProxyAuthenticationTest::add("WebKitWebView", "authentication-proxy", testWebViewAuthenticationProxy);
     422    ProxyAuthenticationTest::add("WebKitWebView", "authentication-proxy-https", testWebViewAuthenticationProxyHTTPS);
    303423}
    304424
Note: See TracChangeset for help on using the changeset viewer.