ssl_pin_verifier.m 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133
  1. /*
  2. ssl_pin_verifier.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 "ssl_pin_verifier.h"
  9. #import "TSKSPKIHashCache.h"
  10. #import "../Dependencies/domain_registry/domain_registry.h"
  11. #import "../configuration_utils.h"
  12. #import "../TSKLog.h"
  13. #pragma mark SSL Pin Verifier
  14. TSKTrustEvaluationResult verifyPublicKeyPin(SecTrustRef serverTrust, NSString *serverHostname, NSSet<NSData *> *knownPins, TSKSPKIHashCache *hashCache)
  15. {
  16. NSCParameterAssert(serverTrust);
  17. NSCParameterAssert(knownPins);
  18. if ((serverTrust == NULL) || (knownPins == nil))
  19. {
  20. TSKLog(@"Invalid pinning parameters for %@", serverHostname);
  21. return TSKTrustEvaluationErrorInvalidParameters;
  22. }
  23. // First re-check the certificate chain using the default SSL validation in case it was disabled
  24. // This gives us revocation (only for EV certs I think?) and also ensures the certificate chain is sane
  25. // And also gives us the exact path that successfully validated the chain
  26. CFRetain(serverTrust);
  27. // Create and use a sane SSL policy to force hostname validation, even if the supplied trust has a bad
  28. // policy configured (such as one from SecPolicyCreateBasicX509())
  29. SecPolicyRef SslPolicy = SecPolicyCreateSSL(YES, (__bridge CFStringRef)(serverHostname));
  30. SecTrustSetPolicies(serverTrust, SslPolicy);
  31. CFRelease(SslPolicy);
  32. SecTrustResultType trustResult = 0;
  33. if (SecTrustEvaluate(serverTrust, &trustResult) != errSecSuccess)
  34. {
  35. TSKLog(@"SecTrustEvaluate error for %@", serverHostname);
  36. CFRelease(serverTrust);
  37. return TSKTrustEvaluationErrorInvalidParameters;
  38. }
  39. if ((trustResult != kSecTrustResultUnspecified) && (trustResult != kSecTrustResultProceed))
  40. {
  41. // Default SSL validation failed
  42. CFDictionaryRef evaluationDetails = SecTrustCopyResult(serverTrust);
  43. TSKLog(@"Error: default SSL validation failed for %@: %@", serverHostname, evaluationDetails);
  44. CFRelease(evaluationDetails);
  45. CFRelease(serverTrust);
  46. return TSKTrustEvaluationFailedInvalidCertificateChain;
  47. }
  48. // Check each certificate in the server's certificate chain (the trust object); start with the CA all the way down to the leaf
  49. CFIndex certificateChainLen = SecTrustGetCertificateCount(serverTrust);
  50. for(int i=(int)certificateChainLen-1;i>=0;i--)
  51. {
  52. // Extract the certificate
  53. SecCertificateRef certificate = SecTrustGetCertificateAtIndex(serverTrust, i);
  54. CFStringRef certificateSubject = SecCertificateCopySubjectSummary(certificate);
  55. TSKLog(@"Checking certificate with CN: %@", certificateSubject);
  56. CFRelease(certificateSubject);
  57. // Generate the subject public key info hash
  58. NSData *subjectPublicKeyInfoHash = [hashCache hashSubjectPublicKeyInfoFromCertificate:certificate];
  59. if (subjectPublicKeyInfoHash == nil)
  60. {
  61. TSKLog(@"Error - could not generate the SPKI hash for %@", serverHostname);
  62. CFRelease(serverTrust);
  63. return TSKTrustEvaluationErrorCouldNotGenerateSpkiHash;
  64. }
  65. // Is the generated hash in our set of pinned hashes ?
  66. TSKLog(@"Testing SSL Pin %@", subjectPublicKeyInfoHash);
  67. if ([knownPins containsObject:subjectPublicKeyInfoHash])
  68. {
  69. TSKLog(@"SSL Pin found for %@", serverHostname);
  70. CFRelease(serverTrust);
  71. return TSKTrustEvaluationSuccess;
  72. }
  73. }
  74. #if !TARGET_OS_IPHONE
  75. // OS X only: if user-defined anchors are whitelisted, allow the App to not enforce pin validation
  76. NSMutableArray *customRootCerts = [NSMutableArray array];
  77. // Retrieve the OS X host's list of user-defined CA certificates
  78. CFArrayRef userRootCerts;
  79. OSStatus status = SecTrustSettingsCopyCertificates(kSecTrustSettingsDomainUser, &userRootCerts);
  80. if (status == errSecSuccess)
  81. {
  82. [customRootCerts addObjectsFromArray:(__bridge NSArray *)(userRootCerts)];
  83. CFRelease(userRootCerts);
  84. }
  85. CFArrayRef adminRootCerts;
  86. status = SecTrustSettingsCopyCertificates(kSecTrustSettingsDomainAdmin, &adminRootCerts);
  87. if (status == errSecSuccess)
  88. {
  89. [customRootCerts addObjectsFromArray:(__bridge NSArray *)(adminRootCerts)];
  90. CFRelease(adminRootCerts);
  91. }
  92. // Is any certificate in the chain a custom anchor that was manually added to the OS' trust store ?
  93. // If we get there, we shouldn't have to check the custom certificates' trust setting (trusted / not trusted)
  94. // as the chain validation was successful right before
  95. if ([customRootCerts count] > 0)
  96. {
  97. for(int i=0;i<certificateChainLen;i++)
  98. {
  99. SecCertificateRef certificate = SecTrustGetCertificateAtIndex(serverTrust, i);
  100. // Is the certificate chain's anchor a user-defined anchor ?
  101. if ([customRootCerts containsObject:(__bridge id)(certificate)])
  102. {
  103. TSKLog(@"Detected user-defined trust anchor in the certificate chain");
  104. CFRelease(serverTrust);
  105. return TSKTrustEvaluationFailedUserDefinedTrustAnchor;
  106. }
  107. }
  108. }
  109. #endif
  110. // If we get here, we didn't find any matching SPKI hash in the chain
  111. TSKLog(@"Error: SSL Pin not found for %@", serverHostname);
  112. CFRelease(serverTrust);
  113. return TSKTrustEvaluationFailedNoMatchingPin;
  114. }