SCPreferencesKeychainPrivate.c [plain text]
#include <sys/param.h>
#include <CoreFoundation/CoreFoundation.h>
#include <CoreFoundation/CFBundlePriv.h> // for _CFBundleCopyMainBundleExecutableURL
#include <SystemConfiguration/SCPrivate.h> // for _SCErrorSet
#include <Security/Security.h>
#include "dy_framework.h"
#include "SCPreferencesInternal.h"
static CFDataRef
copyMyExecutablePath(void)
{
char fspath[MAXPATHLEN];
Boolean isBundle = FALSE;
Boolean ok;
CFDataRef path = NULL;
CFURLRef url;
url = _CFBundleCopyMainBundleExecutableURL(&isBundle);
if (url == NULL) {
return NULL;
}
ok = CFURLGetFileSystemRepresentation(url, TRUE, (UInt8 *)fspath, sizeof(fspath));
CFRelease(url);
if (!ok) {
return NULL;
}
fspath[sizeof(fspath) - 1] = '\0';
if (isBundle) {
const char *slash;
slash = strrchr(fspath, '/');
if (slash != NULL) {
const char *contents;
contents = strstr(fspath, "/Contents/MacOS/");
if ((contents != NULL) &&
((contents + sizeof("/Contents/MacOS/") - 1) == slash)) {
path = CFDataCreate(NULL, (UInt8 *)fspath, contents - fspath);
goto done;
}
}
}
path = CFDataCreate(NULL, (UInt8 *)fspath, strlen(fspath));
done :
return path;
}
#pragma mark -
#pragma mark Keychain helper APIs
static SecAccessRef
_SCSecAccessCreateForUID(uid_t uid)
{
SecAccessRef access = NULL;
OSStatus status;
CSSM_ACL_PROCESS_SUBJECT_SELECTOR selector = {
CSSM_ACL_PROCESS_SELECTOR_CURRENT_VERSION, CSSM_ACL_MATCH_UID, uid, 0 };
CSSM_LIST_ELEMENT subject2 = {
NULL, 0 };
subject2.Element.Word.Data = (UInt8 *)&selector;
subject2.Element.Word.Length = sizeof(selector);
CSSM_LIST_ELEMENT subject1 = {
&subject2, CSSM_ACL_SUBJECT_TYPE_PROCESS, CSSM_LIST_ELEMENT_WORDID };
CSSM_ACL_AUTHORIZATION_TAG rights[] = {
CSSM_ACL_AUTHORIZATION_ANY };
CSSM_ACL_OWNER_PROTOTYPE owner = {
{ CSSM_LIST_TYPE_UNKNOWN, &subject1, &subject2 },
FALSE };
CSSM_ACL_ENTRY_INFO acls[] = {
{
{ { CSSM_LIST_TYPE_UNKNOWN, &subject1, &subject2 },
FALSE, { sizeof(rights) / sizeof(rights[0]), rights },
{ },
{ }
},
0 }
};
status = SecAccessCreateFromOwnerAndACL(&owner,
sizeof(acls) / sizeof(acls[0]),
acls,
&access);
if (status != noErr) {
_SCErrorSet(status);
}
return access;
}
static SecAccessRef
_SCSecAccessCreateForExecutables(CFStringRef label,
CFArrayRef executableURLs)
{
SecAccessRef access = NULL;
CFArrayRef aclList = NULL;
CFIndex i;
CFIndex n;
OSStatus status;
SecTrustedApplicationRef trustedApplication;
CFMutableArrayRef trustedApplications;
trustedApplications = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
status = SecTrustedApplicationCreateFromPath(NULL, &trustedApplication);
if (status == noErr) {
CFArrayAppendValue(trustedApplications, trustedApplication);
CFRelease(trustedApplication);
}
n = (executableURLs != NULL) ? CFArrayGetCount(executableURLs) : 0;
for (i = 0; i < n; i++) {
Boolean ok;
char path[MAXPATHLEN];
CFURLRef url;
url = CFArrayGetValueAtIndex(executableURLs, i);
ok = CFURLGetFileSystemRepresentation(url, TRUE, (UInt8 *)path, sizeof(path));
if (!ok) {
continue;
}
status = SecTrustedApplicationCreateFromPath(path, &trustedApplication);
if (status == noErr) {
CFArrayAppendValue(trustedApplications, trustedApplication);
CFRelease(trustedApplication);
}
}
status = SecAccessCreate(label, trustedApplications, &access);
if (status != noErr) {
goto done;
}
#ifdef NOT_NEEDED
status = SecAccessCopySelectedACLList(access, CSSM_ACL_AUTHORIZATION_DECRYPT, &aclList);
if (status == noErr) {
SecACLRef acl;
CFArrayRef applicationList = NULL;
CFStringRef description = NULL;
CSSM_ACL_KEYCHAIN_PROMPT_SELECTOR promptSelector;
acl = (SecACLRef)CFArrayGetValueAtIndex(aclList, 0);
status = SecACLCopySimpleContents(acl, &applicationList, &description, &promptSelector);
status = SecACLSetSimpleContents(acl, (CFArrayRef)trustedApplications, description, &promptSelector);
if (applicationList != NULL) CFRelease(applicationList);
if (description != NULL) CFRelease(description);
}
#endif // NOT_NEEDED
done :
if (aclList != NULL) CFRelease(aclList);
CFRelease(trustedApplications);
return access;
}
SecKeychainRef
_SCSecKeychainCopySystemKeychain(void)
{
SecPreferencesDomain domain;
SecKeychainRef keychain = NULL;
OSStatus status;
status = SecKeychainGetPreferenceDomain(&domain);
if (status != noErr) {
_SCErrorSet(status);
return NULL;
}
status = SecKeychainSetPreferenceDomain(kSecPreferencesDomainSystem);
if (status != noErr) {
_SCErrorSet(status);
return NULL;
}
status = SecKeychainCopyDomainDefault(kSecPreferencesDomainSystem, &keychain);
if (status != noErr) {
_SCErrorSet(status);
(void) SecKeychainSetPreferenceDomain(domain);
if (keychain != NULL) CFRelease(keychain);
return NULL;
}
status = SecKeychainSetPreferenceDomain(domain);
if (status != noErr) {
_SCErrorSet(status);
if (keychain != NULL) CFRelease(keychain);
return NULL;
}
return keychain;
}
static OSStatus
findKeychainItem(SecKeychainRef keychain,
UInt32 serviceNameLength,
void *serviceName,
SecKeychainItemRef *item)
{
SecKeychainAttribute attributes[1];
SecKeychainAttributeList attributeList = { 1, attributes };
SecKeychainSearchRef search = NULL;
OSStatus status;
attributes[0].tag = kSecServiceItemAttr;
attributes[0].data = serviceName;
attributes[0].length = serviceNameLength;
status = SecKeychainSearchCreateFromAttributes(keychain,
kSecGenericPasswordItemClass,
&attributeList,
&search);
if (status != noErr) {
return status;
}
status = SecKeychainSearchCopyNext(search, item);
CFRelease(search);
return status;
}
CFDataRef
_SCSecKeychainPasswordItemCopy(SecKeychainRef keychain,
CFStringRef unique_id)
{
SecKeychainItemRef item = NULL;
CFDataRef keychain_password = NULL;
const char *keychain_serviceName;
OSStatus status;
keychain_serviceName = _SC_cfstring_to_cstring(unique_id, NULL, 0, kCFStringEncodingUTF8);
status = findKeychainItem(keychain,
strlen(keychain_serviceName),
(void *)keychain_serviceName,
&item);
CFAllocatorDeallocate(NULL, (void *)keychain_serviceName);
if (status == noErr) {
void * pw = NULL;
UInt32 pw_len = 0;
status = SecKeychainItemCopyContent(item, NULL, NULL, &pw_len, &pw);
if (status == noErr) {
keychain_password = CFDataCreate(NULL, pw, pw_len);
status = SecKeychainItemFreeContent(NULL, pw);
}
}
if (item != NULL) CFRelease(item);
if (status != noErr) {
_SCErrorSet(status);
}
return keychain_password;
}
Boolean
_SCSecKeychainPasswordItemExists(SecKeychainRef keychain, CFStringRef unique_id)
{
SecKeychainItemRef item;
const char *keychain_serviceName;
OSStatus status;
keychain_serviceName = _SC_cfstring_to_cstring(unique_id, NULL, 0, kCFStringEncodingUTF8);
status = findKeychainItem(keychain,
strlen(keychain_serviceName),
(void *)keychain_serviceName,
&item);
CFAllocatorDeallocate(NULL, (void *)keychain_serviceName);
if (status != noErr) {
_SCErrorSet(status);
return FALSE;
}
CFRelease(item);
return TRUE;
}
Boolean
_SCSecKeychainPasswordItemRemove(SecKeychainRef keychain, CFStringRef unique_id)
{
SecKeychainItemRef item;
const char *keychain_serviceName;
OSStatus status;
keychain_serviceName = _SC_cfstring_to_cstring(unique_id, NULL, 0, kCFStringEncodingUTF8);
status = findKeychainItem(keychain,
strlen(keychain_serviceName),
(void *)keychain_serviceName,
&item);
CFAllocatorDeallocate(NULL, (void *)keychain_serviceName);
if (status != noErr) {
_SCErrorSet(status);
return FALSE;
}
status = SecKeychainItemDelete(item);
CFRelease(item);
if (status != noErr) {
_SCErrorSet(status);
return FALSE;
}
return TRUE;
}
Boolean
_SCSecKeychainPasswordItemSet(SecKeychainRef keychain,
CFStringRef unique_id,
CFStringRef label,
CFStringRef description,
CFStringRef account,
CFDataRef password,
CFDictionaryRef options)
{
SecAccessRef access = NULL;
CFBooleanRef allowRoot = NULL;
CFArrayRef allowedExecutables = NULL;
SecKeychainAttribute attributes[4];
SecKeychainAttributeList attributeList = { 0, attributes };
CFIndex i;
SecKeychainItemRef item = NULL;
CFIndex n = 0;
OSStatus status;
if (options != NULL) {
if (isA_CFDictionary(options)) {
allowRoot = CFDictionaryGetValue(options, kSCKeychainOptionsAllowRoot);
allowedExecutables = CFDictionaryGetValue(options, kSCKeychainOptionsAllowedExecutables);
} else {
_SCErrorSet(kSCStatusInvalidArgument);
return FALSE;
}
}
if (!isA_CFString(unique_id) ||
((label != NULL) && !isA_CFString (label )) ||
((description != NULL) && !isA_CFString (description )) ||
((account != NULL) && !isA_CFString (account )) ||
((password != NULL) && !isA_CFData (password )) ||
((allowRoot != NULL) && !isA_CFBoolean(allowRoot )) ||
((allowedExecutables != NULL) && !isA_CFArray (allowedExecutables)) ||
((allowRoot != NULL) && (allowedExecutables != NULL))) {
_SCErrorSet(kSCStatusInvalidArgument);
return FALSE;
}
if ((allowRoot != NULL) && CFBooleanGetValue(allowRoot)) {
access = _SCSecAccessCreateForUID(0);
if (access == NULL) {
return FALSE;
}
} else if (allowedExecutables != NULL) {
access = _SCSecAccessCreateForExecutables(label, allowedExecutables);
if (access == NULL) {
return FALSE;
}
}
for (i = 0; i < 4; i++) {
CFStringRef str = NULL;
SecKeychainAttrType tag = 0;
switch (i) {
case 0 :
str = unique_id;
tag = kSecServiceItemAttr;
break;
case 1 :
str = label;
tag = kSecLabelItemAttr;
break;
case 2 :
str = description;
tag = kSecDescriptionItemAttr;
break;
case 3 :
str = account;
tag = kSecAccountItemAttr;
break;
}
if (str == NULL) {
continue;
}
attributes[n].tag = tag;
attributes[n].data = _SC_cfstring_to_cstring(str, NULL, 0, kCFStringEncodingUTF8);
attributes[n].length = strlen(attributes[n].data);
n++;
}
status = findKeychainItem(keychain,
attributes[0].length,
attributes[0].data,
&item);
switch (status) {
case noErr : {
const void *pw = NULL;
UInt32 pw_len = 0;
if (password != NULL) {
pw = CFDataGetBytePtr(password);
pw_len = CFDataGetLength(password);
}
attributeList.count = n;
status = SecKeychainItemModifyContent(item,
&attributeList,
pw_len,
pw);
break;
}
case errSecItemNotFound : {
if (password == NULL) {
status = kSCStatusInvalidArgument;
goto done;
}
attributeList.count = n;
status = SecKeychainItemCreateFromContent(kSecGenericPasswordItemClass,
&attributeList,
CFDataGetLength(password),
CFDataGetBytePtr(password),
keychain,
access,
NULL);
break;
}
default :
break;
}
done :
if (access != NULL) CFRelease(access);
if (item != NULL) CFRelease(item);
for (i = 0; i < n; i++) {
CFAllocatorDeallocate(NULL, attributes[i].data);
}
if (status != noErr) {
_SCErrorSet(status);
return FALSE;
}
return TRUE;
}
#pragma mark -
#pragma mark "System" Keychain APIs (w/SCPreferences)
#include "SCHelper_client.h"
#include <fcntl.h>
#include <unistd.h>
#include <sys/errno.h>
static CFDataRef
__SCPreferencesSystemKeychainPasswordItemCopy_helper(SCPreferencesRef prefs,
CFStringRef unique_id)
{
CFDataRef data = NULL;
Boolean ok;
SCPreferencesPrivateRef prefsPrivate = (SCPreferencesPrivateRef)prefs;
uint32_t status = kSCStatusOK;
CFDataRef reply = NULL;
if (prefsPrivate->helper == -1) {
ok = __SCPreferencesCreate_helper(prefs);
if (!ok) {
return FALSE;
}
}
ok = _SCSerializeString(unique_id, &data, NULL, NULL);
if (!ok) {
goto fail;
}
ok = _SCHelperExec(prefsPrivate->helper,
SCHELPER_MSG_KEYCHAIN_COPY,
data,
&status,
&reply);
if (data != NULL) CFRelease(data);
if (!ok) {
goto fail;
}
if (status != kSCStatusOK) {
goto error;
}
return reply;
fail :
if (prefsPrivate->helper != -1) {
_SCHelperClose(prefsPrivate->helper);
prefsPrivate->helper = -1;
}
status = kSCStatusAccessError;
error :
if (reply != NULL) CFRelease(reply);
_SCErrorSet(status);
return NULL;
}
CFDataRef
_SCPreferencesSystemKeychainPasswordItemCopy(SCPreferencesRef prefs,
CFStringRef unique_id)
{
SecKeychainRef keychain = NULL;
CFDataRef password = NULL;
SCPreferencesPrivateRef prefsPrivate = (SCPreferencesPrivateRef)prefs;
if (prefs == NULL) {
_SCErrorSet(kSCStatusNoPrefsSession);
return FALSE;
}
if (!isA_CFString(unique_id)) {
_SCErrorSet(kSCStatusInvalidArgument);
return FALSE;
}
if (prefsPrivate->authorizationData != NULL) {
password = __SCPreferencesSystemKeychainPasswordItemCopy_helper(prefs, unique_id);
goto done;
}
keychain = _SCSecKeychainCopySystemKeychain();
if (keychain == NULL) {
goto done;
}
password = _SCSecKeychainPasswordItemCopy(keychain, unique_id);
done :
if (keychain != NULL) CFRelease(keychain);
return password;
}
Boolean
_SCPreferencesSystemKeychainPasswordItemExists(SCPreferencesRef prefs,
CFStringRef unique_id)
{
SecKeychainRef keychain = NULL;
Boolean ok = FALSE;
if (prefs == NULL) {
_SCErrorSet(kSCStatusNoPrefsSession);
return FALSE;
}
if (!isA_CFString(unique_id)) {
_SCErrorSet(kSCStatusInvalidArgument);
return FALSE;
}
keychain = _SCSecKeychainCopySystemKeychain();
if (keychain == NULL) {
goto done;
}
ok = _SCSecKeychainPasswordItemExists(keychain, unique_id);
done :
if (keychain != NULL) CFRelease(keychain);
return ok;
}
static Boolean
__SCPreferencesSystemKeychainPasswordItemRemove_helper(SCPreferencesRef prefs,
CFStringRef unique_id)
{
CFDataRef data = NULL;
Boolean ok;
SCPreferencesPrivateRef prefsPrivate = (SCPreferencesPrivateRef)prefs;
uint32_t status = kSCStatusOK;
CFDataRef reply = NULL;
if (prefsPrivate->helper == -1) {
ok = __SCPreferencesCreate_helper(prefs);
if (!ok) {
return FALSE;
}
}
ok = _SCSerializeString(unique_id, &data, NULL, NULL);
if (!ok) {
goto fail;
}
ok = _SCHelperExec(prefsPrivate->helper,
SCHELPER_MSG_KEYCHAIN_REMOVE,
data,
&status,
&reply);
if (data != NULL) CFRelease(data);
if (!ok) {
goto fail;
}
if (status != kSCStatusOK) {
goto error;
}
return TRUE;
fail :
if (prefsPrivate->helper != -1) {
_SCHelperClose(prefsPrivate->helper);
prefsPrivate->helper = -1;
}
status = kSCStatusAccessError;
error :
if (reply != NULL) CFRelease(reply);
_SCErrorSet(status);
return FALSE;
}
Boolean
_SCPreferencesSystemKeychainPasswordItemRemove(SCPreferencesRef prefs,
CFStringRef unique_id)
{
SecKeychainRef keychain = NULL;
Boolean ok = FALSE;
SCPreferencesPrivateRef prefsPrivate = (SCPreferencesPrivateRef)prefs;
if (prefs == NULL) {
_SCErrorSet(kSCStatusNoPrefsSession);
return FALSE;
}
if (!isA_CFString(unique_id)) {
_SCErrorSet(kSCStatusInvalidArgument);
return FALSE;
}
if (prefsPrivate->authorizationData != NULL) {
ok = __SCPreferencesSystemKeychainPasswordItemRemove_helper(prefs, unique_id);
goto done;
}
keychain = _SCSecKeychainCopySystemKeychain();
if (keychain == NULL) {
goto done;
}
ok = _SCSecKeychainPasswordItemRemove(keychain, unique_id);
done :
if (keychain != NULL) CFRelease(keychain);
return ok;
}
static Boolean
__SCPreferencesSystemKeychainPasswordItemSet_helper(SCPreferencesRef prefs,
CFStringRef unique_id,
CFStringRef label,
CFStringRef description,
CFStringRef account,
CFDataRef password,
CFDictionaryRef options)
{
CFDataRef data = NULL;
CFMutableDictionaryRef newOptions = NULL;
Boolean ok;
SCPreferencesPrivateRef prefsPrivate = (SCPreferencesPrivateRef)prefs;
uint32_t status = kSCStatusOK;
CFDataRef reply = NULL;
if (prefsPrivate->helper == -1) {
ok = __SCPreferencesCreate_helper(prefs);
if (!ok) {
return FALSE;
}
}
if (isA_CFDictionary(options)) {
CFArrayRef executableURLs = NULL;
newOptions = CFDictionaryCreateMutableCopy(NULL, 0, options);
if (CFDictionaryGetValueIfPresent(newOptions,
kSCKeychainOptionsAllowedExecutables,
(const void **)&executableURLs)) {
CFMutableArrayRef executablePaths;
CFIndex i;
CFIndex n;
CFDataRef path;
executablePaths = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
path = copyMyExecutablePath();
if (path != NULL) {
CFArrayAppendValue(executablePaths, path);
CFRelease(path);
}
n = CFArrayGetCount(executableURLs);
for (i = 0; i < n; i++) {
char fspath[MAXPATHLEN];
CFURLRef url;
url = CFArrayGetValueAtIndex(executableURLs, i);
ok = CFURLGetFileSystemRepresentation(url, TRUE, (UInt8 *)fspath, sizeof(fspath));
if (!ok) {
continue;
}
fspath[sizeof(fspath) - 1] = '\0';
path = CFDataCreate(NULL, (UInt8 *)fspath, strlen(fspath));
CFArrayAppendValue(executablePaths, path);
CFRelease(path);
}
CFDictionarySetValue(newOptions, kSCKeychainOptionsAllowedExecutables, executablePaths);
CFRelease(executablePaths);
}
} else {
newOptions = CFDictionaryCreateMutable(NULL,
0,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
}
if (unique_id != NULL) CFDictionarySetValue(newOptions, kSCKeychainOptionsUniqueID , unique_id);
if (label != NULL) CFDictionarySetValue(newOptions, kSCKeychainOptionsLabel , label);
if (description != NULL) CFDictionarySetValue(newOptions, kSCKeychainOptionsDescription, description);
if (account != NULL) CFDictionarySetValue(newOptions, kSCKeychainOptionsAccount , account);
if (password != NULL) CFDictionarySetValue(newOptions, kSCKeychainOptionsPassword , password);
if (!CFDictionaryContainsKey(newOptions, kSCKeychainOptionsAllowRoot) &&
!CFDictionaryContainsKey(newOptions, kSCKeychainOptionsAllowedExecutables)) {
CFDataRef path;
path = copyMyExecutablePath();
if (path != NULL) {
CFArrayRef executablePaths;
executablePaths = CFArrayCreate(NULL, (const void **)&path, 1, &kCFTypeArrayCallBacks);
CFRelease(path);
CFDictionarySetValue(newOptions, kSCKeychainOptionsAllowedExecutables, executablePaths);
CFRelease(executablePaths);
}
}
ok = _SCSerialize(newOptions, &data, NULL, NULL);
CFRelease(newOptions);
if (!ok) {
goto fail;
}
ok = _SCHelperExec(prefsPrivate->helper,
SCHELPER_MSG_KEYCHAIN_SET,
data,
&status,
&reply);
if (data != NULL) CFRelease(data);
if (!ok) {
goto fail;
}
if (status != kSCStatusOK) {
goto error;
}
return TRUE;
fail :
if (prefsPrivate->helper != -1) {
_SCHelperClose(prefsPrivate->helper);
prefsPrivate->helper = -1;
}
status = kSCStatusAccessError;
error :
if (reply != NULL) CFRelease(reply);
_SCErrorSet(status);
return FALSE;
}
Boolean
_SCPreferencesSystemKeychainPasswordItemSet(SCPreferencesRef prefs,
CFStringRef unique_id,
CFStringRef label,
CFStringRef description,
CFStringRef account,
CFDataRef password,
CFDictionaryRef options)
{
SecKeychainRef keychain = NULL;
Boolean ok = FALSE;
SCPreferencesPrivateRef prefsPrivate = (SCPreferencesPrivateRef)prefs;
if (prefs == NULL) {
_SCErrorSet(kSCStatusNoPrefsSession);
return FALSE;
}
if (!isA_CFString(unique_id) ||
((label != NULL) && !isA_CFString (label )) ||
((description != NULL) && !isA_CFString (description)) ||
((account != NULL) && !isA_CFString (account )) ||
((password != NULL) && !isA_CFData (password )) ||
((options != NULL) && !isA_CFDictionary(options ))) {
_SCErrorSet(kSCStatusInvalidArgument);
return FALSE;
}
if (prefsPrivate->authorizationData != NULL) {
ok = __SCPreferencesSystemKeychainPasswordItemSet_helper(prefs,
unique_id,
label,
description,
account,
password,
options);
goto done;
}
keychain = _SCSecKeychainCopySystemKeychain();
if (keychain == NULL) {
goto done;
}
ok = _SCSecKeychainPasswordItemSet(keychain,
unique_id,
label,
description,
account,
password,
options);
done :
if (keychain != NULL) CFRelease(keychain);
return ok;
}