]> git.saurik.com Git - apple/configd.git/blob - scselect.tproj/scselect.c
configd-395.6.tar.gz
[apple/configd.git] / scselect.tproj / scselect.c
1 /*
2 * Copyright (c) 2000-2009 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/Versions/A/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 argc -= optind;
201 argv += optind;
202
203 prefix = CFStringCreateWithFormat(NULL, NULL, CFSTR("/%@/"), kSCPrefSets);
204
205 newSet = (argc == 1)
206 ? CFStringCreateWithCString(NULL, argv[0], kCFStringEncodingMacRoman)
207 : CFRetain(CFSTR(""));
208
209 prefs = SCPreferencesCreate(NULL, CFSTR("Select Set Command"), NULL);
210 if (prefs == NULL) {
211 SCPrint(TRUE, stderr, CFSTR("SCPreferencesCreate() failed\n"));
212 exit (1);
213 }
214
215 /* check if a full path to the new "set" was specified */
216 if ((CFStringGetLength(newSet) > 0) && CFStringHasPrefix(newSet, prefix)) {
217 CFRange range;
218 CFMutableStringRef str;
219
220 str = CFStringCreateMutableCopy(NULL, 0, newSet);
221 CFStringDelete(str, CFRangeMake(0, CFStringGetLength(prefix)));
222
223 range = CFStringFind(str, CFSTR("/"), 0);
224 if (range.location != kCFNotFound) {
225 SCPrint(TRUE, stderr, CFSTR("Set \"%@\" not available\n."), newSet);
226 exit (1);
227 }
228
229 CFRelease(newSet);
230 newSet = str;
231 }
232
233 sets = SCPreferencesGetValue(prefs, kSCPrefSets);
234 if (sets == NULL) {
235 SCPrint(TRUE, stderr, CFSTR("No network sets defined.\n"));
236 exit (1);
237 }
238
239 current = SCPreferencesGetValue(prefs, kSCPrefCurrentSet);
240 if (current != NULL) {
241 if (CFStringHasPrefix(current, prefix)) {
242 CFMutableStringRef tmp;
243
244 tmp = CFStringCreateMutableCopy(NULL, 0, current);
245 CFStringDelete(tmp, CFRangeMake(0, CFStringGetLength(prefix)));
246 current = tmp;
247 } else {
248 CFRetain(current);
249 currentMatched = -1; /* not prefixed */
250 }
251 } else {
252 current = CFRetain(CFSTR(""));
253 currentMatched = -2; /* not defined */
254 }
255
256 nSets = CFDictionaryGetCount(sets);
257 if (nSets > 0) {
258 setKeys = CFAllocatorAllocate(NULL, nSets * sizeof(CFStringRef), 0);
259 setVals = CFAllocatorAllocate(NULL, nSets * sizeof(CFDictionaryRef), 0);
260 CFDictionaryGetKeysAndValues(sets, setKeys, setVals);
261 }
262
263 /* check for set with matching name */
264 for (i = 0; i < nSets; i++) {
265 CFStringRef key = (CFStringRef) setKeys[i];
266 CFDictionaryRef dict = (CFDictionaryRef)setVals[i];
267
268 if ((currentMatched >= 0) && CFEqual(key, current)) {
269 currentMatched++;
270 }
271
272 if (CFEqual(newSet, key)) {
273 newSetUDN = CFDictionaryGetValue(dict, kSCPropUserDefinedName);
274 if (newSetUDN != NULL) CFRetain(newSetUDN);
275 goto found;
276 }
277 }
278
279 /* check for set with matching user-defined name */
280 for (i = 0; i < nSets; i++) {
281 CFStringRef key = (CFStringRef) setKeys[i];
282 CFDictionaryRef dict = (CFDictionaryRef)setVals[i];
283
284 newSetUDN = CFDictionaryGetValue(dict, kSCPropUserDefinedName);
285 if ((newSetUDN != NULL) && CFEqual(newSet, newSetUDN)) {
286 CFRelease(newSet);
287 newSet = CFRetain(key);
288 CFRetain(newSetUDN);
289 goto found;
290 }
291 }
292
293 if (argc == 1) {
294 SCPrint(TRUE, stderr, CFSTR("Set \"%@\" not available.\n"), newSet);
295 exit(1);
296 }
297
298 SCPrint(TRUE, stdout,
299 CFSTR("Defined sets include:%s\n"),
300 (currentMatched > 0) ? " (* == current set)" : "");
301
302 for (i = 0; i < nSets; i++) {
303 CFStringRef key = (CFStringRef) setKeys[i];
304 CFDictionaryRef dict = (CFDictionaryRef)setVals[i];
305 CFStringRef udn = CFDictionaryGetValue(dict, kSCPropUserDefinedName);
306
307 SCPrint(TRUE, stdout,
308 CFSTR(" %s %@\t(%@)\n"),
309 ((currentMatched > 0) && CFEqual(key, current)) ? "*" : " ",
310 key,
311 udn ? udn : CFSTR(""));
312 }
313
314 switch (currentMatched) {
315 case -2 :
316 SCPrint(TRUE, stdout, CFSTR("\nCurrent set not defined.\n"));
317 break;
318 case -1 :
319 SCPrint(TRUE, stdout, CFSTR("\nCurrent set \"%@\" may not be valid\n"), current);
320 break;
321 case 0 :
322 SCPrint(TRUE, stdout, CFSTR("\nCurrent set \"%@\" not valid\n"), current);
323 break;
324 default :
325 break;
326 }
327
328 exit (0);
329
330 found :
331
332 if (!(isAdmin() || hasLocalConsoleAccess())) {
333 SCPrint(TRUE, stderr,
334 CFSTR("Only local console users and administrators can change locations\n"));
335 exit (EX_NOPERM);
336 }
337
338 CFRelease(current);
339 current = CFStringCreateWithFormat(NULL, NULL, CFSTR("%@%@"), prefix, newSet);
340
341 if (!SCPreferencesSetValue(prefs, kSCPrefCurrentSet, current)) {
342 SCPrint(TRUE, stderr,
343 CFSTR("SCPreferencesSetValue(...,%@,%@) failed\n"),
344 kSCPrefCurrentSet,
345 current);
346 exit (1);
347 }
348
349 if (!SCPreferencesCommitChanges(prefs)) {
350 SCPrint(TRUE, stderr, CFSTR("SCPreferencesCommitChanges() failed\n"));
351 exit (1);
352 }
353
354 if (apply) {
355 if (!SCPreferencesApplyChanges(prefs)) {
356 SCPrint(TRUE, stderr, CFSTR("SCPreferencesApplyChanges() failed\n"));
357 exit (1);
358 }
359 }
360
361 SCPrint(TRUE, stdout,
362 CFSTR("%@ updated to %@ (%@)\n"),
363 kSCPrefCurrentSet,
364 newSet,
365 newSetUDN ? newSetUDN : CFSTR(""));
366
367 CFRelease(current);
368 CFRelease(newSet);
369 if (newSetUDN != NULL) CFRelease(newSetUDN);
370 CFRelease(prefix);
371 CFRelease(prefs);
372
373 exit (0);
374 return 0;
375 }