1/*
2 * Copyright (C) 2007 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 COMPUTER, INC. ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25#import "config.h"
26#import "AuthenticationMac.h"
27
28#if !USE(CFNETWORK)
29
30#import "AuthenticationChallenge.h"
31#import "AuthenticationClient.h"
32#import "Credential.h"
33#import "ProtectionSpace.h"
34
35#import <Foundation/NSURLAuthenticationChallenge.h>
36#import <Foundation/NSURLCredential.h>
37#import <Foundation/NSURLProtectionSpace.h>
38
39using namespace WebCore;
40
41@interface WebCoreAuthenticationClientAsChallengeSender : NSObject <NSURLAuthenticationChallengeSender>
42{
43    AuthenticationClient* m_client;
44}
45- (id)initWithAuthenticationClient:(AuthenticationClient*)client;
46- (AuthenticationClient*)client;
47- (void)detachClient;
48@end
49
50@implementation WebCoreAuthenticationClientAsChallengeSender
51
52- (id)initWithAuthenticationClient:(AuthenticationClient*)client
53{
54    self = [self init];
55    if (!self)
56        return nil;
57    m_client = client;
58    return self;
59}
60
61- (AuthenticationClient*)client
62{
63    return m_client;
64}
65
66- (void)detachClient
67{
68    m_client = 0;
69}
70
71- (void)useCredential:(NSURLCredential *)credential forAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
72{
73    if (m_client)
74        m_client->receivedCredential(core(challenge), core(credential));
75}
76
77- (void)continueWithoutCredentialForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
78{
79    if (m_client)
80        m_client->receivedRequestToContinueWithoutCredential(core(challenge));
81}
82
83- (void)cancelAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
84{
85    if (m_client)
86        m_client->receivedCancellation(core(challenge));
87}
88
89@end
90
91namespace WebCore {
92
93#if defined(BUILDING_ON_TIGER) || defined(BUILDING_ON_LEOPARD)
94// There is no constant in headers, but NTLM is supported.
95NSString * const NSURLAuthenticationMethodNTLM = @"NSURLAuthenticationMethodNTLM";
96#endif
97
98
99AuthenticationChallenge::AuthenticationChallenge(const ProtectionSpace& protectionSpace,
100                                                 const Credential& proposedCredential,
101                                                 unsigned previousFailureCount,
102                                                 const ResourceResponse& response,
103                                                 const ResourceError& error)
104    : AuthenticationChallengeBase(protectionSpace,
105                                  proposedCredential,
106                                  previousFailureCount,
107                                  response,
108                                  error)
109{
110}
111
112AuthenticationChallenge::AuthenticationChallenge(NSURLAuthenticationChallenge *challenge)
113    : AuthenticationChallengeBase(core([challenge protectionSpace]),
114                                  core([challenge proposedCredential]),
115                                  [challenge previousFailureCount],
116                                  [challenge failureResponse],
117                                  [challenge error])
118    , m_sender([challenge sender])
119    , m_nsChallenge(challenge)
120{
121}
122
123void AuthenticationChallenge::setAuthenticationClient(AuthenticationClient* client)
124{
125    if (client) {
126        m_sender.adoptNS([[WebCoreAuthenticationClientAsChallengeSender alloc] initWithAuthenticationClient:client]);
127        m_nsChallenge.adoptNS([[NSURLAuthenticationChallenge alloc] initWithAuthenticationChallenge:m_nsChallenge.get() sender:m_sender.get()]);
128    } else {
129        if ([m_sender.get() isMemberOfClass:[WebCoreAuthenticationClientAsChallengeSender class]])
130            [(WebCoreAuthenticationClientAsChallengeSender *)m_sender.get() detachClient];
131    }
132}
133
134AuthenticationClient* AuthenticationChallenge::authenticationClient() const
135{
136    if ([m_sender.get() isMemberOfClass:[WebCoreAuthenticationClientAsChallengeSender class]])
137        return [static_cast<WebCoreAuthenticationClientAsChallengeSender*>(m_sender.get()) client];
138
139    return 0;
140}
141
142bool AuthenticationChallenge::platformCompare(const AuthenticationChallenge& a, const AuthenticationChallenge& b)
143{
144    if (a.sender() != b.sender())
145        return false;
146
147    if (a.nsURLAuthenticationChallenge() != b.nsURLAuthenticationChallenge())
148        return false;
149
150    return true;
151}
152
153NSURLAuthenticationChallenge *mac(const AuthenticationChallenge& coreChallenge)
154{
155    if (coreChallenge.nsURLAuthenticationChallenge())
156        return coreChallenge.nsURLAuthenticationChallenge();
157
158    return [[[NSURLAuthenticationChallenge alloc] initWithProtectionSpace:mac(coreChallenge.protectionSpace())
159                                                       proposedCredential:mac(coreChallenge.proposedCredential())
160                                                     previousFailureCount:coreChallenge.previousFailureCount()
161                                                          failureResponse:coreChallenge.failureResponse().nsURLResponse()
162                                                                    error:coreChallenge.error()
163                                                                   sender:coreChallenge.sender()] autorelease];
164}
165
166NSURLProtectionSpace *mac(const ProtectionSpace& coreSpace)
167{
168    NSString *proxyType = nil;
169    NSString *protocol = nil;
170    switch (coreSpace.serverType()) {
171        case ProtectionSpaceServerHTTP:
172            protocol = @"http";
173            break;
174        case ProtectionSpaceServerHTTPS:
175            protocol = @"https";
176            break;
177        case ProtectionSpaceServerFTP:
178            protocol = @"ftp";
179            break;
180        case ProtectionSpaceServerFTPS:
181            protocol = @"ftps";
182            break;
183        case ProtectionSpaceProxyHTTP:
184            proxyType = NSURLProtectionSpaceHTTPProxy;
185            break;
186        case ProtectionSpaceProxyHTTPS:
187            proxyType = NSURLProtectionSpaceHTTPSProxy;
188            break;
189        case ProtectionSpaceProxyFTP:
190            proxyType = NSURLProtectionSpaceFTPProxy;
191            break;
192        case ProtectionSpaceProxySOCKS:
193            proxyType = NSURLProtectionSpaceSOCKSProxy;
194            break;
195        default:
196            ASSERT_NOT_REACHED();
197    }
198
199    NSString *method = nil;
200    switch (coreSpace.authenticationScheme()) {
201        case ProtectionSpaceAuthenticationSchemeDefault:
202            method = NSURLAuthenticationMethodDefault;
203            break;
204        case ProtectionSpaceAuthenticationSchemeHTTPBasic:
205            method = NSURLAuthenticationMethodHTTPBasic;
206            break;
207        case ProtectionSpaceAuthenticationSchemeHTTPDigest:
208            method = NSURLAuthenticationMethodHTTPDigest;
209            break;
210        case ProtectionSpaceAuthenticationSchemeHTMLForm:
211            method = NSURLAuthenticationMethodHTMLForm;
212            break;
213        case ProtectionSpaceAuthenticationSchemeNTLM:
214            method = NSURLAuthenticationMethodNTLM;
215            break;
216#if USE(PROTECTION_SPACE_AUTH_CALLBACK)
217        case ProtectionSpaceAuthenticationSchemeServerTrustEvaluationRequested:
218            method = NSURLAuthenticationMethodServerTrust;
219            break;
220        case ProtectionSpaceAuthenticationSchemeClientCertificateRequested:
221            method = NSURLAuthenticationMethodClientCertificate;
222            break;
223#endif
224        default:
225            ASSERT_NOT_REACHED();
226    }
227
228    if (proxyType)
229        return [[[NSURLProtectionSpace alloc] initWithProxyHost:coreSpace.host()
230                                                           port:coreSpace.port()
231                                                           type:proxyType
232                                                          realm:coreSpace.realm()
233                                           authenticationMethod:method] autorelease];
234    return [[[NSURLProtectionSpace alloc] initWithHost:coreSpace.host()
235                                                  port:coreSpace.port()
236                                              protocol:protocol
237                                                 realm:coreSpace.realm()
238                                  authenticationMethod:method] autorelease];
239}
240
241NSURLCredential *mac(const Credential& coreCredential)
242{
243    if (coreCredential.isEmpty())
244        return nil;
245
246    NSURLCredentialPersistence persistence = NSURLCredentialPersistenceNone;
247    switch (coreCredential.persistence()) {
248        case CredentialPersistenceNone:
249            break;
250        case CredentialPersistenceForSession:
251            persistence = NSURLCredentialPersistenceForSession;
252            break;
253        case CredentialPersistencePermanent:
254            persistence = NSURLCredentialPersistencePermanent;
255            break;
256        default:
257            ASSERT_NOT_REACHED();
258    }
259
260#if CERTIFICATE_CREDENTIALS_SUPPORTED
261    if (coreCredential.type() == CredentialTypeClientCertificate) {
262        return [[[NSURLCredential alloc] initWithIdentity:coreCredential.identity()
263                                             certificates:(NSArray *)coreCredential.certificates()
264                                              persistence:persistence]
265                                              autorelease];
266    }
267#endif
268
269    return [[[NSURLCredential alloc] initWithUser:coreCredential.user()
270                                        password:coreCredential.password()
271                                     persistence:persistence]
272                                     autorelease];
273}
274
275AuthenticationChallenge core(NSURLAuthenticationChallenge *macChallenge)
276{
277    return AuthenticationChallenge(macChallenge);
278}
279
280ProtectionSpace core(NSURLProtectionSpace *macSpace)
281{
282    ProtectionSpaceServerType serverType = ProtectionSpaceProxyHTTP;
283
284    if ([macSpace isProxy]) {
285        NSString *proxyType = [macSpace proxyType];
286        if ([proxyType isEqualToString:NSURLProtectionSpaceHTTPProxy])
287            serverType = ProtectionSpaceProxyHTTP;
288        else if ([proxyType isEqualToString:NSURLProtectionSpaceHTTPSProxy])
289            serverType = ProtectionSpaceProxyHTTPS;
290        else if ([proxyType isEqualToString:NSURLProtectionSpaceFTPProxy])
291            serverType = ProtectionSpaceProxyFTP;
292        else if ([proxyType isEqualToString:NSURLProtectionSpaceSOCKSProxy])
293            serverType = ProtectionSpaceProxySOCKS;
294        else
295            ASSERT_NOT_REACHED();
296    } else {
297        NSString *protocol = [macSpace protocol];
298        if ([protocol caseInsensitiveCompare:@"http"] == NSOrderedSame)
299            serverType = ProtectionSpaceServerHTTP;
300        else if ([protocol caseInsensitiveCompare:@"https"] == NSOrderedSame)
301            serverType = ProtectionSpaceServerHTTPS;
302        else if ([protocol caseInsensitiveCompare:@"ftp"] == NSOrderedSame)
303            serverType = ProtectionSpaceServerFTP;
304        else if ([protocol caseInsensitiveCompare:@"ftps"] == NSOrderedSame)
305            serverType = ProtectionSpaceServerFTPS;
306        else
307            ASSERT_NOT_REACHED();
308    }
309
310    ProtectionSpaceAuthenticationScheme scheme = ProtectionSpaceAuthenticationSchemeDefault;
311    NSString *method = [macSpace authenticationMethod];
312    if ([method isEqualToString:NSURLAuthenticationMethodDefault])
313        scheme = ProtectionSpaceAuthenticationSchemeDefault;
314    else if ([method isEqualToString:NSURLAuthenticationMethodHTTPBasic])
315        scheme = ProtectionSpaceAuthenticationSchemeHTTPBasic;
316    else if ([method isEqualToString:NSURLAuthenticationMethodHTTPDigest])
317        scheme = ProtectionSpaceAuthenticationSchemeHTTPDigest;
318    else if ([method isEqualToString:NSURLAuthenticationMethodHTMLForm])
319        scheme = ProtectionSpaceAuthenticationSchemeHTMLForm;
320    else if ([method isEqualToString:NSURLAuthenticationMethodNTLM])
321        scheme = ProtectionSpaceAuthenticationSchemeNTLM;
322#if USE(PROTECTION_SPACE_AUTH_CALLBACK)
323    else if ([method isEqualToString:NSURLAuthenticationMethodClientCertificate])
324        scheme = ProtectionSpaceAuthenticationSchemeClientCertificateRequested;
325    else if ([method isEqualToString:NSURLAuthenticationMethodServerTrust])
326        scheme = ProtectionSpaceAuthenticationSchemeServerTrustEvaluationRequested;
327#endif
328    else {
329        scheme = ProtectionSpaceAuthenticationSchemeUnknown;
330        ASSERT_NOT_REACHED();
331    }
332
333    return ProtectionSpace([macSpace host], [macSpace port], serverType, [macSpace realm], scheme);
334
335}
336
337Credential core(NSURLCredential *macCredential)
338{
339    CredentialPersistence persistence = CredentialPersistenceNone;
340    switch ([macCredential persistence]) {
341        case NSURLCredentialPersistenceNone:
342            break;
343        case NSURLCredentialPersistenceForSession:
344            persistence = CredentialPersistenceForSession;
345            break;
346        case NSURLCredentialPersistencePermanent:
347            persistence = CredentialPersistencePermanent;
348            break;
349        default:
350            ASSERT_NOT_REACHED();
351    }
352
353#if CERTIFICATE_CREDENTIALS_SUPPORTED
354    SecIdentityRef identity = [macCredential identity];
355    if (identity)
356        return Credential(identity, (CFArrayRef)[macCredential certificates], persistence);
357#endif
358
359    return Credential([macCredential user], [macCredential password], persistence);
360}
361
362} // namespace WebCore
363
364#endif // !USE(CFNETWORK)
365