]> git.saurik.com Git - apple/configd.git/blame - scselect.tproj/scselect.c
configd-453.19.tar.gz
[apple/configd.git] / scselect.tproj / scselect.c
CommitLineData
5958d7c0 1/*
17d3ee29 2 * Copyright (c) 2000-2009, 2011 Apple Inc. All rights reserved.
5958d7c0
A
3 *
4 * @APPLE_LICENSE_HEADER_START@
009ee3c6 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.
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
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.
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
4c5e92e2 52#include <Security/AuthSession.h>
a40a14f8 53#endif /* !TARGET_OS_IPHONE */
4c5e92e2 54
5958d7c0 55
dbf6a266 56static Boolean apply = TRUE;
5958d7c0
A
57
58
dbf6a266 59static 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 68static void
5958d7c0
A
69usage(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
4c5e92e2
A
76static Boolean
77isAdmin()
78{
dbf6a266
A
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;
4c5e92e2
A
104}
105
106
a40a14f8 107#if !TARGET_OS_IPHONE
4c5e92e2
A
108static void *
109__loadSecurity(void) {
edebe297 110 static void *image = NULL;
4c5e92e2 111 if (NULL == image) {
17d3ee29 112 const char *framework = "/System/Library/Frameworks/Security.framework/Security";
4c5e92e2
A
113 struct stat statbuf;
114 const char *suffix = getenv("DYLD_IMAGE_SUFFIX");
115 char path[MAXPATHLEN];
116
edebe297
A
117 strlcpy(path, framework, sizeof(path));
118 if (suffix) strlcat(path, suffix, sizeof(path));
4c5e92e2 119 if (0 <= stat(path, &statbuf)) {
edebe297 120 image = dlopen(path, RTLD_LAZY | RTLD_LOCAL);
4c5e92e2 121 } else {
edebe297 122 image = dlopen(framework, RTLD_LAZY | RTLD_LOCAL);
4c5e92e2
A
123 }
124 }
125 return (void *)image;
126}
127
128
129static OSStatus
130_SessionGetInfo(SecuritySessionId session, SecuritySessionId *sessionId, SessionAttributeBits *attributes)
131{
edebe297
A
132 #undef SessionGetInfo
133 static typeof (SessionGetInfo) *dyfunc = NULL;
4c5e92e2
A
134 if (!dyfunc) {
135 void *image = __loadSecurity();
edebe297 136 if (image) dyfunc = dlsym(image, "SessionGetInfo");
4c5e92e2
A
137 }
138 return dyfunc ? dyfunc(session, sessionId, attributes) : -1;
139}
140#define SessionGetInfo _SessionGetInfo
a40a14f8 141#endif /* !TARGET_OS_IPHONE */
4c5e92e2
A
142
143static Boolean
144hasLocalConsoleAccess()
145{
a40a14f8 146#if !TARGET_OS_IPHONE
dbf6a266
A
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 }
4c5e92e2 156
dbf6a266 157 return (attributeBits & (sessionHasGraphicAccess|sessionIsRemote)) == sessionHasGraphicAccess;
a40a14f8
A
158#else /* !TARGET_OS_IPHONE */
159 return TRUE;
160#endif /* !TARGET_OS_IPHONE */
4c5e92e2
A
161}
162
163
5958d7c0
A
164int
165main(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;
dbf6a266 175 SCPreferencesRef prefs;
5958d7c0
A
176 CFDictionaryRef sets;
177 CFIndex nSets;
a5f60add
A
178 const void **setKeys = NULL;
179 const void **setVals = NULL;
5958d7c0
A
180 CFIndex i;
181
182 /* process any arguments */
183
17d3ee29 184 while ((opt = getopt_long(argc, argv, "dvn", longopts, NULL)) != -1) {
5958d7c0 185 switch(opt) {
17d3ee29
A
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 }
5958d7c0
A
200 }
201 argc -= optind;
202 argv += optind;
203
204 prefix = CFStringCreateWithFormat(NULL, NULL, CFSTR("/%@/"), kSCPrefSets);
205
17d3ee29
A
206 if (argc == 1) {
207 newSet = CFStringCreateWithCString(NULL, argv[0], kCFStringEncodingMacRoman);
5958d7c0 208
17d3ee29
A
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;
5958d7c0 213
17d3ee29
A
214 str = CFStringCreateMutableCopy(NULL, 0, newSet);
215 CFRelease(newSet);
5958d7c0 216
17d3ee29
A
217 CFStringDelete(str, CFRangeMake(0, CFStringGetLength(prefix)));
218 newSet = CFStringCreateCopy(NULL, newSet);
219 CFRelease(str);
5958d7c0 220
17d3ee29
A
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 }
5958d7c0 226 }
17d3ee29
A
227 } else {
228 newSet = CFRetain(CFSTR(""));
229 }
5958d7c0 230
17d3ee29
A
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);
5958d7c0
A
236 }
237
dbf6a266
A
238 sets = SCPreferencesGetValue(prefs, kSCPrefSets);
239 if (sets == NULL) {
240 SCPrint(TRUE, stderr, CFSTR("No network sets defined.\n"));
5958d7c0
A
241 exit (1);
242 }
243
dbf6a266
A
244 current = SCPreferencesGetValue(prefs, kSCPrefCurrentSet);
245 if (current != NULL) {
0fae82ee
A
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 {
a40a14f8 253 CFRetain(current);
0fae82ee
A
254 currentMatched = -1; /* not prefixed */
255 }
256 } else {
a40a14f8 257 current = CFRetain(CFSTR(""));
0fae82ee 258 currentMatched = -2; /* not defined */
5958d7c0
A
259 }
260
261 nSets = CFDictionaryGetCount(sets);
a5f60add
A
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 }
5958d7c0
A
267
268 /* check for set with matching name */
009ee3c6 269 for (i = 0; i < nSets; i++) {
5958d7c0
A
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);
dbf6a266 279 if (newSetUDN != NULL) CFRetain(newSetUDN);
5958d7c0
A
280 goto found;
281 }
282 }
283
284 /* check for set with matching user-defined name */
009ee3c6 285 for (i = 0; i < nSets; i++) {
5958d7c0
A
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);
5958d7c0
A
294 goto found;
295 }
296 }
297
009ee3c6 298 if (argc == 1) {
6bb65964
A
299 SCPrint(TRUE, stderr, CFSTR("Set \"%@\" not available.\n"), newSet);
300 exit(1);
5958d7c0
A
301 }
302
6bb65964 303 SCPrint(TRUE, stdout,
0fae82ee
A
304 CFSTR("Defined sets include:%s\n"),
305 (currentMatched > 0) ? " (* == current set)" : "");
5958d7c0 306
009ee3c6 307 for (i = 0; i < nSets; i++) {
5958d7c0
A
308 CFStringRef key = (CFStringRef) setKeys[i];
309 CFDictionaryRef dict = (CFDictionaryRef)setVals[i];
310 CFStringRef udn = CFDictionaryGetValue(dict, kSCPropUserDefinedName);
311
6bb65964 312 SCPrint(TRUE, stdout,
0fae82ee 313 CFSTR(" %s %@\t(%@)\n"),
5958d7c0
A
314 ((currentMatched > 0) && CFEqual(key, current)) ? "*" : " ",
315 key,
316 udn ? udn : CFSTR(""));
317 }
318
319 switch (currentMatched) {
320 case -2 :
6bb65964 321 SCPrint(TRUE, stdout, CFSTR("\nCurrent set not defined.\n"));
5958d7c0
A
322 break;
323 case -1 :
6bb65964 324 SCPrint(TRUE, stdout, CFSTR("\nCurrent set \"%@\" may not be valid\n"), current);
5958d7c0
A
325 break;
326 case 0 :
6bb65964 327 SCPrint(TRUE, stdout, CFSTR("\nCurrent set \"%@\" not valid\n"), current);
5958d7c0
A
328 break;
329 default :
330 break;
331 }
332
6bb65964 333 exit (0);
5958d7c0
A
334
335 found :
336
4c5e92e2 337 if (!(isAdmin() || hasLocalConsoleAccess())) {
dbf6a266
A
338 SCPrint(TRUE, stderr,
339 CFSTR("Only local console users and administrators can change locations\n"));
340 exit (EX_NOPERM);
4c5e92e2 341 }
dbf6a266 342
a40a14f8
A
343 CFRelease(current);
344 current = CFStringCreateWithFormat(NULL, NULL, CFSTR("%@%@"), prefix, newSet);
345
dbf6a266 346 if (!SCPreferencesSetValue(prefs, kSCPrefCurrentSet, current)) {
0fae82ee
A
347 SCPrint(TRUE, stderr,
348 CFSTR("SCPreferencesSetValue(...,%@,%@) failed\n"),
5958d7c0 349 kSCPrefCurrentSet,
0fae82ee 350 current);
5958d7c0
A
351 exit (1);
352 }
353
dbf6a266 354 if (!SCPreferencesCommitChanges(prefs)) {
0fae82ee 355 SCPrint(TRUE, stderr, CFSTR("SCPreferencesCommitChanges() failed\n"));
5958d7c0
A
356 exit (1);
357 }
358
359 if (apply) {
dbf6a266 360 if (!SCPreferencesApplyChanges(prefs)) {
0fae82ee 361 SCPrint(TRUE, stderr, CFSTR("SCPreferencesApplyChanges() failed\n"));
5958d7c0
A
362 exit (1);
363 }
364 }
365
0fae82ee
A
366 SCPrint(TRUE, stdout,
367 CFSTR("%@ updated to %@ (%@)\n"),
5958d7c0
A
368 kSCPrefCurrentSet,
369 newSet,
370 newSetUDN ? newSetUDN : CFSTR(""));
371
a40a14f8
A
372 CFRelease(current);
373 CFRelease(newSet);
374 if (newSetUDN != NULL) CFRelease(newSetUDN);
375 CFRelease(prefix);
376 CFRelease(prefs);
377
5958d7c0
A
378 exit (0);
379 return 0;
380}