1/* 2 * Copyright (c) 2004-2007, 2009, 2010, 2012, 2013 Apple Inc. All rights reserved. 3 * 4 * @APPLE_LICENSE_HEADER_START@ 5 * 6 * This file contains Original Code and/or Modifications of Original Code 7 * as defined in and that are subject to the Apple Public Source License 8 * Version 2.0 (the 'License'). You may not use this file except in 9 * compliance with the License. Please obtain a copy of the License at 10 * http://www.opensource.apple.com/apsl/ and read it before using this 11 * file. 12 * 13 * The Original Code and all software distributed under the License are 14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 18 * Please see the License for the specific language governing rights and 19 * limitations under the License. 20 * 21 * @APPLE_LICENSE_HEADER_END@ 22 */ 23 24/* 25 * Modification History 26 * 27 * May 27, 2004 Allan Nathanson <ajn@apple.com> 28 * - initial revision 29 */ 30 31 32#include <CoreFoundation/CoreFoundation.h> 33#include <SystemConfiguration/SystemConfiguration.h> 34#include <SystemConfiguration/SCValidation.h> 35#include <SystemConfiguration/SCPrivate.h> 36 37#include <sys/ioctl.h> 38#include <net/if.h> 39 40 41__private_extern__ CFDictionaryRef 42__getPrefsConfiguration(SCPreferencesRef prefs, CFStringRef path) 43{ 44 CFDictionaryRef config; 45 CFIndex n; 46 47 config = SCPreferencesPathGetValue(prefs, path); 48 49 n = isA_CFDictionary(config) ? CFDictionaryGetCount(config) : 0; 50 switch (n) { 51 case 0 : 52 // ignore empty configuration entities 53 config = NULL; 54 break; 55 case 1 : 56 if (CFDictionaryContainsKey(config, kSCResvInactive)) { 57 // ignore [effectively] empty configuration entities 58 config = NULL; 59 } 60 break; 61 default : 62 break; 63 } 64 65 return config; 66} 67 68 69__private_extern__ Boolean 70__setPrefsConfiguration(SCPreferencesRef prefs, 71 CFStringRef path, 72 CFDictionaryRef config, 73 Boolean keepInactive) 74{ 75 CFDictionaryRef curConfig; 76 CFMutableDictionaryRef newConfig = NULL; 77 Boolean ok; 78 79 if ((config != NULL) && !isA_CFDictionary(config)) { 80 _SCErrorSet(kSCStatusInvalidArgument); 81 return FALSE; 82 } 83 84 curConfig = SCPreferencesPathGetValue(prefs, path); 85 86 if (config != NULL) { 87 newConfig = CFDictionaryCreateMutableCopy(NULL, 0, config); 88 } 89 90 if (keepInactive) { 91 if (config == NULL) { 92 newConfig = CFDictionaryCreateMutable(NULL, 93 0, 94 &kCFTypeDictionaryKeyCallBacks, 95 &kCFTypeDictionaryValueCallBacks); 96 } 97 98 if (isA_CFDictionary(curConfig) && CFDictionaryContainsKey(curConfig, kSCResvInactive)) { 99 // if currently disabled 100 CFDictionarySetValue(newConfig, kSCResvInactive, kCFBooleanTrue); 101 } else { 102 // if currently enabled 103 CFDictionaryRemoveValue(newConfig, kSCResvInactive); 104 } 105 } 106 107 // set new configuration 108 if (_SC_CFEqual(curConfig, newConfig)) { 109 // if no change 110 if (newConfig != NULL) CFRelease(newConfig); 111 ok = TRUE; 112 } else if (newConfig != NULL) { 113 // if new configuration (or we are preserving a disabled state) 114 ok = SCPreferencesPathSetValue(prefs, path, newConfig); 115 CFRelease(newConfig); 116 } else { 117 ok = SCPreferencesPathRemoveValue(prefs, path); 118 if (!ok && (SCError() == kSCStatusNoKey)) { 119 ok = TRUE; 120 } 121 } 122 123 return ok; 124} 125 126 127__private_extern__ Boolean 128__getPrefsEnabled(SCPreferencesRef prefs, CFStringRef path) 129{ 130 CFDictionaryRef config; 131 132 config = SCPreferencesPathGetValue(prefs, path); 133 if (isA_CFDictionary(config) && CFDictionaryContainsKey(config, kSCResvInactive)) { 134 return FALSE; 135 } 136 137 return TRUE; 138} 139 140 141__private_extern__ Boolean 142__setPrefsEnabled(SCPreferencesRef prefs, 143 CFStringRef path, 144 Boolean enabled) 145{ 146 CFDictionaryRef curConfig; 147 CFMutableDictionaryRef newConfig = NULL; 148 Boolean ok = FALSE; 149 150 // preserve current configuration 151 curConfig = SCPreferencesPathGetValue(prefs, path); 152 if (curConfig != NULL) { 153 if (!isA_CFDictionary(curConfig)) { 154 _SCErrorSet(kSCStatusFailed); 155 return FALSE; 156 } 157 newConfig = CFDictionaryCreateMutableCopy(NULL, 0, curConfig); 158 159 if (enabled) { 160 // enable 161 CFDictionaryRemoveValue(newConfig, kSCResvInactive); 162 } else { 163 // disable 164 CFDictionarySetValue(newConfig, kSCResvInactive, kCFBooleanTrue); 165 } 166 } else { 167 if (!enabled) { 168 // disable 169 newConfig = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); 170 CFDictionarySetValue(newConfig, kSCResvInactive, kCFBooleanTrue); 171 } 172 } 173 174 // set new configuration 175 if (_SC_CFEqual(curConfig, newConfig)) { 176 // if no change 177 if (newConfig != NULL) CFRelease(newConfig); 178 ok = TRUE; 179 } else if (newConfig != NULL) { 180 // if updated configuration (or we are establishing as disabled) 181 ok = SCPreferencesPathSetValue(prefs, path, newConfig); 182 CFRelease(newConfig); 183 } else { 184 ok = SCPreferencesPathRemoveValue(prefs, path); 185 if (!ok && (SCError() == kSCStatusNoKey)) { 186 ok = TRUE; 187 } 188 } 189 190 return ok; 191} 192 193 194static CFDictionaryRef 195__copyTemplates() 196{ 197 CFBundleRef bundle; 198 CFErrorRef error = NULL; 199 Boolean ok; 200 CFDictionaryRef templates; 201 CFURLRef url; 202 CFDataRef xmlTemplates = NULL; 203 204 bundle = _SC_CFBundleGet(); 205 if (bundle == NULL) { 206 return NULL; 207 } 208 209 url = CFBundleCopyResourceURL(bundle, CFSTR("NetworkConfiguration"), CFSTR("plist"), NULL); 210 if (url == NULL) { 211 return NULL; 212 } 213 214#pragma GCC diagnostic push 215#pragma GCC diagnostic ignored "-Wdeprecated" 216 ok = CFURLCreateDataAndPropertiesFromResource(NULL, url, &xmlTemplates, NULL, NULL, NULL); 217#pragma GCC diagnostic pop 218 CFRelease(url); 219 if (!ok || (xmlTemplates == NULL)) { 220 return NULL; 221 } 222 223 // convert the XML data into a property list 224 templates = CFPropertyListCreateWithData(NULL, xmlTemplates, kCFPropertyListImmutable, NULL, &error); 225 CFRelease(xmlTemplates); 226 if (templates == NULL) { 227 if (error != NULL) { 228 SCLog(TRUE, LOG_DEBUG, CFSTR("could not load SCNetworkConfiguration templates: %@"), error); 229 CFRelease(error); 230 } 231 return NULL; 232 } 233 234 if (!isA_CFDictionary(templates)) { 235 CFRelease(templates); 236 return NULL; 237 } 238 239 return templates; 240} 241 242 243__private_extern__ CFDictionaryRef 244__copyInterfaceTemplate(CFStringRef interfaceType, 245 CFStringRef childInterfaceType) 246{ 247 CFDictionaryRef interface = NULL; 248 CFDictionaryRef interfaces; 249 CFDictionaryRef templates; 250 251 templates = __copyTemplates(); 252 if (templates == NULL) { 253 return NULL; 254 } 255 256 interfaces = CFDictionaryGetValue(templates, CFSTR("Interface")); 257 if (!isA_CFDictionary(interfaces)) { 258 CFRelease(templates); 259 return NULL; 260 } 261 262 if (childInterfaceType == NULL) { 263 interface = CFDictionaryGetValue(interfaces, interfaceType); 264 } else { 265 CFStringRef expandedType; 266 267 if (CFStringFind(childInterfaceType, CFSTR("."), 0).location != kCFNotFound) { 268 // if "vendor" type 269 childInterfaceType = CFSTR("*"); 270 } 271 272 expandedType = CFStringCreateWithFormat(NULL, 273 NULL, 274 CFSTR("%@-%@"), 275 interfaceType, 276 childInterfaceType); 277 interface = CFDictionaryGetValue(interfaces, expandedType); 278 CFRelease(expandedType); 279 } 280 281 if (isA_CFDictionary(interface) && (CFDictionaryGetCount(interface) > 0)) { 282 CFRetain(interface); 283 } else { 284 interface = NULL; 285 } 286 287 CFRelease(templates); 288 289 return interface; 290} 291 292 293__private_extern__ CFDictionaryRef 294__copyProtocolTemplate(CFStringRef interfaceType, 295 CFStringRef childInterfaceType, 296 CFStringRef protocolType) 297{ 298 CFDictionaryRef interface = NULL; 299 CFDictionaryRef protocol = NULL; 300 CFDictionaryRef protocols; 301 CFDictionaryRef templates; 302 303 templates = __copyTemplates(); 304 if (templates == NULL) { 305 return NULL; 306 } 307 308 protocols = CFDictionaryGetValue(templates, CFSTR("Protocol")); 309 if (!isA_CFDictionary(protocols)) { 310 CFRelease(templates); 311 return NULL; 312 } 313 314 if (childInterfaceType == NULL) { 315 interface = CFDictionaryGetValue(protocols, interfaceType); 316 } else { 317 CFStringRef expandedType; 318 319 if (CFStringFind(childInterfaceType, CFSTR("."), 0).location != kCFNotFound) { 320 // if "vendor" type 321 childInterfaceType = CFSTR("*"); 322 } 323 324 expandedType = CFStringCreateWithFormat(NULL, 325 NULL, 326 CFSTR("%@-%@"), 327 interfaceType, 328 childInterfaceType); 329 interface = CFDictionaryGetValue(protocols, expandedType); 330 CFRelease(expandedType); 331 } 332 333 if (isA_CFDictionary(interface)) { 334 protocol = CFDictionaryGetValue(interface, protocolType); 335 if (isA_CFDictionary(protocol)) { 336 CFRetain(protocol); 337 } else { 338 protocol = NULL; 339 } 340 } 341 342 CFRelease(templates); 343 344 return protocol; 345} 346 347 348__private_extern__ Boolean 349__createInterface(int s, CFStringRef interface) 350{ 351 struct ifreq ifr; 352 353 bzero(&ifr, sizeof(ifr)); 354 (void) _SC_cfstring_to_cstring(interface, 355 ifr.ifr_name, 356 sizeof(ifr.ifr_name), 357 kCFStringEncodingASCII); 358 359 if (ioctl(s, SIOCIFCREATE, &ifr) == -1) { 360 SCLog(TRUE, 361 LOG_ERR, 362 CFSTR("could not create interface \"%@\": %s"), 363 interface, 364 strerror(errno)); 365 return FALSE; 366 } 367 368 return TRUE; 369} 370 371 372__private_extern__ Boolean 373__destroyInterface(int s, CFStringRef interface) 374{ 375 struct ifreq ifr; 376 377 bzero(&ifr, sizeof(ifr)); 378 (void) _SC_cfstring_to_cstring(interface, 379 ifr.ifr_name, 380 sizeof(ifr.ifr_name), 381 kCFStringEncodingASCII); 382 383 if (ioctl(s, SIOCIFDESTROY, &ifr) == -1) { 384 SCLog(TRUE, 385 LOG_ERR, 386 CFSTR("could not destroy interface \"%@\": %s"), 387 interface, 388 strerror(errno)); 389 return FALSE; 390 } 391 392 return TRUE; 393} 394 395 396/* 397 * For rdar://problem/4685223 398 * 399 * To keep MoreSCF happy we need to ensure that the first "Set" and 400 * "NetworkService" have a [less than] unique identifier that can 401 * be parsed as a numeric string. 402 * 403 * Note: this backwards compatibility code must be enabled using the 404 * following command: 405 * 406 * sudo defaults write \ 407 * /Library/Preferences/SystemConfiguration/preferences \ 408 * MoreSCF \ 409 * -bool true 410 */ 411__private_extern__ 412CFStringRef 413__SCPreferencesPathCreateUniqueChild_WithMoreSCFCompatibility(SCPreferencesRef prefs, CFStringRef prefix) 414{ 415 static int hack = -1; 416 CFStringRef path = NULL; 417 418 if (hack < 0) { 419 CFBooleanRef enable; 420 421 enable = SCPreferencesGetValue(prefs, CFSTR("MoreSCF")); 422 hack = (isA_CFBoolean(enable) && CFBooleanGetValue(enable)) ? 1 : 0; 423 } 424 425 if (hack > 0) { 426 CFDictionaryRef dict; 427 Boolean ok; 428 429 path = CFStringCreateWithFormat(NULL, NULL, CFSTR("%@/%@"), prefix, CFSTR("0")); 430 dict = SCPreferencesPathGetValue(prefs, path); 431 if (dict != NULL) { 432 // if path "0" exists 433 CFRelease(path); 434 return NULL; 435 } 436 437 // unique child with path "0" does not exist, create 438 dict = CFDictionaryCreate(NULL, 439 NULL, NULL, 0, 440 &kCFTypeDictionaryKeyCallBacks, 441 &kCFTypeDictionaryValueCallBacks); 442 ok = SCPreferencesPathSetValue(prefs, path, dict); 443 CFRelease(dict); 444 if (!ok) { 445 // if create failed 446 CFRelease(path); 447 return NULL; 448 } 449 } 450 451 return path; 452} 453 454 455static CFDataRef 456__copy_legacy_password(CFTypeRef password) 457{ 458 if (password == NULL) { 459 return NULL; 460 } 461 462 if (isA_CFData(password)) { 463 CFIndex n; 464 465 n = CFDataGetLength(password); 466 if ((n % sizeof(UniChar)) == 0) { 467 CFStringEncoding encoding; 468 CFStringRef str; 469 470#if __BIG_ENDIAN__ 471 encoding = (*(CFDataGetBytePtr(password) + 1) == 0x00) ? kCFStringEncodingUTF16LE : kCFStringEncodingUTF16BE; 472#else // __LITTLE_ENDIAN__ 473 encoding = (*(CFDataGetBytePtr(password) ) == 0x00) ? kCFStringEncodingUTF16BE : kCFStringEncodingUTF16LE; 474#endif 475 str = CFStringCreateWithBytes(NULL, 476 (const UInt8 *)CFDataGetBytePtr(password), 477 n, 478 encoding, 479 FALSE); 480 password = CFStringCreateExternalRepresentation(NULL, 481 str, 482 kCFStringEncodingUTF8, 483 0); 484 CFRelease(str); 485 } else { 486 password = NULL; 487 } 488 } else if (isA_CFString(password) && (CFStringGetLength(password) > 0)) { 489 // convert password to CFData 490 password = CFStringCreateExternalRepresentation(NULL, 491 password, 492 kCFStringEncodingUTF8, 493 0); 494 } else { 495 password = NULL; 496 } 497 498 return password; 499} 500 501 502__private_extern__ 503Boolean 504__extract_password(SCPreferencesRef prefs, 505 CFDictionaryRef config, 506 CFStringRef passwordKey, 507 CFStringRef encryptionKey, 508 CFStringRef encryptionKeyChainValue, 509 CFStringRef unique_id, 510 CFDataRef *password) 511{ 512 CFStringRef encryption = NULL; 513 Boolean exists = FALSE; 514 515 // check for keychain password 516 if (config != NULL) { 517 encryption = CFDictionaryGetValue(config, encryptionKey); 518 } 519 if ((encryption == NULL) || 520 (isA_CFString(encryption) && 521 CFEqual(encryption, encryptionKeyChainValue))) { 522 // check password 523 if (password != NULL) { 524 if (prefs != NULL) { 525 *password = _SCPreferencesSystemKeychainPasswordItemCopy(prefs, unique_id); 526 } else { 527 *password = _SCSecKeychainPasswordItemCopy(NULL, unique_id); 528 } 529 exists = (*password != NULL); 530 } else { 531 if (prefs != NULL) { 532 exists = _SCPreferencesSystemKeychainPasswordItemExists(prefs, unique_id); 533 } else { 534 exists = _SCSecKeychainPasswordItemExists(NULL, unique_id); 535 } 536 } 537 } 538 539 // as needed, check for in-line password 540 if (!exists && (encryption == NULL) && (config != NULL)) { 541 CFDataRef inline_password; 542 543 inline_password = CFDictionaryGetValue(config, passwordKey); 544 inline_password = __copy_legacy_password(inline_password); 545 if (inline_password != NULL) { 546 exists = TRUE; 547 548 if (password != NULL) { 549 *password = inline_password; 550 } else { 551 CFRelease(inline_password); 552 } 553 } 554 } 555 556 return exists; 557} 558 559 560__private_extern__ 561Boolean 562__remove_password(SCPreferencesRef prefs, 563 CFDictionaryRef config, 564 CFStringRef passwordKey, 565 CFStringRef encryptionKey, 566 CFStringRef encryptionKeyChainValue, 567 CFStringRef unique_id, 568 CFDictionaryRef *newConfig) 569{ 570 CFStringRef encryption = NULL; 571 Boolean ok = FALSE; 572 573 // check for keychain password 574 if (config != NULL) { 575 encryption = CFDictionaryGetValue(config, encryptionKey); 576 } 577 if ((encryption == NULL) || 578 (isA_CFString(encryption) && 579 CFEqual(encryption, encryptionKeyChainValue))) { 580 // remove keychain password 581 if (prefs != NULL) { 582 ok = _SCPreferencesSystemKeychainPasswordItemRemove(prefs, unique_id); 583 } else { 584 ok = _SCSecKeychainPasswordItemRemove(NULL, unique_id); 585 } 586 } 587 588 // as needed, check if we have an in-line password that we can remove 589 if (!ok && (encryption == NULL) && (config != NULL)) { 590 CFDataRef inline_password; 591 592 inline_password = CFDictionaryGetValue(config, passwordKey); 593 inline_password = __copy_legacy_password(inline_password); 594 if (inline_password != NULL) { 595 CFRelease(inline_password); 596 ok = TRUE; 597 } 598 } 599 600 if (newConfig != NULL) { 601 if (ok && (config != NULL)) { 602 CFMutableDictionaryRef temp; 603 604 temp = CFDictionaryCreateMutableCopy(NULL, 0, config); 605 CFDictionaryRemoveValue(temp, passwordKey); 606 CFDictionaryRemoveValue(temp, encryptionKey); 607 *newConfig = (CFDictionaryRef)temp; 608 } else { 609 *newConfig = NULL; 610 } 611 } 612 613 return ok; 614} 615 616 617__private_extern__ Boolean 618__rank_to_str(SCNetworkServicePrimaryRank rank, CFStringRef *rankStr) 619{ 620 switch (rank) { 621 case kSCNetworkServicePrimaryRankDefault : 622 *rankStr = NULL; 623 break; 624 case kSCNetworkServicePrimaryRankFirst : 625 *rankStr = kSCValNetServicePrimaryRankFirst; 626 break; 627 case kSCNetworkServicePrimaryRankLast : 628 *rankStr = kSCValNetServicePrimaryRankLast; 629 break; 630 case kSCNetworkServicePrimaryRankNever : 631 *rankStr = kSCValNetServicePrimaryRankNever; 632 break; 633 case kSCNetworkServicePrimaryRankScoped : 634 *rankStr = kSCValNetServicePrimaryRankScoped; 635 break; 636 default : 637 return FALSE; 638 } 639 640 return TRUE; 641} 642 643 644__private_extern__ Boolean 645__str_to_rank(CFStringRef rankStr, SCNetworkServicePrimaryRank *rank) 646{ 647 if (isA_CFString(rankStr)) { 648 if (CFEqual(rankStr, kSCValNetServicePrimaryRankFirst)) { 649 *rank = kSCNetworkServicePrimaryRankFirst; 650 } else if (CFEqual(rankStr, kSCValNetServicePrimaryRankLast)) { 651 *rank = kSCNetworkServicePrimaryRankLast; 652 } else if (CFEqual(rankStr, kSCValNetServicePrimaryRankNever)) { 653 *rank = kSCNetworkServicePrimaryRankNever; 654 } else if (CFEqual(rankStr, kSCValNetServicePrimaryRankScoped)) { 655 *rank = kSCNetworkServicePrimaryRankScoped; 656 } else { 657 return FALSE; 658 } 659 } else if (rankStr == NULL) { 660 *rank = kSCNetworkServicePrimaryRankDefault; 661 } else { 662 return FALSE; 663 } 664 665 return TRUE; 666} 667