EAPKeychainUtil.c   [plain text]


/*
 * Copyright (c) 2006-2013 Apple Inc. All rights reserved.
 *
 * @APPLE_LICENSE_HEADER_START@
 * 
 * This file contains Original Code and/or Modifications of Original Code
 * as defined in and that are subject to the Apple Public Source License
 * Version 2.0 (the 'License'). You may not use this file except in
 * compliance with the License. Please obtain a copy of the License at
 * http://www.opensource.apple.com/apsl/ and read it before using this
 * file.
 * 
 * The Original Code and all software distributed under the License are
 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
 * Please see the License for the specific language governing rights and
 * limitations under the License.
 * 
 * @APPLE_LICENSE_HEADER_END@
 */

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <string.h>
#include <TargetConditionals.h>
#include <CoreFoundation/CFString.h>
#include <CoreFoundation/CFNumber.h>
#include "symbol_scope.h"
#include "EAPLog.h"
#include "EAPSecurity.h"
#include "EAPKeychainUtil.h"
#include "EAPKeychainUtilInternal.h"
#include "myCFUtil.h"

#if TARGET_OS_EMBEDDED

OSStatus
EAPSecKeychainPasswordItemRemove(SecKeychainRef keychain,
				 CFStringRef unique_id_str)
{
    const void *	keys[] = {
	kSecClass,
	kSecAttrService
    };
    CFDictionaryRef	query;
    OSStatus		status;

    const void *	values[] = {
	kSecClassGenericPassword,
	unique_id_str
    };

    query = CFDictionaryCreate(NULL, keys, values,
			       sizeof(keys) / sizeof(*keys),
			       &kCFTypeDictionaryKeyCallBacks,
			       &kCFTypeDictionaryValueCallBacks);
    status = SecItemDelete(query);
    CFRelease(query);
    if (status != noErr) {
	EAPLOG_FL(LOG_NOTICE, "SecItemDelete failed: %s (%d)", 
		  EAPSecurityErrorString(status), (int)status);
    }
    return (status);
}

OSStatus
EAPSecKeychainPasswordItemCopy(SecKeychainRef keychain, 
			       CFStringRef unique_id_str, 
			       CFDataRef * ret_password)
{
    const void *	keys[] = {
	kSecClass,
	kSecAttrService,
	kSecReturnData
    };
    CFDictionaryRef	query;
    CFTypeRef		results;
    OSStatus		status;
    const void *	values[] = {
	kSecClassGenericPassword,
	unique_id_str,
	kCFBooleanTrue
    };

    query = CFDictionaryCreate(NULL, keys, values,
			       sizeof(keys) / sizeof(*keys),
			       &kCFTypeDictionaryKeyCallBacks,
			       &kCFTypeDictionaryValueCallBacks);
    status = SecItemCopyMatching(query, &results);
    CFRelease(query);
    if (status == noErr) {
	*ret_password = results;
    }
    else {
	*ret_password = NULL;
    }
    return (status);
}

OSStatus
EAPSecKeychainPasswordItemCreateWithAccess(SecKeychainRef keychain,
					   SecAccessRef access,
					   CFStringRef unique_id_str,
					   CFDataRef label,
					   CFDataRef description,
					   CFDataRef user,
					   CFDataRef password)
{
    CFMutableDictionaryRef	attrs;
    OSStatus			status;

    attrs = CFDictionaryCreateMutable(NULL, 0,
				      &kCFTypeDictionaryKeyCallBacks,
				      &kCFTypeDictionaryValueCallBacks);
    CFDictionarySetValue(attrs, kSecClass, kSecClassGenericPassword);
    CFDictionarySetValue(attrs, kSecAttrService, unique_id_str);
    CFDictionarySetValue(attrs, kSecAttrAccessible, kSecAttrAccessibleAfterFirstUnlock);
    if (label != NULL) {
	CFDictionarySetValue(attrs, kSecAttrLabel, label);
    }
    if (description != NULL) {
	CFDictionarySetValue(attrs, kSecAttrDescription, description);
    }
    if (user != NULL) {
	CFDictionarySetValue(attrs, kSecAttrAccount, user);
    }
    CFDictionarySetValue(attrs, kSecValueData, password);
    status = SecItemAdd(attrs, NULL);
    CFRelease(attrs);
    return (status);
}

OSStatus
EAPSecKeychainPasswordItemSet(SecKeychainRef keychain,
			      CFStringRef unique_id_str,
			      CFDataRef password)
{
    const void *	keys[] = {
	kSecClass,
	kSecAttrService,
	kSecReturnData
    };
    CFTypeRef		existing_password = NULL;
    CFDictionaryRef	query;
    CFDictionaryRef	pass_dict;
    OSStatus		status;
    const void *	values[] = {
	kSecClassGenericPassword,
	unique_id_str,
	kCFBooleanTrue
    };

    query = CFDictionaryCreate(NULL, keys, values,
			       sizeof(keys) / sizeof(*keys),
			       &kCFTypeDictionaryKeyCallBacks,
			       &kCFTypeDictionaryValueCallBacks);
    status = SecItemCopyMatching(query, &existing_password);
    CFRelease(query);
    if (status != noErr) {
	goto done;
    }
    if (existing_password != NULL
	&& CFEqual(password, existing_password)) {
	/* nothing to do */
	goto done;
    }
    query = CFDictionaryCreate(NULL, keys, values,
			       (sizeof(keys) / sizeof(*keys)) - 1,
			       &kCFTypeDictionaryKeyCallBacks,
			       &kCFTypeDictionaryValueCallBacks);
    pass_dict = CFDictionaryCreate(NULL,
				   (const void * *)&kSecValueData,
				   (const void * *)&password,
				   1,
				   &kCFTypeDictionaryKeyCallBacks,
				   &kCFTypeDictionaryValueCallBacks);
    status = SecItemUpdate(query, pass_dict);
    CFRelease(query);
    CFRelease(pass_dict);

 done:
    my_CFRelease(&existing_password);
    return (status);
}

