SCPreferencesKeychainPrivate.c   [plain text]


/*
 * Copyright (c) 2006 Apple Computer, 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@
 */

/*
 * Modification History
 *
 * May 24, 2006	Allan Nathanson (ajn@apple.com)
 * - adapted (for SystemConfiguration)
 *
 * May 10, 2006	Dieter Siegmund (dieter@apple.com)
 * - created (for EAP)
 */

#include <sys/param.h>

#include <CoreFoundation/CoreFoundation.h>
#include <CoreFoundation/CFBundlePriv.h>	// for _CFBundleCopyMainBundleExecutableURL
#include <SystemConfiguration/SCPrivate.h>	// for _SCErrorSet
#include <Security/Security.h>
#include "dy_framework.h"

#include "SCPreferencesInternal.h"


static CFDataRef
copyMyExecutablePath(void)
{
	char		fspath[MAXPATHLEN];
	Boolean		isBundle	= FALSE;
	Boolean		ok;
	CFDataRef	path		= NULL;
	CFURLRef	url;

	url = _CFBundleCopyMainBundleExecutableURL(&isBundle);
	if (url == NULL) {
		return NULL;
	}

	ok  = CFURLGetFileSystemRepresentation(url, TRUE, (UInt8 *)fspath, sizeof(fspath));
	CFRelease(url);
	if (!ok) {
		return NULL;
	}
	fspath[sizeof(fspath) - 1] = '\0';

	if (isBundle) {
		const char	*slash;

		slash = strrchr(fspath, '/');
		if (slash != NULL) {
			const char	*contents;

			contents = strstr(fspath, "/Contents/MacOS/");
			if ((contents != NULL) &&
			    ((contents + sizeof("/Contents/MacOS/") - 1) == slash)) {
				path = CFDataCreate(NULL, (UInt8 *)fspath, contents - fspath);
				goto done;
			}
		}
	}

	path = CFDataCreate(NULL, (UInt8 *)fspath, strlen(fspath));

    done :

	return path;
}


#pragma mark -
#pragma mark Keychain helper APIs


/*
 * Create a SecAccessRef with a custom form.
 *
 * Both the owner and the ACL set allow free access to root,
 * but nothing to anyone else.
 *
 * NOTE: This is not the easiest way to build up CSSM data structures
 *       but it is a way that does not depend on any outside software
 *       layers (other than CSSM and Security's Sec* layer, of course).
 */
static SecAccessRef
_SCSecAccessCreateForUID(uid_t uid)
{
	SecAccessRef	access	= NULL;
	OSStatus	status;

	// make the "uid/gid" ACL subject
	// this is a CSSM_LIST_ELEMENT chain

	CSSM_ACL_PROCESS_SUBJECT_SELECTOR	selector	= {
		CSSM_ACL_PROCESS_SELECTOR_CURRENT_VERSION,			// version
		CSSM_ACL_MATCH_UID,						// active fields mask: match uids (only)
		uid,								// effective user id to match
		0								// effective group id to match
	};

	CSSM_LIST_ELEMENT			subject2	= {
		NULL,								// NextElement
		0								// WordID
		// rest is defaulted
	};

	subject2.Element.Word.Data   = (UInt8 *)&selector;
	subject2.Element.Word.Length = sizeof(selector);

	CSSM_LIST_ELEMENT			subject1	= {
		&subject2,							// NextElement
		CSSM_ACL_SUBJECT_TYPE_PROCESS,					// WordID
		CSSM_LIST_ELEMENT_WORDID					// ElementType
		// rest is defaulted
	};

	// rights granted (replace with individual list if desired)
	CSSM_ACL_AUTHORIZATION_TAG		rights[]	= {
		CSSM_ACL_AUTHORIZATION_ANY					// everything
	};

	// owner component (right to change ACL)
	CSSM_ACL_OWNER_PROTOTYPE		owner		= {
		{								// TypedSubject
			CSSM_LIST_TYPE_UNKNOWN,					   // type of this list
			&subject1,						   // head of the list
			&subject2						   // tail of the list
		},
		FALSE								// Delegate
	};

	// ACL entries (any number, just one here)
	CSSM_ACL_ENTRY_INFO			acls[]		= {
		{
			{							// EntryPublicInfo
				{						   // TypedSubject
					CSSM_LIST_TYPE_UNKNOWN,			      // type of this list
					&subject1,				      // head of the list
					&subject2				      // tail of the list
				},
				FALSE,						   // Delegate
				{						   // Authorization
					sizeof(rights) / sizeof(rights[0]),	      // NumberOfAuthTags
					rights					      // AuthTags
				},
				{						   // TimeRange
				},
				{						   // EntryTag
				}
			},
			0							// EntryHandle
		}
	};

	status = SecAccessCreateFromOwnerAndACL(&owner,
						sizeof(acls) / sizeof(acls[0]),
						acls,
						&access);
	if (status != noErr) {
		_SCErrorSet(status);
	}

	return access;
}


