TSKNSURLSessionDelegateProxy.m 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194
  1. /*
  2. TSKNSURLSessionDelegateProxy.m
  3. TrustKit
  4. Copyright 2015 The TrustKit Project Authors
  5. Licensed under the MIT license, see associated LICENSE file for terms.
  6. See AUTHORS file for the list of project authors.
  7. */
  8. #import "TSKNSURLSessionDelegateProxy.h"
  9. #import "../TrustKit.h"
  10. #import "../TSKLog.h"
  11. #import "../TSKTrustDecision.h"
  12. #import "../TSKPinningValidator.h"
  13. #import "../Dependencies/RSSwizzle/RSSwizzle.h"
  14. @interface TSKNSURLSessionDelegateProxy ()
  15. /* The NSURLSessionDelegate we're going to proxy */
  16. @property (nonatomic) id<NSURLSessionDelegate, NSURLSessionTaskDelegate> originalDelegate;
  17. @property (nonatomic) TrustKit *trustKit;
  18. @end
  19. @implementation TSKNSURLSessionDelegateProxy
  20. #pragma mark Public methods
  21. + (void)swizzleNSURLSessionConstructors:(TrustKit *)trustKit
  22. {
  23. // + sessionWithConfiguration:delegate:delegateQueue:
  24. #pragma clang diagnostic push
  25. #pragma clang diagnostic ignored "-Wshadow"
  26. RSSwizzleClassMethod(NSClassFromString(@"NSURLSession"),
  27. @selector(sessionWithConfiguration:delegate:delegateQueue:),
  28. RSSWReturnType(NSURLSession *),
  29. RSSWArguments(NSURLSessionConfiguration * _Nonnull configuration, id _Nullable delegate, NSOperationQueue * _Nullable queue),
  30. RSSWReplacement(
  31. {
  32. NSURLSession *session;
  33. if (delegate == nil)
  34. {
  35. // Just display a warning
  36. //TSKLog(@"WARNING: +sessionWithConfiguration:delegate:delegateQueue: was called with a nil delegate; TrustKit cannot enforce SSL pinning for any connection initiated by this session");
  37. session = RSSWCallOriginal(configuration, delegate, queue);
  38. }
  39. // Do not swizzle TrustKit objects (such as the reporter)
  40. else if ([NSStringFromClass([delegate class]) hasPrefix:@"TSK"])
  41. {
  42. session = RSSWCallOriginal(configuration, delegate, queue);
  43. }
  44. else
  45. {
  46. // Replace the delegate with our own so we can intercept and handle authentication challenges
  47. TSKNSURLSessionDelegateProxy *swizzledDelegate = [[TSKNSURLSessionDelegateProxy alloc] initWithTrustKit:trustKit
  48. sessionDelegate:delegate];
  49. session = RSSWCallOriginal(configuration, swizzledDelegate, queue);
  50. }
  51. return session;
  52. }));
  53. // Not hooking the following methods as they end up calling +sessionWithConfiguration:delegate:delegateQueue:
  54. // +sessionWithConfiguration:
  55. // +sharedSession
  56. #pragma clang diagnostic pop
  57. }
  58. - (instancetype _Nullable)initWithTrustKit:(TrustKit *)trustKit sessionDelegate:(id<NSURLSessionDelegate, NSURLSessionTaskDelegate>)delegate
  59. {
  60. NSParameterAssert(delegate);
  61. self = [super init];
  62. if (self)
  63. {
  64. _originalDelegate = delegate;
  65. _trustKit = trustKit;
  66. }
  67. TSKLog(@"Proxy-ing NSURLSessionDelegate: %@", NSStringFromClass([delegate class]));
  68. return self;
  69. }
  70. #pragma mark Delegate methods
  71. - (BOOL)respondsToSelector:(SEL)aSelector
  72. {
  73. if (aSelector == @selector(URLSession:task:didReceiveChallenge:completionHandler:))
  74. {
  75. // For the task-level handler, mirror the delegate
  76. return [_originalDelegate respondsToSelector:@selector(URLSession:task:didReceiveChallenge:completionHandler:)];
  77. }
  78. else if (aSelector == @selector(URLSession:didReceiveChallenge:completionHandler:))
  79. {
  80. if ([_originalDelegate respondsToSelector:@selector(URLSession:didReceiveChallenge:completionHandler:)] == YES)
  81. {
  82. return YES;
  83. }
  84. else if ([_originalDelegate respondsToSelector:@selector(URLSession:task:didReceiveChallenge:completionHandler:)] == NO)
  85. {
  86. // If the task-level handler is not implemented in the delegate, we need to implement the session-level handler
  87. // regardless of what the delegate implements, to ensure we get to handle auth challenges so we can do pinning validation
  88. return YES;
  89. }
  90. else
  91. {
  92. // Let the task-level handler handle auth challenges
  93. return NO;
  94. }
  95. }
  96. else
  97. {
  98. // The delegate proxy should mirror the original delegate's methods so that it doesn't change the app flow
  99. return [_originalDelegate respondsToSelector:aSelector];
  100. }
  101. }
  102. - (id)forwardingTargetForSelector:(SEL)sel
  103. {
  104. return _originalDelegate;
  105. }
  106. - (BOOL)common_URLSession:(NSURLSession * _Nonnull)session
  107. challenge:(NSURLAuthenticationChallenge * _Nonnull)challenge
  108. completionHandler:(void (^ _Nonnull)(NSURLSessionAuthChallengeDisposition disposition,
  109. NSURLCredential * _Nullable credential))completionHandler
  110. {
  111. // For SSL pinning we only care about server authentication
  112. if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust])
  113. {
  114. // Check the trust object against the pinning policy
  115. TSKTrustDecision trustDecision = [self.trustKit.pinningValidator evaluateTrust:challenge.protectionSpace.serverTrust
  116. forHostname:challenge.protectionSpace.host];
  117. if (trustDecision == TSKTrustDecisionShouldBlockConnection)
  118. {
  119. // Pinning validation failed - block the connection
  120. completionHandler(NSURLSessionAuthChallengeCancelAuthenticationChallenge, NULL);
  121. return YES; // Challenge handled (blocked), stop here!
  122. }
  123. }
  124. return NO;
  125. }
  126. - (void)URLSession:(NSURLSession * _Nonnull)session
  127. didReceiveChallenge:(NSURLAuthenticationChallenge * _Nonnull)challenge
  128. completionHandler:(TSKURLSessionAuthChallengeCallback)completionHandler
  129. {
  130. if ([self common_URLSession:session challenge:challenge completionHandler:completionHandler])
  131. {
  132. // Challenge handled, stop here!
  133. return;
  134. }
  135. // Forward all challenges (including client auth challenges) to the original delegate
  136. // We will also get here if the pinning validation succeeded or the domain was not pinned
  137. if ([_originalDelegate respondsToSelector:@selector(URLSession:didReceiveChallenge:completionHandler:)])
  138. {
  139. [_originalDelegate URLSession:session didReceiveChallenge:challenge completionHandler:completionHandler];
  140. }
  141. else
  142. {
  143. // The original delegate could not handle the challenge; use the default handler
  144. completionHandler(NSURLSessionAuthChallengePerformDefaultHandling, NULL);
  145. }
  146. }
  147. - (void)URLSession:(NSURLSession * _Nonnull)session
  148. task:(NSURLSessionTask * _Nonnull)task
  149. didReceiveChallenge:(NSURLAuthenticationChallenge * _Nonnull)challenge
  150. completionHandler:(void (^ _Nonnull)(NSURLSessionAuthChallengeDisposition disposition,
  151. NSURLCredential * _Nullable credential))completionHandler
  152. {
  153. if ([self common_URLSession:session challenge:challenge completionHandler:completionHandler])
  154. {
  155. // Challenge handled, stop here!
  156. return;
  157. }
  158. // Forward all challenges (including client auth challenges) to the original delegate
  159. // We will also get here if the pinning validation succeeded or the domain was not pinned
  160. if ([_originalDelegate respondsToSelector:@selector(URLSession:task:didReceiveChallenge:completionHandler:)])
  161. {
  162. [_originalDelegate URLSession:session task:task didReceiveChallenge:challenge completionHandler:completionHandler];
  163. }
  164. else
  165. {
  166. // The original delegate could not handle the challenge; use the default handler
  167. completionHandler(NSURLSessionAuthChallengePerformDefaultHandling, NULL);
  168. }
  169. }
  170. @end