/** * Creates a new SshMsgKexInit object. * * @param props */ public SshMsgKexInit(SshConnectionProperties props) { super(SSH_MSG_KEX_INIT); // Create some random data cookie = new byte[16]; // Seed the random number generator Random r = ConfigurationLoader.getRND(); // Get the next random bytes into our cookie r.nextBytes(cookie); // Get the supported algorithms from the factory objects but adding the // preffered algorithm to the top of the list supportedKex = sortAlgorithmList(SshKeyExchangeFactory.getSupportedKeyExchanges(), props.getPrefKex()); supportedPK = sortAlgorithmList(SshKeyPairFactory.getSupportedKeys(), props.getPrefPublicKey()); supportedEncryptCS = sortAlgorithmList(SshCipherFactory.getSupportedCiphers(), props.getPrefCSEncryption()); supportedEncryptSC = sortAlgorithmList(SshCipherFactory.getSupportedCiphers(), props.getPrefSCEncryption()); supportedMacCS = sortAlgorithmList(SshHmacFactory.getSupportedMacs(), props.getPrefCSMac()); supportedMacSC = sortAlgorithmList(SshHmacFactory.getSupportedMacs(), props.getPrefSCMac()); supportedCompCS = sortAlgorithmList(SshCompressionFactory.getSupportedCompression(), props.getPrefCSComp()); supportedCompSC = sortAlgorithmList(SshCompressionFactory.getSupportedCompression(), props.getPrefSCComp()); // We currently don't support language preferences supportedLangCS = new ArrayList(); supportedLangSC = new ArrayList(); // We don't guess (I don't see the point of this in the protocol!) firstKexFollows = false; }
static { String val = ConfigurationLoader.checkAndGetProperty("user.home", null); if (val != null) { PREF_DIR = new File(val + File.separator + ".sshterm"); } }
public static final void main(String[] args) { try { /** * We use log4j as our logging implementation as it rules but lets not fail if it aint around * so we ! */ if (System.getProperty("log4j.properties") != null) { try { Properties properties = new Properties(); properties.load(ConfigurationLoader.loadFile(System.getProperty("log4j.properties"))); try { Class cls = Class.forName("org.apache.log4j.PropertyConfigurator"); Object obj = cls.newInstance(); Method method = cls.getMethod("configure", new Class[] {Properties.class}); method.invoke(obj, new Object[] {properties}); System.out.println(System.getProperty("log4j.properties")); } catch (Throwable ex) { } } catch (IOException ex) { configureBasicLogging(); } } else { configureBasicLogging(); } XmlConfigurationContext context = new XmlConfigurationContext(); context.setAutomationConfigurationResource("automation.xml"); context.setFailOnError(false); ConfigurationLoader.initialize(false, context); SshTerm term = new SshTerm(); term.init(args); term.newContainer(); } catch (Exception e) { e.printStackTrace(); JOptionPane.showMessageDialog(null, e.getMessage(), "Error", JOptionPane.ERROR_MESSAGE); System.exit(0); } }
/** * @param msg * @param sender * @throws IOException * @throws TransportProtocolException */ public synchronized void sendMessage(SshMessage msg, Object sender) throws IOException { // Send a message, if were in key exchange then add it to // the list unless of course it is a transport protocol or key // exchange message if (log.isDebugEnabled()) { log.info("Sending " + msg.getMessageName()); } int currentState = state.getValue(); if (sender instanceof SshKeyExchange || sender instanceof TransportProtocolCommon || (currentState == TransportProtocolState.CONNECTED)) { sshOut.sendMessage(msg); if (currentState == TransportProtocolState.CONNECTED) { if (sendIgnore) { byte[] count = new byte[1]; ConfigurationLoader.getRND().nextBytes(count); byte[] rand = new byte[(count[0] & 0xFF) + 1]; ConfigurationLoader.getRND().nextBytes(rand); SshMsgIgnore ignore = new SshMsgIgnore(new String(rand)); if (log.isDebugEnabled()) { log.debug("Sending " + ignore.getMessageName()); } sshOut.sendMessage(ignore); } } } else if (currentState == TransportProtocolState.PERFORMING_KEYEXCHANGE) { log.debug("Adding to message queue whilst in key exchange"); synchronized (messageStack) { // Add this message to the end of the list messageStack.add(msg); } } else { throw new TransportProtocolException("The transport protocol is disconnected"); } }
/** Creates a new Main object. */ public Main() { super("ssh-keygen"); /* try { ConfigurationLoader.setLogfile(ConfigurationLoader.getHomeDirectory() + "logs/ssh-keygen.log"); } catch (IOException ex) { }*/ try { ConfigurationLoader.initialize(false); } catch (ConfigurationException ex) { } // Set the frame icon setIconImage(new ResourceIcon(ICON).getImage()); // keygen = new KeygenPanel(); // Create the center banner panel IconWrapperPanel centerPanel = new IconWrapperPanel(new ResourceIcon(ICON), keygen); centerPanel.setBorder(BorderFactory.createEmptyBorder(4, 4, 4, 4)); // Button panel JPanel buttonPanel = new JPanel(new GridBagLayout()); GridBagConstraints gbc = new GridBagConstraints(); gbc.fill = GridBagConstraints.HORIZONTAL; gbc.anchor = GridBagConstraints.CENTER; gbc.insets = new Insets(6, 6, 0, 0); gbc.weighty = 1.0; generate = new JButton("Generate"); generate.addActionListener(this); generate.setMnemonic('g'); this.getRootPane().setDefaultButton(generate); UIUtil.jGridBagAdd(buttonPanel, generate, gbc, GridBagConstraints.RELATIVE); close = new JButton("Close"); close.addActionListener(this); close.setMnemonic('c'); UIUtil.jGridBagAdd(buttonPanel, close, gbc, GridBagConstraints.REMAINDER); JPanel southPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT, 0, 0)); southPanel.add(buttonPanel); // Wrap the whole thing in an empty border JPanel mainPanel = new JPanel(new BorderLayout()); mainPanel.setBorder(BorderFactory.createEmptyBorder(4, 4, 4, 4)); mainPanel.add(centerPanel, BorderLayout.CENTER); mainPanel.add(southPanel, BorderLayout.SOUTH); // Build the main panel getContentPane().setLayout(new GridLayout(1, 1)); getContentPane().add(mainPanel); }
/** * @author $author$ * @version $Revision: 1.2 $ */ public abstract class TransportProtocolCommon implements TransportProtocol, Runnable { // Flag to keep on running // private boolean keepRunning = true; /** */ protected static Log log = LogFactory.getLog(TransportProtocolCommon.class); private static int nextThreadNo = 1; /** */ public static final int EOL_CRLF = 1; /** */ public static final int EOL_LF = 2; /** */ public static final String PROTOCOL_VERSION = "2.0"; /** */ public static String SOFTWARE_VERSION_COMMENTS = "http://www.sshtools.com " + ConfigurationLoader.getVersionString("J2SSH", "j2ssh.properties"); private int threadNo = nextThreadNo++; /** */ protected BigInteger k = null; /** */ protected Boolean completeOnNewKeys = new Boolean(false); /** */ protected HostKeyVerification hosts; /** */ protected Map kexs = new HashMap(); private boolean sendIgnore = false; // protected Map transportMessages = new HashMap(); /** */ protected SshConnectionProperties properties; /** */ protected SshMessageStore messageStore = new SshMessageStore(); /** */ protected SshMsgKexInit clientKexInit = null; /** */ protected SshMsgKexInit serverKexInit = null; /** */ protected String clientIdent = null; /** */ protected String serverIdent = null; /** */ protected TransportProtocolAlgorithmSync algorithmsIn; /** */ protected TransportProtocolAlgorithmSync algorithmsOut; /** */ protected TransportProtocolState state = new TransportProtocolState(); private byte[] exchangeHash = null; /** */ protected byte[] sessionIdentifier = null; /** */ protected byte[] hostKey = null; /** */ protected byte[] signature = null; private Vector eventHandlers = new Vector(); // Storage of messages whilst in key exchange private List messageStack = new ArrayList(); // Message notification registry private Map messageNotifications = new HashMap(); // Key exchange lock for accessing the kex init messages private Object kexLock = new Object(); // Object to synchronize key changing private Object keyLock = new Object(); // The connected socket // private Socket socket; // The underlying transport provider TransportProvider provider; // The thread object private SshThread thread; private long kexTimeout = 3600000L; // private long kexTransferLimitKB = 100L; // 100 K private long kexTransferLimitKB = 1073741824L / 1024L; // private long kexTransferLimit = 1073741824L; private long startTime = System.currentTimeMillis(); private long transferredKB = 0; private long lastTriggeredKB = 0; // The input stream for recieving data /** */ protected TransportProtocolInputStream sshIn; // The output stream for sending data /** */ protected TransportProtocolOutputStream sshOut; private int remoteEOL = EOL_CRLF; // private Map registeredMessages = new HashMap(); private Vector messageStores = new Vector(); /** Creates a new TransportProtocolCommon object. */ public TransportProtocolCommon() {} /** @return */ public int getConnectionId() { return threadNo; } /** @return */ public int getRemoteEOL() { return remoteEOL; } /** @return */ public TransportProtocolState getState() { return state; } /** @return */ public SshConnectionProperties getProperties() { return properties; } /** */ protected abstract void onDisconnect(); /** @param description */ public void disconnect(String description) { if (log.isDebugEnabled()) { log.debug("Disconnect: " + description); } try { state.setValue(TransportProtocolState.DISCONNECTED); state.setDisconnectReason(description); // Send the disconnect message automatically sendDisconnect(SshMsgDisconnect.BY_APPLICATION, description); } catch (Exception e) { log.warn("Failed to send disconnect", e); } } /** @param sendIgnore */ public void setSendIgnore(boolean sendIgnore) { this.sendIgnore = sendIgnore; } /** * @param seconds * @throws TransportProtocolException */ public void setKexTimeout(long seconds) throws TransportProtocolException { if (seconds < 60) { throw new TransportProtocolException("Keys can only be re-exchanged every minute or more"); } kexTimeout = seconds * 1000; } /** * @param kilobytes * @throws TransportProtocolException */ public void setKexTransferLimit(long kilobytes) throws TransportProtocolException { if (kilobytes < 10) { throw new TransportProtocolException( "Keys can only be re-exchanged after every 10k of data, or more"); } // kexTransferLimit = kilobytes * 1024; kexTransferLimitKB = kilobytes; } /*public InetSocketAddress getRemoteAddress() { return (InetSocketAddress)socket.getRemoteSocketAddress(); }*/ public long getOutgoingByteCount() { return sshOut.getNumBytesTransfered(); } /** @return */ public long getIncomingByteCount() { return sshIn.getNumBytesTransfered(); } /** @param eventHandler */ public void addEventHandler(TransportProtocolEventHandler eventHandler) { if (eventHandler != null) { eventHandlers.add(eventHandler); } } /** @throws MessageAlreadyRegisteredException */ public abstract void registerTransportMessages() throws MessageAlreadyRegisteredException; /** @return */ public byte[] getSessionIdentifier() { return (byte[]) sessionIdentifier.clone(); } /** */ public void run() { try { state.setValue(TransportProtocolState.NEGOTIATING_PROTOCOL); log.info("Registering transport protocol messages with inputstream"); algorithmsOut = new TransportProtocolAlgorithmSync(); algorithmsIn = new TransportProtocolAlgorithmSync(); // Create the input/output streams sshIn = new TransportProtocolInputStream(this, provider.getInputStream(), algorithmsIn); sshOut = new TransportProtocolOutputStream(provider.getOutputStream(), this, algorithmsOut); // Register the transport layer messages that this class will handle messageStore.registerMessage(SshMsgDisconnect.SSH_MSG_DISCONNECT, SshMsgDisconnect.class); messageStore.registerMessage(SshMsgIgnore.SSH_MSG_IGNORE, SshMsgIgnore.class); messageStore.registerMessage( SshMsgUnimplemented.SSH_MSG_UNIMPLEMENTED, SshMsgUnimplemented.class); messageStore.registerMessage(SshMsgDebug.SSH_MSG_DEBUG, SshMsgDebug.class); messageStore.registerMessage(SshMsgKexInit.SSH_MSG_KEX_INIT, SshMsgKexInit.class); messageStore.registerMessage(SshMsgNewKeys.SSH_MSG_NEWKEYS, SshMsgNewKeys.class); registerTransportMessages(); List list = SshKeyExchangeFactory.getSupportedKeyExchanges(); Iterator it = list.iterator(); while (it.hasNext()) { String keyExchange = (String) it.next(); SshKeyExchange kex = SshKeyExchangeFactory.newInstance(keyExchange); kex.init(this); kexs.put(keyExchange, kex); } // call abstract to initialise the local ident string setLocalIdent(); // negotiate the protocol version negotiateVersion(); startBinaryPacketProtocol(); } catch (Throwable e) { if (e instanceof IOException) { state.setLastError((IOException) e); } if (state.getValue() != TransportProtocolState.DISCONNECTED) { log.error("The Transport Protocol thread failed", e); // log.info(e.getMessage()); stop(); } } finally { thread = null; } log.debug("The Transport Protocol has been stopped"); } /** * @param msg * @param sender * @throws IOException * @throws TransportProtocolException */ public synchronized void sendMessage(SshMessage msg, Object sender) throws IOException { // Send a message, if were in key exchange then add it to // the list unless of course it is a transport protocol or key // exchange message if (log.isDebugEnabled()) { log.info("Sending " + msg.getMessageName()); } int currentState = state.getValue(); if (sender instanceof SshKeyExchange || sender instanceof TransportProtocolCommon || (currentState == TransportProtocolState.CONNECTED)) { sshOut.sendMessage(msg); if (currentState == TransportProtocolState.CONNECTED) { if (sendIgnore) { byte[] count = new byte[1]; ConfigurationLoader.getRND().nextBytes(count); byte[] rand = new byte[(count[0] & 0xFF) + 1]; ConfigurationLoader.getRND().nextBytes(rand); SshMsgIgnore ignore = new SshMsgIgnore(new String(rand)); if (log.isDebugEnabled()) { log.debug("Sending " + ignore.getMessageName()); } sshOut.sendMessage(ignore); } } } else if (currentState == TransportProtocolState.PERFORMING_KEYEXCHANGE) { log.debug("Adding to message queue whilst in key exchange"); synchronized (messageStack) { // Add this message to the end of the list messageStack.add(msg); } } else { throw new TransportProtocolException("The transport protocol is disconnected"); } } /** @throws IOException */ protected abstract void onStartTransportProtocol() throws IOException; /** * @param provider * @param properties * @throws IOException */ public void startTransportProtocol(TransportProvider provider, SshConnectionProperties properties) throws IOException { // Save the connected socket for later use this.provider = provider; this.properties = properties; // Start the transport layer message loop log.info("Starting transport protocol"); thread = new SshThread(this, "Transport protocol", true); thread.start(); onStartTransportProtocol(); } /** @return */ public String getUnderlyingProviderDetail() { return provider.getProviderDetail(); } /** * @param messageId * @param store * @throws MessageNotRegisteredException */ public void unregisterMessage(Integer messageId, SshMessageStore store) throws MessageNotRegisteredException { if (log.isDebugEnabled()) { log.debug("Unregistering message Id " + messageId.toString()); } if (!messageNotifications.containsKey(messageId)) { throw new MessageNotRegisteredException(messageId); } SshMessageStore actual = (SshMessageStore) messageNotifications.get(messageId); if (!store.equals(actual)) { throw new MessageNotRegisteredException(messageId, store); } messageNotifications.remove(messageId); } /** * @return * @throws AlgorithmNotAgreedException */ protected abstract String getDecryptionAlgorithm() throws AlgorithmNotAgreedException; /** * @return * @throws AlgorithmNotAgreedException */ protected abstract String getEncryptionAlgorithm() throws AlgorithmNotAgreedException; /** * @return * @throws AlgorithmNotAgreedException */ protected abstract String getInputStreamCompAlgortihm() throws AlgorithmNotAgreedException; /** * @return * @throws AlgorithmNotAgreedException */ protected abstract String getInputStreamMacAlgorithm() throws AlgorithmNotAgreedException; /** */ protected abstract void setLocalIdent(); /** @return */ public abstract String getLocalId(); /** @param msg */ protected abstract void setLocalKexInit(SshMsgKexInit msg); /** @return */ protected abstract SshMsgKexInit getLocalKexInit(); /** * @return * @throws AlgorithmNotAgreedException */ protected abstract String getOutputStreamCompAlgorithm() throws AlgorithmNotAgreedException; /** * @return * @throws AlgorithmNotAgreedException */ protected abstract String getOutputStreamMacAlgorithm() throws AlgorithmNotAgreedException; /** @param ident */ protected abstract void setRemoteIdent(String ident); /** @return */ public abstract String getRemoteId(); /** @param msg */ protected abstract void setRemoteKexInit(SshMsgKexInit msg); /** @return */ protected abstract SshMsgKexInit getRemoteKexInit(); /** * @param kex * @throws IOException * @throws KeyExchangeException */ protected abstract void performKeyExchange(SshKeyExchange kex) throws IOException, KeyExchangeException; /** * @return * @throws AlgorithmNotAgreedException */ protected String getKexAlgorithm() throws AlgorithmNotAgreedException { return determineAlgorithm(clientKexInit.getSupportedKex(), serverKexInit.getSupportedKex()); } public boolean isConnected() { return (state.getValue() == TransportProtocolState.CONNECTED) || (state.getValue() == TransportProtocolState.PERFORMING_KEYEXCHANGE); } /** * @throws IOException * @throws KeyExchangeException */ protected void beginKeyExchange() throws IOException, KeyExchangeException { log.info("Starting key exchange"); // state.setValue(TransportProtocolState.PERFORMING_KEYEXCHANGE); String kexAlgorithm = ""; // We now have both kex inits, this is where client/server // implemtations take over so call abstract methods try { // Determine the key exchange algorithm kexAlgorithm = getKexAlgorithm(); if (log.isDebugEnabled()) { log.debug("Key exchange algorithm: " + kexAlgorithm); } // Get an instance of the key exchange algortihm SshKeyExchange kex = (SshKeyExchange) kexs.get(kexAlgorithm); // Do the key exchange performKeyExchange(kex); // Record the output exchangeHash = kex.getExchangeHash(); if (sessionIdentifier == null) { sessionIdentifier = new byte[exchangeHash.length]; System.arraycopy(exchangeHash, 0, sessionIdentifier, 0, sessionIdentifier.length); thread.setSessionId(sessionIdentifier); } hostKey = kex.getHostKey(); signature = kex.getSignature(); k = kex.getSecret(); // Send new keys sendNewKeys(); kex.reset(); } catch (AlgorithmNotAgreedException e) { sendDisconnect( SshMsgDisconnect.KEY_EXCHANGE_FAILED, "No suitable key exchange algorithm was agreed"); throw new KeyExchangeException("No suitable key exchange algorithm could be agreed."); } } /** * @return * @throws IOException */ protected SshMsgKexInit createLocalKexInit() throws IOException { return new SshMsgKexInit(properties); } /** */ protected void onCorruptMac() { log.fatal("Corrupt Mac on Input"); // Send a disconnect message sendDisconnect( SshMsgDisconnect.MAC_ERROR, "Corrupt Mac on input", new SshException("Corrupt Mac on Imput")); } /** * @param msg * @throws IOException */ protected abstract void onMessageReceived(SshMessage msg) throws IOException; /** * @param reason * @param description */ protected void sendDisconnect(int reason, String description) { SshMsgDisconnect msg = new SshMsgDisconnect(reason, description, ""); try { sendMessage(msg, this); stop(); } catch (Exception e) { log.warn("Failed to send disconnect", e); } } /** * @param reason * @param description * @param error */ protected void sendDisconnect(int reason, String description, IOException error) { state.setLastError(error); sendDisconnect(reason, description); } /** @throws IOException */ protected void sendKeyExchangeInit() throws IOException { setLocalKexInit(createLocalKexInit()); sendMessage(getLocalKexInit(), this); state.setValue(TransportProtocolState.PERFORMING_KEYEXCHANGE); } /** @throws IOException */ protected void sendNewKeys() throws IOException { // Send new keys SshMsgNewKeys msg = new SshMsgNewKeys(); sendMessage(msg, this); // Lock the outgoing algorithms so nothing else is sent untill // weve updated them with the new keys algorithmsOut.lock(); // Do we need to hold the algorithmsOut lock during // the input message handling below? If not, then the // lock could be taken just before completeKeyExchange // or even moved into the completeKeyExchange method. // We would then not need the try-finally below (which // is needed for exceptions from eg the readMessage call). boolean hasReleasedLock = false; try { int[] filter = new int[1]; filter[0] = SshMsgNewKeys.SSH_MSG_NEWKEYS; msg = (SshMsgNewKeys) readMessage(filter); if (log.isDebugEnabled()) { log.debug("Received " + msg.getMessageName()); } // Release done in completeKeyExchange hasReleasedLock = true; completeKeyExchange(); } finally { if (!hasReleasedLock) { algorithmsOut.release(); } } } /** * @param encryptCSKey * @param encryptCSIV * @param encryptSCKey * @param encryptSCIV * @param macCSKey * @param macSCKey * @throws AlgorithmNotAgreedException * @throws AlgorithmOperationException * @throws AlgorithmNotSupportedException * @throws AlgorithmInitializationException */ protected abstract void setupNewKeys( byte[] encryptCSKey, byte[] encryptCSIV, byte[] encryptSCKey, byte[] encryptSCIV, byte[] macCSKey, byte[] macSCKey) throws AlgorithmNotAgreedException, AlgorithmOperationException, AlgorithmNotSupportedException, AlgorithmInitializationException; /** * @throws IOException * @throws TransportProtocolException */ protected void completeKeyExchange() throws IOException { log.info("Completing key exchange"); boolean hasReleasedLock = false; try { // Reset the state variables // completeOnNewKeys = new Boolean(false); log.debug("Making keys from key exchange output"); // Make the keys byte[] encryptionKey = makeSshKey('C'); byte[] encryptionIV = makeSshKey('A'); byte[] decryptionKey = makeSshKey('D'); byte[] decryptionIV = makeSshKey('B'); byte[] sendMac = makeSshKey('E'); byte[] receiveMac = makeSshKey('F'); log.debug("Creating algorithm objects"); setupNewKeys(encryptionKey, encryptionIV, decryptionKey, decryptionIV, sendMac, receiveMac); // Reset the key exchange clientKexInit = null; serverKexInit = null; // algorithmsIn.release(); algorithmsOut.release(); hasReleasedLock = true; /* * Update our state, we can send all packets * */ state.setValue(TransportProtocolState.CONNECTED); // Send any outstanding messages synchronized (messageStack) { Iterator it = messageStack.iterator(); log.debug("Sending queued messages"); while (it.hasNext()) { SshMessage msg = (SshMessage) it.next(); sendMessage(msg, this); } messageStack.clear(); } } catch (AlgorithmNotAgreedException anae) { sendDisconnect(SshMsgDisconnect.KEY_EXCHANGE_FAILED, "Algorithm not agreed"); throw new TransportProtocolException( "The connection was disconnected because an algorithm could not be agreed"); } catch (AlgorithmNotSupportedException anse) { sendDisconnect(SshMsgDisconnect.KEY_EXCHANGE_FAILED, "Application error"); throw new TransportProtocolException( "The connection was disconnected because an algorithm class could not be loaded"); } catch (AlgorithmOperationException aoe) { sendDisconnect(SshMsgDisconnect.KEY_EXCHANGE_FAILED, "Algorithm operation error"); throw new TransportProtocolException( "The connection was disconnected because" + " of an algorithm operation error"); } catch (AlgorithmInitializationException aie) { sendDisconnect(SshMsgDisconnect.KEY_EXCHANGE_FAILED, "Algorithm initialization error"); throw new TransportProtocolException( "The connection was disconnected because" + " of an algorithm initialization error"); } finally { if (!hasReleasedLock) { algorithmsOut.release(); } } } /** @return */ protected List getEventHandlers() { return eventHandlers; } /** * @param clientAlgorithms * @param serverAlgorithms * @return * @throws AlgorithmNotAgreedException */ protected String determineAlgorithm(List clientAlgorithms, List serverAlgorithms) throws AlgorithmNotAgreedException { if (log.isDebugEnabled()) { log.debug("Determine Algorithm"); log.debug("Client Algorithms: " + clientAlgorithms.toString()); log.debug("Server Algorithms: " + serverAlgorithms.toString()); } String algorithmClient; String algorithmServer; Iterator itClient = clientAlgorithms.iterator(); while (itClient.hasNext()) { algorithmClient = (String) itClient.next(); Iterator itServer = serverAlgorithms.iterator(); while (itServer.hasNext()) { algorithmServer = (String) itServer.next(); if (algorithmClient.equals(algorithmServer)) { log.debug("Returning " + algorithmClient); return algorithmClient; } } } throw new AlgorithmNotAgreedException("Could not agree algorithm"); } /** @throws IOException */ protected void startBinaryPacketProtocol() throws IOException { // Send our Kex Init sendKeyExchangeInit(); SshMessage msg; // Perform a transport protocol message loop while (state.getValue() != TransportProtocolState.DISCONNECTED) { // Process incoming messages returning any transport protocol // messages to be handled here msg = processMessages(); if (log.isDebugEnabled()) { log.debug("Received " + msg.getMessageName()); } switch (msg.getMessageId()) { case SshMsgKexInit.SSH_MSG_KEX_INIT: { onMsgKexInit((SshMsgKexInit) msg); break; } case SshMsgDisconnect.SSH_MSG_DISCONNECT: { onMsgDisconnect((SshMsgDisconnect) msg); break; } case SshMsgIgnore.SSH_MSG_IGNORE: { onMsgIgnore((SshMsgIgnore) msg); break; } case SshMsgUnimplemented.SSH_MSG_UNIMPLEMENTED: { onMsgUnimplemented((SshMsgUnimplemented) msg); break; } case SshMsgDebug.SSH_MSG_DEBUG: { onMsgDebug((SshMsgDebug) msg); break; } default: onMessageReceived(msg); } } } /** */ protected final void stop() { onDisconnect(); Iterator it = eventHandlers.iterator(); TransportProtocolEventHandler eventHandler; while (it.hasNext()) { eventHandler = (TransportProtocolEventHandler) it.next(); eventHandler.onDisconnect(this); } // Close the input/output streams // sshIn.close(); if (messageStore != null) { messageStore.close(); } // 05/01/2003 moiz change begin: // all close all the registerd messageStores SshMessageStore ms; for (it = messageStores.iterator(); (it != null) && it.hasNext(); ) { ms = (SshMessageStore) it.next(); try { ms.close(); } catch (Exception e) { } } messageStores.clear(); // 05/01/2003 moizd change end: messageStore = null; try { provider.close(); } catch (IOException ioe) { } state.setValue(TransportProtocolState.DISCONNECTED); } private byte[] makeSshKey(char chr) throws IOException { try { // Create the first 20 bytes of key data ByteArrayWriter keydata = new ByteArrayWriter(); byte[] data = new byte[20]; Hash hash = new Hash("SHA"); // Put the dh k value hash.putBigInteger(k); // Put in the exchange hash hash.putBytes(exchangeHash); // Put in the character hash.putByte((byte) chr); // Put the exchange hash in again hash.putBytes(sessionIdentifier); // Create the fist 20 bytes data = hash.doFinal(); keydata.write(data); // Now do the next 20 hash.reset(); // Put the dh k value in again hash.putBigInteger(k); // And the exchange hash hash.putBytes(exchangeHash); // Finally the first 20 bytes of data we created hash.putBytes(data); data = hash.doFinal(); // Put it all together keydata.write(data); // Return it return keydata.toByteArray(); } catch (NoSuchAlgorithmException nsae) { sendDisconnect(SshMsgDisconnect.KEY_EXCHANGE_FAILED, "Application error"); throw new TransportProtocolException("SHA algorithm not supported"); } catch (IOException ioe) { sendDisconnect(SshMsgDisconnect.KEY_EXCHANGE_FAILED, "Application error"); throw new TransportProtocolException("Error writing key data"); } } private void negotiateVersion() throws IOException { byte[] buf; int len; String remoteVer = ""; log.info("Negotiating protocol version"); log.debug("Local identification: " + getLocalId()); // Get the local ident string by calling the abstract method, this // way the implementations set the correct variables for computing the // exchange hash String data = getLocalId() + "\r\n"; // Send our version string provider.getOutputStream().write(data.getBytes()); // Now wait for a reply and evaluate the ident string // buf = new byte[255]; StringBuffer buffer = new StringBuffer(); char ch; int MAX_BUFFER_LENGTH = 255; // Look for a string starting with "SSH-" while (!remoteVer.startsWith("SSH-") && (buffer.length() < MAX_BUFFER_LENGTH)) { // Get the next string while (((ch = (char) provider.getInputStream().read()) != '\n') && (buffer.length() < MAX_BUFFER_LENGTH)) { buffer.append(ch); } // Set trimming off any EOL characters remoteVer = buffer.toString(); // Guess the remote sides EOL by looking at the end of the ident string if (remoteVer.endsWith("\r")) { remoteEOL = EOL_CRLF; } else { remoteEOL = EOL_LF; } log.debug("EOL is guessed at " + ((remoteEOL == EOL_CRLF) ? "CR+LF" : "LF")); // Remove any \r remoteVer = remoteVer.trim(); } // Get the index of the seperators int l = remoteVer.indexOf("-"); int r = remoteVer.indexOf("-", l + 1); // Call abstract method so the implementations can set the // correct member variable setRemoteIdent(remoteVer.trim()); if (log.isDebugEnabled()) { log.debug("Remote identification: " + getRemoteId()); } // Get the version String remoteVersion = remoteVer.substring(l + 1, r); // Evaluate the version, we only support 2.0 if (!(remoteVersion.equals("2.0") || (remoteVersion.equals("1.99")))) { log.fatal("The remote computer does not support protocol version 2.0"); throw new TransportProtocolException( "The protocol version of the remote computer is not supported!"); } log.info("Protocol negotiation complete"); } private void onMsgDebug(SshMsgDebug msg) { log.debug(msg.getMessage()); } private void onMsgDisconnect(SshMsgDisconnect msg) throws IOException { log.info("The remote computer disconnected: " + msg.getDescription()); state.setValue(TransportProtocolState.DISCONNECTED); state.setDisconnectReason(msg.getDescription()); stop(); } private void onMsgIgnore(SshMsgIgnore msg) { if (log.isDebugEnabled()) { log.debug("SSH_MSG_IGNORE with " + String.valueOf(msg.getData().length()) + " bytes of data"); } } private void onMsgKexInit(SshMsgKexInit msg) throws IOException { log.debug("Received remote key exchange init message"); log.debug(msg.toString()); synchronized (kexLock) { setRemoteKexInit(msg); // As either party can initiate a key exchange then we // must check to see if we have sent our own if (state.getValue() != TransportProtocolState.PERFORMING_KEYEXCHANGE) { // if (getLocalKexInit() == null) { sendKeyExchangeInit(); } // } beginKeyExchange(); } } private void onMsgNewKeys(SshMsgNewKeys msg) throws IOException { // Determine whether we have completed our own log.debug("Received New Keys"); // algorithmsIn.lock(); synchronized (completeOnNewKeys) { if (completeOnNewKeys.booleanValue()) { // We need to take this lock since // it is released in completeKeyExchange. algorithmsOut.lock(); completeKeyExchange(); } else { completeOnNewKeys = new Boolean(true); } } } private void onMsgUnimplemented(SshMsgUnimplemented msg) { if (log.isDebugEnabled()) { log.debug( "The message with sequence no " + msg.getSequenceNo() + " was reported as unimplemented by the remote end."); } } /** * @param filter * @return * @throws IOException */ public SshMessage readMessage(int[] filter) throws IOException { byte[] msgdata = null; SshMessage msg; while (state.getValue() != TransportProtocolState.DISCONNECTED) { boolean hasmsg = false; while (!hasmsg) { msgdata = sshIn.readMessage(); hasmsg = true; } Integer messageId = SshMessage.getMessageId(msgdata); // First check the filter for (int i = 0; i < filter.length; i++) { if (filter[i] == messageId.intValue()) { if (messageStore.isRegisteredMessage(messageId)) { return messageStore.createMessage(msgdata); } else { SshMessageStore ms = getMessageStore(messageId); msg = ms.createMessage(msgdata); if (log.isDebugEnabled()) { log.debug("Processing " + msg.getMessageName()); } return msg; } } } if (messageStore.isRegisteredMessage(messageId)) { msg = messageStore.createMessage(msgdata); switch (messageId.intValue()) { case SshMsgDisconnect.SSH_MSG_DISCONNECT: { onMsgDisconnect((SshMsgDisconnect) msg); break; } case SshMsgIgnore.SSH_MSG_IGNORE: { onMsgIgnore((SshMsgIgnore) msg); break; } case SshMsgUnimplemented.SSH_MSG_UNIMPLEMENTED: { onMsgUnimplemented((SshMsgUnimplemented) msg); break; } case SshMsgDebug.SSH_MSG_DEBUG: { onMsgDebug((SshMsgDebug) msg); break; } default: // Exception not allowed throw new IOException("Unexpected transport protocol message"); } } else { throw new IOException("Unexpected message received"); } } throw new IOException("The transport protocol disconnected"); } /** * @return * @throws IOException */ protected SshMessage processMessages() throws IOException { byte[] msgdata = null; SshMessage msg; SshMessageStore ms; while (state.getValue() != TransportProtocolState.DISCONNECTED) { long currentTime = System.currentTimeMillis(); transferredKB = sshIn.getNumBytesTransfered() / 1024 + sshOut.getNumBytesTransfered() / 1024; long kbLimit = transferredKB - lastTriggeredKB; if (((currentTime - startTime) > kexTimeout) || (kbLimit > kexTransferLimitKB)) { // ((sshIn.getNumBytesTransfered() + // sshOut.getNumBytesTransfered()) > kexTransferLimit)) { startTime = currentTime; lastTriggeredKB = transferredKB; if (log.isDebugEnabled()) { log.info("rekey"); } sendKeyExchangeInit(); } boolean hasmsg = false; while (!hasmsg) { try { msgdata = sshIn.readMessage(); hasmsg = true; } catch (InterruptedIOException ex /*SocketTimeoutException ex*/) { log.info("Possible timeout on transport inputstream"); Iterator it = eventHandlers.iterator(); TransportProtocolEventHandler eventHandler; while (it.hasNext()) { eventHandler = (TransportProtocolEventHandler) it.next(); eventHandler.onSocketTimeout(this /*, provider.isConnected()*/); } } } Integer messageId = SshMessage.getMessageId(msgdata); if (!messageStore.isRegisteredMessage(messageId)) { try { ms = getMessageStore(messageId); msg = ms.createMessage(msgdata); if (log.isDebugEnabled()) { log.info("Received " + msg.getMessageName()); } ms.addMessage(msg); } catch (MessageNotRegisteredException mnre) { log.info("Unimplemented message received " + String.valueOf(messageId.intValue())); msg = new SshMsgUnimplemented(sshIn.getSequenceNo()); sendMessage(msg, this); } } else { return messageStore.createMessage(msgdata); } } throw new IOException("The transport protocol has disconnected"); } /** * @param store * @throws MessageAlreadyRegisteredException */ public void addMessageStore(SshMessageStore store) throws MessageAlreadyRegisteredException { messageStores.add(store); } private SshMessageStore getMessageStore(Integer messageId) throws MessageNotRegisteredException { SshMessageStore ms; for (Iterator it = messageStores.iterator(); (it != null) && it.hasNext(); ) { ms = (SshMessageStore) it.next(); if (ms.isRegisteredMessage(messageId)) { return ms; } } throw new MessageNotRegisteredException(messageId); } /** @param ms */ public void removeMessageStore(SshMessageStore ms) { messageStores.remove(ms); } }
private SshPublicKey getAuthorizedKey(String username, String algorithm, byte[] encoded) throws IOException { NativeAuthenticationProvider provider = NativeAuthenticationProvider.getInstance(); String userHome = provider.getHomeDirectory(username); // , nativeSettings); if (userHome == null) { log.warn("There is no home directory for " + username + " is available"); } // Replace '\' with '/' because when we use it in String.replaceAll // for some reason it removes them? if (userHome != null) { userHome = userHome.replace('\\', '/'); } ServerConfiguration config = (ServerConfiguration) ConfigurationLoader.getConfiguration(ServerConfiguration.class); String authorizationFile; String userConfigDir = config.getUserConfigDirectory(); // First replace any '\' with '/' (Becasue replaceAll removes them!) userConfigDir = userConfigDir.replace('\\', '/'); // Replace any home directory tokens if ((userConfigDir.indexOf("%D") > -1) && (userHome == null)) { throw new IOException( "<UserConfigDirectory> requires home directory, but none available for " + username); } int idx = 0; while ((idx = userConfigDir.indexOf("%D", idx + 1)) > -1) { StringBuffer buf = new StringBuffer(userConfigDir); buf = buf.replace(idx, idx + 1, userHome); userConfigDir = buf.toString(); } idx = 0; while ((idx = userConfigDir.indexOf("%U", idx + 1)) > -1) { StringBuffer buf = new StringBuffer(userConfigDir); buf = buf.replace(idx, idx + 1, username); userConfigDir = buf.toString(); } // Replace the '/' with File.seperator and trim userConfigDir = userConfigDir.replace('/', File.separatorChar).trim(); if (!userConfigDir.endsWith(File.separator)) { userConfigDir += File.separator; } authorizationFile = userConfigDir + config.getAuthorizationFile(); // Load the authorization file File file = new File(authorizationFile); if (!file.exists()) { log.info("authorizationFile: " + authorizationFile + " does not exist."); throw new IOException("authorizationFile: " + authorizationFile + " does not exist."); } FileInputStream in = new FileInputStream(file); Authorization keys; try { keys = new Authorization(in); } catch (Exception e) { throw new AuthenticationProtocolException( "Failed to load authorized keys file " + authorizationFile); } // SshPublicKey key = SshPublicKeyFile.parse(encoded); Iterator it = keys.getAuthorizedKeys().iterator(); SshKeyPair pair = SshKeyPairFactory.newInstance(algorithm); SshPublicKey authorizedKey = null; SshPublicKey key = pair.decodePublicKey(encoded); boolean valid = false; String keyfile; while (it.hasNext()) { keyfile = (String) it.next(); // Look for the file in the user config dir first file = new File(userConfigDir + keyfile); // If it does not exist then look absolute if (!file.exists()) { file = new File(keyfile); } if (file.exists()) { // Try to open the public key in the default file format // otherwise attempt the supported key formats SshPublicKeyFile pkf = SshPublicKeyFile.parse(file); authorizedKey = pkf.toPublicKey(); if (authorizedKey.equals(key)) { return authorizedKey; } } else { log.info("Failed attempt to load key file " + keyfile); } } throw new IOException(""); }