#else /* TARGET_OS_EMBEDDED */
#include <Security/SecAccess.h>
#include <Security/SecACL.h>
#include <Security/SecTrustedApplication.h>
#include <Security/SecTrustedApplicationPriv.h>
#include <Security/AuthorizationTags.h>

const CFStringRef kEAPSecKeychainPropPassword = CFSTR("Password");
const CFStringRef kEAPSecKeychainPropLabel = CFSTR("Label");
const CFStringRef kEAPSecKeychainPropDescription = CFSTR("Description");
const CFStringRef kEAPSecKeychainPropAccount = CFSTR("Account");
const CFStringRef kEAPSecKeychainPropTrustedApplications = CFSTR("TrustedApplications");
const CFStringRef kEAPSecKeychainPropAllowRootAccess = CFSTR("AllowRootAccess");

#include <Security/cssmtype.h>
#include <Security/cssmapple.h>
#include <Security/SecKeychain.h>
#include <Security/SecKeychainItem.h>

#define MY_KEYCHAIN_ATTR_MAX		6

typedef struct {
    SecKeychainAttributeInfo	info;
    UInt32			tag[MY_KEYCHAIN_ATTR_MAX];
    UInt32			format[MY_KEYCHAIN_ATTR_MAX];
} mySecKeychainAttributeInfo;

typedef struct {
    SecKeychainAttributeList	list;
    SecKeychainAttribute 	attr[MY_KEYCHAIN_ATTR_MAX];
} mySecKeychainAttributeList;

typedef struct CFStringSecItemTag {
    const CFStringRef *	key;
    UInt32		tag;
} CFStringSecItemTag;

STATIC const CFStringSecItemTag prop_tag_tbl[] = {
    { &kEAPSecKeychainPropAccount, kSecAccountItemAttr },
    { &kEAPSecKeychainPropLabel, kSecLabelItemAttr },
    { &kEAPSecKeychainPropDescription, kSecDescriptionItemAttr },
};
STATIC const int		prop_tag_tbl_size = (sizeof(prop_tag_tbl)
						     / sizeof(prop_tag_tbl[0]));

STATIC void
mySecKeychainAttributeInfoInit(mySecKeychainAttributeInfo * attr_info)
{
    attr_info->info.count = 0;
    attr_info->info.tag = attr_info->tag;
    attr_info->info.format = attr_info->format;
    return;
}

STATIC Boolean
mySecKeychainAttributeInfoAdd(mySecKeychainAttributeInfo * attr_info,
			      UInt32 tag)
{
    int		count = attr_info->info.count;

    if (count >= MY_KEYCHAIN_ATTR_MAX) {
	EAPLOG_FL(LOG_NOTICE, "Trying to add attribute %d but list is full",
		  (int)tag);
	return (FALSE);
    }
    attr_info->format[count] = CSSM_DB_ATTRIBUTE_FORMAT_BLOB;
    attr_info->tag[count] = tag;
    attr_info->info.count++;
    return (TRUE);
}

STATIC void
mySecKeychainAttributeListInit(mySecKeychainAttributeList * attr_list)
{
    attr_list->list.count = 0;
    attr_list->list.attr = attr_list->attr;
    return;
}

STATIC Boolean
mySecKeychainAttributeListAdd(mySecKeychainAttributeList * attr_list,
			      UInt32 attr_tag, CFDataRef attr_data)
{
    SecKeychainAttribute * 	attr;
    int				count = attr_list->list.count;

    if (count >= MY_KEYCHAIN_ATTR_MAX) {
	EAPLOG_FL(LOG_NOTICE, "Trying to add attribute %d but list is full",
		  (int)attr_tag);
	return (FALSE);
    }
    attr = attr_list->attr + count;
    attr->tag = attr_tag;
    attr->length = CFDataGetLength(attr_data);
    attr->data = (void *)CFDataGetBytePtr(attr_data);
    attr_list->list.count++;
    return (TRUE);
}

STATIC Boolean
mySecKeychainAttributeListAddFromDict(mySecKeychainAttributeList * attr_list,
				      CFDictionaryRef attrs)
{
    int				i;
    Boolean			ret = TRUE;
    const CFStringSecItemTag *	tbl;

    for (i = 0, tbl = prop_tag_tbl; i < prop_tag_tbl_size; i++, tbl++) {
	CFDataRef	val = CFDictionaryGetValue(attrs, *tbl->key);

	if (val != NULL) {
	    ret = mySecKeychainAttributeListAdd(attr_list, tbl->tag, val);
	    if (ret == FALSE) {
		EAPLOG_FL(LOG_NOTICE, "mySecKeychainAttributeListAddFromDict() "
			  "failed to add %d", (int)tbl->tag);
		break;
	    }
	}
    }
    return (ret);
}

