Hi,
In the app I’m working on, we rely on SecKeychainUnlock to verify that a password can be used to unlock the login keychain. When macOS 26.4 rolled out, we started getting bug reports that led me to a discovery that makes me think SecKeychainUnlock behavior was changed. I’m going to illustrate my findings with a sample code:
#include <pwd.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <Security/SecKeychain.h>
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
int
main(void)
{
char password[100];
printf("password: ");
scanf("%s", password);
struct passwd *home = getpwuid(getuid());
if (!(home && home->pw_dir))
return 1;
char path[1024];
strcat(path, home->pw_dir);
strcat(path, "/Library/Keychains/login.keychain-db");
SecKeychainRef keychain = NULL;
OSStatus result = SecKeychainOpen(path, &keychain);
if (result != errSecSuccess) {
fprintf(stderr, "SecKeychainOpen failed (error %d)\n", result);
return 1;
}
SecKeychainStatus status = 0;
result = SecKeychainGetStatus(keychain, &status);
if (result != errSecSuccess) {
fprintf(stderr, "SecKeychainGetStatus failed (error %d)\n", result);
return 1;
}
if (status & kSecUnlockStateStatus) {
printf("keychain is unlocked, will try to lock first\n");
result = SecKeychainLock(keychain);
if (result != errSecSuccess) {
fprintf(stderr, "SecKeychainLock failed (error %d)\n", result);
return 1;
}
printf("SecKeychainLock succeeded\n");
} else {
printf("keychain is locked\n");
}
result = SecKeychainUnlock(keychain, strlen(password), password, TRUE);
if (result == errSecSuccess) {
printf("SecKeychainUnlock succeeded\n");
printf("password '%s' appears to be valid\n", password);
} else {
printf("SecKeychainUnlock failed (error %d)\n", result);
printf("password '%s' appears to be invalid\n", password);
}
return 0;
}
Here are the outputs of this program on a machine running macOS 26.3 when provided with a correct password deadbeef and with an incorrect password foobar:
testuser1@tahoe1 kcdebug % ./kcdebug
password: deadbeef
keychain is unlocked, will try to lock first
SecKeychainLock succeeded
SecKeychainUnlock succeeded
password 'deadbeef' appears to be valid
testuser1@tahoe1 kcdebug % ./kcdebug
password: foobar
keychain is unlocked, will try to lock first
SecKeychainLock succeeded
SecKeychainUnlock failed (error -25293)
password 'foobar' appears to be invalid
And here are the outputs of this program on a machine running macOS 26.4:
testuser1@tahoe2 kcdebug % ./kcdebug
password: deadbeef
keychain is unlocked, will try to lock first
SecKeychainLock succeeded
SecKeychainUnlock succeeded
password 'deadbeef' appears to be valid
testuser1@tahoe2 kcdebug % ./kcdebug
password: foobar
keychain is unlocked, will try to lock first
SecKeychainLock succeeded
SecKeychainUnlock succeeded
password 'foobar' appears to be valid
I’m prepared to send a feedback with Feedback Assistant, but I would like to get a confirmation that this is indeed a bug and not an intended change in behavior. I would also like to know what are my options now. SecKeychainUnlock is just a means to an end; what I really need is the ability to keep the keychain password in sync with the user password when the latter is changed by our program.
Thanks in advance.
We’re using SecKeychainChangePassword to change the keychain password.
To be clear, that’s not API, and thus not supported by DTS.
There isn’t a good way to change the password for a file-based keychain. I’ve seen folks do it by spawning the security tool with the set-keychain-password command, but that’s problematic on at least two levels:
- Using command-line tools as API is usually a bad option.
- Especially in a case like this, where the tool was clearly designed to be used by a user.
- You either have to pass in the passwords as arguments or do the pseudo-TTY dance, neither of which is ideal.
I discussed your issue with some folks internally and we couldn’t come up with any good suggestions for you. What you really want is an API to change the user’s password in all relevant contexts (OD, login keychain, data protection keychain, FileVault, and probably more than I’m forgetting). Such an API does not exist.
I recommend that you create an enhancement request for that. I can’t promise it’ll ever happen, but you should at least get your request on file.
IMPORTANT Make sure your ER fully captures your requirements. For example:
- How you’d like to prevent unnecessary work, like you’re currently doing with
SecKeychainUnlock. - What you expect to happen if the password can be changed in one subsystem but not another.
And please post your bug number, just for the record.
Beyond that, I don’t have any good suggestions for you. You’ve chosen a fundamentally bumpy path, and this but one of many bumps that you’ll encounter )-:
Share and Enjoy
—
Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"