All Downloads are FREE. Search and download functionalities are using the official Maven repository.

ch.ethz.ssh2.ServerConnection Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (c) 2012-2013 Christian Plattner. All rights reserved.
 * Please refer to the LICENSE.txt for licensing details.
 */

package ch.ethz.ssh2;

import java.io.CharArrayWriter;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.net.Socket;

import ch.ethz.ssh2.crypto.CryptoWishList;
import ch.ethz.ssh2.crypto.PEMDecoder;
import ch.ethz.ssh2.server.ServerConnectionState;
import ch.ethz.ssh2.signature.DSAPrivateKey;
import ch.ethz.ssh2.signature.RSAPrivateKey;
import ch.ethz.ssh2.transport.ServerTransportManager;

/**
 * A server-side SSH-2 connection.
 *
 * @author Christian
 *
 */
public class ServerConnection
{
	/**
	 * The softwareversion presented to the SSH-2 client.
	 */
	private String softwareversion = String.format("Ganymed_SSHD_%s", Version.getSpecification());

	private final ServerConnectionState state = new ServerConnectionState(this);

	/**
	 * Creates a new ServerConnection that will communicate
	 * with the client over the given Socket.
	 * 

* Note: you need to call {@link #connect()} or {@link #connect(int)} to * perform the initial handshake and establish the encrypted communication. * * @see #connect(int) * * @param s The socket */ public ServerConnection(Socket s) { this(s, null, null); } public ServerConnection(Socket s, String softwareversion) { this(s, null, null); this.softwareversion = softwareversion; } /** * Creates a new ServerConnection that will communicate * with the client over the given Socket. *

* Note: you need to call {@link #connect()} or {@link #connect(int)} to * perform the initial handshake and establish the encrypted communication. *

* Please read the javadoc for the {@link #connect(int)} method. * * @see #connect(int) * * @param s The socket * @param dsa_key The DSA hostkey, may be NULL * @param rsa_key The RSA hostkey, may be NULL */ public ServerConnection(Socket s, DSAPrivateKey dsa_key, RSAPrivateKey rsa_key) { state.s = s; state.softwareversion = softwareversion; state.next_dsa_key = dsa_key; state.next_rsa_key = rsa_key; fixCryptoWishList(state.next_cryptoWishList, state.next_dsa_key, state.next_rsa_key); } /** * Establish the connection and block until the first handshake has completed. *

* Note: this is a wrapper that calls connect(0) (i.e., connect with no timeout). *

* Please read the javadoc for the {@link #connect(int)} method. * * @see #connect(int) * * @throws IOException */ public synchronized void connect() throws IOException { connect(0); } /** * Establish the connection and block until the first handshake has completed. *

* Note 1: either a DSA or a RSA (or both) hostkey must be set before calling this method. *

* Note 2: You must set the callbacks for authentication ({@link #setAuthenticationCallback(ServerAuthenticationCallback)}) * and connection events ({@link #setServerConnectionCallback(ServerConnectionCallback)}). * * @see #setPEMHostKey(char[], String) * @see #setPEMHostKey(File, String) * @see #setRsaHostKey(RSAPrivateKey) * @see #setDsaHostKey(DSAPrivateKey) * * @param timeout_milliseconds Timeout in milliseconds, 0 means no timeout. * @throws IOException */ public synchronized void connect(int timeout_milliseconds) throws IOException { synchronized (state) { if (state.cb_conn == null) throw new IllegalStateException("The callback for connection events has not been set."); if (state.cb_auth == null) throw new IllegalStateException("The callback for authentication events has not been set."); if (state.tm != null) throw new IllegalStateException("The initial handshake has already been started."); if ((state.next_dsa_key == null) && (state.next_rsa_key == null)) throw new IllegalStateException("Neither a RSA nor a DSA host key has been specified!"); state.tm = new ServerTransportManager(state.s); } state.tm.connect(state); /* Wait until first KEX has finished */ state.tm.getConnectionInfo(1); } /** * Retrieve the underlying socket. * * @return the socket that has been passed to the constructor. */ public Socket getSocket() { return state.s; } /** * Force an asynchronous key re-exchange (the call does not block). The * latest values set for MAC, Cipher and DH group exchange parameters will * be used. If a key exchange is currently in progress, then this method has * the only effect that the so far specified parameters will be used for the * next (client driven) key exchange. You may call this method only after * the initial key exchange has been established. *

* Note: This implementation will never start automatically a key exchange (other than the initial one) * unless you or the connected SSH-2 client ask for it. * * @throws IOException * In case of any failure behind the scenes. */ public synchronized void forceKeyExchange() throws IOException { synchronized (state) { if (state.tm == null) throw new IllegalStateException( "Cannot force another key exchange, you need to start the key exchange first."); state.tm.forceKeyExchange(state.next_cryptoWishList, null, state.next_dsa_key, state.next_rsa_key); } } /** * Returns a {@link ConnectionInfo} object containing the details of * the connection. May be called as soon as the first key exchange has been * started. The method blocks in case the first key exchange has not been completed. *

* Note: upon return of this method, authentication may still be pending. * * @return A {@link ConnectionInfo} object. * @throws IOException * In case of any failure behind the scenes; e.g., first key exchange was aborted. */ public synchronized ConnectionInfo getConnectionInfo() throws IOException { synchronized (state) { if (state.tm == null) throw new IllegalStateException( "Cannot get details of connection, you need to start the key exchange first."); } return state.tm.getConnectionInfo(1); } /** * Change the current DSA hostkey. Either a DSA or RSA private key must be set for a successful handshake with * the client. *

* Note: You can change an existing DSA hostkey after the initial kex exchange (the new value will * be used during the next server initiated key exchange), but you cannot remove (i.e., set to null) the * current DSA key, otherwise the next key exchange may fail in case the client supports only DSA hostkeys. * * @param dsa_hostkey */ public synchronized void setDsaHostKey(DSAPrivateKey dsa_hostkey) { synchronized (state) { if ((dsa_hostkey == null) && (state.next_dsa_key != null) && (state.tm != null)) throw new IllegalStateException("Cannot remove DSA hostkey after first key exchange."); state.next_dsa_key = dsa_hostkey; fixCryptoWishList(state.next_cryptoWishList, state.next_dsa_key, state.next_rsa_key); } } /** * Change the current RSA hostkey. Either a DSA or RSA private key must be set for a successful handshake with * the client. *

* Note: You can change an existing RSA hostkey after the initial kex exchange (the new value will * be used during the next server initiated key exchange), but you cannot remove (i.e., set to null) the * current RSA key, otherwise the next key exchange may fail in case the client supports only RSA hostkeys. * * @param rsa_hostkey */ public synchronized void setRsaHostKey(RSAPrivateKey rsa_hostkey) { synchronized (state) { if ((rsa_hostkey == null) && (state.next_rsa_key != null) && (state.tm != null)) throw new IllegalStateException("Cannot remove RSA hostkey after first key exchange."); state.next_rsa_key = rsa_hostkey; fixCryptoWishList(state.next_cryptoWishList, state.next_dsa_key, state.next_rsa_key); } } /** * Utility method that loads a PEM based hostkey (either RSA or DSA based) and * calls either setRsaHostKey() or setDsaHostKey(). * * @param pemdata The PEM data * @param password Password, may be null in case the PEM data is not password protected * @throws IOException In case of any error. */ public void setPEMHostKey(char[] pemdata, String password) throws IOException { Object key = PEMDecoder.decode(pemdata, password); if (key instanceof DSAPrivateKey) setDsaHostKey((DSAPrivateKey) key); if (key instanceof RSAPrivateKey) setRsaHostKey((RSAPrivateKey) key); } /** * Utility method that loads a hostkey from a PEM file (either RSA or DSA based) and * calls either setRsaHostKey() or setDsaHostKey(). * * @param pemFile The PEM file * @param password Password, may be null in case the PEM file is not password protected * @throws IOException */ public void setPEMHostKey(File pemFile, String password) throws IOException { if (pemFile == null) throw new IllegalArgumentException("pemfile argument is null"); char[] buff = new char[256]; CharArrayWriter cw = new CharArrayWriter(); FileReader fr = new FileReader(pemFile); while (true) { int len = fr.read(buff); if (len < 0) break; cw.write(buff, 0, len); } fr.close(); setPEMHostKey(cw.toCharArray(), password); } private void fixCryptoWishList(CryptoWishList next_cryptoWishList, DSAPrivateKey next_dsa_key, RSAPrivateKey next_rsa_key) { if ((next_dsa_key != null) && (next_rsa_key != null)) next_cryptoWishList.serverHostKeyAlgorithms = new String[] { "ssh-rsa", "ssh-dss" }; else if (next_dsa_key != null) next_cryptoWishList.serverHostKeyAlgorithms = new String[] { "ssh-dss" }; else if (next_rsa_key != null) next_cryptoWishList.serverHostKeyAlgorithms = new String[] { "ssh-rsa" }; else next_cryptoWishList.serverHostKeyAlgorithms = new String[0]; } /** * Callback interface with methods that will be called upon events * generated by the client (e.g., client opens a new Session which results in a ServerSession). *

* Note: This must be set before the first handshake. * * @param cb The callback implementation */ public synchronized void setServerConnectionCallback(ServerConnectionCallback cb) { synchronized (state) { state.cb_conn = cb; } } /** * Callback interface with methods that will be called upon authentication events. *

* Note: This must be set before the first handshake. * * @param cb The callback implementation */ public synchronized void setAuthenticationCallback(ServerAuthenticationCallback cb) { synchronized (state) { state.cb_auth = cb; } } /** * Close the connection to the SSH-2 server. All assigned sessions will be * closed, too. Can be called at any time. Don't forget to call this once * you don't need a connection anymore - otherwise the receiver thread may * run forever. */ public void close() { Throwable t = new Throwable("Closed due to user request."); close(t, false); } public void close(Throwable t, boolean hard) { synchronized (state) { if (state.cm != null) state.cm.closeAllChannels(); if (state.tm != null) { state.tm.close(t, hard == false); } } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy