#include <getopt.h>
#include <unistd.h>
#include <sysexits.h>
#include <sys/param.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <dlfcn.h>
#include <grp.h>
#include <SystemConfiguration/SystemConfiguration.h>
#include <SystemConfiguration/SCPrivate.h>
#if !TARGET_OS_IPHONE
#include <Security/AuthSession.h>
#endif
static Boolean apply = TRUE;
static const struct option longopts[] = {
{ "help", no_argument, 0, '?' },
{ 0, 0, 0, 0 }
};
static void
usage(const char *command)
{
SCPrint(TRUE, stderr, CFSTR("usage: %s [-n] new-set-name\n"), command);
exit (EX_USAGE);
}
static Boolean
isAdmin()
{
gid_t groups[NGROUPS_MAX];
int ngroups;
if (getuid() == 0) {
return TRUE; }
ngroups = getgroups(NGROUPS_MAX, groups);
if(ngroups > 0) {
struct group *adminGroup;
adminGroup = getgrnam("admin");
if (adminGroup != NULL) {
gid_t adminGid = adminGroup->gr_gid;
int i;
for (i = 0; i < ngroups; i++) {
if (groups[i] == adminGid) {
return TRUE; }
}
}
}
return FALSE;
}
#if !TARGET_OS_IPHONE
static void *
__loadSecurity(void) {
static void *image = NULL;
if (NULL == image) {
const char *framework = "/System/Library/Frameworks/Security.framework/Security";
struct stat statbuf;
const char *suffix = getenv("DYLD_IMAGE_SUFFIX");
char path[MAXPATHLEN];
strlcpy(path, framework, sizeof(path));
if (suffix) strlcat(path, suffix, sizeof(path));
if (0 <= stat(path, &statbuf)) {
image = dlopen(path, RTLD_LAZY | RTLD_LOCAL);
} else {
image = dlopen(framework, RTLD_LAZY | RTLD_LOCAL);
}
}
return (void *)image;
}
static OSStatus
_SessionGetInfo(SecuritySessionId session, SecuritySessionId *sessionId, SessionAttributeBits *attributes)
{
#undef SessionGetInfo
static typeof (SessionGetInfo) *dyfunc = NULL;
if (!dyfunc) {
void *image = __loadSecurity();
if (image) dyfunc = dlsym(image, "SessionGetInfo");
}
return dyfunc ? dyfunc(session, sessionId, attributes) : -1;
}
#define SessionGetInfo _SessionGetInfo
#endif
static Boolean
hasLocalConsoleAccess()
{
#if !TARGET_OS_IPHONE
OSStatus error;
SecuritySessionId sessionID = 0;
SessionAttributeBits attributeBits = 0;
error = SessionGetInfo(callerSecuritySession, &sessionID, &attributeBits);
if (error != noErr) {
return FALSE;
}
return (attributeBits & (sessionHasGraphicAccess|sessionIsRemote)) == sessionHasGraphicAccess;
#else
return TRUE;
#endif
}
int
main(int argc, char **argv)
{
const char *command = argv[0];
extern int optind;
int opt;
CFStringRef current = NULL;
int currentMatched = 0;
CFStringRef newSet = NULL;
CFStringRef newSetUDN = NULL;
CFStringRef prefix;
SCPreferencesRef prefs;
CFDictionaryRef sets;
CFIndex nSets;
const void **setKeys = NULL;
const void **setVals = NULL;
CFIndex i;
while ((opt = getopt_long(argc, argv, "dvn", longopts, NULL)) != -1) {
switch(opt) {
case 'd':
_sc_debug = TRUE;
_sc_log = FALSE;
break;
case 'v':
_sc_verbose = TRUE;
break;
case 'n':
apply = FALSE;
break;
case '?':
default :
usage(command);
}
}
argc -= optind;
argv += optind;
prefix = CFStringCreateWithFormat(NULL, NULL, CFSTR("/%@/"), kSCPrefSets);
if (argc == 1) {
newSet = CFStringCreateWithCString(NULL, argv[0], kCFStringEncodingMacRoman);
if ((CFStringGetLength(newSet) > 0) && CFStringHasPrefix(newSet, prefix)) {
CFRange range;
CFMutableStringRef str;
str = CFStringCreateMutableCopy(NULL, 0, newSet);
CFRelease(newSet);
CFStringDelete(str, CFRangeMake(0, CFStringGetLength(prefix)));
newSet = CFStringCreateCopy(NULL, newSet);
CFRelease(str);
range = CFStringFind(newSet, CFSTR("/"), 0);
if (range.location != kCFNotFound) {
SCPrint(TRUE, stderr, CFSTR("Set \"%@\" not available\n."), newSet);
exit (1);
}
}
} else {
newSet = CFRetain(CFSTR(""));
}
prefs = SCPreferencesCreate(NULL, CFSTR("Select Set Command"), NULL);
if (prefs == NULL) {
SCPrint(TRUE, stderr, CFSTR("SCPreferencesCreate() failed\n"));
exit (1);
}
sets = SCPreferencesGetValue(prefs, kSCPrefSets);
if (sets == NULL) {
SCPrint(TRUE, stderr, CFSTR("No network sets defined.\n"));
exit (1);
}
current = SCPreferencesGetValue(prefs, kSCPrefCurrentSet);
if (current != NULL) {
if (CFStringHasPrefix(current, prefix)) {
CFMutableStringRef tmp;
tmp = CFStringCreateMutableCopy(NULL, 0, current);
CFStringDelete(tmp, CFRangeMake(0, CFStringGetLength(prefix)));
current = tmp;
} else {
CFRetain(current);
currentMatched = -1;
}
} else {
current = CFRetain(CFSTR(""));
currentMatched = -2;
}
nSets = CFDictionaryGetCount(sets);
if (nSets > 0) {
setKeys = CFAllocatorAllocate(NULL, nSets * sizeof(CFStringRef), 0);
setVals = CFAllocatorAllocate(NULL, nSets * sizeof(CFDictionaryRef), 0);
CFDictionaryGetKeysAndValues(sets, setKeys, setVals);
}
for (i = 0; i < nSets; i++) {
CFStringRef key = (CFStringRef) setKeys[i];
CFDictionaryRef dict = (CFDictionaryRef)setVals[i];
if ((currentMatched >= 0) && CFEqual(key, current)) {
currentMatched++;
}
if (CFEqual(newSet, key)) {
newSetUDN = CFDictionaryGetValue(dict, kSCPropUserDefinedName);
if (newSetUDN != NULL) CFRetain(newSetUDN);
goto found;
}
}
for (i = 0; i < nSets; i++) {
CFStringRef key = (CFStringRef) setKeys[i];
CFDictionaryRef dict = (CFDictionaryRef)setVals[i];
newSetUDN = CFDictionaryGetValue(dict, kSCPropUserDefinedName);
if ((newSetUDN != NULL) && CFEqual(newSet, newSetUDN)) {
CFRelease(newSet);
newSet = CFRetain(key);
CFRetain(newSetUDN);
goto found;
}
}
if (argc == 1) {
SCPrint(TRUE, stderr, CFSTR("Set \"%@\" not available.\n"), newSet);
exit(1);
}
SCPrint(TRUE, stdout,
CFSTR("Defined sets include:%s\n"),
(currentMatched > 0) ? " (* == current set)" : "");
for (i = 0; i < nSets; i++) {
CFStringRef key = (CFStringRef) setKeys[i];
CFDictionaryRef dict = (CFDictionaryRef)setVals[i];
CFStringRef udn = CFDictionaryGetValue(dict, kSCPropUserDefinedName);
SCPrint(TRUE, stdout,
CFSTR(" %s %@\t(%@)\n"),
((currentMatched > 0) && CFEqual(key, current)) ? "*" : " ",
key,
udn ? udn : CFSTR(""));
}
switch (currentMatched) {
case -2 :
SCPrint(TRUE, stdout, CFSTR("\nCurrent set not defined.\n"));
break;
case -1 :
SCPrint(TRUE, stdout, CFSTR("\nCurrent set \"%@\" may not be valid\n"), current);
break;
case 0 :
SCPrint(TRUE, stdout, CFSTR("\nCurrent set \"%@\" not valid\n"), current);
break;
default :
break;
}
exit (0);
found :
if (!(isAdmin() || hasLocalConsoleAccess())) {
SCPrint(TRUE, stderr,
CFSTR("Only local console users and administrators can change locations\n"));
exit (EX_NOPERM);
}
CFRelease(current);
current = CFStringCreateWithFormat(NULL, NULL, CFSTR("%@%@"), prefix, newSet);
if (!SCPreferencesSetValue(prefs, kSCPrefCurrentSet, current)) {
SCPrint(TRUE, stderr,
CFSTR("SCPreferencesSetValue(...,%@,%@) failed\n"),
kSCPrefCurrentSet,
current);
exit (1);
}
if (!SCPreferencesCommitChanges(prefs)) {
SCPrint(TRUE, stderr, CFSTR("SCPreferencesCommitChanges() failed\n"));
exit (1);
}
if (apply) {
if (!SCPreferencesApplyChanges(prefs)) {
SCPrint(TRUE, stderr, CFSTR("SCPreferencesApplyChanges() failed\n"));
exit (1);
}
}
SCPrint(TRUE, stdout,
CFSTR("%@ updated to %@ (%@)\n"),
kSCPrefCurrentSet,
newSet,
newSetUDN ? newSetUDN : CFSTR(""));
CFRelease(current);
CFRelease(newSet);
if (newSetUDN != NULL) CFRelease(newSetUDN);
CFRelease(prefix);
CFRelease(prefs);
exit (0);
return 0;
}