| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160 |
- //
- // AuthURLSessionTaskDelegate.m
- // TunneledWebRequest
- //
- /*
- Licensed under Creative Commons Zero (CC0).
- https://creativecommons.org/publicdomain/zero/1.0/
- */
- // NOTE: this file is shared by TunneledWebRequest and TunneledWebView
- #import "AuthURLSessionTaskDelegate.h"
- #import "OCSPCache.h"
- #import "OCSPURLEncode.h"
- @implementation AuthURLSessionTaskDelegate {
- OCSPCache *ocspCache;
- }
- - (id)initWithLogger:(void (^)(NSString*))logger
- andLocalHTTPProxyPort:(NSInteger)port {
- self = [super init];
- if (self) {
- self.logger = logger;
- self.localHTTPProxyPort = port;
- self->ocspCache = [[OCSPCache alloc] initWithLogger:^(NSString * _Nonnull logLine) {
- [self logWithFormat:@"[OCSPCache] %@", logLine];
- }];
- }
- return self;
- }
- - (void)logWithFormat:(NSString *)format, ... NS_FORMAT_FUNCTION(1, 2) {
- if (self.logger != nil) {
- va_list arguments;
-
- va_start(arguments, format);
- NSString *message = [[NSString alloc] initWithFormat:format arguments:arguments];
- va_end(arguments);
-
- self.logger(message);
- }
- }
- - (void)URLSession:(NSURLSession *)session
- task:(NSURLSessionTask *)task
- didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge
- completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential *))completionHandler
- {
- #pragma unused(session)
- #pragma unused(task)
- assert(challenge != nil);
- assert(completionHandler != nil);
-
- // Resolve NSURLAuthenticationMethodServerTrust ourselves
- if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {
- [self logWithFormat:@"Got SSL certificate for %@, mainDocumentURL: %@, URL: %@",
- challenge.protectionSpace.host,
- [task.currentRequest mainDocumentURL],
- [task.currentRequest URL]];
- SecTrustRef trust = challenge.protectionSpace.serverTrust;
- if (trust == nil) {
- assert(NO);
- }
- SecPolicyRef policy = SecPolicyCreateRevocation(kSecRevocationOCSPMethod |
- kSecRevocationRequirePositiveResponse |
- kSecRevocationNetworkAccessDisabled);
- SecTrustSetPolicies(trust, policy);
- CFRelease(policy);
- // Check if there is a pinned or cached OCSP response
- SecTrustResultType trustResultType;
- SecTrustEvaluate(trust, &trustResultType);
- if ( trustResultType == kSecTrustResultProceed
- || trustResultType == kSecTrustResultUnspecified) {
- [self logWithFormat:@"Pinned or cached OCSP response found by the system"];
- NSURLCredential *credential = [NSURLCredential credentialForTrust:trust];
- assert(credential != nil);
- completionHandler(NSURLSessionAuthChallengeUseCredential, credential);
- return;
- }
- // No pinned OCSP response, try fetching one
- [self logWithFormat:@"Fetching OCSP response through OCSPCache"];
- NSURL* (^modifyOCSPURL)(NSURL *url) = ^NSURL*(NSURL *url) {
- return [self modifyOCSPURL:url];
- };
- [ocspCache lookup:trust
- andTimeout:0
- modifyOCSPURL:modifyOCSPURL
- completion:
- ^(OCSPCacheLookupResult * _Nonnull result) {
- assert(result.response != nil);
- assert(result.err == nil);
- if (result.cached) {
- [self logWithFormat:@"Got cached OCSP response from OCSPCache"];
- } else {
- [self logWithFormat:@"Fetched OCSP response from remote"];
- }
- CFDataRef d = (__bridge CFDataRef)result.response.data;
- SecTrustSetOCSPResponse(trust, d);
- SecTrustResultType trustResultType;
- SecTrustEvaluate(trust, &trustResultType);
- if ( trustResultType == kSecTrustResultProceed
- || trustResultType == kSecTrustResultUnspecified) {
- NSURLCredential *credential = [NSURLCredential credentialForTrust:trust];
- assert(credential != nil);
- completionHandler(NSURLSessionAuthChallengeUseCredential, credential);
- return;
- }
- // Reject the protection space.
- // Do not use NSURLSessionAuthChallengePerformDefaultHandling because it can trigger
- // plaintext OCSP requests.
- completionHandler(NSURLSessionAuthChallengeRejectProtectionSpace, nil);
- return;
- }];
- return;
- }
- completionHandler(NSURLSessionAuthChallengePerformDefaultHandling, nil);
- }
- // Modify the OCSP URLs so they use the local HTTP proxy
- - (nonnull NSURL *)modifyOCSPURL:(nonnull NSURL *)url {
- // The target URL must be encoded, so as to be valid within a query parameter.
- NSString *encodedTargetUrl = [URLEncode encode:url.absoluteString];
- NSNumber *httpProxyPort = [NSNumber numberWithInt:(int)self.localHTTPProxyPort];
- NSString *proxiedURLString = [NSString stringWithFormat:@"http://127.0.0.1:%@/tunneled/%@",
- httpProxyPort,
- encodedTargetUrl];
- NSURL *proxiedURL = [NSURL URLWithString:proxiedURLString];
- [self logWithFormat:@"[OCSPCache] updated OCSP URL %@ to %@", url, proxiedURL];
- return proxiedURL;
- }
- @end
|