-
-
Notifications
You must be signed in to change notification settings - Fork 3
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[TIMOB-19154]: iOS9 Deprecate NSURLConnection and replace with NSURLSession API #46
Changes from 7 commits
00fad55
2796683
1f971ef
7fcf987
cdfccff
5165ad5
f382dd6
48e39e2
e60267d
7655918
724a0a4
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -215,6 +215,23 @@ - (ClientCertificate *)clientCertificateForHost:(NSString *)host { | |
// Return FALSE unless the NSURLAuthenticationChallenge is for TLS trust | ||
// validation (aka NSURLAuthenticationMethodServerTrust) and this security | ||
// manager was configured to handle the current url. | ||
|
||
-(BOOL)willHandleChallenge:(NSURLAuthenticationChallenge *)challenge forSession:(NSURLSession *)session { | ||
BOOL result = NO; | ||
if ([challenge.protectionSpace.authenticationMethod isEqualToString: NSURLAuthenticationMethodServerTrust] || | ||
[challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodClientCertificate]) | ||
{ | ||
NSURL *currentURL = [NSURL URLWithString:challenge.protectionSpace.host]; | ||
if (currentURL.scheme == nil) { | ||
currentURL = [NSURL URLWithString:[NSString stringWithFormat:@"https://%@",challenge.protectionSpace.host]]; | ||
} | ||
result = [self willHandleURL:currentURL]; | ||
} | ||
|
||
DebugLog(@"%s returns %@, challenge = %@, session = %@ URL = %@", __PRETTY_FUNCTION__, NSStringFromBOOL(result), challenge, session, challenge.protectionSpace.host); | ||
return result; | ||
} | ||
|
||
-(BOOL)willHandleChallenge:(NSURLAuthenticationChallenge *)challenge forConnection:(NSURLConnection *)connection { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We should remove the old NSURLConnection references and bump the module version to the next major. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We should not remove NSURLConnection in this release. If someone is using this module(this version) with SDK < 7.1.0, in that case it will create problem. Reason is, before 7.1.0 APSHTTPClient will use NSURLConnection apis from this module. Rather we should plan to remove NSURLConnection from this module with SDK 8.0.0. Till then most of developer will have moved to SDK 7.1.0+ . I'll create a ticket for same. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That's a good reason. I thought it's not compatible anymore, but thats the other way around right? If devs update to 7.1.0, they also need to update to the latest https module. Talking about that, please bump the version to the next feature version. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes with 7.1.0, latest https is mandatory. Updated version. |
||
BOOL result = NO; | ||
if ([challenge.protectionSpace.authenticationMethod isEqualToString: NSURLAuthenticationMethodServerTrust] || | ||
|
@@ -232,6 +249,192 @@ -(BOOL)willHandleChallenge:(NSURLAuthenticationChallenge *)challenge forConnecti | |
|
||
#pragma mark NSURLConnectionDelegate methods | ||
|
||
-(void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential * _Nullable))completionHandler | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Space |
||
{ | ||
DebugLog(@"%s session = %@, challenge = %@", __PRETTY_FUNCTION__, session, challenge); | ||
|
||
// Normalize the server's host name to lower case. | ||
NSString *host = [task.currentRequest.URL.host lowercaseString]; | ||
|
||
DebugLog(@"%s Normalized host name = %@", __PRETTY_FUNCTION__, host); | ||
|
||
// Get the PinnedURL for this server. | ||
ClientCertificate *pinnedClientCertificate = [self clientCertificateForHost:host]; | ||
NSString *authenticationMethod = [[challenge protectionSpace] authenticationMethod]; | ||
|
||
// Handle Two-phase mutual client-authentification | ||
if ([authenticationMethod isEqualToString:NSURLAuthenticationMethodClientCertificate] && pinnedClientCertificate != nil) { | ||
NSData *p12Data = [NSData dataWithContentsOfURL:pinnedClientCertificate.url]; | ||
|
||
if (!p12Data) { | ||
NSString *reason = [NSString stringWithFormat:@"Certificate data could not be extracted for host = %@.", task.currentRequest.URL.host]; | ||
NSException *exception = [NSException exceptionWithName:NSInternalInconsistencyException | ||
reason:reason | ||
userInfo:nil]; | ||
|
||
@throw exception; | ||
} | ||
|
||
CFDataRef inPKCS12Data = (__bridge CFDataRef)p12Data; | ||
SecIdentityRef identity; | ||
|
||
OSStatus result = [self extractIdentity:&identity from:inPKCS12Data with:pinnedClientCertificate.password]; | ||
|
||
if (result != noErr) { | ||
[challenge.sender cancelAuthenticationChallenge:challenge]; | ||
completionHandler(NSURLSessionAuthChallengeCancelAuthenticationChallenge, nil); | ||
return; | ||
} | ||
|
||
SecCertificateRef certificate = NULL; | ||
SecIdentityCopyCertificate (identity, &certificate); | ||
|
||
const void *certificates[] = { certificate }; | ||
CFArrayRef certificatesArray = CFArrayCreate(kCFAllocatorDefault, certificates, 1, NULL); | ||
|
||
// create a credential from the certificate and ideneity, then reply to the challenge with the credential | ||
NSURLCredential *credential = [NSURLCredential credentialWithIdentity:identity | ||
certificates:(__bridge NSArray*)certificatesArray | ||
persistence:NSURLCredentialPersistencePermanent]; | ||
|
||
[challenge.sender useCredential:credential forAuthenticationChallenge:challenge]; | ||
completionHandler(NSURLSessionAuthChallengeUseCredential, credential); | ||
return; | ||
} | ||
|
||
if (![authenticationMethod isEqualToString: NSURLAuthenticationMethodServerTrust]) { | ||
[challenge.sender cancelAuthenticationChallenge:challenge]; | ||
completionHandler(NSURLSessionAuthChallengeCancelAuthenticationChallenge, nil); | ||
return; | ||
} | ||
|
||
// It is a logic error (i.e. a bug in Titanium) if this method is | ||
// called with a URL the security manager was not configured to | ||
// handle. | ||
if (![self willHandleURL:task.currentRequest.URL]) { | ||
NSString *reason = [NSString stringWithFormat:@"LOGIC ERROR: Titanium bug called this SecurityManager with an unknown host \"%@\". Please report this issue to us at https://jira.appcelerator.org/browse/TIMOB", task.currentRequest.URL.host]; | ||
NSDictionary *userInfo = @{ @"session" : session }; | ||
NSException *exception = [NSException exceptionWithName:NSInternalInconsistencyException | ||
reason:reason | ||
userInfo:userInfo]; | ||
|
||
@throw exception; | ||
} | ||
|
||
|
||
SecTrustRef serverTrust = challenge.protectionSpace.serverTrust; | ||
if(serverTrust == nil) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Space |
||
DebugLog(@"%s FAIL: challenge.protectionSpace.serverTrust is nil", __PRETTY_FUNCTION__); | ||
[challenge.sender cancelAuthenticationChallenge:challenge]; | ||
completionHandler(NSURLSessionAuthChallengeCancelAuthenticationChallenge, nil); | ||
return; | ||
} | ||
|
||
// SecTrustEvaluate performs customary X509 | ||
// checks. Unusual conditions will cause the function to | ||
// return *non-success*. Unusual conditions include an | ||
// expired certifcate or self signed certifcate. | ||
OSStatus status = SecTrustEvaluate(serverTrust, NULL); | ||
if(status != errSecSuccess) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Space |
||
DebugLog(@"%s FAIL: standard TLS validation failed. SecTrustEvaluate returned %@", __PRETTY_FUNCTION__, @(status)); | ||
[challenge.sender cancelAuthenticationChallenge:challenge]; | ||
completionHandler(NSURLSessionAuthChallengeCancelAuthenticationChallenge, nil); | ||
return; | ||
} | ||
|
||
DebugLog(@"%s SecTrustEvaluate returned %@", __PRETTY_FUNCTION__, @(status)); | ||
|
||
// Get the PinnedURL for this server. | ||
PublicKey *pinnedPublicKey = [self publicKeyForHost:host]; | ||
|
||
// It is a logic error (a bug in this SecurityManager class) if this | ||
// security manager does not have a PinnedURL for this server. | ||
if (pinnedPublicKey == nil) { | ||
NSString *reason = [NSString stringWithFormat:@"LOGIC ERROR: appcelerator.https module bug: SecurityManager could not find a PublicKey for host \"%@\". Please report this issue to us at https://jira.appcelerator.org/browse/MOD-1706", task.currentRequest.URL.host]; | ||
NSDictionary *userInfo = @{ @"session" : session }; | ||
NSException *exception = [NSException exceptionWithName:NSInternalInconsistencyException | ||
reason:reason | ||
userInfo:userInfo]; | ||
|
||
@throw exception; | ||
} | ||
|
||
DebugLog(@"%s host %@ pinned to publicKey %@", __PRETTY_FUNCTION__, host, pinnedPublicKey); | ||
|
||
CFIndex count = SecTrustGetCertificateCount(serverTrust); | ||
CFIndex i = 0; | ||
|
||
DebugLog(@"Number of certificates: %ld", count); | ||
|
||
for (i = 0; i < count; i++) { | ||
SecCertificateRef item = SecTrustGetCertificateAtIndex(serverTrust, i); | ||
NSString *desc = (NSString *)CFBridgingRelease(CFCopyDescription(item)); | ||
|
||
DebugLog(@"%ld: %@", i, desc); | ||
} | ||
|
||
// Obtain the server's X509 certificate and public key. | ||
SecCertificateRef serverCertificate = SecTrustGetCertificateAtIndex(serverTrust, pinnedPublicKey.trustChainIndex); | ||
if(serverCertificate == nil) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Space |
||
DebugLog(@"%s FAIL: Could not find the server's X509 certificate in serverTrust", __PRETTY_FUNCTION__); | ||
[challenge.sender cancelAuthenticationChallenge:challenge]; | ||
completionHandler(NSURLSessionAuthChallengeCancelAuthenticationChallenge, nil); | ||
return; | ||
} | ||
|
||
// Create a friendlier Objective-C wrapper around this server's X509 | ||
// certificate. | ||
X509Certificate *x509Certificate = [X509Certificate x509CertificateWithSecCertificate:serverCertificate andTrustChainIndex:pinnedPublicKey.trustChainIndex]; | ||
if (x509Certificate == nil) { | ||
// CFBridgingRelease transfer's ownership of the CFStringRef | ||
// returned by CFCopyDescription to ARC. | ||
NSString *serverCertificateDescription = (NSString *)CFBridgingRelease(CFCopyDescription(serverCertificate)); | ||
NSString *reason = [NSString stringWithFormat:@"LOGIC ERROR: appcelerator.https module bug: SecurityManager could not create an X509Certificate for host \"%@\" using the SecCertificateRef \"%@\". Please report this issue to us at https://jira.appcelerator.org/browse/MOD-1706", task.currentRequest.URL.host, serverCertificateDescription]; | ||
NSDictionary *userInfo = @{ @"x509Certificate" : [NSNull null] }; | ||
NSException *exception = [NSException exceptionWithName:NSInternalInconsistencyException | ||
reason:reason | ||
userInfo:userInfo]; | ||
|
||
@throw exception; | ||
} | ||
|
||
DebugLog(@"%s server's X509 certificate = %@", __PRETTY_FUNCTION__, x509Certificate); | ||
// Get the public key from this server's X509 certificate. | ||
PublicKey *serverPublicKey = x509Certificate.publicKey; | ||
if (serverPublicKey == nil) { | ||
NSString *reason = [NSString stringWithFormat:@"LOGIC ERROR: appcelerator.https module bug: SecurityManager could not find the server's public key for host \"%@\" in the X509 certificate \"%@\". Please report this issue to us at https://jira.appcelerator.org/browse/MOD-1706", task.currentRequest.URL.host, x509Certificate]; | ||
NSDictionary *userInfo = @{ @"x509Certificate" : x509Certificate }; | ||
NSException *exception = [NSException exceptionWithName:NSInternalInconsistencyException | ||
reason:reason | ||
userInfo:userInfo]; | ||
|
||
@throw exception; | ||
} | ||
|
||
DebugLog(@"%s server's public key = %@", __PRETTY_FUNCTION__, serverPublicKey); | ||
|
||
// Compare the public keys. If they match, then the server is | ||
// authenticated. | ||
BOOL publicKeysAreEqual = [pinnedPublicKey isEqualToPublicKey:serverPublicKey]; | ||
if(!publicKeysAreEqual) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Space |
||
DebugLog(@"[WARN] Potential \"Man-in-the-Middle\" attack detected since host %@ does not hold the private key corresponding to the public key %@.", host, pinnedPublicKey); | ||
|
||
NSDictionary *userDict = @{@"pinnedPublicKey": pinnedPublicKey, @"serverPublicKey": serverPublicKey }; | ||
|
||
NSException *exception = [NSException exceptionWithName:NSInvalidArgumentException | ||
reason:@"Certificate could not be verified with provided public key" | ||
userInfo:userDict]; | ||
@throw exception; | ||
} | ||
|
||
DebugLog(@"%s publicKeysAreEqual = %@", __PRETTY_FUNCTION__, NSStringFromBOOL(publicKeysAreEqual)); | ||
// Return success since the server holds the private key | ||
// corresponding to the public key held bu this security manager. | ||
NSURLCredential *credential = [NSURLCredential credentialForTrust:serverTrust]; | ||
[challenge.sender useCredential:credential forAuthenticationChallenge:challenge]; | ||
completionHandler(NSURLSessionAuthChallengeUseCredential, credential); | ||
} | ||
|
||
- (void)connection:(NSURLConnection *)connection willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge | ||
{ | ||
DebugLog(@"%s connection = %@, challenge = %@", __PRETTY_FUNCTION__, connection, challenge); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Space