]>
Commit | Line | Data |
---|---|---|
5958d7c0 | 1 | /* |
9de8ab86 | 2 | * Copyright (c) 2000-2009, 2011, 2012, 2014, 2015 Apple Inc. All rights reserved. |
5958d7c0 A |
3 | * |
4 | * @APPLE_LICENSE_HEADER_START@ | |
9de8ab86 | 5 | * |
009ee3c6 A |
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. | |
9de8ab86 | 12 | * |
009ee3c6 A |
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 | |
5958d7c0 A |
15 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, |
16 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, | |
009ee3c6 A |
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. | |
9de8ab86 | 20 | * |
5958d7c0 A |
21 | * @APPLE_LICENSE_HEADER_END@ |
22 | */ | |
23 | ||
0fae82ee A |
24 | /* |
25 | * Modification History | |
26 | * | |
4c5e92e2 A |
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 | * | |
0fae82ee A |
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 | ||
009ee3c6 | 39 | #include <getopt.h> |
5958d7c0 | 40 | #include <unistd.h> |
009ee3c6 | 41 | #include <sysexits.h> |
4c5e92e2 A |
42 | #include <sys/param.h> |
43 | #include <sys/types.h> | |
4c5e92e2 | 44 | #include <sys/stat.h> |
edebe297 | 45 | #include <dlfcn.h> |
4c5e92e2 | 46 | #include <grp.h> |
5958d7c0 | 47 | |
0fae82ee A |
48 | #include <SystemConfiguration/SystemConfiguration.h> |
49 | #include <SystemConfiguration/SCPrivate.h> | |
50 | ||
a40a14f8 | 51 | #if !TARGET_OS_IPHONE |
5e9ce69e | 52 | #include <Security/Authorization.h> |
a40a14f8 | 53 | #endif /* !TARGET_OS_IPHONE */ |
4c5e92e2 | 54 | |
5958d7c0 | 55 | |
dbf6a266 | 56 | static Boolean apply = TRUE; |
5958d7c0 A |
57 | |
58 | ||
dbf6a266 | 59 | static const struct option longopts[] = { |
009ee3c6 A |
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 | ||
4c5e92e2 | 68 | static void |
5958d7c0 A |
69 | usage(const char *command) |
70 | { | |
0fae82ee | 71 | SCPrint(TRUE, stderr, CFSTR("usage: %s [-n] new-set-name\n"), command); |
009ee3c6 | 72 | exit (EX_USAGE); |
5958d7c0 A |
73 | } |
74 | ||
75 | ||
76 | int | |
77 | main(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; | |
dbf6a266 | 87 | SCPreferencesRef prefs; |
5958d7c0 A |
88 | CFDictionaryRef sets; |
89 | CFIndex nSets; | |
a5f60add A |
90 | const void **setKeys = NULL; |
91 | const void **setVals = NULL; | |
5958d7c0 A |
92 | CFIndex i; |
93 | ||
5e9ce69e A |
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 | ||
5958d7c0 A |
101 | /* process any arguments */ |
102 | ||
17d3ee29 | 103 | while ((opt = getopt_long(argc, argv, "dvn", longopts, NULL)) != -1) { |
5958d7c0 | 104 | switch(opt) { |
17d3ee29 A |
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 | } | |
5958d7c0 A |
119 | } |
120 | argc -= optind; | |
121 | argv += optind; | |
122 | ||
123 | prefix = CFStringCreateWithFormat(NULL, NULL, CFSTR("/%@/"), kSCPrefSets); | |
124 | ||
17d3ee29 A |
125 | if (argc == 1) { |
126 | newSet = CFStringCreateWithCString(NULL, argv[0], kCFStringEncodingMacRoman); | |
5958d7c0 | 127 | |
17d3ee29 A |
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; | |
5958d7c0 | 132 | |
17d3ee29 A |
133 | str = CFStringCreateMutableCopy(NULL, 0, newSet); |
134 | CFRelease(newSet); | |
5958d7c0 | 135 | |
17d3ee29 A |
136 | CFStringDelete(str, CFRangeMake(0, CFStringGetLength(prefix))); |
137 | newSet = CFStringCreateCopy(NULL, newSet); | |
138 | CFRelease(str); | |
5958d7c0 | 139 | |
17d3ee29 A |
140 | range = CFStringFind(newSet, CFSTR("/"), 0); |
141 | if (range.location != kCFNotFound) { | |
9de8ab86 | 142 | SCPrint(TRUE, stderr, CFSTR("Set \"%@\" not available\n"), newSet); |
17d3ee29 A |
143 | exit (1); |
144 | } | |
5958d7c0 | 145 | } |
17d3ee29 A |
146 | } else { |
147 | newSet = CFRetain(CFSTR("")); | |
148 | } | |
5958d7c0 | 149 | |
5e9ce69e A |
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"), | |
78403150 | 159 | (int)status); |
5e9ce69e A |
160 | exit (1); |
161 | } | |
17d3ee29 | 162 | |
5e9ce69e A |
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); | |
17d3ee29 A |
170 | if (prefs == NULL) { |
171 | SCPrint(TRUE, stderr, CFSTR("SCPreferencesCreate() failed\n")); | |
172 | exit (1); | |
5958d7c0 | 173 | } |
5e9ce69e A |
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 | |
5958d7c0 | 181 | |
dbf6a266 A |
182 | sets = SCPreferencesGetValue(prefs, kSCPrefSets); |
183 | if (sets == NULL) { | |
184 | SCPrint(TRUE, stderr, CFSTR("No network sets defined.\n")); | |
5958d7c0 A |
185 | exit (1); |
186 | } | |
187 | ||
dbf6a266 A |
188 | current = SCPreferencesGetValue(prefs, kSCPrefCurrentSet); |
189 | if (current != NULL) { | |
0fae82ee A |
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 { | |
a40a14f8 | 197 | CFRetain(current); |
0fae82ee A |
198 | currentMatched = -1; /* not prefixed */ |
199 | } | |
200 | } else { | |
a40a14f8 | 201 | current = CFRetain(CFSTR("")); |
0fae82ee | 202 | currentMatched = -2; /* not defined */ |
5958d7c0 A |
203 | } |
204 | ||
205 | nSets = CFDictionaryGetCount(sets); | |
a5f60add A |
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 | } | |
5958d7c0 A |
211 | |
212 | /* check for set with matching name */ | |
009ee3c6 | 213 | for (i = 0; i < nSets; i++) { |
5958d7c0 A |
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); | |
dbf6a266 | 223 | if (newSetUDN != NULL) CFRetain(newSetUDN); |
5958d7c0 A |
224 | goto found; |
225 | } | |
226 | } | |
227 | ||
228 | /* check for set with matching user-defined name */ | |
009ee3c6 | 229 | for (i = 0; i < nSets; i++) { |
5958d7c0 A |
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); | |
5958d7c0 A |
238 | goto found; |
239 | } | |
240 | } | |
241 | ||
009ee3c6 | 242 | if (argc == 1) { |
6bb65964 A |
243 | SCPrint(TRUE, stderr, CFSTR("Set \"%@\" not available.\n"), newSet); |
244 | exit(1); | |
5958d7c0 A |
245 | } |
246 | ||
6bb65964 | 247 | SCPrint(TRUE, stdout, |
0fae82ee A |
248 | CFSTR("Defined sets include:%s\n"), |
249 | (currentMatched > 0) ? " (* == current set)" : ""); | |
5958d7c0 | 250 | |
009ee3c6 | 251 | for (i = 0; i < nSets; i++) { |
5958d7c0 A |
252 | CFStringRef key = (CFStringRef) setKeys[i]; |
253 | CFDictionaryRef dict = (CFDictionaryRef)setVals[i]; | |
254 | CFStringRef udn = CFDictionaryGetValue(dict, kSCPropUserDefinedName); | |
255 | ||
6bb65964 | 256 | SCPrint(TRUE, stdout, |
0fae82ee | 257 | CFSTR(" %s %@\t(%@)\n"), |
5958d7c0 A |
258 | ((currentMatched > 0) && CFEqual(key, current)) ? "*" : " ", |
259 | key, | |
260 | udn ? udn : CFSTR("")); | |
261 | } | |
262 | ||
263 | switch (currentMatched) { | |
264 | case -2 : | |
6bb65964 | 265 | SCPrint(TRUE, stdout, CFSTR("\nCurrent set not defined.\n")); |
5958d7c0 A |
266 | break; |
267 | case -1 : | |
6bb65964 | 268 | SCPrint(TRUE, stdout, CFSTR("\nCurrent set \"%@\" may not be valid\n"), current); |
5958d7c0 A |
269 | break; |
270 | case 0 : | |
6bb65964 | 271 | SCPrint(TRUE, stdout, CFSTR("\nCurrent set \"%@\" not valid\n"), current); |
5958d7c0 A |
272 | break; |
273 | default : | |
274 | break; | |
275 | } | |
276 | ||
5e9ce69e | 277 | CFRelease(prefix); |
6bb65964 | 278 | exit (0); |
5958d7c0 A |
279 | |
280 | found : | |
281 | ||
a40a14f8 A |
282 | CFRelease(current); |
283 | current = CFStringCreateWithFormat(NULL, NULL, CFSTR("%@%@"), prefix, newSet); | |
284 | ||
9de8ab86 | 285 | SCPreferencesSetValue(prefs, kSCPrefCurrentSet, current); |
5958d7c0 | 286 | |
dbf6a266 | 287 | if (!SCPreferencesCommitChanges(prefs)) { |
5e9ce69e A |
288 | int sc_status = SCError(); |
289 | ||
290 | if (sc_status == kSCStatusAccessError) { | |
291 | SCPrint(TRUE, stderr, | |
292 | CFSTR("Only local console users and administrators can change locations\n")); | |
293 | exit (EX_NOPERM); | |
294 | } else { | |
295 | SCPrint(TRUE, stderr, | |
296 | CFSTR("SCPreferencesCommitChanges() failed: %s\n"), | |
297 | SCErrorString(sc_status)); | |
298 | exit (1); | |
299 | } | |
5958d7c0 A |
300 | } |
301 | ||
302 | if (apply) { | |
dbf6a266 | 303 | if (!SCPreferencesApplyChanges(prefs)) { |
5e9ce69e A |
304 | SCPrint(TRUE, stderr, |
305 | CFSTR("SCPreferencesApplyChanges() failed %s\n"), | |
306 | SCErrorString(SCError())); | |
5958d7c0 A |
307 | exit (1); |
308 | } | |
309 | } | |
310 | ||
0fae82ee A |
311 | SCPrint(TRUE, stdout, |
312 | CFSTR("%@ updated to %@ (%@)\n"), | |
5958d7c0 A |
313 | kSCPrefCurrentSet, |
314 | newSet, | |
315 | newSetUDN ? newSetUDN : CFSTR("")); | |
316 | ||
a40a14f8 A |
317 | CFRelease(current); |
318 | CFRelease(newSet); | |
319 | if (newSetUDN != NULL) CFRelease(newSetUDN); | |
320 | CFRelease(prefix); | |
321 | CFRelease(prefs); | |
322 | ||
5e9ce69e A |
323 | #if !TARGET_OS_IPHONE |
324 | AuthorizationFree(authorization, kAuthorizationFlagDefaults); | |
325 | // AuthorizationFree(authorization, kAuthorizationFlagDestroyRights); | |
326 | #endif /* !TARGET_OS_IPHONE */ | |
327 | ||
5958d7c0 A |
328 | exit (0); |
329 | return 0; | |
330 | } |