diff options
Diffstat (limited to 'kioslave/sftp/ksshprocess.h')
-rw-r--r-- | kioslave/sftp/ksshprocess.h | 623 |
1 files changed, 623 insertions, 0 deletions
diff --git a/kioslave/sftp/ksshprocess.h b/kioslave/sftp/ksshprocess.h new file mode 100644 index 000000000..2ec1abfd6 --- /dev/null +++ b/kioslave/sftp/ksshprocess.h @@ -0,0 +1,623 @@ +/*************************************************************************** + ksshprocess.h - description + ------------------- + begin : Tue Jul 31 2001 + copyright : (C) 2001 by Lucas Fisher + email : [email protected] + ***************************************************************************/ + +/*************************************************************************** + * * + * 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 ([email protected]) + */ + 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 |