| 1 | /* |
| 2 | * Copyright (c) 2000-2009, 2011, 2012, 2014, 2015 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/Authorization.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 | int |
| 77 | main(int argc, char **argv) |
| 78 | { |
| 79 | const char *command = argv[0]; |
| 80 | extern int optind; |
| 81 | int opt; |
| 82 | CFStringRef current = NULL; |
| 83 | int currentMatched = 0; |
| 84 | CFStringRef newSet = NULL; /* set key */ |
| 85 | CFStringRef newSetUDN = NULL; /* user defined name */ |
| 86 | CFStringRef prefix; |
| 87 | SCPreferencesRef prefs; |
| 88 | CFDictionaryRef sets; |
| 89 | CFIndex nSets; |
| 90 | const void **setKeys = NULL; |
| 91 | const void **setVals = NULL; |
| 92 | CFIndex i; |
| 93 | |
| 94 | #if !TARGET_OS_IPHONE |
| 95 | AuthorizationRef authorization = NULL; |
| 96 | AuthorizationFlags flags = kAuthorizationFlagDefaults; |
| 97 | CFMutableDictionaryRef options; |
| 98 | OSStatus status; |
| 99 | #endif // !TARGET_OS_IPHONE |
| 100 | |
| 101 | /* process any arguments */ |
| 102 | |
| 103 | while ((opt = getopt_long(argc, argv, "dvn", longopts, NULL)) != -1) { |
| 104 | switch(opt) { |
| 105 | case 'd': |
| 106 | _sc_debug = TRUE; |
| 107 | _sc_log = FALSE; /* enable framework logging */ |
| 108 | break; |
| 109 | case 'v': |
| 110 | _sc_verbose = TRUE; |
| 111 | break; |
| 112 | case 'n': |
| 113 | apply = FALSE; |
| 114 | break; |
| 115 | case '?': |
| 116 | default : |
| 117 | usage(command); |
| 118 | } |
| 119 | } |
| 120 | argc -= optind; |
| 121 | argv += optind; |
| 122 | |
| 123 | prefix = CFStringCreateWithFormat(NULL, NULL, CFSTR("/%@/"), kSCPrefSets); |
| 124 | |
| 125 | if (argc == 1) { |
| 126 | newSet = CFStringCreateWithCString(NULL, argv[0], kCFStringEncodingMacRoman); |
| 127 | |
| 128 | /* check if a full path to the new "set" was specified */ |
| 129 | if ((CFStringGetLength(newSet) > 0) && CFStringHasPrefix(newSet, prefix)) { |
| 130 | CFRange range; |
| 131 | CFMutableStringRef str; |
| 132 | |
| 133 | str = CFStringCreateMutableCopy(NULL, 0, newSet); |
| 134 | CFRelease(newSet); |
| 135 | |
| 136 | CFStringDelete(str, CFRangeMake(0, CFStringGetLength(prefix))); |
| 137 | newSet = CFStringCreateCopy(NULL, newSet); |
| 138 | CFRelease(str); |
| 139 | |
| 140 | range = CFStringFind(newSet, CFSTR("/"), 0); |
| 141 | if (range.location != kCFNotFound) { |
| 142 | SCPrint(TRUE, stderr, CFSTR("Set \"%@\" not available\n"), newSet); |
| 143 | exit (1); |
| 144 | } |
| 145 | } |
| 146 | } else { |
| 147 | newSet = CFRetain(CFSTR("")); |
| 148 | } |
| 149 | |
| 150 | #if !TARGET_OS_IPHONE |
| 151 | status = AuthorizationCreate(NULL, |
| 152 | kAuthorizationEmptyEnvironment, |
| 153 | flags, |
| 154 | &authorization); |
| 155 | if (status != errAuthorizationSuccess) { |
| 156 | SCPrint(TRUE, |
| 157 | stderr, |
| 158 | CFSTR("AuthorizationCreate() failed: status = %d\n"), |
| 159 | (int)status); |
| 160 | exit (1); |
| 161 | } |
| 162 | |
| 163 | options = CFDictionaryCreateMutable(NULL, |
| 164 | 0, |
| 165 | &kCFTypeDictionaryKeyCallBacks, |
| 166 | &kCFTypeDictionaryValueCallBacks); |
| 167 | CFDictionarySetValue(options, kSCPreferencesOptionChangeNetworkSet, kCFBooleanTrue); |
| 168 | prefs = SCPreferencesCreateWithOptions(NULL, CFSTR("scselect"), NULL, authorization, options); |
| 169 | CFRelease(options); |
| 170 | if (prefs == NULL) { |
| 171 | SCPrint(TRUE, stderr, CFSTR("SCPreferencesCreate() failed\n")); |
| 172 | exit (1); |
| 173 | } |
| 174 | #else // !TARGET_OS_IPHONE |
| 175 | prefs = SCPreferencesCreate(NULL, CFSTR("scselect"), NULL); |
| 176 | if (prefs == NULL) { |
| 177 | SCPrint(TRUE, stderr, CFSTR("SCPreferencesCreate() failed\n")); |
| 178 | exit (1); |
| 179 | } |
| 180 | #endif // !TARGET_OS_IPHONE |
| 181 | |
| 182 | sets = SCPreferencesGetValue(prefs, kSCPrefSets); |
| 183 | if (sets == NULL) { |
| 184 | SCPrint(TRUE, stderr, CFSTR("No network sets defined.\n")); |
| 185 | exit (1); |
| 186 | } |
| 187 | |
| 188 | current = SCPreferencesGetValue(prefs, kSCPrefCurrentSet); |
| 189 | if (current != NULL) { |
| 190 | if (CFStringHasPrefix(current, prefix)) { |
| 191 | CFMutableStringRef tmp; |
| 192 | |
| 193 | tmp = CFStringCreateMutableCopy(NULL, 0, current); |
| 194 | CFStringDelete(tmp, CFRangeMake(0, CFStringGetLength(prefix))); |
| 195 | current = tmp; |
| 196 | } else { |
| 197 | CFRetain(current); |
| 198 | currentMatched = -1; /* not prefixed */ |
| 199 | } |
| 200 | } else { |
| 201 | current = CFRetain(CFSTR("")); |
| 202 | currentMatched = -2; /* not defined */ |
| 203 | } |
| 204 | |
| 205 | nSets = CFDictionaryGetCount(sets); |
| 206 | if (nSets > 0) { |
| 207 | setKeys = CFAllocatorAllocate(NULL, nSets * sizeof(CFStringRef), 0); |
| 208 | setVals = CFAllocatorAllocate(NULL, nSets * sizeof(CFDictionaryRef), 0); |
| 209 | CFDictionaryGetKeysAndValues(sets, setKeys, setVals); |
| 210 | } |
| 211 | |
| 212 | /* check for set with matching name */ |
| 213 | for (i = 0; i < nSets; i++) { |
| 214 | CFStringRef key = (CFStringRef) setKeys[i]; |
| 215 | CFDictionaryRef dict = (CFDictionaryRef)setVals[i]; |
| 216 | |
| 217 | if ((currentMatched >= 0) && CFEqual(key, current)) { |
| 218 | currentMatched++; |
| 219 | } |
| 220 | |
| 221 | if (CFEqual(newSet, key)) { |
| 222 | newSetUDN = CFDictionaryGetValue(dict, kSCPropUserDefinedName); |
| 223 | if (newSetUDN != NULL) CFRetain(newSetUDN); |
| 224 | goto found; |
| 225 | } |
| 226 | } |
| 227 | |
| 228 | /* check for set with matching user-defined name */ |
| 229 | for (i = 0; i < nSets; i++) { |
| 230 | CFStringRef key = (CFStringRef) setKeys[i]; |
| 231 | CFDictionaryRef dict = (CFDictionaryRef)setVals[i]; |
| 232 | |
| 233 | newSetUDN = CFDictionaryGetValue(dict, kSCPropUserDefinedName); |
| 234 | if ((newSetUDN != NULL) && CFEqual(newSet, newSetUDN)) { |
| 235 | CFRelease(newSet); |
| 236 | newSet = CFRetain(key); |
| 237 | CFRetain(newSetUDN); |
| 238 | goto found; |
| 239 | } |
| 240 | } |
| 241 | |
| 242 | if (argc == 1) { |
| 243 | SCPrint(TRUE, stderr, CFSTR("Set \"%@\" not available.\n"), newSet); |
| 244 | exit(1); |
| 245 | } |
| 246 | |
| 247 | SCPrint(TRUE, stdout, |
| 248 | CFSTR("Defined sets include:%s\n"), |
| 249 | (currentMatched > 0) ? " (* == current set)" : ""); |
| 250 | |
| 251 | for (i = 0; i < nSets; i++) { |
| 252 | CFStringRef key = (CFStringRef) setKeys[i]; |
| 253 | CFDictionaryRef dict = (CFDictionaryRef)setVals[i]; |
| 254 | CFStringRef udn = CFDictionaryGetValue(dict, kSCPropUserDefinedName); |
| 255 | |
| 256 | SCPrint(TRUE, stdout, |
| 257 | CFSTR(" %s %@\t(%@)\n"), |
| 258 | ((currentMatched > 0) && CFEqual(key, current)) ? "*" : " ", |
| 259 | key, |
| 260 | udn ? udn : CFSTR("")); |
| 261 | } |
| 262 | |
| 263 | switch (currentMatched) { |
| 264 | case -2 : |
| 265 | SCPrint(TRUE, stdout, CFSTR("\nCurrent set not defined.\n")); |
| 266 | break; |
| 267 | case -1 : |
| 268 | SCPrint(TRUE, stdout, CFSTR("\nCurrent set \"%@\" may not be valid\n"), current); |
| 269 | break; |
| 270 | case 0 : |
| 271 | SCPrint(TRUE, stdout, CFSTR("\nCurrent set \"%@\" not valid\n"), current); |
| 272 | break; |
| 273 | default : |
| 274 | break; |
| 275 | } |
| 276 | |
| 277 | CFRelease(prefix); |
| 278 | exit (0); |
| 279 | |
| 280 | found : |
| 281 | |
| 282 | CFRelease(current); |
| 283 | current = CFStringCreateWithFormat(NULL, NULL, CFSTR("%@%@"), prefix, newSet); |
| 284 | |
| 285 | SCPreferencesSetValue(prefs, kSCPrefCurrentSet, current); |
| 286 | |
| 287 | if (!SCPreferencesCommitChanges(prefs)) { |
| 288 | int sc_status = SCError(); |
| 289 | |
| 290 | if (sc_status == kSCStatusAccessError) { |
| 291 | SCPrint(TRUE, stderr, |
| 292 | CFSTR("Only local console users and administrators can change locations\n")); |
| 293 | exit (EX_NOPERM); |
| 294 | } else { |
| 295 | SCPrint(TRUE, stderr, |
| 296 | CFSTR("SCPreferencesCommitChanges() failed: %s\n"), |
| 297 | SCErrorString(sc_status)); |
| 298 | exit (1); |
| 299 | } |
| 300 | } |
| 301 | |
| 302 | if (apply) { |
| 303 | if (!SCPreferencesApplyChanges(prefs)) { |
| 304 | SCPrint(TRUE, stderr, |
| 305 | CFSTR("SCPreferencesApplyChanges() failed %s\n"), |
| 306 | SCErrorString(SCError())); |
| 307 | exit (1); |
| 308 | } |
| 309 | } |
| 310 | |
| 311 | SCPrint(TRUE, stdout, |
| 312 | CFSTR("%@ updated to %@ (%@)\n"), |
| 313 | kSCPrefCurrentSet, |
| 314 | newSet, |
| 315 | newSetUDN ? newSetUDN : CFSTR("")); |
| 316 | |
| 317 | CFRelease(current); |
| 318 | CFRelease(newSet); |
| 319 | if (newSetUDN != NULL) CFRelease(newSetUDN); |
| 320 | CFRelease(prefix); |
| 321 | CFRelease(prefs); |
| 322 | |
| 323 | #if !TARGET_OS_IPHONE |
| 324 | AuthorizationFree(authorization, kAuthorizationFlagDefaults); |
| 325 | // AuthorizationFree(authorization, kAuthorizationFlagDestroyRights); |
| 326 | #endif /* !TARGET_OS_IPHONE */ |
| 327 | |
| 328 | exit (0); |
| 329 | return 0; |
| 330 | } |