]> git.saurik.com Git - apple/security.git/blob - protocol/SecProtocolConfiguration.m
Security-59754.41.1.tar.gz
[apple/security.git] / protocol / SecProtocolConfiguration.m
1 //
2 // SecProtocolConfiguration.m
3 // Security
4 //
5
6 #import "SecProtocolInternal.h"
7 #import <Security/SecProtocolObject.h>
8 #import <Security/SecProtocolConfiguration.h>
9 #import <Security/SecureTransportPriv.h>
10 #import <CoreFoundation/CFPriv.h>
11 #import <Foundation/Foundation.h>
12
13 #define MINIMUM_RSA_KEY_SIZE 2048
14 #define MINIMUM_ECDSA_KEY_SIZE 256
15 #define MINIMUM_HASH_ALGORITHM kSecSignatureHashAlgorithmSHA256
16 #define MINIMUM_PROTOCOL kTLSProtocol12
17
18 static const char *
19 get_running_process()
20 {
21 static const char *processName = NULL;
22 static dispatch_once_t onceToken;
23 dispatch_once(&onceToken, ^{
24 const char **procName = _CFGetProgname();
25 processName = *procName;
26 });
27 return processName;
28 }
29
30 static bool
31 process_matches_target(const char *target_process)
32 {
33 if (target_process == NULL) {
34 return false;
35 }
36
37 const char *process = get_running_process();
38 if (process != NULL) {
39 return (strlen(target_process) == strlen(process) &&
40 strncmp(process, target_process, strlen(target_process)) == 0);
41 }
42 return false;
43 }
44
45 static bool
46 client_is_WebKit()
47 {
48 static bool is_WebKit = false;
49 static dispatch_once_t onceToken;
50 dispatch_once(&onceToken, ^{
51 is_WebKit = process_matches_target("com.apple.WebKit");
52 });
53 return is_WebKit;
54 }
55
56 static bool
57 client_is_mediaserverd()
58 {
59 static bool is_mediaserverd = false;
60 static dispatch_once_t onceToken;
61 dispatch_once(&onceToken, ^{
62 is_mediaserverd = process_matches_target("mediaserverd");
63 });
64 return is_mediaserverd;
65 }
66
67 sec_protocol_configuration_t
68 sec_protocol_configuration_copy_singleton(void)
69 {
70 static dispatch_once_t onceToken;
71 static sec_protocol_configuration_t singleton = nil;
72 dispatch_once(&onceToken, ^{
73 singleton = sec_protocol_configuration_create_with_builder(sec_protocol_configuration_builder_copy_default());
74 });
75 return singleton;
76 }
77
78 static sec_protocol_options_t
79 sec_protocol_configuration_copy_transformed_options_with_ats_minimums(sec_protocol_options_t options)
80 {
81 sec_protocol_options_set_ats_required(options, true);
82 sec_protocol_options_set_trusted_peer_certificate(options, true);
83 sec_protocol_options_set_minimum_rsa_key_size(options, MINIMUM_RSA_KEY_SIZE);
84 sec_protocol_options_set_minimum_ecdsa_key_size(options, MINIMUM_ECDSA_KEY_SIZE);
85 sec_protocol_options_set_minimum_signature_algorithm(options, MINIMUM_HASH_ALGORITHM);
86 sec_protocol_options_set_min_tls_protocol_version(options, tls_protocol_version_TLSv12);
87 return options;
88 }
89
90 sec_protocol_options_t
91 sec_protocol_configuration_copy_transformed_options(__unused sec_protocol_configuration_t config, sec_protocol_options_t options)
92 {
93 sec_protocol_options_clear_tls_ciphersuites(options);
94 sec_protocol_options_append_tls_ciphersuite_group(options, tls_ciphersuite_group_ats);
95 return sec_protocol_configuration_copy_transformed_options_with_ats_minimums(options);
96 }
97
98 static const char *
99 _find_parent_domain(const char *domain)
100 {
101 size_t domain_len = strlen(domain);
102 size_t index = 0;
103 while (index < domain_len) {
104 // Once we hit a dot, the parent domain begins at the next segment.
105 if (domain[index] == '.' && index < domain_len) {
106 return domain + 1;
107 }
108
109 // Skip over all characters that are not dots.
110 index++;
111 }
112
113 return NULL;
114 }
115
116 static sec_protocol_options_t
117 sec_protocol_configuration_copy_transformed_options_for_host_internal(sec_protocol_configuration_t config, sec_protocol_options_t options,
118 const char *host, bool parent_domain)
119 {
120 xpc_object_t map = sec_protocol_configuration_get_map(config);
121 if (map == nil) {
122 return options;
123 }
124
125 xpc_object_t domain_map = xpc_dictionary_get_dictionary(map, kExceptionDomains);
126 if (domain_map == nil) {
127 return options;
128 }
129
130 xpc_object_t entry = xpc_dictionary_get_dictionary(domain_map, host);
131 if (entry == nil) {
132 const char *parent_host = _find_parent_domain(host);
133 if (parent_host != NULL) {
134 return sec_protocol_configuration_copy_transformed_options_for_host_internal(config, options, parent_host, true);
135 }
136
137 // If we could not find a matching domain, apply the default connection properties.
138 return sec_protocol_configuration_copy_transformed_options(config, options);
139 }
140
141 bool pfs_required = xpc_dictionary_get_bool(entry, kExceptionRequiresForwardSecrecy);
142 if (pfs_required) {
143 sec_protocol_options_clear_tls_ciphersuites(options);
144 sec_protocol_options_append_tls_ciphersuite_group(options, tls_ciphersuite_group_ats);
145 } else {
146 // Otherwise, record the fact that non-PFS ciphersuites are permitted.
147 // This does not mean that the caller actually configured a non-PFS ciphersuite.
148 sec_protocol_options_set_ats_non_pfs_ciphersuite_allowed(options, true);
149 }
150
151 tls_protocol_version_t minimum_protocol = (SSLProtocol)xpc_dictionary_get_int64(entry, kExceptionMinimumTLSVersion);
152 if (minimum_protocol != 0) {
153 // Record the fact that an excepted TLS version was configured.
154 sec_protocol_options_set_min_tls_protocol_version(options, minimum_protocol);
155 sec_protocol_options_set_ats_minimum_tls_version_allowed(options, true);
156 }
157
158 return options;
159 }
160
161 sec_protocol_options_t
162 sec_protocol_configuration_copy_transformed_options_for_host(sec_protocol_configuration_t config, sec_protocol_options_t options, const char *host)
163 {
164 return sec_protocol_configuration_copy_transformed_options_for_host_internal(config, sec_protocol_configuration_copy_transformed_options_with_ats_minimums(options), host, false);
165 }
166
167 bool
168 sec_protocol_configuration_tls_required(sec_protocol_configuration_t config)
169 {
170 xpc_object_t map = sec_protocol_configuration_get_map(config);
171 if (map == nil) {
172 // Fail closed.
173 return true;
174 }
175
176 bool allows_media_loads = xpc_dictionary_get_bool(map, kAllowsArbitraryLoadsForMedia);
177 if (allows_media_loads && client_is_mediaserverd()) {
178 return false;
179 }
180
181 bool allows_web_loads = xpc_dictionary_get_bool(map, kAllowsArbitraryLoadsInWebContent);
182 if (allows_web_loads && client_is_WebKit()) {
183 return false;
184 }
185
186 return !xpc_dictionary_get_bool(map, kAllowsArbitraryLoads);
187 }
188
189 static bool
190 sec_protocol_configuration_tls_required_for_host_internal(sec_protocol_configuration_t config, const char *host, bool parent_domain, bool is_direct)
191 {
192 xpc_object_t map = sec_protocol_configuration_get_map(config);
193 if (map == nil) {
194 // Fail closed.
195 return true;
196 }
197
198 if (is_direct && xpc_dictionary_get_bool(map, kAllowsLocalNetworking)) {
199 // Local domains do not require TLS if the kAllowsLocalNetworking flag is set.
200 return false;
201 }
202
203 xpc_object_t domain_map = xpc_dictionary_get_dictionary(map, kExceptionDomains);
204 if (domain_map == nil) {
205 // Absent per-domain exceptions, use the default.
206 return sec_protocol_configuration_tls_required(config);
207 }
208
209 xpc_object_t entry = xpc_dictionary_get_dictionary(domain_map, host);
210 if (entry == nil) {
211 const char *parent_host = _find_parent_domain(host);
212 if (parent_host != NULL) {
213 return sec_protocol_configuration_tls_required_for_host_internal(config, parent_host, true, is_direct);
214 }
215 return sec_protocol_configuration_tls_required(config);
216 }
217
218 bool requires_tls = !xpc_dictionary_get_bool(entry, kExceptionAllowsInsecureHTTPLoads);
219 bool includes_subdomains = !xpc_dictionary_get_bool(entry, kExceptionAllowsInsecureHTTPLoads);
220
221 if (parent_domain && !includes_subdomains) {
222 // If this domain's exceptions do not apply to subdomains, then default to the application default policy.
223 return sec_protocol_configuration_tls_required(config);
224 }
225
226 return requires_tls;
227 }
228
229 bool
230 sec_protocol_configuration_tls_required_for_host(sec_protocol_configuration_t config, const char *host, bool is_direct)
231 {
232 return sec_protocol_configuration_tls_required_for_host_internal(config, host, false, is_direct);
233 }
234
235 bool
236 sec_protocol_configuration_tls_required_for_address(sec_protocol_configuration_t config, const char *address)
237 {
238 xpc_object_t map = sec_protocol_configuration_get_map(config);
239 if (map == nil) {
240 // Fail closed.
241 return true;
242 }
243
244 return !xpc_dictionary_get_bool(map, kAllowsLocalNetworking);
245 }
246
247 static tls_protocol_version_t
248 sec_protocol_configuration_protocol_string_to_version(const char *protocol)
249 {
250 if (protocol == NULL) {
251 return 0;
252 }
253
254 const char *tlsv10 = "TLSv1.0";
255 const char *tlsv11 = "TLSv1.1";
256 const char *tlsv12 = "TLSv1.2";
257 const char *tlsv13 = "TLSv1.3";
258
259 if (strlen(protocol) == strlen(tlsv10) && strncmp(protocol, tlsv10, strlen(protocol)) == 0) {
260 return tls_protocol_version_TLSv10;
261 } else if (strlen(protocol) == strlen(tlsv11) && strncmp(protocol, tlsv11, strlen(protocol)) == 0) {
262 return tls_protocol_version_TLSv11;
263 } else if (strlen(protocol) == strlen(tlsv12) && strncmp(protocol, tlsv12, strlen(protocol)) == 0) {
264 return tls_protocol_version_TLSv12;
265 } else if (strlen(protocol) == strlen(tlsv13) && strncmp(protocol, tlsv13, strlen(protocol)) == 0) {
266 return tls_protocol_version_TLSv13;
267 }
268
269 return 0;
270 }
271
272 static void
273 sec_protocol_configuration_register_builtin_exception(xpc_object_t dict, const char *name,
274 tls_protocol_version_t protocol, bool requires_pfs,
275 bool allows_http, bool includes_subdomains, bool require_ct)
276 {
277 xpc_object_t domain_map = xpc_dictionary_get_dictionary(dict, kExceptionDomains);
278 if (domain_map) {
279 xpc_object_t entry = xpc_dictionary_create(NULL, NULL, 0);
280 xpc_dictionary_set_value(entry, kExceptionDomains, domain_map);
281
282 xpc_dictionary_set_bool(entry, kIncludesSubdomains, includes_subdomains);
283 xpc_dictionary_set_int64(entry, kExceptionMinimumTLSVersion, protocol);
284 xpc_dictionary_set_bool(entry, kExceptionAllowsInsecureHTTPLoads, allows_http);
285 xpc_dictionary_set_bool(entry, kExceptionRequiresForwardSecrecy, requires_pfs);
286
287 xpc_dictionary_set_value(domain_map, name, entry);
288 }
289 }
290
291 void
292 sec_protocol_configuration_register_builtin_exceptions(sec_protocol_configuration_t config)
293 {
294 xpc_object_t dict = sec_protocol_configuration_get_map(config);
295 sec_protocol_configuration_register_builtin_exception(dict, "apple.com", tls_protocol_version_TLSv12, false, true, true, true);
296 sec_protocol_configuration_register_builtin_exception(dict, "ls.apple.com", tls_protocol_version_TLSv10, false, true, true, true);
297 sec_protocol_configuration_register_builtin_exception(dict, "gs.apple.com", tls_protocol_version_TLSv10, false, true, true, true);
298 sec_protocol_configuration_register_builtin_exception(dict, "geo.apple.com", tls_protocol_version_TLSv10, false, true, true, true);
299 sec_protocol_configuration_register_builtin_exception(dict, "is.autonavi.com", tls_protocol_version_TLSv10, false, true, true, true);
300 sec_protocol_configuration_register_builtin_exception(dict, "apple-mapkit.com", tls_protocol_version_TLSv10, false, true, true, true);
301 sec_protocol_configuration_register_builtin_exception(dict, "setup.icloud.com", tls_protocol_version_TLSv12, false, true, true, true);
302 }
303
304 void
305 sec_protocol_configuration_populate_insecure_defaults(sec_protocol_configuration_t config)
306 {
307 xpc_object_t dict = sec_protocol_configuration_get_map(config);
308 xpc_object_t domain_map = xpc_dictionary_create(NULL, NULL, 0);
309 xpc_dictionary_set_value(dict, kExceptionDomains, domain_map);
310
311 xpc_dictionary_set_bool(dict, kAllowsArbitraryLoadsInWebContent, true);
312 xpc_dictionary_set_bool(dict, kAllowsArbitraryLoadsForMedia, true);
313 xpc_dictionary_set_bool(dict, kAllowsLocalNetworking, true);
314 xpc_dictionary_set_bool(dict, kAllowsArbitraryLoads, true);
315 }
316
317 void
318 sec_protocol_configuration_populate_secure_defaults(sec_protocol_configuration_t config)
319 {
320 xpc_object_t dict = sec_protocol_configuration_get_map(config);
321 xpc_object_t domain_map = xpc_dictionary_create(NULL, NULL, 0);
322 xpc_dictionary_set_value(dict, kExceptionDomains, domain_map);
323
324 xpc_dictionary_set_bool(dict, kAllowsArbitraryLoadsInWebContent, false);
325 xpc_dictionary_set_bool(dict, kAllowsArbitraryLoadsForMedia, false);
326 xpc_dictionary_set_bool(dict, kAllowsLocalNetworking, false);
327 xpc_dictionary_set_bool(dict, kAllowsArbitraryLoads, false);
328 }
329
330 bool
331 sec_protocol_configuration_set_ats_overrides(sec_protocol_configuration_t config, CFDictionaryRef plist)
332 {
333 if (plist == NULL) {
334 return false;
335 }
336
337 #define BOOLEAN_FOR_KEY(dictionary, key, value, default) \
338 bool value = default; \
339 { \
340 if (dictionary[[[NSString alloc] initWithFormat:@"%s", key]]) { \
341 NSNumber *nsValue = [dictionary valueForKey:[[NSString alloc] initWithFormat:@"%s", key]]; \
342 if (nsValue) { \
343 value = [nsValue boolValue]; \
344 } \
345 } \
346 }
347 #define STRING_FOR_KEY(dictionary, key, value, default) \
348 NSString *value = default; \
349 { \
350 if (dictionary[[[NSString alloc] initWithFormat:@"%s", key]]) { \
351 NSString *nsValue = [dictionary valueForKey:[[NSString alloc] initWithFormat:@"%s", key]]; \
352 if (nsValue) { \
353 value = nsValue; \
354 } \
355 } \
356 }
357
358 xpc_object_t dict = sec_protocol_configuration_get_map(config);
359 if (dict == nil) {
360 return false;
361 }
362
363 NSDictionary *plist_dictionary = (__bridge NSDictionary *)plist;
364 BOOLEAN_FOR_KEY(plist_dictionary, kAllowsArbitraryLoads, arbitrary_loads, false);
365 BOOLEAN_FOR_KEY(plist_dictionary, kAllowsArbitraryLoadsInWebContent, web_loads, false);
366 BOOLEAN_FOR_KEY(plist_dictionary, kAllowsArbitraryLoadsForMedia, media_loads, false);
367 BOOLEAN_FOR_KEY(plist_dictionary, kAllowsLocalNetworking, local_networking, false);
368
369 xpc_dictionary_set_bool(dict, kAllowsArbitraryLoads, arbitrary_loads);
370 xpc_dictionary_set_bool(dict, kAllowsArbitraryLoadsInWebContent, web_loads);
371 xpc_dictionary_set_bool(dict, kAllowsArbitraryLoadsForMedia, media_loads);
372 xpc_dictionary_set_bool(dict, kAllowsLocalNetworking, local_networking);
373
374 NSDictionary *exception_domains = [plist_dictionary valueForKey:[[NSString alloc] initWithFormat:@"%s", kExceptionDomains]];
375 if (exception_domains == nil) {
376 return true;
377 }
378
379 xpc_object_t domain_map = xpc_dictionary_get_dictionary(dict, kExceptionDomains);
380 if (domain_map == nil) {
381 // The domain map MUST be present during initialziation
382 return false;
383 }
384
385 [exception_domains enumerateKeysAndObjectsUsingBlock:^(id _key, id _obj, BOOL *stop) {
386 NSString *domain = (NSString *)_key;
387 NSDictionary *entry = (NSDictionary *)_obj;
388 if (entry == nil) {
389 // Exception domains MUST have ATS information set.
390 *stop = YES;
391 }
392
393 BOOLEAN_FOR_KEY(entry, kExceptionAllowsInsecureHTTPLoads, allows_http, false);
394 BOOLEAN_FOR_KEY(entry, kIncludesSubdomains, includes_subdomains, false);
395 BOOLEAN_FOR_KEY(entry, kExceptionRequiresForwardSecrecy, requires_pfs, false);
396 STRING_FOR_KEY(entry, kExceptionMinimumTLSVersion, minimum_tls, @"TLSv1.2");
397
398 xpc_object_t entry_map = xpc_dictionary_create(NULL, NULL, 0);
399 xpc_dictionary_set_bool(entry_map, kIncludesSubdomains, includes_subdomains);
400 xpc_dictionary_set_bool(entry_map, kExceptionAllowsInsecureHTTPLoads, allows_http);
401 xpc_dictionary_set_bool(entry_map, kExceptionRequiresForwardSecrecy, requires_pfs);
402 xpc_dictionary_set_int64(entry_map, kExceptionMinimumTLSVersion, sec_protocol_configuration_protocol_string_to_version([minimum_tls cStringUsingEncoding:NSUTF8StringEncoding]));
403 xpc_dictionary_set_value(domain_map, [domain cStringUsingEncoding:NSUTF8StringEncoding], entry_map);
404 }];
405
406 #undef STRING_FOR_KEY
407 #undef BOOLEAN_FOR_KEY
408
409 return true;
410 }