aboutsummaryrefslogtreecommitdiffstats
path: root/Source
diff options
context:
space:
mode:
authorrowanbeentje <rowan@beent.je>2009-06-05 00:32:42 +0000
committerrowanbeentje <rowan@beent.je>2009-06-05 00:32:42 +0000
commit4e6c8e557f6dd132e159ff4841900d1b72331eea (patch)
treebd972c23c05f09be246f436b3bd511c4f5dbf23e /Source
parent905dd61bd91f3dce22655bf942302739544dd86c (diff)
downloadsequelpro-4e6c8e557f6dd132e159ff4841900d1b72331eea.tar.gz
sequelpro-4e6c8e557f6dd132e159ff4841900d1b72331eea.tar.bz2
sequelpro-4e6c8e557f6dd132e159ff4841900d1b72331eea.zip
Further SSH tunnel improvements:
- SSH Public/private keys are now supported, even if they are password-protected. The user and password fields can be left blank where appropriate. - SSH yes/no queries (ie host key mismatch) and password requests (eg key passphrases) dialogs now automatically resize to match the content from the SSH process.
Diffstat (limited to 'Source')
-rw-r--r--Source/SPSSHTunnel.h4
-rw-r--r--Source/SPSSHTunnel.m61
-rw-r--r--Source/TunnelPassphraseRequester.m95
3 files changed, 122 insertions, 38 deletions
diff --git a/Source/SPSSHTunnel.h b/Source/SPSSHTunnel.h
index 612ef595..4486685a 100644
--- a/Source/SPSSHTunnel.h
+++ b/Source/SPSSHTunnel.h
@@ -19,6 +19,9 @@ enum spsshtunnel_password_modes
{
IBOutlet NSWindow *sshQuestionDialog;
IBOutlet NSTextField *sshQuestionText;
+ IBOutlet NSWindow *sshPasswordDialog;
+ IBOutlet NSTextField *sshPasswordText;
+ IBOutlet NSSecureTextField *sshPasswordField;
NSWindow *parentWindow;
NSTask *task;
@@ -56,6 +59,7 @@ enum spsshtunnel_password_modes
- (void) standardErrorHandler:(NSNotification*)aNotification;
- (NSString *) getPasswordWithVerificationHash:(NSString *)theHash;
- (BOOL) getResponseForQuestion:(NSString *)theQuestion;
+- (NSString *) getPasswordForQuery:(NSString *)theQuery verificationHash:(NSString *)theHash;
- (IBAction) closeSheet:(id)sender;
@end
diff --git a/Source/SPSSHTunnel.m b/Source/SPSSHTunnel.m
index 83beb8a7..79ea780c 100644
--- a/Source/SPSSHTunnel.m
+++ b/Source/SPSSHTunnel.m
@@ -55,6 +55,7 @@
// Set up a connection for use by the tunnel process
tunnelConnectionName = [NSString stringWithFormat:@"SequelPro-%f", [[NSString stringWithFormat:@"%f", [[NSDate date] timeIntervalSince1970]] hash]];
+ tunnelConnectionVerifyHash = [NSString stringWithFormat:@"%f", [[NSString stringWithFormat:@"%f%i", [[NSDate date] timeIntervalSince1970]] hash]];
tunnelConnection = [[NSConnection defaultConnection] retain];
[tunnelConnection runInNewThread];
[tunnelConnection removeRunLoop:[NSRunLoop currentRunLoop]];
@@ -107,7 +108,6 @@
{
if (passwordInKeychain) return NO;
password = [[NSString alloc] initWithString:thePassword];
- tunnelConnectionVerifyHash = [NSString stringWithFormat:@"%f", [[NSString stringWithFormat:@"%f%i", [[NSDate date] timeIntervalSince1970]] hash]];
return YES;
}
@@ -225,7 +225,6 @@
// [taskArguments addObject:@"-C"]; // TODO: compression?
[taskArguments addObject:@"-o ExitOnForwardFailure=yes"];
[taskArguments addObject:[NSString stringWithFormat:@"-o ConnectTimeout=%i", connectionTimeout]];
- [taskArguments addObject:@"-o PubkeyAuthentication=yes"];
[taskArguments addObject:@"-o NumberOfPasswordPrompts=1"];
if (useKeepAlive && keepAliveInterval) {
[taskArguments addObject:@"-o TCPKeepAlive=no"];
@@ -233,7 +232,11 @@
[taskArguments addObject:@"-o ServerAliveCountMax=1"];
}
[taskArguments addObject:[NSString stringWithFormat:@"-p %i", sshPort]];
- [taskArguments addObject:[NSString stringWithFormat:@"%@@%@", sshLogin, sshHost]];
+ if ([sshLogin length]) {
+ [taskArguments addObject:[NSString stringWithFormat:@"%@@%@", sshLogin, sshHost]];
+ } else {
+ [taskArguments addObject:sshHost];
+ }
[taskArguments addObject:[NSString stringWithFormat:@"-L %i/%@/%i", localPort, remoteHost, remotePort]];
[task setArguments:taskArguments];
@@ -245,13 +248,13 @@
[taskEnvironment setObject:authenticationAppPath forKey:@"SSH_ASKPASS"];
[taskEnvironment setObject:@":0" forKey:@"DISPLAY"];
[taskEnvironment setObject:tunnelConnectionName forKey:@"SP_CONNECTION_NAME"];
+ [taskEnvironment setObject:tunnelConnectionVerifyHash forKey:@"SP_CONNECTION_VERIFY_HASH"];
if (passwordInKeychain) {
[taskEnvironment setObject:[[NSNumber numberWithInt:SPSSH_PASSWORD_USES_KEYCHAIN] stringValue] forKey:@"SP_PASSWORD_METHOD"];
[taskEnvironment setObject:keychainName forKey:@"SP_KEYCHAIN_ITEM_NAME"];
[taskEnvironment setObject:keychainAccount forKey:@"SP_KEYCHAIN_ITEM_ACCOUNT"];
} else {
[taskEnvironment setObject:[[NSNumber numberWithInt:SPSSH_PASSWORD_ASKS_UI] stringValue] forKey:@"SP_PASSWORD_METHOD"];
- [taskEnvironment setObject:tunnelConnectionVerifyHash forKey:@"SP_CONNECTION_VERIFY_HASH"];
}
[task setEnvironment:taskEnvironment];
@@ -282,6 +285,8 @@
// Listen for output
[task waitUntilExit];
+
+ // If the task closed unexpectedly, alert appropriately
if (connectionState != SPSSH_STATE_IDLE) {
connectionState = SPSSH_STATE_IDLE;
lastError = [[NSString alloc] initWithString:NSLocalizedString(@"The SSH Tunnel has unexpectedly closed.", @"SSH tunnel unexpectedly closed")];
@@ -343,7 +348,7 @@
lastError = [[NSString alloc] initWithString:NSLocalizedString(@"The SSH Tunnel was closed 'by the remote host'. This may indicate a networking issue or a network timeout.", @"SSH tunnel was closed by remote host message")];
if (delegate) [delegate performSelectorOnMainThread:stateChangeSelector withObject:self waitUntilDone:NO];
}
- if ([message rangeOfString:@"Permission denied (" ].location != NSNotFound) {
+ if ([message rangeOfString:@"Permission denied (" ].location != NSNotFound || [message rangeOfString:@"No more authentication methods to try" ].location != NSNotFound) {
connectionState = SPSSH_STATE_IDLE;
[task terminate];
if (lastError) [lastError release];
@@ -394,9 +399,15 @@
*/
- (BOOL) getResponseForQuestion:(NSString *)theQuestion
{
+ NSSize questionTextSize;
+ NSRect windowFrameRect;
- // Ask how to proceed
+ // Ask how to proceed, sizing the window appropriately to fit the question
[sshQuestionText setStringValue:theQuestion];
+ questionTextSize = [[sshQuestionText cell] cellSizeForBounds:NSMakeRect(0, 0, [sshQuestionText bounds].size.width, 500)];
+ windowFrameRect = [sshQuestionDialog frame];
+ windowFrameRect.size.height = ((questionTextSize.height < 100)?100:questionTextSize.height) + 90;
+ [sshQuestionDialog setFrame:windowFrameRect display:NO];
[NSApp beginSheet:sshQuestionDialog modalForWindow:parentWindow modalDelegate:self didEndSelector:nil contextInfo:nil];
int sshQueryResponseCode = [NSApp runModalForWindow:sshQuestionDialog];
[NSApp endSheet:sshQuestionDialog];
@@ -415,6 +426,44 @@
}
/*
+ * Method to allow an SSH tunnel to request a password. This is used by the program set by the
+ * SSH_ASKPASS environment setting to request passphrases for SSH keys.
+ */
+- (NSString *) getPasswordForQuery:(NSString *)theQuery verificationHash:(NSString *)theHash
+{
+ if (![theHash isEqualToString:tunnelConnectionVerifyHash]) return nil;
+
+ NSSize queryTextSize;
+ NSRect windowFrameRect;
+ NSString *thePassword;
+
+ // Request the password, sizing the window appropriately to fit the query
+ [sshPasswordText setStringValue:theQuery];
+ queryTextSize = [[sshPasswordText cell] cellSizeForBounds:NSMakeRect(0, 0, [sshPasswordText bounds].size.width, 500)];
+ windowFrameRect = [sshPasswordDialog frame];
+ windowFrameRect.size.height = ((queryTextSize.height < 40)?40:queryTextSize.height) + 143;
+ [sshPasswordDialog setFrame:windowFrameRect display:NO];
+ [NSApp beginSheet:sshPasswordDialog modalForWindow:parentWindow modalDelegate:self didEndSelector:nil contextInfo:nil];
+ int sshQueryResponseCode = [NSApp runModalForWindow:sshPasswordDialog];
+ [NSApp endSheet:sshPasswordDialog];
+ [sshPasswordDialog orderOut:nil];
+
+ switch (sshQueryResponseCode) {
+
+ // OK
+ case 1:
+ thePassword = [NSString stringWithString:[sshPasswordField stringValue]];
+ [sshPasswordField setStringValue:@""];
+ [[delegate undoManager] removeAllActionsWithTarget:sshPasswordField];
+ return thePassword;
+
+ // Cancel
+ default:
+ return nil;
+ }
+}
+
+/*
* Ends an existing modal session
*/
- (IBAction) closeSheet:(id)sender
diff --git a/Source/TunnelPassphraseRequester.m b/Source/TunnelPassphraseRequester.m
index 4391e3e7..360af00d 100644
--- a/Source/TunnelPassphraseRequester.m
+++ b/Source/TunnelPassphraseRequester.m
@@ -31,6 +31,7 @@ int main(int argc, const char *argv[])
NSString *argument = nil;
SPSSHTunnel *sequelProTunnel;
NSString *connectionName = [environment objectForKey:@"SP_CONNECTION_NAME"];
+ NSString *verificationHash = [environment objectForKey:@"SP_CONNECTION_VERIFY_HASH"];
if (![environment objectForKey:@"SP_PASSWORD_METHOD"]) {
[pool release];
@@ -38,7 +39,7 @@ int main(int argc, const char *argv[])
}
if (argc > 1) {
- argument = [[NSString alloc] initWithCString:argv[1] encoding:NSUTF8StringEncoding];
+ argument = [[[NSString alloc] initWithCString:argv[1] encoding:NSUTF8StringEncoding] autorelease];
}
// Check if we're being asked a question and respond if so
@@ -59,57 +60,87 @@ int main(int argc, const char *argv[])
return 0;
}
- // If the password method is set to use the keychain, use the supplied keychain name to
- // request the password
- if ([[environment objectForKey:@"SP_PASSWORD_METHOD"] intValue] == SPSSH_PASSWORD_USES_KEYCHAIN) {
- KeyChain *keychain;
- NSString *keychainName = [environment objectForKey:@"SP_KEYCHAIN_ITEM_NAME"];
- NSString *keychainAccount = [environment objectForKey:@"SP_KEYCHAIN_ITEM_ACCOUNT"];
-
- if (!keychainName || !keychainAccount) {
- NSLog(@"SSH Tunnel: keychain authentication specified but insufficient internal details supplied");
+ // Check whether we're being asked for a standard SSH password - if so, use the app-entered value.
+ if (argument && [[argument lowercaseString] rangeOfString:@"password:"].location != NSNotFound ) {
+
+ // If the password method is set to use the keychain, use the supplied keychain name to
+ // request the password
+ if ([[environment objectForKey:@"SP_PASSWORD_METHOD"] intValue] == SPSSH_PASSWORD_USES_KEYCHAIN) {
+ KeyChain *keychain;
+ NSString *keychainName = [environment objectForKey:@"SP_KEYCHAIN_ITEM_NAME"];
+ NSString *keychainAccount = [environment objectForKey:@"SP_KEYCHAIN_ITEM_ACCOUNT"];
+
+ if (!keychainName || !keychainAccount) {
+ NSLog(@"SSH Tunnel: keychain authentication specified but insufficient internal details supplied");
+ [pool release];
+ return 1;
+ }
+
+ keychain = [[KeyChain alloc] init];
+ if (![keychain passwordExistsForName:keychainName account:keychainAccount]) {
+ NSLog(@"SSH Tunnel: specified keychain password not found");
+ [pool release];
+ return 1;
+ }
+
+ printf("%s\n", [[keychain getPasswordForName:keychainName account:keychainAccount] UTF8String]);
[pool release];
- return 1;
+ return 0;
}
- keychain = [[KeyChain alloc] init];
- if (![keychain passwordExistsForName:keychainName account:keychainAccount]) {
- NSLog(@"SSH Tunnel: specified keychain password not found");
+ // If the password method is set to request the password from the tunnel instance, do so.
+ if ([[environment objectForKey:@"SP_PASSWORD_METHOD"] intValue] == SPSSH_PASSWORD_ASKS_UI) {
+ NSString *password;
+
+ if (!connectionName || !verificationHash) {
+ NSLog(@"SSH Tunnel: internal authentication specified but insufficient details supplied");
+ [pool release];
+ return 1;
+ }
+
+ sequelProTunnel = (SPSSHTunnel *)[NSConnection rootProxyForConnectionWithRegisteredName:connectionName host:nil];
+ if (!sequelProTunnel) {
+ NSLog(@"SSH Tunnel: unable to connect to Sequel Pro for internal authentication");
+ [pool release];
+ return 1;
+ }
+
+ password = [sequelProTunnel getPasswordWithVerificationHash:verificationHash];
+ if (!password) {
+ NSLog(@"SSH Tunnel: unable to successfully request password from Sequel Pro for internal authentication");
+ [pool release];
+ return 1;
+ }
+
+ printf("%s\n", [password UTF8String]);
[pool release];
- return 1;
+ return 0;
}
-
- printf("%s\n", [[keychain getPasswordForName:keychainName account:keychainAccount] UTF8String]);
- [pool release];
- return 0;
}
- // If the password method is set to request the password from the tunnel instance, do so.
- if ([[environment objectForKey:@"SP_PASSWORD_METHOD"] intValue] == SPSSH_PASSWORD_ASKS_UI) {
- NSString *password;
- NSString *verificationHash = [environment objectForKey:@"SP_CONNECTION_VERIFY_HASH"];
-
- if (!connectionName || !verificationHash) {
- NSLog(@"SSH Tunnel: internal authentication specified but insufficient details supplied");
+ // Check whether we're being asked for a SSH key passphrase, forward requests to the GUI
+ if (argument && [[argument lowercaseString] rangeOfString:@"enter passphrase for"].location != NSNotFound ) {
+ NSString *passphrase;
+
+ if (!verificationHash) {
+ NSLog(@"SSH Tunnel: key passphrase authentication required but insufficient details supplied to connect to GUI");
[pool release];
return 1;
}
sequelProTunnel = (SPSSHTunnel *)[NSConnection rootProxyForConnectionWithRegisteredName:connectionName host:nil];
if (!sequelProTunnel) {
- NSLog(@"SSH Tunnel: unable to connect to Sequel Pro for internal authentication");
+ NSLog(@"SSH Tunnel: unable to connect to Sequel Pro to show SSH question");
[pool release];
return 1;
}
-
- password = [sequelProTunnel getPasswordWithVerificationHash:verificationHash];
- if (!password) {
- NSLog(@"SSH Tunnel: unable to successfully request password from Sequel Pro for internal authentication");
+ passphrase = [sequelProTunnel getPasswordForQuery:argument verificationHash:verificationHash];
+ if (!passphrase) {
[pool release];
return 1;
}
- printf("%s\n", [password UTF8String]);
+ printf("%s\n", [passphrase UTF8String]);
[pool release];
return 0;
}