]> git.saurik.com Git - apple/configd.git/blob - scselect.tproj/scselect.c
configd-84.6.tar.gz
[apple/configd.git] / scselect.tproj / scselect.c
1 /*
2 * Copyright (c) 2000-2004 Apple Computer, 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/param.h>
45 #include <sys/stat.h>
46 #include <mach-o/dyld.h>
47 #include <grp.h>
48
49 #include <SystemConfiguration/SystemConfiguration.h>
50 #include <SystemConfiguration/SCPrivate.h>
51
52 #include <Security/Security.h>
53 #include <Security/AuthSession.h>
54
55
56 Boolean apply = TRUE;
57
58
59 static 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 static void *
108 __loadSecurity(void) {
109 static const void *image = NULL;
110 if (NULL == image) {
111 const char *framework = "/System/Library/Frameworks/Security.framework/Security";
112 struct stat statbuf;
113 const char *suffix = getenv("DYLD_IMAGE_SUFFIX");
114 char path[MAXPATHLEN];
115
116 strcpy(path, framework);
117 if (suffix) strcat(path, suffix);
118 if (0 <= stat(path, &statbuf)) {
119 image = NSAddImage(path, NSADDIMAGE_OPTION_NONE);
120 } else {
121 image = NSAddImage(framework, NSADDIMAGE_OPTION_NONE);
122 }
123 }
124 return (void *)image;
125 }
126
127
128 static OSStatus
129 _SessionGetInfo(SecuritySessionId session, SecuritySessionId *sessionId, SessionAttributeBits *attributes)
130 {
131 static OSStatus (*dyfunc)(SecuritySessionId, SecuritySessionId *, SessionAttributeBits *) = NULL;
132 if (!dyfunc) {
133 void *image = __loadSecurity();
134 if (image) dyfunc = NSAddressOfSymbol(NSLookupSymbolInImage(image, "_SessionGetInfo", NSLOOKUPSYMBOLINIMAGE_OPTION_BIND));
135 }
136 return dyfunc ? dyfunc(session, sessionId, attributes) : -1;
137 }
138 #define SessionGetInfo _SessionGetInfo
139
140
141 static Boolean
142 hasLocalConsoleAccess()
143 {
144 OSStatus error;
145 SecuritySessionId sessionID = 0;
146 SessionAttributeBits attributeBits = 0;
147
148 error = SessionGetInfo(callerSecuritySession, &sessionID, &attributeBits);
149 if (error != noErr) {
150 /* Security check failed, must not permit access */
151 return FALSE;
152 }
153
154 return (attributeBits & (sessionHasGraphicAccess|sessionIsRemote)) == sessionHasGraphicAccess;
155 }
156
157
158 int
159 main(int argc, char **argv)
160 {
161 const char *command = argv[0];
162 extern int optind;
163 int opt;
164 CFStringRef current = NULL;
165 int currentMatched = 0;
166 CFStringRef newSet = NULL; /* set key */
167 CFStringRef newSetUDN = NULL; /* user defined name */
168 CFStringRef prefix;
169 SCPreferencesRef session;
170 CFDictionaryRef sets;
171 CFIndex nSets;
172 const void **setKeys = NULL;
173 const void **setVals = NULL;
174 CFIndex i;
175
176 /* process any arguments */
177
178 while ((opt = getopt_long(argc, argv, "dvn", longopts, NULL)) != -1)
179 switch(opt) {
180 case 'd':
181 _sc_debug = TRUE;
182 _sc_log = FALSE; /* enable framework logging */
183 break;
184 case 'v':
185 _sc_verbose = TRUE;
186 break;
187 case 'n':
188 apply = FALSE;
189 break;
190 case '?':
191 default :
192 usage(command);
193 }
194 argc -= optind;
195 argv += optind;
196
197 prefix = CFStringCreateWithFormat(NULL, NULL, CFSTR("/%@/"), kSCPrefSets);
198
199 newSet = (argc == 1)
200 ? CFStringCreateWithCString(NULL, argv[0], kCFStringEncodingMacRoman)
201 : CFSTR("");
202
203 session = SCPreferencesCreate(NULL, CFSTR("Select Set Command"), NULL);
204 if (!session) {
205 SCPrint(TRUE, stderr, CFSTR("SCPreferencesCreate() failed\n"));
206 exit (1);
207 }
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 CFStringDelete(str, CFRangeMake(0, CFStringGetLength(prefix)));
216
217 range = CFStringFind(str, CFSTR("/"), 0);
218 if (range.location != kCFNotFound) {
219 SCPrint(TRUE, stderr, CFSTR("Set \"%@\" not available\n."), newSet);
220 exit (1);
221 }
222
223 CFRelease(newSet);
224 newSet = str;
225 }
226
227 sets = SCPreferencesGetValue(session, kSCPrefSets);
228 if (!sets) {
229 SCPrint(TRUE, stderr, CFSTR("SCPreferencesGetValue(...,%s,...) failed\n"));
230 exit (1);
231 }
232
233 current = SCPreferencesGetValue(session, kSCPrefCurrentSet);
234 if (current) {
235 if (CFStringHasPrefix(current, prefix)) {
236 CFMutableStringRef tmp;
237
238 tmp = CFStringCreateMutableCopy(NULL, 0, current);
239 CFStringDelete(tmp, CFRangeMake(0, CFStringGetLength(prefix)));
240 current = tmp;
241 } else {
242 currentMatched = -1; /* not prefixed */
243 }
244 } else {
245 current = CFSTR("");
246 currentMatched = -2; /* not defined */
247 }
248
249 nSets = CFDictionaryGetCount(sets);
250 if (nSets > 0) {
251 setKeys = CFAllocatorAllocate(NULL, nSets * sizeof(CFStringRef), 0);
252 setVals = CFAllocatorAllocate(NULL, nSets * sizeof(CFDictionaryRef), 0);
253 CFDictionaryGetKeysAndValues(sets, setKeys, setVals);
254 }
255
256 /* check for set with matching name */
257 for (i = 0; i < nSets; i++) {
258 CFStringRef key = (CFStringRef) setKeys[i];
259 CFDictionaryRef dict = (CFDictionaryRef)setVals[i];
260
261 if ((currentMatched >= 0) && CFEqual(key, current)) {
262 currentMatched++;
263 }
264
265 if (CFEqual(newSet, key)) {
266 newSetUDN = CFDictionaryGetValue(dict, kSCPropUserDefinedName);
267 if (newSetUDN) CFRetain(newSetUDN);
268 current = CFStringCreateWithFormat(NULL, NULL, CFSTR("%@%@"), prefix, newSet);
269 goto found;
270 }
271 }
272
273 /* check for set with matching user-defined name */
274 for (i = 0; i < nSets; i++) {
275 CFStringRef key = (CFStringRef) setKeys[i];
276 CFDictionaryRef dict = (CFDictionaryRef)setVals[i];
277
278 newSetUDN = CFDictionaryGetValue(dict, kSCPropUserDefinedName);
279 if ((newSetUDN != NULL) && CFEqual(newSet, newSetUDN)) {
280 CFRelease(newSet);
281 newSet = CFRetain(key);
282 CFRetain(newSetUDN);
283 current = CFStringCreateWithFormat(NULL, NULL, CFSTR("%@%@"), prefix, newSet);
284 goto found;
285 }
286 }
287
288 if (argc == 1) {
289 SCPrint(TRUE, stderr, CFSTR("Set \"%@\" not available.\n\n"), newSet);
290 }
291
292 SCPrint(TRUE, stderr,
293 CFSTR("Defined sets include:%s\n"),
294 (currentMatched > 0) ? " (* == current set)" : "");
295
296 for (i = 0; i < nSets; i++) {
297 CFStringRef key = (CFStringRef) setKeys[i];
298 CFDictionaryRef dict = (CFDictionaryRef)setVals[i];
299 CFStringRef udn = CFDictionaryGetValue(dict, kSCPropUserDefinedName);
300
301 SCPrint(TRUE, stderr,
302 CFSTR(" %s %@\t(%@)\n"),
303 ((currentMatched > 0) && CFEqual(key, current)) ? "*" : " ",
304 key,
305 udn ? udn : CFSTR(""));
306 }
307
308 switch (currentMatched) {
309 case -2 :
310 SCPrint(TRUE, stderr, CFSTR("\nCurrentSet not defined.\n"));
311 break;
312 case -1 :
313 SCPrint(TRUE, stderr, CFSTR("\nCurrentSet \"%@\" may not be valid\n"), current);
314 break;
315 case 0 :
316 SCPrint(TRUE, stderr, CFSTR("\nCurrentSet \"%@\" not valid\n"), current);
317 break;
318 default :
319 break;
320 }
321
322 exit (1);
323
324 found :
325
326 if (!(isAdmin() || hasLocalConsoleAccess())) {
327 SCPrint(TRUE, stderr,
328 CFSTR("Only local console users and administrators can change locations\n"));
329 exit (EX_NOPERM);
330 }
331
332 if (!SCPreferencesSetValue(session, kSCPrefCurrentSet, current)) {
333 SCPrint(TRUE, stderr,
334 CFSTR("SCPreferencesSetValue(...,%@,%@) failed\n"),
335 kSCPrefCurrentSet,
336 current);
337 exit (1);
338 }
339
340 if (!SCPreferencesCommitChanges(session)) {
341 SCPrint(TRUE, stderr, CFSTR("SCPreferencesCommitChanges() failed\n"));
342 exit (1);
343 }
344
345 if (apply) {
346 if (!SCPreferencesApplyChanges(session)) {
347 SCPrint(TRUE, stderr, CFSTR("SCPreferencesApplyChanges() failed\n"));
348 exit (1);
349 }
350 }
351
352 CFRelease(session);
353
354 SCPrint(TRUE, stdout,
355 CFSTR("%@ updated to %@ (%@)\n"),
356 kSCPrefCurrentSet,
357 newSet,
358 newSetUDN ? newSetUDN : CFSTR(""));
359
360 exit (0);
361 return 0;
362 }