2 * Copyright (c) 2000-2004 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
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
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.
21 * @APPLE_LICENSE_HEADER_END@
25 * Modification History
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.
32 * June 1, 2001 Allan Nathanson <ajn@apple.com>
33 * - public API conversion
35 * January 1, 2001 Allan Nathanson <ajn@apple.com>
42 #include <sys/param.h>
43 #include <sys/types.h>
44 #include <sys/param.h>
46 #include <mach-o/dyld.h>
49 #include <SystemConfiguration/SystemConfiguration.h>
50 #include <SystemConfiguration/SCPrivate.h>
52 #include <Security/Security.h>
53 #include <Security/AuthSession.h>
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, '?' },
69 usage(const char *command
)
71 SCPrint(TRUE
, stderr
, CFSTR("usage: %s [-n] new-set-name\n"), command
);
79 gid_t groups
[NGROUPS_MAX
];
83 return TRUE
; // if "root"
86 ngroups
= getgroups(NGROUPS_MAX
, groups
);
88 struct group
*adminGroup
;
90 adminGroup
= getgrnam("admin");
91 if (adminGroup
!= NULL
) {
92 gid_t adminGid
= adminGroup
->gr_gid
;
95 for (i
= 0; i
< ngroups
; i
++) {
96 if (groups
[i
] == adminGid
) {
97 return TRUE
; // if a member of group "admin"
108 __loadSecurity(void) {
109 static const void *image
= NULL
;
111 const char *framework
= "/System/Library/Frameworks/Security.framework/Security";
113 const char *suffix
= getenv("DYLD_IMAGE_SUFFIX");
114 char path
[MAXPATHLEN
];
116 strcpy(path
, framework
);
117 if (suffix
) strcat(path
, suffix
);
118 if (0 <= stat(path
, &statbuf
)) {
119 image
= NSAddImage(path
, NSADDIMAGE_OPTION_NONE
);
121 image
= NSAddImage(framework
, NSADDIMAGE_OPTION_NONE
);
124 return (void *)image
;
129 _SessionGetInfo(SecuritySessionId session
, SecuritySessionId
*sessionId
, SessionAttributeBits
*attributes
)
131 static OSStatus (*dyfunc
)(SecuritySessionId
, SecuritySessionId
*, SessionAttributeBits
*) = NULL
;
133 void *image
= __loadSecurity();
134 if (image
) dyfunc
= NSAddressOfSymbol(NSLookupSymbolInImage(image
, "_SessionGetInfo", NSLOOKUPSYMBOLINIMAGE_OPTION_BIND
));
136 return dyfunc
? dyfunc(session
, sessionId
, attributes
) : -1;
138 #define SessionGetInfo _SessionGetInfo
142 hasLocalConsoleAccess()
145 SecuritySessionId sessionID
= 0;
146 SessionAttributeBits attributeBits
= 0;
148 error
= SessionGetInfo(callerSecuritySession
, &sessionID
, &attributeBits
);
149 if (error
!= noErr
) {
150 /* Security check failed, must not permit access */
154 return (attributeBits
& (sessionHasGraphicAccess
|sessionIsRemote
)) == sessionHasGraphicAccess
;
159 main(int argc
, char **argv
)
161 const char *command
= argv
[0];
164 CFStringRef current
= NULL
;
165 int currentMatched
= 0;
166 CFStringRef newSet
= NULL
; /* set key */
167 CFStringRef newSetUDN
= NULL
; /* user defined name */
169 SCPreferencesRef session
;
170 CFDictionaryRef sets
;
172 const void **setKeys
= NULL
;
173 const void **setVals
= NULL
;
176 /* process any arguments */
178 while ((opt
= getopt_long(argc
, argv
, "dvn", longopts
, NULL
)) != -1)
182 _sc_log
= FALSE
; /* enable framework logging */
197 prefix
= CFStringCreateWithFormat(NULL
, NULL
, CFSTR("/%@/"), kSCPrefSets
);
200 ? CFStringCreateWithCString(NULL
, argv
[0], kCFStringEncodingMacRoman
)
203 session
= SCPreferencesCreate(NULL
, CFSTR("Select Set Command"), NULL
);
205 SCPrint(TRUE
, stderr
, CFSTR("SCPreferencesCreate() failed\n"));
209 /* check if a full path to the new "set" was specified */
210 if ((CFStringGetLength(newSet
) > 0) && CFStringHasPrefix(newSet
, prefix
)) {
212 CFMutableStringRef str
;
214 str
= CFStringCreateMutableCopy(NULL
, 0, newSet
);
215 CFStringDelete(str
, CFRangeMake(0, CFStringGetLength(prefix
)));
217 range
= CFStringFind(str
, CFSTR("/"), 0);
218 if (range
.location
!= kCFNotFound
) {
219 SCPrint(TRUE
, stderr
, CFSTR("Set \"%@\" not available\n."), newSet
);
227 sets
= SCPreferencesGetValue(session
, kSCPrefSets
);
229 SCPrint(TRUE
, stderr
, CFSTR("SCPreferencesGetValue(...,%s,...) failed\n"));
233 current
= SCPreferencesGetValue(session
, kSCPrefCurrentSet
);
235 if (CFStringHasPrefix(current
, prefix
)) {
236 CFMutableStringRef tmp
;
238 tmp
= CFStringCreateMutableCopy(NULL
, 0, current
);
239 CFStringDelete(tmp
, CFRangeMake(0, CFStringGetLength(prefix
)));
242 currentMatched
= -1; /* not prefixed */
246 currentMatched
= -2; /* not defined */
249 nSets
= CFDictionaryGetCount(sets
);
251 setKeys
= CFAllocatorAllocate(NULL
, nSets
* sizeof(CFStringRef
), 0);
252 setVals
= CFAllocatorAllocate(NULL
, nSets
* sizeof(CFDictionaryRef
), 0);
253 CFDictionaryGetKeysAndValues(sets
, setKeys
, setVals
);
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
];
261 if ((currentMatched
>= 0) && CFEqual(key
, current
)) {
265 if (CFEqual(newSet
, key
)) {
266 newSetUDN
= CFDictionaryGetValue(dict
, kSCPropUserDefinedName
);
267 if (newSetUDN
) CFRetain(newSetUDN
);
268 current
= CFStringCreateWithFormat(NULL
, NULL
, CFSTR("%@%@"), prefix
, newSet
);
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
];
278 newSetUDN
= CFDictionaryGetValue(dict
, kSCPropUserDefinedName
);
279 if ((newSetUDN
!= NULL
) && CFEqual(newSet
, newSetUDN
)) {
281 newSet
= CFRetain(key
);
283 current
= CFStringCreateWithFormat(NULL
, NULL
, CFSTR("%@%@"), prefix
, newSet
);
289 SCPrint(TRUE
, stderr
, CFSTR("Set \"%@\" not available.\n\n"), newSet
);
292 SCPrint(TRUE
, stderr
,
293 CFSTR("Defined sets include:%s\n"),
294 (currentMatched
> 0) ? " (* == current set)" : "");
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
);
301 SCPrint(TRUE
, stderr
,
302 CFSTR(" %s %@\t(%@)\n"),
303 ((currentMatched
> 0) && CFEqual(key
, current
)) ? "*" : " ",
305 udn
? udn
: CFSTR(""));
308 switch (currentMatched
) {
310 SCPrint(TRUE
, stderr
, CFSTR("\nCurrentSet not defined.\n"));
313 SCPrint(TRUE
, stderr
, CFSTR("\nCurrentSet \"%@\" may not be valid\n"), current
);
316 SCPrint(TRUE
, stderr
, CFSTR("\nCurrentSet \"%@\" not valid\n"), current
);
326 if (!(isAdmin() || hasLocalConsoleAccess())) {
327 SCPrint(TRUE
, stderr
,
328 CFSTR("Only local console users and administrators can change locations\n"));
332 if (!SCPreferencesSetValue(session
, kSCPrefCurrentSet
, current
)) {
333 SCPrint(TRUE
, stderr
,
334 CFSTR("SCPreferencesSetValue(...,%@,%@) failed\n"),
340 if (!SCPreferencesCommitChanges(session
)) {
341 SCPrint(TRUE
, stderr
, CFSTR("SCPreferencesCommitChanges() failed\n"));
346 if (!SCPreferencesApplyChanges(session
)) {
347 SCPrint(TRUE
, stderr
, CFSTR("SCPreferencesApplyChanges() failed\n"));
354 SCPrint(TRUE
, stdout
,
355 CFSTR("%@ updated to %@ (%@)\n"),
358 newSetUDN
? newSetUDN
: CFSTR(""));