PRIVATE_EXTERN OSStatus
EAPSecAccessCreateWithTrustedApplications(CFArrayRef trusted_apps,
					  CFDataRef label_data,
					  SecAccessRef * ret_access)
{
    CFStringRef		label = NULL;
    OSStatus		status;
        
    if (label_data != NULL) {
	label = my_CFStringCreateWithData(label_data);
    }
    else {
	label = CFSTR("--unspecified--");
	CFRetain(label);
    }
    status = SecAccessCreate(label, trusted_apps, ret_access);
    my_CFRelease(&label);
    return (status);
}

PRIVATE_EXTERN OSStatus
EAPSecKeychainItemSetAccessForTrustedApplications(SecKeychainItemRef item,
						  CFArrayRef trusted_apps)
{
    CFArrayRef 		app_list = NULL;
    CFMutableArrayRef	app_list_data = NULL;
    SecACLRef		acl;
    CFArrayRef		acl_list = NULL;
    SecAccessRef	access = NULL;
    CFStringRef 	prompt_description = NULL;
    SecKeychainPromptSelector prompt_selector;
    OSStatus		status;

    status = SecKeychainItemCopyAccess(item, &access);
    if (status != noErr) {
	status = SecAccessCreate(CFSTR("--unspecified--"),
				 trusted_apps, &access);
	if (status != noErr) {
	    goto done;
	}
    }
    else {
	acl_list
	    = SecAccessCopyMatchingACLList(access, kSecACLAuthorizationDecrypt);
	if (acl_list == NULL) {
	    status = errSecAllocate;
	    goto done;
	}
	acl = (SecACLRef)CFArrayGetValueAtIndex(acl_list, 0);
	status = SecACLCopyContents(acl, &app_list, &prompt_description, 
				    &prompt_selector);
	if (status == noErr && app_list != NULL) {
	    int			count;
	    int			i;
	    CFRange		r;
	    CFMutableArrayRef	new_list = NULL;
	    
	    r.location = 0;
	    r.length = CFArrayGetCount(app_list);
	    app_list_data = CFArrayCreateMutable(NULL, r.length,
						 &kCFTypeArrayCallBacks);
	    for (i = 0; i < r.length; i++) {
		SecTrustedApplicationRef	app;
		CFDataRef			data = NULL;

		app = (SecTrustedApplicationRef)
		    CFArrayGetValueAtIndex(app_list, i);
		(void)SecTrustedApplicationCopyExternalRepresentation(app,
								      &data);
		if (data != NULL) {
		    CFArrayAppendValue(app_list_data, data);
		    CFRelease(data);
		}
	    }
	    /* in case it changed */
	    r.length = CFArrayGetCount(app_list_data);
	    count = CFArrayGetCount(trusted_apps);
	    for (i = 0; i < count; i++) {
		bool				already_there;
		SecTrustedApplicationRef	app;
		CFDataRef			data = NULL;

		app = (SecTrustedApplicationRef)
		    CFArrayGetValueAtIndex(trusted_apps, i);

		(void)SecTrustedApplicationCopyExternalRepresentation(app,
								      &data);
		if (data == NULL) {
		    continue;
		}
		already_there = CFArrayContainsValue(app_list_data, r, data);
		CFRelease(data);
		if (already_there) {
		    continue;
		}
		if (new_list == NULL) {
		    new_list = CFArrayCreateMutableCopy(NULL, 0, app_list);
		}
		CFArrayAppendValue(new_list, app);
	    }
	    if (new_list == NULL) {
		/* no modifications required */
		goto done;
	    }
	    status = SecACLSetContents(acl, new_list, prompt_description,
				       prompt_selector);
	    CFRelease(new_list);
	    if (status != noErr) {
		goto done;
	    }
	}
	else {
	    status = SecACLSetContents(acl, trusted_apps,
				       prompt_description,
				       prompt_selector);
	    if (status != noErr) {
		goto done;
	    }
	}
    }
    status = SecKeychainItemSetAccess(item, access);

 done:
    my_CFRelease(&access);
    my_CFRelease(&app_list);
    my_CFRelease(&app_list_data);
    my_CFRelease(&prompt_description);
    my_CFRelease(&acl_list);
    return (status);
}

STATIC OSStatus
KeychainPasswordItemCopy(SecKeychainRef keychain,
			 CFStringRef unique_id_str,
			 SecKeychainItemRef * ret_item)
{
    SecKeychainItemRef	item;
    OSStatus		status;
    CFDataRef		unique_id;

    unique_id = CFStringCreateExternalRepresentation(NULL,
						     unique_id_str,
						     kCFStringEncodingUTF8,
						     0);
    status 
	= SecKeychainFindGenericPassword(keychain,
					 CFDataGetLength(unique_id),
					 (const char *)CFDataGetBytePtr(unique_id),
					 0,
					 NULL,
					 NULL,
					 NULL,
					 &item);
    CFRelease(unique_id);
    *ret_item = NULL;
    if (status != noErr) {
#if TEST_EAPKEYCHAINUTIL
	fprintf(stderr, "SecKeychainFindGenericPassword failed: %s (%d)\n", 
		EAPSecurityErrorString(status), (int)status);
#endif /* TEST_EAPKEYCHAINUTIL */
    }
    else {
	*ret_item = item;
    }
    return (status);
}

STATIC CFStringRef
SecKeychainAttrTypeGetCFString(SecKeychainAttrType type)
{
    int				i;
    const CFStringSecItemTag *	tbl;

    for (i = 0, tbl = prop_tag_tbl; i < prop_tag_tbl_size; i++, tbl++) {
	if (tbl->tag == type) {
	    return (*tbl->key);
	}
    }
    return (NULL);
}