// one example would be to pass a URL for "/System/Library/CoreServices/SystemUIServer.app"
static SecAccessRef
_SCSecAccessCreateForExecutables(CFStringRef	label,
				 CFArrayRef	executableURLs)
{
	SecAccessRef			access			= NULL;
	CFArrayRef			aclList			= NULL;
	CFIndex				i;
	CFIndex				n;
	OSStatus			status;
	SecTrustedApplicationRef	trustedApplication;
	CFMutableArrayRef		trustedApplications;

	trustedApplications = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);

	// Use default access ("confirm access")

	// Next, we make an exception list of applications you want to trust.
	// These applications will be allowed to access the item without requiring
	// user confirmation.

	// Trust the calling application
	status = SecTrustedApplicationCreateFromPath(NULL, &trustedApplication);
	if (status == noErr) {
		CFArrayAppendValue(trustedApplications, trustedApplication);
		CFRelease(trustedApplication);
	}

	n = (executableURLs != NULL) ? CFArrayGetCount(executableURLs) : 0;
	for (i = 0; i < n; i++) {
		Boolean		ok;
		char		path[MAXPATHLEN];
		CFURLRef	url;

		url = CFArrayGetValueAtIndex(executableURLs, i);
		ok = CFURLGetFileSystemRepresentation(url, TRUE, (UInt8 *)path, sizeof(path));
		if (!ok) {
			continue;
		}

		status = SecTrustedApplicationCreateFromPath(path, &trustedApplication);
		if (status == noErr) {
			CFArrayAppendValue(trustedApplications, trustedApplication);
			CFRelease(trustedApplication);
		}
	}

	status = SecAccessCreate(label, trustedApplications, &access);
	if (status != noErr) {
		goto done;
	}

#ifdef	NOT_NEEDED
	// get the access control list for decryption operations (this controls access to an item's data)
	status = SecAccessCopySelectedACLList(access, CSSM_ACL_AUTHORIZATION_DECRYPT, &aclList);
	if (status == noErr) {
		SecACLRef				acl;
		CFArrayRef				applicationList	= NULL;
		CFStringRef				description	= NULL;
		CSSM_ACL_KEYCHAIN_PROMPT_SELECTOR	promptSelector;

		// get the first entry in the access control list
		acl = (SecACLRef)CFArrayGetValueAtIndex(aclList, 0);

		// get the description and prompt selector
		status = SecACLCopySimpleContents(acl, &applicationList, &description, &promptSelector);

		// modify the application list
		status = SecACLSetSimpleContents(acl, (CFArrayRef)trustedApplications, description, &promptSelector);

		if (applicationList != NULL)	CFRelease(applicationList);
		if (description != NULL)	CFRelease(description);
	}
#endif	// NOT_NEEDED

    done :

	if (aclList != NULL)	CFRelease(aclList);
	CFRelease(trustedApplications);

	return	access;
}


SecKeychainRef
_SCSecKeychainCopySystemKeychain(void)
{
	SecPreferencesDomain	domain;
	SecKeychainRef		keychain	= NULL;
	OSStatus		status;

	status = SecKeychainGetPreferenceDomain(&domain);
	if (status != noErr) {
		_SCErrorSet(status);
		return NULL;
	}

	status = SecKeychainSetPreferenceDomain(kSecPreferencesDomainSystem);
	if (status != noErr) {
		_SCErrorSet(status);
		return NULL;
	}

	status = SecKeychainCopyDomainDefault(kSecPreferencesDomainSystem, &keychain);
	if (status != noErr) {
		_SCErrorSet(status);
		(void) SecKeychainSetPreferenceDomain(domain);
		if (keychain != NULL) CFRelease(keychain);
		return NULL;
	}

	status = SecKeychainSetPreferenceDomain(domain);
	if (status != noErr) {
		_SCErrorSet(status);
		if (keychain != NULL) CFRelease(keychain);
		return NULL;
	}

	return keychain;
}


