Skip to content

Commit

Permalink
Merge branch 'master' into warren/our-openssl
Browse files Browse the repository at this point in the history
# By Mike Abdullah
# Via Mike Abdullah
* master:
  Make sure we're persisting SFTP passwords properly and present any error doing so.
  -ck2_setCredential:forSSHHost:port:error:
  Slightly more informative libssh2_userauth_publickey_fromfile error.
  • Loading branch information
dodgio committed Dec 3, 2012
2 parents e3a11e2 + c612134 commit 818f284
Show file tree
Hide file tree
Showing 3 changed files with 96 additions and 4 deletions.
30 changes: 26 additions & 4 deletions CK2SFTPSession.m
Original file line number Diff line number Diff line change
Expand Up @@ -1097,7 +1097,21 @@ - (BOOL)usePublicKeyCredential:(NSURLCredential *)credential error:(NSError **)e

if (result)
{
if (error) *error = [self sessionError];
if (error)
{
*error = [self sessionError];

// Add in path/URL info when sure it makes sense
if (result == LIBSSH2_ERROR_FILE && !publicKey)
{
NSMutableDictionary *userInfo = [[NSMutableDictionary alloc] initWithDictionary:[*error userInfo]];
[userInfo setObject:*error forKey:NSUnderlyingErrorKey];
[userInfo setObject:privateKeyURL forKey:NSURLErrorKey];
[userInfo setObject:privateKey forKey:NSFilePathErrorKey];
*error = [NSError errorWithDomain:[*error domain] code:[*error code] userInfo:userInfo];
[userInfo release];
}
}
return NO;
}
else
Expand Down Expand Up @@ -1189,9 +1203,17 @@ - (void)useCredential:(NSURLCredential *)credential forAuthenticationChallenge:(
}
else
{
// NSURLCredentialStorage will take care of adding to keychain if requested
[[NSURLCredentialStorage sharedCredentialStorage] setCredential:credential
forProtectionSpace:[challenge protectionSpace]];
// Add to keychain if requested
NSURLProtectionSpace *space = [challenge protectionSpace];

NSError *error;
if (![[NSURLCredentialStorage sharedCredentialStorage] ck2_setCredential:credential forSSHHost:[space host] port:[space port] error:&error])
{
// This is a poor way to handle the error, but it will do for the moment to get some immediate customer feedback
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
[NSApp presentError:error];
}];
}

[self initializeSFTP];
}
Expand Down
3 changes: 3 additions & 0 deletions CK2SSHCredential.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,9 @@

@interface NSURLCredentialStorage (CK2SSHCredential)

// NSURLCredentialStorage can only handle HTTP and FTP credentials by default
- (BOOL)ck2_setCredential:(NSURLCredential *)credential forSSHHost:(NSString *)host port:(NSInteger)port error:(NSError **)error;

// Looks up a keychain entry for the private key's passphrase. Nil if none is stored
- (NSURLCredential *)ck2_credentialForPrivateKeyAtURL:(NSURL *)privateKey user:(NSString *)user;
- (BOOL)ck2_setPrivateKeyCredential:(NSURLCredential *)credential;
Expand Down
67 changes: 67 additions & 0 deletions CK2SSHCredential.m
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,73 @@ - (NSURLCredential *)ck2_credentialWithPassword:(NSString *)password persistence

@implementation NSURLCredentialStorage (CK2SSHCredential)

- (NSError *)keychainErrorWithCode:(OSStatus)code;
{
CFStringRef message = SecCopyErrorMessageString(code, NULL);

// com.apple.Keychain was my logical guess at domain, and searching the internet reveals Apple are using it in a few places
NSError *result = [NSError errorWithDomain:@"com.apple.Keychain"
code:code
userInfo:[NSDictionary dictionaryWithObjectsAndKeys:(NSString *)message, NSLocalizedDescriptionKey, nil]];

if (message) CFRelease(message);
return result;
}

- (BOOL)ck2_setCredential:(NSURLCredential *)credential forSSHHost:(NSString *)host port:(NSInteger)port error:(NSError **)error;
{
// Can't do anything with non-persistent credentials
if ([credential persistence] != NSURLCredentialPersistencePermanent) return YES;


// Retrieve the keychain item
NSString *user = [credential user];

SecKeychainItemRef keychainItem;
OSStatus status = SecKeychainFindInternetPassword(NULL,
[host lengthOfBytesUsingEncoding:NSUTF8StringEncoding], [host UTF8String],
0, NULL,
[user lengthOfBytesUsingEncoding:NSUTF8StringEncoding], [user UTF8String],
0, NULL,
port,
kSecProtocolTypeSSH,
kSecAuthenticationTypeDefault,
NULL, NULL,
&keychainItem);


// Store the password
NSString *password = [credential password];
NSAssert(password, @"%@ was handed password-less credential", NSStringFromSelector(_cmd));

if (status == errSecSuccess)
{
status = SecKeychainItemModifyAttributesAndData(keychainItem,
NULL, // no change to attributes
[password lengthOfBytesUsingEncoding:NSUTF8StringEncoding], [password UTF8String]);

CFRelease(keychainItem);
}
else
{
status = SecKeychainAddInternetPassword(NULL,
[host lengthOfBytesUsingEncoding:NSUTF8StringEncoding], [host UTF8String],
0, NULL,
[user lengthOfBytesUsingEncoding:NSUTF8StringEncoding], [user UTF8String],
0, NULL,
port,
kSecProtocolTypeSSH,
kSecAuthenticationTypeDefault,
[password lengthOfBytesUsingEncoding:NSUTF8StringEncoding], [password UTF8String],
NULL);
}

if (status == errSecSuccess) return YES;

if (error) *error = [self keychainErrorWithCode:status];
return NO;
}

- (SecKeychainItemRef)copyKeychainItemForPrivateKeyPath:(NSString *)privateKey;
{
NSString *service = @"SSH";
Expand Down

0 comments on commit 818f284

Please sign in to comment.