PageRenderTime 39ms CodeModel.GetById 11ms RepoModel.GetById 0ms app.codeStats 0ms

/org.eclipse.jgit/src/org/eclipse/jgit/transport/JschConfigSessionFactory.java

https://bitbucket.org/cofarrell/jgit
Java | 294 lines | 157 code | 26 blank | 111 comment | 39 complexity | 5641287d49a9c1f2e52223ee9214ee73 MD5 | raw file
  1. /*
  2. * Copyright (C) 2009, Constantine Plotnikov <constantine.plotnikov@gmail.com>
  3. * Copyright (C) 2008-2009, Google Inc.
  4. * Copyright (C) 2009, Google, Inc.
  5. * Copyright (C) 2009, JetBrains s.r.o.
  6. * Copyright (C) 2008, Robin Rosenberg <robin.rosenberg@dewire.com>
  7. * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>
  8. * and other copyright owners as documented in the project's IP log.
  9. *
  10. * This program and the accompanying materials are made available
  11. * under the terms of the Eclipse Distribution License v1.0 which
  12. * accompanies this distribution, is reproduced below, and is
  13. * available at http://www.eclipse.org/org/documents/edl-v10.php
  14. *
  15. * All rights reserved.
  16. *
  17. * Redistribution and use in source and binary forms, with or
  18. * without modification, are permitted provided that the following
  19. * conditions are met:
  20. *
  21. * - Redistributions of source code must retain the above copyright
  22. * notice, this list of conditions and the following disclaimer.
  23. *
  24. * - Redistributions in binary form must reproduce the above
  25. * copyright notice, this list of conditions and the following
  26. * disclaimer in the documentation and/or other materials provided
  27. * with the distribution.
  28. *
  29. * - Neither the name of the Eclipse Foundation, Inc. nor the
  30. * names of its contributors may be used to endorse or promote
  31. * products derived from this software without specific prior
  32. * written permission.
  33. *
  34. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
  35. * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
  36. * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  37. * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  38. * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
  39. * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  40. * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
  41. * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  42. * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  43. * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
  44. * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  45. * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
  46. * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  47. */
  48. package org.eclipse.jgit.transport;
  49. import java.io.File;
  50. import java.io.FileInputStream;
  51. import java.io.FileNotFoundException;
  52. import java.io.IOException;
  53. import java.net.ConnectException;
  54. import java.net.UnknownHostException;
  55. import java.util.HashMap;
  56. import java.util.Map;
  57. import org.eclipse.jgit.errors.TransportException;
  58. import org.eclipse.jgit.internal.JGitText;
  59. import org.eclipse.jgit.util.FS;
  60. import com.jcraft.jsch.JSch;
  61. import com.jcraft.jsch.JSchException;
  62. import com.jcraft.jsch.Session;
  63. import com.jcraft.jsch.UserInfo;
  64. /**
  65. * The base session factory that loads known hosts and private keys from
  66. * <code>$HOME/.ssh</code>.
  67. * <p>
  68. * This is the default implementation used by JGit and provides most of the
  69. * compatibility necessary to match OpenSSH, a popular implementation of SSH
  70. * used by C Git.
  71. * <p>
  72. * The factory does not provide UI behavior. Override the method
  73. * {@link #configure(org.eclipse.jgit.transport.OpenSshConfig.Host, Session)}
  74. * to supply appropriate {@link UserInfo} to the session.
  75. */
  76. public abstract class JschConfigSessionFactory extends SshSessionFactory {
  77. private final Map<String, JSch> byIdentityFile = new HashMap<String, JSch>();
  78. private JSch defaultJSch;
  79. private OpenSshConfig config;
  80. @Override
  81. public synchronized RemoteSession getSession(URIish uri,
  82. CredentialsProvider credentialsProvider, FS fs, int tms)
  83. throws TransportException {
  84. String user = uri.getUser();
  85. final String pass = uri.getPass();
  86. String host = uri.getHost();
  87. int port = uri.getPort();
  88. try {
  89. if (config == null)
  90. config = OpenSshConfig.get(fs);
  91. final OpenSshConfig.Host hc = config.lookup(host);
  92. host = hc.getHostName();
  93. if (port <= 0)
  94. port = hc.getPort();
  95. if (user == null)
  96. user = hc.getUser();
  97. Session session = createSession(credentialsProvider, fs, user,
  98. pass, host, port, hc);
  99. int retries = 0;
  100. while (!session.isConnected() && retries < 3) {
  101. try {
  102. retries++;
  103. session.connect(tms);
  104. } catch (JSchException e) {
  105. session.disconnect();
  106. session = null;
  107. // if authentication failed maybe credentials changed at the
  108. // remote end therefore reset credentials and retry
  109. if (credentialsProvider != null && e.getCause() == null
  110. && e.getMessage().equals("Auth fail") //$NON-NLS-1$
  111. && retries < 3) {
  112. credentialsProvider.reset(uri);
  113. session = createSession(credentialsProvider, fs, user,
  114. pass, host, port, hc);
  115. } else {
  116. throw e;
  117. }
  118. }
  119. }
  120. return new JschSession(session, uri);
  121. } catch (JSchException je) {
  122. final Throwable c = je.getCause();
  123. if (c instanceof UnknownHostException)
  124. throw new TransportException(uri, JGitText.get().unknownHost);
  125. if (c instanceof ConnectException)
  126. throw new TransportException(uri, c.getMessage());
  127. throw new TransportException(uri, je.getMessage(), je);
  128. }
  129. }
  130. private Session createSession(CredentialsProvider credentialsProvider,
  131. FS fs, String user, final String pass, String host, int port,
  132. final OpenSshConfig.Host hc) throws JSchException {
  133. final Session session = createSession(hc, user, host, port, fs);
  134. if (pass != null)
  135. session.setPassword(pass);
  136. final String strictHostKeyCheckingPolicy = hc
  137. .getStrictHostKeyChecking();
  138. if (strictHostKeyCheckingPolicy != null)
  139. session.setConfig("StrictHostKeyChecking", //$NON-NLS-1$
  140. strictHostKeyCheckingPolicy);
  141. final String pauth = hc.getPreferredAuthentications();
  142. if (pauth != null)
  143. session.setConfig("PreferredAuthentications", pauth); //$NON-NLS-1$
  144. if (credentialsProvider != null
  145. && (!hc.isBatchMode() || !credentialsProvider.isInteractive())) {
  146. session.setUserInfo(new CredentialsProviderUserInfo(session,
  147. credentialsProvider));
  148. }
  149. configure(hc, session);
  150. return session;
  151. }
  152. /**
  153. * Create a new remote session for the requested address.
  154. *
  155. * @param hc
  156. * host configuration
  157. * @param user
  158. * login to authenticate as.
  159. * @param host
  160. * server name to connect to.
  161. * @param port
  162. * port number of the SSH daemon (typically 22).
  163. * @param fs
  164. * the file system abstraction which will be necessary to
  165. * perform certain file system operations.
  166. * @return new session instance, but otherwise unconfigured.
  167. * @throws JSchException
  168. * the session could not be created.
  169. */
  170. protected Session createSession(final OpenSshConfig.Host hc,
  171. final String user, final String host, final int port, FS fs)
  172. throws JSchException {
  173. return getJSch(hc, fs).getSession(user, host, port);
  174. }
  175. /**
  176. * Provide additional configuration for the session based on the host
  177. * information. This method could be used to supply {@link UserInfo}.
  178. *
  179. * @param hc
  180. * host configuration
  181. * @param session
  182. * session to configure
  183. */
  184. protected abstract void configure(OpenSshConfig.Host hc, Session session);
  185. /**
  186. * Obtain the JSch used to create new sessions.
  187. *
  188. * @param hc
  189. * host configuration
  190. * @param fs
  191. * the file system abstraction which will be necessary to
  192. * perform certain file system operations.
  193. * @return the JSch instance to use.
  194. * @throws JSchException
  195. * the user configuration could not be created.
  196. */
  197. protected JSch getJSch(final OpenSshConfig.Host hc, FS fs) throws JSchException {
  198. if (defaultJSch == null) {
  199. defaultJSch = createDefaultJSch(fs);
  200. for (Object name : defaultJSch.getIdentityNames())
  201. byIdentityFile.put((String) name, defaultJSch);
  202. }
  203. final File identityFile = hc.getIdentityFile();
  204. if (identityFile == null)
  205. return defaultJSch;
  206. final String identityKey = identityFile.getAbsolutePath();
  207. JSch jsch = byIdentityFile.get(identityKey);
  208. if (jsch == null) {
  209. jsch = new JSch();
  210. jsch.setHostKeyRepository(defaultJSch.getHostKeyRepository());
  211. jsch.addIdentity(identityKey);
  212. byIdentityFile.put(identityKey, jsch);
  213. }
  214. return jsch;
  215. }
  216. /**
  217. * @param fs
  218. * the file system abstraction which will be necessary to
  219. * perform certain file system operations.
  220. * @return the new default JSch implementation.
  221. * @throws JSchException
  222. * known host keys cannot be loaded.
  223. */
  224. protected JSch createDefaultJSch(FS fs) throws JSchException {
  225. final JSch jsch = new JSch();
  226. knownHosts(jsch, fs);
  227. identities(jsch, fs);
  228. return jsch;
  229. }
  230. private static void knownHosts(final JSch sch, FS fs) throws JSchException {
  231. final File home = fs.userHome();
  232. if (home == null)
  233. return;
  234. final File known_hosts = new File(new File(home, ".ssh"), "known_hosts"); //$NON-NLS-1$ //$NON-NLS-2$
  235. try {
  236. final FileInputStream in = new FileInputStream(known_hosts);
  237. try {
  238. sch.setKnownHosts(in);
  239. } finally {
  240. in.close();
  241. }
  242. } catch (FileNotFoundException none) {
  243. // Oh well. They don't have a known hosts in home.
  244. } catch (IOException err) {
  245. // Oh well. They don't have a known hosts in home.
  246. }
  247. }
  248. private static void identities(final JSch sch, FS fs) {
  249. final File home = fs.userHome();
  250. if (home == null)
  251. return;
  252. final File sshdir = new File(home, ".ssh"); //$NON-NLS-1$
  253. if (sshdir.isDirectory()) {
  254. loadIdentity(sch, new File(sshdir, "identity")); //$NON-NLS-1$
  255. loadIdentity(sch, new File(sshdir, "id_rsa")); //$NON-NLS-1$
  256. loadIdentity(sch, new File(sshdir, "id_dsa")); //$NON-NLS-1$
  257. }
  258. }
  259. private static void loadIdentity(final JSch sch, final File priv) {
  260. if (priv.isFile()) {
  261. try {
  262. sch.addIdentity(priv.getAbsolutePath());
  263. } catch (JSchException e) {
  264. // Instead, pretend the key doesn't exist.
  265. }
  266. }
  267. }
  268. }