1/*
2 * Copyright (c) 2000-2009, 2011, 2012 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 * January 15, 2004		Allan Nathanson <ajn@apple.com>
28 * - limit location changes to "root" (uid==0), users who are
29 *   a member of group "admin", and processses which have access
30 *   to a local graphics console.
31 *
32 * June 1, 2001			Allan Nathanson <ajn@apple.com>
33 * - public API conversion
34 *
35 * January 1, 2001		Allan Nathanson <ajn@apple.com>
36 * - initial revision
37 */
38
39#include <getopt.h>
40#include <unistd.h>
41#include <sysexits.h>
42#include <sys/param.h>
43#include <sys/types.h>
44#include <sys/stat.h>
45#include <dlfcn.h>
46#include <grp.h>
47
48#include <SystemConfiguration/SystemConfiguration.h>
49#include <SystemConfiguration/SCPrivate.h>
50
51#if	!TARGET_OS_IPHONE
52#include <Security/Authorization.h>
53#endif	/* !TARGET_OS_IPHONE */
54
55
56static Boolean	apply	= TRUE;
57
58
59static const struct option longopts[] = {
60//	{ "debug",		no_argument,		0,	'd' },
61//	{ "verbose",		no_argument,		0,	'v' },
62//	{ "do-not-apply",	no_argument,		0,	'n' },
63	{ "help",		no_argument,		0,	'?' },
64	{ 0,			0,                      0,	0 }
65};
66
67
68static void
69usage(const char *command)
70{
71	SCPrint(TRUE, stderr, CFSTR("usage: %s [-n] new-set-name\n"), command);
72	exit (EX_USAGE);
73}
74
75
76int
77main(int argc, char **argv)
78{
79	const char		*command	= argv[0];
80	extern int		optind;
81	int			opt;
82	CFStringRef		current		= NULL;
83	int			currentMatched	= 0;
84	CFStringRef		newSet		= NULL;	/* set key */
85	CFStringRef		newSetUDN	= NULL;	/* user defined name */
86	CFStringRef		prefix;
87	SCPreferencesRef	prefs;
88	CFDictionaryRef		sets;
89	CFIndex			nSets;
90	const void		**setKeys	= NULL;
91	const void		**setVals	= NULL;
92	CFIndex			i;
93
94#if	!TARGET_OS_IPHONE
95	AuthorizationRef	authorization	= NULL;
96	AuthorizationFlags	flags		= kAuthorizationFlagDefaults;
97	CFMutableDictionaryRef	options;
98	OSStatus		status;
99#endif	// !TARGET_OS_IPHONE
100
101	/* process any arguments */
102
103	while ((opt = getopt_long(argc, argv, "dvn", longopts, NULL)) != -1) {
104		switch(opt) {
105			case 'd':
106				_sc_debug = TRUE;
107				_sc_log   = FALSE;	/* enable framework logging */
108				break;
109			case 'v':
110				_sc_verbose = TRUE;
111				break;
112			case 'n':
113				apply = FALSE;
114				break;
115			case '?':
116			default :
117				usage(command);
118		}
119	}
120	argc -= optind;
121	argv += optind;
122
123	prefix = CFStringCreateWithFormat(NULL, NULL, CFSTR("/%@/"), kSCPrefSets);
124
125	if (argc == 1) {
126		newSet = CFStringCreateWithCString(NULL, argv[0], kCFStringEncodingMacRoman);
127
128		/* check if a full path to the new "set" was specified */
129		if ((CFStringGetLength(newSet) > 0) && CFStringHasPrefix(newSet, prefix)) {
130			CFRange			range;
131			CFMutableStringRef	str;
132
133			str = CFStringCreateMutableCopy(NULL, 0, newSet);
134			CFRelease(newSet);
135
136			CFStringDelete(str, CFRangeMake(0, CFStringGetLength(prefix)));
137			newSet = CFStringCreateCopy(NULL, newSet);
138			CFRelease(str);
139
140			range = CFStringFind(newSet, CFSTR("/"), 0);
141			if (range.location != kCFNotFound) {
142				SCPrint(TRUE, stderr, CFSTR("Set \"%@\" not available\n."), newSet);
143				exit (1);
144			}
145		}
146	} else {
147		newSet = CFRetain(CFSTR(""));
148	}
149
150#if	!TARGET_OS_IPHONE
151	status = AuthorizationCreate(NULL,
152				     kAuthorizationEmptyEnvironment,
153				     flags,
154				     &authorization);
155	if (status != errAuthorizationSuccess) {
156		SCPrint(TRUE,
157			stderr,
158			CFSTR("AuthorizationCreate() failed: status = %d\n"),
159			status);
160		exit (1);
161	}
162
163	options = CFDictionaryCreateMutable(NULL,
164					    0,
165					    &kCFTypeDictionaryKeyCallBacks,
166					    &kCFTypeDictionaryValueCallBacks);
167	CFDictionarySetValue(options, kSCPreferencesOptionChangeNetworkSet, kCFBooleanTrue);
168	prefs = SCPreferencesCreateWithOptions(NULL, CFSTR("scselect"), NULL, authorization, options);
169	CFRelease(options);
170	if (prefs == NULL) {
171		SCPrint(TRUE, stderr, CFSTR("SCPreferencesCreate() failed\n"));
172		exit (1);
173	}
174#else	// !TARGET_OS_IPHONE
175	prefs = SCPreferencesCreate(NULL, CFSTR("scselect"), NULL);
176	if (prefs == NULL) {
177		SCPrint(TRUE, stderr, CFSTR("SCPreferencesCreate() failed\n"));
178		exit (1);
179	}
180#endif	// !TARGET_OS_IPHONE
181
182	sets = SCPreferencesGetValue(prefs, kSCPrefSets);
183	if (sets == NULL) {
184		SCPrint(TRUE, stderr, CFSTR("No network sets defined.\n"));
185		exit (1);
186	}
187
188	current = SCPreferencesGetValue(prefs, kSCPrefCurrentSet);
189	if (current != NULL) {
190		if (CFStringHasPrefix(current, prefix)) {
191			CFMutableStringRef	tmp;
192
193			tmp = CFStringCreateMutableCopy(NULL, 0, current);
194			CFStringDelete(tmp, CFRangeMake(0, CFStringGetLength(prefix)));
195			current = tmp;
196		} else {
197			CFRetain(current);
198			currentMatched = -1;	/* not prefixed */
199		}
200	} else {
201		current = CFRetain(CFSTR(""));
202		currentMatched = -2;	/* not defined */
203	}
204
205	nSets = CFDictionaryGetCount(sets);
206	if (nSets > 0) {
207		setKeys = CFAllocatorAllocate(NULL, nSets * sizeof(CFStringRef), 0);
208		setVals = CFAllocatorAllocate(NULL, nSets * sizeof(CFDictionaryRef), 0);
209		CFDictionaryGetKeysAndValues(sets, setKeys, setVals);
210	}
211
212	/* check for set with matching name */
213	for (i = 0; i < nSets; i++) {
214		CFStringRef	key  = (CFStringRef)    setKeys[i];
215		CFDictionaryRef	dict = (CFDictionaryRef)setVals[i];
216
217		if ((currentMatched >= 0) && CFEqual(key, current)) {
218			currentMatched++;
219		}
220
221		if (CFEqual(newSet, key)) {
222			newSetUDN = CFDictionaryGetValue(dict, kSCPropUserDefinedName);
223			if (newSetUDN != NULL) CFRetain(newSetUDN);
224			goto found;
225		}
226	}
227
228	/* check for set with matching user-defined name */
229	for (i = 0; i < nSets; i++) {
230		CFStringRef	key  = (CFStringRef)    setKeys[i];
231		CFDictionaryRef	dict = (CFDictionaryRef)setVals[i];
232
233		newSetUDN = CFDictionaryGetValue(dict, kSCPropUserDefinedName);
234		if ((newSetUDN != NULL) && CFEqual(newSet, newSetUDN)) {
235			CFRelease(newSet);
236			newSet = CFRetain(key);
237			CFRetain(newSetUDN);
238			goto found;
239		}
240	}
241
242	if (argc == 1) {
243		SCPrint(TRUE, stderr, CFSTR("Set \"%@\" not available.\n"), newSet);
244		exit(1);
245	}
246
247	SCPrint(TRUE, stdout,
248		CFSTR("Defined sets include:%s\n"),
249		(currentMatched > 0) ? " (* == current set)" : "");
250
251	for (i = 0; i < nSets; i++) {
252		CFStringRef	key  = (CFStringRef)    setKeys[i];
253		CFDictionaryRef	dict = (CFDictionaryRef)setVals[i];
254		CFStringRef	udn  = CFDictionaryGetValue(dict, kSCPropUserDefinedName);
255
256		SCPrint(TRUE, stdout,
257			CFSTR(" %s %@\t(%@)\n"),
258			((currentMatched > 0) && CFEqual(key, current)) ? "*" : " ",
259			key,
260			udn ? udn : CFSTR(""));
261	}
262
263	switch (currentMatched) {
264		case -2 :
265			SCPrint(TRUE, stdout, CFSTR("\nCurrent set not defined.\n"));
266			break;
267		case -1 :
268			SCPrint(TRUE, stdout, CFSTR("\nCurrent set \"%@\" may not be valid\n"), current);
269			break;
270		case  0 :
271			SCPrint(TRUE, stdout, CFSTR("\nCurrent set \"%@\" not valid\n"), current);
272			break;
273		default :
274			break;
275	}
276
277	CFRelease(prefix);
278	exit (0);
279
280    found :
281
282	CFRelease(current);
283	current = CFStringCreateWithFormat(NULL, NULL, CFSTR("%@%@"), prefix, newSet);
284
285	if (!SCPreferencesSetValue(prefs, kSCPrefCurrentSet, current)) {
286		SCPrint(TRUE, stderr,
287			CFSTR("SCPreferencesSetValue(...,%@,%@) failed: %s\n"),
288			kSCPrefCurrentSet,
289			current,
290			SCErrorString(SCError()));
291		exit (1);
292	}
293
294	if (!SCPreferencesCommitChanges(prefs)) {
295		int	sc_status	= SCError();
296
297		if (sc_status == kSCStatusAccessError) {
298			SCPrint(TRUE, stderr,
299				CFSTR("Only local console users and administrators can change locations\n"));
300			exit (EX_NOPERM);
301		} else {
302			SCPrint(TRUE, stderr,
303				CFSTR("SCPreferencesCommitChanges() failed: %s\n"),
304				SCErrorString(sc_status));
305			exit (1);
306		}
307	}
308
309	if (apply) {
310		if (!SCPreferencesApplyChanges(prefs)) {
311			SCPrint(TRUE, stderr,
312				CFSTR("SCPreferencesApplyChanges() failed %s\n"),
313				SCErrorString(SCError()));
314			exit (1);
315		}
316	}
317
318	SCPrint(TRUE, stdout,
319		CFSTR("%@ updated to %@ (%@)\n"),
320		kSCPrefCurrentSet,
321		newSet,
322		newSetUDN ? newSetUDN : CFSTR(""));
323
324	CFRelease(current);
325	CFRelease(newSet);
326	if (newSetUDN != NULL)	CFRelease(newSetUDN);
327	CFRelease(prefix);
328	CFRelease(prefs);
329
330#if	!TARGET_OS_IPHONE
331	AuthorizationFree(authorization, kAuthorizationFlagDefaults);
332//	AuthorizationFree(authorization, kAuthorizationFlagDestroyRights);
333#endif	/* !TARGET_OS_IPHONE */
334
335	exit (0);
336	return 0;
337}
338