diff options
Diffstat (limited to 'Source/WebKit2/Shared/Authentication')
4 files changed, 338 insertions, 49 deletions
diff --git a/Source/WebKit2/Shared/Authentication/AuthenticationManager.cpp b/Source/WebKit2/Shared/Authentication/AuthenticationManager.cpp index 6e1e06d4a..f16ffa50f 100644 --- a/Source/WebKit2/Shared/Authentication/AuthenticationManager.cpp +++ b/Source/WebKit2/Shared/Authentication/AuthenticationManager.cpp @@ -30,6 +30,8 @@ #include "ChildProcess.h" #include "Download.h" #include "DownloadProxyMessages.h" +#include "NetworkProcessProxyMessages.h" +#include "PendingDownload.h" #include "WebCoreArgumentCoders.h" #include "WebFrame.h" #include "WebPage.h" @@ -37,22 +39,24 @@ #include <WebCore/AuthenticationChallenge.h> #include <WebCore/AuthenticationClient.h> -#if ENABLE(NETWORK_PROCESS) -#include "NetworkProcessProxyMessages.h" -#endif - using namespace WebCore; namespace WebKit { static uint64_t generateAuthenticationChallengeID() { - ASSERT(isMainThread()); + ASSERT(RunLoop::isMain()); static int64_t uniqueAuthenticationChallengeID; return ++uniqueAuthenticationChallengeID; } +static bool canCoalesceChallenge(const WebCore::AuthenticationChallenge& challenge) +{ + // Do not coalesce server trust evaluation requests because ProtectionSpace comparison does not evaluate server trust (e.g. certificate). + return challenge.protectionSpace().authenticationScheme() != ProtectionSpaceAuthenticationSchemeServerTrustEvaluationRequested; +} + const char* AuthenticationManager::supplementName() { return "AuthenticationManager"; @@ -64,41 +68,129 @@ AuthenticationManager::AuthenticationManager(ChildProcess* process) m_process->addMessageReceiver(Messages::AuthenticationManager::messageReceiverName(), *this); } -uint64_t AuthenticationManager::establishIdentifierForChallenge(const WebCore::AuthenticationChallenge& authenticationChallenge) +uint64_t AuthenticationManager::addChallengeToChallengeMap(Challenge&& challenge) { - ASSERT(isMainThread()); + ASSERT(RunLoop::isMain()); uint64_t challengeID = generateAuthenticationChallengeID(); - m_challenges.set(challengeID, authenticationChallenge); + m_challenges.set(challengeID, WTFMove(challenge)); return challengeID; } +bool AuthenticationManager::shouldCoalesceChallenge(uint64_t pageID, uint64_t challengeID, const AuthenticationChallenge& challenge) const +{ + if (!canCoalesceChallenge(challenge)) + return false; + + for (auto& item : m_challenges) { + if (item.key != challengeID && item.value.pageID == pageID && ProtectionSpace::compare(challenge.protectionSpace(), item.value.challenge.protectionSpace())) + return true; + } + return false; +} + +Vector<uint64_t> AuthenticationManager::coalesceChallengesMatching(uint64_t challengeID) const +{ + auto iterator = m_challenges.find(challengeID); + ASSERT(iterator != m_challenges.end()); + + auto& challenge = iterator->value; + + Vector<uint64_t> challengesToCoalesce; + challengesToCoalesce.append(challengeID); + + if (!canCoalesceChallenge(challenge.challenge)) + return challengesToCoalesce; + + for (auto& item : m_challenges) { + if (item.key != challengeID && item.value.pageID == challenge.pageID && ProtectionSpace::compare(challenge.challenge.protectionSpace(), item.value.challenge.protectionSpace())) + challengesToCoalesce.append(item.key); + } + + return challengesToCoalesce; +} + void AuthenticationManager::didReceiveAuthenticationChallenge(WebFrame* frame, const AuthenticationChallenge& authenticationChallenge) { ASSERT(frame); ASSERT(frame->page()); + + auto pageID = frame->page()->pageID(); + uint64_t challengeID = addChallengeToChallengeMap({pageID, authenticationChallenge +#if USE(NETWORK_SESSION) + , { } +#endif + }); + + // Coalesce challenges in the same protection space and in the same page. + if (shouldCoalesceChallenge(pageID, challengeID, authenticationChallenge)) + return; - m_process->send(Messages::WebPageProxy::DidReceiveAuthenticationChallenge(frame->frameID(), authenticationChallenge, establishIdentifierForChallenge(authenticationChallenge)), frame->page()->pageID()); + m_process->send(Messages::WebPageProxy::DidReceiveAuthenticationChallenge(frame->frameID(), authenticationChallenge, challengeID), frame->page()->pageID()); } -#if ENABLE(NETWORK_PROCESS) -void AuthenticationManager::didReceiveAuthenticationChallenge(uint64_t pageID, uint64_t frameID, const AuthenticationChallenge& authenticationChallenge) +#if USE(NETWORK_SESSION) +void AuthenticationManager::didReceiveAuthenticationChallenge(uint64_t pageID, uint64_t frameID, const AuthenticationChallenge& authenticationChallenge, ChallengeCompletionHandler&& completionHandler) { ASSERT(pageID); ASSERT(frameID); + + uint64_t challengeID = addChallengeToChallengeMap({ pageID, authenticationChallenge, WTFMove(completionHandler) }); + + // Coalesce challenges in the same protection space and in the same page. + if (shouldCoalesceChallenge(pageID, challengeID, authenticationChallenge)) + return; - m_process->send(Messages::NetworkProcessProxy::DidReceiveAuthenticationChallenge(pageID, frameID, authenticationChallenge, establishIdentifierForChallenge(authenticationChallenge))); + m_process->send(Messages::NetworkProcessProxy::DidReceiveAuthenticationChallenge(pageID, frameID, authenticationChallenge, challengeID)); +} + +void AuthenticationManager::didReceiveAuthenticationChallenge(PendingDownload& pendingDownload, const WebCore::AuthenticationChallenge& authenticationChallenge, ChallengeCompletionHandler&& completionHandler) +{ + uint64_t dummyPageID = 0; + uint64_t challengeID = addChallengeToChallengeMap({ dummyPageID, authenticationChallenge, WTFMove(completionHandler) }); + + // Coalesce challenges in the same protection space and in the same page. + if (shouldCoalesceChallenge(dummyPageID, challengeID, authenticationChallenge)) + return; + + pendingDownload.send(Messages::DownloadProxy::DidReceiveAuthenticationChallenge(authenticationChallenge, challengeID)); } #endif +void AuthenticationManager::didReceiveAuthenticationChallenge(uint64_t pageID, uint64_t frameID, const AuthenticationChallenge& authenticationChallenge) +{ + ASSERT(pageID); + ASSERT(frameID); + + uint64_t challengeID = addChallengeToChallengeMap({pageID, authenticationChallenge +#if USE(NETWORK_SESSION) + , { } +#endif + }); + + // Coalesce challenges in the same protection space and in the same page. + if (shouldCoalesceChallenge(pageID, challengeID, authenticationChallenge)) + return; + + m_process->send(Messages::NetworkProcessProxy::DidReceiveAuthenticationChallenge(pageID, frameID, authenticationChallenge, challengeID)); +} -void AuthenticationManager::didReceiveAuthenticationChallenge(Download* download, const AuthenticationChallenge& authenticationChallenge) +#if !USE(NETWORK_SESSION) +void AuthenticationManager::didReceiveAuthenticationChallenge(Download& download, const AuthenticationChallenge& authenticationChallenge) { - download->send(Messages::DownloadProxy::DidReceiveAuthenticationChallenge(authenticationChallenge, establishIdentifierForChallenge(authenticationChallenge))); + uint64_t dummyPageID = 0; + uint64_t challengeID = addChallengeToChallengeMap({dummyPageID, authenticationChallenge}); + + // Coalesce challenges in the same protection space and in the same page. + if (shouldCoalesceChallenge(dummyPageID, challengeID, authenticationChallenge)) + return; + + download.send(Messages::DownloadProxy::DidReceiveAuthenticationChallenge(authenticationChallenge, challengeID)); } +#endif // Currently, only Mac knows how to respond to authentication challenges with certificate info. #if !HAVE(SEC_IDENTITY) -bool AuthenticationManager::tryUseCertificateInfoForChallenge(const WebCore::AuthenticationChallenge&, const CertificateInfo&) +bool AuthenticationManager::tryUseCertificateInfoForChallenge(const WebCore::AuthenticationChallenge&, const CertificateInfo&, const ChallengeCompletionHandler&) { return false; } @@ -106,54 +198,153 @@ bool AuthenticationManager::tryUseCertificateInfoForChallenge(const WebCore::Aut void AuthenticationManager::useCredentialForChallenge(uint64_t challengeID, const Credential& credential, const CertificateInfo& certificateInfo) { - ASSERT(isMainThread()); + ASSERT(RunLoop::isMain()); + + for (auto& coalescedChallengeID : coalesceChallengesMatching(challengeID)) + useCredentialForSingleChallenge(coalescedChallengeID, credential, certificateInfo); +} + +void AuthenticationManager::useCredentialForSingleChallenge(uint64_t challengeID, const Credential& credential, const CertificateInfo& certificateInfo) +{ + auto challenge = m_challenges.take(challengeID); + ASSERT(!challenge.challenge.isNull()); - AuthenticationChallenge challenge = m_challenges.take(challengeID); - ASSERT(!challenge.isNull()); +#if USE(NETWORK_SESSION) + auto completionHandler = WTFMove(challenge.completionHandler); +#else + ChallengeCompletionHandler completionHandler = nullptr; +#endif - if (tryUseCertificateInfoForChallenge(challenge, certificateInfo)) + if (tryUseCertificateInfoForChallenge(challenge.challenge, certificateInfo, completionHandler)) return; - - AuthenticationClient* coreClient = challenge.authenticationClient(); - if (!coreClient) { - // This authentication challenge comes from a download. - Download::receivedCredential(challenge, credential); + + AuthenticationClient* coreClient = challenge.challenge.authenticationClient(); +#if USE(NETWORK_SESSION) + // If there is a completion handler, then there is no AuthenticationClient. + // FIXME: Remove the use of AuthenticationClient in WebKit2 once NETWORK_SESSION is used for all loads. + if (completionHandler) { + ASSERT(!coreClient); + completionHandler(AuthenticationChallengeDisposition::UseCredential, credential); return; } +#endif - coreClient->receivedCredential(challenge, credential); + if (coreClient) + coreClient->receivedCredential(challenge.challenge, credential); + else + receivedCredential(challenge.challenge, credential); } void AuthenticationManager::continueWithoutCredentialForChallenge(uint64_t challengeID) { - ASSERT(isMainThread()); + ASSERT(RunLoop::isMain()); - AuthenticationChallenge challenge = m_challenges.take(challengeID); - ASSERT(!challenge.isNull()); - AuthenticationClient* coreClient = challenge.authenticationClient(); - if (!coreClient) { - // This authentication challenge comes from a download. - Download::receivedRequestToContinueWithoutCredential(challenge); + for (auto& coalescedChallengeID : coalesceChallengesMatching(challengeID)) + continueWithoutCredentialForSingleChallenge(coalescedChallengeID); +} + +void AuthenticationManager::continueWithoutCredentialForSingleChallenge(uint64_t challengeID) +{ + auto challenge = m_challenges.take(challengeID); + ASSERT(!challenge.challenge.isNull()); + + AuthenticationClient* coreClient = challenge.challenge.authenticationClient(); +#if USE(NETWORK_SESSION) + if (challenge.completionHandler) { + ASSERT(!coreClient); + challenge.completionHandler(AuthenticationChallengeDisposition::UseCredential, Credential()); return; } +#endif - coreClient->receivedRequestToContinueWithoutCredential(challenge); + if (coreClient) + coreClient->receivedRequestToContinueWithoutCredential(challenge.challenge); + else + receivedRequestToContinueWithoutCredential(challenge.challenge); } void AuthenticationManager::cancelChallenge(uint64_t challengeID) { - ASSERT(isMainThread()); + ASSERT(RunLoop::isMain()); + + for (auto& coalescedChallengeID : coalesceChallengesMatching(challengeID)) + cancelSingleChallenge(coalescedChallengeID); +} + +void AuthenticationManager::cancelSingleChallenge(uint64_t challengeID) +{ + auto challenge = m_challenges.take(challengeID); + ASSERT(!challenge.challenge.isNull()); + + AuthenticationClient* coreClient = challenge.challenge.authenticationClient(); +#if USE(NETWORK_SESSION) + if (challenge.completionHandler) { + ASSERT(!coreClient); + challenge.completionHandler(AuthenticationChallengeDisposition::Cancel, Credential()); + return; + } +#endif + + if (coreClient) + coreClient->receivedCancellation(challenge.challenge); + else + receivedCancellation(challenge.challenge); +} + +void AuthenticationManager::performDefaultHandling(uint64_t challengeID) +{ + ASSERT(RunLoop::isMain()); - AuthenticationChallenge challenge = m_challenges.take(challengeID); - ASSERT(!challenge.isNull()); - AuthenticationClient* coreClient = challenge.authenticationClient(); - if (!coreClient) { - // This authentication challenge comes from a download. - Download::receivedCancellation(challenge); + for (auto& coalescedChallengeID : coalesceChallengesMatching(challengeID)) + performDefaultHandlingForSingleChallenge(coalescedChallengeID); +} + +void AuthenticationManager::performDefaultHandlingForSingleChallenge(uint64_t challengeID) +{ + auto challenge = m_challenges.take(challengeID); + ASSERT(!challenge.challenge.isNull()); + + AuthenticationClient* coreClient = challenge.challenge.authenticationClient(); +#if USE(NETWORK_SESSION) + if (challenge.completionHandler) { + ASSERT(!coreClient); + challenge.completionHandler(AuthenticationChallengeDisposition::PerformDefaultHandling, Credential()); return; } +#endif + + if (coreClient) + coreClient->receivedRequestToPerformDefaultHandling(challenge.challenge); + else + receivedRequestToPerformDefaultHandling(challenge.challenge); +} + +void AuthenticationManager::rejectProtectionSpaceAndContinue(uint64_t challengeID) +{ + ASSERT(RunLoop::isMain()); + + for (auto& coalescedChallengeID : coalesceChallengesMatching(challengeID)) + rejectProtectionSpaceAndContinueForSingleChallenge(coalescedChallengeID); +} + +void AuthenticationManager::rejectProtectionSpaceAndContinueForSingleChallenge(uint64_t challengeID) +{ + auto challenge = m_challenges.take(challengeID); + ASSERT(!challenge.challenge.isNull()); + + AuthenticationClient* coreClient = challenge.challenge.authenticationClient(); +#if USE(NETWORK_SESSION) + if (challenge.completionHandler) { + ASSERT(!coreClient); + challenge.completionHandler(AuthenticationChallengeDisposition::RejectProtectionSpace, Credential()); + return; + } +#endif - coreClient->receivedCancellation(challenge); + if (coreClient) + coreClient->receivedChallengeRejection(challenge.challenge); + else + receivedChallengeRejection(challenge.challenge); } } // namespace WebKit diff --git a/Source/WebKit2/Shared/Authentication/AuthenticationManager.h b/Source/WebKit2/Shared/Authentication/AuthenticationManager.h index 6882092f2..6372cd277 100644 --- a/Source/WebKit2/Shared/Authentication/AuthenticationManager.h +++ b/Source/WebKit2/Shared/Authentication/AuthenticationManager.h @@ -31,6 +31,7 @@ #include "WebProcessSupplement.h" #include <WebCore/AuthenticationChallenge.h> #include <wtf/Forward.h> +#include <wtf/Function.h> #include <wtf/HashMap.h> namespace WebCore { @@ -43,8 +44,18 @@ namespace WebKit { class ChildProcess; class Download; +class DownloadID; +class PendingDownload; class WebFrame; +enum class AuthenticationChallengeDisposition { + UseCredential, + PerformDefaultHandling, + Cancel, + RejectProtectionSpace +}; +typedef Function<void(AuthenticationChallengeDisposition, const WebCore::Credential&)> ChallengeCompletionHandler; + class AuthenticationManager : public WebProcessSupplement, public NetworkProcessSupplement, public IPC::MessageReceiver { WTF_MAKE_NONCOPYABLE(AuthenticationManager); public: @@ -52,31 +63,63 @@ public: static const char* supplementName(); +#if USE(NETWORK_SESSION) + void didReceiveAuthenticationChallenge(uint64_t pageID, uint64_t frameID, const WebCore::AuthenticationChallenge&, ChallengeCompletionHandler&&); + void didReceiveAuthenticationChallenge(PendingDownload&, const WebCore::AuthenticationChallenge&, ChallengeCompletionHandler&&); +#if USE(PROTECTION_SPACE_AUTH_CALLBACK) + void continueCanAuthenticateAgainstProtectionSpace(DownloadID, bool canAuthenticate); +#endif +#endif // Called for resources in the WebProcess (NetworkProcess disabled) void didReceiveAuthenticationChallenge(WebFrame*, const WebCore::AuthenticationChallenge&); // Called for resources in the NetworkProcess (NetworkProcess enabled) void didReceiveAuthenticationChallenge(uint64_t pageID, uint64_t frameID, const WebCore::AuthenticationChallenge&); - // Called for downloads with or without the NetworkProcess - void didReceiveAuthenticationChallenge(Download*, const WebCore::AuthenticationChallenge&); +#if !USE(NETWORK_SESSION) + void didReceiveAuthenticationChallenge(Download&, const WebCore::AuthenticationChallenge&); +#endif void useCredentialForChallenge(uint64_t challengeID, const WebCore::Credential&, const WebCore::CertificateInfo&); void continueWithoutCredentialForChallenge(uint64_t challengeID); void cancelChallenge(uint64_t challengeID); - + void performDefaultHandling(uint64_t challengeID); + void rejectProtectionSpaceAndContinue(uint64_t challengeID); + uint64_t outstandingAuthenticationChallengeCount() const { return m_challenges.size(); } + static void receivedCredential(const WebCore::AuthenticationChallenge&, const WebCore::Credential&); + static void receivedRequestToContinueWithoutCredential(const WebCore::AuthenticationChallenge&); + static void receivedCancellation(const WebCore::AuthenticationChallenge&); + static void receivedRequestToPerformDefaultHandling(const WebCore::AuthenticationChallenge&); + static void receivedChallengeRejection(const WebCore::AuthenticationChallenge&); + private: + struct Challenge { + uint64_t pageID; + WebCore::AuthenticationChallenge challenge; +#if USE(NETWORK_SESSION) + ChallengeCompletionHandler completionHandler; +#endif + }; + // IPC::MessageReceiver - virtual void didReceiveMessage(IPC::Connection*, IPC::MessageDecoder&) override; + void didReceiveMessage(IPC::Connection&, IPC::Decoder&) override; + + bool tryUseCertificateInfoForChallenge(const WebCore::AuthenticationChallenge&, const WebCore::CertificateInfo&, const ChallengeCompletionHandler&); + + uint64_t addChallengeToChallengeMap(Challenge&&); + bool shouldCoalesceChallenge(uint64_t pageID, uint64_t challengeID, const WebCore::AuthenticationChallenge&) const; - bool tryUseCertificateInfoForChallenge(const WebCore::AuthenticationChallenge&, const WebCore::CertificateInfo&); + void useCredentialForSingleChallenge(uint64_t challengeID, const WebCore::Credential&, const WebCore::CertificateInfo&); + void continueWithoutCredentialForSingleChallenge(uint64_t challengeID); + void cancelSingleChallenge(uint64_t challengeID); + void performDefaultHandlingForSingleChallenge(uint64_t challengeID); + void rejectProtectionSpaceAndContinueForSingleChallenge(uint64_t challengeID); - uint64_t establishIdentifierForChallenge(const WebCore::AuthenticationChallenge&); + Vector<uint64_t> coalesceChallengesMatching(uint64_t challengeID) const; ChildProcess* m_process; - typedef HashMap<uint64_t, WebCore::AuthenticationChallenge> AuthenticationChallengeMap; - AuthenticationChallengeMap m_challenges; + HashMap<uint64_t, Challenge> m_challenges; }; } // namespace WebKit diff --git a/Source/WebKit2/Shared/Authentication/AuthenticationManager.messages.in b/Source/WebKit2/Shared/Authentication/AuthenticationManager.messages.in index 34db9b208..da79cecf5 100644 --- a/Source/WebKit2/Shared/Authentication/AuthenticationManager.messages.in +++ b/Source/WebKit2/Shared/Authentication/AuthenticationManager.messages.in @@ -24,4 +24,6 @@ messages -> AuthenticationManager { void UseCredentialForChallenge(uint64_t challengeID, WebCore::Credential credential, WebCore::CertificateInfo certificate); void ContinueWithoutCredentialForChallenge(uint64_t challengeID); void CancelChallenge(uint64_t challengeID); + void PerformDefaultHandling(uint64_t challengeID); + void RejectProtectionSpaceAndContinue(uint64_t challengeID); } diff --git a/Source/WebKit2/Shared/Authentication/soup/AuthenticationManagerSoup.cpp b/Source/WebKit2/Shared/Authentication/soup/AuthenticationManagerSoup.cpp new file mode 100644 index 000000000..c26f2e5e7 --- /dev/null +++ b/Source/WebKit2/Shared/Authentication/soup/AuthenticationManagerSoup.cpp @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "AuthenticationManager.h" + +using namespace WebCore; + +namespace WebKit { + +void AuthenticationManager::receivedCredential(const AuthenticationChallenge&, const Credential&) +{ +} + +void AuthenticationManager::receivedRequestToContinueWithoutCredential(const AuthenticationChallenge&) +{ +} + +void AuthenticationManager::receivedCancellation(const AuthenticationChallenge&) +{ +} + +void AuthenticationManager::receivedRequestToPerformDefaultHandling(const AuthenticationChallenge&) +{ +} + +void AuthenticationManager::receivedChallengeRejection(const AuthenticationChallenge&) +{ +} + +} |
