diff --git a/MOLAuthenticatingURLSession.podspec b/MOLAuthenticatingURLSession.podspec index d704911..f6dbdb0 100644 --- a/MOLAuthenticatingURLSession.podspec +++ b/MOLAuthenticatingURLSession.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'MOLAuthenticatingURLSession' - s.version = '2.6' + s.version = '2.7' s.platform = :osx s.osx.deployment_target = '10.9' s.license = { :type => 'Apache 2.0', :file => 'LICENSE' } diff --git a/Source/MOLAuthenticatingURLSession/MOLAuthenticatingURLSession.m b/Source/MOLAuthenticatingURLSession/MOLAuthenticatingURLSession.m index 15e4665..888ccfa 100644 --- a/Source/MOLAuthenticatingURLSession/MOLAuthenticatingURLSession.m +++ b/Source/MOLAuthenticatingURLSession/MOLAuthenticatingURLSession.m @@ -203,6 +203,7 @@ - (void)URLSession:(NSURLSession *)session - (NSURLCredential *)clientCredentialForProtectionSpace:(NSURLProtectionSpace *)protectionSpace { __block SecIdentityRef foundIdentity = NULL; + NSArray *allCerts; if (self.clientCertFile) { foundIdentity = [self identityFromFile:self.clientCertFile password:self.clientCertPassword]; } else { @@ -214,7 +215,7 @@ - (NSURLCredential *)clientCredentialForProtectionSpace:(NSURLProtectionSpace *) }, (CFTypeRef *)&cfResults); NSArray *results = CFBridgingRelease(cfResults); - NSMutableArray *allCerts = [[MOLCertificate certificatesFromArray:results] mutableCopy]; + allCerts = [MOLCertificate certificatesFromArray:results]; if (self.clientCertCommonName) { foundIdentity = [self identityByFilteringArray:allCerts @@ -255,9 +256,12 @@ - (NSURLCredential *)clientCredentialForProtectionSpace:(NSURLProtectionSpace *) MOLCertificate *clientCert = [[MOLCertificate alloc] initWithSecCertificateRef:certificate]; if (certificate) CFRelease(certificate); if (clientCert) [self log:@"Client Trust: %@", clientCert]; + + NSArray *intermediates = [self locateIntermediatesForCertificate:clientCert inArray:allCerts]; + NSURLCredential *cred = [NSURLCredential credentialWithIdentity:foundIdentity - certificates:nil + certificates:intermediates persistence:NSURLCredentialPersistenceForSession]; if (foundIdentity) CFRelease(foundIdentity); return cred; @@ -409,6 +413,38 @@ - (SecIdentityRef)identityFromFile:(NSString *)file password:(NSString *)passwor identities.firstObject[(__bridge NSString *)kSecImportItemIdentity]); } +// For servers that require the intermediate certificate to be presented when +// using a client certificate, this method will attempt to locate those +// intermediates in the keychain. If the intermediate certificate is not in +// the keychain an empty array will be presented instead. +- (NSArray *)locateIntermediatesForCertificate:(MOLCertificate *)leafCert + inArray:(NSArray *)certs { + SecTrustRef t = NULL; + OSStatus res = SecTrustCreateWithCertificates(leafCert.certRef, NULL, &t); + if (res != errSecSuccess) { + NSString *errMsg = CFBridgingRelease(SecCopyErrorMessageString(res, NULL)); + [self log:@"Failed to create trust for locating intermediate certs: %@", errMsg]; + return nil; + } + + // Evaluate the trust to create the chain, even though we don't + // use the result of the evaluation. The certificates seem to be available + // without calling this but the documentation is clear that + // SecTrustGetCertificateAtIndex shouldn't be called without calling + // SecTrustEvaluate first. + SecTrustResultType _; // unused + SecTrustEvaluate(t, &_); + + NSMutableArray *intermediates = [NSMutableArray array]; + CFIndex certCount = SecTrustGetCertificateCount(t); + for (int i = 1; i < certCount; ++i) { + [intermediates addObject:(id)SecTrustGetCertificateAtIndex(t, i)]; + } + CFRelease(t); + + return intermediates; +} + - (void)log:(NSString *)format, ... { if (self.loggingBlock) { va_list args; diff --git a/WORKSPACE b/WORKSPACE index e8befff..5030e95 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -3,7 +3,7 @@ load("@bazel_tools//tools/build_defs/repo:git.bzl", "git_repository") git_repository( name = "build_bazel_rules_apple", remote = "https://github.com/bazelbuild/rules_apple.git", - tag = "0.6.0", + tag = "0.17.2", ) load( @@ -16,5 +16,5 @@ apple_rules_dependencies() git_repository( name = "MOLCertificate", remote = "https://github.com/google/macops-molcertificate.git", - tag = "v2.0", + tag = "v2.1", )