1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50 package org.eclipse.jgit.transport;
51
52 import java.io.File;
53 import java.io.FileInputStream;
54 import java.io.FileNotFoundException;
55 import java.io.IOException;
56 import java.lang.reflect.InvocationTargetException;
57 import java.lang.reflect.Method;
58 import java.net.ConnectException;
59 import java.net.UnknownHostException;
60 import java.text.MessageFormat;
61 import java.util.HashMap;
62 import java.util.Map;
63
64 import org.eclipse.jgit.errors.TransportException;
65 import org.eclipse.jgit.internal.JGitText;
66 import org.eclipse.jgit.util.FS;
67 import org.slf4j.Logger;
68 import org.slf4j.LoggerFactory;
69
70 import com.jcraft.jsch.JSch;
71 import com.jcraft.jsch.JSchException;
72 import com.jcraft.jsch.Session;
73 import com.jcraft.jsch.UserInfo;
74
75
76
77
78
79
80
81
82
83
84
85
86
87 public abstract class JschConfigSessionFactory extends SshSessionFactory {
88
89 private static final Logger LOG = LoggerFactory
90 .getLogger(JschConfigSessionFactory.class);
91
92 private final Map<String, JSch> byIdentityFile = new HashMap<>();
93
94 private JSch defaultJSch;
95
96 private OpenSshConfig config;
97
98 @Override
99 public synchronized RemoteSession getSession(URIish uri,
100 CredentialsProvider credentialsProvider, FS fs, int tms)
101 throws TransportException {
102
103 String user = uri.getUser();
104 final String pass = uri.getPass();
105 String host = uri.getHost();
106 int port = uri.getPort();
107
108 try {
109 if (config == null)
110 config = OpenSshConfig.get(fs);
111
112 final OpenSshConfig.Host hc = config.lookup(host);
113 host = hc.getHostName();
114 if (port <= 0)
115 port = hc.getPort();
116 if (user == null)
117 user = hc.getUser();
118
119 Session session = createSession(credentialsProvider, fs, user,
120 pass, host, port, hc);
121
122 int retries = 0;
123 while (!session.isConnected()) {
124 try {
125 retries++;
126 session.connect(tms);
127 } catch (JSchException e) {
128 session.disconnect();
129 session = null;
130
131 knownHosts(getJSch(hc, fs), fs);
132
133 if (isAuthenticationCanceled(e)) {
134 throw e;
135 } else if (isAuthenticationFailed(e)
136 && credentialsProvider != null) {
137
138
139 if (retries < 3) {
140 credentialsProvider.reset(uri);
141 session = createSession(credentialsProvider, fs,
142 user, pass, host, port, hc);
143 } else
144 throw e;
145 } else if (retries >= hc.getConnectionAttempts()) {
146 throw e;
147 } else {
148 try {
149 Thread.sleep(1000);
150 session = createSession(credentialsProvider, fs,
151 user, pass, host, port, hc);
152 } catch (InterruptedException e1) {
153 throw new TransportException(
154 JGitText.get().transportSSHRetryInterrupt,
155 e1);
156 }
157 }
158 }
159 }
160
161 return new JschSession(session, uri);
162
163 } catch (JSchException je) {
164 final Throwable c = je.getCause();
165 if (c instanceof UnknownHostException) {
166 throw new TransportException(uri, JGitText.get().unknownHost,
167 je);
168 }
169 if (c instanceof ConnectException) {
170 throw new TransportException(uri, c.getMessage(), je);
171 }
172 throw new TransportException(uri, je.getMessage(), je);
173 }
174
175 }
176
177 private static boolean isAuthenticationFailed(JSchException e) {
178 return e.getCause() == null && e.getMessage().equals("Auth fail");
179 }
180
181 private static boolean isAuthenticationCanceled(JSchException e) {
182 return e.getCause() == null && e.getMessage().equals("Auth cancel");
183 }
184
185 private Session createSession(CredentialsProvider credentialsProvider,
186 FS fs, String user, final String pass, String host, int port,
187 final OpenSshConfig.Host hc) throws JSchException {
188 final Session session = createSession(hc, user, host, port, fs);
189
190
191 setUserName(session, user);
192
193
194 session.setConfig("MaxAuthTries", "1");
195 if (pass != null)
196 session.setPassword(pass);
197 final String strictHostKeyCheckingPolicy = hc
198 .getStrictHostKeyChecking();
199 if (strictHostKeyCheckingPolicy != null)
200 session.setConfig("StrictHostKeyChecking",
201 strictHostKeyCheckingPolicy);
202 final String pauth = hc.getPreferredAuthentications();
203 if (pauth != null)
204 session.setConfig("PreferredAuthentications", pauth);
205 if (credentialsProvider != null
206 && (!hc.isBatchMode() || !credentialsProvider.isInteractive())) {
207 session.setUserInfo(new CredentialsProviderUserInfo(session,
208 credentialsProvider));
209 }
210 configure(hc, session);
211 return session;
212 }
213
214 private void setUserName(Session session, String userName) {
215
216
217
218 if (userName == null || userName.isEmpty()
219 || userName.equals(session.getUserName())) {
220 return;
221 }
222 try {
223 Class<?>[] parameterTypes = { String.class };
224 Method method = Session.class.getDeclaredMethod("setUserName",
225 parameterTypes);
226 method.setAccessible(true);
227 method.invoke(session, userName);
228 } catch (NullPointerException | IllegalAccessException
229 | IllegalArgumentException | InvocationTargetException
230 | NoSuchMethodException | SecurityException e) {
231 LOG.error(MessageFormat.format(JGitText.get().sshUserNameError,
232 userName, session.getUserName()), e);
233 }
234 }
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254 protected Session createSession(final OpenSshConfig.Host hc,
255 final String user, final String host, final int port, FS fs)
256 throws JSchException {
257 return getJSch(hc, fs).getSession(user, host, port);
258 }
259
260
261
262
263
264
265
266
267
268
269 protected void configureJSch(JSch jsch) {
270
271 }
272
273
274
275
276
277
278
279
280
281
282 protected abstract void configure(OpenSshConfig.Host hc, Session session);
283
284
285
286
287
288
289
290
291
292
293
294
295
296 protected JSch getJSch(final OpenSshConfig.Host hc, FS fs) throws JSchException {
297 if (defaultJSch == null) {
298 defaultJSch = createDefaultJSch(fs);
299 if (defaultJSch.getConfigRepository() == null) {
300 defaultJSch.setConfigRepository(config);
301 }
302 for (Object name : defaultJSch.getIdentityNames())
303 byIdentityFile.put((String) name, defaultJSch);
304 }
305
306 final File identityFile = hc.getIdentityFile();
307 if (identityFile == null)
308 return defaultJSch;
309
310 final String identityKey = identityFile.getAbsolutePath();
311 JSch jsch = byIdentityFile.get(identityKey);
312 if (jsch == null) {
313 jsch = new JSch();
314 configureJSch(jsch);
315 if (jsch.getConfigRepository() == null) {
316 jsch.setConfigRepository(defaultJSch.getConfigRepository());
317 }
318 jsch.setHostKeyRepository(defaultJSch.getHostKeyRepository());
319 jsch.addIdentity(identityKey);
320 byIdentityFile.put(identityKey, jsch);
321 }
322 return jsch;
323 }
324
325
326
327
328
329
330
331
332
333 protected JSch createDefaultJSch(FS fs) throws JSchException {
334 final JSch jsch = new JSch();
335 configureJSch(jsch);
336 knownHosts(jsch, fs);
337 identities(jsch, fs);
338 return jsch;
339 }
340
341 private static void knownHosts(final JSch sch, FS fs) throws JSchException {
342 final File home = fs.userHome();
343 if (home == null)
344 return;
345 final File known_hosts = new File(new File(home, ".ssh"), "known_hosts");
346 try {
347 final FileInputStream in = new FileInputStream(known_hosts);
348 try {
349 sch.setKnownHosts(in);
350 } finally {
351 in.close();
352 }
353 } catch (FileNotFoundException none) {
354
355 } catch (IOException err) {
356
357 }
358 }
359
360 private static void identities(final JSch sch, FS fs) {
361 final File home = fs.userHome();
362 if (home == null)
363 return;
364 final File sshdir = new File(home, ".ssh");
365 if (sshdir.isDirectory()) {
366 loadIdentity(sch, new File(sshdir, "identity"));
367 loadIdentity(sch, new File(sshdir, "id_rsa"));
368 loadIdentity(sch, new File(sshdir, "id_dsa"));
369 }
370 }
371
372 private static void loadIdentity(final JSch sch, final File priv) {
373 if (priv.isFile()) {
374 try {
375 sch.addIdentity(priv.getAbsolutePath());
376 } catch (JSchException e) {
377
378 }
379 }
380 }
381 }