static OSStatus
findKeychainItem(SecKeychainRef		keychain,
		 UInt32			serviceNameLength,
		 void			*serviceName,
		 SecKeychainItemRef	*item)
{
	SecKeychainAttribute		attributes[1];
	SecKeychainAttributeList	attributeList	= { 1, attributes };
	SecKeychainSearchRef		search		= NULL;
	OSStatus			status;

	attributes[0].tag    = kSecServiceItemAttr;
	attributes[0].data   = serviceName;
	attributes[0].length = serviceNameLength;

	status = SecKeychainSearchCreateFromAttributes(keychain,
						       kSecGenericPasswordItemClass,
						       &attributeList,
						       &search);
	if (status != noErr) {
		return status;
	}

	status = SecKeychainSearchCopyNext(search, item);
	CFRelease(search);

	return status;
}


CFDataRef
_SCSecKeychainPasswordItemCopy(SecKeychainRef	keychain,
			       CFStringRef	unique_id)
{
	SecKeychainItemRef	item			= NULL;
	CFDataRef		keychain_password	= NULL;
	const char		*keychain_serviceName;
	OSStatus		status;

	keychain_serviceName = _SC_cfstring_to_cstring(unique_id, NULL, 0, kCFStringEncodingUTF8);
	status = findKeychainItem(keychain,
				  strlen(keychain_serviceName),
				  (void *)keychain_serviceName,
				  &item);
	CFAllocatorDeallocate(NULL, (void *)keychain_serviceName);
	if (status == noErr) {
		void *		pw	= NULL;
		UInt32 		pw_len	= 0;

		status = SecKeychainItemCopyContent(item, NULL, NULL, &pw_len, &pw);
		if (status == noErr) {
			keychain_password = CFDataCreate(NULL, pw, pw_len);
			status = SecKeychainItemFreeContent(NULL, pw);
		}
	}
	if (item != NULL) CFRelease(item);
	if (status != noErr) {
		_SCErrorSet(status);
	}

	return keychain_password;
}


Boolean
_SCSecKeychainPasswordItemExists(SecKeychainRef keychain, CFStringRef unique_id)
{
	SecKeychainItemRef	item;
	const char		*keychain_serviceName;
	OSStatus		status;

	keychain_serviceName = _SC_cfstring_to_cstring(unique_id, NULL, 0, kCFStringEncodingUTF8);
	status = findKeychainItem(keychain,
				  strlen(keychain_serviceName),
				  (void *)keychain_serviceName,
				  &item);
	CFAllocatorDeallocate(NULL, (void *)keychain_serviceName);
	if (status != noErr) {
		_SCErrorSet(status);
		return FALSE;
	}

	CFRelease(item);
	return TRUE;
}


Boolean
_SCSecKeychainPasswordItemRemove(SecKeychainRef keychain, CFStringRef unique_id)
{
	SecKeychainItemRef	item;
	const char		*keychain_serviceName;
	OSStatus		status;

	keychain_serviceName = _SC_cfstring_to_cstring(unique_id, NULL, 0, kCFStringEncodingUTF8);
	status = findKeychainItem(keychain,
				  strlen(keychain_serviceName),
				  (void *)keychain_serviceName,
				  &item);
	CFAllocatorDeallocate(NULL, (void *)keychain_serviceName);
	if (status != noErr) {
		_SCErrorSet(status);
		return FALSE;
	}

	status = SecKeychainItemDelete(item);
	CFRelease(item);
	if (status != noErr) {
		_SCErrorSet(status);
		return FALSE;
	}

	return TRUE;
}


