]> git.saurik.com Git - apple/configd.git/blob - scselect.tproj/scselect.c
configd-453.16.tar.gz
[apple/configd.git] / scselect.tproj / scselect.c
1 /*
2 * Copyright (c) 2000-2009, 2011 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/AuthSession.h>
53 #endif /* !TARGET_OS_IPHONE */
54
55
56 static Boolean apply = TRUE;
57
58
59 static 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
68 static void
69 usage(const char *command)
70 {
71 SCPrint(TRUE, stderr, CFSTR("usage: %s [-n] new-set-name\n"), command);
72 exit (EX_USAGE);
73 }
74
75
76 static Boolean
77 isAdmin()
78 {
79 gid_t groups[NGROUPS_MAX];
80 int ngroups;
81
82 if (getuid() == 0) {
83 return TRUE; // if "root"
84 }
85
86 ngroups = getgroups(NGROUPS_MAX, groups);
87 if(ngroups > 0) {
88 struct group *adminGroup;
89
90 adminGroup = getgrnam("admin");
91 if (adminGroup != NULL) {
92 gid_t adminGid = adminGroup->gr_gid;
93 int i;
94
95 for (i = 0; i < ngroups; i++) {
96 if (groups[i] == adminGid) {
97 return TRUE; // if a member of group "admin"
98 }
99 }
100 }
101 }
102
103 return FALSE;
104 }
105
106
107 #if !TARGET_OS_IPHONE
108 static void *
109 __loadSecurity(void) {
110 static void *image = NULL;
111 if (NULL == image) {
112 const char *framework = "/System/Library/Frameworks/Security.framework/Security";
113 struct stat statbuf;
114 const char *suffix = getenv("DYLD_IMAGE_SUFFIX");
115 char path[MAXPATHLEN];
116
117 strlcpy(path, framework, sizeof(path));
118 if (suffix) strlcat(path, suffix, sizeof(path));
119 if (0 <= stat(path, &statbuf)) {
120 image = dlopen(path, RTLD_LAZY | RTLD_LOCAL);
121 } else {
122 image = dlopen(framework, RTLD_LAZY | RTLD_LOCAL);
123 }
124 }
125 return (void *)image;
126 }
127
128
129 static OSStatus
130 _SessionGetInfo(SecuritySessionId session, SecuritySessionId *sessionId, SessionAttributeBits *attributes)
131 {
132 #undef SessionGetInfo
133 static typeof (SessionGetInfo) *dyfunc = NULL;
134 if (!dyfunc) {
135 void *image = __loadSecurity();
136 if (image) dyfunc = dlsym(image, "SessionGetInfo");
137 }
138 return dyfunc ? dyfunc(session, sessionId, attributes) : -1;
139 }
140 #define SessionGetInfo _SessionGetInfo
141 #endif /* !TARGET_OS_IPHONE */
142
143 static Boolean
144 hasLocalConsoleAccess()
145 {
146 #if !TARGET_OS_IPHONE
147 OSStatus error;
148 SecuritySessionId sessionID = 0;
149 SessionAttributeBits attributeBits = 0;
150
151 error = SessionGetInfo(callerSecuritySession, &sessionID, &attributeBits);
152 if (error != noErr) {
153 /* Security check failed, must not permit access */
154 return FALSE;
155 }
156
157 return (attributeBits & (sessionHasGraphicAccess|sessionIsRemote)) == sessionHasGraphicAccess;
158 #else /* !TARGET_OS_IPHONE */
159 return TRUE;
160 #endif /* !TARGET_OS_IPHONE */
161 }
162
163
164 int
165 main(int argc, char **argv)
166 {
167 const char *command = argv[0];
168 extern int optind;
169 int opt;
170 CFStringRef current = NULL;
171 int currentMatched = 0;
172 CFStringRef newSet = NULL; /* set key */
173 CFStringRef newSetUDN = NULL; /* user defined name */
174 CFStringRef prefix;
175 SCPreferencesRef prefs;
176 CFDictionaryRef sets;
177 CFIndex nSets;
178 const void **setKeys = NULL;
179 const void **setVals = NULL;
180 CFIndex i;
181
182 /* process any arguments */
183
184 while ((opt = getopt_long(argc, argv, "dvn", longopts, NULL)) != -1) {
185 switch(opt) {
186 case 'd':
187 _sc_debug = TRUE;
188 _sc_log = FALSE; /* enable framework logging */
189 break;
190 case 'v':
191 _sc_verbose = TRUE;
192 break;
193 case 'n':
194 apply = FALSE;
195 break;
196 case '?':
197 default :
198 usage(command);
199 }
200 }
201 argc -= optind;
202 argv += optind;
203
204 prefix = CFStringCreateWithFormat(NULL, NULL, CFSTR("/%@/"), kSCPrefSets);
205
206 if (argc == 1) {
207 newSet = CFStringCreateWithCString(NULL, argv[0], kCFStringEncodingMacRoman);
208
209 /* check if a full path to the new "set" was specified */
210 if ((CFStringGetLength(newSet) > 0) && CFStringHasPrefix(newSet, prefix)) {
211 CFRange range;
212 CFMutableStringRef str;
213
214 str = CFStringCreateMutableCopy(NULL, 0, newSet);
215 CFRelease(newSet);
216
217 CFStringDelete(str, CFRangeMake(0, CFStringGetLength(prefix)));
218 newSet = CFStringCreateCopy(NULL, newSet);
219 CFRelease(str);
220
221 range = CFStringFind(newSet, CFSTR("/"), 0);
222 if (range.location != kCFNotFound) {
223 SCPrint(TRUE, stderr, CFSTR("Set \"%@\" not available\n."), newSet);
224 exit (1);
225 }
226 }
227 } else {
228 newSet = CFRetain(CFSTR(""));
229 }
230
231
232 prefs = SCPreferencesCreate(NULL, CFSTR("Select Set Command"), NULL);
233 if (prefs == NULL) {
234 SCPrint(TRUE, stderr, CFSTR("SCPreferencesCreate() failed\n"));
235 exit (1);
236 }
237
238 sets = SCPreferencesGetValue(prefs, kSCPrefSets);
239 if (sets == NULL) {
240 SCPrint(TRUE, stderr, CFSTR("No network sets defined.\n"));
241 exit (1);
242 }
243
244 current = SCPreferencesGetValue(prefs, kSCPrefCurrentSet);
245 if (current != NULL) {
246 if (CFStringHasPrefix(current, prefix)) {
247 CFMutableStringRef tmp;
248
249 tmp = CFStringCreateMutableCopy(NULL, 0, current);
250 CFStringDelete(tmp, CFRangeMake(0, CFStringGetLength(prefix)));
251 current = tmp;
252 } else {
253 CFRetain(current);
254 currentMatched = -1; /* not prefixed */
255 }
256 } else {
257 current = CFRetain(CFSTR(""));
258 currentMatched = -2; /* not defined */
259 }
260
261 nSets = CFDictionaryGetCount(sets);
262 if (nSets > 0) {
263 setKeys = CFAllocatorAllocate(NULL, nSets * sizeof(CFStringRef), 0);
264 setVals = CFAllocatorAllocate(NULL, nSets * sizeof(CFDictionaryRef), 0);
265 CFDictionaryGetKeysAndValues(sets, setKeys, setVals);
266 }
267
268 /* check for set with matching name */
269 for (i = 0; i < nSets; i++) {
270 CFStringRef key = (CFStringRef) setKeys[i];
271 CFDictionaryRef dict = (CFDictionaryRef)setVals[i];
272
273 if ((currentMatched >= 0) && CFEqual(key, current)) {
274 currentMatched++;
275 }
276
277 if (CFEqual(newSet, key)) {
278 newSetUDN = CFDictionaryGetValue(dict, kSCPropUserDefinedName);
279 if (newSetUDN != NULL) CFRetain(newSetUDN);
280 goto found;
281 }
282 }
283
284 /* check for set with matching user-defined name */
285 for (i = 0; i < nSets; i++) {
286 CFStringRef key = (CFStringRef) setKeys[i];
287 CFDictionaryRef dict = (CFDictionaryRef)setVals[i];
288
289 newSetUDN = CFDictionaryGetValue(dict, kSCPropUserDefinedName);
290 if ((newSetUDN != NULL) && CFEqual(newSet, newSetUDN)) {
291 CFRelease(newSet);
292 newSet = CFRetain(key);
293 CFRetain(newSetUDN);
294 goto found;
295 }
296 }
297
298 if (argc == 1) {
299 SCPrint(TRUE, stderr, CFSTR("Set \"%@\" not available.\n"), newSet);
300 exit(1);
301 }
302
303 SCPrint(TRUE, stdout,
304 CFSTR("Defined sets include:%s\n"),
305 (currentMatched > 0) ? " (* == current set)" : "");
306
307 for (i = 0; i < nSets; i++) {
308 CFStringRef key = (CFStringRef) setKeys[i];
309 CFDictionaryRef dict = (CFDictionaryRef)setVals[i];
310 CFStringRef udn = CFDictionaryGetValue(dict, kSCPropUserDefinedName);
311
312 SCPrint(TRUE, stdout,
313 CFSTR(" %s %@\t(%@)\n"),
314 ((currentMatched > 0) && CFEqual(key, current)) ? "*" : " ",
315 key,
316 udn ? udn : CFSTR(""));
317 }
318
319 switch (currentMatched) {
320 case -2 :
321 SCPrint(TRUE, stdout, CFSTR("\nCurrent set not defined.\n"));
322 break;
323 case -1 :
324 SCPrint(TRUE, stdout, CFSTR("\nCurrent set \"%@\" may not be valid\n"), current);
325 break;
326 case 0 :
327 SCPrint(TRUE, stdout, CFSTR("\nCurrent set \"%@\" not valid\n"), current);
328 break;
329 default :
330 break;
331 }
332
333 exit (0);
334
335 found :
336
337 if (!(isAdmin() || hasLocalConsoleAccess())) {
338 SCPrint(TRUE, stderr,
339 CFSTR("Only local console users and administrators can change locations\n"));
340 exit (EX_NOPERM);
341 }
342
343 CFRelease(current);
344 current = CFStringCreateWithFormat(NULL, NULL, CFSTR("%@%@"), prefix, newSet);
345
346 if (!SCPreferencesSetValue(prefs, kSCPrefCurrentSet, current)) {
347 SCPrint(TRUE, stderr,
348 CFSTR("SCPreferencesSetValue(...,%@,%@) failed\n"),
349 kSCPrefCurrentSet,
350 current);
351 exit (1);
352 }
353
354 if (!SCPreferencesCommitChanges(prefs)) {
355 SCPrint(TRUE, stderr, CFSTR("SCPreferencesCommitChanges() failed\n"));
356 exit (1);
357 }
358
359 if (apply) {
360 if (!SCPreferencesApplyChanges(prefs)) {
361 SCPrint(TRUE, stderr, CFSTR("SCPreferencesApplyChanges() failed\n"));
362 exit (1);
363 }
364 }
365
366 SCPrint(TRUE, stdout,
367 CFSTR("%@ updated to %@ (%@)\n"),
368 kSCPrefCurrentSet,
369 newSet,
370 newSetUDN ? newSetUDN : CFSTR(""));
371
372 CFRelease(current);
373 CFRelease(newSet);
374 if (newSetUDN != NULL) CFRelease(newSetUDN);
375 CFRelease(prefix);
376 CFRelease(prefs);
377
378 exit (0);
379 return 0;
380 }