internal/pkg/agent/vault/vault_keychain_darwin.c (165 lines of code) (raw):
// Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
// or more contributor license agreements. Licensed under the Elastic License;
// you may not use this file except in compliance with the Elastic License.
// CreateAccessWithUid is based on a modification of MyMakeUidAccess
// at https://opensource.apple.com/source/mDNSResponder/mDNSResponder-1310.80.1/mDNSMacOSX/PreferencePane/BonjourPrefTool/BonjourPrefTool.m
// which is licensed under Apache 2.0
#include <stdio.h>
#include <errno.h>
#include <CoreFoundation/CoreFoundation.h>
#include <Security/Security.h>
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
OSStatus CreateAccessWithUid(uid_t uid, SecAccessRef * ret_access) {
// make the "uid/gid" ACL subject
// this is a CSSM_LIST_ELEMENT chain
CSSM_ACL_PROCESS_SUBJECT_SELECTOR selector = {
CSSM_ACL_PROCESS_SELECTOR_CURRENT_VERSION, // selector version
CSSM_ACL_MATCH_UID, // set mask: match uids (only)
uid, // uid to match
0 // gid (not matched here)
};
CSSM_LIST_ELEMENT subject2 = { NULL, 0, 0, {{0,0,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, {{0,0,0}} };
// rights granted (replace with individual list if desired)
CSSM_ACL_AUTHORIZATION_TAG rights[] = {
CSSM_ACL_AUTHORIZATION_ANY // everything
};
// owner component (right to change ACL)
CSSM_ACL_OWNER_PROTOTYPE owner = {
// TypedSubject
{ CSSM_LIST_TYPE_UNKNOWN, &subject1, &subject2 },
// Delegate
false
};
// ACL entries (any number, just one here)
CSSM_ACL_ENTRY_INFO acls =
{
// CSSM_ACL_ENTRY_PROTOTYPE
{
{ CSSM_LIST_TYPE_UNKNOWN, &subject1, &subject2 }, // TypedSubject
false, // Delegate
{ sizeof(rights) / sizeof(rights[0]), rights }, // Authorization rights for this entry
{ { 0, 0 }, { 0, 0 } }, // CSSM_ACL_VALIDITY_PERIOD
"" // CSSM_STRING EntryTag
},
// CSSM_ACL_HANDLE
0
};
return SecAccessCreateFromOwnerAndACL(&owner, 1, &acls, ret_access);
}
OSStatus OpenKeychain(SecKeychainRef keychain) {
OSStatus status = SecKeychainSetPreferenceDomain(kSecPreferencesDomainSystem);
if (status == noErr) {
status = SecKeychainCopyDomainDefault(kSecPreferencesDomainSystem, &keychain);
}
return status;
}
OSStatus UpdateKeychainItem(SecKeychainRef keychain, const char *name, const char *key, const void *data, size_t len) {
void* pwd = NULL;
UInt32 pwd_len = 0;
SecKeychainItemRef item = NULL;
OSStatus status = SecKeychainFindGenericPassword(keychain,
(UInt32)strlen(key), key,
(UInt32)strlen(name), name,
&pwd_len, &pwd,
&item);
if (status == noErr) { // item is found, update the value
if ((len != pwd_len) || (bcmp(data, pwd, pwd_len) != 0)) {
status = SecKeychainItemModifyAttributesAndData(item, NULL, len, data);
}
}
if (pwd != NULL) {
SecKeychainItemFreeContent(NULL, pwd);
pwd = NULL;
}
return status;
}
OSStatus SetKeychainItem(SecKeychainRef keychain, const char *name, const char *key, const void *data, size_t len) {
SecKeychainItemRef item = NULL;
OSStatus status = UpdateKeychainItem(keychain, name, key, data, len);
if (status == errSecItemNotFound) {
SecAccessRef access = NULL;
status = CreateAccessWithUid(0, &access); // 0 for root uid
if (status == noErr) {
size_t sz = strlen(name);
SecKeychainAttribute attrs[] = {
{ kSecLabelItemAttr, (UInt32)sz, (char*)name },
{ kSecAccountItemAttr, (UInt32)sz, (char*)name },
{ kSecServiceItemAttr, (UInt32)strlen(key), (char*)key }
};
SecKeychainAttributeList attributes = { sizeof(attrs) / sizeof(attrs[0]),
attrs };
status = SecKeychainItemCreateFromContent(
kSecGenericPasswordItemClass,
&attributes,
(UInt32)len,
data,
keychain,
access,
&item);
if (status == errSecDuplicateItem) {
status = UpdateKeychainItem(keychain, name, key, data, len);
}
}
if (access != NULL) {
CFRelease(access);
}
}
if (item != NULL) {
CFRelease(item);
item = NULL;
}
return status;
}
OSStatus GetKeychainItem(SecKeychainRef keychain, const char *name, const char *key, void **data, size_t *len) {
void* pwd = NULL;
UInt32 pwd_len = 0;
SecKeychainItemRef item = NULL;
OSStatus status = SecKeychainFindGenericPassword(keychain,
(UInt32)strlen(key), key,
(UInt32)strlen(name), name,
&pwd_len, &pwd,
&item);
if (status == noErr) {
*data = malloc(pwd_len);
memcpy(*data, pwd, pwd_len);
*len = pwd_len;
}
if (pwd != NULL) {
SecKeychainItemFreeContent(NULL, pwd);
pwd = NULL;
}
if (item != NULL) {
CFRelease(item);
item = NULL;
}
return status;
}
OSStatus ExistsKeychainItem(SecKeychainRef keychain, const char *name, const char *key) {
SecKeychainItemRef item = NULL;
OSStatus status = SecKeychainFindGenericPassword(keychain,
(UInt32)strlen(key), key,
(UInt32)strlen(name), name,
NULL, NULL,
&item);
if (item != NULL) {
CFRelease(item);
item = NULL;
}
return status;
}
OSStatus RemoveKeychainItem(SecKeychainRef keychain, const char *name, const char *key) {
SecKeychainItemRef item = NULL;
OSStatus status = SecKeychainFindGenericPassword(keychain,
(UInt32)strlen(key), key,
(UInt32)strlen(name), name,
NULL, NULL,
&item);
if (status == noErr) {
status = SecKeychainItemDelete(item);
}
if (item != NULL) {
CFRelease(item);
item = NULL;
}
return status;
}
#pragma clang diagnostic pop
char* GetOSStatusMessage(OSStatus status) {
CFStringRef s = SecCopyErrorMessageString(status, NULL);
char *p;
int n;
n = CFStringGetLength(s)*8;
p = malloc(n);
CFStringGetCString(s, p, n, kCFStringEncodingUTF8);
CFRelease(s);
return p;
}