X-Git-Url: https://git.saurik.com/apple/configd.git/blobdiff_plain/791b08356c62be042d56dd05c1cc0ace4b068c53..edebe297f772e4cdd76278ebb777820466d2917b:/SystemConfiguration.fproj/SCNetworkConnectionPrivate.c diff --git a/SystemConfiguration.fproj/SCNetworkConnectionPrivate.c b/SystemConfiguration.fproj/SCNetworkConnectionPrivate.c new file mode 100644 index 0000000..56c444f --- /dev/null +++ b/SystemConfiguration.fproj/SCNetworkConnectionPrivate.c @@ -0,0 +1,1758 @@ +/* + * Copyright (c) 2006, 2007 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#include +#include +#include +#include // for SCLog +#include "SCNetworkConfigurationInternal.h" +#include +#include +#include + + +#pragma mark - +#pragma mark SCUserPreferences + + +typedef struct { + + // base CFType information + CFRuntimeBase cfBase; + + // serviceID + CFStringRef serviceID; + + // user preferences [unique] id + CFStringRef prefsID; + +} SCUserPreferencesPrivate, *SCUserPreferencesPrivateRef; + + +static CFStringRef __SCUserPreferencesCopyDescription (CFTypeRef cf); +static void __SCUserPreferencesDeallocate (CFTypeRef cf); +static Boolean __SCUserPreferencesEqual (CFTypeRef cf1, CFTypeRef cf2); +static CFHashCode __SCUserPreferencesHash (CFTypeRef cf); + + +static CFTypeID __kSCUserPreferencesTypeID = _kCFRuntimeNotATypeID; + + +static const CFRuntimeClass __SCUserPreferencesClass = { + 0, // version + "SCUserPreferences", // className + NULL, // init + NULL, // copy + __SCUserPreferencesDeallocate, // dealloc + __SCUserPreferencesEqual, // equal + __SCUserPreferencesHash, // hash + NULL, // copyFormattingDesc + __SCUserPreferencesCopyDescription // copyDebugDesc +}; + + +static pthread_once_t initialized = PTHREAD_ONCE_INIT; + + +static CFStringRef +__SCUserPreferencesCopyDescription(CFTypeRef cf) +{ + CFAllocatorRef allocator = CFGetAllocator(cf); + CFMutableStringRef result; + SCUserPreferencesPrivateRef prefsPrivate = (SCUserPreferencesPrivateRef)cf; + + result = CFStringCreateMutable(allocator, 0); + CFStringAppendFormat(result, NULL, CFSTR(" {"), cf, allocator); + CFStringAppendFormat(result, NULL, CFSTR("service = %@"), prefsPrivate->serviceID); + CFStringAppendFormat(result, NULL, CFSTR(", id = %@"), prefsPrivate->prefsID); + CFStringAppendFormat(result, NULL, CFSTR("}")); + + return result; +} + + +static void +__SCUserPreferencesDeallocate(CFTypeRef cf) +{ + SCUserPreferencesPrivateRef prefsPrivate = (SCUserPreferencesPrivateRef)cf; + + /* release resources */ + + CFRelease(prefsPrivate->prefsID); + CFRelease(prefsPrivate->serviceID); + + return; +} + + +static Boolean +__SCUserPreferencesEqual(CFTypeRef cf1, CFTypeRef cf2) +{ + SCUserPreferencesPrivateRef s1 = (SCUserPreferencesPrivateRef)cf1; + SCUserPreferencesPrivateRef s2 = (SCUserPreferencesPrivateRef)cf2; + + if (s1 == s2) + return TRUE; + + if (!CFEqual(s1->prefsID, s2->prefsID)) + return FALSE; // if not the same [unique] prefs identifier + + return TRUE; +} + + +static CFHashCode +__SCUserPreferencesHash(CFTypeRef cf) +{ + SCUserPreferencesPrivateRef prefsPrivate = (SCUserPreferencesPrivateRef)cf; + + return CFHash(prefsPrivate->prefsID); +} + + +static void +__SCUserPreferencesInitialize(void) +{ + __kSCUserPreferencesTypeID = _CFRuntimeRegisterClass(&__SCUserPreferencesClass); + return; +} + + +static SCUserPreferencesPrivateRef +__SCUserPreferencesCreatePrivate(CFAllocatorRef allocator, + CFStringRef serviceID, + CFStringRef prefsID) +{ + SCUserPreferencesPrivateRef prefsPrivate; + uint32_t size; + + /* initialize runtime */ + pthread_once(&initialized, __SCUserPreferencesInitialize); + + /* allocate target */ + size = sizeof(SCUserPreferencesPrivate) - sizeof(CFRuntimeBase); + prefsPrivate = (SCUserPreferencesPrivateRef)_CFRuntimeCreateInstance(allocator, + __kSCUserPreferencesTypeID, + size, + NULL); + if (prefsPrivate == NULL) { + return NULL; + } + + prefsPrivate->serviceID = CFStringCreateCopy(NULL, serviceID); + prefsPrivate->prefsID = CFStringCreateCopy(NULL, prefsID); + + return prefsPrivate; +} + + +static __inline__ CFTypeRef +isA_SCUserPreferences(CFTypeRef obj) +{ + return (isA_CFType(obj, SCUserPreferencesGetTypeID())); +} + + +#pragma mark - +#pragma mark SCUserPreferences SPIs + + +#define USER_PREFERENCES_NOTIFICATION "com.apple.networkConnect" +#define USER_PREFERENCES_APPLICATION_ID CFSTR("com.apple.networkConnect") +#define USER_PREFERENCES_ID CFSTR("UniqueIdentifier") +#define USER_PREFERENCES_DEFAULT CFSTR("ConnectByDefault") + + +static CFArrayRef +copyCFPreferencesForServiceID(CFStringRef serviceID) +{ + CFArrayRef prefs; + + // fetch "Managed" or "ByHost" user preferences + (void) CFPreferencesAppSynchronize(USER_PREFERENCES_APPLICATION_ID); + prefs = CFPreferencesCopyAppValue(serviceID, + USER_PREFERENCES_APPLICATION_ID); + + if ((prefs != NULL) && !isA_CFArray(prefs)) { + CFRelease(prefs); + return NULL; + } + + return prefs; +} + + +static Boolean +setCFPreferencesForServiceID(CFStringRef serviceID, CFArrayRef newPreferences) +{ + Boolean ok; + + if (CFPreferencesAppValueIsForced(serviceID, USER_PREFERENCES_APPLICATION_ID)) { + return FALSE; + } + + CFPreferencesSetValue(serviceID, + newPreferences, + USER_PREFERENCES_APPLICATION_ID, + kCFPreferencesCurrentUser, + kCFPreferencesCurrentHost); + ok = CFPreferencesSynchronize(USER_PREFERENCES_APPLICATION_ID, + kCFPreferencesCurrentUser, + kCFPreferencesCurrentHost); + + (void) notify_post(USER_PREFERENCES_NOTIFICATION); + + return ok; +} + + +static void +addPreference(CFMutableArrayRef *newPrefs, CFDictionaryRef newDict) +{ + if (*newPrefs == NULL) { + *newPrefs = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); + } + CFArrayAppendValue(*newPrefs, newDict); + + return; +} + + +typedef CFDictionaryRef (*processPreferencesCallout) (CFStringRef serviceID, + CFDictionaryRef current, + void *context1, + void *context2, + void *context3); + + +static Boolean +processPreferences(CFStringRef serviceID, + processPreferencesCallout callout, + void *context1, + void *context2, + void *context3) +{ + Boolean changed = FALSE; + CFIndex i; + CFIndex n; + CFDictionaryRef newDict = NULL; + CFMutableArrayRef newPrefs = NULL; + Boolean ok = TRUE; + CFArrayRef prefs; + + prefs = copyCFPreferencesForServiceID(serviceID); + n = (prefs != NULL) ? CFArrayGetCount(prefs) : 0; + for (i = 0; i < n; i++) { + CFDictionaryRef dict; + + dict = CFArrayGetValueAtIndex(prefs, i); + if (isA_CFDictionary(dict)) { + newDict = (*callout)(serviceID, dict, context1, context2, context3); + if (newDict == NULL) { + // if entry to be removed + changed = TRUE; + continue; + } + } else { + // if not a CFDictionary, leave as-is + newDict = CFRetain(dict); + } + + if (!CFEqual(dict, newDict)) { + changed = TRUE; + } + + addPreference(&newPrefs, newDict); + CFRelease(newDict); + } + if (prefs != NULL) CFRelease(prefs); + + newDict = (*callout)(serviceID, NULL, context1, context2, context3); + if (newDict != NULL) { + // if new entry + changed = TRUE; + addPreference(&newPrefs, newDict); + CFRelease(newDict); + } + + if (changed) { + ok = setCFPreferencesForServiceID(serviceID, newPrefs); + } + if (newPrefs != NULL) CFRelease(newPrefs); + + return ok; +} + + +static __inline__ Boolean +isMatchingPrefsID(CFDictionaryRef dict, CFStringRef matchID) +{ + CFStringRef prefsID; + + prefsID = CFDictionaryGetValue(dict, USER_PREFERENCES_ID); + if (isA_CFString(prefsID)) { + if (CFEqual(prefsID, matchID)) { + return TRUE; + } + } + + return FALSE; +} + + +CFTypeID +SCUserPreferencesGetTypeID(void) +{ + pthread_once(&initialized, __SCUserPreferencesInitialize); /* initialize runtime */ + return __kSCUserPreferencesTypeID; +} + + +CFStringRef +SCUserPreferencesGetUniqueID(SCUserPreferencesRef userPreferences) +{ + SCUserPreferencesPrivateRef userPrivate = (SCUserPreferencesPrivateRef)userPreferences; + + if (!isA_SCUserPreferences(userPreferences)) { + _SCErrorSet(kSCStatusInvalidArgument); + return FALSE; + } + + return userPrivate->prefsID; +} + + +Boolean +SCUserPreferencesIsForced(SCUserPreferencesRef userPreferences) +{ + SCUserPreferencesPrivateRef userPrivate = (SCUserPreferencesPrivateRef)userPreferences; + + if (!isA_SCUserPreferences(userPreferences)) { + _SCErrorSet(kSCStatusInvalidArgument); + return FALSE; + } + + return CFPreferencesAppValueIsForced(userPrivate->serviceID, USER_PREFERENCES_APPLICATION_ID); +} + + +static CFDictionaryRef +removeCallout(CFStringRef serviceID, + CFDictionaryRef current, + void *context1, + void *context2, + void *context3) +{ + CFStringRef matchID = (CFStringRef)context1; + + if (current == NULL) { + // we have nothing to "add" + return NULL; + } + + if (isMatchingPrefsID(current, matchID)) { + // if we match, don't add (i.e. remove) + return NULL; + } + + return CFRetain(current); +} + + +Boolean +SCUserPreferencesRemove(SCUserPreferencesRef userPreferences) +{ + SCUserPreferencesPrivateRef userPrivate = (SCUserPreferencesPrivateRef)userPreferences; + + if (!isA_SCUserPreferences(userPreferences)) { + _SCErrorSet(kSCStatusInvalidArgument); + return FALSE; + } + + return processPreferences(userPrivate->serviceID, + removeCallout, + (void *)userPrivate->prefsID, + NULL, + NULL); +} + + +static CFDictionaryRef +setCurrentCallout(CFStringRef serviceID, + CFDictionaryRef current, + void *context1, + void *context2, + void *context3) +{ + CFStringRef matchID = (CFStringRef)context1; + CFMutableDictionaryRef newDict; + + if (current == NULL) { + // we have nothing to "add" + return NULL; + } + + newDict = CFDictionaryCreateMutableCopy(NULL, 0, current); + + // remove "default" flag + CFDictionaryRemoveValue(newDict, USER_PREFERENCES_DEFAULT); + + if (isMatchingPrefsID(current, matchID)) { + // if we match, set "default" flag + CFDictionarySetValue(newDict, USER_PREFERENCES_DEFAULT, kCFBooleanTrue); + } + + return newDict; +} + + +Boolean +SCUserPreferencesSetCurrent(SCUserPreferencesRef userPreferences) +{ + SCUserPreferencesPrivateRef userPrivate = (SCUserPreferencesPrivateRef)userPreferences; + + if (!isA_SCUserPreferences(userPreferences)) { + _SCErrorSet(kSCStatusInvalidArgument); + return FALSE; + } + + return processPreferences(userPrivate->serviceID, + setCurrentCallout, + (void *)userPrivate->prefsID, + NULL, + NULL); +} + + +static CFDictionaryRef +copyNameCallout(CFStringRef serviceID, + CFDictionaryRef current, + void *context1, + void *context2, + void *context3) +{ + CFStringRef matchID = (CFStringRef)context1; + CFStringRef *name = (CFStringRef *)context3; + + if (current == NULL) { + // we have nothing to "add" + return NULL; + } + + if (isMatchingPrefsID(current, matchID)) { + *name = CFDictionaryGetValue(current, kSCPropUserDefinedName); + + // for backwards compatibility, we also check for the name in the PPP entity + if (*name == NULL) { + CFDictionaryRef ppp; + + ppp = CFDictionaryGetValue(current, kSCEntNetPPP); + if (isA_CFDictionary(ppp)) { + *name = CFDictionaryGetValue(ppp, kSCPropUserDefinedName); + } + } + + *name = isA_CFString(*name); + if (*name != NULL) { + CFRetain(*name); + } + } + + return CFRetain(current); +} + + +CFStringRef +SCUserPreferencesCopyName(SCUserPreferencesRef userPreferences) +{ + CFStringRef name = NULL; + Boolean ok; + SCUserPreferencesPrivateRef userPrivate = (SCUserPreferencesPrivateRef)userPreferences; + + if (!isA_SCUserPreferences(userPreferences)) { + _SCErrorSet(kSCStatusInvalidArgument); + return FALSE; + } + + // find SCUserPreferences and copy name + ok = processPreferences(userPrivate->serviceID, + copyNameCallout, + (void *)userPrivate->prefsID, + NULL, + (void *)&name); + if (!ok) { + if (name != NULL) { + CFRelease(name); + name = NULL; + } + } + + return name; +} + + +static CFDictionaryRef +setNameCallout(CFStringRef serviceID, + CFDictionaryRef current, + void *context1, + void *context2, + void *context3) +{ + CFStringRef matchID = (CFStringRef)context1; + CFMutableDictionaryRef newDict; + CFStringRef newName = (CFStringRef)context2; + + if (current == NULL) { + // we have nothing to "add" + return NULL; + } + + newDict = CFDictionaryCreateMutableCopy(NULL, 0, current); + + if (isMatchingPrefsID(current, matchID)) { + CFDictionaryRef pppEntity; + + // set the name + if (newName != NULL) { + CFDictionarySetValue(newDict, kSCPropUserDefinedName, newName); + } else { + CFDictionaryRemoveValue(newDict, kSCPropUserDefinedName); + } + + // for backwards compatibility, we also set the name in the PPP entity + pppEntity = CFDictionaryGetValue(newDict, kSCEntNetPPP); + if (isA_CFDictionary(pppEntity)) { + CFMutableDictionaryRef newPPPEntity; + + newPPPEntity = CFDictionaryCreateMutableCopy(NULL, 0, pppEntity); + if (newName != NULL) { + CFDictionarySetValue(newPPPEntity, kSCPropUserDefinedName, newName); + } else { + CFDictionaryRemoveValue(newPPPEntity, kSCPropUserDefinedName); + } + CFDictionarySetValue(newDict, kSCEntNetPPP, newPPPEntity); + CFRelease(newPPPEntity); + } + } + + return newDict; +} + + +Boolean +SCUserPreferencesSetName(SCUserPreferencesRef userPreferences, CFStringRef newName) +{ + Boolean ok; + SCUserPreferencesPrivateRef userPrivate = (SCUserPreferencesPrivateRef)userPreferences; + + if (!isA_SCUserPreferences(userPreferences)) { + _SCErrorSet(kSCStatusInvalidArgument); + return FALSE; + } + + if ((newName != NULL) && !isA_CFString(newName)) { + _SCErrorSet(kSCStatusInvalidArgument); + return FALSE; + } + + // find SCUserPreferences and set name + ok = processPreferences(userPrivate->serviceID, + setNameCallout, + (void *)userPrivate->prefsID, + (void *)newName, + NULL); + + return ok; +} + + +static CFDictionaryRef +copyInterfaceConfigurationCallout(CFStringRef serviceID, + CFDictionaryRef current, + void *context1, + void *context2, + void *context3) +{ + CFDictionaryRef *dict = (CFDictionaryRef *)context3; + CFStringRef interfaceType = (CFStringRef)context2; + CFStringRef matchID = (CFStringRef)context1; + + if (current == NULL) { + // we have nothing to "add" + return NULL; + } + + if (isMatchingPrefsID(current, matchID)) { + *dict = CFDictionaryGetValue(current, interfaceType); + *dict = isA_CFDictionary(*dict); + if (*dict != NULL) { + CFRetain(*dict); + } + } + + return CFRetain(current); +} + + +CFDictionaryRef +SCUserPreferencesCopyInterfaceConfiguration(SCUserPreferencesRef userPreferences, + SCNetworkInterfaceRef interface) +{ + CFStringRef defaultType; + CFDictionaryRef entity = NULL; + Boolean ok; + SCUserPreferencesPrivateRef userPrivate = (SCUserPreferencesPrivateRef)userPreferences; + + if (!isA_SCUserPreferences(userPreferences)) { + _SCErrorSet(kSCStatusInvalidArgument); + return NULL; + } + + if (!isA_SCNetworkInterface(interface)) { + _SCErrorSet(kSCStatusInvalidArgument); + return NULL; + } + + // get InterfaceType + defaultType = __SCNetworkInterfaceGetDefaultConfigurationType(interface); + if (defaultType == NULL) { + _SCErrorSet(kSCStatusInvalidArgument); + return NULL; + } + + // find SCUserPreferences and copy interface entity + ok = processPreferences(userPrivate->serviceID, + copyInterfaceConfigurationCallout, + (void *)userPrivate->prefsID, + (void *)defaultType, + (void *)&entity); + if (!ok) { + if (entity != NULL) { + CFRelease(entity); + entity = NULL; + } + } + + return entity; +} + + +static CFDictionaryRef +setInterfaceConfigurationCallout(CFStringRef serviceID, + CFDictionaryRef current, + void *context1, + void *context2, + void *context3) +{ + CFStringRef interfaceType = (CFStringRef)context2; + CFStringRef matchID = (CFStringRef)context1; + CFMutableDictionaryRef newDict; + CFDictionaryRef newOptions = (CFDictionaryRef)context3; + + if (current == NULL) { + // we have nothing to "add" + return NULL; + } + + newDict = CFDictionaryCreateMutableCopy(NULL, 0, current); + + if (isMatchingPrefsID(current, matchID)) { + if (newOptions != NULL) { + CFDictionarySetValue(newDict, interfaceType, newOptions); + + // for backwards compatibility, we want to ensure that + // the name is set in both the top level and in the PPP + // entity. + if (CFEqual(interfaceType, kSCEntNetPPP)) { + CFStringRef name; + + name = CFDictionaryGetValue(newOptions, kSCPropUserDefinedName); + if (name != NULL) { + // if name was passed in newOptions, push up + CFDictionarySetValue(newDict, kSCPropUserDefinedName, name); + } else { + name = CFDictionaryGetValue(newDict, kSCPropUserDefinedName); + if (name != NULL) { + CFMutableDictionaryRef newPPPEntity; + + // if name in parent, push into entity + newPPPEntity = CFDictionaryCreateMutableCopy(NULL, 0, newOptions); + CFDictionarySetValue(newPPPEntity, kSCPropUserDefinedName, name); + CFDictionarySetValue(newDict, interfaceType, newPPPEntity); + CFRelease(newPPPEntity); + } + } + } + } else { + CFDictionaryRemoveValue(newDict, interfaceType); + } + } + + return newDict; +} + + +Boolean +SCUserPreferencesSetInterfaceConfiguration(SCUserPreferencesRef userPreferences, + SCNetworkInterfaceRef interface, + CFDictionaryRef newOptions) +{ + CFStringRef defaultType; + Boolean ok; + SCUserPreferencesPrivateRef userPrivate = (SCUserPreferencesPrivateRef)userPreferences; + + if (!isA_SCUserPreferences(userPreferences)) { + _SCErrorSet(kSCStatusInvalidArgument); + return FALSE; + } + + if (!isA_SCNetworkInterface(interface)) { + _SCErrorSet(kSCStatusInvalidArgument); + return FALSE; + } + + // get InterfaceType + defaultType = __SCNetworkInterfaceGetDefaultConfigurationType(interface); + if (defaultType == NULL) { + _SCErrorSet(kSCStatusInvalidArgument); + return FALSE; + } + + // set new interface entity for SCUserPreferences + ok = processPreferences(userPrivate->serviceID, + setInterfaceConfigurationCallout, + (void *)userPrivate->prefsID, + (void *)defaultType, + (void *)newOptions); + + return ok; +} + + +CFDictionaryRef +SCUserPreferencesCopyExtendedInterfaceConfiguration(SCUserPreferencesRef userPreferences, + SCNetworkInterfaceRef interface, + CFStringRef extendedType) +{ + CFDictionaryRef entity = NULL; + Boolean ok; + SCUserPreferencesPrivateRef userPrivate = (SCUserPreferencesPrivateRef)userPreferences; + + if (!isA_SCUserPreferences(userPreferences)) { + _SCErrorSet(kSCStatusInvalidArgument); + return NULL; + } + + if (!isA_SCNetworkInterface(interface)) { + _SCErrorSet(kSCStatusInvalidArgument); + return NULL; + } + + if (!__SCNetworkInterfaceIsValidExtendedConfigurationType(interface, extendedType, FALSE)) { + _SCErrorSet(kSCStatusInvalidArgument); + return NULL; + } + + // find SCUserPreferences and copy interface entity + ok = processPreferences(userPrivate->serviceID, + copyInterfaceConfigurationCallout, + (void *)userPrivate->prefsID, + (void *)extendedType, + (void *)&entity); + if (!ok) { + if (entity != NULL) { + CFRelease(entity); + entity = NULL; + } + } + + return entity; +} + + +Boolean +SCUserPreferencesSetExtendedInterfaceConfiguration(SCUserPreferencesRef userPreferences, + SCNetworkInterfaceRef interface, + CFStringRef extendedType, + CFDictionaryRef newOptions) +{ + Boolean ok; + SCUserPreferencesPrivateRef userPrivate = (SCUserPreferencesPrivateRef)userPreferences; + + if (!isA_SCUserPreferences(userPreferences)) { + _SCErrorSet(kSCStatusInvalidArgument); + return FALSE; + } + + if (!isA_SCNetworkInterface(interface)) { + _SCErrorSet(kSCStatusInvalidArgument); + return FALSE; + } + + if (!__SCNetworkInterfaceIsValidExtendedConfigurationType(interface, extendedType, FALSE)) { + _SCErrorSet(kSCStatusInvalidArgument); + return FALSE; + } + + // set new interface entity for SCUserPreferences + ok = processPreferences(userPrivate->serviceID, + setInterfaceConfigurationCallout, + (void *)userPrivate->prefsID, + (void *)extendedType, + (void *)newOptions); + + return ok; +} + + +#pragma mark - +#pragma mark SCNetworkConnection + SCUserPreferences SPIs + + +static CFDictionaryRef +copyAllCallout(CFStringRef serviceID, + CFDictionaryRef current, + void *context1, + void *context2, + void *context3) +{ + CFMutableArrayRef *prefs = (CFMutableArrayRef *)context3; + CFStringRef prefsID; + SCUserPreferencesPrivateRef userPrivate; + + if (current == NULL) { + // we have nothing to "add" + return NULL; + } + + prefsID = CFDictionaryGetValue(current, USER_PREFERENCES_ID); + if (!isA_CFString(prefsID)) { + // if no unique ID + goto done; + } + + userPrivate = __SCUserPreferencesCreatePrivate(NULL, serviceID, prefsID); + if (userPrivate != NULL) { + if (*prefs == NULL) { + *prefs = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); + } + CFArrayAppendValue(*prefs, (SCUserPreferencesRef)userPrivate); + CFRelease(userPrivate); + } + + done : + + return CFRetain(current); +} + + +CFArrayRef /* of SCUserPreferencesRef's */ +SCNetworkConnectionCopyAllUserPreferences(SCNetworkConnectionRef connection) +{ + Boolean ok; + CFMutableArrayRef prefs = NULL; + CFStringRef serviceID; + + // get serviceID + serviceID = SCNetworkConnectionCopyServiceID(connection); + + // collect SCUserPreferences + ok = processPreferences(serviceID, + copyAllCallout, + NULL, + NULL, + (void *)&prefs); + if (!ok) { + if (prefs != NULL) { + CFRelease(prefs); + prefs = NULL; + } + } + + CFRelease(serviceID); + return prefs; +} + + +static CFDictionaryRef +copyCurrentCallout(CFStringRef serviceID, + CFDictionaryRef current, + void *context1, + void *context2, + void *context3) +{ + CFBooleanRef isDefault; + CFStringRef prefsID; + SCUserPreferencesPrivateRef *userPrivate = (SCUserPreferencesPrivateRef *)context3; + + if (current == NULL) { + // we have nothing to "add" + return NULL; + } + + prefsID = CFDictionaryGetValue(current, USER_PREFERENCES_ID); + if (!isA_CFString(prefsID)) { + // if no unique ID + goto done; + } + + isDefault = CFDictionaryGetValue(current, USER_PREFERENCES_DEFAULT); + if (!isA_CFBoolean(isDefault) || !CFBooleanGetValue(isDefault)) { + // if not the default configuration + goto done; + } + + *userPrivate = __SCUserPreferencesCreatePrivate(NULL, serviceID, prefsID); + + done : + + return CFRetain(current); +} + + +SCUserPreferencesRef +SCNetworkConnectionCopyCurrentUserPreferences(SCNetworkConnectionRef connection) +{ + SCUserPreferencesRef current = NULL; + Boolean ok; + CFStringRef serviceID; + + // get serviceID + serviceID = SCNetworkConnectionCopyServiceID(connection); + + // collect SCUserPreferences + ok = processPreferences(serviceID, + copyCurrentCallout, + NULL, + NULL, + (void *)¤t); + if (!ok) { + if (current != NULL) { + CFRelease(current); + current = NULL; + } + } + + CFRelease(serviceID); + return current; +} + + +static CFDictionaryRef +createCallout(CFStringRef serviceID, + CFDictionaryRef current, + void *context1, + void *context2, + void *context3) +{ + CFMutableDictionaryRef newDict; + CFStringRef newPrefsID = (CFStringRef)context1; + + if (current != NULL) { + // don't change existing entries + return CFRetain(current); + } + + newDict = CFDictionaryCreateMutable(NULL, + 0, + &kCFTypeDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks); + CFDictionarySetValue(newDict, USER_PREFERENCES_ID, newPrefsID); + return newDict; +} + + +SCUserPreferencesRef +SCNetworkConnectionCreateUserPreferences(SCNetworkConnectionRef connection) +{ + CFStringRef newPrefsID; + CFStringRef serviceID; + SCUserPreferencesPrivateRef userPrivate; + CFUUIDRef uuid; + + // get serviceID + serviceID = SCNetworkConnectionCopyServiceID(connection); + + // allocate a new user preferences ID + uuid = CFUUIDCreate(NULL); + newPrefsID = CFUUIDCreateString(NULL, uuid); + CFRelease(uuid); + + userPrivate = __SCUserPreferencesCreatePrivate(NULL, serviceID, newPrefsID); + if (userPrivate != NULL) { + (void) processPreferences(serviceID, + createCallout, + (void *)newPrefsID, + NULL, + NULL); + } + + CFRelease(newPrefsID); + CFRelease(serviceID); + return (SCUserPreferencesRef)userPrivate; +} + + +#ifdef NOTNOW +Boolean +SCNetworkConnectionSelectService(CFDictionaryRef selectionOptions, + SCNetworkServiceRef *service, + SCUserPreferencesRef *userPreferences) +{ + return FALSE; +} +#endif // NOTNOW + + +static void +update_PPP_entity(SCUserPreferencesRef userPreferences, CFDictionaryRef *userOptions) +{ + CFStringRef encryption; + CFDictionaryRef entity; + CFStringRef keychainID; + + entity = CFDictionaryGetValue(*userOptions, kSCEntNetPPP); + if (!isA_CFDictionary(entity)) { + return; + } + + encryption = CFDictionaryGetValue(entity, kSCPropNetPPPAuthPasswordEncryption); + if (encryption == NULL) { + // provide default encryption method + encryption = kSCValNetPPPAuthPasswordEncryptionKeychain; + } + + if (!isA_CFString(encryption) || + !CFEqual(encryption, kSCValNetPPPAuthPasswordEncryptionKeychain)) { + return; + } + + keychainID = CFDictionaryGetValue(entity, kSCPropNetPPPAuthPassword); + if (isA_CFString(keychainID)) { + // if password is keychain ID + } else if (isA_CFData(keychainID) && + ((CFDataGetLength((CFDataRef)keychainID) % sizeof(UniChar)) == 0)) { + // if inline password + return; + } else { + keychainID = SCUserPreferencesGetUniqueID(userPreferences); + } + + if (_SCSecKeychainPasswordItemExists(NULL, keychainID)) { + CFMutableDictionaryRef new_entity; + CFMutableDictionaryRef new_options; + + // access PPP password from system keychain + new_entity = CFDictionaryCreateMutableCopy(NULL, 0, entity); + + CFDictionarySetValue(new_entity, + kSCPropNetPPPAuthPassword, + keychainID); + CFDictionarySetValue(new_entity, + kSCPropNetPPPAuthPasswordEncryption, + kSCValNetPPPAuthPasswordEncryptionKeychain); + + new_options = CFDictionaryCreateMutableCopy(NULL, 0, *userOptions); + CFDictionarySetValue(new_options, kSCEntNetPPP, new_entity); + CFRelease(new_entity); + + CFRelease(*userOptions); + *userOptions = new_options; + } + + return; +} + + +static void +update_IPSec_entity(SCUserPreferencesRef userPreferences, CFDictionaryRef *userOptions) +{ + CFStringRef encryption; + CFDictionaryRef entity; + SecKeychainRef keychain = NULL; + CFStringRef keychainID; + CFStringRef method; + CFDataRef sharedSecret; + + entity = CFDictionaryGetValue(*userOptions, kSCEntNetIPSec); + if (!isA_CFDictionary(entity)) { + return; + } + + method = CFDictionaryGetValue(entity, kSCPropNetIPSecAuthenticationMethod); + if (!isA_CFString(method) || + !CFEqual(method, kSCValNetIPSecAuthenticationMethodSharedSecret)) { + return; + } + + encryption = CFDictionaryGetValue(entity, kSCPropNetIPSecSharedSecretEncryption); + if (encryption == NULL) { + // provide default encryption method + encryption = kSCValNetIPSecSharedSecretEncryptionKeychain; + } + + if (!isA_CFString(encryption) || + !CFEqual(encryption, kSCValNetIPSecSharedSecretEncryptionKeychain)) { + return; + } + + keychainID = CFDictionaryGetValue(entity, kSCPropNetIPSecSharedSecret); + if (isA_CFString(keychainID)) { + // if shared secret is keychain ID + CFRetain(keychainID); + } else if (isA_CFData(keychainID) && + ((CFDataGetLength((CFDataRef)keychainID) % sizeof(UniChar)) == 0)) { + // if inline shared secret + return; + } else { + CFStringRef unique_id; + + unique_id = SCUserPreferencesGetUniqueID(userPreferences); + keychainID = (CFStringRef)CFStringCreateMutableCopy(NULL, 0, unique_id); + CFStringAppend((CFMutableStringRef)keychainID, CFSTR(".SS")); + } + + sharedSecret = _SCSecKeychainPasswordItemCopy(NULL, keychainID); + if (sharedSecret != NULL) { + CFMutableDictionaryRef new_entity; + CFMutableDictionaryRef new_options; + CFStringRef password; + + // pass SharedSecret from user keychain + new_entity = CFDictionaryCreateMutableCopy(NULL, 0, entity); + + password = CFStringCreateWithBytes(NULL, + CFDataGetBytePtr(sharedSecret), + CFDataGetLength(sharedSecret), + kCFStringEncodingUTF8, + FALSE); + CFRelease(sharedSecret); + CFDictionarySetValue(new_entity, + kSCPropNetIPSecSharedSecret, + password); + CFRelease(password); + CFDictionaryRemoveValue(new_entity, + kSCPropNetIPSecSharedSecretEncryption); + + new_options = CFDictionaryCreateMutableCopy(NULL, 0, *userOptions); + CFDictionarySetValue(new_options, kSCEntNetIPSec, new_entity); + CFRelease(new_entity); + + CFRelease(*userOptions); + *userOptions = new_options; + goto done; + } + + keychain = _SCSecKeychainCopySystemKeychain(); + if (keychain == NULL) { + goto done; + } + + if (_SCSecKeychainPasswordItemExists(keychain, keychainID)) { + CFMutableDictionaryRef new_entity; + CFMutableDictionaryRef new_options; + + // access SharedSecret from system keychain + new_entity = CFDictionaryCreateMutableCopy(NULL, 0, entity); + + CFDictionarySetValue(new_entity, + kSCPropNetIPSecSharedSecret, + keychainID); + CFDictionarySetValue(new_entity, + kSCPropNetIPSecSharedSecretEncryption, + kSCValNetIPSecSharedSecretEncryptionKeychain); + + new_options = CFDictionaryCreateMutableCopy(NULL, 0, *userOptions); + CFDictionarySetValue(new_options, kSCEntNetIPSec, new_entity); + CFRelease(new_entity); + + CFRelease(*userOptions); + *userOptions = new_options; + } + + done : + + if (keychain != NULL) CFRelease(keychain); + CFRelease(keychainID); + return; +} + + +static CFDictionaryRef +copyOptionsCallout(CFStringRef serviceID, + CFDictionaryRef current, + void *context1, + void *context2, + void *context3) +{ + CFStringRef matchID = (CFStringRef)context1; + CFMutableDictionaryRef *userOptions = (CFMutableDictionaryRef *)context3; + + if (current == NULL) { + // we have nothing to "add" + return NULL; + } + + if (isMatchingPrefsID(current, matchID)) { + // if we match, return options dictionary + if (*userOptions != NULL) CFRelease(*userOptions); + *userOptions = CFDictionaryCreateMutableCopy(NULL, 0, current); + CFDictionaryRemoveValue(*userOptions, USER_PREFERENCES_ID); + CFDictionaryRemoveValue(*userOptions, USER_PREFERENCES_DEFAULT); + } + + return CFRetain(current); +} + + +Boolean +SCNetworkConnectionStartWithUserPreferences(SCNetworkConnectionRef connection, + SCUserPreferencesRef userPreferences, + Boolean linger) +{ + Boolean ok; + CFDictionaryRef userOptions = NULL; + SCUserPreferencesPrivateRef userPrivate = (SCUserPreferencesPrivateRef)userPreferences; + + if (!isA_SCUserPreferences(userPreferences)) { + _SCErrorSet(kSCStatusInvalidArgument); + return FALSE; + } + + (void) processPreferences(userPrivate->serviceID, + copyOptionsCallout, + (void *)userPrivate->prefsID, + NULL, + &userOptions); + + /* + * For some legacy preferences, some of the user options + * were missing yet handled by the APIs. Make sure that + * everything still works! + */ + if (userOptions != NULL) { + update_PPP_entity (userPreferences, &userOptions); + update_IPSec_entity(userPreferences, &userOptions); + } + + ok = SCNetworkConnectionStart(connection, userOptions, linger); + + if (userOptions != NULL) { + CFRelease(userOptions); + } + + return ok; +} + + +#pragma mark - +#pragma mark SCUserPreferences + SCNetworkInterface Password SPIs + + +static CFStringRef +getUserPasswordID(CFDictionaryRef config, SCUserPreferencesRef userPreferences) +{ + CFStringRef unique_id = NULL; + + if (config != NULL) { + CFStringRef encryption; + + encryption = CFDictionaryGetValue(config, kSCPropNetPPPAuthPasswordEncryption); + if (isA_CFString(encryption) && + CFEqual(encryption, kSCValNetPPPAuthPasswordEncryptionKeychain)) { + unique_id = CFDictionaryGetValue(config, kSCPropNetPPPAuthPassword); + } + } + if (unique_id == NULL) { + unique_id = SCUserPreferencesGetUniqueID(userPreferences); + } + + return unique_id; +} + + +static CFStringRef +copyUserSharedSecretID(CFDictionaryRef config, SCUserPreferencesRef userPreferences) +{ + CFMutableStringRef sharedSecret = NULL; + + if (config != NULL) { + CFStringRef encryption; + + encryption = CFDictionaryGetValue(config, kSCPropNetIPSecSharedSecretEncryption); + if (isA_CFString(encryption) && + CFEqual(encryption, kSCValNetIPSecSharedSecretEncryptionKeychain)) { + sharedSecret = (CFMutableStringRef)CFDictionaryGetValue(config, kSCPropNetIPSecSharedSecret); + if (sharedSecret != NULL) { + CFRetain(sharedSecret); + } + } + } + + if (sharedSecret == NULL) { + CFStringRef unique_id; + + unique_id = getUserPasswordID(config, userPreferences); + sharedSecret = CFStringCreateMutableCopy(NULL, 0, unique_id); + CFStringAppend(sharedSecret, CFSTR(".SS")); + } + + return sharedSecret; +} + + +static Boolean +checkUserPreferencesPassword(SCUserPreferencesRef userPreferences, + SCNetworkInterfaceRef interface, + SCNetworkInterfacePasswordType passwordType) +{ + if (!isA_SCUserPreferences(userPreferences)) { + _SCErrorSet(kSCStatusInvalidArgument); + return FALSE; + } + + if (!isA_SCNetworkInterface(interface)) { + _SCErrorSet(kSCStatusInvalidArgument); + return FALSE; + } + + switch (passwordType) { + case kSCNetworkInterfacePasswordTypePPP : { + CFStringRef interfaceType; + + interfaceType = SCNetworkInterfaceGetInterfaceType(interface); + if (!CFEqual(interfaceType, kSCNetworkInterfaceTypePPP)) { + _SCErrorSet(kSCStatusInvalidArgument); + return FALSE; + } + break; + } + + case kSCNetworkInterfacePasswordTypeIPSecSharedSecret : { + CFStringRef interfaceType; + + interfaceType = SCNetworkInterfaceGetInterfaceType(interface); + if (!CFEqual(interfaceType, kSCNetworkInterfaceTypePPP)) { + _SCErrorSet(kSCStatusInvalidArgument); + return FALSE; + } + + interface = SCNetworkInterfaceGetInterface(interface); + if (interface == NULL) { + _SCErrorSet(kSCStatusInvalidArgument); + return FALSE; + } + + interfaceType = SCNetworkInterfaceGetInterfaceType(interface); + if (!CFEqual(interfaceType, kSCNetworkInterfaceTypeL2TP)) { + _SCErrorSet(kSCStatusInvalidArgument); + return FALSE; + } + break; + } + + case kSCNetworkInterfacePasswordTypeEAPOL : { + _SCErrorSet(kSCStatusInvalidArgument); + return FALSE; + } + + default : + break; + } + + return TRUE; +} + + +Boolean +SCUserPreferencesCheckInterfacePassword(SCUserPreferencesRef userPreferences, + SCNetworkInterfaceRef interface, + SCNetworkInterfacePasswordType passwordType) +{ + Boolean exists = FALSE; + + if (!checkUserPreferencesPassword(userPreferences, interface, passwordType)) { + return FALSE; + } + + switch (passwordType) { + case kSCNetworkInterfacePasswordTypePPP : { + CFDictionaryRef config; + CFStringRef unique_id; + + // get configuration + config = SCUserPreferencesCopyInterfaceConfiguration(userPreferences, interface); + + // get userPreferences ID + unique_id = getUserPasswordID(config, userPreferences); + + // check + exists = __extract_password(NULL, + config, + kSCPropNetPPPAuthPassword, + kSCPropNetPPPAuthPasswordEncryption, + kSCValNetPPPAuthPasswordEncryptionKeychain, + unique_id, + NULL); + + if (config != NULL) CFRelease(config); + break; + } + + case kSCNetworkInterfacePasswordTypeIPSecSharedSecret : { + CFDictionaryRef config; + CFStringRef shared_id; + + // get configuration + config = SCUserPreferencesCopyExtendedInterfaceConfiguration(userPreferences, + interface, + kSCEntNetIPSec); + + // get sharedSecret ID + shared_id = copyUserSharedSecretID(config, userPreferences); + + // check + exists = __extract_password(NULL, + config, + kSCPropNetIPSecSharedSecret, + kSCPropNetIPSecSharedSecretEncryption, + kSCValNetIPSecSharedSecretEncryptionKeychain, + shared_id, + NULL); + + if (config != NULL) CFRelease(config); + CFRelease(shared_id); + break; + } + + default : + _SCErrorSet(kSCStatusInvalidArgument); + return FALSE; + } + + return exists; +} + + +CFDataRef +SCUserPreferencesCopyInterfacePassword(SCUserPreferencesRef userPreferences, + SCNetworkInterfaceRef interface, + SCNetworkInterfacePasswordType passwordType) +{ + CFDataRef password = NULL; + + if (!checkUserPreferencesPassword(userPreferences, interface, passwordType)) { + return FALSE; + } + + switch (passwordType) { + case kSCNetworkInterfacePasswordTypePPP : { + CFDictionaryRef config; + CFStringRef unique_id; + + // get configuration + config = SCUserPreferencesCopyInterfaceConfiguration(userPreferences, interface); + + // get userPreferences ID + unique_id = getUserPasswordID(config, userPreferences); + + // extract + (void) __extract_password(NULL, + config, + kSCPropNetPPPAuthPassword, + kSCPropNetPPPAuthPasswordEncryption, + kSCValNetPPPAuthPasswordEncryptionKeychain, + unique_id, + &password); + + if (config != NULL) CFRelease(config); + break; + } + + case kSCNetworkInterfacePasswordTypeIPSecSharedSecret : { + CFDictionaryRef config; + CFStringRef shared_id; + + // get configuration + config = SCUserPreferencesCopyExtendedInterfaceConfiguration(userPreferences, + interface, + kSCEntNetIPSec); + + // get sharedSecret ID + shared_id = copyUserSharedSecretID(config, userPreferences); + + // extract + (void) __extract_password(NULL, + config, + kSCPropNetIPSecSharedSecret, + kSCPropNetIPSecSharedSecretEncryption, + kSCValNetIPSecSharedSecretEncryptionKeychain, + shared_id, + &password); + + if (config != NULL) CFRelease(config); + CFRelease(shared_id); + break; + } + + default : + _SCErrorSet(kSCStatusInvalidArgument); + return NULL; + } + + return password; +} + + +Boolean +SCUserPreferencesRemoveInterfacePassword(SCUserPreferencesRef userPreferences, + SCNetworkInterfaceRef interface, + SCNetworkInterfacePasswordType passwordType) +{ + Boolean ok = FALSE; + + if (!checkUserPreferencesPassword(userPreferences, interface, passwordType)) { + return FALSE; + } + + switch (passwordType) { + case kSCNetworkInterfacePasswordTypePPP : { + CFDictionaryRef config; + CFStringRef unique_id; + + // get configuration + config = SCUserPreferencesCopyInterfaceConfiguration(userPreferences, interface); + + // get userPreferences ID + unique_id = getUserPasswordID(config, userPreferences); + + // remove password + ok = _SCSecKeychainPasswordItemRemove(NULL, unique_id); + if (ok) { + CFDictionaryRef config; + CFMutableDictionaryRef newConfig; + + config = SCUserPreferencesCopyInterfaceConfiguration(userPreferences, interface); + if (config != NULL) { + newConfig = CFDictionaryCreateMutableCopy(NULL, 0, config); + CFDictionaryRemoveValue(newConfig, kSCPropNetPPPAuthPassword); + CFDictionaryRemoveValue(newConfig, kSCPropNetPPPAuthPasswordEncryption); + ok = SCUserPreferencesSetInterfaceConfiguration(userPreferences, interface, newConfig); + CFRelease(newConfig); + } + } + break; + } + + case kSCNetworkInterfacePasswordTypeIPSecSharedSecret : { + CFDictionaryRef config; + CFStringRef shared_id; + + // get configuration + config = SCUserPreferencesCopyExtendedInterfaceConfiguration(userPreferences, + interface, + kSCEntNetIPSec); + + // get sharedSecret ID + shared_id = copyUserSharedSecretID(config, userPreferences); + + // remove password + ok = _SCSecKeychainPasswordItemRemove(NULL, shared_id); + if (ok) { + CFMutableDictionaryRef newConfig; + + if (config != NULL) { + newConfig = CFDictionaryCreateMutableCopy(NULL, 0, config); + CFDictionaryRemoveValue(newConfig, kSCPropNetIPSecSharedSecret); + CFDictionaryRemoveValue(newConfig, kSCPropNetIPSecSharedSecretEncryption); + ok = SCUserPreferencesSetExtendedInterfaceConfiguration(userPreferences, + interface, + kSCEntNetIPSec, + newConfig); + CFRelease(newConfig); + } + } + + if (config != NULL) CFRelease(config); + CFRelease(shared_id); + break; + } + + default : + _SCErrorSet(kSCStatusInvalidArgument); + return FALSE; + } + + return ok; +} + + +Boolean +SCUserPreferencesSetInterfacePassword(SCUserPreferencesRef userPreferences, + SCNetworkInterfaceRef interface, + SCNetworkInterfacePasswordType passwordType, + CFDataRef password, + CFDictionaryRef options) +{ + CFStringRef account = NULL; + CFBundleRef bundle; + CFDictionaryRef config; + CFStringRef description = NULL; + CFStringRef label = NULL; + Boolean ok = FALSE; + + if (!checkUserPreferencesPassword(userPreferences, interface, passwordType)) { + return FALSE; + } + + bundle = _SC_CFBundleGet(); + + switch (passwordType) { + case kSCNetworkInterfacePasswordTypePPP : { + CFStringRef unique_id; + + // get configuration + config = SCUserPreferencesCopyInterfaceConfiguration(userPreferences, interface); + + // get userPreferences ID + unique_id = getUserPasswordID(config, userPreferences); + + // User prefs auth name --> keychain "Account" + if (config != NULL) { + account = CFDictionaryGetValue(config, kSCPropNetPPPAuthName); + } + + // User prefs "name" --> keychain "Name" + label = SCUserPreferencesCopyName(userPreferences); + + // "PPP Password" --> keychain "Kind" + if (bundle != NULL) { + description = CFBundleCopyLocalizedString(bundle, + CFSTR("KEYCHAIN_PPP_PASSWORD"), + CFSTR("PPP Password"), + NULL); + } + + // store password + ok = _SCSecKeychainPasswordItemSet(NULL, + unique_id, + (label != NULL) ? label : CFSTR("PPP"), + (description != NULL) ? description : CFSTR("PPP Password"), + account, + password, + options); + if (ok) { + CFMutableDictionaryRef newConfig; + + if (config != NULL) { + newConfig = CFDictionaryCreateMutableCopy(NULL, 0, config); + } else { + newConfig = CFDictionaryCreateMutable(NULL, + 0, + &kCFTypeDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks); + } + CFDictionarySetValue(newConfig, + kSCPropNetPPPAuthPassword, + unique_id); + CFDictionarySetValue(newConfig, + kSCPropNetPPPAuthPasswordEncryption, + kSCValNetPPPAuthPasswordEncryptionKeychain); + ok = SCUserPreferencesSetInterfaceConfiguration(userPreferences, interface, newConfig); + CFRelease(newConfig); + } + + if (config != NULL) CFRelease(config); + if (description != NULL) CFRelease(description); + if (label != NULL) CFRelease(label); + break; + } + + case kSCNetworkInterfacePasswordTypeIPSecSharedSecret : { + CFStringRef shared_id; + + // get configuration + config = SCUserPreferencesCopyExtendedInterfaceConfiguration(userPreferences, + interface, + kSCEntNetIPSec); + + // get sharedSecret ID + shared_id = copyUserSharedSecretID(config, userPreferences); + + // User prefs "name" --> keychain "Name" + label = SCUserPreferencesCopyName(userPreferences); + + // "IPSec Shared Secret" --> keychain "Kind" + if (bundle != NULL) { + description = CFBundleCopyLocalizedString(bundle, + CFSTR("KEYCHAIN_IPSEC_SHARED_SECRET"), + CFSTR("IPSec Shared Secret"), + NULL); + } + + // set password + ok = _SCSecKeychainPasswordItemSet(NULL, + shared_id, + (label != NULL) ? label : CFSTR("PPP"), + (description != NULL) ? description : CFSTR("IPSec Shared Secret"), + NULL, + password, + options); + if (ok) { + CFMutableDictionaryRef newConfig = NULL; + + if (config != NULL) { + newConfig = CFDictionaryCreateMutableCopy(NULL, 0, config); + } else { + newConfig = CFDictionaryCreateMutable(NULL, + 0, + &kCFTypeDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks); + } + CFDictionarySetValue(newConfig, + kSCPropNetIPSecSharedSecret, + shared_id); + CFDictionarySetValue(newConfig, + kSCPropNetIPSecSharedSecretEncryption, + kSCValNetIPSecSharedSecretEncryptionKeychain); + ok = SCUserPreferencesSetExtendedInterfaceConfiguration(userPreferences, + interface, + kSCEntNetIPSec, + newConfig); + CFRelease(newConfig); + } + + if (config != NULL) CFRelease(config); + if (description != NULL) CFRelease(description); + if (label != NULL) CFRelease(label); + CFRelease(shared_id); + break; + } + + default : + _SCErrorSet(kSCStatusInvalidArgument); + return FALSE; + } + + return ok; +}