pax_global_header 0000666 0000000 0000000 00000000064 13705512536 0014521 g ustar 00root root 0000000 0000000 52 comment=ef7fbc4d34bb8eb4b59a9946d608a799f8f48ce5
SoftwareAG-ganymed-ssh-2-ef7fbc4/ 0000775 0000000 0000000 00000000000 13705512536 0016661 5 ustar 00root root 0000000 0000000 SoftwareAG-ganymed-ssh-2-ef7fbc4/.checkstyle 0000664 0000000 0000000 00000000711 13705512536 0021017 0 ustar 00root root 0000000 0000000
This FAQ includes information regarding topics that were discussed in e-mails between developers and users of the Ganymed SSH-2 for Java library.
Ganymed SSH-2 for Java homepage: https://code.google.com/p/ganymed-ssh-2/
Last update of FAQ: AUG-09-2013.
Please report bugs, typos and any kind of suggestions on our google code website.
The most often source of problems when executing a command with Session.execCommand() are missing/wrong set environment variables on the remote machine. Make sure that the minimum needed environment for XYZ is the same, independentely on how the shell is being invoked.
Example quickfix for bash users:
Note: If you really want to mimic the behavior of putty, then don't use Session.execCommand(), instead aquire a pty (pseudo terminal) and then start a shell (use Session.requestPTY() and Session.startShell()). You then have to communicate with the shell process at the other end through stdin and stdout. However, you also have to implement terminal logic (e.g., escape sequence handling (unless you use a "dumb" pty), "expect-send" logic (output parsing, shell prompt detection), etc.).
If you login by using putty, then putty will normally request a "xterm" pty and your assigned shell (e.g., bash) will be started (a so called "interactive login shell"). In contrast, if you use Session.execCommand() to start a command then (unless you ask for it) no pty will be aquired and the command will be given to the shell as an argument (with the shell's "-c" option).
The way a shell is being invoked has an effect on the set of initialization files which will be read be the shell.
To demonstrate the difference, try the following (from the command line, e.g., with an OpenSSH client):
If you compare the two outputs, then you will (unless you have adjusted your shell's settings) observe different environments.
If you are interested in the details, then please read the INVOCATION section in man page for the bash shell. You may notice that the definitions of "interactive" and "non-interactive" (and combinations with "login") are little bit tricky.
[TOP]In the SSH-2 low level protocol, each channel (e.g., session) has a receive window. When the remote SSH daemon has filled up our receive window, it must wait until we have consumed the input and are ready to accept new data.
Unfortunately, the SSH-2 protocol defines a shared window for stderr and stdout. As a consequence, if, for example, the remote process produces a lot of stderr data and you never consume it, then after some time the local receive window will be full and the sender is blocked. If you then try to read() from stdout, your call will be blocked: there is no stdout data (locally) available and the SSH daemon cannot send you any, since the receive window is full (you would have to read some stderr data first to "free" up space in the receive window).
Fortunately, Ganymed SSH-2 uses a 30KB window - the above described scenario should be very rare.
Many other SSH-2 client implementations just blindly consume any remotely produced data into a buffer which gets automatically extended - however, this can lead to another problem: in the extreme case the remote side can overflow you with data (e.g., leading to out of memory errors).
What can you do about this?
InputStream stdout = new StreamGobbler(mysession.getStdout()); InputStream stderr = new StreamGobbler(mysession.getStderr());You then can access stdout and stderr in any order, in the background the StreamGobblers will automatically consume all data from the remote side and store in an internal buffer.
If you need it, then this library offers quite a raw type of access to the SSH-2 protocol stack. Of course, many people don't need that kind of low level access. If you need buffered streams, then you should the do the same thing as you would probably do with the streams of a TCP socket: wrap them with instances of BufferedInputStream and BufferedOutputStream. In case you use StreamGobblers for the InputStreams, then you don't need any additional wrappers, since the StreamGobblers implement buffering already.
This code snippet will probably work well for most people:
InputStream stdout = new StreamGobbler(mysession.getStdout());
InputStream stderr = new StreamGobbler(mysession.getStderr());
OutputStream stdin = new BufferedOutputStream(mysession.getStdin(), 8192);
If you use Session.execCommand(), then you indeed can only execute only one command per session. This is not a restriction of the library, but rather an enforcement by the underlying SSH-2 protocol (a Session object models the underlying SSH-2 session).
There are several solutions:
You are probably using OpenSSH. By looking at their source code you will find out that there is a hard-coded constant called MAX_SESSIONS in the session.c file which is set to "10" by default. This is a per connection limit. Unfortunately, it is not a run-time tunable parameter. However, this limit has no effect on the number of concurrent port forwardings. Please note: this information is based on the OpenSSH 4.3 release.
Possible solutions:
Just for completeness: starting from release 210, the thrown exception may look as follows:
java.io.IOException: Could not open channel (The server refused to open the channel (SSH_OPEN_ADMINISTRATIVELY_PROHIBITED, 'open failed'))
Many default SSH server installations are configured to refuse the authentication type "password". Often, they only accept "publickey" and "keyboard-interactive". You have different options:
/etc/sshd/sshd_config
and change the value of "PasswordAuthentication" to "yes",
then send a HUP signal to the daemon so that it re-reads its configuration.
In general it is a good idea to call either Connection.getRemainingAuthMethods()
or Connection.isAuthMethodAvailable()
before using a certain authentication method.
Please note that most servers let you in after one successful authentication step. However, in rare cases
you may encounter servers that need several steps. I.e., if one of the Connection.authenticateWithXXX()
methods returns false
and Connection.isAuthenticationPartialSuccess()
returns
true
, then further authentication is needed. For each step, to find out which authentication methods
may proceed, you can use either the Connection.getRemainingAuthMethods()
or the Connection.isAuthMethodAvailable()
method. Again, please have a look into the
SwingShell.java example.
When using putty private keys (e.g., .ppk files) with public key authentication, you get a "Publickey authentication failed" exception. The reason is that the library currently is not able to directly handle private keys in the proprietary format used by putty. However, you can use the "puttygen" tool (from the putty website) to convert your key to the desired format: load your key, then go to the conversions menu and select "Save OpenSSH key" (which saves the key in openssl PEM format, e.g., call it "private.pem").
[TOP]Please read carefully the answer to the following question.
[TOP]The SCP protocol communicates the amount of data to be sent at the start of the transfer, so SCP remains out of consideration. Possible other solutions:
Be careful if you use the "cat" approach, as it may happen that not all your data will be written. If you close the stdin stream and immediatelly close the session (or the whole connection) then some SSH servers do not send the pending data to the process being executed ("cat" in this case). You have to wait until "cat" has received the EOF and terminates before closing the session. However, waiting for the termination may not always work, since SSH servers sometimes "forget" to send the exit code of the remote process. The following code MAY work:
Session sess = conn.openSession();
sess.execCommand("cat > test.txt");
OutputStream stdin = sess.getStdin();
... out.write(...) ... out.write(...) ...
/* The following flush() is only needed if you wrap the */
/* stdin stream (e.g., with a BufferedOutputStream). */
out.flush();
/* Now let's send EOF */
out.close();
/* Let's wait until cat has finished */
sess.waitForCondition(ChannelCondition.EXIT_STATUS, 2000);
/* Better: put the above statement into a while loop! */
/* In ANY CASE: read the Javadocs for waitForCondition() */
/* Show exit status, if available (otherwise "null") */
System.out.println("ExitCode: " + sess.getExitStatus());
/* Now its hopefully safe to close the session */
sess.close();
(Just a thought for another solution: execute cat > test.txt && echo "FINISHED"
and wait until you get "FINISHED" on stdout... - try it on your own risk =)
Please have at look at the examples section in the distribution, especially at the SwingShell.java example.
[TOP] SoftwareAG-ganymed-ssh-2-ef7fbc4/HISTORY.txt 0000664 0000000 0000000 00000042156 13705512536 0020573 0 ustar 00root root 0000000 0000000 Release Notes: ============== build263 2020-07-20 - Add gradle build. - Add OSGi support. - Message authentication: add support for hmac-sha2-256 and hmac-sha2-512. - Key exchange: add support for diffie-hellman-group14-sha256, diffie-hellman-group16-sha512 and diffie-hellman-group18-sha512. build261, 2013-08-09 - Joint work by Christian Plattner and David Kocher. - Hosted at https://code.google.com/p/ganymed-ssh-2/ with a shared SVN repository. - Requires Java 5 or later. - Added basic SSH server implementation. This is a proof of concept, use at your own risk, the interface may change. Beware of side channel attacks and other nifty stuff. See BasicServer.java example. - Provide POM for Maven 3 build. - Performance gains with request parallelism in requests for read and write write operations in SFTPv3Client. - Fixed softwareversion string in client-server handshake. - Bugfix for infinite timeout on socket connect. - SFTPInputStream and SFTPOutputStream to read or write from a SFTPv3FileHandle compatible with standard I/O operations in Java. - SCPClient returning SCPInputStream and SCPOutputStream for get and put operations respectively compatible with stanard I/O operations in Java. - Option to attach a packet listener to SFTPv3Client for logging purposes. - Fixed a bug in KnownHosts.getPreferredServerHostkeyAlgorithmOrder (it always returned null, which is a valid return value but wrong). - Added accessor to query session state and isConnected() in SFTPv3Client returning a boolean. - Adjusted channel window size. - Custom PEMDecryptException to catch PEM decoding failure. - Option to pass custom user agent string for identification string. - Continue silently when invalid key is found in known hosts file. - Add support for file append mode in SFTPv3Client. - Option to pass charset for commands executed on the remote machine. - Add method to get local socket address from LocalPortForwarder. - Allow to remove connection monitors. - Add a connection monitor only if it was not added yet. build251beta1, 2010-08-23: - Due to the slightly changed semantics of Thread.interrupt() and Object.wait() in JDK 6, the code had to be adapted in several places. Please do not use Ganymed SSH-2 build250 on JDK 6, as the Kex and Streamgobbler code may occasionally consume all CPU resources. Background: the JDK 6 javadocs for Object.wait say: "If the current thread is interrupted by any thread before or while it is waiting, then an InterruptedException is thrown." Unfortunately the Java 1.5 documentation does not say "before", only "while it is waiting". Thanks to Elias Horness. build250, 2010-03-15: - Ganymed SSH-2 for Java is back. The software can be downloaded from http://www.cleondris.ch/ssh2 - Fixed the use of default platform character encodings. - Fixes for buggy servers. - Wait for the reply to the ServiceRequest packet before sending the next request. - Re-assign the interrupted flag after a InterruptedException. build211beta4, 2006-12-05: (never officially released) - Added Connection.sendIgnorePacket(...) methods which allow to send SSH_MSG_IGNORE packets. Thanks to Andrei Tchijov. - Added support for the "none" authentication method. - Revised the SHA-1 code. Highly optimized. Speed should be more than doubled. - Changed references to the correct RFCs (instead of the drafts) in the javadocs (where possible). - Fixed the write() method in the SFTP code. Unsatisfiable preconditions stopped the method from writing any bytes. As stated in the documentation, the SFTP code is still experimental. Thanks to Andreas Pueschel. - Added a new createLocalPortForwarder(InetSocketAddress addr, ...) method which allows to specify the local address and port to bind to. Thanks to Andrei Tchijov. - Slightly updated the FAQ. build210, 2006-10-06: - Added HTTP proxy support. See Connection.setProxyData() and the HTTPProxyData class. Thanks to Jean-Pierre Schmit for providing example code. - Added basic support for SFTP (v3). - Beta users: removed support for automatic split of huge read transfers in SFTP, as it was not possible to return EOF in a clean way. The write method still splits huge transfers (in blocks of 32768 bytes). Thanks to Zhong Li. - SCP enhancement. It is now possible to specify an empty target directory name when sending files. This is analogous to using "scp file user@host:" (thanks to Bernd Eggink). - SCP enhancement. It is now possible to receive a remote file and pipe it directly into an OutputStream. Thanks to Bernd Eggink. - SCP enhancement. It is now possible to specify a different remote filename when sending a file. Thanks to Thomas Tatzel. - Added more verbose error messages in case a channel open operation fails (e.g., resource shortage on the server). Related to this, added a comment to the FAQ regarding the limitation on the number of concurrent sessions per connection in OpenSSH. Thanks to Ron Warshawsky. - Added a feature (ConnectionMonitor) to get notified when a connection breaks. Thanks to Daniel Ritz (Alcatel). - It is now possible to override the used SecureRandom instance (Connection.setSecureRandom()). - Added getters for the server's hostname and port to the Connection class. - Added examples for HTTP proxy usage as well as local/remote port forwarding. - Added support for SSH_MSG_KEX_DH_GEX_REQUEST_OLD in the DHGexParameters class (there is a new, additional constructor). Please check the Javadoc for DHGexParameters. - Clarified in the javadoc the issue of re-using Connection objects. Changed the exception message in case connect() is invoked on an already connected connection. - Added an entry to the FAQ regarding pumping data into remote files. Thanks to Daniel Schwager. - Changed JDialog.show() to JDialog.setVisible(true) in the SwingShell example. The show() method is deprecated in Java 5.0. Thanks to Carlo Dapor. - Fixed the behavior of the local port forwarder code. Trying to listen on an already bound port will not fail silently anymore. Also, the accept thread will continue accepting connections even if there was a problem with the establishment of the underlying ssh-forwarding of a previous incoming connection (e.g., one tried to establish a forwarding to a remote port that is not in state open (yet)). Thanks to Claudio Nieder (Inodes, Switzerland) and Daniel Ritz (Alcatel) for pointing this out. Note: the interface for managing port forwardings needs to be further improved. - Tried to implement a workaround for the Sun JVM bug 5092063. Changed InetAddress.getByAddress(byte[]) in the "TransportManager.parseIPv4Address" method (which already is a workaround for JDK's that use the resolver for dotted IP addresses, independently from the 5092063 bug) to InetAddress.getByAddress(String, byte[]). Thanks to Alain Philipin. - Fixed a bug in KnownHosts.addHostkeyToFile. Hostnames were converted to lowercase which is not good in case of hashed hostnames (it leads to a different BASE64 encoding and therefore hashes won't match). Thanks to [unknown]. - Fixed a typo in the SCP client (tag for modification times is 'T' and not 'P'). Thanks to Andreas Sahlbach. - Stupid performance enhancement in the Logger, it did unnecessary calls to System.currentTimeMillis(). - The LICENCE.txt file is now also included in the pre-compiled jar. Of course, redistributions in binary form must *still* include the contents of LICENCE.txt in the documentation and/or other materials provided with the distribution. - Small cleanups in the TransportManager code. build209, 2006-02-14: - A major release, many new features. Thanks to all who supported me with feedback! - Added remote port forwarding support. Please consult the docs for Connection.requestRemotePortForwarding(). - Added X11 forwarding support. Please consult Session.requestX11Forwarding(). X11 support is based on joint work with Simon Hartl (simon.hartl (at) gmx.net). Thanks, Simon! - The SCPClient constructor is now public. The factory method is still there (in the Connection class), however, it will probably be marked as deprecated and eventually be removed in the future. - Added startSubSystem() method to the Session class. Now it is possible to implement subsystems, e.g., sftp, outside of the library. - For advanced users: there is now a much better condition wait interface in the Session class. It is now also possible to wait for the arrival of "exit-status" and "exit-signal". The Session.waitUntilDataAvailable() method still works, but is marked as deprecated. Users that used the beta version, please be aware of the following change: calling the close() method on a Session object will immediatelly raise the ChannelCondition.CLOSED/EOF conditions on the underlying channel - even though the remote side may not have yet responded with a SSH_MSG_CHANNEL_CLOSE message (however, in the background the library still expects the server to send the SSH_MSG_CHANNEL_CLOSE message). See below for an explanation. - The behavior of Session.close() has changed. If you *kill* a Session (i.e., call Session.close() before EOF (or CLOSE) has been sent by the remote side), then immediatelly EOF will (locally) be raised for both stdout and stderr. Further incoming data (for that particular Session) will be ignored. However, remote data that arrived before we sent our SSH_MSG_CHANNEL_CLOSE message is still available (you can think of having appended the EOF marker to the end of the local incoming stdout and stderr queues). The reason to do this is simply because some SSH servers do sometimes not reply to our SSH_MSG_CHANNEL_CLOSE message (event though they should). As a consequence, a local reader may wait forever for the remote SSH_MSG_CHANNEL_EOF or SSH_MSG_CHANNEL_CLOSE messages to arrive. If you are interested, then you can try to reproduce the problem: Execute something like "tail -f /dev/null" (which should do nothing forever) and then concurrently close the Session (enable debug mode to see the SSH packets on the wire) to check how standard compliant your server implementation is). Thanks to Cristiano Sadun. - The Session code does not anymore make use of the synchronized attribute for any of its methods. This has the advantage that a call to Session.close() will never by blocked by concurrent calls to the Session object. However, note that in the worst case the call may still block until we can send our SSH_MSG_CHANNEL_CLOSE over the TCP connection. - The SCP client can now also be used to directly inject the contents of a given byte array into a remote file (thanks to Dieter Baier for suggesting this). - Added support for specifying timeouts for connection establishment. Thanks to Rob Hasselbaum and Ben XYZ. - Performance improvement: we use only a single SecureRandom object per connection (until now there were multiple instances). - Fixed the Swingshell example program, it did not read in the known_hosts file on startup. (thanks to Ashwani Kumar). - There was a typo in the CBCMode class (srcoff was ignored), however since we always pass a zero offset the bug did not show up (thanks to Alex Pakhomov). - While implementing X11 support, found a strange X11 bug in OpenSSH (reported, openssh bug 1076). 12.10.2005: has been fixed for OpenSSH 4.3 by the OpenSSH crowd. - Changed the SingleThreadStdoutStderr example so that it uses the new condition wait interface. - Efficiently handle IPv4 addresses (when creating the underlying socket), there was a report that some JDK's try to lookup dotted addresses with the resolver. (thanks to Alexander Kitaev). - Added setTCPNoDelay() method to the Connection class. - Improved handling of unsupported global/channel requests received from the server. - The KEX code is now more robust. Also, the ServerHostKeyVerifier callback (if specified) will be called before doing any DH calculation. - Major cleanup (i.e., rewrite) of the SSH channel code. - Cleanup up Session class, removed unnecessary IOExceptions. - Implemented 2^32-1 conformance for channel windows. - I got several times questions by e-mail from people that have problems with "putty" keys. Added an entry to the FAQ. - Added an entry to the FAQ regarding how to handle servers with disabled password authentication (thanks to Nicolas Raoul). - Upcoming: SFTP support (in the meantime almost a running gag). - Changed the name from "Ganymed SSH2" to "Ganymed SSH-2". Will this improve the G**gle ranking? =) - Latest javadoc is now also online. build208, 2005-08-24: - Added support for RSA private keys (PEM format), also revised code for RSA signature verification. - Extended support for encrypted PEM files. Supported encryptions: DES-CBC, DES-EDE3-CBC, AES-128-CBC, AES-192-CBC and AES-256-CBC. - Added rather complete support for known_hosts files (in KnownHosts.java). The parser is able to understand the same pseudo-regex (*,?,!) syntax as OpenSSH clients. The class includes support for hostkey comparison as well as functionality to add accepted keys to a known_hosts file. One can also create OpenSSH compatible fingerprints (Hex and Bubblebabble). Hashed hostname entries are understood and can also be generated. - Restructured the examples section, added more examples. The examples should cover most issues. There is also a _very_ basic terminal emulator, see SwingShell.java. - It is now possible to override the default server hostkey algorithm order (for the key exchange) with the Connection.setServerHostKeyAlgorithms method. This makes sense in combination with known_hosts support (e.g., if you already know the server's public ssh-dss key, then you probably prefer the "ssh-dss" over the "ssh-rsa" algorithm). The used server hostkey algorithm is now also reflected in ConnectionInfo. - The default server hostkey algorithm order is now "ssh-rsa", "ssh-dss". - Important: revised Input- and OutputStream code, everything is now unbuffered (less memory consumption, more direct interface, see also StreamGobbler class and the FAQ). - Added StreamGobbler helper class. - Method verifyServerHostKey() in the ServerHostKeyVerifier may now throw exceptions (an alternative to returning "false"). - All background threads (the per-connection receive thread as well as all threads used in forwarders and StreamGobblers) now use setDaemon(true) on startup. - Added "diffie-hellman-group14-sha1" support to the key exchange code. - Added chained IOExceptions where applicable (IOException initialization with initCause()). - Cleaned up packet building code, removed unnecessary server-side methods. - Cleaned up javadoc of SCPClient: replaced umask with mode. - Fixed a bug in the server identification string parser. This actually prevented a successful key exchange with some ssh servers (the server's signature was rejected). Thanks to Alex Molochnikov for the initial bug report and for helping in tracking down the issue. - Fixed a buffer re-allocation bug in the beta version of the StreamGobbler class (thanks to Marc Lijour). - Fixed flawed UINT64 support (thanks to Bob Simons). - Fixed a bug in the build script of the beta builds (sftp beta directory was not completely removed) (thanks to Richard Hash). - Use zero based padding for unencrypted traffic. - Changed again the client identification string (the one presented to the server). - Created a FAQ, available on the website and in the distribution. - Revised javadoc comments. Also, the generated documentation is now located in the subdirectory "javadoc" instead of "doc" (in the distribution). - Added README.txt to the distribution. build207, 2005-07-21: - Added "Keyboard Interactive" authentication method: authenticateWithKeyboardInteractive() in Connection.java, also have a look at InteractiveCallback.java. - Extended authentication interface in Connection.java (backwards compatible). New functionality: getRemainingAuthMethods(), isAuthMethodAvailable(), isAuthenticationComplete() and isAuthenticationPartialSuccess(). - Using an authentication method not supported by the server leads now to an exception (instead of returning "false"). Use isAuthMethodAvailable() if you want to check for the availability of an authentication method. - Fixed a bug in SCPClient which sometimes lead to failed downloads. - Improved channel window handling. - Removed bogus (CVS) version string from Connection.java - Changed client identification string to "Ganymed_buildXXX". - Changed the jar file naming scheme (ganymed-ssh2-buildXXX.jar). - Started adding logging support for debugging purposes (currently only for development). - Cleanup of javadoc and comments at several places. - Reversed order of entries in HISTORY.TXT build206, 2005-07-04: - Fixed small resource issue with SCP (thanks to Michaël Giraud). - Added LocalStreamForwarder. - Added HISTORY.TXT build205, 2005-06-27: - Initial release. SoftwareAG-ganymed-ssh-2-ef7fbc4/LICENSE.txt 0000664 0000000 0000000 00000011030 13705512536 0020477 0 ustar 00root root 0000000 0000000 Copyright (c) 2006 - 2013 Christian Plattner. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: a.) Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. b.) Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. c.) Neither the name of Christian Plattner nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. This software includes work that was released under the following license: Copyright (c) 2005 - 2006 Swiss Federal Institute of Technology (ETH Zurich), Department of Computer Science (http://www.inf.ethz.ch), Christian Plattner. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: a.) Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. b.) Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. c.) Neither the name of ETH Zurich nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. The Java implementations of the AES, Blowfish and 3DES ciphers have been taken (and slightly modified) from the cryptography package released by "The Legion Of The Bouncy Castle". Their license states the following: Copyright (c) 2000 - 2004 The Legion Of The Bouncy Castle (http://www.bouncycastle.org) 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. SoftwareAG-ganymed-ssh-2-ef7fbc4/README.txt 0000664 0000000 0000000 00000005020 13705512536 0020354 0 ustar 00root root 0000000 0000000 This repository is forked from one of the automatic exports of code.google.com/p/ganymed-ssh-2 and contains updates on top of the ganymed-ssh2-262 tag. The ganymed-ssh2-262 tag was used to produce the latest public build in Maven Central, which is version 262, so builds from this repository should be version 263. The latest public build in Maven Central is available at https://search.maven.org/artifact/ch.ethz.ganymed/ganymed-ssh2/262/jar The following features were added in version 263: - Gradle build support. - OSGi support. - Message authentication: support for hmac-sha2-256 and hmac-sha2-512. - Key exchange: support for diffie-hellman-group14-sha256, diffie-hellman-group16-sha512, and diffie-hellman-group18-sha512. The code from this repository is used in Software AG Command Central and tested using the Command Central continuous integration. However, this code is not officially supported by Software AG. ********************************** This software is provided as-is and without warranty or support. It does not constitute part of the Software AG product suite. Users are free to use, fork and modify it, subject to the license agreement. While Software AG welcomes contributions, we cannot guarantee to include every contribution in the master project. ********************************** Original content: Ganymed SSH-2 for Java - build 261 ================================== https://code.google.com/p/ganymed-ssh-2/ Ganymed SSH-2 for Java is a library which implements the SSH-2 protocol in pure Java (tested on J2SE 5 and 6). It allows one to connect to SSH servers from within Java programs. It supports SSH sessions (remote command execution and shell access), local and remote port forwarding, local stream forwarding, X11 forwarding, SCP and SFTP. There are no dependencies on any JCE provider, as all crypto functionality is included. There is also a basic (preliminary) SSH server implementation included. Ganymed SSH-2 for Java was originally developed by Christian Plattner for the Ganymed replication project and a couple of other projects at the IKS group at ETH Zurich (Switzerland). These days, the code is maintained by Dr. Christian Plattner and David Kocher. This distribution contains the source code, examples, javadoc and the FAQ. It also includes a pre-compiled jar version of the library which is ready to use. - Please read the included LICENCE.txt - Latest changes can be found in HISTORY.txt - The FAQ can be found in the FAQ.html Switzerland, August 2013 SoftwareAG-ganymed-ssh-2-ef7fbc4/build.gradle 0000664 0000000 0000000 00000002607 13705512536 0021145 0 ustar 00root root 0000000 0000000 plugins { id 'java-library' } repositories { jcenter() } dependencies { testImplementation 'junit:junit:4.11' } group = 'ch.ethz.ganymed' version = '263' description = 'ganymed-ssh2' sourceCompatibility = '1.6' sourceSets.main.resources.srcDir project.rootDir sourceSets.main.resources.includes = [ 'FAQ.html', 'HISTORY.txt', 'LICENSE.txt', 'overview.html', 'README.txt' ] java { withSourcesJar() withJavadocJar() } javadoc.failOnError = false jar { manifest { attributes( 'Bundle-Description': 'Ganymed SSH2 Client Library', 'Bundle-SymbolicName': 'ch.ethz.ssh2.ganymed-ssh2', 'Bundle-ManifestVersion': '2', 'Bundle-ActivationPolicy': 'lazy', 'Bundle-RequiredExecutionEnvironment': 'JavaSE-1.6', 'Export-Package': 'ch.ethz.ssh2;version=1.0.0,ch.ethz.ssh2.auth;version=1.0.0,ch.ethz.ssh2.channel;version=1.0.0,ch.ethz.ssh2.crypto;version=1.0.0,ch.ethz.ssh2.crypto.cipher;version=1.0.0,ch.ethz.ssh2.crypto.dh;version=1.0.0,ch.ethz.ssh2.crypto.digest;version=1.0.0,ch.ethz.ssh2.log;version=1.0.0,ch.ethz.ssh2.packets;version=1.0.0,ch.ethz.ssh2.server;version=1.0.0,ch.ethz.ssh2.sftp;version=1.0.0,ch.ethz.ssh2.signature;version=1.0.0,ch.ethz.ssh2.transport;version=1.0.0,ch.ethz.ssh2.util;version=1.0.0', 'Bundle-Version': '1.0.263', 'Bundle-Name': 'Ganymed SSH2' ) } } SoftwareAG-ganymed-ssh-2-ef7fbc4/examples/ 0000775 0000000 0000000 00000000000 13705512536 0020477 5 ustar 00root root 0000000 0000000 SoftwareAG-ganymed-ssh-2-ef7fbc4/examples/Basic.java 0000664 0000000 0000000 00000003620 13705512536 0022364 0 ustar 00root root 0000000 0000000 /* * Copyright (c) 2006-2011 Christian Plattner. All rights reserved. * Please refer to the LICENSE.txt for licensing details. */ import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import ch.ethz.ssh2.Connection; import ch.ethz.ssh2.Session; import ch.ethz.ssh2.StreamGobbler; public class Basic { public static void main(String[] args) { String hostname = "127.0.0.1"; String username = "joe"; String password = "joespass"; try { /* Create a connection instance */ Connection conn = new Connection(hostname); /* Now connect */ conn.connect(); /* Authenticate. * If you get an IOException saying something like * "Authentication method password not supported by the server at this stage." * then please check the FAQ. */ boolean isAuthenticated = conn.authenticateWithPassword(username, password); if (isAuthenticated == false) throw new IOException("Authentication failed."); /* Create a session */ Session sess = conn.openSession(); sess.execCommand("uname -a && date && uptime && who"); System.out.println("Here is some information about the remote host:"); /* * This basic example does not handle stderr, which is sometimes dangerous * (please read the FAQ). */ InputStream stdout = new StreamGobbler(sess.getStdout()); BufferedReader br = new BufferedReader(new InputStreamReader(stdout)); while (true) { String line = br.readLine(); if (line == null) { br.close(); break; } System.out.println(line); } /* Show exit status, if available (otherwise "null") */ System.out.println("ExitCode: " + sess.getExitStatus()); /* Close this session */ sess.close(); /* Close the connection */ conn.close(); } catch (IOException e) { e.printStackTrace(System.err); System.exit(2); } } } SoftwareAG-ganymed-ssh-2-ef7fbc4/examples/BasicServer.java 0000664 0000000 0000000 00000013263 13705512536 0023557 0 ustar 00root root 0000000 0000000 /* * Copyright (c) 2006-2013 Christian Plattner. All rights reserved. * Please refer to the LICENSE.txt for licensing details. */ import java.io.IOException; import java.math.BigInteger; import java.net.ServerSocket; import java.net.Socket; import java.security.SecureRandom; import java.util.Random; import ch.ethz.ssh2.AuthenticationResult; import ch.ethz.ssh2.ConnectionInfo; import ch.ethz.ssh2.PtySettings; import ch.ethz.ssh2.ServerAuthenticationCallback; import ch.ethz.ssh2.ServerConnection; import ch.ethz.ssh2.ServerConnectionCallback; import ch.ethz.ssh2.ServerSession; import ch.ethz.ssh2.ServerSessionCallback; import ch.ethz.ssh2.SimpleServerSessionCallback; import ch.ethz.ssh2.signature.RSAPrivateKey; public class BasicServer implements ServerAuthenticationCallback, ServerConnectionCallback { private static final int SERVER_PORT = 2222; private static final String AUTH_USER = "root"; private static final String AUTH_PASS = "kensentme"; public static void main(String[] args) throws IOException { BasicServer bs = new BasicServer(); bs.acceptSingleConnection(); } private static ServerConnection conn = null; private void acceptSingleConnection() throws IOException { System.out.println("Generating RSA private key..."); System.out.flush(); /* Generate random RSA private key */ Random rnd = new SecureRandom(); int N = 1500; BigInteger p = BigInteger.probablePrime(N / 2, rnd); BigInteger q = BigInteger.probablePrime(N / 2, rnd); BigInteger phi = (p.subtract(BigInteger.ONE)).multiply(q.subtract(BigInteger.ONE)); BigInteger n = p.multiply(q); BigInteger e = new BigInteger("65537"); BigInteger d = e.modInverse(phi); RSAPrivateKey hostkey = new RSAPrivateKey(d, e, n); /* Accept a connection */ System.out.println("Waiting for one (1) connection on port " + SERVER_PORT + "..."); System.out.flush(); ServerSocket ss = new ServerSocket(SERVER_PORT); Socket s = ss.accept(); ss.close(); // We only accept one connection and immediately close the server socket /* Wrap the established socket with a SSH-2 connection */ conn = new ServerConnection(s); conn.setRsaHostKey(hostkey); conn.setAuthenticationCallback(this); conn.setServerConnectionCallback(this); /* Do the kex exchange and asynchronously start processing requests */ System.out.println("Doing key exchange with client..."); System.out.flush(); conn.connect(); /* Show some information about the connection */ ConnectionInfo info = conn.getConnectionInfo(); System.out.println("Kex Algo: " + info.keyExchangeAlgorithm); System.out.println("Kex Count: " + info.keyExchangeCounter); System.out.println("Server Host Key Algo: " + info.serverHostKeyAlgorithm); System.out.println("C2S Crypto Algo: " + info.clientToServerCryptoAlgorithm); System.out.println("C2S MAC Algo: " + info.clientToServerMACAlgorithm); System.out.println("S2C Crypto Algo: " + info.serverToClientCryptoAlgorithm); System.out.println("S2C MAC Algo: " + info.serverToClientMACAlgorithm); System.out.flush(); /* The connection is working, have fun and force a key exchange every few * seconds to test whether the client is implemented correctly... */ while (true) { try { Thread.sleep(10000); } catch (InterruptedException e1) { } System.out.println("Forcing key exchange with client..."); System.out.flush(); conn.forceKeyExchange(); } } public ServerSessionCallback acceptSession(final ServerSession session) { SimpleServerSessionCallback cb = new SimpleServerSessionCallback() { @Override public Runnable requestPtyReq(final ServerSession ss, final PtySettings pty) throws IOException { return new Runnable() { public void run() { System.out.println("Client requested " + pty.term + " pty"); } }; } @Override public Runnable requestShell(final ServerSession ss) throws IOException { return new Runnable() { public void run() { try { while (true) { int c = ss.getStdout().read(); if (c < 0) { System.err.println("SESSION EOF"); return; } ss.getStdin().write(("You typed " + c + "\r\n").getBytes()); } } catch (IOException e) { System.err.println("SESSION DOWN"); e.printStackTrace(); } } }; } }; return cb; } public String initAuthentication(ServerConnection sc) { return "=====================================\r\nWelcome to the Ganymed SSH Server\r\nBeware of the barking dog!\r\n=====================================\r\n"; } public String[] getRemainingAuthMethods(ServerConnection sc) { return new String[] { ServerAuthenticationCallback.METHOD_PASSWORD, ServerAuthenticationCallback.METHOD_PUBLICKEY }; } public AuthenticationResult authenticateWithNone(ServerConnection sc, String username) { return AuthenticationResult.FAILURE; } public AuthenticationResult authenticateWithPassword(ServerConnection sc, String username, String password) { if (AUTH_USER.equals(username) && AUTH_PASS.equals(password)) return AuthenticationResult.SUCCESS; return AuthenticationResult.FAILURE; } public AuthenticationResult authenticateWithPublicKey(ServerConnection sc, String username, String algorithm, byte[] publickey, byte[] signature) { /* Isn't that a bit unfair? We offer public key authentication, but then deny every attempt =) */ return AuthenticationResult.FAILURE; } } SoftwareAG-ganymed-ssh-2-ef7fbc4/examples/BasicWithHTTPProxy.java 0000664 0000000 0000000 00000004460 13705512536 0024765 0 ustar 00root root 0000000 0000000 /* * Copyright (c) 2006-2011 Christian Plattner. All rights reserved. * Please refer to the LICENSE.txt for licensing details. */ import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import ch.ethz.ssh2.Connection; import ch.ethz.ssh2.HTTPProxyData; import ch.ethz.ssh2.Session; import ch.ethz.ssh2.StreamGobbler; public class BasicWithHTTPProxy { public static void main(String[] args) { String hostname = "my-ssh-server"; String username = "joe"; String password = "joespass"; String proxyHost = "192.168.1.1"; int proxyPort = 3128; // default port used by squid try { /* Create a connection instance */ Connection conn = new Connection(hostname); /* We want to connect through a HTTP proxy */ conn.setProxyData(new HTTPProxyData(proxyHost, proxyPort)); // if the proxy requires basic authentication: // conn.setProxyData(new HTTPProxyData(proxyHost, proxyPort, "username", "secret")); /* Now connect (through the proxy) */ conn.connect(); /* Authenticate. * If you get an IOException saying something like * "Authentication method password not supported by the server at this stage." * then please check the FAQ. */ boolean isAuthenticated = conn.authenticateWithPassword(username, password); if (isAuthenticated == false) throw new IOException("Authentication failed."); /* Create a session */ Session sess = conn.openSession(); sess.execCommand("uname -a && date && uptime && who"); System.out.println("Here is some information about the remote host:"); /* * This basic example does not handle stderr, which is sometimes dangerous * (please read the FAQ). */ InputStream stdout = new StreamGobbler(sess.getStdout()); BufferedReader br = new BufferedReader(new InputStreamReader(stdout)); while (true) { String line = br.readLine(); if (line == null) { br.close(); break; } System.out.println(line); } /* Show exit status, if available (otherwise "null") */ System.out.println("ExitCode: " + sess.getExitStatus()); /* Close this session */ sess.close(); /* Close the connection */ conn.close(); } catch (IOException e) { e.printStackTrace(System.err); System.exit(2); } } } SoftwareAG-ganymed-ssh-2-ef7fbc4/examples/PortForwarding.java 0000664 0000000 0000000 00000006550 13705512536 0024317 0 ustar 00root root 0000000 0000000 /* * Copyright (c) 2006-2011 Christian Plattner. All rights reserved. * Please refer to the LICENSE.txt for licensing details. */ import java.io.File; import java.io.IOException; import ch.ethz.ssh2.Connection; import ch.ethz.ssh2.LocalPortForwarder; public class PortForwarding { public static void sleepSomeTime(long milliSeconds) { try { Thread.sleep(milliSeconds); } catch (InterruptedException e) { } } public static void main(String[] args) { String hostname = "127.0.0.1"; String username = "joe"; File keyfile = new File("~/.ssh/id_rsa"); // or "~/.ssh/id_dsa" String keyfilePass = "joespass"; // will be ignored if not needed try { /* Create a connection instance */ Connection conn = new Connection(hostname); /* Now connect */ conn.connect(); /* Authenticate */ boolean isAuthenticated = conn.authenticateWithPublicKey(username, keyfile, keyfilePass); if (isAuthenticated == false) throw new IOException("Authentication failed."); /* ===== OK, now let's establish some local port forwardings ===== */ /* Example Port Forwarding: -L 8080:www.ethz.ch:80 (OpenSSH notation) * * This works by allocating a socket to listen on 8080 on the local interface (127.0.0.1). * Whenever a connection is made to this port (127.0.0.1:8080), the connection is forwarded * over the secure channel, and a connection is made to www.ethz.ch:80 from the remote * machine (i.e., the ssh server). * * (the above text is based partially on the OpenSSH man page) */ /* You can create as many of them as you want */ LocalPortForwarder lpf1 = conn.createLocalPortForwarder(8080, "www.ethz.ch", 80); /* Now simply point your webbrowser to 127.0.0.1:8080 */ /* (on the host where you execute this program) */ /* ===== OK, now let's establish some remote port forwardings ===== */ /* Example Port Forwarding: -R 127.0.0.1:8080:www.ganymed.ethz.ch:80 (OpenSSH notation) * * Specifies that the port 127.0.0.1:8080 on the remote server is to be forwarded to the * given host and port on the local side. This works by allocating a socket to listen to port * 8080 on the remote side (the ssh server), and whenever a connection is made to this port, the * connection is forwarded over the secure channel, and a connection is made to * www.ganymed.ethz.ch:80 by the Ganymed SSH-2 library. * * (the above text is based partially on the OpenSSH man page) */ /* You can create as many of them as you want */ conn.requestRemotePortForwarding("127.0.0.1", 8080, "www.ganymed.ethz.ch", 80); /* Now, on the ssh server, if you connect to 127.0.0.1:8080, then the connection is forwarded * through the secure tunnel to the library, which in turn will forward the connection * to www.ganymed.ethz.ch:80. */ /* Sleep a bit... (30 seconds) */ sleepSomeTime(30000); /* Stop accepting remote connections that are being forwarded to www.ganymed.ethz.ch:80 */ conn.cancelRemotePortForwarding(8080); /* Sleep a bit... (20 seconds) */ sleepSomeTime(20000); /* Stop accepting connections on 127.0.0.1:8080 that are being forwarded to www.ethz.ch:80 */ lpf1.close(); /* Close the connection */ conn.close(); } catch (IOException e) { e.printStackTrace(System.err); System.exit(2); } } } SoftwareAG-ganymed-ssh-2-ef7fbc4/examples/PublicKeyAuthentication.java 0000664 0000000 0000000 00000003215 13705512536 0026132 0 ustar 00root root 0000000 0000000 /* * Copyright (c) 2006-2011 Christian Plattner. All rights reserved. * Please refer to the LICENSE.txt for licensing details. */ import java.io.BufferedReader; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import ch.ethz.ssh2.Connection; import ch.ethz.ssh2.Session; import ch.ethz.ssh2.StreamGobbler; public class PublicKeyAuthentication { public static void main(String[] args) { String hostname = "127.0.0.1"; String username = "joe"; File keyfile = new File("~/.ssh/id_rsa"); // or "~/.ssh/id_dsa" String keyfilePass = "joespass"; // will be ignored if not needed try { /* Create a connection instance */ Connection conn = new Connection(hostname); /* Now connect */ conn.connect(); /* Authenticate */ boolean isAuthenticated = conn.authenticateWithPublicKey(username, keyfile, keyfilePass); if (isAuthenticated == false) throw new IOException("Authentication failed."); /* Create a session */ Session sess = conn.openSession(); sess.execCommand("uname -a && date && uptime && who"); InputStream stdout = new StreamGobbler(sess.getStdout()); BufferedReader br = new BufferedReader(new InputStreamReader(stdout)); System.out.println("Here is some information about the remote host:"); while (true) { String line = br.readLine(); if (line == null) { br.close(); break; } System.out.println(line); } /* Close this session */ sess.close(); /* Close the connection */ conn.close(); } catch (IOException e) { e.printStackTrace(System.err); System.exit(2); } } } SoftwareAG-ganymed-ssh-2-ef7fbc4/examples/SimpleVerifier.java 0000664 0000000 0000000 00000002711 13705512536 0024270 0 ustar 00root root 0000000 0000000 /* * Copyright (c) 2006-2011 Christian Plattner. All rights reserved. * Please refer to the LICENSE.txt for licensing details. */ import ch.ethz.ssh2.KnownHosts; import ch.ethz.ssh2.ServerHostKeyVerifier; class SimpleVerifier implements ServerHostKeyVerifier { KnownHosts database; /* * This class is being used by the UsingKnownHosts.java example. */ public SimpleVerifier(KnownHosts database) { if (database == null) throw new IllegalArgumentException(); this.database = database; } public boolean verifyServerHostKey(String hostname, int port, String serverHostKeyAlgorithm, byte[] serverHostKey) throws Exception { int result = database.verifyHostkey(hostname, serverHostKeyAlgorithm, serverHostKey); switch (result) { case KnownHosts.HOSTKEY_IS_OK: return true; // We are happy case KnownHosts.HOSTKEY_IS_NEW: // Unknown host? Blindly accept the key and put it into the cache. // Well, you definitely can do better (e.g., ask the user). // The following call will ONLY put the key into the memory cache! // To save it in a known hosts file, also call "KnownHosts.addHostkeyToFile(...)" database.addHostkey(new String[] { hostname }, serverHostKeyAlgorithm, serverHostKey); return true; case KnownHosts.HOSTKEY_HAS_CHANGED: // Close the connection if the hostkey has changed. // Better: ask user and add new key to database. return false; default: throw new IllegalStateException(); } } } SoftwareAG-ganymed-ssh-2-ef7fbc4/examples/SingleThreadStdoutStderr.java 0000664 0000000 0000000 00000007674 13705512536 0026320 0 ustar 00root root 0000000 0000000 /* * Copyright (c) 2006-2011 Christian Plattner. All rights reserved. * Please refer to the LICENSE.txt for licensing details. */ import java.io.IOException; import java.io.InputStream; import ch.ethz.ssh2.ChannelCondition; import ch.ethz.ssh2.Connection; import ch.ethz.ssh2.Session; public class SingleThreadStdoutStderr { public static void main(String[] args) { String hostname = "127.0.0.1"; String username = "joe"; String password = "joespass"; try { /* Create a connection instance */ Connection conn = new Connection(hostname); /* Now connect */ conn.connect(); /* Authenticate */ boolean isAuthenticated = conn.authenticateWithPassword(username, password); if (isAuthenticated == false) throw new IOException("Authentication failed."); /* Create a session */ Session sess = conn.openSession(); sess.execCommand("echo \"Huge amounts of text on STDOUT\"; echo \"Huge amounts of text on STDERR\" >&2"); /* * Advanced: * The following is a demo on how one can read from stdout and * stderr without having to use two parallel worker threads (i.e., * we don't use the Streamgobblers here) and at the same time not * risking a deadlock (due to a filled SSH2 channel window, caused * by the stream which you are currently NOT reading from =). */ /* Don't wrap these streams and don't let other threads work on * these streams while you work with Session.waitForCondition()!!! */ InputStream stdout = sess.getStdout(); InputStream stderr = sess.getStderr(); byte[] buffer = new byte[8192]; while (true) { if ((stdout.available() == 0) && (stderr.available() == 0)) { /* Even though currently there is no data available, it may be that new data arrives * and the session's underlying channel is closed before we call waitForCondition(). * This means that EOF and STDOUT_DATA (or STDERR_DATA, or both) may * be set together. */ int conditions = sess.waitForCondition(ChannelCondition.STDOUT_DATA | ChannelCondition.STDERR_DATA | ChannelCondition.EOF, 2000); /* Wait no longer than 2 seconds (= 2000 milliseconds) */ if ((conditions & ChannelCondition.TIMEOUT) != 0) { /* A timeout occured. */ throw new IOException("Timeout while waiting for data from peer."); } /* Here we do not need to check separately for CLOSED, since CLOSED implies EOF */ if ((conditions & ChannelCondition.EOF) != 0) { /* The remote side won't send us further data... */ if ((conditions & (ChannelCondition.STDOUT_DATA | ChannelCondition.STDERR_DATA)) == 0) { /* ... and we have consumed all data in the local arrival window. */ break; } } /* OK, either STDOUT_DATA or STDERR_DATA (or both) is set. */ // You can be paranoid and check that the library is not going nuts: // if ((conditions & (ChannelCondition.STDOUT_DATA | ChannelCondition.STDERR_DATA)) == 0) // throw new IllegalStateException("Unexpected condition result (" + conditions + ")"); } /* If you below replace "while" with "if", then the way the output appears on the local * stdout and stder streams is more "balanced". Addtionally reducing the buffer size * will also improve the interleaving, but performance will slightly suffer. * OKOK, that all matters only if you get HUGE amounts of stdout and stderr data =) */ while (stdout.available() > 0) { int len = stdout.read(buffer); if (len > 0) // this check is somewhat paranoid System.out.write(buffer, 0, len); } while (stderr.available() > 0) { int len = stderr.read(buffer); if (len > 0) // this check is somewhat paranoid System.err.write(buffer, 0, len); } } /* Close this session */ sess.close(); /* Close the connection */ conn.close(); } catch (IOException e) { e.printStackTrace(System.err); System.exit(2); } } } SoftwareAG-ganymed-ssh-2-ef7fbc4/examples/StdoutAndStderr.java 0000664 0000000 0000000 00000003644 13705512536 0024442 0 ustar 00root root 0000000 0000000 /* * Copyright (c) 2006-2011 Christian Plattner. All rights reserved. * Please refer to the LICENSE.txt for licensing details. */ import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import ch.ethz.ssh2.Connection; import ch.ethz.ssh2.Session; import ch.ethz.ssh2.StreamGobbler; public class StdoutAndStderr { public static void main(String[] args) { String hostname = "127.0.0.1"; String username = "joe"; String password = "joespass"; try { /* Create a connection instance */ Connection conn = new Connection(hostname); /* Now connect */ conn.connect(); /* Authenticate */ boolean isAuthenticated = conn.authenticateWithPassword(username, password); if (isAuthenticated == false) throw new IOException("Authentication failed."); /* Create a session */ Session sess = conn.openSession(); sess.execCommand("echo \"Text on STDOUT\"; echo \"Text on STDERR\" >&2"); InputStream stdout = new StreamGobbler(sess.getStdout()); InputStream stderr = new StreamGobbler(sess.getStderr()); BufferedReader stdoutReader = new BufferedReader(new InputStreamReader(stdout)); BufferedReader stderrReader = new BufferedReader(new InputStreamReader(stderr)); System.out.println("Here is the output from stdout:"); while (true) { String line = stdoutReader.readLine(); if (line == null) { stdoutReader.close(); break; } System.out.println(line); } System.out.println("Here is the output from stderr:"); while (true) { String line = stderrReader.readLine(); if (line == null) { stderrReader.close(); break; } System.out.println(line); } /* Close this session */ sess.close(); /* Close the connection */ conn.close(); } catch (IOException e) { e.printStackTrace(System.err); System.exit(2); } } } SoftwareAG-ganymed-ssh-2-ef7fbc4/examples/SwingShell.java 0000664 0000000 0000000 00000043070 13705512536 0023425 0 ustar 00root root 0000000 0000000 /* * Copyright (c) 2006-2011 Christian Plattner. All rights reserved. * Please refer to the LICENSE.txt for licensing details. */ import java.awt.BorderLayout; import java.awt.Color; import java.awt.FlowLayout; import java.awt.Font; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.KeyAdapter; import java.awt.event.KeyEvent; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import javax.swing.BoxLayout; import javax.swing.JButton; import javax.swing.JDialog; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JPasswordField; import javax.swing.JTextArea; import javax.swing.JTextField; import javax.swing.SwingUtilities; import ch.ethz.ssh2.Connection; import ch.ethz.ssh2.InteractiveCallback; import ch.ethz.ssh2.KnownHosts; import ch.ethz.ssh2.ServerHostKeyVerifier; import ch.ethz.ssh2.Session; /** * * This is a very primitive SSH-2 dumb terminal (Swing based). * * The purpose of this class is to demonstrate: * * - Verifying server hostkeys with an existing known_hosts file * - Displaying fingerprints of server hostkeys * - Adding a server hostkey to a known_hosts file (+hashing the hostname for security) * - Authentication with DSA, RSA, password and keyboard-interactive methods * */ public class SwingShell { /* * NOTE: to get this feature to work, replace the "tilde" with your home directory, * at least my JVM does not understand it. Need to check the specs. */ static final String knownHostPath = "~/.ssh/known_hosts"; static final String idDSAPath = "~/.ssh/id_dsa"; static final String idRSAPath = "~/.ssh/id_rsa"; JFrame loginFrame = null; JLabel hostLabel; JLabel userLabel; JTextField hostField; JTextField userField; JButton loginButton; KnownHosts database = new KnownHosts(); public SwingShell() { File knownHostFile = new File(knownHostPath); if (knownHostFile.exists()) { try { database.addHostkeys(knownHostFile); } catch (IOException e) { } } } /** * This dialog displays a number of text lines and a text field. * The text field can either be plain text or a password field. */ class EnterSomethingDialog extends JDialog { private static final long serialVersionUID = 1L; JTextField answerField; JPasswordField passwordField; final boolean isPassword; String answer; public EnterSomethingDialog(JFrame parent, String title, String content, boolean isPassword) { this(parent, title, new String[] { content }, isPassword); } public EnterSomethingDialog(JFrame parent, String title, String[] content, boolean isPassword) { super(parent, title, true); this.isPassword = isPassword; JPanel pan = new JPanel(); pan.setLayout(new BoxLayout(pan, BoxLayout.Y_AXIS)); for (int i = 0; i < content.length; i++) { if ((content[i] == null) || (content[i] == "")) continue; JLabel contentLabel = new JLabel(content[i]); pan.add(contentLabel); } answerField = new JTextField(20); passwordField = new JPasswordField(20); if (isPassword) pan.add(passwordField); else pan.add(answerField); KeyAdapter kl = new KeyAdapter() { public void keyTyped(KeyEvent e) { if (e.getKeyChar() == '\n') finish(); } }; answerField.addKeyListener(kl); passwordField.addKeyListener(kl); getContentPane().add(BorderLayout.CENTER, pan); setResizable(false); pack(); setLocationRelativeTo(null); } private void finish() { if (isPassword) answer = new String(passwordField.getPassword()); else answer = answerField.getText(); dispose(); } } /** * TerminalDialog is probably the worst terminal emulator ever written - implementing * a real vt100 is left as an exercise to the reader, i.e., to you =) * */ class TerminalDialog extends JDialog { private static final long serialVersionUID = 1L; JPanel botPanel; JButton logoffButton; JTextArea terminalArea; Session sess; InputStream in; OutputStream out; int x, y; /** * This thread consumes output from the remote server and displays it in * the terminal window. * */ class RemoteConsumer extends Thread { char[][] lines = new char[y][]; int posy = 0; int posx = 0; private void addText(byte[] data, int len) { for (int i = 0; i < len; i++) { char c = (char) (data[i] & 0xff); if (c == 8) // Backspace, VERASE { if (posx < 0) continue; posx--; continue; } if (c == '\r') { posx = 0; continue; } if (c == '\n') { posy++; if (posy >= y) { for (int k = 1; k < y; k++) lines[k - 1] = lines[k]; posy--; lines[y - 1] = new char[x]; for (int k = 0; k < x; k++) lines[y - 1][k] = ' '; } continue; } if (c < 32) { continue; } if (posx >= x) { posx = 0; posy++; if (posy >= y) { posy--; for (int k = 1; k < y; k++) lines[k - 1] = lines[k]; lines[y - 1] = new char[x]; for (int k = 0; k < x; k++) lines[y - 1][k] = ' '; } } if (lines[posy] == null) { lines[posy] = new char[x]; for (int k = 0; k < x; k++) lines[posy][k] = ' '; } lines[posy][posx] = c; posx++; } StringBuffer sb = new StringBuffer(x * y); for (int i = 0; i < lines.length; i++) { if (i != 0) sb.append('\n'); if (lines[i] != null) { sb.append(lines[i]); } } setContent(sb.toString()); } public void run() { byte[] buff = new byte[8192]; try { while (true) { int len = in.read(buff); if (len == -1) return; addText(buff, len); } } catch (Exception e) { } } } public TerminalDialog(JFrame parent, String title, Session sess, int x, int y) throws IOException { super(parent, title, true); this.sess = sess; in = sess.getStdout(); out = sess.getStdin(); this.x = x; this.y = y; botPanel = new JPanel(new FlowLayout(FlowLayout.LEFT)); logoffButton = new JButton("Logout"); botPanel.add(logoffButton); logoffButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { /* Dispose the dialog, "setVisible(true)" method will return */ dispose(); } }); Font f = new Font("Monospaced", Font.PLAIN, 16); terminalArea = new JTextArea(y, x); terminalArea.setFont(f); terminalArea.setBackground(Color.BLACK); terminalArea.setForeground(Color.ORANGE); /* This is a hack. We cannot disable the caret, * since setting editable to false also changes * the meaning of the TAB key - and I want to use it in bash. * Again - this is a simple DEMO terminal =) */ terminalArea.setCaretColor(Color.BLACK); KeyAdapter kl = new KeyAdapter() { public void keyTyped(KeyEvent e) { int c = e.getKeyChar(); try { out.write(c); } catch (IOException e1) { } e.consume(); } }; terminalArea.addKeyListener(kl); getContentPane().add(terminalArea, BorderLayout.CENTER); getContentPane().add(botPanel, BorderLayout.PAGE_END); setResizable(false); pack(); setLocationRelativeTo(parent); new RemoteConsumer().start(); } public void setContent(String lines) { // setText is thread safe, it does not have to be called from // the Swing GUI thread. terminalArea.setText(lines); } } /** * This ServerHostKeyVerifier asks the user on how to proceed if a key cannot be found * in the in-memory database. * */ class AdvancedVerifier implements ServerHostKeyVerifier { public boolean verifyServerHostKey(String hostname, int port, String serverHostKeyAlgorithm, byte[] serverHostKey) throws Exception { final String host = hostname; final String algo = serverHostKeyAlgorithm; String message; /* Check database */ int result = database.verifyHostkey(hostname, serverHostKeyAlgorithm, serverHostKey); switch (result) { case KnownHosts.HOSTKEY_IS_OK: return true; case KnownHosts.HOSTKEY_IS_NEW: message = "Do you want to accept the hostkey (type " + algo + ") from " + host + " ?\n"; break; case KnownHosts.HOSTKEY_HAS_CHANGED: message = "WARNING! Hostkey for " + host + " has changed!\nAccept anyway?\n"; break; default: throw new IllegalStateException(); } /* Include the fingerprints in the message */ String hexFingerprint = KnownHosts.createHexFingerprint(serverHostKeyAlgorithm, serverHostKey); String bubblebabbleFingerprint = KnownHosts.createBubblebabbleFingerprint(serverHostKeyAlgorithm, serverHostKey); message += "Hex Fingerprint: " + hexFingerprint + "\nBubblebabble Fingerprint: " + bubblebabbleFingerprint; /* Now ask the user */ int choice = JOptionPane.showConfirmDialog(loginFrame, message); if (choice == JOptionPane.YES_OPTION) { /* Be really paranoid. We use a hashed hostname entry */ String hashedHostname = KnownHosts.createHashedHostname(hostname); /* Add the hostkey to the in-memory database */ database.addHostkey(new String[] { hashedHostname }, serverHostKeyAlgorithm, serverHostKey); /* Also try to add the key to a known_host file */ try { KnownHosts.addHostkeyToFile(new File(knownHostPath), new String[] { hashedHostname }, serverHostKeyAlgorithm, serverHostKey); } catch (IOException ignore) { } return true; } if (choice == JOptionPane.CANCEL_OPTION) { throw new Exception("The user aborted the server hostkey verification."); } return false; } } /** * The logic that one has to implement if "keyboard-interactive" autentication shall be * supported. * */ class InteractiveLogic implements InteractiveCallback { int promptCount = 0; String lastError; public InteractiveLogic(String lastError) { this.lastError = lastError; } /* the callback may be invoked several times, depending on how many questions-sets the server sends */ public String[] replyToChallenge(String name, String instruction, int numPrompts, String[] prompt, boolean[] echo) throws IOException { String[] result = new String[numPrompts]; for (int i = 0; i < numPrompts; i++) { /* Often, servers just send empty strings for "name" and "instruction" */ String[] content = new String[] { lastError, name, instruction, prompt[i] }; if (lastError != null) { /* show lastError only once */ lastError = null; } EnterSomethingDialog esd = new EnterSomethingDialog(loginFrame, "Keyboard Interactive Authentication", content, !echo[i]); esd.setVisible(true); if (esd.answer == null) throw new IOException("Login aborted by user"); result[i] = esd.answer; promptCount++; } return result; } /* We maintain a prompt counter - this enables the detection of situations where the ssh * server is signaling "authentication failed" even though it did not send a single prompt. */ public int getPromptCount() { return promptCount; } } /** * The SSH-2 connection is established in this thread. * If we would not use a separate thread (e.g., put this code in * the event handler of the "Login" button) then the GUI would not * be responsive (missing window repaints if you move the window etc.) */ class ConnectionThread extends Thread { String hostname; String username; public ConnectionThread(String hostname, String username) { this.hostname = hostname; this.username = username; } public void run() { Connection conn = new Connection(hostname); try { /* * * CONNECT AND VERIFY SERVER HOST KEY (with callback) * */ String[] hostkeyAlgos = database.getPreferredServerHostkeyAlgorithmOrder(hostname); if (hostkeyAlgos != null) conn.setServerHostKeyAlgorithms(hostkeyAlgos); conn.connect(new AdvancedVerifier()); /* * * AUTHENTICATION PHASE * */ boolean enableKeyboardInteractive = true; boolean enableDSA = true; boolean enableRSA = true; String lastError = null; while (true) { if ((enableDSA || enableRSA) && conn.isAuthMethodAvailable(username, "publickey")) { if (enableDSA) { File key = new File(idDSAPath); if (key.exists()) { EnterSomethingDialog esd = new EnterSomethingDialog(loginFrame, "DSA Authentication", new String[] { lastError, "Enter DSA private key password:" }, true); esd.setVisible(true); boolean res = conn.authenticateWithPublicKey(username, key, esd.answer); if (res == true) break; lastError = "DSA authentication failed."; } enableDSA = false; // do not try again } if (enableRSA) { File key = new File(idRSAPath); if (key.exists()) { EnterSomethingDialog esd = new EnterSomethingDialog(loginFrame, "RSA Authentication", new String[] { lastError, "Enter RSA private key password:" }, true); esd.setVisible(true); boolean res = conn.authenticateWithPublicKey(username, key, esd.answer); if (res == true) break; lastError = "RSA authentication failed."; } enableRSA = false; // do not try again } continue; } if (enableKeyboardInteractive && conn.isAuthMethodAvailable(username, "keyboard-interactive")) { InteractiveLogic il = new InteractiveLogic(lastError); boolean res = conn.authenticateWithKeyboardInteractive(username, il); if (res == true) break; if (il.getPromptCount() == 0) { // aha. the server announced that it supports "keyboard-interactive", but when // we asked for it, it just denied the request without sending us any prompt. // That happens with some server versions/configurations. // We just disable the "keyboard-interactive" method and notify the user. lastError = "Keyboard-interactive does not work."; enableKeyboardInteractive = false; // do not try this again } else { lastError = "Keyboard-interactive auth failed."; // try again, if possible } continue; } if (conn.isAuthMethodAvailable(username, "password")) { final EnterSomethingDialog esd = new EnterSomethingDialog(loginFrame, "Password Authentication", new String[] { lastError, "Enter password for " + username }, true); esd.setVisible(true); if (esd.answer == null) throw new IOException("Login aborted by user"); boolean res = conn.authenticateWithPassword(username, esd.answer); if (res == true) break; lastError = "Password authentication failed."; // try again, if possible continue; } throw new IOException("No supported authentication methods available."); } /* * * AUTHENTICATION OK. DO SOMETHING. * */ Session sess = conn.openSession(); int x_width = 90; int y_width = 30; sess.requestPTY("dumb", x_width, y_width, 0, 0, null); sess.startShell(); TerminalDialog td = new TerminalDialog(loginFrame, username + "@" + hostname, sess, x_width, y_width); /* The following call blocks until the dialog has been closed */ td.setVisible(true); } catch (IOException e) { //e.printStackTrace(); JOptionPane.showMessageDialog(loginFrame, "Exception: " + e.getMessage()); } /* * * CLOSE THE CONNECTION. * */ conn.close(); /* * * CLOSE THE LOGIN FRAME - APPLICATION WILL BE EXITED (no more frames) * */ Runnable r = new Runnable() { public void run() { loginFrame.dispose(); } }; SwingUtilities.invokeLater(r); } } void loginPressed() { String hostname = hostField.getText().trim(); String username = userField.getText().trim(); if ((hostname.length() == 0) || (username.length() == 0)) { JOptionPane.showMessageDialog(loginFrame, "Please fill out both fields!"); return; } loginButton.setEnabled(false); hostField.setEnabled(false); userField.setEnabled(false); ConnectionThread ct = new ConnectionThread(hostname, username); ct.start(); } void showGUI() { loginFrame = new JFrame("Ganymed SSH2 SwingShell"); hostLabel = new JLabel("Hostname:"); userLabel = new JLabel("Username:"); hostField = new JTextField("", 20); userField = new JTextField("", 10); loginButton = new JButton("Login"); loginButton.addActionListener(new ActionListener() { public void actionPerformed(java.awt.event.ActionEvent e) { loginPressed(); } }); JPanel loginPanel = new JPanel(); loginPanel.add(hostLabel); loginPanel.add(hostField); loginPanel.add(userLabel); loginPanel.add(userField); loginPanel.add(loginButton); loginFrame.getRootPane().setDefaultButton(loginButton); loginFrame.getContentPane().add(loginPanel, BorderLayout.PAGE_START); //loginFrame.getContentPane().add(textArea, BorderLayout.CENTER); loginFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); loginFrame.pack(); loginFrame.setResizable(false); loginFrame.setLocationRelativeTo(null); loginFrame.setVisible(true); } void startGUI() { Runnable r = new Runnable() { public void run() { showGUI(); } }; SwingUtilities.invokeLater(r); } public static void main(String[] args) { SwingShell client = new SwingShell(); client.startGUI(); } } SoftwareAG-ganymed-ssh-2-ef7fbc4/examples/UsingKnownHosts.java 0000664 0000000 0000000 00000003527 13705512536 0024474 0 ustar 00root root 0000000 0000000 /* * Copyright (c) 2006-2011 Christian Plattner. All rights reserved. * Please refer to the LICENSE.txt for licensing details. */ import java.io.BufferedReader; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import ch.ethz.ssh2.Connection; import ch.ethz.ssh2.KnownHosts; import ch.ethz.ssh2.Session; import ch.ethz.ssh2.StreamGobbler; public class UsingKnownHosts { static KnownHosts database = new KnownHosts(); public static void main(String[] args) throws IOException { String hostname = "somehost"; String username = "joe"; String password = "joespass"; File knownHosts = new File("~/.ssh/known_hosts"); try { /* Load known_hosts file into in-memory database */ if (knownHosts.exists()) database.addHostkeys(knownHosts); /* Create a connection instance */ Connection conn = new Connection(hostname); /* Now connect and use the SimpleVerifier */ conn.connect(new SimpleVerifier(database)); /* Authenticate */ boolean isAuthenticated = conn.authenticateWithPassword(username, password); if (isAuthenticated == false) throw new IOException("Authentication failed."); /* Create a session */ Session sess = conn.openSession(); sess.execCommand("uname -a && date && uptime && who"); InputStream stdout = new StreamGobbler(sess.getStdout()); BufferedReader br = new BufferedReader(new InputStreamReader(stdout)); System.out.println("Here is some information about the remote host:"); while (true) { String line = br.readLine(); if (line == null) { br.close(); break; } System.out.println(line); } /* Close this session */ sess.close(); /* Close the connection */ conn.close(); } catch (IOException e) { e.printStackTrace(System.err); System.exit(2); } } } SoftwareAG-ganymed-ssh-2-ef7fbc4/overview.html 0000664 0000000 0000000 00000001400 13705512536 0021410 0 ustar 00root root 0000000 0000000 Ganymed SSH2 for Java is a library which implements the SSH-2 protocol in pure Java. It allows one to connect to SSH servers from within Java programs. It supports SSH sessions (remote command execution and shell access), local and remote port forwarding, local stream forwarding, X11 forwarding, SCP and SFTP. There are no dependencies on any JCE provider, as all crypto functionality is included.Ganymed SSH2 for Java was first developed for the Ganymed replication project and a couple of other projects at the IKS group at ETH Zurich.
Website: https://code.google.com/p/ganymed-ssh-2/.
Please read the included LICENCE.txt, latest changes can be found in HISTORY.txt.