1/*
2 * Copyright (c) 2012-2013 Apple Inc. All rights reserved.
3 */
4#include <notify.h>
5#include <ne_session.h>
6
7#include <SystemConfiguration/SCPrivate.h>
8#include <SystemConfiguration/VPNAppLayerPrivate.h>
9#include <SystemConfiguration/SNHelperPrivate.h>
10#include <Security/Security.h>
11
12#include <netinet/flow_divert_proto.h>
13
14#include "scnc_main.h"
15#include "scnc_utils.h"
16
17#include "app_layer.h"
18#include "flow_divert_controller.h"
19
20#ifndef FLOW_DIVERT_DNS_SERVICE_SIGNING_ID
21#define FLOW_DIVERT_DNS_SERVICE_SIGNING_ID		"com.apple.mDNSResponder"
22#endif /* FLOW_DIVERT_DNS_SERVICE_SIGNING_ID */
23
24static CFStringRef g_dynamic_store_key = NULL;
25
26static int
27app_layer_get_first_enabled_service_rank(SCPreferencesRef prefs, CFStringRef rule_id, CFDictionaryRef rules_hash, CFArrayRef service_order)
28{
29	CFArrayRef match_services;
30	CFDictionaryRef rule;
31	CFIndex rank = -1;
32
33	rule = CFDictionaryGetValue(rules_hash, rule_id);
34	if (isDictionary(rule)) {
35		CFIndex idx;
36		match_services = CFDictionaryGetValue(rule, kVPNAppLayerRuleMatchServices);
37		for (idx = 0; idx < CFArrayGetCount(match_services); idx++) {
38			CFDictionaryRef match_service = CFArrayGetValueAtIndex(match_services, idx);
39			CFStringRef service_id = CFDictionaryGetValue(match_service, kVPNAppLayerRuleMatchServiceID);
40			SCNetworkServiceRef service = SCNetworkServiceCopy(prefs, service_id);
41			if (service != NULL) {
42				if (SCNetworkServiceGetEnabled(service)) {
43					if (service_order != NULL) {
44						rank = CFArrayGetFirstIndexOfValue(service_order, CFRangeMake(0, CFArrayGetCount(service_order)), service_id);
45					} else {
46						rank = idx;
47					}
48				}
49				CFRelease(service);
50				if (rank >= 0) {
51					break;
52				}
53			}
54		}
55	}
56
57	return rank;
58}
59
60
61static CFIndex
62app_layer_compare_rules(SCPreferencesRef prefs, CFStringRef rule_id1, CFStringRef rule_id2, CFDictionaryRef rules_hash, CFArrayRef service_order)
63{
64	CFIndex rank1 = -1;
65	CFIndex rank2 = -1;
66
67	if (CFEqual(rule_id1, rule_id2)) {
68		return 0;
69	}
70
71	rank1 = app_layer_get_first_enabled_service_rank(prefs, rule_id1, rules_hash, service_order);
72	rank2 = app_layer_get_first_enabled_service_rank(prefs, rule_id2, rules_hash, service_order);
73
74	if (rank1 >= 0 && rank2 >= 0) {
75		return (rank2 - rank1);
76	} else {
77		return rank1 - rank2;
78	}
79}
80
81
82static void
83app_layer_add_executable(SCPreferencesRef prefs, CFMutableDictionaryRef config, CFStringRef rule_id, CFArrayRef exec_infos, CFDictionaryRef rules, CFArrayRef service_order)
84{
85	CFIndex exec_idx;
86	CFIndex exec_info_count = CFArrayGetCount(exec_infos);
87	CFMutableDictionaryRef executables =
88		(CFMutableDictionaryRef)CFDictionaryGetValue(config, kVPNAppLayerMatchExecutables);
89
90	/* Create the signing identifier hash if it doesn't already exist */
91	if (!isDictionary(executables)) {
92		executables = CFDictionaryCreateMutable(kCFAllocatorDefault,
93		                                        0,
94		                                        &kCFTypeDictionaryKeyCallBacks,
95		                                        &kCFTypeDictionaryValueCallBacks);
96		CFDictionarySetValue(config, kVPNAppLayerMatchExecutables, executables);
97		CFRelease(executables);
98	}
99
100	/* Create an entry in the signing identifier hash for each set of executable info in the rule */
101	for (exec_idx = 0; exec_idx < exec_info_count; exec_idx++) {
102		CFDictionaryRef exec_info = CFArrayGetValueAtIndex(exec_infos, exec_idx);
103		CFStringRef signing_id = CFDictionaryGetValue(exec_info, kSCValNetVPNAppRuleExecutableSigningIdentifier);
104
105		if (isString(signing_id)) {
106			Boolean do_insert = TRUE;
107
108			CFDictionaryRef existing_match = CFDictionaryGetValue(executables, signing_id);
109			if (isDictionary(existing_match)) {
110				CFStringRef existing_rule_id = CFDictionaryGetValue(existing_match, kSCValNetVPNAppRuleIdentifier);
111				if (isString(existing_rule_id) && app_layer_compare_rules(prefs, rule_id, existing_rule_id, rules, service_order) < 0) {
112					do_insert = FALSE;
113				}
114			}
115
116			if (do_insert) {
117				CFStringRef designated_req = CFDictionaryGetValue(exec_info, kSCValNetVPNAppRuleExecutableDesignatedRequirement);
118				CFMutableDictionaryRef new_exec_match = CFDictionaryCreateMutable(kCFAllocatorDefault,
119				                                                                  0,
120				                                                                  &kCFTypeDictionaryKeyCallBacks,
121				                                                                  &kCFTypeDictionaryValueCallBacks);
122
123				CFDictionarySetValue(new_exec_match, kSCValNetVPNAppRuleIdentifier, rule_id);
124				if (isString(designated_req)) {
125#if ! TARGET_OS_EMBEDDED
126					SecRequirementRef requirement = NULL;
127					OSStatus req_status;
128
129					req_status = SecRequirementCreateWithString(designated_req, kSecCSDefaultFlags, &requirement);
130					if (req_status != errSecSuccess) {
131						SCLog(TRUE, LOG_WARNING, CFSTR("Discarding executable with invalid designated requirement: %@"), designated_req);
132						do_insert = FALSE;
133					}
134
135					if (requirement != NULL) {
136						CFRelease(requirement);
137					}
138#endif /* ! TARGET_OS_EMBEDDED */
139					CFDictionarySetValue(new_exec_match, kSCValNetVPNAppRuleExecutableDesignatedRequirement, designated_req);
140				}
141#if ! TARGET_OS_EMBEDDED
142			   	else {
143					SCLog(TRUE, LOG_WARNING, CFSTR("Discarding executable with missing designated requirement: %@"), exec_info);
144					do_insert = FALSE;
145				}
146#endif /* ! TARGET_OS_EMBEDDED */
147
148				if (do_insert) {
149					CFDictionarySetValue(executables, signing_id, new_exec_match);
150				}
151
152				CFRelease(new_exec_match);
153			}
154		}
155	}
156
157	SCLog(gSCNCDebug, LOG_INFO, CFSTR("Executables: %@"), executables);
158}
159
160static void
161app_layer_add_account(SCPreferencesRef prefs, CFMutableDictionaryRef config, CFStringRef rule_id, CFArrayRef account_infos, CFDictionaryRef rules, CFArrayRef service_order)
162{
163	CFMutableDictionaryRef accounts = (CFMutableDictionaryRef)CFDictionaryGetValue(config, kVPNAppLayerMatchAccounts);
164
165	if (account_infos != NULL) {
166		CFIndex account_idx;
167		CFIndex num_accounts = CFArrayGetCount(account_infos);
168		if (num_accounts != 0) {
169			if (!isDictionary(accounts)) {
170				accounts = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
171				CFDictionarySetValue(config, kVPNAppLayerMatchAccounts, accounts);
172				CFRelease(accounts);
173			}
174
175			for (account_idx = 0; account_idx < num_accounts; account_idx++) {
176				CFStringRef account_identifier = CFArrayGetValueAtIndex(account_infos, account_idx);
177				if (isString(account_identifier)) {
178					CFStringRef existing_rule_ID = CFDictionaryGetValue(accounts, account_identifier);
179					if (!isString(existing_rule_ID) || !(app_layer_compare_rules(prefs, rule_id, existing_rule_ID, rules, service_order) < 0)) {
180						CFDictionarySetValue(accounts, account_identifier, rule_id);
181					}
182				}
183			}
184		}
185	}
186}
187
188#ifndef kSCValNetVPNAppRuleAccountIdentifierMatch
189#define kSCValNetVPNAppRuleAccountIdentifierMatch CFSTR("AccountIdentifierMatch")
190#endif
191
192static void
193app_layer_add_service(SCPreferencesRef prefs, CFMutableDictionaryRef config, SCNetworkServiceRef scservice, CFArrayRef service_order)
194{
195	CFMutableDictionaryRef rules = (CFMutableDictionaryRef)CFDictionaryGetValue(config, kVPNAppLayerRules);
196	CFIndex rule_count;
197	CFArrayRef rule_ids;
198	CFIndex rule_idx;
199	CFStringRef service_id = SCNetworkServiceGetServiceID(scservice);
200	CFIndex service_rank = 0;
201
202	rule_ids = VPNServiceCopyAppRuleIDs(scservice);
203	if (rule_ids == NULL) {
204		return;
205	}
206
207	if (service_order != NULL) {
208		service_rank = CFArrayGetFirstIndexOfValue(service_order, CFRangeMake(0, CFArrayGetCount(service_order)), service_id);
209	}
210
211	rule_count = CFArrayGetCount(rule_ids);
212	for (rule_idx = 0; rule_idx < rule_count; rule_idx++) {
213		CFStringRef rule_ID = CFArrayGetValueAtIndex(rule_ids, rule_idx);
214		CFDictionaryRef rule_settings = VPNServiceCopyAppRule(scservice, rule_ID);
215		if (rule_settings != NULL) {
216			CFArrayRef rule_match_domains = CFDictionaryGetValue(rule_settings, kSCValNetVPNAppRuleDNSDomainMatch);
217			CFArrayRef rule_match_executables = CFDictionaryGetValue(rule_settings, kSCValNetVPNAppRuleExecutableMatch);
218			CFArrayRef rule_match_accounts = CFDictionaryGetValue(rule_settings, kSCValNetVPNAppRuleAccountIdentifierMatch);
219			CFMutableArrayRef match_services = NULL;
220			CFIndex service_insert_idx = -1;
221			Boolean replace = FALSE;
222			CFMutableDictionaryRef new_match_service;
223
224			if (isDictionary(rules)) {
225				CFDictionaryRef rule = CFDictionaryGetValue(rules, rule_ID);
226				if (isDictionary(rule)) {
227					match_services = (CFMutableArrayRef)CFDictionaryGetValue(rule, kVPNAppLayerRuleMatchServices);
228					if (isArray(match_services)) {
229						CFIndex service_count = CFArrayGetCount(match_services);
230
231						if (SCNetworkServiceGetEnabled(scservice)) {
232							CFIndex service_idx;
233							/* Figure out where to insert this service into the rule's list of services */
234							for (service_idx = 0; service_idx < service_count; service_idx++) {
235								CFDictionaryRef match_service = CFArrayGetValueAtIndex(match_services, service_idx);
236								if (isDictionary(match_service)) {
237									CFStringRef match_sid = CFDictionaryGetValue(match_service, kVPNAppLayerRuleMatchServiceID);
238
239									if (CFEqual(match_sid, service_id)) {
240										service_insert_idx = service_idx;
241										replace = TRUE;
242										break;
243									} else {
244										CFIndex rank = 0;
245										if (service_order != NULL) {
246											rank = CFArrayGetFirstIndexOfValue(service_order,
247											                                   CFRangeMake(0, CFArrayGetCount(service_order)),
248											                                   match_sid);
249										}
250										if (service_rank >= 0 && rank > service_rank) {
251											service_insert_idx = service_idx;
252											break;
253										}
254									}
255								}
256							}
257						}
258
259						if (service_insert_idx < 0) {
260							/* Put it at the end of the list */
261							service_insert_idx = service_count;
262						}
263					} else {
264						match_services = NULL;
265					}
266				}
267			}
268
269			new_match_service = CFDictionaryCreateMutable(kCFAllocatorDefault,
270					0,
271					&kCFTypeDictionaryKeyCallBacks,
272					&kCFTypeDictionaryValueCallBacks);
273
274			CFDictionarySetValue(new_match_service, kVPNAppLayerRuleMatchServiceID, service_id);
275			if (rule_match_domains != NULL) {
276				CFDictionarySetValue(new_match_service, kVPNAppLayerRuleMatchServiceDomains, rule_match_domains);
277			}
278
279			if (service_insert_idx >= 0 && isArray(match_services)) {
280				if (replace) {
281					CFArraySetValueAtIndex(match_services, service_insert_idx, new_match_service);
282				} else {
283					CFArrayInsertValueAtIndex(match_services, service_insert_idx, new_match_service);
284				}
285			} else {
286				/* Here if we did not find an existing rule corresponding to this rule */
287				CFMutableDictionaryRef new_rule = CFDictionaryCreateMutable(kCFAllocatorDefault,
288				                                                            0,
289				                                                            &kCFTypeDictionaryKeyCallBacks,
290				                                                            &kCFTypeDictionaryValueCallBacks);
291
292				match_services = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
293				CFArrayAppendValue(match_services, new_match_service);
294
295				CFDictionarySetValue(new_rule, kVPNAppLayerRuleMatchServices, match_services);
296
297				if (!isDictionary(rules)) {
298					rules = CFDictionaryCreateMutable(kCFAllocatorDefault,
299					                                  0,
300					                                  &kCFTypeDictionaryKeyCallBacks,
301					                                  &kCFTypeDictionaryValueCallBacks);
302					CFDictionarySetValue(config, kVPNAppLayerRules, rules);
303					CFRelease(rules);
304				}
305
306				CFDictionarySetValue(rules, rule_ID, new_rule);
307
308				CFRelease(new_rule);
309				CFRelease(match_services);
310			}
311
312			if (rule_match_executables) {
313				app_layer_add_executable(prefs, config, rule_ID, rule_match_executables, rules, service_order);
314			}
315
316			if (rule_match_accounts) {
317				app_layer_add_account(prefs, config, rule_ID, rule_match_accounts, rules, service_order);
318			}
319
320			CFRelease(new_match_service);
321			CFRelease(rule_settings);
322		}
323	}
324
325	CFRelease(rule_ids);
326}
327
328static void
329app_layer_post_config_changed_notification(CFIndex rule_count)
330{
331	static int notify_token = -1;
332	uint32_t status;
333	uint64_t state = rule_count;
334
335	if (notify_token == -1) {
336		status = notify_register_check(kVPNAPPLAYER_NOTIFY_KEY, &notify_token);
337		if (status != NOTIFY_STATUS_OK) {
338			SCLog(TRUE, LOG_ERR, CFSTR("app_layer_post_config_changed_notification: notify_register_check failed, status = %d"), status);
339			return;
340		}
341	}
342
343	status = notify_set_state(notify_token, state);
344	if (status != NOTIFY_STATUS_OK) {
345		SCLog(TRUE, LOG_ERR, CFSTR("app_layer_post_config_changed_notification: notify_set_state failed, status = %d"), status);
346		notify_cancel(notify_token);
347		notify_token = -1;
348		return;
349	}
350
351	status = notify_post(kVPNAPPLAYER_NOTIFY_KEY);
352	if (status != NOTIFY_STATUS_OK) {
353		SCLog(TRUE, LOG_ERR, CFSTR("app_layer_post_config_changed_notification: notify_post failed, status = %d"), status);
354		notify_cancel(notify_token);
355		notify_token = -1;
356		return;
357	}
358}
359
360#if TARGET_OS_EMBEDDED
361
362static Boolean
363app_layer_is_app_rule_split_or_inactive(CFDictionaryRef rules, CFStringRef rule_id)
364{
365	Boolean result = FALSE;
366	CFIndex idx;
367	CFDictionaryRef rule = CFDictionaryGetValue(rules, rule_id);
368	if (isDictionary(rule)) {
369		CFArrayRef match_services = CFDictionaryGetValue(rule, kVPNAppLayerRuleMatchServices);
370		if (isArray(match_services)) {
371			CFIndex match_services_count = CFArrayGetCount(match_services);
372
373			for (idx = 0; idx < match_services_count; idx++) {
374				CFDictionaryRef match_service = CFArrayGetValueAtIndex(match_services, idx);
375				if (isDictionary(match_service)) {
376					CFArrayRef match_service_domains = CFDictionaryGetValue(match_service, kVPNAppLayerRuleMatchServiceDomains);
377					if (!isArray(match_service_domains) || CFArrayGetCount(match_service_domains) == 0) {
378						CFStringRef match_service_id = CFDictionaryGetValue(match_service, kVPNAppLayerRuleMatchServiceID);
379						if (isString(match_service_id)) {
380							struct service *serv = findbyserviceID(match_service_id);
381							if (serv != NULL && serv->ondemandAction &&
382							    CFStringCompare(serv->ondemandAction, kSCValNetVPNOnDemandRuleActionDisconnect, 0) == kCFCompareEqualTo)
383							{
384								result = TRUE;
385							}
386						}
387						break;
388					}
389				}
390			}
391
392			if (idx == match_services_count) {
393				/* None of the match services had empty match domains */
394				result = TRUE;
395			}
396		}
397	}
398
399	return result;
400}
401
402static Boolean
403app_layer_app_rule_has_service(CFDictionaryRef rules, CFStringRef rule_id, CFStringRef service_id)
404{
405	CFDictionaryRef rule = CFDictionaryGetValue(rules, rule_id);
406	if (isDictionary(rule)) {
407		CFArrayRef match_services = CFDictionaryGetValue(rule, kVPNAppLayerRuleMatchServices);
408		if (isArray(match_services)) {
409			CFIndex match_services_count = CFArrayGetCount(match_services);
410			CFIndex idx;
411			for (idx = 0; idx < match_services_count; idx++) {
412				CFDictionaryRef match_service = CFArrayGetValueAtIndex(match_services, idx);
413				if (isDictionary(match_service)) {
414					CFStringRef match_service_id = CFDictionaryGetValue(match_service, kVPNAppLayerRuleMatchServiceID);
415					if (isString(match_service_id) && CFEqual(service_id, match_service_id)) {
416						return TRUE;
417					}
418				}
419			}
420		}
421	}
422
423	return FALSE;
424}
425
426static Boolean
427cfstring2uuid(CFStringRef cf_str, uuid_t result)
428{
429	uuid_string_t uuid_string;
430
431	if (CFStringGetCString(cf_str, uuid_string, sizeof(uuid_string), kCFStringEncodingASCII)) {
432		if (uuid_parse(uuid_string, result) == 0) {
433			return TRUE;
434		}
435	}
436
437	return FALSE;
438}
439
440static void
441app_layer_setup_executable_uuid(CFStringRef signing_identifier, CFDictionaryRef rules, CFMutableDictionaryRef executable)
442{
443	CFStringRef rule_id = CFDictionaryGetValue(executable, kSCValNetVPNAppRuleIdentifier);
444	CFStringRef existing_uuid = CFDictionaryGetValue(executable, kSCValNetVPNAppRuleExecutableUUID);
445	uuid_t existing_uuid_bytes;
446	Boolean remove_existing = TRUE;
447
448	if (!isString(existing_uuid) || !cfstring2uuid(existing_uuid, existing_uuid_bytes)) {
449		uuid_clear(existing_uuid_bytes);
450	}
451
452	if (app_layer_is_app_rule_split_or_inactive(rules, rule_id)) {
453		/*
454		 * If the associated rule is "split", i.e. there are no associated services
455		 * with a null match domain set, or if the service's current network detection
456		 * action is "disconnect" then we don't want to set this executable's
457		 * UUID in the policy table. Remove any existing UUID.
458		 */
459		CFDictionaryRemoveValue(executable, kSCValNetVPNAppRuleExecutableUUID);
460	} else {
461		CFIndex len = CFStringGetMaximumSizeForEncoding(CFStringGetLength(signing_identifier), kCFStringEncodingUTF8) + 1;
462		char *signing_id_cstr = (char *)malloc(len);
463		uuid_t uuid_bytes;
464
465		CFStringGetCString(signing_identifier, signing_id_cstr, len, kCFStringEncodingUTF8);
466
467		if (snhelper_get_uuid_for_app(signing_id_cstr, uuid_bytes) == 0) {
468			if (uuid_is_null(existing_uuid_bytes) || uuid_compare(uuid_bytes, existing_uuid_bytes)) {
469				uuid_string_t new_uuid_str;
470				CFStringRef new_uuid;
471
472				VPNAppLayerUUIDPolicyAdd(uuid_bytes);
473
474				uuid_unparse(uuid_bytes, new_uuid_str);
475				new_uuid = CFStringCreateWithCString(kCFAllocatorDefault, new_uuid_str, kCFStringEncodingASCII);
476				CFDictionarySetValue(executable, kSCValNetVPNAppRuleExecutableUUID, new_uuid);
477				CFRelease(new_uuid);
478			} else {
479				remove_existing = FALSE;
480			}
481		} else {
482			CFDictionaryRemoveValue(executable, kSCValNetVPNAppRuleExecutableUUID);
483		}
484
485		free(signing_id_cstr);
486	}
487
488	if (remove_existing && !uuid_is_null(existing_uuid_bytes)) {
489		VPNAppLayerUUIDPolicyRemove(existing_uuid_bytes);
490	}
491}
492
493#endif /* TARGET_OS_EMBEDDED */
494
495void
496app_layer_install_app(CFStringRef signing_id)
497{
498#if TARGET_OS_EMBEDDED
499	CFDictionaryRef config = SCDynamicStoreCopyValue(gDynamicStore, g_dynamic_store_key);
500	if (isDictionary(config) && isString(signing_id)) {
501		CFDictionaryRef executables = CFDictionaryGetValue(config, kVPNAppLayerMatchExecutables);
502		if (isDictionary(executables) && CFDictionaryContainsKey(executables, signing_id)) {
503			CFMutableDictionaryRef mutable_config = (CFMutableDictionaryRef)CFPropertyListCreateDeepCopy(kCFAllocatorDefault, config, kCFPropertyListMutableContainers);
504			CFDictionaryRef rules = CFDictionaryGetValue(mutable_config, kVPNAppLayerRules);
505			CFMutableDictionaryRef executable;
506
507			executables = CFDictionaryGetValue(mutable_config, kVPNAppLayerMatchExecutables);
508			executable = (CFMutableDictionaryRef)CFDictionaryGetValue(executables, signing_id);
509
510			if (isDictionary(rules) && isDictionary(executable)) {
511				app_layer_setup_executable_uuid(signing_id, rules, executable);
512
513				if (!CFEqual(config, mutable_config)) {
514					SCLog(TRUE, LOG_INFO, CFSTR("app_layer_install_app: posting new App VPN rules: %@"), mutable_config);
515					SCDynamicStoreSetValue(gDynamicStore, g_dynamic_store_key, mutable_config);
516				}
517			}
518
519			CFRelease(mutable_config);
520		}
521	}
522	if (config != NULL) {
523		CFRelease(config);
524	}
525#else
526#pragma unused(signing_id)
527#endif
528}
529
530void
531app_layer_remove_app(CFStringRef signing_id)
532{
533	SCPreferencesRef prefs = SCPreferencesCreate(kCFAllocatorDefault, CFSTR("app_layer_remove_app"), NULL);
534	if (prefs != NULL) {
535		Boolean changed = FALSE;
536		CFArrayRef services = VPNServiceCopyAll(prefs);
537		if (isA_CFArray(services) && CFArrayGetCount(services) > 0) {
538			CFIndex service_idx;
539			for (service_idx = 0; service_idx < CFArrayGetCount(services); service_idx++) {
540				SCNetworkServiceRef service = CFArrayGetValueAtIndex(services, service_idx);
541				if (VPNServiceIsManagedAppVPN(service)) {
542					CFIndex rule_idx;
543					CFArrayRef rule_ids = VPNServiceCopyAppRuleIDs(service);
544					if (isArray(rule_ids)) {
545						for (rule_idx = 0; rule_idx < CFArrayGetCount(rule_ids); rule_idx++) {
546							CFStringRef rule_id = CFArrayGetValueAtIndex(rule_ids, rule_idx);
547							CFDictionaryRef rule_settings = VPNServiceCopyAppRule(service, rule_id);
548							if (isDictionary(rule_settings)) {
549								CFArrayRef execs = CFDictionaryGetValue(rule_settings, kSCValNetVPNAppRuleExecutableMatch);
550								if (isArray(execs)) {
551									CFMutableArrayRef new_execs = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
552									CFIndex exec_idx;
553									for (exec_idx = 0; exec_idx < CFArrayGetCount(execs); exec_idx++) {
554										CFDictionaryRef exec = CFArrayGetValueAtIndex(execs, exec_idx);
555										CFStringRef exec_signing_id =
556											CFDictionaryGetValue(exec, kSCValNetVPNAppRuleExecutableSigningIdentifier);
557										if (!CFEqual(exec_signing_id, signing_id)) {
558											CFArrayAppendValue(new_execs, exec);
559										}
560									}
561									if (CFArrayGetCount(execs) != CFArrayGetCount(new_execs)) {
562										if (CFArrayGetCount(new_execs) == 0) {
563											if (VPNServiceRemoveAppRule(service, rule_id)) {
564												changed = TRUE;
565											} else {
566												SCLog(TRUE, LOG_WARNING, CFSTR("app_layer_remove_app failed to remove rule %@ from service %@: %s"), rule_id, SCNetworkServiceGetServiceID(service), SCErrorString(SCError()));
567											}
568										} else {
569											CFMutableDictionaryRef new_rule =
570												CFDictionaryCreateMutableCopy(kCFAllocatorDefault, CFDictionaryGetCount(rule_settings), rule_settings);
571											CFDictionarySetValue(new_rule, kSCValNetVPNAppRuleExecutableMatch, new_execs);
572											if (VPNServiceSetAppRule(service, rule_id, new_rule)) {
573												changed = TRUE;
574											} else {
575												SCLog(TRUE, LOG_WARNING, CFSTR("app_layer_remove_app failed to set rule %@ from service %@: %s"), rule_id, SCNetworkServiceGetServiceID(service), SCErrorString(SCError()));
576											}
577											CFRelease(new_rule);
578										}
579									}
580									CFRelease(new_execs);
581								}
582							}
583							if (rule_settings != NULL) {
584								CFRelease(rule_settings);
585							}
586						}
587					}
588					if (rule_ids != NULL) {
589						CFRelease(rule_ids);
590					}
591				}
592			}
593		}
594
595		if (changed) {
596			if (!SCPreferencesCommitChanges(prefs)) {
597				SCLog(TRUE, LOG_WARNING, CFSTR("app_layer_remove_app failed to commit changes while removing app %@: %s"), signing_id, SCErrorString(SCError()));
598			}
599			if (!SCPreferencesApplyChanges(prefs)) {
600				SCLog(TRUE, LOG_WARNING, CFSTR("app_layer_remove_app failed to apply changes while removing app %@: %s"), signing_id, SCErrorString(SCError()));
601			}
602		}
603
604		if (services != NULL) {
605			CFRelease(services);
606		}
607		CFRelease(prefs);
608	}
609}
610
611static void
612app_layer_set_kernel_rules(CFDictionaryRef rules, CFMutableDictionaryRef executables)
613{
614#if ! TARGET_OS_EMBEDDED
615#pragma unused(rules)
616#endif /* ! TARGET_OS_EMBEDDED */
617	CFIndex exec_count = (executables != NULL ? CFDictionaryGetCount(executables) : 0);
618	CFMutableArrayRef signing_ids =
619			CFArrayCreateMutable(kCFAllocatorDefault, exec_count + 1, &kCFTypeArrayCallBacks);
620
621	VPNAppLayerUUIDPolicyClear();
622
623	if (exec_count > 0) {
624		int i;
625		CFStringRef *keys = CFAllocatorAllocate(kCFAllocatorDefault, exec_count * sizeof(*keys), 0);
626
627		CFDictionaryGetKeysAndValues(executables, (const void **)keys, NULL);
628
629		for (i = 0; i < exec_count; i++) {
630#if TARGET_OS_EMBEDDED
631			CFMutableDictionaryRef executable = (CFMutableDictionaryRef)CFDictionaryGetValue(executables, keys[i]);
632			app_layer_setup_executable_uuid(keys[i], rules, executable);
633#endif /* TARGET_OS_EMBEDDED */
634			CFArrayAppendValue(signing_ids, keys[i]);
635		}
636
637		if (CFArrayGetCount(signing_ids) > 0) {
638			CFArrayAppendValue(signing_ids, CFSTR(FLOW_DIVERT_DNS_SERVICE_SIGNING_ID));
639		}
640
641		CFAllocatorDeallocate(kCFAllocatorDefault, keys);
642	}
643
644	flow_divert_set_signing_ids(signing_ids);
645	CFRelease(signing_ids);
646}
647
648void
649app_layer_handle_network_detection_change(CFStringRef service_id)
650{
651#if TARGET_OS_EMBEDDED
652	CFDictionaryRef config = SCDynamicStoreCopyValue(gDynamicStore, g_dynamic_store_key);
653	if (isDictionary(config)) {
654		CFMutableDictionaryRef mutable_config = (CFMutableDictionaryRef)CFPropertyListCreateDeepCopy(kCFAllocatorDefault, config, kCFPropertyListMutableContainers);
655		CFDictionaryRef rules = CFDictionaryGetValue(mutable_config, kVPNAppLayerRules);
656		CFMutableDictionaryRef executables = (CFMutableDictionaryRef)CFDictionaryGetValue(mutable_config, kVPNAppLayerMatchExecutables);
657
658		if (isDictionary(rules) && isDictionary(executables)) {
659			CFStringRef *signing_ids;
660			CFMutableDictionaryRef *executables_list;
661			CFIndex exec_count = CFDictionaryGetCount(executables);
662			CFIndex idx;
663
664			signing_ids = (CFStringRef *)CFAllocatorAllocate(kCFAllocatorDefault, exec_count * sizeof(*signing_ids), 0);
665			executables_list = (CFMutableDictionaryRef *)CFAllocatorAllocate(kCFAllocatorDefault, exec_count * sizeof(*executables_list), 0);
666
667			CFDictionaryGetKeysAndValues(executables, (const void **)signing_ids, (const void **)executables_list);
668
669			for (idx = 0; idx < exec_count; idx++) {
670				CFStringRef rule_id = CFDictionaryGetValue(executables_list[idx], kSCValNetVPNAppRuleIdentifier);
671				if (isString(rule_id) && app_layer_app_rule_has_service(rules, rule_id, service_id)) {
672					app_layer_setup_executable_uuid(signing_ids[idx], rules, executables_list[idx]);
673				}
674			}
675
676			CFAllocatorDeallocate(kCFAllocatorDefault, signing_ids);
677			CFAllocatorDeallocate(kCFAllocatorDefault, executables_list);
678
679			if (!CFEqual(config, mutable_config)) {
680				SCLog(TRUE, LOG_INFO, CFSTR("app_layer_handle_network_detection_change: posting new App VPN rules: %@"), mutable_config);
681				SCDynamicStoreSetValue(gDynamicStore, g_dynamic_store_key, mutable_config);
682			}
683		}
684
685		CFRelease(mutable_config);
686	}
687
688	if (config != NULL) {
689		CFRelease(config);
690	}
691#else
692#pragma unused(service_id)
693#endif /* TARGET_OS_EMBEDDED */
694}
695
696void
697app_layer_prefs_changed(SCPreferencesRef prefs, SCPreferencesNotification notification_type, void *info)
698{
699#pragma unused(info)
700	if (notification_type & kSCPreferencesNotificationApply) {
701		SCNetworkSetRef current_set = SCNetworkSetCopyCurrent(prefs);
702		CFMutableDictionaryRef new_config = NULL;
703		CFArrayRef service_order = NULL;
704		CFArrayRef services = VPNServiceCopyAll(prefs);
705
706		if (current_set != NULL) {
707			service_order = SCNetworkSetGetServiceOrder(current_set);
708		}
709
710		if (services != NULL) {
711			CFMutableDictionaryRef config = CFDictionaryCreateMutable(kCFAllocatorDefault,
712			                                                          0,
713			                                                          &kCFTypeDictionaryKeyCallBacks,
714			                                                          &kCFTypeDictionaryValueCallBacks);
715			CFIndex services_count = CFArrayGetCount(services);
716			CFIndex idx;
717			for (idx = 0; idx < services_count; idx++) {
718				SCNetworkServiceRef service = (SCNetworkServiceRef)CFArrayGetValueAtIndex(services, idx);
719				app_layer_add_service(prefs, config, service, service_order);
720			}
721
722			if (CFDictionaryGetCount(config) > 0) {
723				new_config = config;
724			} else {
725				CFRelease(config);
726			}
727		}
728
729		if (new_config != NULL) {
730			CFDictionaryRef curr_config = (CFDictionaryRef)SCDynamicStoreCopyValue(gDynamicStore, g_dynamic_store_key);
731			if (!isDictionary(curr_config) || !CFEqual(new_config, curr_config)) {
732				CFDictionaryRef rules = CFDictionaryGetValue(new_config, kVPNAppLayerRules);
733				CFMutableDictionaryRef executables = (CFMutableDictionaryRef)CFDictionaryGetValue(new_config, kVPNAppLayerMatchExecutables);
734
735				app_layer_set_kernel_rules(rules, executables);
736
737				SCLog(TRUE, LOG_INFO, CFSTR("app_layer_prefs_changed: posting new App VPN rules: %@"), new_config);
738				SCDynamicStoreSetValue(gDynamicStore, g_dynamic_store_key, new_config);
739				app_layer_post_config_changed_notification(CFDictionaryGetCount(new_config));
740			}
741			CFRelease(new_config);
742			if (curr_config != NULL) {
743				CFRelease(curr_config);
744			}
745		} else {
746			flow_divert_set_signing_ids(NULL);
747			SCDynamicStoreRemoveValue(gDynamicStore, g_dynamic_store_key);
748			app_layer_post_config_changed_notification(0);
749		}
750
751		if (current_set != NULL) {
752			CFRelease(current_set);
753		}
754		if (services != NULL) {
755			CFRelease(services);
756		}
757
758		SCPreferencesSynchronize(prefs);
759	}
760}
761
762void
763app_layer_init(CFRunLoopRef rl, CFStringRef rl_mode)
764{
765	static dispatch_once_t app_layer_initialized;
766
767	dispatch_once(&app_layer_initialized, ^{
768		if (ne_session_use_as_system_vpn()) {
769			return;
770		}
771
772		SCPreferencesRef prefs = SCPreferencesCreate(kCFAllocatorDefault, CFSTR("app_layer_rules"), NULL);
773		SCPreferencesContext prefs_ctx = { 0, NULL, NULL, NULL };
774
775		if (!SCPreferencesSetCallback(prefs, app_layer_prefs_changed, &prefs_ctx)) {
776			SCLog(TRUE, LOG_ERR, CFSTR("app_layer_init: failed to set the prefs callback: %s"), SCErrorString(SCError()));
777			return;
778		}
779
780		if (!SCPreferencesScheduleWithRunLoop(prefs, rl, rl_mode)) {
781			SCLog(TRUE, LOG_ERR, CFSTR("app_layer_init: failed to schedule the prefs with the run loop: %s"), SCErrorString(SCError()));
782			return;
783		}
784
785		g_dynamic_store_key = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL, kSCDynamicStoreDomainState, kSCEntNetAppLayer);
786
787		app_layer_prefs_changed(prefs, kSCPreferencesNotificationApply, NULL);
788
789		CFRelease(prefs);
790	});
791}
792