diff --git a/Limelight/Network/HttpManager.h b/Limelight/Network/HttpManager.h index 8328ded..4541565 100644 --- a/Limelight/Network/HttpManager.h +++ b/Limelight/Network/HttpManager.h @@ -13,6 +13,7 @@ @interface HttpManager : NSObject - (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; diff --git a/Limelight/Network/HttpManager.m b/Limelight/Network/HttpManager.m index 6f679cc..c955f60 100644 --- a/Limelight/Network/HttpManager.m +++ b/Limelight/Network/HttpManager.m @@ -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]); diff --git a/Limelight/Network/PairManager.m b/Limelight/Network/PairManager.m index cae5081..af33c1b 100644 --- a/Limelight/Network/PairManager.m +++ b/Limelight/Network/PairManager.m @@ -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 {