Require cert pinning for HTTPS

This commit is contained in:
Cameron Gutman
2018-12-22 20:39:13 -08:00
parent fbae7f88b5
commit 72dcd76e37
3 changed files with 53 additions and 21 deletions

View File

@@ -13,6 +13,7 @@
@interface HttpManager : NSObject <NSURLSessionDelegate>
- (id) initWithHost:(NSString*) host uniqueId:(NSString*) uniqueId serverCert:(NSData*) serverCert;
- (void) setServerCert:(NSData*) serverCert;
- (NSURLRequest*) newPairRequest:(NSData*)salt clientCert:(NSData*)clientCert;
- (NSURLRequest*) newUnpairRequest;
- (NSURLRequest*) newChallengeRequest:(NSData*)challenge;
@@ -21,6 +22,7 @@
- (NSURLRequest*) newPairChallenge;
- (NSURLRequest*) newAppListRequest;
- (NSURLRequest*) newServerInfoRequest:(bool)fastFail;
- (NSURLRequest*) newHttpServerInfoRequest:(bool)fastFail;
- (NSURLRequest*) newHttpServerInfoRequest;
- (NSURLRequest*) newLaunchRequest:(StreamConfiguration*)config;
- (NSURLRequest*) newResumeRequest:(StreamConfiguration*)config;

View File

@@ -43,6 +43,10 @@ static const NSString* HTTPS_PORT = @"47984";
return [xmlString dataUsingEncoding:NSUTF8StringEncoding];
}
- (void) setServerCert:(NSData*) serverCert {
_serverCert = serverCert;
}
- (id) initWithHost:(NSString*) host uniqueId:(NSString*) uniqueId serverCert:(NSData*) serverCert {
self = [super init];
_uniqueId = uniqueId;
@@ -120,6 +124,9 @@ static const NSString* HTTPS_PORT = @"47984";
}
- (NSURLRequest*) createRequestFromString:(NSString*) urlString timeout:(int)timeout {
// Assert that we only issue HTTPS requests with a pinned cert
assert([urlString hasPrefix:@"http://"] || _serverCert != nil);
NSURL* url = [[NSURL alloc] initWithString:urlString];
NSMutableURLRequest* request = [NSMutableURLRequest requestWithURL:url];
[request setTimeoutInterval:timeout];
@@ -134,7 +141,7 @@ static const NSString* HTTPS_PORT = @"47984";
}
- (NSURLRequest*) newUnpairRequest {
NSString* urlString = [NSString stringWithFormat:@"%@/unpair?uniqueid=%@", _baseHTTPSURL, _uniqueId];
NSString* urlString = [NSString stringWithFormat:@"%@/unpair?uniqueid=%@", _baseHTTPURL, _uniqueId];
return [self createRequestFromString:urlString timeout:NORMAL_TIMEOUT_SEC];
}
@@ -166,13 +173,22 @@ static const NSString* HTTPS_PORT = @"47984";
}
- (NSURLRequest *)newServerInfoRequest:(bool)fastFail {
if (_serverCert == nil) {
// Use HTTP if the cert is not pinned yet
return [self newHttpServerInfoRequest:fastFail];
}
NSString* urlString = [NSString stringWithFormat:@"%@/serverinfo?uniqueid=%@", _baseHTTPSURL, _uniqueId];
return [self createRequestFromString:urlString timeout:(fastFail ? SHORT_TIMEOUT_SEC : NORMAL_TIMEOUT_SEC)];
}
- (NSURLRequest *)newHttpServerInfoRequest {
- (NSURLRequest *)newHttpServerInfoRequest:(bool)fastFail {
NSString* urlString = [NSString stringWithFormat:@"%@/serverinfo", _baseHTTPURL];
return [self createRequestFromString:urlString timeout:NORMAL_TIMEOUT_SEC];
return [self createRequestFromString:urlString timeout:(fastFail ? SHORT_TIMEOUT_SEC : NORMAL_TIMEOUT_SEC)];
}
- (NSURLRequest *)newHttpServerInfoRequest {
return [self newHttpServerInfoRequest:false];
}
- (NSURLRequest*) newLaunchRequest:(StreamConfiguration*)config {
@@ -259,25 +275,35 @@ static const NSString* HTTPS_PORT = @"47984";
// Allow untrusted server certificates
if([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust])
{
if (_serverCert) {
SecCertificateRef actualCert = SecTrustGetCertificateAtIndex(challenge.protectionSpace.serverTrust, 0);
CFDataRef actualCertData;
actualCertData = SecCertificateCopyData(actualCert);
if (!CFEqual(actualCertData, (__bridge CFDataRef)_serverCert)) {
Log(LOG_E, @"Server certificate mismatch");
CFRelease(actualCertData);
completionHandler(NSURLSessionAuthChallengeRejectProtectionSpace, NULL);
return;
}
CFRelease(actualCertData);
// Fall-through to TLS success
if (SecTrustGetCertificateCount(challenge.protectionSpace.serverTrust) != 1) {
Log(LOG_E, @"Server certificate count mismatch");
completionHandler(NSURLSessionAuthChallengeRejectProtectionSpace, NULL);
return;
}
SecCertificateRef actualCert = SecTrustGetCertificateAtIndex(challenge.protectionSpace.serverTrust, 0);
if (actualCert == nil) {
Log(LOG_E, @"Server certificate parsing error");
completionHandler(NSURLSessionAuthChallengeRejectProtectionSpace, NULL);
return;
}
CFDataRef actualCertData = SecCertificateCopyData(actualCert);
if (actualCertData == nil) {
Log(LOG_E, @"Server certificate data parsing error");
completionHandler(NSURLSessionAuthChallengeRejectProtectionSpace, NULL);
return;
}
if (!CFEqual(actualCertData, (__bridge CFDataRef)_serverCert)) {
Log(LOG_E, @"Server certificate mismatch");
CFRelease(actualCertData);
completionHandler(NSURLSessionAuthChallengeRejectProtectionSpace, NULL);
return;
}
CFRelease(actualCertData);
// Allow TLS handshake to proceed
completionHandler(NSURLSessionAuthChallengeUseCredential,
[NSURLCredential credentialForTrust: challenge.protectionSpace.serverTrust]);

View File

@@ -78,6 +78,10 @@
return;
}
// Pin the cert for TLS usage on this host
NSData* derCertBytes = [CryptoManager pemToDer:[Utils hexToBytes:plainCert]];
[_httpManager setServerCert:derCertBytes];
CryptoManager* cryptoMan = [[CryptoManager alloc] init];
NSData* aesKey;
@@ -181,7 +185,7 @@
return;
}
[_callback pairSuccessful: [CryptoManager pemToDer:[Utils hexToBytes:plainCert]]];
[_callback pairSuccessful: derCertBytes];
}
- (BOOL) verifyResponseStatus:(HttpResponse*)resp {