1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.commons.vfs2.provider.sftp;
18
19 import java.io.File;
20 import java.io.IOException;
21 import java.time.Duration;
22 import java.util.Objects;
23 import java.util.Properties;
24
25 import org.apache.commons.lang3.SystemProperties;
26 import org.apache.commons.lang3.SystemUtils;
27 import org.apache.commons.lang3.time.DurationUtils;
28 import org.apache.commons.logging.Log;
29 import org.apache.commons.logging.LogFactory;
30 import org.apache.commons.vfs2.FileSystemException;
31 import org.apache.commons.vfs2.FileSystemOptions;
32
33 import com.jcraft.jsch.ConfigRepository;
34 import com.jcraft.jsch.JSch;
35 import com.jcraft.jsch.JSchException;
36 import com.jcraft.jsch.Logger;
37 import com.jcraft.jsch.OpenSSHConfig;
38 import com.jcraft.jsch.Proxy;
39 import com.jcraft.jsch.ProxyHTTP;
40 import com.jcraft.jsch.ProxySOCKS5;
41 import com.jcraft.jsch.Session;
42 import com.jcraft.jsch.UserInfo;
43
44
45
46
47 public final class SftpClientFactory {
48
49
50 private static final class JSchLogger implements Logger {
51 @Override
52 public boolean isEnabled(final int level) {
53 switch (level) {
54 case FATAL:
55 return LOG.isFatalEnabled();
56 case ERROR:
57 return LOG.isErrorEnabled();
58 case WARN:
59 return LOG.isDebugEnabled();
60 case DEBUG:
61 return LOG.isDebugEnabled();
62 case INFO:
63 return LOG.isInfoEnabled();
64 default:
65 return LOG.isDebugEnabled();
66 }
67 }
68
69 @Override
70 public void log(final int level, final String msg) {
71 switch (level) {
72 case FATAL:
73 LOG.fatal(msg);
74 break;
75 case ERROR:
76 LOG.error(msg);
77 break;
78 case WARN:
79 LOG.warn(msg);
80 break;
81 case DEBUG:
82 LOG.debug(msg);
83 break;
84 case INFO:
85 LOG.info(msg);
86 break;
87 default:
88 LOG.debug(msg);
89 }
90 }
91 }
92 private static final String KEY_COMPRESSION_C2S = "compression.c2s";
93 private static final String KEY_COMPRESSION_S2C = "compression.s2c";
94 private static final String KEY_PREFERRED_AUTHENTICATIONS = "PreferredAuthentications";
95
96 private static final String KEY_STRICT_HOST_KEY_CHECKING = "StrictHostKeyChecking";
97
98 private static final String SSH_DIR_NAME = ".ssh";
99 private static final String OPENSSH_CONFIG_NAME = "config";
100 private static final Log LOG = LogFactory.getLog(SftpClientFactory.class);
101
102 static {
103 JSch.setLogger(new JSchLogger());
104 }
105
106 private static void addIdentities(final JSch jsch, final File sshDir, final IdentityProvider[] identities)
107 throws FileSystemException {
108 if (identities != null) {
109 for (final IdentityProvider info : identities) {
110 addIdentity(jsch, info);
111 }
112 } else {
113
114 final File privateKeyFile = new File(sshDir, "id_rsa");
115 if (privateKeyFile.isFile() && privateKeyFile.canRead()) {
116 addIdentity(jsch, new IdentityInfo(privateKeyFile));
117 }
118 }
119 }
120
121 private static void addIdentity(final JSch jsch, final IdentityProvider identity) throws FileSystemException {
122 try {
123 identity.addIdentity(jsch);
124 } catch (final JSchException e) {
125 throw new FileSystemException("vfs.provider.sftp/load-private-key.error", identity, e);
126 }
127 }
128
129
130
131
132
133
134
135
136
137
138
139
140 public static Session createConnection(final String hostname, final int port, final char[] username,
141 final char[] password, final FileSystemOptions fileSystemOptions) throws FileSystemException {
142 Objects.requireNonNull(username, "username");
143 final JSch jsch = new JSch();
144
145
146 final SftpFileSystemConfigBuilder builder = SftpFileSystemConfigBuilder.getInstance();
147 final File knownHostsFile = builder.getKnownHosts(fileSystemOptions);
148 final IdentityProvider[] identities = builder.getIdentityProvider(fileSystemOptions);
149 final IdentityRepositoryFactory repositoryFactory = builder.getIdentityRepositoryFactory(fileSystemOptions);
150 final ConfigRepository configRepository = builder.getConfigRepository(fileSystemOptions);
151 final boolean loadOpenSSHConfig = builder.isLoadOpenSSHConfig(fileSystemOptions);
152
153 final File sshDir = findSshDir();
154
155 setKnownHosts(jsch, sshDir, knownHostsFile);
156
157 if (repositoryFactory != null) {
158 jsch.setIdentityRepository(repositoryFactory.create(jsch));
159 }
160
161 addIdentities(jsch, sshDir, identities);
162 setConfigRepository(jsch, sshDir, configRepository, loadOpenSSHConfig);
163
164 final Session session;
165 try {
166 session = jsch.getSession(new String(username), hostname, port);
167 if (password != null) {
168 session.setPassword(new String(password));
169 }
170
171 final Duration sessionTimeout = builder.getSessionTimeout(fileSystemOptions);
172 if (sessionTimeout != null) {
173 session.setTimeout(DurationUtils.toMillisInt(sessionTimeout));
174 }
175
176 final UserInfo userInfo = builder.getUserInfo(fileSystemOptions);
177 if (userInfo != null) {
178 session.setUserInfo(userInfo);
179 }
180
181 final Properties config = new Properties();
182
183
184 final String strictHostKeyChecking = builder.getStrictHostKeyChecking(fileSystemOptions);
185 if (strictHostKeyChecking != null) {
186 config.setProperty(KEY_STRICT_HOST_KEY_CHECKING, strictHostKeyChecking);
187 }
188
189 final String preferredAuthentications = builder.getPreferredAuthentications(fileSystemOptions);
190 if (preferredAuthentications != null) {
191 config.setProperty(KEY_PREFERRED_AUTHENTICATIONS, preferredAuthentications);
192 }
193
194
195 final String compression = builder.getCompression(fileSystemOptions);
196 if (compression != null) {
197 config.setProperty(KEY_COMPRESSION_S2C, compression);
198 config.setProperty(KEY_COMPRESSION_C2S, compression);
199 }
200
201 final String keyExchangeAlgorithm = builder.getKeyExchangeAlgorithm(fileSystemOptions);
202 if (keyExchangeAlgorithm != null) {
203 config.setProperty("kex", keyExchangeAlgorithm);
204 }
205
206 final String proxyHost = builder.getProxyHost(fileSystemOptions);
207 if (proxyHost != null) {
208 final int proxyPort = builder.getProxyPort(fileSystemOptions);
209 final SftpFileSystemConfigBuilder.ProxyType proxyType = builder.getProxyType(fileSystemOptions);
210 final String proxyUser = builder.getProxyUser(fileSystemOptions);
211 final String proxyPassword = builder.getProxyPassword(fileSystemOptions);
212 Proxy proxy = null;
213 if (SftpFileSystemConfigBuilder.PROXY_HTTP.equals(proxyType)) {
214 proxy = createProxyHTTP(proxyHost, proxyPort);
215 ((ProxyHTTP) proxy).setUserPasswd(proxyUser, proxyPassword);
216 } else if (SftpFileSystemConfigBuilder.PROXY_SOCKS5.equals(proxyType)) {
217 proxy = createProxySOCKS5(proxyHost, proxyPort);
218 ((ProxySOCKS5) proxy).setUserPasswd(proxyUser, proxyPassword);
219 } else if (SftpFileSystemConfigBuilder.PROXY_STREAM.equals(proxyType)) {
220 proxy = createStreamProxy(proxyHost, proxyPort, fileSystemOptions, builder);
221 }
222
223 if (proxy != null) {
224 session.setProxy(proxy);
225 }
226 }
227
228
229 if (!config.isEmpty()) {
230 session.setConfig(config);
231 }
232 session.setDaemonThread(true);
233 session.connect();
234 } catch (final Exception exc) {
235 throw new FileSystemException("vfs.provider.sftp/connect.error", exc, hostname);
236 }
237
238 return session;
239 }
240
241 private static ProxyHTTP createProxyHTTP(final String proxyHost, final int proxyPort) {
242 return proxyPort == 0 ? new ProxyHTTP(proxyHost) : new ProxyHTTP(proxyHost, proxyPort);
243 }
244
245 private static ProxySOCKS5 createProxySOCKS5(final String proxyHost, final int proxyPort) {
246 return proxyPort == 0 ? new ProxySOCKS5(proxyHost) : new ProxySOCKS5(proxyHost, proxyPort);
247 }
248
249 private static Proxy createStreamProxy(final String proxyHost, final int proxyPort,
250 final FileSystemOptions fileSystemOptions, final SftpFileSystemConfigBuilder builder) {
251
252
253
254
255
256
257 final String proxyUser = builder.getProxyUser(fileSystemOptions);
258 final String proxyPassword = builder.getProxyPassword(fileSystemOptions);
259 final FileSystemOptions proxyOptions = builder.getProxyOptions(fileSystemOptions);
260
261 final String proxyCommand = builder.getProxyCommand(fileSystemOptions);
262
263
264 return new SftpStreamProxy(proxyCommand, proxyUser, proxyHost, proxyPort, proxyPassword, proxyOptions);
265 }
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287 private static File findSshDir() {
288 final String sshDirPath;
289 sshDirPath = System.getProperty("vfs.sftp.sshdir");
290 if (sshDirPath != null) {
291 final File sshDir = new File(sshDirPath);
292 if (sshDir.exists()) {
293 return sshDir;
294 }
295 }
296
297 File sshDir = new File(SystemProperties.getUserHome(), SSH_DIR_NAME);
298 if (sshDir.exists()) {
299 return sshDir;
300 }
301
302 if (SystemUtils.IS_OS_WINDOWS) {
303
304 final String userName = SystemProperties.getUserName();
305 sshDir = new File("C:\\cygwin\\home\\" + userName + "\\" + SSH_DIR_NAME);
306 if (sshDir.exists()) {
307 return sshDir;
308 }
309 }
310 return new File("");
311 }
312
313 private static void setConfigRepository(final JSch jsch, final File sshDir, final ConfigRepository configRepository, final boolean loadOpenSSHConfig)
314 throws FileSystemException {
315 if (configRepository != null) {
316 jsch.setConfigRepository(configRepository);
317 } else if (loadOpenSSHConfig) {
318 try {
319
320 final ConfigRepository openSSHConfig = OpenSSHConfig.parseFile(new File(sshDir, OPENSSH_CONFIG_NAME).getAbsolutePath());
321 jsch.setConfigRepository(openSSHConfig);
322 } catch (final IOException e) {
323 throw new FileSystemException("vfs.provider.sftp/load-openssh-config.error", e);
324 }
325 }
326 }
327
328 private static void setKnownHosts(final JSch jsch, final File sshDir, File knownHostsFile)
329 throws FileSystemException {
330 try {
331 if (knownHostsFile != null) {
332 jsch.setKnownHosts(knownHostsFile.getAbsolutePath());
333 } else {
334
335 knownHostsFile = new File(sshDir, "known_hosts");
336 if (knownHostsFile.isFile() && knownHostsFile.canRead()) {
337 jsch.setKnownHosts(knownHostsFile.getAbsolutePath());
338 }
339 }
340 } catch (final JSchException e) {
341 throw new FileSystemException("vfs.provider.sftp/known-hosts.error", knownHostsFile.getAbsolutePath(), e);
342 }
343
344 }
345
346 private SftpClientFactory() {
347 }
348 }