Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282
  1. /*
  2. * Copyright (C) 2012-2016 Brian P. Hinz. All Rights Reserved.
  3. * Copyright (C) 2000 Const Kaplinsky. All Rights Reserved.
  4. * Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved.
  5. *
  6. * This is free software; you can redistribute it and/or modify
  7. * it under the terms of the GNU General Public License as published by
  8. * the Free Software Foundation; either version 2 of the License, or
  9. * (at your option) any later version.
  10. *
  11. * This software is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU General Public License
  17. * along with this software; if not, write to the Free Software
  18. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
  19. * USA.
  20. */
  21. /*
  22. * tunnel.java - SSH tunneling support
  23. */
  24. package com.tigervnc.vncviewer;
  25. import java.io.BufferedReader;
  26. import java.io.File;
  27. import java.io.InputStream;
  28. import java.io.InputStreamReader;
  29. import java.util.*;
  30. import com.tigervnc.rdr.*;
  31. import com.tigervnc.rfb.*;
  32. import com.tigervnc.rfb.Exception;
  33. import com.tigervnc.network.*;
  34. import com.jcraft.jsch.JSch;
  35. import com.jcraft.jsch.JSchException;
  36. import com.jcraft.jsch.ConfigRepository;
  37. import com.jcraft.jsch.Logger;
  38. import com.jcraft.jsch.OpenSSHConfig;
  39. import com.jcraft.jsch.Session;
  40. import static com.tigervnc.vncviewer.Parameters.*;
  41. public class Tunnel {
  42. private final static String DEFAULT_TUNNEL_TEMPLATE
  43. = "-f -L %L:localhost:%R %H sleep 20";
  44. private final static String DEFAULT_VIA_TEMPLATE
  45. = "-f -L %L:%H:%R %G sleep 20";
  46. public static void createTunnel(CConn cc, int localPort) throws Exception {
  47. int remotePort;
  48. String gatewayHost;
  49. String remoteHost;
  50. remotePort = cc.getServerPort();
  51. gatewayHost = cc.getServerName();
  52. remoteHost = "localhost";
  53. if (!via.getValue().isEmpty()) {
  54. gatewayHost = getSshHost();
  55. remoteHost = cc.getServerName();
  56. }
  57. String pattern = extSSHArgs.getValue();
  58. if (pattern == null || pattern.isEmpty()) {
  59. if (tunnel.getValue() && via.getValue().isEmpty())
  60. pattern = System.getProperty("VNC_TUNNEL_CMD");
  61. else
  62. pattern = System.getProperty("VNC_VIA_CMD");
  63. }
  64. if (extSSH.getValue() ||
  65. (pattern != null && pattern.length() > 0)) {
  66. createTunnelExt(gatewayHost, remoteHost, remotePort, localPort, pattern);
  67. } else {
  68. createTunnelJSch(gatewayHost, remoteHost, remotePort, localPort);
  69. }
  70. }
  71. private static class MyJSchLogger implements Logger {
  72. public boolean isEnabled(int level){
  73. return true;
  74. }
  75. public void log(int level, String msg){
  76. switch (level) {
  77. case Logger.INFO:
  78. vlog.info(msg);
  79. break;
  80. case Logger.ERROR:
  81. vlog.error(msg);
  82. break;
  83. default:
  84. vlog.debug(msg);
  85. }
  86. }
  87. }
  88. public static String getSshHost() {
  89. String sshHost = via.getValue();
  90. if (sshHost.isEmpty())
  91. return vncServerName.getValue();
  92. int end = sshHost.indexOf(":");
  93. if (end < 0)
  94. end = sshHost.length();
  95. sshHost = sshHost.substring(sshHost.indexOf("@")+1, end);
  96. return sshHost;
  97. }
  98. public static String getSshUser() {
  99. String sshUser = (String)System.getProperties().get("user.name");
  100. String viaStr = via.getValue();
  101. if (!viaStr.isEmpty() && viaStr.indexOf("@") > 0)
  102. sshUser = viaStr.substring(0, viaStr.indexOf("@"));
  103. return sshUser;
  104. }
  105. public static int getSshPort() {
  106. String sshPort = "22";
  107. String viaStr = via.getValue();
  108. if (!viaStr.isEmpty() && viaStr.indexOf(":") > 0)
  109. sshPort = viaStr.substring(viaStr.indexOf(":")+1, viaStr.length());
  110. return Integer.parseInt(sshPort);
  111. }
  112. public static String getSshKeyFile() {
  113. if (!sshKeyFile.getValue().isEmpty())
  114. return sshKeyFile.getValue();
  115. String[] ids = { "id_dsa", "id_rsa" };
  116. for (String id : ids) {
  117. File f = new File(FileUtils.getHomeDir()+".ssh/"+id);
  118. if (f.exists() && f.canRead())
  119. return(f.getAbsolutePath());
  120. }
  121. return "";
  122. }
  123. public static String getSshKey() {
  124. if (!sshKey.getValue().isEmpty())
  125. return sshKeyFile.getValue().replaceAll("\\\\n", "\n");
  126. return "";
  127. }
  128. private static void createTunnelJSch(String gatewayHost, String remoteHost,
  129. int remotePort, int localPort) throws Exception {
  130. JSch.setLogger(new MyJSchLogger());
  131. JSch jsch=new JSch();
  132. try {
  133. // NOTE: jsch does not support all ciphers. User may be
  134. // prompted to accept host key authenticy even if
  135. // the key is in the known_hosts file.
  136. File knownHosts = new File(FileUtils.getHomeDir()+".ssh/known_hosts");
  137. if (knownHosts.exists() && knownHosts.canRead())
  138. jsch.setKnownHosts(knownHosts.getAbsolutePath());
  139. ArrayList<File> privateKeys = new ArrayList<File>();
  140. if (!getSshKey().isEmpty()) {
  141. byte[] keyPass = null, key;
  142. if (!sshKeyPass.getValue().isEmpty())
  143. keyPass = sshKeyPass.getValue().getBytes();
  144. jsch.addIdentity("TigerVNC", getSshKey().getBytes(), null, keyPass);
  145. } else if (!getSshKeyFile().isEmpty()) {
  146. File f = new File(getSshKeyFile());
  147. if (!f.exists() || !f.canRead())
  148. throw new Exception("Cannot access SSH key file "+getSshKeyFile());
  149. privateKeys.add(f);
  150. }
  151. for (Iterator<File> i = privateKeys.iterator(); i.hasNext();) {
  152. File privateKey = (File)i.next();
  153. if (privateKey.exists() && privateKey.canRead())
  154. if (!sshKeyPass.getValue().isEmpty())
  155. jsch.addIdentity(privateKey.getAbsolutePath(),
  156. sshKeyPass.getValue());
  157. else
  158. jsch.addIdentity(privateKey.getAbsolutePath());
  159. }
  160. String user = getSshUser();
  161. String label = new String("SSH Authentication");
  162. PasswdDialog dlg =
  163. new PasswdDialog(label, (user == null ? false : true), false);
  164. dlg.userEntry.setText(user != null ? user : "");
  165. File ssh_config = new File(sshConfig.getValue());
  166. if (ssh_config.exists() && ssh_config.canRead()) {
  167. ConfigRepository repo =
  168. OpenSSHConfig.parse(ssh_config.getAbsolutePath());
  169. jsch.setConfigRepository(repo);
  170. }
  171. Session session=jsch.getSession(user, gatewayHost, getSshPort());
  172. session.setUserInfo(dlg);
  173. // OpenSSHConfig doesn't recognize StrictHostKeyChecking
  174. if (session.getConfig("StrictHostKeyChecking") == null)
  175. session.setConfig("StrictHostKeyChecking", "ask");
  176. session.connect();
  177. session.setPortForwardingL(localPort, remoteHost, remotePort);
  178. } catch (java.lang.Exception e) {
  179. throw new Exception(e.getMessage());
  180. }
  181. }
  182. private static void createTunnelExt(String gatewayHost, String remoteHost,
  183. int remotePort, int localPort,
  184. String pattern) throws Exception {
  185. if (pattern == null || pattern.length() < 1) {
  186. if (tunnel.getValue() && via.getValue().isEmpty())
  187. pattern = DEFAULT_TUNNEL_TEMPLATE;
  188. else
  189. pattern = DEFAULT_VIA_TEMPLATE;
  190. }
  191. String cmd = fillCmdPattern(pattern, gatewayHost, remoteHost,
  192. remotePort, localPort);
  193. try {
  194. Thread t = new Thread(new ExtProcess(cmd, vlog, true));
  195. t.start();
  196. // wait for the ssh process to start
  197. Thread.sleep(1000);
  198. } catch (java.lang.Exception e) {
  199. throw new Exception(e.getMessage());
  200. }
  201. }
  202. private static String fillCmdPattern(String pattern, String gatewayHost,
  203. String remoteHost, int remotePort,
  204. int localPort) {
  205. boolean H_found = false, G_found = false, R_found = false, L_found = false;
  206. boolean P_found = false;
  207. String cmd = extSSHClient.getValue() + " ";
  208. pattern.replaceAll("^\\s+", "");
  209. String user = getSshUser();
  210. int sshPort = getSshPort();
  211. gatewayHost = user + "@" + gatewayHost;
  212. for (int i = 0; i < pattern.length(); i++) {
  213. if (pattern.charAt(i) == '%') {
  214. switch (pattern.charAt(++i)) {
  215. case 'H':
  216. cmd += remoteHost;
  217. H_found = true;
  218. continue;
  219. case 'G':
  220. cmd += gatewayHost;
  221. G_found = true;
  222. continue;
  223. case 'R':
  224. cmd += remotePort;
  225. R_found = true;
  226. continue;
  227. case 'L':
  228. cmd += localPort;
  229. L_found = true;
  230. continue;
  231. case 'P':
  232. cmd += sshPort;
  233. P_found = true;
  234. continue;
  235. }
  236. }
  237. cmd += pattern.charAt(i);
  238. }
  239. if (pattern.length() > 1024)
  240. throw new Exception("Tunneling command is too long.");
  241. if (!H_found || !R_found || !L_found)
  242. throw new Exception("%H, %R or %L absent in tunneling command template.");
  243. if (!tunnel.getValue() && !G_found)
  244. throw new Exception("%G pattern absent in tunneling command template.");
  245. vlog.info("SSH command line: "+cmd);
  246. if (VncViewer.os.startsWith("windows"))
  247. cmd.replaceAll("\\\\", "\\\\\\\\");
  248. return cmd;
  249. }
  250. static LogWriter vlog = new LogWriter("Tunnel");
  251. }