STATIC OSStatus
KeychainItemCopyInfo(SecKeychainItemRef item,
		     CFArrayRef keys,
		     CFDictionaryRef * ret_attrs)
{
    CFMutableDictionaryRef 	attrs = NULL;
    int				i;
    void *			password_data = NULL;
    UInt32 			password_length = 0;
    void * *			password_data_p;
    UInt32 *			password_length_p;
    CFRange			range = CFRangeMake(0, CFArrayGetCount(keys));
    mySecKeychainAttributeInfo 	req_attr_info;
    SecKeychainAttributeInfo * 	req_attr_info_p;
    SecKeychainAttributeList * 	ret_attr_list = NULL;
    SecKeychainAttributeList * *ret_attr_list_p;
    OSStatus			status;

    mySecKeychainAttributeInfoInit(&req_attr_info);
    if (CFArrayContainsValue(keys, range, kEAPSecKeychainPropAccount)) {
	mySecKeychainAttributeInfoAdd(&req_attr_info, 
				      kSecAccountItemAttr);
    }
    if (CFArrayContainsValue(keys, range, kEAPSecKeychainPropLabel)) {
	mySecKeychainAttributeInfoAdd(&req_attr_info, 
				      kSecLabelItemAttr);
    }
    if (CFArrayContainsValue(keys, range, kEAPSecKeychainPropDescription)) {
	mySecKeychainAttributeInfoAdd(&req_attr_info, 
				      kSecDescriptionItemAttr);
    }
    if (CFArrayContainsValue(keys, range, kEAPSecKeychainPropPassword)) {
	password_data_p = &password_data;
	password_length_p = &password_length;
    }
    else {
	password_data_p = NULL;
	password_length_p = NULL;
    }
    if (req_attr_info.info.count != 0) {
	req_attr_info_p = &req_attr_info.info;
	ret_attr_list_p = &ret_attr_list;
    }
    else {
	req_attr_info_p = NULL;
	ret_attr_list_p = NULL;
    }
    status = SecKeychainItemCopyAttributesAndData(item, 
						  req_attr_info_p,
						  NULL,
						  ret_attr_list_p,
						  password_length_p,
						  password_data_p);
    if (status != noErr) {
	goto done;
    }
    attrs = CFDictionaryCreateMutable(NULL, 0,
				      &kCFTypeDictionaryKeyCallBacks,
				      &kCFTypeDictionaryValueCallBacks);
    if (password_data != NULL && password_length != 0) {
	CFDataRef	password;

	password = CFDataCreate(NULL, password_data, password_length);
	CFDictionarySetValue(attrs, kEAPSecKeychainPropPassword, password);
	CFRelease(password);
    }
    if (ret_attr_list != NULL) {
	SecKeychainAttribute *	attr = ret_attr_list->attr;

	for (i = 0; i < ret_attr_list->count; i++, attr++) {
	    CFStringRef		key;
	    CFDataRef		val;

	    if (attr->data == NULL || attr->length == 0) {
		continue;
	    }
	    key = SecKeychainAttrTypeGetCFString(attr->tag);
	    if (key == NULL) {
		/* shouldn't happen */
		continue;
	    }
	    val = CFDataCreate(NULL, attr->data, attr->length);
	    CFDictionarySetValue(attrs, key, val);
	    CFRelease(val);
	}
    }
    SecKeychainItemFreeAttributesAndData(ret_attr_list, password_data);

 done:
    *ret_attrs = attrs;
    return (status);

}

OSStatus
EAPSecKeychainPasswordItemRemove(SecKeychainRef keychain,
				 CFStringRef unique_id_str)
{
    SecKeychainItemRef	item;
    OSStatus		status;

    status = KeychainPasswordItemCopy(keychain, unique_id_str, &item);
    if (status != noErr) {
	goto done;
    }
    status = SecKeychainItemDelete(item);
    CFRelease(item);
    if (status != noErr) {
	EAPLOG_FL(LOG_NOTICE, "SecKeychainItemDelete() failed: %s (%d)",
		  EAPSecurityErrorString(status), (int)status);

    }
 done:
    return (status);
}

OSStatus
EAPSecKeychainPasswordItemCopy2(SecKeychainRef keychain, 
				CFStringRef unique_id_str,
				CFArrayRef keys,
				CFDictionaryRef * ret_values)
{
    SecKeychainItemRef		item = NULL;
    OSStatus			status;

    *ret_values = NULL;
    status = KeychainPasswordItemCopy(keychain, unique_id_str, &item);
    if (status == noErr) {
	status = KeychainItemCopyInfo(item, keys, ret_values);
    }
    if (item != NULL) {
	CFRelease(item);
    }
    return (status);
}

OSStatus
EAPSecKeychainPasswordItemCopy(SecKeychainRef keychain, 
			       CFStringRef unique_id_str, 
			       CFDataRef * ret_password)
{
    CFDataRef			password;
    CFDictionaryRef		props;
    CFArrayRef			req_props;
    OSStatus			status;

    *ret_password = NULL;
    req_props = CFArrayCreate(NULL,
			      (const void * *)&kEAPSecKeychainPropPassword, 1,
			      &kCFTypeArrayCallBacks);
    status = EAPSecKeychainPasswordItemCopy2(keychain, 
					     unique_id_str,
					     req_props,
					     &props);
    CFRelease(req_props);
    if (status != noErr) {
	return (status);
    }
    password = CFDictionaryGetValue(props, kEAPSecKeychainPropPassword);
    if (password == NULL) {
	status = errSecItemNotFound;
    }
    else {
	*ret_password = CFRetain(password);
    }
    CFRelease(props);
    return (status);
}

