Compare commits

...

9 Commits

Author SHA1 Message Date
Michael Kirk
587ad297eb clarifying comment 2016-06-14 09:44:41 -07:00
Michael Kirk
d235027f7a use existing NSURLProtocol machinery to annotate request rather than importing objc runtime 2016-06-14 09:36:49 -07:00
Michael Kirk
676f8f9b55 nullable annotations 2016-06-14 09:23:55 -07:00
Michael Kirk
5a26ea8bbf Extract and rename SecurityPolicy for uniform vocabulary 2016-06-13 20:46:36 -07:00
Michael Kirk
a319238e62 minimize upstream diff after rebase 2016-06-13 20:06:28 -07:00
Michael Kirk
066a3c0fbb namespace categories 2016-06-13 20:06:02 -07:00
Frederic Jacobs
bf0414499b Adding OS X libObjC. 2016-06-13 15:44:35 -07:00
Frederic Jacobs
d6d9121183 Adding libObjc to Podspec 2016-06-13 15:44:35 -07:00
Frederic Jacobs
39df52b56c Require TLS 1.2 & enable pinning. 2016-06-13 15:44:35 -07:00
6 changed files with 65 additions and 59 deletions

View File

@ -17,6 +17,10 @@
3345DC871C52ACD70083CCB8 /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F6A12CD3145122FC00C1D980 /* Security.framework */; };
3345DC881C52ACD70083CCB8 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F6B208301450F597009315AF /* Foundation.framework */; };
3345DC8A1C52ACD70083CCB8 /* SRWebSocket.h in Headers */ = {isa = PBXBuildFile; fileRef = F6A12CCF145119B700C1D980 /* SRWebSocket.h */; settings = {ATTRIBUTES = (Public, ); }; };
454A02D51D0FAD010060DFB2 /* SRSecurityPolicy.h in Headers */ = {isa = PBXBuildFile; fileRef = 454A02D41D0FAD010060DFB2 /* SRSecurityPolicy.h */; settings = {ATTRIBUTES = (Public, ); }; };
454A02D61D0FAD010060DFB2 /* SRSecurityPolicy.h in Headers */ = {isa = PBXBuildFile; fileRef = 454A02D41D0FAD010060DFB2 /* SRSecurityPolicy.h */; };
454A02D71D0FAD010060DFB2 /* SRSecurityPolicy.h in Headers */ = {isa = PBXBuildFile; fileRef = 454A02D41D0FAD010060DFB2 /* SRSecurityPolicy.h */; };
454A02D81D0FAD010060DFB2 /* SRSecurityPolicy.h in Headers */ = {isa = PBXBuildFile; fileRef = 454A02D41D0FAD010060DFB2 /* SRSecurityPolicy.h */; };
4861E7751D022211002FAB1D /* SRProxyConnect.h in Headers */ = {isa = PBXBuildFile; fileRef = 4861E7731D022211002FAB1D /* SRProxyConnect.h */; };
4861E7761D022211002FAB1D /* SRProxyConnect.m in Sources */ = {isa = PBXBuildFile; fileRef = 4861E7741D022211002FAB1D /* SRProxyConnect.m */; };
555E0EB41C51E57A00E6BB92 /* SocketRocket.h in Headers */ = {isa = PBXBuildFile; fileRef = 555E0EB11C51E56D00E6BB92 /* SocketRocket.h */; settings = {ATTRIBUTES = (Public, ); }; };
@ -144,6 +148,7 @@
/* Begin PBXFileReference section */
2D4227621BB4358C000C1A6C /* SocketRocket.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SocketRocket.framework; sourceTree = BUILT_PRODUCTS_DIR; };
3345DC901C52ACD70083CCB8 /* SocketRocket.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SocketRocket.framework; sourceTree = BUILT_PRODUCTS_DIR; };
454A02D41D0FAD010060DFB2 /* SRSecurityPolicy.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SRSecurityPolicy.h; path = SocketRocket/SRSecurityPolicy.h; sourceTree = SOURCE_ROOT; };
4861E7731D022211002FAB1D /* SRProxyConnect.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SRProxyConnect.h; sourceTree = "<group>"; };
4861E7741D022211002FAB1D /* SRProxyConnect.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SRProxyConnect.m; sourceTree = "<group>"; };
555E0EB11C51E56D00E6BB92 /* SocketRocket.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SocketRocket.h; sourceTree = "<group>"; };
@ -480,6 +485,7 @@
children = (
81B31C0D1CDC404100D86D43 /* Internal */,
555E0EB11C51E56D00E6BB92 /* SocketRocket.h */,
454A02D41D0FAD010060DFB2 /* SRSecurityPolicy.h */,
F6A12CCF145119B700C1D980 /* SRWebSocket.h */,
F6A12CD0145119B700C1D980 /* SRWebSocket.m */,
81CD05D51CEEC47300497F47 /* NSURLRequest+SRWebSocket.h */,
@ -501,6 +507,7 @@
81B22EE51CE43ECC0073C636 /* SRURLUtilities.h in Headers */,
81B31C151CDC404100D86D43 /* SRIOConsumer.h in Headers */,
81CD05FE1CEEC65D00497F47 /* NSRunLoop+SRWebSocket.h in Headers */,
454A02D61D0FAD010060DFB2 /* SRSecurityPolicy.h in Headers */,
81CD05D81CEEC47300497F47 /* NSURLRequest+SRWebSocket.h in Headers */,
81B31C1D1CDC404100D86D43 /* SRIOConsumerPool.h in Headers */,
2D42277F1BB4365C000C1A6C /* SRWebSocket.h in Headers */,
@ -519,6 +526,7 @@
81B22EE71CE43ECC0073C636 /* SRURLUtilities.h in Headers */,
81B31C171CDC404100D86D43 /* SRIOConsumer.h in Headers */,
81CD06001CEEC65D00497F47 /* NSRunLoop+SRWebSocket.h in Headers */,
454A02D81D0FAD010060DFB2 /* SRSecurityPolicy.h in Headers */,
81CD05DA1CEEC47300497F47 /* NSURLRequest+SRWebSocket.h in Headers */,
81B31C1F1CDC404100D86D43 /* SRIOConsumerPool.h in Headers */,
3345DC8A1C52ACD70083CCB8 /* SRWebSocket.h in Headers */,
@ -537,6 +545,7 @@
81B22EE61CE43ECC0073C636 /* SRURLUtilities.h in Headers */,
81B31C161CDC404100D86D43 /* SRIOConsumer.h in Headers */,
81CD05FF1CEEC65D00497F47 /* NSRunLoop+SRWebSocket.h in Headers */,
454A02D71D0FAD010060DFB2 /* SRSecurityPolicy.h in Headers */,
81CD05D91CEEC47300497F47 /* NSURLRequest+SRWebSocket.h in Headers */,
81B31C1E1CDC404100D86D43 /* SRIOConsumerPool.h in Headers */,
F668C8AA153E92F90044DBAC /* SRWebSocket.h in Headers */,
@ -554,6 +563,7 @@
files = (
81B22EE41CE43ECC0073C636 /* SRURLUtilities.h in Headers */,
81B31C141CDC404100D86D43 /* SRIOConsumer.h in Headers */,
454A02D51D0FAD010060DFB2 /* SRSecurityPolicy.h in Headers */,
81CD05FD1CEEC65D00497F47 /* NSRunLoop+SRWebSocket.h in Headers */,
81CD05D71CEEC47300497F47 /* NSURLRequest+SRWebSocket.h in Headers */,
81B31C1C1CDC404100D86D43 /* SRIOConsumerPool.h in Headers */,

View File

@ -10,24 +10,19 @@
//
#import <Foundation/Foundation.h>
#import "SRSecurityPolicy.h"
NS_ASSUME_NONNULL_BEGIN
@interface NSURLRequest (SRWebSocket)
/**
An array of pinned `SecCertificateRef` SSL certificates that `SRWebSocket` will use for validation.
*/
@property (nullable, nonatomic, strong, readonly) NSArray *SR_SSLPinnedCertificates;
@property (nullable, nonatomic, retain, readonly) id<SRSecurityPolicy> SR_securityPolicy;
@end
@interface NSMutableURLRequest (SRWebSocket)
/**
An array of pinned `SecCertificateRef` SSL certificates that `SRWebSocket` will use for validation.
*/
@property (nullable, nonatomic, strong) NSArray *SR_SSLPinnedCertificates;
@property (nullable, nonatomic, retain) id<SRSecurityPolicy> SR_securityPolicy;
@end

View File

@ -13,25 +13,27 @@
NS_ASSUME_NONNULL_BEGIN
NSString * const kSRSecurityPolicy = @"SRSecurityPolicy";
@implementation NSURLRequest (SRWebSocket)
- (nullable NSArray *)SR_SSLPinnedCertificates
- (nullable id<SRSecurityPolicy>)SR_securityPolicy;
{
return [NSURLProtocol propertyForKey:@"SR_SSLPinnedCertificates" inRequest:self];
return [NSURLProtocol propertyForKey:kSRSecurityPolicy inRequest:self];
}
@end
@implementation NSMutableURLRequest (SRWebSocket)
- (nullable NSArray *)SR_SSLPinnedCertificates
- (void)setSR_securityPolicy:(nullable id<SRSecurityPolicy>)securityPolicy
{
return [NSURLProtocol propertyForKey:@"SR_SSLPinnedCertificates" inRequest:self];
}
- (void)setSR_SSLPinnedCertificates:(nullable NSArray *)SR_SSLPinnedCertificates
{
[NSURLProtocol setProperty:SR_SSLPinnedCertificates forKey:@"SR_SSLPinnedCertificates" inRequest:self];
if (![securityPolicy respondsToSelector:@selector(evaluateServerTrust:forDomain:)]) {
@throw [NSException exceptionWithName:@"Assigning security policy failed."
reason:@"Trying to assign a security policy that doesn't respond to required selector"
userInfo:nil];
}
[NSURLProtocol setProperty:securityPolicy forKey:kSRSecurityPolicy inRequest:self];
}
@end

View File

@ -0,0 +1,19 @@
//
// Copyright 2012 Square Inc.
// Portions Copyright (c) 2016-present, Facebook, Inc.
//
// All rights reserved.
//
// This source code is licensed under the BSD-style license found in the
// LICENSE file in the root directory of this source tree. An additional grant
// of patent rights can be found in the PATENTS file in the same directory.
//
#import <Foundation/Foundation.h>
@protocol SRSecurityPolicy <NSObject>
- (BOOL)evaluateServerTrust:(SecTrustRef)serverTrust
forDomain:(NSString*)domain;
@end

View File

@ -159,6 +159,7 @@ NSString *const SRHTTPResponseErrorKey = @"HTTPResponseStatusCode";
assert(request.URL);
_url = request.URL;
_urlRequest = request;
_pinnedCertFound = NO;
_allowsUntrustedSSLCertificates = allowsUntrustedSSLCertificates;
_requestedProtocols = [protocols copy];
@ -407,14 +408,9 @@ NSString *const SRHTTPResponseErrorKey = @"HTTPResponseStatusCode";
NSMutableDictionary<NSString *, NSNumber *> *sslOptions = [NSMutableDictionary dictionary];
// If we're using pinned certs, don't validate the certificate chain
if ([_urlRequest SR_SSLPinnedCertificates].count) {
sslOptions[(__bridge NSString *)kCFStreamSSLValidatesCertificateChain] = @NO;
}
#if DEBUG
self.allowsUntrustedSSLCertificates = YES;
#endif
// Enforce TLS 1.2
[_outputStream setProperty:(__bridge id)CFSTR("kCFStreamSocketSecurityLevelTLSv1_2") forKey:(__bridge id)kCFStreamPropertySocketSecurityLevel];
self.allowsUntrustedSSLCertificates = YES; // hard coded for OWS which always pins certs.
if (self.allowsUntrustedSSLCertificates) {
sslOptions[(__bridge NSString *)kCFStreamSSLValidatesCertificateChain] = @NO;
@ -1391,40 +1387,22 @@ static const size_t SRFrameHeaderOverhead = 32;
if (_secure && !_pinnedCertFound && (eventCode == NSStreamEventHasBytesAvailable || eventCode == NSStreamEventHasSpaceAvailable)) {
NSArray *sslCerts = [_urlRequest SR_SSLPinnedCertificates];
if (sslCerts) {
SecTrustRef secTrust = (__bridge SecTrustRef)[aStream propertyForKey:(__bridge id)kCFStreamPropertySSLPeerTrust];
if (secTrust) {
NSInteger numCerts = SecTrustGetCertificateCount(secTrust);
for (NSInteger i = 0; i < numCerts && !_pinnedCertFound; i++) {
SecCertificateRef cert = SecTrustGetCertificateAtIndex(secTrust, i);
NSData *certData = CFBridgingRelease(SecCertificateCopyData(cert));
for (id ref in sslCerts) {
SecCertificateRef trustedCert = (__bridge SecCertificateRef)ref;
NSData *trustedCertData = CFBridgingRelease(SecCertificateCopyData(trustedCert));
if ([trustedCertData isEqualToData:certData]) {
_pinnedCertFound = YES;
break;
}
}
}
}
if (!_pinnedCertFound) {
dispatch_async(_workQueue, ^{
NSError *error = SRErrorWithDomainCodeDescription(NSURLErrorDomain, NSURLErrorClientCertificateRejected,
@"Invalid server certificate.");
[weakSelf _failWithError:error];
});
return;
} else if (aStream == _outputStream) {
dispatch_async(_workQueue, ^{
[self didConnect];
});
}
id<SRSecurityPolicy> verifier = [_urlRequest SR_securityPolicy];
if (!verifier) {
@throw [NSException exceptionWithName:@"Can't verify WebSocket trust." reason:@"Missing security policy." userInfo:nil];
}
SecTrustRef secTrust = (__bridge SecTrustRef)[aStream propertyForKey:(__bridge id)kCFStreamPropertySSLPeerTrust];
if (! (secTrust && [verifier evaluateServerTrust:secTrust forDomain:_urlRequest.URL.host])) {
dispatch_async(_workQueue, ^{
NSError *error = SRErrorWithDomainCodeDescription(NSURLErrorDomain, NSURLErrorClientCertificateRejected,
@"Invalid server certificate.");
[weakSelf _failWithError:error];
});
return;
}
_pinnedCertFound = YES;
}
dispatch_async(_workQueue, ^{
@ -1443,7 +1421,8 @@ static const size_t SRFrameHeaderOverhead = 32;
assert(_readBuffer);
// didConnect fires after certificate verification if we're using pinned certificates.
BOOL usingPinnedCerts = [[_urlRequest SR_SSLPinnedCertificates] count] > 0;
// BOOL usingPinnedCerts = [[_urlRequest SR_SSLPinnedCertificates] count] > 0;
BOOL usingPinnedCerts = YES; // hard coded for OWS which always pins certs.
if ((!_secure || !usingPinnedCerts) && self.readyState == SR_CONNECTING && aStream == _inputStream) {
[self didConnect];
}

View File

@ -10,5 +10,6 @@
//
#import <SocketRocket/SRWebSocket.h>
#import <SocketRocket/SRSecurityPolicy.h>
#import <SocketRocket/NSRunLoop+SRWebSocket.h>
#import <SocketRocket/NSURLRequest+SRWebSocket.h>