parse_configuration.m 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237
  1. /*
  2. parse_configuration.m
  3. TrustKit
  4. Copyright 2016 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 "TSKTrustKitConfig.h"
  9. #import "Dependencies/domain_registry/domain_registry.h"
  10. #import "parse_configuration.h"
  11. #import <CommonCrypto/CommonDigest.h>
  12. #import "configuration_utils.h"
  13. NSDictionary *parseTrustKitConfiguration(NSDictionary *trustKitArguments)
  14. {
  15. // Convert settings supplied by the user to a configuration dictionary that can be used by TrustKit
  16. // This includes checking the sanity of the settings and converting public key hashes/pins from an
  17. // NSSArray of NSStrings (as provided by the user) to an NSSet of NSData (as needed by TrustKit)
  18. // Initialize domain registry library
  19. InitializeDomainRegistry();
  20. NSMutableDictionary *finalConfiguration = [[NSMutableDictionary alloc]init];
  21. finalConfiguration[kTSKPinnedDomains] = [[NSMutableDictionary alloc]init];
  22. // Retrieve global settings
  23. // Should we auto-swizzle network delegates
  24. NSNumber *shouldSwizzleNetworkDelegates = trustKitArguments[kTSKSwizzleNetworkDelegates];
  25. if (shouldSwizzleNetworkDelegates == nil)
  26. {
  27. // Default setting is NO
  28. finalConfiguration[kTSKSwizzleNetworkDelegates] = @(NO);
  29. }
  30. else
  31. {
  32. finalConfiguration[kTSKSwizzleNetworkDelegates] = shouldSwizzleNetworkDelegates;
  33. }
  34. #if !TARGET_OS_IPHONE
  35. // OS X only: extract the optional ignorePinningForUserDefinedTrustAnchors setting
  36. NSNumber *shouldIgnorePinningForUserDefinedTrustAnchors = trustKitArguments[kTSKIgnorePinningForUserDefinedTrustAnchors];
  37. if (shouldIgnorePinningForUserDefinedTrustAnchors == nil)
  38. {
  39. // Default setting is YES
  40. finalConfiguration[kTSKIgnorePinningForUserDefinedTrustAnchors] = @(YES);
  41. }
  42. else
  43. {
  44. finalConfiguration[kTSKIgnorePinningForUserDefinedTrustAnchors] = shouldIgnorePinningForUserDefinedTrustAnchors;
  45. }
  46. #endif
  47. // Retrieve the pinning policy for each domains
  48. if ((trustKitArguments[kTSKPinnedDomains] == nil) || ([trustKitArguments[kTSKPinnedDomains] count] < 1))
  49. {
  50. [NSException raise:@"TrustKit configuration invalid"
  51. format:@"TrustKit was initialized with no pinned domains. The configuration format has changed: ensure your domain pinning policies are under the TSKPinnedDomains key within TSKConfiguration."];
  52. }
  53. for (NSString *domainName in trustKitArguments[kTSKPinnedDomains])
  54. {
  55. // Sanity checks on the domain name
  56. if (GetRegistryLength([domainName UTF8String]) == 0)
  57. {
  58. [NSException raise:@"TrustKit configuration invalid"
  59. format:@"TrustKit was initialized with an invalid domain %@", domainName];
  60. }
  61. // Retrieve the supplied arguments for this domain
  62. NSDictionary *domainPinningPolicy = trustKitArguments[kTSKPinnedDomains][domainName];
  63. NSMutableDictionary *domainFinalConfiguration = [[NSMutableDictionary alloc]init];
  64. // Always start with the optional excludeSubDomain setting; if it set, no other TSKDomainConfigurationKey can be set for this domain
  65. NSNumber *shouldExcludeSubdomain = domainPinningPolicy[kTSKExcludeSubdomainFromParentPolicy];
  66. if (shouldExcludeSubdomain != nil && [shouldExcludeSubdomain boolValue])
  67. {
  68. // Confirm that no other TSKDomainConfigurationKeys were set for this domain
  69. if ([[domainPinningPolicy allKeys] count] > 1)
  70. {
  71. [NSException raise:@"TrustKit configuration invalid"
  72. format:@"TrustKit was initialized with TSKExcludeSubdomainFromParentPolicy for domain %@ but detected additional configuration keys", domainName];
  73. }
  74. // Store the whole configuration and continue to the next domain entry
  75. domainFinalConfiguration[kTSKExcludeSubdomainFromParentPolicy] = @(YES);
  76. finalConfiguration[kTSKPinnedDomains][domainName] = [NSDictionary dictionaryWithDictionary:domainFinalConfiguration];
  77. continue;
  78. }
  79. else
  80. {
  81. // Default setting is NO
  82. domainFinalConfiguration[kTSKExcludeSubdomainFromParentPolicy] = @(NO);
  83. }
  84. // Extract the optional includeSubdomains setting
  85. NSNumber *shouldIncludeSubdomains = domainPinningPolicy[kTSKIncludeSubdomains];
  86. if (shouldIncludeSubdomains == nil)
  87. {
  88. // Default setting is NO
  89. domainFinalConfiguration[kTSKIncludeSubdomains] = @(NO);
  90. }
  91. else
  92. {
  93. if ([shouldIncludeSubdomains boolValue] == YES)
  94. {
  95. // Prevent pinning on *.com
  96. // Ran into this issue with *.appspot.com which is part of the public suffix list
  97. if (GetRegistryLength([domainName UTF8String]) == [domainName length])
  98. {
  99. [NSException raise:@"TrustKit configuration invalid"
  100. format:@"TrustKit was initialized with includeSubdomains for a domain suffix %@", domainName];
  101. }
  102. }
  103. domainFinalConfiguration[kTSKIncludeSubdomains] = shouldIncludeSubdomains;
  104. }
  105. // Extract the optional expiration date setting
  106. NSString *expirationDateStr = domainPinningPolicy[kTSKExpirationDate];
  107. if (expirationDateStr != nil)
  108. {
  109. // Convert the string in the yyyy-MM-dd format into an actual date in UTC
  110. NSDateFormatter *dateFormat = [[NSDateFormatter alloc] init];
  111. dateFormat.dateFormat = @"yyyy-MM-dd";
  112. dateFormat.timeZone = [NSTimeZone timeZoneForSecondsFromGMT:0];
  113. NSDate *expirationDate = [dateFormat dateFromString:expirationDateStr];
  114. domainFinalConfiguration[kTSKExpirationDate] = expirationDate;
  115. }
  116. // Extract the optional enforcePinning setting
  117. NSNumber *shouldEnforcePinning = domainPinningPolicy[kTSKEnforcePinning];
  118. if (shouldEnforcePinning != nil)
  119. {
  120. domainFinalConfiguration[kTSKEnforcePinning] = shouldEnforcePinning;
  121. }
  122. else
  123. {
  124. // Default setting is YES
  125. domainFinalConfiguration[kTSKEnforcePinning] = @(YES);
  126. }
  127. // Extract the optional disableDefaultReportUri setting
  128. NSNumber *shouldDisableDefaultReportUri = domainPinningPolicy[kTSKDisableDefaultReportUri];
  129. if (shouldDisableDefaultReportUri != nil)
  130. {
  131. domainFinalConfiguration[kTSKDisableDefaultReportUri] = shouldDisableDefaultReportUri;
  132. }
  133. else
  134. {
  135. // Default setting is NO
  136. domainFinalConfiguration[kTSKDisableDefaultReportUri] = @(NO);
  137. }
  138. // Extract and convert the report URIs if defined
  139. NSArray<NSString *> *reportUriList = domainPinningPolicy[kTSKReportUris];
  140. if (reportUriList != nil)
  141. {
  142. NSMutableArray<NSURL *> *reportUriListFinal = [NSMutableArray array];
  143. for (NSString *reportUriStr in reportUriList)
  144. {
  145. NSURL *reportUri = [NSURL URLWithString:reportUriStr];
  146. if (reportUri == nil)
  147. {
  148. [NSException raise:@"TrustKit configuration invalid"
  149. format:@"TrustKit was initialized with an invalid value for %@ for domain %@", kTSKReportUris, domainName];
  150. }
  151. [reportUriListFinal addObject:reportUri];
  152. }
  153. domainFinalConfiguration[kTSKReportUris] = [NSArray arrayWithArray:reportUriListFinal];
  154. }
  155. // Extract and convert the subject public key info hashes
  156. NSArray<NSString *> *serverSslPinsBase64 = domainPinningPolicy[kTSKPublicKeyHashes];
  157. NSMutableSet<NSData *> *serverSslPinsSet = [NSMutableSet set];
  158. for (NSString *pinnedKeyHashBase64 in serverSslPinsBase64) {
  159. NSData *pinnedKeyHash = [[NSData alloc] initWithBase64EncodedString:pinnedKeyHashBase64 options:(NSDataBase64DecodingOptions)0];
  160. if ([pinnedKeyHash length] != CC_SHA256_DIGEST_LENGTH)
  161. {
  162. // The subject public key info hash doesn't have a valid size
  163. [NSException raise:@"TrustKit configuration invalid"
  164. format:@"TrustKit was initialized with an invalid Pin %@ for domain %@", pinnedKeyHashBase64, domainName];
  165. }
  166. [serverSslPinsSet addObject:pinnedKeyHash];
  167. }
  168. NSUInteger requiredNumberOfPins = [domainFinalConfiguration[kTSKEnforcePinning] boolValue] ? 2 : 1;
  169. if([serverSslPinsSet count] < requiredNumberOfPins)
  170. {
  171. [NSException raise:@"TrustKit configuration invalid"
  172. format:@"TrustKit was initialized with less than %lu pins (ie. no backup pins) for domain %@. This might brick your App; please review the Getting Started guide in ./docs/getting-started.md", (unsigned long)requiredNumberOfPins, domainName];
  173. }
  174. // Save the hashes for this server as an NSSet for quick lookup
  175. domainFinalConfiguration[kTSKPublicKeyHashes] = [NSSet setWithSet:serverSslPinsSet];
  176. // Store the whole configuration
  177. finalConfiguration[kTSKPinnedDomains][domainName] = [NSDictionary dictionaryWithDictionary:domainFinalConfiguration];
  178. }
  179. // Lastly, ensure that we can find a parent policy for subdomains configured with TSKExcludeSubdomainFromParentPolicy
  180. for (NSString *domainName in finalConfiguration[kTSKPinnedDomains])
  181. {
  182. if ([finalConfiguration[kTSKPinnedDomains][domainName][kTSKExcludeSubdomainFromParentPolicy] boolValue])
  183. {
  184. // To force the lookup of a parent domain, we append 'a' to this subdomain so we don't retrieve its policy
  185. NSString *parentDomainConfigKey = getPinningConfigurationKeyForDomain([@"a" stringByAppendingString:domainName], finalConfiguration[kTSKPinnedDomains]);
  186. if (parentDomainConfigKey == nil)
  187. {
  188. [NSException raise:@"TrustKit configuration invalid"
  189. format:@"TrustKit was initialized with TSKExcludeSubdomainFromParentPolicy for domain %@ but could not find a policy for a parent domain", domainName];
  190. }
  191. }
  192. }
  193. return [finalConfiguration copy];
  194. }