diff options
Diffstat (limited to 'Source/SPKeychain.m')
-rw-r--r-- | Source/SPKeychain.m | 261 |
1 files changed, 261 insertions, 0 deletions
diff --git a/Source/SPKeychain.m b/Source/SPKeychain.m new file mode 100644 index 00000000..9aea5248 --- /dev/null +++ b/Source/SPKeychain.m @@ -0,0 +1,261 @@ +// +// $Id$ +// +// SPKeychain.m +// sequel-pro +// +// Created by lorenz textor (lorenz@textor.ch) on Wed Dec 25 2002. +// Copyright (c) 2002-2003 Lorenz Textor. All rights reserved. +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +// More info at <http://code.google.com/p/sequel-pro/> + +#import "SPKeychain.h" + +#import <CoreFoundation/CoreFoundation.h> +#import <Security/Security.h> + +@implementation SPKeychain + +/** + * Add the supplied password to the user's Keychain using the supplied name and account. + */ +- (void)addPassword:(NSString *)password forName:(NSString *)name account:(NSString *)account +{ + [self addPassword:password forName:name account:account withLabel:name]; +} + +/** + * Add the supplied password to the user's Keychain using the supplied name, account, and label. + */ +- (void)addPassword:(NSString *)password forName:(NSString *)name account:(NSString *)account withLabel:(NSString *)label; +{ + OSStatus status; + SecTrustedApplicationRef sequelProRef, sequelProHelperRef; + SecAccessRef passwordAccessRef; + SecKeychainAttribute attributes[4]; + SecKeychainAttributeList attList; + + // Check if password already exists before adding + if (![self passwordExistsForName:name account:account]) { + + // Create a trusted access list with two items - ourselves and the SSH pass app. + NSString *helperPath = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:@"SequelProTunnelAssistant"]; + if ((SecTrustedApplicationCreateFromPath(NULL, &sequelProRef) == noErr) && + (SecTrustedApplicationCreateFromPath([helperPath UTF8String], &sequelProHelperRef) == noErr)) { + + NSArray *trustedApps = [NSArray arrayWithObjects:(id)sequelProRef, (id)sequelProHelperRef, nil]; + status = SecAccessCreate((CFStringRef)name, (CFArrayRef)trustedApps, &passwordAccessRef); + if (status != noErr) { + NSLog(@"Error (%i) while trying to create access list for name: %@ account: %@", status, name, account); + passwordAccessRef = NULL; + } + } + + // Set up the item attributes + attributes[0].tag = kSecGenericItemAttr; + attributes[0].data = "application password"; + attributes[0].length = 20; + attributes[1].tag = kSecLabelItemAttr; + attributes[1].data = (unichar *)[label UTF8String]; + attributes[1].length = strlen([label UTF8String]); + attributes[2].tag = kSecAccountItemAttr; + attributes[2].data = (unichar *)[account UTF8String]; + attributes[2].length = strlen([account UTF8String]); + attributes[3].tag = kSecServiceItemAttr; + attributes[3].data = (unichar *)[name UTF8String]; + attributes[3].length = strlen([name UTF8String]); + attList.count = 4; + attList.attr = attributes; + + // Create the keychain item + status = SecKeychainItemCreateFromContent( + kSecGenericPasswordItemClass, // Generic password type + &attList, // The attribute list created for the keychain item + strlen([password UTF8String]), // Length of password + [password UTF8String], // Password data + NULL, // Default keychain + passwordAccessRef, // Access list for this keychain + NULL); // The item reference + + if (passwordAccessRef) CFRelease(passwordAccessRef); + if (status != noErr) { + NSLog(@"Error (%i) while trying to add password for name: %@ account: %@", status, name, account); + } + } +} + +/** + * Get a password from the user's Keychain for the supplied name and account. + */ +- (NSString *)getPasswordForName:(NSString *)name account:(NSString *)account +{ + OSStatus status; + + void *passwordData; + UInt32 passwordLength; + SecKeychainItemRef itemRef; + NSString *password = @""; + + status = SecKeychainFindGenericPassword( + NULL, // default keychain + strlen([name UTF8String]), // length of service name (bytes) + [name UTF8String], // service name + strlen([account UTF8String]), // length of account name (bytes) + [account UTF8String], // account name + &passwordLength, // length of password + &passwordData, // pointer to password data + &itemRef // the item reference + ); + + if (status == noErr) { + password = [NSString stringWithCString:passwordData length:passwordLength]; + + // Free the data allocated by SecKeychainFindGenericPassword: + SecKeychainItemFreeContent( + NULL, // No attribute data to release + passwordData // Release data + ); + } + + return password; +} + +/** + * Delete a password from the user's Keychain for the supplied name and account. + */ +- (void)deletePasswordForName:(NSString *)name account:(NSString *)account +{ + OSStatus status; + SecKeychainItemRef itemRef = nil; + + // Check if password already exists before deleting + if ([self passwordExistsForName:name account:account]) { + status = SecKeychainFindGenericPassword( + NULL, // default keychain + strlen([name UTF8String]), // length of service name + [name UTF8String], // service name + strlen([account UTF8String]), // length of account name + [account UTF8String], // account name + nil, // length of password + nil, // pointer to password data + &itemRef // the item reference + ); + + if (status == noErr) { + status = SecKeychainItemDelete(itemRef); + + if (status != noErr) { + NSLog(@"Error (%i) while trying to delete password for name: %@ account: %@", status, name, account); + } + } + + if (itemRef) CFRelease(itemRef); + } +} + +/** + * Checks the user's Keychain to see if a password for the supplied name and account exists. + */ +- (BOOL)passwordExistsForName:(NSString *)name account:(NSString *)account +{ + SecKeychainItemRef item; + SecKeychainSearchRef search = NULL; + int numberOfItemsFound = 0; + SecKeychainAttributeList list; + SecKeychainAttribute attributes[2]; + + attributes[0].tag = kSecAccountItemAttr; + attributes[0].data = (void *)[account UTF8String]; // Account name + attributes[0].length = strlen([account UTF8String]); // Length of account name (bytes) + + attributes[1].tag = kSecServiceItemAttr; + attributes[1].data = (void *)[name UTF8String]; // Service name + attributes[1].length = strlen([name UTF8String]); // Length of service name (bytes) + + list.count = 2; + list.attr = attributes; + + if (SecKeychainSearchCreateFromAttributes(NULL, kSecGenericPasswordItemClass, &list, &search) == noErr) { + while (SecKeychainSearchCopyNext(search, &item) == noErr) { + CFRelease(item); + numberOfItemsFound++; + } + } + + if (search) CFRelease(search); + + return (numberOfItemsFound > 0); +} + +/** + * Retrieve the keychain item name for a supplied name and id. + */ +- (NSString *)nameForFavoriteName:(NSString *)theName id:(NSString *)theID +{ + NSString *keychainItemName; + + keychainItemName = [NSString stringWithFormat:@"Sequel Pro : %@ (%i)", + theName, + [theID intValue]]; + + return keychainItemName; +} + +/** + * Retrieve the keychain item account for a supplied user, host, and database - which can be nil. + */ +- (NSString *)accountForUser:(NSString *)theUser host:(NSString *)theHost database:(NSString *)theDatabase +{ + NSString *keychainItemAccount; + + keychainItemAccount = [NSString stringWithFormat:@"%@@%@/%@", + theUser?theUser:@"", + theHost?theHost:@"", + theDatabase?theDatabase:@""]; + + return keychainItemAccount; +} + +/** + * Retrieve the keychain SSH item name for a supplied name and id. + */ +- (NSString *)nameForSSHForFavoriteName:(NSString *)theName id:(NSString *)theID +{ + NSString *sshKeychainItemName; + + sshKeychainItemName = [NSString stringWithFormat:@"Sequel Pro SSHTunnel : %@ (%i)", + theName, + [theID intValue]]; + + return sshKeychainItemName; +} + +/** + * Retrieve the keychain SSH item account for a supplied SSH user and host - which can be nil. + */ +- (NSString *)accountForSSHUser:(NSString *)theSSHUser sshHost:(NSString *)theSSHHost +{ + NSString *sshKeychainItemAccount; + + sshKeychainItemAccount = [NSString stringWithFormat:@"%@@%@", + theSSHUser?theSSHUser:@"", + theSSHHost?theSSHHost:@""]; + + return sshKeychainItemAccount; +} + +@end |