Blame client/iOS/Misc/SFHFKeychainUtils.m

Packit 1fb8d4
//
Packit 1fb8d4
//  SFHFKeychainUtils.m
Packit 1fb8d4
//
Packit 1fb8d4
//  Created by Buzz Andersen on 10/20/08.
Packit 1fb8d4
//  Based partly on code by Jonathan Wight, Jon Crosby, and Mike Malone.
Packit 1fb8d4
//  Copyright 2008 Sci-Fi Hi-Fi. All rights reserved.
Packit 1fb8d4
//
Packit 1fb8d4
//  Permission is hereby granted, free of charge, to any person
Packit 1fb8d4
//  obtaining a copy of this software and associated documentation
Packit 1fb8d4
//  files (the "Software"), to deal in the Software without
Packit 1fb8d4
//  restriction, including without limitation the rights to use,
Packit 1fb8d4
//  copy, modify, merge, publish, distribute, sublicense, and/or sell
Packit 1fb8d4
//  copies of the Software, and to permit persons to whom the
Packit 1fb8d4
//  Software is furnished to do so, subject to the following
Packit 1fb8d4
//  conditions:
Packit 1fb8d4
//
Packit 1fb8d4
//  The above copyright notice and this permission notice shall be
Packit 1fb8d4
//  included in all copies or substantial portions of the Software.
Packit 1fb8d4
//
Packit 1fb8d4
//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
Packit 1fb8d4
//  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
Packit 1fb8d4
//  OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
Packit 1fb8d4
//  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
Packit 1fb8d4
//  HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
Packit 1fb8d4
//  WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
Packit 1fb8d4
//  FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
Packit 1fb8d4
//  OTHER DEALINGS IN THE SOFTWARE.
Packit 1fb8d4
//
Packit 1fb8d4
Packit 1fb8d4
#import "SFHFKeychainUtils.h"
Packit 1fb8d4
#import <Security/Security.h>
Packit 1fb8d4
Packit 1fb8d4
static NSString *SFHFKeychainUtilsErrorDomain = @"SFHFKeychainUtilsErrorDomain";
Packit 1fb8d4
Packit 1fb8d4
#if __IPHONE_OS_VERSION_MIN_REQUIRED < 30000 && TARGET_IPHONE_SIMULATOR
Packit 1fb8d4
@interface SFHFKeychainUtils (PrivateMethods)
Packit 1fb8d4
+ (SecKeychainItemRef) getKeychainItemReferenceForUsername: (NSString *) username andServerName: (NSString *) serverName error: (NSError **) error;
Packit 1fb8d4
@end
Packit 1fb8d4
#endif
Packit 1fb8d4
Packit 1fb8d4
@implementation SFHFKeychainUtils
Packit 1fb8d4
Packit 1fb8d4
#if __IPHONE_OS_VERSION_MIN_REQUIRED < 30000 && TARGET_IPHONE_SIMULATOR
Packit 1fb8d4
Packit 1fb8d4
+ (NSString *) getPasswordForUsername: (NSString *) username andServerName: (NSString *) serverName error: (NSError **) error {
Packit 1fb8d4
	if (!username || !serviceName) {
Packit 1fb8d4
		*error = [NSError errorWithDomain: SFHFKeychainUtilsErrorDomain code: -2000 userInfo: nil];
Packit 1fb8d4
		return nil;
Packit 1fb8d4
	}
Packit 1fb8d4
	
Packit 1fb8d4
	SecKeychainItemRef item = [SFHFKeychainUtils getKeychainItemReferenceForUsername: username andServerName: serverName error: error];
Packit 1fb8d4
	
Packit 1fb8d4
	if (*error || !item) {
Packit 1fb8d4
		return nil;
Packit 1fb8d4
	}
Packit 1fb8d4
	
Packit 1fb8d4
	// from Advanced Mac OS X Programming, ch. 16
Packit 1fb8d4
  UInt32 length;
Packit 1fb8d4
  char *password;
Packit 1fb8d4
  SecKeychainAttribute attributes[8];
Packit 1fb8d4
  SecKeychainAttributeList list;
Packit 1fb8d4
	
Packit 1fb8d4
  attributes[0].tag = kSecAccountItemAttr;
Packit 1fb8d4
  attributes[1].tag = kSecDescriptionItemAttr;
Packit 1fb8d4
  attributes[2].tag = kSecLabelItemAttr;
Packit 1fb8d4
  attributes[3].tag = kSecModDateItemAttr;
Packit 1fb8d4
  
Packit 1fb8d4
  list.count = 4;
Packit 1fb8d4
  list.attr = attributes;
Packit 1fb8d4
  
Packit 1fb8d4
  OSStatus status = SecKeychainItemCopyContent(item, NULL, &list, &length, (void **)&password);
Packit 1fb8d4
	
Packit 1fb8d4
	if (status != noErr) {
Packit 1fb8d4
		*error = [NSError errorWithDomain: SFHFKeychainUtilsErrorDomain code: status userInfo: nil];
Packit 1fb8d4
		return nil;
Packit 1fb8d4
  }
Packit 1fb8d4
  
Packit 1fb8d4
	NSString *passwordString = nil;
Packit 1fb8d4
	
Packit 1fb8d4
	if (password != NULL) {
Packit 1fb8d4
		char passwordBuffer[1024];
Packit 1fb8d4
		
Packit 1fb8d4
		if (length > 1023) {
Packit 1fb8d4
			length = 1023;
Packit 1fb8d4
		}
Packit 1fb8d4
		strncpy(passwordBuffer, password, length);
Packit 1fb8d4
		
Packit 1fb8d4
		passwordBuffer[length] = '\0';
Packit 1fb8d4
		passwordString = [NSString stringWithCString:passwordBuffer];
Packit 1fb8d4
	}
Packit 1fb8d4
	
Packit 1fb8d4
	SecKeychainItemFreeContent(&list, password);
Packit 1fb8d4
  
Packit 1fb8d4
  CFRelease(item);
Packit 1fb8d4
  
Packit 1fb8d4
  return passwordString;
Packit 1fb8d4
}
Packit 1fb8d4
Packit 1fb8d4
+ (void) storeUsername: (NSString *) username andPassword: (NSString *) password forServerName: (NSString *) serverName updateExisting: (BOOL) updateExisting error: (NSError **) error {	
Packit 1fb8d4
	if (!username || !password || !serverName) {
Packit 1fb8d4
		*error = [NSError errorWithDomain: SFHFKeychainUtilsErrorDomain code: -2000 userInfo: nil];
Packit 1fb8d4
		return;
Packit 1fb8d4
	}
Packit 1fb8d4
	
Packit 1fb8d4
	OSStatus status = noErr;
Packit 1fb8d4
	
Packit 1fb8d4
	SecKeychainItemRef item = [SFHFKeychainUtils getKeychainItemReferenceForUsername: username andServerName: serverName error: error];
Packit 1fb8d4
	
Packit 1fb8d4
	if (*error && [*error code] != noErr) {
Packit 1fb8d4
		return;
Packit 1fb8d4
	}
Packit 1fb8d4
	
Packit 1fb8d4
	*error = nil;
Packit 1fb8d4
	
Packit 1fb8d4
	if (item) {
Packit 1fb8d4
		status = SecKeychainItemModifyAttributesAndData(item,
Packit 1fb8d4
                                                    NULL,
Packit 1fb8d4
                                                    strlen([password UTF8String]),
Packit 1fb8d4
                                                    [password UTF8String]);
Packit 1fb8d4
		
Packit 1fb8d4
		CFRelease(item);
Packit 1fb8d4
	}
Packit 1fb8d4
	else {
Packit 1fb8d4
		status = SecKeychainAddGenericPassword(NULL,                                     
Packit 1fb8d4
                                           strlen([serverName UTF8String]), 
Packit 1fb8d4
                                           [serverName UTF8String],
Packit 1fb8d4
                                           strlen([username UTF8String]),                        
Packit 1fb8d4
                                           [username UTF8String],
Packit 1fb8d4
                                           strlen([password UTF8String]),
Packit 1fb8d4
                                           [password UTF8String],
Packit 1fb8d4
                                           NULL);		 
Packit 1fb8d4
	}
Packit 1fb8d4
	
Packit 1fb8d4
	if (status != noErr) {
Packit 1fb8d4
		*error = [NSError errorWithDomain: SFHFKeychainUtilsErrorDomain code: status userInfo: nil];
Packit 1fb8d4
	}
Packit 1fb8d4
}
Packit 1fb8d4
Packit 1fb8d4
+ (void) deleteItemForUsername: (NSString *) username andServerName: (NSString *) serverName error: (NSError **) error {
Packit 1fb8d4
	if (!username || !serverName) {
Packit 1fb8d4
		*error = [NSError errorWithDomain: SFHFKeychainUtilsErrorDomain code: 2000 userInfo: nil];
Packit 1fb8d4
		return;
Packit 1fb8d4
	}
Packit 1fb8d4
	
Packit 1fb8d4
	*error = nil;
Packit 1fb8d4
	
Packit 1fb8d4
	SecKeychainItemRef item = [SFHFKeychainUtils getKeychainItemReferenceForUsername: username andServerName: serverName error: error];
Packit 1fb8d4
	
Packit 1fb8d4
	if (*error && [*error code] != noErr) {
Packit 1fb8d4
		return;
Packit 1fb8d4
	}
Packit 1fb8d4
	
Packit 1fb8d4
	OSStatus status;
Packit 1fb8d4
	
Packit 1fb8d4
	if (item) {
Packit 1fb8d4
		status = SecKeychainItemDelete(item);
Packit 1fb8d4
		
Packit 1fb8d4
		CFRelease(item);
Packit 1fb8d4
	}
Packit 1fb8d4
	
Packit 1fb8d4
	if (status != noErr) {
Packit 1fb8d4
		*error = [NSError errorWithDomain: SFHFKeychainUtilsErrorDomain code: status userInfo: nil];
Packit 1fb8d4
	}
Packit 1fb8d4
}
Packit 1fb8d4
Packit 1fb8d4
+ (SecKeychainItemRef) getKeychainItemReferenceForUsername: (NSString *) username andServerName: (NSString *) serverName error: (NSError **) error {
Packit 1fb8d4
	if (!username || !serverName) {
Packit 1fb8d4
		*error = [NSError errorWithDomain: SFHFKeychainUtilsErrorDomain code: -2000 userInfo: nil];
Packit 1fb8d4
		return nil;
Packit 1fb8d4
	}
Packit 1fb8d4
	
Packit 1fb8d4
	*error = nil;
Packit 1fb8d4
  
Packit 1fb8d4
	SecKeychainItemRef item;
Packit 1fb8d4
	
Packit 1fb8d4
	OSStatus status = SecKeychainFindGenericPassword(NULL,
Packit 1fb8d4
                                                   strlen([serverName UTF8String]),
Packit 1fb8d4
                                                   [serverName UTF8String],
Packit 1fb8d4
                                                   strlen([username UTF8String]),
Packit 1fb8d4
                                                   [username UTF8String],
Packit 1fb8d4
                                                   NULL,
Packit 1fb8d4
                                                   NULL,
Packit 1fb8d4
                                                   &item);
Packit 1fb8d4
	
Packit 1fb8d4
	if (status != noErr) {
Packit 1fb8d4
		if (status != errSecItemNotFound) {
Packit 1fb8d4
			*error = [NSError errorWithDomain: SFHFKeychainUtilsErrorDomain code: status userInfo: nil];
Packit 1fb8d4
		}
Packit 1fb8d4
		
Packit 1fb8d4
		return nil;		
Packit 1fb8d4
	}
Packit 1fb8d4
	
Packit 1fb8d4
	return item;
Packit 1fb8d4
}
Packit 1fb8d4
Packit 1fb8d4
#else
Packit 1fb8d4
Packit 1fb8d4
+ (NSString *) getPasswordForUsername: (NSString *) username andServerName: (NSString *) serverName error: (NSError **) error {
Packit 1fb8d4
	if (!username || !serverName) {
Packit 1fb8d4
		if (error != nil) {
Packit 1fb8d4
			*error = [NSError errorWithDomain: SFHFKeychainUtilsErrorDomain code: -2000 userInfo: nil];
Packit 1fb8d4
		}
Packit 1fb8d4
		return nil;
Packit 1fb8d4
	}
Packit 1fb8d4
	
Packit 1fb8d4
	if (error != nil) {
Packit 1fb8d4
		*error = nil;
Packit 1fb8d4
	}
Packit 1fb8d4
  
Packit 1fb8d4
	// Set up a query dictionary with the base query attributes: item type (generic), username, and service
Packit 1fb8d4
	
Packit 1fb8d4
	NSArray *keys = [[[NSArray alloc] initWithObjects: (NSString *) kSecClass, kSecAttrAccount, kSecAttrService, nil] autorelease];
Packit 1fb8d4
	NSArray *objects = [[[NSArray alloc] initWithObjects: (NSString *) kSecClassGenericPassword, username, serverName, nil] autorelease];
Packit 1fb8d4
	
Packit 1fb8d4
	NSMutableDictionary *query = [[[NSMutableDictionary alloc] initWithObjects: objects forKeys: keys] autorelease];
Packit 1fb8d4
	
Packit 1fb8d4
	// First do a query for attributes, in case we already have a Keychain item with no password data set.
Packit 1fb8d4
	// One likely way such an incorrect item could have come about is due to the previous (incorrect)
Packit 1fb8d4
	// version of this code (which set the password as a generic attribute instead of password data).
Packit 1fb8d4
	
Packit 1fb8d4
	NSDictionary *attributeResult = NULL;
Packit 1fb8d4
	NSMutableDictionary *attributeQuery = [query mutableCopy];
Packit 1fb8d4
	[attributeQuery setObject: (id) kCFBooleanTrue forKey:(id) kSecReturnAttributes];
Packit 1fb8d4
	OSStatus status = SecItemCopyMatching((CFDictionaryRef) attributeQuery, (CFTypeRef *) &attributeResult);
Packit 1fb8d4
	
Packit 1fb8d4
	[attributeResult release];
Packit 1fb8d4
	[attributeQuery release];
Packit 1fb8d4
	
Packit 1fb8d4
	if (status != noErr) {
Packit 1fb8d4
		// No existing item found--simply return nil for the password
Packit 1fb8d4
		if (error != nil && status != errSecItemNotFound) {
Packit 1fb8d4
			//Only return an error if a real exception happened--not simply for "not found."
Packit 1fb8d4
			*error = [NSError errorWithDomain: SFHFKeychainUtilsErrorDomain code: status userInfo: nil];
Packit 1fb8d4
		}
Packit 1fb8d4
		
Packit 1fb8d4
		return nil;
Packit 1fb8d4
	}
Packit 1fb8d4
	
Packit 1fb8d4
	// We have an existing item, now query for the password data associated with it.
Packit 1fb8d4
	
Packit 1fb8d4
	NSData *resultData = nil;
Packit 1fb8d4
	NSMutableDictionary *passwordQuery = [query mutableCopy];
Packit 1fb8d4
	[passwordQuery setObject: (id) kCFBooleanTrue forKey: (id) kSecReturnData];
Packit 1fb8d4
  
Packit 1fb8d4
	status = SecItemCopyMatching((CFDictionaryRef) passwordQuery, (CFTypeRef *) &resultData);
Packit 1fb8d4
	
Packit 1fb8d4
	[resultData autorelease];
Packit 1fb8d4
	[passwordQuery release];
Packit 1fb8d4
	
Packit 1fb8d4
	if (status != noErr) {
Packit 1fb8d4
		if (status == errSecItemNotFound) {
Packit 1fb8d4
			// We found attributes for the item previously, but no password now, so return a special error.
Packit 1fb8d4
			// Users of this API will probably want to detect this error and prompt the user to
Packit 1fb8d4
			// re-enter their credentials.  When you attempt to store the re-entered credentials
Packit 1fb8d4
			// using storeUsername:andPassword:forServiceName:updateExisting:error
Packit 1fb8d4
			// the old, incorrect entry will be deleted and a new one with a properly encrypted
Packit 1fb8d4
			// password will be added.
Packit 1fb8d4
			if (error != nil) {
Packit 1fb8d4
				*error = [NSError errorWithDomain: SFHFKeychainUtilsErrorDomain code: -1999 userInfo: nil];
Packit 1fb8d4
			}
Packit 1fb8d4
		}
Packit 1fb8d4
		else {
Packit 1fb8d4
			// Something else went wrong. Simply return the normal Keychain API error code.
Packit 1fb8d4
			if (error != nil) {
Packit 1fb8d4
				*error = [NSError errorWithDomain: SFHFKeychainUtilsErrorDomain code: status userInfo: nil];
Packit 1fb8d4
			}
Packit 1fb8d4
		}
Packit 1fb8d4
		
Packit 1fb8d4
		return nil;
Packit 1fb8d4
	}
Packit 1fb8d4
  
Packit 1fb8d4
	NSString *password = nil;	
Packit 1fb8d4
  
Packit 1fb8d4
	if (resultData) {
Packit 1fb8d4
		password = [[NSString alloc] initWithData: resultData encoding: NSUTF8StringEncoding];
Packit 1fb8d4
	}
Packit 1fb8d4
	else {
Packit 1fb8d4
		// There is an existing item, but we weren't able to get password data for it for some reason,
Packit 1fb8d4
		// Possibly as a result of an item being incorrectly entered by the previous code.
Packit 1fb8d4
		// Set the -1999 error so the code above us can prompt the user again.
Packit 1fb8d4
		if (error != nil) {
Packit 1fb8d4
			*error = [NSError errorWithDomain: SFHFKeychainUtilsErrorDomain code: -1999 userInfo: nil];
Packit 1fb8d4
		}
Packit 1fb8d4
	}
Packit 1fb8d4
  
Packit 1fb8d4
	return [password autorelease];
Packit 1fb8d4
}
Packit 1fb8d4
Packit 1fb8d4
+ (BOOL) storeUsername: (NSString *) username andPassword: (NSString *) password forServerName: (NSString *) serverName updateExisting: (BOOL) updateExisting error: (NSError **) error 
Packit 1fb8d4
{		
Packit 1fb8d4
	if (!username || !password || !serverName) 
Packit 1fb8d4
  {
Packit 1fb8d4
		if (error != nil) 
Packit 1fb8d4
    {
Packit 1fb8d4
			*error = [NSError errorWithDomain: SFHFKeychainUtilsErrorDomain code: -2000 userInfo: nil];
Packit 1fb8d4
		}
Packit 1fb8d4
		return NO;
Packit 1fb8d4
	}
Packit 1fb8d4
	
Packit 1fb8d4
	// See if we already have a password entered for these credentials.
Packit 1fb8d4
	NSError *getError = nil;
Packit 1fb8d4
	NSString *existingPassword = [SFHFKeychainUtils getPasswordForUsername: username andServerName: serverName error:&getError];
Packit 1fb8d4
  
Packit 1fb8d4
	if ([getError code] == -1999) 
Packit 1fb8d4
  {
Packit 1fb8d4
		// There is an existing entry without a password properly stored (possibly as a result of the previous incorrect version of this code.
Packit 1fb8d4
		// Delete the existing item before moving on entering a correct one.
Packit 1fb8d4
    
Packit 1fb8d4
		getError = nil;
Packit 1fb8d4
		
Packit 1fb8d4
		[self deleteItemForUsername: username andServerName: serverName error: &getError];
Packit 1fb8d4
    
Packit 1fb8d4
		if ([getError code] != noErr) 
Packit 1fb8d4
    {
Packit 1fb8d4
			if (error != nil) 
Packit 1fb8d4
      {
Packit 1fb8d4
				*error = getError;
Packit 1fb8d4
			}
Packit 1fb8d4
			return NO;
Packit 1fb8d4
		}
Packit 1fb8d4
	}
Packit 1fb8d4
	else if ([getError code] != noErr) 
Packit 1fb8d4
  {
Packit 1fb8d4
		if (error != nil) 
Packit 1fb8d4
    {
Packit 1fb8d4
			*error = getError;
Packit 1fb8d4
		}
Packit 1fb8d4
		return NO;
Packit 1fb8d4
	}
Packit 1fb8d4
	
Packit 1fb8d4
	if (error != nil) 
Packit 1fb8d4
  {
Packit 1fb8d4
		*error = nil;
Packit 1fb8d4
	}
Packit 1fb8d4
	
Packit 1fb8d4
	OSStatus status = noErr;
Packit 1fb8d4
  
Packit 1fb8d4
	if (existingPassword) 
Packit 1fb8d4
  {
Packit 1fb8d4
		// We have an existing, properly entered item with a password.
Packit 1fb8d4
		// Update the existing item.
Packit 1fb8d4
		
Packit 1fb8d4
		if (![existingPassword isEqualToString:password] && updateExisting) 
Packit 1fb8d4
    {
Packit 1fb8d4
			//Only update if we're allowed to update existing.  If not, simply do nothing.
Packit 1fb8d4
			
Packit 1fb8d4
			NSArray *keys = [[[NSArray alloc] initWithObjects: (NSString *) kSecClass, 
Packit 1fb8d4
                        kSecAttrService, 
Packit 1fb8d4
                        kSecAttrLabel, 
Packit 1fb8d4
                        kSecAttrAccount, 
Packit 1fb8d4
                        nil] autorelease];
Packit 1fb8d4
			
Packit 1fb8d4
			NSArray *objects = [[[NSArray alloc] initWithObjects: (NSString *) kSecClassGenericPassword, 
Packit 1fb8d4
                           serverName,
Packit 1fb8d4
                           serverName,
Packit 1fb8d4
                           username,
Packit 1fb8d4
                           nil] autorelease];
Packit 1fb8d4
			
Packit 1fb8d4
			NSDictionary *query = [[[NSDictionary alloc] initWithObjects: objects forKeys: keys] autorelease];			
Packit 1fb8d4
			
Packit 1fb8d4
			status = SecItemUpdate((CFDictionaryRef) query, (CFDictionaryRef) [NSDictionary dictionaryWithObject: [password dataUsingEncoding: NSUTF8StringEncoding] forKey: (NSString *) kSecValueData]);
Packit 1fb8d4
		}
Packit 1fb8d4
	}
Packit 1fb8d4
	else 
Packit 1fb8d4
  {
Packit 1fb8d4
		// No existing entry (or an existing, improperly entered, and therefore now
Packit 1fb8d4
		// deleted, entry).  Create a new entry.
Packit 1fb8d4
		
Packit 1fb8d4
		NSArray *keys = [[[NSArray alloc] initWithObjects: (NSString *) kSecClass, 
Packit 1fb8d4
                      kSecAttrService, 
Packit 1fb8d4
                      kSecAttrLabel, 
Packit 1fb8d4
                      kSecAttrAccount, 
Packit 1fb8d4
                      kSecValueData, 
Packit 1fb8d4
                      nil] autorelease];
Packit 1fb8d4
		
Packit 1fb8d4
		NSArray *objects = [[[NSArray alloc] initWithObjects: (NSString *) kSecClassGenericPassword, 
Packit 1fb8d4
                         serverName,
Packit 1fb8d4
                         serverName,
Packit 1fb8d4
                         username,
Packit 1fb8d4
                         [password dataUsingEncoding: NSUTF8StringEncoding],
Packit 1fb8d4
                         nil] autorelease];
Packit 1fb8d4
		
Packit 1fb8d4
		NSDictionary *query = [[[NSDictionary alloc] initWithObjects: objects forKeys: keys] autorelease];			
Packit 1fb8d4
    
Packit 1fb8d4
		status = SecItemAdd((CFDictionaryRef) query, NULL);
Packit 1fb8d4
	}
Packit 1fb8d4
	
Packit 1fb8d4
	if (error != nil && status != noErr) 
Packit 1fb8d4
  {
Packit 1fb8d4
		// Something went wrong with adding the new item. Return the Keychain error code.
Packit 1fb8d4
		*error = [NSError errorWithDomain: SFHFKeychainUtilsErrorDomain code: status userInfo: nil];
Packit 1fb8d4
    
Packit 1fb8d4
    return NO;
Packit 1fb8d4
	}
Packit 1fb8d4
  
Packit 1fb8d4
  return YES;
Packit 1fb8d4
}
Packit 1fb8d4
Packit 1fb8d4
+ (BOOL) deleteItemForUsername: (NSString *) username andServerName: (NSString *) serverName error: (NSError **) error 
Packit 1fb8d4
{
Packit 1fb8d4
	if (!username || !serverName) 
Packit 1fb8d4
  {
Packit 1fb8d4
		if (error != nil) 
Packit 1fb8d4
    {
Packit 1fb8d4
			*error = [NSError errorWithDomain: SFHFKeychainUtilsErrorDomain code: -2000 userInfo: nil];
Packit 1fb8d4
		}
Packit 1fb8d4
		return NO;
Packit 1fb8d4
	}
Packit 1fb8d4
	
Packit 1fb8d4
	if (error != nil) 
Packit 1fb8d4
  {
Packit 1fb8d4
		*error = nil;
Packit 1fb8d4
	}
Packit 1fb8d4
  
Packit 1fb8d4
	NSArray *keys = [[[NSArray alloc] initWithObjects: (NSString *) kSecClass, kSecAttrAccount, kSecAttrService, kSecReturnAttributes, nil] autorelease];
Packit 1fb8d4
	NSArray *objects = [[[NSArray alloc] initWithObjects: (NSString *) kSecClassGenericPassword, username, serverName, kCFBooleanTrue, nil] autorelease];
Packit 1fb8d4
	
Packit 1fb8d4
	NSDictionary *query = [[[NSDictionary alloc] initWithObjects: objects forKeys: keys] autorelease];
Packit 1fb8d4
	
Packit 1fb8d4
	OSStatus status = SecItemDelete((CFDictionaryRef) query);
Packit 1fb8d4
	
Packit 1fb8d4
	if (error != nil && status != noErr) 
Packit 1fb8d4
  {
Packit 1fb8d4
		*error = [NSError errorWithDomain: SFHFKeychainUtilsErrorDomain code: status userInfo: nil];		
Packit 1fb8d4
    
Packit 1fb8d4
    return NO;
Packit 1fb8d4
	}
Packit 1fb8d4
  
Packit 1fb8d4
  return YES;
Packit 1fb8d4
}
Packit 1fb8d4
Packit 1fb8d4
#endif
Packit 1fb8d4
Packit 1fb8d4
@end