OSStatus
EAPSecKeychainPasswordItemCreateWithAccess(SecKeychainRef keychain,
					   SecAccessRef access,
					   CFStringRef unique_id_str,
					   CFDataRef label,
					   CFDataRef description,
					   CFDataRef user,
					   CFDataRef password)
{
    mySecKeychainAttributeList	attr_list;
    OSStatus			status;
    CFDataRef			unique_id;

    unique_id = CFStringCreateExternalRepresentation(NULL,
						     unique_id_str,
						     kCFStringEncodingUTF8,
						     0);
    mySecKeychainAttributeListInit(&attr_list);
    mySecKeychainAttributeListAdd(&attr_list, kSecServiceItemAttr, unique_id);
    if (label != NULL) {
	mySecKeychainAttributeListAdd(&attr_list, kSecLabelItemAttr, label);
    }
    if (description != NULL) {
	mySecKeychainAttributeListAdd(&attr_list, kSecDescriptionItemAttr,
				      description);
    }
    if (user != NULL) {
	mySecKeychainAttributeListAdd(&attr_list, kSecAccountItemAttr, user);
    }
    status = SecKeychainItemCreateFromContent(kSecGenericPasswordItemClass,
					      &attr_list.list,
					      CFDataGetLength(password),
					      CFDataGetBytePtr(password),
					      keychain,
					      access,
					      NULL);
    CFRelease(unique_id);
    return (status);
}

OSStatus
EAPSecKeychainPasswordItemCreate(SecKeychainRef keychain,
				 CFStringRef unique_id_str,
				 CFDictionaryRef attrs)
{
    CFBooleanRef		allow_root;
    SecAccessRef		access = NULL;
    mySecKeychainAttributeList	attr_list;
    CFDataRef 			password;
    void *			password_data;
    UInt32			password_length;
    OSStatus			status;
    CFArrayRef			trusted_apps;
    CFDataRef			unique_id = NULL;

    password = CFDictionaryGetValue(attrs, kEAPSecKeychainPropPassword);
    if (password != NULL) {
	/* set it */
	password_length = CFDataGetLength(password);
	password_data = (void *)CFDataGetBytePtr(password);
    }
    else {
	/* don't set it */
	password_length = 0;
	password_data = NULL;
    }
    trusted_apps = CFDictionaryGetValue(attrs,
					kEAPSecKeychainPropTrustedApplications);
    allow_root = CFDictionaryGetValue(attrs,
				      kEAPSecKeychainPropAllowRootAccess);
    if (trusted_apps != NULL) {
	CFDataRef	label_data;

	label_data = CFDictionaryGetValue(attrs,
					  kEAPSecKeychainPropLabel);
	status = EAPSecAccessCreateWithTrustedApplications(trusted_apps,
							   label_data,
							   &access);
	if (status != noErr) {
	    EAPLOG_FL(LOG_NOTICE,
		      "failed to get SecAccess for Trusted Apps, %d",
		      (int)status);
	    goto done;
	}
    }
    else if (allow_root != NULL && CFBooleanGetValue(allow_root)) {
	CFErrorRef	error;

	access = SecAccessCreateWithOwnerAndACL(0, 0, kSecUseOnlyUID,
						NULL, &error);
	if (access == NULL) {
	    status = errSecAllocate;
	    if (error != NULL) {
		EAPLOG_FL(LOG_NOTICE,
			  "SecAccessCreateWithOwnerAndACL() failed %d",
			  (int)CFErrorGetCode(error));
		CFRelease(error);
	    }
	    goto done;
	}
    }
    mySecKeychainAttributeListInit(&attr_list);
    unique_id = CFStringCreateExternalRepresentation(NULL,
						     unique_id_str,
						     kCFStringEncodingUTF8,
						     0);
    mySecKeychainAttributeListAdd(&attr_list, kSecServiceItemAttr, unique_id);
    if (mySecKeychainAttributeListAddFromDict(&attr_list, attrs) == FALSE) {
	status = errSecBufferTooSmall;
    }
    else {
	status = SecKeychainItemCreateFromContent(kSecGenericPasswordItemClass,
						  &attr_list.list,
						  password_length,
						  password_data,
						  keychain,
						  access,
						  NULL);
    }

 done:
    my_CFRelease(&unique_id);
    my_CFRelease(&access);
    return (status);
}

OSStatus
EAPSecKeychainPasswordItemCreateUnique(SecKeychainRef keychain,
				       CFDictionaryRef attrs,
				       CFStringRef * ret_unique_id)
{
    OSStatus		status;
    CFStringRef		unique_id_str;
    
    unique_id_str = my_CFUUIDStringCreate(NULL);
    status = EAPSecKeychainPasswordItemCreate(keychain,
					      unique_id_str,
					      attrs);
    if (status == noErr	&& ret_unique_id != NULL) {
	*ret_unique_id = unique_id_str;
    }
    else {
	if (ret_unique_id != NULL) {
	    *ret_unique_id = NULL;
	}
	CFRelease(unique_id_str);
    }
    return (status);
}