Boolean
_SCSecKeychainPasswordItemSet(SecKeychainRef	keychain,
			      CFStringRef	unique_id,
			      CFStringRef	label,
			      CFStringRef	description,
			      CFStringRef	account,
			      CFDataRef		password,
			      CFDictionaryRef	options)
{
	SecAccessRef			access			= NULL;
	CFBooleanRef			allowRoot		= NULL;
	CFArrayRef			allowedExecutables	= NULL;
	SecKeychainAttribute		attributes[4];
	SecKeychainAttributeList	attributeList		= { 0, attributes };
	CFIndex				i;
	SecKeychainItemRef		item			= NULL;
	CFIndex				n			= 0;
	OSStatus			status;

	if (options != NULL) {
		if (isA_CFDictionary(options)) {
			allowRoot          = CFDictionaryGetValue(options, kSCKeychainOptionsAllowRoot);
			allowedExecutables = CFDictionaryGetValue(options, kSCKeychainOptionsAllowedExecutables);
		} else {
			_SCErrorSet(kSCStatusInvalidArgument);
			return FALSE;
		}
	}

	if (!isA_CFString(unique_id) ||
	    ((label              != NULL) && !isA_CFString (label             )) ||
	    ((description        != NULL) && !isA_CFString (description       )) ||
	    ((account            != NULL) && !isA_CFString (account           )) ||
	    ((password           != NULL) && !isA_CFData   (password          )) ||
	    ((allowRoot          != NULL) && !isA_CFBoolean(allowRoot         )) ||
	    ((allowedExecutables != NULL) && !isA_CFArray  (allowedExecutables)) ||
	    ((allowRoot          != NULL) && (allowedExecutables != NULL))) {
		_SCErrorSet(kSCStatusInvalidArgument);
		return FALSE;
	}

	if ((allowRoot != NULL) && CFBooleanGetValue(allowRoot)) {
		access = _SCSecAccessCreateForUID(0);
		if (access == NULL) {
			return FALSE;
		}
	} else if (allowedExecutables != NULL) {
		access = _SCSecAccessCreateForExecutables(label, allowedExecutables);
		if (access == NULL) {
			return FALSE;
		}
	}

	for (i = 0; i < 4; i++) {
		CFStringRef		str	= NULL;
		SecKeychainAttrType	tag	= 0;

		switch (i) {
			case 0 :
				str = unique_id;
				tag = kSecServiceItemAttr;
				break;
			case 1 :
				str = label;
				tag = kSecLabelItemAttr;
				break;
			case 2 :
				str = description;
				tag = kSecDescriptionItemAttr;
				break;
			case 3 :
				str = account;
				tag = kSecAccountItemAttr;
				break;
		}

		if (str == NULL) {
			continue;
		}

		attributes[n].tag    = tag;
		attributes[n].data   = _SC_cfstring_to_cstring(str, NULL, 0, kCFStringEncodingUTF8);
		attributes[n].length = strlen(attributes[n].data);
		n++;
	}

	status = findKeychainItem(keychain,
				  attributes[0].length,
				  attributes[0].data,
				  &item);
	switch (status) {
		case noErr : {
			const void	*pw	= NULL;
			UInt32		pw_len	= 0;

			// keychain item exists
			if (password != NULL) {
				pw     = CFDataGetBytePtr(password);
				pw_len = CFDataGetLength(password);
			}

			attributeList.count = n;
			status = SecKeychainItemModifyContent(item,
							      &attributeList,
							      pw_len,
							      pw);
			break;
		}

		case errSecItemNotFound : {
			// no keychain item
			if (password == NULL) {
				// creating new keychain item and password not specified
				status = kSCStatusInvalidArgument;
				goto done;
			}

			attributeList.count = n;
			status = SecKeychainItemCreateFromContent(kSecGenericPasswordItemClass,
								&attributeList,
								CFDataGetLength(password),
								CFDataGetBytePtr(password),
								keychain,
								access,
								NULL);
			break;
		}

		// some other error
		default :
			break;
	}

    done :

	if (access != NULL)	CFRelease(access);
	if (item != NULL)	CFRelease(item);

	for (i = 0; i < n; i++) {
		CFAllocatorDeallocate(NULL, attributes[i].data);
	}

	if (status != noErr) {
		_SCErrorSet(status);
		return FALSE;
	}

	return TRUE;
}


#pragma mark -
#pragma mark "System" Keychain APIs (w/SCPreferences)


#include "SCHelper_client.h"

#include <fcntl.h>
#include <unistd.h>
#include <sys/errno.h>


