Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Replace SecKeychain API with new kSecClass API #4

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 21 additions & 9 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,22 +23,34 @@ jobs:
- name: Install system deps for ubuntu
if: matrix.os == 'ubuntu-latest'
run: |
sudo apt install -y gnome-keyring dbus-x11
sudo apt install -y gnome-keyring dbus-x11 meson ninja-build

- name: Build windows and macos
if: matrix.os == ('windows-test' || 'macos-latest')
uses: BSFishy/meson-build@v1.0.3
with:
action: test
directory: build
ninja-version: 1.10.2

- name: Install mamba
uses: mamba-org/provision-with-micromamba@main
if: matrix.os == 'ubuntu-latest'
uses: mamba-org/setup-micromamba@v1
with:
environment-file: .github/environment.yml
cache-environment: true
post-cleanup: "all"
init-shell: bash

- name: Build
shell: bash -l {0}
- name: Build ubuntu
if: matrix.os == 'ubuntu-latest'
run: |
if [[ ${{ matrix.os }} == 'ubuntu-latest' ]]; then
export $(dbus-launch)
export $(echo 'somerandompass' | gnome-keyring-daemon --unlock)
fi
micromamba activate buildenv
export $(dbus-launch)
export $(echo 'somerandompass' | gnome-keyring-daemon --unlock)
meson setup builddir
cd builddir
ninja
ninja test
./ex1
exit
shell: bash {0}
199 changes: 133 additions & 66 deletions src/libcred_macos.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -63,14 +63,22 @@ namespace libcred
const std::string& password,
std::string* error)
{
OSStatus status = SecKeychainAddGenericPassword(NULL,
service.length(),
service.data(),
account.length(),
account.data(),
password.length(),
password.data(),
NULL);
CFStringRef serviceRef
= CFStringCreateWithCString(NULL, service.c_str(), kCFStringEncodingUTF8);
CFStringRef accountRef
= CFStringCreateWithCString(NULL, account.c_str(), kCFStringEncodingUTF8);
CFDataRef passwordDataRef = CFDataCreate(
NULL, reinterpret_cast<const UInt8*>(password.c_str()), password.length());

CFMutableDictionaryRef attributes = CFDictionaryCreateMutable(
NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
CFDictionaryAddValue(attributes, kSecClass, kSecClassInternetPassword);
CFDictionaryAddValue(attributes, kSecAttrServer, serviceRef);
CFDictionaryAddValue(attributes, kSecAttrAccount, accountRef);
CFDictionaryAddValue(attributes, kSecValueData, passwordDataRef);

// Add the item to the keychain
OSStatus status = SecItemAdd(attributes, NULL);

if (status != errSecSuccess)
{
Expand All @@ -86,34 +94,51 @@ namespace libcred
const std::string& password,
std::string* error)
{
SecKeychainItemRef item;
OSStatus result = SecKeychainFindGenericPassword(NULL,
service.length(),
service.data(),
account.length(),
account.data(),
NULL,
NULL,
&item);

if (result == errSecItemNotFound)
CFStringRef cfAccount
= CFStringCreateWithCString(NULL, account.c_str(), kCFStringEncodingUTF8);
CFStringRef cfService
= CFStringCreateWithCString(NULL, service.c_str(), kCFStringEncodingUTF8);
CFDataRef cfNewPassword
= CFDataCreate(NULL, (const UInt8*) password.c_str(), password.length());

const void* queryKeys[] = { kSecClass, kSecAttrAccount, kSecAttrServer };
const void* queryValues[] = { kSecClassInternetPassword, cfAccount, cfService };
CFDictionaryRef query = CFDictionaryCreate(NULL,
queryKeys,
queryValues,
3,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);

// Create an update dictionary with the new password
const void* updateKeys[] = { kSecValueData };
const void* updateValues[] = { cfNewPassword };
CFDictionaryRef update = CFDictionaryCreate(NULL,
updateKeys,
updateValues,
1,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);

// Perform the update
OSStatus status = SecItemUpdate(query, update);

if (status == errSecItemNotFound)
{
return AddPassword(service, account, password, error);
}
else if (result != errSecSuccess)
else if (status != errSecSuccess)
{
*error = errorStatusToString(result);
*error = errorStatusToString(status);
return FAIL_ERROR;
}

result = SecKeychainItemModifyAttributesAndData(
item, NULL, password.length(), password.data());
CFRelease(item);
if (result != errSecSuccess)
{
*error = errorStatusToString(result);
return FAIL_ERROR;
}
// Clean up
CFRelease(cfAccount);
CFRelease(cfService);
CFRelease(cfNewPassword);
CFRelease(query);
CFRelease(update);

return SUCCESS;
}
Expand All @@ -123,16 +148,30 @@ namespace libcred
std::string* password,
std::string* error)
{
void* data;
UInt32 length;
OSStatus status = SecKeychainFindGenericPassword(NULL,
service.length(),
service.data(),
account.length(),
account.data(),
&length,
&data,
NULL);
CFStringRef cfAccount
= CFStringCreateWithCString(NULL, account.c_str(), kCFStringEncodingUTF8);
CFStringRef cfService
= CFStringCreateWithCString(NULL, service.c_str(), kCFStringEncodingUTF8);

const void* keys[]
= { kSecClass, kSecAttrAccount, kSecAttrServer, kSecReturnData, kSecMatchLimit };
const void* values[] = {
kSecClassInternetPassword, cfAccount, cfService, kCFBooleanTrue, kSecMatchLimitOne
};

CFDictionaryRef query = CFDictionaryCreate(NULL,
keys,
values,
5,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);

CFTypeRef result = NULL;
OSStatus status = SecItemCopyMatching(query, &result);

CFRelease(cfAccount);
CFRelease(cfService);
CFRelease(query);

if (status == errSecItemNotFound)
{
Expand All @@ -144,24 +183,41 @@ namespace libcred
return FAIL_ERROR;
}

*password = std::string(reinterpret_cast<const char*>(data), length);
SecKeychainItemFreeContent(NULL, data);
CFDataRef passwordData = (CFDataRef) result;
*password = std::string((const char*) CFDataGetBytePtr(passwordData),
CFDataGetLength(passwordData));
CFRelease(passwordData);
return SUCCESS;
}

LIBCRED_RESULT delete_password(const std::string& service,
const std::string& account,
std::string* error)
{
SecKeychainItemRef item;
OSStatus status = SecKeychainFindGenericPassword(NULL,
service.length(),
service.data(),
account.length(),
account.data(),
NULL,
NULL,
&item);
// Create a query dictionary to find the existing item
CFStringRef cfAccount
= CFStringCreateWithCString(NULL, account.c_str(), kCFStringEncodingUTF8);
CFStringRef cfService
= CFStringCreateWithCString(NULL, service.c_str(), kCFStringEncodingUTF8);

const void* keys[] = { kSecClass, kSecAttrAccount, kSecAttrServer };
const void* values[] = { kSecClassInternetPassword, cfAccount, cfService };

CFDictionaryRef query = CFDictionaryCreate(NULL,
keys,
values,
3,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);

// Perform the deletion
OSStatus status = SecItemDelete(query);

// Clean up
CFRelease(cfAccount);
CFRelease(cfService);
CFRelease(query);

if (status == errSecItemNotFound)
{
// Item could not be found, so already deleted.
Expand All @@ -173,27 +229,36 @@ namespace libcred
return FAIL_ERROR;
}

status = SecKeychainItemDelete(item);
CFRelease(item);
if (status != errSecSuccess)
{
*error = errorStatusToString(status);
return FAIL_ERROR;
}

return SUCCESS;
}

LIBCRED_RESULT find_password(const std::string& service,
std::string* password,
std::string* error)
{
SecKeychainItemRef item;
void* data;
UInt32 length;
// Create a query dictionary
CFStringRef cfService
= CFStringCreateWithCString(NULL, service.c_str(), kCFStringEncodingUTF8);

const void* keys[] = { kSecClass, kSecAttrServer, kSecReturnData, kSecMatchLimit };
const void* values[]
= { kSecClassInternetPassword, cfService, kCFBooleanTrue, kSecMatchLimitOne };

CFDictionaryRef query = CFDictionaryCreate(NULL,
keys,
values,
4,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);

// Perform the query
CFTypeRef result = NULL;
OSStatus status = SecItemCopyMatching(query, &result);

// Clean up
CFRelease(cfService);
CFRelease(query);

OSStatus status = SecKeychainFindGenericPassword(
NULL, service.length(), service.data(), 0, NULL, &length, &data, &item);
if (status == errSecItemNotFound)
{
return FAIL_NONFATAL;
Expand All @@ -204,9 +269,11 @@ namespace libcred
return FAIL_ERROR;
}

*password = std::string(reinterpret_cast<const char*>(data), length);
SecKeychainItemFreeContent(NULL, data);
CFRelease(item);
CFDataRef passwordData = (CFDataRef) result;
*password = std::string((const char*) CFDataGetBytePtr(passwordData),
CFDataGetLength(passwordData));
CFRelease(passwordData);

return SUCCESS;
}

Expand Down