OSStatus
EAPSecKeychainPasswordItemSet(SecKeychainRef keychain,
			      CFStringRef unique_id_str,
			      CFDataRef password)
{
    SecKeychainItemRef		item;
    OSStatus			status;

    status = KeychainPasswordItemCopy(keychain, unique_id_str, &item);
    if (status != noErr) {
	return (status);
    }
    status = SecKeychainItemModifyAttributesAndData(item,
						    NULL, 
						    CFDataGetLength(password),
						    CFDataGetBytePtr(password));
    CFRelease(item);
    return (status);
}

OSStatus
EAPSecKeychainPasswordItemSet2(SecKeychainRef keychain,
			       CFStringRef unique_id_str,
			       CFDictionaryRef attrs)
{
    mySecKeychainAttributeList	attr_list;
    SecKeychainItemRef		item;
    CFDataRef 			password;
    void *			password_data;
    UInt32			password_length;
    OSStatus			status;

    status = KeychainPasswordItemCopy(keychain, unique_id_str, &item);
    if (status != noErr) {
	return (status);
    }
    mySecKeychainAttributeListInit(&attr_list);
    if (mySecKeychainAttributeListAddFromDict(&attr_list, attrs) == FALSE) {
	status = errSecBufferTooSmall;
	goto done;
    }
    password = CFDictionaryGetValue(attrs, kEAPSecKeychainPropPassword);
    if (password != NULL) {
	/* set it */
	password_length = CFDataGetLength(password);
	password_data = (void *)CFDataGetBytePtr(password);
    }
    else {
	/* don't set it */
	password_length = 0;
	password_data = NULL;
    }
    status = SecKeychainItemModifyAttributesAndData(item,
						    &attr_list.list,
						    password_length,
						    password_data);
 done:
    CFRelease(item);
    return (status);
}

#endif /* TARGET_OS_EMBEDDED */

OSStatus
EAPSecKeychainPasswordItemCreateUniqueWithAccess(SecKeychainRef keychain,
						 SecAccessRef access,
						 CFDataRef label,
						 CFDataRef description,
						 CFDataRef user,
						 CFDataRef password,
						 CFStringRef * ret_unique_id)
{
    OSStatus		status;
    CFStringRef		unique_id_str;

    unique_id_str = my_CFUUIDStringCreate(NULL);
    status = EAPSecKeychainPasswordItemCreateWithAccess(keychain,
							access,
							unique_id_str,
							label,
							description,
							user,
							password);
    if (status == noErr	&& ret_unique_id != NULL) {
	*ret_unique_id = unique_id_str;
    }
    else {
	if (ret_unique_id != NULL) {
	    *ret_unique_id = NULL;
	}
	CFRelease(unique_id_str);
    }
    return (status);
}

#ifdef TEST_EAPKEYCHAINUTIL

#if ! TARGET_OS_EMBEDDED
/*
 * 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's a way that does not depend on any outside software layers
 * (other than CSSM and Security's Sec* layer, of course).
 */
static OSStatus
SecKeychainCopySystemKeychain(SecKeychainRef * ret_keychain)
{
    SecKeychainRef		keychain = NULL;
    OSStatus			status;

    status = SecKeychainCopyDomainDefault(kSecPreferencesDomainSystem,
					  &keychain);
    if (status != noErr && keychain != NULL) {
	CFRelease(keychain);
	keychain = NULL;
    }
    *ret_keychain = keychain;
    return (status);
}

#endif /* ! TARGET_OS_EMBEDDED */

#if TARGET_OS_EMBEDDED
#define SYS_OPT			" "
#else /* TARGET_OS_EMBEDDED */
#define SYS_OPT			" [system] "
#define HAS_KEYCHAINS
#endif /* TARGET_OS_EMBEDDED */

static void 
usage(const char * progname)
{
    fprintf(stderr, "%s" SYS_OPT 
	    "create <label> <description> <username> <password>\n",
	    progname);
    fprintf(stderr, 
	    "%s" SYS_OPT "set <unique_id> <username> <password>\n",
	    progname);
    fprintf(stderr, "%s" SYS_OPT "remove <unique_id>\n", progname);
    fprintf(stderr, "%s" SYS_OPT "get <unique_id>\n", progname);
    exit(1);
}

typedef enum {
    kCommandUnknown,
    kCommandCreate,
    kCommandRemove,
    kCommandSet,
    kCommandGet
} Command;

#define keapolclientPath		"/System/Library/SystemConfiguration/EAPOLController.bundle/Contents/Resources/eapolclient"
#define kAirPortApplicationGroup	"AirPort"
#define kSystemUIServerPath 		"/System/Library/CoreServices/SystemUIServer.app"