static CFDataRef
__SCPreferencesSystemKeychainPasswordItemCopy_helper(SCPreferencesRef	prefs,
						     CFStringRef	unique_id)
{
	CFDataRef		data		= NULL;
	Boolean			ok;
	SCPreferencesPrivateRef	prefsPrivate	= (SCPreferencesPrivateRef)prefs;
	uint32_t		status		= kSCStatusOK;
	CFDataRef		reply		= NULL;

	if (prefsPrivate->helper == -1) {
		ok = __SCPreferencesCreate_helper(prefs);
		if (!ok) {
			return FALSE;
		}
	}

	ok = _SCSerializeString(unique_id, &data, NULL, NULL);
	if (!ok) {
		goto fail;
	}

	// have the helper set the "System" Keychain password
	ok = _SCHelperExec(prefsPrivate->helper,
			   SCHELPER_MSG_KEYCHAIN_COPY,
			   data,
			   &status,
			   &reply);
	if (data != NULL) CFRelease(data);
	if (!ok) {
		goto fail;
	}

	if (status != kSCStatusOK) {
		goto error;
	}

	return reply;

    fail :

	// close helper
	if (prefsPrivate->helper != -1) {
		_SCHelperClose(prefsPrivate->helper);
		prefsPrivate->helper = -1;
	}

	status = kSCStatusAccessError;

    error :

	// return error
	if (reply != NULL) CFRelease(reply);
	_SCErrorSet(status);
	return NULL;
}


CFDataRef
_SCPreferencesSystemKeychainPasswordItemCopy(SCPreferencesRef	prefs,
					     CFStringRef	unique_id)
{
	SecKeychainRef		keychain	= NULL;
	CFDataRef		password	= NULL;
	SCPreferencesPrivateRef	prefsPrivate	= (SCPreferencesPrivateRef)prefs;

	if (prefs == NULL) {
		/* sorry, you must provide a session */
		_SCErrorSet(kSCStatusNoPrefsSession);
		return FALSE;
	}

	if (!isA_CFString(unique_id)) {
		_SCErrorSet(kSCStatusInvalidArgument);
		return FALSE;
	}

	if (prefsPrivate->authorizationData != NULL) {
		password = __SCPreferencesSystemKeychainPasswordItemCopy_helper(prefs, unique_id);
		goto done;
	}

	keychain = _SCSecKeychainCopySystemKeychain();
	if (keychain == NULL) {
		goto done;
	}

	password = _SCSecKeychainPasswordItemCopy(keychain, unique_id);

    done :

	if (keychain != NULL)	CFRelease(keychain);
	return password;
}


Boolean
_SCPreferencesSystemKeychainPasswordItemExists(SCPreferencesRef	prefs,
					       CFStringRef	unique_id)
{
	SecKeychainRef		keychain	= NULL;
	Boolean			ok		= FALSE;
//	SCPreferencesPrivateRef	prefsPrivate	= (SCPreferencesPrivateRef)prefs;

	if (prefs == NULL) {
		/* sorry, you must provide a session */
		_SCErrorSet(kSCStatusNoPrefsSession);
		return FALSE;
	}

	if (!isA_CFString(unique_id)) {
		_SCErrorSet(kSCStatusInvalidArgument);
		return FALSE;
	}

//	if (prefsPrivate->authorizationData != NULL) {
//		ok = __SCPreferencesSystemKeychainPasswordItemExists_helper(prefs, unique_id);
//		goto done;
//	}

	keychain = _SCSecKeychainCopySystemKeychain();
	if (keychain == NULL) {
		goto done;
	}

	ok = _SCSecKeychainPasswordItemExists(keychain, unique_id);

    done :

	if (keychain != NULL)	CFRelease(keychain);
	return ok;
}


