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