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
74
75
76
77
78
79
80
81
82
83
84
85
86 public abstract class JschConfigSessionFactory extends SshSessionFactory {
87
88 private static final Logger LOG = LoggerFactory
89 .getLogger(JschConfigSessionFactory.class);
90
91 private final Map<String, JSch> byIdentityFile = new HashMap<>();
92
93 private JSch defaultJSch;
94
95 private OpenSshConfig config;
96
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
283 protected abstract void configure(OpenSshConfig.Host hc, Session session);
284
285
286
287
288
289
290
291
292
293
294
295
296
297 protected JSch getJSch(final OpenSshConfig.Host hc, FS fs) throws JSchException {
298 if (defaultJSch == null) {
299 defaultJSch = createDefaultJSch(fs);
300 if (defaultJSch.getConfigRepository() == null) {
301 defaultJSch.setConfigRepository(config);
302 }
303 for (Object name : defaultJSch.getIdentityNames())
304 byIdentityFile.put((String) name, defaultJSch);
305 }
306
307 final File identityFile = hc.getIdentityFile();
308 if (identityFile == null)
309 return defaultJSch;
310
311 final String identityKey = identityFile.getAbsolutePath();
312 JSch jsch = byIdentityFile.get(identityKey);
313 if (jsch == null) {
314 jsch = new JSch();
315 configureJSch(jsch);
316 if (jsch.getConfigRepository() == null) {
317 jsch.setConfigRepository(defaultJSch.getConfigRepository());
318 }
319 jsch.setHostKeyRepository(defaultJSch.getHostKeyRepository());
320 jsch.addIdentity(identityKey);
321 byIdentityFile.put(identityKey, jsch);
322 }
323 return jsch;
324 }
325
326
327
328
329
330
331
332
333
334
335
336 protected JSch createDefaultJSch(FS fs) throws JSchException {
337 final JSch jsch = new JSch();
338 configureJSch(jsch);
339 knownHosts(jsch, fs);
340 identities(jsch, fs);
341 return jsch;
342 }
343
344 private static void knownHosts(final JSch sch, FS fs) throws JSchException {
345 final File home = fs.userHome();
346 if (home == null)
347 return;
348 final File known_hosts = new File(new File(home, ".ssh"), "known_hosts");
349 try {
350 final FileInputStream in = new FileInputStream(known_hosts);
351 try {
352 sch.setKnownHosts(in);
353 } finally {
354 in.close();
355 }
356 } catch (FileNotFoundException none) {
357
358 } catch (IOException err) {
359
360 }
361 }
362
363 private static void identities(final JSch sch, FS fs) {
364 final File home = fs.userHome();
365 if (home == null)
366 return;
367 final File sshdir = new File(home, ".ssh");
368 if (sshdir.isDirectory()) {
369 loadIdentity(sch, new File(sshdir, "identity"));
370 loadIdentity(sch, new File(sshdir, "id_rsa"));
371 loadIdentity(sch, new File(sshdir, "id_dsa"));
372 }
373 }
374
375 private static void loadIdentity(final JSch sch, final File priv) {
376 if (priv.isFile()) {
377 try {
378 sch.addIdentity(priv.getAbsolutePath());
379 } catch (JSchException e) {
380
381 }
382 }
383 }
384 }