//
// SequelProTunnelAssistant.m
// sequel-pro
//
// Created by Rowan Beentje on May 4, 2009.
// Copyright (c) 2009 Rowan Beentje. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use,
// copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following
// conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
// OTHER DEALINGS IN THE SOFTWARE.
//
// More info at
#import
#import "SPKeychain.h"
#import "SPSSHTunnel.h"
#import "RegexKitLite.h"
#import "SPConstants.h"
int main(int argc, const char *argv[])
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSDictionary *environment = [[NSProcessInfo processInfo] environment];
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];
return 1;
}
if (argc > 1) {
argument = [[[NSString alloc] initWithCString:argv[1] encoding:NSUTF8StringEncoding] autorelease];
}
// Check if we're being asked a question and respond if so
if (argument && [argument rangeOfString:@" (yes/no)?"].location != NSNotFound) {
sequelProTunnel = (SPSSHTunnel *)[NSConnection rootProxyForConnectionWithRegisteredName:connectionName host:nil];
if (!sequelProTunnel) {
NSLog(@"SSH Tunnel: unable to connect to Sequel Pro to show SSH question");
[pool release];
return 1;
}
BOOL response = [sequelProTunnel getResponseForQuestion:argument];
if (response) {
printf("yes\n");
}
else {
printf("no\n");
}
[pool release];
return 0;
}
// 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"] integerValue] == SPSSHPasswordUsesKeychain) {
SPKeychain *keychain;
NSString *keychainName = [[environment objectForKey:@"SP_KEYCHAIN_ITEM_NAME"] stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSString *keychainAccount = [[environment objectForKey:@"SP_KEYCHAIN_ITEM_ACCOUNT"] stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
if (!keychainName || !keychainAccount) {
NSLog(@"SSH Tunnel: keychain authentication specified but insufficient internal details supplied");
[pool release];
return 1;
}
keychain = [[SPKeychain alloc] init];
if ([keychain passwordExistsForName:keychainName account:keychainAccount]) {
printf("%s\n", [[keychain getPasswordForName:keychainName account:keychainAccount] UTF8String]);
[keychain release];
[pool release];
return 0;
}
[keychain release];
// If retrieving the password failed, log an error and fall back to requesting from the GUI
NSLog(@"SSH Tunnel: specified keychain password not found");
argument = [NSString stringWithFormat:NSLocalizedString(@"The SSH password could not be loaded from the keychain; please enter the SSH password for %@:", @"Prompt for SSH password when keychain fetch failed"), connectionName];
}
// If the password method is set to request the password from the tunnel instance, do so.
if ([[environment objectForKey:@"SP_PASSWORD_METHOD"] integerValue] == SPSSHPasswordAsksUI) {
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) {
printf("%s\n", [password UTF8String]);
[pool release];
return 0;
}
// If retrieving the password failed, log an error and fall back to requesting from the GUI
NSLog(@"SSH Tunnel: unable to successfully request password from Sequel Pro for internal authentication");
argument = [NSString stringWithFormat:NSLocalizedString(@"The SSH password could not be loaded; please enter the SSH password for %@:", @"Prompt for SSH password when direct fetch failed"), connectionName];
}
}
// Check whether we're being asked for a SSH key passphrase
if (argument && [[argument lowercaseString] rangeOfString:@"enter passphrase for"].location != NSNotFound ) {
NSString *passphrase;
NSString *keyName = [argument stringByMatching:@"^\\s*Enter passphrase for key \\'(.*)\\':\\s*$" capture:1L];
if (keyName) {
// Check whether the passphrase is in the keychain, using standard OS X sshagent name and account
SPKeychain *keychain = [[SPKeychain alloc] init];
if ([keychain passwordExistsForName:@"SSH" account:keyName]) {
printf("%s\n", [[keychain getPasswordForName:@"SSH" account:keyName] UTF8String]);
[keychain release];
[pool release];
return 0;
}
[keychain release];
}
// Not found in the keychain - we need to ask the GUI.
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 to show SSH question");
[pool release];
return 1;
}
passphrase = [sequelProTunnel getPasswordForQuery:argument verificationHash:verificationHash];
if (!passphrase) {
[pool release];
return 1;
}
printf("%s\n", [passphrase UTF8String]);
[pool release];
return 0;
}
// SSH has some other question. Show that directly to the user. This is an attempt to support RSA SecurID
if (argument) {
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 to show SSH question");
[pool release];
return 1;
}
passphrase = [sequelProTunnel getPasswordForQuery:argument verificationHash:verificationHash];
if (!passphrase) {
[pool release];
return 1;
}
printf("%s\n", [passphrase UTF8String]);
[pool release];
return 0;
}
[pool release];
return 1;
}