STATIC CFArrayRef
copy_trusted_applications(void)
{
    CFMutableArrayRef		array;
    SecTrustedApplicationRef	trusted_app;
    OSStatus			status;

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

    /* this executable */
    status = SecTrustedApplicationCreateFromPath(NULL, &trusted_app);
    if (status != noErr) {
	fprintf(stderr,
		"SecTrustedApplicationCreateFromPath(NULL) failed, %d\n",
		status);
    }
    else {
	CFArrayAppendValue(array, trusted_app);
	CFRelease(trusted_app);
    }

    /* eapolclient */
    status = SecTrustedApplicationCreateFromPath(keapolclientPath,
						 &trusted_app);
    if (status != noErr) {
	fprintf(stderr,
		"SecTrustedApplicationCreateFromPath(%s) failed, %d\n",
		keapolclientPath,
		status);
    }
    else {
	CFArrayAppendValue(array, trusted_app);
	CFRelease(trusted_app);
    }

    
    /* SystemUIServer */
    status = SecTrustedApplicationCreateFromPath(kSystemUIServerPath, 
						 &trusted_app);
    if (status != noErr) {
	fprintf(stderr,
		"SecTrustedApplicationCreateFromPath(%s) failed, %d\n",
		kSystemUIServerPath,
		status);
    }
    else {
	CFArrayAppendValue(array, trusted_app);
	CFRelease(trusted_app);
    }

    /* AirPort Application Group */
    status 
	= SecTrustedApplicationCreateApplicationGroup(kAirPortApplicationGroup,
						      NULL, &trusted_app);
    if (status != noErr) {
	fprintf(stderr,
		"SecTrustedApplicationCreateApplicationGroup("
		kAirPortApplicationGroup ") failed, %d\n",
		status);
    }
    else {
	CFArrayAppendValue(array, trusted_app);
	CFRelease(trusted_app);
    }
    if (CFArrayGetCount(array) == 0) {
	my_CFRelease(&array);
    }
    return (array);
}

