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.
tdebase/kioslave/sftp/ksshprocess.h

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