1/*
2 * Copyright (c) 2006-2013 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#include <stdio.h>
25#include <stdlib.h>
26#include <unistd.h>
27#include <sys/types.h>
28#include <string.h>
29#include <TargetConditionals.h>
30#include <CoreFoundation/CFString.h>
31#include <CoreFoundation/CFNumber.h>
32#include "symbol_scope.h"
33#include "EAPLog.h"
34#include "EAPSecurity.h"
35#include "EAPKeychainUtil.h"
36#include "EAPKeychainUtilInternal.h"
37#include "myCFUtil.h"
38
39#if TARGET_OS_EMBEDDED
40
41OSStatus
42EAPSecKeychainPasswordItemRemove(SecKeychainRef keychain,
43				 CFStringRef unique_id_str)
44{
45    const void *	keys[] = {
46	kSecClass,
47	kSecAttrService
48    };
49    CFDictionaryRef	query;
50    OSStatus		status;
51
52    const void *	values[] = {
53	kSecClassGenericPassword,
54	unique_id_str
55    };
56
57    query = CFDictionaryCreate(NULL, keys, values,
58			       sizeof(keys) / sizeof(*keys),
59			       &kCFTypeDictionaryKeyCallBacks,
60			       &kCFTypeDictionaryValueCallBacks);
61    status = SecItemDelete(query);
62    CFRelease(query);
63    if (status != noErr) {
64	EAPLOG_FL(LOG_NOTICE, "SecItemDelete failed: %s (%d)",
65		  EAPSecurityErrorString(status), (int)status);
66    }
67    return (status);
68}
69
70OSStatus
71EAPSecKeychainPasswordItemCopy(SecKeychainRef keychain,
72			       CFStringRef unique_id_str,
73			       CFDataRef * ret_password)
74{
75    const void *	keys[] = {
76	kSecClass,
77	kSecAttrService,
78	kSecReturnData
79    };
80    CFDictionaryRef	query;
81    CFTypeRef		results;
82    OSStatus		status;
83    const void *	values[] = {
84	kSecClassGenericPassword,
85	unique_id_str,
86	kCFBooleanTrue
87    };
88
89    query = CFDictionaryCreate(NULL, keys, values,
90			       sizeof(keys) / sizeof(*keys),
91			       &kCFTypeDictionaryKeyCallBacks,
92			       &kCFTypeDictionaryValueCallBacks);
93    status = SecItemCopyMatching(query, &results);
94    CFRelease(query);
95    if (status == noErr) {
96	*ret_password = results;
97    }
98    else {
99	*ret_password = NULL;
100    }
101    return (status);
102}
103
104OSStatus
105EAPSecKeychainPasswordItemCreateWithAccess(SecKeychainRef keychain,
106					   SecAccessRef access,
107					   CFStringRef unique_id_str,
108					   CFDataRef label,
109					   CFDataRef description,
110					   CFDataRef user,
111					   CFDataRef password)
112{
113    CFMutableDictionaryRef	attrs;
114    OSStatus			status;
115
116    attrs = CFDictionaryCreateMutable(NULL, 0,
117				      &kCFTypeDictionaryKeyCallBacks,
118				      &kCFTypeDictionaryValueCallBacks);
119    CFDictionarySetValue(attrs, kSecClass, kSecClassGenericPassword);
120    CFDictionarySetValue(attrs, kSecAttrService, unique_id_str);
121    CFDictionarySetValue(attrs, kSecAttrAccessible, kSecAttrAccessibleAfterFirstUnlock);
122    if (label != NULL) {
123	CFDictionarySetValue(attrs, kSecAttrLabel, label);
124    }
125    if (description != NULL) {
126	CFDictionarySetValue(attrs, kSecAttrDescription, description);
127    }
128    if (user != NULL) {
129	CFDictionarySetValue(attrs, kSecAttrAccount, user);
130    }
131    CFDictionarySetValue(attrs, kSecValueData, password);
132    status = SecItemAdd(attrs, NULL);
133    CFRelease(attrs);
134    return (status);
135}
136
137OSStatus
138EAPSecKeychainPasswordItemSet(SecKeychainRef keychain,
139			      CFStringRef unique_id_str,
140			      CFDataRef password)
141{
142    const void *	keys[] = {
143	kSecClass,
144	kSecAttrService,
145	kSecReturnData
146    };
147    CFTypeRef		existing_password = NULL;
148    CFDictionaryRef	query;
149    CFDictionaryRef	pass_dict;
150    OSStatus		status;
151    const void *	values[] = {
152	kSecClassGenericPassword,
153	unique_id_str,
154	kCFBooleanTrue
155    };
156
157    query = CFDictionaryCreate(NULL, keys, values,
158			       sizeof(keys) / sizeof(*keys),
159			       &kCFTypeDictionaryKeyCallBacks,
160			       &kCFTypeDictionaryValueCallBacks);
161    status = SecItemCopyMatching(query, &existing_password);
162    CFRelease(query);
163    if (status != noErr) {
164	goto done;
165    }
166    if (existing_password != NULL
167	&& CFEqual(password, existing_password)) {
168	/* nothing to do */
169	goto done;
170    }
171    query = CFDictionaryCreate(NULL, keys, values,
172			       (sizeof(keys) / sizeof(*keys)) - 1,
173			       &kCFTypeDictionaryKeyCallBacks,
174			       &kCFTypeDictionaryValueCallBacks);
175    pass_dict = CFDictionaryCreate(NULL,
176				   (const void * *)&kSecValueData,
177				   (const void * *)&password,
178				   1,
179				   &kCFTypeDictionaryKeyCallBacks,
180				   &kCFTypeDictionaryValueCallBacks);
181    status = SecItemUpdate(query, pass_dict);
182    CFRelease(query);
183    CFRelease(pass_dict);
184
185 done:
186    my_CFRelease(&existing_password);
187    return (status);
188}
189
190#else /* TARGET_OS_EMBEDDED */
191#include <Security/SecAccess.h>
192#include <Security/SecACL.h>
193#include <Security/SecTrustedApplication.h>
194#include <Security/SecTrustedApplicationPriv.h>
195#include <Security/AuthorizationTags.h>
196
197const CFStringRef kEAPSecKeychainPropPassword = CFSTR("Password");
198const CFStringRef kEAPSecKeychainPropLabel = CFSTR("Label");
199const CFStringRef kEAPSecKeychainPropDescription = CFSTR("Description");
200const CFStringRef kEAPSecKeychainPropAccount = CFSTR("Account");
201const CFStringRef kEAPSecKeychainPropTrustedApplications = CFSTR("TrustedApplications");
202const CFStringRef kEAPSecKeychainPropAllowRootAccess = CFSTR("AllowRootAccess");
203
204#include <Security/cssmtype.h>
205#include <Security/cssmapple.h>
206#include <Security/SecKeychain.h>
207#include <Security/SecKeychainItem.h>
208
209#define MY_KEYCHAIN_ATTR_MAX		6
210
211typedef struct {
212    SecKeychainAttributeInfo	info;
213    UInt32			tag[MY_KEYCHAIN_ATTR_MAX];
214    UInt32			format[MY_KEYCHAIN_ATTR_MAX];
215} mySecKeychainAttributeInfo;
216
217typedef struct {
218    SecKeychainAttributeList	list;
219    SecKeychainAttribute 	attr[MY_KEYCHAIN_ATTR_MAX];
220} mySecKeychainAttributeList;
221
222typedef struct CFStringSecItemTag {
223    const CFStringRef *	key;
224    UInt32		tag;
225} CFStringSecItemTag;
226
227STATIC const CFStringSecItemTag prop_tag_tbl[] = {
228    { &kEAPSecKeychainPropAccount, kSecAccountItemAttr },
229    { &kEAPSecKeychainPropLabel, kSecLabelItemAttr },
230    { &kEAPSecKeychainPropDescription, kSecDescriptionItemAttr },
231};
232STATIC const int		prop_tag_tbl_size = (sizeof(prop_tag_tbl)
233						     / sizeof(prop_tag_tbl[0]));
234
235STATIC void
236mySecKeychainAttributeInfoInit(mySecKeychainAttributeInfo * attr_info)
237{
238    attr_info->info.count = 0;
239    attr_info->info.tag = attr_info->tag;
240    attr_info->info.format = attr_info->format;
241    return;
242}
243
244STATIC Boolean
245mySecKeychainAttributeInfoAdd(mySecKeychainAttributeInfo * attr_info,
246			      UInt32 tag)
247{
248    int		count = attr_info->info.count;
249
250    if (count >= MY_KEYCHAIN_ATTR_MAX) {
251	EAPLOG_FL(LOG_NOTICE, "Trying to add attribute %d but list is full",
252		  (int)tag);
253	return (FALSE);
254    }
255    attr_info->format[count] = CSSM_DB_ATTRIBUTE_FORMAT_BLOB;
256    attr_info->tag[count] = tag;
257    attr_info->info.count++;
258    return (TRUE);
259}
260
261STATIC void
262mySecKeychainAttributeListInit(mySecKeychainAttributeList * attr_list)
263{
264    attr_list->list.count = 0;
265    attr_list->list.attr = attr_list->attr;
266    return;
267}
268
269STATIC Boolean
270mySecKeychainAttributeListAdd(mySecKeychainAttributeList * attr_list,
271			      UInt32 attr_tag, CFDataRef attr_data)
272{
273    SecKeychainAttribute * 	attr;
274    int				count = attr_list->list.count;
275
276    if (count >= MY_KEYCHAIN_ATTR_MAX) {
277	EAPLOG_FL(LOG_NOTICE, "Trying to add attribute %d but list is full",
278		  (int)attr_tag);
279	return (FALSE);
280    }
281    attr = attr_list->attr + count;
282    attr->tag = attr_tag;
283    attr->length = CFDataGetLength(attr_data);
284    attr->data = (void *)CFDataGetBytePtr(attr_data);
285    attr_list->list.count++;
286    return (TRUE);
287}
288
289STATIC Boolean
290mySecKeychainAttributeListAddFromDict(mySecKeychainAttributeList * attr_list,
291				      CFDictionaryRef attrs)
292{
293    int				i;
294    Boolean			ret = TRUE;
295    const CFStringSecItemTag *	tbl;
296
297    for (i = 0, tbl = prop_tag_tbl; i < prop_tag_tbl_size; i++, tbl++) {
298	CFDataRef	val = CFDictionaryGetValue(attrs, *tbl->key);
299
300	if (val != NULL) {
301	    ret = mySecKeychainAttributeListAdd(attr_list, tbl->tag, val);
302	    if (ret == FALSE) {
303		EAPLOG_FL(LOG_NOTICE, "mySecKeychainAttributeListAddFromDict() "
304			  "failed to add %d", (int)tbl->tag);
305		break;
306	    }
307	}
308    }
309    return (ret);
310}
311
312PRIVATE_EXTERN OSStatus
313EAPSecAccessCreateWithTrustedApplications(CFArrayRef trusted_apps,
314					  CFDataRef label_data,
315					  SecAccessRef * ret_access)
316{
317    CFStringRef		label = NULL;
318    OSStatus		status;
319
320    if (label_data != NULL) {
321	label = my_CFStringCreateWithData(label_data);
322    }
323    else {
324	label = CFSTR("--unspecified--");
325	CFRetain(label);
326    }
327    status = SecAccessCreate(label, trusted_apps, ret_access);
328    my_CFRelease(&label);
329    return (status);
330}
331
332PRIVATE_EXTERN OSStatus
333EAPSecKeychainItemSetAccessForTrustedApplications(SecKeychainItemRef item,
334						  CFArrayRef trusted_apps)
335{
336    CFArrayRef 		app_list = NULL;
337    CFMutableArrayRef	app_list_data = NULL;
338    SecACLRef		acl;
339    CFArrayRef		acl_list = NULL;
340    SecAccessRef	access = NULL;
341    CFStringRef 	prompt_description = NULL;
342    SecKeychainPromptSelector prompt_selector;
343    OSStatus		status;
344
345    status = SecKeychainItemCopyAccess(item, &access);
346    if (status != noErr) {
347	status = SecAccessCreate(CFSTR("--unspecified--"),
348				 trusted_apps, &access);
349	if (status != noErr) {
350	    goto done;
351	}
352    }
353    else {
354	acl_list
355	    = SecAccessCopyMatchingACLList(access, kSecACLAuthorizationDecrypt);
356	if (acl_list == NULL) {
357	    status = errSecAllocate;
358	    goto done;
359	}
360	acl = (SecACLRef)CFArrayGetValueAtIndex(acl_list, 0);
361	status = SecACLCopyContents(acl, &app_list, &prompt_description,
362				    &prompt_selector);
363	if (status == noErr && app_list != NULL) {
364	    int			count;
365	    int			i;
366	    CFRange		r;
367	    CFMutableArrayRef	new_list = NULL;
368
369	    r.location = 0;
370	    r.length = CFArrayGetCount(app_list);
371	    app_list_data = CFArrayCreateMutable(NULL, r.length,
372						 &kCFTypeArrayCallBacks);
373	    for (i = 0; i < r.length; i++) {
374		SecTrustedApplicationRef	app;
375		CFDataRef			data = NULL;
376
377		app = (SecTrustedApplicationRef)
378		    CFArrayGetValueAtIndex(app_list, i);
379		(void)SecTrustedApplicationCopyExternalRepresentation(app,
380								      &data);
381		if (data != NULL) {
382		    CFArrayAppendValue(app_list_data, data);
383		    CFRelease(data);
384		}
385	    }
386	    /* in case it changed */
387	    r.length = CFArrayGetCount(app_list_data);
388	    count = CFArrayGetCount(trusted_apps);
389	    for (i = 0; i < count; i++) {
390		bool				already_there;
391		SecTrustedApplicationRef	app;
392		CFDataRef			data = NULL;
393
394		app = (SecTrustedApplicationRef)
395		    CFArrayGetValueAtIndex(trusted_apps, i);
396
397		(void)SecTrustedApplicationCopyExternalRepresentation(app,
398								      &data);
399		if (data == NULL) {
400		    continue;
401		}
402		already_there = CFArrayContainsValue(app_list_data, r, data);
403		CFRelease(data);
404		if (already_there) {
405		    continue;
406		}
407		if (new_list == NULL) {
408		    new_list = CFArrayCreateMutableCopy(NULL, 0, app_list);
409		}
410		CFArrayAppendValue(new_list, app);
411	    }
412	    if (new_list == NULL) {
413		/* no modifications required */
414		goto done;
415	    }
416	    status = SecACLSetContents(acl, new_list, prompt_description,
417				       prompt_selector);
418	    CFRelease(new_list);
419	    if (status != noErr) {
420		goto done;
421	    }
422	}
423	else {
424	    status = SecACLSetContents(acl, trusted_apps,
425				       prompt_description,
426				       prompt_selector);
427	    if (status != noErr) {
428		goto done;
429	    }
430	}
431    }
432    status = SecKeychainItemSetAccess(item, access);
433
434 done:
435    my_CFRelease(&access);
436    my_CFRelease(&app_list);
437    my_CFRelease(&app_list_data);
438    my_CFRelease(&prompt_description);
439    my_CFRelease(&acl_list);
440    return (status);
441}
442
443STATIC OSStatus
444KeychainPasswordItemCopy(SecKeychainRef keychain,
445			 CFStringRef unique_id_str,
446			 SecKeychainItemRef * ret_item)
447{
448    SecKeychainItemRef	item;
449    OSStatus		status;
450    CFDataRef		unique_id;
451
452    unique_id = CFStringCreateExternalRepresentation(NULL,
453						     unique_id_str,
454						     kCFStringEncodingUTF8,
455						     0);
456    status
457	= SecKeychainFindGenericPassword(keychain,
458					 CFDataGetLength(unique_id),
459					 (const char *)CFDataGetBytePtr(unique_id),
460					 0,
461					 NULL,
462					 NULL,
463					 NULL,
464					 &item);
465    CFRelease(unique_id);
466    *ret_item = NULL;
467    if (status != noErr) {
468#if TEST_EAPKEYCHAINUTIL
469	fprintf(stderr, "SecKeychainFindGenericPassword failed: %s (%d)\n",
470		EAPSecurityErrorString(status), (int)status);
471#endif /* TEST_EAPKEYCHAINUTIL */
472    }
473    else {
474	*ret_item = item;
475    }
476    return (status);
477}
478
479STATIC CFStringRef
480SecKeychainAttrTypeGetCFString(SecKeychainAttrType type)
481{
482    int				i;
483    const CFStringSecItemTag *	tbl;
484
485    for (i = 0, tbl = prop_tag_tbl; i < prop_tag_tbl_size; i++, tbl++) {
486	if (tbl->tag == type) {
487	    return (*tbl->key);
488	}
489    }
490    return (NULL);
491}
492
493STATIC OSStatus
494KeychainItemCopyInfo(SecKeychainItemRef item,
495		     CFArrayRef keys,
496		     CFDictionaryRef * ret_attrs)
497{
498    CFMutableDictionaryRef 	attrs = NULL;
499    int				i;
500    void *			password_data = NULL;
501    UInt32 			password_length = 0;
502    void * *			password_data_p;
503    UInt32 *			password_length_p;
504    CFRange			range = CFRangeMake(0, CFArrayGetCount(keys));
505    mySecKeychainAttributeInfo 	req_attr_info;
506    SecKeychainAttributeInfo * 	req_attr_info_p;
507    SecKeychainAttributeList * 	ret_attr_list = NULL;
508    SecKeychainAttributeList * *ret_attr_list_p;
509    OSStatus			status;
510
511    mySecKeychainAttributeInfoInit(&req_attr_info);
512    if (CFArrayContainsValue(keys, range, kEAPSecKeychainPropAccount)) {
513	mySecKeychainAttributeInfoAdd(&req_attr_info,
514				      kSecAccountItemAttr);
515    }
516    if (CFArrayContainsValue(keys, range, kEAPSecKeychainPropLabel)) {
517	mySecKeychainAttributeInfoAdd(&req_attr_info,
518				      kSecLabelItemAttr);
519    }
520    if (CFArrayContainsValue(keys, range, kEAPSecKeychainPropDescription)) {
521	mySecKeychainAttributeInfoAdd(&req_attr_info,
522				      kSecDescriptionItemAttr);
523    }
524    if (CFArrayContainsValue(keys, range, kEAPSecKeychainPropPassword)) {
525	password_data_p = &password_data;
526	password_length_p = &password_length;
527    }
528    else {
529	password_data_p = NULL;
530	password_length_p = NULL;
531    }
532    if (req_attr_info.info.count != 0) {
533	req_attr_info_p = &req_attr_info.info;
534	ret_attr_list_p = &ret_attr_list;
535    }
536    else {
537	req_attr_info_p = NULL;
538	ret_attr_list_p = NULL;
539    }
540    status = SecKeychainItemCopyAttributesAndData(item,
541						  req_attr_info_p,
542						  NULL,
543						  ret_attr_list_p,
544						  password_length_p,
545						  password_data_p);
546    if (status != noErr) {
547	goto done;
548    }
549    attrs = CFDictionaryCreateMutable(NULL, 0,
550				      &kCFTypeDictionaryKeyCallBacks,
551				      &kCFTypeDictionaryValueCallBacks);
552    if (password_data != NULL && password_length != 0) {
553	CFDataRef	password;
554
555	password = CFDataCreate(NULL, password_data, password_length);
556	CFDictionarySetValue(attrs, kEAPSecKeychainPropPassword, password);
557	CFRelease(password);
558    }
559    if (ret_attr_list != NULL) {
560	SecKeychainAttribute *	attr = ret_attr_list->attr;
561
562	for (i = 0; i < ret_attr_list->count; i++, attr++) {
563	    CFStringRef		key;
564	    CFDataRef		val;
565
566	    if (attr->data == NULL || attr->length == 0) {
567		continue;
568	    }
569	    key = SecKeychainAttrTypeGetCFString(attr->tag);
570	    if (key == NULL) {
571		/* shouldn't happen */
572		continue;
573	    }
574	    val = CFDataCreate(NULL, attr->data, attr->length);
575	    CFDictionarySetValue(attrs, key, val);
576	    CFRelease(val);
577	}
578    }
579    SecKeychainItemFreeAttributesAndData(ret_attr_list, password_data);
580
581 done:
582    *ret_attrs = attrs;
583    return (status);
584
585}
586
587OSStatus
588EAPSecKeychainPasswordItemRemove(SecKeychainRef keychain,
589				 CFStringRef unique_id_str)
590{
591    SecKeychainItemRef	item;
592    OSStatus		status;
593
594    status = KeychainPasswordItemCopy(keychain, unique_id_str, &item);
595    if (status != noErr) {
596	goto done;
597    }
598    status = SecKeychainItemDelete(item);
599    CFRelease(item);
600    if (status != noErr) {
601	EAPLOG_FL(LOG_NOTICE, "SecKeychainItemDelete() failed: %s (%d)",
602		  EAPSecurityErrorString(status), (int)status);
603
604    }
605 done:
606    return (status);
607}
608
609OSStatus
610EAPSecKeychainPasswordItemCopy2(SecKeychainRef keychain,
611				CFStringRef unique_id_str,
612				CFArrayRef keys,
613				CFDictionaryRef * ret_values)
614{
615    SecKeychainItemRef		item = NULL;
616    OSStatus			status;
617
618    *ret_values = NULL;
619    status = KeychainPasswordItemCopy(keychain, unique_id_str, &item);
620    if (status == noErr) {
621	status = KeychainItemCopyInfo(item, keys, ret_values);
622    }
623    if (item != NULL) {
624	CFRelease(item);
625    }
626    return (status);
627}
628
629OSStatus
630EAPSecKeychainPasswordItemCopy(SecKeychainRef keychain,
631			       CFStringRef unique_id_str,
632			       CFDataRef * ret_password)
633{
634    CFDataRef			password;
635    CFDictionaryRef		props;
636    CFArrayRef			req_props;
637    OSStatus			status;
638
639    *ret_password = NULL;
640    req_props = CFArrayCreate(NULL,
641			      (const void * *)&kEAPSecKeychainPropPassword, 1,
642			      &kCFTypeArrayCallBacks);
643    status = EAPSecKeychainPasswordItemCopy2(keychain,
644					     unique_id_str,
645					     req_props,
646					     &props);
647    CFRelease(req_props);
648    if (status != noErr) {
649	return (status);
650    }
651    password = CFDictionaryGetValue(props, kEAPSecKeychainPropPassword);
652    if (password == NULL) {
653	status = errSecItemNotFound;
654    }
655    else {
656	*ret_password = CFRetain(password);
657    }
658    CFRelease(props);
659    return (status);
660}
661
662OSStatus
663EAPSecKeychainPasswordItemCreateWithAccess(SecKeychainRef keychain,
664					   SecAccessRef access,
665					   CFStringRef unique_id_str,
666					   CFDataRef label,
667					   CFDataRef description,
668					   CFDataRef user,
669					   CFDataRef password)
670{
671    mySecKeychainAttributeList	attr_list;
672    OSStatus			status;
673    CFDataRef			unique_id;
674
675    unique_id = CFStringCreateExternalRepresentation(NULL,
676						     unique_id_str,
677						     kCFStringEncodingUTF8,
678						     0);
679    mySecKeychainAttributeListInit(&attr_list);
680    mySecKeychainAttributeListAdd(&attr_list, kSecServiceItemAttr, unique_id);
681    if (label != NULL) {
682	mySecKeychainAttributeListAdd(&attr_list, kSecLabelItemAttr, label);
683    }
684    if (description != NULL) {
685	mySecKeychainAttributeListAdd(&attr_list, kSecDescriptionItemAttr,
686				      description);
687    }
688    if (user != NULL) {
689	mySecKeychainAttributeListAdd(&attr_list, kSecAccountItemAttr, user);
690    }
691    status = SecKeychainItemCreateFromContent(kSecGenericPasswordItemClass,
692					      &attr_list.list,
693					      CFDataGetLength(password),
694					      CFDataGetBytePtr(password),
695					      keychain,
696					      access,
697					      NULL);
698    CFRelease(unique_id);
699    return (status);
700}
701
702OSStatus
703EAPSecKeychainPasswordItemCreate(SecKeychainRef keychain,
704				 CFStringRef unique_id_str,
705				 CFDictionaryRef attrs)
706{
707    CFBooleanRef		allow_root;
708    SecAccessRef		access = NULL;
709    mySecKeychainAttributeList	attr_list;
710    CFDataRef 			password;
711    void *			password_data;
712    UInt32			password_length;
713    OSStatus			status;
714    CFArrayRef			trusted_apps;
715    CFDataRef			unique_id = NULL;
716
717    password = CFDictionaryGetValue(attrs, kEAPSecKeychainPropPassword);
718    if (password != NULL) {
719	/* set it */
720	password_length = CFDataGetLength(password);
721	password_data = (void *)CFDataGetBytePtr(password);
722    }
723    else {
724	/* don't set it */
725	password_length = 0;
726	password_data = NULL;
727    }
728    trusted_apps = CFDictionaryGetValue(attrs,
729					kEAPSecKeychainPropTrustedApplications);
730    allow_root = CFDictionaryGetValue(attrs,
731				      kEAPSecKeychainPropAllowRootAccess);
732    if (trusted_apps != NULL) {
733	CFDataRef	label_data;
734
735	label_data = CFDictionaryGetValue(attrs,
736					  kEAPSecKeychainPropLabel);
737	status = EAPSecAccessCreateWithTrustedApplications(trusted_apps,
738							   label_data,
739							   &access);
740	if (status != noErr) {
741	    EAPLOG_FL(LOG_NOTICE,
742		      "failed to get SecAccess for Trusted Apps, %d",
743		      (int)status);
744	    goto done;
745	}
746    }
747    else if (allow_root != NULL && CFBooleanGetValue(allow_root)) {
748	CFErrorRef	error;
749
750	access = SecAccessCreateWithOwnerAndACL(0, 0, kSecUseOnlyUID,
751						NULL, &error);
752	if (access == NULL) {
753	    status = errSecAllocate;
754	    if (error != NULL) {
755		EAPLOG_FL(LOG_NOTICE,
756			  "SecAccessCreateWithOwnerAndACL() failed %d",
757			  (int)CFErrorGetCode(error));
758		CFRelease(error);
759	    }
760	    goto done;
761	}
762    }
763    mySecKeychainAttributeListInit(&attr_list);
764    unique_id = CFStringCreateExternalRepresentation(NULL,
765						     unique_id_str,
766						     kCFStringEncodingUTF8,
767						     0);
768    mySecKeychainAttributeListAdd(&attr_list, kSecServiceItemAttr, unique_id);
769    if (mySecKeychainAttributeListAddFromDict(&attr_list, attrs) == FALSE) {
770	status = errSecBufferTooSmall;
771    }
772    else {
773	status = SecKeychainItemCreateFromContent(kSecGenericPasswordItemClass,
774						  &attr_list.list,
775						  password_length,
776						  password_data,
777						  keychain,
778						  access,
779						  NULL);
780    }
781
782 done:
783    my_CFRelease(&unique_id);
784    my_CFRelease(&access);
785    return (status);
786}
787
788OSStatus
789EAPSecKeychainPasswordItemCreateUnique(SecKeychainRef keychain,
790				       CFDictionaryRef attrs,
791				       CFStringRef * ret_unique_id)
792{
793    OSStatus		status;
794    CFStringRef		unique_id_str;
795
796    unique_id_str = my_CFUUIDStringCreate(NULL);
797    status = EAPSecKeychainPasswordItemCreate(keychain,
798					      unique_id_str,
799					      attrs);
800    if (status == noErr	&& ret_unique_id != NULL) {
801	*ret_unique_id = unique_id_str;
802    }
803    else {
804	if (ret_unique_id != NULL) {
805	    *ret_unique_id = NULL;
806	}
807	CFRelease(unique_id_str);
808    }
809    return (status);
810}
811
812OSStatus
813EAPSecKeychainPasswordItemSet(SecKeychainRef keychain,
814			      CFStringRef unique_id_str,
815			      CFDataRef password)
816{
817    SecKeychainItemRef		item;
818    OSStatus			status;
819
820    status = KeychainPasswordItemCopy(keychain, unique_id_str, &item);
821    if (status != noErr) {
822	return (status);
823    }
824    status = SecKeychainItemModifyAttributesAndData(item,
825						    NULL,
826						    CFDataGetLength(password),
827						    CFDataGetBytePtr(password));
828    CFRelease(item);
829    return (status);
830}
831
832OSStatus
833EAPSecKeychainPasswordItemSet2(SecKeychainRef keychain,
834			       CFStringRef unique_id_str,
835			       CFDictionaryRef attrs)
836{
837    mySecKeychainAttributeList	attr_list;
838    SecKeychainItemRef		item;
839    CFDataRef 			password;
840    void *			password_data;
841    UInt32			password_length;
842    OSStatus			status;
843
844    status = KeychainPasswordItemCopy(keychain, unique_id_str, &item);
845    if (status != noErr) {
846	return (status);
847    }
848    mySecKeychainAttributeListInit(&attr_list);
849    if (mySecKeychainAttributeListAddFromDict(&attr_list, attrs) == FALSE) {
850	status = errSecBufferTooSmall;
851	goto done;
852    }
853    password = CFDictionaryGetValue(attrs, kEAPSecKeychainPropPassword);
854    if (password != NULL) {
855	/* set it */
856	password_length = CFDataGetLength(password);
857	password_data = (void *)CFDataGetBytePtr(password);
858    }
859    else {
860	/* don't set it */
861	password_length = 0;
862	password_data = NULL;
863    }
864    status = SecKeychainItemModifyAttributesAndData(item,
865						    &attr_list.list,
866						    password_length,
867						    password_data);
868 done:
869    CFRelease(item);
870    return (status);
871}
872
873#endif /* TARGET_OS_EMBEDDED */
874
875OSStatus
876EAPSecKeychainPasswordItemCreateUniqueWithAccess(SecKeychainRef keychain,
877						 SecAccessRef access,
878						 CFDataRef label,
879						 CFDataRef description,
880						 CFDataRef user,
881						 CFDataRef password,
882						 CFStringRef * ret_unique_id)
883{
884    OSStatus		status;
885    CFStringRef		unique_id_str;
886
887    unique_id_str = my_CFUUIDStringCreate(NULL);
888    status = EAPSecKeychainPasswordItemCreateWithAccess(keychain,
889							access,
890							unique_id_str,
891							label,
892							description,
893							user,
894							password);
895    if (status == noErr	&& ret_unique_id != NULL) {
896	*ret_unique_id = unique_id_str;
897    }
898    else {
899	if (ret_unique_id != NULL) {
900	    *ret_unique_id = NULL;
901	}
902	CFRelease(unique_id_str);
903    }
904    return (status);
905}
906
907#ifdef TEST_EAPKEYCHAINUTIL
908
909#if ! TARGET_OS_EMBEDDED
910/*
911 * Create a SecAccessRef with a custom form.
912 * Both the owner and the ACL set allow free access to root,
913 * but nothing to anyone else.
914 * NOTE: This is not the easiest way to build up CSSM data structures.
915 * But it's a way that does not depend on any outside software layers
916 * (other than CSSM and Security's Sec* layer, of course).
917 */
918static OSStatus
919SecKeychainCopySystemKeychain(SecKeychainRef * ret_keychain)
920{
921    SecKeychainRef		keychain = NULL;
922    OSStatus			status;
923
924    status = SecKeychainCopyDomainDefault(kSecPreferencesDomainSystem,
925					  &keychain);
926    if (status != noErr && keychain != NULL) {
927	CFRelease(keychain);
928	keychain = NULL;
929    }
930    *ret_keychain = keychain;
931    return (status);
932}
933
934#endif /* ! TARGET_OS_EMBEDDED */
935
936#if TARGET_OS_EMBEDDED
937#define SYS_OPT			" "
938#else /* TARGET_OS_EMBEDDED */
939#define SYS_OPT			" [system] "
940#define HAS_KEYCHAINS
941#endif /* TARGET_OS_EMBEDDED */
942
943static void
944usage(const char * progname)
945{
946    fprintf(stderr, "%s" SYS_OPT
947	    "create <label> <description> <username> <password>\n",
948	    progname);
949    fprintf(stderr,
950	    "%s" SYS_OPT "set <unique_id> <username> <password>\n",
951	    progname);
952    fprintf(stderr, "%s" SYS_OPT "remove <unique_id>\n", progname);
953    fprintf(stderr, "%s" SYS_OPT "get <unique_id>\n", progname);
954    exit(1);
955}
956
957typedef enum {
958    kCommandUnknown,
959    kCommandCreate,
960    kCommandRemove,
961    kCommandSet,
962    kCommandGet
963} Command;
964
965#define keapolclientPath		"/System/Library/SystemConfiguration/EAPOLController.bundle/Contents/Resources/eapolclient"
966#define kAirPortApplicationGroup	"AirPort"
967#define kSystemUIServerPath 		"/System/Library/CoreServices/SystemUIServer.app"
968
969STATIC CFArrayRef
970copy_trusted_applications(void)
971{
972    CFMutableArrayRef		array;
973    SecTrustedApplicationRef	trusted_app;
974    OSStatus			status;
975
976    array = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
977
978    /* this executable */
979    status = SecTrustedApplicationCreateFromPath(NULL, &trusted_app);
980    if (status != noErr) {
981	fprintf(stderr,
982		"SecTrustedApplicationCreateFromPath(NULL) failed, %d\n",
983		status);
984    }
985    else {
986	CFArrayAppendValue(array, trusted_app);
987	CFRelease(trusted_app);
988    }
989
990    /* eapolclient */
991    status = SecTrustedApplicationCreateFromPath(keapolclientPath,
992						 &trusted_app);
993    if (status != noErr) {
994	fprintf(stderr,
995		"SecTrustedApplicationCreateFromPath(%s) failed, %d\n",
996		keapolclientPath,
997		status);
998    }
999    else {
1000	CFArrayAppendValue(array, trusted_app);
1001	CFRelease(trusted_app);
1002    }
1003
1004
1005    /* SystemUIServer */
1006    status = SecTrustedApplicationCreateFromPath(kSystemUIServerPath,
1007						 &trusted_app);
1008    if (status != noErr) {
1009	fprintf(stderr,
1010		"SecTrustedApplicationCreateFromPath(%s) failed, %d\n",
1011		kSystemUIServerPath,
1012		status);
1013    }
1014    else {
1015	CFArrayAppendValue(array, trusted_app);
1016	CFRelease(trusted_app);
1017    }
1018
1019    /* AirPort Application Group */
1020    status
1021	= SecTrustedApplicationCreateApplicationGroup(kAirPortApplicationGroup,
1022						      NULL, &trusted_app);
1023    if (status != noErr) {
1024	fprintf(stderr,
1025		"SecTrustedApplicationCreateApplicationGroup("
1026		kAirPortApplicationGroup ") failed, %d\n",
1027		status);
1028    }
1029    else {
1030	CFArrayAppendValue(array, trusted_app);
1031	CFRelease(trusted_app);
1032    }
1033    if (CFArrayGetCount(array) == 0) {
1034	my_CFRelease(&array);
1035    }
1036    return (array);
1037}
1038
1039int
1040main(int argc, const char *argv[])
1041{
1042    Command		cmd = kCommandUnknown;
1043    SecKeychainRef	keychain = NULL;
1044    const char *	progname = argv[0];
1045    OSStatus		status;
1046#ifdef HAS_KEYCHAINS
1047    bool		use_system = FALSE;
1048#endif
1049
1050    if (argc < 2) {
1051	usage(progname);
1052    }
1053#ifdef HAS_KEYCHAINS
1054    if (strcmp(argv[1], "system") == 0) {
1055	if (argc < 3) {
1056	    usage(progname);
1057	}
1058	use_system = TRUE;
1059	status = SecKeychainCopySystemKeychain(&keychain);
1060	if (status != noErr) {
1061	    fprintf(stderr, "SecKeychainCopySystemKeychain failed, %s (%d)\n",
1062		    EAPSecurityErrorString(status), (int)status);
1063	    exit(1);
1064	}
1065	argc--;
1066	argv++;
1067    }
1068#endif /* HAS_KEYCHAINS */
1069
1070    if (strcmp(argv[1], "create") == 0) {
1071	if (argc < 6) {
1072	    usage(progname);
1073	}
1074	cmd = kCommandCreate;
1075    }
1076    else if (strcmp(argv[1], "set") == 0) {
1077	if (argc < 5) {
1078	    usage(progname);
1079	}
1080	cmd = kCommandSet;
1081    }
1082    else if (strcmp(argv[1], "get") == 0) {
1083	if (argc < 3) {
1084	    usage(progname);
1085	}
1086	cmd = kCommandGet;
1087    }
1088    else if (strcmp(argv[1], "remove") == 0) {
1089	if (argc < 3) {
1090	    usage(progname);
1091	}
1092	cmd = kCommandRemove;
1093    }
1094    else {
1095	usage(progname);
1096    }
1097    switch (cmd) {
1098    case kCommandSet: {
1099	CFMutableDictionaryRef 	attrs = NULL;
1100	CFDataRef			password;
1101	CFArrayRef			trusted_apps;
1102	CFStringRef			unique_id_str;
1103	CFDataRef			username;
1104
1105	unique_id_str
1106	    = CFStringCreateWithCStringNoCopy(NULL,
1107					      argv[2],
1108					      kCFStringEncodingUTF8,
1109					      kCFAllocatorNull);
1110	username
1111	    = CFDataCreateWithBytesNoCopy(NULL,
1112					  (const UInt8 *)argv[3],
1113					  strlen(argv[3]),
1114					  kCFAllocatorNull);
1115	password
1116	    = CFDataCreateWithBytesNoCopy(NULL,
1117					  (const UInt8 *)argv[4],
1118					  strlen(argv[4]),
1119					  kCFAllocatorNull);
1120	attrs = CFDictionaryCreateMutable(NULL, 0,
1121					  &kCFTypeDictionaryKeyCallBacks,
1122					  &kCFTypeDictionaryValueCallBacks);
1123	CFDictionarySetValue(attrs, kEAPSecKeychainPropAccount,
1124			     username);
1125	CFDictionarySetValue(attrs, kEAPSecKeychainPropPassword,
1126			     password);
1127	trusted_apps = copy_trusted_applications();
1128	CFDictionarySetValue(attrs, kEAPSecKeychainPropTrustedApplications,
1129			     trusted_apps);
1130	CFRelease(trusted_apps);
1131	status = EAPSecKeychainPasswordItemSet2(keychain,
1132						unique_id_str,
1133						attrs);
1134	if (status != noErr) {
1135	    fprintf(stderr, "EAPSecKeychainItemSet2 failed %s (%d)\n",
1136		    EAPSecurityErrorString(status), (int)status);
1137	    exit(2);
1138	}
1139	my_CFRelease(&password);
1140	break;
1141    }
1142    case kCommandCreate: {
1143	SecAccessRef			access = NULL;
1144	CFMutableDictionaryRef	attrs = NULL;
1145	CFDataRef			data;
1146	CFArrayRef			trusted_apps;
1147	CFDataRef			unique_id;
1148	CFStringRef			unique_id_str = NULL;
1149
1150#ifdef HAS_KEYCHAINS
1151	if (use_system) {
1152	    CFErrorRef	error;
1153
1154	    access = SecAccessCreateWithOwnerAndACL(0, 0, kSecUseOnlyUID,
1155						    NULL, &error);
1156	    if (access == NULL) {
1157		if (error != NULL) {
1158		    fprintf(stderr,
1159			    "SecAccessCreateWithOwnerAndACL() failed %d\n",
1160			    (int)CFErrorGetCode(error));
1161		}
1162		exit(2);
1163	    }
1164	}
1165	else {
1166	    status = SecAccessCreate(CFSTR("keychain"), NULL, &access);
1167	    if (status != noErr) {
1168		fprintf(stderr, "SecAccessCreate failed, %s (%d)\n",
1169			EAPSecurityErrorString(status), (int)status);
1170		exit(2);
1171	    }
1172	}
1173#endif /* HAS_KEYCHAINS */
1174
1175	attrs = CFDictionaryCreateMutable(NULL, 0,
1176					  &kCFTypeDictionaryKeyCallBacks,
1177					  &kCFTypeDictionaryValueCallBacks);
1178
1179	/* label */
1180	data = CFDataCreateWithBytesNoCopy(NULL,
1181					   (const UInt8 *)argv[2],
1182					   strlen(argv[2]),
1183					   kCFAllocatorNull);
1184	CFDictionarySetValue(attrs, kEAPSecKeychainPropLabel, data);
1185	CFRelease(data);
1186
1187	/* description */
1188	data = CFDataCreateWithBytesNoCopy(NULL,
1189					   (const UInt8 *)argv[3],
1190					   strlen(argv[3]),
1191					   kCFAllocatorNull);
1192	CFDictionarySetValue(attrs, kEAPSecKeychainPropDescription,
1193			     data);
1194	CFRelease(data);
1195
1196	/* name */
1197	data = CFDataCreateWithBytesNoCopy(NULL,
1198					   (const UInt8 *)argv[4],
1199					   strlen(argv[4]),
1200					   kCFAllocatorNull);
1201	CFDictionarySetValue(attrs, kEAPSecKeychainPropAccount,
1202			     data);
1203	CFRelease(data);
1204
1205	/* password */
1206	data = CFDataCreateWithBytesNoCopy(NULL,
1207					   (const UInt8 *)argv[5],
1208					   strlen(argv[5]),
1209					   kCFAllocatorNull);
1210	CFDictionarySetValue(attrs, kEAPSecKeychainPropPassword,
1211			     data);
1212	CFRelease(data);
1213
1214	/* trusted apps */
1215	trusted_apps = copy_trusted_applications();
1216	CFDictionarySetValue(attrs, kEAPSecKeychainPropTrustedApplications,
1217			     trusted_apps);
1218	CFRelease(trusted_apps);
1219
1220	if (argc > 6) {
1221	    unique_id_str
1222		= CFStringCreateWithCStringNoCopy(NULL,
1223						  argv[6],
1224						  kCFStringEncodingUTF8,
1225						  kCFAllocatorNull);
1226	    status
1227		= EAPSecKeychainPasswordItemCreate(keychain,
1228						   unique_id_str,
1229						   attrs);
1230	    if (status != noErr) {
1231		fprintf(stderr, "EAPSecKeychainItemCreate failed,"
1232			" %s (%d)\n",
1233			EAPSecurityErrorString(status), (int)status);
1234		exit(1);
1235	    }
1236	}
1237	else {
1238	    status
1239		= EAPSecKeychainPasswordItemCreateUnique(keychain,
1240							 attrs,
1241							 &unique_id_str);
1242
1243	    if (status != noErr) {
1244		fprintf(stderr,
1245			"EAPSecKeychainItemCreateUniqueWithAccessfailed,"
1246			" %s (%d)\n",
1247			EAPSecurityErrorString(status), (int)status);
1248		exit(1);
1249	    }
1250	}
1251	unique_id
1252	    = CFStringCreateExternalRepresentation(NULL,
1253						   unique_id_str,
1254						   kCFStringEncodingUTF8,
1255						   0);
1256	fwrite(CFDataGetBytePtr(unique_id),
1257	       CFDataGetLength(unique_id), 1, stdout);
1258	printf("\n");
1259	CFRelease(attrs);
1260	break;
1261    }
1262    case kCommandGet:
1263    case kCommandRemove: {
1264	CFStringRef	unique_id_str;
1265
1266	unique_id_str
1267	    = CFStringCreateWithCStringNoCopy(NULL,
1268					      argv[2],
1269					      kCFStringEncodingUTF8,
1270					      kCFAllocatorNull);
1271	if (cmd == kCommandRemove) {
1272	    status
1273		= EAPSecKeychainPasswordItemRemove(keychain, unique_id_str);
1274	    if (status != noErr) {
1275		fprintf(stderr,
1276			"EAPSecKeychainItemRemove failed, %s (%d)\n",
1277			EAPSecurityErrorString(status), (int)status);
1278		exit(2);
1279	    }
1280	}
1281	else {
1282	    CFDictionaryRef	attrs;
1283	    const void *	keys[] = {
1284		kEAPSecKeychainPropPassword,
1285		kEAPSecKeychainPropAccount,
1286		kEAPSecKeychainPropLabel,
1287		kEAPSecKeychainPropDescription
1288	    };
1289	    int		keys_count = sizeof(keys) / sizeof(keys[0]);
1290	    CFDataRef		password = NULL;
1291	    CFArrayRef	req_props;
1292	    OSStatus		status;
1293	    CFDataRef		username = NULL;
1294
1295	    req_props = CFArrayCreate(NULL,
1296				      keys, keys_count,
1297				      &kCFTypeArrayCallBacks);
1298	    status = EAPSecKeychainPasswordItemCopy2(keychain,
1299						     unique_id_str,
1300						     req_props,
1301						     &attrs);
1302	    CFRelease(req_props);
1303	    if (status != noErr) {
1304		fprintf(stderr,
1305			"EAPSecKeychainPasswordItemCopy2 failed, %s (%d)\n",
1306			EAPSecurityErrorString(status), (int)status);
1307		exit(2);
1308	    }
1309	    CFShow(attrs);
1310	    password = CFDictionaryGetValue(attrs,
1311					    kEAPSecKeychainPropPassword);
1312	    username = CFDictionaryGetValue(attrs,
1313					    kEAPSecKeychainPropAccount);
1314	    if (password != NULL) {
1315		printf("Password = '");
1316		fwrite(CFDataGetBytePtr(password),
1317		       CFDataGetLength(password), 1, stdout);
1318		printf("'\n");
1319	    }
1320	    if (username != NULL) {
1321		printf("Name = '");
1322		fwrite(CFDataGetBytePtr(username),
1323		       CFDataGetLength(username), 1, stdout);
1324		printf("'\n");
1325	    }
1326	    CFRelease(attrs);
1327	}
1328	CFRelease(unique_id_str);
1329    }
1330    default:
1331	break;
1332    }
1333    if (keychain != NULL) {
1334	CFRelease(keychain);
1335    }
1336    exit(0);
1337    return (0);
1338}
1339
1340#endif /* TEST_EAPKEYCHAINUTIL */
1341