Advertisement
Guest User

0B3eM2XDHn7zXTUg3Q1daSk1BNXc.txt

a guest
Mar 6th, 2015
184
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 15.44 KB | None | 0 0
  1. /*
  2. File: ServerTrustChallengeHandler.m
  3.  
  4. Contains: Handles HTTPS server trust challenges.
  5.  
  6. Written by: DTS
  7.  
  8. Copyright: Copyright (c) 2011 Apple Inc. All Rights Reserved.
  9.  
  10. Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Inc.
  11. ("Apple") in consideration of your agreement to the following
  12. terms, and your use, installation, modification or
  13. redistribution of this Apple software constitutes acceptance of
  14. these terms. If you do not agree with these terms, please do
  15. not use, install, modify or redistribute this Apple software.
  16.  
  17. In consideration of your agreement to abide by the following
  18. terms, and subject to these terms, Apple grants you a personal,
  19. non-exclusive license, under Apple's copyrights in this
  20. original Apple software (the "Apple Software"), to use,
  21. reproduce, modify and redistribute the Apple Software, with or
  22. without modifications, in source and/or binary forms; provided
  23. that if you redistribute the Apple Software in its entirety and
  24. without modifications, you must retain this notice and the
  25. following text and disclaimers in all such redistributions of
  26. the Apple Software. Neither the name, trademarks, service marks
  27. or logos of Apple Inc. may be used to endorse or promote
  28. products derived from the Apple Software without specific prior
  29. written permission from Apple. Except as expressly stated in
  30. this notice, no other rights or licenses, express or implied,
  31. are granted by Apple herein, including but not limited to any
  32. patent rights that may be infringed by your derivative works or
  33. by other works in which the Apple Software may be incorporated.
  34.  
  35. The Apple Software is provided by Apple on an "AS IS" basis.
  36. APPLE MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
  37. WITHOUT LIMITATION THE IMPLIED WARRANTIES OF NON-INFRINGEMENT,
  38. MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, REGARDING
  39. THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN
  40. COMBINATION WITH YOUR PRODUCTS.
  41.  
  42. IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT,
  43. INCIDENTAL OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
  44. TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  45. DATA, OR PROFITS; OR BUSINESS INTERRUPTION) ARISING IN ANY WAY
  46. OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION
  47. OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY
  48. OF CONTRACT, TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR
  49. OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE POSSIBILITY OF
  50. SUCH DAMAGE.
  51.  
  52. */
  53.  
  54. #import "ServerTrustChallengeHandler.h"
  55.  
  56. #import "Credentials.h"
  57.  
  58. #import "DebugOptions.h"
  59.  
  60. @interface ServerTrustChallengeHandler ()
  61.  
  62. @property (nonatomic, retain, readwrite) UIAlertView * alertView;
  63.  
  64. @end
  65.  
  66. @implementation ServerTrustChallengeHandler
  67.  
  68. + (void)registerHandlers
  69. // Called by the handler registry within ChallengeHandler to request that the
  70. // concrete subclass register itself.
  71. {
  72. // We observe the serverValidation debug option and, when it changes, either register
  73. // or deregister ourselves based on the value of the option. We pass in
  74. // NSKeyValueObservingOptionInitial so that our observer is called immediately, and this
  75. // then sets up our initial state.
  76. [[DebugOptions sharedDebugOptions] addObserver:self forKeyPath:@"serverValidation" options:NSKeyValueObservingOptionInitial context:NULL];
  77. }
  78.  
  79. + (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
  80. // A KVO callback called when the serverValidation debug option changes. If the user
  81. // has requested default validation, we pull ourselves out of the registry, otherwise we
  82. // make sure we're in there.
  83. {
  84. if ( (object == [DebugOptions sharedDebugOptions]) && [keyPath isEqual:@"serverValidation"] ) {
  85.  
  86. // The following code relies on two properties of challenge handling registration:
  87. //
  88. // o It's OK to deregister a handler that's not registered.
  89. // o It's OK to register a handler that's already registered.
  90. //
  91. // Without these two properties this code would have to keep track of whether it's
  92. // registered or not, which would be less fun.
  93.  
  94. if ([DebugOptions sharedDebugOptions].serverValidation == kDebugOptionsServerValidationDefault) {
  95. [ChallengeHandler deregisterHandlerClass:[self class] forAuthenticationMethod:NSURLAuthenticationMethodServerTrust];
  96. } else {
  97. [ChallengeHandler registerHandlerClass:[self class] forAuthenticationMethod:NSURLAuthenticationMethodServerTrust];
  98. }
  99. } else {
  100. [super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
  101. }
  102. }
  103.  
  104. - (void)dealloc
  105. {
  106. assert(self.alertView == nil);
  107. [super dealloc];
  108. }
  109.  
  110. #pragma mark * Core code
  111.  
  112. @synthesize alertView = _alertView;
  113.  
  114. static SecCertificateRef SecTrustGetLeafCertificate(SecTrustRef trust)
  115. // Returns the leaf certificate from a SecTrust object (that is always the
  116. // certificate at index 0).
  117. {
  118. SecCertificateRef result;
  119.  
  120. assert(trust != NULL);
  121.  
  122. if (SecTrustGetCertificateCount(trust) > 0) {
  123. result = SecTrustGetCertificateAtIndex(trust, 0);
  124. assert(result != NULL);
  125. } else {
  126. result = NULL;
  127. }
  128. return result;
  129. }
  130.  
  131. static NSMutableDictionary * sSiteToCertificateMap; // keys are host names as NSString
  132. // values are SecCertificateRef
  133.  
  134. + (void)resetTrustedCertificates
  135. {
  136. // We don't just release the entire array because the _serverTrustResolvedWithSuccess
  137. // code assumes that, if execution gets that far, sSiteToCertificateMap is not nil.
  138. if (sSiteToCertificateMap != nil) {
  139. [sSiteToCertificateMap removeAllObjects];
  140. }
  141. }
  142.  
  143. - (void)_serverTrustResolvedWithSuccess:(BOOL)success rememberSuccess:(BOOL)rememberSuccess
  144. // Some common code that's called in a variety of places to finally resolve the
  145. // challenge. Also, if rememberSuccess is set, we add an entry for this challenge
  146. // into sSiteToCertificateMap so that future challenges can be automatically resolved.
  147. {
  148. NSURLCredential * credential;
  149.  
  150. // ! success && rememberSuccess is a weird combination, but we allow is so
  151. // that our clients don't have to jump through too many hoops.
  152.  
  153. // On succes, create a credential with which to resolve the challenge.
  154.  
  155. credential = nil;
  156. if (success) {
  157. NSURLProtectionSpace * protectionSpace;
  158. SecTrustRef trust;
  159. NSString * host;
  160. SecCertificateRef serverCert;
  161.  
  162. protectionSpace = [self.challenge protectionSpace];
  163. assert(protectionSpace != nil);
  164.  
  165. trust = [protectionSpace serverTrust];
  166. assert(trust != NULL);
  167.  
  168. credential = [NSURLCredential credentialForTrust:trust];
  169. assert(credential != nil);
  170.  
  171. // If we've been asked to remember the response, do so now.
  172.  
  173. if (rememberSuccess) {
  174. assert(sSiteToCertificateMap != nil);
  175.  
  176. host = [[self.challenge protectionSpace] host];
  177. assert(host != nil);
  178. if ( [sSiteToCertificateMap objectForKey:host] == nil ) {
  179.  
  180. serverCert = SecTrustGetLeafCertificate(trust);
  181. if (serverCert != NULL) {
  182. [sSiteToCertificateMap setObject:(id)serverCert forKey:host];
  183. }
  184. }
  185. }
  186. }
  187.  
  188. // Pass the final credential to the base class's stop code (which in turn
  189. // tells us to tear down our UI) and then tell our delegate.
  190.  
  191. [self stopWithCredential:credential];
  192. [self.delegate challengeHandlerDidFinish:self];
  193. }
  194.  
  195. - (void)_evaluateAskPerUntrustedSiteTrust
  196. // Implements the kDebugOptionsServerValidationAskPerUntrustedSite server trust
  197. // validation option.
  198. {
  199. OSStatus err;
  200. NSURLProtectionSpace * protectionSpace;
  201. SecTrustRef trust;
  202. BOOL trusted;
  203. SecTrustResultType trustResult;
  204. SecCertificateRef previousCert;
  205.  
  206. protectionSpace = [self.challenge protectionSpace];
  207. assert(protectionSpace != nil);
  208.  
  209. trust = [protectionSpace serverTrust];
  210. assert(trust != NULL);
  211.  
  212. // Evaluate the trust the standard way.
  213.  
  214. err = SecTrustEvaluate(trust, &trustResult);
  215. trusted = (err == noErr) && ((trustResult == kSecTrustResultProceed) || (trustResult == kSecTrustResultUnspecified));
  216.  
  217. // If the standard policy says that it's trusted, allow it right now.
  218. // Otherwise do our custom magic.
  219.  
  220. if (trusted) {
  221. [self _serverTrustResolvedWithSuccess:YES rememberSuccess:NO];
  222. } else {
  223. if (sSiteToCertificateMap == nil) {
  224. sSiteToCertificateMap = [[NSMutableDictionary alloc] init];
  225. assert(sSiteToCertificateMap != nil);
  226. }
  227.  
  228. // Check to see if we've previously seen this server.
  229.  
  230. previousCert = (SecCertificateRef) [sSiteToCertificateMap objectForKey:[protectionSpace host]];
  231. assert( (previousCert == NULL) || (CFGetTypeID(previousCert) == SecCertificateGetTypeID()) );
  232.  
  233. if (previousCert == NULL) {
  234. // We've not seen this server before. Ask the user.
  235.  
  236. assert(self.alertView == nil);
  237. self.alertView = [[[UIAlertView alloc] initWithTitle:@"ACCEPT WEBSITE CERTIFICATE"
  238. message:@"THE CERTIFICATE FOR THIS WEBSITE IS INVALID. TAP ACCEPT TO CONNECT TO THIS WEBSITE ANYWAY."
  239. delegate:self
  240. cancelButtonTitle:@"Accept"
  241. otherButtonTitles:@"Cancel",
  242. nil
  243. ] autorelease];
  244. assert(self.alertView != nil);
  245.  
  246. [self.alertView show];
  247.  
  248. // continues in -alertView:clickedButtonAtIndex:
  249. } else {
  250. BOOL success;
  251. SecCertificateRef serverCert;
  252.  
  253. // We've seen this server before. Check to see whether the
  254. // certificate from the connection matches the certificate
  255. // we saw last time. If so, allow the connection. If not,
  256. // deny the connection.
  257.  
  258. success = NO;
  259. serverCert = SecTrustGetLeafCertificate(trust);
  260. if (serverCert != NULL) {
  261. CFDataRef previousCertData;
  262. CFDataRef serverCertData;
  263.  
  264. previousCertData = SecCertificateCopyData(previousCert);
  265. serverCertData = SecCertificateCopyData(serverCert );
  266.  
  267. assert(previousCertData != NULL);
  268. assert(serverCertData != NULL);
  269.  
  270. success = CFEqual(previousCertData, serverCertData);
  271.  
  272. CFRelease(previousCertData);
  273. CFRelease(serverCertData);
  274. }
  275.  
  276. if (success) {
  277. [self _serverTrustResolvedWithSuccess:YES rememberSuccess:NO];
  278. } else {
  279. [self _serverTrustResolvedWithSuccess:NO rememberSuccess:NO];
  280. }
  281. }
  282. }
  283. }
  284.  
  285. - (void)_evaluateImportedCertificatesTrust
  286. // Implements the kDebugOptionsServerValidationTrustImportedCertificates server
  287. // trust validation option.
  288. {
  289. OSStatus err;
  290. NSURLProtectionSpace * protectionSpace;
  291. SecTrustRef trust;
  292. SecTrustResultType trustResult;
  293. BOOL trusted;
  294.  
  295. protectionSpace = [self.challenge protectionSpace];
  296. assert(protectionSpace != nil);
  297.  
  298. trust = [protectionSpace serverTrust];
  299. assert(trust != NULL);
  300.  
  301. // Evaluate the trust the standard way.
  302.  
  303. err = SecTrustEvaluate(trust, &trustResult);
  304. trusted = (err == noErr) && ((trustResult == kSecTrustResultProceed) || (trustResult == kSecTrustResultUnspecified));
  305.  
  306. // If that fails, apply our certificates as anchors and see if that helps.
  307. //
  308. // It's perfectly acceptable to apply all of our certificates to the SecTrust
  309. // object, and let the SecTrust object sort out the mess. Of course, this assumes
  310. // that the user trusts all certificates equally in all situations, which is implicit
  311. // in our user interface; you could provide a more sophisticated user interface
  312. // to allow the user to trust certain certificates for certain sites and so on).
  313.  
  314. if ( ! trusted ) {
  315. err = SecTrustSetAnchorCertificates(trust, (CFArrayRef) [Credentials sharedCredentials].certificates);
  316. if (err == noErr) {
  317. err = SecTrustEvaluate(trust, &trustResult);
  318. }
  319. trusted = (err == noErr) && ((trustResult == kSecTrustResultProceed) || (trustResult == kSecTrustResultUnspecified));
  320. }
  321.  
  322. if (trusted) {
  323. [self _serverTrustResolvedWithSuccess:YES rememberSuccess:NO];
  324. } else {
  325. [self _serverTrustResolvedWithSuccess:NO rememberSuccess:NO];
  326. }
  327. }
  328.  
  329. - (void)_handleServerTrustChallenge
  330. // Handles a server trust challenge according to the serverValidation debug option.
  331. // This is called out of -didStart, and thus can present UI. However, it may
  332. // or not present UI depending on the specific server trust and debug options.
  333. {
  334. switch ( [DebugOptions sharedDebugOptions].serverValidation ) {
  335. default:
  336. // fall through
  337. case kDebugOptionsServerValidationDefault: {
  338. // We should never have got here because we deregister ourselves when
  339. // the user selects the default case.
  340. assert(NO);
  341. [self _serverTrustResolvedWithSuccess:NO rememberSuccess:NO];
  342. } break;
  343. case kDebugOptionsServerValidationAskPerUntrustedSite: {
  344. [self _evaluateAskPerUntrustedSiteTrust];
  345. } break;
  346. case kDebugOptionsServerValidationTrustImportedCertificates: {
  347. [self _evaluateImportedCertificatesTrust];
  348. } break;
  349. case kDebugOptionsServerValidationDisabled: {
  350. // Just say yes.
  351. [self _serverTrustResolvedWithSuccess:YES rememberSuccess:NO];
  352. } break;
  353. }
  354. }
  355.  
  356. - (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
  357. // An alert view delegate callback that's called when the alert is dismissed.
  358. // We use the tapped button index to decide how to resolve the challenge.
  359. {
  360. #pragma unused(alertView)
  361. assert(alertView == self.alertView);
  362. [self _serverTrustResolvedWithSuccess:(buttonIndex == 0) rememberSuccess:YES];
  363. }
  364.  
  365. #pragma mark * Override points
  366.  
  367. - (void)didStart
  368. // Called by our base class to tell us to create our UI.
  369. {
  370. [super didStart];
  371. [self _handleServerTrustChallenge];
  372. }
  373.  
  374. - (void)willFinish
  375. // Called by our base class to tell us to tear down our UI.
  376. {
  377. [super willFinish];
  378.  
  379. // If an alert is still up, tear it down immediately.
  380.  
  381. if (self.alertView != nil) {
  382. self.alertView.delegate = nil;
  383. [self.alertView dismissWithClickedButtonIndex:self.alertView.cancelButtonIndex animated:NO];
  384. self.alertView = nil;
  385. }
  386. }
  387.  
  388. @end
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement