Merging SSL certificate validation support from #131

This commit is contained in:
Blake Watters
2011-07-27 08:40:33 -04:00
parent bd29ee038b
commit d5cc021014
7 changed files with 135 additions and 18 deletions

View File

@@ -130,6 +130,8 @@ NSString* RKPathAppendQueryParams(NSString* resourcePath, NSDictionary* queryPar
BOOL _serviceUnavailableAlertEnabled;
RKRequestCache* _cache;
RKRequestCachePolicy _cachePolicy;
NSMutableSet *_additionalRootCertificates;
BOOL _disableCertificateValidation;
}
/////////////////////////////////////////////////////////////////////////
@@ -146,6 +148,22 @@ NSString* RKPathAppendQueryParams(NSString* resourcePath, NSDictionary* queryPar
*/
@property(nonatomic, readonly) NSMutableDictionary* HTTPHeaders;
#ifdef RESTKIT_SSL_VALIDATION
/**
* A set of additional certificates to be used in evaluating server
* SSL certificates.
*/
@property(nonatomic, readonly) NSSet* additionalRootCertificates;
#endif
/**
* Accept all SSL certificates. This is a potential security exposure,
* and should be used ONLY while debugging in a controlled environment.
*
* *Default*: _NO_
*/
@property(nonatomic, assign) BOOL disableCertificateValidation;
/**
* Will check for network connectivity to the host specified in the baseURL
*
@@ -164,6 +182,16 @@ NSString* RKPathAppendQueryParams(NSString* resourcePath, NSDictionary* queryPar
*/
- (void)setValue:(NSString*)value forHTTPHeaderField:(NSString*)header;
#ifdef RESTKIT_SSL_VALIDATION
/**
* Adds an additional certificate that will be used to evaluate server SSL certs
*
* @param cert The HTTP header to add
* @see additionalRootCertificates
*/
- (void)addRootCertificate:(SecCertificateRef)cert;
#endif
/////////////////////////////////////////////////////////////////////////
/// @name HTTP Authentication
/////////////////////////////////////////////////////////////////////////

View File

@@ -88,6 +88,10 @@ NSString* RKPathAppendQueryParams(NSString* resourcePath, NSDictionary* queryPar
@synthesize password = _password;
@synthesize forceBasicAuthentication = _forceBasicAuthentication;
@synthesize HTTPHeaders = _HTTPHeaders;
#ifdef RESTKIT_SSL_VALIDATION
@synthesize additionalRootCertificates = _additionalRootCertificates;
#endif
@synthesize disableCertificateValidation = _disableCertificateValidation;
@synthesize baseURLReachabilityObserver = _baseURLReachabilityObserver;
@synthesize serviceUnavailableAlertTitle = _serviceUnavailableAlertTitle;
@synthesize serviceUnavailableAlertMessage = _serviceUnavailableAlertMessage;
@@ -121,6 +125,7 @@ NSString* RKPathAppendQueryParams(NSString* resourcePath, NSDictionary* queryPar
self = [super init];
if (self) {
_HTTPHeaders = [[NSMutableDictionary alloc] init];
_additionalRootCertificates = [[NSMutableSet alloc] init];
self.serviceUnavailableAlertEnabled = NO;
self.serviceUnavailableAlertTitle = NSLocalizedString(@"Service Unavailable", nil);
self.serviceUnavailableAlertMessage = NSLocalizedString(@"The remote resource is unavailable. Please try again later.", nil);
@@ -158,6 +163,7 @@ NSString* RKPathAppendQueryParams(NSString* resourcePath, NSDictionary* queryPar
self.serviceUnavailableAlertMessage = nil;
self.cache = nil;
[_HTTPHeaders release];
[_additionalRootCertificates release];
[_baseURLReachabilityObserver release];
[super dealloc];
@@ -209,6 +215,12 @@ NSString* RKPathAppendQueryParams(NSString* resourcePath, NSDictionary* queryPar
[_HTTPHeaders setValue:value forKey:header];
}
#ifdef RESTKIT_SSL_VALIDATION
- (void)addRootCertificate:(SecCertificateRef)cert {
[_additionalRootCertificates addObject:(id)cert];
}
#endif
- (void)setBaseURL:(NSString*)baseURL {
[_baseURL release];
_baseURL = nil;

View File

@@ -76,4 +76,9 @@
*/
- (RKParamsAttachment*)setFile:(NSString*)filePath MIMEType:(NSString*)MIMEType fileName:(NSString*)fileName forParam:(NSString*)param DEPRECATED_ATTRIBUTE;
/**
* Resets the state of the RKParams stream
*/
- (void)reset;
@end

View File

@@ -127,6 +127,12 @@ NSString* const kRKStringBoundary = @"0xKhTmLbOuNdArY";
return _length;
}
- (void)reset {
_bytesDelivered = 0;
_length = 0;
_streamStatus = NSStreamStatusNotOpen;
}
- (NSInputStream*)HTTPBodyStream {
// Open each of our attachments
[_attachments makeObjectsPerformSelector:@selector(open)];

View File

@@ -11,6 +11,7 @@
#import "RKNetwork.h"
#import "RKLog.h"
#import "RKParserRegistry.h"
#import "RKClient.h"
// Set Logging Component
#undef RKLogComponent
@@ -87,26 +88,90 @@ extern NSString* cacheURLKey;
return _request.username && _request.password;
}
// Handle basic auth
- (BOOL)isServerTrusted:(SecTrustRef)trust {
RKClient* client = [RKClient sharedClient];
BOOL proceed = NO;
if( client.disableCertificateValidation ) {
proceed = YES;
}
#ifdef RESTKIT_SSL_VALIDATION
else if( [client.additionalRootCertificates count] > 0 ) {
CFArrayRef rootCerts = (CFArrayRef)[client.additionalRootCertificates allObjects];
SecTrustResultType result;
OSStatus returnCode;
if( rootCerts && CFArrayGetCount(rootCerts) ) {
// this could fail, but the trust evaluation will proceed (it's likely to fail, of course)
SecTrustSetAnchorCertificates(trust, rootCerts);
}
returnCode = SecTrustEvaluate(trust, &result);
if( returnCode == errSecSuccess ) {
proceed = (result == kSecTrustResultProceed || result == kSecTrustResultConfirm || result == kSecTrustResultUnspecified);
if( result == kSecTrustResultRecoverableTrustFailure ) {
// TODO: should try to recover here
// call SecTrustGetCssmResult() for more information about the failure
}
}
}
#endif
return proceed;
}
// Handle basic auth & SSL certificate validation
- (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge {
RKLogDebug(@"Received authentication challenge");
if (! [self hasCredentials]) {
RKLogWarning(@"Received an authentication challenge without any credentials to satify the request.");
[[challenge sender] cancelAuthenticationChallenge:challenge];
return;
if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {
SecTrustRef trust = [[challenge protectionSpace] serverTrust];
if ([self isServerTrusted:trust]) {
[challenge.sender useCredential:[NSURLCredential credentialForTrust:trust] forAuthenticationChallenge:challenge];
} else {
[[challenge sender] cancelAuthenticationChallenge:challenge];
}
return;
}
if ([challenge previousFailureCount] == 0) {
NSURLCredential *newCredential;
newCredential=[NSURLCredential credentialWithUser:[NSString stringWithFormat:@"%@", _request.username]
password:[NSString stringWithFormat:@"%@", _request.password]
persistence:RKNetworkGetGlobalCredentialPersistence()];
[[challenge sender] useCredential:newCredential
forAuthenticationChallenge:challenge];
} else {
RKLogWarning(@"Failed authentication challenge after %d failures", [challenge previousFailureCount]);
[[challenge sender] cancelAuthenticationChallenge:challenge];
}
}
- (BOOL)connection:(NSURLConnection *)connection canAuthenticateAgainstProtectionSpace:(NSURLProtectionSpace *)space {
RKLogDebug(@"Asked if canAuthenticateAgainstProtectionSpace: with authenticationMethod = %@", [space authenticationMethod]);
if ([[space authenticationMethod] isEqualToString:NSURLAuthenticationMethodServerTrust]) {
// server is using an SSL certificate that the OS can't validate
// see whether the client settings allow validation here
RKClient* client = [RKClient sharedClient];
if (client.disableCertificateValidation
#ifdef RESTKIT_SSL_VALIDATION
|| [client.additionalRootCertificates count] > 0
#endif
) {
return YES;
} else {
return NO;
}
}
// Handle non-SSL challenges
BOOL hasCredentials = [self hasCredentials];
if (! hasCredentials) {
RKLogWarning(@"Received an authentication challenge without any credentials to satisfy the request.");
}
if ([challenge previousFailureCount] == 0) {
NSURLCredential *newCredential;
newCredential = [NSURLCredential credentialWithUser:[NSString stringWithFormat:@"%@", _request.username]
password:[NSString stringWithFormat:@"%@", _request.password]
persistence:RKNetworkGetGlobalCredentialPersistence()];
[[challenge sender] useCredential:newCredential
forAuthenticationChallenge:challenge];
} else {
RKLogWarning(@"Failed authentication challenge after %d failures", [challenge previousFailureCount]);
[[challenge sender] cancelAuthenticationChallenge:challenge];
}
return hasCredentials;
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {