/* * (c) Thomas Pasch, 2003. All Rights Reserved. * * --LICENSE NOTICE-- * This program is free software; you can redistribute it and/or * modify it under the terms of license of JSch * (http://www.jcraft.com/jsch/). * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * --LICENSE NOTICE-- */ package de.mud.jta.plugin; import de.mud.jta.Plugin; import de.mud.jta.FilterPlugin; import de.mud.jta.PluginBus; import de.mud.jta.PluginConfig; import de.mud.jta.VisualPlugin; import de.mud.jta.event.ConfigurationListener; import de.mud.jta.event.OnlineStatusListener; import de.mud.jta.event.TerminalTypeRequest; import de.mud.jta.event.WindowSizeRequest; import de.mud.jta.event.LocalEchoRequest; import de.mud.ssh.SshIO; import javax.swing.*; import java.awt.*; import java.awt.event.ActionListener; import java.awt.event.ActionEvent; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import de.mud.jta.event.SocketListener; import de.mud.jta.event.OnlineStatus; import com.jcraft.jsch.JSch; import com.jcraft.jsch.JSchException; import com.jcraft.jsch.ProxyHTTP; import com.jcraft.jsch.ProxySOCKS5; import com.jcraft.jsch.Channel; import com.jcraft.jsch.Session; import com.jcraft.jsch.UserInfo; /** * Secure Shell plugin for the Java Telnet Application (JTA). This is a plugin * to be used instead of Telnet for secure remote terminal sessions over * insecure networks. *

* This plugin works in conjuction with JSch, a open source client site pure * java implementation of the SSH protocol version 2 with a BSD styled * license. *

* This plugin was written mainly because JSch is a more complete * implementation of SSH2 than the SSH plugin that comes with JTA. JSch comes * with proxy and socks5 support, zlib compression and X11 forwarding. After * copying the compiled class file of this source to de.mud.jta.plugin, it is * invoked like this: *

* java -cp de.mud.jta.Main -plugins Status,JTAJSch,Terminal [hostname] *

* Known Bugs:

* Maintainer: Thomas Pasch *

* * @see Java Telnet Application * @see JSch homepage * @see free Open Source version of SSH * @see Official SSH homepage * @version $Id:$ * @author Thomas Pasch */ public class JTAJSch extends Plugin implements FilterPlugin, VisualPlugin { private static final String MENU = "JSch"; private static final String MENU_HTTP_PROXY = "Http Proxy ..."; private static final String MENU_SOCKS_PROXY = "Socks Proxy ..."; private static final String MENU_X11 = "X11 ..."; private static final String MENU_LOCAL_PORT = "Local Port ..."; private static final String MENU_REMOTE_PORT = "Remote Port ..."; private static final JSch jSch_ = new JSch(); private String proxyHttpHost_ = null; private int proxyHttpPort_; private String proxySOCKS5Host_ = null; private int proxySOCKS5Port_; private String xHost_ = null; private int xPort_; protected FilterPlugin source; // protected SshIO handler; protected Session session_; private Channel channel_; private InputStream in_; private OutputStream out_; private String host_; private int port_; // protected String user, pass; private final static int debug = 0; private volatile boolean auth = false; protected MyUserInfo userInfo_; /** * Create a new ssh plugin. */ public JTAJSch(final PluginBus bus, final String id) { super(bus, id); // create a new telnet protocol handler // handler = new SshIO() { // /** get the current terminal type */ // public String getTerminalType() { // return (String)bus.broadcast(new TerminalTypeRequest()); // } // /** get the current window size */ // public Dimension getWindowSize() { // return (Dimension)bus.broadcast(new WindowSizeRequest()); // } // /** notify about local echo */ // public void setLocalEcho(boolean echo) { // bus.broadcast(new LocalEchoRequest(echo)); // } // /** write data to our back end */ // public void write(byte[] b) throws IOException { // source.write(b); // } // }; bus.registerPluginListener(new ConfigurationListener() { public void setConfiguration(PluginConfig config) { String user = config.getProperty("SSH", id, "user"); String pass = config.getProperty("SSH", id, "password"); userInfo_ = new MyUserInfo(user, pass, null); } }); // reset the protocol handler just in case :-) bus.registerPluginListener(new OnlineStatusListener() { public void online() { String user = null, pass = null; if (userInfo_ != null) { user = userInfo_.getUser(); pass = userInfo_.getPassword(); } if(pass == null) { final Frame frame = new Frame("SSH User Authentication"); Panel panel = new Panel(new GridLayout(3,1)); panel.add(new Label("SSH Authorization required")); frame.add("North", panel); panel = new Panel(new GridLayout(2,2)); final TextField login = new TextField(user, 10); final TextField passw = new TextField(10); login.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent evt) { passw.requestFocus(); } }); passw.setEchoChar('*'); panel.add(new Label("User name")); panel.add(login); panel.add(new Label("Password")); panel.add(passw); frame.add("Center", panel); panel = new Panel(); Button cancel = new Button("Cancel"); Button ok = new Button("Login"); ActionListener enter = new ActionListener() { public void actionPerformed(ActionEvent evt) { // handler.setLogin(login.getText()); // handler.setPassword(passw.getText()); userInfo_ = new MyUserInfo(login.getText(), passw.getText(), null); frame.dispose(); connect(); } }; ok.addActionListener(enter); passw.addActionListener(enter); cancel.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent evt) { frame.dispose(); } }); panel.add(cancel); panel.add(ok); frame.add("South", panel); frame.pack(); frame.show(); frame.setLocation(frame.getToolkit().getScreenSize().width/2 - frame.getSize().width/2, frame.getToolkit().getScreenSize().height/2 - frame.getSize().height/2); if(user != null) { passw.requestFocus(); } } else { error(user+":"+pass); // handler.setLogin(user); // handler.setPassword(pass); userInfo_ = new MyUserInfo(user, pass, null); connect(); } } private void connect() { if (auth) { throw new IllegalStateException(); } try { session_ = jSch_.getSession(userInfo_.getUser(), host_, 22); session_.setUserInfo(userInfo_); if (isProxyHttp()) { session_.setProxy(new ProxyHTTP(proxyHttpHost_, proxyHttpPort_)); } else if (isProxySOCKS5()) { session_.setProxy(new ProxySOCKS5(proxySOCKS5Host_, proxySOCKS5Port_)); } if (isXForwarding()) { session_.setX11Host(xHost_); session_.setX11Port(xPort_); } session_.connect(); channel_ = session_.openChannel("shell"); try { in_ = channel_.getInputStream(); out_ = channel_.getOutputStream(); } catch (IOException ee) { ee.printStackTrace(); throw new RuntimeException(ee); } channel_.connect(); } catch (JSchException e) { e.printStackTrace(); throw new RuntimeException(e); } auth = true; } public void offline() { // handler.disconnect(); try { if (in_ != null) in_.close(); if (out_ != null) out_.close(); } catch (IOException e) { e.printStackTrace(); throw new RuntimeException(e); } finally { auth=false; userInfo_ = null; if (channel_ != null) channel_.disconnect(); if (session_ != null) session_.disconnect(); } } }); bus.registerPluginListener(new SocketListener() { public void connect(String host, int port) { host_ = host; port_ = port; bus.broadcast(new OnlineStatus(true)); } public void disconnect() { bus.broadcast(new OnlineStatus(false)); } }); } public void setFilterSource(FilterPlugin source) { if(debug>0) System.err.println("ssh: connected to: "+source); this.source = source; } public FilterPlugin getFilterSource() { return source; } private byte buffer[]; private int pos; /** * Read data from the backend and decrypt it. This is a buffering read * as the encrypted information is usually smaller than its decrypted * pendant. So it will not read from the backend as long as there is * data in the buffer. * @param b the buffer where to read the decrypted data in * @return the amount of bytes actually read. */ public int read(byte[] b) throws IOException { // we don't want to read from the pipeline without authorization while(!auth) try { Thread.sleep(1000); } catch(InterruptedException e) { e.printStackTrace(); } // Empty the buffer before we do anything else if(buffer != null) { int amount = ((buffer.length - pos) <= b.length) ? buffer.length - pos : b.length; System.arraycopy(buffer, pos, b, 0, amount); if(pos + amount < buffer.length) { pos += amount; } else buffer = null; return amount; } // now that the buffer is empty let's read more data and decrypt it // int n = source.read(b); // if(n > 0) { int n; final int length = b.length; if ((n = in_.available()) > 0) { if (n > length) n = length; n = in_.read(b,0, n); byte[] tmp = new byte[n]; System.arraycopy(b, 0, tmp, 0, n); pos = 0; // buffer = handler.handleSSH(tmp); buffer = tmp; if(debug > 0 && buffer != null && buffer.length > 0) System.err.println("ssh: "+buffer); if(buffer != null && buffer.length > 0) { if(debug > 0) System.err.println("ssh: incoming="+n+" now="+buffer.length); int amount = buffer.length <= b.length ? buffer.length : b.length; System.arraycopy(buffer, 0, b, 0, amount); pos = n = amount; if(amount == buffer.length) { buffer = null; pos = 0; } } else return 0; } return n; } /** * Write data to the back end. This hands the data over to the ssh * protocol handler who encrypts the information and writes it to * the actual back end pipe. * @param b the unencrypted data to be encrypted and sent */ public void write(byte[] b) throws IOException { // no write until authorization is done if(!auth) return; for (int i=0;i \r */ b[i] = 13; break; } } // handler.sendData(new String(b)); out_.write(b); } public JComponent getPluginVisual() { // JTextArea comp = new JTextArea("getPluginVisual"); return null; } public JMenu getPluginMenu() { final JMenu menu = new JMenu(MENU); // JMenuItem item = new JMenuItem(MENU_HTTP_PROXY); item.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { String foo = getProxyHttpHost(); int bar = getProxyHttpPort(); String proxy = JOptionPane.showInputDialog( "HTTP proxy server (hostname:port)", ((foo!=null&&bar!=0)? foo + ":" + bar : "")); if (proxy == null) return; if (proxy.length() == 0 ) { setProxyHttp(null, 0); return; } try{ foo=proxy.substring(0, proxy.indexOf(':')); bar=Integer.parseInt(proxy.substring(proxy.indexOf(':')+1)); if(foo!=null){ setProxyHttp(foo, bar); } } catch(Exception ee){ } } }); menu.add(item); // item = new JMenuItem(MENU_SOCKS_PROXY); item.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { String foo=getProxySOCKS5Host(); int bar=getProxySOCKS5Port(); String proxy= JOptionPane.showInputDialog( "SOCKS5 server (hostname:1080)", ((foo!=null&&bar!=0)? foo+":"+bar : "")); if(proxy==null)return; if(proxy.length()==0){ setProxySOCKS5(null, 0); return; } try{ foo=proxy.substring(0, proxy.indexOf(':')); bar=Integer.parseInt(proxy.substring(proxy.indexOf(':')+1)); if(foo!=null){ setProxySOCKS5(foo, bar); } } catch(Exception ee){ } } }); menu.add(item); // item = new JMenuItem(MENU_X11); item.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { String display=JOptionPane.showInputDialog( "XDisplay name (hostname:0)", (getXHost() == null) ? "": (getXHost() + ":" + getXPort())); try{ if(display!=null){ String host = display.substring(0, display.indexOf(':')); int port = Integer.parseInt(display.substring(display.indexOf(':')+1)); setXForwarding(host, port); } } catch(Exception ee){ setXForwarding(null, -1); } } }); menu.add(item); // item = new JMenuItem(MENU_LOCAL_PORT); item.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { if (session_ == null){ JOptionPane.showMessageDialog(menu.getParent(), "Establish the connection before this setting."); return; } try{ String title = "Local port forwarding (port:host:hostport)"; String foo = JOptionPane.showInputDialog(title, ""); if (foo == null) return; int port1 = Integer.parseInt(foo.substring(0, foo.indexOf(':'))); foo = foo.substring(foo.indexOf(':') + 1); String host = foo.substring(0, foo.indexOf(':')); int port2 = Integer.parseInt(foo.substring(foo.indexOf(':') + 1)); setPortForwardingL(port1, host, port2); } catch(Exception ee){ } } }); menu.add(item); // item = new JMenuItem(MENU_REMOTE_PORT); item.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { if (session_ == null) { JOptionPane.showMessageDialog(menu.getParent(), "Establish the connection before this setting."); return; } try{ String title="Remote port forwarding (port:host:hostport)"; String foo=JOptionPane.showInputDialog(title, ""); if (foo == null) return; int port1 = Integer.parseInt(foo.substring(0, foo.indexOf(':'))); foo = foo.substring(foo.indexOf(':') + 1); String host = foo.substring(0, foo.indexOf(':')); int port2 = Integer.parseInt(foo.substring(foo.indexOf(':') + 1)); setPortForwardingR(port1, host, port2); } catch(Exception ee){ } } }); menu.add(item); // return menu; } private boolean isProxyHttp() { return proxyHttpHost_ != null; } private String getProxyHttpHost() { return proxyHttpHost_; } private int getProxyHttpPort() { return proxyHttpPort_; } private void setProxyHttp(String host, int port) { proxyHttpHost_ = host; switch (port) { case -1: proxyHttpHost_ = null; proxyHttpPort_ = port; break; case 0: proxyHttpPort_ = 3128; break; default: proxyHttpPort_ = port; } } private boolean isProxySOCKS5() { return proxySOCKS5Host_ != null; } private String getProxySOCKS5Host() { return proxySOCKS5Host_; } private int getProxySOCKS5Port() { return proxySOCKS5Port_; } private void setProxySOCKS5(String host, int port) { proxySOCKS5Host_ = host; switch (port) { case -1: proxySOCKS5Host_ = null; proxySOCKS5Port_ = port; break; case 0: proxySOCKS5Port_ = 3128; break; default: proxySOCKS5Port_ = port; } } private boolean isXForwarding() { return xHost_ != null; } private String getXHost() { return xHost_; } private int getXPort() { return xPort_; } private void setXForwarding(String host, int port) { xHost_ = host; switch (port) { case -1: xHost_ = null; xPort_ = port; break; default: xPort_ = port; } } private void setPortForwardingR(int port1, String host, int port2) { try{ session_.setPortForwardingR(port1, host, port2); } catch (JSchException e) { e.printStackTrace(); throw new RuntimeException(e); } } private void setPortForwardingL(int port1, String host, int port2) { try{ session_.setPortForwardingL(port1, host, port2); } catch (JSchException e) { e.printStackTrace(); throw new RuntimeException(e); } } } final class MyUserInfo implements UserInfo { private final String user_; private final String password_; private final String passphrase_; public MyUserInfo(String user, String password, String passphrase) { user_ = user; password_ = password; passphrase_ = passphrase; } public String getUser() { return user_; } public String getPassphrase() { return passphrase_; } public String getPassword() { return password_; } public boolean promptPassphrase(String message) { return true; } public boolean promptPassword(String message) { return true; } public boolean promptYesNo(String message) { return true; } public void showMessage(String message) { } }