int
main(int argc, const char *argv[])
{
    Command		cmd = kCommandUnknown;
    SecKeychainRef	keychain = NULL;
    const char *	progname = argv[0];
    OSStatus		status;
#ifdef HAS_KEYCHAINS
    bool		use_system = FALSE;
#endif

    if (argc < 2) {
	usage(progname);
    }
#ifdef HAS_KEYCHAINS
    if (strcmp(argv[1], "system") == 0) {
	if (argc < 3) {
	    usage(progname);
	}
	use_system = TRUE;
	status = SecKeychainCopySystemKeychain(&keychain);
	if (status != noErr) {
	    fprintf(stderr, "SecKeychainCopySystemKeychain failed, %s (%d)\n",
		    EAPSecurityErrorString(status), (int)status);
	    exit(1);
	}
	argc--;
	argv++;
    }
#endif /* HAS_KEYCHAINS */
    
    if (strcmp(argv[1], "create") == 0) {
	if (argc < 6) {
	    usage(progname);
	}
	cmd = kCommandCreate;
    }
    else if (strcmp(argv[1], "set") == 0) {
	if (argc < 5) {
	    usage(progname);
	}
	cmd = kCommandSet;
    }
    else if (strcmp(argv[1], "get") == 0) {
	if (argc < 3) {
	    usage(progname);
	}
	cmd = kCommandGet;
    }
    else if (strcmp(argv[1], "remove") == 0) {
	if (argc < 3) {
	    usage(progname);
	}
	cmd = kCommandRemove;
    }
    else {
	usage(progname);
    }
    switch (cmd) {
    case kCommandSet: {
	CFMutableDictionaryRef 	attrs = NULL;
	CFDataRef			password;
	CFArrayRef			trusted_apps;
	CFStringRef			unique_id_str;
	CFDataRef			username;

	unique_id_str  
	    = CFStringCreateWithCStringNoCopy(NULL, 
					      argv[2],
					      kCFStringEncodingUTF8,
					      kCFAllocatorNull);
	username
	    = CFDataCreateWithBytesNoCopy(NULL,
					  (const UInt8 *)argv[3],
					  strlen(argv[3]),
					  kCFAllocatorNull);
	password
	    = CFDataCreateWithBytesNoCopy(NULL,
					  (const UInt8 *)argv[4],
					  strlen(argv[4]),
					  kCFAllocatorNull);
	attrs = CFDictionaryCreateMutable(NULL, 0,
					  &kCFTypeDictionaryKeyCallBacks,
					  &kCFTypeDictionaryValueCallBacks);
	CFDictionarySetValue(attrs, kEAPSecKeychainPropAccount,
			     username);
	CFDictionarySetValue(attrs, kEAPSecKeychainPropPassword,
			     password);
	trusted_apps = copy_trusted_applications();
	CFDictionarySetValue(attrs, kEAPSecKeychainPropTrustedApplications,
			     trusted_apps);
	CFRelease(trusted_apps);
	status = EAPSecKeychainPasswordItemSet2(keychain,
						unique_id_str,
						attrs);
	if (status != noErr) {
	    fprintf(stderr, "EAPSecKeychainItemSet2 failed %s (%d)\n",
		    EAPSecurityErrorString(status), (int)status);
	    exit(2);
	}
	my_CFRelease(&password);
	break;
    }
    case kCommandCreate: {
	SecAccessRef			access = NULL;
	CFMutableDictionaryRef	attrs = NULL;
	CFDataRef			data;
	CFArrayRef			trusted_apps;
	CFDataRef			unique_id;
	CFStringRef			unique_id_str = NULL;

#ifdef HAS_KEYCHAINS
	if (use_system) {
	    CFErrorRef	error;

	    access = SecAccessCreateWithOwnerAndACL(0, 0, kSecUseOnlyUID,
						    NULL, &error);
	    if (access == NULL) {
		if (error != NULL) {
		    fprintf(stderr,
			    "SecAccessCreateWithOwnerAndACL() failed %d\n",
			    (int)CFErrorGetCode(error));
		}
		exit(2);
	    }
	}
	else {
	    status = SecAccessCreate(CFSTR("keychain"), NULL, &access);
	    if (status != noErr) {
		fprintf(stderr, "SecAccessCreate failed, %s (%d)\n",
			EAPSecurityErrorString(status), (int)status);
		exit(2);
	    }
	}
#endif /* HAS_KEYCHAINS */

	attrs = CFDictionaryCreateMutable(NULL, 0,
					  &kCFTypeDictionaryKeyCallBacks,
					  &kCFTypeDictionaryValueCallBacks);

	/* label */
	data = CFDataCreateWithBytesNoCopy(NULL, 
					   (const UInt8 *)argv[2],
					   strlen(argv[2]),
					   kCFAllocatorNull);
	CFDictionarySetValue(attrs, kEAPSecKeychainPropLabel, data);
	CFRelease(data);
	  
	/* description */
	data = CFDataCreateWithBytesNoCopy(NULL, 
					   (const UInt8 *)argv[3],
					   strlen(argv[3]),
					   kCFAllocatorNull);
	CFDictionarySetValue(attrs, kEAPSecKeychainPropDescription,
			     data);
	CFRelease(data);

	/* name */
	data = CFDataCreateWithBytesNoCopy(NULL, 
					   (const UInt8 *)argv[4],
					   strlen(argv[4]),
					   kCFAllocatorNull);
	CFDictionarySetValue(attrs, kEAPSecKeychainPropAccount,
			     data);
	CFRelease(data);

	/* password */
	data = CFDataCreateWithBytesNoCopy(NULL,
					   (const UInt8 *)argv[5],
					   strlen(argv[5]),
					   kCFAllocatorNull);
	CFDictionarySetValue(attrs, kEAPSecKeychainPropPassword,
			     data);
	CFRelease(data);

	/* trusted apps */
	trusted_apps = copy_trusted_applications();
	CFDictionarySetValue(attrs, kEAPSecKeychainPropTrustedApplications,
			     trusted_apps);
	CFRelease(trusted_apps);

	if (argc > 6) {
	    unique_id_str
		= CFStringCreateWithCStringNoCopy(NULL, 
						  argv[6],
						  kCFStringEncodingUTF8,
						  kCFAllocatorNull);
	    status 
		= EAPSecKeychainPasswordItemCreate(keychain,
						   unique_id_str,
						   attrs);
	    if (status != noErr) {
		fprintf(stderr, "EAPSecKeychainItemCreate failed,"
			" %s (%d)\n",
			EAPSecurityErrorString(status), (int)status);
		exit(1);
	    }
	}
	else {
	    status 
		= EAPSecKeychainPasswordItemCreateUnique(keychain,
							 attrs,
							 &unique_id_str);

	    if (status != noErr) {
		fprintf(stderr,
			"EAPSecKeychainItemCreateUniqueWithAccessfailed,"
			" %s (%d)\n",
			EAPSecurityErrorString(status), (int)status);
		exit(1);
	    }
	}
	unique_id 
	    = CFStringCreateExternalRepresentation(NULL,
						   unique_id_str,
						   kCFStringEncodingUTF8,
						   0);
	fwrite(CFDataGetBytePtr(unique_id),
	       CFDataGetLength(unique_id), 1, stdout);
	printf("\n");
	CFRelease(attrs);
	break;
    }
    case kCommandGet:
    case kCommandRemove: {
	CFStringRef	unique_id_str;

	unique_id_str  
	    = CFStringCreateWithCStringNoCopy(NULL, 
					      argv[2],
					      kCFStringEncodingUTF8,
					      kCFAllocatorNull);
	if (cmd == kCommandRemove) {
	    status
		= EAPSecKeychainPasswordItemRemove(keychain, unique_id_str);
	    if (status != noErr) {
		fprintf(stderr,
			"EAPSecKeychainItemRemove failed, %s (%d)\n",
			EAPSecurityErrorString(status), (int)status);
		exit(2);
	    }
	}
	else {
	    CFDictionaryRef	attrs;
	    const void *	keys[] = {
		kEAPSecKeychainPropPassword,
		kEAPSecKeychainPropAccount,
		kEAPSecKeychainPropLabel,
		kEAPSecKeychainPropDescription
	    };
	    int		keys_count = sizeof(keys) / sizeof(keys[0]);
	    CFDataRef		password = NULL;
	    CFArrayRef	req_props;
	    OSStatus		status;
	    CFDataRef		username = NULL;

	    req_props = CFArrayCreate(NULL,
				      keys, keys_count, 
				      &kCFTypeArrayCallBacks);
	    status = EAPSecKeychainPasswordItemCopy2(keychain, 
						     unique_id_str, 
						     req_props,
						     &attrs);
	    CFRelease(req_props);
	    if (status != noErr) {
		fprintf(stderr,
			"EAPSecKeychainPasswordItemCopy2 failed, %s (%d)\n",
			EAPSecurityErrorString(status), (int)status);
		exit(2);
	    }
	    CFShow(attrs);
	    password = CFDictionaryGetValue(attrs,
					    kEAPSecKeychainPropPassword);
	    username = CFDictionaryGetValue(attrs,
					    kEAPSecKeychainPropAccount);
	    if (password != NULL) {
		printf("Password = '");
		fwrite(CFDataGetBytePtr(password),
		       CFDataGetLength(password), 1, stdout);
		printf("'\n");
	    }
	    if (username != NULL) {
		printf("Name = '");
		fwrite(CFDataGetBytePtr(username),
		       CFDataGetLength(username), 1, stdout);
		printf("'\n");
	    }
	    CFRelease(attrs);
	}
	CFRelease(unique_id_str);
    }
    default:
	break;
    }
    if (keychain != NULL) {
	CFRelease(keychain);
    }
    exit(0);
    return (0);
}

#endif /* TEST_EAPKEYCHAINUTIL */