]> git.saurik.com Git - apple/configd.git/blob - SystemConfiguration.fproj/SCNetworkConfigurationInternal.c
configd-802.40.13.tar.gz
[apple/configd.git] / SystemConfiguration.fproj / SCNetworkConfigurationInternal.c
1 /*
2 * Copyright (c) 2004-2007, 2009, 2010-2013, 2015 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 #if !TARGET_OS_EMBEDDED
194 #define SYSTEMCONFIGURATION_RESOURCES_PATH SYSTEMCONFIGURATION_FRAMEWORK_PATH "/Resources"
195 #else
196 #define SYSTEMCONFIGURATION_RESOURCES_PATH SYSTEMCONFIGURATION_FRAMEWORK_PATH
197 #endif // !TARGET_OS_EMBEDDED
198
199 #define NETWORKCONFIGURATION_RESOURCE_FILE "NetworkConfiguration.plist"
200
201 static CFDictionaryRef
202 __copyTemplates()
203 {
204 CFBundleRef bundle;
205 CFErrorRef error = NULL;
206 SInt32 errorCode;
207 Boolean ok;
208 CFDictionaryRef templates;
209 CFURLRef url;
210 CFDataRef xmlTemplates = NULL;
211
212 bundle = _SC_CFBundleGet();
213 if (bundle == NULL) {
214 return NULL;
215 }
216
217 url = CFBundleCopyResourceURL(bundle, CFSTR("NetworkConfiguration"), CFSTR("plist"), NULL);
218 if (url == NULL) {
219 SC_log(LOG_ERR, "failed to GET resource URL to \"%s\". Trying harder...", NETWORKCONFIGURATION_RESOURCE_FILE);
220 url = CFURLCreateWithFileSystemPath(NULL,
221 CFSTR(SYSTEMCONFIGURATION_RESOURCES_PATH
222 "/"
223 NETWORKCONFIGURATION_RESOURCE_FILE),
224 kCFURLPOSIXPathStyle,
225 TRUE);
226
227 if (url == NULL) {
228 SC_log(LOG_ERR, "failed to CREATE resource URL to \"%s\"", SYSTEMCONFIGURATION_RESOURCES_PATH
229 "/"
230 NETWORKCONFIGURATION_RESOURCE_FILE);
231 return NULL;
232 }
233 }
234
235 #pragma GCC diagnostic push
236 #pragma GCC diagnostic ignored "-Wdeprecated"
237 ok = CFURLCreateDataAndPropertiesFromResource(NULL, url, &xmlTemplates, NULL, NULL, &errorCode);
238 #pragma GCC diagnostic pop
239 CFRelease(url);
240 if (!ok || (xmlTemplates == NULL)) {
241 SC_log(LOG_NOTICE, "%s: failed to create data properties from resource (error=%ld)", __FUNCTION__ , (long)errorCode);
242 return NULL;
243 }
244
245 // convert the XML data into a property list
246 templates = CFPropertyListCreateWithData(NULL, xmlTemplates, kCFPropertyListImmutable, NULL, &error);
247 CFRelease(xmlTemplates);
248 if (templates == NULL) {
249 if (error != NULL) {
250 SC_log(LOG_NOTICE, "could not load SCNetworkConfiguration templates: %@", error);
251 CFRelease(error);
252 }
253 return NULL;
254 }
255
256 if (!isA_CFDictionary(templates)) {
257 CFRelease(templates);
258 return NULL;
259 }
260
261 return templates;
262 }
263
264
265 __private_extern__ CFDictionaryRef
266 __copyInterfaceTemplate(CFStringRef interfaceType,
267 CFStringRef childInterfaceType)
268 {
269 CFDictionaryRef interface = NULL;
270 CFDictionaryRef interfaces;
271 CFDictionaryRef templates;
272
273 templates = __copyTemplates();
274 if (templates == NULL) {
275 return NULL;
276 }
277
278 interfaces = CFDictionaryGetValue(templates, CFSTR("Interface"));
279 if (!isA_CFDictionary(interfaces)) {
280 CFRelease(templates);
281 return NULL;
282 }
283
284 if (childInterfaceType == NULL) {
285 interface = CFDictionaryGetValue(interfaces, interfaceType);
286 } else {
287 CFStringRef expandedType;
288
289 if (CFStringFind(childInterfaceType, CFSTR("."), 0).location != kCFNotFound) {
290 // if "vendor" type
291 childInterfaceType = CFSTR("*");
292 }
293
294 expandedType = CFStringCreateWithFormat(NULL,
295 NULL,
296 CFSTR("%@-%@"),
297 interfaceType,
298 childInterfaceType);
299 interface = CFDictionaryGetValue(interfaces, expandedType);
300 CFRelease(expandedType);
301 }
302
303 if (isA_CFDictionary(interface) && (CFDictionaryGetCount(interface) > 0)) {
304 CFRetain(interface);
305 } else {
306 interface = NULL;
307 }
308
309 CFRelease(templates);
310
311 return interface;
312 }
313
314
315 __private_extern__ CFDictionaryRef
316 __copyProtocolTemplate(CFStringRef interfaceType,
317 CFStringRef childInterfaceType,
318 CFStringRef protocolType)
319 {
320 CFDictionaryRef interface = NULL;
321 CFDictionaryRef protocol = NULL;
322 CFDictionaryRef protocols;
323 CFDictionaryRef templates;
324
325 templates = __copyTemplates();
326 if (templates == NULL) {
327 return NULL;
328 }
329
330 protocols = CFDictionaryGetValue(templates, CFSTR("Protocol"));
331 if (!isA_CFDictionary(protocols)) {
332 CFRelease(templates);
333 return NULL;
334 }
335
336 if (childInterfaceType == NULL) {
337 interface = CFDictionaryGetValue(protocols, interfaceType);
338 } else {
339 CFStringRef expandedType;
340
341 if (CFStringFind(childInterfaceType, CFSTR("."), 0).location != kCFNotFound) {
342 // if "vendor" type
343 childInterfaceType = CFSTR("*");
344 }
345
346 expandedType = CFStringCreateWithFormat(NULL,
347 NULL,
348 CFSTR("%@-%@"),
349 interfaceType,
350 childInterfaceType);
351 interface = CFDictionaryGetValue(protocols, expandedType);
352 CFRelease(expandedType);
353 }
354
355 if (isA_CFDictionary(interface)) {
356 protocol = CFDictionaryGetValue(interface, protocolType);
357 if (isA_CFDictionary(protocol)) {
358 CFRetain(protocol);
359 } else {
360 protocol = NULL;
361 }
362 }
363
364 CFRelease(templates);
365
366 return protocol;
367 }
368
369
370 __private_extern__ Boolean
371 __createInterface(int s, CFStringRef interface)
372 {
373 struct ifreq ifr;
374
375 bzero(&ifr, sizeof(ifr));
376 (void) _SC_cfstring_to_cstring(interface,
377 ifr.ifr_name,
378 sizeof(ifr.ifr_name),
379 kCFStringEncodingASCII);
380
381 if (ioctl(s, SIOCIFCREATE, &ifr) == -1) {
382 SC_log(LOG_NOTICE, "could not create interface \"%@\": %s",
383 interface,
384 strerror(errno));
385 return FALSE;
386 }
387
388 return TRUE;
389 }
390
391
392 __private_extern__ Boolean
393 __destroyInterface(int s, CFStringRef interface)
394 {
395 struct ifreq ifr;
396
397 bzero(&ifr, sizeof(ifr));
398 (void) _SC_cfstring_to_cstring(interface,
399 ifr.ifr_name,
400 sizeof(ifr.ifr_name),
401 kCFStringEncodingASCII);
402
403 if (ioctl(s, SIOCIFDESTROY, &ifr) == -1) {
404 SC_log(LOG_NOTICE, "could not destroy interface \"%@\": %s",
405 interface,
406 strerror(errno));
407 return FALSE;
408 }
409
410 return TRUE;
411 }
412
413
414 /*
415 * For rdar://problem/4685223
416 *
417 * To keep MoreSCF happy we need to ensure that the first "Set" and
418 * "NetworkService" have a [less than] unique identifier that can
419 * be parsed as a numeric string.
420 *
421 * Note: this backwards compatibility code must be enabled using the
422 * following command:
423 *
424 * sudo defaults write \
425 * /Library/Preferences/SystemConfiguration/preferences \
426 * MoreSCF \
427 * -bool true
428 */
429 __private_extern__
430 CFStringRef
431 __SCPreferencesPathCreateUniqueChild_WithMoreSCFCompatibility(SCPreferencesRef prefs, CFStringRef prefix)
432 {
433 static int hack = -1;
434 CFStringRef path = NULL;
435
436 if (hack < 0) {
437 CFBooleanRef enable;
438
439 enable = SCPreferencesGetValue(prefs, CFSTR("MoreSCF"));
440 hack = (isA_CFBoolean(enable) && CFBooleanGetValue(enable)) ? 1 : 0;
441 }
442
443 if (hack > 0) {
444 CFDictionaryRef dict;
445 Boolean ok;
446
447 path = CFStringCreateWithFormat(NULL, NULL, CFSTR("%@/%@"), prefix, CFSTR("0"));
448 dict = SCPreferencesPathGetValue(prefs, path);
449 if (dict != NULL) {
450 // if path "0" exists
451 CFRelease(path);
452 return NULL;
453 }
454
455 // unique child with path "0" does not exist, create
456 dict = CFDictionaryCreate(NULL,
457 NULL, NULL, 0,
458 &kCFTypeDictionaryKeyCallBacks,
459 &kCFTypeDictionaryValueCallBacks);
460 ok = SCPreferencesPathSetValue(prefs, path, dict);
461 CFRelease(dict);
462 if (!ok) {
463 // if create failed
464 CFRelease(path);
465 return NULL;
466 }
467 }
468
469 return path;
470 }
471
472
473 static CFDataRef
474 __copy_legacy_password(CFTypeRef password)
475 {
476 if (password == NULL) {
477 return NULL;
478 }
479
480 if (isA_CFData(password)) {
481 CFIndex n;
482
483 n = CFDataGetLength(password);
484 if ((n % sizeof(UniChar)) == 0) {
485 CFStringEncoding encoding;
486 CFStringRef str;
487
488 #if __BIG_ENDIAN__
489 encoding = (*(CFDataGetBytePtr(password) + 1) == 0x00) ? kCFStringEncodingUTF16LE : kCFStringEncodingUTF16BE;
490 #else // __LITTLE_ENDIAN__
491 encoding = (*(CFDataGetBytePtr(password) ) == 0x00) ? kCFStringEncodingUTF16BE : kCFStringEncodingUTF16LE;
492 #endif
493 str = CFStringCreateWithBytes(NULL,
494 (const UInt8 *)CFDataGetBytePtr(password),
495 n,
496 encoding,
497 FALSE);
498 password = CFStringCreateExternalRepresentation(NULL,
499 str,
500 kCFStringEncodingUTF8,
501 0);
502 CFRelease(str);
503 } else {
504 password = NULL;
505 }
506 } else if (isA_CFString(password) && (CFStringGetLength(password) > 0)) {
507 // convert password to CFData
508 password = CFStringCreateExternalRepresentation(NULL,
509 password,
510 kCFStringEncodingUTF8,
511 0);
512 } else {
513 password = NULL;
514 }
515
516 return password;
517 }
518
519
520 __private_extern__
521 Boolean
522 __extract_password(SCPreferencesRef prefs,
523 CFDictionaryRef config,
524 CFStringRef passwordKey,
525 CFStringRef encryptionKey,
526 CFStringRef encryptionKeyChainValue,
527 CFStringRef unique_id,
528 CFDataRef *password)
529 {
530 CFStringRef encryption = NULL;
531 Boolean exists = FALSE;
532
533 // check for keychain password
534 if (config != NULL) {
535 encryption = CFDictionaryGetValue(config, encryptionKey);
536 }
537 if ((encryption == NULL) ||
538 (isA_CFString(encryption) &&
539 CFEqual(encryption, encryptionKeyChainValue))) {
540 // check password
541 if (password != NULL) {
542 if (prefs != NULL) {
543 *password = _SCPreferencesSystemKeychainPasswordItemCopy(prefs, unique_id);
544 } else {
545 *password = _SCSecKeychainPasswordItemCopy(NULL, unique_id);
546 }
547 exists = (*password != NULL);
548 } else {
549 if (prefs != NULL) {
550 exists = _SCPreferencesSystemKeychainPasswordItemExists(prefs, unique_id);
551 } else {
552 exists = _SCSecKeychainPasswordItemExists(NULL, unique_id);
553 }
554 }
555 }
556
557 // as needed, check for in-line password
558 if (!exists && (encryption == NULL) && (config != NULL)) {
559 CFDataRef inline_password;
560
561 inline_password = CFDictionaryGetValue(config, passwordKey);
562 inline_password = __copy_legacy_password(inline_password);
563 if (inline_password != NULL) {
564 exists = TRUE;
565
566 if (password != NULL) {
567 *password = inline_password;
568 } else {
569 CFRelease(inline_password);
570 }
571 }
572 }
573
574 return exists;
575 }
576
577
578 __private_extern__
579 Boolean
580 __remove_password(SCPreferencesRef prefs,
581 CFDictionaryRef config,
582 CFStringRef passwordKey,
583 CFStringRef encryptionKey,
584 CFStringRef encryptionKeyChainValue,
585 CFStringRef unique_id,
586 CFDictionaryRef *newConfig)
587 {
588 CFStringRef encryption = NULL;
589 Boolean ok = FALSE;
590
591 // check for keychain password
592 if (config != NULL) {
593 encryption = CFDictionaryGetValue(config, encryptionKey);
594 }
595 if ((encryption == NULL) ||
596 (isA_CFString(encryption) &&
597 CFEqual(encryption, encryptionKeyChainValue))) {
598 // remove keychain password
599 if (prefs != NULL) {
600 ok = _SCPreferencesSystemKeychainPasswordItemRemove(prefs, unique_id);
601 } else {
602 ok = _SCSecKeychainPasswordItemRemove(NULL, unique_id);
603 }
604 }
605
606 // as needed, check if we have an in-line password that we can remove
607 if (!ok && (encryption == NULL) && (config != NULL)) {
608 CFDataRef inline_password;
609
610 inline_password = CFDictionaryGetValue(config, passwordKey);
611 inline_password = __copy_legacy_password(inline_password);
612 if (inline_password != NULL) {
613 CFRelease(inline_password);
614 ok = TRUE;
615 }
616 }
617
618 if (newConfig != NULL) {
619 if (ok && (config != NULL)) {
620 CFMutableDictionaryRef temp;
621
622 temp = CFDictionaryCreateMutableCopy(NULL, 0, config);
623 CFDictionaryRemoveValue(temp, passwordKey);
624 CFDictionaryRemoveValue(temp, encryptionKey);
625 *newConfig = (CFDictionaryRef)temp;
626 } else {
627 *newConfig = NULL;
628 }
629 }
630
631 return ok;
632 }
633
634
635 __private_extern__ Boolean
636 __rank_to_str(SCNetworkServicePrimaryRank rank, CFStringRef *rankStr)
637 {
638 switch (rank) {
639 case kSCNetworkServicePrimaryRankDefault :
640 *rankStr = NULL;
641 break;
642 case kSCNetworkServicePrimaryRankFirst :
643 *rankStr = kSCValNetServicePrimaryRankFirst;
644 break;
645 case kSCNetworkServicePrimaryRankLast :
646 *rankStr = kSCValNetServicePrimaryRankLast;
647 break;
648 case kSCNetworkServicePrimaryRankNever :
649 *rankStr = kSCValNetServicePrimaryRankNever;
650 break;
651 case kSCNetworkServicePrimaryRankScoped :
652 *rankStr = kSCValNetServicePrimaryRankScoped;
653 break;
654 default :
655 return FALSE;
656 }
657
658 return TRUE;
659 }
660
661
662 __private_extern__ Boolean
663 __str_to_rank(CFStringRef rankStr, SCNetworkServicePrimaryRank *rank)
664 {
665 if (isA_CFString(rankStr)) {
666 if (CFEqual(rankStr, kSCValNetServicePrimaryRankFirst)) {
667 *rank = kSCNetworkServicePrimaryRankFirst;
668 } else if (CFEqual(rankStr, kSCValNetServicePrimaryRankLast)) {
669 *rank = kSCNetworkServicePrimaryRankLast;
670 } else if (CFEqual(rankStr, kSCValNetServicePrimaryRankNever)) {
671 *rank = kSCNetworkServicePrimaryRankNever;
672 } else if (CFEqual(rankStr, kSCValNetServicePrimaryRankScoped)) {
673 *rank = kSCNetworkServicePrimaryRankScoped;
674 } else {
675 return FALSE;
676 }
677 } else if (rankStr == NULL) {
678 *rank = kSCNetworkServicePrimaryRankDefault;
679 } else {
680 return FALSE;
681 }
682
683 return TRUE;
684 }