static Boolean
__SCPreferencesSystemKeychainPasswordItemRemove_helper(SCPreferencesRef	prefs,
						       CFStringRef	unique_id)
{
	CFDataRef		data		= NULL;
	Boolean			ok;
	SCPreferencesPrivateRef	prefsPrivate	= (SCPreferencesPrivateRef)prefs;
	uint32_t		status		= kSCStatusOK;
	CFDataRef		reply		= NULL;

	if (prefsPrivate->helper == -1) {
		ok = __SCPreferencesCreate_helper(prefs);
		if (!ok) {
			return FALSE;
		}
	}

	ok = _SCSerializeString(unique_id, &data, NULL, NULL);
	if (!ok) {
		goto fail;
	}

	// have the helper set the "System" Keychain password
	ok = _SCHelperExec(prefsPrivate->helper,
			   SCHELPER_MSG_KEYCHAIN_REMOVE,
			   data,
			   &status,
			   &reply);
	if (data != NULL) CFRelease(data);
	if (!ok) {
		goto fail;
	}

	if (status != kSCStatusOK) {
		goto error;
	}

	return TRUE;

    fail :

	// close helper
	if (prefsPrivate->helper != -1) {
		_SCHelperClose(prefsPrivate->helper);
		prefsPrivate->helper = -1;
	}

	status = kSCStatusAccessError;

    error :

	// return error
	if (reply != NULL) CFRelease(reply);
	_SCErrorSet(status);
	return FALSE;
}


Boolean
_SCPreferencesSystemKeychainPasswordItemRemove(SCPreferencesRef	prefs,
					       CFStringRef	unique_id)
{
	SecKeychainRef		keychain	= NULL;
	Boolean			ok		= FALSE;
	SCPreferencesPrivateRef	prefsPrivate	= (SCPreferencesPrivateRef)prefs;

	if (prefs == NULL) {
		/* sorry, you must provide a session */
		_SCErrorSet(kSCStatusNoPrefsSession);
		return FALSE;
	}

	if (!isA_CFString(unique_id)) {
		_SCErrorSet(kSCStatusInvalidArgument);
		return FALSE;
	}

	if (prefsPrivate->authorizationData != NULL) {
		ok = __SCPreferencesSystemKeychainPasswordItemRemove_helper(prefs, unique_id);
		goto done;
	}

	keychain = _SCSecKeychainCopySystemKeychain();
	if (keychain == NULL) {
		goto done;
	}

	ok = _SCSecKeychainPasswordItemRemove(keychain, unique_id);

    done :

	if (keychain != NULL)	CFRelease(keychain);
	return ok;
}


