1/*
2 * Copyright (C) 2010, 2013 Apple Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23 * THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#include "config.h"
27#include "AuthenticationManager.h"
28
29#include "AuthenticationManagerMessages.h"
30#include "Download.h"
31#include "DownloadProxyMessages.h"
32#include "NetworkProcess.h"
33#include "NetworkProcessProxyMessages.h"
34#include "PendingDownload.h"
35#include "WebCoreArgumentCoders.h"
36#include "WebFrame.h"
37#include "WebPage.h"
38#include "WebPageProxyMessages.h"
39#include <WebCore/AuthenticationChallenge.h>
40
41namespace WebKit {
42using namespace WebCore;
43
44static uint64_t generateAuthenticationChallengeID()
45{
46 ASSERT(RunLoop::isMain());
47
48 static int64_t uniqueAuthenticationChallengeID;
49 return ++uniqueAuthenticationChallengeID;
50}
51
52static bool canCoalesceChallenge(const WebCore::AuthenticationChallenge& challenge)
53{
54 // Do not coalesce server trust evaluation requests because ProtectionSpace comparison does not evaluate server trust (e.g. certificate).
55 return challenge.protectionSpace().authenticationScheme() != ProtectionSpaceAuthenticationSchemeServerTrustEvaluationRequested;
56}
57
58const char* AuthenticationManager::supplementName()
59{
60 return "AuthenticationManager";
61}
62
63AuthenticationManager::AuthenticationManager(NetworkProcess& process)
64 : m_process(process)
65{
66 m_process.addMessageReceiver(Messages::AuthenticationManager::messageReceiverName(), *this);
67}
68
69uint64_t AuthenticationManager::addChallengeToChallengeMap(Challenge&& challenge)
70{
71 ASSERT(RunLoop::isMain());
72
73 uint64_t challengeID = generateAuthenticationChallengeID();
74 m_challenges.set(challengeID, WTFMove(challenge));
75 return challengeID;
76}
77
78bool AuthenticationManager::shouldCoalesceChallenge(PageIdentifier pageID, uint64_t challengeID, const AuthenticationChallenge& challenge) const
79{
80 if (!canCoalesceChallenge(challenge))
81 return false;
82
83 for (auto& item : m_challenges) {
84 if (item.key != challengeID && item.value.pageID == pageID && ProtectionSpace::compare(challenge.protectionSpace(), item.value.challenge.protectionSpace()))
85 return true;
86 }
87 return false;
88}
89
90Vector<uint64_t> AuthenticationManager::coalesceChallengesMatching(uint64_t challengeID) const
91{
92 auto iterator = m_challenges.find(challengeID);
93 ASSERT(iterator != m_challenges.end());
94
95 auto& challenge = iterator->value;
96
97 Vector<uint64_t> challengesToCoalesce;
98 challengesToCoalesce.append(challengeID);
99
100 if (!canCoalesceChallenge(challenge.challenge))
101 return challengesToCoalesce;
102
103 for (auto& item : m_challenges) {
104 if (item.key != challengeID && item.value.pageID == challenge.pageID && ProtectionSpace::compare(challenge.challenge.protectionSpace(), item.value.challenge.protectionSpace()))
105 challengesToCoalesce.append(item.key);
106 }
107
108 return challengesToCoalesce;
109}
110
111void AuthenticationManager::didReceiveAuthenticationChallenge(PageIdentifier pageID, uint64_t frameID, const AuthenticationChallenge& authenticationChallenge, ChallengeCompletionHandler&& completionHandler)
112{
113 ASSERT(pageID);
114 ASSERT(frameID);
115
116 uint64_t challengeID = addChallengeToChallengeMap({ pageID, authenticationChallenge, WTFMove(completionHandler) });
117
118 // Coalesce challenges in the same protection space and in the same page.
119 if (shouldCoalesceChallenge(pageID, challengeID, authenticationChallenge))
120 return;
121
122 m_process.send(Messages::NetworkProcessProxy::DidReceiveAuthenticationChallenge(pageID, frameID, authenticationChallenge, challengeID));
123}
124
125void AuthenticationManager::didReceiveAuthenticationChallenge(IPC::MessageSender& download, const WebCore::AuthenticationChallenge& authenticationChallenge, ChallengeCompletionHandler&& completionHandler)
126{
127 PageIdentifier dummyPageID;
128 uint64_t challengeID = addChallengeToChallengeMap({ dummyPageID, authenticationChallenge, WTFMove(completionHandler) });
129
130 // Coalesce challenges in the same protection space and in the same page.
131 if (shouldCoalesceChallenge(dummyPageID, challengeID, authenticationChallenge))
132 return;
133
134 download.send(Messages::DownloadProxy::DidReceiveAuthenticationChallenge(authenticationChallenge, challengeID));
135}
136
137void AuthenticationManager::completeAuthenticationChallenge(uint64_t challengeID, AuthenticationChallengeDisposition disposition, WebCore::Credential&& credential)
138{
139 ASSERT(RunLoop::isMain());
140
141 for (auto& coalescedChallengeID : coalesceChallengesMatching(challengeID)) {
142 auto challenge = m_challenges.take(coalescedChallengeID);
143 ASSERT(!challenge.challenge.isNull());
144 challenge.completionHandler(disposition, credential);
145 }
146}
147
148} // namespace WebKit
149