You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
624 lines
18 KiB
624 lines
18 KiB
/***************************************************************************
|
|
ksshprocess.h - description
|
|
-------------------
|
|
begin : Tue Jul 31 2001
|
|
copyright : (C) 2001 by Lucas Fisher
|
|
email : ljfisher@purdue.edu
|
|
***************************************************************************/
|
|
|
|
/***************************************************************************
|
|
* *
|
|
* 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. *
|
|
* *
|
|
***************************************************************************/
|
|
|
|
#ifndef KSSHPROCESS_H
|
|
#define KSSHPROCESS_H
|
|
|
|
#include <sys/types.h>
|
|
#include <sys/wait.h>
|
|
#include <signal.h>
|
|
#include <unistd.h>
|
|
|
|
#include <qvaluelist.h>
|
|
|
|
#include <kdebug.h>
|
|
|
|
#include "process.h"
|
|
|
|
#define KSSHPROC 7120
|
|
|
|
/**
|
|
* Provides version independent access to ssh. Currently supported
|
|
* versions of SSH are:
|
|
* OpenSSH 2.9p1
|
|
* OpenSSH 2.9p2
|
|
* OpenSSH 3.0
|
|
* OpenSSH 3.1
|
|
* Commercial SSH 3.0.0
|
|
* Other versions of OpenSSH and commerical SSH will probably work also.
|
|
*
|
|
* To setup a SSH connection first create a list of options to use and tell
|
|
* KSshProcess about your options. Then start the ssh connection. Once the
|
|
* connection is setup use the stdin, stdout, stderr, and pty file descriptors
|
|
* to communicate with ssh. For a detailed example of how to use, see
|
|
* ksshprocesstest.cpp.
|
|
*
|
|
* @author Lucas Fisher
|
|
*
|
|
* Example: Connect to ssh server on localhost
|
|
* KSshProcess::SshOpt opt;
|
|
* KSshProcess::SshOptList options;
|
|
*
|
|
* opt.opt = KSshProcess::SSH_HOST;
|
|
* opt.str = "localhost";
|
|
* options.append(opt);
|
|
*
|
|
* opt.opt = KSshProcess::SSH_USERNAME;
|
|
* opt.str = "me";
|
|
* options.append(opt);
|
|
*
|
|
* KSshProcess ssh;
|
|
* if( !ssh.setOptions(options) ) {
|
|
* int err = ssh.error();
|
|
* // process error
|
|
* return false;
|
|
* }
|
|
*
|
|
* int err;
|
|
* QString errMsg;
|
|
* while( !ssh.connect() ) {
|
|
* err = ssh.error(errMsg);
|
|
*
|
|
* switch( err ) {
|
|
* case KSshProcess::ERR_NEW_HOST_KEY:
|
|
* case KSshProcess::ERR_DIFF_HOST_KEY:
|
|
* // ask user to accept key
|
|
* if( acceptHostKey ) {
|
|
* ssh.acceptKey(true);
|
|
* }
|
|
* break;
|
|
*
|
|
* case KSshProcess::ERR_NEED_PASSWORD:
|
|
* // ask user for password
|
|
* ssh.password(userPassword);
|
|
* break;
|
|
*
|
|
* case KSshProcess::ERR_NEED_KEY_PASSPHRASE:
|
|
* // ask user for their key passphrase
|
|
* ssh.keyPassphrase(keyPassphrase);
|
|
* break;
|
|
*
|
|
* default:
|
|
* // somethings wrong, alert user
|
|
* return;
|
|
* }
|
|
* }
|
|
* // We have an open ssh connection to localhost
|
|
*
|
|
*/
|
|
|
|
class KSshProcess {
|
|
public:
|
|
/**
|
|
* SSH Option
|
|
*
|
|
* Stores SSH options for use with KSshProcess.
|
|
*
|
|
* SSH options are configured much like UDS entries.
|
|
* Each option is assigned a constant and a string, bool,
|
|
* or number is assigned based on the option.
|
|
*
|
|
* @author Lucas Fisher (ljfisher@iastate.edu)
|
|
*/
|
|
class SshOpt {
|
|
public:
|
|
Q_UINT32 opt;
|
|
QString str;
|
|
Q_INT32 num;
|
|
bool boolean;
|
|
};
|
|
|
|
/**
|
|
* List of SshOptions and associated iterators
|
|
*/
|
|
typedef QValueList<SshOpt> SshOptList;
|
|
typedef QValueListIterator<SshOpt> SshOptListIterator;
|
|
typedef QValueListConstIterator<SshOpt> SshOptListConstIterator;
|
|
|
|
/**
|
|
* Ssh versions supported by KSshProcess. Subject to change
|
|
* at any time.
|
|
*/
|
|
enum SshVersion {
|
|
OPENSSH_3_6,
|
|
OPENSSH,
|
|
SSH,
|
|
SSH_VER_MAX,
|
|
UNKNOWN_VER
|
|
};
|
|
|
|
/**
|
|
* SSH options supported by KSshProcess. Set SshOpt::opt to one of these
|
|
* values.
|
|
*/
|
|
// we cannot do this like UDSAtomType (ORing the type with the name) because
|
|
// we have too many options for ssh and not enough bits.
|
|
enum SshOptType {
|
|
/**
|
|
* Request server to invoke subsystem. (str)
|
|
*/
|
|
SSH_SUBSYSTEM,
|
|
/**
|
|
* Connect to port on the server. (num)
|
|
*/
|
|
SSH_PORT,
|
|
/**
|
|
* Connect to host. (str)
|
|
*/
|
|
SSH_HOST,
|
|
/**
|
|
* connect using this username. (str)
|
|
*/
|
|
SSH_USERNAME,
|
|
/**
|
|
* connect using this password. (str)
|
|
*/
|
|
SSH_PASSWD,
|
|
/**
|
|
* connect using this version of the SSH protocol. num == 1 or 2
|
|
*/
|
|
SSH_PROTOCOL,
|
|
/**
|
|
* whether to forward X11 connections. (boolean)
|
|
*/
|
|
SSH_FORWARDX11,
|
|
/**
|
|
* whether to do agent forwarding. (boolean)
|
|
*/
|
|
SSH_FORWARDAGENT,
|
|
/**
|
|
* use as escape character. 0 for none (num)
|
|
*/
|
|
SSH_ESCAPE_CHAR,
|
|
/**
|
|
* command for ssh to perform once it is connected (str)
|
|
*/
|
|
SSH_COMMAND,
|
|
/**
|
|
* Set ssh verbosity. This may be added multiple times. It may also cause KSSHProcess
|
|
* to fail since we don't understand all the debug messages.
|
|
*/
|
|
SSH_VERBOSE,
|
|
/**
|
|
* Set a ssh option as one would find in the ssh_config file
|
|
* The str member should be set to 'optName value'
|
|
*/
|
|
SSH_OPTION,
|
|
/**
|
|
* Set some other option not supported by KSSHProcess. The option should
|
|
* be specified in the str member of SshOpt. Careful with this since
|
|
* not all versions of SSH support the same options.
|
|
*/
|
|
SSH_OTHER,
|
|
SSH_OPT_MAX // always last
|
|
}; // that's all for now
|
|
|
|
/**
|
|
* Errors that KSshProcess can encounter. When a member function returns
|
|
* false, call error() to retrieve one of these error codes.
|
|
*/
|
|
enum SshError {
|
|
/**
|
|
* Don't recognize the ssh version
|
|
*/
|
|
ERR_UNKNOWN_VERSION,
|
|
/**
|
|
* Cannot lauch ssh client
|
|
*/
|
|
ERR_CANNOT_LAUNCH,
|
|
/**
|
|
* Interaction with the ssh client failed. This happens when we can't
|
|
* find the password prompt or something similar
|
|
*/
|
|
ERR_INTERACT,
|
|
/**
|
|
* Arguments for both a remotely executed subsystem and command were provide.
|
|
* Only one or the other may be used
|
|
*/
|
|
ERR_CMD_SUBSYS_CONFLICT,
|
|
/**
|
|
* No password was supplied
|
|
*/
|
|
ERR_NEED_PASSWD,
|
|
/**
|
|
* No passphrase was supplied.
|
|
*/
|
|
ERR_NEED_PASSPHRASE,
|
|
/**
|
|
* No usename was supplied
|
|
*/
|
|
ERR_NEED_USERNAME,
|
|
/**
|
|
* Timed out waiting for a response from ssh or the server
|
|
*/
|
|
ERR_TIMED_OUT,
|
|
/**
|
|
* Internal error, probably from a system call
|
|
*/
|
|
ERR_INTERNAL,
|
|
/**
|
|
* ssh was disconnect from the host
|
|
*/
|
|
ERR_DISCONNECTED,
|
|
/**
|
|
* No ssh options have been set. Call setArgs() before calling connect.
|
|
*/
|
|
ERR_NO_OPTIONS,
|
|
/**
|
|
* A host key was received from an unknown host.
|
|
* Call connect() with the acceptHostKey argument to accept the key.
|
|
*/
|
|
ERR_NEW_HOST_KEY,
|
|
/**
|
|
* A host key different from what is stored in the user's known_hosts file
|
|
* has be received. This is an indication of an attack
|
|
*/
|
|
ERR_DIFF_HOST_KEY,
|
|
/**
|
|
* A new or different host key was rejected by the caller. The ssh
|
|
* connection was terminated and the ssh process killed.
|
|
*/
|
|
ERR_HOST_KEY_REJECTED,
|
|
/**
|
|
* An invalid option was found in the SSH option list
|
|
*/
|
|
ERR_INVALID_OPT,
|
|
/**
|
|
* SSH accepted host key without prompting user.
|
|
*/
|
|
ERR_ACCEPTED_KEY,
|
|
/**
|
|
* Authentication failed
|
|
*/
|
|
ERR_AUTH_FAILED,
|
|
/**
|
|
* Authentication failed because a new host key was detected and
|
|
* SSH is configured with strict host key checking enabled.
|
|
*/
|
|
ERR_AUTH_FAILED_NEW_KEY,
|
|
/**
|
|
* Authentication failed because a changed host key was detected and
|
|
* SSH is configured with strict host key checking enabled.
|
|
*/
|
|
ERR_AUTH_FAILED_DIFF_KEY,
|
|
/**
|
|
* The remote host closed the connection for unknown reasons.
|
|
*/
|
|
ERR_CLOSED_BY_REMOTE_HOST,
|
|
/**
|
|
* We have no idea what happened
|
|
*/
|
|
ERR_UNKNOWN,
|
|
/**
|
|
* The connect state machine entered an invalid state.
|
|
*/
|
|
ERR_INVALID_STATE,
|
|
ERR_MAX
|
|
};
|
|
|
|
/**
|
|
* Initialize a SSH process using the first SSH binary found in the PATH
|
|
*/
|
|
KSshProcess();
|
|
|
|
/**
|
|
* Initialize a SSH process using the specified SSH binary.
|
|
* @param pathToSsh The fully qualified path name of the ssh binary
|
|
* KSshProcess should use to setup a SSH connection.
|
|
*/
|
|
KSshProcess(QString pathToSsh);
|
|
~KSshProcess();
|
|
|
|
/**
|
|
* Set the ssh binary KSshProcess should use. This will only affect the
|
|
* next ssh connection attempt using this instance.
|
|
*
|
|
* @param pathToSsh Full path to the ssh binary.
|
|
*
|
|
* @return True if the ssh binary is found and KSshProcess
|
|
* recognizes the version.
|
|
*
|
|
*/
|
|
bool setSshPath(QString pathToSsh);
|
|
|
|
/**
|
|
* Get the ssh version.
|
|
*
|
|
* @return The ssh version or -1 if KSshProcess does not recognize
|
|
* the ssh version. The returned value corresponds to the
|
|
* member of the SshVersion enum.
|
|
*/
|
|
SshVersion version();
|
|
|
|
/**
|
|
* Get a string describing the ssh version
|
|
*
|
|
* @return A string describing the ssh version recognized by KSshProcess
|
|
*/
|
|
//QString versionStr();
|
|
|
|
/**
|
|
* Get the last error encountered by KSshProcess.
|
|
*
|
|
* @param msg Set to the error message, if any, outputted by ssh when it is run.
|
|
*
|
|
* @return The error number. See SshError for descriptions.
|
|
*/
|
|
int error(QString& msg);
|
|
|
|
/**
|
|
* Get the last error encountered by KSshProcess.
|
|
* @return The error number. See SshError for descriptions.
|
|
*/
|
|
int error() { return mError; }
|
|
|
|
QString errorMsg() { return mErrorMsg; }
|
|
|
|
/**
|
|
* Send a signal to the ssh process. Do not use this to end the
|
|
* ssh connection as it will not correctly reset the internal
|
|
* state of the KSshProcess object. Use KSshProcess::disconnect()
|
|
* instead.
|
|
*
|
|
* @param signal The signal to send to the ssh process. See 'kill -l'
|
|
* for a list of possible signals.
|
|
* The default signal is SIGKILL which kills ssh.
|
|
*
|
|
*/
|
|
void kill(int signal = SIGKILL);
|
|
|
|
/**
|
|
* The pid of the ssh process started by this instance of KSshProcess.
|
|
* Only valid if KSshProcess::running() returns true;
|
|
*
|
|
* @return The pid of the running ssh process.
|
|
*/
|
|
int pid() { return ssh.pid(); }
|
|
|
|
/**
|
|
* Whether a ssh connection has been established with a
|
|
* remote host. A establish connection means ssh has successfully
|
|
* authenticated with the remote host and user data can be transfered
|
|
* between the local and remote host. This cannot return
|
|
* true unless the most recent call to KSshProccess::connect() returned true.
|
|
*
|
|
* @return True if a ssh connection has been established with a remote
|
|
* host. False otherwise.
|
|
*/
|
|
bool connected() { return mConnected; }
|
|
|
|
/**
|
|
* Whether a ssh process is currently running. This only indicates
|
|
* if a ssh process has been started and is still running. It does not
|
|
* tell if authentication has been successful. This may return true
|
|
* even if the most recent call to KSshProcess::connect() returned false.
|
|
*
|
|
* @return True if a ssh process started by this instance of KSshProcess
|
|
* is running. False otherwise.
|
|
*/
|
|
bool running() { return mRunning; }
|
|
|
|
/**
|
|
* Print the command line arguments ssh is run with using kdDebug.
|
|
*/
|
|
void printArgs();
|
|
|
|
/**
|
|
* Set the SSH options.
|
|
* This must be called before connect(). See SshOptType for a list of
|
|
* supported ssh options. The required options are SSH_USERNAME
|
|
* and SSH_HOST.
|
|
*
|
|
* To reset the saved options, just recall setOptions() again with
|
|
* a different options list.
|
|
*
|
|
* @param opts A list of SshOpt objects specifying the ssh options.
|
|
*
|
|
* @return True if all options are valid. False if unrecognized options
|
|
* or a required option is missing. Call error()
|
|
* for details.
|
|
*
|
|
*/
|
|
bool setOptions(const SshOptList& opts);
|
|
|
|
/**
|
|
* Create a ssh connection based on the options provided by setOptions().
|
|
* Sets one of the following error codes on failure:
|
|
* <ul>
|
|
* <li>ERR_NO_OPTIONS</li>
|
|
* <li>ERR_CANNOT_LAUNCH</li>
|
|
* <li>ERR_INVALID_STATE</li>
|
|
* <li>ERR_NEED_PASSWD</li>
|
|
* <li>ERR_AUTH_FAILED</li>
|
|
* <li>ERR_NEW_HOST_KEY</li>
|
|
* <li>ERR_KEY_ACCEPTED</li>
|
|
* <li>ERR_DIFF_HOST_KEY</li>
|
|
* <li>ERR_INTERNAL</li>
|
|
* <li>ERR_INTERACT</li>
|
|
* </ul>
|
|
*
|
|
* @param acceptHostKey When true KSshProcess will automatically accept
|
|
* unrecognized or changed host keys.
|
|
*
|
|
* @return True if the ssh connection is successful. False if the connection
|
|
* fails. Call error() to get the reason for the failure.
|
|
*/
|
|
bool connect();
|
|
|
|
|
|
/**
|
|
* Disconnect ssh from the host. This kills the ssh process and
|
|
* resets the internal state of this KSshProcess object. After a
|
|
* disconnect, the same KSshProcess can be used to connect to a
|
|
* host.
|
|
*/
|
|
void disconnect();
|
|
|
|
/**
|
|
* Call to respond to a ERR_NEW_HOST_KEY or ERR_DIFF_HOST_KEY error.
|
|
*
|
|
* @param accept True to accept the host key, false to not accept the
|
|
* host key and kill ssh.
|
|
*
|
|
*/
|
|
void acceptHostKey(bool accept);
|
|
|
|
/**
|
|
* Call to respond to a ERR_NEED_PASSWD or ERR_NEED_PASSPHRASE error.
|
|
*
|
|
* @param password The user password to give ssh.
|
|
*/
|
|
void setPassword(QString password);
|
|
|
|
/**
|
|
* Access to standard in and out of the ssh process.
|
|
*
|
|
* @return The file description for stdin and stdout of the ssh process.
|
|
*/
|
|
int stdioFd() { return ssh.stdioFd(); }
|
|
|
|
/**
|
|
* Access to standard error of the ssh process.
|
|
*
|
|
* @return The file descriptior for stderr of the ssh process.
|
|
*/
|
|
int stderrFd() { return ssh.stderrFd(); }
|
|
|
|
/**
|
|
* Access the pty to which the ssh process is attached.
|
|
*
|
|
* @return The file descriptor of pty to which ssh is attached.
|
|
*/
|
|
int pty() { return ssh.fd(); }
|
|
private:
|
|
/**
|
|
* Path the the ssh binary.
|
|
*/
|
|
QString mSshPath;
|
|
|
|
/**
|
|
* SSH version. This is an index into the supported SSH
|
|
* versions array, and the various messages arrays.
|
|
*/
|
|
SshVersion mVersion;
|
|
|
|
/**
|
|
* User's password. Zero this out when it is no longer needed.
|
|
*/
|
|
QString mPassword;
|
|
|
|
/**
|
|
* User's username.
|
|
*/
|
|
QString mUsername;
|
|
|
|
/**
|
|
* Name of host we are connecting to.
|
|
*/
|
|
QString mHost;
|
|
|
|
/**
|
|
* Accept new or changed host keys if true.
|
|
*/
|
|
bool mAcceptHostKey;
|
|
|
|
/**
|
|
* Flag to tell use if we have an open, authenticated ssh
|
|
* session going.
|
|
*/
|
|
bool mConnected;
|
|
|
|
/**
|
|
* Flag to tell us if we have started a ssh process, we use this
|
|
* to make sure we kill ssh before going away.
|
|
*/
|
|
bool mRunning;
|
|
|
|
/**
|
|
* Save any key fingerprint msg from ssh so we can present
|
|
* it to the caller.
|
|
*/
|
|
QString mKeyFingerprint;
|
|
|
|
/**
|
|
* The location of the known host key file. We grab this from
|
|
* any error messages ssh prints out.
|
|
*/
|
|
QString mKnownHostsFile;
|
|
|
|
/**
|
|
* The state of our connect state machine.
|
|
*/
|
|
int mConnectState;
|
|
|
|
/**
|
|
* Port on on which the target ssh server is listening.
|
|
*/
|
|
int mPort;
|
|
|
|
/**
|
|
* The last error number encountered. This is only valid for the
|
|
* last error.
|
|
*/
|
|
SshError mError;
|
|
|
|
/**
|
|
* An error message that corresponds to the error number set in
|
|
* mError. Optional.
|
|
*/
|
|
QString mErrorMsg;
|
|
|
|
/**
|
|
* Interface to the SSH process we ceate. Handles communication
|
|
* to and from the SSH process using stdin, stdout, stderr, and
|
|
* pty.
|
|
*/
|
|
MyPtyProcess ssh;
|
|
|
|
/**
|
|
* List of arguments we start SSH with.
|
|
*/
|
|
QCStringList mArgs;
|
|
void init();
|
|
|
|
/**
|
|
* Handler to clean up when ssh process terminates.
|
|
*/
|
|
static void SIGCHLD_handler(int signo);
|
|
void installSignalHandlers();
|
|
void removeSignalHandlers();
|
|
|
|
QString getLine();
|
|
|
|
static QRegExp versionStrs[];
|
|
static const char * const passwordPrompt[];
|
|
static const char * const passphrasePrompt[];
|
|
static const char * const authSuccessMsg[];
|
|
static const char * const authFailedMsg[];
|
|
static QRegExp hostKeyMissingMsg[];
|
|
static const char * const hostKeyChangedMsg[];
|
|
static const char * const continuePrompt[];
|
|
static const char * const hostKeyAcceptedMsg[];
|
|
static const char * const tryAgainMsg[];
|
|
static QRegExp hostKeyVerifyFailedMsg[];
|
|
static const char * const connectionClosedMsg[];
|
|
static const char * const changeHostKeyOnDiskPrompt[];
|
|
static QRegExp keyFingerprintMsg[];
|
|
static QRegExp knownHostsFileMsg[];
|
|
};
|
|
#endif
|