static Boolean
__SCPreferencesSystemKeychainPasswordItemSet_helper(SCPreferencesRef	prefs,
						    CFStringRef		unique_id,
						    CFStringRef		label,
						    CFStringRef		description,
						    CFStringRef		account,
						    CFDataRef		password,
						    CFDictionaryRef	options)
{
	CFDataRef		data		= NULL;
	CFMutableDictionaryRef	newOptions	= NULL;
	Boolean			ok;
	SCPreferencesPrivateRef	prefsPrivate	= (SCPreferencesPrivateRef)prefs;
	uint32_t		status		= kSCStatusOK;
	CFDataRef		reply		= NULL;

	if (prefsPrivate->helper == -1) {
		ok = __SCPreferencesCreate_helper(prefs);
		if (!ok) {
			return FALSE;
		}
	}

	if (isA_CFDictionary(options)) {
		CFArrayRef	executableURLs	= NULL;

		newOptions = CFDictionaryCreateMutableCopy(NULL, 0, options);

		if (CFDictionaryGetValueIfPresent(newOptions,
						  kSCKeychainOptionsAllowedExecutables,
						  (const void **)&executableURLs)) {
			CFMutableArrayRef	executablePaths;
			CFIndex			i;
			CFIndex			n;
			CFDataRef		path;

			executablePaths = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);

			path = copyMyExecutablePath();
			if (path != NULL) {
				CFArrayAppendValue(executablePaths, path);
				CFRelease(path);
			}

			n = CFArrayGetCount(executableURLs);
			for (i = 0; i < n; i++) {
				char		fspath[MAXPATHLEN];
				CFURLRef	url;

				url = CFArrayGetValueAtIndex(executableURLs, i);
				ok  = CFURLGetFileSystemRepresentation(url, TRUE, (UInt8 *)fspath, sizeof(fspath));
				if (!ok) {
					continue;
				}
				fspath[sizeof(fspath) - 1] = '\0';
				path = CFDataCreate(NULL, (UInt8 *)fspath, strlen(fspath));
				CFArrayAppendValue(executablePaths, path);
				CFRelease(path);
			}

			CFDictionarySetValue(newOptions, kSCKeychainOptionsAllowedExecutables, executablePaths);
			CFRelease(executablePaths);
		}
	} else {
		newOptions = CFDictionaryCreateMutable(NULL,
						       0,
						       &kCFTypeDictionaryKeyCallBacks,
						       &kCFTypeDictionaryValueCallBacks);
	}

	if (unique_id   != NULL) CFDictionarySetValue(newOptions, kSCKeychainOptionsUniqueID   , unique_id);
	if (label       != NULL) CFDictionarySetValue(newOptions, kSCKeychainOptionsLabel      , label);
	if (description != NULL) CFDictionarySetValue(newOptions, kSCKeychainOptionsDescription, description);
	if (account     != NULL) CFDictionarySetValue(newOptions, kSCKeychainOptionsAccount    , account);
	if (password    != NULL) CFDictionarySetValue(newOptions, kSCKeychainOptionsPassword   , password);

	//
	// if not AllowRoot and a list of executables was not provided than
	// pass the current executable
	//
	if (!CFDictionaryContainsKey(newOptions, kSCKeychainOptionsAllowRoot) &&
	    !CFDictionaryContainsKey(newOptions, kSCKeychainOptionsAllowedExecutables)) {
		CFDataRef	path;

		path = copyMyExecutablePath();
		if (path != NULL) {
			CFArrayRef	executablePaths;

			executablePaths = CFArrayCreate(NULL, (const void **)&path, 1, &kCFTypeArrayCallBacks);
			CFRelease(path);
			CFDictionarySetValue(newOptions, kSCKeychainOptionsAllowedExecutables, executablePaths);
			CFRelease(executablePaths);
		}
	}

	ok = _SCSerialize(newOptions, &data, NULL, NULL);
	CFRelease(newOptions);
	if (!ok) {
		goto fail;
	}

	// have the helper create the "System" Keychain password
	ok = _SCHelperExec(prefsPrivate->helper,
			   SCHELPER_MSG_KEYCHAIN_SET,
			   data,
			   &status,
			   &reply);
	if (data != NULL) CFRelease(data);
	if (!ok) {
		goto fail;
	}

	if (status != kSCStatusOK) {
		goto error;
	}

	return TRUE;

    fail :

	// close helper
	if (prefsPrivate->helper != -1) {
		_SCHelperClose(prefsPrivate->helper);
		prefsPrivate->helper = -1;
	}

	status = kSCStatusAccessError;

    error :

	// return error
	if (reply != NULL) CFRelease(reply);
	_SCErrorSet(status);
	return FALSE;
}


Boolean
_SCPreferencesSystemKeychainPasswordItemSet(SCPreferencesRef	prefs,
					    CFStringRef		unique_id,
					    CFStringRef		label,
					    CFStringRef		description,
					    CFStringRef		account,
					    CFDataRef		password,
					    CFDictionaryRef	options)
{
	SecKeychainRef		keychain		= NULL;
	Boolean			ok			= FALSE;
	SCPreferencesPrivateRef	prefsPrivate		= (SCPreferencesPrivateRef)prefs;

	if (prefs == NULL) {
		/* sorry, you must provide a session */
		_SCErrorSet(kSCStatusNoPrefsSession);
		return FALSE;
	}

	if (!isA_CFString(unique_id) ||
	    ((label       != NULL) && !isA_CFString    (label      )) ||
	    ((description != NULL) && !isA_CFString    (description)) ||
	    ((account     != NULL) && !isA_CFString    (account    )) ||
	    ((password    != NULL) && !isA_CFData      (password   )) ||
	    ((options     != NULL) && !isA_CFDictionary(options    ))) {
		_SCErrorSet(kSCStatusInvalidArgument);
		return FALSE;
	}

	if (prefsPrivate->authorizationData != NULL) {
		ok = __SCPreferencesSystemKeychainPasswordItemSet_helper(prefs,
									 unique_id,
									 label,
									 description,
									 account,
									 password,
									 options);
		goto done;
	}

	keychain = _SCSecKeychainCopySystemKeychain();
	if (keychain == NULL) {
		goto done;
	}

	ok = _SCSecKeychainPasswordItemSet(keychain,
					   unique_id,
					   label,
					   description,
					   account,
					   password,
					   options);

    done :

	if (keychain != NULL)	CFRelease(keychain);
	return ok;
}