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, ¬ify_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