7df723193f6177662c73ccc298937478cf434d7e
[WebKit-https.git] / Source / WebKit / Shared / Authentication / AuthenticationManager.cpp
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 "ChildProcess.h"
31 #include "Download.h"
32 #include "DownloadProxyMessages.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 #include <WebCore/AuthenticationClient.h>
41
42 using namespace WebCore;
43
44 namespace WebKit {
45
46 static uint64_t generateAuthenticationChallengeID()
47 {
48     ASSERT(RunLoop::isMain());
49
50     static int64_t uniqueAuthenticationChallengeID;
51     return ++uniqueAuthenticationChallengeID;
52 }
53
54 static bool canCoalesceChallenge(const WebCore::AuthenticationChallenge& challenge)
55 {
56     // Do not coalesce server trust evaluation requests because ProtectionSpace comparison does not evaluate server trust (e.g. certificate).
57     return challenge.protectionSpace().authenticationScheme() != ProtectionSpaceAuthenticationSchemeServerTrustEvaluationRequested;
58 }
59
60 const char* AuthenticationManager::supplementName()
61 {
62     return "AuthenticationManager";
63 }
64
65 AuthenticationManager::AuthenticationManager(ChildProcess* process)
66     : m_process(process)
67 {
68     m_process->addMessageReceiver(Messages::AuthenticationManager::messageReceiverName(), *this);
69 }
70
71 uint64_t AuthenticationManager::addChallengeToChallengeMap(Challenge&& challenge)
72 {
73     ASSERT(RunLoop::isMain());
74
75     uint64_t challengeID = generateAuthenticationChallengeID();
76     m_challenges.set(challengeID, WTFMove(challenge));
77     return challengeID;
78 }
79
80 bool AuthenticationManager::shouldCoalesceChallenge(uint64_t pageID, uint64_t challengeID, const AuthenticationChallenge& challenge) const
81 {
82     if (!canCoalesceChallenge(challenge))
83         return false;
84
85     for (auto& item : m_challenges) {
86         if (item.key != challengeID && item.value.pageID == pageID && ProtectionSpace::compare(challenge.protectionSpace(), item.value.challenge.protectionSpace()))
87             return true;
88     }
89     return false;
90 }
91
92 Vector<uint64_t> AuthenticationManager::coalesceChallengesMatching(uint64_t challengeID) const
93 {
94     auto iterator = m_challenges.find(challengeID);
95     ASSERT(iterator != m_challenges.end());
96
97     auto& challenge = iterator->value;
98
99     Vector<uint64_t> challengesToCoalesce;
100     challengesToCoalesce.append(challengeID);
101
102     if (!canCoalesceChallenge(challenge.challenge))
103         return challengesToCoalesce;
104
105     for (auto& item : m_challenges) {
106         if (item.key != challengeID && item.value.pageID == challenge.pageID && ProtectionSpace::compare(challenge.challenge.protectionSpace(), item.value.challenge.protectionSpace()))
107             challengesToCoalesce.append(item.key);
108     }
109
110     return challengesToCoalesce;
111 }
112
113 void AuthenticationManager::didReceiveAuthenticationChallenge(WebFrame* frame, const AuthenticationChallenge& authenticationChallenge)
114 {
115     ASSERT(frame);
116     ASSERT(frame->page());
117
118     auto pageID = frame->page()->pageID();
119     uint64_t challengeID = addChallengeToChallengeMap({pageID, authenticationChallenge
120 #if USE(NETWORK_SESSION)
121         , { }
122 #endif
123     });
124
125     // Coalesce challenges in the same protection space and in the same page.
126     if (shouldCoalesceChallenge(pageID, challengeID, authenticationChallenge))
127         return;
128     
129     m_process->send(Messages::WebPageProxy::DidReceiveAuthenticationChallenge(frame->frameID(), authenticationChallenge, challengeID), frame->page()->pageID());
130 }
131
132 #if USE(NETWORK_SESSION)
133 void AuthenticationManager::didReceiveAuthenticationChallenge(uint64_t pageID, uint64_t frameID, const AuthenticationChallenge& authenticationChallenge, ChallengeCompletionHandler&& completionHandler)
134 {
135     ASSERT(pageID);
136     ASSERT(frameID);
137
138     uint64_t challengeID = addChallengeToChallengeMap({ pageID, authenticationChallenge, WTFMove(completionHandler) });
139
140     // Coalesce challenges in the same protection space and in the same page.
141     if (shouldCoalesceChallenge(pageID, challengeID, authenticationChallenge))
142         return;
143     
144     m_process->send(Messages::NetworkProcessProxy::DidReceiveAuthenticationChallenge(pageID, frameID, authenticationChallenge, challengeID));
145 }
146
147 void AuthenticationManager::didReceiveAuthenticationChallenge(PendingDownload& pendingDownload, const WebCore::AuthenticationChallenge& authenticationChallenge, ChallengeCompletionHandler&& completionHandler)
148 {
149     uint64_t dummyPageID = 0;
150     uint64_t challengeID = addChallengeToChallengeMap({ dummyPageID, authenticationChallenge, WTFMove(completionHandler) });
151     
152     // Coalesce challenges in the same protection space and in the same page.
153     if (shouldCoalesceChallenge(dummyPageID, challengeID, authenticationChallenge))
154         return;
155     
156     pendingDownload.send(Messages::DownloadProxy::DidReceiveAuthenticationChallenge(authenticationChallenge, challengeID));
157 }
158 #endif
159
160 #if !USE(NETWORK_SESSION)
161 void AuthenticationManager::didReceiveAuthenticationChallenge(uint64_t pageID, uint64_t frameID, const AuthenticationChallenge& authenticationChallenge)
162 {
163     ASSERT(pageID);
164     ASSERT(frameID);
165
166     uint64_t challengeID = addChallengeToChallengeMap({pageID, authenticationChallenge});
167
168     // Coalesce challenges in the same protection space and in the same page.
169     if (shouldCoalesceChallenge(pageID, challengeID, authenticationChallenge))
170         return;
171     
172     m_process->send(Messages::NetworkProcessProxy::DidReceiveAuthenticationChallenge(pageID, frameID, authenticationChallenge, challengeID));
173 }
174 #endif
175
176 #if !USE(NETWORK_SESSION)
177 void AuthenticationManager::didReceiveAuthenticationChallenge(Download& download, const AuthenticationChallenge& authenticationChallenge)
178 {
179     uint64_t dummyPageID = 0;
180     uint64_t challengeID = addChallengeToChallengeMap({dummyPageID, authenticationChallenge});
181
182     // Coalesce challenges in the same protection space and in the same page.
183     if (shouldCoalesceChallenge(dummyPageID, challengeID, authenticationChallenge))
184         return;
185
186     download.send(Messages::DownloadProxy::DidReceiveAuthenticationChallenge(authenticationChallenge, challengeID));
187 }
188 #endif
189
190 // Currently, only Mac knows how to respond to authentication challenges with certificate info.
191 #if !HAVE(SEC_IDENTITY)
192 bool AuthenticationManager::tryUseCertificateInfoForChallenge(const WebCore::AuthenticationChallenge&, const CertificateInfo&, const ChallengeCompletionHandler&)
193 {
194     return false;
195 }
196 #endif
197
198 void AuthenticationManager::useCredentialForChallenge(uint64_t challengeID, const Credential& credential, const CertificateInfo& certificateInfo)
199 {
200     ASSERT(RunLoop::isMain());
201
202     for (auto& coalescedChallengeID : coalesceChallengesMatching(challengeID))
203         useCredentialForSingleChallenge(coalescedChallengeID, credential, certificateInfo);
204 }
205
206 void AuthenticationManager::useCredentialForSingleChallenge(uint64_t challengeID, const Credential& credential, const CertificateInfo& certificateInfo)
207 {
208     auto challenge = m_challenges.take(challengeID);
209     ASSERT(!challenge.challenge.isNull());
210
211 #if USE(NETWORK_SESSION)
212     auto completionHandler = WTFMove(challenge.completionHandler);
213 #else
214     ChallengeCompletionHandler completionHandler = nullptr;
215 #endif
216     
217     if (tryUseCertificateInfoForChallenge(challenge.challenge, certificateInfo, completionHandler))
218         return;
219
220     AuthenticationClient* coreClient = challenge.challenge.authenticationClient();
221 #if USE(NETWORK_SESSION)
222     // If there is a completion handler, then there is no AuthenticationClient.
223     // FIXME: Remove the use of AuthenticationClient in WebKit2 once NETWORK_SESSION is used for all loads.
224     if (completionHandler) {
225         ASSERT(!coreClient);
226         completionHandler(AuthenticationChallengeDisposition::UseCredential, credential);
227         return;
228     }
229 #endif
230
231     if (coreClient)
232         coreClient->receivedCredential(challenge.challenge, credential);
233     else
234         receivedCredential(challenge.challenge, credential);
235 }
236
237 void AuthenticationManager::continueWithoutCredentialForChallenge(uint64_t challengeID)
238 {
239     ASSERT(RunLoop::isMain());
240
241     for (auto& coalescedChallengeID : coalesceChallengesMatching(challengeID))
242         continueWithoutCredentialForSingleChallenge(coalescedChallengeID);
243 }
244
245 void AuthenticationManager::continueWithoutCredentialForSingleChallenge(uint64_t challengeID)
246 {
247     auto challenge = m_challenges.take(challengeID);
248     ASSERT(!challenge.challenge.isNull());
249
250     AuthenticationClient* coreClient = challenge.challenge.authenticationClient();
251 #if USE(NETWORK_SESSION)
252     if (challenge.completionHandler) {
253         ASSERT(!coreClient);
254         challenge.completionHandler(AuthenticationChallengeDisposition::UseCredential, Credential());
255         return;
256     }
257 #endif
258
259     if (coreClient)
260         coreClient->receivedRequestToContinueWithoutCredential(challenge.challenge);
261     else
262         receivedRequestToContinueWithoutCredential(challenge.challenge);
263 }
264
265 void AuthenticationManager::cancelChallenge(uint64_t challengeID)
266 {
267     ASSERT(RunLoop::isMain());
268
269     for (auto& coalescedChallengeID : coalesceChallengesMatching(challengeID))
270         cancelSingleChallenge(coalescedChallengeID);
271 }
272
273 void AuthenticationManager::cancelSingleChallenge(uint64_t challengeID)
274 {
275     auto challenge = m_challenges.take(challengeID);
276     ASSERT(!challenge.challenge.isNull());
277
278     AuthenticationClient* coreClient = challenge.challenge.authenticationClient();
279 #if USE(NETWORK_SESSION)
280     if (challenge.completionHandler) {
281         ASSERT(!coreClient);
282         challenge.completionHandler(AuthenticationChallengeDisposition::Cancel, Credential());
283         return;
284     }
285 #endif
286
287     if (coreClient)
288         coreClient->receivedCancellation(challenge.challenge);
289     else
290         receivedCancellation(challenge.challenge);
291 }
292
293 void AuthenticationManager::performDefaultHandling(uint64_t challengeID)
294 {
295     ASSERT(RunLoop::isMain());
296
297     for (auto& coalescedChallengeID : coalesceChallengesMatching(challengeID))
298         performDefaultHandlingForSingleChallenge(coalescedChallengeID);
299 }
300
301 void AuthenticationManager::performDefaultHandlingForSingleChallenge(uint64_t challengeID)
302 {
303     auto challenge = m_challenges.take(challengeID);
304     ASSERT(!challenge.challenge.isNull());
305
306     AuthenticationClient* coreClient = challenge.challenge.authenticationClient();
307 #if USE(NETWORK_SESSION)
308     if (challenge.completionHandler) {
309         ASSERT(!coreClient);
310         challenge.completionHandler(AuthenticationChallengeDisposition::PerformDefaultHandling, Credential());
311         return;
312     }
313 #endif
314
315     if (coreClient)
316         coreClient->receivedRequestToPerformDefaultHandling(challenge.challenge);
317     else
318         receivedRequestToPerformDefaultHandling(challenge.challenge);
319 }
320
321 void AuthenticationManager::rejectProtectionSpaceAndContinue(uint64_t challengeID)
322 {
323     ASSERT(RunLoop::isMain());
324
325     for (auto& coalescedChallengeID : coalesceChallengesMatching(challengeID))
326         rejectProtectionSpaceAndContinueForSingleChallenge(coalescedChallengeID);
327 }
328
329 void AuthenticationManager::rejectProtectionSpaceAndContinueForSingleChallenge(uint64_t challengeID)
330 {
331     auto challenge = m_challenges.take(challengeID);
332     ASSERT(!challenge.challenge.isNull());
333
334     AuthenticationClient* coreClient = challenge.challenge.authenticationClient();
335 #if USE(NETWORK_SESSION)
336     if (challenge.completionHandler) {
337         ASSERT(!coreClient);
338         challenge.completionHandler(AuthenticationChallengeDisposition::RejectProtectionSpace, Credential());
339         return;
340     }
341 #endif
342
343     if (coreClient)
344         coreClient->receivedChallengeRejection(challenge.challenge);
345     else
346         receivedChallengeRejection(challenge.challenge);
347 }
348
349 } // namespace WebKit