1/*
2 * Copyright (c) 2006, 2007, 2010, 2014 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 * May 24, 2006	Allan Nathanson (ajn@apple.com)
28 * - adapted (for SystemConfiguration)
29 *
30 * May 10, 2006	Dieter Siegmund (dieter@apple.com)
31 * - created (for EAP)
32 */
33
34#include <Availability.h>
35#include <TargetConditionals.h>
36#include <sys/param.h>
37#include <CoreFoundation/CoreFoundation.h>
38#include <CoreFoundation/CFBundlePriv.h>	// for _CFBundleCopyMainBundleExecutableURL
39#include <SystemConfiguration/SCPrivate.h>	// for _SCErrorSet
40#include "dy_framework.h"
41
42#include "SCPreferencesInternal.h"
43
44
45#if	!TARGET_OS_IPHONE
46static CFDataRef
47copyMyExecutablePath(void)
48{
49	char		fspath[MAXPATHLEN];
50	Boolean		isBundle	= FALSE;
51	Boolean		ok;
52	CFDataRef	path		= NULL;
53	CFURLRef	url;
54
55	url = _CFBundleCopyMainBundleExecutableURL(&isBundle);
56	if (url == NULL) {
57		return NULL;
58	}
59
60	ok  = CFURLGetFileSystemRepresentation(url, TRUE, (UInt8 *)fspath, sizeof(fspath));
61	CFRelease(url);
62	if (!ok) {
63		return NULL;
64	}
65	fspath[sizeof(fspath) - 1] = '\0';
66
67	if (isBundle) {
68		const char	*slash;
69
70		slash = strrchr(fspath, '/');
71		if (slash != NULL) {
72			const char	*contents;
73
74			contents = strstr(fspath, "/Contents/MacOS/");
75			if ((contents != NULL) &&
76			    ((contents + sizeof("/Contents/MacOS/") - 1) == slash)) {
77				path = CFDataCreate(NULL, (UInt8 *)fspath, contents - fspath);
78				goto done;
79			}
80		}
81	}
82
83	path = CFDataCreate(NULL, (UInt8 *)fspath, strlen(fspath));
84
85    done :
86
87	return path;
88}
89
90
91#pragma mark -
92#pragma mark Keychain helper APIs
93
94
95#if	(__MAC_OS_X_VERSION_MIN_REQUIRED < 1070)
96/*
97 * Create a SecAccessRef with a custom form.
98 *
99 * Both the owner and the ACL set allow free access to root,
100 * but nothing to anyone else.
101 *
102 * NOTE: This is not the easiest way to build up CSSM data structures
103 *       but it is a way that does not depend on any outside software
104 *       layers (other than CSSM and Security's Sec* layer, of course).
105 */
106static SecAccessRef
107_SCSecAccessCreateForUID(uid_t uid)
108{
109	SecAccessRef	access	= NULL;
110	OSStatus	status;
111
112	// make the "uid/gid" ACL subject
113	// this is a CSSM_LIST_ELEMENT chain
114
115	CSSM_ACL_PROCESS_SUBJECT_SELECTOR	selector	= {
116		CSSM_ACL_PROCESS_SELECTOR_CURRENT_VERSION,			// version
117		CSSM_ACL_MATCH_UID,						// active fields mask: match uids (only)
118		uid,								// effective user id to match
119		0								// effective group id to match
120	};
121
122	CSSM_LIST_ELEMENT			subject2	= {
123		NULL,								// NextElement
124		0								// WordID
125		// rest is defaulted
126	};
127
128	subject2.Element.Word.Data   = (UInt8 *)&selector;
129	subject2.Element.Word.Length = sizeof(selector);
130
131	CSSM_LIST_ELEMENT			subject1	= {
132		&subject2,							// NextElement
133		CSSM_ACL_SUBJECT_TYPE_PROCESS,					// WordID
134		CSSM_LIST_ELEMENT_WORDID					// ElementType
135		// rest is defaulted
136	};
137
138	// rights granted (replace with individual list if desired)
139	CSSM_ACL_AUTHORIZATION_TAG		rights[]	= {
140		CSSM_ACL_AUTHORIZATION_ANY					// everything
141	};
142
143	// owner component (right to change ACL)
144	CSSM_ACL_OWNER_PROTOTYPE		owner		= {
145		{								// TypedSubject
146			CSSM_LIST_TYPE_UNKNOWN,					   // type of this list
147			&subject1,						   // head of the list
148			&subject2						   // tail of the list
149		},
150		FALSE								// Delegate
151	};
152
153	// ACL entries (any number, just one here)
154	CSSM_ACL_ENTRY_INFO			acls[]		= {
155		{
156			{							// EntryPublicInfo
157				{						   // TypedSubject
158					CSSM_LIST_TYPE_UNKNOWN,			      // type of this list
159					&subject1,				      // head of the list
160					&subject2				      // tail of the list
161				},
162				FALSE,						   // Delegate
163				{						   // Authorization
164					sizeof(rights) / sizeof(rights[0]),	      // NumberOfAuthTags
165					rights					      // AuthTags
166				},
167				{						   // TimeRange
168				},
169				{						   // EntryTag
170				}
171			},
172			0							// EntryHandle
173		}
174	};
175
176	status = SecAccessCreateFromOwnerAndACL(&owner,
177						sizeof(acls) / sizeof(acls[0]),
178						acls,
179						&access);
180	if (status != noErr) {
181		_SCErrorSet(status);
182	}
183
184	return access;
185}
186#endif	// (__MAC_OS_X_VERSION_MIN_REQUIRED < 1070)
187
188
189// one example would be to pass a URL for "/System/Library/CoreServices/SystemUIServer.app"
190static SecAccessRef
191_SCSecAccessCreateForExecutables(CFStringRef	label,
192				 CFArrayRef	executableURLs)
193{
194	SecAccessRef			access			= NULL;
195	CFArrayRef			aclList			= NULL;
196	CFIndex				i;
197	CFIndex				n;
198	OSStatus			status;
199	SecTrustedApplicationRef	trustedApplication;
200	CFMutableArrayRef		trustedApplications;
201
202	trustedApplications = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
203
204	// Use default access ("confirm access")
205
206	// Next, we make an exception list of applications you want to trust.
207	// These applications will be allowed to access the item without requiring
208	// user confirmation.
209
210	// Trust the calling application
211	status = SecTrustedApplicationCreateFromPath(NULL, &trustedApplication);
212	if (status == noErr) {
213		CFArrayAppendValue(trustedApplications, trustedApplication);
214		CFRelease(trustedApplication);
215	}
216
217	n = (executableURLs != NULL) ? CFArrayGetCount(executableURLs) : 0;
218	for (i = 0; i < n; i++) {
219		Boolean		ok;
220		char		path[MAXPATHLEN];
221		CFURLRef	url;
222
223		url = CFArrayGetValueAtIndex(executableURLs, i);
224		ok = CFURLGetFileSystemRepresentation(url, TRUE, (UInt8 *)path, sizeof(path));
225		if (!ok) {
226			continue;
227		}
228
229		status = SecTrustedApplicationCreateFromPath(path, &trustedApplication);
230		if (status == noErr) {
231			CFArrayAppendValue(trustedApplications, trustedApplication);
232			CFRelease(trustedApplication);
233		}
234	}
235
236	status = SecAccessCreate(label, trustedApplications, &access);
237	if (status != noErr) {
238		goto done;
239	}
240
241    done :
242
243	if (aclList != NULL)	CFRelease(aclList);
244	CFRelease(trustedApplications);
245
246	return	access;
247}
248#endif	// !TARGET_OS_IPHONE
249
250
251SecKeychainRef
252_SCSecKeychainCopySystemKeychain(void)
253{
254#if	!TARGET_OS_IPHONE
255	SecKeychainRef		keychain	= NULL;
256	OSStatus		status;
257
258	status = SecKeychainCopyDomainDefault(kSecPreferencesDomainSystem, &keychain);
259	if (status != noErr) {
260		_SCErrorSet(status);
261		if (keychain != NULL) CFRelease(keychain);
262		return NULL;
263	}
264
265	return keychain;
266#else	// !TARGET_OS_IPHONE
267	_SCErrorSet(kSCStatusAccessError);
268	return NULL;
269#endif	// !TARGET_OS_IPHONE
270}
271
272
273#if	!TARGET_OS_IPHONE
274static OSStatus
275findKeychainItem(SecKeychainRef		keychain,
276		 CFStringRef		unique_id,
277		 SecKeychainItemRef	*item)
278{
279	CFMutableDictionaryRef	query;
280	OSStatus		status;
281
282	query = CFDictionaryCreateMutable(NULL,
283					  0,
284					  &kCFTypeDictionaryKeyCallBacks,
285					  &kCFTypeDictionaryValueCallBacks);
286	if (keychain != NULL) {
287		CFArrayRef	keychains;
288
289		keychains = CFArrayCreate(NULL, (const void **)&keychain, 1, &kCFTypeArrayCallBacks);
290		CFDictionarySetValue(query, kSecMatchSearchList, keychains);
291		CFRelease(keychains);
292	}
293	CFDictionarySetValue(query, kSecClass      , kSecClassGenericPassword);
294	CFDictionarySetValue(query, kSecAttrService, unique_id);
295	CFDictionarySetValue(query, kSecReturnRef  , kCFBooleanTrue);
296	status = SecItemCopyMatching(query, (CFTypeRef *)item);
297	CFRelease(query);
298
299	return status;
300}
301#endif	// !TARGET_OS_IPHONE
302
303
304CFDataRef
305_SCSecKeychainPasswordItemCopy(SecKeychainRef	keychain,
306			       CFStringRef	unique_id)
307{
308#if	!TARGET_OS_IPHONE
309	SecKeychainItemRef	item			= NULL;
310	CFDataRef		keychain_password	= NULL;
311	OSStatus		status;
312
313	status = findKeychainItem(keychain, unique_id, &item);
314	if (status == noErr) {
315		void *		pw	= NULL;
316		UInt32 		pw_len	= 0;
317
318		status = SecKeychainItemCopyContent(item, NULL, NULL, &pw_len, &pw);
319		if (status == noErr) {
320			keychain_password = CFDataCreate(NULL, pw, pw_len);
321			status = SecKeychainItemFreeContent(NULL, pw);
322		}
323	}
324	if (item != NULL) CFRelease(item);
325	if (status != noErr) {
326		_SCErrorSet(status);
327	}
328
329	return keychain_password;
330#else	// !TARGET_OS_IPHONE
331	_SCErrorSet(kSCStatusAccessError);
332	return NULL;
333#endif	// !TARGET_OS_IPHONE
334}
335
336
337Boolean
338_SCSecKeychainPasswordItemExists(SecKeychainRef keychain, CFStringRef unique_id)
339{
340#if	!TARGET_OS_IPHONE
341	SecKeychainItemRef	item;
342	OSStatus		status;
343
344	status = findKeychainItem(keychain, unique_id, &item);
345	if (status != noErr) {
346		_SCErrorSet(status);
347		return FALSE;
348	}
349
350	CFRelease(item);
351	return TRUE;
352#else	// !TARGET_OS_IPHONE
353	_SCErrorSet(kSCStatusAccessError);
354	return FALSE;
355#endif	// !TARGET_OS_IPHONE
356}
357
358
359Boolean
360_SCSecKeychainPasswordItemRemove(SecKeychainRef keychain, CFStringRef unique_id)
361{
362#if	!TARGET_OS_IPHONE
363	SecKeychainItemRef	item;
364	OSStatus		status;
365
366	status = findKeychainItem(keychain, unique_id, &item);
367	if (status != noErr) {
368		_SCErrorSet(status);
369		return FALSE;
370	}
371
372	status = SecKeychainItemDelete(item);
373	CFRelease(item);
374	if (status != noErr) {
375		_SCErrorSet(status);
376		return FALSE;
377	}
378
379	return TRUE;
380#else	// !TARGET_OS_IPHONE
381	_SCErrorSet(kSCStatusAccessError);
382	return FALSE;
383#endif	// !TARGET_OS_IPHONE
384}
385
386
387Boolean
388_SCSecKeychainPasswordItemSet(SecKeychainRef	keychain,
389			      CFStringRef	unique_id,
390			      CFStringRef	label,
391			      CFStringRef	description,
392			      CFStringRef	account,
393			      CFDataRef		password,
394			      CFDictionaryRef	options)
395{
396#if	!TARGET_OS_IPHONE
397	SecAccessRef			access			= NULL;
398	CFBooleanRef			allowRoot		= NULL;
399	CFArrayRef			allowedExecutables	= NULL;
400	SecKeychainAttribute		attributes[4];
401	SecKeychainAttributeList	attributeList		= { 0, attributes };
402	CFIndex				i;
403	SecKeychainItemRef		item			= NULL;
404	CFIndex				n			= 0;
405	OSStatus			status;
406
407	if (options != NULL) {
408		if (isA_CFDictionary(options)) {
409			allowRoot          = CFDictionaryGetValue(options, kSCKeychainOptionsAllowRoot);
410			allowedExecutables = CFDictionaryGetValue(options, kSCKeychainOptionsAllowedExecutables);
411		} else {
412			_SCErrorSet(kSCStatusInvalidArgument);
413			return FALSE;
414		}
415	}
416
417	if (!isA_CFString(unique_id) ||
418	    ((label              != NULL) && !isA_CFString (label             )) ||
419	    ((description        != NULL) && !isA_CFString (description       )) ||
420	    ((account            != NULL) && !isA_CFString (account           )) ||
421	    ((password           != NULL) && !isA_CFData   (password          )) ||
422	    ((allowRoot          != NULL) && !isA_CFBoolean(allowRoot         )) ||
423	    ((allowedExecutables != NULL) && !isA_CFArray  (allowedExecutables)) ||
424	    ((allowRoot          != NULL) && (allowedExecutables != NULL))) {
425		_SCErrorSet(kSCStatusInvalidArgument);
426		return FALSE;
427	}
428
429	if ((allowRoot != NULL) && CFBooleanGetValue(allowRoot)) {
430#if	(__MAC_OS_X_VERSION_MIN_REQUIRED < 1070)
431		access = _SCSecAccessCreateForUID(0);
432		if (access == NULL) {
433			return FALSE;
434		}
435#else	// (__MAC_OS_X_VERSION_MIN_REQUIRED < 1070)
436		CFErrorRef	error	= NULL;
437
438		access = SecAccessCreateWithOwnerAndACL(0, 0, kSecUseOnlyUID, NULL, &error);
439		if (access == NULL) {
440			CFIndex	code	= kSCStatusAccessError;
441
442			if (error != NULL) {
443
444				code = CFErrorGetCode(error);
445				CFRelease(error);
446			}
447			_SCErrorSet((int)code);
448			return FALSE;
449		}
450#endif	// (__MAC_OS_X_VERSION_MIN_REQUIRED < 1070)
451	} else if (allowedExecutables != NULL) {
452		access = _SCSecAccessCreateForExecutables(label, allowedExecutables);
453		if (access == NULL) {
454			return FALSE;
455		}
456	}
457
458	for (i = 0; i < 4; i++) {
459		CFStringRef		str	= NULL;
460		SecKeychainAttrType	tag	= 0;
461
462		switch (i) {
463			case 0 :
464				str = unique_id;
465				tag = kSecServiceItemAttr;
466				break;
467			case 1 :
468				str = label;
469				tag = kSecLabelItemAttr;
470				break;
471			case 2 :
472				str = description;
473				tag = kSecDescriptionItemAttr;
474				break;
475			case 3 :
476				str = account;
477				tag = kSecAccountItemAttr;
478				break;
479		}
480
481		if (str == NULL) {
482			continue;
483		}
484
485		attributes[n].tag    = tag;
486		attributes[n].data   = _SC_cfstring_to_cstring(str, NULL, 0, kCFStringEncodingUTF8);
487		attributes[n].length = (UInt32)strlen(attributes[n].data);
488		n++;
489	}
490
491	status = findKeychainItem(keychain, unique_id, &item);
492	switch (status) {
493		case noErr : {
494			const void	*pw	= NULL;
495			UInt32		pw_len	= 0;
496
497			// keychain item exists
498			if (password != NULL) {
499				pw     = CFDataGetBytePtr(password);
500				pw_len = (UInt32)CFDataGetLength(password);
501			}
502
503			attributeList.count = (UInt32)n;
504			status = SecKeychainItemModifyContent(item,
505							      &attributeList,
506							      pw_len,
507							      pw);
508			break;
509		}
510
511		case errSecItemNotFound : {
512			// no keychain item
513			if (password == NULL) {
514				// creating new keychain item and password not specified
515				status = kSCStatusInvalidArgument;
516				goto done;
517			}
518
519			attributeList.count = (UInt32)n;
520			status = SecKeychainItemCreateFromContent(kSecGenericPasswordItemClass,
521								&attributeList,
522								(UInt32)CFDataGetLength(password),
523								CFDataGetBytePtr(password),
524								keychain,
525								access,
526								NULL);
527			break;
528		}
529
530		// some other error
531		default :
532			break;
533	}
534
535    done :
536
537	if (access != NULL)	CFRelease(access);
538	if (item != NULL)	CFRelease(item);
539
540	for (i = 0; i < n; i++) {
541		CFAllocatorDeallocate(NULL, attributes[i].data);
542	}
543
544	if (status != noErr) {
545		_SCErrorSet(status);
546		return FALSE;
547	}
548
549	return TRUE;
550#else	// !TARGET_OS_IPHONE
551	_SCErrorSet(kSCStatusAccessError);
552	return FALSE;
553#endif	// !TARGET_OS_IPHONE
554}
555
556
557#pragma mark -
558#pragma mark "System" Keychain APIs (w/SCPreferences)
559
560
561#include "SCHelper_client.h"
562
563#include <fcntl.h>
564#include <unistd.h>
565#include <sys/errno.h>
566
567
568#if	!TARGET_OS_IPHONE
569static CFDataRef
570__SCPreferencesSystemKeychainPasswordItemCopy_helper(SCPreferencesRef	prefs,
571						     CFStringRef	unique_id)
572{
573	CFDataRef		data		= NULL;
574	Boolean			ok;
575	SCPreferencesPrivateRef	prefsPrivate	= (SCPreferencesPrivateRef)prefs;
576	uint32_t		status		= kSCStatusOK;
577	CFDataRef		reply		= NULL;
578
579	if (prefsPrivate->helper_port == MACH_PORT_NULL) {
580		ok = __SCPreferencesCreate_helper(prefs);
581		if (!ok) {
582			return FALSE;
583		}
584	}
585
586	ok = _SCSerializeString(unique_id, &data, NULL, NULL);
587	if (!ok) {
588		goto fail;
589	}
590
591	// have the helper set the "System" Keychain password
592	ok = _SCHelperExec(prefsPrivate->helper_port,
593			   SCHELPER_MSG_KEYCHAIN_COPY,
594			   data,
595			   &status,
596			   &reply);
597	if (data != NULL) CFRelease(data);
598	if (!ok) {
599		goto fail;
600	}
601
602	if (status != kSCStatusOK) {
603		goto error;
604	}
605
606	return reply;
607
608    fail :
609
610	// close helper
611	if (prefsPrivate->helper_port != MACH_PORT_NULL) {
612		_SCHelperClose(&prefsPrivate->helper_port);
613	}
614
615	status = kSCStatusAccessError;
616
617    error :
618
619	// return error
620	if (reply != NULL) CFRelease(reply);
621	_SCErrorSet(status);
622	return NULL;
623}
624#endif	// !TARGET_OS_IPHONE
625
626
627CFDataRef
628_SCPreferencesSystemKeychainPasswordItemCopy(SCPreferencesRef	prefs,
629					     CFStringRef	unique_id)
630{
631#if	!TARGET_OS_IPHONE
632	SecKeychainRef		keychain	= NULL;
633	CFDataRef		password	= NULL;
634	SCPreferencesPrivateRef	prefsPrivate	= (SCPreferencesPrivateRef)prefs;
635
636	if (prefs == NULL) {
637		/* sorry, you must provide a session */
638		_SCErrorSet(kSCStatusNoPrefsSession);
639		return NULL;
640	}
641
642	if (!isA_CFString(unique_id)) {
643		_SCErrorSet(kSCStatusInvalidArgument);
644		return NULL;
645	}
646
647	if (prefsPrivate->authorizationData != NULL) {
648		password = __SCPreferencesSystemKeychainPasswordItemCopy_helper(prefs, unique_id);
649		goto done;
650	}
651
652	keychain = _SCSecKeychainCopySystemKeychain();
653	if (keychain == NULL) {
654		goto done;
655	}
656
657	password = _SCSecKeychainPasswordItemCopy(keychain, unique_id);
658
659    done :
660
661	if (keychain != NULL)	CFRelease(keychain);
662	return password;
663#else	// !TARGET_OS_IPHONE
664	_SCErrorSet(kSCStatusAccessError);
665	return NULL;
666#endif	// !TARGET_OS_IPHONE
667}
668
669
670Boolean
671_SCPreferencesSystemKeychainPasswordItemExists(SCPreferencesRef	prefs,
672					       CFStringRef	unique_id)
673{
674#if	!TARGET_OS_IPHONE
675	SecKeychainRef		keychain	= NULL;
676	Boolean			ok		= FALSE;
677//	SCPreferencesPrivateRef	prefsPrivate	= (SCPreferencesPrivateRef)prefs;
678
679	if (prefs == NULL) {
680		/* sorry, you must provide a session */
681		_SCErrorSet(kSCStatusNoPrefsSession);
682		return FALSE;
683	}
684
685	if (!isA_CFString(unique_id)) {
686		_SCErrorSet(kSCStatusInvalidArgument);
687		return FALSE;
688	}
689
690//	if (prefsPrivate->authorizationData != NULL) {
691//		ok = __SCPreferencesSystemKeychainPasswordItemExists_helper(prefs, unique_id);
692//		goto done;
693//	}
694
695	keychain = _SCSecKeychainCopySystemKeychain();
696	if (keychain == NULL) {
697		goto done;
698	}
699
700	ok = _SCSecKeychainPasswordItemExists(keychain, unique_id);
701
702    done :
703
704	if (keychain != NULL)	CFRelease(keychain);
705	return ok;
706#else	// !TARGET_OS_IPHONE
707	_SCErrorSet(kSCStatusAccessError);
708	return FALSE;
709#endif	// !TARGET_OS_IPHONE
710}
711
712
713#if	!TARGET_OS_IPHONE
714static Boolean
715__SCPreferencesSystemKeychainPasswordItemRemove_helper(SCPreferencesRef	prefs,
716						       CFStringRef	unique_id)
717{
718	CFDataRef		data		= NULL;
719	Boolean			ok;
720	SCPreferencesPrivateRef	prefsPrivate	= (SCPreferencesPrivateRef)prefs;
721	uint32_t		status		= kSCStatusOK;
722	CFDataRef		reply		= NULL;
723
724	if (prefsPrivate->helper_port == MACH_PORT_NULL) {
725		ok = __SCPreferencesCreate_helper(prefs);
726		if (!ok) {
727			return FALSE;
728		}
729	}
730
731	ok = _SCSerializeString(unique_id, &data, NULL, NULL);
732	if (!ok) {
733		goto fail;
734	}
735
736	// have the helper set the "System" Keychain password
737	ok = _SCHelperExec(prefsPrivate->helper_port,
738			   SCHELPER_MSG_KEYCHAIN_REMOVE,
739			   data,
740			   &status,
741			   &reply);
742	if (data != NULL) CFRelease(data);
743	if (!ok) {
744		goto fail;
745	}
746
747	if (status != kSCStatusOK) {
748		goto error;
749	}
750
751	return TRUE;
752
753    fail :
754
755	// close helper
756	if (prefsPrivate->helper_port != MACH_PORT_NULL) {
757		_SCHelperClose(&prefsPrivate->helper_port);
758	}
759
760	status = kSCStatusAccessError;
761
762    error :
763
764	// return error
765	if (reply != NULL) CFRelease(reply);
766	_SCErrorSet(status);
767	return FALSE;
768}
769#endif	// !TARGET_OS_IPHONE
770
771
772Boolean
773_SCPreferencesSystemKeychainPasswordItemRemove(SCPreferencesRef	prefs,
774					       CFStringRef	unique_id)
775{
776#if	!TARGET_OS_IPHONE
777	SecKeychainRef		keychain	= NULL;
778	Boolean			ok		= FALSE;
779	SCPreferencesPrivateRef	prefsPrivate	= (SCPreferencesPrivateRef)prefs;
780
781	if (prefs == NULL) {
782		/* sorry, you must provide a session */
783		_SCErrorSet(kSCStatusNoPrefsSession);
784		return FALSE;
785	}
786
787	if (!isA_CFString(unique_id)) {
788		_SCErrorSet(kSCStatusInvalidArgument);
789		return FALSE;
790	}
791
792	if (prefsPrivate->authorizationData != NULL) {
793		ok = __SCPreferencesSystemKeychainPasswordItemRemove_helper(prefs, unique_id);
794		goto done;
795	}
796
797	keychain = _SCSecKeychainCopySystemKeychain();
798	if (keychain == NULL) {
799		goto done;
800	}
801
802	ok = _SCSecKeychainPasswordItemRemove(keychain, unique_id);
803
804    done :
805
806	if (keychain != NULL)	CFRelease(keychain);
807	return ok;
808#else	// !TARGET_OS_IPHONE
809	_SCErrorSet(kSCStatusAccessError);
810	return FALSE;
811#endif	// !TARGET_OS_IPHONE
812}
813
814
815#if	!TARGET_OS_IPHONE
816static Boolean
817__SCPreferencesSystemKeychainPasswordItemSet_helper(SCPreferencesRef	prefs,
818						    CFStringRef		unique_id,
819						    CFStringRef		label,
820						    CFStringRef		description,
821						    CFStringRef		account,
822						    CFDataRef		password,
823						    CFDictionaryRef	options)
824{
825	CFDataRef		data		= NULL;
826	CFMutableDictionaryRef	newOptions	= NULL;
827	Boolean			ok;
828	SCPreferencesPrivateRef	prefsPrivate	= (SCPreferencesPrivateRef)prefs;
829	uint32_t		status		= kSCStatusOK;
830	CFDataRef		reply		= NULL;
831
832	if (prefsPrivate->helper_port == MACH_PORT_NULL) {
833		ok = __SCPreferencesCreate_helper(prefs);
834		if (!ok) {
835			return FALSE;
836		}
837	}
838
839	if (isA_CFDictionary(options)) {
840		CFArrayRef	executableURLs	= NULL;
841
842		newOptions = CFDictionaryCreateMutableCopy(NULL, 0, options);
843
844		if (CFDictionaryGetValueIfPresent(newOptions,
845						  kSCKeychainOptionsAllowedExecutables,
846						  (const void **)&executableURLs)) {
847			CFMutableArrayRef	executablePaths;
848			CFIndex			i;
849			CFIndex			n;
850			CFDataRef		path;
851
852			executablePaths = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
853
854			path = copyMyExecutablePath();
855			if (path != NULL) {
856				CFArrayAppendValue(executablePaths, path);
857				CFRelease(path);
858			}
859
860			n = CFArrayGetCount(executableURLs);
861			for (i = 0; i < n; i++) {
862				char		fspath[MAXPATHLEN];
863				CFURLRef	url;
864
865				url = CFArrayGetValueAtIndex(executableURLs, i);
866				ok  = CFURLGetFileSystemRepresentation(url, TRUE, (UInt8 *)fspath, sizeof(fspath));
867				if (!ok) {
868					continue;
869				}
870				fspath[sizeof(fspath) - 1] = '\0';
871				path = CFDataCreate(NULL, (UInt8 *)fspath, strlen(fspath));
872				CFArrayAppendValue(executablePaths, path);
873				CFRelease(path);
874			}
875
876			CFDictionarySetValue(newOptions, kSCKeychainOptionsAllowedExecutables, executablePaths);
877			CFRelease(executablePaths);
878		}
879	} else {
880		newOptions = CFDictionaryCreateMutable(NULL,
881						       0,
882						       &kCFTypeDictionaryKeyCallBacks,
883						       &kCFTypeDictionaryValueCallBacks);
884	}
885
886	if (unique_id   != NULL) CFDictionarySetValue(newOptions, kSCKeychainOptionsUniqueID   , unique_id);
887	if (label       != NULL) CFDictionarySetValue(newOptions, kSCKeychainOptionsLabel      , label);
888	if (description != NULL) CFDictionarySetValue(newOptions, kSCKeychainOptionsDescription, description);
889	if (account     != NULL) CFDictionarySetValue(newOptions, kSCKeychainOptionsAccount    , account);
890	if (password    != NULL) CFDictionarySetValue(newOptions, kSCKeychainOptionsPassword   , password);
891
892	//
893	// if not AllowRoot and a list of executables was not provided than
894	// pass the current executable
895	//
896	if (!CFDictionaryContainsKey(newOptions, kSCKeychainOptionsAllowRoot) &&
897	    !CFDictionaryContainsKey(newOptions, kSCKeychainOptionsAllowedExecutables)) {
898		CFDataRef	path;
899
900		path = copyMyExecutablePath();
901		if (path != NULL) {
902			CFArrayRef	executablePaths;
903
904			executablePaths = CFArrayCreate(NULL, (const void **)&path, 1, &kCFTypeArrayCallBacks);
905			CFRelease(path);
906			CFDictionarySetValue(newOptions, kSCKeychainOptionsAllowedExecutables, executablePaths);
907			CFRelease(executablePaths);
908		}
909	}
910
911	ok = _SCSerialize(newOptions, &data, NULL, NULL);
912	CFRelease(newOptions);
913	if (!ok) {
914		goto fail;
915	}
916
917	// have the helper create the "System" Keychain password
918	ok = _SCHelperExec(prefsPrivate->helper_port,
919			   SCHELPER_MSG_KEYCHAIN_SET,
920			   data,
921			   &status,
922			   &reply);
923	if (data != NULL) CFRelease(data);
924	if (!ok) {
925		goto fail;
926	}
927
928	if (status != kSCStatusOK) {
929		goto error;
930	}
931
932	return TRUE;
933
934    fail :
935
936	// close helper
937	if (prefsPrivate->helper_port != MACH_PORT_NULL) {
938		_SCHelperClose(&prefsPrivate->helper_port);
939	}
940
941	status = kSCStatusAccessError;
942
943    error :
944
945	// return error
946	if (reply != NULL) CFRelease(reply);
947	_SCErrorSet(status);
948	return FALSE;
949}
950#endif	// !TARGET_OS_IPHONE
951
952
953Boolean
954_SCPreferencesSystemKeychainPasswordItemSet(SCPreferencesRef	prefs,
955					    CFStringRef		unique_id,
956					    CFStringRef		label,
957					    CFStringRef		description,
958					    CFStringRef		account,
959					    CFDataRef		password,
960					    CFDictionaryRef	options)
961{
962#if	!TARGET_OS_IPHONE
963	SecKeychainRef		keychain		= NULL;
964	Boolean			ok			= FALSE;
965	SCPreferencesPrivateRef	prefsPrivate		= (SCPreferencesPrivateRef)prefs;
966
967	if (prefs == NULL) {
968		/* sorry, you must provide a session */
969		_SCErrorSet(kSCStatusNoPrefsSession);
970		return FALSE;
971	}
972
973	if (!isA_CFString(unique_id) ||
974	    ((label       != NULL) && !isA_CFString    (label      )) ||
975	    ((description != NULL) && !isA_CFString    (description)) ||
976	    ((account     != NULL) && !isA_CFString    (account    )) ||
977	    ((password    != NULL) && !isA_CFData      (password   )) ||
978	    ((options     != NULL) && !isA_CFDictionary(options    ))) {
979		_SCErrorSet(kSCStatusInvalidArgument);
980		return FALSE;
981	}
982
983	if (prefsPrivate->authorizationData != NULL) {
984		ok = __SCPreferencesSystemKeychainPasswordItemSet_helper(prefs,
985									 unique_id,
986									 label,
987									 description,
988									 account,
989									 password,
990									 options);
991		goto done;
992	}
993
994	keychain = _SCSecKeychainCopySystemKeychain();
995	if (keychain == NULL) {
996		goto done;
997	}
998
999	ok = _SCSecKeychainPasswordItemSet(keychain,
1000					   unique_id,
1001					   label,
1002					   description,
1003					   account,
1004					   password,
1005					   options);
1006
1007    done :
1008
1009	if (keychain != NULL)	CFRelease(keychain);
1010	return ok;
1011#else	// !TARGET_OS_IPHONE
1012	_SCErrorSet(kSCStatusAccessError);
1013	return FALSE;
1014#endif	// !TARGET_OS_IPHONE
1015}
1016