Kevin
Kevin

Reputation: 96

How do you write to the OSX System keychain?

As part of the process of creating a VPN connection programatically in OSX, using Cocoa, I need to store the PPP password in the System keychain. When I try to do this using the keychain API, I get the following error as a result of calling SecKeychainAddGenericPassword:

"Could not write to the file. It may have been opened with insufficient access privileges."

Here is the code I am using:

- (void)storePasswordInKeychain
{
    SecKeychainRef keychain = nil;
    err = SecKeychainCopyDomainDefault(kSecPreferencesDomainSystem, &keychain);
    if (err != errSecSuccess) {
        NSLog(@"Error getting system keychain: %@", SecCopyErrorMessageString(err, NULL));
    } else {
        NSLog(@"Succeeded opening keychain: %@", SecCopyErrorMessageString(err, NULL));
        SecKeychainItemRef item = nil;        
        err = SecKeychainUnlock(keychain, 0, NULL, FALSE);
        NSLog(@"Keychain unlocked: %@", SecCopyErrorMessageString(err, NULL));

        err = SecKeychainAddGenericPassword (keychain, 
                                         3, "VPN", 
                                         8, "username",
                                         8, "password",
                                         &item);
        NSLog(@"Result of storing password: %@", SecCopyErrorMessageString(err, NULL));
    }
}

The discussion How to write to the System.keychain? makes it seem like I need to make a command line call to /usr/bin/security from within my program, but the point of the Keychain API seems to be to avoid that kind of hackery.

Can anybody point me in the right direction for storing a new password in the System keychain? Thanks.

Upvotes: 4

Views: 4176

Answers (4)

halo
halo

Reputation: 11

It is true that the credentials need to go into the system keychain and not the user keychain. You will not need the SMJobBless to do that.

After you unlocked the keychain, create an SecAccessRef like so:

SecAccessRef access = nil;
status = SecAccessCreate(CFSTR("Some VPN Test"), (__bridge CFArrayRef)(self.trustedApps), &access);

Then build your keychain item

SecKeychainAttribute attrs[] = {
  {kSecLabelItemAttr, (int)strlen(labelUTF8), (char *)labelUTF8},
  {kSecAccountItemAttr, (int)strlen(accountUTF8), (char *)accountUTF8},
  {kSecServiceItemAttr, (int)strlen(serviceUTF8), (char *)serviceUTF8},
  {kSecDescriptionItemAttr, (int)strlen(descriptionUTF8), (char *)descriptionUTF8},
};

And finally store it to the keychain:

  SecKeychainAttributeList attributes = {sizeof(attrs) / sizeof(attrs[0]), attrs};
  status = SecKeychainItemCreateFromContent(kSecGenericPasswordItemClass, &attributes, (int)strlen(passwordUTF8), passwordUTF8, keychain, access, &item);

There is a project on Github which does just that. Have a look at the VPNKeychain.m file to see the whole implementation. https://github.com/halo/macosvpn

Upvotes: 1

iPanda
iPanda

Reputation: 61

You need root privilege when you write something to system keychain. For xcode debugging, you just "EditScheme"(From Menu by "Product->EditScheme...->Run->Info->Debug process as->root"). Well,my xcode version is 6.1, maybe there is some difference in different xcode version. Or just use command line by sudo your app. Hope this helps.

Upvotes: 1

Nathan Kinsinger
Nathan Kinsinger

Reputation: 25041

Apple's current recommendation for privileged access is to use the Service Management API. They have a sample project: SMJobBless. I'm not sure if the sandbox will allow system keychain access if you are targeting the Mac App Store.

The other question you point to seems to be recommending the Authorization Services API which Service Management replaced (in 10.6) and which is explicitly stated as not being allowed at all when sandboxed.

Upvotes: 0

CRD
CRD

Reputation: 53010

Surely a VPN username & password belong to a particular user, do you really mean the system keychain and not the user keychain?

Try dropping the calls to SecKeychainCopyDomainDefault & SecKeychainUnlock and just pass NULL as the first argument to SecKeychainAddGenericPassword - this should add the item to the default keychain.

Upvotes: 0

Related Questions