#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <string.h>
#include <Security/cssmtype.h>
#include <Security/cssmapple.h>
#include <Security/SecKeychain.h>
#include <Security/SecKeychainItem.h>
#include <CoreFoundation/CFString.h>
#include <CoreFoundation/CFUUID.h>
#include "EAPSecurity.h"
#include "EAPKeychainUtil.h"
OSStatus
EAPSecKeychainPasswordItemRemove(SecKeychainRef keychain,
CFStringRef unique_id_str)
{
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);
if (status != noErr) {
fprintf(stderr, "SecKeychainFindGenericPassword failed: %s (%d)\n",
EAPSecurityErrorString(status), (int)status);
goto done;
}
status = SecKeychainItemDelete(item);
CFRelease(item);
if (status != noErr) {
fprintf(stderr, "SecKeychainItemDelete() failed: %s (%d)\n",
EAPSecurityErrorString(status), (int)status);
}
done:
return (status);
}
OSStatus
EAPSecKeychainPasswordItemCopy(SecKeychainRef keychain,
CFStringRef unique_id_str,
CFDataRef * ret_password)
{
void * password;
UInt32 password_length;
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,
&password_length,
&password,
NULL);
CFRelease(unique_id);
if (status == noErr) {
*ret_password = CFDataCreate(NULL, password, password_length);
SecKeychainItemFreeContent(NULL, password);
}
return (status);
}
OSStatus
EAPSecKeychainPasswordItemCreateWithAccess(SecKeychainRef keychain,
SecAccessRef access,
CFStringRef unique_id_str,
CFDataRef label,
CFDataRef description,
CFDataRef user,
CFDataRef password)
{
SecKeychainAttribute attributes[4];
SecKeychainAttributeList attributeList = {
0, attributes
};
int i;
OSStatus status;
CFDataRef unique_id;
unique_id = CFStringCreateExternalRepresentation(NULL,
unique_id_str,
kCFStringEncodingUTF8,
0);
attributes[0].tag = kSecServiceItemAttr;
attributes[0].length = CFDataGetLength(unique_id);
attributes[0].data = (void *)CFDataGetBytePtr(unique_id);
i = 1;
if (label != NULL) {
attributes[i].tag = kSecLabelItemAttr;
attributes[i].length = CFDataGetLength(label);
attributes[i].data = (void *)CFDataGetBytePtr(label);
i++;
}
if (description != NULL) {
attributes[i].tag = kSecDescriptionItemAttr;
attributes[i].length = CFDataGetLength(description);
attributes[i].data = (void *)CFDataGetBytePtr(description);
i++;
}
if (user != NULL) {
attributes[i].tag = kSecAccountItemAttr;
attributes[i].length = CFDataGetLength(user);
attributes[i].data = (void *)CFDataGetBytePtr(user);
i++;
}
attributeList.count = i;
status = SecKeychainItemCreateFromContent(kSecGenericPasswordItemClass,
&attributeList,
CFDataGetLength(password),
CFDataGetBytePtr(password),
keychain,
access,
NULL);
CFRelease(unique_id);
return (status);
}
static CFStringRef
EAPCFUUIDStringCreate(CFAllocatorRef alloc)
{
CFUUIDRef uuid;
CFStringRef uuid_str;
uuid = CFUUIDCreate(alloc);
uuid_str = CFUUIDCreateString(alloc, uuid);
CFRelease(uuid);
return (uuid_str);
}
OSStatus
EAPSecKeychainPasswordItemCreateUniqueWithAccess(SecKeychainRef keychain,
SecAccessRef access,
CFDataRef label,
CFDataRef description,
CFDataRef user,
CFDataRef password,
CFStringRef * ret_unique_id)
{
OSStatus status;
CFStringRef unique_id_str;
if (ret_unique_id != NULL) {
*ret_unique_id = NULL;
}
unique_id_str = EAPCFUUIDStringCreate(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 {
CFRelease(unique_id_str);
}
return (status);
}
OSStatus
EAPSecKeychainPasswordItemSet(SecKeychainRef keychain,
CFStringRef unique_id_str,
CFDataRef password)
{
void * existing_password;
UInt32 existing_password_length;
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,
&existing_password_length,
&existing_password,
&item);
CFRelease(unique_id);
if (status != noErr) {
return (status);
}
if (CFDataGetLength(password) == existing_password_length
&& bcmp(CFDataGetBytePtr(password), existing_password,
existing_password_length) == 0) {
goto done;
}
status
= SecKeychainItemModifyAttributesAndData(item,
NULL,
CFDataGetLength(password),
CFDataGetBytePtr(password));
done:
SecKeychainItemFreeContent(NULL, existing_password);
CFRelease(item);
return (status);
}
#ifdef TEST_EAPKEYCHAINUTIL
static OSStatus
EAPSecAccessCreateWithUid(uid_t uid, SecAccessRef * ret_access)
{
CSSM_ACL_PROCESS_SUBJECT_SELECTOR selector = {
CSSM_ACL_PROCESS_SELECTOR_CURRENT_VERSION,
CSSM_ACL_MATCH_UID,
uid,
0
};
CSSM_LIST_ELEMENT subject2 = {
NULL,
0,
CSSM_LIST_ELEMENT_DATUM
};
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
}
};
subject2.Element.Word.Data = (UInt8 *)&selector;
subject2.Element.Word.Length = sizeof(selector);
return (SecAccessCreateFromOwnerAndACL(&owner,
sizeof(acls) / sizeof(acls[0]),
acls,
ret_access));
}
static OSStatus
SecKeychainCopySystemKeychain(SecKeychainRef * ret_keychain)
{
SecKeychainRef keychain = NULL;
OSStatus status;
status = SecKeychainSetPreferenceDomain(kSecPreferencesDomainSystem);
if (status != noErr) {
goto done;
}
status = SecKeychainCopyDomainDefault(kSecPreferencesDomainSystem,
&keychain);
done:
if (status != noErr && keychain != NULL) {
CFRelease(keychain);
keychain = NULL;
}
*ret_keychain = keychain;
return (status);
}
static void
usage(const char * progname)
{
fprintf(stderr, "%s [system] create <label> <description> <username> <password>\n",
progname);
fprintf(stderr,
"%s [system] set <unique_id> <password>\n",
progname);
fprintf(stderr, "%s [system] delete <unique_id>\n", progname);
fprintf(stderr, "%s [system] get <unique_id>\n", progname);
exit(1);
}
typedef enum {
kCommandUnknown,
kCommandCreate,
kCommandRemove,
kCommandSet,
kCommandGet
} Command;
int
main(int argc, const char *argv[])
{
Command cmd = kCommandUnknown;
SecKeychainRef keychain = NULL;
const char * progname = argv[0];
OSStatus status;
bool use_system = FALSE;
if (argc < 2) {
usage(argv[0]);
}
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 (%ld)\n",
EAPSecurityErrorString(status), status);
exit(1);
}
argc--;
argv++;
}
if (strcmp(argv[1], "create") == 0) {
if (argc < 6) {
usage(progname);
}
cmd = kCommandCreate;
}
else if (strcmp(argv[1], "set") == 0) {
if (argc < 4) {
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: {
CFStringRef unique_id_str;
CFDataRef password;
unique_id_str
= CFStringCreateWithCStringNoCopy(NULL,
argv[2],
kCFStringEncodingUTF8,
kCFAllocatorNull);
password
= CFDataCreateWithBytesNoCopy(NULL,
(const UInt8 *)argv[3],
strlen(argv[3]),
kCFAllocatorNull);
status = EAPSecKeychainPasswordItemSet(keychain,
unique_id_str,
password);
if (status != noErr) {
fprintf(stderr, "EAPSecKeychainItemSet failed %s (%ld)\n",
EAPSecurityErrorString(status), status);
exit(2);
}
CFRelease(password);
break;
}
case kCommandCreate: {
SecAccessRef access = NULL;
CFDataRef unique_id;
CFStringRef unique_id_str = NULL;
CFDataRef label;
CFDataRef description;
CFDataRef username;
CFDataRef password;
if (use_system) {
status = EAPSecAccessCreateWithUid(0, &access);
if (status != noErr) {
fprintf(stderr,
"EAPSecAccessCreateWithUid failed, %s (%ld)\n",
EAPSecurityErrorString(status), status);
exit(2);
}
}
else {
status = SecAccessCreate(CFSTR("keychain"), NULL, &access);
if (status != noErr) {
fprintf(stderr, "SecAccessCreate failed, %s (%ld)\n",
EAPSecurityErrorString(status), status);
exit(2);
}
}
label = CFDataCreateWithBytesNoCopy(NULL,
(const UInt8 *)argv[2],
strlen(argv[2]),
kCFAllocatorNull);
description
= CFDataCreateWithBytesNoCopy(NULL,
(const UInt8 *)argv[3],
strlen(argv[3]),
kCFAllocatorNull);
username
= CFDataCreateWithBytesNoCopy(NULL,
(const UInt8 *)argv[4],
strlen(argv[4]),
kCFAllocatorNull);
password
= CFDataCreateWithBytesNoCopy(NULL,
(const UInt8 *)argv[5],
strlen(argv[5]),
kCFAllocatorNull);
status
= EAPSecKeychainPasswordItemCreateUniqueWithAccess(keychain,
access,
label,
description,
username,
password,
&unique_id_str);
if (status != noErr) {
fprintf(stderr, "EAPSecKeychainItemCreateWithAccessfailed,"
" %s (%ld)\n",
EAPSecurityErrorString(status), status);
exit(1);
}
unique_id
= CFStringCreateExternalRepresentation(NULL,
unique_id_str,
kCFStringEncodingUTF8,
0);
fwrite(CFDataGetBytePtr(unique_id),
CFDataGetLength(unique_id), 1, stdout);
printf("\n");
CFRelease(unique_id_str);
CFRelease(unique_id);
CFRelease(label);
CFRelease(description);
CFRelease(username);
CFRelease(password);
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 (%ld)\n",
EAPSecurityErrorString(status), status);
exit(2);
}
}
else {
CFDataRef password = NULL;
OSStatus status;
status = EAPSecKeychainPasswordItemCopy(keychain,
unique_id_str,
&password);
if (status != noErr) {
fprintf(stderr,
"EAPSecKeychainPasswordItemCopy failed, %s (%ld)\n",
EAPSecurityErrorString(status), status);
exit(2);
}
fwrite(CFDataGetBytePtr(password),
CFDataGetLength(password), 1, stdout);
printf("\n");
CFRelease(password);
}
CFRelease(unique_id_str);
}
default:
break;
}
if (keychain != NULL) {
CFRelease(keychain);
}
exit(0);
return (0);
}
#endif TEST_EAPKEYCHAINUTIL